Skip to content

Commit a6e07e7

Browse files
committed
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.
1 parent 5d28013 commit a6e07e7

File tree

2 files changed

+172
-0
lines changed

2 files changed

+172
-0
lines changed

src/Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ BITCOIN_CORE_H = \
132132
chainparamsseeds.h \
133133
checkqueue.h \
134134
clientversion.h \
135+
cluster_linearize.h \
135136
coins.h \
136137
common/args.h \
137138
common/bloom.h \

src/cluster_linearize.h

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
// Copyright (c) The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#ifndef BITCOIN_CLUSTER_LINEARIZE_H
6+
#define BITCOIN_CLUSTER_LINEARIZE_H
7+
8+
#include <stdint.h>
9+
#include <vector>
10+
#include <utility>
11+
12+
#include <util/feefrac.h>
13+
14+
namespace cluster_linearize {
15+
16+
/** Data type to represent cluster input.
17+
*
18+
* cluster[i].first is tx_i's fee and size.
19+
* cluster[i].second[j] is true iff tx_i spends one or more of tx_j's outputs.
20+
*/
21+
template<typename SetType>
22+
using Cluster = std::vector<std::pair<FeeFrac, SetType>>;
23+
24+
/** Data type to represent transaction indices in clusters. */
25+
using ClusterIndex = uint32_t;
26+
27+
/** Data structure that holds a transaction graph's preprocessed data (fee, size, ancestors,
28+
* descendants). */
29+
template<typename SetType>
30+
class DepGraph
31+
{
32+
/** Information about a single transaction. */
33+
struct Entry
34+
{
35+
/** Fee and size of transaction itself. */
36+
FeeFrac feerate;
37+
/** All ancestors of the transaction (including itself). */
38+
SetType ancestors;
39+
/** All descendants of the transaction (including itself). */
40+
SetType descendants;
41+
42+
/** Equality operator (primarily for for testing purposes). */
43+
friend bool operator==(const Entry&, const Entry&) noexcept = default;
44+
45+
/** Construct an empty entry. */
46+
Entry() noexcept = default;
47+
/** Construct an entry with a given feerate, ancestor set, descendant set. */
48+
Entry(const FeeFrac& f, const SetType& a, const SetType& d) noexcept : feerate(f), ancestors(a), descendants(d) {}
49+
};
50+
51+
/** Data for each transaction, in the same order as the Cluster it was constructed from. */
52+
std::vector<Entry> entries;
53+
54+
public:
55+
/** Equality operator (primarily for testing purposes). */
56+
friend bool operator==(const DepGraph&, const DepGraph&) noexcept = default;
57+
58+
// Default constructors.
59+
DepGraph() noexcept = default;
60+
DepGraph(const DepGraph&) noexcept = default;
61+
DepGraph(DepGraph&&) noexcept = default;
62+
DepGraph& operator=(const DepGraph&) noexcept = default;
63+
DepGraph& operator=(DepGraph&&) noexcept = default;
64+
65+
/** Construct a DepGraph object for ntx transactions, with no dependencies.
66+
*
67+
* Complexity: O(N) where N=ntx.
68+
**/
69+
explicit DepGraph(ClusterIndex ntx) noexcept
70+
{
71+
Assume(ntx <= SetType::Size());
72+
entries.resize(ntx);
73+
for (ClusterIndex i = 0; i < ntx; ++i) {
74+
entries[i].ancestors = SetType::Singleton(i);
75+
entries[i].descendants = SetType::Singleton(i);
76+
}
77+
}
78+
79+
/** Construct a DepGraph object given a cluster.
80+
*
81+
* Complexity: O(N^2) where N=cluster.size().
82+
*/
83+
explicit DepGraph(const Cluster<SetType>& cluster) noexcept : entries(cluster.size())
84+
{
85+
for (ClusterIndex i = 0; i < cluster.size(); ++i) {
86+
// Fill in fee and size.
87+
entries[i].feerate = cluster[i].first;
88+
// Fill in direct parents as ancestors.
89+
entries[i].ancestors = cluster[i].second;
90+
// Make sure transactions are ancestors of themselves.
91+
entries[i].ancestors.Set(i);
92+
}
93+
94+
// Propagate ancestor information.
95+
for (ClusterIndex i = 0; i < entries.size(); ++i) {
96+
// At this point, entries[a].ancestors[b] is true iff b is an ancestor of a and there
97+
// is a path from a to b through the subgraph consisting of {a, b} union
98+
// {0, 1, ..., (i-1)}.
99+
SetType to_merge = entries[i].ancestors;
100+
for (ClusterIndex j = 0; j < entries.size(); ++j) {
101+
if (entries[j].ancestors[i]) {
102+
entries[j].ancestors |= to_merge;
103+
}
104+
}
105+
}
106+
107+
// Fill in descendant information by transposing the ancestor information.
108+
for (ClusterIndex i = 0; i < entries.size(); ++i) {
109+
for (auto j : entries[i].ancestors) {
110+
entries[j].descendants.Set(i);
111+
}
112+
}
113+
}
114+
115+
/** Get the number of transactions in the graph. Complexity: O(1). */
116+
auto TxCount() const noexcept { return entries.size(); }
117+
/** Get the feerate of a given transaction i. Complexity: O(1). */
118+
const FeeFrac& FeeRate(ClusterIndex i) const noexcept { return entries[i].feerate; }
119+
/** Get the ancestors of a given transaction i. Complexity: O(1). */
120+
const SetType& Ancestors(ClusterIndex i) const noexcept { return entries[i].ancestors; }
121+
/** Get the descendants of a given transaction i. Complexity: O(1). */
122+
const SetType& Descendants(ClusterIndex i) const noexcept { return entries[i].descendants; }
123+
124+
/** Add a new unconnected transaction to this transaction graph (at the end), and return its
125+
* ClusterIndex.
126+
*
127+
* Complexity: O(1) (amortized, due to resizing of backing vector).
128+
*/
129+
ClusterIndex AddTransaction(const FeeFrac& feefrac) noexcept
130+
{
131+
Assume(TxCount() < SetType::Size());
132+
ClusterIndex new_idx = TxCount();
133+
entries.emplace_back(feefrac, SetType::Singleton(new_idx), SetType::Singleton(new_idx));
134+
return new_idx;
135+
}
136+
137+
/** Modify this transaction graph, adding a dependency between a specified parent and child.
138+
*
139+
* Complexity: O(N) where N=TxCount().
140+
**/
141+
void AddDependency(ClusterIndex parent, ClusterIndex child) noexcept
142+
{
143+
// Bail out if dependency is already implied.
144+
if (entries[child].ancestors[parent]) return;
145+
// To each ancestor of the parent, add as descendants the descendants of the child.
146+
const auto& chl_des = entries[child].descendants;
147+
for (auto anc_of_par : Ancestors(parent)) {
148+
entries[anc_of_par].descendants |= chl_des;
149+
}
150+
// To each descendant of the child, add as ancestors the ancestors of the parent.
151+
const auto& par_anc = entries[parent].ancestors;
152+
for (auto dec_of_chl : Descendants(child)) {
153+
entries[dec_of_chl].ancestors |= par_anc;
154+
}
155+
}
156+
157+
/** Compute the aggregate feerate of a set of nodes in this graph.
158+
*
159+
* Complexity: O(N) where N=elems.Count().
160+
**/
161+
FeeFrac FeeRate(const SetType& elems) const noexcept
162+
{
163+
FeeFrac ret;
164+
for (auto pos : elems) ret += entries[pos].feerate;
165+
return ret;
166+
}
167+
};
168+
169+
} // namespace cluster_linearize
170+
171+
#endif // BITCOIN_CLUSTER_LINEARIZE_H

0 commit comments

Comments
 (0)