29 const uint64_t m_txid{0};
30 SimTxObject() noexcept = default;
31 explicit SimTxObject(uint64_t txid) noexcept : m_txid(txid) {}
48 static constexpr auto MISSING = Pos(-1);
56 std::array<std::shared_ptr<SimTxObject>, MAX_TRANSACTIONS> simmap;
58 std::map<const TxGraph::Ref*, Pos> simrevmap;
60 std::vector<std::shared_ptr<SimTxObject>> removed;
62 std::optional<bool> oversized;
69 uint64_t max_cluster_size;
71 bool real_is_optimal{
false};
74 explicit SimTxGraph(
DepGraphIndex cluster_count, uint64_t cluster_size) :
75 max_cluster_count(cluster_count), max_cluster_size(cluster_size) {}
78 SimTxGraph(
const SimTxGraph&)
noexcept =
default;
79 SimTxGraph& operator=(
const SimTxGraph&)
noexcept =
default;
80 SimTxGraph(SimTxGraph&&) noexcept = default;
81 SimTxGraph& operator=(SimTxGraph&&) noexcept = default;
84 std::vector<SetType> GetComponents()
87 std::vector<SetType>
ret;
91 ret.push_back(component);
101 if (!oversized.has_value()) {
104 for (
auto component : GetComponents()) {
105 if (component.Count() > max_cluster_count) oversized =
true;
106 uint64_t component_size{0};
107 for (
auto i : component) component_size += graph.
FeeRate(i).
size;
108 if (component_size > max_cluster_size) oversized =
true;
135 auto it = simrevmap.find(ref);
136 if (it != simrevmap.end())
return it->second;
141 SimTxObject* GetRef(Pos pos)
145 return simmap[pos].get();
153 real_is_optimal =
false;
154 MakeModified(simpos);
156 simmap[simpos] = std::make_shared<SimTxObject>(txid);
158 auto ptr = simmap[simpos].get();
159 simrevmap[ptr] = simpos;
161 if (oversized.has_value() && !*oversized) oversized = std::nullopt;
167 auto par_pos =
Find(parent);
168 if (par_pos ==
MISSING)
return;
169 auto chl_pos =
Find(child);
170 if (chl_pos ==
MISSING)
return;
172 MakeModified(par_pos);
173 real_is_optimal =
false;
175 if (oversized.has_value() && !*oversized) oversized = std::nullopt;
181 auto pos =
Find(ref);
184 real_is_optimal =
false;
191 auto pos =
Find(ref);
194 real_is_optimal =
false;
196 simrevmap.erase(simmap[pos].get());
199 removed.push_back(std::move(simmap[pos]));
202 if (oversized.has_value() && *oversized) oversized = std::nullopt;
209 void DestroyTransaction(
TxGraph::Ref* ref,
bool reset_oversize)
211 auto pos =
Find(ref);
216 auto remove = std::partition(removed.begin(), removed.end(), [&](
auto& arg) { return arg.get() != ref; });
217 removed.erase(remove, removed.end());
221 real_is_optimal =
false;
222 simrevmap.erase(simmap[pos].get());
225 if (reset_oversize && oversized.has_value() && *oversized) {
226 oversized = std::nullopt;
233 SetType MakeSet(std::span<TxGraph::Ref* const> arg)
237 auto pos =
Find(ptr);
247 auto pos =
Find(arg);
254 void IncludeAncDesc(std::vector<TxGraph::Ref*>& arg,
bool desc)
256 std::vector<TxGraph::Ref*>
ret;
257 for (
auto ptr : arg) {
258 auto simpos =
Find(ptr);
261 ret.push_back(simmap[i].get());
270 for (
auto ptr :
ret) {
271 if (std::find(arg.begin(), arg.end(), ptr) == arg.end()) {
280 bool MatchesOversizedClusters(
const SetType& set)
282 if (set.Any() && !IsOversized())
return false;
285 if (!set.IsSubsetOf(todo))
return false;
291 bool is_oversized = component.Count() > max_cluster_count;
292 uint64_t component_size{0};
293 for (
auto i : component) component_size += graph.
FeeRate(i).
size;
294 is_oversized |= component_size > max_cluster_size;
296 if (is_oversized != set.Overlaps(component))
return false;
321 SimTxObject empty_ref;
332 std::set<uint64_t> assigned_txids;
336 uint64_t txid_a =
static_cast<const SimTxObject&
>(a).m_txid;
337 uint64_t txid_b =
static_cast<const SimTxObject&
>(b).m_txid;
338 assert(assigned_txids.contains(txid_a));
339 assert(assigned_txids.contains(txid_b));
340 return txid_a <=> txid_b;
348 std::vector<SimTxGraph> sims;
350 sims.emplace_back(max_cluster_count, max_cluster_size);
353 struct BlockBuilderData
356 std::unique_ptr<TxGraph::BlockBuilder> builder;
358 SimTxGraph::SetType included;
360 SimTxGraph::SetType done;
364 BlockBuilderData(std::unique_ptr<TxGraph::BlockBuilder> builder_in) : builder(std::move(builder_in)) {}
368 std::vector<BlockBuilderData> block_builders;
372 auto pick_fn = [&]()
noexcept -> SimTxObject* {
373 size_t tx_count[2] = {sims[0].GetTransactionCount(), 0};
375 size_t choices = tx_count[0] + sims[0].removed.size() + 1;
376 if (sims.size() == 2) {
377 tx_count[1] = sims[1].GetTransactionCount();
378 choices += tx_count[1] + sims[1].removed.size();
383 for (
size_t level = 0; level < sims.size(); ++level) {
384 auto& sim = sims[level];
385 if (choice < tx_count[level]) {
387 for (
auto i : sim.graph.Positions()) {
388 if (choice == 0)
return sim.GetRef(i);
393 choice -= tx_count[level];
395 if (choice < sim.removed.size()) {
397 return sim.removed[choice].get();
399 choice -= sim.removed.size();
409 auto get_diagram_fn = [&](
TxGraph::Level level_select) -> std::vector<FeeFrac> {
411 auto& sim = sims[level];
413 std::set<std::vector<TxGraph::Ref*>> clusters;
414 for (
auto i : sim.graph.Positions()) {
415 auto ref = sim.GetRef(i);
416 clusters.insert(real->GetCluster(*ref, level_select));
420 std::vector<FeeFrac> chunk_feerates;
421 for (
const auto& cluster : clusters) {
422 num_tx += cluster.size();
423 std::vector<SimTxGraph::Pos> linearization;
424 linearization.reserve(cluster.size());
425 for (
auto refptr : cluster) linearization.push_back(sim.Find(refptr));
427 chunk_feerates.push_back(chunk_feerate);
433 assert(num_tx == sim.GetTransactionCount());
436 std::sort(chunk_feerates.begin(), chunk_feerates.end(), std::greater{});
437 return chunk_feerates;
455 int builder_idx = block_builders.empty() ? -1 : int((orig_command & 3) % block_builders.size());
460 auto& top_sim = sims.back();
462 auto& main_sim = sims[0];
467 if ((block_builders.empty() || sims.size() > 1) && top_sim.GetTransactionCount() < SimTxGraph::MAX_TRANSACTIONS &&
command-- == 0) {
486 }
while (txid == 0 || assigned_txids.contains(txid));
487 assigned_txids.insert(txid);
489 top_sim.AddTransaction(*real, feerate, txid);
491 }
else if ((block_builders.empty() || sims.size() > 1) && top_sim.GetTransactionCount() + top_sim.removed.size() > 1 &&
command-- == 0) {
493 auto par = pick_fn();
494 auto chl = pick_fn();
495 auto pos_par = top_sim.Find(par);
496 auto pos_chl = top_sim.Find(chl);
500 if (top_sim.graph.Ancestors(pos_par)[pos_chl])
break;
502 top_sim.AddDependency(par, chl);
503 top_sim.real_is_optimal =
false;
504 real->AddDependency(*par, *chl);
506 }
else if ((block_builders.empty() || sims.size() > 1) && top_sim.removed.size() < 100 &&
command-- == 0) {
510 std::vector<TxGraph::Ref*> to_remove;
511 to_remove.push_back(pick_fn());
512 top_sim.IncludeAncDesc(to_remove, alt);
515 std::shuffle(to_remove.begin(), to_remove.end(), rng);
517 real->RemoveTransaction(*ptr);
518 top_sim.RemoveTransaction(ptr);
521 }
else if (sel_sim.removed.size() > 0 &&
command-- == 0) {
531 if (removed_pos != sel_sim.removed.size() - 1) {
532 std::swap(sel_sim.removed[removed_pos], sel_sim.removed.back());
534 sel_sim.removed.pop_back();
536 }
else if (block_builders.empty() &&
command-- == 0) {
538 std::vector<TxGraph::Ref*> to_destroy;
539 to_destroy.push_back(pick_fn());
545 auto old_size = to_destroy.size();
546 for (
auto& sim : sims) sim.IncludeAncDesc(to_destroy, alt);
547 if (to_destroy.size() == old_size)
break;
551 std::shuffle(to_destroy.begin(), to_destroy.end(), rng);
553 for (
size_t level = 0; level < sims.size(); ++level) {
554 sims[level].DestroyTransaction(ptr, level == sims.size() - 1);
558 }
else if (block_builders.empty() &&
command-- == 0) {
566 auto ref = pick_fn();
567 real->SetTransactionFee(*ref,
fee);
568 for (
auto& sim : sims) {
569 sim.SetTransactionFee(ref,
fee);
574 assert(real->GetTransactionCount(level_select) == sel_sim.GetTransactionCount());
578 auto ref = pick_fn();
579 bool exists = real->Exists(*ref, level_select);
581 assert(exists == should_exist);
585 assert(sel_sim.IsOversized() == real->IsOversized(level_select));
589 auto ref = pick_fn();
590 auto feerate = real->GetIndividualFeerate(*ref);
592 for (
auto& sim : sims) {
593 auto simpos = sim.Find(ref);
596 assert(feerate == sim.graph.FeeRate(simpos));
601 }
else if (!main_sim.IsOversized() &&
command-- == 0) {
603 auto ref = pick_fn();
604 auto feerate = real->GetMainChunkFeerate(*ref);
605 auto simpos = main_sim.Find(ref);
611 assert(feerate.
size >= main_sim.graph.FeeRate(simpos).size);
612 assert(feerate.
size <= main_sim.SumAll().size);
615 }
else if (!sel_sim.IsOversized() &&
command-- == 0) {
617 auto ref = pick_fn();
618 auto result = alt ? real->GetDescendants(*ref, level_select)
619 : real->GetAncestors(*ref, level_select);
620 assert(result.size() <= max_cluster_count);
621 auto result_set = sel_sim.MakeSet(result);
622 assert(result.size() == result_set.Count());
623 auto expect_set = sel_sim.GetAncDesc(ref, alt);
624 assert(result_set == expect_set);
626 }
else if (!sel_sim.IsOversized() &&
command-- == 0) {
628 std::vector<TxGraph::Ref*> refs;
632 for (
size_t i = 0; i <
count; ++i) {
636 std::shuffle(refs.begin(), refs.end(), rng);
638 auto result = alt ? real->GetDescendantsUnion(refs, level_select)
639 : real->GetAncestorsUnion(refs, level_select);
640 auto result_set = sel_sim.MakeSet(result);
641 assert(result.size() == result_set.Count());
643 SimTxGraph::SetType expect_set;
644 for (
TxGraph::Ref* ref : refs) expect_set |= sel_sim.GetAncDesc(ref, alt);
646 assert(result_set == expect_set);
648 }
else if (!sel_sim.IsOversized() &&
command-- == 0) {
650 auto ref = pick_fn();
651 auto result = real->GetCluster(*ref, level_select);
653 assert(result.size() <= max_cluster_count);
655 auto left = sel_sim.graph.Positions();
656 uint64_t total_size{0};
657 for (
auto refptr : result) {
658 auto simpos = sel_sim.Find(refptr);
659 total_size += sel_sim.graph.FeeRate(simpos).size;
663 assert(!sel_sim.graph.Ancestors(simpos).Overlaps(left));
666 assert(total_size <= max_cluster_size);
668 auto result_set = sel_sim.MakeSet(result);
669 assert(sel_sim.graph.IsConnected(result_set));
671 auto simpos = sel_sim.Find(ref);
673 assert(result_set[simpos]);
675 assert(result_set.None());
678 for (
auto i : result_set) {
679 assert(sel_sim.graph.Ancestors(i).IsSubsetOf(result_set));
680 assert(sel_sim.graph.Descendants(i).IsSubsetOf(result_set));
685 assert((sims.size() == 2) == real->HaveStaging());
687 }
else if (sims.size() < 2 &&
command-- == 0) {
689 sims.emplace_back(sims.back());
690 sims.back().modified = SimTxGraph::SetType{};
691 real->StartStaging();
693 }
else if (block_builders.empty() && sims.size() > 1 &&
command-- == 0) {
695 real->CommitStaging();
697 const bool main_optimal = std::all_of(sims.cbegin(), sims.cend(), [](
const auto &sim) { return sim.real_is_optimal; });
698 sims.erase(sims.begin());
699 sims.front().real_is_optimal = main_optimal;
701 }
else if (sims.size() > 1 &&
command-- == 0) {
703 real->AbortStaging();
708 sims.back().oversized = std::nullopt;
710 }
else if (!main_sim.IsOversized() &&
command-- == 0) {
712 auto ref_a = pick_fn();
713 auto ref_b = pick_fn();
714 auto sim_a = main_sim.Find(ref_a);
715 auto sim_b = main_sim.Find(ref_b);
718 auto cmp = real->CompareMainOrder(*ref_a, *ref_b);
720 if (sim_a != sim_b)
assert(cmp != 0);
722 if (main_sim.graph.Ancestors(sim_a)[sim_b])
assert(cmp >= 0);
723 if (main_sim.graph.Descendants(sim_a)[sim_b])
assert(cmp <= 0);
728 }
else if (!sel_sim.IsOversized() &&
command-- == 0) {
730 std::vector<TxGraph::Ref*> refs;
734 for (
size_t i = 0; i <
count; ++i) {
738 std::shuffle(refs.begin(), refs.end(), rng);
740 auto result = real->CountDistinctClusters(refs, level_select);
744 SimTxGraph::SetType sim_reps;
745 for (
auto ref : refs) {
747 auto simpos = sel_sim.Find(ref);
750 auto component = sel_sim.graph.GetConnectedComponent(sel_sim.graph.Positions(), simpos);
753 sim_reps.Set(component.First());
757 assert(result == sim_reps.Count());
762 bool ret = real->DoWork(max_cost);
763 uint64_t cost_for_optimal{0};
764 for (
unsigned level = 0; level < sims.size(); ++level) {
769 if (sims[level].IsOversized())
continue;
770 if (level == 0 && !block_builders.empty())
continue;
774 sims[level].real_is_optimal =
true;
777 for (
auto component : sims[level].GetComponents()) {
778 auto cost_opt_this_cluster = MaxOptimalLinearizationCost(component.Count());
779 if (cost_opt_this_cluster > acceptable_cost) {
783 cost_for_optimal += cost_opt_this_cluster + acceptable_cost;
785 cost_for_optimal += cost_opt_this_cluster;
793 assert(max_cost <= cost_for_optimal);
796 }
else if (sims.size() == 2 && !sims[0].IsOversized() && !sims[1].IsOversized() &&
command-- == 0) {
798 auto [real_main_diagram, real_staged_diagram] = real->GetMainStagingDiagrams();
799 auto real_sum_main = std::accumulate(real_main_diagram.begin(), real_main_diagram.end(),
FeeFrac{});
800 auto real_sum_staged = std::accumulate(real_staged_diagram.begin(), real_staged_diagram.end(),
FeeFrac{});
801 auto real_gain = real_sum_staged - real_sum_main;
802 auto sim_gain = sims[1].SumAll() - sims[0].SumAll();
806 assert(sim_gain == real_gain);
808 for (
size_t i = 1; i < real_main_diagram.size(); ++i) {
809 assert(FeeRateCompare(real_main_diagram[i], real_main_diagram[i - 1]) <= 0);
811 for (
size_t i = 1; i < real_staged_diagram.size(); ++i) {
812 assert(FeeRateCompare(real_staged_diagram[i], real_staged_diagram[i - 1]) <= 0);
815 }
else if (block_builders.size() < 4 && !main_sim.IsOversized() &&
command-- == 0) {
817 block_builders.emplace_back(real->GetBlockBuilder());
819 }
else if (!block_builders.empty() &&
command-- == 0) {
821 block_builders.erase(block_builders.begin() + builder_idx);
823 }
else if (!block_builders.empty() &&
command-- == 0) {
825 auto& builder_data = block_builders[builder_idx];
826 auto new_included = builder_data.included;
827 auto new_done = builder_data.done;
828 auto chunk = builder_data.builder->GetCurrentChunk();
831 if (!builder_data.last_feerate.IsEmpty()) {
832 assert(!(chunk->second >> builder_data.last_feerate));
834 builder_data.last_feerate = chunk->second;
839 auto simpos = main_sim.Find(ref);
842 sum_feerate += main_sim.graph.FeeRate(simpos);
844 assert(!new_done[simpos]);
845 new_done.Set(simpos);
847 new_included.Set(simpos);
848 assert(main_sim.graph.Ancestors(simpos).IsSubsetOf(new_included));
850 assert(sum_feerate == chunk->second);
854 if (builder_data.done == builder_data.included) {
855 assert(builder_data.done.Count() == main_sim.GetTransactionCount());
859 if ((orig_command % 7) >= 5) {
860 auto chunk2 = builder_data.builder->GetCurrentChunk();
864 if ((orig_command % 5) >= 3) {
866 builder_data.builder->Skip();
869 builder_data.builder->Include();
870 builder_data.included = new_included;
872 builder_data.done = new_done;
874 }
else if (!main_sim.IsOversized() &&
command-- == 0) {
876 auto [worst_chunk, worst_chunk_feerate] = real->GetWorstMainChunk();
879 if (main_sim.GetTransactionCount() == 0) {
880 assert(worst_chunk.empty());
881 assert(worst_chunk_feerate.IsEmpty());
883 assert(!worst_chunk.empty());
884 SimTxGraph::SetType done;
888 auto simpos = main_sim.Find(ref);
890 sum += main_sim.graph.FeeRate(simpos);
895 assert(main_sim.graph.Descendants(simpos).IsSubsetOf(done));
900 }
else if ((block_builders.empty() || sims.size() > 1) &&
command-- == 0) {
902 bool was_oversized = top_sim.IsOversized();
903 auto removed = real->Trim();
905 assert(was_oversized == !removed.empty());
906 if (!was_oversized)
break;
907 auto removed_set = top_sim.MakeSet(removed);
909 for (
auto simpos : removed_set) {
910 assert(top_sim.graph.Descendants(simpos).IsSubsetOf(removed_set));
914 assert(top_sim.MatchesOversizedClusters(removed_set));
919 for (
auto simpos : removed_set) {
920 top_sim.RemoveTransaction(top_sim.GetRef(simpos));
922 assert(!top_sim.IsOversized());
924 }
else if ((block_builders.empty() || sims.size() > 1) &&
925 top_sim.GetTransactionCount() > max_cluster_count && !top_sim.IsOversized() &&
command-- == 0) {
936 auto clusters = top_sim.GetComponents();
939 bool made_oversized =
false;
940 auto merges_left = clusters.size() - 1;
941 while (merges_left > 0) {
944 auto par_cl = rng.
randrange(clusters.size());
945 auto chl_cl = rng.
randrange(clusters.size() - 1);
946 chl_cl += (chl_cl >= par_cl);
953 for (
int i = 0; i < num_deps; ++i) {
955 auto par_idx = rng.
randrange(clusters[par_cl].Count());
956 SimTxGraph::Pos par_pos = 0;
957 for (
auto j : clusters[par_cl]) {
965 auto chl_idx = rng.
randrange(clusters[chl_cl].Count());
966 SimTxGraph::Pos chl_pos = 0;
967 for (
auto j : clusters[chl_cl]) {
975 auto par_ref = top_sim.GetRef(par_pos);
976 auto chl_ref = top_sim.GetRef(chl_pos);
977 top_sim.AddDependency(par_ref, chl_ref);
978 real->AddDependency(*par_ref, *chl_ref);
981 auto par_cluster = clusters[par_cl];
982 auto chl_cluster = clusters[chl_cl];
983 auto new_cluster = par_cluster | chl_cluster;
985 std::erase_if(clusters, [&](
const auto& cl)
noexcept {
return cl == par_cluster || cl == chl_cluster; });
987 clusters.push_back(new_cluster);
990 if (!made_oversized) {
991 made_oversized = new_cluster.Count() > max_cluster_count;
992 if (!made_oversized) {
994 for (
auto i : new_cluster) total += top_sim.graph.FeeRate(i);
995 if (uint32_t(total.
size) > max_cluster_size) made_oversized =
true;
997 if (made_oversized) merges_left = rng.
randrange(clusters.size());
1002 uint32_t max_removed = 0;
1003 for (
auto& cluster : clusters) {
1005 std::vector<uint32_t> sizes;
1006 for (
auto i : cluster) sizes.push_back(top_sim.graph.FeeRate(i).size);
1007 auto sum_sizes = std::accumulate(sizes.begin(), sizes.end(), uint64_t{0});
1009 std::sort(sizes.begin(), sizes.end(), std::greater{});
1011 while (sizes.size() > max_cluster_count || sum_sizes > max_cluster_size) {
1012 sum_sizes -= sizes.back();
1019 auto removed = real->Trim();
1021 assert(removed.size() >= 1);
1022 assert(removed.size() <= max_removed);
1024 auto removed_set = top_sim.MakeSet(removed);
1025 for (
auto simpos : removed_set) {
1026 assert(top_sim.graph.Descendants(simpos).IsSubsetOf(removed_set));
1030 assert(top_sim.MatchesOversizedClusters(removed_set));
1035 for (
auto simpos : removed_set) {
1036 top_sim.RemoveTransaction(top_sim.GetRef(simpos));
1038 assert(!top_sim.IsOversized());
1042 auto usage = real->GetMainMemoryUsage();
1045 auto usage2 = real->GetMainMemoryUsage();
1049 if (main_sim.GetTransactionCount() == 0) {
1061 real->SanityCheck();
1063 if (!sims[0].IsOversized()) {
1067 std::vector<SimTxGraph::Pos> vec1;
1068 for (
auto i : sims[0].graph.
Positions()) vec1.push_back(i);
1069 std::shuffle(vec1.begin(), vec1.end(), rng);
1071 std::shuffle(vec2.begin(), vec2.end(), rng);
1072 if (vec1 == vec2) std::next_permutation(vec2.begin(), vec2.end());
1075 auto cmp = [&](SimTxGraph::Pos a, SimTxGraph::Pos b)
noexcept {
1076 return real->CompareMainOrder(*sims[0].GetRef(a), *sims[0].GetRef(b)) < 0;
1078 std::sort(vec1.begin(), vec1.end(), cmp);
1079 std::sort(vec2.begin(), vec2.end(), cmp);
1086 auto todo = sims[0].graph.Positions();
1087 for (
auto i : vec1) {
1095 if (sims[0].real_is_optimal) {
1098 auto txid_a = sims[0].GetRef(a)->m_txid;
1099 auto txid_b = sims[0].GetRef(b)->m_txid;
1100 return txid_a <=> txid_b;
1102 auto [sim_lin, sim_optimal, _cost] =
Linearize(sims[0].graph, 300000, rng.
rand64(), fallback_order_sim, vec1);
1114 std::map<SimTxGraph::Pos, int32_t> comp_prefix_sizes;
1118 std::pair<int32_t, uint64_t> max_chunk_tiebreak{0, 0};
1119 for (
const auto& chunk : real_chunking) {
1121 if (chunk.feerate << last_chunk_feerate) {
1122 comp_prefix_sizes.clear();
1123 max_chunk_tiebreak = {0, 0};
1125 last_chunk_feerate = chunk.feerate;
1127 auto component = sims[0].graph.GetConnectedComponent(sims[0].graph.Positions(), chunk.transactions.First());
1128 assert(chunk.transactions.IsSubsetOf(component));
1129 auto comp_key = component.First();
1130 auto& comp_prefix_size = comp_prefix_sizes[comp_key];
1131 comp_prefix_size += chunk.feerate.size;
1133 uint64_t chunk_max_txid{0};
1134 for (
auto tx : chunk.transactions) {
1135 auto txid = sims[0].GetRef(tx)->m_txid;
1136 chunk_max_txid = std::max(txid, chunk_max_txid);
1140 std::pair<int32_t, uint64_t> chunk_tiebreak{comp_prefix_size, chunk_max_txid};
1141 assert(chunk_tiebreak > max_chunk_tiebreak);
1142 max_chunk_tiebreak = chunk_tiebreak;
1149 for (
const auto& component : sims[0].GetComponents()) {
1150 std::vector<DepGraphIndex> sim_chunk_lin, real_chunk_lin;
1151 for (
auto i : sim_lin) {
1152 if (component[i]) sim_chunk_lin.push_back(i);
1154 for (
auto i : vec1) {
1155 if (component[i]) real_chunk_lin.push_back(i);
1157 assert(sim_chunk_lin == real_chunk_lin);
1171 std::vector<std::optional<SimTxObject>> txobjects_redo;
1172 txobjects_redo.resize(sims[0].graph.PositionRange());
1174 std::vector<DepGraphIndex> positions;
1175 for (
auto i : sims[0].graph.Positions()) positions.push_back(i);
1176 std::shuffle(positions.begin(), positions.end(), rng);
1177 for (
auto i : positions) {
1178 txobjects_redo[i].emplace(sims[0].GetRef(i)->m_txid);
1182 std::vector<std::pair<DepGraphIndex, DepGraphIndex>> deps;
1183 for (
auto i : sims[0].graph.Positions()) {
1184 for (
auto j : sims[0].graph.GetReducedParents(i)) {
1185 deps.emplace_back(j, i);
1188 std::shuffle(deps.begin(), deps.end(), rng);
1189 for (
auto [parent, child] : deps) {
1190 real_redo->AddDependency(*txobjects_redo[parent], *txobjects_redo[child]);
1193 if (real_redo->DoWork(300000)) {
1195 auto vec_redo = vec1;
1196 std::shuffle(vec_redo.begin(), vec_redo.end(), rng);
1197 if (vec_redo == vec1) std::next_permutation(vec_redo.begin(), vec_redo.end());
1199 auto cmp_redo = [&](SimTxGraph::Pos a, SimTxGraph::Pos b)
noexcept {
1200 return real_redo->CompareMainOrder(*txobjects_redo[a], *txobjects_redo[b]) < 0;
1202 std::sort(vec_redo.begin(), vec_redo.end(), cmp_redo);
1204 assert(vec1 == vec_redo);
1210 for (
size_t pos = 0; pos < vec1.size(); ++pos) {
1211 auto pos_feerate = real->GetMainChunkFeerate(*sims[0].GetRef(vec1[pos]));
1213 size_t before = rng.
randrange<
size_t>(pos);
1214 auto before_feerate = real->GetMainChunkFeerate(*sims[0].GetRef(vec1[before]));
1215 assert(FeeRateCompare(before_feerate, pos_feerate) >= 0);
1217 if (pos + 1 < vec1.size()) {
1218 size_t after = pos + 1 + rng.
randrange<
size_t>(vec1.size() - 1 - pos);
1219 auto after_feerate = real->GetMainChunkFeerate(*sims[0].GetRef(vec1[after]));
1220 assert(FeeRateCompare(after_feerate, pos_feerate) <= 0);
1226 auto builder = real->GetBlockBuilder();
1227 std::vector<SimTxGraph::Pos> vec_builder;
1228 std::vector<TxGraph::Ref*> last_chunk;
1230 while (
auto chunk = builder->GetCurrentChunk()) {
1235 assert(real->GetMainChunkFeerate(*ref) == chunk->second);
1237 sum += real->GetIndividualFeerate(*ref);
1239 auto simpos = sims[0].Find(ref);
1241 vec_builder.push_back(simpos);
1244 last_chunk = std::move(chunk->first);
1245 last_chunk_feerate = chunk->second;
1248 assert(vec_builder == vec1);
1251 std::reverse(last_chunk.begin(), last_chunk.end());
1252 auto [worst_chunk, worst_chunk_feerate] = real->GetWorstMainChunk();
1253 assert(last_chunk == worst_chunk);
1254 assert(last_chunk_feerate == worst_chunk_feerate);
1262 if (sims.size() >= 2 && !sims[1].IsOversized()) {
1265 auto [main_cmp_diagram, stage_cmp_diagram] = real->GetMainStagingDiagrams();
1267 for (
size_t i = 1; i < main_cmp_diagram.size(); ++i) {
1268 assert(FeeRateCompare(main_cmp_diagram[i], main_cmp_diagram[i - 1]) <= 0);
1270 for (
size_t i = 1; i < stage_cmp_diagram.size(); ++i) {
1271 assert(FeeRateCompare(stage_cmp_diagram[i], stage_cmp_diagram[i - 1]) <= 0);
1277 std::sort(main_cmp_diagram.begin(), main_cmp_diagram.end(), std::greater{});
1278 std::sort(stage_cmp_diagram.begin(), stage_cmp_diagram.end(), std::greater{});
1282 std::vector<FeeFrac> missing_main_cmp;
1283 std::set_difference(main_real_diagram.begin(), main_real_diagram.end(),
1284 main_cmp_diagram.begin(), main_cmp_diagram.end(),
1285 std::inserter(missing_main_cmp, missing_main_cmp.end()),
1287 assert(main_cmp_diagram.size() + missing_main_cmp.size() == main_real_diagram.size());
1290 std::vector<FeeFrac> missing_stage_cmp;
1291 std::set_difference(stage_real_diagram.begin(), stage_real_diagram.end(),
1292 stage_cmp_diagram.begin(), stage_cmp_diagram.end(),
1293 std::inserter(missing_stage_cmp, missing_stage_cmp.end()),
1295 assert(stage_cmp_diagram.size() + missing_stage_cmp.size() == stage_real_diagram.size());
1298 assert(missing_main_cmp == missing_stage_cmp);
1305 for (
const auto& feerate : missing_main_cmp) missing_real += feerate;
1306 FeeFrac missing_expected = sims[1].graph.FeeRate(sims[1].graph.Positions() - sims[1].modified);
1313 assert(real->HaveStaging() == (sims.size() > 1));
1320 assert(real->IsOversized(level) == sim.IsOversized());
1321 assert(real->GetTransactionCount(level) == sim.GetTransactionCount());
1323 if (!sim.IsOversized()) {
1324 auto todo = sim.graph.Positions();
1327 while (todo.Any()) {
1328 auto component = sim.graph.FindConnectedComponent(todo);
1331 for (
auto i : component) {
1333 assert(sim.graph.FeeRate(i) == real->GetIndividualFeerate(*sim.GetRef(i)));
1335 auto expect_anc = sim.graph.Ancestors(i);
1336 auto anc = sim.MakeSet(real->GetAncestors(*sim.GetRef(i), level));
1337 assert(anc.Count() <= max_cluster_count);
1338 assert(anc == expect_anc);
1340 auto expect_desc = sim.graph.Descendants(i);
1341 auto desc = sim.MakeSet(real->GetDescendants(*sim.GetRef(i), level));
1342 assert(desc.Count() <= max_cluster_count);
1343 assert(desc == expect_desc);
1345 auto cluster = real->GetCluster(*sim.GetRef(i), level);
1346 assert(cluster.size() <= max_cluster_count);
1347 assert(sim.MakeSet(cluster) == component);
1350 std::vector<DepGraphIndex> simlin;
1351 SimTxGraph::SetType done;
1352 uint64_t total_size{0};
1354 auto simpos = sim.Find(ptr);
1355 assert(sim.graph.Descendants(simpos).IsSubsetOf(component - done));
1357 assert(sim.graph.Ancestors(simpos).IsSubsetOf(done));
1358 simlin.push_back(simpos);
1359 total_size += sim.graph.FeeRate(simpos).size;
1362 assert(total_size <= max_cluster_size);
1368 for (
auto& chunk : simlinchunk) {
1371 assert(sim.graph.IsConnected(chunk.transactions));
1373 while (chunk.transactions.Any()) {
1374 assert(chunk.transactions[simlin[idx]]);
1375 chunk.transactions.Reset(simlin[idx]);
1376 assert(chunk.feerate == real->GetMainChunkFeerate(*cluster[idx]));
1387 real->SanityCheck();
1390 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,(BITS+std::numeric_limits< size_t >::digits - 1)/std::numeric_limits< size_t >::digits > > > BitSet
#define Assume(val)
Assume is the identity function.
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, ordered by increasing fee/size.
bool IsEmpty() const noexcept
Check if this is empty (size and fee are 0).
Tagged wrapper around FeeFrac to avoid unit confusion.
static FeePerUnit FromFeeFrac(const FeeFrac &feefrac) noexcept
Convert a FeeFrac to a FeePerUnit.
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.