# Secret Contracts
Secret Contracts are the first implementation of general purpose privacy preserving computations on public blockchain. While similar to Ethereum smart contracts in design, Secret Contracts work with encrypted data (inputs, encrypted outputs and encrypted state). These privacy guarantees are made possible by a decentralized network of validators, who run Secret Contracts execution inside Trusted Execution Environments (TEEs).
Secret Contracts are Rust based smart contracts that compile to WebAssembly. Secret Contracts, which are based on Go-CosmWasm, introduce the compute module that runs inside the TEE to enable secure data processing (inputs, outputs and contract state.
Next, we will go through steps to:
- install Rust
- 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 here.
- 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
Update the rust compiler
In case rust is installed already, make sure to update the rust compiler.
rustup update
- 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 secret contract project.
cargo install cargo-generate --features vendored-openssl
# Create your first Secret Contract
- generate the initial project
- compile the secret contract
- run unit tests
- optimize the wasm contract bytecode to prepare for deployment
- deploy the secret contract to your local Secret Network
- instantiate it with contract parameters
# Generate the Secret Contract Project
cargo generate --git https://github.com/enigmampc/secret-template --name mysimplecounter
The git project above is a secret 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
As an example secret contract, mysimplecounter
, handles a state keeping track of a number which may be incremented by any address, but only reset by the creator.
The src
folder contains the following files:
# contract.rs
This file contains functions which define the available contract operations. The functions which all secret contracts contain will be: init
, handle
, and query
.
init
is called once at instantiation of the secret contract with 3 parameters:deps
,env
, andmsg
. The internal state (State
struct imported fromstate.rs
)of the secret contract is initialized with these parameters.
pub fn init<S: Storage, A: Api, Q: Querier>(
deps: &mut Extern<S, A, Q>,
env: Env,
msg: InitMsg,
) -> StdResult<InitResponse> {
let state = State {
count: msg.count,
owner: deps.api.canonical_address(&env.message.sender)?,
};
config(&mut deps.storage).save(&state)?;
Ok(InitResponse::default())
}
deps
and env
are structs Extern
and Env
imported from cosmwasm_std
deps
contains all external dependencies of the contract.
pub struct Extern<S: Storage, A: Api, Q: Querier> {
pub storage: S,
pub api: A,
pub querier: Q,
}
env
contains external state information of the contract.
pub struct Env {
pub block: BlockInfo,
pub message: MessageInfo,
pub contract: ContractInfo,
pub contract_key: Option<String>,
#[serde(default)]
pub contract_code_hash: String,
}
BlockInfo
defines the current block height, time, and chain-id.MessageInfo
defines the address which instantiated the contract and possibly funds sent to the contract at instantiation.ContractInfo
is the address of the contract instance.contract_key
is the code-id used when instantiating the contractcontract_code_hash
is the hex encoded hash of the code. This is used by Secret Network to harden against replaying the contract. It is used to bind the request to a destination contract in a stronger way than just the contract address which can be faked
msg
is the InitMsg
struct imported from msg.rs
. In this case, it defines the initial state of the counter.
pub struct InitMsg {
pub count: i32,
}
The return value of init
(if there are no errors) is an InitResponse
.
pub struct InitResponse<T = Empty>
where
T: Clone + fmt::Debug + PartialEq + JsonSchema,
{
pub messages: Vec<CosmosMsg<T>>,
pub log: Vec<LogAttribute>,
}
handle
handles all incoming transactions. These computations occur on-chain.
The msg
parameter is the HandleMsg
struct imported from msg.rs
. This struct defines the available operations, while the callable functions(try_increment
and try_reset
) are defined in contract.rs
.
pub fn handle<S: Storage, A: Api, Q: Querier>(
deps: &mut Extern<S, A, Q>,
env: Env,
msg: HandleMsg,
) -> StdResult<HandleResponse> {
match msg {
HandleMsg::Increment {} => try_increment(deps, env),
HandleMsg::Reset { count } => try_reset(deps, env, count),
}
}
query
messages are off-chain computations.
The msg
parameter is the QueryMsg
struct imported from msg.rs
. This struct defines the available operations, while the callable function(query_count
) is defined in contract.rs
.
pub fn query<S: Storage, A: Api, Q: Querier>(
deps: &Extern<S, A, Q>,
msg: QueryMsg,
) -> StdResult<Binary> {
match msg {
QueryMsg::GetCount {} => to_binary(&query_count(deps)?),
}
}
# state.rs
The internal state is defined in this file, by the State
struct.
pub struct State {
pub count: i32,
pub owner: CanonicalAddr,
}
The state is saved in a Storage
struct.
This Storage
struct is wrapped in a Singleton
and ReadonlySingleton
. To learn more about the different types of storage, read the documentation for cosmwasm_storage.
pub fn config<S: Storage>(storage: &mut S) -> Singleton<S, State> {
singleton(storage, CONFIG_KEY)
}
pub fn config_read<S: Storage>(storage: &S) -> ReadonlySingleton<S, State> {
singleton_read(storage, CONFIG_KEY)
}
In this example, the state contains an integer count
and the owner
of the contract. owner
is an instance of the CanonicalAddr
struct. Storage
and CanonicalAddr
are imported from cosmwasm_std.
# msg.rs
Contract computations are defined as messages. The JsonSchema for these messages is defined in msg.rs
.
The InitMsg
struct describes the msg
parameter passed to the contract init
function in contract.rs
.
HandleMsg
and QueryMsg
are enums representing possible transaction or query computations. These enums are used to describe the msg
parameter for handle
and query
functions in contract.rs
.
pub enum HandleMsg {
Increment {},
Reset { count: i32 },
}
pub enum QueryMsg {
GetCount {},
}
Calls to handle
and query
can optionally provide a response. These response messages are defined as structs. In this example, the query_count
function in contract.rs
returns the CountResponse
struct.
pub struct CountResponse {
pub count: i32,
}
# lib.rs
This file is essentially boilerplate that you shouldn't need to modify. It provides the necessary abstraction to interact with the wasmi VM and compile the contract to wasm.
# Compile the Secret Contract
Use the following command to compile the Secret Contract, which produces the wasm contract file.
cargo wasm
# Run Unit Tests
Tests in this template currently fail unless you have SGX enabled.
RUST_BACKTRACE=1 cargo unit-test
# Integration Tests
The integration tests are under the tests/
directory and run as:
cargo integration-test
# Generate Msg Schemas
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