From 2071068daae2b41ba21acf53167645ef1ada391f Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Fri, 10 Sep 2021 11:16:34 +0200 Subject: [PATCH 1/4] Add query helpers to Item and Map and use them in cw4 helpers --- Cargo.lock | 1 + packages/cw4/Cargo.toml | 1 + packages/cw4/src/helpers.rs | 44 +++++-------------------------- packages/storage-plus/src/item.rs | 14 +++++++++- packages/storage-plus/src/map.rs | 40 +++++++++++++++++++++++++++- packages/storage-plus/src/path.rs | 2 +- 6 files changed, 62 insertions(+), 40 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bfd3f5f5e..9df23dc96 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -592,6 +592,7 @@ version = "0.8.1" dependencies = [ "cosmwasm-schema", "cosmwasm-std", + "cw-storage-plus 0.8.1", "schemars", "serde", ] diff --git a/packages/cw4/Cargo.toml b/packages/cw4/Cargo.toml index 5b486300e..10834945c 100644 --- a/packages/cw4/Cargo.toml +++ b/packages/cw4/Cargo.toml @@ -10,6 +10,7 @@ homepage = "https://cosmwasm.com" documentation = "https://docs.cosmwasm.com" [dependencies] +cw-storage-plus = { path = "../storage-plus", version = "0.8.1" } cosmwasm-std = { version = "0.16.0" } schemars = "0.8.1" serde = { version = "1.0.103", default-features = false, features = ["derive"] } diff --git a/packages/cw4/src/helpers.rs b/packages/cw4/src/helpers.rs index 0f4490356..2511d75b3 100644 --- a/packages/cw4/src/helpers.rs +++ b/packages/cw4/src/helpers.rs @@ -2,15 +2,15 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use cosmwasm_std::{ - from_slice, to_binary, to_vec, Addr, Binary, ContractResult, CosmosMsg, Empty, QuerierWrapper, - QueryRequest, StdError, StdResult, SystemResult, WasmMsg, WasmQuery, + to_binary, Addr, CosmosMsg, Empty, QuerierWrapper, QueryRequest, StdResult, WasmMsg, WasmQuery, }; use crate::msg::Cw4ExecuteMsg; use crate::query::HooksResponse; use crate::{ - member_key, AdminResponse, Cw4QueryMsg, Member, MemberListResponse, MemberResponse, TOTAL_KEY, + AdminResponse, Cw4QueryMsg, Member, MemberListResponse, MemberResponse, MEMBERS_KEY, TOTAL_KEY, }; +use cw_storage_plus::{Item, Map}; /// Cw4Contract is a wrapper around Addr that provides a lot of helpers /// for working with cw4 contracts @@ -62,14 +62,6 @@ impl Cw4Contract { .into()) } - fn encode_raw_query>(&self, key: T) -> QueryRequest { - WasmQuery::Raw { - contract_addr: self.addr().into(), - key: key.into(), - } - .into() - } - /// Show the hooks pub fn hooks(&self, querier: &QuerierWrapper) -> StdResult> { let query = self.encode_smart_query(Cw4QueryMsg::Hooks {})?; @@ -79,36 +71,14 @@ impl Cw4Contract { /// Read the total weight pub fn total_weight(&self, querier: &QuerierWrapper) -> StdResult { - let query = self.encode_raw_query(TOTAL_KEY.as_bytes()); - querier.query(&query) + const TOTAL: Item = Item::new(TOTAL_KEY); + TOTAL.query(querier, self.addr()) } /// Check if this address is a member, and if so, with which weight pub fn is_member(&self, querier: &QuerierWrapper, addr: &Addr) -> StdResult> { - let path = member_key(addr.as_ref()); - let query = self.encode_raw_query(path); - - // We have to copy the logic of Querier.query to handle the empty case, and not - // try to decode empty result into a u64. - // TODO: add similar API on Querier - this is not the first time I came across it - let raw = to_vec(&query)?; - match querier.raw_query(&raw) { - SystemResult::Err(system_err) => Err(StdError::generic_err(format!( - "Querier system error: {}", - system_err - ))), - SystemResult::Ok(ContractResult::Err(contract_err)) => Err(StdError::generic_err( - format!("Querier contract error: {}", contract_err), - )), - SystemResult::Ok(ContractResult::Ok(value)) => { - // This is the only place we customize - if value.is_empty() { - Ok(None) - } else { - from_slice(&value) - } - } - } + const MEMBERS: Map<&Addr, u64> = Map::new(MEMBERS_KEY); + MEMBERS.query(querier, self.addr(), addr) } /// Return the member's weight at the given snapshot - requires a smart query diff --git a/packages/storage-plus/src/item.rs b/packages/storage-plus/src/item.rs index a4a47b410..93b202bbf 100644 --- a/packages/storage-plus/src/item.rs +++ b/packages/storage-plus/src/item.rs @@ -2,7 +2,7 @@ use serde::de::DeserializeOwned; use serde::Serialize; use std::marker::PhantomData; -use cosmwasm_std::{to_vec, StdError, StdResult, Storage}; +use cosmwasm_std::{to_vec, Addr, QuerierWrapper, StdError, StdResult, Storage, WasmQuery}; use crate::helpers::{may_deserialize, must_deserialize}; @@ -72,6 +72,18 @@ where self.save(store, &output)?; Ok(output) } + + /// If you import the proper Item from the remote contract, this will let you read the data + /// from a remote contract in a type-safe way using WasmQuery::RawQuery. + /// + /// Note that we expect an Item to be set, and error if there is no data there + pub fn query(&self, querier: &QuerierWrapper, remote_contract: Addr) -> StdResult { + let request = WasmQuery::Raw { + contract_addr: remote_contract.into(), + key: self.storage_key.into(), + }; + querier.query(&request.into()) + } } #[cfg(test)] diff --git a/packages/storage-plus/src/map.rs b/packages/storage-plus/src/map.rs index fa5f4fd7c..42046e40f 100644 --- a/packages/storage-plus/src/map.rs +++ b/packages/storage-plus/src/map.rs @@ -8,7 +8,10 @@ use crate::keys::{EmptyPrefix, Prefixer}; use crate::path::Path; #[cfg(feature = "iterator")] use crate::prefix::{Bound, Prefix}; -use cosmwasm_std::{StdError, StdResult, Storage}; +use cosmwasm_std::{ + from_slice, to_vec, Addr, ContractResult, Empty, QuerierWrapper, QueryRequest, StdError, + StdResult, Storage, SystemResult, WasmQuery, +}; #[derive(Debug, Clone)] pub struct Map<'a, K, T> { @@ -83,6 +86,41 @@ where { self.key(k).update(store, action) } + + /// If you import the proper Map from the remote contract, this will let you read the data + /// from a remote contract in a type-safe way using WasmQuery::RawQuery + pub fn query( + &self, + querier: &QuerierWrapper, + remote_contract: Addr, + k: K, + ) -> StdResult> { + let key = self.key(k).storage_key.into(); + let request: QueryRequest = WasmQuery::Raw { + contract_addr: remote_contract.into(), + key, + } + .into(); + + let raw = to_vec(&request).map_err(|serialize_err| { + StdError::generic_err(format!("Serializing QueryRequest: {}", serialize_err)) + })?; + let result = match querier.raw_query(&raw) { + SystemResult::Err(system_err) => Err(StdError::generic_err(format!( + "Querier system error: {}", + system_err + ))), + SystemResult::Ok(ContractResult::Err(contract_err)) => Err(StdError::generic_err( + format!("Querier contract error: {}", contract_err), + )), + SystemResult::Ok(ContractResult::Ok(value)) => Ok(value), + }?; + if result.is_empty() { + Ok(None) + } else { + from_slice(&result).map(Some) + } + } } // short-cut for simple keys, rather than .prefix(()).range(...) diff --git a/packages/storage-plus/src/path.rs b/packages/storage-plus/src/path.rs index ca1db9d38..cfae9b6de 100644 --- a/packages/storage-plus/src/path.rs +++ b/packages/storage-plus/src/path.rs @@ -12,7 +12,7 @@ where T: Serialize + DeserializeOwned, { /// all namespaces prefixes and concatenated with the key - storage_key: Vec, + pub(crate) storage_key: Vec, // see https://doc.rust-lang.org/std/marker/struct.PhantomData.html#unused-type-parameters for why this is needed data: PhantomData, } From ed645ab0f05ee29e31e30f2f4ecbc30db4cadf66 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Fri, 10 Sep 2021 11:46:32 +0200 Subject: [PATCH 2/4] Simplify helper methods --- packages/cw4/src/helpers.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/cw4/src/helpers.rs b/packages/cw4/src/helpers.rs index 2511d75b3..e278409ea 100644 --- a/packages/cw4/src/helpers.rs +++ b/packages/cw4/src/helpers.rs @@ -71,14 +71,12 @@ impl Cw4Contract { /// Read the total weight pub fn total_weight(&self, querier: &QuerierWrapper) -> StdResult { - const TOTAL: Item = Item::new(TOTAL_KEY); - TOTAL.query(querier, self.addr()) + Item::new(TOTAL_KEY).query(querier, self.addr()) } /// Check if this address is a member, and if so, with which weight pub fn is_member(&self, querier: &QuerierWrapper, addr: &Addr) -> StdResult> { - const MEMBERS: Map<&Addr, u64> = Map::new(MEMBERS_KEY); - MEMBERS.query(querier, self.addr(), addr) + Map::new(MEMBERS_KEY).query(querier, self.addr(), addr) } /// Return the member's weight at the given snapshot - requires a smart query From 769e08b5e73c8233e65fcc5626ff651fccd02775 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 13 Sep 2021 13:07:50 +0200 Subject: [PATCH 3/4] Pull out helper to make Map::query less verbose --- packages/storage-plus/src/map.rs | 52 +++++++++++++++++++------------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/packages/storage-plus/src/map.rs b/packages/storage-plus/src/map.rs index 42046e40f..74947432f 100644 --- a/packages/storage-plus/src/map.rs +++ b/packages/storage-plus/src/map.rs @@ -9,8 +9,8 @@ use crate::path::Path; #[cfg(feature = "iterator")] use crate::prefix::{Bound, Prefix}; use cosmwasm_std::{ - from_slice, to_vec, Addr, ContractResult, Empty, QuerierWrapper, QueryRequest, StdError, - StdResult, Storage, SystemResult, WasmQuery, + from_slice, to_vec, Addr, Binary, ContractResult, Empty, QuerierWrapper, QueryRequest, + StdError, StdResult, Storage, SystemResult, WasmQuery, }; #[derive(Debug, Clone)] @@ -96,25 +96,7 @@ where k: K, ) -> StdResult> { let key = self.key(k).storage_key.into(); - let request: QueryRequest = WasmQuery::Raw { - contract_addr: remote_contract.into(), - key, - } - .into(); - - let raw = to_vec(&request).map_err(|serialize_err| { - StdError::generic_err(format!("Serializing QueryRequest: {}", serialize_err)) - })?; - let result = match querier.raw_query(&raw) { - SystemResult::Err(system_err) => Err(StdError::generic_err(format!( - "Querier system error: {}", - system_err - ))), - SystemResult::Ok(ContractResult::Err(contract_err)) => Err(StdError::generic_err( - format!("Querier contract error: {}", contract_err), - )), - SystemResult::Ok(ContractResult::Ok(value)) => Ok(value), - }?; + let result = query_raw(querier, remote_contract, key)?; if result.is_empty() { Ok(None) } else { @@ -123,6 +105,34 @@ where } } +// TODO: move this to a better helpers location +pub(crate) fn query_raw( + querier: &QuerierWrapper, + contract_addr: Addr, + key: Binary, +) -> StdResult { + let request: QueryRequest = WasmQuery::Raw { + contract_addr: contract_addr.into(), + key, + } + .into(); + + let raw = to_vec(&request).map_err(|serialize_err| { + StdError::generic_err(format!("Serializing QueryRequest: {}", serialize_err)) + })?; + match querier.raw_query(&raw) { + SystemResult::Err(system_err) => Err(StdError::generic_err(format!( + "Querier system error: {}", + system_err + ))), + SystemResult::Ok(ContractResult::Err(contract_err)) => Err(StdError::generic_err(format!( + "Querier contract error: {}", + contract_err + ))), + SystemResult::Ok(ContractResult::Ok(value)) => Ok(value), + } +} + // short-cut for simple keys, rather than .prefix(()).range(...) #[cfg(feature = "iterator")] impl<'a, K, T> Map<'a, K, T> From b2fe9bb10040595572be57afcc8f87beb714e1bf Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 13 Sep 2021 14:45:10 +0200 Subject: [PATCH 4/4] Properly pulled out helper function --- packages/storage-plus/src/helpers.rs | 36 +++++++++++++++++++++++++++- packages/storage-plus/src/map.rs | 34 ++------------------------ 2 files changed, 37 insertions(+), 33 deletions(-) diff --git a/packages/storage-plus/src/helpers.rs b/packages/storage-plus/src/helpers.rs index 8841bfa7a..6cc25755f 100644 --- a/packages/storage-plus/src/helpers.rs +++ b/packages/storage-plus/src/helpers.rs @@ -7,7 +7,10 @@ use serde::de::DeserializeOwned; use std::any::type_name; -use cosmwasm_std::{from_slice, StdError, StdResult}; +use cosmwasm_std::{ + from_slice, to_vec, Addr, Binary, ContractResult, Empty, QuerierWrapper, QueryRequest, + StdError, StdResult, SystemResult, WasmQuery, +}; /// may_deserialize parses json bytes from storage (Option), returning Ok(None) if no data present /// @@ -84,6 +87,37 @@ pub(crate) fn encode_length(namespace: &[u8]) -> [u8; 2] { [length_bytes[2], length_bytes[3]] } +/// Use this in Map/SnapshotMap/etc when you want to provide a QueryRaw helper. +/// This is similar to querier.query(WasmQuery::Raw{}), except it does NOT parse the +/// result, but return a possibly empty Binary to be handled by the calling code. +/// That is essential to handle b"" as None. +pub(crate) fn query_raw( + querier: &QuerierWrapper, + contract_addr: Addr, + key: Binary, +) -> StdResult { + let request: QueryRequest = WasmQuery::Raw { + contract_addr: contract_addr.into(), + key, + } + .into(); + + let raw = to_vec(&request).map_err(|serialize_err| { + StdError::generic_err(format!("Serializing QueryRequest: {}", serialize_err)) + })?; + match querier.raw_query(&raw) { + SystemResult::Err(system_err) => Err(StdError::generic_err(format!( + "Querier system error: {}", + system_err + ))), + SystemResult::Ok(ContractResult::Err(contract_err)) => Err(StdError::generic_err(format!( + "Querier contract error: {}", + contract_err + ))), + SystemResult::Ok(ContractResult::Ok(value)) => Ok(value), + } +} + #[cfg(test)] mod test { use super::*; diff --git a/packages/storage-plus/src/map.rs b/packages/storage-plus/src/map.rs index 74947432f..04857ee7e 100644 --- a/packages/storage-plus/src/map.rs +++ b/packages/storage-plus/src/map.rs @@ -2,16 +2,14 @@ use serde::de::DeserializeOwned; use serde::Serialize; use std::marker::PhantomData; +use crate::helpers::query_raw; use crate::keys::PrimaryKey; #[cfg(feature = "iterator")] use crate::keys::{EmptyPrefix, Prefixer}; use crate::path::Path; #[cfg(feature = "iterator")] use crate::prefix::{Bound, Prefix}; -use cosmwasm_std::{ - from_slice, to_vec, Addr, Binary, ContractResult, Empty, QuerierWrapper, QueryRequest, - StdError, StdResult, Storage, SystemResult, WasmQuery, -}; +use cosmwasm_std::{from_slice, Addr, QuerierWrapper, StdError, StdResult, Storage}; #[derive(Debug, Clone)] pub struct Map<'a, K, T> { @@ -105,34 +103,6 @@ where } } -// TODO: move this to a better helpers location -pub(crate) fn query_raw( - querier: &QuerierWrapper, - contract_addr: Addr, - key: Binary, -) -> StdResult { - let request: QueryRequest = WasmQuery::Raw { - contract_addr: contract_addr.into(), - key, - } - .into(); - - let raw = to_vec(&request).map_err(|serialize_err| { - StdError::generic_err(format!("Serializing QueryRequest: {}", serialize_err)) - })?; - match querier.raw_query(&raw) { - SystemResult::Err(system_err) => Err(StdError::generic_err(format!( - "Querier system error: {}", - system_err - ))), - SystemResult::Ok(ContractResult::Err(contract_err)) => Err(StdError::generic_err(format!( - "Querier contract error: {}", - contract_err - ))), - SystemResult::Ok(ContractResult::Ok(value)) => Ok(value), - } -} - // short-cut for simple keys, rather than .prefix(()).range(...) #[cfg(feature = "iterator")] impl<'a, K, T> Map<'a, K, T>