use cosmwasm_std::Coin;
use std::collections::HashSet;
use crate::capabilities::capabilities_from_csv;
use crate::compatibility::check_wasm;
use crate::instance::{Instance, InstanceOptions};
use crate::internals::Logger;
use crate::size::Size;
use crate::{Backend, BackendApi, Querier, Storage, WasmLimits};
use super::mock::{MockApi, MOCK_CONTRACT_ADDR};
use super::querier::MockQuerier;
use super::storage::MockStorage;
const DEFAULT_GAS_LIMIT: u64 = 2_000_000_000; const DEFAULT_MEMORY_LIMIT: Option<Size> = Some(Size::mebi(16));
pub fn mock_instance(
wasm: &[u8],
contract_balance: &[Coin],
) -> Instance<MockApi, MockStorage, MockQuerier> {
mock_instance_with_options(
wasm,
MockInstanceOptions {
contract_balance: Some(contract_balance),
..Default::default()
},
)
}
pub fn mock_instance_with_failing_api(
wasm: &[u8],
contract_balance: &[Coin],
backend_error: &'static str,
) -> Instance<MockApi, MockStorage, MockQuerier> {
mock_instance_with_options(
wasm,
MockInstanceOptions {
contract_balance: Some(contract_balance),
backend_error: Some(backend_error),
..Default::default()
},
)
}
pub fn mock_instance_with_balances(
wasm: &[u8],
balances: &[(&str, &[Coin])],
) -> Instance<MockApi, MockStorage, MockQuerier> {
mock_instance_with_options(
wasm,
MockInstanceOptions {
balances,
..Default::default()
},
)
}
pub fn mock_instance_with_gas_limit(
wasm: &[u8],
gas_limit: u64,
) -> Instance<MockApi, MockStorage, MockQuerier> {
mock_instance_with_options(
wasm,
MockInstanceOptions {
gas_limit,
..Default::default()
},
)
}
#[derive(Debug)]
pub struct MockInstanceOptions<'a> {
pub balances: &'a [(&'a str, &'a [Coin])],
pub contract_balance: Option<&'a [Coin]>,
pub backend_error: Option<&'static str>,
pub available_capabilities: HashSet<String>,
pub gas_limit: u64,
pub memory_limit: Option<Size>,
}
impl MockInstanceOptions<'_> {
fn default_capabilities() -> HashSet<String> {
#[allow(unused_mut)]
let mut out = capabilities_from_csv(
"iterator,staking,cosmwasm_1_1,cosmwasm_1_2,cosmwasm_1_3,cosmwasm_1_4,cosmwasm_2_0,cosmwasm_2_1,cosmwasm_2_2",
);
#[cfg(feature = "stargate")]
out.insert("stargate".to_string());
out
}
}
impl Default for MockInstanceOptions<'_> {
fn default() -> Self {
Self {
balances: Default::default(),
contract_balance: Default::default(),
backend_error: None,
available_capabilities: Self::default_capabilities(),
gas_limit: DEFAULT_GAS_LIMIT,
memory_limit: DEFAULT_MEMORY_LIMIT,
}
}
}
pub fn mock_instance_with_options(
wasm: &[u8],
options: MockInstanceOptions,
) -> Instance<MockApi, MockStorage, MockQuerier> {
check_wasm(
wasm,
&options.available_capabilities,
&WasmLimits::default(),
Logger::Off,
)
.unwrap();
let contract_address = MOCK_CONTRACT_ADDR;
let mut balances = options.balances.to_vec();
if let Some(contract_balance) = options.contract_balance {
if let Some(pos) = balances.iter().position(|item| item.0 == contract_address) {
balances.remove(pos);
}
balances.push((contract_address, contract_balance));
}
let api = if let Some(backend_error) = options.backend_error {
MockApi::new_failing(backend_error)
} else {
MockApi::default()
};
let backend = Backend {
api,
storage: MockStorage::default(),
querier: MockQuerier::new(&balances),
};
let memory_limit = options.memory_limit;
let options = InstanceOptions {
gas_limit: options.gas_limit,
};
Instance::from_code(wasm, backend, options, memory_limit).unwrap()
}
pub fn mock_instance_options() -> (InstanceOptions, Option<Size>) {
(
InstanceOptions {
gas_limit: DEFAULT_GAS_LIMIT,
},
DEFAULT_MEMORY_LIMIT,
)
}
pub fn test_io<A, S, Q>(instance: &mut Instance<A, S, Q>)
where
A: BackendApi + 'static,
S: Storage + 'static,
Q: Querier + 'static,
{
let sizes: Vec<usize> = vec![0, 1, 3, 10, 200, 2000, 5 * 1024];
let bytes: Vec<u8> = vec![0x00, 0xA5, 0xFF];
for size in sizes.into_iter() {
for byte in bytes.iter() {
let original = vec![*byte; size];
let wasm_ptr = instance
.allocate(original.len())
.expect("Could not allocate memory");
instance
.write_memory(wasm_ptr, &original)
.expect("Could not write data");
let wasm_data = instance.read_memory(wasm_ptr, size).expect("error reading");
assert_eq!(
original, wasm_data,
"failed for size {size}; expected: {original:?}; actual: {wasm_data:?}"
);
instance
.deallocate(wasm_ptr)
.expect("Could not deallocate memory");
}
}
}