Skip to content

Commit

Permalink
refactor: mv proofs mod to reth-primitives-traits and split tests (pa…
Browse files Browse the repository at this point in the history
  • Loading branch information
lean-apple authored Jan 20, 2025
1 parent f527b5a commit d2ad477
Show file tree
Hide file tree
Showing 9 changed files with 184 additions and 27 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

81 changes: 78 additions & 3 deletions crates/ethereum/primitives/src/receipt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ use alloy_consensus::{
Eip2718EncodableReceipt, Eip658Value, ReceiptWithBloom, RlpDecodableReceipt,
RlpEncodableReceipt, TxReceipt, TxType, Typed2718,
};
use alloy_primitives::{Bloom, Log};
use alloy_eips::eip2718::Encodable2718;
use alloy_primitives::{Bloom, Log, B256};
use alloy_rlp::{BufMut, Decodable, Encodable, Header};
use reth_primitives_traits::InMemorySize;
use reth_primitives_traits::{proofs::ordered_trie_root_with_encoder, InMemorySize};
use serde::{Deserialize, Serialize};

/// Typed ethereum transaction receipt.
Expand Down Expand Up @@ -80,6 +81,13 @@ impl Receipt {
logs_bloom,
})
}

/// Calculates the receipt root for a header for the reference type of [Receipt].
///
/// NOTE: Prefer `proofs::calculate_receipt_root` if you have log blooms memoized.
pub fn calculate_receipt_root_no_memo(receipts: &[&Self]) -> B256 {
ordered_trie_root_with_encoder(receipts, |r, buf| r.with_bloom_ref().encode_2718(buf))
}
}

