clone_cw_multi_test/wasm_emulation/query/
wasm.rs

1use std::marker::PhantomData;
2
3use crate::addons::MockApiBech32;
4use crate::prefixed_storage::get_full_contract_storage_namespace;
5use crate::queries::wasm::WasmRemoteQuerier;
6use crate::wasm_emulation::query::gas::{
7    GAS_COST_ALL_QUERIES, GAS_COST_CONTRACT_INFO, GAS_COST_RAW_COSMWASM_QUERY,
8};
9use crate::wasm_emulation::query::mock_querier::QueryResultWithGas;
10use crate::wasm_emulation::query::MockQuerier;
11use crate::Contract;
12
13use crate::wasm_emulation::contract::WasmContract;
14use cosmwasm_std::testing::MockStorage;
15use cosmwasm_vm::GasInfo;
16
17use cosmwasm_std::{
18    to_json_binary, Addr, ContractInfoResponse, CustomMsg, CustomQuery, OwnedDeps, Storage,
19    SystemError, SystemResult,
20};
21use cosmwasm_std::{ContractInfo, ContractResult};
22
23use cosmwasm_std::WasmQuery;
24use cw_orch::daemon::queriers::CosmWasm;
25use serde::de::DeserializeOwned;
26
27use crate::wasm_emulation::channel::RemoteChannel;
28
29use super::mock_querier::ForkState;
30
31pub struct WasmQuerier<
32    ExecC: CustomMsg + DeserializeOwned + 'static,
33    QueryC: CustomQuery + DeserializeOwned + 'static,
34> {
35    fork_state: ForkState<ExecC, QueryC>,
36}
37
38impl<
39        ExecC: CustomMsg + DeserializeOwned + 'static,
40        QueryC: CustomQuery + DeserializeOwned + 'static,
41    > WasmQuerier<ExecC, QueryC>
42{
43    pub fn new(fork_state: ForkState<ExecC, QueryC>) -> Self {
44        Self { fork_state }
45    }
46
47    pub fn query(&self, remote: RemoteChannel, request: &WasmQuery) -> QueryResultWithGas {
48        match request {
49            WasmQuery::ContractInfo { contract_addr } => {
50                let addr = Addr::unchecked(contract_addr);
51                let data = if let Some(local_contract) = self
52                    .fork_state
53                    .querier_storage
54                    .wasm
55                    .contracts
56                    .get(contract_addr)
57                {
58                    local_contract.clone()
59                } else {
60                    let maybe_distant_contract = WasmRemoteQuerier::load_distant_contract(
61                        self.fork_state.remote.clone(),
62                        &addr,
63                    );
64
65                    if let Ok(contract) = maybe_distant_contract {
66                        contract
67                    } else {
68                        return (
69                            SystemResult::Err(SystemError::NoSuchContract {
70                                addr: contract_addr.to_string(),
71                            }),
72                            GasInfo::with_externally_used(GAS_COST_CONTRACT_INFO),
73                        );
74                    }
75                };
76                let response =
77                    ContractInfoResponse::new(data.code_id, data.creator, data.admin, false, None);
78                (
79                    SystemResult::Ok(to_json_binary(&response).into()),
80                    GasInfo::with_externally_used(GAS_COST_CONTRACT_INFO),
81                )
82            }
83            WasmQuery::Raw { contract_addr, key } => {
84                // We first try to load that information locally
85                let mut total_key =
86                    get_full_contract_storage_namespace(&Addr::unchecked(contract_addr)).to_vec();
87                total_key.extend_from_slice(key);
88
89                if let Some(value) = self
90                    .fork_state
91                    .querier_storage
92                    .wasm
93                    .storage
94                    .iter()
95                    .find(|e| e.0 == total_key)
96                {
97                    (
98                        SystemResult::Ok(ContractResult::Ok(value.1.clone().into())),
99                        GasInfo::with_externally_used(GAS_COST_RAW_COSMWASM_QUERY),
100                    )
101                } else {
102                    (
103                        SystemResult::Ok(
104                            WasmRemoteQuerier::raw_query(
105                                remote,
106                                &Addr::unchecked(contract_addr),
107                                key.clone(),
108                            )
109                            .map(Into::into)
110                            .into(),
111                        ),
112                        GasInfo::with_externally_used(GAS_COST_RAW_COSMWASM_QUERY),
113                    )
114                }
115            }
116            WasmQuery::Smart { contract_addr, msg } => {
117                let addr = Addr::unchecked(contract_addr);
118
119                let mut storage = MockStorage::default();
120                // Set the storage
121                for (key, value) in self
122                    .fork_state
123                    .querier_storage
124                    .wasm
125                    .get_contract_storage(&addr)
126                {
127                    storage.set(&key, &value);
128                }
129
130                let deps = OwnedDeps {
131                    storage,
132                    api: MockApiBech32::new(&self.fork_state.remote.pub_address_prefix),
133                    querier: MockQuerier::new(self.fork_state.clone()),
134                    custom_query_type: PhantomData::<QueryC>,
135                };
136                let mut env = self.fork_state.local_state.env.clone();
137                env.contract = ContractInfo {
138                    address: addr.clone(),
139                };
140
141                // First we get the code id corresponding to the contract
142                let code_id = if let Some(local_contract) = self
143                    .fork_state
144                    .querier_storage
145                    .wasm
146                    .contracts
147                    .get(contract_addr)
148                {
149                    local_contract.code_id
150                } else {
151                    let wasm_querier = CosmWasm::new_sync(remote.channel.clone(), &remote.rt);
152
153                    let code_info = remote
154                        .rt
155                        .block_on(wasm_querier._contract_info(&addr))
156                        .unwrap();
157
158                    code_info.code_id
159                };
160
161                // Then, we get the corresponding wasm contract
162
163                // If the contract data is already defined in our storage, we load it from there
164                let result = if let Some(code) = self
165                    .fork_state
166                    .querier_storage
167                    .wasm
168                    .codes
169                    .get(&(code_id as usize))
170                {
171                    // Local Wasm Contract case
172                    <WasmContract as Contract<ExecC, QueryC>>::query(
173                        code,
174                        deps.as_ref(),
175                        env,
176                        msg.to_vec(),
177                        self.fork_state.clone(),
178                    )
179                } else if let Some(local_contract) = self
180                    .fork_state
181                    .local_state
182                    .contracts
183                    .get(&(code_id as usize))
184                {
185                    // Local Rust Contract case
186                    unsafe {
187                        local_contract.as_ref().unwrap().query(
188                            deps.as_ref(),
189                            env,
190                            msg.to_vec(),
191                            self.fork_state.clone(),
192                        )
193                    }
194                } else {
195                    // Distant Registered Contract case
196                    // TODO, this should be part of the cache as well
197                    // However, it's not really possible to register that data inside the App, because this is deep in the execution layer
198                    <WasmContract as Contract<ExecC, QueryC>>::query(
199                        &WasmContract::new_distant_code_id(code_id, remote.clone()),
200                        deps.as_ref(),
201                        env,
202                        msg.to_vec(),
203                        self.fork_state.clone(),
204                    )
205                };
206
207                let result = match result {
208                    Err(e) => {
209                        return (
210                            SystemResult::Err(SystemError::InvalidRequest {
211                                error: format!("Error querying a contract: {}", e),
212                                request: msg.clone(),
213                            }),
214                            GasInfo::with_externally_used(0),
215                        )
216                    }
217                    Ok(result) => result,
218                };
219
220                (
221                    SystemResult::Ok(ContractResult::Ok(result)),
222                    GasInfo::with_externally_used(GAS_COST_ALL_QUERIES),
223                )
224            }
225            WasmQuery::CodeInfo { code_id } => {
226                let code_data = self
227                    .fork_state
228                    .querier_storage
229                    .wasm
230                    .code_data
231                    .get(&(*code_id as usize));
232                let res = if let Some(code_data) = code_data {
233                    cosmwasm_std::CodeInfoResponse::new(
234                        *code_id,
235                        code_data.creator.clone(),
236                        code_data.checksum,
237                    )
238                } else {
239                    let maybe_code_info =
240                        WasmRemoteQuerier::code_info(self.fork_state.remote.clone(), *code_id);
241
242                    if let Ok(code_info) = maybe_code_info {
243                        code_info
244                    } else {
245                        return (
246                            SystemResult::Err(SystemError::NoSuchCode { code_id: *code_id }),
247                            GasInfo::with_externally_used(GAS_COST_CONTRACT_INFO),
248                        );
249                    }
250                };
251                (
252                    SystemResult::Ok(to_json_binary(&res).into()),
253                    GasInfo::with_externally_used(GAS_COST_CONTRACT_INFO),
254                )
255            }
256            _ => unimplemented!(),
257        }
258    }
259}