From 61ce26816970ac9bbfd959613d63cb3528d8c050 Mon Sep 17 00:00:00 2001 From: Benjamin DENEUX Date: Mon, 20 Mar 2023 11:25:18 +0100 Subject: [PATCH 01/17] test(logic): add new mock with handler for logic querier --- packages/logic-bindings/src/testing/mock.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/packages/logic-bindings/src/testing/mock.rs b/packages/logic-bindings/src/testing/mock.rs index d38ca6d5..decc51d2 100644 --- a/packages/logic-bindings/src/testing/mock.rs +++ b/packages/logic-bindings/src/testing/mock.rs @@ -13,6 +13,17 @@ pub fn mock_dependencies_with_logic_and_balance( mock_dependencies_with_logic_and_balances(&[(MOCK_CONTRACT_ADDR, contract_balance)]) } +pub fn mock_dependencies_with_logic_handler(handler: LH) -> OwnedDeps, LogicCustomQuery> + where + LH: Fn(&LogicCustomQuery) -> QuerierResult, { + OwnedDeps { + storage: MockStorage::default(), + api: MockApi::default(), + querier: MockLogicQuerier::new(LogicQuerier::new(Box::new(handler)), &[]), + custom_query_type: PhantomData, + } +} + /// Initializes the querier along with the mock_dependencies. /// /// Set the logic querier mock handler. From c9f86641dffe339cff16cd9fba9b3c4a22d50cea Mon Sep 17 00:00:00 2001 From: Benjamin DENEUX Date: Mon, 20 Mar 2023 11:25:40 +0100 Subject: [PATCH 02/17] test(law): prepare mock for law instantiate --- contracts/cw-law-stone/src/contract.rs | 57 +++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/contracts/cw-law-stone/src/contract.rs b/contracts/cw-law-stone/src/contract.rs index a00cb966..cb45894b 100644 --- a/contracts/cw-law-stone/src/contract.rs +++ b/contracts/cw-law-stone/src/contract.rs @@ -3,6 +3,7 @@ use crate::ContractError::NotImplemented; use cosmwasm_std::entry_point; use cosmwasm_std::{Binary, Deps, DepsMut, Env, MessageInfo, Response, StdError, StdResult}; use cw2::set_contract_version; +use logic_bindings::LogicCustomQuery; use crate::error::ContractError; use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; @@ -13,7 +14,7 @@ const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); #[cfg_attr(not(feature = "library"), entry_point)] pub fn instantiate( - deps: DepsMut<'_>, + deps: DepsMut<'_, LogicCustomQuery>, _env: Env, _info: MessageInfo, _msg: InstantiateMsg, @@ -37,3 +38,57 @@ pub fn execute( pub fn query(_deps: Deps<'_>, _env: Env, _msg: QueryMsg) -> StdResult { Err(StdError::generic_err("Not implemented")) } + +#[cfg(test)] +mod tests { + use super::*; + + use cosmwasm_std::testing::{mock_env, mock_info, MockQuerierCustomHandlerResult}; + use cosmwasm_std::{ to_binary, SystemResult, SystemError}; + use logic_bindings::{Answer, AskResponse, LogicCustomQuery, Substitution, Term, Result as LogicResult}; + use logic_bindings::testing::mock::mock_dependencies_with_logic_handler; + + fn custom_logic_handler_with_dependencies(dependencies: Vec, request: &LogicCustomQuery) -> MockQuerierCustomHandlerResult { + let deps_name = format!("[{}]", &dependencies.join(",")); + match request { + LogicCustomQuery::Ask { + query, + .. + } if query == "source_files(Files)." => SystemResult::Ok(to_binary(&AskResponse { + height: 1, + gas_used: 1000, + answer: Some(Answer { + success: true, + has_more: false, + variables: vec!["Files".to_string()], + results: vec![LogicResult { + substitutions: vec![Substitution { + variable: "Files".to_string(), + term: Term { + name: deps_name, + arguments: vec![], + }, + }], + }], + }), + }).into()), + _ => SystemResult::Err(SystemError::InvalidRequest { error: "Ask `souces_files(Files).` predicate not called".to_string(), request: Default::default() }) + } + } + + #[test] + fn proper_initialization() { + let mut deps = mock_dependencies_with_logic_handler(|request| { + custom_logic_handler_with_dependencies(vec!["file1".to_string()], request) + }); + + let msg = InstantiateMsg { + program: Default::default(), + storage_address: "".to_string(), + }; + let info = mock_info("creator", &[]); + + let res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap(); + assert_eq!(0, res.messages.len()); + } +} From 35e8e9199742f27aff5dde678cf19bf67f66bdde Mon Sep 17 00:00:00 2001 From: Benjamin DENEUX Date: Mon, 20 Mar 2023 14:52:41 +0100 Subject: [PATCH 03/17] feat(law): add Law state + tests --- contracts/cw-law-stone/src/contract.rs | 9 +++++++-- contracts/cw-law-stone/src/lib.rs | 1 + contracts/cw-law-stone/src/state.rs | 24 ++++++++++++++++++++++++ 3 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 contracts/cw-law-stone/src/state.rs diff --git a/contracts/cw-law-stone/src/contract.rs b/contracts/cw-law-stone/src/contract.rs index cb45894b..2933ca48 100644 --- a/contracts/cw-law-stone/src/contract.rs +++ b/contracts/cw-law-stone/src/contract.rs @@ -42,7 +42,7 @@ pub fn query(_deps: Deps<'_>, _env: Env, _msg: QueryMsg) -> StdResult { #[cfg(test)] mod tests { use super::*; - + use crate::state::LAW; use cosmwasm_std::testing::{mock_env, mock_info, MockQuerierCustomHandlerResult}; use cosmwasm_std::{ to_binary, SystemResult, SystemError}; use logic_bindings::{Answer, AskResponse, LogicCustomQuery, Substitution, Term, Result as LogicResult}; @@ -84,11 +84,16 @@ mod tests { let msg = InstantiateMsg { program: Default::default(), - storage_address: "".to_string(), + storage_address: "okp41ffzp0xmjhwkltuxcvccl0z9tyfuu7txp5ke0tpkcjpzuq9fcj3pqrteqt3".to_string(), }; let info = mock_info("creator", &[]); let res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap(); assert_eq!(0, res.messages.len()); + + // check internal state too + let law = LAW.load(&deps.storage).unwrap(); + assert_eq!(law.program.storage_address, "okp41ffzp0xmjhwkltuxcvccl0z9tyfuu7txp5ke0tpkcjpzuq9fcj3pqrteqt3".to_string()); + assert_eq!(law.dependencies, vec!["file1".to_string()]); } } diff --git a/contracts/cw-law-stone/src/lib.rs b/contracts/cw-law-stone/src/lib.rs index fddbd06d..14159c9a 100644 --- a/contracts/cw-law-stone/src/lib.rs +++ b/contracts/cw-law-stone/src/lib.rs @@ -13,5 +13,6 @@ pub mod contract; mod error; pub mod msg; +pub mod state; pub use crate::error::ContractError; diff --git a/contracts/cw-law-stone/src/state.rs b/contracts/cw-law-stone/src/state.rs new file mode 100644 index 00000000..fac27a43 --- /dev/null +++ b/contracts/cw-law-stone/src/state.rs @@ -0,0 +1,24 @@ +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +use cw_storage_plus::Item; + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] +pub struct Law { + /// The `cw-storage` object link to the Prolog program carrying law rules and facts. + pub program: Object, + + /// The list of all `cw-storage` dependencies of the law program. + pub dependencies: Vec, +} + +/// Represent a link to an Object stored in the `cw-storage` contract. +pub struct Object { + /// The object id in the `cw-storage` contract. + pub object_id: String, + + /// The `cw-storage` contract address on which the object is stored. + pub storage_address: String, +} + +pub const LAW: Item<'_, Law> = Item::new("law"); From 8819c3824d823d120915176e158d9db088cec9ed Mon Sep 17 00:00:00 2001 From: Benjamin DENEUX Date: Mon, 20 Mar 2023 16:23:10 +0100 Subject: [PATCH 04/17] feat(law): instantiate with sub message store --- Cargo.lock | 1 + contracts/cw-law-stone/Cargo.toml | 1 + contracts/cw-law-stone/src/contract.rs | 118 ++++++++++++++------ contracts/cw-law-stone/src/state.rs | 1 + packages/logic-bindings/src/testing/mock.rs | 9 +- 5 files changed, 91 insertions(+), 39 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fac39eba..aba1fa50 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -214,6 +214,7 @@ dependencies = [ "cosmwasm-std", "cosmwasm-storage", "cw-multi-test", + "cw-storage", "cw-storage-plus", "cw2", "logic-bindings", diff --git a/contracts/cw-law-stone/Cargo.toml b/contracts/cw-law-stone/Cargo.toml index cea27186..d79ed6a4 100644 --- a/contracts/cw-law-stone/Cargo.toml +++ b/contracts/cw-law-stone/Cargo.toml @@ -30,6 +30,7 @@ rpath = false cosmwasm-schema.workspace = true cosmwasm-std.workspace = true cosmwasm-storage.workspace = true +cw-storage = { path = "../cw-storage" } cw-storage-plus.workspace = true cw2.workspace = true logic-bindings = { version = "0.2", path = "../../packages/logic-bindings" } diff --git a/contracts/cw-law-stone/src/contract.rs b/contracts/cw-law-stone/src/contract.rs index 2933ca48..05c54354 100644 --- a/contracts/cw-law-stone/src/contract.rs +++ b/contracts/cw-law-stone/src/contract.rs @@ -1,8 +1,12 @@ use crate::ContractError::NotImplemented; #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; -use cosmwasm_std::{Binary, Deps, DepsMut, Env, MessageInfo, Response, StdError, StdResult}; +use cosmwasm_std::{ + to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdError, StdResult, SubMsg, + WasmMsg, +}; use cw2::set_contract_version; +use cw_storage::msg::ExecuteMsg as StorageMsg; use logic_bindings::LogicCustomQuery; use crate::error::ContractError; @@ -12,16 +16,32 @@ use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; const CONTRACT_NAME: &str = "crates.io:law-stone"; const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); +const STORE_PROGRAM_REPLY_ID: u64 = 1; + #[cfg_attr(not(feature = "library"), entry_point)] pub fn instantiate( deps: DepsMut<'_, LogicCustomQuery>, _env: Env, _info: MessageInfo, - _msg: InstantiateMsg, + msg: InstantiateMsg, ) -> Result { set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; - Err(NotImplemented {}) + let store_msg = StorageMsg::StoreObject { + data: msg.program, + pin: true, + }; + + let store_program_msg = WasmMsg::Execute { + contract_addr: msg.storage_address, + msg: to_binary(&store_msg)?, + funds: vec![], + }; + + Ok(Response::new().add_submessage(SubMsg::reply_on_success( + store_program_msg, + STORE_PROGRAM_REPLY_ID, + ))) } #[cfg_attr(not(feature = "library"), entry_point)] @@ -42,37 +62,46 @@ pub fn query(_deps: Deps<'_>, _env: Env, _msg: QueryMsg) -> StdResult { #[cfg(test)] mod tests { use super::*; - use crate::state::LAW; use cosmwasm_std::testing::{mock_env, mock_info, MockQuerierCustomHandlerResult}; - use cosmwasm_std::{ to_binary, SystemResult, SystemError}; - use logic_bindings::{Answer, AskResponse, LogicCustomQuery, Substitution, Term, Result as LogicResult}; + use cosmwasm_std::{from_binary, to_binary, CosmosMsg, SystemError, SystemResult}; use logic_bindings::testing::mock::mock_dependencies_with_logic_handler; + use logic_bindings::{ + Answer, AskResponse, LogicCustomQuery, Result as LogicResult, Substitution, Term, + }; - fn custom_logic_handler_with_dependencies(dependencies: Vec, request: &LogicCustomQuery) -> MockQuerierCustomHandlerResult { + fn custom_logic_handler_with_dependencies( + dependencies: Vec, + request: &LogicCustomQuery, + ) -> MockQuerierCustomHandlerResult { let deps_name = format!("[{}]", &dependencies.join(",")); match request { - LogicCustomQuery::Ask { - query, - .. - } if query == "source_files(Files)." => SystemResult::Ok(to_binary(&AskResponse { - height: 1, - gas_used: 1000, - answer: Some(Answer { - success: true, - has_more: false, - variables: vec!["Files".to_string()], - results: vec![LogicResult { - substitutions: vec![Substitution { - variable: "Files".to_string(), - term: Term { - name: deps_name, - arguments: vec![], - }, - }], - }], - }), - }).into()), - _ => SystemResult::Err(SystemError::InvalidRequest { error: "Ask `souces_files(Files).` predicate not called".to_string(), request: Default::default() }) + LogicCustomQuery::Ask { query, .. } if query == "source_files(Files)." => { + SystemResult::Ok( + to_binary(&AskResponse { + height: 1, + gas_used: 1000, + answer: Some(Answer { + success: true, + has_more: false, + variables: vec!["Files".to_string()], + results: vec![LogicResult { + substitutions: vec![Substitution { + variable: "Files".to_string(), + term: Term { + name: deps_name, + arguments: vec![], + }, + }], + }], + }), + }) + .into(), + ) + } + _ => SystemResult::Err(SystemError::InvalidRequest { + error: "Ask `souces_files(Files).` predicate not called".to_string(), + request: Default::default(), + }), } } @@ -81,19 +110,36 @@ mod tests { let mut deps = mock_dependencies_with_logic_handler(|request| { custom_logic_handler_with_dependencies(vec!["file1".to_string()], request) }); + let program = to_binary("foo(_) :- true.").unwrap(); let msg = InstantiateMsg { - program: Default::default(), - storage_address: "okp41ffzp0xmjhwkltuxcvccl0z9tyfuu7txp5ke0tpkcjpzuq9fcj3pqrteqt3".to_string(), + program: program.clone(), + storage_address: "okp41ffzp0xmjhwkltuxcvccl0z9tyfuu7txp5ke0tpkcjpzuq9fcj3pqrteqt3" + .to_string(), }; let info = mock_info("creator", &[]); let res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap(); - assert_eq!(0, res.messages.len()); - // check internal state too - let law = LAW.load(&deps.storage).unwrap(); - assert_eq!(law.program.storage_address, "okp41ffzp0xmjhwkltuxcvccl0z9tyfuu7txp5ke0tpkcjpzuq9fcj3pqrteqt3".to_string()); - assert_eq!(law.dependencies, vec!["file1".to_string()]); + // Check if a message is send to the cw-storage to store the logic program. + assert_eq!(1, res.messages.len()); + let sub_msg = res.messages.first().unwrap(); + assert_eq!(STORE_PROGRAM_REPLY_ID, sub_msg.id); + match &sub_msg.msg { + CosmosMsg::Wasm(wasm_msg) => match wasm_msg { + WasmMsg::Execute { msg, .. } => { + let result: StorageMsg = from_binary(msg).unwrap(); + match result { + StorageMsg::StoreObject { data, pin } => { + assert_eq!(data, program); + assert!(pin); + } + _ => assert!(false, "storage message should be a StoreObject message"), + } + } + _ => assert!(false, "wasm message should be a Storage message"), + }, + _ => assert!(false, "cosmos sub message should be a Wasm message execute"), + } } } diff --git a/contracts/cw-law-stone/src/state.rs b/contracts/cw-law-stone/src/state.rs index fac27a43..f0c8f7f4 100644 --- a/contracts/cw-law-stone/src/state.rs +++ b/contracts/cw-law-stone/src/state.rs @@ -13,6 +13,7 @@ pub struct Law { } /// Represent a link to an Object stored in the `cw-storage` contract. +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] pub struct Object { /// The object id in the `cw-storage` contract. pub object_id: String, diff --git a/packages/logic-bindings/src/testing/mock.rs b/packages/logic-bindings/src/testing/mock.rs index decc51d2..6235e811 100644 --- a/packages/logic-bindings/src/testing/mock.rs +++ b/packages/logic-bindings/src/testing/mock.rs @@ -13,9 +13,12 @@ pub fn mock_dependencies_with_logic_and_balance( mock_dependencies_with_logic_and_balances(&[(MOCK_CONTRACT_ADDR, contract_balance)]) } -pub fn mock_dependencies_with_logic_handler(handler: LH) -> OwnedDeps, LogicCustomQuery> - where - LH: Fn(&LogicCustomQuery) -> QuerierResult, { +pub fn mock_dependencies_with_logic_handler( + handler: LH, +) -> OwnedDeps, LogicCustomQuery> +where + LH: Fn(&LogicCustomQuery) -> QuerierResult, +{ OwnedDeps { storage: MockStorage::default(), api: MockApi::default(), From 314bf4aa09003b485ae90a93c8697dd72e52842a Mon Sep 17 00:00:00 2001 From: Benjamin DENEUX Date: Mon, 20 Mar 2023 16:45:17 +0100 Subject: [PATCH 05/17] feat(law): update state with object and dependencies --- contracts/cw-law-stone/src/state.rs | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/contracts/cw-law-stone/src/state.rs b/contracts/cw-law-stone/src/state.rs index f0c8f7f4..119952b3 100644 --- a/contracts/cw-law-stone/src/state.rs +++ b/contracts/cw-law-stone/src/state.rs @@ -1,16 +1,7 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use cw_storage_plus::Item; - -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] -pub struct Law { - /// The `cw-storage` object link to the Prolog program carrying law rules and facts. - pub program: Object, - - /// The list of all `cw-storage` dependencies of the law program. - pub dependencies: Vec, -} +use cw_storage_plus::{Item, Map}; /// Represent a link to an Object stored in the `cw-storage` contract. #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] @@ -22,4 +13,6 @@ pub struct Object { pub storage_address: String, } -pub const LAW: Item<'_, Law> = Item::new("law"); +pub const PROGRAM: Item<'_, Object> = Item::new("program"); + +pub const DEPENDENCIES: Map<'_, &str, Object> = Map::new("dependencies"); From 3c9d30aae96fa08fd26c0b8bc2026f9336d9b463 Mon Sep 17 00:00:00 2001 From: Benjamin DENEUX Date: Tue, 21 Mar 2023 13:39:05 +0100 Subject: [PATCH 06/17] test(law): add test for store_reply --- Cargo.lock | 1 + Cargo.toml | 1 + contracts/cw-law-stone/Cargo.toml | 1 + contracts/cw-law-stone/src/contract.rs | 185 ++++++++++++++++++++++++- contracts/cw-law-stone/src/error.rs | 4 + 5 files changed, 186 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aba1fa50..34c9abe3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -216,6 +216,7 @@ dependencies = [ "cw-multi-test", "cw-storage", "cw-storage-plus", + "cw-utils", "cw2", "logic-bindings", "schemars", diff --git a/Cargo.toml b/Cargo.toml index c6658315..f6e69258 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ cosmwasm-std = "1.2.2" cosmwasm-storage = "1.2.2" cw-multi-test = "0.15.1" cw-storage-plus = "0.15.1" +cw-utils = "1.0.1" cw2 = "0.15.1" schemars = "0.8.12" serde = { version = "1.0.158", default-features = false, features = ["derive"] } diff --git a/contracts/cw-law-stone/Cargo.toml b/contracts/cw-law-stone/Cargo.toml index d79ed6a4..8307f567 100644 --- a/contracts/cw-law-stone/Cargo.toml +++ b/contracts/cw-law-stone/Cargo.toml @@ -32,6 +32,7 @@ cosmwasm-std.workspace = true cosmwasm-storage.workspace = true cw-storage = { path = "../cw-storage" } cw-storage-plus.workspace = true +cw-utils.worksapce = true cw2.workspace = true logic-bindings = { version = "0.2", path = "../../packages/logic-bindings" } schemars.workspace = true diff --git a/contracts/cw-law-stone/src/contract.rs b/contracts/cw-law-stone/src/contract.rs index 05c54354..041a00f8 100644 --- a/contracts/cw-law-stone/src/contract.rs +++ b/contracts/cw-law-stone/src/contract.rs @@ -2,11 +2,12 @@ use crate::ContractError::NotImplemented; #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdError, StdResult, SubMsg, - WasmMsg, + to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Reply, Response, StdError, StdResult, + SubMsg, WasmMsg, }; use cw2::set_contract_version; -use cw_storage::msg::ExecuteMsg as StorageMsg; +use cw_storage::msg::{ExecuteMsg as StorageMsg, ObjectResponse}; +use cw_utils::parse_reply_execute_data; use logic_bindings::LogicCustomQuery; use crate::error::ContractError; @@ -59,11 +60,40 @@ pub fn query(_deps: Deps<'_>, _env: Env, _msg: QueryMsg) -> StdResult { Err(StdError::generic_err("Not implemented")) } +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn reply( + deps: DepsMut<'_, LogicCustomQuery>, + env: Env, + msg: Reply, +) -> Result { + match msg.id { + STORE_PROGRAM_REPLY_ID => reply::store_program_reply(deps, env, msg), + _ => Err(StdError::generic_err("Not implemented").into()), + } +} + +pub mod reply { + use super::*; + + pub fn store_program_reply( + _deps: DepsMut<'_, LogicCustomQuery>, + _env: Env, + msg: Reply, + ) -> Result { + let _ = parse_reply_execute_data(msg)?; + Err(StdError::generic_err("Not implemented").into()) + } +} + #[cfg(test)] mod tests { use super::*; + use crate::state::{DEPENDENCIES, PROGRAM}; use cosmwasm_std::testing::{mock_env, mock_info, MockQuerierCustomHandlerResult}; - use cosmwasm_std::{from_binary, to_binary, CosmosMsg, SystemError, SystemResult}; + use cosmwasm_std::{ + from_binary, to_binary, CosmosMsg, Order, SubMsgResponse, SubMsgResult, SystemError, + SystemResult, + }; use logic_bindings::testing::mock::mock_dependencies_with_logic_handler; use logic_bindings::{ Answer, AskResponse, LogicCustomQuery, Result as LogicResult, Substitution, Term, @@ -108,7 +138,7 @@ mod tests { #[test] fn proper_initialization() { let mut deps = mock_dependencies_with_logic_handler(|request| { - custom_logic_handler_with_dependencies(vec!["file1".to_string()], request) + custom_logic_handler_with_dependencies(vec![], request) }); let program = to_binary("foo(_) :- true.").unwrap(); @@ -132,7 +162,7 @@ mod tests { match result { StorageMsg::StoreObject { data, pin } => { assert_eq!(data, program); - assert!(pin); + assert!(pin, "the main program should be pinned"); } _ => assert!(false, "storage message should be a StoreObject message"), } @@ -142,4 +172,147 @@ mod tests { _ => assert!(false, "cosmos sub message should be a Wasm message execute"), } } + + struct StoreTestCase { + dependencies: Vec<(String, String, String)>, // URI, contract address, object id + object_id: String, + } + + #[test] + fn store_program_reply() { + let cases = vec![ + StoreTestCase { + dependencies: vec![ + ( + "cosmwasm:okp41dclchlcttf2uektxyryg0c6yau63eml5q9uq03myg44ml8cxpxnqavca4s?query=%7B%22object_data%22%3A%7B%22id%22%3A%20%224cbe36399aabfcc7158ee7a66cbfffa525bb0ceab33d1ff2cff08759fe0a9b05%22%7D%7D".to_string(), + "okp41dclchlcttf2uektxyryg0c6yau63eml5q9uq03myg44ml8cxpxnqavca4s".to_string(), + "4cbe36399aabfcc7158ee7a66cbfffa525bb0ceab33d1ff2cff08759fe0a9b05".to_string() + ), + ], + object_id: "0689c526187c6785dfcce28f8df19138da292598dc19548a852de1792062f271" + .to_string(), + }, + StoreTestCase { + dependencies: vec![], + object_id: "4cbe36399aabfcc7158ee7a66cbfffa525bb0ceab33d1ff2cff08759fe0a9b05" + .to_string(), + }, + StoreTestCase { + dependencies: vec![ + ( + "cosmwasm:okp41dclchlcttf2uektxyryg0c6yau63eml5q9uq03myg44ml8cxpxnqavca4s?query=%7B%22object_data%22%3A%7B%22id%22%3A%20%224cbe36399aabfcc7158ee7a66cbfffa525bb0ceab33d1ff2cff08759fe0a9b05%22%7D%7D".to_string(), + "okp41dclchlcttf2uektxyryg0c6yau63eml5q9uq03myg44ml8cxpxnqavca4s".to_string(), // contract addr + "4cbe36399aabfcc7158ee7a66cbfffa525bb0ceab33d1ff2cff08759fe0a9b05".to_string() // object id + ), + ( + "cosmwasm:okp41dclchlcttf2uektxyryg0c6yau63eml5q9uq03myg44ml8cxpxnqavca4s?query=%7B%22object_data%22%3A%7B%22id%22%3A%20%220689c526187c6785dfcce28f8df19138da292598dc19548a852de1792062f271%22%7D%7D".to_string(), + "okp41dclchlcttf2uektxyryg0c6yau63eml5q9uq03myg44ml8cxpxnqavca4s".to_string(), // contract addr + "0689c526187c6785dfcce28f8df19138da292598dc19548a852de1792062f271".to_string() // object id + ), + ], + object_id: "0689c526187c6785dfcce28f8df19138da292598dc19548a852de1792062f271" + .to_string(), + }, + ]; + + for case in cases { + let uris = Box::new( + case.dependencies + .clone() + .into_iter() + .map(|(uri, _, _)| uri) + .collect::>(), + ); + let mut deps = mock_dependencies_with_logic_handler(move |request| { + custom_logic_handler_with_dependencies(uris.to_vec(), request) + }); + + let object = ObjectResponse { + id: case.object_id.clone(), + owner: "creator".to_string(), + is_pinned: true, + size: Default::default(), + }; + let reply = Reply { + id: STORE_PROGRAM_REPLY_ID, + result: SubMsgResult::Ok(SubMsgResponse { + events: vec![], + data: Some(to_binary(&object).unwrap()), + }), + }; + let res = reply::store_program_reply(deps.as_mut(), mock_env(), reply).unwrap(); + + let program = PROGRAM.load(&deps.storage).unwrap(); + assert_eq!(case.object_id.clone(), program.object_id); + + let deps_len_requirement = case.dependencies.len(); + + if deps_len_requirement > 0 { + assert_eq!( + deps_len_requirement, + DEPENDENCIES + .keys_raw(&deps.storage, None, None, Order::Ascending) + .count() + ); + for (_, contract_addr, object_id) in case.dependencies { + let o = DEPENDENCIES.load(&deps.storage, object_id.as_str()); + assert!( + o.is_ok(), + "dependencies should contains each object id dependencies as key" + ); + let o = o.unwrap(); + assert_eq!( + o.object_id, object_id, + "dependencies should contains each object id dependencies as key" + ); + assert_eq!( + o.storage_address, contract_addr, + "dependencies should contains each object id dependencies as key" + ); + } + } + + assert_eq!( + deps_len_requirement, + res.messages.len(), + "response should contains any sub message as dependencies" + ); + + let objects_pinned: Vec = res + .messages + .into_iter() + .flat_map(|sub_msg| -> Option { + match &sub_msg.msg { + CosmosMsg::Wasm(wasm_msg) => match wasm_msg { + WasmMsg::Execute { msg, .. } => { + let result: StorageMsg = from_binary(msg).unwrap(); + match result { + StorageMsg::PinObject { id } => Some(id), + _ => { + assert!(false, "should contains only PinObject message(s)"); + None + } + } + } + _ => { + assert!(false, "wasm message should be a Storage message"); + None + } + }, + _ => { + assert!(false, "cosmos sub message should be a Wasm message execute"); + None + } + } + }) + .collect(); + + for object in objects_pinned { + assert!( + DEPENDENCIES.has(&deps.storage, object.as_str()), + "each dependencies should be pinned by a PinObject message" + ) + } + } + } } diff --git a/contracts/cw-law-stone/src/error.rs b/contracts/cw-law-stone/src/error.rs index 1f8c2bd4..80abc91e 100644 --- a/contracts/cw-law-stone/src/error.rs +++ b/contracts/cw-law-stone/src/error.rs @@ -1,4 +1,5 @@ use cosmwasm_std::StdError; +use cw_utils::ParseReplyError; use thiserror::Error; #[derive(Error, Debug)] @@ -8,4 +9,7 @@ pub enum ContractError { #[error("Not implemented")] NotImplemented {}, + + #[error("{0}")] + Parse(#[from] ParseReplyError), } From e9de097d6f013663404d695594fd9841dff2dc3c Mon Sep 17 00:00:00 2001 From: Benjamin DENEUX Date: Wed, 22 Mar 2023 09:01:20 +0100 Subject: [PATCH 07/17] feat(law): create temporary context on instantiate --- contracts/cw-law-stone/src/contract.rs | 11 +++++++++-- contracts/cw-law-stone/src/state.rs | 4 ++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/contracts/cw-law-stone/src/contract.rs b/contracts/cw-law-stone/src/contract.rs index 041a00f8..f67225f7 100644 --- a/contracts/cw-law-stone/src/contract.rs +++ b/contracts/cw-law-stone/src/contract.rs @@ -12,6 +12,7 @@ use logic_bindings::LogicCustomQuery; use crate::error::ContractError; use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; +use crate::state::INSTANTIATE_CONTEXT; // version info for migration info const CONTRACT_NAME: &str = "crates.io:law-stone"; @@ -29,16 +30,18 @@ pub fn instantiate( set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; let store_msg = StorageMsg::StoreObject { - data: msg.program, + data: msg.program.clone(), pin: true, }; let store_program_msg = WasmMsg::Execute { - contract_addr: msg.storage_address, + contract_addr: msg.storage_address.clone(), msg: to_binary(&store_msg)?, funds: vec![], }; + INSTANTIATE_CONTEXT.save(deps.storage, &(msg.storage_address, msg.program))?; + Ok(Response::new().add_submessage(SubMsg::reply_on_success( store_program_msg, STORE_PROGRAM_REPLY_ID, @@ -171,6 +174,10 @@ mod tests { }, _ => assert!(false, "cosmos sub message should be a Wasm message execute"), } + assert_eq!("okp41ffzp0xmjhwkltuxcvccl0z9tyfuu7txp5ke0tpkcjpzuq9fcj3pqrteqt3".to_string(), + INSTANTIATE_CONTEXT.load(&deps.storage).unwrap().0); + assert_eq!(program, + INSTANTIATE_CONTEXT.load(&deps.storage).unwrap().1) } struct StoreTestCase { diff --git a/contracts/cw-law-stone/src/state.rs b/contracts/cw-law-stone/src/state.rs index 119952b3..fe010465 100644 --- a/contracts/cw-law-stone/src/state.rs +++ b/contracts/cw-law-stone/src/state.rs @@ -1,8 +1,12 @@ +use cosmwasm_std::Binary; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use cw_storage_plus::{Item, Map}; +/// State to store context during contract instantiation +pub const INSTANTIATE_CONTEXT: Item<'_, (String, Binary)> = Item::new("instantiate"); + /// Represent a link to an Object stored in the `cw-storage` contract. #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] pub struct Object { From 2adaa5b76009bb036f4cbc8002985a20a8bcd32b Mon Sep 17 00:00:00 2001 From: Benjamin DENEUX Date: Wed, 22 Mar 2023 16:00:55 +0100 Subject: [PATCH 08/17] feat(law): implement instantiate --- contracts/cw-law-stone/src/contract.rs | 62 +++++++++++++++++++------- contracts/cw-law-stone/src/error.rs | 9 ++++ contracts/cw-law-stone/src/helper.rs | 55 +++++++++++++++++++++++ contracts/cw-law-stone/src/lib.rs | 23 +++++----- 4 files changed, 122 insertions(+), 27 deletions(-) create mode 100644 contracts/cw-law-stone/src/helper.rs diff --git a/contracts/cw-law-stone/src/contract.rs b/contracts/cw-law-stone/src/contract.rs index f67225f7..82507ad8 100644 --- a/contracts/cw-law-stone/src/contract.rs +++ b/contracts/cw-law-stone/src/contract.rs @@ -13,6 +13,7 @@ use logic_bindings::LogicCustomQuery; use crate::error::ContractError; use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; use crate::state::INSTANTIATE_CONTEXT; +use crate::helper::ask_response_to_submsg; // version info for migration info const CONTRACT_NAME: &str = "crates.io:law-stone"; @@ -76,16 +77,46 @@ pub fn reply( } pub mod reply { + use cosmwasm_std::{Attribute, from_binary}; + use cw_storage::msg::BucketResponse; + use cw_storage::msg::ExecuteMsg::StoreObject; + use logic_bindings::{AskResponse, Substitution}; + use crate::helper::get_reply_event_attribute; + use crate::state::{PROGRAM, Object}; use super::*; pub fn store_program_reply( - _deps: DepsMut<'_, LogicCustomQuery>, + deps: DepsMut<'_, LogicCustomQuery>, _env: Env, msg: Reply, ) -> Result { - let _ = parse_reply_execute_data(msg)?; - Err(StdError::generic_err("Not implemented").into()) + + let context = INSTANTIATE_CONTEXT.load(deps.storage)?; + + msg.result.into_result() + .map_err(|_| ContractError::EmptyReply) + .and_then(|e| get_reply_event_attribute(e.events, "id".to_string()).ok_or(ContractError::NoObjectId)) + .map(|obj_id| Object { + object_id: obj_id.to_string(), + storage_address: context.0.clone() + }) + .and_then(|obj| -> Result<(), ContractError> { + PROGRAM.save(deps.storage, &obj).map_err(|e| ContractError::from(e)) + }) + .and_then(|_| -> Result { + let req = LogicCustomQuery::Ask { program: from_binary(&context.1)?, query: "source_files(Files).".to_string() }.into(); + deps.querier.query(&req).map_err(|e| ContractError::from(e)) + }) + .and_then(|res| ask_response_to_submsg(res, context.0, "Files".to_string())) + .map(|msg| { + // Clean instantiate context + INSTANTIATE_CONTEXT.remove(deps.storage); + Response::new() + .add_submessages(msg) + }) } + + } #[cfg(test)] @@ -93,10 +124,7 @@ mod tests { use super::*; use crate::state::{DEPENDENCIES, PROGRAM}; use cosmwasm_std::testing::{mock_env, mock_info, MockQuerierCustomHandlerResult}; - use cosmwasm_std::{ - from_binary, to_binary, CosmosMsg, Order, SubMsgResponse, SubMsgResult, SystemError, - SystemResult, - }; + use cosmwasm_std::{from_binary, to_binary, CosmosMsg, Order, SubMsgResponse, SubMsgResult, SystemError, SystemResult, Event, Attribute}; use logic_bindings::testing::mock::mock_dependencies_with_logic_handler; use logic_bindings::{ Answer, AskResponse, LogicCustomQuery, Result as LogicResult, Substitution, Term, @@ -234,20 +262,22 @@ mod tests { custom_logic_handler_with_dependencies(uris.to_vec(), request) }); - let object = ObjectResponse { - id: case.object_id.clone(), - owner: "creator".to_string(), - is_pinned: true, - size: Default::default(), - }; let reply = Reply { id: STORE_PROGRAM_REPLY_ID, result: SubMsgResult::Ok(SubMsgResponse { - events: vec![], - data: Some(to_binary(&object).unwrap()), + events: vec![ + Event::new("e".to_string()).add_attribute("id".to_string(), case.object_id.clone()) + ], + data: None, }), }; - let res = reply::store_program_reply(deps.as_mut(), mock_env(), reply).unwrap(); + + // Configure the instantiate context + let program = to_binary("foo(_) :- true.").unwrap(); + INSTANTIATE_CONTEXT.save(deps.as_mut().storage, &("okp41dclchlcttf2uektxyryg0c6yau63eml5q9uq03myg44ml8cxpxnqavca4s".to_string(), program)).unwrap(); + + let response = reply::store_program_reply(deps.as_mut(), mock_env(), reply); + let res = response.unwrap(); let program = PROGRAM.load(&deps.storage).unwrap(); assert_eq!(case.object_id.clone(), program.object_id); diff --git a/contracts/cw-law-stone/src/error.rs b/contracts/cw-law-stone/src/error.rs index 80abc91e..e520cad5 100644 --- a/contracts/cw-law-stone/src/error.rs +++ b/contracts/cw-law-stone/src/error.rs @@ -12,4 +12,13 @@ pub enum ContractError { #[error("{0}")] Parse(#[from] ParseReplyError), + + #[error("Could not find ObjectId of stored program")] + NoObjectId, + + #[error("Empty data on reply")] + EmptyReply, + + #[error("Invalid reply message: {0}")] + InvalidReplyMsg(StdError), } diff --git a/contracts/cw-law-stone/src/helper.rs b/contracts/cw-law-stone/src/helper.rs new file mode 100644 index 00000000..a23b6791 --- /dev/null +++ b/contracts/cw-law-stone/src/helper.rs @@ -0,0 +1,55 @@ +use std::env::var; +use cosmwasm_std::{Attribute, Event, SubMsg, to_binary, WasmMsg}; +use logic_bindings::{AskResponse, Substitution}; +use crate::ContractError; +use crate::state::Object; +use cw_storage::msg::ExecuteMsg as StorageMsg; +use crate::ContractError::NotImplemented; + +pub fn get_reply_event_attribute(events: Vec, key: String) -> Option { + let r = events.iter() + .flat_map(|e| e.attributes.clone()) + .filter(|a| a.key == key) + .map(|a| a.value) + .collect::>(); + + if r.len() > 0 { Some(r[0].clone())} else { None } +} + +/// Files terms is List atom, List is represented as String in prolog, filter to remove +/// all paterm to represent the list and return the result as Vec. +fn filter_source_files(substitution: Substitution) -> Vec { + substitution.term.name.split(",") + .into_iter() + .map(|s| s.replace(&['\'', '[', ']'], "")) + .collect::>() +} + +fn uri_to_object(uri: String) -> Result { + Err(NotImplemented {}) +} + +pub fn ask_response_to_submsg(res: AskResponse, storage_addr: String, variable: String) -> Result, ContractError> { + let uris = res.answer + .map(|a| a.results) + .unwrap_or(vec![]) + .iter() + .flat_map(|result| result.substitutions.clone()) + .filter(|s| s.variable == variable) + .flat_map(|s| filter_source_files(s)) + .collect::>(); + + let mut msgs = vec![]; + for uri in uris { + let object = uri_to_object(uri)?; + let msg = WasmMsg::Execute { + contract_addr: storage_addr.to_string(), + msg: to_binary(&StorageMsg::PinObject { + id: object.object_id, + })?, + funds: vec![], + }; + msgs.push(SubMsg::new(msg)) + } + return Ok(msgs) +} diff --git a/contracts/cw-law-stone/src/lib.rs b/contracts/cw-law-stone/src/lib.rs index 14159c9a..7a23a3e3 100644 --- a/contracts/cw-law-stone/src/lib.rs +++ b/contracts/cw-law-stone/src/lib.rs @@ -1,18 +1,19 @@ -#![forbid(unsafe_code)] -#![deny( - warnings, - rust_2018_idioms, - trivial_casts, - trivial_numeric_casts, - unused_lifetimes, - unused_import_braces, - unused_qualifications, - unused_qualifications -)] +// #![forbid(unsafe_code)] +// #![deny( +// warnings, +// rust_2018_idioms, +// trivial_casts, +// trivial_numeric_casts, +// unused_lifetimes, +// unused_import_braces, +// unused_qualifications, +// unused_qualifications +// )] pub mod contract; mod error; pub mod msg; pub mod state; +mod helper; pub use crate::error::ContractError; From 3b11d2dfca1d8ab6981f70791cd57378b1946ae7 Mon Sep 17 00:00:00 2001 From: Benjamin DENEUX Date: Thu, 23 Mar 2023 08:45:13 +0100 Subject: [PATCH 09/17] fix(law): program conversion base64 to string --- contracts/cw-law-stone/src/contract.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contracts/cw-law-stone/src/contract.rs b/contracts/cw-law-stone/src/contract.rs index 82507ad8..5807f16e 100644 --- a/contracts/cw-law-stone/src/contract.rs +++ b/contracts/cw-law-stone/src/contract.rs @@ -77,7 +77,7 @@ pub fn reply( } pub mod reply { - use cosmwasm_std::{Attribute, from_binary}; + use cosmwasm_std::{Attribute, from_binary, QueryRequest}; use cw_storage::msg::BucketResponse; use cw_storage::msg::ExecuteMsg::StoreObject; use logic_bindings::{AskResponse, Substitution}; @@ -104,7 +104,7 @@ pub mod reply { PROGRAM.save(deps.storage, &obj).map_err(|e| ContractError::from(e)) }) .and_then(|_| -> Result { - let req = LogicCustomQuery::Ask { program: from_binary(&context.1)?, query: "source_files(Files).".to_string() }.into(); + let req: QueryRequest = LogicCustomQuery::Ask { program: String::from_utf8(context.1.to_vec()).map_err(|e| StdError::invalid_utf8(e.to_string()))?, query: "source_files(Files).".to_string() }.into(); deps.querier.query(&req).map_err(|e| ContractError::from(e)) }) .and_then(|res| ask_response_to_submsg(res, context.0, "Files".to_string())) @@ -273,7 +273,7 @@ mod tests { }; // Configure the instantiate context - let program = to_binary("foo(_) :- true.").unwrap(); + let program = Binary::from_base64("Zm9vKF8pIDotIHRydWUu").unwrap(); INSTANTIATE_CONTEXT.save(deps.as_mut().storage, &("okp41dclchlcttf2uektxyryg0c6yau63eml5q9uq03myg44ml8cxpxnqavca4s".to_string(), program)).unwrap(); let response = reply::store_program_reply(deps.as_mut(), mock_env(), reply); From 886bffd42015791c716374e04109579c087f4681 Mon Sep 17 00:00:00 2001 From: Benjamin DENEUX Date: Thu, 23 Mar 2023 09:18:01 +0100 Subject: [PATCH 10/17] refactor(law): export func to build query to logic module --- Cargo.lock | 67 ++++++++++++++++++++++++++ contracts/cw-law-stone/Cargo.toml | 1 + contracts/cw-law-stone/src/contract.rs | 6 ++- contracts/cw-law-stone/src/error.rs | 17 +++++++ contracts/cw-law-stone/src/helper.rs | 6 +-- contracts/cw-law-stone/src/lib.rs | 1 + contracts/cw-law-stone/src/uri.rs | 14 ++++++ 7 files changed, 106 insertions(+), 6 deletions(-) create mode 100644 contracts/cw-law-stone/src/uri.rs diff --git a/Cargo.lock b/Cargo.lock index 34c9abe3..22e37903 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -222,6 +222,7 @@ dependencies = [ "schemars", "serde", "thiserror", + "url", ] [[package]] @@ -411,6 +412,15 @@ dependencies = [ "subtle", ] +[[package]] +name = "form_urlencoded" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +dependencies = [ + "percent-encoding", +] + [[package]] name = "forward_ref" version = "1.0.0" @@ -475,6 +485,16 @@ dependencies = [ "digest 0.10.5", ] +[[package]] +name = "idna" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "itertools" version = "0.10.5" @@ -523,6 +543,12 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "percent-encoding" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" + [[package]] name = "pkcs8" version = "0.9.0" @@ -802,6 +828,21 @@ dependencies = [ "syn 2.0.3", ] +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "typenum" version = "1.15.0" @@ -820,12 +861,38 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "unicode-bidi" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" + [[package]] name = "unicode-ident" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd" +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "url" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + [[package]] name = "version_check" version = "0.9.4" diff --git a/contracts/cw-law-stone/Cargo.toml b/contracts/cw-law-stone/Cargo.toml index 8307f567..c54f60e8 100644 --- a/contracts/cw-law-stone/Cargo.toml +++ b/contracts/cw-law-stone/Cargo.toml @@ -38,6 +38,7 @@ logic-bindings = { version = "0.2", path = "../../packages/logic-bindings" } schemars.workspace = true serde.workspace = true thiserror.workspace = true +url = "2.3.1" [dev-dependencies] cw-multi-test.workspace = true diff --git a/contracts/cw-law-stone/src/contract.rs b/contracts/cw-law-stone/src/contract.rs index 5807f16e..dcbcbdbc 100644 --- a/contracts/cw-law-stone/src/contract.rs +++ b/contracts/cw-law-stone/src/contract.rs @@ -104,7 +104,7 @@ pub mod reply { PROGRAM.save(deps.storage, &obj).map_err(|e| ContractError::from(e)) }) .and_then(|_| -> Result { - let req: QueryRequest = LogicCustomQuery::Ask { program: String::from_utf8(context.1.to_vec()).map_err(|e| StdError::invalid_utf8(e.to_string()))?, query: "source_files(Files).".to_string() }.into(); + let req = build_source_files_query(context.1)?.into(); deps.querier.query(&req).map_err(|e| ContractError::from(e)) }) .and_then(|res| ask_response_to_submsg(res, context.0, "Files".to_string())) @@ -116,7 +116,9 @@ pub mod reply { }) } - + fn build_source_files_query(program: Binary) -> Result { + Ok(LogicCustomQuery::Ask { program: String::from_utf8(program.to_vec()).map_err(|e| StdError::invalid_utf8(e.to_string()))?, query: "source_files(Files).".to_string() }) + } } #[cfg(test)] diff --git a/contracts/cw-law-stone/src/error.rs b/contracts/cw-law-stone/src/error.rs index e520cad5..80bca256 100644 --- a/contracts/cw-law-stone/src/error.rs +++ b/contracts/cw-law-stone/src/error.rs @@ -1,6 +1,7 @@ use cosmwasm_std::StdError; use cw_utils::ParseReplyError; use thiserror::Error; +use url::ParseError; #[derive(Error, Debug)] pub enum ContractError { @@ -21,4 +22,20 @@ pub enum ContractError { #[error("Invalid reply message: {0}")] InvalidReplyMsg(StdError), + + #[error("Failed parse dependency uri {uri:?}: {error:?}")] + DependencyUri { + error: UriError, + uri: String + } } + +#[derive(Error, Debug)] +pub enum UriError { + #[error("{0}")] + Parse(#[from] ParseError), + + #[error("Incompatible uri scheme {scheme:?}. Should be {wanted:?}")] + WrongScheme{ scheme: String, wanted: Vec } +} + diff --git a/contracts/cw-law-stone/src/helper.rs b/contracts/cw-law-stone/src/helper.rs index a23b6791..636f756c 100644 --- a/contracts/cw-law-stone/src/helper.rs +++ b/contracts/cw-law-stone/src/helper.rs @@ -1,10 +1,12 @@ use std::env::var; use cosmwasm_std::{Attribute, Event, SubMsg, to_binary, WasmMsg}; +use url::Url; use logic_bindings::{AskResponse, Substitution}; use crate::ContractError; use crate::state::Object; use cw_storage::msg::ExecuteMsg as StorageMsg; use crate::ContractError::NotImplemented; +use crate::uri::uri_to_object; pub fn get_reply_event_attribute(events: Vec, key: String) -> Option { let r = events.iter() @@ -25,10 +27,6 @@ fn filter_source_files(substitution: Substitution) -> Vec { .collect::>() } -fn uri_to_object(uri: String) -> Result { - Err(NotImplemented {}) -} - pub fn ask_response_to_submsg(res: AskResponse, storage_addr: String, variable: String) -> Result, ContractError> { let uris = res.answer .map(|a| a.results) diff --git a/contracts/cw-law-stone/src/lib.rs b/contracts/cw-law-stone/src/lib.rs index 7a23a3e3..9c2eb118 100644 --- a/contracts/cw-law-stone/src/lib.rs +++ b/contracts/cw-law-stone/src/lib.rs @@ -15,5 +15,6 @@ mod error; pub mod msg; pub mod state; mod helper; +mod uri; pub use crate::error::ContractError; diff --git a/contracts/cw-law-stone/src/uri.rs b/contracts/cw-law-stone/src/uri.rs new file mode 100644 index 00000000..3358dd16 --- /dev/null +++ b/contracts/cw-law-stone/src/uri.rs @@ -0,0 +1,14 @@ +use url::Url; +use crate::ContractError; +use crate::ContractError::{DependencyUri, NotImplemented}; +use crate::state::Object; + +// const COSMWASM_SCHEME = "cosmwasm"; + +pub fn uri_to_object(uri: String) -> Result { + // let url = Url::parse(uri.as_str()).map_err(|e| ContractError::DependencyUri { error: e.into(), uri })?; + // if url.scheme() != COSMWASM_SCHEME { + // Err(DependencyUri { error: }) + // } + Err(NotImplemented {}) +} From 3ec96b210ec7b5bfa293b4007024f08871ef5452 Mon Sep 17 00:00:00 2001 From: Benjamin DENEUX Date: Thu, 23 Mar 2023 11:22:35 +0100 Subject: [PATCH 11/17] feat(law): add uri parser to Object --- Cargo.lock | 1 + Cargo.toml | 1 + contracts/cw-law-stone/Cargo.toml | 1 + contracts/cw-law-stone/src/error.rs | 22 +++++- contracts/cw-law-stone/src/helper.rs | 4 +- contracts/cw-law-stone/src/lib.rs | 1 - contracts/cw-law-stone/src/state.rs | 108 +++++++++++++++++++++++++++ contracts/cw-law-stone/src/uri.rs | 14 ---- 8 files changed, 134 insertions(+), 18 deletions(-) delete mode 100644 contracts/cw-law-stone/src/uri.rs diff --git a/Cargo.lock b/Cargo.lock index 22e37903..49396052 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -221,6 +221,7 @@ dependencies = [ "logic-bindings", "schemars", "serde", + "serde-json-wasm", "thiserror", "url", ] diff --git a/Cargo.toml b/Cargo.toml index f6e69258..cec8cfcb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,3 +12,4 @@ cw2 = "0.15.1" schemars = "0.8.12" serde = { version = "1.0.158", default-features = false, features = ["derive"] } thiserror = { version = "1.0.40" } +serde-json-wasm = "0.5.0" diff --git a/contracts/cw-law-stone/Cargo.toml b/contracts/cw-law-stone/Cargo.toml index c54f60e8..8bcb8f8b 100644 --- a/contracts/cw-law-stone/Cargo.toml +++ b/contracts/cw-law-stone/Cargo.toml @@ -39,6 +39,7 @@ schemars.workspace = true serde.workspace = true thiserror.workspace = true url = "2.3.1" +serde-json-wasm.workspace = true [dev-dependencies] cw-multi-test.workspace = true diff --git a/contracts/cw-law-stone/src/error.rs b/contracts/cw-law-stone/src/error.rs index 80bca256..3acf9b23 100644 --- a/contracts/cw-law-stone/src/error.rs +++ b/contracts/cw-law-stone/src/error.rs @@ -1,5 +1,6 @@ use cosmwasm_std::StdError; use cw_utils::ParseReplyError; +use serde_json_wasm::de::Error; use thiserror::Error; use url::ParseError; @@ -30,12 +31,31 @@ pub enum ContractError { } } +impl ContractError { + pub fn dependency_uri(error: UriError, uri: String) -> ContractError { + ContractError::DependencyUri { + error, uri + } + } +} #[derive(Error, Debug)] pub enum UriError { #[error("{0}")] Parse(#[from] ParseError), #[error("Incompatible uri scheme {scheme:?}. Should be {wanted:?}")] - WrongScheme{ scheme: String, wanted: Vec } + WrongScheme{ scheme: String, wanted: Vec }, + + #[error("The given path doesn't correspond to a cw-storage uri")] + IncompatiblePath, + + #[error("URI doesn't contains needed query key")] + MissingQueryKey, + + #[error("{0}")] + JSONDecoding(#[from] Error), + + #[error("The given query is not compatible")] + IncompatibleQuery, } diff --git a/contracts/cw-law-stone/src/helper.rs b/contracts/cw-law-stone/src/helper.rs index 636f756c..1e5c15b6 100644 --- a/contracts/cw-law-stone/src/helper.rs +++ b/contracts/cw-law-stone/src/helper.rs @@ -6,7 +6,6 @@ use crate::ContractError; use crate::state::Object; use cw_storage::msg::ExecuteMsg as StorageMsg; use crate::ContractError::NotImplemented; -use crate::uri::uri_to_object; pub fn get_reply_event_attribute(events: Vec, key: String) -> Option { let r = events.iter() @@ -39,7 +38,8 @@ pub fn ask_response_to_submsg(res: AskResponse, storage_addr: String, variable: let mut msgs = vec![]; for uri in uris { - let object = uri_to_object(uri)?; + let url = Url::parse(uri.as_str()).map_err(|e| ContractError::dependency_uri(e.into(), uri.clone()))?; + let object = Object::try_from(url).map_err(|e| ContractError::dependency_uri(e, uri))?; let msg = WasmMsg::Execute { contract_addr: storage_addr.to_string(), msg: to_binary(&StorageMsg::PinObject { diff --git a/contracts/cw-law-stone/src/lib.rs b/contracts/cw-law-stone/src/lib.rs index 9c2eb118..7a23a3e3 100644 --- a/contracts/cw-law-stone/src/lib.rs +++ b/contracts/cw-law-stone/src/lib.rs @@ -15,6 +15,5 @@ mod error; pub mod msg; pub mod state; mod helper; -mod uri; pub use crate::error::ContractError; diff --git a/contracts/cw-law-stone/src/state.rs b/contracts/cw-law-stone/src/state.rs index fe010465..750c98f7 100644 --- a/contracts/cw-law-stone/src/state.rs +++ b/contracts/cw-law-stone/src/state.rs @@ -1,8 +1,15 @@ +use std::borrow::Cow; +use std::collections::HashMap; use cosmwasm_std::Binary; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use cw_storage_plus::{Item, Map}; +use url::Url; +use crate::ContractError; +use crate::ContractError::NotImplemented; +use crate::error::UriError; +use cw_storage::msg::QueryMsg; /// State to store context during contract instantiation pub const INSTANTIATE_CONTEXT: Item<'_, (String, Binary)> = Item::new("instantiate"); @@ -17,6 +24,107 @@ pub struct Object { pub storage_address: String, } +impl Object { + const COSMWASM_SCHEME: &'static str = "cosmwasm"; + const COSMWASM_PATH_KEY: &'static str = "cw-storage"; +} +impl TryFrom for Object { + type Error = UriError; + + fn try_from(value: Url) -> Result { + if value.scheme() != Object::COSMWASM_SCHEME { + return Err(UriError::WrongScheme { scheme: value.scheme().to_string(), wanted: vec![Object::COSMWASM_SCHEME.to_string()] }) + } + + let path = value.path().to_string(); + let paths = path.split(":").collect::>(); + if paths.len() == 0 || paths.len() > 2 { + return Err(UriError::IncompatiblePath) + } + let storage_address = paths.last().ok_or(UriError::IncompatiblePath)?.to_string(); + + let queries = value.query_pairs().into_owned().collect::>(); + + if let Some(query) = queries.get("query") { + let json: QueryMsg = serde_json_wasm::from_str(query.as_str())?; + + return match json { + QueryMsg::ObjectData { id: object_id } => Ok(Object { object_id, storage_address }), + _ => Err(UriError::IncompatibleQuery) + } + } + + Err(UriError::MissingQueryKey) + } +} + pub const PROGRAM: Item<'_, Object> = Item::new("program"); pub const DEPENDENCIES: Map<'_, &str, Object> = Map::new("dependencies"); + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn object_try_from() { + let cases = vec![ + ( + "coco:okp41dclchlcttf2uektxyryg0c6yau63eml5q9uq03myg44ml8cxpxnqavca4s?query=%7B%22object_data%22%3A%7B%22id%22%3A%20%224cbe36399aabfcc7158ee7a66cbfffa525bb0ceab33d1ff2cff08759fe0a9b05%22%7D%7D".to_string(), + Some(UriError::WrongScheme { scheme: "coco".to_string(), wanted: vec!["cosmwasm".to_string()] }), + None + ), + ( + "cosmwasm:bob:alice:okp41dclchlcttf2uektxyryg0c6yau63eml5q9uq03myg44ml8cxpxnqavca4s?query=%7B%22object_data%22%3A%7B%22id%22%3A%20%224cbe36399aabfcc7158ee7a66cbfffa525bb0ceab33d1ff2cff08759fe0a9b05%22%7D%7D".to_string(), + Some(UriError::IncompatiblePath), + None + ), + ( + "cosmwasm:cw-storage:okp41dclchlcttf2uektxyryg0c6yau63eml5q9uq03myg44ml8cxpxnqavca4s?q=%7B%22object_data%22%3A%7B%22id%22%3A%20%224cbe36399aabfcc7158ee7a66cbfffa525bb0ceab33d1ff2cff08759fe0a9b05%22%7D%7D".to_string(), + Some(UriError::MissingQueryKey), + None + ), + ( + "cosmwasm:cw-storage:okp41dclchlcttf2uektxyryg0c6yau63eml5q9uq03myg44ml8cxpxnqavca4s?query=%7B%22object%22%3A%7B%22id%22%3A%20%224cbe36399aabfcc7158ee7a66cbfffa525bb0ceab33d1ff2cff08759fe0a9b05%22%7D%7D".to_string(), + Some(UriError::IncompatibleQuery), + None + ), + ( + "cosmwasm:cw-storage:okp41dclchlcttf2uektxyryg0c6yau63eml5q9uq03myg44ml8cxpxnqavca4s?query=%7B%22object_data%22%3A%7B%22id%22%3A%20%224cbe36399aabfcc7158ee7a66cbfffa525bb0ceab33d1ff2cff08759fe0a9b05%22%7D%7D".to_string(), + None, + Some( + Object { + object_id: "4cbe36399aabfcc7158ee7a66cbfffa525bb0ceab33d1ff2cff08759fe0a9b05".to_string(), + storage_address: "okp41dclchlcttf2uektxyryg0c6yau63eml5q9uq03myg44ml8cxpxnqavca4s".to_string(), + } + ) + ), + ( + "cosmwasm:okp41dclchlcttf2uektxyryg0c6yau63eml5q9uq03myg44ml8cxpxnqavca4s?query=%7B%22object_data%22%3A%7B%22id%22%3A%20%224cbe36399aabfcc7158ee7a66cbfffa525bb0ceab33d1ff2cff08759fe0a9b05%22%7D%7D".to_string(), + None, + Some( + Object { + object_id: "4cbe36399aabfcc7158ee7a66cbfffa525bb0ceab33d1ff2cff08759fe0a9b05".to_string(), + storage_address: "okp41dclchlcttf2uektxyryg0c6yau63eml5q9uq03myg44ml8cxpxnqavca4s".to_string(), + } + ) + ), + ]; + + for case in cases { + match Url::parse(case.0.as_str()) { + Ok(url) => { + let result = Object::try_from(url); + + if let Some(err) = case.1 { + assert_eq!(err.to_string(), result.unwrap_err().to_string()) + } + else if let Some(o) = case.2 { + assert_eq!(o, result.unwrap()) + } + } + Err(_) => panic!("no error should be thrown") + } + } + } +} diff --git a/contracts/cw-law-stone/src/uri.rs b/contracts/cw-law-stone/src/uri.rs deleted file mode 100644 index 3358dd16..00000000 --- a/contracts/cw-law-stone/src/uri.rs +++ /dev/null @@ -1,14 +0,0 @@ -use url::Url; -use crate::ContractError; -use crate::ContractError::{DependencyUri, NotImplemented}; -use crate::state::Object; - -// const COSMWASM_SCHEME = "cosmwasm"; - -pub fn uri_to_object(uri: String) -> Result { - // let url = Url::parse(uri.as_str()).map_err(|e| ContractError::DependencyUri { error: e.into(), uri })?; - // if url.scheme() != COSMWASM_SCHEME { - // Err(DependencyUri { error: }) - // } - Err(NotImplemented {}) -} From 06df729421747dcc81148b620f072287115da362 Mon Sep 17 00:00:00 2001 From: Arnaud Mimart <33665250+amimart@users.noreply.github.com> Date: Thu, 23 Mar 2023 13:54:16 +0100 Subject: [PATCH 12/17] feat(law): implements object to uri mapping --- Cargo.lock | 1 + contracts/cw-law-stone/Cargo.toml | 3 +- contracts/cw-law-stone/src/state.rs | 71 ++++++++++++++++++++++------- 3 files changed, 57 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 49396052..d9ab642b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -218,6 +218,7 @@ dependencies = [ "cw-storage-plus", "cw-utils", "cw2", + "form_urlencoded", "logic-bindings", "schemars", "serde", diff --git a/contracts/cw-law-stone/Cargo.toml b/contracts/cw-law-stone/Cargo.toml index 8bcb8f8b..a0cf76de 100644 --- a/contracts/cw-law-stone/Cargo.toml +++ b/contracts/cw-law-stone/Cargo.toml @@ -30,6 +30,7 @@ rpath = false cosmwasm-schema.workspace = true cosmwasm-std.workspace = true cosmwasm-storage.workspace = true +serde-json-wasm.workspace = true cw-storage = { path = "../cw-storage" } cw-storage-plus.workspace = true cw-utils.worksapce = true @@ -39,7 +40,7 @@ schemars.workspace = true serde.workspace = true thiserror.workspace = true url = "2.3.1" -serde-json-wasm.workspace = true +form_urlencoded = "1.1.0" [dev-dependencies] cw-multi-test.workspace = true diff --git a/contracts/cw-law-stone/src/state.rs b/contracts/cw-law-stone/src/state.rs index 750c98f7..3d3e5205 100644 --- a/contracts/cw-law-stone/src/state.rs +++ b/contracts/cw-law-stone/src/state.rs @@ -1,15 +1,14 @@ -use std::borrow::Cow; -use std::collections::HashMap; -use cosmwasm_std::Binary; +use cosmwasm_std::{Binary, StdError}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; +use std::collections::HashMap; -use cw_storage_plus::{Item, Map}; -use url::Url; -use crate::ContractError; -use crate::ContractError::NotImplemented; use crate::error::UriError; +use crate::ContractError; +use cw_storage::msg::QueryMsg as StorageQuery; use cw_storage::msg::QueryMsg; +use cw_storage_plus::{Item, Map}; +use url::Url; /// State to store context during contract instantiation pub const INSTANTIATE_CONTEXT: Item<'_, (String, Binary)> = Item::new("instantiate"); @@ -26,38 +25,77 @@ pub struct Object { impl Object { const COSMWASM_SCHEME: &'static str = "cosmwasm"; - const COSMWASM_PATH_KEY: &'static str = "cw-storage"; } + impl TryFrom for Object { type Error = UriError; fn try_from(value: Url) -> Result { if value.scheme() != Object::COSMWASM_SCHEME { - return Err(UriError::WrongScheme { scheme: value.scheme().to_string(), wanted: vec![Object::COSMWASM_SCHEME.to_string()] }) + return Err(UriError::WrongScheme { + scheme: value.scheme().to_string(), + wanted: vec![Object::COSMWASM_SCHEME.to_string()], + }); } let path = value.path().to_string(); let paths = path.split(":").collect::>(); if paths.len() == 0 || paths.len() > 2 { - return Err(UriError::IncompatiblePath) + return Err(UriError::IncompatiblePath); } let storage_address = paths.last().ok_or(UriError::IncompatiblePath)?.to_string(); - let queries = value.query_pairs().into_owned().collect::>(); + let queries = value + .query_pairs() + .into_owned() + .collect::>(); if let Some(query) = queries.get("query") { let json: QueryMsg = serde_json_wasm::from_str(query.as_str())?; return match json { - QueryMsg::ObjectData { id: object_id } => Ok(Object { object_id, storage_address }), - _ => Err(UriError::IncompatibleQuery) - } + QueryMsg::ObjectData { id: object_id } => Ok(Object { + object_id, + storage_address, + }), + _ => Err(UriError::IncompatibleQuery), + }; } Err(UriError::MissingQueryKey) } } +impl TryInto for Object { + type Error = ContractError; + + fn try_into(self) -> Result { + let raw = [ + Object::COSMWASM_SCHEME, + ":cw-storage:", + self.storage_address.as_str(), + "?", + form_urlencoded::Serializer::new(String::new()) + .append_pair( + "query", + serde_json_wasm::to_string(&StorageQuery::ObjectData { id: self.object_id }) + .map_err(|e| { + ContractError::Std(StdError::serialize_err("StorageQuery", e)) + })? + .as_str(), + ) + .finish() + .as_str(), + ] + .join(""); + + Url::parse(&raw).map_err(|e| ContractError::DependencyUri { + uri: raw, + error: UriError::Parse(e), + }) + } +} + pub const PROGRAM: Item<'_, Object> = Item::new("program"); pub const DEPENDENCIES: Map<'_, &str, Object> = Map::new("dependencies"); @@ -118,12 +156,11 @@ mod tests { if let Some(err) = case.1 { assert_eq!(err.to_string(), result.unwrap_err().to_string()) - } - else if let Some(o) = case.2 { + } else if let Some(o) = case.2 { assert_eq!(o, result.unwrap()) } } - Err(_) => panic!("no error should be thrown") + Err(_) => panic!("no error should be thrown"), } } } From c27c2db1d5e2338a34513b1bdde403419e98fe19 Mon Sep 17 00:00:00 2001 From: Arnaud Mimart <33665250+amimart@users.noreply.github.com> Date: Thu, 23 Mar 2023 13:55:09 +0100 Subject: [PATCH 13/17] feat(law): implements source_files ask query --- contracts/cw-law-stone/src/contract.rs | 176 ++++++++++++++++++------- contracts/cw-law-stone/src/helper.rs | 52 ++++---- contracts/cw-law-stone/src/lib.rs | 24 ++-- 3 files changed, 170 insertions(+), 82 deletions(-) diff --git a/contracts/cw-law-stone/src/contract.rs b/contracts/cw-law-stone/src/contract.rs index dcbcbdbc..ae4f139f 100644 --- a/contracts/cw-law-stone/src/contract.rs +++ b/contracts/cw-law-stone/src/contract.rs @@ -6,14 +6,12 @@ use cosmwasm_std::{ SubMsg, WasmMsg, }; use cw2::set_contract_version; -use cw_storage::msg::{ExecuteMsg as StorageMsg, ObjectResponse}; -use cw_utils::parse_reply_execute_data; +use cw_storage::msg::ExecuteMsg as StorageMsg; use logic_bindings::LogicCustomQuery; use crate::error::ContractError; use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; use crate::state::INSTANTIATE_CONTEXT; -use crate::helper::ask_response_to_submsg; // version info for migration info const CONTRACT_NAME: &str = "crates.io:law-stone"; @@ -77,68 +75,114 @@ pub fn reply( } pub mod reply { - use cosmwasm_std::{Attribute, from_binary, QueryRequest}; - use cw_storage::msg::BucketResponse; - use cw_storage::msg::ExecuteMsg::StoreObject; - use logic_bindings::{AskResponse, Substitution}; - use crate::helper::get_reply_event_attribute; - use crate::state::{PROGRAM, Object}; use super::*; + use crate::helper::{ask_response_to_objects, get_reply_event_attribute}; + use crate::state::{Object, DEPENDENCIES, PROGRAM}; + use url::Url; pub fn store_program_reply( deps: DepsMut<'_, LogicCustomQuery>, _env: Env, msg: Reply, ) -> Result { - let context = INSTANTIATE_CONTEXT.load(deps.storage)?; - msg.result.into_result() + msg.result + .into_result() .map_err(|_| ContractError::EmptyReply) - .and_then(|e| get_reply_event_attribute(e.events, "id".to_string()).ok_or(ContractError::NoObjectId)) + .and_then(|e| { + get_reply_event_attribute(e.events, "id".to_string()) + .ok_or(ContractError::NoObjectId) + }) .map(|obj_id| Object { object_id: obj_id.to_string(), - storage_address: context.0.clone() - }) - .and_then(|obj| -> Result<(), ContractError> { - PROGRAM.save(deps.storage, &obj).map_err(|e| ContractError::from(e)) - }) - .and_then(|_| -> Result { - let req = build_source_files_query(context.1)?.into(); - deps.querier.query(&req).map_err(|e| ContractError::from(e)) + storage_address: context.0.clone(), }) - .and_then(|res| ask_response_to_submsg(res, context.0, "Files".to_string())) - .map(|msg| { + .and_then(|program| -> Result, ContractError> { + PROGRAM + .save(deps.storage, &program) + .map_err(|e| ContractError::from(e))?; + // Clean instantiate context INSTANTIATE_CONTEXT.remove(deps.storage); - Response::new() - .add_submessages(msg) + + let req = build_source_files_query(program.clone())?.into(); + let res = deps + .querier + .query(&req) + .map_err(|e| ContractError::from(e))?; + + let objects = ask_response_to_objects(res, "Files".to_string())?; + let mut msgs = Vec::with_capacity(objects.len()); + for obj in objects { + if obj.object_id == program.object_id { + continue; + } + DEPENDENCIES.save(deps.storage, obj.object_id.as_str(), &obj)?; + + msgs.push(SubMsg::new(WasmMsg::Execute { + msg: to_binary(&StorageMsg::PinObject { + id: obj.clone().object_id, + })?, + contract_addr: obj.clone().storage_address, + funds: vec![], + })); + } + + Ok(msgs) }) + .map(|msg| Response::new().add_submessages(msg)) } - fn build_source_files_query(program: Binary) -> Result { - Ok(LogicCustomQuery::Ask { program: String::from_utf8(program.to_vec()).map_err(|e| StdError::invalid_utf8(e.to_string()))?, query: "source_files(Files).".to_string() }) + pub fn build_source_files_query(program: Object) -> Result { + let program_uri: Url = program.try_into()?; + + Ok(LogicCustomQuery::Ask { + program: "source_files(Files) :- bagof(File, source_file(File), Files).".to_string(), + query: [ + "consult('", + program_uri.as_str(), + "'), source_files(Files).", + ] + .join("") + .to_string(), + }) } } #[cfg(test)] mod tests { use super::*; - use crate::state::{DEPENDENCIES, PROGRAM}; + use crate::state::{Object, DEPENDENCIES, PROGRAM}; use cosmwasm_std::testing::{mock_env, mock_info, MockQuerierCustomHandlerResult}; - use cosmwasm_std::{from_binary, to_binary, CosmosMsg, Order, SubMsgResponse, SubMsgResult, SystemError, SystemResult, Event, Attribute}; + use cosmwasm_std::{ + from_binary, to_binary, CosmosMsg, Event, Order, SubMsgResponse, SubMsgResult, SystemError, + SystemResult, + }; use logic_bindings::testing::mock::mock_dependencies_with_logic_handler; use logic_bindings::{ Answer, AskResponse, LogicCustomQuery, Result as LogicResult, Substitution, Term, }; + use url::Url; fn custom_logic_handler_with_dependencies( dependencies: Vec, + program: Object, request: &LogicCustomQuery, ) -> MockQuerierCustomHandlerResult { - let deps_name = format!("[{}]", &dependencies.join(",")); + let program_uri: Url = program.clone().try_into().unwrap(); + let mut updated_deps = dependencies.clone(); + updated_deps.push(program_uri.to_string()); + let deps_name = format!("[{}]", &updated_deps.join(",")); + let LogicCustomQuery::Ask { + program: exp_program, + query: exp_query, + .. + } = reply::build_source_files_query(program).unwrap(); match request { - LogicCustomQuery::Ask { query, .. } if query == "source_files(Files)." => { + LogicCustomQuery::Ask { program, query } + if *query == exp_query && *program == exp_program => + { SystemResult::Ok( to_binary(&AskResponse { height: 1, @@ -170,9 +214,8 @@ mod tests { #[test] fn proper_initialization() { - let mut deps = mock_dependencies_with_logic_handler(|request| { - custom_logic_handler_with_dependencies(vec![], request) - }); + let mut deps = + mock_dependencies_with_logic_handler(|_| SystemResult::Err(SystemError::Unknown {})); let program = to_binary("foo(_) :- true.").unwrap(); let msg = InstantiateMsg { @@ -204,12 +247,14 @@ mod tests { }, _ => assert!(false, "cosmos sub message should be a Wasm message execute"), } - assert_eq!("okp41ffzp0xmjhwkltuxcvccl0z9tyfuu7txp5ke0tpkcjpzuq9fcj3pqrteqt3".to_string(), - INSTANTIATE_CONTEXT.load(&deps.storage).unwrap().0); - assert_eq!(program, - INSTANTIATE_CONTEXT.load(&deps.storage).unwrap().1) + assert_eq!( + "okp41ffzp0xmjhwkltuxcvccl0z9tyfuu7txp5ke0tpkcjpzuq9fcj3pqrteqt3".to_string(), + INSTANTIATE_CONTEXT.load(&deps.storage).unwrap().0 + ); + assert_eq!(program, INSTANTIATE_CONTEXT.load(&deps.storage).unwrap().1) } + #[derive(Clone)] struct StoreTestCase { dependencies: Vec<(String, String, String)>, // URI, contract address, object id object_id: String, @@ -247,7 +292,7 @@ mod tests { "0689c526187c6785dfcce28f8df19138da292598dc19548a852de1792062f271".to_string() // object id ), ], - object_id: "0689c526187c6785dfcce28f8df19138da292598dc19548a852de1792062f271" + object_id: "1cc6de7672c97db145a3940df2264140ea893c6688fa5ca55b73cb8b68e0574d" .to_string(), }, ]; @@ -260,31 +305,49 @@ mod tests { .map(|(uri, _, _)| uri) .collect::>(), ); + let program_object_id = case.clone().object_id; let mut deps = mock_dependencies_with_logic_handler(move |request| { - custom_logic_handler_with_dependencies(uris.to_vec(), request) + custom_logic_handler_with_dependencies( + uris.to_vec(), + Object { + object_id: program_object_id.clone(), + storage_address: + "okp41dclchlcttf2uektxyryg0c6yau63eml5q9uq03myg44ml8cxpxnqavca4s" + .to_string(), + }, + request, + ) }); let reply = Reply { id: STORE_PROGRAM_REPLY_ID, result: SubMsgResult::Ok(SubMsgResponse { - events: vec![ - Event::new("e".to_string()).add_attribute("id".to_string(), case.object_id.clone()) - ], + events: vec![Event::new("e".to_string()) + .add_attribute("id".to_string(), case.clone().object_id)], data: None, }), }; // Configure the instantiate context let program = Binary::from_base64("Zm9vKF8pIDotIHRydWUu").unwrap(); - INSTANTIATE_CONTEXT.save(deps.as_mut().storage, &("okp41dclchlcttf2uektxyryg0c6yau63eml5q9uq03myg44ml8cxpxnqavca4s".to_string(), program)).unwrap(); + INSTANTIATE_CONTEXT + .save( + deps.as_mut().storage, + &( + "okp41dclchlcttf2uektxyryg0c6yau63eml5q9uq03myg44ml8cxpxnqavca4s" + .to_string(), + program, + ), + ) + .unwrap(); let response = reply::store_program_reply(deps.as_mut(), mock_env(), reply); let res = response.unwrap(); let program = PROGRAM.load(&deps.storage).unwrap(); - assert_eq!(case.object_id.clone(), program.object_id); + assert_eq!(case.clone().object_id, program.object_id); - let deps_len_requirement = case.dependencies.len(); + let deps_len_requirement = case.clone().dependencies.len(); if deps_len_requirement > 0 { assert_eq!( @@ -293,7 +356,7 @@ mod tests { .keys_raw(&deps.storage, None, None, Order::Ascending) .count() ); - for (_, contract_addr, object_id) in case.dependencies { + for (_, contract_addr, object_id) in case.clone().dependencies { let o = DEPENDENCIES.load(&deps.storage, object_id.as_str()); assert!( o.is_ok(), @@ -354,4 +417,25 @@ mod tests { } } } + + #[test] + fn build_source_files_query() { + let result = reply::build_source_files_query(Object { + object_id: "1cc6de7672c97db145a3940df2264140ea893c6688fa5ca55b73cb8b68e0574d" + .to_string(), + storage_address: "okp41ffzp0xmjhwkltuxcvccl0z9tyfuu7txp5ke0tpkcjpzuq9fcj3pqrteqt3" + .to_string(), + }); + + match result { + Ok(LogicCustomQuery::Ask { program, query }) => { + assert_eq!( + program, + "source_files(Files) :- bagof(File, source_file(File), Files)." + ); + assert_eq!(query, "consult('cosmwasm:cw-storage:okp41ffzp0xmjhwkltuxcvccl0z9tyfuu7txp5ke0tpkcjpzuq9fcj3pqrteqt3?query=%7B%22object_data%22%3A%7B%22id%22%3A%221cc6de7672c97db145a3940df2264140ea893c6688fa5ca55b73cb8b68e0574d%22%7D%7D'), source_files(Files).") + } + _ => assert!(false, "Expected Ok(LogicCustomQuery)."), + } + } } diff --git a/contracts/cw-law-stone/src/helper.rs b/contracts/cw-law-stone/src/helper.rs index 1e5c15b6..91633a19 100644 --- a/contracts/cw-law-stone/src/helper.rs +++ b/contracts/cw-law-stone/src/helper.rs @@ -1,33 +1,42 @@ -use std::env::var; -use cosmwasm_std::{Attribute, Event, SubMsg, to_binary, WasmMsg}; -use url::Url; -use logic_bindings::{AskResponse, Substitution}; -use crate::ContractError; use crate::state::Object; -use cw_storage::msg::ExecuteMsg as StorageMsg; -use crate::ContractError::NotImplemented; +use crate::ContractError; +use cosmwasm_std::Event; +use logic_bindings::{AskResponse, Substitution}; +use url::Url; pub fn get_reply_event_attribute(events: Vec, key: String) -> Option { - let r = events.iter() + let r = events + .iter() .flat_map(|e| e.attributes.clone()) .filter(|a| a.key == key) .map(|a| a.value) .collect::>(); - if r.len() > 0 { Some(r[0].clone())} else { None } + if r.len() > 0 { + Some(r[0].clone()) + } else { + None + } } /// Files terms is List atom, List is represented as String in prolog, filter to remove /// all paterm to represent the list and return the result as Vec. fn filter_source_files(substitution: Substitution) -> Vec { - substitution.term.name.split(",") + substitution + .term + .name + .split(",") .into_iter() .map(|s| s.replace(&['\'', '[', ']'], "")) .collect::>() } -pub fn ask_response_to_submsg(res: AskResponse, storage_addr: String, variable: String) -> Result, ContractError> { - let uris = res.answer +pub fn ask_response_to_objects( + res: AskResponse, + variable: String, +) -> Result, ContractError> { + let uris = res + .answer .map(|a| a.results) .unwrap_or(vec![]) .iter() @@ -36,18 +45,13 @@ pub fn ask_response_to_submsg(res: AskResponse, storage_addr: String, variable: .flat_map(|s| filter_source_files(s)) .collect::>(); - let mut msgs = vec![]; + let mut objects = vec![]; for uri in uris { - let url = Url::parse(uri.as_str()).map_err(|e| ContractError::dependency_uri(e.into(), uri.clone()))?; - let object = Object::try_from(url).map_err(|e| ContractError::dependency_uri(e, uri))?; - let msg = WasmMsg::Execute { - contract_addr: storage_addr.to_string(), - msg: to_binary(&StorageMsg::PinObject { - id: object.object_id, - })?, - funds: vec![], - }; - msgs.push(SubMsg::new(msg)) + let url = Url::parse(uri.as_str()) + .map_err(|e| ContractError::dependency_uri(e.into(), uri.clone()))?; + let object = Object::try_from(url).map_err(|e| ContractError::dependency_uri(e, uri))?; + + objects.push(object) } - return Ok(msgs) + return Ok(objects); } diff --git a/contracts/cw-law-stone/src/lib.rs b/contracts/cw-law-stone/src/lib.rs index 7a23a3e3..c4c88f15 100644 --- a/contracts/cw-law-stone/src/lib.rs +++ b/contracts/cw-law-stone/src/lib.rs @@ -1,19 +1,19 @@ -// #![forbid(unsafe_code)] -// #![deny( -// warnings, -// rust_2018_idioms, -// trivial_casts, -// trivial_numeric_casts, -// unused_lifetimes, -// unused_import_braces, -// unused_qualifications, -// unused_qualifications -// )] +#![forbid(unsafe_code)] +#![deny( + warnings, + rust_2018_idioms, + trivial_casts, + trivial_numeric_casts, + unused_lifetimes, + unused_import_braces, + unused_qualifications, + unused_qualifications +)] pub mod contract; mod error; +mod helper; pub mod msg; pub mod state; -mod helper; pub use crate::error::ContractError; From c92a9a4853f565645ed689cd044153bb36bc37c5 Mon Sep 17 00:00:00 2001 From: Arnaud Mimart <33665250+amimart@users.noreply.github.com> Date: Thu, 23 Mar 2023 14:01:43 +0100 Subject: [PATCH 14/17] refactor(law): make errors consistent --- contracts/cw-law-stone/src/contract.rs | 11 ++++++++--- contracts/cw-law-stone/src/error.rs | 18 +++--------------- contracts/cw-law-stone/src/state.rs | 2 +- 3 files changed, 12 insertions(+), 19 deletions(-) diff --git a/contracts/cw-law-stone/src/contract.rs b/contracts/cw-law-stone/src/contract.rs index ae4f139f..9fcaeb26 100644 --- a/contracts/cw-law-stone/src/contract.rs +++ b/contracts/cw-law-stone/src/contract.rs @@ -89,10 +89,15 @@ pub mod reply { msg.result .into_result() - .map_err(|_| ContractError::EmptyReply) + .map_err(|_| { + ContractError::InvalidReplyMsg(StdError::generic_err("no message in reply")) + }) .and_then(|e| { - get_reply_event_attribute(e.events, "id".to_string()) - .ok_or(ContractError::NoObjectId) + get_reply_event_attribute(e.events, "id".to_string()).ok_or( + ContractError::InvalidReplyMsg(StdError::generic_err( + "reply event doesn't contains object id", + )), + ) }) .map(|obj_id| Object { object_id: obj_id.to_string(), diff --git a/contracts/cw-law-stone/src/error.rs b/contracts/cw-law-stone/src/error.rs index 3acf9b23..2e6bc858 100644 --- a/contracts/cw-law-stone/src/error.rs +++ b/contracts/cw-law-stone/src/error.rs @@ -15,27 +15,16 @@ pub enum ContractError { #[error("{0}")] Parse(#[from] ParseReplyError), - #[error("Could not find ObjectId of stored program")] - NoObjectId, - - #[error("Empty data on reply")] - EmptyReply, - #[error("Invalid reply message: {0}")] InvalidReplyMsg(StdError), #[error("Failed parse dependency uri {uri:?}: {error:?}")] - DependencyUri { - error: UriError, - uri: String - } + LogicLoadUri { error: UriError, uri: String }, } impl ContractError { pub fn dependency_uri(error: UriError, uri: String) -> ContractError { - ContractError::DependencyUri { - error, uri - } + ContractError::LogicLoadUri { error, uri } } } #[derive(Error, Debug)] @@ -44,7 +33,7 @@ pub enum UriError { Parse(#[from] ParseError), #[error("Incompatible uri scheme {scheme:?}. Should be {wanted:?}")] - WrongScheme{ scheme: String, wanted: Vec }, + WrongScheme { scheme: String, wanted: Vec }, #[error("The given path doesn't correspond to a cw-storage uri")] IncompatiblePath, @@ -58,4 +47,3 @@ pub enum UriError { #[error("The given query is not compatible")] IncompatibleQuery, } - diff --git a/contracts/cw-law-stone/src/state.rs b/contracts/cw-law-stone/src/state.rs index 3d3e5205..b0bf86eb 100644 --- a/contracts/cw-law-stone/src/state.rs +++ b/contracts/cw-law-stone/src/state.rs @@ -89,7 +89,7 @@ impl TryInto for Object { ] .join(""); - Url::parse(&raw).map_err(|e| ContractError::DependencyUri { + Url::parse(&raw).map_err(|e| ContractError::LogicLoadUri { uri: raw, error: UriError::Parse(e), }) From bcdbd3fdc511c507157da072b14b6e8b4c10fc7c Mon Sep 17 00:00:00 2001 From: Arnaud Mimart <33665250+amimart@users.noreply.github.com> Date: Thu, 23 Mar 2023 14:15:09 +0100 Subject: [PATCH 15/17] style: make linters happy --- contracts/cw-law-stone/src/contract.rs | 37 +++++++++----------------- contracts/cw-law-stone/src/helper.rs | 18 +++++-------- contracts/cw-law-stone/src/state.rs | 4 +-- 3 files changed, 20 insertions(+), 39 deletions(-) diff --git a/contracts/cw-law-stone/src/contract.rs b/contracts/cw-law-stone/src/contract.rs index 9fcaeb26..9b81696e 100644 --- a/contracts/cw-law-stone/src/contract.rs +++ b/contracts/cw-law-stone/src/contract.rs @@ -100,22 +100,19 @@ pub mod reply { ) }) .map(|obj_id| Object { - object_id: obj_id.to_string(), + object_id: obj_id, storage_address: context.0.clone(), }) .and_then(|program| -> Result, ContractError> { PROGRAM .save(deps.storage, &program) - .map_err(|e| ContractError::from(e))?; + .map_err(ContractError::from)?; // Clean instantiate context INSTANTIATE_CONTEXT.remove(deps.storage); let req = build_source_files_query(program.clone())?.into(); - let res = deps - .querier - .query(&req) - .map_err(|e| ContractError::from(e))?; + let res = deps.querier.query(&req).map_err(ContractError::from)?; let objects = ask_response_to_objects(res, "Files".to_string())?; let mut msgs = Vec::with_capacity(objects.len()); @@ -149,8 +146,7 @@ pub mod reply { program_uri.as_str(), "'), source_files(Files).", ] - .join("") - .to_string(), + .join(""), }) } } @@ -176,7 +172,7 @@ mod tests { request: &LogicCustomQuery, ) -> MockQuerierCustomHandlerResult { let program_uri: Url = program.clone().try_into().unwrap(); - let mut updated_deps = dependencies.clone(); + let mut updated_deps = dependencies; updated_deps.push(program_uri.to_string()); let deps_name = format!("[{}]", &updated_deps.join(",")); let LogicCustomQuery::Ask { @@ -245,12 +241,12 @@ mod tests { assert_eq!(data, program); assert!(pin, "the main program should be pinned"); } - _ => assert!(false, "storage message should be a StoreObject message"), + _ => panic!("storage message should be a StoreObject message"), } } - _ => assert!(false, "wasm message should be a Storage message"), + _ => panic!("wasm message should be a Storage message"), }, - _ => assert!(false, "cosmos sub message should be a Wasm message execute"), + _ => panic!("cosmos sub message should be a Wasm message execute"), } assert_eq!( "okp41ffzp0xmjhwkltuxcvccl0z9tyfuu7txp5ke0tpkcjpzuq9fcj3pqrteqt3".to_string(), @@ -395,21 +391,12 @@ mod tests { let result: StorageMsg = from_binary(msg).unwrap(); match result { StorageMsg::PinObject { id } => Some(id), - _ => { - assert!(false, "should contains only PinObject message(s)"); - None - } + _ => panic!("should contains only PinObject message(s)"), } } - _ => { - assert!(false, "wasm message should be a Storage message"); - None - } + _ => panic!("wasm message should be a Storage message"), }, - _ => { - assert!(false, "cosmos sub message should be a Wasm message execute"); - None - } + _ => panic!("cosmos sub message should be a Wasm message execute"), } }) .collect(); @@ -440,7 +427,7 @@ mod tests { ); assert_eq!(query, "consult('cosmwasm:cw-storage:okp41ffzp0xmjhwkltuxcvccl0z9tyfuu7txp5ke0tpkcjpzuq9fcj3pqrteqt3?query=%7B%22object_data%22%3A%7B%22id%22%3A%221cc6de7672c97db145a3940df2264140ea893c6688fa5ca55b73cb8b68e0574d%22%7D%7D'), source_files(Files).") } - _ => assert!(false, "Expected Ok(LogicCustomQuery)."), + _ => panic!("Expected Ok(LogicCustomQuery)."), } } } diff --git a/contracts/cw-law-stone/src/helper.rs b/contracts/cw-law-stone/src/helper.rs index 91633a19..7d518bda 100644 --- a/contracts/cw-law-stone/src/helper.rs +++ b/contracts/cw-law-stone/src/helper.rs @@ -5,18 +5,12 @@ use logic_bindings::{AskResponse, Substitution}; use url::Url; pub fn get_reply_event_attribute(events: Vec, key: String) -> Option { - let r = events + return events .iter() .flat_map(|e| e.attributes.clone()) .filter(|a| a.key == key) .map(|a| a.value) - .collect::>(); - - if r.len() > 0 { - Some(r[0].clone()) - } else { - None - } + .next(); } /// Files terms is List atom, List is represented as String in prolog, filter to remove @@ -25,9 +19,9 @@ fn filter_source_files(substitution: Substitution) -> Vec { substitution .term .name - .split(",") + .split(',') .into_iter() - .map(|s| s.replace(&['\'', '[', ']'], "")) + .map(|s| s.replace(['\'', '[', ']'], "")) .collect::>() } @@ -42,7 +36,7 @@ pub fn ask_response_to_objects( .iter() .flat_map(|result| result.substitutions.clone()) .filter(|s| s.variable == variable) - .flat_map(|s| filter_source_files(s)) + .flat_map(filter_source_files) .collect::>(); let mut objects = vec![]; @@ -53,5 +47,5 @@ pub fn ask_response_to_objects( objects.push(object) } - return Ok(objects); + Ok(objects) } diff --git a/contracts/cw-law-stone/src/state.rs b/contracts/cw-law-stone/src/state.rs index b0bf86eb..dd8de1e4 100644 --- a/contracts/cw-law-stone/src/state.rs +++ b/contracts/cw-law-stone/src/state.rs @@ -39,8 +39,8 @@ impl TryFrom for Object { } let path = value.path().to_string(); - let paths = path.split(":").collect::>(); - if paths.len() == 0 || paths.len() > 2 { + let paths = path.split(':').collect::>(); + if paths.is_empty() || paths.len() > 2 { return Err(UriError::IncompatiblePath); } let storage_address = paths.last().ok_or(UriError::IncompatiblePath)?.to_string(); From 3a4ede79d2cd67c9426f6fe19598c4b11220f208 Mon Sep 17 00:00:00 2001 From: Benjamin DENEUX Date: Thu, 23 Mar 2023 14:58:17 +0100 Subject: [PATCH 16/17] test(law): remove program from instantiate context --- Cargo.toml | 2 +- contracts/cw-law-stone/Cargo.toml | 4 ++-- contracts/cw-law-stone/src/contract.rs | 16 ++++++---------- contracts/cw-law-stone/src/state.rs | 4 ++-- 4 files changed, 11 insertions(+), 15 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index cec8cfcb..35e4df65 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,5 +11,5 @@ cw-utils = "1.0.1" cw2 = "0.15.1" schemars = "0.8.12" serde = { version = "1.0.158", default-features = false, features = ["derive"] } -thiserror = { version = "1.0.40" } serde-json-wasm = "0.5.0" +thiserror = { version = "1.0.40" } diff --git a/contracts/cw-law-stone/Cargo.toml b/contracts/cw-law-stone/Cargo.toml index a0cf76de..d25eece2 100644 --- a/contracts/cw-law-stone/Cargo.toml +++ b/contracts/cw-law-stone/Cargo.toml @@ -30,17 +30,17 @@ rpath = false cosmwasm-schema.workspace = true cosmwasm-std.workspace = true cosmwasm-storage.workspace = true -serde-json-wasm.workspace = true cw-storage = { path = "../cw-storage" } cw-storage-plus.workspace = true cw-utils.worksapce = true cw2.workspace = true +form_urlencoded = "1.1.0" logic-bindings = { version = "0.2", path = "../../packages/logic-bindings" } schemars.workspace = true +serde-json-wasm.workspace = true serde.workspace = true thiserror.workspace = true url = "2.3.1" -form_urlencoded = "1.1.0" [dev-dependencies] cw-multi-test.workspace = true diff --git a/contracts/cw-law-stone/src/contract.rs b/contracts/cw-law-stone/src/contract.rs index 9b81696e..674dc1af 100644 --- a/contracts/cw-law-stone/src/contract.rs +++ b/contracts/cw-law-stone/src/contract.rs @@ -39,7 +39,7 @@ pub fn instantiate( funds: vec![], }; - INSTANTIATE_CONTEXT.save(deps.storage, &(msg.storage_address, msg.program))?; + INSTANTIATE_CONTEXT.save(deps.storage, &msg.storage_address)?; Ok(Response::new().add_submessage(SubMsg::reply_on_success( store_program_msg, @@ -101,7 +101,7 @@ pub mod reply { }) .map(|obj_id| Object { object_id: obj_id, - storage_address: context.0.clone(), + storage_address: context.clone(), }) .and_then(|program| -> Result, ContractError> { PROGRAM @@ -250,9 +250,8 @@ mod tests { } assert_eq!( "okp41ffzp0xmjhwkltuxcvccl0z9tyfuu7txp5ke0tpkcjpzuq9fcj3pqrteqt3".to_string(), - INSTANTIATE_CONTEXT.load(&deps.storage).unwrap().0 + INSTANTIATE_CONTEXT.load(&deps.storage).unwrap() ); - assert_eq!(program, INSTANTIATE_CONTEXT.load(&deps.storage).unwrap().1) } #[derive(Clone)] @@ -330,15 +329,10 @@ mod tests { }; // Configure the instantiate context - let program = Binary::from_base64("Zm9vKF8pIDotIHRydWUu").unwrap(); INSTANTIATE_CONTEXT .save( deps.as_mut().storage, - &( - "okp41dclchlcttf2uektxyryg0c6yau63eml5q9uq03myg44ml8cxpxnqavca4s" - .to_string(), - program, - ), + &"okp41dclchlcttf2uektxyryg0c6yau63eml5q9uq03myg44ml8cxpxnqavca4s".to_string(), ) .unwrap(); @@ -407,6 +401,8 @@ mod tests { "each dependencies should be pinned by a PinObject message" ) } + + assert!(INSTANTIATE_CONTEXT.load(&deps.storage).is_err(), "the instantiate context should be cleaned at the end") } } diff --git a/contracts/cw-law-stone/src/state.rs b/contracts/cw-law-stone/src/state.rs index dd8de1e4..8e8bf066 100644 --- a/contracts/cw-law-stone/src/state.rs +++ b/contracts/cw-law-stone/src/state.rs @@ -1,4 +1,4 @@ -use cosmwasm_std::{Binary, StdError}; +use cosmwasm_std::StdError; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use std::collections::HashMap; @@ -11,7 +11,7 @@ use cw_storage_plus::{Item, Map}; use url::Url; /// State to store context during contract instantiation -pub const INSTANTIATE_CONTEXT: Item<'_, (String, Binary)> = Item::new("instantiate"); +pub const INSTANTIATE_CONTEXT: Item<'_, String> = Item::new("instantiate"); /// Represent a link to an Object stored in the `cw-storage` contract. #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] From b21e1c8c4cbb58698b23e5e4b2984180130db049 Mon Sep 17 00:00:00 2001 From: Arnaud Mimart <33665250+amimart@users.noreply.github.com> Date: Thu, 23 Mar 2023 15:08:26 +0100 Subject: [PATCH 17/17] style(law): make linter happy --- contracts/cw-law-stone/src/contract.rs | 11 +++++++---- contracts/cw-law-stone/src/helper.rs | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/contracts/cw-law-stone/src/contract.rs b/contracts/cw-law-stone/src/contract.rs index 674dc1af..900c3df2 100644 --- a/contracts/cw-law-stone/src/contract.rs +++ b/contracts/cw-law-stone/src/contract.rs @@ -93,11 +93,11 @@ pub mod reply { ContractError::InvalidReplyMsg(StdError::generic_err("no message in reply")) }) .and_then(|e| { - get_reply_event_attribute(e.events, "id".to_string()).ok_or( + get_reply_event_attribute(e.events, "id".to_string()).ok_or_else(|| { ContractError::InvalidReplyMsg(StdError::generic_err( "reply event doesn't contains object id", - )), - ) + )) + }) }) .map(|obj_id| Object { object_id: obj_id, @@ -402,7 +402,10 @@ mod tests { ) } - assert!(INSTANTIATE_CONTEXT.load(&deps.storage).is_err(), "the instantiate context should be cleaned at the end") + assert!( + INSTANTIATE_CONTEXT.load(&deps.storage).is_err(), + "the instantiate context should be cleaned at the end" + ) } } diff --git a/contracts/cw-law-stone/src/helper.rs b/contracts/cw-law-stone/src/helper.rs index 7d518bda..0ca3ca25 100644 --- a/contracts/cw-law-stone/src/helper.rs +++ b/contracts/cw-law-stone/src/helper.rs @@ -32,7 +32,7 @@ pub fn ask_response_to_objects( let uris = res .answer .map(|a| a.results) - .unwrap_or(vec![]) + .unwrap_or_default() .iter() .flat_map(|result| result.substitutions.clone()) .filter(|s| s.variable == variable)