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

Implemented expiration for cw1-subkeys contract #356

Merged
merged 3 commits into from
Jul 27, 2021

Conversation

hashedone
Copy link
Contributor

@hashedone hashedone commented Jul 27, 2021

There is one edge case which is not clean - what to do, when previous allowance is expired, but no new allowance is set. There are two reasonable solutions:

  • treat previous expired allowance as no allowance, so missing allowance in IncreaseAllowance in such case would just set expiration to Expire::Never {} as it is default expiration for new allowances. This feels natural, but might cause dangerous usecase: if transaction was scheduled before previous allowance expired, then probably he expected it not to change, but it would change, if transaction would be validated to late (and it would be never expiring)
  • behave, as it would be setting allowance which is already expired - return an error - this seems to be way more reasonable

This obviously doesn't impact decreasing allowance, as decreasing non existing allowance is always an error (nothing to decrease).

@hashedone hashedone requested review from maurolacy and ethanfrey July 27, 2021 14:05
@hashedone hashedone force-pushed the subkeys-allowance-expire branch from e32b6b1 to 441b52c Compare July 27, 2021 14:32
Copy link
Member

@ethanfrey ethanfrey left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good fixes and good tests.
One change requested to not use the custom is_expired method (if the other one has an issue, then let's fix it).
(Asserting error variant in tests is optional update)

Once you do that, feel free to merge.

@@ -107,6 +107,10 @@ where
}) => {
ALLOWANCES.update::<_, ContractError>(deps.storage, &info.sender, |allow| {
let mut allowance = allow.ok_or(ContractError::NoAllowance {})?;
if is_expired(&env, allowance.expires) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow, super important bug here. 💯

@@ -127,6 +131,14 @@ where
Ok(res)
}

pub fn is_expired(env: &Env, expire: Expiration) -> bool {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to implement this, you can do:

expire.is_expired(&env.block)

https://github.com/CosmWasm/cosmwasm-plus/blob/main/packages/cw0/src/expiration.rs#L41-L47

let mut allowance = allow.unwrap_or_default();
ALLOWANCES.update::<_, ContractError>(deps.storage, &spender_addr, |allow| {
let mut allowance = allow
.filter(|allow| !is_expired(&env, allow.expires))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good checks for expiration (and good error below).
I just request using the Expiration method

@@ -217,7 +239,7 @@ where

pub fn execute_decrease_allowance<T>(
deps: DepsMut,
_env: Env,
env: Env,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto.
(All these _env were good tips we were not checking for expiration.

// we can use unchecked here as it is a query - bad value means a miss, we never write it
let spender = deps.api.addr_validate(&spender)?;
let allow = ALLOWANCES
.may_load(deps.storage, &spender)?
.filter(|allow| !is_expired(&env, allow.expires))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

makes sense

@@ -351,7 +391,9 @@ fn can_execute(deps: Deps, sender: String, msg: CosmosMsg) -> StdResult<bool> {
let allowance = ALLOWANCES.may_load(deps.storage, &sender)?;
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()),
Some(allow) => {
Ok(!is_expired(&env, allow.expires) && allow.balance.sub(amount).is_ok())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another good catch

@@ -475,6 +526,14 @@ mod tests {
withdraw: false,
};

// Expiration constant working properly with default `mock_env`
const NON_EXPIRED_HEIGHT: Expiration = Expiration::AtHeight(22_222);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice to make these variables.

assert_eq!(allowance, Allowance::default());
}

#[test]
fn query_expired() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

}
]
}
.canonical(),
);
}

#[test]
fn previous_expired() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice test, it removes previously expired when it add the new one.

.expire_allowances(SPENDER1, NON_EXPIRED_HEIGHT)
.init();

execute(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would usually asset which error it was. Something like:

let err = execute(...).unwrap_err();
assert!(matches!(err, ContractError::SettingExpiredAllowance(_)), "{}", err);
```

@hashedone
Copy link
Contributor Author

I just somehow missed is_expired method - I have no idea how, I would replace all occurrences.

@hashedone hashedone merged commit 1ca155d into main Jul 27, 2021
@hashedone hashedone deleted the subkeys-allowance-expire branch July 27, 2021 15:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants