@@ -5,7 +5,7 @@ use std::ops::Deref;
5
5
use cosmwasm_std:: {
6
6
to_binary, Addr , Api , Attribute , BankMsg , Binary , BlockInfo , Coin , ContractInfo ,
7
7
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 ,
9
9
SubMsgResult , TransactionInfo , WasmMsg , WasmQuery ,
10
10
} ;
11
11
use cosmwasm_storage:: { prefixed, prefixed_read, PrefixedStorage , ReadonlyPrefixedStorage } ;
@@ -191,6 +191,80 @@ impl<ExecC, QueryC> WasmKeeper<ExecC, QueryC> {
191
191
. load ( & prefixed_read ( storage, NAMESPACE_WASM ) , address)
192
192
. map_err ( Into :: into)
193
193
}
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
+ }
194
268
}
195
269
196
270
impl < ExecC , QueryC > WasmKeeper < ExecC , QueryC >
@@ -782,75 +856,6 @@ where
782
856
// it is lowercase to be compatible with the MockApi implementation of cosmwasm-std >= 1.0.0-beta8
783
857
Addr :: unchecked ( format ! ( "contract{}" , count) )
784
858
}
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
- }
854
859
}
855
860
856
861
// TODO: replace with code in utils
@@ -1050,6 +1055,57 @@ mod test {
1050
1055
assert_eq ! ( expected, from_slice( & info) . unwrap( ) ) ;
1051
1056
}
1052
1057
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
+
1053
1109
#[ test]
1054
1110
fn contract_send_coins ( ) {
1055
1111
let api = MockApi :: default ( ) ;
0 commit comments