From 799e2c066a7e1aab5090f19fdbaff63369c443c0 Mon Sep 17 00:00:00 2001 From: Aaron Blankstein Date: Tue, 28 Apr 2020 10:01:51 -0500 Subject: [PATCH] docs: update clarity references to latest from stacks-blockchain:master (#578) --- _core/smart/clarityCLI.md | 4 +- _core/smart/clarityRef.md | 200 +++++++++++++++++++++----------------- _core/smart/functions.md | 116 ---------------------- 3 files changed, 113 insertions(+), 207 deletions(-) delete mode 100644 _core/smart/functions.md diff --git a/_core/smart/clarityCLI.md b/_core/smart/clarityCLI.md index 676c570a..1cba0f56 100644 --- a/_core/smart/clarityCLI.md +++ b/_core/smart/clarityCLI.md @@ -6,7 +6,7 @@ permalink: /:collection/:path.html # Clarity CLI {:.no_toc} -You use the `clarity-cli` command to work with smart contracts within the Blockstack virtual environment. This command has the following subcommands: +You use the `clarity-cli` command to work with smart contracts within a local-only Blockstack virtual environment. This command has the following subcommands: * TOC {:toc} @@ -89,4 +89,4 @@ Executes a public function of a defined contract. clarity-cli generate_address ``` -Generates a random Stacks public address for testing purposes. \ No newline at end of file +Generates a random Stacks public address for testing purposes. diff --git a/_core/smart/clarityRef.md b/_core/smart/clarityRef.md index 31ae569c..2390250b 100644 --- a/_core/smart/clarityRef.md +++ b/_core/smart/clarityRef.md @@ -10,104 +10,126 @@ This file contains the reference for the Clarity language. * TOC {:toc} -## 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. - -### Uint type - -The unsigned integer type (`uint`) in the Clarity language is a 16-byte unsigned integer. Using the unsigned type can ensure that any value underflows (negative numbers) will cause a transaction to be aborted. - -Anywhere a developer wishes to use a literal unsigned integer (for example, incrementing or decrementing an input by a constant) the integer literal should use the `u` prefix, e.g., `u123`. - -### Bool type - -Supports values of `true` or `false`. - -### Buffer type - -Buffer types represent fixed-length byte buffers. A Buffer can either be constructed using: -- String literals, for example `"alice.id"` or `hash160("bob.id")`, -- Hexadecimals literals, for example `0xABCDEF`. - -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. - -### 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-constant imaginary-number-a (tuple (real 1) (i 2))) -(define-constant imaginary-number-b (tuple (real 2) (i 3))) +## Clarity Type System + +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: + +* `(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) ``` -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: - - - - - - - - - - - - - - - - - - - - - - -
FunctionDescription
(map-get map-name key-tuple)Fetches the value associated with a given key in the map, or returns none if there is no such value.
(map-set! map-name key-tuple value-tuple)Sets the value of key-tuple in the data map
(map-insert! map-name key-tuple value-tuple)Sets the value of key-tuple in the data map if and only if an entry does not already exist.
(map-delete! map-name key-tuple)Deletes key-tuple from the data map.
- - -To access a named value of a given tuple, the `(get name tuple)` function returns that item from the tuple. - -### Optional type - -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. +* Dynamic dispatch: the callee is passed as an argument, and typed +as a trait reference (). + +```scheme +(define-public (swap (token-a ) + (amount-a uint) + (owner-a principal) + (token-b ) + (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)))) +``` -### Response type +Traits can either be locally defined: -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). +```scheme +(define-trait can-transfer-tokens ( + (transfer-from? (principal principal uint) (response uint))) +``` -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). +Or imported from an existing contract: -### Algebraic +```scheme +(use-trait can-transfer-tokens + .contract-defining-trait.can-transfer-tokens) +``` -Clarity supports limited algebraic data types, it has an `(optional A)` type and a `(response A B)` type. An `(optioanl A)` type can either be `(some A)` or `(none)`, and a `(response A B)` type can either be `(ok A)` or `(err B)`. For example, `(some u3)` and `(none)` would have type `(optional uint)`, and `(ok \"woot!\")` and `(err 'false)` would have type `(response (buff 5) bool)`. +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: -The algebraic data types have two variants (e.g. `(some ...)` or `(none)`; `(ok ...)` or `(err ...)`). +```scheme +(impl-trait .contract-defining-trait.can-transfer-tokens) +``` -The `default-to`, `expects`, and `expects-err!` functions unpack algebraic data types. To *unpack* means to extract one of the "inner" types. Unpacking an `(optional A)` means to do something to get at `A`, and unpacking `(response A B)` means to do something to get at ether `A` or `B` (where "do something" is specific to the function doing the unpacking). The built-in functions that "unpack" an algebraic data type each behave differently. +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. + +The following limitations are imposed on contract calls: + +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. ## Keyword reference diff --git a/_core/smart/functions.md b/_core/smart/functions.md deleted file mode 100644 index a4d43ef5..00000000 --- a/_core/smart/functions.md +++ /dev/null @@ -1,116 +0,0 @@ ---- -layout: smart -description: "Clarity: Defining Functions and Maps" -permalink: /:collection/:path.html ---- -# Defining Functions and Maps -{:.no_toc} - -Clarity includes _defines_ and native functions for creating user-defined functions. - -* TOC -{:toc} - -## define-private and define-public functions - -Functions specified via `define-public` statements are public functions. Functions without these designations, simple `define-private` 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: - - - - - - - - - - - - - - - - - - - -
foo.A =>bar.BData impact that results
Function returnserrokNo changes result from either function.
okerrChange from foo.A is possible; no changes from bar.B materialize.
- -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: - - - - - - - - - - - - - - - - - - - - - - -
FunctionDescription
(map-get? map-name key-tuple)Fetches the value associated with a given key in the map. Returns (optional (tuple))
(map-set map-name key-tuple value-tuple)Sets the value of key-tuple in the data map.
(map-insert map-name key-tuple value-tuple)Sets the value of key-tuple in the data map if and only if an entry does not already exist.
(map-delete map-name key-tuple)Removes the value associated with the input key for the given map.
- -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 `map-set` 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-public ...)`, `(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. - -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. -