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