chore: use alloy traits for PoolTransaction (#14228)

This commit is contained in:
Arsenii Kulikov
2025-02-05 06:05:00 +04:00
committed by GitHub
parent 6d5607dc2b
commit a63f92e017
17 changed files with 276 additions and 322 deletions

1
Cargo.lock generated
View File

@ -3122,6 +3122,7 @@ dependencies = [
name = "example-network-txpool" name = "example-network-txpool"
version = "0.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"alloy-consensus",
"eyre", "eyre",
"reth-network", "reth-network",
"reth-provider", "reth-provider",

View File

@ -1710,7 +1710,7 @@ impl PooledTransactionsHashesBuilder {
Self::Eth68(msg) => { Self::Eth68(msg) => {
msg.hashes.push(*pooled_tx.hash()); msg.hashes.push(*pooled_tx.hash());
msg.sizes.push(pooled_tx.encoded_length()); msg.sizes.push(pooled_tx.encoded_length());
msg.types.push(pooled_tx.transaction.tx_type()); msg.types.push(pooled_tx.transaction.ty());
} }
} }
} }

View File

@ -2,9 +2,8 @@
use alloy_consensus::{ use alloy_consensus::{
BlobTransactionSidecar, BlobTransactionValidationError, BlockHeader, Transaction, Typed2718, BlobTransactionSidecar, BlobTransactionValidationError, BlockHeader, Transaction, Typed2718,
}; };
use alloy_eips::eip2718::Encodable2718; use alloy_eips::{eip2718::Encodable2718, eip7702::SignedAuthorization};
use alloy_primitives::{Address, TxHash, TxKind, U256}; use alloy_primitives::{Address, Bytes, TxHash, TxKind, B256, U256};
use op_alloy_consensus::OpTypedTransaction;
use parking_lot::RwLock; use parking_lot::RwLock;
use reth_node_api::{Block, BlockBody}; use reth_node_api::{Block, BlockBody};
use reth_optimism_evm::RethL1BlockInfo; use reth_optimism_evm::RethL1BlockInfo;
@ -14,7 +13,7 @@ use reth_primitives::{
transaction::TransactionConversionError, GotExpected, InvalidTransactionError, Recovered, transaction::TransactionConversionError, GotExpected, InvalidTransactionError, Recovered,
SealedBlock, SealedBlock,
}; };
use reth_primitives_traits::SignedTransaction; use reth_primitives_traits::{InMemorySize, SignedTransaction};
use reth_provider::{BlockReaderIdExt, ChainSpecProvider, StateProviderFactory}; use reth_provider::{BlockReaderIdExt, ChainSpecProvider, StateProviderFactory};
use reth_revm::L1BlockInfo; use reth_revm::L1BlockInfo;
use reth_transaction_pool::{ use reth_transaction_pool::{
@ -123,68 +122,94 @@ impl PoolTransaction for OpPooledTransaction {
self.inner.transaction.signer_ref() self.inner.transaction.signer_ref()
} }
fn nonce(&self) -> u64 {
self.inner.transaction.nonce()
}
fn cost(&self) -> &U256 { fn cost(&self) -> &U256 {
&self.inner.cost &self.inner.cost
} }
fn gas_limit(&self) -> u64 {
self.inner.transaction.gas_limit()
}
fn max_fee_per_gas(&self) -> u128 {
self.inner.transaction.max_fee_per_gas()
}
fn access_list(&self) -> Option<&AccessList> {
self.inner.transaction.access_list()
}
fn max_priority_fee_per_gas(&self) -> Option<u128> {
self.inner.transaction.max_priority_fee_per_gas()
}
fn max_fee_per_blob_gas(&self) -> Option<u128> {
self.inner.transaction.max_fee_per_blob_gas()
}
fn effective_tip_per_gas(&self, base_fee: u64) -> Option<u128> {
self.inner.transaction.effective_tip_per_gas(base_fee)
}
fn priority_fee_or_price(&self) -> u128 {
self.inner.transaction.priority_fee_or_price()
}
fn kind(&self) -> TxKind {
self.inner.transaction.kind()
}
fn is_create(&self) -> bool {
self.inner.transaction.is_create()
}
fn input(&self) -> &[u8] {
self.inner.transaction.input()
}
fn size(&self) -> usize {
self.inner.transaction.input().len()
}
fn tx_type(&self) -> u8 {
self.inner.transaction.ty()
}
fn encoded_length(&self) -> usize { fn encoded_length(&self) -> usize {
self.inner.encoded_length self.inner.encoded_length
} }
}
fn chain_id(&self) -> Option<u64> { impl Typed2718 for OpPooledTransaction {
self.inner.transaction.chain_id() fn ty(&self) -> u8 {
self.inner.ty()
}
}
impl InMemorySize for OpPooledTransaction {
fn size(&self) -> usize {
self.inner.size()
}
}
impl alloy_consensus::Transaction for OpPooledTransaction {
fn chain_id(&self) -> Option<alloy_primitives::ChainId> {
self.inner.chain_id()
}
fn nonce(&self) -> u64 {
self.inner.nonce()
}
fn gas_limit(&self) -> u64 {
self.inner.gas_limit()
}
fn gas_price(&self) -> Option<u128> {
self.inner.gas_price()
}
fn max_fee_per_gas(&self) -> u128 {
self.inner.max_fee_per_gas()
}
fn max_priority_fee_per_gas(&self) -> Option<u128> {
self.inner.max_priority_fee_per_gas()
}
fn max_fee_per_blob_gas(&self) -> Option<u128> {
self.inner.max_fee_per_blob_gas()
}
fn priority_fee_or_price(&self) -> u128 {
self.inner.priority_fee_or_price()
}
fn effective_gas_price(&self, base_fee: Option<u64>) -> u128 {
self.inner.effective_gas_price(base_fee)
}
fn is_dynamic_fee(&self) -> bool {
self.inner.is_dynamic_fee()
}
fn kind(&self) -> TxKind {
self.inner.kind()
}
fn is_create(&self) -> bool {
self.inner.is_create()
}
fn value(&self) -> U256 {
self.inner.value()
}
fn input(&self) -> &Bytes {
self.inner.input()
}
fn access_list(&self) -> Option<&AccessList> {
self.inner.access_list()
}
fn blob_versioned_hashes(&self) -> Option<&[B256]> {
self.inner.blob_versioned_hashes()
}
fn authorization_list(&self) -> Option<&[SignedAuthorization]> {
self.inner.authorization_list()
} }
} }
@ -193,10 +218,6 @@ impl EthPoolTransaction for OpPooledTransaction {
EthBlobTransactionSidecar::None EthBlobTransactionSidecar::None
} }
fn blob_count(&self) -> usize {
0
}
fn try_into_pooled_eip4844( fn try_into_pooled_eip4844(
self, self,
_sidecar: Arc<BlobTransactionSidecar>, _sidecar: Arc<BlobTransactionSidecar>,
@ -216,14 +237,7 @@ impl EthPoolTransaction for OpPooledTransaction {
_sidecar: &BlobTransactionSidecar, _sidecar: &BlobTransactionSidecar,
_settings: &KzgSettings, _settings: &KzgSettings,
) -> Result<(), BlobTransactionValidationError> { ) -> Result<(), BlobTransactionValidationError> {
Err(BlobTransactionValidationError::NotBlobTransaction(self.tx_type())) Err(BlobTransactionValidationError::NotBlobTransaction(self.ty()))
}
fn authorization_count(&self) -> usize {
match self.inner.transaction.transaction() {
OpTypedTransaction::Eip7702(tx) => tx.authorization_list.len(),
_ => 0,
}
} }
} }

