docs: update documentation, mostly by copying content from sip. delete content that is discussed elsewhere (or managed elsewhere, like the tutorials, sdk-quickstart

This commit is contained in:
Aaron Blankstein
2020-04-17 15:31:12 -05:00
parent 26e90ff8e7
commit 71ee71095c
9 changed files with 160 additions and 1047 deletions

View File

@@ -11,13 +11,13 @@ You have two options for setting up a local development environment:
Fetch the docker image
```bash
$ docker pull blockstack/blockstack-core:alpha.contracts.1
$ docker pull blockstack/stacks-blockchain
```
You can "enter" a docker environment using the following command:
```bash
$ docker run -it -v $HOME/blockstack-dev-data:/data/ blockstack/blockstack-core:alpha.contracts.1 bash
$ docker run -it -v $HOME/blockstack-dev-data:/data/ blockstack/stacks-blockchain bash
```
This also _mounts_ the folder `$HOME/blockstack-dev-data` so that you
@@ -65,7 +65,7 @@ You should see a message saying "Database created".
Start by type checking the "names" sample contract.
```bash
$ blockstack-core local check sample-programs/names.clar /data/db
$ blockstack-core local check sample-contracts/names.clar /data/db
```
You should get an error:
@@ -83,7 +83,7 @@ that contract doesn't exist yet!
If you type check the `tokens.clar` contract, it should pass validation:
```bash
$ blockstack-core local check sample-programs/tokens.clar /data/db
$ blockstack-core local check sample-contracts/tokens.clar /data/db
```
When the `check` command executes successfully, it does not output any information,
@@ -94,20 +94,20 @@ it just exits with the stand UNIX `0` exit code.
Now, let's instantiate the tokens contract:
```bash
$ blockstack-core local launch tokens sample-programs/tokens.clar /data/db
$ blockstack-core local launch tokens sample-contracts/tokens.clar /data/db
```
Now that our development database has an instantiated tokens contract, let's see if
the `names.clar` contract will successfully pass the type checker now:
```bash
$ blockstack-core local check sample-programs/names.clar /data/db
$ blockstack-core local check sample-contracts/names.clar /data/db
```
It should pass validation. So now let's instantiate the names contract as well.
```bash
$ blockstack-core local launch names sample-programs/names.clar /data/db
$ blockstack-core local launch names sample-contracts/names.clar /data/db
```
### Executing a contract
@@ -198,7 +198,7 @@ defined in the smart contract VM.
You can obtain them from the docker container with:
```
$ docker run -it -v $HOME/blockstack-dev-data:/data/ blockstack/blockstack-core:alpha.contracts.1 blockstack-core docgen
$ docker run -it -v $HOME/blockstack-dev-data:/data/ blockstack/stacks-blockchain blockstack-core docgen
```
This outputs a JSON encoding of the API specifications for the native functions.

View File

@@ -1,90 +0,0 @@
---
layout: core
permalink: /:collection/:path.html
---
# clarity-cli command line
{:.no_toc}
You use the `clarity-cli` command to work with smart contracts within the Blockstack virtual environment. This command has the following subcommands:
* TOC
{:toc}
## initialize
```bash
clarity-cli initialize [vm-state.db]
```
Initializes a local VM state database. If the database exists, this command throws an error.
## mine_block
```bash
clarity-cli mine_block [block time] [vm-state.db]
```
Simulates mining a new block.
## get_block_height
```bash
clarity-cli get_block_height [vm-state.db]
```
Prints the simulated block height.
## check
```bash
clarity-cli check [program-file.scm] (vm-state.db)
```
Type checks a potential contract definition.
## launch
```bash
clarity-cli launch [contract-name] [contract-definition.scm] [vm-state.db]
```
Launches a new contract in the local VM state database.
## eval
```bash
clarity-cli eval [context-contract-name] (program.scm) [vm-state.db]
```
Evaluates, in read-only mode, a program in a given contract context.
## eval_raw
```bash
```
Type check and evaluate an expression for validity inside of a functions source. It does not evaluate within a contract or database context.
## repl
```bash
clarity-cli repl
```
Type check and evaluate expressions in a stdin/stdout loop.
## execute
```bash
clarity-cli execute [vm-state.db] [contract-name] [public-function-name] [sender-address] [args...]
```
Executes a public function of a defined contract.
## generate_address
```bash
clarity-cli generate_address
```
Generates a random Stacks public address for testing purposes.

View File

@@ -9,141 +9,126 @@ This file contains the reference for the Clarity language.
* TOC
{:toc}
## Block Properties
## Clarity Type System
The `get-block-info` function fetches property details for a block at a specified block height. For example:
The Clarity language uses a strong static type system. Function arguments
and database schemas require specified types, and use of types is checked
during contract launch. The type system does _not_ have a universal
super type. The type system contains the following types:
```cl
(get-block-info time 10) ;; Returns 1557860301
* `(tuple (key-name-0 key-type-0) (key-name-1 key-type-1) ...)` -
a typed tuple with named fields.
* `(list max-len entry-type)` - a list of maximum length `max-len`, with
entries of type `entry-type`
* `(response ok-type err-type)` - object used by public functions to commit
their changes or abort. May be returned or used by other functions as
well, however, only public functions have the commit/abort behavior.
* `(optional some-type)` - an option type for objects that can either be
`(some value)` or `none`
* `(buff max-len)` := byte buffer or maximum length `max-len`.
* `principal` := object representing a principal (whether a contract principal
or standard principal).
* `bool` := boolean value (`true` or `false`)
* `int` := signed 128-bit integer
* `uint` := unsigned 128-bit integer
## Public Functions
Functions specified via `define-public` statements are _public_
functions and these are the only types of functions which may
be called directly through signed blockchain transactions. In addition
to being callable directly from a transaction (see the Stacks wire formats
for more details on Stacks transactions), public function may be called
by other smart contracts.
Public functions _must_ return a `(response ...)` type. This is used
by Clarity to determine whether or not to materialize any changes from
the execution of the function. If a function returns an `(err ...)`
type, and mutations on the blockchain state from executing the
function (and any function that it called during execution) will be
aborted.
In addition to function defined via `define-public`, contracts may expose
read-only functions. These functions, defined via `define-read-only`, are
callable by other smart contracts, and may be queryable via public blockchain
explorers. These functions _may not_ mutate any blockchain state. Unlike normal
public functions, read-only functions may return any type.
## Contract Calls
A smart contract may call functions from other smart contracts using a
`(contract-call?)` function.
This function returns a response type result-- the return value of the
called smart contract function.
We distinguish 2 different types of `contract-call?`:
* Static dispatch: the callee is a known, invariant contract available
on-chain when the caller contract is deployed. In this case, the
callee's principal is provided as the first argument, followed by the
name of the method and its arguments:
```scheme
(contract-call?
.registrar
register-name
name-to-register)
```
Because the Clarity language is in pre-release, the block properties that are fetched are simulated properties from a SQLite database. The available property names are:
* Dynamic dispatch: the callee is passed as an argument, and typed
as a trait reference (<A>).
<table class="uk-table">
<tr>
<th>Property</th>
<th>Definition</th>
</tr>
<tr>
<td><code>header-hash</code></td>
<td>A 32-byte buffer containing the block hash.</td>
</tr>
<tr>
<td><code>burnchain-header-hash</code></td>
<td>A 32-byte buffer that contains the hash from the proof of burn.</td>
</tr>
<tr>
<td><code>vrf-seed</code></td>
<td>A 32-byte buffer containing the Verifiable Random Function (VRF) seed value used for the block.</td>
</tr>
<tr>
<td><code>time</code></td>
<td>An integer value containing that roughly corresponds to when the block was mined. This is a Unix epoch timestamp in seconds. </td>
</tr>
</table>
{% include warning.html content="The <code>time</code> does not increase monotonically with each block. Block times are accurate only to within two hours. See <a href='https://github.com/bitcoin/bips/blob/master/bip-0113.mediawiki' target='_blank'>BIP113</a> for more information." %}
## Supported types
This section lists the types available to smart contracts. The only atomic types supported by the Clarity are booleans, integers, fixed length buffers, and principals.
### Int type
The integer type in the Clarity language is a 16-byte signed integer, which allows it to specify the maximum amount of microstacks spendable in a single Stacks transfer. The special `BlockHeightInt` you can obtain with the `get-block-info` function.
### Bool type
Takes values of the constants `true` and `false`.
### Buffer type
Buffer types represent fixed-length byte buffers. Currently, the only way to construct a Buffer is using string literals, for example `"alice.id"` or `hash160("bob.id")`
All of the hash functions return buffers:
`hash160`
`sha256`
`keccak256`
The block properties `header-hash`, `burnchain-header-hash`, and `vrf-seed` are all buffers.
### List type
Clarity supports 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.
### Principal type
Clarity provides this primitive for checking whether or not the smart contract transaction was signed by a particular principal. Principals represent a spending entity and are roughly equivalent to a Stacks address. The principal's signature is not checked by the smart contract, but by the virtual machine. A smart contract function can use the globally defined `tx-sender` variable to obtain the current principal.
Smart contracts may also be principals (represented by the smart contract's identifier). However, there is no private key associated with the smart contract, and it cannot broadcast a signed transaction on the blockchain. A smart contract uses the special variable `contract-name` to refer to its own principal.
[//]: # You can use the `is-contract?` to determine whether a given principal corresponds to a smart contract.
### Tuple type
To support the use of named fields in keys and values, Clarity allows the construction of named tuples using a function `(tuple ...)`, for example
```cl
(define imaginary-number-a (tuple (real 1) (i 2)))
(define imaginary-number-b (tuple (real 2) (i 3)))
```scheme
(define-public (swap (token-a <can-transfer-tokens>)
(amount-a uint)
(owner-a principal)
(token-b <can-transfer-tokens>)
(amount-b uint)
(owner-b principal)))
(begin
(unwrap! (contract-call? token-a transfer-from? owner-a owner-b amount-a))
(unwrap! (contract-call? token-b transfer-from? owner-b owner-a amount-b))))
```
This allows for creating named tuples on the fly, which is useful for data maps where the keys and values are themselves named tuples. Values in a given mapping are set or fetched using:
Traits can either be locally defined:
<table class="uk-table uk-table-small">
<tr>
<th class="uk-width-small">Function</th>
<th>Description</th>
</tr>
<tr>
<td><code>(fetch-entry map-name key-tuple)</code></td>
<td>Fetches the value associated with a given key in the map, or returns <code>none</code> if there is no such value.</td>
</tr>
<tr>
<td><code>(set-entry! map-name key-tuple value-tuple)</code></td>
<td>Sets the value of key-tuple in the data map</td>
</tr>
<tr>
<td><code>(insert-entry! map-name key-tuple value-tuple)</code></td>
<td>Sets the value of key-tuple in the data map if and only if an entry does not already exist.</td>
</tr>
<tr>
<td><code>(delete-entry! map-name key-tuple)</code></td>
<td>Deletes key-tuple from the data map.</td>
</tr>
</table>
```scheme
(define-trait can-transfer-tokens (
(transfer-from? (principal principal uint) (response uint)))
```
Or imported from an existing contract:
To access a named value of a given tuple, the `(get name tuple)` function returns that item from the tuple.
```scheme
(use-trait can-transfer-tokens
.contract-defining-trait.can-transfer-tokens)
```
### Optional type
Looking at trait conformance, callee contracts have two different paths.
They can either be "compatible" with a trait by defining methods
matching some of the methods defined in a trait, or explicitely declare
conformance using the `impl-trait` statement:
Represents an optional value. This is used in place of the typical usage of "null" values in other languages, and represents a type that can either be some value or `none`. Optional types are used as the return types of data-map functions.
```scheme
(impl-trait .contract-defining-trait.can-transfer-tokens)
```
### Response type
Explicit conformance should be prefered when adequate.
It acts as a safeguard by helping the static analysis system to detect
deviations in method signatures before contract deployment.
Response types represent the result of a public function. Use this type to indicate and return data associated with the execution of the function. Also, the response should indicate whether the function error'ed (and therefore did not materialize any data in the database) or ran `ok` (in which case data materialized in the database).
The following limitations are imposed on contract calls:
Response types contain two subtypes -- a response type in the event of `ok` (that is, a public function returns an integer code on success) and an `err` type (that is, a function returns a buffer on error).
## Native variables
The Clarity language includes native variables you can use in your contract.
### block-height
The height of a block in the Stacks blockchain. Block height is the number of blocks in the chain between any given block and the very first block in the blockchain. You can obtain a `block-height` via the `get-block-info` function.
### contract-name
Represents the current contract.
### tx-sender
Represents the current principal. This variable does not change during inter-contract calls. 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. This enables a wide variety of applications, but it comes with some dangers for users of smart contracts. Static analysis of Clarity contracts guarantees the language allows clients to deduce which functions a given smart contract will ever call. Good clients should always warn users about any potential side effects of a given transaction.
1. On static dispatches, callee smart contracts _must_ exist at the
time of creation.
2. No cycles may exist in the call graph of a smart contract. This
prevents recursion (and re-entrancy bugs). Such structures can
be detected with static analysis of the call graph, and will be
rejected by the network.
3. `contract-call?` are for inter-contract calls only. Attempts to
execute when the caller is also the callee will abort the
transaction.
## Clarity function reference
@@ -174,7 +159,7 @@ Represents the current principal. This variable does not change during inter-con
</table>
{{function_vals[4]}}
<h4>Example</h4>
```cl
```scheme
{{function_vals[5] | lstrip | rstrip }}
```
<hr class="uk-divider-icon">

View File

@@ -1,137 +0,0 @@
---
layout: core
permalink: /:collection/:path.html
---
# Define functions and data maps
{:.no_toc}
Clarity includes _defines_ and native functions for creating user-defined functions.
* TOC
{:toc}
## define and define-public functions
Functions specified via `define-public` statements are public functions. Functions without these designations, simple `define` statements, are private functions. You can run a contract's public functions directly via the `clarity-cli execute` command line directly or from other contracts. You can use the `clarity eval` or `clarity eval_raw` commands to evaluate private functions via the command line.
Public functions return a Response type result. If the function returns an `ok` type, then the function call is considered valid, and any changes made to the blockchain state will be materialized. If the function returns an `err` type, it is considered invalid, and has no effect on the smart contract's state.
For example, consider two functions, `foo.A` and `bar.B` where the `foo.A` function calls `bar.B`, the table below shows the data materialization that results from the possible combination of return values:
<table class="uk-table">
<tr>
<th></th>
<th>foo.A =&gt;</th>
<th>bar.B</th>
<th>Data impact that results</th>
</tr>
<tr>
<th rowspan="2">Function returns</th>
<td><code>err</code></td>
<td><code>ok</code></td>
<td>No changes result from either function.</td>
</tr>
<tr>
<td><code>ok</code></td>
<td><code>err</code></td>
<td>Change from <code>foo.A</code> is possible; no changes from <code>foo.B</code> materialize.</td>
</tr>
</table>
Defining of constants and functions are allowed for simplifying code using a define statement. However, these are purely syntactic. If a definition cannot be inlined, the contract is rejected as illegal. These definitions are also private, in that functions defined this way may only be called by other functions defined in the given smart contract.
## define-read-only functions
Functions specified via `define-read-only` statements are public. Unlike functions created by `define-public`, functions created with `define-read-only` may return any type. However, `define-read-only` statements cannot perform state mutations. Any attempts to modify contract state by these functions or functions called by these functions result in an error.
## define-map functions for data
Data within a smart contract's data-space is stored within maps. These stores relate a typed-tuple to another typed-tuple (almost like a typed key-value store). As opposed to a table data structure, a map only associates a given key with exactly one value. A smart contract defines the data schema of a data map with the `define-map` function.
```cl
(define-map map-name ((key-name-0 key-type-0) ...) ((val-name-0 val-type-0) ...))
```
Clarity contracts can only call the `define-map` function in the top-level of the smart-contract (similar to `define`. This function accepts a name for the map, and a definition of the structure of the key and value types. Each of these is a list of `(name, type)` pairs. Types are either the values `'principal`, `'integer`, `'bool` or the output of one of the hash calls which is an n-byte fixed-length buffer.
To support the use of named fields in keys and values, Clarity allows the construction of tuples using a function `(tuple ((key0 expr0) (key1 expr1) ...))`, for example:
```cl
(tuple (name "blockstack") (id 1337))
```
This allows for creating named tuples on the fly, which is useful for data maps where the keys and values are themselves named tuples. To access a named value of a given tuple, the function (get #name tuple) will return that item from the tuple.
The `define-map` interface, as described, disallows range-queries and queries-by-prefix on data maps. Within a smart contract function, you cannot iterate over an entire map. Values in a given mapping are set or fetched using the following functions:
<table class="uk-table">
<tr>
<th>Function</th>
<th>Description</th>
</tr>
<tr>
<td><code>(fetch-entry map-name key-tuple)</code></td>
<td>Fetches the value associated with a given key in the map, or returns <code>'null</code> if there is none.</td>
</tr>
<tr>
<td><code>(set-entry! map-name key-tuple value-tuple)</code></td>
<td>Sets the value of key-tuple in the data map.</td>
</tr>
<tr>
<td><code>(insert-entry! map-name key-tuple value-tuple)</code></td>
<td>Sets the value of <code>key-tuple</code> in the data map if and only if an entry does not already exist.</td>
</tr>
<tr>
<td><code>(delete-entry! map-name key-tuple)</code></td>
<td>Removes the value associated with the input key for the given map.</td>
</tr>
</table>
Data maps make reasoning about functions easier. By inspecting a given function definition, it is clear which maps will be modified and, even within those maps, which keys are affected by a given invocation. Also, the interface of data maps ensures that the return types of map operations are fixed length; Fixed length returns is a requirement for static analysis of a contract's runtime, costs, and other properties.
## List operations and functions
Lists may be multi-dimensional. However, note that runtime admission checks on typed function-parameters and data-map functions like `set-entry!` are charged based on the _maximal_ size of the multi-dimensional list.
You can call `filter` `map` and `fold` functions with user-defined functions (that is, functions defined with `(define ...)`, `(define-read-only ...)`, or `(define-public ...)`) or simple, native functions (for example, `+`, `-`, `not`).
## Intra-contract calls
A smart contract may call functions from other smart contracts using a `(contract-call!)` function:
```cl
(contract-call! contract-name function-name arg0 arg1 ...)
```
This function accepts a function name and the smart contract's name as input. For example, to call the function `token-transfer` in the smart contract, you would use:
`(contract-call! tokens token-transfer burn-address name-price))`
For intra-contract calls dynamic dispatch is not supported. When a contract is launched, any contracts it depends on (calls) must exist. Additionally, no cycles may exist in the call graph of a smart contract. This prevents recursion (and re-entrancy bugs. A static analysis of the call graph detects such structures and they are rejected by the network.
A smart contract may not modify other smart contracts' data directly; it can read data stored in those smart contracts' maps. This read ability does not alter any confidentiality guarantees of Clarity. All data in a smart contract is inherently public, andis readable through querying the underlying database in any case.
### Reading from other smart contracts
To read another contract's data, use `(fetch-contract-entry)` function. This behaves identically to `(fetch-entry)`, though it accepts a contract principal as an argument in addition to the map name:
```cl
(fetch-contract-entry
'contract-name
'map-name
'key-tuple) ;; value tuple or none
```
For example, you could do this:
```cl
(fetch-contract-entry
names
name-map
1) ;;returns owner principal of name
```
Just as with the `(contract-call)` function, the map name and contract principal arguments must be constants, specified at the time of publishing.
Finally, and importantly, the `tx-sender` variable does not change during inter-contract calls. 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. This enables a wide variety of applications, but it comes with some dangers for users of smart contracts. However, the static analysis guarantees of Clarity allow clients to know a priori which functions a given smart contract will ever call. Good clients should always warn users about any potential side effects of a given transaction.

View File

@@ -1,24 +0,0 @@
---
layout: core
permalink: /:collection/:path.html
---
# Install Clarity from Source
Build using `rust` and `cargo`:
```bash
$ cargo build --release
```
Install globally (you may have to run as sudoer):
```bash
$ cargo install --path .
```
You should now be able to run the command:
```bash
$ blockstack-core
```

View File

@@ -13,47 +13,60 @@ _Principals_ are a Clarity native type that represents a spending entity. This s
## Principals and tx-sender
A principal is represented by a public-key hash or multi-signature Stacks address. Assets in Clarity and the Stacks blockchain are "owned" by objects of the principal type; put another way, principal object types may own an asset.
Assets in the smart contracting language and blockchain are
"owned" by objects of the principal type, meaning that any object of
the principal type may own an asset. For the case of public-key hash
and multi-signature Stacks addresses, a given principal can operate on
their assets by issuing a signed transaction on the blockchain. _Smart
contracts_ may also be principals (reprepresented by the smart
contract's identifier), however, there is no private key associated
with the smart contract, and it cannot broadcast a signed transaction
on the blockchain.
A given principal operates on its assets by issuing a signed transaction on the Stacks blockchain. A Clarity contract can use a globally defined `tx-sender` variable to obtain the current principal.
The following user-defined function transfers an asset, in this case, tokens, between two principals:
A Clarity contract can use a globally defined `tx-sender` variable to
obtain the current principal. The following example defines a transaction
type that transfers `amount` uSTX from the sender to a recipient if some
condition is met, otherwise returning a 400 error code.
```cl
(define-public (transfer-to-recipient! (recipient principal) (amount uint))
(if (check-condition)
(stx-transfer? amount tx-sender recipient)
(err u400)))
```
(define (transfer! (sender principal) (recipient principal) (amount int))
(if (and
(not (eq? sender recipient))
(debit-balance! sender amount)
(credit-balance! recipient amount))
true
false))
```
The principal's signature is not checked by the smart contract, but by the virtual machine.
## Smart contracts as principals
Smart contracts themselves are principals and are represented by the smart contract's identifier. You create the identifier when you launch the contract, for example, the contract identifier here is `hanomine`.
Smart contracts themselves are principals and are represented by the
smart contract's identifier -- which is the publishing address of the
contract _and_ the contract's name, e.g.:
```bash
clarity-cli launch hanomine /data/hano.clar /data/db
```cl
'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR.contract-name
```
A smart contract may use the special variable `contract-name` to refer to its own principal.
For convenience, smart contracts may write a contract's identifier in the
form `.contract-name`, which is expanded by the Clarity interpreter into
a fully-qualified contract identifier that corresponds to the same
publishing address as the contract it appears in.
To allow smart contracts to operate on assets it owns, smart contracts may use the special `(as-contract expr)` function. This function executes the expression (passed as an argument) with the `tx-sender` set to the contract's principal, rather than the current sender. The `as-contract` function returns the value of the provided expression.
In order for a smart contract to operate on assets it owns, smart contracts
may use the special `(as-contract ...)` function. This function
executes the expression (passed as an argument) with the `tx-sender`
set to the contract's principal, rather than the current sender. The
`as-contract` function returns the value of the provided expression.
For example, a smart contract that implements something like a "token faucet" could be implemented as so:
For example, a smart contract that implements something like a "token
faucet" could be implemented as so:
```cl
(define-public (claim-from-faucet)
(if (is-none? (fetch-entry claimed-before (tuple (sender tx-sender))))
(if (is-none? (fetch-entry claimed-before {sender: tx-sender}))
(let ((requester tx-sender)) ;; set a local variable requester = tx-sender
(begin
(insert-entry! claimed-before {sender: requester} {claimed: true})
(as-contract (stacks-transfer! requester 1)))))
(err 1))
(as-contract (stacks-transfer? u1 tx-sender requester))))
(err 1)))
```
In this example, the public function `claim-from-faucet`:
@@ -63,6 +76,6 @@ In this example, the public function `claim-from-faucet`:
* Adds an entry to the tracking map.
* Uses `as-contract` to send 1 microstack
Contract writers can use the primitive function `is-contract?` to determine whether a given principal corresponds to a smart contract.
Unlike other principals, there is no private key associated with a smart contract. As it lacks a private key, a Clarity smart contract cannot broadcast a signed transaction on the blockchain.
Unlike other principals, there is no private key associated with a
smart contract. As it lacks a private key, a Clarity smart contract
cannot broadcast a signed transaction on the blockchain.

View File

@@ -1,268 +0,0 @@
---
layout: core
permalink: /:collection/:path.html
---
# Quickstart for the SDK
{:.no_toc}
You can use the software developer kit (SDK) to develop, test, and deploy Clarity smart contracts. The SDK goes beyond the basic test environment to allow for development of Javascript or TypeScript clients that call upon Clarity contracts.
* TOC
{:toc}
<div class="uk-card uk-card-default uk-card-body">
<h5>Clarity is in pre-release</h5>
<p>Clarity, its accompanying toolset, and the SDK are in pre-release. If you encounter issues with or have feature requests regarding Clarity, please create an issue on the <a href='https://github.com/blockstack/blockstack-core/issues' target='_blank'>blockstack/blockstack-core</a> repository. To read previous or join ongoing discussions about smart contracts in general and Clarity in particular, visit the <strong><a href='https://forum.blockstack.org/c/clarity' target='_blank'>Smart Contracts</a></strong> topic in the Blockstack Forum.
</p>
</div>
## About this tutorial and the prerequisites you need
{% include note.html content="This tutorial was written on macOS High Sierra 10.13.4. If you use a Windows or Linux system, you can still follow along. However, you will need to \"translate\" appropriately for your operating system." %}
For this tutorial, you will use `npm` to manage dependencies and scripts. The tutorial relies on the `npm` dependency manager. Before you begin, verify you have installed `npm` using the `which` command to verify.
```bash
$ which npm
/usr/local/bin/npm
```
If you don't find `npm` in your system, [install
it](https://www.npmjs.com/get-npm).
You use `npm` to install Yeoman. Yeoman is a generic scaffolding system that
helps users rapidly start new projects and streamline the maintenance of
existing projects. Verify you have installed `yo` using the `which` command.
```
$ which yo
/usr/local/bin/yo
```
If you don't have Yeoman, you can install it with the `npm install -g yo` command.
## Task 1: Generate an initial Clarity project
The SDK uses Yeoman to generate a project scaffold &mdash; an initial set of directories and files.
1. Create a new directory for your project.
```sh
mkdir hello-clarity-sdk
```
2. Change into your new project directory.
```sh
cd hello-clarity-sdk
```
3. Use the `npm` command to initialize a Clarity project.
```sh
npm init yo clarity-dev
npx: installed 15 in 1.892s
create package.json
create .vscode/extensions.json
...
Project created at /private/tmp/hello-clarity-sdk
✔ create-yo ok!
```
Depending on your connection speed, it may take time to construct the scaffolding.
## Task 2: Investigate the generated project
Your project should contain three directories:
| Directory |Description |
|---|---|
| `contracts` | Contains `.clar` files (Clarity contract files) here. |
| `test` | Contains files for testing your application. |
| `node_modules` | Contains packages the project depends on. Added by `npm`. |
The `contracts` directory contains a single file in `sample/hello-world.clar` file.
```cl
(define (say-hi)
"hello world")
(define (echo-number (val int))
val)
```
The contract exposes 2 rudimentary functions. The **say-hi** returns a `hello world` string. The **increment-number**: echos `val`.
The project also includes `tests/hello-world.ts` file. The test is written in Typescript. You can also write tests in Javascript.
{% highlight cl linenos %}
import { Client, Provider, ProviderRegistry, Result } from "@blockstack/clarity";
import { assert } from "chai";
describe("hello world contract test suite", () => {
let helloWorldClient: Client;
let provider: Provider;
before(async () => {
provider = await ProviderRegistry.createProvider();
helloWorldClient = new Client("hello-world", "sample/hello-world", provider);
});
it("should have a valid syntax", async () => {
await helloWorldClient.checkContract();
});
describe("deploying an instance of the contract", () => {
before(async () => {
await helloWorldClient.deployContract();
});
it("should return 'hello world'", async () => {
const query = helloWorldClient.createQuery({ method: { name: "say-hi", args: [] } });
const receipt = await helloWorldClient.submitQuery(query);
const result = Result.unwrap(receipt);
const parsedResult = Buffer.from(result.replace("0x", ""), "hex").toString();
assert.equal(parsedResult, "hello world");
});
it("should echo number", async () => {
const query = helloWorldClient.createQuery({
method: { name: "echo-number", args: ["123"] }
});
const receipt = await helloWorldClient.submitQuery(query);
const result = Result.unwrap(receipt);
assert.equal(result, "123");
});
});
after(async () => {
await provider.close();
});
});
{% endhighlight %}
The `hello-world.ts` test file is a client that runs the `hello-world.clar` contract. Tests are critical for smart contracts as they are intended to manipulate assets and their ownership. These manipulations are irreversible within a blockchain. As you create a contracts, you should not be surprise if you end up spending more time and having more code in your `tests` than in your `contracts` directory. The `tests/hello-world.ts` file in the scaffold has the following content:
The first part of the test (lines 1 -10) sets up the test environment. It defines a Clarity `provider` and launches it (line 9). The Client instance contains a contract name and the path to the sample code. This test also checks the client (line 14) and then launches it (line 19), this is equivalent to running `clarity-cli check` with the command line. The remaining test code exercises the contract. Try running this test.
```sh
npm run test
> hello-clarity-sdk@0.0.0 test /private/tmp/hello-clarity-sdk
> mocha
hello world contract test suite
✓ should have a valid syntax
deploying an instance of the contract
✓ should print hello world message
✓ should echo number
3 passing (182ms)
```
In the next section, try your hand at expanding the `hello-world.clar` program.
## Task 3: Try to expand the contract
In this task, you are challenged to expand the contents of the `contracts/hello-world.clar` file. Use your favorite editor and open the `contracts/hello-world.clar` file. If you use Visual Studio Code, you can install the Blockstack Clarity extension. The extension provides `syntax coloration` and some `autocompletion`.
Edit the `hello-world.clar` file.
```cl
;; Functions
(define (hello-world)
"hello world")
(define (echo-number (val int))
val)
```
Use the `+` function to create a `increment-number-by-10` function.
<div class="uk-inline">
<button class="uk-button uk-button-primary" enter="button">ANSWER</button>
<div uk-dropdown>
<pre>
;; Functions
(define (say-hi)
"hello world")
(define (increment-number (number int))
(+ 1 number))
(define (increment-number-by-10 (number int))
(+ 10 number))
</pre>
</div>
</div>
Use the `+` and `-` function to create a `decrement-number` user-defined method.
<div class="uk-inline">
<button class="uk-button uk-button-primary" enter="button">ANSWER</button>
<div uk-dropdown>
<pre>
;; Functions
(define (say-hi)
"hello world")
(define (increment-number (number int))
(+ 1 number))
(define (increment-number-by-10 (number int))
(+ 10 number))
(define (decrement-number (number int))
(- number 1))
</pre>
</div>
</div>
Finally, try adding a `counter` variable and be sure to store it. Increment `counter` in your code` and add a `get-counter` funtion to return the result. Here is a hint, you can add a `var` to a contract by adding the following line (before the function):
```cl
;; Storage
(define-data-var internal-value int 0)
```
<div class="uk-inline">
<button class="uk-button uk-button-primary" enter="button">ANSWER</button>
<div uk-dropdown>
<pre>
;; Storage
(define-data-var counter int 0)
;; Functions
(define (say-hi)
"hello world")
(define (increment-number (number int))
(+ 1 number))
(define (increment-number-by-10 (number int))
(+ 10 number))
(define (decrement-number (number int))
(- number 1))
(define (increment-counter)
(set-var! counter (+ 1 counter)))
(define (get-counter)
(counter))
</pre>
</div>
</div>
To review other, longer sample programs visit the <a href="https://github.com/blockstack/clarity-js-sdk/tree/master/packages/clarity-tutorials" target="_blank">clarity-js-sdk</a> repository.

View File

@@ -1,336 +0,0 @@
---
layout: core
permalink: /:collection/:path.html
---
# Hello Clarity tutorial
In this tutorial, you learn how to use Clarity, Blockstack's smart contracting language. Use this tutorial to get a quick introduction to Clarity and the default Blockstack test environment.
* TOC
{:toc}
<div class="uk-card uk-card-default uk-card-body">
<h5>Clarity is in pre-release</h5>
<p>Clarity and its accompanying toolset are in pre-release. If you encounter issues with or have feature requests regarding Clarity, please create an issue on the <a href='https://github.com/blockstack/blockstack-core/issues' target='_blank'>blockstack/blockstack-core</a> repository. To read previous or join ongoing discussions about smart contracts in general and Clarity in particular, visit the <strong><a href='https://forum.blockstack.org/c/clarity' target='_blank'>Smart Contracts</a></strong> topic in the Blockstack Forum.
</p>
</div>
## Before you begin (pre-requisites)
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 <a href="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 <a href='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.
1. Pull the Blockstack core `clarity-developer-preview` image from Docker Hub.
```bash
$ docker pull blockstack/blockstack-core:clarity-developer-preview
```
2. Start the Blockstack Core test environment with a Bash shell.
```bash
$ 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.
3. List the contents of the `sample-programs` directory.
```bash
root@f88368ba07b2:/src/blockstack-core# ls sample-programs/
names.clar tokens.clar
```
The sample programs 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.
```bash
root@c28600552694:/src/blockstack-core# cat sample-programs/tokens.clar
```
The next section gives you an introduction to the Clarity language by way of examining this program's code.
## Task 2: Review a simple Clarity program
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.
```cl
(define (get-balance (account principal))
(default-to 0 (get balance (fetch-entry tokens (tuple (account account))))))
```
`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`.
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 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..
```cl
(define (token-credit! (account principal) (tokens int))
(if (<= tokens 0)
(err "must move positive balance")
(let ((current-amount (get-balance account)))
(begin
(set-entry! tokens (tuple (account account))
(tuple (balance (+ tokens current-amount))))
(ok tokens)))))
```
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.
```cl
(define-public (token-transfer (to principal) (amount int))
(let ((balance (get-balance tx-sender)))
(if (or (> amount balance) (<= amount 0))
(err "must transfer positive balance and possess funds")
(begin
(set-entry! tokens (tuple (account tx-sender))
(tuple (balance (- balance amount))))
(token-credit! to amount)))))
(define-public (mint! (amount int))
(let ((balance (get-balance tx-sender)))
(token-credit! tx-sender amount)))
(token-credit! 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 10000)
(token-credit! 'SM2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQVX8X0G 300)
```
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.
Take a moment to `cat` the contents of the `names.clar` file.
```bash
cat names.clar
````
Which `tokens.clar` function is being called?
## Task 3: Initialize data-space and launch contracts
In this task, you interact with the the contracts using the `clarity-cli` command line.
1. Initialize a new `db` database in the `/data/` directory
```bash
# clarity-cli initialize /data/db
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.
2. Type check the `names.clar` contract.
```bash
# clarity-cli check sample-programs/names.clar /data/db
```
You should get an error:
```
Type check error.
NoSuchContract("tokens")
```
This happens because the `names.clar` contract _calls_ the `tokens.clar` contract, and that contract has not been created on the blockchain.
3. Type check the `tokens.clar` contract, it should pass a check as it does not use the `contract-call` function:
```bash
# clarity-cli check sample-programs/tokens.clar /data/db
Checks passed.
```
When the `check` command executes successfully and exits with the stand UNIX `0` exit code.
4. Launch the `tokens.clar` 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.
```bash
# clarity-cli launch tokens sample-programs/tokens.clar /data/db
Contract initialized!
```
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.
5. Recheck the `names.clar` contract.
```bash
# clarity-cli check sample-programs/names.clar /data/db
```
The program should pass validation because its dependency on `tokens.clar` is fulfilled.
6. Instantiate the `names.clar` contract as well.
```bash
# clarity-cli launch names sample-programs/names.clar /data/db
```
## Task 4. Examine the SQLite database
The test environment uses a SQLite database to represent the blockchain. You initialized this database when you ran this earlier:
```bash
clarity-cli initialize /data/db
```
As you work the contracts, data is added to the `db` database because you pass this database as a parameter, for example:
```bash
clarity-cli launch tokens sample-programs/tokens.clar /data/db
```
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:
<table class="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.
```
sqlite> select * from maps_table;
1|tokens|tokens|{"Atom":{"TupleType":{"type_map":{"account":{"Atom":"PrincipalType"}}}}}|{"Atom":{"TupleType":{"type_map":{"balance":{"Atom":"IntType"}}}}}
sqlite>
````
## Task 5: Execute a public function
In this section, you use the public `mint!` function in the `tokens` contract to mint some new tokens.
1. Use the `clarity_cli` command to create a demo address.
```
# clarity-cli generate_address
SP26CHZZ26Q25WDD1CFJYSED169PS9HTNX445XKDG
```
2. Add the address to your environment.
```bash
# DEMO_ADDRESS=SP26CHZZ26Q25WDD1CFJYSED169PS9HTNX445XKDG
```
3. Get the current balance of your new address.
```bash
# echo "(get-balance '$DEMO_ADDRESS)" | clarity-cli eval tokens /data/db
Program executed successfully! Output:
0
```
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.
```bash
# clarity-cli execute /data/db tokens mint! $DEMO_ADDRESS 100000
```
This executes the public `mint!` function defined in the tokens contract, sending 100000 tokens to you `$DEMO_ADDRESS`.
5. Use the `clarity-cli eval` command to check the result of this call.
```bash
# echo "(get-balance '$DEMO_ADDRESS)" | clarity-cli eval tokens /data/db
Program executed successfully! Output:
100000
```
## Task 6: Spend tokens by registering a name
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.
1. Compute the hash of the name we want to register.
You'll _salt_ the hash with the salt `8888`:
```bash
# echo "(hash160 (xor 10 8888))" | clarity-cli eval names /data/db
Program executed successfully! Output:
0xb572fb1ce2e9665f1efd0994fe077b50c3a48fde
```
The value of the name hash is:
```
0xb572fb1ce2e9665f1efd0994fe077b50c3a48fde
```
2. Preorder the name using the _execute_ command:
```bash
# clarity-cli execute /data/db names preorder $DEMO_ADDRESS 0xb572fb1ce2e9665f1efd0994fe077b50c3a48fde 1000
Transaction executed and committed. Returned: 0
```
This executes the public `preorder` function defined in the `names.clar` contract. The function reserves a name by paying the name fee (in this case, 1000 tokens).
3. Check the demo address' new balance:
```bash
# echo "(get-balance '$DEMO_ADDRESS)" | clarity-cli eval tokens /data/db
Program executed successfully! Output:
99000
```
4. Register the name by executing the _register_ function:
```bash
# clarity-cli execute /data/db names register $DEMO_ADDRESS \'$DEMO_ADDRESS 10 8888
Transaction executed and committed. Returned: 0clarity-cli execute /data/db names register $DEMO_ADDRESS \'$DEMO_ADDRESS 10 8888
```
5. Lookup the "owner address" for the name:
```bash
# echo "(get owner (fetch-entry name-map (tuple (name 10))))" | clarity-cli eval names /data/db
Program executed successfully! Output:
(some 'SP26CHZZ26Q25WDD1CFJYSED169PS9HTNX445XKDG)
```
## Where to go next
{:.no_toc}
* <a href="clarityRef.html">Clarity Language Reference</a>
* <a href="clarityRef.html">clarity-cli command line</a>

View File

@@ -63,7 +63,7 @@ following limitations:
functions defined this way may only be called by other functions
defined in the given smart contract.
7. Functions specified via `define-public` statements are _public_
functions. Arguments to these functions must specify their types.
functions.
8. Functions specified via `define-read-only` statements are _public_
functions and perform _no_ state mutations. Any attempts to
modify contract state by these functions or functions called by
@@ -89,7 +89,7 @@ any type.
* Lists may be multi-dimensional (i.e., lists may contain other lists), however each
entry of this list must be of the same type.
* `filter` `map` and `fold` functions may only be called with user-defined functions
(i.e., functions defined with `(define ...)`, `(define-read-only ...)`, or
(i.e., functions defined with `(define-private ...)`, `(define-read-only ...)`, or
`(define-public ...)`) or simple native functions (e.g., `+`, `-`, `not`).
* Functions that return lists of a different size than the input size
(e.g., `(append-item ...)`) take a required _constant_ parameter that indicates
@@ -323,8 +323,8 @@ allows the construction of named tuples using a function `(tuple ...)`,
e.g.,
```
(define imaginary-number-a (tuple (real 1) (i 2)))
(define imaginary-number-b (tuple (real 2) (i 3)))
(define-constant imaginary-number-a (tuple (real 1) (i 2)))
(define-constant imaginary-number-b (tuple (real 2) (i 3)))
```
@@ -333,36 +333,6 @@ data maps where the keys and values are themselves named tuples. To
access a named value of a given tuple, the function `(get #name
tuple)` will return that item from the tuple.
### Reading from Other Smart Contracts
While a smart contract may not _modify_ other smart contracts' data
directly, it _can_ read data stored in those smart contracts' maps.
(Note: this does not alter any confidentiality guarantees of the smart
contracting language. All data in the smart contracts is inherently
public, and will be readable through querying the underlying database
in any case.) In order to do so, a contract may use the
`(contract-map-get)` function, which behaves identically to
`(map-get)`, though it accepts a contract principal as an argument
in addition to the map name:
```
(contract-map-get
'contract-principal
'map-name
'key-tuple) -> value tuple or none
Example:
(contract-map-get
'SC3H92H297DX3YDPFHZGH90G8Z4NPH4VE8E83YWAQ
'name-map
12234) -> returns owner principal of name represent by integer 12234
```
Just as with the `(contract-call?)` function, the map name and contract
principal arguments must be constants, specified at the time of
publishing.
### Time-shifted Evaluations
The Stacks language supports _historical_ data queries using the
@@ -719,8 +689,8 @@ In this simple scheme, names are represented by integers, but in
practice, a buffer would probably be used.
```scheme
(define burn-address '1111111111111111111114oLvT2)
(define (price-function name)
(define-constant burn-address '1111111111111111111114oLvT2)
(define-private (price-function name)
(if (< name 1e5) 1000 100))
(define-map name-map