diff --git a/rust/agents/relayer/src/relayer.rs b/rust/agents/relayer/src/relayer.rs index 2f1a801cf..c8a3aa40e 100644 --- a/rust/agents/relayer/src/relayer.rs +++ b/rust/agents/relayer/src/relayer.rs @@ -2,7 +2,7 @@ use std::collections::{HashMap, HashSet}; use std::sync::Arc; use async_trait::async_trait; -use eyre::{Context, Result}; +use eyre::{ensure, Context, Result}; use hyperlane_core::U256; use tokio::sync::{ mpsc::{self, UnboundedReceiver, UnboundedSender}, @@ -67,10 +67,18 @@ impl BaseAgent for Relayer { let core = settings.build_hyperlane_core(metrics.clone()); let db = DB::from_path(&settings.db)?; + let destination_chains = settings.destinationchainnames.split(','); + + for destination_chain in destination_chains.clone() { + let Some(cfg) = settings.chains.get(destination_chain) else { continue }; + ensure!( + cfg.signer.is_some(), + format!("Destination chain {destination_chain} does not have a configured signer") + ) + } + // Use defined remote chains + the origin chain - let chain_names: Vec<_> = settings - .destinationchainnames - .split(',') + let chain_names: Vec<_> = destination_chains .chain([settings.originchainname.as_str()]) .collect(); diff --git a/rust/hyperlane-base/src/macros.rs b/rust/hyperlane-base/src/macros.rs index f6f8a1bdf..decb6e111 100644 --- a/rust/hyperlane-base/src/macros.rs +++ b/rust/hyperlane-base/src/macros.rs @@ -44,6 +44,7 @@ macro_rules! decl_agent { }; } +use crate::Settings; /// Export this so they don't need to import paste. #[doc(hidden)] pub use paste; @@ -103,6 +104,12 @@ macro_rules! decl_settings { } } + impl AsMut for [<$name Settings>] { + fn as_mut(&mut self) -> &mut hyperlane_base::Settings { + &mut self.base + } + } + impl hyperlane_base::NewFromSettings for [<$name Settings>] { type Error = eyre::Report; @@ -117,7 +124,10 @@ macro_rules! decl_settings { /// Static logic called by the decl_settings! macro. Do not call directly! #[doc(hidden)] -pub fn _new_settings<'de, T: Deserialize<'de>>(name: &str) -> eyre::Result { +pub fn _new_settings<'de, T>(name: &str) -> eyre::Result +where + T: Deserialize<'de> + AsMut, +{ use crate::settings::loader::load_settings_object; load_settings_object::(name, &[]) diff --git a/rust/hyperlane-base/src/settings/loader.rs b/rust/hyperlane-base/src/settings/loader.rs index 849ea5498..877b5aaa0 100644 --- a/rust/hyperlane-base/src/settings/loader.rs +++ b/rust/hyperlane-base/src/settings/loader.rs @@ -3,16 +3,21 @@ use std::env; use std::error::Error; use std::path::PathBuf; +use crate::Settings; use config::{Config, Environment, File}; use eyre::{Context, Result}; use serde::Deserialize; /// Load a settings object from the config locations. /// Further documentation can be found in the `settings` module. -pub(crate) fn load_settings_object<'de, T: Deserialize<'de>, S: AsRef>( +pub(crate) fn load_settings_object<'de, T, S>( agent_prefix: &str, ignore_prefixes: &[S], -) -> Result { +) -> Result +where + T: Deserialize<'de> + AsMut, + S: AsRef, +{ // Derive additional prefix from agent name let prefix = format!("HYP_{}", agent_prefix).to_ascii_uppercase(); @@ -81,8 +86,11 @@ pub(crate) fn load_settings_object<'de, T: Deserialize<'de>, S: AsRef>( } }; - match Config::try_deserialize(config_deserializer) { - Ok(cfg) => Ok(cfg), + match Config::try_deserialize::(config_deserializer) { + Ok(mut cfg) => { + cfg.as_mut().post_deserialize(); + Ok(cfg) + } Err(err) => { let err_str = err.to_string(); diff --git a/rust/hyperlane-base/src/settings/mod.rs b/rust/hyperlane-base/src/settings/mod.rs index 07761a338..ce6410d6c 100644 --- a/rust/hyperlane-base/src/settings/mod.rs +++ b/rust/hyperlane-base/src/settings/mod.rs @@ -77,6 +77,7 @@ use rusoto_kms::KmsClient; use serde::Deserialize; pub use chains::{ChainConf, ChainSetup, CoreContractAddresses}; +use hyperlane_core::utils::StrOrInt; use hyperlane_core::{ db::{HyperlaneDB, DB}, HyperlaneChain, HyperlaneDomain, HyperlaneProvider, InterchainGasPaymaster, @@ -128,10 +129,14 @@ static KMS_CLIENT: OnceCell = OnceCell::new(); pub struct Settings { /// Configuration for contracts on each chain pub chains: HashMap, + /// Default signer configuration for chains which do not define their own. + /// This value is intentionally private as it will get consumed by + /// `post_deserialize`. + defaultsigner: Option, /// Gelato config pub gelato: Option, /// Port to listen for prometheus scrape requests - pub metrics: Option, + pub metrics: Option, /// The tracing configuration pub tracing: TracingConfig, } @@ -251,12 +256,20 @@ impl Settings { name, self.metrics .as_ref() - .map(|v| v.parse::().context("Port must be a valid u16")) + .map(|v| v.try_into().context("Port must be a valid u16")) .transpose()?, prometheus::Registry::new(), )?)) } + /// Make internal connections as-needed after deserializing. + pub(super) fn post_deserialize(&mut self) { + let Some(signer) = self.defaultsigner.take() else { return }; + for chain in self.chains.values_mut() { + chain.signer.get_or_insert_with(|| signer.clone()); + } + } + /// Private to preserve linearity of AgentCore::from_settings -- creating an /// agent consumes the settings. fn clone(&self) -> Self { @@ -265,6 +278,7 @@ impl Settings { gelato: self.gelato.clone(), metrics: self.metrics.clone(), tracing: self.tracing.clone(), + defaultsigner: self.defaultsigner.clone(), } } } diff --git a/rust/hyperlane-core/src/utils.rs b/rust/hyperlane-core/src/utils.rs index f2d220b0c..03783b0e9 100644 --- a/rust/hyperlane-core/src/utils.rs +++ b/rust/hyperlane-core/src/utils.rs @@ -204,6 +204,7 @@ macro_rules! convert_to { }; } +convert_to!(u16); convert_to!(u32); convert_to!(u64); diff --git a/rust/utils/run-locally/src/main.rs b/rust/utils/run-locally/src/main.rs index 091eff1cf..c0dd72f0d 100644 --- a/rust/utils/run-locally/src/main.rs +++ b/rust/utils/run-locally/src/main.rs @@ -154,8 +154,9 @@ fn main() -> ExitCode { "HYP_BASE_CHAINS_TEST1_SIGNER_TYPE" => "hexKey", "HYP_BASE_CHAINS_TEST2_SIGNER_KEY" => "f214f2b2cd398c806f84e317254e0f0b801d0643303237d97a22a48e01628897", "HYP_BASE_CHAINS_TEST2_SIGNER_TYPE" => "hexKey", - "HYP_BASE_CHAINS_TEST3_SIGNER_KEY" => "701b615bbdfb9de65240bc28bd21bbc0d996645a3dd57e7b12bc2bdf6f192c82", - "HYP_BASE_CHAINS_TEST3_SIGNER_TYPE" => "hexKey", + // default is used for TEST3 + "HYP_BASE_DEFAULTSIGNER_KEY" => "701b615bbdfb9de65240bc28bd21bbc0d996645a3dd57e7b12bc2bdf6f192c82", + "HYP_BASE_DEFAULTSIGNER_TYPE" => "hexKey", "HYP_RELAYER_GASPAYMENTENFORCEMENT" => r#"[{"type": "none"}]"#, "HYP_RELAYER_ORIGINCHAINNAME" => "test1", "HYP_RELAYER_DESTINATIONCHAINNAMES" => "test2,test3",