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. The two line-number columns refer to before and after.
src/txmempool.cpp
CHANGED
|
@@ -37,6 +37,14 @@
|
|
|
37
37
|
TRACEPOINT_SEMAPHORE(mempool, added);
|
|
38
38
|
TRACEPOINT_SEMAPHORE(mempool, removed);
|
|
39
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
|
+
|
|
40
48
|
bool TestLockPointValidity(CChain& active_chain, const LockPoints& lp)
|
|
41
49
|
{
|
|
42
50
|
AssertLockHeld(cs_main);
|
|
@@ -174,7 +182,7 @@ static CTxMemPool::Options&& Flatten(CTxMemPool::Options&& opts, bilingual_str&
|
|
|
174
182
|
}
|
|
175
183
|
|
|
176
184
|
CTxMemPool::CTxMemPool(Options opts, bilingual_str& error)
|
|
177
|
-
: m_opts{Flatten(std::move(opts), error)}
|
|
185
|
+
: m_dust_relay_feerate{opts.dust_relay_feerate}, m_opts{Flatten(std::move(opts), error)}
|
|
178
186
|
{
|
|
179
187
|
m_txgraph = MakeTxGraph(
|
|
180
188
|
/*max_cluster_count=*/m_opts.limits.cluster_count,
|
|
@@ -187,6 +195,43 @@ CTxMemPool::CTxMemPool(Options opts, bilingual_str& error)
|
|
|
187
195
|
});
|
|
188
196
|
}
|
|
189
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
|
+
|
|
190
235
|
bool CTxMemPool::isSpent(const COutPoint& outpoint) const
|
|
191
236
|
{
|
|
192
237
|
LOCK(cs);
|
|
@@ -246,9 +291,17 @@ void CTxMemPool::addNewTransaction(CTxMemPool::txiter newit)
|
|
|
246
291
|
// In that case, our disconnect block logic will call UpdateTransactionsFromBlock
|
|
247
292
|
// to clean up the mess we're leaving here.
|
|
248
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
|
+
|
|
249
300
|
nTransactionsUpdated++;
|
|
250
301
|
totalTxSize += entry.GetTxSize();
|
|
251
302
|
m_total_fee += entry.GetFee();
|
|
303
|
+
m_modified_fee_usage += AbsoluteFee(entry.GetModifiedFee());
|
|
304
|
+
m_policy_weight_usage += entry.GetAdjustedWeight();
|
|
252
305
|
|
|
253
306
|
txns_randomized.emplace_back(tx.GetWitnessHash(), newit);
|
|
254
307
|
newit->idx_randomized = txns_randomized.size() - 1;
|
|
@@ -298,8 +351,17 @@ void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason)
|
|
|
298
351
|
txns_randomized.clear();
|
|
299
352
|
}
|
|
300
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
|
+
}
|
|
301
361
|
totalTxSize -= it->GetTxSize();
|
|
302
362
|
m_total_fee -= it->GetFee();
|
|
363
|
+
m_modified_fee_usage -= AbsoluteFee(it->GetModifiedFee());
|
|
364
|
+
m_policy_weight_usage -= it->GetAdjustedWeight();
|
|
303
365
|
cachedInnerUsage -= it->DynamicMemoryUsage();
|
|
304
366
|
mapTx.erase(it);
|
|
305
367
|
nTransactionsUpdated++;
|
|
@@ -442,6 +504,8 @@ void CTxMemPool::check(const CCoinsViewCache& active_coins_tip, int64_t spendhei
|
|
|
442
504
|
|
|
443
505
|
uint64_t checkTotal = 0;
|
|
444
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;
|
|
445
509
|
CAmount check_total_modified_fee{0};
|
|
446
510
|
int64_t check_total_adjusted_weight{0};
|
|
447
511
|
uint64_t innerUsage = 0;
|
|
@@ -477,6 +541,11 @@ void CTxMemPool::check(const CCoinsViewCache& active_coins_tip, int64_t spendhei
|
|
|
477
541
|
check_total_adjusted_weight += it->GetAdjustedWeight();
|
|
478
542
|
check_total_fee += it->GetFee();
|
|
479
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
|
+
}
|
|
480
549
|
innerUsage += it->DynamicMemoryUsage();
|
|
481
550
|
const CTransaction& tx = it->GetTx();
|
|
482
551
|
|
|
@@ -548,6 +617,9 @@ void CTxMemPool::check(const CCoinsViewCache& active_coins_tip, int64_t spendhei
|
|
|
548
617
|
|
|
549
618
|
assert(totalTxSize == checkTotal);
|
|
550
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);
|
|
551
623
|
assert(diagram.back().fee == check_total_modified_fee);
|
|
552
624
|
assert(diagram.back().size == check_total_adjusted_weight);
|
|
553
625
|
assert(innerUsage == cachedInnerUsage);
|
|
@@ -627,13 +699,43 @@ CTransactionRef CTxMemPool::get(const Txid& hash) const
|
|
|
627
699
|
return i->GetSharedTx();
|
|
628
700
|
}
|
|
629
701
|
|
|
630
|
-
void CTxMemPool::PrioritiseTransaction(const Txid& hash, const CAmount& nFeeDelta)
|
|
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)
|
|
631
727
|
{
|
|
632
728
|
{
|
|
633
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
|
+
}
|
|
634
737
|
CAmount &delta = mapDeltas[hash];
|
|
635
738
|
delta = SaturatingAdd(delta, nFeeDelta);
|
|
636
|
-
txiter it = mapTx.find(hash);
|
|
637
739
|
if (it != mapTx.end()) {
|
|
638
740
|
// PrioritiseTransaction calls stack on previous ones. Set the new
|
|
639
741
|
// transaction fee to be current modified fee + feedelta.
|
|
@@ -652,6 +754,7 @@ void CTxMemPool::PrioritiseTransaction(const Txid& hash, const CAmount& nFeeDelt
|
|
|
652
754
|
FormatMoney(delta));
|
|
653
755
|
}
|
|
654
756
|
}
|
|
757
|
+
return true;
|
|
655
758
|
}
|
|
656
759
|
|
|
657
760
|
void CTxMemPool::ApplyDelta(const Txid& hash, CAmount &nFeeDelta) const
|
|
@@ -661,7 +764,7 @@ void CTxMemPool::ApplyDelta(const Txid& hash, CAmount &nFeeDelta) const
|
|
|
661
764
|
if (pos == mapDeltas.end())
|
|
662
765
|
return;
|
|
663
766
|
const CAmount &delta = pos->second;
|
|
664
|
-
nFeeDelta += delta;
|
|
767
|
+
nFeeDelta = SaturatingAdd(nFeeDelta, delta);
|
|
665
768
|
}
|
|
666
769
|
|
|
667
770
|
void CTxMemPool::ClearPrioritisation(const Txid& hash)
|
|
@@ -778,7 +881,11 @@ void CCoinsViewMemPool::Reset()
|
|
|
778
881
|
size_t CTxMemPool::DynamicMemoryUsage() const {
|
|
779
882
|
LOCK(cs);
|
|
780
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.
|
|
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;
|
|
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;
|
|
782
889
|
}
|
|
783
890
|
|
|
784
891
|
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
1109
|
return m_pool->m_txgraph->GetMainStagingDiagrams();
|
|
1003
1110
|
}
|
|
1004
1111
|
|
|
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)
|
|
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)
|
|
1006
1113
|
{
|
|
1007
1114
|
LOCK(m_pool->cs);
|
|
1008
1115
|
Assume(m_to_add.find(tx->GetHash()) == m_to_add.end());
|
|
@@ -1014,13 +1121,15 @@ CTxMemPool::ChangeSet::TxHandle CTxMemPool::ChangeSet::StageAddition(const CTran
|
|
|
1014
1121
|
CAmount delta{0};
|
|
1015
1122
|
m_pool->ApplyDelta(tx->GetHash(), delta);
|
|
1016
1123
|
|
|
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;
|
|
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())};
|
|
1019
1132
|
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
1133
|
|
|
1025
1134
|
m_entry_vec.push_back(newit);
|
|
1026
1135
|
|
|
@@ -1043,6 +1152,8 @@ void CTxMemPool::ChangeSet::Apply()
|
|
|
1043
1152
|
m_pool->Apply(this);
|
|
1044
1153
|
m_to_add.clear();
|
|
1045
1154
|
m_to_remove.clear();
|
|
1155
|
+
m_pool->m_staged_fee_usage = 0;
|
|
1156
|
+
m_pool->m_staged_weight_usage = 0;
|
|
1046
1157
|
m_entry_vec.clear();
|
|
1047
1158
|
m_ancestors.clear();
|
|
1048
1159
|
}
|