Skip to content

Commit 234ff3a

Browse files
authored
Merge pull request #43 from mintlayer/roy/mempool
Roy/mempool
2 parents a8e5cf1 + a78b0cf commit 234ff3a

File tree

15 files changed

+3410
-169
lines changed

15 files changed

+3410
-169
lines changed

Cargo.lock

Lines changed: 222 additions & 165 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ members = [
1616
"chainstate", # code on chainstate of blocks and transactions
1717
"script", # bitcoin script and its interfaces
1818
"logging", # logging engine and its interfaces
19+
"mempool", # mempool interface and implementation
1920
"p2p", # p2p communication interfaces and protocols
2021
"rpc", # rpc abstraction and implementation
2122
"serialization", # full featured serialization interfaces and implementations
@@ -40,6 +41,7 @@ default-members = [
4041
"chainstate",
4142
"script",
4243
"logging",
44+
"mempool",
4345
"p2p",
4446
"rpc",
4547
"serialization",
@@ -61,6 +63,7 @@ chainstate = { path = "chainstate"}
6163
chainstate-types = { path = "chainstate-types"}
6264
script = { path = "script"}
6365
logging = { path = "logging"}
66+
mempool = { path = "mempool"}
6467
p2p = { path = "p2p"}
6568
rpc = { path = "rpc"}
6669
serialization = { path = "serialization"}

common/src/chain/transaction.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
use crate::primitives::{Id, Idable};
1919
use serialization::{DirectDecode, DirectEncode, Encode};
20+
use thiserror::Error;
2021

2122
use crate::chain::transaction::transaction_v1::TransactionV1;
2223

@@ -54,8 +55,9 @@ impl Idable for Transaction {
5455
}
5556
}
5657

57-
#[derive(Debug, Clone)]
58+
#[derive(Error, Debug, Clone)]
5859
pub enum TransactionCreationError {
60+
#[error("An unknown error has occurred")]
5961
Unknown,
6062
}
6163

common/src/chain/transaction/input.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,15 @@ impl From<Id<Genesis>> for OutPointSourceId {
5252
}
5353
}
5454

