Skip to content

Commit 10661dd

Browse files
authored
Merge pull request #732 from CosmWasm/dump-state-multitest
Dump state multitest
2 parents 6d20854 + 7f86a66 commit 10661dd

File tree

2 files changed

+133
-71
lines changed

2 files changed

+133
-71
lines changed

packages/multi-test/src/app.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ use anyhow::Result as AnyResult;
55
use cosmwasm_std::testing::{mock_env, MockApi, MockStorage};
66
use cosmwasm_std::{
77
from_slice, to_binary, Addr, Api, Binary, BlockInfo, ContractResult, CustomQuery, Empty,
8-
Querier, QuerierResult, QuerierWrapper, QueryRequest, Storage, SystemError, SystemResult,
8+
Querier, QuerierResult, QuerierWrapper, QueryRequest, Record, Storage, SystemError,
9+
SystemResult,
910
};
1011
use schemars::JsonSchema;
1112
use serde::de::DeserializeOwned;
@@ -565,6 +566,11 @@ where
565566
pub fn contract_data(&self, address: &Addr) -> AnyResult<ContractData> {
566567
self.read_module(|router, _, storage| router.wasm.load_contract(storage, address))
567568
}
569+
570+
/// This gets a raw state dump of all key-values held by a given contract
571+
pub fn dump_wasm_raw(&self, address: &Addr) -> Vec<Record> {
572+
self.read_module(|router, _, storage| router.wasm.dump_wasm_raw(storage, address))
573+
}
568574
}
569575

570576
impl<BankT, ApiT, StorageT, CustomT, WasmT, StakingT, DistrT>

packages/multi-test/src/wasm.rs

Lines changed: 126 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use std::ops::Deref;
55
use cosmwasm_std::{
66
to_binary, Addr, Api, Attribute, BankMsg, Binary, BlockInfo, Coin, ContractInfo,
77
ContractInfoResponse, CustomQuery, Deps, DepsMut, Env, Event, MessageInfo, Order, Querier,
8-
QuerierWrapper, Reply, ReplyOn, Response, StdResult, Storage, SubMsg, SubMsgResponse,
8+
QuerierWrapper, Record, Reply, ReplyOn, Response, StdResult, Storage, SubMsg, SubMsgResponse,
99
SubMsgResult, TransactionInfo, WasmMsg, WasmQuery,
1010
};
1111
use cosmwasm_storage::{prefixed, prefixed_read, PrefixedStorage, ReadonlyPrefixedStorage};
@@ -191,6 +191,80 @@ impl<ExecC, QueryC> WasmKeeper<ExecC, QueryC> {
191191
.load(&prefixed_read(storage, NAMESPACE_WASM), address)
192192
.map_err(Into::into)
193193
}
194+
195+
pub fn dump_wasm_raw(&self, storage: &dyn Storage, address: &Addr) -> Vec<Record> {
196+
let storage = self.contract_storage_readonly(storage, address);
197+
storage.range(None, None, Order::Ascending).collect()
198+
}
199+
200+
fn contract_namespace(&self, contract: &Addr) -> Vec<u8> {
201+
let mut name = b"contract_data/".to_vec();
202+
name.extend_from_slice(contract.as_bytes());
203+
name
204+
}
205+
206+
fn contract_storage<'a>(
207+
&self,
208+
storage: &'a mut dyn Storage,
209+
address: &Addr,
210+
) -> Box<dyn Storage + 'a> {
211+
// We double-namespace this, once from global storage -> wasm_storage
212+
// then from wasm_storage -> the contracts subspace
213+
let namespace = self.contract_namespace(address);
214+
let storage = PrefixedStorage::multilevel(storage, &[NAMESPACE_WASM, &namespace]);
215+
Box::new(storage)
216+
}
217+
218+
// fails RUNTIME if you try to write. please don't
219+
fn contract_storage_readonly<'a>(
220+
&self,
221+
storage: &'a dyn Storage,
222+
address: &Addr,
223+
) -> Box<dyn Storage + 'a> {
224+
// We double-namespace this, once from global storage -> wasm_storage
225+
// then from wasm_storage -> the contracts subspace
226+
let namespace = self.contract_namespace(address);
227+
let storage = ReadonlyPrefixedStorage::multilevel(storage, &[NAMESPACE_WASM, &namespace]);
228+
Box::new(storage)
229+
}
230+
231+
fn verify_attributes(attributes: &[Attribute]) -> AnyResult<()> {
232+
for attr in attributes {
233+
let key = attr.key.trim();
234+
let val = attr.value.trim();
235+
236+
if key.is_empty() {
237+
bail!(Error::empty_attribute_key(val));
238+
}
239+
240+
if val.is_empty() {
241+
bail!(Error::empty_attribute_value(key));
242+
}
243+
244+
if key.starts_with('_') {
245+
bail!(Error::reserved_attribute_key(key));
246+
}
247+
}
248+
249+
Ok(())
250+
}
251+
252+
fn verify_response<T>(response: Response<T>) -> AnyResult<Response<T>>
253+
where
254+
T: Clone + fmt::Debug + PartialEq + JsonSchema,
255+
{
256+
Self::verify_attributes(&response.attributes)?;
257+
258+
for event in &response.events {
259+
Self::verify_attributes(&event.attributes)?;
260+
let ty = event.ty.trim();
261+
if ty.len() < 2 {
262+
bail!(Error::event_type_too_short(ty));
263+
}
264+
}
265+
266+
Ok(response)
267+
}
194268
}
195269

