cfx_packing_pool/
pool_config.rs

1use cfx_math::nth_inv_root;
2use cfx_types::U256;
3use typenum::{U12, U15, U2, U3, U4, U5};
4
5use crate::transaction::PackingPoolTransaction;
6use malloc_size_of_derive::MallocSizeOf;
7
8/// Configuration settings for a [`PackingBatch`][crate::PackingBatch].
9#[derive(Default, MallocSizeOf, Clone, Copy)]
10pub struct PackingPoolConfig {
11    /// The maximum gas limit for a packing batch. If there is only a single
12    /// transaction, this limit is not enforced.
13    pub(crate) address_gas_limit: U256,
14    ///  The maximum number of transactions allowed in a packing batch.
15    pub(crate) address_tx_count: usize,
16    /// The degree parameter used in the random packing algorithm, with
17    /// supported values ranging from 1 to 5.
18    pub(crate) loss_ratio_degree: u8,
19}
20
21impl PackingPoolConfig {
22    pub fn new(
23        address_gas_limit: U256, address_tx_count: usize, loss_ratio_degree: u8,
24    ) -> Self {
25        assert!(loss_ratio_degree > 0 && loss_ratio_degree <= 5);
26        Self {
27            address_gas_limit,
28            address_tx_count,
29            loss_ratio_degree,
30        }
31    }
32
33    #[cfg(test)]
34    pub fn new_for_test() -> Self { Self::new(3_000_000.into(), 20, 4) }
35
36    /// Calculates the longest acceptable prefix of transactions in the provided
37    /// list.
38    ///
39    /// This function determines the maximum number of consecutive transactions
40    /// starting from the beginning of `txs` that can be accepted based on
41    /// their cumulative gas limit and other criteria. It returns the index
42    /// of the first transaction that is not acceptable and the total gas limit
43    /// of the acceptable portion.
44    ///
45    /// # Parameters
46    /// - `txs`: A reference to a vector of transactions to be evaluated.
47    /// - `replaced`: An optional parameter that, if provided, indicates a
48    ///   specific transaction and its position in `txs` that should be
49    ///   considered as replaced for the purpose of this calculation. This is
50    ///   used to simulate the effect of replacing a transaction within `txs`.
51    ///
52    /// # Returns
53    /// Returns a tuple containing:
54    /// - The index of the first transaction in `txs` that is not acceptable. If
55    ///   all transactions are acceptable, this will be the length of `txs`.
56    /// - The total gas limit of the acceptable portion of `txs`.
57    #[inline]
58    pub(crate) fn check_acceptable_batch<TX: PackingPoolTransaction>(
59        &self, txs: &Vec<TX>, replaced: Option<(&TX, usize)>,
60    ) -> (usize, U256) {
61        let mut total_gas_limit = U256::zero();
62        for (idx, tx) in txs.iter().enumerate() {
63            if idx >= self.address_tx_count {
64                return (idx, total_gas_limit);
65            }
66
67            let tx_gas = replaced
68                .filter(|(_, i)| idx == *i)
69                .map_or(tx.gas_limit(), |(r, _)| r.gas_limit());
70
71            if total_gas_limit + tx_gas > self.address_gas_limit {
72                if idx == 0 {
73                    // If there is only one tx, it can exceed address_gas_limit
74                    return (1, tx_gas);
75                } else {
76                    return (idx, total_gas_limit);
77                }
78            }
79            total_gas_limit += tx_gas;
80        }
81        (txs.len(), total_gas_limit)
82    }
83
84    /// Calculates the loss ratio for the random packing algorithm.
85    ///
86    /// The loss ratio is defined as the reciprocal of `gas_price` raised to the
87    /// power of the `degree` (a configuration parameter). Since this value
88    /// is less than 1, the function represents 1 as 2^128, thereby
89    /// preserving 128 binary digits of precision in the result.
90    ///
91    /// Note that the precision of the returned value is limited:
92    /// - For `degree = 5`, the precision is 12 bits.
93    /// - For other values of `degree`, the precision is 15 bits.
94    /// This limited precision is by design, as the packing pool does not
95    /// require high precision and this approach helps in saving
96    /// computational resources.
97    pub(crate) fn loss_ratio(&self, gas_price: U256) -> U256 {
98        if gas_price.is_zero() {
99            return U256::one() << 128;
100        }
101
102        (match self.loss_ratio_degree {
103            1 => U256::MAX / gas_price,
104            2 => nth_inv_root::<U2, U15>(gas_price),
105            3 => nth_inv_root::<U3, U15>(gas_price),
106            4 => nth_inv_root::<U4, U15>(gas_price),
107            5 => nth_inv_root::<U5, U12>(gas_price),
108            _ => unreachable!(),
109        }) >> 128
110    }
111}