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