use crate::bank::{Bank, BankKeeper, BankSudo};
use crate::contracts::Contract;
use crate::error::{bail, AnyResult};
use crate::executor::{AppResponse, Executor};
use crate::gov::Gov;
use crate::ibc::Ibc;
use crate::module::{FailingModule, Module};
use crate::staking::{Distribution, DistributionKeeper, StakeKeeper, Staking, StakingSudo};
use crate::stargate::{Stargate, StargateFailingModule};
use crate::transactions::transactional;
use crate::wasm::{ContractData, Wasm, WasmKeeper, WasmSudo};
use crate::{AppBuilder, GovFailingModule, IbcFailingModule};
use cosmwasm_std::testing::{MockApi, MockStorage};
use cosmwasm_std::{
from_json, to_json_binary, Addr, Api, Binary, BlockInfo, ContractResult, CosmosMsg, CustomMsg,
CustomQuery, Empty, Querier, QuerierResult, QuerierWrapper, QueryRequest, Record, Storage,
SystemError, SystemResult,
};
use serde::{de::DeserializeOwned, Serialize};
use std::fmt::Debug;
use std::marker::PhantomData;
pub fn next_block(block: &mut BlockInfo) {
block.time = block.time.plus_seconds(5);
block.height += 1;
}
pub type BasicApp<ExecC = Empty, QueryC = Empty> = App<
BankKeeper,
MockApi,
MockStorage,
FailingModule<ExecC, QueryC, Empty>,
WasmKeeper<ExecC, QueryC>,
StakeKeeper,
DistributionKeeper,
IbcFailingModule,
GovFailingModule,
StargateFailingModule,
>;
#[derive(Clone)]
pub struct App<
Bank = BankKeeper,
Api = MockApi,
Storage = MockStorage,
Custom = FailingModule<Empty, Empty, Empty>,
Wasm = WasmKeeper<Empty, Empty>,
Staking = StakeKeeper,
Distr = DistributionKeeper,
Ibc = IbcFailingModule,
Gov = GovFailingModule,
Stargate = StargateFailingModule,
> {
pub(crate) router: Router<Bank, Custom, Wasm, Staking, Distr, Ibc, Gov, Stargate>,
pub(crate) api: Api,
pub(crate) storage: Storage,
pub(crate) block: BlockInfo,
}
pub fn no_init<BankT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT, StargateT>(
router: &mut Router<BankT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT, StargateT>,
api: &dyn Api,
storage: &mut dyn Storage,
) {
let _ = (router, api, storage);
}
impl Default for BasicApp {
fn default() -> Self {
Self::new(no_init)
}
}
impl BasicApp {
pub fn new<F>(init_fn: F) -> Self
where
F: FnOnce(
&mut Router<
BankKeeper,
FailingModule<Empty, Empty, Empty>,
WasmKeeper<Empty, Empty>,
StakeKeeper,
DistributionKeeper,
IbcFailingModule,
GovFailingModule,
StargateFailingModule,
>,
&dyn Api,
&mut dyn Storage,
),
{
AppBuilder::new().build(init_fn)
}
}
pub fn custom_app<ExecC, QueryC, F>(init_fn: F) -> BasicApp<ExecC, QueryC>
where
ExecC: CustomMsg + DeserializeOwned + 'static,
QueryC: Debug + CustomQuery + DeserializeOwned + 'static,
F: FnOnce(
&mut Router<
BankKeeper,
FailingModule<ExecC, QueryC, Empty>,
WasmKeeper<ExecC, QueryC>,
StakeKeeper,
DistributionKeeper,
IbcFailingModule,
GovFailingModule,
StargateFailingModule,
>,
&dyn Api,
&mut dyn Storage,
),
{
AppBuilder::new_custom().build(init_fn)
}
impl<BankT, ApiT, StorageT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT, StargateT> Querier
for App<BankT, ApiT, StorageT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT, StargateT>
where
CustomT::ExecT: CustomMsg + DeserializeOwned + 'static,
CustomT::QueryT: CustomQuery + DeserializeOwned + 'static,
WasmT: Wasm<CustomT::ExecT, CustomT::QueryT>,
BankT: Bank,
ApiT: Api,
StorageT: Storage,
CustomT: Module,
StakingT: Staking,
DistrT: Distribution,
IbcT: Ibc,
GovT: Gov,
StargateT: Stargate,
{
fn raw_query(&self, bin_request: &[u8]) -> QuerierResult {
self.router
.querier(&self.api, &self.storage, &self.block)
.raw_query(bin_request)
}
}
impl<BankT, ApiT, StorageT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT, StargateT>
Executor<CustomT::ExecT>
for App<BankT, ApiT, StorageT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT, StargateT>
where
CustomT::ExecT: CustomMsg + DeserializeOwned + 'static,
CustomT::QueryT: CustomQuery + DeserializeOwned + 'static,
WasmT: Wasm<CustomT::ExecT, CustomT::QueryT>,
BankT: Bank,
ApiT: Api,
StorageT: Storage,
CustomT: Module,
StakingT: Staking,
DistrT: Distribution,
IbcT: Ibc,
GovT: Gov,
StargateT: Stargate,
{
fn execute(&mut self, sender: Addr, msg: CosmosMsg<CustomT::ExecT>) -> AnyResult<AppResponse> {
let mut all = self.execute_multi(sender, vec![msg])?;
let res = all.pop().unwrap();
Ok(res)
}
}
impl<BankT, ApiT, StorageT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT, StargateT>
App<BankT, ApiT, StorageT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT, StargateT>
where
WasmT: Wasm<CustomT::ExecT, CustomT::QueryT>,
BankT: Bank,
ApiT: Api,
StorageT: Storage,
CustomT: Module,
StakingT: Staking,
DistrT: Distribution,
IbcT: Ibc,
GovT: Gov,
StargateT: Stargate,
{
pub fn router(
&self,
) -> &Router<BankT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT, StargateT> {
&self.router
}
pub fn api(&self) -> &ApiT {
&self.api
}
pub fn storage(&self) -> &StorageT {
&self.storage
}
pub fn storage_mut(&mut self) -> &mut StorageT {
&mut self.storage
}
pub fn init_modules<F, T>(&mut self, init_fn: F) -> T
where
F: FnOnce(
&mut Router<BankT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT, StargateT>,
&dyn Api,
&mut dyn Storage,
) -> T,
{
init_fn(&mut self.router, &self.api, &mut self.storage)
}
pub fn read_module<F, T>(&self, query_fn: F) -> T
where
F: FnOnce(
&Router<BankT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT, StargateT>,
&dyn Api,
&dyn Storage,
) -> T,
{
query_fn(&self.router, &self.api, &self.storage)
}
}
impl<BankT, ApiT, StorageT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT, StargateT>
App<BankT, ApiT, StorageT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT, StargateT>
where
BankT: Bank,
ApiT: Api,
StorageT: Storage,
CustomT: Module,
WasmT: Wasm<CustomT::ExecT, CustomT::QueryT>,
StakingT: Staking,
DistrT: Distribution,
IbcT: Ibc,
GovT: Gov,
StargateT: Stargate,
CustomT::ExecT: CustomMsg + DeserializeOwned + 'static,
CustomT::QueryT: CustomQuery + DeserializeOwned + 'static,
{
pub fn store_code(&mut self, code: Box<dyn Contract<CustomT::ExecT, CustomT::QueryT>>) -> u64 {
self.init_modules(|router, _, _| {
router
.wasm
.store_code(MockApi::default().addr_make("creator"), code)
})
}
pub fn store_code_with_creator(
&mut self,
creator: Addr,
code: Box<dyn Contract<CustomT::ExecT, CustomT::QueryT>>,
) -> u64 {
self.init_modules(|router, _, _| router.wasm.store_code(creator, code))
}
pub fn store_code_with_id(
&mut self,
creator: Addr,
code_id: u64,
code: Box<dyn Contract<CustomT::ExecT, CustomT::QueryT>>,
) -> AnyResult<u64> {
self.init_modules(|router, _, _| router.wasm.store_code_with_id(creator, code_id, code))
}
pub fn duplicate_code(&mut self, code_id: u64) -> AnyResult<u64> {
self.init_modules(|router, _, _| router.wasm.duplicate_code(code_id))
}
pub fn contract_data(&self, address: &Addr) -> AnyResult<ContractData> {
self.read_module(|router, _, storage| router.wasm.contract_data(storage, address))
}
pub fn dump_wasm_raw(&self, address: &Addr) -> Vec<Record> {
self.read_module(|router, _, storage| router.wasm.dump_wasm_raw(storage, address))
}
}
impl<BankT, ApiT, StorageT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT, StargateT>
App<BankT, ApiT, StorageT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT, StargateT>
where
CustomT::ExecT: CustomMsg + DeserializeOwned + 'static,
CustomT::QueryT: CustomQuery + DeserializeOwned + 'static,
WasmT: Wasm<CustomT::ExecT, CustomT::QueryT>,
BankT: Bank,
ApiT: Api,
StorageT: Storage,
CustomT: Module,
StakingT: Staking,
DistrT: Distribution,
IbcT: Ibc,
GovT: Gov,
StargateT: Stargate,
{
pub fn set_block(&mut self, block: BlockInfo) {
self.router
.staking
.process_queue(&self.api, &mut self.storage, &self.router, &self.block)
.unwrap();
self.block = block;
}
pub fn update_block<F: Fn(&mut BlockInfo)>(&mut self, action: F) {
self.router
.staking
.process_queue(&self.api, &mut self.storage, &self.router, &self.block)
.unwrap();
action(&mut self.block);
}
pub fn block_info(&self) -> BlockInfo {
self.block.clone()
}
pub fn wrap(&self) -> QuerierWrapper<CustomT::QueryT> {
QuerierWrapper::new(self)
}
pub fn execute_multi(
&mut self,
sender: Addr,
msgs: Vec<CosmosMsg<CustomT::ExecT>>,
) -> AnyResult<Vec<AppResponse>> {
let Self {
block,
router,
api,
storage,
} = self;
transactional(&mut *storage, |write_cache, _| {
msgs.into_iter()
.map(|msg| router.execute(&*api, write_cache, block, sender.clone(), msg))
.collect()
})
}
pub fn wasm_sudo<T: Serialize, U: Into<Addr>>(
&mut self,
contract_addr: U,
msg: &T,
) -> AnyResult<AppResponse> {
let msg = WasmSudo {
contract_addr: contract_addr.into(),
message: to_json_binary(msg)?,
};
let Self {
block,
router,
api,
storage,
} = self;
transactional(&mut *storage, |write_cache, _| {
router.wasm.sudo(&*api, write_cache, router, block, msg)
})
}
pub fn sudo(&mut self, msg: SudoMsg) -> AnyResult<AppResponse> {
let Self {
block,
router,
api,
storage,
} = self;
transactional(&mut *storage, |write_cache, _| {
router.sudo(&*api, write_cache, block, msg)
})
}
}
#[derive(Clone)]
pub struct Router<Bank, Custom, Wasm, Staking, Distr, Ibc, Gov, Stargate> {
pub(crate) wasm: Wasm,
pub bank: Bank,
pub custom: Custom,
pub staking: Staking,
pub distribution: Distr,
pub ibc: Ibc,
pub gov: Gov,
pub stargate: Stargate,
}
impl<BankT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT, StargateT>
Router<BankT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT, StargateT>
where
CustomT::ExecT: CustomMsg + DeserializeOwned + 'static,
CustomT::QueryT: CustomQuery + DeserializeOwned + 'static,
CustomT: Module,
WasmT: Wasm<CustomT::ExecT, CustomT::QueryT>,
BankT: Bank,
StakingT: Staking,
DistrT: Distribution,
IbcT: Ibc,
GovT: Gov,
StargateT: Stargate,
{
pub fn querier<'a>(
&'a self,
api: &'a dyn Api,
storage: &'a dyn Storage,
block_info: &'a BlockInfo,
) -> RouterQuerier<'a, CustomT::ExecT, CustomT::QueryT> {
RouterQuerier {
router: self,
api,
storage,
block_info,
}
}
}
pub enum SudoMsg {
Bank(BankSudo),
Custom(Empty),
Staking(StakingSudo),
Wasm(WasmSudo),
}
impl From<WasmSudo> for SudoMsg {
fn from(wasm: WasmSudo) -> Self {
SudoMsg::Wasm(wasm)
}
}
impl From<BankSudo> for SudoMsg {
fn from(bank: BankSudo) -> Self {
SudoMsg::Bank(bank)
}
}
impl From<StakingSudo> for SudoMsg {
fn from(staking: StakingSudo) -> Self {
SudoMsg::Staking(staking)
}
}
pub trait CosmosRouter {
type ExecC: CustomMsg;
type QueryC: CustomQuery;
fn execute(
&self,
api: &dyn Api,
storage: &mut dyn Storage,
block: &BlockInfo,
sender: Addr,
msg: CosmosMsg<Self::ExecC>,
) -> AnyResult<AppResponse>;
fn query(
&self,
api: &dyn Api,
storage: &dyn Storage,
block: &BlockInfo,
request: QueryRequest<Self::QueryC>,
) -> AnyResult<Binary>;
fn sudo(
&self,
api: &dyn Api,
storage: &mut dyn Storage,
block: &BlockInfo,
msg: SudoMsg,
) -> AnyResult<AppResponse>;
}
impl<BankT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT, StargateT> CosmosRouter
for Router<BankT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT, StargateT>
where
CustomT::ExecT: CustomMsg + DeserializeOwned + 'static,
CustomT::QueryT: CustomQuery + DeserializeOwned + 'static,
CustomT: Module,
WasmT: Wasm<CustomT::ExecT, CustomT::QueryT>,
BankT: Bank,
StakingT: Staking,
DistrT: Distribution,
IbcT: Ibc,
GovT: Gov,
StargateT: Stargate,
{
type ExecC = CustomT::ExecT;
type QueryC = CustomT::QueryT;
fn execute(
&self,
api: &dyn Api,
storage: &mut dyn Storage,
block: &BlockInfo,
sender: Addr,
msg: CosmosMsg<Self::ExecC>,
) -> AnyResult<AppResponse> {
match msg {
CosmosMsg::Wasm(msg) => self.wasm.execute(api, storage, self, block, sender, msg),
CosmosMsg::Bank(msg) => self.bank.execute(api, storage, self, block, sender, msg),
CosmosMsg::Custom(msg) => self.custom.execute(api, storage, self, block, sender, msg),
CosmosMsg::Staking(msg) => self.staking.execute(api, storage, self, block, sender, msg),
CosmosMsg::Distribution(msg) => self
.distribution
.execute(api, storage, self, block, sender, msg),
CosmosMsg::Ibc(msg) => self.ibc.execute(api, storage, self, block, sender, msg),
CosmosMsg::Gov(msg) => self.gov.execute(api, storage, self, block, sender, msg),
CosmosMsg::Any(msg) => self
.stargate
.execute(api, storage, self, block, sender, msg),
_ => bail!("Cannot execute {:?}", msg),
}
}
fn query(
&self,
api: &dyn Api,
storage: &dyn Storage,
block: &BlockInfo,
request: QueryRequest<Self::QueryC>,
) -> AnyResult<Binary> {
let querier = self.querier(api, storage, block);
match request {
QueryRequest::Wasm(req) => self.wasm.query(api, storage, &querier, block, req),
QueryRequest::Bank(req) => self.bank.query(api, storage, &querier, block, req),
QueryRequest::Custom(req) => self.custom.query(api, storage, &querier, block, req),
QueryRequest::Staking(req) => self.staking.query(api, storage, &querier, block, req),
QueryRequest::Ibc(req) => self.ibc.query(api, storage, &querier, block, req),
QueryRequest::Grpc(req) => self.stargate.query(api, storage, &querier, block, req),
_ => unimplemented!(),
}
}
fn sudo(
&self,
api: &dyn Api,
storage: &mut dyn Storage,
block: &BlockInfo,
msg: SudoMsg,
) -> AnyResult<AppResponse> {
match msg {
SudoMsg::Wasm(msg) => self.wasm.sudo(api, storage, self, block, msg),
SudoMsg::Bank(msg) => self.bank.sudo(api, storage, self, block, msg),
SudoMsg::Staking(msg) => self.staking.sudo(api, storage, self, block, msg),
SudoMsg::Custom(_) => unimplemented!(),
}
}
}
pub struct MockRouter<ExecC, QueryC>(PhantomData<(ExecC, QueryC)>);
impl Default for MockRouter<Empty, Empty> {
fn default() -> Self {
Self::new()
}
}
impl<ExecC, QueryC> MockRouter<ExecC, QueryC> {
pub fn new() -> Self
where
QueryC: CustomQuery,
{
MockRouter(PhantomData)
}
}
impl<ExecC, QueryC> CosmosRouter for MockRouter<ExecC, QueryC>
where
ExecC: CustomMsg,
QueryC: CustomQuery,
{
type ExecC = ExecC;
type QueryC = QueryC;
fn execute(
&self,
_api: &dyn Api,
_storage: &mut dyn Storage,
_block: &BlockInfo,
_sender: Addr,
_msg: CosmosMsg<Self::ExecC>,
) -> AnyResult<AppResponse> {
panic!("Cannot execute MockRouters");
}
fn query(
&self,
_api: &dyn Api,
_storage: &dyn Storage,
_block: &BlockInfo,
_request: QueryRequest<Self::QueryC>,
) -> AnyResult<Binary> {
panic!("Cannot query MockRouters");
}
fn sudo(
&self,
_api: &dyn Api,
_storage: &mut dyn Storage,
_block: &BlockInfo,
_msg: SudoMsg,
) -> AnyResult<AppResponse> {
panic!("Cannot sudo MockRouters");
}
}
pub struct RouterQuerier<'a, ExecC, QueryC> {
router: &'a dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
api: &'a dyn Api,
storage: &'a dyn Storage,
block_info: &'a BlockInfo,
}
impl<'a, ExecC, QueryC> RouterQuerier<'a, ExecC, QueryC> {
pub fn new(
router: &'a dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
api: &'a dyn Api,
storage: &'a dyn Storage,
block_info: &'a BlockInfo,
) -> Self {
Self {
router,
api,
storage,
block_info,
}
}
}
impl<'a, ExecC, QueryC> Querier for RouterQuerier<'a, ExecC, QueryC>
where
ExecC: CustomMsg + DeserializeOwned + 'static,
QueryC: CustomQuery + DeserializeOwned + 'static,
{
fn raw_query(&self, bin_request: &[u8]) -> QuerierResult {
let request: QueryRequest<QueryC> = match from_json(bin_request) {
Ok(v) => v,
Err(e) => {
return SystemResult::Err(SystemError::InvalidRequest {
error: format!("Parsing query request: {}", e),
request: bin_request.into(),
})
}
};
let contract_result: ContractResult<Binary> = self
.router
.query(self.api, self.storage, self.block_info, request)
.into();
SystemResult::Ok(contract_result)
}
}