mirror of
https://github.com/mimblewimble/grin.git
synced 2025-01-21 03:21: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 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()));
|
||||||
|
|
|
@ -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(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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};
|
||||||
|
|
|
@ -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(),
|
||||||
|
|
|
@ -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.
|
||||||
|
|
Loading…
Reference in a new issue