impl Eip2718EncodableReceipt for Receipt {
Expand Down Expand Up @@ -188,9 +196,21 @@ impl reth_primitives_traits::Receipt for Receipt {}
#[cfg(test)]
mod tests {
use super::*;
use crate::TransactionSigned;
use alloy_eips::eip2718::Encodable2718;
use alloy_primitives::{address, b256, bytes, hex_literal::hex, Bytes};
use alloy_primitives::{
address, b256, bloom, bytes, hex_literal::hex, Address, Bytes, Log, LogData,
};
use alloy_rlp::Decodable;
use reth_codecs::Compact;
use reth_primitives_traits::proofs::{
calculate_receipt_root, calculate_transaction_root, calculate_withdrawals_root,
};

/// Ethereum full block.
///
/// Withdrawals can be optionally included at the end of the RLP encoded message.
pub(crate) type Block<T = TransactionSigned> = alloy_consensus::Block<T>;

#[test]
fn test_decode_receipt() {
Expand Down Expand Up @@ -319,4 +339,59 @@ mod tests {
"Encoded length for legacy receipt should match the actual encoded data length"
);
}

#[test]
fn check_transaction_root() {
let data = &hex!("f90262f901f9a092230ce5476ae868e98c7979cfc165a93f8b6ad1922acf2df62e340916efd49da01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa02307107a867056ca33b5087e77c4174f47625e48fb49f1c70ced34890ddd88f3a08151d548273f6683169524b66ca9fe338b9ce42bc3540046c828fd939ae23bcba0c598f69a5674cae9337261b669970e24abc0b46e6d284372a239ec8ccbf20b0ab901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000018502540be40082a8618203e800a00000000000000000000000000000000000000000000000000000000000000000880000000000000000f863f861800a8405f5e10094100000000000000000000000000000000000000080801ba07e09e26678ed4fac08a249ebe8ed680bf9051a5e14ad223e4b2b9d26e0208f37a05f6e3f188e3e6eab7d7d3b6568f5eac7d687b08d307d3154ccd8c87b4630509bc0");
let block_rlp = &mut data.as_slice();
let block: Block = Block::decode(block_rlp).unwrap();

let tx_root = calculate_transaction_root(&block.body.transactions);
assert_eq!(block.transactions_root, tx_root, "Must be the same");
}

#[test]
fn check_withdrawals_root() {
// Single withdrawal, amount 0
// https://github.com/ethereum/tests/blob/9760400e667eba241265016b02644ef62ab55de2/BlockchainTests/EIPTests/bc4895-withdrawals/amountIs0.json
let data = &hex!("f90238f90219a0151934ad9b654c50197f37018ee5ee9bb922dec0a1b5e24a6d679cb111cdb107a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa0046119afb1ab36aaa8f66088677ed96cd62762f6d3e65642898e189fbe702d51a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008001887fffffffffffffff8082079e42a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b42188000000000000000009a048a703da164234812273ea083e4ec3d09d028300cd325b46a6a75402e5a7ab95c0c0d9d8808094c94f5374fce5edbc8e2a8697c15331677e6ebf0b80");
let block: Block = Block::decode(&mut data.as_slice()).unwrap();
assert!(block.body.withdrawals.is_some());
let withdrawals = block.body.withdrawals.as_ref().unwrap();
assert_eq!(withdrawals.len(), 1);
let withdrawals_root = calculate_withdrawals_root(withdrawals);
assert_eq!(block.withdrawals_root, Some(withdrawals_root));

// 4 withdrawals, identical indices
// https://github.com/ethereum/tests/blob/9760400e667eba241265016b02644ef62ab55de2/BlockchainTests/EIPTests/bc4895-withdrawals/twoIdenticalIndex.json
let data = &hex!("f9028cf90219a0151934ad9b654c50197f37018ee5ee9bb922dec0a1b5e24a6d679cb111cdb107a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa0ccf7b62d616c2ad7af862d67b9dcd2119a90cebbff8c3cd1e5d7fc99f8755774a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008001887fffffffffffffff8082079e42a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b42188000000000000000009a0a95b9a7b58a6b3cb4001eb0be67951c5517141cb0183a255b5cae027a7b10b36c0c0f86cda808094c94f5374fce5edbc8e2a8697c15331677e6ebf0b822710da028094c94f5374fce5edbc8e2a8697c15331677e6ebf0b822710da018094c94f5374fce5edbc8e2a8697c15331677e6ebf0b822710da028094c94f5374fce5edbc8e2a8697c15331677e6ebf0b822710");
let block: Block = Block::decode(&mut data.as_slice()).unwrap();
assert!(block.body.withdrawals.is_some());
let withdrawals = block.body.withdrawals.as_ref().unwrap();
assert_eq!(withdrawals.len(), 4);
let withdrawals_root = calculate_withdrawals_root(withdrawals);
assert_eq!(block.withdrawals_root, Some(withdrawals_root));
}
#[test]
fn check_receipt_root_optimism() {
use alloy_consensus::ReceiptWithBloom;

let logs = vec![Log {
address: Address::ZERO,
data: LogData::new_unchecked(vec![], Default::default()),
}];
let bloom = bloom!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001");
let receipt = ReceiptWithBloom {
receipt: Receipt {
tx_type: TxType::Eip2930,
success: true,
cumulative_gas_used: 102068,
logs,
},
logs_bloom: bloom,
};
let receipt = vec![receipt];
let root = calculate_receipt_root(&receipt);
assert_eq!(root, b256!("fe70ae4a136d98944951b2123859698d59ad251a381abc9960fa81cae3d0d4a0"));
}
}
2 changes: 1 addition & 1 deletion crates/evm/execution-types/src/execution_outcome.rs
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ impl ExecutionOutcome {
/// of receipt. This is a expensive operation.
pub fn ethereum_receipts_root(&self, _block_number: BlockNumber) -> Option<B256> {
self.receipts.root_slow(self.block_number_to_index(_block_number)?, |receipts| {
reth_primitives::proofs::calculate_receipt_root_no_memo(receipts)
reth_primitives::Receipt::calculate_receipt_root_no_memo(receipts)
})
}
}
Expand Down
10 changes: 7 additions & 3 deletions crates/primitives-traits/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ rayon = { workspace = true, optional = true }

[dev-dependencies]
reth-codecs.workspace = true
reth-chainspec = { workspace = true, features = ["arbitrary"] }

alloy-primitives = { workspace = true, features = ["arbitrary", "serde"] }
alloy-consensus = { workspace = true, features = ["arbitrary", "serde"] }
Expand Down Expand Up @@ -93,12 +94,14 @@ std = [
"thiserror/std",
"alloy-trie/std",
"op-alloy-consensus?/std",
"serde_json/std"
"serde_json/std",
"reth-chainspec/std"
]
secp256k1 = ["dep:secp256k1"]
test-utils = [
"arbitrary",
"reth-codecs?/test-utils"
"reth-codecs?/test-utils",
"reth-chainspec/test-utils"
]
arbitrary = [
"std",
Expand All @@ -113,7 +116,8 @@ arbitrary = [
"secp256k1?/global-context",
"secp256k1?/rand",
"op-alloy-consensus?/arbitrary",
"alloy-trie/arbitrary"
"alloy-trie/arbitrary",
"reth-chainspec/arbitrary"
]
serde-bincode-compat = [
"serde",
Expand Down
2 changes: 2 additions & 0 deletions crates/primitives-traits/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ pub use error::{GotExpected, GotExpectedBoxed};
mod log;
pub use alloy_primitives::{logs_bloom, Log, LogData};

pub mod proofs;

mod storage;
pub use storage::StorageEntry;

Expand Down
90 changes: 90 additions & 0 deletions crates/primitives-traits/src/proofs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
//! Helper function for calculating Merkle proofs and hashes.
pub use alloy_trie::root::ordered_trie_root_with_encoder;

pub use alloy_consensus::proofs::calculate_receipt_root;

/// Calculate a transaction root.
///
/// `(rlp(index), encoded(tx))` pairs.
#[doc(inline)]
pub use alloy_consensus::proofs::calculate_transaction_root;

/// Calculates the root hash of the withdrawals.
#[doc(inline)]
pub use alloy_consensus::proofs::calculate_withdrawals_root;

/// Calculates the root hash for ommer/uncle headers.
#[doc(inline)]
pub use alloy_consensus::proofs::calculate_ommers_root;

#[cfg(test)]
mod tests {
use alloy_consensus::EMPTY_ROOT_HASH;
use alloy_genesis::GenesisAccount;
use alloy_primitives::{b256, hex_literal::hex, Address, B256, U256};
use alloy_trie::root::{state_root_ref_unhashed, state_root_unhashed};
use reth_chainspec::{HOLESKY, MAINNET, SEPOLIA};
use std::collections::HashMap;

#[test]
fn check_empty_state_root() {
let genesis_alloc = HashMap::<Address, GenesisAccount>::new();
let root = state_root_unhashed(genesis_alloc);
assert_eq!(root, EMPTY_ROOT_HASH);
}

#[test]
fn test_simple_account_state_root() {
// each fixture specifies an address and expected root hash - the address is initialized
// with a maximum balance, and is the only account in the state.
// these test cases are generated by using geth with a custom genesis.json (with a single
// account that has max balance)
let fixtures: Vec<(Address, B256)> = vec![
(
hex!("9fe4abd71ad081f091bd06dd1c16f7e92927561e").into(),
hex!("4b35be4231841d212ce2fa43aedbddeadd6eb7d420195664f9f0d55629db8c32").into(),
),
(
hex!("c2ba9d87f8be0ade00c60d3656c1188e008fbfa2").into(),
hex!("e1389256c47d63df8856d7729dec9dc2dae074a7f0cbc49acad1cf7b29f7fe94").into(),
),
];

for (test_addr, expected_root) in fixtures {
let mut genesis_alloc = HashMap::new();
genesis_alloc
.insert(test_addr, GenesisAccount { balance: U256::MAX, ..Default::default() });

let root = state_root_unhashed(genesis_alloc);

assert_eq!(root, expected_root);
}
}

#[test]
fn test_chain_state_roots() {
let expected_mainnet_state_root =
b256!("d7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544");
let calculated_mainnet_state_root = state_root_ref_unhashed(&MAINNET.genesis.alloc);
assert_eq!(
expected_mainnet_state_root, calculated_mainnet_state_root,
"mainnet state root mismatch"
);

let expected_sepolia_state_root =
b256!("5eb6e371a698b8d68f665192350ffcecbbbf322916f4b51bd79bb6887da3f494");
let calculated_sepolia_state_root = state_root_ref_unhashed(&SEPOLIA.genesis.alloc);
assert_eq!(
expected_sepolia_state_root, calculated_sepolia_state_root,
"sepolia state root mismatch"
);

let expected_holesky_state_root =
b256!("69d8c9d72f6fa4ad42d4702b433707212f90db395eb54dc20bc85de253788783");
let calculated_holesky_state_root = state_root_ref_unhashed(&HOLESKY.genesis.alloc);
assert_eq!(
expected_holesky_state_root, calculated_holesky_state_root,
"holesky state root mismatch"
);
}
}
1 change: 0 additions & 1 deletion crates/primitives/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ reth-static-file-types.workspace = true
# ethereum
alloy-consensus.workspace = true
alloy-primitives = { workspace = true, features = ["rand", "rlp"] }
alloy-eips = { workspace = true, features = ["serde"] }
alloy-trie = { workspace = true, features = ["serde"] }

# for eip-4844
Expand Down
15 changes: 2 additions & 13 deletions crates/primitives/src/proofs.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
//! Helper function for calculating Merkle proofs and hashes.
use crate::Receipt;
use alloy_consensus::TxReceipt;
use alloy_eips::eip2718::Encodable2718;
use alloy_primitives::B256;
pub use alloy_trie::root::ordered_trie_root_with_encoder;

pub use alloy_consensus::proofs::calculate_receipt_root;
Expand All @@ -22,22 +17,16 @@ pub use alloy_consensus::proofs::calculate_withdrawals_root;
#[doc(inline)]
pub use alloy_consensus::proofs::calculate_ommers_root;

/// Calculates the receipt root for a header for the reference type of [Receipt].
///
/// NOTE: Prefer [`calculate_receipt_root`] if you have log blooms memoized.
pub fn calculate_receipt_root_no_memo(receipts: &[&Receipt]) -> B256 {
ordered_trie_root_with_encoder(receipts, |r, buf| r.with_bloom_ref().encode_2718(buf))
}

#[cfg(test)]
mod tests {
use super::*;
use crate::{Block, TxType};
use alloy_consensus::EMPTY_ROOT_HASH;
use alloy_genesis::GenesisAccount;
use alloy_primitives::{b256, bloom, hex_literal::hex, Address, Log, LogData, U256};
use alloy_primitives::{b256, bloom, hex_literal::hex, Address, Log, LogData, B256, U256};
use alloy_rlp::Decodable;
use reth_chainspec::{HOLESKY, MAINNET, SEPOLIA};
use reth_ethereum_primitives::Receipt;
use reth_trie_common::root::{state_root_ref_unhashed, state_root_unhashed};
use std::collections::HashMap;

Expand Down
9 changes: 3 additions & 6 deletions crates/rpc/rpc/src/eth/helpers/pending_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,7 @@ use alloy_eips::{eip7685::EMPTY_REQUESTS_HASH, merge::BEACON_NONCE};
use alloy_primitives::U256;
use reth_chainspec::{EthChainSpec, EthereumHardforks};
use reth_evm::ConfigureEvm;
use reth_primitives::{
logs_bloom,
proofs::{calculate_receipt_root_no_memo, calculate_transaction_root},
BlockBody, Receipt,
};
use reth_primitives::{logs_bloom, proofs::calculate_transaction_root, BlockBody, Receipt};
use reth_provider::{
BlockReader, BlockReaderIdExt, ChainSpecProvider, ProviderBlock, ProviderReceipt, ProviderTx,
StateProviderFactory,
Expand Down Expand Up @@ -64,7 +60,8 @@ where
let chain_spec = self.provider().chain_spec();

let transactions_root = calculate_transaction_root(&transactions);
let receipts_root = calculate_receipt_root_no_memo(&receipts.iter().collect::<Vec<_>>());
let receipts_root =
Receipt::calculate_receipt_root_no_memo(&receipts.iter().collect::<Vec<_>>());

let logs_bloom = logs_bloom(receipts.iter().flat_map(|r| &r.logs));

Expand Down

0 comments on commit d2ad477

Please sign in to comment.