Skip to content
Open
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: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ members = [
"crates/crypto",
"crates/data-chain",
"crates/storage",
"crates/node",
"crates/node", "crates/mempool",
]
resolver = "2"

Expand Down
36 changes: 36 additions & 0 deletions crates/mempool/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
[package]
name = "mempool"
version.workspace = true
edition.workspace = true
authors.workspace = true
license.workspace = true
repository.workspace = true

[dependencies]
# Reth transaction pool and primitives
reth-transaction-pool = { git = "https://github.com/paradigmxyz/reth", rev = "v1.1.0" }
reth-primitives = { git = "https://github.com/paradigmxyz/reth", rev = "v1.1.0" }
reth-storage-api = { git = "https://github.com/paradigmxyz/reth", rev = "v1.1.0" }
reth-chainspec = { git = "https://github.com/paradigmxyz/reth", rev = "v1.1.0" }

# Ethereum types (alloy suite - use Reth compatible version)
alloy-primitives = "0.8"
alloy-eips = "0.4"

# Serialization
serde = { workspace = true }
serde_json = { workspace = true }

# Error handling
thiserror = { workspace = true }

# Async
tokio = { workspace = true }
async-trait = { workspace = true }

# Logging
tracing = { workspace = true }

[dev-dependencies]
reth-transaction-pool = { git = "https://github.com/paradigmxyz/reth", rev = "v1.1.0", features = ["test-utils"] }
reth-provider = { git = "https://github.com/paradigmxyz/reth", rev = "v1.1.0", features = ["test-utils"] }
89 changes: 89 additions & 0 deletions crates/mempool/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# CipherBFT Mempool

This crate wraps Reth's transaction pool and adds CipherBFT-specific validation.

## What is implemented

- `CipherBftPool<P>`: thin wrapper over Reth's pool (`pool.rs`)
- `CipherBftValidator<V>`: wrapper for Reth validator (`validator.rs`)
- BFT policy checks: min gas price + nonce gap (inside `validate_bft_policy`)
- `MempoolConfig -> PoolConfig` mapping (`config.rs`)
- Worker adapter: pending/queued/batch helpers (`CipherBftPoolAdapter`)

## Pool creation

### Recommended (build internally)

`CipherBftPool::new(...)` builds the Reth pool and validator internally.

```rust
use cipherbft_mempool::{CipherBftPool, MempoolConfig};
use reth_chainspec::ChainSpec;
use reth_provider::StateProviderFactory;
use reth_transaction_pool::blobstore::InMemoryBlobStore;
use std::sync::Arc;

let chain_spec: Arc<ChainSpec> = Arc::new(/* ... */);
let client: impl StateProviderFactory = /* ... */;
let blob_store = InMemoryBlobStore::default();
let chain_id = 1;
let config = MempoolConfig::default();

let pool = CipherBftPool::new(chain_spec, client, blob_store, chain_id, config)?;
```

### Wrap an existing Reth pool

Use this when you already constructed a `Pool`.

```rust
use cipherbft_mempool::{CipherBftPool, CipherBftValidator, MempoolConfig};
use reth_transaction_pool::{Pool, CoinbaseTipOrdering, PoolConfig};

let state_provider = client.latest()?;
let validator = CipherBftValidator::new(chain_spec, client, blob_store.clone(), chain_id);
let pool_config: PoolConfig = mempool_config.clone().into();
let reth_pool = Pool::new(
validator,
CoinbaseTipOrdering::default(),
blob_store,
pool_config,
);

let pool = CipherBftPool::wrap(reth_pool, mempool_config, state_provider);
```

## Transaction insertion

`add_transaction` accepts several input types (`TransactionSigned`, recovered, pooled, raw bytes):

```rust
use reth_transaction_pool::TransactionOrigin;

pool.add_transaction(TransactionOrigin::External, tx_signed).await?;
pool.add_transaction(TransactionOrigin::External, tx_recovered).await?;
pool.add_transaction(TransactionOrigin::External, pooled).await?;
```

Or insert pooled transactions directly:

```rust
pool.add_pooled_transaction(TransactionOrigin::Local, pooled_tx).await?;
```

## Adapter helpers (worker-facing)

```rust
let adapter = pool.adapter();
let pending = adapter.pending_transactions();
let queued = adapter.queued_transactions();
let batch = adapter.get_transactions_for_batch(100, 30_000_000);
let stats = adapter.stats();
adapter.remove_finalized(&tx_hashes);
```

## Notes

- BFT policy checks are enforced before handing transactions to Reth.
- Standard Ethereum validation is delegated to Reth.
- Worker integration is not yet wired in the node.
108 changes: 108 additions & 0 deletions crates/mempool/src/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
//! Mempool configuration
//!
//! We primarily rely on Reth's `PoolConfig`, but expose a CipherBFT-friendly
//! wrapper so higher layers can express their preferences without depending
//! on Reth types directly.

use reth_transaction_pool::PoolConfig;
use serde::{Deserialize, Serialize};

/// Mempool configuration with CipherBFT-specific knobs.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MempoolConfig {
/// Maximum executable transactions to keep in the pending sub-pool.
pub max_pending: usize,

/// Maximum queued transactions per sender (maps to Reth's account slots).
pub max_queued_per_account: usize,

/// Maximum nonce gap before the transaction is considered queued.
pub max_nonce_gap: u64,

/// Minimum gas price (in wei) we allow into the pool.
pub min_gas_price: u128,

/// Default price bump (in %) required to replace a transaction.
pub default_price_bump: u128,

/// Price bump (in %) required to replace a blob transaction.
pub replace_blob_tx_price_bump: u128,
}

