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;
72 bool real_is_optimal{
false};
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;
88 std::vector<SetType>
ret;
102 if (!oversized.has_value()) {
106 if (
component.Count() > max_cluster_count) oversized =
true;
127 for (
auto i : graph.Positions()) {
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;
157 simmap[
simpos] = std::make_shared<SimTxObject>(txid);
159 auto ptr = simmap[
simpos].get();
162 if (oversized.has_value() && !*oversized) oversized = std::nullopt;
169 if (
par_pos == MISSING)
return;
171 if (
chl_pos == MISSING)
return;
174 real_is_optimal =
false;
176 if (oversized.has_value() && !*oversized) oversized = std::nullopt;
182 auto pos =
Find(ref);
183 if (pos == MISSING)
return;
185 real_is_optimal =
false;
192 auto pos =
Find(ref);
193 if (pos == MISSING)
return;
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;
212 auto pos =
Find(ref);
213 if (pos == MISSING) {
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());
227 oversized = std::nullopt;
234 SetType
MakeSet(std::span<TxGraph::Ref* const> arg)
238 auto pos =
Find(ptr);
248 auto pos =
Find(arg);
249 if (pos == MISSING)
return {};
257 std::vector<TxGraph::Ref*>
ret;
258 for (
auto ptr : arg) {
261 for (
auto i : desc ? graph.Descendants(
simpos) : graph.Ancestors(
simpos)) {
262 ret.push_back(simmap[i].get());
271 for (
auto ptr :
ret) {
272 if (std::find(arg.begin(), arg.end(), ptr) == arg.end()) {
283 if (set.Any() && !IsOversized())
return false;
286 if (!set.IsSubsetOf(
todo))
return false;
349 std::vector<SimTxGraph>
sims;
351 sims.emplace_back(max_cluster_count, max_cluster_size);
357 std::unique_ptr<TxGraph::BlockBuilder>
builder;
361 SimTxGraph::SetType done;
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();
384 for (
size_t level = 0; level <
sims.size(); ++level) {
386 if (
choice < tx_count[level]) {
388 for (
auto i :
sim.graph.Positions()) {
394 choice -= tx_count[level];
414 std::set<std::vector<TxGraph::Ref*>>
clusters;
415 for (
auto i :
sim.graph.Positions()) {
416 auto ref =
sim.GetRef(i);
422 for (
const auto& cluster :
clusters) {
498 if (
pos_par != SimTxGraph::MISSING &&
pos_chl != SimTxGraph::MISSING) {
504 top_sim.real_is_optimal =
false;
518 real->RemoveTransaction(*ptr);
519 top_sim.RemoveTransaction(ptr);
554 for (
size_t level = 0; level <
sims.size(); ++level) {
555 sims[level].DestroyTransaction(ptr, level ==
sims.size() - 1);
568 real->SetTransactionFee(*ref,
fee);
570 sim.SetTransactionFee(ref,
fee);
591 auto feerate =
real->GetIndividualFeerate(*ref);
595 if (
simpos != SimTxGraph::MISSING) {
605 auto feerate =
real->GetMainChunkFeerate(*ref);
607 if (
simpos == SimTxGraph::MISSING) {
621 assert(result.size() <= max_cluster_count);
629 std::vector<TxGraph::Ref*>
refs;
633 for (
size_t i = 0; i <
count; ++i) {
654 assert(result.size() <= max_cluster_count);
656 auto left =
sel_sim.graph.Positions();
658 for (
auto refptr : result) {
673 if (
simpos != SimTxGraph::MISSING) {
691 sims.back().modified = SimTxGraph::SetType{};
692 real->StartStaging();
696 real->CommitStaging();
698 const bool main_optimal = std::all_of(
sims.cbegin(),
sims.cend(), [](
const auto &
sim) { return sim.real_is_optimal; });
704 real->AbortStaging();
709 sims.back().oversized = std::nullopt;
718 if (
sim_a == SimTxGraph::MISSING ||
sim_b == SimTxGraph::MISSING)
break;
731 std::vector<TxGraph::Ref*>
refs;
735 for (
size_t i = 0; i <
count; ++i) {
746 for (
auto ref :
refs) {
749 if (
simpos == SimTxGraph::MISSING)
continue;
765 for (
unsigned level = 0; level <
sims.size(); ++level) {
770 if (
sims[level].IsOversized())
continue;
775 sims[level].real_is_optimal =
true;
797 }
else if (
sims.size() == 2 && !
sims[0].IsOversized() && !
sims[1].IsOversized() &&
command-- == 0) {
880 if (
main_sim.GetTransactionCount() == 0) {
885 SimTxGraph::SetType done;
904 auto removed =
real->Trim();
954 for (
int i = 0; i <
num_deps; ++i) {
1006 std::vector<uint32_t>
sizes;
1007 for (
auto i : cluster)
sizes.push_back(
top_sim.graph.FeeRate(i).size);
1010 std::ranges::sort(
sizes, std::greater{});
1012 while (
sizes.size() > max_cluster_count ||
sum_sizes > max_cluster_size) {
1020 auto removed =
real->Trim();
1022 assert(removed.size() >= 1);
1043 auto usage =
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);
1076 auto cmp = [&](SimTxGraph::Pos
a, SimTxGraph::Pos b)
noexcept {
1087 auto todo =
sims[0].graph.Positions();
1088 for (
auto i :
vec1) {
1096 if (
sims[0].real_is_optimal) {
1128 auto component =
sims[0].graph.GetConnectedComponent(
sims[0].graph.Positions(),
chunk.transactions.First());
1135 for (
auto tx :
chunk.transactions) {
1136 auto txid =
sims[0].GetRef(tx)->m_txid;
1155 for (
auto i :
vec1) {
1176 for (
auto i :
sims[0].graph.Positions())
positions.push_back(i);
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);
1200 auto cmp_redo = [&](SimTxGraph::Pos
a, SimTxGraph::Pos b)
noexcept {
1211 for (
size_t pos = 0; pos <
vec1.size(); ++pos) {
1218 if (pos + 1 <
vec1.size()) {
1238 sum +=
real->GetIndividualFeerate(*ref);
1263 if (
sims.size() >= 2 && !
sims[1].IsOversized()) {
1322 assert(
real->GetTransactionCount(level) ==
sim.GetTransactionCount());
1324 if (!
sim.IsOversized()) {
1325 auto todo =
sim.graph.Positions();
1328 while (
todo.Any()) {
1334 assert(
sim.graph.FeeRate(i) ==
real->GetIndividualFeerate(*
sim.GetRef(i)));
1337 auto anc =
sim.MakeSet(
real->GetAncestors(*
sim.GetRef(i), level));
1338 assert(
anc.Count() <= max_cluster_count);
1342 auto desc =
sim.MakeSet(
real->GetDescendants(*
sim.GetRef(i), level));
1343 assert(desc.Count() <= max_cluster_count);
1346 auto cluster =
real->GetCluster(*
sim.GetRef(i), level);
1347 assert(cluster.size() <= max_cluster_count);
1351 std::vector<DepGraphIndex>
simlin;
1352 SimTxGraph::SetType done;
1374 while (
chunk.transactions.Any()) {
1388 real->SanityCheck();
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 ...
uint64_t rand64() noexcept
Generate a random 64-bit integer.
T ConsumeIntegralInRange(T min, T max)
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.
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)
uint64_t MaxOptimalLinearizationCost(DepGraphIndex cluster_count)
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.
SeedRandomStateForTest(SeedRand::ZEROS)
FuzzedDataProvider provider
@ 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,...
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.
constexpr auto Ticks(Dur2 d)
Helper to count the seconds of a duration/time_point.