Skip to content

Commit

Permalink
feat(minor-coordinator)!: verifier information query (axelarnetwork#637)
Browse files Browse the repository at this point in the history
  • Loading branch information
maancham authored Sep 27, 2024
1 parent 2ef5243 commit 1775139
Show file tree
Hide file tree
Showing 27 changed files with 378 additions and 41 deletions.
3 changes: 3 additions & 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/coordinator/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ msgs-derive = { workspace = true }
multisig = { workspace = true, features = ["library"] }
report = { workspace = true }
router-api = { workspace = true }
service-registry-api = { workspace = true }
thiserror = { workspace = true }

[dev-dependencies]
Expand Down
19 changes: 19 additions & 0 deletions contracts/coordinator/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,27 @@ pub enum Error {
"failed to execute ReadyToUnbond query at coordinator contract. verifier_address: {0}"
)]
ReadyToUnbond(String),

#[error(
"failed to execute VerifierDetailsWithProvers query at coordinator contract. service_name: {service_name}, verifier_address: {verifier_address}"
)]
VerifierDetailsWithProvers {
service_name: String,
verifier_address: String,
},
}

impl From<QueryMsg> for Error {
fn from(value: QueryMsg) -> Self {
match value {
QueryMsg::ReadyToUnbond { verifier_address } => Error::ReadyToUnbond(verifier_address),
QueryMsg::VerifierInfo {
service_name,
verifier,
} => Error::VerifierDetailsWithProvers {
service_name,
verifier_address: verifier,
},
}
}
}
Expand Down Expand Up @@ -114,6 +129,10 @@ mod test {
QueryMsg::ReadyToUnbond {
verifier_address: _,
} => Ok(to_json_binary(&true).into()).into(),
QueryMsg::VerifierInfo {
service_name: _,
verifier: _,
} => Ok(to_json_binary(&true).into()).into(),
}
}
_ => panic!("unexpected query: {:?}", msg),
Expand Down
32 changes: 27 additions & 5 deletions contracts/coordinator/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,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, Binary, Deps, DepsMut, Empty, Env, MessageInfo, Response, Storage,
to_json_binary, Addr, Binary, Deps, DepsMut, Env, MessageInfo, Response, Storage,
};
use error_stack::report;
use error_stack::{report, ResultExt};
use itertools::Itertools;

use crate::error::ContractError;
use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg};
use crate::state::is_prover_registered;
use crate::msg::{ExecuteMsg, InstantiateMsg, MigrationMsg, QueryMsg};
use crate::state::{is_prover_registered, Config, CONFIG};

