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