From e32ba1599c599e75b1da3393f71f633de860505f Mon Sep 17 00:00:00 2001 From: glozow Date: Thu, 11 May 2023 17:50:05 +0100 Subject: [PATCH] [txpackages] IsChildWithParentsTree() Many edge cases exist when parents in a child-with-parents package can spend each other. However, this pattern should also be uncommon in normal use cases. --- src/policy/packages.cpp | 15 +++++++++++++++ src/policy/packages.h | 4 ++++ src/test/txpackage_tests.cpp | 3 +++ 3 files changed, 22 insertions(+) diff --git a/src/policy/packages.cpp b/src/policy/packages.cpp index fd272a2642e..47a9274a311 100644 --- a/src/policy/packages.cpp +++ b/src/policy/packages.cpp @@ -88,3 +88,18 @@ bool IsChildWithParents(const Package& package) return std::all_of(package.cbegin(), package.cend() - 1, [&input_txids](const auto& ptx) { return input_txids.count(ptx->GetHash()) > 0; }); } + +bool IsChildWithParentsTree(const Package& package) +{ + if (!IsChildWithParents(package)) return false; + std::unordered_set parent_txids; + std::transform(package.cbegin(), package.cend() - 1, std::inserter(parent_txids, parent_txids.end()), + [](const auto& ptx) { return ptx->GetHash(); }); + // Each parent must not have an input who is one of the other parents. + return std::all_of(package.cbegin(), package.cend() - 1, [&](const auto& ptx) { + for (const auto& input : ptx->vin) { + if (parent_txids.count(input.prevout.hash) > 0) return false; + } + return true; + }); +} diff --git a/src/policy/packages.h b/src/policy/packages.h index 702667b8ade..cf37857e4bb 100644 --- a/src/policy/packages.h +++ b/src/policy/packages.h @@ -63,4 +63,8 @@ bool CheckPackage(const Package& txns, PackageValidationState& state); */ bool IsChildWithParents(const Package& package); +/** Context-free check that a package IsChildWithParents() and none of the parents depend on each + * other (the package is a "tree"). + */ +bool IsChildWithParentsTree(const Package& package); #endif // BITCOIN_POLICY_PACKAGES_H diff --git a/src/test/txpackage_tests.cpp b/src/test/txpackage_tests.cpp index 571b58156f7..a8318e9fdbe 100644 --- a/src/test/txpackage_tests.cpp +++ b/src/test/txpackage_tests.cpp @@ -162,6 +162,7 @@ BOOST_FIXTURE_TEST_CASE(noncontextual_package_tests, TestChain100Setup) BOOST_CHECK_EQUAL(state.GetResult(), PackageValidationResult::PCKG_POLICY); BOOST_CHECK_EQUAL(state.GetRejectReason(), "package-not-sorted"); BOOST_CHECK(IsChildWithParents({tx_parent, tx_child})); + BOOST_CHECK(IsChildWithParentsTree({tx_parent, tx_child})); } // 24 Parents and 1 Child @@ -187,6 +188,7 @@ BOOST_FIXTURE_TEST_CASE(noncontextual_package_tests, TestChain100Setup) PackageValidationState state; BOOST_CHECK(CheckPackage(package, state)); BOOST_CHECK(IsChildWithParents(package)); + BOOST_CHECK(IsChildWithParentsTree(package)); package.erase(package.begin()); BOOST_CHECK(IsChildWithParents(package)); @@ -219,6 +221,7 @@ BOOST_FIXTURE_TEST_CASE(noncontextual_package_tests, TestChain100Setup) BOOST_CHECK(IsChildWithParents({tx_parent, tx_parent_also_child})); BOOST_CHECK(IsChildWithParents({tx_parent, tx_child})); BOOST_CHECK(IsChildWithParents({tx_parent, tx_parent_also_child, tx_child})); + BOOST_CHECK(!IsChildWithParentsTree({tx_parent, tx_parent_also_child, tx_child})); // IsChildWithParents does not detect unsorted parents. BOOST_CHECK(IsChildWithParents({tx_parent_also_child, tx_parent, tx_child})); BOOST_CHECK(CheckPackage({tx_parent, tx_parent_also_child, tx_child}, state));