From 1e73e3aefcd42207b2683adc11988e7a834b88ca Mon Sep 17 00:00:00 2001 From: Ignotus Peverell Date: Sat, 7 Oct 2017 18:24:11 +0000 Subject: [PATCH] Add a min fee to accept transactions in the pool Configuration for a minum accept fee base for the transaction pool. The base is multipled by a weight computed from the transaction number of inputs, outputs and kernels. The transaction fee is required to always be larger than the weight times the base. min_fee = base * (-1*input_len + 4*output_len + kernel_len) The weight is set to never be less than one. Also added a configurable (and fairly naive for now) max pool capacity in number of transactions. --- grin/src/server.rs | 2 +- grin/src/types.rs | 4 ++++ pool/Cargo.toml | 2 ++ pool/src/lib.rs | 5 ++++- pool/src/pool.rs | 36 ++++++++++++++++++++++++++++++++++-- pool/src/types.rs | 25 +++++++++++++++++++++++++ 6 files changed, 70 insertions(+), 4 deletions(-) diff --git a/grin/src/server.rs b/grin/src/server.rs index a17edf582..aa1c7db62 100644 --- a/grin/src/server.rs +++ b/grin/src/server.rs @@ -80,7 +80,7 @@ impl Server { let pool_adapter = Arc::new(PoolToChainAdapter::new()); let tx_pool = Arc::new(RwLock::new( - pool::TransactionPool::new(pool_adapter.clone()), + pool::TransactionPool::new(config.pool_config.clone(), pool_adapter.clone()), )); let chain_adapter = Arc::new(ChainToPoolAndNetAdapter::new(tx_pool.clone())); diff --git a/grin/src/types.rs b/grin/src/types.rs index 6465ccb7b..a01ca708e 100644 --- a/grin/src/types.rs +++ b/grin/src/types.rs @@ -17,6 +17,7 @@ use std::convert::From; use api; use chain; use p2p; +use pool; use store; use pow; use core::global::MiningParameterMode; @@ -97,6 +98,8 @@ pub struct ServerConfig { /// Configuration for the mining daemon pub mining_config: Option, + + pub pool_config: pool::PoolConfig, } impl Default for ServerConfig { @@ -110,6 +113,7 @@ impl Default for ServerConfig { p2p_config: Some(p2p::P2PConfig::default()), mining_config: Some(pow::types::MinerConfig::default()), mining_parameter_mode: Some(MiningParameterMode::Production), + pool_config: pool::PoolConfig::default(), } } } diff --git a/pool/Cargo.toml b/pool/Cargo.toml index bebc7efe5..dd413c4bc 100644 --- a/pool/Cargo.toml +++ b/pool/Cargo.toml @@ -10,6 +10,8 @@ grin_keychain = { path = "../keychain" } grin_store = { path = "../store" } grin_p2p = { path = "../p2p" } secp256k1zkp = { git = "https://github.com/mimblewimble/rust-secp256k1-zkp" } +serde = "~1.0.8" +serde_derive = "~1.0.8" time = "^0.1" rand = "0.3" log = "0.3" diff --git a/pool/src/lib.rs b/pool/src/lib.rs index 1addcfbb7..ad8e04f3a 100644 --- a/pool/src/lib.rs +++ b/pool/src/lib.rs @@ -28,6 +28,9 @@ mod pool; extern crate time; extern crate rand; +extern crate serde; +#[macro_use] +extern crate serde_derive; extern crate log; extern crate blake2_rfc as blake2; extern crate grin_core as core; @@ -35,4 +38,4 @@ extern crate grin_keychain as keychain; extern crate secp256k1zkp as secp; pub use pool::TransactionPool; -pub use types::{BlockChain, TxSource, PoolError}; +pub use types::{BlockChain, TxSource, PoolError, PoolConfig}; diff --git a/pool/src/pool.rs b/pool/src/pool.rs index f2b98b8f2..4cad0a803 100644 --- a/pool/src/pool.rs +++ b/pool/src/pool.rs @@ -14,7 +14,7 @@ //! Top-level Pool type, methods, and tests -use types::{Pool, BlockChain, Orphans, Parent, PoolError, TxSource, TransactionGraphContainer}; +use types::*; pub use graph; use core::core::transaction; @@ -32,6 +32,7 @@ use std::collections::HashMap; /// The transactions HashMap holds ownership of all transactions in the pool, /// keyed by their transaction hash. pub struct TransactionPool { + config: PoolConfig, /// All transactions in the pool pub transactions: HashMap>, /// The pool itself @@ -49,8 +50,9 @@ where T: BlockChain, { /// Create a new transaction pool - pub fn new(chain: Arc) -> TransactionPool { + pub fn new(config: PoolConfig, chain: Arc) -> TransactionPool { TransactionPool { + config: config, transactions: HashMap::new(), pool: Pool::empty(), orphans: Orphans::empty(), @@ -127,6 +129,12 @@ where _: TxSource, tx: transaction::Transaction, ) -> Result<(), PoolError> { + + // Do we have the capacity to accept this transaction? + if let Err(e) = self.is_acceptable(&tx) { + return Err(e); + } + // Making sure the transaction is valid before anything else. let secp = secp::Secp256k1::with_caps(secp::ContextFlag::Commit); tx.validate(&secp).map_err(|_e| PoolError::Invalid)?; @@ -551,6 +559,26 @@ where .map(|x| self.transactions.get(x).unwrap().clone()) .collect() } + + /// Whether the transaction is acceptable to the pool, given both how + /// full the pool is and the transaction weight. + fn is_acceptable(&self, tx: &transaction::Transaction) -> Result<(), PoolError> { + if self.total_size() > self.config.max_pool_size { + // TODO evict old/large transactions instead + return Err(PoolError::OverCapacity); + } + if self.config.accept_fee_base > 0 { + let mut tx_weight = -1 * (tx.inputs.len() as i32) + (4 * tx.outputs.len() as i32) + 1; + if tx_weight < 1 { + tx_weight = 1; + } + let threshold = (tx_weight as u64) * self.config.accept_fee_base; + if tx.fee < threshold { + return Err(PoolError::LowFeeTransaction(threshold)); + } + } + Ok(()) + } } #[cfg(test)] @@ -1027,6 +1055,10 @@ mod tests { fn test_setup(dummy_chain: &Arc) -> TransactionPool { TransactionPool { + config: PoolConfig{ + accept_fee_base: 0, + max_pool_size: 10_000, + }, transactions: HashMap::new(), pool: Pool::empty(), orphans: Orphans::empty(), diff --git a/pool/src/types.rs b/pool/src/types.rs index 0918d957d..4dcb17f6e 100644 --- a/pool/src/types.rs +++ b/pool/src/types.rs @@ -28,6 +28,27 @@ use core::core::block; use core::core::transaction; use core::core::hash; +/// Tranasction pool configuration +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct PoolConfig { + /// Base fee for a transaction to be accepted by the pool. The transaction + /// weight is computed from its number of inputs, outputs and kernels and + /// multipled by the base fee to compare to the actual transaction fee. + pub accept_fee_base: u64, + + /// Maximum capacity of the pool in number of transactions + pub max_pool_size: usize, +} + +impl Default for PoolConfig { + fn default() -> PoolConfig { + PoolConfig { + accept_fee_base: 10, + max_pool_size: 50_000, + } + } +} + /// Placeholder: the data representing where we heard about a tx from. /// /// Used to make decisions based on transaction acceptance priority from @@ -105,6 +126,10 @@ pub enum PoolError { OutputNotFound, /// TODO - is this the right level of abstraction for pool errors? OutputSpent, + /// Transaction pool is over capacity, can't accept more transactions + OverCapacity, + /// Transaction fee is too low given its weight + LowFeeTransaction(u64), } /// Interface that the pool requires from a blockchain implementation.