Skip to content

Latest commit

 

History

History
292 lines (208 loc) · 15.5 KB

CONTRACTS.md

File metadata and controls

292 lines (208 loc) · 15.5 KB

Deploying Contracts

The following provides a step-by-step set of examples including basic and advanced features to deploy contracts. Prior knowledge about permissions and execution semantics is recommended and can be found in our Techspec here.

Prerequisites

Using binaries (recommended):

  • Install rustup.
  • Install the casperlabs package, which contains casperlabs-client.

Building from source:

If you build from source, you will need to add the build directories to your PATH, for example:

export PATH="<path-to-CasperLabs-repo>/client/target/universal/stage/bin:$PATH"

Or you can run the client commands from the root directory of the repo using explicit paths to the binaries.

Instructions

Step 1: Clone the main repo to obtain the example contracts and set up your toolchain
git clone [email protected]:CasperLabs/CasperLabs.git
cd CasperLabs/execution-engine
rustup toolchain install $(cat rust-toolchain)
rustup target add --toolchain $(cat rust-toolchain) wasm32-unknown-unknown

Source code of contract examples are currently located in ./execution-engine/contracts/examples directory inside the main repo as follows here.

Step 2: Build the example contracts
make build-example-contracts
export COUNTER_DEFINE="$(pwd)/target/wasm32-unknown-unknown/release/counter_define.wasm"
Step 3: Create an account at clarity.casperlabs.io

Create an account with an account key, which automatically creates a new keypair. This keypair should be downloaded to the machine where you will deploy contracts.

Step 4: Add motes to the account

You can add motes to the account using the faucet. Select the account key associated with the account you want to add motes to and "Request Tokens". When the process is complete, the status is updated with the available amount to use with your account.

Step 5: Deploy counterdefine.wasm
casperlabs-client \
    --host deploy.casperlabs.io \
    deploy \
    --private-key <path-to-private-key> \
    --session $COUNTER_DEFINE \
    --payment-amount 2000000

Note: --payment-amount is used to define the maximum number of motes to spend on the execution of the deploy. As shown in the example, 2,000,000 is the amount needed to execute the counter define contract, see further details here.

Source code for the contract used in this example is found here.

You should see the following output:

Success!

Note: The deploy command is a convenience function combining multiple actions (make, sign, send) in the case of a single signature. For signing with multiple keys, see Advanced usage in this document.

Step 6: Observe

See the instructions here.

Step 7: Call the counter contract
casperlabs-client \
    --host deploy.casperlabs.io \
    deploy \
    --private-key <path-to-private-key> \
    --session-name counter_inc \
    --payment-amount 2000000

You should see the following output:

Success!

--session-name tells the system to use a previous stored contract under the given name. In this case the counter_define wasm we deployed in Step 5 stored a contract under the name counter_inc, which we can now call.

