diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 2ca08007e4..3671ef9e52 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -428,20 +428,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" -[[package]] -name = "backoff" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62ddb9cb1ec0a098ad4bbf9344d0713fa193ae1a80af55febcff2627b6a00c1" -dependencies = [ - "futures-core", - "getrandom 0.2.10", - "instant", - "pin-project-lite", - "rand 0.8.5", - "tokio", -] - [[package]] name = "backtrace" version = "0.3.68" @@ -6063,7 +6049,6 @@ name = "relayer" version = "0.1.0" dependencies = [ "async-trait", - "backoff", "config", "convert_case 0.6.0", "derive-new", diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 67a8597b60..3608f3f1f8 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -52,7 +52,6 @@ Inflector = "0.11.4" anyhow = "1.0" async-trait = "0.1" auto_impl = "1.0" -backoff = { version = "0.4.0", features = ["tokio"] } backtrace = "0.3" base64 = "0.21.2" bincode = "1.3" diff --git a/rust/agents/relayer/Cargo.toml b/rust/agents/relayer/Cargo.toml index 406bf96925..7eb5b83fe6 100644 --- a/rust/agents/relayer/Cargo.toml +++ b/rust/agents/relayer/Cargo.toml @@ -11,7 +11,6 @@ version.workspace = true [dependencies] async-trait.workspace = true -backoff.workspace = true config.workspace = true convert_case.workspace = true derive-new.workspace = true diff --git a/rust/agents/relayer/src/merkle_tree/builder.rs b/rust/agents/relayer/src/merkle_tree/builder.rs index 56930cf6fe..68c046f263 100644 --- a/rust/agents/relayer/src/merkle_tree/builder.rs +++ b/rust/agents/relayer/src/merkle_tree/builder.rs @@ -59,9 +59,6 @@ pub enum MerkleTreeBuilderError { /// DB Error #[error("{0}")] DbError(#[from] DbError), - /// Some other error occured. - #[error("Failed to build the merkle tree: {0}")] - Other(String), } impl MerkleTreeBuilder { @@ -81,16 +78,19 @@ impl MerkleTreeBuilder { message_nonce: u32, root_index: u32, ) -> Result, MerkleTreeBuilderError> { - let Some(leaf_index) = self - .db + self.db .retrieve_message_id_by_nonce(&message_nonce)? - .and_then(|message_id| self.db.retrieve_merkle_leaf_index_by_message_id(&message_id).ok().flatten()) - else { - return Ok(None); - }; - self.prover - .prove_against_previous(leaf_index as usize, root_index as usize) - .map(Option::from) + .and_then(|message_id| { + self.db + .retrieve_merkle_leaf_index_by_message_id(&message_id) + .ok() + .flatten() + }) + .map(|leaf_index| { + self.prover + .prove_against_previous(leaf_index as usize, root_index as usize) + }) + .transpose() .map_err(Into::into) } diff --git a/rust/agents/relayer/src/msg/metadata/base.rs b/rust/agents/relayer/src/msg/metadata/base.rs index cce56efd15..7fea6ea32f 100644 --- a/rust/agents/relayer/src/msg/metadata/base.rs +++ b/rust/agents/relayer/src/msg/metadata/base.rs @@ -1,8 +1,6 @@ use std::{collections::HashMap, fmt::Debug, str::FromStr, sync::Arc}; use async_trait::async_trait; -use backoff::Error as BackoffError; -use backoff::{future::retry, ExponentialBackoff}; use derive_new::new; use eyre::{Context, Result}; use hyperlane_base::db::HyperlaneRocksDB; @@ -19,7 +17,7 @@ use tokio::sync::RwLock; use tracing::{debug, info, instrument, warn}; use crate::{ - merkle_tree::builder::{MerkleTreeBuilder, MerkleTreeBuilderError}, + merkle_tree::builder::MerkleTreeBuilder, msg::metadata::{ multisig::{ LegacyMultisigMetadataBuilder, MerkleRootMultisigMetadataBuilder, @@ -102,15 +100,6 @@ impl MetadataBuilder for BaseMetadataBuilder { } } -fn constant_backoff() -> ExponentialBackoff { - ExponentialBackoff { - initial_interval: std::time::Duration::from_secs(1), - multiplier: 1.0, - max_elapsed_time: None, - ..ExponentialBackoff::default() - } -} - impl BaseMetadataBuilder { pub fn domain(&self) -> &HyperlaneDomain { &self.destination_chain_setup.domain @@ -128,33 +117,25 @@ impl BaseMetadataBuilder { pub async fn get_proof(&self, nonce: u32, checkpoint: Checkpoint) -> Result> { const CTX: &str = "When fetching message proof"; - let proof = retry(constant_backoff(), || async { - self.origin_prover_sync - .read() - .await - .get_proof(nonce, checkpoint.index) - .context(CTX) - // If no proof is found, `get_proof(...)` returns `Ok(None)`, - // so errors should break the retry loop. - .map_err(BackoffError::permanent)? - .ok_or(MerkleTreeBuilderError::Other("No proof found in DB".into())) - .context(CTX) - // Transient errors are retried - .map_err(BackoffError::transient) - }) - .await?; - // checkpoint may be fraudulent if the root does not - // match the canonical root at the checkpoint's index - if proof.root() != checkpoint.root { - info!( - ?checkpoint, - canonical_root = ?proof.root(), - "Could not fetch metadata: checkpoint root does not match canonical root from merkle proof" - ); - Ok(None) - } else { - Ok(Some(proof)) - } + let proof = self.origin_prover_sync + .read() + .await + .get_proof(nonce, checkpoint.index) + .context(CTX)? + .and_then(|proof| { + // checkpoint may be fraudulent if the root does not + // match the canonical root at the checkpoint's index + if proof.root() == checkpoint.root { + return Some(proof) + } + info!( + ?checkpoint, + canonical_root = ?proof.root(), + "Could not fetch metadata: checkpoint root does not match canonical root from merkle proof" + ); + None + }); + Ok(proof) } pub async fn highest_known_nonce(&self) -> Option { diff --git a/rust/agents/relayer/src/msg/metadata/multisig/base.rs b/rust/agents/relayer/src/msg/metadata/multisig/base.rs index ad671ae341..a34e4b57b8 100644 --- a/rust/agents/relayer/src/msg/metadata/multisig/base.rs +++ b/rust/agents/relayer/src/msg/metadata/multisig/base.rs @@ -19,19 +19,19 @@ use crate::msg::metadata::MetadataBuilder; pub struct MultisigMetadata { checkpoint: Checkpoint, signatures: Vec, - merkle_leaf_id: Option, + merkle_leaf_index: Option, message_id: Option, proof: Option, } #[derive(Debug, Display, PartialEq, Eq, Clone)] pub enum MetadataToken { - MerkleRoot, + CheckpointMerkleRoot, CheckpointIndex, - CheckpointMerkleTree, + CheckpointMerkleTreeHook, MessageId, MerkleProof, - MerkleIndex, + MessageMerkleLeafIndex, Threshold, Signatures, Validators, @@ -57,16 +57,18 @@ pub trait MultisigIsmMetadataBuilder: AsRef + Send + Sync { ) -> Result> { let build_token = |token: &MetadataToken| -> Result> { match token { - MetadataToken::MerkleRoot => Ok(metadata.checkpoint.root.to_fixed_bytes().into()), - MetadataToken::MerkleIndex => Ok(metadata - .merkle_leaf_id + MetadataToken::CheckpointMerkleRoot => { + Ok(metadata.checkpoint.root.to_fixed_bytes().into()) + } + MetadataToken::MessageMerkleLeafIndex => Ok(metadata + .merkle_leaf_index .ok_or(eyre::eyre!("Failed to fetch metadata"))? .to_be_bytes() .into()), MetadataToken::CheckpointIndex => { Ok(metadata.checkpoint.index.to_be_bytes().into()) } - MetadataToken::CheckpointMerkleTree => Ok(metadata + MetadataToken::CheckpointMerkleTreeHook => Ok(metadata .checkpoint .merkle_tree_hook_address .to_fixed_bytes() diff --git a/rust/agents/relayer/src/msg/metadata/multisig/legacy_multisig.rs b/rust/agents/relayer/src/msg/metadata/multisig/legacy_multisig.rs index 98a9c4bcb1..b5eaec9d4c 100644 --- a/rust/agents/relayer/src/msg/metadata/multisig/legacy_multisig.rs +++ b/rust/agents/relayer/src/msg/metadata/multisig/legacy_multisig.rs @@ -6,7 +6,7 @@ use derive_new::new; use eyre::{Context, Result}; use hyperlane_base::MultisigCheckpointSyncer; -use hyperlane_core::{HyperlaneMessage, H256}; +use hyperlane_core::{unwrap_or_none_result, HyperlaneMessage, H256}; use crate::msg::metadata::BaseMetadataBuilder; @@ -19,9 +19,9 @@ pub struct LegacyMultisigMetadataBuilder(BaseMetadataBuilder); impl MultisigIsmMetadataBuilder for LegacyMultisigMetadataBuilder { fn token_layout(&self) -> Vec { vec![ - MetadataToken::MerkleRoot, + MetadataToken::CheckpointMerkleRoot, MetadataToken::CheckpointIndex, - MetadataToken::CheckpointMerkleTree, + MetadataToken::CheckpointMerkleTreeHook, MetadataToken::MerkleProof, MetadataToken::Threshold, MetadataToken::Signatures, @@ -37,31 +37,25 @@ impl MultisigIsmMetadataBuilder for LegacyMultisigMetadataBuilder { checkpoint_syncer: &MultisigCheckpointSyncer, ) -> Result> { const CTX: &str = "When fetching LegacyMultisig metadata"; - let Some(highest_nonce) = self.highest_known_nonce().await - else { - return Ok(None); - }; - let Some(quorum_checkpoint) = checkpoint_syncer - .legacy_fetch_checkpoint_in_range( - validators, - threshold as usize, - message.nonce, - highest_nonce, - ) - .await - .context(CTX)? - else { - return Ok(None); - }; - - let Some(proof) = self - .get_proof(message.nonce, quorum_checkpoint.checkpoint) - .await - .context(CTX)? - else { - return Ok(None); - }; - + unwrap_or_none_result!(highest_nonce, self.highest_known_nonce().await); + unwrap_or_none_result!( + quorum_checkpoint, + checkpoint_syncer + .legacy_fetch_checkpoint_in_range( + validators, + threshold as usize, + message.nonce, + highest_nonce, + ) + .await + .context(CTX)? + ); + unwrap_or_none_result!( + proof, + self.get_proof(message.nonce, quorum_checkpoint.checkpoint) + .await + .context(CTX)? + ); Ok(Some(MultisigMetadata::new( quorum_checkpoint.checkpoint, quorum_checkpoint.signatures, diff --git a/rust/agents/relayer/src/msg/metadata/multisig/merkle_root_multisig.rs b/rust/agents/relayer/src/msg/metadata/multisig/merkle_root_multisig.rs index f304fcff1e..334971f648 100644 --- a/rust/agents/relayer/src/msg/metadata/multisig/merkle_root_multisig.rs +++ b/rust/agents/relayer/src/msg/metadata/multisig/merkle_root_multisig.rs @@ -6,7 +6,7 @@ use derive_new::new; use eyre::{Context, Result}; use hyperlane_base::MultisigCheckpointSyncer; -use hyperlane_core::{HyperlaneMessage, H256}; +use hyperlane_core::{unwrap_or_none_result, HyperlaneMessage, H256}; use crate::msg::metadata::BaseMetadataBuilder; @@ -18,8 +18,8 @@ pub struct MerkleRootMultisigMetadataBuilder(BaseMetadataBuilder); impl MultisigIsmMetadataBuilder for MerkleRootMultisigMetadataBuilder { fn token_layout(&self) -> Vec { vec![ - MetadataToken::CheckpointMerkleTree, - MetadataToken::MerkleIndex, + MetadataToken::CheckpointMerkleTreeHook, + MetadataToken::MessageMerkleLeafIndex, MetadataToken::MessageId, MetadataToken::MerkleProof, MetadataToken::CheckpointIndex, @@ -35,35 +35,35 @@ impl MultisigIsmMetadataBuilder for MerkleRootMultisigMetadataBuilder { checkpoint_syncer: &MultisigCheckpointSyncer, ) -> Result> { const CTX: &str = "When fetching MerkleRootMultisig metadata"; - let Some(highest_nonce) = self.highest_known_nonce().await - else { - return Ok(None); - }; - let Some(quorum_checkpoint) = checkpoint_syncer - .fetch_checkpoint_in_range(validators, threshold as usize, message.nonce, highest_nonce) - .await - .context(CTX)? - else { - return Ok(None); - }; - - let Some(proof) = self - .get_proof(message.nonce, quorum_checkpoint.checkpoint.checkpoint) - .await - .context(CTX)? - else { - return Ok(None); - }; - - let merkle_leaf_id = self - .get_merkle_leaf_id_by_message_id(message.id()) - .await - .context(CTX)?; - + unwrap_or_none_result!(highest_nonce, self.highest_known_nonce().await); + unwrap_or_none_result!( + quorum_checkpoint, + checkpoint_syncer + .fetch_checkpoint_in_range( + validators, + threshold as usize, + message.nonce, + highest_nonce + ) + .await + .context(CTX)? + ); + unwrap_or_none_result!( + proof, + self.get_proof(message.nonce, quorum_checkpoint.checkpoint.checkpoint) + .await + .context(CTX)? + ); + unwrap_or_none_result!( + merkle_leaf_id, + self.get_merkle_leaf_id_by_message_id(message.id()) + .await + .context(CTX)? + ); Ok(Some(MultisigMetadata::new( quorum_checkpoint.checkpoint.checkpoint, quorum_checkpoint.signatures, - merkle_leaf_id, + Some(merkle_leaf_id), Some(quorum_checkpoint.checkpoint.message_id), Some(proof), ))) diff --git a/rust/agents/relayer/src/msg/metadata/multisig/message_id_multisig.rs b/rust/agents/relayer/src/msg/metadata/multisig/message_id_multisig.rs index b71551f9b1..30b4fe1dad 100644 --- a/rust/agents/relayer/src/msg/metadata/multisig/message_id_multisig.rs +++ b/rust/agents/relayer/src/msg/metadata/multisig/message_id_multisig.rs @@ -6,7 +6,7 @@ use derive_new::new; use eyre::{Context, Result}; use hyperlane_base::MultisigCheckpointSyncer; -use hyperlane_core::{HyperlaneMessage, H256}; +use hyperlane_core::{unwrap_or_none_result, HyperlaneMessage, H256}; use tracing::warn; use crate::msg::metadata::BaseMetadataBuilder; @@ -20,9 +20,9 @@ pub struct MessageIdMultisigMetadataBuilder(BaseMetadataBuilder); impl MultisigIsmMetadataBuilder for MessageIdMultisigMetadataBuilder { fn token_layout(&self) -> Vec { vec![ - MetadataToken::CheckpointMerkleTree, - MetadataToken::MerkleRoot, - MetadataToken::MerkleIndex, + MetadataToken::CheckpointMerkleTreeHook, + MetadataToken::CheckpointMerkleRoot, + MetadataToken::MessageMerkleLeafIndex, MetadataToken::Signatures, ] } @@ -35,13 +35,13 @@ impl MultisigIsmMetadataBuilder for MessageIdMultisigMetadataBuilder { checkpoint_syncer: &MultisigCheckpointSyncer, ) -> Result> { const CTX: &str = "When fetching MessageIdMultisig metadata"; - let Some(quorum_checkpoint) = checkpoint_syncer - .fetch_checkpoint(validators, threshold as usize, message.nonce) - .await - .context(CTX)? - else { - return Ok(None); - }; + unwrap_or_none_result!( + quorum_checkpoint, + checkpoint_syncer + .fetch_checkpoint(validators, threshold as usize, message.nonce) + .await + .context(CTX)? + ); if quorum_checkpoint.checkpoint.message_id != message.id() { warn!( @@ -51,15 +51,18 @@ impl MultisigIsmMetadataBuilder for MessageIdMultisigMetadataBuilder { ); return Ok(None); } - let merkle_leaf_id = self - .get_merkle_leaf_id_by_message_id(message.id()) - .await - .context(CTX)?; + + unwrap_or_none_result!( + merkle_leaf_id, + self.get_merkle_leaf_id_by_message_id(message.id()) + .await + .context(CTX)? + ); Ok(Some(MultisigMetadata::new( quorum_checkpoint.checkpoint.checkpoint, quorum_checkpoint.signatures, - merkle_leaf_id, + Some(merkle_leaf_id), None, None, ))) diff --git a/rust/agents/scraper/src/chain_scraper/mod.rs b/rust/agents/scraper/src/chain_scraper/mod.rs index fa54957830..09294905a6 100644 --- a/rust/agents/scraper/src/chain_scraper/mod.rs +++ b/rust/agents/scraper/src/chain_scraper/mod.rs @@ -7,9 +7,9 @@ use async_trait::async_trait; use eyre::Result; use hyperlane_base::settings::IndexSettings; use hyperlane_core::{ - BlockInfo, Delivery, HyperlaneDomain, HyperlaneLogStore, HyperlaneMessage, - HyperlaneMessageStore, HyperlaneProvider, HyperlaneWatermarkedLogStore, InterchainGasPayment, - LogMeta, H256, + unwrap_or_none_result, BlockInfo, Delivery, HyperlaneDomain, HyperlaneLogStore, + HyperlaneMessage, HyperlaneMessageStore, HyperlaneProvider, HyperlaneWatermarkedLogStore, + InterchainGasPayment, LogMeta, H256, }; use itertools::Itertools; use tracing::trace; @@ -383,18 +383,13 @@ impl HyperlaneMessageStore for HyperlaneSqlDb { /// Retrieves the block number at which the message with the provided nonce /// was dispatched. async fn retrieve_dispatched_block_number(&self, nonce: u32) -> Result> { - let Some(tx_id) = self - .db - .retrieve_dispatched_tx_id(self.domain().id(), &self.mailbox_address, nonce) - .await? - else { - return Ok(None); - }; - - let Some(block_id) = self.db.retrieve_block_id(tx_id).await? else { - return Ok(None); - }; - + unwrap_or_none_result!( + tx_id, + self.db + .retrieve_dispatched_tx_id(self.domain().id(), &self.mailbox_address, nonce) + .await? + ); + unwrap_or_none_result!(block_id, self.db.retrieve_block_id(tx_id).await?); Ok(self.db.retrieve_block_number(block_id).await?) } } diff --git a/rust/chains/hyperlane-ethereum/src/mailbox.rs b/rust/chains/hyperlane-ethereum/src/mailbox.rs index 44139b9595..390c620302 100644 --- a/rust/chains/hyperlane-ethereum/src/mailbox.rs +++ b/rust/chains/hyperlane-ethereum/src/mailbox.rs @@ -10,7 +10,6 @@ use async_trait::async_trait; use ethers::abi::AbiEncode; use ethers::prelude::Middleware; use ethers_contract::builders::ContractCall; -use ethers_core::types::BlockNumber; use tracing::instrument; use hyperlane_core::{ @@ -23,7 +22,7 @@ use hyperlane_core::{ use crate::contracts::arbitrum_node_interface::ArbitrumNodeInterface; use crate::contracts::i_mailbox::{IMailbox as EthereumMailboxInternal, ProcessCall, IMAILBOX_ABI}; use crate::trait_builder::BuildableWithProvider; -use crate::tx::{fill_tx_gas_params, report_tx}; +use crate::tx::{call_with_lag, fill_tx_gas_params, report_tx}; use crate::EthereumProvider; impl std::fmt::Display for EthereumMailboxInternal @@ -301,21 +300,8 @@ where { #[instrument(skip(self))] async fn count(&self, maybe_lag: Option) -> ChainResult { - let lag = maybe_lag.map(|v| v.get()).unwrap_or(0).into(); - let fixed_block_number: BlockNumber = self - .provider - .get_block_number() - .await - .map_err(ChainCommunicationError::from_other)? - .saturating_sub(lag) - .into(); - - let nonce = self - .contract - .nonce() - .block(fixed_block_number) - .call() - .await?; + let call = call_with_lag(self.contract.nonce(), &self.provider, maybe_lag).await?; + let nonce = call.call().await?; Ok(nonce) } diff --git a/rust/chains/hyperlane-ethereum/src/merkle_tree_hook.rs b/rust/chains/hyperlane-ethereum/src/merkle_tree_hook.rs index 28bc406a0e..cddd91c2e8 100644 --- a/rust/chains/hyperlane-ethereum/src/merkle_tree_hook.rs +++ b/rust/chains/hyperlane-ethereum/src/merkle_tree_hook.rs @@ -5,7 +5,6 @@ use std::sync::Arc; use async_trait::async_trait; use ethers::prelude::Middleware; -use ethers_core::types::BlockNumber; use hyperlane_core::accumulator::incremental::IncrementalMerkle; use tracing::instrument; @@ -15,10 +14,28 @@ use hyperlane_core::{ MerkleTreeInsertion, SequenceIndexer, H256, }; -use crate::contracts::merkle_tree_hook::MerkleTreeHook as MerkleTreeHookContract; +use crate::contracts::merkle_tree_hook::{MerkleTreeHook as MerkleTreeHookContract, Tree}; use crate::trait_builder::BuildableWithProvider; +use crate::tx::call_with_lag; use crate::EthereumProvider; +// We don't need the reverse of this impl, so it's ok to disable the clippy lint +#[allow(clippy::from_over_into)] +impl Into for Tree { + fn into(self) -> IncrementalMerkle { + let branch = self + .branch + .iter() + .map(|v| v.into()) + .collect::>() + // we're iterating over a fixed-size array and want to collect into a + // fixed-size array of the same size (32), so this is safe + .try_into() + .unwrap(); + IncrementalMerkle::new(branch, self.count.as_usize()) + } +} + pub struct MerkleTreeHookBuilder {} #[async_trait] @@ -203,22 +220,10 @@ where { #[instrument(skip(self))] async fn latest_checkpoint(&self, maybe_lag: Option) -> ChainResult { - let lag = maybe_lag.map(|v| v.get()).unwrap_or(0).into(); + let call = + call_with_lag(self.contract.latest_checkpoint(), &self.provider, maybe_lag).await?; - let fixed_block_number: BlockNumber = self - .provider - .get_block_number() - .await - .map_err(ChainCommunicationError::from_other)? - .saturating_sub(lag) - .into(); - - let (root, index) = self - .contract - .latest_checkpoint() - .block(fixed_block_number) - .call() - .await?; + let (root, index) = call.call().await?; Ok(Checkpoint { merkle_tree_hook_address: self.address(), mailbox_domain: self.domain.id(), @@ -230,53 +235,15 @@ where #[instrument(skip(self))] #[allow(clippy::needless_range_loop)] async fn tree(&self, maybe_lag: Option) -> ChainResult { - let lag = maybe_lag.map(|v| v.get()).unwrap_or(0).into(); - - let fixed_block_number: BlockNumber = self - .provider - .get_block_number() - .await - .map_err(ChainCommunicationError::from_other)? - .saturating_sub(lag) - .into(); + let call = call_with_lag(self.contract.tree(), &self.provider, maybe_lag).await?; - // TODO: implement From for IncrementalMerkle - let raw_tree = self - .contract - .tree() - .block(fixed_block_number) - .call() - .await?; - let branch = raw_tree - .branch - .iter() - .map(|v| v.into()) - .collect::>() - .try_into() - .unwrap(); - - let tree = IncrementalMerkle::new(branch, raw_tree.count.as_usize()); - - Ok(tree) + Ok(call.call().await?.into()) } #[instrument(skip(self))] async fn count(&self, maybe_lag: Option) -> ChainResult { - let lag = maybe_lag.map(|v| v.get()).unwrap_or(0).into(); - let fixed_block_number: BlockNumber = self - .provider - .get_block_number() - .await - .map_err(ChainCommunicationError::from_other)? - .saturating_sub(lag) - .into(); - - let count = self - .contract - .count() - .block(fixed_block_number) - .call() - .await?; + let call = call_with_lag(self.contract.count(), &self.provider, maybe_lag).await?; + let count = call.call().await?; Ok(count) } } diff --git a/rust/chains/hyperlane-ethereum/src/tx.rs b/rust/chains/hyperlane-ethereum/src/tx.rs index dbfb47a09b..bb295346e1 100644 --- a/rust/chains/hyperlane-ethereum/src/tx.rs +++ b/rust/chains/hyperlane-ethereum/src/tx.rs @@ -1,3 +1,4 @@ +use std::num::NonZeroU64; use std::sync::Arc; use std::time::Duration; @@ -5,6 +6,7 @@ use ethers::abi::Detokenize; use ethers::prelude::{NameOrAddress, TransactionReceipt}; use ethers::types::Eip1559TransactionRequest; use ethers_contract::builders::ContractCall; +use ethers_core::types::BlockNumber; use tracing::{error, info}; use hyperlane_core::utils::fmt_bytes; @@ -117,3 +119,25 @@ where eip_1559_tx.tx = ethers::types::transaction::eip2718::TypedTransaction::Eip1559(request); Ok(eip_1559_tx.gas(gas_limit)) } + +pub(crate) async fn call_with_lag( + call: ethers::contract::builders::ContractCall, + provider: &M, + maybe_lag: Option, +) -> 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)) + } else { + Ok(call) + } +} diff --git a/rust/hyperlane-base/src/contract_sync/cursor.rs b/rust/hyperlane-base/src/contract_sync/cursor.rs index b66b9915a5..cdbbffaf95 100644 --- a/rust/hyperlane-base/src/contract_sync/cursor.rs +++ b/rust/hyperlane-base/src/contract_sync/cursor.rs @@ -10,7 +10,7 @@ use async_trait::async_trait; use derive_new::new; use eyre::Result; use tokio::time::sleep; -use tracing::{debug, info, warn}; +use tracing::{debug, warn}; use hyperlane_core::{ ChainCommunicationError, ChainResult, ContractSyncCursor, CursorAction, HyperlaneMessage, @@ -225,11 +225,11 @@ impl ForwardMessageSyncCursor { .retrieve_dispatched_block_number(self.cursor.sync_state.next_sequence) .await { - info!(next_block = block_number, "Fast forwarding next block"); + debug!(next_block = block_number, "Fast forwarding next block"); // It's possible that eth_getLogs dropped logs from this block, therefore we cannot do block_number + 1. self.cursor.sync_state.next_block = block_number; } - info!( + debug!( next_nonce = self.cursor.sync_state.next_sequence + 1, "Fast forwarding next nonce" ); diff --git a/rust/hyperlane-base/src/settings/chains.rs b/rust/hyperlane-base/src/settings/chains.rs index 8e5a5fb318..b85cdd18d4 100644 --- a/rust/hyperlane-base/src/settings/chains.rs +++ b/rust/hyperlane-base/src/settings/chains.rs @@ -126,7 +126,6 @@ impl ChainConf { self.build_ethereum(conf, &locator, metrics, h_eth::MailboxBuilder {}) .await } - ChainConnectionConf::Fuel(conf) => { let wallet = self.fuel_signer().await.context(ctx)?; hyperlane_fuel::FuelMailbox::new(conf, locator, wallet) diff --git a/rust/hyperlane-base/src/settings/loader/arguments.rs b/rust/hyperlane-base/src/settings/loader/arguments.rs index 5cf1aeacf0..eedbb476de 100644 --- a/rust/hyperlane-base/src/settings/loader/arguments.rs +++ b/rust/hyperlane-base/src/settings/loader/arguments.rs @@ -1,6 +1,7 @@ use std::ffi::{OsStr, OsString}; use config::{ConfigError, Map, Source, Value, ValueKind}; +use hyperlane_core::unwrap_or_none_result; use itertools::Itertools; /// A source for loading configuration from command line arguments. @@ -154,9 +155,7 @@ impl Iterator for ArgumentParser { impl ArgumentParser { #[inline(never)] fn find_next_kv_pair(&mut self) -> Result, Error> { - let Some(idx) = self.index_of_next_key() else { - return Ok(None); - }; + unwrap_or_none_result!(idx, self.index_of_next_key()); // full term without leading '--' let term = &os_to_str(&self.0[idx])?[2..]; if term.is_empty() { diff --git a/rust/hyperlane-core/src/types/checkpoint.rs b/rust/hyperlane-core/src/types/checkpoint.rs index 3f3eb294d9..35fe34ce2a 100644 --- a/rust/hyperlane-core/src/types/checkpoint.rs +++ b/rust/hyperlane-core/src/types/checkpoint.rs @@ -9,9 +9,9 @@ use crate::{utils::domain_hash, Signable, Signature, SignedType, H160, H256}; /// An Hyperlane checkpoint #[derive(Copy, Clone, Eq, PartialEq, Serialize, Deserialize, Debug)] pub struct Checkpoint { - /// The mailbox address + /// The merkle tree hook address pub merkle_tree_hook_address: H256, - /// The mailbox chain + /// The mailbox / merkle tree hook domain pub mailbox_domain: u32, /// The checkpointed root pub root: H256, diff --git a/rust/hyperlane-core/src/utils.rs b/rust/hyperlane-core/src/utils.rs index 4edeffeed6..7fdefe4af6 100644 --- a/rust/hyperlane-core/src/utils.rs +++ b/rust/hyperlane-core/src/utils.rs @@ -225,4 +225,28 @@ macro_rules! many_to_one { } } +/// Unwrap an expression that returns an `Option`, and return `Ok(None)` if it is `None`. +/// Otherwise, assign the value to the given variable name. +/// We use the pattern of returning `Ok(None)` a lot because of our retry logic, +/// and the goal of this macro is to reduce the boilerplate. +/// ```ignore +/// // before using the macro: +/// let Some(idx) = self.index_of_next_key() +/// else { +/// return Ok(None); +/// }; +/// // after: +/// unwrap_or_none_result!(idx, self.index_of_next_key()); +/// ``` +#[macro_export] +macro_rules! unwrap_or_none_result { + ($variable_name:ident, $e:expr $(, $else_e:expr)?) => { + let Some($variable_name) = $e + else { + $($else_e)? + return Ok(None); + }; + }; +} + pub(crate) use many_to_one; diff --git a/rust/sealevel/environments/local-e2e/warp-routes/testwarproute/program-ids.json b/rust/sealevel/environments/local-e2e/warp-routes/testwarproute/program-ids.json index ba62748efe..c5e945eae3 100644 --- a/rust/sealevel/environments/local-e2e/warp-routes/testwarproute/program-ids.json +++ b/rust/sealevel/environments/local-e2e/warp-routes/testwarproute/program-ids.json @@ -1,10 +1,10 @@ { - "sealeveltest1": { - "hex": "0xa77b4e2ed231894cc8cb8eee21adcc705d8489bccc6b2fcf40a358de23e60b7b", - "base58": "CGn8yNtSD3aTTqJfYhUb6s1aVTN75NzwtsFKo1e83aga" - }, "sealeveltest2": { "hex": "0x2317f9615d4ebc2419ad4b88580e2a80a03b2c7a60bc960de7d6934dbc37a87e", "base58": "3MzUPjP5LEkiHH82nEAe28Xtz9ztuMqWc8UmuKxrpVQH" + }, + "sealeveltest1": { + "hex": "0xa77b4e2ed231894cc8cb8eee21adcc705d8489bccc6b2fcf40a358de23e60b7b", + "base58": "CGn8yNtSD3aTTqJfYhUb6s1aVTN75NzwtsFKo1e83aga" } } \ No newline at end of file diff --git a/rust/sealevel/programs/ism/multisig-ism-message-id/src/metadata.rs b/rust/sealevel/programs/ism/multisig-ism-message-id/src/metadata.rs index 423fae12c1..50b3e2c64b 100644 --- a/rust/sealevel/programs/ism/multisig-ism-message-id/src/metadata.rs +++ b/rust/sealevel/programs/ism/multisig-ism-message-id/src/metadata.rs @@ -5,7 +5,7 @@ use crate::error::Error; #[derive(Debug)] pub struct MultisigIsmMessageIdMetadata { - pub origin_mailbox: H256, + pub origin_merkle_tree_hook: H256, pub merkle_root: H256, pub validator_signatures: Vec, } @@ -53,7 +53,7 @@ impl TryFrom> for MultisigIsmMessageIdMetadata { } Ok(Self { - origin_mailbox, + origin_merkle_tree_hook: origin_mailbox, merkle_root, validator_signatures, }) @@ -66,7 +66,7 @@ impl Encode for MultisigIsmMessageIdMetadata { W: std::io::Write, { let mut bytes_written = 0; - bytes_written += writer.write(self.origin_mailbox.as_ref())?; + bytes_written += writer.write(self.origin_merkle_tree_hook.as_ref())?; bytes_written += writer.write(self.merkle_root.as_ref())?; for signature in &self.validator_signatures { bytes_written += writer.write(&signature.as_fixed_bytes()[..])?; @@ -104,7 +104,7 @@ mod test { } let metadata = MultisigIsmMessageIdMetadata::try_from(metadata_bytes).unwrap(); - assert_eq!(metadata.origin_mailbox, origin_mailbox); + assert_eq!(metadata.origin_merkle_tree_hook, origin_mailbox); assert_eq!(metadata.merkle_root, merkle_root); assert_eq!(metadata.validator_signatures, validator_signatures); } diff --git a/rust/sealevel/programs/ism/multisig-ism-message-id/src/processor.rs b/rust/sealevel/programs/ism/multisig-ism-message-id/src/processor.rs index fbd9d924f9..400cfdc7b2 100644 --- a/rust/sealevel/programs/ism/multisig-ism-message-id/src/processor.rs +++ b/rust/sealevel/programs/ism/multisig-ism-message-id/src/processor.rs @@ -246,7 +246,7 @@ fn verify( let multisig_ism = MultisigIsm::new( CheckpointWithMessageId { checkpoint: Checkpoint { - merkle_tree_hook_address: metadata.origin_mailbox, + merkle_tree_hook_address: metadata.origin_merkle_tree_hook, mailbox_domain: message.origin, root: metadata.merkle_root, index: message.nonce, @@ -599,7 +599,7 @@ pub mod test { // is handled in compliance with what the Mailbox expects InterchainSecurityModuleInstruction::Verify(VerifyInstruction { metadata: MultisigIsmMessageIdMetadata { - origin_mailbox: checkpoint.merkle_tree_hook_address, + origin_merkle_tree_hook: checkpoint.merkle_tree_hook_address, merkle_root: checkpoint.root, validator_signatures: vec![ EcdsaSignature::from_bytes(&signatures[0]).unwrap(), @@ -624,7 +624,7 @@ pub mod test { // is handled in compliance with what the Mailbox expects InterchainSecurityModuleInstruction::Verify(VerifyInstruction { metadata: MultisigIsmMessageIdMetadata { - origin_mailbox: checkpoint.merkle_tree_hook_address, + origin_merkle_tree_hook: checkpoint.merkle_tree_hook_address, merkle_root: checkpoint.root, validator_signatures: vec![ EcdsaSignature::from_bytes(&signatures[1]).unwrap(), @@ -652,7 +652,7 @@ pub mod test { // is handled in compliance with what the Mailbox expects InterchainSecurityModuleInstruction::Verify(VerifyInstruction { metadata: MultisigIsmMessageIdMetadata { - origin_mailbox: checkpoint.merkle_tree_hook_address, + origin_merkle_tree_hook: checkpoint.merkle_tree_hook_address, merkle_root: checkpoint.root, validator_signatures: vec![ EcdsaSignature::from_bytes(&signatures[0]).unwrap(), @@ -676,7 +676,7 @@ pub mod test { // is handled in compliance with what the Mailbox expects InterchainSecurityModuleInstruction::Verify(VerifyInstruction { metadata: MultisigIsmMessageIdMetadata { - origin_mailbox: checkpoint.merkle_tree_hook_address, + origin_merkle_tree_hook: checkpoint.merkle_tree_hook_address, merkle_root: checkpoint.root, validator_signatures: vec![ EcdsaSignature::from_bytes(&signatures[0]).unwrap(), diff --git a/rust/sealevel/programs/ism/multisig-ism-message-id/tests/functional.rs b/rust/sealevel/programs/ism/multisig-ism-message-id/tests/functional.rs index 720416dc62..b7820649fc 100644 --- a/rust/sealevel/programs/ism/multisig-ism-message-id/tests/functional.rs +++ b/rust/sealevel/programs/ism/multisig-ism-message-id/tests/functional.rs @@ -419,7 +419,7 @@ async fn test_ism_verify() { // A valid verify instruction with a quorum let verify_instruction = VerifyInstruction { metadata: MultisigIsmMessageIdMetadata { - origin_mailbox: checkpoint.merkle_tree_hook_address, + origin_merkle_tree_hook: checkpoint.merkle_tree_hook_address, merkle_root: checkpoint.root, validator_signatures: vec![ EcdsaSignature::from_bytes(&signatures[0]).unwrap(), diff --git a/rust/utils/run-locally/src/invariants.rs b/rust/utils/run-locally/src/invariants.rs index 707379c836..5a4ddd707d 100644 --- a/rust/utils/run-locally/src/invariants.rs +++ b/rust/utils/run-locally/src/invariants.rs @@ -63,15 +63,14 @@ pub fn termination_invariants_met( .sum::(); // TestSendReceiver randomly breaks gas payments up into // two. So we expect at least as many gas payments as messages. - // TODO: fix this once eth gas payments are introduced - // if gas_payment_events_count < total_messages_expected { - // log!( - // "Relayer has {} gas payment events, expected at least {}", - // gas_payment_events_count, - // total_messages_expected - // ); - // return Ok(false); - // } + if gas_payment_events_count < total_messages_expected { + log!( + "Relayer has {} gas payment events, expected at least {}", + gas_payment_events_count, + total_messages_expected + ); + return Ok(false); + } // if !solana_termination_invariants_met(solana_cli_tools_path, solana_config_path) { // log!("Solana termination invariants not met");