From 72ecc3226134f4a60ec965baca47d6b400f7f240 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 8 Mar 2021 21:58:47 +0100 Subject: [PATCH 1/3] migrate cw1-whitelist --- Cargo.lock | 2 +- contracts/cw1-whitelist/Cargo.toml | 2 +- contracts/cw1-whitelist/src/contract.rs | 16 ++++++++-------- contracts/cw1-whitelist/src/state.rs | 15 +++------------ 4 files changed, 13 insertions(+), 22 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 96ea178b7..8edd3d8d9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -438,7 +438,7 @@ version = "0.5.0" dependencies = [ "cosmwasm-schema", "cosmwasm-std", - "cosmwasm-storage", + "cw-storage-plus", "cw0", "cw1", "cw2", diff --git a/contracts/cw1-whitelist/Cargo.toml b/contracts/cw1-whitelist/Cargo.toml index 3de6b6767..4d2800f10 100644 --- a/contracts/cw1-whitelist/Cargo.toml +++ b/contracts/cw1-whitelist/Cargo.toml @@ -22,7 +22,7 @@ cw0 = { path = "../../packages/cw0", version = "0.5.0" } cw1 = { path = "../../packages/cw1", version = "0.5.0" } cw2 = { path = "../../packages/cw2", version = "0.5.0" } cosmwasm-std = { version = "0.14.0-alpha2", features = ["iterator"] } -cosmwasm-storage = { version = "0.14.0-alpha2", features = ["iterator"] } +cw-storage-plus = { path = "../../packages/storage-plus", version = "0.5.0", features = ["iterator"] } schemars = "0.7" serde = { version = "1.0.103", default-features = false, features = ["derive"] } thiserror = { version = "1.0.20" } diff --git a/contracts/cw1-whitelist/src/contract.rs b/contracts/cw1-whitelist/src/contract.rs index ef77d6390..dff5a9a40 100644 --- a/contracts/cw1-whitelist/src/contract.rs +++ b/contracts/cw1-whitelist/src/contract.rs @@ -10,7 +10,7 @@ use cw2::set_contract_version; use crate::error::ContractError; use crate::msg::{AdminListResponse, HandleMsg, InitMsg, QueryMsg}; -use crate::state::{admin_list, admin_list_read, AdminList}; +use crate::state::{AdminList, ADMIN_LIST}; // version info for migration info const CONTRACT_NAME: &str = "crates.io:cw1-whitelist"; @@ -22,7 +22,7 @@ pub fn init(deps: DepsMut, _env: Env, _info: MessageInfo, msg: InitMsg) -> StdRe admins: map_canonical(deps.api, &msg.admins)?, mutable: msg.mutable, }; - admin_list(deps.storage).save(&cfg)?; + ADMIN_LIST.save(deps.storage, &cfg)?; Ok(Response::default()) } @@ -76,12 +76,12 @@ pub fn execute_freeze( _env: Env, info: MessageInfo, ) -> Result { - let mut cfg = admin_list_read(deps.storage).load()?; + let mut cfg = ADMIN_LIST.load(deps.storage)?; if !cfg.can_modify(&deps.api.canonical_address(&info.sender)?) { Err(ContractError::Unauthorized {}) } else { cfg.mutable = false; - admin_list(deps.storage).save(&cfg)?; + ADMIN_LIST.save(deps.storage, &cfg)?; let mut res = Response::default(); res.attributes = vec![attr("action", "freeze")]; @@ -95,12 +95,12 @@ pub fn execute_update_admins( info: MessageInfo, admins: Vec, ) -> Result { - let mut cfg = admin_list_read(deps.storage).load()?; + let mut cfg = ADMIN_LIST.load(deps.storage)?; if !cfg.can_modify(&deps.api.canonical_address(&info.sender)?) { Err(ContractError::Unauthorized {}) } else { cfg.admins = map_canonical(deps.api, &admins)?; - admin_list(deps.storage).save(&cfg)?; + ADMIN_LIST.save(deps.storage, &cfg)?; let mut res = Response::default(); res.attributes = vec![attr("action", "update_admins")]; @@ -109,7 +109,7 @@ pub fn execute_update_admins( } fn can_execute(deps: Deps, sender: &HumanAddr) -> StdResult { - let cfg = admin_list_read(deps.storage).load()?; + let cfg = ADMIN_LIST.load(deps.storage)?; let can = cfg.is_admin(&deps.api.canonical_address(sender)?); Ok(can) } @@ -122,7 +122,7 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { } pub fn query_admin_list(deps: Deps) -> StdResult { - let cfg = admin_list_read(deps.storage).load()?; + let cfg = ADMIN_LIST.load(deps.storage)?; Ok(AdminListResponse { admins: map_human(deps.api, &cfg.admins)?, mutable: cfg.mutable, diff --git a/contracts/cw1-whitelist/src/state.rs b/contracts/cw1-whitelist/src/state.rs index 8c359cd8c..508c6a473 100644 --- a/contracts/cw1-whitelist/src/state.rs +++ b/contracts/cw1-whitelist/src/state.rs @@ -1,8 +1,8 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use cosmwasm_std::{CanonicalAddr, Storage}; -use cosmwasm_storage::{singleton, singleton_read, ReadonlySingleton, Singleton}; +use cosmwasm_std::CanonicalAddr; +use cw_storage_plus::Item; #[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug, Default)] pub struct AdminList { @@ -22,16 +22,7 @@ impl AdminList { } } -pub const ADMIN_LIST_KEY: &[u8] = b"admin_list"; - -// config is all config information -pub fn admin_list(storage: &mut dyn Storage) -> Singleton { - singleton(storage, ADMIN_LIST_KEY) -} - -pub fn admin_list_read(storage: &dyn Storage) -> ReadonlySingleton { - singleton_read(storage, ADMIN_LIST_KEY) -} +pub const ADMIN_LIST: Item = Item::new("admin_list"); #[cfg(test)] mod tests { From e322fe64446ef44bba14350c57173155c51f8d66 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 8 Mar 2021 22:09:43 +0100 Subject: [PATCH 2/3] Convert cw1-subkeys (mostly) --- Cargo.lock | 12 +--- contracts/cw1-subkeys/Cargo.toml | 2 +- contracts/cw1-subkeys/src/contract.rs | 81 +++++++++++++-------------- contracts/cw1-subkeys/src/state.rs | 30 +--------- 4 files changed, 44 insertions(+), 81 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8edd3d8d9..c77fb96df 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -187,16 +187,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "cosmwasm-storage" -version = "0.14.0-alpha2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f41a29ea7e55465ce7a1d691ffa976d78925b3a6609139f8f2a85c191cfea56" -dependencies = [ - "cosmwasm-std", - "serde", -] - [[package]] name = "cosmwasm-vm" version = "0.14.0-alpha2" @@ -422,7 +412,7 @@ version = "0.5.0" dependencies = [ "cosmwasm-schema", "cosmwasm-std", - "cosmwasm-storage", + "cw-storage-plus", "cw0", "cw1", "cw1-whitelist", diff --git a/contracts/cw1-subkeys/Cargo.toml b/contracts/cw1-subkeys/Cargo.toml index bbef225ee..f6c4b3fa1 100644 --- a/contracts/cw1-subkeys/Cargo.toml +++ b/contracts/cw1-subkeys/Cargo.toml @@ -23,7 +23,7 @@ cw1 = { path = "../../packages/cw1", version = "0.5.0" } cw2 = { path = "../../packages/cw2", version = "0.5.0" } cw1-whitelist = { path = "../cw1-whitelist", version = "0.5.0", features = ["library"] } cosmwasm-std = { version = "0.14.0-alpha2", features = ["iterator", "staking"] } -cosmwasm-storage = { version = "0.14.0-alpha2", features = ["iterator"] } +cw-storage-plus = { path = "../../packages/storage-plus", version = "0.5.0", features = ["iterator"] } schemars = "0.7" serde = { version = "1.0.103", default-features = false, features = ["derive"] } thiserror = { version = "1.0.20" } diff --git a/contracts/cw1-subkeys/src/contract.rs b/contracts/cw1-subkeys/src/contract.rs index 965be957d..0aa046226 100644 --- a/contracts/cw1-subkeys/src/contract.rs +++ b/contracts/cw1-subkeys/src/contract.rs @@ -6,12 +6,12 @@ use cosmwasm_std::{ attr, to_binary, BankMsg, Binary, CanonicalAddr, Coin, CosmosMsg, Deps, DepsMut, Empty, Env, HumanAddr, MessageInfo, Order, Response, StakingMsg, StdError, StdResult, }; -use cw0::{calc_range_start_human, Expiration}; +use cw0::{maybe_canonical, Expiration}; use cw1::CanExecuteResponse; use cw1_whitelist::{ contract::{execute_freeze, execute_update_admins, init as whitelist_init, query_admin_list}, msg::InitMsg, - state::admin_list_read, + state::ADMIN_LIST, }; use cw2::set_contract_version; @@ -20,9 +20,8 @@ use crate::msg::{ AllAllowancesResponse, AllPermissionsResponse, AllowanceInfo, HandleMsg, PermissionsInfo, QueryMsg, }; -use crate::state::{ - allowances, allowances_read, permissions, permissions_read, Allowance, Permissions, -}; +use crate::state::{Allowance, Permissions, ALLOWANCES, PERMISSIONS}; +use cw_storage_plus::Bound; // version info for migration info const CONTRACT_NAME: &str = "crates.io:cw1-subkeys"; @@ -72,7 +71,7 @@ pub fn execute_execute( where T: Clone + fmt::Debug + PartialEq + JsonSchema, { - let cfg = admin_list_read(deps.storage).load()?; + let cfg = ADMIN_LIST.load(deps.storage)?; let owner_raw = &deps.api.canonical_address(&info.sender)?; // this is the admin behavior (same as cw1-whitelist) if cfg.is_admin(owner_raw) { @@ -84,22 +83,19 @@ where for msg in &msgs { match msg { CosmosMsg::Staking(staking_msg) => { - let permissions = permissions(deps.storage); - let perm = permissions.may_load(owner_raw.as_slice())?; + let perm = PERMISSIONS.may_load(deps.storage, &owner_raw)?; let perm = perm.ok_or(ContractError::NotAllowed {})?; - check_staking_permissions(staking_msg, perm)?; } CosmosMsg::Bank(BankMsg::Send { to_address: _, amount, }) => { - let mut allowances = allowances(deps.storage); - let allow = allowances.may_load(owner_raw.as_slice())?; + let allow = ALLOWANCES.may_load(deps.storage, &owner_raw)?; let mut allowance = allow.ok_or(ContractError::NoAllowance {})?; // Decrease allowance allowance.balance = allowance.balance.sub(amount.clone())?; - allowances.save(owner_raw.as_slice(), &allowance)?; + ALLOWANCES.save(deps.storage, &owner_raw, &allowance)?; } _ => { return Err(ContractError::MessageTypeRejected {}); @@ -158,7 +154,7 @@ pub fn execute_increase_allowance( where T: Clone + fmt::Debug + PartialEq + JsonSchema, { - let cfg = admin_list_read(deps.storage).load()?; + let cfg = ADMIN_LIST.load(deps.storage)?; let spender_raw = &deps.api.canonical_address(&spender)?; let owner_raw = &deps.api.canonical_address(&info.sender)?; @@ -169,7 +165,7 @@ where return Err(ContractError::CannotSetOwnAccount {}); } - allowances(deps.storage).update::<_, StdError>(spender_raw.as_slice(), |allow| { + ALLOWANCES.update::<_, StdError>(deps.storage, &spender_raw, |allow| { let mut allowance = allow.unwrap_or_default(); if let Some(exp) = expires { allowance.expires = exp; @@ -204,7 +200,7 @@ pub fn execute_decrease_allowance( where T: Clone + fmt::Debug + PartialEq + JsonSchema, { - let cfg = admin_list_read(deps.storage).load()?; + let cfg = ADMIN_LIST.load(deps.storage)?; let spender_raw = &deps.api.canonical_address(&spender)?; let owner_raw = &deps.api.canonical_address(&info.sender)?; @@ -215,18 +211,17 @@ where return Err(ContractError::CannotSetOwnAccount {}); } - let allowance = - allowances(deps.storage).update::<_, ContractError>(spender_raw.as_slice(), |allow| { - // Fail fast - let mut allowance = allow.ok_or(ContractError::NoAllowance {})?; - if let Some(exp) = expires { - allowance.expires = exp; - } - allowance.balance = allowance.balance.sub_saturating(amount.clone())?; // Tolerates underflows (amount bigger than balance), but fails if there are no tokens at all for the denom (report potential errors) - Ok(allowance) - })?; + let allowance = ALLOWANCES.update::<_, ContractError>(deps.storage, &spender_raw, |allow| { + // Fail fast + let mut allowance = allow.ok_or(ContractError::NoAllowance {})?; + if let Some(exp) = expires { + allowance.expires = exp; + } + allowance.balance = allowance.balance.sub_saturating(amount.clone())?; // Tolerates underflows (amount bigger than balance), but fails if there are no tokens at all for the denom (report potential errors) + Ok(allowance) + })?; if allowance.balance.is_empty() { - allowances(deps.storage).remove(spender_raw.as_slice()); + ALLOWANCES.remove(deps.storage, &spender_raw); } let res = Response { @@ -254,7 +249,7 @@ pub fn execute_set_permissions( where T: Clone + fmt::Debug + PartialEq + JsonSchema, { - let cfg = admin_list_read(deps.storage).load()?; + let cfg = ADMIN_LIST.load(deps.storage)?; let spender_raw = &deps.api.canonical_address(&spender)?; let owner_raw = &deps.api.canonical_address(&info.sender)?; @@ -264,7 +259,7 @@ where if spender_raw == owner_raw { return Err(ContractError::CannotSetOwnAccount {}); } - permissions(deps.storage).save(spender_raw.as_slice(), &perm)?; + PERMISSIONS.save(deps.storage, &spender_raw, &perm)?; let res = Response { submessages: vec![], @@ -298,8 +293,8 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { // if the subkey has no allowance, return an empty struct (not an error) pub fn query_allowance(deps: Deps, spender: HumanAddr) -> StdResult { let subkey = deps.api.canonical_address(&spender)?; - let allow = allowances_read(deps.storage) - .may_load(subkey.as_slice())? + let allow = ALLOWANCES + .may_load(deps.storage, &subkey)? .unwrap_or_default(); Ok(allow) } @@ -307,8 +302,8 @@ pub fn query_allowance(deps: Deps, spender: HumanAddr) -> StdResult { // if the subkey has no permissions, return an empty struct (not an error) pub fn query_permissions(deps: Deps, spender: HumanAddr) -> StdResult { let subkey = deps.api.canonical_address(&spender)?; - let permissions = permissions_read(deps.storage) - .may_load(subkey.as_slice())? + let permissions = PERMISSIONS + .may_load(deps.storage, &subkey)? .unwrap_or_default(); Ok(permissions) } @@ -326,14 +321,14 @@ fn query_can_execute( // this can just return booleans and the query_can_execute wrapper creates the struct once, not on every path fn can_execute(deps: Deps, sender: HumanAddr, msg: CosmosMsg) -> StdResult { let owner_raw = deps.api.canonical_address(&sender)?; - let cfg = admin_list_read(deps.storage).load()?; + let cfg = ADMIN_LIST.load(deps.storage)?; if cfg.is_admin(&owner_raw) { return Ok(true); } match msg { CosmosMsg::Bank(BankMsg::Send { amount, .. }) => { // now we check if there is enough allowance for this message - let allowance = allowances_read(deps.storage).may_load(owner_raw.as_slice())?; + let allowance = ALLOWANCES.may_load(deps.storage, &owner_raw)?; match allowance { // if there is an allowance, we subtract the requested amount to ensure it is covered (error on underflow) Some(allow) => Ok(allow.balance.sub(amount).is_ok()), @@ -341,7 +336,7 @@ fn can_execute(deps: Deps, sender: HumanAddr, msg: CosmosMsg) -> StdResult } } CosmosMsg::Staking(staking_msg) => { - let perm_opt = permissions_read(deps.storage).may_load(owner_raw.as_slice())?; + let perm_opt = PERMISSIONS.may_load(deps.storage, &owner_raw)?; match perm_opt { Some(permission) => Ok(check_staking_permissions(&staking_msg, permission).is_ok()), None => Ok(false), @@ -365,11 +360,12 @@ pub fn query_all_allowances( limit: Option, ) -> StdResult { let limit = calc_limit(limit); - let range_start = calc_range_start_human(deps.api, start_after)?; + let canon = maybe_canonical(deps.api, start_after)?; + let start = canon.map(Bound::exclusive); let api = &deps.api; - let res: StdResult> = allowances_read(deps.storage) - .range(range_start.as_deref(), None, Order::Ascending) + let res: StdResult> = ALLOWANCES + .range(deps.storage, start, None, Order::Ascending) .take(limit) .map(|item| { item.and_then(|(k, allow)| { @@ -391,11 +387,12 @@ pub fn query_all_permissions( limit: Option, ) -> StdResult { let limit = calc_limit(limit); - let range_start = calc_range_start_human(deps.api, start_after)?; + let canon = maybe_canonical(deps.api, start_after)?; + let start = canon.map(Bound::exclusive); let api = &deps.api; - let res: StdResult> = permissions_read(deps.storage) - .range(range_start.as_deref(), None, Order::Ascending) + let res: StdResult> = PERMISSIONS + .range(deps.storage, start, None, Order::Ascending) .take(limit) .map(|item| { item.and_then(|(k, perm)| { @@ -1463,7 +1460,7 @@ mod tests { }; let spender_raw = &deps.api.canonical_address(&spender).unwrap(); - let _ = permissions(&mut deps.storage).save(spender_raw.as_slice(), &perm); + let _ = PERMISSIONS.save(&mut deps.storage, &spender_raw, &perm); // let us make some queries... different msg types by owner and by other let send_msg = CosmosMsg::Bank(BankMsg::Send { diff --git a/contracts/cw1-subkeys/src/state.rs b/contracts/cw1-subkeys/src/state.rs index 3e263f946..aa167e179 100644 --- a/contracts/cw1-subkeys/src/state.rs +++ b/contracts/cw1-subkeys/src/state.rs @@ -1,9 +1,8 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use cosmwasm_std::Storage; -use cosmwasm_storage::{bucket, bucket_read, Bucket, ReadonlyBucket}; use cw0::{Expiration, NativeBalance}; +use cw_storage_plus::Map; use std::fmt; @@ -29,34 +28,11 @@ impl fmt::Display for Permissions { } } -const PREFIX_PERMISSIONS: &[u8] = b"permissions"; - -/// returns a bucket with all permissions (query by subkey) -pub fn permissions(storage: &mut dyn Storage) -> Bucket { - bucket(storage, PREFIX_PERMISSIONS) -} - -/// returns a bucket with all permissionsk (query by subkey) -/// (read-only version for queries) -pub fn permissions_read(storage: &dyn Storage) -> ReadonlyBucket { - bucket_read(storage, PREFIX_PERMISSIONS) -} - #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema, Default)] pub struct Allowance { pub balance: NativeBalance, pub expires: Expiration, } -const PREFIX_ALLOWANCE: &[u8] = b"allowance"; - -/// returns a bucket with all allowances (query by subkey) -pub fn allowances(storage: &mut dyn Storage) -> Bucket { - bucket(storage, PREFIX_ALLOWANCE) -} - -/// returns a bucket with all allowances (query by subkey) -/// (read-only version for queries) -pub fn allowances_read(storage: &dyn Storage) -> ReadonlyBucket { - bucket_read(storage, PREFIX_ALLOWANCE) -} +pub const PERMISSIONS: Map<&[u8], Permissions> = Map::new("permissions"); +pub const ALLOWANCES: Map<&[u8], Allowance> = Map::new("permissions"); From ad46bf1bb262f911e5858e433b5414b72f7fc44c Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 9 Mar 2021 11:10:07 +0100 Subject: [PATCH 3/3] Fix cw1-subkeys typo --- contracts/cw1-subkeys/src/state.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/cw1-subkeys/src/state.rs b/contracts/cw1-subkeys/src/state.rs index aa167e179..9169a88f8 100644 --- a/contracts/cw1-subkeys/src/state.rs +++ b/contracts/cw1-subkeys/src/state.rs @@ -35,4 +35,4 @@ pub struct Allowance { } pub const PERMISSIONS: Map<&[u8], Permissions> = Map::new("permissions"); -pub const ALLOWANCES: Map<&[u8], Allowance> = Map::new("permissions"); +pub const ALLOWANCES: Map<&[u8], Allowance> = Map::new("allowances");