Note: store_function stores a contract under a URef (Key::URef), store_function_at_hash stores a contract under a Hash ( Key::Hash), a 256-bit unique identifier which is generated by the system. put_key is used to associate a Key with a human-readable name (this is only valid in the context where put_key is run via a store_* function.

--session-name works with either storage function, --session-hash works with contracts stored at hashes.

Note: when a contract is stored under a Hash it is immutable (that Hash will always point to exactly that contract), while storing under a URef allows the contract to be upgraded, for example, with the upgrade_contract_at_uref function, see CasperLabs Contract_API and source here for details.

For details about storage see the Contract API here.

Step 8: Call a contract with arguments
export TRANSFER="$(pwd)/target/wasm32-unknown-unknown/release/transfer_to_account.wasm"

casperlabs-client \
    --host deploy.casperlabs.io \
    deploy \
    --private-key <path-to-new-private-key> \
    --session $TRANSFER \
	  --session-args '[{"name" : "target", "value" : {"bytes_value" : "<base-16-public-key>"}}, {"name": "amount", "value" : {"long_value" : 1000}}]' \
    --payment-amount 2000000

<public-key-in-hex> is the address to send the motes to.

Note: Transfers can be done in a more convenient way using the transfer sub-command of the client, see casperlabs-client transfer --help for details.

Contract argument details

Smart contracts can be parametrized. A list of contract arguments can be specified on command line when the contract is deployed.

The client's deploy command accepts parameter --session-args that can be used to specify types and values of contract arguments as a serialized sequence of Arg values in a protobuf JSON format, with binary data represented in Base16 format.

Note: contract arguments are positional, and so the "name" attribute is currently not used. However, we plan to change contract arguments to be keyword (named) arguments. The structure of the Arg protobuf message and its JSON serialized form is ready for this change.

Supported types of contract arguments

protobuf Arg Contract API type Example value in protobuf JSON format
int_value u32 '[{"name": "amount", "value": {"int_value": 123456}}]'
long_value u64 '[{"name": "amount", "value": {"long_value": 123456}}]'
big_int u512 '[{"name": "amount", "value": {"big_int": {"value": "123456", "bit_width": 512}}}]'
string_value String '[{"name": "surname", "value": {"string_value": "Nakamoto"}}]'
optional_value Option<T> '{"name": "maybe_number", "value": {"optional_value": {}}} or {"name": "maybe_number", "value": {"optional_value": {"long_value": 1000000}}}'
hash Key::Hash '{"name": "my_hash", "value": {"key": {"hash": {"hash": "9d39b7fba47d07c1af6f711efe604a112ab371e2deefb99a613d2b3dcdfba414"}}}}'
address Key::Address '{"name": "my_address", "value": {"key": {"address": {"account": "9d39b7fba47d07c1af6f711efe604a112ab371e2deefb99a613d2b3dcdfba414"}}}}'
uref Key::URef '{"name": "my_uref", "value": {"key": {"uref": {"uref": "9d39b7fba47d07c1af6f711efe604a112ab371e2deefb99a613d2b3dcdfba414", "access_rights": 5}}}}'
local Key::Local '{"name": "my_local", "value": {"key": {"local": {"hash": "9d39b7fba47d07c1af6f711efe604a112ab371e2deefb99a613d2b3dcdfba414"}}}}'
int_list Vec<i32> '{"name": "my_int_list", "value": {"int_list": {"values": [0, 1, 2]}}}'
string_list Vec<String> '{"name": "my_string_list", "value": {"string_list": {"values": ["A", "B", "C"]}}}'

Numeric values of access_rights in uref are defined in `enum AccessRights in state.proto.

Advanced usage

Creating, signing, and deploying contracts with multiple signatures

The deploy command on its own provides multiple actions strung together optimizing for the common case, with the capability to separate concerns between your key management and deploy creation. See details about generating account key pairs here.

Every account can associate multiple keys with it and give each a weight. Collective weight of signing keys decides whether an action of certain type can be made. In order to collect weight of different associated keys, a deploy has to be signed by corresponding private keys. The deploy command creates a deploy, signs it and deploys to the node but doesn't allow for signing with multiple keys. Therefore, we split deploy into separate commands:

  • make-deploy - creates a deploy from input parameters
  • sign-deploy - signs a deploy with given private key
  • print-deploy - prints information of a deploy
  • send-deploy - sends a deploy to CasperLabs node
  • show-deploy - queries the status of a deploy

To make a deploy signed with multiple keys: first make the deploy with make-deploy, sign it with the keys calling sign-deploy for each key, and then send it to the node with send-deploy.

Commands read input deploy from both a file (-i flag) and STDIN. They can also write to both file and STDOUT.

For more detailed description about deploy commands, use the --help flag (casper-client --help).

You can find detailed information about associated keys and weights here.

Example usage

Creating a deploy

The following command will write a deploy in binary format to STDOUT:

casperlabs-client \
    --host localhost \
    make-deploy \
    --session session-code.wasm \
    --payment payment-code.wasm \
    --from a1130120d27f6f692545858cc5c284b1ef30fe287caef648b0c405def88f543a

It is possible to write it to a file, by supplying -o argument:

casperlabs-client \
    --host localhost \
    make-deploy \
    --session session-code.wasm \
    --payment payment-code.wasm \
    --from a1130120d27f6f692545858cc5c284b1ef30fe287caef648b0c405def88f543a
    -o /deploys/deploy_1

Time to live of a deploy

The time to live (TTL) of a deploy is the amount of time after its timestamp that the deploy may be included in a block. After the TTL has elapsed, if the deploy has not been included in a block, any node will discard it from the deploy buffer. The TTL can be specified (in units of milliseconds) from the CasperLabs client using the --ttl-millis argument in the deploy sub-command:

casperlabs-client\
    --host deploy.casperlabs.io \
    deploy \
    --ttl-millis <arg>

A protocol-level maximum value for the TTL is given in the Chainspec; values larger than this will not be accepted. In addition, each node sets a minimum accepted value as part of its local configuration (set by the node operator). By default CasperLabs nodes set the minimum allowed TTL to be 24 hours. If no TTL is specified then the maximum value is used by default.

Deploy dependencies

casperlabs-client\
    --host deploy.casperlabs.io \
    deploy \
    --dependencies <arg>...

This parameter provides a mechanism implemented to explicitly enforce an ordering to deploys as sometimes this is important. Use the CasperLabs client deploy sub-command.--dependencies passes the argument list of deploy hashes (base16 encoded) which must be executed before this deploy.

Signing a deploy
casperlabs-client \
    --host localhost \
    sign-deploy \
    --public-key public-key.pem \
    --private-key private-key.pem

This will read a deploy to sign from STDIN and output signed deploy to STDOUT. There are -i and -o flags for, respectively, reading a deploy from a file and writing signed deploy to a file.

Note: this step may be repeated multiple times to sign a deploy with multiple keys. This feature allows supporting multi-sig transactions out-of-the-box. You can see details about generating account keys here.

You can find more informatiom about Associated Keys and Weights here

Printing a deploy
casperlabs-client \
    --host localhost \
    print-deploy

This will print information of a deploy into STDOUT. There are --json and --bytes-standard flags for, respectively, using standard JSON vs Protobuf text encoding and standard ASCII-escaped for Protobuf or Base64 for JSON bytes encoding vs custom Base16. The same set of flags is also available for all show-* and query-state commands.

Sending deploy to the node
casperlabs-client \
    --host localhost \
    send-deploy

In the example above there is no -i argument, meaning that the signed deploy will be read from STDIN.

Reading from STDIN and writing to STDOUT allows for piping output from one command to the input of another one (commands are incomplete for better readability):

casperlabs-client make-deploy [arguments] | \
casperlabs-client sign-deploy --private-key [private_key] --public-key [public_key] | \
casperlabs-client send-deploy
Showing deploy status
casperlabs-client\
    --host deploy.casperlabs.io \
    --port 40401 show-deploy <deploy-hash>

The show-deploy command will return a status (pending, processed, finalized, discarded as well as information about its execution (success or error with message), and the block(s) it is included in (if any).

See a description of state provided here for the status' as listed:

  • PENDING
  • PROCESSED
  • FINALIZED
  • DISCARDED

You can also retrieve further information about a deploy from our platform (APIs, et. al.). See additional details here.

Using a local standalone node

If you are testing with a local standalone node, you will need to change the --host argument:

casperlabs-client \
    --host 127.0.0.1 \
    deploy \
    --private-key <path-to-private-key> \
    --session $COUNTER_DEFINE

You will also need to explicitly propose after making a deploy (or several deploys), in order for your deploys to be committed:

casperlabs-client --host 127.0.0.1 propose