Skip to content
Bitcoin Roots

v31.1-roots.1 · Commit 3: [PATCH 03/17] feat(policy): preserve cluster accounting and repair dynamic dust

src/txmempool.cpp

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

File metadata
index bfff181683277273582787bd40b1700fae115ae8..e9456994b4cb2985a490596e2a444b04a1f513a3 100644

Download original patch · Release notes

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

+ Added− RemovedUnmarked lines are context.

Scroll the code area horizontally to read long lines. Before is on the left; after is on the right. Unified view fits narrow screens more easily.

src/txmempool.cpp CHANGED
@@ -37,6 +37,14 @@
37
  TRACEPOINT_SEMAPHORE(mempool, added);
38
  TRACEPOINT_SEMAPHORE(mempool, removed);
39
 
 
 
 
 
 
 
 
 
40
  bool TestLockPointValidity(CChain& active_chain, const LockPoints& lp)
41
  {
42
  AssertLockHeld(cs_main);
@@ -174,7 +182,7 @@ static CTxMemPool::Options&& Flatten(CTxMemPool::Options&& opts, bilingual_str&
174
  }
175
 
176
  CTxMemPool::CTxMemPool(Options opts, bilingual_str& error)
177
- : m_opts{Flatten(std::move(opts), error)}
178
  {
179
  m_txgraph = MakeTxGraph(
180
  /*max_cluster_count=*/m_opts.limits.cluster_count,
@@ -187,6 +195,43 @@ CTxMemPool::CTxMemPool(Options opts, bilingual_str& error)
187
  });
188
  }
189
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
190
  bool CTxMemPool::isSpent(const COutPoint& outpoint) const
191
  {
192
  LOCK(cs);
@@ -246,9 +291,17 @@ void CTxMemPool::addNewTransaction(CTxMemPool::txiter newit)
246
  // In that case, our disconnect block logic will call UpdateTransactionsFromBlock
247
  // to clean up the mess we're leaving here.
248
 
 
 
 
 
 
 
249
  nTransactionsUpdated++;
250
  totalTxSize += entry.GetTxSize();
251
  m_total_fee += entry.GetFee();
 
 
252
 
253
  txns_randomized.emplace_back(tx.GetWitnessHash(), newit);
254
  newit->idx_randomized = txns_randomized.size() - 1;
@@ -298,8 +351,17 @@ void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason)
298
  txns_randomized.clear();
299
  }
300
 
 
 
 
 
 
 
 
301
  totalTxSize -= it->GetTxSize();
302
  m_total_fee -= it->GetFee();
 
 
303
  cachedInnerUsage -= it->DynamicMemoryUsage();
304
  mapTx.erase(it);
305
  nTransactionsUpdated++;
