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

SNIP-24 - Query Permits #22

Merged
merged 41 commits into from
Oct 19, 2021
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
cd78f1e
Working prototype :tada:
assafmo Sep 25, 2021
e299b4d
Validate permit message
assafmo Sep 25, 2021
7065602
Rename account to query_balance_of
assafmo Sep 25, 2021
4dd5088
Fix permit's content serialization to bytes
assafmo Sep 26, 2021
468eda9
Update make start-server
assafmo Sep 26, 2021
0582c62
Refactor, make permit API UX better
assafmo Sep 26, 2021
476c043
remain::sorted on SignedPermit
assafmo Sep 26, 2021
2365946
Query with permit for all authenticated queries
assafmo Sep 26, 2021
7d8a365
Refactor some var/func names
assafmo Sep 26, 2021
e3ac874
Implement revoke permit
assafmo Sep 26, 2021
b1f3d46
Clean makefile
assafmo Sep 26, 2021
ca14f16
Refactor RevokedPermits storage
assafmo Sep 27, 2021
7c0d408
:shrug:
assafmo Sep 27, 2021
a8dace9
simplified permit query interface
reuvenpo Sep 27, 2021
c3071e9
Fix permit for query_allowance
assafmo Sep 29, 2021
d986d2e
Rename struct SignedPermit -> PermitParams
assafmo Sep 29, 2021
8a8d419
A small refactor
assafmo Sep 29, 2021
2323bf4
Code comment rephrase
assafmo Sep 29, 2021
e14e857
cargo schema
reuvenpo Sep 29, 2021
3844f6c
Simplify return logic of is_permit_revoked()
assafmo Sep 29, 2021
934452e
Merge branch 'query-balance-prmit' of github.com:enigmampc/snip20-ref…
assafmo Sep 29, 2021
38bc240
Merge pull request #23 from enigmampc/query-balance-prmit-2
assafmo Sep 29, 2021
37b191f
Fix merge conflict
assafmo Sep 29, 2021
1ce2de6
Rename query_with_permit() -> permit_queries()
assafmo Sep 29, 2021
716ee8a
Rename permit.signed -> permit.params
assafmo Sep 29, 2021
7af1d05
cargo schema
reuvenpo Sep 29, 2021
736a0d9
Query permit permissions
assafmo Sep 29, 2021
128e3f7
Add prefix for RevokedPemits storage keys
assafmo Oct 4, 2021
c9b53b5
Test permits for wrong token
assafmo Oct 8, 2021
59d61c7
Permit: make errors more readable for humans
assafmo Oct 13, 2021
7a27a75
Permits: test every failure scenario
assafmo Oct 13, 2021
0384401
Permit: test query balance
assafmo Oct 13, 2021
60b4c53
Permit: test query history & allowance
assafmo Oct 13, 2021
8e16bb4
Restore all the other tests
assafmo Oct 13, 2021
0400dcb
Move permit operations to secret-toolkit
assafmo Oct 14, 2021
942debd
Fix secret-toolkit dependency
assafmo Oct 17, 2021
ea27639
Pass storage key prefix to RevokedPemits
assafmo Oct 18, 2021
caa8ab7
RevokedPemits -> RevokedPermits
assafmo Oct 18, 2021
b2f764e
Use cosmwasm-std v1.0.0
assafmo Oct 18, 2021
bae405b
Update secret-toolkit
assafmo Oct 19, 2021
2d1d606
cargo schema
assafmo Oct 19, 2021
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
49 changes: 49 additions & 0 deletions Cargo.lock

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

8 changes: 4 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,13 @@ name = "snip20-reference-impl"
version = "0.1.0"
authors = ["Itzik <[email protected]>"]
edition = "2018"

exclude = [
# Those files are rust-optimizer artifacts. You might want to commit them for convenience but they should not be part of the source code publication.
"contract.wasm",
"hash.txt",
]

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[lib]
crate-type = ["cdylib", "rlib"]

Expand Down Expand Up @@ -44,10 +42,12 @@ bincode2 = "2.0.1"
subtle = { version = "2.2.3", default-features = false }
base64 = "0.12.3"
hex = "0.4.2"

rand_chacha = { version = "0.2.2", default-features = false }
rand_core = { version = "0.5.1", default-features = false }
rand_core = { version = "0.5.1", default-features = false }
sha2 = { version = "0.9.1", default-features = false }
ripemd160 = "0.9.1"
secp256k1 = "0.19.0"
remain = "0.2.2"

[dev-dependencies]
cosmwasm-schema = { version = "0.9.2" }
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,9 @@ contract.wasm.gz: contract.wasm
.PHONY: start-server
start-server: # CTRL+C to stop
docker run -it --rm \
-p 26657:26657 -p 26656:26656 -p 1317:1317 \
-p 26657:26657 -p 26656:26656 -p 1337:1337 \
-v $$(pwd):/root/code \
--name secretdev enigmampc/secret-network-sw-dev:v1.0.4-3
--name secretdev enigmampc/secret-network-sw-dev:v1.0.4-4

