From bde3c168e21b4a62cc3ceed7ad3c89fd16564cb6 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Fri, 16 Sep 2022 08:54:04 +0000 Subject: [PATCH] Add `lcli block-root` tool (#3580) ## Issue Addressed NA ## Proposed Changes Adds a simple tool for computing the block root of some block from a beacon-API or a file. This is useful for benchmarking. ## Additional Info NA --- lcli/src/block_root.rs | 103 ++++++++++++++++++++++++++++++++++ lcli/src/main.rs | 39 +++++++++++++ lcli/src/transition_blocks.rs | 7 ++- 3 files changed, 147 insertions(+), 2 deletions(-) create mode 100644 lcli/src/block_root.rs diff --git a/lcli/src/block_root.rs b/lcli/src/block_root.rs new file mode 100644 index 000000000..7631872c5 --- /dev/null +++ b/lcli/src/block_root.rs @@ -0,0 +1,103 @@ +//! # Block Root +//! +//! Use this tool to compute the canonical root of a `SignedBeaconBlock`. This is most likely only +//! useful for benchmarking with tools like `flamegraph`. +//! +//! It can load a block from a SSZ file or download it from a beaconAPI. +//! +//! Logging output is controlled via the `RUST_LOG` environment variable. For example, `export +//! RUST_LOG=debug`. +//! +//! ## Examples +//! +//! Download a block and re-compute the canonical root 5,000 times. +//! +//! ```ignore +//! lcli block-root \ +//! --beacon-url http://localhost:5052 \ +//! --block-id 0x3d887d30ee25c9c1ce7621ec30a7b49b07d6a03200df9c7206faca52a533f432 \ +//! --runs 5000 +//! ``` +//! +//! Load a block from SSZ and compute the canonical root once. +//! +//! ```ignore +//! lcli block-root \ +//! --block-path /tmp/block.ssz \ +//! --runs 1 +//! ``` +use crate::transition_blocks::load_from_ssz_with; +use clap::ArgMatches; +use clap_utils::{parse_optional, parse_required}; +use environment::Environment; +use eth2::{types::BlockId, BeaconNodeHttpClient, SensitiveUrl, Timeouts}; +use std::path::PathBuf; +use std::time::{Duration, Instant}; +use types::{EthSpec, FullPayload, SignedBeaconBlock}; + +const HTTP_TIMEOUT: Duration = Duration::from_secs(5); + +pub fn run(mut env: Environment, matches: &ArgMatches) -> Result<(), String> { + let spec = &T::default_spec(); + let executor = env.core_context().executor; + + /* + * Parse (most) CLI arguments. + */ + + let block_path: Option = parse_optional(matches, "block-path")?; + let beacon_url: Option = parse_optional(matches, "beacon-url")?; + let runs: usize = parse_required(matches, "runs")?; + + info!("Using {} spec", T::spec_name()); + info!("Doing {} runs", runs); + + /* + * Load the block and pre-state from disk or beaconAPI URL. + */ + + let block: SignedBeaconBlock> = match (block_path, beacon_url) { + (Some(block_path), None) => { + info!("Block path: {:?}", block_path); + load_from_ssz_with(&block_path, spec, SignedBeaconBlock::from_ssz_bytes)? + } + (None, Some(beacon_url)) => { + let block_id: BlockId = parse_required(matches, "block-id")?; + let client = BeaconNodeHttpClient::new(beacon_url, Timeouts::set_all(HTTP_TIMEOUT)); + executor + .handle() + .ok_or("shutdown in progress")? + .block_on(async move { + let block = client + .get_beacon_blocks(block_id) + .await + .map_err(|e| format!("Failed to download block: {:?}", e))? + .ok_or_else(|| format!("Unable to locate block at {:?}", block_id))? + .data; + Ok::<_, String>(block) + }) + .map_err(|e| format!("Failed to complete task: {:?}", e))? + } + _ => return Err("must supply --block-path *or* --beacon-url".into()), + }; + + /* + * Perform the core "runs". + */ + + let mut block_root = None; + for i in 0..runs { + let start = Instant::now(); + + block_root = Some(block.canonical_root()); + + let duration = Instant::now().duration_since(start); + info!("Run {}: {:?}", i, duration); + } + + if let Some(block_root) = block_root { + info!("Block root is {:?}", block_root); + } + + Ok(()) +} diff --git a/lcli/src/main.rs b/lcli/src/main.rs index e6a4eeeac..84b951e3f 100644 --- a/lcli/src/main.rs +++ b/lcli/src/main.rs @@ -1,5 +1,6 @@ #[macro_use] extern crate log; +mod block_root; mod change_genesis_time; mod check_deposit_data; mod create_payload_header; @@ -714,6 +715,42 @@ fn main() { .help("List of Attestations to convert to indexed form (JSON)"), ) ) + .subcommand( + SubCommand::with_name("block-root") + .about("Computes the block root of some block") + .arg( + Arg::with_name("block-path") + .long("block-path") + .value_name("PATH") + .takes_value(true) + .conflicts_with("beacon-url") + .requires("pre-state-path") + .help("Path to load a SignedBeaconBlock from file as SSZ."), + ) + .arg( + Arg::with_name("beacon-url") + .long("beacon-url") + .value_name("URL") + .takes_value(true) + .help("URL to a beacon-API provider."), + ) + .arg( + Arg::with_name("block-id") + .long("block-id") + .value_name("BLOCK_ID") + .takes_value(true) + .requires("beacon-url") + .help("Identifier for a block as per beacon-API standards (slot, root, etc.)"), + ) + .arg( + Arg::with_name("runs") + .long("runs") + .value_name("INTEGER") + .takes_value(true) + .default_value("1") + .help("Number of repeat runs, useful for benchmarking."), + ) + ) .get_matches(); let result = matches @@ -799,6 +836,8 @@ fn run( .map_err(|e| format!("Failed to run insecure-validators command: {}", e)), ("indexed-attestations", Some(matches)) => indexed_attestations::run::(matches) .map_err(|e| format!("Failed to run indexed-attestations command: {}", e)), + ("block-root", Some(matches)) => block_root::run::(env, matches) + .map_err(|e| format!("Failed to run block-root command: {}", e)), (other, _) => Err(format!("Unknown subcommand {}. See --help.", other)), } } diff --git a/lcli/src/transition_blocks.rs b/lcli/src/transition_blocks.rs index 793bdb642..dc825d2c0 100644 --- a/lcli/src/transition_blocks.rs +++ b/lcli/src/transition_blocks.rs @@ -6,6 +6,9 @@ //! It can load states and blocks from file or pull them from a beaconAPI. Objects pulled from a //! beaconAPI can be saved to disk to reduce future calls to that server. //! +//! Logging output is controlled via the `RUST_LOG` environment variable. For example, `export +//! RUST_LOG=debug`. +//! //! ## Examples //! //! ### Run using a block from a beaconAPI @@ -124,8 +127,8 @@ pub fn run(mut env: Environment, matches: &ArgMatches) -> Result< let (mut pre_state, mut state_root_opt, block) = match (pre_state_path, block_path, beacon_url) { (Some(pre_state_path), Some(block_path), None) => { - info!("Block path: {:?}", pre_state_path); - info!("Pre-state path: {:?}", block_path); + info!("Block path: {:?}", block_path); + info!("Pre-state path: {:?}", pre_state_path); let pre_state = load_from_ssz_with(&pre_state_path, spec, BeaconState::from_ssz_bytes)?; let block = load_from_ssz_with(&block_path, spec, SignedBeaconBlock::from_ssz_bytes)?; (pre_state, None, block)