Skip to content

Commit 144a290

Browse files
committed
[policy] require submitted packages to be child-with-unconfirmed-parents
Note that this code path is not ever executed yet, because ProcessNewPackage asserts test_accept=true.
1 parent d59ddc5 commit 144a290

File tree

1 file changed

+83
-2
lines changed

1 file changed

+83
-2
lines changed

src/validation.cpp

Lines changed: 83 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,17 @@ class MemPoolAccept
477477
};
478478
}
479479

480+
/** Parameters for child-with-unconfirmed-parents package validation. */
481+
static ATMPArgs PackageChildWithParents(const CChainParams& chainparams, int64_t accept_time,
482+
std::vector<COutPoint>& coins_to_uncache) {
483+
return ATMPArgs{/* m_chainparams */ chainparams,
484+
/* m_accept_time */ accept_time,
485+
/* m_bypass_limits */ false,
486+
/* m_coins_to_uncache */ coins_to_uncache,
487+
/* m_test_accept */ false,
488+
/* m_allow_bip125_replacement */ false,
489+
};
490+
}
480491
// No default ctor to avoid exposing details to clients and allowing the possibility of
481492
// mixing up the order of the arguments. Use static functions above instead.
482493
ATMPArgs() = delete;
@@ -492,6 +503,12 @@ class MemPoolAccept
492503
*/
493504
PackageMempoolAcceptResult AcceptMultipleTransactions(const std::vector<CTransactionRef>& txns, ATMPArgs& args) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
494505

506+
/**
507+
* Package (more specific than just multiple transactions) acceptance. Package must be a child
508+
* with all of its unconfirmed parents, and topologically sorted.
509+
*/
510+
PackageMempoolAcceptResult AcceptPackage(const Package& package, ATMPArgs& args) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
511+
495512
private:
496513
// All the intermediate state that gets passed between the various levels
497514
// of checking a given transaction.
@@ -1077,6 +1094,62 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptMultipleTransactions(const std::
10771094
return PackageMempoolAcceptResult(package_state, std::move(results));
10781095
}
10791096

1097+
PackageMempoolAcceptResult MemPoolAccept::AcceptPackage(const Package& package, ATMPArgs& args)
1098+
{
1099+
AssertLockHeld(cs_main);
1100+
PackageValidationState package_state;
1101+
1102+
// Check that the package is well-formed. If it isn't, we won't try to validate any of the
1103+
// transactions and thus won't return any MempoolAcceptResults, just a package-wide error.
1104+
1105+
// Context-free package checks.
1106+
if (!CheckPackage(package, package_state)) return PackageMempoolAcceptResult(package_state, {});
1107+
1108+
// All transactions in the package must be a parent of the last transaction. This is just an
1109+
// opportunity for us to fail fast on a context-free check without taking the mempool lock.
1110+
if (!IsChildWithParents(package)) {
1111+
package_state.Invalid(PackageValidationResult::PCKG_POLICY, "package-not-child-with-parents");
1112+
return PackageMempoolAcceptResult(package_state, {});
1113+
}
1114+
1115+
const auto& child = package[package.size() - 1];
1116+
// The package must be 1 child with all of its unconfirmed parents. The package is expected to
1117+
// be sorted, so the last transaction is the child.
1118+
std::unordered_set<uint256, SaltedTxidHasher> unconfirmed_parent_txids;
1119+
std::transform(package.cbegin(), package.end() - 1,
1120+
std::inserter(unconfirmed_parent_txids, unconfirmed_parent_txids.end()),
1121+
[](const auto& tx) { return tx->GetHash(); });
1122+
1123+
// All child inputs must refer to a preceding package transaction or a confirmed UTXO. The only
1124+
// way to verify this is to look up the child's inputs in our current coins view (not including
1125+
// mempool), and enforce that all parents not present in the package be available at chain tip.
1126+
// Since this check can bring new coins into the coins cache, keep track of these coins and
1127+
// uncache them if we don't end up submitting this package to the mempool.
1128+
const CCoinsViewCache& coins_tip_cache = m_active_chainstate.CoinsTip();
1129+
for (const auto& input : child->vin) {
1130+
if (!coins_tip_cache.HaveCoinInCache(input.prevout)) {
1131+
args.m_coins_to_uncache.push_back(input.prevout);
1132+
}
1133+
}
1134+
// Using the MemPoolAccept m_view cache allows us to look up these same coins faster later.
1135+
// This should be connecting directly to CoinsTip, not to m_viewmempool, because we specifically
1136+
// require inputs to be confirmed if they aren't in the package.
1137+
m_view.SetBackend(m_active_chainstate.CoinsTip());
1138+
const auto package_or_confirmed = [this, &unconfirmed_parent_txids](const auto& input) {
1139+
return unconfirmed_parent_txids.count(input.prevout.hash) > 0 || m_view.HaveCoin(input.prevout);
1140+
};
1141+
if (!std::all_of(child->vin.cbegin(), child->vin.cend(), package_or_confirmed)) {
1142+
package_state.Invalid(PackageValidationResult::PCKG_POLICY, "package-not-child-with-unconfirmed-parents");
1143+
return PackageMempoolAcceptResult(package_state, {});
1144+
}
1145+
// Protect against bugs where we pull more inputs from disk that miss being added to
1146+
// coins_to_uncache. The backend will be connected again when needed in PreChecks.
1147+
m_view.SetBackend(m_dummy);
1148+
1149+
LOCK(m_pool.cs);
1150+
return AcceptMultipleTransactions(package, args);
1151+
}
1152+
10801153
} // anon namespace
10811154

10821155
/** (try to) add transaction to memory pool with a specified acceptance time **/
@@ -1120,8 +1193,16 @@ PackageMempoolAcceptResult ProcessNewPackage(CChainState& active_chainstate, CTx
11201193

11211194
std::vector<COutPoint> coins_to_uncache;
11221195
const CChainParams& chainparams = Params();
1123-
auto args = MemPoolAccept::ATMPArgs::PackageTestAccept(chainparams, GetTime(), coins_to_uncache);
1124-
const PackageMempoolAcceptResult result = MemPoolAccept(pool, active_chainstate).AcceptMultipleTransactions(package, args);
1196+
const auto result = [&]() EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
1197+
AssertLockHeld(cs_main);
1198+
if (test_accept) {
1199+
auto args = MemPoolAccept::ATMPArgs::PackageTestAccept(chainparams, GetTime(), coins_to_uncache);
1200+
return MemPoolAccept(pool, active_chainstate).AcceptMultipleTransactions(package, args);
1201+
} else {
1202+
auto args = MemPoolAccept::ATMPArgs::PackageChildWithParents(chainparams, GetTime(), coins_to_uncache);
1203+
return MemPoolAccept(pool, active_chainstate).AcceptPackage(package, args);
1204+
}
1205+
}();
11251206

11261207
// Uncache coins pertaining to transactions that were not submitted to the mempool.
11271208
for (const COutPoint& hashTx : coins_to_uncache) {

0 commit comments

Comments
 (0)