196270
impl<ExecC, QueryC> WasmKeeper<ExecC, QueryC>
@@ -782,75 +856,6 @@ where
782856
// it is lowercase to be compatible with the MockApi implementation of cosmwasm-std >= 1.0.0-beta8
783857
Addr::unchecked(format!("contract{}", count))
784858
}
785-
786-
fn contract_namespace(&self, contract: &Addr) -> Vec<u8> {
787-
let mut name = b"contract_data/".to_vec();
788-
name.extend_from_slice(contract.as_bytes());
789-
name
790-
}
791-
792-
fn contract_storage<'a>(
793-
&self,
794-
storage: &'a mut dyn Storage,
795-
address: &Addr,
796-
) -> Box<dyn Storage + 'a> {
797-
// We double-namespace this, once from global storage -> wasm_storage
798-
// then from wasm_storage -> the contracts subspace
799-
let namespace = self.contract_namespace(address);
800-
let storage = PrefixedStorage::multilevel(storage, &[NAMESPACE_WASM, &namespace]);
801-
Box::new(storage)
802-
}
803-
804-
// fails RUNTIME if you try to write. please don't
805-
fn contract_storage_readonly<'a>(
806-
&self,
807-
storage: &'a dyn Storage,
808-
address: &Addr,
809-
) -> Box<dyn Storage + 'a> {
810-
// We double-namespace this, once from global storage -> wasm_storage
811-
// then from wasm_storage -> the contracts subspace
812-
let namespace = self.contract_namespace(address);
813-
let storage = ReadonlyPrefixedStorage::multilevel(storage, &[NAMESPACE_WASM, &namespace]);
814-
Box::new(storage)
815-
}
816-
817-
fn verify_attributes(attributes: &[Attribute]) -> AnyResult<()> {
818-
for attr in attributes {
819-
let key = attr.key.trim();
820-
let val = attr.value.trim();
821-
822-
if key.is_empty() {
823-
bail!(Error::empty_attribute_key(val));
824-
}
825-
826-
if val.is_empty() {
827-
bail!(Error::empty_attribute_value(key));
828-
}
829-
830-
if key.starts_with('_') {
831-
bail!(Error::reserved_attribute_key(key));
832-
}
833-
}
834-
835-
Ok(())
836-
}
837-
838-
fn verify_response<T>(response: Response<T>) -> AnyResult<Response<T>>
839-
where
840-
T: Clone + fmt::Debug + PartialEq + JsonSchema,
841-
{
842-
Self::verify_attributes(&response.attributes)?;
843-
844-
for event in &response.events {
845-
Self::verify_attributes(&event.attributes)?;
846-
let ty = event.ty.trim();
847-
if ty.len() < 2 {
848-
bail!(Error::event_type_too_short(ty));
849-
}
850-
}
851-
852-
Ok(response)
853-
}
854859
}
855860

856861
// TODO: replace with code in utils
@@ -1050,6 +1055,57 @@ mod test {
10501055
assert_eq!(expected, from_slice(&info).unwrap());
10511056
}
10521057

1058+
#[test]
1059+
fn can_dump_raw_wasm_state() {
1060+
let api = MockApi::default();
1061+
let mut keeper = WasmKeeper::<Empty, Empty>::new();
1062+
let block = mock_env().block;
1063+
let code_id = keeper.store_code(payout::contract());
1064+
1065+
let mut wasm_storage = MockStorage::new();
1066+
1067+
let contract_addr = keeper
1068+
.register_contract(
1069+
&mut wasm_storage,
1070+
code_id,
1071+
Addr::unchecked("foobar"),
1072+
Addr::unchecked("admin"),
1073+
"label".to_owned(),
1074+
1000,
1075+
)
1076+
.unwrap();
1077+
1078+
// make a contract with state
1079+
let payout = coin(1500, "mlg");
1080+
let msg = payout::InstantiateMessage {
1081+
payout: payout.clone(),
1082+
};
1083+
keeper
1084+
.call_instantiate(
1085+
contract_addr.clone(),
1086+
&api,
1087+
&mut wasm_storage,
1088+
&mock_router(),
1089+
&block,
1090+
mock_info("foobar", &[]),
1091+
to_vec(&msg).unwrap(),
1092+
)
1093+
.unwrap();
1094+
1095+
// dump state
1096+
let state = keeper.dump_wasm_raw(&wasm_storage, &contract_addr);
1097+
assert_eq!(state.len(), 2);
1098+
// check contents
1099+
let (k, v) = &state[0];
1100+
assert_eq!(k.as_slice(), b"count");
1101+
let count: u32 = from_slice(v).unwrap();
1102+
assert_eq!(count, 1);
1103+
let (k, v) = &state[1];
1104+
assert_eq!(k.as_slice(), b"payout");
1105+
let stored_pay: payout::InstantiateMessage = from_slice(v).unwrap();
1106+
assert_eq!(stored_pay.payout, payout);
1107+
}
1108+
10531109
#[test]
10541110
fn contract_send_coins() {
10551111
let api = MockApi::default();

0 commit comments

Comments
 (0)