Coverage Report

Created: 2026-09-15 16:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/root/bitcoin/src/test/util/cluster_linearize.h
Line
Count
Source
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 <cstdint>
16
#include <numeric>
17
#include <utility>
18
#include <vector>
19
20
namespace cluster_linearize {
21
22
using TestBitSet = BitSet<32>;
23
24
/** A formatter for a bespoke serialization for acyclic DepGraph objects.
25
 *
26
 * The serialization format outputs information about transactions in a topological order (parents
27
 * before children), together with position information so transactions can be moved back to their
28
 * correct position on deserialization.
29
 *
30
 * - For each transaction t in the DepGraph (in some topological order);
31
 *   - The size: VARINT(t.size), which cannot be 0.
32
 *   - The fee: VARINT(SignedToUnsigned(t.fee)), see below for SignedToUnsigned.
33
 *   - For each direct dependency:
34
 *     - VARINT(skip)
35
 *   - The position of t in the cluster: VARINT(skip)
36
 * - The end of the graph: VARINT(0)
37
 *
38
 * The list of skip values encodes the dependencies of t, as well as its position in the cluster.
39
 * Each skip value is the number of possibilities that were available, but were not taken. These
40
 * possibilities are, in order:
41
 * - For each previous transaction in the graph, in reverse serialization order, whether it is a
42
 *   direct parent of t (but excluding transactions which are already implied to be dependencies
43
 *   by parent relations that were serialized before it).
44
 * - The various insertion positions in the cluster, from the very end of the cluster, to the
45
 *   front.
46
 * - The appending of 1, 2, 3, ... holes at the end of the cluster, followed by appending the new
47
 *   transaction.
48
 *
49
 * Let's say you have a 7-transaction cluster, consisting of transactions F,A,C,B,_,G,E,_,D
50
 * (where _ represent holes; unused positions within the DepGraph) but serialized in order
51
 * A,B,C,D,E,F,G, because that happens to be a topological ordering. By the time G gets serialized,
52
 * what has been serialized already represents the cluster F,A,C,B,_,E,_,D (in that order). G has B
53
 * and E as direct parents, and E depends on C.
54
 *
55
 * In this case, the possibilities are, in order:
56
 * - [ ] the dependency G->F
57
 * - [X] the dependency G->E
58
 * - [ ] the dependency G->D
59
 * - [X] the dependency G->B
60
 * - [ ] the dependency G->A
61
 * - [ ] put G at the end of the cluster
62
 * - [ ] put G before D
63
 * - [ ] put G before the hole before D
64
 * - [X] put G before E
65
 * - [ ] put G before the hole before E
66
 * - [ ] put G before B
67
 * - [ ] put G before C
68
 * - [ ] put G before A
69
 * - [ ] put G before F
70
 * - [ ] add 1 hole at the end of the cluster, followed by G
71
 * - [ ] add 2 holes at the end of the cluster, followed by G
72
 * - [ ] add ...
73
 *
74
 * The skip values in this case are 1 (G->F), 1 (G->D), 4 (G->A, G at end, G before D, G before
75
 * hole). No skip after 4 is needed (or permitted), because there can only be one position for G.
76
 * Also note that G->C is not included in the list of possibilities, as it is implied by the
77
 * included G->E and E->C that came before it. On deserialization, if the last skip value was 8 or
78
 * larger (putting G before the beginning of the cluster), it is interpreted as wrapping around
79
 * back to the end.
80
 *
81
 *
82
 * Rationale:
83
 * - Why VARINTs? They are flexible enough to represent large numbers where needed, but more
84
 *   compact for smaller numbers. The serialization format is designed so that simple structures
85
 *   involve smaller numbers, so smaller size maps to simpler graphs.
86
 * - Why use SignedToUnsigned? It results in small unsigned values for signed values with small
87
 *   absolute value. This way we can encode negative fees in graphs, but still let small negative
88
 *   numbers have small encodings.
89
 * - Why are the parents emitted in reverse order compared to the transactions themselves? This
90
 *   naturally lets us skip parents-of-parents, as they will be reflected as implied dependencies.
91
 * - Why encode skip values and not a bitmask to convey the list positions? It turns out that the
92
 *   most complex graphs (in terms of linearization complexity) are ones with ~1 dependency per
93
 *   transaction. The current encoding uses ~1 byte per transaction for dependencies in this case,
94
 *   while a bitmask would require ~N/2 bits per transaction.
95
 */
96
97
struct DepGraphFormatter
98
{
99
    /** Convert x>=0 to 2x (even), x<0 to -2x-1 (odd). */
100
    static uint64_t SignedToUnsigned(int64_t x) noexcept
101
0
    {
102
0
        if (x < 0) {
  Branch (102:13): [True: 0, False: 0]
103
0
            return 2 * uint64_t(-(x + 1)) + 1;
104
0
        } else {
105
0
            return 2 * uint64_t(x);
106
0
        }
107
0
    }
108
109
    /** Convert even x to x/2 (>=0), odd x to -(x/2)-1 (<0). */
110
    static int64_t UnsignedToSigned(uint64_t x) noexcept
111
0
    {
112
0
        if (x & 1) {
  Branch (112:13): [True: 0, False: 0]
113
0
            return -int64_t(x / 2) - 1;
114
0
        } else {
115
0
            return int64_t(x / 2);
116
0
        }
117
0
    }
118
119
    template <typename Stream, typename SetType>
120
    static void Ser(Stream& s, const DepGraph<SetType>& depgraph)
121
0
    {
122
        /** Construct a topological order to serialize the transactions in. */
123
0
        std::vector<DepGraphIndex> topo_order;
124
0
        topo_order.reserve(depgraph.TxCount());
125
0
        for (auto i : depgraph.Positions()) topo_order.push_back(i);
  Branch (125:21): [True: 0, False: 0]
126
0
        std::sort(topo_order.begin(), topo_order.end(), [&](DepGraphIndex a, DepGraphIndex b) {
127
0
            auto anc_a = depgraph.Ancestors(a).Count(), anc_b = depgraph.Ancestors(b).Count();
128
0
            if (anc_a != anc_b) return anc_a < anc_b;
  Branch (128:17): [True: 0, False: 0]
129
0
            return a < b;
130
0
        });
131
132
        /** Which positions (incl. holes) the deserializer already knows when it has deserialized
133
         *  what has been serialized here so far. */
134
0
        SetType done;
135
136
        // Loop over the transactions in topological order.
137
0
        for (DepGraphIndex topo_idx = 0; topo_idx < topo_order.size(); ++topo_idx) {
  Branch (137:42): [True: 0, False: 0]
138
            /** Which depgraph index we are currently writing. */
139
0
            DepGraphIndex idx = topo_order[topo_idx];
140
            // Write size, which must be larger than 0.
141
0
            s << VARINT_MODE(depgraph.FeeRate(idx).size, VarIntMode::NONNEGATIVE_SIGNED);
142
            // Write fee, encoded as an unsigned varint (odd=negative, even=non-negative).
143
0
            s << VARINT(SignedToUnsigned(depgraph.FeeRate(idx).fee));
144
            // Write dependency information.
145
0
            SetType written_parents;
146
0
            uint64_t diff = 0; //!< How many potential parent/child relations we have skipped over.
147
0
            for (DepGraphIndex dep_dist = 0; dep_dist < topo_idx; ++dep_dist) {
  Branch (147:46): [True: 0, False: 0]
148
                /** Which depgraph index we are currently considering as parent of idx. */
149
0
                DepGraphIndex dep_idx = topo_order[topo_idx - 1 - dep_dist];
150
                // Ignore transactions which are already known to be ancestors.
151
0
                if (depgraph.Descendants(dep_idx).Overlaps(written_parents)) continue;
  Branch (151:21): [True: 0, False: 0]
152
0
                if (depgraph.Ancestors(idx)[dep_idx]) {
  Branch (152:21): [True: 0, False: 0]
153
                    // When an actual parent is encountered, encode how many non-parents were skipped
154
                    // before it.
155
0
                    s << VARINT(diff);
156
0
                    diff = 0;
157
0
                    written_parents.Set(dep_idx);
158
0
                } else {
159
                    // When a non-parent is encountered, increment the skip counter.
160
0
                    ++diff;
161
0
                }
162
0
            }
163
            // Write position information.
164
0
            auto add_holes = SetType::Fill(idx) - done - depgraph.Positions();
165
0
            if (add_holes.None()) {
  Branch (165:17): [True: 0, False: 0]
166
                // The new transaction is to be inserted N positions back from the end of the
167
                // cluster. Emit N to indicate that that many insertion choices are skipped.
168
0
                auto skips = (done - SetType::Fill(idx)).Count();
169
0
                s << VARINT(diff + skips);
170
0
            } else {
171
                // The new transaction is to be appended at the end of the cluster, after N holes.
172
                // Emit current_cluster_size + N, to indicate all insertion choices are skipped,
173
                // plus N possibilities for the number of holes.
174
0
                s << VARINT(diff + done.Count() + add_holes.Count());
175
0
                done |= add_holes;
176
0
            }
177
0
            done.Set(idx);
178
0
        }
179
180
        // Output a final 0 to denote the end of the graph.
181
0
        s << uint8_t{0};
182
0
    }
183
184
    template <typename Stream, typename SetType>
185
    void Unser(Stream& s, DepGraph<SetType>& depgraph)
186
0
    {
187
        /** The dependency graph which we deserialize into first, with transactions in
188
         *  topological serialization order, not original cluster order. */
189
0
        DepGraph<SetType> topo_depgraph;
190
        /** Mapping from serialization order to cluster order, used later to reconstruct the
191
         *  cluster order. */
192
0
        std::vector<DepGraphIndex> reordering;
193
        /** How big the entries vector in the reconstructed depgraph will be (including holes). */
194
0
        DepGraphIndex total_size{0};
195
196
        // Read transactions in topological order.
197
0
        while (true) {
  Branch (197:16): [Folded - Ignored]
198
0
            FeeFrac new_feerate; //!< The new transaction's fee and size.
199
0
            SetType new_ancestors; //!< The new transaction's ancestors (excluding itself).
200
0
            uint64_t diff{0}; //!< How many potential parents/insertions we have to skip.
201
0
            bool read_error{false};
202
0
            try {
203
                // Read size. Size 0 signifies the end of the DepGraph.
204
0
                int32_t size;
205
0
                s >> VARINT_MODE(size, VarIntMode::NONNEGATIVE_SIGNED);
206
0
                size &= 0x3FFFFF; // Enough for size up to 4M.
207
0
                static_assert(0x3FFFFF >= 4000000);
208
0
                if (size == 0 || topo_depgraph.TxCount() == SetType::Size()) break;
  Branch (208:21): [True: 0, False: 0]
  Branch (208:34): [True: 0, False: 0]
209
                // Read fee, encoded as an unsigned varint (odd=negative, even=non-negative).
210
0
                uint64_t coded_fee;
211
0
                s >> VARINT(coded_fee);
212
0
                coded_fee &= 0xFFFFFFFFFFFFF; // Enough for fee between -21M...21M BTC.
213
0
                static_assert(0xFFFFFFFFFFFFF > uint64_t{2} * 21000000 * 100000000);
214
0
                new_feerate = {UnsignedToSigned(coded_fee), size};
215
                // Read dependency information.
216
0
                auto topo_idx = reordering.size();
217
0
                s >> VARINT(diff);
218
0
                for (DepGraphIndex dep_dist = 0; dep_dist < topo_idx; ++dep_dist) {
  Branch (218:50): [True: 0, False: 0]
219
                    /** Which topo_depgraph index we are currently considering as parent of topo_idx. */
220
0
                    DepGraphIndex dep_topo_idx = topo_idx - 1 - dep_dist;
221
                    // Ignore transactions which are already known ancestors of topo_idx.
222
0
                    if (new_ancestors[dep_topo_idx]) continue;
  Branch (222:25): [True: 0, False: 0]
223
0
                    if (diff == 0) {
  Branch (223:25): [True: 0, False: 0]
224
                        // When the skip counter has reached 0, add an actual dependency.
225
0
                        new_ancestors |= topo_depgraph.Ancestors(dep_topo_idx);
226
                        // And read the number of skips after it.
227
0
                        s >> VARINT(diff);
228
0
                    } else {
229
                        // Otherwise, dep_topo_idx is not a parent. Decrement and continue.
230
0
                        --diff;
231
0
                    }
232
0
                }
233
0
            } catch (const std::ios_base::failure&) {
234
                // Continue even if a read error was encountered.
235
0
                read_error = true;
236
0
            }
237
            // Construct a new transaction whenever we made it past the new_feerate construction.
238
0
            if (new_feerate.IsEmpty()) break;
  Branch (238:17): [True: 0, False: 0]
239
0
            assert(reordering.size() < SetType::Size());
  Branch (239:13): [True: 0, False: 0]
240
0
            auto topo_idx = topo_depgraph.AddTransaction(new_feerate);
241
0
            topo_depgraph.AddDependencies(new_ancestors, topo_idx);
242
0
            if (total_size < SetType::Size()) {
  Branch (242:17): [True: 0, False: 0]
243
                // Normal case.
244
0
                diff %= SetType::Size();
245
0
                if (diff <= total_size) {
  Branch (245:21): [True: 0, False: 0]
246
                    // Insert the new transaction at distance diff back from the end.
247
0
                    for (auto& pos : reordering) {
  Branch (247:36): [True: 0, False: 0]
248
0
                        pos += (pos >= total_size - diff);
249
0
                    }
250
0
                    reordering.push_back(total_size++ - diff);
251
0
                } else {
252
                    // Append diff - total_size holes at the end, plus the new transaction.
253
0
                    total_size = diff;
254
0
                    reordering.push_back(total_size++);
255
0
                }
256
0
            } else {
257
                // In case total_size == SetType::Size, it is not possible to insert the new
258
                // transaction without exceeding SetType's size. Instead, interpret diff as an
259
                // index into the holes, and overwrite a position there. This branch is never used
260
                // when deserializing the output of the serializer, but gives meaning to otherwise
261
                // invalid input.
262
0
                diff %= (SetType::Size() - reordering.size());
263
0
                SetType holes = SetType::Fill(SetType::Size());
264
0
                for (auto pos : reordering) holes.Reset(pos);
  Branch (264:31): [True: 0, False: 0]
265
0
                for (auto pos : holes) {
  Branch (265:31): [True: 0, False: 0]
266
0
                    if (diff == 0) {
  Branch (266:25): [True: 0, False: 0]
267
0
                        reordering.push_back(pos);
268
0
                        break;
269
0
                    }
270
0
                    --diff;
271
0
                }
272
0
            }
273
            // Stop if a read error was encountered during deserialization.
274
0
            if (read_error) break;
  Branch (274:17): [True: 0, False: 0]
275
0
        }
276
277
        // Construct the original cluster order depgraph.
278
0
        depgraph = DepGraph(topo_depgraph, reordering, total_size);
279
0
    }
280
};
281
282
/** Perform a sanity/consistency check on a DepGraph. */
283
template<typename SetType>
284
void SanityCheck(const DepGraph<SetType>& depgraph)
285
0
{
286
    // Verify Positions and PositionRange consistency.
287
0
    DepGraphIndex num_positions{0};
288
0
    DepGraphIndex position_range{0};
289
0
    for (DepGraphIndex i : depgraph.Positions()) {
  Branch (289:26): [True: 0, False: 0]
290
0
        ++num_positions;
291
0
        position_range = i + 1;
292
0
    }
293
0
    assert(num_positions == depgraph.TxCount());
  Branch (293:5): [True: 0, False: 0]
294
0
    assert(position_range == depgraph.PositionRange());
  Branch (294:5): [True: 0, False: 0]
295
0
    assert(position_range >= num_positions);
  Branch (295:5): [True: 0, False: 0]
296
0
    assert(position_range <= SetType::Size());
  Branch (296:5): [True: 0, False: 0]
297
    // Consistency check between ancestors internally.
298
0
    for (DepGraphIndex i : depgraph.Positions()) {
  Branch (298:26): [True: 0, False: 0]
299
        // Transactions include themselves as ancestors.
300
0
        assert(depgraph.Ancestors(i)[i]);
  Branch (300:9): [True: 0, False: 0]
301
        // If a is an ancestor of b, then b's ancestors must include all of a's ancestors.
302
0
        for (auto a : depgraph.Ancestors(i)) {
  Branch (302:21): [True: 0, False: 0]
303
0
            assert(depgraph.Ancestors(i).IsSupersetOf(depgraph.Ancestors(a)));
  Branch (303:13): [True: 0, False: 0]
304
0
        }
305
0
    }
306
    // Consistency check between ancestors and descendants.
307
0
    for (DepGraphIndex i : depgraph.Positions()) {
  Branch (307:26): [True: 0, False: 0]
308
0
        for (DepGraphIndex j : depgraph.Positions()) {
  Branch (308:30): [True: 0, False: 0]
309
0
            assert(depgraph.Ancestors(i)[j] == depgraph.Descendants(j)[i]);
  Branch (309:13): [True: 0, False: 0]
310
0
        }
311
        // No transaction is a parent or child of itself.
312
0
        auto parents = depgraph.GetReducedParents(i);
313
0
        auto children = depgraph.GetReducedChildren(i);
314
0
        assert(!parents[i]);
  Branch (314:9): [True: 0, False: 0]
315
0
        assert(!children[i]);
  Branch (315:9): [True: 0, False: 0]
316
        // Parents of a transaction do not have ancestors inside those parents (except itself).
317
        // Note that even the transaction itself may be missing (if it is part of a cycle).
318
0
        for (auto parent : parents) {
  Branch (318:26): [True: 0, False: 0]
319
0
            assert((depgraph.Ancestors(parent) & parents).IsSubsetOf(SetType::Singleton(parent)));
  Branch (319:13): [True: 0, False: 0]
320
0
        }
321
        // Similar for children and descendants.
322
0
        for (auto child : children) {
  Branch (322:25): [True: 0, False: 0]
323
0
            assert((depgraph.Descendants(child) & children).IsSubsetOf(SetType::Singleton(child)));
  Branch (323:13): [True: 0, False: 0]
324
0
        }
325
0
    }
326
0
    if (depgraph.IsAcyclic()) {
  Branch (326:9): [True: 0, False: 0]
327
        // If DepGraph is acyclic, serialize + deserialize must roundtrip.
328
0
        std::vector<unsigned char> ser;
329
0
        VectorWriter writer(ser, 0);
330
0
        writer << Using<DepGraphFormatter>(depgraph);
331
0
        SpanReader reader(ser);
332
0
        DepGraph<SetType> decoded_depgraph;
333
0
        reader >> Using<DepGraphFormatter>(decoded_depgraph);
334
0
        assert(depgraph == decoded_depgraph);
  Branch (334:9): [True: 0, False: 0]
335
0
        assert(reader.empty());
  Branch (335:9): [True: 0, False: 0]
336
        // It must also deserialize correctly without the terminal 0 byte (as the deserializer
337
        // will upon EOF still return what it read so far).
338
0
        assert(ser.size() >= 1 && ser.back() == 0);
  Branch (338:9): [True: 0, False: 0]
  Branch (338:9): [True: 0, False: 0]
  Branch (338:9): [True: 0, False: 0]
339
0
        ser.pop_back();
340
0
        reader = SpanReader{ser};
341
0
        decoded_depgraph = {};
342
0
        reader >> Using<DepGraphFormatter>(decoded_depgraph);
343
0
        assert(depgraph == decoded_depgraph);
  Branch (343:9): [True: 0, False: 0]
344
0
        assert(reader.empty());
  Branch (344:9): [True: 0, False: 0]
345
346
        // In acyclic graphs, the union of parents with parents of parents etc. yields the
347
        // full ancestor set (and similar for children and descendants).
348
0
        std::vector<SetType> parents(depgraph.PositionRange()), children(depgraph.PositionRange());
349
0
        for (DepGraphIndex i : depgraph.Positions()) {
  Branch (349:30): [True: 0, False: 0]
350
0
            parents[i] = depgraph.GetReducedParents(i);
351
0
            children[i] = depgraph.GetReducedChildren(i);
352
0
        }
353
0
        for (auto i : depgraph.Positions()) {
  Branch (353:21): [True: 0, False: 0]
354
            // Initialize the set of ancestors with just the current transaction itself.
355
0
            SetType ancestors = SetType::Singleton(i);
356
            // Iteratively add parents of all transactions in the ancestor set to itself.
357
0
            while (true) {
  Branch (357:20): [Folded - Ignored]
358
0
                const auto old_ancestors = ancestors;
359
0
                for (auto j : ancestors) ancestors |= parents[j];
  Branch (359:29): [True: 0, False: 0]
360
                // Stop when no more changes are being made.
361
0
                if (old_ancestors == ancestors) break;
  Branch (361:21): [True: 0, False: 0]
362
0
            }
363
0
            assert(ancestors == depgraph.Ancestors(i));
  Branch (363:13): [True: 0, False: 0]
364
365
            // Initialize the set of descendants with just the current transaction itself.
366
0
            SetType descendants = SetType::Singleton(i);
367
            // Iteratively add children of all transactions in the descendant set to itself.
368
0
            while (true) {
  Branch (368:20): [Folded - Ignored]
369
0
                const auto old_descendants = descendants;
370
0
                for (auto j : descendants) descendants |= children[j];
  Branch (370:29): [True: 0, False: 0]
371
                // Stop when no more changes are being made.
372
0
                if (old_descendants == descendants) break;
  Branch (372:21): [True: 0, False: 0]
373
0
            }
374
0
            assert(descendants == depgraph.Descendants(i));
  Branch (374:13): [True: 0, False: 0]
375
0
        }
376
0
    }
377
0
}
378
379
/** Perform a sanity check on a linearization. */
380
template<typename SetType>
381
void SanityCheck(const DepGraph<SetType>& depgraph, std::span<const DepGraphIndex> linearization)
382
0
{
383
    // Check completeness.
384
0
    assert(linearization.size() == depgraph.TxCount());
  Branch (384:5): [True: 0, False: 0]
385
0
    SetType done;
386
0
    for (auto i : linearization) {
  Branch (386:17): [True: 0, False: 0]
387
        // Check transaction position is in range.
388
0
        assert(depgraph.Positions()[i]);
  Branch (388:9): [True: 0, False: 0]
389
        // Check topology and lack of duplicates.
390
0
        assert((depgraph.Ancestors(i) - done) == SetType::Singleton(i));
  Branch (390:9): [True: 0, False: 0]
391
0
        done.Set(i);
392
0
    }
393
0
}
394
395
inline uint64_t MaxOptimalLinearizationCost(DepGraphIndex cluster_count)
396
0
{
397
    // These are the largest numbers seen returned as cost by Linearize(), in a large randomized
398
    // trial. There exist almost certainly far worse cases, but they are unlikely to be
399
    // encountered in randomized tests. The purpose of these numbers is guaranteeing that for
400
    // *some* reasonable cost bound, optimal linearizations are always found.
401
0
    static constexpr uint64_t COSTS[65] = {
402
0
        0,
403
0
        0, 545, 928, 1633, 2647, 4065, 5598, 8258,
404
0
        9505, 11471, 14137, 19553, 20460, 26191, 28397, 32599,
405
0
        41631, 47419, 56329, 57767, 72196, 63652, 95366, 96537,
406
0
        115653, 125407, 131734, 145090, 156349, 164665, 194224, 203953,
407
0
        207710, 225878, 239971, 252284, 256534, 222142, 251332, 357098,
408
0
        325788, 295867, 410053, 497483, 533892, 576572, 577845, 572400,
409
0
        592536, 455082, 609249, 659130, 714091, 544507, 718788, 562378,
410
0
        601926, 1025081, 732725, 708896, 738224, 900445, 1092519, 1139946
411
0
    };
412
0
    assert(cluster_count < std::size(COSTS));
  Branch (412:5): [True: 0, False: 0]
413
    // Multiply the table number by two, to account for the fact that they are not absolutes.
414
0
    return COSTS[cluster_count] * 2;
415
0
}
416
417
} // namespace cluster_linearize
418
419
#endif // BITCOIN_TEST_UTIL_CLUSTER_LINEARIZE_H