@@ -442,6 +504,8 @@ void CTxMemPool::check(const CCoinsViewCache& active_coins_tip, int64_t spendhei
442
 
443
  uint64_t checkTotal = 0;
444
  CAmount check_total_fee{0};
 
 
445
  CAmount check_total_modified_fee{0};
446
  int64_t check_total_adjusted_weight{0};
447
  uint64_t innerUsage = 0;
@@ -477,6 +541,11 @@ void CTxMemPool::check(const CCoinsViewCache& active_coins_tip, int64_t spendhei
477
  check_total_adjusted_weight += it->GetAdjustedWeight();
478
  check_total_fee += it->GetFee();
479
  check_total_modified_fee += it->GetModifiedFee();
 
 
 
 
 
480
  innerUsage += it->DynamicMemoryUsage();
481
  const CTransaction& tx = it->GetTx();
482
 
@@ -548,6 +617,9 @@ void CTxMemPool::check(const CCoinsViewCache& active_coins_tip, int64_t spendhei
548
 
549
  assert(totalTxSize == checkTotal);
550
  assert(m_total_fee == check_total_fee);
 
 
 
551
  assert(diagram.back().fee == check_total_modified_fee);
552
  assert(diagram.back().size == check_total_adjusted_weight);
553
  assert(innerUsage == cachedInnerUsage);
@@ -627,13 +699,43 @@ CTransactionRef CTxMemPool::get(const Txid& hash) const
627
  return i->GetSharedTx();
628
  }
629
 
630
- void CTxMemPool::PrioritiseTransaction(const Txid& hash, const CAmount& nFeeDelta)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
631
  {
632
  {
633
  LOCK(cs);
 
 
 
 
 
 
 
634
  CAmount &delta = mapDeltas[hash];
635
  delta = SaturatingAdd(delta, nFeeDelta);
636
- txiter it = mapTx.find(hash);
637
  if (it != mapTx.end()) {
638
  // PrioritiseTransaction calls stack on previous ones. Set the new
639
  // transaction fee to be current modified fee + feedelta.
@@ -652,6 +754,7 @@ void CTxMemPool::PrioritiseTransaction(const Txid& hash, const CAmount& nFeeDelt
652
  FormatMoney(delta));
653
  }
654
  }
 
655
  }
656
 
657
  void CTxMemPool::ApplyDelta(const Txid& hash, CAmount &nFeeDelta) const
@@ -661,7 +764,7 @@ void CTxMemPool::ApplyDelta(const Txid& hash, CAmount &nFeeDelta) const
661
  if (pos == mapDeltas.end())
662
  return;
663
  const CAmount &delta = pos->second;
664
- nFeeDelta += delta;
665
  }
666
 
667
  void CTxMemPool::ClearPrioritisation(const Txid& hash)
@@ -778,7 +881,11 @@ void CCoinsViewMemPool::Reset()
778
  size_t CTxMemPool::DynamicMemoryUsage() const {
779
  LOCK(cs);
780
  // Estimate the overhead of mapTx to be 9 pointers (3 pointers per index) + an allocation, as no exact formula for boost::multi_index_contained is implemented.
781
- return memusage::MallocUsage(sizeof(CTxMemPoolEntry) + 9 * sizeof(void*)) * mapTx.size() + memusage::DynamicUsage(mapNextTx) + memusage::DynamicUsage(mapDeltas) + memusage::DynamicUsage(txns_randomized) + m_txgraph->GetMainMemoryUsage() + cachedInnerUsage;
 
 
 
 
782
  }
783
 
784
  void CTxMemPool::RemoveUnbroadcastTx(const Txid& txid, const bool unchecked) {
@@ -1002,7 +1109,7 @@ util::Result<std::pair<std::vector<FeeFrac>, std::vector<FeeFrac>>> CTxMemPool::
1002
  return m_pool->m_txgraph->GetMainStagingDiagrams();
1003
  }
1004
 
1005
- 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)
1006
  {
1007
  LOCK(m_pool->cs);
1008
  Assume(m_to_add.find(tx->GetHash()) == m_to_add.end());
@@ -1014,13 +1121,15 @@ CTxMemPool::ChangeSet::TxHandle CTxMemPool::ChangeSet::StageAddition(const CTran
1014
  CAmount delta{0};
1015
  m_pool->ApplyDelta(tx->GetHash(), delta);
1016
 
1017
- FeePerWeight feerate(fee, GetSigOpsAdjustedWeight(GetTransactionWeight(*tx), sigops_cost, ::nBytesPerSigOp));
1018
- auto newit = m_to_add.emplace(tx, fee, time, entry_height, entry_sequence, spends_coinbase, sigops_cost, lp).first;
 
 
 
 
 
 
1019
  m_pool->m_txgraph->AddTransaction(const_cast<CTxMemPoolEntry&>(*newit), feerate);
1020
- if (delta) {
1021
- newit->UpdateModifiedFee(delta);
1022
- m_pool->m_txgraph->SetTransactionFee(*newit, newit->GetModifiedFee());
1023
- }
1024
 
1025
  m_entry_vec.push_back(newit);
1026
 
@@ -1043,6 +1152,8 @@ void CTxMemPool::ChangeSet::Apply()
1043
  m_pool->Apply(this);
1044
  m_to_add.clear();
1045
  m_to_remove.clear();
 
 
1046
  m_entry_vec.clear();
1047
  m_ancestors.clear();
1048
  }
 
37
  TRACEPOINT_SEMAPHORE(mempool, added);
38
  TRACEPOINT_SEMAPHORE(mempool, removed);
39
 
40
+ namespace {
41
+ uint64_t AbsoluteFee(CAmount fee)
42
+ {
43
+ // Avoid negating INT64_MIN.
44
+ return fee < 0 ? static_cast<uint64_t>(-(fee + 1)) + 1 : static_cast<uint64_t>(fee);
45
+ }
46
+ }
47
+
48
  bool TestLockPointValidity(CChain& active_chain, const LockPoints& lp)
49
  {
50
  AssertLockHeld(cs_main);
 
182
  }
183
 
184
  CTxMemPool::CTxMemPool(Options opts, bilingual_str& error)
185
+ : m_dust_relay_feerate{opts.dust_relay_feerate}, m_opts{Flatten(std::move(opts), error)}
186
  {
187
  m_txgraph = MakeTxGraph(
188
  /*max_cluster_count=*/m_opts.limits.cluster_count,
 
195
  });
196
  }
