Coverage Report

Created: 2026-09-15 16:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/root/bitcoin/src/test/fuzz/block_template_manager.cpp
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
#include <node/block_template_manager.h>
6
7
#include <consensus/amount.h>
8
#include <consensus/consensus.h>
9
#include <consensus/validation.h>
10
#include <interfaces/types.h>
11
#include <kernel/mempool_entry.h>
12
#include <node/miner.h>
13
#include <node/mining_args.h>
14
#include <node/mining_types.h>
15
#include <policy/feerate.h>
16
#include <policy/policy.h>
17
#include <pow.h>
18
#include <primitives/block.h>
19
#include <primitives/transaction.h>
20
#include <test/fuzz/FuzzedDataProvider.h>
21
#include <test/fuzz/fuzz.h>
22
#include <test/fuzz/util.h>
23
#include <test/fuzz/util/mempool.h>
24
#include <test/util/mining.h>
25
#include <test/util/random.h>
26
#include <test/util/script.h>
27
#include <test/util/setup_common.h>
28
#include <test/util/txmempool.h>
29
#include <txmempool.h>
30
#include <uint256.h>
31
#include <util/time.h>
32
33
#include <algorithm>
34
#include <cassert>
35
#include <chrono>
36
#include <cstddef>
37
#include <cstdint>
38
#include <memory>
39
#include <numeric>
40
#include <optional>
41
#include <stdexcept>
42
#include <string>
43
#include <utility>
44
#include <vector>
45
46
using node::BlockCreateOptions;
47
48
namespace {
49
struct SpendableCoin {
50
    COutPoint outpoint;
51
    CAmount amount;
52
    bool is_coinbase;
53
};
54
const TestingSetup* g_setup;
55
std::vector<SpendableCoin> g_mature_coinbase_outpoints;
56
57
void initialize_block_template_manager()
58
0
{
59
0
    static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>();
60
0
    g_setup = testing_setup.get();
61
0
    SetMockTime(WITH_LOCK(g_setup->m_node.chainman->GetMutex(),
62
0
                          return g_setup->m_node.chainman->ActiveTip()->Time()));
63
0
    const BlockCreateOptions assembler_options{.coinbase_output_script = P2WSH_OP_TRUE};
64
0
    for (int i{0}; i < 2 * COINBASE_MATURITY; ++i) {
  Branch (64:20): [True: 0, False: 0]
65
0
        COutPoint prevout{MineBlock(g_setup->m_node, assembler_options)};
66
0
        if (i < COINBASE_MATURITY) {
  Branch (66:13): [True: 0, False: 0]
67
0
            LOCK(::cs_main);
68
0
            const auto coin{g_setup->m_node.chainman->ActiveChainstate().CoinsTip().GetCoin(prevout)};
69
0
            assert(coin);
  Branch (69:13): [True: 0, False: 0]
70
0
            g_mature_coinbase_outpoints.push_back({prevout, coin->out.nValue, /*is_coinbase=*/true});
71
0
        }
72
0
    }
73
0
    g_setup->m_node.validation_signals->SyncWithValidationInterfaceQueue();
74
0
}
75
76
/** Add random transactions to the mempool, bypassing validation. */
77
void PopulateRandTransactionsToMempool(FuzzedDataProvider& fuzzed_data_provider, CTxMemPool& mempool, int weight_budget)
78
1.62k
{
79
3.05k
    while (weight_budget > 0 && fuzzed_data_provider.remaining_bytes() > 0) {
  Branch (79:12): [True: 2.47k, False: 586]
  Branch (79:33): [True: 2.20k, False: 265]
80
2.20k
        const std::optional<CMutableTransaction> mutable_tx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider, TX_WITH_WITNESS);
81
2.20k
        if (!mutable_tx) break;
  Branch (81:13): [True: 732, False: 1.47k]
82
1.47k
        auto tx = MakeTransactionRef(*mutable_tx);
83
1.47k
        CTxMemPoolEntry mempool_entry{ConsumeTxMemPoolEntry(fuzzed_data_provider, *tx)};
84
1.47k
        const Txid txid = tx->GetHash();
85
1.47k
        if (mempool.exists(txid)) continue;
  Branch (85:13): [True: 146, False: 1.33k]
86
1.33k
        const int32_t tx_weight = mempool_entry.GetTxWeight();
87
1.33k
        if (tx_weight > weight_budget) break;
  Branch (87:13): [True: 40, False: 1.29k]
88
1.29k
        TryAddToMempool(mempool, mempool_entry);
89
1.29k
        weight_budget -= tx_weight;
90
1.29k
    }
91
1.62k
}
92
93
/** Add transactions with existing inputs and valid scripts, bypassing mempool validation. */
94
void PopulateResolvableTransactionsToMempool(FuzzedDataProvider& fuzzed_data_provider,
95
                                             CTxMemPool& mempool,
96
                                             int weight_budget,
97
                                             std::vector<SpendableCoin>& spendable_outpoints)
