diff options
Diffstat (limited to 'src/test/txpackage_tests.cpp')
-rw-r--r-- | src/test/txpackage_tests.cpp | 113 |
1 files changed, 112 insertions, 1 deletions
diff --git a/src/test/txpackage_tests.cpp b/src/test/txpackage_tests.cpp index ea211aedf3..3c36b78260 100644 --- a/src/test/txpackage_tests.cpp +++ b/src/test/txpackage_tests.cpp @@ -283,6 +283,8 @@ BOOST_AUTO_TEST_CASE(noncontextual_package_tests) BOOST_CHECK(GetPackageHash({tx_parent}) != GetPackageHash({tx_child})); BOOST_CHECK(GetPackageHash({tx_child, tx_child}) != GetPackageHash({tx_child})); BOOST_CHECK(GetPackageHash({tx_child, tx_parent}) != GetPackageHash({tx_child, tx_child})); + BOOST_CHECK(!IsChildWithParents({})); + BOOST_CHECK(!IsChildWithParentsTree({})); } // 24 Parents and 1 Child @@ -492,6 +494,97 @@ BOOST_AUTO_TEST_CASE(package_submission_tests) } } +// Tests for packages containing a single transaction +BOOST_AUTO_TEST_CASE(package_single_tx) +{ + // Mine blocks to mature coinbases. + mineBlocks(3); + LOCK(cs_main); + auto expected_pool_size{m_node.mempool->size()}; + + const CAmount high_fee{1000}; + + // No unconfirmed parents + CKey single_key = GenerateRandomKey(); + CScript single_locking_script = GetScriptForDestination(PKHash(single_key.GetPubKey())); + auto mtx_single = CreateValidMempoolTransaction(/*input_transaction=*/m_coinbase_txns[0], /*input_vout=*/0, + /*input_height=*/0, /*input_signing_key=*/coinbaseKey, + /*output_destination=*/single_locking_script, + /*output_amount=*/CAmount(49 * COIN), /*submit=*/false); + CTransactionRef tx_single = MakeTransactionRef(mtx_single); + Package package_tx_single{tx_single}; + const auto result_single_tx = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool, + package_tx_single, /*test_accept=*/false, /*client_maxfeerate=*/{}); + expected_pool_size += 1; + BOOST_CHECK_MESSAGE(result_single_tx.m_state.IsValid(), + "Package validation unexpectedly failed: " << result_single_tx.m_state.ToString()); + BOOST_CHECK_EQUAL(m_node.mempool->size(), expected_pool_size); + + // Parent and Child. Both submitted by themselves through the ProcessNewPackage interface. + CKey parent_key = GenerateRandomKey(); + CScript parent_locking_script = GetScriptForDestination(WitnessV0KeyHash(parent_key.GetPubKey())); + auto mtx_parent = CreateValidMempoolTransaction(/*input_transaction=*/m_coinbase_txns[1], /*input_vout=*/0, + /*input_height=*/0, /*input_signing_key=*/coinbaseKey, + /*output_destination=*/parent_locking_script, + /*output_amount=*/CAmount(50 * COIN) - high_fee, /*submit=*/false); + CTransactionRef tx_parent = MakeTransactionRef(mtx_parent); + Package package_just_parent{tx_parent}; + const auto result_just_parent = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool, package_just_parent, /*test_accept=*/false, /*client_maxfeerate=*/{}); + if (auto err_parent_child{CheckPackageMempoolAcceptResult(package_just_parent, result_just_parent, /*expect_valid=*/true, nullptr)}) { + BOOST_ERROR(err_parent_child.value()); + } else { + auto it_parent = result_just_parent.m_tx_results.find(tx_parent->GetWitnessHash()); + BOOST_CHECK_MESSAGE(it_parent->second.m_state.IsValid(), it_parent->second.m_state.ToString()); + BOOST_CHECK(it_parent->second.m_effective_feerate.value().GetFee(GetVirtualTransactionSize(*tx_parent)) == high_fee); + BOOST_CHECK_EQUAL(it_parent->second.m_wtxids_fee_calculations.value().size(), 1); + BOOST_CHECK_EQUAL(it_parent->second.m_wtxids_fee_calculations.value().front(), tx_parent->GetWitnessHash()); + } + expected_pool_size += 1; + BOOST_CHECK_EQUAL(m_node.mempool->size(), expected_pool_size); + + CKey child_key = GenerateRandomKey(); + CScript child_locking_script = GetScriptForDestination(WitnessV0KeyHash(child_key.GetPubKey())); + auto mtx_child = CreateValidMempoolTransaction(/*input_transaction=*/tx_parent, /*input_vout=*/0, + /*input_height=*/101, /*input_signing_key=*/parent_key, + /*output_destination=*/child_locking_script, + /*output_amount=*/CAmount(50 * COIN) - 2 * high_fee, /*submit=*/false); + CTransactionRef tx_child = MakeTransactionRef(mtx_child); + Package package_just_child{tx_child}; + const auto result_just_child = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool, package_just_child, /*test_accept=*/false, /*client_maxfeerate=*/{}); + if (auto err_parent_child{CheckPackageMempoolAcceptResult(package_just_child, result_just_child, /*expect_valid=*/true, nullptr)}) { + BOOST_ERROR(err_parent_child.value()); + } else { + auto it_child = result_just_child.m_tx_results.find(tx_child->GetWitnessHash()); + BOOST_CHECK_MESSAGE(it_child->second.m_state.IsValid(), it_child->second.m_state.ToString()); + BOOST_CHECK(it_child->second.m_effective_feerate.value().GetFee(GetVirtualTransactionSize(*tx_child)) == high_fee); + BOOST_CHECK_EQUAL(it_child->second.m_wtxids_fee_calculations.value().size(), 1); + BOOST_CHECK_EQUAL(it_child->second.m_wtxids_fee_calculations.value().front(), tx_child->GetWitnessHash()); + } + expected_pool_size += 1; + BOOST_CHECK_EQUAL(m_node.mempool->size(), expected_pool_size); + + // Too-low fee to RBF tx_single + auto mtx_single_low_fee = CreateValidMempoolTransaction(/*input_transaction=*/m_coinbase_txns[0], /*input_vout=*/0, + /*input_height=*/0, /*input_signing_key=*/coinbaseKey, + /*output_destination=*/single_locking_script, + /*output_amount=*/CAmount(49 * COIN - 1), /*submit=*/false); + CTransactionRef tx_single_low_fee = MakeTransactionRef(mtx_single_low_fee); + Package package_tx_single_low_fee{tx_single_low_fee}; + const auto result_single_tx_low_fee = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool, + package_tx_single_low_fee, /*test_accept=*/false, /*client_maxfeerate=*/{}); + + BOOST_CHECK_EQUAL(m_node.mempool->size(), expected_pool_size); + + BOOST_CHECK(!result_single_tx_low_fee.m_state.IsValid()); + BOOST_CHECK_EQUAL(result_single_tx_low_fee.m_state.GetResult(), PackageValidationResult::PCKG_TX); + auto it_low_fee = result_single_tx_low_fee.m_tx_results.find(tx_single_low_fee->GetWitnessHash()); + BOOST_CHECK_EQUAL(it_low_fee->second.m_state.GetResult(), TxValidationResult::TX_RECONSIDERABLE); + if (auto err_single{CheckPackageMempoolAcceptResult(package_tx_single_low_fee, result_single_tx_low_fee, /*expect_valid=*/false, m_node.mempool.get())}) { + BOOST_ERROR(err_single.value()); + } + BOOST_CHECK_EQUAL(m_node.mempool->size(), expected_pool_size); +} + // Tests for packages containing transactions that have same-txid-different-witness equivalents in // the mempool. BOOST_AUTO_TEST_CASE(package_witness_swap_tests) @@ -1078,7 +1171,25 @@ BOOST_AUTO_TEST_CASE(package_rbf_tests) BOOST_CHECK_EQUAL(it_child_3->second.m_effective_feerate.value().GetFee(package3_total_vsize), 199 + 1300); BOOST_CHECK_EQUAL(m_node.mempool->size(), expected_pool_size); - } + // Finally, check that we can prioritise tx_child_1 to get package1 into the mempool. + // It should not be possible to resubmit package1 and get it in without prioritisation. + const auto submit4 = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool, package1, false, std::nullopt); + if (auto err_4{CheckPackageMempoolAcceptResult(package1, submit4, /*expect_valid=*/false, m_node.mempool.get())}) { + BOOST_ERROR(err_4.value()); + } + m_node.mempool->PrioritiseTransaction(tx_child_1->GetHash(), 1363); + const auto submit5 = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool, package1, false, std::nullopt); + if (auto err_5{CheckPackageMempoolAcceptResult(package1, submit5, /*expect_valid=*/true, m_node.mempool.get())}) { + BOOST_ERROR(err_5.value()); + } + it_parent_1 = submit5.m_tx_results.find(tx_parent_1->GetWitnessHash()); + it_child_1 = submit5.m_tx_results.find(tx_child_1->GetWitnessHash()); + BOOST_CHECK_EQUAL(it_parent_1->second.m_result_type, MempoolAcceptResult::ResultType::VALID); + BOOST_CHECK_EQUAL(it_child_1->second.m_result_type, MempoolAcceptResult::ResultType::VALID); + LOCK(m_node.mempool->cs); + BOOST_CHECK(m_node.mempool->GetIter(tx_parent_1->GetHash()).has_value()); + BOOST_CHECK(m_node.mempool->GetIter(tx_child_1->GetHash()).has_value()); + } } BOOST_AUTO_TEST_SUITE_END() |