reth_bench/bench/send_invalid_payload/
invalidation.rs

1use alloy_eips::eip4895::Withdrawal;
2use alloy_primitives::{Address, Bloom, Bytes, B256, U256};
3use alloy_rpc_types_engine::{ExecutionPayloadV1, ExecutionPayloadV2, ExecutionPayloadV3};
4
5/// Configuration for invalidating payload fields
6#[derive(Debug, Default)]
7pub(super) struct InvalidationConfig {
8    // Explicit value overrides (Option<T>)
9    pub(super) parent_hash: Option<B256>,
10    pub(super) fee_recipient: Option<Address>,
11    pub(super) state_root: Option<B256>,
12    pub(super) receipts_root: Option<B256>,
13    pub(super) logs_bloom: Option<Bloom>,
14    pub(super) prev_randao: Option<B256>,
15    pub(super) block_number: Option<u64>,
16    pub(super) gas_limit: Option<u64>,
17    pub(super) gas_used: Option<u64>,
18    pub(super) timestamp: Option<u64>,
19    pub(super) extra_data: Option<Bytes>,
20    pub(super) base_fee_per_gas: Option<u64>,
21    pub(super) block_hash: Option<B256>,
22    pub(super) blob_gas_used: Option<u64>,
23    pub(super) excess_blob_gas: Option<u64>,
24
25    // Auto-invalidation flags
26    pub(super) invalidate_parent_hash: bool,
27    pub(super) invalidate_state_root: bool,
28    pub(super) invalidate_receipts_root: bool,
29    pub(super) invalidate_gas_used: bool,
30    pub(super) invalidate_block_number: bool,
31    pub(super) invalidate_timestamp: bool,
32    pub(super) invalidate_base_fee: bool,
33    pub(super) invalidate_transactions: bool,
34    pub(super) invalidate_block_hash: bool,
35    pub(super) invalidate_withdrawals: bool,
36    pub(super) invalidate_blob_gas_used: bool,
37    pub(super) invalidate_excess_blob_gas: bool,
38}
39
40impl InvalidationConfig {
41    /// Returns true if `block_hash` is being explicitly set or auto-invalidated.
42    /// When true, the caller should skip recalculating the block hash since it will be overwritten.
43    pub(super) const fn should_skip_hash_recalc(&self) -> bool {
44        self.block_hash.is_some() || self.invalidate_block_hash
45    }
46
47    /// Applies invalidations to a V1 payload, returns list of what was changed.
48    pub(super) fn apply_to_payload_v1(&self, payload: &mut ExecutionPayloadV1) -> Vec<String> {
49        let mut changes = Vec::new();
50
51        // Explicit value overrides
52        if let Some(parent_hash) = self.parent_hash {
53            payload.parent_hash = parent_hash;
54            changes.push(format!("parent_hash = {parent_hash}"));
55        }
56
57        if let Some(fee_recipient) = self.fee_recipient {
58            payload.fee_recipient = fee_recipient;
59            changes.push(format!("fee_recipient = {fee_recipient}"));
60        }
61
62        if let Some(state_root) = self.state_root {
63            payload.state_root = state_root;
64            changes.push(format!("state_root = {state_root}"));
65        }
66
67        if let Some(receipts_root) = self.receipts_root {
68            payload.receipts_root = receipts_root;
69            changes.push(format!("receipts_root = {receipts_root}"));
70        }
71
72        if let Some(logs_bloom) = self.logs_bloom {
73            payload.logs_bloom = logs_bloom;
74            changes.push("logs_bloom = <custom>".to_string());
75        }
76
77        if let Some(prev_randao) = self.prev_randao {
78            payload.prev_randao = prev_randao;
79            changes.push(format!("prev_randao = {prev_randao}"));
80        }
81
82        if let Some(block_number) = self.block_number {
83            payload.block_number = block_number;
84            changes.push(format!("block_number = {block_number}"));
85        }
86
87        if let Some(gas_limit) = self.gas_limit {
88            payload.gas_limit = gas_limit;
89            changes.push(format!("gas_limit = {gas_limit}"));
90        }
91
92        if let Some(gas_used) = self.gas_used {
93            payload.gas_used = gas_used;
94            changes.push(format!("gas_used = {gas_used}"));
95        }
96
97        if let Some(timestamp) = self.timestamp {
98            payload.timestamp = timestamp;
99            changes.push(format!("timestamp = {timestamp}"));
100        }
101
102        if let Some(ref extra_data) = self.extra_data {
103            payload.extra_data = extra_data.clone();
104            changes.push(format!("extra_data = {} bytes", extra_data.len()));
105        }
106
107        if let Some(base_fee_per_gas) = self.base_fee_per_gas {
108            payload.base_fee_per_gas = U256::from_limbs([base_fee_per_gas, 0, 0, 0]);
109            changes.push(format!("base_fee_per_gas = {base_fee_per_gas}"));
110        }
111
112        if let Some(block_hash) = self.block_hash {
113            payload.block_hash = block_hash;
114            changes.push(format!("block_hash = {block_hash}"));
115        }
116
117        // Auto-invalidation flags
118        if self.invalidate_parent_hash {
119            let random_hash = B256::random();
120            payload.parent_hash = random_hash;
121            changes.push(format!("parent_hash = {random_hash} (auto-invalidated: random)"));
122        }
123
124        if self.invalidate_state_root {
125            payload.state_root = B256::ZERO;
126            changes.push("state_root = ZERO (auto-invalidated: empty trie root)".to_string());
127        }
128
129        if self.invalidate_receipts_root {
130            payload.receipts_root = B256::ZERO;
131            changes.push("receipts_root = ZERO (auto-invalidated)".to_string());
132        }
133
134        if self.invalidate_gas_used {
135            let invalid_gas = payload.gas_limit + 1;
136            payload.gas_used = invalid_gas;
137            changes.push(format!("gas_used = {invalid_gas} (auto-invalidated: exceeds gas_limit)"));
138        }
139
140        if self.invalidate_block_number {
141            let invalid_number = payload.block_number + 999;
142            payload.block_number = invalid_number;
143            changes.push(format!("block_number = {invalid_number} (auto-invalidated: huge gap)"));
144        }
145
146        if self.invalidate_timestamp {
147            payload.timestamp = 0;
148            changes.push("timestamp = 0 (auto-invalidated: impossibly old)".to_string());
149        }
150
151        if self.invalidate_base_fee {
152            payload.base_fee_per_gas = U256::ZERO;
153            changes
154                .push("base_fee_per_gas = 0 (auto-invalidated: invalid post-London)".to_string());
155        }
156
157        if self.invalidate_transactions {
158            let invalid_tx = Bytes::from_static(&[0xff, 0xff, 0xff]);
159            payload.transactions.insert(0, invalid_tx);
160            changes.push("transactions = prepended invalid RLP (auto-invalidated)".to_string());
161        }
162
163        if self.invalidate_block_hash {
164            let random_hash = B256::random();
165            payload.block_hash = random_hash;
166            changes.push(format!("block_hash = {random_hash} (auto-invalidated: random)"));
167        }
168
169        changes
170    }
171
172    /// Applies invalidations to a V2 payload, returns list of what was changed.
173    pub(super) fn apply_to_payload_v2(&self, payload: &mut ExecutionPayloadV2) -> Vec<String> {
174        let mut changes = self.apply_to_payload_v1(&mut payload.payload_inner);
175
176        // Handle withdrawals invalidation (V2+)
177        if self.invalidate_withdrawals {
178            let fake_withdrawal = Withdrawal {
179                index: u64::MAX,
180                validator_index: u64::MAX,
181                address: Address::ZERO,
182                amount: u64::MAX,
183            };
184            payload.withdrawals.push(fake_withdrawal);
185            changes.push("withdrawals = added fake withdrawal (auto-invalidated)".to_string());
186        }
187
188        changes
189    }
190
191    /// Applies invalidations to a V3 payload, returns list of what was changed.
192    pub(super) fn apply_to_payload_v3(&self, payload: &mut ExecutionPayloadV3) -> Vec<String> {
193        let mut changes = self.apply_to_payload_v2(&mut payload.payload_inner);
194
195        // Explicit overrides for V3 fields
196        if let Some(blob_gas_used) = self.blob_gas_used {
197            payload.blob_gas_used = blob_gas_used;
198            changes.push(format!("blob_gas_used = {blob_gas_used}"));
199        }
200
201        if let Some(excess_blob_gas) = self.excess_blob_gas {
202            payload.excess_blob_gas = excess_blob_gas;
203            changes.push(format!("excess_blob_gas = {excess_blob_gas}"));
204        }
205
206        // Auto-invalidation for V3 fields
207        if self.invalidate_blob_gas_used {
208            payload.blob_gas_used = u64::MAX;
209            changes.push("blob_gas_used = MAX (auto-invalidated)".to_string());
210        }
211
212        if self.invalidate_excess_blob_gas {
213            payload.excess_blob_gas = u64::MAX;
214            changes.push("excess_blob_gas = MAX (auto-invalidated)".to_string());
215        }
216
217        changes
218    }
219}