cw1-subkeys: Implement permission functionality#75
Conversation
ethanfrey
left a comment
There was a problem hiding this comment.
Thanks for your first contract PR @orkunkl
In general, you got all the pieces connected and it looks generally correct. I added a number of comments to improve code quality. Can you go through them and also take another pass on naming design and I will then re-review this
| }, | ||
| } | ||
|
|
||
| pub struct AllowanceResponse { |
There was a problem hiding this comment.
You removed the AllowanceResponse? I think you need both.
Oh, this is README....
There was a problem hiding this comment.
I wanted to make helpers.ts and msg.rs naming identical
|
|
||
| type Expiration = { at_height: { height: number } } | { at_time: { time: number } } | { never: {}} | ||
|
|
||
| interface Permissions { |
There was a problem hiding this comment.
Question:
If we add permissions for eg. governance votes, or calling wasm contracts, would you keep them here? Or use a separate type?
To see if we want one large "Permissions" or "StakingPermissions", "VotingPermissions", etc.
There was a problem hiding this comment.
Separate permissions design brings a lot of extra code(bucket) to both smart contract and helpers.ts. This struct will not contain hundreds of booleans(max 15-20?). I guess using single permissions struct is the optimal way
There was a problem hiding this comment.
If rust and buckets provides a way to define permissions generically, then multiple permissions that extend Permissions trait is better.
There was a problem hiding this comment.
Fair enough, we keep one Permissions and extend it for other packages.
Please add a comment on the type explaining that choice (actually only in helpers.rs, not here)
There was a problem hiding this comment.
I assume you mean state.rs by helpers.rs? or helpers.ts? I am confused 😕
There was a problem hiding this comment.
I meant state.rs
I should re-review again
|
Looks good. One comment requested, and please |
ethanfrey
left a comment
There was a problem hiding this comment.
Much nicer.
If you could cover a few more cases in the test code, I can merge this
|
|
||
| // TODO: define more of these | ||
| type CosmosMsg = SendMsg; | ||
| type CosmosMsg = SendMsg | DelegateMsg | UndelegateMsg | RedelegateMsg | WithdrawMsg |
| for msg in &msgs { | ||
| match msg { | ||
| CosmosMsg::Staking(staking_msg) => { | ||
| let permissions = permissions(&mut deps.storage); |
There was a problem hiding this comment.
This is fine and explicit. I generally make this more compact.
let perm = permissions(&mut deps.storage).load(owner_raw.as_slice())?;
check_staking_permissions(staking_msg, perm)?;But it is fine as is. (and with custom error message)
| staking_msg: &StakingMsg, | ||
| permissions: Permissions, | ||
| ) -> Result<bool, PermissionErr> { | ||
| match staking_msg { |
| QueryMsg::AllAllowances { start_after, limit } => { | ||
| to_binary(&query_all_allowances(deps, start_after, limit)?) | ||
| } | ||
| QueryMsg::AllPermissions { start_after, limit } => { |
| } | ||
| } | ||
| CosmosMsg::Staking(staking_msg) => { | ||
| permissions_read(&deps.storage) |
There was a problem hiding this comment.
Interesting chain.
I think the ? and match approach for allowances is more legible. But nice that you learned a bit more about map and map_or and handling Options and Results. Key skills for working with rust
There was a problem hiding this comment.
Just some old scala and functional programming skills λ
There was a problem hiding this comment.
Applied the ? and match case.
But can_send method looks weird to me. The return value is StdResult, but in tests, it is expected to be always boolean. Which throws the error information to the thrash 🤔
There was a problem hiding this comment.
Error should only throw if we have trouble parsing from disk, and we cannot prove that will never happen.
In all normal cases (non-corrupt db), we should get true or false. You would have to write invalid json to the key to trigger an error
| // default is no permission | ||
| handle(&mut deps, env.clone(), setup_perm_msg2).unwrap(); | ||
|
|
||
| let permissions = query_permissions(&deps, spender1.clone()).unwrap(); |
There was a problem hiding this comment.
Looks good. I would add one more test, where you set the allowance for someone with a permission and then ensure that both are set properly (setting allowance doesn't unset the permission)
| } | ||
|
|
||
| // spender2 cannot execute (no permission) | ||
| for msg in &msgs { |
There was a problem hiding this comment.
I would add a few more tests where one sender has eg. 2 permissions set, 2 unset and ensure that those two pass, and the other 2 fail.
Just a double check that there is no typo in which permission is checked for which message. And ensures future changes don't break this (what if we swap delegate and redelegate permission checks? these tests would still pass)
There was a problem hiding this comment.
Added the test. Developer errors must be more distinctive and functional than just a str :( CosmWasm/cosmwasm#518
ethanfrey
left a comment
There was a problem hiding this comment.
Looks good.
Two minor nits on code style. You can ideally change them (or ignore them if you disagree strongly) and then ready for merge.
| }) | ||
| let perm_opt = permissions_read(&deps.storage).may_load(owner_raw.as_slice())?; | ||
| match perm_opt { | ||
| Some(permission) => match check_staking_permissions(&staking_msg, permission) { |
There was a problem hiding this comment.
This could actually be:
Some(permission) => Ok(check_staking_permissions(&staking_msg, permission).is_ok())
Error only returned from may_load unable to parse.
| let denom = "token1"; | ||
| let amount = 10000; | ||
| let allow = coin(amount, denom); | ||
| let coin = coin(amount, denom); |
There was a problem hiding this comment.
This shadows the function coin for the rest of this test function. I would avoid that
(I only shadow one variable with another when transforming it, eg. let msg = msg.to_string();)
There was a problem hiding this comment.
I see 👍 Renamed it to coin1. allow was not a good fit there.
| msgs: msg_delegate.clone(), | ||
| }, | ||
| ); | ||
| // FIXME need better error check here |
There was a problem hiding this comment.
You want typed errors... I know we will make this happen
|
|
||
| // tests permissions and allowances are independent features and does not affect each other | ||
| #[test] | ||
| fn permissions_allowances_independent() { |
|
2 points left:
|
|
Meaning 0.2.1 helpers.ts points to 0.2.0 (or 0.1.1) code. I am happy to make a new tag shortly and then we have the updated helpers, but this chicken-and-egg problem is what stops us from using tagged helpers.ts, which is the real solution. Any ideas for work-arounds? |
|
I am happy to merge this when you give the 👍 And we can cut a release of cosmwasm-plus contract today |
|
Houston, the shuttle is ready to launch 🚀 👍 |
Implements permission functionality for messages: