Crate cw_multi_test

source ·
Expand description

§CosmWasm MultiTest

CosmWasm MultiTest is designed to simulate a blockchain environment in pure Rust. This allows to run unit tests that involve contract 🡘 contract, and contract 🡘 module interactions. CosmWasm MultiTest is not intended to be a full blockchain application, but to simulate the Cosmos SDK x/wasm module close enough to gain confidence in multi-contract deployments, before testing them on a live blockchain.

The following sections explains some of the design for those who want to use the API, as well as those who want to take a look under the hood of CosmWasm MultiTest.

§Key APIs

§App

The main entry point to the system is called App, which represents a blockchain application. It maintains an idea of block height and time, which can be updated to simulate multiple blocks. You can use application’s update_block method to increment the timestamp by 5 seconds and the height by 1 (simulating a new block) or you can write any other mutator of BlockInfo to advance more.

App exposes an entry point execute that allows to execute any CosmosMsg and wraps it in an atomic transaction. That is, only if execute returns a success, then the state will be committed. It returns the data and a list of Events on successful execution or an Err(String) on error. There are some helper methods tied to the Executor trait that create the CosmosMsg for you to provide a less verbose API. App’s methods like instantiate_contract, execute_contract, and send_tokens are exposed for your convenience in writing tests. Each method executes one CosmosMsg atomically, as if it was submitted by a user. You can also use execute_multi if you wish to execute multiple messages together that revert the state as a whole in case of any failure.

The other key entry point to App is the Querier interface that it implements. In particular, you can use wrap to get a QuerierWrapper, which provides all kinds of interesting APIs to query the blockchain, like query_all_balances and query_wasm_smart. Putting this all together, you have one Storage wrapped into an application, where you can execute contracts and bank, query them easily, and update the current BlockInfo, in an API that is not very verbose or cumbersome. Under the hood it will process all messages returned from contracts, move bank tokens and call into other contracts.

You can easily create an App for use in your testcode like shown below. Having a single utility function for creating and configuring the App is the common pattern while testing contracts with CosmWasm MultiTest.

use cw_multi_test::App;

fn mock_app() -> App {
  App::default()
}

The App maintains the root Storage, and the BlockInfo for the current block. It also contains a Router (discussed below), which can process any CosmosMsg variant by passing it to the proper keeper.

Note: App properly handles submessages and reply blocks.

Note: While the API currently supports custom messages, we don’t currently have an implementation of the default keeper, except of experimental CachingCustomHandler.

§Contracts

Before you can call contracts, you must instantiate them. And to instantiate them, you need a code_id. In wasmd, this code_id points to some stored Wasm code that is then run. In multitest, we use it to point to a Box<dyn Contract> that should be run. That is, you need to implement the Contract trait and then add the contract to the App via store_code function.

The Contract trait defines the major entry points to any CosmWasm contract: instantiate, execute, query, sudo, reply (for submessages) and migrate.

In order to easily implement Contract from some existing contract code, we use the ContractWrapper struct, which takes some function pointers and combines them. You can take a look at test_helpers module for some examples or how to do so (and useful mocks for some test cases). Here is an example of wrapping a CosmWasm contract into a Contract trait to be added to an App:

use cosmwasm_std::Empty;
use cw1_whitelist::contract::{execute, instantiate, query};
use cw_multi_test::{App, Contract, ContractWrapper};

pub fn contract_whitelist() -> Box<dyn Contract<Empty>> {
    Box::new(ContractWrapper::new(execute, instantiate, query))
}

let mut app = App::default();
let code_id = app.store_code(contract_whitelist());
// use this code_id to instantiate a contract

§Modules

There is only one root Storage, stored inside App. This is wrapped into a transaction, and then passed down to other functions to work with. The code that modifies the Storage is divided into modules much like the CosmosSDK. Currently, the message processing logic is divided into one module for every CosmosMsg variant. Bank handles BankMsg and BankQuery, Wasm handles WasmMsg and WasmQuery, etc.

§Router

The Router groups all modules in the system into one “macro-module” that can handle any CosmosMsg. While Bank handles BankMsg, and Wasm handles WasmMsg, we need to combine them into a larger composite to let them process messages from App. This is the whole concept of the Router. If you take a look at the execute method, you will see it is quite straightforward.

