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