mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
369 lines
12 KiB
Rust
369 lines
12 KiB
Rust
use crate::EthPooledTransaction;
|
|
use rand::Rng;
|
|
use reth_chainspec::MAINNET;
|
|
use reth_primitives::{
|
|
constants::MIN_PROTOCOL_BASE_FEE, sign_message, AccessList, Address, Bytes, Transaction,
|
|
TransactionSigned, TxEip1559, TxEip4844, TxKind, TxLegacy, B256, U256,
|
|
};
|
|
|
|
/// A generator for transactions for testing purposes.
|
|
#[derive(Debug)]
|
|
pub struct TransactionGenerator<R> {
|
|
/// The random number generator used for generating keys and selecting signers.
|
|
pub rng: R,
|
|
/// The set of signer keys available for transaction generation.
|
|
pub signer_keys: Vec<B256>,
|
|
/// The base fee for transactions.
|
|
pub base_fee: u128,
|
|
/// The gas limit for transactions.
|
|
pub gas_limit: u64,
|
|
}
|
|
|
|
impl<R: Rng> TransactionGenerator<R> {
|
|
/// Initializes the generator with 10 random signers
|
|
pub fn new(rng: R) -> Self {
|
|
Self::with_num_signers(rng, 10)
|
|
}
|
|
|
|
/// Generates random random signers
|
|
pub fn with_num_signers(rng: R, num_signers: usize) -> Self {
|
|
Self {
|
|
rng,
|
|
signer_keys: (0..num_signers).map(|_| B256::random()).collect(),
|
|
base_fee: MIN_PROTOCOL_BASE_FEE as u128,
|
|
gas_limit: 300_000,
|
|
}
|
|
}
|
|
|
|
/// Adds a new signer to the set
|
|
pub fn push_signer(&mut self, signer: B256) -> &mut Self {
|
|
self.signer_keys.push(signer);
|
|
self
|
|
}
|
|
|
|
/// Sets the default gas limit for all generated transactions
|
|
pub fn set_gas_limit(&mut self, gas_limit: u64) -> &mut Self {
|
|
self.gas_limit = gas_limit;
|
|
self
|
|
}
|
|
|
|
/// Sets the default gas limit for all generated transactions
|
|
pub const fn with_gas_limit(mut self, gas_limit: u64) -> Self {
|
|
self.gas_limit = gas_limit;
|
|
self
|
|
}
|
|
|
|
/// Sets the base fee for the generated transactions
|
|
pub fn set_base_fee(&mut self, base_fee: u64) -> &mut Self {
|
|
self.base_fee = base_fee as u128;
|
|
self
|
|
}
|
|
|
|
/// Sets the base fee for the generated transactions
|
|
pub const fn with_base_fee(mut self, base_fee: u64) -> Self {
|
|
self.base_fee = base_fee as u128;
|
|
self
|
|
}
|
|
|
|
/// Adds the given signers to the set.
|
|
pub fn extend_signers(&mut self, signers: impl IntoIterator<Item = B256>) -> &mut Self {
|
|
self.signer_keys.extend(signers);
|
|
self
|
|
}
|
|
|
|
/// Returns a random signer from the set
|
|
fn rng_signer(&mut self) -> B256 {
|
|
let idx = self.rng.gen_range(0..self.signer_keys.len());
|
|
self.signer_keys[idx]
|
|
}
|
|
|
|
/// Creates a new transaction with a random signer
|
|
pub fn transaction(&mut self) -> TransactionBuilder {
|
|
TransactionBuilder::default()
|
|
.signer(self.rng_signer())
|
|
.max_fee_per_gas(self.base_fee)
|
|
.max_priority_fee_per_gas(self.base_fee)
|
|
.gas_limit(self.gas_limit)
|
|
}
|
|
|
|
/// Creates a new transaction with a random signer
|
|
pub fn gen_eip1559(&mut self) -> TransactionSigned {
|
|
self.transaction().into_eip1559()
|
|
}
|
|
|
|
/// Creates a new transaction with a random signer
|
|
pub fn gen_eip4844(&mut self) -> TransactionSigned {
|
|
self.transaction().into_eip4844()
|
|
}
|
|
|
|
/// Generates and returns a pooled EIP-1559 transaction with a random signer.
|
|
pub fn gen_eip1559_pooled(&mut self) -> EthPooledTransaction {
|
|
self.gen_eip1559().into_ecrecovered().unwrap().try_into().unwrap()
|
|
}
|
|
|
|
/// Generates and returns a pooled EIP-4844 transaction with a random signer.
|
|
pub fn gen_eip4844_pooled(&mut self) -> EthPooledTransaction {
|
|
let tx = self.gen_eip4844().into_ecrecovered().unwrap();
|
|
let encoded_length = tx.length_without_header();
|
|
EthPooledTransaction::new(tx, encoded_length)
|
|
}
|
|
}
|
|
|
|
/// A Builder type to configure and create a transaction.
|
|
#[derive(Debug)]
|
|
pub struct TransactionBuilder {
|
|
/// The signer used to sign the transaction.
|
|
pub signer: B256,
|
|
/// The chain ID on which the transaction will be executed.
|
|
pub chain_id: u64,
|
|
/// The nonce value for the transaction to prevent replay attacks.
|
|
pub nonce: u64,
|
|
/// The maximum amount of gas units that the transaction can consume.
|
|
pub gas_limit: u64,
|
|
/// The maximum fee per gas unit that the sender is willing to pay.
|
|
pub max_fee_per_gas: u128,
|
|
/// The maximum priority fee per gas unit that the sender is willing to pay for faster
|
|
/// processing.
|
|
pub max_priority_fee_per_gas: u128,
|
|
/// The recipient or contract address of the transaction.
|
|
pub to: TxKind,
|
|
/// The value to be transferred in the transaction.
|
|
pub value: U256,
|
|
/// The list of addresses and storage keys that the transaction can access.
|
|
pub access_list: AccessList,
|
|
/// The input data for the transaction, typically containing function parameters for contract
|
|
/// calls.
|
|
pub input: Bytes,
|
|
}
|
|
|
|
impl TransactionBuilder {
|
|
/// Converts the transaction builder into a legacy transaction format.
|
|
pub fn into_legacy(self) -> TransactionSigned {
|
|
Self::signed(
|
|
TxLegacy {
|
|
chain_id: Some(self.chain_id),
|
|
nonce: self.nonce,
|
|
gas_limit: self.gas_limit.into(),
|
|
gas_price: self.max_fee_per_gas,
|
|
to: self.to,
|
|
value: self.value,
|
|
input: self.input,
|
|
}
|
|
.into(),
|
|
self.signer,
|
|
)
|
|
}
|
|
|
|
/// Converts the transaction builder into a transaction format using EIP-1559.
|
|
pub fn into_eip1559(self) -> TransactionSigned {
|
|
Self::signed(
|
|
TxEip1559 {
|
|
chain_id: self.chain_id,
|
|
nonce: self.nonce,
|
|
gas_limit: self.gas_limit.into(),
|
|
max_fee_per_gas: self.max_fee_per_gas,
|
|
max_priority_fee_per_gas: self.max_priority_fee_per_gas,
|
|
to: self.to,
|
|
value: self.value,
|
|
access_list: self.access_list,
|
|
input: self.input,
|
|
}
|
|
.into(),
|
|
self.signer,
|
|
)
|
|
}
|
|
/// Converts the transaction builder into a transaction format using EIP-4844.
|
|
pub fn into_eip4844(self) -> TransactionSigned {
|
|
Self::signed(
|
|
TxEip4844 {
|
|
chain_id: self.chain_id,
|
|
nonce: self.nonce,
|
|
gas_limit: self.gas_limit,
|
|
max_fee_per_gas: self.max_fee_per_gas,
|
|
max_priority_fee_per_gas: self.max_priority_fee_per_gas,
|
|
placeholder: None,
|
|
to: match self.to {
|
|
TxKind::Call(to) => to,
|
|
TxKind::Create => Address::default(),
|
|
},
|
|
value: self.value,
|
|
access_list: self.access_list,
|
|
input: self.input,
|
|
blob_versioned_hashes: Default::default(),
|
|
max_fee_per_blob_gas: Default::default(),
|
|
}
|
|
.into(),
|
|
self.signer,
|
|
)
|
|
}
|
|
|
|
/// Signs the provided transaction using the specified signer and returns a signed transaction.
|
|
fn signed(transaction: Transaction, signer: B256) -> TransactionSigned {
|
|
let signature = sign_message(signer, transaction.signature_hash()).unwrap();
|
|
TransactionSigned::from_transaction_and_signature(transaction, signature)
|
|
}
|
|
|
|
/// Sets the signer for the transaction builder.
|
|
pub const fn signer(mut self, signer: B256) -> Self {
|
|
self.signer = signer;
|
|
self
|
|
}
|
|
|
|
/// Sets the gas limit for the transaction builder.
|
|
pub const fn gas_limit(mut self, gas_limit: u64) -> Self {
|
|
self.gas_limit = gas_limit;
|
|
self
|
|
}
|
|
|
|
/// Sets the nonce for the transaction builder.
|
|
pub const fn nonce(mut self, nonce: u64) -> Self {
|
|
self.nonce = nonce;
|
|
self
|
|
}
|
|
|
|
/// Increments the nonce value of the transaction builder by 1.
|
|
pub const fn inc_nonce(mut self) -> Self {
|
|
self.nonce += 1;
|
|
self
|
|
}
|
|
|
|
/// Decrements the nonce value of the transaction builder by 1, avoiding underflow.
|
|
pub const fn decr_nonce(mut self) -> Self {
|
|
self.nonce = self.nonce.saturating_sub(1);
|
|
self
|
|
}
|
|
|
|
/// Sets the maximum fee per gas for the transaction builder.
|
|
pub const fn max_fee_per_gas(mut self, max_fee_per_gas: u128) -> Self {
|
|
self.max_fee_per_gas = max_fee_per_gas;
|
|
self
|
|
}
|
|
|
|
/// Sets the maximum priority fee per gas for the transaction builder.
|
|
pub const fn max_priority_fee_per_gas(mut self, max_priority_fee_per_gas: u128) -> Self {
|
|
self.max_priority_fee_per_gas = max_priority_fee_per_gas;
|
|
self
|
|
}
|
|
|
|
/// Sets the recipient or contract address for the transaction builder.
|
|
pub const fn to(mut self, to: Address) -> Self {
|
|
self.to = TxKind::Call(to);
|
|
self
|
|
}
|
|
|
|
/// Sets the value to be transferred in the transaction.
|
|
pub fn value(mut self, value: u128) -> Self {
|
|
self.value = U256::from(value);
|
|
self
|
|
}
|
|
|
|
/// Sets the access list for the transaction builder.
|
|
pub fn access_list(mut self, access_list: AccessList) -> Self {
|
|
self.access_list = access_list;
|
|
self
|
|
}
|
|
|
|
/// Sets the transaction input data.
|
|
pub fn input(mut self, input: impl Into<Bytes>) -> Self {
|
|
self.input = input.into();
|
|
self
|
|
}
|
|
|
|
/// Sets the chain ID for the transaction.
|
|
pub const fn chain_id(mut self, chain_id: u64) -> Self {
|
|
self.chain_id = chain_id;
|
|
self
|
|
}
|
|
|
|
/// Sets the chain ID for the transaction, mutable reference version.
|
|
pub fn set_chain_id(&mut self, chain_id: u64) -> &mut Self {
|
|
self.chain_id = chain_id;
|
|
self
|
|
}
|
|
|
|
/// Sets the nonce for the transaction, mutable reference version.
|
|
pub fn set_nonce(&mut self, nonce: u64) -> &mut Self {
|
|
self.nonce = nonce;
|
|
self
|
|
}
|
|
|
|
/// Sets the gas limit for the transaction, mutable reference version.
|
|
pub fn set_gas_limit(&mut self, gas_limit: u64) -> &mut Self {
|
|
self.gas_limit = gas_limit;
|
|
self
|
|
}
|
|
|
|
/// Sets the maximum fee per gas for the transaction, mutable reference version.
|
|
pub fn set_max_fee_per_gas(&mut self, max_fee_per_gas: u128) -> &mut Self {
|
|
self.max_fee_per_gas = max_fee_per_gas;
|
|
self
|
|
}
|
|
|
|
/// Sets the maximum priority fee per gas for the transaction, mutable reference version.
|
|
pub fn set_max_priority_fee_per_gas(&mut self, max_priority_fee_per_gas: u128) -> &mut Self {
|
|
self.max_priority_fee_per_gas = max_priority_fee_per_gas;
|
|
self
|
|
}
|
|
|
|
/// Sets the recipient or contract address for the transaction, mutable reference version.
|
|
pub fn set_to(&mut self, to: Address) -> &mut Self {
|
|
self.to = to.into();
|
|
self
|
|
}
|
|
|
|
/// Sets the value to be transferred in the transaction, mutable reference version.
|
|
pub fn set_value(&mut self, value: u128) -> &mut Self {
|
|
self.value = U256::from(value);
|
|
self
|
|
}
|
|
|
|
/// Sets the access list for the transaction, mutable reference version.
|
|
pub fn set_access_list(&mut self, access_list: AccessList) -> &mut Self {
|
|
self.access_list = access_list;
|
|
self
|
|
}
|
|
|
|
/// Sets the signer for the transaction, mutable reference version.
|
|
pub fn set_signer(&mut self, signer: B256) -> &mut Self {
|
|
self.signer = signer;
|
|
self
|
|
}
|
|
|
|
/// Sets the transaction input data, mutable reference version.
|
|
pub fn set_input(&mut self, input: impl Into<Bytes>) -> &mut Self {
|
|
self.input = input.into();
|
|
self
|
|
}
|
|
}
|
|
|
|
impl Default for TransactionBuilder {
|
|
fn default() -> Self {
|
|
Self {
|
|
signer: B256::random(),
|
|
chain_id: MAINNET.chain.id(),
|
|
nonce: 0,
|
|
gas_limit: 0,
|
|
max_fee_per_gas: 0,
|
|
max_priority_fee_per_gas: 0,
|
|
to: Default::default(),
|
|
value: Default::default(),
|
|
access_list: Default::default(),
|
|
input: Default::default(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use rand::thread_rng;
|
|
|
|
#[test]
|
|
fn test_generate_transaction() {
|
|
let rng = thread_rng();
|
|
let mut gen = TransactionGenerator::new(rng);
|
|
let _tx = gen.transaction().into_legacy();
|
|
let _tx = gen.transaction().into_eip1559();
|
|
}
|
|
}
|