Skip to content
Bitcoin Roots

v30.3-roots.1 · Commit 3: [PATCH 03/68] feat(policy): preserve Roots relay and network controls on Core 30.3

src/txmempool.cpp

Before: src/txmempool.cpp · After: src/txmempool.cpp

File metadata
index cae16a675bd4cc91a5603376c5e1f79674e81355..17ca9abc1c843b5ee54e2de9f97d56d041bba150 100644

Download original patch · Release notes

Diff rows 1–336 of 336. Line numbers refer to the original before/after files.

+ Added− RemovedUnmarked lines are context.

Scroll the code area horizontally to read long lines. The two line-number columns refer to before and after.

src/txmempool.cpp CHANGED
@@ -11,11 +11,16 @@
11
11
  #include <consensus/consensus.h>
12
12
  #include <consensus/tx_verify.h>
13
13
  #include <consensus/validation.h>
14
+ #include <crypto/ripemd160.h>
14
15
  #include <logging.h>
16
+ #include <policy/coin_age_priority.h>
17
+ #include <policy/fees.h>
15
18
  #include <policy/policy.h>
16
19
  #include <policy/settings.h>
17
20
  #include <random.h>
21
+ #include <scheduler.h>
18
22
  #include <tinyformat.h>
23
+ #include <script/script.h>
19
24
  #include <util/check.h>
20
25
  #include <util/feefrac.h>
21
26
  #include <util/moneystr.h>
@@ -37,6 +42,49 @@
37
42
  TRACEPOINT_SEMAPHORE(mempool, added);
38
43
  TRACEPOINT_SEMAPHORE(mempool, removed);
39
44
 
45
+ void CTxMemPoolEntry::UpdateCachedPriority(unsigned int current_height, CAmount value_in_current_block)
46
+ {
47
+ const int height_diff{int(current_height) - int(cachedHeight)};
48
+ cachedPriority += (double(height_diff) * inChainInputValue) / nModSize;
49
+ cachedHeight = current_height;
50
+ inChainInputValue += value_in_current_block;
51
+ assert(MoneyRange(inChainInputValue));
52
+ }
53
+
54
+ double CTxMemPoolEntry::GetPriority(unsigned int current_height) const
55
+ {
56
+ // This is accurate when the height range does not cross a block containing
57
+ // one of the transaction's inputs.
58
+ const int height_diff{int(current_height) - int(cachedHeight)};
59
+ const double priority{cachedPriority + (double(height_diff) * inChainInputValue) / nModSize};
60
+ return std::max(priority, 0.0);
61
+ }
62
+
63
+ namespace {
64
+ struct UpdatePriority {
65
+ unsigned int height;
66
+ CAmount value;
67
+
68
+ void operator()(CTxMemPoolEntry& entry) const
69
+ {
70
+ entry.UpdateCachedPriority(height, value);
71
+ }
72
+ };
73
+ } // namespace
74
+
75
+ void CTxMemPool::UpdateDependentPriorities(const CTransaction& tx, unsigned int block_height, bool add_to_chain)
76
+ {
77
+ LOCK(cs);
78
+ for (unsigned int i{0}; i < tx.vout.size(); ++i) {
79
+ const auto next_tx{mapNextTx.find(COutPoint{tx.GetHash(), i})};
80
+ if (next_tx == mapNextTx.end()) continue;
81
+
82
+ const txiter entry{mapTx.find(next_tx->second->GetHash())};
83
+ const CAmount value{add_to_chain ? tx.vout[i].nValue : -tx.vout[i].nValue};
84
+ mapTx.modify(entry, UpdatePriority{block_height, value});
85
+ }
86
+ }
87
+
40
88
  bool TestLockPointValidity(CChain& active_chain, const LockPoints& lp)
41
89
  {
42
90
  AssertLockHeld(cs_main);
@@ -54,6 +102,13 @@ bool TestLockPointValidity(CChain& active_chain, const LockPoints& lp)
54
102
  return true;
55
103
  }
56
104
 
105
+ uint160 ScriptHashkey(const CScript& script)
106
+ {
107
+ uint160 hash;
108
+ CRIPEMD160().Write(script.data(), script.size()).Finalize(hash.begin());
109
+ return hash;
110
+ }
111
+
57
112
  void CTxMemPool::UpdateForDescendants(txiter updateIt, cacheMap& cachedDescendants,
58
113
  const std::set<Txid>& setExclude, std::set<Txid>& descendants_to_remove)
