Coverage Report

Created: 2026-10-07 13:27

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/tmp/bitcoin/src/node/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 <chain.h>
8
#include <consensus/amount.h>
9
#include <consensus/params.h>
10
#include <consensus/validation.h>
11
#include <interfaces/types.h>
12
#include <kernel/chainparams.h>
13
#include <node/kernel_notifications.h>
14
#include <node/miner.h>
15
#include <node/mining_args.h>
16
#include <primitives/block.h>
17
#include <sync.h>
18
#include <uint256.h>
19
#include <util/check.h>
20
#include <util/signalinterrupt.h>
21
#include <validation.h>
22
#include <validationinterface.h>
23
24
#include <algorithm>
25
#include <compare>
26
#include <condition_variable>
27
#include <numeric>
28
#include <utility>
29
#include <vector>
30
31
namespace node {
32
33
using interfaces::BlockRef;
34
35
BlockTemplateManager::BlockTemplateManager(CTxMemPool& mempool, ChainstateManager& chainman,
36
                                           KernelNotifications& notifications,
37
                                           BlockCreateOptions block_create_args)
38
1.32k
    : m_mempool(mempool), m_chainman(chainman), m_notifications(notifications), m_block_create_args(std::move(block_create_args))
39
1.32k
{
40
1.32k
}
41
42
std::unique_ptr<CBlockTemplate> BlockTemplateManager::CreateNewTemplate(const BlockCreateOptions& options)
43
43.6k
{
44
43.6k
    return BlockAssembler{
45
43.6k
        m_chainman.ActiveChainstate(),
46
43.6k
        &m_mempool,
47
43.6k
        MergeMiningOptions(options, m_block_create_args),
48
43.6k
    }.CreateNewBlock();
49
43.6k
}
50
51
namespace {
52
class SubmitBlockStateCatcher final : public CValidationInterface
53
{
54
public:
55
    uint256 m_hash;
56
    bool m_found{false};
57
    BlockValidationState m_state;
58
59
197
    explicit SubmitBlockStateCatcher(const uint256& hash) : m_hash{hash} {}
60
61
protected:
62
    void BlockChecked(const std::shared_ptr<const CBlock>& block, const BlockValidationState& state) override
63
137
    {
64
137
        if (block->GetHash() != m_hash) return;
65
        // ProcessNewBlock emits BlockChecked synchronously while holding cs_main,
66
        // so SubmitBlock can read these fields after ProcessNewBlock returns
67
        // without extra synchronization.
68
137
        m_found = true;
69
137
        m_state = state;
70
137
    }
71
};
72
} // namespace
73
74
bool BlockTemplateManager::SubmitBlock(const std::shared_ptr<const CBlock>& block, std::string& reason, std::string& debug)
75
197
{
76
197
    reason.clear();
77
197
    debug.clear();
78
79
    // This follows the submitblock RPC's validation-state capture pattern, but
80
    // is intentionally kept separate from the RPC implementation. The RPC entry
81
    // point decodes hex, formats BIP22/JSONRPC results, and calls
82
    // UpdateUncommittedBlockStructures() for legacy witness handling. IPC
83
    // callers submit already-formed blocks and need bool + reason/debug
84
    // results.
85
197
    auto sc = std::make_shared<SubmitBlockStateCatcher>(block->GetHash());
86
197
    CHECK_NONFATAL(m_chainman.m_options.signals)->RegisterSharedValidationInterface(sc);
87
197
    bool new_block;
88
197
    bool accepted = m_chainman.ProcessNewBlock(block, /*force_processing=*/true, /*min_pow_checked=*/true, /*new_block=*/&new_block);
89
    // No queue drain is needed. The BlockChecked notification used above is
90
    // emitted synchronously by ProcessNewBlock, unlike most validation signals.
91
197
    CHECK_NONFATAL(m_chainman.m_options.signals)->UnregisterSharedValidationInterface(sc);
92
93
197
    if (!new_block && accepted) {
94
58
        reason = "duplicate";
95
139
    } else if (!accepted && (!sc->m_found || sc->m_state.IsValid())) {
96
        // ProcessNewBlock can fail without a validation result, for example
97
        // from an activation or system error. It can also fail after a valid
98
        // BlockChecked result. In these cases the validation result is
99
        // inconclusive.
100
0
        reason = "inconclusive";
101
139
    } else if (!sc->m_found) {
102
        // The block was accepted but not connected, for example if it does not
103
        // have more work than the current tip.
104
2
        reason = "inconclusive";
105
137
    } else if (!sc->m_state.IsValid()) {
106
6
        reason = sc->m_state.GetRejectReason();
107
6
        debug = sc->m_state.GetDebugMessage();
108
6
    }
109
197
    const bool result{accepted && new_block && reason.empty()};
110
197
    CHECK_NONFATAL(result == reason.empty());
111
197
    return result;
112
197
}
113
114
std::optional<BlockRef> BlockTemplateManager::GetTip()
115
428
{
116
428
    LOCK(::cs_main);
117
428
    CBlockIndex* tip{m_chainman.ActiveChain().Tip()};
118
428
    if (!tip) return {};
119
428
    return BlockRef{tip->GetBlockHash(), tip->nHeight};
120
428
}
121
122
void BlockTemplateManager::InterruptWait(bool& interrupt_wait)
123
3
{
124
3
    LOCK(m_notifications.m_tip_block_mutex);
125
3
    interrupt_wait = true;
126
3
    m_notifications.m_tip_block_cv.notify_all();
127
3
}
128
129
std::unique_ptr<CBlockTemplate> BlockTemplateManager::WaitAndCreateNewBlock(
130
    const std::unique_ptr<CBlockTemplate>& block_template,
131
    const BlockWaitOptions& wait_options,
132
    const BlockCreateOptions& create_options,
133
    bool& interrupt_wait)
134
75
{
135
    // Delay calculating the current template fees, just in case a new block
136
    // comes in before the next tick.
137
75
    CAmount current_fees = -1;
138
139
    // Alternate waiting for a new tip and checking if fees have risen.
140
    // The latter check is expensive so we only run it once per second.
141
75
    auto now{NodeClock::now()};
142
75
    const auto deadline = now + wait_options.timeout;
143
75
    const MillisecondsDouble tick{1000};
144
75
    const bool allow_min_difficulty{m_chainman.GetParams().GetConsensus().fPowAllowMinDifficultyBlocks};
145
146
93
    do {
147
93
        bool tip_changed{false};
148
93
        {
149
93
            WAIT_LOCK(m_notifications.m_tip_block_mutex, lock);
150
            // Note that wait_until() checks the predicate before waiting
151
128
            m_notifications.m_tip_block_cv.wait_until(lock, std::min(now + tick, deadline), [&]() EXCLUSIVE_LOCKS_REQUIRED(m_notifications.m_tip_block_mutex) {
152
128
                AssertLockHeld(m_notifications.m_tip_block_mutex);
153
128
                const auto tip_block{m_notifications.TipBlock()};
154
                // We assume tip_block is set, because this is an instance
155
                // method on BlockTemplate and no template could have been
156
                // generated before a tip exists.
157
128
                tip_changed = Assume(tip_block) && tip_block != block_template->block.hashPrevBlock;
158
128
                return tip_changed || m_chainman.m_interrupt || interrupt_wait;
159
128
            });
160
93
            if (interrupt_wait) {
161
1
                interrupt_wait = false;
162
1
                return nullptr;
163
1
            }
164
93
        }
165
166
92
        if (m_chainman.m_interrupt) return nullptr;
167
        // At this point the tip changed, a full tick went by or we reached
168
        // the deadline.
169
170
        // Must release m_tip_block_mutex before locking cs_main, to avoid deadlocks.
171
92
        LOCK(::cs_main);
172
173
        // On test networks return a minimum difficulty block after 20 minutes
174
92
        if (!tip_changed && allow_min_difficulty) {
175
28
            const NodeClock::time_point tip_time{std::chrono::seconds{m_chainman.ActiveChain().Tip()->GetBlockTime()}};
176
28
            if (now > tip_time + 20min) {
177
1
                tip_changed = true;
178
1
            }
179
28
        }
180
181
        /**
182
         * We determine if fees increased compared to the previous template by generating
183
         * a fresh template. There may be more efficient ways to determine how much
184
         * (approximate) fees for the next block increased, perhaps more so after
185
         * Cluster Mempool.
186
         *
187
         * We'll also create a new template if the tip changed during this iteration.
188
         */
189
92
        if (wait_options.fee_threshold < MAX_MONEY || tip_changed) {
190
92
            auto new_tmpl{CreateNewTemplate(create_options)};
191
192
            // If the tip changed, return the new template regardless of its fees.
193
92
            if (tip_changed) return new_tmpl;
194
195
            // Calculate the original template total fees if we haven't already
196
31
            if (current_fees == -1) {
197
13
                current_fees = std::accumulate(block_template->vTxFees.begin(), block_template->vTxFees.end(), CAmount{0});
198
13
            }
199
200
            // Check if fees increased enough to return the new template
201
31
            const CAmount new_fees = std::accumulate(new_tmpl->vTxFees.begin(), new_tmpl->vTxFees.end(), CAmount{0});
202
31
            Assume(wait_options.fee_threshold != MAX_MONEY);
203
31
            if (new_fees >= current_fees + wait_options.fee_threshold) return new_tmpl;
204
31
        }
205
206
24
        now = NodeClock::now();
207
24
    } while (now < deadline);
208
209
6
    return nullptr;
210
75
}
211
212
bool BlockTemplateManager::CooldownIfHeadersAhead(const BlockRef& last_tip, bool& interrupt_mining)
213
19
{
214
19
    uint256 last_tip_hash{last_tip.hash};
215
216
20
    while (const std::optional<int> remaining = m_chainman.BlocksAheadOfTip()) {
217
2
        const int cooldown_seconds = std::clamp(*remaining, 3, 20);
218
2
        const auto cooldown_deadline{MockableSteadyClock::now() + std::chrono::seconds{cooldown_seconds}};
219
220
2
        {
221
2
            WAIT_LOCK(m_notifications.m_tip_block_mutex, lock);
222
4
            m_notifications.m_tip_block_cv.wait_until(lock, cooldown_deadline, [&]() EXCLUSIVE_LOCKS_REQUIRED(m_notifications.m_tip_block_mutex) {
223
4
                const auto tip_block = m_notifications.TipBlock();
224
4
                return m_chainman.m_interrupt || interrupt_mining || (tip_block && *tip_block != last_tip_hash);
225
4
            });
226
2
            if (m_chainman.m_interrupt || interrupt_mining) {
227
0
                interrupt_mining = false;
228
0
                return false;
229
0
            }
230
231
            // If the tip changed during the wait, extend the deadline
232
2
            const auto tip_block = m_notifications.TipBlock();
233
2
            if (tip_block && *tip_block != last_tip_hash) {
234
1
                last_tip_hash = *tip_block;
235
1
                continue;
236
1
            }
237
2
        }
238
239
        // No tip change and the cooldown window has expired.
240
1
        if (MockableSteadyClock::now() >= cooldown_deadline) break;
241
1
    }
242
243
19
    return true;
244
19
}
245
246
std::optional<BlockRef> BlockTemplateManager::WaitTipChanged(const uint256& current_tip, MillisecondsDouble timeout)
247
26
{
248
26
    bool interrupt_wait{false};
249
26
    return WaitTipChanged(current_tip, timeout, interrupt_wait);
250
26
}
251
252
std::optional<BlockRef> BlockTemplateManager::WaitTipChanged(const uint256& current_tip, MillisecondsDouble& timeout, bool& interrupt)
253
199
{
254
199
    Assume(timeout >= 0ms); // No internal callers should use a negative timeout
255
199
    if (timeout < 0ms) timeout = 0ms;
256
199
    if (timeout > std::chrono::years{100}) timeout = std::chrono::years{100}; // Upper bound to avoid UB in std::chrono
257
199
    auto deadline{std::chrono::steady_clock::now() + timeout};
258
199
    {
259
199
        WAIT_LOCK(m_notifications.m_tip_block_mutex, lock);
260
        // For callers convenience, wait longer than the provided timeout
261
        // during startup for the tip to be non-null. That way this function
262
        // always returns valid tip information when possible and only
263
        // returns null when shutting down, not when timing out.
264
199
        m_notifications.m_tip_block_cv.wait(lock, [&]() EXCLUSIVE_LOCKS_REQUIRED(m_notifications.m_tip_block_mutex) {
265
199
            AssertLockHeld(m_notifications.m_tip_block_mutex);
266
199
            return m_notifications.TipBlock() || m_chainman.m_interrupt || interrupt;
267
199
        });
268
199
        if (m_chainman.m_interrupt || interrupt) {
269
2
            interrupt = false;
270
2
            return {};
271
2
        }
272
        // At this point TipBlock is set, so continue to wait until it is
273
        // different from `current_tip` provided by caller.
274
223
        m_notifications.m_tip_block_cv.wait_until(lock, deadline, [&]() EXCLUSIVE_LOCKS_REQUIRED(m_notifications.m_tip_block_mutex) {
275
223
            return Assume(m_notifications.TipBlock()) != current_tip || m_chainman.m_interrupt || interrupt;
276
223
        });
277
197
        if (m_chainman.m_interrupt || interrupt) {
278
2
            interrupt = false;
279
2
            return {};
280
2
        }
281
197
    }
282
283
    // Must release m_tip_block_mutex before GetTip() locks cs_main, to
284
    // avoid deadlocks.
285
195
    return GetTip();
286
197
}
287
288
} // namespace node