reth_ethereum_consensus/
validation.rs1use alloc::vec::Vec;
2use alloy_consensus::{proofs::calculate_receipt_root, BlockHeader, TxReceipt};
3use alloy_eips::eip7685::Requests;
4use alloy_primitives::{Bloom, B256};
5use reth_chainspec::EthereumHardforks;
6use reth_consensus::ConsensusError;
7use reth_primitives_traits::{
8 receipt::gas_spent_by_transactions, Block, GotExpected, Receipt, RecoveredBlock,
9};
10
11pub fn validate_block_post_execution<B, R, ChainSpec>(
16 block: &RecoveredBlock<B>,
17 chain_spec: &ChainSpec,
18 receipts: &[R],
19 requests: &Requests,
20) -> Result<(), ConsensusError>
21where
22 B: Block,
23 R: Receipt,
24 ChainSpec: EthereumHardforks,
25{
26 let cumulative_gas_used =
28 receipts.last().map(|receipt| receipt.cumulative_gas_used()).unwrap_or(0);
29 if block.header().gas_used() != cumulative_gas_used {
30 return Err(ConsensusError::BlockGasUsed {
31 gas: GotExpected { got: cumulative_gas_used, expected: block.header().gas_used() },
32 gas_spent_by_tx: gas_spent_by_transactions(receipts),
33 })
34 }
35
36 if chain_spec.is_byzantium_active_at_block(block.header().number()) {
41 if let Err(error) =
42 verify_receipts(block.header().receipts_root(), block.header().logs_bloom(), receipts)
43 {
44 tracing::debug!(%error, ?receipts, "receipts verification failed");
45 return Err(error)
46 }
47 }
48
49 if chain_spec.is_prague_active_at_timestamp(block.header().timestamp()) {
51 let Some(header_requests_hash) = block.header().requests_hash() else {
52 return Err(ConsensusError::RequestsHashMissing)
53 };
54 let requests_hash = requests.requests_hash();
55 if requests_hash != header_requests_hash {
56 return Err(ConsensusError::BodyRequestsHashDiff(
57 GotExpected::new(requests_hash, header_requests_hash).into(),
58 ))
59 }
60 }
61
62 Ok(())
63}
64
65fn verify_receipts<R: Receipt>(
68 expected_receipts_root: B256,
69 expected_logs_bloom: Bloom,
70 receipts: &[R],
71) -> Result<(), ConsensusError> {
72 let receipts_with_bloom = receipts.iter().map(TxReceipt::with_bloom_ref).collect::<Vec<_>>();
74 let receipts_root = calculate_receipt_root(&receipts_with_bloom);
75
76 let logs_bloom = receipts_with_bloom.iter().fold(Bloom::ZERO, |bloom, r| bloom | r.bloom_ref());
78
79 compare_receipts_root_and_logs_bloom(
80 receipts_root,
81 logs_bloom,
82 expected_receipts_root,
83 expected_logs_bloom,
84 )?;
85
86 Ok(())
87}
88
89fn compare_receipts_root_and_logs_bloom(
92 calculated_receipts_root: B256,
93 calculated_logs_bloom: Bloom,
94 expected_receipts_root: B256,
95 expected_logs_bloom: Bloom,
96) -> Result<(), ConsensusError> {
97 if calculated_receipts_root != expected_receipts_root {
98 return Err(ConsensusError::BodyReceiptRootDiff(
99 GotExpected { got: calculated_receipts_root, expected: expected_receipts_root }.into(),
100 ))
101 }
102
103 if calculated_logs_bloom != expected_logs_bloom {
104 return Err(ConsensusError::BodyBloomLogDiff(
105 GotExpected { got: calculated_logs_bloom, expected: expected_logs_bloom }.into(),
106 ))
107 }
108
109 Ok(())
110}
111
112#[cfg(test)]
113mod tests {
114 use super::*;
115 use alloy_primitives::{b256, hex};
116 use reth_ethereum_primitives::Receipt;
117
118 #[test]
119 fn test_verify_receipts_success() {
120 let receipts = vec![Receipt::default(); 5];
122
123 assert!(verify_receipts(
125 b256!("0x61353b4fb714dc1fccacbf7eafc4273e62f3d1eed716fe41b2a0cd2e12c63ebc"),
126 Bloom::from(hex!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")),
127 &receipts
128 )
129 .is_ok());
130 }
131
132 #[test]
133 fn test_verify_receipts_incorrect_root() {
134 let expected_receipts_root = B256::random();
136 let expected_logs_bloom = Bloom::random();
137
138 let receipts = vec![Receipt::default(); 5];
140
141 assert!(verify_receipts(expected_receipts_root, expected_logs_bloom, &receipts).is_err());
142 }
143
144 #[test]
145 fn test_compare_receipts_root_and_logs_bloom_success() {
146 let calculated_receipts_root = B256::random();
147 let calculated_logs_bloom = Bloom::random();
148
149 let expected_receipts_root = calculated_receipts_root;
150 let expected_logs_bloom = calculated_logs_bloom;
151
152 assert!(compare_receipts_root_and_logs_bloom(
153 calculated_receipts_root,
154 calculated_logs_bloom,
155 expected_receipts_root,
156 expected_logs_bloom
157 )
158 .is_ok());
159 }
160
161 #[test]
162 fn test_compare_receipts_root_failure() {
163 let calculated_receipts_root = B256::random();
164 let calculated_logs_bloom = Bloom::random();
165
166 let expected_receipts_root = B256::random();
167 let expected_logs_bloom = calculated_logs_bloom;
168
169 assert_eq!(
170 compare_receipts_root_and_logs_bloom(
171 calculated_receipts_root,
172 calculated_logs_bloom,
173 expected_receipts_root,
174 expected_logs_bloom
175 ),
176 Err(ConsensusError::BodyReceiptRootDiff(
177 GotExpected { got: calculated_receipts_root, expected: expected_receipts_root }
178 .into()
179 ))
180 );
181 }
182
183 #[test]
184 fn test_compare_log_bloom_failure() {
185 let calculated_receipts_root = B256::random();
186 let calculated_logs_bloom = Bloom::random();
187
188 let expected_receipts_root = calculated_receipts_root;
189 let expected_logs_bloom = Bloom::random();
190
191 assert_eq!(
192 compare_receipts_root_and_logs_bloom(
193 calculated_receipts_root,
194 calculated_logs_bloom,
195 expected_receipts_root,
196 expected_logs_bloom
197 ),
198 Err(ConsensusError::BodyBloomLogDiff(
199 GotExpected { got: calculated_logs_bloom, expected: expected_logs_bloom }.into()
200 ))
201 );
202 }
203}