59
114
  {
@@ -404,7 +459,7 @@ void CTxMemPoolEntry::UpdateAncestorState(int32_t modifySize, CAmount modifyFee,
404
459
  static CTxMemPool::Options&& Flatten(CTxMemPool::Options&& opts, bilingual_str& error)
405
460
  {
406
461
  opts.check_ratio = std::clamp<int>(opts.check_ratio, 0, 1'000'000);
407
- int64_t descendant_limit_bytes = opts.limits.descendant_size_vbytes * 40;
462
+ int64_t descendant_limit_bytes = maxmempoolMinimumBytes(opts.limits.descendant_size_vbytes);
408
463
  if (opts.max_size_bytes < 0 || opts.max_size_bytes < descendant_limit_bytes) {
409
464
  error = strprintf(_("-maxmempool must be at least %d MB"), std::ceil(descendant_limit_bytes / 1'000'000.0));
410
465
  }
@@ -414,6 +469,17 @@ static CTxMemPool::Options&& Flatten(CTxMemPool::Options&& opts, bilingual_str&
414
469
  CTxMemPool::CTxMemPool(Options opts, bilingual_str& error)
415
470
  : m_opts{Flatten(std::move(opts), error)}
416
471
  {
472
+ Assert(m_opts.scheduler || !m_opts.dust_relay_target);
473
+ m_opts.dust_relay_feerate_floor = m_opts.dust_relay_feerate;
474
+ #ifdef BUILDING_FOR_LIBBITCOINKERNEL
475
+ assert(!m_opts.scheduler);
476
+ #else
477
+ if (m_opts.scheduler) {
478
+ m_opts.scheduler->scheduleEvery([this]{
479
+ UpdateDynamicDustFeerate();
480
+ }, DYNAMIC_DUST_FEERATE_UPDATE_INTERVAL);
481
+ }
482
+ #endif
417
483
  }
418
484
 
419
485
  bool CTxMemPool::isSpent(const COutPoint& outpoint) const
@@ -453,11 +519,20 @@ void CTxMemPool::Apply(ChangeSet* changeset)
453
519
  ancestors = *Assume(changeset->CalculateMemPoolAncestors(tx_entry, Limits::NoLimits()));
454
520
  }
455
521
  // First splice this entry into mapTx.
522
+ #if BOOST_VERSION >= 107400
456
523
  auto node_handle = changeset->m_to_add.extract(tx_entry);
457
524
  auto result = mapTx.insert(std::move(node_handle));
458
525
 
459
526
  Assume(result.inserted);
460
527
  txiter it = result.position;
528
+ #else
529
+ // Boost 1.73 didn't support node extraction, so we have to copy
530
+ auto result = mapTx.emplace(CTxMemPoolEntry::ExplicitCopy, *tx_entry);
531
+ changeset->m_to_add.erase(tx_entry);
532
+
533
+ Assume(result.second);
534
+ txiter it = result.first;
535
+ #endif
461
536
 
462
537
  // Now update the entry for ancestors/descendants.
463
538
  if (ancestors.has_value()) {
@@ -510,6 +585,17 @@ void CTxMemPool::addNewTransaction(CTxMemPool::txiter newit, CTxMemPool::setEntr
510
585
  txns_randomized.emplace_back(tx.GetWitnessHash(), newit);
511
586
  newit->idx_randomized = txns_randomized.size() - 1;
512
587
 
588
+ for (auto& vSPK : entry.mapSPK) {
589
+ const uint160& SPKKey = vSPK.first;
590
+ const MemPool_SPK_State& claims = vSPK.second;
591
+ if (claims & MSS_CREATED) {
592
+ mapUsedSPK[SPKKey].first = &tx;
593
+ }
594
+ if (claims & MSS_SPENT) {
595
+ mapUsedSPK[SPKKey].second = &tx;
596
+ }
597
+ }
598
+
513
599
  TRACEPOINT(mempool, added,
514
600
  entry.GetTx().GetHash().data(),
515
601
  entry.GetTxSize(),
@@ -538,6 +624,7 @@ void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason)
538
624
  std::chrono::duration_cast<std::chrono::duration<std::uint64_t>>(it->GetTime()).count()
539
625
  );
540
626
 
627
+ const CTransaction& tx = it->GetTx();
541
628
  for (const CTxIn& txin : it->GetTx().vin)
542
629
  mapNextTx.erase(txin.prevout);
543
630
 
@@ -555,6 +642,19 @@ void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason)
555
642
  txns_randomized.clear();
556
643
  }
557
644
 
645
+ for (auto& vSPK : it->mapSPK) {
646
+ const uint160& SPKKey = vSPK.first;
647
+ if (mapUsedSPK[SPKKey].first == &tx) {
648
+ mapUsedSPK[SPKKey].first = NULL;
649
+ }
650
+ if (mapUsedSPK[SPKKey].second == &tx) {
651
+ mapUsedSPK[SPKKey].second = NULL;
652
+ }
653
+ if (!(mapUsedSPK[SPKKey].first || mapUsedSPK[SPKKey].second)) {
654
+ mapUsedSPK.erase(SPKKey);
655
+ }
656
+ }
657
+
558
658
  totalTxSize -= it->GetTxSize();
559
659
  m_total_fee -= it->GetFee();
560
660
  cachedInnerUsage -= it->DynamicMemoryUsage();
@@ -671,6 +771,7 @@ void CTxMemPool::removeForBlock(const std::vector<CTransactionRef>& vtx, unsigne
671
771
  if (mapTx.size() || mapNextTx.size() || mapDeltas.size()) {
672
772
  txs_removed_for_block.reserve(vtx.size());
673
773
  for (const auto& tx : vtx) {
774
+ UpdateDependentPriorities(*tx, nBlockHeight, true);
674
775
  txiter it = mapTx.find(tx->GetHash());
675
776
  if (it != mapTx.end()) {
676
777
  setEntries stage;
@@ -689,6 +790,38 @@ void CTxMemPool::removeForBlock(const std::vector<CTransactionRef>& vtx, unsigne
689
790
  blockSinceLastRollingFeeBump = true;
690
791
  }
691
792
 
793
+ #ifndef BUILDING_FOR_LIBBITCOINKERNEL
794
+ void CTxMemPool::UpdateDynamicDustFeerate()
795
+ {
796
+ CFeeRate est_feerate{0};
797
+ if (m_opts.dust_relay_target < 0 && m_opts.estimator) {
798
+ static constexpr double target_success_threshold{0.8};
799
+ est_feerate = m_opts.estimator->estimateRawFee(-m_opts.dust_relay_target, target_success_threshold, FeeEstimateHorizon::LONG_HALFLIFE, nullptr);
800
+ } else if (m_opts.dust_relay_target > 0) {
801
+ auto bytes_remaining = int64_t{m_opts.dust_relay_target} * 1'000;
802
+ LOCK(cs);
803
+ for (auto mi = mapTx.get<ancestor_score>().begin(); mi != mapTx.get<ancestor_score>().end(); ++mi) {
804
+ bytes_remaining -= mi->GetTxSize();
805
+ if (bytes_remaining <= 0) {
806
+ est_feerate = CFeeRate(mi->GetFee(), mi->GetTxSize());
807
+ break;
808
+ }
809
+ }
810
+ }
811
+
812
+ est_feerate = (est_feerate * m_opts.dust_relay_multiplier) / 1'000;
813
+
814
+ if (est_feerate < m_opts.dust_relay_feerate_floor) {
815
+ est_feerate = m_opts.dust_relay_feerate_floor;
816
+ }
817
+
818
+ if (m_opts.dust_relay_feerate != est_feerate) {
819
+ LogDebug(BCLog::MEMPOOL, "Updating dust feerate to %s\n", est_feerate.ToString(FeeEstimateMode::SAT_VB));
820
+ m_opts.dust_relay_feerate = est_feerate;
821
+ }
822
+ }
823
+ #endif
824
+
692
825
  void CTxMemPool::check(const CCoinsViewCache& active_coins_tip, int64_t spendheight) const
693
826
  {
694
827
  if (m_opts.check_ratio == 0) return;
@@ -708,6 +841,14 @@ void CTxMemPool::check(const CCoinsViewCache& active_coins_tip, int64_t spendhei
708
841
 
709
842
  for (const auto& it : GetSortedDepthAndScore()) {
710
843
  checkTotal += it->GetTxSize();
844
+ const auto fresh_coin_age = GetCoinAge(it->GetTx(), active_coins_tip, spendheight);
845
+ const auto fresh_mod_vsize = CalculateModifiedSize(it->GetTx(), it->GetTxSize());
846
+ const double freshPriority = ComputePriority2(fresh_coin_age.inputs_coin_age, fresh_mod_vsize);
847
+ double cachePriority = it->GetPriority(spendheight);
848
+ double priDiff = cachePriority > freshPriority ? cachePriority - freshPriority : freshPriority - cachePriority;
849
+ // Verify that the difference between the on the fly calculation and a fresh calculation
850
+ // is small enough to be a result of double imprecision.
851
+ assert(priDiff < .0001 * freshPriority + 1);
711
852
  check_total_fee += it->GetFee();
712
853
  innerUsage += it->DynamicMemoryUsage();
713
854
  const CTransaction& tx = it->GetTx();
@@ -843,6 +984,21 @@ std::vector<CTxMemPool::indexed_transaction_set::const_iterator> CTxMemPool::Get
843
984
  return iters;
844
985
  }
845
986
 
987
+ void CTxMemPool::FindScriptPubKey(const std::set<CScript>& needles, std::map<COutPoint, Coin>& out_results) {
988
+ LOCK(cs);
989
+ for (const CTxMemPoolEntry& entry : mapTx) {
990
+ const CTransaction& tx = entry.GetTx();
991
+ const Txid& hash = tx.GetHash();
992
+ for (size_t txo_index = tx.vout.size(); txo_index > 0; ) {
993
+ --txo_index;
994
+ const CTxOut& txo = tx.vout[txo_index];
995
+ if (needles.count(txo.scriptPubKey)) {
996
+ out_results.emplace(COutPoint(hash, txo_index), Coin(txo, MEMPOOL_HEIGHT, false));
997
+ }
998
+ }
999
+ }
1000
+ }
1001
+
846
1002
  std::vector<CTxMemPoolEntryRef> CTxMemPool::entryAll() const
847
1003
  {
848
1004
  AssertLockHeld(cs);
@@ -885,15 +1041,17 @@ CTransactionRef CTxMemPool::get(const Txid& hash) const
885
1041
  return i->GetSharedTx();
886
1042
  }
887
1043
 
888
- void CTxMemPool::PrioritiseTransaction(const Txid& hash, const CAmount& nFeeDelta)
1044
+ void CTxMemPool::PrioritiseTransaction(const Txid& hash, double dPriorityDelta, const CAmount& nFeeDelta)
889
1045
  {
890
1046
  {
891
1047
  LOCK(cs);
892
- CAmount &delta = mapDeltas[hash];
893
- delta = SaturatingAdd(delta, nFeeDelta);
1048
+ std::pair<double, CAmount> &deltas = mapDeltas[hash];
1049
+ deltas.first += dPriorityDelta;
1050
+ deltas.second = SaturatingAdd(deltas.second, nFeeDelta);
894
1051
  txiter it = mapTx.find(hash);
895
1052
  if (it != mapTx.end()) {
896
1053
  mapTx.modify(it, [&nFeeDelta](CTxMemPoolEntry& e) { e.UpdateModifiedFee(nFeeDelta); });
1054
+
897
1055
  // Now update all ancestors' modified fees with descendants
898
1056
  auto ancestors{AssumeCalculateMemPoolAncestors(__func__, *it, Limits::NoLimits(), /*fSearchForParents=*/false)};
899
1057
  for (txiter ancestorIt : ancestors) {
@@ -908,27 +1066,29 @@ void CTxMemPool::PrioritiseTransaction(const Txid& hash, const CAmount& nFeeDelt
908
1066
  }
909
1067
  ++nTransactionsUpdated;
910
1068
  }
911
- if (delta == 0) {
1069
+ if (deltas.first == 0. && deltas.second == 0) {
912
1070
  mapDeltas.erase(hash);
913
1071
  LogPrintf("PrioritiseTransaction: %s (%sin mempool) delta cleared\n", hash.ToString(), it == mapTx.end() ? "not " : "");
914
1072
  } else {
915
- LogPrintf("PrioritiseTransaction: %s (%sin mempool) fee += %s, new delta=%s\n",
1073
+ LogPrintf("PrioritiseTransaction: %s (%sin mempool) priority += %f, fee += %s, new delta=%s\n",
916
1074
  hash.ToString(),
917
1075
  it == mapTx.end() ? "not " : "",
1076
+ dPriorityDelta,
918
1077
  FormatMoney(nFeeDelta),
919
- FormatMoney(delta));
1078
+ FormatMoney(deltas.second));
920
1079
  }
921
1080
  }
922
1081
  }
923
1082
 
924
- void CTxMemPool::ApplyDelta(const Txid& hash, CAmount &nFeeDelta) const
1083
+ void CTxMemPool::ApplyDeltas(const Txid& hash, double &dPriorityDelta, CAmount &nFeeDelta) const
925
1084
  {
926
1085
  AssertLockHeld(cs);
927
- std::map<Txid, CAmount>::const_iterator pos = mapDeltas.find(hash);
1086
+ const auto pos = mapDeltas.find(hash);
928
1087
  if (pos == mapDeltas.end())
929
1088
  return;
930
- const CAmount &delta = pos->second;
931
- nFeeDelta += delta;
1089
+ const std::pair<double, CAmount> &deltas = pos->second;
1090
+ dPriorityDelta += deltas.first;
1091
+ nFeeDelta += deltas.second;
932
1092
  }
933
1093
 
934
1094
  void CTxMemPool::ClearPrioritisation(const Txid& hash)
@@ -948,7 +1108,7 @@ std::vector<CTxMemPool::delta_info> CTxMemPool::GetPrioritisedTransactions() con
948
1108
  const bool in_mempool{iter != mapTx.end()};
949
1109
  std::optional<CAmount> modified_fee;
950
1110
  if (in_mempool) modified_fee = iter->GetModifiedFee();
951
- result.emplace_back(delta_info{in_mempool, delta, modified_fee, txid});
1111
+ result.emplace_back(delta_info{in_mempool, delta.second, delta.first, modified_fee, txid});
952
1112
  }
953
1113
  return result;
954
1114
  }
@@ -1045,7 +1205,7 @@ void CCoinsViewMemPool::Reset()
1045
1205
  size_t CTxMemPool::DynamicMemoryUsage() const {
1046
1206
  LOCK(cs);
1047
1207
  // Estimate the overhead of mapTx to be 15 pointers + an allocation, as no exact formula for boost::multi_index_contained is implemented.
1048
- return memusage::MallocUsage(sizeof(CTxMemPoolEntry) + 15 * sizeof(void*)) * mapTx.size() + memusage::DynamicUsage(mapNextTx) + memusage::DynamicUsage(mapDeltas) + memusage::DynamicUsage(txns_randomized) + cachedInnerUsage;
1208
+ return memusage::MallocUsage(sizeof(CTxMemPoolEntry) + 15 * sizeof(void*)) * mapTx.size() + memusage::DynamicUsage(mapNextTx) + memusage::DynamicUsage(mapDeltas) + memusage::DynamicUsage(mapUsedSPK) + memusage::DynamicUsage(txns_randomized) + cachedInnerUsage;
1049
1209
  }
1050
1210
 
1051
1211
  void CTxMemPool::RemoveUnbroadcastTx(const Txid& txid, const bool unchecked) {
@@ -1385,13 +1545,15 @@ util::Result<std::pair<std::vector<FeeFrac>, std::vector<FeeFrac>>> CTxMemPool::
1385
1545
  return std::make_pair(old_chunks, new_chunks);
1386
1546
  }
1387
1547
 
1388
- CTxMemPool::ChangeSet::TxHandle CTxMemPool::ChangeSet::StageAddition(const CTransactionRef& tx, const CAmount fee, int64_t time, unsigned int entry_height, uint64_t entry_sequence, bool spends_coinbase, int64_t sigops_cost, LockPoints lp)
1548
+ CTxMemPool::ChangeSet::TxHandle CTxMemPool::ChangeSet::StageAddition(const CTransactionRef& tx, const CAmount fee, int64_t time, unsigned int entry_height, uint64_t entry_sequence, const CoinAgeCache coin_age_cache, bool spends_coinbase, int32_t extra_weight, int64_t sigops_cost, LockPoints lp)
1389
1549
  {
1390
1550
  LOCK(m_pool->cs);
1391
1551
  Assume(m_to_add.find(tx->GetHash()) == m_to_add.end());
1392
- auto newit = m_to_add.emplace(tx, fee, time, entry_height, entry_sequence, spends_coinbase, sigops_cost, lp).first;
1552
+ auto newit = m_to_add.emplace(tx, fee, time, entry_height, entry_sequence, coin_age_cache, spends_coinbase, /*extra_weight=*/ extra_weight, /*sigops_cost=*/ sigops_cost, lp).first;
1553
+ double priority_delta{0.};
1393
1554
  CAmount delta{0};
1394
- m_pool->ApplyDelta(tx->GetHash(), delta);
1555
+ m_pool->ApplyDeltas(tx->GetHash(), priority_delta, delta);
1556
+ // NOTE: priority_delta is handled in addPriorityTxs
1395
1557
  if (delta) m_to_add.modify(newit, [&delta](CTxMemPoolEntry& e) { e.UpdateModifiedFee(delta); });
1396
1558
 
1397
1559
  m_entry_vec.push_back(newit);