1use alloc::vec::Vec;
2use alloy_consensus::{proofs::calculate_receipt_root, BlockHeader, TxReceipt};
3use alloy_eips::Encodable2718;
4use alloy_primitives::{Bloom, Bytes, B256};
5use reth_chainspec::EthereumHardforks;
6use reth_consensus::ConsensusError;
7use reth_execution_types::BlockExecutionResult;
8use reth_primitives_traits::{
9 receipt::gas_spent_by_transactions, Block, GotExpected, Receipt, RecoveredBlock,
10};
11
12pub fn validate_block_post_execution<B, R, ChainSpec>(
21 block: &RecoveredBlock<B>,
22 chain_spec: &ChainSpec,
23 result: &BlockExecutionResult<R>,
24 receipt_root_bloom: Option<(B256, Bloom)>,
25 block_access_list_hash: Option<B256>,
26) -> Result<(), ConsensusError>
27where
28 B: Block,
29 R: Receipt,
30 ChainSpec: EthereumHardforks,
31{
32 validate_block_post_execution_with_bal_hashes(
33 block,
34 chain_spec,
35 result,
36 receipt_root_bloom,
37 block_access_list_hash,
38 false,
39 )
40}
41
42pub(crate) fn validate_block_post_execution_with_bal_hashes<B, R, ChainSpec>(
44 block: &RecoveredBlock<B>,
45 chain_spec: &ChainSpec,
46 result: &BlockExecutionResult<R>,
47 receipt_root_bloom: Option<(B256, Bloom)>,
48 block_access_list_hash: Option<B256>,
49 allow_bal_hashes: bool,
50) -> Result<(), ConsensusError>
51where
52 B: Block,
53 R: Receipt,
54 ChainSpec: EthereumHardforks,
55{
56 if block.header().gas_used() != result.gas_used {
58 return Err(ConsensusError::BlockGasUsed {
59 gas: GotExpected { got: result.gas_used, expected: block.header().gas_used() },
60 gas_spent_by_tx: gas_spent_by_transactions(&result.receipts),
61 })
62 }
63
64 if chain_spec.is_byzantium_active_at_block(block.header().number()) {
69 let res = if let Some((receipts_root, logs_bloom)) = receipt_root_bloom {
70 compare_receipts_root_and_logs_bloom(
71 receipts_root,
72 logs_bloom,
73 block.header().receipts_root(),
74 block.header().logs_bloom(),
75 )
76 } else {
77 verify_receipts(
78 block.header().receipts_root(),
79 block.header().logs_bloom(),
80 &result.receipts,
81 )
82 };
83
84 if let Err(error) = res {
85 let receipts = result
86 .receipts
87 .iter()
88 .map(|r| Bytes::from(r.with_bloom_ref().encoded_2718()))
89 .collect::<Vec<_>>();
90 tracing::debug!(%error, ?receipts, "receipts verification failed");
91 return Err(error)
92 }
93 }
94
95 if chain_spec.is_prague_active_at_timestamp(block.header().timestamp()) {
97 let Some(header_requests_hash) = block.header().requests_hash() else {
98 return Err(ConsensusError::RequestsHashMissing)
99 };
100 let requests_hash = result.requests.requests_hash();
101 if requests_hash != header_requests_hash {
102 return Err(ConsensusError::BodyRequestsHashDiff(
103 GotExpected::new(requests_hash, header_requests_hash).into(),
104 ))
105 }
106 }
107
108 let is_allowed_pre_amsterdam_bal_hash = allow_bal_hashes &&
110 !chain_spec.is_amsterdam_active_at_timestamp(block.header().timestamp()) &&
111 block.header().block_access_list_hash().is_some();
112
113 if (chain_spec.is_amsterdam_active_at_timestamp(block.header().timestamp()) ||
114 is_allowed_pre_amsterdam_bal_hash) &&
115 let Some(block_access_list_hash) = block_access_list_hash
116 {
117 let block_bal_hash = block.header().block_access_list_hash().unwrap_or_default();
118 if block_access_list_hash != block_bal_hash {
119 return Err(ConsensusError::BlockAccessListHashMismatch(
120 GotExpected::new(block_access_list_hash, block_bal_hash).into(),
121 ))
122 }
123 }
124
125 Ok(())
126}
127
128fn verify_receipts<R: Receipt>(
131 expected_receipts_root: B256,
132 expected_logs_bloom: Bloom,
133 receipts: &[R],
134) -> Result<(), ConsensusError> {
135 let receipts_with_bloom = receipts.iter().map(TxReceipt::with_bloom_ref).collect::<Vec<_>>();
137 let receipts_root = calculate_receipt_root(&receipts_with_bloom);
138
139 let logs_bloom = receipts_with_bloom.iter().fold(Bloom::ZERO, |bloom, r| bloom | r.bloom_ref());
141
142 compare_receipts_root_and_logs_bloom(
143 receipts_root,
144 logs_bloom,
145 expected_receipts_root,
146 expected_logs_bloom,
147 )
148}
149
150fn compare_receipts_root_and_logs_bloom(
153 calculated_receipts_root: B256,
154 calculated_logs_bloom: Bloom,
155 expected_receipts_root: B256,
156 expected_logs_bloom: Bloom,
157) -> Result<(), ConsensusError> {
158 if calculated_receipts_root != expected_receipts_root {
159 return Err(ConsensusError::BodyReceiptRootDiff(
160 GotExpected { got: calculated_receipts_root, expected: expected_receipts_root }.into(),
161 ))
162 }
163
164 if calculated_logs_bloom != expected_logs_bloom {
165 return Err(ConsensusError::BodyBloomLogDiff(
166 GotExpected { got: calculated_logs_bloom, expected: expected_logs_bloom }.into(),
167 ))
168 }
169
170 Ok(())
171}
172
173#[cfg(test)]
174mod tests {
175 use super::*;
176 use alloy_primitives::{b256, hex};
177 use reth_ethereum_primitives::Receipt;
178
179 #[test]
180 fn test_verify_receipts_success() {
181 let receipts: Vec<Receipt> = vec![Receipt::default(); 5];
183
184 assert!(verify_receipts(
186 b256!("0x61353b4fb714dc1fccacbf7eafc4273e62f3d1eed716fe41b2a0cd2e12c63ebc"),
187 Bloom::from(hex!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")),
188 &receipts
189 )
190 .is_ok());
191 }
192
193 #[test]
194 fn test_verify_receipts_incorrect_root() {
195 let expected_receipts_root = B256::random();
197 let expected_logs_bloom = Bloom::random();
198
199 let receipts: Vec<Receipt> = vec![Receipt::default(); 5];
201
202 assert!(verify_receipts(expected_receipts_root, expected_logs_bloom, &receipts).is_err());
203 }
204
205 #[test]
206 fn test_compare_receipts_root_and_logs_bloom_success() {
207 let calculated_receipts_root = B256::random();
208 let calculated_logs_bloom = Bloom::random();
209
210 let expected_receipts_root = calculated_receipts_root;
211 let expected_logs_bloom = calculated_logs_bloom;
212
213 assert!(compare_receipts_root_and_logs_bloom(
214 calculated_receipts_root,
215 calculated_logs_bloom,
216 expected_receipts_root,
217 expected_logs_bloom
218 )
219 .is_ok());
220 }
221
222 #[test]
223 fn test_compare_receipts_root_failure() {
224 let calculated_receipts_root = B256::random();
225 let calculated_logs_bloom = Bloom::random();
226
227 let expected_receipts_root = B256::random();
228 let expected_logs_bloom = calculated_logs_bloom;
229
230 assert!(matches!(
231 compare_receipts_root_and_logs_bloom(
232 calculated_receipts_root,
233 calculated_logs_bloom,
234 expected_receipts_root,
235 expected_logs_bloom
236 ).unwrap_err(),
237 ConsensusError::BodyReceiptRootDiff(diff)
238 if diff.got == calculated_receipts_root && diff.expected == expected_receipts_root
239 ));
240 }
241
242 #[test]
243 fn test_compare_log_bloom_failure() {
244 let calculated_receipts_root = B256::random();
245 let calculated_logs_bloom = Bloom::random();
246
247 let expected_receipts_root = calculated_receipts_root;
248 let expected_logs_bloom = Bloom::random();
249
250 assert!(matches!(
251 compare_receipts_root_and_logs_bloom(
252 calculated_receipts_root,
253 calculated_logs_bloom,
254 expected_receipts_root,
255 expected_logs_bloom
256 ).unwrap_err(),
257 ConsensusError::BodyBloomLogDiff(diff)
258 if diff.got == calculated_logs_bloom && diff.expected == expected_logs_bloom
259 ));
260 }
261}