feat(cosmos): ism dry-run (#3077)

### Description

Implements `dry_run_verify` for the aggregation ISM on cosmwasm. One
remaining issue is that the estimated gas is hardcoded to `1`, because
we're actually [just
querying](37fea49429/contracts/isms/aggregate/src/lib.rs (L108))
via rpc rather than simulating a tx. The `verify` tx isn't marked as a
contract
[entrypoint](https://book.cosmwasm.com/basics/entry-points.html) so it
can't be called from outside iiuc. (here's the
[verify](37fea49429/contracts/isms/aggregate/src/lib.rs (L124))
fn for reference).

Worth mentioning that the query interface for the aggregation ISM is
named incorrectly - it should return fields called `threshold` and
`modules`, but instead copies the response from the multisig ISM and
returns `threshold` and `validators`. This can be particularly
misleading because validators have 20-bytes long addresses, whereas
modules (contracts) have 32-bytes long addresses.

### Related issues

- Fixes https://github.com/hyperlane-xyz/issues/issues/807

### Backward compatibility

yes

### Testing

E2E. The ISM setup is `routing` -> `aggregation (1/1)` -> `multisig
(1/1)`
pull/3074/head
Daniel Savu 11 months ago committed by GitHub
parent f73ee0b273
commit c2cf7be8f2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 32
      rust/chains/hyperlane-cosmos/src/aggregation_ism.rs
  2. 23
      rust/chains/hyperlane-cosmos/src/interchain_security_module.rs
  3. 25
      rust/chains/hyperlane-cosmos/src/payloads/aggregate_ism.rs
  4. 15
      rust/utils/run-locally/src/cosmos/deploy.rs
  5. 2
      rust/utils/run-locally/src/cosmos/link.rs
  6. 2
      rust/utils/run-locally/src/cosmos/types.rs

