From 20d53d7868810a11ce378ed1172487273b56acc7 Mon Sep 17 00:00:00 2001 From: pedro bufulin Date: Fri, 29 Nov 2024 12:32:04 -0300 Subject: [PATCH 1/2] feat: add headState serialization to file --- .../examples/dump_state_to_json.rs | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 crates/forrestrie-examples/examples/dump_state_to_json.rs diff --git a/crates/forrestrie-examples/examples/dump_state_to_json.rs b/crates/forrestrie-examples/examples/dump_state_to_json.rs new file mode 100644 index 00000000..5391fd1e --- /dev/null +++ b/crates/forrestrie-examples/examples/dump_state_to_json.rs @@ -0,0 +1,34 @@ +/* +Copyright 2024-, Semiotic AI, Inc. +SPDX-License-Identifier: Apache-2.0 +*/ + +//! Dumps a single head state into json. Useful for not relying on fetching it, +//! decreases the time necessary for verifying a proof. +use std::{fs::File, io::Write}; + +use forrestrie::beacon_state::HeadState; +use types::MainnetEthSpec; + +const LIGHT_CLIENT_DATA_URL: &str = + "https://www.lightclientdata.org/eth/v2/debug/beacon/states/head"; + +#[tokio::main] +async fn main() { + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Get the head state of the Beacon chain from a Beacon API provider. + let state_handle = tokio::spawn(async move { + let url = LIGHT_CLIENT_DATA_URL.to_string(); + println!("Requesting head state ... (this can take a while!)"); + let response = reqwest::get(url).await.unwrap(); + let head_state: HeadState = response.json().await.unwrap(); + head_state + }); + + let mut file = File::create("head_state.json").expect("Failed to create file"); + let head_state = state_handle.await.unwrap(); + let json_string = + serde_json::to_string(&head_state).expect("Failed to serialize head_state to JSON"); + file.write_all(json_string.as_bytes()) + .expect("Failed to write to file"); +} From 80d82817f238915523956e9f3c35ac58dede9cd5 Mon Sep 17 00:00:00 2001 From: pedro bufulin Date: Fri, 29 Nov 2024 12:39:54 -0300 Subject: [PATCH 2/2] feat: add example post capella era verify read from local storage of state and roots --- .../post_capella_era_from_deserialize.rs | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 crates/forrestrie-examples/examples/post_capella_era_from_deserialize.rs diff --git a/crates/forrestrie-examples/examples/post_capella_era_from_deserialize.rs b/crates/forrestrie-examples/examples/post_capella_era_from_deserialize.rs new file mode 100644 index 00000000..6c9c8b13 --- /dev/null +++ b/crates/forrestrie-examples/examples/post_capella_era_from_deserialize.rs @@ -0,0 +1,98 @@ +// Copyright 2024-, Semiotic AI, Inc. +// SPDX-License-Identifier: Apache-2.0 + +//! In this example, we verify a complete era of beacon blocks to be canonical. +//! We first read from storage a complete era of beacon blocks (8192 beacon blocks), compute the associated historical summary and +//! compare it against the historical summary from a current consensus stated. + +use std::{fs::File, io::Read}; + +use forrestrie::beacon_state::{ + compute_block_roots_proof_only, HeadState, CAPELLA_START_ERA, HISTORY_TREE_DEPTH, + SLOTS_PER_HISTORICAL_ROOT, +}; +use primitive_types::H256; +use trin_validation::constants::EPOCH_SIZE; +use types::{historical_summary::HistoricalSummary, MainnetEthSpec}; + +use merkle_proof::verify_merkle_proof; +use merkle_proof::MerkleTree; + +/// This slot is the starting slot of the Beacon block era. +const BEACON_SLOT_NUMBER: u64 = 10436608; + +#[tokio::main] +async fn main() { + println!("this also takes a while because the HeadState is a 700mb file"); + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Get the head state of the Beacon chain from deserializing storage. + let mut file = File::open("head_state.json").expect("Failed to open file"); + let mut json_string = String::new(); + file.read_to_string(&mut json_string) + .expect("Failed to read file"); + + let head_state: HeadState = + serde_json::from_str(&json_string).expect("Failed to deserialize head_state from JSON"); + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // get the block roots from deserializing storage. + let mut file = File::open("beacon_block_roots.json").expect("Failed to open file"); + let mut json_string = String::new(); + file.read_to_string(&mut json_string) + .expect("Failed to read file"); + + let beacon_block_roots: Vec = + serde_json::from_str(&json_string).expect("Failed to deserialize head_state from JSON"); + + let era = BEACON_SLOT_NUMBER as usize / SLOTS_PER_HISTORICAL_ROOT; + let era_index = era - CAPELLA_START_ERA; + + // Caculate the block_summary_root from the beacon blocks. Note that the block_summary_root is a field in the HistoricalSummary. + let beacon_block_roots_tree_hash_root = + MerkleTree::create(&beacon_block_roots, HISTORY_TREE_DEPTH).hash(); + + let historical_summary: &HistoricalSummary = head_state + .data() + .historical_summaries() + .unwrap() + .get(era_index) + .unwrap(); + + let block_summary_root = historical_summary.block_summary_root(); + println!( + "{:?}, {:?}", + beacon_block_roots_tree_hash_root, block_summary_root + ); + + // if the roots match, it means that the whole era is included in the historical_summary + assert_eq!(beacon_block_roots_tree_hash_root, block_summary_root); + + // It is also possible to make a verifiable merkle proof for it + + let index = BEACON_SLOT_NUMBER as usize % SLOTS_PER_HISTORICAL_ROOT as usize; + let proof = + compute_block_roots_proof_only::(&beacon_block_roots, index).unwrap(); + + let block_roots_tree_hash_root = historical_summary.block_summary_root(); + assert_eq!(proof.len(), HISTORY_TREE_DEPTH); + + // Verify the proof for a single block + let historical_root_index: i32 = (BEACON_SLOT_NUMBER as usize % EPOCH_SIZE as usize) + .try_into() + .unwrap(); + let block_root = beacon_block_roots[historical_root_index as usize]; + + assert!( + verify_merkle_proof( + // This is the equivalent beacon root of the slot in BEACON_SLOT_NUMBER constant + block_root, + &proof, // the proof of the block's inclusion in the block roots + HISTORY_TREE_DEPTH, // the depth of the block roots tree + index, // the index of the block in the era + block_roots_tree_hash_root // The root of the block roots + ), + "Merkle proof verification failed" + ); + + println!("successfully verified merkle proof") +}