Skip to content

Commit 864e7b4

Browse files
authored
feat(minor-service-registry)!: verifier details query (#591)
1 parent 1feb216 commit 864e7b4

File tree

6 files changed

+199
-35
lines changed

6 files changed

+199
-35
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

contracts/service-registry/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ cosmwasm-std = { workspace = true }
3939
cw-storage-plus = { workspace = true }
4040
cw2 = { workspace = true }
4141
error-stack = { workspace = true }
42+
itertools = { workspace = true }
4243
msgs-derive = { workspace = true }
4344
report = { workspace = true }
4445
router-api = { workspace = true }

contracts/service-registry/src/contract.rs

Lines changed: 102 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@ use axelar_wasm_std::{address, permission_control, FnExt};
22
#[cfg(not(feature = "library"))]
33
use cosmwasm_std::entry_point;
44
use cosmwasm_std::{
5-
to_json_binary, Addr, BankMsg, Binary, Coin, Deps, DepsMut, Empty, Env, MessageInfo, Order,
5+
to_json_binary, Addr, BankMsg, Binary, Coin, Deps, DepsMut, Empty, Env, MessageInfo,
66
QueryRequest, Response, Storage, WasmQuery,
77
};
88
use error_stack::{bail, Report, ResultExt};
99

1010
use crate::error::ContractError;
1111
use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg};
12-
use crate::state::{AuthorizationState, BondingState, Service, Verifier, SERVICES, VERIFIERS};
12+
use crate::state::{AuthorizationState, BondingState, Service, SERVICES, VERIFIERS};
1313