View File

@ -113,7 +113,7 @@ fn generate_test_data(
mod implementations { mod implementations {
use super::*; use super::*;
use reth_transaction_pool::PoolTransaction; use alloy_consensus::Transaction;
use std::collections::BinaryHeap; use std::collections::BinaryHeap;
/// This implementation appends the transactions and uses [`Vec::sort_by`] function for sorting. /// This implementation appends the transactions and uses [`Vec::sort_by`] function for sorting.

View File

@ -4,6 +4,8 @@ use crate::{
pool::pending::PendingTransaction, pool::pending::PendingTransaction,
PoolTransaction, TransactionOrdering, ValidPoolTransaction, PoolTransaction, TransactionOrdering, ValidPoolTransaction,
}; };
use alloy_consensus::Transaction;
use alloy_eips::Typed2718;
use alloy_primitives::Address; use alloy_primitives::Address;
use core::fmt; use core::fmt;
use reth_payload_util::PayloadTransactions; use reth_payload_util::PayloadTransactions;

View File

@ -29,7 +29,7 @@ pub(crate) struct BlobTransactions<T: PoolTransaction> {
pending_fees: PendingFees, pending_fees: PendingFees,
/// Keeps track of the size of this pool. /// Keeps track of the size of this pool.
/// ///
/// See also [`PoolTransaction::size`]. /// See also [`reth_primitives_traits::InMemorySize::size`].
size_of: SizeTracker, size_of: SizeTracker,
} }

View File

@ -87,7 +87,7 @@ use parking_lot::{Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard};
use reth_eth_wire_types::HandleMempoolData; use reth_eth_wire_types::HandleMempoolData;
use reth_execution_types::ChangedAccount; use reth_execution_types::ChangedAccount;
use alloy_eips::eip4844::BlobTransactionSidecar; use alloy_eips::{eip4844::BlobTransactionSidecar, Typed2718};
use reth_primitives::Recovered; use reth_primitives::Recovered;
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use std::{collections::HashSet, fmt, sync::Arc, time::Instant}; use std::{collections::HashSet, fmt, sync::Arc, time::Instant};

View File

@ -43,7 +43,7 @@ pub struct ParkedPool<T: ParkedOrd> {
sender_transaction_count: FxHashMap<SenderId, SenderTransactionCount>, sender_transaction_count: FxHashMap<SenderId, SenderTransactionCount>,
/// Keeps track of the size of this pool. /// Keeps track of the size of this pool.
/// ///
/// See also [`PoolTransaction::size`]. /// See also [`reth_primitives_traits::InMemorySize::size`].
size_of: SizeTracker, size_of: SizeTracker,
} }
@ -520,6 +520,7 @@ impl<T: PoolTransaction> Ord for QueuedOrd<T> {
mod tests { mod tests {
use super::*; use super::*;
use crate::test_utils::{MockTransaction, MockTransactionFactory, MockTransactionSet}; use crate::test_utils::{MockTransaction, MockTransactionFactory, MockTransactionSet};
use alloy_consensus::Transaction;
use alloy_primitives::address; use alloy_primitives::address;
use reth_primitives::TxType; use reth_primitives::TxType;
use std::collections::HashSet; use std::collections::HashSet;

View File

@ -43,7 +43,7 @@ pub struct PendingPool<T: TransactionOrdering> {
independent_transactions: FxHashMap<SenderId, PendingTransaction<T>>, independent_transactions: FxHashMap<SenderId, PendingTransaction<T>>,
/// Keeps track of the size of this pool. /// Keeps track of the size of this pool.
/// ///
/// See also [`PoolTransaction::size`](crate::traits::PoolTransaction::size). /// See also [`reth_primitives_traits::InMemorySize::size`].
size_of: SizeTracker, size_of: SizeTracker,
/// Used to broadcast new transactions that have been added to the `PendingPool` to existing /// Used to broadcast new transactions that have been added to the `PendingPool` to existing
/// `static_files` of this pool. /// `static_files` of this pool.
@ -613,6 +613,7 @@ mod tests {
test_utils::{MockOrdering, MockTransaction, MockTransactionFactory, MockTransactionSet}, test_utils::{MockOrdering, MockTransaction, MockTransactionFactory, MockTransactionSet},
PoolTransaction, PoolTransaction,
}; };
use alloy_consensus::Transaction;
use alloy_primitives::address; use alloy_primitives::address;
use reth_primitives::TxType; use reth_primitives::TxType;
use std::collections::HashSet; use std::collections::HashSet;

View File

@ -25,6 +25,7 @@ use alloy_consensus::constants::{
use alloy_eips::{ use alloy_eips::{
eip1559::{ETHEREUM_BLOCK_GAS_LIMIT, MIN_PROTOCOL_BASE_FEE}, eip1559::{ETHEREUM_BLOCK_GAS_LIMIT, MIN_PROTOCOL_BASE_FEE},
eip4844::BLOB_TX_MIN_BLOB_GASPRICE, eip4844::BLOB_TX_MIN_BLOB_GASPRICE,
Typed2718,
}; };
use alloy_primitives::{Address, TxHash, B256}; use alloy_primitives::{Address, TxHash, B256};
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
@ -571,7 +572,7 @@ impl<T: TransactionOrdering> TxPool<T> {
let mut eip7702_count = 0; let mut eip7702_count = 0;
for tx in self.all_transactions.transactions_iter() { for tx in self.all_transactions.transactions_iter() {
match tx.transaction.tx_type() { match tx.transaction.ty() {
LEGACY_TX_TYPE_ID => legacy_count += 1, LEGACY_TX_TYPE_ID => legacy_count += 1,
EIP2930_TX_TYPE_ID => eip2930_count += 1, EIP2930_TX_TYPE_ID => eip2930_count += 1,
EIP1559_TX_TYPE_ID => eip1559_count += 1, EIP1559_TX_TYPE_ID => eip1559_count += 1,
@ -1978,6 +1979,7 @@ mod tests {
traits::TransactionOrigin, traits::TransactionOrigin,
SubPoolLimit, SubPoolLimit,
}; };
use alloy_consensus::Transaction;
use alloy_primitives::address; use alloy_primitives::address;
use reth_primitives::TxType; use reth_primitives::TxType;

View File

@ -218,6 +218,8 @@ pub enum MockTransaction {
input: Bytes, input: Bytes,
/// The sidecar information for the transaction. /// The sidecar information for the transaction.
sidecar: BlobTransactionSidecar, sidecar: BlobTransactionSidecar,
/// The blob versioned hashes for the transaction.
blob_versioned_hashes: Vec<B256>,
/// The size of the transaction, returned in the implementation of [`PoolTransaction`]. /// The size of the transaction, returned in the implementation of [`PoolTransaction`].
size: usize, size: usize,
/// The cost of the transaction, returned in the implementation of [`PoolTransaction`]. /// The cost of the transaction, returned in the implementation of [`PoolTransaction`].
@ -359,6 +361,7 @@ impl MockTransaction {
input: Bytes::new(), input: Bytes::new(),
access_list: Default::default(), access_list: Default::default(),
sidecar: Default::default(), sidecar: Default::default(),
blob_versioned_hashes: Default::default(),
size: Default::default(), size: Default::default(),
cost: U256::ZERO, cost: U256::ZERO,
} }
@ -367,7 +370,10 @@ impl MockTransaction {
/// Returns a new EIP4844 transaction with a provided sidecar /// Returns a new EIP4844 transaction with a provided sidecar
pub fn eip4844_with_sidecar(sidecar: BlobTransactionSidecar) -> Self { pub fn eip4844_with_sidecar(sidecar: BlobTransactionSidecar) -> Self {
let mut transaction = Self::eip4844(); let mut transaction = Self::eip4844();
if let Self::Eip4844 { sidecar: ref mut existing_sidecar, .. } = &mut transaction { if let Self::Eip4844 { sidecar: existing_sidecar, blob_versioned_hashes, .. } =
&mut transaction
{
*blob_versioned_hashes = sidecar.versioned_hashes().collect();
*existing_sidecar = sidecar; *existing_sidecar = sidecar;
} }
transaction transaction
@ -702,10 +708,6 @@ impl PoolTransaction for MockTransaction {
self.get_sender() self.get_sender()
} }
fn nonce(&self) -> u64 {
*self.get_nonce()
}
// Having `get_cost` from `make_setters_getters` would be cleaner but we didn't // Having `get_cost` from `make_setters_getters` would be cleaner but we didn't
// want to also generate the error-prone cost setters. For now cost should be // want to also generate the error-prone cost setters. For now cost should be
// correct at construction and auto-updated per field update via `update_cost`, // correct at construction and auto-updated per field update via `update_cost`,
@ -720,10 +722,56 @@ impl PoolTransaction for MockTransaction {
} }
} }
/// Returns the encoded length of the transaction.
fn encoded_length(&self) -> usize {
self.size()
}
}
impl InMemorySize for MockTransaction {
fn size(&self) -> usize {
*self.get_size()
}
}
impl Typed2718 for MockTransaction {
fn ty(&self) -> u8 {
match self {
Self::Legacy { .. } => TxType::Legacy.into(),
Self::Eip1559 { .. } => TxType::Eip1559.into(),
Self::Eip4844 { .. } => TxType::Eip4844.into(),
Self::Eip2930 { .. } => TxType::Eip2930.into(),
Self::Eip7702 { .. } => TxType::Eip7702.into(),
}
}
}
impl alloy_consensus::Transaction for MockTransaction {
fn chain_id(&self) -> Option<u64> {
match self {
Self::Legacy { chain_id, .. } => *chain_id,
Self::Eip1559 { chain_id, .. } |
Self::Eip4844 { chain_id, .. } |
Self::Eip2930 { chain_id, .. } |
Self::Eip7702 { chain_id, .. } => Some(*chain_id),
}
}
fn nonce(&self) -> u64 {
*self.get_nonce()
}
fn gas_limit(&self) -> u64 { fn gas_limit(&self) -> u64 {
*self.get_gas_limit() *self.get_gas_limit()
} }
fn gas_price(&self) -> Option<u128> {
match self {
Self::Legacy { gas_price, .. } | Self::Eip2930 { gas_price, .. } => Some(*gas_price),
_ => None,
}
}
fn max_fee_per_gas(&self) -> u128 { fn max_fee_per_gas(&self) -> u128 {
match self { match self {
Self::Legacy { gas_price, .. } | Self::Eip2930 { gas_price, .. } => *gas_price, Self::Legacy { gas_price, .. } | Self::Eip2930 { gas_price, .. } => *gas_price,
@ -733,16 +781,6 @@ impl PoolTransaction for MockTransaction {
} }
} }
fn access_list(&self) -> Option<&AccessList> {
match self {
Self::Legacy { .. } => None,
Self::Eip1559 { access_list: accesslist, .. } |
Self::Eip4844 { access_list: accesslist, .. } |
Self::Eip2930 { access_list: accesslist, .. } |
Self::Eip7702 { access_list: accesslist, .. } => Some(accesslist),
}
}
fn max_priority_fee_per_gas(&self) -> Option<u128> { fn max_priority_fee_per_gas(&self) -> Option<u128> {
match self { match self {
Self::Legacy { .. } | Self::Eip2930 { .. } => None, Self::Legacy { .. } | Self::Eip2930 { .. } => None,
@ -759,33 +797,6 @@ impl PoolTransaction for MockTransaction {
} }
} }
/// Calculates the effective tip per gas given a base fee.
fn effective_tip_per_gas(&self, base_fee: u64) -> Option<u128> {
// Convert base_fee to u128 for precision in calculations
let base_fee = base_fee as u128;
// Retrieve the maximum fee per gas
let max_fee_per_gas = self.max_fee_per_gas();
// If the maximum fee per gas is less than the base fee, return None
if max_fee_per_gas < base_fee {
return None
}
// Calculate the fee by subtracting the base fee from the maximum fee per gas
let fee = max_fee_per_gas - base_fee;
// If the maximum priority fee per gas is available, return the minimum of fee and priority
// fee
if let Some(priority_fee) = self.max_priority_fee_per_gas() {
return Some(fee.min(priority_fee))
}
// Otherwise, return the calculated fee
Some(fee)
}
/// Returns the priority fee or gas price based on the transaction type.
fn priority_fee_or_price(&self) -> u128 { fn priority_fee_or_price(&self) -> u128 {
match self { match self {
Self::Legacy { gas_price, .. } | Self::Eip2930 { gas_price, .. } => *gas_price, Self::Legacy { gas_price, .. } | Self::Eip2930 { gas_price, .. } => *gas_price,
@ -795,7 +806,28 @@ impl PoolTransaction for MockTransaction {
} }
} }
/// Returns the transaction kind associated with the transaction. fn effective_gas_price(&self, base_fee: Option<u64>) -> u128 {
base_fee.map_or(self.max_fee_per_gas(), |base_fee| {
// if the tip is greater than the max priority fee per gas, set it to the max
// priority fee per gas + base fee
let tip = self.max_fee_per_gas().saturating_sub(base_fee as u128);
if let Some(max_tip) = self.max_priority_fee_per_gas() {
if tip > max_tip {
max_tip + base_fee as u128
} else {
// otherwise return the max fee per gas
self.max_fee_per_gas()
}
} else {
self.max_fee_per_gas()
}
})
}
fn is_dynamic_fee(&self) -> bool {
!matches!(self, Self::Legacy { .. } | Self::Eip2930 { .. })
}
fn kind(&self) -> TxKind { fn kind(&self) -> TxKind {
match self { match self {
Self::Legacy { to, .. } | Self::Eip1559 { to, .. } | Self::Eip2930 { to, .. } => *to, Self::Legacy { to, .. } | Self::Eip1559 { to, .. } | Self::Eip2930 { to, .. } => *to,
@ -803,7 +835,6 @@ impl PoolTransaction for MockTransaction {
} }
} }
/// Returns true if the transaction is a contract creation.
fn is_create(&self) -> bool { fn is_create(&self) -> bool {
match self { match self {
Self::Legacy { to, .. } | Self::Eip1559 { to, .. } | Self::Eip2930 { to, .. } => { Self::Legacy { to, .. } | Self::Eip1559 { to, .. } | Self::Eip2930 { to, .. } => {
@ -813,40 +844,41 @@ impl PoolTransaction for MockTransaction {
} }
} }
/// Returns the input data associated with the transaction. fn value(&self) -> U256 {
fn input(&self) -> &[u8] { match self {
Self::Legacy { value, .. } |
Self::Eip1559 { value, .. } |
Self::Eip2930 { value, .. } |
Self::Eip4844 { value, .. } |
Self::Eip7702 { value, .. } => *value,
}
}
fn input(&self) -> &Bytes {
self.get_input() self.get_input()
} }
/// Returns the size of the transaction. fn access_list(&self) -> Option<&AccessList> {
fn size(&self) -> usize {
*self.get_size()
}
/// Returns the transaction type as a byte identifier.
fn tx_type(&self) -> u8 {
match self { match self {
Self::Legacy { .. } => TxType::Legacy.into(), Self::Legacy { .. } => None,
Self::Eip1559 { .. } => TxType::Eip1559.into(), Self::Eip1559 { access_list: accesslist, .. } |
Self::Eip4844 { .. } => TxType::Eip4844.into(), Self::Eip4844 { access_list: accesslist, .. } |
Self::Eip2930 { .. } => TxType::Eip2930.into(), Self::Eip2930 { access_list: accesslist, .. } |
Self::Eip7702 { .. } => TxType::Eip7702.into(), Self::Eip7702 { access_list: accesslist, .. } => Some(accesslist),
} }
} }
/// Returns the encoded length of the transaction. fn blob_versioned_hashes(&self) -> Option<&[B256]> {
fn encoded_length(&self) -> usize {
self.size()
}
/// Returns the chain ID associated with the transaction.
fn chain_id(&self) -> Option<u64> {
match self { match self {
Self::Legacy { chain_id, .. } => *chain_id, Self::Eip4844 { blob_versioned_hashes, .. } => Some(blob_versioned_hashes),
Self::Eip1559 { chain_id, .. } | _ => None,
Self::Eip4844 { chain_id, .. } | }
Self::Eip2930 { chain_id, .. } | }
Self::Eip7702 { chain_id, .. } => Some(*chain_id),
fn authorization_list(&self) -> Option<&[SignedAuthorization]> {
match self {
Self::Eip7702 { authorization_list, .. } => Some(authorization_list),
_ => None,
} }
} }
} }
@ -859,13 +891,6 @@ impl EthPoolTransaction for MockTransaction {
} }
} }
fn blob_count(&self) -> usize {
match self {
Self::Eip4844 { sidecar, .. } => sidecar.blobs.len(),
_ => 0,
}
}
fn try_into_pooled_eip4844( fn try_into_pooled_eip4844(
self, self,
sidecar: Arc<BlobTransactionSidecar>, sidecar: Arc<BlobTransactionSidecar>,
@ -897,10 +922,6 @@ impl EthPoolTransaction for MockTransaction {
_ => Err(BlobTransactionValidationError::NotBlobTransaction(self.tx_type())), _ => Err(BlobTransactionValidationError::NotBlobTransaction(self.tx_type())),
} }
} }
fn authorization_count(&self) -> usize {
0
}
} }
impl TryFrom<Recovered<TransactionSigned>> for MockTransaction { impl TryFrom<Recovered<TransactionSigned>> for MockTransaction {
@ -1009,6 +1030,7 @@ impl TryFrom<Recovered<TransactionSigned>> for MockTransaction {
input, input,
access_list, access_list,
sidecar: BlobTransactionSidecar::default(), sidecar: BlobTransactionSidecar::default(),
blob_versioned_hashes: Default::default(),
size, size,
cost: U256::from(gas_limit) * U256::from(max_fee_per_gas) + value, cost: U256::from(gas_limit) * U256::from(max_fee_per_gas) + value,
}), }),
@ -1202,7 +1224,7 @@ impl MockTransactionFactory {
/// Generates a transaction ID for the given [`MockTransaction`]. /// Generates a transaction ID for the given [`MockTransaction`].
pub fn tx_id(&mut self, tx: &MockTransaction) -> TransactionId { pub fn tx_id(&mut self, tx: &MockTransaction) -> TransactionId {
let sender = self.ids.sender_id_or_create(tx.sender()); let sender = self.ids.sender_id_or_create(tx.sender());
TransactionId::new(sender, tx.nonce()) TransactionId::new(sender, *tx.get_nonce())
} }
/// Validates a [`MockTransaction`] and returns a [`MockValidTx`]. /// Validates a [`MockTransaction`] and returns a [`MockValidTx`].

View File

@ -7,12 +7,13 @@ use crate::{
}; };
use alloy_consensus::{ use alloy_consensus::{
constants::{EIP1559_TX_TYPE_ID, EIP4844_TX_TYPE_ID, EIP7702_TX_TYPE_ID}, constants::{EIP1559_TX_TYPE_ID, EIP4844_TX_TYPE_ID, EIP7702_TX_TYPE_ID},
BlockHeader, Signed, Transaction as _, Typed2718, BlockHeader, Signed, Typed2718,
}; };
use alloy_eips::{ use alloy_eips::{
eip2718::Encodable2718, eip2718::Encodable2718,
eip2930::AccessList, eip2930::AccessList,
eip4844::{BlobAndProofV1, BlobTransactionSidecar, BlobTransactionValidationError}, eip4844::{BlobAndProofV1, BlobTransactionSidecar, BlobTransactionValidationError},
eip7702::SignedAuthorization,
}; };
use alloy_primitives::{Address, TxHash, TxKind, B256, U256}; use alloy_primitives::{Address, TxHash, TxKind, B256, U256};
use futures_util::{ready, Stream}; use futures_util::{ready, Stream};
@ -21,9 +22,9 @@ use reth_execution_types::ChangedAccount;
use reth_primitives::{ use reth_primitives::{
kzg::KzgSettings, kzg::KzgSettings,
transaction::{SignedTransactionIntoRecoveredExt, TryFromRecoveredTransactionError}, transaction::{SignedTransactionIntoRecoveredExt, TryFromRecoveredTransactionError},
PooledTransaction, Recovered, SealedBlock, Transaction, TransactionSigned, PooledTransaction, Recovered, SealedBlock, TransactionSigned,
}; };
use reth_primitives_traits::{Block, SignedTransaction}; use reth_primitives_traits::{Block, InMemorySize, SignedTransaction};
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{ use std::{
@ -964,7 +965,9 @@ impl BestTransactionsAttributes {
/// sidecar when they are gossiped around the network. It is expected that the `Consensus` format is /// sidecar when they are gossiped around the network. It is expected that the `Consensus` format is
/// a subset of the `Pooled` format. /// a subset of the `Pooled` format.
pub trait PoolTransaction: pub trait PoolTransaction:
fmt::Debug alloy_consensus::Transaction
+ InMemorySize
+ fmt::Debug
+ Send + Send
+ Sync + Sync
+ Clone + Clone
@ -1029,9 +1032,6 @@ pub trait PoolTransaction:
/// Reference to the Sender of the transaction. /// Reference to the Sender of the transaction.
fn sender_ref(&self) -> &Address; fn sender_ref(&self) -> &Address;
/// Returns the nonce for this transaction.
fn nonce(&self) -> u64;
/// Returns the cost that this transaction is allowed to consume: /// Returns the cost that this transaction is allowed to consume:
/// ///
/// For EIP-1559 transactions: `max_fee_per_gas * gas_limit + tx_value`. /// For EIP-1559 transactions: `max_fee_per_gas * gas_limit + tx_value`.
@ -1040,87 +1040,11 @@ pub trait PoolTransaction:
/// max_blob_fee_per_gas * blob_gas_used`. /// max_blob_fee_per_gas * blob_gas_used`.
fn cost(&self) -> &U256; fn cost(&self) -> &U256;
/// Amount of gas that should be used in executing this transaction. This is paid up-front.
fn gas_limit(&self) -> u64;
/// Returns the EIP-1559 the maximum fee per gas the caller is willing to pay.
///
/// For legacy transactions this is `gas_price`.
///
/// This is also commonly referred to as the "Gas Fee Cap" (`GasFeeCap`).
fn max_fee_per_gas(&self) -> u128;
/// Returns the `access_list` for the particular transaction type.
/// For Legacy transactions, returns default.
fn access_list(&self) -> Option<&AccessList>;
/// Returns the EIP-1559 Priority fee the caller is paying to the block author.
///
/// This will return `None` for non-EIP1559 transactions
fn max_priority_fee_per_gas(&self) -> Option<u128>;
/// Returns the EIP-4844 max fee per data gas
///
/// This will return `None` for non-EIP4844 transactions
fn max_fee_per_blob_gas(&self) -> Option<u128>;
/// Returns the effective tip for this transaction.
///
/// For EIP-1559 transactions: `min(max_fee_per_gas - base_fee, max_priority_fee_per_gas)`.
/// For legacy transactions: `gas_price - base_fee`.
fn effective_tip_per_gas(&self, base_fee: u64) -> Option<u128>;
/// Returns the max priority fee per gas if the transaction is an EIP-1559 transaction, and
/// otherwise returns the gas price.
fn priority_fee_or_price(&self) -> u128;
/// Returns the transaction's [`TxKind`], which is the address of the recipient or
/// [`TxKind::Create`] if the transaction is a contract creation.
fn kind(&self) -> TxKind;
/// Returns true if the transaction is a contract creation.
/// We don't provide a default implementation via `kind` as it copies the 21-byte
/// [`TxKind`] for this simple check. A proper implementation shouldn't allocate.
fn is_create(&self) -> bool;
/// Returns the recipient of the transaction if it is not a [`TxKind::Create`]
/// transaction.
fn to(&self) -> Option<Address> {
self.kind().to().copied()
}
/// Returns the input data of this transaction.
fn input(&self) -> &[u8];
/// Returns a measurement of the heap usage of this type and all its internals.
fn size(&self) -> usize;
/// Returns the transaction type
fn tx_type(&self) -> u8;
/// Returns true if the transaction is an EIP-1559 transaction.
fn is_eip1559(&self) -> bool {
self.tx_type() == EIP1559_TX_TYPE_ID
}
/// Returns true if the transaction is an EIP-4844 transaction.
fn is_eip4844(&self) -> bool {
self.tx_type() == EIP4844_TX_TYPE_ID
}
/// Returns true if the transaction is an EIP-7702 transaction.
fn is_eip7702(&self) -> bool {
self.tx_type() == EIP7702_TX_TYPE_ID
}
/// Returns the length of the rlp encoded transaction object /// Returns the length of the rlp encoded transaction object
/// ///
/// Note: Implementations should cache this value. /// Note: Implementations should cache this value.
fn encoded_length(&self) -> usize; fn encoded_length(&self) -> usize;
/// Returns `chain_id`
fn chain_id(&self) -> Option<u64>;
/// Ensures that the transaction's code size does not exceed the provided `max_init_code_size`. /// Ensures that the transaction's code size does not exceed the provided `max_init_code_size`.
/// ///
/// This is specifically relevant for contract creation transactions ([`TxKind::Create`]), /// This is specifically relevant for contract creation transactions ([`TxKind::Create`]),
@ -1149,14 +1073,11 @@ pub trait EthPoolTransaction: PoolTransaction {
/// Extracts the blob sidecar from the transaction. /// Extracts the blob sidecar from the transaction.
fn take_blob(&mut self) -> EthBlobTransactionSidecar; fn take_blob(&mut self) -> EthBlobTransactionSidecar;
/// Returns the number of blobs this transaction has.
fn blob_count(&self) -> usize;
/// A specialization for the EIP-4844 transaction type. /// A specialization for the EIP-4844 transaction type.
/// Tries to reattach the blob sidecar to the transaction. /// Tries to reattach the blob sidecar to the transaction.
/// ///
/// This returns an option, but callers should ensure that the transaction is an EIP-4844 /// This returns an option, but callers should ensure that the transaction is an EIP-4844
/// transaction: [`PoolTransaction::is_eip4844`]. /// transaction: [`Typed2718::is_eip4844`].
fn try_into_pooled_eip4844( fn try_into_pooled_eip4844(
self, self,
sidecar: Arc<BlobTransactionSidecar>, sidecar: Arc<BlobTransactionSidecar>,
@ -1176,9 +1097,6 @@ pub trait EthPoolTransaction: PoolTransaction {
blob: &BlobTransactionSidecar, blob: &BlobTransactionSidecar,
settings: &KzgSettings, settings: &KzgSettings,
) -> Result<(), BlobTransactionValidationError>; ) -> Result<(), BlobTransactionValidationError>;
/// Returns the number of authorizations this transaction has.
fn authorization_count(&self) -> usize;
} }
/// The default [`PoolTransaction`] for the [Pool](crate::Pool) for Ethereum. /// The default [`PoolTransaction`] for the [Pool](crate::Pool) for Ethereum.
@ -1300,11 +1218,6 @@ impl PoolTransaction for EthPooledTransaction {
self.transaction.signer_ref() self.transaction.signer_ref()
} }
/// Returns the nonce for this transaction.
fn nonce(&self) -> u64 {
self.transaction.nonce()
}
/// Returns the cost that this transaction is allowed to consume: /// Returns the cost that this transaction is allowed to consume:
/// ///
/// For EIP-1559 transactions: `max_fee_per_gas * gas_limit + tx_value`. /// For EIP-1559 transactions: `max_fee_per_gas * gas_limit + tx_value`.
@ -1315,82 +1228,91 @@ impl PoolTransaction for EthPooledTransaction {
&self.cost &self.cost
} }
/// Amount of gas that should be used in executing this transaction. This is paid up-front. /// Returns the length of the rlp encoded object
fn encoded_length(&self) -> usize {
self.encoded_length
}
}
impl<T: Typed2718> Typed2718 for EthPooledTransaction<T> {
fn ty(&self) -> u8 {
self.transaction.ty()
}
}
impl<T: InMemorySize> InMemorySize for EthPooledTransaction<T> {
fn size(&self) -> usize {
self.transaction.size()
}
}
impl<T: alloy_consensus::Transaction> alloy_consensus::Transaction for EthPooledTransaction<T> {
fn chain_id(&self) -> Option<alloy_primitives::ChainId> {
self.transaction.chain_id()
}
fn nonce(&self) -> u64 {
self.transaction.nonce()
}
fn gas_limit(&self) -> u64 { fn gas_limit(&self) -> u64 {
self.transaction.gas_limit() self.transaction.gas_limit()
} }
/// Returns the EIP-1559 Max base fee the caller is willing to pay. fn gas_price(&self) -> Option<u128> {
/// self.transaction.gas_price()
/// For legacy transactions this is `gas_price`. }
///
/// This is also commonly referred to as the "Gas Fee Cap" (`GasFeeCap`).
fn max_fee_per_gas(&self) -> u128 { fn max_fee_per_gas(&self) -> u128 {
self.transaction.transaction().max_fee_per_gas() self.transaction.max_fee_per_gas()
} }
fn access_list(&self) -> Option<&AccessList> {
self.transaction.access_list()
}
/// Returns the EIP-1559 Priority fee the caller is paying to the block author.
///
/// This will return `None` for non-EIP1559 transactions
fn max_priority_fee_per_gas(&self) -> Option<u128> { fn max_priority_fee_per_gas(&self) -> Option<u128> {
self.transaction.transaction().max_priority_fee_per_gas() self.transaction.max_priority_fee_per_gas()
} }
fn max_fee_per_blob_gas(&self) -> Option<u128> { fn max_fee_per_blob_gas(&self) -> Option<u128> {
self.transaction.max_fee_per_blob_gas() self.transaction.max_fee_per_blob_gas()
} }
/// Returns the effective tip for this transaction.
///
/// For EIP-1559 transactions: `min(max_fee_per_gas - base_fee, max_priority_fee_per_gas)`.
/// For legacy transactions: `gas_price - base_fee`.
fn effective_tip_per_gas(&self, base_fee: u64) -> Option<u128> {
self.transaction.effective_tip_per_gas(base_fee)
}
/// Returns the max priority fee per gas if the transaction is an EIP-1559 transaction, and
/// otherwise returns the gas price.
fn priority_fee_or_price(&self) -> u128 { fn priority_fee_or_price(&self) -> u128 {
self.transaction.priority_fee_or_price() self.transaction.priority_fee_or_price()
} }
/// Returns the transaction's [`TxKind`], which is the address of the recipient or fn effective_gas_price(&self, base_fee: Option<u64>) -> u128 {
/// [`TxKind::Create`] if the transaction is a contract creation. self.transaction.effective_gas_price(base_fee)
}
fn is_dynamic_fee(&self) -> bool {
self.transaction.is_dynamic_fee()
}
fn kind(&self) -> TxKind { fn kind(&self) -> TxKind {
self.transaction.kind() self.transaction.kind()
} }
/// Returns true if the transaction is a contract creation.
fn is_create(&self) -> bool { fn is_create(&self) -> bool {
self.transaction.is_create() self.transaction.is_create()
} }
fn input(&self) -> &[u8] { fn value(&self) -> U256 {
self.transaction.value()
}
fn input(&self) -> &revm_primitives::Bytes {
self.transaction.input() self.transaction.input()
} }
/// Returns a measurement of the heap usage of this type and all its internals. fn access_list(&self) -> Option<&AccessList> {
fn size(&self) -> usize { self.transaction.access_list()
self.transaction.transaction().input().len()
} }
/// Returns the transaction type fn blob_versioned_hashes(&self) -> Option<&[B256]> {
fn tx_type(&self) -> u8 { self.transaction.blob_versioned_hashes()
self.transaction.ty()
} }
/// Returns the length of the rlp encoded object fn authorization_list(&self) -> Option<&[SignedAuthorization]> {
fn encoded_length(&self) -> usize { self.transaction.authorization_list()
self.encoded_length
}
/// Returns `chain_id`
fn chain_id(&self) -> Option<u64> {
self.transaction.chain_id()
} }
} }
@ -1403,13 +1325,6 @@ impl EthPoolTransaction for EthPooledTransaction {
} }
} }
fn blob_count(&self) -> usize {
match self.transaction.transaction() {
Transaction::Eip4844(tx) => tx.blob_versioned_hashes.len(),
_ => 0,
}
}
fn try_into_pooled_eip4844( fn try_into_pooled_eip4844(
self, self,
sidecar: Arc<BlobTransactionSidecar>, sidecar: Arc<BlobTransactionSidecar>,
@ -1438,15 +1353,8 @@ impl EthPoolTransaction for EthPooledTransaction {
settings: &KzgSettings, settings: &KzgSettings,
) -> Result<(), BlobTransactionValidationError> { ) -> Result<(), BlobTransactionValidationError> {
match self.transaction.transaction() { match self.transaction.transaction() {
Transaction::Eip4844(tx) => tx.validate_blob(sidecar, settings), reth_primitives::Transaction::Eip4844(tx) => tx.validate_blob(sidecar, settings),
_ => Err(BlobTransactionValidationError::NotBlobTransaction(self.tx_type())), _ => Err(BlobTransactionValidationError::NotBlobTransaction(self.ty())),
}
}
fn authorization_count(&self) -> usize {
match self.transaction.transaction() {
Transaction::Eip7702(tx) => tx.authorization_list.len(),
_ => 0,
} }
} }
} }
@ -1640,7 +1548,7 @@ mod tests {
use alloy_consensus::{TxEip1559, TxEip2930, TxEip4844, TxEip7702, TxLegacy}; use alloy_consensus::{TxEip1559, TxEip2930, TxEip4844, TxEip7702, TxLegacy};
use alloy_eips::eip4844::DATA_GAS_PER_BLOB; use alloy_eips::eip4844::DATA_GAS_PER_BLOB;
use alloy_primitives::PrimitiveSignature as Signature; use alloy_primitives::PrimitiveSignature as Signature;
use reth_primitives::TransactionSigned; use reth_primitives::{Transaction, TransactionSigned};
#[test] #[test]
fn test_pool_size_invariants() { fn test_pool_size_invariants() {

View File

@ -190,7 +190,7 @@ where
maybe_state: &mut Option<Box<dyn StateProvider>>, maybe_state: &mut Option<Box<dyn StateProvider>>,
) -> TransactionValidationOutcome<Tx> { ) -> TransactionValidationOutcome<Tx> {
// Checks for tx_type // Checks for tx_type
match transaction.tx_type() { match transaction.ty() {
LEGACY_TX_TYPE_ID => { LEGACY_TX_TYPE_ID => {
// Accept legacy transactions // Accept legacy transactions
} }
@ -307,7 +307,7 @@ where
) )
} }
if transaction.authorization_count() == 0 { if transaction.authorization_list().is_none_or(|l| l.is_empty()) {
return TransactionValidationOutcome::Invalid( return TransactionValidationOutcome::Invalid(
transaction, transaction,
Eip7702PoolTransactionError::MissingEip7702AuthorizationList.into(), Eip7702PoolTransactionError::MissingEip7702AuthorizationList.into(),
@ -329,7 +329,7 @@ where
) )
} }
let blob_count = transaction.blob_count(); let blob_count = transaction.blob_versioned_hashes().map(|b| b.len()).unwrap_or(0);
if blob_count == 0 { if blob_count == 0 {
// no blobs // no blobs
return TransactionValidationOutcome::Invalid( return TransactionValidationOutcome::Invalid(
@ -865,7 +865,7 @@ pub fn ensure_intrinsic_gas<T: EthPoolTransaction>(
transaction.input(), transaction.input(),
transaction.is_create(), transaction.is_create(),
transaction.access_list().map(|list| list.0.as_slice()).unwrap_or(&[]), transaction.access_list().map(|list| list.0.as_slice()).unwrap_or(&[]),
transaction.authorization_count() as u64, transaction.authorization_list().map(|l| l.len()).unwrap_or(0) as u64,
); );
let gas_limit = transaction.gas_limit(); let gas_limit = transaction.gas_limit();
@ -883,6 +883,7 @@ mod tests {
blobstore::InMemoryBlobStore, error::PoolErrorKind, traits::PoolTransaction, blobstore::InMemoryBlobStore, error::PoolErrorKind, traits::PoolTransaction,
CoinbaseTipOrdering, EthPooledTransaction, Pool, TransactionPool, CoinbaseTipOrdering, EthPooledTransaction, Pool, TransactionPool,
}; };
use alloy_consensus::Transaction;
use alloy_eips::eip2718::Decodable2718; use alloy_eips::eip2718::Decodable2718;
use alloy_primitives::{hex, U256}; use alloy_primitives::{hex, U256};
use reth_primitives::{transaction::SignedTransactionIntoRecoveredExt, PooledTransaction}; use reth_primitives::{transaction::SignedTransactionIntoRecoveredExt, PooledTransaction};

View File

@ -282,7 +282,7 @@ impl<T: PoolTransaction> ValidPoolTransaction<T> {
/// Returns the type identifier of the transaction /// Returns the type identifier of the transaction
pub fn tx_type(&self) -> u8 { pub fn tx_type(&self) -> u8 {
self.transaction.tx_type() self.transaction.ty()
} }
/// Returns the address of the sender /// Returns the address of the sender

View File

@ -1,5 +1,6 @@
//! Transaction pool eviction tests. //! Transaction pool eviction tests.
use alloy_consensus::Transaction;
use alloy_eips::eip1559::{ETHEREUM_BLOCK_GAS_LIMIT, MIN_PROTOCOL_BASE_FEE}; use alloy_eips::eip1559::{ETHEREUM_BLOCK_GAS_LIMIT, MIN_PROTOCOL_BASE_FEE};
use alloy_primitives::{Address, B256}; use alloy_primitives::{Address, B256};
use rand::distributions::Uniform; use rand::distributions::Uniform;
@ -8,8 +9,7 @@ use reth_transaction_pool::{
test_utils::{ test_utils::{
MockFeeRange, MockTransactionDistribution, MockTransactionRatio, TestPool, TestPoolBuilder, MockFeeRange, MockTransactionDistribution, MockTransactionRatio, TestPool, TestPoolBuilder,
}, },
BlockInfo, PoolConfig, PoolTransaction, SubPoolLimit, TransactionOrigin, TransactionPool, BlockInfo, PoolConfig, SubPoolLimit, TransactionOrigin, TransactionPool, TransactionPoolExt,
TransactionPoolExt,
}; };
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]

View File

@ -6,6 +6,7 @@ edition.workspace = true
license.workspace = true license.workspace = true
[dependencies] [dependencies]
alloy-consensus.workspace = true
reth-provider = { workspace = true, features = ["test-utils"] } reth-provider = { workspace = true, features = ["test-utils"] }
eyre.workspace = true eyre.workspace = true
tokio.workspace = true tokio.workspace = true

View File

@ -7,6 +7,7 @@
//! cargo run --release -p network-txpool -- node //! cargo run --release -p network-txpool -- node
//! ``` //! ```
use alloy_consensus::Transaction;
use reth_network::{config::rng_secret_key, EthNetworkPrimitives, NetworkConfig, NetworkManager}; use reth_network::{config::rng_secret_key, EthNetworkPrimitives, NetworkConfig, NetworkManager};
use reth_provider::test_utils::NoopProvider; use reth_provider::test_utils::NoopProvider;
use reth_transaction_pool::{ use reth_transaction_pool::{