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>(
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 let receipts = receipts
45 .iter()
46 .map(|r| Bytes::from(r.with_bloom_ref().encoded_2718()))
47 .collect::<Vec<_>>();
48 tracing::debug!(%error, ?receipts, "receipts verification failed");
49 return Err(error)
50 }
51 }
52
53 if chain_spec.is_prague_active_at_timestamp(block.header().timestamp()) {
55 let Some(header_requests_hash) = block.header().requests_hash() else {
56 return Err(ConsensusError::RequestsHashMissing)
57 };
58 let requests_hash = requests.requests_hash();
59 if requests_hash != header_requests_hash {
60 return Err(ConsensusError::BodyRequestsHashDiff(
61 GotExpected::new(requests_hash, header_requests_hash).into(),
62 ))
63 }
64 }
65
66 Ok(())
67}
68
69fn verify_receipts<R: Receipt>(
72 expected_receipts_root: B256,
73 expected_logs_bloom: Bloom,
74 receipts: &[R],
75) -> Result<(), ConsensusError> {
76 let receipts_with_bloom = receipts.iter().map(TxReceipt::with_bloom_ref).collect::<Vec<_>>();
78 let receipts_root = calculate_receipt_root(&receipts_with_bloom);
79
80 let logs_bloom = receipts_with_bloom.iter().fold(Bloom::ZERO, |bloom, r| bloom | r.bloom_ref());
82
83 compare_receipts_root_and_logs_bloom(
84 receipts_root,
85 logs_bloom,
86 expected_receipts_root,
87 expected_logs_bloom,
88 )?;
89
90 Ok(())
91}
92
93fn compare_receipts_root_and_logs_bloom(
96 calculated_receipts_root: B256,
97 calculated_logs_bloom: Bloom,
98 expected_receipts_root: B256,
99 expected_logs_bloom: Bloom,
100) -> Result<(), ConsensusError> {
101 if calculated_receipts_root != expected_receipts_root {
102 return Err(ConsensusError::BodyReceiptRootDiff(
103 GotExpected { got: calculated_receipts_root, expected: expected_receipts_root }.into(),
104 ))
105 }
106
107 if calculated_logs_bloom != expected_logs_bloom {
108 return Err(ConsensusError::BodyBloomLogDiff(
109 GotExpected { got: calculated_logs_bloom, expected: expected_logs_bloom }.into(),
110 ))
111 }
112
113 Ok(())
114}
115
116#[cfg(test)]
117mod tests {
118 use super::*;
119 use alloy_primitives::{b256, hex};
120 use reth_ethereum_primitives::Receipt;
121
122 #[test]
123 fn test_verify_receipts_success() {
124 let receipts: Vec<Receipt> = vec![Receipt::default(); 5];
126
127 assert!(verify_receipts(
129 b256!("0x61353b4fb714dc1fccacbf7eafc4273e62f3d1eed716fe41b2a0cd2e12c63ebc"),
130 Bloom::from(hex!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")),
131 &receipts
132 )
133 .is_ok());
134 }
135
136 #[test]
137 fn test_verify_receipts_incorrect_root() {
138 let expected_receipts_root = B256::random();
140 let expected_logs_bloom = Bloom::random();
141
142 let receipts: Vec<Receipt> = vec![Receipt::default(); 5];
144
145 assert!(verify_receipts(expected_receipts_root, expected_logs_bloom, &receipts).is_err());
146 }
147
148 #[test]
149 fn test_compare_receipts_root_and_logs_bloom_success() {
150 let calculated_receipts_root = B256::random();
151 let calculated_logs_bloom = Bloom::random();
152
153 let expected_receipts_root = calculated_receipts_root;
154 let expected_logs_bloom = calculated_logs_bloom;
155
156 assert!(compare_receipts_root_and_logs_bloom(
157 calculated_receipts_root,
158 calculated_logs_bloom,
159 expected_receipts_root,
160 expected_logs_bloom
161 )
162 .is_ok());
163 }
164
165 #[test]
166 fn test_compare_receipts_root_failure() {
167 let calculated_receipts_root = B256::random();
168 let calculated_logs_bloom = Bloom::random();
169
170 let expected_receipts_root = B256::random();
171 let expected_logs_bloom = calculated_logs_bloom;
172
173 assert_eq!(
174 compare_receipts_root_and_logs_bloom(
175 calculated_receipts_root,
176 calculated_logs_bloom,
177 expected_receipts_root,
178 expected_logs_bloom
179 ),
180 Err(ConsensusError::BodyReceiptRootDiff(
181 GotExpected { got: calculated_receipts_root, expected: expected_receipts_root }
182 .into()
183 ))
184 );
185 }
186
187 #[test]
188 fn test_compare_log_bloom_failure() {
189 let calculated_receipts_root = B256::random();
190 let calculated_logs_bloom = Bloom::random();
191
192 let expected_receipts_root = calculated_receipts_root;
193 let expected_logs_bloom = Bloom::random();
194
195 assert_eq!(
196 compare_receipts_root_and_logs_bloom(
197 calculated_receipts_root,
198 calculated_logs_bloom,
199 expected_receipts_root,
200 expected_logs_bloom
201 ),
202 Err(ConsensusError::BodyBloomLogDiff(
203 GotExpected { got: calculated_logs_bloom, expected: expected_logs_bloom }.into()
204 ))
205 );
206 }
207}