Bitcoin Core 28.0.0
P2P Digital Currency
Loading...
Searching...
No Matches
cluster_linearize.h
Go to the documentation of this file.
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_TEST_UTIL_CLUSTER_LINEARIZE_H
6#define BITCOIN_TEST_UTIL_CLUSTER_LINEARIZE_H
7
8#include <cluster_linearize.h>
9#include <serialize.h>
10#include <span.h>
11#include <streams.h>
12#include <util/bitset.h>
13#include <util/feefrac.h>
14
15#include <stdint.h>
16#include <numeric>
17#include <vector>
18#include <utility>
19
20namespace {
21
22using namespace cluster_linearize;
23
24using TestBitSet = BitSet<32>;
25
27template<typename SetType>
28bool IsAcyclic(const DepGraph<SetType>& depgraph) noexcept
29{
30 for (ClusterIndex i = 0; i < depgraph.TxCount(); ++i) {
31 if ((depgraph.Ancestors(i) & depgraph.Descendants(i)) != SetType::Singleton(i)) {
32 return false;
33 }
34 }
35 return true;
36}
37
102struct DepGraphFormatter
103{
105 static uint64_t SignedToUnsigned(int64_t x) noexcept
106 {
107 if (x < 0) {
108 return 2 * uint64_t(-(x + 1)) + 1;
109 } else {
110 return 2 * uint64_t(x);
111 }
112 }
113
115 static int64_t UnsignedToSigned(uint64_t x) noexcept
116 {
117 if (x & 1) {
118 return -int64_t(x / 2) - 1;
119 } else {
120 return int64_t(x / 2);
121 }
122 }
123
124 template <typename Stream, typename SetType>
125 static void Ser(Stream& s, const DepGraph<SetType>& depgraph)
126 {
128 std::vector<ClusterIndex> topo_order(depgraph.TxCount());
129 std::iota(topo_order.begin(), topo_order.end(), ClusterIndex{0});
130 std::sort(topo_order.begin(), topo_order.end(), [&](ClusterIndex a, ClusterIndex b) {
131 auto anc_a = depgraph.Ancestors(a).Count(), anc_b = depgraph.Ancestors(b).Count();
132 if (anc_a != anc_b) return anc_a < anc_b;
133 return a < b;
134 });
135
138 std::vector<ClusterIndex> rebuilt_order;
139 rebuilt_order.reserve(depgraph.TxCount());
140
141 // Loop over the transactions in topological order.
142 for (ClusterIndex topo_idx = 0; topo_idx < topo_order.size(); ++topo_idx) {
144 ClusterIndex idx = topo_order[topo_idx];
145 // Write size, which must be larger than 0.
147 // Write fee, encoded as an unsigned varint (odd=negative, even=non-negative).
148 s << VARINT(SignedToUnsigned(depgraph.FeeRate(idx).fee));
149 // Write dependency information.
150 SetType written_parents;
151 uint64_t diff = 0;
152 for (ClusterIndex dep_dist = 0; dep_dist < topo_idx; ++dep_dist) {
154 ClusterIndex dep_idx = topo_order[topo_idx - 1 - dep_dist];
155 // Ignore transactions which are already known to be ancestors.
156 if (depgraph.Descendants(dep_idx).Overlaps(written_parents)) continue;
157 if (depgraph.Ancestors(idx)[dep_idx]) {
158 // When an actual parent is encounted, encode how many non-parents were skipped
159 // before it.
160 s << VARINT(diff);
161 diff = 0;
162 written_parents.Set(dep_idx);
163 } else {
164 // When a non-parent is encountered, increment the skip counter.
165 ++diff;
166 }
167 }
168 // Write position information.
169 ClusterIndex insert_distance = 0;
170 while (insert_distance < rebuilt_order.size()) {
171 // Loop to find how far from the end in rebuilt_order to insert.
172 if (idx > *(rebuilt_order.end() - 1 - insert_distance)) break;
173 ++insert_distance;
174 }
175 rebuilt_order.insert(rebuilt_order.end() - insert_distance, idx);
176 s << VARINT(diff + insert_distance);
177 }
178
179 // Output a final 0 to denote the end of the graph.
180 s << uint8_t{0};
181 }
182
183 template <typename Stream, typename SetType>
184 void Unser(Stream& s, DepGraph<SetType>& depgraph)
185 {
188 DepGraph<SetType> topo_depgraph;
191 std::vector<ClusterIndex> reordering;
192
193 // Read transactions in topological order.
194 try {
195 while (true) {
196 // Read size. Size 0 signifies the end of the DepGraph.
197 int32_t size;
199 size &= 0x3FFFFF; // Enough for size up to 4M.
200 static_assert(0x3FFFFF >= 4000000);
201 if (size == 0 || topo_depgraph.TxCount() == SetType::Size()) break;
202 // Read fee, encoded as an unsigned varint (odd=negative, even=non-negative).
203 uint64_t coded_fee;
204 s >> VARINT(coded_fee);
205 coded_fee &= 0xFFFFFFFFFFFFF; // Enough for fee between -21M...21M BTC.
206 static_assert(0xFFFFFFFFFFFFF > uint64_t{2} * 21000000 * 100000000);
207 auto fee = UnsignedToSigned(coded_fee);
208 // Extend topo_depgraph with the new transaction (at the end).
209 auto topo_idx = topo_depgraph.AddTransaction({fee, size});
210 reordering.push_back(topo_idx);
211 // Read dependency information.
212 uint64_t diff = 0;
213 s >> VARINT(diff);
214 for (ClusterIndex dep_dist = 0; dep_dist < topo_idx; ++dep_dist) {
216 ClusterIndex dep_topo_idx = topo_idx - 1 - dep_dist;
217 // Ignore transactions which are already known ancestors of topo_idx.
218 if (topo_depgraph.Descendants(dep_topo_idx)[topo_idx]) continue;
219 if (diff == 0) {
220 // When the skip counter has reached 0, add an actual dependency.
221 topo_depgraph.AddDependency(dep_topo_idx, topo_idx);
222 // And read the number of skips after it.
223 s >> VARINT(diff);
224 } else {
225 // Otherwise, dep_topo_idx is not a parent. Decrement and continue.
226 --diff;
227 }
228 }
229 // If we reach this point, we can interpret the remaining skip value as how far from the
230 // end of reordering topo_idx should be placed (wrapping around), so move it to its
231 // correct location. The preliminary reordering.push_back(topo_idx) above was to make
232 // sure that if a deserialization exception occurs, topo_idx still appears somewhere.
233 reordering.pop_back();
234 reordering.insert(reordering.end() - (diff % (reordering.size() + 1)), topo_idx);
235 }
236 } catch (const std::ios_base::failure&) {}
237
238 // Construct the original cluster order depgraph.
239 depgraph = {};
240 // Add transactions to depgraph in the original cluster order.
241 for (auto topo_idx : reordering) {
242 depgraph.AddTransaction(topo_depgraph.FeeRate(topo_idx));
243 }
244 // Translate dependencies from topological to cluster order.
245 for (ClusterIndex idx = 0; idx < reordering.size(); ++idx) {
246 ClusterIndex topo_idx = reordering[idx];
247 for (ClusterIndex dep_idx = 0; dep_idx < reordering.size(); ++dep_idx) {
248 ClusterIndex dep_topo_idx = reordering[dep_idx];
249 if (topo_depgraph.Ancestors(topo_idx)[dep_topo_idx]) {
250 depgraph.AddDependency(dep_idx, idx);
251 }
252 }
253 }
254 }
255};
256
258template<typename SetType>
259void SanityCheck(const DepGraph<SetType>& depgraph)
260{
261 // Consistency check between ancestors internally.
262 for (ClusterIndex i = 0; i < depgraph.TxCount(); ++i) {
263 // Transactions include themselves as ancestors.
264 assert(depgraph.Ancestors(i)[i]);
265 // If a is an ancestor of b, then b's ancestors must include all of a's ancestors.
266 for (auto a : depgraph.Ancestors(i)) {
267 assert(depgraph.Ancestors(i).IsSupersetOf(depgraph.Ancestors(a)));
268 }
269 }
270 // Consistency check between ancestors and descendants.
271 for (ClusterIndex i = 0; i < depgraph.TxCount(); ++i) {
272 for (ClusterIndex j = 0; j < depgraph.TxCount(); ++j) {
273 assert(depgraph.Ancestors(i)[j] == depgraph.Descendants(j)[i]);
274 }
275 }
276 // If DepGraph is acyclic, serialize + deserialize must roundtrip.
277 if (IsAcyclic(depgraph)) {
278 std::vector<unsigned char> ser;
279 VectorWriter writer(ser, 0);
280 writer << Using<DepGraphFormatter>(depgraph);
281 SpanReader reader(ser);
282 DepGraph<TestBitSet> decoded_depgraph;
283 reader >> Using<DepGraphFormatter>(decoded_depgraph);
284 assert(depgraph == decoded_depgraph);
285 assert(reader.empty());
286 // It must also deserialize correctly without the terminal 0 byte (as the deserializer
287 // will upon EOF still return what it read so far).
288 assert(ser.size() >= 1 && ser.back() == 0);
289 ser.pop_back();
290 reader = SpanReader{ser};
291 decoded_depgraph = {};
292 reader >> Using<DepGraphFormatter>(decoded_depgraph);
293 assert(depgraph == decoded_depgraph);
294 assert(reader.empty());
295 }
296}
297
299template<typename SetType>
300void VerifyDepGraphFromCluster(const Cluster<SetType>& cluster, const DepGraph<SetType>& depgraph)
301{
302 // Sanity check the depgraph, which includes a check for correspondence between ancestors and
303 // descendants, so it suffices to check just ancestors below.
304 SanityCheck(depgraph);
305 // Verify transaction count.
306 assert(cluster.size() == depgraph.TxCount());
307 // Verify feerates.
308 for (ClusterIndex i = 0; i < depgraph.TxCount(); ++i) {
309 assert(depgraph.FeeRate(i) == cluster[i].first);
310 }
311 // Verify ancestors.
312 for (ClusterIndex i = 0; i < depgraph.TxCount(); ++i) {
313 // Start with the transaction having itself as ancestor.
314 auto ancestors = SetType::Singleton(i);
315 // Add parents of ancestors to the set of ancestors until it stops changing.
316 while (true) {
317 const auto old_ancestors = ancestors;
318 for (auto ancestor : ancestors) {
319 ancestors |= cluster[ancestor].second;
320 }
321 if (old_ancestors == ancestors) break;
322 }
323 // Compare against depgraph.
324 assert(depgraph.Ancestors(i) == ancestors);
325 // Some additional sanity tests:
326 // - Every transaction has itself as ancestor.
327 assert(ancestors[i]);
328 // - Every transaction has its direct parents as ancestors.
329 for (auto parent : cluster[i].second) {
330 assert(ancestors[parent]);
331 }
332 }
333}
334
336template<typename SetType>
337void SanityCheck(const DepGraph<SetType>& depgraph, Span<const ClusterIndex> linearization)
338{
339 // Check completeness.
340 assert(linearization.size() == depgraph.TxCount());
341 TestBitSet done;
342 for (auto i : linearization) {
343 // Check transaction position is in range.
344 assert(i < depgraph.TxCount());
345 // Check topology and lack of duplicates.
346 assert((depgraph.Ancestors(i) - done) == TestBitSet::Singleton(i));
347 done.Set(i);
348 }
349}
350
351} // namespace
352
353#endif // BITCOIN_TEST_UTIL_CLUSTER_LINEARIZE_H
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
Definition bitset.h:523
A Span is an object that can refer to a contiguous sequence of objects.
Definition span.h:98
constexpr std::size_t size() const noexcept
Definition span.h:187
Minimal stream for reading from an existing byte array by Span.
Definition streams.h:101
Data structure that holds a transaction graph's preprocessed data (fee, size, ancestors,...
void AddDependency(ClusterIndex parent, ClusterIndex child) noexcept
Modify this transaction graph, adding a dependency between a specified parent and child.
const FeeFrac & FeeRate(ClusterIndex i) const noexcept
Get the feerate of a given transaction i.
const SetType & Ancestors(ClusterIndex i) const noexcept
Get the ancestors of a given transaction i.
auto TxCount() const noexcept
Get the number of transactions in the graph.
const SetType & Descendants(ClusterIndex i) const noexcept
Get the descendants of a given transaction i.
ClusterIndex AddTransaction(const FeeFrac &feefrac) noexcept
Add a new unconnected transaction to this transaction graph (at the end), and return its ClusterIndex...
uint32_t ClusterIndex
Data type to represent transaction indices in clusters.
std::vector< std::pair< FeeFrac, SetType > > Cluster
Data type to represent cluster input.
#define VARINT(obj)
Definition serialize.h:498
#define VARINT_MODE(obj, mode)
Definition serialize.h:497
@ NONNEGATIVE_SIGNED
static Wrapper< Formatter, T & > Using(T &&t)
Cause serialization/deserialization of an object to be done using a specified formatter class.
Definition serialize.h:495
int64_t fee
Definition feefrac.h:63
int32_t size
Definition feefrac.h:64
assert(!tx.IsCoinBase())