Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgrade cw1 to storage plus #241

Merged
merged 3 commits into from
Mar 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 2 additions & 12 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion contracts/cw1-subkeys/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
Expand Down
81 changes: 39 additions & 42 deletions contracts/cw1-subkeys/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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";
Expand Down Expand Up @@ -72,7 +71,7 @@ pub fn execute_execute<T>(
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) {
Expand All @@ -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 {});
Expand Down Expand Up @@ -158,7 +154,7 @@ pub fn execute_increase_allowance<T>(
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)?;

Expand All @@ -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;
Expand Down Expand Up @@ -204,7 +200,7 @@ pub fn execute_decrease_allowance<T>(
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)?;

Expand All @@ -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 {
Expand Down Expand Up @@ -254,7 +249,7 @@ pub fn execute_set_permissions<T>(
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)?;

Expand All @@ -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![],
Expand Down Expand Up @@ -298,17 +293,17 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> {
// if the subkey has no allowance, return an empty struct (not an error)
pub fn query_allowance(deps: Deps, spender: HumanAddr) -> StdResult<Allowance> {
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)
}

// if the subkey has no permissions, return an empty struct (not an error)
pub fn query_permissions(deps: Deps, spender: HumanAddr) -> StdResult<Permissions> {
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)
}
Expand All @@ -326,22 +321,22 @@ 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<bool> {
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()),
None => Ok(false),
}
}
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),
Expand All @@ -365,11 +360,12 @@ pub fn query_all_allowances(
limit: Option<u32>,
) -> StdResult<AllAllowancesResponse> {
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<Vec<AllowanceInfo>> = allowances_read(deps.storage)
.range(range_start.as_deref(), None, Order::Ascending)
let res: StdResult<Vec<AllowanceInfo>> = ALLOWANCES
.range(deps.storage, start, None, Order::Ascending)
.take(limit)
.map(|item| {
item.and_then(|(k, allow)| {
Expand All @@ -391,11 +387,12 @@ pub fn query_all_permissions(
limit: Option<u32>,
) -> StdResult<AllPermissionsResponse> {
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<Vec<PermissionsInfo>> = permissions_read(deps.storage)
.range(range_start.as_deref(), None, Order::Ascending)
let res: StdResult<Vec<PermissionsInfo>> = PERMISSIONS
.range(deps.storage, start, None, Order::Ascending)
.take(limit)
.map(|item| {
item.and_then(|(k, perm)| {
Expand Down Expand Up @@ -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 {
Expand Down
30 changes: 3 additions & 27 deletions contracts/cw1-subkeys/src/state.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -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<Permissions> {
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<Permissions> {
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<Allowance> {
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<Allowance> {
bucket_read(storage, PREFIX_ALLOWANCE)
}
pub const PERMISSIONS: Map<&[u8], Permissions> = Map::new("permissions");
pub const ALLOWANCES: Map<&[u8], Allowance> = Map::new("allowances");
2 changes: 1 addition & 1 deletion contracts/cw1-whitelist/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
Expand Down
16 changes: 8 additions & 8 deletions contracts/cw1-whitelist/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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())
}

Expand Down Expand Up @@ -76,12 +76,12 @@ pub fn execute_freeze(
_env: Env,
info: MessageInfo,
) -> Result<Response, ContractError> {
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")];
Expand All @@ -95,12 +95,12 @@ pub fn execute_update_admins(
info: MessageInfo,
admins: Vec<HumanAddr>,
) -> Result<Response, ContractError> {
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")];
Expand All @@ -109,7 +109,7 @@ pub fn execute_update_admins(
}

fn can_execute(deps: Deps, sender: &HumanAddr) -> StdResult<bool> {
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)
}
Expand All @@ -122,7 +122,7 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> {
}

pub fn query_admin_list(deps: Deps) -> StdResult<AdminListResponse> {
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,
Expand Down
Loading