1414
mod execute;
1515
mod migrations;
@@ -182,6 +182,7 @@ pub fn migrate(
182182

183183
#[cfg(test)]
184184
mod test {
185+
use std::collections::HashSet;
185186
use std::str::FromStr;
186187

187188
use axelar_wasm_std::error::err_contains;
@@ -193,7 +194,8 @@ mod test {
193194
use router_api::ChainName;
194195

195196
use super::*;
196-
use crate::state::{WeightedVerifier, VERIFIER_WEIGHT};
197+
use crate::msg::VerifierDetails;
198+
use crate::state::{Verifier, WeightedVerifier, VERIFIER_WEIGHT};
197199

198200
const GOVERNANCE_ADDRESS: &str = "governance";
199201
const UNAUTHORIZED_ADDRESS: &str = "unauthorized";
@@ -2055,7 +2057,7 @@ mod test {
20552057
},
20562058
);
20572059
assert!(res.is_ok());
2058-
let verifier: Verifier = from_json(
2060+
let verifier2_details: VerifierDetails = from_json(
20592061
query(
20602062
deps.as_ref(),
20612063
mock_env(),
@@ -2067,6 +2069,7 @@ mod test {
20672069
.unwrap(),
20682070
)
20692071
.unwrap();
2072+
let verifier = verifier2_details.verifier;
20702073

20712074
assert_eq!(
20722075
verifier.bonding_state,
@@ -2111,4 +2114,99 @@ mod test {
21112114
ContractError::VerifierJailed
21122115
));
21132116
}
2117+
2118+
#[test]
2119+
fn get_single_verifier_details() {
2120+
let mut deps = setup();
2121+
2122+
let service_name = "validators";
2123+
let min_verifier_bond: nonempty::Uint128 = Uint128::new(100).try_into().unwrap();
2124+
let res = execute(
2125+
deps.as_mut(),
2126+
mock_env(),
2127+
mock_info(GOVERNANCE_ADDRESS, &[]),
2128+
ExecuteMsg::RegisterService {
2129+
service_name: service_name.into(),
2130+
coordinator_contract: Addr::unchecked(COORDINATOR_ADDRESS),
2131+
min_num_verifiers: 0,
2132+
max_num_verifiers: Some(100),
2133+
min_verifier_bond,
2134+
bond_denom: AXL_DENOMINATION.into(),
2135+
unbonding_period_days: 10,
2136+
description: "Some service".into(),
2137+
},
2138+
);
2139+
assert!(res.is_ok());
2140+
2141+
let res = execute(
2142+
deps.as_mut(),
2143+
mock_env(),
2144+
mock_info(GOVERNANCE_ADDRESS, &[]),
2145+
ExecuteMsg::AuthorizeVerifiers {
2146+
verifiers: vec![VERIFIER_ADDRESS.into()],
2147+
service_name: service_name.into(),
2148+
},
2149+
);
2150+
assert!(res.is_ok());
2151+
2152+
let res = execute(
2153+
deps.as_mut(),
2154+
mock_env(),
2155+
mock_info(
2156+
VERIFIER_ADDRESS,
2157+
&coins(min_verifier_bond.into_inner().u128(), AXL_DENOMINATION),
2158+
),
2159+
ExecuteMsg::BondVerifier {
2160+
service_name: service_name.into(),
2161+
},
2162+
);
2163+
assert!(res.is_ok());
2164+
2165+
let chains = vec![
2166+
ChainName::from_str("ethereum").unwrap(),
2167+
ChainName::from_str("binance").unwrap(),
2168+
ChainName::from_str("avalanche").unwrap(),
2169+
];
2170+
let res = execute(
2171+
deps.as_mut(),
2172+
mock_env(),
2173+
mock_info(VERIFIER_ADDRESS, &[]),
2174+
ExecuteMsg::RegisterChainSupport {
2175+
service_name: service_name.into(),
2176+
chains: chains.clone(),
2177+
},
2178+
);
2179+
assert!(res.is_ok());
2180+
2181+
let verifier_details: VerifierDetails = from_json(
2182+
query(
2183+
deps.as_ref(),
2184+
mock_env(),
2185+
QueryMsg::Verifier {
2186+
service_name: service_name.into(),
2187+
verifier: VERIFIER_ADDRESS.into(),
2188+
},
2189+
)
2190+
.unwrap(),
2191+
)
2192+
.unwrap();
2193+
2194+
assert_eq!(
2195+
verifier_details.verifier,
2196+
Verifier {
2197+
address: Addr::unchecked(VERIFIER_ADDRESS),
2198+
bonding_state: BondingState::Bonded {
2199+
amount: min_verifier_bond
2200+
},
2201+
authorization_state: AuthorizationState::Authorized,
2202+
service_name: service_name.into()
2203+
}
2204+
);
2205+
assert_eq!(verifier_details.weight, VERIFIER_WEIGHT);
2206+
2207+
let expected_chains: HashSet<ChainName> = chains.into_iter().collect();
2208+
let actual_chains: HashSet<ChainName> =
2209+
verifier_details.supported_chains.into_iter().collect();
2210+
assert_eq!(expected_chains, actual_chains);
2211+
}
21142212
}

contracts/service-registry/src/contract/query.rs

Lines changed: 39 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1+
use cosmwasm_std::Order;
2+
use itertools::Itertools;
13
use router_api::ChainName;
24

35
use super::*;
6+
use crate::msg::VerifierDetails;
47
use crate::state::{WeightedVerifier, VERIFIERS, VERIFIERS_PER_CHAIN, VERIFIER_WEIGHT};
58

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

1518
let verifiers: Vec<_> = VERIFIERS_PER_CHAIN
16-
.prefix((&service_name, &chain_name))
17-
.range(deps.storage, None, None, Order::Ascending)
18-
.map(|res| res.and_then(|(addr, _)| VERIFIERS.load(deps.storage, (&service_name, &addr))))
19-
.collect::<Result<Vec<Verifier>, _>>()?
20-
.into_iter()
21-
.filter(|verifier| match verifier.bonding_state {
22-
BondingState::Bonded { amount } => amount >= service.min_verifier_bond,
23-
_ => false,
19+
.prefix((service_name.clone(), chain_name.clone()))
20+
.keys(deps.storage, None, None, Order::Ascending)
21+
.filter_map_ok(|verifier_addr| {
22+
VERIFIERS
23+
.may_load(deps.storage, (&service_name, &verifier_addr))
24+
.ok()
25+
.flatten()
2426
})
25-
.filter(|verifier| verifier.authorization_state == AuthorizationState::Authorized)
26-
.map(|verifier| WeightedVerifier {
27+
.filter_ok(|verifier| {
28+
matches!(
29+
verifier.bonding_state,
30+
BondingState::Bonded { amount } if amount >= service.min_verifier_bond
31+
)
32+
})
33+
.filter_ok(|verifier| verifier.authorization_state == AuthorizationState::Authorized)
34+
.map_ok(|verifier| WeightedVerifier {
2735
verifier_info: verifier,
2836
weight: VERIFIER_WEIGHT, // all verifiers have an identical const weight for now
2937
})
30-
.collect();
38+
.try_collect()?;
3139

3240
if verifiers.len() < service.min_num_verifiers.into() {
3341
Err(ContractError::NotEnoughVerifiers)
@@ -40,17 +48,26 @@ pub fn verifier(
4048
deps: Deps,
4149
service_name: String,
4250
verifier: String,
43-
) -> Result<Verifier, axelar_wasm_std::error::ContractError> {
44-
VERIFIERS
45-
.may_load(
46-
deps.storage,
47-
(
48-
&service_name,
49-
&address::validate_cosmwasm_address(deps.api, &verifier)?,
50-
),
51-
)?
52-
.ok_or(ContractError::VerifierNotFound)?
53-
.then(Ok)
51+
) -> Result<VerifierDetails, axelar_wasm_std::error::ContractError> {
52+
let verifier_addr = address::validate_cosmwasm_address(deps.api, &verifier)?;
53+
54+
let verifier = VERIFIERS
55+
.may_load(deps.storage, (&service_name, &verifier_addr))?
56+
.ok_or(ContractError::VerifierNotFound)?;
57+
58+
let supported_chains = VERIFIERS_PER_CHAIN
59+
.idx
60+
.verifier_address
61+
.prefix((service_name, verifier_addr.clone()))
62+
.keys(deps.storage, None, None, Order::Ascending)
63+
.map_ok(|(_, chain, _)| chain)
64+
.try_collect()?;
65+
66+
Ok(VerifierDetails {
67+
verifier,
68+
weight: VERIFIER_WEIGHT,
69+
supported_chains,
70+
})
5471
}
5572

5673
pub fn service(deps: Deps, service_name: String) -> Result<Service, ContractError> {

contracts/service-registry/src/msg.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ use cosmwasm_schema::{cw_serde, QueryResponses};
33
use cosmwasm_std::Addr;
44
use msgs_derive::EnsurePermissions;
55
use router_api::ChainName;
6+
use schemars::JsonSchema;
7+
use serde::{Deserialize, Serialize};
8+
9+
use crate::Verifier;
610

711
#[cw_serde]
812
pub struct InstantiateMsg {
@@ -79,13 +83,20 @@ pub enum QueryMsg {
7983
#[returns(crate::state::Service)]
8084
Service { service_name: String },
8185

82-
#[returns(crate::state::Verifier)]
86+
#[returns(VerifierDetails)]
8387
Verifier {
8488
service_name: String,
8589
verifier: String,
8690
},
8791
}
8892

93+
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
94+
pub struct VerifierDetails {
95+
pub verifier: Verifier,
96+
pub weight: nonempty::Uint128,
97+
pub supported_chains: Vec<ChainName>,
98+
}
99+
89100
#[cw_serde]
90101
pub struct MigrateMsg {
91102
pub coordinator_contract: Addr,

contracts/service-registry/src/state.rs

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use axelar_wasm_std::nonempty;
22
use axelar_wasm_std::snapshot::Participant;
33
use cosmwasm_schema::cw_serde;
44
use cosmwasm_std::{Addr, Storage, Timestamp, Uint128};
5-
use cw_storage_plus::Map;
5+
use cw_storage_plus::{Index, IndexList, IndexedMap, KeyDeserialize, Map, MultiIndex};
66
use router_api::ChainName;
77
use schemars::JsonSchema;
88
use serde::{Deserialize, Serialize};
@@ -143,12 +143,46 @@ pub enum AuthorizationState {
143143
Jailed,
144144
}
145145

146-
type ServiceName = str;
146+
pub struct VerifierPerChainIndexes<'a> {
147+
pub verifier_address: MultiIndex<
148+
'a,
149+
(ServiceName, VerifierAddress),
150+
(),
151+
(ServiceName, ChainName, VerifierAddress),
152+
>,
153+
}
154+
155+
impl<'a> IndexList<()> for VerifierPerChainIndexes<'a> {
156+
fn get_indexes(&'_ self) -> Box<dyn Iterator<Item = &'_ dyn Index<()>> + '_> {
157+
let v: Vec<&dyn Index<()>> = vec![&self.verifier_address];
158+
Box::new(v.into_iter())
159+
}
160+
}
161+
162+
pub const VERIFIERS_PER_CHAIN: IndexedMap<
163+
(ServiceName, ChainName, VerifierAddress),
164+
(),
165+
VerifierPerChainIndexes,
166+
> = IndexedMap::new(
167+
"verifiers_per_chain",
168+
VerifierPerChainIndexes {
169+
verifier_address: MultiIndex::new(
170+
|pk: &[u8], _: &()| {
171+
let (service_name, _, verifier) =
172+
<(ServiceName, ChainName, VerifierAddress)>::from_slice(pk)
173+
.expect("invalid primary key");
174+
(service_name, verifier)
175+
},
176+
"verifiers_per_chain",
177+
"verifiers_per_chain__address",
178+
),
179+
},
180+
);
181+
182+
type ServiceName = String;
147183
type VerifierAddress = Addr;
148184

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

154188
pub fn register_chains_support(
@@ -158,9 +192,12 @@ pub fn register_chains_support(
158192
verifier: VerifierAddress,
159193
) -> Result<(), ContractError> {
160194
for chain in chains.iter() {
161-
VERIFIERS_PER_CHAIN.save(storage, (&service_name, chain, &verifier), &())?;
195+
VERIFIERS_PER_CHAIN.save(
196+
storage,
197+
(service_name.clone(), chain.clone(), verifier.clone()),
198+
&(),
199+
)?;
162200
}
163-
164201
Ok(())
165202
}
166203

@@ -171,9 +208,8 @@ pub fn deregister_chains_support(
171208
verifier: VerifierAddress,
172209
) -> Result<(), ContractError> {
173210
for chain in chains {
174-
VERIFIERS_PER_CHAIN.remove(storage, (&service_name, &chain, &verifier));
211+
VERIFIERS_PER_CHAIN.remove(storage, (service_name.clone(), chain, verifier.clone()))?;
175212
}
176-
177213
Ok(())
178214
}
179215

0 commit comments

Comments
 (0)