Skip to content

Commit

Permalink
feat(cosmos): ism dry-run (#3077)
Browse files Browse the repository at this point in the history
### 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](https://github.com/many-things/cw-hyperlane/blob/37fea49429108d0cad46c64c3c1ebc467817ff8c/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](https://github.com/many-things/cw-hyperlane/blob/37fea49429108d0cad46c64c3c1ebc467817ff8c/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 hyperlane-xyz/issues#807

### Backward compatibility

yes

### Testing

E2E. The ISM setup is `routing` -> `aggregation (1/1)` -> `multisig
(1/1)`
  • Loading branch information
daniel-savu authored Dec 20, 2023
1 parent f73ee0b commit c2cf7be
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 31 deletions.
32 changes: 23 additions & 9 deletions rust/chains/hyperlane-cosmos/src/aggregation_ism.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -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))
Expand Down
23 changes: 21 additions & 2 deletions rust/chains/hyperlane-cosmos/src/interchain_security_module.rs
Original file line number Diff line number Diff line change
@@ -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},
},
Expand Down Expand Up @@ -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))
}
}
25 changes: 6 additions & 19 deletions rust/chains/hyperlane-cosmos/src/payloads/aggregate_ism.rs
Original file line number Diff line number Diff line change
@@ -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,
}
15 changes: 15 additions & 0 deletions rust/utils/run-locally/src/cosmos/deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -188,6 +202,7 @@ pub fn deploy_cw_hyperlane(
hook_routing,
igp,
igp_oracle,
ism_aggregate,
ism_routing,
ism_multisig,
mailbox,
Expand Down
2 changes: 1 addition & 1 deletion rust/utils/run-locally/src/cosmos/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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![],
Expand Down
2 changes: 2 additions & 0 deletions rust/utils/run-locally/src/cosmos/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand Down

0 comments on commit c2cf7be

Please sign in to comment.