Skip to content
Bitcoin Roots

v29.4-roots.2 · Commit 3: [PATCH 03/21] policy: restore conservative relay and network controls

src/txmempool.cpp

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

File metadata
index 3a5a3fb306d38669600ac395c58c68c10f27f787..dd76521e4e288b6aa428a29c64f021d9b2ef13fd 100644

Download original patch · Release notes

Diff rows 1–290 of 290. 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>
@@ -54,6 +59,13 @@ bool TestLockPointValidity(CChain& active_chain, const LockPoints& lp)
54
59
  return true;
55
60
  }
56
61
 
62
+ uint160 ScriptHashkey(const CScript& script)
63
+ {
64
+ uint160 hash;
65
+ CRIPEMD160().Write(script.data(), script.size()).Finalize(hash.begin());
66
+ return hash;
67
+ }
68
+
57
69
  void CTxMemPool::UpdateForDescendants(txiter updateIt, cacheMap& cachedDescendants,
58
70
  const std::set<uint256>& setExclude, std::set<uint256>& descendants_to_remove)
59
71
  {
@@ -404,7 +416,7 @@ void CTxMemPoolEntry::UpdateAncestorState(int32_t modifySize, CAmount modifyFee,
404
416
  static CTxMemPool::Options&& Flatten(CTxMemPool::Options&& opts, bilingual_str& error)
405
417
  {
406
418
  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;
419
+ int64_t descendant_limit_bytes = maxmempoolMinimumBytes(opts.limits.descendant_size_vbytes);
408
420
  if (opts.max_size_bytes < 0 || opts.max_size_bytes < descendant_limit_bytes) {
409
421
  error = strprintf(_("-maxmempool must be at least %d MB"), std::ceil(descendant_limit_bytes / 1'000'000.0));
410
422
  }
@@ -414,6 +426,17 @@ static CTxMemPool::Options&& Flatten(CTxMemPool::Options&& opts, bilingual_str&
414
426
  CTxMemPool::CTxMemPool(Options opts, bilingual_str& error)
415
427
  : m_opts{Flatten(std::move(opts), error)}
416
428
  {
429
+ Assert(m_opts.scheduler || !m_opts.dust_relay_target);
430
+ m_opts.dust_relay_feerate_floor = m_opts.dust_relay_feerate;
431
+ #ifdef BUILDING_FOR_LIBBITCOINKERNEL
432
+ assert(!m_opts.scheduler);
433
+ #else
434
+ if (m_opts.scheduler) {
435
+ m_opts.scheduler->scheduleEvery([this]{
436
+ UpdateDynamicDustFeerate();
437
+ }, DYNAMIC_DUST_FEERATE_UPDATE_INTERVAL);
438
+ }
439
+ #endif
417
440
  }
418
441
 
419
442
  bool CTxMemPool::isSpent(const COutPoint& outpoint) const
@@ -453,11 +476,20 @@ void CTxMemPool::Apply(ChangeSet* changeset)
453
476
  ancestors = *Assume(changeset->CalculateMemPoolAncestors(tx_entry, Limits::NoLimits()));
454
477
  }
455
478
  // First splice this entry into mapTx.
479
+ #if BOOST_VERSION >= 107400
456
480
  auto node_handle = changeset->m_to_add.extract(tx_entry);
457
481
  auto result = mapTx.insert(std::move(node_handle));
458
482
 
459
483
  Assume(result.inserted);
460
484
  txiter it = result.position;
485
+ #else
486
+ // Boost 1.73 didn't support node extraction, so we have to copy
487
+ auto result = mapTx.emplace(CTxMemPoolEntry::ExplicitCopy, *tx_entry);
488
+ changeset->m_to_add.erase(tx_entry);
489
+
490
+ Assume(result.second);
491
+ txiter it = result.first;
492
+ #endif
461
493
 
462
494
  // Now update the entry for ancestors/descendants.
463
495
  if (ancestors.has_value()) {
@@ -510,6 +542,17 @@ void CTxMemPool::addNewTransaction(CTxMemPool::txiter newit, CTxMemPool::setEntr
510
542
  txns_randomized.emplace_back(newit->GetSharedTx());
511
543
  newit->idx_randomized = txns_randomized.size() - 1;
512
544
 
545
+ for (auto& vSPK : entry.mapSPK) {
546
+ const uint160& SPKKey = vSPK.first;
547
+ const MemPool_SPK_State& claims = vSPK.second;
548
+ if (claims & MSS_CREATED) {
549
+ mapUsedSPK[SPKKey].first = &tx;
550
+ }
551
+ if (claims & MSS_SPENT) {
552
+ mapUsedSPK[SPKKey].second = &tx;
553
+ }
554
+ }
555
+
513
556
  TRACEPOINT(mempool, added,
514
557
  entry.GetTx().GetHash().data(),
515
558
  entry.GetTxSize(),
@@ -538,6 +581,7 @@ void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason)
538
581
  std::chrono::duration_cast<std::chrono::duration<std::uint64_t>>(it->GetTime()).count()
539
582
  );
540
583
 
584
+ const CTransaction& tx = it->GetTx();
541
585
  for (const CTxIn& txin : it->GetTx().vin)
542
586
  mapNextTx.erase(txin.prevout);
543
587
 
@@ -554,6 +598,19 @@ void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason)
554
598
  } else
555
599
  txns_randomized.clear();
556
600
 
601
+ for (auto& vSPK : it->mapSPK) {
602
+ const uint160& SPKKey = vSPK.first;
603
+ if (mapUsedSPK[SPKKey].first == &tx) {
604
+ mapUsedSPK[SPKKey].first = NULL;
605
+ }
606
+ if (mapUsedSPK[SPKKey].second == &tx) {
607
+ mapUsedSPK[SPKKey].second = NULL;
608
+ }
609
+ if (!(mapUsedSPK[SPKKey].first || mapUsedSPK[SPKKey].second)) {
610
+ mapUsedSPK.erase(SPKKey);
611
+ }
612
+ }
613
+
557
614
  totalTxSize -= it->GetTxSize();
558
615
  m_total_fee -= it->GetFee();
559
616
  cachedInnerUsage -= it->DynamicMemoryUsage();
@@ -669,9 +726,11 @@ void CTxMemPool::removeForBlock(const std::vector<CTransactionRef>& vtx, unsigne
669
726
  AssertLockHeld(cs);
670
727
  Assume(!m_have_changeset);
671
728
  std::vector<RemovedMempoolTransactionInfo> txs_removed_for_block;
729
+ if (mapTx.size() || mapNextTx.size() || mapDeltas.size()) {
672
730
  txs_removed_for_block.reserve(vtx.size());
673
731
  for (const auto& tx : vtx)
674
732
  {
733
+ UpdateDependentPriorities(*tx, nBlockHeight, true);
675
734
  txiter it = mapTx.find(tx->GetHash());
676
735
  if (it != mapTx.end()) {
677
736
  setEntries stage;
@@ -682,6 +741,7 @@ void CTxMemPool::removeForBlock(const std::vector<CTransactionRef>& vtx, unsigne
682
741
  removeConflicts(*tx);
683
742
  ClearPrioritisation(tx->GetHash());
684
743
  }
744
+ }
685
745
  if (m_opts.signals) {
686
746
  m_opts.signals->MempoolTransactionsRemovedForBlock(txs_removed_for_block, nBlockHeight);
687
747
  }
@@ -689,6 +749,38 @@ void CTxMemPool::removeForBlock(const std::vector<CTransactionRef>& vtx, unsigne
689
749
  blockSinceLastRollingFeeBump = true;
690
750
  }
691
751
 
752
+ #ifndef BUILDING_FOR_LIBBITCOINKERNEL
753
+ void CTxMemPool::UpdateDynamicDustFeerate()
754
+ {
755
+ CFeeRate est_feerate{0};
756
+ if (m_opts.dust_relay_target < 0 && m_opts.estimator) {
757
+ static constexpr double target_success_threshold{0.8};
758
+ est_feerate = m_opts.estimator->estimateRawFee(-m_opts.dust_relay_target, target_success_threshold, FeeEstimateHorizon::LONG_HALFLIFE, nullptr);
759
+ } else if (m_opts.dust_relay_target > 0) {
760
+ auto bytes_remaining = int64_t{m_opts.dust_relay_target} * 1'000;
761
+ LOCK(cs);
762
+ for (auto mi = mapTx.get<ancestor_score>().begin(); mi != mapTx.get<ancestor_score>().end(); ++mi) {
763
+ bytes_remaining -= mi->GetTxSize();
764
+ if (bytes_remaining <= 0) {
765
+ est_feerate = CFeeRate(mi->GetFee(), mi->GetTxSize());
766
+ break;
767
+ }
768
+ }
769
+ }
770
+
771
+ est_feerate = (est_feerate * m_opts.dust_relay_multiplier) / 1'000;
772
+
773
+ if (est_feerate < m_opts.dust_relay_feerate_floor) {
774
+ est_feerate = m_opts.dust_relay_feerate_floor;
775
+ }
776
+
777
+ if (m_opts.dust_relay_feerate != est_feerate) {
778
+ LogDebug(BCLog::MEMPOOL, "Updating dust feerate to %s\n", est_feerate.ToString(FeeEstimateMode::SAT_VB));
779
+ m_opts.dust_relay_feerate = est_feerate;
780
+ }
781
+ }
782
+ #endif
783
+
692
784
  void CTxMemPool::check(const CCoinsViewCache& active_coins_tip, int64_t spendheight) const
693
785
  {
694
786
  if (m_opts.check_ratio == 0) return;
@@ -708,6 +800,14 @@ void CTxMemPool::check(const CCoinsViewCache& active_coins_tip, int64_t spendhei
708
800
 
709
801
  for (const auto& it : GetSortedDepthAndScore()) {
710
802
  checkTotal += it->GetTxSize();
803
+ const auto fresh_coin_age = GetCoinAge(it->GetTx(), active_coins_tip, spendheight);
804
+ const auto fresh_mod_vsize = CalculateModifiedSize(it->GetTx(), it->GetTxSize());
805
+ const double freshPriority = ComputePriority2(fresh_coin_age.inputs_coin_age, fresh_mod_vsize);
806
+ double cachePriority = it->GetPriority(spendheight);
807
+ double priDiff = cachePriority > freshPriority ? cachePriority - freshPriority : freshPriority - cachePriority;
808
+ // Verify that the difference between the on the fly calculation and a fresh calculation
809
+ // is small enough to be a result of double imprecision.
810
+ assert(priDiff < .0001 * freshPriority + 1);
711
811
  check_total_fee += it->GetFee();
712
812
  innerUsage += it->DynamicMemoryUsage();
713
813
  const CTransaction& tx = it->GetTx();
@@ -844,6 +944,21 @@ std::vector<CTxMemPool::indexed_transaction_set::const_iterator> CTxMemPool::Get
844
944
  return iters;
845
945
  }
846
946
 
947
+ void CTxMemPool::FindScriptPubKey(const std::set<CScript>& needles, std::map<COutPoint, Coin>& out_results) {
948
+ LOCK(cs);
949
+ for (const CTxMemPoolEntry& entry : mapTx) {
950
+ const CTransaction& tx = entry.GetTx();
951
+ const Txid& hash = tx.GetHash();
952
+ for (size_t txo_index = tx.vout.size(); txo_index > 0; ) {
953
+ --txo_index;
954
+ const CTxOut& txo = tx.vout[txo_index];
955
+ if (needles.count(txo.scriptPubKey)) {
956
+ out_results.emplace(COutPoint(hash, txo_index), Coin(txo, MEMPOOL_HEIGHT, false));
957
+ }
958
+ }
959
+ }
960
+ }
961
+
847
962
  static TxMempoolInfo GetInfo(CTxMemPool::indexed_transaction_set::const_iterator it) {
848
963
  return TxMempoolInfo{it->GetSharedTx(), it->GetTime(), it->GetFee(), it->GetTxSize(), it->GetModifiedFee() - it->GetFee()};
849
964
  }
@@ -910,15 +1025,17 @@ TxMempoolInfo CTxMemPool::info_for_relay(const GenTxid& gtxid, uint64_t last_seq
910
1025
  }
911
1026
  }
912
1027
 
913
- void CTxMemPool::PrioritiseTransaction(const uint256& hash, const CAmount& nFeeDelta)
1028
+ void CTxMemPool::PrioritiseTransaction(const uint256& hash, double dPriorityDelta, const CAmount& nFeeDelta)
914
1029
  {
915
1030
  {
916
1031
  LOCK(cs);
917
- CAmount &delta = mapDeltas[hash];
918
- delta = SaturatingAdd(delta, nFeeDelta);
1032
+ std::pair<double, CAmount> &deltas = mapDeltas[hash];
1033
+ deltas.first += dPriorityDelta;
1034
+ deltas.second = SaturatingAdd(deltas.second, nFeeDelta);
919
1035
  txiter it = mapTx.find(hash);
920
1036
  if (it != mapTx.end()) {
921
1037
  mapTx.modify(it, [&nFeeDelta](CTxMemPoolEntry& e) { e.UpdateModifiedFee(nFeeDelta); });
1038
+
922
1039
  // Now update all ancestors' modified fees with descendants
923
1040
  auto ancestors{AssumeCalculateMemPoolAncestors(__func__, *it, Limits::NoLimits(), /*fSearchForParents=*/false)};
924
1041
  for (txiter ancestorIt : ancestors) {
@@ -933,27 +1050,29 @@ void CTxMemPool::PrioritiseTransaction(const uint256& hash, const CAmount& nFeeD
933
1050
  }
934
1051
  ++nTransactionsUpdated;
935
1052
  }
936
- if (delta == 0) {
1053
+ if (deltas.first == 0. && deltas.second == 0) {
937
1054
  mapDeltas.erase(hash);
938
1055
  LogPrintf("PrioritiseTransaction: %s (%sin mempool) delta cleared\n", hash.ToString(), it == mapTx.end() ? "not " : "");
939
1056
  } else {
940
- LogPrintf("PrioritiseTransaction: %s (%sin mempool) fee += %s, new delta=%s\n",
1057
+ LogPrintf("PrioritiseTransaction: %s (%sin mempool) priority += %f, fee += %s, new delta=%s\n",
941
1058
  hash.ToString(),
942
1059
  it == mapTx.end() ? "not " : "",
1060
+ dPriorityDelta,
943
1061
  FormatMoney(nFeeDelta),
944
- FormatMoney(delta));
1062
+ FormatMoney(deltas.second));
945
1063
  }
946
1064
  }
947
1065
  }
948
1066
 
949
- void CTxMemPool::ApplyDelta(const uint256& hash, CAmount &nFeeDelta) const
1067
+ void CTxMemPool::ApplyDeltas(const uint256& hash, double &dPriorityDelta, CAmount &nFeeDelta) const
950
1068
  {
951
1069
  AssertLockHeld(cs);
952
- std::map<uint256, CAmount>::const_iterator pos = mapDeltas.find(hash);
1070
+ std::map<uint256, std::pair<double, CAmount> >::const_iterator pos = mapDeltas.find(hash);
953
1071
  if (pos == mapDeltas.end())
954
1072
  return;
955
- const CAmount &delta = pos->second;
956
- nFeeDelta += delta;
1073
+ const std::pair<double, CAmount> &deltas = pos->second;
1074
+ dPriorityDelta += deltas.first;
1075
+ nFeeDelta += deltas.second;
957
1076
  }
958
1077
 
959
1078
  void CTxMemPool::ClearPrioritisation(const uint256& hash)
@@ -973,7 +1092,7 @@ std::vector<CTxMemPool::delta_info> CTxMemPool::GetPrioritisedTransactions() con
973
1092
  const bool in_mempool{iter != mapTx.end()};
974
1093
  std::optional<CAmount> modified_fee;
975
1094
  if (in_mempool) modified_fee = iter->GetModifiedFee();
976
- result.emplace_back(delta_info{in_mempool, delta, modified_fee, txid});
1095
+ result.emplace_back(delta_info{in_mempool, delta.second, delta.first, modified_fee, txid});
977
1096
  }
978
1097
  return result;
979
1098
  }
@@ -1403,13 +1522,15 @@ util::Result<std::pair<std::vector<FeeFrac>, std::vector<FeeFrac>>> CTxMemPool::
1403
1522
  return std::make_pair(old_chunks, new_chunks);
1404
1523
  }
1405
1524
 
1406
- 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)
1525
+ 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)
1407
1526
  {
1408
1527
  LOCK(m_pool->cs);
1409
1528
  Assume(m_to_add.find(tx->GetHash()) == m_to_add.end());
1410
- auto newit = m_to_add.emplace(tx, fee, time, entry_height, entry_sequence, spends_coinbase, sigops_cost, lp).first;
1529
+ 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;
1530
+ double priority_delta{0.};
1411
1531
  CAmount delta{0};
1412
- m_pool->ApplyDelta(tx->GetHash(), delta);
1532
+ m_pool->ApplyDeltas(tx->GetHash(), priority_delta, delta);
1533
+ // NOTE: priority_delta is handled in addPriorityTxs
1413
1534
  if (delta) m_to_add.modify(newit, [&delta](CTxMemPoolEntry& e) { e.UpdateModifiedFee(delta); });
1414
1535
 
1415
1536
  m_entry_vec.push_back(newit);