[doc] replace mentions of v3 with TRUC

Keep mentions of v3 in debug strings to help people who might not know
that TRUC is applied when version=3.
Also keep variable names in tests, as it is less verbose to keep v3 and v2.
This commit is contained in:
glozow 2024-06-11 13:37:05 +01:00
parent 089b5757df
commit a573dd2617
9 changed files with 131 additions and 130 deletions

View File

@ -43,7 +43,7 @@ struct ParentInfo {
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;
@ -65,7 +65,7 @@ std::optional<std::string> PackageV3Checks(const CTransactionRef& ptx, int64_t v
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)) {
@ -80,7 +80,7 @@ std::optional<std::string> PackageV3Checks(const CTransactionRef& ptx, int64_t v
const bool has_parent{mempool_ancestors.size() + in_package_parents.size() > 0};
if (has_parent) {
// A v3 child cannot be too large.
// A TRUC 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",
ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString(),
@ -140,7 +140,7 @@ 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)",
@ -166,7 +166,7 @@ std::optional<std::pair<std::string, CTransactionRef>> SingleV3Checks(const CTra
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)",
@ -203,7 +203,7 @@ 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 this transaction spends TRUC 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),
@ -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
// 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.

View File

@ -15,35 +15,36 @@
#include <set>
#include <string>
// This module enforces rules for BIP 431 TRUC transactions (with version=3) which help make
// RBF abilities more robust.
// 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};
// v3 only allows 1 parent and 1 child when unconfirmed.
// 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 V3_DESCENDANT_LIMIT{2};
/** Maximum number of transactions including a V3 tx and all its mempool ancestors. */
/** Maximum number of transactions including a TRUC 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. */
/** Maximum sigop-adjusted virtual size of a tx which spends from an unconfirmed TRUC 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
/** Must be called for every transaction, even if not TRUC. 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
* 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 V3_ANCESTOR_LIMIT.
* 4. A TRUC's descendant set, including itself, must be within V3_DESCENDANT_LIMIT.
* 5. If a TRUC 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.
* 6. A TRUC tx must be within V3_MAX_VSIZE.
*
*
* @param[in] mempool_ancestors The in-mempool ancestors of ptx.
@ -53,11 +54,11 @@ static_assert(V3_MAX_VSIZE + V3_CHILD_MAX_VSIZE <= DEFAULT_DESCENDANT_SIZE_LIMIT
* @param[in] vsize The sigop-adjusted virtual size of ptx.
*
* @returns 3 possibilities:
* - std::nullopt if all v3 checks were applied successfully
* - 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 v3 rule and sibling eviction is not
* - debug string + nullptr if this transaction violates some TRUC rule and sibling eviction is not
* applicable.
*/
std::optional<std::pair<std::string, CTransactionRef>> SingleV3Checks(const CTransactionRef& ptx,
@ -65,23 +66,23 @@ std::optional<std::pair<std::string, CTransactionRef>> SingleV3Checks(const CTra
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.
/** 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 v3 transaction, verify it has no direct v3 parents in the mempool or the 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 v3 transaction, verify that any direct parents in the mempool or the package are v3.
* 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 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,
* 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 PV3C(T) will fail
* - if a TRUC transaction T has a parent in the package and a child in the package, then PV3C(T) will fail
* - if a TRUC transaction T and a TRUC (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,
* - if a TRUC transaction T and a TRUC (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
* - if a TRUC 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.

View File

@ -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.

View File

@ -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,7 +105,7 @@ 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
// ^
@ -140,7 +140,7 @@ BOOST_FIXTURE_TEST_CASE(version3_tests, RegTestingSetup)
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);
}
// V3 cannot spend from an unconfirmed non-v3 transaction.
// TRUC cannot spend from an unconfirmed non-TRUC transaction.
{
// mempool_tx_v2
// ^
@ -202,7 +202,7 @@ BOOST_FIXTURE_TEST_CASE(version3_tests, RegTestingSetup)
BOOST_CHECK(PackageV3Checks(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;
@ -255,7 +255,7 @@ BOOST_FIXTURE_TEST_CASE(version3_tests, RegTestingSetup)
BOOST_CHECK(PackageV3Checks(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);
{
@ -273,7 +273,7 @@ BOOST_FIXTURE_TEST_CASE(version3_tests, RegTestingSetup)
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);
@ -317,7 +317,7 @@ BOOST_FIXTURE_TEST_CASE(version3_tests, RegTestingSetup)
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 V3_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);
@ -329,7 +329,7 @@ BOOST_FIXTURE_TEST_CASE(version3_tests, RegTestingSetup)
BOOST_CHECK(PackageV3Checks(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);

View File

@ -150,7 +150,7 @@ void CheckMempoolV3Invariants(const CTxMemPool& tx_pool)
// Check that special maximum virtual size is respected
Assert(entry.GetTxSize() <= V3_MAX_VSIZE);
// Check that special v3 ancestor/descendant limits and rules are always respected
// Check that special TRUC 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);
@ -159,12 +159,12 @@ void CheckMempoolV3Invariants(const CTxMemPool& tx_pool)
// 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.
// 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);
}

View File

@ -47,12 +47,12 @@ 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 V3_ANCESTOR_LIMIT
* - a TRUC tx's descendant count must be within V3_DESCENDANT_LIMIT
* - if a TRUC tx has ancestors, its sigop-adjusted vsize must be within V3_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);

View File

@ -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
@ -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 SingleV3Checks 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());
package_state.Invalid(PackageValidationResult::PCKG_POLICY, "TRUC-violation", err.value());
return PackageMempoolAcceptResult(package_state, {});
}
}

View File

@ -39,7 +39,7 @@ def cleanup(extra_args=None):
return wrapper
return decorator
class MempoolAcceptV3(BitcoinTestFramework):
class MempoolTRUC(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
self.extra_args = [[]]
@ -52,23 +52,23 @@ class MempoolAcceptV3(BitcoinTestFramework):
assert all([txid in txids for txid in mempool_contents])
@cleanup(extra_args=["-datacarriersize=20000"])
def test_v3_max_vsize(self):
def test_truc_max_vsize(self):
node = self.nodes[0]
self.log.info("Test v3-specific maximum transaction vsize")
self.log.info("Test TRUC-specific maximum transaction vsize")
tx_v3_heavy = self.wallet.create_self_transfer(target_weight=(V3_MAX_VSIZE + 1) * WITNESS_SCALE_FACTOR, version=3)
assert_greater_than_or_equal(tx_v3_heavy["tx"].get_vsize(), V3_MAX_VSIZE)
expected_error_heavy = f"v3-rule-violation, v3 tx {tx_v3_heavy['txid']} (wtxid={tx_v3_heavy['wtxid']}) is too big"
expected_error_heavy = f"TRUC-violation, v3 tx {tx_v3_heavy['txid']} (wtxid={tx_v3_heavy['wtxid']}) is too big"
assert_raises_rpc_error(-26, expected_error_heavy, node.sendrawtransaction, tx_v3_heavy["hex"])
self.check_mempool([])
# Ensure we are hitting the v3-specific limit and not something else
# Ensure we are hitting the TRUC-specific limit and not something else
tx_v2_heavy = self.wallet.send_self_transfer(from_node=node, target_weight=(V3_MAX_VSIZE + 1) * WITNESS_SCALE_FACTOR, version=2)
self.check_mempool([tx_v2_heavy["txid"]])
@cleanup(extra_args=["-datacarriersize=1000"])
def test_v3_acceptance(self):
def test_truc_acceptance(self):
node = self.nodes[0]
self.log.info("Test a child of a v3 transaction cannot be more than 1000vB")
self.log.info("Test a child of a TRUC transaction cannot be more than 1000vB")
tx_v3_parent_normal = self.wallet.send_self_transfer(from_node=node, version=3)
self.check_mempool([tx_v3_parent_normal["txid"]])
tx_v3_child_heavy = self.wallet.create_self_transfer(
@ -77,13 +77,13 @@ class MempoolAcceptV3(BitcoinTestFramework):
version=3
)
assert_greater_than_or_equal(tx_v3_child_heavy["tx"].get_vsize(), 1000)
expected_error_child_heavy = f"v3-rule-violation, v3 child tx {tx_v3_child_heavy['txid']} (wtxid={tx_v3_child_heavy['wtxid']}) is too big"
expected_error_child_heavy = f"TRUC-violation, v3 child tx {tx_v3_child_heavy['txid']} (wtxid={tx_v3_child_heavy['wtxid']}) is too big"
assert_raises_rpc_error(-26, expected_error_child_heavy, node.sendrawtransaction, tx_v3_child_heavy["hex"])
self.check_mempool([tx_v3_parent_normal["txid"]])
# tx has no descendants
assert_equal(node.getmempoolentry(tx_v3_parent_normal["txid"])["descendantcount"], 1)
self.log.info("Test that, during replacements, only the new transaction counts for v3 descendant limit")
self.log.info("Test that, during replacements, only the new transaction counts for TRUC descendant limit")
tx_v3_child_almost_heavy = self.wallet.send_self_transfer(
from_node=node,
fee_rate=DEFAULT_FEE,
@ -106,9 +106,9 @@ class MempoolAcceptV3(BitcoinTestFramework):
assert_equal(node.getmempoolentry(tx_v3_parent_normal["txid"])["descendantcount"], 2)
@cleanup(extra_args=None)
def test_v3_replacement(self):
def test_truc_replacement(self):
node = self.nodes[0]
self.log.info("Test v3 transactions may be replaced by v3 transactions")
self.log.info("Test TRUC transactions may be replaced by TRUC transactions")
utxo_v3_bip125 = self.wallet.get_utxo()
tx_v3_bip125 = self.wallet.send_self_transfer(
from_node=node,
@ -127,7 +127,7 @@ class MempoolAcceptV3(BitcoinTestFramework):
)
self.check_mempool([tx_v3_bip125_rbf["txid"]])
self.log.info("Test v3 transactions may be replaced by V2 transactions")
self.log.info("Test TRUC transactions may be replaced by non-TRUC (BIP125) transactions")
tx_v3_bip125_rbf_v2 = self.wallet.send_self_transfer(
from_node=node,
fee_rate=DEFAULT_FEE * 3,
@ -136,7 +136,7 @@ class MempoolAcceptV3(BitcoinTestFramework):
)
self.check_mempool([tx_v3_bip125_rbf_v2["txid"]])
self.log.info("Test that replacements cannot cause violation of inherited v3")
self.log.info("Test that replacements cannot cause violation of inherited TRUC")
utxo_v3_parent = self.wallet.get_utxo()
tx_v3_parent = self.wallet.send_self_transfer(
from_node=node,
@ -157,15 +157,15 @@ class MempoolAcceptV3(BitcoinTestFramework):
utxo_to_spend=tx_v3_parent["new_utxo"],
version=2
)
expected_error_v2_v3 = f"v3-rule-violation, non-v3 tx {tx_v3_child_rbf_v2['txid']} (wtxid={tx_v3_child_rbf_v2['wtxid']}) cannot spend from v3 tx {tx_v3_parent['txid']} (wtxid={tx_v3_parent['wtxid']})"
expected_error_v2_v3 = f"TRUC-violation, non-v3 tx {tx_v3_child_rbf_v2['txid']} (wtxid={tx_v3_child_rbf_v2['wtxid']}) cannot spend from v3 tx {tx_v3_parent['txid']} (wtxid={tx_v3_parent['wtxid']})"
assert_raises_rpc_error(-26, expected_error_v2_v3, node.sendrawtransaction, tx_v3_child_rbf_v2["hex"])
self.check_mempool([tx_v3_bip125_rbf_v2["txid"], tx_v3_parent["txid"], tx_v3_child["txid"]])
@cleanup(extra_args=None)
def test_v3_bip125(self):
def test_truc_bip125(self):
node = self.nodes[0]
self.log.info("Test v3 transactions that don't signal BIP125 are replaceable")
self.log.info("Test TRUC transactions that don't signal BIP125 are replaceable")
assert_equal(node.getmempoolinfo()["fullrbf"], False)
utxo_v3_no_bip125 = self.wallet.get_utxo()
tx_v3_no_bip125 = self.wallet.send_self_transfer(
@ -187,9 +187,9 @@ class MempoolAcceptV3(BitcoinTestFramework):
self.check_mempool([tx_v3_no_bip125_rbf["txid"]])
@cleanup(extra_args=["-datacarriersize=40000"])
def test_v3_reorg(self):
def test_truc_reorg(self):
node = self.nodes[0]
self.log.info("Test that, during a reorg, v3 rules are not enforced")
self.log.info("Test that, during a reorg, TRUC rules are not enforced")
tx_v2_block = self.wallet.send_self_transfer(from_node=node, version=2)
tx_v3_block = self.wallet.send_self_transfer(from_node=node, version=3)
tx_v3_block2 = self.wallet.send_self_transfer(from_node=node, version=3)
@ -211,12 +211,12 @@ class MempoolAcceptV3(BitcoinTestFramework):
@cleanup(extra_args=["-limitdescendantsize=10", "-datacarriersize=40000"])
def test_nondefault_package_limits(self):
"""
Max standard tx size + v3 rules imply the ancestor/descendant rules (at their default
Max standard tx size + TRUC rules imply the ancestor/descendant rules (at their default
values), but those checks must not be skipped. Ensure both sets of checks are done by
changing the ancestor/descendant limit configurations.
"""
node = self.nodes[0]
self.log.info("Test that a decreased limitdescendantsize also applies to v3 child")
self.log.info("Test that a decreased limitdescendantsize also applies to TRUC child")
parent_target_weight = 9990 * WITNESS_SCALE_FACTOR
child_target_weight = 500 * WITNESS_SCALE_FACTOR
tx_v3_parent_large1 = self.wallet.send_self_transfer(
@ -253,7 +253,7 @@ class MempoolAcceptV3(BitcoinTestFramework):
version=3
)
# Parent and child are within v3 limits
# Parent and child are within TRUC limits
assert_greater_than_or_equal(V3_MAX_VSIZE, tx_v3_parent_large2["tx"].get_vsize())
assert_greater_than_or_equal(1000, tx_v3_child_large2["tx"].get_vsize())
assert_greater_than(tx_v3_parent_large2["tx"].get_vsize() + tx_v3_child_large2["tx"].get_vsize(), 10000)
@ -262,8 +262,8 @@ class MempoolAcceptV3(BitcoinTestFramework):
self.check_mempool([tx_v3_parent_large2["txid"]])
@cleanup(extra_args=["-datacarriersize=1000"])
def test_v3_ancestors_package(self):
self.log.info("Test that v3 ancestor limits are checked within the package")
def test_truc_ancestors_package(self):
self.log.info("Test that TRUC ancestor limits are checked within the package")
node = self.nodes[0]
tx_v3_parent_normal = self.wallet.create_self_transfer(
fee_rate=0,
@ -289,34 +289,34 @@ class MempoolAcceptV3(BitcoinTestFramework):
self.check_mempool([])
result = node.submitpackage([tx_v3_parent_normal["hex"], tx_v3_parent_2_normal["hex"], tx_v3_child_multiparent["hex"]])
assert_equal(result['package_msg'], f"v3-violation, tx {tx_v3_child_multiparent['txid']} (wtxid={tx_v3_child_multiparent['wtxid']}) would have too many ancestors")
assert_equal(result['package_msg'], f"TRUC-violation, tx {tx_v3_child_multiparent['txid']} (wtxid={tx_v3_child_multiparent['wtxid']}) would have too many ancestors")
self.check_mempool([])
self.check_mempool([])
result = node.submitpackage([tx_v3_parent_normal["hex"], tx_v3_child_heavy["hex"]])
# tx_v3_child_heavy is heavy based on weight, not sigops.
assert_equal(result['package_msg'], f"v3-violation, v3 child tx {tx_v3_child_heavy['txid']} (wtxid={tx_v3_child_heavy['wtxid']}) is too big: {tx_v3_child_heavy['tx'].get_vsize()} > 1000 virtual bytes")
assert_equal(result['package_msg'], f"TRUC-violation, v3 child tx {tx_v3_child_heavy['txid']} (wtxid={tx_v3_child_heavy['wtxid']}) is too big: {tx_v3_child_heavy['tx'].get_vsize()} > 1000 virtual bytes")
self.check_mempool([])
tx_v3_parent = self.wallet.create_self_transfer(version=3)
tx_v3_child = self.wallet.create_self_transfer(utxo_to_spend=tx_v3_parent["new_utxo"], version=3)
tx_v3_grandchild = self.wallet.create_self_transfer(utxo_to_spend=tx_v3_child["new_utxo"], version=3)
result = node.testmempoolaccept([tx_v3_parent["hex"], tx_v3_child["hex"], tx_v3_grandchild["hex"]])
assert all([txresult["package-error"] == f"v3-violation, tx {tx_v3_grandchild['txid']} (wtxid={tx_v3_grandchild['wtxid']}) would have too many ancestors" for txresult in result])
assert all([txresult["package-error"] == f"TRUC-violation, tx {tx_v3_grandchild['txid']} (wtxid={tx_v3_grandchild['wtxid']}) would have too many ancestors" for txresult in result])
@cleanup(extra_args=None)
def test_v3_ancestors_package_and_mempool(self):
def test_truc_ancestors_package_and_mempool(self):
"""
A v3 transaction in a package cannot have 2 v3 parents.
A TRUC transaction in a package cannot have 2 TRUC parents.
Test that if we have a transaction graph A -> B -> C, where A, B, C are
all v3 transactions, that we cannot use submitpackage to get the
all TRUC transactions, that we cannot use submitpackage to get the
transactions all into the mempool.
Verify, in particular, that if A is already in the mempool, then
submitpackage(B, C) will fail.
"""
node = self.nodes[0]
self.log.info("Test that v3 ancestor limits include transactions within the package and all in-mempool ancestors")
self.log.info("Test that TRUC ancestor limits include transactions within the package and all in-mempool ancestors")
# This is our transaction "A":
tx_in_mempool = self.wallet.send_self_transfer(from_node=node, version=3)
@ -331,7 +331,7 @@ class MempoolAcceptV3(BitcoinTestFramework):
# submitpackage(B, C) should fail
result = node.submitpackage([tx_0fee_parent["hex"], tx_child_violator["hex"]])
assert_equal(result['package_msg'], f"v3-violation, tx {tx_child_violator['txid']} (wtxid={tx_child_violator['wtxid']}) would have too many ancestors")
assert_equal(result['package_msg'], f"TRUC-violation, tx {tx_child_violator['txid']} (wtxid={tx_child_violator['wtxid']}) would have too many ancestors")
self.check_mempool([tx_in_mempool["txid"]])
@cleanup(extra_args=None)
@ -341,7 +341,7 @@ class MempoolAcceptV3(BitcoinTestFramework):
However, this option is only available in single transaction acceptance. It doesn't work in
a multi-testmempoolaccept (where RBF is disabled) or when doing package CPFP.
"""
self.log.info("Test v3 sibling eviction in submitpackage and multi-testmempoolaccept")
self.log.info("Test TRUC sibling eviction in submitpackage and multi-testmempoolaccept")
node = self.nodes[0]
# Add a parent + child to mempool
tx_mempool_parent = self.wallet.send_self_transfer_multi(
@ -384,17 +384,17 @@ class MempoolAcceptV3(BitcoinTestFramework):
# Fails with another non-related transaction via testmempoolaccept
tx_unrelated = self.wallet.create_self_transfer(version=3)
result_test_unrelated = node.testmempoolaccept([tx_sibling_1["hex"], tx_unrelated["hex"]])
assert_equal(result_test_unrelated[0]["reject-reason"], "v3-rule-violation")
assert_equal(result_test_unrelated[0]["reject-reason"], "TRUC-violation")
# Fails in a package via testmempoolaccept
result_test_1p1c = node.testmempoolaccept([tx_sibling_1["hex"], tx_has_mempool_uncle["hex"]])
assert_equal(result_test_1p1c[0]["reject-reason"], "v3-rule-violation")
assert_equal(result_test_1p1c[0]["reject-reason"], "TRUC-violation")
# Allowed when tx is submitted in a package and evaluated individually.
# Note that the child failed since it would be the 3rd generation.
result_package_indiv = node.submitpackage([tx_sibling_1["hex"], tx_has_mempool_uncle["hex"]])
self.check_mempool([tx_mempool_parent["txid"], tx_sibling_1["txid"]])
expected_error_gen3 = f"v3-rule-violation, tx {tx_has_mempool_uncle['txid']} (wtxid={tx_has_mempool_uncle['wtxid']}) would have too many ancestors"
expected_error_gen3 = f"TRUC-violation, tx {tx_has_mempool_uncle['txid']} (wtxid={tx_has_mempool_uncle['wtxid']}) would have too many ancestors"
assert_equal(result_package_indiv["tx-results"][tx_has_mempool_uncle['wtxid']]['error'], expected_error_gen3)
@ -402,17 +402,17 @@ class MempoolAcceptV3(BitcoinTestFramework):
node.submitpackage([tx_mempool_parent["hex"], tx_sibling_2["hex"]])
self.check_mempool([tx_mempool_parent["txid"], tx_sibling_2["txid"]])
# Child cannot pay for sibling eviction for parent, as it violates v3 topology limits
# Child cannot pay for sibling eviction for parent, as it violates TRUC topology limits
result_package_cpfp = node.submitpackage([tx_sibling_3["hex"], tx_bumps_parent_with_sibling["hex"]])
self.check_mempool([tx_mempool_parent["txid"], tx_sibling_2["txid"]])
expected_error_cpfp = f"v3-rule-violation, tx {tx_mempool_parent['txid']} (wtxid={tx_mempool_parent['wtxid']}) would exceed descendant count limit"
expected_error_cpfp = f"TRUC-violation, tx {tx_mempool_parent['txid']} (wtxid={tx_mempool_parent['wtxid']}) would exceed descendant count limit"
assert_equal(result_package_cpfp["tx-results"][tx_sibling_3['wtxid']]['error'], expected_error_cpfp)
@cleanup(extra_args=["-datacarriersize=1000"])
def test_v3_package_inheritance(self):
self.log.info("Test that v3 inheritance is checked within package")
def test_truc_package_inheritance(self):
self.log.info("Test that TRUC inheritance is checked within package")
node = self.nodes[0]
tx_v3_parent = self.wallet.create_self_transfer(
fee_rate=0,
@ -426,14 +426,14 @@ class MempoolAcceptV3(BitcoinTestFramework):
)
self.check_mempool([])
result = node.submitpackage([tx_v3_parent["hex"], tx_v2_child["hex"]])
assert_equal(result['package_msg'], f"v3-violation, non-v3 tx {tx_v2_child['txid']} (wtxid={tx_v2_child['wtxid']}) cannot spend from v3 tx {tx_v3_parent['txid']} (wtxid={tx_v3_parent['wtxid']})")
assert_equal(result['package_msg'], f"TRUC-violation, non-v3 tx {tx_v2_child['txid']} (wtxid={tx_v2_child['wtxid']}) cannot spend from v3 tx {tx_v3_parent['txid']} (wtxid={tx_v3_parent['wtxid']})")
self.check_mempool([])
@cleanup(extra_args=None)
def test_v3_in_testmempoolaccept(self):
def test_truc_in_testmempoolaccept(self):
node = self.nodes[0]
self.log.info("Test that v3 inheritance is accurately assessed in testmempoolaccept")
self.log.info("Test that TRUC inheritance is accurately assessed in testmempoolaccept")
tx_v2 = self.wallet.create_self_transfer(version=2)
tx_v2_from_v2 = self.wallet.create_self_transfer(utxo_to_spend=tx_v2["new_utxo"], version=2)
tx_v3_from_v2 = self.wallet.create_self_transfer(utxo_to_spend=tx_v2["new_utxo"], version=3)
@ -447,11 +447,11 @@ class MempoolAcceptV3(BitcoinTestFramework):
assert all([result["allowed"] for result in test_accept_v2_and_v3])
test_accept_v3_from_v2 = node.testmempoolaccept([tx_v2["hex"], tx_v3_from_v2["hex"]])
expected_error_v3_from_v2 = f"v3-violation, v3 tx {tx_v3_from_v2['txid']} (wtxid={tx_v3_from_v2['wtxid']}) cannot spend from non-v3 tx {tx_v2['txid']} (wtxid={tx_v2['wtxid']})"
expected_error_v3_from_v2 = f"TRUC-violation, v3 tx {tx_v3_from_v2['txid']} (wtxid={tx_v3_from_v2['wtxid']}) cannot spend from non-v3 tx {tx_v2['txid']} (wtxid={tx_v2['wtxid']})"
assert all([result["package-error"] == expected_error_v3_from_v2 for result in test_accept_v3_from_v2])
test_accept_v2_from_v3 = node.testmempoolaccept([tx_v3["hex"], tx_v2_from_v3["hex"]])
expected_error_v2_from_v3 = f"v3-violation, non-v3 tx {tx_v2_from_v3['txid']} (wtxid={tx_v2_from_v3['wtxid']}) cannot spend from v3 tx {tx_v3['txid']} (wtxid={tx_v3['wtxid']})"
expected_error_v2_from_v3 = f"TRUC-violation, non-v3 tx {tx_v2_from_v3['txid']} (wtxid={tx_v2_from_v3['wtxid']}) cannot spend from v3 tx {tx_v3['txid']} (wtxid={tx_v3['wtxid']})"
assert all([result["package-error"] == expected_error_v2_from_v3 for result in test_accept_v2_from_v3])
test_accept_pairs = node.testmempoolaccept([tx_v2["hex"], tx_v3["hex"], tx_v2_from_v2["hex"], tx_v3_from_v3["hex"]])
@ -463,16 +463,16 @@ class MempoolAcceptV3(BitcoinTestFramework):
tx_v3_child_1 = self.wallet.create_self_transfer(utxo_to_spend=tx_v3_parent["new_utxos"][0], version=3)
tx_v3_child_2 = self.wallet.create_self_transfer(utxo_to_spend=tx_v3_parent["new_utxos"][1], version=3)
test_accept_2children = node.testmempoolaccept([tx_v3_parent["hex"], tx_v3_child_1["hex"], tx_v3_child_2["hex"]])
expected_error_2children = f"v3-violation, tx {tx_v3_parent['txid']} (wtxid={tx_v3_parent['wtxid']}) would exceed descendant count limit"
expected_error_2children = f"TRUC-violation, tx {tx_v3_parent['txid']} (wtxid={tx_v3_parent['wtxid']}) would exceed descendant count limit"
assert all([result["package-error"] == expected_error_2children for result in test_accept_2children])
# Extra v3 transaction does not get incorrectly marked as extra descendant
# Extra TRUC transaction does not get incorrectly marked as extra descendant
test_accept_1child_with_exra = node.testmempoolaccept([tx_v3_parent["hex"], tx_v3_child_1["hex"], tx_v3_independent["hex"]])
assert all([result["allowed"] for result in test_accept_1child_with_exra])
# Extra v3 transaction does not make us ignore the extra descendant
# Extra TRUC transaction does not make us ignore the extra descendant
test_accept_2children_with_exra = node.testmempoolaccept([tx_v3_parent["hex"], tx_v3_child_1["hex"], tx_v3_child_2["hex"], tx_v3_independent["hex"]])
expected_error_extra = f"v3-violation, tx {tx_v3_parent['txid']} (wtxid={tx_v3_parent['wtxid']}) would exceed descendant count limit"
expected_error_extra = f"TRUC-violation, tx {tx_v3_parent['txid']} (wtxid={tx_v3_parent['wtxid']}) would exceed descendant count limit"
assert all([result["package-error"] == expected_error_extra for result in test_accept_2children_with_exra])
# Same result if the parent is already in mempool
node.sendrawtransaction(tx_v3_parent["hex"])
@ -482,7 +482,7 @@ class MempoolAcceptV3(BitcoinTestFramework):
@cleanup(extra_args=None)
def test_reorg_2child_rbf(self):
node = self.nodes[0]
self.log.info("Test that children of a v3 transaction can be replaced individually, even if there are multiple due to reorg")
self.log.info("Test that children of a TRUC transaction can be replaced individually, even if there are multiple due to reorg")
ancestor_tx = self.wallet.send_self_transfer_multi(from_node=node, num_outputs=2, version=3)
self.check_mempool([ancestor_tx["txid"]])
@ -511,8 +511,8 @@ class MempoolAcceptV3(BitcoinTestFramework):
assert_equal(node.getmempoolentry(ancestor_tx["txid"])["descendantcount"], 3)
@cleanup(extra_args=None)
def test_v3_sibling_eviction(self):
self.log.info("Test sibling eviction for v3")
def test_truc_sibling_eviction(self):
self.log.info("Test sibling eviction for TRUC")
node = self.nodes[0]
tx_v3_parent = self.wallet.send_self_transfer_multi(from_node=node, num_outputs=2, version=3)
# This is the sibling to replace
@ -609,7 +609,7 @@ class MempoolAcceptV3(BitcoinTestFramework):
utxo_to_spend=tx_with_multi_children["new_utxos"][2],
fee_rate=DEFAULT_FEE*50
)
expected_error_2siblings = f"v3-rule-violation, tx {tx_with_multi_children['txid']} (wtxid={tx_with_multi_children['wtxid']}) would exceed descendant count limit"
expected_error_2siblings = f"TRUC-violation, tx {tx_with_multi_children['txid']} (wtxid={tx_with_multi_children['wtxid']}) would exceed descendant count limit"
assert_raises_rpc_error(-26, expected_error_2siblings, node.sendrawtransaction, tx_with_sibling3["hex"])
# However, an RBF (with conflicting inputs) is possible even if the resulting cluster size exceeds 2
@ -627,21 +627,21 @@ class MempoolAcceptV3(BitcoinTestFramework):
node = self.nodes[0]
self.wallet = MiniWallet(node)
self.generate(self.wallet, 120)
self.test_v3_max_vsize()
self.test_v3_acceptance()
self.test_v3_replacement()
self.test_v3_bip125()
self.test_v3_reorg()
self.test_truc_max_vsize()
self.test_truc_acceptance()
self.test_truc_replacement()
self.test_truc_bip125()
self.test_truc_reorg()
self.test_nondefault_package_limits()
self.test_v3_ancestors_package()
self.test_v3_ancestors_package_and_mempool()
self.test_truc_ancestors_package()
self.test_truc_ancestors_package_and_mempool()
self.test_sibling_eviction_package()
self.test_v3_package_inheritance()
self.test_v3_in_testmempoolaccept()
self.test_truc_package_inheritance()
self.test_truc_in_testmempoolaccept()
self.test_reorg_2child_rbf()
self.test_v3_sibling_eviction()
self.test_truc_sibling_eviction()
self.test_reorg_sibling_eviction_1p2c()
if __name__ == "__main__":
MempoolAcceptV3().main()
MempoolTRUC().main()

View File

@ -114,16 +114,16 @@ class CreateTxWalletTest(BitcoinTestFramework):
self.log.info('Check wallet does not create transactions with version=3 yet')
wallet_rpc = self.nodes[0].get_wallet_rpc(self.default_wallet_name)
self.nodes[0].createwallet("v3")
wallet_v3 = self.nodes[0].get_wallet_rpc("v3")
self.nodes[0].createwallet("version3")
wallet_v3 = self.nodes[0].get_wallet_rpc("version3")
tx_data = wallet_rpc.send(outputs=[{wallet_v3.getnewaddress(): 25}], options={"change_position": 0})
wallet_tx_data = wallet_rpc.gettransaction(tx_data["txid"])
tx_current_version = tx_from_hex(wallet_tx_data["hex"])
# While v3 transactions are standard, the CURRENT_VERSION is 2.
# While version=3 transactions are standard, the CURRENT_VERSION is 2.
# This test can be removed if CURRENT_VERSION is changed, and replaced with tests that the
# wallet handles v3 rules properly.
# wallet handles TRUC rules properly.
assert_equal(tx_current_version.version, 2)
wallet_v3.unloadwallet()