Skip to content

Commit 13650fe

Browse files
committed
[policy] detect unsorted packages
1 parent 9ef643e commit 13650fe

File tree

2 files changed

+24
-8
lines changed

2 files changed

+24
-8
lines changed

src/validation.cpp

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
#include <uint256.h>
4343
#include <undo.h>
4444
#include <util/check.h> // For NDEBUG compile time check
45+
#include <util/hasher.h>
4546
#include <util/moneystr.h>
4647
#include <util/rbf.h>
4748
#include <util/strencodings.h>
@@ -1093,12 +1094,29 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptMultipleTransactions(const std::
10931094
return PackageMempoolAcceptResult(package_state, {});
10941095
}
10951096

1097+
// Construct workspaces and check package policies.
10961098
std::vector<Workspace> workspaces{};
10971099
workspaces.reserve(package_count);
1098-
std::transform(txns.cbegin(), txns.cend(), std::back_inserter(workspaces), [](const auto& tx) {
1099-
return Workspace(tx);
1100-
});
1101-
1100+
{
1101+
std::unordered_set<uint256, SaltedTxidHasher> later_txids;
1102+
std::transform(txns.cbegin(), txns.cend(), std::inserter(later_txids, later_txids.end()),
1103+
[](const auto& tx) { return tx->GetHash(); });
1104+
// Require the package to be sorted in order of dependency, i.e. parents appear before children.
1105+
// An unsorted package will fail anyway on missing-inputs, but it's better to quit earlier and
1106+
// fail on something less ambiguous (missing-inputs could also be an orphan or trying to
1107+
// spend nonexistent coins).
1108+
for (const auto& tx : txns) {
1109+
for (const auto& input : tx->vin) {
1110+
if (later_txids.find(input.prevout.hash) != later_txids.end()) {
1111+
// The parent is a subsequent transaction in the package.
1112+
package_state.Invalid(PackageValidationResult::PCKG_POLICY, "package-not-sorted");
1113+
return PackageMempoolAcceptResult(package_state, {});
1114+
}
1115+
}
1116+
later_txids.erase(tx->GetHash());
1117+
workspaces.emplace_back(Workspace(tx));
1118+
}
1119+
}
11021120
std::map<const uint256, const MempoolAcceptResult> results;
11031121
{
11041122
// Don't allow any conflicting transactions, i.e. spending the same inputs, in a package.

test/functional/rpc_packages.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -169,10 +169,8 @@ def test_chain(self):
169169
chain_txns.append(tx)
170170

171171
self.log.info("Check that testmempoolaccept requires packages to be sorted by dependency")
172-
testres_multiple_unsorted = node.testmempoolaccept(rawtxs=chain_hex[::-1])
173-
assert_equal(testres_multiple_unsorted,
174-
[{"txid": chain_txns[-1].rehash(), "wtxid": chain_txns[-1].getwtxid(), "allowed": False, "reject-reason": "missing-inputs"}]
175-
+ [{"txid": tx.rehash(), "wtxid": tx.getwtxid()} for tx in chain_txns[::-1]][1:])
172+
assert_equal(node.testmempoolaccept(rawtxs=chain_hex[::-1]),
173+
[{"txid": tx.rehash(), "wtxid": tx.getwtxid(), "package-error": "package-not-sorted"} for tx in chain_txns[::-1]])
176174

177175
self.log.info("Testmempoolaccept a chain of 25 transactions")
178176
testres_multiple = node.testmempoolaccept(rawtxs=chain_hex)

0 commit comments

Comments
 (0)