1use crate::wasm_emulation::api::RealApi;
2use crate::wasm_emulation::channel::RemoteChannel;
3use crate::wasm_emulation::input::QuerierStorage;
4use cosmwasm_std::CustomMsg;
5use cw_storage_plus::Item;
6
7use crate::bank::{Bank, BankKeeper, BankSudo};
8use crate::error::{bail, AnyResult};
9use crate::executor::{AppResponse, Executor};
10use crate::gov::Gov;
11use crate::ibc::Ibc;
12use crate::module::{FailingModule, Module};
13use crate::staking::{Distribution, DistributionKeeper, StakeKeeper, Staking, StakingSudo};
14use crate::transactions::transactional;
15use crate::wasm::{ContractData, Wasm, WasmKeeper, WasmSudo};
16use crate::{AppBuilder, Contract, GovFailingModule, IbcFailingModule};
17use cosmwasm_std::testing::{MockApi, MockStorage};
18use cosmwasm_std::{
19 from_json, to_json_binary, Addr, Api, Binary, BlockInfo, ContractResult, CosmosMsg,
20 CustomQuery, Empty, Querier, QuerierResult, QuerierWrapper, QueryRequest, Record, Storage,
21 SystemError, SystemResult,
22};
23use schemars::JsonSchema;
24use serde::{de::DeserializeOwned, Serialize};
25use std::fmt::Debug;
26use std::marker::PhantomData;
27
28const ADDRESSES: Item<Vec<Addr>> = Item::new("addresses");
29
30pub fn next_block(block: &mut BlockInfo) {
31 block.time = block.time.plus_seconds(5);
32 block.height += 1;
33}
34
35pub type BasicApp<ExecC = Empty, QueryC = Empty> = App<
37 BankKeeper,
38 MockApi,
39 MockStorage,
40 FailingModule<ExecC, QueryC, Empty>,
41 WasmKeeper<ExecC, QueryC>,
42 StakeKeeper,
43 DistributionKeeper,
44 IbcFailingModule,
45 GovFailingModule,
46>;
47
48#[derive(Clone)]
52pub struct App<
53 Bank = BankKeeper,
54 Api = MockApi,
55 Storage = MockStorage,
56 Custom = FailingModule<Empty, Empty, Empty>,
57 Wasm = WasmKeeper<Empty, Empty>,
58 Staking = StakeKeeper,
59 Distr = DistributionKeeper,
60 Ibc = IbcFailingModule,
61 Gov = GovFailingModule,
62> {
63 pub(crate) router: Router<Bank, Custom, Wasm, Staking, Distr, Ibc, Gov>,
64 pub(crate) api: Api,
65 pub(crate) storage: Storage,
66 pub(crate) block: BlockInfo,
67 pub(crate) remote: RemoteChannel,
68}
69
70impl BasicApp {
71 pub fn new<F>(remote: RemoteChannel, init_fn: F) -> AnyResult<Self>
73 where
74 F: FnOnce(
75 &mut Router<
76 BankKeeper,
77 FailingModule<Empty, Empty, Empty>,
78 WasmKeeper<Empty, Empty>,
79 StakeKeeper,
80 DistributionKeeper,
81 IbcFailingModule,
82 GovFailingModule,
83 >,
84 &dyn Api,
85 &mut dyn Storage,
86 ),
87 {
88 AppBuilder::new().with_remote(remote).build(init_fn)
89 }
90}
91
92pub fn custom_app<ExecC, QueryC, F>(
95 remote: RemoteChannel,
96 init_fn: F,
97) -> AnyResult<BasicApp<ExecC, QueryC>>
98where
99 ExecC: CustomMsg + DeserializeOwned + 'static,
100 QueryC: Debug + CustomQuery + DeserializeOwned + 'static,
101 F: FnOnce(
102 &mut Router<
103 BankKeeper,
104 FailingModule<ExecC, QueryC, Empty>,
105 WasmKeeper<ExecC, QueryC>,
106 StakeKeeper,
107 DistributionKeeper,
108 IbcFailingModule,
109 GovFailingModule,
110 >,
111 &dyn Api,
112 &mut dyn Storage,
113 ),
114{
115 AppBuilder::new_custom().with_remote(remote).build(init_fn)
116}
117
118impl<BankT, ApiT, StorageT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT> Querier
119 for App<BankT, ApiT, StorageT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT>
120where
121 CustomT::ExecT: Clone + Debug + PartialEq + JsonSchema + DeserializeOwned + 'static,
122 CustomT::QueryT: CustomQuery + DeserializeOwned + 'static,
123 WasmT: Wasm<CustomT::ExecT, CustomT::QueryT>,
124 BankT: Bank,
125 ApiT: Api,
126 StorageT: Storage,
127 CustomT: Module,
128 StakingT: Staking,
129 DistrT: Distribution,
130 IbcT: Ibc,
131 GovT: Gov,
132{
133 fn raw_query(&self, bin_request: &[u8]) -> QuerierResult {
134 self.router
135 .querier(&self.api, &self.storage, &self.block)
136 .raw_query(bin_request)
137 }
138}
139
140impl<BankT, ApiT, StorageT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT> Executor<CustomT::ExecT>
141 for App<BankT, ApiT, StorageT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT>
142where
143 CustomT::ExecT: Clone + Debug + PartialEq + JsonSchema + DeserializeOwned + 'static,
144 CustomT::QueryT: CustomQuery + DeserializeOwned + 'static,
145 WasmT: Wasm<CustomT::ExecT, CustomT::QueryT>,
146 BankT: Bank,
147 ApiT: Api,
148 StorageT: Storage,
149 CustomT: Module,
150 StakingT: Staking,
151 DistrT: Distribution,
152 IbcT: Ibc,
153 GovT: Gov,
154{
155 fn execute(&mut self, sender: Addr, msg: CosmosMsg<CustomT::ExecT>) -> AnyResult<AppResponse> {
156 let mut all = self.execute_multi(sender, vec![msg])?;
157 let res = all.pop().unwrap();
158 Ok(res)
159 }
160}
161
162impl<BankT, ApiT, StorageT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT>
163 App<BankT, ApiT, StorageT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT>
164where
165 WasmT: Wasm<CustomT::ExecT, CustomT::QueryT>,
166 BankT: Bank,
167 ApiT: Api,
168 StorageT: Storage,
169 CustomT: Module,
170 StakingT: Staking,
171 DistrT: Distribution,
172 IbcT: Ibc,
173 GovT: Gov,
174 CustomT::QueryT: CustomQuery,
175{
176 pub fn router(&self) -> &Router<BankT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT> {
178 &self.router
179 }
180
181 pub fn api(&self) -> &ApiT {
183 &self.api
184 }
185
186 pub fn storage(&self) -> &StorageT {
188 &self.storage
189 }
190
191 pub fn storage_mut(&mut self) -> &mut StorageT {
193 &mut self.storage
194 }
195
196 pub fn init_modules<F, T>(&mut self, init_fn: F) -> T
197 where
198 F: FnOnce(
199 &mut Router<BankT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT>,
200 &dyn Api,
201 &mut dyn Storage,
202 ) -> T,
203 {
204 init_fn(&mut self.router, &self.api, &mut self.storage)
205 }
206
207 pub fn read_module<F, T>(&self, query_fn: F) -> T
208 where
209 F: FnOnce(
210 &Router<BankT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT>,
211 &dyn Api,
212 &dyn Storage,
213 ) -> T,
214 {
215 query_fn(&self.router, &self.api, &self.storage)
216 }
217}
218
219impl<BankT, ApiT, StorageT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT>
222 App<BankT, ApiT, StorageT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT>
223where
224 BankT: Bank,
225 ApiT: Api,
226 StorageT: Storage,
227 CustomT: Module,
228 WasmT: Wasm<CustomT::ExecT, CustomT::QueryT>,
229 StakingT: Staking,
230 DistrT: Distribution,
231 IbcT: Ibc,
232 GovT: Gov,
233 CustomT::ExecT: CustomMsg + DeserializeOwned + 'static,
234 CustomT::QueryT: CustomQuery + DeserializeOwned + 'static,
235{
236 pub fn store_wasm_code(&mut self, code: Vec<u8>) -> u64 {
240 self.init_modules(|router, _, _| {
241 router
242 .wasm
243 .store_wasm_code(Addr::unchecked("code-creator"), code)
244 })
245 }
246
247 pub fn store_code(&mut self, code: Box<dyn Contract<CustomT::ExecT, CustomT::QueryT>>) -> u64 {
250 self.init_modules(|router, _, _| {
251 router
252 .wasm
253 .store_code(Addr::unchecked("code-creator"), code)
254 })
255 }
256
257 pub fn store_wasm_code_with_creator(&mut self, creator: Addr, code: Vec<u8>) -> u64 {
260 self.init_modules(|router, _, _| router.wasm.store_wasm_code(creator, code))
261 }
262
263 pub fn store_code_with_creator(
266 &mut self,
267 creator: Addr,
268 code: Box<dyn Contract<CustomT::ExecT, CustomT::QueryT>>,
269 ) -> u64 {
270 self.init_modules(|router, _, _| router.wasm.store_code(creator, code))
271 }
272
273 pub fn contract_data(&self, address: &Addr) -> AnyResult<ContractData> {
275 self.read_module(|router, _, storage| router.wasm.contract_data(storage, address))
276 }
277
278 pub fn dump_wasm_raw(&self, address: &Addr) -> Vec<Record> {
280 self.read_module(|router, _, storage| router.wasm.dump_wasm_raw(storage, address))
281 }
282}
283
284impl<BankT, ApiT, StorageT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT>
285 App<BankT, ApiT, StorageT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT>
286where
287 CustomT::ExecT: Debug + PartialEq + Clone + JsonSchema + DeserializeOwned + 'static,
288 CustomT::QueryT: CustomQuery + DeserializeOwned + 'static,
289 WasmT: Wasm<CustomT::ExecT, CustomT::QueryT>,
290 BankT: Bank,
291 ApiT: Api,
292 StorageT: Storage,
293 CustomT: Module,
294 StakingT: Staking,
295 DistrT: Distribution,
296 IbcT: Ibc,
297 GovT: Gov,
298{
299 pub fn set_block(&mut self, block: BlockInfo) {
300 self.router
301 .staking
302 .process_queue(&self.api, &mut self.storage, &self.router, &self.block)
303 .unwrap();
304 self.block = block;
305 }
306
307 pub fn update_block<F: Fn(&mut BlockInfo)>(&mut self, action: F) {
309 self.router
310 .staking
311 .process_queue(&self.api, &mut self.storage, &self.router, &self.block)
312 .unwrap();
313 action(&mut self.block);
314 }
315
316 pub fn block_info(&self) -> BlockInfo {
318 self.block.clone()
319 }
320
321 pub fn next_address(&mut self) -> Addr {
323 let Self {
324 storage, remote, ..
325 } = self;
326
327 let mut addresses = ADDRESSES.may_load(storage).unwrap().unwrap_or_default();
328
329 let new_address =
330 RealApi::new(&remote.pub_address_prefix.clone()).next_address(addresses.len());
331 addresses.push(new_address.clone());
332 ADDRESSES.save(storage, &addresses).unwrap();
333
334 new_address
335 }
336
337 pub fn wrap(&self) -> QuerierWrapper<CustomT::QueryT> {
340 QuerierWrapper::new(self)
341 }
342
343 pub fn get_querier_storage(&self) -> AnyResult<QuerierStorage> {
344 let wasm = self.router.wasm.query_all(&self.storage)?;
346 let bank = self.router.bank.query_all(&self.storage)?;
347 Ok(QuerierStorage { wasm, bank })
348 }
349
350 pub fn execute_multi(
354 &mut self,
355 sender: Addr,
356 msgs: Vec<CosmosMsg<CustomT::ExecT>>,
357 ) -> AnyResult<Vec<AppResponse>> {
358 let Self {
363 block,
364 router,
365 api,
366 storage,
367 ..
368 } = self;
369
370 transactional(&mut *storage, |write_cache, _| {
371 msgs.into_iter()
372 .map(|msg| router.execute(&*api, write_cache, block, sender.clone(), msg))
373 .collect()
374 })
375 }
376
377 pub fn wasm_sudo<T: Serialize, U: Into<Addr>>(
381 &mut self,
382 contract_addr: U,
383 msg: &T,
384 ) -> AnyResult<AppResponse> {
385 let msg = to_json_binary(msg)?;
386
387 let Self {
388 block,
389 router,
390 api,
391 storage,
392 ..
393 } = self;
394
395 transactional(&mut *storage, |write_cache, _| {
396 router
397 .wasm
398 .sudo(&*api, contract_addr.into(), write_cache, router, block, msg)
399 })
400 }
401
402 pub fn sudo(&mut self, msg: SudoMsg) -> AnyResult<AppResponse> {
406 let Self {
410 block,
411 router,
412 api,
413 storage,
414 ..
415 } = self;
416
417 transactional(&mut *storage, |write_cache, _| {
418 router.sudo(&*api, write_cache, block, msg)
419 })
420 }
421}
422
423#[derive(Clone)]
424pub struct Router<Bank, Custom, Wasm, Staking, Distr, Ibc, Gov> {
425 pub(crate) wasm: Wasm,
428 pub bank: Bank,
430 pub custom: Custom,
431 pub staking: Staking,
432 pub distribution: Distr,
433 pub ibc: Ibc,
434 pub gov: Gov,
435}
436
437impl<BankT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT>
438 Router<BankT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT>
439where
440 CustomT::ExecT: Clone + Debug + PartialEq + JsonSchema + DeserializeOwned + 'static,
441 CustomT::QueryT: CustomQuery + DeserializeOwned + 'static,
442 CustomT: Module,
443 WasmT: Wasm<CustomT::ExecT, CustomT::QueryT>,
444 BankT: Bank,
445 StakingT: Staking,
446 DistrT: Distribution,
447 IbcT: Ibc,
448 GovT: Gov,
449{
450 pub fn querier<'a>(
451 &'a self,
452 api: &'a dyn Api,
453 storage: &'a dyn Storage,
454 block_info: &'a BlockInfo,
455 ) -> RouterQuerier<'a, CustomT::ExecT, CustomT::QueryT> {
456 RouterQuerier {
457 router: self,
458 api,
459 storage,
460 block_info,
461 }
462 }
463}
464
465pub enum SudoMsg {
468 Bank(BankSudo),
469 Custom(Empty),
470 Staking(StakingSudo),
471 Wasm(WasmSudo),
472}
473
474impl From<WasmSudo> for SudoMsg {
475 fn from(wasm: WasmSudo) -> Self {
476 SudoMsg::Wasm(wasm)
477 }
478}
479
480impl From<BankSudo> for SudoMsg {
481 fn from(bank: BankSudo) -> Self {
482 SudoMsg::Bank(bank)
483 }
484}
485
486impl From<StakingSudo> for SudoMsg {
487 fn from(staking: StakingSudo) -> Self {
488 SudoMsg::Staking(staking)
489 }
490}
491
492pub trait CosmosRouter {
493 type ExecC;
494 type QueryC: CustomQuery;
495
496 fn execute(
497 &self,
498 api: &dyn Api,
499 storage: &mut dyn Storage,
500 block: &BlockInfo,
501 sender: Addr,
502 msg: CosmosMsg<Self::ExecC>,
503 ) -> AnyResult<AppResponse>;
504
505 fn query(
506 &self,
507 api: &dyn Api,
508 storage: &dyn Storage,
509 block: &BlockInfo,
510 request: QueryRequest<Self::QueryC>,
511 ) -> AnyResult<Binary>;
512
513 fn sudo(
514 &self,
515 api: &dyn Api,
516 storage: &mut dyn Storage,
517 block: &BlockInfo,
518 msg: SudoMsg,
519 ) -> AnyResult<AppResponse>;
520
521 fn get_querier_storage(&self, storage: &dyn Storage) -> AnyResult<QuerierStorage>;
522}
523
524impl<BankT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT> CosmosRouter
525 for Router<BankT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT>
526where
527 CustomT::ExecT: Debug + Clone + PartialEq + JsonSchema + DeserializeOwned + 'static,
528 CustomT::QueryT: CustomQuery + DeserializeOwned + 'static,
529 CustomT: Module,
530 WasmT: Wasm<CustomT::ExecT, CustomT::QueryT>,
531 BankT: Bank,
532 StakingT: Staking,
533 DistrT: Distribution,
534 IbcT: Ibc,
535 GovT: Gov,
536{
537 type ExecC = CustomT::ExecT;
538 type QueryC = CustomT::QueryT;
539
540 fn execute(
541 &self,
542 api: &dyn Api,
543 storage: &mut dyn Storage,
544 block: &BlockInfo,
545 sender: Addr,
546 msg: CosmosMsg<Self::ExecC>,
547 ) -> AnyResult<AppResponse> {
548 match msg {
549 CosmosMsg::Wasm(msg) => self.wasm.execute(api, storage, self, block, sender, msg),
550 CosmosMsg::Bank(msg) => self.bank.execute(api, storage, self, block, sender, msg),
551 CosmosMsg::Custom(msg) => self.custom.execute(api, storage, self, block, sender, msg),
552 CosmosMsg::Staking(msg) => self.staking.execute(api, storage, self, block, sender, msg),
553 CosmosMsg::Distribution(msg) => self
554 .distribution
555 .execute(api, storage, self, block, sender, msg),
556 CosmosMsg::Ibc(msg) => self.ibc.execute(api, storage, self, block, sender, msg),
557 CosmosMsg::Gov(msg) => self.gov.execute(api, storage, self, block, sender, msg),
558 _ => bail!("Cannot execute {:?}", msg),
559 }
560 }
561
562 fn query(
566 &self,
567 api: &dyn Api,
568 storage: &dyn Storage,
569 block: &BlockInfo,
570 request: QueryRequest<Self::QueryC>,
571 ) -> AnyResult<Binary> {
572 let querier = self.querier(api, storage, block);
573 match request {
574 QueryRequest::Wasm(req) => self.wasm.query(api, storage, self, &querier, block, req),
575 QueryRequest::Bank(req) => self.bank.query(api, storage, &querier, block, req),
576 QueryRequest::Custom(req) => self.custom.query(api, storage, &querier, block, req),
577 QueryRequest::Staking(req) => self.staking.query(api, storage, &querier, block, req),
578 QueryRequest::Ibc(req) => self.ibc.query(api, storage, &querier, block, req),
579 _ => unimplemented!(),
580 }
581 }
582
583 fn sudo(
584 &self,
585 api: &dyn Api,
586 storage: &mut dyn Storage,
587 block: &BlockInfo,
588 msg: SudoMsg,
589 ) -> AnyResult<AppResponse> {
590 match msg {
591 SudoMsg::Wasm(msg) => {
592 self.wasm
593 .sudo(api, msg.contract_addr, storage, self, block, msg.msg)
594 }
595 SudoMsg::Bank(msg) => self.bank.sudo(api, storage, self, block, msg),
596 SudoMsg::Staking(msg) => self.staking.sudo(api, storage, self, block, msg),
597 SudoMsg::Custom(_) => unimplemented!(),
598 }
599 }
600
601 fn get_querier_storage(&self, storage: &dyn Storage) -> AnyResult<QuerierStorage> {
602 let wasm = self.wasm.query_all(storage)?;
604 let bank = self.bank.query_all(storage)?;
605 Ok(QuerierStorage { wasm, bank })
606 }
607}
608
609pub struct MockRouter<ExecC, QueryC>(PhantomData<(ExecC, QueryC)>);
610
611impl Default for MockRouter<Empty, Empty> {
612 fn default() -> Self {
613 Self::new()
614 }
615}
616
617impl<ExecC, QueryC> MockRouter<ExecC, QueryC> {
618 pub fn new() -> Self
619 where
620 QueryC: CustomQuery,
621 {
622 MockRouter(PhantomData)
623 }
624}
625
626impl<ExecC, QueryC> CosmosRouter for MockRouter<ExecC, QueryC>
627where
628 QueryC: CustomQuery,
629{
630 type ExecC = ExecC;
631 type QueryC = QueryC;
632
633 fn execute(
634 &self,
635 _api: &dyn Api,
636 _storage: &mut dyn Storage,
637 _block: &BlockInfo,
638 _sender: Addr,
639 _msg: CosmosMsg<Self::ExecC>,
640 ) -> AnyResult<AppResponse> {
641 panic!("Cannot execute MockRouters");
642 }
643
644 fn query(
645 &self,
646 _api: &dyn Api,
647 _storage: &dyn Storage,
648 _block: &BlockInfo,
649 _request: QueryRequest<Self::QueryC>,
650 ) -> AnyResult<Binary> {
651 panic!("Cannot query MockRouters");
652 }
653
654 fn sudo(
655 &self,
656 _api: &dyn Api,
657 _storage: &mut dyn Storage,
658 _block: &BlockInfo,
659 _msg: SudoMsg,
660 ) -> AnyResult<AppResponse> {
661 panic!("Cannot sudo MockRouters");
662 }
663
664 fn get_querier_storage(&self, _storage: &dyn Storage) -> AnyResult<QuerierStorage> {
665 Ok(QuerierStorage::default())
666 }
667}
668
669pub struct RouterQuerier<'a, ExecC, QueryC> {
670 router: &'a dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
671 api: &'a dyn Api,
672 storage: &'a dyn Storage,
673 block_info: &'a BlockInfo,
674}
675
676impl<'a, ExecC, QueryC> RouterQuerier<'a, ExecC, QueryC> {
677 pub fn new(
678 router: &'a dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
679 api: &'a dyn Api,
680 storage: &'a dyn Storage,
681 block_info: &'a BlockInfo,
682 ) -> Self {
683 Self {
684 router,
685 api,
686 storage,
687 block_info,
688 }
689 }
690}
691
692impl<'a, ExecC, QueryC> Querier for RouterQuerier<'a, ExecC, QueryC>
693where
694 ExecC: Clone + Debug + PartialEq + JsonSchema + DeserializeOwned + 'static,
695 QueryC: CustomQuery + DeserializeOwned + 'static,
696{
697 fn raw_query(&self, bin_request: &[u8]) -> QuerierResult {
698 let request: QueryRequest<QueryC> = match from_json(bin_request) {
699 Ok(v) => v,
700 Err(e) => {
701 return SystemResult::Err(SystemError::InvalidRequest {
702 error: format!("Parsing query request: {}", e),
703 request: bin_request.into(),
704 })
705 }
706 };
707 let contract_result: ContractResult<Binary> = self
708 .router
709 .query(self.api, self.storage, self.block_info, request)
710 .into();
711 SystemResult::Ok(contract_result)
712 }
713}