55+
impl OutPointSourceId {
56+
pub fn get_tx_id(&self) -> Option<&Id<Transaction>> {
57+
match self {
58+
OutPointSourceId::Transaction(id) => Some(id),
59+
_ => None,
60+
}
61+
}
62+
}
63+
5564
#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode)]
5665
pub struct OutPoint {
5766
id: OutPointSourceId,

common/src/primitives/amount.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,35 @@ mod tests {
317317
assert_eq!(Amount { val: IntType::MAX } + Amount { val: 1 }, None);
318318
}
319319

320+
#[test]
321+
fn sum_some() {
322+
let amounts = vec![Amount { val: 1 }, Amount { val: 2 }, Amount { val: 3 }];
323+
assert_eq!(
324+
amounts.into_iter().sum::<Option<Amount>>(),
325+
Some(Amount { val: 6 })
326+
);
327+
}
328+
329+
#[test]
330+
fn sum_overflow() {
331+
let amounts = vec![
332+
Amount { val: 1 },
333+
Amount { val: 2 },
334+
Amount {
335+
val: IntType::MAX - 2,
336+
},
337+
];
338+
assert_eq!(amounts.into_iter().sum::<Option<Amount>>(), None);
339+
}
340+
341+
#[test]
342+
fn sum_empty() {
343+
assert_eq!(
344+
vec![].into_iter().sum::<Option<Amount>>(),
345+
Some(Amount::from_atoms(0))
346+
)
347+
}
348+
320349
#[test]
321350
fn sub_underflow() {
322351
assert_eq!(Amount { val: IntType::MIN } - Amount { val: 1 }, None);

deny.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ skip = [
4444
{name = "textwrap"},
4545
{name = "wasi"},
4646
{name = "winapi"},
47+
{name = "windows"},
4748
{name = "windows_aarch64_msvc" },
4849
{name = "windows_i686_gnu" },
4950
{name = "windows_i686_msvc" },
@@ -70,6 +71,7 @@ allow = [
7071
"LicenseRef-webpki",
7172
"WTFPL",
7273
"BSL-1.0",
74+
"Unicode-DFS-2016",
7375
"Unlicense",#this is a specific license rather than no license at all
7476
] #deny a license not in this set of licenses
7577

mempool/Cargo.toml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
[package]
2+
name = "mempool"
3+
version = "0.1.0"
4+
edition = "2021"
5+
license = "MIT"
6+
7+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
8+
9+
[dependencies]
10+
parity-scale-codec = {version = "3.1", features = ["derive", "chain-error"]}
11+
serialization = { path = '../serialization' }
12+
common = { path = '../common' }
13+
utils = { path = '../utils' }
14+
logging = { path = '../logging' }
15+
anyhow = "1.0"
16+
thiserror = "1.0"
17+
lazy_static = "1.4"
18+
mockall = "0.11.0"

mempool/src/error.rs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
use thiserror::Error;
2+
3+
use common::chain::transaction::Transaction;
4+
use common::chain::OutPoint;
5+
use common::primitives::amount::Amount;
6+
use common::primitives::Id;
7+
use common::primitives::H256;
8+
9+
#[derive(Debug, Error)]
10+
pub enum Error {
11+
#[error("Mempool is full")]
12+
MempoolFull,
13+
#[error(transparent)]
14+
TxValidationError(#[from] TxValidationError),
15+
}
16+
17+
#[derive(Debug, Error)]
18+
pub enum TxValidationError {
19+
#[error("Transaction has no inputs.")]
20+
NoInputs,
21+
#[error("Transaction has no outputs.")]
22+
NoOutputs,
23+
#[error("Transaction has duplicate inputs.")]
24+
DuplicateInputs,
25+
#[error("Outpoint not found: {outpoint:?}")]
26+
OutPointNotFound {
27+
outpoint: OutPoint,
28+
tx_id: Id<Transaction>,
29+
},
30+
#[error("Transaction exceeds the maximum block size.")]
31+
ExceedsMaxBlockSize,
32+
#[error("Transaction already exists in the mempool.")]
33+
TransactionAlreadyInMempool,
34+
#[error("Transaction conflicts with another, irreplaceable transaction.")]
35+
ConflictWithIrreplaceableTransaction,
36+
#[error("The sum of the transaction's inputs' values overflows.")]
37+
InputValuesOverflow,
38+
#[error("The sum of the transaction's outputs' values overflows.")]
39+
OutputValuesOverflow,
40+
#[error("The sum of the transaction's inputs is smaller than the sum of its outputs.")]
41+
InputsBelowOutputs,
42+
#[error("Replacement transaction has fee lower than the original. Replacement fee is {replacement_fee:?}, original fee {original_fee:?}")]
43+
ReplacementFeeLowerThanOriginal {
44+
replacement_tx: H256,
45+
replacement_fee: Amount,
46+
original_tx: H256,
47+
original_fee: Amount,
48+
},
49+
#[error("Transaction would require too many replacements.")]
50+
TooManyPotentialReplacements,
51+
#[error("Replacement transaction spends an unconfirmed input which was not spent by any of the original transactions.")]
52+
SpendsNewUnconfirmedOutput,
53+
#[error("The sum of the fees of this transaction's conflicts overflows.")]
54+
ConflictsFeeOverflow,
55+
#[error("Transaction pays a fee that is lower than the fee of its conflicts with their descendants.")]
56+
TransactionFeeLowerThanConflictsWithDescendants,
57+
#[error("Underflow in computing transaction's additional fees.")]
58+
AdditionalFeesUnderflow,
59+
#[error("Transaction does not pay sufficient fees to be relayed.")]
60+
InsufficientFeesToRelay { tx_fee: Amount, relay_fee: Amount },
61+
#[error("Replacement transaction does not pay enough for its bandwidth.")]
62+
InsufficientFeesToRelayRBF,
63+
#[error("Rolling fee threshold not met.")]
64+
RollingFeeThresholdNotMet { minimum_fee: Amount, tx_fee: Amount },
65+
#[error("Overflow encountered while updating ancestor fee.")]
66+
AncestorFeeUpdateOverflow,
67+
#[error("Transaction is a descendant of expired transaction.")]
68+
DescendantOfExpiredTransaction,
69+
#[error("Internal Error.")]
70+
InternalError,
71+
}

mempool/src/feerate.rs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
use common::primitives::amount::Amount;
2+
3+
lazy_static::lazy_static! {
4+
pub(crate) static ref INCREMENTAL_RELAY_FEE_RATE: FeeRate = FeeRate::new(Amount::from_atoms(1000));
5+
pub(crate) static ref INCREMENTAL_RELAY_THRESHOLD: FeeRate = FeeRate::new(Amount::from_atoms(500));
6+
}
7+
8+
// TODO we should reconsider using Amount and define functions for the specific operations required
9+
// on FeeRate. Previous attempts to do this did not pan out, but this should be revisited to avoid
10+
// dangerous arithmetic operations
11+
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
12+
pub(crate) struct FeeRate {
13+
atoms_per_kb: u128,
14+
}
15+
16+
impl FeeRate {
17+
pub(crate) fn new(atoms_per_kb: Amount) -> Self {
18+
Self {
19+
atoms_per_kb: atoms_per_kb.into_atoms(),
20+
}
21+
}
22+
23+
pub(crate) fn from_total_tx_fee(total_tx_fee: Amount, tx_size: usize) -> Self {
24+
Self {
25+
atoms_per_kb: Self::div_up(1000 * total_tx_fee.into_atoms(), tx_size),
26+
}
27+
}
28+
29+
pub(crate) fn compute_fee(&self, size: usize) -> Amount {
30+
let size = u128::try_from(size).expect("compute_fee conversion");
31+
// +999 for ceil operation
32+
Amount::from_atoms((self.atoms_per_kb * size + 999) / 1000)
33+
}
34+
35+
pub(crate) fn atoms_per_kb(&self) -> u128 {
36+
self.atoms_per_kb
37+
}
38+
39+
// TODO Use NonZeroUsize for divisor
40+
fn div_up(dividend: u128, divisor: usize) -> u128 {
41+
debug_assert!(divisor != 0);
42+
let divisor = u128::try_from(divisor).expect("div_up conversion");
43+
(dividend + divisor - 1) / divisor
44+
}
45+
}
46+
47+
impl std::ops::Add for FeeRate {
48+
type Output = FeeRate;
49+
fn add(self, other: Self) -> Self::Output {
50+
let atoms_per_kb = self.atoms_per_kb + other.atoms_per_kb;
51+
FeeRate { atoms_per_kb }
52+
}
53+
}
54+
55+
#[cfg(test)]
56+
mod tests {
57+
use super::*;
58+
use std::num::NonZeroU128;
59+
60+
impl std::ops::Div<NonZeroU128> for FeeRate {
61+
type Output = FeeRate;
62+
fn div(self, rhs: NonZeroU128) -> Self::Output {
63+
FeeRate {
64+
atoms_per_kb: self.atoms_per_kb / rhs,
65+
}
66+
}
67+
}
68+
69+
#[test]
70+
fn test_div_up() {
71+
let fee = 7;
72+
let tx_size = usize::MAX;
73+
let rate = FeeRate::div_up(fee, tx_size);
74+
assert_eq!(rate, 1);
75+
}
76+
}

mempool/src/lib.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#![deny(clippy::clone_on_ref_ptr)]
2+
3+
pub mod error;
4+
mod feerate;
5+
pub mod pool;
6+
7+
pub use error::Error as MempoolError;

0 commit comments

Comments
 (0)