diff options
author | Ava Chow <github@achow101.com> | 2024-07-02 17:49:32 -0400 |
---|---|---|
committer | Ava Chow <github@achow101.com> | 2024-07-02 17:49:32 -0400 |
commit | 3325a0afa45ee88c9e859f1002183f1bd61868da (patch) | |
tree | 9fd7c07e064f118ce45b476fb399629d23f83757 /src | |
parent | 9251bc71110412ff248ee99fbb215d84b60c8c7c (diff) | |
parent | 926b8e39dcbc0a3a8a75ef0a29bdca2bf738d746 (diff) |
Merge bitcoin/bitcoin#30272: doc: use TRUC instead of v3 and add release note
926b8e39dcbc0a3a8a75ef0a29bdca2bf738d746 [doc] add release note for TRUC (glozow)
19a9b90617419f68d0f1c90ee115b5220be99a16 use version=3 instead of v3 in debug strings (glozow)
881fac8e609be17eb71bd9a54c0284b304e2e2e2 scripted-diff: change names from V3 to TRUC (glozow)
a573dd261748d2a80560f73db08f7dca788c7fcf [doc] replace mentions of v3 with TRUC (glozow)
089b5757dff39a9a06cdb625aaced9beeb72958d rename mempool_accept_v3.py to mempool_truc.py (glozow)
f543852a89d93441645250c40c3980aeb0c3b664 rename policy/v3_policy.* to policy/truc_policy.* (glozow)
Pull request description:
Adds a release note for TRUC policy which will be live in v28.0.
For clarity, replaces mentions of "v3" with "TRUC" in most places. Suggested in
- https://github.com/bitcoin/bitcoin/pull/29496#discussion_r1629749583
- https://github.com/bitcoin/bitcoin/pull/29496#discussion_r1624500904
I changed error strings from "v3-violation" to "TRUC-violation" but left v3 in the debug strings because I think it might be clearer for somebody who is debugging. Similarly, I left some variables unchanged because I think they're more descriptive this way, e.g. `tx_v3_from_v2_and_v3`. I'm happy to debate places that should or shouldn't be documented differently in this PR, whatever is clearest to everyone.
ACKs for top commit:
instagibbs:
reACK https://github.com/bitcoin/bitcoin/pull/30272/commits/926b8e39dcbc0a3a8a75ef0a29bdca2bf738d746
achow101:
ACK 926b8e39dcbc0a3a8a75ef0a29bdca2bf738d746
ismaelsadeeq:
Code review ACK 926b8e39dcbc0a3a8a75ef0a29bdca2bf738d746
Tree-SHA512: 16c88add0a29dc6d1236c4d45f34a17b850f6727b231953cbd52eb9f7268d1d802563eadfc8b7928c94ed3d7a615275dd103e57e81439ebf3ba2b12efa1e42af
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 8 | ||||
-rw-r--r-- | src/policy/truc_policy.cpp (renamed from src/policy/v3_policy.cpp) | 84 | ||||
-rw-r--r-- | src/policy/truc_policy.h | 94 | ||||
-rw-r--r-- | src/policy/v3_policy.h | 93 | ||||
-rw-r--r-- | src/test/fuzz/package_eval.cpp | 6 | ||||
-rw-r--r-- | src/test/fuzz/tx_pool.cpp | 6 | ||||
-rw-r--r-- | src/test/txvalidation_tests.cpp | 100 | ||||
-rw-r--r-- | src/test/util/txmempool.cpp | 22 | ||||
-rw-r--r-- | src/test/util/txmempool.h | 14 | ||||
-rw-r--r-- | src/validation.cpp | 30 |
10 files changed, 229 insertions, 228 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 52d4aae893..183d196a7b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -244,7 +244,6 @@ BITCOIN_CORE_H = \ node/warnings.h \ noui.h \ outputtype.h \ - policy/v3_policy.h \ policy/feerate.h \ policy/fees.h \ policy/fees_args.h \ @@ -252,6 +251,7 @@ BITCOIN_CORE_H = \ policy/policy.h \ policy/rbf.h \ policy/settings.h \ + policy/truc_policy.h \ pow.h \ protocol.h \ psbt.h \ @@ -448,12 +448,12 @@ libbitcoin_node_a_SOURCES = \ node/validation_cache_args.cpp \ node/warnings.cpp \ noui.cpp \ - policy/v3_policy.cpp \ policy/fees.cpp \ policy/fees_args.cpp \ policy/packages.cpp \ policy/rbf.cpp \ policy/settings.cpp \ + policy/truc_policy.cpp \ pow.cpp \ rest.cpp \ rpc/blockchain.cpp \ @@ -708,9 +708,9 @@ libbitcoin_common_a_SOURCES = \ netbase.cpp \ net_permissions.cpp \ outputtype.cpp \ - policy/v3_policy.cpp \ policy/feerate.cpp \ policy/policy.cpp \ + policy/truc_policy.cpp \ protocol.cpp \ psbt.cpp \ rpc/external_signer.cpp \ @@ -955,12 +955,12 @@ libbitcoinkernel_la_SOURCES = \ node/blockstorage.cpp \ node/chainstate.cpp \ node/utxo_snapshot.cpp \ - policy/v3_policy.cpp \ policy/feerate.cpp \ policy/packages.cpp \ policy/policy.cpp \ policy/rbf.cpp \ policy/settings.cpp \ + policy/truc_policy.cpp \ pow.cpp \ primitives/block.cpp \ primitives/transaction.cpp \ diff --git a/src/policy/v3_policy.cpp b/src/policy/truc_policy.cpp index 6bd043b8e3..69e8d5ed1d 100644 --- a/src/policy/v3_policy.cpp +++ b/src/policy/truc_policy.cpp @@ -2,7 +2,7 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include <policy/v3_policy.h> +#include <policy/truc_policy.h> #include <coins.h> #include <consensus/amount.h> @@ -14,7 +14,7 @@ #include <numeric> #include <vector> -/** Helper for PackageV3Checks: Returns a vector containing the indices of transactions (within +/** Helper for PackageTRUCChecks: Returns a vector containing the indices of transactions (within * package) that are direct parents of ptx. */ std::vector<size_t> FindInPackageParents(const Package& package, const CTransactionRef& ptx) { @@ -37,13 +37,13 @@ std::vector<size_t> FindInPackageParents(const Package& package, const CTransact return in_package_parents; } -/** Helper for PackageV3Checks, storing info for a mempool or package parent. */ +/** Helper for PackageTRUCChecks, storing info for a mempool or package parent. */ struct ParentInfo { /** Txid used to identify this parent by prevout */ const Txid& m_txid; /** Wtxid used for debug string */ const Wtxid& m_wtxid; - /** version used to check inheritance of v3 and non-v3 */ + /** version used to check inheritance of TRUC and non-TRUC */ decltype(CTransaction::version) m_version; /** If parent is in mempool, whether it has any descendants in mempool. */ bool m_has_mempool_descendant; @@ -55,36 +55,36 @@ struct ParentInfo { {} }; -std::optional<std::string> PackageV3Checks(const CTransactionRef& ptx, int64_t vsize, +std::optional<std::string> PackageTRUCChecks(const CTransactionRef& ptx, int64_t vsize, const Package& package, const CTxMemPool::setEntries& mempool_ancestors) { // This function is specialized for these limits, and must be reimplemented if they ever change. - static_assert(V3_ANCESTOR_LIMIT == 2); - static_assert(V3_DESCENDANT_LIMIT == 2); + static_assert(TRUC_ANCESTOR_LIMIT == 2); + static_assert(TRUC_DESCENDANT_LIMIT == 2); const auto in_package_parents{FindInPackageParents(package, ptx)}; - // Now we have all ancestors, so we can start checking v3 rules. + // Now we have all ancestors, so we can start checking TRUC rules. if (ptx->version == TRUC_VERSION) { - // SingleV3Checks should have checked this already. - if (!Assume(vsize <= V3_MAX_VSIZE)) { - return strprintf("v3 tx %s (wtxid=%s) is too big: %u > %u virtual bytes", - ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString(), vsize, V3_MAX_VSIZE); + // SingleTRUCChecks should have checked this already. + if (!Assume(vsize <= TRUC_MAX_VSIZE)) { + return strprintf("version=3 tx %s (wtxid=%s) is too big: %u > %u virtual bytes", + ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString(), vsize, TRUC_MAX_VSIZE); } - if (mempool_ancestors.size() + in_package_parents.size() + 1 > V3_ANCESTOR_LIMIT) { + if (mempool_ancestors.size() + in_package_parents.size() + 1 > TRUC_ANCESTOR_LIMIT) { return strprintf("tx %s (wtxid=%s) would have too many ancestors", ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString()); } const bool has_parent{mempool_ancestors.size() + in_package_parents.size() > 0}; if (has_parent) { - // A v3 child cannot be too large. - if (vsize > V3_CHILD_MAX_VSIZE) { - return strprintf("v3 child tx %s (wtxid=%s) is too big: %u > %u virtual bytes", + // A TRUC child cannot be too large. + if (vsize > TRUC_CHILD_MAX_VSIZE) { + return strprintf("version=3 child tx %s (wtxid=%s) is too big: %u > %u virtual bytes", ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString(), - vsize, V3_CHILD_MAX_VSIZE); + vsize, TRUC_CHILD_MAX_VSIZE); } // Exactly 1 parent exists, either in mempool or package. Find it. @@ -107,7 +107,7 @@ std::optional<std::string> PackageV3Checks(const CTransactionRef& ptx, int64_t v // If there is a parent, it must have the right version. if (parent_info.m_version != TRUC_VERSION) { - return strprintf("v3 tx %s (wtxid=%s) cannot spend from non-v3 tx %s (wtxid=%s)", + return strprintf("version=3 tx %s (wtxid=%s) cannot spend from non-version=3 tx %s (wtxid=%s)", ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString(), parent_info.m_txid.ToString(), parent_info.m_wtxid.ToString()); } @@ -118,7 +118,7 @@ std::optional<std::string> PackageV3Checks(const CTransactionRef& ptx, int64_t v for (auto& input : package_tx->vin) { // Fail if we find another tx with the same parent. We don't check whether the - // sibling is to-be-replaced (done in SingleV3Checks) because these transactions + // sibling is to-be-replaced (done in SingleTRUCChecks) because these transactions // are within the same package. if (input.prevout.hash == parent_info.m_txid) { return strprintf("tx %s (wtxid=%s) would exceed descendant count limit", @@ -140,17 +140,17 @@ std::optional<std::string> PackageV3Checks(const CTransactionRef& ptx, int64_t v } } } else { - // Non-v3 transactions cannot have v3 parents. + // Non-TRUC transactions cannot have TRUC parents. for (auto it : mempool_ancestors) { if (it->GetTx().version == TRUC_VERSION) { - return strprintf("non-v3 tx %s (wtxid=%s) cannot spend from v3 tx %s (wtxid=%s)", + return strprintf("non-version=3 tx %s (wtxid=%s) cannot spend from version=3 tx %s (wtxid=%s)", ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString(), it->GetSharedTx()->GetHash().ToString(), it->GetSharedTx()->GetWitnessHash().ToString()); } } for (const auto& index: in_package_parents) { if (package.at(index)->version == TRUC_VERSION) { - return strprintf("non-v3 tx %s (wtxid=%s) cannot spend from v3 tx %s (wtxid=%s)", + return strprintf("non-version=3 tx %s (wtxid=%s) cannot spend from version=3 tx %s (wtxid=%s)", ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString(), package.at(index)->GetHash().ToString(), @@ -161,20 +161,20 @@ std::optional<std::string> PackageV3Checks(const CTransactionRef& ptx, int64_t v return std::nullopt; } -std::optional<std::pair<std::string, CTransactionRef>> SingleV3Checks(const CTransactionRef& ptx, +std::optional<std::pair<std::string, CTransactionRef>> SingleTRUCChecks(const CTransactionRef& ptx, const CTxMemPool::setEntries& mempool_ancestors, const std::set<Txid>& direct_conflicts, int64_t vsize) { - // Check v3 and non-v3 inheritance. + // Check TRUC and non-TRUC inheritance. for (const auto& entry : mempool_ancestors) { if (ptx->version != TRUC_VERSION && entry->GetTx().version == TRUC_VERSION) { - return std::make_pair(strprintf("non-v3 tx %s (wtxid=%s) cannot spend from v3 tx %s (wtxid=%s)", + return std::make_pair(strprintf("non-version=3 tx %s (wtxid=%s) cannot spend from version=3 tx %s (wtxid=%s)", ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString(), entry->GetSharedTx()->GetHash().ToString(), entry->GetSharedTx()->GetWitnessHash().ToString()), nullptr); } else if (ptx->version == TRUC_VERSION && entry->GetTx().version != TRUC_VERSION) { - return std::make_pair(strprintf("v3 tx %s (wtxid=%s) cannot spend from non-v3 tx %s (wtxid=%s)", + return std::make_pair(strprintf("version=3 tx %s (wtxid=%s) cannot spend from non-version=3 tx %s (wtxid=%s)", ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString(), entry->GetSharedTx()->GetHash().ToString(), entry->GetSharedTx()->GetWitnessHash().ToString()), nullptr); @@ -182,20 +182,20 @@ std::optional<std::pair<std::string, CTransactionRef>> SingleV3Checks(const CTra } // This function is specialized for these limits, and must be reimplemented if they ever change. - static_assert(V3_ANCESTOR_LIMIT == 2); - static_assert(V3_DESCENDANT_LIMIT == 2); + static_assert(TRUC_ANCESTOR_LIMIT == 2); + static_assert(TRUC_DESCENDANT_LIMIT == 2); // The rest of the rules only apply to transactions with version=3. if (ptx->version != TRUC_VERSION) return std::nullopt; - if (vsize > V3_MAX_VSIZE) { - return std::make_pair(strprintf("v3 tx %s (wtxid=%s) is too big: %u > %u virtual bytes", - ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString(), vsize, V3_MAX_VSIZE), + if (vsize > TRUC_MAX_VSIZE) { + return std::make_pair(strprintf("version=3 tx %s (wtxid=%s) is too big: %u > %u virtual bytes", + ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString(), vsize, TRUC_MAX_VSIZE), nullptr); } - // Check that V3_ANCESTOR_LIMIT would not be violated. - if (mempool_ancestors.size() + 1 > V3_ANCESTOR_LIMIT) { + // Check that TRUC_ANCESTOR_LIMIT would not be violated. + if (mempool_ancestors.size() + 1 > TRUC_ANCESTOR_LIMIT) { return std::make_pair(strprintf("tx %s (wtxid=%s) would have too many ancestors", ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString()), nullptr); @@ -203,10 +203,10 @@ std::optional<std::pair<std::string, CTransactionRef>> SingleV3Checks(const CTra // Remaining checks only pertain to transactions with unconfirmed ancestors. if (mempool_ancestors.size() > 0) { - // If this transaction spends V3 parents, it cannot be too large. - if (vsize > V3_CHILD_MAX_VSIZE) { - return std::make_pair(strprintf("v3 child tx %s (wtxid=%s) is too big: %u > %u virtual bytes", - ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString(), vsize, V3_CHILD_MAX_VSIZE), + // If this transaction spends TRUC parents, it cannot be too large. + if (vsize > TRUC_CHILD_MAX_VSIZE) { + return std::make_pair(strprintf("version=3 child tx %s (wtxid=%s) is too big: %u > %u virtual bytes", + ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString(), vsize, TRUC_CHILD_MAX_VSIZE), nullptr); } @@ -217,14 +217,14 @@ std::optional<std::pair<std::string, CTransactionRef>> SingleV3Checks(const CTra // possible through a reorg. const auto& children = parent_entry->GetMemPoolChildrenConst(); // Don't double-count a transaction that is going to be replaced. This logic assumes that - // any descendant of the V3 transaction is a direct child, which makes sense because a V3 - // transaction can only have 1 descendant. + // any descendant of the TRUC transaction is a direct child, which makes sense because a + // TRUC transaction can only have 1 descendant. const bool child_will_be_replaced = !children.empty() && std::any_of(children.cbegin(), children.cend(), [&direct_conflicts](const CTxMemPoolEntry& child){return direct_conflicts.count(child.GetTx().GetHash()) > 0;}); - if (parent_entry->GetCountWithDescendants() + 1 > V3_DESCENDANT_LIMIT && !child_will_be_replaced) { - // Allow sibling eviction for v3 transaction: if another child already exists, even if - // we don't conflict inputs with it, consider evicting it under RBF rules. We rely on v3 rules + if (parent_entry->GetCountWithDescendants() + 1 > TRUC_DESCENDANT_LIMIT && !child_will_be_replaced) { + // Allow sibling eviction for TRUC transaction: if another child already exists, even if + // we don't conflict inputs with it, consider evicting it under RBF rules. We rely on TRUC rules // only permitting 1 descendant, as otherwise we would need to have logic for deciding // which descendant to evict. Skip if this isn't true, e.g. if the transaction has // multiple children or the sibling also has descendants due to a reorg. diff --git a/src/policy/truc_policy.h b/src/policy/truc_policy.h new file mode 100644 index 0000000000..dbc77696c6 --- /dev/null +++ b/src/policy/truc_policy.h @@ -0,0 +1,94 @@ +// Copyright (c) 2022 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_TRUC_POLICY_H +#define BITCOIN_POLICY_TRUC_POLICY_H + +#include <consensus/amount.h> +#include <policy/packages.h> +#include <policy/policy.h> +#include <primitives/transaction.h> +#include <txmempool.h> +#include <util/result.h> + +#include <set> +#include <string> + +// This module enforces rules for BIP 431 TRUC transactions which help make +// RBF abilities more robust. A transaction with version=3 is treated as TRUC. +static constexpr decltype(CTransaction::version) TRUC_VERSION{3}; + +// TRUC only allows 1 parent and 1 child when unconfirmed. This translates to a descendant set size +// of 2 and ancestor set size of 2. +/** Maximum number of transactions including an unconfirmed tx and its descendants. */ +static constexpr unsigned int TRUC_DESCENDANT_LIMIT{2}; +/** Maximum number of transactions including a TRUC tx and all its mempool ancestors. */ +static constexpr unsigned int TRUC_ANCESTOR_LIMIT{2}; + +/** Maximum sigop-adjusted virtual size of all v3 transactions. */ +static constexpr int64_t TRUC_MAX_VSIZE{10000}; +/** Maximum sigop-adjusted virtual size of a tx which spends from an unconfirmed TRUC transaction. */ +static constexpr int64_t TRUC_CHILD_MAX_VSIZE{1000}; +// These limits are within the default ancestor/descendant limits. +static_assert(TRUC_MAX_VSIZE + TRUC_CHILD_MAX_VSIZE <= DEFAULT_ANCESTOR_SIZE_LIMIT_KVB * 1000); +static_assert(TRUC_MAX_VSIZE + TRUC_CHILD_MAX_VSIZE <= DEFAULT_DESCENDANT_SIZE_LIMIT_KVB * 1000); + +/** Must be called for every transaction, even if not TRUC. Not strictly necessary for transactions + * accepted through AcceptMultipleTransactions. + * + * Checks the following rules: + * 1. A TRUC tx must only have TRUC unconfirmed ancestors. + * 2. A non-TRUC tx must only have non-TRUC unconfirmed ancestors. + * 3. A TRUC's ancestor set, including itself, must be within TRUC_ANCESTOR_LIMIT. + * 4. A TRUC's descendant set, including itself, must be within TRUC_DESCENDANT_LIMIT. + * 5. If a TRUC tx has any unconfirmed ancestors, the tx's sigop-adjusted vsize must be within + * TRUC_CHILD_MAX_VSIZE. + * 6. A TRUC tx must be within TRUC_MAX_VSIZE. + * + * + * @param[in] mempool_ancestors The in-mempool ancestors of ptx. + * @param[in] direct_conflicts In-mempool transactions this tx conflicts with. These conflicts + * are used to more accurately calculate the resulting descendant + * count of in-mempool ancestors. + * @param[in] vsize The sigop-adjusted virtual size of ptx. + * + * @returns 3 possibilities: + * - std::nullopt if all TRUC checks were applied successfully + * - debug string + pointer to a mempool sibling if this transaction would be the second child in a + * 1-parent-1-child cluster; the caller may consider evicting the specified sibling or return an + * error with the debug string. + * - debug string + nullptr if this transaction violates some TRUC rule and sibling eviction is not + * applicable. + */ +std::optional<std::pair<std::string, CTransactionRef>> SingleTRUCChecks(const CTransactionRef& ptx, + const CTxMemPool::setEntries& mempool_ancestors, + const std::set<Txid>& direct_conflicts, + int64_t vsize); + +/** Must be called for every transaction that is submitted within a package, even if not TRUC. + * + * For each transaction in a package: + * If it's not a TRUC transaction, verify it has no direct TRUC parents in the mempool or the package. + + * If it is a TRUC transaction, verify that any direct parents in the mempool or the package are TRUC. + * If such a parent exists, verify that parent has no other children in the package or the mempool, + * and that the transaction itself has no children in the package. + * + * If any TRUC violations in the package exist, this test will fail for one of them: + * - if a TRUC transaction T has a parent in the mempool and a child in the package, then PTRUCC(T) will fail + * - if a TRUC transaction T has a parent in the package and a child in the package, then PTRUCC(T) will fail + * - if a TRUC transaction T and a TRUC (sibling) transaction U have some parent in the mempool, + * then PTRUCC(T) and PTRUCC(U) will fail + * - if a TRUC transaction T and a TRUC (sibling) transaction U have some parent in the package, + * then PTRUCC(T) and PTRUCC(U) will fail + * - if a TRUC transaction T has a parent P and a grandparent G in the package, then + * PTRUCC(P) will fail (though PTRUCC(G) and PTRUCC(T) might succeed). + * + * @returns debug string if an error occurs, std::nullopt otherwise. + * */ +std::optional<std::string> PackageTRUCChecks(const CTransactionRef& ptx, int64_t vsize, + const Package& package, + const CTxMemPool::setEntries& mempool_ancestors); + +#endif // BITCOIN_POLICY_TRUC_POLICY_H diff --git a/src/policy/v3_policy.h b/src/policy/v3_policy.h deleted file mode 100644 index 90eaeda46f..0000000000 --- a/src/policy/v3_policy.h +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright (c) 2022 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_V3_POLICY_H -#define BITCOIN_POLICY_V3_POLICY_H - -#include <consensus/amount.h> -#include <policy/packages.h> -#include <policy/policy.h> -#include <primitives/transaction.h> -#include <txmempool.h> -#include <util/result.h> - -#include <set> -#include <string> - -// This module enforces rules for BIP 431 TRUC transactions (with version=3) which help make -// RBF abilities more robust. -static constexpr decltype(CTransaction::version) TRUC_VERSION{3}; - -// v3 only allows 1 parent and 1 child when unconfirmed. -/** Maximum number of transactions including an unconfirmed tx and its descendants. */ -static constexpr unsigned int V3_DESCENDANT_LIMIT{2}; -/** Maximum number of transactions including a V3 tx and all its mempool ancestors. */ -static constexpr unsigned int V3_ANCESTOR_LIMIT{2}; - -/** Maximum sigop-adjusted virtual size of all v3 transactions. */ -static constexpr int64_t V3_MAX_VSIZE{10000}; -/** Maximum sigop-adjusted virtual size of a tx which spends from an unconfirmed v3 transaction. */ -static constexpr int64_t V3_CHILD_MAX_VSIZE{1000}; -// These limits are within the default ancestor/descendant limits. -static_assert(V3_MAX_VSIZE + V3_CHILD_MAX_VSIZE <= DEFAULT_ANCESTOR_SIZE_LIMIT_KVB * 1000); -static_assert(V3_MAX_VSIZE + V3_CHILD_MAX_VSIZE <= DEFAULT_DESCENDANT_SIZE_LIMIT_KVB * 1000); - -/** Must be called for every transaction, even if not v3. Not strictly necessary for transactions - * accepted through AcceptMultipleTransactions. - * - * Checks the following rules: - * 1. A v3 tx must only have v3 unconfirmed ancestors. - * 2. A non-v3 tx must only have non-v3 unconfirmed ancestors. - * 3. A v3's ancestor set, including itself, must be within V3_ANCESTOR_LIMIT. - * 4. A v3's descendant set, including itself, must be within V3_DESCENDANT_LIMIT. - * 5. If a v3 tx has any unconfirmed ancestors, the tx's sigop-adjusted vsize must be within - * V3_CHILD_MAX_VSIZE. - * 6. A v3 tx must be within V3_MAX_VSIZE. - * - * - * @param[in] mempool_ancestors The in-mempool ancestors of ptx. - * @param[in] direct_conflicts In-mempool transactions this tx conflicts with. These conflicts - * are used to more accurately calculate the resulting descendant - * count of in-mempool ancestors. - * @param[in] vsize The sigop-adjusted virtual size of ptx. - * - * @returns 3 possibilities: - * - std::nullopt if all v3 checks were applied successfully - * - debug string + pointer to a mempool sibling if this transaction would be the second child in a - * 1-parent-1-child cluster; the caller may consider evicting the specified sibling or return an - * error with the debug string. - * - debug string + nullptr if this transaction violates some v3 rule and sibling eviction is not - * applicable. - */ -std::optional<std::pair<std::string, CTransactionRef>> SingleV3Checks(const CTransactionRef& ptx, - const CTxMemPool::setEntries& mempool_ancestors, - const std::set<Txid>& direct_conflicts, - int64_t vsize); - -/** Must be called for every transaction that is submitted within a package, even if not v3. - * - * For each transaction in a package: - * If it's not a v3 transaction, verify it has no direct v3 parents in the mempool or the package. - - * If it is a v3 transaction, verify that any direct parents in the mempool or the package are v3. - * If such a parent exists, verify that parent has no other children in the package or the mempool, - * and that the transaction itself has no children in the package. - * - * If any v3 violations in the package exist, this test will fail for one of them: - * - if a v3 transaction T has a parent in the mempool and a child in the package, then PV3C(T) will fail - * - if a v3 transaction T has a parent in the package and a child in the package, then PV3C(T) will fail - * - if a v3 transaction T and a v3 (sibling) transaction U have some parent in the mempool, - * then PV3C(T) and PV3C(U) will fail - * - if a v3 transaction T and a v3 (sibling) transaction U have some parent in the package, - * then PV3C(T) and PV3C(U) will fail - * - if a v3 transaction T has a parent P and a grandparent G in the package, then - * PV3C(P) will fail (though PV3C(G) and PV3C(T) might succeed). - * - * @returns debug string if an error occurs, std::nullopt otherwise. - * */ -std::optional<std::string> PackageV3Checks(const CTransactionRef& ptx, int64_t vsize, - const Package& package, - const CTxMemPool::setEntries& mempool_ancestors); - -#endif // BITCOIN_POLICY_V3_POLICY_H diff --git a/src/test/fuzz/package_eval.cpp b/src/test/fuzz/package_eval.cpp index 53aedf23ea..652c7a7609 100644 --- a/src/test/fuzz/package_eval.cpp +++ b/src/test/fuzz/package_eval.cpp @@ -6,7 +6,7 @@ #include <node/context.h> #include <node/mempool_args.h> #include <node/miner.h> -#include <policy/v3_policy.h> +#include <policy/truc_policy.h> #include <test/fuzz/FuzzedDataProvider.h> #include <test/fuzz/fuzz.h> #include <test/fuzz/util.h> @@ -225,7 +225,7 @@ FUZZ_TARGET(tx_package_eval, .init = initialize_tx_pool) tx_mut.vin.emplace_back(); } - // Make a p2pk output to make sigops adjusted vsize to violate v3, potentially, which is never spent + // Make a p2pk output to make sigops adjusted vsize to violate TRUC rules, potentially, which is never spent if (last_tx && amount_in > 1000 && fuzzed_data_provider.ConsumeBool()) { tx_mut.vout.emplace_back(1000, CScript() << std::vector<unsigned char>(33, 0x02) << OP_CHECKSIG); // Don't add any other outputs. @@ -320,7 +320,7 @@ FUZZ_TARGET(tx_package_eval, .init = initialize_tx_pool) Assert(result_package.m_tx_results.size() == txs.size() || result_package.m_tx_results.empty()); } - CheckMempoolV3Invariants(tx_pool); + CheckMempoolTRUCInvariants(tx_pool); } node.validation_signals->UnregisterSharedValidationInterface(outpoints_updater); diff --git a/src/test/fuzz/tx_pool.cpp b/src/test/fuzz/tx_pool.cpp index b6b91445f9..64861311db 100644 --- a/src/test/fuzz/tx_pool.cpp +++ b/src/test/fuzz/tx_pool.cpp @@ -6,7 +6,7 @@ #include <node/context.h> #include <node/mempool_args.h> #include <node/miner.h> -#include <policy/v3_policy.h> +#include <policy/truc_policy.h> #include <test/fuzz/FuzzedDataProvider.h> #include <test/fuzz/fuzz.h> #include <test/fuzz/util.h> @@ -320,7 +320,7 @@ FUZZ_TARGET(tx_pool_standard, .init = initialize_tx_pool) if (accepted) { Assert(added.size() == 1); // For now, no package acceptance Assert(tx == *added.begin()); - CheckMempoolV3Invariants(tx_pool); + CheckMempoolTRUCInvariants(tx_pool); } else { // Do not consider rejected transaction removed removed.erase(tx); @@ -413,7 +413,7 @@ FUZZ_TARGET(tx_pool, .init = initialize_tx_pool) const bool accepted = res.m_result_type == MempoolAcceptResult::ResultType::VALID; if (accepted) { txids.push_back(tx->GetHash()); - CheckMempoolV3Invariants(tx_pool); + CheckMempoolTRUCInvariants(tx_pool); } } Finish(fuzzed_data_provider, tx_pool, chainstate); diff --git a/src/test/txvalidation_tests.cpp b/src/test/txvalidation_tests.cpp index f429f94a2f..97b27ef370 100644 --- a/src/test/txvalidation_tests.cpp +++ b/src/test/txvalidation_tests.cpp @@ -4,9 +4,9 @@ #include <consensus/validation.h> #include <key_io.h> -#include <policy/v3_policy.h> #include <policy/packages.h> #include <policy/policy.h> +#include <policy/truc_policy.h> #include <primitives/transaction.h> #include <random.h> #include <script/script.h> @@ -91,7 +91,7 @@ static inline CTransactionRef make_tx(const std::vector<COutPoint>& inputs, int3 BOOST_FIXTURE_TEST_CASE(version3_tests, RegTestingSetup) { - // Test V3 policy helper functions + // Test TRUC policy helper functions CTxMemPool& pool = *Assert(m_node.mempool); LOCK2(cs_main, pool.cs); TestMemPoolEntryHelper entry; @@ -105,77 +105,77 @@ BOOST_FIXTURE_TEST_CASE(version3_tests, RegTestingSetup) // Default values. CTxMemPool::Limits m_limits{}; - // Cannot spend from an unconfirmed v3 transaction unless this tx is also v3. + // Cannot spend from an unconfirmed TRUC transaction unless this tx is also TRUC. { // mempool_tx_v3 // ^ // tx_v2_from_v3 auto tx_v2_from_v3 = make_tx({COutPoint{mempool_tx_v3->GetHash(), 0}}, /*version=*/2); auto ancestors_v2_from_v3{pool.CalculateMemPoolAncestors(entry.FromTx(tx_v2_from_v3), m_limits)}; - const auto expected_error_str{strprintf("non-v3 tx %s (wtxid=%s) cannot spend from v3 tx %s (wtxid=%s)", + const auto expected_error_str{strprintf("non-version=3 tx %s (wtxid=%s) cannot spend from version=3 tx %s (wtxid=%s)", tx_v2_from_v3->GetHash().ToString(), tx_v2_from_v3->GetWitnessHash().ToString(), mempool_tx_v3->GetHash().ToString(), mempool_tx_v3->GetWitnessHash().ToString())}; - auto result_v2_from_v3{SingleV3Checks(tx_v2_from_v3, *ancestors_v2_from_v3, empty_conflicts_set, GetVirtualTransactionSize(*tx_v2_from_v3))}; + auto result_v2_from_v3{SingleTRUCChecks(tx_v2_from_v3, *ancestors_v2_from_v3, empty_conflicts_set, GetVirtualTransactionSize(*tx_v2_from_v3))}; BOOST_CHECK_EQUAL(result_v2_from_v3->first, expected_error_str); BOOST_CHECK_EQUAL(result_v2_from_v3->second, nullptr); Package package_v3_v2{mempool_tx_v3, tx_v2_from_v3}; - BOOST_CHECK_EQUAL(*PackageV3Checks(tx_v2_from_v3, GetVirtualTransactionSize(*tx_v2_from_v3), package_v3_v2, empty_ancestors), expected_error_str); + BOOST_CHECK_EQUAL(*PackageTRUCChecks(tx_v2_from_v3, GetVirtualTransactionSize(*tx_v2_from_v3), package_v3_v2, empty_ancestors), expected_error_str); CTxMemPool::setEntries entries_mempool_v3{pool.GetIter(mempool_tx_v3->GetHash().ToUint256()).value()}; - BOOST_CHECK_EQUAL(*PackageV3Checks(tx_v2_from_v3, GetVirtualTransactionSize(*tx_v2_from_v3), {tx_v2_from_v3}, entries_mempool_v3), expected_error_str); + BOOST_CHECK_EQUAL(*PackageTRUCChecks(tx_v2_from_v3, GetVirtualTransactionSize(*tx_v2_from_v3), {tx_v2_from_v3}, entries_mempool_v3), expected_error_str); // mempool_tx_v3 mempool_tx_v2 // ^ ^ // tx_v2_from_v2_and_v3 auto tx_v2_from_v2_and_v3 = make_tx({COutPoint{mempool_tx_v3->GetHash(), 0}, COutPoint{mempool_tx_v2->GetHash(), 0}}, /*version=*/2); auto ancestors_v2_from_both{pool.CalculateMemPoolAncestors(entry.FromTx(tx_v2_from_v2_and_v3), m_limits)}; - const auto expected_error_str_2{strprintf("non-v3 tx %s (wtxid=%s) cannot spend from v3 tx %s (wtxid=%s)", + const auto expected_error_str_2{strprintf("non-version=3 tx %s (wtxid=%s) cannot spend from version=3 tx %s (wtxid=%s)", tx_v2_from_v2_and_v3->GetHash().ToString(), tx_v2_from_v2_and_v3->GetWitnessHash().ToString(), mempool_tx_v3->GetHash().ToString(), mempool_tx_v3->GetWitnessHash().ToString())}; - auto result_v2_from_both{SingleV3Checks(tx_v2_from_v2_and_v3, *ancestors_v2_from_both, empty_conflicts_set, GetVirtualTransactionSize(*tx_v2_from_v2_and_v3))}; + auto result_v2_from_both{SingleTRUCChecks(tx_v2_from_v2_and_v3, *ancestors_v2_from_both, empty_conflicts_set, GetVirtualTransactionSize(*tx_v2_from_v2_and_v3))}; BOOST_CHECK_EQUAL(result_v2_from_both->first, expected_error_str_2); BOOST_CHECK_EQUAL(result_v2_from_both->second, nullptr); Package package_v3_v2_v2{mempool_tx_v3, mempool_tx_v2, tx_v2_from_v2_and_v3}; - BOOST_CHECK_EQUAL(*PackageV3Checks(tx_v2_from_v2_and_v3, GetVirtualTransactionSize(*tx_v2_from_v2_and_v3), package_v3_v2_v2, empty_ancestors), expected_error_str_2); + BOOST_CHECK_EQUAL(*PackageTRUCChecks(tx_v2_from_v2_and_v3, GetVirtualTransactionSize(*tx_v2_from_v2_and_v3), package_v3_v2_v2, empty_ancestors), expected_error_str_2); } - // V3 cannot spend from an unconfirmed non-v3 transaction. + // TRUC cannot spend from an unconfirmed non-TRUC transaction. { // mempool_tx_v2 // ^ // tx_v3_from_v2 auto tx_v3_from_v2 = make_tx({COutPoint{mempool_tx_v2->GetHash(), 0}}, /*version=*/3); auto ancestors_v3_from_v2{pool.CalculateMemPoolAncestors(entry.FromTx(tx_v3_from_v2), m_limits)}; - const auto expected_error_str{strprintf("v3 tx %s (wtxid=%s) cannot spend from non-v3 tx %s (wtxid=%s)", + const auto expected_error_str{strprintf("version=3 tx %s (wtxid=%s) cannot spend from non-version=3 tx %s (wtxid=%s)", tx_v3_from_v2->GetHash().ToString(), tx_v3_from_v2->GetWitnessHash().ToString(), mempool_tx_v2->GetHash().ToString(), mempool_tx_v2->GetWitnessHash().ToString())}; - auto result_v3_from_v2{SingleV3Checks(tx_v3_from_v2, *ancestors_v3_from_v2, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_from_v2))}; + auto result_v3_from_v2{SingleTRUCChecks(tx_v3_from_v2, *ancestors_v3_from_v2, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_from_v2))}; BOOST_CHECK_EQUAL(result_v3_from_v2->first, expected_error_str); BOOST_CHECK_EQUAL(result_v3_from_v2->second, nullptr); Package package_v2_v3{mempool_tx_v2, tx_v3_from_v2}; - BOOST_CHECK_EQUAL(*PackageV3Checks(tx_v3_from_v2, GetVirtualTransactionSize(*tx_v3_from_v2), package_v2_v3, empty_ancestors), expected_error_str); + BOOST_CHECK_EQUAL(*PackageTRUCChecks(tx_v3_from_v2, GetVirtualTransactionSize(*tx_v3_from_v2), package_v2_v3, empty_ancestors), expected_error_str); CTxMemPool::setEntries entries_mempool_v2{pool.GetIter(mempool_tx_v2->GetHash().ToUint256()).value()}; - BOOST_CHECK_EQUAL(*PackageV3Checks(tx_v3_from_v2, GetVirtualTransactionSize(*tx_v3_from_v2), {tx_v3_from_v2}, entries_mempool_v2), expected_error_str); + BOOST_CHECK_EQUAL(*PackageTRUCChecks(tx_v3_from_v2, GetVirtualTransactionSize(*tx_v3_from_v2), {tx_v3_from_v2}, entries_mempool_v2), expected_error_str); // mempool_tx_v3 mempool_tx_v2 // ^ ^ // tx_v3_from_v2_and_v3 auto tx_v3_from_v2_and_v3 = make_tx({COutPoint{mempool_tx_v3->GetHash(), 0}, COutPoint{mempool_tx_v2->GetHash(), 0}}, /*version=*/3); auto ancestors_v3_from_both{pool.CalculateMemPoolAncestors(entry.FromTx(tx_v3_from_v2_and_v3), m_limits)}; - const auto expected_error_str_2{strprintf("v3 tx %s (wtxid=%s) cannot spend from non-v3 tx %s (wtxid=%s)", + const auto expected_error_str_2{strprintf("version=3 tx %s (wtxid=%s) cannot spend from non-version=3 tx %s (wtxid=%s)", tx_v3_from_v2_and_v3->GetHash().ToString(), tx_v3_from_v2_and_v3->GetWitnessHash().ToString(), mempool_tx_v2->GetHash().ToString(), mempool_tx_v2->GetWitnessHash().ToString())}; - auto result_v3_from_both{SingleV3Checks(tx_v3_from_v2_and_v3, *ancestors_v3_from_both, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_from_v2_and_v3))}; + auto result_v3_from_both{SingleTRUCChecks(tx_v3_from_v2_and_v3, *ancestors_v3_from_both, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_from_v2_and_v3))}; BOOST_CHECK_EQUAL(result_v3_from_both->first, expected_error_str_2); BOOST_CHECK_EQUAL(result_v3_from_both->second, nullptr); - // tx_v3_from_v2_and_v3 also violates V3_ANCESTOR_LIMIT. + // tx_v3_from_v2_and_v3 also violates TRUC_ANCESTOR_LIMIT. const auto expected_error_str_3{strprintf("tx %s (wtxid=%s) would have too many ancestors", tx_v3_from_v2_and_v3->GetHash().ToString(), tx_v3_from_v2_and_v3->GetWitnessHash().ToString())}; Package package_v3_v2_v3{mempool_tx_v3, mempool_tx_v2, tx_v3_from_v2_and_v3}; - BOOST_CHECK_EQUAL(*PackageV3Checks(tx_v3_from_v2_and_v3, GetVirtualTransactionSize(*tx_v3_from_v2_and_v3), package_v3_v2_v3, empty_ancestors), expected_error_str_3); + BOOST_CHECK_EQUAL(*PackageTRUCChecks(tx_v3_from_v2_and_v3, GetVirtualTransactionSize(*tx_v3_from_v2_and_v3), package_v3_v2_v3, empty_ancestors), expected_error_str_3); } // V3 from V3 is ok, and non-V3 from non-V3 is ok. { @@ -184,25 +184,25 @@ BOOST_FIXTURE_TEST_CASE(version3_tests, RegTestingSetup) // tx_v3_from_v3 auto tx_v3_from_v3 = make_tx({COutPoint{mempool_tx_v3->GetHash(), 0}}, /*version=*/3); auto ancestors_v3{pool.CalculateMemPoolAncestors(entry.FromTx(tx_v3_from_v3), m_limits)}; - BOOST_CHECK(SingleV3Checks(tx_v3_from_v3, *ancestors_v3, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_from_v3)) + BOOST_CHECK(SingleTRUCChecks(tx_v3_from_v3, *ancestors_v3, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_from_v3)) == std::nullopt); Package package_v3_v3{mempool_tx_v3, tx_v3_from_v3}; - BOOST_CHECK(PackageV3Checks(tx_v3_from_v3, GetVirtualTransactionSize(*tx_v3_from_v3), package_v3_v3, empty_ancestors) == std::nullopt); + BOOST_CHECK(PackageTRUCChecks(tx_v3_from_v3, GetVirtualTransactionSize(*tx_v3_from_v3), package_v3_v3, empty_ancestors) == std::nullopt); // mempool_tx_v2 // ^ // tx_v2_from_v2 auto tx_v2_from_v2 = make_tx({COutPoint{mempool_tx_v2->GetHash(), 0}}, /*version=*/2); auto ancestors_v2{pool.CalculateMemPoolAncestors(entry.FromTx(tx_v2_from_v2), m_limits)}; - BOOST_CHECK(SingleV3Checks(tx_v2_from_v2, *ancestors_v2, empty_conflicts_set, GetVirtualTransactionSize(*tx_v2_from_v2)) + BOOST_CHECK(SingleTRUCChecks(tx_v2_from_v2, *ancestors_v2, empty_conflicts_set, GetVirtualTransactionSize(*tx_v2_from_v2)) == std::nullopt); Package package_v2_v2{mempool_tx_v2, tx_v2_from_v2}; - BOOST_CHECK(PackageV3Checks(tx_v2_from_v2, GetVirtualTransactionSize(*tx_v2_from_v2), package_v2_v2, empty_ancestors) == std::nullopt); + BOOST_CHECK(PackageTRUCChecks(tx_v2_from_v2, GetVirtualTransactionSize(*tx_v2_from_v2), package_v2_v2, empty_ancestors) == std::nullopt); } - // Tx spending v3 cannot have too many mempool ancestors + // Tx spending TRUC cannot have too many mempool ancestors // Configuration where the tx has multiple direct parents. { Package package_multi_parents; @@ -221,11 +221,11 @@ BOOST_FIXTURE_TEST_CASE(version3_tests, RegTestingSetup) BOOST_CHECK_EQUAL(ancestors->size(), 3); const auto expected_error_str{strprintf("tx %s (wtxid=%s) would have too many ancestors", tx_v3_multi_parent->GetHash().ToString(), tx_v3_multi_parent->GetWitnessHash().ToString())}; - auto result{SingleV3Checks(tx_v3_multi_parent, *ancestors, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_multi_parent))}; + auto result{SingleTRUCChecks(tx_v3_multi_parent, *ancestors, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_multi_parent))}; BOOST_CHECK_EQUAL(result->first, expected_error_str); BOOST_CHECK_EQUAL(result->second, nullptr); - BOOST_CHECK_EQUAL(*PackageV3Checks(tx_v3_multi_parent, GetVirtualTransactionSize(*tx_v3_multi_parent), package_multi_parents, empty_ancestors), + BOOST_CHECK_EQUAL(*PackageTRUCChecks(tx_v3_multi_parent, GetVirtualTransactionSize(*tx_v3_multi_parent), package_multi_parents, empty_ancestors), expected_error_str); } @@ -246,34 +246,34 @@ BOOST_FIXTURE_TEST_CASE(version3_tests, RegTestingSetup) auto ancestors{pool.CalculateMemPoolAncestors(entry.FromTx(tx_v3_multi_gen), m_limits)}; const auto expected_error_str{strprintf("tx %s (wtxid=%s) would have too many ancestors", tx_v3_multi_gen->GetHash().ToString(), tx_v3_multi_gen->GetWitnessHash().ToString())}; - auto result{SingleV3Checks(tx_v3_multi_gen, *ancestors, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_multi_gen))}; + auto result{SingleTRUCChecks(tx_v3_multi_gen, *ancestors, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_multi_gen))}; BOOST_CHECK_EQUAL(result->first, expected_error_str); BOOST_CHECK_EQUAL(result->second, nullptr); // Middle tx is what triggers a failure for the grandchild: - BOOST_CHECK_EQUAL(*PackageV3Checks(middle_tx, GetVirtualTransactionSize(*middle_tx), package_multi_gen, empty_ancestors), expected_error_str); - BOOST_CHECK(PackageV3Checks(tx_v3_multi_gen, GetVirtualTransactionSize(*tx_v3_multi_gen), package_multi_gen, empty_ancestors) == std::nullopt); + BOOST_CHECK_EQUAL(*PackageTRUCChecks(middle_tx, GetVirtualTransactionSize(*middle_tx), package_multi_gen, empty_ancestors), expected_error_str); + BOOST_CHECK(PackageTRUCChecks(tx_v3_multi_gen, GetVirtualTransactionSize(*tx_v3_multi_gen), package_multi_gen, empty_ancestors) == std::nullopt); } - // Tx spending v3 cannot be too large in virtual size. + // Tx spending TRUC cannot be too large in virtual size. auto many_inputs{random_outpoints(100)}; many_inputs.emplace_back(mempool_tx_v3->GetHash(), 0); { auto tx_v3_child_big = make_tx(many_inputs, /*version=*/3); const auto vsize{GetVirtualTransactionSize(*tx_v3_child_big)}; auto ancestors{pool.CalculateMemPoolAncestors(entry.FromTx(tx_v3_child_big), m_limits)}; - const auto expected_error_str{strprintf("v3 child tx %s (wtxid=%s) is too big: %u > %u virtual bytes", - tx_v3_child_big->GetHash().ToString(), tx_v3_child_big->GetWitnessHash().ToString(), vsize, V3_CHILD_MAX_VSIZE)}; - auto result{SingleV3Checks(tx_v3_child_big, *ancestors, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_child_big))}; + const auto expected_error_str{strprintf("version=3 child tx %s (wtxid=%s) is too big: %u > %u virtual bytes", + tx_v3_child_big->GetHash().ToString(), tx_v3_child_big->GetWitnessHash().ToString(), vsize, TRUC_CHILD_MAX_VSIZE)}; + auto result{SingleTRUCChecks(tx_v3_child_big, *ancestors, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_child_big))}; BOOST_CHECK_EQUAL(result->first, expected_error_str); BOOST_CHECK_EQUAL(result->second, nullptr); Package package_child_big{mempool_tx_v3, tx_v3_child_big}; - BOOST_CHECK_EQUAL(*PackageV3Checks(tx_v3_child_big, GetVirtualTransactionSize(*tx_v3_child_big), package_child_big, empty_ancestors), + BOOST_CHECK_EQUAL(*PackageTRUCChecks(tx_v3_child_big, GetVirtualTransactionSize(*tx_v3_child_big), package_child_big, empty_ancestors), expected_error_str); } - // Tx spending v3 cannot have too many sigops. + // Tx spending TRUC cannot have too many sigops. // This child has 10 P2WSH multisig inputs. auto multisig_outpoints{random_outpoints(10)}; multisig_outpoints.emplace_back(mempool_tx_v3->GetHash(), 0); @@ -302,34 +302,34 @@ BOOST_FIXTURE_TEST_CASE(version3_tests, RegTestingSetup) BOOST_CHECK_EQUAL(total_sigops, tx_many_sigops->vin.size() * MAX_PUBKEYS_PER_MULTISIG); const int64_t bip141_vsize{GetVirtualTransactionSize(*tx_many_sigops)}; // Weight limit is not reached... - BOOST_CHECK(SingleV3Checks(tx_many_sigops, *ancestors, empty_conflicts_set, bip141_vsize) == std::nullopt); + BOOST_CHECK(SingleTRUCChecks(tx_many_sigops, *ancestors, empty_conflicts_set, bip141_vsize) == std::nullopt); // ...but sigop limit is. - const auto expected_error_str{strprintf("v3 child tx %s (wtxid=%s) is too big: %u > %u virtual bytes", + const auto expected_error_str{strprintf("version=3 child tx %s (wtxid=%s) is too big: %u > %u virtual bytes", tx_many_sigops->GetHash().ToString(), tx_many_sigops->GetWitnessHash().ToString(), - total_sigops * DEFAULT_BYTES_PER_SIGOP / WITNESS_SCALE_FACTOR, V3_CHILD_MAX_VSIZE)}; - auto result{SingleV3Checks(tx_many_sigops, *ancestors, empty_conflicts_set, + total_sigops * DEFAULT_BYTES_PER_SIGOP / WITNESS_SCALE_FACTOR, TRUC_CHILD_MAX_VSIZE)}; + auto result{SingleTRUCChecks(tx_many_sigops, *ancestors, empty_conflicts_set, GetVirtualTransactionSize(*tx_many_sigops, /*nSigOpCost=*/total_sigops, /*bytes_per_sigop=*/ DEFAULT_BYTES_PER_SIGOP))}; BOOST_CHECK_EQUAL(result->first, expected_error_str); BOOST_CHECK_EQUAL(result->second, nullptr); Package package_child_sigops{mempool_tx_v3, tx_many_sigops}; - BOOST_CHECK_EQUAL(*PackageV3Checks(tx_many_sigops, total_sigops * DEFAULT_BYTES_PER_SIGOP / WITNESS_SCALE_FACTOR, package_child_sigops, empty_ancestors), + BOOST_CHECK_EQUAL(*PackageTRUCChecks(tx_many_sigops, total_sigops * DEFAULT_BYTES_PER_SIGOP / WITNESS_SCALE_FACTOR, package_child_sigops, empty_ancestors), expected_error_str); } - // Parent + child with v3 in the mempool. Child is allowed as long as it is under V3_CHILD_MAX_VSIZE. + // Parent + child with TRUC in the mempool. Child is allowed as long as it is under TRUC_CHILD_MAX_VSIZE. auto tx_mempool_v3_child = make_tx({COutPoint{mempool_tx_v3->GetHash(), 0}}, /*version=*/3); { - BOOST_CHECK(GetTransactionWeight(*tx_mempool_v3_child) <= V3_CHILD_MAX_VSIZE * WITNESS_SCALE_FACTOR); + BOOST_CHECK(GetTransactionWeight(*tx_mempool_v3_child) <= TRUC_CHILD_MAX_VSIZE * WITNESS_SCALE_FACTOR); auto ancestors{pool.CalculateMemPoolAncestors(entry.FromTx(tx_mempool_v3_child), m_limits)}; - BOOST_CHECK(SingleV3Checks(tx_mempool_v3_child, *ancestors, empty_conflicts_set, GetVirtualTransactionSize(*tx_mempool_v3_child)) == std::nullopt); + BOOST_CHECK(SingleTRUCChecks(tx_mempool_v3_child, *ancestors, empty_conflicts_set, GetVirtualTransactionSize(*tx_mempool_v3_child)) == std::nullopt); pool.addUnchecked(entry.FromTx(tx_mempool_v3_child)); Package package_v3_1p1c{mempool_tx_v3, tx_mempool_v3_child}; - BOOST_CHECK(PackageV3Checks(tx_mempool_v3_child, GetVirtualTransactionSize(*tx_mempool_v3_child), package_v3_1p1c, empty_ancestors) == std::nullopt); + BOOST_CHECK(PackageTRUCChecks(tx_mempool_v3_child, GetVirtualTransactionSize(*tx_mempool_v3_child), package_v3_1p1c, empty_ancestors) == std::nullopt); } - // A v3 transaction cannot have more than 1 descendant. Sibling is returned when exactly 1 exists. + // A TRUC transaction cannot have more than 1 descendant. Sibling is returned when exactly 1 exists. { auto tx_v3_child2 = make_tx({COutPoint{mempool_tx_v3->GetHash(), 1}}, /*version=*/3); @@ -337,17 +337,17 @@ BOOST_FIXTURE_TEST_CASE(version3_tests, RegTestingSetup) auto ancestors_1sibling{pool.CalculateMemPoolAncestors(entry.FromTx(tx_v3_child2), m_limits)}; const auto expected_error_str{strprintf("tx %s (wtxid=%s) would exceed descendant count limit", mempool_tx_v3->GetHash().ToString(), mempool_tx_v3->GetWitnessHash().ToString())}; - auto result_with_sibling_eviction{SingleV3Checks(tx_v3_child2, *ancestors_1sibling, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_child2))}; + auto result_with_sibling_eviction{SingleTRUCChecks(tx_v3_child2, *ancestors_1sibling, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_child2))}; BOOST_CHECK_EQUAL(result_with_sibling_eviction->first, expected_error_str); // The other mempool child is returned to allow for sibling eviction. BOOST_CHECK_EQUAL(result_with_sibling_eviction->second, tx_mempool_v3_child); // If directly replacing the child, make sure there is no double-counting. - BOOST_CHECK(SingleV3Checks(tx_v3_child2, *ancestors_1sibling, {tx_mempool_v3_child->GetHash()}, GetVirtualTransactionSize(*tx_v3_child2)) + BOOST_CHECK(SingleTRUCChecks(tx_v3_child2, *ancestors_1sibling, {tx_mempool_v3_child->GetHash()}, GetVirtualTransactionSize(*tx_v3_child2)) == std::nullopt); Package package_v3_1p2c{mempool_tx_v3, tx_mempool_v3_child, tx_v3_child2}; - BOOST_CHECK_EQUAL(*PackageV3Checks(tx_v3_child2, GetVirtualTransactionSize(*tx_v3_child2), package_v3_1p2c, empty_ancestors), + BOOST_CHECK_EQUAL(*PackageTRUCChecks(tx_v3_child2, GetVirtualTransactionSize(*tx_v3_child2), package_v3_1p2c, empty_ancestors), expected_error_str); // Configuration where parent already has 2 other children in mempool (no sibling eviction allowed). This may happen as the result of a reorg. @@ -357,7 +357,7 @@ BOOST_FIXTURE_TEST_CASE(version3_tests, RegTestingSetup) BOOST_CHECK_EQUAL(entry_mempool_parent->GetCountWithDescendants(), 3); auto ancestors_2siblings{pool.CalculateMemPoolAncestors(entry.FromTx(tx_v3_child3), m_limits)}; - auto result_2children{SingleV3Checks(tx_v3_child3, *ancestors_2siblings, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_child3))}; + auto result_2children{SingleTRUCChecks(tx_v3_child3, *ancestors_2siblings, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_child3))}; BOOST_CHECK_EQUAL(result_2children->first, expected_error_str); // The other mempool child is not returned because sibling eviction is not allowed. BOOST_CHECK_EQUAL(result_2children->second, nullptr); @@ -377,7 +377,7 @@ BOOST_FIXTURE_TEST_CASE(version3_tests, RegTestingSetup) auto ancestors_3gen{pool.CalculateMemPoolAncestors(entry.FromTx(tx_to_submit), m_limits)}; const auto expected_error_str{strprintf("tx %s (wtxid=%s) would exceed descendant count limit", tx_mempool_grandparent->GetHash().ToString(), tx_mempool_grandparent->GetWitnessHash().ToString())}; - auto result_3gen{SingleV3Checks(tx_to_submit, *ancestors_3gen, empty_conflicts_set, GetVirtualTransactionSize(*tx_to_submit))}; + auto result_3gen{SingleTRUCChecks(tx_to_submit, *ancestors_3gen, empty_conflicts_set, GetVirtualTransactionSize(*tx_to_submit))}; BOOST_CHECK_EQUAL(result_3gen->first, expected_error_str); // The other mempool child is not returned because sibling eviction is not allowed. BOOST_CHECK_EQUAL(result_3gen->second, nullptr); diff --git a/src/test/util/txmempool.cpp b/src/test/util/txmempool.cpp index 94d50bba50..9d6b4810d0 100644 --- a/src/test/util/txmempool.cpp +++ b/src/test/util/txmempool.cpp @@ -8,7 +8,7 @@ #include <node/context.h> #include <node/mempool_args.h> #include <policy/rbf.h> -#include <policy/v3_policy.h> +#include <policy/truc_policy.h> #include <txmempool.h> #include <util/check.h> #include <util/time.h> @@ -141,30 +141,30 @@ std::optional<std::string> CheckPackageMempoolAcceptResult(const Package& txns, return std::nullopt; } -void CheckMempoolV3Invariants(const CTxMemPool& tx_pool) +void CheckMempoolTRUCInvariants(const CTxMemPool& tx_pool) { LOCK(tx_pool.cs); for (const auto& tx_info : tx_pool.infoAll()) { const auto& entry = *Assert(tx_pool.GetEntry(tx_info.tx->GetHash())); if (tx_info.tx->version == TRUC_VERSION) { // Check that special maximum virtual size is respected - Assert(entry.GetTxSize() <= V3_MAX_VSIZE); + Assert(entry.GetTxSize() <= TRUC_MAX_VSIZE); - // Check that special v3 ancestor/descendant limits and rules are always respected - Assert(entry.GetCountWithDescendants() <= V3_DESCENDANT_LIMIT); - Assert(entry.GetCountWithAncestors() <= V3_ANCESTOR_LIMIT); - Assert(entry.GetSizeWithDescendants() <= V3_MAX_VSIZE + V3_CHILD_MAX_VSIZE); - Assert(entry.GetSizeWithAncestors() <= V3_MAX_VSIZE + V3_CHILD_MAX_VSIZE); + // Check that special TRUC ancestor/descendant limits and rules are always respected + Assert(entry.GetCountWithDescendants() <= TRUC_DESCENDANT_LIMIT); + Assert(entry.GetCountWithAncestors() <= TRUC_ANCESTOR_LIMIT); + Assert(entry.GetSizeWithDescendants() <= TRUC_MAX_VSIZE + TRUC_CHILD_MAX_VSIZE); + Assert(entry.GetSizeWithAncestors() <= TRUC_MAX_VSIZE + TRUC_CHILD_MAX_VSIZE); // If this transaction has at least 1 ancestor, it's a "child" and has restricted weight. if (entry.GetCountWithAncestors() > 1) { - Assert(entry.GetTxSize() <= V3_CHILD_MAX_VSIZE); - // All v3 transactions must only have v3 unconfirmed parents. + Assert(entry.GetTxSize() <= TRUC_CHILD_MAX_VSIZE); + // All TRUC transactions must only have TRUC unconfirmed parents. const auto& parents = entry.GetMemPoolParentsConst(); Assert(parents.begin()->get().GetSharedTx()->version == TRUC_VERSION); } } else if (entry.GetCountWithAncestors() > 1) { - // All non-v3 transactions must only have non-v3 unconfirmed parents. + // All non-TRUC transactions must only have non-TRUC unconfirmed parents. for (const auto& parent : entry.GetMemPoolParentsConst()) { Assert(parent.get().GetSharedTx()->version != TRUC_VERSION); } diff --git a/src/test/util/txmempool.h b/src/test/util/txmempool.h index b3022af7df..6d41fdf87f 100644 --- a/src/test/util/txmempool.h +++ b/src/test/util/txmempool.h @@ -47,13 +47,13 @@ std::optional<std::string> CheckPackageMempoolAcceptResult(const Package& txns, bool expect_valid, const CTxMemPool* mempool); -/** For every transaction in tx_pool, check v3 invariants: - * - a v3 tx's ancestor count must be within V3_ANCESTOR_LIMIT - * - a v3 tx's descendant count must be within V3_DESCENDANT_LIMIT - * - if a v3 tx has ancestors, its sigop-adjusted vsize must be within V3_CHILD_MAX_VSIZE - * - any non-v3 tx must only have non-v3 parents - * - any v3 tx must only have v3 parents +/** For every transaction in tx_pool, check TRUC invariants: + * - a TRUC tx's ancestor count must be within TRUC_ANCESTOR_LIMIT + * - a TRUC tx's descendant count must be within TRUC_DESCENDANT_LIMIT + * - if a TRUC tx has ancestors, its sigop-adjusted vsize must be within TRUC_CHILD_MAX_VSIZE + * - any non-TRUC tx must only have non-TRUC parents + * - any TRUC tx must only have TRUC parents * */ -void CheckMempoolV3Invariants(const CTxMemPool& tx_pool); +void CheckMempoolTRUCInvariants(const CTxMemPool& tx_pool); #endif // BITCOIN_TEST_UTIL_TXMEMPOOL_H diff --git a/src/validation.cpp b/src/validation.cpp index 4241d51d15..7b586c45b8 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -35,7 +35,7 @@ #include <policy/policy.h> #include <policy/rbf.h> #include <policy/settings.h> -#include <policy/v3_policy.h> +#include <policy/truc_policy.h> #include <pow.h> #include <primitives/block.h> #include <primitives/transaction.h> @@ -336,7 +336,7 @@ void Chainstate::MaybeUpdateMempoolForReorg( // Also updates valid entries' cached LockPoints if needed. // If false, the tx is still valid and its lockpoints are updated. // If true, the tx would be invalid in the next block; remove this entry and all of its descendants. - // Note that v3 rules are not applied here, so reorgs may cause violations of v3 inheritance or + // Note that TRUC rules are not applied here, so reorgs may cause violations of TRUC inheritance or // topology restrictions. const auto filter_final_and_mature = [&](CTxMemPool::txiter it) EXCLUSIVE_LOCKS_REQUIRED(m_mempool->cs, ::cs_main) { @@ -829,7 +829,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws) // check all unconfirmed ancestors; otherwise an opt-in ancestor // might be replaced, causing removal of this descendant. // - // All V3 transactions are considered replaceable. + // All TRUC transactions are considered replaceable. // // Replaceability signaling of the original transactions may be // ignored due to node setting. @@ -936,7 +936,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws) // while a tx could be package CPFP'd when entering the mempool, we do not have a DoS-resistant // method of ensuring the tx remains bumped. For example, the fee-bumping child could disappear // due to a replacement. - // The only exception is v3 transactions. + // The only exception is TRUC transactions. if (!bypass_limits && ws.m_ptx->version != TRUC_VERSION && ws.m_modified_fees < m_pool.m_opts.min_relay_feerate.GetFee(ws.m_vsize)) { // Even though this is a fee-related failure, this result is TX_MEMPOOL_POLICY, not // TX_RECONSIDERABLE, because it cannot be bypassed using package validation. @@ -1005,7 +1005,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws) // If the new transaction is relatively small (up to 40k weight) // and has at most one ancestor (ie ancestor limit of 2, including // the new transaction), allow it if its parent has exactly the - // descendant limit descendants. The transaction also cannot be v3, + // descendant limit descendants. The transaction also cannot be TRUC, // as its topology restrictions do not allow a second child. // // This allows protocols which rely on distrusting counterparties @@ -1032,7 +1032,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws) // Even though just checking direct mempool parents for inheritance would be sufficient, we // check using the full ancestor set here because it's more convenient to use what we have // already calculated. - if (const auto err{SingleV3Checks(ws.m_ptx, ws.m_ancestors, ws.m_conflicts, ws.m_vsize)}) { + if (const auto err{SingleTRUCChecks(ws.m_ptx, ws.m_ancestors, ws.m_conflicts, ws.m_vsize)}) { // Single transaction contexts only. if (args.m_allow_sibling_eviction && err->second != nullptr) { // We should only be considering where replacement is considered valid as well. @@ -1043,15 +1043,15 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws) ws.m_conflicts.insert(err->second->GetHash()); // Adding the sibling to m_iters_conflicting here means that it doesn't count towards // RBF Carve Out above. This is correct, since removing to-be-replaced transactions from - // the descendant count is done separately in SingleV3Checks for v3 transactions. + // the descendant count is done separately in SingleTRUCChecks for TRUC transactions. ws.m_iters_conflicting.insert(m_pool.GetIter(err->second->GetHash()).value()); ws.m_sibling_eviction = true; // The sibling will be treated as part of the to-be-replaced set in ReplacementChecks. - // Note that we are not checking whether it opts in to replaceability via BIP125 or v3 - // (which is normally done in PreChecks). However, the only way a v3 transaction can - // have a non-v3 and non-BIP125 descendant is due to a reorg. + // Note that we are not checking whether it opts in to replaceability via BIP125 or TRUC + // (which is normally done in PreChecks). However, the only way a TRUC transaction can + // have a non-TRUC and non-BIP125 descendant is due to a reorg. } else { - return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "v3-rule-violation", err->first); + return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "TRUC-violation", err->first); } } @@ -1103,7 +1103,7 @@ bool MemPoolAccept::ReplacementChecks(Workspace& ws) } // Enforce Rule #2. if (const auto err_string{HasNoNewUnconfirmed(tx, m_pool, m_subpackage.m_all_conflicts)}) { - // Sibling eviction is only done for v3 transactions, which cannot have multiple ancestors. + // Sibling eviction is only done for TRUC transactions, which cannot have multiple ancestors. Assume(!ws.m_sibling_eviction); return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, strprintf("replacement-adds-unconfirmed%s", ws.m_sibling_eviction ? " (including sibling eviction)" : ""), *err_string); @@ -1545,10 +1545,10 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptMultipleTransactions(const std:: } // At this point we have all in-mempool ancestors, and we know every transaction's vsize. - // Run the v3 checks on the package. + // Run the TRUC checks on the package. for (Workspace& ws : workspaces) { - if (auto err{PackageV3Checks(ws.m_ptx, ws.m_vsize, txns, ws.m_ancestors)}) { - package_state.Invalid(PackageValidationResult::PCKG_POLICY, "v3-violation", err.value()); + if (auto err{PackageTRUCChecks(ws.m_ptx, ws.m_vsize, txns, ws.m_ancestors)}) { + package_state.Invalid(PackageValidationResult::PCKG_POLICY, "TRUC-violation", err.value()); return PackageMempoolAcceptResult(package_state, {}); } } |