Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

37 changes: 36 additions & 1 deletion crates/interfaces/src/consensus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ pub enum Error {
#[error("Transaction nonce is not consistent.")]
TransactionNonceNotConsistent,
#[error("Account does not have enough funds ({available_funds:?}) to cover transaction max fee: {max_fee:?}.")]
InsufficientFunds { max_fee: u128, available_funds: u128 },
InsufficientFunds { max_fee: u128, available_funds: U256 },
#[error("Eip2930 transaction is enabled after berlin hardfork.")]
TransactionEip2930Disabled,
#[error("Old legacy transaction before Spurious Dragon should not have chain_id.")]
Expand Down Expand Up @@ -130,4 +130,39 @@ pub enum Error {
WithdrawalIndexInvalid { got: u64, expected: u64 },
#[error("Missing withdrawals")]
BodyWithdrawalsMissing,
/// Thrown when calculating gas usage
#[error("gas uint64 overflow")]
GasUintOverflow,
/// returned if the transaction is specified to use less gas than required to start the
/// invocation.
#[error("intrinsic gas too low")]
GasTooLow,
/// returned if the transaction gas exceeds the limit
#[error("intrinsic gas too high")]
GasTooHigh,
/// thrown if a transaction is not supported in the current network configuration.
#[error("transaction type not supported")]
TxTypeNotSupported,
/// Thrown to ensure no one is able to specify a transaction with a tip higher than the total
/// fee cap.
#[error("max priority fee per gas higher than max fee per gas")]
TipAboveFeeCap,
/// A sanity error to avoid huge numbers specified in the tip field.
#[error("max priority fee per gas higher than 2^256-1")]
TipVeryHigh,
/// A sanity error to avoid huge numbers specified in the fee cap field.
#[error("max fee per gas higher than 2^256-1")]
FeeCapVeryHigh,
/// Thrown post London if the transaction's fee is less than the base fee of the block
#[error("max fee per gas less than block base fee")]
FeeCapTooLow,
/// Thrown if the sender of a transaction is a contract.
#[error("sender not an eoa")]
SenderNoEOA,
}

impl From<crate::error::Error> for Error {
fn from(_: crate::error::Error) -> Self {
Error::TransactionSignerRecoveryError
}
}
2 changes: 2 additions & 0 deletions crates/rpc/rpc/src/eth/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,8 @@ impl From<PoolError> for GethTxPoolError {
PoolError::DiscardedOnInsert(_) => GethTxPoolError::TxPoolOverflow,
PoolError::TxExceedsGasLimit(_, _, _) => GethTxPoolError::GasLimit,
PoolError::TxExceedsMaxInitCodeSize(_, _, _) => GethTxPoolError::OversizedData,
PoolError::AccountNotFound(_) => GethTxPoolError::InvalidSender,
PoolError::OversizedData(_, _, _) => GethTxPoolError::OversizedData,
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions crates/transaction-pool/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ normal = [

# reth
reth-primitives = { path = "../primitives" }
reth-provider = { path = "../storage/provider" }
reth-interfaces = { path = "../interfaces" }
reth-rlp = { path = "../rlp" }

# async/futures
Expand Down
10 changes: 10 additions & 0 deletions crates/transaction-pool/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,14 @@ pub enum PoolError {
/// respect the max_init_code_size.
#[error("[{0:?}] Transaction's size {1} exceeds max_init_code_size {2}.")]
TxExceedsMaxInitCodeSize(TxHash, usize, usize),
/// Thrown if the transaction contains an invalid signature
#[error("[{0:?}]: Invalid sender")]
AccountNotFound(TxHash),
/// Thrown if the input data of a transaction is greater
/// than some meaningful limit a user might use. This is not a consensus error
/// making the transaction invalid, rather a DOS protection.
#[error("[{0:?}]: Input data too large")]
OversizedData(TxHash, usize, usize),
}

// === impl PoolError ===
Expand All @@ -43,6 +51,8 @@ impl PoolError {
PoolError::DiscardedOnInsert(hash) => hash,
PoolError::TxExceedsGasLimit(hash, _, _) => hash,
PoolError::TxExceedsMaxInitCodeSize(hash, _, _) => hash,
PoolError::AccountNotFound(hash) => hash,
PoolError::OversizedData(hash, _, _) => hash,
}
}
}
48 changes: 44 additions & 4 deletions crates/transaction-pool/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ use crate::{
pool::PoolInner,
traits::{NewTransactionEvent, PoolSize},
};

use reth_interfaces::consensus::Error;
use reth_primitives::{TxHash, U256};
use std::{collections::HashMap, sync::Arc};
use tokio::sync::mpsc::Receiver;
Expand All @@ -110,6 +112,24 @@ mod validate;
/// Common test helpers for mocking A pool
pub mod test_utils;

// TX_SLOT_SIZE is used to calculate how many data slots a single transaction
// takes up based on its size. The slots are used as DoS protection, ensuring
// that validating a new transaction remains a constant operation (in reality
// O(maxslots), where max slots are 4 currently).
pub(crate) const TX_SLOT_SIZE: usize = 32 * 1024;

// TX_MAX_SIZE is the maximum size a single transaction can have. This field has
// non-trivial consequences: larger transactions are significantly harder and
// more expensive to propagate; larger transactions also take more resources
// to validate whether they fit into the pool or not.
pub(crate) const TX_MAX_SIZE: usize = 4 * TX_SLOT_SIZE; //128KB

// Maximum bytecode to permit for a contract
pub(crate) const MAX_CODE_SIZE: usize = 24576;

// Maximum initcode to permit in a creation transaction and create instructions
pub(crate) const MAX_INIT_CODE_SIZE: usize = 2 * MAX_CODE_SIZE;

/// A shareable, generic, customizable `TransactionPool` implementation.
#[derive(Debug)]
pub struct Pool<V: TransactionValidator, T: TransactionOrdering> {
Expand Down Expand Up @@ -144,7 +164,8 @@ where
&self,
origin: TransactionOrigin,
transactions: impl IntoIterator<Item = V::Transaction>,
) -> PoolResult<HashMap<TxHash, TransactionValidationOutcome<V::Transaction>>> {
) -> PoolResult<HashMap<TxHash, Result<TransactionValidationOutcome<V::Transaction>, Error>>>
{
let outcome = futures_util::future::join_all(
transactions.into_iter().map(|tx| self.validate(origin, tx)),
)
Expand All @@ -160,10 +181,12 @@ where
&self,
origin: TransactionOrigin,
transaction: V::Transaction,
) -> (TxHash, TransactionValidationOutcome<V::Transaction>) {
) -> (TxHash, Result<TransactionValidationOutcome<V::Transaction>, Error>) {
let hash = *transaction.hash();

// TODO(mattsse): this is where additional validate checks would go, like banned senders
// etc...

let outcome = self.pool.validator().validate_transaction(origin, transaction).await;

(hash, outcome)
Expand Down Expand Up @@ -203,7 +226,22 @@ where
transaction: Self::Transaction,
) -> PoolResult<TxHash> {
let (_, tx) = self.validate(origin, transaction).await;
self.pool.add_transactions(origin, std::iter::once(tx)).pop().expect("exists; qed")

match tx {
Ok(TransactionValidationOutcome::Valid {
balance: _,
state_nonce: _,
transaction: _,
}) => self
.pool
.add_transactions(origin, std::iter::once(tx.unwrap()))
.pop()
.expect("exists; qed"),
Ok(TransactionValidationOutcome::Invalid(_transaction, error)) => Err(error),
Err(_err) => {
unimplemented!()
}
}
}

async fn add_transactions(
Expand All @@ -212,7 +250,9 @@ where
transactions: Vec<Self::Transaction>,
) -> PoolResult<Vec<PoolResult<TxHash>>> {
let validated = self.validate_all(origin, transactions).await?;
let transactions = self.pool.add_transactions(origin, validated.into_values());

let transactions =
self.pool.add_transactions(origin, validated.into_values().map(|x| x.unwrap()));
Ok(transactions)
}

Expand Down
4 changes: 2 additions & 2 deletions crates/transaction-pool/src/pool/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Transaction Pool internals.
//!
//! Incoming transactions are before they enter the pool first. The validation outcome can have 3
//! states:
//! Incoming transactions validated are before they enter the pool first. The validation outcome can
//! have 3 states:
//!
//! 1. Transaction can _never_ be valid
//! 2. Transaction is _currently_ valid
Expand Down
4 changes: 4 additions & 0 deletions crates/transaction-pool/src/test_utils/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,10 @@ impl PoolTransaction for MockTransaction {
fn encoded_length(&self) -> usize {
0
}

fn chain_id(&self) -> Option<u64> {
Some(1)
}
}

impl FromRecoveredTransaction for MockTransaction {
Expand Down
7 changes: 4 additions & 3 deletions crates/transaction-pool/src/test_utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use crate::{
};
use async_trait::async_trait;
pub use mock::*;
use reth_interfaces::consensus::Error;
use std::{marker::PhantomData, sync::Arc};

/// A [Pool] used for testing
Expand Down Expand Up @@ -36,12 +37,12 @@ impl<T: PoolTransaction> TransactionValidator for NoopTransactionValidator<T> {
&self,
origin: TransactionOrigin,
transaction: Self::Transaction,
) -> TransactionValidationOutcome<Self::Transaction> {
TransactionValidationOutcome::Valid {
) -> Result<TransactionValidationOutcome<Self::Transaction>, Error> {
Ok(TransactionValidationOutcome::Valid {
balance: Default::default(),
state_nonce: 0,
transaction,
}
})
}
}

Expand Down
8 changes: 8 additions & 0 deletions crates/transaction-pool/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,9 @@ pub trait PoolTransaction: fmt::Debug + Send + Sync + FromRecoveredTransaction {

/// Returns the length of the rlp encoded object
fn encoded_length(&self) -> usize;

/// Returns chain_id
fn chain_id(&self) -> Option<u64>;
}

/// The default [PoolTransaction] for the [Pool](crate::Pool).
Expand Down Expand Up @@ -390,6 +393,11 @@ impl PoolTransaction for PooledTransaction {
fn encoded_length(&self) -> usize {
self.transaction.length()
}

/// Returns chain_id
fn chain_id(&self) -> Option<u64> {
self.transaction.chain_id()
}
}

impl FromRecoveredTransaction for PooledTransaction {
Expand Down
Loading