98
2.77k
{
99
52.8k
    while (weight_budget > 0 && !spendable_outpoints.empty() && fuzzed_data_provider.remaining_bytes() > 0) {
  Branch (99:12): [True: 52.6k, False: 254]
  Branch (99:33): [True: 52.6k, False: 18]
  Branch (99:65): [True: 50.8k, False: 1.74k]
100
50.8k
        CMutableTransaction tx_mut;
101
50.8k
        tx_mut.nLockTime = fuzzed_data_provider.ConsumeBool() ? 0 : fuzzed_data_provider.ConsumeIntegral<uint32_t>();
  Branch (101:28): [True: 46.3k, False: 4.52k]
102
        // Small fan-in keeps the outpoint pool alive, so many transactions form layered clusters.
103
50.8k
        const auto num_in = fuzzed_data_provider.ConsumeIntegralInRange<int>(1, std::min<size_t>(5, spendable_outpoints.size()));
104
50.8k
        const auto num_out = fuzzed_data_provider.ConsumeIntegralInRange<int>(1, num_in * 2);
105
50.8k
        std::vector<SpendableCoin> consumed_outpoints;
106
50.8k
        CAmount amount_in{0};
107
50.8k
        bool spends_coinbase{false};
108
133k
        for (int i{0}; i < num_in; ++i) {
  Branch (108:24): [True: 82.4k, False: 50.8k]
109
82.4k
            const size_t outpoint_index{
110
82.4k
                fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, spendable_outpoints.size() - 1)};
111
82.4k
            const auto [prevout, amount, is_coinbase]{spendable_outpoints[outpoint_index]};
112
82.4k
            spendable_outpoints.erase(spendable_outpoints.begin() + outpoint_index);
113
82.4k
            consumed_outpoints.push_back({prevout, amount, is_coinbase});
114
82.4k
            amount_in += amount;
115
82.4k
            spends_coinbase |= is_coinbase;
116
82.4k
            CTxIn in;
117
82.4k
            in.prevout = prevout;
118
            // The assembler checks locktime finality but not BIP68, so keep relative locktime disabled.
119
82.4k
            in.nSequence = ConsumeSequence(fuzzed_data_provider) | CTxIn::SEQUENCE_LOCKTIME_DISABLE_FLAG;
120
82.4k
            in.scriptWitness.stack = {WITNESS_STACK_ELEM_OP_TRUE};
121
82.4k
            tx_mut.vin.push_back(in);
122
82.4k
        }
123
        // A bare P2PK output adds legacy sigops without needing a valid signature.
124
50.8k
        const bool add_sigops{fuzzed_data_provider.ConsumeBool()};
125
50.8k
        const CAmount fee{fuzzed_data_provider.ConsumeIntegralInRange<CAmount>(0, amount_in)};
126
50.8k
        const CAmount amount_out{(amount_in - fee) / num_out};
127
189k
        for (int i{0}; i < num_out; ++i) {
  Branch (127:24): [True: 139k, False: 50.8k]
128
139k
            if (i == 0 && add_sigops) {
  Branch (128:17): [True: 50.8k, False: 88.2k]
  Branch (128:27): [True: 45.3k, False: 5.55k]
129
45.3k
                tx_mut.vout.emplace_back(amount_out, CScript() << std::vector<unsigned char>(33, 0x02) << OP_CHECKSIG);
130
93.7k
            } else {
131
93.7k
                tx_mut.vout.emplace_back(amount_out, P2WSH_OP_TRUE);
132
93.7k
            }
133
139k
        }
134
50.8k
        CTransactionRef tx{MakeTransactionRef(tx_mut)};
135
        // The output division remainder also goes to fees.
136
50.8k
        const CAmount actual_fee{amount_in - amount_out * num_out};
137
50.8k
        TestMemPoolEntryHelper entry;
138
50.8k
        CTxMemPoolEntry mempool_entry{
139
50.8k
            entry.Fee(actual_fee).SpendsCoinbase(spends_coinbase).SigOpsCost(add_sigops ? WITNESS_SCALE_FACTOR : 0).FromTx(tx)};
  Branch (139:78): [True: 45.3k, False: 5.55k]
140
50.8k
        const int32_t tx_weight{mempool_entry.GetTxWeight()};
141
50.8k
        if (tx_weight > weight_budget) {
  Branch (141:13): [True: 757, False: 50.1k]
142
757
            spendable_outpoints.insert(spendable_outpoints.end(), consumed_outpoints.begin(), consumed_outpoints.end());
143
757
            break;
144
757
        }
145
50.1k
        TryAddToMempool(mempool, mempool_entry);
146
        // Only offer the outputs for spending when the mempool accepted the transaction.
147
50.1k
        if (!mempool.exists(tx->GetHash())) {
  Branch (147:13): [True: 1.65k, False: 48.4k]
148
1.65k
            spendable_outpoints.insert(spendable_outpoints.end(), consumed_outpoints.begin(), consumed_outpoints.end());
149
1.65k
            continue;
150
1.65k
        }
151
48.4k
        weight_budget -= tx_weight;
152
137k
        for (uint32_t n{add_sigops ? 1u : 0u}; n < tx->vout.size(); ++n) {
  Branch (152:25): [True: 43.2k, False: 5.18k]
  Branch (152:48): [True: 89.4k, False: 48.4k]
153
89.4k
            spendable_outpoints.push_back({COutPoint{tx->GetHash(), n}, tx->vout[n].nValue, /*is_coinbase=*/false});
154
89.4k
        }
155
48.4k
    }
