reth_ethereum_consensus/
validation.rs1use alloy_consensus::{proofs::calculate_receipt_root, BlockHeader, TxReceipt};
2use alloy_eips::eip7685::Requests;
3use alloy_primitives::{Bloom, B256};
4use reth_chainspec::EthereumHardforks;
5use reth_consensus::ConsensusError;
6use reth_primitives::{gas_spent_by_transactions, GotExpected, RecoveredBlock};
7use reth_primitives_traits::{Block, Receipt};
8
9pub fn validate_block_post_execution<B, R, ChainSpec>(
14 block: &RecoveredBlock<B>,
15 chain_spec: &ChainSpec,
16 receipts: &[R],
17 requests: &Requests,
18) -> Result<(), ConsensusError>
19where
20 B: Block,
21 R: Receipt,
22 ChainSpec: EthereumHardforks,
23{
24 let cumulative_gas_used =
26 receipts.last().map(|receipt| receipt.cumulative_gas_used()).unwrap_or(0);
27 if block.header().gas_used() != cumulative_gas_used {
28 return Err(ConsensusError::BlockGasUsed {
29 gas: GotExpected { got: cumulative_gas_used, expected: block.header().gas_used() },
30 gas_spent_by_tx: gas_spent_by_transactions(receipts),
31 })
32 }
33
34 if chain_spec.is_byzantium_active_at_block(block.header().number()) {
39 if let Err(error) =
40 verify_receipts(block.header().receipts_root(), block.header().logs_bloom(), receipts)
41 {
42 tracing::debug!(%error, ?receipts, "receipts verification failed");
43 return Err(error)
44 }
45 }
46
47 if chain_spec.is_prague_active_at_timestamp(block.header().timestamp()) {
49 let Some(header_requests_hash) = block.header().requests_hash() else {
50 return Err(ConsensusError::RequestsHashMissing)
51 };
52 let requests_hash = requests.requests_hash();
53 if requests_hash != header_requests_hash {
54 return Err(ConsensusError::BodyRequestsHashDiff(
55 GotExpected::new(requests_hash, header_requests_hash).into(),
56 ))
57 }
58 }
59
60 Ok(())
61}
62
63fn verify_receipts<R: Receipt>(
66 expected_receipts_root: B256,
67 expected_logs_bloom: Bloom,
68 receipts: &[R],
69) -> Result<(), ConsensusError> {
70 let receipts_with_bloom = receipts.iter().map(TxReceipt::with_bloom_ref).collect::<Vec<_>>();
72 let receipts_root = calculate_receipt_root(&receipts_with_bloom);
73
74 let logs_bloom = receipts_with_bloom.iter().fold(Bloom::ZERO, |bloom, r| bloom | r.bloom());
76
77 compare_receipts_root_and_logs_bloom(
78 receipts_root,
79 logs_bloom,
80 expected_receipts_root,
81 expected_logs_bloom,
82 )?;
83
84 Ok(())
85}
86
87fn compare_receipts_root_and_logs_bloom(
90 calculated_receipts_root: B256,
91 calculated_logs_bloom: Bloom,
92 expected_receipts_root: B256,
93 expected_logs_bloom: Bloom,
94) -> Result<(), ConsensusError> {
95 if calculated_receipts_root != expected_receipts_root {
96 return Err(ConsensusError::BodyReceiptRootDiff(
97 GotExpected { got: calculated_receipts_root, expected: expected_receipts_root }.into(),
98 ))
99 }
100
101 if calculated_logs_bloom != expected_logs_bloom {
102 return Err(ConsensusError::BodyBloomLogDiff(
103 GotExpected { got: calculated_logs_bloom, expected: expected_logs_bloom }.into(),
104 ))
105 }
106
107 Ok(())
108}
109
110#[cfg(test)]
111mod tests {
112 use alloy_primitives::hex;
113 use reth_primitives::Receipt;
114
115 use super::*;
116
117 #[test]
118 fn test_verify_receipts_success() {
119 let receipts = vec![Receipt::default(); 5];
121
122 assert!(verify_receipts(
124 B256::from(hex!("61353b4fb714dc1fccacbf7eafc4273e62f3d1eed716fe41b2a0cd2e12c63ebc")),
125 Bloom::from(hex!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")),
126 &receipts
127 )
128 .is_ok());
129 }
130
131 #[test]
132 fn test_verify_receipts_incorrect_root() {
133 let expected_receipts_root = B256::random();
135 let expected_logs_bloom = Bloom::random();
136
137 let receipts = vec![Receipt::default(); 5];
139
140 assert!(verify_receipts(expected_receipts_root, expected_logs_bloom, &receipts).is_err());
141 }
142
143 #[test]
144 fn test_compare_receipts_root_and_logs_bloom_success() {
145 let calculated_receipts_root = B256::random();
146 let calculated_logs_bloom = Bloom::random();
147
148 let expected_receipts_root = calculated_receipts_root;
149 let expected_logs_bloom = calculated_logs_bloom;
150
151 assert!(compare_receipts_root_and_logs_bloom(
152 calculated_receipts_root,
153 calculated_logs_bloom,
154 expected_receipts_root,
155 expected_logs_bloom
156 )
157 .is_ok());
158 }
159
160 #[test]
161 fn test_compare_receipts_root_failure() {
162 let calculated_receipts_root = B256::random();
163 let calculated_logs_bloom = Bloom::random();
164
165 let expected_receipts_root = B256::random();
166 let expected_logs_bloom = calculated_logs_bloom;
167
168 assert_eq!(
169 compare_receipts_root_and_logs_bloom(
170 calculated_receipts_root,
171 calculated_logs_bloom,
172 expected_receipts_root,
173 expected_logs_bloom
174 ),
175 Err(ConsensusError::BodyReceiptRootDiff(
176 GotExpected { got: calculated_receipts_root, expected: expected_receipts_root }
177 .into()
178 ))
179 );
180 }
181
182 #[test]
183 fn test_compare_log_bloom_failure() {
184 let calculated_receipts_root = B256::random();
185 let calculated_logs_bloom = Bloom::random();
186
187 let expected_receipts_root = calculated_receipts_root;
188 let expected_logs_bloom = Bloom::random();
189
190 assert_eq!(
191 compare_receipts_root_and_logs_bloom(
192 calculated_receipts_root,
193 calculated_logs_bloom,
194 expected_receipts_root,
195 expected_logs_bloom
196 ),
197 Err(ConsensusError::BodyBloomLogDiff(
198 GotExpected { got: calculated_logs_bloom, expected: expected_logs_bloom }.into()
199 ))
200 );
201 }
202}