@ -20,12 +20,12 @@ In this tutorial, you learn how to use Clarity, Blockstack's smart contracting l
The Clarity language goes live in the next Stacks blockchain fork. Until the fork, you can run Clarity in a test environment. You run this test environment in a Docker container. Before you begin this tutorial, make sure you have <ahref="https://docs.docker.com"target="_blank">Docker installed on your workstation</a>.
If for some reason you don't want to run the test environment with Docker, you can build and maintain a local environment. Instructions for downloading and building the environment are available in the `blockstack/blockstack-core` repository's <ahref='https://github.com/blockstack/blockstack-core'target='_blank'>README</a> file.
If, for some reason, you don't want to run the test environment with Docker, you can build and maintain a local environment. Instructions for downloading and building the environment are available in the `blockstack/blockstack-core` repository's <ahref='https://github.com/blockstack/blockstack-core'target='_blank'>README</a> file.
## Task 1: Set up the test environment
Blockstack publishes the `clarity-developer-preview` image on Docker hub. A container built from this image contains sample programs, the Blockstack Core, and tools for working with them. In this task, you use Docker to pull and and run the image on your local workstation.
Blockstack publishes the `clarity-developer-preview` image on Docker hub. A container built from this image contains sample programs, the Blockstack Core, and tools for working with them. In this task, you use Docker to pull and run the image on your local workstation.
1. Pull the Blockstack core `clarity-developer-preview` image from Docker Hub.
@ -39,7 +39,7 @@ Blockstack publishes the `clarity-developer-preview` image on Docker hub. A cont
$ docker run -it -v $HOME/blockstack-dev-data:/data/ blockstack/blockstack-core:clarity-developer-preview bash
```
The launches a container with the Clarity test environment and opens a bash shell into the container. The `-v` flag creates a local `$HOME/blockstack-dev-data` directory in your workstation and mounts it at the `/data` directory inside the container. The shell opens into the `src/blockstack-core` directory. This directory contains the source for a core and includes Clarity contract samples you can run.
The command launches a container with the Clarity test environment and opens a bash shell into the container. The `-v` flag creates a local `$HOME/blockstack-dev-data` directory in your workstation and mounts it at the `/data` directory inside the container. The shell opens into the `src/blockstack-core` directory. This directory contains the source for a core and includes Clarity contract samples you can run.
3. List the contents of the `sample-programs` directory.
@ -48,7 +48,7 @@ Blockstack publishes the `clarity-developer-preview` image on Docker hub. A cont
names.clar tokens.clar
```
The sample programs directory contains two simple Clarity programs. Clarity code files have a `.clar` suffix.
The sample program's directory contains two simple Clarity programs. Clarity code files have a `.clar` suffix.
4. Go ahead and display the contents of the `tokens.clar` program with the `cat` command.
@ -62,35 +62,36 @@ Blockstack publishes the `clarity-developer-preview` image on Docker hub. A cont
If you haven't already done so, use the `cat` or `more` command to display the `tokens.clar` file's code. Clarity is designed for static analysis; it is not a compiled language and is not Turing complete. It language is a LISP-like language. LISP is an acronym for list processing.
The first line of the `tokens.clar` program contains a user-defined `get-balance` function.
The first lines of the `tokens.clar` program contains a user-defined `get-balance` function.
`get-balance` is a private function because it is constructed with the `define` call. To create public functions, you would use the `define-public` function. Public functions can be called from other contracts or even from the command line with the `clarity-cli`.
`get-balance` is a private function because it is constructed with the `define-private` call. To create public functions, you would use the `define-public` function. Public functions can be called from other contracts or even from the command line with the `clarity-cli`.
Notice the program is enclosed in `()` (parentheses) and each statement as well. The `get-balance` function takes an `account` argument of the special type `principal`. Principals represent a spending entity and are roughly equivalent to a Stacks address.
Along with the `principal` types, Clarity supports booleans, integers, and fixedlength buffers. Variables are created via `let` binding but there is no support for mutating functions like `set`.
Along with the `principal` types, Clarity supports booleans, integers, and fixed-length buffers. Variables are created via `let` binding, but there is no support for mutating functions like `set`.
The next sequence of lines shows an `if` statement that allows you to set conditions for execution in the language..
The next sequence of lines shows an `if` statement that allows you to set conditions for execution in the language.
Every smart contract has both a data space and code. The data space of a contract may only interact with that contract. This particular function is interacting with a map named `tokens`. The `set-entry!` function is a native function that sets the value associated with the input key to the inputted value in the `tokens` data map. Because `set-entry!` mutates data so it has an `!` exclamation point; this is by convention in Clarity.
In the first `token-transfer` public function, you see that it calls the private `get-balance` function and passes it `tx-sender`. The `tx-sender` isa a globally defined variable that represents the the current principal.
In the first `token-transfer` public function, you see that it calls the private `get-balance` function and passes it `tx-sender`. The `tx-sender` is a globally defined variable that represents the current principal.
@ -98,7 +99,7 @@ In the first `token-transfer` public function, you see that it calls the private
(if (or (> amount balance) (<= amount 0))
(err "must transfer positive balance and possess funds")
(begin
(set-entry! tokens (tuple (account tx-sender))
(map-set! tokens (tuple (account tx-sender))
(tuple (balance (- balance amount))))
(token-credit! to amount)))))
@ -112,12 +113,12 @@ In the first `token-transfer` public function, you see that it calls the private
The final two lines of the program pass a principal, represented by a Stacks address, and an amount to the private user-defined `token-credit` function.
Smart contracts may call other smart contracts using a `contract-call!` function. This means that if a transaction invokes a function in a given smart contract, that function is able to make calls into other smart contracts on your behalf. The ability to read and do a static analysis of Clarity code allows clients to learn which functions a given smart contract will ever call. Good clients should always warn users about any potential side effects of a given transaction.
Smart contracts may call other smart contracts using a `contract-call!` function. This ability means that if a transaction invokes a function in a given smart contract, that function is able to make calls into other smart contracts on your behalf. The ability to read and do a static analysis of Clarity code allows clients to learn which functions a given smart contract will ever call. Good clients should always warn users about any potential side effects of a given transaction.
Take a moment to `cat` the contents of the `names.clar` file.
Take a moment to `cat` the contents of the `sample-programs/names.clar` file.
```bash
cat names.clar
cat sample-programs/names.clar
````
Which `tokens.clar` function is being called?
@ -133,18 +134,18 @@ In this task, you interact with the the contracts using the `clarity-cli` comman
Database created
```
You should see a message saying `Database created`. The command creates an SQLlite database. The database is available in the container and also in your workstation. In this tutorial, your workstation mount should at this point contain the `$HOME/blockstack-dev-data/db` directory.
You should see a message saying `Database created`. The command creates an SQLlite database. The database is available in the container and also in your workstation. In this tutorial, your workstation mount should, at this point, contain the `$HOME/blockstack-dev-data/db` directory.
Error (line 11, column 1): use of unresolved contract ''S1G2081040G2081040G2081040G208105NK8PE5.tokens'.
```
This happens because the `names.clar` contract _calls_ the `tokens.clar` contract, and that contract has not been created on the blockchain.
@ -158,34 +159,44 @@ In this task, you interact with the the contracts using the `clarity-cli` comman
When the `check` command executes successfully and exits with the stand UNIX `0` exit code.
4. Launch the `tokens.clar` contract.
4. Generate a demo Stacks address for testing your contract.
You use the `launch` command to instantiate a contract on the Stacks blockchain. If you have dependencies between contracts, for example names.clar is dependent on tokens.clar, you must launch the dependency first.
This address is used to name your contract at launch time. You can use any existing Stacks address. For this sample, you are going to use the `generate_address` command to create one.
Once launched, you can execute the contract or a public method on the contract. Your development database has an instantiated `tokens` contract. If you were to close the container and restart it later with the same mount point and you wouldn't need to relaunch that database; it persists until you remove it from your local drive.
The demo address you generate will be different than the one that appears in this example.
The program should pass validation because its dependency on `tokens.clar` is fulfilled.
6. Launch the `tokens.clar` contract and assign it to your `DEMO_ADDRESS` address.
6. Instantiate the `names.clar` contract as well.
You use the `launch` command to instantiate a contract on the Stacks blockchain. If you have dependencies between contracts, for example `names.clar` is dependent on `tokens.clar`, you must launch the dependency first.
Once launched, you can execute the contract or a public method on the contract. Your development database has an instantiated `tokens` contract. If you were to close the container and restart it later with the same mount point and you wouldn't need to relaunch that database; it persists until you remove it from your local drive.
7. Instantiate the `names.clar` contract and assign it to your `DEMO_ADDRESS` address. as well.
The database exists on your local workstation and persists through restarts of the container. You can use this database to examine the effects of your Clarity programs. The tables in the SQLite database are the following:
<tableclass="uk-table">
<tr>
<th>Name</th>
<th>Purpose</th>
</tr>
<tr>
<td><code>contracts</code></td>
<td>Lists contracts and stores a JSON description of it.</td>
</tr>
<tr>
<td><code>data_table</code></td>
<td>Lists the data associated with a contract.</td>
</tr>
<tr>
<td><code>maps_table</code></td>
<td>Lists maps types associated with a contract and stores JSON description of it.</td>
</tr>
<tr>
<td><code>simmed_block_table</code></td>
<td>Supports the test environment by simulating responses to blockchain information queries.</td>
</tr>
<tr>
<td><code>type_analysis_table</code></td>
<td>Provides a JSON describing contract data.</td>
</tr>
</table>
While not required, you can install SQLite in your local environment and use it to examine the data associated with and impacted by your contract. For example, this what the `maps_table` contains after you initialize the `tokens` contract.
The database exists on your local workstation and persists through restarts of the container. You can use this database to explore the transactional effects of your Clarity programs. The SQLite database includes a single `data_table` and a set of `marf` structures.
While not required, you can install SQLite in your local environment and use it to examine the data associated with and impacted by your contract. For example, this what the `data_able` contains after you initialize the `tokens` contract.
## Task 5: Execute a public function
<imgsrc="../images/sqlite-contract.png"alt="">
In this section, you use the public `mint!` function in the `tokens` contract to mint some new tokens.
The `marf` directory defines a data structure that handles key-value lookups in the presence of blockchain forks. These structures are not intended for use in debugging, they simply support the implementation.
1. Use the `clarity_cli` command to create a demo address.
This command uses the private `get-balance` function in the `tokens` contract and pipes the result to the `eval` subcommand. The `eval` subcommand lets you evaluate both public and _private_ functions of a contract in read-only mode.
4. Try minting some tokens and sending them to an address we'll use for our demo.
2. Try minting some tokens and sending them to an address we'll use for our demo.
Now, let's register a name using the `names.clar` contract. Names are just integers in this sample contract, so you'll register the name 10.
Now, let's register a name using the `names.clar` contract. Names can _only_ be integers in this sample contract, so you'll register the name 10 in this environment.
1. Compute the hash of the name we want to register.
"description":"The `fold` function applies the input function `func` to each element of the\ninput list _and_ the output of the previous application of the `fold` function. When invoked on\nthe first list element, it uses the `initial-value` as the second input. `fold` returns the last\nvalue return by the successive applications.",
"description":"The `map-get` function looks up and returns an entry from a contract's data map.\nThe value is looked up using `key-tuple`.\nIf there is no value associated with that key in the data map, the function returns a (none) option. Otherwise,\nit returns (some value)",
"description":"The `map-get` function looks up and returns an entry from a contract's data map.\nThe value is looked up using `key-tuple`.\nIf there is no value associated with that key in the data map, the function returns a `none` option. Otherwise,\nit returns `(some value)`.",
"example":"(expects! (map-get names-map (tuple (name \"blockstack\"))) (err 1)) ;; Returns (tuple (id 1337))\n(expects! (map-get names-map ((name \"blockstack\"))) (err 1)) ;; Same command, using a shorthand for constructing the tuple\n"
"description":"The `contract-map-get` function looks up and returns an entry from a\ncontract other than the current contract's data map. The value is looked up using `key-tuple`.\nIf there is no value associated with that key in the data map, the function returns a (none) option. Otherwise,\nit returns (some value).",
"example":"(expects! (contract-map-get names-contract names-map (tuple (name \"blockstack\")) (err 1))) ;; Returns (tuple (id 1337))\n(expects! (contract-map-get names-contract names-map ((name \"blockstack\")) (err 1)));; Same command, using a shorthand for constructing the tuple\n"
"description":"The `contract-map-get` function looks up and returns an entry from a\ncontract other than the current contract's data map. The value is looked up using `key-tuple`.\nIf there is no value associated with that key in the data map, the function returns a `none` option. Otherwise,\nit returns `(some value)`.",
"example":"(expects! (contract-map-get .names-contract names-map (tuple (name \"blockstack\")) (err 1))) ;; Returns (tuple (id 1337))\n(expects! (contract-map-get .names-contract names-map ((name \"blockstack\")) (err 1)));; Same command, using a shorthand for constructing the tuple\n"
"description":"The `contract-call!` function executes the given public function of the given contract.\nYou _may not_ this function to call a public function defined in the current contract. If the public\nfunction returns _err_, any database changes resulting from calling `contract-call!` are aborted.\nIf the function returns _ok_, database changes occurred.",
"example":"(contract-call! tokens transfer 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 19) ;; Returns (ok 1)"
"example":"(contract-call! .tokens transfer 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 19) ;; Returns (ok 1)"
},
{
"name":"as-contract",
@ -320,6 +328,14 @@
"description":"The `as-contract` function switches the current context's `tx-origin` value to the _contract's_ \nprincipal and executes `expr` with that context. It returns the resulting value of `expr`.",
"description":"The `at-block` function evaluates the expression `expr` _as if_ it were evaluated at the end of the\nblock indicated by the _block-hash_ argument. The `expr` closure must be read-only.\n\nThe function returns the result of evaluating `expr`.\n",
"description":"The `default-to` function attempts to 'unpack' the second argument: if the argument is\na `(some ...)` option, it returns the inner value of the option. If the second argument is a `(none)` value,\n`default-to` it returns the value of `default-value`.",
"description":"The `asserts!` function admits a boolean argument and asserts its evaluation: \nif bool-expr is `true`, `asserts!` returns `true` and proceeds in the program execution. \nIf the supplied argument is returning a 'false value, `asserts!` _returns_ `thrown-value` and exits the current \ncontrol-flow.",