Skip to content

Commit

Permalink
feat(minor-service-registry)!: verifier details query (#591)
Browse files Browse the repository at this point in the history
  • Loading branch information
maancham authored Sep 4, 2024
1 parent 1feb216 commit 864e7b4
Show file tree
Hide file tree
Showing 6 changed files with 199 additions and 35 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/service-registry/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ cosmwasm-std = { workspace = true }
cw-storage-plus = { workspace = true }
cw2 = { workspace = true }
error-stack = { workspace = true }
itertools = { workspace = true }
msgs-derive = { workspace = true }
report = { workspace = true }
router-api = { workspace = true }
Expand Down
106 changes: 102 additions & 4 deletions contracts/service-registry/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ use axelar_wasm_std::{address, permission_control, FnExt};
#[cfg(not(feature = "library"))]
use cosmwasm_std::entry_point;
use cosmwasm_std::{
to_json_binary, Addr, BankMsg, Binary, Coin, Deps, DepsMut, Empty, Env, MessageInfo, Order,
to_json_binary, Addr, BankMsg, Binary, Coin, Deps, DepsMut, Empty, Env, MessageInfo,
QueryRequest, Response, Storage, WasmQuery,
};
use error_stack::{bail, Report, ResultExt};

use crate::error::ContractError;
use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg};
use crate::state::{AuthorizationState, BondingState, Service, Verifier, SERVICES, VERIFIERS};
use crate::state::{AuthorizationState, BondingState, Service, SERVICES, VERIFIERS};

