This repository can be used to get up and running on a local Secret Network developer testnet (secretdev) to start working with the cosmwasm-based smart contracts (soon to be secret contracts!).
The developer blockchain is configured to run inside a docker container. Install Docker for your environment (Mac, Windows, Linux).
Open a terminal window and change to your project directory. Then start SecretNetwork, labelled secretdev from here on:
$ docker run -it --rm \
-p 26657:26657 -p 26656:26656 -p 1317:1317 \
--name secretdev enigmampc/secret-network-sw-dev:v1.0.2
NOTE: The secretdev docker container can be stopped by CTRL+C
At this point you're running a local SecretNetwork full-node. Let's connect to the container so we can view and manage the secret keys:
NOTE: In a new terminal
docker exec -it secretdev /bin/bash
The local blockchain has a couple of keys setup for you (similar to accounts if you're familiar with Truffle Ganache). The keys are stored in the test
keyring backend, which makes it easier for local development and testing.
secretcli keys list --keyring-backend test
exit
when you are done
Secret Contracts are based on Cosmwasm which is an implementation on the Cosmos network. The CosmWasm smart contracts are like Ethereum's smart contracts except they can be used on other blockchains using the Inter Blockchain Protocol (IBC). CosmWasm smart contracts are written in the Rust language.
The SecretNetwork has a compute module that we'll use to store, query and instantiate the smart contract. Once stored on the blockchain the smart contract has to be created (or instantiated) in order to execute its methods. This is similar to doing an Ethereum migrate
using truffle which handles the deployment and creation of a smart contract.
Eventually the smart contracts will become secret contracts (in a future blockchain upgrade) running in an SGX enclave (Trusted Execution Environment) where computations are performed on the encrypted contract data (i.e. inputs, state).
Next we'll walkthrough steps to:
- install Rust (you can check out the Rust book, rustlings course, examples and more at https://www.rust-lang.org/learn)
- install the Rust dependencies
- create your first project
The Rust dependencies include the Rust compiler, cargo (package manager), toolchain and a package to generate projects (you can check out the Rust book, rustlings course, examples and more at https://www.rust-lang.org/learn).
- Install Rust
More information about installing Rust can be found here: https://www.rust-lang.org/tools/install.
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source $HOME/.cargo/env
- Add rustup target wasm32 for both stable and nightly
rustup default stable
rustup target list --installed
rustup target add wasm32-unknown-unknown
rustup install nightly
rustup target add wasm32-unknown-unknown --toolchain nightly
- If using linux, install the standard build tools:
apt install build-essential
- Run cargo install cargo-generate
Cargo generate is the tool you'll use to create a smart contract project (https://doc.rust-lang.org/cargo).
cargo install cargo-generate --features vendored-openssl
To create the smart contract you'll:
- generate the initial project
- compile the smart contract
- run unit tests
- optimize the wasm contract bytecode to prepare for deployment
- deploy the smart contract to your local SecretNetwork
- instantiate it with contract parameters
Generate the smart contract project
cargo generate --git https://github.com/enigmampc/secret-template --name mysimplecounter
The git project above is a cosmwasm smart contract template that implements a simple counter. The contract is created with a parameter for the initial count and allows subsequent incrementing.
Change directory to the project you created and view the structure and files that were created.
cd mysimplecounter
The generate creates a directory with the project name and has this structure:
Cargo.lock Developing.md LICENSE Publishing.md examples schema tests
Cargo.toml Importing.md NOTICE README.md rustfmt.toml src
Use the following command to compile the smart contract which produces the wasm contract file.
cargo wasm
Run unit tests
RUST_BACKTRACE=1 cargo unit-test
The integration tests are under the tests/
directory and run as:
cargo integration-test
We can also generate JSON Schemas that serve as a guide for anyone trying to use the contract, to specify which arguments they need.
Auto-generate msg schemas (when changed):
cargo schema
Before deploying or storing the contract on the testnet, you need to run the secret contract optimizer.
docker run --rm -v "$(pwd)":/code \
--mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \
--mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \
enigmampc/secret-contract-optimizer
The contract wasm needs to be optimized to get a smaller footprint. Cosmwasm notes state the contract would be too large for the blockchain unless optimized. This example contract.wasm is 1.8M before optimizing, 90K after.
This creates a zip of two files:
- contract.wasm
- hash.txt
# First lets start it up again, this time mounting our project's code inside the container.
docker run -it --rm \
-p 26657:26657 -p 26656:26656 -p 1317:1317 \
-v $(pwd):/root/code \
--name secretdev enigmampc/secret-network-sw-dev:v1.0.0
Upload the optimized contract.wasm.gz:
docker exec -it secretdev /bin/bash
cd code
secretcli tx compute store contract.wasm --from a --gas 1000000 -y --keyring-backend test
List current smart contract code
secretcli query compute list-code
[
{
"id": 1,
"creator": "secret1zy80x04d4jh4nvcqmamgjqe7whus5tcw406sna",
"data_hash": "D98F0CA3E8568B6B59772257E07CAC2ED31DD89466BFFAA35B09564B39484D92",
"source": "",
"builder": ""
}
]
At this point the contract's been uploaded and stored on the testnet, but there's no "instance."
This is like discovery migrate
which handles both the deploying and creation of the contract instance, except in Cosmos the deploy-execute process consists of 3 steps rather than 2 in Ethereum. You can read more about the logic behind this decision, and other comparisons to Solidity, in the cosmwasm documentation. These steps are:
- Upload Code - Upload some optimized wasm code, no state nor contract address (example Standard ERC20 contract)
- Instantiate Contract - Instantiate a code reference with some initial state, creates new address (example set token name, max issuance, etc for my ERC20 token)
- Execute Contract - This may support many different calls, but they are all unprivileged usage of a previously instantiated contract, depends on the contract design (example: Send ERC20 token, grant approval to other contract)
To create an instance of this project we must also provide some JSON input data, a starting count.
INIT="{\"count\": 100000000}"
CODE_ID=1
secretcli tx compute instantiate $CODE_ID "$INIT" --from a --label "my counter" -y --keyring-backend test
With the contract now initialized, we can find its address
secretcli query compute list-contract-by-code 1
Our instance is secret18vd8fpwxzck93qlwghaj6arh4p7c5n8978vsyg
We can query the contract state
CONTRACT=secret18vd8fpwxzck93qlwghaj6arh4p7c5n8978vsyg
# NB On public testnet
secretcli query compute query $CONTRACT "{\"get_count\": {}}"
# Add if using the Docker version
secretcli query compute contract-state smart $CONTRACT "{\"get_count\": {}}"
And we can increment our counter
secretcli tx compute execute $CONTRACT "{\"increment\": {}}" --from a --keyring-backend test
The source directory (src/
) has these files:
contract.rs lib.rs msg.rs state.rs
The developer modifies contract.rs
for contract logic, contract entry points are init
, handle
and query
functions.
init
in the Counter contract initializes the storage, specifically the current count and the signer/owner of the instance being initialized.
We also define handle
, a generic handler for all functions writing to storage, the counter can be incremented and reset. These functions are provided the storage and the environment, the latter's used by the reset
function to compare the signer with the contract owner.
Finally we have query
for all functions reading state, we only have query_count
, returning the counter state.
The rest of the contract file is unit tests so you can confidently change the contract logic.
The state.rs
file defines the State struct, used for storing the contract data, the only information persisted between multiple contract calls.
The msg.rs
file is where the InitMsg parameters are specified (like a constructor), the types of Query (GetCount) and Handle[r] (Increment) messages, and any custom structs for each query response.
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct InitMsg {
pub count: i32,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "lowercase")]
pub enum HandleMsg {
Increment {},
Reset { count: i32 },
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "lowercase")]
pub enum QueryMsg {
// GetCount returns the current count as a json-encoded number
GetCount {},
}
// We define a custom struct for each query response
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct CountResponse {
pub count: i32,
}
Unit tests are coded in the contract.rs
file itself:
#[cfg(test)]
mod tests {
use super::*;
use cosmwasm::errors::Error;
use cosmwasm::mock::{dependencies, mock_env};
use cosmwasm::serde::from_slice;
use cosmwasm::types::coin;
#[test]
fn proper_initialization() {
let mut deps = dependencies(20);
let msg = InitMsg { count: 17 };
let env = mock_env(&deps.api, "creator", &coin("1000", "earth"), &[]);
// we can just call .unwrap() to assert this was a success
let res = init(&mut deps, env, msg).unwrap();
assert_eq!(0, res.messages.len());
// it worked, let's query the state
let res = query(&deps, QueryMsg::GetCount {}).unwrap();
let value: CountResponse = from_slice(&res).unwrap();
assert_eq!(17, value.count);
}
Smart Contracts in the Secret Network use cosmwasm. Therefore, for troubleshooting and additional context, cosmwasm documentation may be very useful. Here are some of the links we relied on in putting together this guide: