Skip to content

Commit

Permalink
common: Introduce DisplayAmount
Browse files Browse the repository at this point in the history
Introduce a version of `DecimalAmount` that has equality comparison so
it can be easily used in error types. See doc comments for more details.
  • Loading branch information
iljakuklic committed Feb 2, 2024
1 parent 79ecb2b commit c1d86ad
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 10 deletions.
64 changes: 64 additions & 0 deletions common/src/primitives/decimal_amount.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,70 @@ pub enum ParseError {
IntParse(#[from] std::num::ParseIntError),
}

/// Just like [DecimalAmount] but useful in error types, picking a different set of trade-offs.
///
/// While [DecimalAmount] is intended to be used as a type to serialize/deserialize amounts to/from
/// a string, [DisplayAmount] is for printing only. It has an equality comparison (comparing the
/// string representation). To prevent the result of the comparison from affecting subsequent
/// [Amount] calculations, there is no easy way of converting `DisplayAmount` to `Amount`.
///
/// To further encourage debuggability, we only provide the `from_amount_full` constructor for
/// converting from `Amount` while `from_amount_minimal` is omitted. The full variant keeps all the
/// trailing zeros, making it possible to see the amount both in coin/token units and in atoms.
///
/// This is most useful in error types where we want to display the amount and subsequently compare
/// the errors for equality in tests.
#[derive(Clone, Copy)]
pub struct DisplayAmount(DecimalAmount);

impl DisplayAmount {
/// Convert from [DecimalAmount]
pub const fn from_decimal_amount(value: DecimalAmount) -> Self {
Self(value)
}

/// Convert from integer with no decimals
pub const fn from_uint_integral(number: u128) -> Self {
Self(DecimalAmount::from_uint_integral(number))
}

/// Convert from integer, interpreting the last N digits as the fractional part
pub const fn from_uint_decimal(mantissa: UnsignedIntType, decimals: u8) -> Self {
Self(DecimalAmount::from_uint_decimal(mantissa, decimals))
}

/// Convert from [Amount], keeping all decimal digits
pub const fn from_amount_full(amount: Amount, decimals: u8) -> Self {
Self(DecimalAmount::from_amount_full(amount, decimals))
}
}

impl std::cmp::PartialEq for DisplayAmount {
fn eq(&self, other: &Self) -> bool {
self.0.is_same(&other.0)
}
}

impl std::cmp::Eq for DisplayAmount {}

impl From<DecimalAmount> for DisplayAmount {
fn from(value: DecimalAmount) -> Self {
Self::from_decimal_amount(value)
}
}

impl std::fmt::Display for DisplayAmount {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}

impl std::fmt::Debug for DisplayAmount {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}

#[cfg(test)]
mod test {
use super::*;
Expand Down
16 changes: 11 additions & 5 deletions mempool/src/error/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use thiserror::Error;

use common::{
chain::{GenBlock, Transaction},
primitives::{Id, H256},
primitives::{decimal_amount::DisplayAmount, Id, H256},
};

use crate::pool::fee::Fee;
Expand Down Expand Up @@ -77,12 +77,18 @@ pub enum MempoolPolicyError {
TransactionFeeLowerThanConflictsWithDescendants,
#[error("Underflow in computing transaction's additional fees.")]
AdditionalFeesUnderflow,
#[error("Transaction does not pay sufficient fees to be relayed (tx_fee: {tx_fee:?}, min_relay_fee: {min_relay_fee:?}).")]
InsufficientFeesToRelay { tx_fee: Fee, min_relay_fee: Fee },
#[error("Transaction does not pay sufficient fees to be relayed (tx_fee: {tx_fee}, min_relay_fee: {min_relay_fee}).")]
InsufficientFeesToRelay {
tx_fee: DisplayAmount,
min_relay_fee: DisplayAmount,
},
#[error("Replacement transaction does not pay enough for its bandwidth.")]
InsufficientFeesToRelayRBF,
#[error("Rolling fee threshold not met.")]
RollingFeeThresholdNotMet { minimum_fee: Fee, tx_fee: Fee },
#[error("Rolling fee threshold not met (fee is {tx_fee}, minimum {minimum_fee}).")]
RollingFeeThresholdNotMet {
minimum_fee: DisplayAmount,
tx_fee: DisplayAmount,
},
#[error("Overflow encountered while computing fee with ancestors")]
AncestorFeeOverflow,
#[error("Overflow encountered while updating ancestor fee.")]
Expand Down
12 changes: 7 additions & 5 deletions mempool/src/pool/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ use common::{
block::timestamp::BlockTimestamp, Block, ChainConfig, GenBlock, SignedTransaction,
Transaction,
},
primitives::{amount::Amount, time::Time, BlockHeight, Id},
primitives::{amount::Amount, decimal_amount::DisplayAmount, time::Time, BlockHeight, Id},
time_getter::TimeGetter,
};
use logging::log;
Expand Down Expand Up @@ -529,14 +529,15 @@ impl<M: MemoryUsageEstimator> Mempool<M> {
}

fn pays_minimum_mempool_fee(&self, tx: &TxEntryWithFee) -> Result<(), MempoolPolicyError> {
let decimals = self.chain_config.coin_decimals();
let tx_fee = tx.fee();
let minimum_fee = self.get_update_minimum_mempool_fee(tx.transaction())?;
log::debug!("pays_minimum_mempool_fee tx_fee = {tx_fee:?}, minimum_fee = {minimum_fee:?}");
ensure!(
tx_fee >= minimum_fee,
MempoolPolicyError::RollingFeeThresholdNotMet {
minimum_fee,
tx_fee,
minimum_fee: DisplayAmount::from_amount_full(minimum_fee.into(), decimals),
tx_fee: DisplayAmount::from_amount_full(tx_fee.into(), decimals),
}
);
Ok(())
Expand All @@ -558,14 +559,15 @@ impl<M: MemoryUsageEstimator> Mempool<M> {
}

fn pays_minimum_relay_fees(&self, tx: &TxEntryWithFee) -> Result<(), MempoolPolicyError> {
let decimals = self.chain_config.coin_decimals();
let tx_fee = tx.fee();
let min_relay_fee = self.get_minimum_relay_fee(tx.transaction())?;
log::debug!("tx_fee: {:?}, min_relay_fee: {:?}", tx_fee, min_relay_fee);
ensure!(
tx_fee >= min_relay_fee,
MempoolPolicyError::InsufficientFeesToRelay {
tx_fee,
min_relay_fee
tx_fee: DisplayAmount::from_amount_full(tx_fee.into(), decimals),
min_relay_fee: DisplayAmount::from_amount_full(min_relay_fee.into(), decimals),
}
);
Ok(())
Expand Down

0 comments on commit c1d86ad

Please sign in to comment.