Browse Source

Merge branch 'blockstack-connect-release' of https://github.com/blockstack/docs.blockstack into blockstack-connect-release

blockstack-connect-release
Mark M. Hendrickson 5 years ago
parent
commit
c6ac47f19e
  1. BIN
      _android/images/blockstack-install.png
  2. BIN
      _android/images/blockstack-signin.png
  3. BIN
      _android/images/chrome-prompt.png
  4. BIN
      _android/images/configure-activity.png
  5. BIN
      _android/images/connect-response.png
  6. BIN
      _android/images/connect-ui.png
  7. BIN
      _android/images/create-restore.png
  8. BIN
      _android/images/final-app.png
  9. BIN
      _android/images/helloandroid.zip
  10. BIN
      _android/images/initial-build.png
  11. BIN
      _android/images/new-interface.png
  12. BIN
      _android/images/oreo-api.png
  13. BIN
      _android/images/q-api.png
  14. BIN
      _android/images/running-app.png
  15. BIN
      _android/images/select-hdw.png
  16. BIN
      _android/images/sync-project.png
  17. BIN
      _android/images/sync-success.png
  18. 751
      _android/tutorial.md
  19. 257
      _browser/blockstack_storage.md
  20. BIN
      _browser/images/display-complete.png
  21. BIN
      _browser/images/initial-app.png
  22. BIN
      _browser/images/login-choice.png
  23. BIN
      _browser/images/login-no-auth.png
  24. BIN
      _browser/images/login.gif
  25. BIN
      _browser/images/login.png
  26. BIN
      _browser/images/make-a-list.png
  27. BIN
      _browser/images/multi-player-storage-status.png
  28. BIN
      _browser/images/multiple-lists.png
  29. BIN
      _browser/images/network-connections.gif
  30. BIN
      _browser/images/publish-data-perm.png
  31. BIN
      _browser/images/saving-status.png
  32. BIN
      _browser/images/todo-app.png
  33. BIN
      _browser/images/todo-sign-in.png
  34. 28
      _browser/todo-list.md
  35. 19
      _common/core_ref.md
  36. 24
      _core/smart/cli-wallet-quickstart.md
  37. 93
      _core/smart/neon-node.md
  38. 20
      _core/smart/overview.md
  39. 209
      _core/smart/principals.md
  40. 2
      _core/smart/sdk-quickstart.md
  41. 167
      _core/smart/testnet-node.md
  42. 14
      _core/smart/tutorial-counter.md
  43. 4
      _core/smart/tutorial.md
  44. 2
      _develop/cliDocs.md
  45. 47
      _develop/connect/get-started.md
  46. 206
      _develop/connect/use-with-clarity.md
  47. 2
      _develop/radiks-intro.md
  48. 4
      _includes/sign_in.md
  49. 14
      _ios/tutorial.md

BIN
_android/images/blockstack-install.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

BIN
_android/images/blockstack-signin.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 48 KiB

BIN
_android/images/chrome-prompt.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

BIN
_android/images/configure-activity.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 56 KiB

BIN
_android/images/connect-response.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

BIN
_android/images/connect-ui.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

BIN
_android/images/create-restore.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

After

Width:  |  Height:  |  Size: 34 KiB

BIN
_android/images/final-app.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

After

Width:  |  Height:  |  Size: 26 KiB

BIN
_android/images/helloandroid.zip

Binary file not shown.

BIN
_android/images/initial-build.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

BIN
_android/images/new-interface.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 20 KiB

BIN
_android/images/oreo-api.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 123 KiB

BIN
_android/images/q-api.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

BIN
_android/images/running-app.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 59 KiB

BIN
_android/images/select-hdw.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

After

Width:  |  Height:  |  Size: 70 KiB

BIN
_android/images/sync-project.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 124 KiB

After

Width:  |  Height:  |  Size: 48 KiB

BIN
_android/images/sync-success.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 139 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB

751
_android/tutorial.md

