use std::{num::ParseIntError, path::PathBuf, str::FromStr}; use clap::Parser; use color_eyre::eyre::{eyre, Result}; use cosmrs::AccountId; use cw_proof::proof::cw::RawCwProof; use serde::{Deserialize, Serialize}; use tendermint_light_client::types::{Hash, Height, LightBlock, TrustThreshold}; use tendermint_rpc::HttpClientUrl; use tracing::metadata::LevelFilter; pub fn parse_trust_threshold(s: &str) -> Result { if let Some((l, r)) = s.split_once('/') { TrustThreshold::new(l.parse()?, r.parse()?).map_err(Into::into) } else { Err(eyre!( "invalid trust threshold: {s}, format must be X/Y where X and Y are integers" )) } } #[derive(Clone, Debug)] pub struct List(pub Vec); impl> FromStr for List { type Err = E; fn from_str(s: &str) -> Result { s.split(',') .map(|s| s.parse()) .collect::, _>>() .map(Self) } } #[derive(clap::Args, Debug, Clone)] pub struct Verbosity { /// Increase verbosity, can be repeated up to 2 times #[arg(long, short, action = clap::ArgAction::Count)] pub verbose: u8, } impl Verbosity { pub fn to_level_filter(&self) -> LevelFilter { match self.verbose { 0 => LevelFilter::INFO, 1 => LevelFilter::DEBUG, _ => LevelFilter::TRACE, } } fn default() -> Self { Self { verbose: 0 } } } impl FromStr for Verbosity { type Err = ParseIntError; fn from_str(s: &str) -> Result { let verbose: u8 = s.parse()?; Ok(Self { verbose }) } } #[derive(Clone, Debug, Serialize, Deserialize)] pub struct ProofOutput { pub light_client_proof: Vec, pub merkle_proof: RawCwProof, } // TODO: Investigate if it's possible to derive default using Clap's default values impl Default for Config { fn default() -> Self { Config { chain_id: String::default(), primary: "http://127.0.0.1:26657".parse().unwrap(), witnesses: "[]".parse().unwrap(), trusted_height: Height::default(), trusted_hash: Hash::default(), trust_threshold: TrustThreshold::TWO_THIRDS, trusting_period: 1209600u64, max_clock_drift: 5u64, max_block_lag: 5u64, trace_file: None, verbose: Verbosity::default(), contract_address: "".parse().unwrap(), storage_key: String::default(), } } } #[derive(Debug, Parser)] #[command(author, version, about, long_about = None)] pub struct Config { /// Identifier of the chain #[clap(long)] pub chain_id: String, /// Primary RPC address #[clap(long, default_value = "http://127.0.0.1:26657")] pub primary: HttpClientUrl, /// Comma-separated list of witnesses RPC addresses #[clap(long)] pub witnesses: List, /// Height of trusted header #[clap(long)] pub trusted_height: Height, /// Hash of trusted header #[clap(long)] pub trusted_hash: Hash, /// Trust threshold #[clap(long, value_parser = parse_trust_threshold, default_value_t = TrustThreshold::TWO_THIRDS)] pub trust_threshold: TrustThreshold, /// Trusting period, in seconds (default: two weeks) #[clap(long, default_value = "1209600")] pub trusting_period: u64, /// Maximum clock drift, in seconds #[clap(long, default_value = "5")] pub max_clock_drift: u64, /// Maximum block lag, in seconds #[clap(long, default_value = "5")] pub max_block_lag: u64, /// Output file to store light client proof (AKA verification trace) #[clap(long)] pub trace_file: Option, /// Increase verbosity #[clap(flatten)] pub verbose: Verbosity, /// Address of the CosmWasm contract #[clap(long)] pub contract_address: AccountId, /// Storage key of the state item for which proofs must be retrieved #[clap(long)] pub storage_key: String, }