From 33e3ab8b90984f775a4128adedcc0734674e2781 Mon Sep 17 00:00:00 2001 From: "emlautarom1-agent[bot]" <292495798+emlautarom1-agent[bot]@users.noreply.github.com> Date: Wed, 26 Aug 2026 13:41:07 -0300 Subject: [PATCH 1/9] fix(cli): install the tracing subscriber before flag validation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Consolidate the per-command log and Loki flags into a single `TracingArgs` flattened onto the root command as global args, and initialize tracing once in `main` from those flags, before any command builds its config. Warnings raised while validating flags — notably the insecure-relay warning — reach the subscriber on every command, and `enr`/`version` get a subscriber too. Console output goes to stderr so a command's stdout stays pipeable, and `--log-level` accepts only the levels Charon documents, so a typo cannot parse as an env-filter target directive and silently disable logging. `pluto_tracing::init` spawns the Loki background task and returns the worker, so the drain lifetime is owned in one place. Closes #604 --- crates/cli/README.md | 19 +-- crates/cli/src/cli.rs | 5 + crates/cli/src/commands/common.rs | 160 +++++++++++++++++--- crates/cli/src/commands/dkg.rs | 90 +++++------ crates/cli/src/commands/relay.rs | 163 +------------------- crates/cli/src/commands/run.rs | 217 +++++---------------------- crates/cli/src/main.rs | 99 ++++++------ crates/dkg/Cargo.toml | 1 - crates/dkg/src/dkg.rs | 14 -- crates/peerinfo/examples/peerinfo.rs | 8 +- crates/relay-server/Cargo.toml | 2 +- crates/relay-server/src/config.rs | 4 - crates/tracing/examples/basic.rs | 7 +- crates/tracing/src/config.rs | 19 +-- crates/tracing/src/init.rs | 62 ++++++-- crates/tracing/src/lib.rs | 2 +- 16 files changed, 354 insertions(+), 518 deletions(-) diff --git a/crates/cli/README.md b/crates/cli/README.md index 4efcee5c..05906307 100644 --- a/crates/cli/README.md +++ b/crates/cli/README.md @@ -6,7 +6,7 @@ Pluto enables the operation of Ethereum validators in a fault tolerant manner by ## Commands (current) -Most flags below also read a `CHARON_*` environment variable (for example `--beacon-node-endpoints` reads `CHARON_BEACON_NODE_ENDPOINTS`), mirroring charon's environment surface. Exceptions: none of the `alpha test` flags have env bindings, nor do `enr --verbose`, `version --verbose` and `relay --log-color`. A variable that is set but empty is treated as unset. Run `pluto --help` for the authoritative list — flags with a binding show an `[env: ...]` line. +Most flags below also read a `CHARON_*` environment variable (for example `--beacon-node-endpoints` reads `CHARON_BEACON_NODE_ENDPOINTS`), mirroring charon's environment surface. A variable that is set but empty is treated as unset. Run `pluto --help` for the authoritative list — flags with a binding show an `[env: ...]` line. Some flags are accepted for charon compatibility but are not yet wired up. They are marked below as **[IGNORED]** (parsed, then dropped — `pluto run` logs a warning for most of them) or **[UNSUPPORTED]** (setting the flag makes the command exit at startup with an error). @@ -59,7 +59,7 @@ Starts the long-running Pluto middleware process to perform distributed validato - `--testnet-genesis-timestamp `: Genesis timestamp of the custom test network. - `--testnet-capella-hard-fork `: Capella hard fork version of the custom test network. - The custom network is only registered when the testnet flags are fully specified; a partial set is silently ignored and pluto falls back to the built-in network registry. -- Plus the [common P2P flags](#common-p2p-flags) and [common logging flags](#common-logging-flags) (including the Loki flags). +- Plus the [common P2P flags](#common-p2p-flags) and [common logging flags](#common-logging-flags). ### `pluto relay` @@ -75,7 +75,7 @@ Starts a libp2p circuit relay that charon clients can use to discover and connec - `--p2p-advertise-private-addresses`: Enable advertising of libp2p auto-detected private addresses. - `--monitoring-address `: Listening address (ip and port) for the monitoring API (prometheus). - `--debug-address `: **[IGNORED]** Parsed but no debug listener is started (no warning is emitted). -- Plus the [common P2P flags](#common-p2p-flags) and [common logging flags](#common-logging-flags) (including the Loki flags). Note that `--p2p-relays` is accepted but unused by the relay itself. +- Plus the [common P2P flags](#common-p2p-flags) and [common logging flags](#common-logging-flags). Note that `--p2p-relays` is accepted but unused by the relay itself. ### `pluto dkg` @@ -94,7 +94,7 @@ Participate in a distributed key generation ceremony for a specific cluster defi - `--publish-address `: The URL to publish the cluster to. (default: `https://api.obol.tech/v1`) - `--publish-timeout `: Timeout for publishing a cluster; increase for clusters with more than 200 validators. (default: `30s`) - `--zipped`: Create a tar archive compressed with gzip of the target directory after creation. -- Plus the [common P2P flags](#common-p2p-flags) and the [common logging flags](#common-logging-flags) (Loki flags are **not** available on `dkg`). +- Plus the [common P2P flags](#common-p2p-flags) and the [common logging flags](#common-logging-flags). ### `pluto enr` @@ -266,15 +266,16 @@ Shared by `run`, `relay`, `dkg` and `alpha test peers`. ### Common logging flags -Shared by `run`, `relay` and `dkg`. +Global: accepted by every command, and parsed identically before or after the subcommand (`pluto --log-level=debug run` and `pluto run --log-level=debug` are equivalent). + +All log output goes to stderr, leaving each command's stdout free for its own data. + +`RUST_LOG` is not consulted; `--log-level` (or its default) always decides the filter. - `--log-format `: **[IGNORED]** Accepted but not yet applied — output is always console-formatted. (default: `console`) - `--log-level `: Log level; `debug`, `info`, `warn` or `error`. (default: `info`) -- `--log-color `: Log color; `auto`, `force` or `disable`. (default: `auto`) +- `--log-color `: Log color; `auto`, `force` or `disable`. `auto` means "unless `NO_COLOR` is set", not TTY detection. (default: `auto`) - `--log-output-path `: **[IGNORED]** Accepted but not yet applied — no log file is written. - -`run` and `relay` additionally support Loki output: - - `--loki-addresses `: Enables sending of logfmt structured logs to a Loki log aggregation server, in addition to normal stderr logs. Only the first address is used; extra entries are ignored with a warning (charon fans out to every address). - `--loki-service `: Service label sent with logs to Loki. (default: `pluto`) diff --git a/crates/cli/src/cli.rs b/crates/cli/src/cli.rs index 121a201b..2e72ba7a 100644 --- a/crates/cli/src/cli.rs +++ b/crates/cli/src/cli.rs @@ -3,6 +3,7 @@ use clap::{Parser, Subcommand}; use crate::commands::{ + common::TracingArgs, create_cluster::CreateClusterArgs, create_dkg::CreateDkgArgs, create_enr::CreateEnrArgs, @@ -29,6 +30,10 @@ pub struct Cli { /// The subcommand to execute. #[command(subcommand)] pub command: Commands, + + /// Logging flags, shared by every subcommand. + #[command(flatten)] + pub tracing: TracingArgs, } /// Available commands. diff --git a/crates/cli/src/commands/common.rs b/crates/cli/src/commands/common.rs index 9a430943..222780a1 100644 --- a/crates/cli/src/commands/common.rs +++ b/crates/cli/src/commands/common.rs @@ -1,5 +1,7 @@ //! Shared helpers for CLI commands. +use std::{collections::HashMap, path::PathBuf}; + use pluto_p2p::config::RelayAddr; use tracing::warn; @@ -24,32 +26,148 @@ pub enum ConsoleColor { Disable, } -/// Builds a tracing configuration for CLI commands, optionally enabling Loki. +/// Console log verbosity, matching the levels Charon accepts. +#[derive(clap::ValueEnum, Clone, Copy, Debug, Default, PartialEq, Eq)] +pub enum LogLevel { + /// Everything, including per-duty detail. + Debug, + /// Normal operational events. + #[default] + Info, + /// Only recoverable problems. + Warn, + /// Only failures. + Error, +} + +impl LogLevel { + /// The `EnvFilter` directive this level maps to. + fn as_directive(self) -> &'static str { + match self { + Self::Debug => "debug", + Self::Info => "info", + Self::Warn => "warn", + Self::Error => "error", + } + } +} + +/// Logging and Loki flags, accepted by every subcommand. /// -/// `loki` is `Some` when the caller wants events forwarded to a Loki endpoint -/// (e.g. via `--loki-addresses`), and `None` for commands that only need -/// console output. +/// These are `global`, so they parse identically before or after the +/// subcommand and are readable from the root [`crate::cli::Cli`] before any +/// command-specific config conversion runs. That ordering is what lets +/// `main` install the subscriber before validation starts. // TODO: wire `log-output-path` (file output) and `log-format` (logfmt/json) // into the tracing layers. `pluto_tracing` supports console + Loki only, so -// `run`/`dkg`/`relay` accept these flags but do not yet apply them. -pub fn build_console_tracing_config( - level: impl Into, - color: &ConsoleColor, - loki: Option, -) -> pluto_tracing::TracingConfig { - let mut builder = pluto_tracing::TracingConfig::builder().with_default_console(); - - builder = match color { - ConsoleColor::Auto => builder.console_with_ansi(std::env::var("NO_COLOR").is_err()), - ConsoleColor::Force => builder.console_with_ansi(true), - ConsoleColor::Disable => builder.console_with_ansi(false), - }; - - if let Some(loki) = loki { - builder = builder.loki(loki); +// these flags are accepted but not yet applied. +#[derive(clap::Args, Clone, Debug)] +#[command(next_help_heading = "Logging")] +pub struct TracingArgs { + #[arg( + long = "log-format", + env = "CHARON_LOG_FORMAT", + default_value = "console", + global = true, + display_order = 1000, + help = "Log format; console, logfmt or json" + )] + pub log_format: String, + + #[arg( + long = "log-level", + env = "CHARON_LOG_LEVEL", + default_value = "info", + global = true, + display_order = 1001, + help = "Log level; debug, info, warn or error" + )] + pub log_level: LogLevel, + + #[arg( + long = "log-color", + env = "CHARON_LOG_COLOR", + default_value = "auto", + global = true, + display_order = 1002, + help = "Log color; auto, force, disable." + )] + pub log_color: ConsoleColor, + + #[arg( + long = "log-output-path", + env = "CHARON_LOG_OUTPUT_PATH", + global = true, + display_order = 1003, + help = "Path in which to write on-disk logs." + )] + pub log_output_path: Option, + + #[arg( + long = "loki-addresses", + env = "CHARON_LOKI_ADDRESSES", + value_delimiter = ',', + global = true, + display_order = 1004, + help = "Enables sending of logfmt structured logs to these Loki log aggregation server addresses. This is in addition to normal stderr logs." + )] + pub loki_addresses: Vec, + + #[arg( + long = "loki-service", + env = "CHARON_LOKI_SERVICE", + default_value = "pluto", + global = true, + display_order = 1005, + help = "Service label sent with logs to Loki." + )] + pub loki_service: String, +} + +impl TracingArgs { + /// Builds the subscriber configuration. + /// + /// Emits nothing: this runs before the subscriber exists, so any diagnostic + /// it produced would be dropped. Deferred warnings live in + /// [`TracingArgs::warn_unused`]. + pub fn tracing_config(&self) -> pluto_tracing::TracingConfig { + let ansi = match self.log_color { + ConsoleColor::Auto => std::env::var_os("NO_COLOR").is_none(), + ConsoleColor::Force => true, + ConsoleColor::Disable => false, + }; + + let mut builder = pluto_tracing::TracingConfig::builder() + .with_default_console() + .console_with_ansi(ansi) + .override_env_filter(self.log_level.as_directive()); + + // Only the first address is used; see `warn_unused`. + if let Some(loki_url) = self.loki_addresses.first() { + builder = builder.loki(pluto_tracing::LokiConfig { + loki_url: loki_url.clone(), + labels: HashMap::from([("service".to_string(), self.loki_service.clone())]), + extra_fields: HashMap::new(), + }); + } + + builder.build() } - builder.override_env_filter(level.into()).build() + /// Reports flag values that were accepted but not applied. + /// + /// Call once the subscriber is installed. + pub fn warn_unused(&self) { + // Charon fans logs out to every entry in `loki-addresses`, but + // `pluto_tracing::TracingConfig` supports a single Loki layer today. + let ignored = self.loki_addresses.len().saturating_sub(1); + if ignored > 0 { + warn!( + ignored, + "Additional --loki-addresses ignored; only the first is used" + ); + } + } } /// Parses the configured relay addresses, warning about insecure ones. diff --git a/crates/cli/src/commands/dkg.rs b/crates/cli/src/commands/dkg.rs index cc806ca1..4388f054 100644 --- a/crates/cli/src/commands/dkg.rs +++ b/crates/cli/src/commands/dkg.rs @@ -3,7 +3,7 @@ use std::{future::Future, path::PathBuf}; use crate::{ - commands::common::{ConsoleColor, LICENSE, build_console_tracing_config, parse_relay_addrs}, + commands::common::{LICENSE, parse_relay_addrs}, duration::Duration, error::{CliError, Result}, }; @@ -56,9 +56,6 @@ pub struct DkgArgs { #[command(flatten)] pub p2p: DkgP2PArgs, - #[command(flatten)] - pub log: DkgLogArgs, - #[arg( long = "publish-address", env = "CHARON_PUBLISH_ADDRESS", @@ -123,8 +120,6 @@ impl TryFrom for pluto_dkg::dkg::Config { fn try_from(args: DkgArgs) -> Result { validate_p2p_args(&args.p2p)?; - let tracing_config = - build_console_tracing_config(args.log.level.clone(), &args.log.color, None); let p2p_config = { let relays = parse_relay_addrs(&args.p2p.relays)?; @@ -143,7 +138,6 @@ impl TryFrom for pluto_dkg::dkg::Config { .no_verify(args.no_verify) .data_dir(args.data_dir) .p2p(p2p_config) - .log(tracing_config) .keymanager( pluto_dkg::dkg::KeymanagerConfig::builder() .address(args.keymanager_address) @@ -217,41 +211,6 @@ pub struct DkgP2PArgs { pub disable_reuseport: bool, } -/// Logging arguments for the `dkg` command. -#[derive(clap::Args, Clone, Debug)] -pub struct DkgLogArgs { - #[arg( - long = "log-format", - env = "CHARON_LOG_FORMAT", - default_value = "console", - help = "Log format; console, logfmt or json" - )] - pub format: String, - - #[arg( - long = "log-level", - env = "CHARON_LOG_LEVEL", - default_value = "info", - help = "Log level; debug, info, warn or error" - )] - pub level: String, - - #[arg( - long = "log-color", - env = "CHARON_LOG_COLOR", - default_value = "auto", - help = "Log color; auto, force, disable." - )] - pub color: ConsoleColor, - - #[arg( - long = "log-output-path", - env = "CHARON_LOG_OUTPUT_PATH", - help = "Path in which to write on-disk logs." - )] - pub log_output_path: Option, -} - /// Runs the `dkg` command from an already-built configuration. pub async fn run(config: pluto_dkg::dkg::Config, ct: CancellationToken) -> Result<()> { run_with_runner(config, ct, pluto_dkg::dkg::run).await @@ -283,10 +242,13 @@ fn validate_p2p_args(args: &DkgP2PArgs) -> Result<()> { #[cfg(test)] mod tests { use super::*; - use crate::cli::{Cli, Commands}; + use crate::{ + cli::{Cli, Commands}, + commands::common::LogLevel, + }; use clap::Parser; use pluto_p2p::config::RelayAddr; - use std::{sync::Arc, time::Duration as StdDuration}; + use std::{path::Path, sync::Arc, time::Duration as StdDuration}; #[test] fn dkg_is_registered_as_top_level_subcommand() { @@ -302,6 +264,9 @@ mod tests { fn dkg_defaults_match_go() { let cli = Cli::try_parse_from(["pluto", "dkg"]).expect("dkg command should parse"); + assert_eq!(cli.tracing.log_level, LogLevel::Info); + assert_eq!(cli.tracing.log_format, "console"); + let Commands::Dkg(args) = cli.command else { panic!("expected dkg command"); }; @@ -326,8 +291,6 @@ mod tests { args.p2p.relays, pluto_p2p::config::DEFAULT_RELAYS.map(String::from).to_vec(), ); - assert_eq!(args.log.level, "info"); - assert_eq!(args.log.format, "console"); } #[test] @@ -357,7 +320,6 @@ mod tests { ("keymanager-address", "CHARON_KEYMANAGER_ADDRESS"), ("keymanager-auth-token", "CHARON_KEYMANAGER_AUTH_TOKEN"), ("p2p-relays", "CHARON_P2P_RELAYS"), - ("log-level", "CHARON_LOG_LEVEL"), ("publish", "CHARON_PUBLISH"), ("publish-timeout", "CHARON_PUBLISH_TIMEOUT"), ("timeout", "CHARON_TIMEOUT"), @@ -406,6 +368,23 @@ mod tests { ]) .expect("dkg command should parse"); + // Log flags are global, so they land on the root rather than on `dkg`. + assert_eq!(cli.tracing.log_level, LogLevel::Debug); + assert_eq!(cli.tracing.log_format, "json"); + assert_eq!( + cli.tracing.log_output_path.as_deref(), + Some(Path::new("/tmp/pluto.log")) + ); + let tracing_config = cli.tracing.tracing_config(); + assert_eq!(tracing_config.override_env_filter.as_deref(), Some("debug")); + assert!( + tracing_config + .console + .as_ref() + .expect("console config") + .with_ansi + ); + let Commands::Dkg(args) = cli.command else { panic!("expected dkg command"); }; @@ -430,9 +409,6 @@ mod tests { assert_eq!(config.p2p.tcp_addrs, vec!["0.0.0.0:9000".to_string()]); assert_eq!(config.p2p.udp_addrs, vec!["0.0.0.0:9000".to_string()]); assert!(config.p2p.disable_reuse_port); - assert_eq!(config.log.override_env_filter.as_deref(), Some("debug")); - let console = config.log.console.as_ref().expect("console config"); - assert!(console.with_ansi); assert!(config.publish.enabled); assert_eq!(config.publish.address, "https://api.example/v1"); assert_eq!(config.publish.timeout, StdDuration::from_secs(40)); @@ -453,6 +429,17 @@ mod tests { "--log-output-path=/tmp/pluto.log", ]) .expect("dkg command should parse"); + + let tracing_config = cli.tracing.tracing_config(); + assert_eq!(tracing_config.override_env_filter.as_deref(), Some("debug")); + assert!( + !tracing_config + .console + .as_ref() + .expect("console config") + .with_ansi + ); + let Commands::Dkg(args) = cli.command else { panic!("expected dkg command"); }; @@ -466,9 +453,6 @@ mod tests { move |config, token| async move { assert!(!token.is_cancelled()); assert_eq!(config.def_file, ".charon/cluster-definition.json"); - assert_eq!(config.log.override_env_filter.as_deref(), Some("debug")); - let console = config.log.console.as_ref().expect("console config"); - assert!(!console.with_ansi); events.lock().expect("lock").push("runner"); Ok(()) } diff --git a/crates/cli/src/commands/relay.rs b/crates/cli/src/commands/relay.rs index 44e9d795..3ef0538d 100644 --- a/crates/cli/src/commands/relay.rs +++ b/crates/cli/src/commands/relay.rs @@ -1,16 +1,12 @@ use crate::{ - commands::common::{ConsoleColor, LICENSE, build_console_tracing_config, parse_relay_addrs}, + commands::common::{LICENSE, parse_relay_addrs}, error::CliError, }; use pluto_p2p::k1; -use std::{collections::HashMap, path::PathBuf, time::Duration}; +use std::path::PathBuf; use tokio_util::sync::CancellationToken; use tracing::{error, info}; -/// Grace period given to the Loki background task to flush buffered logs -/// once `BackgroundTaskController::shutdown` has been signalled. -const LOKI_FLUSH_TIMEOUT: Duration = Duration::from_secs(3); - /// Arguments for the relay command. #[derive(clap::Args, Clone)] pub struct RelayArgs { @@ -25,12 +21,6 @@ pub struct RelayArgs { #[clap(flatten)] pub p2p: RelayP2PArgs, - - #[clap(flatten)] - pub log: RelayLogFlags, - - #[clap(flatten)] - pub loki: RelayLokiArgs, } impl TryInto for RelayArgs { @@ -65,35 +55,6 @@ impl TryInto for RelayArgs { } }; - let loki_config = match self.loki.loki_addresses.as_slice() { - [] => None, - [loki_url, rest @ ..] => { - if !rest.is_empty() { - // Charon fans logs out to every entry in `loki-addresses`, but - // `pluto_tracing::TracingConfig` only supports a single Loki - // layer today. `tracing::warn!` would be a no-op here because - // no subscriber is installed yet (init happens later inside - // `commands::relay::run`), so write directly to stderr. - eprintln!( - "warning: {extra} additional --loki-addresses ignored; only the first is used", - extra = rest.len(), - ); - } - - let labels = - HashMap::from([("service".to_string(), self.loki.loki_service.clone())]); - - Some(pluto_tracing::LokiConfig { - loki_url: loki_url.clone(), - labels, - extra_fields: HashMap::new(), - }) - } - }; - - let log_config = - build_console_tracing_config(self.log.level.clone(), &self.log.color, loki_config); - let builder = pluto_relay_server::config::Config::builder() .data_dir(self.data_dir.data_dir) .http_addr(self.relay.http_address) @@ -107,8 +68,7 @@ impl TryInto for RelayArgs { .filter_private_addrs(!self.relay.advertise_priv) .maybe_monitoring_addr(self.debug_monitoring.monitor_addr) .maybe_debug_addr(self.debug_monitoring.debug_addr) - .p2p_config(p2p_config) - .log_config(log_config); + .p2p_config(p2p_config); Ok(builder.build()) } @@ -245,99 +205,11 @@ pub struct RelayP2PArgs { pub disable_reuseport: bool, } -#[derive(clap::Args, Clone)] -pub struct RelayLogFlags { - #[arg( - long = "log-format", - env = "CHARON_LOG_FORMAT", - default_value = "console", - help = "Log format; console, logfmt or json" - )] - pub format: String, - - #[arg( - long = "log-level", - env = "CHARON_LOG_LEVEL", - default_value = "info", - help = "Log level; debug, info, warn or error" - )] - pub level: String, - - #[arg(long = "log-color", default_value = "auto", help = "Log color")] - pub color: ConsoleColor, - - #[arg( - long = "log-output-path", - env = "CHARON_LOG_OUTPUT_PATH", - help = "Path in which to write on-disk logs." - )] - pub log_output_path: Option, -} - -#[derive(clap::Args, Clone)] -pub struct RelayLokiArgs { - #[arg( - long = "loki-addresses", - env = "CHARON_LOKI_ADDRESSES", - value_delimiter = ',', - help = "Enables sending of logfmt structured logs to these Loki log aggregation server addresses. This is in addition to normal stderr logs." - )] - pub loki_addresses: Vec, - - #[arg( - long = "loki-service", - env = "CHARON_LOKI_SERVICE", - default_value = "pluto", - help = "Service label sent with logs to Loki." - )] - pub loki_service: String, -} - pub async fn run( config: pluto_relay_server::config::Config, ct: CancellationToken, ) -> Result<(), CliError> { - let loki_shutdown = match pluto_tracing::init(&config.log_config) { - Ok(Some(loki)) => { - let controller = loki.controller; - let handle = tokio::spawn(loki.task); - Some((controller, handle)) - } - Ok(None) => None, - // In tests, the global tracing subscriber is shared across runs in the - // same process, so reinitializing fails. In production this would mean - // the relay silently uses an unrelated subscriber and Loki forwarding - // is dropped — fail loudly instead. - #[cfg(test)] - Err(pluto_tracing::init::Error::Init(_)) => None, - Err(err) => return Err(err.into()), - }; - - // Run the relay in an inner scope so every early `?` / `return Err(..)` is - // captured into `result` and the Loki cleanup below always runs. - let result = serve_relay(&config, ct).await; - - if let Err(err) = &result { - // Surface the shutdown reason through the subscriber so it reaches - // Loki before we close the worker; `main` only `eprintln!`s the - // returned error and that path bypasses the tracing subscriber. - error!(error = %err, "relay exited with error"); - } - - // Drain the Loki worker under a single budget so a hung Loki endpoint - // (e.g. `controller.shutdown` blocked on a full mpsc) cannot wedge - // process exit. After the budget elapses we hard-abort the worker. - if let Some((controller, handle)) = loki_shutdown { - let abort_handle = handle.abort_handle(); - let _ = tokio::time::timeout(LOKI_FLUSH_TIMEOUT, async { - controller.shutdown().await; - let _ = handle.await; - }) - .await; - abort_handle.abort(); - } - - result + serve_relay(&config, ct).await } async fn serve_relay( @@ -424,16 +296,6 @@ mod tests { udp_addrs: vec![], disable_reuseport: false, }, - log: super::RelayLogFlags { - format: "console".into(), - level: "error".into(), - color: super::ConsoleColor::Disable, - log_output_path: None, - }, - loki: super::RelayLokiArgs { - loki_addresses: vec![], - loki_service: "pluto".into(), - }, } } @@ -503,9 +365,8 @@ mod tests { let dir = tempfile::tempdir().unwrap(); let args = relay_args(dir.path()); - // Covers the CLI entry point that the fixture bypasses: tracing init and - // the Loki drain. A pre-cancelled token is deterministic because the - // shutdown arm of the serve loop is the `biased` first branch. + // A pre-cancelled token is deterministic because the shutdown arm of + // the serve loop is the `biased` first branch. let ct = CancellationToken::new(); ct.cancel(); @@ -749,7 +610,7 @@ mod tests { static CLIENT: LazyLock = LazyLock::new(reqwest::Client::new); /// Relay arguments every test starts from: all listeners on [`ANY_ADDR`], - /// quiet logs, no relays to dial. + /// no relays to dial. /// /// `advertise_priv` is load-bearing: without it, `filter_private_addrs` /// drops the loopback listen addresses, and `/enr` answers 500 forever. @@ -778,16 +639,6 @@ mod tests { udp_addrs: vec![ANY_ADDR.into()], disable_reuseport: false, }, - log: super::RelayLogFlags { - format: "console".into(), - level: "error".into(), - color: super::ConsoleColor::Disable, - log_output_path: None, - }, - loki: super::RelayLokiArgs { - loki_addresses: vec![], - loki_service: "".into(), - }, } } diff --git a/crates/cli/src/commands/run.rs b/crates/cli/src/commands/run.rs index 500e71fa..39c126e0 100644 --- a/crates/cli/src/commands/run.rs +++ b/crates/cli/src/commands/run.rs @@ -26,7 +26,6 @@ //! ignored with a warning. use std::{ - collections::HashMap, net::{SocketAddr, ToSocketAddrs}, path::{Path, PathBuf}, time::Duration as StdDuration, @@ -35,10 +34,10 @@ use std::{ use pluto_eth2util::helpers::validate_http_headers; use pluto_featureset::{Feature, FeaturesetError, Status}; use tokio_util::sync::CancellationToken; -use tracing::{error, info, warn}; +use tracing::{info, warn}; use crate::{ - commands::common::{ConsoleColor, LICENSE, build_console_tracing_config, parse_relay_addrs}, + commands::common::{LICENSE, parse_relay_addrs}, duration::Duration, error::{CliError, Result}, }; @@ -50,8 +49,6 @@ const MAX_GRAFFITI_BYTES: usize = 28; const MAX_GRAFFITI_BYTES_NO_APPEND: usize = 32; /// Maximum peer nickname length in bytes. const MAX_NICKNAME_BYTES: usize = 32; -/// Grace period for the Loki background task to flush buffered logs on exit. -const LOKI_FLUSH_TIMEOUT: StdDuration = StdDuration::from_secs(3); /// Default `--monitoring-address`. const DEFAULT_MONITORING_ADDR: &str = "127.0.0.1:3620"; /// Default `--simnet-validator-keys-dir` (matches Charon). @@ -83,12 +80,6 @@ pub struct RunArgs { #[command(flatten)] pub p2p: RunP2PArgs, - #[command(flatten)] - pub log: RunLogArgs, - - #[command(flatten)] - pub loki: RunLokiArgs, - #[command(flatten)] pub feature: RunFeatureArgs, } @@ -478,61 +469,6 @@ pub struct RunP2PArgs { pub disable_reuseport: bool, } -/// Logging flags. -#[derive(clap::Args, Clone, Debug)] -pub struct RunLogArgs { - #[arg( - long = "log-format", - env = "CHARON_LOG_FORMAT", - default_value = "console", - help = "Log format; console, logfmt or json" - )] - pub format: String, - - #[arg( - long = "log-level", - env = "CHARON_LOG_LEVEL", - default_value = "info", - help = "Log level; debug, info, warn or error" - )] - pub level: String, - - #[arg( - long = "log-color", - env = "CHARON_LOG_COLOR", - default_value = "auto", - help = "Log color; auto, force, disable." - )] - pub color: ConsoleColor, - - #[arg( - long = "log-output-path", - env = "CHARON_LOG_OUTPUT_PATH", - help = "Path in which to write on-disk logs." - )] - pub log_output_path: Option, -} - -/// Loki flags. -#[derive(clap::Args, Clone, Debug)] -pub struct RunLokiArgs { - #[arg( - long = "loki-addresses", - env = "CHARON_LOKI_ADDRESSES", - value_delimiter = ',', - help = "Enables sending of logfmt structured logs to these Loki log aggregation server addresses. This is in addition to normal stderr logs." - )] - pub loki_addresses: Vec, - - #[arg( - long = "loki-service", - env = "CHARON_LOKI_SERVICE", - default_value = "pluto", - help = "Service label sent with logs to Loki." - )] - pub loki_service: String, -} - /// Feature set flags. #[derive(clap::Args, Clone, Debug)] pub struct RunFeatureArgs { @@ -614,15 +550,13 @@ pub struct FeatureSetConfig { } /// Configuration for the `run` command — the settings produced from the parsed -/// flags, consumed by [`run`] (tracing/Loki) and bridged into +/// flags, consumed by [`run`] and bridged into /// [`pluto_app::node::AppConfig`] by [`build_app_config`]; `p2p_fuzz` is the /// single test-only field, set only via the hidden `unsafe run` command. #[derive(Debug)] pub struct RunConfig { /// P2P configuration built from [`RunP2PArgs`]. pub p2p: pluto_p2p::config::P2PConfig, - /// Tracing configuration built from [`RunLogArgs`]/[`RunLokiArgs`]. - pub log: pluto_tracing::TracingConfig, /// Feature set configuration. pub feature_set: FeatureSetConfig, /// Path to the cluster lock file. @@ -717,8 +651,6 @@ impl TryFrom for RunConfig { debug_monitoring, no_verify, p2p, - log, - loki, feature, } = args; @@ -791,12 +723,8 @@ impl TryFrom for RunConfig { disable_reuse_port: p2p.disable_reuseport, }; - let log_config = - build_console_tracing_config(log.level, &log.color, build_loki_config(&loki)); - Ok(Self { p2p: p2p_config, - log: log_config, feature_set: FeatureSetConfig { min_status: feature.feature_set, enabled: feature.feature_set_enable, @@ -895,72 +823,11 @@ fn validate_vc_tls(cert: &str, key: &str) -> Result<()> { Ok(()) } -/// Builds the optional Loki tracing configuration from the loki flags. -/// -/// Only a single Loki endpoint is supported today, so any extra -/// `--loki-addresses` entries are ignored with a warning. The warning goes to -/// stderr because no tracing subscriber is installed yet. -fn build_loki_config(loki: &RunLokiArgs) -> Option { - match loki.loki_addresses.as_slice() { - [] => None, - [loki_url, rest @ ..] => { - if !rest.is_empty() { - eprintln!( - "warning: {extra} additional --loki-addresses ignored; only the first is used", - extra = rest.len(), - ); - } - - Some(pluto_tracing::LokiConfig { - loki_url: loki_url.clone(), - labels: HashMap::from([("service".to_string(), loki.loki_service.clone())]), - extra_fields: HashMap::new(), - }) - } - } -} - /// Runs the `run` command from an already-built configuration. -/// -/// Initializes tracing and owns the Loki lifecycle for the command's lifetime: -/// when `--loki-addresses` is set, the background task is spawned here and -/// drained on exit so buffered logs are delivered. pub async fn run(config: RunConfig, ct: CancellationToken) -> Result<()> { - let loki_shutdown = match pluto_tracing::init(&config.log) { - Ok(Some(loki)) => Some((loki.controller, tokio::spawn(loki.task))), - Ok(None) => None, - // In tests the global subscriber is shared across runs in the same - // process, so reinitializing fails; treat that as "no Loki worker" - // rather than failing the command. - #[cfg(test)] - Err(pluto_tracing::init::Error::Init(_)) => None, - Err(err) => return Err(err.into()), - }; - info!("{LICENSE}"); - let result = run_workflow(config, ct).await; - - if let Err(err) = &result { - // Surface the failure through the subscriber so it reaches Loki before - // the worker is drained; `main` only `eprintln!`s the returned error - // and that path bypasses the tracing subscriber. - error!(error = %err, "run exited with error"); - } - - // Drain the Loki worker under a single budget so a hung endpoint cannot - // wedge process exit; hard-abort after the budget elapses. - if let Some((controller, handle)) = loki_shutdown { - let abort_handle = handle.abort_handle(); - let _ = tokio::time::timeout(LOKI_FLUSH_TIMEOUT, async { - controller.shutdown().await; - let _ = handle.await; - }) - .await; - abort_handle.abort(); - } - - result + run_workflow(config, ct).await } /// The long-running validator workflow: bridges the parsed [`RunConfig`] into @@ -998,7 +865,6 @@ fn build_app_config(config: RunConfig) -> Result { // bridge behavior fails to compile instead of being silently dropped. let RunConfig { p2p, - log: _, feature_set: _, lock_file, manifest_file: _, @@ -1096,7 +962,6 @@ fn check_unsupported_flags(config: &RunConfig) -> Result<()> { } /// Warns about observability/availability-only flags the run workflow ignores. -/// Runs after tracing init (see [`run`]) so the warnings reach the subscriber. fn warn_ignored_flags(config: &RunConfig) { if !config.debug_addr.is_empty() { warn!( @@ -1174,12 +1039,16 @@ fn parse_socket_addr(flag: &str, addr: &str) -> Result { #[cfg(test)] mod tests { use super::*; - use crate::cli::{Cli, Commands, UnsafeCommands}; + use crate::{ + cli::{Cli, Commands, UnsafeCommands}, + commands::common::LogLevel, + }; use clap::{CommandFactory, Parser}; use std::{collections::BTreeSet, time::Duration as StdDuration}; - /// Every flag the safe `run` command must expose. - const EXPECTED_RUN_FLAGS: [&str; 54] = [ + /// Every flag the safe `run` command must expose itself. The log and Loki + /// flags are global and live on the root command. + const EXPECTED_RUN_FLAGS: [&str; 48] = [ // priv key "private-key-file", "private-key-file-lock", @@ -1230,14 +1099,6 @@ mod tests { "p2p-tcp-address", "p2p-udp-address", "p2p-disable-reuseport", - // log - "log-format", - "log-level", - "log-color", - "log-output-path", - // loki - "loki-addresses", - "loki-service", // feature "feature-set-enable", "feature-set-disable", @@ -1280,6 +1141,23 @@ mod tests { (*args).try_into() } + /// Parses safe `run` args and returns the tracing config the global log + /// flags produce. + fn parse_tracing(extra: &[&str]) -> pluto_tracing::TracingConfig { + let mut argv = vec![ + "pluto", + "run", + "--beacon-node-endpoints", + "http://beacon.node", + ]; + argv.extend_from_slice(extra); + + Cli::try_parse_from(argv) + .expect("run args should parse") + .tracing + .tracing_config() + } + /// Returns the `Display` string of the error from a failing `parse_run`. fn run_err(extra: &[&str]) -> String { parse_run(extra) @@ -1381,6 +1259,13 @@ mod tests { "http://beacon.node", ]) .expect("run command should parse"); + + // Log and Loki flags are global, so they land on the root. + assert_eq!(cli.tracing.log_level, LogLevel::Info); + assert_eq!(cli.tracing.log_format, "console"); + assert_eq!(cli.tracing.loki_service, "pluto"); + assert!(cli.tracing.loki_addresses.is_empty()); + let Commands::Run(args) = cli.command else { panic!("expected run command"); }; @@ -1440,13 +1325,6 @@ mod tests { assert!(args.p2p.tcp_addrs.is_empty()); assert!(args.p2p.external_ip.is_none()); - // Log. - assert_eq!(args.log.level, "info"); - assert_eq!(args.log.format, "console"); - - // Loki. - assert_eq!(args.loki.loki_service, "pluto"); - // Feature. assert_eq!(args.feature.feature_set, "stable"); assert!(args.feature.feature_set_enable.is_empty()); @@ -1582,19 +1460,16 @@ mod tests { #[test] fn run_loki_config_built_from_addresses() { - // `--loki-addresses` must produce a Loki layer in the tracing config so - // `run` spawns/drains the Loki worker (regression for the lifecycle bug - // where the worker was initialized in `main` and then dropped). - let config = parse_run(&[ + // The Loki flags are global, but they must still reach the subscriber + // config when supplied on `run`. + let tracing_config = parse_tracing(&[ "--loki-addresses", "http://loki.test/push", "--loki-service", "svc", - ]) - .expect("config should build"); + ]); - let loki = config - .log + let loki = tracing_config .loki .as_ref() .expect("loki layer should be configured"); @@ -1602,13 +1477,7 @@ mod tests { assert_eq!(loki.labels.get("service").map(String::as_str), Some("svc")); // No `--loki-addresses` → no Loki layer (nothing to spawn). - assert!( - parse_run(&[]) - .expect("config should build") - .log - .loki - .is_none() - ); + assert!(parse_tracing(&[]).loki.is_none()); } #[test] @@ -1749,10 +1618,6 @@ mod tests { ); // p2p_fuzz is never set on the safe `run` path. assert!(!config.p2p_fuzz); - // `--log-color=force` forces ANSI on the console layer. - let console = config.log.console.as_ref().expect("console config"); - assert!(console.with_ansi); - assert_eq!(config.log.override_env_filter.as_deref(), Some("debug")); } /// Builds the app config from safe `run` flags. diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index 79bb40a7..a7bb6326 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -9,6 +9,7 @@ use clap::FromArgMatches; use cli::{AlphaCommands, Cli, Commands, CreateCommands, TestCommands, UnsafeCommands}; use std::process::ExitCode; use tokio_util::sync::CancellationToken; +use tracing::error; mod ascii; mod cli; @@ -18,19 +19,43 @@ mod error; #[tokio::main] async fn main() -> ExitCode { - match run().await { + let matches = cli::build_command().get_matches(); + + let cli = match Cli::from_arg_matches(&matches) { + Ok(cli) => cli, + Err(err) => { + eprintln!("{err}"); + return ExitCode::FAILURE; + } + }; + + let loki = match pluto_tracing::init(&cli.tracing.tracing_config()) { + Ok(loki) => loki, + Err(err) => { + eprintln!("{err}"); + return ExitCode::FAILURE; + } + }; + cli.tracing.warn_unused(); + + let result = run(cli.command).await; + + let exit = match &result { Ok(()) => ExitCode::SUCCESS, Err(err) => { - eprintln!("Error: {}", err); + error!(error = %err, "command exited with error"); ExitCode::FAILURE } + }; + + if let Some(loki) = loki { + loki.shutdown().await; } -} -async fn run() -> std::result::Result<(), CliError> { - let matches = cli::build_command().get_matches(); - let cli = Cli::from_arg_matches(&matches)?; + exit +} +async fn run(command: Commands) -> std::result::Result<(), CliError> { // Top level cancellation token for graceful shutdown on Ctrl+C / SIGTERM. let ct = CancellationToken::new(); tokio::spawn({ @@ -61,23 +86,18 @@ async fn run() -> std::result::Result<(), CliError> { }); let mut stdout = std::io::stdout(); - match cli.command { - Commands::Create(args) => { - pluto_tracing::init(&pluto_tracing::TracingConfig::default()) - .expect("Failed to initialize tracing"); - match args.command { - CreateCommands::Dkg(args) => commands::create_dkg::run(*args).await, - CreateCommands::Enr(args) => commands::create_enr::run(args), - CreateCommands::Cluster(args) => { - commands::create_cluster::run(&mut stdout, *args).await - } + match command { + Commands::Create(args) => match args.command { + CreateCommands::Dkg(args) => commands::create_dkg::run(*args).await, + CreateCommands::Enr(args) => commands::create_enr::run(args), + CreateCommands::Cluster(args) => { + commands::create_cluster::run(&mut stdout, *args).await } - } + }, Commands::Enr(args) => commands::enr::run(args), Commands::Version(args) => commands::version::run(args), Commands::Dkg(args) => { let config: pluto_dkg::dkg::Config = (*args).try_into()?; - pluto_tracing::init(&config.log).expect("Failed to initialize tracing"); commands::dkg::run(config, ct).await } Commands::Relay(args) => { @@ -86,7 +106,6 @@ async fn run() -> std::result::Result<(), CliError> { } Commands::Run(args) => { let config: commands::run::RunConfig = (*args).try_into()?; - // Tracing/Loki init is owned by `commands::run::run`. commands::run::run(config, ct).await } Commands::Unsafe(args) => match args.command { @@ -96,32 +115,26 @@ async fn run() -> std::result::Result<(), CliError> { } }, Commands::Alpha(args) => match args.command { - AlphaCommands::Test(args) => { - pluto_tracing::init(&pluto_tracing::TracingConfig::default()) - .expect("Failed to initialize tracing"); - match args.command { - TestCommands::Peers(args) => commands::test::peers::run(args, &mut stdout, ct) + AlphaCommands::Test(args) => match args.command { + TestCommands::Peers(args) => commands::test::peers::run(args, &mut stdout, ct) + .await + .map(|_| ()), + TestCommands::Beacon(args) => commands::test::beacon::run(args, &mut stdout, ct) + .await + .map(|_| ()), + TestCommands::Validator(args) => { + commands::test::validator::run(args, &mut stdout, ct) .await - .map(|_| ()), - TestCommands::Beacon(args) => { - commands::test::beacon::run(args, &mut stdout, ct) - .await - .map(|_| ()) - } - TestCommands::Validator(args) => { - commands::test::validator::run(args, &mut stdout, ct) - .await - .map(|_| ()) - } - TestCommands::Mev(args) => commands::test::mev::run(args, &mut stdout, ct) - .await - .map(|_| ()), - TestCommands::Infra(args) => commands::test::infra::run(args, &mut stdout, ct) - .await - .map(|_| ()), - TestCommands::All(args) => commands::test::all::run(*args, &mut stdout).await, + .map(|_| ()) } - } + TestCommands::Mev(args) => commands::test::mev::run(args, &mut stdout, ct) + .await + .map(|_| ()), + TestCommands::Infra(args) => commands::test::infra::run(args, &mut stdout, ct) + .await + .map(|_| ()), + TestCommands::All(args) => commands::test::all::run(*args, &mut stdout).await, + }, }, } } diff --git a/crates/dkg/Cargo.toml b/crates/dkg/Cargo.toml index 0cca0559..4b7545b1 100644 --- a/crates/dkg/Cargo.toml +++ b/crates/dkg/Cargo.toml @@ -33,7 +33,6 @@ pluto-parsigex.workspace = true pluto-peerinfo.workspace = true pluto-frost.workspace = true async-trait.workspace = true -pluto-tracing.workspace = true hex.workspace = true rand.workspace = true serde.workspace = true diff --git a/crates/dkg/src/dkg.rs b/crates/dkg/src/dkg.rs index d5bc9d91..25d0383d 100644 --- a/crates/dkg/src/dkg.rs +++ b/crates/dkg/src/dkg.rs @@ -36,7 +36,6 @@ use pluto_eth2util::keymanager::{self, KeymanagerError}; use pluto_p2p::{ bootnode::BootnodeError, config::P2PConfig, k1::key_path, p2p::P2PError, peer::Peer, }; -use pluto_tracing::TracingConfig; use url::Url; const DEFAULT_DATA_DIR: &str = ".charon"; @@ -275,10 +274,6 @@ pub struct Config { #[builder(default = default_p2p_config())] pub p2p: P2PConfig, - /// Shared tracing configuration for the DKG entrypoint. - #[builder(default = default_tracing_config())] - pub log: pluto_tracing::TracingConfig, - /// Keymanager configuration. #[builder(default)] pub keymanager: KeymanagerConfig, @@ -369,13 +364,6 @@ fn default_p2p_config() -> P2PConfig { } } -fn default_tracing_config() -> TracingConfig { - TracingConfig::builder() - .with_default_console() - .override_env_filter("info") - .build() -} - /// Runs the DKG entrypoint. pub async fn run(conf: Config, ct: CancellationToken) -> Result<(), DkgError> { if ct.is_cancelled() { @@ -1091,8 +1079,6 @@ mod tests { assert!(!config.no_verify); assert_eq!(config.data_dir, path::PathBuf::from(DEFAULT_DATA_DIR)); assert_eq!(config.p2p.relays, pluto_p2p::config::default_relays()); - assert_eq!(config.log.override_env_filter.as_deref(), Some("info")); - assert!(config.log.console.is_some()); assert_eq!(config.publish.address, DEFAULT_PUBLISH_ADDRESS); assert_eq!(config.publish.timeout, DEFAULT_PUBLISH_TIMEOUT); assert!(!config.publish.enabled); diff --git a/crates/peerinfo/examples/peerinfo.rs b/crates/peerinfo/examples/peerinfo.rs index a96e1d68..02e9cae8 100644 --- a/crates/peerinfo/examples/peerinfo.rs +++ b/crates/peerinfo/examples/peerinfo.rs @@ -234,8 +234,8 @@ async fn main() -> anyhow::Result<()> { // Initialize tracing with optional Loki support let tracing_config = build_tracing_config(&args); - if let Some(loki) = pluto_tracing::init(&tracing_config)? { - tokio::spawn(loki.task); + let loki = pluto_tracing::init(&tracing_config)?; + if loki.is_some() { tracing::info!("Loki logging enabled"); } @@ -401,5 +401,9 @@ async fn main() -> anyhow::Result<()> { } } + if let Some(loki) = loki { + loki.shutdown().await; + } + Ok(()) } diff --git a/crates/relay-server/Cargo.toml b/crates/relay-server/Cargo.toml index 8e04354e..1818ecb3 100644 --- a/crates/relay-server/Cargo.toml +++ b/crates/relay-server/Cargo.toml @@ -19,7 +19,6 @@ vise.workspace = true tokio.workspace = true tokio-util.workspace = true rand.workspace = true -pluto-tracing.workspace = true tracing.workspace = true pluto-p2p.workspace = true pluto-core.workspace = true @@ -27,6 +26,7 @@ pluto-core.workspace = true [dev-dependencies] reqwest = { workspace = true } serde_json = { workspace = true } +pluto-tracing.workspace = true [lints] workspace = true diff --git a/crates/relay-server/src/config.rs b/crates/relay-server/src/config.rs index a919015b..16073131 100644 --- a/crates/relay-server/src/config.rs +++ b/crates/relay-server/src/config.rs @@ -3,7 +3,6 @@ use std::{num::NonZeroU32, path::PathBuf, time::Duration}; use bon::Builder; use libp2p::relay; use pluto_p2p::config::P2PConfig; -use pluto_tracing::TracingConfig; /// One hour in seconds. pub const ONE_HOUR_SECONDS: u64 = 60 * 60; @@ -36,9 +35,6 @@ pub struct Config { pub debug_addr: Option, /// The P2P configuration. pub p2p_config: P2PConfig, - /// The logging configuration. - #[builder(default)] - pub log_config: TracingConfig, /// Whether to automatically generate a P2P key. #[builder(default = false)] pub auto_p2p_key: bool, diff --git a/crates/tracing/examples/basic.rs b/crates/tracing/examples/basic.rs index 29cb35b7..70955775 100644 --- a/crates/tracing/examples/basic.rs +++ b/crates/tracing/examples/basic.rs @@ -22,7 +22,6 @@ async fn main() { // Initialize tracing with default console config let config = TracingConfig::builder() .with_default_console() - .with_metrics(true) .loki(LokiConfig { loki_url: "http://localhost:3100".to_string(), labels: HashMap::new(), @@ -35,8 +34,6 @@ async fn main() { .expect("Failed to initialize tracing") .expect("Loki background task should be Some"); - tokio::spawn(loki.task); - let bind_address = SocketAddr::from(([0, 0, 0, 0], 9464)); let exporter = MetricsExporter::default() @@ -71,7 +68,9 @@ async fn main() { info!("Processing completed"); // Wait for 10 seconds to see the logs in Loki - std::thread::sleep(std::time::Duration::from_secs(10)); + tokio::time::sleep(std::time::Duration::from_secs(10)).await; + + loki.shutdown().await; } #[instrument] diff --git a/crates/tracing/src/config.rs b/crates/tracing/src/config.rs index 3fde25b2..2223a77e 100644 --- a/crates/tracing/src/config.rs +++ b/crates/tracing/src/config.rs @@ -7,13 +7,10 @@ pub struct TracingConfig { /// logging is enabled. pub loki: Option, - /// Console configuration. Enables console logging if provided. If not - no - /// console logging is enabled. + /// Console layer options. Defaults are used when absent; console logging is + /// always enabled. pub console: Option, - /// Enables metrics logging. If not - no metrics logging is enabled. - pub metrics: bool, - /// Overrides the environment filter. If not - the environment filter is /// used. pub override_env_filter: Option, @@ -178,18 +175,6 @@ impl TracingConfigBuilder { self } - /// Enables metrics logging. - pub fn with_metrics(mut self, enabled: bool) -> Self { - self.tracing_config.metrics = enabled; - self - } - - /// Sets whether metrics logging is enabled. - pub fn metrics(mut self, enabled: bool) -> Self { - self.tracing_config.metrics = enabled; - self - } - /// Sets the environment filter override. pub fn override_env_filter(mut self, filter: impl Into) -> Self { self.tracing_config.override_env_filter = Some(filter.into()); diff --git a/crates/tracing/src/init.rs b/crates/tracing/src/init.rs index 8b116969..0b6469b2 100644 --- a/crates/tracing/src/init.rs +++ b/crates/tracing/src/init.rs @@ -1,8 +1,8 @@ -use std::str::FromStr; +use std::{str::FromStr, time::Duration}; use base64::{Engine as _, engine::general_purpose::STANDARD as BASE64}; use percent_encoding::percent_decode_str; -use tracing_loki::{BackgroundTask, BackgroundTaskController, url::Url}; +use tracing_loki::{BackgroundTaskController, url::Url}; use tracing_subscriber::{ EnvFilter, Registry, layer::SubscriberExt as _, util::SubscriberInitExt as _, }; @@ -27,23 +27,48 @@ pub enum Error { type Result = std::result::Result; -/// Loki background task plus the controller used to signal graceful shutdown. +/// Grace period given to the Loki background task to flush buffered events once +/// shutdown has been signalled. +const FLUSH_TIMEOUT: Duration = Duration::from_secs(3); + +/// The running Loki background task, returned by [`init`] when Loki is +/// configured. /// -/// For long-lived services, hold onto `controller` and call -/// `controller.shutdown().await` followed by awaiting the spawned `task` -/// before exit so buffered events are drained. Short-lived programs (e.g. -/// examples, one-shot CLI subcommands) may drop the controller; any logs -/// not yet posted to Loki at process exit will be lost. -#[must_use = "the background `task` must be spawned for events to reach Loki"] -pub struct LokiInit { - /// Handle used to tell the background task to drain its queue and exit. - pub controller: BackgroundTaskController, - /// Future that ships buffered events to Loki; must be spawned to run. - pub task: BackgroundTask, +/// Dropping this rather than calling [`LokiWorker::shutdown`] loses every event +/// not yet posted to Loki. +pub struct LokiWorker { + controller: BackgroundTaskController, + handle: tokio::task::JoinHandle<()>, +} + +impl LokiWorker { + /// Drains buffered events and stops the worker. + /// + /// Signalling and draining share a single grace period so an unreachable + /// Loki endpoint cannot wedge process exit; the worker is hard-aborted once + /// the budget elapses. + pub async fn shutdown(self) { + let abort_handle = self.handle.abort_handle(); + + let _ = tokio::time::timeout(FLUSH_TIMEOUT, async { + self.controller.shutdown().await; + let _ = self.handle.await; + }) + .await; + + abort_handle.abort(); + } } /// Initializes the tracing subscriber. -pub fn init(config: &TracingConfig) -> Result> { +/// +/// When `config` enables Loki, the background task that ships events is spawned +/// here and returned so the caller can drain it before exit. +/// +/// # Panics +/// +/// Panics when Loki is configured and this is called outside a Tokio runtime. +pub fn init(config: &TracingConfig) -> Result> { let env_filter = if let Some(override_env_filter) = config.override_env_filter.as_ref() { EnvFilter::from_str(override_env_filter).unwrap_or_else(|_| default_env_filter()) } else { @@ -53,6 +78,8 @@ pub fn init(config: &TracingConfig) -> Result> { let console_config = config.console.clone().unwrap_or_default(); let fmt_layer = tracing_subscriber::fmt::layer() + // Logs belong on stderr so a command's stdout stays pipeable. + .with_writer(std::io::stderr) .with_target(console_config.with_target) .with_level(console_config.with_level) .with_thread_ids(console_config.with_thread_ids) @@ -91,7 +118,10 @@ pub fn init(config: &TracingConfig) -> Result> { let registry = registry.with(loki_layer); registry.try_init()?; - Ok(Some(LokiInit { controller, task })) + Ok(Some(LokiWorker { + controller, + handle: tokio::spawn(task), + })) } else { registry.try_init()?; Ok(None) diff --git a/crates/tracing/src/lib.rs b/crates/tracing/src/lib.rs index 7a4d407d..c467d9d6 100644 --- a/crates/tracing/src/lib.rs +++ b/crates/tracing/src/lib.rs @@ -18,4 +18,4 @@ pub mod metrics; pub use config::{ConsoleConfig, LokiConfig, TracingConfig, TracingConfigBuilder}; -pub use init::{LokiInit, init}; +pub use init::{LokiWorker, init}; From 8145a0b16ab6e8a7f4977f752cd25c4b4629cc5e Mon Sep 17 00:00:00 2001 From: Lautaro Emanuel Date: Wed, 26 Aug 2026 13:49:08 -0300 Subject: [PATCH 2/9] fix(cli): accept any casing for --log-level and --log-color MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Charon resolves the level through `zapcore.ParseLevel` and lowercases the color before matching, so both accept `INFO` and `FORCE` as readily as `info` and `force` — a common shape for values supplied through `CHARON_LOG_LEVEL` in unit files and compose manifests. Values that name no level at all stay rejected, so a typo cannot parse as an env-filter target directive and silently disable logging. --- crates/cli/src/commands/common.rs | 52 +++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/crates/cli/src/commands/common.rs b/crates/cli/src/commands/common.rs index 222780a1..a2c1c697 100644 --- a/crates/cli/src/commands/common.rs +++ b/crates/cli/src/commands/common.rs @@ -79,6 +79,7 @@ pub struct TracingArgs { env = "CHARON_LOG_LEVEL", default_value = "info", global = true, + ignore_case = true, display_order = 1001, help = "Log level; debug, info, warn or error" )] @@ -89,6 +90,7 @@ pub struct TracingArgs { env = "CHARON_LOG_COLOR", default_value = "auto", global = true, + ignore_case = true, display_order = 1002, help = "Log color; auto, force, disable." )] @@ -209,6 +211,56 @@ pub fn parse_relay_addrs(relays: &[String]) -> std::result::Result::try_parse_from([ + "pluto", + "enr", + &format!("--log-level={level}"), + ]) + .unwrap_or_else(|err| panic!("--log-level={level} should parse: {err}")); + + assert_eq!( + cli.tracing.tracing_config().override_env_filter.as_deref(), + Some("debug") + ); + } + + for color in ["disable", "DISABLE", "Disable"] { + let cli = ::try_parse_from([ + "pluto", + "enr", + &format!("--log-color={color}"), + ]) + .unwrap_or_else(|err| panic!("--log-color={color} should parse: {err}")); + + assert!( + !cli.tracing + .tracing_config() + .console + .expect("console") + .with_ansi + ); + } + } + + #[test] + fn log_level_still_rejects_values_charon_does_not_accept() { + // A free-form level would parse as an `EnvFilter` target directive and + // silently disable all logging. + let err = + match ::try_parse_from(["pluto", "enr", "--log-level=nonsense"]) { + Ok(_) => panic!("bogus level should be rejected"), + Err(err) => err, + }; + + assert_eq!(err.kind(), clap::error::ErrorKind::InvalidValue); + } // Per-address parsing is covered by `RelayAddr`'s own tests; what is left // to check here is the empty-value contract and the error wrapping. From d3d3ce09f5563a983a909256fa96ce278d6f20ce Mon Sep 17 00:00:00 2001 From: "emlautarom1-agent[bot]" <292495798+emlautarom1-agent[bot]@users.noreply.github.com> Date: Wed, 26 Aug 2026 13:52:22 -0300 Subject: [PATCH 3/9] refactor(cli): drop the unconstructed CliError variants `main` reports a tracing-init failure and a clap parse failure directly on stderr, since neither has a subscriber to report through, so nothing converts either error into a `CliError`. Removing the variants also removes the `From` impls that made them look reachable. --- crates/cli/src/error.rs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/crates/cli/src/error.rs b/crates/cli/src/error.rs index c1ce261a..4bf2787b 100644 --- a/crates/cli/src/error.rs +++ b/crates/cli/src/error.rs @@ -116,14 +116,6 @@ pub enum CliError { #[error("Eth2util deposit error: {0}")] Eth2utilDepositError(#[from] pluto_eth2util::deposit::DepositError), - /// Tracing initialization error. - #[error("Tracing initialization error: {0}")] - TracingInitError(#[from] pluto_tracing::init::Error), - - /// Command parsing error. - #[error("Command parsing error: {0}")] - CommandParsingError(#[from] clap::Error), - /// Create DKG error. #[error("Create DKG error: {0}")] CreateDKGError(#[from] crate::commands::create_dkg::CreateDkgError), From 93c6624c00e0d18cb0027393b74aafb36d8f77ad Mon Sep 17 00:00:00 2001 From: "emlautarom1-agent[bot]" <292495798+emlautarom1-agent[bot]@users.noreply.github.com> Date: Wed, 26 Aug 2026 14:39:16 -0300 Subject: [PATCH 4/9] refactor(cli): mark incomplete logging flags with TODOs `--log-format` and `--log-output-path` are accepted and never applied, and only the first `--loki-addresses` entry reaches a layer. A runtime warning covered one of the three and fired on every command that set it, including the ones that never read the flag. Note the gap on each field instead, so it is visible where the flag is declared and stays next to the work needed to close it. --- crates/cli/README.md | 2 +- crates/cli/src/commands/common.rs | 26 +++++--------------------- crates/cli/src/main.rs | 1 - 3 files changed, 6 insertions(+), 23 deletions(-) diff --git a/crates/cli/README.md b/crates/cli/README.md index 05906307..21338c30 100644 --- a/crates/cli/README.md +++ b/crates/cli/README.md @@ -276,7 +276,7 @@ All log output goes to stderr, leaving each command's stdout free for its own da - `--log-level `: Log level; `debug`, `info`, `warn` or `error`. (default: `info`) - `--log-color `: Log color; `auto`, `force` or `disable`. `auto` means "unless `NO_COLOR` is set", not TTY detection. (default: `auto`) - `--log-output-path `: **[IGNORED]** Accepted but not yet applied — no log file is written. -- `--loki-addresses `: Enables sending of logfmt structured logs to a Loki log aggregation server, in addition to normal stderr logs. Only the first address is used; extra entries are ignored with a warning (charon fans out to every address). +- `--loki-addresses `: Enables sending of logfmt structured logs to a Loki log aggregation server, in addition to normal stderr logs. Only the first address is used; extra entries are silently ignored (charon fans out to every address). - `--loki-service `: Service label sent with logs to Loki. (default: `pluto`) ## Example diff --git a/crates/cli/src/commands/common.rs b/crates/cli/src/commands/common.rs index a2c1c697..ccbe12ec 100644 --- a/crates/cli/src/commands/common.rs +++ b/crates/cli/src/commands/common.rs @@ -58,12 +58,10 @@ impl LogLevel { /// subcommand and are readable from the root [`crate::cli::Cli`] before any /// command-specific config conversion runs. That ordering is what lets /// `main` install the subscriber before validation starts. -// TODO: wire `log-output-path` (file output) and `log-format` (logfmt/json) -// into the tracing layers. `pluto_tracing` supports console + Loki only, so -// these flags are accepted but not yet applied. #[derive(clap::Args, Clone, Debug)] #[command(next_help_heading = "Logging")] pub struct TracingArgs { + // TODO: wire into the tracing layers; output is always console-formatted. #[arg( long = "log-format", env = "CHARON_LOG_FORMAT", @@ -96,6 +94,7 @@ pub struct TracingArgs { )] pub log_color: ConsoleColor, + // TODO: write logs to this path; no file is written today. #[arg( long = "log-output-path", env = "CHARON_LOG_OUTPUT_PATH", @@ -105,6 +104,8 @@ pub struct TracingArgs { )] pub log_output_path: Option, + // TODO: fan out to every address like charon; `pluto_tracing` supports a + // single Loki layer, so only the first is used. #[arg( long = "loki-addresses", env = "CHARON_LOKI_ADDRESSES", @@ -130,8 +131,7 @@ impl TracingArgs { /// Builds the subscriber configuration. /// /// Emits nothing: this runs before the subscriber exists, so any diagnostic - /// it produced would be dropped. Deferred warnings live in - /// [`TracingArgs::warn_unused`]. + /// it produced would be dropped. pub fn tracing_config(&self) -> pluto_tracing::TracingConfig { let ansi = match self.log_color { ConsoleColor::Auto => std::env::var_os("NO_COLOR").is_none(), @@ -144,7 +144,6 @@ impl TracingArgs { .console_with_ansi(ansi) .override_env_filter(self.log_level.as_directive()); - // Only the first address is used; see `warn_unused`. if let Some(loki_url) = self.loki_addresses.first() { builder = builder.loki(pluto_tracing::LokiConfig { loki_url: loki_url.clone(), @@ -155,21 +154,6 @@ impl TracingArgs { builder.build() } - - /// Reports flag values that were accepted but not applied. - /// - /// Call once the subscriber is installed. - pub fn warn_unused(&self) { - // Charon fans logs out to every entry in `loki-addresses`, but - // `pluto_tracing::TracingConfig` supports a single Loki layer today. - let ignored = self.loki_addresses.len().saturating_sub(1); - if ignored > 0 { - warn!( - ignored, - "Additional --loki-addresses ignored; only the first is used" - ); - } - } } /// Parses the configured relay addresses, warning about insecure ones. diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index a7bb6326..c67e96bc 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -36,7 +36,6 @@ async fn main() -> ExitCode { return ExitCode::FAILURE; } }; - cli.tracing.warn_unused(); let result = run(cli.command).await; From 2a9b5d394f96d704cdd21cf37cd49c2db285f19a Mon Sep 17 00:00:00 2001 From: Lautaro Emanuel Date: Fri, 28 Aug 2026 17:01:18 -0300 Subject: [PATCH 5/9] fix(cli): warn when extra --loki-addresses are ignored Charon fans logs out to every `--loki-addresses` entry; `pluto_tracing` supports a single Loki layer, so only the first one reaches a subscriber. Report the dropped entries through `warn!` once the subscriber is installed, so the diagnostic also reaches Loki. --- crates/cli/README.md | 2 +- crates/cli/src/commands/common.rs | 26 +++++++++++++++++++++----- crates/cli/src/main.rs | 1 + 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/crates/cli/README.md b/crates/cli/README.md index 21338c30..05906307 100644 --- a/crates/cli/README.md +++ b/crates/cli/README.md @@ -276,7 +276,7 @@ All log output goes to stderr, leaving each command's stdout free for its own da - `--log-level `: Log level; `debug`, `info`, `warn` or `error`. (default: `info`) - `--log-color `: Log color; `auto`, `force` or `disable`. `auto` means "unless `NO_COLOR` is set", not TTY detection. (default: `auto`) - `--log-output-path `: **[IGNORED]** Accepted but not yet applied — no log file is written. -- `--loki-addresses `: Enables sending of logfmt structured logs to a Loki log aggregation server, in addition to normal stderr logs. Only the first address is used; extra entries are silently ignored (charon fans out to every address). +- `--loki-addresses `: Enables sending of logfmt structured logs to a Loki log aggregation server, in addition to normal stderr logs. Only the first address is used; extra entries are ignored with a warning (charon fans out to every address). - `--loki-service `: Service label sent with logs to Loki. (default: `pluto`) ## Example diff --git a/crates/cli/src/commands/common.rs b/crates/cli/src/commands/common.rs index ccbe12ec..a2c1c697 100644 --- a/crates/cli/src/commands/common.rs +++ b/crates/cli/src/commands/common.rs @@ -58,10 +58,12 @@ impl LogLevel { /// subcommand and are readable from the root [`crate::cli::Cli`] before any /// command-specific config conversion runs. That ordering is what lets /// `main` install the subscriber before validation starts. +// TODO: wire `log-output-path` (file output) and `log-format` (logfmt/json) +// into the tracing layers. `pluto_tracing` supports console + Loki only, so +// these flags are accepted but not yet applied. #[derive(clap::Args, Clone, Debug)] #[command(next_help_heading = "Logging")] pub struct TracingArgs { - // TODO: wire into the tracing layers; output is always console-formatted. #[arg( long = "log-format", env = "CHARON_LOG_FORMAT", @@ -94,7 +96,6 @@ pub struct TracingArgs { )] pub log_color: ConsoleColor, - // TODO: write logs to this path; no file is written today. #[arg( long = "log-output-path", env = "CHARON_LOG_OUTPUT_PATH", @@ -104,8 +105,6 @@ pub struct TracingArgs { )] pub log_output_path: Option, - // TODO: fan out to every address like charon; `pluto_tracing` supports a - // single Loki layer, so only the first is used. #[arg( long = "loki-addresses", env = "CHARON_LOKI_ADDRESSES", @@ -131,7 +130,8 @@ impl TracingArgs { /// Builds the subscriber configuration. /// /// Emits nothing: this runs before the subscriber exists, so any diagnostic - /// it produced would be dropped. + /// it produced would be dropped. Deferred warnings live in + /// [`TracingArgs::warn_unused`]. pub fn tracing_config(&self) -> pluto_tracing::TracingConfig { let ansi = match self.log_color { ConsoleColor::Auto => std::env::var_os("NO_COLOR").is_none(), @@ -144,6 +144,7 @@ impl TracingArgs { .console_with_ansi(ansi) .override_env_filter(self.log_level.as_directive()); + // Only the first address is used; see `warn_unused`. if let Some(loki_url) = self.loki_addresses.first() { builder = builder.loki(pluto_tracing::LokiConfig { loki_url: loki_url.clone(), @@ -154,6 +155,21 @@ impl TracingArgs { builder.build() } + + /// Reports flag values that were accepted but not applied. + /// + /// Call once the subscriber is installed. + pub fn warn_unused(&self) { + // Charon fans logs out to every entry in `loki-addresses`, but + // `pluto_tracing::TracingConfig` supports a single Loki layer today. + let ignored = self.loki_addresses.len().saturating_sub(1); + if ignored > 0 { + warn!( + ignored, + "Additional --loki-addresses ignored; only the first is used" + ); + } + } } /// Parses the configured relay addresses, warning about insecure ones. diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index c67e96bc..a7bb6326 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -36,6 +36,7 @@ async fn main() -> ExitCode { return ExitCode::FAILURE; } }; + cli.tracing.warn_unused(); let result = run(cli.command).await; From 7d7ffaa0dd82909ce9f548d4267a64313a4c84b5 Mon Sep 17 00:00:00 2001 From: Lautaro Emanuel Date: Fri, 28 Aug 2026 17:13:02 -0300 Subject: [PATCH 6/9] feat(cli): add strongly typed log format options --- crates/cli/src/commands/common.rs | 39 ++++++++++++++++++++++++------- crates/cli/src/commands/dkg.rs | 6 ++--- crates/cli/src/commands/run.rs | 4 ++-- 3 files changed, 36 insertions(+), 13 deletions(-) diff --git a/crates/cli/src/commands/common.rs b/crates/cli/src/commands/common.rs index a2c1c697..6683d8f2 100644 --- a/crates/cli/src/commands/common.rs +++ b/crates/cli/src/commands/common.rs @@ -52,6 +52,18 @@ impl LogLevel { } } +/// Log output encoding +#[derive(clap::ValueEnum, Clone, Copy, Debug, Default, PartialEq, Eq)] +pub enum LogFormat { + /// Human-readable, optionally colored. + #[default] + Console, + /// Flat `key=value` pairs. + Logfmt, + /// One JSON object per event. + Json, +} + /// Logging and Loki flags, accepted by every subcommand. /// /// These are `global`, so they parse identically before or after the @@ -69,10 +81,11 @@ pub struct TracingArgs { env = "CHARON_LOG_FORMAT", default_value = "console", global = true, + ignore_case = true, display_order = 1000, help = "Log format; console, logfmt or json" )] - pub log_format: String, + pub log_format: LogFormat, #[arg( long = "log-level", @@ -247,19 +260,29 @@ mod tests { .with_ansi ); } + + for format in ["logfmt", "LOGFMT", "Logfmt"] { + let cli = ::try_parse_from([ + "pluto", + "enr", + &format!("--log-format={format}"), + ]) + .unwrap_or_else(|err| panic!("--log-format={format} should parse: {err}")); + + assert_eq!(cli.tracing.log_format, LogFormat::Logfmt); + } } #[test] - fn log_level_still_rejects_values_charon_does_not_accept() { - // A free-form level would parse as an `EnvFilter` target directive and - // silently disable all logging. - let err = - match ::try_parse_from(["pluto", "enr", "--log-level=nonsense"]) { - Ok(_) => panic!("bogus level should be rejected"), + fn log_flags_reject_values_charon_does_not_accept() { + for flag in ["--log-level=nonsense", "--log-format=nonsense"] { + let err = match ::try_parse_from(["pluto", "enr", flag]) { + Ok(_) => panic!("{flag} should be rejected"), Err(err) => err, }; - assert_eq!(err.kind(), clap::error::ErrorKind::InvalidValue); + assert_eq!(err.kind(), clap::error::ErrorKind::InvalidValue); + } } // Per-address parsing is covered by `RelayAddr`'s own tests; what is left diff --git a/crates/cli/src/commands/dkg.rs b/crates/cli/src/commands/dkg.rs index 4388f054..463e1344 100644 --- a/crates/cli/src/commands/dkg.rs +++ b/crates/cli/src/commands/dkg.rs @@ -244,7 +244,7 @@ mod tests { use super::*; use crate::{ cli::{Cli, Commands}, - commands::common::LogLevel, + commands::common::{LogFormat, LogLevel}, }; use clap::Parser; use pluto_p2p::config::RelayAddr; @@ -265,7 +265,7 @@ mod tests { let cli = Cli::try_parse_from(["pluto", "dkg"]).expect("dkg command should parse"); assert_eq!(cli.tracing.log_level, LogLevel::Info); - assert_eq!(cli.tracing.log_format, "console"); + assert_eq!(cli.tracing.log_format, LogFormat::Console); let Commands::Dkg(args) = cli.command else { panic!("expected dkg command"); @@ -370,7 +370,7 @@ mod tests { // Log flags are global, so they land on the root rather than on `dkg`. assert_eq!(cli.tracing.log_level, LogLevel::Debug); - assert_eq!(cli.tracing.log_format, "json"); + assert_eq!(cli.tracing.log_format, LogFormat::Json); assert_eq!( cli.tracing.log_output_path.as_deref(), Some(Path::new("/tmp/pluto.log")) diff --git a/crates/cli/src/commands/run.rs b/crates/cli/src/commands/run.rs index 39c126e0..ccce35c5 100644 --- a/crates/cli/src/commands/run.rs +++ b/crates/cli/src/commands/run.rs @@ -1041,7 +1041,7 @@ mod tests { use super::*; use crate::{ cli::{Cli, Commands, UnsafeCommands}, - commands::common::LogLevel, + commands::common::{LogFormat, LogLevel}, }; use clap::{CommandFactory, Parser}; use std::{collections::BTreeSet, time::Duration as StdDuration}; @@ -1262,7 +1262,7 @@ mod tests { // Log and Loki flags are global, so they land on the root. assert_eq!(cli.tracing.log_level, LogLevel::Info); - assert_eq!(cli.tracing.log_format, "console"); + assert_eq!(cli.tracing.log_format, LogFormat::Console); assert_eq!(cli.tracing.loki_service, "pluto"); assert!(cli.tracing.loki_addresses.is_empty()); From 261c4f175c3ab686d69591b11c698dd27de55b26 Mon Sep 17 00:00:00 2001 From: Lautaro Emanuel Date: Fri, 28 Aug 2026 18:06:43 -0300 Subject: [PATCH 7/9] Update Cargo.lock file --- Cargo.lock | 353 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 206 insertions(+), 147 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c8d0e56e..13a9cb6e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -121,7 +121,7 @@ dependencies = [ "either", "k256", "once_cell", - "rand 0.8.7", + "rand 0.8.8", "secp256k1 0.30.0", "serde", "serde_json", @@ -170,9 +170,9 @@ dependencies = [ [[package]] name = "alloy-core" -version = "1.6.1" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8421a5ee9019b6d89f92935d0f8facd9f6ca088b41d7cc47244a02ccde4545f" +checksum = "e88cf3d065edfb29a13278215b8521d3ef72a41e2432e019c1f0dd8e30649a5d" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", @@ -183,9 +183,9 @@ dependencies = [ [[package]] name = "alloy-dyn-abi" -version = "1.6.1" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a04eb4abc2b5074a18e687ee63918f407cc7990083cba9b999445f839796060" +checksum = "d9f1a3f2206f2ba4206fdeeddce6640eed3e26b8a13ac41444adb66b76d8e650" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -303,9 +303,9 @@ dependencies = [ [[package]] name = "alloy-json-abi" -version = "1.6.1" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cee30dd4c2f4b23f434fdf675e7bf9681b86768141277266c6f548ef25cba0a" +checksum = "208699c66c453fbb4c50d2e602f8ceff8a5f1fa48ac8b6ee3b6357fdc93da311" dependencies = [ "alloy-primitives", "alloy-sol-type-parser", @@ -369,9 +369,9 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "1.6.1" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f007e257069855bdf21d27762fd3f3705a613f805c9a08309bf353503f081d71" +checksum = "9c902f0ca3f8353c41e3e1ec3cf26be49412525bc48ab9d3c4710d7be4f01832" dependencies = [ "alloy-rlp", "bytes", @@ -381,7 +381,7 @@ dependencies = [ "fixed-cache", "foldhash 0.2.0", "hashbrown 0.17.1", - "indexmap 2.14.0", + "indexmap 2.14.1", "itoa", "k256", "keccak-asm", @@ -566,15 +566,15 @@ dependencies = [ "alloy-signer", "async-trait", "k256", - "rand 0.8.7", + "rand 0.8.8", "thiserror 2.0.20", ] [[package]] name = "alloy-sol-macro" -version = "1.6.1" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5655c38d5f84955bf727b2eeb62fddd91ebb98fd1d7ae6eb77f73ea88f9b9cf" +checksum = "fdcbd48d60e029be4a325c3a2f1312761caea4ed249f18ba9e8ed24ca1bf01e6" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", @@ -586,15 +586,15 @@ dependencies = [ [[package]] name = "alloy-sol-macro-expander" -version = "1.6.1" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6277c780e07b76951e09a59788dde230d1582612324177d11a43a61e21a6bb83" +checksum = "59c9f7c535f99a7e7b64cc520968b09ed14cec3715572fcc277cfbff602808cd" dependencies = [ "alloy-json-abi", "alloy-sol-macro-input", "const-hex", "heck", - "indexmap 2.14.0", + "indexmap 2.14.1", "proc-macro-error3", "proc-macro2", "quote", @@ -605,9 +605,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro-input" -version = "1.6.1" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9762b2ad3e5a0c09886de54fe549ab0056681df843cb082e2df7e1c0eb270d30" +checksum = "1abd404fbc12f543823005146b73fd07621bdc0baaa950d26995c543a9d73811" dependencies = [ "alloy-json-abi", "const-hex", @@ -623,9 +623,9 @@ dependencies = [ [[package]] name = "alloy-sol-type-parser" -version = "1.6.1" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da4c7130f0f01f4719678bda3db3bc7267fc2f7f9d0565e3bd964cd2bb45050d" +checksum = "40a7fd71864526bfeca8903010d5bb7fd28a0a4f5cc55818304c9cad8f0d63ab" dependencies = [ "serde", "winnow", @@ -633,9 +633,9 @@ dependencies = [ [[package]] name = "alloy-sol-types" -version = "1.6.1" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d96e74d6213180f78dbdccddce8af02a639c160c94b0a543fa35c77c58b8a7fc" +checksum = "adfc2ba3fb0e865de4934bcad6d37fc51e9ffcd5294be1322eab38e4494e051b" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -704,7 +704,7 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d0626c4f3b2028f7e8db32f53c22d9ecd5939f772317b7c4b6fe5219cb0589a" dependencies = [ - "darling", + "darling 0.23.0", "proc-macro2", "quote", "syn 2.0.119", @@ -1011,7 +1011,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1df2c09229cbc5a028b1d70e00fdb2acee28b1055dfb5ca73eea49c5a25c4e7c" dependencies = [ "num-traits", - "rand 0.8.7", + "rand 0.8.8", ] [[package]] @@ -1021,7 +1021,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" dependencies = [ "num-traits", - "rand 0.8.7", + "rand 0.8.8", ] [[package]] @@ -1031,7 +1031,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "246a225cc6131e9ee4f24619af0f19d67761fff15d7ccc22e42b80846e69449a" dependencies = [ "num-traits", - "rand 0.8.7", + "rand 0.8.8", ] [[package]] @@ -1041,7 +1041,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "367c9c827ed431bff6868b7aa926e05b16eb46603cc8b6e768e4a5553fa1d155" dependencies = [ "num-traits", - "rand 0.8.7", + "rand 0.8.8", ] [[package]] @@ -1175,7 +1175,7 @@ checksum = "82f6aeea286b8eb4dd3431a1be1b59d290ace00f5bfd8e2a159bc2a05e2c1667" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.4", ] [[package]] @@ -1544,34 +1544,32 @@ dependencies = [ [[package]] name = "bon" -version = "3.9.3" +version = "3.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a602c73c7b0148ec6d12af6fd5cc7a46e2eacc8878271a999abac56eed12f561" +checksum = "9e3fac94a66da67200398458a25412bcc3f9b6443b5119a6cad9cf3ccfcd8cc6" dependencies = [ "bon-macros", - "rustversion", ] [[package]] name = "bon-macros" -version = "3.9.3" +version = "3.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dee98b0db6a962de883bf5d20362dee4d7ca0d12fe39a7c6c73c844e1cd7c1f" +checksum = "d4654961ad0494e4774c5c60b4cb4cd0ae9b9d92d039d901638b1dba97ebebf5" dependencies = [ - "darling", + "darling 0.24.1", "ident_case", - "prettyplease", + "prettyplease 0.3.0", "proc-macro2", "quote", - "rustversion", - "syn 2.0.119", + "syn 3.0.4", ] [[package]] name = "borsh" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a88b7ea17d208c4193f2c1e6de3c35fe71f98c96982d5ced308bdcc749ff6e1f" +checksum = "553c5d846a6ba5150c65e3b1b8ec073bcf1abc20f9b7220de384a4443ea4e20a" dependencies = [ "borsh-derive", "bytes", @@ -1580,15 +1578,15 @@ dependencies = [ [[package]] name = "borsh-derive" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8f347189c62a579b8cd5f80714efa178f52e461dc2e6d701d264f5ff22e566c" +checksum = "12cdfe656708a01f89b451a7d36466e6fe6c414de0aa18fc54f864f6f9ca9f56" dependencies = [ "once_cell", "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.119", + "syn 3.0.4", ] [[package]] @@ -1679,9 +1677,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.4.3" +version = "1.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "509591b7bcd67f4ef775afad7662703b4935daaa6ec0e5605cfb1090b32a2b6d" +checksum = "0ad534f4357a5264cce5019c989cf66a4f0dc4e0d1b1d15f8aacec0ff7360273" dependencies = [ "find-msvc-tools", "jobserver", @@ -1714,12 +1712,12 @@ dependencies = [ [[package]] name = "chacha20" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81" +checksum = "65c35e4b699c7e15ccbe7ee35c005e4fc0a278d22238a2857e6ce2dadeda1b06" dependencies = [ "cfg-if", - "cpufeatures 0.3.0", + "cpufeatures 0.3.1", "rand_core 0.10.1", ] @@ -1819,7 +1817,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.4", ] [[package]] @@ -1845,9 +1843,9 @@ checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" [[package]] name = "combine" -version = "4.6.7" +version = "4.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +checksum = "cfc320937d09e6de266b31b9afb480f197d7a861be86be7cb2ea7e5d1bfffc5e" dependencies = [ "bytes", "memchr", @@ -1959,9 +1957,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +checksum = "5ca28b0ae3115b884660db4118d803791fd6756b6e88f39c0f3f7859060d7566" dependencies = [ "libc", ] @@ -1983,9 +1981,9 @@ checksum = "217698eaf96b4a3f0bc4f3662aaa55bdf913cd54d7204591faa790070c6d0853" [[package]] name = "crc32fast" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +checksum = "8498c871161e1742aaa9d52551b2d6ebdd4c3d45a3be423e3728f33b955be550" dependencies = [ "cfg-if", ] @@ -2177,8 +2175,18 @@ version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" dependencies = [ - "darling_core", - "darling_macro", + "darling_core 0.23.0", + "darling_macro 0.23.0", +] + +[[package]] +name = "darling" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed17f5901b6630b993ca003def43f2f8ef4014fc13b047b57aad617ff32bc2ec" +dependencies = [ + "darling_core 0.24.1", + "darling_macro 0.24.1", ] [[package]] @@ -2195,17 +2203,41 @@ dependencies = [ "syn 2.0.119", ] +[[package]] +name = "darling_core" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6837e2cf7485aaae18f86181d2f0e9a7ed297a025e220aeabf63fdebd3a2ddff" +dependencies = [ + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 3.0.4", +] + [[package]] name = "darling_macro" version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" dependencies = [ - "darling_core", + "darling_core 0.23.0", "quote", "syn 2.0.119", ] +[[package]] +name = "darling_macro" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ac7135c3ef02b2f7833bbeb1be5ba7f966dcde8a87c6b87f65a778d71a02785" +dependencies = [ + "darling_core 0.24.1", + "quote", + "syn 3.0.4", +] + [[package]] name = "dashmap" version = "6.2.1" @@ -2243,7 +2275,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c54e03a951783e8b327515db3f2a2fd0e3bed362a96b066f341ce66ed49b4ead" dependencies = [ "data-encoding", - "syn 3.0.3", + "syn 3.0.4", ] [[package]] @@ -2401,7 +2433,7 @@ checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.4", ] [[package]] @@ -2566,7 +2598,7 @@ checksum = "a65863d15a4ce2888bd2f0f543cc963d3879c3a022c8ee43f6141d479a3ac815" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.4", ] [[package]] @@ -2640,7 +2672,7 @@ version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "daf022360bdbe9456eda5f35718a50476d5b2a0d51a97ed4eae27420737a6fba" dependencies = [ - "darling", + "darling 0.23.0", "proc-macro2", "quote", "syn 2.0.119", @@ -2757,7 +2789,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" dependencies = [ "byteorder", - "rand 0.8.7", + "rand 0.8.8", "rustc-hex", "static_assertions", ] @@ -2770,12 +2802,13 @@ checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" [[package]] name = "flate2" -version = "1.1.9" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +checksum = "6e634e2e0ebac1ee034020da1ca582e17ffe4e0f5e985823721e168928136dcb" dependencies = [ "crc32fast", "miniz_oxide", + "zlib-rs", ] [[package]] @@ -2893,7 +2926,7 @@ checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.4", ] [[package]] @@ -3051,9 +3084,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.18" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "839c0e8a181239723652be9062bb56ca5bf5f64011f73b623f6f4fc59086a228" +checksum = "ef8e5e5a340588f4452631496976cf8636d4a7ecf600239fdc27615d2530bc16" dependencies = [ "atomic-waker", "bytes", @@ -3061,7 +3094,7 @@ dependencies = [ "futures-core", "futures-sink", "http", - "indexmap 2.14.0", + "indexmap 2.14.1", "slab", "tokio", "tokio-util", @@ -3294,9 +3327,9 @@ dependencies = [ [[package]] name = "hyper" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d22053281f852e11534f5198498373cbb59295120a20771d90f7ed1897490a72" +checksum = "27b501faa50e7a26c3d3560ca625132f4078a17771f4810baf70475ae48cbe43" dependencies = [ "atomic-waker", "bytes", @@ -3491,9 +3524,9 @@ checksum = "e590f038c1464a96894fd6d10127e90a8be4509f56ff7ecef851b15cee0b7caa" [[package]] name = "icu_provider" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92a7ed671a6aad807a8651a2e1782a6598fda9ce5185dd8158549e95a91c6428" +checksum = "d27bbb9d3abbefac45d55f647c9de1d44aafcd1186eb91879afef17c396c3e73" dependencies = [ "displaydoc", "icu_locale_core", @@ -3618,9 +3651,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.14.0" +version = "2.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +checksum = "07aa2048142242915a31d35844fb311e0e53fcca590c3a0a40dcf1b841fa09eb" dependencies = [ "equivalent", "hashbrown 0.17.1", @@ -3844,12 +3877,12 @@ dependencies = [ [[package]] name = "keccak" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffd9697dc4a9a62e2da93389f34400b77a28f0287711263cabb203b3ccb9c0e4" +checksum = "d8f198d1db720e4940b5a493201d199d9f24f568f8f746bd13706243a2f71598" dependencies = [ "cfg-if", - "cpufeatures 0.3.0", + "cpufeatures 0.3.1", ] [[package]] @@ -3891,9 +3924,9 @@ checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" [[package]] name = "libgit2-sys" -version = "0.18.7+1.9.6" +version = "0.18.8+1.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23c7391e4b9f4ffab1a624223cc1d7385ff9a678f490768add717de7ea2f4d89" +checksum = "7f7c568b25d7489bc3fb2988ed69ab111d2944d2f5fec3d5c987fe545ea97b50" dependencies = [ "cc", "libc", @@ -3970,7 +4003,7 @@ dependencies = [ "libp2p-swarm", "quick-protobuf", "quick-protobuf-codec", - "rand 0.8.7", + "rand 0.8.8", "rand_core 0.6.4", "thiserror 2.0.20", "tracing", @@ -4005,7 +4038,7 @@ dependencies = [ "parking_lot", "pin-project", "quick-protobuf", - "rand 0.8.7", + "rand 0.8.8", "rw-stream-sink", "thiserror 2.0.20", "tracing", @@ -4063,7 +4096,7 @@ dependencies = [ "k256", "multihash", "prost 0.14.4", - "rand 0.8.7", + "rand 0.8.8", "sha2", "thiserror 2.0.20", "tracing", @@ -4082,7 +4115,7 @@ dependencies = [ "libp2p-core", "libp2p-identity", "libp2p-swarm", - "rand 0.8.7", + "rand 0.8.8", "smallvec", "socket2 0.5.10", "tokio", @@ -4121,7 +4154,7 @@ dependencies = [ "multiaddr", "multihash", "quick-protobuf", - "rand 0.8.7", + "rand 0.8.8", "snow", "static_assertions", "thiserror 2.0.20", @@ -4141,7 +4174,7 @@ dependencies = [ "libp2p-core", "libp2p-identity", "libp2p-swarm", - "rand 0.8.7", + "rand 0.8.8", "tracing", "web-time", ] @@ -4160,7 +4193,7 @@ dependencies = [ "libp2p-tls", "quinn", "quinn-proto", - "rand 0.8.7", + "rand 0.8.8", "ring", "rustls", "socket2 0.5.10", @@ -4186,7 +4219,7 @@ dependencies = [ "libp2p-swarm", "quick-protobuf", "quick-protobuf-codec", - "rand 0.8.7", + "rand 0.8.8", "static_assertions", "thiserror 2.0.20", "tracing", @@ -4205,7 +4238,7 @@ dependencies = [ "libp2p-core", "libp2p-identity", "libp2p-swarm", - "rand 0.8.7", + "rand 0.8.8", "smallvec", "tracing", ] @@ -4225,7 +4258,7 @@ dependencies = [ "libp2p-identity", "libp2p-swarm-derive", "multistream-select", - "rand 0.8.7", + "rand 0.8.8", "smallvec", "tokio", "tracing", @@ -4343,9 +4376,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.33" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" +checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" [[package]] name = "loki-api" @@ -4359,9 +4392,9 @@ dependencies = [ [[package]] name = "lru" -version = "0.18.2" +version = "0.18.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d2f2f9b4ba7e6b24d95e7e899329d35be83bcded72c8540cdd5368932d1d90a" +checksum = "0d317b4b9eb398e6acce275758ec6125535505e7a146fb1a9b8bda2451b0ff4c" dependencies = [ "hashbrown 0.17.1", ] @@ -4385,9 +4418,9 @@ dependencies = [ [[package]] name = "match-lookup" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "757aee279b8bdbb9f9e676796fd459e4207a1f986e87886700abf589f5abf771" +checksum = "549e39695cc0b640f3cb378053832db3d2133422d49e8dcae5c866a2aaf1f730" dependencies = [ "proc-macro2", "quote", @@ -4439,9 +4472,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.9" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +checksum = "b63fbc4a50860e98e7b2aa7804ded1db5cbc3aff9193adaff57a6931bf7c4b4c" dependencies = [ "adler2", "simd-adler32", @@ -4784,11 +4817,11 @@ dependencies = [ "eventsource-stream", "futures-core", "http", - "indexmap 2.14.0", + "indexmap 2.14.1", "oas3", - "prettyplease", + "prettyplease 0.2.37", "proc-macro2", - "quick-xml", + "quick-xml 0.42.0", "quote", "regex", "reqwest 0.13.4", @@ -4998,7 +5031,7 @@ checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455" dependencies = [ "fixedbitset", "hashbrown 0.15.5", - "indexmap 2.14.0", + "indexmap 2.14.1", ] [[package]] @@ -5128,7 +5161,7 @@ dependencies = [ "pluto-testutil", "prost 0.14.4", "prost-types 0.14.4", - "rand 0.8.7", + "rand 0.8.8", "regex", "reqwest 0.13.4", "serde", @@ -5181,8 +5214,8 @@ dependencies = [ "pluto-ssz", "pluto-testutil", "pluto-tracing", - "quick-xml", - "rand 0.8.7", + "quick-xml 0.41.0", + "rand 0.8.8", "reqwest 0.13.4", "serde", "serde_json", @@ -5216,7 +5249,7 @@ dependencies = [ "pluto-p2p", "pluto-ssz", "pluto-testutil", - "rand 0.8.7", + "rand 0.8.8", "reqwest 0.13.4", "serde", "serde_json", @@ -5301,7 +5334,7 @@ dependencies = [ "pluto-tracing", "prost 0.14.4", "prost-types 0.14.4", - "rand 0.8.7", + "rand 0.8.8", "regex", "reqwest 0.13.4", "serde", @@ -5325,7 +5358,7 @@ dependencies = [ "blst", "hex", "pluto-eth2api", - "rand 0.8.7", + "rand 0.8.8", "rand_core 0.6.4", "test-case", "thiserror 2.0.20", @@ -5363,7 +5396,7 @@ dependencies = [ "pluto-tracing", "prost 0.14.4", "prost-types 0.14.4", - "rand 0.8.7", + "rand 0.8.8", "serde", "serde_json", "sha2", @@ -5440,7 +5473,7 @@ dependencies = [ "pluto-k1util", "pluto-ssz", "pluto-testutil", - "rand 0.8.7", + "rand 0.8.8", "regex", "reqwest 0.13.4", "scrypt", @@ -5479,7 +5512,7 @@ version = "1.7.1" dependencies = [ "blst", "hex", - "rand 0.8.7", + "rand 0.8.8", "rand_core 0.6.4", "serde", "serde_json", @@ -5544,7 +5577,7 @@ dependencies = [ "pluto-testutil", "pluto-tracing", "prost 0.14.4", - "rand 0.8.7", + "rand 0.8.8", "reqwest 0.13.4", "serde_json", "tempfile", @@ -5639,7 +5672,7 @@ dependencies = [ "pluto-testutil", "prost 0.14.4", "prost-types 0.14.4", - "rand 0.8.7", + "rand 0.8.8", "test-case", "thiserror 2.0.20", "tokio", @@ -5660,7 +5693,7 @@ dependencies = [ "pluto-eth2util", "pluto-p2p", "pluto-tracing", - "rand 0.8.7", + "rand 0.8.8", "reqwest 0.13.4", "serde_json", "thiserror 2.0.20", @@ -5700,7 +5733,7 @@ dependencies = [ "pluto-crypto", "pluto-eth2api", "pluto-eth2util", - "rand 0.8.7", + "rand 0.8.8", "reqwest 0.13.4", "serde", "serde_json", @@ -5813,6 +5846,16 @@ dependencies = [ "syn 2.0.119", ] +[[package]] +name = "prettyplease" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bfe0f4c752e450fc2faf62654f1c134747922825d5b04ca717b8874f41a40c0" +dependencies = [ + "proc-macro2", + "syn 3.0.4", +] + [[package]] name = "primitive-types" version = "0.12.2" @@ -5933,7 +5976,7 @@ dependencies = [ "log", "multimap", "petgraph", - "prettyplease", + "prettyplease 0.2.37", "prost 0.14.4", "prost-types 0.14.4", "regex", @@ -6017,6 +6060,16 @@ dependencies = [ "serde", ] +[[package]] +name = "quick-xml" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41b1177fdf999d2321d3fb46ff47159d9c1fb9ad66a4879f8c50a0b504615e9b" +dependencies = [ + "memchr", + "serde", +] + [[package]] name = "quinn" version = "0.11.11" @@ -6105,9 +6158,9 @@ checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" [[package]] name = "rand" -version = "0.8.7" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22f6172bdec972074665ed81ed53b71da00bfc44b65a753cfde883ec4c702a1a" +checksum = "e058c7de0b26af77780c769414d6257830bb240f3c38477dbc2c16e5f54d6d4c" dependencies = [ "libc", "rand_chacha 0.3.1", @@ -6132,7 +6185,7 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80" dependencies = [ - "chacha20 0.10.1", + "chacha20 0.10.2", "getrandom 0.4.3", "rand_core 0.10.1", ] @@ -6268,7 +6321,7 @@ checksum = "92ecd8964f8453721699a1ed72037b0db49ce2f5a5138486ee89bed6f67cdf3a" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.4", ] [[package]] @@ -6461,7 +6514,7 @@ dependencies = [ "parity-scale-codec", "primitive-types", "proptest", - "rand 0.8.7", + "rand 0.8.8", "rand 0.9.5", "rlp", "ruint-macro", @@ -6595,9 +6648,9 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" [[package]] name = "rustls-webpki" -version = "0.103.14" +version = "0.103.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0527518605e68109d875e248ea259b6758801cf165e4b2c2733ae3b51f12535a" +checksum = "f3c3cf1d8b1e7d4927e2d154c3fcb02979afb9939629c62cd9048d4f07b60ac2" dependencies = [ "aws-lc-rs", "ring", @@ -6719,7 +6772,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b50c5943d326858130af85e049f2661ba3c78b26589b8ab98e65e80ae44a1252" dependencies = [ "bitcoin_hashes", - "rand 0.8.7", + "rand 0.8.8", "secp256k1-sys 0.10.1", "serde", ] @@ -6840,7 +6893,7 @@ checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.4", ] [[package]] @@ -6849,7 +6902,7 @@ version = "1.0.151" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" dependencies = [ - "indexmap 2.14.0", + "indexmap 2.14.1", "itoa", "memchr", "serde", @@ -6876,7 +6929,7 @@ checksum = "8d3b1629de253c70a0508c3899572da79ca359fdab27c7920ff00406df418906" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.4", ] [[package]] @@ -6911,7 +6964,7 @@ dependencies = [ "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.14.0", + "indexmap 2.14.1", "jiff", "schemars 0.9.0", "schemars 1.2.2", @@ -6927,7 +6980,7 @@ version = "3.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8705578779c2b6bd90d84d66eb2e206b708b1a4d7b9f17641b293545bf1c7e46" dependencies = [ - "darling", + "darling 0.23.0", "proc-macro2", "quote", "syn 2.0.119", @@ -6939,7 +6992,7 @@ version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.14.0", + "indexmap 2.14.1", "itoa", "ryu", "serde", @@ -6984,7 +7037,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be176f1a57ce4e3d31c1a166222d9768de5954f811601fb7ca06fc8203905ce1" dependencies = [ "digest 0.11.3", - "keccak 0.2.1", + "keccak 0.2.2", ] [[package]] @@ -7199,9 +7252,9 @@ dependencies = [ [[package]] name = "syn" -version = "3.0.3" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" +checksum = "e6275cddf4610d1775e6d1fe9469b2e77d0f39fd98fb7450901b821e0c53649f" dependencies = [ "proc-macro2", "quote", @@ -7210,9 +7263,9 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "1.6.1" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "083be3061e64d362cbe6ef12cfe1307ba3884326d8856448fe8a120fa2c44ebf" +checksum = "e452eb8cb83fc8b81597eb07c8d39f770d04905af9c5bffce8bea7213df29960" dependencies = [ "paste", "proc-macro2", @@ -7412,7 +7465,7 @@ checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.4", ] [[package]] @@ -7523,7 +7576,7 @@ checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.4", ] [[package]] @@ -7569,7 +7622,7 @@ version = "1.1.4+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3aace63f4bbcdfc2c965b059de67119c89c4017a70d633be6c104910f67056f5" dependencies = [ - "indexmap 2.14.0", + "indexmap 2.14.1", "serde_core", "serde_spanned", "toml_datetime", @@ -7593,7 +7646,7 @@ version = "0.25.13+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6975367e4d2ef766d86af01ffad14b622fecc8d4357a998fbc4deb6e9bacaf9b" dependencies = [ - "indexmap 2.14.0", + "indexmap 2.14.1", "toml_datetime", "toml_parser", "winnow", @@ -7662,7 +7715,7 @@ checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" dependencies = [ "futures-core", "futures-util", - "indexmap 2.14.0", + "indexmap 2.14.1", "pin-project-lite", "slab", "sync_wrapper", @@ -7814,7 +7867,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8840ad4d852e325d3afa7fde8a50b2412f89dce47d7eb291c0cc7f87cd040f38" dependencies = [ - "darling", + "darling 0.23.0", "proc-macro2", "quote", "syn 2.0.119", @@ -7987,9 +8040,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.24.1" +version = "1.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cefc03fd367c0c6d4305de1b312cf00248c4114f4a0418ce6a6af769e3b0bd9" +checksum = "b5772d71c9be8a8a6ac2117d949c5b224c1b72241bb611d9a3012edcf8af7812" dependencies = [ "getrandom 0.4.3", "js-sys", @@ -8019,7 +8072,7 @@ version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240e4b81c20a1d6d50d1d7265c658dfbd204e8b9ac4d80f3c931f39462196335" dependencies = [ - "darling", + "darling 0.23.0", "proc-macro-error3", "proc-macro2", "quote", @@ -8646,7 +8699,7 @@ dependencies = [ "nohash-hasher", "parking_lot", "pin-project", - "rand 0.8.7", + "rand 0.8.8", "static_assertions", ] @@ -8783,15 +8836,21 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.5" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f212a141d820099d57ffafb9569be9617a6f27d3dc881fbee8fb56642f917a9" +checksum = "34df6fc39dbd26ddc9c10e6a2984476e13acce22e64e4487636ef494369225da" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.4", ] +[[package]] +name = "zlib-rs" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34b31d188d9d685a4f9c7b46d6e36631b07058d2cfe190267adce54dc230bf12" + [[package]] name = "zmij" version = "1.0.23" From a24a528a7792fa2c5d97406c99f58e55ccc8f7cc Mon Sep 17 00:00:00 2001 From: Lautaro Emanuel Date: Fri, 28 Aug 2026 18:09:40 -0300 Subject: [PATCH 8/9] Inline `serve_relay` into `run` --- crates/cli/src/commands/relay.rs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/crates/cli/src/commands/relay.rs b/crates/cli/src/commands/relay.rs index a38bad69..f6e371a1 100644 --- a/crates/cli/src/commands/relay.rs +++ b/crates/cli/src/commands/relay.rs @@ -199,20 +199,13 @@ pub struct RelayP2PArgs { pub async fn run( config: pluto_relay_server::config::Config, ct: CancellationToken, -) -> Result<(), CliError> { - serve_relay(&config, ct).await -} - -async fn serve_relay( - config: &pluto_relay_server::config::Config, - ct: CancellationToken, ) -> Result<(), CliError> { info!("{LICENSE}"); info!(config = ?config); - let key = load_or_create_key(config)?; + let key = load_or_create_key(&config)?; - pluto_relay_server::p2p::run_relay_p2p_node(config, key, ct) + pluto_relay_server::p2p::run_relay_p2p_node(&config, key, ct) .await .map_err(Into::into) } From d509bde3a6f5015798922c490e9137670bf2d025 Mon Sep 17 00:00:00 2001 From: Lautaro Emanuel Date: Fri, 28 Aug 2026 18:11:47 -0300 Subject: [PATCH 9/9] Inline `run_workflow` into `run` --- crates/cli/src/commands/run.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/crates/cli/src/commands/run.rs b/crates/cli/src/commands/run.rs index ccce35c5..78c60c92 100644 --- a/crates/cli/src/commands/run.rs +++ b/crates/cli/src/commands/run.rs @@ -827,13 +827,6 @@ fn validate_vc_tls(cert: &str, key: &str) -> Result<()> { pub async fn run(config: RunConfig, ct: CancellationToken) -> Result<()> { info!("{LICENSE}"); - run_workflow(config, ct).await -} - -/// The long-running validator workflow: bridges the parsed [`RunConfig`] into -/// [`pluto_app::node::AppConfig`] and drives the node until `ct` fires (signal -/// handling lives in `main`). -async fn run_workflow(config: RunConfig, ct: CancellationToken) -> Result<()> { let app_config = build_app_config(config)?; pluto_app::node::App::new(app_config).run(ct).await?; Ok(())