Skip to content

Commit

Permalink
fix ci: miscellaneous chores and fixes (#3)
Browse files Browse the repository at this point in the history
* build(header-accumulator): fix workspace crate dev-dependency import

* build: install protoc in CI tests

* fix(header-accumulator): fix cargo fmt

* chore(flat-files-decoder): fix clippy::doc-lazy-continuation warning

* chore(firehose-client): fix clippy::unnecessary_to_owned warning

* chore(flat-files-decoder): fix clippy::filter_next warning

* chore(sf-protos): fix code snippet in doc comment

* chore(header-accumulator): fix clippy::bool_assert_comparison

* chore(firehose-client): add streaming support for beacon and ethereum

* build(forrestrie): add firehose-client as dependency

* build(forrestrie): add reqwest as dependency

* chore(forrestrie): make historical roots index public

* chore(forrestrie): add fetch and verify block example

* chore(forrestrie): add verify block inclusion proof example

* chore(forrestrie): add historical state roots proof example

* chore(forrestrie): add historical summary proof example

* chore(forrestrie): add block roots proof example

* chore(forrestrie): add block roots only proof example

* chore(forrestrie): add empty slot hashes example

* chore(firehose-client): add fetch ethereum block example

* chore(firehose-client): add stream ethereum blocks example

* chore(firehose-client): add stream beacon blocks example

* chore(forehose-client): add fetch beacon block example

* docs: add note on examples to README

* build(workspace): use workspace dependency imports for legibility

* ci(tests): remove coverage requirements for now
  • Loading branch information
suchapalaver committed Nov 29, 2024
1 parent 3ad630a commit bb72327
Show file tree
Hide file tree
Showing 10 changed files with 635 additions and 512 deletions.
8 changes: 5 additions & 3 deletions forrestrie/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@ path = "src/main.rs"
[dependencies]
merkle_proof.workspace = true
primitive-types.workspace = true
serde.workspace = true
serde = { workspace = true, features = ["derive"] }
sf-protos = { path = "../sf-protos" }
tree_hash.workspace = true
types.workspace = true
sf-protos = { path = "../sf-protos" }

[dev-dependencies]
firehose-client = { path = "../firehose-client" }
insta.workspace = true
reqwest.workspace = true
serde_json.workspace = true
tokio.workspace = true
tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }
88 changes: 88 additions & 0 deletions forrestrie/examples/block_roots_only_proof.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
//! # Inclusion Proofs for Block Roots Only
//!
//! For this test, we want to prove that a block_root is included in the `block_summary_root`
//! field of a [`HistoricalSummary`] from the [`BeaconState`] historical_summaries List.
//! A [`HistoricalSummary`] contains the roots of two Merkle trees, `block_summary_root` and
//! `state_summary_root`.
//! We are interested in the `block_summary_root` tree, whose leaves consists of the
//! [`BeaconBlockHeader`] roots for one era (8192 consecutive slots).
//! For this test, we are using the state at the first [`Slot`] of an era to build the proof.
//! We chose this [`Slot`] because it is the first [`Slot`] of an era, and all of the
//! [`BeaconBlockHeader`] roots needed to construct the [`HistoricalSummary`] for this era are
//! available in `state.block_roots`.
use forrestrie::beacon_state::{HeadState, CAPELLA_START_ERA, HISTORY_TREE_DEPTH};
use merkle_proof::verify_merkle_proof;
use types::{historical_summary::HistoricalSummary, MainnetEthSpec};

