From ee18f6a9f7c3692cf2ee2769eb11f3b5929a06be Mon Sep 17 00:00:00 2001 From: Michael Sproul Date: Tue, 31 May 2022 06:09:08 +0000 Subject: [PATCH] Add `lcli indexed-attestations` (#3221) ## Proposed Changes It's reasonably often that we want to manually convert an attestation to indexed form. This PR adds an `lcli` command for doing this, using an SSZ state and a list of JSON attestations (as extracted from a JSON block) as input. --- lcli/src/indexed_attestations.rs | 48 ++++++++++++++++++++++++++++++++ lcli/src/main.rs | 23 +++++++++++++++ 2 files changed, 71 insertions(+) create mode 100644 lcli/src/indexed_attestations.rs diff --git a/lcli/src/indexed_attestations.rs b/lcli/src/indexed_attestations.rs new file mode 100644 index 000000000..6e3bfa51d --- /dev/null +++ b/lcli/src/indexed_attestations.rs @@ -0,0 +1,48 @@ +use clap::ArgMatches; +use clap_utils::parse_required; +use state_processing::common::get_indexed_attestation; +use std::fs::File; +use std::io::Read; +use std::path::{Path, PathBuf}; +use types::*; + +fn read_file_bytes(filename: &Path) -> Result, String> { + let mut bytes = vec![]; + let mut file = File::open(filename) + .map_err(|e| format!("Unable to open {}: {}", filename.display(), e))?; + file.read_to_end(&mut bytes) + .map_err(|e| format!("Unable to read {}: {}", filename.display(), e))?; + Ok(bytes) +} + +pub fn run(matches: &ArgMatches) -> Result<(), String> { + let spec = &T::default_spec(); + + let state_file: PathBuf = parse_required(matches, "state")?; + let attestations_file: PathBuf = parse_required(matches, "attestations")?; + + let mut state = BeaconState::::from_ssz_bytes(&read_file_bytes(&state_file)?, spec) + .map_err(|e| format!("Invalid state: {:?}", e))?; + state + .build_all_committee_caches(spec) + .map_err(|e| format!("{:?}", e))?; + + let attestations: Vec> = + serde_json::from_slice(&read_file_bytes(&attestations_file)?) + .map_err(|e| format!("Invalid attestation list: {:?}", e))?; + + let indexed_attestations = attestations + .into_iter() + .map(|att| { + let committee = state.get_beacon_committee(att.data.slot, att.data.index)?; + get_indexed_attestation(committee.committee, &att) + }) + .collect::, _>>() + .map_err(|e| format!("Error constructing indexed attestation: {:?}", e))?; + + let string_output = serde_json::to_string_pretty(&indexed_attestations) + .map_err(|e| format!("Unable to convert to JSON: {:?}", e))?; + println!("{}", string_output); + + Ok(()) +} diff --git a/lcli/src/main.rs b/lcli/src/main.rs index 996bfc0ac..0a36768d1 100644 --- a/lcli/src/main.rs +++ b/lcli/src/main.rs @@ -6,6 +6,7 @@ mod create_payload_header; mod deploy_deposit_contract; mod eth1_genesis; mod generate_bootnode_enr; +mod indexed_attestations; mod insecure_validators; mod interop_genesis; mod new_testnet; @@ -598,6 +599,26 @@ fn main() { .help("The number of nodes to divide the validator keys to"), ) ) + .subcommand( + SubCommand::with_name("indexed-attestations") + .about("Convert attestations to indexed form, using the committees from a state.") + .arg( + Arg::with_name("state") + .long("state") + .value_name("SSZ_STATE") + .takes_value(true) + .required(true) + .help("BeaconState to generate committees from (SSZ)"), + ) + .arg( + Arg::with_name("attestations") + .long("attestations") + .value_name("JSON_ATTESTATIONS") + .takes_value(true) + .required(true) + .help("List of Attestations to convert to indexed form (JSON)"), + ) + ) .get_matches(); let result = matches @@ -679,6 +700,8 @@ fn run( .map_err(|e| format!("Failed to run generate-bootnode-enr command: {}", e)), ("insecure-validators", Some(matches)) => insecure_validators::run(matches) .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)), (other, _) => Err(format!("Unknown subcommand {}. See --help.", other)), } }