reth_ethereum_consensus/
validation.rsuse alloy_consensus::{proofs::calculate_receipt_root, BlockHeader, TxReceipt};
use alloy_eips::eip7685::Requests;
use alloy_primitives::{Bloom, B256};
use reth_chainspec::EthereumHardforks;
use reth_consensus::ConsensusError;
use reth_primitives::{gas_spent_by_transactions, BlockWithSenders, GotExpected, Receipt};
use reth_primitives_traits::Block;
pub fn validate_block_post_execution<B, ChainSpec>(
block: &BlockWithSenders<B>,
chain_spec: &ChainSpec,
receipts: &[Receipt],
requests: &Requests,
) -> Result<(), ConsensusError>
where
B: Block,
ChainSpec: EthereumHardforks,
{
let cumulative_gas_used =
receipts.last().map(|receipt| receipt.cumulative_gas_used).unwrap_or(0);
if block.header().gas_used() != cumulative_gas_used {
return Err(ConsensusError::BlockGasUsed {
gas: GotExpected { got: cumulative_gas_used, expected: block.header().gas_used() },
gas_spent_by_tx: gas_spent_by_transactions(receipts),
})
}
if chain_spec.is_byzantium_active_at_block(block.header().number()) {
if let Err(error) =
verify_receipts(block.header().receipts_root(), block.header().logs_bloom(), receipts)
{
tracing::debug!(%error, ?receipts, "receipts verification failed");
return Err(error)
}
}
if chain_spec.is_prague_active_at_timestamp(block.header().timestamp()) {
let Some(header_requests_hash) = block.header().requests_hash() else {
return Err(ConsensusError::RequestsHashMissing)
};
let requests_hash = requests.requests_hash();
if requests_hash != header_requests_hash {
return Err(ConsensusError::BodyRequestsHashDiff(
GotExpected::new(requests_hash, header_requests_hash).into(),
))
}
}
Ok(())
}
fn verify_receipts(
expected_receipts_root: B256,
expected_logs_bloom: Bloom,
receipts: &[Receipt],
) -> Result<(), ConsensusError> {
let receipts_with_bloom = receipts.iter().map(Receipt::with_bloom_ref).collect::<Vec<_>>();
let receipts_root = calculate_receipt_root(&receipts_with_bloom);
let logs_bloom = receipts_with_bloom.iter().fold(Bloom::ZERO, |bloom, r| bloom | r.bloom());
compare_receipts_root_and_logs_bloom(
receipts_root,
logs_bloom,
expected_receipts_root,
expected_logs_bloom,
)?;
Ok(())
}
fn compare_receipts_root_and_logs_bloom(
calculated_receipts_root: B256,
calculated_logs_bloom: Bloom,
expected_receipts_root: B256,
expected_logs_bloom: Bloom,
) -> Result<(), ConsensusError> {
if calculated_receipts_root != expected_receipts_root {
return Err(ConsensusError::BodyReceiptRootDiff(
GotExpected { got: calculated_receipts_root, expected: expected_receipts_root }.into(),
))
}
if calculated_logs_bloom != expected_logs_bloom {
return Err(ConsensusError::BodyBloomLogDiff(
GotExpected { got: calculated_logs_bloom, expected: expected_logs_bloom }.into(),
))
}
Ok(())
}
#[cfg(test)]
mod tests {
use alloy_primitives::hex;
use super::*;
#[test]
fn test_verify_receipts_success() {
let receipts = vec![Receipt::default(); 5];
assert!(verify_receipts(
B256::from(hex!("61353b4fb714dc1fccacbf7eafc4273e62f3d1eed716fe41b2a0cd2e12c63ebc")),
Bloom::from(hex!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")),
&receipts
)
.is_ok());
}
#[test]
fn test_verify_receipts_incorrect_root() {
let expected_receipts_root = B256::random();
let expected_logs_bloom = Bloom::random();
let receipts = vec![Receipt::default(); 5];
assert!(verify_receipts(expected_receipts_root, expected_logs_bloom, &receipts).is_err());
}
#[test]
fn test_compare_receipts_root_and_logs_bloom_success() {
let calculated_receipts_root = B256::random();
let calculated_logs_bloom = Bloom::random();
let expected_receipts_root = calculated_receipts_root;
let expected_logs_bloom = calculated_logs_bloom;
assert!(compare_receipts_root_and_logs_bloom(
calculated_receipts_root,
calculated_logs_bloom,
expected_receipts_root,
expected_logs_bloom
)
.is_ok());
}
#[test]
fn test_compare_receipts_root_failure() {
let calculated_receipts_root = B256::random();
let calculated_logs_bloom = Bloom::random();
let expected_receipts_root = B256::random();
let expected_logs_bloom = calculated_logs_bloom;
assert_eq!(
compare_receipts_root_and_logs_bloom(
calculated_receipts_root,
calculated_logs_bloom,
expected_receipts_root,
expected_logs_bloom
),
Err(ConsensusError::BodyReceiptRootDiff(
GotExpected { got: calculated_receipts_root, expected: expected_receipts_root }
.into()
))
);
}
#[test]
fn test_compare_log_bloom_failure() {
let calculated_receipts_root = B256::random();
let calculated_logs_bloom = Bloom::random();
let expected_receipts_root = calculated_receipts_root;
let expected_logs_bloom = Bloom::random();
assert_eq!(
compare_receipts_root_and_logs_bloom(
calculated_receipts_root,
calculated_logs_bloom,
expected_receipts_root,
expected_logs_bloom
),
Err(ConsensusError::BodyBloomLogDiff(
GotExpected { got: calculated_logs_bloom, expected: expected_logs_bloom }.into()
))
);
}
}