19#include <unordered_set>
38enum class QualityLevel
94 TrimTxData* m_uf_parent;
104 friend class TxGraphImpl;
105 friend class BlockBuilderImpl;
111 QualityLevel m_quality{QualityLevel::NONE};
125 Cluster(
const Cluster&) =
delete;
126 Cluster& operator=(
const Cluster&) =
delete;
127 Cluster(Cluster&&) =
delete;
128 Cluster& operator=(Cluster&&) =
delete;
135 return m_quality == QualityLevel::ACCEPTABLE || m_quality == QualityLevel::OPTIMAL;
140 return m_quality != QualityLevel::NEEDS_FIX && m_quality != QualityLevel::NEEDS_SPLIT_FIX;
145 return m_quality == QualityLevel::OPTIMAL;
150 bool IsOversized() const noexcept {
return m_quality == QualityLevel::OVERSIZED_SINGLETON; }
154 return m_quality == QualityLevel::NEEDS_SPLIT || m_quality == QualityLevel::NEEDS_SPLIT_FIX;
195 virtual void GetConflicts(const TxGraphImpl& graph,
std::
vector<Cluster*>& out) const noexcept = 0;
200 virtual void Clear(TxGraphImpl& graph,
int level) noexcept = 0;
204 virtual void Compact() 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;
247 virtual void SanityCheck(const TxGraphImpl& graph,
int level) const = 0;
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;
279 LinearizationIndex GetTxCount() const noexcept final {
return m_linearization.size(); }
280 uint64_t GetTotalTxSize() const noexcept final;
285 int GetLevel(
const TxGraphImpl& graph)
const noexcept final;
287 void Updated(TxGraphImpl& graph,
int level,
bool rename)
noexcept final;
289 Cluster*
CopyToStaging(TxGraphImpl& graph)
const noexcept final;
290 void GetConflicts(
const TxGraphImpl& graph, std::vector<Cluster*>& out)
const noexcept final;
292 void Clear(TxGraphImpl& graph,
int level)
noexcept final;
293 void MoveToMain(TxGraphImpl& graph)
noexcept final;
294 void Compact() noexcept final;
296 [[
nodiscard]]
bool Split(TxGraphImpl& graph,
int level) noexcept final;
297 void Merge(TxGraphImpl& graph,
int level, Cluster& cluster) noexcept final;
307 void SanityCheck(const TxGraphImpl& graph,
int level) const final;
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;
335 LinearizationIndex GetTxCount() const noexcept final {
return m_graph_index != NO_GRAPH_INDEX; }
337 uint64_t GetTotalTxSize() const noexcept final {
return GetTxCount() ? m_feerate.
size : 0; }
342 int GetLevel(
const TxGraphImpl& graph)
const noexcept final;
344 void Updated(TxGraphImpl& graph,
int level,
bool rename)
noexcept final;
346 Cluster*
CopyToStaging(TxGraphImpl& graph)
const noexcept final;
347 void GetConflicts(
const TxGraphImpl& graph, std::vector<Cluster*>& out)
const noexcept final;
349 void Clear(TxGraphImpl& graph,
int level)
noexcept final;
350 void MoveToMain(TxGraphImpl& graph)
noexcept final;
351 void Compact() noexcept final;
353 [[
nodiscard]]
bool Split(TxGraphImpl& graph,
int level) noexcept final;
354 void Merge(TxGraphImpl& graph,
int level, Cluster& cluster) noexcept final;
364 void SanityCheck(const TxGraphImpl& graph,
int level) const final;
392 friend class Cluster;
393 friend class SingletonClusterImpl;
394 friend class GenericClusterImpl;
395 friend class BlockBuilderImpl;
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;
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;
494 if (
a == b)
return std::strong_ordering::equal;
495 Assume(
a < m_entries.size() && b < m_entries.size());
497 const auto&
entry_b = m_entries[b];
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;
516 *m_entries[
entry_b.m_main_max_chunk_fallback].m_ref);
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; }
594 bool IsMissing() const noexcept {
return cluster ==
nullptr && index == 0; }
598 bool IsPresent() const noexcept {
return cluster !=
nullptr; }
608 ChunkIndex::iterator m_main_chunkindex_iterator;
619 int32_t m_main_equal_feerate_chunk_prefix_size;
624 GraphIndex m_main_max_chunk_fallback = GraphIndex(-1);
628 std::vector<Entry> m_entries;
631 std::vector<GraphIndex> m_unlinked;
635 explicit TxGraphImpl(
641 m_max_cluster_count(max_cluster_count),
642 m_max_cluster_size(max_cluster_size),
645 m_main_chunkindex(ChunkOrder(
this))
647 Assume(max_cluster_count >= 1);
655 TxGraphImpl(const TxGraphImpl&) =
delete;
656 TxGraphImpl& operator=(const TxGraphImpl&) =
delete;
657 TxGraphImpl(TxGraphImpl&&) =
delete;
658 TxGraphImpl& operator=(TxGraphImpl&&) =
delete;
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(); }
690 std::vector<Cluster*> GetConflicts() const noexcept;
698 return std::make_unique<GenericClusterImpl>(m_next_sequence_counter++);
703 return std::make_unique<SingletonClusterImpl>(m_next_sequence_counter++);
709 if (tx_count >= SingletonClusterImpl::MIN_INTENDED_TX_COUNT && tx_count <= SingletonClusterImpl::MAX_TX_COUNT) {
712 if (tx_count >= GenericClusterImpl::MIN_INTENDED_TX_COUNT && tx_count <= GenericClusterImpl::MAX_TX_COUNT) {
722 void UpdateRef(GraphIndex idx, Ref&
new_location)
noexcept final
724 auto& entry = m_entries[idx];
725 Assume(entry.m_ref !=
nullptr);
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;
745 for (
int level = 0; level <=
GetTopLevel(); ++level) {
746 if (entry.m_locator[level].IsPresent()) {
749 }
else if (entry.m_locator[level].IsRemoved()) {
767 m_unlinked.push_back(idx);
775 void Compact() noexcept;
779 Cluster*
PullIn(Cluster* cluster,
int level) noexcept;
784 void Split(Cluster& cluster,
int level) noexcept;
790 void Merge(
std::span<Cluster*>
to_merge,
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;
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;
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;
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;
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();
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;
903 auto insert_result = m_main_chunkindex.insert(std::move(m_main_chunkindex_discarded.back()));
906 m_main_chunkindex_discarded.pop_back();
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) {
949 m_linearization.push_back(
ret);
961void GenericClusterImpl::AddDependencies(SetType parents,
DepGraphIndex child)
noexcept
966void SingletonClusterImpl::AddDependencies(SetType parents,
DepGraphIndex child)
noexcept
970 Assume(parents == SetType{} || parents == SetType::Fill(0));
975 for (
auto pos : m_linearization) {
978 for (
auto pos : m_linearization) {
983 m_linearization.clear();
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];
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();
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;
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);
1097 for (
unsigned chunk_idx = 0; chunk_idx <
chunking.size(); ++chunk_idx) {
1111 auto it =
chunk.transactions.begin();
1114 while (it !=
chunk.transactions.end()) {
1125 auto& entry = graph.m_entries[
graph_idx];
1126 entry.m_main_lin_index =
lin_idx++;
1131 chunk.transactions.Reset(idx);
1132 if (
chunk.transactions.None()) {
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);
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;
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
1200 std::vector<Cluster*>
ret;
1203 auto& entry = m_entries[i];
1204 if (entry.m_locator[0].IsPresent()) {
1205 ret.push_back(entry.m_locator[0].cluster);
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
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);
1280 graph.ClearLocator(level, idx, m_quality == QualityLevel::OVERSIZED_SINGLETON);
1291 m_linearization.erase(std::remove_if(m_linearization.begin(), m_linearization.end(),
1292 [&](
auto pos) { return todo[pos]; }),
1293 m_linearization.end());
1298 graph.SetClusterQuality(level, m_quality, m_setindex,
new_quality);
1302void SingletonClusterImpl::ApplyRemovals(TxGraphImpl& graph,
int level, std::span<GraphIndex>&
to_remove)
noexcept
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
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
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();
1353 auto cluster = graph.ExtractCluster(1,
quality, m_setindex);
1354 graph.InsertCluster(0, std::move(cluster),
quality);
1358void SingletonClusterImpl::MoveToMain(TxGraphImpl& graph)
noexcept
1361 auto& entry = graph.m_entries[m_graph_index];
1362 entry.m_locator[1].SetMissing();
1366 auto cluster = graph.ExtractCluster(1,
quality, m_setindex);
1367 graph.InsertCluster(0, std::move(cluster),
quality);
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
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
1414 auto& entry =
ret.emplace_back();
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;
1439bool GenericClusterImpl::Split(TxGraphImpl& graph,
int level)
noexcept
1453 while (
todo.Any()) {
1464 graph.SetClusterQuality(level, m_quality, m_setindex,
split_quality);
1485 for (
auto i : m_linearization) {
1492 for (
auto i : m_linearization) {
1504 graph.GetClusterSet(level).m_cluster_usage +=
new_cluster->TotalMemoryUsage();
1509 m_linearization.clear();
1513bool SingletonClusterImpl::Split(TxGraphImpl& graph,
int level)
noexcept
1521void GenericClusterImpl::Merge(TxGraphImpl& graph,
int level, Cluster& other)
noexcept
1524 std::vector<DepGraphIndex>
remap(other.GetDepGraphIndexRange());
1533 m_mapping.push_back(idx);
1534 m_linearization.push_back(
new_pos);
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; });
1566 auto&
first_child = graph.m_entries[it->second].m_locator[level];
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);
1576 parents.Set(parent.index);
1589 graph.SetClusterQuality(level, m_quality, m_setindex, QualityLevel::NEEDS_FIX);
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;
1622 ret->m_quality = QualityLevel::NONE;
1638ClusterSetIndex TxGraphImpl::InsertCluster(
int level, std::unique_ptr<Cluster>&& cluster, QualityLevel
quality)
noexcept
1643 Assume(cluster->m_quality == QualityLevel::NONE);
1650 cluster->m_setindex =
ret;
1667void TxGraphImpl::DeleteCluster(Cluster& cluster,
int level)
noexcept
1675std::pair<Cluster*, int> TxGraphImpl::FindClusterAndLevel(GraphIndex idx,
int level)
const noexcept
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
1703 cluster = cluster->CopyToStaging(*
this);
1708void TxGraphImpl::ApplyRemovals(
int up_to_level)
noexcept
1711 for (
int level = 0; level <=
up_to_level; ++level) {
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;
1732 Cluster* cluster = m_entries[
to_remove_span.front()].m_locator[level].cluster;
1733 if (cluster !=
nullptr) {
1748void TxGraphImpl::SwapIndexes(GraphIndex
a, GraphIndex b, std::vector<Cluster*>&
affected_main)
noexcept
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);
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{});
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());
1816 m_entries.pop_back();
1824 cluster->Updated(*
this, 0,
true);
1829void TxGraphImpl::Split(Cluster& cluster,
int level)
noexcept
1834 bool del = cluster.Split(*
this, level);
1841void TxGraphImpl::SplitAll(
int up_to_level)
noexcept
1846 for (
int level = 0; level <=
up_to_level; ++level) {
1847 for (
auto quality : {QualityLevel::NEEDS_SPLIT_FIX, QualityLevel::NEEDS_SPLIT}) {
1849 while (!
queue.empty()) {
1850 Split(*
queue.back().get(), level);
1856void TxGraphImpl::GroupClusters(
int level)
noexcept
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;
1881 auto graph_idx = cluster->GetClusterEntry(0);
1905 std::sort(
an_clusters.begin(),
an_clusters.end(), [](
auto&
a,
auto& b)
noexcept { return a.second < b.second; });
1908 std::sort(
an_deps.begin(),
an_deps.end(), [&](
auto&
a,
auto& b)
noexcept { return a.second < b.second; });
1933 [](
auto&
a,
uint64_t seq)
noexcept { return a.sequence < seq; });
1941 while (
data->parent != data) {
1979 for (
const auto& [dep,
_] :
an_deps) {
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; });
2068void TxGraphImpl::Merge(std::span<Cluster*>
to_merge,
int level)
noexcept
2081 for (
size_t i = 1; i <
to_merge.size(); ++i) {
2085 if (size > max_size) {
2114void TxGraphImpl::ApplyDependencies(
int level)
noexcept
2123 if (
clusterset.m_deps_to_add.empty())
return;
2134 cluster =
PullIn(cluster, cluster->GetLevel(*
this));
2144 const auto&
loc = m_entries[
deps_span[0].second].m_locator[level];
2146 loc.cluster->ApplyDependencies(*
this, level,
deps_span);
2157std::pair<uint64_t, bool> GenericClusterImpl::Relinearize(TxGraphImpl& graph,
int level,
uint64_t max_cost)
noexcept
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);
2185 graph.SetClusterQuality(level, m_quality, m_setindex, QualityLevel::OPTIMAL);
2188 graph.SetClusterQuality(level, m_quality, m_setindex, QualityLevel::ACCEPTABLE);
2191 graph.SetClusterQuality(level, m_quality, m_setindex, QualityLevel::NEEDS_RELINEARIZE);
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
2220 for (
auto quality : {QualityLevel::NEEDS_FIX, QualityLevel::NEEDS_RELINEARIZE}) {
2222 while (!
queue.empty()) {
2230void TxGraphImpl::AddTransaction(Ref&
arg,
const FeePerWeight& feerate)
noexcept
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;
2246 cluster->AppendTransaction(idx, feerate);
2250 InsertCluster(level, std::move(cluster), oversized ? QualityLevel::OVERSIZED_SINGLETON : QualityLevel::OPTIMAL);
2262void TxGraphImpl::RemoveTransaction(
const Ref&
arg)
noexcept
2266 if (GetRefGraph(
arg) ==
nullptr)
return;
2272 if (cluster ==
nullptr)
return;
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);
2289 if (GetRefIndex(parent) == GetRefIndex(
child))
return;
2299 clusterset.m_deps_to_add.emplace_back(GetRefIndex(parent), GetRefIndex(
child));
2307 if (GetRefGraph(
arg) ==
nullptr)
return false;
2313 return cluster !=
nullptr;
2316void GenericClusterImpl::GetAncestorRefs(
const TxGraphImpl& graph, std::span<std::pair<Cluster*, DepGraphIndex>>&
args, std::vector<TxGraph::Ref*>& output)
noexcept
2321 while (!
args.empty()) {
2322 if (
args.front().first !=
this)
break;
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
2352 while (!
args.empty()) {
2353 if (
args.front().first !=
this)
break;
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
2376 for (
auto& ref :
range) {
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();
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 {};
2440 if (cluster ==
nullptr)
return {};
2442 std::pair<Cluster*, DepGraphIndex>
match = {cluster, m_entries[GetRefIndex(
arg)].m_locator[
cluster_level].index};
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 {};
2461 if (cluster ==
nullptr)
return {};
2463 std::pair<Cluster*, DepGraphIndex>
match = {cluster, m_entries[GetRefIndex(
arg)].m_locator[
cluster_level].index};
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
2479 std::vector<std::pair<Cluster*, DepGraphIndex>>
matches;
2484 if (GetRefGraph(*
arg) ==
nullptr)
continue;
2488 if (cluster ==
nullptr)
continue;
2493 std::sort(
matches.begin(),
matches.end(), [](
auto&
a,
auto& b)
noexcept { return CompareClusters(a.first, b.first) < 0; });
2496 std::vector<TxGraph::Ref*>
ret;
2503std::vector<TxGraph::Ref*> TxGraphImpl::GetDescendantsUnion(std::span<const Ref* const>
args, Level
level_select)
noexcept
2512 std::vector<std::pair<Cluster*, DepGraphIndex>>
matches;
2517 if (GetRefGraph(*
arg) ==
nullptr)
continue;
2521 if (cluster ==
nullptr)
continue;
2526 std::sort(
matches.begin(),
matches.end(), [](
auto&
a,
auto& b)
noexcept { return CompareClusters(a.first, b.first) < 0; });
2529 std::vector<TxGraph::Ref*>
ret;
2536std::vector<TxGraph::Ref*> TxGraphImpl::GetCluster(
const Ref&
arg, Level
level_select)
noexcept
2540 if (GetRefGraph(
arg) ==
nullptr)
return {};
2549 if (cluster ==
nullptr)
return {};
2552 std::vector<TxGraph::Ref*>
ret(cluster->GetTxCount());
2553 cluster->GetClusterRefs(*
this,
ret, 0);
2564FeePerWeight TxGraphImpl::GetIndividualFeerate(
const Ref&
arg)
noexcept
2567 if (GetRefGraph(
arg) ==
nullptr)
return {};
2571 Cluster* cluster{
nullptr};
2573 for (level = 0; level <=
GetTopLevel(); ++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);
2590 if (GetRefGraph(
arg) ==
nullptr)
return {};
2595 Assume(m_main_clusterset.m_deps_to_add.empty());
2598 if (cluster ==
nullptr)
return {};
2602 const auto& entry = m_entries[GetRefIndex(
arg)];
2603 return entry.m_main_chunk_feerate;
2606bool TxGraphImpl::IsOversized(Level
level_select)
noexcept
2626void TxGraphImpl::StartStaging() noexcept
2629 Assume(!m_staging_clusterset.has_value());
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);
2699 for (
auto idx : m_staging_clusterset->m_removed) {
2700 m_entries[idx].m_locator[1].SetMissing();
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();
2730 if (m_quality == QualityLevel::OVERSIZED_SINGLETON) {
2733 graph.SetClusterQuality(level, m_quality, m_setindex, QualityLevel::NEEDS_RELINEARIZE);
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);
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)];
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) {
2810std::pair<std::vector<FeeFrac>, std::vector<FeeFrac>> TxGraphImpl::GetMainStagingDiagrams() noexcept
2812 Assume(m_staging_clusterset.has_value());
2814 Assume(m_main_clusterset.m_deps_to_add.empty());
2816 Assume(m_staging_clusterset->m_deps_to_add.empty());
2826 for (
const auto& cluster : m_staging_clusterset->m_clusters[
quality]) {
2836void GenericClusterImpl::SanityCheck(
const TxGraphImpl& graph,
int level)
const
2856 if (m_linearization.empty())
return;
2858 for (
auto lin_pos : m_linearization) {
2860 const auto& entry = graph.m_entries[m_mapping[
lin_pos]];
2867 assert(entry.m_locator[level].cluster ==
this);
2889 if (graph.m_main_clusterset.m_to_remove.empty()) {
2891 assert((entry.m_main_chunkindex_iterator != graph.m_main_chunkindex.end()) ==
is_chunk_end);
2893 auto&
chunk_data = *entry.m_main_chunkindex_iterator;
2909void SingletonClusterImpl::SanityCheck(
const TxGraphImpl& graph,
int level)
const
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);
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;
2932void TxGraphImpl::SanityCheck()
const
2948 for (GraphIndex idx = 0; idx < m_entries.size(); ++idx) {
2949 const auto& entry = m_entries[idx];
2950 if (entry.m_ref ==
nullptr) {
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());
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);
2977 }
else if (locator.IsRemoved()) {
2990 for (
int level = 0; level <=
GetTopLevel(); ++level) {
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);
3030 if (cluster.GetTxCount() != 0) {
3034 cluster.SanityCheck(*
this, level);
3047 for (GraphIndex idx :
clusterset.m_to_remove) {
3048 assert(idx < m_entries.size());
3089 std::set<GraphIndex>
actual_unlinked(m_unlinked.begin(), m_unlinked.end());
3101 for (
const auto&
chunk : m_main_chunkindex) {
3102 GraphIndex idx =
chunk.m_graph_index;
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;
3126 if (
clusterset.m_oversized ==
true)
continue;
3128 while (!
queue.empty()) {
3135 if (
quality == QualityLevel::NEEDS_FIX ||
quality == QualityLevel::NEEDS_RELINEARIZE) {
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;
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()) {
3189 ret->first.resize(1);
3192 m_known_end_of_cluster =
true;
3197 m_known_end_of_cluster = m_cur_cluster->GetClusterRefs(*m_graph,
ret->first,
start_pos);
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()) {
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
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();
3271 ret.first.resize(1);
3278 std::reverse(
ret.first.begin(),
ret.first.end());
3285std::vector<TxGraph::Ref*> TxGraphImpl::Trim() noexcept
3288 Assume(m_main_chunkindex_observers == 0 || level != 0);
3289 std::vector<TxGraph::Ref*>
ret;
3324 std::vector<std::pair<GraphIndex, GraphIndex>>
deps_by_child;
3331 std::vector<std::vector<TrimTxData>::iterator>
trim_heap;
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 {
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;
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; });
3429 if (
trim_it->m_deps_left == 0 &&
trim_it->m_tx_size <= m_max_cluster_size) {
3476 entry.m_uf_parent = &entry;
3477 entry.m_uf_count = 1;
3478 entry.m_uf_size = entry.m_tx_size;
3511 if (--
chl_it->m_deps_left == 0) {
3535size_t TxGraphImpl::GetMainMemoryUsage() noexcept
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,
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.
uint32_t DepGraphIndex
Data type to represent transaction indices in DepGraphs and the clusters they represent.
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.
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.
Data structure storing a fee and size, ordered by increasing fee/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,...
static 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.