.PHONY: schema
schema:
Expand Down
120 changes: 118 additions & 2 deletions src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,23 @@ use cosmwasm_std::{
HandleResponse, HumanAddr, InitResponse, Querier, QueryResult, ReadonlyStorage, StdError,
StdResult, Storage, Uint128,
};
use ripemd160::{Digest, Ripemd160};
use secp256k1::Secp256k1;
use sha2::Sha256;

use crate::batch;
use crate::msg::QueryWithPermit;
use crate::msg::{
space_pad, ContractStatusLevel, HandleAnswer, HandleMsg, InitMsg, QueryAnswer, QueryMsg,
ResponseStatus::Success,
};
use crate::permit::Permit;
use crate::rand::sha_256;
use crate::receiver::Snip20ReceiveMsg;
use crate::state::{
get_receiver_hash, read_allowance, read_viewing_key, set_receiver_hash, write_allowance,
write_viewing_key, Balances, Config, Constants, ReadonlyBalances, ReadonlyConfig,
RevokedPemits,
};
use crate::transaction_history::{
get_transfers, get_txs, store_burn, store_deposit, store_mint, store_redeem, store_transfer,
Expand Down Expand Up @@ -90,6 +96,7 @@ pub fn init<S: Storage, A: Api, Q: Querier>(
redeem_is_enabled: init_config.redeem_enabled(),
mint_is_enabled: init_config.mint_enabled(),
burn_is_enabled: init_config.burn_enabled(),
contract_address: env.contract.address,
})?;
config.set_total_supply(total_supply);
config.set_contract_status(ContractStatusLevel::NormalRun);
Expand Down Expand Up @@ -230,6 +237,7 @@ pub fn handle<S: Storage, A: Api, Q: Querier>(
HandleMsg::AddMinters { minters, .. } => add_minters(deps, env, minters),
HandleMsg::RemoveMinters { minters, .. } => remove_minters(deps, env, minters),
HandleMsg::SetMinters { minters, .. } => set_minters(deps, env, minters),
HandleMsg::RevokePermit { permit_name, .. } => revoke_permit(deps, env, permit_name),
};

pad_response(response)
Expand All @@ -242,11 +250,105 @@ pub fn query<S: Storage, A: Api, Q: Querier>(deps: &Extern<S, A, Q>, msg: QueryM
QueryMsg::ContractStatus {} => query_contract_status(&deps.storage),
QueryMsg::ExchangeRate {} => query_exchange_rate(&deps.storage),
QueryMsg::Minters { .. } => query_minters(deps),
_ => authenticated_queries(deps, msg),
QueryMsg::WithPermit { permit, query } => query_with_permit(deps, permit, query),
_ => viewing_keys_queries(deps, msg),
}
}

pub fn authenticated_queries<S: Storage, A: Api, Q: Querier>(
fn query_with_permit<S: Storage, A: Api, Q: Querier>(
deps: &Extern<S, A, Q>,
permit: Permit,
query: QueryWithPermit,
) -> Result<Binary, StdError> {
if permit.signed.msgs.len() != 1 {
return Err(StdError::generic_err(format!(
"Must sign exactly 1 permit message, got: {:?}.",
permit.signed.msgs.len()
)));
}

if permit.signed.msgs[0].r#type != "query_permit" {
return Err(StdError::generic_err(format!(
"Type must be 'query_permit', got: {:?}.",
permit.signed.msgs[0].r#type
)));
}

let permit_content = &permit.signed.msgs[0].value;
assafmo marked this conversation as resolved.
Show resolved Hide resolved

// Validate permit content
let token_address = ReadonlyConfig::from_storage(&deps.storage)
.constants()?
.contract_address;

if !permit_content.allowed_tokens.contains(&token_address) {
return Err(StdError::generic_err(format!(
"Permit doesn't apply to token {:?}, allowed tokens: {:?}",
token_address, permit_content.allowed_tokens
)));
}

// Derive account from pubkey
let pubkey = permit.signature.pub_key.value;
let account = deps.api.human_address(&pubkey_to_account(&pubkey))?;

// Validate permit_name
let permit_name = &permit_content.permit_name;
let is_permit_revoked = RevokedPemits::is_permit_revoked(&deps.storage, &account, &permit_name);
if is_permit_revoked {
return Err(StdError::generic_err(format!(
"Permit '{:?}' was revoked by account {:?}",
permit_name, account
)));
}

// Validate signature, reference: https://github.com/enigmampc/SecretNetwork/blob/f591ed0cb3af28608df3bf19d6cfb733cca48100/cosmwasm/packages/wasmi-runtime/src/crypto/secp256k1.rs#L49-L82
let signed_bytes = &to_binary(&permit.signed)?.0;
let signed_bytes_hash = Sha256::digest(signed_bytes);
let secp256k1_msg =
secp256k1::Message::from_slice(signed_bytes_hash.as_slice()).map_err(|err| {
StdError::generic_err(format!(
"Failed to create a secp256k1 message from signed_bytes: {:?}",
err
))
})?;

let secp256k1_verifier = Secp256k1::verification_only();

let secp256k1_signature = secp256k1::Signature::from_compact(&permit.signature.signature.0)
.map_err(|err| StdError::generic_err(format!("Malformed signature: {:?}", err)))?;
let secp256k1_pubkey = secp256k1::PublicKey::from_slice(pubkey.0.as_slice())
.map_err(|err| StdError::generic_err(format!("Malformed pubkey: {:?}", err)))?;

secp256k1_verifier
.verify(&secp256k1_msg, &secp256k1_signature, &secp256k1_pubkey)
.map_err(|err| {
StdError::generic_err(format!(
"Failed to verify signatures for the given permit: {:?}",
err
))
})?;
reuvenpo marked this conversation as resolved.
Show resolved Hide resolved

// We're verified! We can now execute the query
match query {
QueryWithPermit::Balance {} => query_balance(&deps, &account),
QueryWithPermit::TransferHistory { page, page_size } => {
query_transfers(&deps, &account, page.unwrap_or(0), page_size)
}
QueryWithPermit::TransactionHistory { page, page_size } => {
query_transactions(&deps, &account, page.unwrap_or(0), page_size)
}
QueryWithPermit::Allowance { spender } => query_allowance(deps, account, spender),
reuvenpo marked this conversation as resolved.
Show resolved Hide resolved
}
}

