reth_ethereum_consensus/
validation.rs
1use 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_traits::{
7 receipt::gas_spent_by_transactions, Block, GotExpected, Receipt, RecoveredBlock,
8};
9
10pub fn validate_block_post_execution<B, R, ChainSpec>(
15 block: &RecoveredBlock<B>,
16 chain_spec: &ChainSpec,
17 receipts: &[R],
18 requests: &Requests,
19) -> Result<(), ConsensusError>
20where
21 B: Block,
22 R: Receipt,
23 ChainSpec: EthereumHardforks,
24{
25 let cumulative_gas_used =
27 receipts.last().map(|receipt| receipt.cumulative_gas_used()).unwrap_or(0);
28 if block.header().gas_used() != cumulative_gas_used {
29 return Err(ConsensusError::BlockGasUsed {
30 gas: GotExpected { got: cumulative_gas_used, expected: block.header().gas_used() },
31 gas_spent_by_tx: gas_spent_by_transactions(receipts),
32 })
33 }
34
35 if chain_spec.is_byzantium_active_at_block(block.header().number()) {
40 if let Err(error) =
41 verify_receipts(block.header().receipts_root(), block.header().logs_bloom(), receipts)
42 {
43 tracing::debug!(%error, ?receipts, "receipts verification failed");
44 return Err(error)
45 }
46 }
47
48 if chain_spec.is_prague_active_at_timestamp(block.header().timestamp()) {
50 let Some(header_requests_hash) = block.header().requests_hash() else {
51 return Err(ConsensusError::RequestsHashMissing)
52 };
53 let requests_hash = requests.requests_hash();
54 if requests_hash != header_requests_hash {
55 return Err(ConsensusError::BodyRequestsHashDiff(
56 GotExpected::new(requests_hash, header_requests_hash).into(),
57 ))
58 }
59 }
60
61 Ok(())
62}
63
64fn verify_receipts<R: Receipt>(
67 expected_receipts_root: B256,
68 expected_logs_bloom: Bloom,
69 receipts: &[R],
70) -> Result<(), ConsensusError> {
71 let receipts_with_bloom = receipts.iter().map(TxReceipt::with_bloom_ref).collect::<Vec<_>>();
73 let receipts_root = calculate_receipt_root(&receipts_with_bloom);
74
75 let logs_bloom = receipts_with_bloom.iter().fold(Bloom::ZERO, |bloom, r| bloom | r.bloom());
77
78 compare_receipts_root_and_logs_bloom(
79 receipts_root,
80 logs_bloom,
81 expected_receipts_root,
82 expected_logs_bloom,
83 )?;
84
85 Ok(())
86}
87
88fn compare_receipts_root_and_logs_bloom(
91 calculated_receipts_root: B256,
92 calculated_logs_bloom: Bloom,
93 expected_receipts_root: B256,
94 expected_logs_bloom: Bloom,
95) -> Result<(), ConsensusError> {
96 if calculated_receipts_root != expected_receipts_root {
97 return Err(ConsensusError::BodyReceiptRootDiff(
98 GotExpected { got: calculated_receipts_root, expected: expected_receipts_root }.into(),
99 ))
100 }
101
102 if calculated_logs_bloom != expected_logs_bloom {
103 return Err(ConsensusError::BodyBloomLogDiff(
104 GotExpected { got: calculated_logs_bloom, expected: expected_logs_bloom }.into(),
105 ))
106 }
107
108 Ok(())
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114 use alloy_primitives::{b256, hex};
115 use reth_ethereum_primitives::Receipt;
116
117 #[test]
118 fn test_verify_receipts_success() {
119 let receipts = vec![Receipt::default(); 5];
121
122 assert!(verify_receipts(
124 b256!("0x61353b4fb714dc1fccacbf7eafc4273e62f3d1eed716fe41b2a0cd2e12c63ebc"),
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}