Skip to content

Commit a7ebe53

Browse files
kwvgPastaPastaPasta
authored andcommitted
merge bitcoin#22084: package testmempoolaccept followups
inapplicable: - 5cac95c (we don't have RBF)
1 parent 792b430 commit a7ebe53

9 files changed

+102
-73
lines changed

src/Makefile.am

+1
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,7 @@ libbitcoin_server_a_SOURCES = \
484484
node/ui_interface.cpp \
485485
noui.cpp \
486486
policy/fees.cpp \
487+
policy/packages.cpp \
487488
policy/policy.cpp \
488489
policy/settings.cpp \
489490
pow.cpp \

src/policy/packages.cpp

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Copyright (c) 2021 The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#include <consensus/validation.h>
6+
#include <policy/packages.h>
7+
#include <primitives/transaction.h>
8+
#include <uint256.h>
9+
#include <util/hasher.h>
10+
11+
#include <numeric>
12+
#include <unordered_set>
13+
14+
bool CheckPackage(const Package& txns, PackageValidationState& state)
15+
{
16+
const unsigned int package_count = txns.size();
17+
18+
if (package_count > MAX_PACKAGE_COUNT) {
19+
return state.Invalid(PackageValidationResult::PCKG_POLICY, "package-too-many-transactions");
20+
}
21+
22+
const int64_t total_size = std::accumulate(txns.cbegin(), txns.cend(), 0,
23+
[](int64_t sum, const auto& tx) { return sum + GetVirtualTransactionSize(*tx); });
24+
// If the package only contains 1 tx, it's better to report the policy violation on individual tx size.
25+
if (package_count > 1 && total_size > MAX_PACKAGE_SIZE * 1000) {
26+
return state.Invalid(PackageValidationResult::PCKG_POLICY, "package-too-large");
27+
}
28+
29+
// Require the package to be sorted in order of dependency, i.e. parents appear before children.
30+
// An unsorted package will fail anyway on missing-inputs, but it's better to quit earlier and
31+
// fail on something less ambiguous (missing-inputs could also be an orphan or trying to
32+
// spend nonexistent coins).
33+
std::unordered_set<uint256, SaltedTxidHasher> later_txids;
34+
std::transform(txns.cbegin(), txns.cend(), std::inserter(later_txids, later_txids.end()),
35+
[](const auto& tx) { return tx->GetHash(); });
36+
for (const auto& tx : txns) {
37+
for (const auto& input : tx->vin) {
38+
if (later_txids.find(input.prevout.hash) != later_txids.end()) {
39+
// The parent is a subsequent transaction in the package.
40+
return state.Invalid(PackageValidationResult::PCKG_POLICY, "package-not-sorted");
41+
}
42+
}
43+
later_txids.erase(tx->GetHash());
44+
}
45+
46+
// Don't allow any conflicting transactions, i.e. spending the same inputs, in a package.
47+
std::unordered_set<COutPoint, SaltedOutpointHasher> inputs_seen;
48+
for (const auto& tx : txns) {
49+
for (const auto& input : tx->vin) {
50+
if (inputs_seen.find(input.prevout) != inputs_seen.end()) {
51+
// This input is also present in another tx in the package.
52+
return state.Invalid(PackageValidationResult::PCKG_POLICY, "conflict-in-package");
53+
}
54+
}
55+
// Batch-add all the inputs for a tx at a time. If we added them 1 at a time, we could
56+
// catch duplicate inputs within a single tx. This is a more severe, consensus error,
57+
// and we want to report that from CheckTransaction instead.
58+
std::transform(tx->vin.cbegin(), tx->vin.cend(), std::inserter(inputs_seen, inputs_seen.end()),
59+
[](const auto& input) { return input.prevout; });
60+
}
61+
return true;
62+
}

src/policy/packages.h

+10
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#define BITCOIN_POLICY_PACKAGES_H
77

88
#include <consensus/validation.h>
9+
#include <policy/policy.h>
910
#include <primitives/transaction.h>
1011

1112
#include <vector>
@@ -14,6 +15,7 @@
1415
static constexpr uint32_t MAX_PACKAGE_COUNT{25};
1516
/** Default maximum total virtual size of transactions in a package in KvB. */
1617
static constexpr uint32_t MAX_PACKAGE_SIZE{101};
18+
static_assert(MAX_PACKAGE_SIZE * 1000 >= MAX_STANDARD_TX_SIZE);
1719

1820
/** A "reason" why a package was invalid. It may be that one or more of the included
1921
* transactions is invalid or the package itself violates our rules.
@@ -31,4 +33,12 @@ using Package = std::vector<CTransactionRef>;
3133

3234
class PackageValidationState : public ValidationState<PackageValidationResult> {};
3335

36+
/** Context-free package policy checks:
37+
* 1. The number of transactions cannot exceed MAX_PACKAGE_COUNT.
38+
* 2. The total virtual size cannot exceed MAX_PACKAGE_SIZE.
39+
* 3. If any dependencies exist between transactions, parents must appear before children.
40+
* 4. Transactions cannot conflict, i.e., spend the same inputs.
41+
*/
42+
bool CheckPackage(const Package& txns, PackageValidationState& state);
43+
3444
#endif // BITCOIN_POLICY_PACKAGES_H

src/rpc/rawtransaction.cpp

+5-5
Original file line numberDiff line numberDiff line change
@@ -1159,7 +1159,7 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request)
11591159
"\nReturns result of mempool acceptance tests indicating if raw transaction (serialized, hex-encoded) would be accepted by mempool.\n"
11601160
"\nIf multiple transactions are passed in, parents must come before children and package policies apply: the transactions cannot conflict with any mempool transactions or each other.\n"
11611161
"\nIf one transaction fails, other transactions may not be fully validated (the 'allowed' key will be blank).\n"
1162-
"\nThe maximum number of transactions allowed is 25 (MAX_PACKAGE_COUNT)\n"
1162+
"\nThe maximum number of transactions allowed is " + ToString(MAX_PACKAGE_COUNT) + ".\n"
11631163
"\nThis checks if transactions violate the consensus or policy rules.\n"
11641164
"\nSee sendrawtransaction call.\n",
11651165
{
@@ -1174,7 +1174,7 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request)
11741174
RPCResult{
11751175
RPCResult::Type::ARR, "", "The result of the mempool acceptance test for each raw transaction in the input array.\n"
11761176
"Returns results for each transaction in the same order they were passed in.\n"
1177-
"It is possible for transactions to not be fully validated ('allowed' unset) if an earlier transaction failed.\n",
1177+
"It is possible for transactions to not be fully validated ('allowed' unset) if another transaction failed.\n",
11781178
{
11791179
{RPCResult::Type::OBJ, "", "",
11801180
{
@@ -1207,7 +1207,6 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request)
12071207
UniValue::VARR,
12081208
UniValueType(), // VNUM or VSTR, checked inside AmountFromValue()
12091209
});
1210-
12111210
const UniValue raw_transactions = request.params[0].get_array();
12121211
if (raw_transactions.size() < 1 || raw_transactions.size() > MAX_PACKAGE_COUNT) {
12131212
throw JSONRPCError(RPC_INVALID_PARAMETER,
@@ -1219,6 +1218,7 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request)
12191218
CFeeRate(AmountFromValue(request.params[1]));
12201219

12211220
std::vector<CTransactionRef> txns;
1221+
txns.reserve(raw_transactions.size());
12221222
for (const auto& rawtx : raw_transactions.getValues()) {
12231223
CMutableTransaction mtx;
12241224
if (!DecodeHexTx(mtx, rawtx.get_str())) {
@@ -1239,8 +1239,8 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request)
12391239
}();
12401240

12411241
UniValue rpc_result(UniValue::VARR);
1242-
// We will check transaction fees we iterate through txns in order. If any transaction fee
1243-
// exceeds maxfeerate, we will keave the rest of the validation results blank, because it
1242+
// We will check transaction fees while we iterate through txns in order. If any transaction fee
1243+
// exceeds maxfeerate, we will leave the rest of the validation results blank, because it
12441244
// doesn't make sense to return a validation result for a transaction if its ancestor(s) would
12451245
// not be submitted.
12461246
bool exit_early{false};

src/test/miner_tests.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ struct MinerTestingSetup : public TestingSetup {
3535
void TestPackageSelection(const CChainParams& chainparams, const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, m_node.mempool->cs);
3636
bool TestSequenceLocks(const CTransaction& tx, int flags) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, m_node.mempool->cs)
3737
{
38-
CCoinsViewMemPool viewMempool(&m_node.chainman->ActiveChainstate().CoinsTip(), *m_node.mempool);
39-
return CheckSequenceLocks(m_node.chainman->ActiveChain().Tip(), viewMempool, tx, flags);
38+
CCoinsViewMemPool view_mempool(&m_node.chainman->ActiveChainstate().CoinsTip(), *m_node.mempool);
39+
return CheckSequenceLocks(m_node.chainman->ActiveChain().Tip(), view_mempool, tx, flags);
4040
}
4141
BlockAssembler AssemblerForTest(const CChainParams& params);
4242
};

src/txmempool.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -728,9 +728,9 @@ void CTxMemPool::removeForReorg(CChainState& active_chainstate, int flags) EXCLU
728728
LockPoints lp = it->GetLockPoints();
729729
assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
730730
bool validLP = TestLockPointValidity(active_chainstate.m_chain, &lp);
731-
CCoinsViewMemPool viewMempool(&active_chainstate.CoinsTip(), *this);
731+
CCoinsViewMemPool view_mempool(&active_chainstate.CoinsTip(), *this);
732732
if (!CheckFinalTx(active_chainstate.m_chain.Tip(), tx, flags)
733-
|| !CheckSequenceLocks(active_chainstate.m_chain.Tip(), viewMempool, tx, flags, &lp, validLP)) {
733+
|| !CheckSequenceLocks(active_chainstate.m_chain.Tip(), view_mempool, tx, flags, &lp, validLP)) {
734734
// Note if CheckSequenceLocks fails the LockPoints may still be invalid
735735
// So it's critical that we remove the tx and not depend on the LockPoints.
736736
txToRemove.insert(it);

src/txmempool.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -859,7 +859,8 @@ class CCoinsViewMemPool : public CCoinsViewBacked
859859
public:
860860
CCoinsViewMemPool(CCoinsView* baseIn, const CTxMemPool& mempoolIn);
861861
bool GetCoin(const COutPoint &outpoint, Coin &coin) const override;
862-
/** Add the coins created by this transaction. */
862+
/** Add the coins created by this transaction. These coins are only temporarily stored in
863+
* m_temp_added and cannot be flushed to the back end. Only used for package validation. */
863864
void PackageAddTransaction(const CTransactionRef& tx);
864865
};
865866

src/validation.cpp

+6-57
Original file line numberDiff line numberDiff line change
@@ -559,7 +559,7 @@ class MemPoolAccept
559559
/**
560560
* Multiple transaction acceptance. Transactions may or may not be interdependent,
561561
* but must not conflict with each other. Parents must come before children if any
562-
* dependencies exist, otherwise a TX_MISSING_INPUTS error will be returned.
562+
* dependencies exist.
563563
*/
564564
PackageMempoolAcceptResult AcceptMultipleTransactions(const std::vector<CTransactionRef>& txns, ATMPArgs& args) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
565565

@@ -982,65 +982,15 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptMultipleTransactions(const std::
982982
{
983983
AssertLockHeld(cs_main);
984984

985+
// These context-free package limits can be done before taking the mempool lock.
985986
PackageValidationState package_state;
986-
const unsigned int package_count = txns.size();
987+
if (!CheckPackage(txns, package_state)) return PackageMempoolAcceptResult(package_state, {});
987988

988-
// These context-free package limits can be checked before taking the mempool lock.
989-
if (package_count > MAX_PACKAGE_COUNT) {
990-
package_state.Invalid(PackageValidationResult::PCKG_POLICY, "package-too-many-transactions");
991-
return PackageMempoolAcceptResult(package_state, {});
992-
}
993-
994-
const int64_t total_size = std::accumulate(txns.cbegin(), txns.cend(), 0,
995-
[](int64_t sum, const auto& tx) { return sum + GetVirtualTransactionSize(*tx); });
996-
// If the package only contains 1 tx, it's better to report the policy violation on individual tx size.
997-
if (package_count > 1 && total_size > MAX_PACKAGE_SIZE * 1000) {
998-
package_state.Invalid(PackageValidationResult::PCKG_POLICY, "package-too-large");
999-
return PackageMempoolAcceptResult(package_state, {});
1000-
}
1001-
1002-
// Construct workspaces and check package policies.
1003989
std::vector<Workspace> workspaces{};
1004-
workspaces.reserve(package_count);
1005-
{
1006-
std::unordered_set<uint256, SaltedTxidHasher> later_txids;
1007-
std::transform(txns.cbegin(), txns.cend(), std::inserter(later_txids, later_txids.end()),
1008-
[](const auto& tx) { return tx->GetHash(); });
1009-
// Require the package to be sorted in order of dependency, i.e. parents appear before children.
1010-
// An unsorted package will fail anyway on missing-inputs, but it's better to quit earlier and
1011-
// fail on something less ambiguous (missing-inputs could also be an orphan or trying to
1012-
// spend nonexistent coins).
1013-
for (const auto& tx : txns) {
1014-
for (const auto& input : tx->vin) {
1015-
if (later_txids.find(input.prevout.hash) != later_txids.end()) {
1016-
// The parent is a subsequent transaction in the package.
1017-
package_state.Invalid(PackageValidationResult::PCKG_POLICY, "package-not-sorted");
1018-
return PackageMempoolAcceptResult(package_state, {});
1019-
}
1020-
}
1021-
later_txids.erase(tx->GetHash());
1022-
workspaces.emplace_back(Workspace(tx));
1023-
}
1024-
}
990+
workspaces.reserve(txns.size());
991+
std::transform(txns.cbegin(), txns.cend(), std::back_inserter(workspaces),
992+
[](const auto& tx) { return Workspace(tx); });
1025993
std::map<const uint256, const MempoolAcceptResult> results;
1026-
{
1027-
// Don't allow any conflicting transactions, i.e. spending the same inputs, in a package.
1028-
std::unordered_set<COutPoint, SaltedOutpointHasher> inputs_seen;
1029-
for (const auto& tx : txns) {
1030-
for (const auto& input : tx->vin) {
1031-
if (inputs_seen.find(input.prevout) != inputs_seen.end()) {
1032-
// This input is also present in another tx in the package.
1033-
package_state.Invalid(PackageValidationResult::PCKG_POLICY, "conflict-in-package");
1034-
return PackageMempoolAcceptResult(package_state, {});
1035-
}
1036-
}
1037-
// Batch-add all the inputs for a tx at a time. If we added them 1 at a time, we could
1038-
// catch duplicate inputs within a single tx. This is a more severe, consensus error,
1039-
// and we want to report that from CheckTransaction instead.
1040-
std::transform(tx->vin.cbegin(), tx->vin.cend(), std::inserter(inputs_seen, inputs_seen.end()),
1041-
[](const auto& input) { return input.prevout; });
1042-
}
1043-
}
1044994

1045995
LOCK(m_pool.cs);
1046996

@@ -1127,7 +1077,6 @@ PackageMempoolAcceptResult ProcessNewPackage(CChainState& active_chainstate, CTx
11271077
const PackageMempoolAcceptResult result = MemPoolAccept(pool, active_chainstate).AcceptMultipleTransactions(package, args);
11281078

11291079
// Uncache coins pertaining to transactions that were not submitted to the mempool.
1130-
// Ensure the cache is still within its size limits.
11311080
for (const COutPoint& hashTx : coins_to_uncache) {
11321081
active_chainstate.CoinsTip().Uncache(hashTx);
11331082
}

src/validation.h

+12-6
Original file line numberDiff line numberDiff line change
@@ -291,10 +291,12 @@ MempoolAcceptResult AcceptToMemoryPool(CChainState& active_chainstate, CTxMemPoo
291291
bool bypass_limits, bool test_accept=false) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
292292

293293
/**
294-
* Atomically test acceptance of a package. If the package only contains one tx, package rules still apply.
294+
* Atomically test acceptance of a package. If the package only contains one tx, package rules still
295+
* apply. The transaction(s) cannot spend the same inputs as any transaction in the mempool.
295296
* @param[in] txns Group of transactions which may be independent or contain
296-
* parent-child dependencies. The transactions must not conflict, i.e.
297-
* must not spend the same inputs. Parents must appear before children.
297+
* parent-child dependencies. The transactions must not conflict
298+
* with each other, i.e., must not spend the same inputs. If any
299+
* dependencies exist, parents must appear before children.
298300
* @returns a PackageMempoolAcceptResult which includes a MempoolAcceptResult for each transaction.
299301
* If a transaction fails, validation will exit early and some results may be missing.
300302
*/
@@ -329,9 +331,13 @@ bool TestLockPointValidity(CChain& active_chain, const LockPoints* lp) EXCLUSIVE
329331
* Check if transaction will be BIP68 final in the next block to be created on top of tip.
330332
* @param[in] tip Chain tip to check tx sequence locks against. For example,
331333
* the tip of the current active chain.
332-
* @param[in] coins_view Any CCoinsView that provides access to the relevant coins
333-
* for checking sequence locks. Any CCoinsView can be passed in;
334-
* it is assumed to be consistent with the tip.
334+
* @param[in] coins_view Any CCoinsView that provides access to the relevant coins for
335+
* checking sequence locks. For example, it can be a CCoinsViewCache
336+
* that isn't connected to anything but contains all the relevant
337+
* coins, or a CCoinsViewMemPool that is connected to the
338+
* mempool and chainstate UTXO set. In the latter case, the caller is
339+
* responsible for holding the appropriate locks to ensure that
340+
* calls to GetCoin() return correct coins.
335341
* Simulates calling SequenceLocks() with data from the tip passed in.
336342
* Optionally stores in LockPoints the resulting height and time calculated and the hash
337343
* of the block needed for calculation or skips the calculation and uses the LockPoints

0 commit comments

Comments
 (0)