Last active
November 17, 2022 15:34
-
-
Save dakom/e1d8bdbe7f604a7445f80f889ec19acc to your computer and use it in GitHub Desktop.
cosmwasm vm gas profiling
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// EXAMPLE: | |
// let contract_1 = Contract::new("contract_1", contract_1::msg::InstantiateMsg{}); | |
// let (gas_used, resp) = contract_1.execute(contract_1::msg::ExecuteMsg::Foo{inner_msg_data: "bar"}); | |
// println!("computational gas used: {}", gas); | |
// println!("execution response: {:?}", resp); | |
// | |
// storage actions can also be calculated by estimating as follows: | |
// let data_len = storage_binary_len(&SomeStorageStruct{}); | |
// calculate costs according to https://github.com/cosmos/cosmos-sdk/blob/65015c2f96fa0087891d4caa3267c426e737ddeb/store/types/gas.go#L231 | |
use std::{ | |
fs::File, | |
io::Read, | |
path::Path, | |
str, | |
}; | |
use cosmwasm_std::{coins, Empty, Response}; | |
use cosmwasm_vm::testing::{ | |
mock_backend, mock_env, mock_info, MockApi, MockStorage, MockQuerier | |
}; | |
use cosmwasm_vm::{ | |
call_execute, call_instantiate, Instance, | |
InstanceOptions, Size, | |
}; | |
use serde::Serialize; | |
// set this to the path where your compiled .wasm resides | |
const ARTIFACTS_PATH:&str = "../artifacts"; | |
const DEFAULT_MEMORY_LIMIT: Size = Size::mebi(64); | |
const HIGH_GAS_LIMIT: u64 = 20_000_000_000_000_000; // ~20s, allows many calls on one instance | |
const COIN_DENOM:&str = "uwasm"; | |
// vm gas -> sdk gas multiplier | |
// see https://github.com/CosmWasm/wasmd/blob/ab4ffa51e651a9a21095dbb102a032d62b11729c/x/wasm/keeper/gas_register.go#L31 | |
const GAS_MULTIPLIER:u64 = 140_000_000; | |
pub type MockInstance = Instance<MockApi, MockStorage, MockQuerier>; | |
pub struct Contract { | |
pub instance: MockInstance | |
} | |
impl Contract { | |
pub fn new(contract_name: &str, instantiate_msg: impl Serialize) -> Self { | |
let bytes = load_file_bytes(contract_name).unwrap(); | |
let backend = mock_backend(&[]); | |
let options: InstanceOptions = InstanceOptions { | |
gas_limit: HIGH_GAS_LIMIT, | |
print_debug: true, | |
}; | |
let mut instance = | |
Instance::from_code(&bytes, backend, options, Some(DEFAULT_MEMORY_LIMIT)).unwrap(); | |
let admin = mock_info("admin", &coins(1000, COIN_DENOM)); | |
let msg = serde_json_wasm::to_vec(&instantiate_msg).unwrap(); | |
call_instantiate::<_, _, _, Empty>(&mut instance, &mock_env(), &admin, &msg).unwrap().unwrap(); | |
Self { | |
instance | |
} | |
} | |
// returns (gas_used_in_sdk_units, Response) | |
pub fn execute(&mut self, execute_msg: impl Serialize) -> (u64, Response) { | |
let user = mock_info("user", &[]); | |
println!("EXECUTING: [{}]", serde_json_wasm::to_string(&execute_msg).unwrap()); | |
let msg = serde_json_wasm::to_vec(&execute_msg).unwrap(); | |
let gas_before = self.instance.get_gas_left(); | |
let res = | |
call_execute::<_, _, _, Empty>(&mut self.instance, &mock_env(), &user, &msg).unwrap().unwrap(); | |
let gas_used = gas_before - self.instance.get_gas_left(); | |
((gas_used / CONFIG.wasm_gas_multiplier), res) | |
} | |
} | |
pub fn load_file_bytes(contract_name: &str) -> Result<Vec<u8>, String> { | |
let path_str = format!("{}/{}.wasm", ARTIFACTS_PATH, contract_name); | |
let path = Path::new(&path_str); | |
let mut f = File::open(path.clone()).map_err(|_| format!("file not found: {:?}", path))?; | |
let mut contents = Vec::new(); | |
f.read_to_end(&mut contents) | |
.map_err(|_| "unable to read the file")?; | |
Ok(contents) | |
} | |
fn storage_binary_len(data: &impl Serialize) -> usize { | |
let bytes:Vec<u8> = cosmwasm_std::to_vec(data).unwrap(); | |
let bytes_len = bytes.len(); | |
((bytes_len as f32 / 3.0).ceil() * 4.0) as usize | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment