clone_cw_multi_test/wasm_emulation/storage/
analyzer.rs1use crate::{
2 prefixed_storage::{decode_length, to_length_prefixed, CONTRACT_STORAGE_PREFIX},
3 wasm_emulation::channel::RemoteChannel,
4 BankKeeper, Distribution, Gov, Ibc, Module, Staking, WasmKeeper,
5};
6use cosmwasm_std::{Addr, Api, Coin, CustomMsg, CustomQuery, Storage};
7use cw_orch::prelude::BankQuerier;
8use cw_utils::NativeBalance;
9use rustc_serialize::json::Json;
10use serde::de::DeserializeOwned;
11use serde::Serialize;
12use treediff::diff;
13use treediff::tools::Recorder;
14
15use crate::wasm::NAMESPACE_WASM;
16
17use crate::{wasm_emulation::input::QuerierStorage, App};
18
19use anyhow::Result as AnyResult;
20
21#[derive(Serialize)]
22pub struct SerializableCoin {
23 amount: String,
24 denom: String,
25}
26
27pub struct StorageAnalyzer {
28 pub storage: QuerierStorage,
29 pub remote: RemoteChannel,
30}
31
32pub type CustomWasmKeeper<CustomT> =
33 WasmKeeper<<CustomT as Module>::ExecT, <CustomT as Module>::QueryT>;
34
35impl StorageAnalyzer {
36 pub fn new<ApiT, StorageT, CustomT, StakingT, DistrT, IbcT, GovT>(
37 app: &App<
38 BankKeeper,
39 ApiT,
40 StorageT,
41 CustomT,
42 CustomWasmKeeper<CustomT>,
43 StakingT,
44 DistrT,
45 IbcT,
46 GovT,
47 >,
48 ) -> AnyResult<Self>
49 where
50 CustomT::ExecT: CustomMsg + DeserializeOwned + 'static,
51 CustomT::QueryT: CustomQuery + DeserializeOwned + 'static,
52
53 ApiT: Api,
54 StorageT: Storage,
55 CustomT: Module,
56 StakingT: Staking,
57 DistrT: Distribution,
58 IbcT: Ibc,
59 GovT: Gov,
60 {
61 Ok(Self {
62 storage: app.get_querier_storage()?,
63 remote: app.remote.clone(),
64 })
65 }
66
67 pub fn get_contract_storage(
68 &self,
69 contract_addr: impl Into<String>,
70 ) -> Vec<(Vec<u8>, Vec<u8>)> {
71 self.storage
72 .wasm
73 .get_contract_storage(&Addr::unchecked(contract_addr.into()))
74 }
75
76 pub fn readable_storage(&self, contract_addr: impl Into<String>) -> Vec<(String, String)> {
77 self.storage
78 .wasm
79 .get_contract_storage(&Addr::unchecked(contract_addr.into()))
80 .into_iter()
81 .map(|(key, value)| {
82 (
83 String::from_utf8_lossy(&key).to_string(),
84 String::from_utf8_lossy(&value).to_string(),
85 )
86 })
87 .collect()
88 }
89
90 pub fn all_contract_storage(&self) -> Vec<(String, Vec<u8>, Vec<u8>)> {
92 self.storage
94 .wasm
95 .storage
96 .iter()
97 .filter(|(key, _)| {
98 let prefix = to_length_prefixed(NAMESPACE_WASM);
100 key.len() >= prefix.len() && key[..prefix.len()] == prefix
101 })
102 .filter_map(|(key, value)| {
103 let prefix_len = to_length_prefixed(NAMESPACE_WASM).len();
106 let resulting_key = &key[prefix_len..];
107 let addr_len: usize = decode_length([resulting_key[0], resulting_key[1]])
108 .try_into()
109 .unwrap();
110 let contract_addr_addr =
111 String::from_utf8_lossy(&resulting_key[2..(addr_len + 2)]).to_string();
112
113 let split: Vec<_> = contract_addr_addr.split('/').collect();
114 if split.len() != 2 || format!("{}/", split[0]) != CONTRACT_STORAGE_PREFIX {
115 return None;
116 }
117
118 Some((
119 split[1].to_string(),
120 resulting_key[addr_len + 2..].to_vec(),
121 value.clone(),
122 ))
123 })
124 .collect()
125 }
126
127 pub fn all_readable_contract_storage(&self) -> Vec<(String, String, String)> {
128 self.all_contract_storage()
129 .into_iter()
130 .map(|(contract, key, value)| {
131 (
132 contract,
133 String::from_utf8_lossy(&key).to_string(),
134 String::from_utf8_lossy(&value).to_string(),
135 )
136 })
137 .collect()
138 }
139
140 pub fn compare_all_readable_contract_storage(&self) {
141 let wasm_querier = cw_orch::daemon::queriers::CosmWasm::new_sync(
142 self.remote.channel.clone(),
143 &self.remote.rt,
144 );
145 self.all_contract_storage()
146 .into_iter()
147 .for_each(|(contract_addr, key, value)| {
148 let distant_data = self.remote.rt.block_on(
150 wasm_querier
151 ._contract_raw_state(&Addr::unchecked(contract_addr.clone()), key.clone()),
152 );
153
154 if let Ok(data) = distant_data {
155 let local_json: Json =
156 if let Ok(v) = String::from_utf8_lossy(&value).to_string().parse() {
157 v
158 } else {
159 log::info!(
160 "Storage at {}, and key {}, was : {:x?}, now {:x?}",
161 contract_addr,
162 String::from_utf8_lossy(&key).to_string(),
163 data.data,
164 value
165 );
166 return;
167 };
168 let distant_json: Json =
169 if let Ok(v) = String::from_utf8_lossy(&data.data).to_string().parse() {
170 v
171 } else {
172 log::info!(
173 "Storage at {}, and key {}, was : {:x?}, now {:x?}",
174 contract_addr,
175 String::from_utf8_lossy(&key).to_string(),
176 data.data,
177 value
178 );
179 return;
180 };
181
182 let mut d = Recorder::default();
183 diff(&distant_json, &local_json, &mut d);
184
185 let changes: Vec<_> = d
186 .calls
187 .iter()
188 .filter(|change| {
189 !matches!(change, treediff::tools::ChangeType::Unchanged(..))
190 })
191 .collect();
192
193 log::info!(
194 "Storage at {}, and key {}, changed like so : {:?}",
195 contract_addr,
196 String::from_utf8_lossy(&key).to_string(),
197 changes
198 );
199 } else if let Ok(v) = String::from_utf8_lossy(&value).to_string().parse::<Json>() {
200 log::info!(
201 "Storage at {}, and key {}, is new : {}",
202 contract_addr,
203 String::from_utf8_lossy(&key).to_string(),
204 v
205 );
206 } else {
207 log::info!(
208 "Storage at {}, and key {}, is new : {:?}",
209 contract_addr,
210 String::from_utf8_lossy(&key).to_string(),
211 value
212 );
213 }
214 });
215 }
216
217 pub fn get_balance(&self, addr: impl Into<String>) -> Vec<Coin> {
218 let addr: String = addr.into();
219 self.storage
220 .bank
221 .storage
222 .iter()
223 .find(|(a, _)| a.as_str() == addr)
224 .map(|(_, b)| b.0.clone())
225 .unwrap_or(vec![])
226 }
227
228 pub fn compare_all_balances(&self) {
229 let bank_querier = cw_orch::daemon::queriers::Bank {
230 channel: self.remote.channel.clone(),
231 rt_handle: Some(self.remote.rt.clone()),
232 };
233 self.get_all_local_balances()
234 .into_iter()
235 .for_each(|(addr, balances)| {
236 let distant_data = bank_querier.balance(&addr, None);
238
239 if let Ok(distant_coins) = distant_data {
240 let distant_coins = serde_json::to_string(&distant_coins).unwrap();
241 let distant_coins: Json = distant_coins.parse().unwrap();
242
243 let local_coins = serde_json::to_string(&balances.0).unwrap();
244 let local_coins: Json = local_coins.parse().unwrap();
245
246 let mut d = Recorder::default();
247 diff(&distant_coins, &local_coins, &mut d);
248
249 let changes: Vec<_> = d
250 .calls
251 .iter()
252 .filter(|change| {
253 !matches!(change, treediff::tools::ChangeType::Unchanged(..))
254 })
255 .collect();
256
257 log::info!("Bank balance for {} changed like so : {:?}", addr, changes);
258 }
259 });
260 }
261
262 pub fn get_all_local_balances(&self) -> Vec<(Addr, NativeBalance)> {
263 self.storage.bank.storage.clone()
264 }
265}