Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
5f869fa
feat(txpool): initial sketch
mattsse Sep 29, 2022
91efdc2
more abstractions
mattsse Sep 30, 2022
4f8b9d9
work on listeners
mattsse Sep 30, 2022
ddcf584
Merge branch 'master' into matt/add-tx-pool
mattsse Oct 3, 2022
f679140
work on tx pool
mattsse Oct 3, 2022
8a04dd6
Merge branch 'master' into matt/add-tx-pool
mattsse Oct 3, 2022
6238619
work on tx pool
mattsse Oct 3, 2022
22f1d88
feat: more transaction types
mattsse Oct 3, 2022
00ad854
Merge branch 'master' into matt/add-tx-pool
mattsse Oct 3, 2022
6028f47
chore: cleanup
mattsse Oct 3, 2022
b2ff8b7
work on queued pool
mattsse Oct 4, 2022
9e1e377
work on pending pool
mattsse Oct 4, 2022
3e20868
docs: more docs
mattsse Oct 4, 2022
6b28560
feat: add transactions iterator
mattsse Oct 4, 2022
07aab5c
impl transaction pruning
mattsse Oct 4, 2022
56dfb76
refactor: separate pool functions
mattsse Oct 4, 2022
df66604
refactor: different pool types
mattsse Oct 4, 2022
8ba1501
feat: work on PoolApi
mattsse Oct 4, 2022
153e5bf
docs: more pool docs
mattsse Oct 5, 2022
a319290
refactor: unify transaction associated types
mattsse Oct 5, 2022
fc0e773
feat: add basic event handling
mattsse Oct 5, 2022
957e330
feat: more helper functions
mattsse Oct 5, 2022
9fdea85
style: cleanup, clippy fixes
mattsse Oct 5, 2022
1d710a3
rename pool
mattsse Oct 5, 2022
5152cd1
feat: implement constructors
mattsse Oct 5, 2022
acea23a
feat: extend transaction pool api
mattsse Oct 5, 2022
9ea4337
feat: more pool api impls
mattsse Oct 5, 2022
53ec974
feat: introduce chainevents
mattsse Oct 5, 2022
b6936fa
feat: more pool api
mattsse Oct 5, 2022
eb0fa84
feat: start integrating dynamic fee
mattsse Oct 5, 2022
62a9953
simplify ids
mattsse Oct 6, 2022
9368ec0
use address type
mattsse Oct 6, 2022
24c10c3
add sender info
mattsse Oct 6, 2022
3002938
more refactoring
mattsse Oct 6, 2022
0eb1539
refactor: remove associated Id type
mattsse Oct 6, 2022
9f23ade
ease warnings
mattsse Oct 6, 2022
82c5c4b
add dependency function
mattsse Oct 6, 2022
af1aaf3
remove Hash associated type
mattsse Oct 6, 2022
28324d1
Merge branch 'master' into matt/add-tx-pool
mattsse Oct 6, 2022
fe4ca48
Merge branch 'master' into matt/add-tx-pool
mattsse Oct 6, 2022
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
33 changes: 33 additions & 0 deletions Cargo.lock

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

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ members = [
"crates/net/rpc-types",
"crates/primitives",
"crates/stages",
"crates/transaction-pool",
"crates/db"
]

[dependencies]
Expand Down
27 changes: 27 additions & 0 deletions crates/transaction-pool/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
[package]
name = "reth-transaction-pool"
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"
repository = "https://github.com/foundry-rs/reth"
readme = "README.md"
description = """
Transaction pool implementation
"""

[dependencies]

# eth
reth-primitives = { path = "../primitives" }

# async/futures
async-trait = "0.1"
futures = "0.3"
parking_lot = "0.12"

# misc
thiserror = "1.0"
tracing = "0.1"
serde = { version = "1.0", features = ["derive"] }
linked-hash-map = "0.5"
fnv = "1.0.7"
24 changes: 24 additions & 0 deletions crates/transaction-pool/src/client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//! Provides access to the chain's storage

use crate::{
error::{PoolError, PoolResult},
validate::TransactionValidator,
};
use reth_primitives::{BlockID, U64};

