From 8ffe5aa1b163cc4807a6174e77f5480969fa2e78 Mon Sep 17 00:00:00 2001 From: Joe Betz Date: Fri, 3 May 2024 17:39:25 +0200 Subject: [PATCH] Interpret fee rates in reference fee unit (#21) * Introduce CValue for distinguishing amounts from values * Remove random character * Define insertion operator for CValue * Move CValue to separate module * Remove consensus changes * Remove CValue usage for fee rates, restrict to mempool * More cleanup * Use CValue for descendant and ancestor fee updates * More mempool fixes * Remove unused include * Fix variable shadowing * More conversions * Factor in fee asset when validating package fees * More conversions * Convert dust relay fee from RFU * More cleanup * Cleanup * Don't factor in asset in when calculating dust threshold * Convert package fees * Return CValue from CalculateExchangeValue * Use CValue for reverse exchange rate conversion * Update method documentation * Rename currency conversion methods * Fix typo * Add justification for RFU to comments * Use accessor instead of public instance variable unpacking CValue * Use GetValue() in fee rate conversion * Only apply dust check to outputs denominated in same asset as fee * Interpret coin selection effective feerate in RFU * Change GetModifiedFee() method to use RFU for summing with ancestor and descendant transactions * Add functional test for "fee_rate" parameter in fundrawtransaction RPC and -mintxfee node configuration parameter * Normalize fees to RFU during fee estimation * Lint fixes * More linting fixes * More linting * Apply method renams to fee amount conversions * Add any asset fee rates test to test runner * More amount conversion fixes * Add compiler switch for currency constants * Add new configuration flag to documentation * Apply fixes to fee estimation functional test from https://github.com/ElementsProject/elements/pull/1298 * Add tests for paytxfee parameter * Cleanup * Fix compiler flag * Add tests for blockmintxfee * Remove whitespace * Add comments clarifying what's being demonstrated * Remove nFeeValue, recompute as needed * Revert "Remove nFeeValue, recompute as needed" This reverts commit ab3a67b51c8e956562842118e50d55d31ceb0fbd. * Move No Coin configuration documentation to separate line * Add constant for full name of currency atom * Fix constant reference * Fix typo --- README.md | 5 +- configure.ac | 13 ++ src/Makefile.am | 1 + src/exchangerates.cpp | 26 ++- src/exchangerates.h | 22 ++- src/node/miner.cpp | 4 +- src/node/miner.h | 6 +- src/node/transaction.cpp | 4 +- src/policy/feerate.cpp | 13 ++ src/policy/feerate.h | 14 ++ src/policy/fees.cpp | 2 +- src/policy/policy.cpp | 4 +- src/policy/value.h | 65 ++++++++ src/rpc/blockchain.cpp | 20 +-- src/rpc/mining.cpp | 2 +- src/txmempool.cpp | 31 ++-- src/txmempool.h | 31 ++-- src/validation.cpp | 18 +- src/wallet/coinselection.cpp | 2 +- src/wallet/fees.cpp | 4 +- src/wallet/spend.cpp | 13 +- .../functional/feature_any_asset_fee_rates.py | 156 ++++++++++++++++++ test/functional/feature_fee_estimation.py | 9 +- test/functional/test_runner.py | 1 + test/lint/lint-circular-dependencies.sh | 4 + 25 files changed, 384 insertions(+), 86 deletions(-) create mode 100644 src/policy/value.h create mode 100755 test/functional/feature_any_asset_fee_rates.py diff --git a/README.md b/README.md index f34909e143..5df22efebd 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,10 @@ To speed up the build if not necessary, disable bench and tests in configure: ```bash ./configure --without-gui --without-natpmp --without-miniupnpc --disable-bench --disable-tests ``` +To configure RPC documentation to denominate fee rates using RFU and rfa instead of BTC and sat: +```bash +./configure --enable-any-asset-fees +``` Modes ----- @@ -106,4 +110,3 @@ https://github.com/ElementsProject/elementsproject.github.io Secure Reporting ------------------ See [our vulnerability reporting guide](SECURITY.md) - diff --git a/configure.ac b/configure.ac index aa9008c641..4b4b9a4029 100644 --- a/configure.ac +++ b/configure.ac @@ -266,6 +266,12 @@ AC_ARG_ENABLE([liquid], [liquid_build=yes], [liquid_build=no]) +AC_ARG_ENABLE([any_asset_fees], + [AS_HELP_STRING([--enable-any-asset-fees], + [Enable build that uses constants for any asset fees feature])], + [any_asset_fees=yes], + [any_asset_fees=no]) + if test "$use_asm" = "yes"; then AC_DEFINE([USE_ASM], [1], [Define this symbol to build in assembly routines]) fi @@ -273,6 +279,11 @@ fi if test "$liquid_build" = "yes"; then AC_DEFINE(LIQUID, 1, [Define this symbol for Liquid builds]) fi + +if test "$any_asset_fees" = "yes"; then + AC_DEFINE(ANY_ASSET_FEES, 1, [Define this symbol to modify constants for any asset fees feature]) +fi + AC_ARG_ENABLE([zmq], [AS_HELP_STRING([--disable-zmq], [disable ZMQ notifications])], @@ -1833,6 +1844,7 @@ AM_CONDITIONAL([WORDS_BIGENDIAN], [test "$ac_cv_c_bigendian" = "yes"]) AM_CONDITIONAL([USE_NATPMP], [test "$use_natpmp" = "yes"]) AM_CONDITIONAL([USE_UPNP], [test "$use_upnp" = "yes"]) AM_CONDITIONAL([LIQUID], [test "$liquid_build" = "yes"]) +AM_CONDITIONAL([ANY_ASSET_FEES], [test "$any_asset_fees" = "yes"]) dnl for minisketch AM_CONDITIONAL([ENABLE_CLMUL], [test "$enable_clmul" = "yes"]) @@ -1986,6 +1998,7 @@ echo " with upnp = $use_upnp" echo " with natpmp = $use_natpmp" echo " use asm = $use_asm" echo " liquid_build = $liquid_build" +echo " any_asset_fees = $any_asset_fees" echo " USDT tracing = $use_usdt" echo " sanitizers = $use_sanitizers" echo " debug enabled = $enable_debug" diff --git a/src/Makefile.am b/src/Makefile.am index 58a73989a7..e7fa89a543 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -209,6 +209,7 @@ BITCOIN_CORE_H = \ policy/policy.h \ policy/rbf.h \ policy/settings.h \ + policy/value.h \ pow.h \ primitives/pak.h \ protocol.h \ diff --git a/src/exchangerates.cpp b/src/exchangerates.cpp index cb8f27a4bb..018d8591e3 100644 --- a/src/exchangerates.cpp +++ b/src/exchangerates.cpp @@ -1,24 +1,40 @@ #include #include #include +#include #include #include #include #include -CAmount ExchangeRateMap::CalculateExchangeValue(const CAmount& amount, const CAsset& asset) { +CValue ExchangeRateMap::ConvertAmountToValue(const CAmount& amount, const CAsset& asset) { + int64_t int64_max = std::numeric_limits::max(); auto it = this->find(asset); if (it == this->end()) { - return 0; + return CValue(0); } auto scaled_value = it->second.m_scaled_value; - __uint128_t value = ((__uint128_t)amount * (__uint128_t)scaled_value) / (__uint128_t)exchange_rate_scale; + __uint128_t result = ((__uint128_t)amount * (__uint128_t)scaled_value) / (__uint128_t)exchange_rate_scale; + if (result > int64_max) { + return CValue(int64_max); + } else { + return CValue((int64_t) result); + } +} + +CAmount ExchangeRateMap::ConvertValueToAmount(const CValue& value, const CAsset& asset) { int64_t int64_max = std::numeric_limits::max(); - if (value > int64_max) { + auto it = this->find(asset); + if (it == this->end()) { + return int64_max; + } + auto scaled_value = it->second.m_scaled_value; + __uint128_t result = ((__uint128_t)value.GetValue() * (__uint128_t)exchange_rate_scale) / (__uint128_t)scaled_value; + if (result > int64_max) { return int64_max; } else { - return (int64_t) value; + return (int64_t) result; } } diff --git a/src/exchangerates.h b/src/exchangerates.h index 17f697f810..d00f176976 100644 --- a/src/exchangerates.h +++ b/src/exchangerates.h @@ -3,9 +3,10 @@ #include #include +#include #include -constexpr const CAmount exchange_rate_scale = COIN; +constexpr const CAmount exchange_rate_scale = COIN; // 100,000,000 const std::string exchange_rates_config_file = "exchangerates.json"; class CAssetExchangeRate @@ -33,15 +34,24 @@ class ExchangeRateMap : public std::map } /** - * Calculate the exchange value + * Convert an amount denominated in some asset to the node's RFU (reference fee unit) * - * @param[in] amount Corresponds to CTxMemPoolEntry.nFeeAmount + * @param[in] amount Corresponds to CTxMemPoolEntry.nFee * @param[in] asset Corresponds to CTxMemPoolEntry.nFeeAsset - * @return the value at current exchange rate. Corresponds to CTxMemPoolEntry.nFee + * @return the value at current exchange rate. Corresponds to CTxMemPoolEntry.nFeeValue */ - CAmount CalculateExchangeValue(const CAmount& amount, const CAsset& asset); + CValue ConvertAmountToValue(const CAmount& amount, const CAsset& asset); -/** + /** + * Convert an amount denominated in the node's RFU (reference fee unit) into some asset + * + * @param[in] value Corresponds to CTxMemPoolEntry.nFeeValue + * @param[in] asset Corresponds to CTxMemPoolEntry.nFeeAsset + * @return the amount at current exchange rate. Corresponds to CTxMemPoolEntry.nFee + */ + CAmount ConvertValueToAmount(const CValue& value, const CAsset& asset); + + /** * Load the exchange rate map from the default JSON config file in /exchangerates.json. * * @param[in] errors Vector for storing error messages, if there are any. diff --git a/src/node/miner.cpp b/src/node/miner.cpp index a3b2858b4c..f335ab343a 100644 --- a/src/node/miner.cpp +++ b/src/node/miner.cpp @@ -452,7 +452,7 @@ void BlockAssembler::addPackageTxs(int& nPackagesSelected, int& nDescendantsUpda assert(!inBlock.count(iter)); uint64_t packageSize = iter->GetSizeWithAncestors(); - CAmount packageFees = iter->GetModFeesWithAncestors(); + CValue packageFees = iter->GetModFeesWithAncestors(); int64_t packageSigOpsCost = iter->GetSigOpCostWithAncestors(); if (fUsingModified) { packageSize = modit->nSizeWithAncestors; @@ -460,7 +460,7 @@ void BlockAssembler::addPackageTxs(int& nPackagesSelected, int& nDescendantsUpda packageSigOpsCost = modit->nSigOpCostWithAncestors; } - if (packageFees < blockMinFeeRate.GetFee(packageSize)) { + if (packageFees.GetValue() < blockMinFeeRate.GetFee(packageSize)) { // Everything else we might consider has a lower fee rate return; } diff --git a/src/node/miner.h b/src/node/miner.h index aa7d563978..3bec52a2c2 100644 --- a/src/node/miner.h +++ b/src/node/miner.h @@ -47,13 +47,13 @@ struct CTxMemPoolModifiedEntry { int64_t GetModifiedFee() const { return iter->GetModifiedFee(); } uint64_t GetSizeWithAncestors() const { return nSizeWithAncestors; } - CAmount GetModFeesWithAncestors() const { return nModFeesWithAncestors; } + CValue GetModFeesWithAncestors() const { return nModFeesWithAncestors; } size_t GetTxSize() const { return iter->GetTxSize(); } const CTransaction& GetTx() const { return iter->GetTx(); } CTxMemPool::txiter iter; uint64_t nSizeWithAncestors; - CAmount nModFeesWithAncestors; + CValue nModFeesWithAncestors; int64_t nSigOpCostWithAncestors; }; @@ -116,7 +116,7 @@ struct update_for_parent_inclusion void operator() (CTxMemPoolModifiedEntry &e) { - e.nModFeesWithAncestors -= iter->GetFee(); + e.nModFeesWithAncestors -= iter->GetFeeValue(); e.nSizeWithAncestors -= iter->GetTxSize(); e.nSigOpCostWithAncestors -= iter->GetSigOpCost(); } diff --git a/src/node/transaction.cpp b/src/node/transaction.cpp index a4f050d5d5..567d9dfb96 100644 --- a/src/node/transaction.cpp +++ b/src/node/transaction.cpp @@ -76,8 +76,8 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) { return HandleATMPError(result.m_state, err_string); } else if (g_con_any_asset_fees) { - CAmount mBaseFeesValue = ExchangeRateMap::GetInstance().CalculateExchangeValue(result.m_base_fees.value(), tx->GetFeeAsset(::policyAsset)); - if (mBaseFeesValue > max_tx_fee) { + CValue mBaseFeesValue = ExchangeRateMap::GetInstance().ConvertAmountToValue(result.m_base_fees.value(), tx->GetFeeAsset(::policyAsset)); + if (mBaseFeesValue.GetValue() > max_tx_fee) { return TransactionError::MAX_FEE_EXCEEDED; } } else if (result.m_base_fees.value() > max_tx_fee) { diff --git a/src/policy/feerate.cpp b/src/policy/feerate.cpp index 0ea56d8db7..59eda803fd 100644 --- a/src/policy/feerate.cpp +++ b/src/policy/feerate.cpp @@ -3,7 +3,11 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include +#include #include +#include +#include #include @@ -36,6 +40,15 @@ CAmount CFeeRate::GetFee(uint32_t num_bytes) const return nFee; } +CAmount CFeeRate::GetFee(uint32_t num_bytes, const CAsset& asset) const +{ + CValue nFee = CValue(this->GetFee(num_bytes)); + if (g_con_any_asset_fees) { + nFee = ExchangeRateMap::GetInstance().ConvertValueToAmount(nFee, asset); + } + return nFee.GetValue(); +} + std::string CFeeRate::ToString(const FeeEstimateMode& fee_estimate_mode) const { switch (fee_estimate_mode) { diff --git a/src/policy/feerate.h b/src/policy/feerate.h index 50fd6fd11b..dfb72b28e8 100644 --- a/src/policy/feerate.h +++ b/src/policy/feerate.h @@ -6,13 +6,21 @@ #ifndef BITCOIN_POLICY_FEERATE_H #define BITCOIN_POLICY_FEERATE_H +#include #include #include #include +#ifdef ANY_ASSET_FEES +const std::string CURRENCY_UNIT = "RFU"; // One formatted unit (reference fee unit) +const std::string CURRENCY_ATOM = "rfa"; // One indivisible minimum value unit (reference fee atom) +const std::string CURRENCY_ATOM_FULL = "reference fee atom"; +#else const std::string CURRENCY_UNIT = "BTC"; // One formatted unit const std::string CURRENCY_ATOM = "sat"; // One indivisible minimum value unit +const std::string CURRENCY_ATOM_FULL = "satoshi"; +#endif /* Used to determine type of fee estimation requested */ enum class FeeEstimateMode { @@ -56,6 +64,12 @@ class CFeeRate */ CAmount GetFee(uint32_t num_bytes) const; + /** + * Return the fee in denominations of the fee asset for the given + * vsize in vbytes. + */ + CAmount GetFee(uint32_t num_bytes, const CAsset& asset) const; + /** * Return the fee in satoshis for a vsize of 1000 vbytes */ diff --git a/src/policy/fees.cpp b/src/policy/fees.cpp index 80ac7571f3..04546b27a3 100644 --- a/src/policy/fees.cpp +++ b/src/policy/fees.cpp @@ -569,7 +569,7 @@ void CBlockPolicyEstimator::processTransaction(const CTxMemPoolEntry& entry, boo trackedTxs++; // Feerates are stored and reported as BTC-per-kb: - CFeeRate feeRate(entry.GetFee(), entry.GetTxSize()); + CFeeRate feeRate(entry.GetFeeValue().GetValue(), entry.GetTxSize()); mapMemPoolTxs[hash].blockHeight = txHeight; unsigned int bucketIndex = feeStats->NewTx(txHeight, (double)feeRate.GetFeePerK()); diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp index 99435154d2..936947d28d 100644 --- a/src/policy/policy.cpp +++ b/src/policy/policy.cpp @@ -53,7 +53,7 @@ CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFeeIn) nSize += (32 + 4 + 1 + 107 + 4); // the 148 mentioned above } - return dustRelayFeeIn.GetFee(nSize); + return dustRelayFeeIn.GetFee(nSize, txout.nAsset.GetAsset()); } bool IsDust(const CTxOut& txout, const CFeeRate& dustRelayFeeIn) @@ -146,7 +146,7 @@ bool IsStandardTx(const CTransaction& tx, bool permit_bare_multisig, const CFeeR } else if ((whichType == TxoutType::MULTISIG) && (!permit_bare_multisig)) { reason = "bare-multisig"; return false; - } else if ((txout.nAsset.IsExplicit() && txout.nAsset.GetAsset() == policyAsset) && IsDust(txout, dust_relay_fee)) { + } else if ((txout.nAsset.IsExplicit() && txout.nAsset.GetAsset() == tx.GetFeeAsset(::policyAsset)) && IsDust(txout, dust_relay_fee)) { reason = "dust"; return false; } diff --git a/src/policy/value.h b/src/policy/value.h new file mode 100644 index 0000000000..af5f0f5b7f --- /dev/null +++ b/src/policy/value.h @@ -0,0 +1,65 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2021 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_POLICY_VALUE_H +#define BITCOIN_POLICY_VALUE_H + +#include + +/** ELEMENTS: Amount denominated in the node's RFU (reference fee unit). Used only + * when con_any_asset_fees is enabled in order to distinguish from amounts in an + * actual asset. RFU is needed to make amounts comparable when sorting transactions + * in the mempool, as well as for fee estimation and subsequent validation of those + * fees according to various limits (e.g., mintxfee, paytsxfee, blockmintxfee, + * incrementalrelaytxfee, etc.). + */ +struct CValue +{ + private: + int64_t value; + + public: + CValue(): value(0) {} + CValue(const int64_t value): value(value) {} + + int64_t GetValue() const + { + return value; + } + + CValue operator -(const CValue& operand) + { + return CValue(value - operand.value); + } + + CValue operator -=(const CValue& operand) + { + value -= operand.value; + return *this; + } + + CValue operator +(const CValue& operand) + { + return CValue(value + operand.value); + } + + CValue operator +=(const CValue& operand) + { + value += operand.value; + return *this; + } + + bool operator ==(const CValue& operand) + { + return value == operand.value; + } + + bool operator !=(const CValue& operand) + { + return value != operand.value; + } +}; + +#endif // BITCOIN_POLICY_VALUE_H diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 48467a3e18..f84948f594 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -560,23 +560,23 @@ static void entryToJSON(const CTxMemPool& pool, UniValue& info, const CTxMemPool info.pushKV("descendantcount", e.GetCountWithDescendants()); info.pushKV("descendantsize", e.GetSizeWithDescendants()); if (deprecated_fee_fields_enabled) { - info.pushKV("descendantfees", e.GetModFeesWithDescendants()); + info.pushKV("descendantfees", e.GetModFeesWithDescendants().GetValue()); } info.pushKV("ancestorcount", e.GetCountWithAncestors()); info.pushKV("ancestorsize", e.GetSizeWithAncestors()); if (deprecated_fee_fields_enabled) { - info.pushKV("ancestorfees", e.GetModFeesWithAncestors()); + info.pushKV("ancestorfees", e.GetModFeesWithAncestors().GetValue()); } info.pushKV("wtxid", pool.vTxHashes[e.vTxHashesIdx].first.ToString()); UniValue fees(UniValue::VOBJ); fees.pushKV("base", ValueFromAmount(e.GetFee())); fees.pushKV("modified", ValueFromAmount(e.GetModifiedFee())); - fees.pushKV("ancestor", ValueFromAmount(e.GetModFeesWithAncestors())); - fees.pushKV("descendant", ValueFromAmount(e.GetModFeesWithDescendants())); + fees.pushKV("ancestor", ValueFromAmount(e.GetModFeesWithAncestors().GetValue())); + fees.pushKV("descendant", ValueFromAmount(e.GetModFeesWithDescendants().GetValue())); if (g_con_any_asset_fees) { fees.pushKV("asset", e.GetFeeAsset().GetHex()); - fees.pushKV("value", ValueFromAmount(e.GetFeeValue())); + fees.pushKV("value", ValueFromAmount(e.GetFeeValue().GetValue())); } info.pushKV("fees", fees); @@ -2308,7 +2308,7 @@ static constexpr size_t PER_UTXO_OVERHEAD = sizeof(COutPoint) + sizeof(uint32_t) static RPCHelpMan getblockstats() { return RPCHelpMan{"getblockstats", - "\nCompute per block statistics for a given window. All amounts are in satoshis.\n" + "\nCompute per block statistics for a given window. All amounts are in " + CURRENCY_ATOM_FULL + "s.\n" "It won't work for some heights with pruning.\n", { {"hash_or_height", RPCArg::Type::NUM, RPCArg::Optional::NO, "The block hash or height of the target block", "", {"", "string or numeric"}}, @@ -2323,10 +2323,10 @@ static RPCHelpMan getblockstats() RPCResult::Type::OBJ, "", "", { {RPCResult::Type::NUM, "avgfee", /*optional=*/true, "Average fee in the block"}, - {RPCResult::Type::NUM, "avgfeerate", /*optional=*/true, "Average feerate (in satoshis per virtual byte)"}, + {RPCResult::Type::NUM, "avgfeerate", /*optional=*/true, "Average feerate (in " + CURRENCY_ATOM_FULL + "s per virtual byte)"}, {RPCResult::Type::NUM, "avgtxsize", /*optional=*/true, "Average transaction size"}, {RPCResult::Type::STR_HEX, "blockhash", /*optional=*/true, "The block hash (to check for potential reorgs)"}, - {RPCResult::Type::ARR_FIXED, "feerate_percentiles", /*optional=*/true, "Feerates at the 10th, 25th, 50th, 75th, and 90th percentile weight unit (in satoshis per virtual byte)", + {RPCResult::Type::ARR_FIXED, "feerate_percentiles", /*optional=*/true, "Feerates at the 10th, 25th, 50th, 75th, and 90th percentile weight unit (in " + CURRENCY_ATOM_FULL + " per virtual byte)", { {RPCResult::Type::NUM, "10th_percentile_feerate", "The 10th percentile feerate"}, {RPCResult::Type::NUM, "25th_percentile_feerate", "The 25th percentile feerate"}, @@ -2337,13 +2337,13 @@ static RPCHelpMan getblockstats() {RPCResult::Type::NUM, "height", /*optional=*/true, "The height of the block"}, {RPCResult::Type::NUM, "ins", /*optional=*/true, "The number of inputs (excluding coinbase)"}, {RPCResult::Type::NUM, "maxfee", /*optional=*/true, "Maximum fee in the block"}, - {RPCResult::Type::NUM, "maxfeerate", /*optional=*/true, "Maximum feerate (in satoshis per virtual byte)"}, + {RPCResult::Type::NUM, "maxfeerate", /*optional=*/true, "Maximum feerate (in " + CURRENCY_ATOM_FULL + "s per virtual byte)"}, {RPCResult::Type::NUM, "maxtxsize", /*optional=*/true, "Maximum transaction size"}, {RPCResult::Type::NUM, "medianfee", /*optional=*/true, "Truncated median fee in the block"}, {RPCResult::Type::NUM, "mediantime", /*optional=*/true, "The block median time past"}, {RPCResult::Type::NUM, "mediantxsize", /*optional=*/true, "Truncated median transaction size"}, {RPCResult::Type::NUM, "minfee", /*optional=*/true, "Minimum fee in the block"}, - {RPCResult::Type::NUM, "minfeerate", /*optional=*/true, "Minimum feerate (in satoshis per virtual byte)"}, + {RPCResult::Type::NUM, "minfeerate", /*optional=*/true, "Minimum feerate (in " + CURRENCY_ATOM_FULL + "s per virtual byte)"}, {RPCResult::Type::NUM, "mintxsize", /*optional=*/true, "Minimum transaction size"}, {RPCResult::Type::NUM, "outs", /*optional=*/true, "The number of outputs"}, {RPCResult::Type::NUM, "subsidy", /*optional=*/true, "The block subsidy"}, diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 193f2f5a75..90b037d74a 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -485,7 +485,7 @@ static RPCHelpMan prioritisetransaction() {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id."}, {"dummy", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "API-Compatibility for previous API. Must be zero or null.\n" " DEPRECATED. For forward compatibility use named arguments and omit this parameter."}, - {"fee_delta", RPCArg::Type::NUM, RPCArg::Optional::NO, "The fee value (in satoshis) to add (or subtract, if negative).\n" + {"fee_delta", RPCArg::Type::NUM, RPCArg::Optional::NO, "The fee value (in " + CURRENCY_ATOM_FULL + "s) to add (or subtract, if negative).\n" " Note, that this value is not a fee rate. It is a value to modify absolute fee of the TX.\n" " The fee is not actually paid, only the algorithm for selecting transactions into a block\n" " considers the transaction as it would have paid a higher (or lower) fee."}, diff --git a/src/txmempool.cpp b/src/txmempool.cpp index f4e6ef1585..db7da9548a 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -29,7 +30,7 @@ // Helpers for modifying CTxMemPool::mapTx, which is a boost multi_index. struct update_descendant_state { - update_descendant_state(int64_t _modifySize, CAmount _modifyFee, int64_t _modifyCount) : + update_descendant_state(int64_t _modifySize, CValue _modifyFee, int64_t _modifyCount) : modifySize(_modifySize), modifyFee(_modifyFee), modifyCount(_modifyCount) {} @@ -38,13 +39,13 @@ struct update_descendant_state private: int64_t modifySize; - CAmount modifyFee; + CValue modifyFee; int64_t modifyCount; }; struct update_ancestor_state { - update_ancestor_state(int64_t _modifySize, CAmount _modifyFee, int64_t _modifyCount, int64_t _modifySigOpsCost) : + update_ancestor_state(int64_t _modifySize, CValue _modifyFee, int64_t _modifyCount, int64_t _modifySigOpsCost) : modifySize(_modifySize), modifyFee(_modifyFee), modifyCount(_modifyCount), modifySigOpsCost(_modifySigOpsCost) {} @@ -53,7 +54,7 @@ struct update_ancestor_state private: int64_t modifySize; - CAmount modifyFee; + CValue modifyFee; int64_t modifyCount; int64_t modifySigOpsCost; }; @@ -70,12 +71,12 @@ struct update_fee_delta struct update_fee_value { - explicit update_fee_value(int64_t _feeValue) : feeValue(_feeValue) { } + explicit update_fee_value(CValue _feeValue) : feeValue(_feeValue) { } void operator() (CTxMemPoolEntry &e) { e.UpdateFeeValue(feeValue); } private: - int64_t feeValue; + CValue feeValue; }; bool TestLockPointValidity(CChain& active_chain, const LockPoints& lp) @@ -95,7 +96,7 @@ bool TestLockPointValidity(CChain& active_chain, const LockPoints& lp) return true; } -CTxMemPoolEntry::CTxMemPoolEntry(const CTransactionRef& tx, CAmount fee, CAsset feeAsset, CAmount feeValue, +CTxMemPoolEntry::CTxMemPoolEntry(const CTransactionRef& tx, CAmount fee, CAsset feeAsset, CValue feeValue, int64_t time, unsigned int entry_height, bool spends_coinbase, int64_t sigops_cost, LockPoints lp, const std::set>& _setPeginsSpent) @@ -124,7 +125,7 @@ void CTxMemPoolEntry::UpdateFeeDelta(int64_t newFeeDelta) feeDelta = newFeeDelta; } -void CTxMemPoolEntry::UpdateFeeValue(const CAmount newFeeValue) +void CTxMemPoolEntry::UpdateFeeValue(CValue newFeeValue) { nModFeesWithDescendants += newFeeValue - nFeeValue; nModFeesWithAncestors += newFeeValue - nFeeValue; @@ -466,7 +467,7 @@ void CTxMemPool::UpdateForRemoveFromMempool(const setEntries &entriesToRemove, b } } -void CTxMemPoolEntry::UpdateDescendantState(int64_t modifySize, CAmount modifyFee, int64_t modifyCount) +void CTxMemPoolEntry::UpdateDescendantState(int64_t modifySize, CValue modifyFee, int64_t modifyCount) { nSizeWithDescendants += modifySize; assert(int64_t(nSizeWithDescendants) > 0); @@ -475,7 +476,7 @@ void CTxMemPoolEntry::UpdateDescendantState(int64_t modifySize, CAmount modifyFe assert(int64_t(nCountWithDescendants) > 0); } -void CTxMemPoolEntry::UpdateAncestorState(int64_t modifySize, CAmount modifyFee, int64_t modifyCount, int64_t modifySigOps) +void CTxMemPoolEntry::UpdateAncestorState(int64_t modifySize, CValue modifyFee, int64_t modifyCount, int64_t modifySigOps) { nSizeWithAncestors += modifySize; assert(int64_t(nSizeWithAncestors) > 0); @@ -870,7 +871,7 @@ void CTxMemPool::check(const CBlockIndex* active_chain_tip, const CCoinsViewCach assert(it->GetCountWithAncestors() == nCountCheck); assert(it->GetSizeWithAncestors() == nSizeCheck); assert(it->GetSigOpCostWithAncestors() == nSigOpCheck); - assert(it->GetModFeesWithAncestors() == nFeesCheck); + assert(it->GetModFeesWithAncestors().GetValue() == nFeesCheck); // Sanity check: we are walking in ascending ancestor count order. assert(prev_ancestor_count <= it->GetCountWithAncestors()); prev_ancestor_count = it->GetCountWithAncestors(); @@ -1078,8 +1079,8 @@ void CTxMemPool::RecomputeFees() ExchangeRateMap exchangeRateMap = ExchangeRateMap::GetInstance(); for (CTxMemPoolEntry tx : mapTx) { txiter it = mapTx.find(tx.GetTx().GetHash()); - CAmount newFeeValue = exchangeRateMap.CalculateExchangeValue(tx.GetFee(), tx.GetFeeAsset()); - CAmount feeValueDelta = newFeeValue - tx.GetFeeValue(); + CValue newFeeValue = exchangeRateMap.ConvertAmountToValue(tx.GetFee(), tx.GetFeeAsset()); + CValue feeValueDelta = newFeeValue - tx.GetFeeValue(); if (feeValueDelta != 0) { mapTx.modify(it, update_fee_value(newFeeValue)); @@ -1287,7 +1288,7 @@ void CTxMemPool::TrimToSize(size_t sizelimit, std::vector* pvNoSpends // "minimum reasonable fee rate" (ie some value under which we consider txn // to have 0 fee). This way, we don't allow txn to enter mempool with feerate // equal to txn which were removed with no block in between. - CFeeRate removed(it->GetModFeesWithDescendants(), it->GetSizeWithDescendants()); + CFeeRate removed(it->GetModFeesWithDescendants().GetValue(), it->GetSizeWithDescendants()); removed += incrementalRelayFee; trackPackageRemoved(removed); maxFeeRateRemoved = std::max(maxFeeRateRemoved, removed); @@ -1347,7 +1348,7 @@ void CTxMemPool::GetTransactionAncestry(const uint256& txid, size_t& ancestors, if (it != mapTx.end()) { ancestors = it->GetCountWithAncestors(); if (ancestorsize) *ancestorsize = it->GetSizeWithAncestors(); - if (ancestorfees) *ancestorfees = it->GetModFeesWithAncestors(); + if (ancestorfees) *ancestorfees = it->GetModFeesWithAncestors().GetValue(); descendants = CalculateDescendantMaximum(it); } } diff --git a/src/txmempool.h b/src/txmempool.h index a254281f4d..8e06c00855 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -97,7 +98,7 @@ class CTxMemPoolEntry mutable Children m_children; const CAmount nFee; //!< Cached to avoid expensive parent-transaction lookups const CAsset nFeeAsset; //!< ELEMENTS: The asset used for fee payment. Always equal to policyAsset unless con_any_asset_fees is enabled. - CAmount nFeeValue; //!< ELEMENTS: Value in reference unit, computed using configured exchange rates. It is not a `const` because it needs to be updated whenever exchange rates change. + CValue nFeeValue; //!< ELEMENTS: Value in reference unit, computed using configured exchange rates. It is not a `const` because it needs to be updated whenever exchange rates change. const size_t nTxWeight; //!< ... and avoid recomputing tx weight (also used for GetTxSize()) const size_t nUsageSize; //!< ... and total memory usage const int64_t nTime; //!< Local time when entering the mempool @@ -112,16 +113,16 @@ class CTxMemPoolEntry // descendants as well. uint64_t nCountWithDescendants{1}; //!< number of descendant transactions uint64_t nSizeWithDescendants; //!< ... and size - CAmount nModFeesWithDescendants; //!< ... and total fees (all including us) + CValue nModFeesWithDescendants; //!< ... and total fees (all including us) // Analogous statistics for ancestor transactions uint64_t nCountWithAncestors{1}; uint64_t nSizeWithAncestors; - CAmount nModFeesWithAncestors; + CValue nModFeesWithAncestors; int64_t nSigOpCostWithAncestors; public: - CTxMemPoolEntry(const CTransactionRef& tx, CAmount fee, const CAsset feeAsset, const CAmount feeValue, + CTxMemPoolEntry(const CTransactionRef& tx, CAmount fee, const CAsset feeAsset, const CValue feeValue, int64_t time, unsigned int entry_height, bool spends_coinbase, int64_t sigops_cost, LockPoints lp, @@ -131,37 +132,37 @@ class CTxMemPoolEntry CTransactionRef GetSharedTx() const { return this->tx; } const CAmount& GetFee() const { return nFee; } const CAsset& GetFeeAsset() const { return nFeeAsset; } - const CAmount& GetFeeValue() const { return nFeeValue; } + const CValue& GetFeeValue() const { return nFeeValue; } size_t GetTxSize() const; size_t GetTxWeight() const { return nTxWeight; } std::chrono::seconds GetTime() const { return std::chrono::seconds{nTime}; } unsigned int GetHeight() const { return entryHeight; } int64_t GetSigOpCost() const { return sigOpCost; } - int64_t GetModifiedFee() const { return nFee + feeDelta; } + int64_t GetModifiedFee() const { return nFeeValue.GetValue() + feeDelta; } size_t DynamicMemoryUsage() const { return nUsageSize; } const LockPoints& GetLockPoints() const { return lockPoints; } // Adjusts the descendant state. - void UpdateDescendantState(int64_t modifySize, CAmount modifyFee, int64_t modifyCount); + void UpdateDescendantState(int64_t modifySize, CValue modifyFee, int64_t modifyCount); // Adjusts the ancestor state - void UpdateAncestorState(int64_t modifySize, CAmount modifyFee, int64_t modifyCount, int64_t modifySigOps); + void UpdateAncestorState(int64_t modifySize, CValue modifyFee, int64_t modifyCount, int64_t modifySigOps); // Updates the fee delta used for mining priority score, and the // modified fees with descendants. void UpdateFeeDelta(int64_t feeDelta); // Updates the fee value and the modified fees with ancestors and descendants. - void UpdateFeeValue(const CAmount fee); + void UpdateFeeValue(CValue fee); // Update the LockPoints after a reorg void UpdateLockPoints(const LockPoints& lp); uint64_t GetCountWithDescendants() const { return nCountWithDescendants; } uint64_t GetSizeWithDescendants() const { return nSizeWithDescendants; } - CAmount GetModFeesWithDescendants() const { return nModFeesWithDescendants; } + CValue GetModFeesWithDescendants() const { return nModFeesWithDescendants; } bool GetSpendsCoinbase() const { return spendsCoinbase; } uint64_t GetCountWithAncestors() const { return nCountWithAncestors; } uint64_t GetSizeWithAncestors() const { return nSizeWithAncestors; } - CAmount GetModFeesWithAncestors() const { return nModFeesWithAncestors; } + CValue GetModFeesWithAncestors() const { return nModFeesWithAncestors; } int64_t GetSigOpCostWithAncestors() const { return nSigOpCostWithAncestors; } const Parents& GetMemPoolParentsConst() const { return m_parents; } @@ -238,10 +239,10 @@ class CompareTxMemPoolEntryByDescendantScore // Compare feerate with descendants to feerate of the transaction, and // return the fee/size for the max. double f1 = (double)a.GetModifiedFee() * a.GetSizeWithDescendants(); - double f2 = (double)a.GetModFeesWithDescendants() * a.GetTxSize(); + double f2 = (double)a.GetModFeesWithDescendants().GetValue() * a.GetTxSize(); if (f2 > f1) { - mod_fee = a.GetModFeesWithDescendants(); + mod_fee = a.GetModFeesWithDescendants().GetValue(); size = a.GetSizeWithDescendants(); } else { mod_fee = a.GetModifiedFee(); @@ -312,10 +313,10 @@ class CompareTxMemPoolEntryByAncestorFee // Compare feerate with ancestors to feerate of the transaction, and // return the fee/size for the min. double f1 = (double)a.GetModifiedFee() * a.GetSizeWithAncestors(); - double f2 = (double)a.GetModFeesWithAncestors() * a.GetTxSize(); + double f2 = (double)a.GetModFeesWithAncestors().GetValue() * a.GetTxSize(); if (f1 > f2) { - mod_fee = a.GetModFeesWithAncestors(); + mod_fee = a.GetModFeesWithAncestors().GetValue(); size = a.GetSizeWithAncestors(); } else { mod_fee = a.GetModifiedFee(); diff --git a/src/validation.cpp b/src/validation.cpp index fae3c72d94..7da36f2db1 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -32,6 +33,7 @@ #include #include #include +#include #include #include #include