/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 | } |