diff options
Diffstat (limited to 'src/policy')
-rw-r--r-- | src/policy/feerate.cpp | 27 | ||||
-rw-r--r-- | src/policy/feerate.h | 16 | ||||
-rw-r--r-- | src/policy/fees.cpp | 54 | ||||
-rw-r--r-- | src/policy/fees.h | 2 | ||||
-rw-r--r-- | src/policy/packages.cpp | 62 | ||||
-rw-r--r-- | src/policy/packages.h | 44 | ||||
-rw-r--r-- | src/policy/policy.cpp | 18 | ||||
-rw-r--r-- | src/policy/policy.h | 3 | ||||
-rw-r--r-- | src/policy/rbf.cpp | 140 | ||||
-rw-r--r-- | src/policy/rbf.h | 68 |
10 files changed, 380 insertions, 54 deletions
diff --git a/src/policy/feerate.cpp b/src/policy/feerate.cpp index 3da85fedf9..ce149067b7 100644 --- a/src/policy/feerate.cpp +++ b/src/policy/feerate.cpp @@ -7,29 +7,30 @@ #include <tinyformat.h> -CFeeRate::CFeeRate(const CAmount& nFeePaid, size_t nBytes_) +#include <cmath> + +CFeeRate::CFeeRate(const CAmount& nFeePaid, uint32_t num_bytes) { - assert(nBytes_ <= uint64_t(std::numeric_limits<int64_t>::max())); - int64_t nSize = int64_t(nBytes_); + const int64_t nSize{num_bytes}; - if (nSize > 0) + if (nSize > 0) { nSatoshisPerK = nFeePaid * 1000 / nSize; - else + } else { nSatoshisPerK = 0; + } } -CAmount CFeeRate::GetFee(size_t nBytes_) const +CAmount CFeeRate::GetFee(uint32_t num_bytes) const { - assert(nBytes_ <= uint64_t(std::numeric_limits<int64_t>::max())); - int64_t nSize = int64_t(nBytes_); + const int64_t nSize{num_bytes}; - CAmount nFee = nSatoshisPerK * nSize / 1000; + // Be explicit that we're converting from a double to int64_t (CAmount) here. + // We've previously had issues with the silent double->int64_t conversion. + CAmount nFee{static_cast<CAmount>(std::ceil(nSatoshisPerK * nSize / 1000.0))}; if (nFee == 0 && nSize != 0) { - if (nSatoshisPerK > 0) - nFee = CAmount(1); - if (nSatoshisPerK < 0) - nFee = CAmount(-1); + if (nSatoshisPerK > 0) nFee = CAmount(1); + if (nSatoshisPerK < 0) nFee = CAmount(-1); } return nFee; diff --git a/src/policy/feerate.h b/src/policy/feerate.h index 0e4f9914b8..13c7ec2002 100644 --- a/src/policy/feerate.h +++ b/src/policy/feerate.h @@ -6,7 +6,7 @@ #ifndef BITCOIN_POLICY_FEERATE_H #define BITCOIN_POLICY_FEERATE_H -#include <amount.h> +#include <consensus/amount.h> #include <serialize.h> #include <string> @@ -39,20 +39,18 @@ public: // We've previously had bugs creep in from silent double->int conversion... static_assert(std::is_integral<I>::value, "CFeeRate should be used without floats"); } - /** Constructor for a fee rate in satoshis per kvB (sat/kvB). The size in bytes must not exceed (2^63 - 1). + /** Constructor for a fee rate in satoshis per kvB (sat/kvB). * - * Passing an nBytes value of COIN (1e8) returns a fee rate in satoshis per vB (sat/vB), + * Passing a num_bytes value of COIN (1e8) returns a fee rate in satoshis per vB (sat/vB), * e.g. (nFeePaid * 1e8 / 1e3) == (nFeePaid / 1e5), * where 1e5 is the ratio to convert from BTC/kvB to sat/vB. - * - * @param[in] nFeePaid CAmount fee rate to construct with - * @param[in] nBytes size_t bytes (units) to construct with */ - CFeeRate(const CAmount& nFeePaid, size_t nBytes); + CFeeRate(const CAmount& nFeePaid, uint32_t num_bytes); /** * Return the fee in satoshis for the given size in bytes. + * If the calculated fee would have fractional satoshis, then the returned fee will always be rounded up to the nearest satoshi. */ - CAmount GetFee(size_t nBytes) const; + CAmount GetFee(uint32_t num_bytes) const; /** * Return the fee in satoshis for a size of 1000 bytes */ @@ -69,4 +67,4 @@ public: SERIALIZE_METHODS(CFeeRate, obj) { READWRITE(obj.nSatoshisPerK); } }; -#endif // BITCOIN_POLICY_FEERATE_H +#endif // BITCOIN_POLICY_FEERATE_H diff --git a/src/policy/fees.cpp b/src/policy/fees.cpp index 2b70c18b7e..36cf786bd5 100644 --- a/src/policy/fees.cpp +++ b/src/policy/fees.cpp @@ -10,6 +10,7 @@ #include <logging.h> #include <streams.h> #include <txmempool.h> +#include <util/serfloat.h> #include <util/system.h> static const char* FEE_ESTIMATES_FILENAME = "fee_estimates.dat"; @@ -26,6 +27,25 @@ std::string StringForFeeEstimateHorizon(FeeEstimateHorizon horizon) assert(false); } +namespace { + +struct EncodedDoubleFormatter +{ + template<typename Stream> void Ser(Stream &s, double v) + { + s << EncodeDouble(v); + } + + template<typename Stream> void Unser(Stream& s, double& v) + { + uint64_t encoded; + s >> encoded; + v = DecodeDouble(encoded); + } +}; + +} // namespace + /** * We will instantiate an instance of this class to track transactions that were * included in a block. We will lump transactions into a bucket according to their @@ -356,12 +376,12 @@ double TxConfirmStats::EstimateMedianVal(int confTarget, double sufficientTxVal, void TxConfirmStats::Write(CAutoFile& fileout) const { - fileout << decay; + fileout << Using<EncodedDoubleFormatter>(decay); fileout << scale; - fileout << m_feerate_avg; - fileout << txCtAvg; - fileout << confAvg; - fileout << failAvg; + fileout << Using<VectorFormatter<EncodedDoubleFormatter>>(m_feerate_avg); + fileout << Using<VectorFormatter<EncodedDoubleFormatter>>(txCtAvg); + fileout << Using<VectorFormatter<VectorFormatter<EncodedDoubleFormatter>>>(confAvg); + fileout << Using<VectorFormatter<VectorFormatter<EncodedDoubleFormatter>>>(failAvg); } void TxConfirmStats::Read(CAutoFile& filein, int nFileVersion, size_t numBuckets) @@ -372,7 +392,7 @@ void TxConfirmStats::Read(CAutoFile& filein, int nFileVersion, size_t numBuckets size_t maxConfirms, maxPeriods; // The current version will store the decay with each individual TxConfirmStats and also keep a scale factor - filein >> decay; + filein >> Using<EncodedDoubleFormatter>(decay); if (decay <= 0 || decay >= 1) { throw std::runtime_error("Corrupt estimates file. Decay must be between 0 and 1 (non-inclusive)"); } @@ -381,15 +401,15 @@ void TxConfirmStats::Read(CAutoFile& filein, int nFileVersion, size_t numBuckets throw std::runtime_error("Corrupt estimates file. Scale must be non-zero"); } - filein >> m_feerate_avg; + filein >> Using<VectorFormatter<EncodedDoubleFormatter>>(m_feerate_avg); if (m_feerate_avg.size() != numBuckets) { throw std::runtime_error("Corrupt estimates file. Mismatch in feerate average bucket count"); } - filein >> txCtAvg; + filein >> Using<VectorFormatter<EncodedDoubleFormatter>>(txCtAvg); if (txCtAvg.size() != numBuckets) { throw std::runtime_error("Corrupt estimates file. Mismatch in tx count bucket count"); } - filein >> confAvg; + filein >> Using<VectorFormatter<VectorFormatter<EncodedDoubleFormatter>>>(confAvg); maxPeriods = confAvg.size(); maxConfirms = scale * maxPeriods; @@ -402,7 +422,7 @@ void TxConfirmStats::Read(CAutoFile& filein, int nFileVersion, size_t numBuckets } } - filein >> failAvg; + filein >> Using<VectorFormatter<VectorFormatter<EncodedDoubleFormatter>>>(failAvg); if (maxPeriods != failAvg.size()) { throw std::runtime_error("Corrupt estimates file. Mismatch in confirms tracked for failures"); } @@ -510,10 +530,10 @@ CBlockPolicyEstimator::CBlockPolicyEstimator() longStats = std::unique_ptr<TxConfirmStats>(new TxConfirmStats(buckets, bucketMap, LONG_BLOCK_PERIODS, LONG_DECAY, LONG_SCALE)); // If the fee estimation file is present, read recorded estimations - fs::path est_filepath = GetDataDir() / FEE_ESTIMATES_FILENAME; + fs::path est_filepath = gArgs.GetDataDirNet() / FEE_ESTIMATES_FILENAME; CAutoFile est_file(fsbridge::fopen(est_filepath, "rb"), SER_DISK, CLIENT_VERSION); if (est_file.IsNull() || !Read(est_file)) { - LogPrintf("Failed to read fee estimates from %s. Continue anyway.\n", est_filepath.string()); + LogPrintf("Failed to read fee estimates from %s. Continue anyway.\n", fs::PathToString(est_filepath)); } } @@ -535,7 +555,7 @@ void CBlockPolicyEstimator::processTransaction(const CTxMemPoolEntry& entry, boo if (txHeight != nBestSeenHeight) { // Ignore side chains and re-orgs; assuming they are random they don't // affect the estimate. We'll potentially double count transactions in 1-block reorgs. - // Ignore txs if BlockPolicyEstimator is not in sync with ::ChainActive().Tip(). + // Ignore txs if BlockPolicyEstimator is not in sync with ActiveChain().Tip(). // It will be synced next time a block is processed. return; } @@ -871,10 +891,10 @@ CFeeRate CBlockPolicyEstimator::estimateSmartFee(int confTarget, FeeCalculation void CBlockPolicyEstimator::Flush() { FlushUnconfirmed(); - fs::path est_filepath = GetDataDir() / FEE_ESTIMATES_FILENAME; + fs::path est_filepath = gArgs.GetDataDirNet() / FEE_ESTIMATES_FILENAME; CAutoFile est_file(fsbridge::fopen(est_filepath, "wb"), SER_DISK, CLIENT_VERSION); if (est_file.IsNull() || !Write(est_file)) { - LogPrintf("Failed to write fee estimates to %s. Continue anyway.\n", est_filepath.string()); + LogPrintf("Failed to write fee estimates to %s. Continue anyway.\n", fs::PathToString(est_filepath)); } } @@ -891,7 +911,7 @@ bool CBlockPolicyEstimator::Write(CAutoFile& fileout) const else { fileout << historicalFirst << historicalBest; } - fileout << buckets; + fileout << Using<VectorFormatter<EncodedDoubleFormatter>>(buckets); feeStats->Write(fileout); shortStats->Write(fileout); longStats->Write(fileout); @@ -927,7 +947,7 @@ bool CBlockPolicyEstimator::Read(CAutoFile& filein) throw std::runtime_error("Corrupt estimates file. Historical block range for estimates is invalid"); } std::vector<double> fileBuckets; - filein >> fileBuckets; + filein >> Using<VectorFormatter<EncodedDoubleFormatter>>(fileBuckets); size_t numBuckets = fileBuckets.size(); if (numBuckets <= 1 || numBuckets > 1000) { throw std::runtime_error("Corrupt estimates file. Must have between 2 and 1000 feerate buckets"); diff --git a/src/policy/fees.h b/src/policy/fees.h index c05cabadf9..37a7051045 100644 --- a/src/policy/fees.h +++ b/src/policy/fees.h @@ -5,7 +5,7 @@ #ifndef BITCOIN_POLICY_FEES_H #define BITCOIN_POLICY_FEES_H -#include <amount.h> +#include <consensus/amount.h> #include <policy/feerate.h> #include <uint256.h> #include <random.h> diff --git a/src/policy/packages.cpp b/src/policy/packages.cpp new file mode 100644 index 0000000000..cfd0539965 --- /dev/null +++ b/src/policy/packages.cpp @@ -0,0 +1,62 @@ +// Copyright (c) 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. + +#include <consensus/validation.h> +#include <policy/packages.h> +#include <primitives/transaction.h> +#include <uint256.h> +#include <util/hasher.h> + +#include <numeric> +#include <unordered_set> + +bool CheckPackage(const Package& txns, PackageValidationState& state) +{ + const unsigned int package_count = txns.size(); + + if (package_count > MAX_PACKAGE_COUNT) { + return state.Invalid(PackageValidationResult::PCKG_POLICY, "package-too-many-transactions"); + } + + const int64_t total_size = std::accumulate(txns.cbegin(), txns.cend(), 0, + [](int64_t sum, const auto& tx) { return sum + GetVirtualTransactionSize(*tx); }); + // If the package only contains 1 tx, it's better to report the policy violation on individual tx size. + if (package_count > 1 && total_size > MAX_PACKAGE_SIZE * 1000) { + return state.Invalid(PackageValidationResult::PCKG_POLICY, "package-too-large"); + } + + // Require the package to be sorted in order of dependency, i.e. parents appear before children. + // An unsorted package will fail anyway on missing-inputs, but it's better to quit earlier and + // fail on something less ambiguous (missing-inputs could also be an orphan or trying to + // spend nonexistent coins). + std::unordered_set<uint256, SaltedTxidHasher> later_txids; + std::transform(txns.cbegin(), txns.cend(), std::inserter(later_txids, later_txids.end()), + [](const auto& tx) { return tx->GetHash(); }); + for (const auto& tx : txns) { + for (const auto& input : tx->vin) { + if (later_txids.find(input.prevout.hash) != later_txids.end()) { + // The parent is a subsequent transaction in the package. + return state.Invalid(PackageValidationResult::PCKG_POLICY, "package-not-sorted"); + } + } + later_txids.erase(tx->GetHash()); + } + + // Don't allow any conflicting transactions, i.e. spending the same inputs, in a package. + std::unordered_set<COutPoint, SaltedOutpointHasher> inputs_seen; + for (const auto& tx : txns) { + for (const auto& input : tx->vin) { + if (inputs_seen.find(input.prevout) != inputs_seen.end()) { + // This input is also present in another tx in the package. + return state.Invalid(PackageValidationResult::PCKG_POLICY, "conflict-in-package"); + } + } + // Batch-add all the inputs for a tx at a time. If we added them 1 at a time, we could + // catch duplicate inputs within a single tx. This is a more severe, consensus error, + // and we want to report that from CheckTransaction instead. + std::transform(tx->vin.cbegin(), tx->vin.cend(), std::inserter(inputs_seen, inputs_seen.end()), + [](const auto& input) { return input.prevout; }); + } + return true; +} diff --git a/src/policy/packages.h b/src/policy/packages.h new file mode 100644 index 0000000000..6b7ac3e450 --- /dev/null +++ b/src/policy/packages.h @@ -0,0 +1,44 @@ +// Copyright (c) 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_PACKAGES_H +#define BITCOIN_POLICY_PACKAGES_H + +#include <consensus/validation.h> +#include <policy/policy.h> +#include <primitives/transaction.h> + +#include <vector> + +/** Default maximum number of transactions in a package. */ +static constexpr uint32_t MAX_PACKAGE_COUNT{25}; +/** Default maximum total virtual size of transactions in a package in KvB. */ +static constexpr uint32_t MAX_PACKAGE_SIZE{101}; +static_assert(MAX_PACKAGE_SIZE * WITNESS_SCALE_FACTOR * 1000 >= MAX_STANDARD_TX_WEIGHT); + +/** A "reason" why a package was invalid. It may be that one or more of the included + * transactions is invalid or the package itself violates our rules. + * We don't distinguish between consensus and policy violations right now. + */ +enum class PackageValidationResult { + PCKG_RESULT_UNSET = 0, //!< Initial value. The package has not yet been rejected. + PCKG_POLICY, //!< The package itself is invalid (e.g. too many transactions). + PCKG_TX, //!< At least one tx is invalid. +}; + +/** A package is an ordered list of transactions. The transactions cannot conflict with (spend the + * same inputs as) one another. */ +using Package = std::vector<CTransactionRef>; + +class PackageValidationState : public ValidationState<PackageValidationResult> {}; + +/** Context-free package policy checks: + * 1. The number of transactions cannot exceed MAX_PACKAGE_COUNT. + * 2. The total virtual size cannot exceed MAX_PACKAGE_SIZE. + * 3. If any dependencies exist between transactions, parents must appear before children. + * 4. Transactions cannot conflict, i.e., spend the same inputs. + */ +bool CheckPackage(const Package& txns, PackageValidationState& state); + +#endif // BITCOIN_POLICY_PACKAGES_H diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp index 9e433584e7..2c30b20d5b 100644 --- a/src/policy/policy.cpp +++ b/src/policy/policy.cpp @@ -22,7 +22,7 @@ CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFeeIn) // so dust is a spendable txout less than // 182*dustRelayFee/1000 (in satoshis). // 546 satoshis at the default rate of 3000 sat/kvB. - // A typical spendable segwit txout is 31 bytes big, and will + // A typical spendable segwit P2WPKH txout is 31 bytes big, and will // need a CTxIn of at least 67 bytes to spend: // so dust is a spendable txout less than // 98*dustRelayFee/1000 (in satoshis). @@ -34,6 +34,11 @@ CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFeeIn) int witnessversion = 0; std::vector<unsigned char> witnessprogram; + // Note this computation is for spending a Segwit v0 P2WPKH output (a 33 bytes + // public key + an ECDSA signature). For Segwit v1 Taproot outputs the minimum + // satisfaction is lower (a single BIP340 signature) but this computation was + // kept to not further reduce the dust level. + // See discussion in https://github.com/bitcoin/bitcoin/pull/22779 for details. if (txout.scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) { // sum the sizes of the parts of a transaction input // with 75% segwit discount applied to the script size. @@ -156,13 +161,13 @@ bool IsStandardTx(const CTransaction& tx, bool permit_bare_multisig, const CFeeR * * Note that only the non-witness portion of the transaction is checked here. */ -bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs, bool taproot_active) +bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs) { - if (tx.IsCoinBase()) + if (tx.IsCoinBase()) { return true; // Coinbases don't use vin normally + } - for (unsigned int i = 0; i < tx.vin.size(); i++) - { + for (unsigned int i = 0; i < tx.vin.size(); i++) { const CTxOut& prev = mapInputs.AccessCoin(tx.vin[i].prevout).out; std::vector<std::vector<unsigned char> > vSolutions; @@ -184,9 +189,6 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs, if (subscript.GetSigOpCount(true) > MAX_P2SH_SIGOPS) { return false; } - } else if (whichType == TxoutType::WITNESS_V1_TAPROOT) { - // Don't allow Taproot spends unless Taproot is active. - if (!taproot_active) return false; } } diff --git a/src/policy/policy.h b/src/policy/policy.h index f2a3f35546..f6ac6500f6 100644 --- a/src/policy/policy.h +++ b/src/policy/policy.h @@ -105,10 +105,9 @@ bool IsStandardTx(const CTransaction& tx, bool permit_bare_multisig, const CFeeR /** * Check for standard transaction types * @param[in] mapInputs Map of previous transactions that have outputs we're spending -* @param[in] taproot_active Whether or taproot consensus rules are active (used to decide whether spends of them are permitted) * @return True if all inputs (scriptSigs) use only standard transaction forms */ -bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs, bool taproot_active); +bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs); /** * Check if the transaction is over standard P2WSH resources limit: * 3600bytes witnessScript size, 80bytes per witness stack element, 100 witness stack elements diff --git a/src/policy/rbf.cpp b/src/policy/rbf.cpp index 8125b41c41..7e6b0cf245 100644 --- a/src/policy/rbf.cpp +++ b/src/policy/rbf.cpp @@ -3,13 +3,17 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include <policy/rbf.h> + +#include <policy/settings.h> +#include <tinyformat.h> +#include <util/moneystr.h> #include <util/rbf.h> RBFTransactionState IsRBFOptIn(const CTransaction& tx, const CTxMemPool& pool) { AssertLockHeld(pool.cs); - CTxMemPool::setEntries setAncestors; + CTxMemPool::setEntries ancestors; // First check the transaction itself. if (SignalsOptInRBF(tx)) { @@ -18,7 +22,7 @@ RBFTransactionState IsRBFOptIn(const CTransaction& tx, const CTxMemPool& pool) // If this transaction is not in our mempool, then we can't be sure // we will know about all its inputs. - if (!pool.exists(tx.GetHash())) { + if (!pool.exists(GenTxid::Txid(tx.GetHash()))) { return RBFTransactionState::UNKNOWN; } @@ -27,9 +31,9 @@ RBFTransactionState IsRBFOptIn(const CTransaction& tx, const CTxMemPool& pool) uint64_t noLimit = std::numeric_limits<uint64_t>::max(); std::string dummy; CTxMemPoolEntry entry = *pool.mapTx.find(tx.GetHash()); - pool.CalculateMemPoolAncestors(entry, setAncestors, noLimit, noLimit, noLimit, noLimit, dummy, false); + pool.CalculateMemPoolAncestors(entry, ancestors, noLimit, noLimit, noLimit, noLimit, dummy, false); - for (CTxMemPool::txiter it : setAncestors) { + for (CTxMemPool::txiter it : ancestors) { if (SignalsOptInRBF(it->GetTx())) { return RBFTransactionState::REPLACEABLE_BIP125; } @@ -42,3 +46,131 @@ RBFTransactionState IsRBFOptInEmptyMempool(const CTransaction& tx) // If we don't have a local mempool we can only check the transaction itself. return SignalsOptInRBF(tx) ? RBFTransactionState::REPLACEABLE_BIP125 : RBFTransactionState::UNKNOWN; } + +std::optional<std::string> GetEntriesForConflicts(const CTransaction& tx, + CTxMemPool& pool, + const CTxMemPool::setEntries& iters_conflicting, + CTxMemPool::setEntries& all_conflicts) +{ + AssertLockHeld(pool.cs); + const uint256 txid = tx.GetHash(); + uint64_t nConflictingCount = 0; + for (const auto& mi : iters_conflicting) { + nConflictingCount += mi->GetCountWithDescendants(); + // BIP125 Rule #5: don't consider replacing more than MAX_BIP125_REPLACEMENT_CANDIDATES + // entries from the mempool. This potentially overestimates the number of actual + // descendants (i.e. if multiple conflicts share a descendant, it will be counted multiple + // times), but we just want to be conservative to avoid doing too much work. + if (nConflictingCount > MAX_BIP125_REPLACEMENT_CANDIDATES) { + return strprintf("rejecting replacement %s; too many potential replacements (%d > %d)\n", + txid.ToString(), + nConflictingCount, + MAX_BIP125_REPLACEMENT_CANDIDATES); + } + } + // Calculate the set of all transactions that would have to be evicted. + for (CTxMemPool::txiter it : iters_conflicting) { + pool.CalculateDescendants(it, all_conflicts); + } + return std::nullopt; +} + +std::optional<std::string> HasNoNewUnconfirmed(const CTransaction& tx, + const CTxMemPool& pool, + const CTxMemPool::setEntries& iters_conflicting) +{ + AssertLockHeld(pool.cs); + std::set<uint256> parents_of_conflicts; + for (const auto& mi : iters_conflicting) { + for (const CTxIn& txin : mi->GetTx().vin) { + parents_of_conflicts.insert(txin.prevout.hash); + } + } + + for (unsigned int j = 0; j < tx.vin.size(); j++) { + // BIP125 Rule #2: We don't want to accept replacements that require low feerate junk to be + // mined first. Ideally we'd keep track of the ancestor feerates and make the decision + // based on that, but for now requiring all new inputs to be confirmed works. + // + // Note that if you relax this to make RBF a little more useful, this may break the + // CalculateMempoolAncestors RBF relaxation which subtracts the conflict count/size from the + // descendant limit. + if (!parents_of_conflicts.count(tx.vin[j].prevout.hash)) { + // Rather than check the UTXO set - potentially expensive - it's cheaper to just check + // if the new input refers to a tx that's in the mempool. + if (pool.exists(GenTxid::Txid(tx.vin[j].prevout.hash))) { + return strprintf("replacement %s adds unconfirmed input, idx %d", + tx.GetHash().ToString(), j); + } + } + } + return std::nullopt; +} + +std::optional<std::string> EntriesAndTxidsDisjoint(const CTxMemPool::setEntries& ancestors, + const std::set<uint256>& direct_conflicts, + const uint256& txid) +{ + for (CTxMemPool::txiter ancestorIt : ancestors) { + const uint256& hashAncestor = ancestorIt->GetTx().GetHash(); + if (direct_conflicts.count(hashAncestor)) { + return strprintf("%s spends conflicting transaction %s", + txid.ToString(), + hashAncestor.ToString()); + } + } + return std::nullopt; +} + +std::optional<std::string> PaysMoreThanConflicts(const CTxMemPool::setEntries& iters_conflicting, + CFeeRate replacement_feerate, + const uint256& txid) +{ + for (const auto& mi : iters_conflicting) { + // Don't allow the replacement to reduce the feerate of the mempool. + // + // We usually don't want to accept replacements with lower feerates than what they replaced + // as that would lower the feerate of the next block. Requiring that the feerate always be + // increased is also an easy-to-reason about way to prevent DoS attacks via replacements. + // + // We only consider the feerates of transactions being directly replaced, not their indirect + // descendants. While that does mean high feerate children are ignored when deciding whether + // or not to replace, we do require the replacement to pay more overall fees too, mitigating + // most cases. + CFeeRate original_feerate(mi->GetModifiedFee(), mi->GetTxSize()); + if (replacement_feerate <= original_feerate) { + return strprintf("rejecting replacement %s; new feerate %s <= old feerate %s", + txid.ToString(), + replacement_feerate.ToString(), + original_feerate.ToString()); + } + } + return std::nullopt; +} + +std::optional<std::string> PaysForRBF(CAmount original_fees, + CAmount replacement_fees, + size_t replacement_vsize, + CFeeRate relay_fee, + const uint256& txid) +{ + // BIP125 Rule #3: The replacement fees must be greater than or equal to fees of the + // transactions it replaces, otherwise the bandwidth used by those conflicting transactions + // would not be paid for. + if (replacement_fees < original_fees) { + return strprintf("rejecting replacement %s, less fees than conflicting txs; %s < %s", + txid.ToString(), FormatMoney(replacement_fees), FormatMoney(original_fees)); + } + + // BIP125 Rule #4: The new transaction must pay for its own bandwidth. Otherwise, we have a DoS + // vector where attackers can cause a transaction to be replaced (and relayed) repeatedly by + // increasing the fee by tiny amounts. + CAmount additional_fees = replacement_fees - original_fees; + if (additional_fees < relay_fee.GetFee(replacement_vsize)) { + return strprintf("rejecting replacement %s, not enough additional fees to relay; %s < %s", + txid.ToString(), + FormatMoney(additional_fees), + FormatMoney(relay_fee.GetFee(replacement_vsize))); + } + return std::nullopt; +} diff --git a/src/policy/rbf.h b/src/policy/rbf.h index e078070c1c..be8c2e5b8b 100644 --- a/src/policy/rbf.h +++ b/src/policy/rbf.h @@ -5,7 +5,16 @@ #ifndef BITCOIN_POLICY_RBF_H #define BITCOIN_POLICY_RBF_H +#include <primitives/transaction.h> #include <txmempool.h> +#include <uint256.h> + +#include <optional> +#include <string> + +/** Maximum number of transactions that can be replaced by BIP125 RBF (Rule #5). This includes all + * mempool conflicts and their descendants. */ +static constexpr uint32_t MAX_BIP125_REPLACEMENT_CANDIDATES{100}; /** The rbf state of unconfirmed transactions */ enum class RBFTransactionState { @@ -31,4 +40,63 @@ enum class RBFTransactionState { RBFTransactionState IsRBFOptIn(const CTransaction& tx, const CTxMemPool& pool) EXCLUSIVE_LOCKS_REQUIRED(pool.cs); RBFTransactionState IsRBFOptInEmptyMempool(const CTransaction& tx); +/** Get all descendants of iters_conflicting. Also enforce BIP125 Rule #5, "The number of original + * transactions to be replaced and their descendant transactions which will be evicted from the + * mempool must not exceed a total of 100 transactions." Quit as early as possible. There cannot be + * more than MAX_BIP125_REPLACEMENT_CANDIDATES potential entries. + * @param[in] iters_conflicting The set of iterators to mempool entries. + * @param[out] all_conflicts Populated with all the mempool entries that would be replaced, + * which includes descendants of iters_conflicting. Not cleared at + * the start; any existing mempool entries will remain in the set. + * @returns an error message if Rule #5 is broken, otherwise a std::nullopt. + */ +std::optional<std::string> GetEntriesForConflicts(const CTransaction& tx, CTxMemPool& pool, + const CTxMemPool::setEntries& iters_conflicting, + CTxMemPool::setEntries& all_conflicts) + EXCLUSIVE_LOCKS_REQUIRED(pool.cs); + +/** BIP125 Rule #2: "The replacement transaction may only include an unconfirmed input if that input + * was included in one of the original transactions." + * @returns error message if Rule #2 is broken, otherwise std::nullopt. */ +std::optional<std::string> HasNoNewUnconfirmed(const CTransaction& tx, const CTxMemPool& pool, + const CTxMemPool::setEntries& iters_conflicting) + EXCLUSIVE_LOCKS_REQUIRED(pool.cs); + +/** Check the intersection between two sets of transactions (a set of mempool entries and a set of + * txids) to make sure they are disjoint. + * @param[in] ancestors Set of mempool entries corresponding to ancestors of the + * replacement transactions. + * @param[in] direct_conflicts Set of txids corresponding to the mempool conflicts + * (candidates to be replaced). + * @param[in] txid Transaction ID, included in the error message if violation occurs. + * @returns error message if the sets intersect, std::nullopt if they are disjoint. + */ +std::optional<std::string> EntriesAndTxidsDisjoint(const CTxMemPool::setEntries& ancestors, + const std::set<uint256>& direct_conflicts, + const uint256& txid); + +/** Check that the feerate of the replacement transaction(s) is higher than the feerate of each + * of the transactions in iters_conflicting. + * @param[in] iters_conflicting The set of mempool entries. + * @returns error message if fees insufficient, otherwise std::nullopt. + */ +std::optional<std::string> PaysMoreThanConflicts(const CTxMemPool::setEntries& iters_conflicting, + CFeeRate replacement_feerate, const uint256& txid); + +/** Enforce BIP125 Rule #3 "The replacement transaction pays an absolute fee of at least the sum + * paid by the original transactions." Enforce BIP125 Rule #4 "The replacement transaction must also + * pay for its own bandwidth at or above the rate set by the node's minimum relay fee setting." + * @param[in] original_fees Total modified fees of original transaction(s). + * @param[in] replacement_fees Total modified fees of replacement transaction(s). + * @param[in] replacement_vsize Total virtual size of replacement transaction(s). + * @param[in] relay_fee The node's minimum feerate for transaction relay. + * @param[in] txid Transaction ID, included in the error message if violation occurs. + * @returns error string if fees are insufficient, otherwise std::nullopt. + */ +std::optional<std::string> PaysForRBF(CAmount original_fees, + CAmount replacement_fees, + size_t replacement_vsize, + CFeeRate relay_fee, + const uint256& txid); + #endif // BITCOIN_POLICY_RBF_H |