Branch data Line data Source code
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 : 210 : BlockTemplateManager::BlockTemplateManager(CTxMemPool& mempool, ChainstateManager& chainman,
36 : : KernelNotifications& notifications,
37 : 210 : BlockCreateOptions block_create_args)
38 : 210 : : m_mempool(mempool), m_chainman(chainman), m_notifications(notifications), m_block_create_args(std::move(block_create_args))
39 : : {
40 : 210 : }
41 : :
42 : 9418 : std::unique_ptr<CBlockTemplate> BlockTemplateManager::CreateNewTemplate(const BlockCreateOptions& options)
43 : : {
44 : 9417 : return BlockAssembler{
45 : 9418 : m_chainman.ActiveChainstate(),
46 : 9418 : &m_mempool,
47 [ + - ]: 18836 : MergeMiningOptions(options, m_block_create_args),
48 [ + + + + ]: 28248 : }.CreateNewBlock();
49 : : }
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 : 165 : explicit SubmitBlockStateCatcher(const uint256& hash) : m_hash{hash} {}
60 : :
61 : : protected:
62 : 110 : void BlockChecked(const std::shared_ptr<const CBlock>& block, const BlockValidationState& state) override
63 : : {
64 [ + - ]: 110 : 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 : 110 : m_found = true;
69 : 110 : m_state = state;
70 : : }
71 : : };
72 : : } // namespace
73 : :
74 : 165 : bool BlockTemplateManager::SubmitBlock(const std::shared_ptr<const CBlock>& block, std::string& reason, std::string& debug)
75 : : {
76 : 165 : reason.clear();
77 : 165 : 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 : 165 : auto sc = std::make_shared<SubmitBlockStateCatcher>(block->GetHash());
86 [ + - + - : 330 : CHECK_NONFATAL(m_chainman.m_options.signals)->RegisterSharedValidationInterface(sc);
+ - ]
87 : 165 : bool new_block;
88 [ + - ]: 165 : 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 [ + - + - : 330 : CHECK_NONFATAL(m_chainman.m_options.signals)->UnregisterSharedValidationInterface(sc);
+ - ]
92 : :
93 [ + + + - ]: 165 : if (!new_block && accepted) {
94 [ + - ]: 55 : reason = "duplicate";
95 [ - + - - : 110 : } 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 [ - + ]: 110 : } 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 [ # # ]: 0 : reason = "inconclusive";
105 [ - + ]: 110 : } else if (!sc->m_state.IsValid()) {
106 [ # # ]: 0 : reason = sc->m_state.GetRejectReason();
107 [ # # ]: 0 : debug = sc->m_state.GetDebugMessage();
108 : : }
109 [ + - + + : 165 : const bool result{accepted && new_block && reason.empty()};
- + ]
110 [ + - ]: 165 : CHECK_NONFATAL(result == reason.empty());
111 [ + - ]: 165 : return result;
112 : 165 : }
113 : :
114 : 243 : std::optional<BlockRef> BlockTemplateManager::GetTip()
115 : : {
116 : 243 : LOCK(::cs_main);
117 [ + - - + ]: 243 : CBlockIndex* tip{m_chainman.ActiveChain().Tip()};
118 [ - + ]: 243 : if (!tip) return {};
119 : 243 : return BlockRef{tip->GetBlockHash(), tip->nHeight};
120 : 243 : }
121 : :
122 : 0 : void BlockTemplateManager::InterruptWait(bool& interrupt_wait)
123 : : {
124 : 0 : LOCK(m_notifications.m_tip_block_mutex);
125 : 0 : interrupt_wait = true;
126 [ # # ]: 0 : m_notifications.m_tip_block_cv.notify_all();
127 : 0 : }
128 : :
129 : 62 : 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 : : {
135 : : // Delay calculating the current template fees, just in case a new block
136 : : // comes in before the next tick.
137 : 62 : 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 : 62 : auto now{NodeClock::now()};
142 : 62 : const auto deadline = now + wait_options.timeout;
143 : 62 : const MillisecondsDouble tick{1000};
144 : 62 : const bool allow_min_difficulty{m_chainman.GetParams().GetConsensus().fPowAllowMinDifficultyBlocks};
145 : :
146 : 62 : do {
147 : 62 : bool tip_changed{false};
148 : 62 : {
149 : 62 : WAIT_LOCK(m_notifications.m_tip_block_mutex, lock);
150 : : // Note that wait_until() checks the predicate before waiting
151 [ + - ]: 62 : m_notifications.m_tip_block_cv.wait_until(lock, std::min(now + tick, deadline), [&]() EXCLUSIVE_LOCKS_REQUIRED(m_notifications.m_tip_block_mutex) {
152 : 69 : AssertLockHeld(m_notifications.m_tip_block_mutex);
153 : 69 : 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 [ + - + + ]: 69 : tip_changed = Assume(tip_block) && tip_block != block_template->block.hashPrevBlock;
158 [ + + + - : 69 : return tip_changed || m_chainman.m_interrupt || interrupt_wait;
- + ]
159 : : });
160 [ - + ]: 62 : if (interrupt_wait) {
161 : 0 : interrupt_wait = false;
162 [ # # ]: 0 : return nullptr;
163 : : }
164 : 0 : }
165 : :
166 [ - + ]: 62 : 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 : 62 : LOCK(::cs_main);
172 : :
173 : : // On test networks return a minimum difficulty block after 20 minutes
174 [ + + + + ]: 62 : if (!tip_changed && allow_min_difficulty) {
175 [ + - - + ]: 6 : const NodeClock::time_point tip_time{std::chrono::seconds{m_chainman.ActiveChain().Tip()->GetBlockTime()}};
176 [ + + ]: 3 : if (now > tip_time + 20min) {
177 : 1 : tip_changed = true;
178 : : }
179 : : }
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 [ + + + - ]: 62 : if (wait_options.fee_threshold < MAX_MONEY || tip_changed) {
190 [ + - ]: 62 : auto new_tmpl{CreateNewTemplate(create_options)};
191 : :
192 : : // If the tip changed, return the new template regardless of its fees.
193 [ + + ]: 62 : if (tip_changed) return new_tmpl;
194 : :
195 : : // Calculate the original template total fees if we haven't already
196 [ + - ]: 6 : if (current_fees == -1) {
197 : 6 : current_fees = std::accumulate(block_template->vTxFees.begin(), block_template->vTxFees.end(), CAmount{0});
198 : : }
199 : :
200 : : // Check if fees increased enough to return the new template
201 : 6 : const CAmount new_fees = std::accumulate(new_tmpl->vTxFees.begin(), new_tmpl->vTxFees.end(), CAmount{0});
202 [ + + ]: 6 : Assume(wait_options.fee_threshold != MAX_MONEY);
203 [ + + ]: 6 : if (new_fees >= current_fees + wait_options.fee_threshold) return new_tmpl;
204 [ + - ]: 62 : }
205 : :
206 [ + - ]: 4 : now = NodeClock::now();
207 [ - + ]: 62 : } while (now < deadline);
208 : :
209 : 4 : return nullptr;
210 : : }
211 : :
212 : 0 : bool BlockTemplateManager::CooldownIfHeadersAhead(const BlockRef& last_tip, bool& interrupt_mining)
213 : : {
214 : 0 : uint256 last_tip_hash{last_tip.hash};
215 : :
216 [ # # ]: 0 : while (const std::optional<int> remaining = m_chainman.BlocksAheadOfTip()) {
217 [ # # ]: 0 : const int cooldown_seconds = std::clamp(*remaining, 3, 20);
218 : 0 : const auto cooldown_deadline{MockableSteadyClock::now() + std::chrono::seconds{cooldown_seconds}};
219 : :
220 : 0 : {
221 : 0 : WAIT_LOCK(m_notifications.m_tip_block_mutex, lock);
222 [ # # ]: 0 : m_notifications.m_tip_block_cv.wait_until(lock, cooldown_deadline, [&]() EXCLUSIVE_LOCKS_REQUIRED(m_notifications.m_tip_block_mutex) {
223 : 0 : const auto tip_block = m_notifications.TipBlock();
224 [ # # # # : 0 : return m_chainman.m_interrupt || interrupt_mining || (tip_block && *tip_block != last_tip_hash);
# # # # ]
225 : : });
226 [ # # # # : 0 : if (m_chainman.m_interrupt || interrupt_mining) {
# # ]
227 : 0 : interrupt_mining = false;
228 [ # # ]: 0 : return false;
229 : : }
230 : :
231 : : // If the tip changed during the wait, extend the deadline
232 [ # # ]: 0 : const auto tip_block = m_notifications.TipBlock();
233 [ # # # # ]: 0 : if (tip_block && *tip_block != last_tip_hash) {
234 [ # # ]: 0 : last_tip_hash = *tip_block;
235 [ # # ]: 0 : continue;
236 : : }
237 : 0 : }
238 : :
239 : : // No tip change and the cooldown window has expired.
240 [ # # ]: 0 : if (MockableSteadyClock::now() >= cooldown_deadline) break;
241 : : }
242 : :
243 : : return true;
244 : : }
245 : :
246 : 0 : std::optional<BlockRef> BlockTemplateManager::WaitTipChanged(const uint256& current_tip, MillisecondsDouble timeout)
247 : : {
248 : 0 : bool interrupt_wait{false};
249 : 0 : return WaitTipChanged(current_tip, timeout, interrupt_wait);
250 : : }
251 : :
252 : 133 : std::optional<BlockRef> BlockTemplateManager::WaitTipChanged(const uint256& current_tip, MillisecondsDouble& timeout, bool& interrupt)
253 : : {
254 : 133 : Assume(timeout >= 0ms); // No internal callers should use a negative timeout
255 [ - + ]: 133 : if (timeout < 0ms) timeout = 0ms;
256 [ + - ]: 133 : if (timeout > std::chrono::years{100}) timeout = std::chrono::years{100}; // Upper bound to avoid UB in std::chrono
257 : 133 : auto deadline{std::chrono::steady_clock::now() + timeout};
258 : 133 : {
259 : 133 : 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 [ + - ]: 133 : m_notifications.m_tip_block_cv.wait(lock, [&]() EXCLUSIVE_LOCKS_REQUIRED(m_notifications.m_tip_block_mutex) {
265 : 133 : AssertLockHeld(m_notifications.m_tip_block_mutex);
266 [ - + - - : 133 : return m_notifications.TipBlock() || m_chainman.m_interrupt || interrupt;
- - ]
267 : : });
268 [ + - + - : 133 : if (m_chainman.m_interrupt || interrupt) {
+ - ]
269 : 0 : interrupt = false;
270 : 0 : return {};
271 : : }
272 : : // At this point TipBlock is set, so continue to wait until it is
273 : : // different from `current_tip` provided by caller.
274 [ + - ]: 133 : m_notifications.m_tip_block_cv.wait_until(lock, deadline, [&]() EXCLUSIVE_LOCKS_REQUIRED(m_notifications.m_tip_block_mutex) {
275 [ - + - - : 133 : return Assume(m_notifications.TipBlock()) != current_tip || m_chainman.m_interrupt || interrupt;
- - ]
276 : : });
277 [ + - + - : 133 : if (m_chainman.m_interrupt || interrupt) {
+ - ]
278 : 0 : interrupt = false;
279 : 0 : return {};
280 : : }
281 : 0 : }
282 : :
283 : : // Must release m_tip_block_mutex before GetTip() locks cs_main, to
284 : : // avoid deadlocks.
285 : 133 : return GetTip();
286 : : }
287 : :
288 : : } // namespace node
|