use self::core::consensus;
use self::core::core::block;
use self::core::core::committed;
use self::core::core::hash::Hash;
use self::core::core::transaction::{self, Transaction};
use self::core::core::{BlockHeader, BlockSums, Inputs, OutputIdentifier};
use chrono::prelude::*;
use mwc_core as core;
use mwc_keychain as keychain;
const DANDELION_EPOCH_SECS: u16 = 600;
const DANDELION_EMBARGO_SECS: u16 = 180;
const DANDELION_AGGREGATION_SECS: u16 = 30;
const DANDELION_STEM_PROBABILITY: u8 = 90;
const DANDELION_ALWAYS_STEM_OUR_TXS: bool = true;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct DandelionConfig {
#[serde(default = "default_dandelion_epoch_secs")]
pub epoch_secs: u16,
#[serde(default = "default_dandelion_embargo_secs")]
pub embargo_secs: u16,
#[serde(default = "default_dandelion_aggregation_secs")]
pub aggregation_secs: u16,
#[serde(default = "default_dandelion_stem_probability")]
pub stem_probability: u8,
#[serde(default = "default_dandelion_always_stem_our_txs")]
pub always_stem_our_txs: bool,
}
impl Default for DandelionConfig {
fn default() -> DandelionConfig {
DandelionConfig {
epoch_secs: default_dandelion_epoch_secs(),
embargo_secs: default_dandelion_embargo_secs(),
aggregation_secs: default_dandelion_aggregation_secs(),
stem_probability: default_dandelion_stem_probability(),
always_stem_our_txs: default_dandelion_always_stem_our_txs(),
}
}
}
fn default_dandelion_epoch_secs() -> u16 {
DANDELION_EPOCH_SECS
}
fn default_dandelion_embargo_secs() -> u16 {
DANDELION_EMBARGO_SECS
}
fn default_dandelion_aggregation_secs() -> u16 {
DANDELION_AGGREGATION_SECS
}
fn default_dandelion_stem_probability() -> u8 {
DANDELION_STEM_PROBABILITY
}
fn default_dandelion_always_stem_our_txs() -> bool {
DANDELION_ALWAYS_STEM_OUR_TXS
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct PoolConfig {
#[serde(default = "default_tx_fee_base")]
pub tx_fee_base: Option<u64>,
#[serde(default = "default_reorg_cache_timeout")]
pub reorg_cache_timeout: i64,
#[serde(default = "default_max_pool_size")]
pub max_pool_size: usize,
#[serde(default = "default_max_stempool_size")]
pub max_stempool_size: usize,
#[serde(default = "default_mineable_max_weight")]
pub mineable_max_weight: u64,
}
impl Default for PoolConfig {
fn default() -> PoolConfig {
PoolConfig {
tx_fee_base: default_tx_fee_base(),
max_pool_size: default_max_pool_size(),
reorg_cache_timeout: default_reorg_cache_timeout(),
max_stempool_size: default_max_stempool_size(),
mineable_max_weight: default_mineable_max_weight(),
}
}
}
pub fn default_tx_fee_base() -> Option<u64> {
None
}
fn default_max_pool_size() -> usize {
150_000
}
fn default_reorg_cache_timeout() -> i64 {
1440 }
fn default_max_stempool_size() -> usize {
50_000
}
fn default_mineable_max_weight() -> u64 {
consensus::MAX_BLOCK_WEIGHT
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct PoolEntry {
pub src: TxSource,
pub tx_at: DateTime<Utc>,
pub tx: Transaction,
}
impl PoolEntry {
pub fn new(tx: Transaction, src: TxSource) -> PoolEntry {
PoolEntry {
src,
tx_at: Utc::now(),
tx,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
pub enum TxSource {
PushApi,
Broadcast,
Fluff,
EmbargoExpired,
Deaggregate,
}
impl TxSource {
pub fn is_pushed(&self) -> bool {
match self {
TxSource::PushApi => true,
_ => false,
}
}
}
#[derive(Debug, thiserror::Error, PartialEq)]
pub enum PoolError {
#[error("Tx Pool Invalid Tx {0}")]
InvalidTx(transaction::Error),
#[error("Tx Pool Invalid Block {0}")]
InvalidBlock(block::Error),
#[error("Tx Pool Keychain error {0}")]
Keychain(keychain::Error),
#[error("Tx Pool Committed error {0}")]
Committed(committed::Error),
#[error("Tx Pool Immature transaction")]
ImmatureTransaction,
#[error("Tx Pool Immature coinbase")]
ImmatureCoinbase,
#[error("Tx Pool Dandelion error")]
DandelionError,
#[error("Tx Pool Over capacity")]
OverCapacity,
#[error("Tx Pool Low fee transaction {0}")]
LowFeeTransaction(u64),
#[error("Tx Pool Duplicate commitment")]
DuplicateCommitment,
#[error("Tx Pool Duplicate kernel or duplicate output to spent, {0}")]
DuplicateKernelOrDuplicateSpent(String),
#[error("Tx Pool Duplicate tx")]
DuplicateTx,
#[error("NRD kernel pre-HF3")]
NRDKernelPreHF3,
#[error("NRD kernel not enabled")]
NRDKernelNotEnabled,
#[error("NRD kernel relative height")]
NRDKernelRelativeHeight,
#[error("Tx Pool General error {0}")]
Other(String),
}
impl From<transaction::Error> for PoolError {
fn from(e: transaction::Error) -> PoolError {
match e {
transaction::Error::InvalidNRDRelativeHeight => PoolError::NRDKernelRelativeHeight,
e @ _ => PoolError::InvalidTx(e),
}
}
}
impl From<block::Error> for PoolError {
fn from(e: block::Error) -> PoolError {
PoolError::InvalidBlock(e)
}
}
impl From<keychain::Error> for PoolError {
fn from(e: keychain::Error) -> PoolError {
PoolError::Keychain(e)
}
}
impl From<committed::Error> for PoolError {
fn from(e: committed::Error) -> PoolError {
PoolError::Committed(e)
}
}
pub trait BlockChain: Sync + Send {
fn verify_coinbase_maturity(&self, inputs: &Inputs) -> Result<(), PoolError>;
fn verify_tx_lock_height(&self, tx: &transaction::Transaction) -> Result<(), PoolError>;
fn validate_tx(&self, tx: &Transaction) -> Result<(), PoolError>;
fn validate_inputs(&self, inputs: &Inputs) -> Result<Vec<OutputIdentifier>, PoolError>;
fn chain_head(&self) -> Result<BlockHeader, PoolError>;
fn get_block_header(&self, hash: &Hash) -> Result<BlockHeader, PoolError>;
fn get_block_sums(&self, hash: &Hash) -> Result<BlockSums, PoolError>;
fn replay_attack_check(&self, tx: &Transaction) -> Result<(), PoolError>;
}
pub trait PoolAdapter: Send + Sync {
fn tx_accepted(&self, entry: &PoolEntry, height: u64);
fn stem_tx_accepted(&self, entry: &PoolEntry) -> Result<(), PoolError>;
}
#[allow(dead_code)]
pub struct NoopPoolAdapter {}
impl PoolAdapter for NoopPoolAdapter {
fn tx_accepted(&self, _entry: &PoolEntry, _height: u64) {}
fn stem_tx_accepted(&self, _entry: &PoolEntry) -> Result<(), PoolError> {
Ok(())
}
}