mod execute;
mod migrations;
Expand Down Expand Up @@ -182,6 +182,7 @@ pub fn migrate(

#[cfg(test)]
mod test {
use std::collections::HashSet;
use std::str::FromStr;

use axelar_wasm_std::error::err_contains;
Expand All @@ -193,7 +194,8 @@ mod test {
use router_api::ChainName;

use super::*;
use crate::state::{WeightedVerifier, VERIFIER_WEIGHT};
use crate::msg::VerifierDetails;
use crate::state::{Verifier, WeightedVerifier, VERIFIER_WEIGHT};

const GOVERNANCE_ADDRESS: &str = "governance";
const UNAUTHORIZED_ADDRESS: &str = "unauthorized";
Expand Down Expand Up @@ -2055,7 +2057,7 @@ mod test {
},
);
assert!(res.is_ok());
let verifier: Verifier = from_json(
let verifier2_details: VerifierDetails = from_json(
query(
deps.as_ref(),
mock_env(),
Expand All @@ -2067,6 +2069,7 @@ mod test {
.unwrap(),
)
.unwrap();
let verifier = verifier2_details.verifier;

assert_eq!(
verifier.bonding_state,
Expand Down Expand Up @@ -2111,4 +2114,99 @@ mod test {
ContractError::VerifierJailed
));
}

#[test]
fn get_single_verifier_details() {
let mut deps = setup();

let service_name = "validators";
let min_verifier_bond: nonempty::Uint128 = Uint128::new(100).try_into().unwrap();
let res = execute(
deps.as_mut(),
mock_env(),
mock_info(GOVERNANCE_ADDRESS, &[]),
ExecuteMsg::RegisterService {
service_name: service_name.into(),
coordinator_contract: Addr::unchecked(COORDINATOR_ADDRESS),
min_num_verifiers: 0,
max_num_verifiers: Some(100),
min_verifier_bond,
bond_denom: AXL_DENOMINATION.into(),
unbonding_period_days: 10,
description: "Some service".into(),
},
);
assert!(res.is_ok());

let res = execute(
deps.as_mut(),
mock_env(),
mock_info(GOVERNANCE_ADDRESS, &[]),
ExecuteMsg::AuthorizeVerifiers {
verifiers: vec![VERIFIER_ADDRESS.into()],
service_name: service_name.into(),
},
);
assert!(res.is_ok());

let res = execute(
deps.as_mut(),
mock_env(),
mock_info(
VERIFIER_ADDRESS,
&coins(min_verifier_bond.into_inner().u128(), AXL_DENOMINATION),
),
ExecuteMsg::BondVerifier {
service_name: service_name.into(),
},
);
assert!(res.is_ok());

let chains = vec![
ChainName::from_str("ethereum").unwrap(),
ChainName::from_str("binance").unwrap(),
ChainName::from_str("avalanche").unwrap(),
];
let res = execute(
deps.as_mut(),
mock_env(),
mock_info(VERIFIER_ADDRESS, &[]),
ExecuteMsg::RegisterChainSupport {
service_name: service_name.into(),
chains: chains.clone(),
},
);
assert!(res.is_ok());

let verifier_details: VerifierDetails = from_json(
query(
deps.as_ref(),
mock_env(),
QueryMsg::Verifier {
service_name: service_name.into(),
verifier: VERIFIER_ADDRESS.into(),
},
)
.unwrap(),
)
.unwrap();

assert_eq!(
verifier_details.verifier,
Verifier {
address: Addr::unchecked(VERIFIER_ADDRESS),
bonding_state: BondingState::Bonded {
amount: min_verifier_bond
},
authorization_state: AuthorizationState::Authorized,
service_name: service_name.into()
}
);
assert_eq!(verifier_details.weight, VERIFIER_WEIGHT);

let expected_chains: HashSet<ChainName> = chains.into_iter().collect();
let actual_chains: HashSet<ChainName> =
verifier_details.supported_chains.into_iter().collect();
assert_eq!(expected_chains, actual_chains);
}
}
61 changes: 39 additions & 22 deletions contracts/service-registry/src/contract/query.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use cosmwasm_std::Order;
use itertools::Itertools;
use router_api::ChainName;

use super::*;
use crate::msg::VerifierDetails;
use crate::state::{WeightedVerifier, VERIFIERS, VERIFIERS_PER_CHAIN, VERIFIER_WEIGHT};

pub fn active_verifiers(
Expand All @@ -13,21 +16,26 @@ pub fn active_verifiers(
.ok_or(ContractError::ServiceNotFound)?;

let verifiers: Vec<_> = VERIFIERS_PER_CHAIN
.prefix((&service_name, &chain_name))
.range(deps.storage, None, None, Order::Ascending)
.map(|res| res.and_then(|(addr, _)| VERIFIERS.load(deps.storage, (&service_name, &addr))))
.collect::<Result<Vec<Verifier>, _>>()?
.into_iter()
.filter(|verifier| match verifier.bonding_state {
BondingState::Bonded { amount } => amount >= service.min_verifier_bond,
_ => false,
.prefix((service_name.clone(), chain_name.clone()))
.keys(deps.storage, None, None, Order::Ascending)
.filter_map_ok(|verifier_addr| {
VERIFIERS
.may_load(deps.storage, (&service_name, &verifier_addr))
.ok()
.flatten()
})
.filter(|verifier| verifier.authorization_state == AuthorizationState::Authorized)
.map(|verifier| WeightedVerifier {
.filter_ok(|verifier| {
matches!(
verifier.bonding_state,
BondingState::Bonded { amount } if amount >= service.min_verifier_bond
)
})
.filter_ok(|verifier| verifier.authorization_state == AuthorizationState::Authorized)
.map_ok(|verifier| WeightedVerifier {
verifier_info: verifier,
weight: VERIFIER_WEIGHT, // all verifiers have an identical const weight for now
})
.collect();
.try_collect()?;

if verifiers.len() < service.min_num_verifiers.into() {
Err(ContractError::NotEnoughVerifiers)
Expand All @@ -40,17 +48,26 @@ pub fn verifier(
deps: Deps,
service_name: String,
verifier: String,
) -> Result<Verifier, axelar_wasm_std::error::ContractError> {
VERIFIERS
.may_load(
deps.storage,
(
&service_name,
&address::validate_cosmwasm_address(deps.api, &verifier)?,
),
)?
.ok_or(ContractError::VerifierNotFound)?
.then(Ok)
) -> Result<VerifierDetails, axelar_wasm_std::error::ContractError> {
let verifier_addr = address::validate_cosmwasm_address(deps.api, &verifier)?;

let verifier = VERIFIERS
.may_load(deps.storage, (&service_name, &verifier_addr))?
.ok_or(ContractError::VerifierNotFound)?;

let supported_chains = VERIFIERS_PER_CHAIN
.idx
.verifier_address
.prefix((service_name, verifier_addr.clone()))
.keys(deps.storage, None, None, Order::Ascending)
.map_ok(|(_, chain, _)| chain)
.try_collect()?;

Ok(VerifierDetails {
verifier,
weight: VERIFIER_WEIGHT,
supported_chains,
})
}

pub fn service(deps: Deps, service_name: String) -> Result<Service, ContractError> {
Expand Down
13 changes: 12 additions & 1 deletion contracts/service-registry/src/msg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ use cosmwasm_schema::{cw_serde, QueryResponses};
use cosmwasm_std::Addr;
use msgs_derive::EnsurePermissions;
use router_api::ChainName;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

use crate::Verifier;

#[cw_serde]
pub struct InstantiateMsg {
Expand Down Expand Up @@ -79,13 +83,20 @@ pub enum QueryMsg {
#[returns(crate::state::Service)]
Service { service_name: String },

#[returns(crate::state::Verifier)]
#[returns(VerifierDetails)]
Verifier {
service_name: String,
verifier: String,
},
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
pub struct VerifierDetails {
pub verifier: Verifier,
pub weight: nonempty::Uint128,
pub supported_chains: Vec<ChainName>,
}

#[cw_serde]
pub struct MigrateMsg {
pub coordinator_contract: Addr,
Expand Down
52 changes: 44 additions & 8 deletions contracts/service-registry/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use axelar_wasm_std::nonempty;
use axelar_wasm_std::snapshot::Participant;
use cosmwasm_schema::cw_serde;
use cosmwasm_std::{Addr, Storage, Timestamp, Uint128};
use cw_storage_plus::Map;
use cw_storage_plus::{Index, IndexList, IndexedMap, KeyDeserialize, Map, MultiIndex};
use router_api::ChainName;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -143,12 +143,46 @@ pub enum AuthorizationState {
Jailed,
}

type ServiceName = str;
pub struct VerifierPerChainIndexes<'a> {
pub verifier_address: MultiIndex<
'a,
(ServiceName, VerifierAddress),
(),
(ServiceName, ChainName, VerifierAddress),
>,
}

impl<'a> IndexList<()> for VerifierPerChainIndexes<'a> {
fn get_indexes(&'_ self) -> Box<dyn Iterator<Item = &'_ dyn Index<()>> + '_> {
let v: Vec<&dyn Index<()>> = vec![&self.verifier_address];
Box::new(v.into_iter())
}
}

pub const VERIFIERS_PER_CHAIN: IndexedMap<
(ServiceName, ChainName, VerifierAddress),
(),
VerifierPerChainIndexes,
> = IndexedMap::new(
"verifiers_per_chain",
VerifierPerChainIndexes {
verifier_address: MultiIndex::new(
|pk: &[u8], _: &()| {
let (service_name, _, verifier) =
<(ServiceName, ChainName, VerifierAddress)>::from_slice(pk)
.expect("invalid primary key");
(service_name, verifier)
},
"verifiers_per_chain",
"verifiers_per_chain__address",
),
},
);

type ServiceName = String;
type VerifierAddress = Addr;

pub const SERVICES: Map<&ServiceName, Service> = Map::new("services");
pub const VERIFIERS_PER_CHAIN: Map<(&ServiceName, &ChainName, &VerifierAddress), ()> =
Map::new("verifiers_per_chain");
pub const VERIFIERS: Map<(&ServiceName, &VerifierAddress), Verifier> = Map::new("verifiers");

pub fn register_chains_support(
Expand All @@ -158,9 +192,12 @@ pub fn register_chains_support(
verifier: VerifierAddress,
) -> Result<(), ContractError> {
for chain in chains.iter() {
VERIFIERS_PER_CHAIN.save(storage, (&service_name, chain, &verifier), &())?;
VERIFIERS_PER_CHAIN.save(
storage,
(service_name.clone(), chain.clone(), verifier.clone()),
&(),
)?;
}

Ok(())
}

Expand All @@ -171,9 +208,8 @@ pub fn deregister_chains_support(
verifier: VerifierAddress,
) -> Result<(), ContractError> {
for chain in chains {
VERIFIERS_PER_CHAIN.remove(storage, (&service_name, &chain, &verifier));
VERIFIERS_PER_CHAIN.remove(storage, (service_name.clone(), chain, verifier.clone()))?;
}

Ok(())
}

Expand Down

0 comments on commit 864e7b4

Please sign in to comment.