From f8a336bf7cd154783dc8bf08912589a960f3ef5f Mon Sep 17 00:00:00 2001 From: pedro bufulin Date: Thu, 22 Aug 2024 18:10:06 -0300 Subject: [PATCH 01/48] refactor(execution_block): ReceiptWrapper fields as snake_case --- crates/forrestrie/src/execution_block.rs | 89 ++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 crates/forrestrie/src/execution_block.rs diff --git a/crates/forrestrie/src/execution_block.rs b/crates/forrestrie/src/execution_block.rs new file mode 100644 index 00000000..06d5ac4d --- /dev/null +++ b/crates/forrestrie/src/execution_block.rs @@ -0,0 +1,89 @@ +use ethers::prelude::*; +use reth::primitives::{Log, TxType}; +use serde::{Deserialize, Deserializer, Serialize}; + +// reth::primitives::proofs::calculate_receipt_root; + +#[derive(Debug, Deserialize, Serialize)] +pub struct ReceiptWrapper { + #[serde(rename = "type")] + #[serde(deserialize_with = "str_to_type")] + pub tx_type: TxType, + #[serde(rename = "blockHash")] + pub block_hash: String, + #[serde(rename = "blockNumber")] + pub block_number: String, + pub logs: Vec, + #[serde(rename = "cumulativeGasUsed")] + pub cumulative_gas_used: U256, + #[serde(deserialize_with = "status_to_bool")] + pub status: bool, +} + +fn status_to_bool<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + let status_str: &str = Deserialize::deserialize(deserializer)?; + match status_str { + "0x1" => Ok(true), + "0x0" => Ok(false), + _ => Err(serde::de::Error::custom("Invalid status value")), + } +} + +// Custom deserialization function for TxType +fn str_to_type<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + let tx_type_str: &str = Deserialize::deserialize(deserializer)?; + // Convert the hex string (without the "0x" prefix) to u8 + let tx_type_value = u8::from_str_radix(tx_type_str.trim_start_matches("0x"), 16) + .map_err(|_| serde::de::Error::custom("Invalid tx_type value"))?; + TxType::try_from(tx_type_value).map_err(|_| serde::de::Error::custom("Invalid tx_type value")) +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct ReceiptsFromBlock { + pub result: Vec, +} + +#[cfg(test)] +mod tests { + + use std::cell::LazyCell; + + use crate::beacon_block::BlockWrapper; + + use super::*; + + /// Deneb block JSON file shared among contributors. + /// The block hash is `0x5dde05ab1da7f768ed3ea2d53c6fa0d79c0c2283e52bb0d00842a4bdbf14c0ab`. + const DENEB_BLOCK_JSON: &str = include_str!("../../../bb-8786333.json"); + const BLOCK_RECEIPTS_JSON: &str = include_str!("../../../eb-19584570-receipts.json"); + + const BLOCK_WRAPPER: LazyCell = LazyCell::new(|| { + serde_json::from_str(DENEB_BLOCK_JSON).expect( + "For this spike we are using a Deneb block JSON file that has been shared among contributors", + ) + }); + + const RECEIPTS: LazyCell = LazyCell::new(|| { + serde_json::from_str(BLOCK_RECEIPTS_JSON).expect( + "This is all the receipt data from a block, fetch with eth_getBlockReceipts method", + ) + }); + + #[test] + fn test_parse_wrapped_receipt_into_reth_receipt() { + let block_wrapper: &LazyCell = &BLOCK_WRAPPER; + let block = &block_wrapper.data.message; + + let block_body = block.body_deneb().unwrap(); + let payload = &block_body.execution_payload; + let receits_root = payload.execution_payload.receipts_root; + + let receipts = &RECEIPTS; + } +} From c2245a3388d129da02e0cf65d96926021b904250 Mon Sep 17 00:00:00 2001 From: pedro bufulin Date: Mon, 26 Aug 2024 12:28:33 -0300 Subject: [PATCH 02/48] feat(execution_block): try_from ReceiptsJson into ReceiptWithBloom, hash_builder_root --- crates/forrestrie/src/execution_block.rs | 113 ++++++++++++++++++++--- 1 file changed, 99 insertions(+), 14 deletions(-) diff --git a/crates/forrestrie/src/execution_block.rs b/crates/forrestrie/src/execution_block.rs index 06d5ac4d..3d33453b 100644 --- a/crates/forrestrie/src/execution_block.rs +++ b/crates/forrestrie/src/execution_block.rs @@ -1,11 +1,13 @@ +use alloy_rlp::Encodable; use ethers::prelude::*; -use reth::primitives::{Log, TxType}; +use reth::primitives::{Bloom, Log, Receipt, ReceiptWithBloom, TxType, B256}; +use reth_trie::{root::adjust_index_for_rlp, HashBuilder, Nibbles}; use serde::{Deserialize, Deserializer, Serialize}; // reth::primitives::proofs::calculate_receipt_root; #[derive(Debug, Deserialize, Serialize)] -pub struct ReceiptWrapper { +pub struct ReceiptJson { #[serde(rename = "type")] #[serde(deserialize_with = "str_to_type")] pub tx_type: TxType, @@ -18,6 +20,42 @@ pub struct ReceiptWrapper { pub cumulative_gas_used: U256, #[serde(deserialize_with = "status_to_bool")] pub status: bool, + // TODO: should we trust logsBloom provided or calculate it from the logs? + #[serde(rename = "logsBloom")] + pub logs_bloom: Bloom, +} + +impl TryFrom<&ReceiptJson> for ReceiptWithBloom { + type Error = String; + + fn try_from(receipt_json: &ReceiptJson) -> Result { + let cumulative_gas_used = receipt_json + .cumulative_gas_used + .try_into() + .map_err(|_| "Failed to convert U256 to u64".to_string())?; + + let receipt = Receipt { + tx_type: receipt_json.tx_type, + success: receipt_json.status, + cumulative_gas_used, + logs: receipt_json.logs.clone(), + // #[cfg(feature = "optimism")] + // deposit_nonce: None, // Handle Optimism-specific fields as necessary + // #[cfg(feature = "optimism")] + // deposit_receipt_version: None, + }; + + // Create the ReceiptWithBloom struct + Ok(ReceiptWithBloom { + bloom: receipt_json.logs_bloom, + receipt, + }) + } +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct ReceiptsFromBlock { + pub result: Vec, } fn status_to_bool<'de, D>(deserializer: D) -> Result @@ -32,7 +70,6 @@ where } } -// Custom deserialization function for TxType fn str_to_type<'de, D>(deserializer: D) -> Result where D: Deserializer<'de>, @@ -44,17 +81,31 @@ where TxType::try_from(tx_type_value).map_err(|_| serde::de::Error::custom("Invalid tx_type value")) } -#[derive(Debug, Deserialize, Serialize)] -pub struct ReceiptsFromBlock { - pub result: Vec, +pub fn hash_builder_root(receipts: &[ReceiptWithBloom]) -> B256 { + let mut index_buffer = Vec::new(); + let mut value_buffer = Vec::new(); + + let mut hb = HashBuilder::default(); + let receipts_len = receipts.len(); + for i in 0..receipts_len { + let index = adjust_index_for_rlp(i, receipts_len); + + index_buffer.clear(); + index.encode(&mut index_buffer); + + value_buffer.clear(); + receipts[index].encode_inner(&mut value_buffer, false); + hb.add_leaf(Nibbles::unpack(&index_buffer), &value_buffer); + } + + hb.root() } #[cfg(test)] mod tests { - use std::cell::LazyCell; - use crate::beacon_block::BlockWrapper; + use std::cell::LazyCell; use super::*; @@ -69,7 +120,7 @@ mod tests { ) }); - const RECEIPTS: LazyCell = LazyCell::new(|| { + const BLOCK_RECEIPTS: LazyCell = LazyCell::new(|| { serde_json::from_str(BLOCK_RECEIPTS_JSON).expect( "This is all the receipt data from a block, fetch with eth_getBlockReceipts method", ) @@ -77,13 +128,47 @@ mod tests { #[test] fn test_parse_wrapped_receipt_into_reth_receipt() { - let block_wrapper: &LazyCell = &BLOCK_WRAPPER; - let block = &block_wrapper.data.message; + let block_receipts: &LazyCell = &BLOCK_RECEIPTS; + let receipts_with_bloom: Result, String> = block_receipts + .result + .iter() + .map(|receipt_json| ReceiptWithBloom::try_from(receipt_json)) + .collect::, _>>(); + + // Check that the conversion was successful + assert!( + receipts_with_bloom.is_ok(), + "Conversion failed with error: {:?}", + receipts_with_bloom.err().unwrap() + ); + } - let block_body = block.body_deneb().unwrap(); + #[test] + fn test_compute_receipts_trie() { + let block_wrapper: &LazyCell = &BLOCK_WRAPPER; + let block: &::types::BeaconBlock<::types::MainnetEthSpec> = &block_wrapper.data.message; + let block_body: &::types::BeaconBlockBodyDeneb<::types::MainnetEthSpec> = + block.body_deneb().unwrap(); let payload = &block_body.execution_payload; - let receits_root = payload.execution_payload.receipts_root; + let receipts_root = payload.execution_payload.receipts_root; + + let block_receipts: &LazyCell = &BLOCK_RECEIPTS; + let receipts_with_bloom: Result, String> = block_receipts + .result + .iter() + .map(|receipt_json| ReceiptWithBloom::try_from(receipt_json)) + .collect::, _>>(); - let receipts = &RECEIPTS; + match receipts_with_bloom { + Ok(receipts) => { + let root: reth::revm::primitives::FixedBytes<32> = hash_builder_root(&receipts); + let root_h256 = H256::from(root.0); + assert_eq!(root_h256, receipts_root, "Roots do not match!"); + } + Err(e) => { + // Handle the error (e.g., by logging or panicking) + panic!("Failed to convert receipts: {}", e); + } + } } } From ad7d0d5f01f7edc3a227acb942b2b8ec3151b283 Mon Sep 17 00:00:00 2001 From: pedro bufulin Date: Tue, 10 Sep 2024 20:40:14 -0300 Subject: [PATCH 03/48] feat: generate proofs with proof_retainer inside hashBuilder --- crates/forrestrie/src/execution_block.rs | 43 ++++++++++++++++++++---- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/crates/forrestrie/src/execution_block.rs b/crates/forrestrie/src/execution_block.rs index 3d33453b..d5257040 100644 --- a/crates/forrestrie/src/execution_block.rs +++ b/crates/forrestrie/src/execution_block.rs @@ -1,7 +1,7 @@ use alloy_rlp::Encodable; use ethers::prelude::*; -use reth::primitives::{Bloom, Log, Receipt, ReceiptWithBloom, TxType, B256}; -use reth_trie::{root::adjust_index_for_rlp, HashBuilder, Nibbles}; +use reth::primitives::{Bloom, Log, Receipt, ReceiptWithBloom, TxType}; +use reth_trie_common::{proof::ProofRetainer, root::adjust_index_for_rlp, HashBuilder, Nibbles}; use serde::{Deserialize, Deserializer, Serialize}; // reth::primitives::proofs::calculate_receipt_root; @@ -81,24 +81,48 @@ where TxType::try_from(tx_type_value).map_err(|_| serde::de::Error::custom("Invalid tx_type value")) } -pub fn hash_builder_root(receipts: &[ReceiptWithBloom]) -> B256 { +// builds the trie to generate proofs from the Receipts +// generate a different root. Make sure that the source of receipts sorts them by `logIndex` +pub fn build_trie_with_proofs( + receipts: &[ReceiptWithBloom], + receipts_idx: &[usize], +) -> HashBuilder { let mut index_buffer = Vec::new(); let mut value_buffer = Vec::new(); - let mut hb = HashBuilder::default(); + // Initialize ProofRetainer with the target nibbles (the keys for which we want proofs) + let targets: Vec = receipts_idx + .iter() + .map(|&i| { + let index = adjust_index_for_rlp(i, receipts.len()); + index.encode(&mut index_buffer); + Nibbles::unpack(&index_buffer) + }) + .collect(); + + let proof_retainer: ProofRetainer = ProofRetainer::new(targets); + let mut hb = HashBuilder::default().with_proof_retainer(proof_retainer); + let receipts_len = receipts.len(); + for i in 0..receipts_len { let index = adjust_index_for_rlp(i, receipts_len); index_buffer.clear(); + let nibbles = Nibbles::unpack(&index_buffer); index.encode(&mut index_buffer); value_buffer.clear(); receipts[index].encode_inner(&mut value_buffer, false); hb.add_leaf(Nibbles::unpack(&index_buffer), &value_buffer); + // Safely mutate the ProofRetainer if it exists + if let Some(ref mut proof_retainer) = hb.proof_retainer { + // Retain the proof if the current nibbles match any target + proof_retainer.retain(&nibbles, &value_buffer); + } } - hb.root() + hb } #[cfg(test)] @@ -144,7 +168,7 @@ mod tests { } #[test] - fn test_compute_receipts_trie() { + fn test_compute_receipts_trie_root_and_proof() { let block_wrapper: &LazyCell = &BLOCK_WRAPPER; let block: &::types::BeaconBlock<::types::MainnetEthSpec> = &block_wrapper.data.message; let block_body: &::types::BeaconBlockBodyDeneb<::types::MainnetEthSpec> = @@ -159,9 +183,12 @@ mod tests { .map(|receipt_json| ReceiptWithBloom::try_from(receipt_json)) .collect::, _>>(); + // computes the root and verify against existing data + let mut hb: HashBuilder; match receipts_with_bloom { Ok(receipts) => { - let root: reth::revm::primitives::FixedBytes<32> = hash_builder_root(&receipts); + hb = build_trie_with_proofs(&receipts, &[0, 1, 2]); + let root: reth::revm::primitives::FixedBytes<32> = hb.root(); let root_h256 = H256::from(root.0); assert_eq!(root_h256, receipts_root, "Roots do not match!"); } @@ -170,5 +197,7 @@ mod tests { panic!("Failed to convert receipts: {}", e); } } + + // verify the proofs } } From be54b0a0b37337c4a39c4db41e4033da084e5880 Mon Sep 17 00:00:00 2001 From: pedro bufulin Date: Fri, 13 Sep 2024 18:23:55 -0300 Subject: [PATCH 04/48] feat: generate receipt proof with ProofRetainer and verify it against targets --- crates/forrestrie/src/execution_block.rs | 81 ++++++++++++++++++++---- 1 file changed, 69 insertions(+), 12 deletions(-) diff --git a/crates/forrestrie/src/execution_block.rs b/crates/forrestrie/src/execution_block.rs index d5257040..46c1010c 100644 --- a/crates/forrestrie/src/execution_block.rs +++ b/crates/forrestrie/src/execution_block.rs @@ -25,6 +25,19 @@ pub struct ReceiptJson { pub logs_bloom: Bloom, } +// represents leafs that are being generated proofs on +pub struct Target { + pub nibbles: Nibbles, + pub value: Vec, +} + +impl Target { + // Constructor to create a new Target + pub fn new(nibbles: Nibbles, value: Vec) -> Self { + Target { nibbles, value } + } +} + impl TryFrom<&ReceiptJson> for ReceiptWithBloom { type Error = String; @@ -95,6 +108,7 @@ pub fn build_trie_with_proofs( .iter() .map(|&i| { let index = adjust_index_for_rlp(i, receipts.len()); + index_buffer.clear(); index.encode(&mut index_buffer); Nibbles::unpack(&index_buffer) }) @@ -106,20 +120,15 @@ pub fn build_trie_with_proofs( let receipts_len = receipts.len(); for i in 0..receipts_len { - let index = adjust_index_for_rlp(i, receipts_len); - index_buffer.clear(); - let nibbles = Nibbles::unpack(&index_buffer); + value_buffer.clear(); + + let index = adjust_index_for_rlp(i, receipts_len); index.encode(&mut index_buffer); - value_buffer.clear(); receipts[index].encode_inner(&mut value_buffer, false); + // NOTICE: if the ProofRetainer is set, add_leaf automatically retains the proofs for the targets hb.add_leaf(Nibbles::unpack(&index_buffer), &value_buffer); - // Safely mutate the ProofRetainer if it exists - if let Some(ref mut proof_retainer) = hb.proof_retainer { - // Retain the proof if the current nibbles match any target - proof_retainer.retain(&nibbles, &value_buffer); - } } hb @@ -128,6 +137,10 @@ pub fn build_trie_with_proofs( #[cfg(test)] mod tests { + use k256::pkcs8::der::Encode; + use reth::primitives::B256; + use reth_trie_common::proof::verify_proof; + use crate::beacon_block::BlockWrapper; use std::cell::LazyCell; @@ -180,17 +193,39 @@ mod tests { let receipts_with_bloom: Result, String> = block_receipts .result .iter() - .map(|receipt_json| ReceiptWithBloom::try_from(receipt_json)) + .map(ReceiptWithBloom::try_from) .collect::, _>>(); // computes the root and verify against existing data let mut hb: HashBuilder; + let receipts_idx = &[0, 1, 2]; + let mut targets: Vec = Vec::new(); + match receipts_with_bloom { Ok(receipts) => { - hb = build_trie_with_proofs(&receipts, &[0, 1, 2]); + hb = build_trie_with_proofs(&receipts, receipts_idx); let root: reth::revm::primitives::FixedBytes<32> = hb.root(); let root_h256 = H256::from(root.0); assert_eq!(root_h256, receipts_root, "Roots do not match!"); + + let mut index_buffer = Vec::new(); + let mut value_buffer = Vec::new(); + + // build some of the targets to get proofs for them + let receipts_len = receipts.len(); + for i in receipts_idx { + index_buffer.clear(); + value_buffer.clear(); + + let index = adjust_index_for_rlp(*i, receipts_len); + index.encode(&mut index_buffer); + + println!("index_buffer (raw bytes): {:?}", &index_buffer); + + receipts[index].encode_inner(&mut value_buffer, false); + let nibble = Nibbles::unpack(&index_buffer); + targets.push(Target::new(nibble, value_buffer.clone())); + } } Err(e) => { // Handle the error (e.g., by logging or panicking) @@ -198,6 +233,28 @@ mod tests { } } - // verify the proofs + let proof = hb.take_proofs(); + let proof1 = proof + .iter() + .filter_map(|(k, v)| targets[0].nibbles.starts_with(k).then_some(v)); + + // verifies proof for retained targets + assert_eq!( + verify_proof( + hb.root(), + targets[0].nibbles.clone(), + Some(targets[0].value.to_vec()), + proof1.clone(), + ), + Ok(()) + ); + + // checks for non existent proof + // let index = adjust_index_for_rlp(*i, receipts_len); + // let target = Nibbles::unpack(0x); + // assert_eq!( + // verify_proof(hb.root(), target, None, proof.values()), + // Ok(()) + // ); } } From c28a69ffdbf257783296333fb128def71fce6b41 Mon Sep 17 00:00:00 2001 From: pedro bufulin Date: Fri, 13 Sep 2024 19:10:25 -0300 Subject: [PATCH 05/48] refactor: method chaining --- crates/forrestrie/src/execution_block.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/forrestrie/src/execution_block.rs b/crates/forrestrie/src/execution_block.rs index 46c1010c..87047212 100644 --- a/crates/forrestrie/src/execution_block.rs +++ b/crates/forrestrie/src/execution_block.rs @@ -4,8 +4,6 @@ use reth::primitives::{Bloom, Log, Receipt, ReceiptWithBloom, TxType}; use reth_trie_common::{proof::ProofRetainer, root::adjust_index_for_rlp, HashBuilder, Nibbles}; use serde::{Deserialize, Deserializer, Serialize}; -// reth::primitives::proofs::calculate_receipt_root; - #[derive(Debug, Deserialize, Serialize)] pub struct ReceiptJson { #[serde(rename = "type")] @@ -169,7 +167,7 @@ mod tests { let receipts_with_bloom: Result, String> = block_receipts .result .iter() - .map(|receipt_json| ReceiptWithBloom::try_from(receipt_json)) + .map(ReceiptWithBloom::try_from) .collect::, _>>(); // Check that the conversion was successful From 2644cfad57d6583b624cbbdbd30c42e178c94f87 Mon Sep 17 00:00:00 2001 From: pedro bufulin Date: Fri, 13 Sep 2024 19:37:01 -0300 Subject: [PATCH 06/48] refactor: rename receipt indexes variable to represent targets of proofs in trie, sort dependencies alphabetically --- crates/forrestrie/src/execution_block.rs | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/crates/forrestrie/src/execution_block.rs b/crates/forrestrie/src/execution_block.rs index 87047212..75ee244c 100644 --- a/crates/forrestrie/src/execution_block.rs +++ b/crates/forrestrie/src/execution_block.rs @@ -94,15 +94,12 @@ where // builds the trie to generate proofs from the Receipts // generate a different root. Make sure that the source of receipts sorts them by `logIndex` -pub fn build_trie_with_proofs( - receipts: &[ReceiptWithBloom], - receipts_idx: &[usize], -) -> HashBuilder { +pub fn build_trie_with_proofs(receipts: &[ReceiptWithBloom], target_idxs: &[usize]) -> HashBuilder { let mut index_buffer = Vec::new(); let mut value_buffer = Vec::new(); // Initialize ProofRetainer with the target nibbles (the keys for which we want proofs) - let targets: Vec = receipts_idx + let targets: Vec = target_idxs .iter() .map(|&i| { let index = adjust_index_for_rlp(i, receipts.len()); @@ -135,8 +132,6 @@ pub fn build_trie_with_proofs( #[cfg(test)] mod tests { - use k256::pkcs8::der::Encode; - use reth::primitives::B256; use reth_trie_common::proof::verify_proof; use crate::beacon_block::BlockWrapper; @@ -196,22 +191,21 @@ mod tests { // computes the root and verify against existing data let mut hb: HashBuilder; - let receipts_idx = &[0, 1, 2]; + let target_idxs = &[0, 1, 2]; let mut targets: Vec = Vec::new(); match receipts_with_bloom { Ok(receipts) => { - hb = build_trie_with_proofs(&receipts, receipts_idx); - let root: reth::revm::primitives::FixedBytes<32> = hb.root(); - let root_h256 = H256::from(root.0); - assert_eq!(root_h256, receipts_root, "Roots do not match!"); + hb = build_trie_with_proofs(&receipts, target_idxs); + let calculated_root = H256::from(hb.root().0); + assert_eq!(calculated_root, receipts_root, "Roots do not match!"); let mut index_buffer = Vec::new(); let mut value_buffer = Vec::new(); // build some of the targets to get proofs for them let receipts_len = receipts.len(); - for i in receipts_idx { + for i in target_idxs { index_buffer.clear(); value_buffer.clear(); From 5958e3c5f9ea3caee3399337c1ecee8d3160b2e8 Mon Sep 17 00:00:00 2001 From: pedro bufulin Date: Fri, 13 Sep 2024 20:09:15 -0300 Subject: [PATCH 07/48] test: uses all targets in vector --- crates/forrestrie/src/execution_block.rs | 46 +++++++++++++----------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/crates/forrestrie/src/execution_block.rs b/crates/forrestrie/src/execution_block.rs index 75ee244c..e76c45ca 100644 --- a/crates/forrestrie/src/execution_block.rs +++ b/crates/forrestrie/src/execution_block.rs @@ -123,6 +123,7 @@ pub fn build_trie_with_proofs(receipts: &[ReceiptWithBloom], target_idxs: &[usiz receipts[index].encode_inner(&mut value_buffer, false); // NOTICE: if the ProofRetainer is set, add_leaf automatically retains the proofs for the targets + print!("{:?}", Nibbles::unpack(&index_buffer)); hb.add_leaf(Nibbles::unpack(&index_buffer), &value_buffer); } @@ -193,6 +194,7 @@ mod tests { let mut hb: HashBuilder; let target_idxs = &[0, 1, 2]; let mut targets: Vec = Vec::new(); + let receipts_len; match receipts_with_bloom { Ok(receipts) => { @@ -204,7 +206,7 @@ mod tests { let mut value_buffer = Vec::new(); // build some of the targets to get proofs for them - let receipts_len = receipts.len(); + receipts_len = receipts.len(); for i in target_idxs { index_buffer.clear(); value_buffer.clear(); @@ -212,10 +214,9 @@ mod tests { let index = adjust_index_for_rlp(*i, receipts_len); index.encode(&mut index_buffer); - println!("index_buffer (raw bytes): {:?}", &index_buffer); - receipts[index].encode_inner(&mut value_buffer, false); let nibble = Nibbles::unpack(&index_buffer); + println!("{:?} targets added", nibble); targets.push(Target::new(nibble, value_buffer.clone())); } } @@ -225,25 +226,30 @@ mod tests { } } - let proof = hb.take_proofs(); - let proof1 = proof - .iter() - .filter_map(|(k, v)| targets[0].nibbles.starts_with(k).then_some(v)); - // verifies proof for retained targets - assert_eq!( - verify_proof( - hb.root(), - targets[0].nibbles.clone(), - Some(targets[0].value.to_vec()), - proof1.clone(), - ), - Ok(()) - ); + let proof = hb.take_proofs(); + for target in targets.iter() { + let proof1 = proof + .iter() + .filter_map(|(k, v)| target.nibbles.starts_with(k).then_some(v)); + + assert_eq!( + verify_proof( + hb.root(), + target.nibbles.clone(), + Some(target.value.to_vec()), + proof1.clone(), + ), + Ok(()) + ); + } - // checks for non existent proof - // let index = adjust_index_for_rlp(*i, receipts_len); - // let target = Nibbles::unpack(0x); + // check for exclusion proof + // let mut index_buffer = Vec::new(); + // let index = adjust_index_for_rlp(6, receipts_len); + // index.encode(&mut index_buffer); + // let target = Nibbles::unpack(index_buffer); + // println!("target nibble for exclusion: {:?}", target); // assert_eq!( // verify_proof(hb.root(), target, None, proof.values()), // Ok(()) From 5d8ae4cfe75a29cb0b13a5ab98d9cd1e5af99abb Mon Sep 17 00:00:00 2001 From: pedro bufulin Date: Fri, 13 Sep 2024 20:11:29 -0300 Subject: [PATCH 08/48] test: exclusion proof test --- crates/forrestrie/src/execution_block.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/crates/forrestrie/src/execution_block.rs b/crates/forrestrie/src/execution_block.rs index e76c45ca..0f47f399 100644 --- a/crates/forrestrie/src/execution_block.rs +++ b/crates/forrestrie/src/execution_block.rs @@ -245,14 +245,14 @@ mod tests { } // check for exclusion proof - // let mut index_buffer = Vec::new(); - // let index = adjust_index_for_rlp(6, receipts_len); - // index.encode(&mut index_buffer); - // let target = Nibbles::unpack(index_buffer); - // println!("target nibble for exclusion: {:?}", target); - // assert_eq!( - // verify_proof(hb.root(), target, None, proof.values()), - // Ok(()) - // ); + let mut index_buffer = Vec::new(); + let index = adjust_index_for_rlp(6, receipts_len); + index.encode(&mut index_buffer); + let target = Nibbles::unpack(index_buffer); + println!("target nibble for exclusion: {:?}", target); + assert_eq!( + verify_proof(hb.root(), target, None, proof.values()), + Ok(()) + ); } } From a28ab04ef2e7a7709ba52ffccc0c79d65db1bdaf Mon Sep 17 00:00:00 2001 From: pedro bufulin Date: Thu, 19 Sep 2024 17:22:05 -0300 Subject: [PATCH 09/48] test: mvoes failing exclusion proof test and try_from test from this branch these are different topics and proof generation was already achieved in this branch --- crates/forrestrie/src/execution_block.rs | 40 +++++------------------- 1 file changed, 8 insertions(+), 32 deletions(-) diff --git a/crates/forrestrie/src/execution_block.rs b/crates/forrestrie/src/execution_block.rs index 0f47f399..f946bc04 100644 --- a/crates/forrestrie/src/execution_block.rs +++ b/crates/forrestrie/src/execution_block.rs @@ -1,6 +1,6 @@ use alloy_rlp::Encodable; use ethers::prelude::*; -use reth::primitives::{Bloom, Log, Receipt, ReceiptWithBloom, TxType}; +use reth::primitives::{Bloom, Receipt, ReceiptWithBloom, TxType}; use reth_trie_common::{proof::ProofRetainer, root::adjust_index_for_rlp, HashBuilder, Nibbles}; use serde::{Deserialize, Deserializer, Serialize}; @@ -50,13 +50,14 @@ impl TryFrom<&ReceiptJson> for ReceiptWithBloom { success: receipt_json.status, cumulative_gas_used, logs: receipt_json.logs.clone(), + // NOTICE: receipts will have more fields depending of the EVM chain. + // this is how to handle them in the futuro // #[cfg(feature = "optimism")] // deposit_nonce: None, // Handle Optimism-specific fields as necessary // #[cfg(feature = "optimism")] // deposit_receipt_version: None, }; - // Create the ReceiptWithBloom struct Ok(ReceiptWithBloom { bloom: receipt_json.logs_bloom, receipt, @@ -157,23 +158,6 @@ mod tests { ) }); - #[test] - fn test_parse_wrapped_receipt_into_reth_receipt() { - let block_receipts: &LazyCell = &BLOCK_RECEIPTS; - let receipts_with_bloom: Result, String> = block_receipts - .result - .iter() - .map(ReceiptWithBloom::try_from) - .collect::, _>>(); - - // Check that the conversion was successful - assert!( - receipts_with_bloom.is_ok(), - "Conversion failed with error: {:?}", - receipts_with_bloom.err().unwrap() - ); - } - #[test] fn test_compute_receipts_trie_root_and_proof() { let block_wrapper: &LazyCell = &BLOCK_WRAPPER; @@ -192,7 +176,9 @@ mod tests { // computes the root and verify against existing data let mut hb: HashBuilder; - let target_idxs = &[0, 1, 2]; + //target_idxs are the logIndexes for receipts to get proofs from. + // these values are arbitrary + let target_idxs = &[0, 1, 2, 129]; let mut targets: Vec = Vec::new(); let receipts_len; @@ -214,8 +200,9 @@ mod tests { let index = adjust_index_for_rlp(*i, receipts_len); index.encode(&mut index_buffer); - receipts[index].encode_inner(&mut value_buffer, false); let nibble = Nibbles::unpack(&index_buffer); + + receipts[index].encode_inner(&mut value_buffer, false); println!("{:?} targets added", nibble); targets.push(Target::new(nibble, value_buffer.clone())); } @@ -243,16 +230,5 @@ mod tests { Ok(()) ); } - - // check for exclusion proof - let mut index_buffer = Vec::new(); - let index = adjust_index_for_rlp(6, receipts_len); - index.encode(&mut index_buffer); - let target = Nibbles::unpack(index_buffer); - println!("target nibble for exclusion: {:?}", target); - assert_eq!( - verify_proof(hb.root(), target, None, proof.values()), - Ok(()) - ); } } From 55ad16dbd019cbaa0fcf87b0178445ce71012d73 Mon Sep 17 00:00:00 2001 From: pedro bufulin Date: Thu, 19 Sep 2024 17:27:27 -0300 Subject: [PATCH 10/48] test: removes target index out of bounds for exclusion proof --- crates/forrestrie/src/execution_block.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/crates/forrestrie/src/execution_block.rs b/crates/forrestrie/src/execution_block.rs index f946bc04..d60e5f4b 100644 --- a/crates/forrestrie/src/execution_block.rs +++ b/crates/forrestrie/src/execution_block.rs @@ -1,6 +1,6 @@ use alloy_rlp::Encodable; use ethers::prelude::*; -use reth::primitives::{Bloom, Receipt, ReceiptWithBloom, TxType}; +use reth::primitives::{Bloom, Log, Receipt, ReceiptWithBloom, TxType}; use reth_trie_common::{proof::ProofRetainer, root::adjust_index_for_rlp, HashBuilder, Nibbles}; use serde::{Deserialize, Deserializer, Serialize}; @@ -124,7 +124,6 @@ pub fn build_trie_with_proofs(receipts: &[ReceiptWithBloom], target_idxs: &[usiz receipts[index].encode_inner(&mut value_buffer, false); // NOTICE: if the ProofRetainer is set, add_leaf automatically retains the proofs for the targets - print!("{:?}", Nibbles::unpack(&index_buffer)); hb.add_leaf(Nibbles::unpack(&index_buffer), &value_buffer); } @@ -178,7 +177,7 @@ mod tests { let mut hb: HashBuilder; //target_idxs are the logIndexes for receipts to get proofs from. // these values are arbitrary - let target_idxs = &[0, 1, 2, 129]; + let target_idxs = &[0, 1, 2]; let mut targets: Vec = Vec::new(); let receipts_len; @@ -203,7 +202,6 @@ mod tests { let nibble = Nibbles::unpack(&index_buffer); receipts[index].encode_inner(&mut value_buffer, false); - println!("{:?} targets added", nibble); targets.push(Target::new(nibble, value_buffer.clone())); } } From 5397a39bc2577395abbbe797ab260dcf836a2fa1 Mon Sep 17 00:00:00 2001 From: pedro bufulin Date: Thu, 19 Sep 2024 17:31:31 -0300 Subject: [PATCH 11/48] docs: TargetLeaf doc and name change --- crates/forrestrie/src/execution_block.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/crates/forrestrie/src/execution_block.rs b/crates/forrestrie/src/execution_block.rs index d60e5f4b..ded66d8c 100644 --- a/crates/forrestrie/src/execution_block.rs +++ b/crates/forrestrie/src/execution_block.rs @@ -23,16 +23,17 @@ pub struct ReceiptJson { pub logs_bloom: Bloom, } -// represents leafs that are being generated proofs on -pub struct Target { +/// Represents a leaf in the trie for which a proof is to be generated, i.e., the target of the proof. +/// The `nibbles` represent the path to the leaf in the trie, and the `value` is the data stored at the leaf. +pub struct TargetLeaf { pub nibbles: Nibbles, pub value: Vec, } -impl Target { - // Constructor to create a new Target +impl TargetLeaf { + // Constructor to create a new TargetLeaf pub fn new(nibbles: Nibbles, value: Vec) -> Self { - Target { nibbles, value } + TargetLeaf { nibbles, value } } } @@ -178,7 +179,7 @@ mod tests { //target_idxs are the logIndexes for receipts to get proofs from. // these values are arbitrary let target_idxs = &[0, 1, 2]; - let mut targets: Vec = Vec::new(); + let mut targets: Vec = Vec::new(); let receipts_len; match receipts_with_bloom { @@ -202,7 +203,7 @@ mod tests { let nibble = Nibbles::unpack(&index_buffer); receipts[index].encode_inner(&mut value_buffer, false); - targets.push(Target::new(nibble, value_buffer.clone())); + targets.push(TargetLeaf::new(nibble, value_buffer.clone())); } } Err(e) => { From eed754a86c4b322e8e158ec433cdf3a9c02f01b5 Mon Sep 17 00:00:00 2001 From: pedro bufulin Date: Thu, 19 Sep 2024 17:33:55 -0300 Subject: [PATCH 12/48] docs(README.md): adds `` around head-state.json From ce54a608ed98048eafe5814f1564afe6d1b10b3e Mon Sep 17 00:00:00 2001 From: pedro bufulin Date: Wed, 23 Oct 2024 16:37:44 -0300 Subject: [PATCH 13/48] feat(lib.rs): adds execution_layer --- crates/forrestrie/src/{execution_block.rs => execution_layer.rs} | 0 crates/forrestrie/src/lib.rs | 1 + 2 files changed, 1 insertion(+) rename crates/forrestrie/src/{execution_block.rs => execution_layer.rs} (100%) diff --git a/crates/forrestrie/src/execution_block.rs b/crates/forrestrie/src/execution_layer.rs similarity index 100% rename from crates/forrestrie/src/execution_block.rs rename to crates/forrestrie/src/execution_layer.rs diff --git a/crates/forrestrie/src/lib.rs b/crates/forrestrie/src/lib.rs index 38bed740..004216f2 100644 --- a/crates/forrestrie/src/lib.rs +++ b/crates/forrestrie/src/lib.rs @@ -1,3 +1,4 @@ pub mod beacon_block; pub mod beacon_state; pub mod beacon_v1; +pub mod execution_layer; From 73ea6bde7d1ce251c317c11e2d52c026582f0bb3 Mon Sep 17 00:00:00 2001 From: pedro bufulin Date: Mon, 28 Oct 2024 16:28:06 -0300 Subject: [PATCH 14/48] test(execution_layer): add fake ReceiptJson generation method --- Cargo.lock | 21 +++++++++ Cargo.toml | 1 + crates/forrestrie/Cargo.toml | 7 +++ crates/forrestrie/src/execution_layer.rs | 54 ++++++++++-------------- 4 files changed, 52 insertions(+), 31 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cd5ee53a..c8a159ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1630,6 +1630,12 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "deunicode" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "339544cc9e2c4dc3fc7149fd630c5f22263a4fdf18a98afd0075784968b5cf00" + [[package]] name = "digest" version = "0.9.0" @@ -2094,6 +2100,16 @@ dependencies = [ "validator", ] +[[package]] +name = "fake" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d391ba4af7f1d93f01fcf7b2f29e2bc9348e109dfdbf4dcbdc51dfa38dab0b6" +dependencies = [ + "deunicode", + "rand", +] + [[package]] name = "fallible-iterator" version = "0.2.0" @@ -2317,8 +2333,11 @@ dependencies = [ name = "forrestrie" version = "0.1.1" dependencies = [ + "alloy-primitives 0.8.9", + "alloy-rlp", "bls", "ethportal-api", + "fake", "firehose-protos", "futures", "merkle_proof", @@ -2327,6 +2346,8 @@ dependencies = [ "prost-build", "prost-wkt", "prost-wkt-types", + "reth-primitives", + "reth-trie-common", "serde", "ssz_types 0.6.0", "tonic", diff --git a/Cargo.toml b/Cargo.toml index 2d3a4304..b0bb902a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ dotenvy = "0.15.7" env_logger = "0.11.5" ethereum-types = "=0.14.1" ethportal-api = { git = "https://github.com/ethereum/trin.git", version = "0.2.2", tag = "v0.1.0-alpha.51" } +fake = "2.10.0" futures = "0.3.31" hex = "0.4.3" http = "1.1.0" diff --git a/crates/forrestrie/Cargo.toml b/crates/forrestrie/Cargo.toml index cbde017e..55420383 100644 --- a/crates/forrestrie/Cargo.toml +++ b/crates/forrestrie/Cargo.toml @@ -8,6 +8,8 @@ name = "forrestrie" path = "src/lib.rs" [dependencies] +alloy-rlp.workspace = true +alloy-primitives.workspace = true bls.workspace = true ethportal-api.workspace = true firehose-protos = { path = "../firehose-protos" } @@ -17,12 +19,17 @@ primitive-types.workspace = true prost.workspace = true prost-wkt.workspace = true prost-wkt-types.workspace = true +reth-trie-common.workspace = true +reth-primitives.workspace = true serde = { workspace = true, features = ["derive"] } ssz_types.workspace = true tonic.workspace = true tree_hash = "0.6.0" types.workspace = true +[dev-dependencies] +fake.workspace = true + [build-dependencies] prost-build.workspace = true tonic-build.workspace = true diff --git a/crates/forrestrie/src/execution_layer.rs b/crates/forrestrie/src/execution_layer.rs index ded66d8c..ad913ad0 100644 --- a/crates/forrestrie/src/execution_layer.rs +++ b/crates/forrestrie/src/execution_layer.rs @@ -1,6 +1,6 @@ +use alloy_primitives::{Bloom, U256}; use alloy_rlp::Encodable; -use ethers::prelude::*; -use reth::primitives::{Bloom, Log, Receipt, ReceiptWithBloom, TxType}; +use reth_primitives::{Log, Receipt, ReceiptWithBloom, TxType}; use reth_trie_common::{proof::ProofRetainer, root::adjust_index_for_rlp, HashBuilder, Nibbles}; use serde::{Deserialize, Deserializer, Serialize}; @@ -23,6 +23,22 @@ pub struct ReceiptJson { pub logs_bloom: Bloom, } +impl ReceiptJson { + #[cfg(test)] + fn fake() -> Self { + ReceiptJson { + tx_type: TxType::Eip1559, // Replace with any desired variant + block_hash: "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" + .to_string(), + block_number: "0x1a".to_string(), + logs: vec![Log::empty()], + cumulative_gas_used: U256::from(0x5208), // Mock gas used value + status: true, // Mock status as successful + logs_bloom: Bloom::default(), // Mock an empty logs bloom + } + } +} + /// Represents a leaf in the trie for which a proof is to be generated, i.e., the target of the proof. /// The `nibbles` represent the path to the leaf in the trie, and the `value` is the data stored at the leaf. pub struct TargetLeaf { @@ -133,41 +149,17 @@ pub fn build_trie_with_proofs(receipts: &[ReceiptWithBloom], target_idxs: &[usiz #[cfg(test)] mod tests { - + use primitive_types::H256; use reth_trie_common::proof::verify_proof; - use crate::beacon_block::BlockWrapper; use std::cell::LazyCell; use super::*; - /// Deneb block JSON file shared among contributors. - /// The block hash is `0x5dde05ab1da7f768ed3ea2d53c6fa0d79c0c2283e52bb0d00842a4bdbf14c0ab`. - const DENEB_BLOCK_JSON: &str = include_str!("../../../bb-8786333.json"); - const BLOCK_RECEIPTS_JSON: &str = include_str!("../../../eb-19584570-receipts.json"); - - const BLOCK_WRAPPER: LazyCell = LazyCell::new(|| { - serde_json::from_str(DENEB_BLOCK_JSON).expect( - "For this spike we are using a Deneb block JSON file that has been shared among contributors", - ) - }); - - const BLOCK_RECEIPTS: LazyCell = LazyCell::new(|| { - serde_json::from_str(BLOCK_RECEIPTS_JSON).expect( - "This is all the receipt data from a block, fetch with eth_getBlockReceipts method", - ) - }); - #[test] fn test_compute_receipts_trie_root_and_proof() { - let block_wrapper: &LazyCell = &BLOCK_WRAPPER; - let block: &::types::BeaconBlock<::types::MainnetEthSpec> = &block_wrapper.data.message; - let block_body: &::types::BeaconBlockBodyDeneb<::types::MainnetEthSpec> = - block.body_deneb().unwrap(); - let payload = &block_body.execution_payload; - let receipts_root = payload.execution_payload.receipts_root; - - let block_receipts: &LazyCell = &BLOCK_RECEIPTS; + // TODO: create ReceiptsFromBlock dummy variables instead of reading from json + let receipts_with_bloom: Result, String> = block_receipts .result .iter() @@ -186,7 +178,7 @@ mod tests { Ok(receipts) => { hb = build_trie_with_proofs(&receipts, target_idxs); let calculated_root = H256::from(hb.root().0); - assert_eq!(calculated_root, receipts_root, "Roots do not match!"); + // assert_eq!(calculated_root, receipts_root, "Roots do not match!"); let mut index_buffer = Vec::new(); let mut value_buffer = Vec::new(); @@ -213,7 +205,7 @@ mod tests { } // verifies proof for retained targets - let proof = hb.take_proofs(); + let proof = hb.take_proof_nodes(); for target in targets.iter() { let proof1 = proof .iter() From 326025314e697634b974deaf18b5ff08f9d1f43c Mon Sep 17 00:00:00 2001 From: Joseph Livesey Date: Mon, 28 Oct 2024 15:49:27 -0400 Subject: [PATCH 15/48] test: add multiple fake receipt generation --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index c8a159ff..16fb9ad6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2333,7 +2333,7 @@ dependencies = [ name = "forrestrie" version = "0.1.1" dependencies = [ - "alloy-primitives 0.8.9", + "alloy-primitives 0.8.10", "alloy-rlp", "bls", "ethportal-api", From 7300a751593e98483710fa557dbb437212cf1233 Mon Sep 17 00:00:00 2001 From: Joseph Livesey Date: Mon, 28 Oct 2024 15:49:27 -0400 Subject: [PATCH 16/48] test: add multiple fake receipt generation --- Cargo.lock | 1 + crates/forrestrie/Cargo.toml | 1 + crates/forrestrie/src/execution_layer.rs | 22 +++++++++++++++++++++- 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 16fb9ad6..b150e9a6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2346,6 +2346,7 @@ dependencies = [ "prost-build", "prost-wkt", "prost-wkt-types", + "rand", "reth-primitives", "reth-trie-common", "serde", diff --git a/crates/forrestrie/Cargo.toml b/crates/forrestrie/Cargo.toml index 55420383..b1b22a5d 100644 --- a/crates/forrestrie/Cargo.toml +++ b/crates/forrestrie/Cargo.toml @@ -28,6 +28,7 @@ tree_hash = "0.6.0" types.workspace = true [dev-dependencies] +rand.workspace = true fake.workspace = true [build-dependencies] diff --git a/crates/forrestrie/src/execution_layer.rs b/crates/forrestrie/src/execution_layer.rs index ad913ad0..36b6c35a 100644 --- a/crates/forrestrie/src/execution_layer.rs +++ b/crates/forrestrie/src/execution_layer.rs @@ -26,6 +26,16 @@ pub struct ReceiptJson { impl ReceiptJson { #[cfg(test)] fn fake() -> Self { + use alloy_primitives::Address; + use alloy_rlp::Bytes; + + fn fake_log() -> Log { + // generate random slice of bytes + let data = [0x01, 0x02, 0x03, 0x04]; + + Log::new(Address::default(), vec![]).unwrap() + } + ReceiptJson { tx_type: TxType::Eip1559, // Replace with any desired variant block_hash: "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" @@ -87,6 +97,14 @@ pub struct ReceiptsFromBlock { pub result: Vec, } +impl FromIterator for ReceiptsFromBlock { + fn from_iter>(iter: I) -> Self { + ReceiptsFromBlock { + result: iter.into_iter().collect(), + } + } +} + fn status_to_bool<'de, D>(deserializer: D) -> Result where D: Deserializer<'de>, @@ -158,7 +176,8 @@ mod tests { #[test] fn test_compute_receipts_trie_root_and_proof() { - // TODO: create ReceiptsFromBlock dummy variables instead of reading from json + let block_receipts: ReceiptsFromBlock = + (0..10).into_iter().map(|_| ReceiptJson::fake()).collect(); let receipts_with_bloom: Result, String> = block_receipts .result @@ -178,6 +197,7 @@ mod tests { Ok(receipts) => { hb = build_trie_with_proofs(&receipts, target_idxs); let calculated_root = H256::from(hb.root().0); + dbg!(&calculated_root); // assert_eq!(calculated_root, receipts_root, "Roots do not match!"); let mut index_buffer = Vec::new(); From b52a3a4a44d1937462b36d45731346ad0200628d Mon Sep 17 00:00:00 2001 From: pedro bufulin Date: Mon, 28 Oct 2024 17:36:27 -0300 Subject: [PATCH 17/48] test: random log generation attempt --- crates/firehose-protos/build.rs | 1 + .../examples/receipts_proof.bak | 83 +++++++++++++++++++ crates/forrestrie/src/execution_layer.rs | 24 ++++-- 3 files changed, 103 insertions(+), 5 deletions(-) create mode 100644 crates/forrestrie-examples/examples/receipts_proof.bak diff --git a/crates/firehose-protos/build.rs b/crates/firehose-protos/build.rs index 5c59d07c..2dbed275 100644 --- a/crates/firehose-protos/build.rs +++ b/crates/firehose-protos/build.rs @@ -13,6 +13,7 @@ fn main() { config.extern_path(".google.protobuf.Timestamp", "::prost_wkt_types::Timestamp"); tonic_build::configure() + .protoc_arg("--experimental_allow_proto3_optional") .build_client(true) .file_descriptor_set_path(out_dir.join("descriptors.bin")) .compile_protos_with_config( diff --git a/crates/forrestrie-examples/examples/receipts_proof.bak b/crates/forrestrie-examples/examples/receipts_proof.bak new file mode 100644 index 00000000..8d9700ab --- /dev/null +++ b/crates/forrestrie-examples/examples/receipts_proof.bak @@ -0,0 +1,83 @@ +// //! # Execution Layer block receipt proofs +// //! +// //! +// //! This is a scenario where it is necessary to prove a receipt to be in a 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() { +// let block_wrapper: &LazyCell = &BLOCK_WRAPPER; +// let block: &::types::BeaconBlock<::types::MainnetEthSpec> = &block_wrapper.data.message; +// let block_body: &::types::BeaconBlockBodyDeneb<::types::MainnetEthSpec> = +// block.body_deneb().unwrap(); +// let payload = &block_body.execution_payload; +// let receipts_root = payload.execution_payload.receipts_root; + +// let block_receipts: &LazyCell = &BLOCK_RECEIPTS; +// let receipts_with_bloom: Result, String> = block_receipts +// .result +// .iter() +// .map(ReceiptWithBloom::try_from) +// .collect::, _>>(); + +// // computes the root and verify against existing data +// let mut hb: HashBuilder; +// //target_idxs are the logIndexes for receipts to get proofs from. +// // these values are arbitrary +// let target_idxs = &[0, 1, 2]; +// let mut targets: Vec = Vec::new(); +// let receipts_len; + +// match receipts_with_bloom { +// Ok(receipts) => { +// hb = build_trie_with_proofs(&receipts, target_idxs); +// let calculated_root = H256::from(hb.root().0); +// assert_eq!(calculated_root, receipts_root, "Roots do not match!"); + +// let mut index_buffer = Vec::new(); +// let mut value_buffer = Vec::new(); + +// // build some of the targets to get proofs for them +// receipts_len = receipts.len(); +// for i in target_idxs { +// index_buffer.clear(); +// value_buffer.clear(); + +// let index = adjust_index_for_rlp(*i, receipts_len); +// index.encode(&mut index_buffer); + +// let nibble = Nibbles::unpack(&index_buffer); + +// receipts[index].encode_inner(&mut value_buffer, false); +// targets.push(TargetLeaf::new(nibble, value_buffer.clone())); +// } +// } +// Err(e) => { +// // Handle the error (e.g., by logging or panicking) +// panic!("Failed to convert receipts: {}", e); +// } +// } + +// // verifies proof for retained targets +// let proof = hb.take_proof_nodes(); +// for target in targets.iter() { +// let proof1 = proof +// .iter() +// .filter_map(|(k, v)| target.nibbles.starts_with(k).then_some(v)); + +// assert_eq!( +// verify_proof( +// hb.root(), +// target.nibbles.clone(), +// Some(target.value.to_vec()), +// proof1.clone(), +// ), +// Ok(()) +// ); +// } +// } diff --git a/crates/forrestrie/src/execution_layer.rs b/crates/forrestrie/src/execution_layer.rs index 36b6c35a..0b2d72ca 100644 --- a/crates/forrestrie/src/execution_layer.rs +++ b/crates/forrestrie/src/execution_layer.rs @@ -26,22 +26,33 @@ pub struct ReceiptJson { impl ReceiptJson { #[cfg(test)] fn fake() -> Self { - use alloy_primitives::Address; - use alloy_rlp::Bytes; + use alloy_primitives::{Address, Bytes}; + use rand::{self, Rng}; fn fake_log() -> Log { // generate random slice of bytes - let data = [0x01, 0x02, 0x03, 0x04]; + let mut rng = rand::thread_rng(); - Log::new(Address::default(), vec![]).unwrap() + // Generate a random u32 + let random_u32: u32 = rng.gen(); + + Log::new( + Address::default(), + vec![], + Bytes::from(random_u32.to_be_bytes().to_vec()), + ) + .unwrap() } + let logs: Vec = (3..5).into_iter().map(|_| fake_log()).collect(); + dbg!(&logs); + ReceiptJson { tx_type: TxType::Eip1559, // Replace with any desired variant block_hash: "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" .to_string(), block_number: "0x1a".to_string(), - logs: vec![Log::empty()], + logs, cumulative_gas_used: U256::from(0x5208), // Mock gas used value status: true, // Mock status as successful logs_bloom: Bloom::default(), // Mock an empty logs bloom @@ -176,6 +187,9 @@ mod tests { #[test] fn test_compute_receipts_trie_root_and_proof() { + // TODO: instead of generating receipts, pick a small exempt from + // the execution block that fits here. It should work better, + // since faking logs requires the log to be properly rlp encoded let block_receipts: ReceiptsFromBlock = (0..10).into_iter().map(|_| ReceiptJson::fake()).collect(); From 010be45c15c13a3f2f689f0b3e640965823ef7a5 Mon Sep 17 00:00:00 2001 From: pedro bufulin Date: Thu, 31 Oct 2024 00:30:52 -0300 Subject: [PATCH 18/48] test: changes method of matching target nodes for verifying proof --- crates/forrestrie/src/execution_layer.rs | 58 ++++++++++++------------ 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/crates/forrestrie/src/execution_layer.rs b/crates/forrestrie/src/execution_layer.rs index 0b2d72ca..3f8a545a 100644 --- a/crates/forrestrie/src/execution_layer.rs +++ b/crates/forrestrie/src/execution_layer.rs @@ -26,26 +26,32 @@ pub struct ReceiptJson { impl ReceiptJson { #[cfg(test)] fn fake() -> Self { - use alloy_primitives::{Address, Bytes}; - use rand::{self, Rng}; + use std::io::Read; + + use alloy_primitives::{bytes, fixed_bytes, Address, Bytes}; + use rand::{self, random, rngs::OsRng, RngCore}; fn fake_log() -> Log { // generate random slice of bytes - let mut rng = rand::thread_rng(); + let mut random_bytes = [0u8; 20]; + OsRng.fill_bytes(&mut random_bytes); + // Create a 32-byte array initialized with zeros + let mut bytes = [0u8; 32]; - // Generate a random u32 - let random_u32: u32 = rng.gen(); + // Insert the random bytes into the last 20 bytes of the array + bytes[12..].copy_from_slice(&random_bytes); - Log::new( - Address::default(), - vec![], - Bytes::from(random_u32.to_be_bytes().to_vec()), + // Generate a random u32 + Log::new_unchecked( + Address::random(), + vec![fixed_bytes!( + "ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef" + )], + Bytes::from(random_bytes), ) - .unwrap() } let logs: Vec = (3..5).into_iter().map(|_| fake_log()).collect(); - dbg!(&logs); ReceiptJson { tx_type: TxType::Eip1559, // Replace with any desired variant @@ -53,15 +59,16 @@ impl ReceiptJson { .to_string(), block_number: "0x1a".to_string(), logs, - cumulative_gas_used: U256::from(0x5208), // Mock gas used value - status: true, // Mock status as successful - logs_bloom: Bloom::default(), // Mock an empty logs bloom + cumulative_gas_used: U256::from(0x5208), + status: true, + logs_bloom: Bloom::default(), } } } /// Represents a leaf in the trie for which a proof is to be generated, i.e., the target of the proof. /// The `nibbles` represent the path to the leaf in the trie, and the `value` is the data stored at the leaf. +#[derive(Debug)] pub struct TargetLeaf { pub nibbles: Nibbles, pub value: Vec, @@ -178,12 +185,8 @@ pub fn build_trie_with_proofs(receipts: &[ReceiptWithBloom], target_idxs: &[usiz #[cfg(test)] mod tests { - use primitive_types::H256; - use reth_trie_common::proof::verify_proof; - - use std::cell::LazyCell; - use super::*; + use reth_trie_common::proof::verify_proof; #[test] fn test_compute_receipts_trie_root_and_proof() { @@ -199,20 +202,19 @@ mod tests { .map(ReceiptWithBloom::try_from) .collect::, _>>(); + dbg!(&receipts_with_bloom); + // computes the root and verify against existing data let mut hb: HashBuilder; //target_idxs are the logIndexes for receipts to get proofs from. // these values are arbitrary - let target_idxs = &[0, 1, 2]; + let target_idxs = &[1]; let mut targets: Vec = Vec::new(); let receipts_len; match receipts_with_bloom { Ok(receipts) => { hb = build_trie_with_proofs(&receipts, target_idxs); - let calculated_root = H256::from(hb.root().0); - dbg!(&calculated_root); - // assert_eq!(calculated_root, receipts_root, "Roots do not match!"); let mut index_buffer = Vec::new(); let mut value_buffer = Vec::new(); @@ -241,16 +243,16 @@ mod tests { // verifies proof for retained targets let proof = hb.take_proof_nodes(); for target in targets.iter() { - let proof1 = proof - .iter() - .filter_map(|(k, v)| target.nibbles.starts_with(k).then_some(v)); - assert_eq!( verify_proof( hb.root(), target.nibbles.clone(), Some(target.value.to_vec()), - proof1.clone(), + proof + .clone() + .matching_nodes_sorted(&target.nibbles) + .iter() + .map(|(_, node)| node) ), Ok(()) ); From d8a798eeb5585b8d4ed5a82eb0edd02dcd4349da Mon Sep 17 00:00:00 2001 From: pedro bufulin Date: Thu, 31 Oct 2024 11:01:21 -0300 Subject: [PATCH 19/48] test: add static fake log, call root() to retain proofs --- crates/forrestrie/src/execution_layer.rs | 30 ++++++++++++++---------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/crates/forrestrie/src/execution_layer.rs b/crates/forrestrie/src/execution_layer.rs index 3f8a545a..5c486906 100644 --- a/crates/forrestrie/src/execution_layer.rs +++ b/crates/forrestrie/src/execution_layer.rs @@ -26,10 +26,8 @@ pub struct ReceiptJson { impl ReceiptJson { #[cfg(test)] fn fake() -> Self { - use std::io::Read; - - use alloy_primitives::{bytes, fixed_bytes, Address, Bytes}; - use rand::{self, random, rngs::OsRng, RngCore}; + use alloy_primitives::{bytes, fixed_bytes, Address}; + use rand::{self, rngs::OsRng, RngCore}; fn fake_log() -> Log { // generate random slice of bytes @@ -41,17 +39,22 @@ impl ReceiptJson { // Insert the random bytes into the last 20 bytes of the array bytes[12..].copy_from_slice(&random_bytes); - // Generate a random u32 + // Generate a static Log based on an actual log receipt Log::new_unchecked( Address::random(), - vec![fixed_bytes!( - "ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef" - )], - Bytes::from(random_bytes), + vec![ + fixed_bytes!( + "e1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c" + ), + fixed_bytes!( + "0000000000000000000000003328f7f4a1d1c57c35df56bbf0c9dcafca309c49" + ), + ], + bytes!("0000000000000000000000000000000000000000000000000dcf09da3e1eb9f3"), ) } - let logs: Vec = (3..5).into_iter().map(|_| fake_log()).collect(); + let logs: Vec = (0..5).into_iter().map(|_| fake_log()).collect(); ReceiptJson { tx_type: TxType::Eip1559, // Replace with any desired variant @@ -202,13 +205,11 @@ mod tests { .map(ReceiptWithBloom::try_from) .collect::, _>>(); - dbg!(&receipts_with_bloom); - // computes the root and verify against existing data let mut hb: HashBuilder; //target_idxs are the logIndexes for receipts to get proofs from. // these values are arbitrary - let target_idxs = &[1]; + let target_idxs = &[4]; let mut targets: Vec = Vec::new(); let receipts_len; @@ -240,6 +241,9 @@ mod tests { } } + // necessary to call this method to retain proofs + hb.root(); + // verifies proof for retained targets let proof = hb.take_proof_nodes(); for target in targets.iter() { From 7cdc4e2cef967bd06cf02299eef237c643daf268 Mon Sep 17 00:00:00 2001 From: pedro bufulin Date: Fri, 1 Nov 2024 13:48:37 -0300 Subject: [PATCH 20/48] feat: update alloy-primitives --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index b0bb902a..4cbb7e07 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ members = ["crates/*"] resolver = "2" [workspace.dependencies] -alloy-primitives = "0.8.9" +alloy-primitives = "0.8.10" alloy-consensus = "0.4.2" alloy-eip2930 = "0.1.0" alloy-rlp = "0.3.9" From 5820b740500a1416d83d900e138916b530d00921 Mon Sep 17 00:00:00 2001 From: pedro bufulin Date: Thu, 22 Aug 2024 18:10:06 -0300 Subject: [PATCH 21/48] refactor(execution_block): ReceiptWrapper fields as snake_case --- crates/forrestrie/src/execution_block.rs | 89 ++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 crates/forrestrie/src/execution_block.rs diff --git a/crates/forrestrie/src/execution_block.rs b/crates/forrestrie/src/execution_block.rs new file mode 100644 index 00000000..06d5ac4d --- /dev/null +++ b/crates/forrestrie/src/execution_block.rs @@ -0,0 +1,89 @@ +use ethers::prelude::*; +use reth::primitives::{Log, TxType}; +use serde::{Deserialize, Deserializer, Serialize}; + +// reth::primitives::proofs::calculate_receipt_root; + +#[derive(Debug, Deserialize, Serialize)] +pub struct ReceiptWrapper { + #[serde(rename = "type")] + #[serde(deserialize_with = "str_to_type")] + pub tx_type: TxType, + #[serde(rename = "blockHash")] + pub block_hash: String, + #[serde(rename = "blockNumber")] + pub block_number: String, + pub logs: Vec, + #[serde(rename = "cumulativeGasUsed")] + pub cumulative_gas_used: U256, + #[serde(deserialize_with = "status_to_bool")] + pub status: bool, +} + +fn status_to_bool<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + let status_str: &str = Deserialize::deserialize(deserializer)?; + match status_str { + "0x1" => Ok(true), + "0x0" => Ok(false), + _ => Err(serde::de::Error::custom("Invalid status value")), + } +} + +// Custom deserialization function for TxType +fn str_to_type<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + let tx_type_str: &str = Deserialize::deserialize(deserializer)?; + // Convert the hex string (without the "0x" prefix) to u8 + let tx_type_value = u8::from_str_radix(tx_type_str.trim_start_matches("0x"), 16) + .map_err(|_| serde::de::Error::custom("Invalid tx_type value"))?; + TxType::try_from(tx_type_value).map_err(|_| serde::de::Error::custom("Invalid tx_type value")) +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct ReceiptsFromBlock { + pub result: Vec, +} + +#[cfg(test)] +mod tests { + + use std::cell::LazyCell; + + use crate::beacon_block::BlockWrapper; + + use super::*; + + /// Deneb block JSON file shared among contributors. + /// The block hash is `0x5dde05ab1da7f768ed3ea2d53c6fa0d79c0c2283e52bb0d00842a4bdbf14c0ab`. + const DENEB_BLOCK_JSON: &str = include_str!("../../../bb-8786333.json"); + const BLOCK_RECEIPTS_JSON: &str = include_str!("../../../eb-19584570-receipts.json"); + + const BLOCK_WRAPPER: LazyCell = LazyCell::new(|| { + serde_json::from_str(DENEB_BLOCK_JSON).expect( + "For this spike we are using a Deneb block JSON file that has been shared among contributors", + ) + }); + + const RECEIPTS: LazyCell = LazyCell::new(|| { + serde_json::from_str(BLOCK_RECEIPTS_JSON).expect( + "This is all the receipt data from a block, fetch with eth_getBlockReceipts method", + ) + }); + + #[test] + fn test_parse_wrapped_receipt_into_reth_receipt() { + let block_wrapper: &LazyCell = &BLOCK_WRAPPER; + let block = &block_wrapper.data.message; + + let block_body = block.body_deneb().unwrap(); + let payload = &block_body.execution_payload; + let receits_root = payload.execution_payload.receipts_root; + + let receipts = &RECEIPTS; + } +} From 079ee93bfaf16f000bf721072a52c824df23d8dc Mon Sep 17 00:00:00 2001 From: pedro bufulin Date: Mon, 26 Aug 2024 12:28:33 -0300 Subject: [PATCH 22/48] feat(execution_block): try_from ReceiptsJson into ReceiptWithBloom, hash_builder_root --- crates/forrestrie/src/execution_block.rs | 113 ++++++++++++++++++++--- 1 file changed, 99 insertions(+), 14 deletions(-) diff --git a/crates/forrestrie/src/execution_block.rs b/crates/forrestrie/src/execution_block.rs index 06d5ac4d..3d33453b 100644 --- a/crates/forrestrie/src/execution_block.rs +++ b/crates/forrestrie/src/execution_block.rs @@ -1,11 +1,13 @@ +use alloy_rlp::Encodable; use ethers::prelude::*; -use reth::primitives::{Log, TxType}; +use reth::primitives::{Bloom, Log, Receipt, ReceiptWithBloom, TxType, B256}; +use reth_trie::{root::adjust_index_for_rlp, HashBuilder, Nibbles}; use serde::{Deserialize, Deserializer, Serialize}; // reth::primitives::proofs::calculate_receipt_root; #[derive(Debug, Deserialize, Serialize)] -pub struct ReceiptWrapper { +pub struct ReceiptJson { #[serde(rename = "type")] #[serde(deserialize_with = "str_to_type")] pub tx_type: TxType, @@ -18,6 +20,42 @@ pub struct ReceiptWrapper { pub cumulative_gas_used: U256, #[serde(deserialize_with = "status_to_bool")] pub status: bool, + // TODO: should we trust logsBloom provided or calculate it from the logs? + #[serde(rename = "logsBloom")] + pub logs_bloom: Bloom, +} + +impl TryFrom<&ReceiptJson> for ReceiptWithBloom { + type Error = String; + + fn try_from(receipt_json: &ReceiptJson) -> Result { + let cumulative_gas_used = receipt_json + .cumulative_gas_used + .try_into() + .map_err(|_| "Failed to convert U256 to u64".to_string())?; + + let receipt = Receipt { + tx_type: receipt_json.tx_type, + success: receipt_json.status, + cumulative_gas_used, + logs: receipt_json.logs.clone(), + // #[cfg(feature = "optimism")] + // deposit_nonce: None, // Handle Optimism-specific fields as necessary + // #[cfg(feature = "optimism")] + // deposit_receipt_version: None, + }; + + // Create the ReceiptWithBloom struct + Ok(ReceiptWithBloom { + bloom: receipt_json.logs_bloom, + receipt, + }) + } +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct ReceiptsFromBlock { + pub result: Vec, } fn status_to_bool<'de, D>(deserializer: D) -> Result @@ -32,7 +70,6 @@ where } } -// Custom deserialization function for TxType fn str_to_type<'de, D>(deserializer: D) -> Result where D: Deserializer<'de>, @@ -44,17 +81,31 @@ where TxType::try_from(tx_type_value).map_err(|_| serde::de::Error::custom("Invalid tx_type value")) } -#[derive(Debug, Deserialize, Serialize)] -pub struct ReceiptsFromBlock { - pub result: Vec, +pub fn hash_builder_root(receipts: &[ReceiptWithBloom]) -> B256 { + let mut index_buffer = Vec::new(); + let mut value_buffer = Vec::new(); + + let mut hb = HashBuilder::default(); + let receipts_len = receipts.len(); + for i in 0..receipts_len { + let index = adjust_index_for_rlp(i, receipts_len); + + index_buffer.clear(); + index.encode(&mut index_buffer); + + value_buffer.clear(); + receipts[index].encode_inner(&mut value_buffer, false); + hb.add_leaf(Nibbles::unpack(&index_buffer), &value_buffer); + } + + hb.root() } #[cfg(test)] mod tests { - use std::cell::LazyCell; - use crate::beacon_block::BlockWrapper; + use std::cell::LazyCell; use super::*; @@ -69,7 +120,7 @@ mod tests { ) }); - const RECEIPTS: LazyCell = LazyCell::new(|| { + const BLOCK_RECEIPTS: LazyCell = LazyCell::new(|| { serde_json::from_str(BLOCK_RECEIPTS_JSON).expect( "This is all the receipt data from a block, fetch with eth_getBlockReceipts method", ) @@ -77,13 +128,47 @@ mod tests { #[test] fn test_parse_wrapped_receipt_into_reth_receipt() { - let block_wrapper: &LazyCell = &BLOCK_WRAPPER; - let block = &block_wrapper.data.message; + let block_receipts: &LazyCell = &BLOCK_RECEIPTS; + let receipts_with_bloom: Result, String> = block_receipts + .result + .iter() + .map(|receipt_json| ReceiptWithBloom::try_from(receipt_json)) + .collect::, _>>(); + + // Check that the conversion was successful + assert!( + receipts_with_bloom.is_ok(), + "Conversion failed with error: {:?}", + receipts_with_bloom.err().unwrap() + ); + } - let block_body = block.body_deneb().unwrap(); + #[test] + fn test_compute_receipts_trie() { + let block_wrapper: &LazyCell = &BLOCK_WRAPPER; + let block: &::types::BeaconBlock<::types::MainnetEthSpec> = &block_wrapper.data.message; + let block_body: &::types::BeaconBlockBodyDeneb<::types::MainnetEthSpec> = + block.body_deneb().unwrap(); let payload = &block_body.execution_payload; - let receits_root = payload.execution_payload.receipts_root; + let receipts_root = payload.execution_payload.receipts_root; + + let block_receipts: &LazyCell = &BLOCK_RECEIPTS; + let receipts_with_bloom: Result, String> = block_receipts + .result + .iter() + .map(|receipt_json| ReceiptWithBloom::try_from(receipt_json)) + .collect::, _>>(); - let receipts = &RECEIPTS; + match receipts_with_bloom { + Ok(receipts) => { + let root: reth::revm::primitives::FixedBytes<32> = hash_builder_root(&receipts); + let root_h256 = H256::from(root.0); + assert_eq!(root_h256, receipts_root, "Roots do not match!"); + } + Err(e) => { + // Handle the error (e.g., by logging or panicking) + panic!("Failed to convert receipts: {}", e); + } + } } } From 5a5dc1506639c238ef96e55a1e3d0cae912d8086 Mon Sep 17 00:00:00 2001 From: pedro bufulin Date: Tue, 10 Sep 2024 20:40:14 -0300 Subject: [PATCH 23/48] feat: generate proofs with proof_retainer inside hashBuilder --- crates/forrestrie/src/execution_block.rs | 43 ++++++++++++++++++++---- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/crates/forrestrie/src/execution_block.rs b/crates/forrestrie/src/execution_block.rs index 3d33453b..d5257040 100644 --- a/crates/forrestrie/src/execution_block.rs +++ b/crates/forrestrie/src/execution_block.rs @@ -1,7 +1,7 @@ use alloy_rlp::Encodable; use ethers::prelude::*; -use reth::primitives::{Bloom, Log, Receipt, ReceiptWithBloom, TxType, B256}; -use reth_trie::{root::adjust_index_for_rlp, HashBuilder, Nibbles}; +use reth::primitives::{Bloom, Log, Receipt, ReceiptWithBloom, TxType}; +use reth_trie_common::{proof::ProofRetainer, root::adjust_index_for_rlp, HashBuilder, Nibbles}; use serde::{Deserialize, Deserializer, Serialize}; // reth::primitives::proofs::calculate_receipt_root; @@ -81,24 +81,48 @@ where TxType::try_from(tx_type_value).map_err(|_| serde::de::Error::custom("Invalid tx_type value")) } -pub fn hash_builder_root(receipts: &[ReceiptWithBloom]) -> B256 { +// builds the trie to generate proofs from the Receipts +// generate a different root. Make sure that the source of receipts sorts them by `logIndex` +pub fn build_trie_with_proofs( + receipts: &[ReceiptWithBloom], + receipts_idx: &[usize], +) -> HashBuilder { let mut index_buffer = Vec::new(); let mut value_buffer = Vec::new(); - let mut hb = HashBuilder::default(); + // Initialize ProofRetainer with the target nibbles (the keys for which we want proofs) + let targets: Vec = receipts_idx + .iter() + .map(|&i| { + let index = adjust_index_for_rlp(i, receipts.len()); + index.encode(&mut index_buffer); + Nibbles::unpack(&index_buffer) + }) + .collect(); + + let proof_retainer: ProofRetainer = ProofRetainer::new(targets); + let mut hb = HashBuilder::default().with_proof_retainer(proof_retainer); + let receipts_len = receipts.len(); + for i in 0..receipts_len { let index = adjust_index_for_rlp(i, receipts_len); index_buffer.clear(); + let nibbles = Nibbles::unpack(&index_buffer); index.encode(&mut index_buffer); value_buffer.clear(); receipts[index].encode_inner(&mut value_buffer, false); hb.add_leaf(Nibbles::unpack(&index_buffer), &value_buffer); + // Safely mutate the ProofRetainer if it exists + if let Some(ref mut proof_retainer) = hb.proof_retainer { + // Retain the proof if the current nibbles match any target + proof_retainer.retain(&nibbles, &value_buffer); + } } - hb.root() + hb } #[cfg(test)] @@ -144,7 +168,7 @@ mod tests { } #[test] - fn test_compute_receipts_trie() { + fn test_compute_receipts_trie_root_and_proof() { let block_wrapper: &LazyCell = &BLOCK_WRAPPER; let block: &::types::BeaconBlock<::types::MainnetEthSpec> = &block_wrapper.data.message; let block_body: &::types::BeaconBlockBodyDeneb<::types::MainnetEthSpec> = @@ -159,9 +183,12 @@ mod tests { .map(|receipt_json| ReceiptWithBloom::try_from(receipt_json)) .collect::, _>>(); + // computes the root and verify against existing data + let mut hb: HashBuilder; match receipts_with_bloom { Ok(receipts) => { - let root: reth::revm::primitives::FixedBytes<32> = hash_builder_root(&receipts); + hb = build_trie_with_proofs(&receipts, &[0, 1, 2]); + let root: reth::revm::primitives::FixedBytes<32> = hb.root(); let root_h256 = H256::from(root.0); assert_eq!(root_h256, receipts_root, "Roots do not match!"); } @@ -170,5 +197,7 @@ mod tests { panic!("Failed to convert receipts: {}", e); } } + + // verify the proofs } } From ad31c4ce01d06294c0a2bc69a7cefc0e5e4fb1eb Mon Sep 17 00:00:00 2001 From: pedro bufulin Date: Fri, 13 Sep 2024 18:23:55 -0300 Subject: [PATCH 24/48] feat: generate receipt proof with ProofRetainer and verify it against targets --- crates/forrestrie/src/execution_block.rs | 81 ++++++++++++++++++++---- 1 file changed, 69 insertions(+), 12 deletions(-) diff --git a/crates/forrestrie/src/execution_block.rs b/crates/forrestrie/src/execution_block.rs index d5257040..46c1010c 100644 --- a/crates/forrestrie/src/execution_block.rs +++ b/crates/forrestrie/src/execution_block.rs @@ -25,6 +25,19 @@ pub struct ReceiptJson { pub logs_bloom: Bloom, } +// represents leafs that are being generated proofs on +pub struct Target { + pub nibbles: Nibbles, + pub value: Vec, +} + +impl Target { + // Constructor to create a new Target + pub fn new(nibbles: Nibbles, value: Vec) -> Self { + Target { nibbles, value } + } +} + impl TryFrom<&ReceiptJson> for ReceiptWithBloom { type Error = String; @@ -95,6 +108,7 @@ pub fn build_trie_with_proofs( .iter() .map(|&i| { let index = adjust_index_for_rlp(i, receipts.len()); + index_buffer.clear(); index.encode(&mut index_buffer); Nibbles::unpack(&index_buffer) }) @@ -106,20 +120,15 @@ pub fn build_trie_with_proofs( let receipts_len = receipts.len(); for i in 0..receipts_len { - let index = adjust_index_for_rlp(i, receipts_len); - index_buffer.clear(); - let nibbles = Nibbles::unpack(&index_buffer); + value_buffer.clear(); + + let index = adjust_index_for_rlp(i, receipts_len); index.encode(&mut index_buffer); - value_buffer.clear(); receipts[index].encode_inner(&mut value_buffer, false); + // NOTICE: if the ProofRetainer is set, add_leaf automatically retains the proofs for the targets hb.add_leaf(Nibbles::unpack(&index_buffer), &value_buffer); - // Safely mutate the ProofRetainer if it exists - if let Some(ref mut proof_retainer) = hb.proof_retainer { - // Retain the proof if the current nibbles match any target - proof_retainer.retain(&nibbles, &value_buffer); - } } hb @@ -128,6 +137,10 @@ pub fn build_trie_with_proofs( #[cfg(test)] mod tests { + use k256::pkcs8::der::Encode; + use reth::primitives::B256; + use reth_trie_common::proof::verify_proof; + use crate::beacon_block::BlockWrapper; use std::cell::LazyCell; @@ -180,17 +193,39 @@ mod tests { let receipts_with_bloom: Result, String> = block_receipts .result .iter() - .map(|receipt_json| ReceiptWithBloom::try_from(receipt_json)) + .map(ReceiptWithBloom::try_from) .collect::, _>>(); // computes the root and verify against existing data let mut hb: HashBuilder; + let receipts_idx = &[0, 1, 2]; + let mut targets: Vec = Vec::new(); + match receipts_with_bloom { Ok(receipts) => { - hb = build_trie_with_proofs(&receipts, &[0, 1, 2]); + hb = build_trie_with_proofs(&receipts, receipts_idx); let root: reth::revm::primitives::FixedBytes<32> = hb.root(); let root_h256 = H256::from(root.0); assert_eq!(root_h256, receipts_root, "Roots do not match!"); + + let mut index_buffer = Vec::new(); + let mut value_buffer = Vec::new(); + + // build some of the targets to get proofs for them + let receipts_len = receipts.len(); + for i in receipts_idx { + index_buffer.clear(); + value_buffer.clear(); + + let index = adjust_index_for_rlp(*i, receipts_len); + index.encode(&mut index_buffer); + + println!("index_buffer (raw bytes): {:?}", &index_buffer); + + receipts[index].encode_inner(&mut value_buffer, false); + let nibble = Nibbles::unpack(&index_buffer); + targets.push(Target::new(nibble, value_buffer.clone())); + } } Err(e) => { // Handle the error (e.g., by logging or panicking) @@ -198,6 +233,28 @@ mod tests { } } - // verify the proofs + let proof = hb.take_proofs(); + let proof1 = proof + .iter() + .filter_map(|(k, v)| targets[0].nibbles.starts_with(k).then_some(v)); + + // verifies proof for retained targets + assert_eq!( + verify_proof( + hb.root(), + targets[0].nibbles.clone(), + Some(targets[0].value.to_vec()), + proof1.clone(), + ), + Ok(()) + ); + + // checks for non existent proof + // let index = adjust_index_for_rlp(*i, receipts_len); + // let target = Nibbles::unpack(0x); + // assert_eq!( + // verify_proof(hb.root(), target, None, proof.values()), + // Ok(()) + // ); } } From 20d70ff1bbd7b65d8520a5f2715e43c18e3e0fd4 Mon Sep 17 00:00:00 2001 From: pedro bufulin Date: Fri, 13 Sep 2024 19:10:25 -0300 Subject: [PATCH 25/48] refactor: method chaining --- crates/forrestrie/src/execution_block.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/forrestrie/src/execution_block.rs b/crates/forrestrie/src/execution_block.rs index 46c1010c..87047212 100644 --- a/crates/forrestrie/src/execution_block.rs +++ b/crates/forrestrie/src/execution_block.rs @@ -4,8 +4,6 @@ use reth::primitives::{Bloom, Log, Receipt, ReceiptWithBloom, TxType}; use reth_trie_common::{proof::ProofRetainer, root::adjust_index_for_rlp, HashBuilder, Nibbles}; use serde::{Deserialize, Deserializer, Serialize}; -// reth::primitives::proofs::calculate_receipt_root; - #[derive(Debug, Deserialize, Serialize)] pub struct ReceiptJson { #[serde(rename = "type")] @@ -169,7 +167,7 @@ mod tests { let receipts_with_bloom: Result, String> = block_receipts .result .iter() - .map(|receipt_json| ReceiptWithBloom::try_from(receipt_json)) + .map(ReceiptWithBloom::try_from) .collect::, _>>(); // Check that the conversion was successful From 42457674a5ca12027165fb9e15289d53baa9d100 Mon Sep 17 00:00:00 2001 From: pedro bufulin Date: Fri, 13 Sep 2024 19:37:01 -0300 Subject: [PATCH 26/48] refactor: rename receipt indexes variable to represent targets of proofs in trie, sort dependencies alphabetically --- crates/forrestrie/src/execution_block.rs | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/crates/forrestrie/src/execution_block.rs b/crates/forrestrie/src/execution_block.rs index 87047212..75ee244c 100644 --- a/crates/forrestrie/src/execution_block.rs +++ b/crates/forrestrie/src/execution_block.rs @@ -94,15 +94,12 @@ where // builds the trie to generate proofs from the Receipts // generate a different root. Make sure that the source of receipts sorts them by `logIndex` -pub fn build_trie_with_proofs( - receipts: &[ReceiptWithBloom], - receipts_idx: &[usize], -) -> HashBuilder { +pub fn build_trie_with_proofs(receipts: &[ReceiptWithBloom], target_idxs: &[usize]) -> HashBuilder { let mut index_buffer = Vec::new(); let mut value_buffer = Vec::new(); // Initialize ProofRetainer with the target nibbles (the keys for which we want proofs) - let targets: Vec = receipts_idx + let targets: Vec = target_idxs .iter() .map(|&i| { let index = adjust_index_for_rlp(i, receipts.len()); @@ -135,8 +132,6 @@ pub fn build_trie_with_proofs( #[cfg(test)] mod tests { - use k256::pkcs8::der::Encode; - use reth::primitives::B256; use reth_trie_common::proof::verify_proof; use crate::beacon_block::BlockWrapper; @@ -196,22 +191,21 @@ mod tests { // computes the root and verify against existing data let mut hb: HashBuilder; - let receipts_idx = &[0, 1, 2]; + let target_idxs = &[0, 1, 2]; let mut targets: Vec = Vec::new(); match receipts_with_bloom { Ok(receipts) => { - hb = build_trie_with_proofs(&receipts, receipts_idx); - let root: reth::revm::primitives::FixedBytes<32> = hb.root(); - let root_h256 = H256::from(root.0); - assert_eq!(root_h256, receipts_root, "Roots do not match!"); + hb = build_trie_with_proofs(&receipts, target_idxs); + let calculated_root = H256::from(hb.root().0); + assert_eq!(calculated_root, receipts_root, "Roots do not match!"); let mut index_buffer = Vec::new(); let mut value_buffer = Vec::new(); // build some of the targets to get proofs for them let receipts_len = receipts.len(); - for i in receipts_idx { + for i in target_idxs { index_buffer.clear(); value_buffer.clear(); From 8bffc6161984566e8227d79f6867c59938d53ce5 Mon Sep 17 00:00:00 2001 From: pedro bufulin Date: Fri, 13 Sep 2024 20:09:15 -0300 Subject: [PATCH 27/48] test: uses all targets in vector --- crates/forrestrie/src/execution_block.rs | 46 +++++++++++++----------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/crates/forrestrie/src/execution_block.rs b/crates/forrestrie/src/execution_block.rs index 75ee244c..e76c45ca 100644 --- a/crates/forrestrie/src/execution_block.rs +++ b/crates/forrestrie/src/execution_block.rs @@ -123,6 +123,7 @@ pub fn build_trie_with_proofs(receipts: &[ReceiptWithBloom], target_idxs: &[usiz receipts[index].encode_inner(&mut value_buffer, false); // NOTICE: if the ProofRetainer is set, add_leaf automatically retains the proofs for the targets + print!("{:?}", Nibbles::unpack(&index_buffer)); hb.add_leaf(Nibbles::unpack(&index_buffer), &value_buffer); } @@ -193,6 +194,7 @@ mod tests { let mut hb: HashBuilder; let target_idxs = &[0, 1, 2]; let mut targets: Vec = Vec::new(); + let receipts_len; match receipts_with_bloom { Ok(receipts) => { @@ -204,7 +206,7 @@ mod tests { let mut value_buffer = Vec::new(); // build some of the targets to get proofs for them - let receipts_len = receipts.len(); + receipts_len = receipts.len(); for i in target_idxs { index_buffer.clear(); value_buffer.clear(); @@ -212,10 +214,9 @@ mod tests { let index = adjust_index_for_rlp(*i, receipts_len); index.encode(&mut index_buffer); - println!("index_buffer (raw bytes): {:?}", &index_buffer); - receipts[index].encode_inner(&mut value_buffer, false); let nibble = Nibbles::unpack(&index_buffer); + println!("{:?} targets added", nibble); targets.push(Target::new(nibble, value_buffer.clone())); } } @@ -225,25 +226,30 @@ mod tests { } } - let proof = hb.take_proofs(); - let proof1 = proof - .iter() - .filter_map(|(k, v)| targets[0].nibbles.starts_with(k).then_some(v)); - // verifies proof for retained targets - assert_eq!( - verify_proof( - hb.root(), - targets[0].nibbles.clone(), - Some(targets[0].value.to_vec()), - proof1.clone(), - ), - Ok(()) - ); + let proof = hb.take_proofs(); + for target in targets.iter() { + let proof1 = proof + .iter() + .filter_map(|(k, v)| target.nibbles.starts_with(k).then_some(v)); + + assert_eq!( + verify_proof( + hb.root(), + target.nibbles.clone(), + Some(target.value.to_vec()), + proof1.clone(), + ), + Ok(()) + ); + } - // checks for non existent proof - // let index = adjust_index_for_rlp(*i, receipts_len); - // let target = Nibbles::unpack(0x); + // check for exclusion proof + // let mut index_buffer = Vec::new(); + // let index = adjust_index_for_rlp(6, receipts_len); + // index.encode(&mut index_buffer); + // let target = Nibbles::unpack(index_buffer); + // println!("target nibble for exclusion: {:?}", target); // assert_eq!( // verify_proof(hb.root(), target, None, proof.values()), // Ok(()) From 424f656444cbe96bec2cb6e21a6d53a93dff393a Mon Sep 17 00:00:00 2001 From: pedro bufulin Date: Fri, 13 Sep 2024 20:11:29 -0300 Subject: [PATCH 28/48] test: exclusion proof test --- crates/forrestrie/src/execution_block.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/crates/forrestrie/src/execution_block.rs b/crates/forrestrie/src/execution_block.rs index e76c45ca..0f47f399 100644 --- a/crates/forrestrie/src/execution_block.rs +++ b/crates/forrestrie/src/execution_block.rs @@ -245,14 +245,14 @@ mod tests { } // check for exclusion proof - // let mut index_buffer = Vec::new(); - // let index = adjust_index_for_rlp(6, receipts_len); - // index.encode(&mut index_buffer); - // let target = Nibbles::unpack(index_buffer); - // println!("target nibble for exclusion: {:?}", target); - // assert_eq!( - // verify_proof(hb.root(), target, None, proof.values()), - // Ok(()) - // ); + let mut index_buffer = Vec::new(); + let index = adjust_index_for_rlp(6, receipts_len); + index.encode(&mut index_buffer); + let target = Nibbles::unpack(index_buffer); + println!("target nibble for exclusion: {:?}", target); + assert_eq!( + verify_proof(hb.root(), target, None, proof.values()), + Ok(()) + ); } } From 5dcf95f97b0c9b71aca235cbf1b8e93723877d83 Mon Sep 17 00:00:00 2001 From: pedro bufulin Date: Thu, 19 Sep 2024 17:22:05 -0300 Subject: [PATCH 29/48] test: mvoes failing exclusion proof test and try_from test from this branch these are different topics and proof generation was already achieved in this branch --- crates/forrestrie/src/execution_block.rs | 40 +++++------------------- 1 file changed, 8 insertions(+), 32 deletions(-) diff --git a/crates/forrestrie/src/execution_block.rs b/crates/forrestrie/src/execution_block.rs index 0f47f399..f946bc04 100644 --- a/crates/forrestrie/src/execution_block.rs +++ b/crates/forrestrie/src/execution_block.rs @@ -1,6 +1,6 @@ use alloy_rlp::Encodable; use ethers::prelude::*; -use reth::primitives::{Bloom, Log, Receipt, ReceiptWithBloom, TxType}; +use reth::primitives::{Bloom, Receipt, ReceiptWithBloom, TxType}; use reth_trie_common::{proof::ProofRetainer, root::adjust_index_for_rlp, HashBuilder, Nibbles}; use serde::{Deserialize, Deserializer, Serialize}; @@ -50,13 +50,14 @@ impl TryFrom<&ReceiptJson> for ReceiptWithBloom { success: receipt_json.status, cumulative_gas_used, logs: receipt_json.logs.clone(), + // NOTICE: receipts will have more fields depending of the EVM chain. + // this is how to handle them in the futuro // #[cfg(feature = "optimism")] // deposit_nonce: None, // Handle Optimism-specific fields as necessary // #[cfg(feature = "optimism")] // deposit_receipt_version: None, }; - // Create the ReceiptWithBloom struct Ok(ReceiptWithBloom { bloom: receipt_json.logs_bloom, receipt, @@ -157,23 +158,6 @@ mod tests { ) }); - #[test] - fn test_parse_wrapped_receipt_into_reth_receipt() { - let block_receipts: &LazyCell = &BLOCK_RECEIPTS; - let receipts_with_bloom: Result, String> = block_receipts - .result - .iter() - .map(ReceiptWithBloom::try_from) - .collect::, _>>(); - - // Check that the conversion was successful - assert!( - receipts_with_bloom.is_ok(), - "Conversion failed with error: {:?}", - receipts_with_bloom.err().unwrap() - ); - } - #[test] fn test_compute_receipts_trie_root_and_proof() { let block_wrapper: &LazyCell = &BLOCK_WRAPPER; @@ -192,7 +176,9 @@ mod tests { // computes the root and verify against existing data let mut hb: HashBuilder; - let target_idxs = &[0, 1, 2]; + //target_idxs are the logIndexes for receipts to get proofs from. + // these values are arbitrary + let target_idxs = &[0, 1, 2, 129]; let mut targets: Vec = Vec::new(); let receipts_len; @@ -214,8 +200,9 @@ mod tests { let index = adjust_index_for_rlp(*i, receipts_len); index.encode(&mut index_buffer); - receipts[index].encode_inner(&mut value_buffer, false); let nibble = Nibbles::unpack(&index_buffer); + + receipts[index].encode_inner(&mut value_buffer, false); println!("{:?} targets added", nibble); targets.push(Target::new(nibble, value_buffer.clone())); } @@ -243,16 +230,5 @@ mod tests { Ok(()) ); } - - // check for exclusion proof - let mut index_buffer = Vec::new(); - let index = adjust_index_for_rlp(6, receipts_len); - index.encode(&mut index_buffer); - let target = Nibbles::unpack(index_buffer); - println!("target nibble for exclusion: {:?}", target); - assert_eq!( - verify_proof(hb.root(), target, None, proof.values()), - Ok(()) - ); } } From 7bbc705d7963ba80b894bc4cdf2653a21a0553e2 Mon Sep 17 00:00:00 2001 From: pedro bufulin Date: Thu, 19 Sep 2024 17:27:27 -0300 Subject: [PATCH 30/48] test: removes target index out of bounds for exclusion proof --- crates/forrestrie/src/execution_block.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/crates/forrestrie/src/execution_block.rs b/crates/forrestrie/src/execution_block.rs index f946bc04..d60e5f4b 100644 --- a/crates/forrestrie/src/execution_block.rs +++ b/crates/forrestrie/src/execution_block.rs @@ -1,6 +1,6 @@ use alloy_rlp::Encodable; use ethers::prelude::*; -use reth::primitives::{Bloom, Receipt, ReceiptWithBloom, TxType}; +use reth::primitives::{Bloom, Log, Receipt, ReceiptWithBloom, TxType}; use reth_trie_common::{proof::ProofRetainer, root::adjust_index_for_rlp, HashBuilder, Nibbles}; use serde::{Deserialize, Deserializer, Serialize}; @@ -124,7 +124,6 @@ pub fn build_trie_with_proofs(receipts: &[ReceiptWithBloom], target_idxs: &[usiz receipts[index].encode_inner(&mut value_buffer, false); // NOTICE: if the ProofRetainer is set, add_leaf automatically retains the proofs for the targets - print!("{:?}", Nibbles::unpack(&index_buffer)); hb.add_leaf(Nibbles::unpack(&index_buffer), &value_buffer); } @@ -178,7 +177,7 @@ mod tests { let mut hb: HashBuilder; //target_idxs are the logIndexes for receipts to get proofs from. // these values are arbitrary - let target_idxs = &[0, 1, 2, 129]; + let target_idxs = &[0, 1, 2]; let mut targets: Vec = Vec::new(); let receipts_len; @@ -203,7 +202,6 @@ mod tests { let nibble = Nibbles::unpack(&index_buffer); receipts[index].encode_inner(&mut value_buffer, false); - println!("{:?} targets added", nibble); targets.push(Target::new(nibble, value_buffer.clone())); } } From cb751d696c8e2f108ee05e349f7832ebb56a2c22 Mon Sep 17 00:00:00 2001 From: pedro bufulin Date: Thu, 19 Sep 2024 17:31:31 -0300 Subject: [PATCH 31/48] docs: TargetLeaf doc and name change --- crates/forrestrie/src/execution_block.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/crates/forrestrie/src/execution_block.rs b/crates/forrestrie/src/execution_block.rs index d60e5f4b..ded66d8c 100644 --- a/crates/forrestrie/src/execution_block.rs +++ b/crates/forrestrie/src/execution_block.rs @@ -23,16 +23,17 @@ pub struct ReceiptJson { pub logs_bloom: Bloom, } -// represents leafs that are being generated proofs on -pub struct Target { +/// Represents a leaf in the trie for which a proof is to be generated, i.e., the target of the proof. +/// The `nibbles` represent the path to the leaf in the trie, and the `value` is the data stored at the leaf. +pub struct TargetLeaf { pub nibbles: Nibbles, pub value: Vec, } -impl Target { - // Constructor to create a new Target +impl TargetLeaf { + // Constructor to create a new TargetLeaf pub fn new(nibbles: Nibbles, value: Vec) -> Self { - Target { nibbles, value } + TargetLeaf { nibbles, value } } } @@ -178,7 +179,7 @@ mod tests { //target_idxs are the logIndexes for receipts to get proofs from. // these values are arbitrary let target_idxs = &[0, 1, 2]; - let mut targets: Vec = Vec::new(); + let mut targets: Vec = Vec::new(); let receipts_len; match receipts_with_bloom { @@ -202,7 +203,7 @@ mod tests { let nibble = Nibbles::unpack(&index_buffer); receipts[index].encode_inner(&mut value_buffer, false); - targets.push(Target::new(nibble, value_buffer.clone())); + targets.push(TargetLeaf::new(nibble, value_buffer.clone())); } } Err(e) => { From 813ed13b7e9bad077e95990fae0c92c6437adef6 Mon Sep 17 00:00:00 2001 From: pedro bufulin Date: Thu, 19 Sep 2024 17:33:55 -0300 Subject: [PATCH 32/48] docs(README.md): adds `` around head-state.json From 7cde1db54c9be46d85e52e4896dccdd9a1c9bfc5 Mon Sep 17 00:00:00 2001 From: pedro bufulin Date: Wed, 23 Oct 2024 16:37:44 -0300 Subject: [PATCH 33/48] feat(lib.rs): adds execution_layer --- crates/forrestrie/src/execution_block.rs | 233 ----------------------- 1 file changed, 233 deletions(-) delete mode 100644 crates/forrestrie/src/execution_block.rs diff --git a/crates/forrestrie/src/execution_block.rs b/crates/forrestrie/src/execution_block.rs deleted file mode 100644 index ded66d8c..00000000 --- a/crates/forrestrie/src/execution_block.rs +++ /dev/null @@ -1,233 +0,0 @@ -use alloy_rlp::Encodable; -use ethers::prelude::*; -use reth::primitives::{Bloom, Log, Receipt, ReceiptWithBloom, TxType}; -use reth_trie_common::{proof::ProofRetainer, root::adjust_index_for_rlp, HashBuilder, Nibbles}; -use serde::{Deserialize, Deserializer, Serialize}; - -#[derive(Debug, Deserialize, Serialize)] -pub struct ReceiptJson { - #[serde(rename = "type")] - #[serde(deserialize_with = "str_to_type")] - pub tx_type: TxType, - #[serde(rename = "blockHash")] - pub block_hash: String, - #[serde(rename = "blockNumber")] - pub block_number: String, - pub logs: Vec, - #[serde(rename = "cumulativeGasUsed")] - pub cumulative_gas_used: U256, - #[serde(deserialize_with = "status_to_bool")] - pub status: bool, - // TODO: should we trust logsBloom provided or calculate it from the logs? - #[serde(rename = "logsBloom")] - pub logs_bloom: Bloom, -} - -/// Represents a leaf in the trie for which a proof is to be generated, i.e., the target of the proof. -/// The `nibbles` represent the path to the leaf in the trie, and the `value` is the data stored at the leaf. -pub struct TargetLeaf { - pub nibbles: Nibbles, - pub value: Vec, -} - -impl TargetLeaf { - // Constructor to create a new TargetLeaf - pub fn new(nibbles: Nibbles, value: Vec) -> Self { - TargetLeaf { nibbles, value } - } -} - -impl TryFrom<&ReceiptJson> for ReceiptWithBloom { - type Error = String; - - fn try_from(receipt_json: &ReceiptJson) -> Result { - let cumulative_gas_used = receipt_json - .cumulative_gas_used - .try_into() - .map_err(|_| "Failed to convert U256 to u64".to_string())?; - - let receipt = Receipt { - tx_type: receipt_json.tx_type, - success: receipt_json.status, - cumulative_gas_used, - logs: receipt_json.logs.clone(), - // NOTICE: receipts will have more fields depending of the EVM chain. - // this is how to handle them in the futuro - // #[cfg(feature = "optimism")] - // deposit_nonce: None, // Handle Optimism-specific fields as necessary - // #[cfg(feature = "optimism")] - // deposit_receipt_version: None, - }; - - Ok(ReceiptWithBloom { - bloom: receipt_json.logs_bloom, - receipt, - }) - } -} - -#[derive(Debug, Deserialize, Serialize)] -pub struct ReceiptsFromBlock { - pub result: Vec, -} - -fn status_to_bool<'de, D>(deserializer: D) -> Result -where - D: Deserializer<'de>, -{ - let status_str: &str = Deserialize::deserialize(deserializer)?; - match status_str { - "0x1" => Ok(true), - "0x0" => Ok(false), - _ => Err(serde::de::Error::custom("Invalid status value")), - } -} - -fn str_to_type<'de, D>(deserializer: D) -> Result -where - D: Deserializer<'de>, -{ - let tx_type_str: &str = Deserialize::deserialize(deserializer)?; - // Convert the hex string (without the "0x" prefix) to u8 - let tx_type_value = u8::from_str_radix(tx_type_str.trim_start_matches("0x"), 16) - .map_err(|_| serde::de::Error::custom("Invalid tx_type value"))?; - TxType::try_from(tx_type_value).map_err(|_| serde::de::Error::custom("Invalid tx_type value")) -} - -// builds the trie to generate proofs from the Receipts -// generate a different root. Make sure that the source of receipts sorts them by `logIndex` -pub fn build_trie_with_proofs(receipts: &[ReceiptWithBloom], target_idxs: &[usize]) -> HashBuilder { - let mut index_buffer = Vec::new(); - let mut value_buffer = Vec::new(); - - // Initialize ProofRetainer with the target nibbles (the keys for which we want proofs) - let targets: Vec = target_idxs - .iter() - .map(|&i| { - let index = adjust_index_for_rlp(i, receipts.len()); - index_buffer.clear(); - index.encode(&mut index_buffer); - Nibbles::unpack(&index_buffer) - }) - .collect(); - - let proof_retainer: ProofRetainer = ProofRetainer::new(targets); - let mut hb = HashBuilder::default().with_proof_retainer(proof_retainer); - - let receipts_len = receipts.len(); - - for i in 0..receipts_len { - index_buffer.clear(); - value_buffer.clear(); - - let index = adjust_index_for_rlp(i, receipts_len); - index.encode(&mut index_buffer); - - receipts[index].encode_inner(&mut value_buffer, false); - // NOTICE: if the ProofRetainer is set, add_leaf automatically retains the proofs for the targets - hb.add_leaf(Nibbles::unpack(&index_buffer), &value_buffer); - } - - hb -} - -#[cfg(test)] -mod tests { - - use reth_trie_common::proof::verify_proof; - - use crate::beacon_block::BlockWrapper; - use std::cell::LazyCell; - - use super::*; - - /// Deneb block JSON file shared among contributors. - /// The block hash is `0x5dde05ab1da7f768ed3ea2d53c6fa0d79c0c2283e52bb0d00842a4bdbf14c0ab`. - const DENEB_BLOCK_JSON: &str = include_str!("../../../bb-8786333.json"); - const BLOCK_RECEIPTS_JSON: &str = include_str!("../../../eb-19584570-receipts.json"); - - const BLOCK_WRAPPER: LazyCell = LazyCell::new(|| { - serde_json::from_str(DENEB_BLOCK_JSON).expect( - "For this spike we are using a Deneb block JSON file that has been shared among contributors", - ) - }); - - const BLOCK_RECEIPTS: LazyCell = LazyCell::new(|| { - serde_json::from_str(BLOCK_RECEIPTS_JSON).expect( - "This is all the receipt data from a block, fetch with eth_getBlockReceipts method", - ) - }); - - #[test] - fn test_compute_receipts_trie_root_and_proof() { - let block_wrapper: &LazyCell = &BLOCK_WRAPPER; - let block: &::types::BeaconBlock<::types::MainnetEthSpec> = &block_wrapper.data.message; - let block_body: &::types::BeaconBlockBodyDeneb<::types::MainnetEthSpec> = - block.body_deneb().unwrap(); - let payload = &block_body.execution_payload; - let receipts_root = payload.execution_payload.receipts_root; - - let block_receipts: &LazyCell = &BLOCK_RECEIPTS; - let receipts_with_bloom: Result, String> = block_receipts - .result - .iter() - .map(ReceiptWithBloom::try_from) - .collect::, _>>(); - - // computes the root and verify against existing data - let mut hb: HashBuilder; - //target_idxs are the logIndexes for receipts to get proofs from. - // these values are arbitrary - let target_idxs = &[0, 1, 2]; - let mut targets: Vec = Vec::new(); - let receipts_len; - - match receipts_with_bloom { - Ok(receipts) => { - hb = build_trie_with_proofs(&receipts, target_idxs); - let calculated_root = H256::from(hb.root().0); - assert_eq!(calculated_root, receipts_root, "Roots do not match!"); - - let mut index_buffer = Vec::new(); - let mut value_buffer = Vec::new(); - - // build some of the targets to get proofs for them - receipts_len = receipts.len(); - for i in target_idxs { - index_buffer.clear(); - value_buffer.clear(); - - let index = adjust_index_for_rlp(*i, receipts_len); - index.encode(&mut index_buffer); - - let nibble = Nibbles::unpack(&index_buffer); - - receipts[index].encode_inner(&mut value_buffer, false); - targets.push(TargetLeaf::new(nibble, value_buffer.clone())); - } - } - Err(e) => { - // Handle the error (e.g., by logging or panicking) - panic!("Failed to convert receipts: {}", e); - } - } - - // verifies proof for retained targets - let proof = hb.take_proofs(); - for target in targets.iter() { - let proof1 = proof - .iter() - .filter_map(|(k, v)| target.nibbles.starts_with(k).then_some(v)); - - assert_eq!( - verify_proof( - hb.root(), - target.nibbles.clone(), - Some(target.value.to_vec()), - proof1.clone(), - ), - Ok(()) - ); - } - } -} From aba6024e916d9d514ff88c3d5072d02cfaeeae2a Mon Sep 17 00:00:00 2001 From: pedro bufulin Date: Mon, 28 Oct 2024 16:28:06 -0300 Subject: [PATCH 34/48] test(execution_layer): add fake ReceiptJson generation method --- Cargo.lock | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index b150e9a6..142714da 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2357,6 +2357,29 @@ dependencies = [ "types", ] +[[package]] +name = "forrestrie-examples" +version = "0.1.1" +dependencies = [ + "bls", + "ethportal-api", + "fake", + "firehose-protos", + "futures", + "merkle_proof", + "primitive-types", + "prost", + "prost-build", + "prost-wkt", + "prost-wkt-types", + "serde", + "ssz_types 0.6.0", + "tonic", + "tonic-build", + "tree_hash 0.6.0", + "types", +] + [[package]] name = "forrestrie-examples" version = "0.1.1" From d94256e48fa165f6db4686e9951b3e963557565a Mon Sep 17 00:00:00 2001 From: pedro bufulin Date: Mon, 28 Oct 2024 17:36:27 -0300 Subject: [PATCH 35/48] test: random log generation attempt --- Cargo.lock | 1 - 1 file changed, 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 142714da..3b337324 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2363,7 +2363,6 @@ version = "0.1.1" dependencies = [ "bls", "ethportal-api", - "fake", "firehose-protos", "futures", "merkle_proof", From 21bcfffab0232a78153312fce9d3c2e5536c2a78 Mon Sep 17 00:00:00 2001 From: pedro bufulin Date: Thu, 31 Oct 2024 00:30:52 -0300 Subject: [PATCH 36/48] test: changes method of matching target nodes for verifying proof --- crates/forrestrie/src/execution_layer.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/forrestrie/src/execution_layer.rs b/crates/forrestrie/src/execution_layer.rs index 5c486906..05ed7dfc 100644 --- a/crates/forrestrie/src/execution_layer.rs +++ b/crates/forrestrie/src/execution_layer.rs @@ -205,6 +205,8 @@ mod tests { .map(ReceiptWithBloom::try_from) .collect::, _>>(); + dbg!(&receipts_with_bloom); + // computes the root and verify against existing data let mut hb: HashBuilder; //target_idxs are the logIndexes for receipts to get proofs from. From 6badc7fc38d244a7d3285f139d86b6328ec780fb Mon Sep 17 00:00:00 2001 From: pedro bufulin Date: Thu, 31 Oct 2024 11:01:21 -0300 Subject: [PATCH 37/48] test: add static fake log, call root() to retain proofs --- crates/forrestrie/src/execution_layer.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/forrestrie/src/execution_layer.rs b/crates/forrestrie/src/execution_layer.rs index 05ed7dfc..5c486906 100644 --- a/crates/forrestrie/src/execution_layer.rs +++ b/crates/forrestrie/src/execution_layer.rs @@ -205,8 +205,6 @@ mod tests { .map(ReceiptWithBloom::try_from) .collect::, _>>(); - dbg!(&receipts_with_bloom); - // computes the root and verify against existing data let mut hb: HashBuilder; //target_idxs are the logIndexes for receipts to get proofs from. From 574666c9162953fdeea8f2256b51826800051bfd Mon Sep 17 00:00:00 2001 From: pedro bufulin Date: Fri, 1 Nov 2024 14:38:57 -0300 Subject: [PATCH 38/48] refactor: change vector initialization to avoid clear() usage --- Cargo.lock | 134 ++++++++++------------- crates/forrestrie/src/execution_layer.rs | 26 ++--- 2 files changed, 69 insertions(+), 91 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3b337324..131af377 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -96,9 +96,9 @@ dependencies = [ [[package]] name = "alloy-chains" -version = "0.1.44" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c660915971620592abe2c292c859957eb60e73a60c0eba34a6793eea60512cff" +checksum = "836cf02383d9ebb35502d379bcd1ae803155094077eaab9c29131d888cd5fa3e" dependencies = [ "alloy-primitives 0.8.10", "num_enum", @@ -299,7 +299,7 @@ checksum = "2b09cae092c27b6f1bde952653a22708691802e57bfef4a2973b80bea21efd3f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -355,7 +355,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -371,7 +371,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", "syn-solidity", "tiny-keccak", ] @@ -387,7 +387,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", "syn-solidity", ] @@ -476,9 +476,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8365de52b16c035ff4fcafe0092ba9390540e3e352870ac09933bebcaa2c8c56" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anstyle-parse" @@ -510,9 +510,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.91" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c042108f3ed77fd83760a5fd79b53be043192bb3b9dba91d8c574c0ada7850c8" +checksum = "74f37166d7d48a0284b99dd824694c26119c700b53bf0d1540cdb147dbdaaf13" [[package]] name = "arbitrary" @@ -681,7 +681,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -692,7 +692,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -720,7 +720,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -882,7 +882,7 @@ dependencies = [ "regex", "rustc-hash 1.1.0", "shlex", - "syn 2.0.85", + "syn 2.0.86", "which", ] @@ -1167,7 +1167,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -1471,7 +1471,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -1519,7 +1519,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -1541,7 +1541,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core 0.20.10", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -1592,7 +1592,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -1605,7 +1605,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version 0.4.1", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -1626,7 +1626,7 @@ dependencies = [ "convert_case 0.6.0", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", "unicode-xid", ] @@ -1831,7 +1831,7 @@ checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -2048,7 +2048,7 @@ dependencies = [ "darling 0.20.10", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -2357,28 +2357,6 @@ dependencies = [ "types", ] -[[package]] -name = "forrestrie-examples" -version = "0.1.1" -dependencies = [ - "bls", - "ethportal-api", - "firehose-protos", - "futures", - "merkle_proof", - "primitive-types", - "prost", - "prost-build", - "prost-wkt", - "prost-wkt-types", - "serde", - "ssz_types 0.6.0", - "tonic", - "tonic-build", - "tree_hash 0.6.0", - "types", -] - [[package]] name = "forrestrie-examples" version = "0.1.1" @@ -2475,7 +2453,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -3045,9 +3023,9 @@ dependencies = [ [[package]] name = "insta" -version = "1.41.0" +version = "1.41.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f72d3e19488cf7d8ea52d2fc0f8754fc933398b337cd3cbdb28aaeb35159ef" +checksum = "7e9ffc4d4892617c50a928c52b2961cb5174b6fc6ebf252b2fac9d21955c48b8" dependencies = [ "console", "lazy_static", @@ -3283,7 +3261,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -3817,7 +3795,7 @@ checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -3956,7 +3934,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -4107,7 +4085,7 @@ checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -4200,7 +4178,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" dependencies = [ "proc-macro2", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -4268,7 +4246,7 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -4327,7 +4305,7 @@ dependencies = [ "prost", "prost-types", "regex", - "syn 2.0.85", + "syn 2.0.86", "tempfile", ] @@ -4341,7 +4319,7 @@ dependencies = [ "itertools 0.13.0", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -4696,7 +4674,7 @@ dependencies = [ "convert_case 0.6.0", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -4950,7 +4928,7 @@ dependencies = [ "proc-macro2", "quote", "rust-embed-utils", - "syn 2.0.85", + "syn 2.0.86", "walkdir", ] @@ -5284,7 +5262,7 @@ checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -5477,7 +5455,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -5613,7 +5591,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -5672,9 +5650,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.85" +version = "2.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56" +checksum = "e89275301d38033efb81a6e60e3497e734dfcc62571f2854bf4b16690398824c" dependencies = [ "proc-macro2", "quote", @@ -5690,7 +5668,7 @@ dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -5759,22 +5737,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.65" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d11abd9594d9b38965ef50805c5e469ca9cc6f197f883f717e0269a3057b3d5" +checksum = "5d171f59dbaa811dbbb1aee1e73db92ec2b122911a48e1390dfe327a821ddede" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.65" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602" +checksum = "b08be0f17bd307950653ce45db00cd31200d82b624b36e181337d9c7d92765b5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -5909,7 +5887,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -6034,7 +6012,7 @@ dependencies = [ "prost-build", "prost-types", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -6103,7 +6081,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -6200,7 +6178,7 @@ dependencies = [ "darling 0.20.10", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -6338,7 +6316,7 @@ checksum = "70b20a22c42c8f1cd23ce5e34f165d4d37038f5b663ad20fb6adbdf029172483" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -6604,7 +6582,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", "wasm-bindgen-shared", ] @@ -6638,7 +6616,7 @@ checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -6881,7 +6859,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] @@ -6901,7 +6879,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.86", ] [[package]] diff --git a/crates/forrestrie/src/execution_layer.rs b/crates/forrestrie/src/execution_layer.rs index 5c486906..66c57618 100644 --- a/crates/forrestrie/src/execution_layer.rs +++ b/crates/forrestrie/src/execution_layer.rs @@ -152,35 +152,35 @@ where // builds the trie to generate proofs from the Receipts // generate a different root. Make sure that the source of receipts sorts them by `logIndex` pub fn build_trie_with_proofs(receipts: &[ReceiptWithBloom], target_idxs: &[usize]) -> HashBuilder { - let mut index_buffer = Vec::new(); - let mut value_buffer = Vec::new(); - // Initialize ProofRetainer with the target nibbles (the keys for which we want proofs) + let receipts_len = receipts.len(); let targets: Vec = target_idxs .iter() .map(|&i| { - let index = adjust_index_for_rlp(i, receipts.len()); - index_buffer.clear(); + let index = adjust_index_for_rlp(i, receipts_len); + let mut index_buffer = Vec::new(); index.encode(&mut index_buffer); Nibbles::unpack(&index_buffer) }) .collect(); - let proof_retainer: ProofRetainer = ProofRetainer::new(targets); + let proof_retainer = ProofRetainer::new(targets); let mut hb = HashBuilder::default().with_proof_retainer(proof_retainer); - let receipts_len = receipts.len(); - for i in 0..receipts_len { - index_buffer.clear(); - value_buffer.clear(); - + // Adjust the index for RLP let index = adjust_index_for_rlp(i, receipts_len); + + // Encode the index into nibbles + let mut index_buffer = Vec::new(); index.encode(&mut index_buffer); + let index_nibbles = Nibbles::unpack(&index_buffer); + // Encode the receipt value + let mut value_buffer = Vec::new(); receipts[index].encode_inner(&mut value_buffer, false); - // NOTICE: if the ProofRetainer is set, add_leaf automatically retains the proofs for the targets - hb.add_leaf(Nibbles::unpack(&index_buffer), &value_buffer); + + hb.add_leaf(index_nibbles, &value_buffer); } hb From 73de5cdf49208376c80f09dcb9b1f142d7ba8a2f Mon Sep 17 00:00:00 2001 From: pedro bufulin Date: Fri, 1 Nov 2024 14:54:38 -0300 Subject: [PATCH 39/48] docs: add top-level doc --- crates/forrestrie/src/execution_layer.rs | 25 ++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/crates/forrestrie/src/execution_layer.rs b/crates/forrestrie/src/execution_layer.rs index 66c57618..fb7f3e95 100644 --- a/crates/forrestrie/src/execution_layer.rs +++ b/crates/forrestrie/src/execution_layer.rs @@ -1,3 +1,28 @@ +//! Execution Layer functionality to build a Merkle Patricia Trie (MPT) from Ethereum receipts +//! and generate inclusion proofs for specified receipts within the trie. It includes data structures +//! for parsing and handling receipt data, as well as utilities for encoding and decoding as required +//! by the Ethereum specification.. +//! +//! ### Example +//! +//! ```rust +//! // Assume `receipts_json` is a vector of deserialized receipt objects from a execution block given by a Ethereum node. +//! let receipts_with_bloom: Vec = receipts_json +//! .iter() +//! .map(ReceiptWithBloom::try_from) +//! .collect::>()?; +//! +//! // Specify the indices of receipts for which proofs are needed. +//! let target_indices = &[0, 2, 5]; +//! +//! // Build the trie and obtain proofs. +//! let hash_builder = build_trie_with_proofs(&receipts_with_bloom, target_indices); +//! +//! // Retrieve the root hash of the trie, and retain the proofs so they can be verified. +//! let trie_root = hash_builder.root(); +//! +//! ``` + use alloy_primitives::{Bloom, U256}; use alloy_rlp::Encodable; use reth_primitives::{Log, Receipt, ReceiptWithBloom, TxType}; From 71ade4ac4c5f6efa443ae2d98f22d3e24aa227d3 Mon Sep 17 00:00:00 2001 From: pedro bufulin Date: Fri, 1 Nov 2024 15:08:58 -0300 Subject: [PATCH 40/48] refactor: fix clippy --- crates/forrestrie/src/execution_layer.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/forrestrie/src/execution_layer.rs b/crates/forrestrie/src/execution_layer.rs index fb7f3e95..2d5e3049 100644 --- a/crates/forrestrie/src/execution_layer.rs +++ b/crates/forrestrie/src/execution_layer.rs @@ -79,7 +79,7 @@ impl ReceiptJson { ) } - let logs: Vec = (0..5).into_iter().map(|_| fake_log()).collect(); + let logs: Vec = (0..5).map(|_| fake_log()).collect(); ReceiptJson { tx_type: TxType::Eip1559, // Replace with any desired variant @@ -221,8 +221,7 @@ mod tests { // TODO: instead of generating receipts, pick a small exempt from // the execution block that fits here. It should work better, // since faking logs requires the log to be properly rlp encoded - let block_receipts: ReceiptsFromBlock = - (0..10).into_iter().map(|_| ReceiptJson::fake()).collect(); + let block_receipts: ReceiptsFromBlock = (0_i32..10).map(|_| ReceiptJson::fake()).collect(); let receipts_with_bloom: Result, String> = block_receipts .result From 506b8cba2d24a8ceb6a5f9eb8f92b92c7b1c7b6f Mon Sep 17 00:00:00 2001 From: pedro bufulin Date: Fri, 1 Nov 2024 15:11:50 -0300 Subject: [PATCH 41/48] docs: ignore rust code for testing --- crates/forrestrie/src/execution_layer.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/forrestrie/src/execution_layer.rs b/crates/forrestrie/src/execution_layer.rs index 2d5e3049..3e053282 100644 --- a/crates/forrestrie/src/execution_layer.rs +++ b/crates/forrestrie/src/execution_layer.rs @@ -5,8 +5,8 @@ //! //! ### Example //! -//! ```rust -//! // Assume `receipts_json` is a vector of deserialized receipt objects from a execution block given by a Ethereum node. +//! ```rust,ignore +//! // Assume `receipts_json` is a vector of deserialized receipt objects from an execution block given by an Ethereum node. //! let receipts_with_bloom: Vec = receipts_json //! .iter() //! .map(ReceiptWithBloom::try_from) @@ -20,7 +20,6 @@ //! //! // Retrieve the root hash of the trie, and retain the proofs so they can be verified. //! let trie_root = hash_builder.root(); -//! //! ``` use alloy_primitives::{Bloom, U256}; From c99a8ae812f1ed37a9b2154bb225ca13e7d92ec4 Mon Sep 17 00:00:00 2001 From: pedro bufulin Date: Mon, 4 Nov 2024 10:13:11 -0300 Subject: [PATCH 42/48] refactor: remove TODO --- crates/forrestrie/src/execution_layer.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/crates/forrestrie/src/execution_layer.rs b/crates/forrestrie/src/execution_layer.rs index 3e053282..36ace749 100644 --- a/crates/forrestrie/src/execution_layer.rs +++ b/crates/forrestrie/src/execution_layer.rs @@ -217,9 +217,6 @@ mod tests { #[test] fn test_compute_receipts_trie_root_and_proof() { - // TODO: instead of generating receipts, pick a small exempt from - // the execution block that fits here. It should work better, - // since faking logs requires the log to be properly rlp encoded let block_receipts: ReceiptsFromBlock = (0_i32..10).map(|_| ReceiptJson::fake()).collect(); let receipts_with_bloom: Result, String> = block_receipts From a63b7da3ff2f0990c4a90e874abf18998db0e9cf Mon Sep 17 00:00:00 2001 From: pedro bufulin Date: Mon, 4 Nov 2024 11:00:23 -0300 Subject: [PATCH 43/48] refactor: remove backup file for example code --- .../examples/receipts_proof.bak | 83 ------------------- 1 file changed, 83 deletions(-) delete mode 100644 crates/forrestrie-examples/examples/receipts_proof.bak diff --git a/crates/forrestrie-examples/examples/receipts_proof.bak b/crates/forrestrie-examples/examples/receipts_proof.bak deleted file mode 100644 index 8d9700ab..00000000 --- a/crates/forrestrie-examples/examples/receipts_proof.bak +++ /dev/null @@ -1,83 +0,0 @@ -// //! # Execution Layer block receipt proofs -// //! -// //! -// //! This is a scenario where it is necessary to prove a receipt to be in a 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() { -// let block_wrapper: &LazyCell = &BLOCK_WRAPPER; -// let block: &::types::BeaconBlock<::types::MainnetEthSpec> = &block_wrapper.data.message; -// let block_body: &::types::BeaconBlockBodyDeneb<::types::MainnetEthSpec> = -// block.body_deneb().unwrap(); -// let payload = &block_body.execution_payload; -// let receipts_root = payload.execution_payload.receipts_root; - -// let block_receipts: &LazyCell = &BLOCK_RECEIPTS; -// let receipts_with_bloom: Result, String> = block_receipts -// .result -// .iter() -// .map(ReceiptWithBloom::try_from) -// .collect::, _>>(); - -// // computes the root and verify against existing data -// let mut hb: HashBuilder; -// //target_idxs are the logIndexes for receipts to get proofs from. -// // these values are arbitrary -// let target_idxs = &[0, 1, 2]; -// let mut targets: Vec = Vec::new(); -// let receipts_len; - -// match receipts_with_bloom { -// Ok(receipts) => { -// hb = build_trie_with_proofs(&receipts, target_idxs); -// let calculated_root = H256::from(hb.root().0); -// assert_eq!(calculated_root, receipts_root, "Roots do not match!"); - -// let mut index_buffer = Vec::new(); -// let mut value_buffer = Vec::new(); - -// // build some of the targets to get proofs for them -// receipts_len = receipts.len(); -// for i in target_idxs { -// index_buffer.clear(); -// value_buffer.clear(); - -// let index = adjust_index_for_rlp(*i, receipts_len); -// index.encode(&mut index_buffer); - -// let nibble = Nibbles::unpack(&index_buffer); - -// receipts[index].encode_inner(&mut value_buffer, false); -// targets.push(TargetLeaf::new(nibble, value_buffer.clone())); -// } -// } -// Err(e) => { -// // Handle the error (e.g., by logging or panicking) -// panic!("Failed to convert receipts: {}", e); -// } -// } - -// // verifies proof for retained targets -// let proof = hb.take_proof_nodes(); -// for target in targets.iter() { -// let proof1 = proof -// .iter() -// .filter_map(|(k, v)| target.nibbles.starts_with(k).then_some(v)); - -// assert_eq!( -// verify_proof( -// hb.root(), -// target.nibbles.clone(), -// Some(target.value.to_vec()), -// proof1.clone(), -// ), -// Ok(()) -// ); -// } -// } From d17f4e8d849ad32261d68435dc4b438f4c2c4d7e Mon Sep 17 00:00:00 2001 From: pedro bufulin Date: Mon, 4 Nov 2024 11:02:59 -0300 Subject: [PATCH 44/48] docs: correct punctuation --- crates/forrestrie/src/execution_layer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/forrestrie/src/execution_layer.rs b/crates/forrestrie/src/execution_layer.rs index 36ace749..269369b2 100644 --- a/crates/forrestrie/src/execution_layer.rs +++ b/crates/forrestrie/src/execution_layer.rs @@ -1,7 +1,7 @@ //! Execution Layer functionality to build a Merkle Patricia Trie (MPT) from Ethereum receipts //! and generate inclusion proofs for specified receipts within the trie. It includes data structures //! for parsing and handling receipt data, as well as utilities for encoding and decoding as required -//! by the Ethereum specification.. +//! by the Ethereum specification. //! //! ### Example //! From b8b0b7548925503b4e9f71f38e20a78dc87a5e3f Mon Sep 17 00:00:00 2001 From: pedro bufulin Date: Mon, 4 Nov 2024 11:05:05 -0300 Subject: [PATCH 45/48] refactor(firehose-protos/build): remove legacy arg --- crates/firehose-protos/build.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/firehose-protos/build.rs b/crates/firehose-protos/build.rs index 2dbed275..5c59d07c 100644 --- a/crates/firehose-protos/build.rs +++ b/crates/firehose-protos/build.rs @@ -13,7 +13,6 @@ fn main() { config.extern_path(".google.protobuf.Timestamp", "::prost_wkt_types::Timestamp"); tonic_build::configure() - .protoc_arg("--experimental_allow_proto3_optional") .build_client(true) .file_descriptor_set_path(out_dir.join("descriptors.bin")) .compile_protos_with_config( From 835508ae9c2b2ddbbb32feb67785e6b48f5270e8 Mon Sep 17 00:00:00 2001 From: pedro bufulin Date: Mon, 4 Nov 2024 11:09:39 -0300 Subject: [PATCH 46/48] docs: add / to achieve item-level documentation --- crates/forrestrie/src/execution_layer.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/forrestrie/src/execution_layer.rs b/crates/forrestrie/src/execution_layer.rs index 269369b2..9cd8bbfd 100644 --- a/crates/forrestrie/src/execution_layer.rs +++ b/crates/forrestrie/src/execution_layer.rs @@ -173,8 +173,8 @@ where TxType::try_from(tx_type_value).map_err(|_| serde::de::Error::custom("Invalid tx_type value")) } -// builds the trie to generate proofs from the Receipts -// generate a different root. Make sure that the source of receipts sorts them by `logIndex` +/// builds the trie to generate proofs from the Receipts +/// generate a different root. Make sure that the source of receipts sorts them by `logIndex` pub fn build_trie_with_proofs(receipts: &[ReceiptWithBloom], target_idxs: &[usize]) -> HashBuilder { // Initialize ProofRetainer with the target nibbles (the keys for which we want proofs) let receipts_len = receipts.len(); From f7124c28e8b58bed144948d643d8fa2032c60406 Mon Sep 17 00:00:00 2001 From: pedro bufulin Date: Mon, 4 Nov 2024 11:22:57 -0300 Subject: [PATCH 47/48] docs: move doc of usage to the `build_trie_with_proofs` --- crates/forrestrie/src/execution_layer.rs | 37 ++++++++++++------------ 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/crates/forrestrie/src/execution_layer.rs b/crates/forrestrie/src/execution_layer.rs index 9cd8bbfd..fa61a9cf 100644 --- a/crates/forrestrie/src/execution_layer.rs +++ b/crates/forrestrie/src/execution_layer.rs @@ -2,25 +2,6 @@ //! and generate inclusion proofs for specified receipts within the trie. It includes data structures //! for parsing and handling receipt data, as well as utilities for encoding and decoding as required //! by the Ethereum specification. -//! -//! ### Example -//! -//! ```rust,ignore -//! // Assume `receipts_json` is a vector of deserialized receipt objects from an execution block given by an Ethereum node. -//! let receipts_with_bloom: Vec = receipts_json -//! .iter() -//! .map(ReceiptWithBloom::try_from) -//! .collect::>()?; -//! -//! // Specify the indices of receipts for which proofs are needed. -//! let target_indices = &[0, 2, 5]; -//! -//! // Build the trie and obtain proofs. -//! let hash_builder = build_trie_with_proofs(&receipts_with_bloom, target_indices); -//! -//! // Retrieve the root hash of the trie, and retain the proofs so they can be verified. -//! let trie_root = hash_builder.root(); -//! ``` use alloy_primitives::{Bloom, U256}; use alloy_rlp::Encodable; @@ -175,6 +156,24 @@ where /// builds the trie to generate proofs from the Receipts /// generate a different root. Make sure that the source of receipts sorts them by `logIndex` +/// # Example +/// +/// ```no_run +/// // Assume `receipts_json` is a vector of deserialized receipt objects from an execution block given by an Ethereum node. +/// let receipts_with_bloom: Vec = receipts_json +/// .iter() +/// .map(ReceiptWithBloom::try_from) +/// .collect::>()?; +/// +/// // Specify the indices of receipts for which proofs are needed. +/// let target_indices = &[0, 2, 5]; +/// +/// // Build the trie and obtain proofs. +/// let hash_builder = build_trie_with_proofs(&receipts_with_bloom, target_indices); +/// +/// // Retrieve the root hash of the trie, and retain the proofs so they can be verified. +/// let trie_root = hash_builder.root(); +/// ``` pub fn build_trie_with_proofs(receipts: &[ReceiptWithBloom], target_idxs: &[usize]) -> HashBuilder { // Initialize ProofRetainer with the target nibbles (the keys for which we want proofs) let receipts_len = receipts.len(); From 63ae4156818772fe6d5d4365e9beba297ccc2512 Mon Sep 17 00:00:00 2001 From: pedro bufulin Date: Mon, 4 Nov 2024 11:43:40 -0300 Subject: [PATCH 48/48] docs: fix compile example --- crates/forrestrie/src/execution_layer.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/crates/forrestrie/src/execution_layer.rs b/crates/forrestrie/src/execution_layer.rs index fa61a9cf..839a72a2 100644 --- a/crates/forrestrie/src/execution_layer.rs +++ b/crates/forrestrie/src/execution_layer.rs @@ -159,17 +159,20 @@ where /// # Example /// /// ```no_run +/// # use reth_primitives::ReceiptWithBloom; +/// # use forrestrie::execution_layer::build_trie_with_proofs; +/// # let receipts_json = vec![]; /// // Assume `receipts_json` is a vector of deserialized receipt objects from an execution block given by an Ethereum node. /// let receipts_with_bloom: Vec = receipts_json /// .iter() /// .map(ReceiptWithBloom::try_from) -/// .collect::>()?; +/// .collect::>().unwrap(); /// /// // Specify the indices of receipts for which proofs are needed. /// let target_indices = &[0, 2, 5]; /// /// // Build the trie and obtain proofs. -/// let hash_builder = build_trie_with_proofs(&receipts_with_bloom, target_indices); +/// let mut hash_builder = build_trie_with_proofs(&receipts_with_bloom, target_indices); /// /// // Retrieve the root hash of the trie, and retain the proofs so they can be verified. /// let trie_root = hash_builder.root();