/// The interface used to interact with the blockchain and access storage.
#[async_trait::async_trait]
pub trait PoolClient: Send + Sync + TransactionValidator {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is TransactionValidator needed here?

/// Error type that can be converted to the crate's internal Error.
type Error: Into<PoolError>;

/// Returns the block number for the given block identifier.
fn convert_block_id(&self, block_id: &BlockID) -> PoolResult<Option<U64>>;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure if i understand why would we need get block number and mapping of hash->height. TxPool should only care about base_fee and transactions that are removed/added, and that is broadcasted to txpool on block inclusion.


/// Same as [`PoolClient::convert_block_id()`] but returns an error if no matching block number
/// was found
fn ensure_block_number(&self, block_id: &BlockID) -> PoolResult<U64> {
self.convert_block_id(block_id)
.and_then(|number| number.ok_or(PoolError::BlockNumberNotFound(*block_id)))
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: we probably need account_info(address) to get the account nonce and balance.

}
13 changes: 13 additions & 0 deletions crates/transaction-pool/src/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
///! Configuration options for the Transaction pool.
#[derive(Debug, Clone)]
pub struct PoolConfig {
// TODO add limits for subpools
// TODO limits for per peer
// TODO config whether to check if transactions are banned
}

impl Default for PoolConfig {
fn default() -> Self {
todo!()
}
}
23 changes: 23 additions & 0 deletions crates/transaction-pool/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//! Transaction pool errors

use reth_primitives::BlockID;

/// Transaction pool result type.
pub type PoolResult<T> = Result<T, PoolError>;

/// All errors the Transaction pool can throw.
#[derive(Debug, thiserror::Error)]
pub enum PoolError {
/// Thrown if a replacement transaction's gas price is below the already imported transaction
#[error("Tx: insufficient gas price to replace existing transaction")]
ReplacementUnderpriced,
/// Encountered a transaction that was already added into the poll
#[error("[{0:?}] Already added")]
AlreadyAdded(Box<dyn std::any::Any + Send + Sync>),
/// Encountered a cycle in the graph pool
#[error("Transaction with cyclic dependent transactions")]
CyclicTransaction,
/// Thrown if no number was found for the given block id
#[error("Invalid block id: {0:?}")]
BlockNumberNotFound(BlockID),
}
95 changes: 95 additions & 0 deletions crates/transaction-pool/src/identifier.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
use crate::U256;
use fnv::FnvHashMap;
use reth_primitives::Address;
use std::collections::HashMap;

/// An internal mapping of addresses.
///
/// This assigns a _unique_ `SenderId` for a new `Address`.
#[derive(Debug)]
pub struct SenderIdentifiers {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I searched code and this seems not used

/// The identifier to use next.
id: u64,
/// Assigned `SenderId` for an `Address`.
address_to_id: HashMap<Address, SenderId>,
/// Reverse mapping of `SenderId` to `Address`.
sender_to_address: FnvHashMap<SenderId, Address>,
}

impl SenderIdentifiers {
/// Returns the address for the given identifier.
pub fn address(&self, id: &SenderId) -> Option<&Address> {
self.sender_to_address.get(id)
}

/// Returns the `SenderId` that belongs to the given address, if it exists
pub fn sender_id(&self, addr: &Address) -> Option<SenderId> {
self.address_to_id.get(addr).copied()
}

/// Returns the existing `SendId` or assigns a new one if it's missing
pub fn sender_id_or_create(&mut self, addr: Address) -> SenderId {
if let Some(id) = self.sender_id(&addr) {
return id
}
let id = self.next_id();
self.address_to_id.insert(addr, id);
self.sender_to_address.insert(id, addr);
id
}

/// Returns a new address
fn next_id(&mut self) -> SenderId {
let id = self.id;
self.id = self.id.wrapping_add(1);
SenderId(id)
}
}

/// A _unique_ identifier for a sender of an address.
///
/// This is the identifier of an internal `address` mapping that is valid in the context of this
/// program.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct SenderId(u64);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like there is some internal mapping that uses SenderId, and it is used inside TransactionId as a field.
GraphPool has it as sender_info but it seems like it is not used, maybe it is just a leftover, that still needs impl to be used.


/// A unique identifier of a transaction of a Sender.
///
/// This serves as an identifier for dependencies of a transaction:
/// A transaction with a nonce higher than the current state nonce depends on `tx.nonce - 1`.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct TransactionId {
/// Sender of this transaction
pub sender: SenderId,
/// Nonce of this transaction
pub nonce: u64,
}

// === impl TransactionId ===

impl TransactionId {
/// Create a new identifier pair
pub fn new(sender: SenderId, nonce: u64) -> Self {
Self { sender, nonce }
}

/// Returns the id a transactions depends on
///
/// This returns `transaction_nonce - 1` if `transaction_nonce` is higher than the
/// `on_chain_none`
pub fn dependency(
transaction_nonce: u64,
on_chain_nonce: u64,
sender: SenderId,
) -> Option<TransactionId> {
if transaction_nonce == on_chain_nonce {
return None
}
let prev_nonce = transaction_nonce.saturating_sub(1);
if on_chain_nonce <= prev_nonce {
Some(Self::new(sender, prev_nonce))
} else {
None
}
}
}
114 changes: 114 additions & 0 deletions crates/transaction-pool/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
#![warn(missing_docs)] // unreachable_pub, missing_debug_implementations
#![allow(unused)] // TODO(mattsse) remove after progress was made
#![deny(unused_must_use, rust_2018_idioms)]
#![doc(test(
no_crate_inject,
attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables))
))]

