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