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/node/miner.cpp

Before: src/node/miner.cpp · After: src/node/miner.cpp

File metadata
index 39f0879ce2c5edd2c7d48b242a4ee87c6f63d936..cc420370b914f2944d962b4a06dcc7cf3544ff2d 100644

Download original patch · Release notes

Diff rows 1–234 of 234. 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/node/miner.cpp CHANGED
@@ -28,6 +28,9 @@
28
28
  #include <validation.h>
29
29
 
30
30
  #include <algorithm>
31
+ #include <cassert>
32
+ #include <map>
33
+ #include <vector>
31
34
  #include <utility>
32
35
  #include <numeric>
33
36
 
@@ -80,6 +83,7 @@ static BlockAssembler::Options ClampOptions(BlockAssembler::Options options)
80
83
  {
81
84
  options.block_reserved_weight = std::clamp<size_t>(options.block_reserved_weight, MINIMUM_BLOCK_RESERVED_WEIGHT, MAX_BLOCK_WEIGHT);
82
85
  options.coinbase_output_max_additional_sigops = std::clamp<size_t>(options.coinbase_output_max_additional_sigops, 0, MAX_BLOCK_SIGOPS_COST);
86
+ options.nBlockMaxSize = std::clamp<size_t>(options.nBlockMaxSize, DEFAULT_BLOCK_RESERVED_SIZE, MAX_BLOCK_SERIALIZED_SIZE);
83
87
  // Limit weight to between block_reserved_weight and MAX_BLOCK_WEIGHT for sanity:
84
88
  // block_reserved_weight can safely exceed -blockmaxweight, but the rest of the block template will be empty.
85
89
  options.nBlockMaxWeight = std::clamp<size_t>(options.nBlockMaxWeight, options.block_reserved_weight, MAX_BLOCK_WEIGHT);
@@ -92,12 +96,131 @@ BlockAssembler::BlockAssembler(Chainstate& chainstate, const CTxMemPool* mempool
92
96
  m_chainstate{chainstate},
93
97
  m_options{ClampOptions(options)}
94
98
  {
99
+ fNeedSizeAccounting = m_options.nBlockMaxSize < MAX_BLOCK_SERIALIZED_SIZE;
100
+ }
101
+
102
+ namespace {
103
+ using TxCoinAgePriority = std::pair<double, CTxMemPool::txiter>;
104
+
105
+ struct TxCoinAgePriorityCompare {
106
+ bool operator()(const TxCoinAgePriority& a, const TxCoinAgePriority& b) const
107
+ {
108
+ if (a.first == b.first) {
109
+ return CompareTxMemPoolEntryByScore{}(*b.second, *a.second);
110
+ }
111
+ return a.first < b.first;
112
+ }
113
+ };
114
+ } // namespace
115
+
116
+ bool BlockAssembler::isStillDependent(const CTxMemPool& mempool, CTxMemPool::txiter iter)
117
+ {
118
+ assert(iter != mempool.mapTx.end());
119
+ for (const auto& parent : iter->GetMemPoolParentsConst()) {
120
+ const auto parent_it{mempool.mapTx.iterator_to(parent)};
121
+ if (!inBlock.count(parent_it)) return true;
122
+ }
123
+ return false;
124
+ }
125
+
126
+ bool BlockAssembler::TestForBlock(CTxMemPool::txiter iter)
127
+ {
128
+ const auto package_size{iter->GetSizeWithAncestors()};
129
+ const int64_t package_sigops{iter->GetSigOpCostWithAncestors()};
130
+ if (!TestPackage(package_size, package_sigops)) {
131
+ if (nBlockWeight > m_options.nBlockMaxWeight - 400 || nBlockSigOpsCost > MAX_BLOCK_SIGOPS_COST - 8 || lastFewTxs > 50) {
132
+ blockFinished = true;
133
+ return false;
134
+ }
135
+ if (nBlockWeight > m_options.nBlockMaxWeight - 4000) ++lastFewTxs;
136
+ return false;
137
+ }
138
+
139
+ CTxMemPool::setEntries package;
140
+ package.insert(iter);
141
+ if (!TestPackageTransactions(package)) {
142
+ if (nBlockSize > m_options.nBlockMaxSize - 100 || lastFewTxs > 50) {
143
+ blockFinished = true;
144
+ return false;
145
+ }
146
+ if (nBlockSize > m_options.nBlockMaxSize - 1000) ++lastFewTxs;
147
+ return false;
148
+ }
149
+ return true;
150
+ }
151
+
152
+ void BlockAssembler::addPriorityTxs(const CTxMemPool& mempool, int& packages_selected)
153
+ {
154
+ AssertLockHeld(mempool.cs);
155
+
156
+ uint64_t priority_size{static_cast<uint64_t>(gArgs.GetIntArg("-blockprioritysize", DEFAULT_BLOCK_PRIORITY_SIZE))};
157
+ priority_size = std::min<uint64_t>(priority_size, m_options.nBlockMaxSize);
158
+ if (priority_size == 0) return;
159
+
160
+ const bool previous_size_accounting{fNeedSizeAccounting};
161
+ fNeedSizeAccounting = true;
162
+
163
+ std::vector<TxCoinAgePriority> priorities;
164
+ std::map<CTxMemPool::txiter, double, CompareIteratorByHash> waiting;
165
+ priorities.reserve(mempool.mapTx.size());
166
+ for (auto iter{mempool.mapTx.begin()}; iter != mempool.mapTx.end(); ++iter) {
167
+ double priority{iter->GetPriority(nHeight)};
168
+ CAmount fee_delta{0};
169
+ mempool.ApplyDeltas(iter->GetTx().GetHash(), priority, fee_delta);
170
+ priorities.emplace_back(priority, iter);
171
+ }
172
+
173
+ TxCoinAgePriorityCompare compare;
174
+ std::make_heap(priorities.begin(), priorities.end(), compare);
175
+ while (!priorities.empty() && !blockFinished) {
176
+ const CTxMemPool::txiter iter{priorities.front().second};
177
+ const double priority{priorities.front().first};
178
+ std::pop_heap(priorities.begin(), priorities.end(), compare);
179
+ priorities.pop_back();
180
+
181
+ if (inBlock.count(iter)) {
182
+ assert(false);
183
+ continue;
184
+ }
185
+ if (isStillDependent(mempool, iter)) {
186
+ waiting.emplace(iter, priority);
187
+ continue;
188
+ }
189
+ if (!TestForBlock(iter)) continue;
190
+
191
+ AddToBlock(iter);
192
+ ++packages_selected;
193
+ if (nBlockSize >= priority_size || priority <= MINIMUM_TX_PRIORITY) break;
194
+
195
+ for (const auto& child : iter->GetMemPoolChildrenConst()) {
196
+ const auto child_iter{mempool.mapTx.iterator_to(child)};
197
+ const auto waiting_iter{waiting.find(child_iter)};
198
+ if (waiting_iter == waiting.end()) continue;
199
+ priorities.emplace_back(waiting_iter->second, child_iter);
200
+ std::push_heap(priorities.begin(), priorities.end(), compare);
201
+ waiting.erase(waiting_iter);
202
+ }
203
+ }
204
+ fNeedSizeAccounting = previous_size_accounting;
95
205
  }
96
206
 
97
207
  void ApplyArgsManOptions(const ArgsManager& args, BlockAssembler::Options& options)
98
208
  {
99
209
  // Block resource limits
100
- options.nBlockMaxWeight = args.GetIntArg("-blockmaxweight", options.nBlockMaxWeight);
210
+ // If only one resource limit is set, leave the other unrestricted. If
211
+ // neither is set, retain the conservative default weight limit.
212
+ bool weight_set{false};
213
+ if (args.IsArgSet("-blockmaxweight")) {
214
+ options.nBlockMaxWeight = args.GetIntArg("-blockmaxweight", DEFAULT_BLOCK_MAX_WEIGHT);
215
+ options.nBlockMaxSize = MAX_BLOCK_SERIALIZED_SIZE;
216
+ weight_set = true;
217
+ }
218
+ if (args.IsArgSet("-blockmaxsize")) {
219
+ options.nBlockMaxSize = args.GetIntArg("-blockmaxsize", DEFAULT_BLOCK_MAX_SIZE);
220
+ if (!weight_set) {
221
+ options.nBlockMaxWeight = MAX_BLOCK_WEIGHT;
222
+ }
223
+ }
101
224
  if (const auto blockmintxfee{args.GetArg("-blockmintxfee")}) {
102
225
  if (const auto parsed{ParseMoney(*blockmintxfee)}) options.blockMinFeeRate = CFeeRate{*parsed};
103
226
  }
@@ -110,6 +233,7 @@ void BlockAssembler::resetBlock()
110
233
  inBlock.clear();
111
234
 
112
235
  // Reserve space for fixed-size block header, txs count, and coinbase tx.
236
+ nBlockSize = 1000;
113
237
  nBlockWeight = m_options.block_reserved_weight;
114
238
  nBlockSigOpsCost = m_options.coinbase_output_max_additional_sigops;
115
239
 
@@ -149,7 +273,9 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock()
149
273
  int nPackagesSelected = 0;
150
274
  int nDescendantsUpdated = 0;
151
275
  if (m_mempool) {
152
- addPackageTxs(nPackagesSelected, nDescendantsUpdated);
276
+ LOCK(m_mempool->cs);
277
+ addPriorityTxs(*m_mempool, nPackagesSelected);
278
+ addPackageTxs(*m_mempool, nPackagesSelected, nDescendantsUpdated);
153
279
  }
154
280
 
155
281
  const auto time_1{SteadyClock::now()};
@@ -198,7 +324,7 @@ void BlockAssembler::onlyUnconfirmed(CTxMemPool::setEntries& testSet)
198
324
  {
199
325
  for (CTxMemPool::setEntries::iterator iit = testSet.begin(); iit != testSet.end(); ) {
200
326
  // Only test txs not already in the block
201
- if (inBlock.count((*iit)->GetSharedTx()->GetHash())) {
327
+ if (inBlock.count(*iit)) {
202
328
  testSet.erase(iit++);
203
329
  } else {
204
330
  iit++;
@@ -220,12 +346,21 @@ bool BlockAssembler::TestPackage(uint64_t packageSize, int64_t packageSigOpsCost
220
346
 
221
347
  // Perform transaction-level checks before adding to block:
222
348
  // - transaction finality (locktime)
349
+ // - serialized size when -blockmaxsize is in use
223
350
  bool BlockAssembler::TestPackageTransactions(const CTxMemPool::setEntries& package) const
224
351
  {
352
+ uint64_t potential_block_size{nBlockSize};
225
353
  for (CTxMemPool::txiter it : package) {
226
354
  if (!IsFinalTx(it->GetTx(), nHeight, m_lock_time_cutoff)) {
227
355
  return false;
228
356
  }
357
+ if (fNeedSizeAccounting) {
358
+ const uint64_t tx_size{::GetSerializeSize(TX_WITH_WITNESS(it->GetTx()))};
359
+ if (potential_block_size + tx_size >= m_options.nBlockMaxSize) {
360
+ return false;
361
+ }
362
+ potential_block_size += tx_size;
363
+ }
229
364
  }
230
365
  return true;
231
366
  }
@@ -236,10 +371,11 @@ void BlockAssembler::AddToBlock(CTxMemPool::txiter iter)
236
371
  pblocktemplate->vTxFees.push_back(iter->GetFee());
237
372
  pblocktemplate->vTxSigOpsCost.push_back(iter->GetSigOpCost());
238
373
  nBlockWeight += iter->GetTxWeight();
374
+ nBlockSize += GetSerializeSize(TX_WITH_WITNESS(iter->GetTx()));
239
375
  ++nBlockTx;
240
376
  nBlockSigOpsCost += iter->GetSigOpCost();
241
377
  nFees += iter->GetFee();
242
- inBlock.insert(iter->GetSharedTx()->GetHash());
378
+ inBlock.insert(iter);
243
379
 
244
380
  if (m_options.print_modified_fee) {
245
381
  LogPrintf("fee rate %s txid %s\n",
@@ -299,10 +435,9 @@ void BlockAssembler::SortForBlock(const CTxMemPool::setEntries& package, std::ve
299
435
  // Each time through the loop, we compare the best transaction in
300
436
  // mapModifiedTxs with the next transaction in the mempool to decide what
301
437
  // transaction package to work on next.
302
- void BlockAssembler::addPackageTxs(int& nPackagesSelected, int& nDescendantsUpdated)
438
+ void BlockAssembler::addPackageTxs(const CTxMemPool& mempool, int& nPackagesSelected, int& nDescendantsUpdated)
303
439
  {
304
- const auto& mempool{*Assert(m_mempool)};
305
- LOCK(mempool.cs);
440
+ AssertLockHeld(mempool.cs);
306
441
 
307
442
  // mapModifiedTx will store sorted packages after they are modified
308
443
  // because some of their txs are already in the block
@@ -337,7 +472,7 @@ void BlockAssembler::addPackageTxs(int& nPackagesSelected, int& nDescendantsUpda
337
472
  if (mi != mempool.mapTx.get<ancestor_score>().end()) {
338
473
  auto it = mempool.mapTx.project<0>(mi);
339
474
  assert(it != mempool.mapTx.end());
340
- if (mapModifiedTx.count(it) || inBlock.count(it->GetSharedTx()->GetHash()) || failedTx.count(it->GetSharedTx()->GetHash())) {
475
+ if (mapModifiedTx.count(it) || inBlock.count(it) || failedTx.count(it->GetSharedTx()->GetHash())) {
341
476
  ++mi;
342
477
  continue;
343
478
  }
@@ -371,7 +506,7 @@ void BlockAssembler::addPackageTxs(int& nPackagesSelected, int& nDescendantsUpda
371
506
 
372
507
  // We skip mapTx entries that are inBlock, and mapModifiedTx shouldn't
373
508
  // contain anything that is inBlock.
374
- assert(!inBlock.count(iter->GetSharedTx()->GetHash()));
509
+ assert(!inBlock.count(iter));
375
510
 
376
511
  uint64_t packageSize = iter->GetSizeWithAncestors();
377
512
  CAmount packageFees = iter->GetModFeesWithAncestors();