30 const uint64_t m_txid{0};
31 SimTxObject() noexcept = default;
32 explicit SimTxObject(uint64_t txid) noexcept : m_txid(txid) {}
49 static constexpr auto MISSING = Pos(-1);
57 std::array<std::shared_ptr<SimTxObject>, MAX_TRANSACTIONS> simmap;
59 std::map<const TxGraph::Ref*, Pos> simrevmap;
61 std::vector<std::shared_ptr<SimTxObject>> removed;
63 std::optional<bool> oversized;
70 uint64_t max_cluster_size;
72 bool real_is_optimal{
false};
75 explicit SimTxGraph(
DepGraphIndex cluster_count, uint64_t cluster_size) :
76 max_cluster_count(cluster_count), max_cluster_size(cluster_size) {}
79 SimTxGraph(
const SimTxGraph&)
noexcept =
default;
80 SimTxGraph& operator=(
const SimTxGraph&)
noexcept =
default;
81 SimTxGraph(SimTxGraph&&) noexcept = default;
82 SimTxGraph& operator=(SimTxGraph&&) noexcept = default;
85 std::vector<SetType> GetComponents()
88 std::vector<SetType>
ret;
92 ret.push_back(component);
102 if (!oversized.has_value()) {
105 for (
auto component : GetComponents()) {
106 if (component.Count() > max_cluster_count) oversized =
true;
107 uint64_t component_size{0};
108 for (
auto i : component) component_size += graph.
FeeRate(i).
size;
109 if (component_size > max_cluster_size) oversized =
true;
136 auto it = simrevmap.find(ref);
137 if (it != simrevmap.end())
return it->second;
142 SimTxObject* GetRef(Pos pos)
146 return simmap[pos].get();
154 real_is_optimal =
false;
155 MakeModified(simpos);
157 simmap[simpos] = std::make_shared<SimTxObject>(txid);
159 auto ptr = simmap[simpos].get();
160 simrevmap[ptr] = simpos;
162 if (oversized.has_value() && !*oversized) oversized = std::nullopt;
168 auto par_pos =
Find(parent);
169 if (par_pos ==
MISSING)
return;
170 auto chl_pos =
Find(child);
171 if (chl_pos ==
MISSING)
return;
173 MakeModified(par_pos);
174 real_is_optimal =
false;
176 if (oversized.has_value() && !*oversized) oversized = std::nullopt;
182 auto pos =
Find(ref);
185 real_is_optimal =
false;
192 auto pos =
Find(ref);
195 real_is_optimal =
false;
197 simrevmap.erase(simmap[pos].get());
200 removed.push_back(std::move(simmap[pos]));
203 if (oversized.has_value() && *oversized) oversized = std::nullopt;
210 void DestroyTransaction(
TxGraph::Ref* ref,
bool reset_oversize)
212 auto pos =
Find(ref);
217 auto remove = std::partition(removed.begin(), removed.end(), [&](
auto& arg) { return arg.get() != ref; });
218 removed.erase(remove, removed.end());
222 real_is_optimal =
false;
223 simrevmap.erase(simmap[pos].get());
226 if (reset_oversize && oversized.has_value() && *oversized) {
227 oversized = std::nullopt;
234 SetType MakeSet(std::span<TxGraph::Ref* const> arg)
238 auto pos =
Find(ptr);
248 auto pos =
Find(arg);
255 void IncludeAncDesc(std::vector<TxGraph::Ref*>& arg,
bool desc)
257 std::vector<TxGraph::Ref*>
ret;
258 for (
auto ptr : arg) {
259 auto simpos =
Find(ptr);
262 ret.push_back(simmap[i].get());
271 for (
auto ptr :
ret) {
272 if (std::find(arg.begin(), arg.end(), ptr) == arg.end()) {
281 bool MatchesOversizedClusters(
const SetType& set)
283 if (set.Any() && !IsOversized())
return false;
286 if (!set.IsSubsetOf(todo))
return false;
292 bool is_oversized = component.Count() > max_cluster_count;
293 uint64_t component_size{0};
294 for (
auto i : component) component_size += graph.
FeeRate(i).
size;
295 is_oversized |= component_size > max_cluster_size;
297 if (is_oversized != set.Overlaps(component))
return false;
322 SimTxObject empty_ref;
333 std::set<uint64_t> assigned_txids;
337 uint64_t txid_a =
static_cast<const SimTxObject&
>(a).m_txid;
338 uint64_t txid_b =
static_cast<const SimTxObject&
>(b).m_txid;
339 assert(assigned_txids.contains(txid_a));
340 assert(assigned_txids.contains(txid_b));
341 return txid_a <=> txid_b;
349 std::vector<SimTxGraph> sims;
351 sims.emplace_back(max_cluster_count, max_cluster_size);
354 struct BlockBuilderData
357 std::unique_ptr<TxGraph::BlockBuilder> builder;
359 SimTxGraph::SetType included;
361 SimTxGraph::SetType done;
365 BlockBuilderData(std::unique_ptr<TxGraph::BlockBuilder> builder_in) : builder(std::move(builder_in)) {}
369 std::vector<BlockBuilderData> block_builders;
373 auto pick_fn = [&]()
noexcept -> SimTxObject* {
374 size_t tx_count[2] = {sims[0].GetTransactionCount(), 0};
376 size_t choices = tx_count[0] + sims[0].removed.size() + 1;
377 if (sims.size() == 2) {
378 tx_count[1] = sims[1].GetTransactionCount();
379 choices += tx_count[1] + sims[1].removed.size();
384 for (
size_t level = 0; level < sims.size(); ++level) {
385 auto& sim = sims[level];
386 if (choice < tx_count[level]) {
388 for (
auto i : sim.graph.Positions()) {
389 if (choice == 0)
return sim.GetRef(i);
394 choice -= tx_count[level];
396 if (choice < sim.removed.size()) {
398 return sim.removed[choice].get();
400 choice -= sim.removed.size();
410 auto get_diagram_fn = [&](
TxGraph::Level level_select) -> std::vector<FeeFrac> {
412 auto& sim = sims[level];
414 std::set<std::vector<TxGraph::Ref*>> clusters;
415 for (
auto i : sim.graph.Positions()) {
416 auto ref = sim.GetRef(i);
417 clusters.insert(real->GetCluster(*ref, level_select));
421 std::vector<FeeFrac> chunk_feerates;
422 for (
const auto& cluster : clusters) {
423 num_tx += cluster.size();
424 std::vector<SimTxGraph::Pos> linearization;
425 linearization.reserve(cluster.size());
426 for (
auto refptr : cluster) linearization.push_back(sim.Find(refptr));
428 chunk_feerates.push_back(chunk_feerate);
434 assert(num_tx == sim.GetTransactionCount());
438 return chunk_feerates;
456 int builder_idx = block_builders.empty() ? -1 : int((orig_command & 3) % block_builders.size());
461 auto& top_sim = sims.back();
463 auto& main_sim = sims[0];
468 if ((block_builders.empty() || sims.size() > 1) && top_sim.GetTransactionCount() < SimTxGraph::MAX_TRANSACTIONS &&
command-- == 0) {
487 }
while (txid == 0 || assigned_txids.contains(txid));
488 assigned_txids.insert(txid);
490 top_sim.AddTransaction(*real, feerate, txid);
492 }
else if ((block_builders.empty() || sims.size() > 1) && top_sim.GetTransactionCount() + top_sim.removed.size() > 1 &&
command-- == 0) {
494 auto par = pick_fn();
495 auto chl = pick_fn();
496 auto pos_par = top_sim.Find(par);
497 auto pos_chl = top_sim.Find(chl);
501 if (top_sim.graph.Ancestors(pos_par)[pos_chl])
break;
503 top_sim.AddDependency(par, chl);
504 top_sim.real_is_optimal =
false;
505 real->AddDependency(*par, *chl);
507 }
else if ((block_builders.empty() || sims.size() > 1) && top_sim.removed.size() < 100 &&
command-- == 0) {
511 std::vector<TxGraph::Ref*> to_remove;
512 to_remove.push_back(pick_fn());
513 top_sim.IncludeAncDesc(to_remove, alt);
516 std::shuffle(to_remove.begin(), to_remove.end(), rng);
518 real->RemoveTransaction(*ptr);
519 top_sim.RemoveTransaction(ptr);
522 }
else if (sel_sim.removed.size() > 0 &&
command-- == 0) {
532 if (removed_pos != sel_sim.removed.size() - 1) {
533 std::swap(sel_sim.removed[removed_pos], sel_sim.removed.back());
535 sel_sim.removed.pop_back();
537 }
else if (block_builders.empty() &&
command-- == 0) {
539 std::vector<TxGraph::Ref*> to_destroy;
540 to_destroy.push_back(pick_fn());
546 auto old_size = to_destroy.size();
547 for (
auto& sim : sims) sim.IncludeAncDesc(to_destroy, alt);
548 if (to_destroy.size() == old_size)
break;
552 std::shuffle(to_destroy.begin(), to_destroy.end(), rng);
554 for (
size_t level = 0; level < sims.size(); ++level) {
555 sims[level].DestroyTransaction(ptr, level == sims.size() - 1);
559 }
else if (block_builders.empty() &&
command-- == 0) {
567 auto ref = pick_fn();
568 real->SetTransactionFee(*ref,
fee);
569 for (
auto& sim : sims) {
570 sim.SetTransactionFee(ref,
fee);
575 assert(real->GetTransactionCount(level_select) == sel_sim.GetTransactionCount());
579 auto ref = pick_fn();
580 bool exists = real->Exists(*ref, level_select);
582 assert(exists == should_exist);
586 assert(sel_sim.IsOversized() == real->IsOversized(level_select));
590 auto ref = pick_fn();
591 auto feerate = real->GetIndividualFeerate(*ref);
593 for (
auto& sim : sims) {
594 auto simpos = sim.Find(ref);
597 assert(feerate == sim.graph.FeeRate(simpos));
602 }
else if (!main_sim.IsOversized() &&
command-- == 0) {
604 auto ref = pick_fn();
605 auto feerate = real->GetMainChunkFeerate(*ref);
606 auto simpos = main_sim.Find(ref);
612 assert(feerate.
size >= main_sim.graph.FeeRate(simpos).size);
613 assert(feerate.
size <= main_sim.SumAll().size);
616 }
else if (!sel_sim.IsOversized() &&
command-- == 0) {
618 auto ref = pick_fn();
619 auto result = alt ? real->GetDescendants(*ref, level_select)
620 : real->GetAncestors(*ref, level_select);
621 assert(result.size() <= max_cluster_count);
622 auto result_set = sel_sim.MakeSet(result);
623 assert(result.size() == result_set.Count());
624 auto expect_set = sel_sim.GetAncDesc(ref, alt);
625 assert(result_set == expect_set);
627 }
else if (!sel_sim.IsOversized() &&
command-- == 0) {
629 std::vector<TxGraph::Ref*> refs;
633 for (
size_t i = 0; i <
count; ++i) {
637 std::shuffle(refs.begin(), refs.end(), rng);
639 auto result = alt ? real->GetDescendantsUnion(refs, level_select)
640 : real->GetAncestorsUnion(refs, level_select);
641 auto result_set = sel_sim.MakeSet(result);
642 assert(result.size() == result_set.Count());
644 SimTxGraph::SetType expect_set;
645 for (
TxGraph::Ref* ref : refs) expect_set |= sel_sim.GetAncDesc(ref, alt);
647 assert(result_set == expect_set);
649 }
else if (!sel_sim.IsOversized() &&
command-- == 0) {
651 auto ref = pick_fn();
652 auto result = real->GetCluster(*ref, level_select);
654 assert(result.size() <= max_cluster_count);
656 auto left = sel_sim.graph.Positions();
657 uint64_t total_size{0};
658 for (
auto refptr : result) {
659 auto simpos = sel_sim.Find(refptr);
660 total_size += sel_sim.graph.FeeRate(simpos).size;
664 assert(!sel_sim.graph.Ancestors(simpos).Overlaps(left));
667 assert(total_size <= max_cluster_size);
669 auto result_set = sel_sim.MakeSet(result);
670 assert(sel_sim.graph.IsConnected(result_set));
672 auto simpos = sel_sim.Find(ref);
674 assert(result_set[simpos]);
676 assert(result_set.None());
679 for (
auto i : result_set) {
680 assert(sel_sim.graph.Ancestors(i).IsSubsetOf(result_set));
681 assert(sel_sim.graph.Descendants(i).IsSubsetOf(result_set));
686 assert((sims.size() == 2) == real->HaveStaging());
688 }
else if (sims.size() < 2 &&
command-- == 0) {
690 sims.emplace_back(sims.back());
691 sims.back().modified = SimTxGraph::SetType{};
692 real->StartStaging();
694 }
else if (block_builders.empty() && sims.size() > 1 &&
command-- == 0) {
696 real->CommitStaging();
698 const bool main_optimal = std::all_of(sims.cbegin(), sims.cend(), [](
const auto &sim) { return sim.real_is_optimal; });
699 sims.erase(sims.begin());
700 sims.front().real_is_optimal = main_optimal;
702 }
else if (sims.size() > 1 &&
command-- == 0) {
704 real->AbortStaging();
709 sims.back().oversized = std::nullopt;
711 }
else if (!main_sim.IsOversized() &&
command-- == 0) {
713 auto ref_a = pick_fn();
714 auto ref_b = pick_fn();
715 auto sim_a = main_sim.Find(ref_a);
716 auto sim_b = main_sim.Find(ref_b);
719 auto cmp = real->CompareMainOrder(*ref_a, *ref_b);
721 if (sim_a != sim_b)
assert(cmp != 0);
723 if (main_sim.graph.Ancestors(sim_a)[sim_b])
assert(cmp >= 0);
724 if (main_sim.graph.Descendants(sim_a)[sim_b])
assert(cmp <= 0);
729 }
else if (!sel_sim.IsOversized() &&
command-- == 0) {
731 std::vector<TxGraph::Ref*> refs;
735 for (
size_t i = 0; i <
count; ++i) {
739 std::shuffle(refs.begin(), refs.end(), rng);
741 auto result = real->CountDistinctClusters(refs, level_select);
745 SimTxGraph::SetType sim_reps;
746 for (
auto ref : refs) {
748 auto simpos = sel_sim.Find(ref);
751 auto component = sel_sim.graph.GetConnectedComponent(sel_sim.graph.Positions(), simpos);
754 sim_reps.Set(component.First());
758 assert(result == sim_reps.Count());
763 bool ret = real->DoWork(max_cost);
764 uint64_t cost_for_optimal{0};
765 for (
unsigned level = 0; level < sims.size(); ++level) {
770 if (sims[level].IsOversized())
continue;
771 if (level == 0 && !block_builders.empty())
continue;
775 sims[level].real_is_optimal =
true;
778 for (
auto component : sims[level].GetComponents()) {
779 auto cost_opt_this_cluster = MaxOptimalLinearizationCost(component.Count());
780 if (cost_opt_this_cluster > acceptable_cost) {
784 cost_for_optimal += cost_opt_this_cluster + acceptable_cost;
786 cost_for_optimal += cost_opt_this_cluster;
794 assert(max_cost <= cost_for_optimal);
797 }
else if (sims.size() == 2 && !sims[0].IsOversized() && !sims[1].IsOversized() &&
command-- == 0) {
799 auto [real_main_diagram, real_staged_diagram] = real->GetMainStagingDiagrams();
800 auto real_sum_main = std::accumulate(real_main_diagram.begin(), real_main_diagram.end(),
FeeFrac{});
801 auto real_sum_staged = std::accumulate(real_staged_diagram.begin(), real_staged_diagram.end(),
FeeFrac{});
802 auto real_gain = real_sum_staged - real_sum_main;
803 auto sim_gain = sims[1].SumAll() - sims[0].SumAll();
807 assert(sim_gain == real_gain);
809 for (
size_t i = 1; i < real_main_diagram.size(); ++i) {
812 for (
size_t i = 1; i < real_staged_diagram.size(); ++i) {
816 }
else if (block_builders.size() < 4 && !main_sim.IsOversized() &&
command-- == 0) {
818 block_builders.emplace_back(real->GetBlockBuilder());
820 }
else if (!block_builders.empty() &&
command-- == 0) {
822 block_builders.erase(block_builders.begin() + builder_idx);
824 }
else if (!block_builders.empty() &&
command-- == 0) {
826 auto& builder_data = block_builders[builder_idx];
827 auto new_included = builder_data.included;
828 auto new_done = builder_data.done;
829 auto chunk = builder_data.builder->GetCurrentChunk();
832 if (!builder_data.last_feerate.IsEmpty()) {
835 builder_data.last_feerate = chunk->second;
840 auto simpos = main_sim.Find(ref);
843 sum_feerate += main_sim.graph.FeeRate(simpos);
845 assert(!new_done[simpos]);
846 new_done.Set(simpos);
848 new_included.Set(simpos);
849 assert(main_sim.graph.Ancestors(simpos).IsSubsetOf(new_included));
851 assert(sum_feerate == chunk->second);
855 if (builder_data.done == builder_data.included) {
856 assert(builder_data.done.Count() == main_sim.GetTransactionCount());
860 if ((orig_command % 7) >= 5) {
861 auto chunk2 = builder_data.builder->GetCurrentChunk();
865 if ((orig_command % 5) >= 3) {
867 builder_data.builder->Skip();
870 builder_data.builder->Include();
871 builder_data.included = new_included;
873 builder_data.done = new_done;
875 }
else if (!main_sim.IsOversized() &&
command-- == 0) {
877 auto [worst_chunk, worst_chunk_feerate] = real->GetWorstMainChunk();
880 if (main_sim.GetTransactionCount() == 0) {
881 assert(worst_chunk.empty());
882 assert(worst_chunk_feerate.IsEmpty());
884 assert(!worst_chunk.empty());
885 SimTxGraph::SetType done;
889 auto simpos = main_sim.Find(ref);
891 sum += main_sim.graph.FeeRate(simpos);
896 assert(main_sim.graph.Descendants(simpos).IsSubsetOf(done));
901 }
else if ((block_builders.empty() || sims.size() > 1) &&
command-- == 0) {
903 bool was_oversized = top_sim.IsOversized();
904 auto removed = real->Trim();
906 assert(was_oversized == !removed.empty());
907 if (!was_oversized)
break;
908 auto removed_set = top_sim.MakeSet(removed);
910 for (
auto simpos : removed_set) {
911 assert(top_sim.graph.Descendants(simpos).IsSubsetOf(removed_set));
915 assert(top_sim.MatchesOversizedClusters(removed_set));
920 for (
auto simpos : removed_set) {
921 top_sim.RemoveTransaction(top_sim.GetRef(simpos));
923 assert(!top_sim.IsOversized());
925 }
else if ((block_builders.empty() || sims.size() > 1) &&
926 top_sim.GetTransactionCount() > max_cluster_count && !top_sim.IsOversized() &&
command-- == 0) {
937 auto clusters = top_sim.GetComponents();
940 bool made_oversized =
false;
941 auto merges_left = clusters.size() - 1;
942 while (merges_left > 0) {
945 auto par_cl = rng.
randrange(clusters.size());
946 auto chl_cl = rng.
randrange(clusters.size() - 1);
947 chl_cl += (chl_cl >= par_cl);
954 for (
int i = 0; i < num_deps; ++i) {
956 auto par_idx = rng.
randrange(clusters[par_cl].Count());
957 SimTxGraph::Pos par_pos = 0;
958 for (
auto j : clusters[par_cl]) {
966 auto chl_idx = rng.
randrange(clusters[chl_cl].Count());
967 SimTxGraph::Pos chl_pos = 0;
968 for (
auto j : clusters[chl_cl]) {
976 auto par_ref = top_sim.GetRef(par_pos);
977 auto chl_ref = top_sim.GetRef(chl_pos);
978 top_sim.AddDependency(par_ref, chl_ref);
979 real->AddDependency(*par_ref, *chl_ref);
982 auto par_cluster = clusters[par_cl];
983 auto chl_cluster = clusters[chl_cl];
984 auto new_cluster = par_cluster | chl_cluster;
986 std::erase_if(clusters, [&](
const auto& cl)
noexcept {
return cl == par_cluster || cl == chl_cluster; });
988 clusters.push_back(new_cluster);
991 if (!made_oversized) {
992 made_oversized = new_cluster.Count() > max_cluster_count;
993 if (!made_oversized) {
995 for (
auto i : new_cluster) total += top_sim.graph.FeeRate(i);
996 if (uint32_t(total.
size) > max_cluster_size) made_oversized =
true;
998 if (made_oversized) merges_left = rng.
randrange(clusters.size());
1003 uint32_t max_removed = 0;
1004 for (
auto& cluster : clusters) {
1006 std::vector<uint32_t> sizes;
1007 for (
auto i : cluster) sizes.push_back(top_sim.graph.FeeRate(i).size);
1008 auto sum_sizes = std::accumulate(sizes.begin(), sizes.end(), uint64_t{0});
1010 std::ranges::sort(sizes, std::greater{});
1012 while (sizes.size() > max_cluster_count || sum_sizes > max_cluster_size) {
1013 sum_sizes -= sizes.back();
1020 auto removed = real->Trim();
1022 assert(removed.size() >= 1);
1023 assert(removed.size() <= max_removed);
1025 auto removed_set = top_sim.MakeSet(removed);
1026 for (
auto simpos : removed_set) {
1027 assert(top_sim.graph.Descendants(simpos).IsSubsetOf(removed_set));
1031 assert(top_sim.MatchesOversizedClusters(removed_set));
1036 for (
auto simpos : removed_set) {
1037 top_sim.RemoveTransaction(top_sim.GetRef(simpos));
1039 assert(!top_sim.IsOversized());
1043 auto usage = real->GetMainMemoryUsage();
1046 auto usage2 = real->GetMainMemoryUsage();
1050 if (main_sim.GetTransactionCount() == 0) {
1062 real->SanityCheck();
1064 if (!sims[0].IsOversized()) {
1068 std::vector<SimTxGraph::Pos> vec1;
1069 for (
auto i : sims[0].graph.
Positions()) vec1.push_back(i);
1070 std::shuffle(vec1.begin(), vec1.end(), rng);
1072 std::shuffle(vec2.begin(), vec2.end(), rng);
1073 if (vec1 == vec2) std::next_permutation(vec2.begin(), vec2.end());
1076 auto cmp = [&](SimTxGraph::Pos a, SimTxGraph::Pos b)
noexcept {
1077 return real->CompareMainOrder(*sims[0].GetRef(a), *sims[0].GetRef(b)) < 0;
1079 std::ranges::sort(vec1, cmp);
1080 std::ranges::sort(vec2, cmp);
1087 auto todo = sims[0].graph.Positions();
1088 for (
auto i : vec1) {
1096 if (sims[0].real_is_optimal) {
1099 auto txid_a = sims[0].GetRef(a)->m_txid;
1100 auto txid_b = sims[0].GetRef(b)->m_txid;
1101 return txid_a <=> txid_b;
1103 auto [sim_lin, sim_optimal, _cost] =
Linearize(sims[0].graph, 300000, rng.
rand64(), fallback_order_sim, vec1);
1115 std::map<SimTxGraph::Pos, int32_t> comp_prefix_sizes;
1119 std::pair<int32_t, uint64_t> max_chunk_tiebreak{0, 0};
1120 for (
const auto& chunk : real_chunking) {
1123 comp_prefix_sizes.clear();
1124 max_chunk_tiebreak = {0, 0};
1126 last_chunk_feerate = chunk.feerate;
1128 auto component = sims[0].graph.GetConnectedComponent(sims[0].graph.Positions(), chunk.transactions.First());
1129 assert(chunk.transactions.IsSubsetOf(component));
1130 auto comp_key = component.First();
1131 auto& comp_prefix_size = comp_prefix_sizes[comp_key];
1132 comp_prefix_size += chunk.feerate.size;
1134 uint64_t chunk_max_txid{0};
1135 for (
auto tx : chunk.transactions) {
1136 auto txid = sims[0].GetRef(tx)->m_txid;
1137 chunk_max_txid = std::max(txid, chunk_max_txid);
1141 std::pair<int32_t, uint64_t> chunk_tiebreak{comp_prefix_size, chunk_max_txid};
1142 assert(chunk_tiebreak > max_chunk_tiebreak);
1143 max_chunk_tiebreak = chunk_tiebreak;
1150 for (
const auto& component : sims[0].GetComponents()) {
1151 std::vector<DepGraphIndex> sim_chunk_lin, real_chunk_lin;
1152 for (
auto i : sim_lin) {
1153 if (component[i]) sim_chunk_lin.push_back(i);
1155 for (
auto i : vec1) {
1156 if (component[i]) real_chunk_lin.push_back(i);
1158 assert(sim_chunk_lin == real_chunk_lin);
1172 std::vector<std::optional<SimTxObject>> txobjects_redo;
1173 txobjects_redo.resize(sims[0].graph.PositionRange());
1175 std::vector<DepGraphIndex> positions;
1176 for (
auto i : sims[0].graph.Positions()) positions.push_back(i);
1177 std::shuffle(positions.begin(), positions.end(), rng);
1178 for (
auto i : positions) {
1179 txobjects_redo[i].emplace(sims[0].GetRef(i)->m_txid);
1183 std::vector<std::pair<DepGraphIndex, DepGraphIndex>> deps;
1184 for (
auto i : sims[0].graph.Positions()) {
1185 for (
auto j : sims[0].graph.GetReducedParents(i)) {
1186 deps.emplace_back(j, i);
1189 std::shuffle(deps.begin(), deps.end(), rng);
1190 for (
auto [parent, child] : deps) {
1191 real_redo->AddDependency(*txobjects_redo[parent], *txobjects_redo[child]);
1194 if (real_redo->DoWork(300000)) {
1196 auto vec_redo = vec1;
1197 std::shuffle(vec_redo.begin(), vec_redo.end(), rng);
1198 if (vec_redo == vec1) std::next_permutation(vec_redo.begin(), vec_redo.end());
1200 auto cmp_redo = [&](SimTxGraph::Pos a, SimTxGraph::Pos b)
noexcept {
1201 return real_redo->CompareMainOrder(*txobjects_redo[a], *txobjects_redo[b]) < 0;
1203 std::ranges::sort(vec_redo, cmp_redo);
1205 assert(vec1 == vec_redo);
1211 for (
size_t pos = 0; pos < vec1.size(); ++pos) {
1212 auto pos_feerate = real->GetMainChunkFeerate(*sims[0].GetRef(vec1[pos]));
1214 size_t before = rng.
randrange<
size_t>(pos);
1215 auto before_feerate = real->GetMainChunkFeerate(*sims[0].GetRef(vec1[before]));
1218 if (pos + 1 < vec1.size()) {
1219 size_t after = pos + 1 + rng.
randrange<
size_t>(vec1.size() - 1 - pos);
1220 auto after_feerate = real->GetMainChunkFeerate(*sims[0].GetRef(vec1[after]));
1227 auto builder = real->GetBlockBuilder();
1228 std::vector<SimTxGraph::Pos> vec_builder;
1229 std::vector<TxGraph::Ref*> last_chunk;
1231 while (
auto chunk = builder->GetCurrentChunk()) {
1236 assert(real->GetMainChunkFeerate(*ref) == chunk->second);
1238 sum += real->GetIndividualFeerate(*ref);
1240 auto simpos = sims[0].Find(ref);
1242 vec_builder.push_back(simpos);
1245 last_chunk = std::move(chunk->first);
1246 last_chunk_feerate = chunk->second;
1249 assert(vec_builder == vec1);
1252 std::reverse(last_chunk.begin(), last_chunk.end());
1253 auto [worst_chunk, worst_chunk_feerate] = real->GetWorstMainChunk();
1254 assert(last_chunk == worst_chunk);
1255 assert(last_chunk_feerate == worst_chunk_feerate);
1263 if (sims.size() >= 2 && !sims[1].IsOversized()) {
1266 auto [main_cmp_diagram, stage_cmp_diagram] = real->GetMainStagingDiagrams();
1268 for (
size_t i = 1; i < main_cmp_diagram.size(); ++i) {
1271 for (
size_t i = 1; i < stage_cmp_diagram.size(); ++i) {
1283 std::vector<FeeFrac> missing_main_cmp;
1284 std::set_difference(main_real_diagram.begin(), main_real_diagram.end(),
1285 main_cmp_diagram.begin(), main_cmp_diagram.end(),
1286 std::inserter(missing_main_cmp, missing_main_cmp.end()),
1288 assert(main_cmp_diagram.size() + missing_main_cmp.size() == main_real_diagram.size());
1291 std::vector<FeeFrac> missing_stage_cmp;
1292 std::set_difference(stage_real_diagram.begin(), stage_real_diagram.end(),
1293 stage_cmp_diagram.begin(), stage_cmp_diagram.end(),
1294 std::inserter(missing_stage_cmp, missing_stage_cmp.end()),
1296 assert(stage_cmp_diagram.size() + missing_stage_cmp.size() == stage_real_diagram.size());
1299 assert(missing_main_cmp == missing_stage_cmp);
1306 for (
const auto& feerate : missing_main_cmp) missing_real += feerate;
1307 FeeFrac missing_expected = sims[1].graph.FeeRate(sims[1].graph.Positions() - sims[1].modified);
1314 assert(real->HaveStaging() == (sims.size() > 1));
1321 assert(real->IsOversized(level) == sim.IsOversized());
1322 assert(real->GetTransactionCount(level) == sim.GetTransactionCount());
1324 if (!sim.IsOversized()) {
1325 auto todo = sim.graph.Positions();
1328 while (todo.Any()) {
1329 auto component = sim.graph.FindConnectedComponent(todo);
1332 for (
auto i : component) {
1334 assert(sim.graph.FeeRate(i) == real->GetIndividualFeerate(*sim.GetRef(i)));
1336 auto expect_anc = sim.graph.Ancestors(i);
1337 auto anc = sim.MakeSet(real->GetAncestors(*sim.GetRef(i), level));
1338 assert(anc.Count() <= max_cluster_count);
1339 assert(anc == expect_anc);
1341 auto expect_desc = sim.graph.Descendants(i);
1342 auto desc = sim.MakeSet(real->GetDescendants(*sim.GetRef(i), level));
1343 assert(desc.Count() <= max_cluster_count);
1344 assert(desc == expect_desc);
1346 auto cluster = real->GetCluster(*sim.GetRef(i), level);
1347 assert(cluster.size() <= max_cluster_count);
1348 assert(sim.MakeSet(cluster) == component);
1351 std::vector<DepGraphIndex> simlin;
1352 SimTxGraph::SetType done;
1353 uint64_t total_size{0};
1355 auto simpos = sim.Find(ptr);
1356 assert(sim.graph.Descendants(simpos).IsSubsetOf(component - done));
1358 assert(sim.graph.Ancestors(simpos).IsSubsetOf(done));
1359 simlin.push_back(simpos);
1360 total_size += sim.graph.FeeRate(simpos).size;
1363 assert(total_size <= max_cluster_size);
1369 for (
auto& chunk : simlinchunk) {
1372 assert(sim.graph.IsConnected(chunk.transactions));
1374 while (chunk.transactions.Any()) {
1375 assert(chunk.transactions[simlin[idx]]);
1376 chunk.transactions.Reset(simlin[idx]);
1377 assert(chunk.feerate == real->GetMainChunkFeerate(*cluster[idx]));
1388 real->SanityCheck();
1391 block_builders.clear();
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 ...
T ConsumeIntegralInRange(T min, T max)
constexpr uint64_t rand64() noexcept
I randrange(I range) noexcept
Generate a random integer in the range [0..range), with range > 0.
Data structure to encapsulate fees, sizes, and dependencies for a set of transactions.
@ MAIN
Always refers to the main graph, whether staging is present or not.
@ TOP
Refers to staging if it exists, main otherwise.
virtual void AddTransaction(Ref &arg, const FeePerWeight &feerate) noexcept=0
Initialize arg (which must be an empty Ref) to refer to a new transaction in this graph with the spec...
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.
const FeeFrac & FeeRate(DepGraphIndex i) const noexcept
Get the feerate of a given transaction i.
SetType GetConnectedComponent(const SetType &todo, DepGraphIndex tx) const noexcept
Get the connected component within the subset "todo" that contains tx (which must be in todo).
SetType FindConnectedComponent(const SetType &todo) const noexcept
Find some connected component within the subset "todo" of this graph.
DepGraphIndex AddTransaction(const FeeFrac &feefrac) noexcept
Add a new unconnected transaction to this transaction graph (in the first available position),...
void RemoveTransactions(const SetType &del) noexcept
Remove the specified positions from this DepGraph.
auto TxCount() const noexcept
Get the number of transactions in the graph.
const SetType & Descendants(DepGraphIndex i) const noexcept
Get the descendants of a given transaction i.
const SetType & Positions() const noexcept
Get the set of transactions positions in use.
void AddDependencies(const SetType &parents, DepGraphIndex child) noexcept
Modify this transaction graph, adding multiple parents to a specified child.
constexpr MaybeCoin MISSING
#define LIMITED_WHILE(condition, limit)
Can be used to limit a theoretically unbounded loop.
Value Find(const std::map< Key, Value > &map, const Key &key)
std::vector< FeeFrac > ChunkLinearization(const DepGraph< SetType > &depgraph, std::span< const DepGraphIndex > linearization) noexcept
Compute the feerates of the chunks of linearization.
std::tuple< std::vector< DepGraphIndex >, bool, uint64_t > Linearize(const DepGraph< SetType > &depgraph, uint64_t max_cost, uint64_t rng_seed, const StrongComparator< DepGraphIndex > auto &fallback_order, std::span< const DepGraphIndex > old_linearization={}, bool is_topological=true) noexcept
Find or improve a linearization for a cluster.
uint32_t DepGraphIndex
Data type to represent transaction indices in DepGraphs and the clusters they represent.
void PostLinearize(const DepGraph< SetType > &depgraph, std::span< DepGraphIndex > linearization)
Improve a given linearization.
std::vector< SetInfo< SetType > > ChunkLinearizationInfo(const DepGraph< SetType > &depgraph, std::span< const DepGraphIndex > linearization) noexcept
Compute the chunks of linearization as SetInfos.
Data structure storing a fee and size.
bool IsEmpty() const noexcept
Check if this is empty (size and fee are 0).
Tagged wrapper around FeeFrac to avoid unit confusion.
static FeePerUnit FromFeeFrac(const FeeFrac &feefrac) noexcept
Convert a FeeFrac to a FeePerUnit.
void SeedRandomStateForTest(SeedRand seedtype)
Seed the global RNG state for testing and log the seed value.
@ ZEROS
Seed with a compile time constant of zeros.
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
std::partial_ordering CompareChunks(std::span< const FeeFrac > chunks0, std::span< const FeeFrac > chunks1)
Compare the feerate diagrams implied by the provided sorted chunks data.