20#include <unordered_set>
40enum class QualityLevel
96 TrimTxData* m_uf_parent;
106 friend class TxGraphImpl;
107 friend class BlockBuilderImpl;
113 QualityLevel m_quality{QualityLevel::NONE};
127 Cluster(
const Cluster&) =
delete;
128 Cluster& operator=(
const Cluster&) =
delete;
129 Cluster(Cluster&&) =
delete;
130 Cluster& operator=(Cluster&&) =
delete;
137 return m_quality == QualityLevel::ACCEPTABLE || m_quality == QualityLevel::OPTIMAL;
142 return m_quality != QualityLevel::NEEDS_FIX && m_quality != QualityLevel::NEEDS_SPLIT_FIX;
147 return m_quality == QualityLevel::OPTIMAL;
152 bool IsOversized() const noexcept {
return m_quality == QualityLevel::OVERSIZED_SINGLETON; }
156 return m_quality == QualityLevel::NEEDS_SPLIT || m_quality == QualityLevel::NEEDS_SPLIT_FIX;
197 virtual void GetConflicts(const TxGraphImpl& graph,
std::
vector<Cluster*>& out) const noexcept = 0;
202 virtual void Clear(TxGraphImpl& graph,
int level) noexcept = 0;
206 virtual void Compact() noexcept = 0;
215 [[
nodiscard]]
virtual bool Split(TxGraphImpl& graph,
int level) noexcept = 0;
217 virtual void Merge(TxGraphImpl& graph,
int level, Cluster& cluster) noexcept = 0;
256 friend class TxGraphImpl;
262 std::vector<GraphIndex> m_mapping;
265 std::vector<DepGraphIndex> m_linearization;
271 static constexpr DepGraphIndex MAX_TX_COUNT{SetType::Size()};
273 GenericClusterImpl() noexcept =
delete;
281 LinearizationIndex GetTxCount() const noexcept final {
return m_linearization.size(); }
282 uint64_t GetTotalTxSize() const noexcept final;
287 int GetLevel(
const TxGraphImpl& graph)
const noexcept final;
289 void Updated(TxGraphImpl& graph,
int level,
bool rename)
noexcept final;
291 Cluster*
CopyToStaging(TxGraphImpl& graph)
const noexcept final;
292 void GetConflicts(
const TxGraphImpl& graph, std::vector<Cluster*>& out)
const noexcept final;
294 void Clear(TxGraphImpl& graph,
int level)
noexcept final;
295 void MoveToMain(TxGraphImpl& graph)
noexcept final;
296 void Compact() noexcept final;
298 [[
nodiscard]]
bool Split(TxGraphImpl& graph,
int level) noexcept final;
299 void Merge(TxGraphImpl& graph,
int level, Cluster& cluster) noexcept final;
309 void SanityCheck(const TxGraphImpl& graph,
int level) const final;
315 friend class TxGraphImpl;
320 static constexpr auto NO_GRAPH_INDEX = GraphIndex(-1);
322 GraphIndex m_graph_index = NO_GRAPH_INDEX;
330 SingletonClusterImpl() noexcept =
delete;
337 LinearizationIndex GetTxCount() const noexcept final {
return m_graph_index != NO_GRAPH_INDEX; }
339 uint64_t GetTotalTxSize() const noexcept final {
return GetTxCount() ? m_feerate.
size : 0; }
344 int GetLevel(
const TxGraphImpl& graph)
const noexcept final;
346 void Updated(TxGraphImpl& graph,
int level,
bool rename)
noexcept final;
348 Cluster*
CopyToStaging(TxGraphImpl& graph)
const noexcept final;
349 void GetConflicts(
const TxGraphImpl& graph, std::vector<Cluster*>& out)
const noexcept final;
351 void Clear(TxGraphImpl& graph,
int level)
noexcept final;
352 void MoveToMain(TxGraphImpl& graph)
noexcept final;
353 void Compact() noexcept final;
355 [[
nodiscard]]
bool Split(TxGraphImpl& graph,
int level) noexcept final;
356 void Merge(TxGraphImpl& graph,
int level, Cluster& cluster) noexcept final;
366 void SanityCheck(const TxGraphImpl& graph,
int level) const final;
394 friend class Cluster;
395 friend class SingletonClusterImpl;
396 friend class GenericClusterImpl;
397 friend class BlockBuilderImpl;
427 std::vector<GroupEntry> m_groups;
429 std::vector<Cluster*> m_group_clusters;
436 std::array<std::vector<std::unique_ptr<Cluster>>,
int(QualityLevel::NONE)> m_clusters;
438 std::vector<GraphIndex> m_to_remove;
441 std::vector<std::pair<GraphIndex, GraphIndex>> m_deps_to_add;
443 std::optional<GroupData> m_group_data = GroupData{};
447 std::vector<GraphIndex> m_removed;
450 GraphIndex m_txcount{0};
452 GraphIndex m_txcount_oversized{0};
454 std::optional<bool> m_oversized{
false};
457 size_t m_cluster_usage{0};
459 ClusterSet() noexcept =
default;
463 ClusterSet m_main_clusterset;
465 std::optional<ClusterSet> m_staging_clusterset;
467 uint64_t m_next_sequence_counter{0};
473 mutable GraphIndex m_graph_index;
482 static std::strong_ordering
CompareClusters(Cluster*
a, Cluster* b)
noexcept
485 if (
a ==
nullptr || b ==
nullptr) {
486 return (
a !=
nullptr) <=> (b !=
nullptr);
489 Assume(
a == b ||
a->m_sequence != b->m_sequence);
490 return a->m_sequence <=> b->m_sequence;
496 if (
a == b)
return std::strong_ordering::equal;
497 Assume(
a < m_entries.size() && b < m_entries.size());
499 const auto&
entry_b = m_entries[b];
507 if (
entry_a.m_main_equal_feerate_chunk_prefix_size !=
entry_b.m_main_equal_feerate_chunk_prefix_size) {
508 return entry_a.m_main_equal_feerate_chunk_prefix_size <=>
entry_b.m_main_equal_feerate_chunk_prefix_size;
517 *m_entries[
entry_b.m_main_max_chunk_fallback].m_ref);
530 const TxGraphImpl*
const m_graph;
532 explicit ChunkOrder(
const TxGraphImpl* graph) : m_graph(graph) {}
534 bool operator()(
const ChunkData&
a,
const ChunkData& b)
const noexcept
536 return m_graph->CompareMainTransactions(
a.m_graph_index, b.m_graph_index) < 0;
541 using ChunkIndex = std::set<ChunkData, ChunkOrder>;
545 ChunkIndex m_main_chunkindex;
547 size_t m_main_chunkindex_observers{0};
549 std::vector<ChunkIndex::node_type> m_main_chunkindex_discarded;
584 Cluster* cluster{
nullptr};
589 void SetMissing() noexcept { cluster =
nullptr; index = 0; }
595 bool IsMissing() const noexcept {
return cluster ==
nullptr && index == 0; }
599 bool IsPresent() const noexcept {
return cluster !=
nullptr; }
609 ChunkIndex::iterator m_main_chunkindex_iterator;
620 int32_t m_main_equal_feerate_chunk_prefix_size;
625 GraphIndex m_main_max_chunk_fallback = GraphIndex(-1);
629 std::vector<Entry> m_entries;
632 std::vector<GraphIndex> m_unlinked;
636 explicit TxGraphImpl(
642 m_max_cluster_count(max_cluster_count),
643 m_max_cluster_size(max_cluster_size),
646 m_main_chunkindex(ChunkOrder(
this))
648 Assume(max_cluster_count >= 1);
656 TxGraphImpl(const TxGraphImpl&) =
delete;
657 TxGraphImpl& operator=(const TxGraphImpl&) =
delete;
658 TxGraphImpl(TxGraphImpl&&) =
delete;
659 TxGraphImpl& operator=(TxGraphImpl&&) =
delete;
680 int GetTopLevel() const noexcept {
return m_staging_clusterset.has_value(); }
682 int GetSpecifiedLevel(Level level)
const noexcept {
return level == Level::TOP && m_staging_clusterset.has_value(); }
691 std::vector<Cluster*> GetConflicts() const noexcept;
699 return std::make_unique<GenericClusterImpl>(m_next_sequence_counter++);
704 return std::make_unique<SingletonClusterImpl>(m_next_sequence_counter++);
710 if (tx_count >= SingletonClusterImpl::MIN_INTENDED_TX_COUNT && tx_count <= SingletonClusterImpl::MAX_TX_COUNT) {
713 if (tx_count >= GenericClusterImpl::MIN_INTENDED_TX_COUNT && tx_count <= GenericClusterImpl::MAX_TX_COUNT) {
723 void UpdateRef(GraphIndex idx, Ref&
new_location)
noexcept final
725 auto& entry = m_entries[idx];
726 Assume(entry.m_ref !=
nullptr);
731 void UnlinkRef(GraphIndex idx)
noexcept final
733 auto& entry = m_entries[idx];
734 Assume(entry.m_ref !=
nullptr);
735 Assume(m_main_chunkindex_observers == 0 || !entry.m_locator[0].IsPresent());
738 if (entry.m_locator[0].IsPresent()) {
739 entry.m_locator[0].cluster->RemoveChunkData(*
this);
741 entry.m_ref =
nullptr;
746 for (
int level = 0; level <=
GetTopLevel(); ++level) {
747 if (entry.m_locator[level].IsPresent()) {
750 }
else if (entry.m_locator[level].IsRemoved()) {
768 m_unlinked.push_back(idx);
776 void Compact() noexcept;
780 Cluster*
PullIn(Cluster* cluster,
int level) noexcept;
785 void Split(Cluster& cluster,
int level) noexcept;
791 void Merge(
std::span<Cluster*>
to_merge,
int level) noexcept;
801 void AddTransaction(Ref& arg, const
FeePerWeight& feerate) noexcept final;
802 void RemoveTransaction(const Ref& arg) noexcept final;
803 void AddDependency(const Ref& parent, const Ref&
child) noexcept final;
804 void SetTransactionFee(const Ref&,
int64_t fee) noexcept final;
808 void StartStaging() noexcept final;
809 void CommitStaging() noexcept final;
810 void AbortStaging() noexcept final;
811 bool HaveStaging() const noexcept final {
return m_staging_clusterset.has_value(); }
813 bool Exists(
const Ref& arg, Level level)
noexcept final;
814 FeePerWeight GetMainChunkFeerate(
const Ref& arg)
noexcept final;
815 FeePerWeight GetIndividualFeerate(
const Ref& arg)
noexcept final;
816 std::vector<Ref*> GetCluster(
const Ref& arg, Level level)
noexcept final;
817 std::vector<Ref*> GetAncestors(
const Ref& arg, Level level)
noexcept final;
818 std::vector<Ref*> GetDescendants(
const Ref& arg, Level level)
noexcept final;
819 std::vector<Ref*> GetAncestorsUnion(std::span<const Ref* const>
args, Level level)
noexcept final;
820 std::vector<Ref*> GetDescendantsUnion(std::span<const Ref* const>
args, Level level)
noexcept final;
821 GraphIndex GetTransactionCount(Level level)
noexcept final;
822 bool IsOversized(Level level)
noexcept final;
823 std::strong_ordering CompareMainOrder(
const Ref&
a,
const Ref& b)
noexcept final;
824 GraphIndex CountDistinctClusters(std::span<const Ref* const>
refs, Level level)
noexcept final;
825 std::pair<std::vector<FeeFrac>, std::vector<FeeFrac>> GetMainStagingDiagrams() noexcept final;
828 std::
unique_ptr<BlockBuilder> GetBlockBuilder() noexcept final;
831 size_t GetMainMemoryUsage() noexcept final;
836TxGraphImpl::ClusterSet& TxGraphImpl::
GetClusterSet(
int level) noexcept
838 if (level == 0)
return m_main_clusterset;
840 Assume(m_staging_clusterset.has_value());
841 return *m_staging_clusterset;
844const TxGraphImpl::ClusterSet& TxGraphImpl::GetClusterSet(
int level)
const noexcept
846 if (level == 0)
return m_main_clusterset;
848 Assume(m_staging_clusterset.has_value());
849 return *m_staging_clusterset;
857 TxGraphImpl*
const m_graph;
859 std::unordered_set<uint64_t> m_excluded_clusters;
861 TxGraphImpl::ChunkIndex::const_iterator m_cur_iter;
864 Cluster* m_cur_cluster;
866 bool m_known_end_of_cluster;
869 void Next() noexcept;
873 BlockBuilderImpl(TxGraphImpl& graph) noexcept;
876 ~BlockBuilderImpl() final;
878 void Include() noexcept final;
879 void Skip() noexcept final;
884 if (entry.m_main_chunkindex_iterator != m_main_chunkindex.end()) {
885 Assume(m_main_chunkindex_observers == 0);
888 m_main_chunkindex_discarded.emplace_back(m_main_chunkindex.extract(entry.m_main_chunkindex_iterator));
889 entry.m_main_chunkindex_iterator = m_main_chunkindex.end();
895 auto& entry = m_entries[idx];
898 Assume(entry.m_ref !=
nullptr);
899 if (!m_main_chunkindex_discarded.empty()) {
901 auto&
node = m_main_chunkindex_discarded.back().value();
902 node.m_graph_index = idx;
904 auto insert_result = m_main_chunkindex.insert(std::move(m_main_chunkindex_discarded.back()));
907 m_main_chunkindex_discarded.pop_back();
916size_t GenericClusterImpl::TotalMemoryUsage() const noexcept
925 sizeof(std::unique_ptr<Cluster>);
928size_t SingletonClusterImpl::TotalMemoryUsage() const noexcept
933 sizeof(std::unique_ptr<Cluster>);
936uint64_t GenericClusterImpl::GetTotalTxSize() const noexcept
939 for (
auto i : m_linearization) {
950 m_linearization.push_back(
ret);
962void GenericClusterImpl::AddDependencies(SetType parents,
DepGraphIndex child)
noexcept
967void SingletonClusterImpl::AddDependencies(SetType parents,
DepGraphIndex child)
noexcept
971 Assume(parents == SetType{} || parents == SetType::Fill(0));
976 for (
auto pos : m_linearization) {
979 for (
auto pos : m_linearization) {
984 m_linearization.clear();
993 m_graph_index = NO_GRAPH_INDEX;
997int GenericClusterImpl::GetLevel(
const TxGraphImpl& graph)
const noexcept
1000 if (!
Assume(!m_linearization.empty()))
return -1;
1003 const auto& entry = graph.m_entries[m_mapping[m_linearization.front()]];
1005 for (
int level = 0; level <
MAX_LEVELS; ++level) {
1006 if (entry.m_locator[level].cluster ==
this)
return level;
1014int SingletonClusterImpl::GetLevel(
const TxGraphImpl& graph)
const noexcept
1017 if (!
Assume(GetTxCount()))
return -1;
1020 const auto& entry = graph.m_entries[m_graph_index];
1022 for (
int level = 0; level <
MAX_LEVELS; ++level) {
1023 if (entry.m_locator[level].cluster ==
this)
return level;
1031void TxGraphImpl::ClearLocator(
int level, GraphIndex idx,
bool oversized_tx)
noexcept
1033 auto& entry = m_entries[idx];
1035 Assume(entry.m_locator[level].IsPresent());
1037 if (level == 0 || !entry.m_locator[level - 1].IsPresent()) {
1038 entry.m_locator[level].SetMissing();
1040 entry.m_locator[level].SetRemoved();
1048 if (entry.m_locator[1].IsRemoved()) {
1049 entry.m_locator[1].SetMissing();
1050 }
else if (!entry.m_locator[1].IsPresent()) {
1051 --m_staging_clusterset->m_txcount;
1052 m_staging_clusterset->m_txcount_oversized -=
oversized_tx;
1058void GenericClusterImpl::RemoveChunkData(TxGraphImpl& graph)
noexcept
1061 auto& entry = graph.m_entries[m_mapping[idx]];
1062 graph.ClearChunkData(entry);
1066void SingletonClusterImpl::RemoveChunkData(TxGraphImpl& graph)
noexcept
1068 if (GetTxCount() == 0)
return;
1069 auto& entry = graph.m_entries[m_graph_index];
1070 graph.ClearChunkData(entry);
1073void GenericClusterImpl::Updated(TxGraphImpl& graph,
int level,
bool rename)
noexcept
1077 auto& entry = graph.m_entries[m_mapping[idx]];
1080 if (level == 0 && !
rename) graph.ClearChunkData(entry);
1081 entry.m_locator[level].SetPresent(
this, idx);
1098 for (
unsigned chunk_idx = 0; chunk_idx <
chunking.size(); ++chunk_idx) {
1112 auto it =
chunk.transactions.begin();
1115 while (it !=
chunk.transactions.end()) {
1126 auto& entry = graph.m_entries[
graph_idx];
1127 entry.m_main_lin_index =
lin_idx++;
1132 chunk.transactions.Reset(idx);
1133 if (
chunk.transactions.None()) {
1149void SingletonClusterImpl::Updated(TxGraphImpl& graph,
int level,
bool rename)
noexcept
1152 if (GetTxCount() == 0)
return;
1154 auto& entry = graph.m_entries[m_graph_index];
1157 if (level == 0 && !
rename) graph.ClearChunkData(entry);
1158 entry.m_locator[level].SetPresent(
this, 0);
1162 entry.m_main_lin_index = 0;
1163 entry.m_main_chunk_feerate = m_feerate;
1164 entry.m_main_equal_feerate_chunk_prefix_size = m_feerate.
size;
1165 entry.m_main_max_chunk_fallback = m_graph_index;
1172void GenericClusterImpl::GetConflicts(
const TxGraphImpl& graph, std::vector<Cluster*>& out)
const noexcept
1174 for (
auto i : m_linearization) {
1175 auto& entry = graph.m_entries[m_mapping[i]];
1178 if (entry.m_locator[0].IsPresent()) {
1179 out.push_back(entry.m_locator[0].cluster);
1184void SingletonClusterImpl::GetConflicts(
const TxGraphImpl& graph, std::vector<Cluster*>& out)
const noexcept
1187 if (GetTxCount() == 0)
return;
1189 auto& entry = graph.m_entries[m_graph_index];
1192 if (entry.m_locator[0].IsPresent()) {
1193 out.push_back(entry.m_locator[0].cluster);
1197std::vector<Cluster*> TxGraphImpl::GetConflicts() const noexcept
1201 std::vector<Cluster*>
ret;
1204 auto& entry = m_entries[i];
1205 if (entry.m_locator[0].IsPresent()) {
1206 ret.push_back(entry.m_locator[0].cluster);
1212 for (
const auto& cluster :
clusters) {
1213 cluster->GetConflicts(*
this,
ret);
1217 std::ranges::sort(
ret, [](Cluster*
a, Cluster* b)
noexcept {
return CompareClusters(
a, b) < 0; });
1218 ret.erase(std::ranges::unique(
ret).begin(),
ret.end());
1222Cluster* GenericClusterImpl::CopyToStaging(TxGraphImpl& graph)
const noexcept
1225 auto ret = graph.CreateEmptyGenericCluster();
1226 auto ptr =
ret.get();
1228 ptr->m_depgraph = m_depgraph;
1229 ptr->m_mapping = m_mapping;
1230 ptr->m_linearization = m_linearization;
1232 graph.InsertCluster(1, std::move(
ret), m_quality);
1234 ptr->Updated(graph, 1,
false);
1236 graph.GetClusterSet(1).m_cluster_usage += ptr->TotalMemoryUsage();
1240Cluster* SingletonClusterImpl::CopyToStaging(TxGraphImpl& graph)
const noexcept
1243 auto ret = graph.CreateEmptySingletonCluster();
1244 auto ptr =
ret.get();
1246 ptr->m_graph_index = m_graph_index;
1247 ptr->m_feerate = m_feerate;
1249 graph.InsertCluster(1, std::move(
ret), m_quality);
1251 ptr->Updated(graph, 1,
false);
1253 graph.GetClusterSet(1).m_cluster_usage += ptr->TotalMemoryUsage();
1257void GenericClusterImpl::ApplyRemovals(TxGraphImpl& graph,
int level, std::span<GraphIndex>&
to_remove)
noexcept
1265 Assume(idx < graph.m_entries.size());
1266 auto& entry = graph.m_entries[idx];
1267 auto& locator = entry.m_locator[level];
1269 if (locator.cluster !=
this)
break;
1271 todo.Set(locator.index);
1275 m_mapping[locator.index] = GraphIndex(-1);
1281 graph.ClearLocator(level, idx, m_quality == QualityLevel::OVERSIZED_SINGLETON);
1292 std::erase_if(m_linearization, [&](
auto pos) {
return todo[pos]; });
1297 graph.SetClusterQuality(level, m_quality, m_setindex,
new_quality);
1301void SingletonClusterImpl::ApplyRemovals(TxGraphImpl& graph,
int level, std::span<GraphIndex>&
to_remove)
noexcept
1313 graph.ClearLocator(level, m_graph_index, m_quality == QualityLevel::OVERSIZED_SINGLETON);
1314 m_graph_index = NO_GRAPH_INDEX;
1315 graph.SetClusterQuality(level, m_quality, m_setindex, QualityLevel::NEEDS_SPLIT);
1320void GenericClusterImpl::Clear(TxGraphImpl& graph,
int level)
noexcept
1324 for (
auto i : m_linearization) {
1325 graph.ClearLocator(level, m_mapping[i], m_quality == QualityLevel::OVERSIZED_SINGLETON);
1328 m_linearization.clear();
1332void SingletonClusterImpl::Clear(TxGraphImpl& graph,
int level)
noexcept
1336 graph.ClearLocator(level, m_graph_index, m_quality == QualityLevel::OVERSIZED_SINGLETON);
1337 m_graph_index = NO_GRAPH_INDEX;
1340void GenericClusterImpl::MoveToMain(TxGraphImpl& graph)
noexcept
1342 for (
auto i : m_linearization) {
1343 GraphIndex idx = m_mapping[i];
1344 auto& entry = graph.m_entries[idx];
1345 entry.m_locator[1].SetMissing();
1352 auto cluster = graph.ExtractCluster(1,
quality, m_setindex);
1353 graph.InsertCluster(0, std::move(cluster),
quality);
1357void SingletonClusterImpl::MoveToMain(TxGraphImpl& graph)
noexcept
1360 auto& entry = graph.m_entries[m_graph_index];
1361 entry.m_locator[1].SetMissing();
1365 auto cluster = graph.ExtractCluster(1,
quality, m_setindex);
1366 graph.InsertCluster(0, std::move(cluster),
quality);
1371void GenericClusterImpl::Compact() noexcept
1373 m_linearization.shrink_to_fit();
1374 m_mapping.shrink_to_fit();
1378void SingletonClusterImpl::Compact() noexcept
1383void GenericClusterImpl::AppendChunkFeerates(std::vector<FeeFrac>&
ret)
const noexcept
1390void SingletonClusterImpl::AppendChunkFeerates(std::vector<FeeFrac>&
ret)
const noexcept
1393 ret.push_back(m_feerate);
1397uint64_t GenericClusterImpl::AppendTrimData(std::vector<TrimTxData>&
ret, std::vector<std::pair<GraphIndex, GraphIndex>>&
deps)
const noexcept
1413 auto& entry =
ret.emplace_back();
1421 size += entry.m_tx_size;
1428uint64_t SingletonClusterImpl::AppendTrimData(std::vector<TrimTxData>&
ret, std::vector<std::pair<GraphIndex, GraphIndex>>&
deps)
const noexcept
1430 if (!GetTxCount())
return 0;
1431 auto& entry =
ret.emplace_back();
1432 entry.m_chunk_feerate = m_feerate;
1433 entry.m_index = m_graph_index;
1434 entry.m_tx_size = m_feerate.
size;
1435 return m_feerate.
size;
1438bool GenericClusterImpl::Split(TxGraphImpl& graph,
int level)
noexcept
1452 while (
todo.Any()) {
1463 graph.SetClusterQuality(level, m_quality, m_setindex,
split_quality);
1484 for (
auto i : m_linearization) {
1491 for (
auto i : m_linearization) {
1503 graph.GetClusterSet(level).m_cluster_usage +=
new_cluster->TotalMemoryUsage();
1508 m_linearization.clear();
1512bool SingletonClusterImpl::Split(TxGraphImpl& graph,
int level)
noexcept
1520void GenericClusterImpl::Merge(TxGraphImpl& graph,
int level, Cluster& other)
noexcept
1523 std::vector<DepGraphIndex>
remap(other.GetDepGraphIndexRange());
1532 m_mapping.push_back(idx);
1533 m_linearization.push_back(
new_pos);
1544 auto& entry = graph.m_entries[idx];
1547 if (level == 0) graph.ClearChunkData(entry);
1548 entry.m_locator[level].SetPresent(
this,
remap[pos]);
1552void SingletonClusterImpl::Merge(TxGraphImpl&,
int, Cluster&)
noexcept
1558void GenericClusterImpl::ApplyDependencies(TxGraphImpl& graph,
int level, std::span<std::pair<GraphIndex, GraphIndex>>
to_apply)
noexcept
1561 std::ranges::sort(
to_apply, [](
auto&
a,
auto& b) {
return a.second < b.second; });
1565 auto&
first_child = graph.m_entries[it->second].m_locator[level];
1571 auto&
child = graph.m_entries[it->second].m_locator[level];
1572 auto& parent = graph.m_entries[it->first].m_locator[level];
1573 Assume(
child.cluster ==
this && parent.cluster ==
this);
1575 parents.Set(parent.index);
1588 graph.SetClusterQuality(level, m_quality, m_setindex, QualityLevel::NEEDS_FIX);
1594void SingletonClusterImpl::ApplyDependencies(TxGraphImpl&,
int, std::span<std::pair<GraphIndex, GraphIndex>>) noexcept
1600TxGraphImpl::~TxGraphImpl() noexcept
1604 for (
auto& entry : m_entries) {
1605 if (entry.m_ref !=
nullptr) {
1606 GetRefGraph(*entry.m_ref) =
nullptr;
1621 ret->m_quality = QualityLevel::NONE;
1637ClusterSetIndex TxGraphImpl::InsertCluster(
int level, std::unique_ptr<Cluster>&& cluster, QualityLevel
quality)
noexcept
1642 Assume(cluster->m_quality == QualityLevel::NONE);
1649 cluster->m_setindex =
ret;
1666void TxGraphImpl::DeleteCluster(Cluster& cluster,
int level)
noexcept
1674std::pair<Cluster*, int> TxGraphImpl::FindClusterAndLevel(GraphIndex idx,
int level)
const noexcept
1677 auto& entry = m_entries[idx];
1679 for (
int l = level;
l >= 0; --
l) {
1682 if (entry.m_locator[
l].IsMissing())
continue;
1684 if (entry.m_locator[
l].IsRemoved())
break;
1686 return {entry.m_locator[
l].cluster,
l};
1689 return {
nullptr, -1};
1692Cluster* TxGraphImpl::PullIn(Cluster* cluster,
int level)
noexcept
1702 cluster = cluster->CopyToStaging(*
this);
1707void TxGraphImpl::ApplyRemovals(
int up_to_level)
noexcept
1710 for (
int level = 0; level <=
up_to_level; ++level) {
1723 std::ranges::sort(
to_remove, [&](GraphIndex
a, GraphIndex b)
noexcept {
1724 Cluster*
cluster_a = m_entries[
a].m_locator[level].cluster;
1725 Cluster*
cluster_b = m_entries[b].m_locator[level].cluster;
1731 Cluster* cluster = m_entries[
to_remove_span.front()].m_locator[level].cluster;
1732 if (cluster !=
nullptr) {
1747void TxGraphImpl::SwapIndexes(GraphIndex
a, GraphIndex b, std::vector<Cluster*>&
affected_main)
noexcept
1750 Assume(b < m_entries.size());
1752 std::swap(m_entries[
a], m_entries[b]);
1754 for (
int i = 0; i < 2; ++i) {
1755 GraphIndex idx = i ? b :
a;
1756 Entry& entry = m_entries[idx];
1758 if (entry.m_ref) GetRefIndex(*entry.m_ref) = idx;
1760 if (entry.m_main_chunkindex_iterator != m_main_chunkindex.end()) {
1761 entry.m_main_chunkindex_iterator->m_graph_index = idx;
1764 for (
int level = 0; level <
MAX_LEVELS; ++level) {
1765 Locator& locator = entry.m_locator[level];
1766 if (locator.IsPresent()) {
1767 locator.cluster->UpdateMapping(locator.index, idx);
1774void TxGraphImpl::Compact() noexcept
1778 if (!m_main_clusterset.m_deps_to_add.empty())
return;
1779 if (!m_main_clusterset.m_to_remove.empty())
return;
1780 Assume(m_main_clusterset.m_removed.empty());
1781 if (m_staging_clusterset.has_value()) {
1782 if (!m_staging_clusterset->m_deps_to_add.empty())
return;
1783 if (!m_staging_clusterset->m_to_remove.empty())
return;
1784 if (!m_staging_clusterset->m_removed.empty())
return;
1794 std::ranges::sort(m_unlinked, std::greater{});
1797 auto last = GraphIndex(-1);
1798 for (GraphIndex idx : m_unlinked) {
1805 Entry& entry = m_entries[idx];
1806 Assume(entry.m_ref ==
nullptr);
1808 for (
int level = 0; level <
MAX_LEVELS; ++level) {
1809 Assume(!entry.m_locator[level].IsPresent());
1815 m_entries.pop_back();
1823 cluster->Updated(*
this, 0,
true);
1828void TxGraphImpl::Split(Cluster& cluster,
int level)
noexcept
1833 bool del = cluster.Split(*
this, level);
1840void TxGraphImpl::SplitAll(
int up_to_level)
noexcept
1845 for (
int level = 0; level <=
up_to_level; ++level) {
1846 for (
auto quality : {QualityLevel::NEEDS_SPLIT_FIX, QualityLevel::NEEDS_SPLIT}) {
1848 while (!
queue.empty()) {
1855void TxGraphImpl::GroupClusters(
int level)
noexcept
1859 if (
clusterset.m_group_data.has_value())
return;
1869 std::vector<std::pair<Cluster*, uint64_t>>
an_clusters;
1874 std::vector<std::pair<std::pair<GraphIndex, GraphIndex>,
uint64_t>>
an_deps;
1880 auto graph_idx = cluster->GetClusterEntry(0);
1904 std::ranges::sort(
an_clusters, [](
auto&
a,
auto& b)
noexcept {
return a.second < b.second; });
1907 std::ranges::sort(
an_deps, [&](
auto&
a,
auto& b)
noexcept {
return a.second < b.second; });
1932 [](
auto&
a,
uint64_t seq)
noexcept { return a.sequence < seq; });
1978 for (
const auto& [dep,
_] :
an_deps) {
2020 std::ranges::sort(
an_deps, [](
auto&
a,
auto& b)
noexcept {
return a.second < b.second; });
2021 std::ranges::sort(
an_clusters, [](
auto&
a,
auto& b)
noexcept {
return a.second < b.second; });
2067void TxGraphImpl::Merge(std::span<Cluster*>
to_merge,
int level)
noexcept
2080 for (
size_t i = 1; i <
to_merge.size(); ++i) {
2084 if (size > max_size) {
2113void TxGraphImpl::ApplyDependencies(
int level)
noexcept
2122 if (
clusterset.m_deps_to_add.empty())
return;
2133 cluster =
PullIn(cluster, cluster->GetLevel(*
this));
2143 const auto&
loc = m_entries[
deps_span[0].second].m_locator[level];
2145 loc.cluster->ApplyDependencies(*
this, level,
deps_span);
2156std::pair<uint64_t, bool> GenericClusterImpl::Relinearize(TxGraphImpl& graph,
int level,
uint64_t max_cost)
noexcept
2165 const auto ref_a = graph.m_entries[m_mapping[
a]].m_ref;
2166 const auto ref_b = graph.m_entries[m_mapping[b]].m_ref;
2167 return graph.m_fallback_order(*
ref_a, *
ref_b);
2184 graph.SetClusterQuality(level, m_quality, m_setindex, QualityLevel::OPTIMAL);
2187 graph.SetClusterQuality(level, m_quality, m_setindex, QualityLevel::ACCEPTABLE);
2190 graph.SetClusterQuality(level, m_quality, m_setindex, QualityLevel::NEEDS_RELINEARIZE);
2198std::pair<uint64_t, bool> SingletonClusterImpl::Relinearize(TxGraphImpl& graph,
int level,
uint64_t max_cost)
noexcept
2206void TxGraphImpl::MakeAcceptable(Cluster& cluster,
int level)
noexcept
2209 if (!cluster.NeedsSplitting() && !cluster.IsAcceptable() && !cluster.IsOversized()) {
2210 cluster.Relinearize(*
this, level, m_acceptable_cost);
2214void TxGraphImpl::MakeAllAcceptable(
int level)
noexcept
2219 for (
auto quality : {QualityLevel::NEEDS_FIX, QualityLevel::NEEDS_RELINEARIZE}) {
2221 while (!
queue.empty()) {
2229void TxGraphImpl::AddTransaction(Ref& arg,
const FeePerWeight& feerate)
noexcept
2233 Assume(GetRefGraph(arg) ==
nullptr);
2235 auto idx = m_entries.size();
2236 m_entries.emplace_back();
2237 auto& entry = m_entries.back();
2238 entry.m_main_chunkindex_iterator = m_main_chunkindex.end();
2240 GetRefGraph(arg) =
this;
2241 GetRefIndex(arg) = idx;
2243 bool oversized =
uint64_t(feerate.
size) > m_max_cluster_size;
2245 cluster->AppendTransaction(idx, feerate);
2249 InsertCluster(level, std::move(cluster), oversized ? QualityLevel::OVERSIZED_SINGLETON : QualityLevel::OPTIMAL);
2261void TxGraphImpl::RemoveTransaction(
const Ref& arg)
noexcept
2265 if (GetRefGraph(arg) ==
nullptr)
return;
2266 Assume(GetRefGraph(arg) ==
this);
2270 auto cluster =
FindCluster(GetRefIndex(arg), level);
2271 if (cluster ==
nullptr)
return;
2274 clusterset.m_to_remove.push_back(GetRefIndex(arg));
2280void TxGraphImpl::AddDependency(
const Ref& parent,
const Ref&
child)
noexcept
2284 if (GetRefGraph(parent) ==
nullptr || GetRefGraph(
child) ==
nullptr)
return;
2285 Assume(GetRefGraph(parent) ==
this && GetRefGraph(
child) ==
this);
2288 if (GetRefIndex(parent) == GetRefIndex(
child))
return;
2298 clusterset.m_deps_to_add.emplace_back(GetRefIndex(parent), GetRefIndex(
child));
2304bool TxGraphImpl::Exists(
const Ref& arg, Level
level_select)
noexcept
2306 if (GetRefGraph(arg) ==
nullptr)
return false;
2307 Assume(GetRefGraph(arg) ==
this);
2311 auto cluster =
FindCluster(GetRefIndex(arg), level);
2312 return cluster !=
nullptr;
2315void GenericClusterImpl::GetAncestorRefs(
const TxGraphImpl& graph, std::span<std::pair<Cluster*, DepGraphIndex>>&
args, std::vector<TxGraph::Ref*>& output)
noexcept
2320 while (!
args.empty()) {
2321 if (
args.front().first !=
this)
break;
2328 const auto& entry = graph.m_entries[m_mapping[idx]];
2329 Assume(entry.m_ref !=
nullptr);
2330 output.push_back(entry.m_ref);
2334void SingletonClusterImpl::GetAncestorRefs(
const TxGraphImpl& graph, std::span<std::pair<Cluster*, DepGraphIndex>>&
args, std::vector<TxGraph::Ref*>& output)
noexcept
2337 while (!
args.empty()) {
2338 if (
args.front().first !=
this)
break;
2341 const auto& entry = graph.m_entries[m_graph_index];
2342 Assume(entry.m_ref !=
nullptr);
2343 output.push_back(entry.m_ref);
2346void GenericClusterImpl::GetDescendantRefs(
const TxGraphImpl& graph, std::span<std::pair<Cluster*, DepGraphIndex>>&
args, std::vector<TxGraph::Ref*>& output)
noexcept
2351 while (!
args.empty()) {
2352 if (
args.front().first !=
this)
break;
2359 const auto& entry = graph.m_entries[m_mapping[idx]];
2360 Assume(entry.m_ref !=
nullptr);
2361 output.push_back(entry.m_ref);
2365void SingletonClusterImpl::GetDescendantRefs(
const TxGraphImpl& graph, std::span<std::pair<Cluster*, DepGraphIndex>>&
args, std::vector<TxGraph::Ref*>& output)
noexcept
2375 for (
auto& ref : range) {
2377 const auto& entry = graph.m_entries[m_mapping[m_linearization[
start_pos++]]];
2378 Assume(entry.m_ref !=
nullptr);
2382 return start_pos == m_linearization.size();
2385bool SingletonClusterImpl::GetClusterRefs(TxGraphImpl& graph, std::span<TxGraph::Ref*> range,
LinearizationIndex start_pos)
noexcept
2390 const auto& entry = graph.m_entries[m_graph_index];
2391 Assume(entry.m_ref !=
nullptr);
2392 range[0] = entry.m_ref;
2408void GenericClusterImpl::MakeStagingTransactionsMissing(TxGraphImpl& graph)
noexcept
2412 for (
auto ci : m_linearization) {
2413 GraphIndex idx = m_mapping[
ci];
2414 auto& entry = graph.m_entries[idx];
2415 entry.m_locator[1].SetMissing();
2419void SingletonClusterImpl::MakeStagingTransactionsMissing(TxGraphImpl& graph)
noexcept
2422 auto& entry = graph.m_entries[m_graph_index];
2423 entry.m_locator[1].SetMissing();
2427std::vector<TxGraph::Ref*> TxGraphImpl::GetAncestors(
const Ref& arg, Level
level_select)
noexcept
2430 if (GetRefGraph(arg) ==
nullptr)
return {};
2431 Assume(GetRefGraph(arg) ==
this);
2439 if (cluster ==
nullptr)
return {};
2441 std::pair<Cluster*, DepGraphIndex>
match = {cluster, m_entries[GetRefIndex(arg)].m_locator[
cluster_level].index};
2443 std::vector<TxGraph::Ref*>
ret;
2444 cluster->GetAncestorRefs(*
this,
matches,
ret);
2448std::vector<TxGraph::Ref*> TxGraphImpl::GetDescendants(
const Ref& arg, Level
level_select)
noexcept
2451 if (GetRefGraph(arg) ==
nullptr)
return {};
2452 Assume(GetRefGraph(arg) ==
this);
2460 if (cluster ==
nullptr)
return {};
2462 std::pair<Cluster*, DepGraphIndex>
match = {cluster, m_entries[GetRefIndex(arg)].m_locator[
cluster_level].index};
2464 std::vector<TxGraph::Ref*>
ret;
2465 cluster->GetDescendantRefs(*
this,
matches,
ret);
2469std::vector<TxGraph::Ref*> TxGraphImpl::GetAncestorsUnion(std::span<const Ref* const>
args, Level
level_select)
noexcept
2478 std::vector<std::pair<Cluster*, DepGraphIndex>>
matches;
2480 for (
auto arg :
args) {
2483 if (GetRefGraph(*arg) ==
nullptr)
continue;
2484 Assume(GetRefGraph(*arg) ==
this);
2487 if (cluster ==
nullptr)
continue;
2495 std::vector<TxGraph::Ref*>
ret;
2502std::vector<TxGraph::Ref*> TxGraphImpl::GetDescendantsUnion(std::span<const Ref* const>
args, Level
level_select)
noexcept
2511 std::vector<std::pair<Cluster*, DepGraphIndex>>
matches;
2513 for (
auto arg :
args) {
2516 if (GetRefGraph(*arg) ==
nullptr)
continue;
2517 Assume(GetRefGraph(*arg) ==
this);
2520 if (cluster ==
nullptr)
continue;
2528 std::vector<TxGraph::Ref*>
ret;
2535std::vector<TxGraph::Ref*> TxGraphImpl::GetCluster(
const Ref& arg, Level
level_select)
noexcept
2539 if (GetRefGraph(arg) ==
nullptr)
return {};
2540 Assume(GetRefGraph(arg) ==
this);
2548 if (cluster ==
nullptr)
return {};
2551 std::vector<TxGraph::Ref*>
ret(cluster->GetTxCount());
2552 cluster->GetClusterRefs(*
this,
ret, 0);
2563FeePerWeight TxGraphImpl::GetIndividualFeerate(
const Ref& arg)
noexcept
2566 if (GetRefGraph(arg) ==
nullptr)
return {};
2567 Assume(GetRefGraph(arg) ==
this);
2570 Cluster* cluster{
nullptr};
2572 for (level = 0; level <=
GetTopLevel(); ++level) {
2576 if (m_entries[GetRefIndex(arg)].m_locator[level].
IsPresent()) {
2577 cluster = m_entries[GetRefIndex(arg)].m_locator[level].cluster;
2581 if (cluster ==
nullptr)
return {};
2583 return cluster->GetIndividualFeerate(m_entries[GetRefIndex(arg)].m_locator[level].index);
2586FeePerWeight TxGraphImpl::GetMainChunkFeerate(
const Ref& arg)
noexcept
2589 if (GetRefGraph(arg) ==
nullptr)
return {};
2590 Assume(GetRefGraph(arg) ==
this);
2594 Assume(m_main_clusterset.m_deps_to_add.empty());
2597 if (cluster ==
nullptr)
return {};
2601 const auto& entry = m_entries[GetRefIndex(arg)];
2602 return entry.m_main_chunk_feerate;
2605bool TxGraphImpl::IsOversized(Level
level_select)
noexcept
2625void TxGraphImpl::StartStaging() noexcept
2628 Assume(!m_staging_clusterset.has_value());
2637 m_staging_clusterset.emplace();
2640 m_staging_clusterset->m_txcount = m_main_clusterset.m_txcount;
2641 m_staging_clusterset->m_txcount_oversized = m_main_clusterset.m_txcount_oversized;
2642 m_staging_clusterset->m_deps_to_add = m_main_clusterset.m_deps_to_add;
2643 m_staging_clusterset->m_group_data = m_main_clusterset.m_group_data;
2644 m_staging_clusterset->m_oversized = m_main_clusterset.m_oversized;
2645 Assume(m_staging_clusterset->m_oversized.has_value());
2646 m_staging_clusterset->m_cluster_usage = 0;
2649void TxGraphImpl::AbortStaging() noexcept
2652 Assume(m_staging_clusterset.has_value());
2655 for (
auto idx : m_staging_clusterset->m_removed) {
2656 m_entries[idx].m_locator[1].SetMissing();
2660 for (
auto& cluster : m_staging_clusterset->m_clusters[
quality]) {
2661 cluster->MakeStagingTransactionsMissing(*
this);
2665 m_staging_clusterset.reset();
2667 if (!m_main_clusterset.m_group_data.has_value()) {
2670 if (m_main_clusterset.m_to_remove.empty() && m_main_clusterset.m_txcount_oversized > 0) {
2673 m_main_clusterset.m_oversized =
true;
2675 m_main_clusterset.m_oversized = std::nullopt;
2680void TxGraphImpl::CommitStaging() noexcept
2683 Assume(m_staging_clusterset.has_value());
2684 Assume(m_main_chunkindex_observers == 0);
2698 for (
auto idx : m_staging_clusterset->m_removed) {
2699 m_entries[idx].m_locator[1].SetMissing();
2709 m_main_clusterset.m_deps_to_add = std::move(m_staging_clusterset->m_deps_to_add);
2710 m_main_clusterset.m_to_remove = std::move(m_staging_clusterset->m_to_remove);
2711 m_main_clusterset.m_group_data = std::move(m_staging_clusterset->m_group_data);
2712 m_main_clusterset.m_oversized = m_staging_clusterset->m_oversized;
2713 m_main_clusterset.m_txcount = m_staging_clusterset->m_txcount;
2714 m_main_clusterset.m_txcount_oversized = m_staging_clusterset->m_txcount_oversized;
2716 m_staging_clusterset.reset();
2729 if (m_quality == QualityLevel::OVERSIZED_SINGLETON) {
2732 graph.SetClusterQuality(level, m_quality, m_setindex, QualityLevel::NEEDS_RELINEARIZE);
2745void TxGraphImpl::SetTransactionFee(
const Ref& ref,
int64_t fee)
noexcept
2748 if (GetRefGraph(ref) ==
nullptr)
return;
2749 Assume(GetRefGraph(ref) ==
this);
2750 Assume(m_main_chunkindex_observers == 0);
2752 auto& entry = m_entries[GetRefIndex(ref)];
2753 for (
int level = 0; level <
MAX_LEVELS; ++level) {
2754 auto& locator = entry.m_locator[level];
2755 if (locator.IsPresent()) {
2756 locator.cluster->SetFee(*
this, level, locator.index,
fee);
2761std::strong_ordering TxGraphImpl::CompareMainOrder(
const Ref&
a,
const Ref& b)
noexcept
2764 Assume(GetRefGraph(
a) ==
this);
2765 Assume(GetRefGraph(b) ==
this);
2768 Assume(m_main_clusterset.m_deps_to_add.empty());
2770 const auto&
entry_a = m_entries[GetRefIndex(
a)];
2771 const auto&
entry_b = m_entries[GetRefIndex(b)];
2791 for (
const Ref* ref :
refs) {
2793 if (GetRefGraph(*ref) ==
nullptr)
continue;
2794 Assume(GetRefGraph(*ref) ==
this);
2795 auto cluster =
FindCluster(GetRefIndex(*ref), level);
2796 if (cluster !=
nullptr)
clusters.push_back(cluster);
2800 Cluster*
last{
nullptr};
2802 for (Cluster* cluster :
clusters) {
2809std::pair<std::vector<FeeFrac>, std::vector<FeeFrac>> TxGraphImpl::GetMainStagingDiagrams() noexcept
2811 Assume(m_staging_clusterset.has_value());
2813 Assume(m_main_clusterset.m_deps_to_add.empty());
2815 Assume(m_staging_clusterset->m_deps_to_add.empty());
2825 for (
const auto& cluster : m_staging_clusterset->m_clusters[
quality]) {
2835void GenericClusterImpl::SanityCheck(
const TxGraphImpl& graph,
int level)
const
2855 if (m_linearization.empty())
return;
2857 for (
auto lin_pos : m_linearization) {
2859 const auto& entry = graph.m_entries[m_mapping[
lin_pos]];
2866 assert(entry.m_locator[level].cluster ==
this);
2888 if (graph.m_main_clusterset.m_to_remove.empty()) {
2890 assert((entry.m_main_chunkindex_iterator != graph.m_main_chunkindex.end()) ==
is_chunk_end);
2892 auto&
chunk_data = *entry.m_main_chunkindex_iterator;
2908void SingletonClusterImpl::SanityCheck(
const TxGraphImpl& graph,
int level)
const
2913 const auto& entry = graph.m_entries[m_graph_index];
2915 assert(entry.m_locator[level].cluster ==
this);
2916 assert(entry.m_locator[level].index == 0);
2919 assert(entry.m_main_lin_index == 0);
2920 assert(entry.m_main_chunk_feerate == m_feerate);
2921 assert(entry.m_main_equal_feerate_chunk_prefix_size == m_feerate.
size);
2922 if (graph.m_main_clusterset.m_to_remove.empty()) {
2923 assert(entry.m_main_chunkindex_iterator != graph.m_main_chunkindex.end());
2924 auto&
chunk_data = *entry.m_main_chunkindex_iterator;
2931void TxGraphImpl::SanityCheck()
const
2949 for (GraphIndex idx = 0; idx < m_entries.size(); ++idx) {
2950 const auto& entry = m_entries[idx];
2951 if (entry.m_ref ==
nullptr) {
2956 assert(GetRefGraph(*entry.m_ref) ==
this);
2957 assert(GetRefIndex(*entry.m_ref) == idx);
2959 if (entry.m_main_chunkindex_iterator != m_main_chunkindex.end()) {
2961 assert(entry.m_locator[0].IsPresent());
2966 for (
int level = 0; level <
MAX_LEVELS; ++level) {
2967 const auto& locator = entry.m_locator[level];
2969 assert(locator.IsMissing() + locator.IsRemoved() + locator.IsPresent() == 1);
2970 if (locator.IsPresent()) {
2974 assert(locator.cluster->GetClusterEntry(locator.index) == idx);
2978 }
else if (locator.IsRemoved()) {
2991 for (
int level = 0; level <=
GetTopLevel(); ++level) {
3006 assert(cluster.GetTxCount() <= m_max_cluster_count);
3009 assert(cluster.GetTxCount() == 0 || level == cluster.GetLevel(*
this));
3014 assert(cluster.IsOversized() || cluster.GetTotalTxSize() <= m_max_cluster_size);
3016 assert(!cluster.IsOversized() || cluster.GetTxCount() == 1);
3019 assert(cluster.GetTxCount() <= cluster.GetMaxTxCount());
3022 if (!cluster.NeedsSplitting()) {
3023 assert(cluster.GetTxCount() >= cluster.GetMinIntendedTxCount());
3027 assert(cluster.m_sequence < m_next_sequence_counter);
3032 if (cluster.GetTxCount() != 0) {
3036 cluster.SanityCheck(*
this, level);
3049 for (GraphIndex idx :
clusterset.m_to_remove) {
3050 assert(idx < m_entries.size());
3071 for (
const Cluster*
cl :
clusterset.m_group_data->m_group_clusters) {
3101 std::set<GraphIndex>
actual_unlinked(m_unlinked.begin(), m_unlinked.end());
3113 for (
const auto&
chunk : m_main_chunkindex) {
3114 GraphIndex idx =
chunk.m_graph_index;
3130 for (QualityLevel
quality : {QualityLevel::NEEDS_FIX, QualityLevel::NEEDS_RELINEARIZE, QualityLevel::ACCEPTABLE}) {
3132 for (
int level =
GetTopLevel(); level >= 0; --level) {
3134 if (level == 0 && m_main_chunkindex_observers != 0)
continue;
3138 if (
clusterset.m_oversized ==
true)
continue;
3140 while (!
queue.empty()) {
3147 if (
quality == QualityLevel::NEEDS_FIX ||
quality == QualityLevel::NEEDS_RELINEARIZE) {
3171void BlockBuilderImpl::Next() noexcept
3174 if (m_cur_iter == m_graph->m_main_chunkindex.end())
return;
3178 m_cur_cluster =
nullptr;
3179 if (m_cur_iter == m_graph->m_main_chunkindex.end())
break;
3184 m_known_end_of_cluster =
false;
3186 if (!m_excluded_clusters.contains(m_cur_cluster->m_sequence))
break;
3190std::optional<std::pair<std::vector<TxGraph::Ref*>,
FeePerWeight>> BlockBuilderImpl::GetCurrentChunk() noexcept
3192 std::optional<std::pair<std::vector<TxGraph::Ref*>,
FeePerWeight>>
ret;
3194 if (m_cur_iter != m_graph->m_main_chunkindex.end()) {
3201 ret->first.resize(1);
3204 m_known_end_of_cluster =
true;
3209 m_known_end_of_cluster = m_cur_cluster->GetClusterRefs(*m_graph,
ret->first,
start_pos);
3219BlockBuilderImpl::BlockBuilderImpl(TxGraphImpl& graph) noexcept : m_graph(&graph)
3222 m_graph->MakeAllAcceptable(0);
3224 Assume(m_graph->m_main_clusterset.m_deps_to_add.empty());
3227 ++m_graph->m_main_chunkindex_observers;
3229 m_cur_iter = m_graph->m_main_chunkindex.begin();
3230 m_cur_cluster =
nullptr;
3231 if (m_cur_iter != m_graph->m_main_chunkindex.end()) {
3239BlockBuilderImpl::~BlockBuilderImpl()
3241 Assume(m_graph->m_main_chunkindex_observers > 0);
3243 --m_graph->m_main_chunkindex_observers;
3246void BlockBuilderImpl::Include() noexcept
3253void BlockBuilderImpl::Skip() noexcept
3259 if (m_cur_cluster !=
nullptr && !m_known_end_of_cluster) {
3260 m_excluded_clusters.insert(m_cur_cluster->m_sequence);
3265std::unique_ptr<TxGraph::BlockBuilder> TxGraphImpl::GetBlockBuilder() noexcept
3267 return std::make_unique<BlockBuilderImpl>(*
this);
3270std::pair<std::vector<TxGraph::Ref*>,
FeePerWeight> TxGraphImpl::GetWorstMainChunk() noexcept
3275 Assume(m_main_clusterset.m_deps_to_add.empty());
3277 if (!m_main_chunkindex.empty()) {
3278 const auto&
chunk_data = *m_main_chunkindex.rbegin();
3283 ret.first.resize(1);
3290 std::reverse(
ret.first.begin(),
ret.first.end());
3297std::vector<TxGraph::Ref*> TxGraphImpl::Trim() noexcept
3300 Assume(m_main_chunkindex_observers == 0 || level != 0);
3301 std::vector<TxGraph::Ref*>
ret;
3336 std::vector<std::pair<GraphIndex, GraphIndex>>
deps_by_child;
3343 std::vector<std::vector<TrimTxData>::iterator>
trim_heap;
3348 static constexpr auto cmp_fn = [](
auto a,
auto b)
noexcept {
3357 static constexpr auto find_fn = [](TrimTxData* arg)
noexcept {
3358 while (arg != arg->m_uf_parent) {
3361 auto par = arg->m_uf_parent;
3362 arg->m_uf_parent =
par->m_uf_parent;
3370 static constexpr auto union_fn = [](TrimTxData*
arg1, TrimTxData*
arg2)
noexcept {
3383 rep1->m_uf_size +=
rep2->m_uf_size;
3384 rep1->m_uf_count +=
rep2->m_uf_count;
3389 auto locate_fn = [&](GraphIndex index)
noexcept {
3390 auto it = std::lower_bound(
trim_data.begin(),
trim_data.end(), index, [](TrimTxData&
elem, GraphIndex idx)
noexcept {
3391 return elem.m_index < idx;
3413 if (
trim_data.size() <= m_max_cluster_count && size <= m_max_cluster_size)
continue;
3417 std::ranges::sort(
trim_data, [](
auto&
a,
auto& b)
noexcept {
return a.m_index < b.m_index; });
3426 std::ranges::sort(
deps_by_child, [](
auto&
a,
auto& b)
noexcept {
return a.second < b.second; });
3441 if (
trim_it->m_deps_left == 0 &&
trim_it->m_tx_size <= m_max_cluster_size) {
3450 std::ranges::sort(
deps_by_parent, [](
auto&
a,
auto& b)
noexcept {
return a.first < b.first; });
3488 entry.m_uf_parent = &entry;
3489 entry.m_uf_count = 1;
3490 entry.m_uf_size = entry.m_tx_size;
3523 if (--
chl_it->m_deps_left == 0) {
3547size_t TxGraphImpl::GetMainMemoryUsage() noexcept
3554 m_main_clusterset.m_cluster_usage +
3556 sizeof(Entry) * m_main_clusterset.m_txcount +
3568 m_graph->UnlinkRef(m_index);
3576 if (other.m_graph) other.m_graph->UpdateRef(other.m_index, *
this);
3578 std::swap(m_graph, other.m_graph);
3579 std::swap(m_index, other.m_index);
3583 unsigned max_cluster_count,
3588 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, CeilDiv(BITS, size_t{std::numeric_limits< size_t >::digits})> > > BitSet
#define Assume(val)
Assume is the identity function.
Wrapper around FeeFrac & derived types, which adds a feerate-based ordering which treats equal-feerat...
Wrapper around FeeFrac & derived types, which adds a total ordering which first sorts by feerate and ...
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.
static const PrecomputedData data
Precomputed COutPoint and CCoins values.
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 SanityCheck(const DepGraph< SetType > &depgraph)
Perform a sanity/consistency check on a DepGraph.
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(std::span< const char > sp LIFETIMEBOUND, 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.
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,...
constexpr unsigned MAX_CLUSTER_COUNT_LIMIT
constexpr auto Ticks(Dur2 d)
Helper to count the seconds of a duration/time_point.
void ClearShrink(V &v) noexcept
Clear a vector (or std::deque) and release its allocated memory.