197
 
198
+ CFeeRate CTxMemPool::GetDustRelayFee() const
199
+ {
200
+ LOCK(cs);
201
+ return m_dust_relay_feerate;
202
+ }
203
+
204
+ StandardnessOptions CTxMemPool::GetStandardnessOptions() const
205
+ {
206
+ LOCK(cs);
207
+ StandardnessOptions options{m_opts};
208
+ options.dust_relay_feerate = m_dust_relay_feerate;
209
+ return options;
210
+ }
211
+
212
+ std::optional<CFeeRate> CTxMemPool::EstimateDustFromMempool(int32_t target_kvb) const
213
+ {
214
+ Assume(target_kvb > 0);
215
+ int64_t remaining{int64_t{target_kvb} * 1000};
216
+ LOCK(cs);
217
+ for (const auto& entry : GetSortedScoreWithTopology()) {
218
+ remaining -= entry->GetTxSize();
219
+ if (remaining <= 0) return CFeeRate{entry->GetFee(), static_cast<int32_t>(entry->GetTxSize())};
220
+ }
221
+ return std::nullopt;
222
+ }
223
+
224
+ void CTxMemPool::UpdateDynamicDustFee(std::optional<CFeeRate> estimate)
225
+ {
226
+ if (m_opts.dust_relay_target == 0) return;
227
+ const CFeeRate fee{estimate ? std::max(ScaleDustFee(*estimate, m_opts.dust_relay_multiplier), m_opts.dust_relay_feerate) : m_opts.dust_relay_feerate};
228
+ LOCK(cs);
229
+ if (m_dust_relay_feerate != fee) {
230
+ LogDebug(BCLog::MEMPOOL, "Updating dust feerate to %s\n", fee.ToString(FeeRateFormat::SAT_VB));
231
+ m_dust_relay_feerate = fee;
232
+ }
233
+ }
234
+
235
  bool CTxMemPool::isSpent(const COutPoint& outpoint) const
236
  {
237
  LOCK(cs);
 
291
  // In that case, our disconnect block logic will call UpdateTransactionsFromBlock
292
  // to clean up the mess we're leaving here.
293
 
294
+ for (const auto& [script, claims] : entry.m_script_claims) {
295
+ auto& [created, spent] = m_used_scripts[script];
296
+ if (claims & 2) created.insert(tx.GetHash());
297
+ if (claims & 1) spent.insert(tx.GetHash());
298
+ }
299
+
300
  nTransactionsUpdated++;
301
  totalTxSize += entry.GetTxSize();
302
  m_total_fee += entry.GetFee();
303
+ m_modified_fee_usage += AbsoluteFee(entry.GetModifiedFee());
304
+ m_policy_weight_usage += entry.GetAdjustedWeight();
305
 
306
  txns_randomized.emplace_back(tx.GetWitnessHash(), newit);
307
  newit->idx_randomized = txns_randomized.size() - 1;
 
351
  txns_randomized.clear();
352
  }
353
 