#[tokio::main]
async fn main() {
// You may need to update the slot being queried as the state data is updated.
// Multiply a recent era by 8192 to get the slot number.
const SLOT: u64 = 10182656;
let url = format!("https://www.lightclientdata.org/eth/v2/debug/beacon/states/{SLOT}");
println!("Requesting state for slot {SLOT} ... (this can take a while!)");
let response = reqwest::get(url).await.unwrap();
let transition_state: HeadState<MainnetEthSpec> = if response.status().is_success() {
let json_response: serde_json::Value = response.json().await.unwrap();
serde_json::from_value(json_response).unwrap()
} else {
panic!("Request failed with status: {}", response.status());
};

// There are 8192 slots in an era. 8790016 / 8192 = 1073.
let proof_era = transition_state.data().slot().as_usize() / 8192usize;

// In this test we are using the `historical_summaries` (introduced in Capella) for
// verification, so we need to subtract the Capella start era to get the correct index.
let proof_era_index = proof_era - CAPELLA_START_ERA - 1;

// We are going to prove that the block_root at index 4096 is included in the block_roots
// tree.
// This is an arbitrary choice just for test purposes.
let index = 4096usize;

// Buffer of most recent 8192 block roots:
let block_root_at_index = *transition_state.data().block_roots().get(index).unwrap();

let proof = transition_state
.compute_block_roots_proof_only(index)
.unwrap();

// To verify the proof, we use the state from a later slot.
// The HistoricalSummary used to generate this proof is included in the historical_summaries
// list of this state.
let url = "https://www.lightclientdata.org/eth/v2/debug/beacon/states/head".to_string();
println!("Requesting head state ... (this can take a while!)");
let response = reqwest::get(url).await.unwrap();
let state: HeadState<MainnetEthSpec> = if response.status().is_success() {
let json_response: serde_json::Value = response.json().await.unwrap();
serde_json::from_value(json_response).unwrap()
} else {
panic!("Request failed with status: {}", response.status());
};

// The verifier retrieves the block_summary_root for the historical_summary and verifies the
// proof against it.
let historical_summary: &HistoricalSummary = state
.data()
.historical_summaries()
.unwrap()
.get(proof_era_index)
.unwrap();

let block_roots_summary_root = historical_summary.block_summary_root();

assert!(
verify_merkle_proof(
block_root_at_index,
&proof,
HISTORY_TREE_DEPTH,
index,
block_roots_summary_root
),
"Merkle proof verification failed"
);

println!("Block roots only merkle proof verification succeeded");
}
87 changes: 87 additions & 0 deletions forrestrie/examples/block_roots_proofs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
//! # Block Roots Proofs
//!
//! We want to prove that a block_root is included in a [`HistoricalSummary`] from the [`BeaconState`] historical_summaries List.
//! A [`HistoricalSummary`] contains the roots of two Merkle trees, block_summary_root and state_summary root.
//! We are interested in the block_summary tree, whose leaves consists of the [`BeaconBlockHeader`] roots for one era (8192 consecutive slots).
//! For example, we could have used the state at [`Slot`] 8790016, which is the first [`Slot`] of era 1073, to build the proof.
//! Because it is the first [`Slot`] of an era, all of the [`BeaconBlockHeader`] roots needed to construct the
//! [`HistoricalSummary`] for this era are available in state.block_roots.
//! The last block root in the `block_roots` buffer is the block root of the previous block.
use forrestrie::beacon_state::{
HeadState, CAPELLA_START_ERA, HISTORICAL_SUMMARY_TREE_DEPTH, SLOTS_PER_HISTORICAL_ROOT,
};
use merkle_proof::verify_merkle_proof;
use tree_hash::TreeHash;
use types::MainnetEthSpec;
#[tokio::main]
async fn main() {
// You may need to update the slot being queried as the state data is updated.
// Multiply a recent era by 8192 to get the slot number.
const SLOT: u64 = 10182656;
let url = format!("https://www.lightclientdata.org/eth/v2/debug/beacon/states/{SLOT}");
println!("Requesting state for slot {SLOT} ... (this can take a while!)");
let response = reqwest::get(url).await.unwrap();
let transition_state: HeadState<MainnetEthSpec> = if response.status().is_success() {
let json_response: serde_json::Value = response.json().await.unwrap();
serde_json::from_value(json_response).unwrap()
} else {
panic!("Request failed with status: {}", response.status());
};

// There are 8192 slots in an era.
let proof_era = transition_state.data().slot().as_usize() / SLOTS_PER_HISTORICAL_ROOT;

// In this test we are using the historical_summaries (introduced in Capella) for verification,
// so we need to subtract the Capella start era to get the correct index.
let proof_era_index = proof_era - CAPELLA_START_ERA - 1;

// We are going to prove that the block_root at index 4096 is included in the block_roots tree.
// This is an arbitrary choice just for test purposes.
let index = 4096usize;

// Buffer of most recent 8192 block roots:
let block_root_at_index = *transition_state.data().block_roots().get(index).unwrap();

assert!(
transition_state.block_roots_contain_entire_era().unwrap(),
"Block roots buffer does not contain an entire era"
);

let proof = transition_state.compute_block_roots_proof(index).unwrap();

// To verify the proof, we use the state from a later slot.
// The HistoricalSummary used to generate this proof is included in the historical_summaries list of this state.
let url = "https://www.lightclientdata.org/eth/v2/debug/beacon/states/head".to_string();
println!("Requesting head state ... (this can take a while!)");
let response = reqwest::get(url).await.unwrap();
let state: HeadState<MainnetEthSpec> = if response.status().is_success() {
let json_response: serde_json::Value = response.json().await.unwrap();
serde_json::from_value(json_response).unwrap()
} else {
panic!("Request failed with status: {}", response.status());
};

// The verifier retrieves the block_summary_root for the historical_summary and verifies the proof against it.
let historical_summary = state
.data()
.historical_summaries()
.unwrap()
.get(proof_era_index)
.unwrap();

let historical_summary_root = historical_summary.tree_hash_root();

assert!(
verify_merkle_proof(
block_root_at_index,
&proof,
HISTORICAL_SUMMARY_TREE_DEPTH,
index,
historical_summary_root
),
"Merkle proof verification failed"
);

println!("Block roots proof verified successfully");
}
Loading

0 comments on commit bb72327

Please sign in to comment.