@ -1,15 +1,17 @@
use std::str::FromStr;
use crate::{
address::CosmosAddress,
grpc::WasmProvider,
payloads::aggregate_ism::{ModulesAndThresholdRequest, ModulesAndThresholdResponse},
payloads::{
ism_routes::QueryIsmGeneralRequest,
multisig_ism::{VerifyInfoRequest, VerifyInfoRequestInner, VerifyInfoResponse},
},
ConnectionConf, CosmosProvider, Signer,
};
use async_trait::async_trait;
use hyperlane_core::{
AggregationIsm, ChainResult, ContractLocator, HyperlaneChain, HyperlaneContract,
HyperlaneDomain, HyperlaneMessage, HyperlaneProvider, H256,
HyperlaneDomain, HyperlaneMessage, HyperlaneProvider, RawHyperlaneMessage, H256,
};
use tracing::instrument;
@ -66,15 +68,27 @@ impl AggregationIsm for CosmosAggregationIsm {
&self,
message: &HyperlaneMessage,
) -> ChainResult<(Vec<H256>, u8)> {
let payload = ModulesAndThresholdRequest::new(message);
let payload = VerifyInfoRequest {
verify_info: VerifyInfoRequestInner {
message: hex::encode(RawHyperlaneMessage::from(message)),
},
};
let data = self.provider.grpc().wasm_query(payload, None).await?;
let response: ModulesAndThresholdResponse = serde_json::from_slice(&data)?;
let data = self
.provider
.grpc()
.wasm_query(QueryIsmGeneralRequest { ism: payload }, None)
.await?;
let response: VerifyInfoResponse = serde_json::from_slice(&data)?;
// Note that due to a misnomer in the CosmWasm implementation, the `modules` field is called `validators`.
let modules: ChainResult<Vec<H256>> = response
.modules
.into_iter()
.map(|module| CosmosAddress::from_str(&module).map(|ca| ca.digest()))
.validators
.iter()
// The returned values are Bech32-decoded Cosmos addresses.
// Since they are not EOAs but rather contracts, they are 32 bytes long and
// need to be parsed directly as an `H256`.
.map(|module| H256::from_str(module).map_err(Into::into))
.collect();
Ok((modules?, response.threshold))

@ -1,12 +1,14 @@
use async_trait::async_trait;
use hyperlane_core::{
ChainResult, ContractLocator, HyperlaneChain, HyperlaneContract, HyperlaneDomain,
HyperlaneMessage, HyperlaneProvider, InterchainSecurityModule, ModuleType, H256, U256,
HyperlaneMessage, HyperlaneProvider, InterchainSecurityModule, ModuleType, RawHyperlaneMessage,
H256, U256,
};
use crate::{
grpc::WasmProvider,
payloads::{
aggregate_ism::{VerifyRequest, VerifyRequestInner, VerifyResponse},
general::EmptyStruct,
ism_routes::{QueryIsmGeneralRequest, QueryIsmModuleTypeRequest},
},
@ -91,6 +93,23 @@ impl InterchainSecurityModule for CosmosInterchainSecurityModule {
message: &HyperlaneMessage,
metadata: &[u8],
) -> ChainResult<Option<U256>> {
Ok(Some(U256::from(1000))) // TODO
let payload = VerifyRequest {
verify: VerifyRequestInner {
metadata: hex::encode(metadata),
message: hex::encode(RawHyperlaneMessage::from(message)),
},
};
let data = self
.provider
.grpc()
.wasm_query(QueryIsmGeneralRequest { ism: payload }, None)
.await?;
let response: VerifyResponse = serde_json::from_slice(&data)?;
// We can't simulate the `verify` call in CosmWasm because
// it's not marked as an entrypoint. So we just use the query interface
// and hardcode a gas value - this can be inefficient if one ISM is
// vastly cheaper than another one.
let dummy_gas_value = U256::one();
Ok(response.verified.then_some(dummy_gas_value))
}
}

@ -1,30 +1,17 @@
use hyperlane_core::{HyperlaneMessage, RawHyperlaneMessage};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug)]
pub struct ModulesAndThresholdRequest {
modules_and_threshold: ModulesAndThresholdRequestInner,
}
impl ModulesAndThresholdRequest {
pub fn new(message: &HyperlaneMessage) -> Self {
Self {
modules_and_threshold: ModulesAndThresholdRequestInner {
message: hex::encode(RawHyperlaneMessage::from(message)),
},
}
}
pub struct VerifyRequest {
pub verify: VerifyRequestInner,
}
#[derive(Serialize, Deserialize, Debug)]
struct ModulesAndThresholdRequestInner {
/// Hex-encoded Hyperlane message
pub struct VerifyRequestInner {
pub metadata: String,
pub message: String,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct ModulesAndThresholdResponse {
pub threshold: u8,
/// Bech32-encoded module addresses
pub modules: Vec<String>,
pub struct VerifyResponse {
pub verified: bool,
}

@ -113,6 +113,20 @@ pub fn deploy_cw_hyperlane(
"hpl_ism_multisig",
);
// deploy ism - aggregation
let ism_aggregate = cli.wasm_init(
&endpoint,
&deployer,
Some(deployer_addr),
codes.hpl_ism_aggregate,
ism::aggregate::InstantiateMsg {
owner: deployer_addr.clone(),
threshold: 1,
isms: vec![ism_multisig.clone()],
},
"hpl_ism_aggregate",
);
// deploy merkle hook
let hook_merkle = cli.wasm_init(
&endpoint,
@ -188,6 +202,7 @@ pub fn deploy_cw_hyperlane(
hook_routing,
igp,
igp_oracle,
ism_aggregate,
ism_routing,
ism_multisig,
mailbox,

@ -162,7 +162,7 @@ fn link_network(
ism::routing::ExecuteMsg::Set {
ism: ism::routing::IsmSet {
domain: target_domain,
address: network.deployments.ism_multisig.clone(),
address: network.deployments.ism_aggregate.clone(),
},
},
vec![],

@ -42,6 +42,7 @@ pub struct Codes {
pub hpl_hook_routing: u64,
pub hpl_igp: u64,
pub hpl_igp_oracle: u64,
pub hpl_ism_aggregate: u64,
pub hpl_ism_multisig: u64,
pub hpl_ism_routing: u64,
pub hpl_test_mock_ism: u64,
@ -57,6 +58,7 @@ pub struct Deployments {
pub hook_routing: String,
pub igp: String,
pub igp_oracle: String,
pub ism_aggregate: String,
pub ism_routing: String,
pub ism_multisig: String,
pub mailbox: String,

Loading…
Cancel
Save