Skip to content

Commit 84eff59

Browse files
committed
Merge 9749 via unique_spk_mempool-28+knots
2 parents 9d18c6e + 399f5d0 commit 84eff59

File tree

10 files changed

+163
-21
lines changed

10 files changed

+163
-21
lines changed

src/init.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -690,6 +690,7 @@ void SetupServerArgs(ArgsManager& argsman)
690690
OptionsCategory::NODE_RELAY);
691691
argsman.AddArg("-minrelaytxfee=<amt>", strprintf("Fees (in %s/kvB) smaller than this are considered zero fee for relaying, mining and transaction creation (default: %s)",
692692
CURRENCY_UNIT, FormatMoney(DEFAULT_MIN_RELAY_TX_FEE)), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
693+
argsman.AddArg("-spkreuse=<policy>", strprintf("Either \"allow\" to relay/mine transactions reusing addresses or other pubkey scripts, or \"conflict\" to treat them as exclusive prior to being mined (default: %s)", DEFAULT_SPKREUSE), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
693694
argsman.AddArg("-whitelistforcerelay", strprintf("Add 'forcerelay' permission to whitelisted peers with default permissions. This will relay transactions even if the transactions were already in the mempool. (default: %d)", DEFAULT_WHITELISTFORCERELAY), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
694695
argsman.AddArg("-whitelistrelay", strprintf("Add 'relay' permission to whitelisted peers with default permissions. This will accept relayed transactions even when not relaying transactions (default: %d)", DEFAULT_WHITELISTRELAY), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
695696

@@ -1087,6 +1088,16 @@ bool AppInitParameterInteraction(const ArgsManager& args)
10871088

10881089
if (!g_wallet_init_interface.ParameterInteraction()) return false;
10891090

1091+
{
1092+
std::string strSpkReuse = gArgs.GetArg("-spkreuse", DEFAULT_SPKREUSE);
1093+
// Uses string values so future versions can implement other modes
1094+
if (strSpkReuse == "allow" || gArgs.GetBoolArg("-spkreuse", false)) {
1095+
SpkReuseMode = SRM_ALLOW;
1096+
} else {
1097+
SpkReuseMode = SRM_REJECT;
1098+
}
1099+
}
1100+
10901101
// Option to startup with mocktime set (used for regression testing):
10911102
SetMockTime(args.GetIntArg("-mocktime", 0)); // SetMockTime(0) is a no-op
10921103

src/kernel/mempool_entry.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,15 @@ struct LockPoints {
3737
CBlockIndex* maxInputBlock{nullptr};
3838
};
3939

40+
enum MemPool_SPK_State {
41+
MSS_UNSEEN = 0,
42+
MSS_SPENT = 1, // .second
43+
MSS_CREATED = 2, // .first
44+
MSS_BOTH = 3,
45+
};
46+
47+
typedef std::map<uint160, enum MemPool_SPK_State> SPKStates_t;
48+
4049
struct CompareIteratorByHash {
4150
// SFINAE for T where T is either a pointer type (e.g., a txiter) or a reference_wrapper<T>
4251
// (e.g. a wrapped CTxMemPoolEntry&)
@@ -215,6 +224,8 @@ class CTxMemPoolEntry
215224

216225
mutable size_t idx_randomized; //!< Index in mempool's txns_randomized
217226
mutable Epoch::Marker m_epoch_marker; //!< epoch when last touched, useful for graph algorithms
227+
228+
SPKStates_t mapSPK;
218229
};
219230

220231
using CTxMemPoolEntryRef = CTxMemPoolEntry::CTxMemPoolEntryRef;

src/policy/policy.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ static constexpr unsigned int MAX_STANDARD_SCRIPTSIG_SIZE{1650};
6767
* only increase the dust limit after prior releases were already not creating
6868
* outputs below the new threshold */
6969
static constexpr unsigned int DUST_RELAY_TX_FEE{3000};
70+
static const std::string DEFAULT_SPKREUSE{"allow"};
7071
/** Default for -minrelaytxfee, minimum relay fee for transactions */
7172
static constexpr unsigned int DEFAULT_MIN_RELAY_TX_FEE{1000};
7273
/** Default for -limitancestorcount, max number of in-mempool ancestors */

src/policy/rbf.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,12 +119,17 @@ std::optional<std::string> HasNoNewUnconfirmed(const CTransaction& tx,
119119
}
120120

121121
std::optional<std::string> EntriesAndTxidsDisjoint(const CTxMemPool::setEntries& ancestors,
122-
const std::set<Txid>& direct_conflicts,
123-
const uint256& txid)
122+
const std::map<Txid, bool>& direct_conflicts,
123+
const uint256& txid, bool* const out_violates_policy)
124124
{
125125
for (CTxMemPool::txiter ancestorIt : ancestors) {
126126
const Txid& hashAncestor = ancestorIt->GetTx().GetHash();
127-
if (direct_conflicts.count(hashAncestor)) {
127+
const auto& conflictit = direct_conflicts.find(hashAncestor);
128+
if (conflictit != direct_conflicts.end()) {
129+
if (!conflictit->second /* mere SPK conflict, NOT invalid */) {
130+
if (out_violates_policy) *out_violates_policy = true;
131+
continue;
132+
}
128133
return strprintf("%s spends conflicting transaction %s",
129134
txid.ToString(),
130135
hashAncestor.ToString());

src/policy/rbf.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,11 +88,12 @@ std::optional<std::string> HasNoNewUnconfirmed(const CTransaction& tx, const CTx
8888
* @param[in] direct_conflicts Set of txids corresponding to the mempool conflicts
8989
* (candidates to be replaced).
9090
* @param[in] txid Transaction ID, included in the error message if violation occurs.
91-
* @returns error message if the sets intersect, std::nullopt if they are disjoint.
91+
* @param[out] out_violates_policy Assigned to true if there are any policy-only conflicts.
92+
* @returns error message if the sets intersect (consensus-only conflicts), std::nullopt if they are disjoint or only intersect on policy matters.
9293
*/
9394
std::optional<std::string> EntriesAndTxidsDisjoint(const CTxMemPool::setEntries& ancestors,
94-
const std::set<Txid>& direct_conflicts,
95-
const uint256& txid);
95+
const std::map<Txid, bool>& direct_conflicts,
96+
const uint256& txid, bool* out_violates_policy);
9697

9798
/** Check that the feerate of the replacement transaction(s) is higher than the feerate of each
9899
* of the transactions in iters_conflicting.

src/test/rbf_tests.cpp

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -216,14 +216,22 @@ BOOST_FIXTURE_TEST_CASE(rbf_helper_functions, TestChain100Setup)
216216
BOOST_CHECK(PaysMoreThanConflicts(set_34_cpfp, CFeeRate(entry4_high->GetModifiedFee(), entry4_high->GetTxSize()), unused_txid).has_value());
217217

218218
// Tests for EntriesAndTxidsDisjoint
219-
BOOST_CHECK(EntriesAndTxidsDisjoint(empty_set, {tx1->GetHash()}, unused_txid) == std::nullopt);
220-
BOOST_CHECK(EntriesAndTxidsDisjoint(set_12_normal, {tx3->GetHash()}, unused_txid) == std::nullopt);
221-
BOOST_CHECK(EntriesAndTxidsDisjoint({entry2_normal}, {tx2->GetHash()}, unused_txid).has_value());
222-
BOOST_CHECK(EntriesAndTxidsDisjoint(set_12_normal, {tx1->GetHash()}, unused_txid).has_value());
223-
BOOST_CHECK(EntriesAndTxidsDisjoint(set_12_normal, {tx2->GetHash()}, unused_txid).has_value());
219+
bool violates_policy{false};
220+
BOOST_CHECK(EntriesAndTxidsDisjoint(empty_set, {{tx1->GetHash(), true}}, unused_txid, &violates_policy) == std::nullopt);
221+
BOOST_CHECK(!violates_policy);
222+
BOOST_CHECK(EntriesAndTxidsDisjoint(set_12_normal, {{tx3->GetHash(), true}}, unused_txid, &violates_policy) == std::nullopt);
223+
BOOST_CHECK(!violates_policy);
224+
BOOST_CHECK(EntriesAndTxidsDisjoint({entry2_normal}, {{tx2->GetHash(), true}}, unused_txid, &violates_policy).has_value());
225+
BOOST_CHECK(!violates_policy);
226+
BOOST_CHECK(EntriesAndTxidsDisjoint(set_12_normal, {{tx1->GetHash(), true}}, unused_txid, &violates_policy).has_value());
227+
BOOST_CHECK(!violates_policy);
228+
BOOST_CHECK(EntriesAndTxidsDisjoint(set_12_normal, {{tx2->GetHash(), true}}, unused_txid, &violates_policy).has_value());
229+
BOOST_CHECK(!violates_policy);
224230
// EntriesAndTxidsDisjoint does not calculate descendants of iters_conflicting; it uses whatever
225231
// the caller passed in. As such, no error is returned even though entry2_normal is a descendant of tx1.
226-
BOOST_CHECK(EntriesAndTxidsDisjoint({entry2_normal}, {tx1->GetHash()}, unused_txid) == std::nullopt);
232+
BOOST_CHECK(EntriesAndTxidsDisjoint({entry2_normal}, {{tx1->GetHash(), true}}, unused_txid, &violates_policy) == std::nullopt);
233+
BOOST_CHECK(!violates_policy);
234+
// TODO: Add tests for policy-only conflicts
227235

228236
// Tests for PaysForRBF
229237
const CFeeRate incremental_relay_feerate{DEFAULT_INCREMENTAL_RELAY_FEE};

src/txmempool.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@
1111
#include <consensus/consensus.h>
1212
#include <consensus/tx_verify.h>
1313
#include <consensus/validation.h>
14+
#include <crypto/ripemd160.h>
1415
#include <logging.h>
1516
#include <policy/coin_age_priority.h>
1617
#include <policy/policy.h>
1718
#include <policy/settings.h>
1819
#include <random.h>
1920
#include <tinyformat.h>
21+
#include <script/script.h>
2022
#include <util/check.h>
2123
#include <util/feefrac.h>
2224
#include <util/moneystr.h>
@@ -52,6 +54,13 @@ bool TestLockPointValidity(CChain& active_chain, const LockPoints& lp)
5254
return true;
5355
}
5456

57+
uint160 ScriptHashkey(const CScript& script)
58+
{
59+
uint160 hash;
60+
CRIPEMD160().Write(script.data(), script.size()).Finalize(hash.begin());
61+
return hash;
62+
}
63+
5564
void CTxMemPool::UpdateForDescendants(txiter updateIt, cacheMap& cachedDescendants,
5665
const std::set<uint256>& setExclude, std::set<uint256>& descendants_to_remove)
5766
{
@@ -480,6 +489,17 @@ void CTxMemPool::addUnchecked(const CTxMemPoolEntry &entry, setEntries &setAnces
480489
txns_randomized.emplace_back(newit->GetSharedTx());
481490
newit->idx_randomized = txns_randomized.size() - 1;
482491

492+
for (auto& vSPK : entry.mapSPK) {
493+
const uint160& SPKKey = vSPK.first;
494+
const MemPool_SPK_State& claims = vSPK.second;
495+
if (claims & MSS_CREATED) {
496+
mapUsedSPK[SPKKey].first = &tx;
497+
}
498+
if (claims & MSS_SPENT) {
499+
mapUsedSPK[SPKKey].second = &tx;
500+
}
501+
}
502+
483503
TRACE3(mempool, added,
484504
entry.GetTx().GetHash().data(),
485505
entry.GetTxSize(),
@@ -508,6 +528,7 @@ void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason)
508528
std::chrono::duration_cast<std::chrono::duration<std::uint64_t>>(it->GetTime()).count()
509529
);
510530

531+
const CTransaction& tx = it->GetTx();
511532
for (const CTxIn& txin : it->GetTx().vin)
512533
mapNextTx.erase(txin.prevout);
513534

@@ -524,6 +545,19 @@ void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason)
524545
} else
525546
txns_randomized.clear();
526547

548+
for (auto& vSPK : it->mapSPK) {
549+
const uint160& SPKKey = vSPK.first;
550+
if (mapUsedSPK[SPKKey].first == &tx) {
551+
mapUsedSPK[SPKKey].first = NULL;
552+
}
553+
if (mapUsedSPK[SPKKey].second == &tx) {
554+
mapUsedSPK[SPKKey].second = NULL;
555+
}
556+
if (!(mapUsedSPK[SPKKey].first || mapUsedSPK[SPKKey].second)) {
557+
mapUsedSPK.erase(SPKKey);
558+
}
559+
}
560+
527561
totalTxSize -= it->GetTxSize();
528562
m_total_fee -= it->GetFee();
529563
cachedInnerUsage -= it->DynamicMemoryUsage();

src/txmempool.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include <policy/feerate.h>
1818
#include <policy/packages.h>
1919
#include <primitives/transaction.h>
20+
#include <script/script.h>
2021
#include <sync.h>
2122
#include <util/epochguard.h>
2223
#include <util/hasher.h>
@@ -205,6 +206,8 @@ class CompareTxMemPoolEntryByAncestorFee
205206
}
206207
};
207208

209+
uint160 ScriptHashkey(const CScript& script);
210+
208211
// Multi_index tag names
209212
struct descendant_score {};
210213
struct entry_time {};
@@ -403,6 +406,9 @@ class CTxMemPool
403406
using Limits = kernel::MemPoolLimits;
404407

405408
uint64_t CalculateDescendantMaximum(txiter entry) const EXCLUSIVE_LOCKS_REQUIRED(cs);
409+
410+
std::map<uint160, std::pair<const CTransaction *, const CTransaction *>> mapUsedSPK;
411+
406412
private:
407413
typedef std::map<txiter, setEntries, CompareIteratorByHash> cacheMap;
408414

0 commit comments

Comments
 (0)