aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPieter Wuille <pieter@wuille.net>2024-01-29 22:05:53 -0500
committerPieter Wuille <pieter@wuille.net>2024-07-25 10:16:37 -0400
commita6e07e769a1af652a14e533f6d3558ccdefb1de5 (patch)
treefcf17ad39faf8d5bf1dd550f233810aea210ffbc /src
parent5d280130446d57d653c749005a2e363265d87686 (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.am1
-rw-r--r--src/cluster_linearize.h171
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