From 67a4ce2ab24a105e40e6652cafc667a7117cba2e Mon Sep 17 00:00:00 2001 From: CJ Cobb <46455409+cjcobb23@users.noreply.github.com> Date: Thu, 5 Sep 2024 16:16:24 -0400 Subject: [PATCH] feat(minor-service-registry): add client for queries (#600) --- Cargo.lock | 2 + contracts/service-registry/Cargo.toml | 2 + contracts/service-registry/src/client.rs | 243 ++++++++++++++++++ contracts/service-registry/src/lib.rs | 1 + ..._verifiers_returns_active_verifiers.golden | 15 ++ ...iers_returns_error_when_query_fails.golden | 1 + ...vice_returns_error_when_query_fails.golden | 1 + .../query_service_returns_service.golden | 10 + ...fier_returns_error_when_query_fails.golden | 1 + .../query_verifier_returns_verifier.golden | 10 + 10 files changed, 286 insertions(+) create mode 100644 contracts/service-registry/src/client.rs create mode 100644 contracts/service-registry/src/testdata/query_active_verifiers_returns_active_verifiers.golden create mode 100644 contracts/service-registry/src/testdata/query_active_verifiers_returns_error_when_query_fails.golden create mode 100644 contracts/service-registry/src/testdata/query_service_returns_error_when_query_fails.golden create mode 100644 contracts/service-registry/src/testdata/query_service_returns_service.golden create mode 100644 contracts/service-registry/src/testdata/query_verifier_returns_error_when_query_fails.golden create mode 100644 contracts/service-registry/src/testdata/query_verifier_returns_verifier.golden diff --git a/Cargo.lock b/Cargo.lock index e8262480a..f87fb9d59 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7530,6 +7530,7 @@ name = "service-registry" version = "1.0.0" dependencies = [ "axelar-wasm-std", + "client", "coordinator", "cosmwasm-schema", "cosmwasm-std", @@ -7537,6 +7538,7 @@ dependencies = [ "cw-storage-plus 1.2.0", "cw2 1.1.2", "error-stack", + "goldie", "integration-tests", "itertools 0.11.0", "msgs-derive", diff --git a/contracts/service-registry/Cargo.toml b/contracts/service-registry/Cargo.toml index 14262c47e..7045cff22 100644 --- a/contracts/service-registry/Cargo.toml +++ b/contracts/service-registry/Cargo.toml @@ -33,6 +33,7 @@ optimize = """docker run --rm -v "$(pwd)":/code \ [dependencies] axelar-wasm-std = { workspace = true, features = ["derive"] } +client = { workspace = true } coordinator = { workspace = true, features = ["library"] } cosmwasm-schema = { workspace = true } cosmwasm-std = { workspace = true } @@ -49,6 +50,7 @@ thiserror = { workspace = true } [dev-dependencies] cw-multi-test = "0.15.1" +goldie = { workspace = true } integration-tests = { workspace = true } [lints] diff --git a/contracts/service-registry/src/client.rs b/contracts/service-registry/src/client.rs new file mode 100644 index 000000000..619dd69fd --- /dev/null +++ b/contracts/service-registry/src/client.rs @@ -0,0 +1,243 @@ +use error_stack::ResultExt; +use router_api::ChainName; + +use crate::msg::{ExecuteMsg, QueryMsg}; +use crate::{Service, Verifier, WeightedVerifier}; + +type Result = error_stack::Result; + +#[derive(thiserror::Error, Debug, PartialEq)] +pub enum Error { + #[error("failed to query service registry for active verifiers for service {service_name} and chain {chain_name}")] + QueryActiveVerifiers { + service_name: String, + chain_name: ChainName, + }, + + #[error("failed to query service registry for service {0}")] + QueryService(String), + + #[error("failed to query service registry for verifier {verifier} of service {service_name}")] + QueryVerifier { + service_name: String, + verifier: String, + }, +} + +impl From for Error { + fn from(value: QueryMsg) -> Self { + match value { + QueryMsg::ActiveVerifiers { + service_name, + chain_name, + } => Error::QueryActiveVerifiers { + service_name, + chain_name, + }, + QueryMsg::Service { service_name } => Error::QueryService(service_name), + QueryMsg::Verifier { + service_name, + verifier, + } => Error::QueryVerifier { + service_name, + verifier, + }, + } + } +} + +impl<'a> From> for Client<'a> { + fn from(client: client::Client<'a, ExecuteMsg, QueryMsg>) -> Self { + Client { client } + } +} + +pub struct Client<'a> { + client: client::Client<'a, ExecuteMsg, QueryMsg>, +} + +impl<'a> Client<'a> { + // TODO: add execute methods + + pub fn active_verifiers( + &self, + service_name: String, + chain_name: ChainName, + ) -> Result> { + let msg = QueryMsg::ActiveVerifiers { + service_name, + chain_name, + }; + self.client.query(&msg).change_context_lazy(|| msg.into()) + } + + pub fn service(&self, service_name: String) -> Result { + let msg = QueryMsg::Service { service_name }; + self.client.query(&msg).change_context_lazy(|| msg.into()) + } + + pub fn verifier(&self, service_name: String, verifier: String) -> Result { + let msg = QueryMsg::Verifier { + service_name, + verifier, + }; + self.client.query(&msg).change_context_lazy(|| msg.into()) + } +} + +#[cfg(test)] +mod test { + + use axelar_wasm_std::nonempty::Uint128; + use cosmwasm_std::testing::MockQuerier; + use cosmwasm_std::{from_json, to_json_binary, Addr, QuerierWrapper, SystemError, WasmQuery}; + use router_api::ChainName; + + use crate::client::Client; + use crate::msg::QueryMsg; + use crate::{Service, Verifier, WeightedVerifier}; + + #[test] + fn query_active_verifiers_returns_error_when_query_fails() { + let (querier, addr) = setup_queries_to_fail(); + let client: Client = client::Client::new(QuerierWrapper::new(&querier), &addr).into(); + let service_name = "verifiers".to_string(); + let chain_name: ChainName = "ethereum".try_into().unwrap(); + let res = client.active_verifiers(service_name.clone(), chain_name.clone()); + + assert!(res.is_err()); + goldie::assert!(res.unwrap_err().to_string()); + } + + #[test] + fn query_active_verifiers_returns_active_verifiers() { + let (querier, addr) = setup_queries_to_succeed(); + let client: Client = client::Client::new(QuerierWrapper::new(&querier), &addr).into(); + let service_name = "verifiers".to_string(); + let chain_name: ChainName = "ethereum".try_into().unwrap(); + let res = client.active_verifiers(service_name.clone(), chain_name.clone()); + + assert!(res.is_ok()); + goldie::assert_json!(res.unwrap()); + } + + #[test] + fn query_verifier_returns_error_when_query_fails() { + let (querier, addr) = setup_queries_to_fail(); + let client: Client = client::Client::new(QuerierWrapper::new(&querier), &addr).into(); + let service_name = "verifiers".to_string(); + let verifier = Addr::unchecked("verifier").to_string(); + let res = client.verifier(service_name.clone(), verifier.clone()); + + assert!(res.is_err()); + goldie::assert!(res.unwrap_err().to_string()); + } + + #[test] + fn query_verifier_returns_verifier() { + let (querier, addr) = setup_queries_to_succeed(); + let client: Client = client::Client::new(QuerierWrapper::new(&querier), &addr).into(); + let service_name = "verifiers".to_string(); + let verifier = Addr::unchecked("verifier").to_string(); + let res = client.verifier(service_name.clone(), verifier.clone()); + + assert!(res.is_ok()); + goldie::assert_json!(res.unwrap()); + } + + #[test] + fn query_service_returns_error_when_query_fails() { + let (querier, addr) = setup_queries_to_fail(); + let client: Client = client::Client::new(QuerierWrapper::new(&querier), &addr).into(); + let service_name = "verifiers".to_string(); + let res = client.service(service_name.clone()); + + assert!(res.is_err()); + goldie::assert!(res.unwrap_err().to_string()); + } + + #[test] + fn query_service_returns_service() { + let (querier, addr) = setup_queries_to_succeed(); + let client: Client = client::Client::new(QuerierWrapper::new(&querier), &addr).into(); + let service_name = "verifiers".to_string(); + let res = client.service(service_name.clone()); + + 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 = "service-registry"; + + 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::(msg).unwrap(); + match msg { + QueryMsg::ActiveVerifiers { + service_name, + chain_name: _, + } => Ok(to_json_binary(&vec![WeightedVerifier { + verifier_info: Verifier { + address: Addr::unchecked("verifier"), + bonding_state: crate::BondingState::Bonded { + amount: Uint128::one(), + }, + authorization_state: crate::AuthorizationState::Authorized, + service_name, + }, + weight: Uint128::one(), + }]) + .into()) + .into(), + QueryMsg::Service { service_name } => Ok(to_json_binary(&Service { + name: service_name, + coordinator_contract: Addr::unchecked("coordinator"), + min_num_verifiers: 1, + max_num_verifiers: None, + min_verifier_bond: Uint128::one(), + bond_denom: "uaxl".into(), + unbonding_period_days: 10, + description: "some service".into(), + }) + .into()) + .into(), + QueryMsg::Verifier { + service_name, + verifier, + } => Ok(to_json_binary(&Verifier { + address: Addr::unchecked(verifier), + bonding_state: crate::BondingState::Bonded { + amount: Uint128::one(), + }, + authorization_state: crate::AuthorizationState::Authorized, + service_name, + }) + .into()) + .into(), + } + } + _ => panic!("unexpected query: {:?}", msg), + }); + + (querier, Addr::unchecked(addr)) + } +} diff --git a/contracts/service-registry/src/lib.rs b/contracts/service-registry/src/lib.rs index d439e2a24..1c84095b8 100644 --- a/contracts/service-registry/src/lib.rs +++ b/contracts/service-registry/src/lib.rs @@ -1,3 +1,4 @@ +pub mod client; pub mod contract; pub mod error; pub mod helpers; diff --git a/contracts/service-registry/src/testdata/query_active_verifiers_returns_active_verifiers.golden b/contracts/service-registry/src/testdata/query_active_verifiers_returns_active_verifiers.golden new file mode 100644 index 000000000..69c5ecc8e --- /dev/null +++ b/contracts/service-registry/src/testdata/query_active_verifiers_returns_active_verifiers.golden @@ -0,0 +1,15 @@ +[ + { + "verifier_info": { + "address": "verifier", + "bonding_state": { + "Bonded": { + "amount": "1" + } + }, + "authorization_state": "Authorized", + "service_name": "verifiers" + }, + "weight": "1" + } +] \ No newline at end of file diff --git a/contracts/service-registry/src/testdata/query_active_verifiers_returns_error_when_query_fails.golden b/contracts/service-registry/src/testdata/query_active_verifiers_returns_error_when_query_fails.golden new file mode 100644 index 000000000..d8db5ba46 --- /dev/null +++ b/contracts/service-registry/src/testdata/query_active_verifiers_returns_error_when_query_fails.golden @@ -0,0 +1 @@ +failed to query service registry for active verifiers for service verifiers and chain ethereum \ No newline at end of file diff --git a/contracts/service-registry/src/testdata/query_service_returns_error_when_query_fails.golden b/contracts/service-registry/src/testdata/query_service_returns_error_when_query_fails.golden new file mode 100644 index 000000000..05243ab1f --- /dev/null +++ b/contracts/service-registry/src/testdata/query_service_returns_error_when_query_fails.golden @@ -0,0 +1 @@ +failed to query service registry for service verifiers \ No newline at end of file diff --git a/contracts/service-registry/src/testdata/query_service_returns_service.golden b/contracts/service-registry/src/testdata/query_service_returns_service.golden new file mode 100644 index 000000000..71da486cf --- /dev/null +++ b/contracts/service-registry/src/testdata/query_service_returns_service.golden @@ -0,0 +1,10 @@ +{ + "name": "verifiers", + "coordinator_contract": "coordinator", + "min_num_verifiers": 1, + "max_num_verifiers": null, + "min_verifier_bond": "1", + "bond_denom": "uaxl", + "unbonding_period_days": 10, + "description": "some service" +} \ No newline at end of file diff --git a/contracts/service-registry/src/testdata/query_verifier_returns_error_when_query_fails.golden b/contracts/service-registry/src/testdata/query_verifier_returns_error_when_query_fails.golden new file mode 100644 index 000000000..86b364672 --- /dev/null +++ b/contracts/service-registry/src/testdata/query_verifier_returns_error_when_query_fails.golden @@ -0,0 +1 @@ +failed to query service registry for verifier verifier of service verifiers \ No newline at end of file diff --git a/contracts/service-registry/src/testdata/query_verifier_returns_verifier.golden b/contracts/service-registry/src/testdata/query_verifier_returns_verifier.golden new file mode 100644 index 000000000..1a51940c2 --- /dev/null +++ b/contracts/service-registry/src/testdata/query_verifier_returns_verifier.golden @@ -0,0 +1,10 @@ +{ + "address": "verifier", + "bonding_state": { + "Bonded": { + "amount": "1" + } + }, + "authorization_state": "Authorized", + "service_name": "verifiers" +} \ No newline at end of file