156
2.77k
}
157
} // namespace
158
159
FUZZ_TARGET(block_template_manager, .init = initialize_block_template_manager)
160
3.86k
{
161
3.86k
    FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
162
3.86k
    const auto& node = g_setup->m_node;
163
3.86k
    auto& block_template_manager = *Assert(node.block_template_manager);
164
3.86k
    auto& mempool = *Assert(node.mempool);
165
3.86k
    SeedRandomStateForTest(SeedRand::ZEROS);
166
3.86k
    SetMockTime(WITH_LOCK(node.chainman->GetMutex(),
167
3.86k
                          return node.chainman->ActiveTip()->Time()));
168
3.86k
    {
169
3.86k
        LOCK(mempool.cs);
170
3.86k
        mempool.TrimToSize(0);
171
3.86k
    }
172
3.86k
    const bool use_valid_transactions{fuzzed_data_provider.ConsumeBool()};
173
3.86k
    std::vector<SpendableCoin> spendable_outpoints;
174
3.86k
    int weight_budget = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, DEFAULT_BLOCK_MAX_WEIGHT * 10);
175
3.86k
    if (use_valid_transactions) {
  Branch (175:9): [True: 2.59k, False: 1.27k]
176
2.59k
        spendable_outpoints = g_mature_coinbase_outpoints;
177
2.59k
        PopulateResolvableTransactionsToMempool(fuzzed_data_provider, mempool, weight_budget, spendable_outpoints);
178
2.59k
    } else {
179
1.27k
        PopulateRandTransactionsToMempool(fuzzed_data_provider, mempool, weight_budget);
180
1.27k
    }
