From 056750b816930cb6bc97ea1143a71cdfaf79b17e Mon Sep 17 00:00:00 2001 From: Dariusz Depta Date: Fri, 8 Sep 2023 11:19:10 +0200 Subject: [PATCH 1/7] Prepared proposal version. --- src/app.rs | 40 +++++++++++++++++++++++ src/error.rs | 2 +- src/wasm.rs | 90 ++++++++++++++++++++++++++++++++++------------------ 3 files changed, 101 insertions(+), 31 deletions(-) diff --git a/src/app.rs b/src/app.rs index 93471097..1a6f552f 100644 --- a/src/app.rs +++ b/src/app.rs @@ -728,6 +728,28 @@ where self.init_modules(|router, _, _| router.wasm.store_code(creator, code)) } + /// Duplicates the contract code identified by `code_id` and returns + /// the identifier of newly created copy of the contract code. + /// + /// # Examples + /// + /// ``` + /// use cosmwasm_std::Addr; + /// use cw_multi_test::App; + /// + /// let mut app = App::default(); + /// + /// // there is no contract code with identifier 100 stored yet, returns an error + /// assert_eq!("Unregistered code id: 100", app.duplicate_code(Addr::unchecked("creator"), 100).unwrap_err().to_string()); + /// + /// // zero is an invalid identifier for contract code, returns an error + /// assert_eq!("Unregistered code id: 0", app.duplicate_code(Addr::unchecked("creator"), 0).unwrap_err().to_string()); + /// + /// ``` + pub fn duplicate_code(&mut self, creator: Addr, code_id: u64) -> AnyResult { + self.init_modules(|router, _, _| router.wasm.duplicate_code(creator, code_id)) + } + /// This allows to get `ContractData` for specific contract pub fn contract_data(&self, address: &Addr) -> AnyResult { self.read_module(|router, _, storage| router.wasm.load_contract(storage, address)) @@ -1136,6 +1158,24 @@ mod test { use crate::test_helpers::{CustomMsg, EmptyMsg}; use crate::transactions::StorageTransaction; + #[test] + fn duplicate_contract_code() { + // set up application + let mut app = App::default(); + + // set up original contract + #[cfg(not(feature = "multitest_api_1_0"))] + let original_code_id = app.store_code(payout::contract()); + #[cfg(feature = "multitest_api_1_0")] + let original_code_id = app.store_code(Addr::unchecked("creator"), payout::contract()); + + // duplicate contract code + let duplicate_code_id = app + .duplicate_code(Addr::unchecked("creator"), original_code_id) + .unwrap(); + assert_ne!(original_code_id, duplicate_code_id); + } + fn get_balance( app: &App, addr: &Addr, diff --git a/src/error.rs b/src/error.rs index 8879c039..31f6d2ce 100644 --- a/src/error.rs +++ b/src/error.rs @@ -21,7 +21,7 @@ pub enum Error { #[error("Unsupported wasm message: {0:?}")] UnsupportedWasmMsg(WasmMsg), - #[error("Unregistered code id")] + #[error("Unregistered code id: {0}")] UnregisteredCodeId(u64), } diff --git a/src/wasm.rs b/src/wasm.rs index 94f667f5..7b8d549b 100644 --- a/src/wasm.rs +++ b/src/wasm.rs @@ -1,4 +1,3 @@ -use std::borrow::Borrow; use std::fmt::Debug; use cosmwasm_std::{ @@ -66,13 +65,17 @@ pub struct ContractData { } /// Contract code base data. -struct CodeData { - /// Address of an account that initially created the contract. +struct CodeData { + /// Address of an account that initially stored the contract code. //FIXME Remove this feature flag when the default flag is `cosmwasm_1_2` or higher. #[cfg_attr(feature = "cosmwasm_1_1", allow(dead_code))] creator: Addr, - /// The contract code base. - code: Box>, + /// Seed used to generate a checksum for underlying code base. + //FIXME Remove this feature flag when the default flag is `cosmwasm_1_2` or higher. + #[cfg_attr(feature = "cosmwasm_1_1", allow(dead_code))] + seed: usize, + /// Identifier of the code base where the contract code is stored in memory. + code_base_id: usize, } pub trait Wasm { @@ -110,9 +113,10 @@ pub trait Wasm { } pub struct WasmKeeper { - /// `codes` is in-memory lookup that stands in for wasm code, - /// this can only be edited on the WasmRouter, and just read in caches. - codes: Vec>, + /// Contract codes that stand for wasm code in real-life blockchain. + code_base: Vec>>, + /// Code data with code base identifier and additional attributes. + code_data: Vec, /// Just markers to make type elision fork when using it as `Wasm` trait _p: std::marker::PhantomData, generator: Box, @@ -139,9 +143,11 @@ impl AddressGenerator for SimpleAddressGenerator { } impl Default for WasmKeeper { + /// Returns the default value for [WasmKeeper]. fn default() -> Self { Self { - codes: Vec::default(), + code_base: Vec::default(), + code_data: Vec::default(), _p: std::marker::PhantomData, generator: Box::new(SimpleAddressGenerator()), } @@ -182,14 +188,14 @@ where #[cfg(feature = "cosmwasm_1_2")] WasmQuery::CodeInfo { code_id } => { let code_data = self - .codes + .code_data .get((code_id - 1) as usize) .ok_or(Error::UnregisteredCodeId(code_id))?; let mut res = cosmwasm_std::CodeInfoResponse::default(); res.code_id = code_id; res.creator = code_data.creator.to_string(); res.checksum = cosmwasm_std::HexBinary::from( - Sha256::digest(format!("contract code {}", res.code_id)).to_vec(), + Sha256::digest(format!("contract code {}", code_data.seed)).to_vec(), ); to_binary(&res).map_err(Into::into) } @@ -234,11 +240,47 @@ impl WasmKeeper { /// Stores contract code in the in-memory lookup table. /// Returns an identifier of the stored contract code. pub fn store_code(&mut self, creator: Addr, code: Box>) -> u64 { - let code_id = self.codes.len() + 1; - self.codes.push(CodeData:: { creator, code }); + let code_base_id = self.code_base.len(); + self.code_base.push(code); + let code_id = self.code_data.len() + 1; + self.code_data.push(CodeData { + creator, + seed: code_id, + code_base_id, + }); code_id as u64 } + /// Duplicates contract code with specified identifier. + pub fn duplicate_code(&mut self, creator: Addr, code_id: u64) -> AnyResult { + if code_id < 1 { + bail!(Error::UnregisteredCodeId(code_id)); + } + let code_data = self + .code_data + .get((code_id - 1) as usize) + .ok_or(Error::UnregisteredCodeId(code_id))?; + self.code_data.push(CodeData { + creator, + seed: code_data.seed, + code_base_id: code_data.code_base_id, + }); + Ok(code_id + 1) + } + + /// Returns a handler to code of contract having specified code id. + #[allow(clippy::borrowed_box)] + pub fn get_code(&self, code_id: u64) -> AnyResult<&Box>> { + if code_id < 1 { + bail!(Error::UnregisteredCodeId(code_id)); + } + let code_data = self + .code_data + .get((code_id - 1) as usize) + .ok_or(Error::UnregisteredCodeId(code_id))?; + Ok(&self.code_base[code_data.code_base_id]) + } + pub fn load_contract(&self, storage: &dyn Storage, address: &Addr) -> AnyResult { CONTRACTS .load(&prefixed_read(storage, NAMESPACE_WASM), address) @@ -330,11 +372,9 @@ where } pub fn new_with_custom_address_generator(generator: impl AddressGenerator + 'static) -> Self { - let default = Self::default(); Self { - codes: default.codes, - _p: default._p, generator: Box::new(generator), + ..Default::default() } } @@ -534,7 +574,7 @@ where let contract_addr = api.addr_validate(&contract_addr)?; // check admin status and update the stored code_id - if new_code_id as usize > self.codes.len() { + if new_code_id as usize > self.code_data.len() { bail!("Cannot migrate contract to unregistered code id"); } let mut data = self.load_contract(storage, &contract_addr)?; @@ -742,7 +782,7 @@ where label: String, created: u64, ) -> AnyResult { - if code_id as usize > self.codes.len() { + if code_id as usize > self.code_data.len() { bail!("Cannot init contract with unregistered code id"); } @@ -879,12 +919,7 @@ where F: FnOnce(&Box>, Deps, Env) -> AnyResult, { let contract = self.load_contract(storage, &address)?; - let handler = self - .codes - .get((contract.code_id - 1) as usize) - .ok_or(Error::UnregisteredCodeId(contract.code_id))? - .code - .borrow(); + let handler = self.get_code(contract.code_id)?; let storage = self.contract_storage_readonly(storage, &address); let env = self.get_env(address, block); @@ -910,12 +945,7 @@ where ExecC: DeserializeOwned, { let contract = self.load_contract(storage, &address)?; - let handler = self - .codes - .get((contract.code_id - 1) as usize) - .ok_or(Error::UnregisteredCodeId(contract.code_id))? - .code - .borrow(); + let handler = self.get_code(contract.code_id)?; // We don't actually need a transaction here, as it is already embedded in a transactional. // execute_submsg or App.execute_multi. From 2bb95dbefa66bbb219a95835be371c540a5a01cf Mon Sep 17 00:00:00 2001 From: Dariusz Depta Date: Mon, 11 Sep 2023 16:09:07 +0200 Subject: [PATCH 2/7] Refactoring. --- src/app.rs | 24 +++++++++++++++--------- src/wasm.rs | 12 ++++++------ 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/app.rs b/src/app.rs index 1a6f552f..f3e5e0d0 100644 --- a/src/app.rs +++ b/src/app.rs @@ -729,7 +729,7 @@ where } /// Duplicates the contract code identified by `code_id` and returns - /// the identifier of newly created copy of the contract code. + /// the identifier of newly created copy of the contract. /// /// # Examples /// @@ -740,14 +740,14 @@ where /// let mut app = App::default(); /// /// // there is no contract code with identifier 100 stored yet, returns an error - /// assert_eq!("Unregistered code id: 100", app.duplicate_code(Addr::unchecked("creator"), 100).unwrap_err().to_string()); + /// assert_eq!("Unregistered code id: 100", app.duplicate_code(100).unwrap_err().to_string()); /// /// // zero is an invalid identifier for contract code, returns an error - /// assert_eq!("Unregistered code id: 0", app.duplicate_code(Addr::unchecked("creator"), 0).unwrap_err().to_string()); + /// assert_eq!("Unregistered code id: 0", app.duplicate_code(0).unwrap_err().to_string()); /// /// ``` - pub fn duplicate_code(&mut self, creator: Addr, code_id: u64) -> AnyResult { - self.init_modules(|router, _, _| router.wasm.duplicate_code(creator, code_id)) + pub fn duplicate_code(&mut self, code_id: u64) -> AnyResult { + self.init_modules(|router, _, _| router.wasm.duplicate_code(code_id)) } /// This allows to get `ContractData` for specific contract @@ -1159,21 +1159,27 @@ mod test { use crate::transactions::StorageTransaction; #[test] + #[cfg(feature = "cosmwasm_1_2")] fn duplicate_contract_code() { // set up application let mut app = App::default(); - // set up original contract + // store original contract code #[cfg(not(feature = "multitest_api_1_0"))] let original_code_id = app.store_code(payout::contract()); #[cfg(feature = "multitest_api_1_0")] let original_code_id = app.store_code(Addr::unchecked("creator"), payout::contract()); // duplicate contract code - let duplicate_code_id = app - .duplicate_code(Addr::unchecked("creator"), original_code_id) - .unwrap(); + let duplicate_code_id = app.duplicate_code(original_code_id).unwrap(); assert_ne!(original_code_id, duplicate_code_id); + + // query and compare code info of both contracts + let original_response = app.wrap().query_wasm_code_info(original_code_id).unwrap(); + let duplicate_response = app.wrap().query_wasm_code_info(duplicate_code_id).unwrap(); + assert_ne!(original_response.code_id, duplicate_response.code_id); + assert_eq!(original_response.creator, duplicate_response.creator); + assert_eq!(original_response.checksum, duplicate_response.checksum); } fn get_balance( diff --git a/src/wasm.rs b/src/wasm.rs index 7b8d549b..85c1bc57 100644 --- a/src/wasm.rs +++ b/src/wasm.rs @@ -252,7 +252,7 @@ impl WasmKeeper { } /// Duplicates contract code with specified identifier. - pub fn duplicate_code(&mut self, creator: Addr, code_id: u64) -> AnyResult { + pub fn duplicate_code(&mut self, code_id: u64) -> AnyResult { if code_id < 1 { bail!(Error::UnregisteredCodeId(code_id)); } @@ -261,16 +261,16 @@ impl WasmKeeper { .get((code_id - 1) as usize) .ok_or(Error::UnregisteredCodeId(code_id))?; self.code_data.push(CodeData { - creator, + creator: code_data.creator.clone(), seed: code_data.seed, code_base_id: code_data.code_base_id, }); Ok(code_id + 1) } - /// Returns a handler to code of contract having specified code id. + /// Returns a handler to code of the contract with specified code id. #[allow(clippy::borrowed_box)] - pub fn get_code(&self, code_id: u64) -> AnyResult<&Box>> { + pub fn get_contract_code(&self, code_id: u64) -> AnyResult<&Box>> { if code_id < 1 { bail!(Error::UnregisteredCodeId(code_id)); } @@ -919,7 +919,7 @@ where F: FnOnce(&Box>, Deps, Env) -> AnyResult, { let contract = self.load_contract(storage, &address)?; - let handler = self.get_code(contract.code_id)?; + let handler = self.get_contract_code(contract.code_id)?; let storage = self.contract_storage_readonly(storage, &address); let env = self.get_env(address, block); @@ -945,7 +945,7 @@ where ExecC: DeserializeOwned, { let contract = self.load_contract(storage, &address)?; - let handler = self.get_code(contract.code_id)?; + let handler = self.get_contract_code(contract.code_id)?; // We don't actually need a transaction here, as it is already embedded in a transactional. // execute_submsg or App.execute_multi. From a5342bf0d94957e3155547c8f921dbf2429afbc5 Mon Sep 17 00:00:00 2001 From: Dariusz Depta Date: Mon, 11 Sep 2023 16:30:51 +0200 Subject: [PATCH 3/7] Removed a reference to Box. --- src/wasm.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/wasm.rs b/src/wasm.rs index 85c1bc57..2786e85c 100644 --- a/src/wasm.rs +++ b/src/wasm.rs @@ -1,3 +1,4 @@ +use std::borrow::Borrow; use std::fmt::Debug; use cosmwasm_std::{ @@ -269,8 +270,7 @@ impl WasmKeeper { } /// Returns a handler to code of the contract with specified code id. - #[allow(clippy::borrowed_box)] - pub fn get_contract_code(&self, code_id: u64) -> AnyResult<&Box>> { + pub fn get_contract_code(&self, code_id: u64) -> AnyResult<&dyn Contract> { if code_id < 1 { bail!(Error::UnregisteredCodeId(code_id)); } @@ -278,7 +278,7 @@ impl WasmKeeper { .code_data .get((code_id - 1) as usize) .ok_or(Error::UnregisteredCodeId(code_id))?; - Ok(&self.code_base[code_data.code_base_id]) + Ok(self.code_base[code_data.code_base_id].borrow()) } pub fn load_contract(&self, storage: &dyn Storage, address: &Addr) -> AnyResult { @@ -916,7 +916,7 @@ where action: F, ) -> AnyResult where - F: FnOnce(&Box>, Deps, Env) -> AnyResult, + F: FnOnce(&dyn Contract, Deps, Env) -> AnyResult, { let contract = self.load_contract(storage, &address)?; let handler = self.get_contract_code(contract.code_id)?; @@ -941,7 +941,7 @@ where action: F, ) -> AnyResult where - F: FnOnce(&Box>, DepsMut, Env) -> AnyResult, + F: FnOnce(&dyn Contract, DepsMut, Env) -> AnyResult, ExecC: DeserializeOwned, { let contract = self.load_contract(storage, &address)?; From 97c1e73741d4d37f0750e2f90a77733c66fd4c4c Mon Sep 17 00:00:00 2001 From: Dariusz Depta Date: Tue, 12 Sep 2023 15:43:57 +0200 Subject: [PATCH 4/7] Refactoring. --- src/app.rs | 21 ++++++++++++++++++--- src/error.rs | 5 ++++- src/wasm.rs | 32 ++++++++++++++------------------ 3 files changed, 36 insertions(+), 22 deletions(-) diff --git a/src/app.rs b/src/app.rs index f3e5e0d0..e718fa6d 100644 --- a/src/app.rs +++ b/src/app.rs @@ -740,10 +740,10 @@ where /// let mut app = App::default(); /// /// // there is no contract code with identifier 100 stored yet, returns an error - /// assert_eq!("Unregistered code id: 100", app.duplicate_code(100).unwrap_err().to_string()); + /// assert_eq!("code id 100: no such code", app.duplicate_code(100).unwrap_err().to_string()); /// /// // zero is an invalid identifier for contract code, returns an error - /// assert_eq!("Unregistered code id: 0", app.duplicate_code(0).unwrap_err().to_string()); + /// assert_eq!("code id: invalid", app.duplicate_code(0).unwrap_err().to_string()); /// /// ``` pub fn duplicate_code(&mut self, code_id: u64) -> AnyResult { @@ -2790,7 +2790,7 @@ mod test { #[test] #[cfg(feature = "cosmwasm_1_2")] - fn query_contract_info() { + fn query_existing_code_info() { use super::*; let mut app = App::default(); #[cfg(not(feature = "multitest_api_1_0"))] @@ -2802,6 +2802,21 @@ mod test { assert_eq!("creator", code_info_response.creator); assert!(!code_info_response.checksum.is_empty()); } + + #[test] + #[cfg(feature = "cosmwasm_1_2")] + fn query_non_existing_code_info() { + use super::*; + let app = App::default(); + assert_eq!( + "Generic error: Querier contract error: code id: invalid", + app.wrap().query_wasm_code_info(0).unwrap_err().to_string() + ); + assert_eq!( + "Generic error: Querier contract error: code id 1: no such code", + app.wrap().query_wasm_code_info(1).unwrap_err().to_string() + ); + } } mod custom_messages { diff --git a/src/error.rs b/src/error.rs index 31f6d2ce..626b792c 100644 --- a/src/error.rs +++ b/src/error.rs @@ -21,7 +21,10 @@ pub enum Error { #[error("Unsupported wasm message: {0:?}")] UnsupportedWasmMsg(WasmMsg), - #[error("Unregistered code id: {0}")] + #[error("code id: invalid")] + InvalidCodeId, + + #[error("code id {0}: no such code")] UnregisteredCodeId(u64), } diff --git a/src/wasm.rs b/src/wasm.rs index 2786e85c..7b6d1cad 100644 --- a/src/wasm.rs +++ b/src/wasm.rs @@ -188,10 +188,7 @@ where } #[cfg(feature = "cosmwasm_1_2")] WasmQuery::CodeInfo { code_id } => { - let code_data = self - .code_data - .get((code_id - 1) as usize) - .ok_or(Error::UnregisteredCodeId(code_id))?; + let code_data = self.code_data(code_id)?; let mut res = cosmwasm_std::CodeInfoResponse::default(); res.code_id = code_id; res.creator = code_data.creator.to_string(); @@ -254,13 +251,7 @@ impl WasmKeeper { /// Duplicates contract code with specified identifier. pub fn duplicate_code(&mut self, code_id: u64) -> AnyResult { - if code_id < 1 { - bail!(Error::UnregisteredCodeId(code_id)); - } - let code_data = self - .code_data - .get((code_id - 1) as usize) - .ok_or(Error::UnregisteredCodeId(code_id))?; + let code_data = self.code_data(code_id)?; self.code_data.push(CodeData { creator: code_data.creator.clone(), seed: code_data.seed, @@ -270,15 +261,20 @@ impl WasmKeeper { } /// Returns a handler to code of the contract with specified code id. - pub fn get_contract_code(&self, code_id: u64) -> AnyResult<&dyn Contract> { + pub fn contract_code(&self, code_id: u64) -> AnyResult<&dyn Contract> { + let code_data = self.code_data(code_id)?; + Ok(self.code_base[code_data.code_base_id].borrow()) + } + + /// Returns code data of the contract with specified code id. + fn code_data(&self, code_id: u64) -> AnyResult<&CodeData> { if code_id < 1 { - bail!(Error::UnregisteredCodeId(code_id)); + bail!(Error::InvalidCodeId); } - let code_data = self + Ok(self .code_data .get((code_id - 1) as usize) - .ok_or(Error::UnregisteredCodeId(code_id))?; - Ok(self.code_base[code_data.code_base_id].borrow()) + .ok_or(Error::UnregisteredCodeId(code_id))?) } pub fn load_contract(&self, storage: &dyn Storage, address: &Addr) -> AnyResult { @@ -919,7 +915,7 @@ where F: FnOnce(&dyn Contract, Deps, Env) -> AnyResult, { let contract = self.load_contract(storage, &address)?; - let handler = self.get_contract_code(contract.code_id)?; + let handler = self.contract_code(contract.code_id)?; let storage = self.contract_storage_readonly(storage, &address); let env = self.get_env(address, block); @@ -945,7 +941,7 @@ where ExecC: DeserializeOwned, { let contract = self.load_contract(storage, &address)?; - let handler = self.get_contract_code(contract.code_id)?; + let handler = self.contract_code(contract.code_id)?; // We don't actually need a transaction here, as it is already embedded in a transactional. // execute_submsg or App.execute_multi. From e76d7ccdfb10148d0fb65f2f17321367f233ad4e Mon Sep 17 00:00:00 2001 From: Dariusz Depta Date: Tue, 12 Sep 2023 17:08:29 +0200 Subject: [PATCH 5/7] Refactoring. --- src/app.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/app.rs b/src/app.rs index e718fa6d..0ef04325 100644 --- a/src/app.rs +++ b/src/app.rs @@ -737,6 +737,10 @@ where /// use cosmwasm_std::Addr; /// use cw_multi_test::App; /// + /// //TODO: + /// // Provide a happy path example when testing contracts are public, + /// // otherwise the example code would be unreadable. + /// /// let mut app = App::default(); /// /// // there is no contract code with identifier 100 stored yet, returns an error From 399d78971529664bec2b7e4b2645287958d96f63 Mon Sep 17 00:00:00 2001 From: Dariusz Depta Date: Wed, 13 Sep 2023 10:20:06 +0200 Subject: [PATCH 6/7] Updated code documentation. --- src/app.rs | 43 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/src/app.rs b/src/app.rs index 0ef04325..afe0701b 100644 --- a/src/app.rs +++ b/src/app.rs @@ -729,7 +729,7 @@ where } /// Duplicates the contract code identified by `code_id` and returns - /// the identifier of newly created copy of the contract. + /// the identifier of the newly created copy of the contract code. /// /// # Examples /// @@ -737,18 +737,49 @@ where /// use cosmwasm_std::Addr; /// use cw_multi_test::App; /// - /// //TODO: - /// // Provide a happy path example when testing contracts are public, - /// // otherwise the example code would be unreadable. + /// // contract implementation + /// mod echo { + /// # use cosmwasm_std::{Binary, Deps, DepsMut, Empty, Env, MessageInfo, Response, StdError, SubMsg, WasmMsg}; + /// # use serde::{Deserialize, Serialize}; + /// # use cw_multi_test::{Contract, ContractWrapper}; + /// # + /// # #[derive(Debug, Clone, Serialize, Deserialize, Default)] + /// # pub struct EmptyMsg {} + /// # + /// # fn instantiate(_: DepsMut, _: Env, _: MessageInfo, _: EmptyMsg) -> Result { + /// # Ok(Response::default()) + /// # } + /// # + /// # fn execute(_: DepsMut, _: Env, _info: MessageInfo, msg: WasmMsg) -> Result { + /// # let message = SubMsg::new(msg); + /// # Ok(Response::new().add_submessage(message)) + /// # } + /// # + /// # fn query(_deps: Deps, _env: Env, _msg: EmptyMsg) -> Result { + /// # Err(StdError::generic_err("not implemented yet")) + /// # } + /// // returns the contract + /// pub fn contract() -> Box> { + /// # Box::new(ContractWrapper::new(execute, instantiate, query)) + /// } + /// } /// /// let mut app = App::default(); /// - /// // there is no contract code with identifier 100 stored yet, returns an error - /// assert_eq!("code id 100: no such code", app.duplicate_code(100).unwrap_err().to_string()); + /// // store a new contract, save the code id + /// # #[cfg(not(feature = "multitest_api_1_0"))] + /// let code_id = app.store_code_with_creator(Addr::unchecked("creator"), echo::contract()); + /// # #[cfg(feature = "multitest_api_1_0")] + /// # let code_id = app.store_code(Addr::unchecked("creator"), echo::contract()); + /// + /// // duplicate the existing contract, duplicated contract has different code id + /// assert_ne!(code_id, app.duplicate_code(code_id).unwrap()); /// /// // zero is an invalid identifier for contract code, returns an error /// assert_eq!("code id: invalid", app.duplicate_code(0).unwrap_err().to_string()); /// + /// // there is no contract code with identifier 100 stored yet, returns an error + /// assert_eq!("code id 100: no such code", app.duplicate_code(100).unwrap_err().to_string()); /// ``` pub fn duplicate_code(&mut self, code_id: u64) -> AnyResult { self.init_modules(|router, _, _| router.wasm.duplicate_code(code_id)) From ff32631946c7d9c0e1091e517384b4a146eaa5e8 Mon Sep 17 00:00:00 2001 From: Dariusz Depta Date: Wed, 13 Sep 2023 16:46:16 +0200 Subject: [PATCH 7/7] Refactoring. --- src/app.rs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/app.rs b/src/app.rs index afe0701b..3d745d08 100644 --- a/src/app.rs +++ b/src/app.rs @@ -739,27 +739,26 @@ where /// /// // contract implementation /// mod echo { + /// // contract entry points not shown here + /// # use std::todo; /// # use cosmwasm_std::{Binary, Deps, DepsMut, Empty, Env, MessageInfo, Response, StdError, SubMsg, WasmMsg}; /// # use serde::{Deserialize, Serialize}; /// # use cw_multi_test::{Contract, ContractWrapper}; /// # - /// # #[derive(Debug, Clone, Serialize, Deserialize, Default)] - /// # pub struct EmptyMsg {} - /// # - /// # fn instantiate(_: DepsMut, _: Env, _: MessageInfo, _: EmptyMsg) -> Result { - /// # Ok(Response::default()) + /// # fn instantiate(_: DepsMut, _: Env, _: MessageInfo, _: Empty) -> Result { + /// # todo!() /// # } /// # /// # fn execute(_: DepsMut, _: Env, _info: MessageInfo, msg: WasmMsg) -> Result { - /// # let message = SubMsg::new(msg); - /// # Ok(Response::new().add_submessage(message)) + /// # todo!() /// # } /// # - /// # fn query(_deps: Deps, _env: Env, _msg: EmptyMsg) -> Result { - /// # Err(StdError::generic_err("not implemented yet")) + /// # fn query(_deps: Deps, _env: Env, _msg: Empty) -> Result { + /// # todo!() /// # } - /// // returns the contract + /// # /// pub fn contract() -> Box> { + /// // should return the contract /// # Box::new(ContractWrapper::new(execute, instantiate, query)) /// } /// }