diff --git a/crates/node/builder/src/components/pool.rs b/crates/node/builder/src/components/pool.rs index 2d431831ee3..f3e5bad4b26 100644 --- a/crates/node/builder/src/components/pool.rs +++ b/crates/node/builder/src/components/pool.rs @@ -128,7 +128,7 @@ impl<'a, Node: FullNodeTypes, V> TxPoolBuilder<'a, Node, V> { impl<'a, Node: FullNodeTypes, V> TxPoolBuilder<'a, Node, TransactionValidationTaskExecutor> where - V: TransactionValidator + Clone + 'static, + V: TransactionValidator + 'static, V::Transaction: PoolTransaction> + reth_transaction_pool::EthPoolTransaction, { diff --git a/crates/optimism/txpool/src/validator.rs b/crates/optimism/txpool/src/validator.rs index 6c986e9498f..631c4255942 100644 --- a/crates/optimism/txpool/src/validator.rs +++ b/crates/optimism/txpool/src/validator.rs @@ -43,7 +43,7 @@ impl OpL1BlockInfo { #[derive(Debug, Clone)] pub struct OpTransactionValidator { /// The type that performs the actual validation. - inner: EthTransactionValidator, + inner: Arc>, /// Additional block info required for validation. block_info: Arc, /// If true, ensure that the transaction's sender has enough balance to cover the L1 gas fee @@ -118,7 +118,7 @@ where block_info: OpL1BlockInfo, ) -> Self { Self { - inner, + inner: Arc::new(inner), block_info: Arc::new(block_info), require_l1_data_gas_fee: true, supervisor_client: None, diff --git a/crates/transaction-pool/src/maintain.rs b/crates/transaction-pool/src/maintain.rs index 300ff6eb410..9f48590d9d9 100644 --- a/crates/transaction-pool/src/maintain.rs +++ b/crates/transaction-pool/src/maintain.rs @@ -800,7 +800,7 @@ mod tests { let validator = EthTransactionValidatorBuilder::new(provider).build(blob_store.clone()); let txpool = Pool::new( - validator.clone(), + validator, CoinbaseTipOrdering::default(), blob_store.clone(), Default::default(), diff --git a/crates/transaction-pool/src/validate/eth.rs b/crates/transaction-pool/src/validate/eth.rs index 2551e7b9a2e..9e657ba1ed0 100644 --- a/crates/transaction-pool/src/validate/eth.rs +++ b/crates/transaction-pool/src/validate/eth.rs @@ -42,12 +42,56 @@ use std::{ }; use tokio::sync::Mutex; -/// Validator for Ethereum transactions. -/// It is a [`TransactionValidator`] implementation that validates ethereum transaction. -#[derive(Debug, Clone)] +/// A [`TransactionValidator`] implementation that validates ethereum transaction. +/// +/// It supports all known ethereum transaction types: +/// - Legacy +/// - EIP-2718 +/// - EIP-1559 +/// - EIP-4844 +/// - EIP-7702 +/// +/// And enforces additional constraints such as: +/// - Maximum transaction size +/// - Maximum gas limit +/// +/// And adheres to the configured [`LocalTransactionConfig`]. +#[derive(Debug)] pub struct EthTransactionValidator { - /// The type that performs the actual validation. - inner: Arc>, + /// This type fetches account info from the db + client: Client, + /// Blobstore used for fetching re-injected blob transactions. + blob_store: Box, + /// tracks activated forks relevant for transaction validation + fork_tracker: ForkTracker, + /// Fork indicator whether we are using EIP-2718 type transactions. + eip2718: bool, + /// Fork indicator whether we are using EIP-1559 type transactions. + eip1559: bool, + /// Fork indicator whether we are using EIP-4844 blob transactions. + eip4844: bool, + /// Fork indicator whether we are using EIP-7702 type transactions. + eip7702: bool, + /// The current max gas limit + block_gas_limit: AtomicU64, + /// The current tx fee cap limit in wei locally submitted into the pool. + tx_fee_cap: Option, + /// Minimum priority fee to enforce for acceptance into the pool. + minimum_priority_fee: Option, + /// Stores the setup and parameters needed for validating KZG proofs. + kzg_settings: EnvKzgSettings, + /// How to handle [`TransactionOrigin::Local`](TransactionOrigin) transactions. + local_transactions_config: LocalTransactionConfig, + /// Maximum size in bytes a single transaction can have in order to be accepted into the pool. + max_tx_input_bytes: usize, + /// Maximum gas limit for individual transactions + max_tx_gas_limit: Option, + /// Disable balance checks during transaction validation + disable_balance_check: bool, + /// Marker for the transaction type + _marker: PhantomData, + /// Metrics for tsx pool validation + validation_metrics: TxPoolValidationMetrics, } impl EthTransactionValidator { @@ -59,65 +103,73 @@ impl EthTransactionValidator { self.client().chain_spec() } + /// Returns the configured chain id + pub fn chain_id(&self) -> u64 + where + Client: ChainSpecProvider, + { + self.client().chain_spec().chain().id() + } + /// Returns the configured client - pub fn client(&self) -> &Client { - &self.inner.client + pub const fn client(&self) -> &Client { + &self.client } /// Returns the tracks activated forks relevant for transaction validation - pub fn fork_tracker(&self) -> &ForkTracker { - &self.inner.fork_tracker + pub const fn fork_tracker(&self) -> &ForkTracker { + &self.fork_tracker } /// Returns if there are EIP-2718 type transactions - pub fn eip2718(&self) -> bool { - self.inner.eip2718 + pub const fn eip2718(&self) -> bool { + self.eip2718 } /// Returns if there are EIP-1559 type transactions - pub fn eip1559(&self) -> bool { - self.inner.eip1559 + pub const fn eip1559(&self) -> bool { + self.eip1559 } /// Returns if there are EIP-4844 blob transactions - pub fn eip4844(&self) -> bool { - self.inner.eip4844 + pub const fn eip4844(&self) -> bool { + self.eip4844 } /// Returns if there are EIP-7702 type transactions - pub fn eip7702(&self) -> bool { - self.inner.eip7702 + pub const fn eip7702(&self) -> bool { + self.eip7702 } /// Returns the current tx fee cap limit in wei locally submitted into the pool - pub fn tx_fee_cap(&self) -> &Option { - &self.inner.tx_fee_cap + pub const fn tx_fee_cap(&self) -> &Option { + &self.tx_fee_cap } /// Returns the minimum priority fee to enforce for acceptance into the pool - pub fn minimum_priority_fee(&self) -> &Option { - &self.inner.minimum_priority_fee + pub const fn minimum_priority_fee(&self) -> &Option { + &self.minimum_priority_fee } /// Returns the setup and parameters needed for validating KZG proofs. - pub fn kzg_settings(&self) -> &EnvKzgSettings { - &self.inner.kzg_settings + pub const fn kzg_settings(&self) -> &EnvKzgSettings { + &self.kzg_settings } /// Returns the config to handle [`TransactionOrigin::Local`](TransactionOrigin) transactions.. - pub fn local_transactions_config(&self) -> &LocalTransactionConfig { - &self.inner.local_transactions_config + pub const fn local_transactions_config(&self) -> &LocalTransactionConfig { + &self.local_transactions_config } /// Returns the maximum size in bytes a single transaction can have in order to be accepted into /// the pool. - pub fn max_tx_input_bytes(&self) -> usize { - self.inner.max_tx_input_bytes + pub const fn max_tx_input_bytes(&self) -> usize { + self.max_tx_input_bytes } /// Returns whether balance checks are disabled for this validator. - pub fn disable_balance_check(&self) -> bool { - self.inner.disable_balance_check + pub const fn disable_balance_check(&self) -> bool { + self.disable_balance_check } } @@ -128,7 +180,7 @@ where { /// Returns the current max gas limit pub fn block_gas_limit(&self) -> u64 { - self.inner.max_gas_limit() + self.max_gas_limit() } /// Validates a single transaction. @@ -139,7 +191,7 @@ where origin: TransactionOrigin, transaction: Tx, ) -> TransactionValidationOutcome { - self.inner.validate_one_with_provider(origin, transaction, &mut None) + self.validate_one_with_provider(origin, transaction, &mut None) } /// Validates a single transaction with the provided state provider. @@ -154,158 +206,7 @@ where transaction: Tx, state: &mut Option>, ) -> TransactionValidationOutcome { - self.inner.validate_one_with_provider(origin, transaction, state) - } - - /// Validates that the sender’s account has valid or no bytecode. - pub fn validate_sender_bytecode( - &self, - transaction: &Tx, - account: &Account, - state: &dyn AccountInfoReader, - ) -> Result, TransactionValidationOutcome> { - self.inner.validate_sender_bytecode(transaction, account, state) - } - - /// Checks if the transaction nonce is valid. - pub fn validate_sender_nonce( - &self, - transaction: &Tx, - account: &Account, - ) -> Result<(), InvalidPoolTransactionError> { - self.inner.validate_sender_nonce(transaction, account) - } - - /// Ensures the sender has sufficient account balance. - pub fn validate_sender_balance( - &self, - transaction: &Tx, - account: &Account, - ) -> Result<(), InvalidPoolTransactionError> { - self.inner.validate_sender_balance(transaction, account) - } - - /// Validates EIP-4844 blob sidecar data and returns the extracted sidecar, if any. - pub fn validate_eip4844( - &self, - transaction: &mut Tx, - ) -> Result, InvalidPoolTransactionError> { - self.inner.validate_eip4844(transaction) - } - - /// Returns the recovered authorities for the given transaction - pub fn recover_authorities(&self, transaction: &Tx) -> std::option::Option> { - self.inner.recover_authorities(transaction) - } -} - -impl TransactionValidator for EthTransactionValidator -where - Client: ChainSpecProvider + StateProviderFactory, - Tx: EthPoolTransaction, -{ - type Transaction = Tx; - - async fn validate_transaction( - &self, - origin: TransactionOrigin, - transaction: Self::Transaction, - ) -> TransactionValidationOutcome { - self.validate_one(origin, transaction) - } - - async fn validate_transactions( - &self, - transactions: Vec<(TransactionOrigin, Self::Transaction)>, - ) -> Vec> { - self.inner.validate_batch(transactions) - } - - async fn validate_transactions_with_origin( - &self, - origin: TransactionOrigin, - transactions: impl IntoIterator + Send, - ) -> Vec> { - self.inner.validate_batch_with_origin(origin, transactions) - } - - fn on_new_head_block(&self, new_tip_block: &SealedBlock) - where - B: Block, - { - self.inner.on_new_head_block(new_tip_block.header()) - } -} - -/// A [`TransactionValidator`] implementation that validates ethereum transaction. -/// -/// It supports all known ethereum transaction types: -/// - Legacy -/// - EIP-2718 -/// - EIP-1559 -/// - EIP-4844 -/// - EIP-7702 -/// -/// And enforces additional constraints such as: -/// - Maximum transaction size -/// - Maximum gas limit -/// -/// And adheres to the configured [`LocalTransactionConfig`]. -#[derive(Debug)] -pub(crate) struct EthTransactionValidatorInner { - /// This type fetches account info from the db - client: Client, - /// Blobstore used for fetching re-injected blob transactions. - blob_store: Box, - /// tracks activated forks relevant for transaction validation - fork_tracker: ForkTracker, - /// Fork indicator whether we are using EIP-2718 type transactions. - eip2718: bool, - /// Fork indicator whether we are using EIP-1559 type transactions. - eip1559: bool, - /// Fork indicator whether we are using EIP-4844 blob transactions. - eip4844: bool, - /// Fork indicator whether we are using EIP-7702 type transactions. - eip7702: bool, - /// The current max gas limit - block_gas_limit: AtomicU64, - /// The current tx fee cap limit in wei locally submitted into the pool. - tx_fee_cap: Option, - /// Minimum priority fee to enforce for acceptance into the pool. - minimum_priority_fee: Option, - /// Stores the setup and parameters needed for validating KZG proofs. - kzg_settings: EnvKzgSettings, - /// How to handle [`TransactionOrigin::Local`](TransactionOrigin) transactions. - local_transactions_config: LocalTransactionConfig, - /// Maximum size in bytes a single transaction can have in order to be accepted into the pool. - max_tx_input_bytes: usize, - /// Maximum gas limit for individual transactions - max_tx_gas_limit: Option, - /// Disable balance checks during transaction validation - disable_balance_check: bool, - /// Marker for the transaction type - _marker: PhantomData, - /// Metrics for tsx pool validation - validation_metrics: TxPoolValidationMetrics, -} - -// === impl EthTransactionValidatorInner === - -impl EthTransactionValidatorInner { - /// Returns the configured chain id - pub(crate) fn chain_id(&self) -> u64 { - self.client.chain_spec().chain().id() - } -} - -impl EthTransactionValidatorInner -where - Client: ChainSpecProvider + StateProviderFactory, - Tx: EthPoolTransaction, -{ - /// Returns the configured chain spec - fn chain_spec(&self) -> Arc { - self.client.chain_spec() + self.validate_one_with_provider(origin, transaction, state) } /// Validates a single transaction using an optional cached state provider. @@ -660,7 +561,7 @@ where } /// Validates that the sender’s account has valid or no bytecode. - fn validate_sender_bytecode( + pub fn validate_sender_bytecode( &self, transaction: &Tx, sender: &Account, @@ -695,7 +596,7 @@ where } /// Checks if the transaction nonce is valid. - fn validate_sender_nonce( + pub fn validate_sender_nonce( &self, transaction: &Tx, sender: &Account, @@ -713,7 +614,7 @@ where } /// Ensures the sender has sufficient account balance. - fn validate_sender_balance( + pub fn validate_sender_balance( &self, transaction: &Tx, sender: &Account, @@ -731,7 +632,7 @@ where } /// Validates EIP-4844 blob sidecar data and returns the extracted sidecar, if any. - fn validate_eip4844( + pub fn validate_eip4844( &self, transaction: &mut Tx, ) -> Result, InvalidPoolTransactionError> { @@ -855,6 +756,44 @@ where } } +impl TransactionValidator for EthTransactionValidator +where + Client: ChainSpecProvider + StateProviderFactory, + Tx: EthPoolTransaction, +{ + type Transaction = Tx; + + async fn validate_transaction( + &self, + origin: TransactionOrigin, + transaction: Self::Transaction, + ) -> TransactionValidationOutcome { + self.validate_one(origin, transaction) + } + + async fn validate_transactions( + &self, + transactions: Vec<(TransactionOrigin, Self::Transaction)>, + ) -> Vec> { + self.validate_batch(transactions) + } + + async fn validate_transactions_with_origin( + &self, + origin: TransactionOrigin, + transactions: impl IntoIterator + Send, + ) -> Vec> { + self.validate_batch_with_origin(origin, transactions) + } + + fn on_new_head_block(&self, new_tip_block: &SealedBlock) + where + B: Block, + { + self.on_new_head_block(new_tip_block.header()) + } +} + /// A builder for [`EthTransactionValidator`] and [`TransactionValidationTaskExecutor`] #[derive(Debug)] pub struct EthTransactionValidatorBuilder { @@ -1145,7 +1084,7 @@ impl EthTransactionValidatorBuilder { max_blob_count: AtomicU64::new(max_blob_count), }; - let inner = EthTransactionValidatorInner { + EthTransactionValidator { client, eip2718, eip1559, @@ -1163,9 +1102,7 @@ impl EthTransactionValidatorBuilder { disable_balance_check, _marker: Default::default(), validation_metrics: TxPoolValidationMetrics::default(), - }; - - EthTransactionValidator { inner: Arc::new(inner) } + } } /// Builds a [`EthTransactionValidator`] and spawns validation tasks via the @@ -1207,7 +1144,7 @@ impl EthTransactionValidatorBuilder { let to_validation_task = Arc::new(Mutex::new(tx)); - TransactionValidationTaskExecutor { validator, to_validation_task } + TransactionValidationTaskExecutor { validator: Arc::new(validator), to_validation_task } } } diff --git a/crates/transaction-pool/src/validate/task.rs b/crates/transaction-pool/src/validate/task.rs index 93f16a585b0..e1a9f79b057 100644 --- a/crates/transaction-pool/src/validate/task.rs +++ b/crates/transaction-pool/src/validate/task.rs @@ -78,14 +78,23 @@ impl ValidationJobSender { /// A [`TransactionValidator`] implementation that validates ethereum transaction. /// This validator is non-blocking, all validation work is done in a separate task. -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct TransactionValidationTaskExecutor { /// The validator that will validate transactions on a separate task. - pub validator: V, + pub validator: Arc, /// The sender half to validation tasks that perform the actual validation. pub to_validation_task: Arc>, } +impl Clone for TransactionValidationTaskExecutor { + fn clone(&self) -> Self { + Self { + validator: self.validator.clone(), + to_validation_task: self.to_validation_task.clone(), + } + } +} + // === impl TransactionValidationTaskExecutor === impl TransactionValidationTaskExecutor<()> { @@ -102,13 +111,13 @@ impl TransactionValidationTaskExecutor { F: FnMut(V) -> T, { TransactionValidationTaskExecutor { - validator: f(self.validator), + validator: Arc::new(f(Arc::into_inner(self.validator).unwrap())), to_validation_task: self.to_validation_task, } } /// Returns the validator. - pub const fn validator(&self) -> &V { + pub fn validator(&self) -> &V { &self.validator } } @@ -156,13 +165,13 @@ impl TransactionValidationTaskExecutor { /// validation tasks. pub fn new(validator: V) -> Self { let (tx, _) = ValidationTask::new(); - Self { validator, to_validation_task: Arc::new(sync::Mutex::new(tx)) } + Self { validator: Arc::new(validator), to_validation_task: Arc::new(sync::Mutex::new(tx)) } } } impl TransactionValidator for TransactionValidationTaskExecutor where - V: TransactionValidator + Clone + 'static, + V: TransactionValidator + 'static, { type Transaction = ::Transaction;