Support default signer configuration (#1908)

### Description

- Adds a `defaultSigner` to settings which will automatically set all
chains without their own signer configuration to use it.
- Adds validation to relayers at startup to ensure that all destination
chains have a signer configuration set.
- Updates e2e test to validate this works

### Drive-by changes

- Support `StrOrInt` or metric port deserialization.

### Related issues

- Relates to #1856

### Backward compatibility

_Are these changes backward compatible?_

Yes

_Are there any infrastructure implications, e.g. changes that would
prohibit deploying older commits using this infra tooling?_

None

### Testing

_What kind of testing have these changes undergone?_

E2E Test
pull/1950/head
Mattie Conover 2 years ago committed by GitHub
parent 6e0c44a828
commit 2c0b12528b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 16
      rust/agents/relayer/src/relayer.rs
  2. 12
      rust/hyperlane-base/src/macros.rs
  3. 16
      rust/hyperlane-base/src/settings/loader.rs
  4. 18
      rust/hyperlane-base/src/settings/mod.rs
  5. 1
      rust/hyperlane-core/src/utils.rs
  6. 5
      rust/utils/run-locally/src/main.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();

@ -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<hyperlane_base::Settings> 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<T> {
pub fn _new_settings<'de, T>(name: &str) -> eyre::Result<T>
where
T: Deserialize<'de> + AsMut<Settings>,
{
use crate::settings::loader::load_settings_object;
load_settings_object::<T, &str>(name, &[])

@ -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<str>>(
pub(crate) fn load_settings_object<'de, T, S>(
agent_prefix: &str,
ignore_prefixes: &[S],
) -> Result<T> {
) -> Result<T>
where
T: Deserialize<'de> + AsMut<Settings>,
S: AsRef<str>,
{
// 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<str>>(
}
};
match Config::try_deserialize(config_deserializer) {
Ok(cfg) => Ok(cfg),
match Config::try_deserialize::<T>(config_deserializer) {
Ok(mut cfg) => {
cfg.as_mut().post_deserialize();
Ok(cfg)
}
Err(err) => {
let err_str = err.to_string();

@ -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<KmsClient> = OnceCell::new();
pub struct Settings {
/// Configuration for contracts on each chain
pub chains: HashMap<String, ChainSetup>,
/// 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<SignerConf>,
/// Gelato config
pub gelato: Option<GelatoConf>,
/// Port to listen for prometheus scrape requests
pub metrics: Option<String>,
pub metrics: Option<StrOrInt>,
/// The tracing configuration
pub tracing: TracingConfig,
}
@ -251,12 +256,20 @@ impl Settings {
name,
self.metrics
.as_ref()
.map(|v| v.parse::<u16>().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(),
}
}
}

@ -204,6 +204,7 @@ macro_rules! convert_to {
};
}
convert_to!(u16);
convert_to!(u32);
convert_to!(u64);

@ -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",

Loading…
Cancel
Save