reth_transaction_pool/test_utils/
gen.rs

1use crate::EthPooledTransaction;
2use alloy_consensus::{TxEip1559, TxEip4844, TxLegacy};
3use alloy_eips::{eip1559::MIN_PROTOCOL_BASE_FEE, eip2718::Encodable2718, eip2930::AccessList};
4use alloy_primitives::{Address, Bytes, TxKind, B256, U256};
5use rand::Rng;
6use reth_chainspec::MAINNET;
7use reth_primitives::{sign_message, Transaction, TransactionSigned};
8
9/// A generator for transactions for testing purposes.
10#[derive(Debug)]
11pub struct TransactionGenerator<R> {
12    /// The random number generator used for generating keys and selecting signers.
13    pub rng: R,
14    /// The set of signer keys available for transaction generation.
15    pub signer_keys: Vec<B256>,
16    /// The base fee for transactions.
17    pub base_fee: u128,
18    /// The gas limit for transactions.
19    pub gas_limit: u64,
20}
21
22impl<R: Rng> TransactionGenerator<R> {
23    /// Initializes the generator with 10 random signers
24    pub fn new(rng: R) -> Self {
25        Self::with_num_signers(rng, 10)
26    }
27
28    /// Generates random random signers
29    pub fn with_num_signers(rng: R, num_signers: usize) -> Self {
30        Self {
31            rng,
32            signer_keys: (0..num_signers).map(|_| B256::random()).collect(),
33            base_fee: MIN_PROTOCOL_BASE_FEE as u128,
34            gas_limit: 300_000,
35        }
36    }
37
38    /// Adds a new signer to the set
39    pub fn push_signer(&mut self, signer: B256) -> &mut Self {
40        self.signer_keys.push(signer);
41        self
42    }
43
44    /// Sets the default gas limit for all generated transactions
45    pub fn set_gas_limit(&mut self, gas_limit: u64) -> &mut Self {
46        self.gas_limit = gas_limit;
47        self
48    }
49
50    /// Sets the default gas limit for all generated transactions
51    pub const fn with_gas_limit(mut self, gas_limit: u64) -> Self {
52        self.gas_limit = gas_limit;
53        self
54    }
55
56    /// Sets the base fee for the generated transactions
57    pub fn set_base_fee(&mut self, base_fee: u64) -> &mut Self {
58        self.base_fee = base_fee as u128;
59        self
60    }
61
62    /// Sets the base fee for the generated transactions
63    pub const fn with_base_fee(mut self, base_fee: u64) -> Self {
64        self.base_fee = base_fee as u128;
65        self
66    }
67
68    /// Adds the given signers to the set.
69    pub fn extend_signers(&mut self, signers: impl IntoIterator<Item = B256>) -> &mut Self {
70        self.signer_keys.extend(signers);
71        self
72    }
73
74    /// Returns a random signer from the set
75    fn rng_signer(&mut self) -> B256 {
76        let idx = self.rng.gen_range(0..self.signer_keys.len());
77        self.signer_keys[idx]
78    }
79
80    /// Creates a new transaction with a random signer
81    pub fn transaction(&mut self) -> TransactionBuilder {
82        TransactionBuilder::default()
83            .signer(self.rng_signer())
84            .max_fee_per_gas(self.base_fee)
85            .max_priority_fee_per_gas(self.base_fee)
86            .gas_limit(self.gas_limit)
87    }
88
89    /// Creates a new transaction with a random signer
90    pub fn gen_eip1559(&mut self) -> TransactionSigned {
91        self.transaction().into_eip1559()
92    }
93
94    /// Creates a new transaction with a random signer
95    pub fn gen_eip4844(&mut self) -> TransactionSigned {
96        self.transaction().into_eip4844()
97    }
98
99    /// Generates and returns a pooled EIP-1559 transaction with a random signer.
100    pub fn gen_eip1559_pooled(&mut self) -> EthPooledTransaction {
101        self.gen_eip1559().into_ecrecovered().unwrap().try_into().unwrap()
102    }
103
104    /// Generates and returns a pooled EIP-4844 transaction with a random signer.
105    pub fn gen_eip4844_pooled(&mut self) -> EthPooledTransaction {
106        let tx = self.gen_eip4844().into_ecrecovered().unwrap();
107        let encoded_length = tx.encode_2718_len();
108        EthPooledTransaction::new(tx, encoded_length)
109    }
110}
111
112/// A Builder type to configure and create a transaction.
113#[derive(Debug)]
114pub struct TransactionBuilder {
115    /// The signer used to sign the transaction.
116    pub signer: B256,
117    /// The chain ID on which the transaction will be executed.
118    pub chain_id: u64,
119    /// The nonce value for the transaction to prevent replay attacks.
120    pub nonce: u64,
121    /// The maximum amount of gas units that the transaction can consume.
122    pub gas_limit: u64,
123    /// The maximum fee per gas unit that the sender is willing to pay.
124    pub max_fee_per_gas: u128,
125    /// The maximum priority fee per gas unit that the sender is willing to pay for faster
126    /// processing.
127    pub max_priority_fee_per_gas: u128,
128    /// The recipient or contract address of the transaction.
129    pub to: TxKind,
130    /// The value to be transferred in the transaction.
131    pub value: U256,
132    /// The list of addresses and storage keys that the transaction can access.
133    pub access_list: AccessList,
134    /// The input data for the transaction, typically containing function parameters for contract
135    /// calls.
136    pub input: Bytes,
137}
138
139impl TransactionBuilder {
140    /// Converts the transaction builder into a legacy transaction format.
141    pub fn into_legacy(self) -> TransactionSigned {
142        Self::signed(
143            TxLegacy {
144                chain_id: Some(self.chain_id),
145                nonce: self.nonce,
146                gas_limit: self.gas_limit,
147                gas_price: self.max_fee_per_gas,
148                to: self.to,
149                value: self.value,
150                input: self.input,
151            }
152            .into(),
153            self.signer,
154        )
155    }
156
157    /// Converts the transaction builder into a transaction format using EIP-1559.
158    pub fn into_eip1559(self) -> TransactionSigned {
159        Self::signed(
160            TxEip1559 {
161                chain_id: self.chain_id,
162                nonce: self.nonce,
163                gas_limit: self.gas_limit,
164                max_fee_per_gas: self.max_fee_per_gas,
165                max_priority_fee_per_gas: self.max_priority_fee_per_gas,
166                to: self.to,
167                value: self.value,
168                access_list: self.access_list,
169                input: self.input,
170            }
171            .into(),
172            self.signer,
173        )
174    }
175    /// Converts the transaction builder into a transaction format using EIP-4844.
176    pub fn into_eip4844(self) -> TransactionSigned {
177        Self::signed(
178            TxEip4844 {
179                chain_id: self.chain_id,
180                nonce: self.nonce,
181                gas_limit: self.gas_limit,
182                max_fee_per_gas: self.max_fee_per_gas,
183                max_priority_fee_per_gas: self.max_priority_fee_per_gas,
184                to: match self.to {
185                    TxKind::Call(to) => to,
186                    TxKind::Create => Address::default(),
187                },
188                value: self.value,
189                access_list: self.access_list,
190                input: self.input,
191                blob_versioned_hashes: Default::default(),
192                max_fee_per_blob_gas: Default::default(),
193            }
194            .into(),
195            self.signer,
196        )
197    }
198
199    /// Signs the provided transaction using the specified signer and returns a signed transaction.
200    fn signed(transaction: Transaction, signer: B256) -> TransactionSigned {
201        let signature = sign_message(signer, transaction.signature_hash()).unwrap();
202        TransactionSigned::new_unhashed(transaction, signature)
203    }
204
205    /// Sets the signer for the transaction builder.
206    pub const fn signer(mut self, signer: B256) -> Self {
207        self.signer = signer;
208        self
209    }
210
211    /// Sets the gas limit for the transaction builder.
212    pub const fn gas_limit(mut self, gas_limit: u64) -> Self {
213        self.gas_limit = gas_limit;
214        self
215    }
216
217    /// Sets the nonce for the transaction builder.
218    pub const fn nonce(mut self, nonce: u64) -> Self {
219        self.nonce = nonce;
220        self
221    }
222
223    /// Increments the nonce value of the transaction builder by 1.
224    pub const fn inc_nonce(mut self) -> Self {
225        self.nonce += 1;
226        self
227    }
228
229    /// Decrements the nonce value of the transaction builder by 1, avoiding underflow.
230    pub const fn decr_nonce(mut self) -> Self {
231        self.nonce = self.nonce.saturating_sub(1);
232        self
233    }
234
235    /// Sets the maximum fee per gas for the transaction builder.
236    pub const fn max_fee_per_gas(mut self, max_fee_per_gas: u128) -> Self {
237        self.max_fee_per_gas = max_fee_per_gas;
238        self
239    }
240
241    /// Sets the maximum priority fee per gas for the transaction builder.
242    pub const fn max_priority_fee_per_gas(mut self, max_priority_fee_per_gas: u128) -> Self {
243        self.max_priority_fee_per_gas = max_priority_fee_per_gas;
244        self
245    }
246
247    /// Sets the recipient or contract address for the transaction builder.
248    pub const fn to(mut self, to: Address) -> Self {
249        self.to = TxKind::Call(to);
250        self
251    }
252
253    /// Sets the value to be transferred in the transaction.
254    pub fn value(mut self, value: u128) -> Self {
255        self.value = U256::from(value);
256        self
257    }
258
259    /// Sets the access list for the transaction builder.
260    pub fn access_list(mut self, access_list: AccessList) -> Self {
261        self.access_list = access_list;
262        self
263    }
264
265    /// Sets the transaction input data.
266    pub fn input(mut self, input: impl Into<Bytes>) -> Self {
267        self.input = input.into();
268        self
269    }
270
271    /// Sets the chain ID for the transaction.
272    pub const fn chain_id(mut self, chain_id: u64) -> Self {
273        self.chain_id = chain_id;
274        self
275    }
276
277    /// Sets the chain ID for the transaction, mutable reference version.
278    pub fn set_chain_id(&mut self, chain_id: u64) -> &mut Self {
279        self.chain_id = chain_id;
280        self
281    }
282
283    /// Sets the nonce for the transaction, mutable reference version.
284    pub fn set_nonce(&mut self, nonce: u64) -> &mut Self {
285        self.nonce = nonce;
286        self
287    }
288
289    /// Sets the gas limit for the transaction, mutable reference version.
290    pub fn set_gas_limit(&mut self, gas_limit: u64) -> &mut Self {
291        self.gas_limit = gas_limit;
292        self
293    }
294
295    /// Sets the maximum fee per gas for the transaction, mutable reference version.
296    pub fn set_max_fee_per_gas(&mut self, max_fee_per_gas: u128) -> &mut Self {
297        self.max_fee_per_gas = max_fee_per_gas;
298        self
299    }
300
301    /// Sets the maximum priority fee per gas for the transaction, mutable reference version.
302    pub fn set_max_priority_fee_per_gas(&mut self, max_priority_fee_per_gas: u128) -> &mut Self {
303        self.max_priority_fee_per_gas = max_priority_fee_per_gas;
304        self
305    }
306
307    /// Sets the recipient or contract address for the transaction, mutable reference version.
308    pub fn set_to(&mut self, to: Address) -> &mut Self {
309        self.to = to.into();
310        self
311    }
312
313    /// Sets the value to be transferred in the transaction, mutable reference version.
314    pub fn set_value(&mut self, value: u128) -> &mut Self {
315        self.value = U256::from(value);
316        self
317    }
318
319    /// Sets the access list for the transaction, mutable reference version.
320    pub fn set_access_list(&mut self, access_list: AccessList) -> &mut Self {
321        self.access_list = access_list;
322        self
323    }
324
325    /// Sets the signer for the transaction, mutable reference version.
326    pub fn set_signer(&mut self, signer: B256) -> &mut Self {
327        self.signer = signer;
328        self
329    }
330
331    /// Sets the transaction input data, mutable reference version.
332    pub fn set_input(&mut self, input: impl Into<Bytes>) -> &mut Self {
333        self.input = input.into();
334        self
335    }
336}
337
338impl Default for TransactionBuilder {
339    fn default() -> Self {
340        Self {
341            signer: B256::random(),
342            chain_id: MAINNET.chain.id(),
343            nonce: 0,
344            gas_limit: 0,
345            max_fee_per_gas: 0,
346            max_priority_fee_per_gas: 0,
347            to: Default::default(),
348            value: Default::default(),
349            access_list: Default::default(),
350            input: Default::default(),
351        }
352    }
353}
354
355#[cfg(test)]
356mod tests {
357    use super::*;
358    use rand::thread_rng;
359
360    #[test]
361    fn test_generate_transaction() {
362        let rng = thread_rng();
363        let mut gen = TransactionGenerator::new(rng);
364        let _tx = gen.transaction().into_legacy();
365        let _tx = gen.transaction().into_eip1559();
366    }
367}