25static constexpr int MAX_LEVELS{2};
31using LinearizationIndex = uint32_t;
33using ClusterSetIndex = uint32_t;
36enum class QualityLevel
44 NEEDS_SPLIT_ACCEPTABLE,
72 uint32_t m_parent_count;
74 uint32_t m_parent_offset;
76 uint32_t m_children_count;
78 uint32_t m_children_offset;
90 TrimTxData* m_uf_parent;
100 friend class TxGraphImpl;
108 std::vector<GraphIndex> m_mapping;
111 std::vector<DepGraphIndex> m_linearization;
115 ClusterSetIndex m_setindex{ClusterSetIndex(-1)};
123 Cluster() noexcept = delete;
125 explicit Cluster(uint64_t
sequence) noexcept;
127 explicit Cluster(uint64_t
sequence, TxGraphImpl& graph, const
FeePerWeight& feerate, GraphIndex graph_index) noexcept;
130 Cluster(const Cluster&) = delete;
131 Cluster& operator=(const Cluster&) = delete;
132 Cluster(Cluster&&) = delete;
133 Cluster& operator=(Cluster&&) = delete;
138 bool IsAcceptable(
bool after_split = false) const noexcept
140 return m_quality == QualityLevel::ACCEPTABLE || m_quality == QualityLevel::OPTIMAL ||
141 (after_split && m_quality == QualityLevel::NEEDS_SPLIT_ACCEPTABLE);
144 bool IsOptimal() const noexcept
146 return m_quality == QualityLevel::OPTIMAL;
151 bool IsOversized() const noexcept {
return m_quality == QualityLevel::OVERSIZED_SINGLETON; }
153 bool NeedsSplitting() const noexcept
155 return m_quality == QualityLevel::NEEDS_SPLIT ||
156 m_quality == QualityLevel::NEEDS_SPLIT_ACCEPTABLE;
159 LinearizationIndex GetTxCount() const noexcept {
return m_linearization.size(); }
161 uint64_t GetTotalTxSize() const noexcept;
163 GraphIndex GetClusterEntry(
DepGraphIndex index) const noexcept {
return m_mapping[index]; }
165 void UpdateMapping(
DepGraphIndex cluster_idx, GraphIndex graph_idx)
noexcept { m_mapping[cluster_idx] = graph_idx; }
167 void Updated(TxGraphImpl& graph)
noexcept;
169 Cluster* CopyToStaging(TxGraphImpl& graph)
const noexcept;
171 void GetConflicts(
const TxGraphImpl& graph, std::vector<Cluster*>&
out)
const noexcept;
174 void MakeStagingTransactionsMissing(TxGraphImpl& graph)
noexcept;
176 void Clear(TxGraphImpl& graph)
noexcept;
178 void MoveToMain(TxGraphImpl& graph)
noexcept;
184 void ApplyRemovals(TxGraphImpl& graph, std::span<GraphIndex>& to_remove)
noexcept;
187 [[nodiscard]]
bool Split(TxGraphImpl& graph)
noexcept;
189 void Merge(TxGraphImpl& graph, Cluster& cluster)
noexcept;
191 void ApplyDependencies(TxGraphImpl& graph, std::span<std::pair<GraphIndex, GraphIndex>> to_apply)
noexcept;
194 std::pair<uint64_t, bool> Relinearize(TxGraphImpl& graph, uint64_t max_iters)
noexcept;
196 void AppendChunkFeerates(std::vector<FeeFrac>&
ret)
const noexcept;
200 uint64_t AppendTrimData(std::vector<TrimTxData>&
ret, std::vector<std::pair<GraphIndex, GraphIndex>>& deps)
const noexcept;
206 void GetAncestorRefs(
const TxGraphImpl& graph, std::span<std::pair<Cluster*, DepGraphIndex>>&
args, std::vector<TxGraph::Ref*>& output)
noexcept;
209 void GetDescendantRefs(
const TxGraphImpl& graph, std::span<std::pair<Cluster*, DepGraphIndex>>&
args, std::vector<TxGraph::Ref*>& output)
noexcept;
213 bool GetClusterRefs(TxGraphImpl& graph, std::span<TxGraph::Ref*> range, LinearizationIndex start_pos)
noexcept;
221 void SanityCheck(
const TxGraphImpl& graph,
int level)
const;
248class TxGraphImpl final :
public TxGraph
250 friend class Cluster;
251 friend class BlockBuilderImpl;
258 const uint64_t m_max_cluster_size;
261 const uint64_t m_acceptable_iters;
267 uint32_t m_cluster_offset;
269 uint32_t m_cluster_count;
271 uint32_t m_deps_offset;
273 uint32_t m_deps_count;
280 std::vector<GroupEntry> m_groups;
282 std::vector<Cluster*> m_group_clusters;
289 std::array<std::vector<std::unique_ptr<Cluster>>, int(
QualityLevel::NONE)> m_clusters;
291 std::vector<GraphIndex> m_to_remove;
294 std::vector<std::pair<GraphIndex, GraphIndex>> m_deps_to_add;
296 std::optional<GroupData> m_group_data = GroupData{};
300 std::vector<GraphIndex> m_removed;
303 GraphIndex m_txcount{0};
305 GraphIndex m_txcount_oversized{0};
307 std::optional<bool> m_oversized{
false};
309 ClusterSet() noexcept = default;
313 ClusterSet m_main_clusterset;
315 std::optional<ClusterSet> m_staging_clusterset;
317 uint64_t m_next_sequence_counter{0};
323 mutable GraphIndex m_graph_index;
325 LinearizationIndex m_chunk_count;
327 ChunkData(GraphIndex graph_index, LinearizationIndex chunk_count) noexcept :
328 m_graph_index{graph_index}, m_chunk_count{chunk_count} {}
332 static std::strong_ordering CompareClusters(Cluster* a, Cluster* b)
noexcept
335 if (a ==
nullptr || b ==
nullptr) {
336 return (a !=
nullptr) <=> (b !=
nullptr);
339 Assume(a == b || a->m_sequence != b->m_sequence);
340 return a->m_sequence <=> b->m_sequence;
344 std::strong_ordering CompareMainTransactions(GraphIndex a, GraphIndex b)
const noexcept
346 Assume(a < m_entries.size() && b < m_entries.size());
347 const auto& entry_a = m_entries[a];
348 const auto& entry_b = m_entries[b];
350 auto feerate_cmp = FeeRateCompare(entry_b.m_main_chunk_feerate, entry_a.m_main_chunk_feerate);
351 if (feerate_cmp < 0)
return std::strong_ordering::less;
352 if (feerate_cmp > 0)
return std::strong_ordering::greater;
354 const auto& locator_a = entry_a.m_locator[0];
355 const auto& locator_b = entry_b.m_locator[0];
356 Assume(locator_a.IsPresent() && locator_b.IsPresent());
357 if (locator_a.cluster != locator_b.cluster) {
358 return CompareClusters(locator_a.cluster, locator_b.cluster);
361 return entry_a.m_main_lin_index <=> entry_b.m_main_lin_index;
367 const TxGraphImpl*
const m_graph;
369 explicit ChunkOrder(
const TxGraphImpl* graph) : m_graph(graph) {}
371 bool operator()(
const ChunkData& a,
const ChunkData& b)
const noexcept
373 return m_graph->CompareMainTransactions(a.m_graph_index, b.m_graph_index) < 0;
378 using ChunkIndex = std::set<ChunkData, ChunkOrder>;
382 ChunkIndex m_main_chunkindex;
384 size_t m_main_chunkindex_observers{0};
386 std::vector<ChunkIndex::node_type> m_main_chunkindex_discarded;
421 Cluster* cluster{
nullptr};
426 void SetMissing() noexcept { cluster =
nullptr; index = 0; }
428 void SetRemoved() noexcept { cluster =
nullptr; index =
DepGraphIndex(-1); }
430 void SetPresent(Cluster* c,
DepGraphIndex i)
noexcept { cluster = c; index = i; }
432 bool IsMissing() const noexcept {
return cluster ==
nullptr && index == 0; }
434 bool IsRemoved() const noexcept {
return cluster ==
nullptr && index ==
DepGraphIndex(-1); }
436 bool IsPresent() const noexcept {
return cluster !=
nullptr; }
446 ChunkIndex::iterator m_main_chunkindex_iterator;
448 Locator m_locator[MAX_LEVELS];
452 LinearizationIndex m_main_lin_index;
456 std::vector<Entry> m_entries;
459 std::vector<GraphIndex> m_unlinked;
463 explicit TxGraphImpl(
DepGraphIndex max_cluster_count, uint64_t max_cluster_size, uint64_t acceptable_iters) noexcept :
464 m_max_cluster_count(max_cluster_count),
465 m_max_cluster_size(max_cluster_size),
466 m_acceptable_iters(acceptable_iters),
467 m_main_chunkindex(ChunkOrder(
this))
469 Assume(max_cluster_count >= 1);
474 ~TxGraphImpl() noexcept;
477 TxGraphImpl(const TxGraphImpl&) = delete;
478 TxGraphImpl& operator=(const TxGraphImpl&) = delete;
479 TxGraphImpl(TxGraphImpl&&) = delete;
480 TxGraphImpl& operator=(TxGraphImpl&&) = delete;
485 void SwapIndexes(GraphIndex a, GraphIndex b) noexcept;
488 Cluster* FindCluster(GraphIndex idx,
int level) const noexcept;
490 std::unique_ptr<Cluster> ExtractCluster(
int level, QualityLevel quality, ClusterSetIndex setindex) noexcept;
492 void DeleteCluster(Cluster& cluster) noexcept;
494 ClusterSetIndex InsertCluster(
int level,
std::unique_ptr<Cluster>&& cluster, QualityLevel quality) noexcept;
496 void SetClusterQuality(
int level, QualityLevel old_quality, ClusterSetIndex old_index, QualityLevel new_quality) noexcept;
498 int GetTopLevel() const noexcept {
return m_staging_clusterset.has_value(); }
500 int GetSpecifiedLevel(
bool main_only)
const noexcept {
return m_staging_clusterset.has_value() && !main_only; }
502 ClusterSet& GetClusterSet(
int level)
noexcept;
503 const ClusterSet& GetClusterSet(
int level)
const noexcept;
507 void ClearLocator(
int level, GraphIndex index,
bool oversized_tx)
noexcept;
509 std::vector<Cluster*> GetConflicts() const noexcept;
511 void ClearChunkData(Entry& entry) noexcept;
513 void CreateChunkData(GraphIndex idx, LinearizationIndex chunk_count) noexcept;
518 void UpdateRef(GraphIndex idx, Ref& new_location) noexcept final
520 auto& entry = m_entries[idx];
521 Assume(entry.m_ref !=
nullptr);
522 entry.m_ref = &new_location;
526 void UnlinkRef(GraphIndex idx)
noexcept final
528 auto& entry = m_entries[idx];
529 Assume(entry.m_ref !=
nullptr);
530 Assume(m_main_chunkindex_observers == 0 || !entry.m_locator[0].IsPresent());
531 entry.m_ref =
nullptr;
534 bool exists_anywhere{
false};
536 for (
int level = 0; level <= GetTopLevel(); ++level) {
537 if (entry.m_locator[level].IsPresent()) {
538 exists_anywhere =
true;
540 }
else if (entry.m_locator[level].IsRemoved()) {
544 auto& clusterset = GetClusterSet(level);
545 clusterset.m_to_remove.push_back(idx);
547 clusterset.m_group_data = std::nullopt;
553 if (level == GetTopLevel() && clusterset.m_oversized ==
true) {
554 clusterset.m_oversized = std::nullopt;
558 m_unlinked.push_back(idx);
559 if (!exists_anywhere) Compact();
566 void Compact() noexcept;
570 Cluster* PullIn(Cluster* cluster) noexcept;
573 void ApplyRemovals(
int up_to_level) noexcept;
575 void Split(Cluster& cluster) noexcept;
577 void SplitAll(
int up_to_level) noexcept;
579 void GroupClusters(
int level) noexcept;
581 void Merge(
std::span<Cluster*> to_merge) noexcept;
583 void ApplyDependencies(
int level) noexcept;
585 void MakeAcceptable(Cluster& cluster) noexcept;
587 void MakeAllAcceptable(
int level) noexcept;
591 Ref AddTransaction(const
FeePerWeight& feerate) noexcept final;
592 void RemoveTransaction(const Ref& arg) noexcept final;
593 void AddDependency(const Ref& parent, const Ref& child) noexcept final;
594 void SetTransactionFee(const Ref&, int64_t
fee) noexcept final;
596 bool DoWork(uint64_t iters) noexcept final;
598 void StartStaging() noexcept final;
599 void CommitStaging() noexcept final;
600 void AbortStaging() noexcept final;
601 bool HaveStaging() const noexcept final {
return m_staging_clusterset.has_value(); }
603 bool Exists(
const Ref& arg,
bool main_only =
false) noexcept final;
604 FeePerWeight GetMainChunkFeerate(const Ref& arg) noexcept final;
605 FeePerWeight GetIndividualFeerate(const Ref& arg) noexcept final;
606 std::vector<Ref*> GetCluster(const Ref& arg,
bool main_only = false) noexcept final;
607 std::vector<Ref*> GetAncestors(const Ref& arg,
bool main_only = false) noexcept final;
608 std::vector<Ref*> GetDescendants(const Ref& arg,
bool main_only = false) noexcept final;
609 std::vector<Ref*> GetAncestorsUnion(
std::span<const Ref* const>
args,
bool main_only = false) noexcept final;
610 std::vector<Ref*> GetDescendantsUnion(
std::span<const Ref* const>
args,
bool main_only = false) noexcept final;
611 GraphIndex GetTransactionCount(
bool main_only = false) noexcept final;
612 bool IsOversized(
bool main_only = false) noexcept final;
613 std::strong_ordering CompareMainOrder(const Ref& a, const Ref& b) noexcept final;
614 GraphIndex CountDistinctClusters(
std::span<const Ref* const> refs,
bool main_only = false) noexcept final;
616 std::vector<Ref*> Trim() noexcept final;
618 std::unique_ptr<BlockBuilder> GetBlockBuilder() noexcept final;
621 void SanityCheck() const final;
624TxGraphImpl::ClusterSet& TxGraphImpl::GetClusterSet(
int level) noexcept
626 if (level == 0)
return m_main_clusterset;
628 Assume(m_staging_clusterset.has_value());
629 return *m_staging_clusterset;
632const TxGraphImpl::ClusterSet& TxGraphImpl::GetClusterSet(
int level)
const noexcept
634 if (level == 0)
return m_main_clusterset;
636 Assume(m_staging_clusterset.has_value());
637 return *m_staging_clusterset;
645 TxGraphImpl*
const m_graph;
647 std::set<Cluster*> m_excluded_clusters;
649 TxGraphImpl::ChunkIndex::const_iterator m_cur_iter;
652 Cluster* m_cur_cluster;
654 bool m_known_end_of_cluster;
657 void Next() noexcept;
661 BlockBuilderImpl(TxGraphImpl& graph) noexcept;
664 ~BlockBuilderImpl() final;
666 void Include() noexcept final;
667 void Skip() noexcept final;
670void TxGraphImpl::ClearChunkData(Entry& entry) noexcept
672 if (entry.m_main_chunkindex_iterator != m_main_chunkindex.end()) {
673 Assume(m_main_chunkindex_observers == 0);
676 m_main_chunkindex_discarded.emplace_back(m_main_chunkindex.extract(entry.m_main_chunkindex_iterator));
677 entry.m_main_chunkindex_iterator = m_main_chunkindex.end();
681void TxGraphImpl::CreateChunkData(GraphIndex idx, LinearizationIndex chunk_count)
noexcept
683 auto& entry = m_entries[idx];
684 if (!m_main_chunkindex_discarded.empty()) {
686 auto&
node = m_main_chunkindex_discarded.back().value();
687 node.m_graph_index = idx;
688 node.m_chunk_count = chunk_count;
689 auto insert_result = m_main_chunkindex.insert(std::move(m_main_chunkindex_discarded.back()));
690 Assume(insert_result.inserted);
691 entry.m_main_chunkindex_iterator = insert_result.position;
692 m_main_chunkindex_discarded.pop_back();
695 auto emplace_result = m_main_chunkindex.emplace(idx, chunk_count);
696 Assume(emplace_result.second);
697 entry.m_main_chunkindex_iterator = emplace_result.first;
701uint64_t Cluster::GetTotalTxSize() const noexcept
704 for (
auto i : m_linearization) {
710void TxGraphImpl::ClearLocator(
int level, GraphIndex idx,
bool oversized_tx)
noexcept
712 auto& entry = m_entries[idx];
713 auto& clusterset = GetClusterSet(level);
714 Assume(entry.m_locator[level].IsPresent());
716 if (level == 0 || !entry.m_locator[level - 1].IsPresent()) {
717 entry.m_locator[level].SetMissing();
719 entry.m_locator[level].SetRemoved();
720 clusterset.m_removed.push_back(idx);
723 --clusterset.m_txcount;
724 clusterset.m_txcount_oversized -= oversized_tx;
726 if (level == 0 && GetTopLevel() == 1) {
727 if (entry.m_locator[1].IsRemoved()) {
728 entry.m_locator[1].SetMissing();
729 }
else if (!entry.m_locator[1].IsPresent()) {
730 --m_staging_clusterset->m_txcount;
731 m_staging_clusterset->m_txcount_oversized -= oversized_tx;
734 if (level == 0) ClearChunkData(entry);
737void Cluster::Updated(TxGraphImpl& graph)
noexcept
741 auto& entry = graph.m_entries[m_mapping[idx]];
744 if (m_level == 0) graph.ClearChunkData(entry);
745 entry.m_locator[m_level].SetPresent(
this, idx);
752 if (m_level == 0 && IsAcceptable()) {
754 LinearizationIndex lin_idx{0};
756 for (
unsigned chunk_idx = 0; chunk_idx < chunking.NumChunksLeft(); ++chunk_idx) {
757 auto chunk = chunking.GetChunk(chunk_idx);
758 auto chunk_count = chunk.transactions.Count();
763 GraphIndex graph_idx = m_mapping[idx];
764 auto& entry = graph.m_entries[graph_idx];
765 entry.m_main_lin_index = lin_idx++;
767 Assume(chunk.transactions[idx]);
768 chunk.transactions.Reset(idx);
769 if (chunk.transactions.None()) {
771 if (chunk_count == 1 && chunk_idx + 1 == chunking.NumChunksLeft()) {
775 chunk_count = LinearizationIndex(-1);
777 graph.CreateChunkData(graph_idx, chunk_count);
785void Cluster::GetConflicts(
const TxGraphImpl& graph, std::vector<Cluster*>&
out)
const noexcept
788 for (
auto i : m_linearization) {
789 auto& entry = graph.m_entries[m_mapping[i]];
792 if (entry.m_locator[0].IsPresent()) {
793 out.push_back(entry.m_locator[0].cluster);
798std::vector<Cluster*> TxGraphImpl::GetConflicts() const noexcept
800 Assume(GetTopLevel() == 1);
801 auto& clusterset = GetClusterSet(1);
802 std::vector<Cluster*>
ret;
804 for (
auto i : clusterset.m_removed) {
805 auto& entry = m_entries[i];
806 if (entry.m_locator[0].IsPresent()) {
807 ret.push_back(entry.m_locator[0].cluster);
812 auto& clusters = clusterset.m_clusters[quality];
813 for (
const auto& cluster : clusters) {
814 cluster->GetConflicts(*
this,
ret);
818 std::sort(
ret.begin(),
ret.end(), [](Cluster* a, Cluster* b)
noexcept { return CompareClusters(a, b) < 0; });
819 ret.erase(std::unique(
ret.begin(),
ret.end()),
ret.end());
823Cluster* Cluster::CopyToStaging(TxGraphImpl& graph)
const noexcept
826 auto ret = std::make_unique<Cluster>(graph.m_next_sequence_counter++);
827 auto ptr =
ret.get();
829 ptr->m_depgraph = m_depgraph;
830 ptr->m_mapping = m_mapping;
831 ptr->m_linearization = m_linearization;
833 graph.InsertCluster(1, std::move(
ret), m_quality);
839void Cluster::ApplyRemovals(TxGraphImpl& graph, std::span<GraphIndex>& to_remove)
noexcept
842 Assume(!to_remove.empty());
845 GraphIndex idx = to_remove.front();
846 Assume(idx < graph.m_entries.size());
847 auto& entry = graph.m_entries[idx];
848 auto& locator = entry.m_locator[m_level];
850 if (locator.cluster !=
this)
break;
852 todo.Set(locator.index);
856 m_mapping[locator.index] = GraphIndex(-1);
859 entry.m_main_lin_index = LinearizationIndex(-1);
862 graph.ClearLocator(m_level, idx, m_quality == QualityLevel::OVERSIZED_SINGLETON);
863 to_remove = to_remove.subspan(1);
864 }
while(!to_remove.empty());
866 auto quality = m_quality;
874 while (!m_linearization.empty() && todo[m_linearization.back()]) {
875 todo.Reset(m_linearization.back());
876 m_linearization.pop_back();
881 if (IsAcceptable(
true)) {
882 quality = QualityLevel::NEEDS_SPLIT_ACCEPTABLE;
884 quality = QualityLevel::NEEDS_SPLIT;
888 m_linearization.erase(std::remove_if(
889 m_linearization.begin(),
890 m_linearization.end(),
891 [&](
auto pos) { return todo[pos]; }), m_linearization.end());
892 quality = QualityLevel::NEEDS_SPLIT;
894 graph.SetClusterQuality(m_level, m_quality, m_setindex, quality);
898void Cluster::Clear(TxGraphImpl& graph)
noexcept
900 for (
auto i : m_linearization) {
901 graph.ClearLocator(m_level, m_mapping[i], m_quality == QualityLevel::OVERSIZED_SINGLETON);
904 m_linearization.clear();
908void Cluster::MoveToMain(TxGraphImpl& graph)
noexcept
911 for (
auto i : m_linearization) {
912 GraphIndex idx = m_mapping[i];
913 auto& entry = graph.m_entries[idx];
914 entry.m_locator[1].SetMissing();
916 auto quality = m_quality;
917 auto cluster = graph.ExtractCluster(1, quality, m_setindex);
918 graph.InsertCluster(0, std::move(cluster), quality);
922void Cluster::AppendChunkFeerates(std::vector<FeeFrac>&
ret)
const noexcept
925 ret.reserve(
ret.size() + chunk_feerates.size());
926 ret.insert(
ret.end(), chunk_feerates.begin(), chunk_feerates.end());
929uint64_t Cluster::AppendTrimData(std::vector<TrimTxData>&
ret, std::vector<std::pair<GraphIndex, GraphIndex>>& deps)
const noexcept
932 LinearizationIndex pos{0};
934 auto prev_index = GraphIndex(-1);
936 for (
unsigned i = 0; i < linchunking.NumChunksLeft(); ++i) {
937 const auto& [chunk, chunk_feerate] = linchunking.GetChunk(i);
939 auto chunk_tx_count = chunk.Count();
940 for (
unsigned j = 0; j < chunk_tx_count; ++j) {
941 auto cluster_idx = m_linearization[pos];
943 Assume(chunk[cluster_idx]);
945 auto& entry =
ret.emplace_back();
947 entry.m_index = m_mapping[cluster_idx];
950 if (pos != 0) deps.emplace_back(prev_index, entry.m_index);
951 prev_index = entry.m_index;
952 entry.m_tx_size = m_depgraph.
FeeRate(cluster_idx).
size;
953 size += entry.m_tx_size;
965 QualityLevel new_quality = IsAcceptable(
true) ? QualityLevel::ACCEPTABLE
966 : QualityLevel::NEEDS_RELINEARIZE;
973 if (new_quality == QualityLevel::ACCEPTABLE) {
980 std::vector<std::pair<Cluster*, DepGraphIndex>> remap(m_depgraph.
PositionRange());
981 std::vector<Cluster*> new_clusters;
986 auto split_quality = component.Count() <= 2 ? QualityLevel::OPTIMAL : new_quality;
987 if (first && component == todo) {
990 graph.SetClusterQuality(m_level, m_quality, m_setindex, split_quality);
998 auto new_cluster = std::make_unique<Cluster>(graph.m_next_sequence_counter++);
999 new_clusters.push_back(new_cluster.get());
1002 for (
auto i : component) {
1005 graph.InsertCluster(m_level, std::move(new_cluster), split_quality);
1009 for (
auto i : m_linearization) {
1011 Cluster* new_cluster = remap[i].first;
1013 remap[i].second = new_cluster->m_depgraph.AddTransaction(m_depgraph.
FeeRate(i));
1015 new_cluster->m_mapping.push_back(m_mapping[i]);
1018 new_cluster->m_linearization.push_back(remap[i].second);
1021 for (
auto i : m_linearization) {
1023 Cluster* new_cluster = remap[i].first;
1025 SetType new_parents;
1026 for (
auto par : m_depgraph.
GetReducedParents(i)) new_parents.Set(remap[par].second);
1027 new_cluster->m_depgraph.AddDependencies(new_parents, remap[i].second);
1030 for (Cluster* new_cluster : new_clusters) {
1031 new_cluster->Updated(graph);
1036 m_linearization.clear();
1040void Cluster::Merge(TxGraphImpl& graph, Cluster& other)
noexcept
1043 std::vector<DepGraphIndex> remap(other.m_depgraph.PositionRange());
1045 for (
auto pos : other.m_linearization) {
1046 auto idx = other.m_mapping[pos];
1048 auto new_pos = m_depgraph.
AddTransaction(other.m_depgraph.FeeRate(pos));
1049 remap[pos] = new_pos;
1050 if (new_pos == m_mapping.size()) {
1051 m_mapping.push_back(idx);
1053 m_mapping[new_pos] = idx;
1055 m_linearization.push_back(new_pos);
1060 for (
auto par : other.m_depgraph.GetReducedParents(pos)) {
1061 parents.Set(remap[par]);
1067 auto& entry = graph.m_entries[idx];
1070 if (m_level == 0) graph.ClearChunkData(entry);
1071 entry.m_locator[m_level].SetPresent(
this, new_pos);
1075 other.m_linearization.clear();
1076 other.m_mapping.clear();
1079void Cluster::ApplyDependencies(TxGraphImpl& graph, std::span<std::pair<GraphIndex, GraphIndex>> to_apply)
noexcept
1090 std::sort(to_apply.begin(), to_apply.end(), [](
auto& a,
auto& b) { return a.second < b.second; });
1092 auto it = to_apply.begin();
1093 while (it != to_apply.end()) {
1094 auto& first_child = graph.m_entries[it->second].m_locator[m_level];
1095 const auto child_idx = first_child.index;
1099 while (it != to_apply.end()) {
1100 auto& child = graph.m_entries[it->second].m_locator[m_level];
1101 auto& parent = graph.m_entries[it->first].m_locator[m_level];
1102 Assume(child.cluster ==
this && parent.cluster ==
this);
1103 if (child.index != child_idx)
break;
1104 parents.Set(parent.index);
1117 Assume(!NeedsSplitting());
1119 if (IsAcceptable()) {
1120 graph.SetClusterQuality(m_level, m_quality, m_setindex, QualityLevel::NEEDS_RELINEARIZE);
1127TxGraphImpl::~TxGraphImpl() noexcept
1131 for (
auto& entry : m_entries) {
1132 if (entry.m_ref !=
nullptr) {
1133 GetRefGraph(*entry.m_ref) =
nullptr;
1138std::unique_ptr<Cluster> TxGraphImpl::ExtractCluster(
int level, QualityLevel quality, ClusterSetIndex setindex)
noexcept
1142 auto& clusterset = GetClusterSet(level);
1143 auto& quality_clusters = clusterset.m_clusters[int(quality)];
1144 Assume(setindex < quality_clusters.size());
1147 std::unique_ptr<Cluster>
ret = std::move(quality_clusters[setindex]);
1149 ret->m_setindex = ClusterSetIndex(-1);
1153 auto max_setindex = quality_clusters.size() - 1;
1154 if (setindex != max_setindex) {
1156 quality_clusters.back()->m_setindex = setindex;
1157 quality_clusters.back()->m_level = level;
1158 quality_clusters[setindex] = std::move(quality_clusters.back());
1161 quality_clusters.pop_back();
1166ClusterSetIndex TxGraphImpl::InsertCluster(
int level, std::unique_ptr<Cluster>&& cluster, QualityLevel quality)
noexcept
1174 auto& clusterset = GetClusterSet(level);
1175 auto& quality_clusters = clusterset.m_clusters[int(quality)];
1176 ClusterSetIndex
ret = quality_clusters.size();
1177 cluster->m_quality = quality;
1178 cluster->m_setindex =
ret;
1179 cluster->m_level = level;
1180 quality_clusters.push_back(std::move(cluster));
1184void TxGraphImpl::SetClusterQuality(
int level, QualityLevel old_quality, ClusterSetIndex old_index, QualityLevel new_quality)
noexcept
1189 if (old_quality == new_quality)
return;
1191 auto cluster_ptr = ExtractCluster(level, old_quality, old_index);
1193 InsertCluster(level, std::move(cluster_ptr), new_quality);
1196void TxGraphImpl::DeleteCluster(Cluster& cluster)
noexcept
1199 auto cluster_ptr = ExtractCluster(cluster.m_level, cluster.m_quality, cluster.m_setindex);
1201 cluster_ptr.reset();
1204Cluster* TxGraphImpl::FindCluster(GraphIndex idx,
int level)
const noexcept
1206 Assume(level >= 0 && level <= GetTopLevel());
1207 auto& entry = m_entries[idx];
1209 for (
int l = level; l >= 0; --l) {
1212 if (entry.m_locator[l].IsMissing())
continue;
1214 if (entry.m_locator[l].IsRemoved())
break;
1216 return entry.m_locator[l].cluster;
1222Cluster* TxGraphImpl::PullIn(Cluster* cluster)
noexcept
1224 int to_level = GetTopLevel();
1226 int level = cluster->m_level;
1227 Assume(level <= to_level);
1232 MakeAcceptable(*cluster);
1233 cluster = cluster->CopyToStaging(*
this);
1238void TxGraphImpl::ApplyRemovals(
int up_to_level)
noexcept
1240 Assume(up_to_level >= 0 && up_to_level <= GetTopLevel());
1241 for (
int level = 0; level <= up_to_level; ++level) {
1242 auto& clusterset = GetClusterSet(level);
1243 auto& to_remove = clusterset.m_to_remove;
1245 if (to_remove.empty())
continue;
1248 for (GraphIndex index : to_remove) {
1249 auto cluster = FindCluster(index, level);
1250 if (cluster !=
nullptr) PullIn(cluster);
1254 std::sort(to_remove.begin(), to_remove.end(), [&](GraphIndex a, GraphIndex b)
noexcept {
1255 Cluster* cluster_a = m_entries[a].m_locator[level].cluster;
1256 Cluster* cluster_b = m_entries[b].m_locator[level].cluster;
1257 return CompareClusters(cluster_a, cluster_b) < 0;
1260 std::span to_remove_span{to_remove};
1261 while (!to_remove_span.empty()) {
1262 Cluster* cluster = m_entries[to_remove_span.front()].m_locator[level].cluster;
1263 if (cluster !=
nullptr) {
1266 cluster->ApplyRemovals(*
this, to_remove_span);
1270 to_remove_span = to_remove_span.subspan(1);
1278void TxGraphImpl::SwapIndexes(GraphIndex a, GraphIndex b)
noexcept
1280 Assume(a < m_entries.size());
1281 Assume(b < m_entries.size());
1283 std::swap(m_entries[a], m_entries[b]);
1285 for (
int i = 0; i < 2; ++i) {
1286 GraphIndex idx = i ? b : a;
1287 Entry& entry = m_entries[idx];
1289 if (entry.m_ref) GetRefIndex(*entry.m_ref) = idx;
1291 if (entry.m_main_chunkindex_iterator != m_main_chunkindex.end()) {
1292 entry.m_main_chunkindex_iterator->m_graph_index = idx;
1296 for (
int level = 0; level < MAX_LEVELS; ++level) {
1297 Locator& locator = entry.m_locator[level];
1298 if (locator.IsPresent()) {
1299 locator.cluster->UpdateMapping(locator.index, idx);
1305void TxGraphImpl::Compact() noexcept
1309 if (!m_main_clusterset.m_deps_to_add.empty())
return;
1310 if (!m_main_clusterset.m_to_remove.empty())
return;
1311 Assume(m_main_clusterset.m_removed.empty());
1312 if (m_staging_clusterset.has_value()) {
1313 if (!m_staging_clusterset->m_deps_to_add.empty())
return;
1314 if (!m_staging_clusterset->m_to_remove.empty())
return;
1315 if (!m_staging_clusterset->m_removed.empty())
return;
1325 std::sort(m_unlinked.begin(), m_unlinked.end(), std::greater{});
1327 auto last = GraphIndex(-1);
1328 for (GraphIndex idx : m_unlinked) {
1335 Entry& entry = m_entries[idx];
1336 Assume(entry.m_ref ==
nullptr);
1338 for (
int level = 0; level < MAX_LEVELS; ++level) {
1339 Assume(!entry.m_locator[level].IsPresent());
1343 if (idx != m_entries.size() - 1) SwapIndexes(idx, m_entries.size() - 1);
1345 m_entries.pop_back();
1354 ApplyRemovals(cluster.m_level);
1355 bool del = cluster.Split(*
this);
1358 DeleteCluster(cluster);
1362void TxGraphImpl::SplitAll(
int up_to_level)
noexcept
1364 Assume(up_to_level >= 0 && up_to_level <= GetTopLevel());
1366 ApplyRemovals(up_to_level);
1367 for (
int level = 0; level <= up_to_level; ++level) {
1368 for (
auto quality : {QualityLevel::NEEDS_SPLIT, QualityLevel::NEEDS_SPLIT_ACCEPTABLE}) {
1369 auto& queue = GetClusterSet(level).m_clusters[int(quality)];
1370 while (!queue.empty()) {
1371 Split(*queue.back().get());
1377void TxGraphImpl::GroupClusters(
int level)
noexcept
1379 auto& clusterset = GetClusterSet(level);
1381 if (clusterset.m_group_data.has_value())
return;
1391 std::vector<std::pair<Cluster*, uint64_t>> an_clusters;
1396 std::vector<std::pair<std::pair<GraphIndex, GraphIndex>, uint64_t>> an_deps;
1400 for (
int level_iter = 0; level_iter <= level; ++level_iter) {
1401 for (
auto& cluster : GetClusterSet(level_iter).m_clusters[
int(QualityLevel::OVERSIZED_SINGLETON)]) {
1402 auto graph_idx = cluster->GetClusterEntry(0);
1403 auto cur_cluster = FindCluster(graph_idx, level);
1404 if (cur_cluster ==
nullptr)
continue;
1405 an_clusters.emplace_back(cur_cluster, cur_cluster->m_sequence);
1411 an_deps.reserve(clusterset.m_deps_to_add.size());
1412 for (
const auto& [par, chl] : clusterset.m_deps_to_add) {
1413 auto par_cluster = FindCluster(par, level);
1414 auto chl_cluster = FindCluster(chl, level);
1416 if (par_cluster ==
nullptr || chl_cluster ==
nullptr)
continue;
1417 an_clusters.emplace_back(par_cluster, par_cluster->m_sequence);
1420 if (chl_cluster != par_cluster) an_clusters.emplace_back(chl_cluster, chl_cluster->m_sequence);
1422 an_deps.emplace_back(std::pair{par, chl}, chl_cluster->m_sequence);
1426 std::sort(an_clusters.begin(), an_clusters.end(), [](
auto& a,
auto& b)
noexcept { return a.second < b.second; });
1427 an_clusters.erase(std::unique(an_clusters.begin(), an_clusters.end()), an_clusters.end());
1429 std::sort(an_deps.begin(), an_deps.end(), [&](
auto& a,
auto& b)
noexcept { return a.second < b.second; });
1435 struct PartitionData
1443 PartitionData* parent;
1449 std::vector<PartitionData> partition_data;
1452 auto locate_fn = [&](uint64_t
sequence)
noexcept -> PartitionData* {
1453 auto it = std::lower_bound(partition_data.begin(), partition_data.end(),
sequence,
1454 [](
auto& a, uint64_t seq)
noexcept { return a.sequence < seq; });
1455 Assume(it != partition_data.end());
1461 static constexpr auto find_root_fn = [](PartitionData*
data)
noexcept -> PartitionData* {
1465 auto par =
data->parent;
1466 data->parent = par->parent;
1474 static constexpr auto union_fn = [](PartitionData* arg1, PartitionData* arg2)
noexcept {
1477 auto rep1 = find_root_fn(arg1);
1478 auto rep2 = find_root_fn(arg2);
1479 if (rep1 == rep2)
return rep1;
1482 if (rep1->rank < rep2->rank) std::swap(rep1, rep2);
1483 rep2->parent = rep1;
1484 rep1->rank += (rep1->rank == rep2->rank);
1489 partition_data.resize(an_clusters.size());
1490 for (
size_t i = 0; i < an_clusters.size(); ++i) {
1491 partition_data[i].sequence = an_clusters[i].first->m_sequence;
1492 partition_data[i].parent = &partition_data[i];
1493 partition_data[i].rank = 0;
1498 Cluster* last_chl_cluster{
nullptr};
1499 PartitionData* last_partition{
nullptr};
1500 for (
const auto& [dep,
_] : an_deps) {
1501 auto [par, chl] = dep;
1502 auto par_cluster = FindCluster(par, level);
1503 auto chl_cluster = FindCluster(chl, level);
1504 Assume(chl_cluster !=
nullptr && par_cluster !=
nullptr);
1506 if (par_cluster == chl_cluster)
continue;
1508 if (chl_cluster == last_chl_cluster) {
1512 last_partition = union_fn(locate_fn(par_cluster->m_sequence), last_partition);
1514 last_chl_cluster = chl_cluster;
1515 last_partition = union_fn(locate_fn(par_cluster->m_sequence), locate_fn(chl_cluster->m_sequence));
1521 auto deps_it = an_deps.begin();
1522 for (
size_t i = 0; i < partition_data.size(); ++i) {
1523 auto&
data = partition_data[i];
1526 auto rep_seq = find_root_fn(&
data)->sequence;
1527 an_clusters[i].second = rep_seq;
1529 while (deps_it != an_deps.end()) {
1530 auto [par, chl] = deps_it->first;
1531 auto chl_cluster = FindCluster(chl, level);
1532 Assume(chl_cluster !=
nullptr);
1533 if (chl_cluster->m_sequence >
data.sequence)
break;
1534 deps_it->second = rep_seq;
1542 std::sort(an_deps.begin(), an_deps.end(), [](
auto& a,
auto& b)
noexcept { return a.second < b.second; });
1543 std::sort(an_clusters.begin(), an_clusters.end(), [](
auto& a,
auto& b)
noexcept { return a.second < b.second; });
1547 clusterset.m_group_data = GroupData{};
1548 clusterset.m_group_data->m_group_clusters.reserve(an_clusters.size());
1549 clusterset.m_deps_to_add.clear();
1550 clusterset.m_deps_to_add.reserve(an_deps.size());
1551 clusterset.m_oversized =
false;
1552 auto an_deps_it = an_deps.begin();
1553 auto an_clusters_it = an_clusters.begin();
1554 while (an_clusters_it != an_clusters.end()) {
1556 auto rep = an_clusters_it->second;
1558 auto& new_entry = clusterset.m_group_data->m_groups.emplace_back();
1559 new_entry.m_cluster_offset = clusterset.m_group_data->m_group_clusters.size();
1560 new_entry.m_cluster_count = 0;
1561 new_entry.m_deps_offset = clusterset.m_deps_to_add.size();
1562 new_entry.m_deps_count = 0;
1563 uint32_t total_count{0};
1564 uint64_t total_size{0};
1566 while (an_clusters_it != an_clusters.end() && an_clusters_it->second == rep) {
1567 clusterset.m_group_data->m_group_clusters.push_back(an_clusters_it->first);
1568 total_count += an_clusters_it->first->GetTxCount();
1569 total_size += an_clusters_it->first->GetTotalTxSize();
1571 ++new_entry.m_cluster_count;
1574 while (an_deps_it != an_deps.end() && an_deps_it->second == rep) {
1575 clusterset.m_deps_to_add.push_back(an_deps_it->first);
1577 ++new_entry.m_deps_count;
1580 if (total_count > m_max_cluster_count || total_size > m_max_cluster_size) {
1581 clusterset.m_oversized =
true;
1584 Assume(an_deps_it == an_deps.end());
1585 Assume(an_clusters_it == an_clusters.end());
1589void TxGraphImpl::Merge(std::span<Cluster*> to_merge)
noexcept
1591 Assume(!to_merge.empty());
1593 if (to_merge.size() == 1)
return;
1598 size_t max_size_pos{0};
1599 DepGraphIndex max_size = to_merge[max_size_pos]->GetTxCount();
1600 for (
size_t i = 1; i < to_merge.size(); ++i) {
1602 if (size > max_size) {
1607 if (max_size_pos != 0) std::swap(to_merge[0], to_merge[max_size_pos]);
1610 for (
size_t i = 1; i < to_merge.size(); ++i) {
1611 to_merge[0]->Merge(*
this, *to_merge[i]);
1612 DeleteCluster(*to_merge[i]);
1616void TxGraphImpl::ApplyDependencies(
int level)
noexcept
1618 auto& clusterset = GetClusterSet(level);
1620 if (clusterset.m_oversized ==
true)
return;
1622 GroupClusters(level);
1623 Assume(clusterset.m_group_data.has_value());
1625 if (clusterset.m_deps_to_add.empty())
return;
1627 if (clusterset.m_oversized ==
true)
return;
1630 for (
const auto& group_entry : clusterset.m_group_data->m_groups) {
1631 auto cluster_span = std::span{clusterset.m_group_data->m_group_clusters}
1632 .subspan(group_entry.m_cluster_offset, group_entry.m_cluster_count);
1635 for (Cluster*& cluster : cluster_span) {
1636 cluster = PullIn(cluster);
1640 Merge(cluster_span);
1643 auto deps_span = std::span{clusterset.m_deps_to_add}
1644 .subspan(group_entry.m_deps_offset, group_entry.m_deps_count);
1645 Assume(!deps_span.empty());
1646 const auto& loc = m_entries[deps_span[0].second].m_locator[level];
1648 loc.cluster->ApplyDependencies(*
this, deps_span);
1652 clusterset.m_deps_to_add.clear();
1656 clusterset.m_group_data = GroupData{};
1659std::pair<uint64_t, bool> Cluster::Relinearize(TxGraphImpl& graph, uint64_t max_iters)
noexcept
1662 Assume(!NeedsSplitting());
1664 if (IsOptimal())
return {0,
false};
1666 uint64_t rng_seed = graph.m_rng.rand64();
1667 auto [linearization, optimal, cost] =
Linearize(m_depgraph, max_iters, rng_seed, m_linearization);
1672 m_linearization = std::move(linearization);
1674 bool improved =
false;
1676 graph.SetClusterQuality(m_level, m_quality, m_setindex, QualityLevel::OPTIMAL);
1678 }
else if (max_iters >= graph.m_acceptable_iters && !IsAcceptable()) {
1679 graph.SetClusterQuality(m_level, m_quality, m_setindex, QualityLevel::ACCEPTABLE);
1684 return {cost, improved};
1687void TxGraphImpl::MakeAcceptable(Cluster& cluster)
noexcept
1690 if (!cluster.NeedsSplitting() && !cluster.IsAcceptable() && !cluster.IsOversized()) {
1691 cluster.Relinearize(*
this, m_acceptable_iters);
1695void TxGraphImpl::MakeAllAcceptable(
int level)
noexcept
1697 ApplyDependencies(level);
1698 auto& clusterset = GetClusterSet(level);
1699 if (clusterset.m_oversized ==
true)
return;
1700 auto& queue = clusterset.m_clusters[int(QualityLevel::NEEDS_RELINEARIZE)];
1701 while (!queue.empty()) {
1702 MakeAcceptable(*queue.back().get());
1708Cluster::Cluster(uint64_t
sequence, TxGraphImpl& graph,
const FeePerWeight& feerate, GraphIndex graph_index) noexcept :
1713 m_mapping.push_back(graph_index);
1714 m_linearization.push_back(cluster_idx);
1719 Assume(m_main_chunkindex_observers == 0 || GetTopLevel() != 0);
1720 Assume(feerate.size > 0);
1724 auto idx = m_entries.size();
1725 m_entries.emplace_back();
1726 auto& entry = m_entries.back();
1727 entry.m_main_chunkindex_iterator = m_main_chunkindex.end();
1729 GetRefGraph(
ret) =
this;
1730 GetRefIndex(
ret) = idx;
1732 bool oversized = uint64_t(feerate.size) > m_max_cluster_size;
1733 auto cluster = std::make_unique<Cluster>(m_next_sequence_counter++, *
this, feerate, idx);
1734 auto cluster_ptr = cluster.get();
1735 int level = GetTopLevel();
1736 auto& clusterset = GetClusterSet(level);
1737 InsertCluster(level, std::move(cluster), oversized ? QualityLevel::OVERSIZED_SINGLETON : QualityLevel::OPTIMAL);
1738 cluster_ptr->Updated(*
this);
1739 ++clusterset.m_txcount;
1742 ++clusterset.m_txcount_oversized;
1743 clusterset.m_oversized =
true;
1744 clusterset.m_group_data = std::nullopt;
1750void TxGraphImpl::RemoveTransaction(
const Ref& arg)
noexcept
1754 if (GetRefGraph(arg) ==
nullptr)
return;
1755 Assume(GetRefGraph(arg) ==
this);
1756 Assume(m_main_chunkindex_observers == 0 || GetTopLevel() != 0);
1758 int level = GetTopLevel();
1759 auto cluster = FindCluster(GetRefIndex(arg), level);
1760 if (cluster ==
nullptr)
return;
1762 auto& clusterset = GetClusterSet(level);
1763 clusterset.m_to_remove.push_back(GetRefIndex(arg));
1765 clusterset.m_group_data.reset();
1766 if (clusterset.m_oversized ==
true) clusterset.m_oversized = std::nullopt;
1769void TxGraphImpl::AddDependency(
const Ref& parent,
const Ref& child)
noexcept
1773 if (GetRefGraph(parent) ==
nullptr || GetRefGraph(child) ==
nullptr)
return;
1774 Assume(GetRefGraph(parent) ==
this && GetRefGraph(child) ==
this);
1775 Assume(m_main_chunkindex_observers == 0 || GetTopLevel() != 0);
1777 if (GetRefIndex(parent) == GetRefIndex(child))
return;
1780 int level = GetTopLevel();
1781 auto par_cluster = FindCluster(GetRefIndex(parent), level);
1782 if (par_cluster ==
nullptr)
return;
1783 auto chl_cluster = FindCluster(GetRefIndex(child), level);
1784 if (chl_cluster ==
nullptr)
return;
1786 auto& clusterset = GetClusterSet(level);
1787 clusterset.m_deps_to_add.emplace_back(GetRefIndex(parent), GetRefIndex(child));
1789 clusterset.m_group_data.reset();
1790 if (clusterset.m_oversized ==
false) clusterset.m_oversized = std::nullopt;
1793bool TxGraphImpl::Exists(
const Ref& arg,
bool main_only)
noexcept
1795 if (GetRefGraph(arg) ==
nullptr)
return false;
1796 Assume(GetRefGraph(arg) ==
this);
1797 size_t level = GetSpecifiedLevel(main_only);
1799 ApplyRemovals(level);
1800 auto cluster = FindCluster(GetRefIndex(arg), level);
1801 return cluster !=
nullptr;
1804void Cluster::GetAncestorRefs(
const TxGraphImpl& graph, std::span<std::pair<Cluster*, DepGraphIndex>>&
args, std::vector<TxGraph::Ref*>& output)
noexcept
1807 SetType ancestors_union;
1809 while (!
args.empty()) {
1810 if (
args.front().first !=
this)
break;
1811 ancestors_union |= m_depgraph.
Ancestors(
args.front().second);
1814 Assume(ancestors_union.Any());
1816 for (
auto idx : ancestors_union) {
1817 const auto& entry = graph.m_entries[m_mapping[idx]];
1818 Assume(entry.m_ref !=
nullptr);
1819 output.push_back(entry.m_ref);
1823void Cluster::GetDescendantRefs(
const TxGraphImpl& graph, std::span<std::pair<Cluster*, DepGraphIndex>>&
args, std::vector<TxGraph::Ref*>& output)
noexcept
1826 SetType descendants_union;
1828 while (!
args.empty()) {
1829 if (
args.front().first !=
this)
break;
1833 Assume(descendants_union.Any());
1835 for (
auto idx : descendants_union) {
1836 const auto& entry = graph.m_entries[m_mapping[idx]];
1837 Assume(entry.m_ref !=
nullptr);
1838 output.push_back(entry.m_ref);
1842bool Cluster::GetClusterRefs(TxGraphImpl& graph, std::span<TxGraph::Ref*> range, LinearizationIndex start_pos)
noexcept
1846 for (
auto& ref : range) {
1847 Assume(start_pos < m_linearization.size());
1848 const auto& entry = graph.m_entries[m_mapping[m_linearization[start_pos++]]];
1849 Assume(entry.m_ref !=
nullptr);
1853 return start_pos == m_linearization.size();
1861void Cluster::MakeStagingTransactionsMissing(TxGraphImpl& graph)
noexcept
1866 for (
auto ci : m_linearization) {
1867 GraphIndex idx = m_mapping[ci];
1868 auto& entry = graph.m_entries[idx];
1869 entry.m_locator[1].SetMissing();
1873std::vector<TxGraph::Ref*> TxGraphImpl::GetAncestors(
const Ref& arg,
bool main_only)
noexcept
1876 if (GetRefGraph(arg) ==
nullptr)
return {};
1877 Assume(GetRefGraph(arg) ==
this);
1879 size_t level = GetSpecifiedLevel(main_only);
1880 ApplyDependencies(level);
1882 Assume(GetClusterSet(level).m_deps_to_add.empty());
1884 auto cluster = FindCluster(GetRefIndex(arg), level);
1885 if (cluster ==
nullptr)
return {};
1887 std::pair<Cluster*, DepGraphIndex> match = {cluster, m_entries[GetRefIndex(arg)].m_locator[cluster->m_level].index};
1888 auto matches = std::span(&match, 1);
1889 std::vector<TxGraph::Ref*>
ret;
1890 cluster->GetAncestorRefs(*
this, matches,
ret);
1894std::vector<TxGraph::Ref*> TxGraphImpl::GetDescendants(
const Ref& arg,
bool main_only)
noexcept
1897 if (GetRefGraph(arg) ==
nullptr)
return {};
1898 Assume(GetRefGraph(arg) ==
this);
1900 size_t level = GetSpecifiedLevel(main_only);
1901 ApplyDependencies(level);
1903 Assume(GetClusterSet(level).m_deps_to_add.empty());
1905 auto cluster = FindCluster(GetRefIndex(arg), level);
1906 if (cluster ==
nullptr)
return {};
1908 std::pair<Cluster*, DepGraphIndex> match = {cluster, m_entries[GetRefIndex(arg)].m_locator[cluster->m_level].index};
1909 auto matches = std::span(&match, 1);
1910 std::vector<TxGraph::Ref*>
ret;
1911 cluster->GetDescendantRefs(*
this, matches,
ret);
1915std::vector<TxGraph::Ref*> TxGraphImpl::GetAncestorsUnion(std::span<const Ref* const>
args,
bool main_only)
noexcept
1918 size_t level = GetSpecifiedLevel(main_only);
1919 ApplyDependencies(level);
1921 Assume(GetClusterSet(level).m_deps_to_add.empty());
1924 std::vector<std::pair<Cluster*, DepGraphIndex>> matches;
1925 matches.reserve(
args.size());
1926 for (
auto arg :
args) {
1929 if (GetRefGraph(*arg) ==
nullptr)
continue;
1930 Assume(GetRefGraph(*arg) ==
this);
1932 auto cluster = FindCluster(GetRefIndex(*arg), level);
1933 if (cluster ==
nullptr)
continue;
1935 matches.emplace_back(cluster, m_entries[GetRefIndex(*arg)].m_locator[cluster->m_level].index);
1938 std::sort(matches.begin(), matches.end(), [](
auto& a,
auto& b)
noexcept { return CompareClusters(a.first, b.first) < 0; });
1940 std::span match_span(matches);
1941 std::vector<TxGraph::Ref*>
ret;
1942 while (!match_span.empty()) {
1943 match_span.front().first->GetAncestorRefs(*
this, match_span,
ret);
1948std::vector<TxGraph::Ref*> TxGraphImpl::GetDescendantsUnion(std::span<const Ref* const>
args,
bool main_only)
noexcept
1951 size_t level = GetSpecifiedLevel(main_only);
1952 ApplyDependencies(level);
1954 Assume(GetClusterSet(level).m_deps_to_add.empty());
1957 std::vector<std::pair<Cluster*, DepGraphIndex>> matches;
1958 matches.reserve(
args.size());
1959 for (
auto arg :
args) {
1962 if (GetRefGraph(*arg) ==
nullptr)
continue;
1963 Assume(GetRefGraph(*arg) ==
this);
1965 auto cluster = FindCluster(GetRefIndex(*arg), level);
1966 if (cluster ==
nullptr)
continue;
1968 matches.emplace_back(cluster, m_entries[GetRefIndex(*arg)].m_locator[cluster->m_level].index);
1971 std::sort(matches.begin(), matches.end(), [](
auto& a,
auto& b)
noexcept { return CompareClusters(a.first, b.first) < 0; });
1973 std::span match_span(matches);
1974 std::vector<TxGraph::Ref*>
ret;
1975 while (!match_span.empty()) {
1976 match_span.front().first->GetDescendantRefs(*
this, match_span,
ret);
1981std::vector<TxGraph::Ref*> TxGraphImpl::GetCluster(
const Ref& arg,
bool main_only)
noexcept
1985 if (GetRefGraph(arg) ==
nullptr)
return {};
1986 Assume(GetRefGraph(arg) ==
this);
1988 size_t level = GetSpecifiedLevel(main_only);
1989 ApplyDependencies(level);
1991 Assume(GetClusterSet(level).m_deps_to_add.empty());
1993 auto cluster = FindCluster(GetRefIndex(arg), level);
1994 if (cluster ==
nullptr)
return {};
1996 MakeAcceptable(*cluster);
1997 std::vector<TxGraph::Ref*>
ret(cluster->GetTxCount());
1998 cluster->GetClusterRefs(*
this,
ret, 0);
2004 size_t level = GetSpecifiedLevel(main_only);
2005 ApplyRemovals(level);
2006 return GetClusterSet(level).m_txcount;
2009FeePerWeight TxGraphImpl::GetIndividualFeerate(
const Ref& arg)
noexcept
2012 if (GetRefGraph(arg) ==
nullptr)
return {};
2013 Assume(GetRefGraph(arg) ==
this);
2016 Cluster* cluster{
nullptr};
2017 for (
int level = 0; level <= GetTopLevel(); ++level) {
2020 ApplyRemovals(level);
2021 if (m_entries[GetRefIndex(arg)].m_locator[level].IsPresent()) {
2022 cluster = m_entries[GetRefIndex(arg)].m_locator[level].cluster;
2026 if (cluster ==
nullptr)
return {};
2028 return cluster->GetIndividualFeerate(m_entries[GetRefIndex(arg)].m_locator[cluster->m_level].index);
2031FeePerWeight TxGraphImpl::GetMainChunkFeerate(
const Ref& arg)
noexcept
2034 if (GetRefGraph(arg) ==
nullptr)
return {};
2035 Assume(GetRefGraph(arg) ==
this);
2037 ApplyDependencies(0);
2039 Assume(m_main_clusterset.m_deps_to_add.empty());
2041 auto cluster = FindCluster(GetRefIndex(arg), 0);
2042 if (cluster ==
nullptr)
return {};
2045 MakeAcceptable(*cluster);
2046 const auto& entry = m_entries[GetRefIndex(arg)];
2047 return entry.m_main_chunk_feerate;
2050bool TxGraphImpl::IsOversized(
bool main_only)
noexcept
2052 size_t level = GetSpecifiedLevel(main_only);
2053 auto& clusterset = GetClusterSet(level);
2054 if (clusterset.m_oversized.has_value()) {
2056 return *clusterset.m_oversized;
2058 ApplyRemovals(level);
2059 if (clusterset.m_txcount_oversized > 0) {
2060 clusterset.m_oversized =
true;
2064 GroupClusters(level);
2066 Assume(clusterset.m_oversized.has_value());
2067 return *clusterset.m_oversized;
2070void TxGraphImpl::StartStaging() noexcept
2073 Assume(!m_staging_clusterset.has_value());
2080 ApplyDependencies(0);
2082 m_staging_clusterset.emplace();
2085 m_staging_clusterset->m_txcount = m_main_clusterset.m_txcount;
2086 m_staging_clusterset->m_txcount_oversized = m_main_clusterset.m_txcount_oversized;
2087 m_staging_clusterset->m_deps_to_add = m_main_clusterset.m_deps_to_add;
2088 m_staging_clusterset->m_group_data = m_main_clusterset.m_group_data;
2089 m_staging_clusterset->m_oversized = m_main_clusterset.m_oversized;
2090 Assume(m_staging_clusterset->m_oversized.has_value());
2093void TxGraphImpl::AbortStaging() noexcept
2096 Assume(m_staging_clusterset.has_value());
2099 for (
auto idx : m_staging_clusterset->m_removed) {
2100 m_entries[idx].m_locator[1].SetMissing();
2104 for (
auto& cluster : m_staging_clusterset->m_clusters[quality]) {
2105 cluster->MakeStagingTransactionsMissing(*
this);
2109 m_staging_clusterset.reset();
2111 if (!m_main_clusterset.m_group_data.has_value()) {
2114 if (m_main_clusterset.m_to_remove.empty() && m_main_clusterset.m_txcount_oversized > 0) {
2117 m_main_clusterset.m_oversized =
true;
2119 m_main_clusterset.m_oversized = std::nullopt;
2124void TxGraphImpl::CommitStaging() noexcept
2127 Assume(m_staging_clusterset.has_value());
2128 Assume(m_main_chunkindex_observers == 0);
2131 auto conflicts = GetConflicts();
2132 for (Cluster* conflict : conflicts) {
2133 conflict->Clear(*
this);
2134 DeleteCluster(*conflict);
2138 for (
auto idx : m_staging_clusterset->m_removed) {
2139 m_entries[idx].m_locator[1].SetMissing();
2143 auto& stage_sets = m_staging_clusterset->m_clusters[quality];
2144 while (!stage_sets.empty()) {
2145 stage_sets.back()->MoveToMain(*
this);
2149 m_main_clusterset.m_deps_to_add = std::move(m_staging_clusterset->m_deps_to_add);
2150 m_main_clusterset.m_to_remove = std::move(m_staging_clusterset->m_to_remove);
2151 m_main_clusterset.m_group_data = std::move(m_staging_clusterset->m_group_data);
2152 m_main_clusterset.m_oversized = std::move(m_staging_clusterset->m_oversized);
2153 m_main_clusterset.m_txcount = std::move(m_staging_clusterset->m_txcount);
2154 m_main_clusterset.m_txcount_oversized = std::move(m_staging_clusterset->m_txcount_oversized);
2156 m_staging_clusterset.reset();
2160void Cluster::SetFee(TxGraphImpl& graph,
DepGraphIndex idx, int64_t
fee)
noexcept
2169 if (m_quality == QualityLevel::OVERSIZED_SINGLETON) {
2171 }
else if (!NeedsSplitting()) {
2172 graph.SetClusterQuality(m_level, m_quality, m_setindex, QualityLevel::NEEDS_RELINEARIZE);
2174 graph.SetClusterQuality(m_level, m_quality, m_setindex, QualityLevel::NEEDS_SPLIT);
2179void TxGraphImpl::SetTransactionFee(
const Ref& ref, int64_t
fee)
noexcept
2182 if (GetRefGraph(ref) ==
nullptr)
return;
2183 Assume(GetRefGraph(ref) ==
this);
2184 Assume(m_main_chunkindex_observers == 0);
2186 auto& entry = m_entries[GetRefIndex(ref)];
2187 for (
int level = 0; level < MAX_LEVELS; ++level) {
2188 auto& locator = entry.m_locator[level];
2189 if (locator.IsPresent()) {
2190 locator.cluster->SetFee(*
this, locator.index,
fee);
2195std::strong_ordering TxGraphImpl::CompareMainOrder(
const Ref& a,
const Ref& b)
noexcept
2198 Assume(GetRefGraph(a) ==
this);
2199 Assume(GetRefGraph(b) ==
this);
2201 ApplyDependencies(0);
2202 Assume(m_main_clusterset.m_deps_to_add.empty());
2204 const auto& entry_a = m_entries[GetRefIndex(a)];
2205 const auto& entry_b = m_entries[GetRefIndex(b)];
2206 const auto& locator_a = entry_a.m_locator[0];
2207 const auto& locator_b = entry_b.m_locator[0];
2208 Assume(locator_a.IsPresent());
2209 Assume(locator_b.IsPresent());
2210 MakeAcceptable(*locator_a.cluster);
2211 MakeAcceptable(*locator_b.cluster);
2213 return CompareMainTransactions(GetRefIndex(a), GetRefIndex(b));
2216TxGraph::GraphIndex TxGraphImpl::CountDistinctClusters(std::span<const Ref* const> refs,
bool main_only)
noexcept
2218 size_t level = GetSpecifiedLevel(main_only);
2219 ApplyDependencies(level);
2220 auto& clusterset = GetClusterSet(level);
2221 Assume(clusterset.m_deps_to_add.empty());
2223 std::vector<Cluster*> clusters;
2224 clusters.reserve(refs.size());
2225 for (
const Ref* ref : refs) {
2227 if (GetRefGraph(*ref) ==
nullptr)
continue;
2228 Assume(GetRefGraph(*ref) ==
this);
2229 auto cluster = FindCluster(GetRefIndex(*ref), level);
2230 if (cluster !=
nullptr) clusters.push_back(cluster);
2233 std::sort(clusters.begin(), clusters.end(), [](Cluster* a, Cluster* b)
noexcept { return CompareClusters(a, b) < 0; });
2234 Cluster* last{
nullptr};
2236 for (Cluster* cluster : clusters) {
2237 ret += (cluster != last);
2243std::pair<std::vector<FeeFrac>, std::vector<FeeFrac>> TxGraphImpl::GetMainStagingDiagrams() noexcept
2245 Assume(m_staging_clusterset.has_value());
2246 MakeAllAcceptable(0);
2247 Assume(m_main_clusterset.m_deps_to_add.empty());
2248 MakeAllAcceptable(1);
2249 Assume(m_staging_clusterset->m_deps_to_add.empty());
2252 auto main_clusters = GetConflicts();
2253 std::vector<FeeFrac> main_feerates, staging_feerates;
2254 for (Cluster* cluster : main_clusters) {
2255 cluster->AppendChunkFeerates(main_feerates);
2259 for (
const auto& cluster : m_staging_clusterset->m_clusters[quality]) {
2260 cluster->AppendChunkFeerates(staging_feerates);
2264 std::sort(main_feerates.begin(), main_feerates.end(), [](
auto& a,
auto& b) { return a > b; });
2265 std::sort(staging_feerates.begin(), staging_feerates.end(), [](
auto& a,
auto& b) { return a > b; });
2266 return std::make_pair(std::move(main_feerates), std::move(staging_feerates));
2269void Cluster::SanityCheck(
const TxGraphImpl& graph,
int level)
const
2276 assert(m_linearization.size() <= graph.m_max_cluster_count);
2278 assert(m_level == level);
2282 assert(m_quality == QualityLevel::OVERSIZED_SINGLETON || GetTotalTxSize() <= graph.m_max_cluster_size);
2286 assert(m_quality != QualityLevel::OVERSIZED_SINGLETON || m_linearization.size() == 1);
2293 LinearizationIndex linindex{0};
2296 for (
auto lin_pos : m_linearization) {
2297 assert(lin_pos < m_mapping.size());
2298 const auto& entry = graph.m_entries[m_mapping[lin_pos]];
2300 m_done.Set(lin_pos);
2303 assert(entry.m_locator[level].cluster ==
this);
2304 assert(entry.m_locator[level].index == lin_pos);
2306 if (level == 0 && IsAcceptable()) {
2307 assert(entry.m_main_lin_index == linindex);
2309 if (!linchunking.GetChunk(0).transactions[lin_pos]) {
2310 linchunking.MarkDone(linchunking.GetChunk(0).transactions);
2313 assert(entry.m_main_chunk_feerate == linchunking.GetChunk(0).feerate);
2316 bool is_chunk_end = (chunk_pos == linchunking.GetChunk(0).transactions.Count());
2317 assert((entry.m_main_chunkindex_iterator != graph.m_main_chunkindex.end()) == is_chunk_end);
2319 auto& chunk_data = *entry.m_main_chunkindex_iterator;
2320 if (m_done == m_depgraph.
Positions() && chunk_pos == 1) {
2321 assert(chunk_data.m_chunk_count == LinearizationIndex(-1));
2323 assert(chunk_data.m_chunk_count == chunk_pos);
2334void TxGraphImpl::SanityCheck()
const
2337 std::set<GraphIndex> expected_unlinked;
2339 std::set<const Cluster*> expected_clusters[MAX_LEVELS];
2341 std::set<GraphIndex> expected_removed[MAX_LEVELS];
2343 std::set<uint64_t> sequences;
2345 std::set<GraphIndex> expected_chunkindex;
2347 bool compact_possible{
true};
2350 for (GraphIndex idx = 0; idx < m_entries.size(); ++idx) {
2351 const auto& entry = m_entries[idx];
2352 if (entry.m_ref ==
nullptr) {
2354 expected_unlinked.insert(idx);
2357 assert(GetRefGraph(*entry.m_ref) ==
this);
2358 assert(GetRefIndex(*entry.m_ref) == idx);
2360 if (entry.m_main_chunkindex_iterator != m_main_chunkindex.end()) {
2362 assert(entry.m_locator[0].IsPresent());
2363 expected_chunkindex.insert(idx);
2366 bool was_present{
false}, was_removed{
false};
2367 for (
int level = 0; level < MAX_LEVELS; ++level) {
2368 const auto& locator = entry.m_locator[level];
2370 assert(locator.IsMissing() + locator.IsRemoved() + locator.IsPresent() == 1);
2371 if (locator.IsPresent()) {
2375 assert(locator.cluster->GetClusterEntry(locator.index) == idx);
2377 expected_clusters[level].insert(locator.cluster);
2379 }
else if (locator.IsRemoved()) {
2383 assert(was_present && !was_removed);
2385 expected_removed[level].insert(idx);
2392 for (
int level = 0; level <= GetTopLevel(); ++level) {
2393 assert(level < MAX_LEVELS);
2394 auto& clusterset = GetClusterSet(level);
2395 std::set<const Cluster*> actual_clusters;
2399 QualityLevel quality{qual};
2400 const auto& quality_clusters = clusterset.m_clusters[qual];
2402 for (ClusterSetIndex setindex = 0; setindex < quality_clusters.size(); ++setindex) {
2403 const auto& cluster = *quality_clusters[setindex];
2405 assert(cluster.m_sequence < m_next_sequence_counter);
2406 assert(sequences.count(cluster.m_sequence) == 0);
2407 sequences.insert(cluster.m_sequence);
2410 if (cluster.GetTxCount() != 0) {
2411 actual_clusters.insert(&cluster);
2414 cluster.SanityCheck(*
this, level);
2416 assert(cluster.m_quality == quality);
2417 assert(cluster.m_setindex == setindex);
2422 for (GraphIndex idx : clusterset.m_to_remove) {
2423 assert(idx < m_entries.size());
2431 for (
auto [par_idx, chl_idx] : clusterset.m_deps_to_add) {
2432 assert(par_idx != chl_idx);
2433 assert(par_idx < m_entries.size());
2434 assert(chl_idx < m_entries.size());
2438 assert(actual_clusters == expected_clusters[level]);
2441 std::set<GraphIndex> actual_removed(clusterset.m_removed.begin(), clusterset.m_removed.end());
2442 for (
auto i : expected_unlinked) {
2447 actual_removed.erase(i);
2448 expected_removed[level].erase(i);
2451 assert(actual_removed == expected_removed[level]);
2454 if (!clusterset.m_deps_to_add.empty()) compact_possible =
false;
2455 if (!clusterset.m_to_remove.empty()) compact_possible =
false;
2456 if (!clusterset.m_removed.empty()) compact_possible =
false;
2460 if (level < GetTopLevel())
assert(clusterset.m_oversized.has_value());
2464 std::set<GraphIndex> actual_unlinked(m_unlinked.begin(), m_unlinked.end());
2465 assert(actual_unlinked == expected_unlinked);
2469 if (compact_possible) {
2470 assert(actual_unlinked.empty());
2474 std::set<GraphIndex> actual_chunkindex;
2476 for (
const auto& chunk : m_main_chunkindex) {
2477 GraphIndex idx = chunk.m_graph_index;
2478 actual_chunkindex.insert(idx);
2479 auto chunk_feerate = m_entries[idx].m_main_chunk_feerate;
2480 if (!last_chunk_feerate.
IsEmpty()) {
2481 assert(FeeRateCompare(last_chunk_feerate, chunk_feerate) >= 0);
2483 last_chunk_feerate = chunk_feerate;
2485 assert(actual_chunkindex == expected_chunkindex);
2488bool TxGraphImpl::DoWork(uint64_t iters)
noexcept
2490 uint64_t iters_done{0};
2493 for (QualityLevel quality : {QualityLevel::NEEDS_RELINEARIZE, QualityLevel::ACCEPTABLE}) {
2495 for (
int level = GetTopLevel(); level >= 0; --level) {
2497 if (level == 0 && m_main_chunkindex_observers != 0)
continue;
2498 ApplyDependencies(level);
2499 auto& clusterset = GetClusterSet(level);
2501 if (clusterset.m_oversized ==
true)
continue;
2502 auto& queue = clusterset.m_clusters[int(quality)];
2503 while (!queue.empty()) {
2504 if (iters_done >= iters)
return false;
2508 auto pos = m_rng.
randrange<
size_t>(queue.size());
2509 auto iters_now = iters - iters_done;
2510 if (quality == QualityLevel::NEEDS_RELINEARIZE) {
2515 iters_now = std::min(iters_now, m_acceptable_iters);
2517 auto [cost, improved] = queue[pos].get()->Relinearize(*
this, iters_now);
2524 if (!improved)
return false;
2534void BlockBuilderImpl::Next() noexcept
2537 if (m_cur_iter == m_graph->m_main_chunkindex.end())
return;
2541 m_cur_cluster =
nullptr;
2542 if (m_cur_iter == m_graph->m_main_chunkindex.end())
break;
2544 const auto& chunk_data = *m_cur_iter;
2545 const auto& chunk_end_entry = m_graph->m_entries[chunk_data.m_graph_index];
2546 m_cur_cluster = chunk_end_entry.m_locator[0].cluster;
2547 m_known_end_of_cluster =
false;
2549 if (!m_excluded_clusters.contains(m_cur_cluster))
break;
2553std::optional<std::pair<std::vector<TxGraph::Ref*>,
FeePerWeight>> BlockBuilderImpl::GetCurrentChunk() noexcept
2555 std::optional<std::pair<std::vector<TxGraph::Ref*>,
FeePerWeight>>
ret;
2557 if (m_cur_iter != m_graph->m_main_chunkindex.end()) {
2559 const auto& chunk_data = *m_cur_iter;
2560 const auto& chunk_end_entry = m_graph->m_entries[chunk_data.m_graph_index];
2561 if (chunk_data.m_chunk_count == LinearizationIndex(-1)) {
2564 ret->first.resize(1);
2565 Assume(chunk_end_entry.m_ref !=
nullptr);
2566 ret->first[0] = chunk_end_entry.m_ref;
2567 m_known_end_of_cluster =
true;
2570 ret->first.resize(chunk_data.m_chunk_count);
2571 auto start_pos = chunk_end_entry.m_main_lin_index + 1 - chunk_data.m_chunk_count;
2572 m_known_end_of_cluster = m_cur_cluster->GetClusterRefs(*m_graph,
ret->first, start_pos);
2575 Assume(!m_known_end_of_cluster || chunk_data.m_chunk_count > 1);
2577 ret->second = chunk_end_entry.m_main_chunk_feerate;
2582BlockBuilderImpl::BlockBuilderImpl(TxGraphImpl& graph) noexcept : m_graph(&graph)
2585 m_graph->MakeAllAcceptable(0);
2587 Assume(m_graph->m_main_clusterset.m_deps_to_add.empty());
2590 ++m_graph->m_main_chunkindex_observers;
2592 m_cur_iter = m_graph->m_main_chunkindex.begin();
2593 m_cur_cluster =
nullptr;
2594 if (m_cur_iter != m_graph->m_main_chunkindex.end()) {
2596 const auto& chunk_data = *m_cur_iter;
2597 const auto& chunk_end_entry = m_graph->m_entries[chunk_data.m_graph_index];
2598 m_cur_cluster = chunk_end_entry.m_locator[0].cluster;
2602BlockBuilderImpl::~BlockBuilderImpl()
2604 Assume(m_graph->m_main_chunkindex_observers > 0);
2606 --m_graph->m_main_chunkindex_observers;
2609void BlockBuilderImpl::Include() noexcept
2616void BlockBuilderImpl::Skip() noexcept
2622 if (m_cur_cluster !=
nullptr && !m_known_end_of_cluster) {
2623 m_excluded_clusters.insert(m_cur_cluster);
2628std::unique_ptr<TxGraph::BlockBuilder> TxGraphImpl::GetBlockBuilder() noexcept
2630 return std::make_unique<BlockBuilderImpl>(*
this);
2633std::pair<std::vector<TxGraph::Ref*>,
FeePerWeight> TxGraphImpl::GetWorstMainChunk() noexcept
2637 MakeAllAcceptable(0);
2638 Assume(m_main_clusterset.m_deps_to_add.empty());
2640 if (!m_main_chunkindex.empty()) {
2641 const auto& chunk_data = *m_main_chunkindex.rbegin();
2642 const auto& chunk_end_entry = m_entries[chunk_data.m_graph_index];
2643 Cluster* cluster = chunk_end_entry.m_locator[0].cluster;
2644 if (chunk_data.m_chunk_count == LinearizationIndex(-1) || chunk_data.m_chunk_count == 1) {
2646 ret.first.resize(1);
2647 Assume(chunk_end_entry.m_ref !=
nullptr);
2648 ret.first[0] = chunk_end_entry.m_ref;
2650 ret.first.resize(chunk_data.m_chunk_count);
2651 auto start_pos = chunk_end_entry.m_main_lin_index + 1 - chunk_data.m_chunk_count;
2652 cluster->GetClusterRefs(*
this,
ret.first, start_pos);
2653 std::reverse(
ret.first.begin(),
ret.first.end());
2655 ret.second = chunk_end_entry.m_main_chunk_feerate;
2660std::vector<TxGraph::Ref*> TxGraphImpl::Trim() noexcept
2662 int level = GetTopLevel();
2663 Assume(m_main_chunkindex_observers == 0 || level != 0);
2664 std::vector<TxGraph::Ref*>
ret;
2667 auto& clusterset = GetClusterSet(level);
2668 if (clusterset.m_oversized ==
false)
return ret;
2669 GroupClusters(level);
2670 Assume(clusterset.m_group_data.has_value());
2672 Assume(clusterset.m_oversized.has_value());
2673 if (clusterset.m_oversized ==
false)
return ret;
2697 std::vector<std::pair<GraphIndex, GraphIndex>> deps_by_parent;
2699 std::vector<std::pair<GraphIndex, GraphIndex>> deps_by_child;
2703 std::vector<TrimTxData> trim_data;
2706 std::vector<std::vector<TrimTxData>::iterator> trim_heap;
2708 std::vector<TrimTxData*> current_deps;
2711 static constexpr auto cmp_fn = [](
auto a,
auto b)
noexcept {
2716 return a->m_chunk_feerate < b->m_chunk_feerate;
2720 static constexpr auto find_fn = [](TrimTxData* arg)
noexcept {
2721 while (arg != arg->m_uf_parent) {
2724 auto par = arg->m_uf_parent;
2725 arg->m_uf_parent = par->m_uf_parent;
2733 static constexpr auto union_fn = [](TrimTxData* arg1, TrimTxData* arg2)
noexcept {
2735 auto rep1 = find_fn(arg1);
2736 auto rep2 = find_fn(arg2);
2739 if (rep1 == rep2)
return rep1;
2742 if (rep1->m_uf_count < rep2->m_uf_count) std::swap(rep1, rep2);
2743 rep2->m_uf_parent = rep1;
2746 rep1->m_uf_size += rep2->m_uf_size;
2747 rep1->m_uf_count += rep2->m_uf_count;
2752 auto locate_fn = [&](GraphIndex index)
noexcept {
2753 auto it = std::lower_bound(trim_data.begin(), trim_data.end(), index, [](TrimTxData& elem, GraphIndex idx)
noexcept {
2754 return elem.m_index < idx;
2756 Assume(it != trim_data.end() && it->m_index == index);
2761 for (
const auto& group_data : clusterset.m_group_data->m_groups) {
2764 deps_by_child.clear();
2765 deps_by_parent.clear();
2768 auto cluster_span = std::span{clusterset.m_group_data->m_group_clusters}
2769 .subspan(group_data.m_cluster_offset, group_data.m_cluster_count);
2771 for (Cluster* cluster : cluster_span) {
2772 size += cluster->AppendTrimData(trim_data, deps_by_child);
2775 if (trim_data.size() <= m_max_cluster_count && size <= m_max_cluster_size)
continue;
2779 std::sort(trim_data.begin(), trim_data.end(), [](
auto& a,
auto& b)
noexcept { return a.m_index < b.m_index; });
2782 deps_by_child.insert(deps_by_child.end(),
2783 clusterset.m_deps_to_add.begin() + group_data.m_deps_offset,
2784 clusterset.m_deps_to_add.begin() + group_data.m_deps_offset + group_data.m_deps_count);
2788 std::sort(deps_by_child.begin(), deps_by_child.end(), [](
auto& a,
auto& b)
noexcept { return a.second < b.second; });
2792 auto deps_it = deps_by_child.begin();
2793 for (
auto trim_it = trim_data.begin(); trim_it != trim_data.end(); ++trim_it) {
2794 trim_it->m_parent_offset = deps_it - deps_by_child.begin();
2795 trim_it->m_deps_left = 0;
2796 while (deps_it != deps_by_child.end() && deps_it->second == trim_it->m_index) {
2797 ++trim_it->m_deps_left;
2800 trim_it->m_parent_count = trim_it->m_deps_left;
2803 if (trim_it->m_deps_left == 0 && trim_it->m_tx_size <= m_max_cluster_size) {
2804 trim_heap.push_back(trim_it);
2807 Assume(deps_it == deps_by_child.end());
2811 deps_by_parent = deps_by_child;
2812 std::sort(deps_by_parent.begin(), deps_by_parent.end(), [](
auto& a,
auto& b)
noexcept { return a.first < b.first; });
2816 deps_it = deps_by_parent.begin();
2817 for (
auto& trim_entry : trim_data) {
2818 trim_entry.m_children_count = 0;
2819 trim_entry.m_children_offset = deps_it - deps_by_parent.begin();
2820 while (deps_it != deps_by_parent.end() && deps_it->first == trim_entry.m_index) {
2821 ++trim_entry.m_children_count;
2825 Assume(deps_it == deps_by_parent.end());
2828 std::make_heap(trim_heap.begin(), trim_heap.end(), cmp_fn);
2842 while (!trim_heap.empty()) {
2844 std::pop_heap(trim_heap.begin(), trim_heap.end(), cmp_fn);
2846 auto& entry = *trim_heap.back();
2847 trim_heap.pop_back();
2850 entry.m_uf_parent = &entry;
2851 entry.m_uf_count = 1;
2852 entry.m_uf_size = entry.m_tx_size;
2855 current_deps.clear();
2856 for (
auto& [par, chl] : std::span{deps_by_child}.subspan(entry.m_parent_offset, entry.m_parent_count)) {
2857 Assume(chl == entry.m_index);
2858 current_deps.push_back(find_fn(&*locate_fn(par)));
2860 std::sort(current_deps.begin(), current_deps.end());
2861 current_deps.erase(std::unique(current_deps.begin(), current_deps.end()), current_deps.end());
2864 uint32_t new_count = 1;
2865 uint64_t new_size = entry.m_tx_size;
2866 for (TrimTxData* ptr : current_deps) {
2867 new_count += ptr->m_uf_count;
2868 new_size += ptr->m_uf_size;
2871 if (new_count > m_max_cluster_count || new_size > m_max_cluster_size)
continue;
2875 for (TrimTxData* ptr : current_deps) rep = union_fn(ptr, rep);
2877 entry.m_deps_left = uint32_t(-1);
2879 for (
auto& [par, chl] : std::span{deps_by_parent}.subspan(entry.m_children_offset, entry.m_children_count)) {
2880 Assume(par == entry.m_index);
2881 auto chl_it = locate_fn(chl);
2884 Assume(chl_it->m_deps_left > 0);
2885 if (--chl_it->m_deps_left == 0) {
2886 trim_heap.push_back(chl_it);
2887 std::push_heap(trim_heap.begin(), trim_heap.end(), cmp_fn);
2896 for (
const auto& trim_entry : trim_data) {
2897 if (trim_entry.m_deps_left != uint32_t(-1)) {
2898 ret.push_back(m_entries[trim_entry.m_index].m_ref);
2899 clusterset.m_to_remove.push_back(trim_entry.m_index);
2903 clusterset.m_group_data.reset();
2904 clusterset.m_oversized =
false;
2915 m_graph->UnlinkRef(m_index);
2923 if (m_graph) m_graph->UnlinkRef(m_index);
2927 m_graph = other.m_graph;
2928 m_index = other.m_index;
2929 other.m_graph =
nullptr;
2937 if (other.m_graph) other.m_graph->UpdateRef(other.m_index, *
this);
2939 std::swap(m_graph, other.m_graph);
2940 std::swap(m_index, other.m_index);
2943std::unique_ptr<TxGraph>
MakeTxGraph(
unsigned max_cluster_count, uint64_t max_cluster_size, uint64_t acceptable_iters)
noexcept
2945 return std::make_unique<TxGraphImpl>(max_cluster_count, max_cluster_size, acceptable_iters);
std::conditional_t<(BITS<=32), bitset_detail::IntBitSet< uint32_t >, std::conditional_t<(BITS<=std::numeric_limits< size_t >::digits), bitset_detail::IntBitSet< size_t >, bitset_detail::MultiIntBitSet< size_t,(BITS+std::numeric_limits< size_t >::digits - 1)/std::numeric_limits< size_t >::digits > > > BitSet
#define Assume(val)
Assume is the identity function.
I randrange(I range) noexcept
Generate a random integer in the range [0..range), with range > 0.
Interface returned by GetBlockBuilder.
TxGraph * m_graph
Which Graph the Entry lives in.
virtual ~Ref()
Destroy this Ref.
Ref & operator=(Ref &&other) noexcept
Ref() noexcept=default
Construct an empty Ref.
Data structure to encapsulate fees, sizes, and dependencies for a set of transactions.
virtual void UpdateRef(GraphIndex index, Ref &new_location) noexcept=0
Inform the TxGraph implementation that a TxGraph::Ref has moved.
uint32_t GraphIndex
Internal identifier for a transaction within a TxGraph.
Data structure that holds a transaction graph's preprocessed data (fee, size, ancestors,...
const SetType & Ancestors(DepGraphIndex i) const noexcept
Get the ancestors of a given transaction i.
SetType GetReducedParents(DepGraphIndex i) const noexcept
Compute the (reduced) set of parents of node i in this graph.
const FeeFrac & FeeRate(DepGraphIndex i) const noexcept
Get the feerate of a given transaction i.
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.
const SetType & Descendants(DepGraphIndex i) const noexcept
Get the descendants of a given transaction i.
const SetType & Positions() const noexcept
Get the set of transactions positions in use.
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.
Data structure encapsulating the chunking of a linearization, permitting removal of subsets.
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< T > Split(const std::span< const char > &sp, std::string_view separators, bool include_sep=false)
Split a string on any char found in separators, returning a vector.
Data structure storing a fee and size, ordered by increasing fee/size.
bool IsEmpty() const noexcept
Check if this is empty (size and fee are 0).
Tagged wrapper around FeeFrac to avoid unit confusion.
static FeePerUnit FromFeeFrac(const FeeFrac &feefrac) noexcept
Convert a FeeFrac to a FeePerUnit.
consteval auto _(util::TranslatedLiteral str)
std::unique_ptr< TxGraph > MakeTxGraph(unsigned max_cluster_count, uint64_t max_cluster_size, uint64_t acceptable_iters) noexcept
Construct a new TxGraph with the specified limit on the number of transactions within a cluster,...
static constexpr unsigned MAX_CLUSTER_COUNT_LIMIT
void ClearShrink(V &v) noexcept
Clear a vector (or std::deque) and release its allocated memory.