reth_bench/bench/send_invalid_payload/
invalidation.rs1use 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#[derive(Debug, Default)]
9pub(super) struct InvalidationConfig {
10 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 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 pub(super) const fn should_skip_hash_recalc(&self) -> bool {
49 self.block_hash.is_some() || self.invalidate_block_hash
50 }
51
52 pub(super) fn apply_to_payload_v1(&self, payload: &mut ExecutionPayloadV1) -> Vec<String> {
54 let mut changes = Vec::new();
55
56 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 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 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 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 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 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 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 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 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 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 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}