mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-06-02 17:24:58 +02:00
Merge bitcoin/bitcoin#31385: package validation: relax the package-not-child-with-unconfirmed-parents rule
ea17a9423f[doc] release note for relaxing requirement of all unconfirmed parents present (glozow)12f48d5ed3test: add chained 1p1c propagation test (Greg Sanders)525be56741[unit test] package submission 2p1c with 1 parent missing (glozow)f24771af05relax child-with-unconfirmed-parents rule (glozow) Pull request description: Broadens the package validation interface, see #27463 for wider context. On master, package rules include that (1) the package topology must be child-wth-parents (2) all of the child's unconfirmed parents must be present. This PR relaxes the second rule, leaving the first rule untouched (there are plans to change that as well, but not here). Original motivation for this rule was based on the idea that we would have a child-with-unconfirmed-parents package relay protocol, and this would verify that the peer provided the "correct" package. For various reasons, we're not planning on doing this. We could potentially do this for ancestor packages (with a similar definition that all UTXOs to make the tx valid are available in this package), but it's also questionable whether it's useful to enforce this. This rule gets in the way of certain usage of 1p1c package relay currently. If a transaction has multiple parents, of which only 1 requires a package CPFP, this rule blocks the package from relaying. Even if all the non-low-feerate parents are already in mempool, when the p2p logic submits the 1p1c package, it gets rejected for not meeting this rule. ACKs for top commit: ishaanam: re-utACKea17a9423finstagibbs: ACKea17a9423fTree-SHA512: c2231761ae7b2acea10a96735e7a36c646f517964d0acb59bacbae1c5a1950e0223458b84c6d5ce008f0c1d53c1605df0fb3cd0064ee535ead006eb7c0fa625b
This commit is contained in:
@@ -355,6 +355,9 @@ BOOST_AUTO_TEST_CASE(noncontextual_package_tests)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(package_submission_tests)
|
||||
{
|
||||
// Mine blocks to mature coinbases.
|
||||
mineBlocks(3);
|
||||
MockMempoolMinFee(CFeeRate(5000));
|
||||
LOCK(cs_main);
|
||||
unsigned int expected_pool_size = m_node.mempool->size();
|
||||
CKey parent_key = GenerateRandomKey();
|
||||
@@ -441,20 +444,6 @@ BOOST_AUTO_TEST_CASE(package_submission_tests)
|
||||
BOOST_CHECK_EQUAL(result_quit_early.m_state.GetResult(), PackageValidationResult::PCKG_TX);
|
||||
}
|
||||
|
||||
// Child with missing parent.
|
||||
mtx_child.vin.emplace_back(COutPoint(package_unrelated[0]->GetHash(), 0));
|
||||
Package package_missing_parent;
|
||||
package_missing_parent.push_back(tx_parent);
|
||||
package_missing_parent.push_back(MakeTransactionRef(mtx_child));
|
||||
{
|
||||
const auto result_missing_parent = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool,
|
||||
package_missing_parent, /*test_accept=*/false, /*client_maxfeerate=*/{});
|
||||
BOOST_CHECK(result_missing_parent.m_state.IsInvalid());
|
||||
BOOST_CHECK_EQUAL(result_missing_parent.m_state.GetResult(), PackageValidationResult::PCKG_POLICY);
|
||||
BOOST_CHECK_EQUAL(result_missing_parent.m_state.GetRejectReason(), "package-not-child-with-unconfirmed-parents");
|
||||
BOOST_CHECK_EQUAL(m_node.mempool->size(), expected_pool_size);
|
||||
}
|
||||
|
||||
// Submit package with parent + child.
|
||||
{
|
||||
const auto submit_parent_child = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool,
|
||||
@@ -492,6 +481,60 @@ BOOST_AUTO_TEST_CASE(package_submission_tests)
|
||||
|
||||
BOOST_CHECK_EQUAL(m_node.mempool->size(), expected_pool_size);
|
||||
}
|
||||
|
||||
// In-mempool parent and child with missing parent.
|
||||
{
|
||||
auto tx_parent_1 = MakeTransactionRef(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 - low_fee_amt), /*submit=*/false));
|
||||
auto tx_parent_2 = MakeTransactionRef(CreateValidMempoolTransaction(/*input_transaction=*/m_coinbase_txns[2], /*input_vout=*/0,
|
||||
/*input_height=*/0, /*input_signing_key=*/coinbaseKey,
|
||||
/*output_destination=*/parent_locking_script,
|
||||
/*output_amount=*/CAmount(50 * COIN - 800), /*submit=*/false));
|
||||
|
||||
auto tx_child_missing_parent = MakeTransactionRef(CreateValidMempoolTransaction({tx_parent_1, tx_parent_2},
|
||||
{{tx_parent_1->GetHash(), 0}, {tx_parent_2->GetHash(), 0}},
|
||||
/*input_height=*/0, {parent_key},
|
||||
{{49 * COIN, child_locking_script}}, /*submit=*/false));
|
||||
|
||||
Package package_missing_parent{tx_parent_1, tx_child_missing_parent};
|
||||
|
||||
const auto result_missing_parent = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool,
|
||||
package_missing_parent, /*test_accept=*/false, /*client_maxfeerate=*/{});
|
||||
if (auto err_missing_parent{CheckPackageMempoolAcceptResult(package_missing_parent, result_missing_parent, /*expect_valid=*/false, m_node.mempool.get())}) {
|
||||
BOOST_ERROR(err_missing_parent.value());
|
||||
} else {
|
||||
auto it_parent = result_missing_parent.m_tx_results.find(tx_parent_1->GetWitnessHash());
|
||||
auto it_child = result_missing_parent.m_tx_results.find(tx_child_missing_parent->GetWitnessHash());
|
||||
|
||||
BOOST_CHECK_EQUAL(result_missing_parent.m_state.GetResult(), PackageValidationResult::PCKG_TX);
|
||||
BOOST_CHECK_EQUAL(result_missing_parent.m_state.GetRejectReason(), "transaction failed");
|
||||
|
||||
BOOST_CHECK_EQUAL(it_parent->second.m_state.GetResult(), TxValidationResult::TX_RECONSIDERABLE);
|
||||
BOOST_CHECK_EQUAL(it_child->second.m_state.GetResult(), TxValidationResult::TX_MISSING_INPUTS);
|
||||
BOOST_CHECK_EQUAL(it_child->second.m_state.GetRejectReason(), "bad-txns-inputs-missingorspent");
|
||||
BOOST_CHECK_EQUAL(m_node.mempool->size(), expected_pool_size);
|
||||
}
|
||||
|
||||
// Submit parent2 ahead of time, should become ok.
|
||||
Package package_just_parent2{tx_parent_2};
|
||||
expected_pool_size += 1;
|
||||
const auto result_just_parent2 = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool,
|
||||
package_just_parent2, /*test_accept=*/false, /*client_maxfeerate=*/{});
|
||||
if (auto err_parent2{CheckPackageMempoolAcceptResult(package_just_parent2, result_just_parent2, /*expect_valid=*/true, m_node.mempool.get())}) {
|
||||
BOOST_ERROR(err_parent2.value());
|
||||
}
|
||||
BOOST_CHECK_EQUAL(m_node.mempool->size(), expected_pool_size);
|
||||
|
||||
const auto result_parent_already_in = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool,
|
||||
package_missing_parent, /*test_accept=*/false, /*client_maxfeerate=*/{});
|
||||
expected_pool_size += 2;
|
||||
if (auto err_parent_already_in{CheckPackageMempoolAcceptResult(package_missing_parent, result_parent_already_in, /*expect_valid=*/true, m_node.mempool.get())}) {
|
||||
BOOST_ERROR(err_parent_already_in.value());
|
||||
}
|
||||
BOOST_CHECK_EQUAL(m_node.mempool->size(), expected_pool_size);
|
||||
}
|
||||
}
|
||||
|
||||
// Tests for packages containing a single transaction
|
||||
|
||||
Reference in New Issue
Block a user