1212#include < vector>
1313#include < utility>
1414
15+ #include < random.h>
1516#include < util/feefrac.h>
1617#include < util/vecdeque.h>
1718
@@ -225,6 +226,13 @@ struct SetInfo
225226 return {transactions | txn, feerate + depgraph.FeeRate (txn - transactions)};
226227 }
227228
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+
228236 /* * Permit equality testing. */
229237 friend bool operator ==(const SetInfo&, const SetInfo&) noexcept = default ;
230238};
@@ -356,6 +364,8 @@ class AncestorCandidateFinder
356364template <typename SetType>
357365class SearchCandidateFinder
358366{
367+ /* * Internal RNG. */
368+ InsecureRandomContext m_rng;
359369 /* * Internal dependency graph for the cluster. */
360370 const DepGraph<SetType>& m_depgraph;
361371 /* * Which transactions are left to do (sorted indices). */
@@ -365,10 +375,12 @@ class SearchCandidateFinder
365375 /* * Construct a candidate finder for a graph.
366376 *
367377 * @param[in] depgraph Dependency graph for the to-be-linearized cluster.
378+ * @param[in] rng_seed A random seed to control the search order.
368379 *
369380 * Complexity: O(1).
370381 */
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),
372384 m_depgraph (depgraph),
373385 m_todo (SetType::Fill(depgraph.TxCount())) {}
374386
@@ -413,6 +425,13 @@ class SearchCandidateFinder
413425 /* * Construct a new work item. */
414426 WorkItem (SetInfo<SetType>&& i, SetType&& u) noexcept :
415427 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+ }
416435 };
417436
418437 /* * The queue of work items. */
@@ -493,9 +512,14 @@ class SearchCandidateFinder
493512 // (BFS) corresponds to always taking from the front, which potentially uses more memory
494513 // (up to exponential in the transaction count), but seems to work better in practice.
495514 //
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.
498517 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+
499523 // Processing the first queue item, and then using DFS for everything it gives rise to,
500524 // may increase the queue size by the number of undecided elements in there, minus 1
501525 // for the first queue item being removed. Thus, only when that pushes the queue over
@@ -534,6 +558,9 @@ class SearchCandidateFinder
534558 *
535559 * @param[in] depgraph Dependency graph of the cluster to be linearized.
536560 * @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.
537564 * @return A pair of:
538565 * - The resulting linearization.
539566 * - A boolean indicating whether the result is guaranteed to be
@@ -542,15 +569,15 @@ class SearchCandidateFinder
542569 * Complexity: O(N * min(max_iterations + N, 2^N)) where N=depgraph.TxCount().
543570 */
544571template <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
546573{
547574 if (depgraph.TxCount () == 0 ) return {{}, true };
548575
549576 uint64_t iterations_left = max_iterations;
550577 std::vector<ClusterIndex> linearization;
551578
552579 AncestorCandidateFinder anc_finder (depgraph);
553- SearchCandidateFinder src_finder (depgraph);
580+ SearchCandidateFinder src_finder (depgraph, rng_seed );
554581 linearization.reserve (depgraph.TxCount ());
555582 bool optimal = true ;
556583
0 commit comments