354
+ for (const auto& [script, claims] : it->m_script_claims) {
355
+ auto used = m_used_scripts.find(script);
356
+ Assume(used != m_used_scripts.end());
357
+ used->second.first.erase(it->GetTx().GetHash());
358
+ used->second.second.erase(it->GetTx().GetHash());
359
+ if (used->second.first.empty() && used->second.second.empty()) m_used_scripts.erase(used);
360
+ }
361
  totalTxSize -= it->GetTxSize();
362
  m_total_fee -= it->GetFee();
363
+ m_modified_fee_usage -= AbsoluteFee(it->GetModifiedFee());
364
+ m_policy_weight_usage -= it->GetAdjustedWeight();
365
  cachedInnerUsage -= it->DynamicMemoryUsage();
366
  mapTx.erase(it);
367
  nTransactionsUpdated++;
 
504
 
505
  uint64_t checkTotal = 0;
506
  CAmount check_total_fee{0};
507
+ uint64_t check_absolute_fee{0};
508
+ std::map<CScript, std::pair<std::set<Txid>, std::set<Txid>>> check_scripts;
509
  CAmount check_total_modified_fee{0};
510
  int64_t check_total_adjusted_weight{0};
511
  uint64_t innerUsage = 0;
 
541
  check_total_adjusted_weight += it->GetAdjustedWeight();
542
  check_total_fee += it->GetFee();
543
  check_total_modified_fee += it->GetModifiedFee();
544
+ check_absolute_fee += AbsoluteFee(it->GetModifiedFee());
545
+ for (const auto& [script, claims] : it->m_script_claims) {
546
+ if (claims & 2) check_scripts[script].first.insert(it->GetTx().GetHash());
547
+ if (claims & 1) check_scripts[script].second.insert(it->GetTx().GetHash());
548
+ }
549
  innerUsage += it->DynamicMemoryUsage();
550
  const CTransaction& tx = it->GetTx();
551
 
 
617
 
618
  assert(totalTxSize == checkTotal);
619
  assert(m_total_fee == check_total_fee);
620
+ assert(m_modified_fee_usage == check_absolute_fee);
621
+ assert(m_policy_weight_usage == static_cast<uint64_t>(check_total_adjusted_weight));
622
+ assert(m_used_scripts == check_scripts);
623
  assert(diagram.back().fee == check_total_modified_fee);
624
  assert(diagram.back().size == check_total_adjusted_weight);
625
  assert(innerUsage == cachedInnerUsage);
 
699
  return i->GetSharedTx();
700
  }
701
 
702
+ std::set<Txid> CTxMemPool::GetScriptConflicts(const CScript& script, bool creating) const
703
+ {
704
+ AssertLockHeld(cs);
705
+ std::set<Txid> conflicts;
706
+ const auto it = m_used_scripts.find(script);
707
+ if (it != m_used_scripts.end()) {
708
+ conflicts = it->second.second;
709
+ if (creating) conflicts.insert(it->second.first.begin(), it->second.first.end());
710
+ }
711
+ return conflicts;
712
+ }
713
+
714
+ bool CTxMemPool::CanAddPolicyWeight(int64_t weight) const
715
+ {
716
+ AssertLockHeld(cs);
717
+ return weight > 0 && static_cast<uint64_t>(weight) <= uint64_t{INT32_MAX} - m_policy_weight_usage - m_staged_weight_usage;
718
+ }
719
+
720
+ bool CTxMemPool::CanAddModifiedFee(CAmount fee) const
721
+ {
722
+ AssertLockHeld(cs);
723
+ return AbsoluteFee(fee) <= uint64_t{INT64_MAX} - m_modified_fee_usage - m_staged_fee_usage;
724
+ }
725
+
726
+ bool CTxMemPool::PrioritiseTransaction(const Txid& hash, const CAmount& nFeeDelta)
727
  {
728
  {
729
  LOCK(cs);
730
+ txiter it = mapTx.find(hash);
731
+ if (it != mapTx.end()) {
732
+ const auto new_fee = SaturatingAdd(it->GetModifiedFee(), nFeeDelta);
733
+ const auto other_usage = m_modified_fee_usage - AbsoluteFee(it->GetModifiedFee()) + m_staged_fee_usage;
734
+ if (AbsoluteFee(new_fee) > uint64_t{INT64_MAX} - other_usage) return false;
735
+ m_modified_fee_usage = m_modified_fee_usage - AbsoluteFee(it->GetModifiedFee()) + AbsoluteFee(new_fee);
736
+ }
737
  CAmount &delta = mapDeltas[hash];
738
  delta = SaturatingAdd(delta, nFeeDelta);
 
739
  if (it != mapTx.end()) {
740
  // PrioritiseTransaction calls stack on previous ones. Set the new
741
  // transaction fee to be current modified fee + feedelta.
 
754
  FormatMoney(delta));
755
  }
756
  }
