19#include <unordered_set>
27static constexpr int MAX_LEVELS{2};
33using LinearizationIndex = uint32_t;
35using ClusterSetIndex = uint32_t;
38enum class QualityLevel
76 uint32_t m_parent_count;
78 uint32_t m_parent_offset;
80 uint32_t m_children_count;
82 uint32_t m_children_offset;
94 TrimTxData* m_uf_parent;
104 friend class TxGraphImpl;
105 friend class BlockBuilderImpl;
113 ClusterSetIndex m_setindex{ClusterSetIndex(-1)};
122 virtual ~Cluster() =
default;
125 Cluster(
const Cluster&) =
delete;
126 Cluster& operator=(
const Cluster&) =
delete;
127 Cluster(Cluster&&) =
delete;
128 Cluster& operator=(Cluster&&) =
delete;
133 bool IsAcceptable() const noexcept
135 return m_quality == QualityLevel::ACCEPTABLE || m_quality == QualityLevel::OPTIMAL;
138 bool IsTopological() const noexcept
140 return m_quality != QualityLevel::NEEDS_FIX && m_quality != QualityLevel::NEEDS_SPLIT_FIX;
143 bool IsOptimal() const noexcept
145 return m_quality == QualityLevel::OPTIMAL;
150 bool IsOversized() const noexcept {
return m_quality == QualityLevel::OVERSIZED_SINGLETON; }
152 bool NeedsSplitting() const noexcept
154 return m_quality == QualityLevel::NEEDS_SPLIT || m_quality == QualityLevel::NEEDS_SPLIT_FIX;
158 virtual DepGraphIndex GetMinIntendedTxCount() const noexcept = 0;
163 virtual
size_t TotalMemoryUsage() const noexcept = 0;
165 virtual
DepGraphIndex GetDepGraphIndexRange() const noexcept = 0;
167 virtual LinearizationIndex GetTxCount() const noexcept = 0;
169 virtual uint64_t GetTotalTxSize() const noexcept = 0;
171 virtual GraphIndex GetClusterEntry(
DepGraphIndex index) const noexcept = 0;
176 virtual
void AddDependencies(SetType parents,
DepGraphIndex child) noexcept = 0;
182 virtual
int GetLevel(const TxGraphImpl& graph) const noexcept = 0;
184 virtual
void UpdateMapping(
DepGraphIndex cluster_idx, GraphIndex graph_idx) noexcept = 0;
189 virtual
void Updated(TxGraphImpl& graph,
int level,
bool rename) noexcept = 0;
191 virtual
void RemoveChunkData(TxGraphImpl& graph) noexcept = 0;
193 virtual Cluster* CopyToStaging(TxGraphImpl& graph) const noexcept = 0;
195 virtual
void GetConflicts(const TxGraphImpl& graph,
std::vector<Cluster*>&
out) const noexcept = 0;
198 virtual
void MakeStagingTransactionsMissing(TxGraphImpl& graph) noexcept = 0;
200 virtual
void Clear(TxGraphImpl& graph,
int level) noexcept = 0;
202 virtual
void MoveToMain(TxGraphImpl& graph) noexcept = 0;
204 virtual
void Compact() noexcept = 0;
210 virtual
void ApplyRemovals(TxGraphImpl& graph,
int level,
std::span<GraphIndex>& to_remove) noexcept = 0;
213 [[nodiscard]] virtual
bool Split(TxGraphImpl& graph,
int level) noexcept = 0;
215 virtual
void Merge(TxGraphImpl& graph,
int level, Cluster& cluster) noexcept = 0;
217 virtual
void ApplyDependencies(TxGraphImpl& graph,
int level,
std::span<
std::pair<GraphIndex, GraphIndex>> to_apply) noexcept = 0;
220 virtual
std::pair<uint64_t,
bool> Relinearize(TxGraphImpl& graph,
int level, uint64_t max_cost) noexcept = 0;
222 virtual
void AppendChunkFeerates(
std::vector<
FeeFrac>&
ret) const noexcept = 0;
226 virtual uint64_t AppendTrimData(
std::vector<TrimTxData>&
ret,
std::vector<
std::pair<GraphIndex, GraphIndex>>& deps) const noexcept = 0;
239 virtual
bool GetClusterRefs(TxGraphImpl& graph,
std::span<
TxGraph::Ref*> range, LinearizationIndex start_pos) noexcept = 0;
243 virtual
void SetFee(TxGraphImpl& graph,
int level,
DepGraphIndex idx, int64_t
fee) noexcept = 0;
247 virtual
void SanityCheck(const TxGraphImpl& graph,
int level) const = 0;
252class GenericClusterImpl final : public Cluster
254 friend class TxGraphImpl;
260 std::vector<GraphIndex> m_mapping;
263 std::vector<DepGraphIndex> m_linearization;
269 static constexpr DepGraphIndex MAX_TX_COUNT{SetType::Size()};
271 GenericClusterImpl() noexcept = delete;
273 explicit GenericClusterImpl(uint64_t
sequence) noexcept;
275 size_t TotalMemoryUsage() const noexcept final;
276 constexpr
DepGraphIndex GetMinIntendedTxCount() const noexcept final {
return MIN_INTENDED_TX_COUNT; }
277 constexpr DepGraphIndex GetMaxTxCount() const noexcept final {
return MAX_TX_COUNT; }
279 LinearizationIndex GetTxCount() const noexcept final {
return m_linearization.size(); }
280 uint64_t GetTotalTxSize() const noexcept final;
281 GraphIndex GetClusterEntry(
DepGraphIndex index) const noexcept final {
return m_mapping[index]; }
283 void AddDependencies(SetType parents,
DepGraphIndex child)
noexcept final;
284 void ExtractTransactions(
const std::function<
void (
DepGraphIndex, GraphIndex,
FeePerWeight)>& visit1_fn,
const std::function<
void (
DepGraphIndex, GraphIndex, SetType)>& visit2_fn)
noexcept final;
285 int GetLevel(
const TxGraphImpl& graph)
const noexcept final;
286 void UpdateMapping(
DepGraphIndex cluster_idx, GraphIndex graph_idx)
noexcept final { m_mapping[cluster_idx] = graph_idx; }
287 void Updated(TxGraphImpl& graph,
int level,
bool rename)
noexcept final;
288 void RemoveChunkData(TxGraphImpl& graph)
noexcept final;
289 Cluster* CopyToStaging(TxGraphImpl& graph)
const noexcept final;
290 void GetConflicts(
const TxGraphImpl& graph, std::vector<Cluster*>&
out)
const noexcept final;
291 void MakeStagingTransactionsMissing(TxGraphImpl& graph)
noexcept final;
292 void Clear(TxGraphImpl& graph,
int level)
noexcept final;
293 void MoveToMain(TxGraphImpl& graph)
noexcept final;
294 void Compact() noexcept final;
295 void ApplyRemovals(TxGraphImpl& graph,
int level,
std::span<GraphIndex>& to_remove) noexcept final;
296 [[nodiscard]]
bool Split(TxGraphImpl& graph,
int level) noexcept final;
297 void Merge(TxGraphImpl& graph,
int level, Cluster& cluster) noexcept final;
298 void ApplyDependencies(TxGraphImpl& graph,
int level,
std::span<
std::pair<GraphIndex, GraphIndex>> to_apply) noexcept final;
299 std::pair<uint64_t,
bool> Relinearize(TxGraphImpl& graph,
int level, uint64_t max_cost) noexcept final;
300 void AppendChunkFeerates(
std::vector<
FeeFrac>&
ret) const noexcept final;
301 uint64_t AppendTrimData(
std::vector<TrimTxData>&
ret,
std::vector<
std::pair<GraphIndex, GraphIndex>>& deps) const noexcept final;
304 bool GetClusterRefs(TxGraphImpl& graph,
std::span<
TxGraph::Ref*> range, LinearizationIndex start_pos) noexcept final;
306 void SetFee(TxGraphImpl& graph,
int level,
DepGraphIndex idx, int64_t
fee) noexcept final;
307 void SanityCheck(const TxGraphImpl& graph,
int level) const final;
311class SingletonClusterImpl final : public Cluster
313 friend class TxGraphImpl;
318 static constexpr auto NO_GRAPH_INDEX = GraphIndex(-1);
320 GraphIndex m_graph_index = NO_GRAPH_INDEX;
328 SingletonClusterImpl() noexcept = delete;
330 explicit SingletonClusterImpl(uint64_t
sequence) noexcept : Cluster(
sequence) {}
332 size_t TotalMemoryUsage() const noexcept final;
333 constexpr
DepGraphIndex GetMinIntendedTxCount() const noexcept final {
return MIN_INTENDED_TX_COUNT; }
334 constexpr DepGraphIndex GetMaxTxCount() const noexcept final {
return MAX_TX_COUNT; }
335 LinearizationIndex GetTxCount() const noexcept final {
return m_graph_index != NO_GRAPH_INDEX; }
336 DepGraphIndex GetDepGraphIndexRange() const noexcept final {
return GetTxCount(); }
337 uint64_t GetTotalTxSize() const noexcept final {
return GetTxCount() ? m_feerate.
size : 0; }
338 GraphIndex GetClusterEntry(
DepGraphIndex index)
const noexcept final {
Assume(index == 0);
Assume(GetTxCount());
return m_graph_index; }
340 void AddDependencies(SetType parents,
DepGraphIndex child)
noexcept final;
341 void ExtractTransactions(
const std::function<
void (
DepGraphIndex, GraphIndex,
FeePerWeight)>& visit1_fn,
const std::function<
void (
DepGraphIndex, GraphIndex, SetType)>& visit2_fn)
noexcept final;
342 int GetLevel(
const TxGraphImpl& graph)
const noexcept final;
343 void UpdateMapping(
DepGraphIndex cluster_idx, GraphIndex graph_idx)
noexcept final {
Assume(cluster_idx == 0); m_graph_index = graph_idx; }
344 void Updated(TxGraphImpl& graph,
int level,
bool rename)
noexcept final;
345 void RemoveChunkData(TxGraphImpl& graph)
noexcept final;
346 Cluster* CopyToStaging(TxGraphImpl& graph)
const noexcept final;
347 void GetConflicts(
const TxGraphImpl& graph, std::vector<Cluster*>&
out)
const noexcept final;
348 void MakeStagingTransactionsMissing(TxGraphImpl& graph)
noexcept final;
349 void Clear(TxGraphImpl& graph,
int level)
noexcept final;
350 void MoveToMain(TxGraphImpl& graph)
noexcept final;
351 void Compact() noexcept final;
352 void ApplyRemovals(TxGraphImpl& graph,
int level,
std::span<GraphIndex>& to_remove) noexcept final;
353 [[nodiscard]]
bool Split(TxGraphImpl& graph,
int level) noexcept final;
354 void Merge(TxGraphImpl& graph,
int level, Cluster& cluster) noexcept final;
355 void ApplyDependencies(TxGraphImpl& graph,
int level,
std::span<
std::pair<GraphIndex, GraphIndex>> to_apply) noexcept final;
356 std::pair<uint64_t,
bool> Relinearize(TxGraphImpl& graph,
int level, uint64_t max_cost) noexcept final;
357 void AppendChunkFeerates(
std::vector<
FeeFrac>&
ret) const noexcept final;
358 uint64_t AppendTrimData(
std::vector<TrimTxData>&
ret,
std::vector<
std::pair<GraphIndex, GraphIndex>>& deps) const noexcept final;
361 bool GetClusterRefs(TxGraphImpl& graph,
std::span<
TxGraph::Ref*> range, LinearizationIndex start_pos) noexcept final;
363 void SetFee(TxGraphImpl& graph,
int level,
DepGraphIndex idx, int64_t
fee) noexcept final;
364 void SanityCheck(const TxGraphImpl& graph,
int level) const final;
390class TxGraphImpl final : public
TxGraph
392 friend class Cluster;
393 friend class SingletonClusterImpl;
394 friend class GenericClusterImpl;
395 friend class BlockBuilderImpl;
402 const uint64_t m_max_cluster_size;
404 const uint64_t m_acceptable_cost;
412 uint32_t m_cluster_offset;
414 uint32_t m_cluster_count;
416 uint32_t m_deps_offset;
418 uint32_t m_deps_count;
425 std::vector<GroupEntry> m_groups;
427 std::vector<Cluster*> m_group_clusters;
434 std::array<std::vector<std::unique_ptr<Cluster>>, int(
QualityLevel::NONE)> m_clusters;
436 std::vector<GraphIndex> m_to_remove;
439 std::vector<std::pair<GraphIndex, GraphIndex>> m_deps_to_add;
441 std::optional<GroupData> m_group_data = GroupData{};
445 std::vector<GraphIndex> m_removed;
448 GraphIndex m_txcount{0};
450 GraphIndex m_txcount_oversized{0};
452 std::optional<bool> m_oversized{
false};
455 size_t m_cluster_usage{0};
457 ClusterSet() noexcept = default;
461 ClusterSet m_main_clusterset;
463 std::optional<ClusterSet> m_staging_clusterset;
465 uint64_t m_next_sequence_counter{0};
471 mutable GraphIndex m_graph_index;
473 LinearizationIndex m_chunk_count;
475 ChunkData(GraphIndex graph_index, LinearizationIndex chunk_count) noexcept :
476 m_graph_index{graph_index}, m_chunk_count{chunk_count} {}
480 static std::strong_ordering CompareClusters(Cluster* a, Cluster* b)
noexcept
483 if (a ==
nullptr || b ==
nullptr) {
484 return (a !=
nullptr) <=> (b !=
nullptr);
487 Assume(a == b || a->m_sequence != b->m_sequence);
488 return a->m_sequence <=> b->m_sequence;
492 std::strong_ordering CompareMainTransactions(GraphIndex a, GraphIndex b)
const noexcept
494 if (a == b)
return std::strong_ordering::equal;
495 Assume(a < m_entries.size() && b < m_entries.size());
496 const auto& entry_a = m_entries[a];
497 const auto& entry_b = m_entries[b];
499 auto feerate_cmp = FeeRateCompare(entry_b.m_main_chunk_feerate, entry_a.m_main_chunk_feerate);
500 if (feerate_cmp < 0)
return std::strong_ordering::less;
501 if (feerate_cmp > 0)
return std::strong_ordering::greater;
506 if (entry_a.m_main_equal_feerate_chunk_prefix_size != entry_b.m_main_equal_feerate_chunk_prefix_size) {
507 return entry_a.m_main_equal_feerate_chunk_prefix_size <=> entry_b.m_main_equal_feerate_chunk_prefix_size;
511 const auto& locator_a = entry_a.m_locator[0];
512 const auto& locator_b = entry_b.m_locator[0];
513 Assume(locator_a.IsPresent() && locator_b.IsPresent());
514 if (locator_a.cluster != locator_b.cluster) {
515 auto fallback_cmp = m_fallback_order(*m_entries[entry_a.m_main_max_chunk_fallback].m_ref,
516 *m_entries[entry_b.m_main_max_chunk_fallback].m_ref);
517 if (fallback_cmp != 0)
return fallback_cmp;
520 return CompareClusters(locator_a.cluster, locator_b.cluster);
523 return entry_a.m_main_lin_index <=> entry_b.m_main_lin_index;
529 const TxGraphImpl*
const m_graph;
531 explicit ChunkOrder(
const TxGraphImpl* graph) : m_graph(graph) {}
533 bool operator()(
const ChunkData& a,
const ChunkData& b)
const noexcept
535 return m_graph->CompareMainTransactions(a.m_graph_index, b.m_graph_index) < 0;
540 using ChunkIndex = std::set<ChunkData, ChunkOrder>;
544 ChunkIndex m_main_chunkindex;
546 size_t m_main_chunkindex_observers{0};
548 std::vector<ChunkIndex::node_type> m_main_chunkindex_discarded;
583 Cluster* cluster{
nullptr};
588 void SetMissing() noexcept { cluster =
nullptr; index = 0; }
590 void SetRemoved() noexcept { cluster =
nullptr; index =
DepGraphIndex(-1); }
592 void SetPresent(Cluster* c,
DepGraphIndex i)
noexcept { cluster = c; index = i; }
594 bool IsMissing() const noexcept {
return cluster ==
nullptr && index == 0; }
596 bool IsRemoved() const noexcept {
return cluster ==
nullptr && index ==
DepGraphIndex(-1); }
598 bool IsPresent() const noexcept {
return cluster !=
nullptr; }
608 ChunkIndex::iterator m_main_chunkindex_iterator;
610 Locator m_locator[MAX_LEVELS];
619 int32_t m_main_equal_feerate_chunk_prefix_size;
621 LinearizationIndex m_main_lin_index;
624 GraphIndex m_main_max_chunk_fallback = GraphIndex(-1);
628 std::vector<Entry> m_entries;
631 std::vector<GraphIndex> m_unlinked;
635 explicit TxGraphImpl(
637 uint64_t max_cluster_size,
638 uint64_t acceptable_cost,
641 m_max_cluster_count(max_cluster_count),
642 m_max_cluster_size(max_cluster_size),
643 m_acceptable_cost(acceptable_cost),
644 m_fallback_order(fallback_order),
645 m_main_chunkindex(ChunkOrder(
this))
647 Assume(max_cluster_count >= 1);
652 ~TxGraphImpl() noexcept;
655 TxGraphImpl(const TxGraphImpl&) = delete;
656 TxGraphImpl& operator=(const TxGraphImpl&) = delete;
657 TxGraphImpl(TxGraphImpl&&) = delete;
658 TxGraphImpl& operator=(TxGraphImpl&&) = delete;
664 void SwapIndexes(GraphIndex a, GraphIndex b,
std::vector<Cluster*>& affected_main) noexcept;
667 Cluster* FindCluster(GraphIndex idx,
int level) const noexcept {
return FindClusterAndLevel(idx, level).first; }
669 std::pair<Cluster*, int> FindClusterAndLevel(GraphIndex idx,
int level)
const noexcept;
671 std::unique_ptr<Cluster> ExtractCluster(
int level, QualityLevel quality, ClusterSetIndex setindex)
noexcept;
673 void DeleteCluster(Cluster& cluster,
int level)
noexcept;
675 ClusterSetIndex InsertCluster(
int level, std::unique_ptr<Cluster>&& cluster, QualityLevel quality)
noexcept;
677 void SetClusterQuality(
int level, QualityLevel old_quality, ClusterSetIndex old_index, QualityLevel new_quality)
noexcept;
679 int GetTopLevel() const noexcept {
return m_staging_clusterset.has_value(); }
681 int GetSpecifiedLevel(
Level level)
const noexcept {
return level == Level::TOP && m_staging_clusterset.has_value(); }
683 ClusterSet& GetClusterSet(
int level)
noexcept;
684 const ClusterSet& GetClusterSet(
int level)
const noexcept;
688 void ClearLocator(
int level, GraphIndex index,
bool oversized_tx)
noexcept;
690 std::vector<Cluster*> GetConflicts() const noexcept;
692 void ClearChunkData(Entry& entry) noexcept;
694 void CreateChunkData(GraphIndex idx, LinearizationIndex chunk_count) noexcept;
696 std::unique_ptr<GenericClusterImpl> CreateEmptyGenericCluster() noexcept
698 return std::make_unique<GenericClusterImpl>(m_next_sequence_counter++);
701 std::unique_ptr<SingletonClusterImpl> CreateEmptySingletonCluster() noexcept
703 return std::make_unique<SingletonClusterImpl>(m_next_sequence_counter++);
707 std::unique_ptr<Cluster> CreateEmptyCluster(
DepGraphIndex tx_count)
noexcept
709 if (tx_count >= SingletonClusterImpl::MIN_INTENDED_TX_COUNT && tx_count <= SingletonClusterImpl::MAX_TX_COUNT) {
710 return CreateEmptySingletonCluster();
712 if (tx_count >= GenericClusterImpl::MIN_INTENDED_TX_COUNT && tx_count <= GenericClusterImpl::MAX_TX_COUNT) {
713 return CreateEmptyGenericCluster();
722 void UpdateRef(GraphIndex idx, Ref& new_location)
noexcept final
724 auto& entry = m_entries[idx];
725 Assume(entry.m_ref !=
nullptr);
726 entry.m_ref = &new_location;
730 void UnlinkRef(GraphIndex idx)
noexcept final
732 auto& entry = m_entries[idx];
733 Assume(entry.m_ref !=
nullptr);
734 Assume(m_main_chunkindex_observers == 0 || !entry.m_locator[0].IsPresent());
737 if (entry.m_locator[0].IsPresent()) {
738 entry.m_locator[0].cluster->RemoveChunkData(*
this);
740 entry.m_ref =
nullptr;
743 bool exists_anywhere{
false};
745 for (
int level = 0; level <= GetTopLevel(); ++level) {
746 if (entry.m_locator[level].IsPresent()) {
747 exists_anywhere =
true;
749 }
else if (entry.m_locator[level].IsRemoved()) {
753 auto& clusterset = GetClusterSet(level);
754 clusterset.m_to_remove.push_back(idx);
756 clusterset.m_group_data = std::nullopt;
762 if (level == GetTopLevel() && clusterset.m_oversized ==
true) {
763 clusterset.m_oversized = std::nullopt;
767 m_unlinked.push_back(idx);
768 if (!exists_anywhere) Compact();
775 void Compact() noexcept;
779 Cluster* PullIn(Cluster* cluster,
int level) noexcept;
782 void ApplyRemovals(
int up_to_level) noexcept;
784 void Split(Cluster& cluster,
int level) noexcept;
786 void SplitAll(
int up_to_level) noexcept;
788 void GroupClusters(
int level) noexcept;
790 void Merge(
std::span<Cluster*> to_merge,
int level) noexcept;
792 void ApplyDependencies(
int level) noexcept;
794 void MakeAcceptable(Cluster& cluster,
int level) noexcept;
796 void MakeAllAcceptable(
int level) noexcept;
800 void AddTransaction(Ref& arg, const
FeePerWeight& feerate) noexcept final;
801 void RemoveTransaction(const Ref& arg) noexcept final;
802 void AddDependency(const Ref& parent, const Ref& child) noexcept final;
803 void SetTransactionFee(const Ref&, int64_t
fee) noexcept final;
805 bool DoWork(uint64_t max_cost) noexcept final;
807 void StartStaging() noexcept final;
808 void CommitStaging() noexcept final;
809 void AbortStaging() noexcept final;
810 bool HaveStaging() const noexcept final {
return m_staging_clusterset.has_value(); }
812 bool Exists(
const Ref& arg,
Level level)
noexcept final;
813 FeePerWeight GetMainChunkFeerate(
const Ref& arg)
noexcept final;
814 FeePerWeight GetIndividualFeerate(
const Ref& arg)
noexcept final;
815 std::vector<Ref*> GetCluster(
const Ref& arg,
Level level)
noexcept final;
816 std::vector<Ref*> GetAncestors(
const Ref& arg,
Level level)
noexcept final;
817 std::vector<Ref*> GetDescendants(
const Ref& arg,
Level level)
noexcept final;
818 std::vector<Ref*> GetAncestorsUnion(std::span<const Ref* const>
args,
Level level)
noexcept final;
819 std::vector<Ref*> GetDescendantsUnion(std::span<const Ref* const>
args,
Level level)
noexcept final;
820 GraphIndex GetTransactionCount(
Level level)
noexcept final;
821 bool IsOversized(
Level level)
noexcept final;
822 std::strong_ordering CompareMainOrder(
const Ref& a,
const Ref& b)
noexcept final;
823 GraphIndex CountDistinctClusters(std::span<const Ref* const> refs,
Level level)
noexcept final;
824 std::pair<std::vector<FeeFrac>, std::vector<FeeFrac>> GetMainStagingDiagrams() noexcept final;
825 std::vector<Ref*> Trim() noexcept final;
827 std::unique_ptr<BlockBuilder> GetBlockBuilder() noexcept final;
830 size_t GetMainMemoryUsage() noexcept final;
832 void SanityCheck() const final;
835TxGraphImpl::ClusterSet& TxGraphImpl::GetClusterSet(
int level) noexcept
837 if (level == 0)
return m_main_clusterset;
839 Assume(m_staging_clusterset.has_value());
840 return *m_staging_clusterset;
843const TxGraphImpl::ClusterSet& TxGraphImpl::GetClusterSet(
int level)
const noexcept
845 if (level == 0)
return m_main_clusterset;
847 Assume(m_staging_clusterset.has_value());
848 return *m_staging_clusterset;
856 TxGraphImpl*
const m_graph;
858 std::unordered_set<uint64_t> m_excluded_clusters;
860 TxGraphImpl::ChunkIndex::const_iterator m_cur_iter;
863 Cluster* m_cur_cluster;
865 bool m_known_end_of_cluster;
868 void Next() noexcept;
872 BlockBuilderImpl(TxGraphImpl& graph) noexcept;
875 ~BlockBuilderImpl() final;
877 void Include() noexcept final;
878 void Skip() noexcept final;
881void TxGraphImpl::ClearChunkData(Entry& entry) noexcept
883 if (entry.m_main_chunkindex_iterator != m_main_chunkindex.end()) {
884 Assume(m_main_chunkindex_observers == 0);
887 m_main_chunkindex_discarded.emplace_back(m_main_chunkindex.extract(entry.m_main_chunkindex_iterator));
888 entry.m_main_chunkindex_iterator = m_main_chunkindex.end();
892void TxGraphImpl::CreateChunkData(GraphIndex idx, LinearizationIndex chunk_count)
noexcept
894 auto& entry = m_entries[idx];
897 Assume(entry.m_ref !=
nullptr);
898 if (!m_main_chunkindex_discarded.empty()) {
900 auto&
node = m_main_chunkindex_discarded.back().value();
901 node.m_graph_index = idx;
902 node.m_chunk_count = chunk_count;
903 auto insert_result = m_main_chunkindex.insert(std::move(m_main_chunkindex_discarded.back()));
904 Assume(insert_result.inserted);
905 entry.m_main_chunkindex_iterator = insert_result.position;
906 m_main_chunkindex_discarded.pop_back();
909 auto emplace_result = m_main_chunkindex.emplace(idx, chunk_count);
910 Assume(emplace_result.second);
911 entry.m_main_chunkindex_iterator = emplace_result.first;
915size_t GenericClusterImpl::TotalMemoryUsage() const noexcept
924 sizeof(std::unique_ptr<Cluster>);
927size_t SingletonClusterImpl::TotalMemoryUsage() const noexcept
932 sizeof(std::unique_ptr<Cluster>);
935uint64_t GenericClusterImpl::GetTotalTxSize() const noexcept
938 for (
auto i : m_linearization) {
946 Assume(graph_idx != GraphIndex(-1));
948 m_mapping.push_back(graph_idx);
949 m_linearization.push_back(
ret);
956 m_graph_index = graph_idx;
961void GenericClusterImpl::AddDependencies(SetType parents,
DepGraphIndex child)
noexcept
966void SingletonClusterImpl::AddDependencies(SetType parents,
DepGraphIndex child)
noexcept
970 Assume(parents == SetType{} || parents == SetType::Fill(0));
973void GenericClusterImpl::ExtractTransactions(
const std::function<
void (
DepGraphIndex, GraphIndex,
FeePerWeight)>& visit1_fn,
const std::function<
void (
DepGraphIndex, GraphIndex, SetType)>& visit2_fn)
noexcept
975 for (
auto pos : m_linearization) {
978 for (
auto pos : m_linearization) {
983 m_linearization.clear();
987void SingletonClusterImpl::ExtractTransactions(
const std::function<
void (
DepGraphIndex, GraphIndex,
FeePerWeight)>& visit1_fn,
const std::function<
void (
DepGraphIndex, GraphIndex, SetType)>& visit2_fn)
noexcept
990 visit1_fn(0, m_graph_index, m_feerate);
991 visit2_fn(0, m_graph_index, SetType{});
992 m_graph_index = NO_GRAPH_INDEX;
996int GenericClusterImpl::GetLevel(
const TxGraphImpl& graph)
const noexcept
999 if (!
Assume(!m_linearization.empty()))
return -1;
1002 const auto& entry = graph.m_entries[m_mapping[m_linearization.front()]];
1004 for (
int level = 0; level < MAX_LEVELS; ++level) {
1005 if (entry.m_locator[level].cluster ==
this)
return level;
1013int SingletonClusterImpl::GetLevel(
const TxGraphImpl& graph)
const noexcept
1016 if (!
Assume(GetTxCount()))
return -1;
1019 const auto& entry = graph.m_entries[m_graph_index];
1021 for (
int level = 0; level < MAX_LEVELS; ++level) {
1022 if (entry.m_locator[level].cluster ==
this)
return level;
1030void TxGraphImpl::ClearLocator(
int level, GraphIndex idx,
bool oversized_tx)
noexcept
1032 auto& entry = m_entries[idx];
1033 auto& clusterset = GetClusterSet(level);
1034 Assume(entry.m_locator[level].IsPresent());
1036 if (level == 0 || !entry.m_locator[level - 1].IsPresent()) {
1037 entry.m_locator[level].SetMissing();
1039 entry.m_locator[level].SetRemoved();
1040 clusterset.m_removed.push_back(idx);
1043 --clusterset.m_txcount;
1044 clusterset.m_txcount_oversized -= oversized_tx;
1046 if (level == 0 && GetTopLevel() == 1) {
1047 if (entry.m_locator[1].IsRemoved()) {
1048 entry.m_locator[1].SetMissing();
1049 }
else if (!entry.m_locator[1].IsPresent()) {
1050 --m_staging_clusterset->m_txcount;
1051 m_staging_clusterset->m_txcount_oversized -= oversized_tx;
1054 if (level == 0) ClearChunkData(entry);
1057void GenericClusterImpl::RemoveChunkData(TxGraphImpl& graph)
noexcept
1060 auto& entry = graph.m_entries[m_mapping[idx]];
1061 graph.ClearChunkData(entry);
1065void SingletonClusterImpl::RemoveChunkData(TxGraphImpl& graph)
noexcept
1067 if (GetTxCount() == 0)
return;
1068 auto& entry = graph.m_entries[m_graph_index];
1069 graph.ClearChunkData(entry);
1072void GenericClusterImpl::Updated(TxGraphImpl& graph,
int level,
bool rename)
noexcept
1076 auto& entry = graph.m_entries[m_mapping[idx]];
1079 if (level == 0 && !rename) graph.ClearChunkData(entry);
1080 entry.m_locator[level].SetPresent(
this, idx);
1090 if (level == 0 && (rename || IsAcceptable())) {
1092 LinearizationIndex lin_idx{0};
1095 FeeFrac equal_feerate_chunk_feerate;
1097 for (
unsigned chunk_idx = 0; chunk_idx < chunking.size(); ++chunk_idx) {
1098 auto& chunk = chunking[chunk_idx];
1099 auto chunk_count = chunk.transactions.Count();
1103 if (chunk.feerate << equal_feerate_chunk_feerate) {
1104 equal_feerate_chunk_feerate = chunk.feerate;
1108 equal_feerate_chunk_feerate += chunk.feerate;
1111 auto it = chunk.transactions.begin();
1112 GraphIndex max_element = m_mapping[*it];
1114 while (it != chunk.transactions.end()) {
1115 GraphIndex this_element = m_mapping[*it];
1116 if (graph.m_fallback_order(*graph.m_entries[this_element].m_ref, *graph.m_entries[max_element].m_ref) > 0) {
1117 max_element = this_element;
1124 GraphIndex graph_idx = m_mapping[idx];
1125 auto& entry = graph.m_entries[graph_idx];
1126 entry.m_main_lin_index = lin_idx++;
1128 entry.m_main_equal_feerate_chunk_prefix_size = equal_feerate_chunk_feerate.
size;
1129 entry.m_main_max_chunk_fallback = max_element;
1130 Assume(chunk.transactions[idx]);
1131 chunk.transactions.Reset(idx);
1132 if (chunk.transactions.None()) {
1134 if (chunk_count == 1 && chunk_idx + 1 == chunking.size()) {
1138 chunk_count = LinearizationIndex(-1);
1140 if (!rename) graph.CreateChunkData(graph_idx, chunk_count);
1148void SingletonClusterImpl::Updated(TxGraphImpl& graph,
int level,
bool rename)
noexcept
1151 if (GetTxCount() == 0)
return;
1153 auto& entry = graph.m_entries[m_graph_index];
1156 if (level == 0 && !rename) graph.ClearChunkData(entry);
1157 entry.m_locator[level].SetPresent(
this, 0);
1160 if (level == 0 && (rename || IsAcceptable())) {
1161 entry.m_main_lin_index = 0;
1162 entry.m_main_chunk_feerate = m_feerate;
1163 entry.m_main_equal_feerate_chunk_prefix_size = m_feerate.
size;
1164 entry.m_main_max_chunk_fallback = m_graph_index;
1167 if (!rename) graph.CreateChunkData(m_graph_index, LinearizationIndex(-1));
1171void GenericClusterImpl::GetConflicts(
const TxGraphImpl& graph, std::vector<Cluster*>&
out)
const noexcept
1173 for (
auto i : m_linearization) {
1174 auto& entry = graph.m_entries[m_mapping[i]];
1177 if (entry.m_locator[0].IsPresent()) {
1178 out.push_back(entry.m_locator[0].cluster);
1183void SingletonClusterImpl::GetConflicts(
const TxGraphImpl& graph, std::vector<Cluster*>&
out)
const noexcept
1186 if (GetTxCount() == 0)
return;
1188 auto& entry = graph.m_entries[m_graph_index];
1191 if (entry.m_locator[0].IsPresent()) {
1192 out.push_back(entry.m_locator[0].cluster);
1196std::vector<Cluster*> TxGraphImpl::GetConflicts() const noexcept
1198 Assume(GetTopLevel() == 1);
1199 auto& clusterset = GetClusterSet(1);
1200 std::vector<Cluster*>
ret;
1202 for (
auto i : clusterset.m_removed) {
1203 auto& entry = m_entries[i];
1204 if (entry.m_locator[0].IsPresent()) {
1205 ret.push_back(entry.m_locator[0].cluster);
1210 auto& clusters = clusterset.m_clusters[quality];
1211 for (
const auto& cluster : clusters) {
1212 cluster->GetConflicts(*
this,
ret);
1216 std::sort(
ret.begin(),
ret.end(), [](Cluster* a, Cluster* b)
noexcept { return CompareClusters(a, b) < 0; });
1217 ret.erase(std::unique(
ret.begin(),
ret.end()),
ret.end());
1221Cluster* GenericClusterImpl::CopyToStaging(TxGraphImpl& graph)
const noexcept
1224 auto ret = graph.CreateEmptyGenericCluster();
1225 auto ptr =
ret.get();
1227 ptr->m_depgraph = m_depgraph;
1228 ptr->m_mapping = m_mapping;
1229 ptr->m_linearization = m_linearization;
1231 graph.InsertCluster(1, std::move(
ret), m_quality);
1233 ptr->Updated(graph, 1,
false);
1235 graph.GetClusterSet(1).m_cluster_usage += ptr->TotalMemoryUsage();
1239Cluster* SingletonClusterImpl::CopyToStaging(TxGraphImpl& graph)
const noexcept
1242 auto ret = graph.CreateEmptySingletonCluster();
1243 auto ptr =
ret.get();
1245 ptr->m_graph_index = m_graph_index;
1246 ptr->m_feerate = m_feerate;
1248 graph.InsertCluster(1, std::move(
ret), m_quality);
1250 ptr->Updated(graph, 1,
false);
1252 graph.GetClusterSet(1).m_cluster_usage += ptr->TotalMemoryUsage();
1256void GenericClusterImpl::ApplyRemovals(TxGraphImpl& graph,
int level, std::span<GraphIndex>& to_remove)
noexcept
1259 Assume(!to_remove.empty());
1261 graph.GetClusterSet(level).m_cluster_usage -= TotalMemoryUsage();
1263 GraphIndex idx = to_remove.front();
1264 Assume(idx < graph.m_entries.size());
1265 auto& entry = graph.m_entries[idx];
1266 auto& locator = entry.m_locator[level];
1268 if (locator.cluster !=
this)
break;
1270 todo.Set(locator.index);
1274 m_mapping[locator.index] = GraphIndex(-1);
1277 entry.m_main_lin_index = LinearizationIndex(-1);
1280 graph.ClearLocator(level, idx, m_quality == QualityLevel::OVERSIZED_SINGLETON);
1281 to_remove = to_remove.subspan(1);
1282 }
while(!to_remove.empty());
1291 m_linearization.erase(std::remove_if(m_linearization.begin(), m_linearization.end(),
1292 [&](
auto pos) { return todo[pos]; }),
1293 m_linearization.end());
1296 graph.GetClusterSet(level).m_cluster_usage += TotalMemoryUsage();
1297 auto new_quality = IsTopological() ? QualityLevel::NEEDS_SPLIT : QualityLevel::NEEDS_SPLIT_FIX;
1298 graph.SetClusterQuality(level, m_quality, m_setindex, new_quality);
1299 Updated(graph, level,
false);
1302void SingletonClusterImpl::ApplyRemovals(TxGraphImpl& graph,
int level, std::span<GraphIndex>& to_remove)
noexcept
1305 Assume(!to_remove.empty());
1307 Assume(to_remove.front() == m_graph_index);
1311 to_remove = to_remove.subspan(1);
1312 }
while (!to_remove.empty() && to_remove.front() == m_graph_index);
1314 graph.ClearLocator(level, m_graph_index, m_quality == QualityLevel::OVERSIZED_SINGLETON);
1315 m_graph_index = NO_GRAPH_INDEX;
1316 graph.SetClusterQuality(level, m_quality, m_setindex, QualityLevel::NEEDS_SPLIT);
1321void GenericClusterImpl::Clear(TxGraphImpl& graph,
int level)
noexcept
1324 graph.GetClusterSet(level).m_cluster_usage -= TotalMemoryUsage();
1325 for (
auto i : m_linearization) {
1326 graph.ClearLocator(level, m_mapping[i], m_quality == QualityLevel::OVERSIZED_SINGLETON);
1329 m_linearization.clear();
1333void SingletonClusterImpl::Clear(TxGraphImpl& graph,
int level)
noexcept
1336 graph.GetClusterSet(level).m_cluster_usage -= TotalMemoryUsage();
1337 graph.ClearLocator(level, m_graph_index, m_quality == QualityLevel::OVERSIZED_SINGLETON);
1338 m_graph_index = NO_GRAPH_INDEX;
1341void GenericClusterImpl::MoveToMain(TxGraphImpl& graph)
noexcept
1343 for (
auto i : m_linearization) {
1344 GraphIndex idx = m_mapping[i];
1345 auto& entry = graph.m_entries[idx];
1346 entry.m_locator[1].SetMissing();
1348 auto quality = m_quality;
1350 graph.GetClusterSet(1).m_cluster_usage -= TotalMemoryUsage();
1351 graph.GetClusterSet(0).m_cluster_usage += TotalMemoryUsage();
1353 auto cluster = graph.ExtractCluster(1, quality, m_setindex);
1354 graph.InsertCluster(0, std::move(cluster), quality);
1355 Updated(graph, 0,
false);
1358void SingletonClusterImpl::MoveToMain(TxGraphImpl& graph)
noexcept
1361 auto& entry = graph.m_entries[m_graph_index];
1362 entry.m_locator[1].SetMissing();
1364 auto quality = m_quality;
1365 graph.GetClusterSet(1).m_cluster_usage -= TotalMemoryUsage();
1366 auto cluster = graph.ExtractCluster(1, quality, m_setindex);
1367 graph.InsertCluster(0, std::move(cluster), quality);
1368 graph.GetClusterSet(0).m_cluster_usage += TotalMemoryUsage();
1369 Updated(graph, 0,
false);
1372void GenericClusterImpl::Compact() noexcept
1374 m_linearization.shrink_to_fit();
1375 m_mapping.shrink_to_fit();
1379void SingletonClusterImpl::Compact() noexcept
1384void GenericClusterImpl::AppendChunkFeerates(std::vector<FeeFrac>&
ret)
const noexcept
1387 ret.reserve(
ret.size() + chunk_feerates.size());
1388 ret.insert(
ret.end(), chunk_feerates.begin(), chunk_feerates.end());
1391void SingletonClusterImpl::AppendChunkFeerates(std::vector<FeeFrac>&
ret)
const noexcept
1394 ret.push_back(m_feerate);
1398uint64_t GenericClusterImpl::AppendTrimData(std::vector<TrimTxData>&
ret, std::vector<std::pair<GraphIndex, GraphIndex>>& deps)
const noexcept
1402 LinearizationIndex pos{0};
1404 auto prev_index = GraphIndex(-1);
1406 for (
const auto& [chunk, chunk_feerate] : linchunking) {
1408 auto chunk_tx_count = chunk.Count();
1409 for (
unsigned j = 0; j < chunk_tx_count; ++j) {
1410 auto cluster_idx = m_linearization[pos];
1412 Assume(chunk[cluster_idx]);
1414 auto& entry =
ret.emplace_back();
1416 entry.m_index = m_mapping[cluster_idx];
1419 if (pos != 0) deps.emplace_back(prev_index, entry.m_index);
1420 prev_index = entry.m_index;
1421 entry.m_tx_size = m_depgraph.
FeeRate(cluster_idx).
size;
1422 size += entry.m_tx_size;
1429uint64_t SingletonClusterImpl::AppendTrimData(std::vector<TrimTxData>&
ret, std::vector<std::pair<GraphIndex, GraphIndex>>& deps)
const noexcept
1431 if (!GetTxCount())
return 0;
1432 auto& entry =
ret.emplace_back();
1433 entry.m_chunk_feerate = m_feerate;
1434 entry.m_index = m_graph_index;
1435 entry.m_tx_size = m_feerate.
size;
1436 return m_feerate.
size;
1442 Assume(NeedsSplitting());
1444 QualityLevel new_quality = IsTopological() ? QualityLevel::NEEDS_RELINEARIZE : QualityLevel::NEEDS_FIX;
1449 std::vector<std::pair<Cluster*, DepGraphIndex>> remap(m_depgraph.
PositionRange());
1450 std::vector<Cluster*> new_clusters;
1453 while (todo.Any()) {
1455 auto component_size = component.Count();
1456 auto split_quality = component_size <= 1 ? QualityLevel::OPTIMAL : new_quality;
1457 if (first && component == todo && SetType::Fill(component_size) == component && component_size >= MIN_INTENDED_TX_COUNT) {
1464 graph.SetClusterQuality(level, m_quality, m_setindex, split_quality);
1467 Updated(graph, level,
false);
1472 auto new_cluster = graph.CreateEmptyCluster(component_size);
1473 new_clusters.push_back(new_cluster.get());
1476 for (
auto i : component) {
1479 graph.InsertCluster(level, std::move(new_cluster), split_quality);
1483 graph.GetClusterSet(level).m_cluster_usage -= TotalMemoryUsage();
1485 for (
auto i : m_linearization) {
1487 Cluster* new_cluster = remap[i].first;
1492 for (
auto i : m_linearization) {
1494 Cluster* new_cluster = remap[i].first;
1496 SetType new_parents;
1497 for (
auto par : m_depgraph.
GetReducedParents(i)) new_parents.Set(remap[par].second);
1498 new_cluster->AddDependencies(new_parents, remap[i].second);
1501 for (Cluster* new_cluster : new_clusters) {
1502 new_cluster->Updated(graph, level,
false);
1503 new_cluster->Compact();
1504 graph.GetClusterSet(level).m_cluster_usage += new_cluster->TotalMemoryUsage();
1509 m_linearization.clear();
1515 Assume(NeedsSplitting());
1517 graph.GetClusterSet(level).m_cluster_usage -= TotalMemoryUsage();
1521void GenericClusterImpl::Merge(TxGraphImpl& graph,
int level, Cluster& other)
noexcept
1524 std::vector<DepGraphIndex> remap(other.GetDepGraphIndexRange());
1531 Assume(new_pos == m_mapping.size());
1532 remap[pos] = new_pos;
1533 m_mapping.push_back(idx);
1534 m_linearization.push_back(new_pos);
1535 }, [&](
DepGraphIndex pos, GraphIndex idx, SetType other_parents)
noexcept {
1538 for (
auto par : other_parents) {
1539 parents.Set(remap[par]);
1545 auto& entry = graph.m_entries[idx];
1548 if (level == 0) graph.ClearChunkData(entry);
1549 entry.m_locator[level].SetPresent(
this, remap[pos]);
1553void SingletonClusterImpl::Merge(TxGraphImpl&,
int, Cluster&)
noexcept
1559void GenericClusterImpl::ApplyDependencies(TxGraphImpl& graph,
int level, std::span<std::pair<GraphIndex, GraphIndex>> to_apply)
noexcept
1562 std::sort(to_apply.begin(), to_apply.end(), [](
auto& a,
auto& b) { return a.second < b.second; });
1564 auto it = to_apply.begin();
1565 while (it != to_apply.end()) {
1566 auto& first_child = graph.m_entries[it->second].m_locator[level];
1567 const auto child_idx = first_child.index;
1571 while (it != to_apply.end()) {
1572 auto& child = graph.m_entries[it->second].m_locator[level];
1573 auto& parent = graph.m_entries[it->first].m_locator[level];
1574 Assume(child.cluster ==
this && parent.cluster ==
this);
1575 if (child.index != child_idx)
break;
1576 parents.Set(parent.index);
1587 Assume(!NeedsSplitting());
1589 graph.SetClusterQuality(level, m_quality, m_setindex, QualityLevel::NEEDS_FIX);
1592 Updated(graph, level,
false);
1595void SingletonClusterImpl::ApplyDependencies(TxGraphImpl&,
int, std::span<std::pair<GraphIndex, GraphIndex>>) noexcept
1601TxGraphImpl::~TxGraphImpl() noexcept
1605 for (
auto& entry : m_entries) {
1606 if (entry.m_ref !=
nullptr) {
1607 GetRefGraph(*entry.m_ref) =
nullptr;
1612std::unique_ptr<Cluster> TxGraphImpl::ExtractCluster(
int level, QualityLevel quality, ClusterSetIndex setindex)
noexcept
1616 auto& clusterset = GetClusterSet(level);
1617 auto& quality_clusters = clusterset.m_clusters[int(quality)];
1618 Assume(setindex < quality_clusters.size());
1621 std::unique_ptr<Cluster>
ret = std::move(quality_clusters[setindex]);
1623 ret->m_setindex = ClusterSetIndex(-1);
1626 auto max_setindex = quality_clusters.size() - 1;
1627 if (setindex != max_setindex) {
1629 quality_clusters.back()->m_setindex = setindex;
1630 quality_clusters[setindex] = std::move(quality_clusters.back());
1633 quality_clusters.pop_back();
1638ClusterSetIndex TxGraphImpl::InsertCluster(
int level, std::unique_ptr<Cluster>&& cluster, QualityLevel quality)
noexcept
1646 auto& clusterset = GetClusterSet(level);
1647 auto& quality_clusters = clusterset.m_clusters[int(quality)];
1648 ClusterSetIndex
ret = quality_clusters.size();
1649 cluster->m_quality = quality;
1650 cluster->m_setindex =
ret;
1651 quality_clusters.push_back(std::move(cluster));
1655void TxGraphImpl::SetClusterQuality(
int level, QualityLevel old_quality, ClusterSetIndex old_index, QualityLevel new_quality)
noexcept
1660 if (old_quality == new_quality)
return;
1662 auto cluster_ptr = ExtractCluster(level, old_quality, old_index);
1664 InsertCluster(level, std::move(cluster_ptr), new_quality);
1667void TxGraphImpl::DeleteCluster(Cluster& cluster,
int level)
noexcept
1670 auto cluster_ptr = ExtractCluster(level, cluster.m_quality, cluster.m_setindex);
1672 cluster_ptr.reset();
1675std::pair<Cluster*, int> TxGraphImpl::FindClusterAndLevel(GraphIndex idx,
int level)
const noexcept
1677 Assume(level >= 0 && level <= GetTopLevel());
1678 auto& entry = m_entries[idx];
1680 for (
int l = level; l >= 0; --l) {
1683 if (entry.m_locator[l].IsMissing())
continue;
1685 if (entry.m_locator[l].IsRemoved())
break;
1687 return {entry.m_locator[l].cluster, l};
1690 return {
nullptr, -1};
1693Cluster* TxGraphImpl::PullIn(Cluster* cluster,
int level)
noexcept
1695 int to_level = GetTopLevel();
1697 Assume(level <= to_level);
1702 MakeAcceptable(*cluster, level);
1703 cluster = cluster->CopyToStaging(*
this);
1708void TxGraphImpl::ApplyRemovals(
int up_to_level)
noexcept
1710 Assume(up_to_level >= 0 && up_to_level <= GetTopLevel());
1711 for (
int level = 0; level <= up_to_level; ++level) {
1712 auto& clusterset = GetClusterSet(level);
1713 auto& to_remove = clusterset.m_to_remove;
1715 if (to_remove.empty())
continue;
1718 for (GraphIndex index : to_remove) {
1719 auto [cluster, cluster_level] = FindClusterAndLevel(index, level);
1720 if (cluster !=
nullptr) PullIn(cluster, cluster_level);
1724 std::sort(to_remove.begin(), to_remove.end(), [&](GraphIndex a, GraphIndex b)
noexcept {
1725 Cluster* cluster_a = m_entries[a].m_locator[level].cluster;
1726 Cluster* cluster_b = m_entries[b].m_locator[level].cluster;
1727 return CompareClusters(cluster_a, cluster_b) < 0;
1730 std::span to_remove_span{to_remove};
1731 while (!to_remove_span.empty()) {
1732 Cluster* cluster = m_entries[to_remove_span.front()].m_locator[level].cluster;
1733 if (cluster !=
nullptr) {
1736 cluster->ApplyRemovals(*
this, level, to_remove_span);
1740 to_remove_span = to_remove_span.subspan(1);
1748void TxGraphImpl::SwapIndexes(GraphIndex a, GraphIndex b, std::vector<Cluster*>& affected_main)
noexcept
1750 Assume(a < m_entries.size());
1751 Assume(b < m_entries.size());
1753 std::swap(m_entries[a], m_entries[b]);
1755 for (
int i = 0; i < 2; ++i) {
1756 GraphIndex idx = i ? b : a;
1757 Entry& entry = m_entries[idx];
1759 if (entry.m_ref) GetRefIndex(*entry.m_ref) = idx;
1761 if (entry.m_main_chunkindex_iterator != m_main_chunkindex.end()) {
1762 entry.m_main_chunkindex_iterator->m_graph_index = idx;
1765 for (
int level = 0; level < MAX_LEVELS; ++level) {
1766 Locator& locator = entry.m_locator[level];
1767 if (locator.IsPresent()) {
1768 locator.cluster->UpdateMapping(locator.index, idx);
1769 if (level == 0) affected_main.push_back(locator.cluster);
1775void TxGraphImpl::Compact() noexcept
1779 if (!m_main_clusterset.m_deps_to_add.empty())
return;
1780 if (!m_main_clusterset.m_to_remove.empty())
return;
1781 Assume(m_main_clusterset.m_removed.empty());
1782 if (m_staging_clusterset.has_value()) {
1783 if (!m_staging_clusterset->m_deps_to_add.empty())
return;
1784 if (!m_staging_clusterset->m_to_remove.empty())
return;
1785 if (!m_staging_clusterset->m_removed.empty())
return;
1795 std::sort(m_unlinked.begin(), m_unlinked.end(), std::greater{});
1797 std::vector<Cluster*> affected_main;
1798 auto last = GraphIndex(-1);
1799 for (GraphIndex idx : m_unlinked) {
1806 Entry& entry = m_entries[idx];
1807 Assume(entry.m_ref ==
nullptr);
1809 for (
int level = 0; level < MAX_LEVELS; ++level) {
1810 Assume(!entry.m_locator[level].IsPresent());
1814 if (idx != m_entries.size() - 1) SwapIndexes(idx, m_entries.size() - 1, affected_main);
1816 m_entries.pop_back();
1821 std::sort(affected_main.begin(), affected_main.end());
1822 affected_main.erase(std::unique(affected_main.begin(), affected_main.end()), affected_main.end());
1823 for (Cluster* cluster : affected_main) {
1824 cluster->Updated(*
this, 0,
true);
1833 ApplyRemovals(level);
1834 bool del = cluster.Split(*
this, level);
1837 DeleteCluster(cluster, level);
1841void TxGraphImpl::SplitAll(
int up_to_level)
noexcept
1843 Assume(up_to_level >= 0 && up_to_level <= GetTopLevel());
1845 ApplyRemovals(up_to_level);
1846 for (
int level = 0; level <= up_to_level; ++level) {
1847 for (
auto quality : {QualityLevel::NEEDS_SPLIT_FIX, QualityLevel::NEEDS_SPLIT}) {
1848 auto& queue = GetClusterSet(level).m_clusters[int(quality)];
1849 while (!queue.empty()) {
1850 Split(*queue.back().get(), level);
1856void TxGraphImpl::GroupClusters(
int level)
noexcept
1858 auto& clusterset = GetClusterSet(level);
1860 if (clusterset.m_group_data.has_value())
return;
1870 std::vector<std::pair<Cluster*, uint64_t>> an_clusters;
1875 std::vector<std::pair<std::pair<GraphIndex, GraphIndex>, uint64_t>> an_deps;
1879 for (
int level_iter = 0; level_iter <= level; ++level_iter) {
1880 for (
auto& cluster : GetClusterSet(level_iter).m_clusters[
int(QualityLevel::OVERSIZED_SINGLETON)]) {
1881 auto graph_idx = cluster->GetClusterEntry(0);
1882 auto cur_cluster = FindCluster(graph_idx, level);
1883 if (cur_cluster ==
nullptr)
continue;
1884 an_clusters.emplace_back(cur_cluster, cur_cluster->m_sequence);
1890 an_deps.reserve(clusterset.m_deps_to_add.size());
1891 for (
const auto& [par, chl] : clusterset.m_deps_to_add) {
1892 auto par_cluster = FindCluster(par, level);
1893 auto chl_cluster = FindCluster(chl, level);
1895 if (par_cluster ==
nullptr || chl_cluster ==
nullptr)
continue;
1896 an_clusters.emplace_back(par_cluster, par_cluster->m_sequence);
1899 if (chl_cluster != par_cluster) an_clusters.emplace_back(chl_cluster, chl_cluster->m_sequence);
1901 an_deps.emplace_back(std::pair{par, chl}, chl_cluster->m_sequence);
1905 std::sort(an_clusters.begin(), an_clusters.end(), [](
auto& a,
auto& b)
noexcept { return a.second < b.second; });
1906 an_clusters.erase(std::unique(an_clusters.begin(), an_clusters.end()), an_clusters.end());
1908 std::sort(an_deps.begin(), an_deps.end(), [&](
auto& a,
auto& b)
noexcept { return a.second < b.second; });
1914 struct PartitionData
1922 PartitionData* parent;
1928 std::vector<PartitionData> partition_data;
1931 auto locate_fn = [&](uint64_t
sequence)
noexcept -> PartitionData* {
1932 auto it = std::lower_bound(partition_data.begin(), partition_data.end(),
sequence,
1933 [](
auto& a, uint64_t seq)
noexcept { return a.sequence < seq; });
1934 Assume(it != partition_data.end());
1940 static constexpr auto find_root_fn = [](PartitionData*
data)
noexcept -> PartitionData* {
1944 auto par =
data->parent;
1945 data->parent = par->parent;
1953 static constexpr auto union_fn = [](PartitionData* arg1, PartitionData* arg2)
noexcept {
1956 auto rep1 = find_root_fn(arg1);
1957 auto rep2 = find_root_fn(arg2);
1958 if (rep1 == rep2)
return rep1;
1961 if (rep1->rank < rep2->rank) std::swap(rep1, rep2);
1962 rep2->parent = rep1;
1963 rep1->rank += (rep1->rank == rep2->rank);
1968 partition_data.resize(an_clusters.size());
1969 for (
size_t i = 0; i < an_clusters.size(); ++i) {
1970 partition_data[i].sequence = an_clusters[i].first->m_sequence;
1971 partition_data[i].parent = &partition_data[i];
1972 partition_data[i].rank = 0;
1977 Cluster* last_chl_cluster{
nullptr};
1978 PartitionData* last_partition{
nullptr};
1979 for (
const auto& [dep,
_] : an_deps) {
1980 auto [par, chl] = dep;
1981 auto par_cluster = FindCluster(par, level);
1982 auto chl_cluster = FindCluster(chl, level);
1983 Assume(chl_cluster !=
nullptr && par_cluster !=
nullptr);
1985 if (par_cluster == chl_cluster)
continue;
1987 if (chl_cluster == last_chl_cluster) {
1991 last_partition = union_fn(locate_fn(par_cluster->m_sequence), last_partition);
1993 last_chl_cluster = chl_cluster;
1994 last_partition = union_fn(locate_fn(par_cluster->m_sequence), locate_fn(chl_cluster->m_sequence));
2000 auto deps_it = an_deps.begin();
2001 for (
size_t i = 0; i < partition_data.size(); ++i) {
2002 auto&
data = partition_data[i];
2005 auto rep_seq = find_root_fn(&
data)->sequence;
2006 an_clusters[i].second = rep_seq;
2008 while (deps_it != an_deps.end()) {
2009 auto [par, chl] = deps_it->first;
2010 auto chl_cluster = FindCluster(chl, level);
2011 Assume(chl_cluster !=
nullptr);
2012 if (chl_cluster->m_sequence >
data.sequence)
break;
2013 deps_it->second = rep_seq;
2021 std::sort(an_deps.begin(), an_deps.end(), [](
auto& a,
auto& b)
noexcept { return a.second < b.second; });
2022 std::sort(an_clusters.begin(), an_clusters.end(), [](
auto& a,
auto& b)
noexcept { return a.second < b.second; });
2026 clusterset.m_group_data = GroupData{};
2027 clusterset.m_group_data->m_group_clusters.reserve(an_clusters.size());
2028 clusterset.m_deps_to_add.clear();
2029 clusterset.m_deps_to_add.reserve(an_deps.size());
2030 clusterset.m_oversized =
false;
2031 auto an_deps_it = an_deps.begin();
2032 auto an_clusters_it = an_clusters.begin();
2033 while (an_clusters_it != an_clusters.end()) {
2035 auto rep = an_clusters_it->second;
2037 auto& new_entry = clusterset.m_group_data->m_groups.emplace_back();
2038 new_entry.m_cluster_offset = clusterset.m_group_data->m_group_clusters.size();
2039 new_entry.m_cluster_count = 0;
2040 new_entry.m_deps_offset = clusterset.m_deps_to_add.size();
2041 new_entry.m_deps_count = 0;
2042 uint32_t total_count{0};
2043 uint64_t total_size{0};
2045 while (an_clusters_it != an_clusters.end() && an_clusters_it->second == rep) {
2046 clusterset.m_group_data->m_group_clusters.push_back(an_clusters_it->first);
2047 total_count += an_clusters_it->first->GetTxCount();
2048 total_size += an_clusters_it->first->GetTotalTxSize();
2050 ++new_entry.m_cluster_count;
2053 while (an_deps_it != an_deps.end() && an_deps_it->second == rep) {
2054 clusterset.m_deps_to_add.push_back(an_deps_it->first);
2056 ++new_entry.m_deps_count;
2059 if (total_count > m_max_cluster_count || total_size > m_max_cluster_size) {
2060 clusterset.m_oversized =
true;
2063 Assume(an_deps_it == an_deps.end());
2064 Assume(an_clusters_it == an_clusters.end());
2068void TxGraphImpl::Merge(std::span<Cluster*> to_merge,
int level)
noexcept
2070 Assume(!to_merge.empty());
2072 if (to_merge.size() == 1)
return;
2077 size_t max_size_pos{0};
2078 DepGraphIndex max_size = to_merge[max_size_pos]->GetTxCount();
2079 GetClusterSet(level).m_cluster_usage -= to_merge[max_size_pos]->TotalMemoryUsage();
2081 for (
size_t i = 1; i < to_merge.size(); ++i) {
2082 GetClusterSet(level).m_cluster_usage -= to_merge[i]->TotalMemoryUsage();
2085 if (size > max_size) {
2090 if (max_size_pos != 0) std::swap(to_merge[0], to_merge[max_size_pos]);
2092 size_t start_idx = 1;
2093 Cluster* into_cluster = to_merge[0];
2094 if (total_size > into_cluster->GetMaxTxCount()) {
2098 auto new_cluster = CreateEmptyCluster(total_size);
2099 into_cluster = new_cluster.get();
2100 InsertCluster(level, std::move(new_cluster), QualityLevel::OPTIMAL);
2106 for (
size_t i = start_idx; i < to_merge.size(); ++i) {
2107 into_cluster->Merge(*
this, level, *to_merge[i]);
2108 DeleteCluster(*to_merge[i], level);
2110 into_cluster->Compact();
2111 GetClusterSet(level).m_cluster_usage += into_cluster->TotalMemoryUsage();
2114void TxGraphImpl::ApplyDependencies(
int level)
noexcept
2116 auto& clusterset = GetClusterSet(level);
2118 if (clusterset.m_oversized ==
true)
return;
2120 GroupClusters(level);
2121 Assume(clusterset.m_group_data.has_value());
2123 if (clusterset.m_deps_to_add.empty())
return;
2125 if (clusterset.m_oversized ==
true)
return;
2128 for (
const auto& group_entry : clusterset.m_group_data->m_groups) {
2129 auto cluster_span = std::span{clusterset.m_group_data->m_group_clusters}
2130 .subspan(group_entry.m_cluster_offset, group_entry.m_cluster_count);
2133 for (Cluster*& cluster : cluster_span) {
2134 cluster = PullIn(cluster, cluster->GetLevel(*
this));
2138 Merge(cluster_span, level);
2141 auto deps_span = std::span{clusterset.m_deps_to_add}
2142 .subspan(group_entry.m_deps_offset, group_entry.m_deps_count);
2143 Assume(!deps_span.empty());
2144 const auto& loc = m_entries[deps_span[0].second].m_locator[level];
2146 loc.cluster->ApplyDependencies(*
this, level, deps_span);
2150 clusterset.m_deps_to_add.clear();
2154 clusterset.m_group_data = GroupData{};
2157std::pair<uint64_t, bool> GenericClusterImpl::Relinearize(TxGraphImpl& graph,
int level, uint64_t max_cost)
noexcept
2160 Assume(!NeedsSplitting());
2162 if (IsOptimal())
return {0,
false};
2164 uint64_t rng_seed = graph.m_rng.rand64();
2166 const auto ref_a = graph.m_entries[m_mapping[a]].m_ref;
2167 const auto ref_b = graph.m_entries[m_mapping[b]].m_ref;
2168 return graph.m_fallback_order(*ref_a, *ref_b);
2170 auto [linearization, optimal, cost] =
Linearize(
2181 m_linearization = std::move(linearization);
2183 bool improved =
false;
2185 graph.SetClusterQuality(level, m_quality, m_setindex, QualityLevel::OPTIMAL);
2187 }
else if (max_cost >= graph.m_acceptable_cost && !IsAcceptable()) {
2188 graph.SetClusterQuality(level, m_quality, m_setindex, QualityLevel::ACCEPTABLE);
2190 }
else if (!IsTopological()) {
2191 graph.SetClusterQuality(level, m_quality, m_setindex, QualityLevel::NEEDS_RELINEARIZE);
2195 Updated(graph, level,
false);
2196 return {cost, improved};
2199std::pair<uint64_t, bool> SingletonClusterImpl::Relinearize(TxGraphImpl& graph,
int level, uint64_t max_cost)
noexcept
2207void TxGraphImpl::MakeAcceptable(Cluster& cluster,
int level)
noexcept
2210 if (!cluster.NeedsSplitting() && !cluster.IsAcceptable() && !cluster.IsOversized()) {
2211 cluster.Relinearize(*
this, level, m_acceptable_cost);
2215void TxGraphImpl::MakeAllAcceptable(
int level)
noexcept
2217 ApplyDependencies(level);
2218 auto& clusterset = GetClusterSet(level);
2219 if (clusterset.m_oversized ==
true)
return;
2220 for (
auto quality : {QualityLevel::NEEDS_FIX, QualityLevel::NEEDS_RELINEARIZE}) {
2221 auto& queue = clusterset.m_clusters[int(quality)];
2222 while (!queue.empty()) {
2223 MakeAcceptable(*queue.back().get(), level);
2228GenericClusterImpl::GenericClusterImpl(uint64_t
sequence) noexcept : Cluster{
sequence} {}
2230void TxGraphImpl::AddTransaction(Ref& arg,
const FeePerWeight& feerate)
noexcept
2232 Assume(m_main_chunkindex_observers == 0 || GetTopLevel() != 0);
2234 Assume(GetRefGraph(arg) ==
nullptr);
2236 auto idx = m_entries.size();
2237 m_entries.emplace_back();
2238 auto& entry = m_entries.back();
2239 entry.m_main_chunkindex_iterator = m_main_chunkindex.end();
2241 GetRefGraph(arg) =
this;
2242 GetRefIndex(arg) = idx;
2244 bool oversized = uint64_t(feerate.
size) > m_max_cluster_size;
2245 auto cluster = CreateEmptyCluster(1);
2246 cluster->AppendTransaction(idx, feerate);
2247 auto cluster_ptr = cluster.get();
2248 int level = GetTopLevel();
2249 auto& clusterset = GetClusterSet(level);
2250 InsertCluster(level, std::move(cluster), oversized ? QualityLevel::OVERSIZED_SINGLETON : QualityLevel::OPTIMAL);
2251 cluster_ptr->Updated(*
this, level,
false);
2252 clusterset.m_cluster_usage += cluster_ptr->TotalMemoryUsage();
2253 ++clusterset.m_txcount;
2256 ++clusterset.m_txcount_oversized;
2257 clusterset.m_oversized =
true;
2258 clusterset.m_group_data = std::nullopt;
2262void TxGraphImpl::RemoveTransaction(
const Ref& arg)
noexcept
2266 if (GetRefGraph(arg) ==
nullptr)
return;
2267 Assume(GetRefGraph(arg) ==
this);
2268 Assume(m_main_chunkindex_observers == 0 || GetTopLevel() != 0);
2270 int level = GetTopLevel();
2271 auto cluster = FindCluster(GetRefIndex(arg), level);
2272 if (cluster ==
nullptr)
return;
2274 auto& clusterset = GetClusterSet(level);
2275 clusterset.m_to_remove.push_back(GetRefIndex(arg));
2277 clusterset.m_group_data.reset();
2278 if (clusterset.m_oversized ==
true) clusterset.m_oversized = std::nullopt;
2281void TxGraphImpl::AddDependency(
const Ref& parent,
const Ref& child)
noexcept
2285 if (GetRefGraph(parent) ==
nullptr || GetRefGraph(child) ==
nullptr)
return;
2286 Assume(GetRefGraph(parent) ==
this && GetRefGraph(child) ==
this);
2287 Assume(m_main_chunkindex_observers == 0 || GetTopLevel() != 0);
2289 if (GetRefIndex(parent) == GetRefIndex(child))
return;
2292 int level = GetTopLevel();
2293 auto par_cluster = FindCluster(GetRefIndex(parent), level);
2294 if (par_cluster ==
nullptr)
return;
2295 auto chl_cluster = FindCluster(GetRefIndex(child), level);
2296 if (chl_cluster ==
nullptr)
return;
2298 auto& clusterset = GetClusterSet(level);
2299 clusterset.m_deps_to_add.emplace_back(GetRefIndex(parent), GetRefIndex(child));
2301 clusterset.m_group_data.reset();
2302 if (clusterset.m_oversized ==
false) clusterset.m_oversized = std::nullopt;
2305bool TxGraphImpl::Exists(
const Ref& arg,
Level level_select)
noexcept
2307 if (GetRefGraph(arg) ==
nullptr)
return false;
2308 Assume(GetRefGraph(arg) ==
this);
2309 size_t level = GetSpecifiedLevel(level_select);
2311 ApplyRemovals(level);
2312 auto cluster = FindCluster(GetRefIndex(arg), level);
2313 return cluster !=
nullptr;
2316void GenericClusterImpl::GetAncestorRefs(
const TxGraphImpl& graph, std::span<std::pair<Cluster*, DepGraphIndex>>&
args, std::vector<TxGraph::Ref*>& output)
noexcept
2319 SetType ancestors_union;
2321 while (!
args.empty()) {
2322 if (
args.front().first !=
this)
break;
2323 ancestors_union |= m_depgraph.
Ancestors(
args.front().second);
2326 Assume(ancestors_union.Any());
2328 for (
auto idx : ancestors_union) {
2329 const auto& entry = graph.m_entries[m_mapping[idx]];
2330 Assume(entry.m_ref !=
nullptr);
2331 output.push_back(entry.m_ref);
2335void SingletonClusterImpl::GetAncestorRefs(
const TxGraphImpl& graph, std::span<std::pair<Cluster*, DepGraphIndex>>&
args, std::vector<TxGraph::Ref*>& output)
noexcept
2338 while (!
args.empty()) {
2339 if (
args.front().first !=
this)
break;
2342 const auto& entry = graph.m_entries[m_graph_index];
2343 Assume(entry.m_ref !=
nullptr);
2344 output.push_back(entry.m_ref);
2347void GenericClusterImpl::GetDescendantRefs(
const TxGraphImpl& graph, std::span<std::pair<Cluster*, DepGraphIndex>>&
args, std::vector<TxGraph::Ref*>& output)
noexcept
2350 SetType descendants_union;
2352 while (!
args.empty()) {
2353 if (
args.front().first !=
this)
break;
2357 Assume(descendants_union.Any());
2359 for (
auto idx : descendants_union) {
2360 const auto& entry = graph.m_entries[m_mapping[idx]];
2361 Assume(entry.m_ref !=
nullptr);
2362 output.push_back(entry.m_ref);
2366void SingletonClusterImpl::GetDescendantRefs(
const TxGraphImpl& graph, std::span<std::pair<Cluster*, DepGraphIndex>>&
args, std::vector<TxGraph::Ref*>& output)
noexcept
2369 GetAncestorRefs(graph,
args, output);
2372bool GenericClusterImpl::GetClusterRefs(TxGraphImpl& graph, std::span<TxGraph::Ref*> range, LinearizationIndex start_pos)
noexcept
2376 for (
auto& ref : range) {
2377 Assume(start_pos < m_linearization.size());
2378 const auto& entry = graph.m_entries[m_mapping[m_linearization[start_pos++]]];
2379 Assume(entry.m_ref !=
nullptr);
2383 return start_pos == m_linearization.size();
2386bool SingletonClusterImpl::GetClusterRefs(TxGraphImpl& graph, std::span<TxGraph::Ref*> range, LinearizationIndex start_pos)
noexcept
2391 const auto& entry = graph.m_entries[m_graph_index];
2392 Assume(entry.m_ref !=
nullptr);
2393 range[0] = entry.m_ref;
2409void GenericClusterImpl::MakeStagingTransactionsMissing(TxGraphImpl& graph)
noexcept
2413 for (
auto ci : m_linearization) {
2414 GraphIndex idx = m_mapping[ci];
2415 auto& entry = graph.m_entries[idx];
2416 entry.m_locator[1].SetMissing();
2420void SingletonClusterImpl::MakeStagingTransactionsMissing(TxGraphImpl& graph)
noexcept
2423 auto& entry = graph.m_entries[m_graph_index];
2424 entry.m_locator[1].SetMissing();
2428std::vector<TxGraph::Ref*> TxGraphImpl::GetAncestors(
const Ref& arg,
Level level_select)
noexcept
2431 if (GetRefGraph(arg) ==
nullptr)
return {};
2432 Assume(GetRefGraph(arg) ==
this);
2434 size_t level = GetSpecifiedLevel(level_select);
2435 ApplyDependencies(level);
2437 Assume(GetClusterSet(level).m_deps_to_add.empty());
2439 auto [cluster, cluster_level] = FindClusterAndLevel(GetRefIndex(arg), level);
2440 if (cluster ==
nullptr)
return {};
2442 std::pair<Cluster*, DepGraphIndex> match = {cluster, m_entries[GetRefIndex(arg)].m_locator[cluster_level].index};
2443 auto matches = std::span(&match, 1);
2444 std::vector<TxGraph::Ref*>
ret;
2445 cluster->GetAncestorRefs(*
this, matches,
ret);
2449std::vector<TxGraph::Ref*> TxGraphImpl::GetDescendants(
const Ref& arg,
Level level_select)
noexcept
2452 if (GetRefGraph(arg) ==
nullptr)
return {};
2453 Assume(GetRefGraph(arg) ==
this);
2455 size_t level = GetSpecifiedLevel(level_select);
2456 ApplyDependencies(level);
2458 Assume(GetClusterSet(level).m_deps_to_add.empty());
2460 auto [cluster, cluster_level] = FindClusterAndLevel(GetRefIndex(arg), level);
2461 if (cluster ==
nullptr)
return {};
2463 std::pair<Cluster*, DepGraphIndex> match = {cluster, m_entries[GetRefIndex(arg)].m_locator[cluster_level].index};
2464 auto matches = std::span(&match, 1);
2465 std::vector<TxGraph::Ref*>
ret;
2466 cluster->GetDescendantRefs(*
this, matches,
ret);
2470std::vector<TxGraph::Ref*> TxGraphImpl::GetAncestorsUnion(std::span<const Ref* const>
args,
Level level_select)
noexcept
2473 size_t level = GetSpecifiedLevel(level_select);
2474 ApplyDependencies(level);
2476 Assume(GetClusterSet(level).m_deps_to_add.empty());
2479 std::vector<std::pair<Cluster*, DepGraphIndex>> matches;
2480 matches.reserve(
args.size());
2481 for (
auto arg :
args) {
2484 if (GetRefGraph(*arg) ==
nullptr)
continue;
2485 Assume(GetRefGraph(*arg) ==
this);
2487 auto [cluster, cluster_level] = FindClusterAndLevel(GetRefIndex(*arg), level);
2488 if (cluster ==
nullptr)
continue;
2490 matches.emplace_back(cluster, m_entries[GetRefIndex(*arg)].m_locator[cluster_level].index);
2493 std::sort(matches.begin(), matches.end(), [](
auto& a,
auto& b)
noexcept { return CompareClusters(a.first, b.first) < 0; });
2495 std::span match_span(matches);
2496 std::vector<TxGraph::Ref*>
ret;
2497 while (!match_span.empty()) {
2498 match_span.front().first->GetAncestorRefs(*
this, match_span,
ret);
2503std::vector<TxGraph::Ref*> TxGraphImpl::GetDescendantsUnion(std::span<const Ref* const>
args,
Level level_select)
noexcept
2506 size_t level = GetSpecifiedLevel(level_select);
2507 ApplyDependencies(level);
2509 Assume(GetClusterSet(level).m_deps_to_add.empty());
2512 std::vector<std::pair<Cluster*, DepGraphIndex>> matches;
2513 matches.reserve(
args.size());
2514 for (
auto arg :
args) {
2517 if (GetRefGraph(*arg) ==
nullptr)
continue;
2518 Assume(GetRefGraph(*arg) ==
this);
2520 auto [cluster, cluster_level] = FindClusterAndLevel(GetRefIndex(*arg), level);
2521 if (cluster ==
nullptr)
continue;
2523 matches.emplace_back(cluster, m_entries[GetRefIndex(*arg)].m_locator[cluster_level].index);
2526 std::sort(matches.begin(), matches.end(), [](
auto& a,
auto& b)
noexcept { return CompareClusters(a.first, b.first) < 0; });
2528 std::span match_span(matches);
2529 std::vector<TxGraph::Ref*>
ret;
2530 while (!match_span.empty()) {
2531 match_span.front().first->GetDescendantRefs(*
this, match_span,
ret);
2536std::vector<TxGraph::Ref*> TxGraphImpl::GetCluster(
const Ref& arg,
Level level_select)
noexcept
2540 if (GetRefGraph(arg) ==
nullptr)
return {};
2541 Assume(GetRefGraph(arg) ==
this);
2543 size_t level = GetSpecifiedLevel(level_select);
2544 ApplyDependencies(level);
2546 Assume(GetClusterSet(level).m_deps_to_add.empty());
2548 auto [cluster, cluster_level] = FindClusterAndLevel(GetRefIndex(arg), level);
2549 if (cluster ==
nullptr)
return {};
2551 MakeAcceptable(*cluster, cluster_level);
2552 std::vector<TxGraph::Ref*>
ret(cluster->GetTxCount());
2553 cluster->GetClusterRefs(*
this,
ret, 0);
2559 size_t level = GetSpecifiedLevel(level_select);
2560 ApplyRemovals(level);
2561 return GetClusterSet(level).m_txcount;
2564FeePerWeight TxGraphImpl::GetIndividualFeerate(
const Ref& arg)
noexcept
2567 if (GetRefGraph(arg) ==
nullptr)
return {};
2568 Assume(GetRefGraph(arg) ==
this);
2571 Cluster* cluster{
nullptr};
2573 for (level = 0; level <= GetTopLevel(); ++level) {
2576 ApplyRemovals(level);
2577 if (m_entries[GetRefIndex(arg)].m_locator[level].IsPresent()) {
2578 cluster = m_entries[GetRefIndex(arg)].m_locator[level].cluster;
2582 if (cluster ==
nullptr)
return {};
2584 return cluster->GetIndividualFeerate(m_entries[GetRefIndex(arg)].m_locator[level].index);
2587FeePerWeight TxGraphImpl::GetMainChunkFeerate(
const Ref& arg)
noexcept
2590 if (GetRefGraph(arg) ==
nullptr)
return {};
2591 Assume(GetRefGraph(arg) ==
this);
2593 ApplyDependencies(0);
2595 Assume(m_main_clusterset.m_deps_to_add.empty());
2597 auto [cluster, cluster_level] = FindClusterAndLevel(GetRefIndex(arg), 0);
2598 if (cluster ==
nullptr)
return {};
2601 MakeAcceptable(*cluster, cluster_level);
2602 const auto& entry = m_entries[GetRefIndex(arg)];
2603 return entry.m_main_chunk_feerate;
2606bool TxGraphImpl::IsOversized(
Level level_select)
noexcept
2608 size_t level = GetSpecifiedLevel(level_select);
2609 auto& clusterset = GetClusterSet(level);
2610 if (clusterset.m_oversized.has_value()) {
2612 return *clusterset.m_oversized;
2614 ApplyRemovals(level);
2615 if (clusterset.m_txcount_oversized > 0) {
2616 clusterset.m_oversized =
true;
2620 GroupClusters(level);
2622 Assume(clusterset.m_oversized.has_value());
2623 return *clusterset.m_oversized;
2626void TxGraphImpl::StartStaging() noexcept
2629 Assume(!m_staging_clusterset.has_value());
2636 ApplyDependencies(0);
2638 m_staging_clusterset.emplace();
2641 m_staging_clusterset->m_txcount = m_main_clusterset.m_txcount;
2642 m_staging_clusterset->m_txcount_oversized = m_main_clusterset.m_txcount_oversized;
2643 m_staging_clusterset->m_deps_to_add = m_main_clusterset.m_deps_to_add;
2644 m_staging_clusterset->m_group_data = m_main_clusterset.m_group_data;
2645 m_staging_clusterset->m_oversized = m_main_clusterset.m_oversized;
2646 Assume(m_staging_clusterset->m_oversized.has_value());
2647 m_staging_clusterset->m_cluster_usage = 0;
2650void TxGraphImpl::AbortStaging() noexcept
2653 Assume(m_staging_clusterset.has_value());
2656 for (
auto idx : m_staging_clusterset->m_removed) {
2657 m_entries[idx].m_locator[1].SetMissing();
2661 for (
auto& cluster : m_staging_clusterset->m_clusters[quality]) {
2662 cluster->MakeStagingTransactionsMissing(*
this);
2666 m_staging_clusterset.reset();
2668 if (!m_main_clusterset.m_group_data.has_value()) {
2671 if (m_main_clusterset.m_to_remove.empty() && m_main_clusterset.m_txcount_oversized > 0) {
2674 m_main_clusterset.m_oversized =
true;
2676 m_main_clusterset.m_oversized = std::nullopt;
2681void TxGraphImpl::CommitStaging() noexcept
2684 Assume(m_staging_clusterset.has_value());
2685 Assume(m_main_chunkindex_observers == 0);
2692 auto conflicts = GetConflicts();
2693 for (Cluster* conflict : conflicts) {
2694 conflict->Clear(*
this, 0);
2695 DeleteCluster(*conflict, 0);
2699 for (
auto idx : m_staging_clusterset->m_removed) {
2700 m_entries[idx].m_locator[1].SetMissing();
2704 auto& stage_sets = m_staging_clusterset->m_clusters[quality];
2705 while (!stage_sets.empty()) {
2706 stage_sets.back()->MoveToMain(*
this);
2710 m_main_clusterset.m_deps_to_add = std::move(m_staging_clusterset->m_deps_to_add);
2711 m_main_clusterset.m_to_remove = std::move(m_staging_clusterset->m_to_remove);
2712 m_main_clusterset.m_group_data = std::move(m_staging_clusterset->m_group_data);
2713 m_main_clusterset.m_oversized = std::move(m_staging_clusterset->m_oversized);
2714 m_main_clusterset.m_txcount = std::move(m_staging_clusterset->m_txcount);
2715 m_main_clusterset.m_txcount_oversized = std::move(m_staging_clusterset->m_txcount_oversized);
2717 m_staging_clusterset.reset();
2721void GenericClusterImpl::SetFee(TxGraphImpl& graph,
int level,
DepGraphIndex idx, int64_t
fee)
noexcept
2730 if (m_quality == QualityLevel::OVERSIZED_SINGLETON) {
2732 }
else if (IsAcceptable()) {
2733 graph.SetClusterQuality(level, m_quality, m_setindex, QualityLevel::NEEDS_RELINEARIZE);
2735 Updated(graph, level,
false);
2738void SingletonClusterImpl::SetFee(TxGraphImpl& graph,
int level,
DepGraphIndex idx, int64_t
fee)
noexcept
2743 Updated(graph, level,
false);
2746void TxGraphImpl::SetTransactionFee(
const Ref& ref, int64_t
fee)
noexcept
2749 if (GetRefGraph(ref) ==
nullptr)
return;
2750 Assume(GetRefGraph(ref) ==
this);
2751 Assume(m_main_chunkindex_observers == 0);
2753 auto& entry = m_entries[GetRefIndex(ref)];
2754 for (
int level = 0; level < MAX_LEVELS; ++level) {
2755 auto& locator = entry.m_locator[level];
2756 if (locator.IsPresent()) {
2757 locator.cluster->SetFee(*
this, level, locator.index,
fee);
2762std::strong_ordering TxGraphImpl::CompareMainOrder(
const Ref& a,
const Ref& b)
noexcept
2765 Assume(GetRefGraph(a) ==
this);
2766 Assume(GetRefGraph(b) ==
this);
2768 ApplyDependencies(0);
2769 Assume(m_main_clusterset.m_deps_to_add.empty());
2771 const auto& entry_a = m_entries[GetRefIndex(a)];
2772 const auto& entry_b = m_entries[GetRefIndex(b)];
2773 const auto& locator_a = entry_a.m_locator[0];
2774 const auto& locator_b = entry_b.m_locator[0];
2775 Assume(locator_a.IsPresent());
2776 Assume(locator_b.IsPresent());
2777 MakeAcceptable(*locator_a.cluster, 0);
2778 MakeAcceptable(*locator_b.cluster, 0);
2780 return CompareMainTransactions(GetRefIndex(a), GetRefIndex(b));
2783TxGraph::GraphIndex TxGraphImpl::CountDistinctClusters(std::span<const Ref* const> refs,
Level level_select)
noexcept
2785 size_t level = GetSpecifiedLevel(level_select);
2786 ApplyDependencies(level);
2787 auto& clusterset = GetClusterSet(level);
2788 Assume(clusterset.m_deps_to_add.empty());
2790 std::vector<Cluster*> clusters;
2791 clusters.reserve(refs.size());
2792 for (
const Ref* ref : refs) {
2794 if (GetRefGraph(*ref) ==
nullptr)
continue;
2795 Assume(GetRefGraph(*ref) ==
this);
2796 auto cluster = FindCluster(GetRefIndex(*ref), level);
2797 if (cluster !=
nullptr) clusters.push_back(cluster);
2800 std::sort(clusters.begin(), clusters.end(), [](Cluster* a, Cluster* b)
noexcept { return CompareClusters(a, b) < 0; });
2801 Cluster* last{
nullptr};
2803 for (Cluster* cluster : clusters) {
2804 ret += (cluster != last);
2810std::pair<std::vector<FeeFrac>, std::vector<FeeFrac>> TxGraphImpl::GetMainStagingDiagrams() noexcept
2812 Assume(m_staging_clusterset.has_value());
2813 MakeAllAcceptable(0);
2814 Assume(m_main_clusterset.m_deps_to_add.empty());
2815 MakeAllAcceptable(1);
2816 Assume(m_staging_clusterset->m_deps_to_add.empty());
2819 auto main_clusters = GetConflicts();
2820 std::vector<FeeFrac> main_feerates, staging_feerates;
2821 for (Cluster* cluster : main_clusters) {
2822 cluster->AppendChunkFeerates(main_feerates);
2826 for (
const auto& cluster : m_staging_clusterset->m_clusters[quality]) {
2827 cluster->AppendChunkFeerates(staging_feerates);
2831 std::sort(main_feerates.begin(), main_feerates.end(), [](
auto& a,
auto& b) { return a > b; });
2832 std::sort(staging_feerates.begin(), staging_feerates.end(), [](
auto& a,
auto& b) { return a > b; });
2833 return std::make_pair(std::move(main_feerates), std::move(staging_feerates));
2836void GenericClusterImpl::SanityCheck(
const TxGraphImpl& graph,
int level)
const
2843 if (!NeedsSplitting()) {
2849 unsigned chunk_num{0};
2853 LinearizationIndex linindex{0};
2856 if (m_linearization.empty())
return;
2857 FeeFrac equal_feerate_prefix = linchunking[chunk_num].feerate;
2858 for (
auto lin_pos : m_linearization) {
2859 assert(lin_pos < m_mapping.size());
2860 const auto& entry = graph.m_entries[m_mapping[lin_pos]];
2862 m_done.Set(lin_pos);
2863 if (IsTopological()) {
2867 assert(entry.m_locator[level].cluster ==
this);
2868 assert(entry.m_locator[level].index == lin_pos);
2870 if (level == 0 && IsAcceptable()) {
2871 assert(entry.m_main_lin_index == linindex);
2873 if (!linchunking[chunk_num].transactions[lin_pos]) {
2876 assert(chunk_num < linchunking.size());
2878 if (linchunking[chunk_num].feerate << equal_feerate_prefix) {
2879 equal_feerate_prefix = linchunking[chunk_num].feerate;
2881 assert(!(linchunking[chunk_num].feerate >> equal_feerate_prefix));
2882 equal_feerate_prefix += linchunking[chunk_num].feerate;
2885 assert(entry.m_main_chunk_feerate == linchunking[chunk_num].feerate);
2886 assert(entry.m_main_equal_feerate_chunk_prefix_size == equal_feerate_prefix.
size);
2889 if (graph.m_main_clusterset.m_to_remove.empty()) {
2890 bool is_chunk_end = (chunk_pos == linchunking[chunk_num].transactions.Count());
2891 assert((entry.m_main_chunkindex_iterator != graph.m_main_chunkindex.end()) == is_chunk_end);
2893 auto& chunk_data = *entry.m_main_chunkindex_iterator;
2894 if (m_done == m_depgraph.
Positions() && chunk_pos == 1) {
2895 assert(chunk_data.m_chunk_count == LinearizationIndex(-1));
2897 assert(chunk_data.m_chunk_count == chunk_pos);
2909void SingletonClusterImpl::SanityCheck(
const TxGraphImpl& graph,
int level)
const
2912 Assume(IsOptimal() || IsOversized() || NeedsSplitting());
2914 const auto& entry = graph.m_entries[m_graph_index];
2916 assert(entry.m_locator[level].cluster ==
this);
2917 assert(entry.m_locator[level].index == 0);
2919 if (level == 0 && IsAcceptable()) {
2920 assert(entry.m_main_lin_index == 0);
2921 assert(entry.m_main_chunk_feerate == m_feerate);
2922 assert(entry.m_main_equal_feerate_chunk_prefix_size == m_feerate.
size);
2923 if (graph.m_main_clusterset.m_to_remove.empty()) {
2924 assert(entry.m_main_chunkindex_iterator != graph.m_main_chunkindex.end());
2925 auto& chunk_data = *entry.m_main_chunkindex_iterator;
2926 assert(chunk_data.m_chunk_count == LinearizationIndex(-1));
2932void TxGraphImpl::SanityCheck()
const
2935 std::set<GraphIndex> expected_unlinked;
2937 std::set<const Cluster*> expected_clusters[MAX_LEVELS];
2939 std::set<GraphIndex> expected_removed[MAX_LEVELS];
2941 std::set<uint64_t> sequences;
2943 std::set<GraphIndex> expected_chunkindex;
2945 bool compact_possible{
true};
2948 for (GraphIndex idx = 0; idx < m_entries.size(); ++idx) {
2949 const auto& entry = m_entries[idx];
2950 if (entry.m_ref ==
nullptr) {
2952 expected_unlinked.insert(idx);
2955 assert(GetRefGraph(*entry.m_ref) ==
this);
2956 assert(GetRefIndex(*entry.m_ref) == idx);
2958 if (entry.m_main_chunkindex_iterator != m_main_chunkindex.end()) {
2960 assert(entry.m_locator[0].IsPresent());
2961 expected_chunkindex.insert(idx);
2964 bool was_present{
false}, was_removed{
false};
2965 for (
int level = 0; level < MAX_LEVELS; ++level) {
2966 const auto& locator = entry.m_locator[level];
2968 assert(locator.IsMissing() + locator.IsRemoved() + locator.IsPresent() == 1);
2969 if (locator.IsPresent()) {
2973 assert(locator.cluster->GetClusterEntry(locator.index) == idx);
2975 expected_clusters[level].insert(locator.cluster);
2977 }
else if (locator.IsRemoved()) {
2981 assert(was_present && !was_removed);
2983 expected_removed[level].insert(idx);
2990 for (
int level = 0; level <= GetTopLevel(); ++level) {
2991 assert(level < MAX_LEVELS);
2992 auto& clusterset = GetClusterSet(level);
2993 std::set<const Cluster*> actual_clusters;
2994 size_t recomputed_cluster_usage{0};
2998 QualityLevel quality{qual};
2999 const auto& quality_clusters = clusterset.m_clusters[qual];
3001 for (ClusterSetIndex setindex = 0; setindex < quality_clusters.size(); ++setindex) {
3002 const auto& cluster = *quality_clusters[setindex];
3004 assert(cluster.GetTxCount() <= m_max_cluster_count);
3007 assert(cluster.GetTxCount() == 0 || level == cluster.GetLevel(*
this));
3012 assert(cluster.IsOversized() || cluster.GetTotalTxSize() <= m_max_cluster_size);
3014 assert(!cluster.IsOversized() || cluster.GetTxCount() == 1);
3017 assert(cluster.GetTxCount() <= cluster.GetMaxTxCount());
3020 if (!cluster.NeedsSplitting()) {
3021 assert(cluster.GetTxCount() >= cluster.GetMinIntendedTxCount());
3025 assert(cluster.m_sequence < m_next_sequence_counter);
3026 assert(!sequences.contains(cluster.m_sequence));
3027 sequences.insert(cluster.m_sequence);
3030 if (cluster.GetTxCount() != 0) {
3031 actual_clusters.insert(&cluster);
3034 cluster.SanityCheck(*
this, level);
3036 assert(cluster.m_quality == quality);
3037 assert(cluster.m_setindex == setindex);
3039 recomputed_cluster_usage += cluster.TotalMemoryUsage();
3044 assert(clusterset.m_cluster_usage == recomputed_cluster_usage);
3047 for (GraphIndex idx : clusterset.m_to_remove) {
3048 assert(idx < m_entries.size());
3056 for (
auto [par_idx, chl_idx] : clusterset.m_deps_to_add) {
3057 assert(par_idx != chl_idx);
3058 assert(par_idx < m_entries.size());
3059 assert(chl_idx < m_entries.size());
3063 assert(actual_clusters == expected_clusters[level]);
3066 std::set<GraphIndex> actual_removed(clusterset.m_removed.begin(), clusterset.m_removed.end());
3067 for (
auto i : expected_unlinked) {
3072 actual_removed.erase(i);
3073 expected_removed[level].erase(i);
3076 assert(actual_removed == expected_removed[level]);
3079 if (!clusterset.m_deps_to_add.empty()) compact_possible =
false;
3080 if (!clusterset.m_to_remove.empty()) compact_possible =
false;
3081 if (!clusterset.m_removed.empty()) compact_possible =
false;
3085 if (level < GetTopLevel())
assert(clusterset.m_oversized.has_value());
3089 std::set<GraphIndex> actual_unlinked(m_unlinked.begin(), m_unlinked.end());
3090 assert(actual_unlinked == expected_unlinked);
3094 if (compact_possible) {
3095 assert(actual_unlinked.empty());
3099 std::set<GraphIndex> actual_chunkindex;
3101 for (
const auto& chunk : m_main_chunkindex) {
3102 GraphIndex idx = chunk.m_graph_index;
3103 actual_chunkindex.insert(idx);
3104 auto chunk_feerate = m_entries[idx].m_main_chunk_feerate;
3105 if (!last_chunk_feerate.
IsEmpty()) {
3106 assert(FeeRateCompare(last_chunk_feerate, chunk_feerate) >= 0);
3108 last_chunk_feerate = chunk_feerate;
3110 assert(actual_chunkindex == expected_chunkindex);
3113bool TxGraphImpl::DoWork(uint64_t max_cost)
noexcept
3115 uint64_t cost_done{0};
3118 for (QualityLevel quality : {QualityLevel::NEEDS_FIX, QualityLevel::NEEDS_RELINEARIZE, QualityLevel::ACCEPTABLE}) {
3120 for (
int level = GetTopLevel(); level >= 0; --level) {
3122 if (level == 0 && m_main_chunkindex_observers != 0)
continue;
3123 ApplyDependencies(level);
3124 auto& clusterset = GetClusterSet(level);
3126 if (clusterset.m_oversized ==
true)
continue;
3127 auto& queue = clusterset.m_clusters[int(quality)];
3128 while (!queue.empty()) {
3129 if (cost_done >= max_cost)
return false;
3133 auto pos = m_rng.
randrange<
size_t>(queue.size());
3134 auto cost_now = max_cost - cost_done;
3135 if (quality == QualityLevel::NEEDS_FIX || quality == QualityLevel::NEEDS_RELINEARIZE) {
3140 cost_now = std::min(cost_now, m_acceptable_cost);
3142 auto [cost, improved] = queue[pos].get()->Relinearize(*
this, level, cost_now);
3149 if (!improved)
return false;
3159void BlockBuilderImpl::Next() noexcept
3162 if (m_cur_iter == m_graph->m_main_chunkindex.end())
return;
3166 m_cur_cluster =
nullptr;
3167 if (m_cur_iter == m_graph->m_main_chunkindex.end())
break;
3169 const auto& chunk_data = *m_cur_iter;
3170 const auto& chunk_end_entry = m_graph->m_entries[chunk_data.m_graph_index];
3171 m_cur_cluster = chunk_end_entry.m_locator[0].cluster;
3172 m_known_end_of_cluster =
false;
3174 if (!m_excluded_clusters.contains(m_cur_cluster->m_sequence))
break;
3178std::optional<std::pair<std::vector<TxGraph::Ref*>,
FeePerWeight>> BlockBuilderImpl::GetCurrentChunk() noexcept
3180 std::optional<std::pair<std::vector<TxGraph::Ref*>,
FeePerWeight>>
ret;
3182 if (m_cur_iter != m_graph->m_main_chunkindex.end()) {
3184 const auto& chunk_data = *m_cur_iter;
3185 const auto& chunk_end_entry = m_graph->m_entries[chunk_data.m_graph_index];
3186 if (chunk_data.m_chunk_count == LinearizationIndex(-1)) {
3189 ret->first.resize(1);
3190 Assume(chunk_end_entry.m_ref !=
nullptr);
3191 ret->first[0] = chunk_end_entry.m_ref;
3192 m_known_end_of_cluster =
true;
3195 ret->first.resize(chunk_data.m_chunk_count);
3196 auto start_pos = chunk_end_entry.m_main_lin_index + 1 - chunk_data.m_chunk_count;
3197 m_known_end_of_cluster = m_cur_cluster->GetClusterRefs(*m_graph,
ret->first, start_pos);
3200 Assume(!m_known_end_of_cluster || chunk_data.m_chunk_count > 1);
3202 ret->second = chunk_end_entry.m_main_chunk_feerate;
3207BlockBuilderImpl::BlockBuilderImpl(TxGraphImpl& graph) noexcept : m_graph(&graph)
3210 m_graph->MakeAllAcceptable(0);
3212 Assume(m_graph->m_main_clusterset.m_deps_to_add.empty());
3215 ++m_graph->m_main_chunkindex_observers;
3217 m_cur_iter = m_graph->m_main_chunkindex.begin();
3218 m_cur_cluster =
nullptr;
3219 if (m_cur_iter != m_graph->m_main_chunkindex.end()) {
3221 const auto& chunk_data = *m_cur_iter;
3222 const auto& chunk_end_entry = m_graph->m_entries[chunk_data.m_graph_index];
3223 m_cur_cluster = chunk_end_entry.m_locator[0].cluster;
3227BlockBuilderImpl::~BlockBuilderImpl()
3229 Assume(m_graph->m_main_chunkindex_observers > 0);
3231 --m_graph->m_main_chunkindex_observers;
3234void BlockBuilderImpl::Include() noexcept
3241void BlockBuilderImpl::Skip() noexcept
3247 if (m_cur_cluster !=
nullptr && !m_known_end_of_cluster) {
3248 m_excluded_clusters.insert(m_cur_cluster->m_sequence);
3253std::unique_ptr<TxGraph::BlockBuilder> TxGraphImpl::GetBlockBuilder() noexcept
3255 return std::make_unique<BlockBuilderImpl>(*
this);
3258std::pair<std::vector<TxGraph::Ref*>,
FeePerWeight> TxGraphImpl::GetWorstMainChunk() noexcept
3262 MakeAllAcceptable(0);
3263 Assume(m_main_clusterset.m_deps_to_add.empty());
3265 if (!m_main_chunkindex.empty()) {
3266 const auto& chunk_data = *m_main_chunkindex.rbegin();
3267 const auto& chunk_end_entry = m_entries[chunk_data.m_graph_index];
3268 Cluster* cluster = chunk_end_entry.m_locator[0].cluster;
3269 if (chunk_data.m_chunk_count == LinearizationIndex(-1) || chunk_data.m_chunk_count == 1) {
3271 ret.first.resize(1);
3272 Assume(chunk_end_entry.m_ref !=
nullptr);
3273 ret.first[0] = chunk_end_entry.m_ref;
3275 ret.first.resize(chunk_data.m_chunk_count);
3276 auto start_pos = chunk_end_entry.m_main_lin_index + 1 - chunk_data.m_chunk_count;
3277 cluster->GetClusterRefs(*
this,
ret.first, start_pos);
3278 std::reverse(
ret.first.begin(),
ret.first.end());
3280 ret.second = chunk_end_entry.m_main_chunk_feerate;
3285std::vector<TxGraph::Ref*> TxGraphImpl::Trim() noexcept
3287 int level = GetTopLevel();
3288 Assume(m_main_chunkindex_observers == 0 || level != 0);
3289 std::vector<TxGraph::Ref*>
ret;
3292 auto& clusterset = GetClusterSet(level);
3293 if (clusterset.m_oversized ==
false)
return ret;
3294 GroupClusters(level);
3295 Assume(clusterset.m_group_data.has_value());
3297 Assume(clusterset.m_oversized.has_value());
3298 if (clusterset.m_oversized ==
false)
return ret;
3322 std::vector<std::pair<GraphIndex, GraphIndex>> deps_by_parent;
3324 std::vector<std::pair<GraphIndex, GraphIndex>> deps_by_child;
3328 std::vector<TrimTxData> trim_data;
3331 std::vector<std::vector<TrimTxData>::iterator> trim_heap;
3333 std::vector<TrimTxData*> current_deps;
3336 static constexpr auto cmp_fn = [](
auto a,
auto b)
noexcept {
3341 return a->m_chunk_feerate < b->m_chunk_feerate;
3345 static constexpr auto find_fn = [](TrimTxData* arg)
noexcept {
3346 while (arg != arg->m_uf_parent) {
3349 auto par = arg->m_uf_parent;
3350 arg->m_uf_parent = par->m_uf_parent;
3358 static constexpr auto union_fn = [](TrimTxData* arg1, TrimTxData* arg2)
noexcept {
3360 auto rep1 = find_fn(arg1);
3361 auto rep2 = find_fn(arg2);
3364 if (rep1 == rep2)
return rep1;
3367 if (rep1->m_uf_count < rep2->m_uf_count) std::swap(rep1, rep2);
3368 rep2->m_uf_parent = rep1;
3371 rep1->m_uf_size += rep2->m_uf_size;
3372 rep1->m_uf_count += rep2->m_uf_count;
3377 auto locate_fn = [&](GraphIndex index)
noexcept {
3378 auto it = std::lower_bound(trim_data.begin(), trim_data.end(), index, [](TrimTxData& elem, GraphIndex idx)
noexcept {
3379 return elem.m_index < idx;
3381 Assume(it != trim_data.end() && it->m_index == index);
3386 for (
const auto& group_data : clusterset.m_group_data->m_groups) {
3389 deps_by_child.clear();
3390 deps_by_parent.clear();
3393 auto cluster_span = std::span{clusterset.m_group_data->m_group_clusters}
3394 .subspan(group_data.m_cluster_offset, group_data.m_cluster_count);
3396 for (Cluster* cluster : cluster_span) {
3397 MakeAcceptable(*cluster, cluster->GetLevel(*
this));
3398 size += cluster->AppendTrimData(trim_data, deps_by_child);
3401 if (trim_data.size() <= m_max_cluster_count && size <= m_max_cluster_size)
continue;
3405 std::sort(trim_data.begin(), trim_data.end(), [](
auto& a,
auto& b)
noexcept { return a.m_index < b.m_index; });
3408 deps_by_child.insert(deps_by_child.end(),
3409 clusterset.m_deps_to_add.begin() + group_data.m_deps_offset,
3410 clusterset.m_deps_to_add.begin() + group_data.m_deps_offset + group_data.m_deps_count);
3414 std::sort(deps_by_child.begin(), deps_by_child.end(), [](
auto& a,
auto& b)
noexcept { return a.second < b.second; });
3418 auto deps_it = deps_by_child.begin();
3419 for (
auto trim_it = trim_data.begin(); trim_it != trim_data.end(); ++trim_it) {
3420 trim_it->m_parent_offset = deps_it - deps_by_child.begin();
3421 trim_it->m_deps_left = 0;
3422 while (deps_it != deps_by_child.end() && deps_it->second == trim_it->m_index) {
3423 ++trim_it->m_deps_left;
3426 trim_it->m_parent_count = trim_it->m_deps_left;
3429 if (trim_it->m_deps_left == 0 && trim_it->m_tx_size <= m_max_cluster_size) {
3430 trim_heap.push_back(trim_it);
3433 Assume(deps_it == deps_by_child.end());
3437 deps_by_parent = deps_by_child;
3438 std::sort(deps_by_parent.begin(), deps_by_parent.end(), [](
auto& a,
auto& b)
noexcept { return a.first < b.first; });
3442 deps_it = deps_by_parent.begin();
3443 for (
auto& trim_entry : trim_data) {
3444 trim_entry.m_children_count = 0;
3445 trim_entry.m_children_offset = deps_it - deps_by_parent.begin();
3446 while (deps_it != deps_by_parent.end() && deps_it->first == trim_entry.m_index) {
3447 ++trim_entry.m_children_count;
3451 Assume(deps_it == deps_by_parent.end());
3454 std::make_heap(trim_heap.begin(), trim_heap.end(), cmp_fn);
3468 while (!trim_heap.empty()) {
3470 std::pop_heap(trim_heap.begin(), trim_heap.end(), cmp_fn);
3472 auto& entry = *trim_heap.back();
3473 trim_heap.pop_back();
3476 entry.m_uf_parent = &entry;
3477 entry.m_uf_count = 1;
3478 entry.m_uf_size = entry.m_tx_size;
3481 current_deps.clear();
3482 for (
auto& [par, chl] : std::span{deps_by_child}.subspan(entry.m_parent_offset, entry.m_parent_count)) {
3483 Assume(chl == entry.m_index);
3484 current_deps.push_back(find_fn(&*locate_fn(par)));
3486 std::sort(current_deps.begin(), current_deps.end());
3487 current_deps.erase(std::unique(current_deps.begin(), current_deps.end()), current_deps.end());
3490 uint32_t new_count = 1;
3491 uint64_t new_size = entry.m_tx_size;
3492 for (TrimTxData* ptr : current_deps) {
3493 new_count += ptr->m_uf_count;
3494 new_size += ptr->m_uf_size;
3497 if (new_count > m_max_cluster_count || new_size > m_max_cluster_size)
continue;
3501 for (TrimTxData* ptr : current_deps) rep = union_fn(ptr, rep);
3503 entry.m_deps_left = uint32_t(-1);
3505 for (
auto& [par, chl] : std::span{deps_by_parent}.subspan(entry.m_children_offset, entry.m_children_count)) {
3506 Assume(par == entry.m_index);
3507 auto chl_it = locate_fn(chl);
3510 Assume(chl_it->m_deps_left > 0);
3511 if (--chl_it->m_deps_left == 0) {
3512 trim_heap.push_back(chl_it);
3513 std::push_heap(trim_heap.begin(), trim_heap.end(), cmp_fn);
3522 for (
const auto& trim_entry : trim_data) {
3523 if (trim_entry.m_deps_left != uint32_t(-1)) {
3524 ret.push_back(m_entries[trim_entry.m_index].m_ref);
3525 clusterset.m_to_remove.push_back(trim_entry.m_index);
3529 clusterset.m_group_data.reset();
3530 clusterset.m_oversized =
false;
3535size_t TxGraphImpl::GetMainMemoryUsage() noexcept
3539 ApplyDependencies(0);
3542 m_main_clusterset.m_cluster_usage +
3544 sizeof(Entry) * m_main_clusterset.m_txcount +
3556 m_graph->UnlinkRef(m_index);
3564 if (other.m_graph) other.m_graph->UpdateRef(other.m_index, *
this);
3566 std::swap(m_graph, other.m_graph);
3567 std::swap(m_index, other.m_index);
3571 unsigned max_cluster_count,
3572 uint64_t max_cluster_size,
3573 uint64_t acceptable_cost,
3576 return std::make_unique<TxGraphImpl>(
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.
virtual ~Ref()
Destroy this Ref.
Ref() noexcept=default
Construct an empty Ref.
Data structure to encapsulate fees, sizes, and dependencies for a set of transactions.
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.
size_t DynamicMemoryUsage() const noexcept
const SetType & Positions() const noexcept
Get the set of transactions positions in use.
void Compact() noexcept
Reduce memory usage if possible.
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.
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_cost, uint64_t rng_seed, const StrongComparator< DepGraphIndex > auto &fallback_order, std::span< const DepGraphIndex > old_linearization={}, bool is_topological=true) noexcept
Find or improve a linearization for a cluster.
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.
static size_t MallocUsage(size_t alloc)
Compute the total memory used by allocating alloc bytes.
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_cost, const std::function< std::strong_ordering(const TxGraph::Ref &, const TxGraph::Ref &)> &fallback_order) 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.