Bitcoin Core  28.1.0
P2P Digital Currency
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 
20 namespace {
21 
22 using namespace cluster_linearize;
23 
24 using TestBitSet = BitSet<32>;
25 
27 template<typename SetType>
28 bool 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 
102 struct 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 
258 template<typename SetType>
259 void 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 
299 template<typename SetType>
300 void 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 
336 template<typename SetType>
337 void 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
#define VARINT(obj)
Definition: serialize.h:498
int64_t fee
Definition: feefrac.h:63
ClusterIndex AddTransaction(const FeeFrac &feefrac) noexcept
Add a new unconnected transaction to this transaction graph (at the end), and return its ClusterIndex...
assert(!tx.IsCoinBase())
uint32_t ClusterIndex
Data type to represent transaction indices in clusters.
const SetType & Descendants(ClusterIndex i) const noexcept
Get the descendants of a given transaction i.
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:100
#define VARINT_MODE(obj, mode)
Definition: serialize.h:497
void AddDependency(ClusterIndex parent, ClusterIndex child) noexcept
Modify this transaction graph, adding a dependency between a specified parent and child...
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:525
int32_t size
Definition: feefrac.h:64
Data structure that holds a transaction graph&#39;s preprocessed data (fee, size, ancestors, descendants).
const FeeFrac & FeeRate(ClusterIndex i) const noexcept
Get the feerate of a given transaction i.
std::vector< std::pair< FeeFrac, SetType > > Cluster
Data type to represent cluster input.
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.