Bitcoin Core 31.99.0
P2P Digital Currency
coinscache_sim.cpp
Go to the documentation of this file.
1// Copyright (c) 2023-present 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#include <coins.h>
6#include <crypto/sha256.h>
8#include <primitives/block.h>
11#include <test/fuzz/fuzz.h>
12#include <test/fuzz/util.h>
14#include <util/threadpool.h>
15
16#include <cassert>
17#include <cstdint>
18#include <memory>
19#include <optional>
20#include <vector>
21
22namespace {
23
25constexpr uint32_t NUM_OUTPOINTS = 256;
27constexpr uint32_t NUM_COINS = 256;
29constexpr uint32_t MAX_CACHES = 4;
31using coinidx_type = uint8_t;
32
33struct PrecomputedData
34{
36 COutPoint outpoints[NUM_OUTPOINTS];
37
39 Coin coins[NUM_COINS];
40
42 CBlock block;
43
44 PrecomputedData()
45 {
46 static const uint8_t PREFIX_O[1] = {'o'};
47 static const uint8_t PREFIX_S[1] = {'s'};
48 static const uint8_t PREFIX_M[1] = {'m'};
50 CMutableTransaction coinbase;
51 coinbase.vin.emplace_back();
52 block.vtx.push_back(MakeTransactionRef(coinbase));
53
55 for (uint32_t i = 0; i < NUM_OUTPOINTS; ++i) {
56 uint32_t idx = (i * 1200U) >> 12; /* Map 3 or 4 entries to same txid. */
57 const uint8_t ser[4] = {uint8_t(idx), uint8_t(idx >> 8), uint8_t(idx >> 16), uint8_t(idx >> 24)};
58 uint256 txid;
59 CSHA256().Write(PREFIX_O, 1).Write(ser, sizeof(ser)).Finalize(txid.begin());
60 outpoints[i].hash = Txid::FromUint256(txid);
61 outpoints[i].n = i;
62 tx.vin.emplace_back(outpoints[i]);
63 }
64 block.vtx.push_back(MakeTransactionRef(tx));
65
66 for (uint32_t i = 0; i < NUM_COINS; ++i) {
67 const uint8_t ser[4] = {uint8_t(i), uint8_t(i >> 8), uint8_t(i >> 16), uint8_t(i >> 24)};
68 uint256 hash;
69 CSHA256().Write(PREFIX_S, 1).Write(ser, sizeof(ser)).Finalize(hash.begin());
70 /* Convert hash to scriptPubkeys (of different lengths, so SanityCheck's cached memory
71 * usage check has a chance to detect mismatches). */
72 switch (i % 5U) {
73 case 0: /* P2PKH */
74 coins[i].out.scriptPubKey.resize(25);
75 coins[i].out.scriptPubKey[0] = OP_DUP;
76 coins[i].out.scriptPubKey[1] = OP_HASH160;
77 coins[i].out.scriptPubKey[2] = 20;
78 std::copy(hash.begin(), hash.begin() + 20, coins[i].out.scriptPubKey.begin() + 3);
79 coins[i].out.scriptPubKey[23] = OP_EQUALVERIFY;
80 coins[i].out.scriptPubKey[24] = OP_CHECKSIG;
81 break;
82 case 1: /* P2SH */
83 coins[i].out.scriptPubKey.resize(23);
84 coins[i].out.scriptPubKey[0] = OP_HASH160;
85 coins[i].out.scriptPubKey[1] = 20;
86 std::copy(hash.begin(), hash.begin() + 20, coins[i].out.scriptPubKey.begin() + 2);
87 coins[i].out.scriptPubKey[22] = OP_EQUAL;
88 break;
89 case 2: /* P2WPKH */
90 coins[i].out.scriptPubKey.resize(22);
91 coins[i].out.scriptPubKey[0] = OP_0;
92 coins[i].out.scriptPubKey[1] = 20;
93 std::copy(hash.begin(), hash.begin() + 20, coins[i].out.scriptPubKey.begin() + 2);
94 break;
95 case 3: /* P2WSH */
96 coins[i].out.scriptPubKey.resize(34);
97 coins[i].out.scriptPubKey[0] = OP_0;
98 coins[i].out.scriptPubKey[1] = 32;
99 std::copy(hash.begin(), hash.begin() + 32, coins[i].out.scriptPubKey.begin() + 2);
100 break;
101 case 4: /* P2TR */
102 coins[i].out.scriptPubKey.resize(34);
103 coins[i].out.scriptPubKey[0] = OP_1;
104 coins[i].out.scriptPubKey[1] = 32;
105 std::copy(hash.begin(), hash.begin() + 32, coins[i].out.scriptPubKey.begin() + 2);
106 break;
107 }
108 /* Hash again to construct nValue and fCoinBase. */
109 CSHA256().Write(PREFIX_M, 1).Write(ser, sizeof(ser)).Finalize(hash.begin());
110 coins[i].out.nValue = CAmount(hash.GetUint64(0) % MAX_MONEY);
111 coins[i].fCoinBase = (hash.GetUint64(1) & 7) == 0;
112 coins[i].nHeight = 0; /* Real nHeight used in simulation is set dynamically. */
113 }
114 }
115};
116
117enum class EntryType : uint8_t
118{
119 /* This entry in the cache does not exist (so we'd have to look in the parent cache). */
120 NONE,
121
122 /* This entry in the cache corresponds to an unspent coin. */
123 UNSPENT,
124
125 /* This entry in the cache corresponds to a spent coin. */
126 SPENT,
127};
128
129struct CacheEntry
130{
131 /* Type of entry. */
132 EntryType entrytype;
133
134 /* Index in the coins array this entry corresponds to (only if entrytype == UNSPENT). */
135 coinidx_type coinidx;
136
137 /* nHeight value for this entry (so the coins[coinidx].nHeight value is ignored; only if entrytype == UNSPENT). */
138 uint32_t height;
139};
140
141struct CacheLevel
142{
143 CacheEntry entry[NUM_OUTPOINTS];
144
145 void Wipe() {
146 for (uint32_t i = 0; i < NUM_OUTPOINTS; ++i) {
147 entry[i].entrytype = EntryType::NONE;
148 }
149 }
150};
151
156class CoinsViewBottom final : public CoinsViewEmpty
157{
158 std::map<COutPoint, Coin> m_data;
159
160public:
161 std::optional<Coin> GetCoin(const COutPoint& outpoint) const final
162 {
163 if (auto it{m_data.find(outpoint)}; it != m_data.end()) {
164 assert(!it->second.IsSpent());
165 return it->second;
166 }
167 return std::nullopt;
168 }
169
170 void BatchWrite(CoinsViewCacheCursor& cursor, const uint256&) final
171 {
172 for (auto it{cursor.Begin()}; it != cursor.End(); it = cursor.NextAndMaybeErase(*it)) {
173 if (it->second.IsDirty()) {
174 if (it->second.coin.IsSpent()) {
175 m_data.erase(it->first);
176 } else {
177 if (cursor.WillErase(*it)) {
178 m_data[it->first] = std::move(it->second.coin);
179 } else {
180 m_data[it->first] = it->second.coin;
181 }
182 }
183 } else {
184 /* For non-dirty entries being written, compare them with what we have. */
185 auto it2 = m_data.find(it->first);
186 if (it->second.coin.IsSpent()) {
187 assert(it2 == m_data.end());
188 } else {
189 assert(it2 != m_data.end());
190 assert(it->second.coin.out == it2->second.out);
191 assert(it->second.coin.fCoinBase == it2->second.fCoinBase);
192 assert(it->second.coin.nHeight == it2->second.nHeight);
193 }
194 }
195 }
196 }
197};
198
199// Hold a non-movable ResetGuard on the heap so StartFetching can remain active
200// for the lifetime of a CoinsViewOverlay cache level.
201struct OverlayFetchScope
202{
204 OverlayFetchScope(CoinsViewOverlay& view, const CBlock& block) : guard(view.StartFetching(block)) {}
205};
206
207// Reuse a single global thread pool across fuzz iterations. Creating and destroying a pool every
208// iteration leaks memory, since iterations can run faster than the OS can tear down the threads.
209std::shared_ptr<ThreadPool> g_thread_pool{std::make_shared<ThreadPool>("cache_fuzz")};
210
212{
213 if (!g_thread_pool->WorkersCount()) g_thread_pool->Start(DEFAULT_PREVOUTFETCH_THREADS);
214}
215
216} // namespace
217
218FUZZ_TARGET(coinscache_sim, .init = [] { static auto setup{MakeNoLogFileContext<>()}; })
219{
223 static const PrecomputedData data;
224
226 CoinsViewBottom bottom;
228 std::vector<std::unique_ptr<CCoinsViewCache>> caches;
230 std::vector<std::unique_ptr<OverlayFetchScope>> fetch_scopes;
232 CacheLevel sim_caches[MAX_CACHES + 1];
234 uint32_t current_height = 1U;
235
236 // Initialize bottom simulated cache.
237 sim_caches[0].Wipe();
238
240 auto lookup = [&](uint32_t outpointidx, int sim_idx = -1) -> std::optional<std::pair<coinidx_type, uint32_t>> {
241 uint32_t cache_idx = sim_idx == -1 ? caches.size() : sim_idx;
242 while (true) {
243 const auto& entry = sim_caches[cache_idx].entry[outpointidx];
244 if (entry.entrytype == EntryType::UNSPENT) {
245 return {{entry.coinidx, entry.height}};
246 } else if (entry.entrytype == EntryType::SPENT) {
247 return std::nullopt;
248 };
249 if (cache_idx == 0) break;
250 --cache_idx;
251 }
252 return std::nullopt;
253 };
254
256 auto flush = [&]() {
257 assert(caches.size() >= 1);
258 auto& cache = sim_caches[caches.size()];
259 auto& prev_cache = sim_caches[caches.size() - 1];
260 for (uint32_t outpointidx = 0; outpointidx < NUM_OUTPOINTS; ++outpointidx) {
261 if (cache.entry[outpointidx].entrytype != EntryType::NONE) {
262 prev_cache.entry[outpointidx] = cache.entry[outpointidx];
263 cache.entry[outpointidx].entrytype = EntryType::NONE;
264 }
265 }
266 };
267
269 const auto make_fetch_scope{[&] {
270 auto& overlay{static_cast<CoinsViewOverlay&>(*caches.back())};
271 return std::make_unique<OverlayFetchScope>(overlay, data.block);
272 }};
273
274 // Main simulation loop: read commands from the fuzzer input, and apply them
275 // to both the real cache stack and the simulation.
276 FuzzedDataProvider provider(buffer.data(), buffer.size());
278 // Every operation (except "Change height") moves current height forward,
279 // so it functions as a kind of epoch, making ~all UTXOs unique.
281 // Make sure there is always at least one CCoinsViewCache.
282 if (caches.empty()) {
283 caches.emplace_back(new CCoinsViewCache(&bottom, /*deterministic=*/true));
284 fetch_scopes.emplace_back();
285 sim_caches[caches.size()].Wipe();
286 }
287 assert(caches.size() == fetch_scopes.size());
288
289 // Execute command.
290 CallOneOf(
291 provider,
292
293 [&]() { // PeekCoin/GetCoin
294 uint32_t outpointidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_OUTPOINTS - 1);
295 // Look up in simulation data.
296 auto sim = lookup(outpointidx);
297 // Look up in real caches.
298 auto realcoin = provider.ConsumeBool() ?
299 caches.back()->PeekCoin(data.outpoints[outpointidx]) :
300 caches.back()->GetCoin(data.outpoints[outpointidx]);
301 // Compare results.
302 if (!sim.has_value()) {
303 assert(!realcoin);
304 } else {
305 assert(realcoin && !realcoin->IsSpent());
306 const auto& simcoin = data.coins[sim->first];
307 assert(realcoin->out == simcoin.out);
308 assert(realcoin->fCoinBase == simcoin.fCoinBase);
309 assert(realcoin->nHeight == sim->second);
310 }
311 },
312
313 [&]() { // HaveCoin
314 uint32_t outpointidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_OUTPOINTS - 1);
315 // Look up in simulation data.
316 auto sim = lookup(outpointidx);
317 // Look up in real caches.
318 auto real = caches.back()->HaveCoin(data.outpoints[outpointidx]);
319 // Compare results.
320 assert(sim.has_value() == real);
321 },
322
323 [&]() { // HaveCoinInCache
324 uint32_t outpointidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_OUTPOINTS - 1);
325 // Invoke on real cache (there is no equivalent in simulation, so nothing to compare result with).
326 (void)caches.back()->HaveCoinInCache(data.outpoints[outpointidx]);
327 },
328
329 [&]() { // AccessCoin
330 uint32_t outpointidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_OUTPOINTS - 1);
331 // Look up in simulation data.
332 auto sim = lookup(outpointidx);
333 // Look up in real caches.
334 const auto& realcoin = caches.back()->AccessCoin(data.outpoints[outpointidx]);
335 // Compare results.
336 if (!sim.has_value()) {
337 assert(realcoin.IsSpent());
338 } else {
339 assert(!realcoin.IsSpent());
340 const auto& simcoin = data.coins[sim->first];
341 assert(simcoin.out == realcoin.out);
342 assert(simcoin.fCoinBase == realcoin.fCoinBase);
343 assert(realcoin.nHeight == sim->second);
344 }
345 },
346
347 [&]() { // AddCoin (only possible_overwrite if necessary)
348 uint32_t outpointidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_OUTPOINTS - 1);
349 uint32_t coinidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_COINS - 1);
350 // Look up in simulation data (to know whether we must set possible_overwrite or not).
351 auto sim = lookup(outpointidx);
352 // Invoke on real caches.
353 Coin coin = data.coins[coinidx];
354 coin.nHeight = current_height;
355 caches.back()->AddCoin(data.outpoints[outpointidx], std::move(coin), sim.has_value());
356 // Apply to simulation data.
357 auto& entry = sim_caches[caches.size()].entry[outpointidx];
358 entry.entrytype = EntryType::UNSPENT;
359 entry.coinidx = coinidx;
360 entry.height = current_height;
361 },
362
363 [&]() { // AddCoin (always possible_overwrite)
364 uint32_t outpointidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_OUTPOINTS - 1);
365 uint32_t coinidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_COINS - 1);
366 // Invoke on real caches.
367 Coin coin = data.coins[coinidx];
368 coin.nHeight = current_height;
369 caches.back()->AddCoin(data.outpoints[outpointidx], std::move(coin), true);
370 // Apply to simulation data.
371 auto& entry = sim_caches[caches.size()].entry[outpointidx];
372 entry.entrytype = EntryType::UNSPENT;
373 entry.coinidx = coinidx;
374 entry.height = current_height;
375 },
376
377 [&]() { // SpendCoin (moveto = nullptr)
378 uint32_t outpointidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_OUTPOINTS - 1);
379 // Invoke on real caches.
380 caches.back()->SpendCoin(data.outpoints[outpointidx], nullptr);
381 // Apply to simulation data.
382 sim_caches[caches.size()].entry[outpointidx].entrytype = EntryType::SPENT;
383 },
384
385 [&]() { // SpendCoin (with moveto)
386 uint32_t outpointidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_OUTPOINTS - 1);
387 // Look up in simulation data (to compare the returned *moveto with).
388 auto sim = lookup(outpointidx);
389 // Invoke on real caches.
390 Coin realcoin;
391 caches.back()->SpendCoin(data.outpoints[outpointidx], &realcoin);
392 // Apply to simulation data.
393 sim_caches[caches.size()].entry[outpointidx].entrytype = EntryType::SPENT;
394 // Compare *moveto with the value expected based on simulation data.
395 if (!sim.has_value()) {
396 assert(realcoin.IsSpent());
397 } else {
398 assert(!realcoin.IsSpent());
399 const auto& simcoin = data.coins[sim->first];
400 assert(simcoin.out == realcoin.out);
401 assert(simcoin.fCoinBase == realcoin.fCoinBase);
402 assert(realcoin.nHeight == sim->second);
403 }
404 },
405
406 [&]() { // Uncache
407 uint32_t outpointidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_OUTPOINTS - 1);
408 // Apply to real caches (there is no equivalent in our simulation).
409 caches.back()->Uncache(data.outpoints[outpointidx]);
410 },
411
412 [&]() { // Add a cache level (if not already at the max).
413 if (caches.size() != MAX_CACHES) {
414 // Apply to real caches.
415 if (provider.ConsumeBool()) {
416 caches.emplace_back(new CCoinsViewCache(&*caches.back(), /*deterministic=*/true));
417 fetch_scopes.emplace_back();
418 } else {
419 caches.emplace_back(new CoinsViewOverlay(&*caches.back(), g_thread_pool, /*deterministic=*/true));
420 fetch_scopes.emplace_back(make_fetch_scope());
421 }
422 // Apply to simulation data.
423 sim_caches[caches.size()].Wipe();
424 }
425 },
426
427 [&]() { // Remove a cache level.
428 // Apply to real caches (this reduces caches.size(), implicitly doing the same on the simulation data).
429 caches.back()->SanityCheck();
430 fetch_scopes.pop_back();
431 caches.pop_back();
432 },
433
434 [&]() { // Flush.
435 // CoinsViewOverlay::Flush() must have all inputs consumed before being called
436 if (auto* overlay{dynamic_cast<CoinsViewOverlay*>(caches.back().get())};
437 overlay && !overlay->AllInputsConsumed()) {
438 return;
439 }
440 // Apply to simulation data.
441 flush();
442 // Apply to real caches.
443 caches.back()->Flush(/*reallocate_cache=*/provider.ConsumeBool());
444 },
445
446 [&]() { // Sync.
447 if (fetch_scopes.back()) return; // CoinsViewOverlay::Sync() is never called in production
448 // Apply to simulation data (note that in our simulation, syncing and flushing is the same thing).
449 flush();
450 // Apply to real caches.
451 caches.back()->Sync();
452 },
453
454 [&]() { // Reset.
455 sim_caches[caches.size()].Wipe();
456 // Apply to real caches. Optionally start fetching again.
457 if (fetch_scopes.back() && provider.ConsumeBool()) {
458 fetch_scopes.back().reset(); // Stop fetching before starting again.
460 } else {
461 (void)caches.back()->CreateResetGuard();
462 }
463 },
464
465 [&]() { // GetCacheSize
466 (void)caches.back()->GetCacheSize();
467 },
468
469 [&]() { // DynamicMemoryUsage
470 (void)caches.back()->DynamicMemoryUsage();
471 },
472
473 [&]() { // Change height
475 }
476 );
477 }
478
479 // Sanity check all the remaining caches
480 for (const auto& cache : caches) {
481 cache->SanityCheck();
482 }
483
484 // Full comparison between caches and simulation data, from bottom to top,
485 for (unsigned sim_idx = 1; sim_idx <= caches.size(); ++sim_idx) {
486 auto& cache = *caches[sim_idx - 1];
487 size_t cache_size = 0;
488
489 for (uint32_t outpointidx = 0; outpointidx < NUM_OUTPOINTS; ++outpointidx) {
490 cache_size += cache.HaveCoinInCache(data.outpoints[outpointidx]);
491 const auto real{cache.PeekCoin(data.outpoints[outpointidx])};
492 auto sim = lookup(outpointidx, sim_idx);
493 if (!sim.has_value()) {
494 assert(!real);
495 } else {
496 assert(!real->IsSpent());
497 assert(real->out == data.coins[sim->first].out);
498 assert(real->fCoinBase == data.coins[sim->first].fCoinBase);
499 assert(real->nHeight == sim->second);
500 }
501 }
502
503 // HaveCoinInCache ignores spent coins, so GetCacheSize() may exceed it.
504 assert(cache.GetCacheSize() >= cache_size);
505 }
506
507 // Compare the bottom coinsview (not a CCoinsViewCache) with sim_cache[0].
508 for (uint32_t outpointidx = 0; outpointidx < NUM_OUTPOINTS; ++outpointidx) {
509 auto realcoin = bottom.GetCoin(data.outpoints[outpointidx]);
510 auto sim = lookup(outpointidx, 0);
511 if (!sim.has_value()) {
512 assert(!realcoin);
513 } else {
514 assert(realcoin && !realcoin->IsSpent());
515 assert(realcoin->out == data.coins[sim->first].out);
516 assert(realcoin->fCoinBase == data.coins[sim->first].fCoinBase);
517 assert(realcoin->nHeight == sim->second);
518 }
519 }
520
521 // Tear down the fetch scopes top down. Otherwise lower level could reset while upper level is reading from it.
522 while (!fetch_scopes.empty()) fetch_scopes.pop_back();
523}
constexpr CAmount MAX_MONEY
No amount larger than this (in satoshi) is valid.
Definition: amount.h:26
int64_t CAmount
Amount in satoshis (Can be negative)
Definition: amount.h:12
constexpr int32_t DEFAULT_PREVOUTFETCH_THREADS
Definition: block.h:74
std::vector< CTransactionRef > vtx
Definition: block.h:77
CCoinsView that adds a memory cache for transactions to another CCoinsView.
Definition: coins.h:437
An outpoint - a combination of a transaction hash and an index n into its vout.
Definition: transaction.h:29
uint32_t n
Definition: transaction.h:32
Txid hash
Definition: transaction.h:31
A hasher class for SHA-256.
Definition: sha256.h:14
void Finalize(unsigned char hash[OUTPUT_SIZE])
Definition: sha256.cpp:725
CSHA256 & Write(const unsigned char *data, size_t len)
Definition: sha256.cpp:699
CScript scriptPubKey
Definition: transaction.h:143
CAmount nValue
Definition: transaction.h:142
A UTXO entry.
Definition: coins.h:46
CTxOut out
unspent transaction output
Definition: coins.h:49
bool IsSpent() const
Either this coin never existed (see e.g.
Definition: coins.h:94
bool fCoinBase
whether containing transaction was a coinbase
Definition: coins.h:52
uint32_t nHeight
at which height this containing transaction was included in the active block chain
Definition: coins.h:55
Noop coins view.
Definition: coins.h:392
void BatchWrite(CoinsViewCacheCursor &cursor, const uint256 &) override
Do a bulk modification (multiple Coin changes + BestBlock change).
Definition: coins.h:407
std::optional< Coin > GetCoin(const COutPoint &) const override
Retrieve the Coin (unspent transaction output) for a given outpoint.
Definition: coins.h:402
CCoinsViewCache subclass that asynchronously fetches most block input prevouts in parallel during Con...
Definition: coins.h:656
bool AllInputsConsumed() const noexcept
Verify that all parallel fetched input prevouts have been consumed.
Definition: coins.h:783
T ConsumeIntegralInRange(T min, T max)
constexpr uint64_t GetUint64(int pos) const
Definition: uint256.h:109
constexpr unsigned char * begin()
Definition: uint256.h:101
void resize(size_type new_size)
Definition: prevector.h:276
static transaction_identifier FromUint256(const uint256 &id)
256-bit opaque blob.
Definition: uint256.h:196
constexpr CAmount SPENT
std::vector< std::unique_ptr< CCoinsViewCache > > caches
Real CCoinsViewCache objects.
uint32_t current_height
Current height in the simulation.
CacheLevel sim_caches[MAX_CACHES+1]
Simulated cache data (sim_caches[0] matches bottom, sim_caches[i+1] matches caches[i]).
static const PrecomputedData data
Precomputed COutPoint and CCoins values.
auto flush
Flush changes in top cache to the one below.
sim_caches[0] Wipe()
LIMITED_WHILE(provider.remaining_bytes(), 10000)
std::vector< std::unique_ptr< OverlayFetchScope > > fetch_scopes
Long-lived StartFetching guards, parallel to caches (entries are nullptr unless corresponding level i...
const auto make_fetch_scope
Helper creating a fetch scope for the top cache (which must be a CoinsViewOverlay).
StartPoolIfNeeded()
CoinsViewBottom bottom
Dummy coinsview instance (base of the hierarchy).
auto lookup
Helper lookup function in the simulated cache stack.
FuzzedDataProvider provider(buffer.data(), buffer.size())
#define FUZZ_TARGET(...)
Definition: fuzz.h:35
@ NONE
Definition: categories.h:15
Definition: basic.cpp:11
static CTransactionRef MakeTransactionRef(Tx &&txIn)
Definition: transaction.h:404
@ OP_CHECKSIG
Definition: script.h:191
@ OP_EQUAL
Definition: script.h:147
@ OP_DUP
Definition: script.h:126
@ OP_HASH160
Definition: script.h:188
@ OP_1
Definition: script.h:84
@ OP_0
Definition: script.h:77
@ OP_EQUALVERIFY
Definition: script.h:148
A mutable version of CTransaction.
Definition: transaction.h:358
std::vector< CTxIn > vin
Definition: transaction.h:359
Cursor for iterating over the linked list of flagged entries in CCoinsViewCache.
Definition: coins.h:309
SeedRandomStateForTest(SeedRand::ZEROS)
size_t CallOneOf(FuzzedDataProvider &fuzzed_data_provider, Callables... callables)
Definition: util.h:37
@ ZEROS
Seed with a compile time constant of zeros.
static int setup(void)
Definition: tests.c:8154
assert(!tx.IsCoinBase())