Crate reth_transaction_pool

source ·
Expand description

Reth’s transaction pool implementation.

This crate provides a generic transaction pool implementation.

§Functionality

The transaction pool is responsible for

  • recording incoming transactions
  • providing existing transactions
  • ordering and providing the best transactions for block production
  • monitoring memory footprint and enforce pool size limits
  • storing blob data for transactions in a separate blobstore on insertion

§Assumptions

§Transaction type

The pool expects certain ethereum related information from the generic transaction type of the pool (PoolTransaction), this includes gas price, base fee (EIP-1559 transactions), nonce etc. It makes no assumptions about the encoding format, but the transaction type must report its size so pool size limits (memory) can be enforced.

§Transaction ordering

The pending pool contains transactions that can be mined on the current state. The order in which they’re returned are determined by a Priority value returned by the TransactionOrdering type this pool is configured with.

This is only used in the pending pool to yield the best transactions for block production. The base pool is ordered by base fee, and the queued pool by current distance.

§Validation

The pool itself does not validate incoming transactions, instead this should be provided by implementing TransactionsValidator. Only transactions that the validator returns as valid are included in the pool. It is assumed that transaction that are in the pool are either valid on the current state or could become valid after certain state changes. transaction that can never become valid (e.g. nonce lower than current on chain nonce) will never be added to the pool and instead are discarded right away.

§State Changes

Once a new block is mined, the pool needs to be updated with a changeset in order to:

  • remove mined transactions
  • update using account changes: balance changes
  • base fee updates

§Implementation details

The TransactionPool trait exposes all externally used functionality of the pool, such as inserting, querying specific transactions by hash or retrieving the best transactions. In addition, it enables the registration of event listeners that are notified of state changes. Events are communicated via channels.

§Architecture

The final TransactionPool is made up of two layers:

The lowest layer is the actual pool implementations that manages (validated) transactions: TxPool. This is contained in a higher level pool type that guards the low level pool and handles additional listeners or metrics: PoolInner.

The transaction pool will be used by separate consumers (RPC, P2P), to make sharing easier, the Pool type is just an Arc wrapper around PoolInner. This is the usable type that provides the TransactionPool interface.

§Blob Transactions

Blob transaction can be quite large hence they are stored in a separate blobstore. The pool is responsible for inserting blob data for new transactions into the blobstore. See also ValidTransaction

§Examples

Listen for new transactions and print them:

use reth_chainspec::MAINNET;
use reth_provider::{BlockReaderIdExt, ChainSpecProvider, StateProviderFactory};
use reth_tasks::TokioTaskExecutor;
use reth_transaction_pool::{TransactionValidationTaskExecutor, Pool, TransactionPool};
use reth_transaction_pool::blobstore::InMemoryBlobStore;
async fn t<C>(client: C)  where C: StateProviderFactory + BlockReaderIdExt + ChainSpecProvider + Clone + 'static{
    let blob_store = InMemoryBlobStore::default();
    let pool = Pool::eth_pool(
        TransactionValidationTaskExecutor::eth(client, MAINNET.clone(), blob_store.clone(), TokioTaskExecutor::default()),
        blob_store,
        Default::default(),
    );
  let mut transactions = pool.pending_transactions_listener();
  tokio::task::spawn( async move {
     while let Some(tx) = transactions.recv().await {
         println!("New transaction: {:?}", tx);
     }
  });

  // do something useful with the pool, like RPC integration

Spawn maintenance task to keep the pool updated

use futures_util::Stream;
use reth_chainspec::MAINNET;
use reth_provider::{BlockReaderIdExt, CanonStateNotification, ChainSpecProvider, StateProviderFactory};
use reth_tasks::TokioTaskExecutor;
use reth_tasks::TaskSpawner;
use reth_tasks::TaskManager;
use reth_transaction_pool::{TransactionValidationTaskExecutor, Pool};
use reth_transaction_pool::blobstore::InMemoryBlobStore;
use reth_transaction_pool::maintain::{maintain_transaction_pool_future};

 async fn t<C, St>(client: C, stream: St)
   where C: StateProviderFactory + BlockReaderIdExt + ChainSpecProvider + Clone + 'static,
    St: Stream<Item = CanonStateNotification> + Send + Unpin + 'static,
    {
    let blob_store = InMemoryBlobStore::default();
    let rt = tokio::runtime::Runtime::new().unwrap();
    let manager = TaskManager::new(rt.handle().clone());
    let executor = manager.executor();
    let pool = Pool::eth_pool(
        TransactionValidationTaskExecutor::eth(client.clone(), MAINNET.clone(), blob_store.clone(), executor.clone()),
        blob_store,
        Default::default(),
    );

  // spawn a task that listens for new blocks and updates the pool's transactions, mined transactions etc..
  tokio::task::spawn(maintain_transaction_pool_future(client, pool, stream, executor.clone(), Default::default()));

§Feature Flags

  • serde (default): Enable serde support
  • test-utils: Export utilities for testing

Re-exports§

Modules§

  • Storage for blob data of EIP4844 transactions.
  • Transaction pool errors
  • Identifier types for transactions and senders.
  • Support for maintaining the state of the transaction pool
  • Transaction pool metrics.
  • A transaction pool implementation that does nothing.
  • Transaction Pool internals.
  • test_utilstest-utils
    Common test helpers for mocking a pool Internal helpers for testing.
  • Transaction validation abstractions.

Structs§

Enums§

Constants§

Traits§

Type Aliases§