Note that the only way one module can call or query another module is by dispatching messages via the Router. This allows us to implement an independent Wasm in a way that it can process SubMsg that call into Bank. You can see an example of that in send method of the WasmKeeper, where it moves bank tokens from one account to another.

§Addons

(tbd)

Modules§

Structs§

  • Always accepting module
  • Blockchain application simulator
  • Utility to build App in stages. When particular properties are not explicitly set, then default values are used.
  • A subset of data returned as a response of a contract entry point, such as instantiate, execute or migrate.
  • A structure representing a default bank keeper.
  • Contract data includes information about contract, equivalent of ContractInfo in wasmd interface.
  • This structure wraps the Contract trait implementor and provides generic access to the contract’s entry-points.
  • A structure representing a default distribution keeper.
  • Always failing module
  • The Router plays a critical role in managing and directing transactions within the Cosmos blockchain.
  • Default contract address generator used in WasmKeeper.
  • A structure representing a default stake keeper.
  • A structure containing some general staking parameters.
  • A structure representing a default wasm keeper.
  • A structure representing a privileged message.

Enums§

  • A message representing privileged actions in bank module.
  • Staking privileged action definition.
  • We use it to allow calling into modules from another module in sudo mode. Things like gov proposals belong here.

Traits§

  • Common address generator interface.
  • This trait defines the interface for simulating banking operations.
  • Provides a custom interface for generating checksums for contract code. This is crucial for ensuring code integrity and is particularly useful in environments where code verification is a key part of the contract deployment process. This trait defines a method to calculate checksum based on the creator’s address and a unique code identifier.
  • This trait serves as a primary interface for interacting with contracts.
  • A trait representing the Cosmos based chain’s router.
  • A trait defining a behavior of the distribution keeper.
  • A trait defining a default behavior of the message executor.
  • Handles governance-related operations within the test environment. This trait is essential for testing contracts that interact with governance mechanisms, simulating proposals, voting, and other governance activities.
  • Manages Inter-Blockchain Communication (IBC) functionalities. This trait is critical for testing contracts that involve cross-chain interactions, reflecting the interconnected nature of the Cosmos ecosystem.
  • Defines conversions to Addr, this conversion is format agnostic and should be aligned with the format generated by MockApi.
  • Defines conversions to Bech32 compatible addresses.
  • Defines conversions to Bech32m compatible addresses.
  • General module
  • A trait defining a behavior of the stake keeper.
  • Interface to module handling any messages and queries.
  • Acts as the interface for interacting with WebAssembly (Wasm) modules. This trait is crucial for testing smart contracts written in languages that compile to WebAssembly, which is common in the Cosmos and CosmWasm ecosystems.

Functions§

  • Creates new default App implementation working with customized exec and query messages. Outside the App implementation to make type elision better.
  • Advances the blockchain environment to the next block in tests, enabling developers to simulate time-dependent contract behaviors and block-related triggers efficiently.
  • No-op application initialization function.

Type Aliases§

  • A type alias for the default-built App. It simplifies storage and handling in typical scenarios, streamlining the use of the App structure in standard test setups.
  • This is essential to create a custom app with custom module.
  • A type alias for a module that accepts governance-related interactions. It’s used in scenarios where you need to test how your contract interacts with governance processes and messages.
  • This type alias represents a module designed to fail in response to governance operations. It’s useful for testing how contracts behave when governance actions do not proceed as expected.
  • Ideal for testing contracts that involve IBC, this module is designed to successfully handle cross-chain messages. It’s key for ensuring that your contract can smoothly interact with other blockchains in the Cosmos network.
  • Use this to test how your contract deals with problematic IBC scenarios. It’s a module that deliberately fails in handling IBC messages, allowing you to check how your contract behaves in less-than-ideal cross-chain communication situations.
  • Implementation of the cosmwasm_std::Api trait that uses Bech32 format for humanizing canonical addresses.
  • Implementation of the cosmwasm_std::Api trait that uses Bech32m format for humanizing canonical addresses.
  • Always accepting stargate module.
  • Always accepting stargate module.