fn pubkey_to_account(pubkey: &Binary) -> CanonicalAddr {
let mut hasher = Ripemd160::new();
hasher.update(Sha256::digest(&pubkey.0));
CanonicalAddr(Binary(hasher.finalize().to_vec()))
}
reuvenpo marked this conversation as resolved.
Show resolved Hide resolved

pub fn viewing_keys_queries<S: Storage, A: Api, Q: Querier>(
deps: &Extern<S, A, Q>,
msg: QueryMsg,
) -> QueryResult {
Expand Down Expand Up @@ -1611,6 +1713,20 @@ fn perform_transfer<T: Storage>(
Ok(())
}

fn revoke_permit<S: Storage, A: Api, Q: Querier>(
deps: &mut Extern<S, A, Q>,
env: Env,
permit_name: String,
) -> StdResult<HandleResponse> {
RevokedPemits::revoke_permit(&mut deps.storage, &env.message.sender, &permit_name);

Ok(HandleResponse {
messages: vec![],
log: vec![],
data: Some(to_binary(&HandleAnswer::RevokePemit { status: Success })?),
})
}

fn is_admin<S: Storage>(config: &Config<S>, account: &HumanAddr) -> StdResult<bool> {
let consts = config.constants()?;
if &consts.admin != account {
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
mod batch;
pub mod contract;
pub mod msg;
mod permit;
mod rand;
pub mod receiver;
pub mod state;
Expand Down
24 changes: 24 additions & 0 deletions src/msg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use serde::{Deserialize, Serialize};
use cosmwasm_std::{Binary, HumanAddr, StdError, StdResult, Uint128};

use crate::batch;
use crate::permit::Permit;
use crate::transaction_history::{RichTx, Tx};
use crate::viewing_key::ViewingKey;

Expand Down Expand Up @@ -212,6 +213,11 @@ pub enum HandleMsg {
level: ContractStatusLevel,
padding: Option<String>,
},

// Permit
RevokePermit {
permit_name: String,
},
}
assafmo marked this conversation as resolved.
Show resolved Hide resolved

#[derive(Serialize, Deserialize, JsonSchema, Debug)]
Expand Down Expand Up @@ -305,6 +311,11 @@ pub enum HandleAnswer {
SetContractStatus {
status: ResponseStatus,
},

// Permit
RevokePemit {
status: ResponseStatus,
},
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
Expand Down Expand Up @@ -336,6 +347,10 @@ pub enum QueryMsg {
page_size: u32,
},
Minters {},
WithPermit {
permit: Permit,
query: QueryWithPermit,
},
}

impl QueryMsg {
Expand All @@ -357,6 +372,15 @@ impl QueryMsg {
}
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum QueryWithPermit {
Allowance { spender: HumanAddr },
Balance {},
TransferHistory { page: Option<u32>, page_size: u32 },
TransactionHistory { page: Option<u32>, page_size: u32 },
}
assafmo marked this conversation as resolved.
Show resolved Hide resolved

#[derive(Serialize, Deserialize, JsonSchema, Debug)]
#[serde(rename_all = "snake_case")]
pub enum QueryAnswer {
Expand Down
Loading