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/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 : 4083 : BlockTemplateManager::BlockTemplateManager(CTxMemPool& mempool, ChainstateManager& chainman,
37 : : KernelNotifications& notifications,
38 : 4083 : BlockCreateOptions block_create_args)
39 : 4083 : : m_mempool(mempool), m_chainman(chainman), m_notifications(notifications), m_block_create_args(std::move(block_create_args))
40 : : {
41 : 4083 : }
42 : :
43 : 480129 : std::unique_ptr<CBlockTemplate> BlockTemplateManager::CreateNewTemplate(const BlockCreateOptions& options)
44 : : {
45 : 480129 : return BlockAssembler{
46 : 480129 : m_chainman.ActiveChainstate(),
47 : 480129 : &m_mempool,
48 [ + - ]: 960258 : MergeMiningOptions(options, m_block_create_args),
49 [ + - + - ]: 1440387 : }.CreateNewBlock();
50 : : }
51 : :
52 : 0 : bool BlockTemplateManager::SubmitBlock(const std::shared_ptr<const CBlock>& block, std::string& reason, std::string& debug)
53 : : {
54 : 0 : reason.clear();
55 : 0 : 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 : 0 : auto sc = std::make_shared<BlockValidationStateCatcher>(block->GetHash());
64 [ # # # # : 0 : CHECK_NONFATAL(m_chainman.m_options.signals)->RegisterSharedValidationInterface(sc);
# # ]
65 : 0 : bool new_block;
66 [ # # ]: 0 : 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 [ # # # # : 0 : CHECK_NONFATAL(m_chainman.m_options.signals)->UnregisterSharedValidationInterface(sc);
# # ]
70 : :
71 [ # # # # ]: 0 : if (!new_block && accepted) {
72 [ # # ]: 0 : reason = "duplicate";
73 [ # # # # : 0 : } 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 [ # # ]: 0 : } 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 [ # # ]: 0 : reason = "inconclusive";
83 [ # # ]: 0 : } else if (!sc->m_state->IsValid()) {
84 [ # # ]: 0 : reason = sc->m_state->GetRejectReason();
85 [ # # ]: 0 : debug = sc->m_state->GetDebugMessage();
86 : : }
87 [ # # # # : 0 : const bool result{accepted && new_block && reason.empty()};
# # ]
88 [ # # ]: 0 : CHECK_NONFATAL(result == reason.empty());
89 [ # # ]: 0 : return result;
90 : 0 : }
91 : :
92 : 5 : std::optional<BlockRef> BlockTemplateManager::GetTip()
93 : : {
94 : 5 : LOCK(::cs_main);
95 [ + - - + ]: 5 : CBlockIndex* tip{m_chainman.ActiveChain().Tip()};
96 [ - + ]: 5 : if (!tip) return {};
97 : 5 : return BlockRef{tip->GetBlockHash(), tip->nHeight};
98 : 5 : }
99 : :
100 : 0 : void BlockTemplateManager::InterruptWait(bool& interrupt_wait)
101 : : {
102 : 0 : LOCK(m_notifications.m_tip_block_mutex);
103 : 0 : interrupt_wait = true;
104 [ # # ]: 0 : m_notifications.m_tip_block_cv.notify_all();
105 : 0 : }
106 : :
107 : 0 : 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 : : {
113 : : // Delay calculating the current template fees, just in case a new block
114 : : // comes in before the next tick.
115 : 0 : 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 : 0 : auto now{NodeClock::now()};
120 : 0 : const auto deadline = now + wait_options.timeout;
121 : 0 : const MillisecondsDouble tick{1000};
122 : 0 : const bool allow_min_difficulty{m_chainman.GetParams().GetConsensus().fPowAllowMinDifficultyBlocks};
123 : :
124 : 0 : do {
125 : 0 : bool tip_changed{false};
126 : 0 : {
127 : 0 : WAIT_LOCK(m_notifications.m_tip_block_mutex, lock);
128 : : // Note that wait_until() checks the predicate before waiting
129 [ # # ]: 0 : m_notifications.m_tip_block_cv.wait_until(lock, std::min(now + tick, deadline), [&]() EXCLUSIVE_LOCKS_REQUIRED(m_notifications.m_tip_block_mutex) {
130 : 0 : AssertLockHeld(m_notifications.m_tip_block_mutex);
131 : 0 : 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 [ # # # # ]: 0 : tip_changed = Assume(tip_block) && tip_block != block_template->block.hashPrevBlock;
136 [ # # # # : 0 : return tip_changed || m_chainman.m_interrupt || interrupt_wait;
# # ]
137 : : });
138 [ # # ]: 0 : if (interrupt_wait) {
139 : 0 : interrupt_wait = false;
140 [ # # ]: 0 : return nullptr;
141 : : }
142 : 0 : }
143 : :
144 [ # # ]: 0 : 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 : 0 : LOCK(::cs_main);
150 : :
151 : : // On test networks return a minimum difficulty block after 20 minutes
152 [ # # # # ]: 0 : if (!tip_changed && allow_min_difficulty) {
153 [ # # # # ]: 0 : const NodeClock::time_point tip_time{std::chrono::seconds{m_chainman.ActiveChain().Tip()->GetBlockTime()}};
154 [ # # ]: 0 : if (now > tip_time + 20min) {
155 : 0 : tip_changed = true;
156 : : }
157 : : }
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 [ # # # # ]: 0 : if (wait_options.fee_threshold < MAX_MONEY || tip_changed) {
168 [ # # ]: 0 : auto new_tmpl{CreateNewTemplate(create_options)};
169 : :
170 : : // If the tip changed, return the new template regardless of its fees.
171 [ # # ]: 0 : if (tip_changed) return new_tmpl;
172 : :
173 : : // Calculate the original template total fees if we haven't already
174 [ # # ]: 0 : if (current_fees == -1) {
175 : 0 : current_fees = std::accumulate(block_template->vTxFees.begin(), block_template->vTxFees.end(), CAmount{0});
176 : : }
177 : :
178 : : // Check if fees increased enough to return the new template
179 : 0 : const CAmount new_fees = std::accumulate(new_tmpl->vTxFees.begin(), new_tmpl->vTxFees.end(), CAmount{0});
180 [ # # ]: 0 : Assume(wait_options.fee_threshold != MAX_MONEY);
181 [ # # ]: 0 : if (new_fees >= current_fees + wait_options.fee_threshold) return new_tmpl;
182 [ # # ]: 0 : }
183 : :
184 [ # # ]: 0 : now = NodeClock::now();
185 [ # # ]: 0 : } while (now < deadline);
186 : :
187 : 0 : return nullptr;
188 : : }
189 : :
190 : 0 : bool BlockTemplateManager::CooldownIfHeadersAhead(const BlockRef& last_tip, bool& interrupt_mining)
191 : : {
192 : 0 : uint256 last_tip_hash{last_tip.hash};
193 : :
194 [ # # ]: 0 : while (const std::optional<int> remaining = m_chainman.BlocksAheadOfTip()) {
195 [ # # ]: 0 : const int cooldown_seconds = std::clamp(*remaining, 3, 20);
196 : 0 : const auto cooldown_deadline{MockableSteadyClock::now() + std::chrono::seconds{cooldown_seconds}};
197 : :
198 : 0 : {
199 : 0 : WAIT_LOCK(m_notifications.m_tip_block_mutex, lock);
200 [ # # ]: 0 : m_notifications.m_tip_block_cv.wait_until(lock, cooldown_deadline, [&]() EXCLUSIVE_LOCKS_REQUIRED(m_notifications.m_tip_block_mutex) {
201 : 0 : const auto tip_block = m_notifications.TipBlock();
202 [ # # # # : 0 : return m_chainman.m_interrupt || interrupt_mining || (tip_block && *tip_block != last_tip_hash);
# # # # ]
203 : : });
204 [ # # # # : 0 : if (m_chainman.m_interrupt || interrupt_mining) {
# # ]
205 : 0 : interrupt_mining = false;
206 [ # # ]: 0 : return false;
207 : : }
208 : :
209 : : // If the tip changed during the wait, extend the deadline
210 [ # # ]: 0 : const auto tip_block = m_notifications.TipBlock();
211 [ # # # # ]: 0 : if (tip_block && *tip_block != last_tip_hash) {
212 [ # # ]: 0 : last_tip_hash = *tip_block;
213 [ # # ]: 0 : continue;
214 : : }
215 : 0 : }
216 : :
217 : : // No tip change and the cooldown window has expired.
218 [ # # ]: 0 : if (MockableSteadyClock::now() >= cooldown_deadline) break;
219 : : }
220 : :
221 : : return true;
222 : : }
223 : :
224 : 0 : std::optional<BlockRef> BlockTemplateManager::WaitTipChanged(const uint256& current_tip, MillisecondsDouble timeout)
225 : : {
226 : 0 : bool interrupt_wait{false};
227 : 0 : return WaitTipChanged(current_tip, timeout, interrupt_wait);
228 : : }
229 : :
230 : 0 : std::optional<BlockRef> BlockTemplateManager::WaitTipChanged(const uint256& current_tip, MillisecondsDouble& timeout, bool& interrupt)
231 : : {
232 : 0 : Assume(timeout >= 0ms); // No internal callers should use a negative timeout
233 [ # # ]: 0 : if (timeout < 0ms) timeout = 0ms;
234 [ # # ]: 0 : if (timeout > std::chrono::years{100}) timeout = std::chrono::years{100}; // Upper bound to avoid UB in std::chrono
235 : 0 : auto deadline{std::chrono::steady_clock::now() + timeout};
236 : 0 : {
237 : 0 : 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 [ # # ]: 0 : m_notifications.m_tip_block_cv.wait(lock, [&]() EXCLUSIVE_LOCKS_REQUIRED(m_notifications.m_tip_block_mutex) {
243 : 0 : AssertLockHeld(m_notifications.m_tip_block_mutex);
244 [ # # # # : 0 : return m_notifications.TipBlock() || m_chainman.m_interrupt || interrupt;
# # ]
245 : : });
246 [ # # # # : 0 : if (m_chainman.m_interrupt || interrupt) {
# # ]
247 : 0 : interrupt = false;
248 : 0 : return {};
249 : : }
250 : : // At this point TipBlock is set, so continue to wait until it is
251 : : // different from `current_tip` provided by caller.
252 [ # # ]: 0 : m_notifications.m_tip_block_cv.wait_until(lock, deadline, [&]() EXCLUSIVE_LOCKS_REQUIRED(m_notifications.m_tip_block_mutex) {
253 [ # # # # : 0 : return Assume(m_notifications.TipBlock()) != current_tip || m_chainman.m_interrupt || interrupt;
# # ]
254 : : });
255 [ # # # # : 0 : if (m_chainman.m_interrupt || interrupt) {
# # ]
256 : 0 : interrupt = false;
257 : 0 : return {};
258 : : }
259 : 0 : }
260 : :
261 : : // Must release m_tip_block_mutex before GetTip() locks cs_main, to
262 : : // avoid deadlocks.
263 : 0 : return GetTip();
264 : : }
265 : :
266 : : } // namespace node
|