mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-01-21 15:50:07 +01:00
rpc: Allow single transaction through submitpackage
And under the hood suppoert single transactions in AcceptPackage. This simplifies user experience and paves the way for reducing number of codepaths for transaction acceptance in the future. Co-Authored-By: instagibbs <gsanders87@gmail.com>
This commit is contained in:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user