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