diff --git a/crates/rpc/rpc/src/eth/error.rs b/crates/rpc/rpc/src/eth/error.rs index b3cb1f6302..044f562126 100644 --- a/crates/rpc/rpc/src/eth/error.rs +++ b/crates/rpc/rpc/src/eth/error.rs @@ -470,6 +470,9 @@ pub enum RpcPoolError { /// Custom pool error #[error("{0:?}")] PoolTransactionError(Box), + /// Unable to find the blob for an EIP4844 transaction + #[error("blob not found for EIP4844 transaction")] + MissingEip4844Blob, #[error(transparent)] Other(Box), } @@ -508,6 +511,7 @@ impl From for RpcPoolError { InvalidPoolTransactionError::OversizedData(_, _) => RpcPoolError::OversizedData, InvalidPoolTransactionError::Underpriced => RpcPoolError::Underpriced, InvalidPoolTransactionError::Other(err) => RpcPoolError::PoolTransactionError(err), + InvalidPoolTransactionError::MissingEip4844Blob => RpcPoolError::MissingEip4844Blob, } } } diff --git a/crates/transaction-pool/src/error.rs b/crates/transaction-pool/src/error.rs index 12ccfbebe5..fd2ca0bf7e 100644 --- a/crates/transaction-pool/src/error.rs +++ b/crates/transaction-pool/src/error.rs @@ -138,6 +138,9 @@ pub enum InvalidPoolTransactionError { /// Thrown if the transaction's fee is below the minimum fee #[error("transaction underpriced")] Underpriced, + /// Thrown if we're unable to find the blob for a transaction that was previously extracted + #[error("blob not found for EIP4844 transaction")] + MissingEip4844Blob, /// Any other error that occurred while inserting/validating that is transaction specific #[error("{0:?}")] Other(Box), @@ -195,6 +198,11 @@ impl InvalidPoolTransactionError { false } InvalidPoolTransactionError::Other(err) => err.is_bad_transaction(), + InvalidPoolTransactionError::MissingEip4844Blob => { + // this is only reachable when blob transactions are reinjected and we're unable to + // find the previously extracted blob + false + } } } } diff --git a/crates/transaction-pool/src/lib.rs b/crates/transaction-pool/src/lib.rs index 5702776563..42f19157a8 100644 --- a/crates/transaction-pool/src/lib.rs +++ b/crates/transaction-pool/src/lib.rs @@ -180,9 +180,10 @@ pub use crate::{ }, traits::{ AllPoolTransactions, BestTransactions, BlockInfo, CanonicalStateUpdate, ChangedAccount, - EthPooledTransaction, GetPooledTransactionLimit, NewTransactionEvent, - PendingTransactionListenerKind, PoolSize, PoolTransaction, PropagateKind, - PropagatedTransactions, TransactionOrigin, TransactionPool, TransactionPoolExt, + EthBlobTransactionSidecar, EthPoolTransaction, EthPooledTransaction, + GetPooledTransactionLimit, NewTransactionEvent, PendingTransactionListenerKind, PoolSize, + PoolTransaction, PropagateKind, PropagatedTransactions, TransactionOrigin, TransactionPool, + TransactionPoolExt, }, validate::{ EthTransactionValidator, TransactionValidationOutcome, TransactionValidationTaskExecutor, diff --git a/crates/transaction-pool/src/traits.rs b/crates/transaction-pool/src/traits.rs index 2da408b77c..7540a6340f 100644 --- a/crates/transaction-pool/src/traits.rs +++ b/crates/transaction-pool/src/traits.rs @@ -640,6 +640,13 @@ pub trait PoolTransaction: fn chain_id(&self) -> Option; } +/// An extension trait that provides additional interfaces for the +/// [EthTransactionValidator](crate::EthTransactionValidator). +pub trait EthPoolTransaction: PoolTransaction { + /// Extracts the blob sidecar from the transaction. + fn take_blob(&mut self) -> EthBlobTransactionSidecar; +} + /// The default [PoolTransaction] for the [Pool](crate::Pool) for Ethereum. /// /// This type is essentially a wrapper around [TransactionSignedEcRecovered] with additional fields @@ -659,7 +666,7 @@ pub struct EthPooledTransaction { /// Represents the blob sidecar of the [EthPooledTransaction]. #[derive(Debug, Clone, PartialEq, Eq)] -pub(crate) enum EthBlobTransactionSidecar { +pub enum EthBlobTransactionSidecar { /// This transaction does not have a blob sidecar None, /// This transaction has a blob sidecar (EIP-4844) but it is missing @@ -812,6 +819,16 @@ impl PoolTransaction for EthPooledTransaction { } } +impl EthPoolTransaction for EthPooledTransaction { + fn take_blob(&mut self) -> EthBlobTransactionSidecar { + if self.is_eip4844() { + std::mem::replace(&mut self.blob_sidecar, EthBlobTransactionSidecar::Missing) + } else { + EthBlobTransactionSidecar::None + } + } +} + impl FromRecoveredTransaction for EthPooledTransaction { fn from_recovered_transaction(tx: TransactionSignedEcRecovered) -> Self { EthPooledTransaction::new(tx) diff --git a/crates/transaction-pool/src/validate/eth.rs b/crates/transaction-pool/src/validate/eth.rs index f5899b4f8b..a61fec1ec7 100644 --- a/crates/transaction-pool/src/validate/eth.rs +++ b/crates/transaction-pool/src/validate/eth.rs @@ -3,9 +3,10 @@ use crate::{ blobstore::BlobStore, error::InvalidPoolTransactionError, - traits::{PoolTransaction, TransactionOrigin}, + traits::TransactionOrigin, validate::{ValidTransaction, ValidationTask, MAX_INIT_CODE_SIZE, TX_MAX_SIZE}, - TransactionValidationOutcome, TransactionValidationTaskExecutor, TransactionValidator, + EthBlobTransactionSidecar, EthPoolTransaction, TransactionValidationOutcome, + TransactionValidationTaskExecutor, TransactionValidator, }; use reth_primitives::{ constants::{eip4844::KZG_TRUSTED_SETUP, ETHEREUM_BLOCK_GAS_LIMIT}, @@ -32,7 +33,7 @@ pub struct EthTransactionValidator { impl TransactionValidator for EthTransactionValidator where Client: StateProviderFactory, - Tx: PoolTransaction, + Tx: EthPoolTransaction, { type Transaction = Tx; @@ -57,7 +58,6 @@ pub(crate) struct EthTransactionValidatorInner { /// This type fetches account info from the db client: Client, /// Blobstore used for fetching re-injected blob transactions. - #[allow(unused)] blob_store: Box, /// tracks activated forks relevant for transaction validation fork_tracker: ForkTracker, @@ -93,14 +93,14 @@ impl EthTransactionValidatorInner { impl TransactionValidator for EthTransactionValidatorInner where Client: StateProviderFactory, - Tx: PoolTransaction, + Tx: EthPoolTransaction, { type Transaction = Tx; async fn validate_transaction( &self, origin: TransactionOrigin, - transaction: Self::Transaction, + mut transaction: Self::Transaction, ) -> TransactionValidationOutcome { // Checks for tx_type match transaction.tx_type() { @@ -198,6 +198,8 @@ where } } + let mut blob_sidecar = None; + // blob tx checks if transaction.is_eip4844() { // Cancun fork is required for blob txs @@ -207,7 +209,31 @@ where InvalidTransactionError::TxTypeNotSupported.into(), ) } - // TODO add checks for blob tx + + // extract the blob from the transaction + match transaction.take_blob() { + EthBlobTransactionSidecar::None => { + // this should not happen + return TransactionValidationOutcome::Invalid( + transaction, + InvalidTransactionError::TxTypeNotSupported.into(), + ) + } + EthBlobTransactionSidecar::Missing => { + if let Ok(Some(_)) = self.blob_store.get(*transaction.hash()) { + // validated transaction is already in the store + } else { + return TransactionValidationOutcome::Invalid( + transaction, + InvalidPoolTransactionError::MissingEip4844Blob, + ) + } + } + EthBlobTransactionSidecar::Present(blob) => { + //TODO(mattsse): verify the blob + blob_sidecar = Some(blob); + } + } } let account = match self @@ -255,7 +281,7 @@ where TransactionValidationOutcome::Valid { balance: account.balance, state_nonce: account.nonce, - transaction: ValidTransaction::Valid(transaction), + transaction: ValidTransaction::new(transaction, blob_sidecar), // by this point assume all external transactions should be propagated propagate: match origin { TransactionOrigin::External => true, diff --git a/crates/transaction-pool/src/validate/mod.rs b/crates/transaction-pool/src/validate/mod.rs index 1e5cc78f89..d99100961a 100644 --- a/crates/transaction-pool/src/validate/mod.rs +++ b/crates/transaction-pool/src/validate/mod.rs @@ -86,6 +86,17 @@ pub enum ValidTransaction { }, } +impl ValidTransaction { + /// Creates a new valid transaction with an optional sidecar. + pub fn new(transaction: T, sidecar: Option) -> Self { + if let Some(sidecar) = sidecar { + Self::ValidWithSidecar { transaction, sidecar } + } else { + Self::Valid(transaction) + } + } +} + impl ValidTransaction { #[inline] pub(crate) fn transaction(&self) -> &T {