The home for Hyperlane core contracts, sdk packages, and other infrastructure
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
hyperlane-monorepo/rust/agents/validator/src/settings.rs

196 lines
6.2 KiB

//! Validator configuration.
//!
//! The correct settings shape is defined in the TypeScript SDK metadata. While the the exact shape
//! and validations it defines are not applied here, we should mirror them.
//! ANY CHANGES HERE NEED TO BE REFLECTED IN THE TYPESCRIPT SDK.
use std::{collections::HashSet, path::PathBuf, time::Duration};
use derive_more::{AsMut, AsRef, Deref, DerefMut};
use eyre::{eyre, Context};
use hyperlane_base::{
impl_loadable_from_settings,
settings::{
parser::{RawAgentConf, RawAgentSignerConf, ValueParser},
CheckpointSyncerConf, Settings, SignerConf,
},
};
use hyperlane_core::{cfg_unwrap_all, config::*, HyperlaneDomain, HyperlaneDomainProtocol};
use serde::Deserialize;
use serde_json::Value;
/// Settings for `Validator`
#[derive(Debug, AsRef, AsMut, Deref, DerefMut)]
pub struct ValidatorSettings {
#[as_ref]
#[as_mut]
#[deref]
#[deref_mut]
base: Settings,
/// Database path
pub db: PathBuf,
/// Chain to validate messages on
pub origin_chain: HyperlaneDomain,
/// The validator attestation signer
pub validator: SignerConf,
/// The checkpoint syncer configuration
pub checkpoint_syncer: CheckpointSyncerConf,
/// The reorg_period in blocks
pub reorg_period: u64,
/// How frequently to check for new checkpoints
pub interval: Duration,
}
#[derive(Debug, Deserialize)]
#[serde(transparent)]
struct RawValidatorSettings(Value);
impl_loadable_from_settings!(Validator, RawValidatorSettings -> ValidatorSettings);
impl FromRawConf<RawValidatorSettings> for ValidatorSettings {
fn from_config_filtered(
raw: RawValidatorSettings,
cwp: &ConfigPath,
_filter: (),
) -> ConfigResult<Self> {
let mut err = ConfigParsingError::default();
let p = ValueParser::new(cwp.clone(), &raw.0);
let origin_chain_name = p
.chain(&mut err)
.get_key("originChainName")
.parse_string()
.end();
let origin_chain_name_set = origin_chain_name.map(|s| HashSet::from([s]));
let base: Option<Settings> = p
.parse_from_raw_config::<Settings, RawAgentConf, Option<&HashSet<&str>>>(
origin_chain_name_set.as_ref(),
"Expected valid base agent configuration",
)
.take_config_err(&mut err);
let origin_chain = if let (Some(base), Some(origin_chain_name)) = (&base, origin_chain_name)
{
base.lookup_domain(origin_chain_name)
.context("Missing configuration for the origin chain")
.take_err(&mut err, || cwp + "origin_chain_name")
} else {
None
};
let validator = p
.chain(&mut err)
.get_key("validator")
.parse_from_raw_config::<SignerConf, RawAgentSignerConf, NoFilter>(
(),
"Expected valid validator configuration",
)
.end();
let db = p
.chain(&mut err)
.get_opt_key("db")
.parse_from_str("Expected db file path")
.unwrap_or_else(|| {
std::env::current_dir()
.unwrap()
.join(format!("validator_db_{}", origin_chain_name.unwrap_or("")))
});
let checkpoint_syncer = p
.chain(&mut err)
.get_key("checkpointSyncer")
.and_then(parse_checkpoint_syncer)
.end();
let interval = p
.chain(&mut err)
.get_opt_key("interval")
.parse_u64()
.map(Duration::from_secs)
.unwrap_or(Duration::from_secs(5));
cfg_unwrap_all!(cwp, err: [origin_chain_name]);
let reorg_period = p
.chain(&mut err)
.get_key("chains")
.get_key(origin_chain_name)
.get_opt_key("blocks")
.get_opt_key("reorgPeriod")
.parse_u64()
.unwrap_or(1);
cfg_unwrap_all!(cwp, err: [base, origin_chain, validator, checkpoint_syncer]);
let mut base: Settings = base;
// If the origin chain is an EVM chain, then we can use the validator as the signer if needed.
if origin_chain.domain_protocol() == HyperlaneDomainProtocol::Ethereum {
if let Some(origin) = base.chains.get_mut(origin_chain.name()) {
origin.signer.get_or_insert_with(|| validator.clone());
}
}
err.into_result(Self {
base,
db,
origin_chain,
validator,
checkpoint_syncer,
reorg_period,
interval,
})
}
}
/// Expects ValidatorAgentConfig.checkpointSyncer
fn parse_checkpoint_syncer(syncer: ValueParser) -> ConfigResult<CheckpointSyncerConf> {
let mut err = ConfigParsingError::default();
let syncer_type = syncer.chain(&mut err).get_key("type").parse_string().end();
match syncer_type {
Some("localStorage") => {
let path = syncer
.chain(&mut err)
.get_key("path")
.parse_from_str("Expected checkpoint syncer file path")
.end();
cfg_unwrap_all!(&syncer.cwp, err: [path]);
err.into_result(CheckpointSyncerConf::LocalStorage { path })
}
Some("s3") => {
let bucket = syncer
.chain(&mut err)
.get_key("bucket")
.parse_string()
.end()
.map(str::to_owned);
let region = syncer
.chain(&mut err)
.get_key("region")
.parse_from_str("Expected aws region")
.end();
let folder = syncer
.chain(&mut err)
.get_opt_key("folder")
.parse_string()
.end()
.map(str::to_owned);
cfg_unwrap_all!(&syncer.cwp, err: [bucket, region]);
err.into_result(CheckpointSyncerConf::S3 {
bucket,
region,
folder,
})
}
Some(_) => {
Err(eyre!("Unknown checkpoint syncer type")).into_config_result(|| &syncer.cwp + "type")
}
None => Err(err),
}
}