Bitcoin Core 30.99.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 <cstdint>
10#include <numeric>
11#include <optional>
12#include <utility>
13#include <vector>
14
15#include <memusage.h>
16#include <random.h>
17#include <span.h>
18#include <util/feefrac.h>
19#include <util/vecdeque.h>
20
22
24using DepGraphIndex = uint32_t;
25
28template<typename SetType>
30{
32 struct Entry
33 {
37 SetType ancestors;
39 SetType descendants;
40
42 friend bool operator==(const Entry&, const Entry&) noexcept = default;
43
45 Entry() noexcept = default;
47 Entry(const FeeFrac& f, const SetType& a, const SetType& d) noexcept : feerate(f), ancestors(a), descendants(d) {}
48 };
49
51 std::vector<Entry> entries;
52
54 SetType m_used;
55
56public:
58 friend bool operator==(const DepGraph& a, const DepGraph& b) noexcept
59 {
60 if (a.m_used != b.m_used) return false;
61 // Only compare the used positions within the entries vector.
62 for (auto idx : a.m_used) {
63 if (a.entries[idx] != b.entries[idx]) return false;
64 }
65 return true;
66 }
67
68 // Default constructors.
69 DepGraph() noexcept = default;
70 DepGraph(const DepGraph&) noexcept = default;
71 DepGraph(DepGraph&&) noexcept = default;
72 DepGraph& operator=(const DepGraph&) noexcept = default;
73 DepGraph& operator=(DepGraph&&) noexcept = default;
74
90 DepGraph(const DepGraph<SetType>& depgraph, std::span<const DepGraphIndex> mapping, DepGraphIndex pos_range) noexcept : entries(pos_range)
91 {
92 Assume(mapping.size() == depgraph.PositionRange());
93 Assume((pos_range == 0) == (depgraph.TxCount() == 0));
94 for (DepGraphIndex i : depgraph.Positions()) {
95 auto new_idx = mapping[i];
96 Assume(new_idx < pos_range);
97 // Add transaction.
98 entries[new_idx].ancestors = SetType::Singleton(new_idx);
99 entries[new_idx].descendants = SetType::Singleton(new_idx);
100 m_used.Set(new_idx);
101 // Fill in fee and size.
102 entries[new_idx].feerate = depgraph.entries[i].feerate;
103 }
104 for (DepGraphIndex i : depgraph.Positions()) {
105 // Fill in dependencies by mapping direct parents.
106 SetType parents;
107 for (auto j : depgraph.GetReducedParents(i)) parents.Set(mapping[j]);
108 AddDependencies(parents, mapping[i]);
109 }
110 // Verify that the provided pos_range was correct (no unused positions at the end).
111 Assume(m_used.None() ? (pos_range == 0) : (pos_range == m_used.Last() + 1));
112 }
113
115 const SetType& Positions() const noexcept { return m_used; }
117 DepGraphIndex PositionRange() const noexcept { return entries.size(); }
119 auto TxCount() const noexcept { return m_used.Count(); }
121 const FeeFrac& FeeRate(DepGraphIndex i) const noexcept { return entries[i].feerate; }
123 FeeFrac& FeeRate(DepGraphIndex i) noexcept { return entries[i].feerate; }
125 const SetType& Ancestors(DepGraphIndex i) const noexcept { return entries[i].ancestors; }
127 const SetType& Descendants(DepGraphIndex i) const noexcept { return entries[i].descendants; }
128
134 DepGraphIndex AddTransaction(const FeeFrac& feefrac) noexcept
135 {
136 static constexpr auto ALL_POSITIONS = SetType::Fill(SetType::Size());
137 auto available = ALL_POSITIONS - m_used;
138 Assume(available.Any());
139 DepGraphIndex new_idx = available.First();
140 if (new_idx == entries.size()) {
141 entries.emplace_back(feefrac, SetType::Singleton(new_idx), SetType::Singleton(new_idx));
142 } else {
143 entries[new_idx] = Entry(feefrac, SetType::Singleton(new_idx), SetType::Singleton(new_idx));
144 }
145 m_used.Set(new_idx);
146 return new_idx;
147 }
148
158 void RemoveTransactions(const SetType& del) noexcept
159 {
160 m_used -= del;
161 // Remove now-unused trailing entries.
162 while (!entries.empty() && !m_used[entries.size() - 1]) {
163 entries.pop_back();
164 }
165 // Remove the deleted transactions from ancestors/descendants of other transactions. Note
166 // that the deleted positions will retain old feerate and dependency information. This does
167 // not matter as they will be overwritten by AddTransaction if they get used again.
168 for (auto& entry : entries) {
169 entry.ancestors &= m_used;
170 entry.descendants &= m_used;
171 }
172 }
173
178 void AddDependencies(const SetType& parents, DepGraphIndex child) noexcept
179 {
180 Assume(m_used[child]);
181 Assume(parents.IsSubsetOf(m_used));
182 // Compute the ancestors of parents that are not already ancestors of child.
183 SetType par_anc;
184 for (auto par : parents - Ancestors(child)) {
185 par_anc |= Ancestors(par);
186 }
187 par_anc -= Ancestors(child);
188 // Bail out if there are no such ancestors.
189 if (par_anc.None()) return;
190 // To each such ancestor, add as descendants the descendants of the child.
191 const auto& chl_des = entries[child].descendants;
192 for (auto anc_of_par : par_anc) {
193 entries[anc_of_par].descendants |= chl_des;
194 }
195 // To each descendant of the child, add those ancestors.
196 for (auto dec_of_chl : Descendants(child)) {
197 entries[dec_of_chl].ancestors |= par_anc;
198 }
199 }
200
209 SetType GetReducedParents(DepGraphIndex i) const noexcept
210 {
211 SetType parents = Ancestors(i);
212 parents.Reset(i);
213 for (auto parent : parents) {
214 if (parents[parent]) {
215 parents -= Ancestors(parent);
216 parents.Set(parent);
217 }
218 }
219 return parents;
220 }
221
230 SetType GetReducedChildren(DepGraphIndex i) const noexcept
231 {
232 SetType children = Descendants(i);
233 children.Reset(i);
234 for (auto child : children) {
235 if (children[child]) {
236 children -= Descendants(child);
237 children.Set(child);
238 }
239 }
240 return children;
241 }
242
247 FeeFrac FeeRate(const SetType& elems) const noexcept
248 {
249 FeeFrac ret;
250 for (auto pos : elems) ret += entries[pos].feerate;
251 return ret;
252 }
253
264 SetType GetConnectedComponent(const SetType& todo, DepGraphIndex tx) const noexcept
265 {
266 Assume(todo[tx]);
267 Assume(todo.IsSubsetOf(m_used));
268 auto to_add = SetType::Singleton(tx);
269 SetType ret;
270 do {
271 SetType old = ret;
272 for (auto add : to_add) {
273 ret |= Descendants(add);
274 ret |= Ancestors(add);
275 }
276 ret &= todo;
277 to_add = ret - old;
278 } while (to_add.Any());
279 return ret;
280 }
281
289 SetType FindConnectedComponent(const SetType& todo) const noexcept
290 {
291 if (todo.None()) return todo;
292 return GetConnectedComponent(todo, todo.First());
293 }
294
299 bool IsConnected(const SetType& subset) const noexcept
300 {
301 return FindConnectedComponent(subset) == subset;
302 }
303
308 bool IsConnected() const noexcept { return IsConnected(m_used); }
309
314 void AppendTopo(std::vector<DepGraphIndex>& list, const SetType& select) const noexcept
315 {
316 DepGraphIndex old_len = list.size();
317 for (auto i : select) list.push_back(i);
318 std::sort(list.begin() + old_len, list.end(), [&](DepGraphIndex a, DepGraphIndex b) noexcept {
319 const auto a_anc_count = entries[a].ancestors.Count();
320 const auto b_anc_count = entries[b].ancestors.Count();
321 if (a_anc_count != b_anc_count) return a_anc_count < b_anc_count;
322 return a < b;
323 });
324 }
325
327 bool IsAcyclic() const noexcept
328 {
329 for (auto i : Positions()) {
330 if ((Ancestors(i) & Descendants(i)) != SetType::Singleton(i)) {
331 return false;
332 }
333 }
334 return true;
335 }
336
337 unsigned CountDependencies() const noexcept
338 {
339 unsigned ret = 0;
340 for (auto i : Positions()) {
341 ret += GetReducedParents(i).Count();
342 }
343 return ret;
344 }
345
347 void Compact() noexcept
348 {
349 entries.shrink_to_fit();
350 }
351
352 size_t DynamicMemoryUsage() const noexcept
353 {
355 }
356};
357
359template<typename SetType>
361{
366
368 SetInfo() noexcept = default;
369
371 SetInfo(const SetType& txn, const FeeFrac& fr) noexcept : transactions(txn), feerate(fr) {}
372
374 explicit SetInfo(const DepGraph<SetType>& depgraph, DepGraphIndex pos) noexcept :
375 transactions(SetType::Singleton(pos)), feerate(depgraph.FeeRate(pos)) {}
376
378 explicit SetInfo(const DepGraph<SetType>& depgraph, const SetType& txn) noexcept :
379 transactions(txn), feerate(depgraph.FeeRate(txn)) {}
380
382 void Set(const DepGraph<SetType>& depgraph, DepGraphIndex pos) noexcept
383 {
384 Assume(!transactions[pos]);
385 transactions.Set(pos);
386 feerate += depgraph.FeeRate(pos);
387 }
388
390 SetInfo& operator|=(const SetInfo& other) noexcept
391 {
392 Assume(!transactions.Overlaps(other.transactions));
393 transactions |= other.transactions;
394 feerate += other.feerate;
395 return *this;
396 }
397
399 SetInfo& operator-=(const SetInfo& other) noexcept
400 {
401 Assume(other.transactions.IsSubsetOf(transactions));
402 transactions -= other.transactions;
403 feerate -= other.feerate;
404 return *this;
405 }
406
408 SetInfo operator-(const SetInfo& other) const noexcept
409 {
410 Assume(other.transactions.IsSubsetOf(transactions));
411 return {transactions - other.transactions, feerate - other.feerate};
412 }
413
415 friend void swap(SetInfo& a, SetInfo& b) noexcept
416 {
417 swap(a.transactions, b.transactions);
418 swap(a.feerate, b.feerate);
419 }
420
422 friend bool operator==(const SetInfo&, const SetInfo&) noexcept = default;
423};
424
426template<typename SetType>
427std::vector<SetInfo<SetType>> ChunkLinearizationInfo(const DepGraph<SetType>& depgraph, std::span<const DepGraphIndex> linearization) noexcept
428{
429 std::vector<SetInfo<SetType>> ret;
430 for (DepGraphIndex i : linearization) {
432 SetInfo<SetType> new_chunk(depgraph, i);
433 // As long as the new chunk has a higher feerate than the last chunk so far, absorb it.
434 while (!ret.empty() && new_chunk.feerate >> ret.back().feerate) {
435 new_chunk |= ret.back();
436 ret.pop_back();
437 }
438 // Actually move that new chunk into the chunking.
439 ret.emplace_back(std::move(new_chunk));
440 }
441 return ret;
442}
443
446template<typename SetType>
447std::vector<FeeFrac> ChunkLinearization(const DepGraph<SetType>& depgraph, std::span<const DepGraphIndex> linearization) noexcept
448{
449 std::vector<FeeFrac> ret;
450 for (DepGraphIndex i : linearization) {
452 auto new_chunk = depgraph.FeeRate(i);
453 // As long as the new chunk has a higher feerate than the last chunk so far, absorb it.
454 while (!ret.empty() && new_chunk >> ret.back()) {
455 new_chunk += ret.back();
456 ret.pop_back();
457 }
458 // Actually move that new chunk into the chunking.
459 ret.push_back(std::move(new_chunk));
460 }
461 return ret;
462}
463
598template<typename SetType>
600{
601private:
604
606 using TxIdx = uint32_t;
608 using DepIdx = uint32_t;
609
612 struct TxData {
614 std::vector<DepIdx> child_deps;
616 SetType parents;
618 SetType children;
625 };
626
628 struct DepData {
630 bool active;
636 };
637
642 std::vector<TxData> m_tx_data;
644 std::vector<DepData> m_dep_data;
647
649 uint64_t m_cost{0};
650
656 template<bool Subtract>
657 void UpdateChunk(const SetType& chunk, TxIdx query, TxIdx chunk_rep, const SetInfo<SetType>& dep_change) noexcept
658 {
659 // Iterate over all the chunk's transactions.
660 for (auto tx_idx : chunk) {
661 auto& tx_data = m_tx_data[tx_idx];
662 // Update the chunk representative.
663 tx_data.chunk_rep = chunk_rep;
664 // Iterate over all active dependencies with tx_idx as parent. Combined with the outer
665 // loop this iterates over all internal active dependencies of the chunk.
666 auto child_deps = std::span{tx_data.child_deps};
667 for (auto dep_idx : child_deps) {
668 auto& dep_entry = m_dep_data[dep_idx];
669 Assume(dep_entry.parent == tx_idx);
670 // Skip inactive dependencies.
671 if (!dep_entry.active) continue;
672 // If this dependency's top_setinfo contains query, update it to add/remove
673 // dep_change.
674 if (dep_entry.top_setinfo.transactions[query]) {
675 if constexpr (Subtract) {
676 dep_entry.top_setinfo -= dep_change;
677 } else {
678 dep_entry.top_setinfo |= dep_change;
679 }
680 }
681 }
682 }
683 }
684
686 TxIdx Activate(DepIdx dep_idx) noexcept
687 {
688 auto& dep_data = m_dep_data[dep_idx];
689 Assume(!dep_data.active);
690 auto& child_tx_data = m_tx_data[dep_data.child];
691 auto& parent_tx_data = m_tx_data[dep_data.parent];
692
693 // Gather information about the parent and child chunks.
694 Assume(parent_tx_data.chunk_rep != child_tx_data.chunk_rep);
695 auto& par_chunk_data = m_tx_data[parent_tx_data.chunk_rep];
696 auto& chl_chunk_data = m_tx_data[child_tx_data.chunk_rep];
697 TxIdx top_rep = parent_tx_data.chunk_rep;
698 auto top_part = par_chunk_data.chunk_setinfo;
699 auto bottom_part = chl_chunk_data.chunk_setinfo;
700 // Update the parent chunk to also contain the child.
701 par_chunk_data.chunk_setinfo |= bottom_part;
702 m_cost += par_chunk_data.chunk_setinfo.transactions.Count();
703
704 // Consider the following example:
705 //
706 // A A There are two chunks, ABC and DEF, and the inactive E->C dependency
707 // / \ / \ is activated, resulting in a single chunk ABCDEF.
708 // B C B C
709 // : ==> | Dependency | top set before | top set after | change
710 // D E D E B->A | AC | ACDEF | +DEF
711 // \ / \ / C->A | AB | AB |
712 // F F F->D | D | D |
713 // F->E | E | ABCE | +ABC
714 //
715 // The common pattern here is that any dependency which has the parent or child of the
716 // dependency being activated (E->C here) in its top set, will have the opposite part added
717 // to it. This is true for B->A and F->E, but not for C->A and F->D.
718 //
719 // Let UpdateChunk traverse the old parent chunk top_part (ABC in example), and add
720 // bottom_part (DEF) to every dependency's top_set which has the parent (C) in it. The
721 // representative of each of these transactions was already top_rep, so that is not being
722 // changed here.
723 UpdateChunk<false>(/*chunk=*/top_part.transactions, /*query=*/dep_data.parent,
724 /*chunk_rep=*/top_rep, /*dep_change=*/bottom_part);
725 // Let UpdateChunk traverse the old child chunk bottom_part (DEF in example), and add
726 // top_part (ABC) to every dependency's top_set which has the child (E) in it. At the same
727 // time, change the representative of each of these transactions to be top_rep, which
728 // becomes the representative for the merged chunk.
729 UpdateChunk<false>(/*chunk=*/bottom_part.transactions, /*query=*/dep_data.child,
730 /*chunk_rep=*/top_rep, /*dep_change=*/top_part);
731 // Make active.
732 dep_data.active = true;
733 dep_data.top_setinfo = top_part;
734 return top_rep;
735 }
736
738 void Deactivate(DepIdx dep_idx) noexcept
739 {
740 auto& dep_data = m_dep_data[dep_idx];
741 Assume(dep_data.active);
742 auto& parent_tx_data = m_tx_data[dep_data.parent];
743 // Make inactive.
744 dep_data.active = false;
745 // Update representatives.
746 auto& chunk_data = m_tx_data[parent_tx_data.chunk_rep];
747 m_cost += chunk_data.chunk_setinfo.transactions.Count();
748 auto top_part = dep_data.top_setinfo;
749 auto bottom_part = chunk_data.chunk_setinfo - top_part;
750 TxIdx bottom_rep = dep_data.child;
751 auto& bottom_chunk_data = m_tx_data[bottom_rep];
752 bottom_chunk_data.chunk_setinfo = bottom_part;
753 TxIdx top_rep = dep_data.parent;
754 auto& top_chunk_data = m_tx_data[top_rep];
755 top_chunk_data.chunk_setinfo = top_part;
756
757 // See the comment above in Activate(). We perform the opposite operations here,
758 // removing instead of adding.
759 //
760 // Let UpdateChunk traverse the old parent chunk top_part, and remove bottom_part from
761 // every dependency's top_set which has the parent in it. At the same time, change the
762 // representative of each of these transactions to be top_rep.
763 UpdateChunk<true>(/*chunk=*/top_part.transactions, /*query=*/dep_data.parent,
764 /*chunk_rep=*/top_rep, /*dep_change=*/bottom_part);
765 // Let UpdateChunk traverse the old child chunk bottom_part, and remove top_part from every
766 // dependency's top_set which has the child in it. At the same time, change the
767 // representative of each of these transactions to be bottom_rep.
768 UpdateChunk<true>(/*chunk=*/bottom_part.transactions, /*query=*/dep_data.child,
769 /*chunk_rep=*/bottom_rep, /*dep_change=*/top_part);
770 }
771
774 TxIdx MergeChunks(TxIdx top_rep, TxIdx bottom_rep) noexcept
775 {
776 auto& top_chunk = m_tx_data[top_rep];
777 Assume(top_chunk.chunk_rep == top_rep);
778 auto& bottom_chunk = m_tx_data[bottom_rep];
779 Assume(bottom_chunk.chunk_rep == bottom_rep);
780 // Count the number of dependencies between bottom_chunk and top_chunk.
781 TxIdx num_deps{0};
782 for (auto tx : top_chunk.chunk_setinfo.transactions) {
783 auto& tx_data = m_tx_data[tx];
784 num_deps += (tx_data.children & bottom_chunk.chunk_setinfo.transactions).Count();
785 }
786 Assume(num_deps > 0);
787 // Uniformly randomly pick one of them and activate it.
788 TxIdx pick = m_rng.randrange(num_deps);
789 for (auto tx : top_chunk.chunk_setinfo.transactions) {
790 auto& tx_data = m_tx_data[tx];
791 auto intersect = tx_data.children & bottom_chunk.chunk_setinfo.transactions;
792 auto count = intersect.Count();
793 if (pick < count) {
794 for (auto dep : tx_data.child_deps) {
795 auto& dep_data = m_dep_data[dep];
796 if (bottom_chunk.chunk_setinfo.transactions[dep_data.child]) {
797 if (pick == 0) return Activate(dep);
798 --pick;
799 }
800 }
801 break;
802 }
803 pick -= count;
804 }
805 Assume(false);
806 return TxIdx(-1);
807 }
808
811 template<bool DownWard>
812 TxIdx MergeStep(TxIdx chunk_rep) noexcept
813 {
815 auto& chunk_data = m_tx_data[chunk_rep];
816 SetType chunk_txn = chunk_data.chunk_setinfo.transactions;
817 // Iterate over all transactions in the chunk, figuring out which other chunk each
818 // depends on, but only testing each other chunk once. For those depended-on chunks,
819 // remember the highest-feerate (if DownWard) or lowest-feerate (if !DownWard) one.
820 // If multiple equal-feerate candidate chunks to merge with exist, pick a random one
821 // among them.
822
825 SetType explored = chunk_txn;
829 FeeFrac best_other_chunk_feerate = chunk_data.chunk_setinfo.feerate;
831 TxIdx best_other_chunk_rep = TxIdx(-1);
834 uint64_t best_other_chunk_tiebreak{0};
835 for (auto tx : chunk_txn) {
836 auto& tx_data = m_tx_data[tx];
839 auto newly_reached = (DownWard ? tx_data.children : tx_data.parents) - explored;
840 explored |= newly_reached;
841 while (newly_reached.Any()) {
842 // Find a chunk inside newly_reached, and remove it from newly_reached.
843 auto reached_chunk_rep = m_tx_data[newly_reached.First()].chunk_rep;
844 auto& reached_chunk = m_tx_data[reached_chunk_rep].chunk_setinfo;
845 newly_reached -= reached_chunk.transactions;
846 // See if it has an acceptable feerate.
847 auto cmp = DownWard ? FeeRateCompare(best_other_chunk_feerate, reached_chunk.feerate)
848 : FeeRateCompare(reached_chunk.feerate, best_other_chunk_feerate);
849 if (cmp > 0) continue;
850 uint64_t tiebreak = m_rng.rand64();
851 if (cmp < 0 || tiebreak >= best_other_chunk_tiebreak) {
852 best_other_chunk_feerate = reached_chunk.feerate;
853 best_other_chunk_rep = reached_chunk_rep;
854 best_other_chunk_tiebreak = tiebreak;
855 }
856 }
857 }
858 // Stop if there are no candidate chunks to merge with.
859 if (best_other_chunk_rep == TxIdx(-1)) return TxIdx(-1);
860 if constexpr (DownWard) {
861 chunk_rep = MergeChunks(chunk_rep, best_other_chunk_rep);
862 } else {
863 chunk_rep = MergeChunks(best_other_chunk_rep, chunk_rep);
864 }
865 Assume(chunk_rep != TxIdx(-1));
866 return chunk_rep;
867 }
868
869
871 template<bool DownWard>
872 void MergeSequence(TxIdx tx_idx) noexcept
873 {
874 auto chunk_rep = m_tx_data[tx_idx].chunk_rep;
875 while (true) {
876 auto merged_rep = MergeStep<DownWard>(chunk_rep);
877 if (merged_rep == TxIdx(-1)) break;
878 chunk_rep = merged_rep;
879 }
880 // Add the chunk to the queue of improvable chunks.
882 }
883
886 void Improve(DepIdx dep_idx) noexcept
887 {
888 auto& dep_data = m_dep_data[dep_idx];
889 Assume(dep_data.active);
890 // Deactivate the specified dependency, splitting it into two new chunks: a top containing
891 // the parent, and a bottom containing the child. The top should have a higher feerate.
892 Deactivate(dep_idx);
893
894 // At this point we have exactly two chunks which may violate topology constraints (the
895 // parent chunk and child chunk that were produced by deactivating dep_idx). We can fix
896 // these using just merge sequences, one upwards and one downwards, avoiding the need for a
897 // full MakeTopological.
898
899 // Merge the top chunk with lower-feerate chunks it depends on (which may be the bottom it
900 // was just split from, or other pre-existing chunks).
901 MergeSequence<false>(dep_data.parent);
902 // Merge the bottom chunk with higher-feerate chunks that depend on it.
903 MergeSequence<true>(dep_data.child);
904 }
905
906public:
909 explicit SpanningForestState(const DepGraph<SetType>& depgraph, uint64_t rng_seed) noexcept : m_rng(rng_seed)
910 {
911 m_transaction_idxs = depgraph.Positions();
912 auto num_transactions = m_transaction_idxs.Count();
913 m_tx_data.resize(depgraph.PositionRange());
914 // Reserve the maximum number of (reserved) dependencies the cluster can have, so
915 // m_dep_data won't need any reallocations during construction. For a cluster with N
916 // transactions, the worst case consists of two sets of transactions, the parents and the
917 // children, where each child depends on each parent and nothing else. For even N, both
918 // sets can be sized N/2, which means N^2/4 dependencies. For odd N, one can be (N + 1)/2
919 // and the other can be (N - 1)/2, meaning (N^2 - 1)/4 dependencies. Because N^2 is odd in
920 // this case, N^2/4 (with rounding-down division) is the correct value in both cases.
921 m_dep_data.reserve((num_transactions * num_transactions) / 4);
922 for (auto tx : m_transaction_idxs) {
923 // Fill in transaction data.
924 auto& tx_data = m_tx_data[tx];
925 tx_data.chunk_rep = tx;
926 tx_data.chunk_setinfo.transactions = SetType::Singleton(tx);
927 tx_data.chunk_setinfo.feerate = depgraph.FeeRate(tx);
928 // Add its dependencies.
929 SetType parents = depgraph.GetReducedParents(tx);
930 for (auto par : parents) {
931 auto& par_tx_data = m_tx_data[par];
932 auto dep_idx = m_dep_data.size();
933 // Construct new dependency.
934 auto& dep = m_dep_data.emplace_back();
935 dep.active = false;
936 dep.parent = par;
937 dep.child = tx;
938 // Add it as parent of the child.
939 tx_data.parents.Set(par);
940 // Add it as child of the parent.
941 par_tx_data.child_deps.push_back(dep_idx);
942 par_tx_data.children.Set(tx);
943 }
944 }
945 }
946
950 void LoadLinearization(std::span<const DepGraphIndex> old_linearization) noexcept
951 {
952 // Add transactions one by one, in order of existing linearization.
953 for (DepGraphIndex tx : old_linearization) {
954 auto chunk_rep = m_tx_data[tx].chunk_rep;
955 // Merge the chunk upwards, as long as merging succeeds.
956 while (true) {
957 chunk_rep = MergeStep<false>(chunk_rep);
958 if (chunk_rep == TxIdx(-1)) break;
959 }
960 }
961 }
962
964 void MakeTopological() noexcept
965 {
966 for (auto tx : m_transaction_idxs) {
967 auto& tx_data = m_tx_data[tx];
968 if (tx_data.chunk_rep == tx) {
970 // Randomize the initial order of suboptimal chunks in the queue.
972 if (j != m_suboptimal_chunks.size() - 1) {
974 }
975 }
976 }
977 while (!m_suboptimal_chunks.empty()) {
978 // Pop an entry from the potentially-suboptimal chunk queue.
981 auto& chunk_data = m_tx_data[chunk];
982 // If what was popped is not currently a chunk representative, continue. This may
983 // happen when it was merged with something else since being added.
984 if (chunk_data.chunk_rep != chunk) continue;
985 int flip = m_rng.randbool();
986 for (int i = 0; i < 2; ++i) {
987 if (i ^ flip) {
988 // Attempt to merge the chunk upwards.
989 auto result_up = MergeStep<false>(chunk);
990 if (result_up != TxIdx(-1)) {
992 break;
993 }
994 } else {
995 // Attempt to merge the chunk downwards.
996 auto result_down = MergeStep<true>(chunk);
997 if (result_down != TxIdx(-1)) {
998 m_suboptimal_chunks.push_back(result_down);
999 break;
1000 }
1001 }
1002 }
1003 }
1004 }
1005
1007 void StartOptimizing() noexcept
1008 {
1009 // Mark chunks suboptimal.
1010 for (auto tx : m_transaction_idxs) {
1011 auto& tx_data = m_tx_data[tx];
1012 if (tx_data.chunk_rep == tx) {
1014 // Randomize the initial order of suboptimal chunks in the queue.
1016 if (j != m_suboptimal_chunks.size() - 1) {
1018 }
1019 }
1020 }
1021 }
1022
1024 bool OptimizeStep() noexcept
1025 {
1026 while (!m_suboptimal_chunks.empty()) {
1027 // Pop an entry from the potentially-suboptimal chunk queue.
1030 auto& chunk_data = m_tx_data[chunk];
1031 // If what was popped is not currently a chunk representative, continue. This may
1032 // happen when a split chunk merges in Improve() with one or more existing chunks that
1033 // are themselves on the suboptimal queue already.
1034 if (chunk_data.chunk_rep != chunk) continue;
1035 // Remember the best dependency seen so far.
1036 DepIdx candidate_dep = DepIdx(-1);
1037 uint64_t candidate_tiebreak = 0;
1038 // Iterate over all transactions.
1039 for (auto tx : chunk_data.chunk_setinfo.transactions) {
1040 const auto& tx_data = m_tx_data[tx];
1041 // Iterate over all active child dependencies of the transaction.
1042 const auto children = std::span{tx_data.child_deps};
1043 for (DepIdx dep_idx : children) {
1044 const auto& dep_data = m_dep_data[dep_idx];
1045 if (!dep_data.active) continue;
1046 // Skip if this dependency is ineligible (the top chunk that would be created
1047 // does not have higher feerate than the chunk it is currently part of).
1048 auto cmp = FeeRateCompare(dep_data.top_setinfo.feerate, chunk_data.chunk_setinfo.feerate);
1049 if (cmp <= 0) continue;
1050 // Generate a random tiebreak for this dependency, and reject it if its tiebreak
1051 // is worse than the best so far. This means that among all eligible
1052 // dependencies, a uniformly random one will be chosen.
1053 uint64_t tiebreak = m_rng.rand64();
1054 if (tiebreak < candidate_tiebreak) continue;
1055 // Remember this as our (new) candidate dependency.
1056 candidate_dep = dep_idx;
1057 candidate_tiebreak = tiebreak;
1058 }
1059 }
1060 // If a candidate with positive gain was found, deactivate it and then make the state
1061 // topological again with a sequence of merges.
1062 if (candidate_dep != DepIdx(-1)) Improve(candidate_dep);
1063 // Stop processing for now, even if nothing was activated, as the loop above may have
1064 // had a nontrivial cost.
1065 return !m_suboptimal_chunks.empty();
1066 }
1067 // No improvable chunk was found, we are done.
1068 return false;
1069 }
1070
1073 std::vector<DepGraphIndex> GetLinearization() noexcept
1074 {
1076 std::vector<DepGraphIndex> ret;
1077 ret.reserve(m_transaction_idxs.Count());
1080 std::vector<std::pair<TxIdx, uint64_t>> ready_chunks;
1087 std::vector<std::pair<TxIdx, TxIdx>> chunk_deps(m_tx_data.size(), {0, 0});
1089 SetType chunk_reps;
1091 std::vector<TxIdx> ready_tx;
1092 // Populate chunk_deps[c] with the number of {out-of-chunk dependencies, dependencies} the
1093 // child has.
1094 for (TxIdx chl_idx : m_transaction_idxs) {
1095 const auto& chl_data = m_tx_data[chl_idx];
1096 chunk_deps[chl_idx].second = chl_data.parents.Count();
1097 auto chl_chunk_rep = chl_data.chunk_rep;
1098 chunk_reps.Set(chl_chunk_rep);
1099 for (auto par_idx : chl_data.parents) {
1100 auto par_chunk_rep = m_tx_data[par_idx].chunk_rep;
1101 chunk_deps[chl_chunk_rep].first += (par_chunk_rep != chl_chunk_rep);
1102 }
1103 }
1104 // Construct a heap with all chunks that have no out-of-chunk dependencies.
1106 auto chunk_cmp_fn = [&](const std::pair<TxIdx, uint64_t>& a, const std::pair<TxIdx, uint64_t>& b) noexcept {
1107 auto& chunk_a = m_tx_data[a.first];
1108 auto& chunk_b = m_tx_data[b.first];
1109 Assume(chunk_a.chunk_rep == a.first);
1110 Assume(chunk_b.chunk_rep == b.first);
1111 // First sort by chunk feerate.
1112 if (chunk_a.chunk_setinfo.feerate != chunk_b.chunk_setinfo.feerate) {
1113 return chunk_a.chunk_setinfo.feerate < chunk_b.chunk_setinfo.feerate;
1114 }
1115 // Tie-break randomly.
1116 if (a.second != b.second) return a.second < b.second;
1117 // Lastly, tie-break by chunk representative.
1118 return a.first < b.first;
1119 };
1120 for (TxIdx chunk_rep : chunk_reps) {
1121 if (chunk_deps[chunk_rep].first == 0) ready_chunks.emplace_back(chunk_rep, m_rng.rand64());
1122 }
1123 std::make_heap(ready_chunks.begin(), ready_chunks.end(), chunk_cmp_fn);
1124 // Pop chunks off the heap, highest-feerate ones first.
1125 while (!ready_chunks.empty()) {
1126 auto [chunk_rep, _rnd] = ready_chunks.front();
1127 std::pop_heap(ready_chunks.begin(), ready_chunks.end(), chunk_cmp_fn);
1128 ready_chunks.pop_back();
1129 Assume(m_tx_data[chunk_rep].chunk_rep == chunk_rep);
1130 Assume(chunk_deps[chunk_rep].first == 0);
1131 const auto& chunk_txn = m_tx_data[chunk_rep].chunk_setinfo.transactions;
1132 // Build heap of all includable transactions in chunk.
1133 for (TxIdx tx_idx : chunk_txn) {
1134 if (chunk_deps[tx_idx].second == 0) {
1135 ready_tx.push_back(tx_idx);
1136 }
1137 }
1138 Assume(!ready_tx.empty());
1139 // Pick transactions from the ready queue, append them to linearization, and decrement
1140 // dependency counts.
1141 while (!ready_tx.empty()) {
1142 // Move a random queue element to the back.
1143 auto pos = m_rng.randrange(ready_tx.size());
1144 if (pos != ready_tx.size() - 1) std::swap(ready_tx.back(), ready_tx[pos]);
1145 // Pop from the back.
1146 auto tx_idx = ready_tx.back();
1147 Assume(chunk_txn[tx_idx]);
1148 ready_tx.pop_back();
1149 // Append to linearization.
1150 ret.push_back(tx_idx);
1151 // Decrement dependency counts.
1152 auto& tx_data = m_tx_data[tx_idx];
1153 for (TxIdx chl_idx : tx_data.children) {
1154 auto& chl_data = m_tx_data[chl_idx];
1155 // Decrement tx dependency count.
1156 Assume(chunk_deps[chl_idx].second > 0);
1157 if (--chunk_deps[chl_idx].second == 0 && chunk_txn[chl_idx]) {
1158 // Child tx has no dependencies left, and is in this chunk. Add it to the tx queue.
1159 ready_tx.push_back(chl_idx);
1160 }
1161 // Decrement chunk dependency count if this is out-of-chunk dependency.
1162 if (chl_data.chunk_rep != chunk_rep) {
1163 Assume(chunk_deps[chl_data.chunk_rep].first > 0);
1164 if (--chunk_deps[chl_data.chunk_rep].first == 0) {
1165 // Child chunk has no dependencies left. Add it to the chunk heap.
1166 ready_chunks.emplace_back(chl_data.chunk_rep, m_rng.rand64());
1167 std::push_heap(ready_chunks.begin(), ready_chunks.end(), chunk_cmp_fn);
1168 }
1169 }
1170 }
1171 }
1172 }
1173 Assume(ret.size() == m_transaction_idxs.Count());
1174 return ret;
1175 }
1176
1186 std::vector<FeeFrac> GetDiagram() const noexcept
1187 {
1188 std::vector<FeeFrac> ret;
1189 for (auto tx : m_transaction_idxs) {
1190 if (m_tx_data[tx].chunk_rep == tx) {
1191 ret.push_back(m_tx_data[tx].chunk_setinfo.feerate);
1192 }
1193 }
1194 std::sort(ret.begin(), ret.end(), std::greater{});
1195 return ret;
1196 }
1197
1199 uint64_t GetCost() const noexcept { return m_cost; }
1200
1202 void SanityCheck(const DepGraph<SetType>& depgraph) const
1203 {
1204 //
1205 // Verify dependency parent/child information, and build list of (active) dependencies.
1206 //
1207 std::vector<std::pair<TxIdx, TxIdx>> expected_dependencies;
1208 std::vector<std::tuple<TxIdx, TxIdx, DepIdx>> all_dependencies;
1209 std::vector<std::tuple<TxIdx, TxIdx, DepIdx>> active_dependencies;
1210 for (auto parent_idx : depgraph.Positions()) {
1211 for (auto child_idx : depgraph.GetReducedChildren(parent_idx)) {
1212 expected_dependencies.emplace_back(parent_idx, child_idx);
1213 }
1214 }
1215 for (DepIdx dep_idx = 0; dep_idx < m_dep_data.size(); ++dep_idx) {
1216 const auto& dep_data = m_dep_data[dep_idx];
1217 all_dependencies.emplace_back(dep_data.parent, dep_data.child, dep_idx);
1218 // Also add to active_dependencies if it is active.
1219 if (m_dep_data[dep_idx].active) {
1220 active_dependencies.emplace_back(dep_data.parent, dep_data.child, dep_idx);
1221 }
1222 }
1223 std::sort(expected_dependencies.begin(), expected_dependencies.end());
1224 std::sort(all_dependencies.begin(), all_dependencies.end());
1225 assert(expected_dependencies.size() == all_dependencies.size());
1226 for (size_t i = 0; i < expected_dependencies.size(); ++i) {
1227 assert(expected_dependencies[i] ==
1228 std::make_pair(std::get<0>(all_dependencies[i]),
1229 std::get<1>(all_dependencies[i])));
1230 }
1231
1232 //
1233 // Verify the chunks against the list of active dependencies
1234 //
1235 for (auto tx_idx: depgraph.Positions()) {
1236 // Only process chunks for now.
1237 if (m_tx_data[tx_idx].chunk_rep == tx_idx) {
1238 const auto& chunk_data = m_tx_data[tx_idx];
1239 // Verify that transactions in the chunk point back to it. This guarantees
1240 // that chunks are non-overlapping.
1241 for (auto chunk_tx : chunk_data.chunk_setinfo.transactions) {
1242 assert(m_tx_data[chunk_tx].chunk_rep == tx_idx);
1243 }
1244 // Verify the chunk's transaction set: it must contain the representative, and for
1245 // every active dependency, if it contains the parent or child, it must contain
1246 // both. It must have exactly N-1 active dependencies in it, guaranteeing it is
1247 // acyclic.
1248 SetType expected_chunk = SetType::Singleton(tx_idx);
1249 while (true) {
1250 auto old = expected_chunk;
1251 size_t active_dep_count{0};
1252 for (const auto& [par, chl, _dep] : active_dependencies) {
1253 if (expected_chunk[par] || expected_chunk[chl]) {
1254 expected_chunk.Set(par);
1255 expected_chunk.Set(chl);
1256 ++active_dep_count;
1257 }
1258 }
1259 if (old == expected_chunk) {
1260 assert(expected_chunk.Count() == active_dep_count + 1);
1261 break;
1262 }
1263 }
1264 assert(chunk_data.chunk_setinfo.transactions == expected_chunk);
1265 // Verify the chunk's feerate.
1266 assert(chunk_data.chunk_setinfo.feerate ==
1267 depgraph.FeeRate(chunk_data.chunk_setinfo.transactions));
1268 }
1269 }
1270
1271 //
1272 // Verify other transaction data.
1273 //
1274 assert(m_transaction_idxs == depgraph.Positions());
1275 for (auto tx_idx : m_transaction_idxs) {
1276 const auto& tx_data = m_tx_data[tx_idx];
1277 // Verify it has a valid chunk representative, and that chunk includes this
1278 // transaction.
1279 assert(m_tx_data[tx_data.chunk_rep].chunk_rep == tx_data.chunk_rep);
1280 assert(m_tx_data[tx_data.chunk_rep].chunk_setinfo.transactions[tx_idx]);
1281 // Verify parents/children.
1282 assert(tx_data.parents == depgraph.GetReducedParents(tx_idx));
1283 assert(tx_data.children == depgraph.GetReducedChildren(tx_idx));
1284 // Verify list of child dependencies.
1285 std::vector<DepIdx> expected_child_deps;
1286 for (const auto& [par_idx, chl_idx, dep_idx] : all_dependencies) {
1287 if (tx_idx == par_idx) {
1288 assert(tx_data.children[chl_idx]);
1289 expected_child_deps.push_back(dep_idx);
1290 }
1291 }
1292 std::sort(expected_child_deps.begin(), expected_child_deps.end());
1293 auto child_deps_copy = tx_data.child_deps;
1294 std::sort(child_deps_copy.begin(), child_deps_copy.end());
1295 assert(expected_child_deps == child_deps_copy);
1296 }
1297
1298 //
1299 // Verify active dependencies' top_setinfo.
1300 //
1301 for (const auto& [par_idx, chl_idx, dep_idx] : active_dependencies) {
1302 const auto& dep_data = m_dep_data[dep_idx];
1303 // Verify the top_info's transactions: it must contain the parent, and for every
1304 // active dependency, except dep_idx itself, if it contains the parent or child, it
1305 // must contain both.
1306 SetType expected_top = SetType::Singleton(par_idx);
1307 while (true) {
1308 auto old = expected_top;
1309 for (const auto& [par2_idx, chl2_idx, dep2_idx] : active_dependencies) {
1310 if (dep2_idx != dep_idx && (expected_top[par2_idx] || expected_top[chl2_idx])) {
1311 expected_top.Set(par2_idx);
1312 expected_top.Set(chl2_idx);
1313 }
1314 }
1315 if (old == expected_top) break;
1316 }
1317 assert(!expected_top[chl_idx]);
1318 assert(dep_data.top_setinfo.transactions == expected_top);
1319 // Verify the top_info's feerate.
1320 assert(dep_data.top_setinfo.feerate ==
1321 depgraph.FeeRate(dep_data.top_setinfo.transactions));
1322 }
1323
1324 //
1325 // Verify m_suboptimal_chunks.
1326 //
1327 for (size_t i = 0; i < m_suboptimal_chunks.size(); ++i) {
1328 auto tx_idx = m_suboptimal_chunks[i];
1329 assert(m_transaction_idxs[tx_idx]);
1330 }
1331 }
1332};
1333
1350template<typename SetType>
1351std::tuple<std::vector<DepGraphIndex>, bool, uint64_t> Linearize(const DepGraph<SetType>& depgraph, uint64_t max_iterations, uint64_t rng_seed, std::span<const DepGraphIndex> old_linearization = {}) noexcept
1352{
1354 SpanningForestState forest(depgraph, rng_seed);
1355 if (!old_linearization.empty()) {
1356 forest.LoadLinearization(old_linearization);
1357 } else {
1358 forest.MakeTopological();
1359 }
1360 // Make improvement steps to it until we hit the max_iterations limit, or an optimal result
1361 // is found.
1362 bool optimal = false;
1363 if (forest.GetCost() < max_iterations) {
1364 forest.StartOptimizing();
1365 do {
1366 if (!forest.OptimizeStep()) {
1367 optimal = true;
1368 break;
1369 }
1370 } while (forest.GetCost() < max_iterations);
1371 }
1372 return {forest.GetLinearization(), optimal, forest.GetCost()};
1373}
1374
1391template<typename SetType>
1392void PostLinearize(const DepGraph<SetType>& depgraph, std::span<DepGraphIndex> linearization)
1393{
1394 // This algorithm performs a number of passes (currently 2); the even ones operate from back to
1395 // front, the odd ones from front to back. Each results in an equal-or-better linearization
1396 // than the one started from.
1397 // - One pass in either direction guarantees that the resulting chunks are connected.
1398 // - Each direction corresponds to one shape of tree being linearized optimally (forward passes
1399 // guarantee this for graphs where each transaction has at most one child; backward passes
1400 // guarantee this for graphs where each transaction has at most one parent).
1401 // - Starting with a backward pass guarantees the moved-tree property.
1402 //
1403 // During an odd (forward) pass, the high-level operation is:
1404 // - Start with an empty list of groups L=[].
1405 // - For every transaction i in the old linearization, from front to back:
1406 // - Append a new group C=[i], containing just i, to the back of L.
1407 // - While L has at least one group before C, and the group immediately before C has feerate
1408 // lower than C:
1409 // - If C depends on P:
1410 // - Merge P into C, making C the concatenation of P+C, continuing with the combined C.
1411 // - Otherwise:
1412 // - Swap P with C, continuing with the now-moved C.
1413 // - The output linearization is the concatenation of the groups in L.
1414 //
1415 // During even (backward) passes, i iterates from the back to the front of the existing
1416 // linearization, and new groups are prepended instead of appended to the list L. To enable
1417 // more code reuse, both passes append groups, but during even passes the meanings of
1418 // parent/child, and of high/low feerate are reversed, and the final concatenation is reversed
1419 // on output.
1420 //
1421 // In the implementation below, the groups are represented by singly-linked lists (pointing
1422 // from the back to the front), which are themselves organized in a singly-linked circular
1423 // list (each group pointing to its predecessor, with a special sentinel group at the front
1424 // that points back to the last group).
1425 //
1426 // Information about transaction t is stored in entries[t + 1], while the sentinel is in
1427 // entries[0].
1428
1430 static constexpr DepGraphIndex SENTINEL{0};
1432 static constexpr DepGraphIndex NO_PREV_TX{0};
1433
1434
1436 struct TxEntry
1437 {
1440 DepGraphIndex prev_tx;
1441
1442 // The fields below are only used for transactions that are the last one in a group
1443 // (referred to as tail transactions below).
1444
1446 DepGraphIndex first_tx;
1449 DepGraphIndex prev_group;
1451 SetType group;
1453 SetType deps;
1455 FeeFrac feerate;
1456 };
1457
1458 // As an example, consider the state corresponding to the linearization [1,0,3,2], with
1459 // groups [1,0,3] and [2], in an odd pass. The linked lists would be:
1460 //
1461 // +-----+
1462 // 0<-P-- | 0 S | ---\ Legend:
1463 // +-----+ |
1464 // ^ | - digit in box: entries index
1465 // /--------------F---------+ G | (note: one more than tx value)
1466 // v \ | | - S: sentinel group
1467 // +-----+ +-----+ +-----+ | (empty feerate)
1468 // 0<-P-- | 2 | <--P-- | 1 | <--P-- | 4 T | | - T: tail transaction, contains
1469 // +-----+ +-----+ +-----+ | fields beyond prev_tv.
1470 // ^ | - P: prev_tx reference
1471 // G G - F: first_tx reference
1472 // | | - G: prev_group reference
1473 // +-----+ |
1474 // 0<-P-- | 3 T | <--/
1475 // +-----+
1476 // ^ |
1477 // \-F-/
1478 //
1479 // During an even pass, the diagram above would correspond to linearization [2,3,0,1], with
1480 // groups [2] and [3,0,1].
1481
1482 std::vector<TxEntry> entries(depgraph.PositionRange() + 1);
1483
1484 // Perform two passes over the linearization.
1485 for (int pass = 0; pass < 2; ++pass) {
1486 int rev = !(pass & 1);
1487 // Construct a sentinel group, identifying the start of the list.
1488 entries[SENTINEL].prev_group = SENTINEL;
1489 Assume(entries[SENTINEL].feerate.IsEmpty());
1490
1491 // Iterate over all elements in the existing linearization.
1492 for (DepGraphIndex i = 0; i < linearization.size(); ++i) {
1493 // Even passes are from back to front; odd passes from front to back.
1494 DepGraphIndex idx = linearization[rev ? linearization.size() - 1 - i : i];
1495 // Construct a new group containing just idx. In even passes, the meaning of
1496 // parent/child and high/low feerate are swapped.
1497 DepGraphIndex cur_group = idx + 1;
1498 entries[cur_group].group = SetType::Singleton(idx);
1499 entries[cur_group].deps = rev ? depgraph.Descendants(idx): depgraph.Ancestors(idx);
1500 entries[cur_group].feerate = depgraph.FeeRate(idx);
1501 if (rev) entries[cur_group].feerate.fee = -entries[cur_group].feerate.fee;
1502 entries[cur_group].prev_tx = NO_PREV_TX; // No previous transaction in group.
1503 entries[cur_group].first_tx = cur_group; // Transaction itself is first of group.
1504 // Insert the new group at the back of the groups linked list.
1505 entries[cur_group].prev_group = entries[SENTINEL].prev_group;
1506 entries[SENTINEL].prev_group = cur_group;
1507
1508 // Start merge/swap cycle.
1509 DepGraphIndex next_group = SENTINEL; // We inserted at the end, so next group is sentinel.
1510 DepGraphIndex prev_group = entries[cur_group].prev_group;
1511 // Continue as long as the current group has higher feerate than the previous one.
1512 while (entries[cur_group].feerate >> entries[prev_group].feerate) {
1513 // prev_group/cur_group/next_group refer to (the last transactions of) 3
1514 // consecutive entries in groups list.
1515 Assume(cur_group == entries[next_group].prev_group);
1516 Assume(prev_group == entries[cur_group].prev_group);
1517 // The sentinel has empty feerate, which is neither higher or lower than other
1518 // feerates. Thus, the while loop we are in here guarantees that cur_group and
1519 // prev_group are not the sentinel.
1520 Assume(cur_group != SENTINEL);
1521 Assume(prev_group != SENTINEL);
1522 if (entries[cur_group].deps.Overlaps(entries[prev_group].group)) {
1523 // There is a dependency between cur_group and prev_group; merge prev_group
1524 // into cur_group. The group/deps/feerate fields of prev_group remain unchanged
1525 // but become unused.
1526 entries[cur_group].group |= entries[prev_group].group;
1527 entries[cur_group].deps |= entries[prev_group].deps;
1528 entries[cur_group].feerate += entries[prev_group].feerate;
1529 // Make the first of the current group point to the tail of the previous group.
1530 entries[entries[cur_group].first_tx].prev_tx = prev_group;
1531 // The first of the previous group becomes the first of the newly-merged group.
1532 entries[cur_group].first_tx = entries[prev_group].first_tx;
1533 // The previous group becomes whatever group was before the former one.
1534 prev_group = entries[prev_group].prev_group;
1535 entries[cur_group].prev_group = prev_group;
1536 } else {
1537 // There is no dependency between cur_group and prev_group; swap them.
1538 DepGraphIndex preprev_group = entries[prev_group].prev_group;
1539 // If PP, P, C, N were the old preprev, prev, cur, next groups, then the new
1540 // layout becomes [PP, C, P, N]. Update prev_groups to reflect that order.
1541 entries[next_group].prev_group = prev_group;
1542 entries[prev_group].prev_group = cur_group;
1543 entries[cur_group].prev_group = preprev_group;
1544 // The current group remains the same, but the groups before/after it have
1545 // changed.
1546 next_group = prev_group;
1547 prev_group = preprev_group;
1548 }
1549 }
1550 }
1551
1552 // Convert the entries back to linearization (overwriting the existing one).
1553 DepGraphIndex cur_group = entries[0].prev_group;
1554 DepGraphIndex done = 0;
1555 while (cur_group != SENTINEL) {
1556 DepGraphIndex cur_tx = cur_group;
1557 // Traverse the transactions of cur_group (from back to front), and write them in the
1558 // same order during odd passes, and reversed (front to back) in even passes.
1559 if (rev) {
1560 do {
1561 *(linearization.begin() + (done++)) = cur_tx - 1;
1562 cur_tx = entries[cur_tx].prev_tx;
1563 } while (cur_tx != NO_PREV_TX);
1564 } else {
1565 do {
1566 *(linearization.end() - (++done)) = cur_tx - 1;
1567 cur_tx = entries[cur_tx].prev_tx;
1568 } while (cur_tx != NO_PREV_TX);
1569 }
1570 cur_group = entries[cur_group].prev_group;
1571 }
1572 Assume(done == linearization.size());
1573 }
1574}
1575
1577template<typename SetType>
1578void FixLinearization(const DepGraph<SetType>& depgraph, std::span<DepGraphIndex> linearization) noexcept
1579{
1580 // This algorithm can be summarized as moving every element in the linearization backwards
1581 // until it is placed after all its ancestors.
1582 SetType done;
1583 const auto len = linearization.size();
1584 // Iterate over the elements of linearization from back to front (i is distance from back).
1585 for (DepGraphIndex i = 0; i < len; ++i) {
1587 DepGraphIndex elem = linearization[len - 1 - i];
1589 DepGraphIndex j = i;
1590 // Figure out which elements need to be moved before elem.
1591 SetType place_before = done & depgraph.Ancestors(elem);
1592 // Find which position to place elem in (updating j), continuously moving the elements
1593 // in between forward.
1594 while (place_before.Any()) {
1595 // j cannot be 0 here; if it was, then there was necessarily nothing earlier which
1596 // elem needs to be placed before anymore, and place_before would be empty.
1597 Assume(j > 0);
1598 auto to_swap = linearization[len - 1 - (j - 1)];
1599 place_before.Reset(to_swap);
1600 linearization[len - 1 - (j--)] = to_swap;
1601 }
1602 // Put elem in its final position and mark it as done.
1603 linearization[len - 1 - j] = elem;
1604 done.Set(elem);
1605 }
1606}
1607
1608} // namespace cluster_linearize
1609
1610#endif // BITCOIN_CLUSTER_LINEARIZE_H
int ret
#define Assume(val)
Assume is the identity function.
Definition: check.h:125
xoroshiro128++ PRNG.
Definition: random.h:425
constexpr uint64_t rand64() noexcept
Definition: random.h:448
I randrange(I range) noexcept
Generate a random integer in the range [0..range), with range > 0.
Definition: random.h:254
bool randbool() noexcept
Generate a random boolean.
Definition: random.h:325
bool empty() const noexcept
Test whether the contents of this deque is empty.
Definition: vecdeque.h:310
void push_back(T &&elem)
Move-construct a new element at the end of the deque.
Definition: vecdeque.h:227
void pop_front()
Remove the first element of the deque.
Definition: vecdeque.h:250
size_t size() const noexcept
Get the number of elements in this deque.
Definition: vecdeque.h:312
void emplace_back(Args &&... args)
Construct a new element at the end of the deque.
Definition: vecdeque.h:219
T & front() noexcept
Get a mutable reference to the first element of the deque.
Definition: vecdeque.h:268
T & back() noexcept
Get a mutable reference to the last element of the deque.
Definition: vecdeque.h:282
Data structure that holds a transaction graph's preprocessed data (fee, size, ancestors,...
unsigned CountDependencies() const noexcept
const SetType & Ancestors(DepGraphIndex i) const noexcept
Get the ancestors of a given transaction i.
FeeFrac & FeeRate(DepGraphIndex i) noexcept
Get the mutable feerate of a given transaction i.
SetType GetReducedChildren(DepGraphIndex i) const noexcept
Compute the (reduced) set of children of node i in this graph.
SetType GetReducedParents(DepGraphIndex i) const noexcept
Compute the (reduced) set of parents of node i in this graph.
void AppendTopo(std::vector< DepGraphIndex > &list, const SetType &select) const noexcept
Append the entries of select to list in a topologically valid order.
const FeeFrac & FeeRate(DepGraphIndex i) const noexcept
Get the feerate of a given transaction i.
SetType GetConnectedComponent(const SetType &todo, DepGraphIndex tx) const noexcept
Get the connected component within the subset "todo" that contains tx (which must be in todo).
bool IsConnected() const noexcept
Determine if this entire graph is connected.
bool IsConnected(const SetType &subset) const noexcept
Determine if a subset is connected.
DepGraphIndex PositionRange() const noexcept
Get the range of positions in this DepGraph.
SetType FindConnectedComponent(const SetType &todo) const noexcept
Find some connected component within the subset "todo" of this graph.
DepGraphIndex AddTransaction(const FeeFrac &feefrac) noexcept
Add a new unconnected transaction to this transaction graph (in the first available position),...
void RemoveTransactions(const SetType &del) noexcept
Remove the specified positions from this DepGraph.
auto TxCount() const noexcept
Get the number of transactions in the graph.
std::vector< Entry > entries
Data for each transaction.
const SetType & Descendants(DepGraphIndex i) const noexcept
Get the descendants of a given transaction i.
friend bool operator==(const DepGraph &a, const DepGraph &b) noexcept
Equality operator (primarily for testing purposes).
size_t DynamicMemoryUsage() const noexcept
DepGraph() noexcept=default
SetType m_used
Which positions are used.
const SetType & Positions() const noexcept
Get the set of transactions positions in use.
void Compact() noexcept
Reduce memory usage if possible.
FeeFrac FeeRate(const SetType &elems) const noexcept
Compute the aggregate feerate of a set of nodes in this graph.
void AddDependencies(const SetType &parents, DepGraphIndex child) noexcept
Modify this transaction graph, adding multiple parents to a specified child.
bool IsAcyclic() const noexcept
Check if this graph is acyclic.
Class to represent the internal state of the spanning-forest linearization (SFL) algorithm.
void UpdateChunk(const SetType &chunk, TxIdx query, TxIdx chunk_rep, const SetInfo< SetType > &dep_change) noexcept
Update a chunk:
std::vector< TxData > m_tx_data
Information about each transaction (and chunks).
InsecureRandomContext m_rng
Internal RNG.
bool OptimizeStep() noexcept
Try to improve the forest.
uint64_t GetCost() const noexcept
Determine how much work was performed so far.
TxIdx Activate(DepIdx dep_idx) noexcept
Make a specified inactive dependency active.
void Improve(DepIdx dep_idx) noexcept
Split a chunk, and then merge the resulting two chunks to make the graph topological again.
std::vector< FeeFrac > GetDiagram() const noexcept
Get the diagram for the current state, which must be topological.
std::vector< DepData > m_dep_data
Information about each dependency.
void MakeTopological() noexcept
Make state topological.
TxIdx MergeChunks(TxIdx top_rep, TxIdx bottom_rep) noexcept
Activate a dependency from the chunk represented by bottom_rep to the chunk represented by top_rep,...
uint32_t TxIdx
Data type to represent indexing into m_tx_data.
TxIdx MergeStep(TxIdx chunk_rep) noexcept
Perform an upward or downward merge step, on the specified chunk representative.
void SanityCheck(const DepGraph< SetType > &depgraph) const
Verify internal consistency of the data structure.
SpanningForestState(const DepGraph< SetType > &depgraph, uint64_t rng_seed) noexcept
Construct a spanning forest for the given DepGraph, with every transaction in its own chunk (not topo...
void StartOptimizing() noexcept
Initialize the data structure for optimization.
VecDeque< TxIdx > m_suboptimal_chunks
A FIFO of chunk representatives of chunks that may be improved still.
void LoadLinearization(std::span< const DepGraphIndex > old_linearization) noexcept
Load an existing linearization.
std::vector< DepGraphIndex > GetLinearization() noexcept
Construct a topologically-valid linearization from the current forest state.
uint32_t DepIdx
Data type to represent indexing into m_dep_data.
SetType m_transaction_idxs
The set of all TxIdx's of transactions in the cluster indexing into m_tx_data.
void MergeSequence(TxIdx tx_idx) noexcept
Perform an upward or downward merge sequence on the specified transaction.
uint64_t m_cost
The number of updated transactions in activations/deactivations.
void Deactivate(DepIdx dep_idx) noexcept
Make a specified active dependency inactive.
std::vector< FeeFrac > ChunkLinearization(const DepGraph< SetType > &depgraph, std::span< const DepGraphIndex > linearization) noexcept
Compute the feerates of the chunks of linearization.
std::tuple< std::vector< DepGraphIndex >, bool, uint64_t > Linearize(const DepGraph< SetType > &depgraph, uint64_t max_iterations, uint64_t rng_seed, std::span< const DepGraphIndex > old_linearization={}) noexcept
Find or improve a linearization for a cluster.
void FixLinearization(const DepGraph< SetType > &depgraph, std::span< DepGraphIndex > linearization) noexcept
Make linearization topological, retaining its ordering where possible.
uint32_t DepGraphIndex
Data type to represent transaction indices in DepGraphs and the clusters they represent.
void PostLinearize(const DepGraph< SetType > &depgraph, std::span< DepGraphIndex > linearization)
Improve a given linearization.
std::vector< SetInfo< SetType > > ChunkLinearizationInfo(const DepGraph< SetType > &depgraph, std::span< const DepGraphIndex > linearization) noexcept
Compute the chunks of linearization as SetInfos.
static size_t DynamicUsage(const int8_t &v)
Dynamic memory usage for built-in types is zero.
Definition: memusage.h:31
Data structure storing a fee and size, ordered by increasing fee/size.
Definition: feefrac.h:40
int64_t fee
Definition: feefrac.h:107
bool IsEmpty() const noexcept
Check if this is empty (size and fee are 0).
Definition: feefrac.h:120
Information about a single transaction.
SetType descendants
All descendants of the transaction (including itself).
friend bool operator==(const Entry &, const Entry &) noexcept=default
Equality operator (primarily for testing purposes).
Entry() noexcept=default
Construct an empty entry.
FeeFrac feerate
Fee and size of transaction itself.
SetType ancestors
All ancestors of the transaction (including itself).
A set of transactions together with their aggregate feerate.
SetInfo(const DepGraph< SetType > &depgraph, DepGraphIndex pos) noexcept
Construct a SetInfo for a given transaction in a depgraph.
SetInfo operator-(const SetInfo &other) const noexcept
Compute the difference between this and other SetInfo (which must be a subset).
FeeFrac feerate
Their combined fee and size.
SetInfo() noexcept=default
Construct a SetInfo for the empty set.
void Set(const DepGraph< SetType > &depgraph, DepGraphIndex pos) noexcept
Add a transaction to this SetInfo (which must not yet be in it).
SetType transactions
The transactions in the set.
friend void swap(SetInfo &a, SetInfo &b) noexcept
Swap two SetInfo objects.
SetInfo(const DepGraph< SetType > &depgraph, const SetType &txn) noexcept
Construct a SetInfo for a set of transactions in a depgraph.
SetInfo & operator|=(const SetInfo &other) noexcept
Add the transactions of other to this SetInfo (no overlap allowed).
SetInfo & operator-=(const SetInfo &other) noexcept
Remove the transactions of other from this SetInfo (which must be a subset).
friend bool operator==(const SetInfo &, const SetInfo &) noexcept=default
Permit equality testing.
Structure with information about a single dependency.
bool active
Whether this dependency is active.
SetInfo< SetType > top_setinfo
(Only if this dependency is active) the would-be top chunk and its feerate that would be formed if th...
TxIdx parent
What the parent and child transactions are.
Structure with information about a single transaction.
std::vector< DepIdx > child_deps
The dependencies to children of this transaction.
SetType parents
The set of parent transactions of this transaction.
SetType children
The set of child transactions of this transaction.
TxIdx chunk_rep
Which transaction holds the chunk_setinfo for the chunk this transaction is in (the representative fo...
SetInfo< SetType > chunk_setinfo
(Only if this transaction is the representative for the chunk it is in) the total chunk set and feera...
static int count
assert(!tx.IsCoinBase())