diff --git a/zebra-chain/src/transaction/unmined/zip317.rs b/zebra-chain/src/transaction/unmined/zip317.rs index d2b748a39d8..9923991764b 100644 --- a/zebra-chain/src/transaction/unmined/zip317.rs +++ b/zebra-chain/src/transaction/unmined/zip317.rs @@ -45,6 +45,23 @@ const MIN_BLOCK_PRODUCTION_SUBSTITUTE_FEE: i64 = 1; /// `block_unpaid_action_limit` in ZIP-317. pub const BLOCK_PRODUCTION_UNPAID_ACTION_LIMIT: u32 = 50; +/// The minimum fee per kilobyte for Zebra mempool transactions. +/// Also used as the minimum fee for a mempool transaction. +/// +/// Based on `DEFAULT_MIN_RELAY_TX_FEE` in `zcashd`: +/// +/// +/// This is a `usize` to simplify transaction size-based calculation code. +pub const MIN_MEMPOOL_TX_FEE_RATE: usize = 100; + +/// The fee cap for [`MIN_MEMPOOL_TX_FEE_RATE`] minimum required mempool fees. +/// +/// Based on `LEGACY_DEFAULT_FEE` in `zcashd`: +/// +/// +/// This is a `usize` to simplify transaction size-based calculation code. +pub const MEMPOOL_TX_FEE_REQUIREMENT_CAP: usize = 1000; + /// Returns the conventional fee for `transaction`, as defined by [ZIP-317]. /// /// [ZIP-317]: https://zips.z.cash/zip-0317#fee-calculation @@ -184,11 +201,24 @@ pub fn mempool_checks( // > with a maximum fee of 1000 zatoshis. In zcashd this is `DEFAULT_MIN_RELAY_TX_FEE`. // // - let limit = std::cmp::min( - 1000, - (f32::floor(100.0 * transaction_size as f32 / 1000.0)) as u64, + const KILOBYTE: usize = 1000; + + // This calculation can't overflow, because transactions are limited to 2 MB, + // and usize is at least 4 GB. + assert!( + MIN_MEMPOOL_TX_FEE_RATE + < usize::MAX / usize::try_from(MAX_BLOCK_BYTES).expect("constant fits in usize"), + "the fee rate multiplication must never overflow", ); - if miner_fee < Amount::::try_from(limit).expect("limit value is invalid") { + + let min_fee = (MIN_MEMPOOL_TX_FEE_RATE * transaction_size / KILOBYTE) + .clamp(MIN_MEMPOOL_TX_FEE_RATE, MEMPOOL_TX_FEE_REQUIREMENT_CAP); + let min_fee: u64 = min_fee + .try_into() + .expect("clamped value always fits in u64"); + let min_fee: Amount = min_fee.try_into().expect("clamped value is positive"); + + if miner_fee < min_fee { return Err(Error::FeeBelowMinimumRate); }