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.
This commit is contained in:
Ignotus Peverell 2017-10-07 18:24:11 +00:00
parent 3dd1dde00b
commit 1e73e3aefc
No known key found for this signature in database
GPG key ID: 99CD25F39F8F8211
6 changed files with 70 additions and 4 deletions

View file

@ -80,7 +80,7 @@ impl Server {
let pool_adapter = Arc::new(PoolToChainAdapter::new()); let pool_adapter = Arc::new(PoolToChainAdapter::new());
let tx_pool = Arc::new(RwLock::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())); let chain_adapter = Arc::new(ChainToPoolAndNetAdapter::new(tx_pool.clone()));

View file

@ -17,6 +17,7 @@ use std::convert::From;
use api; use api;
use chain; use chain;
use p2p; use p2p;
use pool;
use store; use store;
use pow; use pow;
use core::global::MiningParameterMode; use core::global::MiningParameterMode;
@ -97,6 +98,8 @@ pub struct ServerConfig {
/// Configuration for the mining daemon /// Configuration for the mining daemon
pub mining_config: Option<pow::types::MinerConfig>, pub mining_config: Option<pow::types::MinerConfig>,
pub pool_config: pool::PoolConfig,
} }
impl Default for ServerConfig { impl Default for ServerConfig {
@ -110,6 +113,7 @@ impl Default for ServerConfig {
p2p_config: Some(p2p::P2PConfig::default()), p2p_config: Some(p2p::P2PConfig::default()),
mining_config: Some(pow::types::MinerConfig::default()), mining_config: Some(pow::types::MinerConfig::default()),
mining_parameter_mode: Some(MiningParameterMode::Production), mining_parameter_mode: Some(MiningParameterMode::Production),
pool_config: pool::PoolConfig::default(),
} }
} }
} }

View file

@ -10,6 +10,8 @@ grin_keychain = { path = "../keychain" }
grin_store = { path = "../store" } grin_store = { path = "../store" }
grin_p2p = { path = "../p2p" } grin_p2p = { path = "../p2p" }
secp256k1zkp = { git = "https://github.com/mimblewimble/rust-secp256k1-zkp" } secp256k1zkp = { git = "https://github.com/mimblewimble/rust-secp256k1-zkp" }
serde = "~1.0.8"
serde_derive = "~1.0.8"
time = "^0.1" time = "^0.1"
rand = "0.3" rand = "0.3"
log = "0.3" log = "0.3"

View file

@ -28,6 +28,9 @@ mod pool;
extern crate time; extern crate time;
extern crate rand; extern crate rand;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate log; extern crate log;
extern crate blake2_rfc as blake2; extern crate blake2_rfc as blake2;
extern crate grin_core as core; extern crate grin_core as core;
@ -35,4 +38,4 @@ extern crate grin_keychain as keychain;
extern crate secp256k1zkp as secp; extern crate secp256k1zkp as secp;
pub use pool::TransactionPool; pub use pool::TransactionPool;
pub use types::{BlockChain, TxSource, PoolError}; pub use types::{BlockChain, TxSource, PoolError, PoolConfig};

View file

@ -14,7 +14,7 @@
//! Top-level Pool type, methods, and tests //! Top-level Pool type, methods, and tests
use types::{Pool, BlockChain, Orphans, Parent, PoolError, TxSource, TransactionGraphContainer}; use types::*;
pub use graph; pub use graph;
use core::core::transaction; use core::core::transaction;
@ -32,6 +32,7 @@ use std::collections::HashMap;
/// The transactions HashMap holds ownership of all transactions in the pool, /// The transactions HashMap holds ownership of all transactions in the pool,
/// keyed by their transaction hash. /// keyed by their transaction hash.
pub struct TransactionPool<T> { pub struct TransactionPool<T> {
config: PoolConfig,
/// All transactions in the pool /// All transactions in the pool
pub transactions: HashMap<hash::Hash, Box<transaction::Transaction>>, pub transactions: HashMap<hash::Hash, Box<transaction::Transaction>>,
/// The pool itself /// The pool itself
@ -49,8 +50,9 @@ where
T: BlockChain, T: BlockChain,
{ {
/// Create a new transaction pool /// Create a new transaction pool
pub fn new(chain: Arc<T>) -> TransactionPool<T> { pub fn new(config: PoolConfig, chain: Arc<T>) -> TransactionPool<T> {
TransactionPool { TransactionPool {
config: config,
transactions: HashMap::new(), transactions: HashMap::new(),
pool: Pool::empty(), pool: Pool::empty(),
orphans: Orphans::empty(), orphans: Orphans::empty(),
@ -127,6 +129,12 @@ where
_: TxSource, _: TxSource,
tx: transaction::Transaction, tx: transaction::Transaction,
) -> Result<(), PoolError> { ) -> 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. // Making sure the transaction is valid before anything else.
let secp = secp::Secp256k1::with_caps(secp::ContextFlag::Commit); let secp = secp::Secp256k1::with_caps(secp::ContextFlag::Commit);
tx.validate(&secp).map_err(|_e| PoolError::Invalid)?; tx.validate(&secp).map_err(|_e| PoolError::Invalid)?;
@ -551,6 +559,26 @@ where
.map(|x| self.transactions.get(x).unwrap().clone()) .map(|x| self.transactions.get(x).unwrap().clone())
.collect() .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)] #[cfg(test)]
@ -1027,6 +1055,10 @@ mod tests {
fn test_setup(dummy_chain: &Arc<DummyChainImpl>) -> TransactionPool<DummyChainImpl> { fn test_setup(dummy_chain: &Arc<DummyChainImpl>) -> TransactionPool<DummyChainImpl> {
TransactionPool { TransactionPool {
config: PoolConfig{
accept_fee_base: 0,
max_pool_size: 10_000,
},
transactions: HashMap::new(), transactions: HashMap::new(),
pool: Pool::empty(), pool: Pool::empty(),
orphans: Orphans::empty(), orphans: Orphans::empty(),

View file

@ -28,6 +28,27 @@ use core::core::block;
use core::core::transaction; use core::core::transaction;
use core::core::hash; 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. /// Placeholder: the data representing where we heard about a tx from.
/// ///
/// Used to make decisions based on transaction acceptance priority from /// Used to make decisions based on transaction acceptance priority from
@ -105,6 +126,10 @@ pub enum PoolError {
OutputNotFound, OutputNotFound,
/// TODO - is this the right level of abstraction for pool errors? /// TODO - is this the right level of abstraction for pool errors?
OutputSpent, 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. /// Interface that the pool requires from a blockchain implementation.