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>(
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 if block.header().gas_used() != result.gas_used {
34 return Err(ConsensusError::BlockGasUsed {
35 gas: GotExpected { got: result.gas_used, expected: block.header().gas_used() },
36 gas_spent_by_tx: gas_spent_by_transactions(&result.receipts),
37 })
38 }
39
40 if chain_spec.is_byzantium_active_at_block(block.header().number()) {
45 let res = 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(
54 block.header().receipts_root(),
55 block.header().logs_bloom(),
56 &result.receipts,
57 )
58 };
59
60 if let Err(error) = res {
61 let receipts = result
62 .receipts
63 .iter()
64 .map(|r| Bytes::from(r.with_bloom_ref().encoded_2718()))
65 .collect::<Vec<_>>();
66 tracing::debug!(%error, ?receipts, "receipts verification failed");
67 return Err(error)
68 }
69 }
70
71 if chain_spec.is_prague_active_at_timestamp(block.header().timestamp()) {
73 let Some(header_requests_hash) = block.header().requests_hash() else {
74 return Err(ConsensusError::RequestsHashMissing)
75 };
76 let requests_hash = result.requests.requests_hash();
77 if requests_hash != header_requests_hash {
78 return Err(ConsensusError::BodyRequestsHashDiff(
79 GotExpected::new(requests_hash, header_requests_hash).into(),
80 ))
81 }
82 }
83
84 if chain_spec.is_amsterdam_active_at_timestamp(block.header().timestamp()) &&
86 let Some(block_access_list_hash) = block_access_list_hash
87 {
88 let block_bal_hash = block.header().block_access_list_hash().unwrap_or_default();
89 if block_access_list_hash != block_bal_hash {
90 return Err(ConsensusError::BlockAccessListHashMismatch(
91 GotExpected::new(block_access_list_hash, block_bal_hash).into(),
92 ))
93 }
94 }
95
96 Ok(())
97}
98
99fn verify_receipts<R: Receipt>(
102 expected_receipts_root: B256,
103 expected_logs_bloom: Bloom,
104 receipts: &[R],
105) -> Result<(), ConsensusError> {
106 let receipts_with_bloom = receipts.iter().map(TxReceipt::with_bloom_ref).collect::<Vec<_>>();
108 let receipts_root = calculate_receipt_root(&receipts_with_bloom);
109
110 let logs_bloom = receipts_with_bloom.iter().fold(Bloom::ZERO, |bloom, r| bloom | r.bloom_ref());
112
113 compare_receipts_root_and_logs_bloom(
114 receipts_root,
115 logs_bloom,
116 expected_receipts_root,
117 expected_logs_bloom,
118 )
119}
120
121fn compare_receipts_root_and_logs_bloom(
124 calculated_receipts_root: B256,
125 calculated_logs_bloom: Bloom,
126 expected_receipts_root: B256,
127 expected_logs_bloom: Bloom,
128) -> Result<(), ConsensusError> {
129 if calculated_receipts_root != expected_receipts_root {
130 return Err(ConsensusError::BodyReceiptRootDiff(
131 GotExpected { got: calculated_receipts_root, expected: expected_receipts_root }.into(),
132 ))
133 }
134
135 if calculated_logs_bloom != expected_logs_bloom {
136 return Err(ConsensusError::BodyBloomLogDiff(
137 GotExpected { got: calculated_logs_bloom, expected: expected_logs_bloom }.into(),
138 ))
139 }
140
141 Ok(())
142}
143
144#[cfg(test)]
145mod tests {
146 use super::*;
147 use alloy_primitives::{b256, hex};
148 use reth_ethereum_primitives::Receipt;
149
150 #[test]
151 fn test_verify_receipts_success() {
152 let receipts: Vec<Receipt> = vec![Receipt::default(); 5];
154
155 assert!(verify_receipts(
157 b256!("0x61353b4fb714dc1fccacbf7eafc4273e62f3d1eed716fe41b2a0cd2e12c63ebc"),
158 Bloom::from(hex!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")),
159 &receipts
160 )
161 .is_ok());
162 }
163
164 #[test]
165 fn test_verify_receipts_incorrect_root() {
166 let expected_receipts_root = B256::random();
168 let expected_logs_bloom = Bloom::random();
169
170 let receipts: Vec<Receipt> = vec![Receipt::default(); 5];
172
173 assert!(verify_receipts(expected_receipts_root, expected_logs_bloom, &receipts).is_err());
174 }
175
176 #[test]
177 fn test_compare_receipts_root_and_logs_bloom_success() {
178 let calculated_receipts_root = B256::random();
179 let calculated_logs_bloom = Bloom::random();
180
181 let expected_receipts_root = calculated_receipts_root;
182 let expected_logs_bloom = calculated_logs_bloom;
183
184 assert!(compare_receipts_root_and_logs_bloom(
185 calculated_receipts_root,
186 calculated_logs_bloom,
187 expected_receipts_root,
188 expected_logs_bloom
189 )
190 .is_ok());
191 }
192
193 #[test]
194 fn test_compare_receipts_root_failure() {
195 let calculated_receipts_root = B256::random();
196 let calculated_logs_bloom = Bloom::random();
197
198 let expected_receipts_root = B256::random();
199 let expected_logs_bloom = calculated_logs_bloom;
200
201 assert!(matches!(
202 compare_receipts_root_and_logs_bloom(
203 calculated_receipts_root,
204 calculated_logs_bloom,
205 expected_receipts_root,
206 expected_logs_bloom
207 ).unwrap_err(),
208 ConsensusError::BodyReceiptRootDiff(diff)
209 if diff.got == calculated_receipts_root && diff.expected == expected_receipts_root
210 ));
211 }
212
213 #[test]
214 fn test_compare_log_bloom_failure() {
215 let calculated_receipts_root = B256::random();
216 let calculated_logs_bloom = Bloom::random();
217
218 let expected_receipts_root = calculated_receipts_root;
219 let expected_logs_bloom = Bloom::random();
220
221 assert!(matches!(
222 compare_receipts_root_and_logs_bloom(
223 calculated_receipts_root,
224 calculated_logs_bloom,
225 expected_receipts_root,
226 expected_logs_bloom
227 ).unwrap_err(),
228 ConsensusError::BodyBloomLogDiff(diff)
229 if diff.got == calculated_logs_bloom && diff.expected == expected_logs_bloom
230 ));
231 }
232}