/root/bitcoin/src/node/interfaces.cpp
Line | Count | Source |
1 | | // Copyright (c) 2018-present 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 <addrdb.h> |
6 | | #include <banman.h> |
7 | | #include <blockfilter.h> |
8 | | #include <chain.h> |
9 | | #include <chainparams.h> |
10 | | #include <common/args.h> |
11 | | #include <consensus/merkle.h> |
12 | | #include <consensus/validation.h> |
13 | | #include <deploymentstatus.h> |
14 | | #include <external_signer.h> |
15 | | #include <index/blockfilterindex.h> |
16 | | #include <init.h> |
17 | | #include <interfaces/chain.h> |
18 | | #include <interfaces/handler.h> |
19 | | #include <interfaces/mining.h> |
20 | | #include <interfaces/node.h> |
21 | | #include <interfaces/types.h> |
22 | | #include <interfaces/wallet.h> |
23 | | #include <kernel/chain.h> |
24 | | #include <kernel/context.h> |
25 | | #include <kernel/mempool_entry.h> |
26 | | #include <logging.h> |
27 | | #include <mapport.h> |
28 | | #include <net.h> |
29 | | #include <net_processing.h> |
30 | | #include <netaddress.h> |
31 | | #include <netbase.h> |
32 | | #include <node/blockstorage.h> |
33 | | #include <node/coin.h> |
34 | | #include <node/context.h> |
35 | | #include <node/interface_ui.h> |
36 | | #include <node/mini_miner.h> |
37 | | #include <node/miner.h> |
38 | | #include <node/kernel_notifications.h> |
39 | | #include <node/transaction.h> |
40 | | #include <node/types.h> |
41 | | #include <node/warnings.h> |
42 | | #include <policy/feerate.h> |
43 | | #include <policy/fees/block_policy_estimator.h> |
44 | | #include <policy/policy.h> |
45 | | #include <policy/rbf.h> |
46 | | #include <policy/settings.h> |
47 | | #include <primitives/block.h> |
48 | | #include <primitives/transaction.h> |
49 | | #include <rpc/blockchain.h> |
50 | | #include <rpc/protocol.h> |
51 | | #include <rpc/server.h> |
52 | | #include <support/allocators/secure.h> |
53 | | #include <sync.h> |
54 | | #include <txmempool.h> |
55 | | #include <uint256.h> |
56 | | #include <univalue.h> |
57 | | #include <util/check.h> |
58 | | #include <util/result.h> |
59 | | #include <util/signalinterrupt.h> |
60 | | #include <util/string.h> |
61 | | #include <util/translation.h> |
62 | | #include <validation.h> |
63 | | #include <validationinterface.h> |
64 | | |
65 | | #include <bitcoin-build-config.h> // IWYU pragma: keep |
66 | | |
67 | | #include <any> |
68 | | #include <memory> |
69 | | #include <optional> |
70 | | #include <stdexcept> |
71 | | #include <utility> |
72 | | |
73 | | #include <boost/signals2/signal.hpp> |
74 | | |
75 | | using interfaces::BlockRef; |
76 | | using interfaces::BlockTemplate; |
77 | | using interfaces::BlockTip; |
78 | | using interfaces::Chain; |
79 | | using interfaces::FoundBlock; |
80 | | using interfaces::Handler; |
81 | | using interfaces::MakeSignalHandler; |
82 | | using interfaces::Mining; |
83 | | using interfaces::Node; |
84 | | using interfaces::WalletLoader; |
85 | | using kernel::ChainstateRole; |
86 | | using node::BlockAssembler; |
87 | | using node::BlockWaitOptions; |
88 | | using node::CoinbaseTx; |
89 | | using util::Join; |
90 | | |
91 | | namespace node { |
92 | | // All members of the classes in this namespace are intentionally public, as the |
93 | | // classes themselves are private. |
94 | | namespace { |
95 | | #ifdef ENABLE_EXTERNAL_SIGNER |
96 | | class ExternalSignerImpl : public interfaces::ExternalSigner |
97 | | { |
98 | | public: |
99 | | ExternalSignerImpl(::ExternalSigner signer) : m_signer(std::move(signer)) {} |
100 | | std::string getName() override { return m_signer.m_name; } |
101 | | ::ExternalSigner m_signer; |
102 | | }; |
103 | | #endif |
104 | | |
105 | | class NodeImpl : public Node |
106 | | { |
107 | | public: |
108 | 0 | explicit NodeImpl(NodeContext& context) { setContext(&context); } |
109 | 0 | void initLogging() override { InitLogging(args()); } |
110 | 0 | void initParameterInteraction() override { InitParameterInteraction(args()); } |
111 | 0 | bilingual_str getWarnings() override { return Join(Assert(m_context->warnings)->GetMessages(), Untranslated("<hr />")); }Line | Count | Source | 113 | 0 | #define Assert(val) inline_assertion_check<true>(val, std::source_location::current(), #val) |
|
112 | 0 | int getExitStatus() override { return Assert(m_context)->exit_status.load(); }Line | Count | Source | 113 | 0 | #define Assert(val) inline_assertion_check<true>(val, std::source_location::current(), #val) |
|
113 | 0 | BCLog::CategoryMask getLogCategories() override { return LogInstance().GetCategoryMask(); } |
114 | | bool baseInitialize() override |
115 | 0 | { |
116 | 0 | if (!AppInitBasicSetup(args(), Assert(context())->exit_status)) return false; Line | Count | Source | 113 | 0 | #define Assert(val) inline_assertion_check<true>(val, std::source_location::current(), #val) |
|
117 | 0 | if (!AppInitParameterInteraction(args())) return false; |
118 | | |
119 | 0 | m_context->warnings = std::make_unique<node::Warnings>(); |
120 | 0 | m_context->kernel = std::make_unique<kernel::Context>(); |
121 | 0 | m_context->ecc_context = std::make_unique<ECC_Context>(); |
122 | 0 | if (!AppInitSanityChecks(*m_context->kernel)) return false; |
123 | | |
124 | 0 | if (!AppInitLockDirectories()) return false; |
125 | 0 | if (!AppInitInterfaces(*m_context)) return false; |
126 | | |
127 | 0 | return true; |
128 | 0 | } |
129 | | bool appInitMain(interfaces::BlockAndHeaderTipInfo* tip_info) override |
130 | 0 | { |
131 | 0 | if (AppInitMain(*m_context, tip_info)) return true; |
132 | | // Error during initialization, set exit status before continue |
133 | 0 | m_context->exit_status.store(EXIT_FAILURE); |
134 | 0 | return false; |
135 | 0 | } |
136 | | void appShutdown() override |
137 | 0 | { |
138 | 0 | Shutdown(*m_context); |
139 | 0 | } |
140 | | void startShutdown() override |
141 | 0 | { |
142 | 0 | NodeContext& ctx{*Assert(m_context)};Line | Count | Source | 113 | 0 | #define Assert(val) inline_assertion_check<true>(val, std::source_location::current(), #val) |
|
143 | 0 | if (!(Assert(ctx.shutdown_request))()) {Line | Count | Source | 113 | 0 | #define Assert(val) inline_assertion_check<true>(val, std::source_location::current(), #val) |
|
144 | 0 | LogError("Failed to send shutdown signal\n");Line | Count | Source | 97 | 0 | #define LogError(...) LogPrintLevel_(BCLog::LogFlags::ALL, BCLog::Level::Error, /*should_ratelimit=*/true, __VA_ARGS__) Line | Count | Source | 89 | 0 | #define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(SourceLocation{__func__}, category, level, should_ratelimit, __VA_ARGS__) |
|
|
145 | 0 | } |
146 | 0 | Interrupt(*m_context); |
147 | 0 | } |
148 | 0 | bool shutdownRequested() override { return ShutdownRequested(*Assert(m_context)); };Line | Count | Source | 113 | 0 | #define Assert(val) inline_assertion_check<true>(val, std::source_location::current(), #val) |
|
149 | | bool isSettingIgnored(const std::string& name) override |
150 | 0 | { |
151 | 0 | bool ignored = false; |
152 | 0 | args().LockSettings([&](common::Settings& settings) { |
153 | 0 | if (auto* options = common::FindKey(settings.command_line_options, name)) { |
154 | 0 | ignored = !options->empty(); |
155 | 0 | } |
156 | 0 | }); |
157 | 0 | return ignored; |
158 | 0 | } |
159 | 0 | common::SettingsValue getPersistentSetting(const std::string& name) override { return args().GetPersistentSetting(name); } |
160 | | void updateRwSetting(const std::string& name, const common::SettingsValue& value) override |
161 | 0 | { |
162 | 0 | args().LockSettings([&](common::Settings& settings) { |
163 | 0 | if (value.isNull()) { |
164 | 0 | settings.rw_settings.erase(name); |
165 | 0 | } else { |
166 | 0 | settings.rw_settings[name] = value; |
167 | 0 | } |
168 | 0 | }); |
169 | 0 | args().WriteSettingsFile(); |
170 | 0 | } |
171 | | void forceSetting(const std::string& name, const common::SettingsValue& value) override |
172 | 0 | { |
173 | 0 | args().LockSettings([&](common::Settings& settings) { |
174 | 0 | if (value.isNull()) { |
175 | 0 | settings.forced_settings.erase(name); |
176 | 0 | } else { |
177 | 0 | settings.forced_settings[name] = value; |
178 | 0 | } |
179 | 0 | }); |
180 | 0 | } |
181 | | void resetSettings() override |
182 | 0 | { |
183 | 0 | args().WriteSettingsFile(/*errors=*/nullptr, /*backup=*/true); |
184 | 0 | args().LockSettings([&](common::Settings& settings) { |
185 | 0 | settings.rw_settings.clear(); |
186 | 0 | }); |
187 | 0 | args().WriteSettingsFile(); |
188 | 0 | } |
189 | 0 | void mapPort(bool enable) override { StartMapPort(enable); } |
190 | 0 | bool getProxy(Network net, Proxy& proxy_info) override { return GetProxy(net, proxy_info); } |
191 | | size_t getNodeCount(ConnectionDirection flags) override |
192 | 0 | { |
193 | 0 | return m_context->connman ? m_context->connman->GetNodeCount(flags) : 0; |
194 | 0 | } |
195 | | bool getNodesStats(NodesStats& stats) override |
196 | 0 | { |
197 | 0 | stats.clear(); |
198 | |
|
199 | 0 | if (m_context->connman) { |
200 | 0 | std::vector<CNodeStats> stats_temp; |
201 | 0 | m_context->connman->GetNodeStats(stats_temp); |
202 | |
|
203 | 0 | stats.reserve(stats_temp.size()); |
204 | 0 | for (auto& node_stats_temp : stats_temp) { |
205 | 0 | stats.emplace_back(std::move(node_stats_temp), false, CNodeStateStats()); |
206 | 0 | } |
207 | | |
208 | | // Try to retrieve the CNodeStateStats for each node. |
209 | 0 | if (m_context->peerman) { |
210 | 0 | TRY_LOCK(::cs_main, lockMain); Line | Count | Source | 271 | 0 | #define TRY_LOCK(cs, name) UniqueLock name(LOCK_ARGS(cs), true) Line | Count | Source | 270 | 0 | #define LOCK_ARGS(cs) MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__ |
|
|
211 | 0 | if (lockMain) { |
212 | 0 | for (auto& node_stats : stats) { |
213 | 0 | std::get<1>(node_stats) = |
214 | 0 | m_context->peerman->GetNodeStateStats(std::get<0>(node_stats).nodeid, std::get<2>(node_stats)); |
215 | 0 | } |
216 | 0 | } |
217 | 0 | } |
218 | 0 | return true; |
219 | 0 | } |
220 | 0 | return false; |
221 | 0 | } |
222 | | bool getBanned(banmap_t& banmap) override |
223 | 0 | { |
224 | 0 | if (m_context->banman) { |
225 | 0 | m_context->banman->GetBanned(banmap); |
226 | 0 | return true; |
227 | 0 | } |
228 | 0 | return false; |
229 | 0 | } |
230 | | bool ban(const CNetAddr& net_addr, int64_t ban_time_offset) override |
231 | 0 | { |
232 | 0 | if (m_context->banman) { |
233 | 0 | m_context->banman->Ban(net_addr, ban_time_offset); |
234 | 0 | return true; |
235 | 0 | } |
236 | 0 | return false; |
237 | 0 | } |
238 | | bool unban(const CSubNet& ip) override |
239 | 0 | { |
240 | 0 | if (m_context->banman) { |
241 | 0 | m_context->banman->Unban(ip); |
242 | 0 | return true; |
243 | 0 | } |
244 | 0 | return false; |
245 | 0 | } |
246 | | bool disconnectByAddress(const CNetAddr& net_addr) override |
247 | 0 | { |
248 | 0 | if (m_context->connman) { |
249 | 0 | return m_context->connman->DisconnectNode(net_addr); |
250 | 0 | } |
251 | 0 | return false; |
252 | 0 | } |
253 | | bool disconnectById(NodeId id) override |
254 | 0 | { |
255 | 0 | if (m_context->connman) { |
256 | 0 | return m_context->connman->DisconnectNode(id); |
257 | 0 | } |
258 | 0 | return false; |
259 | 0 | } |
260 | | std::vector<std::unique_ptr<interfaces::ExternalSigner>> listExternalSigners() override |
261 | 0 | { |
262 | | #ifdef ENABLE_EXTERNAL_SIGNER |
263 | | std::vector<ExternalSigner> signers = {}; |
264 | | const std::string command = args().GetArg("-signer", ""); |
265 | | if (command == "") return {}; |
266 | | ExternalSigner::Enumerate(command, signers, Params().GetChainTypeString()); |
267 | | std::vector<std::unique_ptr<interfaces::ExternalSigner>> result; |
268 | | result.reserve(signers.size()); |
269 | | for (auto& signer : signers) { |
270 | | result.emplace_back(std::make_unique<ExternalSignerImpl>(std::move(signer))); |
271 | | } |
272 | | return result; |
273 | | #else |
274 | | // This result is indistinguishable from a successful call that returns |
275 | | // no signers. For the current GUI this doesn't matter, because the wallet |
276 | | // creation dialog disables the external signer checkbox in both |
277 | | // cases. The return type could be changed to std::optional<std::vector> |
278 | | // (or something that also includes error messages) if this distinction |
279 | | // becomes important. |
280 | 0 | return {}; |
281 | 0 | #endif // ENABLE_EXTERNAL_SIGNER |
282 | 0 | } |
283 | 0 | int64_t getTotalBytesRecv() override { return m_context->connman ? m_context->connman->GetTotalBytesRecv() : 0; } |
284 | 0 | int64_t getTotalBytesSent() override { return m_context->connman ? m_context->connman->GetTotalBytesSent() : 0; } |
285 | 0 | size_t getMempoolSize() override { return m_context->mempool ? m_context->mempool->size() : 0; } |
286 | 0 | size_t getMempoolDynamicUsage() override { return m_context->mempool ? m_context->mempool->DynamicMemoryUsage() : 0; } |
287 | 0 | size_t getMempoolMaxUsage() override { return m_context->mempool ? m_context->mempool->m_opts.max_size_bytes : 0; } |
288 | | bool getHeaderTip(int& height, int64_t& block_time) override |
289 | 0 | { |
290 | 0 | LOCK(::cs_main); Line | Count | Source | 266 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) Line | Count | Source | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) Line | Count | Source | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) Line | Count | Source | 8 | 0 | #define PASTE(x, y) x ## y |
|
|
|
|
291 | 0 | auto best_header = chainman().m_best_header; |
292 | 0 | if (best_header) { |
293 | 0 | height = best_header->nHeight; |
294 | 0 | block_time = best_header->GetBlockTime(); |
295 | 0 | return true; |
296 | 0 | } |
297 | 0 | return false; |
298 | 0 | } |
299 | | std::map<CNetAddr, LocalServiceInfo> getNetLocalAddresses() override |
300 | 0 | { |
301 | 0 | if (m_context->connman) |
302 | 0 | return m_context->connman->getNetLocalAddresses(); |
303 | 0 | else |
304 | 0 | return {}; |
305 | 0 | } |
306 | | int getNumBlocks() override |
307 | 0 | { |
308 | 0 | LOCK(::cs_main); Line | Count | Source | 266 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) Line | Count | Source | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) Line | Count | Source | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) Line | Count | Source | 8 | 0 | #define PASTE(x, y) x ## y |
|
|
|
|
309 | 0 | return chainman().ActiveChain().Height(); |
310 | 0 | } |
311 | | uint256 getBestBlockHash() override |
312 | 0 | { |
313 | 0 | const CBlockIndex* tip = WITH_LOCK(::cs_main, return chainman().ActiveChain().Tip()); Line | Count | Source | 297 | 0 | #define WITH_LOCK(cs, code) (MaybeCheckNotHeld(cs), [&]() -> decltype(auto) { LOCK(cs); code; }()) |
|
314 | 0 | return tip ? tip->GetBlockHash() : chainman().GetParams().GenesisBlock().GetHash(); |
315 | 0 | } |
316 | | int64_t getLastBlockTime() override |
317 | 0 | { |
318 | 0 | LOCK(::cs_main); Line | Count | Source | 266 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) Line | Count | Source | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) Line | Count | Source | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) Line | Count | Source | 8 | 0 | #define PASTE(x, y) x ## y |
|
|
|
|
319 | 0 | if (chainman().ActiveChain().Tip()) { |
320 | 0 | return chainman().ActiveChain().Tip()->GetBlockTime(); |
321 | 0 | } |
322 | 0 | return chainman().GetParams().GenesisBlock().GetBlockTime(); // Genesis block's time of current network |
323 | 0 | } |
324 | | double getVerificationProgress() override |
325 | 0 | { |
326 | 0 | LOCK(chainman().GetMutex()); Line | Count | Source | 266 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) Line | Count | Source | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) Line | Count | Source | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) Line | Count | Source | 8 | 0 | #define PASTE(x, y) x ## y |
|
|
|
|
327 | 0 | return chainman().GuessVerificationProgress(chainman().ActiveTip()); |
328 | 0 | } |
329 | | bool isInitialBlockDownload() override |
330 | 0 | { |
331 | 0 | return chainman().IsInitialBlockDownload(); |
332 | 0 | } |
333 | 0 | bool isLoadingBlocks() override { return chainman().m_blockman.LoadingBlocks(); } |
334 | | void setNetworkActive(bool active) override |
335 | 0 | { |
336 | 0 | if (m_context->connman) { |
337 | 0 | m_context->connman->SetNetworkActive(active); |
338 | 0 | } |
339 | 0 | } |
340 | 0 | bool getNetworkActive() override { return m_context->connman && m_context->connman->GetNetworkActive(); } |
341 | | CFeeRate getDustRelayFee() override |
342 | 0 | { |
343 | 0 | if (!m_context->mempool) return CFeeRate{DUST_RELAY_TX_FEE}; |
344 | 0 | return m_context->mempool->m_opts.dust_relay_feerate; |
345 | 0 | } |
346 | | UniValue executeRpc(const std::string& command, const UniValue& params, const std::string& uri) override |
347 | 0 | { |
348 | 0 | JSONRPCRequest req; |
349 | 0 | req.context = m_context; |
350 | 0 | req.params = params; |
351 | 0 | req.strMethod = command; |
352 | 0 | req.URI = uri; |
353 | 0 | return ::tableRPC.execute(req); |
354 | 0 | } |
355 | 0 | std::vector<std::string> listRpcCommands() override { return ::tableRPC.listCommands(); } |
356 | | std::optional<Coin> getUnspentOutput(const COutPoint& output) override |
357 | 0 | { |
358 | 0 | LOCK(::cs_main); Line | Count | Source | 266 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) Line | Count | Source | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) Line | Count | Source | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) Line | Count | Source | 8 | 0 | #define PASTE(x, y) x ## y |
|
|
|
|
359 | 0 | return chainman().ActiveChainstate().CoinsTip().GetCoin(output); |
360 | 0 | } |
361 | | TransactionError broadcastTransaction(CTransactionRef tx, CAmount max_tx_fee, std::string& err_string) override |
362 | 0 | { |
363 | 0 | return BroadcastTransaction(*m_context, |
364 | 0 | std::move(tx), |
365 | 0 | err_string, |
366 | 0 | max_tx_fee, |
367 | 0 | TxBroadcast::MEMPOOL_AND_BROADCAST_TO_ALL, |
368 | 0 | /*wait_callback=*/false); |
369 | 0 | } |
370 | | WalletLoader& walletLoader() override |
371 | 0 | { |
372 | 0 | return *Assert(m_context->wallet_loader); Line | Count | Source | 113 | 0 | #define Assert(val) inline_assertion_check<true>(val, std::source_location::current(), #val) |
|
373 | 0 | } |
374 | | std::unique_ptr<Handler> handleInitMessage(InitMessageFn fn) override |
375 | 0 | { |
376 | 0 | return MakeSignalHandler(::uiInterface.InitMessage_connect(fn)); |
377 | 0 | } |
378 | | std::unique_ptr<Handler> handleMessageBox(MessageBoxFn fn) override |
379 | 0 | { |
380 | 0 | return MakeSignalHandler(::uiInterface.ThreadSafeMessageBox_connect(fn)); |
381 | 0 | } |
382 | | std::unique_ptr<Handler> handleQuestion(QuestionFn fn) override |
383 | 0 | { |
384 | 0 | return MakeSignalHandler(::uiInterface.ThreadSafeQuestion_connect(fn)); |
385 | 0 | } |
386 | | std::unique_ptr<Handler> handleShowProgress(ShowProgressFn fn) override |
387 | 0 | { |
388 | 0 | return MakeSignalHandler(::uiInterface.ShowProgress_connect(fn)); |
389 | 0 | } |
390 | | std::unique_ptr<Handler> handleInitWallet(InitWalletFn fn) override |
391 | 0 | { |
392 | 0 | return MakeSignalHandler(::uiInterface.InitWallet_connect(fn)); |
393 | 0 | } |
394 | | std::unique_ptr<Handler> handleNotifyNumConnectionsChanged(NotifyNumConnectionsChangedFn fn) override |
395 | 0 | { |
396 | 0 | return MakeSignalHandler(::uiInterface.NotifyNumConnectionsChanged_connect(fn)); |
397 | 0 | } |
398 | | std::unique_ptr<Handler> handleNotifyNetworkActiveChanged(NotifyNetworkActiveChangedFn fn) override |
399 | 0 | { |
400 | 0 | return MakeSignalHandler(::uiInterface.NotifyNetworkActiveChanged_connect(fn)); |
401 | 0 | } |
402 | | std::unique_ptr<Handler> handleNotifyAlertChanged(NotifyAlertChangedFn fn) override |
403 | 0 | { |
404 | 0 | return MakeSignalHandler(::uiInterface.NotifyAlertChanged_connect(fn)); |
405 | 0 | } |
406 | | std::unique_ptr<Handler> handleBannedListChanged(BannedListChangedFn fn) override |
407 | 0 | { |
408 | 0 | return MakeSignalHandler(::uiInterface.BannedListChanged_connect(fn)); |
409 | 0 | } |
410 | | std::unique_ptr<Handler> handleNotifyBlockTip(NotifyBlockTipFn fn) override |
411 | 0 | { |
412 | 0 | return MakeSignalHandler(::uiInterface.NotifyBlockTip_connect([fn](SynchronizationState sync_state, const CBlockIndex& block, double verification_progress) { |
413 | 0 | fn(sync_state, BlockTip{block.nHeight, block.GetBlockTime(), block.GetBlockHash()}, verification_progress); |
414 | 0 | })); |
415 | 0 | } |
416 | | std::unique_ptr<Handler> handleNotifyHeaderTip(NotifyHeaderTipFn fn) override |
417 | 0 | { |
418 | 0 | return MakeSignalHandler( |
419 | 0 | ::uiInterface.NotifyHeaderTip_connect([fn](SynchronizationState sync_state, int64_t height, int64_t timestamp, bool presync) { |
420 | 0 | fn(sync_state, BlockTip{(int)height, timestamp, uint256{}}, presync); |
421 | 0 | })); |
422 | 0 | } |
423 | 0 | NodeContext* context() override { return m_context; } |
424 | | void setContext(NodeContext* context) override |
425 | 0 | { |
426 | 0 | m_context = context; |
427 | 0 | } |
428 | 0 | ArgsManager& args() { return *Assert(Assert(m_context)->args); }Line | Count | Source | 113 | 0 | #define Assert(val) inline_assertion_check<true>(val, std::source_location::current(), #val) |
|
429 | 0 | ChainstateManager& chainman() { return *Assert(m_context->chainman); }Line | Count | Source | 113 | 0 | #define Assert(val) inline_assertion_check<true>(val, std::source_location::current(), #val) |
|
430 | | NodeContext* m_context{nullptr}; |
431 | | }; |
432 | | |
433 | | // NOLINTNEXTLINE(misc-no-recursion) |
434 | | bool FillBlock(const CBlockIndex* index, const FoundBlock& block, UniqueLock<RecursiveMutex>& lock, const CChain& active, const BlockManager& blockman) EXCLUSIVE_LOCKS_REQUIRED(cs_main) |
435 | 0 | { |
436 | 0 | if (!index) return false; |
437 | 0 | if (block.m_hash) *block.m_hash = index->GetBlockHash(); |
438 | 0 | if (block.m_height) *block.m_height = index->nHeight; |
439 | 0 | if (block.m_time) *block.m_time = index->GetBlockTime(); |
440 | 0 | if (block.m_max_time) *block.m_max_time = index->GetBlockTimeMax(); |
441 | 0 | if (block.m_mtp_time) *block.m_mtp_time = index->GetMedianTimePast(); |
442 | 0 | if (block.m_in_active_chain) *block.m_in_active_chain = active[index->nHeight] == index; |
443 | 0 | if (block.m_locator) { *block.m_locator = GetLocator(index); } |
444 | 0 | if (block.m_next_block) FillBlock(active[index->nHeight] == index ? active[index->nHeight + 1] : nullptr, *block.m_next_block, lock, active, blockman); |
445 | 0 | if (block.m_data) { |
446 | 0 | REVERSE_LOCK(lock, cs_main); Line | Count | Source | 252 | 0 | #define REVERSE_LOCK(g, cs) typename std::decay<decltype(g)>::type::reverse_lock UNIQUE_NAME(revlock)(g, cs, #cs, __FILE__, __LINE__) Line | Count | Source | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) Line | Count | Source | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) Line | Count | Source | 8 | 0 | #define PASTE(x, y) x ## y |
|
|
|
|
447 | 0 | if (!blockman.ReadBlock(*block.m_data, *index)) block.m_data->SetNull(); |
448 | 0 | } |
449 | 0 | block.found = true; |
450 | 0 | return true; |
451 | 0 | } |
452 | | |
453 | | class NotificationsProxy : public CValidationInterface |
454 | | { |
455 | | public: |
456 | | explicit NotificationsProxy(std::shared_ptr<Chain::Notifications> notifications) |
457 | 0 | : m_notifications(std::move(notifications)) {} |
458 | 0 | virtual ~NotificationsProxy() = default; |
459 | | void TransactionAddedToMempool(const NewMempoolTransactionInfo& tx, uint64_t mempool_sequence) override |
460 | 0 | { |
461 | 0 | m_notifications->transactionAddedToMempool(tx.info.m_tx); |
462 | 0 | } |
463 | | void TransactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason, uint64_t mempool_sequence) override |
464 | 0 | { |
465 | 0 | m_notifications->transactionRemovedFromMempool(tx, reason); |
466 | 0 | } |
467 | | void BlockConnected(const ChainstateRole& role, const std::shared_ptr<const CBlock>& block, const CBlockIndex* index) override |
468 | 0 | { |
469 | 0 | m_notifications->blockConnected(role, kernel::MakeBlockInfo(index, block.get())); |
470 | 0 | } |
471 | | void BlockDisconnected(const std::shared_ptr<const CBlock>& block, const CBlockIndex* index) override |
472 | 0 | { |
473 | 0 | m_notifications->blockDisconnected(kernel::MakeBlockInfo(index, block.get())); |
474 | 0 | } |
475 | | void UpdatedBlockTip(const CBlockIndex* index, const CBlockIndex* fork_index, bool is_ibd) override |
476 | 0 | { |
477 | 0 | m_notifications->updatedBlockTip(); |
478 | 0 | } |
479 | | void ChainStateFlushed(const ChainstateRole& role, const CBlockLocator& locator) override |
480 | 0 | { |
481 | 0 | m_notifications->chainStateFlushed(role, locator); |
482 | 0 | } |
483 | | std::shared_ptr<Chain::Notifications> m_notifications; |
484 | | }; |
485 | | |
486 | | class NotificationsHandlerImpl : public Handler |
487 | | { |
488 | | public: |
489 | | explicit NotificationsHandlerImpl(ValidationSignals& signals, std::shared_ptr<Chain::Notifications> notifications) |
490 | 0 | : m_signals{signals}, m_proxy{std::make_shared<NotificationsProxy>(std::move(notifications))} |
491 | 0 | { |
492 | 0 | m_signals.RegisterSharedValidationInterface(m_proxy); |
493 | 0 | } |
494 | 0 | ~NotificationsHandlerImpl() override { disconnect(); } |
495 | | void disconnect() override |
496 | 0 | { |
497 | 0 | if (m_proxy) { |
498 | 0 | m_signals.UnregisterSharedValidationInterface(m_proxy); |
499 | 0 | m_proxy.reset(); |
500 | 0 | } |
501 | 0 | } |
502 | | ValidationSignals& m_signals; |
503 | | std::shared_ptr<NotificationsProxy> m_proxy; |
504 | | }; |
505 | | |
506 | | class RpcHandlerImpl : public Handler |
507 | | { |
508 | | public: |
509 | 0 | explicit RpcHandlerImpl(const CRPCCommand& command) : m_command(command), m_wrapped_command(&command) |
510 | 0 | { |
511 | 0 | m_command.actor = [this](const JSONRPCRequest& request, UniValue& result, bool last_handler) { |
512 | 0 | if (!m_wrapped_command) return false; |
513 | 0 | try { |
514 | 0 | return m_wrapped_command->actor(request, result, last_handler); |
515 | 0 | } catch (const UniValue& e) { |
516 | | // If this is not the last handler and a wallet not found |
517 | | // exception was thrown, return false so the next handler can |
518 | | // try to handle the request. Otherwise, reraise the exception. |
519 | 0 | if (!last_handler) { |
520 | 0 | const UniValue& code = e["code"]; |
521 | 0 | if (code.isNum() && code.getInt<int>() == RPC_WALLET_NOT_FOUND) { |
522 | 0 | return false; |
523 | 0 | } |
524 | 0 | } |
525 | 0 | throw; |
526 | 0 | } |
527 | 0 | }; |
528 | 0 | ::tableRPC.appendCommand(m_command.name, &m_command); |
529 | 0 | } |
530 | | |
531 | | void disconnect() final |
532 | 0 | { |
533 | 0 | if (m_wrapped_command) { |
534 | 0 | m_wrapped_command = nullptr; |
535 | 0 | ::tableRPC.removeCommand(m_command.name, &m_command); |
536 | 0 | } |
537 | 0 | } |
538 | | |
539 | 0 | ~RpcHandlerImpl() override { disconnect(); } |
540 | | |
541 | | CRPCCommand m_command; |
542 | | const CRPCCommand* m_wrapped_command; |
543 | | }; |
544 | | |
545 | | class ChainImpl : public Chain |
546 | | { |
547 | | public: |
548 | 0 | explicit ChainImpl(NodeContext& node) : m_node(node) {} |
549 | | std::optional<int> getHeight() override |
550 | 0 | { |
551 | 0 | const int height{WITH_LOCK(::cs_main, return chainman().ActiveChain().Height())};Line | Count | Source | 297 | 0 | #define WITH_LOCK(cs, code) (MaybeCheckNotHeld(cs), [&]() -> decltype(auto) { LOCK(cs); code; }()) |
|
552 | 0 | return height >= 0 ? std::optional{height} : std::nullopt; |
553 | 0 | } |
554 | | uint256 getBlockHash(int height) override |
555 | 0 | { |
556 | 0 | LOCK(::cs_main); Line | Count | Source | 266 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) Line | Count | Source | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) Line | Count | Source | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) Line | Count | Source | 8 | 0 | #define PASTE(x, y) x ## y |
|
|
|
|
557 | 0 | return Assert(chainman().ActiveChain()[height])->GetBlockHash(); Line | Count | Source | 113 | 0 | #define Assert(val) inline_assertion_check<true>(val, std::source_location::current(), #val) |
|
558 | 0 | } |
559 | | bool haveBlockOnDisk(int height) override |
560 | 0 | { |
561 | 0 | LOCK(::cs_main); Line | Count | Source | 266 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) Line | Count | Source | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) Line | Count | Source | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) Line | Count | Source | 8 | 0 | #define PASTE(x, y) x ## y |
|
|
|
|
562 | 0 | const CBlockIndex* block{chainman().ActiveChain()[height]}; |
563 | 0 | return block && ((block->nStatus & BLOCK_HAVE_DATA) != 0) && block->nTx > 0; |
564 | 0 | } |
565 | | std::optional<int> findLocatorFork(const CBlockLocator& locator) override |
566 | 0 | { |
567 | 0 | LOCK(::cs_main); Line | Count | Source | 266 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) Line | Count | Source | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) Line | Count | Source | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) Line | Count | Source | 8 | 0 | #define PASTE(x, y) x ## y |
|
|
|
|
568 | 0 | if (const CBlockIndex* fork = chainman().ActiveChainstate().FindForkInGlobalIndex(locator)) { |
569 | 0 | return fork->nHeight; |
570 | 0 | } |
571 | 0 | return std::nullopt; |
572 | 0 | } |
573 | | bool hasBlockFilterIndex(BlockFilterType filter_type) override |
574 | 0 | { |
575 | 0 | return GetBlockFilterIndex(filter_type) != nullptr; |
576 | 0 | } |
577 | | std::optional<bool> blockFilterMatchesAny(BlockFilterType filter_type, const uint256& block_hash, const GCSFilter::ElementSet& filter_set) override |
578 | 0 | { |
579 | 0 | const BlockFilterIndex* block_filter_index{GetBlockFilterIndex(filter_type)}; |
580 | 0 | if (!block_filter_index) return std::nullopt; |
581 | | |
582 | 0 | BlockFilter filter; |
583 | 0 | const CBlockIndex* index{WITH_LOCK(::cs_main, return chainman().m_blockman.LookupBlockIndex(block_hash))};Line | Count | Source | 297 | 0 | #define WITH_LOCK(cs, code) (MaybeCheckNotHeld(cs), [&]() -> decltype(auto) { LOCK(cs); code; }()) |
|
584 | 0 | if (index == nullptr || !block_filter_index->LookupFilter(index, filter)) return std::nullopt; |
585 | 0 | return filter.GetFilter().MatchAny(filter_set); |
586 | 0 | } |
587 | | bool findBlock(const uint256& hash, const FoundBlock& block) override |
588 | 0 | { |
589 | 0 | WAIT_LOCK(cs_main, lock); Line | Count | Source | 272 | 0 | #define WAIT_LOCK(cs, name) UniqueLock name(LOCK_ARGS(cs)) Line | Count | Source | 270 | 0 | #define LOCK_ARGS(cs) MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__ |
|
|
590 | 0 | return FillBlock(chainman().m_blockman.LookupBlockIndex(hash), block, lock, chainman().ActiveChain(), chainman().m_blockman); |
591 | 0 | } |
592 | | bool findFirstBlockWithTimeAndHeight(int64_t min_time, int min_height, const FoundBlock& block) override |
593 | 0 | { |
594 | 0 | WAIT_LOCK(cs_main, lock); Line | Count | Source | 272 | 0 | #define WAIT_LOCK(cs, name) UniqueLock name(LOCK_ARGS(cs)) Line | Count | Source | 270 | 0 | #define LOCK_ARGS(cs) MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__ |
|
|
595 | 0 | const CChain& active = chainman().ActiveChain(); |
596 | 0 | return FillBlock(active.FindEarliestAtLeast(min_time, min_height), block, lock, active, chainman().m_blockman); |
597 | 0 | } |
598 | | bool findAncestorByHeight(const uint256& block_hash, int ancestor_height, const FoundBlock& ancestor_out) override |
599 | 0 | { |
600 | 0 | WAIT_LOCK(cs_main, lock); Line | Count | Source | 272 | 0 | #define WAIT_LOCK(cs, name) UniqueLock name(LOCK_ARGS(cs)) Line | Count | Source | 270 | 0 | #define LOCK_ARGS(cs) MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__ |
|
|
601 | 0 | const CChain& active = chainman().ActiveChain(); |
602 | 0 | if (const CBlockIndex* block = chainman().m_blockman.LookupBlockIndex(block_hash)) { |
603 | 0 | if (const CBlockIndex* ancestor = block->GetAncestor(ancestor_height)) { |
604 | 0 | return FillBlock(ancestor, ancestor_out, lock, active, chainman().m_blockman); |
605 | 0 | } |
606 | 0 | } |
607 | 0 | return FillBlock(nullptr, ancestor_out, lock, active, chainman().m_blockman); |
608 | 0 | } |
609 | | bool findAncestorByHash(const uint256& block_hash, const uint256& ancestor_hash, const FoundBlock& ancestor_out) override |
610 | 0 | { |
611 | 0 | WAIT_LOCK(cs_main, lock); Line | Count | Source | 272 | 0 | #define WAIT_LOCK(cs, name) UniqueLock name(LOCK_ARGS(cs)) Line | Count | Source | 270 | 0 | #define LOCK_ARGS(cs) MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__ |
|
|
612 | 0 | const CBlockIndex* block = chainman().m_blockman.LookupBlockIndex(block_hash); |
613 | 0 | const CBlockIndex* ancestor = chainman().m_blockman.LookupBlockIndex(ancestor_hash); |
614 | 0 | if (block && ancestor && block->GetAncestor(ancestor->nHeight) != ancestor) ancestor = nullptr; |
615 | 0 | return FillBlock(ancestor, ancestor_out, lock, chainman().ActiveChain(), chainman().m_blockman); |
616 | 0 | } |
617 | | bool findCommonAncestor(const uint256& block_hash1, const uint256& block_hash2, const FoundBlock& ancestor_out, const FoundBlock& block1_out, const FoundBlock& block2_out) override |
618 | 0 | { |
619 | 0 | WAIT_LOCK(cs_main, lock); Line | Count | Source | 272 | 0 | #define WAIT_LOCK(cs, name) UniqueLock name(LOCK_ARGS(cs)) Line | Count | Source | 270 | 0 | #define LOCK_ARGS(cs) MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__ |
|
|
620 | 0 | const CChain& active = chainman().ActiveChain(); |
621 | 0 | const CBlockIndex* block1 = chainman().m_blockman.LookupBlockIndex(block_hash1); |
622 | 0 | const CBlockIndex* block2 = chainman().m_blockman.LookupBlockIndex(block_hash2); |
623 | 0 | const CBlockIndex* ancestor = block1 && block2 ? LastCommonAncestor(block1, block2) : nullptr; |
624 | | // Using & instead of && below to avoid short circuiting and leaving |
625 | | // output uninitialized. Cast bool to int to avoid -Wbitwise-instead-of-logical |
626 | | // compiler warnings. |
627 | 0 | return int{FillBlock(ancestor, ancestor_out, lock, active, chainman().m_blockman)} & |
628 | 0 | int{FillBlock(block1, block1_out, lock, active, chainman().m_blockman)} & |
629 | 0 | int{FillBlock(block2, block2_out, lock, active, chainman().m_blockman)}; |
630 | 0 | } |
631 | 0 | void findCoins(std::map<COutPoint, Coin>& coins) override { return FindCoins(m_node, coins); } |
632 | | double guessVerificationProgress(const uint256& block_hash) override |
633 | 0 | { |
634 | 0 | LOCK(chainman().GetMutex()); Line | Count | Source | 266 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) Line | Count | Source | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) Line | Count | Source | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) Line | Count | Source | 8 | 0 | #define PASTE(x, y) x ## y |
|
|
|
|
635 | 0 | return chainman().GuessVerificationProgress(chainman().m_blockman.LookupBlockIndex(block_hash)); |
636 | 0 | } |
637 | | bool hasBlocks(const uint256& block_hash, int min_height, std::optional<int> max_height) override |
638 | 0 | { |
639 | | // hasBlocks returns true if all ancestors of block_hash in specified |
640 | | // range have block data (are not pruned), false if any ancestors in |
641 | | // specified range are missing data. |
642 | | // |
643 | | // For simplicity and robustness, min_height and max_height are only |
644 | | // used to limit the range, and passing min_height that's too low or |
645 | | // max_height that's too high will not crash or change the result. |
646 | 0 | LOCK(::cs_main); Line | Count | Source | 266 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) Line | Count | Source | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) Line | Count | Source | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) Line | Count | Source | 8 | 0 | #define PASTE(x, y) x ## y |
|
|
|
|
647 | 0 | if (const CBlockIndex* block = chainman().m_blockman.LookupBlockIndex(block_hash)) { |
648 | 0 | if (max_height && block->nHeight >= *max_height) block = block->GetAncestor(*max_height); |
649 | 0 | for (; block->nStatus & BLOCK_HAVE_DATA; block = block->pprev) { |
650 | | // Check pprev to not segfault if min_height is too low |
651 | 0 | if (block->nHeight <= min_height || !block->pprev) return true; |
652 | 0 | } |
653 | 0 | } |
654 | 0 | return false; |
655 | 0 | } |
656 | | RBFTransactionState isRBFOptIn(const CTransaction& tx) override |
657 | 0 | { |
658 | 0 | if (!m_node.mempool) return IsRBFOptInEmptyMempool(tx); |
659 | 0 | LOCK(m_node.mempool->cs); Line | Count | Source | 266 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) Line | Count | Source | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) Line | Count | Source | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) Line | Count | Source | 8 | 0 | #define PASTE(x, y) x ## y |
|
|
|
|
660 | 0 | return IsRBFOptIn(tx, *m_node.mempool); |
661 | 0 | } |
662 | | bool isInMempool(const Txid& txid) override |
663 | 0 | { |
664 | 0 | if (!m_node.mempool) return false; |
665 | 0 | return m_node.mempool->exists(txid); |
666 | 0 | } |
667 | | bool hasDescendantsInMempool(const Txid& txid) override |
668 | 0 | { |
669 | 0 | if (!m_node.mempool) return false; |
670 | 0 | return m_node.mempool->HasDescendants(txid); |
671 | 0 | } |
672 | | bool broadcastTransaction(const CTransactionRef& tx, |
673 | | const CAmount& max_tx_fee, |
674 | | TxBroadcast broadcast_method, |
675 | | std::string& err_string) override |
676 | 0 | { |
677 | 0 | const TransactionError err = BroadcastTransaction(m_node, tx, err_string, max_tx_fee, broadcast_method, /*wait_callback=*/false); |
678 | | // Chain clients only care about failures to accept the tx to the mempool. Disregard non-mempool related failures. |
679 | | // Note: this will need to be updated if BroadcastTransactions() is updated to return other non-mempool failures |
680 | | // that Chain clients do not need to know about. |
681 | 0 | return TransactionError::OK == err; |
682 | 0 | } |
683 | | void getTransactionAncestry(const Txid& txid, size_t& ancestors, size_t& cluster_count, size_t* ancestorsize, CAmount* ancestorfees) override |
684 | 0 | { |
685 | 0 | ancestors = cluster_count = 0; |
686 | 0 | if (!m_node.mempool) return; |
687 | 0 | m_node.mempool->GetTransactionAncestry(txid, ancestors, cluster_count, ancestorsize, ancestorfees); |
688 | 0 | } |
689 | | |
690 | | std::map<COutPoint, CAmount> calculateIndividualBumpFees(const std::vector<COutPoint>& outpoints, const CFeeRate& target_feerate) override |
691 | 0 | { |
692 | 0 | if (!m_node.mempool) { |
693 | 0 | std::map<COutPoint, CAmount> bump_fees; |
694 | 0 | for (const auto& outpoint : outpoints) { |
695 | 0 | bump_fees.emplace(outpoint, 0); |
696 | 0 | } |
697 | 0 | return bump_fees; |
698 | 0 | } |
699 | 0 | return MiniMiner(*m_node.mempool, outpoints).CalculateBumpFees(target_feerate); |
700 | 0 | } |
701 | | |
702 | | std::optional<CAmount> calculateCombinedBumpFee(const std::vector<COutPoint>& outpoints, const CFeeRate& target_feerate) override |
703 | 0 | { |
704 | 0 | if (!m_node.mempool) { |
705 | 0 | return 0; |
706 | 0 | } |
707 | 0 | return MiniMiner(*m_node.mempool, outpoints).CalculateTotalBumpFees(target_feerate); |
708 | 0 | } |
709 | | void getPackageLimits(unsigned int& limit_ancestor_count, unsigned int& limit_descendant_count) override |
710 | 0 | { |
711 | 0 | const CTxMemPool::Limits default_limits{}; |
712 | |
|
713 | 0 | const CTxMemPool::Limits& limits{m_node.mempool ? m_node.mempool->m_opts.limits : default_limits}; |
714 | |
|
715 | 0 | limit_ancestor_count = limits.ancestor_count; |
716 | 0 | limit_descendant_count = limits.descendant_count; |
717 | 0 | } |
718 | | util::Result<void> checkChainLimits(const CTransactionRef& tx) override |
719 | 0 | { |
720 | 0 | if (!m_node.mempool) return {}; |
721 | 0 | if (!m_node.mempool->CheckPolicyLimits(tx)) { |
722 | 0 | return util::Error{Untranslated("too many unconfirmed transactions in cluster")}; |
723 | 0 | } |
724 | 0 | return {}; |
725 | 0 | } |
726 | | CFeeRate estimateSmartFee(int num_blocks, bool conservative, FeeCalculation* calc) override |
727 | 0 | { |
728 | 0 | if (!m_node.fee_estimator) return {}; |
729 | 0 | return m_node.fee_estimator->estimateSmartFee(num_blocks, calc, conservative); |
730 | 0 | } |
731 | | unsigned int estimateMaxBlocks() override |
732 | 0 | { |
733 | 0 | if (!m_node.fee_estimator) return 0; |
734 | 0 | return m_node.fee_estimator->HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE); |
735 | 0 | } |
736 | | CFeeRate mempoolMinFee() override |
737 | 0 | { |
738 | 0 | if (!m_node.mempool) return {}; |
739 | 0 | return m_node.mempool->GetMinFee(); |
740 | 0 | } |
741 | | CFeeRate relayMinFee() override |
742 | 0 | { |
743 | 0 | if (!m_node.mempool) return CFeeRate{DEFAULT_MIN_RELAY_TX_FEE}; |
744 | 0 | return m_node.mempool->m_opts.min_relay_feerate; |
745 | 0 | } |
746 | | CFeeRate relayIncrementalFee() override |
747 | 0 | { |
748 | 0 | if (!m_node.mempool) return CFeeRate{DEFAULT_INCREMENTAL_RELAY_FEE}; |
749 | 0 | return m_node.mempool->m_opts.incremental_relay_feerate; |
750 | 0 | } |
751 | | CFeeRate relayDustFee() override |
752 | 0 | { |
753 | 0 | if (!m_node.mempool) return CFeeRate{DUST_RELAY_TX_FEE}; |
754 | 0 | return m_node.mempool->m_opts.dust_relay_feerate; |
755 | 0 | } |
756 | | bool havePruned() override |
757 | 0 | { |
758 | 0 | LOCK(::cs_main); Line | Count | Source | 266 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) Line | Count | Source | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) Line | Count | Source | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) Line | Count | Source | 8 | 0 | #define PASTE(x, y) x ## y |
|
|
|
|
759 | 0 | return chainman().m_blockman.m_have_pruned; |
760 | 0 | } |
761 | | std::optional<int> getPruneHeight() override |
762 | 0 | { |
763 | 0 | LOCK(chainman().GetMutex()); Line | Count | Source | 266 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) Line | Count | Source | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) Line | Count | Source | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) Line | Count | Source | 8 | 0 | #define PASTE(x, y) x ## y |
|
|
|
|
764 | 0 | return GetPruneHeight(chainman().m_blockman, chainman().ActiveChain()); |
765 | 0 | } |
766 | 0 | bool isReadyToBroadcast() override { return !chainman().m_blockman.LoadingBlocks() && !isInitialBlockDownload(); } |
767 | | bool isInitialBlockDownload() override |
768 | 0 | { |
769 | 0 | return chainman().IsInitialBlockDownload(); |
770 | 0 | } |
771 | 0 | bool shutdownRequested() override { return ShutdownRequested(m_node); } |
772 | 0 | void initMessage(const std::string& message) override { ::uiInterface.InitMessage(message); } |
773 | 0 | void initWarning(const bilingual_str& message) override { InitWarning(message); } |
774 | 0 | void initError(const bilingual_str& message) override { InitError(message); } |
775 | | void showProgress(const std::string& title, int progress, bool resume_possible) override |
776 | 0 | { |
777 | 0 | ::uiInterface.ShowProgress(title, progress, resume_possible); |
778 | 0 | } |
779 | | std::unique_ptr<Handler> handleNotifications(std::shared_ptr<Notifications> notifications) override |
780 | 0 | { |
781 | 0 | return std::make_unique<NotificationsHandlerImpl>(validation_signals(), std::move(notifications)); |
782 | 0 | } |
783 | | void waitForNotificationsIfTipChanged(const uint256& old_tip) override |
784 | 0 | { |
785 | 0 | if (!old_tip.IsNull() && old_tip == WITH_LOCK(::cs_main, return chainman().ActiveChain().Tip()->GetBlockHash())) return; Line | Count | Source | 297 | 0 | #define WITH_LOCK(cs, code) (MaybeCheckNotHeld(cs), [&]() -> decltype(auto) { LOCK(cs); code; }()) |
|
786 | 0 | validation_signals().SyncWithValidationInterfaceQueue(); |
787 | 0 | } |
788 | | void waitForNotifications() override |
789 | 0 | { |
790 | 0 | validation_signals().SyncWithValidationInterfaceQueue(); |
791 | 0 | } |
792 | | std::unique_ptr<Handler> handleRpc(const CRPCCommand& command) override |
793 | 0 | { |
794 | 0 | return std::make_unique<RpcHandlerImpl>(command); |
795 | 0 | } |
796 | 0 | bool rpcEnableDeprecated(const std::string& method) override { return IsDeprecatedRPCEnabled(method); } |
797 | | common::SettingsValue getSetting(const std::string& name) override |
798 | 0 | { |
799 | 0 | return args().GetSetting(name); |
800 | 0 | } |
801 | | std::vector<common::SettingsValue> getSettingsList(const std::string& name) override |
802 | 0 | { |
803 | 0 | return args().GetSettingsList(name); |
804 | 0 | } |
805 | | common::SettingsValue getRwSetting(const std::string& name) override |
806 | 0 | { |
807 | 0 | common::SettingsValue result; |
808 | 0 | args().LockSettings([&](const common::Settings& settings) { |
809 | 0 | if (const common::SettingsValue* value = common::FindKey(settings.rw_settings, name)) { |
810 | 0 | result = *value; |
811 | 0 | } |
812 | 0 | }); |
813 | 0 | return result; |
814 | 0 | } |
815 | | bool updateRwSetting(const std::string& name, |
816 | | const interfaces::SettingsUpdate& update_settings_func) override |
817 | 0 | { |
818 | 0 | std::optional<interfaces::SettingsAction> action; |
819 | 0 | args().LockSettings([&](common::Settings& settings) { |
820 | 0 | if (auto* value = common::FindKey(settings.rw_settings, name)) { |
821 | 0 | action = update_settings_func(*value); |
822 | 0 | if (value->isNull()) settings.rw_settings.erase(name); |
823 | 0 | } else { |
824 | 0 | UniValue new_value; |
825 | 0 | action = update_settings_func(new_value); |
826 | 0 | if (!new_value.isNull()) settings.rw_settings[name] = std::move(new_value); |
827 | 0 | } |
828 | 0 | }); |
829 | 0 | if (!action) return false; |
830 | | // Now dump value to disk if requested |
831 | 0 | return *action != interfaces::SettingsAction::WRITE || args().WriteSettingsFile(); |
832 | 0 | } |
833 | | bool overwriteRwSetting(const std::string& name, common::SettingsValue value, interfaces::SettingsAction action) override |
834 | 0 | { |
835 | 0 | return updateRwSetting(name, [&](common::SettingsValue& settings) { |
836 | 0 | settings = std::move(value); |
837 | 0 | return action; |
838 | 0 | }); |
839 | 0 | } |
840 | | bool deleteRwSettings(const std::string& name, interfaces::SettingsAction action) override |
841 | 0 | { |
842 | 0 | return overwriteRwSetting(name, {}, action); |
843 | 0 | } |
844 | | void requestMempoolTransactions(Notifications& notifications) override |
845 | 0 | { |
846 | 0 | if (!m_node.mempool) return; |
847 | 0 | LOCK2(::cs_main, m_node.mempool->cs); Line | Count | Source | 268 | 0 | UniqueLock criticalblock1(MaybeCheckNotHeld(cs1), #cs1, __FILE__, __LINE__); \ | 269 | 0 | UniqueLock criticalblock2(MaybeCheckNotHeld(cs2), #cs2, __FILE__, __LINE__) |
|
848 | 0 | for (const CTxMemPoolEntry& entry : m_node.mempool->entryAll()) { |
849 | 0 | notifications.transactionAddedToMempool(entry.GetSharedTx()); |
850 | 0 | } |
851 | 0 | } |
852 | | bool hasAssumedValidChain() override |
853 | 0 | { |
854 | 0 | LOCK(::cs_main); Line | Count | Source | 266 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) Line | Count | Source | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) Line | Count | Source | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) Line | Count | Source | 8 | 0 | #define PASTE(x, y) x ## y |
|
|
|
|
855 | 0 | return bool{chainman().CurrentChainstate().m_from_snapshot_blockhash}; |
856 | 0 | } |
857 | | |
858 | 0 | NodeContext* context() override { return &m_node; } |
859 | 0 | ArgsManager& args() { return *Assert(m_node.args); }Line | Count | Source | 113 | 0 | #define Assert(val) inline_assertion_check<true>(val, std::source_location::current(), #val) |
|
860 | 0 | ChainstateManager& chainman() { return *Assert(m_node.chainman); }Line | Count | Source | 113 | 0 | #define Assert(val) inline_assertion_check<true>(val, std::source_location::current(), #val) |
|
861 | 0 | ValidationSignals& validation_signals() { return *Assert(m_node.validation_signals); }Line | Count | Source | 113 | 0 | #define Assert(val) inline_assertion_check<true>(val, std::source_location::current(), #val) |
|
862 | | NodeContext& m_node; |
863 | | }; |
864 | | |
865 | | class BlockTemplateImpl : public BlockTemplate |
866 | | { |
867 | | public: |
868 | | explicit BlockTemplateImpl(BlockAssembler::Options assemble_options, |
869 | | std::unique_ptr<CBlockTemplate> block_template, |
870 | 0 | NodeContext& node) : m_assemble_options(std::move(assemble_options)), |
871 | 0 | m_block_template(std::move(block_template)), |
872 | 0 | m_node(node) |
873 | 0 | { |
874 | 0 | assert(m_block_template); |
875 | 0 | } |
876 | | |
877 | | CBlockHeader getBlockHeader() override |
878 | 0 | { |
879 | 0 | return m_block_template->block; |
880 | 0 | } |
881 | | |
882 | | CBlock getBlock() override |
883 | 0 | { |
884 | 0 | return m_block_template->block; |
885 | 0 | } |
886 | | |
887 | | std::vector<CAmount> getTxFees() override |
888 | 0 | { |
889 | 0 | return m_block_template->vTxFees; |
890 | 0 | } |
891 | | |
892 | | std::vector<int64_t> getTxSigops() override |
893 | 0 | { |
894 | 0 | return m_block_template->vTxSigOpsCost; |
895 | 0 | } |
896 | | |
897 | | CoinbaseTx getCoinbaseTx() override |
898 | 0 | { |
899 | 0 | return m_block_template->m_coinbase_tx; |
900 | 0 | } |
901 | | |
902 | | std::vector<uint256> getCoinbaseMerklePath() override |
903 | 0 | { |
904 | 0 | return TransactionMerklePath(m_block_template->block, 0); |
905 | 0 | } |
906 | | |
907 | | bool submitSolution(uint32_t version, uint32_t timestamp, uint32_t nonce, CTransactionRef coinbase) override |
908 | 0 | { |
909 | 0 | AddMerkleRootAndCoinbase(m_block_template->block, std::move(coinbase), version, timestamp, nonce); |
910 | 0 | return chainman().ProcessNewBlock(std::make_shared<const CBlock>(m_block_template->block), /*force_processing=*/true, /*min_pow_checked=*/true, /*new_block=*/nullptr); |
911 | 0 | } |
912 | | |
913 | | std::unique_ptr<BlockTemplate> waitNext(BlockWaitOptions options) override |
914 | 0 | { |
915 | 0 | auto new_template = WaitAndCreateNewBlock(chainman(), notifications(), m_node.mempool.get(), m_block_template, options, m_assemble_options, m_interrupt_wait); |
916 | 0 | if (new_template) return std::make_unique<BlockTemplateImpl>(m_assemble_options, std::move(new_template), m_node); |
917 | 0 | return nullptr; |
918 | 0 | } |
919 | | |
920 | | void interruptWait() override |
921 | 0 | { |
922 | 0 | InterruptWait(notifications(), m_interrupt_wait); |
923 | 0 | } |
924 | | |
925 | | const BlockAssembler::Options m_assemble_options; |
926 | | |
927 | | const std::unique_ptr<CBlockTemplate> m_block_template; |
928 | | |
929 | | bool m_interrupt_wait{false}; |
930 | 0 | ChainstateManager& chainman() { return *Assert(m_node.chainman); }Line | Count | Source | 113 | 0 | #define Assert(val) inline_assertion_check<true>(val, std::source_location::current(), #val) |
|
931 | 0 | KernelNotifications& notifications() { return *Assert(m_node.notifications); }Line | Count | Source | 113 | 0 | #define Assert(val) inline_assertion_check<true>(val, std::source_location::current(), #val) |
|
932 | | NodeContext& m_node; |
933 | | }; |
934 | | |
935 | | class MinerImpl : public Mining |
936 | | { |
937 | | public: |
938 | 0 | explicit MinerImpl(NodeContext& node) : m_node(node) {} |
939 | | |
940 | | bool isTestChain() override |
941 | 0 | { |
942 | 0 | return chainman().GetParams().IsTestChain(); |
943 | 0 | } |
944 | | |
945 | | bool isInitialBlockDownload() override |
946 | 0 | { |
947 | 0 | return chainman().IsInitialBlockDownload(); |
948 | 0 | } |
949 | | |
950 | | std::optional<BlockRef> getTip() override |
951 | 0 | { |
952 | 0 | return GetTip(chainman()); |
953 | 0 | } |
954 | | |
955 | | std::optional<BlockRef> waitTipChanged(uint256 current_tip, MillisecondsDouble timeout) override |
956 | 0 | { |
957 | 0 | return WaitTipChanged(chainman(), notifications(), current_tip, timeout, m_interrupt_mining); |
958 | 0 | } |
959 | | |
960 | | std::unique_ptr<BlockTemplate> createNewBlock(const BlockCreateOptions& options, bool cooldown) override |
961 | 0 | { |
962 | | // Reject too-small values instead of clamping so callers don't silently |
963 | | // end up mining with different options than requested. This matches the |
964 | | // behavior of the `-blockreservedweight` startup option, which rejects |
965 | | // values below MINIMUM_BLOCK_RESERVED_WEIGHT. |
966 | 0 | if (options.block_reserved_weight && options.block_reserved_weight < MINIMUM_BLOCK_RESERVED_WEIGHT) { |
967 | 0 | throw std::runtime_error(strprintf("block_reserved_weight (%zu) must be at least %u weight units",Line | Count | Source | 1172 | 0 | #define strprintf tfm::format |
|
968 | 0 | *options.block_reserved_weight, |
969 | 0 | MINIMUM_BLOCK_RESERVED_WEIGHT)); |
970 | 0 | } |
971 | | |
972 | | // Ensure m_tip_block is set so consumers of BlockTemplate can rely on that. |
973 | 0 | std::optional<BlockRef> maybe_tip{waitTipChanged(uint256::ZERO, MillisecondsDouble::max())}; |
974 | |
|
975 | 0 | if (!maybe_tip) return {}; |
976 | | |
977 | 0 | if (cooldown) { |
978 | | // Do not return a template during IBD, because it can have long |
979 | | // pauses and sometimes takes a while to get started. Although this |
980 | | // is useful in general, it's gated behind the cooldown argument, |
981 | | // because on regtest and single miner signets this would wait |
982 | | // forever if no block was mined in the past day. |
983 | 0 | while (chainman().IsInitialBlockDownload()) { |
984 | 0 | maybe_tip = waitTipChanged(maybe_tip->hash, MillisecondsDouble{1000}); |
985 | 0 | if (!maybe_tip || chainman().m_interrupt || WITH_LOCK(notifications().m_tip_block_mutex, return m_interrupt_mining)) return {};Line | Count | Source | 297 | 0 | #define WITH_LOCK(cs, code) (MaybeCheckNotHeld(cs), [&]() -> decltype(auto) { LOCK(cs); code; }()) |
|
986 | 0 | } |
987 | | |
988 | | // Also wait during the final catch-up moments after IBD. |
989 | 0 | if (!CooldownIfHeadersAhead(chainman(), notifications(), *maybe_tip, m_interrupt_mining)) return {}; |
990 | 0 | } |
991 | | |
992 | 0 | BlockAssembler::Options assemble_options{options}; |
993 | 0 | ApplyArgsManOptions(*Assert(m_node.args), assemble_options); Line | Count | Source | 113 | 0 | #define Assert(val) inline_assertion_check<true>(val, std::source_location::current(), #val) |
|
994 | 0 | return std::make_unique<BlockTemplateImpl>(assemble_options, BlockAssembler{chainman().ActiveChainstate(), context()->mempool.get(), assemble_options}.CreateNewBlock(), m_node); |
995 | 0 | } |
996 | | |
997 | | void interrupt() override |
998 | 0 | { |
999 | 0 | InterruptWait(notifications(), m_interrupt_mining); |
1000 | 0 | } |
1001 | | |
1002 | | bool checkBlock(const CBlock& block, const node::BlockCheckOptions& options, std::string& reason, std::string& debug) override |
1003 | 0 | { |
1004 | 0 | LOCK(chainman().GetMutex()); Line | Count | Source | 266 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) Line | Count | Source | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) Line | Count | Source | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) Line | Count | Source | 8 | 0 | #define PASTE(x, y) x ## y |
|
|
|
|
1005 | 0 | BlockValidationState state{TestBlockValidity(chainman().ActiveChainstate(), block, /*check_pow=*/options.check_pow, /*check_merkle_root=*/options.check_merkle_root)}; |
1006 | 0 | reason = state.GetRejectReason(); |
1007 | 0 | debug = state.GetDebugMessage(); |
1008 | 0 | return state.IsValid(); |
1009 | 0 | } |
1010 | | |
1011 | 0 | NodeContext* context() override { return &m_node; } |
1012 | 0 | ChainstateManager& chainman() { return *Assert(m_node.chainman); }Line | Count | Source | 113 | 0 | #define Assert(val) inline_assertion_check<true>(val, std::source_location::current(), #val) |
|
1013 | 0 | KernelNotifications& notifications() { return *Assert(m_node.notifications); }Line | Count | Source | 113 | 0 | #define Assert(val) inline_assertion_check<true>(val, std::source_location::current(), #val) |
|
1014 | | // Treat as if guarded by notifications().m_tip_block_mutex |
1015 | | bool m_interrupt_mining{false}; |
1016 | | NodeContext& m_node; |
1017 | | }; |
1018 | | } // namespace |
1019 | | } // namespace node |
1020 | | |
1021 | | namespace interfaces { |
1022 | 0 | std::unique_ptr<Node> MakeNode(node::NodeContext& context) { return std::make_unique<node::NodeImpl>(context); } |
1023 | 0 | std::unique_ptr<Chain> MakeChain(node::NodeContext& context) { return std::make_unique<node::ChainImpl>(context); } |
1024 | | std::unique_ptr<Mining> MakeMining(node::NodeContext& context, bool wait_loaded) |
1025 | 0 | { |
1026 | 0 | if (wait_loaded) { |
1027 | 0 | node::KernelNotifications& kernel_notifications(*Assert(context.notifications)); Line | Count | Source | 113 | 0 | #define Assert(val) inline_assertion_check<true>(val, std::source_location::current(), #val) |
|
1028 | 0 | util::SignalInterrupt& interrupt(*Assert(context.shutdown_signal)); Line | Count | Source | 113 | 0 | #define Assert(val) inline_assertion_check<true>(val, std::source_location::current(), #val) |
|
1029 | 0 | WAIT_LOCK(kernel_notifications.m_tip_block_mutex, lock); Line | Count | Source | 272 | 0 | #define WAIT_LOCK(cs, name) UniqueLock name(LOCK_ARGS(cs)) Line | Count | Source | 270 | 0 | #define LOCK_ARGS(cs) MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__ |
|
|
1030 | 0 | kernel_notifications.m_tip_block_cv.wait(lock, [&]() EXCLUSIVE_LOCKS_REQUIRED(kernel_notifications.m_tip_block_mutex) { |
1031 | 0 | return kernel_notifications.m_state.chainstate_loaded || interrupt; |
1032 | 0 | }); |
1033 | 0 | if (interrupt) return nullptr; |
1034 | 0 | } |
1035 | 0 | return std::make_unique<node::MinerImpl>(context); |
1036 | 0 | } |
1037 | | } // namespace interfaces |