Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Secret Network offers scalable permissionless smart contracts with a private by default design— bringing novel use cases to blockchain not feasible on public systems. Secret Network was the first protocol to provide private smart contracts on mainnet, live since September 2020.
Secret Network is built with the Cosmos Software Development Kit (SDK) bringing interoperable privacy to the entire Cosmos and EVM ecosystems. Secret Network uses a combination of the Intel SGX (Software Guard Extension) Trusted Execution Environment technology, several encryption schemes, and key management to bring privacy by default to blockchain users. Secret Contracts are an implementation of the Rust-based smart contract compiling toolkit CosmWasm, adding private metadata possibilities. Secret Network is powered by the Native public coin SCRT which is used for fees, Proof-of-Stake, security, and Governance. With more than 30+ Dapps on mainnet and 100+ full time builders, Secret Network aims to bring privacy to the world.
Get the latest version: https://github.com/scrtlabs/SecretNetwork
Secret Network is built with the Cosmos SDK and the Tendermint consensus engine. Secret Network provides a platform for scalable, private permissionless smart contracts which can connect to the interchain.
Secret Network leverages novel key management techniques, encryption schemes, and Trusted Execution Environment (TEE) technology to bring encrypted input, output, and state to the blockchain.
The decentralized network of computers that host Secret Network come to a consensus (delegated Proof-of-Stake — Byzantine Fault Tolerance) without ever obtaining access to the data they process. Users can use “viewing keys” to view their sensitive data or enable third parties to do the same.
Secret brings privacy to the Interchain by leveraging CosmWasm IBC compatible smart contracts and IBC native token bridging. In this Section we dive into all of these elements of the techstack.
Steps Of A Private Transactions - overview
For a video introduction to the Secret techstack you can watch this playlist:
An extensive discussion of Secret Network's cryptography
A simple way to describe a blockchain is that it’s a distributed ledger on a peer-to-peer (P2P) network where each node of that network contains a copy of the blockchain state. The state of the blockchain is updated by an application and broadcast to each node, agreeing on this new state through consensus.
Want to learn about Cosmos and the Secret Techstack via a video instead? Check this video series on the Secret Network Youtube.
Cosmos is an ecosystem of independent, interconnected and application specific blockchains commonly referred to as the internet of blockchains. By having all these application specific interconnected blockchains, the ecosystem can easily scale horizontally which avoids most of the bottlenecks and scalability issues we know today on other heterogenous blockchains.
The conceptual layers of a blockchain are:
We will cover in this article the following three layers for an ease of understanding:
Network layer: Responsible for the propagation of transactions between the nodes of the ecosystem
Consensus layer: Enables the nodes to agree on the current state of the system.
Application layer: Responsible for updating the state given a set of transactions, i.e. processing transactions
To reach adoption and grow the internet of blockchain, developers should focus on their application specific development. They should not spend time on building from scratch the consensus and networking layers. That’s what Tendermint core is taking care of by providing Networking and Consensus layers saving a lot of time and headaches for the developers.
The following figure shows the Networking and Consensus layers as “Tendermint Core” and Cosmos SDK as the “Application layer”:
On top of Tendermint Core, Cosmos introduced the Cosmos SDK, an open-source framework for building application specific blockchains with composable modules (plugins) to help developers build their blockchain faster and easier. Developers could build their application directly on Tendermint core using ABCI to interact with Tendermint but Cosmos SDK allows a faster and battle tested path to bootstrap an application.
In order to scale horizontally and fulfill the vision of an internet of interoperable blockchains, blockchains can support the Inter-Blockchain Communication Protocol (IBC), which enables value transfers, token transfers, and other communication between chains, all without the need to involve exchanges or make compromises regarding the chains' sovereignties. Following figures show blockchains interconnected with IBC (IBC Networks)\
The following process is based on how Tendermint works to reach consensus in a standard app chain but with specificity related to the privacy features of Secret Network. Please note that Secret Network is permissionless, validators and nodes can join the network at any time.
Developers write and deploy Secret Contracts which are executed by the validators, the binary for these contracts is public.
Users submit transactions to the mempool — which can include encrypted data inputs
Validators receive encrypted data from users and execute the Secret Contract
During Secret Contract execution:
Encrypted inputs are decrypted inside a Trusted Execution Environment
Requested functions are executed inside a Trusted Execution Environment
Read/write state from Tendermint can be performed (state is always encrypted when at rest, and is only decrypted within the Trusted Execution Environment)
Outputs are encrypted
The block-proposing validator proposes a block containing the encrypted outputs and updated encrypted state
At least 2/3 of participating validators achieve consensus on the encrypted output and state following Tendermint BFT
The encrypted output and state is committed on-chain to the specific contract
Using the Eliptic-Curve Diffie Hellman key exchange (ECDH) the user generates a shared secret from their private key and the Secret Network public key. The network can generate the same shared secret using the user public key and the Network private key (only available within the Trusted Execution Environment (TEE)).
The user then generates a shared tx_encryption_key
using HKDF-SHA256 and the shared secret generated in step 1. The pseudo-random HDKF is used to ensure deterministic consensus across all nodes. The random component comes from a 256-bit nonce so that each transaction has its own encryption key, an AES-256-GCM encryption key is never used twice.
After initiating a transaction the user encrypts the input data with the shared transaction encryption key, using an AES-256-GCM authenticated encryption scheme.
The user then sends a single transaction with encrypted input data, gas fee in SCRT, and optionally a SCRT deposit (as required by the contract) to the contract address. The transaction adds a message with the user public key and the used Nonce so that the network can generate the same tx_encryption_key.
Validators receive the transaction in the shared mempool, after the Tendermint check_tx
is deemed successful, and take up the data to process the transaction.
Each validator runs on a machine with an Intel SGX compatible CPU. The enclave within this CPU is the trusted component, whereas the process interacting with the enclave locally from the outside is the untrusted component. The encrypted transaction input is passed from the untrusted to the trusted component.
The enclave uses ECDH to derive the same shared secret from the users public key and the networks private key. The network then derives the tx_encryption_key
from the public signed nonce and this shared secret using HDKF. Within the trusted component the transaction input is decrypted to plaintext.
The requested contract computation is executed on the plaintext input by the WASMI runtime, which is instantiated within the trusted enclave.
There are several possible outcomes from each computation, all of which occur on-chain simultaneously:
The contract state is updated.
The transaction output is encrypted for the transaction sender and committed on-chain
If present, callbacks to other contracts.
If present, send messages to other modules.
The output of the computation is encrypted using the tx_encryption_key
and an AES-256-GCM authenticated encryption scheme.
The encrypted output is then returned to the untrusted component of the validator node.
The validator that proposes the current block broadcasts the encrypted output.
Other validators compare their result to that of the block proposer. If more than two-thirds of the current voting power agrees on the result, the proposed block (and all transactions within it) are included in the Secret Network.
Want to learn about Secret transactions in a video format instead? Check out the last part of the Secret Network techstack video series.
In distributed systems, replication of information on all machines is fundamental. In our case, Tendermint is responsible for replicating securely and consistently the state-machine amongst the nodes of the system.
It is composed of a Byzantine-Fault-Tolerance (BFT) Proof-of-Stake (PoS) consensus algorithm and a peer-to-peer networking protocol. It communicates to the application layer through the ABCI protocol.
Tendermint allows developers to focus on the application level and not take care of peer discovery, block propagation, consensus, etc …
In short, BFT represents the ability of a system to continue operating even if some of its nodes fail or act maliciously. In the Tendermint case, it can only tolerate up to 1/3 of failures, meaning that the blockchain will halt momentarily until 2/3 of the validator set comes to consensus.
Unlike Nakamoto consensus where it is subject to 51% attack (meaning that 51% of the actors acting maliciously could attack and alter the blockchain), Tendermint is more resistant as it is subject to a 66% attack.
Tendermint consensus module relies on Proof-of-stake and BFT.
Let’s take a look at the consensus process from a high-level standpoint:
Transactions are received by the node and go into a local cache mempool
Before going into the node mempool, Tendermint asks the application if the transaction is valid through “CheckTx” ABCI message
If it’s valid, transaction is added to the mempool and broadcasted to the peer nodes
A new consensus round is initiated with a Proposer Node
The Proposer selects transactions in the mempool to be included in a new block
This proposed block is broadcasted to all nodes (Pre-vote phase) and nodes verify that the block is valid, simultaneously verifying that the Proposer is also valid and sign the pre-vote message
If >2/3 of nodes pre-vote for this block, block is valid (but not committed) and we enter the pre-commit phase
Same as pre-vote, Tendermint will wait for >2/3 of nodes to sign the pre-commit message
There are two stages of voting to tolerate Byzantine faults, meaning that the pre-commit ensures that enough validators witnessed the result of pre-vote stage
The block is then committed, broadcasted to all nodes and transactions in this block executed by the application to update its state (for example account balance update etc)
Once a block is committed, there is no way to revert it and that gives an instant finality
All nodes are processing the transactions of every block even if they are not the block proposer
A new round is proposed and a new proposer is designated
💡 For a graphic view of this consensus process check: What is Tendermint?
This consensus ensures that all nodes maintain the same blockchain, i.e. the same list of blocks containing the past transactions and that all nodes could propose a block through Proposer rotation.
Users, known as delegators here, can choose which validator they want to delegate based on their reputation, stability, security and infrastructure. The amount of the native chain token owned and delegated to a validator represents its voting power, giving them the opportunity to be a new block proposer more often. Decentralization here is “measured” by the voting power distribution amongst the validators and not the number of validators.
ABCI layer is the communication protocol for Tendermint to make requests to the Application, like CheckTx (as we saw in the consensus explanation), indicate a Begin or End of a block, Deliver transactions to the application through DeliverTx, Query the application for account balance for example.
Finally, below a figure representing the Tendermint stack and all elements we went through in this paragraph:
Blockchain technology is a complex base layer allowing open-source protocols and decentralized applications to be built on top of it. Secret is a sovereign layer 1 of the Cosmos ecosystem built with the Cosmos SDK.
This section will walk you through the Secret technology stack to get a better understanding of what’s under the hood of the privacy hub for Web3. This "Blockchain technology" section starts with a quick grasp of the different blockchain layers are and an explanation of the Cosmos stack
In this section we will explain in depth the components of the blockchain stack:
The Cosmos part of the Secret Techstack is also covered Part 1 and Part 2 of the Secret Network techstack video series.
The core vision of Cosmos is to scale horizontally by having an ecosystem of interconnected application specific blockchains. Inter-Blockchain Communication Protocol (IBC) is responsible for interconnecting heterogeneous blockchains or in another way, relaying data packets between arbitrary state machines (i.e application blockchains).
It could also be defined as a generic and standard protocol implementation for transferring value between chains, not only limited to Cosmos chains as other blockchains with different consensus could support IBC to communicate with the Cosmos Ecosystem (Polkadot, Ethereum through a peg zone for example).
IBC provides a common protocol for blockchains to communicate in a standardized and trustless way.
There is a lot to talk about IBC in terms of current and future capabilities, and the technology layers, but that’s not the goal of this paragraph. We’ll go through the high level principles here.
IBC is composed of two layers:
Transport layer (IBC/TAO) - provides the infrastructure for establishing secure connections and data packets authentication between chains
Application layer (IBC/APP) - defines how data packets should be packaged and interpreted by sending and receiving chains
This is wrapped up in a “light client” and relayers are external components for passing messages through blockchains:
Relayers are off-chain actors ensuring the “physical” connection between two chains, scanning the state of the two interconnected chains, looking for an intention to send a transaction on a chain and then relaying the data and its commitment proof to the other chain.
For example in the case of fungible token transfer between two chains, the relayers are responsible for proving that your tokens are locked in Chain A and giving a representation (Voucher) on Chain B.
Relayers are using the light client of each blockchain to verify incoming messages on chain A and submit them (and the proof of commitment) on chain B. Chain A can’t send data directly to chain B and instead “commit” the hash of the data packet in its own state machine. That’s this specific state that relayers are monitoring to send this packet and its proof to the chain B.
The major difference of IBC compared to existing bridge solutions is that there are no third parties to trust, no multisig involved, for transferring value/messages between blockchains. This concept of IBC native security means that if you trust chain A and chain B then you also trust IBC-TokenA on chain B and vice versa. As long as relayers are operated by any party and channels/ports are open and authentication successful between blockchains, messages can be passed along.
An analogy of IBC could be seen as an internet application on a computer. A channel is an IP connection, with the IBC portID being an IP port and the IBC channelID being an IP address.
IBC security does not depend on third parties to verify the validity of transactions between blockchain. IBC security is mostly done by the light clients who verify proofs of commitments and the state of the two interconnected blockchain. In short terms, IBC security is based on::
Trust in the chains you connect with
Fault isolation mechanisms, to limit damage done if a chain is acting maliciously
IBC is then still Byzantine resistant thanks to the proof validation by the light client. If a relayer were to act maliciously, the packet would be rejected by the counterparty chain because the proof would be invalid (because light client and relayers are independent).
Fungible token transfer is one example but IBC allows for Non-Fungible token transfer, multi-chain smart contracts and interchain accounts (interacting on a blockchain account from another source blockchain). This is the function enabling Secret Network to be the privacy hub for the Interchain. Other Cosmos chains can store and manipulate private data (even private keys) on Secret Network from the comfort of their own chain, Privacy as a service.
Finally, below a figure explaining the travel of an IBC packet between blockchains:
An introduction to CosmWasm
CosmWasm is a modular framework for writing secure smart contracts in Rust, and using them in any blockchain built with the Cosmos SDK. It's a low-level tool developers use to implement entirely new features. The framework enables the creation of modular and reusable code for smart contracts, without being exposed to the underlying nature of the blockchain and its inner workings. These smart contracts are run securely inside a WebAssembly (WASM) virtual machine (VM). WASM acts as an intermediate language compiler for the VM.
Everything in blockchain is a smart contract. To understand what this means, it helps to know that a smart contract is simply a set of rules that describe how parties interact with each other. These rules are written as code and stored on the blockchain, where they can be executed by the network itself.
The blockchain world has seen several attempts to make smart contracts safer, but these solutions were either too complicated or simply didn't work. CosmWasm is different because it's easy to use and works well in practice.
CosmWasm wraps Rust binaries as secure smart contracts. The source code is verified by the compiler and the resulting binary contains all of its dependencies statically linked. This prevents attackers from modifying any part of the contract after it has been deployed on-chain.
You don't have to write Rust code in order to use CosmWasm. If you're a developer and want to write your own contracts using CosmWasm, you can! As long as your programming language generates a WASM binary with no external dependencies, and defines the correct entry points it will work with CosmWasm. This means you can use any programming language of your choosing. You could write your contract in Go or Python, for example, and then deploy it with CosmWasm.
💡 For more information on CosmWasm navigate to: https://docs.cosmwasm.com/docs/1.0/
Secret Network uses both symmetric and asymmetric encryption protocols. Specifically, asymmetric cryptography is used for achieving consensus and sharing secrets between nodes and users, whereas symmetric cryptography is used for input/output encryption with users of Secret Contracts, as well as internal contract state encryption.
Secret Network Protocol uses the Elliptic-curve Diffie-Hellman (ECDH) key exchange mechanism between users and validators. This process involves the user, the Secret Blockchain, as well as the trusted component of the Secret Protocol. It is initiated any time a transaction is sent from the user to the Secret Contract.
The encryption and key derivation logic Secret Network uses are as follows:
HKDF-SHA256 function for deterministic key derivation;
ECDH (x25519) function for generating public / private key pairs; and
AES-128-SIV symmetric encryption scheme.
A combination of the above symmetric and asymmetric encryption methods is used to create a safe process for the bootstrapping of the decentralized network, the addition of new SGX nodes, and the encryption of input, output, and state. The management of all the private and public keys may only be shared under specific conditions, and others never leave the SGX instance to ensure private data handling.
The Secret Network uses a random number called the “consensus seed” as a parameter to derive a public and private key set used by the Network for encryption purposes. The consensus seed is unknown to any party in the ecosystem and was created at the genesis of the network by the network itself. For deterministic key generation, the network uses a known “salt” which is chosen to be the hash of the Bitcoin halving block.
As a transaction is initiated by a user of the network they derive Input Key Material (IKM) using their own private key and the network-generated public key, the network derives the same IKM using the user's public key and the network-generated private key. An encryption key for the transaction is then derived inside the TEE from the IKM, the salt, and a random number generated with the consensus seed. The encryption key is used to encrypt the input specific to this transaction. Only the protocol and the user signing the transaction have access to the encryption key, and therefore access to the input, output, and state related to this specific smart contract computation.
To do deterministic key generation inside the SGX enclave Secret Network leverages HDKF-SHA256 1 2. HDKF-SHA256 is a key derivation function for symmetric (private key) encryption. The function generates a 256-bit encryption key from a common public "salt" and a piece of Input Key Material (IKM). The salt
1 for the use in the Secret Network encryption schemes is chosen to be the Bitcoin's block halving hash hkdf_salt = 0x000000000000000000024bead8df69990852c202db0e0097c1a12ea637d7e96d
HDKF is commonly used to extract entropy from a larger source and deliver smaller output (eg. an encryption key) as well as expand already existing random output into a larger cryptographic-ally independent output. The deterministic keys coming from HDKF can be shared amongst network participants without revealing the underlying randomness. In the end this symmetric function is used to ensure safety for the pseudo-random consensus_seed
and secure the shared secrets of the network participants.
The output of the HDKF is a curve25519 private key, which can be used to derive a public key as well.
Elliptic-curve Diffie-Hellman ECDH (x25519) is a key derivation protocol designed to support assymetric encryption by returning a public-private key pair. ECDH allows for sharing secrets over public channels as one needs the private key to decrypt information while using the public key for sending the encrypted message. These Shared secrets can be used by both parties to then set up subsequent symmetric keys with functions like HDKF as mentioned above. ECDH delivers 256 bits Curve25519 encryption keys which have a probabilistic level of security of 2^128.
ECDH also allows for a special way of generating shared secrets which involves using the private and public key of both participants. Participant A and B can create a shared secret by doing: ecdh(Apriv, Bpub) == ecdh(Bpriv, Apub)
, this feature is called "key-exchange" and is the basis of sharing information amongst network participants on Secret Network.
For additional explanation of Diffie-Hellman, check out this video.
Advanced Encyption Standard (AES) is an encryption algorithm slightly varying from the block cipher "Rijndael" set to a fixed 128 bits size block. The algorithm generates 256 bit encryption keys which offer very high security guarantees.
The AES-SIV encryption scheme is a perfect addition to the ECDH keypairs used in SGX enclaves. The combination allows for sharing encrypted data amongst nodes and protecting the private entropy of the protocol. AES-128-SIV was chosen to prevent IV misuse by client libraries. The algorithm does not pad ciphertext which leaks information about the plaintext, in particular its size.
Smart contract blockchains are typically public by default. This means that the ledger, the transactions, and the data contained in the smart contract are accessible to anyone. However, this is not the case with Secret Network as it’s the first blockchain that offers programmable privacy through privacy-preserving smart contracts (“Secret Contracts”). “Secret Contracts” have both public and private metadata. Private data on Secret Network is encrypted at input, state, and output and therefore never accessible to any nodes, developers, users, or everyone else.
Programmable privacy is the ability to compute with private data while allowing for not only transfers (transactional privacy) but arbitrarily private complex computations. This allows developers to include sensitive data in their smart contracts without moving off-chain to centralized (and less secure) systems; allowing for truly private and scalable decentralized applications— the true vision of the decentralized web.
To achieve such programmable privacy, Secret Network uses a combination of techniques which will be explained in the further sections of this documentation.
Want to learn about Privacy for smart contracts and the Secret network techstack? Check out the following video series for an introduction:
The main difference when it comes to Cosmos compared to virtual-machine blockchain, is the application-specific blockchain concept. Developers can build from scratch their application specific blockchain on top of Tendermint through ABCI protocol. Cosmos SDK is the framework offering a bank of independent modules for implementing the application state machine, ABCI, service routers to route messages between modules, and a flavor of features like governance, staking, slashing, etc …
Below a figure showing the architecture differences between a VM blockchain and Cosmos:
A virtual-machine interprets Smart Contracts to change the state of the underlying blockchain state machine. It’s developer friendly and easy to use to deploy applications but comes with certain limitations:
Specific programming language accepted by the VM
VM limited set of functions and lack of flexibility
Smart Contracts are all run by the same virtual machine restraining performance as all application compete for block resources
Limited sovereignty, meaning that the application is dependent of the underlying blockchain governance decision
In Cosmos, developers have all the keys to develop a sovereign, secure, flexible and tailor made application specific blockchain.
Cosmos SDK allows developers to tweak, if needed, the framework or the consensus engine or any modules to match their application/network requirements.
As the application is not competing with others for blockspace or is limited by the VM computation, the performance is enhanced and only limited by the state-machine itself.
In terms of security, developers are not constrained by the VM cryptographic functions or any VM exploitable mechanisms and can rely on their own cryptography or audited libraries.
In conclusion Cosmos SDK is giving an easy and fast way to bootstrap an application specific blockchain relying on an efficient and proven technology without tradeoffs on security and sovereignty and with access to an extensive modules library.
💡 For more information about the Cosmos SDK: High-level Overview | Cosmos SDK & Main Components of the Cosmos SDK
When a full node resumes network participation, it reads consensus_seed
from $HOME/.sgx_secrets/consensus_seed.sealed
, does the same key derivation steps as above in steps 2-5.
The new full node verifies the remote attestation proof of the bootstrap node from genesis.json
and creates a remote attestation proof for their own machine to show to the network that the node's Enclave is genuine.
Using HKDF-SHA256, hkdf_salt
and nonce
(a 256 bit true random) a private key is derived. From registration_privkey
calculate registration_pubkey
The node needs to send an secretcli tx register auth
transaction with the following inputs:
The remote attestation proof the node's Enclave is genuine
registration_pubkey
256 bits true random nonce
On the consensus layer, inside the enclave of every full node the auth transaction comes in.
The Network validator nodes:
Receive the secretcli tx register auth
transaction
Verify the remote attestation proof that the new node's Enclave is genuine
seed_exchange_key
seed_exchange_key
: An AES-128-SIV encryption key is used to send consensus_seed
to the new node
This key is derived in several steps:
This IKM is never publicly available and protects the Secret network private entropy
In the second step seed_exchange_key
is derived using HKDF-SHA256 from seed_exchange_ikm
and nonce
. When sending the seed_exchange_key
to new nodes the Nonce is added as plaintext, it just serves the function of making each seed_exchange_key
unique.
consensus_seed
with the new nodeThe seed_exchange_key
generated in step 5 is used to encrypt the consensus_seed
. The AD
for this encryption algorithm is the public key of the new node: new_node_public_key
All this logic is done in side the Authorization transaction. secretcli tx register auth
outputs the encrypted_consensus_seed
The encrypted output is received by the new full node. The new node now has access to the encrypted_consensus_seed
and will have to decrypt to plaintext to receive the consensus_seed
seed_exchange_key
The AES-128-SIV encryption key seed_exchange_key
is used to decrypt consensus_seed
To derive this the reverse logic is followed highlighted in step 5.
First the same seed_exchange_ikm
is derived using ECDH (x25519) with consensus_seed_exchange_pubkey
(public in genesis.json
) and registration_privkey
(available only inside the new node's Enclave) This is the DH-key echange in action as this is the reverse public/private input of the IKM generation in step 5.
seed_exchange_key
is derived using HKDF-SHA256 with seed_exchange_ikm
and nonce
encrypted_consensus_seed
encrypted_consensus_seed
is encrypted with AES-128-SIV, seed_exchange_key
as the encryption key and the public key of the registering node as the ad
as the decryption additional data The new node now has all of these parameters inside its Enclave, so it's able to decrypt consensus_seed
from encrypted_consensus_seed
and then seal consensus_seed
to disk at $HOME/.sgx_secrets/consensus_seed.sealed
Secret Network is set up to perform computation while having encrypted state, input and output. The state is encrypted using private keys generated during the network bootstrapping from a consensus_seed
. The bootstrap node was the first full node on the network and generated the shared secrets similar to a universal trusted zero knowledge setup.
The bootstrap node first does a remote attestation to prove they are running a genuine SGX enclave with updated software. After registering the bootstrap node handled the initialization of following variables:
Consensus_seed
a true random 256 bit seed used as entropy for generating shareable keypairs between the nodes of the network.
consensus_seed_exchange_pubkey
- consensus_seed_exchange_pubkey
an HDKF private key and a matching curve25519 public key for encryption of the random seed and sharing this with other full nodes in the network.
consensus_io_exchange_pubkey
- consensus_io_exchange_pubkey
an HDKF private key and a matching curve25519 public key for encrypting transactions IO in the network. Also referenced as the "Secret Network keypair".
consensus_state_ikm
An input keyring material (IKM) for HKDF-SHA256 is used to derive encryption keys for contract state.
consensus_callback_secret
A curve25519 private key is used to create callback signatures when contracts call other contracts
The bootstrap node proves to have a genuine enclave after which it can participate in the network. More information can be found on this page.
consensus_seed
The bootstrap node is instructed when started to generate a true random 256 bit seed inside the enclave, the consensus_seed
.
The consensus_seed
is sealed with MRSIGNER to a local file : $HOME/.sgx_secrets/consensus_seed.sealed
For the network to start decentralizing the bootstrap node needs to be able to share the consensus_seed
. The network can then use the seed to create shared secrets while in the enclave. To securely share the seed the Network uses a DH-key exchange.
Using HKDF-SHA256, hkdf_salt
and consensus_seed
a private key is derived. From consensus_seed_exchange_privkey
calculate consensus_seed_exchange_pubkey
Using HKDF-SHA256, hkdf_salt
and consensus_seed
a private key is derived. - From consensus_io_exchange_privkey
calculate consensus_io_exchange_pubkey
consensus_state_ikm
Using HKDF-SHA256, hkdf_salt
and consensus_seed
derive consensus_state_ikm
5. consensus_callback_secret
Using HKDF-SHA256, hkdf_salt
and consensus_seed
derive consensus_callback_secret
Publish to genesis.json
:
The remote attestation proof that the Enclave is genuine
consensus_seed_exchange_pubkey
consensus_io_exchange_pubkey
When a contract is executed on chain the state of the contracts needs to be encrypted so that observers can not see the computation that is initialized. The contract should be able to call certain functions inside the enclave and store the contract state on-chain.
A contract can call 3 different functions: write_db(field_name, value)
, read_db(field_name)
, and remove_db(field_name)
. It is important that the field_name
remains constant between contract calls.
We will go over the different steps associated with the encryption of the contract state.
contract_key
The contract_key
is the encryption key for the contract state and is a combination of two values: signer_id || authenticated_contract_key
. Every contract has its own unforgeable encryption key. The concatenation of the values is what makes every unique and this is important for several reasons
Make sure the state of two contracts with the same code is different
Make sure a malicious node runner won't be able to locally encrypt transactions with it's own encryption key, and then decrypt the resulting state with the fake key
so to reiterate, every contract on Secret Network has its own unique and unforgeable encryption key contract_key
This process of creating contract_key
is started when the Secret contract is deployed on-chain. First authentication_key
is generated using HDKF-SHA256 inside the enclave from the following values:
consensus_state_ikm
HDK-salt
signer_id
From the authentication_key
create authenticated_contract_key
by calling the hmac-SHA256 hash function with the contract code_hash
as hashing data.
This step makes sure the key is unique for every contracts with different code.
Lastly concat the signer_id
and authenticated_contract_key
to create contract_key
. This step makes it so the key is unforgeable as the key can only be recreated with the current signer_id
contract_key
with enclaveEvery time a contract execution is called, contract_key
should be sent to the enclave. In the enclave, the following verification needs to happen to proof a genuine contract_key
write_db(field_name, value)
read_db(field_name)
remove_db(field_name)
Very similar to read_db
.
Intel SGX is one of the most used and widely available implementations of Trusted Execution Environments (TEEs). Secret Network has selected this technology for the initial version of the Secret Network for two main reasons: Usability & Security.
SGX is more performant and more flexible than other solutions for privacy-preserving computation. The Secret Network is building a platform for decentralized, general-purpose private computation. This requires a privacy solution that can enable a wide range of use cases. It also requires computations to be on par, performance-wise, with non-privacy preserving computation, so that speed does not limit application usability.
SGX is one of the most widely adopted technologies for TEEs, it is also battle-hardened. Attacks are often theoretical, executed in laboratory settings, and are rapidly addressed by Intel. Many high-value targets exist that have not been compromised. No privacy solution is 100% secure, but we believe the security guarantees provided by Intel SGX are adequate for a wide range of use cases.
Secret Network only allows for Intel SGX chips, AMD-SEV or other TEE technologies are not usable for running nodes on the network.
SGX comes in 2 forms; SGX-ME and SGX-SPS. SGX-ME (management engine) uses small extra chips to manage functions related to the enclave such as memory and energy management. SGX-SPS (Server Platform Services) allows the bypassing of the ME chip. To further reduce the number of possible attack vectors on the network, Secret Network has opted to only use SGX-SPS. Hence, all attack vectors of the ME chip do not apply to Secret Network.
Furthermore, each full node on Secret Network creates an attestation report that proves that their CPU is using the latest firmware/microcode (processor firmware) upgrades before it registers. The entire network verifies the attestation report of the new node on-chain, to ensure that node operators cannot decrypt anything. Once the new node gets the shared key of the consensus they become part of the consensus and are able to process computations and transactions in parallel to the network.
Secret Network recently upgraded to version 1.7.1, which rotated the network consensus seed during the upgrade.
A consensus seed is a true random 256 bit seed that is used as entropy for generating shareable keypairs between the nodes of the network.
Previously, the consensus seed remained unchanged ever since the network’s inception–the network state was encrypted using private keys generated during the network bootstrapping from a consensus seed, similar to a universal trusted zero knowledge setup. However, if anyone were to gain access to this seed they would have the master key to decrypt the state of the entire network. Thus, Secret Network has introduced consensus seed rotation in order to increase network security.
In order to protect against potential future breaches, Secret Network developed a two-part protocol for changing the consensus seed:
Rotate the current seed (The "Genesis" seed) and change the encryption scheme
Implement contract state migration and allow seed rotation on upgrade (currently in development)
It is important to note that the upgrade to consensus seed rotation does not change the state (and the consensus seed) of the network prior to the upgrade. This means that the new encryption scheme must be able to distinguish between values that were encrypted with the genesis seed and those that will be encrypted with future seeds. To this end, the following features were implemented:
A way to distinguish between values that were encrypted with the genesis seed and those that will be encrypted with future seeds
The ability to iterate over all of the keys for a specific contract using CosmWasm iterators (currently in development)
The ability to decrypt state keys, rotate the seed, and decrypt and re-encrypt all keys & values in the state
In order to rotate the Genesis seed, a new seed must be created that will be shared with all current nodes as well as new nodes. To this end, Secret Labs updated all of the existing nodes with the new seed and every new node that joins the network will contain 2 consensus seeds (The genesis and the current) and will use them both based on the encrypted value.
The new seed can be received via three methods:
On upgrade: When upgrading to 1.7.1, the node will identify that the current seed is missing and will communicate with Secret Labs’ seed service in order to obtain the seed
On Registration: On registration, both the current and the genesis seed will be passed to the newly registered node
On Bootstrap: Boostrap will access the seed service unless the "use_seed_service_on_bootstrap" feature is off (Which is the default state in localsecret). If so, it will generate one instead.
In the previous encryption scheme, the key and the value were stored as follows:
In the new encryption scheme, the plaintext key is no longer necessary in order to decrypt the value. The encrypted value also looks a bit different in the new scheme:
This new encryption scheme ensures that:
the encrypted_state_key encrypts differently between different keys
The salt will verify that on different instances, the same value is encrypted differently for the same key. The salt is the current block's timestamp and the msg id, which is a counter of the messages and allows for different values between different messages in the same block.
In order to authenticate a node, the node first sends an attestation report to the designated /authenticate endpoint as the request body. The server then responds with a challenge, which is a randomly generated 4-byte value that is linked to the public key of the node. The node then creates a new attestation report that incorporates the challenge into its quote, which proves that the node can generate new attestation reports certified by Intel while also rendering old certificates invalid. Subsequently, the node transmits this new attestation report to the /seed/[id] endpoint, where the ID represents the desired seed. Upon receiving the attestation report, the server verifies it and sends the seed to the node.
The port of the service is 4487 and there are 2 DNS names that are used.
On MainNet - sss.scrtlabs.com
On Pulsar - sssd.scrtlabs.com
Secret Network has implemented consensus seed rotation in the upgrade to version 1.7.1. Previously, the consensus seed remained unchanged since the network's inception, but this posed a security risk as anyone with access to the seed would have the master key to decrypt the entire network's state. The new consensus seed rotation includes a two-part protocol to change the genesis seed and encryption scheme, and implement contract state migration to allow seed rotation on upgrade. The upgrade does not change the network state prior to the upgrade, so the new encryption scheme distinguishes between values encrypted with the genesis seed and those encrypted with future seeds. Secret Labs updated all existing nodes with the new seed and new nodes joining the network will have both the genesis and current seeds, using them based on the encrypted value. These updates increase the security of Secret Network by protecting against potential future breaches.
Secret Network leverages TEE technology to do computation with encrypted input, output, and state. The consensus and computation layer of the Secret Network is combined; every validator uses an Intel SGX CPU and processes every transaction.
Private metadata used in Secret Contracts is encrypted before sent to validators for computation. Data is only decrypted inside the TEE of any specific validator, which is inaccessible to them. Computations following the smart contract are done over the decrypted data and the output is encrypted and written to state.
The consensus encryption seed of the network is only stored inside the TEE of each validator node; no entity has access to the encryption keys.
Enclaves also go through a detailed registration and attestation process. Specifically, the attestation process which each validator running an SGX enclave must go through ensures the following assertions regarding privacy and correctness:
The application’s identity
Its intactness (that it has not been tampered with)
That it is running securely within an enclave on an Intel SGX enabled platform
For our purposes, the attestation key is only used once upon registration. After registration new keys are provisioned to the enclave and used to communicate with the network. This process is described in more detail below.
Secret Network uses Intel's Software Guard Extensions (SGX) implementation of TEE technology. TEE refers to a secure area of a processor where data is inaccessible to any other component in the system. A TEE acts as a blackbox for computation, input and output can be known, but the state inside the TEE is never revealed.
Intel’s Software Guard Extensions (SGX) is a set of security-related instructions built into certain Intel CPUs enabling TEEs. By using SGX chips, the chip owners, system operators, and observers have strong cryptographic guarantees that no party can view what's happening inside of the Secret memory space.
This part of the documentation will discuss all aspects of TEE technology and the way that Secret Network implements it for a secure private computation environment.
Transaction encryption unlike contract state encryption has two parties who need data access. The scheme therefore makes use of the DH-key exchange as described in the previous section to generate a shared encryption key. This symmetric tx_encryption_key
is unique for every transaction and can be used by both the network and the user to verify the completed transactions.
Using the Eliptic-Curve Diffie Hellman key exchange (ECDH) the user generates a shared secret from consensus_io_exchange_pubkey
and tx_sender_wallet_privkey
.
tx_encryption_key
- user sideThe user then generates a shared tx_encryption_key
using HKDF-SHA256 and the tx_encryption_ikm
generated in step 1. The pseudo-random HDKF is used to ensure deterministic consensus across all nodes.
The random component comes from a 256-bit nonce so that each transaction has its own encryption key, An AES-256-GCM encryption key is never used twice.
After initiating a transaction the user encrypts the input data with the shared transaction encryption key, using an AES-256-GCM authenticated encryption scheme.
The input (
msg
) to the contract is always prepended with the sha256 hash of the contract's code. This is meant to prevent replaying an encrypted input of a legitimate contract to a malicious contract, and asking the malicious contract to decrypt the input.
In this attack example the output will still be encrypted with a tx_encryption_key
that only the original sender knows, but the malicious contract can be written to save the decrypted input to its state, and then via a getter with no access control retrieve the encrypted input.
tx_ecryption_key
- network sideThe enclave uses ECDH to derive the same tx_encryption_ikm
from the tx_sender_wallet_pubkey
and the consensus_io_exchange_privkey
. The network then derives the tx_encryption_key
from the publicly signed nonce
and this shared secret using HDKF.
Within the trusted component the transaction input is decrypted to plaintext.
The output must be a valid JSON object, as it is passed to multiple mechanisms for final processing:
Logs are treated as Tendermint events
Messages can be callbacks to another contract call or contract init
Messages can also instruct sending funds from the contract's wallet
A data section which is free-form bytes to be interpreted by the client (or dApp)
An error section
Here is an example output for an execution:
on a Contract
message, the msg
value should be the same msg
as in our tx_input
, so we need to prepend the nonce
and tx_sender_wallet_pubkey
just like we did on the tx sender above
On a Contract
message, we also send a callback_signature
, so we can verify the parameters sent to the enclave (read more here: ......)
For the rest of the encrypted outputs we only need to send the ciphertext, as the tx sender can get consensus_io_exchange_pubkey
from genesis.json
and nonce
from the tx_input
that is attached to the tx_output
with this info only they can decrypt the transaction details.
Here is an example output with an error:
An example output for a query:
The output of the computation is encrypted using the tx_encryption_key
The transaction output is written to the chain and only the wallet with the right tx_sender_wallet_privkey
can derive tx_encryption_key
. To everyone else but the tx signer the transaction data will be private.
Every encrypted value can be decrypted by the user following:
For output["ok"]["messages"][i]["type"] == "Contract"
, output["ok"]["messages"][i]["msg"]
will be decrypted in by the consensus layer when it handles the contract callback
For more detailed information on the Intel SGX remote attestation process you can check out this page:
Enclaves generate and contain their private signing/attestation keys, preventing access from any entity outside of each enclave. All data can only be signed using keys associated with specific instruction sets running in each enclave. For more details on key generation and management within enclaves, see our section about .
Trusted Execution Environments are essentially stateless. To preserve information that’s stored in an enclave, it must be explicitly sent outside the enclave to untrusted memory. SGX provides a capability called data sealing which encrypts enclave data in the enclave using an encryption key derived from the CPU. This encrypted data block can only be decrypted, or unsealed, on the same system. This SGX-specific method for storing data is not used to store computation input/output data in the Secret Network. It is used to store the enclave’s signing key.
We seal the signing key because this key is created during the remote attestation process. We do not want the enclave to be required to perform remote attestation between each computation. If the enclave fails for some reason, and the key is lost, the worker would be obligated to go through the remote attestation process again. The only way to store persistent data from the enclave is through sealing.
Remote attestation, an advanced feature of Intel SGX, is the process of proving an enclave is established in a secure hardware environment. A remote party should be able to verify that the right application is running inside an enclave on an Intel SGX enabled platform.
Remote attestation provides verification for three things:
The application’s identity
Its intactness (that it has not been tampered with)
That it's running securely within an enclave on an Intel SGX enabled platform
Attestation is necessary in order to make remote access secure because an enclave’s contents may have to be accessed remotely, not from the same platform [1]
The attestation process consists of seven stages, encompassing several actors, namely the service provider (referred to as a challenger) on one platform; and the application, the application’s enclave, the Intel-provided Quoting Enclave (QE), and Provisioning Enclave (PvE) on another platform. A separate entity in the attestation process is Intel Attestation Service (IAS), which carries out the verification of the enclave [1][2][3].
In short, the seven stages of remote attestation comprise of making a remote attestation request (stage 1), performing a local attestation (stages 2-3), converting the local attestation to a remote attestation (stages 4-5), returning the remote attestation to the challenger (stage 6), and verifying the remote attestation (stage 7) [1][3].
Intel Remote Attestation also includes the establishment of a secure communication session between the service provider and the application. This is analogous to how the familiar TLS handshake includes both authentication and session establishment.
More info coming soon
Now that we have a better understanding of how Secret is leveraging SGX, let’s see how the TEE and enclaves works with the Trusted and Untrusted cores.
Responsible for running the Cosmos SDK and Tendermint.
Contains code for creating and managing the enclave (load and destroy).
Can call the CosmWasm module and kick off Secret Contract execution within the enclave.
Responsible for executing Secret Contracts.
Responsible for SGX-specific mechanisms: Remote Attestation and Sealing.
Able to make read/write calls from the Tendermint state at any point during execution.
The enclave only stores the seed. The enclave may also store the local node's key pair.
Note: During contract execution, only the state of the contract being executed can be changed. Other contracts can be queried (i.e. run code that can't change the state of another contract) synchronously, but calls to other contracts and requests for transactions can only be queued. Those operations will happen after the contract has finished running. This is intentional as it prevents a lot of bugs, like the re-entrancy bugs plaguing Ethereum.
Below is a diagram of how the Untrusted and Trusted behave on a User transaction and Secret Contract execution:
\
The "Getting Started" section is to help new community members start developing on Secret Network using standard tools for Secret Contract development. The goal is to provide an easy guide to follow with step-by-step instructions. We will also review some of the unique characteristics of contracts on Secret Network, though we will keep things at a very high level.
Topics covered:
Setting up a development Environment
Compiling and Deploying a Secret Contract
Interacting with Contracts on the Secret Network
Breakdown of a Secret Contract (optional)
What won't be covered:
An in-depth guide to developing Secret Contracts - Secret By Example
Secret Network architecture - Secret Network Techstack
How to create a web UI for Secret Contracts - Secret.Js
Most of the topics covered focus directly on Secret Network but are also relevant to other cosmos-based blockchains.
If at any point you have any trouble understanding or following along, join us on discord to get help from the community!
If you have suggestions, improvements, or corrections, let us know on GitHub!
Environment configuration instructions to get started developing on Secret Network.
LocalSecret is a complete Secret Network testnet and ecosystem containerized with Docker. It simplifies the way secret contract developers test their contracts in a sandbox before they deploy them on a public testnet or mainnet (You can think of it like a local testnet that only you have access to).
There are 3 main tools we will use -
IDE - a development environment (usually VS Code or JetBrains CLion/IDEA with Rust support)
SecretCLI - a command-line tool to interact with the blockchain
LocalSecret - a local Secret Network chain set up for development purposes
We're going to install our environment manually so that you can code in a text editor of your choosing, but if you would prefer to work with JetBrains you can also configure your environment that way using the resources at the bottom of this page.
To follow along with the guide, we will be using git
, make
, rust
, and docker
.
Install git
:
Follow the prompts to install Git.
Open a terminal and verify the installation was successful by typing git --version
Install make
:
Install git
and perl
(for Windows):
Note: support for make
on Windows is limited, so we'll provide separate commands for Windows where necessary
Having Trouble? You might need to restart your terminal, or run a command like:
source "$HOME/.cargo/env"
After installing Rust to configure the current shell
SecretCLI is a command-line tool that lets us interact with the Secret Network blockchain. It is used to send and query data as well as manage user keys and wallets.
Run the following commands in Powershell to download the latest version of SecretCLI and add it to your profile's PATH:
Afterwards, restart the terminal and test the installation with the following command:
Set the file name to secretcli
and set it as executable
Set the file name to secretcli
and set it as executable
An instance of LocalSecret requires approximately 2.5 GB of RAM to run. You may need to increase the resources available in your Docker settings.
The installation methods differ based on the processor architecture. This is because Secret Network makes use of Intel SGX to protect private data.
Now that you have docker installed, open the docker application and then in your terminal run:
Unfortunately, even LocalSecret inside a docker cannot be run on an M1 Mac. As a workaround, we recommend using a LocalSecret instance in a Gitpod environment.
This environment is set up in such a way that can be accessed remotely as well.
To connect, prepend the port number with the Gitpod URL. e.g., if my workspace is at https://scrtlabs-gitpoddevenv-shqyv12iyrv.ws-eu54.gitpod.io
then I would be able to connect to the RPC service at https://26657-scrtlabs-gitpoddevenv-shqyv12iyrv.ws-eu54.gitpod.io
To set up SecretCLI to connect to this environment, use the following commands
Congrats! You now have a containerized version of LocalSecret running inside docker! You should see blocks being created in real time:
Now it's time to learn how to compile and deploy your first smart contract 🎉
Secret Contracts are written using the . CosmWasm contracts are written in Rust, which is later compiled to WebAssembly (or WASM for short). To write our first Secret Contract, we need to set up a LocalSecret development environment with all the tools required so that you can upload, instantiate, and execute your smart contracts.
For a step-by-step Secret Network environment configuration video tutorial, 🎥. Otherwise, continue reading!
Download the latest .
Go to and the download will start automatically. Note that this is a project called Git for Windows, which is separate from Git itself; for more information on it, go to .
Go to and download the recommended version for your system. StrawberryPerl is an open-source Perl environment for Windows; for more information, visit . Perl is used to build other dependencies that will be installed later.
Download and run .
Download and run
Cargo generate is the tool you'll use to create a smart contract project.
is an open platform for developing, shipping, and running applications.
Download secretcli
for your system .
Download secretcli
for your system .
For a more detailed and in-depth guide on SecretCLI installation and usage, check out the .
To get started, follow the instructions at the .
The amazing folks at created a prebuilt containerized environment containing an IDE (IntelliJ IDEA) that contains everything you need to get started. If you're comfortable with JetBrains IDEs, this may be a good choice for you.
To get started, visit , or just go for it and run the IDE from docker:
Then after a few seconds you will be able to access your in-browser IDE at .
Once the environment loads, clone the repository from
To know whether you have a Mac with an M1 chip or an Intel chip, click on the Apple logo located in the lefthand corner of your desktop and navigate to About This Mac/Overview, and confirm whether the processor is M1 or Intel. If you have an Intel chip, you can run LocalSecret in docker, otherwise, proceed with these set up instructions to learn how to .
Run an instance of LocalSecret in Gitpod by .
Secret Contracts are enabled by the “compute” module of the Cosmos SDK, and execute over plaintext while still allowing for encrypted inputs, outputs, and states because of trusted and verifiable computations. Consensus with an encrypted state is reached by using shared secrets amongst the validator nodes of the network.
In this part of the documentation, we highlight the flow of data for interactions on the secret network and dive into the design of Secret Contracts and their differentiations from standard CosmWasm contracts.
A Secret Contract’s code is always deployed publicly on-chain, so users and developers know exactly what code will be executed on submitted data. However, the data that is submitted is encrypted, it cannot be read by a developer, anyone observing the chain, or anyone running a node. If the behavior of the code is trusted (which is possible because it is recorded on chain), a user of Secret Contracts obtains strong privacy guarantees.
The encrypted data can only be accessed from within the “trusted execution environment”, or enclave, that the compute module requires each validator to run. The computation of the Secret Contract is then performed, within this trusted enclave, where the data is decrypted. When the computation is completed, the output is encrypted and recorded on-chain.
Want to learn more about the encryption specification of the contract state? - Read the technical specification on Contract state encryption here.
Secret contracts are an altered version of the CosmWasm Rust based smart contract framework and share many resemblance. The contracts written in Rust compile to a Wasm binary that is than run by the Wasmd module of the cosmos SDK. The version of Secret is altered in such a way that all executions are done inside the secure enclave requiring additional data verification and more. This also means queries of the contract state or contract execution are permissioned to only the signer of the transaction themselves. Contract state queries requiring opening up the VM in the enclave and are therefore more intensive to run than generic plaintext cosmos-sdk queries.
Crates.io serves as a centralized repository for Rust packages, also known as "crates." When building applications or libraries with Rust, developers often need to use external libraries for functionality not provided by the standard library, many of which are included on Crates.io. Below is an example of how to import Secret Labs' crates, such as secret-cosmwasm-std
, a fork of the original cosmwasm-storage
repository adapted for use in SecretNetwork's Secret Contracts.
You can think of "crates" as individual packages or modules in Rust, while "dependencies" refer to external crates that your project relies on for additional functionality.
When developing a Rust project, you will often have dependencies on other crates. These dependencies can come from crates.io (the default Rust package registry), a Git repository (such as one hosted by Secret Labs!), or a local path on your system. The Rust package manager, Cargo, is responsible for managing these dependencies, ensuring that the correct versions are fetched and built, and handling transitive dependencies (i.e., the dependencies of your dependencies).
Crates: A crate is a package or a module in Rust containing code and resources, like libraries or applications. Crates are the building blocks of Rust projects and are intended to be easily shared and reused. A crate can be a binary (application) or a library. Each crate has a unique name and a Cargo.toml
file containing metadata, such as the crate's name, version, authors, and dependencies.
Dependencies: Dependencies refer to external crates that your Rust project or library relies on for additional functionality. These external crates are not part of your project's codebase but are required for your project to build and function correctly. Dependencies are declared in the Cargo.toml
file of your project under the [dependencies]
section.
Additional development concepts to improve your experience building on Secret Network.
In this example, we will compile, upload, and instantiate our first smart contract using SecretCLI and LocalSecret.
Now that you've set up your LocalSecret development environment, we are going to learn how to compile, upload, and instantiate a smart contract using SecretCLI in your LocalSecret testnet environment.
For a step-by-step Secret Network SecretCLI video tutorial, follow along here 🎥. Otherwise, continue reading!
We will be working with a basic counter contract, which allows users to increment a counter variable by 1 and also reset the counter. If you've never worked with smart contracts written in Rust before that is perfectly fine. By the end of this tutorial you will know how to upload and instantiate a Secret Network smart contract in your terminal using SecretCLI.
The first thing we need to do is clone the counter contract from the Secret Network github repo. Secret Network developed this counter contract template so that developers have a simple structure to work with when developing new smart contracts, but we're going to use the contract exactly as it is for learning purposes.
Go to the folder in which you want to save your counter smart contract and run:
When you run the above code, it will name your contract folder directory "my-counter-contract". But you can change the name by altering the text that follows the --name
flag.
Start by opening the my-counter-contract
project folder in your text editor. If you navigate to my-counter-contract/src
you will seecontract.rs, msg.rs, lib.rs, and state.rs
—these are the files that make up our counter smart contract. If you've never worked with a Rust smart contract before, perhaps take some time to familiarize yourself with the code, although in this tutorial we will not be going into detail discussing the contract logic.
Next, configure SecretCLI to work with LocalSecret by running the following code in your text editor terminal (this is assuming you already have an instance of LocalSecret running in docker, which we learned in the previous section)
Did you know? By default, SecretCLI tries to connect to a locally running network
Since we are not making any changes to the contract code, we are going to compile it exactly as it is. To compile the code, run make build
in your terminal. This will take our Rust code and build a Web Assembly file that we can deploy to Secret Network. Basically, it just takes the smart contract that we've written and translates the code into a language that the blockchain can understand.
Run make build
from the terminal, or just GUI it up -
This will create a contract.wasm
and contract.wasm.gz
file in the root directory.
While we could upload this contract wasm file to the blockchain exactly as it is, instead we are going to follow best practices and optimize the wasm file. This just means we are going to reduce the size of the file so that it costs less gas to upload, which is critical when you eventually upload contracts to mainnet. Make sure you have an instance of LocalSecret running and then run the following code:
Optimize compiled wasm
You should now have an optimized contract.wasm.gz
file in your root directory, which is ready to be uploaded to the blockchain! Also note that the optimizer should have removed the contract.wasm
file from your root directory 👍
Now that we have a working contract and an optimized wasm file, we can upload it to the blockchain and see it in action. This is called storing the contract code. We are using a local testnet environment, but the same commands apply no matter which network you want to use - local, public testnet, or mainnet.
In order to store the contract code, we first must create a wallet address in order to pay for transactions such as uploading and executing the contract.
To interact with the blockchain, we first need to initialize a wallet. From the terminal run:
secretcli keys add myWallet
You can name your wallet whatever you'd like, for this tutorial I chose to name my wallet "myWallet"
You should now have access to a wallet with a unique name, address, and mnemonic, which you can use to upload the contract to the blockchain:
The wallet currently has zero funds, which you query by running this secretcli command (be sure to use your wallet address in place of mine)
To fund the wallet so that it can execute transactions, run:
Your wallet address should now have 1000000000 uscrt 🤯
Finally, we can upload our contract:
--from <name>
refers to which account (or wallet) is sending the transaction. Update <name> to your wallet address.
--gas 5000000
refers to the cost of the transaction we are sending. Gas is the unit of cost which we measure how expensive a transaction is.
--chain-id
refers to which chain we are uploading to, which in this case is LocalSecret!
To verify whether storing the code has been successful, we can use SecretCLI to query the chain:
Which should give an output similar to the following:
In the previous step we stored the contract code on the blockchain. To actually use it, we need to instantiate a new instance of it.
instantiate 1
is the code_id that you created in the previous section
{"count": 1}
**** is the instantiation message. Here we instantiate a starting count of 1, but you can make it any i32 you want
--from <name>
**** is your wallet address
--label
is a mandatory field that gives the contract a unique meaningful identifier
Let's check that the instantiate command worked:
Now we will see the address of our deployed contract
Congratulations! You just finished compiling and deploying your first contract! Now it's time to see it in action!
Get started developing on Secret Network using the public testnet and secret.js
In the previous section, we learned how to upload, execute, and query a Secret Network smart contract using SecretCLI and LocalSecret. Now we are going to repeat this process, but we will be uploading, executing, and querying our smart contract on a public testnet using Secret.js.
Secret.js is a JavaScript SDK for writing applications that interact with the Secret Network blockchain.
Key features include:
Written in TypeScript and provided with type definitions.
Provides simple abstractions over core data structures.
Supports every possible message and transaction type.
Exposes every possible query type.
Handles input/output encryption/decryption for Secret Contracts.
Works in Node.js, modern web browsers and React Native.
By the end of this tutorial, you will learn how to:
Add the Secret Testnet to your keplr wallet (and fund your wallet with testnet tokens)
Optimize and compile your Secret Network smart contract
Upload and Instantiate your contract using Secret.js
Execute and Query your contract using Secret.js
Let's get started!
To follow along with the guide, we will be using npm,
git,
make,
rust,
and docker
. Follow the Setting Up Your Environment guide here if you need any assistance.
Additionally, you will need to have the Secret Testnet configured with your keplr wallet and also fund it with testnet tokens. Learn how to configure and fund your keplr wallet here.
We will be working with a basic counter contract, which allows users to increment a counter variable by 1 and also reset the counter. If you've never worked with smart contracts written in Rust before that is perfectly fine.
The first thing you need to do is clone the counter contract from the Secret Network github repo. Secret Network developed this counter contract template so that developers have a simple structure to work with when developing new smart contracts, but we're going to use the contract exactly as it is for learning purposes.
Go to the folder in which you want to save your counter smart contract and run:
When you run the above code, it will name your contract folder directory "my-counter-contract". But you can change the name by altering the text that follows the --name
flag.
Start by opening the my-counter-contract
project folder in your text editor. If you navigate to my-counter-contract/src
you will seecontract.rs, msg.rs, lib.rs, and state.rs
—these are the files that make up our counter smart contract. If you've never worked with a Rust smart contract before, perhaps take some time to familiarize yourself with the code, although in this tutorial we will not be going into detail discussing the contract logic.
In your root project folder, ie my-counter-contract
, create a new folder. For this tutorial I am choosing to call the folder node
.
In your my-counter-contract/node
folder, create a new javascript file––I chose to name mineindex.js
.
Run npm init -y
to create a package.json file.
Add "type" : "module"
to your package.json file.
Install secret.js and dotenv: npm i secretjs dotenv
Create a .env
file in your node
folder, and add the variable MNEMONIC
along with your wallet address seed phrase, like so:
Never use a wallet with actual funds when working with the testnet. If your seed phrase were pushed to github you could lose all of your funds. Create a new wallet that you use solely for working with the testnet.
Congrats! 🎉 You now have your environment configured to develop with secret.js.
Since we are not making any changes to the contract code, we are going to compile it exactly as it is. To compile the code, run make build
in your terminal. This will take our Rust code and build a Web Assembly file that we can deploy to Secret Network. Basically, it just takes the smart contract that we've written and translates the code into a language that the blockchain can understand.
Run make build
from the terminal, or just GUI it up -
This will create a contract.wasm
and contract.wasm.gz
file in the root directory.
While we could upload this contract wasm file to the blockchain exactly as it is, instead we are going to follow best practices and optimize the wasm file. This just means we are going to reduce the size of the file so that it costs less gas to upload, which is critical when you eventually upload contracts to mainnet. Make sure you have docker installed and then run the following code:
Optimize compiled wasm
You should now have an optimized contract.wasm.gz
file in your root directory, which is ready to be uploaded to the blockchain! Also note that the optimizer should have removed the contract.wasm
file from your root directory 👍
Now that we have a working contract and an optimized wasm file, we can upload it to the blockchain and see it in action. This is called storing the contract code. We are using a public testnet environment, but the same commands apply no matter which network you want to use - local, public testnet, or mainnet.
Start by configuring your index.js
file like so:
You now have secret.js imported, a wallet
variable that points to your wallet address, and a contract_wasm
variable that points to the smart contract wasm file that we are going to upload to the testnet. The next step is to configure your Secret Network Client, which is used to broadcast transactions, send queries and receive chain information.
Note the chainId and the url that we are using. This chainId and url are for the Secret Network testnet. If you want to upload to LocalSecret or Mainnet instead, all you would need to do is swap out the chainId and url. A list of alternate API endpoints can be found here.
When connecting to a local node, you must indicate a port, which is 1317
Now console.log(secretjs)
and run node index.js
in the terminal of your my-counter-contract/node
folder to see that you have successfully connected to the Secret Network Client:
To upload a compiled contract to Secret Network, you can use the following code:
Run node index.js
in your terminal to call the upload_contract()
function. Upon successful upload, a codeId and a contractCodeHash will be logged in your terminal:
Be sure to save the codeId and contractCodeHash as variables so you can access them in additional function calls.
In the previous step, we stored the contract code on the blockchain. To actually use it, we need to instantiate a new instance of it. Comment out upload_contract()
and then add instantiate_contract()
.
Note that there is an initMsg
which contains count:0
. You can make the starting count whatever you'd like (as well as the contract label
).
Run node index.js
in your terminal to call the instantiate_contract()
function. Upon successful instantiation, a contractAddress will be logged in your terminal:
Be sure to save the contractAddress as a variable so you can access it in additional function calls.
Congrats 🎉! You just finished uploading and instantiating your first contract on a public Secret Network testnet! Now it's time to see it in action!
The way you interact with contracts on a blockchain is by sending contract messages. A message contains the JSON description of a specific action that should be taken on behalf of the sender, and in most Rust smart contracts they are defined in the msg.rs
file.
In our msg.rs
file, there are two enums: ExecuteMsg
, and QueryMsg
. They are enums because each variant of them represents a different message which can be sent. For example, the ExecuteMsg::Increment
corresponds to the try_increment
message in our contract.rs
file.
In the previous section, we compiled, uploaded and instantiated our counter smart contract. Now we are going to query the contract and also execute messages to update the contract state. Let's start by querying the counter contract we instantiated.
A Query Message is used to request information from a contract; unlike execute messages
, query messages do not change contract state, and are used just like database queries. You can think of queries as questions you can ask a smart contract.
Let's query our counter smart contract to return the current count. It should be 0, because that was the count we instantiated in the previous section. We query the count by calling the Query Message get_count {}
, which is defined in our msg.rs file. Comment out instantiate_contract()
and then add try_query_count()
.
The query returns:
Great! Now that we have queried the contract's starting count, let's execute an increment{}
message to modify the contract state.
An Execute Message is used for handling messages which modify contract state. They are used to perform contract actions.
Did you know? Messages aren't free! Each transaction costs a small fee, which represents how many resources were required to complete it. This cost is measured in gas units.
The counter contract consists of two execute messages: increment{}
, which increments the count by 1, and reset{}
, which resets the count to any i32
you want. The current count is 0, let's call the Execute Message increment{}
to increase the contract count by 1.
Nice work! Now we can query the contract once again to see if the contract state was successfully incremented by 1. The query returns:
Way to go! You have now successfully interacted with a Secret Network smart contract on the public testnet!
Congratulations! You completed the tutorial and successfully compiled, uploaded, deployed and executed a Secret Contract! The contract is the business logic that powers a blockchain application, but a full application contains other components as well. If you want to learn more about Secret Contracts, or explore what you just did more in depth, feel free to explore these awesome resources:
Secret University counter contract breakdown - explains the counter contract in depth
Millionaire's problem breakdown - explains how a Secret Contract works
CosmWasm Documentation - everything you want to know about CosmWasm
Secret.JS - Building a web UI for a Secret Contract
Uint128
is a data structure designed to work with usigned 128-bit integers.
If you are familiar with Rust, you might know that it has it's own native primitive - u128
. Uint128
differs from u128
in that it is a string encoded integer, rather than the traditional little/big-endian.
Simliarly, cosmwasm-std
also has Uint64
and Uint256
and all of the following applies there as well.
In general, JSON implementations usually accept [-(2^53)+1,(2^53)-1]
as an acceptable range for numbers. So if we need more than that (for example for 64(unsigned), 128 and 256 numbers) we'll want to use a string-encoded numbers. That's why we'll prefer to use Uint128
in entrypoint messages, for example:
Depends on the needs of your contract, you can choose to use either Uint128
or u128
.
As a rule of thumb, most of the time you will want to store numbers as u128
rather than Uint128
.
More specifically, since Uint128
is a string encoded number the storage space it'll consume will depend on the number of digits of the number you are storing. u128
on the other hand will always take a constant amount of storage space. That's why Uint128
will be more efficient for very small numbers (and then, why use 128-bit integer to begin with?), while u128
will be more efficient for most use cases.
Example:
Floating points are a big no-no in blockchain. The reason being, and without diving into too much detail, that floating point operations might be non-deterministic, so different nodes in the blockchain might get different results and not reach consensus.
That being said, there are different ways to overcome this.
Sometimes you can absorb some lack of precision, and you can use integer division. For example, if you want to divide 1 million tokens between three addresses:
You can often increase integer division's precision by enlarging your inputs by some factor. When you are done with the calculations, you can shrink the number to the original scale.
Let's look at an example calculation with the following inputs:
Not scaling up before the calculation causes loss of precision:
Instead, we can first scale up the inputs by a constant SCALE_FACTOR
, and shrink them back down at the end:
Scale factors can be as big as possible, provided they don't cause overflows.
If you still need decimals in your code, a fixed-point decimals library can assist you.
Keep in mind that using fixed-point decimals comes with an overhead (both efficiency and ease of use), so you would prefer to avoid it if possible.
Sometimes even when you don't use floats directly, one of your contract's dependencies do. In that case you'd want to turn off the feature that using the floats or just replace the library altogether.
Learn how to create a SNIP-20 token on Secret Network
In this tutorial, we are going to create our own SNIP-20 token on Secret Network using Secret Labs' SNIP-20 reference implementation contract, and we will learn how to upload, instantiate, execute, and query our SNIP-20 contract using Secret.js. Let's dive in!
Now that you've cloned the SNIP-20 reference implementation repo above, let's compile the contract. In your terminal run make compile-optimized
.
In Rust, a Makefile can be used to automate tasks such as building the project, running tests, or even generating documentation. Make compile-optimized
is running the following optimizer command, which you can view in the Makefile:
In your root project folder, create a new folder called node.
In your node
folder, create a new javascript file calledindex.js
.
Run npm init -y
to create a package.json file.
Add "type" : "module"
to your package.json file.
Install secret.js:npm i secretjs
In your index.js file, paste the following (be sure to replace the wallet seed phrase with your wallet seed phrase):
Run node index.js
in your terminal to execute the upload_contract()
function. Upon successful execution, a codeId and contract hash will be returned:
In your index.js file, paste the following:
Now we are going to instantiate some ZBRA coin. If you want to create your own coin name, update the name, symbol,
and amount
fields respectively. Be sure to comment out upload_contract()
and now run node index.js
to call instantiate_contract()
. Upon successful execution, a contract address will be returned:
To check that the instantiation of our SNIP-20 ZEBRA token was successful, let's query the smart contract's token info:
The following is returned upon successful query:
Now let's execute the transfer message with secret.js. Be sure to update the recipient
wallet address with your own wallet before executing the code below. For testing purposes, I am using two Keplr wallet connected to the Secret Network testnet in order to move funds back and forth:
Congrats! You just successfully transferred your own SNIP-20 token on Secret Network! 🎉
Learn how to write a full stack decentralized React application utilizing a Secret smart contract and Secret.js
Yao's Millionaires' problem is a secure multi-party computation problem introduced in 1982 by computer scientist and computational theorist Andrew Yao. The problem discusses two millionaires, Alice and Bob, who are interested in knowing which of them is richer without revealing their actual wealth.
In this demo you will learn how to integrate the Secret Millionaire contract with a front end designed in React using Secret.Js. Let's get started!
Submit net worth
Reset net worth
Query net worth
Thus, we need to design our frontend in an intuitive manner that will allow the user to execute these messages. By the end of this tutorial you will learn how to do the following:
Integrate Keplr wallet with React.js
Use secret.js to execute multiple transactions simultaneously (submit and reset net worth)
Use secret.js to query the updated Secret Millionaire smart contract
In fewer than 100 lines of code, you have the functionality to connect and disconnect a Keplr wallet and can now use this data in every other part of any dApp you choose to create.
Now that a user can connect to our front end, let's write some functions with Secret.js that execute the submit_net_worth()
and reset_net_worth()
functions.
This function takes in two objects, each representing a millionaire, and sends their name and net worth to the Millionaire smart contract using the broadcast
method of the secretjs
library. This method accepts an array of transactions to send to the blockchain and an options object. In this case, the options object specifies a gas limit of 300,000, which is the maximum amount of computational work the transactions are allowed to perform before they are halted. Now let's use React.js to fire this function every time a user clicks on a button.
The handleSubmit
function is a handler for a form submit event in our React application. This function takes user inputs (names and net worths of two millionaires), submits this data to the blockchain, queries the richer millionaire, and updates the UI accordingly:
e.preventDefault();
: This line stops the default form submission event in a web page (which would typically reload the page).
setMillionaire1({ name: name1, networth: networth1 });
and setMillionaire2({ name: name2, networth: networth2 });
: These two lines update the states of Millionaire1
and Millionaire2
respectively. setMillionaire1
and setMillionaire2
are state-setting functions from the useState React hook.
await submit_net_worth({ name: name1, networth: networth1 }, { name: name2, networth: networth2 });
: This line calls the submit_net_worth
function described above, passing two objects containing the names and net worths of two millionaires. It then waits for the function to finish.
await query_net_worth(myQuery);
: This line calls the query_net_worth
function and waits for the function to finish.
setRicherModalOpen(true);
and setShowRicherButton(false);
: These two lines update the states controlling whether a modal and a button are displayed in the UI. They hide a button and show a modal that presents the result of the query_net_worth
function.
alert("Please approve the transaction in keplr.");
: If any error is thrown during the execution of the function, it is caught, and an alert is shown to the user instructing them to approve the transaction in Keplr.
resetSubmit
resets the smart contract state back to 0:
Lastly, we need to be able to query the result of submitted transaction, namely, which millionaire is richer. This function queries the Millionaire contract to get information about who is richer among the previously submitted millionaires, stores the result in the myQuery
array, and then we can display information stored in myQuery
in our front end.
First, we use the queryContract
method of the compute
module from secretjs
library to send a query to the Millionaire contract. This method takes an object as its argument with the following properties:
contract_address
: The address of the smart contract to which the query is being sent.
query
: The query message to be sent to the contract. In this case, it's calling the who_is_richer
method of the contract, which returns information about the richer of the two millionaires.
code_hash
: The code hash of the contract to ensure that the contract code hasn't been tampered with.
The result of this query is stored in the query
variable.
The result of the query is then pushed onto the myQuery
array. This array stores the results of all queries made so far, and we can display this on our React front end.
Explanation of executing modules inside a CosmWasm contract using sending Native SCRT as an example.
Full example guide on mitigating privacy risks.
Here we use a sample contract for selling a secret message to illustrate padding, gas evaporation, and side-chain attack prevention using a trusted actor to confirm transactions. In the contract we define three methods: posting a secret for sale, buying a secret, and confirming purchases. All three message types have two optional parameters: gas_target
and padding
. These will be used to help mask which message is being called.
execute
Padding and evaporation are parameters that are set for all message types.
The padding
parameter is used by the client to pad the size of message being sent to the contract. The padding
parameter is not used within the contract itself and is only necessary when using Keplr's amino format. In other cases the dapp or wallet can simply add extra space characters to the end of the json message.
We also want to pad the result sent back to the client. We can use the pad_handle_result
function from secret toolkit to format the response to a fixed block size. If you have responses that are large, you will need a sufficiently large block size to successfully mask the method being run.
After padding the result at the end of execute
entry point function, we use gas_target
along with the evaporation api functions to evaporate any remaining gas to hit the gas target. Note, we simply ignore both the padding
and the gas_target
message parameters using ..
in the match statement.
Just like we want to use padding to hide the message type when sending between the client and the contract, the number of bytes written to the chain can leak information about what is written. For example, a coin amount stored as a Uint128
will write a different number of bytes depending on the size of the number, and also in our example the secret message could leak information based on its length. For data structs we want to store on chain we usually create a stored version and create conversion methods between the unstored and stored versions. Note in the example, we use the secret toolkit space_pad
function to make all secrets stored as 256 bytes long masking the content.
Padding and evaporation help to protect privacy about what types of functions are being called in a contract and about what data is being read and written from storage. However, our contract is still vulnerable to a side chain attack if a buyer can simply purchase a secret without a trusted actor confirming the transaction. This is because a malicious actor could fork the chain and buy the secret on the forked chain without ever paying any coins on the mainnet.
In our case a trusted actor who can confirm the purchase is the seller of the secret. If all purchases must have the ConfirmPurchase
transaction performed after buying, then it is impossible for the side-chain attack to occur. Other contracts that need to implement this functionality might require a trusted third-party actor instead, for example a game contract where turns are executed on-chain.
Uint128
is a thin wrapper around u128
that uses strings for JSON encoding/decoding, such that the full u128
range can be used for clients that convert JSON numbers to floats, like JavaScript and jq ().
If you are familiar with Messages, you already know that most of the time we will use to deserialize them from JSON (if not, you should read on and the concept of ). Output will often be serialized in the same way.
Note - integer division in Rust will always round down towards zero ().
There are several Rust libraries that implement fixed-point decimals, but you'd probably be best to use Cosmwasm's own .
But the hard part is to identify what causes the problem to begin with. It might get pretty complicated, and probably a bit too involved for this doc, but there's this that was published in the Cosmwasm blog that is very helpful for this.
You can clone the source code , which we will reference throughout the course of this documentation.
Use to set up your developer environment.
The initMsg
object in our index.js
file is referencing the instantiation message defined in . Notice that we chose to omit the optional config
variable. If we include config
, there is a variety of additional contract functionality that we could program, such as burn, mint, admin privileges, etc .
The reason total supply
is null
is because we chose to make total supply
hidden in our instantiation message. If you want it to be public, then in the set public_total_supply
to true.
Now that we have successfully instantiated our SNIP-20 contract, let's send an to better understand the contract's functionality.
Start by adding the token to your Keplr wallet. Click on Keplr, select the hamburger icon, select "Add Token", and then paste in your token's contract address. If you need to fund your wallet to execute the transaction, you can do so using the . You should now see your token in your Keplr wallet!
Let's to another wallet address. The transfer message is defined in msg.rs as follows:
we will be working with demonstrates an example implementation that allows two millionaires to submit their net worth and determine who is richer, without revealing their actual net worth.
for the full stack application, on Secret testnet.
To interact with the dApp, you will need to have the Secret Testnet (pulsar-3) configured with your Keplr wallet and also fund it with testnet tokens. !
This tutorial assumes that you've already learned how to to the Secret testnet and now you will learn how to connect your instantiated smart contract to a frontend library, namely, React.js.
Excluding the instantiation message, the Secret Millionaire smart contract is capable of :
In order to execute the Secret millionaire contract, users must first be able to connect to their Keplr wallet. To see a generalized approach to connecting to Keplr wallet with Secret.js, you can . However, for our application you will learn how to connect to Keplr with React.js so that the user's wallet can control every page of your app.
Before we proceed, review the function:
This function awaits , an asynchronous function which enables Secret Network on Keplr, retrieves the user's account information from Keplr, creates a Secret Network client with this information, and then sets the secretjs
instance and secretAddress
with these details so that we can then share this data with the rest of our application. Notice that when we establish the Secret Network client with Secret.js we are also specifying the url
and chainId
of the client, which in this case is the url
+ chainId
for Secret testnet:
CreateContext() is the React.js hook that allows us to share the Secret Network client (aka the user's wallet address) throughout the entire application.
such that when a millionaire's net worth is submitted, the contract saves the first two entries and then returns an error for subsequent attempts until a reset. The reset operation resets the contract's state back to its initial condition, where no millionaires are registered.
Because the Secret smart contract requires two millionaires to be submitted at any given time, it would be an improved UI experience if the user could submit two millionaires simultaneously, rather than having to execute two separate transactions. We are able to implement this functionality with Secret.js using and the broadcast
method:
You now have all of the tools you need to create a decentralized full stack application on Secret Network. In this tutorial you learned how to write React.js functions to connect to Keplr wallet, submit simultaneous transactions and reset net worth data, and query a Secret smart contract for the wealthier party. If you have further questions as you continue to develop on Secret Network, please at 12pmET.
Sending Native SCRT from a contract is realtively trivial and can be done using the message. This is a call. Any native denoms can be send in this way (ex. native IBC tokens).
The "potential attack vector" section in the Encryption specs of the documentation highlights most of the privacy attacks developers should consider.
The "Privacy essentials" section also quickly describes everything to take into account.
How to design your contract to limit the amount of leaked data from storage access patterns
Coming Soon!
Deciding who can and can't see your private data, and how to do it.
Permissioned viewing is entirely unique to Secret Network with its encrypted state. It gives you ability to have precise control over who has access to what data. In order to determine when data is allowed to be released however, you need to have a way to prove who is trying to access it. Our two ways of doing this are through Viewing Keys and Permits/Certs.
With the new Shockwave alpha update, Permits have become more efficient in almost every application. The exception is inter-contract communication permissions. Here, viewing keys still hold a lead. However, in some cases you may want to include an option to use both in your smart contract and leave the decision up to the users. Some users will have a preference for one over the other.
The equivalent of padding for gas_used
When a smart contract is executed, CosmWasm meters how much gas the execution consumes while the program is running. On Secret Network, this all happens within the enclave. However, once execution completes, the total amount of gas that was consumed leaves the enclave and is made public in transaction metadata.
The public gas_used
result of a contract execution message reflects the sum of costs for the actual instructions that were executed. Within a typical smart contract, most of its methods consume different amounts of gas. With SNIP-20 for example, an attacker could deduce which method a transaction executed with a high degree of certainty; create_viewing_key
vs. transfer
vs. increase_allowance
, each of which produce a distinct gas_used
amount in the public transaction metadata.
In some cases, this can leak even more information such as which code path was taken, the value of a conditional, how many iterations a loop completed, and so on.
Evaporation is a concept that was introduced to overcome the privacy risks associated with gas_used
. Evaporation refers to the practice of deliberately consuming extra gas during execution in order to pad the gas_used
amount before it leaves the enclave.
With evaporation, clients can now instruct contracts on exactly how much gas their execution should consume, yielding a consistent gas_used
across all methods.
A contract can evaporate an arbitrary amount of gas using the built-in API function gas_evaporate
. In order to evaporate any remaining gas, the check_gas
API function allows contracts to calculate the difference between the amount consumed up to that point, and the amount the client wants the contract to consume by the end of its execution:
Note that there is no API function to get the amount of actual gas remaining. Instead, that information must come from the client. This practice ensures that users are still able to execute multiple messages in a single transaction by specifying gas limits to each contract execution individually.
The new check_gas
API function essentially gives contracts read access to CosmWasm's internal gas metering. Calling this function more than once during execution allows contracts to track changes in the amount of gas consumed. This simple feature enables a realm of interesting use cases not previously possible.
In its simplest form, gas tracking can be used as a development aid to inspect the amount of gas used by certain blocks of code, giving developers more insight and help with optimizing their gas footprint.
A more advanced use case involving gas tracking is opportunistic execution, where contracts take advantage of excess gas that would otherwise be evaporated by performing work that needs to be done anyway. That work could be anything from generic housecleaning duties, such as processing items in a queue, to more intentional duties, such as notarizing previous user actions to protect against tx-replay attacks.
To give an example, imagine an NFT auction contract that accepts bids on listed items. When a listing expires, the listed NFT should be transferred to the highest bidder. In typical contract design, the transfer would only actually happen once the winning bidder calls a method to "claim" their winnings. However, with opportunistic execution, the contract can automatically perform the transfer during any execution. For example, someone creating a viewing key could end up paying the gas fees to complete the transfer of a previous listing's NFT to its winning bidder.
Introduction to Secret Network viewing keys with code examples
Viewing keys are passwords meant to validate users at times when the blockchain cannot. Specifically in queries, the query sender isn't authenticated and the contract doesn't know who is the querier. Therefore viewing keys were invented to provide a way of access control for users:
Alice sends a transaction set_viewing_key(password)
The contract stores (alice,password)
Later on, a query is sent to the contract query("balance",alice,password)
If (alice,password)
matches what's in storage, the contract returns Alice's balance to the querier.
set_seed
: Sets an initial pseudorandom number generator (PRNG) seed for the store.
create
: Creates a new viewing key, saves it to the storage, and returns it.
set
: Sets a new viewing key based on a predetermined value.
check
: Checks if a viewing key matches an account.
To make use of the secret-toolkit viewing keys package, import it into your cargo.toml
file:
This contract is designed to create top secret messages that can only be decrypted if the user has the correct viewing key that is associated with the secret_message
struct.
This function stores a secret message at a specified index in the contract's storage, which is mapped to a viewing key. This ensures that the secret message can only be accessed by the correct viewing key, and that each secret message has its own unique viewing key.
Let's go over it in more detail:
A new viewing key is created by calling ViewingKey::create
. The parameters passed include the mutable dependencies (which includes the storage), the environment env
, the MessageInfo
, the sender account, and a hard-coded entropy value b"entropy"
.
The secret message is then stored in the contract's storage and is mapped to the viewing key. The SECRET_MESSAGE.insert
line is performing this task.
Next, the viewing key itself is stored in the contract's storage, but this time indexed with a user-defined index
parameter and prefixed by the sender's account address. This is done by VIEWING_KEY.add_suffix(info.sender.as_bytes()).insert(deps.storage, &index, &viewing_key)
.
Finally, the function returns a default Response
instance indicating successful execution of the function.
See the evaporation example contract's for more details.
To see an implementation of viewing keys in a Secret smart contract, check out the
Secret Network developed the for creating and managing viewing keys in Secret smart contracts. The methods provided include:
If you would like to see an example implementation of Secret Network viewing keys, see the .
Let's review the function:
Viewing keys provide a mechanism for access control in blockchain applications when the blockchain itself can't authenticate the query sender. Secret-toolkit
allows developers to set seeds, create and check viewing keys, and set a new viewing key based on a predetermined value
Should you have further questions, please reach out on and a Secret developer will get back to you shortly
Permits are the successor to viewing keys. With increases in efficiency made in the Shockwave update they are now the defacto viewing permission method in almost every situation.
Permits are simply a dummy transaction signed by a wallet. The wallet is able to use this signature as proof that the transactor is who they say they are. Any wallet capable of using Tendermint is able to carry out this process.
For an in-depth implementation of query permits, navigate to the snip-24 section of the docs here.
Generating a permit happens outside of the smart-contract, but checking them in your smart contract is extremely simple. Once again the Secret-Toolkit provides a package for us, aptly named permit
. The function that checks the permit is called validate
shown below.
To use this, simply call validate
inside of your query provided with the necessary permit variables.
Permits can be used at any time, and are amazing for almost every permissioned viewing application. The only time it may be more efficient to use viewing keys instead is during inter-contract communication.
An introduction to Secret VRF, a secure and verifiable random number generator
Secret Network's randomness API allows developers to access random numbers in their CosmWasm contracts, enhancing the capabilities of the platform. The randomness feature is accessible within Secret Contracts through the Env struct. It includes an optional random field, which contains a random number as a Binary type. The random field is only available when the "random" feature is enabled.
Randomness is essential in many applications, including:
Gaming and gambling platforms, where fair and unpredictable outcomes are crucial
Cryptographic systems that require secure random keys or nonces
Randomized algorithms for various use cases, such as distributed systems or optimization problems
The proposer for each block generates a strong, random seed inside SGX.
This seed is then included in the block header and signed by all validators who can verify its authenticity inside their SGX.
Secret Network's in-SGX light client prevents the proposer from simulating a block before all other validators sign it. Consequently, the proposer cannot gain maximal extractable value (MEV) by generating random seeds until they find a favorable simulation of the block.
Before calling the contract, the chain injects env.block.random = hkdf_sha256(block_random_seed + wasm_call_count)
.
Thus each contract call gets a unique random seed.
For a more in-depth explanation of why and how this method of randomness works feel free to read the feature explainer
Step-by-step guide on how to execute Secret Network smart contracts that communicate with each other
In previous sections, we explored the creation of isolated smart contracts within the Secret Network ecosystem. To construct more intricate systems using smart contracts, it is essential to establish effective communication between them. This documentation will delve into the implementation of secure and efficient communication between secret contracts, allowing you to build powerful, interconnected applications on Secret Network.
In this tutorial, we will be designing a simple smart contract that can execute a counter smart contract that is already deployed to the Secret Testnet. Thus, we are working with two smart contracts:
Manager Contract - which executes the Counter Contract
Counter Contract - which is executed by the Manager Contract
By the end of this tutorial, you will understand how to implement the Increment()
function on a Manager smart contract, which increments a counter smart contract by one every time the Increment function is called.
To follow along with this tutorial step-by-step, clone down the Secret Network counter template repo here 😊
We will be designing a Manager Smart Contract which can execute a Counter Contract that is deployed to the Secret testnet. Let's start by creating the message that we want to execute on the counter smart contract. In the src directory (which currently contains contract.rs
, lib.rs
, msg.rs
and state.rs
), create a counter.rs
file and add the following:
CounterExecuteMsg
contains a single function Increment{}.
This is the function we will call to increment the counter contract once we have completed designing our Manager smart contract.
Now, navigate to the msg.rs
file. Replace the existing code with the following:
Here we have an empty InstantiateMsg
, as well as an enum ExecuteMsg
, with a single variant, IncrementCounter
, which contains two strings: contract
and code_hash.
This is the contract address and code hash of the counter contract, which we will be calling from the Manager contract.
What is the code hash?
Unlike other Cosmos chains, Secret Network requires the hash of the smart contract in addition to the address when executing calls to smart contracts.
Contract hashes are what binds a transaction to the specific contract code being called. Otherwise, it would be possible to perform a replay attack in a forked chain with a modified contract that decrypts and prints the input message.
Now, navigate to the contract.rs
file. Replace the execute entry point with the following:
When the IncrementCounter
variant is matched, the function calls the try_increment
function, which contains two fields, contract
and code_hash
. The contract
field is the address of the contract to be incremented, and the code_hash
field is the code hash of the contract. This function contains the logic for incrementing the counter in our counter contract.
Remember the counter.rs file that we created earlier with the CounterExecuteMsg
enum containing Increment{}
? 🤔 Now, in the try_increment()
function, we are going to use a WasmMsg to call Increment{}
in order to increment the counter contract.
In CosmWasm, a WasmMsg
is an enum used to perform actions such as instantiating a new contract, executing a function on an existing contract, or sending tokens to a contract. Here are the main variants of WasmMsg
:
Instantiate
: This variant is used to create a new instance of a Wasm smart contract. It contains the following fields:
code_id
: The ID of the Wasm code to instantiate.
msg
: The message to initialize the contract (usually a JSON object).
funds
: The amount of tokens to send to the contract upon instantiation.
label
: A human-readable label for the new contract instance.
admin
: An optional address that, if provided, will have administrative privileges over the contract instance (e.g., for migration or update purposes).
Execute
: This variant is used to execute a function on an existing Wasm smart contract (and is the variant that we are calling in our contract) It contains the following fields:
contract_addr
: The address of the contract to execute the function on.
msg
: The message to be processed by the contract (usually a JSON object).
funds
: The amount of tokens to send to the contract along with the execution message.
Migrate
: This variant is used to migrate an existing Wasm smart contract to a new code version. It contains the following fields:
contract_addr
: The address of the contract to migrate.
new_code_id
: The ID of the new Wasm code to migrate the contract to.
msg
: The message to initialize the new code version (usually a JSON object).
In the counter.rs file, comment out or delete the existing execute functions and add the following:
The try_increment
function creates a WasmMsg::Execute
message to increment the counter in the specified smart contract and returns a Response
object containing the message and an attribute. When the Response
object is returned by the smart contract's execute
function, the blockchain will execute the WasmMsg::Execute
message, effectively incrementing the counter in the target contract.
Here's a breakdown of the code inside the function:
let exec_msg = CounterExecuteMsg::Increment {};
: This line creates a new CounterExecuteMsg
enum instance with the Increment
variant, which represents a message to increment the counter.
let cosmos_msg = WasmMsg::Execute { ... };
: This block creates a WasmMsg
enum instance with the Execute
variant. The Execute
variant is used to execute our Counter smart contract. The contract_addr
field is set to the contract
parameter, the code_hash
field is set to the code_hash
parameter, the msg
field is set to the binary representation of exec_msg
, and the funds
field is set to an empty vector, indicating no funds are sent with this message.
Ok(Response::new() ... )
: This block constructs a new Response
object with the following:
.add_message(cosmos_msg)
: The cosmos_msg
(a WasmMsg::Execute
instance) is added to the response as a message to be executed after the current contract execution finishes.
.add_attribute("action", "increment")
: An attribute with key "action" and value "increment" is added to the response. Attributes are used to store additional information about the operation and can be useful for indexing and querying transactions.
You can view the completed code repo here which contains the upload, instantiation, and execution functions using secret.js.
Let's focus on how to write the increment function as seen below:
The secret.js function increase_count
sends an increment_counter
message (which is the message we defined in msg.rs
) to our Counter smart contract. This message increments the counter contract via the WasmMsg that we defined in the increase_count
function.
You can call this function by commenting out the other functions in index.js and running node index.js
in the terminal (make sure you are in the secret-messages/manager/node
directory).
Now, in your terminal, navigate to the secret/counter/node
directory. Call the query_count
function to see the current count of the counter contract. After using the manager contract to increment, it should now be incremented by one!
Congratulations, you have now created a Manager smart contract that interacts with a Counter smart contract on Secret Network! By designing a Multi-Contract System, you can enable secure and efficient communication between secret contracts, allowing for more intricate and interconnected decentralized applications.
A new feature in Secret Network v1.9 is Execution Finalization.
Execution Finalization is a concept where the contract notifies the chain that the current execution must be the last massage in a single transaction. Or, in other words, the last part of an execution unit. This will cause the chain to revert the transaction if any action is taken after this flag is set.
Secret Network (and in fact all Cosmos chains) run discrete blocks of execution instructions called transactions. A single transaction can contain multiple messages. Each message contains a specific instruction. Any failure of any message in the transaction will cause the entire transaction to be reverted. This behavior can be exploited by attackers (or even non-maliciously) to select only favorable scenarios to be executed, while the unfavorable ones to be reverted.
For example, consider a dice game. The user sends some bet. If he makes the correct bet, he will receive his original bet plus his winnings. Otherwise, his bet is lost. An attacker can abuse such logic by sending a transaction with two messages. The first is the bet message, while the second is message that will conditionally fail if the bet is lost. This way, he is guaranteed to make a profit.
This same behavior can also be used for flash-loans, which are not inherently malicious but can be used to leverage capital for exploitation without risk of significant loss.
The new FinalizeTx message is now available for developers to toggle in case a specific execution path should be the last in a transaction. That way, it is guaranteed to be executed as the contract expects and cannot be reverted by the user.
To use FinalizeTx, simple append the FinalizeTx message when returning a result from your contract:
Messages are executed FIFO (first in, first out). If you want to send other messages before finalizing the execution, simply append them to the response before the FinalizeTx message.
We suggest developers consider whether or not a user conditionally reverting transactions is behavior that may affect the security of their application, as it may allow for scenarios that reveal confidential information or result in loss of funds. That being said, the trade-off is that such executions cannot be chained with other contract interactions, so usage of this feature should not be used without careful consideration.
A step-by-step tutorial of how to use Secret Network's randomness API to generate a coin flip
In this tutorial, you will learn how to access the randomness feature and use it with a smart contract that returns a truly random coin flip 🎉
In your Cargo.toml file, add secret-toolkit-storage 0.9.0:
Make sure you're compiling with rust < 1.70. Newer versions are currently not compatible.
LocalSecret is a tool that allows you to run a local Secret Network on your machine for testing and development purposes.
Here are the steps to use the randomness feature with LocalSecret:
Clone the Secret Labs examples repo and then navigate to the vrf-randomness-tutorial folder:
To consume the random number, you need to import the necessary dependencies in your contract.rs
file in order to access the random number from the env parameter.
In your contract, import the necessary dependencies (these are already imported in the cloned repo):
In the contract's entry point (e.g., execute, instantiate, or query), you can access the random number from the env
parameter:
The env and block_info structures are defined as:
Where random
is 32 bytes and base64 encoded.
Below is a simple coin flip function that uses the randomness feature:
try_flip()
uses the config
function to update the state of the smart contract by flipping a coin and storing the result in the flip
field in the state
variable. Specifically, it generates a random number using the random
field of the env.block
object, which is an optional value representing the most recent block's metadata, and takes the modulo 2 to obtain a value of either 0 or 1. It then updates the flip
field of the state
variable to this value.
Now, let's compile, upload, instantiate, and execute the contract to see it in action!
To compile your contract, in your terminal, make sure you have docker open, and then run:
This returns the optimized contract wasm file, ie contract.wasm.gz
To upload your contract to a containerized version of LocalSecret in docker, make sure you have docker installed and open, and then create a new tab in your terminal and run:
Congrats, you now have a new instance of LocalSecret running that can access the random number feature!
To confirm that the contract upload was successful:
Now let's instantiate our contract with a starting flip of 1 (1 meaning Heads or Tales, up to you!)
To confirm that the contract instantiation was successful:
Now that we have a contract address (which is returned from the list-contract-by-code
query above), we an execute the coin flip with the randomness feature!
To flip the coin simply run:
And to query that it was successful, run:
You might have to execute the flip function a few times to see the queried flip change, since there is a 50% chance the flip will return the same number :D
Please follow the guide
For a detailed feature explainer head to the
What follows is a step-by-step tutorial of how to use Secret Network's randomness API to generate a coin flip (returning either 0 or 1) with true randomness. You can follow along and/or view .
and be sure to install the
Next, so you can upload the contract to LocalSecret. Then run the following to upload:
Congrats! In this step-by-step tutorial on creating a coin flip contract, you learned how to compile, upload, instantiate, and execute a contract on LocalSecret using Secret Network's randomness API to generate random numbers 🎉 For documentation on Secret VRF in a contract on another IBC-connected chain, .