impl Default for MempoolConfig {
fn default() -> Self {
Self {
max_pending: 10_000,
max_queued_per_account: 100,
max_nonce_gap: 16,
min_gas_price: 1_000_000_000, // 1 gwei
default_price_bump: 10,
replace_blob_tx_price_bump: 100,
}
}
}

impl From<MempoolConfig> for PoolConfig {
fn from(cfg: MempoolConfig) -> Self {
let mut pool_cfg = PoolConfig::default();
pool_cfg.pending_limit.max_txs = cfg.max_pending;
pool_cfg.queued_limit.max_txs = cfg.max_pending;
pool_cfg.max_account_slots = cfg.max_queued_per_account;
pool_cfg.price_bumps.default_price_bump = cfg.default_price_bump;
pool_cfg.price_bumps.replace_blob_tx_price_bump = cfg.replace_blob_tx_price_bump;

// TODO: map min_gas_price and max_nonce_gap once upstream hooks are wired.
pool_cfg
}
}

// probably not needed
// impl MempoolConfig {
// /// Convert borrowed configuration to the underlying Reth configuration.
// pub fn to_reth_config(&self) -> PoolConfig {
// let mut pool_cfg = PoolConfig::default();
// pool_cfg.pending_limit.max_txs = self.max_pending;
// pool_cfg.queued_limit.max_txs = self.max_pending;
// pool_cfg.max_account_slots = self.max_queued_per_account;
// pool_cfg.price_bumps.default_price_bump = self.default_price_bump;
// pool_cfg.price_bumps.replace_blob_tx_price_bump = self.replace_blob_tx_price_bump;

// // TODO: map min_gas_price and max_nonce_gap once upstream hooks are wired.
// pool_cfg
// }
// }

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_default_config() {
let cfg = MempoolConfig::default();
assert_eq!(cfg.max_pending, 10_000);
assert_eq!(cfg.max_queued_per_account, 100);
assert_eq!(cfg.max_nonce_gap, 16);
assert_eq!(cfg.min_gas_price, 1_000_000_000);
assert_eq!(cfg.default_price_bump, 10);
assert_eq!(cfg.replace_blob_tx_price_bump, 100);
}

#[test]
fn test_reth_conversion() {
let cfg = MempoolConfig {
max_pending: 5_000,
max_queued_per_account: 42,
default_price_bump: 25,
replace_blob_tx_price_bump: 150,
..Default::default()
};

// let reth_cfg = cfg.to_reth_config();
let reth_cfg: PoolConfig = cfg.into();
assert_eq!(reth_cfg.pending_limit.max_txs, 5_000);
assert_eq!(reth_cfg.queued_limit.max_txs, 5_000);
assert_eq!(reth_cfg.max_account_slots, 42);
assert_eq!(reth_cfg.price_bumps.default_price_bump, 25);
assert_eq!(reth_cfg.price_bumps.replace_blob_tx_price_bump, 150);
}
}
51 changes: 51 additions & 0 deletions crates/mempool/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//! Error types for mempool operations
//!
//! MP-1 단계: 데이터 구조 정의만 유지. Reth PoolError를 감싸고 최소한의
//! BFT 정책 에러만 노출한다.

use reth_transaction_pool::error::PoolError;
use thiserror::Error;

/// Mempool error types (expanded for MP-2)
#[derive(Error, Debug)]
pub enum MempoolError {
/// Bubble up errors coming from the underlying Reth pool.
#[error(transparent)]
Pool(#[from] PoolError),

/// Gas price below policy threshold.
#[error("Insufficient gas price: got {got}, min {min}")]
InsufficientGasPrice { got: u128, min: u128 },

/// Nonce gap exceeds configured maximum.
#[error("Nonce gap exceeds maximum: gap={gap} > max={max}")]
NonceGapExceeded { gap: u64, max: u64 },

/// Invalid signature.
#[error("Invalid transaction signature")]
InvalidSignature,

/// Nonce too low (already executed).
#[error("Nonce too low: tx nonce {tx_nonce}, current {current_nonce}")]
NonceTooLow { tx_nonce: u64, current_nonce: u64 },

/// Insufficient balance for gas.
#[error("Insufficient balance: need {need}, have {have}")]
InsufficientBalance { need: u128, have: u128 },

/// Gas limit too high.
#[error("Gas limit too high: {gas_limit} > {max}")]
GasLimitTooHigh { gas_limit: u64, max: u64 },

/// Transaction size exceeds limit.
#[error("Transaction too large: {size} > {max}")]
OversizedTransaction { size: usize, max: usize },

/// Failed to convert into pool-specific transaction type.
#[error("Transaction conversion failed: {0}")]
Conversion(String),

/// Internal error marker.
#[error("Internal error: {0}")]
Internal(String),
}
26 changes: 26 additions & 0 deletions crates/mempool/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//! CipherBFT Mempool - Transaction mempool based on Reth's TransactionPool
//!
//! # Architecture
//!
//! The mempool is organized around Reth's transaction pool with CipherBFT-specific
//! wrapping for DCL/CL integration.
//!
//! ## Modules
//!
//! - `error`: Error types for mempool operations
//! - `config`: Configuration for the mempool
//! - `transaction`: Transaction metadata tracking
//! - `account`: Per-account state management
//! - `pool`: Main pool adapter over Reth's TransactionPool

pub mod config;
pub mod error;
pub mod pool;
pub mod transaction;
pub mod validator;

pub use config::MempoolConfig;
pub use error::MempoolError;
pub use pool::CipherBftPool;
pub use transaction::TransactionOrdering;
pub use validator::CipherBftValidator;
Loading