|
| 1 | + |
| 2 | +# Blocks |
| 3 | + |
| 4 | +There are several cases when testing CosmWasm smart contracts requires simulating delays on the |
| 5 | +blockchain, like: |
| 6 | + |
| 7 | +- staking and unstaking processes, |
| 8 | +- governance voting periods, |
| 9 | +- validator set changes, |
| 10 | +- token vesting schedules, |
| 11 | +- incentive distribution, or |
| 12 | +- IBC packet timeouts. |
| 13 | + |
| 14 | +## Operations on blocks |
| 15 | + |
| 16 | +**MultiTest** provides several operations to initialize and update current block properties, such |
| 17 | +as height and timestamp, in order to test delays on the blockchain. |
| 18 | + |
| 19 | +:::tip |
| 20 | + |
| 21 | +Although it is possible to modify **only** the height or **only** the timestamp of the block in |
| 22 | +**MultiTest**, this is not feasible in a real-life blockchain and may lead to unpredictable test |
| 23 | +results that do not reflect the blockchain's real-life behavior. |
| 24 | + |
| 25 | +::: |
| 26 | + |
| 27 | +:::warning |
| 28 | + |
| 29 | + Always increment the height **and** the timestamp of the block in tests. |
| 30 | + |
| 31 | +::: |
| 32 | + |
| 33 | +In the following chapters, you will find practical examples of several operations on blocks, such as |
| 34 | +block initialization and generating a new block. |
| 35 | + |
| 36 | +## Initialization with the default block |
| 37 | + |
| 38 | +In **MultiTest**, the chain is initialized with a default block that has the following properties: |
| 39 | + |
| 40 | +- `height`: **12345**, |
| 41 | +- `time`: **1571797419879305533** (Wed Oct 23 2019 02:23:39 GMT+0000), |
| 42 | +- `chain_id`: **cosmos-testnet-14002**. |
| 43 | + |
| 44 | +These properties constitute the [`BlockInfo`][BlockInfo] structure, which is defined in the |
| 45 | +CosmWasm library for representing block metadata. [`BlockInfo`][BlockInfo] can be retrieved |
| 46 | +at any point in a test by calling the [`block_info`][block_info] method provided by |
| 47 | +`App`. |
| 48 | + |
| 49 | +The following example illustrates this case in detail. |
| 50 | + |
| 51 | +```rust showLineNumbers {4,7,10,13,16} |
| 52 | +use cw_multi_test::App; |
| 53 | + |
| 54 | +// create the default chain simulator |
| 55 | +let app = App::default(); |
| 56 | + |
| 57 | +// get the initial block properties |
| 58 | +let block = app.block_info(); |
| 59 | + |
| 60 | +// default block height is 12345 |
| 61 | +assert_eq!(12345, block.height); |
| 62 | + |
| 63 | +// default block timestamp is Wed Oct 23 2019 02:23:39 GMT+0000 |
| 64 | +assert_eq!(1571797419879305533, block.time.nanos()); |
| 65 | + |
| 66 | +// default chain identifier is "cosmos-testnet-14002" |
| 67 | +assert_eq!("cosmos-testnet-14002", block.chain_id); |
| 68 | +``` |
| 69 | + |
| 70 | +In line 4, the chain is initialized and _started_ with the default settings by calling the function |
| 71 | +[`default`][app_default] on `App`. In line 7, the current block metadata (of type |
| 72 | +[`BlockInfo`][BlockInfo]) is retrieved and assigned to variable `block`. In the next |
| 73 | +few lines, the default values are checked: |
| 74 | + |
| 75 | +- line 10: block `height` is `12345`, |
| 76 | +- line 13: block `time` is a Unix timestamp of value `1571797419879305533`, which is |
| 77 | + the numeric representation of `Wednesday, October 23, 2019 02:23:39 GMT`, |
| 78 | +- line 16: block `chain_id` has a default value `"cosmos-testnet-14002"`. |
| 79 | + |
| 80 | +## Initialization with the custom block |
| 81 | + |
| 82 | +Although the chain initialization with the default block may be suitable for most test cases, it is |
| 83 | +possible to initialize the chain with a custom [`BlockInfo`][BlockInfo] using the |
| 84 | +[`set_block`][set_block] method provided by `App`. The following example explains this |
| 85 | +case in detail. |
| 86 | + |
| 87 | +```rust showLineNumbers {8-12} |
| 88 | +use cosmwasm_std::{BlockInfo, Timestamp}; |
| 89 | +use cw_multi_test::App; |
| 90 | + |
| 91 | +// create the default chain simulator |
| 92 | +let mut app = App::default(); |
| 93 | + |
| 94 | +// create and use a custom block |
| 95 | +app.set_block(BlockInfo { |
| 96 | + height: 1, |
| 97 | + time: Timestamp::from_seconds(1723627489), |
| 98 | + chain_id: "starship-testnet".to_string(), |
| 99 | +}); |
| 100 | + |
| 101 | +// get the custom block properties |
| 102 | +let block = app.block_info(); |
| 103 | + |
| 104 | +// now the block height is 1 |
| 105 | +assert_eq!(1, block.height); |
| 106 | + |
| 107 | +// now the block timestamp is Wed Aug 14 2024 09:24:49 GMT+0000 |
| 108 | +assert_eq!(1723627489, block.time.seconds()); |
| 109 | + |
| 110 | +// now the chain identifier is "starship-testnet" |
| 111 | +assert_eq!("starship-testnet", block.chain_id); |
| 112 | +``` |
| 113 | + |
| 114 | +The chain is started with the default settings in line 5. Then in line 8, the |
| 115 | +[`set_block`][set_block] method is called with custom block properties provided as |
| 116 | +[`BlockInfo`][BlockInfo] structure. The current block metadata is retrieved in line 12 and in |
| 117 | +the next few lines the detailed values are checked: |
| 118 | + |
| 119 | +- line 18: block `height` is now `1`, |
| 120 | +- line 21: new block `time` is a Unix timestamp of value `1723627489`, which is the |
| 121 | + numeric representation (in seconds) of `Wednesday, August 14, 2024 09:24:49 GMT`, |
| 122 | +- line 24: block `chain_id` has now a value `"starship-testnet"`. |
| 123 | + |
| 124 | +This way, you can initialize the current block in tests to best suit your requirements. |
| 125 | + |
| 126 | +:::info |
| 127 | + |
| 128 | +You can also initialize the blockchain simulator with the custom block using [AppBuilder], as |
| 129 | +shown in this [example](app-builder#with_block). |
| 130 | + |
| 131 | +::: |
| 132 | + |
| 133 | +## Generating next block with the default step |
| 134 | + |
| 135 | +To generate a new block in tests, you can use the [`update_block`][update_block] method |
| 136 | +provided by `App`. In the following example, [`update_block`][update_block] method |
| 137 | +takes a function [`next_block`][next_block] as an argument. [`next_block`][next_block] |
| 138 | +function - provided by **`MultiTest`** - increases the block height by `1` and advances the block |
| 139 | +time by `5` seconds, simulating the generation of a new block every `5` seconds in a real-life |
| 140 | +blockchain. The `chain_id` remains unchanged. |
| 141 | + |
| 142 | +```rust showLineNumbers {7} |
| 143 | +use cw_multi_test::{next_block, App}; |
| 144 | + |
| 145 | +// create the default chain simulator |
| 146 | +let mut app = App::default(); |
| 147 | + |
| 148 | +// update the block by `generating` next block |
| 149 | +app.update_block(next_block); |
| 150 | + |
| 151 | +// get the current block properties |
| 152 | +let block = app.block_info(); |
| 153 | + |
| 154 | +// now the block height is 1 after the initial value |
| 155 | +assert_eq!(12346, block.height); |
| 156 | + |
| 157 | +// now the block timestamp is 5 seconds after the initial value |
| 158 | +// current value is: Wed Oct 23 2019 02:23:44 GMT+0000 |
| 159 | +// initial value was: Wed Oct 23 2019 02:23:39 GMT+0000 |
| 160 | +assert_eq!(1571797424879305533, block.time.nanos()); |
| 161 | + |
| 162 | +// chain identifier remains unchanged |
| 163 | +assert_eq!("cosmos-testnet-14002", block.chain_id); |
| 164 | +``` |
| 165 | + |
| 166 | +In line 7 the [`update_block`][update_block] method is called and in the next few lines the |
| 167 | +current block properties are checked. In line 10 the block metadata is retrieved and assigned to |
| 168 | +variable `block`. In line 13, we check if the block height has changed. The default block |
| 169 | +height is `12345`, and we check it against the value `12346`. As this assertion |
| 170 | +passes, you can see that the block height was indeed incremented by `1`. Similarly, the block's time |
| 171 | +is checked against the Unix timestamp, which is 5 seconds later than the default value: |
| 172 | +`1571797424879305533` - `1571797419879305533` = `5000000000` = 5 seconds. |
| 173 | + |
| 174 | +## Generating next block with the custom step |
| 175 | + |
| 176 | +Several events on the blockchain occur after a period longer than a few seconds. In such cases, |
| 177 | +incrementing the block time multiple times until the desired time is reached would be impractical. |
| 178 | +In **`MultiTest`**, it is possible to advance the block using a specific block height and time. To |
| 179 | +achieve this, pass a custom closure to the [`update_block`][update_block] method. This |
| 180 | +closure takes a mutable [`BlockInfo`][BlockInfo] structure as an argument and modifies its |
| 181 | +properties, as shown in the example below. |
| 182 | + |
| 183 | +```rust showLineNumbers {7-10} |
| 184 | +use cw_multi_test::App; |
| 185 | + |
| 186 | +// create the default chain simulator |
| 187 | +let mut app = App::default(); |
| 188 | + |
| 189 | +// 'generate' custom block |
| 190 | +app.update_block(|block| { |
| 191 | + block.time = block.time.plus_days(6); |
| 192 | + block.height += 10000; |
| 193 | +}); |
| 194 | + |
| 195 | +// get the block properties after updating |
| 196 | +let block = app.block_info(); |
| 197 | + |
| 198 | +// now the block height is 10000 after the initial value |
| 199 | +assert_eq!(22345, block.height); |
| 200 | + |
| 201 | +// now the block timestamp is 6 days after the initial value |
| 202 | +// current value is: Wed Oct 23 2019 02:23:44 GMT+0000 |
| 203 | +// initial value was: Tue Oct 29 2019 02:23:39 GMT+0000 |
| 204 | +assert_eq!(1572315819879305533, block.time.nanos()); |
| 205 | + |
| 206 | +// chain identifier remains unchanged |
| 207 | +assert_eq!("cosmos-testnet-14002", block.chain_id); |
| 208 | +``` |
| 209 | + |
| 210 | +In line 4, the default block is initialized during the chain start. Then in line 7, the |
| 211 | +[`update_block`][update_block] method takes a custom closure as an argument. This closure |
| 212 | +increments the block height by **10000** and advances the block time by **6 days**. Similarly like |
| 213 | +in previous examples, the new block metadata is retrieved and the current properties are checked. |
| 214 | +The new block height should be `12345` + `10000` = `22345`. The new block time |
| 215 | +should be `1572315819879305533` -`1571797419879305533` = `518400000000000` = |
| 216 | +518400 seconds = 8640 minutes = 144 hours = **6 days**. The chain identifier remains unchanged. |
| 217 | + |
| 218 | +[BlockInfo]: https://docs.rs/cosmwasm-std/latest/cosmwasm_std/struct.BlockInfo.html |
| 219 | +[AppBuilder]: https://docs.rs/cw-multi-test/latest/cw_multi_test/struct.AppBuilder.html |
| 220 | +[block_info]: https://docs.rs/cw-multi-test/latest/cw_multi_test/struct.App.html#method.block_info |
| 221 | +[set_block]: https://docs.rs/cw-multi-test/latest/cw_multi_test/struct.App.html#method.set_block |
| 222 | +[update_block]: https://docs.rs/cw-multi-test/latest/cw_multi_test/struct.App.html#method.update_block |
| 223 | +[next_block]: https://docs.rs/cw-multi-test/latest/cw_multi_test/fn.next_block.html |
| 224 | +[app_default]: app#default |
0 commit comments