diff --git a/lcli/src/indexed_attestations.rs b/lcli/src/indexed_attestations.rs new file mode 100644 index 0000000000..6e3bfa51d3 --- /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 996bfc0ac7..0a36768d15 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)), } }