Bitcoin Core  28.1.0
P2P Digital Currency
cluster_linearize.h
Go to the documentation of this file.
1 // Copyright (c) The Bitcoin Core developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 
5 #ifndef BITCOIN_CLUSTER_LINEARIZE_H
6 #define BITCOIN_CLUSTER_LINEARIZE_H
7 
8 #include <algorithm>
9 #include <numeric>
10 #include <optional>
11 #include <stdint.h>
12 #include <vector>
13 #include <utility>
14 
15 #include <random.h>
16 #include <span.h>
17 #include <util/feefrac.h>
18 #include <util/vecdeque.h>
19 
20 namespace cluster_linearize {
21 
27 template<typename SetType>
28 using Cluster = std::vector<std::pair<FeeFrac, SetType>>;
29 
31 using ClusterIndex = uint32_t;
32 
35 template<typename SetType>
36 class DepGraph
37 {
39  struct Entry
40  {
44  SetType ancestors;
46  SetType descendants;
47 
49  friend bool operator==(const Entry&, const Entry&) noexcept = default;
50 
52  Entry() noexcept = default;
54  Entry(const FeeFrac& f, const SetType& a, const SetType& d) noexcept : feerate(f), ancestors(a), descendants(d) {}
55  };
56 
58  std::vector<Entry> entries;
59 
60 public:
62  friend bool operator==(const DepGraph&, const DepGraph&) noexcept = default;
63 
64  // Default constructors.
65  DepGraph() noexcept = default;
66  DepGraph(const DepGraph&) noexcept = default;
67  DepGraph(DepGraph&&) noexcept = default;
68  DepGraph& operator=(const DepGraph&) noexcept = default;
69  DepGraph& operator=(DepGraph&&) noexcept = default;
70 
75  explicit DepGraph(ClusterIndex ntx) noexcept
76  {
77  Assume(ntx <= SetType::Size());
78  entries.resize(ntx);
79  for (ClusterIndex i = 0; i < ntx; ++i) {
80  entries[i].ancestors = SetType::Singleton(i);
81  entries[i].descendants = SetType::Singleton(i);
82  }
83  }
84 
89  explicit DepGraph(const Cluster<SetType>& cluster) noexcept : entries(cluster.size())
90  {
91  for (ClusterIndex i = 0; i < cluster.size(); ++i) {
92  // Fill in fee and size.
93  entries[i].feerate = cluster[i].first;
94  // Fill in direct parents as ancestors.
95  entries[i].ancestors = cluster[i].second;
96  // Make sure transactions are ancestors of themselves.
97  entries[i].ancestors.Set(i);
98  }
99 
100  // Propagate ancestor information.
101  for (ClusterIndex i = 0; i < entries.size(); ++i) {
102  // At this point, entries[a].ancestors[b] is true iff b is an ancestor of a and there
103  // is a path from a to b through the subgraph consisting of {a, b} union
104  // {0, 1, ..., (i-1)}.
105  SetType to_merge = entries[i].ancestors;
106  for (ClusterIndex j = 0; j < entries.size(); ++j) {
107  if (entries[j].ancestors[i]) {
108  entries[j].ancestors |= to_merge;
109  }
110  }
111  }
112 
113  // Fill in descendant information by transposing the ancestor information.
114  for (ClusterIndex i = 0; i < entries.size(); ++i) {
115  for (auto j : entries[i].ancestors) {
116  entries[j].descendants.Set(i);
117  }
118  }
119  }
120 
122  auto TxCount() const noexcept { return entries.size(); }
124  const FeeFrac& FeeRate(ClusterIndex i) const noexcept { return entries[i].feerate; }
126  FeeFrac& FeeRate(ClusterIndex i) noexcept { return entries[i].feerate; }
128  const SetType& Ancestors(ClusterIndex i) const noexcept { return entries[i].ancestors; }
130  const SetType& Descendants(ClusterIndex i) const noexcept { return entries[i].descendants; }
131 
137  ClusterIndex AddTransaction(const FeeFrac& feefrac) noexcept
138  {
139  Assume(TxCount() < SetType::Size());
140  ClusterIndex new_idx = TxCount();
141  entries.emplace_back(feefrac, SetType::Singleton(new_idx), SetType::Singleton(new_idx));
142  return new_idx;
143  }
144 
149  void AddDependency(ClusterIndex parent, ClusterIndex child) noexcept
150  {
151  // Bail out if dependency is already implied.
152  if (entries[child].ancestors[parent]) return;
153  // To each ancestor of the parent, add as descendants the descendants of the child.
154  const auto& chl_des = entries[child].descendants;
155  for (auto anc_of_par : Ancestors(parent)) {
156  entries[anc_of_par].descendants |= chl_des;
157  }
158  // To each descendant of the child, add as ancestors the ancestors of the parent.
159  const auto& par_anc = entries[parent].ancestors;
160  for (auto dec_of_chl : Descendants(child)) {
161  entries[dec_of_chl].ancestors |= par_anc;
162  }
163  }
164 
169  FeeFrac FeeRate(const SetType& elems) const noexcept
170  {
171  FeeFrac ret;
172  for (auto pos : elems) ret += entries[pos].feerate;
173  return ret;
174  }
175 
188  SetType FindConnectedComponent(const SetType& todo) const noexcept
189  {
190  if (todo.None()) return todo;
191  auto to_add = SetType::Singleton(todo.First());
192  SetType ret;
193  do {
194  SetType old = ret;
195  for (auto add : to_add) {
196  ret |= Descendants(add);
197  ret |= Ancestors(add);
198  }
199  ret &= todo;
200  to_add = ret - old;
201  } while (to_add.Any());
202  return ret;
203  }
204 
209  bool IsConnected(const SetType& subset) const noexcept
210  {
211  return FindConnectedComponent(subset) == subset;
212  }
213 
218  bool IsConnected() const noexcept { return IsConnected(SetType::Fill(TxCount())); }
219 
224  void AppendTopo(std::vector<ClusterIndex>& list, const SetType& select) const noexcept
225  {
226  ClusterIndex old_len = list.size();
227  for (auto i : select) list.push_back(i);
228  std::sort(list.begin() + old_len, list.end(), [&](ClusterIndex a, ClusterIndex b) noexcept {
229  const auto a_anc_count = entries[a].ancestors.Count();
230  const auto b_anc_count = entries[b].ancestors.Count();
231  if (a_anc_count != b_anc_count) return a_anc_count < b_anc_count;
232  return a < b;
233  });
234  }
235 };
236 
238 template<typename SetType>
239 struct SetInfo
240 {
242  SetType transactions;
245 
247  SetInfo() noexcept = default;
248 
250  SetInfo(const SetType& txn, const FeeFrac& fr) noexcept : transactions(txn), feerate(fr) {}
251 
253  explicit SetInfo(const DepGraph<SetType>& depgraph, ClusterIndex pos) noexcept :
254  transactions(SetType::Singleton(pos)), feerate(depgraph.FeeRate(pos)) {}
255 
257  explicit SetInfo(const DepGraph<SetType>& depgraph, const SetType& txn) noexcept :
258  transactions(txn), feerate(depgraph.FeeRate(txn)) {}
259 
261  SetInfo& operator|=(const SetInfo& other) noexcept
262  {
263  Assume(!transactions.Overlaps(other.transactions));
264  transactions |= other.transactions;
265  feerate += other.feerate;
266  return *this;
267  }
268 
271  [[nodiscard]] SetInfo Add(const DepGraph<SetType>& depgraph, const SetType& txn) const noexcept
272  {
273  return {transactions | txn, feerate + depgraph.FeeRate(txn - transactions)};
274  }
275 
277  friend void swap(SetInfo& a, SetInfo& b) noexcept
278  {
279  swap(a.transactions, b.transactions);
280  swap(a.feerate, b.feerate);
281  }
282 
284  friend bool operator==(const SetInfo&, const SetInfo&) noexcept = default;
285 };
286 
288 template<typename SetType>
289 std::vector<FeeFrac> ChunkLinearization(const DepGraph<SetType>& depgraph, Span<const ClusterIndex> linearization) noexcept
290 {
291  std::vector<FeeFrac> ret;
292  for (ClusterIndex i : linearization) {
294  auto new_chunk = depgraph.FeeRate(i);
295  // As long as the new chunk has a higher feerate than the last chunk so far, absorb it.
296  while (!ret.empty() && new_chunk >> ret.back()) {
297  new_chunk += ret.back();
298  ret.pop_back();
299  }
300  // Actually move that new chunk into the chunking.
301  ret.push_back(std::move(new_chunk));
302  }
303  return ret;
304 }
305 
307 template<typename SetType>
309 {
312 
315 
317  std::vector<SetInfo<SetType>> m_chunks;
318 
321 
323  SetType m_todo;
324 
326  void BuildChunks() noexcept
327  {
328  // Caller must clear m_chunks.
329  Assume(m_chunks.empty());
330 
331  // Chop off the initial part of m_linearization that is already done.
332  while (!m_linearization.empty() && !m_todo[m_linearization.front()]) {
334  }
335 
336  // Iterate over the remaining entries in m_linearization. This is effectively the same
337  // algorithm as ChunkLinearization, but supports skipping parts of the linearization and
338  // keeps track of the sets themselves instead of just their feerates.
339  for (auto idx : m_linearization) {
340  if (!m_todo[idx]) continue;
341  // Start with an initial chunk containing just element idx.
342  SetInfo add(m_depgraph, idx);
343  // Absorb existing final chunks into add while they have lower feerate.
344  while (!m_chunks.empty() && add.feerate >> m_chunks.back().feerate) {
345  add |= m_chunks.back();
346  m_chunks.pop_back();
347  }
348  // Remember new chunk.
349  m_chunks.push_back(std::move(add));
350  }
351  }
352 
353 public:
356  m_depgraph(depgraph), m_linearization(lin)
357  {
358  // Mark everything in lin as todo still.
359  for (auto i : m_linearization) m_todo.Set(i);
360  // Compute the initial chunking.
361  m_chunks.reserve(depgraph.TxCount());
362  BuildChunks();
363  }
364 
366  ClusterIndex NumChunksLeft() const noexcept { return m_chunks.size() - m_chunks_skip; }
367 
369  const SetInfo<SetType>& GetChunk(ClusterIndex n) const noexcept
370  {
371  Assume(n + m_chunks_skip < m_chunks.size());
372  return m_chunks[n + m_chunks_skip];
373  }
374 
376  void MarkDone(SetType subset) noexcept
377  {
378  Assume(subset.Any());
379  Assume(subset.IsSubsetOf(m_todo));
380  m_todo -= subset;
381  if (GetChunk(0).transactions == subset) {
382  // If the newly done transactions exactly match the first chunk of the remainder of
383  // the linearization, we do not need to rechunk; just remember to skip one
384  // additional chunk.
385  ++m_chunks_skip;
386  // With subset marked done, some prefix of m_linearization will be done now. How long
387  // that prefix is depends on how many done elements were interspersed with subset,
388  // but at least as many transactions as there are in subset.
389  m_linearization = m_linearization.subspan(subset.Count());
390  } else {
391  // Otherwise rechunk what remains of m_linearization.
392  m_chunks.clear();
393  m_chunks_skip = 0;
394  BuildChunks();
395  }
396  }
397 
408  {
409  Assume(subset.transactions.IsSubsetOf(m_todo));
410  SetInfo<SetType> accumulator;
411  // Iterate over all chunks of the remaining linearization.
412  for (ClusterIndex i = 0; i < NumChunksLeft(); ++i) {
413  // Find what (if any) intersection the chunk has with subset.
414  const SetType to_add = GetChunk(i).transactions & subset.transactions;
415  if (to_add.Any()) {
416  // If adding that to accumulator makes us hit all of subset, we are done as no
417  // shorter intersection with higher/equal feerate exists.
418  accumulator.transactions |= to_add;
419  if (accumulator.transactions == subset.transactions) break;
420  // Otherwise update the accumulator feerate.
421  accumulator.feerate += m_depgraph.FeeRate(to_add);
422  // If that does result in something better, or something with the same feerate but
423  // smaller, return that. Even if a longer, higher-feerate intersection exists, it
424  // does not hurt to return the shorter one (the remainder of the longer intersection
425  // will generally be found in the next call to Intersect, but even if not, it is not
426  // required for the improvement guarantee this function makes).
427  if (!(accumulator.feerate << subset.feerate)) return accumulator;
428  }
429  }
430  return subset;
431  }
432 };
433 
443 template<typename SetType>
445 {
449  SetType m_todo;
451  std::vector<FeeFrac> m_ancestor_set_feerates;
452 
453 public:
459  m_depgraph(depgraph),
460  m_todo{SetType::Fill(depgraph.TxCount())},
461  m_ancestor_set_feerates(depgraph.TxCount())
462  {
463  // Precompute ancestor-set feerates.
464  for (ClusterIndex i = 0; i < depgraph.TxCount(); ++i) {
466  SetType anc_to_add = m_depgraph.Ancestors(i);
467  FeeFrac anc_feerate;
468  // Reuse accumulated feerate from first ancestor, if usable.
469  Assume(anc_to_add.Any());
470  ClusterIndex first = anc_to_add.First();
471  if (first < i) {
472  anc_feerate = m_ancestor_set_feerates[first];
473  Assume(!anc_feerate.IsEmpty());
474  anc_to_add -= m_depgraph.Ancestors(first);
475  }
476  // Add in other ancestors (which necessarily include i itself).
477  Assume(anc_to_add[i]);
478  anc_feerate += m_depgraph.FeeRate(anc_to_add);
479  // Store the result.
480  m_ancestor_set_feerates[i] = anc_feerate;
481  }
482  }
483 
490  void MarkDone(SetType select) noexcept
491  {
492  Assume(select.Any());
493  Assume(select.IsSubsetOf(m_todo));
494  m_todo -= select;
495  for (auto i : select) {
496  auto feerate = m_depgraph.FeeRate(i);
497  for (auto j : m_depgraph.Descendants(i) & m_todo) {
498  m_ancestor_set_feerates[j] -= feerate;
499  }
500  }
501  }
502 
504  bool AllDone() const noexcept
505  {
506  return m_todo.None();
507  }
508 
515  {
516  Assume(!AllDone());
517  std::optional<ClusterIndex> best;
518  for (auto i : m_todo) {
519  if (best.has_value()) {
520  Assume(!m_ancestor_set_feerates[i].IsEmpty());
521  if (!(m_ancestor_set_feerates[i] > m_ancestor_set_feerates[*best])) continue;
522  }
523  best = i;
524  }
525  Assume(best.has_value());
526  return {m_depgraph.Ancestors(*best) & m_todo, m_ancestor_set_feerates[*best]};
527  }
528 };
529 
539 template<typename SetType>
541 {
547  SetType m_todo;
548 
549 public:
557  SearchCandidateFinder(const DepGraph<SetType>& depgraph LIFETIMEBOUND, uint64_t rng_seed) noexcept :
558  m_rng(rng_seed),
559  m_depgraph(depgraph),
560  m_todo(SetType::Fill(depgraph.TxCount())) {}
561 
563  bool AllDone() const noexcept
564  {
565  return m_todo.None();
566  }
567 
585  std::pair<SetInfo<SetType>, uint64_t> FindCandidateSet(uint64_t max_iterations, SetInfo<SetType> best) noexcept
586  {
587  Assume(!AllDone());
588 
590  struct WorkItem
591  {
595  SetInfo<SetType> inc;
598  SetType und;
599 
601  WorkItem(SetInfo<SetType>&& i, SetType&& u) noexcept :
602  inc(std::move(i)), und(std::move(u)) {}
603 
605  void Swap(WorkItem& other) noexcept
606  {
607  swap(inc, other.inc);
608  swap(und, other.und);
609  }
610  };
611 
613  VecDeque<WorkItem> queue;
614  queue.reserve(std::max<size_t>(256, 2 * m_todo.Count()));
615 
616  // Create an initial entry with m_todo as undecided. Also use it as best if not provided,
617  // so that during the work processing loop below, and during the add_fn/split_fn calls, we
618  // do not need to deal with the best=empty case.
619  if (best.feerate.IsEmpty()) best = SetInfo(m_depgraph, m_todo);
620  queue.emplace_back(SetInfo<SetType>{}, SetType{m_todo});
621 
623  uint64_t iterations_left = max_iterations;
624 
631  auto add_fn = [&](SetInfo<SetType> inc, SetType und) noexcept {
632  if (!inc.feerate.IsEmpty()) {
633  // If inc's feerate is better than best's, remember it as our new best.
634  if (inc.feerate > best.feerate) {
635  best = inc;
636  }
637  } else {
638  Assume(inc.transactions.None());
639  }
640 
641  // Make sure there are undecided transactions left to split on.
642  if (und.None()) return;
643 
644  // Actually construct a new work item on the queue. Due to the switch to DFS when queue
645  // space runs out (see below), we know that no reallocation of the queue should ever
646  // occur.
647  Assume(queue.size() < queue.capacity());
648  queue.emplace_back(std::move(inc), std::move(und));
649  };
650 
654  auto split_fn = [&](WorkItem&& elem) noexcept {
655  // Any queue element must have undecided transactions left, otherwise there is nothing
656  // to explore anymore.
657  Assume(elem.und.Any());
658  // The included and undecided set are all subsets of m_todo.
659  Assume(elem.inc.transactions.IsSubsetOf(m_todo) && elem.und.IsSubsetOf(m_todo));
660  // Included transactions cannot be undecided.
661  Assume(!elem.inc.transactions.Overlaps(elem.und));
662 
663  // Pick the first undecided transaction as the one to split on.
664  const ClusterIndex split = elem.und.First();
665 
666  // Add a work item corresponding to exclusion of the split transaction.
667  const auto& desc = m_depgraph.Descendants(split);
668  add_fn(/*inc=*/elem.inc,
669  /*und=*/elem.und - desc);
670 
671  // Add a work item corresponding to inclusion of the split transaction.
672  const auto anc = m_depgraph.Ancestors(split) & m_todo;
673  add_fn(/*inc=*/elem.inc.Add(m_depgraph, anc),
674  /*und=*/elem.und - anc);
675 
676  // Account for the performed split.
677  --iterations_left;
678  };
679 
680  // Work processing loop.
681  //
682  // New work items are always added at the back of the queue, but items to process use a
683  // hybrid approach where they can be taken from the front or the back.
684  //
685  // Depth-first search (DFS) corresponds to always taking from the back of the queue. This
686  // is very memory-efficient (linear in the number of transactions). Breadth-first search
687  // (BFS) corresponds to always taking from the front, which potentially uses more memory
688  // (up to exponential in the transaction count), but seems to work better in practice.
689  //
690  // The approach here combines the two: use BFS (plus random swapping) until the queue grows
691  // too large, at which point we temporarily switch to DFS until the size shrinks again.
692  while (!queue.empty()) {
693  // Randomly swap the first two items to randomize the search order.
694  if (queue.size() > 1 && m_rng.randbool()) {
695  queue[0].Swap(queue[1]);
696  }
697 
698  // Processing the first queue item, and then using DFS for everything it gives rise to,
699  // may increase the queue size by the number of undecided elements in there, minus 1
700  // for the first queue item being removed. Thus, only when that pushes the queue over
701  // its capacity can we not process from the front (BFS), and should we use DFS.
702  while (queue.size() - 1 + queue.front().und.Count() > queue.capacity()) {
703  if (!iterations_left) break;
704  auto elem = queue.back();
705  queue.pop_back();
706  split_fn(std::move(elem));
707  }
708 
709  // Process one entry from the front of the queue (BFS exploration)
710  if (!iterations_left) break;
711  auto elem = queue.front();
712  queue.pop_front();
713  split_fn(std::move(elem));
714  }
715 
716  // Return the found best set and the number of iterations performed.
717  return {std::move(best), max_iterations - iterations_left};
718  }
719 
724  void MarkDone(const SetType& done) noexcept
725  {
726  Assume(done.Any());
727  Assume(done.IsSubsetOf(m_todo));
728  m_todo -= done;
729  }
730 };
731 
749 template<typename SetType>
750 std::pair<std::vector<ClusterIndex>, bool> Linearize(const DepGraph<SetType>& depgraph, uint64_t max_iterations, uint64_t rng_seed, Span<const ClusterIndex> old_linearization = {}) noexcept
751 {
752  Assume(old_linearization.empty() || old_linearization.size() == depgraph.TxCount());
753  if (depgraph.TxCount() == 0) return {{}, true};
754 
755  uint64_t iterations_left = max_iterations;
756  std::vector<ClusterIndex> linearization;
757 
758  AncestorCandidateFinder anc_finder(depgraph);
759  SearchCandidateFinder src_finder(depgraph, rng_seed);
760  linearization.reserve(depgraph.TxCount());
761  bool optimal = true;
762 
764  LinearizationChunking old_chunking(depgraph, old_linearization);
765 
766  while (true) {
767  // Find the highest-feerate prefix of the remainder of old_linearization.
768  SetInfo<SetType> best_prefix;
769  if (old_chunking.NumChunksLeft()) best_prefix = old_chunking.GetChunk(0);
770 
771  // Then initialize best to be either the best remaining ancestor set, or the first chunk.
772  auto best = anc_finder.FindCandidateSet();
773  if (!best_prefix.feerate.IsEmpty() && best_prefix.feerate >= best.feerate) best = best_prefix;
774 
775  // Invoke bounded search to update best, with up to half of our remaining iterations as
776  // limit.
777  uint64_t max_iterations_now = (iterations_left + 1) / 2;
778  uint64_t iterations_done_now = 0;
779  std::tie(best, iterations_done_now) = src_finder.FindCandidateSet(max_iterations_now, best);
780  iterations_left -= iterations_done_now;
781 
782  if (iterations_done_now == max_iterations_now) {
783  optimal = false;
784  // If the search result is not (guaranteed to be) optimal, run intersections to make
785  // sure we don't pick something that makes us unable to reach further diagram points
786  // of the old linearization.
787  if (old_chunking.NumChunksLeft() > 0) {
788  best = old_chunking.IntersectPrefixes(best);
789  }
790  }
791 
792  // Add to output in topological order.
793  depgraph.AppendTopo(linearization, best.transactions);
794 
795  // Update state to reflect best is no longer to be linearized.
796  anc_finder.MarkDone(best.transactions);
797  if (anc_finder.AllDone()) break;
798  src_finder.MarkDone(best.transactions);
799  if (old_chunking.NumChunksLeft() > 0) {
800  old_chunking.MarkDone(best.transactions);
801  }
802  }
803 
804  return {std::move(linearization), optimal};
805 }
806 
823 template<typename SetType>
824 void PostLinearize(const DepGraph<SetType>& depgraph, Span<ClusterIndex> linearization)
825 {
826  // This algorithm performs a number of passes (currently 2); the even ones operate from back to
827  // front, the odd ones from front to back. Each results in an equal-or-better linearization
828  // than the one started from.
829  // - One pass in either direction guarantees that the resulting chunks are connected.
830  // - Each direction corresponds to one shape of tree being linearized optimally (forward passes
831  // guarantee this for graphs where each transaction has at most one child; backward passes
832  // guarantee this for graphs where each transaction has at most one parent).
833  // - Starting with a backward pass guarantees the moved-tree property.
834  //
835  // During an odd (forward) pass, the high-level operation is:
836  // - Start with an empty list of groups L=[].
837  // - For every transaction i in the old linearization, from front to back:
838  // - Append a new group C=[i], containing just i, to the back of L.
839  // - While L has at least one group before C, and the group immediately before C has feerate
840  // lower than C:
841  // - If C depends on P:
842  // - Merge P into C, making C the concatenation of P+C, continuing with the combined C.
843  // - Otherwise:
844  // - Swap P with C, continuing with the now-moved C.
845  // - The output linearization is the concatenation of the groups in L.
846  //
847  // During even (backward) passes, i iterates from the back to the front of the existing
848  // linearization, and new groups are prepended instead of appended to the list L. To enable
849  // more code reuse, both passes append groups, but during even passes the meanings of
850  // parent/child, and of high/low feerate are reversed, and the final concatenation is reversed
851  // on output.
852  //
853  // In the implementation below, the groups are represented by singly-linked lists (pointing
854  // from the back to the front), which are themselves organized in a singly-linked circular
855  // list (each group pointing to its predecessor, with a special sentinel group at the front
856  // that points back to the last group).
857  //
858  // Information about transaction t is stored in entries[t + 1], while the sentinel is in
859  // entries[0].
860 
862  static constexpr ClusterIndex SENTINEL{0};
864  static constexpr ClusterIndex NO_PREV_TX{0};
865 
866 
868  struct TxEntry
869  {
872  ClusterIndex prev_tx;
873 
874  // The fields below are only used for transactions that are the last one in a group
875  // (referred to as tail transactions below).
876 
878  ClusterIndex first_tx;
881  ClusterIndex prev_group;
883  SetType group;
885  SetType deps;
887  FeeFrac feerate;
888  };
889 
890  // As an example, consider the state corresponding to the linearization [1,0,3,2], with
891  // groups [1,0,3] and [2], in an odd pass. The linked lists would be:
892  //
893  // +-----+
894  // 0<-P-- | 0 S | ---\ Legend:
895  // +-----+ |
896  // ^ | - digit in box: entries index
897  // /--------------F---------+ G | (note: one more than tx value)
898  // v \ | | - S: sentinel group
899  // +-----+ +-----+ +-----+ | (empty feerate)
900  // 0<-P-- | 2 | <--P-- | 1 | <--P-- | 4 T | | - T: tail transaction, contains
901  // +-----+ +-----+ +-----+ | fields beyond prev_tv.
902  // ^ | - P: prev_tx reference
903  // G G - F: first_tx reference
904  // | | - G: prev_group reference
905  // +-----+ |
906  // 0<-P-- | 3 T | <--/
907  // +-----+
908  // ^ |
909  // \-F-/
910  //
911  // During an even pass, the diagram above would correspond to linearization [2,3,0,1], with
912  // groups [2] and [3,0,1].
913 
914  std::vector<TxEntry> entries(linearization.size() + 1);
915 
916  // Perform two passes over the linearization.
917  for (int pass = 0; pass < 2; ++pass) {
918  int rev = !(pass & 1);
919  // Construct a sentinel group, identifying the start of the list.
920  entries[SENTINEL].prev_group = SENTINEL;
921  Assume(entries[SENTINEL].feerate.IsEmpty());
922 
923  // Iterate over all elements in the existing linearization.
924  for (ClusterIndex i = 0; i < linearization.size(); ++i) {
925  // Even passes are from back to front; odd passes from front to back.
926  ClusterIndex idx = linearization[rev ? linearization.size() - 1 - i : i];
927  // Construct a new group containing just idx. In even passes, the meaning of
928  // parent/child and high/low feerate are swapped.
929  ClusterIndex cur_group = idx + 1;
930  entries[cur_group].group = SetType::Singleton(idx);
931  entries[cur_group].deps = rev ? depgraph.Descendants(idx): depgraph.Ancestors(idx);
932  entries[cur_group].feerate = depgraph.FeeRate(idx);
933  if (rev) entries[cur_group].feerate.fee = -entries[cur_group].feerate.fee;
934  entries[cur_group].prev_tx = NO_PREV_TX; // No previous transaction in group.
935  entries[cur_group].first_tx = cur_group; // Transaction itself is first of group.
936  // Insert the new group at the back of the groups linked list.
937  entries[cur_group].prev_group = entries[SENTINEL].prev_group;
938  entries[SENTINEL].prev_group = cur_group;
939 
940  // Start merge/swap cycle.
941  ClusterIndex next_group = SENTINEL; // We inserted at the end, so next group is sentinel.
942  ClusterIndex prev_group = entries[cur_group].prev_group;
943  // Continue as long as the current group has higher feerate than the previous one.
944  while (entries[cur_group].feerate >> entries[prev_group].feerate) {
945  // prev_group/cur_group/next_group refer to (the last transactions of) 3
946  // consecutive entries in groups list.
947  Assume(cur_group == entries[next_group].prev_group);
948  Assume(prev_group == entries[cur_group].prev_group);
949  // The sentinel has empty feerate, which is neither higher or lower than other
950  // feerates. Thus, the while loop we are in here guarantees that cur_group and
951  // prev_group are not the sentinel.
952  Assume(cur_group != SENTINEL);
953  Assume(prev_group != SENTINEL);
954  if (entries[cur_group].deps.Overlaps(entries[prev_group].group)) {
955  // There is a dependency between cur_group and prev_group; merge prev_group
956  // into cur_group. The group/deps/feerate fields of prev_group remain unchanged
957  // but become unused.
958  entries[cur_group].group |= entries[prev_group].group;
959  entries[cur_group].deps |= entries[prev_group].deps;
960  entries[cur_group].feerate += entries[prev_group].feerate;
961  // Make the first of the current group point to the tail of the previous group.
962  entries[entries[cur_group].first_tx].prev_tx = prev_group;
963  // The first of the previous group becomes the first of the newly-merged group.
964  entries[cur_group].first_tx = entries[prev_group].first_tx;
965  // The previous group becomes whatever group was before the former one.
966  prev_group = entries[prev_group].prev_group;
967  entries[cur_group].prev_group = prev_group;
968  } else {
969  // There is no dependency between cur_group and prev_group; swap them.
970  ClusterIndex preprev_group = entries[prev_group].prev_group;
971  // If PP, P, C, N were the old preprev, prev, cur, next groups, then the new
972  // layout becomes [PP, C, P, N]. Update prev_groups to reflect that order.
973  entries[next_group].prev_group = prev_group;
974  entries[prev_group].prev_group = cur_group;
975  entries[cur_group].prev_group = preprev_group;
976  // The current group remains the same, but the groups before/after it have
977  // changed.
978  next_group = prev_group;
979  prev_group = preprev_group;
980  }
981  }
982  }
983 
984  // Convert the entries back to linearization (overwriting the existing one).
985  ClusterIndex cur_group = entries[0].prev_group;
986  ClusterIndex done = 0;
987  while (cur_group != SENTINEL) {
988  ClusterIndex cur_tx = cur_group;
989  // Traverse the transactions of cur_group (from back to front), and write them in the
990  // same order during odd passes, and reversed (front to back) in even passes.
991  if (rev) {
992  do {
993  *(linearization.begin() + (done++)) = cur_tx - 1;
994  cur_tx = entries[cur_tx].prev_tx;
995  } while (cur_tx != NO_PREV_TX);
996  } else {
997  do {
998  *(linearization.end() - (++done)) = cur_tx - 1;
999  cur_tx = entries[cur_tx].prev_tx;
1000  } while (cur_tx != NO_PREV_TX);
1001  }
1002  cur_group = entries[cur_group].prev_group;
1003  }
1004  Assume(done == linearization.size());
1005  }
1006 }
1007 
1012 template<typename SetType>
1013 std::vector<ClusterIndex> MergeLinearizations(const DepGraph<SetType>& depgraph, Span<const ClusterIndex> lin1, Span<const ClusterIndex> lin2)
1014 {
1015  Assume(lin1.size() == depgraph.TxCount());
1016  Assume(lin2.size() == depgraph.TxCount());
1017 
1019  LinearizationChunking chunking1(depgraph, lin1), chunking2(depgraph, lin2);
1021  std::vector<ClusterIndex> ret;
1022  if (depgraph.TxCount() == 0) return ret;
1023  ret.reserve(depgraph.TxCount());
1024 
1025  while (true) {
1026  // As long as we are not done, both linearizations must have chunks left.
1027  Assume(chunking1.NumChunksLeft() > 0);
1028  Assume(chunking2.NumChunksLeft() > 0);
1029  // Find the set to output by taking the best remaining chunk, and then intersecting it with
1030  // prefixes of remaining chunks of the other linearization.
1031  SetInfo<SetType> best;
1032  const auto& lin1_firstchunk = chunking1.GetChunk(0);
1033  const auto& lin2_firstchunk = chunking2.GetChunk(0);
1034  if (lin2_firstchunk.feerate >> lin1_firstchunk.feerate) {
1035  best = chunking1.IntersectPrefixes(lin2_firstchunk);
1036  } else {
1037  best = chunking2.IntersectPrefixes(lin1_firstchunk);
1038  }
1039  // Append the result to the output and mark it as done.
1040  depgraph.AppendTopo(ret, best.transactions);
1041  chunking1.MarkDone(best.transactions);
1042  if (chunking1.NumChunksLeft() == 0) break;
1043  chunking2.MarkDone(best.transactions);
1044  }
1045 
1046  Assume(ret.size() == depgraph.TxCount());
1047  return ret;
1048 }
1049 
1050 } // namespace cluster_linearize
1051 
1052 #endif // BITCOIN_CLUSTER_LINEARIZE_H
const DepGraph< SetType > & m_depgraph
Internal dependency graph.
CONSTEXPR_IF_NOT_DEBUG Span< C > subspan(std::size_t offset) const noexcept
Definition: span.h:195
A set of transactions together with their aggregate feerate.
int64_t fee
Definition: feefrac.h:63
int ret
SetType descendants
All descendants of the transaction (including itself).
void BuildChunks() noexcept
Fill the m_chunks variable, and remove the done prefix of m_linearization.
size_t size() const noexcept
Get the number of elements in this deque.
Definition: vecdeque.h:312
bool randbool() noexcept
Generate a random boolean.
Definition: random.h:316
const DepGraph< SetType > & m_depgraph
Internal dependency graph for the cluster.
friend bool operator==(const SetInfo &, const SetInfo &) noexcept=default
Permit equality testing.
Data structure encapsulating the chunking of a linearization, permitting removal of subsets...
ClusterIndex AddTransaction(const FeeFrac &feefrac) noexcept
Add a new unconnected transaction to this transaction graph (at the end), and return its ClusterIndex...
constexpr C * end() const noexcept
Definition: span.h:176
SetType FindConnectedComponent(const SetType &todo) const noexcept
Find some connected component within the subset "todo" of this graph.
bool AllDone() const noexcept
Check whether any unlinearized transactions remain.
Data structure largely mimicking std::deque, but using single preallocated ring buffer.
Definition: vecdeque.h:24
FeeFrac feerate
Their combined fee and size.
T & front() noexcept
Get a mutable reference to the first element of the deque.
Definition: vecdeque.h:268
uint32_t ClusterIndex
Data type to represent transaction indices in clusters.
friend bool operator==(const Entry &, const Entry &) noexcept=default
Equality operator (primarily for for testing purposes).
FeeFrac feerate
Fee and size of transaction itself.
FeeFrac FeeRate(const SetType &elems) const noexcept
Compute the aggregate feerate of a set of nodes in this graph.
SetInfo< SetType > IntersectPrefixes(const SetInfo< SetType > &subset) const noexcept
Find the shortest intersection between subset and the prefixes of remaining chunks of the linearizati...
const SetType & Descendants(ClusterIndex i) const noexcept
Get the descendants of a given transaction i.
const SetInfo< SetType > & GetChunk(ClusterIndex n) const noexcept
Access a chunk.
constexpr std::size_t size() const noexcept
Definition: span.h:187
void pop_back()
Remove the last element of the deque.
Definition: vecdeque.h:260
SetInfo(const DepGraph< SetType > &depgraph, const SetType &txn) noexcept
Construct a SetInfo for a set of transactions in a depgraph.
T & back() noexcept
Get a mutable reference to the last element of the deque.
Definition: vecdeque.h:282
friend void swap(SetInfo &a, SetInfo &b) noexcept
Swap two SetInfo objects.
InsecureRandomContext m_rng
Internal RNG.
Entry() noexcept=default
Construct an empty entry.
bool empty() const noexcept
Test whether the contents of this deque is empty.
Definition: vecdeque.h:310
SetType m_todo
Which transaction are left to include.
void MarkDone(SetType select) noexcept
Remove a set of transactions from the set of to-be-linearized ones.
LinearizationChunking(const DepGraph< SetType > &depgraph LIFETIMEBOUND, Span< const ClusterIndex > lin LIFETIMEBOUND) noexcept
Initialize a LinearizationSubset object for a given length of linearization.
std::vector< SetInfo< SetType > > m_chunks
Chunk sets and their feerates, of what remains of the linearization.
bool AllDone() const noexcept
Check whether any unlinearized transactions remain.
void emplace_back(Args &&... args)
Construct a new element at the end of the deque.
Definition: vecdeque.h:219
#define LIFETIMEBOUND
Definition: attributes.h:16
void MarkDone(SetType subset) noexcept
Remove some subset of transactions from the linearization.
bool IsConnected(const SetType &subset) const noexcept
Determine if a subset is connected.
const DepGraph< SetType > & m_depgraph
The depgraph this linearization is for.
DepGraph(const Cluster< SetType > &cluster) noexcept
Construct a DepGraph object given a cluster.
void reserve(size_t capacity)
Increase the capacity to capacity.
Definition: vecdeque.h:206
std::vector< Entry > entries
Data for each transaction, in the same order as the Cluster it was constructed from.
SetInfo Add(const DepGraph< SetType > &depgraph, const SetType &txn) const noexcept
Construct a new SetInfo equal to this, with more transactions added (which may overlap with the exist...
Class encapsulating the state needed to find the best remaining ancestor set.
xoroshiro128++ PRNG.
Definition: random.h:415
std::pair< SetInfo< SetType >, uint64_t > FindCandidateSet(uint64_t max_iterations, SetInfo< SetType > best) noexcept
Find a high-feerate topologically-valid subset of what remains of the cluster.
bool IsConnected() const noexcept
Determine if this entire graph is connected.
friend bool operator==(const DepGraph &, const DepGraph &) noexcept=default
Equality operator (primarily for testing purposes).
CONSTEXPR_IF_NOT_DEBUG C & front() const noexcept
Definition: span.h:177
bool IsEmpty() const noexcept
Check if this is empty (size and fee are 0).
Definition: feefrac.h:76
#define Assume(val)
Assume is the identity function.
Definition: check.h:89
SetType transactions
The transactions in the set.
FeeFrac & FeeRate(ClusterIndex i) noexcept
Get the mutable feerate of a given transaction i.
SetInfo(const DepGraph< SetType > &depgraph, ClusterIndex pos) noexcept
Construct a SetInfo for a given transaction in a depgraph.
void pop_front()
Remove the first element of the deque.
Definition: vecdeque.h:250
constexpr C * begin() const noexcept
Definition: span.h:175
ClusterIndex m_chunks_skip
How large a prefix of m_chunks corresponds to removed transactions.
Data structure storing a fee and size, ordered by increasing fee/size.
Definition: feefrac.h:38
void AddDependency(ClusterIndex parent, ClusterIndex child) noexcept
Modify this transaction graph, adding a dependency between a specified parent and child...
SetType ancestors
All ancestors of the transaction (including itself).
Data structure that holds a transaction graph&#39;s preprocessed data (fee, size, ancestors, descendants).
std::pair< std::vector< ClusterIndex >, bool > Linearize(const DepGraph< SetType > &depgraph, uint64_t max_iterations, uint64_t rng_seed, Span< const ClusterIndex > old_linearization={}) noexcept
Find or improve a linearization for a cluster.
constexpr bool empty() const noexcept
Definition: span.h:189
SetInfo< SetType > FindCandidateSet() const noexcept
Find the best (highest-feerate, smallest among those in case of a tie) ancestor set among the remaini...
DepGraph() noexcept=default
SetType m_todo
Which transactions are left to do (sorted indices).
static std::vector< std::string > split(const std::string &str, const std::string &delims=" \)
Definition: subprocess.h:303
ClusterIndex NumChunksLeft() const noexcept
Determine how many chunks remain in the linearization.
SetType m_todo
Which transactions remain in the linearization.
std::vector< ClusterIndex > MergeLinearizations(const DepGraph< SetType > &depgraph, Span< const ClusterIndex > lin1, Span< const ClusterIndex > lin2)
Merge two linearizations for the same cluster into one that is as good as both.
size_t capacity() const noexcept
Get the capacity of this deque (maximum size it can have without reallocating).
Definition: vecdeque.h:314
void PostLinearize(const DepGraph< SetType > &depgraph, Span< ClusterIndex > linearization)
Improve a given linearization.
const FeeFrac & FeeRate(ClusterIndex i) const noexcept
Get the feerate of a given transaction i.
Information about a single transaction.
std::vector< std::pair< FeeFrac, SetType > > Cluster
Data type to represent cluster input.
SetInfo() noexcept=default
Construct a SetInfo for the empty set.
std::vector< FeeFrac > ChunkLinearization(const DepGraph< SetType > &depgraph, Span< const ClusterIndex > linearization) noexcept
Compute the feerates of the chunks of linearization.
SetInfo & operator|=(const SetInfo &other) noexcept
Add the transactions of other to this SetInfo (no overlap allowed).
SearchCandidateFinder(const DepGraph< SetType > &depgraph LIFETIMEBOUND, uint64_t rng_seed) noexcept
Construct a candidate finder for a graph.
AncestorCandidateFinder(const DepGraph< SetType > &depgraph LIFETIMEBOUND) noexcept
Construct an AncestorCandidateFinder for a given cluster.
Span< const ClusterIndex > m_linearization
The linearization we started from, possibly with removed prefix stripped.
const SetType & Ancestors(ClusterIndex i) const noexcept
Get the ancestors of a given transaction i.
auto TxCount() const noexcept
Get the number of transactions in the graph.
void MarkDone(const SetType &done) noexcept
Remove a subset of transactions from the cluster being linearized.
Class encapsulating the state needed to perform search for good candidate sets.
std::vector< FeeFrac > m_ancestor_set_feerates
Precomputed ancestor-set feerates (only kept up-to-date for indices in m_todo).
void AppendTopo(std::vector< ClusterIndex > &list, const SetType &select) const noexcept
Append the entries of select to list in a topologically valid order.