diff --git a/rust/agents/relayer/src/relayer.rs b/rust/agents/relayer/src/relayer.rs index 459661c7bf..ba4ac30cdb 100644 --- a/rust/agents/relayer/src/relayer.rs +++ b/rust/agents/relayer/src/relayer.rs @@ -9,7 +9,7 @@ use derive_more::AsRef; use eyre::Result; use hyperlane_base::{ db::{HyperlaneRocksDB, DB}, - metrics::{AgentMetrics, Metrics}, + metrics::{AgentMetrics, AgentMetricsUpdater}, run_all, settings::ChainConf, BaseAgent, ContractSyncMetrics, CoreMetrics, HyperlaneAgentCore, SequencedDataContractSync, @@ -72,7 +72,7 @@ pub struct Relayer { skip_transaction_gas_limit_for: HashSet, allow_local_checkpoint_syncers: bool, core_metrics: Arc, - agent_metrics: Metrics, + agent_metrics: AgentMetrics, } impl Debug for Relayer { @@ -101,7 +101,7 @@ impl BaseAgent for Relayer { async fn from_settings( settings: Self::Settings, core_metrics: Arc, - agent_metrics: Metrics, + agent_metrics: AgentMetrics, ) -> Result where Self: Sized, @@ -275,11 +275,8 @@ impl BaseAgent for Relayer { .agent_metrics_conf(Self::AGENT_NAME.to_string()) .await .unwrap(); - let agent_metrics_fetcher = dest_conf - .build_agent_metrics_fetcher(&self.core_metrics) - .await - .unwrap(); - let agent_metrics = AgentMetrics::new( + let agent_metrics_fetcher = dest_conf.build_provider(&self.core_metrics).await.unwrap(); + let agent_metrics = AgentMetricsUpdater::new( self.agent_metrics.clone(), agent_metrics_conf, agent_metrics_fetcher, @@ -291,7 +288,7 @@ impl BaseAgent for Relayer { .await; Ok(()) }) - .instrument(info_span!("AgentMetricsFetcher")); + .instrument(info_span!("AgentMetrics")); tasks.push(fetcher_task); } diff --git a/rust/agents/scraper/src/agent.rs b/rust/agents/scraper/src/agent.rs index 7178ee7706..9941c6a806 100644 --- a/rust/agents/scraper/src/agent.rs +++ b/rust/agents/scraper/src/agent.rs @@ -3,8 +3,8 @@ use std::{collections::HashMap, sync::Arc}; use async_trait::async_trait; use derive_more::AsRef; use hyperlane_base::{ - metrics::Metrics as AgentMetrics, run_all, settings::IndexSettings, BaseAgent, - ContractSyncMetrics, CoreMetrics, HyperlaneAgentCore, + metrics::AgentMetrics, run_all, settings::IndexSettings, BaseAgent, ContractSyncMetrics, + CoreMetrics, HyperlaneAgentCore, }; use hyperlane_core::HyperlaneDomain; use tokio::task::JoinHandle; diff --git a/rust/agents/validator/src/validator.rs b/rust/agents/validator/src/validator.rs index 1a27a31968..42474030ed 100644 --- a/rust/agents/validator/src/validator.rs +++ b/rust/agents/validator/src/validator.rs @@ -10,7 +10,7 @@ use tracing::{error, info, info_span, instrument::Instrumented, warn, Instrument use hyperlane_base::{ db::{HyperlaneRocksDB, DB}, - metrics::Metrics as AgentMetrics, + metrics::AgentMetrics, run_all, BaseAgent, CheckpointSyncer, ContractSyncMetrics, CoreMetrics, HyperlaneAgentCore, SequencedDataContractSync, }; diff --git a/rust/chains/hyperlane-cosmos/src/libs/address.rs b/rust/chains/hyperlane-cosmos/src/libs/address.rs index 7cfdb83caf..507bd24172 100644 --- a/rust/chains/hyperlane-cosmos/src/libs/address.rs +++ b/rust/chains/hyperlane-cosmos/src/libs/address.rs @@ -132,6 +132,11 @@ pub mod test { addr.address(), "neutron1kknekjxg0ear00dky5ykzs8wwp2gz62z9s6aaj" ); + // TODO: watch out for this edge case. This check will fail unless + // the first 12 bytes are removed from the digest. + // let digest = addr.digest(); + // let addr2 = CosmosAddress::from_h256(digest, prefix).expect("Cosmos address creation failed"); + // assert_eq!(addr.address(), addr2.address()); } #[test] diff --git a/rust/chains/hyperlane-cosmos/src/mailbox.rs b/rust/chains/hyperlane-cosmos/src/mailbox.rs index 2249546b77..4aafd29c87 100644 --- a/rust/chains/hyperlane-cosmos/src/mailbox.rs +++ b/rust/chains/hyperlane-cosmos/src/mailbox.rs @@ -96,7 +96,7 @@ impl Debug for CosmosMailbox { impl Mailbox for CosmosMailbox { #[instrument(level = "debug", err, ret, skip(self))] async fn count(&self, lag: Option) -> ChainResult { - let block_height = get_block_height_for_lag(&self.provider.grpc(), lag).await?; + let block_height = get_block_height_for_lag(self.provider.grpc(), lag).await?; self.nonce_at_block(block_height).await } diff --git a/rust/chains/hyperlane-cosmos/src/merkle_tree_hook.rs b/rust/chains/hyperlane-cosmos/src/merkle_tree_hook.rs index 40a0a2ab55..3de0821dff 100644 --- a/rust/chains/hyperlane-cosmos/src/merkle_tree_hook.rs +++ b/rust/chains/hyperlane-cosmos/src/merkle_tree_hook.rs @@ -83,7 +83,7 @@ impl MerkleTreeHook for CosmosMerkleTreeHook { tree: general::EmptyStruct {}, }; - let block_height = get_block_height_for_lag(&self.provider.grpc(), lag).await?; + let block_height = get_block_height_for_lag(self.provider.grpc(), lag).await?; let data = self .provider @@ -117,7 +117,7 @@ impl MerkleTreeHook for CosmosMerkleTreeHook { count: general::EmptyStruct {}, }; - let block_height = get_block_height_for_lag(&self.provider.grpc(), lag).await?; + let block_height = get_block_height_for_lag(self.provider.grpc(), lag).await?; self.count_at_block(block_height).await } @@ -128,7 +128,7 @@ impl MerkleTreeHook for CosmosMerkleTreeHook { check_point: general::EmptyStruct {}, }; - let block_height = get_block_height_for_lag(&self.provider.grpc(), lag).await?; + let block_height = get_block_height_for_lag(self.provider.grpc(), lag).await?; let data = self .provider diff --git a/rust/chains/hyperlane-cosmos/src/providers/mod.rs b/rust/chains/hyperlane-cosmos/src/providers/mod.rs index 66763509b5..1ac97f1e75 100644 --- a/rust/chains/hyperlane-cosmos/src/providers/mod.rs +++ b/rust/chains/hyperlane-cosmos/src/providers/mod.rs @@ -1,7 +1,7 @@ use async_trait::async_trait; use hyperlane_core::{ - metrics::agent::AgentMetricsFetcher, BlockInfo, ChainResult, ContractLocator, HyperlaneChain, - HyperlaneDomain, HyperlaneProvider, TxnInfo, H256, U256, + BlockInfo, ChainResult, ContractLocator, HyperlaneChain, HyperlaneDomain, HyperlaneProvider, + TxnInfo, H256, U256, }; use tendermint_rpc::{client::CompatMode, HttpClient}; @@ -51,8 +51,8 @@ impl CosmosProvider { } /// Get a grpc client - pub fn grpc(&self) -> WasmGrpcProvider { - self.grpc_client.clone() + pub fn grpc(&self) -> &WasmGrpcProvider { + &self.grpc_client } /// Get an rpc client @@ -71,17 +71,6 @@ impl HyperlaneChain for CosmosProvider { } } -#[async_trait] -impl AgentMetricsFetcher for CosmosProvider { - async fn get_balance(&self, address: String) -> ChainResult { - Ok(self - .grpc_client - .get_balance(address, self.canonical_asset.clone()) - .await? - .into()) - } -} - #[async_trait] impl HyperlaneProvider for CosmosProvider { async fn get_block_by_hash(&self, _hash: &H256) -> ChainResult { @@ -96,4 +85,12 @@ impl HyperlaneProvider for CosmosProvider { // FIXME Ok(true) } + + async fn get_balance(&self, address: String) -> ChainResult { + Ok(self + .grpc_client + .get_balance(address, self.canonical_asset.clone()) + .await? + .into()) + } } diff --git a/rust/chains/hyperlane-ethereum/src/provider.rs b/rust/chains/hyperlane-ethereum/src/provider.rs index c73c1d8da5..5fced1aaf4 100644 --- a/rust/chains/hyperlane-ethereum/src/provider.rs +++ b/rust/chains/hyperlane-ethereum/src/provider.rs @@ -6,8 +6,8 @@ use std::time::Duration; use async_trait::async_trait; use derive_new::new; use ethers::prelude::Middleware; -use ethers_core::types::Address; -use hyperlane_core::{ethers_core_types, metrics::agent::AgentMetricsFetcher, U256}; +use ethers_core::abi::Address; +use hyperlane_core::{ethers_core_types, U256}; use tokio::time::sleep; use tracing::instrument; @@ -45,25 +45,6 @@ where } } -#[async_trait] -impl AgentMetricsFetcher for EthereumProvider -where - M: Middleware + 'static, -{ - #[instrument(err, skip(self))] - async fn get_balance(&self, address: String) -> ChainResult { - // Can't use the address directly as a string, because ethers interprets it - // as an ENS name rather than an address. - let addr: Address = address.parse()?; - let balance = self - .provider - .get_balance(addr, None) - .await - .map_err(ChainCommunicationError::from_other)?; - Ok(balance.into()) - } -} - #[async_trait] impl HyperlaneProvider for EthereumProvider where @@ -125,6 +106,19 @@ where .map_err(ChainCommunicationError::from_other)?; Ok(!code.is_empty()) } + + #[instrument(err, skip(self))] + async fn get_balance(&self, address: String) -> ChainResult { + // Can't use the address directly as a string, because ethers interprets it + // as an ENS name rather than an address. + let addr: Address = address.parse()?; + let balance = self + .provider + .get_balance(addr, None) + .await + .map_err(ChainCommunicationError::from_other)?; + Ok(balance.into()) + } } impl EthereumProvider @@ -165,29 +159,6 @@ impl BuildableWithProvider for HyperlaneProviderBuilder { } } -/// Builder for the Agent Metrics Fetcher. -// TODO: Remove this when trait upcasting is stabilized and Box can be used -// as Box -// Tracking issue: -// https://github.com/rust-lang/rust/issues/65991 -pub struct AgentMetricsFetcherBuilder {} - -#[async_trait] -impl BuildableWithProvider for AgentMetricsFetcherBuilder { - type Output = Box; - - async fn build_with_provider( - &self, - provider: M, - locator: &ContractLocator, - ) -> Self::Output { - Box::new(EthereumProvider::new( - Arc::new(provider), - locator.domain.clone(), - )) - } -} - /// Call a get function that returns a Result> and retry if the inner /// option is None. This can happen because the provider has not discovered the /// object we are looking for yet. diff --git a/rust/chains/hyperlane-fuel/src/provider.rs b/rust/chains/hyperlane-fuel/src/provider.rs index 54c38e850c..8048076e04 100644 --- a/rust/chains/hyperlane-fuel/src/provider.rs +++ b/rust/chains/hyperlane-fuel/src/provider.rs @@ -1,8 +1,7 @@ use async_trait::async_trait; use hyperlane_core::{ - metrics::agent::AgentMetricsFetcher, BlockInfo, ChainResult, HyperlaneChain, HyperlaneDomain, - HyperlaneProvider, TxnInfo, H256, U256, + BlockInfo, ChainResult, HyperlaneChain, HyperlaneDomain, HyperlaneProvider, TxnInfo, H256, U256, }; /// A wrapper around a fuel provider to get generic blockchain information. @@ -19,13 +18,6 @@ impl HyperlaneChain for FuelProvider { } } -#[async_trait] -impl AgentMetricsFetcher for FuelProvider { - async fn get_balance(&self, address: String) -> ChainResult { - todo!() - } -} - #[async_trait] impl HyperlaneProvider for FuelProvider { async fn get_block_by_hash(&self, hash: &H256) -> ChainResult { @@ -39,4 +31,8 @@ impl HyperlaneProvider for FuelProvider { async fn is_contract(&self, address: &H256) -> ChainResult { todo!() } + + async fn get_balance(&self, address: String) -> ChainResult { + todo!() + } } diff --git a/rust/chains/hyperlane-sealevel/src/provider.rs b/rust/chains/hyperlane-sealevel/src/provider.rs index fdd20795a8..47be23014c 100644 --- a/rust/chains/hyperlane-sealevel/src/provider.rs +++ b/rust/chains/hyperlane-sealevel/src/provider.rs @@ -1,8 +1,7 @@ use async_trait::async_trait; use hyperlane_core::{ - metrics::agent::AgentMetricsFetcher, BlockInfo, ChainResult, HyperlaneChain, HyperlaneDomain, - HyperlaneProvider, TxnInfo, H256, U256, + BlockInfo, ChainResult, HyperlaneChain, HyperlaneDomain, HyperlaneProvider, TxnInfo, H256, U256, }; /// A wrapper around a Sealevel provider to get generic blockchain information. @@ -30,13 +29,6 @@ impl HyperlaneChain for SealevelProvider { } } -#[async_trait] -impl AgentMetricsFetcher for SealevelProvider { - async fn get_balance(&self, _address: String) -> ChainResult { - todo!() // FIXME - } -} - #[async_trait] impl HyperlaneProvider for SealevelProvider { async fn get_block_by_hash(&self, _hash: &H256) -> ChainResult { @@ -51,4 +43,8 @@ impl HyperlaneProvider for SealevelProvider { // FIXME Ok(true) } + + async fn get_balance(&self, _address: String) -> ChainResult { + todo!() // FIXME + } } diff --git a/rust/hyperlane-base/src/agent.rs b/rust/hyperlane-base/src/agent.rs index 40fa639051..5df9250889 100644 --- a/rust/hyperlane-base/src/agent.rs +++ b/rust/hyperlane-base/src/agent.rs @@ -8,7 +8,7 @@ use tokio::task::JoinHandle; use tracing::{debug_span, instrument::Instrumented, Instrument}; use crate::{ - metrics::{create_agent_metrics, CoreMetrics, Metrics as AgentMetrics}, + metrics::{create_agent_metrics, AgentMetrics, CoreMetrics}, settings::Settings, }; diff --git a/rust/hyperlane-base/src/metrics/agent_metrics.rs b/rust/hyperlane-base/src/metrics/agent_metrics.rs index 87d10993f9..bf60cc9afc 100644 --- a/rust/hyperlane-base/src/metrics/agent_metrics.rs +++ b/rust/hyperlane-base/src/metrics/agent_metrics.rs @@ -4,7 +4,8 @@ use derive_builder::Builder; use derive_new::new; use eyre::Result; use hyperlane_core::metrics::agent::u256_as_scaled_f64; -use hyperlane_core::{metrics::agent::AgentMetricsFetcher, HyperlaneDomain}; +use hyperlane_core::HyperlaneDomain; +use hyperlane_core::HyperlaneProvider; use maplit::hashmap; use prometheus::GaugeVec; use tokio::time::MissedTickBehavior; @@ -27,7 +28,7 @@ pub const WALLET_BALANCE_HELP: &str = /// Agent-specific metrics #[derive(Clone, Builder)] -pub struct Metrics { +pub struct AgentMetrics { /// Current balance of native tokens for the /// wallet address. /// - `chain`: the chain name (or chain ID if the name is unknown) of the @@ -41,8 +42,8 @@ pub struct Metrics { wallet_balance: Option, } -pub(crate) fn create_agent_metrics(metrics: &CoreMetrics) -> Result { - Ok(MetricsBuilder::default() +pub(crate) fn create_agent_metrics(metrics: &CoreMetrics) -> Result { + Ok(AgentMetricsBuilder::default() .wallet_balance(metrics.new_gauge( "wallet_balance", WALLET_BALANCE_HELP, @@ -67,15 +68,15 @@ pub struct AgentMetricsConf { pub name: String, } -/// Utility struct to update agent metrics +/// Utility struct to update agent metrics for a given chain #[derive(new)] -pub struct AgentMetrics { - metrics: Metrics, +pub struct AgentMetricsUpdater { + metrics: AgentMetrics, conf: AgentMetricsConf, - fetcher: Box, + provider: Box, } -impl AgentMetrics { +impl AgentMetricsUpdater { async fn update_wallet_balances(&self) { let Some(wallet_addr) = self.conf.address.clone() else { return; @@ -86,7 +87,7 @@ impl AgentMetrics { }; let chain = self.conf.domain.name(); - match self.fetcher.get_balance(wallet_addr.clone()).await { + match self.provider.get_balance(wallet_addr.clone()).await { Ok(balance) => { // Okay, so the native type is not a token, but whatever, close enough. // Note: This is ETH for many chains, but not all so that is why we use `N` and `Native` diff --git a/rust/hyperlane-base/src/settings/chains.rs b/rust/hyperlane-base/src/settings/chains.rs index 99add295c2..a56ad31afb 100644 --- a/rust/hyperlane-base/src/settings/chains.rs +++ b/rust/hyperlane-base/src/settings/chains.rs @@ -6,9 +6,9 @@ use eyre::{eyre, Context, Result}; use ethers_prometheus::middleware::{ChainInfo, ContractInfo, PrometheusMiddlewareConf}; use hyperlane_core::{ - metrics::agent::AgentMetricsFetcher, AggregationIsm, CcipReadIsm, ContractLocator, - HyperlaneAbi, HyperlaneDomain, HyperlaneDomainProtocol, HyperlaneMessage, HyperlaneProvider, - IndexMode, InterchainGasPaymaster, InterchainGasPayment, InterchainSecurityModule, Mailbox, + AggregationIsm, CcipReadIsm, ContractLocator, HyperlaneAbi, HyperlaneDomain, + HyperlaneDomainProtocol, HyperlaneMessage, HyperlaneProvider, IndexMode, + InterchainGasPaymaster, InterchainGasPayment, InterchainSecurityModule, Mailbox, MerkleTreeHook, MerkleTreeInsertion, MultisigIsm, RoutingIsm, SequenceIndexer, ValidatorAnnounce, H256, }; @@ -119,7 +119,16 @@ impl ChainConf { } ChainConnectionConf::Fuel(_) => todo!(), ChainConnectionConf::Sealevel(_) => todo!(), - ChainConnectionConf::Cosmos(_) => todo!(), + ChainConnectionConf::Cosmos(conf) => { + let locator = self.locator(H256::zero()); + let provider = CosmosProvider::new( + locator.domain.clone(), + conf.clone(), + Some(locator.clone()), + None, + )?; + Ok(Box::new(provider) as Box) + } } .context(ctx) } @@ -596,39 +605,6 @@ impl ChainConf { .context(ctx) } - /// Try to convert the chain setting into a trait object for fetching agent metrics - pub async fn build_agent_metrics_fetcher( - &self, - metrics: &CoreMetrics, - ) -> Result> { - match &self.connection { - ChainConnectionConf::Ethereum(conf) => { - let locator = self.locator(H256::zero()); - let provider = self - .build_ethereum( - conf, - &locator, - metrics, - h_eth::AgentMetricsFetcherBuilder {}, - ) - .await?; - Ok(provider) - } - ChainConnectionConf::Fuel(_) => todo!(), - ChainConnectionConf::Sealevel(_) => todo!(), - ChainConnectionConf::Cosmos(conf) => { - let locator = self.locator(H256::zero()); - let provider = CosmosProvider::new( - locator.domain.clone(), - conf.clone(), - Some(locator.clone()), - None, - )?; - Ok(Box::new(provider) as Box) - } - } - } - async fn signer(&self) -> Result> { if let Some(conf) = &self.signer { Ok(Some(conf.build::().await?)) diff --git a/rust/hyperlane-core/src/metrics/agent.rs b/rust/hyperlane-core/src/metrics/agent.rs index e7e77c9c6d..1aee160740 100644 --- a/rust/hyperlane-core/src/metrics/agent.rs +++ b/rust/hyperlane-core/src/metrics/agent.rs @@ -1,21 +1,11 @@ use std::time::Duration; -use async_trait::async_trait; - -use crate::{ChainResult, U256}; +use crate::U256; /// Interval for querying the prometheus metrics endpoint. /// This should be whatever the prometheus scrape interval is pub const METRICS_SCRAPE_INTERVAL: Duration = Duration::from_secs(60); -/// Trait to be implemented by all chain-specific agent implementations, -/// to support gathering agent metrics. -#[async_trait] -pub trait AgentMetricsFetcher: Send + Sync { - /// Fetch the balance of the wallet address associated with the chain provider. - async fn get_balance(&self, address: String) -> ChainResult; -} - /// Convert a u256 scaled integer value into the corresponding f64 value. #[cfg(feature = "float")] pub fn u256_as_scaled_f64(value: U256, decimals: u8) -> f64 { diff --git a/rust/hyperlane-core/src/traits/provider.rs b/rust/hyperlane-core/src/traits/provider.rs index 4d0d127467..7b2c930926 100644 --- a/rust/hyperlane-core/src/traits/provider.rs +++ b/rust/hyperlane-core/src/traits/provider.rs @@ -4,9 +4,7 @@ use async_trait::async_trait; use auto_impl::auto_impl; use thiserror::Error; -use crate::{ - metrics::agent::AgentMetricsFetcher, BlockInfo, ChainResult, HyperlaneChain, TxnInfo, H256, -}; +use crate::{BlockInfo, ChainResult, HyperlaneChain, TxnInfo, H256, U256}; /// Interface for a provider. Allows abstraction over different provider types /// for different chains. @@ -17,7 +15,7 @@ use crate::{ /// the context of a contract. #[async_trait] #[auto_impl(&, Box, Arc)] -pub trait HyperlaneProvider: HyperlaneChain + AgentMetricsFetcher + Send + Sync + Debug { +pub trait HyperlaneProvider: HyperlaneChain + Send + Sync + Debug { /// Get block info for a given block hash async fn get_block_by_hash(&self, hash: &H256) -> ChainResult; @@ -26,6 +24,9 @@ pub trait HyperlaneProvider: HyperlaneChain + AgentMetricsFetcher + Send + Sync /// Returns whether a contract exists at the provided address async fn is_contract(&self, address: &H256) -> ChainResult; + + /// Fetch the balance of the wallet address associated with the chain provider. + async fn get_balance(&self, address: String) -> ChainResult; } /// Errors when querying for provider information. diff --git a/rust/utils/run-locally/src/cosmos/mod.rs b/rust/utils/run-locally/src/cosmos/mod.rs index 1bf109e15f..28c9515137 100644 --- a/rust/utils/run-locally/src/cosmos/mod.rs +++ b/rust/utils/run-locally/src/cosmos/mod.rs @@ -590,13 +590,13 @@ fn termination_invariants_met( } let ending_relayer_balance: f64 = agent_balance_sum(relayer_metrics_port).unwrap(); + + // Make sure the balance was correctly updated in the metrics. // Ideally, make sure that the difference is >= gas_per_tx * gas_cost, set here: // https://github.com/hyperlane-xyz/hyperlane-monorepo/blob/c2288eb31734ba1f2f997e2c6ecb30176427bc2c/rust/utils/run-locally/src/cosmos/cli.rs#L55 // What's stopping this is that the format returned by the `uosmo` balance query is a surprisingly low number (0.000003999999995184) // but then maybe the gas_per_tx is just very low - how can we check that? (maybe by simulating said tx) if starting_relayer_balance <= ending_relayer_balance { - // worth retrying this because metrics are polled every - // `METRICS_SCRAPE_INTERVAL` log!( "Expected starting relayer balance to be greater than ending relayer balance, but got {} <= {}", starting_relayer_balance, diff --git a/rust/utils/run-locally/src/invariants.rs b/rust/utils/run-locally/src/invariants.rs index 4e76c9473c..6fe857a436 100644 --- a/rust/utils/run-locally/src/invariants.rs +++ b/rust/utils/run-locally/src/invariants.rs @@ -132,9 +132,8 @@ pub fn termination_invariants_met( } let ending_relayer_balance: f64 = agent_balance_sum(9092).unwrap(); + // Make sure the balance was correctly updated in the metrics. if starting_relayer_balance <= ending_relayer_balance { - // worth retrying this because metrics are polled every - // `METRICS_SCRAPE_INTERVAL` log!( "Expected starting relayer balance to be greater than ending relayer balance, but got {} <= {}", starting_relayer_balance,