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;
71 bool real_is_optimal{
false};
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;
87 std::vector<SetType>
ret;
101 if (!oversized.has_value()) {
105 if (
component.Count() > max_cluster_count) oversized =
true;
126 for (
auto i : graph.Positions()) {
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;
156 simmap[
simpos] = std::make_shared<SimTxObject>(txid);
158 auto ptr = simmap[
simpos].get();
161 if (oversized.has_value() && !*oversized) oversized = std::nullopt;
168 if (
par_pos == MISSING)
return;
170 if (
chl_pos == MISSING)
return;
173 real_is_optimal =
false;
175 if (oversized.has_value() && !*oversized) oversized = std::nullopt;
181 auto pos =
Find(ref);
182 if (pos == MISSING)
return;
184 real_is_optimal =
false;
191 auto pos =
Find(ref);
192 if (pos == MISSING)
return;
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;
211 auto pos =
Find(ref);
212 if (pos == MISSING) {
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());
226 oversized = std::nullopt;
233 SetType
MakeSet(std::span<TxGraph::Ref* const>
arg)
237 auto pos =
Find(ptr);
248 if (pos == MISSING)
return {};
256 std::vector<TxGraph::Ref*>
ret;
257 for (
auto ptr :
arg) {
260 for (
auto i : desc ? graph.Descendants(
simpos) : graph.Ancestors(
simpos)) {
261 ret.push_back(simmap[i].get());
270 for (
auto ptr :
ret) {
271 if (std::find(
arg.begin(),
arg.end(), ptr) ==
arg.end()) {
282 if (set.Any() && !IsOversized())
return false;
285 if (!set.IsSubsetOf(
todo))
return false;
348 std::vector<SimTxGraph>
sims;
350 sims.emplace_back(max_cluster_count, max_cluster_size);
356 std::unique_ptr<TxGraph::BlockBuilder>
builder;
360 SimTxGraph::SetType done;
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();
383 for (
size_t level = 0; level <
sims.size(); ++level) {
385 if (
choice < tx_count[level]) {
387 for (
auto i :
sim.graph.Positions()) {
393 choice -= tx_count[level];
413 std::set<std::vector<TxGraph::Ref*>>
clusters;
414 for (
auto i :
sim.graph.Positions()) {
415 auto ref =
sim.GetRef(i);
421 for (
const auto& cluster :
clusters) {
497 if (
pos_par != SimTxGraph::MISSING &&
pos_chl != SimTxGraph::MISSING) {
503 top_sim.real_is_optimal =
false;
517 real->RemoveTransaction(*ptr);
518 top_sim.RemoveTransaction(ptr);
553 for (
size_t level = 0; level <
sims.size(); ++level) {
554 sims[level].DestroyTransaction(ptr, level ==
sims.size() - 1);
567 real->SetTransactionFee(*ref,
fee);
569 sim.SetTransactionFee(ref,
fee);
590 auto feerate =
real->GetIndividualFeerate(*ref);
594 if (
simpos != SimTxGraph::MISSING) {
604 auto feerate =
real->GetMainChunkFeerate(*ref);
606 if (
simpos == SimTxGraph::MISSING) {
620 assert(result.size() <= max_cluster_count);
628 std::vector<TxGraph::Ref*>
refs;
632 for (
size_t i = 0; i <
count; ++i) {
636 std::shuffle(
refs.begin(),
refs.end(), rng);
653 assert(result.size() <= max_cluster_count);
655 auto left =
sel_sim.graph.Positions();
657 for (
auto refptr : result) {
672 if (
simpos != SimTxGraph::MISSING) {
690 sims.back().modified = SimTxGraph::SetType{};
691 real->StartStaging();
695 real->CommitStaging();
697 const bool main_optimal = std::all_of(
sims.cbegin(),
sims.cend(), [](
const auto &
sim) { return sim.real_is_optimal; });
703 real->AbortStaging();
708 sims.back().oversized = std::nullopt;
717 if (
sim_a == SimTxGraph::MISSING ||
sim_b == SimTxGraph::MISSING)
break;
730 std::vector<TxGraph::Ref*>
refs;
734 for (
size_t i = 0; i <
count; ++i) {
738 std::shuffle(
refs.begin(),
refs.end(), rng);
745 for (
auto ref :
refs) {
748 if (
simpos == SimTxGraph::MISSING)
continue;
764 for (
unsigned level = 0; level <
sims.size(); ++level) {
769 if (
sims[level].IsOversized())
continue;
774 sims[level].real_is_optimal =
true;
796 }
else if (
sims.size() == 2 && !
sims[0].IsOversized() && !
sims[1].IsOversized() &&
command-- == 0) {
879 if (
main_sim.GetTransactionCount() == 0) {
884 SimTxGraph::SetType done;
903 auto removed =
real->Trim();
953 for (
int i = 0; i <
num_deps; ++i) {
1005 std::vector<uint32_t>
sizes;
1006 for (
auto i : cluster)
sizes.push_back(
top_sim.graph.FeeRate(i).size);
1009 std::sort(
sizes.begin(),
sizes.end(), std::greater{});
1011 while (
sizes.size() > max_cluster_count ||
sum_sizes > max_cluster_size) {
1019 auto removed =
real->Trim();
1021 assert(removed.size() >= 1);
1042 auto usage =
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);
1075 auto cmp = [&](SimTxGraph::Pos
a, SimTxGraph::Pos b)
noexcept {
1086 auto todo =
sims[0].graph.Positions();
1087 for (
auto i :
vec1) {
1095 if (
sims[0].real_is_optimal) {
1127 auto component =
sims[0].graph.GetConnectedComponent(
sims[0].graph.Positions(),
chunk.transactions.First());
1134 for (
auto tx :
chunk.transactions) {
1135 auto txid =
sims[0].GetRef(tx)->m_txid;
1154 for (
auto i :
vec1) {
1175 for (
auto i :
sims[0].graph.Positions())
positions.push_back(i);
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);
1199 auto cmp_redo = [&](SimTxGraph::Pos
a, SimTxGraph::Pos b)
noexcept {
1210 for (
size_t pos = 0; pos <
vec1.size(); ++pos) {
1217 if (pos + 1 <
vec1.size()) {
1237 sum +=
real->GetIndividualFeerate(*ref);
1262 if (
sims.size() >= 2 && !
sims[1].IsOversized()) {
1321 assert(
real->GetTransactionCount(level) ==
sim.GetTransactionCount());
1323 if (!
sim.IsOversized()) {
1324 auto todo =
sim.graph.Positions();
1327 while (
todo.Any()) {
1333 assert(
sim.graph.FeeRate(i) ==
real->GetIndividualFeerate(*
sim.GetRef(i)));
1336 auto anc =
sim.MakeSet(
real->GetAncestors(*
sim.GetRef(i), level));
1337 assert(
anc.Count() <= max_cluster_count);
1341 auto desc =
sim.MakeSet(
real->GetDescendants(*
sim.GetRef(i), level));
1342 assert(desc.Count() <= max_cluster_count);
1345 auto cluster =
real->GetCluster(*
sim.GetRef(i), level);
1346 assert(cluster.size() <= max_cluster_count);
1350 std::vector<DepGraphIndex>
simlin;
1351 SimTxGraph::SetType done;
1373 while (
chunk.transactions.Any()) {
1387 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,(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.
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)
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.
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.
constexpr auto Ticks(Dur2 d)
Helper to count the seconds of a duration/time_point.