12
12
#include < vector>
13
13
#include < utility>
14
14
15
+ #include < random.h>
15
16
#include < util/feefrac.h>
16
17
#include < util/vecdeque.h>
17
18
@@ -225,6 +226,13 @@ struct SetInfo
225
226
return {transactions | txn, feerate + depgraph.FeeRate (txn - transactions)};
226
227
}
227
228
229
+ /* * Swap two SetInfo objects. */
230
+ friend void swap (SetInfo& a, SetInfo& b) noexcept
231
+ {
232
+ swap (a.transactions , b.transactions );
233
+ swap (a.feerate , b.feerate );
234
+ }
235
+
228
236
/* * Permit equality testing. */
229
237
friend bool operator ==(const SetInfo&, const SetInfo&) noexcept = default ;
230
238
};
@@ -356,6 +364,8 @@ class AncestorCandidateFinder
356
364
template <typename SetType>
357
365
class SearchCandidateFinder
358
366
{
367
+ /* * Internal RNG. */
368
+ InsecureRandomContext m_rng;
359
369
/* * Internal dependency graph for the cluster. */
360
370
const DepGraph<SetType>& m_depgraph;
361
371
/* * Which transactions are left to do (sorted indices). */
@@ -365,10 +375,12 @@ class SearchCandidateFinder
365
375
/* * Construct a candidate finder for a graph.
366
376
*
367
377
* @param[in] depgraph Dependency graph for the to-be-linearized cluster.
378
+ * @param[in] rng_seed A random seed to control the search order.
368
379
*
369
380
* Complexity: O(1).
370
381
*/
371
- SearchCandidateFinder (const DepGraph<SetType>& depgraph LIFETIMEBOUND) noexcept :
382
+ SearchCandidateFinder (const DepGraph<SetType>& depgraph LIFETIMEBOUND, uint64_t rng_seed) noexcept :
383
+ m_rng (rng_seed),
372
384
m_depgraph (depgraph),
373
385
m_todo (SetType::Fill(depgraph.TxCount())) {}
374
386
@@ -413,6 +425,13 @@ class SearchCandidateFinder
413
425
/* * Construct a new work item. */
414
426
WorkItem (SetInfo<SetType>&& i, SetType&& u) noexcept :
415
427
inc (std::move(i)), und(std::move(u)) {}
428
+
429
+ /* * Swap two WorkItems. */
430
+ void Swap (WorkItem& other) noexcept
431
+ {
432
+ swap (inc, other.inc );
433
+ swap (und, other.und );
434
+ }
416
435
};
417
436
418
437
/* * The queue of work items. */
@@ -493,9 +512,14 @@ class SearchCandidateFinder
493
512
// (BFS) corresponds to always taking from the front, which potentially uses more memory
494
513
// (up to exponential in the transaction count), but seems to work better in practice.
495
514
//
496
- // The approach here combines the two: use BFS until the queue grows too large, at which
497
- // point we temporarily switch to DFS until the size shrinks again.
515
+ // The approach here combines the two: use BFS (plus random swapping) until the queue grows
516
+ // too large, at which point we temporarily switch to DFS until the size shrinks again.
498
517
while (!queue.empty ()) {
518
+ // Randomly swap the first two items to randomize the search order.
519
+ if (queue.size () > 1 && m_rng.randbool ()) {
520
+ queue[0 ].Swap (queue[1 ]);
521
+ }
522
+
499
523
// Processing the first queue item, and then using DFS for everything it gives rise to,
500
524
// may increase the queue size by the number of undecided elements in there, minus 1
501
525
// for the first queue item being removed. Thus, only when that pushes the queue over
@@ -534,6 +558,9 @@ class SearchCandidateFinder
534
558
*
535
559
* @param[in] depgraph Dependency graph of the cluster to be linearized.
536
560
* @param[in] max_iterations Upper bound on the number of optimization steps that will be done.
561
+ * @param[in] rng_seed A random number seed to control search order. This prevents peers
562
+ * from predicting exactly which clusters would be hard for us to
563
+ * linearize.
537
564
* @return A pair of:
538
565
* - The resulting linearization.
539
566
* - A boolean indicating whether the result is guaranteed to be
@@ -542,15 +569,15 @@ class SearchCandidateFinder
542
569
* Complexity: O(N * min(max_iterations + N, 2^N)) where N=depgraph.TxCount().
543
570
*/
544
571
template <typename SetType>
545
- std::pair<std::vector<ClusterIndex>, bool > Linearize (const DepGraph<SetType>& depgraph, uint64_t max_iterations) noexcept
572
+ std::pair<std::vector<ClusterIndex>, bool > Linearize (const DepGraph<SetType>& depgraph, uint64_t max_iterations, uint64_t rng_seed ) noexcept
546
573
{
547
574
if (depgraph.TxCount () == 0 ) return {{}, true };
548
575
549
576
uint64_t iterations_left = max_iterations;
550
577
std::vector<ClusterIndex> linearization;
551
578
552
579
AncestorCandidateFinder anc_finder (depgraph);
553
- SearchCandidateFinder src_finder (depgraph);
580
+ SearchCandidateFinder src_finder (depgraph, rng_seed );
554
581
linearization.reserve (depgraph.TxCount ());
555
582
bool optimal = true ;
556
583
0 commit comments