Skip to main content

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