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
This commit is contained in:
Paul Hauner 2022-09-16 08:54:04 +00:00
parent 2cd3e3a768
commit bde3c168e2
3 changed files with 147 additions and 2 deletions

103
lcli/src/block_root.rs Normal file
View File

@ -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<T: EthSpec>(mut env: Environment<T>, matches: &ArgMatches) -> Result<(), String> {
let spec = &T::default_spec();
let executor = env.core_context().executor;
/*
* Parse (most) CLI arguments.
*/
let block_path: Option<PathBuf> = parse_optional(matches, "block-path")?;
let beacon_url: Option<SensitiveUrl> = 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<T, FullPayload<T>> = 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(())
}

View File

@ -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<T: EthSpec>(
.map_err(|e| format!("Failed to run insecure-validators command: {}", e)),
("indexed-attestations", Some(matches)) => indexed_attestations::run::<T>(matches)
.map_err(|e| format!("Failed to run indexed-attestations command: {}", e)),
("block-root", Some(matches)) => block_root::run::<T>(env, matches)
.map_err(|e| format!("Failed to run block-root command: {}", e)),
(other, _) => Err(format!("Unknown subcommand {}. See --help.", other)),
}
}

View File

@ -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<T: EthSpec>(mut env: Environment<T>, 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)