181
6.54k
    LIMITED_WHILE (fuzzed_data_provider.remaining_bytes() > 0, 10) {
182
6.54k
        BlockCreateOptions options;
183
6.54k
        options.test_block_validity = use_valid_transactions;
184
6.54k
        if (fuzzed_data_provider.ConsumeBool()) {
  Branch (184:13): [True: 1.44k, False: 5.10k]
185
1.44k
            options.use_mempool = fuzzed_data_provider.ConsumeBool();
186
1.44k
        }
187
6.54k
        if (fuzzed_data_provider.ConsumeBool()) {
  Branch (187:13): [True: 1.13k, False: 5.41k]
188
1.13k
            options.block_reserved_weight = fuzzed_data_provider.ConsumeIntegral<uint64_t>();
189
1.13k
        }
190
6.54k
        if (fuzzed_data_provider.ConsumeBool()) {
  Branch (190:13): [True: 1.13k, False: 5.41k]
191
1.13k
            const CAmount fee_amount = fuzzed_data_provider.ConsumeIntegralInRange<CAmount>(0, COIN);
192
1.13k
            const int32_t fee_size = fuzzed_data_provider.ConsumeIntegralInRange<int32_t>(1, DEFAULT_BLOCK_MAX_WEIGHT / WITNESS_SCALE_FACTOR);
193
1.13k
            options.block_min_fee_rate = CFeeRate(fee_amount, fee_size);
194
1.13k
        }
195
6.54k
        if (fuzzed_data_provider.ConsumeBool()) {
  Branch (195:13): [True: 1.02k, False: 5.52k]
196
1.02k
            options.block_max_weight = fuzzed_data_provider.ConsumeIntegral<uint64_t>();
197
1.02k
        }
198
6.54k
        if (fuzzed_data_provider.ConsumeBool()) {
  Branch (198:13): [True: 1.41k, False: 5.13k]
199
1.41k
            options.print_modified_fee = fuzzed_data_provider.ConsumeBool();
200
1.41k
        }
201
6.54k
        if (fuzzed_data_provider.ConsumeBool()) {
  Branch (201:13): [True: 1.24k, False: 5.30k]
202
1.24k
            options.coinbase_output_max_additional_sigops = fuzzed_data_provider.ConsumeIntegral<size_t>();
203
1.24k
        }
204
        // An arbitrary coinbase script can exceed consensus weight/sigops limits and fail TestBlockValidity.
205
6.54k
        if (!use_valid_transactions && fuzzed_data_provider.ConsumeBool()) {
  Branch (205:13): [True: 2.61k, False: 3.93k]
  Branch (205:40): [True: 1.44k, False: 1.16k]
206
1.44k
            options.coinbase_output_script = ConsumeScript(fuzzed_data_provider);
207
1.44k
        }
208
6.54k
        const auto merged_options{node::MergeMiningOptions(options, block_template_manager.BlockCreateArgs())};
209
6.54k
        const auto options_check{node::CheckMiningOptions(merged_options, /*use_argnames=*/false)};
210
6.54k
        std::unique_ptr<node::CBlockTemplate> block_template;
211
6.54k
        try {
212
6.54k
            block_template = block_template_manager.CreateNewTemplate(options);
213
6.54k
        } catch (const std::runtime_error& e) {
214
2.15k
            if (!options_check) {
  Branch (214:17): [True: 2.15k, False: 0]
215
2.15k
                assert(e.what() == util::ErrorString(options_check).original);
  Branch (215:17): [True: 2.15k, False: 0]
216
2.15k
                continue;
217
2.15k
            }
218
            // test_block_validity is set only for valid transactions, so TestBlockValidity
219
            // should not fail.
220
0
            throw;
221
2.15k
        }
222
6.54k
        assert(options_check);
  Branch (222:9): [True: 4.39k, False: 0]
223
4.39k
        assert(block_template);
  Branch (223:9): [True: 4.39k, False: 0]
224
4.39k
        const auto resolved{node::FlattenMiningOptions(merged_options)};
225
4.39k
        const CBlock& block{block_template->block};
226
        // Coinbase is first; the per-tx vectors exclude it and track the block.
227
4.39k
        assert(!block.vtx.empty() && block.vtx[0]->IsCoinBase());
  Branch (227:9): [True: 4.39k, False: 0]
  Branch (227:9): [True: 4.39k, False: 0]
  Branch (227:9): [True: 4.39k, False: 0]
228
4.39k
        assert(block_template->vTxFees.size() == block.vtx.size() - 1);
  Branch (228:9): [True: 4.39k, False: 0]
229
4.39k
        assert(block_template->vTxSigOpsCost.size() == block.vtx.size() - 1);
  Branch (229:9): [True: 4.39k, False: 0]
230
        // Reserved weight plus selected transactions stays within the limit. The
231
        // coinbase is covered by the reserved weight, so it is not counted here.
232
4.39k
        uint64_t weight{*resolved.block_reserved_weight};
233
29.0k
        for (size_t i{1}; i < block.vtx.size(); ++i)
  Branch (233:27): [True: 24.6k, False: 4.39k]
234
24.6k
            weight += GetTransactionWeight(*block.vtx[i]);
235
4.39k
        assert(weight <= *resolved.block_max_weight);
  Branch (235:9): [True: 4.39k, False: 0]
236
        // Reserved coinbase sigops plus selected transactions stays within the limit.
237
4.39k
        int64_t sigops = resolved.coinbase_output_max_additional_sigops;
238
4.39k
        for (const int64_t tx_sigops : block_template->vTxSigOpsCost)
  Branch (238:38): [True: 24.6k, False: 4.39k]
239
24.6k
            sigops += tx_sigops;
240
4.39k
        assert(sigops <= MAX_BLOCK_SIGOPS_COST);
  Branch (240:9): [True: 4.39k, False: 0]
241
        // Tracked fees are non-negative; without the mempool only the coinbase is included.
242
4.39k
        for (const CAmount fee : block_template->vTxFees)
  Branch (242:32): [True: 24.6k, False: 4.39k]
243
24.6k
            assert(fee >= 0);
  Branch (243:13): [True: 24.6k, False: 0]
244
4.39k
        if (!resolved.use_mempool) assert(block.vtx.size() == 1);
  Branch (244:13): [True: 249, False: 4.14k]
  Branch (244:36): [True: 249, False: 0]
245
246
4.39k
        const uint256 tip_hash{block.hashPrevBlock};
247
        // The template builds on the current tip.
248
4.39k
        {
249
4.39k
            const std::optional<interfaces::BlockRef> tip{block_template_manager.GetTip()};
250
4.39k
            assert(tip && tip->hash == tip_hash);
  Branch (250:13): [True: 4.39k, False: 0]
  Branch (250:13): [True: 4.39k, False: 0]
  Branch (250:13): [True: 4.39k, False: 0]
251
4.39k
            assert(tip->height == WITH_LOCK(node.chainman->GetMutex(), return node.chainman->ActiveHeight()));
  Branch (251:13): [True: 4.39k, False: 0]
252
4.39k
        }
253
254
        // Zero-timeout waits still check their predicates once.
255
4.39k
        {
256
            // The wait consumes and resets the interrupt flag, so keep a copy of the request.
257
4.39k
            const bool interrupted{fuzzed_data_provider.ConsumeBool()};
258
4.39k
            bool interrupt{false};
259
4.39k
            if (interrupted) block_template_manager.InterruptWait(interrupt);
  Branch (259:17): [True: 722, False: 3.66k]
260
4.39k
            assert(interrupt == interrupted);
  Branch (260:13): [True: 4.39k, False: 0]
261
4.39k
            const uint256 current_tip{fuzzed_data_provider.ConsumeBool() ? tip_hash : ConsumeUInt256(fuzzed_data_provider)};
  Branch (261:39): [True: 1.44k, False: 2.94k]
262
4.39k
            MillisecondsDouble timeout{0};
263
4.39k
            const std::optional<interfaces::BlockRef> wait_tip{block_template_manager.WaitTipChanged(current_tip, timeout, interrupt)};
264
4.39k
            assert(!interrupt);
  Branch (264:13): [True: 4.39k, False: 0]
265
4.39k
            if (interrupted) {
  Branch (265:17): [True: 722, False: 3.66k]
266
722
                assert(!wait_tip);
  Branch (266:17): [True: 722, False: 0]
267
3.66k
            } else {
268
3.66k
                assert(wait_tip && wait_tip->hash == tip_hash);
  Branch (268:17): [True: 3.66k, False: 0]
  Branch (268:17): [True: 3.66k, False: 0]
  Branch (268:17): [True: 3.66k, False: 0]
269
3.66k
            }
270
4.39k
        }
271
4.39k
        {
272
            // Fees or testnet min-difficulty can produce a template without a tip change.
273
4.39k
            if (fuzzed_data_provider.ConsumeBool()) {
  Branch (273:17): [True: 535, False: 3.85k]
274
535
                const int extra_weight_budget{
275
535
                    fuzzed_data_provider.ConsumeIntegralInRange<int>(0, DEFAULT_BLOCK_MAX_WEIGHT)};
276
535
                if (use_valid_transactions) {
  Branch (276:21): [True: 182, False: 353]
277
182
                    PopulateResolvableTransactionsToMempool(fuzzed_data_provider, mempool, extra_weight_budget, spendable_outpoints);
278
353
                } else {
279
353
                    PopulateRandTransactionsToMempool(fuzzed_data_provider, mempool, extra_weight_budget);
280
353
                }
281
535
            }
282
4.39k
            const std::chrono::seconds mock_offset{fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(0, 30 * 60)};
283
4.39k
            SetMockTime(WITH_LOCK(node.chainman->GetMutex(),
284
4.39k
                                  return node.chainman->ActiveTip()->Time()) +
285
4.39k
                        mock_offset);
286
4.39k
            const bool min_difficulty_window{mock_offset > std::chrono::minutes{20}};
287
4.39k
            node::BlockWaitOptions wait_options;
288
4.39k
            wait_options.timeout = MillisecondsDouble{0};
289
4.39k
            if (fuzzed_data_provider.ConsumeBool()) {
  Branch (289:17): [True: 720, False: 3.67k]
290
720
                wait_options.fee_threshold = fuzzed_data_provider.ConsumeIntegralInRange<CAmount>(0, MAX_MONEY);
291
720
            }
292
4.39k
            const bool interrupted{fuzzed_data_provider.ConsumeBool()};
293
4.39k
            bool interrupt{false};
294
4.39k
            if (interrupted) block_template_manager.InterruptWait(interrupt);
  Branch (294:17): [True: 316, False: 4.07k]
295
4.39k
            assert(interrupt == interrupted);
  Branch (295:13): [True: 4.39k, False: 0]
296
4.39k
            const auto next_template{block_template_manager.WaitAndCreateNewBlock(block_template, wait_options, options, interrupt)};
297
4.39k
            assert(!interrupt);
  Branch (297:13): [True: 4.39k, False: 0]
298
4.39k
            if (interrupted) {
  Branch (298:17): [True: 316, False: 4.07k]
299
316
                assert(!next_template);
  Branch (299:17): [True: 316, False: 0]
300
4.07k
            } else if (min_difficulty_window) {
  Branch (300:24): [True: 1.80k, False: 2.26k]
301
1.80k
                assert(next_template && next_template->block.hashPrevBlock == tip_hash);
  Branch (301:17): [True: 1.80k, False: 0]
  Branch (301:17): [True: 1.80k, False: 0]
  Branch (301:17): [True: 1.80k, False: 0]
302
2.26k
            } else if (next_template) {
  Branch (302:24): [True: 85, False: 2.18k]
303
85
                assert(next_template->block.hashPrevBlock == tip_hash);
  Branch (303:17): [True: 85, False: 0]
304
85
                const CAmount old_fees{std::accumulate(block_template->vTxFees.begin(), block_template->vTxFees.end(), CAmount{0})};
305
85
                const CAmount new_fees{std::accumulate(next_template->vTxFees.begin(), next_template->vTxFees.end(), CAmount{0})};
306
85
                assert(wait_options.fee_threshold < MAX_MONEY);
  Branch (306:17): [True: 85, False: 0]
307
85
                assert(new_fees >= old_fees + wait_options.fee_threshold);
  Branch (307:17): [True: 85, False: 0]
308
85
            }
309
4.39k
        }
310
4.39k
        {
311
            // With no headers ahead of the tip the cooldown returns
312
            // immediately and leaves the interrupt flag untouched.
313
4.39k
            bool interrupt{fuzzed_data_provider.ConsumeBool()};
314
4.39k
            const bool interrupted{interrupt};
315
4.39k
            assert(block_template_manager.CooldownIfHeadersAhead(*block_template_manager.GetTip(), interrupt));
  Branch (315:13): [True: 4.39k, False: 0]
316
4.39k
            assert(interrupt == interrupted);
  Branch (316:13): [True: 4.39k, False: 0]
317
4.39k
        }
318
4.39k
        if (fuzzed_data_provider.ConsumeBool()) {
  Branch (318:13): [True: 535, False: 3.85k]
319
            // An unsolved header can pass the regtest target by chance.
320
535
            if (!CheckProofOfWork(block.GetHash(), block.nBits, node.chainman->GetConsensus())) {
  Branch (320:17): [True: 187, False: 348]
321
187
                std::string reason;
322
187
                std::string debug;
323
187
                assert(!block_template_manager.SubmitBlock(std::make_shared<const CBlock>(block), reason, debug));
  Branch (323:17): [True: 187, False: 0]
324
187
                assert(reason == "high-hash");
  Branch (324:17): [True: 187, False: 0]
325
187
                assert(debug == "proof of work failed");
  Branch (325:17): [True: 187, False: 0]
326
187
            }
327
535
        }
328
4.39k
    }
329
3.86k
}