Relayer aggregation ism (#2411)
### Description Adds AggregationISM support to the relayer, by adding the scaffolding, sub-ism metadata fetching, and custom metadata encoding. ### Drive-by changes - adds `dry_run_verify(...)` to the `InterchainSecurityModule` trait and implements it for `EthereumInterchainSecurityModule` - changes the default ISM in the test environment from `routingIsm(multisigIsm)` to `routingIsm(aggregationIsm([multisigIsm, messageIdIsm], threshold = 1))` ### Related issues - Fixes https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/1757 ### Backward compatibility _Are these changes backward compatible?_ Yes _Are there any infrastructure implications, e.g. changes that would prohibit deploying older commits using this infra tooling?_ None ### Testing The AggregationISM metadata encoding is tested using unit tests whose input was taken from the tests in `AggregationIsm.t.sol`, by printing the individual ISM metadatas and the final metadata. The overall AggregationISM is tested with the `run-locally` script.dan/testnet-aggregation-ism
parent
16a29edb00
commit
7889385501
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,262 @@ |
|||||||
|
use async_trait::async_trait; |
||||||
|
use derive_more::Deref; |
||||||
|
use futures_util::future::join_all; |
||||||
|
|
||||||
|
use derive_new::new; |
||||||
|
use eyre::Context; |
||||||
|
use tracing::{info, instrument}; |
||||||
|
|
||||||
|
use hyperlane_core::{HyperlaneMessage, InterchainSecurityModule, H256, U256}; |
||||||
|
|
||||||
|
use super::{BaseMetadataBuilder, MetadataBuilder}; |
||||||
|
|
||||||
|
/// Bytes used to store one member of the (start, end) range tuple
|
||||||
|
/// Copied from `AggregationIsmMetadata.sol`
|
||||||
|
const METADATA_RANGE_SIZE: usize = 4; |
||||||
|
|
||||||
|
#[derive(Clone, Debug, new, Deref)] |
||||||
|
pub struct AggregationIsmMetadataBuilder { |
||||||
|
base: BaseMetadataBuilder, |
||||||
|
} |
||||||
|
|
||||||
|
#[derive(Clone, Debug, new, PartialEq, Eq)] |
||||||
|
struct SubModuleMetadata { |
||||||
|
/// The index of the sub-module (ISM) in the aggregation ISM.
|
||||||
|
index: usize, |
||||||
|
/// The metadata for the sub-module.
|
||||||
|
metadata: Vec<u8>, |
||||||
|
} |
||||||
|
|
||||||
|
#[derive(Debug)] |
||||||
|
struct IsmAndMetadata { |
||||||
|
ism: Box<dyn InterchainSecurityModule>, |
||||||
|
meta: SubModuleMetadata, |
||||||
|
} |
||||||
|
|
||||||
|
impl IsmAndMetadata { |
||||||
|
fn new(ism: Box<dyn InterchainSecurityModule>, index: usize, metadata: Vec<u8>) -> Self { |
||||||
|
Self { |
||||||
|
ism, |
||||||
|
meta: SubModuleMetadata::new(index, metadata), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl AggregationIsmMetadataBuilder { |
||||||
|
fn format_metadata(metadatas: &mut [SubModuleMetadata], ism_count: usize) -> Vec<u8> { |
||||||
|
// See test solidity implementation of this fn at:
|
||||||
|
// https://github.com/hyperlane-xyz/hyperlane-monorepo/blob/445da4fb0d8140a08c4b314e3051b7a934b0f968/solidity/test/isms/AggregationIsm.t.sol#L35
|
||||||
|
fn encode_byte_index(i: usize) -> [u8; 4] { |
||||||
|
(i as u32).to_be_bytes() |
||||||
|
} |
||||||
|
let range_tuples_size = METADATA_RANGE_SIZE * 2 * ism_count; |
||||||
|
// Format of metadata:
|
||||||
|
// [????:????] Metadata start/end uint32 ranges, packed as uint64
|
||||||
|
// [????:????] ISM metadata, packed encoding
|
||||||
|
// Initialize the range tuple part of the buffer, so the actual metadatas can
|
||||||
|
// simply be appended to it
|
||||||
|
let mut buffer = vec![0; range_tuples_size]; |
||||||
|
for SubModuleMetadata { index, metadata } in metadatas.iter_mut() { |
||||||
|
let range_start = buffer.len(); |
||||||
|
buffer.append(metadata); |
||||||
|
let range_end = buffer.len(); |
||||||
|
|
||||||
|
// The new tuple starts at the end of the previous ones.
|
||||||
|
// Also see: https://github.com/hyperlane-xyz/hyperlane-monorepo/blob/445da4fb0d8140a08c4b314e3051b7a934b0f968/solidity/contracts/libs/isms/AggregationIsmMetadata.sol#L49
|
||||||
|
let encoded_range_start = METADATA_RANGE_SIZE * 2 * (*index); |
||||||
|
// Overwrite the 0-initialized buffer
|
||||||
|
buffer.splice( |
||||||
|
encoded_range_start..(encoded_range_start + METADATA_RANGE_SIZE * 2), |
||||||
|
[encode_byte_index(range_start), encode_byte_index(range_end)].concat(), |
||||||
|
); |
||||||
|
} |
||||||
|
buffer |
||||||
|
} |
||||||
|
|
||||||
|
fn n_cheapest_metas( |
||||||
|
mut metas_and_gas: Vec<(SubModuleMetadata, U256)>, |
||||||
|
n: usize, |
||||||
|
) -> Vec<SubModuleMetadata> { |
||||||
|
// Sort by gas cost in ascending order
|
||||||
|
metas_and_gas.sort_by(|(_, gas_1), (_, gas_2)| gas_1.cmp(gas_2)); |
||||||
|
// Take the cheapest n (the aggregation ISM threshold)
|
||||||
|
let mut cheapest: Vec<_> = metas_and_gas[..n].into(); |
||||||
|
// Sort by index in ascending order, to match the order expected by the smart contract
|
||||||
|
cheapest.sort_by(|(meta_1, _), (meta_2, _)| meta_1.index.cmp(&meta_2.index)); |
||||||
|
cheapest.into_iter().map(|(meta, _)| meta).collect() |
||||||
|
} |
||||||
|
|
||||||
|
async fn cheapest_valid_metas( |
||||||
|
sub_modules: Vec<IsmAndMetadata>, |
||||||
|
message: &HyperlaneMessage, |
||||||
|
threshold: usize, |
||||||
|
) -> Option<Vec<SubModuleMetadata>> { |
||||||
|
let gas_cost_results: Vec<_> = join_all( |
||||||
|
sub_modules |
||||||
|
.iter() |
||||||
|
.map(|module| module.ism.dry_run_verify(message, &(module.meta.metadata))), |
||||||
|
) |
||||||
|
.await; |
||||||
|
|
||||||
|
// Filter out the ISMs without a gas cost estimate
|
||||||
|
let metas_and_gas: Vec<_> = sub_modules |
||||||
|
.into_iter() |
||||||
|
.zip(gas_cost_results.into_iter()) |
||||||
|
.filter_map(|(module, gas_cost)| gas_cost.ok().flatten().map(|gc| (module.meta, gc))) |
||||||
|
.collect(); |
||||||
|
|
||||||
|
let metas_and_gas_count = metas_and_gas.len(); |
||||||
|
if metas_and_gas_count < threshold { |
||||||
|
info!("Could not fetch all metadata: Found {metas_and_gas_count} of the {threshold} required ISM metadata pieces"); |
||||||
|
return None; |
||||||
|
} |
||||||
|
Some(Self::n_cheapest_metas(metas_and_gas, threshold)) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[async_trait] |
||||||
|
impl MetadataBuilder for AggregationIsmMetadataBuilder { |
||||||
|
#[instrument(err, skip(self))] |
||||||
|
async fn build( |
||||||
|
&self, |
||||||
|
ism_address: H256, |
||||||
|
message: &HyperlaneMessage, |
||||||
|
) -> eyre::Result<Option<Vec<u8>>> { |
||||||
|
const CTX: &str = "When fetching AggregationIsm metadata"; |
||||||
|
let ism = self.build_aggregation_ism(ism_address).await.context(CTX)?; |
||||||
|
let (ism_addresses, threshold) = ism.modules_and_threshold(message).await.context(CTX)?; |
||||||
|
let threshold = threshold as usize; |
||||||
|
|
||||||
|
let metas = join_all( |
||||||
|
ism_addresses |
||||||
|
.iter() |
||||||
|
.map(|ism_address| self.base.build(*ism_address, message)), |
||||||
|
) |
||||||
|
.await; |
||||||
|
|
||||||
|
let sub_modules = join_all( |
||||||
|
ism_addresses |
||||||
|
.iter() |
||||||
|
.map(|ism_address| self.base.build_ism(*ism_address)), |
||||||
|
) |
||||||
|
.await; |
||||||
|
|
||||||
|
let filtered_sub_module_metas = metas |
||||||
|
.into_iter() |
||||||
|
.enumerate() |
||||||
|
.zip(sub_modules.into_iter()) |
||||||
|
.filter_map(|((index, meta_result), sub_module_result)| { |
||||||
|
match (meta_result, sub_module_result) { |
||||||
|
(Ok(Some(meta)), Ok(ism)) => Some(IsmAndMetadata::new(ism, index, meta)), |
||||||
|
_ => None, |
||||||
|
} |
||||||
|
}) |
||||||
|
.collect(); |
||||||
|
|
||||||
|
let maybe_aggregation_metadata = |
||||||
|
Self::cheapest_valid_metas(filtered_sub_module_metas, message, threshold) |
||||||
|
.await |
||||||
|
.map(|mut metas| Self::format_metadata(&mut metas, ism_addresses.len())); |
||||||
|
Ok(maybe_aggregation_metadata) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[cfg(test)] |
||||||
|
mod test { |
||||||
|
use ethers::utils::hex::FromHex; |
||||||
|
|
||||||
|
use super::*; |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn test_format_n_of_n_metadata_works_correctly() { |
||||||
|
let mut metadatas = vec![ |
||||||
|
SubModuleMetadata::new( |
||||||
|
0, |
||||||
|
Vec::from_hex("290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563") |
||||||
|
.unwrap(), |
||||||
|
), |
||||||
|
SubModuleMetadata::new( |
||||||
|
1, |
||||||
|
Vec::from_hex("510e4e770828ddbf7f7b00ab00a9f6adaf81c0dc9cc85f1f8249c256942d61d9") |
||||||
|
.unwrap(), |
||||||
|
), |
||||||
|
SubModuleMetadata::new( |
||||||
|
2, |
||||||
|
Vec::from_hex("356e5a2cc1eba076e650ac7473fccc37952b46bc2e419a200cec0c451dce2336") |
||||||
|
.unwrap(), |
||||||
|
), |
||||||
|
]; |
||||||
|
let expected = Vec::from_hex("000000180000003800000038000000580000005800000078290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563510e4e770828ddbf7f7b00ab00a9f6adaf81c0dc9cc85f1f8249c256942d61d9356e5a2cc1eba076e650ac7473fccc37952b46bc2e419a200cec0c451dce2336").unwrap(); |
||||||
|
assert_eq!( |
||||||
|
AggregationIsmMetadataBuilder::format_metadata(&mut metadatas, 3), |
||||||
|
expected |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn test_format_n_of_m_metadata_works_correctly() { |
||||||
|
// We're passing the metadatas of 4 ISMs (indexes 0, 1, 2, 4) out of 5
|
||||||
|
let mut metadatas = vec![ |
||||||
|
SubModuleMetadata::new( |
||||||
|
0, |
||||||
|
Vec::from_hex("290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563") |
||||||
|
.unwrap(), |
||||||
|
), |
||||||
|
SubModuleMetadata::new( |
||||||
|
1, |
||||||
|
Vec::from_hex("510e4e770828ddbf7f7b00ab00a9f6adaf81c0dc9cc85f1f8249c256942d61d9") |
||||||
|
.unwrap(), |
||||||
|
), |
||||||
|
SubModuleMetadata::new( |
||||||
|
2, |
||||||
|
Vec::from_hex("356e5a2cc1eba076e650ac7473fccc37952b46bc2e419a200cec0c451dce2336") |
||||||
|
.unwrap(), |
||||||
|
), |
||||||
|
SubModuleMetadata::new( |
||||||
|
4, |
||||||
|
Vec::from_hex("f2e59013a0a379837166b59f871b20a8a0d101d1c355ea85d35329360e69c000") |
||||||
|
.unwrap(), |
||||||
|
), |
||||||
|
]; |
||||||
|
let expected = Vec::from_hex("000000280000004800000048000000680000006800000088000000000000000000000088000000a8290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563510e4e770828ddbf7f7b00ab00a9f6adaf81c0dc9cc85f1f8249c256942d61d9356e5a2cc1eba076e650ac7473fccc37952b46bc2e419a200cec0c451dce2336f2e59013a0a379837166b59f871b20a8a0d101d1c355ea85d35329360e69c000").unwrap(); |
||||||
|
assert_eq!( |
||||||
|
AggregationIsmMetadataBuilder::format_metadata(&mut metadatas, 5), |
||||||
|
expected |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn test_format_empty_metadata_works_correctly() { |
||||||
|
let mut metadatas = vec![SubModuleMetadata::new(0, Vec::from_hex("").unwrap())]; |
||||||
|
let expected = Vec::from_hex("0000000800000008").unwrap(); |
||||||
|
assert_eq!( |
||||||
|
AggregationIsmMetadataBuilder::format_metadata(&mut metadatas, 1), |
||||||
|
expected |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn test_n_cheapest_metas_works() { |
||||||
|
let metas_and_gas = vec![ |
||||||
|
( |
||||||
|
SubModuleMetadata::new(3, vec![]), |
||||||
|
U256::from_dec_str("3").unwrap(), |
||||||
|
), |
||||||
|
( |
||||||
|
SubModuleMetadata::new(2, vec![]), |
||||||
|
U256::from_dec_str("2").unwrap(), |
||||||
|
), |
||||||
|
( |
||||||
|
SubModuleMetadata::new(1, vec![]), |
||||||
|
U256::from_dec_str("1").unwrap(), |
||||||
|
), |
||||||
|
]; |
||||||
|
assert_eq!( |
||||||
|
AggregationIsmMetadataBuilder::n_cheapest_metas(metas_and_gas, 2), |
||||||
|
vec![ |
||||||
|
SubModuleMetadata::new(1, vec![]), |
||||||
|
SubModuleMetadata::new(2, vec![]) |
||||||
|
] |
||||||
|
) |
||||||
|
} |
||||||
|
} |
@ -1,7 +1,9 @@ |
|||||||
|
mod aggregation; |
||||||
mod base; |
mod base; |
||||||
mod multisig; |
mod multisig; |
||||||
mod routing; |
mod routing; |
||||||
|
|
||||||
|
use aggregation::AggregationIsmMetadataBuilder; |
||||||
pub(crate) use base::BaseMetadataBuilder; |
pub(crate) use base::BaseMetadataBuilder; |
||||||
pub(crate) use base::MetadataBuilder; |
pub(crate) use base::MetadataBuilder; |
||||||
use routing::RoutingIsmMetadataBuilder; |
use routing::RoutingIsmMetadataBuilder; |
||||||
|
@ -0,0 +1,63 @@ |
|||||||
|
[ |
||||||
|
{ |
||||||
|
"inputs": [], |
||||||
|
"name": "moduleType", |
||||||
|
"outputs": [ |
||||||
|
{ |
||||||
|
"internalType": "uint8", |
||||||
|
"name": "", |
||||||
|
"type": "uint8" |
||||||
|
} |
||||||
|
], |
||||||
|
"stateMutability": "view", |
||||||
|
"type": "function" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"inputs": [ |
||||||
|
{ |
||||||
|
"internalType": "bytes", |
||||||
|
"name": "_message", |
||||||
|
"type": "bytes" |
||||||
|
} |
||||||
|
], |
||||||
|
"name": "modulesAndThreshold", |
||||||
|
"outputs": [ |
||||||
|
{ |
||||||
|
"internalType": "address[]", |
||||||
|
"name": "modules", |
||||||
|
"type": "address[]" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"internalType": "uint8", |
||||||
|
"name": "threshold", |
||||||
|
"type": "uint8" |
||||||
|
} |
||||||
|
], |
||||||
|
"stateMutability": "view", |
||||||
|
"type": "function" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"inputs": [ |
||||||
|
{ |
||||||
|
"internalType": "bytes", |
||||||
|
"name": "_metadata", |
||||||
|
"type": "bytes" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"internalType": "bytes", |
||||||
|
"name": "_message", |
||||||
|
"type": "bytes" |
||||||
|
} |
||||||
|
], |
||||||
|
"name": "verify", |
||||||
|
"outputs": [ |
||||||
|
{ |
||||||
|
"internalType": "bool", |
||||||
|
"name": "", |
||||||
|
"type": "bool" |
||||||
|
} |
||||||
|
], |
||||||
|
"stateMutability": "nonpayable", |
||||||
|
"type": "function" |
||||||
|
} |
||||||
|
] |
@ -0,0 +1,117 @@ |
|||||||
|
#![allow(clippy::enum_variant_names)] |
||||||
|
#![allow(missing_docs)] |
||||||
|
|
||||||
|
use std::collections::HashMap; |
||||||
|
use std::sync::Arc; |
||||||
|
|
||||||
|
use async_trait::async_trait; |
||||||
|
use ethers::providers::Middleware; |
||||||
|
use tracing::instrument; |
||||||
|
|
||||||
|
use hyperlane_core::{ |
||||||
|
AggregationIsm, ChainResult, ContractLocator, HyperlaneAbi, HyperlaneChain, HyperlaneContract, |
||||||
|
HyperlaneDomain, HyperlaneMessage, HyperlaneProvider, RawHyperlaneMessage, H256, |
||||||
|
}; |
||||||
|
|
||||||
|
use crate::contracts::i_aggregation_ism::{ |
||||||
|
IAggregationIsm as EthereumAggregationIsmInternal, IAGGREGATIONISM_ABI, |
||||||
|
}; |
||||||
|
use crate::trait_builder::BuildableWithProvider; |
||||||
|
use crate::EthereumProvider; |
||||||
|
|
||||||
|
pub struct AggregationIsmBuilder {} |
||||||
|
|
||||||
|
#[async_trait] |
||||||
|
impl BuildableWithProvider for AggregationIsmBuilder { |
||||||
|
type Output = Box<dyn AggregationIsm>; |
||||||
|
|
||||||
|
async fn build_with_provider<M: Middleware + 'static>( |
||||||
|
&self, |
||||||
|
provider: M, |
||||||
|
locator: &ContractLocator, |
||||||
|
) -> Self::Output { |
||||||
|
Box::new(EthereumAggregationIsm::new(Arc::new(provider), locator)) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// A reference to an AggregationIsm contract on some Ethereum chain
|
||||||
|
#[derive(Debug)] |
||||||
|
pub struct EthereumAggregationIsm<M> |
||||||
|
where |
||||||
|
M: Middleware, |
||||||
|
{ |
||||||
|
contract: Arc<EthereumAggregationIsmInternal<M>>, |
||||||
|
domain: HyperlaneDomain, |
||||||
|
} |
||||||
|
|
||||||
|
impl<M> EthereumAggregationIsm<M> |
||||||
|
where |
||||||
|
M: Middleware + 'static, |
||||||
|
{ |
||||||
|
/// Create a reference to a mailbox at a specific Ethereum address on some
|
||||||
|
/// chain
|
||||||
|
pub fn new(provider: Arc<M>, locator: &ContractLocator) -> Self { |
||||||
|
Self { |
||||||
|
contract: Arc::new(EthereumAggregationIsmInternal::new( |
||||||
|
locator.address, |
||||||
|
provider, |
||||||
|
)), |
||||||
|
domain: locator.domain.clone(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl<M> HyperlaneChain for EthereumAggregationIsm<M> |
||||||
|
where |
||||||
|
M: Middleware + 'static, |
||||||
|
{ |
||||||
|
fn domain(&self) -> &HyperlaneDomain { |
||||||
|
&self.domain |
||||||
|
} |
||||||
|
|
||||||
|
fn provider(&self) -> Box<dyn HyperlaneProvider> { |
||||||
|
Box::new(EthereumProvider::new( |
||||||
|
self.contract.client(), |
||||||
|
self.domain.clone(), |
||||||
|
)) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl<M> HyperlaneContract for EthereumAggregationIsm<M> |
||||||
|
where |
||||||
|
M: Middleware + 'static, |
||||||
|
{ |
||||||
|
fn address(&self) -> H256 { |
||||||
|
self.contract.address().into() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[async_trait] |
||||||
|
impl<M> AggregationIsm for EthereumAggregationIsm<M> |
||||||
|
where |
||||||
|
M: Middleware + 'static, |
||||||
|
{ |
||||||
|
#[instrument(err)] |
||||||
|
async fn modules_and_threshold( |
||||||
|
&self, |
||||||
|
message: &HyperlaneMessage, |
||||||
|
) -> ChainResult<(Vec<H256>, u8)> { |
||||||
|
let (isms, threshold) = self |
||||||
|
.contract |
||||||
|
.modules_and_threshold(RawHyperlaneMessage::from(message).to_vec().into()) |
||||||
|
.call() |
||||||
|
.await?; |
||||||
|
let isms_h256 = isms.iter().map(|address| (*address).into()).collect(); |
||||||
|
Ok((isms_h256, threshold)) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pub struct EthereumRoutingIsmAbi; |
||||||
|
|
||||||
|
impl HyperlaneAbi for EthereumRoutingIsmAbi { |
||||||
|
const SELECTOR_SIZE_BYTES: usize = 4; |
||||||
|
|
||||||
|
fn fn_map() -> HashMap<Vec<u8>, &'static str> { |
||||||
|
super::extract_fn_map(&IAGGREGATIONISM_ABI) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,18 @@ |
|||||||
|
use std::fmt::Debug; |
||||||
|
|
||||||
|
use async_trait::async_trait; |
||||||
|
use auto_impl::auto_impl; |
||||||
|
|
||||||
|
use crate::{ChainResult, HyperlaneContract, HyperlaneMessage, H256}; |
||||||
|
|
||||||
|
/// Interface for the AggregationIsm chain contract. Allows abstraction over
|
||||||
|
/// different chains
|
||||||
|
#[async_trait] |
||||||
|
#[auto_impl(&, Box, Arc)] |
||||||
|
pub trait AggregationIsm: HyperlaneContract + Send + Sync + Debug { |
||||||
|
/// Returns the `m` ISMs and `n` threshold needed to n-of-m verify the message
|
||||||
|
async fn modules_and_threshold( |
||||||
|
&self, |
||||||
|
message: &HyperlaneMessage, |
||||||
|
) -> ChainResult<(Vec<H256>, u8)>; |
||||||
|
} |
@ -0,0 +1,14 @@ |
|||||||
|
import { AggregationIsmConfig, ModuleType } from '@hyperlane-xyz/sdk'; |
||||||
|
|
||||||
|
import { merkleRootMultisig, messageIdMultisig } from './multisigIsm'; |
||||||
|
|
||||||
|
export const aggregationIsm = (validatorKey: string): AggregationIsmConfig => { |
||||||
|
return { |
||||||
|
type: ModuleType.AGGREGATION, |
||||||
|
modules: [ |
||||||
|
merkleRootMultisig(validatorKey), |
||||||
|
messageIdMultisig(validatorKey), |
||||||
|
], |
||||||
|
threshold: 1, |
||||||
|
}; |
||||||
|
}; |
@ -1,21 +1,41 @@ |
|||||||
import { ChainMap, ModuleType, MultisigIsmConfig } from '@hyperlane-xyz/sdk'; |
import { ChainMap, ModuleType, MultisigIsmConfig } from '@hyperlane-xyz/sdk'; |
||||||
|
|
||||||
// the addresses here must line up with the e2e test's validator addresses
|
// the addresses here must line up with the e2e test's validator addresses
|
||||||
export const multisigIsm: ChainMap<MultisigIsmConfig> = { |
// Validators are anvil accounts 4-6
|
||||||
// Validators are anvil accounts 4-6
|
export const chainToValidator: Record<string, string> = { |
||||||
test1: { |
test1: '0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65', |
||||||
|
test2: '0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc', |
||||||
|
test3: '0x976EA74026E726554dB657fA54763abd0C3a0aa9', |
||||||
|
}; |
||||||
|
|
||||||
|
export const legacyMultisig = (validatorKey: string): MultisigIsmConfig => { |
||||||
|
return { |
||||||
type: ModuleType.LEGACY_MULTISIG, |
type: ModuleType.LEGACY_MULTISIG, |
||||||
validators: ['0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65'], |
validators: [chainToValidator[validatorKey]], |
||||||
threshold: 1, |
threshold: 1, |
||||||
}, |
}; |
||||||
test2: { |
}; |
||||||
|
|
||||||
|
export const merkleRootMultisig = (validatorKey: string): MultisigIsmConfig => { |
||||||
|
return { |
||||||
type: ModuleType.MERKLE_ROOT_MULTISIG, |
type: ModuleType.MERKLE_ROOT_MULTISIG, |
||||||
validators: ['0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc'], |
validators: [validatorKey], |
||||||
threshold: 1, |
threshold: 1, |
||||||
}, |
}; |
||||||
test3: { |
}; |
||||||
|
|
||||||
|
export const messageIdMultisig = (validatorKey: string): MultisigIsmConfig => { |
||||||
|
return { |
||||||
type: ModuleType.MESSAGE_ID_MULTISIG, |
type: ModuleType.MESSAGE_ID_MULTISIG, |
||||||
validators: ['0x976EA74026E726554dB657fA54763abd0C3a0aa9'], |
validators: [validatorKey], |
||||||
threshold: 1, |
threshold: 1, |
||||||
}, |
}; |
||||||
|
}; |
||||||
|
|
||||||
|
// the addresses here must line up with the e2e test's validator addresses
|
||||||
|
export const multisigIsm: ChainMap<MultisigIsmConfig> = { |
||||||
|
// Validators are anvil accounts 4-6
|
||||||
|
test1: legacyMultisig('test1'), |
||||||
|
test2: merkleRootMultisig('test2'), |
||||||
|
test3: messageIdMultisig('test3'), |
||||||
}; |
}; |
||||||
|
@ -0,0 +1,16 @@ |
|||||||
|
import { ModuleType, RoutingIsmConfig } from '@hyperlane-xyz/sdk'; |
||||||
|
|
||||||
|
import { multisigIsm } from './multisigIsm'; |
||||||
|
|
||||||
|
export const routingIsm = ( |
||||||
|
local_chain: string, |
||||||
|
owner: string, |
||||||
|
): RoutingIsmConfig => { |
||||||
|
return { |
||||||
|
type: ModuleType.ROUTING, |
||||||
|
owner, |
||||||
|
domains: Object.fromEntries( |
||||||
|
Object.entries(multisigIsm).filter(([chain]) => chain !== local_chain), |
||||||
|
), |
||||||
|
}; |
||||||
|
}; |
Loading…
Reference in new issue