aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorglozow <gloriajzhao@gmail.com>2023-05-11 17:50:05 +0100
committerglozow <gloriajzhao@gmail.com>2023-10-02 10:13:38 +0100
commite32ba1599c599e75b1da3393f71f633de860505f (patch)
tree3ea33a6c45e642620ed3383c1b44caaeeaab579d
parentb4f28cc345ef9c5261c4a8d743654a44784c7802 (diff)
downloadbitcoin-e32ba1599c599e75b1da3393f71f633de860505f.tar.xz
[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.
-rw-r--r--src/policy/packages.cpp15
-rw-r--r--src/policy/packages.h4
-rw-r--r--src/test/txpackage_tests.cpp3
3 files changed, 22 insertions, 0 deletions
diff --git a/src/policy/packages.cpp b/src/policy/packages.cpp
index fd272a2642..47a9274a31 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<uint256, SaltedTxidHasher> 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 702667b8ad..cf37857e4b 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 571b58156f..a8318e9fdb 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));