From 42382e7d8833994e09d403d6b82742606cb32f02 Mon Sep 17 00:00:00 2001 From: Alexander Pastushenko Date: Mon, 30 Sep 2024 13:06:17 +0400 Subject: [PATCH 1/4] Implement reorg tag support for relayer --- .../chains/hyperlane-ethereum/src/config.rs | 34 ++++++++++++++- .../src/contracts/interchain_gas.rs | 28 ++++++------- .../src/contracts/mailbox.rs | 27 ++++++------ .../src/contracts/merkle_tree_hook.rs | 28 ++++++------- .../hyperlane-ethereum/src/contracts/utils.rs | 35 +++++++++++++++- .../hyperlane-base/src/settings/chains.rs | 42 +++++++++++-------- .../hyperlane-base/src/settings/parser/mod.rs | 6 +-- rust/main/hyperlane-core/src/chain.rs | 26 +++++++++++- 8 files changed, 159 insertions(+), 67 deletions(-) diff --git a/rust/main/chains/hyperlane-ethereum/src/config.rs b/rust/main/chains/hyperlane-ethereum/src/config.rs index 8735b5fd25..cb106eb68d 100644 --- a/rust/main/chains/hyperlane-ethereum/src/config.rs +++ b/rust/main/chains/hyperlane-ethereum/src/config.rs @@ -1,4 +1,6 @@ -use hyperlane_core::{config::OperationBatchConfig, U256}; +use ethers_core::types::{BlockId, BlockNumber}; +use eyre::{eyre, Report}; +use hyperlane_core::{config::OperationBatchConfig, ReorgPeriod, U256}; use url::Url; /// Ethereum RPC connection configuration @@ -52,3 +54,33 @@ pub struct TransactionOverrides { /// Max priority fee per gas to use for EIP-1559 transactions. pub max_priority_fee_per_gas: Option, } + +/// Ethereum reorg period +#[derive(Copy, Clone, Debug)] +pub enum EthereumReorgPeriod { + /// Number of blocks + Blocks(u32), + /// A block tag + Tag(BlockId), +} + +impl TryFrom<&ReorgPeriod> for EthereumReorgPeriod { + type Error = Report; + + fn try_from(value: &ReorgPeriod) -> Result { + match value { + ReorgPeriod::Blocks(blocks) => Ok(EthereumReorgPeriod::Blocks(*blocks)), + ReorgPeriod::Tag(tag) => { + let tag = match tag.as_str() { + "latest" => BlockNumber::Latest, + "finalized" => BlockNumber::Finalized, + "safe" => BlockNumber::Safe, + "earliest" => BlockNumber::Earliest, + "pending" => BlockNumber::Pending, + _ => return Err(eyre!("Invalid Ethereum reorg period")), + }; + Ok(EthereumReorgPeriod::Tag(tag.into())) + } + } + } +} diff --git a/rust/main/chains/hyperlane-ethereum/src/contracts/interchain_gas.rs b/rust/main/chains/hyperlane-ethereum/src/contracts/interchain_gas.rs index b14903a640..2b12a93453 100644 --- a/rust/main/chains/hyperlane-ethereum/src/contracts/interchain_gas.rs +++ b/rust/main/chains/hyperlane-ethereum/src/contracts/interchain_gas.rs @@ -9,18 +9,18 @@ use async_trait::async_trait; use ethers::prelude::Middleware; use hyperlane_core::rpc_clients::call_and_retry_indefinitely; use hyperlane_core::{ - ChainCommunicationError, ChainResult, ContractLocator, HyperlaneAbi, HyperlaneChain, - HyperlaneContract, HyperlaneDomain, HyperlaneProvider, Indexed, Indexer, - InterchainGasPaymaster, InterchainGasPayment, LogMeta, SequenceAwareIndexer, H160, H256, H512, + ChainResult, ContractLocator, HyperlaneAbi, HyperlaneChain, HyperlaneContract, HyperlaneDomain, + HyperlaneProvider, Indexed, Indexer, InterchainGasPaymaster, InterchainGasPayment, LogMeta, + SequenceAwareIndexer, H160, H256, H512, }; use tracing::instrument; -use super::utils::fetch_raw_logs_and_meta; +use super::utils::{fetch_raw_logs_and_meta, get_finalized_block_number}; use crate::interfaces::i_interchain_gas_paymaster::{ GasPaymentFilter, IInterchainGasPaymaster as EthereumInterchainGasPaymasterInternal, IINTERCHAINGASPAYMASTER_ABI, }; -use crate::{BuildableWithProvider, ConnectionConf, EthereumProvider}; +use crate::{BuildableWithProvider, ConnectionConf, EthereumProvider, EthereumReorgPeriod}; impl Display for EthereumInterchainGasPaymasterInternal where @@ -33,7 +33,7 @@ where pub struct InterchainGasPaymasterIndexerBuilder { pub mailbox_address: H160, - pub reorg_period: u32, + pub reorg_period: EthereumReorgPeriod, } #[async_trait] @@ -63,7 +63,7 @@ where { contract: Arc>, provider: Arc, - reorg_period: u32, + reorg_period: EthereumReorgPeriod, } impl EthereumInterchainGasPaymasterIndexer @@ -71,7 +71,11 @@ where M: Middleware + 'static, { /// Create new EthereumInterchainGasPaymasterIndexer - pub fn new(provider: Arc, locator: &ContractLocator, reorg_period: u32) -> Self { + pub fn new( + provider: Arc, + locator: &ContractLocator, + reorg_period: EthereumReorgPeriod, + ) -> Self { Self { contract: Arc::new(EthereumInterchainGasPaymasterInternal::new( locator.address, @@ -122,13 +126,7 @@ where #[instrument(level = "debug", err, ret, skip(self))] #[allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue async fn get_finalized_block_number(&self) -> ChainResult { - Ok(self - .provider - .get_block_number() - .await - .map_err(ChainCommunicationError::from_other)? - .as_u32() - .saturating_sub(self.reorg_period)) + get_finalized_block_number(&self.provider, self.reorg_period).await } async fn fetch_logs_by_tx_hash( diff --git a/rust/main/chains/hyperlane-ethereum/src/contracts/mailbox.rs b/rust/main/chains/hyperlane-ethereum/src/contracts/mailbox.rs index 72d01e4d62..30bea02028 100644 --- a/rust/main/chains/hyperlane-ethereum/src/contracts/mailbox.rs +++ b/rust/main/chains/hyperlane-ethereum/src/contracts/mailbox.rs @@ -32,10 +32,13 @@ use crate::interfaces::i_mailbox::{ }; use crate::interfaces::mailbox::DispatchFilter; use crate::tx::{call_with_lag, fill_tx_gas_params, report_tx}; -use crate::{BuildableWithProvider, ConnectionConf, EthereumProvider, TransactionOverrides}; +use crate::{ + BuildableWithProvider, ConnectionConf, EthereumProvider, EthereumReorgPeriod, + TransactionOverrides, +}; use super::multicall::{self, build_multicall}; -use super::utils::fetch_raw_logs_and_meta; +use super::utils::{fetch_raw_logs_and_meta, get_finalized_block_number}; impl std::fmt::Display for EthereumMailboxInternal where @@ -47,7 +50,7 @@ where } pub struct SequenceIndexerBuilder { - pub reorg_period: u32, + pub reorg_period: EthereumReorgPeriod, } #[async_trait] @@ -70,7 +73,7 @@ impl BuildableWithProvider for SequenceIndexerBuilder { } pub struct DeliveryIndexerBuilder { - pub reorg_period: u32, + pub reorg_period: EthereumReorgPeriod, } #[async_trait] @@ -100,7 +103,7 @@ where { contract: Arc>, provider: Arc, - reorg_period: u32, + reorg_period: EthereumReorgPeriod, } impl EthereumMailboxIndexer @@ -108,7 +111,11 @@ where M: Middleware + 'static, { /// Create new EthereumMailboxIndexer - pub fn new(provider: Arc, locator: &ContractLocator, reorg_period: u32) -> Self { + pub fn new( + provider: Arc, + locator: &ContractLocator, + reorg_period: EthereumReorgPeriod, + ) -> Self { let contract = Arc::new(EthereumMailboxInternal::new( locator.address, provider.clone(), @@ -122,13 +129,7 @@ where #[instrument(level = "debug", err, ret, skip(self))] async fn get_finalized_block_number(&self) -> ChainResult { - Ok(self - .provider - .get_block_number() - .await - .map_err(ChainCommunicationError::from_other)? - .as_u32() - .saturating_sub(self.reorg_period)) + get_finalized_block_number(&self.provider, self.reorg_period).await } } diff --git a/rust/main/chains/hyperlane-ethereum/src/contracts/merkle_tree_hook.rs b/rust/main/chains/hyperlane-ethereum/src/contracts/merkle_tree_hook.rs index a2f8a4a745..c5a1514629 100644 --- a/rust/main/chains/hyperlane-ethereum/src/contracts/merkle_tree_hook.rs +++ b/rust/main/chains/hyperlane-ethereum/src/contracts/merkle_tree_hook.rs @@ -10,18 +10,18 @@ use hyperlane_core::rpc_clients::call_and_retry_indefinitely; use tracing::instrument; use hyperlane_core::{ - ChainCommunicationError, ChainResult, Checkpoint, ContractLocator, HyperlaneChain, - HyperlaneContract, HyperlaneDomain, HyperlaneProvider, Indexed, Indexer, LogMeta, - MerkleTreeHook, MerkleTreeInsertion, SequenceAwareIndexer, H256, H512, + ChainResult, Checkpoint, ContractLocator, HyperlaneChain, HyperlaneContract, HyperlaneDomain, + HyperlaneProvider, Indexed, Indexer, LogMeta, MerkleTreeHook, MerkleTreeInsertion, + SequenceAwareIndexer, H256, H512, }; use crate::interfaces::merkle_tree_hook::{ InsertedIntoTreeFilter, MerkleTreeHook as MerkleTreeHookContract, Tree, }; use crate::tx::call_with_lag; -use crate::{BuildableWithProvider, ConnectionConf, EthereumProvider}; +use crate::{BuildableWithProvider, ConnectionConf, EthereumProvider, EthereumReorgPeriod}; -use super::utils::fetch_raw_logs_and_meta; +use super::utils::{fetch_raw_logs_and_meta, get_finalized_block_number}; // We don't need the reverse of this impl, so it's ok to disable the clippy lint #[allow(clippy::from_over_into)] @@ -58,7 +58,7 @@ impl BuildableWithProvider for MerkleTreeHookBuilder { } pub struct MerkleTreeHookIndexerBuilder { - pub reorg_period: u32, + pub reorg_period: EthereumReorgPeriod, } #[async_trait] @@ -88,7 +88,7 @@ where { contract: Arc>, provider: Arc, - reorg_period: u32, + reorg_period: EthereumReorgPeriod, } impl EthereumMerkleTreeHookIndexer @@ -96,7 +96,11 @@ where M: Middleware + 'static, { /// Create new EthereumMerkleTreeHookIndexer - pub fn new(provider: Arc, locator: &ContractLocator, reorg_period: u32) -> Self { + pub fn new( + provider: Arc, + locator: &ContractLocator, + reorg_period: EthereumReorgPeriod, + ) -> Self { Self { contract: Arc::new(MerkleTreeHookContract::new( locator.address, @@ -143,13 +147,7 @@ where #[instrument(level = "debug", err, skip(self))] #[allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue async fn get_finalized_block_number(&self) -> ChainResult { - Ok(self - .provider - .get_block_number() - .await - .map_err(ChainCommunicationError::from_other)? - .as_u32() - .saturating_sub(self.reorg_period)) + get_finalized_block_number(&self.provider, self.reorg_period).await } async fn fetch_logs_by_tx_hash( diff --git a/rust/main/chains/hyperlane-ethereum/src/contracts/utils.rs b/rust/main/chains/hyperlane-ethereum/src/contracts/utils.rs index 2905199184..5a07c76eb0 100644 --- a/rust/main/chains/hyperlane-ethereum/src/contracts/utils.rs +++ b/rust/main/chains/hyperlane-ethereum/src/contracts/utils.rs @@ -6,7 +6,10 @@ use ethers::{ types::{H160 as EthersH160, H256 as EthersH256}, }; use ethers_contract::{ContractError, EthEvent, LogMeta as EthersLogMeta}; -use hyperlane_core::{ChainResult, LogMeta, H512}; +use hyperlane_core::{ChainCommunicationError, ChainResult, LogMeta, H512}; +use tracing::instrument; + +use crate::EthereumReorgPeriod; pub async fn fetch_raw_logs_and_meta( tx_hash: H512, @@ -44,3 +47,33 @@ where .collect(); Ok(logs) } + +#[instrument(level = "trace", err, ret, skip(provider))] +pub async fn get_finalized_block_number( + provider: &M, + reorg_period: EthereumReorgPeriod, +) -> ChainResult +where + M: Middleware + 'static, +{ + let number = match reorg_period { + EthereumReorgPeriod::Blocks(blocks) => provider + .get_block_number() + .await + .map_err(ChainCommunicationError::from_other)? + .as_u32() + .saturating_sub(blocks), + + EthereumReorgPeriod::Tag(tag) => provider + .get_block(tag) + .await + .map_err(ChainCommunicationError::from_other)? + .and_then(|block| block.number) + .ok_or(ChainCommunicationError::CustomError( + "Unable to get finalized block number".into(), + ))? + .as_u32(), + }; + + Ok(number) +} diff --git a/rust/main/hyperlane-base/src/settings/chains.rs b/rust/main/hyperlane-base/src/settings/chains.rs index 8dcb2b9c58..a665aa9d14 100644 --- a/rust/main/hyperlane-base/src/settings/chains.rs +++ b/rust/main/hyperlane-base/src/settings/chains.rs @@ -10,13 +10,13 @@ use hyperlane_core::{ config::OperationBatchConfig, AggregationIsm, CcipReadIsm, ContractLocator, HyperlaneAbi, HyperlaneDomain, HyperlaneDomainProtocol, HyperlaneMessage, HyperlaneProvider, IndexMode, InterchainGasPaymaster, InterchainGasPayment, InterchainSecurityModule, Mailbox, - MerkleTreeHook, MerkleTreeInsertion, MultisigIsm, RoutingIsm, SequenceAwareIndexer, - ValidatorAnnounce, H256, + MerkleTreeHook, MerkleTreeInsertion, MultisigIsm, ReorgPeriod, RoutingIsm, + SequenceAwareIndexer, ValidatorAnnounce, H256, }; use hyperlane_cosmos as h_cosmos; use hyperlane_ethereum::{ self as h_eth, BuildableWithProvider, EthereumInterchainGasPaymasterAbi, EthereumMailboxAbi, - EthereumValidatorAnnounceAbi, + EthereumReorgPeriod, EthereumValidatorAnnounceAbi, }; use hyperlane_fuel as h_fuel; use hyperlane_sealevel as h_sealevel; @@ -45,7 +45,7 @@ pub struct ChainConf { /// Signer configuration for this chain pub signer: Option, /// The reorg period of the chain, i.e. the number of blocks until finality - pub reorg_period: u32, + pub reorg_period: ReorgPeriod, /// Addresses of contracts on the chain pub addresses: CoreContractAddresses, /// The chain connection details @@ -272,13 +272,13 @@ impl ChainConf { match &self.connection { ChainConnectionConf::Ethereum(conf) => { + let reorg_period = + EthereumReorgPeriod::try_from(&self.reorg_period).context(ctx)?; self.build_ethereum( conf, &locator, metrics, - h_eth::SequenceIndexerBuilder { - reorg_period: self.reorg_period, - }, + h_eth::SequenceIndexerBuilder { reorg_period }, ) .await } @@ -289,11 +289,12 @@ impl ChainConf { } ChainConnectionConf::Cosmos(conf) => { let signer = self.cosmos_signer().await.context(ctx)?; + let reorg_period = self.reorg_period.as_number().context(ctx)?; let indexer = Box::new(h_cosmos::CosmosMailboxDispatchIndexer::new( conf.clone(), locator, signer, - self.reorg_period, + reorg_period, )?); Ok(indexer as Box>) } @@ -311,13 +312,13 @@ impl ChainConf { match &self.connection { ChainConnectionConf::Ethereum(conf) => { + let reorg_period = + EthereumReorgPeriod::try_from(&self.reorg_period).context(ctx)?; self.build_ethereum( conf, &locator, metrics, - h_eth::DeliveryIndexerBuilder { - reorg_period: self.reorg_period, - }, + h_eth::DeliveryIndexerBuilder { reorg_period }, ) .await } @@ -328,11 +329,12 @@ impl ChainConf { } ChainConnectionConf::Cosmos(conf) => { let signer = self.cosmos_signer().await.context(ctx)?; + let reorg_period = self.reorg_period.as_number().context(ctx)?; let indexer = Box::new(h_cosmos::CosmosMailboxDeliveryIndexer::new( conf.clone(), locator, signer, - self.reorg_period, + reorg_period, )?); Ok(indexer as Box>) } @@ -389,13 +391,15 @@ impl ChainConf { match &self.connection { ChainConnectionConf::Ethereum(conf) => { + let reorg_period = + EthereumReorgPeriod::try_from(&self.reorg_period).context(ctx)?; self.build_ethereum( conf, &locator, metrics, h_eth::InterchainGasPaymasterIndexerBuilder { mailbox_address: self.addresses.mailbox.into(), - reorg_period: self.reorg_period, + reorg_period, }, ) .await @@ -408,10 +412,11 @@ impl ChainConf { Ok(indexer as Box>) } ChainConnectionConf::Cosmos(conf) => { + let reorg_period = self.reorg_period.as_number().context(ctx)?; let indexer = Box::new(h_cosmos::CosmosInterchainGasPaymasterIndexer::new( conf.clone(), locator, - self.reorg_period, + reorg_period, )?); Ok(indexer as Box>) } @@ -429,13 +434,13 @@ impl ChainConf { match &self.connection { ChainConnectionConf::Ethereum(conf) => { + let reorg_period = + EthereumReorgPeriod::try_from(&self.reorg_period).context(ctx)?; self.build_ethereum( conf, &locator, metrics, - h_eth::MerkleTreeHookIndexerBuilder { - reorg_period: self.reorg_period, - }, + h_eth::MerkleTreeHookIndexerBuilder { reorg_period }, ) .await } @@ -450,12 +455,13 @@ impl ChainConf { } ChainConnectionConf::Cosmos(conf) => { let signer = self.cosmos_signer().await.context(ctx)?; + let reorg_period = self.reorg_period.as_number().context(ctx)?; let indexer = Box::new(h_cosmos::CosmosMerkleTreeHookIndexer::new( conf.clone(), locator, // TODO: remove signer requirement entirely signer, - self.reorg_period, + reorg_period, )?); Ok(indexer as Box>) } diff --git a/rust/main/hyperlane-base/src/settings/parser/mod.rs b/rust/main/hyperlane-base/src/settings/parser/mod.rs index f5c1451a0a..124beb927a 100644 --- a/rust/main/hyperlane-base/src/settings/parser/mod.rs +++ b/rust/main/hyperlane-base/src/settings/parser/mod.rs @@ -19,7 +19,7 @@ use url::Url; use h_cosmos::RawCosmosAmount; use hyperlane_core::{ cfg_unwrap_all, config::*, HyperlaneDomain, HyperlaneDomainProtocol, - HyperlaneDomainTechnicalStack, IndexMode, + HyperlaneDomainTechnicalStack, IndexMode, ReorgPeriod, }; use crate::settings::{ @@ -136,8 +136,8 @@ fn parse_chain( .chain(&mut err) .get_opt_key("blocks") .get_key("reorgPeriod") - .parse_u32() - .unwrap_or(1); + .parse_value("Invalid reorgPeriod") + .unwrap_or(ReorgPeriod::Blocks(1)); let rpcs = parse_base_and_override_urls(&chain, "rpcUrls", "customRpcUrls", "http", &mut err); diff --git a/rust/main/hyperlane-core/src/chain.rs b/rust/main/hyperlane-core/src/chain.rs index d1b7c5e349..136a916c4d 100644 --- a/rust/main/hyperlane-core/src/chain.rs +++ b/rust/main/hyperlane-core/src/chain.rs @@ -6,9 +6,10 @@ use std::{ }; use derive_new::new; +use eyre::{eyre, Report}; use num_derive::FromPrimitive; use num_traits::FromPrimitive; -use serde::Serialize; +use serde::{Deserialize, Serialize}; #[cfg(feature = "strum")] use strum::{EnumIter, EnumString, IntoStaticStr}; @@ -39,6 +40,29 @@ impl<'a> std::fmt::Display for ContractLocator<'a> { } } +#[derive(Debug, Clone, Deserialize)] +#[serde(untagged)] +pub enum ReorgPeriod { + Blocks(u32), + Tag(String), +} + +impl ReorgPeriod { + pub fn as_number(&self) -> Result { + if let ReorgPeriod::Blocks(blocks) = self { + Ok(*blocks) + } else { + Err(eyre!("Invalid reorg period")) + } + } +} + +impl Default for ReorgPeriod { + fn default() -> Self { + ReorgPeriod::Blocks(0) + } +} + /// All domains supported by Hyperlane. #[derive(FromPrimitive, PartialEq, Eq, Debug, Clone, Copy, Hash, Serialize)] #[cfg_attr( From 8068c07c45788ca86765f93d4203d13addd4bd44 Mon Sep 17 00:00:00 2001 From: Alexander Pastushenko Date: Tue, 1 Oct 2024 22:47:21 +0400 Subject: [PATCH 2/4] Implement reorg tag support for validator --- rust/main/agents/validator/src/settings.rs | 12 ++++---- rust/main/agents/validator/src/submit.rs | 16 ++++++---- rust/main/agents/validator/src/validator.rs | 14 ++++----- .../hyperlane-cosmos/src/mailbox/contract.rs | 4 +-- .../hyperlane-cosmos/src/merkle_tree_hook.rs | 8 ++--- .../main/chains/hyperlane-cosmos/src/utils.rs | 9 +++--- .../src/contracts/mailbox.rs | 5 ++-- .../src/contracts/merkle_tree_hook.rs | 9 +++--- rust/main/chains/hyperlane-ethereum/src/tx.rs | 30 +++++++++++-------- .../main/chains/hyperlane-fuel/src/mailbox.rs | 7 ++--- .../chains/hyperlane-sealevel/src/mailbox.rs | 6 ++-- .../src/merkle_tree_hook.rs | 10 +++---- .../main/hyperlane-core/src/traits/mailbox.rs | 5 ++-- .../src/traits/merkle_tree_hook.rs | 8 ++--- rust/main/hyperlane-test/src/mocks/mailbox.rs | 10 +++---- 15 files changed, 80 insertions(+), 73 deletions(-) diff --git a/rust/main/agents/validator/src/settings.rs b/rust/main/agents/validator/src/settings.rs index 02dc87cb13..ffe41fb53f 100644 --- a/rust/main/agents/validator/src/settings.rs +++ b/rust/main/agents/validator/src/settings.rs @@ -15,7 +15,9 @@ use hyperlane_base::{ CheckpointSyncerConf, Settings, SignerConf, }, }; -use hyperlane_core::{cfg_unwrap_all, config::*, HyperlaneDomain, HyperlaneDomainProtocol}; +use hyperlane_core::{ + cfg_unwrap_all, config::*, HyperlaneDomain, HyperlaneDomainProtocol, ReorgPeriod, +}; use serde::Deserialize; use serde_json::Value; @@ -36,8 +38,8 @@ pub struct ValidatorSettings { pub validator: SignerConf, /// The checkpoint syncer configuration pub checkpoint_syncer: CheckpointSyncerConf, - /// The reorg_period in blocks - pub reorg_period: u64, + /// The reorg configuration + pub reorg_period: ReorgPeriod, /// How frequently to check for new checkpoints pub interval: Duration, } @@ -122,8 +124,8 @@ impl FromRawConf for ValidatorSettings { .get_key(origin_chain_name) .get_opt_key("blocks") .get_opt_key("reorgPeriod") - .parse_u64() - .unwrap_or(1); + .parse_value("Invalid reorgPeriod") + .unwrap_or(ReorgPeriod::Blocks(1)); cfg_unwrap_all!(cwp, err: [base, origin_chain, validator, checkpoint_syncer]); diff --git a/rust/main/agents/validator/src/submit.rs b/rust/main/agents/validator/src/submit.rs index 8f91f284fb..470fabe717 100644 --- a/rust/main/agents/validator/src/submit.rs +++ b/rust/main/agents/validator/src/submit.rs @@ -1,10 +1,9 @@ -use std::num::NonZeroU64; use std::sync::Arc; use std::time::{Duration, Instant}; use std::vec; use hyperlane_core::rpc_clients::call_and_retry_indefinitely; -use hyperlane_core::{ChainResult, MerkleTreeHook}; +use hyperlane_core::{ChainResult, MerkleTreeHook, ReorgPeriod}; use prometheus::IntGauge; use tokio::time::sleep; use tracing::{debug, error, info}; @@ -19,7 +18,7 @@ use hyperlane_ethereum::SingletonSignerHandle; #[derive(Clone)] pub(crate) struct ValidatorSubmitter { interval: Duration, - reorg_period: Option, + reorg_period: Option, signer: SingletonSignerHandle, merkle_tree_hook: Arc, checkpoint_syncer: Arc, @@ -30,7 +29,7 @@ pub(crate) struct ValidatorSubmitter { impl ValidatorSubmitter { pub(crate) fn new( interval: Duration, - reorg_period: u64, + reorg_period: ReorgPeriod, merkle_tree_hook: Arc, signer: SingletonSignerHandle, checkpoint_syncer: Arc, @@ -38,7 +37,7 @@ impl ValidatorSubmitter { metrics: ValidatorSubmitterMetrics, ) -> Self { Self { - reorg_period: NonZeroU64::new(reorg_period), + reorg_period: Some(reorg_period), interval, merkle_tree_hook, signer, @@ -93,7 +92,12 @@ impl ValidatorSubmitter { // Lag by reorg period because this is our correctness checkpoint. let latest_checkpoint = call_and_retry_indefinitely(|| { let merkle_tree_hook = self.merkle_tree_hook.clone(); - Box::pin(async move { merkle_tree_hook.latest_checkpoint(self.reorg_period).await }) + let reorg_period = self.reorg_period.clone(); + Box::pin(async move { + merkle_tree_hook + .latest_checkpoint(reorg_period.as_ref()) + .await + }) }) .await; diff --git a/rust/main/agents/validator/src/validator.rs b/rust/main/agents/validator/src/validator.rs index a4f9a9e003..61b352a357 100644 --- a/rust/main/agents/validator/src/validator.rs +++ b/rust/main/agents/validator/src/validator.rs @@ -1,4 +1,4 @@ -use std::{num::NonZeroU64, sync::Arc, time::Duration}; +use std::{sync::Arc, time::Duration}; use crate::server as validator_server; use async_trait::async_trait; @@ -19,8 +19,8 @@ use hyperlane_base::{ use hyperlane_core::{ Announcement, ChainResult, HyperlaneChain, HyperlaneContract, HyperlaneDomain, HyperlaneSigner, - HyperlaneSignerExt, Mailbox, MerkleTreeHook, MerkleTreeInsertion, TxOutcome, ValidatorAnnounce, - H256, U256, + HyperlaneSignerExt, Mailbox, MerkleTreeHook, MerkleTreeInsertion, ReorgPeriod, TxOutcome, + ValidatorAnnounce, H256, U256, }; use hyperlane_ethereum::{SingletonSigner, SingletonSignerHandle}; @@ -44,7 +44,7 @@ pub struct Validator { signer: SingletonSignerHandle, // temporary holder until `run` is called signer_instance: Option>, - reorg_period: u64, + reorg_period: ReorgPeriod, interval: Duration, checkpoint_syncer: Arc, core_metrics: Arc, @@ -180,7 +180,7 @@ impl BaseAgent for Validator { // announce the validator after spawning the signer task self.announce().await.expect("Failed to announce validator"); - let reorg_period = NonZeroU64::new(self.reorg_period); + let reorg_period = Some(&self.reorg_period); // Ensure that the merkle tree hook has count > 0 before we begin indexing // messages or submitting checkpoints. @@ -237,7 +237,7 @@ impl Validator { async fn run_checkpoint_submitters(&self) -> Vec>> { let submitter = ValidatorSubmitter::new( self.interval, - self.reorg_period, + self.reorg_period.clone(), self.merkle_tree_hook.clone(), self.signer.clone(), self.checkpoint_syncer.clone(), @@ -245,7 +245,7 @@ impl Validator { ValidatorSubmitterMetrics::new(&self.core.metrics, &self.origin_chain), ); - let reorg_period = NonZeroU64::new(self.reorg_period); + let reorg_period = Some(&self.reorg_period); let tip_tree = self .merkle_tree_hook .tree(reorg_period) diff --git a/rust/main/chains/hyperlane-cosmos/src/mailbox/contract.rs b/rust/main/chains/hyperlane-cosmos/src/mailbox/contract.rs index 9d9f39c541..8c2cbc2687 100644 --- a/rust/main/chains/hyperlane-cosmos/src/mailbox/contract.rs +++ b/rust/main/chains/hyperlane-cosmos/src/mailbox/contract.rs @@ -8,7 +8,7 @@ use tracing::instrument; use hyperlane_core::{ utils::bytes_to_hex, ChainResult, ContractLocator, HyperlaneChain, HyperlaneContract, HyperlaneDomain, HyperlaneMessage, HyperlaneProvider, Mailbox, RawHyperlaneMessage, - TxCostEstimate, TxOutcome, H256, U256, + ReorgPeriod, TxCostEstimate, TxOutcome, H256, U256, }; use crate::address::CosmosAddress; @@ -83,7 +83,7 @@ impl HyperlaneChain for CosmosMailbox { impl Mailbox for CosmosMailbox { #[instrument(level = "debug", err, ret, skip(self))] #[allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue - async fn count(&self, lag: Option) -> ChainResult { + async fn count(&self, lag: Option<&ReorgPeriod>) -> ChainResult { let block_height = get_block_height_for_lag(self.provider.grpc(), lag).await?; self.nonce_at_block(block_height).await } diff --git a/rust/main/chains/hyperlane-cosmos/src/merkle_tree_hook.rs b/rust/main/chains/hyperlane-cosmos/src/merkle_tree_hook.rs index c9e48c59f2..286456a923 100644 --- a/rust/main/chains/hyperlane-cosmos/src/merkle_tree_hook.rs +++ b/rust/main/chains/hyperlane-cosmos/src/merkle_tree_hook.rs @@ -10,7 +10,7 @@ use hyperlane_core::accumulator::incremental::IncrementalMerkle; use hyperlane_core::{ ChainCommunicationError, ChainResult, Checkpoint, ContractLocator, HyperlaneChain, HyperlaneContract, HyperlaneDomain, HyperlaneProvider, Indexed, Indexer, LogMeta, - MerkleTreeHook, MerkleTreeInsertion, SequenceAwareIndexer, H256, H512, + MerkleTreeHook, MerkleTreeInsertion, ReorgPeriod, SequenceAwareIndexer, H256, H512, }; use crate::grpc::WasmProvider; @@ -76,7 +76,7 @@ impl MerkleTreeHook for CosmosMerkleTreeHook { /// Return the incremental merkle tree in storage #[instrument(level = "debug", err, ret, skip(self))] #[allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue - async fn tree(&self, lag: Option) -> ChainResult { + async fn tree(&self, lag: Option<&ReorgPeriod>) -> ChainResult { let payload = merkle_tree_hook::MerkleTreeRequest { tree: general::EmptyStruct {}, }; @@ -110,7 +110,7 @@ impl MerkleTreeHook for CosmosMerkleTreeHook { } /// Gets the current leaf count of the merkle tree - async fn count(&self, lag: Option) -> ChainResult { + async fn count(&self, lag: Option<&ReorgPeriod>) -> ChainResult { let payload = merkle_tree_hook::MerkleTreeCountRequest { count: general::EmptyStruct {}, }; @@ -121,7 +121,7 @@ impl MerkleTreeHook for CosmosMerkleTreeHook { } #[instrument(level = "debug", err, ret, skip(self))] #[allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue - async fn latest_checkpoint(&self, lag: Option) -> ChainResult { + async fn latest_checkpoint(&self, lag: Option<&ReorgPeriod>) -> ChainResult { let payload = merkle_tree_hook::CheckPointRequest { check_point: general::EmptyStruct {}, }; diff --git a/rust/main/chains/hyperlane-cosmos/src/utils.rs b/rust/main/chains/hyperlane-cosmos/src/utils.rs index 74cb75a279..1538ccf6cc 100644 --- a/rust/main/chains/hyperlane-cosmos/src/utils.rs +++ b/rust/main/chains/hyperlane-cosmos/src/utils.rs @@ -11,7 +11,7 @@ use tendermint::Hash; use tokio::task::JoinHandle; use tracing::warn; -use hyperlane_core::{ChainCommunicationError, ChainResult, Indexed, LogMeta, H256}; +use hyperlane_core::{ChainCommunicationError, ChainResult, Indexed, LogMeta, ReorgPeriod, H256}; use crate::grpc::{WasmGrpcProvider, WasmProvider}; use crate::rpc::{CosmosWasmRpcProvider, ParsedEvent, WasmRpcProvider}; @@ -29,12 +29,13 @@ pub(crate) static CONTRACT_ADDRESS_ATTRIBUTE_KEY_BASE64: Lazy = /// tip directly can be used. pub(crate) async fn get_block_height_for_lag( provider: &WasmGrpcProvider, - lag: Option, + lag: Option<&ReorgPeriod>, ) -> ChainResult> { let block_height = match lag { - Some(lag) => { + Some(reorg_period) => { + let lag = reorg_period.as_number()?; let tip = provider.latest_block_height().await?; - let block_height = tip - lag.get(); + let block_height = tip - lag as u64; Some(block_height) } None => None, diff --git a/rust/main/chains/hyperlane-ethereum/src/contracts/mailbox.rs b/rust/main/chains/hyperlane-ethereum/src/contracts/mailbox.rs index 30bea02028..a20d968b09 100644 --- a/rust/main/chains/hyperlane-ethereum/src/contracts/mailbox.rs +++ b/rust/main/chains/hyperlane-ethereum/src/contracts/mailbox.rs @@ -2,7 +2,6 @@ #![allow(missing_docs)] use std::collections::HashMap; -use std::num::NonZeroU64; use std::ops::RangeInclusive; use std::sync::Arc; @@ -14,7 +13,7 @@ use ethers_contract::builders::ContractCall; use ethers_contract::{Multicall, MulticallResult}; use futures_util::future::join_all; use hyperlane_core::rpc_clients::call_and_retry_indefinitely; -use hyperlane_core::{BatchResult, QueueOperation, H512}; +use hyperlane_core::{BatchResult, QueueOperation, ReorgPeriod, H512}; use itertools::Itertools; use tracing::instrument; @@ -461,7 +460,7 @@ where M: Middleware + 'static, { #[instrument(skip(self))] - async fn count(&self, maybe_lag: Option) -> ChainResult { + async fn count(&self, maybe_lag: Option<&ReorgPeriod>) -> ChainResult { let call = call_with_lag(self.contract.nonce(), &self.provider, maybe_lag).await?; let nonce = call.call().await?; Ok(nonce) diff --git a/rust/main/chains/hyperlane-ethereum/src/contracts/merkle_tree_hook.rs b/rust/main/chains/hyperlane-ethereum/src/contracts/merkle_tree_hook.rs index c5a1514629..29f2a98b64 100644 --- a/rust/main/chains/hyperlane-ethereum/src/contracts/merkle_tree_hook.rs +++ b/rust/main/chains/hyperlane-ethereum/src/contracts/merkle_tree_hook.rs @@ -1,5 +1,4 @@ #![allow(missing_docs)] -use std::num::NonZeroU64; use std::ops::RangeInclusive; use std::sync::Arc; @@ -11,7 +10,7 @@ use tracing::instrument; use hyperlane_core::{ ChainResult, Checkpoint, ContractLocator, HyperlaneChain, HyperlaneContract, HyperlaneDomain, - HyperlaneProvider, Indexed, Indexer, LogMeta, MerkleTreeHook, MerkleTreeInsertion, + HyperlaneProvider, Indexed, Indexer, LogMeta, MerkleTreeHook, MerkleTreeInsertion, ReorgPeriod, SequenceAwareIndexer, H256, H512, }; @@ -251,7 +250,7 @@ where M: Middleware + 'static, { #[instrument(skip(self))] - async fn latest_checkpoint(&self, maybe_lag: Option) -> ChainResult { + async fn latest_checkpoint(&self, maybe_lag: Option<&ReorgPeriod>) -> ChainResult { let call = call_with_lag(self.contract.latest_checkpoint(), &self.provider, maybe_lag).await?; @@ -266,14 +265,14 @@ where #[instrument(skip(self))] #[allow(clippy::needless_range_loop)] - async fn tree(&self, maybe_lag: Option) -> ChainResult { + async fn tree(&self, maybe_lag: Option<&ReorgPeriod>) -> ChainResult { let call = call_with_lag(self.contract.tree(), &self.provider, maybe_lag).await?; Ok(call.call().await?.into()) } #[instrument(skip(self))] - async fn count(&self, maybe_lag: Option) -> ChainResult { + async fn count(&self, maybe_lag: Option<&ReorgPeriod>) -> ChainResult { let call = call_with_lag(self.contract.count(), &self.provider, maybe_lag).await?; let count = call.call().await?; Ok(count) diff --git a/rust/main/chains/hyperlane-ethereum/src/tx.rs b/rust/main/chains/hyperlane-ethereum/src/tx.rs index eb42f66d40..773538af8c 100644 --- a/rust/main/chains/hyperlane-ethereum/src/tx.rs +++ b/rust/main/chains/hyperlane-ethereum/src/tx.rs @@ -1,4 +1,3 @@ -use std::num::NonZeroU64; use std::sync::Arc; use std::time::Duration; @@ -16,10 +15,12 @@ use ethers_core::{ EIP1559_FEE_ESTIMATION_REWARD_PERCENTILE, }, }; -use hyperlane_core::{utils::bytes_to_hex, ChainCommunicationError, ChainResult, H256, U256}; +use hyperlane_core::{ + utils::bytes_to_hex, ChainCommunicationError, ChainResult, ReorgPeriod, H256, U256, +}; use tracing::{debug, error, info, warn}; -use crate::{Middleware, TransactionOverrides}; +use crate::{EthereumReorgPeriod, Middleware, TransactionOverrides}; /// An amount of gas to add to the estimated gas pub const GAS_ESTIMATE_BUFFER: u32 = 75_000; @@ -219,20 +220,25 @@ where pub(crate) async fn call_with_lag( call: ethers::contract::builders::ContractCall, provider: &M, - maybe_lag: Option, + maybe_lag: Option<&ReorgPeriod>, ) -> ChainResult> where M: Middleware + 'static, T: Detokenize, { - if let Some(lag) = maybe_lag { - let fixed_block_number: BlockNumber = provider - .get_block_number() - .await - .map_err(ChainCommunicationError::from_other)? - .saturating_sub(lag.get().into()) - .into(); - Ok(call.block(fixed_block_number)) + if let Some(reorg_period) = maybe_lag { + let reorg_period = EthereumReorgPeriod::try_from(reorg_period)?; + let block = match reorg_period { + EthereumReorgPeriod::Blocks(lag) => provider + .get_block_number() + .await + .map_err(ChainCommunicationError::from_other)? + .saturating_sub(lag.into()) + .into(), + EthereumReorgPeriod::Tag(tag) => tag, + }; + + Ok(call.block(block)) } else { Ok(call) } diff --git a/rust/main/chains/hyperlane-fuel/src/mailbox.rs b/rust/main/chains/hyperlane-fuel/src/mailbox.rs index 3df6bdc9e2..c4df8ab88b 100644 --- a/rust/main/chains/hyperlane-fuel/src/mailbox.rs +++ b/rust/main/chains/hyperlane-fuel/src/mailbox.rs @@ -10,13 +10,12 @@ use fuels::{ use hyperlane_core::{ utils::bytes_to_hex, ChainCommunicationError, ChainResult, ContractLocator, HyperlaneAbi, HyperlaneChain, HyperlaneContract, HyperlaneDomain, HyperlaneMessage, HyperlaneProvider, - Indexed, Indexer, LogMeta, Mailbox, RawHyperlaneMessage, SequenceAwareIndexer, TxCostEstimate, - TxOutcome, H256, H512, U256, + Indexed, Indexer, LogMeta, Mailbox, RawHyperlaneMessage, ReorgPeriod, SequenceAwareIndexer, + TxCostEstimate, TxOutcome, H256, H512, U256, }; use std::{ collections::HashMap, fmt::{Debug, Formatter}, - num::NonZeroU64, ops::RangeInclusive, }; use tracing::{instrument, warn}; @@ -74,7 +73,7 @@ impl Debug for FuelMailbox { impl Mailbox for FuelMailbox { #[instrument(level = "debug", err, ret, skip(self))] #[allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue - async fn count(&self, lag: Option) -> ChainResult { + async fn count(&self, lag: Option<&ReorgPeriod>) -> ChainResult { assert!( lag.is_none(), "Fuel does not support querying point-in-time" diff --git a/rust/main/chains/hyperlane-sealevel/src/mailbox.rs b/rust/main/chains/hyperlane-sealevel/src/mailbox.rs index e919b2de71..7c27ac0252 100644 --- a/rust/main/chains/hyperlane-sealevel/src/mailbox.rs +++ b/rust/main/chains/hyperlane-sealevel/src/mailbox.rs @@ -11,8 +11,8 @@ use hyperlane_core::{ accumulator::incremental::IncrementalMerkle, BatchItem, ChainCommunicationError, ChainResult, Checkpoint, ContractLocator, Decode as _, Encode as _, FixedPointNumber, HyperlaneAbi, HyperlaneChain, HyperlaneContract, HyperlaneDomain, HyperlaneMessage, HyperlaneProvider, - Indexed, Indexer, KnownHyperlaneDomain, LogMeta, Mailbox, MerkleTreeHook, SequenceAwareIndexer, - TxCostEstimate, TxOutcome, H256, H512, U256, + Indexed, Indexer, KnownHyperlaneDomain, LogMeta, Mailbox, MerkleTreeHook, ReorgPeriod, + SequenceAwareIndexer, TxCostEstimate, TxOutcome, H256, H512, U256, }; use hyperlane_sealevel_interchain_security_module_interface::{ InterchainSecurityModuleInstruction, VerifyInstruction, @@ -424,7 +424,7 @@ impl std::fmt::Debug for SealevelMailbox { #[async_trait] impl Mailbox for SealevelMailbox { #[instrument(err, ret, skip(self))] - async fn count(&self, _maybe_lag: Option) -> ChainResult { + async fn count(&self, _maybe_lag: Option<&ReorgPeriod>) -> ChainResult { ::count(self, _maybe_lag).await } diff --git a/rust/main/chains/hyperlane-sealevel/src/merkle_tree_hook.rs b/rust/main/chains/hyperlane-sealevel/src/merkle_tree_hook.rs index 3778627b2a..eb8831d42b 100644 --- a/rust/main/chains/hyperlane-sealevel/src/merkle_tree_hook.rs +++ b/rust/main/chains/hyperlane-sealevel/src/merkle_tree_hook.rs @@ -1,11 +1,11 @@ -use std::{num::NonZeroU64, ops::RangeInclusive}; +use std::ops::RangeInclusive; use async_trait::async_trait; use derive_new::new; use hyperlane_core::{ accumulator::incremental::IncrementalMerkle, ChainCommunicationError, ChainResult, Checkpoint, HyperlaneChain, HyperlaneMessage, Indexed, Indexer, LogMeta, MerkleTreeHook, - MerkleTreeInsertion, SequenceAwareIndexer, + MerkleTreeInsertion, ReorgPeriod, SequenceAwareIndexer, }; use hyperlane_sealevel_mailbox::accounts::OutboxAccount; use solana_sdk::commitment_config::CommitmentConfig; @@ -17,7 +17,7 @@ use crate::{SealevelMailbox, SealevelMailboxIndexer}; impl MerkleTreeHook for SealevelMailbox { #[instrument(err, ret, skip(self))] #[allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue - async fn tree(&self, lag: Option) -> ChainResult { + async fn tree(&self, lag: Option<&ReorgPeriod>) -> ChainResult { assert!( lag.is_none(), "Sealevel does not support querying point-in-time" @@ -41,7 +41,7 @@ impl MerkleTreeHook for SealevelMailbox { #[instrument(err, ret, skip(self))] #[allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue - async fn latest_checkpoint(&self, lag: Option) -> ChainResult { + async fn latest_checkpoint(&self, lag: Option<&ReorgPeriod>) -> ChainResult { assert!( lag.is_none(), "Sealevel does not support querying point-in-time" @@ -70,7 +70,7 @@ impl MerkleTreeHook for SealevelMailbox { #[instrument(err, ret, skip(self))] #[allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue - async fn count(&self, _maybe_lag: Option) -> ChainResult { + async fn count(&self, _maybe_lag: Option<&ReorgPeriod>) -> ChainResult { let tree = self.tree(_maybe_lag).await?; tree.count() diff --git a/rust/main/hyperlane-core/src/traits/mailbox.rs b/rust/main/hyperlane-core/src/traits/mailbox.rs index d5e9081b65..0c6c80284e 100644 --- a/rust/main/hyperlane-core/src/traits/mailbox.rs +++ b/rust/main/hyperlane-core/src/traits/mailbox.rs @@ -1,12 +1,11 @@ use std::fmt::Debug; -use std::num::NonZeroU64; use async_trait::async_trait; use derive_new::new; use crate::{ traits::TxOutcome, utils::domain_hash, BatchItem, ChainCommunicationError, ChainResult, - HyperlaneContract, HyperlaneMessage, QueueOperation, TxCostEstimate, H256, U256, + HyperlaneContract, HyperlaneMessage, QueueOperation, ReorgPeriod, TxCostEstimate, H256, U256, }; /// Interface for the Mailbox chain contract. Allows abstraction over different @@ -22,7 +21,7 @@ pub trait Mailbox: HyperlaneContract + Send + Sync + Debug { /// /// - `lag` is how far behind the current block to query, if not specified /// it will query at the latest block. - async fn count(&self, lag: Option) -> ChainResult; + async fn count(&self, lag: Option<&ReorgPeriod>) -> ChainResult; /// Fetch the status of a message async fn delivered(&self, id: H256) -> ChainResult; diff --git a/rust/main/hyperlane-core/src/traits/merkle_tree_hook.rs b/rust/main/hyperlane-core/src/traits/merkle_tree_hook.rs index 35f3fa4efb..f06cd9258d 100644 --- a/rust/main/hyperlane-core/src/traits/merkle_tree_hook.rs +++ b/rust/main/hyperlane-core/src/traits/merkle_tree_hook.rs @@ -1,11 +1,11 @@ use std::fmt::Debug; -use std::num::NonZeroU64; use async_trait::async_trait; use auto_impl::auto_impl; use crate::{ accumulator::incremental::IncrementalMerkle, ChainResult, Checkpoint, HyperlaneContract, + ReorgPeriod, }; /// Interface for the MerkleTreeHook chain contract. Allows abstraction over different @@ -17,17 +17,17 @@ pub trait MerkleTreeHook: HyperlaneContract + Send + Sync + Debug { /// /// - `lag` is how far behind the current block to query, if not specified /// it will query at the latest block. - async fn tree(&self, lag: Option) -> ChainResult; + async fn tree(&self, lag: Option<&ReorgPeriod>) -> ChainResult; /// Gets the current leaf count of the merkle tree /// /// - `lag` is how far behind the current block to query, if not specified /// it will query at the latest block. - async fn count(&self, lag: Option) -> ChainResult; + async fn count(&self, lag: Option<&ReorgPeriod>) -> ChainResult; /// Get the latest checkpoint. /// /// - `lag` is how far behind the current block to query, if not specified /// it will query at the latest block. - async fn latest_checkpoint(&self, lag: Option) -> ChainResult; + async fn latest_checkpoint(&self, lag: Option<&ReorgPeriod>) -> ChainResult; } diff --git a/rust/main/hyperlane-test/src/mocks/mailbox.rs b/rust/main/hyperlane-test/src/mocks/mailbox.rs index dc09e1026f..44760d349f 100644 --- a/rust/main/hyperlane-test/src/mocks/mailbox.rs +++ b/rust/main/hyperlane-test/src/mocks/mailbox.rs @@ -1,7 +1,5 @@ #![allow(non_snake_case)] -use std::num::NonZeroU64; - use async_trait::async_trait; use mockall::*; @@ -28,11 +26,11 @@ mock! { nonce: usize, ) -> ChainResult> {} - pub fn _tree(&self, maybe_lag: Option) -> ChainResult {} + pub fn _tree<'a>(&self, maybe_lag: Option<&'a ReorgPeriod>) -> ChainResult {} - pub fn _count(&self, maybe_lag: Option) -> ChainResult {} + pub fn _count<'a>(&self, maybe_lag: Option<&'a ReorgPeriod>) -> ChainResult {} - pub fn _latest_checkpoint(&self, maybe_lag: Option) -> ChainResult {} + pub fn _latest_checkpoint<'a>(&self, maybe_lag: Option<&'a ReorgPeriod>) -> ChainResult {} pub fn _default_ism(&self) -> ChainResult {} pub fn _recipient_ism(&self, recipient: H256) -> ChainResult {} @@ -68,7 +66,7 @@ impl std::fmt::Debug for MockMailboxContract { #[async_trait] impl Mailbox for MockMailboxContract { - async fn count(&self, maybe_lag: Option) -> ChainResult { + async fn count(&self, maybe_lag: Option<&ReorgPeriod>) -> ChainResult { self._count(maybe_lag) } From 6257b597220eae2377ea27c306405986d8226bf3 Mon Sep 17 00:00:00 2001 From: Alexander Pastushenko Date: Thu, 3 Oct 2024 17:48:02 +0400 Subject: [PATCH 3/4] Update typescript SDK and CLI --- typescript/cli/src/config/chain.ts | 13 ++++++++++--- typescript/infra/config/registry.ts | 2 +- typescript/infra/src/config/agent/validator.ts | 4 ++-- typescript/sdk/src/metadata/chainMetadata.test.ts | 10 ++++++++++ typescript/sdk/src/metadata/chainMetadataTypes.ts | 11 +++++++---- 5 files changed, 30 insertions(+), 10 deletions(-) diff --git a/typescript/cli/src/config/chain.ts b/typescript/cli/src/config/chain.ts index 199e026c82..d3279a2029 100644 --- a/typescript/cli/src/config/chain.ts +++ b/typescript/cli/src/config/chain.ts @@ -168,6 +168,11 @@ async function addBlockOrGasConfig(metadata: ChainMetadata): Promise { } async function addBlockConfig(metadata: ChainMetadata): Promise { + const parseReorgPeriod = (value: string) => { + const parsed = parseInt(value, 10); + return isNaN(parsed) ? value : parsed; + }; + const wantBlockConfig = await confirm({ message: 'Do you want to add block config for this chain', }); @@ -179,8 +184,10 @@ async function addBlockConfig(metadata: ChainMetadata): Promise { }); const blockReorgPeriod = await input({ message: - 'Enter no. of blocks before a transaction has a near-zero chance of reverting (0-500):', - validate: (value) => parseInt(value) >= 0 && parseInt(value) <= 500, + 'Enter no. of blocks before a transaction has a near-zero chance of reverting (0-500) or block tag:', + validate: (value) => + isNaN(parseInt(value)) || + (parseInt(value) >= 0 && parseInt(value) <= 500), }); const blockTimeEstimate = await input({ message: 'Enter the rough estimate of time per block in seconds (0-20):', @@ -188,7 +195,7 @@ async function addBlockConfig(metadata: ChainMetadata): Promise { }); metadata.blocks = { confirmations: parseInt(blockConfirmation, 10), - reorgPeriod: parseInt(blockReorgPeriod, 10), + reorgPeriod: parseReorgPeriod(blockReorgPeriod), estimateBlockTime: parseInt(blockTimeEstimate, 10), }; } diff --git a/typescript/infra/config/registry.ts b/typescript/infra/config/registry.ts index 51ac877295..4b72c9f5be 100644 --- a/typescript/infra/config/registry.ts +++ b/typescript/infra/config/registry.ts @@ -78,7 +78,7 @@ export function getDomainId(chainName: ChainName): number { return resolveDomainId(chain); } -export function getReorgPeriod(chainName: ChainName): number { +export function getReorgPeriod(chainName: ChainName): string | number { const chain = getChain(chainName); return resolveReorgPeriod(chain); } diff --git a/typescript/infra/src/config/agent/validator.ts b/typescript/infra/src/config/agent/validator.ts index 05293abfa4..e18220017c 100644 --- a/typescript/infra/src/config/agent/validator.ts +++ b/typescript/infra/src/config/agent/validator.ts @@ -26,8 +26,8 @@ export type ValidatorBaseChainConfigMap = ChainMap; export interface ValidatorBaseChainConfig { // How frequently to check for new checkpoints interval: number; - // The reorg_period in blocks; overrides chain metadata - reorgPeriod: number; + // The reorg_period in blocks or block tag; overrides chain metadata + reorgPeriod: string | number; // Individual validator agents validators: Array; } diff --git a/typescript/sdk/src/metadata/chainMetadata.test.ts b/typescript/sdk/src/metadata/chainMetadata.test.ts index 773eb9c033..65a980f834 100644 --- a/typescript/sdk/src/metadata/chainMetadata.test.ts +++ b/typescript/sdk/src/metadata/chainMetadata.test.ts @@ -62,6 +62,16 @@ describe('ChainMetadataSchema', () => { grpcUrls: [], }), ).to.eq(true); + + expect( + isValidChainMetadata({ + ...minimalSchema, + blocks: { + confirmations: 1, + reorgPeriod: 'finalized', + }, + }), + ).to.eq(true); }); it('Rejects invalid schemas', () => { diff --git a/typescript/sdk/src/metadata/chainMetadataTypes.ts b/typescript/sdk/src/metadata/chainMetadataTypes.ts index b83b3f8c6e..177de40c8b 100644 --- a/typescript/sdk/src/metadata/chainMetadataTypes.ts +++ b/typescript/sdk/src/metadata/chainMetadataTypes.ts @@ -117,9 +117,12 @@ export const ChainMetadataSchemaObject = z.object({ confirmations: ZUint.describe( 'Number of blocks to wait before considering a transaction confirmed.', ), - reorgPeriod: ZUint.optional().describe( - 'Number of blocks before a transaction has a near-zero chance of reverting.', - ), + reorgPeriod: z + .union([ZUint, z.string()]) + .optional() + .describe( + 'Number of blocks before a transaction has a near-zero chance of reverting or block tag.', + ), estimateBlockTime: z .number() .positive() @@ -368,7 +371,7 @@ export function getChainIdNumber(chainMetadata: ChainMetadata): number { else throw new Error('ChainId is not a number, chain may be of Cosmos type'); } -export function getReorgPeriod(chainMetadata: ChainMetadata): number { +export function getReorgPeriod(chainMetadata: ChainMetadata): string | number { if (chainMetadata.blocks?.reorgPeriod !== undefined) return chainMetadata.blocks.reorgPeriod; else throw new Error('Chain has no reorg period'); From 9a5640b089c41e81ed88e2edb773940ef74c4ae1 Mon Sep 17 00:00:00 2001 From: Alexander Pastushenko Date: Fri, 4 Oct 2024 15:16:56 +0400 Subject: [PATCH 4/4] Fix reorgPeriod parsing --- rust/main/hyperlane-core/src/chain.rs | 64 +++++++++++++++++++++++++-- 1 file changed, 60 insertions(+), 4 deletions(-) diff --git a/rust/main/hyperlane-core/src/chain.rs b/rust/main/hyperlane-core/src/chain.rs index 136a916c4d..ccad538d24 100644 --- a/rust/main/hyperlane-core/src/chain.rs +++ b/rust/main/hyperlane-core/src/chain.rs @@ -9,7 +9,7 @@ use derive_new::new; use eyre::{eyre, Report}; use num_derive::FromPrimitive; use num_traits::FromPrimitive; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Deserializer, Serialize}; #[cfg(feature = "strum")] use strum::{EnumIter, EnumString, IntoStaticStr}; @@ -40,8 +40,7 @@ impl<'a> std::fmt::Display for ContractLocator<'a> { } } -#[derive(Debug, Clone, Deserialize)] -#[serde(untagged)] +#[derive(Debug, Clone, PartialEq)] pub enum ReorgPeriod { Blocks(u32), Tag(String), @@ -63,6 +62,45 @@ impl Default for ReorgPeriod { } } +impl<'de> Deserialize<'de> for ReorgPeriod { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + use serde::de; + + struct ReorgPeriodVisitor; + + impl<'de> de::Visitor<'de> for ReorgPeriodVisitor { + type Value = ReorgPeriod; + + fn expecting(&self, f: &mut Formatter) -> std::fmt::Result { + f.write_str("reorgPeriod as a number or string") + } + + fn visit_u64(self, v: u64) -> Result + where + E: de::Error, + { + let v = v.try_into().map_err(de::Error::custom)?; + Ok(ReorgPeriod::Blocks(v)) + } + + fn visit_str(self, v: &str) -> Result + where + E: de::Error, + { + match v.parse::() { + Ok(v) => Ok(ReorgPeriod::Blocks(v)), + Err(_) => Ok(ReorgPeriod::Tag(v.to_string())), + } + } + } + + deserializer.deserialize_any(ReorgPeriodVisitor) + } +} + /// All domains supported by Hyperlane. #[derive(FromPrimitive, PartialEq, Eq, Debug, Clone, Copy, Hash, Serialize)] #[cfg_attr( @@ -520,7 +558,7 @@ impl HyperlaneDomain { mod tests { use std::str::FromStr; - use crate::KnownHyperlaneDomain; + use crate::{KnownHyperlaneDomain, ReorgPeriod}; #[test] fn domain_strings() { @@ -573,4 +611,22 @@ mod tests { ); assert!("foo".parse::().is_err()); } + + #[test] + fn parse_reorg_period() { + assert_eq!( + serde_json::from_value::(12.into()).unwrap(), + ReorgPeriod::Blocks(12) + ); + + assert_eq!( + serde_json::from_value::("12".into()).unwrap(), + ReorgPeriod::Blocks(12) + ); + + assert_eq!( + serde_json::from_value::("finalized".into()).unwrap(), + ReorgPeriod::Tag("finalized".into()) + ); + } }