Skip to content

Commit

Permalink
feat(minor-multisig): implement client for queries (#607)
Browse files Browse the repository at this point in the history
  • Loading branch information
cjcobb23 authored Sep 10, 2024
1 parent 847ac0c commit a73560d
Show file tree
Hide file tree
Showing 12 changed files with 362 additions and 0 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions contracts/multisig/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ optimize = """docker run --rm -v "$(pwd)":/code \

[dependencies]
axelar-wasm-std = { workspace = true, features = ["derive"] }
client = { workspace = true }
cosmwasm-crypto = "1.2.7"
cosmwasm-schema = { workspace = true }
cosmwasm-std = { workspace = true }
Expand Down
279 changes: 279 additions & 0 deletions contracts/multisig/src/client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,279 @@
use cosmwasm_schema::cw_serde;
use cosmwasm_std::Uint64;
use error_stack::{Result, ResultExt};
use router_api::ChainName;

use crate::key::{KeyType, PublicKey};
use crate::msg::{ExecuteMsg, QueryMsg};
use crate::multisig::Multisig;
use crate::verifier_set::VerifierSet;

#[derive(thiserror::Error)]
#[cw_serde]
pub enum Error {
#[error("failed to query multisig contract for multisig session. session_id: {0}")]
QueryMultisigSession(Uint64),

#[error("failed to query multisig contract for verifier set: verifier_set_id: {0}")]
QueryVerifierSet(String),

#[error("failed to query multisig contract for verifier public key. verifier_address: {verifier_address}, key_type: {key_type}")]
QueryPublicKey {
verifier_address: String,
key_type: KeyType,
},

#[error("failed to query multisig contract for caller authorization. contract_address: {contract_address}, chain_name: {chain_name}")]
QueryIsCallerAuthorized {
contract_address: String,
chain_name: ChainName,
},
}

impl<'a> From<client::Client<'a, ExecuteMsg, QueryMsg>> for Client<'a> {
fn from(client: client::Client<'a, ExecuteMsg, QueryMsg>) -> Self {
Client { client }
}
}

impl From<QueryMsg> for Error {
fn from(value: QueryMsg) -> Self {
match value {
QueryMsg::Multisig { session_id } => Error::QueryMultisigSession(session_id),
QueryMsg::VerifierSet { verifier_set_id } => Error::QueryVerifierSet(verifier_set_id),
QueryMsg::PublicKey {
verifier_address,
key_type,
} => Error::QueryPublicKey {
verifier_address,
key_type,
},
QueryMsg::IsCallerAuthorized {
contract_address,
chain_name,
} => Error::QueryIsCallerAuthorized {
contract_address,
chain_name,
},
}
}
}

pub struct Client<'a> {
client: client::Client<'a, ExecuteMsg, QueryMsg>,
}

impl<'a> Client<'a> {
pub fn multisig(&self, session_id: Uint64) -> Result<Multisig, Error> {
let msg = QueryMsg::Multisig { session_id };
self.client.query(&msg).change_context_lazy(|| msg.into())
}

pub fn verifier_set(&self, verifier_set_id: String) -> Result<VerifierSet, Error> {
let msg = QueryMsg::VerifierSet { verifier_set_id };
self.client.query(&msg).change_context_lazy(|| msg.into())
}

pub fn public_key(
&self,
verifier_address: String,
key_type: KeyType,
) -> Result<PublicKey, Error> {
let msg = QueryMsg::PublicKey {
verifier_address,
key_type,
};
self.client.query(&msg).change_context_lazy(|| msg.into())
}

pub fn is_caller_authorized(
&self,
contract_address: String,
chain_name: ChainName,
) -> Result<bool, Error> {
let msg = QueryMsg::IsCallerAuthorized {
contract_address,
chain_name,
};
self.client.query(&msg).change_context_lazy(|| msg.into())
}
}

#[cfg(test)]
mod test {

use cosmwasm_std::testing::MockQuerier;
use cosmwasm_std::{
from_json, to_json_binary, Addr, QuerierWrapper, SystemError, Uint64, WasmQuery,
};

use crate::client::Client;
use crate::key::{KeyType, PublicKey, Signature};
use crate::msg::QueryMsg;
use crate::multisig::Multisig;
use crate::test::common::{build_verifier_set, ecdsa_test_data};
use crate::types::MultisigState;

#[test]
fn query_multisig_session_returns_error_when_query_errors() {
let (querier, addr) = setup_queries_to_fail();
let client: Client = client::Client::new(QuerierWrapper::new(&querier), &addr).into();

let session_id: Uint64 = 1u64.into();
let res = client.multisig(session_id);
assert!(res.is_err());
goldie::assert!(res.unwrap_err().to_string());
}

#[test]
fn query_multisig_session_returns_session() {
let (querier, addr) = setup_queries_to_succeed();
let client: Client = client::Client::new(QuerierWrapper::new(&querier), &addr).into();

let session_id: Uint64 = 1u64.into();
let res = client.multisig(session_id);
assert!(res.is_ok());
goldie::assert_json!(res.unwrap());
}

#[test]
fn query_verifier_set_returns_error_when_query_errors() {
let (querier, addr) = setup_queries_to_fail();
let client: Client = client::Client::new(QuerierWrapper::new(&querier), &addr).into();

let verifier_set_id = "my_set".to_string();
let res = client.verifier_set(verifier_set_id.clone());
assert!(res.is_err());
goldie::assert!(res.unwrap_err().to_string());
}

#[test]
fn query_verifier_set_returns_verifier_set() {
let (querier, addr) = setup_queries_to_succeed();
let client: Client = client::Client::new(QuerierWrapper::new(&querier), &addr).into();

let verifier_set_id = "my_set".to_string();
let res = client.verifier_set(verifier_set_id.clone());
assert!(res.is_ok());
goldie::assert_json!(res.unwrap());
}

#[test]
fn query_public_key_returns_error_when_query_errors() {
let (querier, addr) = setup_queries_to_fail();
let client: Client = client::Client::new(QuerierWrapper::new(&querier), &addr).into();

let verifier_address = Addr::unchecked("verifier").to_string();
let key_type = crate::key::KeyType::Ecdsa;
let res = client.public_key(verifier_address.clone(), key_type);
assert!(res.is_err());
goldie::assert!(res.unwrap_err().to_string());
}

#[test]
fn query_public_key_returns_public_key() {
let (querier, addr) = setup_queries_to_succeed();
let client: Client = client::Client::new(QuerierWrapper::new(&querier), &addr).into();

let verifier_address = Addr::unchecked("verifier").to_string();
let key_type = crate::key::KeyType::Ecdsa;
let res = client.public_key(verifier_address.clone(), key_type);
println!("{:?}", res);
assert!(res.is_ok());
goldie::assert_json!(res.unwrap());
}

#[test]
fn query_is_caller_authorized_returns_error_when_query_errors() {
let (querier, addr) = setup_queries_to_fail();
let client: Client = client::Client::new(QuerierWrapper::new(&querier), &addr).into();

let contract_address = Addr::unchecked("prover").to_string();
let chain_name = "ethereum".parse().unwrap();
let res = client.is_caller_authorized(contract_address, chain_name);
assert!(res.is_err());
goldie::assert!(res.unwrap_err().to_string());
}

#[test]
fn query_is_caller_authorized_returns_caller_authorization() {
let (querier, addr) = setup_queries_to_succeed();
let client: Client = client::Client::new(QuerierWrapper::new(&querier), &addr).into();

let contract_address = Addr::unchecked("prover").to_string();
let chain_name = "ethereum".parse().unwrap();
let res = client.is_caller_authorized(contract_address, chain_name);
assert!(res.is_ok());
goldie::assert_json!(res.unwrap());
}

fn setup_queries_to_fail() -> (MockQuerier, Addr) {
let addr = "multisig";

let mut querier = MockQuerier::default();
querier.update_wasm(move |msg| match msg {
WasmQuery::Smart {
contract_addr,
msg: _,
} if contract_addr == addr => {
Err(SystemError::Unknown {}).into() // simulate cryptic error seen in production
}
_ => panic!("unexpected query: {:?}", msg),
});

(querier, Addr::unchecked(addr))
}

fn setup_queries_to_succeed() -> (MockQuerier, Addr) {
let addr = "multisig";

let mut querier = MockQuerier::default();
querier.update_wasm(move |msg| match msg {
WasmQuery::Smart { contract_addr, msg } if contract_addr == addr => {
let msg = from_json::<QueryMsg>(msg).unwrap();
match msg {
QueryMsg::Multisig { session_id: _ } => Ok(to_json_binary(&Multisig {
state: MultisigState::Completed { completed_at: 1 },
verifier_set: build_verifier_set(
crate::key::KeyType::Ecdsa,
&ecdsa_test_data::signers(),
),

signatures: ecdsa_test_data::signers()
.into_iter()
.map(|signer| {
(
signer.address.to_string(),
Signature::try_from((KeyType::Ecdsa, signer.signature))
.unwrap(),
)
})
.collect(),
})
.into())
.into(),
QueryMsg::VerifierSet { verifier_set_id: _ } => Ok(to_json_binary(
&build_verifier_set(KeyType::Ecdsa, &ecdsa_test_data::signers()),
)
.into())
.into(),
QueryMsg::PublicKey {
verifier_address: _,
key_type: _,
} => Ok(to_json_binary(
&PublicKey::try_from((KeyType::Ecdsa, ecdsa_test_data::pub_key())).unwrap(),
)
.into())
.into(),
QueryMsg::IsCallerAuthorized {
contract_address: _,
chain_name: _,
} => Ok(to_json_binary(&true).into()).into(),
}
}
_ => panic!("unexpected query: {:?}", msg),
});

(querier, Addr::unchecked(addr))
}
}
1 change: 1 addition & 0 deletions contracts/multisig/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod client;
pub mod contract;
pub mod error;
pub mod events;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
true
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
failed to query multisig contract for caller authorization. contract_address: prover, chain_name: ethereum
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
failed to query multisig contract for multisig session. session_id: 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"state": {
"completed": {
"completed_at": 1
}
},
"verifier_set": {
"signers": {
"signer1": {
"address": "signer1",
"weight": "1",
"pub_key": {
"ecdsa": "025e0231bfad810e5276e2cf9eb2f3f380ce0bdf6d84c3b6173499d3ddcc008856"
}
},
"signer2": {
"address": "signer2",
"weight": "1",
"pub_key": {
"ecdsa": "036ff6f4b2bc5e08aba924bd8fd986608f3685ca651a015b3d9d6a656de14769fe"
}
},
"signer3": {
"address": "signer3",
"weight": "1",
"pub_key": {
"ecdsa": "03686cbbef9f9e9a5c852883cb2637b55fc76bee6ee6a3ff636e7bea2e41beece4"
}
}
},
"threshold": "2",
"created_at": 0
},
"signatures": {
"signer2": {
"ecdsa": "a7ec5d1c15e84ba4b5da23fee49d77c5c81b3b1859411d1ef8193bf5a39783c76813e4cf4e1e1bfa0ea19c9f5b61d25ce978da137f3adb1730cba3d842702e72"
},
"signer3": {
"ecdsa": "d1bc22fd89d97dfe4091c73d2002823ca9ab29b742ae531d2560bf2abafb313f7d2c3263d09d9aa72f01ed1d49046e39f6513ea61241fd59cc53d02fc4222351"
},
"signer1": {
"ecdsa": "d7822dd89b9df02d64b91f69cff5811dfd4de16b792d9c6054b417c733bbcc542c1e504c8a1dffac94b5828a93e33a6b45d1bf59b2f9f28ffa56b8398d68a1c5"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
failed to query multisig contract for verifier public key. verifier_address: verifier, key_type: Ecdsa
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"ecdsa": "025e0231bfad810e5276e2cf9eb2f3f380ce0bdf6d84c3b6173499d3ddcc008856"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
failed to query multisig contract for verifier set: verifier_set_id: my_set
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"signers": {
"signer1": {
"address": "signer1",
"weight": "1",
"pub_key": {
"ecdsa": "025e0231bfad810e5276e2cf9eb2f3f380ce0bdf6d84c3b6173499d3ddcc008856"
}
},
"signer2": {
"address": "signer2",
"weight": "1",
"pub_key": {
"ecdsa": "036ff6f4b2bc5e08aba924bd8fd986608f3685ca651a015b3d9d6a656de14769fe"
}
},
"signer3": {
"address": "signer3",
"weight": "1",
"pub_key": {
"ecdsa": "03686cbbef9f9e9a5c852883cb2637b55fc76bee6ee6a3ff636e7bea2e41beece4"
}
}
},
"threshold": "2",
"created_at": 0
}

0 comments on commit a73560d

Please sign in to comment.