757
+ return true;
758
  }
759
 
760
  void CTxMemPool::ApplyDelta(const Txid& hash, CAmount &nFeeDelta) const
 
764
  if (pos == mapDeltas.end())
765
  return;
766
  const CAmount &delta = pos->second;
767
+ nFeeDelta = SaturatingAdd(nFeeDelta, delta);
768
  }
769
 
770
  void CTxMemPool::ClearPrioritisation(const Txid& hash)
 
881
  size_t CTxMemPool::DynamicMemoryUsage() const {
882
  LOCK(cs);
883
  // Estimate the overhead of mapTx to be 9 pointers (3 pointers per index) + an allocation, as no exact formula for boost::multi_index_contained is implemented.
884
+ size_t script_usage{memusage::DynamicUsage(m_used_scripts)};
885
+ for (const auto& [script, claims] : m_used_scripts) {
886
+ script_usage += RecursiveDynamicUsage(script) + memusage::DynamicUsage(claims.first) + memusage::DynamicUsage(claims.second);
887
+ }
888
+ return script_usage + memusage::MallocUsage(sizeof(CTxMemPoolEntry) + 9 * sizeof(void*)) * mapTx.size() + memusage::DynamicUsage(mapNextTx) + memusage::DynamicUsage(mapDeltas) + memusage::DynamicUsage(txns_randomized) + m_txgraph->GetMainMemoryUsage() + cachedInnerUsage;
889
  }
890
 
891
  void CTxMemPool::RemoveUnbroadcastTx(const Txid& txid, const bool unchecked) {
 
1109
  return m_pool->m_txgraph->GetMainStagingDiagrams();
1110
  }
1111
 
1112
+ 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, int32_t extra_weight, CAmount dust_penalty, std::map<CScript, unsigned> script_claims)
1113
  {
1114
  LOCK(m_pool->cs);
1115
  Assume(m_to_add.find(tx->GetHash()) == m_to_add.end());
 
1121
  CAmount delta{0};
1122
  m_pool->ApplyDelta(tx->GetHash(), delta);
1123
 
1124
+ auto newit = m_to_add.emplace(tx, fee, time, entry_height, entry_sequence, spends_coinbase, sigops_cost, lp, extra_weight, dust_penalty, std::move(script_claims)).first;
1125
+ newit->UpdateModifiedFee(delta);
1126
+ newit->UpdateModifiedFee(-dust_penalty);
1127
+ Assume(m_pool->CanAddPolicyWeight(newit->GetAdjustedWeight()));
1128
+ m_pool->m_staged_weight_usage += newit->GetAdjustedWeight();
1129
+ Assume(m_pool->CanAddModifiedFee(newit->GetModifiedFee()));
1130
+ m_pool->m_staged_fee_usage += AbsoluteFee(newit->GetModifiedFee());
1131
+ const FeePerWeight feerate{newit->GetModifiedFee(), static_cast<int32_t>(newit->GetAdjustedWeight())};
1132
  m_pool->m_txgraph->AddTransaction(const_cast<CTxMemPoolEntry&>(*newit), feerate);
 
 
 
 
1133
 
1134
  m_entry_vec.push_back(newit);
1135
 
 
1152
  m_pool->Apply(this);
1153
  m_to_add.clear();
1154
  m_to_remove.clear();
1155
+ m_pool->m_staged_fee_usage = 0;
1156
+ m_pool->m_staged_weight_usage = 0;
1157
  m_entry_vec.clear();
1158
  m_ancestors.clear();
1159
  }