mirror of
https://github.com/mimblewimble/grin.git
synced 2025-01-20 19:11:08 +03:00
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:
parent
3dd1dde00b
commit
1e73e3aefc
6 changed files with 70 additions and 4 deletions
|
@ -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()));
|
||||
|
|
|
@ -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<pow::types::MinerConfig>,
|
||||
|
||||
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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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<T> {
|
||||
config: PoolConfig,
|
||||
/// All transactions in the pool
|
||||
pub transactions: HashMap<hash::Hash, Box<transaction::Transaction>>,
|
||||
/// The pool itself
|
||||
|
@ -49,8 +50,9 @@ where
|
|||
T: BlockChain,
|
||||
{
|
||||
/// Create a new transaction pool
|
||||
pub fn new(chain: Arc<T>) -> TransactionPool<T> {
|
||||
pub fn new(config: PoolConfig, chain: Arc<T>) -> TransactionPool<T> {
|
||||
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<DummyChainImpl>) -> TransactionPool<DummyChainImpl> {
|
||||
TransactionPool {
|
||||
config: PoolConfig{
|
||||
accept_fee_base: 0,
|
||||
max_pool_size: 10_000,
|
||||
},
|
||||
transactions: HashMap::new(),
|
||||
pool: Pool::empty(),
|
||||
orphans: Orphans::empty(),
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in a new issue