@ -3,21 +3,22 @@ layout: learn
description: Learn about the Android SDK description: Learn about the Android SDK
permalink: /:collection/:path.html permalink: /:collection/:path.html
--- ---
# Android DApps # Android DApps
{:.no_toc} {:.no_toc}
This tutorial is written for readers who are new to either or both Blockstack This tutorial is written for readers who are new to either or both Blockstack
and Android to create a decentralized application. It contains the following and Android to create a decentralized application. It contains the following
content: content:
* TOC TOC
{:toc} {:toc}
This tutorial was extensively tested using Android Studio 3.1 on a MacBook Air This tutorial was extensively tested using Android Studio 3.6 on a Dell XPS 13
running High Sierra 10.13.4. If your environment is different, you may encounter running Ubuntu 19. If your environment is different, you may encounter
slight or even major discrepancies when performing the procedures in this slight or even major discrepancies when performing the procedures in this
tutorial. Please [join the Blockstack community tutorial. Please [join the Blockstack discord server](https://chat.blockstack.org) and post questions or comments to
Slack](https://slofile.com/slack/blockstack) and post questions or comments to
the `#support` channel. the `#support` channel.
Finally, this tutorial is written for all levels from the beginner to the most Finally, this tutorial is written for all levels from the beginner to the most
@ -36,7 +37,7 @@ intended for user on an Android phone.
![](images/final-app.png) ![](images/final-app.png)
Only users with an existing `blockstack.id` can run your Only users with an existing blockstack id can run your
final sample application. When complete, users interact with the sample final sample application. When complete, users interact with the sample
application by doing the following: application by doing the following:
@ -45,7 +46,7 @@ application by doing the following:
## Set up your environment ## Set up your environment
This sample application has two code bases, a Blockstack `hello-blockstack` This sample application has two code bases, a Blockstack `hello-blockstack`
application and a `hello-andriod` Android application. Before you start web application and a `hello-andriod` Android application. Before you start
developing the sample, there are a few elements you need in your environment. developing the sample, there are a few elements you need in your environment.
### Install Android Studio ### Install Android Studio
@ -56,7 +57,7 @@ step. However, you will need to adjust the remaining instructions for your
environment. environment.
Follow the installation instructions to download and [Android Studio Follow the installation instructions to download and [Android Studio
3.1](https://developer.android.com/studio/install) for your operating system. 3.6](https://developer.android.com/studio/install) for your operating system.
Depending on your network connection, this can take between 15-30 minutes. Depending on your network connection, this can take between 15-30 minutes.
![](images/studio-download.png) ![](images/studio-download.png)
@ -75,27 +76,9 @@ $ which npm npx
If you don't have these installed, take a moment to install or upgrade as needed. If you don't have these installed, take a moment to install or upgrade as needed.
### Install the Blockstack test rig ## Build the Blockstack hello-world web app
Users interact with Blockstack-enabled applications through a web browser. You
can Blockstack in test mode, on `localhost` or you can interact with completed
apps through the Blockstack webapp which is available at
[https://browser.blockstack.org/].
If you have already installed Blockstack for testing locally and have an
existing Blockstack ID, skip this section. Otherwise, continue onto install
Blockstack.
1. Go to [Blockstack](https://blockstack.org/install)
![](images/blockstack-install.png)
2. Install the version appropriate for your operating system.
## Build the Blockstack hello-world
In this section, you build a Blockstack `hello-world` application. Then, you In this section, you build a Blockstack `hello-world` web application that acts as the companion site to your Android application.
modify the `hello-world` to interact with the Android app via a redirect.
### Generate and launch your hello-blockstack application ### Generate and launch your hello-blockstack application
@ -106,74 +89,66 @@ In this section, you build an initial React.js application called
1. Create a `hello-blockstack` directory. 1. Create a `hello-blockstack` directory.
```bash ```bash
mkdir hello-blockstack mkdir hello-blockstack
``` ```
2. Change into your new directory. 2. Change into your new directory.
```bash ```bash
cd hello-blockstack cd hello-blockstack
``` ```
3. Use the Blockstack application generator to create your initial `hello-blockstack` application. 3. Use the Blockstack application generator to create your initial `hello-blockstack` application.
```bash ```bash
$ npx generator-blockstack --react $ npx generator-blockstack --plain
npx: installed 338 in 13.792s create package.json
create package.json create .gitignore
create .gitignore create webpack.config.js
create webpack.config.js create netlify.toml
create netlify.toml create firebase.json
create firebase.json ...
... npm WARN ajv-errors@1.0.1 requires a peer of ajv@>=5.0.0 but none is installed. You must install peer dependencies yourself.
I'm all done. Running npm install for you to install the required dependencies. If this fails, try running the command yourself.
added 840 packages from 582 contributors and audited 843 packages in 18.84s
> fsevents@1.2.9 install /private/tmp/testymc/node_modules/fsevents found 0 vulnerabilities
> node install ```
added 775 packages from 455 contributors and audited 9435 packages in 20.934s
found 0 vulnerabilities
```
Depending on your environment you may have some warnings with the installation. Optionally, you can fix these before continuing to the next section.
```
4. Respond to the prompts to populate the initial app. Depending on your environment you may have some warnings with the installation. Optionally, you can fix these before continuing to the next section.
After the process completes successfully, you see a prompt similar to the following: 4. Depending on your environment, respond to the prompts to populate the initial app.
You might see a prompt similar to the following:
```bash ```bash
[fsevents] Success: [fsevents] Success:
"/Users/theuser/repos/hello-blockstack/node_modules/fsevents/lib/binding/Release/node-v59-darwin-x64/fse.node" "/Users/theuser/repos/hello-blockstack/node_modules/fsevents/lib/binding/Release/node-v59-darwin-x64/fse.node"
is installed via remote npm notice created a lockfile as package-lock.json. is installed via remote npm notice created a lockfile as package-lock.json.
You should commit this file. added 1060 packages in 26.901s You should commit this file. added 1060 packages in 26.901s
``` ```
5. Run the initial application. 5. After the initial setup you can now run the initial application.
```bash ```bash
$ npm run start $ npm run start
> hello-blockstack@0.0.0 start /Users/meepers/repos/hello-blockstack > hello-blockstack-webpack@0.0.0 start /home/user/hello-blockstack
> webpack-dev-server > webpack-dev-server
Project is running at http://localhost:8080/ ℹ 「wds」: Project is running at http://localhost:9000/
webpack output is served from / ℹ 「wds」: webpack output is served from /
404s will fallback to /index.html ℹ 「wds」: Content not from webpack is served from /home/user/hello-blockstack/dist
Hash: 4d2312ba236a4b95dc3a ℹ 「wdm」: Hash: f5d88efe9c9194f66ddd
Version: webpack 2.7.0 Version: webpack 4.43.0
Time: 2969ms Time: 2733ms
Asset Size Chunks Chunk Names Built at: 05/19/2020 10:44:50 AM
.... Asset Size Chunks Chunk Names
Child html-webpack-plugin for "index.html": main.js 5.39 MiB main [emitted] main
chunk {0} index.html 541 kB [entry] [rendered] ....
[0] ./~/lodash/lodash.js 540 kB {0} [built] [./src/index.js] 1.8 KiB {main} [built]
[1] ./~/html-webpack-plugin/lib/loader.js!./src/index.html 533 bytes {0} [built] + 610 hidden modules
[2] (webpack)/buildin/global.js 509 bytes {0} [built] ℹ 「wdm」: Compiled successfully.
[3] (webpack)/buildin/module.js 517 bytes {0} [built] ```
webpack: Compiled successfully.
```
The system opens a browser displaying your running application. The system opens a browser displaying your running application.
@ -182,76 +157,23 @@ In this section, you build an initial React.js application called
At this point, the browser is running a Blockstack server on your local host. At this point, the browser is running a Blockstack server on your local host.
This is for testing your applications only. This is for testing your applications only.
6. Choose **Sign in with Blockstack** 6. Choose **Get started**
The system displays a prompt allowing you to create a new Blockstack ID or restore an existing one. The system displays a prompt allowing you to create a new Blockstack ID or restore an existing one.
![](images/create-restore.png) ![](images/create-restore.png)
7. Follow the prompts appropriate to your situation. 7. Follow the prompts appropriate to your situation.
If you are restoring an existing ID, you may see a prompt about your user At this point you have only a single application
being nameless, ignore it. At this point you have only a single application on your test server. So, you should see this single application, with your
on your test server. So, you should see this single application, with your own `blockstack.id`, once you are signed in:
own `blockstack.id` display name, once you are signed in:
![](images/running-app.png)
### Add a redirect end point to your application
When a user opens the webapp from the Blockstack Browser on an Android phone,
you want the web app to redirect the user to your Android application. The work
you do here will allow it.
1. From the terminal command line, change directory to the root of your sample
application directory.
2. Use the `touch` command to add a redirect endpoint to your application. ![](images/running-app.png)
This endpoint on the web version of your app will redirect Android users back
to your mobile app.
```bash
$ touch public/redirect.html
```
3. Open `redirect.html` and add code to the endpoint.
```html
<!DOCTYPE html>
<html>
<head>
<title>Hello, Blockstack!</title>
<script>
function getParameterByName(name) {
var match = RegExp('[?&]' + name + '=([^&]*)').exec(window.location.search);
return match && decodeURIComponent(match[1].replace(/\+/g, ' '));
}
var authResponse = getParameterByName('authResponse')
window.location="myblockstackapp:" + authResponse
</script>
<body>
</body>
</html>
```
Blockstack apps are identified by their domain names. The endpoint will
receive a get request with the query parameter `authResponse=XXXX` and
should redirect the browser to `myblockstackapp:XXXX`.
`myblockstackapp:` is custom protocol handler. The handler should be unique
to your application. Your app's web-based authentication uses this handler
to redirect the user back to your Android app. Later, you'll add a reference
to this handler in your Android application.
5. Close and save the `redirect.html` file.
6. Ensure your Blockstack compiles successfully.
## Create the hello-android project ## Create the hello-android project
In this section, you'll create an Android application in Android Studio. You'll In this section, you'll create an Android application in Android Studio. You'll
run the application in the emulator to test it. run the application in the emulator to test it.
### Create a simple project ### Create a simple project
@ -259,79 +181,74 @@ run the application in the emulator to test it.
In this section, you create an inital project. You'll validate the application's In this section, you create an inital project. You'll validate the application's
iniatial state by creating an emulator to run it in. Open Android Studio and do the following: iniatial state by creating an emulator to run it in. Open Android Studio and do the following:
1. Open Android Studio and choose **Start a new Andriod Studio project**. 1. Open Android Studio and choose **Start a new Andriod Studio project**.
If studio is already started, choose **File > New > New Project**. If studio is already started, choose **File > New > New Project**.
2. Enter these fields in the **Create Android Project** page. 2. Choose a project template: select **Empty Activity** and press **Next**
<table class="uk-table"> 3. Enter these fields in the **Create Android Project** page.
<tr>
<th>Application Name</th> <table class="uk-table">
<td><code>hello-android</code></td> <tr>
</tr> <th>Name</th>
<tr> <td><code>Hello Android</code></td>
<th>Company domain</th> </tr>
<td><code><i>USERNAME</i>.example.com</code></td> <tr>
</tr> <th>Package name</th>
<tr> <td><code>blockstack.id.<i>USERNAME</i>.hello</code></td>
<th>Project location</th> </tr>
<td><code>/Users/<i>USERNAME</i>/AndroidStudioProjects/helloandroid</code></td> <tr>
</tr> <th>Project location</th>
<tr> <td><code>/home/<i>USERNAME</i>/AndroidStudioProjects/helloandroid</code></td>
<th>Include Kotlin support</th> </tr>
<td>Set (checked)</td> <tr>
</tr> <th>Language</th>
</table> <td>Select (Kotlin)</td>
</tr>
3. Press **Next** to display **Target Android Devices**. <tr>
4. Check **Phone and Tablet**. <th>Minimum SDK</th>
5. Choose API 27: Andriod 8.1 (Oreo) for the target version. <td>Select (API 21: Android 5.0 (Lollipop))</td>
6. Press **Next**. </tr>
7. Choose **Empty Activity** and press **Next**. </table>
8. Leave the **Configure Activity** dialog with its defaults.
![](images/configure-activity.png)
![](images/configure-activity.png)
4. Press **Finish**.
9. Press **Finish**.
Android studio builds your initial project. This can take a bit the first time you do it. Android studio builds your initial project. This can take a bit the first time you do it.
![](images/initial-build.png)
### Run the app in an emulator ### Run the app in an emulator
In this section, you run the appliation and create an emulator when prompted. In this section, you run the appliation and create an emulator when prompted.
1. Once the project is imported into studio, click the `app` module in the **Project** window. 1. Once the project is imported into studio, click the `app` module in the **Project** window.
2. Then, select **Run > Run** (or click the green arrow in the toolbar). 2. Then, select **Run > Run** (or click the green arrow in the toolbar).
Studio prompts you to **Select Deployment Target**. Studio prompts you to **Select Deployment Target**.
3. Choose **Create New Virtual Device** and press **OK**. 3. Choose **Create New Virtual Device** and press **OK**.
Studio prompts you to **Select Hardware**. Studio prompts you to **Select Hardware**.
4. Choose a Phone running Pixel XL. 4. Choose a Phone device configuration like "Pixel".
![](images/select-hdw.png) ![](images/select-hdw.png)
Studio prompts you for a system image. Studio prompts you for a system image.
5. Choose **Oreo** which is API level 27 and press **Next**. 5. Choose **Q** which is API level 29 and press **Next**.
![](images/oreo-api.png) ![](images/q-api.png)
Studio asks you to verify your new emulator configuration. Studio asks you to verify your new emulator configuration.
6. Press **Finish**. 6. Press **Finish**.
The emulation takes a moment to build. Then, studio launches the emulation and opens your application. The emulation takes a moment to build. Then, studio launches the emulation and opens your application.
![](images/hello-andriod-1.png)
![](images/hello-andriod-1.png)
### Configure your application with the Blockstack SDK ### Configure your application with the Blockstack SDK
@ -339,84 +256,79 @@ Now that you have created your initial project and verified it running in an emu
1. In studio, open the `AndroidManifest.xml` file. 1. In studio, open the `AndroidManifest.xml` file.
2. Add an `<intent-filter>` with the custom handler for Blockstack. 2. Add an `<intent-filter>` with the custom handler for Blockstack. Replace the host ("flamboyant-darwin-d11c17.netlify.app") with your own url once you have hosted your web app publicly.
```XML
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="myblockstackapp" />
</intent-filter>
```
2. Open the Project's `build.gradle` file. ```XML
3. Add the Jitpack repository `maven { url 'https://jitpack.io' }` to the `repositories` section. <intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" android:host="flamboyant-darwin-d11c17.netlify.app" />
</intent-filter>
```
When you finish, that section looks like this: 3. Open the Project's `build.gradle` file ("Project: Hello Android").
4. Add the Jitpack repository `maven { url 'https://jitpack.io' }` to the `repositories` section. for all projects
```JS When you finish, that section looks like this:
allprojects {
repositories {
google()
jcenter()
maven { url 'https://jitpack.io' }
}
}
```
4. Open the Module `build.gradle` file. ```JS
5. Set the `defaultConfig minSdkVersion` to `19`. allprojects {
repositories {
google()
jcenter()
maven { url 'https://jitpack.io' }
}
}
```
When you are done, you should see (within your own username not `meepers`): 5. Open the Module `build.gradle` file ("Module: app").
6. Fix a problem with duplicate meta data in the Android SDK: add `packagingOptions`
```JS ```JS
android { android {
compileSdkVersion 27
defaultConfig {
applicationId "com.example.meepers.hello_android"
minSdkVersion 19
targetSdkVersion 27
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
... ...
packagingOptions {
exclude 'META-INF/*'
}
} }
``` ```
7. Below this, add the Blockstack Android SDK dependency to your project's `dependencies` list: 7. Below this, add the Blockstack Android SDK dependency to your project's `dependencies` list:
When you are done you should see: When you are done you should see:
```JS ```JS
dependencies { dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'com.android.support:appcompat-v7:27.1.1' implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3' implementation 'androidx.core:core-ktx:1.2.0'
implementation 'com.android.support:design:27.1.1' implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.12' testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
implementation 'com.github.blockstack:blockstack-android:0.4.0'
implementation 'com.github.blockstack:blockstack-android:7940940'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.2.0'
} }
``` ```
**NOTE**: Ignore the warning on the appcompat` dependencies. **NOTE**: Ignore the warning on the `junit` dependencies.
8. Sync your project. 8. Sync your project.
![](images/sync-project.png) ![](images/sync-project.png)
Be sure to check the sync completed successfully. Be sure to check the sync completed successfully.
![](images/sync-success.png) ![](images/sync-success.png)
10. Run your app in the emulator. 9. Run your app in the emulator.
You've made a lot of changes, make sure the emulator is still running You've made a lot of changes, make sure the emulator is still running
correctly. correctly.
### Add a simple interface ### Add a simple interface
@ -424,48 +336,50 @@ Now that you have created your initial project and verified it running in an emu
The `activity_main.xml` file defines the graphical elements. Some elements are required before you can functionality to your `MainActivity.kt` code. The `activity_main.xml` file defines the graphical elements. Some elements are required before you can functionality to your `MainActivity.kt` code.
3. Replace the entire content of the file with the following code: 2. Replace the entire content of the file with the following code:
The new interface includes a `BlockstackSignInButton` which is provided by The new interface includes a `BlockstackSignInButton` which is provided by
the SDK. This SDK includes a themed "Sign in with Blockstack" button the SDK. This SDK includes a themed "Get Started" button
(`BlockstackSignInButton`). You use this button here with the (`BlockstackSignInButton`). You use this button here with the
`org.blockstack.android.sdk.ui.BlockstackSignInButton` class. `org.blockstack.android.sdk.ui.BlockstackSignInButton` class.
```XML ```XML
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
tools:context=".MainActivity"> android:layout_margin="16dp"
tools:context=".MainActivity">
<org.blockstack.android.sdk.ui.BlockstackSignInButton
android:id="@+id/signInButton" <org.blockstack.android.sdk.ui.BlockstackSignInButton
android:layout_width="307dp" android:id="@+id/signInButton"
android:layout_height="45dp" android:layout_width="match_parent"
android:layout_margin="4dp" android:layout_height="wrap_content"
android:layout_marginEnd="185dp" app:layout_constraintTop_toTopOf="parent"
android:layout_marginStart="39dp" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintBottom_toTopOf="@id/userDataTextView"/>
tools:layout_editor_absoluteY="16dp" />
<TextView
<TextView android:id="@+id/userDataTextView"
android:id="@+id/userDataTextView" android:layout_width="wrap_content"
android:layout_width="370dp" android:layout_height="wrap_content"
android:layout_height="27dp" android:text="Hello World!"
tools:layout_editor_absoluteX="6dp" app:layout_constraintBottom_toBottomOf="parent"
tools:layout_editor_absoluteY="70dp" /> app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
</android> app:layout_constraintTop_toBottomOf="@id/signInButton" />
</androidx.constraintlayout.widget.ConstraintLayout>
``` ```
This codes adds a button and some text to your application. This codes adds a button and some text to your application.
4. Choose **Run > Apply changes**. 3. Choose **Run > Apply changes**.
5. Choose **Run > Run app** in the emulator. 4. Choose **Run > Run app** in the emulator.
The emulator now contains a new interface with a button: The emulator now contains a new interface with a button:
@ -474,27 +388,35 @@ Now that you have created your initial project and verified it running in an emu
### Add session & authentication code ### Add session & authentication code
1. Open the `MainActivity.kt` file. 1. Open the `MainActivity.kt` file.
2. Add some additional imports to the top below the `android.os.Bundle` import. 2. Add some additional imports to the top, replace the `android.os.Bundle` import.
When you are done, your imports should appear as follows: When you are done, your imports should appear as follows:
```kotlin ```kotlin
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.support.v7.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import android.view.View import androidx.lifecycle.lifecycleScope
import kotlinx.android.synthetic.main.activity_main.* import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.blockstack.android.sdk.BlockstackSession import org.blockstack.android.sdk.BlockstackSession
import org.blockstack.android.sdk.Scope import org.blockstack.android.sdk.BlockstackSignIn
import org.blockstack.android.sdk.UserData import org.blockstack.android.sdk.SessionStore
import org.blockstack.android.sdk.toBlockstackConfig import org.blockstack.android.sdk.getBlockstackSharedPreferences
``` import org.blockstack.android.sdk.model.UserData
3. Add a variable for the Blockstack session before `onCreate`. import org.blockstack.android.sdk.model.toBlockstackConfig
import org.blockstack.android.sdk.ui.SignInProvider
import org.blockstack.android.sdk.ui.showBlockstackConnect
```
3. Add one variable for the Blockstack sign-in flow and one for the Blockstack session that deals with all other function. This needs to be added before `onCreate`.
```kotlin ```kotlin
class MainActivity : AppCompatActivity() { class MainActivity : AppCompatActivity() {
private var _blockstackSession: BlockstackSession? = null private lateinit var blockstackSession: BlockstackSession
private lateinit var blockstackSignIn: BlockstackSignIn
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -503,201 +425,180 @@ Now that you have created your initial project and verified it running in an emu
} }
``` ```
4. Replace the existing the `onCreate` function with the following: 4. Replace the existing the `onCreate` function with the following, use your own domain if you are already hosting your web app somewhere:
```kotlin
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val scopes = arrayOf(Scope.StoreWrite)
val config = "https://flamboyant-darwin-d11c17.netlify.com"
.toBlockstackConfig(scopes)
_blockstackSession = BlockstackSession(this, config) ```kotlin
override fun onCreate(savedInstanceState: Bundle?) {
signInButton.isEnabled = true super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val appConfig =
"https://flamboyant-darwin-d11c17.netlify.app".toBlockstackConfig()
val sessionStore = SessionStore(getBlockstackSharedPreferences())
blockstackSession = BlockstackSession(sessionStore, appConfig)
blockstackSignIn = BlockstackSignIn(sessionStore, appConfig)
BlockstackSignIn.shouldLaunchInCustomTabs = false
signInButton.setOnClickListener {
showBlockstackConnect()
}
signInButton.setOnClickListener { view: View -> if (intent?.action == Intent.ACTION_VIEW) {
blockstackSession().redirectUserToSignIn { // handle the redirect from sign in
// only called on error userDataTextView.text = "Signing in now ..."
lifecycleScope.launch(Dispatchers.IO) {
handleAuthResponse(intent)
} }
} }
if (intent?.action == Intent.ACTION_VIEW) { }
// handle the redirect from sign in ```
handleAuthResponse(intent)
}
}
```
This new `onCreate` does several things: This new `onCreate` does several things:
* Define the initial state for the `signInButton`. - Supply authentication information for connecting to your Blockstack app: `appDomain` (for `scopes`, `redirectURI`, `manifestURI` the default values are used)
* Supply authentication information for connecting to your Blockstack app: `appDomain` and `scopes` (for `redirectURI`, `manifestURI` the default values are used) - Add a listener for the button click.
* Add a listener for the button click. - Handles the redirect intent from the sign-in flow.
Notice that the application in this example is a URI you have not set up. Notice that the application in this example is a URI you have not set up.
Registering and application name takes time, so in time's interest you'll Registering and application name takes time, so in time's interest you'll
use an existing app that is identical to the `hello-world` you created use an existing app (https://flamboyant-darwin-d11c17.netlify.app) that is identical to the `hello-world` you created
earlier. For a production version, you'll need to replace `appDomain`, earlier. For a production version, you'll need to replace `appDomain`,
`redirectURI`, `manifestURI` and `scopes` with values appropriate for your `redirectURI`, `manifestURI` and `scopes` with values appropriate for your
app. app.
5. Add a private function to reflect when a user successfully signs in. 5. Add a private function to reflect when a user successfully signs in.
```kotlin ```kotlin
private fun onSignIn(userData: UserData) { private fun onSignIn(userData: UserData) {
userDataTextView.text = "Signed in as ${userData.decentralizedID}" userDataTextView.text = "Signed in as ${userData.decentralizedID}"
signInButton.isEnabled = false signInButton.isEnabled = false
} }
``` ```
6. Handle sign in requests with an `onNewIntent` function if the activity was already opened when signing in 6. Create a handler for the authentication response.
Retrieve the authentication token from the custom protocol handler call and ```kotlin
send it to the Blockstack session. private fun handleAuthResponse(intent: Intent) {
val authResponse = intent.data?.getQueryParameter("authResponse")
if (authResponse != null) {
val userData = blockstackSession.handlePendingSignIn(authResponse)
if (userData.hasValue) {
// The user is now signed in!
runOnUiThread {
onSignIn(userData.value!!)
}
}
}
}
```
```kotlin 7. Add `SignInProvider` interface for better user onboarding. Let the activity implement the interface and implement the member function `getBlockstackSignIn`
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
if (intent?.action == Intent.ACTION_VIEW) { ```kotlin
handleAuthResponse(intent) class MainActivity : AppCompatActivity(), SignInProvider {
} ...
}
```
7. Create a handler for the authentication response. override fun provideBlockstackSignIn(): BlockstackSignIn {
return blockstackSignIn
```kotlin }
private fun handleAuthResponse(intent: Intent) { }
val response = intent.dataString ```
if (response != null) {
val authResponseTokens = response.split(':')
if (authResponseTokens.size > 1) {
val authResponse = authResponseTokens[1]
blockstackSession().handlePendingSignIn(authResponse, { userData ->
if (userData.hasValue) {
// The user is now signed in!
runOnUiThread {
onSignIn(userData.value!!)
}
}
})
}
}
}
```
8. Add the convenience method to access the blockstack session. 8. Verify your final `MainActivity.kt` code looks like this:
```kotlin ```kotlin
fun blockstackSession() : BlockstackSession { package blockstack.id.user.hello
val session = _blockstackSession
if(session != null) {
return session
} else {
throw IllegalStateException("No session.")
}
}
```
9. Verify your final `MainActivity.kt` code looks like this: import android.content.Intent
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import kotlinx.android.synthetic.main.activity_main.\*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.blockstack.android.sdk.BlockstackSession
import org.blockstack.android.sdk.BlockstackSignIn
import org.blockstack.android.sdk.SessionStore
import org.blockstack.android.sdk.getBlockstackSharedPreferences
import org.blockstack.android.sdk.model.UserData
import org.blockstack.android.sdk.model.toBlockstackConfig
import org.blockstack.android.sdk.ui.SignInProvider
import org.blockstack.android.sdk.ui.showBlockstackConnect
```kotlin class MainActivity : AppCompatActivity(), SignInProvider {
class MainActivity : AppCompatActivity() {
private var _blockstackSession: BlockstackSession? = null private lateinit var blockstackSession: BlockstackSession
private lateinit var blockstackSignIn: BlockstackSignIn
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main) setContentView(R.layout.activity_main)
signInButton.isEnabled = false val appConfig =
"https://flamboyant-darwin-d11c17.netlify.app".toBlockstackConfig()
val scopes = arrayOf(Scope.StoreWrite) val sessionStore = SessionStore(getBlockstackSharedPreferences())
val config = "https://flamboyant-darwin-d11c17.netlify.com" blockstackSession = BlockstackSession(sessionStore, appConfig)
.toBlockstackConfig(scopes) blockstackSignIn = BlockstackSignIn(sessionStore, appConfig)
BlockstackSignIn.shouldLaunchInCustomTabs = false
_blockstackSession = BlockstackSession(this, config)
signInButton.isEnabled = true
signInButton.setOnClickListener { view: View -> signInButton.setOnClickListener {
blockstackSession().redirectUserToSignIn { showBlockstackConnect()
// only called on error
}
} }
if (intent?.action == Intent.ACTION_VIEW) { if (intent?.action == Intent.ACTION_VIEW) {
handleAuthResponse(intent) // handle the redirect from sign in
userDataTextView.text = "Signing in now ..."
lifecycleScope.launch(Dispatchers.IO) {
handleAuthResponse(intent)
}
} }
} }
private fun onSignIn(userData: UserData) { private fun onSignIn(userData: UserData) {
userDataTextView.text = "Signed in as ${userData.decentralizedID}" userDataTextView.text = "Signed in as ${userData.decentralizedID}"
signInButton.isEnabled = false signInButton.isEnabled = false
} }
override fun onNewIntent(intent: Intent?) { private suspend fun handleAuthResponse(intent: Intent) {
super.onNewIntent(intent) val authResponse = intent.data?.getQueryParameter("authResponse")
if (authResponse != null) {
if (intent?.action == Intent.ACTION_VIEW) { val userData = blockstackSession.handlePendingSignIn(authResponse)
handleAuthResponse(intent) if (userData.hasValue) {
} // The user is now signed in!
} runOnUiThread {
onSignIn(userData.value!!)
private fun handleAuthResponse(intent: Intent) { }
val response = intent.dataString
if (response != null) {
val authResponseTokens = response.split(':')
if (authResponseTokens.size > 1) {
val authResponse = authResponseTokens[1]
blockstackSession().handlePendingSignIn(authResponse, { userData ->
if (userData.hasValue) {
// The user is now signed in!
runOnUiThread {
onSignIn(userData.value!!)
}
}
})
} }
} }
} }
fun blockstackSession() : BlockstackSession { override fun provideBlockstackSignIn(): BlockstackSignIn {
val session = _blockstackSession return blockstackSignIn
if(session != null) {
return session
} else {
throw IllegalStateException("No session.")
}
} }
} }
``` ```
### Run the final app in the emulator ### Run the final app in the emulator
1. Choose **Run > Apply changes**. 1. Choose **Run > Apply changes**.
2. Choose **Run > Run app** in the emulator. 2. Choose **Run > Run app** in the emulator.
3. When you see the application open, choose **Sign in with Blockstack**. 3. When you see the application open, choose **Get Started**.
4. A small information is presented about the sign-in flow
The system prompts you how to open.
![](images/chrome-prompt.png) ![](images/connect-ui.png)
4. Choose **Chrome** and click **ALWAYS**. 5. Select **Get Started**
5. Move through the rest of the Chrome prompts. The system might prompt you how to select a browser.
The system presents you with your final application. 6. Work through the Blockstack prompts to login.
7. Blockstack redirects you to a web site. Open it with your Android app: Select **Hello Android** and **Always**
![](images/final-app.png) ![](images/connect-response.png)
6. Work through the Blockstack prompts to login. 8. The screen after the sign-in flow shows the owner address of the username that was entered during sign-in.
![](images/final-app.png)
## Where to go next ## Where to go next

257
_browser/blockstack_storage.md

@ -14,7 +14,7 @@ topics:
* TOC * TOC
{:toc} {:toc}
This tutorial does not teach you about authentication. That is covered in depth [in the hello-blockstack tutorial](hello-blockstack). This tutorial does not teach you about authentication. That is covered in depth [in the guide to Blockstack Connect](/develop/connect/get-started).
<!--TODO: authentication tutorial--> <!--TODO: authentication tutorial-->
<!--Strictly speaking not sure it is necessary here to send them out--> <!--Strictly speaking not sure it is necessary here to send them out-->
@ -32,8 +32,7 @@ to follow along, basic familiarity with React.js is helpful. When complete, the
- displaying statuses in the user profile - displaying statuses in the user profile
- looking up the profiles and statuses of other users - looking up the profiles and statuses of other users
The basic identity and storage services are provided by `blockstack.js`. To test The basic identity and storage services are provided by `blockstack.js`.
the application, you need to have already [registered a Blockstack ID](ids-introduction).
For this tutorial, you will use the following tools: For this tutorial, you will use the following tools:
@ -220,48 +219,36 @@ These are the `UserSession.putFile`, `UserSession.getFile`, and `lookupProfile`
1. Open the `src/components/Profile.js` file. 1. Open the `src/components/Profile.js` file.
2. Replace the initial state in the `constructor()` method so that it holds the key properties required by the app. 2. Replace the initial state in the component method so that it holds the key properties required by the app.
This code constructs a Blockstack `Person` object to hold the profile. Your constructor should look like this: This code constructs a Blockstack `Person` object to hold the profile. Your component should look like this:
```javascript ```javascript
constructor(props) { export const Profile = ({ userData, handleSignOut }) => {
super(props); const [newStatus, setNewStatus] = React.useState('');
const [statuses, setStatuses] = React.useState([]);
this.state = { const [statusIndex, setStatusIndex] = React.useState(0);
person: { const [isLoading, setLoading] = React.useState(false);
name() { const [username, setUsername] = React.useState(userData.username);
return 'Anonymous'; const [person, setPerson] = React.useState(new Person(userData.profile));
}, const { authOptions } = useConnect();
avatarUrl() { const { userSession } = authOptions;
return avatarFallbackImage; // ...
},
},
username: "",
newStatus: "",
statuses: [],
statusIndex: 0,
isLoading: false
};
} }
``` ```
3. Locate the `render()` method. 3. Modify the rendered result to add a text input and submit button to the
4. Modify the `render()` method to add a text input and submit button to the
by replacing it with the code below: by replacing it with the code below:
The following code renders the `person.name` and `person.avatarURL` The following code renders the `person.name` and `person.avatarURL`
properties from the profile on the display: properties from the profile on the display:
```javascript ```javascript
render() { export const Profile = ({ userData, handleSignOut }) => {
const { handleSignOut, userSession } = this.props; // ... state setup from before
const { person } = this.state;
const { username } = this.state;
return ( return (
!userSession.isSignInPending() && person ?
<div className="container"> <div className="container">
<div className="row"> <div className="row">
<div className="col-md-offset-3 col-md-6"> <div className="col-md-offset-3 col-md-6">
@ -289,15 +276,15 @@ These are the `UserSession.putFile`, `UserSession.getFile`, and `lookupProfile`
<div className="new-status"> <div className="new-status">
<div className="col-md-12"> <div className="col-md-12">
<textarea className="input-status" <textarea className="input-status"
value={this.state.newStatus} value={newStatus}
onChange={e => this.handleNewStatusChange(e)} onChange={handleNewStatus}
placeholder="Enter a status" placeholder="Enter a status"
/> />
</div> </div>
<div className="col-md-12"> <div className="col-md-12">
<button <button
className="btn btn-primary btn-lg" className="btn btn-primary btn-lg"
onClick={e => this.handleNewStatusSubmit(e)} onClick={handleNewStatusSubmit}
> >
Submit Submit
</button> </button>
@ -306,7 +293,7 @@ These are the `UserSession.putFile`, `UserSession.getFile`, and `lookupProfile`
</div> </div>
</div> </div>
</div> : null </div>
); );
} }
``` ```
@ -315,61 +302,36 @@ These are the `UserSession.putFile`, `UserSession.getFile`, and `lookupProfile`
user's Blockstack ID. To display this, your app must extract the ID from the user's Blockstack ID. To display this, your app must extract the ID from the
user profile data. user profile data.
Notice that the `userSession` property passed into our profile renderer contains 7. Add two methods in the `Profile` component to handle the status input events:
the `isSignInPending()` method which checks if a sign in operation is pending.
5. Locate the `componentWillMount()` method.
6. Add the `username` property below the `person` property.
You'll use the Blockstack `loadUserData()` method in our user session to access the `username`.
```javascript
componentWillMount() {
const { userSession } = this.props
this.setState({
person: new Person(userSession.loadUserData().profile),
username: userSession.loadUserData().username
});
}
```
7. Add two methods in the `Profile` class to handle the status input events:
```javascript ```javascript
handleNewStatusChange(event) { const handleNewStatus = (event) => {
this.setState({newStatus: event.target.value}) setNewStatus(event.target.value);
} }
handleNewStatusSubmit(event) { const handleNewStatusSubmit = async (event) => {
this.saveNewStatus(this.state.newStatus) await saveNewStatus(newStatus);
this.setState({ setNewStatus("");
newStatus: ""
})
} }
``` ```
8. Add a `saveNewStatus()` method to save the new statuses. 8. Add a `saveNewStatus()` method to save the new statuses.
```javascript ```javascript
saveNewStatus(statusText) { const saveNewStatus = async (statusText) => {
const { userSession } = this.props const _statuses = statuses
let statuses = this.state.statuses
let status = { let status = {
id: this.state.statusIndex++, id: statusIndex + 1,
text: statusText.trim(), text: statusText.trim(),
created_at: Date.now() created_at: Date.now()
} }
statuses.unshift(status) _statuses.unshift(status)
const options = { encrypt: false } const options = { encrypt: false }
userSession.putFile('statuses.json', JSON.stringify(statuses), options) await userSession.putFile('statuses.json', JSON.stringify(_statuses), options);
.then(() => { setStatuses(_statuses);
this.setState({ setStatusIndex(statusIndex + 1);
statuses: statuses
})
})
} }
``` ```
@ -396,13 +358,12 @@ Update `Profile.js` again.
```javascript ```javascript
<div className="col-md-12 statuses"> <div className="col-md-12 statuses">
{this.state.isLoading && <span>Loading...</span>} {isLoading && <span>Loading...</span>}
{this.state.statuses.map((status) => ( {statuses.map((status) => (
<div className="status" key={status.id}> <div className="status" key={status.id}>
{status.text} {status.text}
</div> </div>
) ))}
)}
</div> </div>
``` ```
This displays existing state. Your code needs to fetch statuses on page load. This displays existing state. Your code needs to fetch statuses on page load.
@ -410,34 +371,26 @@ Update `Profile.js` again.
4. Add a new method called `fetchData()` after the `saveNewStatus()` method. 4. Add a new method called `fetchData()` after the `saveNewStatus()` method.
```javascript ```javascript
fetchData() { const fetchData = async () => {
const { userSession } = this.props setLoading(true);
this.setState({ isLoading: true })
const options = { decrypt: false } const options = { decrypt: false }
userSession.getFile('statuses.json', options) const file = await userSession.getFile('statuses.json', options)
.then((file) => { const _statuses = JSON.parse(file || '[]')
var statuses = JSON.parse(file || '[]') setStatusIndex(_statuses.length);
this.setState({ setStatuses(_statuses);
person: new Person(userSession.loadUserData().profile), setLoading(false);
username: userSession.loadUserData().username,
statusIndex: statuses.length,
statuses: statuses,
})
})
.finally(() => {
this.setState({ isLoading: false })
})
} }
``` ```
By default, `getFile()` this method decrypts data; because the default `putFile()` encrypts it. In this case, the app shares statuses publicly. So, there is no need to decrypt. By default, `getFile()` this method decrypts data; because the default `putFile()` encrypts it. In this case, the app shares statuses publicly. So, there is no need to decrypt.
5. Call `fetchData()` from the `componentDidMount()` method. 5. Call `fetchData()` from the by using React's `useEffect` method, which will fetch data whenever the component's username state is changed..
```javascript ```javascript
// after setting up your component's state
componentDidMount() { React.useEffect(() => {
this.fetchData() fetchData();
} }, [username]);
``` ```
6. Save the file. 6. Save the file.
@ -578,8 +531,13 @@ process URL paths that contain the `.` (dot) character for example,
3. Add a single method to the `Profile` class that determines if the app is viewing the local user's profile or another user's profile. 3. Add a single method to the `Profile` class that determines if the app is viewing the local user's profile or another user's profile.
```javascript ```javascript
isLocal() { // Make sure you add this new prop!
return this.props.match.params.username ? false : true export const Profile = ({ userData, handleSignOut, match }) => {
// ...
const isLocal = () => {
return match.params.username ? false : true
}
// ...
} }
``` ```
@ -588,37 +546,25 @@ process URL paths that contain the `.` (dot) character for example,
4. Modify the `fetchData()` method like so: 4. Modify the `fetchData()` method like so:
```javascript ```javascript
fetchData() { const fetchData = async () => {
const { userSession } = this.props setLoading(true);
this.setState({ isLoading: true }) if (isLocal()) {
if (this.isLocal()) {
const options = { decrypt: false } const options = { decrypt: false }
userSession.getFile('statuses.json', options) const file = await userSession.getFile('statuses.json', options)
.then((file) => { const _statuses = JSON.parse(file || '[]')
var statuses = JSON.parse(file || '[]') setStatusIndex(_statuses.length);
this.setState({ setStatuses(_statuses);
person: new Person(userSession.loadUserData().profile), setLoading(false);
username: userSession.loadUserData().username,
statusIndex: statuses.length,
statuses: statuses,
})
})
.finally(() => {
this.setState({ isLoading: false })
})
} else { } else {
const username = this.props.match.params.username const username = match.params.username
lookupProfile(username) try {
.then((profile) => { const newProfile = await lookupProfile(username)
this.setState({ setPerson(new Person(newProfile));
person: new Person(profile), setUsername(username);
username: username } catch (error) {
}) console.log('Could not resolve profile');
}) }
.catch((error) => {
console.log('could not resolve profile')
})
} }
} }
``` ```
@ -630,40 +576,25 @@ process URL paths that contain the `.` (dot) character for example,
documentation](http://blockstack.github.io/blockstack.js/#getfile) for documentation](http://blockstack.github.io/blockstack.js/#getfile) for
details. details.
5. Add the following block to `fetchData()` right after the call to `lookupProfile(username)... catch((error)=>{..}` block: 5. Add the following block to `fetchData()` right after the call to `setUsername(username)` block:
```javascript ```javascript
const options = { username: username, decrypt: false } const options = { username: username, decrypt: false }
userSession.getFile('statuses.json', options) const file = await userSession.getFile('statuses.json', options)
.then((file) => { const _statuses = JSON.parse(file || '[]')
var statuses = JSON.parse(file || '[]') setStatusIndex(_statuses.length);
this.setState({ setStatuses(_statuses);
statusIndex: statuses.length, setLoading(false);
statuses: statuses
})
})
.catch((error) => {
console.log('could not fetch statuses')
})
.finally(() => {
this.setState({ isLoading: false })
})
``` ```
This fetches the user statuses. This fetches the user statuses.
Finally, you must conditionally render the logout button, status input textbox, and submit button so they don't show up when viewing another user's profile. Finally, you must conditionally render the logout button, status input textbox, and submit button so they don't show up when viewing another user's profile.
6. Replace the `render()` method with the following: 6. Replace the returned JSX in the `Profile` component with the following:
```javascript ```javascript
render() {
const { handleSignOut, userSession } = this.props;
const { person } = this.state;
const { username } = this.state;
return ( return (
!userSession.isSignInPending() && person ?
<div className="container"> <div className="container">
<div className="row"> <div className="row">
<div className="col-md-offset-3 col-md-6"> <div className="col-md-offset-3 col-md-6">
@ -680,28 +611,28 @@ process URL paths that contain the `.` (dot) character for example,
: 'Nameless Person' }</span> : 'Nameless Person' }</span>
</h1> </h1>
<span>{username}</span> <span>{username}</span>
{this.isLocal() && {isLocal() &&
<span> <span>
&nbsp;|&nbsp; &nbsp;|&nbsp;
<a onClick={ handleSignOut.bind(this) }>(Logout)</a> <a onClick={handleSignOut}>(Logout)</a>
</span> </span>
} }
</div> </div>
</div> </div>
</div> </div>
{this.isLocal() && {isLocal() &&
<div className="new-status"> <div className="new-status">
<div className="col-md-12"> <div className="col-md-12">
<textarea className="input-status" <textarea className="input-status"
value={this.state.newStatus} value={newStatus}
onChange={e => this.handleNewStatusChange(e)} onChange={handleNewStatus}
placeholder="What's on your mind?" placeholder="What's on your mind?"
/> />
</div> </div>
<div className="col-md-12 text-right"> <div className="col-md-12 text-right">
<button <button
className="btn btn-primary btn-lg" className="btn btn-primary btn-lg"
onClick={e => this.handleNewStatusSubmit(e)} onClick={handleNewStatusSubmit}
> >
Submit Submit
</button> </button>
@ -709,19 +640,17 @@ process URL paths that contain the `.` (dot) character for example,
</div> </div>
} }
<div className="col-md-12 statuses"> <div className="col-md-12 statuses">
{this.state.isLoading && <span>Loading...</span>} {isLoading && <span>Loading...</span>}
{this.state.statuses.map((status) => ( {statuses.map((status) => (
<div className="status" key={status.id}> <div className="status" key={status.id}>
{status.text} {status.text}
</div> </div>
) ))}
)}
</div> </div>
</div> </div>
</div> </div>
</div> : null </div>
); );
}
``` ```
This checks to ensure that users are viewing their own profile, by wrapping the **Logout** button and inputs with the `{isLocal() && ...}` condition. This checks to ensure that users are viewing their own profile, by wrapping the **Logout** button and inputs with the `{isLocal() && ...}` condition.

BIN
_browser/images/display-complete.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

BIN
_browser/images/initial-app.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

BIN
_browser/images/login-choice.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

BIN
_browser/images/login-no-auth.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

BIN
_browser/images/login.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
_browser/images/login.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

BIN
_browser/images/make-a-list.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

BIN
_browser/images/multi-player-storage-status.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

BIN
_browser/images/multiple-lists.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

BIN
_browser/images/network-connections.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
_browser/images/publish-data-perm.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

BIN
_browser/images/saving-status.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

BIN
_browser/images/todo-app.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

BIN
_browser/images/todo-sign-in.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

28
_browser/todo-list.md

@ -189,19 +189,27 @@ in and sign out is handled in each of these files:
| `components/Signin.js ` | Code for the initial sign on page. | | `components/Signin.js ` | Code for the initial sign on page. |
| `components/Profile.js` | Application data storage and user sign out. | | `components/Profile.js` | Application data storage and user sign out. |
The `src/components/App.js` code configures an `AppConfig` object and then uses this to create a `UserSession`. Then, the application calls a [`redirectToSignIn()`](https://blockstack.github.io/blockstack.js#redirectToSignIn) function which generates the `authRequest` and redirects the user to the Blockstack authenticator: <!-- The `src/components/App.js` code configures an `AppConfig` object and then uses this to create a `UserSession`. Then, the application calls a [`redirectToSignIn()`](https://blockstack.github.io/blockstack.js#redirectToSignIn) function which generates the `authRequest` and redirects the user to the Blockstack authenticator: -->
```js The `src/components/App.js` code configures a `UserSession` and other `authOptions`, which are passed to the `Connect` component. The `Connect` component acts as a "provider" for the rest of your application, and essentially creates a re-usable configuration for you.
...
const userSession = new UserSession({ appConfig })
export default class App extends Component { In the `src/components/Signin.js` component, we are then calling the `useConnect` hook. This hook returns many helper functions, one of which is `doOpenAuth`. Calling this method will being the authentication process. First, it injects a modal into your application, which acts as a way of "warming up" your user to Blockstack authentication. When the user continues, they are redirected to the Blockstack authenticator, where they can finish signing up.
handleSignIn(e) { ```js
e.preventDefault(); import React from 'react';
userSession.redirectToSignIn(); import { useConnect } from '@blockstack/connect';
}
... export const Signin = () => {
const { doOpenAuth } = useConnect();
return (
<button
onClick={() => doOpenAuth()}
>
Sign In with Blockstack
</button>
)
};
``` ```
Once the user authenticates, the application handles the `authResponse` in the `src/components/Profile.js` file. : Once the user authenticates, the application handles the `authResponse` in the `src/components/Profile.js` file. :

19
_common/core_ref.md

@ -1,5 +1,18 @@
--- ---
layout: externalurl layout: core
redirect_url: https://core.blockstack.org/ title: "Stacks Blockchain APIs"
title: "Stacks Node API" description: Interacting with the Stacks 2.0 Blockchain
permalink: /:collection/:path.html
--- ---
With the launch of Stacks 2.0, a new version of the Blockstack blockchain was released. There are two ways of interacting with the blockchain, either using the Stacks Blockchain API or by making RPC calls to a Stacks Core directly.
## Stacks Core API
The Stacks 2.0 blockchain's Rust implementation exposes RPC endpoints (in JSON format), which can be used to interface with the Stacks blockchain. [You can find the RPC API references here](https://docs.blockstack.org/core/smart/rpc-api.html).
## Stacks Blockchain API
The Stacks Blockchain API was built to maintain pageable materialized views of the Stacks 2.0 Blockchain. It is a server that exposes a RESTful JSON API, hosted by PBC. It introduces aidditonal functionality (e.g. get all transactions), as well as proxies calls directly to Stacks Node. [You can find the OpenAPI specification and documentation here](https://blockstack.github.io/stacks-blockchain-sidecar/).
*Note: Using this API requires you to trust the server, but provides a faster onboarding experience. It also addresses performance issues for which querying a node itself would be too slow or difficult.*
{% include note.html content="If you are looking for the Stacks 1.0 RPC endpoint references, please follow this <a href='https://core.blockstack.org/'>link</a>." %}

24
_core/smart/cli-wallet-quickstart.md

@ -22,13 +22,14 @@ To install the Blockstack CLI, run the following command in terminal.
First, we are going to generate a new wallet for Testnet. To generate a wallet use the `make_keychain` command with the `-t` option for Testnet. First, we are going to generate a new wallet for Testnet. To generate a wallet use the `make_keychain` command with the `-t` option for Testnet.
``` ```
$ blockstack-cli make_keychain -t $ blockstack make_keychain -t
{ {
"mnemonic": "private unhappy random runway boil scissors remove harvest fatigue inherit inquiry still before mountain pet tail mad accuse second milk client rebuild salt chase", "mnemonic": "private unhappy random runway boil scissors remove harvest fatigue inherit inquiry still before mountain pet tail mad accuse second milk client rebuild salt chase",
"keyInfo": { "keyInfo": {
"privateKey": "381314da39a45f43f45ffd33b5d8767d1a38db0da71fea50ed9508e048765cf301", "privateKey": "381314da39a45f43f45ffd33b5d8767d1a38db0da71fea50ed9508e048765cf301",
"address": "ST1BG7MHW2R524WMF7X8PGG3V45ZN040EB9EW0GQJ", "address": "ST1BG7MHW2R524WMF7X8PGG3V45ZN040EB9EW0GQJ",
"btcAddress": "n4X37UmRZYk9HawtS1w4xRtqJWhByxiz3c",
"index": 0 "index": 0
} }
} }
@ -54,18 +55,22 @@ Once the faucet transaction has been broadcasted, you will need to wait for the
Once you’ve requested Testnet Stacks tokens from the faucet, you can check the balance of your account using the following command. Once you’ve requested Testnet Stacks tokens from the faucet, you can check the balance of your account using the following command.
``` ```
$ blockstack-cli balance ST1BG7MHW2R524WMF7X8PGG3V45ZN040EB9EW0GQJ -H "http://neon.blockstack.org:20443" $ blockstack balance ST1BG7MHW2R524WMF7X8PGG3V45ZN040EB9EW0GQJ -t
{ {
"balance": "1000", "balance": "10000",
"nonce": 0 "nonce": 0
} }
``` ```
Using the `-H` option, we specify a connection to the testnet node at `http://neon.blockstack.org:20443` By default, using the `-t` flag causes the CLI to connect to the neon testnet node at `http://neon.blockstack.org:20443`.
To specify a node to connect to, add the `-H` flag followed by the URL of the node `"http://localhost:20443"`. This flag can be used with all commands in this guide.
Verify that your account has tokens before continuing to the next step to send tokens. Verify that your account has tokens before continuing to the next step to send tokens.
Take note that the nonce for the account is `0`. We will be using this number in the next step.
## Sending Tokens ## Sending Tokens
In order to send tokens, we will need the 5 parameters below. In order to send tokens, we will need the 5 parameters below.
@ -83,22 +88,21 @@ In order to send tokens, we will need the 5 parameters below.
Once we have the parameters, we can use the `send_tokens` command: Once we have the parameters, we can use the `send_tokens` command:
``` ```
$ blockstack-cli send_tokens ST1WZ69T99RHQMQX3D91ZH2R37GV5NK8KDS5D5VDZ 1000 200 0 381314da39a45f43f45ffd33b5d8767d1a38db0da71fea50ed9508e048765cf301 -t -T "http://neon.blockstack.org:20443/v2/transactions" $ blockstack send_tokens ST2KMMVJAB00W5Z6XWTFPH6B13JE9RJ2DCSHYX0S7 1000 200 0 381314da39a45f43f45ffd33b5d8767d1a38db0da71fea50ed9508e048765cf301 -t
d32de0d66b4a07e0d7eeca320c37a10111c8c703315e79e17df76de6950c622c d32de0d66b4a07e0d7eeca320c37a10111c8c703315e79e17df76de6950c622c
``` ```
With this command we’re sending 1000 microstacks to the Stacks address `ST1WZ69T99RHQMQX3D91ZH2R37GV5NK8KDS5D5VDZ`. With this command we’re sending 1000 microstacks to the Stacks address `ST2KMMVJAB00W5Z6XWTFPH6B13JE9RJ2DCSHYX0S7`.
We set the fee rate to `200` microstacks. We set the fee rate to `200` microstacks. If you're not sure how much your transaction will cost. You can add the `-e` flag to estimate the transaction fee needed to get processed by the network, without broadcasting your transaction.
The nonce is set to `0` for this transaction, since it will be the first transaction we send from this account. The nonce is set to `0` for this transaction, since it will be the first transaction we send from this account. For subsequent transactions, you will need to increment this number by `1` each time. You can check the current nonce for the account using the `balance` command.
Finally, the last parameter is the private key from earlier. `381314da39a45f43f45ffd33b5d8767d1a38db0da71fea50ed9508e048765cf301` Finally, the last parameter is the private key from earlier. `381314da39a45f43f45ffd33b5d8767d1a38db0da71fea50ed9508e048765cf301`
Once again, we’re using the `-t` option to indicate that this is a Testnet transaction, so it should be broadcasted to Testnet. Once again, we’re using the `-t` option to indicate that this is a Testnet transaction, so it should be broadcasted to Testnet.
Using the `-T` option, we specify that we want to broadcast to the testnet node at `http://neon.blockstack.org:20443/v2/transactions`
If valid, your transaction will now be broadcasted to the network and you will see the transaction ID displayed on the screen. If valid, your transaction will now be broadcasted to the network and you will see the transaction ID displayed on the screen.
To view the raw transaction without broadcasting, add the `-x` flag to your command.

93
_core/smart/neon-node.md

@ -1,93 +0,0 @@
---
layout: smart
description: "Run a Stacks Testnet Node"
permalink: /:collection/:path.html
---
# Running a Neon Testnet Node
{:.no_toc}
"Neon" is phase 1 of the Stacks 2.0 testnet. In Neon, you can run a node and connect it to a public network. This guide will walk you through downloading and running your own node in the Neon network.
* TOC
{:toc}
### Prerequisites
Note: If you use Linux, you may need to manually install [`libssl-dev`](https://wiki.openssl.org/index.php/Libssl_API) and other packages. In your command line, run the following to get all packages:
```bash
sudo apt-get install build-essential cmake libssl-dev pkg-config
```
### Download and install the `stacks-blockchain` repository
The first step is to ensure that you have Rust installed. If you are using macOS, Linux, or another Unix-like OS, run the following. If you are on a different OS, follow the [official Rust installation guide](https://www.rust-lang.org/tools/install).
```bash
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
```
If Rust is already installed, you might see this prompt. Select 'Proceed with Installation' to make sure you have the latest version installed.
![rustup prompt](/core/images/rust-install.png)
In case you just installed Rust, you will be prompted to run the following command to make the `cargo` command available:
```bash
source $HOME/.cargo/env
```
Next, clone this repository:
```bash
git clone https://github.com/blockstack/stacks-blockchain.git
cd stacks-blockchain
```
Install the Stacks node by running:
```bash
cargo install --path ./testnet
```
### Run your node
You're all set to run a node that connects to the Neon network.
Back in the command line, run:
```bash
stacks-node neon
```
The first time you run this, you'll see some logs indicating that the Rust code is being compiled. Once that's done, you should see some logs that look something like the this:
```
INFO [1588108047.585] [src/chainstate/stacks/index/marf.rs:732] First-ever block 0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206
```
Awesome! Your node is now connected to the Neon network. Your node will receive new blocks when they are produced, and you can use your [node's RPC API](/core/smart/rpc-api) to send transactions, fetch information for contracts and accounts, and more.
### Creating an optimized binary
The steps above are great for trying to run a node temporarily. If you want to host a node on a server somewhere, you might want to generate an optimized binary. To do so, use the same configuration as above, but run:
```bash
cd testnet
cargo build --release --bin stacks-node
```
The above code will compile an optimized binary. To use it, run:
```bash
cd ..
./target/release/stacks-node start --config=./testnet/conf/neon-follower-conf.toml
```
### Enable debug logging
In case you are running into issues or would like to see verbose logging, you can run your node with debug logging enabled. In the command line, run:
```bash
BLOCKSTACK_DEBUG=1 stacks-node neon
```

20
_core/smart/overview.md

@ -6,20 +6,18 @@ permalink: /:collection/:path.html
# Introduction to Clarity # Introduction to Clarity
{:.no_toc} {:.no_toc}
Clarity is a smart contracting language for use with the Stacks 2.0 blockchain. It supports programmatic control over digital assets. Clarity is a programming language for writing smart contracts on the Stacks 2.0 blockchain. It supports programmatic control over digital assets.
* TOC * TOC
{:toc} {:toc}
## Smart contracts ## Smart contracts
Smart contracts encode and enforce the rules for modifying a particular set of data as shared among people and entities who don't necessarily trust each other. For example, a smart contract could allow creators to add a new tv show to a streaming service, but require that users first pay for a decryption key before viewing it. To do this, the smart contract could be written such that the movie creator had to publicly disclose the decryption key in order to receive payment. The movie creators would therefore be incentivized to work on tv show episodes that people want to watch, based on the viewers' upfront payments that are locked-in until the creator releases the episode and the decryption key. Smart contracts encode and enforce rules for modifying a particular set of data that is shared among people and entities who don't necessarily trust each other. For example, a smart contract can hold funds in escrow until multiple parties agree to release them, create its own ledger and keep track of its own novel tokens (fungible or non-fungible), and even help make supply chains more transparent.
Because smart contracts run on top of a blockchain, anyone can query them, and anyone can submit transactions to execute them. A smart contract execution can result in new transactions being written to the blockchain. Because smart contracts are programs that exist in a blockchain, anyone can query them, and anyone can submit transactions to execute them. A smart contract execution can result in new transactions being written to the blockchain.
Apps use the blockchain to manage a global state that is visible to the public. To get back to the streaming service example, the global state could include a list of user that paid for a specific tv show episode. This is possible because the blockchain stores the history of all accepted transactions. Apps can take advantage of smart contracts to manage a global state that is visible to the public. Anyone can audit the blockchain in order to independently verify that an app's global shared state has been managed correctly according to the smart contract's rules.
Anyone can audit the blockchain in order to independently verify that an app's global shared state has been managed correctly according to the smart contract's rules.
## Use cases ## Use cases
@ -44,12 +42,12 @@ A Clarity smart contract is composed of two parts &mdash; a data space and a set
Note some of the key Clarity language rules and limitations. Note some of the key Clarity language rules and limitations.
* The only atomic types are booleans, integers, fixed length buffers, and principals * The only primitive types are booleans, integers, buffers, and principals
* Recursion is illegal and there is no lambda function. * Recursion is illegal and there are no anonymous functions.
* Looping may only be performed via `map`, `filter`, or `fold` * Looping may only be performed via `map`, `filter`, or `fold`
* There is support for lists of the atomic types, however, the only variable length lists in the language appear as function inputs; There is no support for list operations like append or join. * There is support for lists, however, the only variable length lists in the language appear as function inputs; There is no support for list operations like append or join.
* Variables are created via `let` binding and there is no support for mutating functions like `set`. * Variables are immutable.
## Learning Clarity ## Learning Clarity
You can try a [Hello World tutorial](tutorial.html) or jump right into the [language reference](clarityRef.html). You can try a [Hello World tutorial](tutorial.html) or jump right into the [language reference](clarityRef.html).

209
_core/smart/principals.md

@ -36,6 +36,32 @@ is a multiple of 10, otherwise returning a 400 error code.
(err u400))) (err u400)))
``` ```
Clarity provides an additional variable to help smart contracts
authenticate a transaction sender. The keyword `contract-caller`
returns the principal that _called_ the current contract. If an
inter-contract call occurred, `contract-caller` returns the last
contract in the stack of callers. For example, suppose there are three
contracts A, B, and C, each with an `invoke` function such that
`A::invoke` calls `B::invoke` and `B::invoke` calls `C::invoke`.
When a user Bob issues a transaction that calls `A::invoke`, the value
of `contract-caller` in each successive invoke function's body would change:
```
in A::invoke, contract-caller = Bob
in B::invoke, contract-caller = A
in C::invoke, contract-caller = B
```
This allows contracts to make assertions and perform authorization
checks using not only the `tx-sender` (which in this example, would
always be "Bob"), but also using the `contract-caller`. This could be
used to ensure that a particular function is only ever called directly
and never called via an inter-contract call (by asserting that
`tx-sender` and `contract-caller` are equal). We provide an example of
a two different types of authorization checks in the rocket ship
example below.
## Smart contracts as principals ## Smart contracts as principals
Smart contracts themselves are principals and are represented by the Smart contracts themselves are principals and are represented by the
@ -100,3 +126,186 @@ In this example, the public function `claim-from-faucet`:
Unlike other principals, there is no private key associated with a Unlike other principals, there is no private key associated with a
smart contract. As it lacks a private key, a Clarity smart contract smart contract. As it lacks a private key, a Clarity smart contract
cannot broadcast a signed transaction on the blockchain. cannot broadcast a signed transaction on the blockchain.
## Example: Authorization checks
The interactions between `tx-sender`, `contract-caller` and
`as-contract` are subtle, but are important when performing
authorization checks in a contract. In this example contract,
we'll show two different kinds of authorization checks
a contract may wish to perform, and then walk through how
different ways in which contract functions may be called
will pass or fail those checks.
This contract defines a "rocket-ship" non-fungible-token
that a principal may own and manage the authorized pilots.
Pilots are principals that are allowed to "fly" the
rocket ship.
This contract performs two different authorization checks:
1. Before a ship is allowed to fly, the contract checks whether or not
the transaction was created and signed by an authorized pilot. A
pilot, could, for example, call another contract, which then calls
the "fly-ship" public function on the pilot's behalf.
2. Before modifying the allowed-pilots for a given rocket ship, the
contract checks that the transaction was signed by the owner of the
rocket ship. Furthermore, the contract requires that this function
be called _directly_ by the ship's owner, rather than through a
inter-contract-call.
The second type of check is more restrictive than the first check,
and is helpful for guarding very sensitive routines --- it
protects users from unknowingly calling a function on a malicious
contract that subsequently tries to call sensitive functions on
another contract.
```scheme
;;
;; rockets-base.clar
;;
(define-non-fungible-token rocket-ship uint)
;; a map from rocket ships to their allowed
;; pilots
(define-map allowed-pilots
((rocket-ship uint)) ((pilots (list 10 principal))))
;; implementing a contains function via fold
(define-private (contains-check
(y principal)
(to-check { p: principal, result: bool }))
(if (get result to-check)
to-check
{ p: (get p to-check),
result: (is-eq (get p to-check) y) }))
(define-private (contains (x principal) (find-in (list 10 principal)))
(get result (fold contains-check find-in
{ p: x, result: false })))
(define-read-only (is-my-ship (ship uint))
(is-eq (some tx-sender) (nft-get-owner? rocket-ship ship)))
;; this function will print a message
;; (and emit an event) if the tx-sender was
;; an authorized flyer.
;;
;; here we use tx-sender, because we want
;; to allow the user to let other contracts
;; fly the ship on behalf of users
(define-public (fly-ship (ship uint))
(let ((pilots (default-to
(list)
(get pilots (map-get? allowed-pilots { rocket-ship: ship })))))
(if (contains tx-sender pilots)
(begin (print "Flew the rocket-ship!")
(ok true))
(begin (print "Tried to fly without permission!")
(ok false)))))
;;
;; Authorize a new pilot.
;;
;; here we want to ensure that this function
;; was called _directly_ by the user by
;; checking that tx-sender and contract-caller are equal.
;; if any other contract is in the call stack, contract-caller
;; would be updated to a different principal.
;;
(define-public (authorize-pilot (ship uint) (pilot principal))
(begin
;; sender must equal caller: an intermediate contract is
;; not issuing this call.
(asserts! (is-eq tx-sender contract-caller) (err u1))
;; sender must own the rocket ship
(asserts! (is-eq (some tx-sender)
(nft-get-owner? rocket-ship ship)) (err u2))
(let ((prev-pilots (default-to
(list)
(get pilots (map-get? allowed-pilots { rocket-ship: ship })))))
;; don't add a pilot already in the list
(asserts! (not (contains pilot prev-pilots)) (err u3))
;; append to the list, and check that it is less than
;; the allowed maximum
(match (as-max-len? (append prev-pilots pilot) u10)
next-pilots
(ok (map-set allowed-pilots {rocket-ship: ship} {pilots: next-pilots}))
;; too many pilots already
(err u4)))))
```
### Extending functionality: Multi-flyer contract
The authorization scheme for `fly-ship` allows pilots to fly rocket-ships from
other contracts. This allows other contracts to provide new functionality built
around calling that function.
For example, we can create a contract that calls `fly-ship`
for multiple rocket-ships in a single transaction:
```
;;
;; rockets-multi.clar
;;
(define-private (call-fly (ship uint))
(unwrap! (contract-call? .rockets-base fly-ship ship) false))
;; try to fly all the ships, returning a list of whether
;; or not we were able to fly the supplied ships
(define-public (fly-all (ships (list 10 uint)))
(ok (map call-fly ships)))
```
### Authorization for Contract-Owned Assets
The check in `authorize-pilot` protects users from malicious contracts,
but how would such a scheme support contract-owned assets? This is what the
`as-contract` function is used for. The `as-contract` function executes
the supplied closure as if the sender of the transaction was the current
contract, rather than the user -- it does this by updating `tx-sender`
to the current contract principal. We can use this to, for example, create
a smart contract rocket-ship-line:
```
;;
;; rockets-ship-line.clar
;;
(define-constant line-ceo 'SP19Y6GRV9X778VFS1V8WT2H502WFK33XZXADJWZ)
(define-data-var employed-pilots (list 10 principal) (list))
;; This function will:
;; * check that it is called by the line-ceo
;; * check that the rocket is owned by the contract
;; * authorize each employed pilot to the ship
(define-public (add-managed-rocket (ship uint))
(begin
;; only the ceo may call this function!
(asserts! (is-eq tx-sender contract-caller line-ceo) (err u1))
;; start executing as the contract
(as-contract (begin
;; make sure the contract owns the ship!
(asserts! (contract-call? .rockets-base is-my-ship ship) (err u2))
;; register all of our pilots on the ship
(add-pilots-to ship)))))
;; add all the pilots to a ship using fold --
;; the fold checks the return type of previous calls,
;; skipping subsequent contract-calls if one fails.
(define-private (add-pilot-via-fold (pilot principal) (prior-result (response uint uint)))
(let ((ship (try! prior-result)))
(try! (contract-call? .rockets-base authorize-pilot ship pilot))
(ok ship)))
(define-private (add-pilots-to (ship uint))
(fold add-pilot-via-fold (var-get employed-pilots) (ok ship)))
```
In order for the contract to add each pilot to a new ship, the contract must
call `authorize-pilot`. However, the contract wants to perform this action on
behalf of a ship the _contract_ owns, not the transaction sender. To do this,
the contract uses `as-contract`.

2
_core/smart/sdk-quickstart.md

@ -174,7 +174,7 @@ npm run test
✓ should have a valid syntax ✓ should have a valid syntax
deploying an instance of the contract deploying an instance of the contract
✓ should print hello world message ✓ should print hello world message
✓ should echo number ✓ should echo number (54ms)
3 passing (182ms) 3 passing (182ms)

167
_core/smart/testnet-node.md

@ -0,0 +1,167 @@
---
layout: smart
description: "Run a Stacks Testnet Node"
permalink: /:collection/:path.html
redirect_from:
- /core/smart/neon-node.html
---
# Running a Testnet Node
{:.no_toc}
The Stacks 2.0 testnet is currently in development. As part of the testnet, you can run a node and connect it to a public network. This guide will walk you through downloading and running your own node in the testnet network.
* TOC
{:toc}
### Prerequisites
Note: If you use Linux, you may need to manually install [`libssl-dev`](https://wiki.openssl.org/index.php/Libssl_API) and other packages. In your command line, run the following to get all packages:
```bash
sudo apt-get install build-essential cmake libssl-dev pkg-config
```
Ensure that you have Rust installed. If you are using macOS, Linux, or another Unix-like OS, run the following. If you are on a different OS, follow the [official Rust installation guide](https://www.rust-lang.org/tools/install).
```bash
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
```
If Rust is already installed, you might see this prompt. Select 'Proceed with Installation' to make sure you have the latest version installed.
![rustup prompt](/core/images/rust-install.png)
In case you just installed Rust, you will be prompted to run the following command to make the `cargo` command available:
```bash
source $HOME/.cargo/env
```
### Download and install the `stacks-blockchain` repository
Next, clone this repository:
```bash
git clone https://github.com/blockstack/stacks-blockchain.git
cd stacks-blockchain
```
Install the Stacks node by running:
```bash
cargo install --path ./testnet/stacks-node
```
### Run your node
You're all set to run a node that connects to the testnet network.
Back in the command line, run:
```bash
stacks-node argon
```
The first time you run this, you'll see some logs indicating that the Rust code is being compiled. Once that's done, you should see some logs that look something like the this:
```
INFO [1588108047.585] [src/chainstate/stacks/index/marf.rs:732] First-ever block 0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206
```
Awesome! Your node is now connected to the testnet network. Your node will receive new blocks when they are produced, and you can use your [node's RPC API](/core/smart/rpc-api) to send transactions, fetch information for contracts and accounts, and more.
### Running a miner
Once you've followed the above steps to run a node, it's only a few more steps to run a proof-of-burn miner on the testnet.
First, we need to generate a keychain. With this keychain, we'll get some testnet BTC from a faucet, and then use that BTC to start mining.
To get a keychain, the simplest way is to use the `blockstack-cli`. We'll use the `make_keychain` command, and pass `-t` to indicate that we want a testnet keychain.
```bash
npx blockstack-cli@1.1.0-beta.1 make_keychain -t
```
After this runs, you'll probably see some installation logs, and at the end you should see some JSON that looks like this:
```json
{
"mnemonic": "exhaust spin topic distance hole december impulse gate century absent breeze ostrich armed clerk oak peace want scrap auction sniff cradle siren blur blur",
"keyInfo": {
"privateKey": "2033269b55026ff2eddaf06d2e56938f7fd8e9d697af8fe0f857bb5962894d5801",
"address": "STTX57EGWW058FZ6WG3WS2YRBQ8HDFGBKEFBNXTF",
"btcAddress": "mkRYR7KkPB1wjxNjVz3HByqAvVz8c4B6ND",
"index": 0
}
}
```
We need to get some testnet BTC to that address. Grab the `btcAddress` field, and head over to [the Stacks testnet website](https://testnet.blockstack.org/faucet). In the BTC faucet section, past in your `btcAddress`, and submit. You'll be sent 0.5 testnet BTC to that address. **Don't lose this information** - we'll need to use the `privateKey` field later on.
Now, we need to configure out node to use this Bitcoin keychain. In the `stacks-blockchain` folder, create a new file called `testnet/stacks-node/conf/testnet-miner-conf.toml`.
Paste in the following configuration:
```toml
[node]
rpc_bind = "0.0.0.0:20443"
p2p_bind = "0.0.0.0:20444"
bootstrap_node = "048dd4f26101715853533dee005f0915375854fd5be73405f679c1917a5d4d16aaaf3c4c0d7a9c132a36b8c5fe1287f07dad8c910174d789eb24bdfb5ae26f5f27@argon.blockstack.org:20444"
# Enter your private key here!
seed = "replace-with-your-private-key"
miner = true
[burnchain]
chain = "bitcoin"
mode = "argon"
peer_host = "argon.blockstack.org"
rpc_port = 18443
peer_port = 18444
[[mstx_balance]]
address = "STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6"
amount = 10000000000000000
[[mstx_balance]]
address = "ST11NJTTKGVT6D1HY4NJRVQWMQM7TVAR091EJ8P2Y"
amount = 10000000000000000
[[mstx_balance]]
address = "ST1HB1T8WRNBYB0Y3T7WXZS38NKKPTBR3EG9EPJKR"
amount = 10000000000000000
[[mstx_balance]]
address = "STRYYQQ9M8KAF4NS7WNZQYY59X93XEKR31JP64CP"
amount = 10000000000000000
```
Now, grab your `privateKey` from earlier, when you ran the `make_keychain` command. Replace the `seed` field with your private key. Save and close this configuration file.
To run your miner, run this in the command line:
```bash
stacks-node start --config=./testnet/stacks-node/conf/testnet-miner-conf.toml
```
Your node should start. It will take some time to sync, and then your miner will be running!
### Creating an optimized binary
The steps above are great for trying to run a node temporarily. If you want to host a node on a server somewhere, you might want to generate an optimized binary. To do so, use the same configuration as above, but run:
```bash
cd testnet/stacks-node
cargo build --release --bin stacks-node
```
The above code will compile an optimized binary. To use it, run:
```bash
cd ../..
./target/release/stacks-node start --config=./testnet/conf/argon-follower-conf.toml
```
### Enable debug logging
In case you are running into issues or would like to see verbose logging, you can run your node with debug logging enabled. In the command line, run:
```bash
BLOCKSTACK_DEBUG=1 stacks-node argon
```

14
_core/smart/tutorial-counter.md

@ -37,7 +37,6 @@ You have to select a template and a name for your local folder. For the counter
```bash ```bash
? Template - one of [hello-world, counter]: counter ? Template - one of [hello-world, counter]: counter
? Project name: (clarity-counter)
``` ```
Finally, the project dependencies are installed and your project is ready for development. Because you already completed the [Hello World tutorial](tutorial.html), the project structure is familiar to you. The main difference is that we have additional tests for a new counter smart contract. Finally, the project dependencies are installed and your project is ready for development. Because you already completed the [Hello World tutorial](tutorial.html), the project structure is familiar to you. The main difference is that we have additional tests for a new counter smart contract.
@ -65,6 +64,10 @@ counter contract test suite
1 passing (734ms) 1 passing (734ms)
3 failing 3 failing
... # error details
npm ERR! Test failed. See above for more details.
``` ```
It looks like we see some failed tests! That is on purpose - we will implement the new smart contract in the next steps! After every step in this tutorial, we will rerun the tests to ensure we're on the right track. It looks like we see some failed tests! That is on purpose - we will implement the new smart contract in the next steps! After every step in this tutorial, we will rerun the tests to ensure we're on the right track.
@ -89,7 +92,7 @@ Let's get familiar with the tests to understand what the new smart contract shou
The file was already created during the project setup. The file was already created during the project setup.
2. With the editor of your choice, open the file and add the following lines of code: 2. With the editor of your choice, open `contracts/counter.clar` and add the following lines of code:
```cl ```cl
(define-data-var counter int 0) (define-data-var counter int 0)
@ -151,16 +154,19 @@ Let's get familiar with the tests to understand what the new smart contract shou
```cl ```cl
(define-data-var counter int 0) (define-data-var counter int 0)
(define-public (get-counter)
(ok (var-get counter)))
(define-public (increment) (define-public (increment)
(begin (begin
(var-set counter (+ (var-get counter) 1)) (var-set counter (+ (var-get counter) 1))
(ok (var-get counter)))) (ok (var-get counter))))
(define-public (decrement) (define-public (decrement)
(begin (begin
(var-set counter (- (var-get counter) 1)) (var-set counter (- (var-get counter) 1))
(ok (var-get counter)))) (ok (var-get counter))))
(define-public (get-counter)
(ok (var-get counter)))
``` ```
With the completion of this tutorial, you ... With the completion of this tutorial, you ...

4
_core/smart/tutorial.md

@ -44,15 +44,13 @@ In this step, you initialize a starter project for Clarity development:
```bash ```bash
? Template - one of [hello-world, counter]: (hello-world) ? Template - one of [hello-world, counter]: (hello-world)
? Project name: (clarity-hello-world)
``` ```
Finally, after the project dependencies have been installed, your project is ready for development. Finally, after the project dependencies have been installed, your project is ready for development.
3. The project is located in a new folder, `clarity-hello-world` by default. Jump into the folder and have a look at the file structure: 3. The project resources are created in your current folder. Have a look at the project structure:
```bash ```bash
cd clarity-hello-world
ls ls
``` ```

2
_develop/cliDocs.md

@ -2,6 +2,6 @@
layout: learn layout: learn
permalink: /:collection/:path.html permalink: /:collection/:path.html
--- ---
# blockstack_cli reference # Blockstack CLI Reference
{% include commandline.md %} {% include commandline.md %}

47
_develop/connect/get-started.md

@ -5,11 +5,6 @@ permalink: /:collection/:path.html
# Guide to Blockstack Connect # Guide to Blockstack Connect
{:.no_toc} {:.no_toc}
Blockstack Connect is a Javascript library for integrating your application with Stacks v2. With Connect, you get some big benefits:
<!-- - -->
* TOC * TOC
{:toc} {:toc}
@ -142,4 +137,44 @@ Then, you can use API methods under the `blockstackConnect` global variable:
```javascript ```javascript
const authOptions = { /** See docs above for options */ }; const authOptions = { /** See docs above for options */ };
blockstackConnect.showBlockstackConnect(authOptions); blockstackConnect.showBlockstackConnect(authOptions);
``` ```
## Handling redirect fallbacks
Connect is built to use popups with the [`window.postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage) API, which provides a much better and seamless user experience. However, there are times when this flow can fail. For example, the popup may be blocked, or the `window.postMessage` API might not work properly (which often happens on mobile browsers).
To make sure your app handles this gracefully, you'll need to handle the case where authentication is performed through regular HTTP redirects. With redirects, your users will be sent back to your app at a URL like:
`${authOptions.redirectTo}?authResponse=....`
To finalize authentication with this flow, you'll need to utilize the `UserSession` methods `isSignInPending()` and `handlePendingSignIn()`. For more information, check out the [blockstack.js API reference](https://blockstack.github.io/blockstack.js/).
```js
const userSession = new UserSession(appConfig);
// ... call this code on page load
if (userSession.isSignInPending()) {
const userData = await userSession.handlePendingSignIn();
// your user is now logged in.
}
```
## Design Guidance
Blockstack is valuable to users, but it can also be a barrier to those unfamiliar with Blockstack. The following guidelines serve to remedy that and help you onboard as many new users as you can.
### Delay Blockstack onboarding as long as possible
People will often leave apps when things are asked of them before they experience the app. Give them a chance to try your app before you ask them to sign up with Blockstack. For example, a note taking app could let a new user write a couple of notes before prompting them to save their progress.
### Provide an easy way in for new users
Many new users to your app will not be familiar with Blockstack yet and will be hesitant to click a Blockstack-branded button. Provide a generic button for users that are new to your app and Blockstack. Blockstack Connect will introduce new users to Blockstack and recognize existing users.
![Design Guidance Example](./docs/call-to-action-branding.png)
### Provide a quick way for existing users to sign in
You can point users to a specific part of the Blockstack App. For instance, a “Sign in” button on your website can redirect users to the sign in flow of the Blockstack App. If you do this, make sure you also have an option that is explicitly for new users and that points to the sign up flow.
To implement this functionality, check out our section on sending users to sign in immediately.

206
_develop/connect/use-with-clarity.md

@ -7,19 +7,12 @@ permalink: /:collection/:path.html
With Connect, you can interact with the Stacks 2.0 blockchain. You can allow your users to send transactions and interact with smart contracts. With Connect, you can interact with the Stacks 2.0 blockchain. You can allow your users to send transactions and interact with smart contracts.
* TOC
{:toc}
## How it works
For your app's users to be able to execute a smart contract function, they need to sign and broadcast a transaction. It's important that users remain control of the private keys that sign these transactions. Connect provides an easy-to-use workflow that allows your users to securely sign transactions.
Connect allows you to open the authenticator with parameters indicating the details of the transaction - like the smart contract address, function name, and specific arguments. Your users get the chance to see these details, and then sign and broadcast the transaction in a single click. Their transaction will be securely signed and broadcasted onto the Stacks blockchain. After this is done, a callback is fired to allow you to update your app.
## Usage
<div class="uk-card uk-card-default uk-card-body"> <div class="uk-card uk-card-default uk-card-body">
<h5 class="uk-card-title">Transaction signing is still in progress</h5> <h5 class="uk-card-title">Transaction signing is still in progress</h5>
<p>
To use these features, make sure you install the <code>testnet</code> tag the <code>@blockstack/connect</code> NPM package. You can do this by running <code>npm install --save @blockstack/connect@testnet</code>, or by specifying <code>testnet</code> as the version in
your package.json file.
</p>
<p> <p>
The Stacks 2.0 blockchain is still in testnet, and our web app integration is also still in beta. In order to use transaction signing in your application, you need to use the configuration `authOrigin` with `@blockstack/connect`. The Stacks 2.0 blockchain is still in testnet, and our web app integration is also still in beta. In order to use transaction signing in your application, you need to use the configuration `authOrigin` with `@blockstack/connect`.
</p> </p>
@ -33,34 +26,81 @@ Connect allows you to open the authenticator with parameters indicating the deta
</pre> </pre>
</div> </div>
### `ContractCallOptions` * TOC
{:toc}
## How it works
For your app's users to be able to execute a smart contract function, they need to sign and broadcast a transaction. It's important that users remain in control of the private keys that sign these transactions. Connect provides an easy-to-use workflow that allows your users to securely sign transactions.
Connect allows you to open the authenticator with parameters indicating the details of the transaction - like the smart contract address, function name, and specific arguments. Your users get the chance to see these details, and then sign and broadcast the transaction in a single click. Their transaction will be securely signed and broadcasted onto the Stacks blockchain. After this is done, a callback is fired to allow you to update your app.
## Calling Clarity Contract Functions
Once you have a Clarity smart contract built and deployed, you'll naturally want to allow your app's users to interact with it.
When signing a transaction, you need to specify a few details. Here is the exact interface that describes what options you have: To initiate a contract call transaction, use the `openContractCall` function.
```ts ```ts
export interface ContractCallOptions { import { openContractCall } from '@blockstack/connect';
// While in beta, you must provide this option:
const authOrigin = 'https://deploy-preview-301--stacks-authenticator.netlify.app';
// Here's an example of options:
const myStatus = 'hey there';
const options = {
contractAddress: 'ST22T6ZS7HVWEMZHHFK77H4GTNDTWNPQAX8WZAKHJ',
contractName: 'status',
functionName: 'write-status!',
functionArgs: [
{
type: 'buff',
value: myStatus,
}
],
authOrigin,
appDetails: {
name: 'SuperApp',
icon: 'https://example.com/icon.png'
},
finished: (data) => {
console.log('TX ID:', data.txId);
console.log('Raw TX:', data.txRaw);
},
};
await openContractCall(opts);
```
When calling `openContractCall`, you need to specify a few details. Here is the exact interface that describes what options you have:
```ts
interface ContractCallOptions {
contractAddress: string; contractAddress: string;
functionName: string; functionName: string;
contractName: string; contractName: string;
functionArgs?: any[]; functionArgs?: any[];
authOrigin?: string; authOrigin?: string;
userSession?: UserSession; appDetails: {
appDetails?: AuthOptions['appDetails']; name: string;
finished?: (data: FinishedTxData) => void; icon: string;
};
finished: (data: FinishedTxData) => void;
} }
``` ```
parameter | type | default | optional | description parameter | type | optional | description
---|---|---|---|--- ---|---|---|---
contractAddress | string | | false | The Stacks address that published this contract contractAddress | string | false | The Stacks address that published this contract
contractName | string | | false | The name that was used when publishing this contract contractName | string | false | The name that was used when publishing this contract
functionName | string | | false | The name of the function you're calling. This needs to be a [public function](/core/smart/clarityRef.html#define-public). functionName | string | false | The name of the function you're calling. This needs to be a [public function](/core/smart/clarityRef.html#define-public).
functionArgs | array | | false | The arguments you're calling the function with. You'll need to provide the Clarity type with each argument. See the below section for details. functionArgs | array | false | The arguments you're calling the function with. You'll need to provide the Clarity type with each argument. See the below section for details. Defaults to `[]`.
userSession | UserSession | | true | A `UserSession` instance appDetails | object | false | A dictionary that includes `name` and `icon`
appDetails | object | | false | A dictionary that includes `name` and `icon` finished | function | false | A callback that is fired when the transaction is signed and broadcasted. Your callback will receive an object back with a `txId` and a `txRaw`, both of which are strings.
finished | function | | false | A callback that is fired when the transaction is signed and broadcasted. Your callback will receive an object back with a `txId` and a `txRaw`, both of which are strings. authOrigin | string | true | The location of the authenticator. This is only necessary when developing the authenticator locally, or when using beta features. Defaults to `"https://app.blockstack.org"`.
#### Passing Clarity types with function arguments ### Passing Clarity types with function arguments
To be able to serialize your transaction properly, you need to provide the appropriate Clarity type with each argument. These types are named the same as they are in Clarity. The `value` that you pass must be a string. The types you can pass are: To be able to serialize your transaction properly, you need to provide the appropriate Clarity type with each argument. These types are named the same as they are in Clarity. The `value` that you pass must be a string. The types you can pass are:
@ -68,7 +108,7 @@ To be able to serialize your transaction properly, you need to provide the appro
- `int` - i.e. `"12"` - `int` - i.e. `"12"`
- `bool` - can be "true", "false", "0" or "1" - `bool` - can be "true", "false", "0" or "1"
- `buff` - i.e. `"asdf"` - `buff` - i.e. `"asdf"`
- `principal` - This can be a contract principal, or a standard principal. Examples: `"ST22T6ZS7HVWEMZHHFK77H4GTNDTWNPQAX8WZAKHJ"` or `"ST22T6ZS7HVWEMZHHFK77H4GTNDTWNPQAX8WZAKHJ.my-contract"`. - `principal` - This can be a contract principal, or a standard principal. [Read more about principals](/core/smart/principals.html). Examples: `"ST22T6ZS7HVWEMZHHFK77H4GTNDTWNPQAX8WZAKHJ"` or `"ST22T6ZS7HVWEMZHHFK77H4GTNDTWNPQAX8WZAKHJ.my-contract"`.
Using these types, each argument is an object with the keys `type` and `value`. For example: Using these types, each argument is an object with the keys `type` and `value`. For example:
@ -87,40 +127,110 @@ const functionArguments = [
If you're using Typescript, these Clarity types can be imported as `ContractCallArgumentType` from `@blockstack/connect`. If you're using Typescript, these Clarity types can be imported as `ContractCallArgumentType` from `@blockstack/connect`.
### Usage in ES6 (non-React) apps ## Stacks (STX) Token Transfers
STX token transfers can be initiated with the `openSTXTransfer` function.
```ts ```ts
import { openContractCall } from '@blockstack/connect'; import { openSTXTransfer } from '@blockstack/connect';
// Here's an example of options: // While in beta, you must provide this option:
const myStatus = 'hey there'; const authOrigin = 'https://deploy-preview-301--stacks-authenticator.netlify.app';
const options = {
contractAddress: 'ST22T6ZS7HVWEMZHHFK77H4GTNDTWNPQAX8WZAKHJ', openSTXTransfer({
contractName: 'status', recipient: 'ST2EB9WEQNR9P0K28D2DC352TM75YG3K0GT7V13CV',
functionName: 'write-status!', amount: '100',
functionArgs: [ memo: 'Testing STX Transfers!',
{ authOrigin,
type: 'buff',
value: myStatus,
}
],
appDetails: { appDetails: {
name: 'SuperApp', name: 'SuperApp',
icon: 'https://example.com/icon.png' icon: 'https://example.com/icon.png'
}, },
finished: (data) => { finished: data => {
console.log('TX ID:', data.txId); console.log(data.txId);
console.log('Raw TX:', data.txRaw);
}, },
}; });
```
await openContractCall(opts); When calling `openSTXTransfer`, you need to specify a few details. Here are the options you have:
```ts
interface STXTransferOptions {
recipient: string;
amount: string;
memo?: string;
authOrigin?: string;
appDetails: {
name: string;
icon: string;
};
finished: (data: FinishedTxData) => void;
}
``` ```
### Usage in React Apps parameter | type | optional | description
---|---|---|---
recipient | string | false | The STX Address for the recipient of this STX transfer
amount | string | false | The amount of microstacks (µSTX) to be transferred. This argument is a string to prevent floating point errors. There are 1,000,000 µSTX per STX.
memo | string | true | An optional memo to include in the transaction.
appDetails | object | false | A dictionary that includes `name` and `icon`
finished | function | false | A callback that is fired when the transaction is signed and broadcasted. Your callback will receive an object back with a `txId` and a `txRaw`, both of which are strings.
authOrigin | string | true | The location of the authenticator. This is only necessary when developing the authenticator locally, or when using beta features. Defaults to `"https://app.blockstack.org"`.
## Deploying Clarity Contracts
To allow your app's users to deploy arbitrary Clarity contracts, use the `openContractDeploy` method.
```ts
import { openContractDeploy } from '@blockstack/connect';
const codeBody = '(begin (print "hello, world"))';
// While in beta, you must provide this option:
const authOrigin = 'https://deploy-preview-301--stacks-authenticator.netlify.app';
openContractDeploy({
contractName: 'my-contract-name',
codeBody,
authOrigin,
appDetails: {
name: 'SuperApp',
icon: 'https://example.com/icon.png'
},
finished: (data) => {
console.log(data.txId);
}
})
```
Here is the interface for the options you can provide to `openContractDeploy`:
```ts
interface ContractDeployOptions {
codeBody: string;
contractName: string;
authOrigin?: string;
appDetails: {
name: string;
icon: string;
};
finished: (data: FinishedTxData) => void;
}
```
parameter | type | optional | description
---|---|---|---
codeBody | string | false | The Clarity source code for this contract
contractName | string | false | The name for this contract
appDetails | object | false | A dictionary that includes `name` and `icon`
finished | function | false | A callback that is fired when the transaction is signed and broadcasted. Your callback will receive an object back with a `txId` and a `txRaw`, both of which are strings.
authOrigin | string | true | The location of the authenticator. This is only necessary when developing the authenticator locally, or when using beta features. Defaults to `"https://app.blockstack.org"`.
## Usage in React Apps
Make sure you follow the [setup instructions](/develop/connect/get-started.html#in-react-apps) first. When you're using `useConnect`, you don't have to specify `appDetails` - we'll pick that up from your existing configuration. Make sure you follow the [setup instructions](/develop/connect/get-started.html#in-react-apps) first. When you're using `useConnect`, you don't have to specify `appDetails` - we'll pick that up from your existing configuration.
Each transaction signing method is exposed through the `useConnect` hook, but they're prefixed with `do` instead of `open`, to remain consistent with our React action naming standards.
```tsx ```tsx
import { useConnect } from '@blockstack/connect'; import { useConnect } from '@blockstack/connect';

2
_develop/radiks-intro.md

@ -29,7 +29,7 @@ Radiks consists of a database, a pre-built server, and a client. A developer add
2. Saves a raw JSON of this encrypted data in the user's Gaia storage. 2. Saves a raw JSON of this encrypted data in the user's Gaia storage.
3. Stores the encrypted data on the Radiks server. 3. Stores the encrypted data on the Radiks server.
Radiks can store both public and sensitive, non-public data since all data is encrypted by default before it leaves the client. Your application can query Radiks for public data and then decrypt the sensitive information on the client. Radix servers can only return queries for unencrypted data. Radiks can store both public and sensitive, non-public data since all data is encrypted by default before it leaves the client. Your application can query Radiks for public data and then decrypt the sensitive information on the client. Radiks servers can only return queries for unencrypted data.
## How Radiks authorizes writes ## How Radiks authorizes writes

4
_includes/sign_in.md

@ -4,9 +4,9 @@ A decentralized application (DApp) and the Blockstack Browser communicate during
![](/storage/images/app-sign-in.png) ![](/storage/images/app-sign-in.png)
When a user chooses to **Sign in with Blockstack** on a DApp, it calls the `redirectToSignIn()` method which sends an `authRequest` to the Blockstack Browser. Blockstack passes the token in via a URL query string in the `authRequest` parameter: When a user chooses to **Sign in with Blockstack** on a DApp, it calls the `doOpenAuth()` method which sends an `authRequest` to the Blockstack Authenticator. Blockstack passes the token in via a URL query string in the `authRequest` parameter:
`https://browser.blockstack.org/auth?authRequest=j902120cn829n1jnvoa...` `https://app.blockstack.org/#/sign-up?authRequest=j902120cn829n1jnvoa...`
When the Blockstack Browser receives the request, it generates an (`authResponse`) token to the application using an _ephemeral transit key_ . The ephemeral transit key is just used for the particular instance of the application, in this case, to sign the `authRequest`. The application stores the ephemeral transit key during the request generation. The public portion of the transit key is passed in the `authRequest` token. The Blockstack Browser uses the public portion of the key to encrypt an _app-private key_ which is returned via the `authResponse`. When the Blockstack Browser receives the request, it generates an (`authResponse`) token to the application using an _ephemeral transit key_ . The ephemeral transit key is just used for the particular instance of the application, in this case, to sign the `authRequest`. The application stores the ephemeral transit key during the request generation. The public portion of the transit key is passed in the `authRequest` token. The Blockstack Browser uses the public portion of the key to encrypt an _app-private key_ which is returned via the `authResponse`.

14
_ios/tutorial.md

@ -71,15 +71,15 @@ $ which npm npx
If you don't have these installed, take a moment to install or upgrade as needed. If you don't have these installed, take a moment to install or upgrade as needed.
### Install the CocoaPods 1.6.0.beta.1 dependency manager ### Install the CocoaPods 1.6.0 dependency manager
The sample application only runs on devices with iOS 11.0 or higher. You install The sample application only runs on devices with iOS 11.0 or higher. You install
the Blockstack iOS SDK through the CocoaPods. Cocoapods is a dependency manager the Blockstack iOS SDK through the CocoaPods. Cocoapods is a dependency manager
for Swift and Objective-C Cocoa projects. It’s a simple, user friendly way to for Swift and Objective-C Cocoa projects. It’s a simple, user friendly way to
use libraries from the community in your project. use libraries from the community in your project.
You must use the `1.6.0.beta.1` version of CocoaPods or newer to avoid an You must use CocoaPods version `1.6.0` or newer to avoid an
incapability between Cocoapods and XCode. Before starting the tutorial, confirm incompatibility between Cocoapods and XCode. Before starting the tutorial, confirm
you have installed CocoaPods. you have installed CocoaPods.
```bash ```bash
@ -500,7 +500,7 @@ Rather than have you build up your own UI in the interface builder, this section
defines the graphical elements, and their functionality will be defined in defines the graphical elements, and their functionality will be defined in
a respective `.swift` file. a respective `.swift` file.
3. Within the `<viewController>` element, replace the existing `<view>` subelement with the following: 3. Within the `<viewController>` element, replace the existing `<view>` sub-element with the following:
```xml ```xml
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC"> <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
@ -619,7 +619,7 @@ registred it with Blockstack and the app would be available on the Web. This
example skips this registration step and uses an example application we've example skips this registration step and uses an example application we've
already created for you: already created for you:
`https://heuristic-brown-7a88f8.netlify.com/redirect.html` `https://heuristic-brown-7a88f8.netlify.app/redirect.html`
This web application already has a redirect in place for you. You'll reference This web application already has a redirect in place for you. You'll reference
this application in your mobile add for now. In XCode, do the following; this application in your mobile add for now. In XCode, do the following;
@ -687,8 +687,8 @@ this application in your mobile add for now. In XCode, do the following;
} else { } else {
print("Currently signed out so signing in.") print("Currently signed out so signing in.")
// Address of deployed example web app // Address of deployed example web app
Blockstack.shared.signIn(redirectURI: URL(string: "https://heuristic-brown-7a88f8.netlify.com/redirect.html")!, Blockstack.shared.signIn(redirectURI: URL(string: "https://heuristic-brown-7a88f8.netlify.app/redirect.html")!,
appDomain: URL(string: "https://heuristic-brown-7a88f8.netlify.com")!) { authResult in appDomain: URL(string: "https://heuristic-brown-7a88f8.netlify.app")!) { authResult in
switch authResult { switch authResult {
case .success(let userData): case .success(let userData):
print("Sign in SUCCESS", userData.profile?.name as Any) print("Sign in SUCCESS", userData.profile?.name as Any)

Loading…
Cancel
Save