//! Reth's transaction pool implementation

use futures::channel::mpsc::Receiver;
use parking_lot::Mutex;
use reth_primitives::{BlockID, TxHash, U256, U64};
use std::sync::Arc;

mod client;
mod config;
pub mod error;
mod identifier;
mod ordering;
pub mod pool;
mod traits;
mod validate;

pub use crate::{
client::PoolClient,
config::PoolConfig,
ordering::TransactionOrdering,
pool::BasicPool,
traits::{BestTransactions, NewBlockEvent, PoolTransaction, TransactionPool},
validate::{TransactionValidationOutcome, TransactionValidator},
};
use crate::{error::PoolResult, validate::ValidPoolTransaction};

/// A generic, customizable `TransactionPool` implementation.
pub struct Pool<P: PoolClient, T: TransactionOrdering> {
/// The actual transaction pool where transactions and subscriptions are handled.
pool: BasicPool<P, T>,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have 3 layers of abstractions here. Pool-> BasicPool->PoolInner, this probably can be shrunk to only two as I am not sure if that abstraction is needed.

/// Tracks status updates linked to chain events.
update_status: Arc<Mutex<UpdateStatus>>,
/// Chain/Storage access.
client: Arc<P>,
}

// === impl Pool ===

impl<P, T> Pool<P, T>
where
P: PoolClient,
T: TransactionOrdering<Transaction = <P as TransactionValidator>::Transaction>,
{
/// Creates a new `Pool` with the given config and client and ordering.
pub fn new(client: Arc<P>, ordering: Arc<T>, config: PoolConfig) -> Self {
let pool = BasicPool::new(Arc::clone(&client), ordering, config);
Self { pool, update_status: Arc::new(Default::default()), client }
}
}

/// implements the `TransactionPool` interface for various transaction pool API consumers.
#[async_trait::async_trait]
impl<P, T> TransactionPool for Pool<P, T>
where
P: PoolClient,
T: TransactionOrdering<Transaction = <P as TransactionValidator>::Transaction>,
{
type Transaction = T::Transaction;

async fn on_new_block(&self, _event: NewBlockEvent) {
// TODO perform maintenance: update pool accordingly
todo!()
}

async fn add_transaction(
&self,
block_id: BlockID,
transaction: Self::Transaction,
) -> PoolResult<TxHash> {
self.pool.clone().add_transaction(&block_id, transaction).await
}

async fn add_transactions(
&self,
block_id: BlockID,
transactions: Vec<Self::Transaction>,
) -> PoolResult<Vec<PoolResult<TxHash>>> {
self.pool.clone().add_transactions(&block_id, transactions).await
}

fn ready_transactions_listener(&self) -> Receiver<TxHash> {
self.pool.ready_transactions_listener()
}

fn best_transactions(
&self,
) -> Box<dyn BestTransactions<Item = Arc<ValidPoolTransaction<Self::Transaction>>>> {
Box::new(self.pool.inner().ready_transactions())
}

fn remove_invalid(
&self,
_tx_hashes: &[TxHash],
) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
todo!()
}
}

/// Tracks the current update status of the pool.
#[derive(Debug, Clone, Default)]
struct UpdateStatus {
/// Block number when the pool was last updated.
updated_at: U64,
/// Current base fee that needs to be enforced
base_fee: U256,
}
20 changes: 20 additions & 0 deletions crates/transaction-pool/src/ordering.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use crate::traits::PoolTransaction;
use std::fmt;

/// Transaction ordering.
///
/// Decides how transactions should be ordered within the pool.
///
/// The returned priority must reflect natural `Ordering`.
// TODO: for custom, more advanced scoring it would be ideal to determine the priority in the
// context of the entire pool instead of standalone by alone looking at a single transaction
pub trait TransactionOrdering: Send + Sync + 'static {
/// Priority of a transaction.
type Priority: Ord + Clone + Default + fmt::Debug + Send + Sync;

/// The transaction type to score.
type Transaction: PoolTransaction + Send + Sync + 'static;

/// Returns the priority score for the given transaction.
fn priority(&self, transaction: &Self::Transaction) -> Self::Priority;
}
Loading