pub const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME");
pub const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");
Expand All @@ -23,8 +23,13 @@ pub const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");
pub fn migrate(
deps: DepsMut,
_env: Env,
_msg: Empty,
msg: MigrationMsg,
) -> Result<Response, axelar_wasm_std::error::ContractError> {
let service_registry = validate_cosmwasm_address(deps.api, &msg.service_registry)?;

migrations::v1_0_0::migrate(deps.storage, service_registry)
.change_context(ContractError::Migration)?;

// this needs to be the last thing to do during migration,
// because previous migration steps should check the old version
cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
Expand All @@ -41,6 +46,11 @@ pub fn instantiate(
) -> Result<Response, axelar_wasm_std::error::ContractError> {
cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;

let config = Config {
service_registry: address::validate_cosmwasm_address(deps.api, &msg.service_registry)?,
};
CONFIG.save(deps.storage, &config)?;

let governance = address::validate_cosmwasm_address(deps.api, &msg.governance_address)?;
permission_control::set_governance(deps.storage, &governance)?;

Expand Down Expand Up @@ -105,6 +115,17 @@ pub fn query(
worker_address,
)?)?
}
QueryMsg::VerifierInfo {
service_name,
verifier,
} => {
let verifier_address = validate_cosmwasm_address(deps.api, &verifier)?;
to_json_binary(&query::verifier_details_with_provers(
deps,
service_name,
verifier_address,
)?)?
}
}
.then(Ok)
}
Expand Down Expand Up @@ -138,6 +159,7 @@ mod tests {

let instantiate_msg = InstantiateMsg {
governance_address: governance.to_string(),
service_registry: Addr::unchecked("random_service").to_string(),
};

let res = instantiate(deps.as_mut(), env.clone(), info.clone(), instantiate_msg);
Expand Down
2 changes: 1 addition & 1 deletion contracts/coordinator/src/contract/migrations/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1 @@

pub mod v1_0_0;
115 changes: 115 additions & 0 deletions contracts/coordinator/src/contract/migrations/v1_0_0.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
#![allow(deprecated)]

use axelar_wasm_std::nonempty;
use cosmwasm_std::{Addr, StdError, Storage};
use cw2::VersionError;

use crate::contract::CONTRACT_NAME;

const BASE_VERSION: &str = "1.0.0";

#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error(transparent)]
Std(#[from] StdError),
#[error(transparent)]
Version(#[from] VersionError),
#[error(transparent)]
NonEmpty(#[from] nonempty::Error),
}

pub fn migrate(storage: &mut dyn Storage, service_registry: Addr) -> Result<(), Error> {
cw2::assert_contract_version(storage, CONTRACT_NAME, BASE_VERSION)?;

migrate_config(storage, service_registry)?;
Ok(())
}

fn migrate_config(storage: &mut dyn Storage, service_registry: Addr) -> Result<(), Error> {
let new_config = crate::state::Config { service_registry };
crate::state::CONFIG.save(storage, &new_config)?;
Ok(())
}

#[cfg(test)]
mod tests {
use axelar_wasm_std::{address, permission_control};
use cosmwasm_schema::cw_serde;
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};
use cosmwasm_std::{Addr, DepsMut, Env, MessageInfo, Response};

use crate::contract::migrations::v1_0_0::{self, BASE_VERSION};
use crate::contract::CONTRACT_NAME;

const GOVERNANCE: &str = "governance";

#[test]
fn migrate_checks_contract_version() {
let mut deps = mock_dependencies();
instantiate_contract(deps.as_mut());

cw2::set_contract_version(deps.as_mut().storage, CONTRACT_NAME, "something wrong").unwrap();

assert!(v1_0_0::migrate(deps.as_mut().storage, Addr::unchecked("random_service")).is_err());

cw2::set_contract_version(deps.as_mut().storage, CONTRACT_NAME, BASE_VERSION).unwrap();

assert!(v1_0_0::migrate(deps.as_mut().storage, Addr::unchecked("random_service")).is_ok());
}

#[test]
fn migrate_config() {
let mut deps = mock_dependencies();
instantiate(
deps.as_mut(),
mock_env(),
mock_info("admin", &[]),
InstantiateMsg {
governance_address: "governance".to_string(),
},
)
.unwrap();

let service_registry_address = Addr::unchecked("random_service");
assert!(v1_0_0::migrate(deps.as_mut().storage, service_registry_address.clone()).is_ok());

let config_result = crate::state::CONFIG.load(deps.as_mut().storage);
assert!(config_result.is_ok());

let config = config_result.unwrap();
assert_eq!(config.service_registry, service_registry_address)
}

fn instantiate_contract(deps: DepsMut) {
instantiate(
deps,
mock_env(),
mock_info("admin", &[]),
InstantiateMsg {
governance_address: GOVERNANCE.to_string(),
},
)
.unwrap();
}

#[deprecated(since = "1.0.0", note = "only used to test the migration")]
fn instantiate(
deps: DepsMut,
_env: Env,
_info: MessageInfo,
msg: InstantiateMsg,
) -> Result<Response, axelar_wasm_std::error::ContractError> {
cw2::set_contract_version(deps.storage, CONTRACT_NAME, BASE_VERSION)?;

let governance = address::validate_cosmwasm_address(deps.api, &msg.governance_address)?;
permission_control::set_governance(deps.storage, &governance)?;

Ok(Response::default())
}

#[cw_serde]
#[deprecated(since = "1.0.0", note = "only used to test the migration")]
struct InstantiateMsg {
pub governance_address: String,
}
}
52 changes: 49 additions & 3 deletions contracts/coordinator/src/contract/query.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,41 @@
use std::collections::HashSet;

use cosmwasm_std::{Addr, Deps, Order, StdResult};
use error_stack::{Result, ResultExt};
use itertools::Itertools;
use service_registry_api::msg::VerifierDetails;

use crate::error::ContractError;
use crate::msg::VerifierInfo;
use crate::state::{load_config, VERIFIER_PROVER_INDEXED_MAP};

pub fn check_verifier_ready_to_unbond(deps: Deps, verifier_address: Addr) -> StdResult<bool> {
Ok(!is_verifier_in_any_verifier_set(deps, &verifier_address))
}

pub fn verifier_details_with_provers(
deps: Deps,
service_name: String,
verifier_address: Addr,
) -> Result<VerifierInfo, ContractError> {
let config = load_config(deps.storage);

let service_registry: service_registry_api::Client =
client::ContractClient::new(deps.querier, &config.service_registry).into();

let verifier_details: VerifierDetails = service_registry
.verifier(service_name, verifier_address.to_string())
.change_context(ContractError::FailedToGetVerifierDetails)?;

use crate::state::VERIFIER_PROVER_INDEXED_MAP;
let active_prover_set = get_provers_for_verifier(deps, verifier_address)?;

Ok(VerifierInfo {
verifier: verifier_details.verifier,
weight: verifier_details.weight,
supported_chains: verifier_details.supported_chains,
actively_signing_for: active_prover_set,
})
}

fn is_verifier_in_any_verifier_set(deps: Deps, verifier_address: &Addr) -> bool {
VERIFIER_PROVER_INDEXED_MAP
Expand All @@ -11,6 +46,17 @@ fn is_verifier_in_any_verifier_set(deps: Deps, verifier_address: &Addr) -> bool
.any(|_| true)
}

pub fn check_verifier_ready_to_unbond(deps: Deps, verifier_address: Addr) -> StdResult<bool> {
Ok(!is_verifier_in_any_verifier_set(deps, &verifier_address))
fn get_provers_for_verifier(
deps: Deps,
verifier_address: Addr,
) -> Result<HashSet<Addr>, ContractError> {
let provers = VERIFIER_PROVER_INDEXED_MAP
.idx
.by_verifier
.prefix(verifier_address)
.range(deps.storage, None, None, Order::Ascending)
.map(|result| result.map(|(_, record)| record.prover))
.try_collect();

provers.change_context(ContractError::FailedToGetProversForVerifier)
}
9 changes: 9 additions & 0 deletions contracts/coordinator/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,13 @@ pub enum ContractError {

#[error("prover is not registered")]
ProverNotRegistered,

#[error("failed to obtain verifier details")]
FailedToGetVerifierDetails,

#[error("failed to get provers of verifier")]
FailedToGetProversForVerifier,

#[error("failed to migrate contract state")]
Migration,
}
23 changes: 23 additions & 0 deletions contracts/coordinator/src/msg.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
use std::collections::HashSet;

use axelar_wasm_std::nonempty;
use cosmwasm_schema::{cw_serde, QueryResponses};
use cosmwasm_std::Addr;
use msgs_derive::EnsurePermissions;
use router_api::ChainName;
use service_registry_api::Verifier;

#[cw_serde]
pub struct MigrationMsg {
pub service_registry: String,
}

#[cw_serde]
pub struct InstantiateMsg {
pub governance_address: String,
pub service_registry: String,
}

#[cw_serde]
Expand All @@ -26,4 +35,18 @@ pub enum ExecuteMsg {
pub enum QueryMsg {
#[returns(bool)]
ReadyToUnbond { verifier_address: String },

#[returns(VerifierInfo)]
VerifierInfo {
service_name: String,
verifier: String,
},
}

#[cw_serde]
pub struct VerifierInfo {
pub verifier: Verifier,
pub weight: nonempty::Uint128,
pub supported_chains: Vec<ChainName>,
pub actively_signing_for: HashSet<Addr>,
}
Loading

0 comments on commit 1775139

Please sign in to comment.