From 047c57bad090b9c4e85b38b0b380aad921f82c51 Mon Sep 17 00:00:00 2001 From: Dariusz Depta Date: Mon, 18 Nov 2024 18:12:12 +0100 Subject: [PATCH] Documented operations on blocks. --- src/pages/cw-multi-test/blocks.mdx | 218 +++++++++++++++++++++++++++ src/pages/cw-multi-test/features.mdx | 26 ++-- 2 files changed, 231 insertions(+), 13 deletions(-) create mode 100644 src/pages/cw-multi-test/blocks.mdx diff --git a/src/pages/cw-multi-test/blocks.mdx b/src/pages/cw-multi-test/blocks.mdx new file mode 100644 index 00000000..0ab2ab7e --- /dev/null +++ b/src/pages/cw-multi-test/blocks.mdx @@ -0,0 +1,218 @@ +--- +tags: ["multitest", "blocks"] +--- + +import { Callout } from "nextra/components"; + +[BlockInfo]: https://docs.rs/cosmwasm-std/latest/cosmwasm_std/struct.BlockInfo.html +[block_info]: https://docs.rs/cw-multi-test/latest/cw_multi_test/struct.App.html#method.block_info +[set_block]: https://docs.rs/cw-multi-test/latest/cw_multi_test/struct.App.html#method.set_block +[update_block]: + https://docs.rs/cw-multi-test/latest/cw_multi_test/struct.App.html#method.update_block +[next_block]: https://docs.rs/cw-multi-test/latest/cw_multi_test/fn.next_block.html +[app_default]: app#appdefault + +# Blocks + +There are several cases when testing CosmWasm smart contracts requires simulating delays on the +blockchain, like: + +- staking and unstaking processes, +- governance voting periods, +- validator set changes, +- token vesting schedules, +- incentive distribution, or +- IBC packet timeouts. + +## Operations on blocks + +**`MultiTest`** provides several operations to initialize and update current block properties, such +as height and timestamp, in order to test delays on the blockchain. + + + Although it is possible to modify **only** the height or **only** the timestamp of the block in + **`MultiTest`**, this is not feasible in a real-life blockchain and may lead to unpredictable test + results that do not reflect the blockchain's real-life behavior. + + + + Always increment the height **and** the timestamp of the block in tests. + + +In the following chapters, you will find practical examples of several operations on blocks, such as +block initialization and generating a new block. + +## Initialization with the default block + +In **`MultiTest`**, the chain is initialized with a default block that has the following properties: + +- `height{:rust}`: **12345**, +- `time{:rust}`: **1571797419879305533** (Wed Oct 23 2019 02:23:39 GMT+0000), +- `chain_id{:rust}`: **cosmos-testnet-14002**. + +These properties constitute the [`BlockInfo{:rust}`][BlockInfo] structure, which is defined in the +CosmWasm library for representing block metadata. [`BlockInfo{:rust}`][BlockInfo] can be retrieved +at any point in a test by calling the [`block_info(){:rust}`][block_info] function provided by +`App{:rust}`. + +The following example illustrates this case in detail. + +```rust {4, 7} showLineNumbers copy /block_info/ /assert_eq!(12345/ /assert_eq!(1571797419879305533/ /assert_eq!("cosmos-testnet-14002"/ +use cw_multi_test::App; + +// create the default chain simulator +let app = App::default(); + +// get the initial block properties +let block = app.block_info(); + +// default block height is 12345 +assert_eq!(12345, block.height); + +// default block timestamp is Wed Oct 23 2019 02:23:39 GMT+0000 +assert_eq!(1571797419879305533, block.time.nanos()); + +// default chain identifier is "cosmos-testnet-14002" +assert_eq!("cosmos-testnet-14002", block.chain_id); +``` + +In line 4, the chain is initialized and _started_ with the default settings by calling the function +[`default(){:rust}`][app_default] on `App{:rust}`. In line 7, the current block metadata (of type +[`BlockInfo{:rust}`][BlockInfo]) is retrieved and assigned to variable `block{:rust}`. In the next +few lines, the default values are checked: + +- line 10: block `height{:rust}` is `12345{:rust}`, +- line 13: block `time{:rust}` is a Unix timestamp of value `1571797419879305533{:rust}`, which is + the numeric representation of Wednesday, October 23, 2019 02:23:39 GMT, +- line 16: block `chain_id{:rust}` has a default value `"cosmos-testnet-14002"{:rust}`. + +## Initialization with the custom block + +Although the chain initialization with the default block may be suitable for most test cases, it is +possible to initialize the chain with a custom [`BlockInfo{:rust}`][BlockInfo] using the +[`set_block(){:rust}`][set_block] function provided by `App{:rust}`. The following example explains +this case in detail. + +```rust showLineNumbers {8-12} /set_block/ /BlockInfo/ +use cosmwasm_std::{BlockInfo, Timestamp}; +use cw_multi_test::App; + +// create the default chain simulator +let mut app = App::default(); + +// create and use a custom block +app.set_block(BlockInfo { + height: 1, + time: Timestamp::from_seconds(1723627489), + chain_id: "starship-testnet".to_string(), +}); + +// get the custom block properties +let block = app.block_info(); + +// now the block height is 1 +assert_eq!(1, block.height); + +// now the block timestamp is Wed Aug 14 2024 09:24:49 GMT+0000 +assert_eq!(1723627489, block.time.seconds()); + +// now the chain identifier is "starship-testnet" +assert_eq!("starship-testnet", block.chain_id); +``` + +The chain is started with the default settings in line 5. Then in line 8, the +[`set_block(){:rust}`][set_block] function is called with custom block properties provided as +[`BlockInfo{:rust}`][BlockInfo] structure. The current block metadata is retrieved in line 12 and in +the next few lines the detailed values are checked: + +- line 18: block `height{:rust}` is now `1{:rust}`, +- line 21: new block `time{:rust}` is a Unix timestamp of value `1723627489{:rust}`, which is the + numeric representation (in seconds) of Wednesday, August 14, 2024 09:24:49 GMT, +- line 24: block `chain_id{:rust}` has now a value `"starship-testnet"{:rust}`. + +This way, you can initialize the current block in tests to best suit your requirements. + +## Generating next block with the default step + +To generate a new block in tests, you can use the [`update_block(){:rust}`][update_block] function +provided by `App{:rust}`. In the following example, [`update_block(){:rust}`][update_block] function +takes a function [`next_block(){:rust}`][next_block] as an argument. +[`next_block(){:rust}`][next_block] function - provided by **`MultiTest`** - increases the block +height by `1` and advances the block time by `5` seconds, simulating the generation of a new block +every `5` seconds in a real-life blockchain. The `chain_id{:rust}` remains unchanged. + +```rust showLineNumbers {7} /update_block/ /next_block/ +use cw_multi_test::{next_block, App}; + +// create the default chain simulator +let mut app = App::default(); + +// update the block by `generating` next block +app.update_block(next_block); + +// get the current block properties +let block = app.block_info(); + +// now the block height is 1 after the initial value +assert_eq!(12346, block.height); + +// now the block timestamp is 5 seconds after the initial value +// current value is: Wed Oct 23 2019 02:23:44 GMT+0000 +// initial value was: Wed Oct 23 2019 02:23:39 GMT+0000 +assert_eq!(1571797424879305533, block.time.nanos()); + +// chain identifier remains unchanged +assert_eq!("cosmos-testnet-14002", block.chain_id); +``` + +In line 7 the [`update_block(){:rust}`][update_block] function is called and in the next few lines +the current block properties are checked. In line 10 the block metadata is retrieved and assigned to +variable `block{:rust}`. In line 13, we check if the block height has changed. The default block +height is `12345{:rust}`, and we check it against the value `12346{:rust}`. As this assertion +passes, you can see that the block height was indeed incremented by `1`. Similarly, the block's time +is checked against the Unix timestamp, which is 5 seconds later than the default value: +`1571797424879305533{:rust}` - `1571797419879305533{:rust}` = `5000000000{:rust}` = 5 seconds. + +## Generating next block with the custom step + +Several events on the blockchain occur after a period longer than a few seconds. In such cases, +incrementing the block time multiple times until the desired time is reached would be impractical. +In **`MultiTest`**, it is possible to advance the block using a specific block height and time. To +achieve this, pass a custom closure to the [`update_block(){:rust}`][update_block] function. This +closure takes a mutable [`BlockInfo{:rust}`][BlockInfo] structure as an argument and modifies its +properties, as shown in the example below. + +```rust showLineNumbers {7-10} /update_block/ +use cw_multi_test::App; + +// create the default chain simulator +let mut app = App::default(); + +// 'generate' custom block +app.update_block(|block| { + block.time = block.time.plus_days(6); + block.height += 10000; +}); + +// get the block properties after updating +let block = app.block_info(); + +// now the block height is 10000 after the initial value +assert_eq!(22345, block.height); + +// now the block timestamp is 6 days after the initial value +// current value is: Wed Oct 23 2019 02:23:44 GMT+0000 +// initial value was: Tue Oct 29 2019 02:23:39 GMT+0000 +assert_eq!(1572315819879305533, block.time.nanos()); + +// chain identifier remains unchanged +assert_eq!("cosmos-testnet-14002", block.chain_id); +``` + +In line 4, the default block is initialized during the chain start. Then in line 7, the +[`update_block(){:rust}`][update_block] function takes a custom closure as an argument. This closure +increments the block height by **10000** and advances the block time by **6 days**. Similarly like +in prevous examples, the new block metadata is retrieved and the current properties are checked. The +new block height should be `12345{:rust}` + `10000{:rust}` = `22345{:rust}`. The new block time +should be `1572315819879305533{:rust}` -`1571797419879305533{:rust}` = `518400000000000{:rust}` = +518400 seconds = 8640 minutes = 144 hours = **6 days**. The chain identifier remains unchanged. diff --git a/src/pages/cw-multi-test/features.mdx b/src/pages/cw-multi-test/features.mdx index ca8be60d..6b94186c 100644 --- a/src/pages/cw-multi-test/features.mdx +++ b/src/pages/cw-multi-test/features.mdx @@ -15,19 +15,19 @@ you can provide your own, using `AppBuilder`'s function listed in **AppBuilder&n column. Names of **`MultiTest`** feature flags required to enable specific functionality are shown in the column **Feature flag**. -| Feature | Default
implementation | Feature
flag | AppBuilder
constructor | Functionality | -| ------------ | :------------------------: | :--------------: | -------------------------- | -------------------------------------------------- | -| Blocks | **YES** | | `with_block` | Operations on blocks. | -| API | **YES** | | `with_api` | Access to CosmWasm API. | -| Storage | **YES** | | `with_storage` | Access to storage. | -| Bank | **YES** | | `with_bank` | Interactions with **Bank** module. | -| Staking | **YES** | `staking` | `with_staking` | Interactions with **Staking** module. | -| Distribution | **YES** | `staking` | `with_distribution` | Interactions with **Distribution** module. | -| Governance | **NO** | | `with_gov` | Interactions with **Governance** module. | -| Stargate | **NO** | `stargate` | `with_stargate` | Operations using `Stargate` and/or `Any` messages. | -| Wasm | **YES** | | `with_wasm` | Interactions with **Wasm** module. | -| Custom | **NO** | | `new_custom` | Operations using custom module. | -| IBC | **NO** | `stargate` | `with_ibc` | Inter-blockchain communication operations. | +| Feature | Default
implementation | Feature
flag | AppBuilder
constructor | Functionality | +| ---------------- | :------------------------: | :--------------: | -------------------------- | -------------------------------------------------- | +| [Blocks](blocks) | **YES** | | `with_block` | Operations on blocks. | +| API | **YES** | | `with_api` | Access to CosmWasm API. | +| Storage | **YES** | | `with_storage` | Access to storage. | +| Bank | **YES** | | `with_bank` | Interactions with **Bank** module. | +| Staking | **YES** | `staking` | `with_staking` | Interactions with **Staking** module. | +| Distribution | **YES** | `staking` | `with_distribution` | Interactions with **Distribution** module. | +| Governance | **NO** | | `with_gov` | Interactions with **Governance** module. | +| Stargate | **NO** | `stargate` | `with_stargate` | Operations using `Stargate` and/or `Any` messages. | +| Wasm | **YES** | | `with_wasm` | Interactions with **Wasm** module. | +| Custom | **NO** | | `new_custom` | Operations using custom module. | +| IBC | **NO** | `stargate` | `with_ibc` | Inter-blockchain communication operations. | ## Feature flags summary