From 9538ad29e520ec6e631d7adf30b1b043c5559361 Mon Sep 17 00:00:00 2001 From: Janito Vaqueiro Ferreira Filho Date: Fri, 6 May 2022 00:31:52 -0300 Subject: [PATCH] feat(util): add a `zebra-tip-height` utility (#4289) * Move `init_tracing` to `lib.rs` Allow it to be reused by other binaries. * Fix `hex` dependency inclusion in `zebra-chain` It would previously fail because `hex` was being included without the `serde` feature, even though we required that for `transparent::Script`. * Implement `FromStr` for `Network` Make it easy to receive it as a command-line parameter. * Add `zebra-tip-height` utility Obtains the chain tip height of a directory containing Zebra state. * Remove Tokio dependency from `zebra-utils` It wasn't actually used by the `zebra-tip-height` utility. * Remove `BoxStateService` type alias It's not needed if we don't return the unused state service. This also allows removing the `tower` dependency. * Remove unnecessary attribute Leftover from copied code. * Make `cache_dir` argument optional Fallback to the default Zebra state cache directory. * Remove added newline Minor formatting fix, to avoid adding an unnecessary newline. * Move `tip-height` command into `zebrad` Make it available as a sub-command in `zebrad`. * Make some zebrad sub-commands only log warnings Co-authored-by: teor --- zebra-chain/Cargo.toml | 6 +- zebra-chain/src/parameters/network.rs | 20 +++++- zebra-utils/src/bin/zebra-checkpoints/main.rs | 9 +-- zebra-utils/src/lib.rs | 9 +++ zebrad/src/application.rs | 8 ++- zebrad/src/commands.rs | 41 +++++++++++- zebrad/src/commands/tip_height.rs | 64 +++++++++++++++++++ 7 files changed, 139 insertions(+), 18 deletions(-) create mode 100644 zebrad/src/commands/tip_height.rs diff --git a/zebra-chain/Cargo.toml b/zebra-chain/Cargo.toml index 4f08eb45..8319822b 100644 --- a/zebra-chain/Cargo.toml +++ b/zebra-chain/Cargo.toml @@ -9,7 +9,7 @@ edition = "2021" [features] default = [] -proptest-impl = ["proptest", "proptest-derive", "zebra-test", "rand", "rand_chacha", "tokio", "hex/serde"] +proptest-impl = ["proptest", "proptest-derive", "zebra-test", "rand", "rand_chacha", "tokio"] bench = ["zebra-test"] [dependencies] @@ -29,7 +29,7 @@ fpe = "0.5.1" futures = "0.3.21" group = "0.11.0" halo2 = { package = "halo2_proofs", version = "=0.1.0-beta.4" } -hex = "0.4.3" +hex = { version = "0.4.3", features = ["serde"] } incrementalmerkletree = "0.3.0-beta.2" itertools = "0.10.3" jubjub = "0.8.0" @@ -77,8 +77,6 @@ itertools = "0.10.3" spandoc = "0.2.2" tracing = "0.1.31" -hex = { version = "0.4.3", features = ["serde"] } - proptest = "0.10.1" proptest-derive = "0.3.0" rand = { version = "0.8.5", package = "rand" } diff --git a/zebra-chain/src/parameters/network.rs b/zebra-chain/src/parameters/network.rs index 1cf127e1..c10dfd58 100644 --- a/zebra-chain/src/parameters/network.rs +++ b/zebra-chain/src/parameters/network.rs @@ -1,4 +1,6 @@ -use std::{convert::From, fmt}; +use std::{convert::From, fmt, str::FromStr}; + +use thiserror::Error; use crate::{block::Height, parameters::NetworkUpgrade::Canopy}; @@ -117,3 +119,19 @@ impl Default for Network { Network::Mainnet } } + +impl FromStr for Network { + type Err = InvalidNetworkError; + + fn from_str(string: &str) -> Result { + match string.to_lowercase().as_str() { + "mainnet" => Ok(Network::Mainnet), + "testnet" => Ok(Network::Testnet), + _ => Err(InvalidNetworkError(string.to_owned())), + } + } +} + +#[derive(Clone, Debug, Error)] +#[error("Invalid network: {0}")] +pub struct InvalidNetworkError(String); diff --git a/zebra-utils/src/bin/zebra-checkpoints/main.rs b/zebra-utils/src/bin/zebra-checkpoints/main.rs index 60b0e05a..5a5b7087 100644 --- a/zebra-utils/src/bin/zebra-checkpoints/main.rs +++ b/zebra-utils/src/bin/zebra-checkpoints/main.rs @@ -12,22 +12,15 @@ use color_eyre::eyre::{ensure, Result}; use serde_json::Value; use std::process::Stdio; use structopt::StructOpt; -use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; use zebra_chain::block; +use zebra_utils::init_tracing; #[cfg(unix)] use std::os::unix::process::ExitStatusExt; mod args; -/// Initialise tracing using its defaults. -fn init_tracing() { - tracing_subscriber::Registry::default() - .with(tracing_error::ErrorLayer::default()) - .init(); -} - /// Return a new `zcash-cli` command, including the `zebra-checkpoints` /// passthrough arguments. fn passthrough_cmd() -> std::process::Command { diff --git a/zebra-utils/src/lib.rs b/zebra-utils/src/lib.rs index 5bf220bc..d811cf1c 100644 --- a/zebra-utils/src/lib.rs +++ b/zebra-utils/src/lib.rs @@ -4,3 +4,12 @@ #![doc(html_favicon_url = "https://zfnd.org/wp-content/uploads/2022/03/zebra-favicon-128.png")] #![doc(html_logo_url = "https://zfnd.org/wp-content/uploads/2022/03/zebra-icon.png")] #![doc(html_root_url = "https://doc.zebra.zfnd.org/zebra_utils")] + +use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; + +/// Initialise tracing using its defaults. +pub fn init_tracing() { + tracing_subscriber::Registry::default() + .with(tracing_error::ErrorLayer::default()) + .init(); +} diff --git a/zebrad/src/application.rs b/zebrad/src/application.rs index 10f74313..48770173 100644 --- a/zebrad/src/application.rs +++ b/zebrad/src/application.rs @@ -342,14 +342,18 @@ impl Application for ZebradApp { .as_ref() .expect("config is loaded before register_components"); - let default_filter = if command.verbose { "debug" } else { "info" }; + let default_filter = command + .command + .as_ref() + .map(|zcmd| zcmd.default_tracing_filter(command.verbose)) + .unwrap_or("warn"); let is_server = command .command .as_ref() .map(ZebradCmd::is_server) .unwrap_or(false); - // Ignore the tracing filter for short-lived commands + // Ignore the configured tracing filter for short-lived utility commands let mut tracing_config = cfg_ref.tracing.clone(); if is_server { // Override the default tracing filter based on the command-line verbosity. diff --git a/zebrad/src/commands.rs b/zebrad/src/commands.rs index a2ac67b7..9dcc2745 100644 --- a/zebrad/src/commands.rs +++ b/zebrad/src/commands.rs @@ -4,12 +4,13 @@ mod copy_state; mod download; mod generate; mod start; +mod tip_height; mod version; use self::ZebradCmd::*; use self::{ copy_state::CopyStateCmd, download::DownloadCmd, generate::GenerateCmd, start::StartCmd, - version::VersionCmd, + tip_height::TipHeightCmd, version::VersionCmd, }; use crate::config::ZebradConfig; @@ -46,6 +47,10 @@ pub enum ZebradCmd { #[options(help = "start the application")] Start(StartCmd), + /// The `tip-height` subcommand + #[options(help = "get the block height of Zebra's persisted chain state")] + TipHeight(TipHeightCmd), + /// The `version` subcommand #[options(help = "display version information")] Version(VersionCmd), @@ -54,12 +59,41 @@ pub enum ZebradCmd { impl ZebradCmd { /// Returns true if this command is a server command. /// + /// Servers load extra components, and use the configured tracing filter. + /// /// For example, `Start` acts as a Zcash node. pub(crate) fn is_server(&self) -> bool { + // List all the commands, so new commands have to make a choice here match self { - // List all the commands, so new commands have to make a choice here + // Commands that run as a configured server CopyState(_) | Start(_) => true, - Download(_) | Generate(_) | Help(_) | Version(_) => false, + + // Utility commands that don't use server components + Download(_) | Generate(_) | Help(_) | TipHeight(_) | Version(_) => false, + } + } + + /// Returns the default log level for this command, based on the `verbose` command line flag. + /// + /// Some commands need to be quiet by default. + pub(crate) fn default_tracing_filter(&self, verbose: bool) -> &'static str { + let only_show_warnings = match self { + // Commands that generate quiet output by default. + // This output: + // - is used by automated tools, or + // - needs to be read easily. + Generate(_) | TipHeight(_) | Help(_) | Version(_) => true, + + // Commands that generate informative logging output by default. + CopyState(_) | Download(_) | Start(_) => false, + }; + + if only_show_warnings && !verbose { + "warn" + } else if only_show_warnings || !verbose { + "info" + } else { + "debug" } } } @@ -72,6 +106,7 @@ impl Runnable for ZebradCmd { Generate(cmd) => cmd.run(), ZebradCmd::Help(cmd) => cmd.run(), Start(cmd) => cmd.run(), + TipHeight(cmd) => cmd.run(), Version(cmd) => cmd.run(), } } diff --git a/zebrad/src/commands/tip_height.rs b/zebrad/src/commands/tip_height.rs new file mode 100644 index 00000000..733f82e8 --- /dev/null +++ b/zebrad/src/commands/tip_height.rs @@ -0,0 +1,64 @@ +//! `tip-height` subcommand - prints the block height of Zebra's persisted chain state. +//! +//! Prints the chain tip height stored in Zebra's state. This is useful for developers to inspect +//! Zebra state directories. + +use std::path::PathBuf; + +use abscissa_core::{Command, Options, Runnable}; +use color_eyre::eyre::{eyre, Result}; + +use zebra_chain::{block, chain_tip::ChainTip, parameters::Network}; +use zebra_state::LatestChainTip; + +use crate::prelude::app_config; + +/// `zebra-tip-height` subcommand +#[derive(Command, Debug, Options)] +pub struct TipHeightCmd { + /// Path to Zebra's cached state. + #[options(help = "path to directory with the Zebra chain state")] + cache_dir: Option, + + /// The network to obtain the chain tip. + #[options(default = "mainnet", help = "the network of the chain to load")] + network: Network, +} + +impl Runnable for TipHeightCmd { + /// `tip-height` sub-command entrypoint. + /// + /// Reads the chain tip height from a cache directory with Zebra's state. + #[allow(clippy::print_stdout)] + fn run(&self) { + match self.load_tip_height() { + Ok(height) => println!("{}", height.0), + Err(error) => tracing::error!("Failed to read chain tip height from state: {error}"), + } + } +} + +impl TipHeightCmd { + /// Load the chain tip height from the state cache directory. + fn load_tip_height(&self) -> Result { + let latest_chain_tip = self.load_latest_chain_tip(); + + latest_chain_tip + .best_tip_height() + .ok_or_else(|| eyre!("State directory doesn't have a chain tip block")) + } + + /// Starts a state service using the `cache_dir` and `network` from the provided arguments. + fn load_latest_chain_tip(&self) -> LatestChainTip { + let mut config = app_config().state.clone(); + + if let Some(cache_dir) = self.cache_dir.clone() { + config.cache_dir = cache_dir; + } + + let (_state_service, _read_state_service, latest_chain_tip, _chain_tip_change) = + zebra_state::init(config, self.network); + + latest_chain_tip + } +}