diff options
author | Pieter Wuille <pieter@wuille.net> | 2024-01-29 22:05:53 -0500 |
---|---|---|
committer | Pieter Wuille <pieter@wuille.net> | 2024-07-25 10:16:37 -0400 |
commit | a6e07e769a1af652a14e533f6d3558ccdefb1de5 (patch) | |
tree | fcf17ad39faf8d5bf1dd550f233810aea210ffbc /src | |
parent | 5d280130446d57d653c749005a2e363265d87686 (diff) |
clusterlin: introduce cluster_linearize.h with Cluster and DepGraph types
This primarily adds the DepGraph class, which encapsulates precomputed
ancestor/descendant information for a given transaction cluster, with a
number of utility features (inspectors for set feerates, computing
reduced parents/children, adding transactions, adding dependencies), which
will become needed in future commits.
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 1 | ||||
-rw-r--r-- | src/cluster_linearize.h | 171 |
2 files changed, 172 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 72dd942c40..36de5dd150 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -132,6 +132,7 @@ BITCOIN_CORE_H = \ chainparamsseeds.h \ checkqueue.h \ clientversion.h \ + cluster_linearize.h \ coins.h \ common/args.h \ common/bloom.h \ diff --git a/src/cluster_linearize.h b/src/cluster_linearize.h new file mode 100644 index 0000000000..2e230bcd63 --- /dev/null +++ b/src/cluster_linearize.h @@ -0,0 +1,171 @@ +// Copyright (c) The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_CLUSTER_LINEARIZE_H +#define BITCOIN_CLUSTER_LINEARIZE_H + +#include <stdint.h> +#include <vector> +#include <utility> + +#include <util/feefrac.h> + +namespace cluster_linearize { + +/** Data type to represent cluster input. + * + * cluster[i].first is tx_i's fee and size. + * cluster[i].second[j] is true iff tx_i spends one or more of tx_j's outputs. + */ +template<typename SetType> +using Cluster = std::vector<std::pair<FeeFrac, SetType>>; + +/** Data type to represent transaction indices in clusters. */ +using ClusterIndex = uint32_t; + +/** Data structure that holds a transaction graph's preprocessed data (fee, size, ancestors, + * descendants). */ +template<typename SetType> +class DepGraph +{ + /** Information about a single transaction. */ + struct Entry + { + /** Fee and size of transaction itself. */ + FeeFrac feerate; + /** All ancestors of the transaction (including itself). */ + SetType ancestors; + /** All descendants of the transaction (including itself). */ + SetType descendants; + + /** Equality operator (primarily for for testing purposes). */ + friend bool operator==(const Entry&, const Entry&) noexcept = default; + + /** Construct an empty entry. */ + Entry() noexcept = default; + /** Construct an entry with a given feerate, ancestor set, descendant set. */ + Entry(const FeeFrac& f, const SetType& a, const SetType& d) noexcept : feerate(f), ancestors(a), descendants(d) {} + }; + + /** Data for each transaction, in the same order as the Cluster it was constructed from. */ + std::vector<Entry> entries; + +public: + /** Equality operator (primarily for testing purposes). */ + friend bool operator==(const DepGraph&, const DepGraph&) noexcept = default; + + // Default constructors. + DepGraph() noexcept = default; + DepGraph(const DepGraph&) noexcept = default; + DepGraph(DepGraph&&) noexcept = default; + DepGraph& operator=(const DepGraph&) noexcept = default; + DepGraph& operator=(DepGraph&&) noexcept = default; + + /** Construct a DepGraph object for ntx transactions, with no dependencies. + * + * Complexity: O(N) where N=ntx. + **/ + explicit DepGraph(ClusterIndex ntx) noexcept + { + Assume(ntx <= SetType::Size()); + entries.resize(ntx); + for (ClusterIndex i = 0; i < ntx; ++i) { + entries[i].ancestors = SetType::Singleton(i); + entries[i].descendants = SetType::Singleton(i); + } + } + + /** Construct a DepGraph object given a cluster. + * + * Complexity: O(N^2) where N=cluster.size(). + */ + explicit DepGraph(const Cluster<SetType>& cluster) noexcept : entries(cluster.size()) + { + for (ClusterIndex i = 0; i < cluster.size(); ++i) { + // Fill in fee and size. + entries[i].feerate = cluster[i].first; + // Fill in direct parents as ancestors. + entries[i].ancestors = cluster[i].second; + // Make sure transactions are ancestors of themselves. + entries[i].ancestors.Set(i); + } + + // Propagate ancestor information. + for (ClusterIndex i = 0; i < entries.size(); ++i) { + // At this point, entries[a].ancestors[b] is true iff b is an ancestor of a and there + // is a path from a to b through the subgraph consisting of {a, b} union + // {0, 1, ..., (i-1)}. + SetType to_merge = entries[i].ancestors; + for (ClusterIndex j = 0; j < entries.size(); ++j) { + if (entries[j].ancestors[i]) { + entries[j].ancestors |= to_merge; + } + } + } + + // Fill in descendant information by transposing the ancestor information. + for (ClusterIndex i = 0; i < entries.size(); ++i) { + for (auto j : entries[i].ancestors) { + entries[j].descendants.Set(i); + } + } + } + + /** Get the number of transactions in the graph. Complexity: O(1). */ + auto TxCount() const noexcept { return entries.size(); } + /** Get the feerate of a given transaction i. Complexity: O(1). */ + const FeeFrac& FeeRate(ClusterIndex i) const noexcept { return entries[i].feerate; } + /** Get the ancestors of a given transaction i. Complexity: O(1). */ + const SetType& Ancestors(ClusterIndex i) const noexcept { return entries[i].ancestors; } + /** Get the descendants of a given transaction i. Complexity: O(1). */ + const SetType& Descendants(ClusterIndex i) const noexcept { return entries[i].descendants; } + + /** Add a new unconnected transaction to this transaction graph (at the end), and return its + * ClusterIndex. + * + * Complexity: O(1) (amortized, due to resizing of backing vector). + */ + ClusterIndex AddTransaction(const FeeFrac& feefrac) noexcept + { + Assume(TxCount() < SetType::Size()); + ClusterIndex new_idx = TxCount(); + entries.emplace_back(feefrac, SetType::Singleton(new_idx), SetType::Singleton(new_idx)); + return new_idx; + } + + /** Modify this transaction graph, adding a dependency between a specified parent and child. + * + * Complexity: O(N) where N=TxCount(). + **/ + void AddDependency(ClusterIndex parent, ClusterIndex child) noexcept + { + // Bail out if dependency is already implied. + if (entries[child].ancestors[parent]) return; + // To each ancestor of the parent, add as descendants the descendants of the child. + const auto& chl_des = entries[child].descendants; + for (auto anc_of_par : Ancestors(parent)) { + entries[anc_of_par].descendants |= chl_des; + } + // To each descendant of the child, add as ancestors the ancestors of the parent. + const auto& par_anc = entries[parent].ancestors; + for (auto dec_of_chl : Descendants(child)) { + entries[dec_of_chl].ancestors |= par_anc; + } + } + + /** Compute the aggregate feerate of a set of nodes in this graph. + * + * Complexity: O(N) where N=elems.Count(). + **/ + FeeFrac FeeRate(const SetType& elems) const noexcept + { + FeeFrac ret; + for (auto pos : elems) ret += entries[pos].feerate; + return ret; + } +}; + +} // namespace cluster_linearize + +#endif // BITCOIN_CLUSTER_LINEARIZE_H |