Branch data Line data Source code
1 : : // Copyright (c) 2010 Satoshi Nakamoto
2 : : // Copyright (c) 2009-2022 The Bitcoin Core developers
3 : : // Distributed under the MIT software license, see the accompanying
4 : : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 : :
6 : : #include <config/bitcoin-config.h> // IWYU pragma: keep
7 : :
8 : : #include <chain.h>
9 : : #include <chainparams.h>
10 : : #include <common/system.h>
11 : : #include <consensus/amount.h>
12 : : #include <consensus/consensus.h>
13 : : #include <consensus/merkle.h>
14 : : #include <consensus/params.h>
15 : : #include <consensus/validation.h>
16 : : #include <core_io.h>
17 : : #include <deploymentinfo.h>
18 : : #include <deploymentstatus.h>
19 : : #include <interfaces/mining.h>
20 : : #include <key_io.h>
21 : : #include <net.h>
22 : : #include <node/context.h>
23 : : #include <node/miner.h>
24 : : #include <node/warnings.h>
25 : : #include <pow.h>
26 : : #include <rpc/blockchain.h>
27 : : #include <rpc/mining.h>
28 : : #include <rpc/server.h>
29 : : #include <rpc/server_util.h>
30 : : #include <rpc/util.h>
31 : : #include <script/descriptor.h>
32 : : #include <script/script.h>
33 : : #include <script/signingprovider.h>
34 : : #include <txmempool.h>
35 : : #include <univalue.h>
36 : : #include <util/signalinterrupt.h>
37 : : #include <util/strencodings.h>
38 : : #include <util/string.h>
39 : : #include <util/time.h>
40 : : #include <util/translation.h>
41 : : #include <validation.h>
42 : : #include <validationinterface.h>
43 : :
44 : : #include <memory>
45 : : #include <stdint.h>
46 : :
47 : : using node::BlockAssembler;
48 : : using node::CBlockTemplate;
49 : : using interfaces::Mining;
50 : : using node::NodeContext;
51 : : using node::RegenerateCommitments;
52 : : using node::UpdateTime;
53 : : using util::ToString;
54 : :
55 : : /**
56 : : * Return average network hashes per second based on the last 'lookup' blocks,
57 : : * or from the last difficulty change if 'lookup' is -1.
58 : : * If 'height' is -1, compute the estimate from current chain tip.
59 : : * If 'height' is a valid block height, compute the estimate at the time when a given block was found.
60 : : */
61 : 25 : static UniValue GetNetworkHashPS(int lookup, int height, const CChain& active_chain) {
62 [ + + ]: 25 : if (lookup < -1 || lookup == 0) {
63 [ + - + - ]: 8 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid nblocks. Must be a positive number or -1.");
64 : : }
65 : :
66 [ + + + + ]: 21 : if (height < -1 || height > active_chain.Height()) {
67 [ + - + - ]: 8 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Block does not exist at specified height");
68 : : }
69 : :
70 [ + - ]: 17 : const CBlockIndex* pb = active_chain.Tip();
71 : :
72 [ + + ]: 17 : if (height >= 0) {
73 : 2 : pb = active_chain[height];
74 : : }
75 : :
76 [ + - + + ]: 17 : if (pb == nullptr || !pb->nHeight)
77 : 3 : return 0;
78 : :
79 : : // If lookup is -1, then use blocks since last difficulty change.
80 [ + + ]: 14 : if (lookup == -1)
81 : 2 : lookup = pb->nHeight % Params().GetConsensus().DifficultyAdjustmentInterval() + 1;
82 : :
83 : : // If lookup is larger than chain, then set it to chain length.
84 [ + + ]: 14 : if (lookup > pb->nHeight)
85 : 6 : lookup = pb->nHeight;
86 : :
87 : 14 : const CBlockIndex* pb0 = pb;
88 : 14 : int64_t minTime = pb0->GetBlockTime();
89 : 14 : int64_t maxTime = minTime;
90 [ + + ]: 2174 : for (int i = 0; i < lookup; i++) {
91 : 2160 : pb0 = pb0->pprev;
92 [ + + ]: 2160 : int64_t time = pb0->GetBlockTime();
93 [ + + ]: 2160 : minTime = std::min(time, minTime);
94 [ + + ]: 4319 : maxTime = std::max(time, maxTime);
95 : : }
96 : :
97 : : // In case there's a situation where minTime == maxTime, we don't want a divide by zero exception.
98 [ - + ]: 14 : if (minTime == maxTime)
99 : 0 : return 0;
100 : :
101 : 14 : arith_uint256 workDiff = pb->nChainWork - pb0->nChainWork;
102 : 14 : int64_t timeDiff = maxTime - minTime;
103 : :
104 : 14 : return workDiff.getdouble() / timeDiff;
105 : : }
106 : :
107 : 2442 : static RPCHelpMan getnetworkhashps()
108 : : {
109 : 2442 : return RPCHelpMan{"getnetworkhashps",
110 : : "\nReturns the estimated network hashes per second based on the last n blocks.\n"
111 : : "Pass in [blocks] to override # of blocks, -1 specifies since last difficulty change.\n"
112 : : "Pass in [height] to estimate the network speed at the time when a certain block was found.\n",
113 : : {
114 [ + - ]: 4884 : {"nblocks", RPCArg::Type::NUM, RPCArg::Default{120}, "The number of previous blocks to calculate estimate from, or -1 for blocks since last difficulty change."},
115 [ + - ]: 4884 : {"height", RPCArg::Type::NUM, RPCArg::Default{-1}, "To estimate at the time of the given height."},
116 : : },
117 [ + - ]: 4884 : RPCResult{
118 [ + - + - : 2442 : RPCResult::Type::NUM, "", "Hashes per second estimated"},
+ - ]
119 [ + - ]: 4884 : RPCExamples{
120 [ + - + - : 4884 : HelpExampleCli("getnetworkhashps", "")
+ - ]
121 [ + - + - : 9768 : + HelpExampleRpc("getnetworkhashps", "")
+ - + - ]
122 : : },
123 : 25 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
124 : : {
125 : 25 : ChainstateManager& chainman = EnsureAnyChainman(request.context);
126 : 25 : LOCK(cs_main);
127 [ + - + - : 25 : return GetNetworkHashPS(self.Arg<int>("nblocks"), self.Arg<int>("height"), chainman.ActiveChain());
+ - + + ]
128 : 17 : },
129 [ + - + - : 26862 : };
+ - + - +
- + - + +
- - ]
130 [ + - + - : 17094 : }
+ - + - +
- - - ]
131 : :
132 : 42794 : static bool GenerateBlock(ChainstateManager& chainman, Mining& miner, CBlock& block, uint64_t& max_tries, std::shared_ptr<const CBlock>& block_out, bool process_new_block)
133 : : {
134 : 42794 : block_out.reset();
135 : 42794 : block.hashMerkleRoot = BlockMerkleRoot(block);
136 : :
137 [ + - + - : 475473 : while (max_tries > 0 && block.nNonce < std::numeric_limits<uint32_t>::max() && !CheckProofOfWork(block.GetHash(), block.nBits, chainman.GetConsensus()) && !chainman.m_interrupt) {
+ + + - ]
138 : 389885 : ++block.nNonce;
139 : 389885 : --max_tries;
140 : : }
141 [ + - + - ]: 42794 : if (max_tries == 0 || chainman.m_interrupt) {
142 : 0 : return false;
143 : : }
144 [ + - ]: 42794 : if (block.nNonce == std::numeric_limits<uint32_t>::max()) {
145 : : return true;
146 : : }
147 : :
148 [ - + ]: 42794 : block_out = std::make_shared<const CBlock>(block);
149 : :
150 [ + + ]: 42794 : if (!process_new_block) return true;
151 : :
152 [ - + ]: 42793 : if (!miner.processNewBlock(block_out, nullptr)) {
153 [ # # # # ]: 0 : throw JSONRPCError(RPC_INTERNAL_ERROR, "ProcessNewBlock, block not accepted");
154 : : }
155 : :
156 : : return true;
157 : : }
158 : :
159 : 2934 : static UniValue generateBlocks(ChainstateManager& chainman, Mining& miner, const CScript& coinbase_script, int nGenerate, uint64_t nMaxTries)
160 : : {
161 : 2934 : UniValue blockHashes(UniValue::VARR);
162 [ + + + - : 45700 : while (nGenerate > 0 && !chainman.m_interrupt) {
+ - ]
163 [ + - ]: 85532 : std::unique_ptr<CBlockTemplate> pblocktemplate(miner.createNewBlock(coinbase_script));
164 [ - + ]: 42766 : if (!pblocktemplate.get())
165 [ # # # # ]: 0 : throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block");
166 : :
167 : 42766 : std::shared_ptr<const CBlock> block_out;
168 [ + - + - ]: 42766 : if (!GenerateBlock(chainman, miner, pblocktemplate->block, nMaxTries, block_out, /*process_new_block=*/true)) {
169 : : break;
170 : : }
171 : :
172 [ + - ]: 42766 : if (block_out) {
173 : 42766 : --nGenerate;
174 [ + - + - : 42766 : blockHashes.push_back(block_out->GetHash().GetHex());
+ - + - ]
175 : : }
176 : 42766 : }
177 : 2934 : return blockHashes;
178 : 0 : }
179 : :
180 : 726 : static bool getScriptFromDescriptor(const std::string& descriptor, CScript& script, std::string& error)
181 : : {
182 : 726 : FlatSigningProvider key_provider;
183 [ + - ]: 726 : const auto desc = Parse(descriptor, key_provider, error, /* require_checksum = */ false);
184 [ + + ]: 726 : if (desc) {
185 [ + - + + ]: 716 : if (desc->IsRange()) {
186 [ + - + - ]: 2 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Ranged descriptor not accepted. Maybe pass through deriveaddresses first?");
187 : : }
188 : :
189 : 715 : FlatSigningProvider provider;
190 : 715 : std::vector<CScript> scripts;
191 [ + - + + ]: 715 : if (!desc->Expand(0, key_provider, scripts, provider)) {
192 [ + - + - ]: 2 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Cannot derive script without private keys");
193 : : }
194 : :
195 : : // Combo descriptors can have 2 or 4 scripts, so we can't just check scripts.size() == 1
196 [ + - - + : 714 : CHECK_NONFATAL(scripts.size() > 0 && scripts.size() <= 4);
+ - ]
197 : :
198 [ + + ]: 714 : if (scripts.size() == 1) {
199 [ + - ]: 712 : script = scripts.at(0);
200 [ + + ]: 2 : } else if (scripts.size() == 4) {
201 : : // For uncompressed keys, take the 3rd script, since it is p2wpkh
202 [ + - ]: 1 : script = scripts.at(2);
203 : : } else {
204 : : // Else take the 2nd script, since it is p2pkh
205 [ + - ]: 1 : script = scripts.at(1);
206 : : }
207 : :
208 : 714 : return true;
209 : 716 : } else {
210 : : return false;
211 : : }
212 : 728 : }
213 : :
214 : 3099 : static RPCHelpMan generatetodescriptor()
215 : : {
216 : 3099 : return RPCHelpMan{
217 : : "generatetodescriptor",
218 : : "Mine to a specified descriptor and return the block hashes.",
219 : : {
220 [ + - ]: 3099 : {"num_blocks", RPCArg::Type::NUM, RPCArg::Optional::NO, "How many blocks are generated."},
221 [ + - ]: 3099 : {"descriptor", RPCArg::Type::STR, RPCArg::Optional::NO, "The descriptor to send the newly generated bitcoin to."},
222 [ + - ]: 6198 : {"maxtries", RPCArg::Type::NUM, RPCArg::Default{DEFAULT_MAX_TRIES}, "How many iterations to try."},
223 : : },
224 [ + - + - : 12396 : RPCResult{
+ + - - ]
225 : : RPCResult::Type::ARR, "", "hashes of blocks generated",
226 : : {
227 : : {RPCResult::Type::STR_HEX, "", "blockhash"},
228 : : }
229 [ + - + - : 6198 : },
+ - + - +
- ]
230 [ + - ]: 6198 : RPCExamples{
231 [ + - + - : 6198 : "\nGenerate 11 blocks to mydesc\n" + HelpExampleCli("generatetodescriptor", "11 \"mydesc\"")},
+ - + - ]
232 : 692 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
233 : : {
234 : 692 : const auto num_blocks{self.Arg<int>("num_blocks")};
235 : 692 : const auto max_tries{self.Arg<uint64_t>("maxtries")};
236 : :
237 [ + - ]: 692 : CScript coinbase_script;
238 [ + - ]: 692 : std::string error;
239 [ + - + - : 692 : if (!getScriptFromDescriptor(self.Arg<std::string>("descriptor"), coinbase_script, error)) {
- + ]
240 [ # # ]: 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error);
241 : : }
242 : :
243 [ + - ]: 692 : NodeContext& node = EnsureAnyNodeContext(request.context);
244 [ + - ]: 692 : Mining& miner = EnsureMining(node);
245 [ + - ]: 692 : ChainstateManager& chainman = EnsureChainman(node);
246 : :
247 [ + - ]: 692 : return generateBlocks(chainman, miner, coinbase_script, num_blocks, max_tries);
248 : 692 : },
249 [ + - + - : 40287 : };
+ - + - +
- + - + -
+ + - - ]
250 [ + - + - : 30990 : }
+ - + - +
- + - + -
+ - + - -
- ]
251 : :
252 : 2409 : static RPCHelpMan generate()
253 : : {
254 [ + - + - ]: 9636 : return RPCHelpMan{"generate", "has been replaced by the -generate cli option. Refer to -help for more information.", {}, {}, RPCExamples{""}, [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue {
255 [ + - + - ]: 2 : throw JSONRPCError(RPC_METHOD_NOT_FOUND, self.ToString());
256 [ + - + - : 12045 : }};
+ - + - ]
257 : : }
258 : :
259 : 4653 : static RPCHelpMan generatetoaddress()
260 : : {
261 : 4653 : return RPCHelpMan{"generatetoaddress",
262 : : "Mine to a specified address and return the block hashes.",
263 : : {
264 [ + - ]: 4653 : {"nblocks", RPCArg::Type::NUM, RPCArg::Optional::NO, "How many blocks are generated."},
265 [ + - ]: 4653 : {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The address to send the newly generated bitcoin to."},
266 [ + - ]: 9306 : {"maxtries", RPCArg::Type::NUM, RPCArg::Default{DEFAULT_MAX_TRIES}, "How many iterations to try."},
267 : : },
268 [ + - + - : 18612 : RPCResult{
+ + - - ]
269 : : RPCResult::Type::ARR, "", "hashes of blocks generated",
270 : : {
271 : : {RPCResult::Type::STR_HEX, "", "blockhash"},
272 [ + - + - : 9306 : }},
+ - + - +
- ]
273 [ + - ]: 9306 : RPCExamples{
274 : : "\nGenerate 11 blocks to myaddress\n"
275 [ + - + - : 9306 : + HelpExampleCli("generatetoaddress", "11 \"myaddress\"")
+ - + - ]
276 : 4653 : + "If you are using the " PACKAGE_NAME " wallet, you can get a new address to send the newly generated bitcoin to with:\n"
277 [ + - + - : 18612 : + HelpExampleCli("getnewaddress", "")
+ - + - ]
278 : : },
279 : 2244 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
280 : : {
281 : 2244 : const int num_blocks{request.params[0].getInt<int>()};
282 [ + + ]: 2244 : const uint64_t max_tries{request.params[2].isNull() ? DEFAULT_MAX_TRIES : request.params[2].getInt<int>()};
283 : :
284 : 2244 : CTxDestination destination = DecodeDestination(request.params[1].get_str());
285 [ + - + + ]: 2244 : if (!IsValidDestination(destination)) {
286 [ + - + - ]: 4 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Error: Invalid address");
287 : : }
288 : :
289 [ + - ]: 2242 : NodeContext& node = EnsureAnyNodeContext(request.context);
290 [ + - ]: 2242 : Mining& miner = EnsureMining(node);
291 [ + - ]: 2242 : ChainstateManager& chainman = EnsureChainman(node);
292 : :
293 [ + - ]: 2242 : CScript coinbase_script = GetScriptForDestination(destination);
294 : :
295 [ + - ]: 2242 : return generateBlocks(chainman, miner, coinbase_script, num_blocks, max_tries);
296 : 2242 : },
297 [ + - + - : 60489 : };
+ - + - +
- + - + -
+ + - - ]
298 [ + - + - : 46530 : }
+ - + - +
- + - + -
+ - + - -
- ]
299 : :
300 : 2441 : static RPCHelpMan generateblock()
301 : : {
302 : 2441 : return RPCHelpMan{"generateblock",
303 : : "Mine a set of ordered transactions to a specified address or descriptor and return the block hash.",
304 : : {
305 [ + - ]: 2441 : {"output", RPCArg::Type::STR, RPCArg::Optional::NO, "The address or descriptor to send the newly generated bitcoin to."},
306 [ + - ]: 2441 : {"transactions", RPCArg::Type::ARR, RPCArg::Optional::NO, "An array of hex strings which are either txids or raw transactions.\n"
307 : : "Txids must reference transactions currently in the mempool.\n"
308 : : "All transactions must be valid and in valid order, otherwise the block will be rejected.",
309 : : {
310 [ + - ]: 2441 : {"rawtx/txid", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, ""},
311 : : },
312 : : },
313 [ + - ]: 4882 : {"submit", RPCArg::Type::BOOL, RPCArg::Default{true}, "Whether to submit the block before the RPC call returns or to return it as hex."},
314 : : },
315 [ + - + - : 12205 : RPCResult{
+ + - - ]
316 : : RPCResult::Type::OBJ, "", "",
317 : : {
318 : : {RPCResult::Type::STR_HEX, "hash", "hash of generated block"},
319 : : {RPCResult::Type::STR_HEX, "hex", /*optional=*/true, "hex of generated block, only present when submit=false"},
320 : : }
321 [ + - + - : 7323 : },
+ - + - +
- + - + -
+ - ]
322 [ + - ]: 4882 : RPCExamples{
323 : : "\nGenerate a block to myaddress, with txs rawtx and mempool_txid\n"
324 [ + - + - : 4882 : + HelpExampleCli("generateblock", R"("myaddress" '["rawtx", "mempool_txid"]')")
+ - + - ]
325 : : },
326 : 34 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
327 : : {
328 : 34 : const auto address_or_descriptor = request.params[0].get_str();
329 [ + + ]: 34 : CScript coinbase_script;
330 [ + + ]: 34 : std::string error;
331 : :
332 [ + + + + ]: 34 : if (!getScriptFromDescriptor(address_or_descriptor, coinbase_script, error)) {
333 [ + - ]: 10 : const auto destination = DecodeDestination(address_or_descriptor);
334 [ + - + + ]: 10 : if (!IsValidDestination(destination)) {
335 [ + - + - ]: 2 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Error: Invalid address or descriptor");
336 : : }
337 : :
338 [ + - ]: 18 : coinbase_script = GetScriptForDestination(destination);
339 : 10 : }
340 : :
341 [ + - ]: 31 : NodeContext& node = EnsureAnyNodeContext(request.context);
342 [ + - ]: 31 : Mining& miner = EnsureMining(node);
343 [ + - ]: 31 : const CTxMemPool& mempool = EnsureMemPool(node);
344 : :
345 : 31 : std::vector<CTransactionRef> txs;
346 [ + - + - : 31 : const auto raw_txs_or_txids = request.params[1].get_array();
+ - ]
347 [ + + ]: 40 : for (size_t i = 0; i < raw_txs_or_txids.size(); i++) {
348 [ + - + - : 11 : const auto str(raw_txs_or_txids[i].get_str());
+ - ]
349 : :
350 : 11 : uint256 hash;
351 [ + - ]: 11 : CMutableTransaction mtx;
352 [ + - + + ]: 11 : if (ParseHashStr(str, hash)) {
353 : :
354 [ + - ]: 4 : const auto tx = mempool.get(hash);
355 [ + + ]: 4 : if (!tx) {
356 [ + - + - ]: 2 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Transaction %s not in mempool.", str));
357 : : }
358 : :
359 [ + - ]: 3 : txs.emplace_back(tx);
360 : :
361 [ + - + + ]: 11 : } else if (DecodeHexTx(mtx, str)) {
362 [ + - + - : 12 : txs.push_back(MakeTransactionRef(std::move(mtx)));
- + ]
363 : :
364 : : } else {
365 [ + - + - ]: 2 : throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("Transaction decode failed for %s. Make sure the tx has at least one input.", str));
366 : : }
367 : 13 : }
368 : :
369 [ + - + + : 29 : const bool process_new_block{request.params[2].isNull() ? true : request.params[2].get_bool()};
+ - + - ]
370 : 29 : CBlock block;
371 : :
372 [ + - ]: 29 : ChainstateManager& chainman = EnsureChainman(node);
373 : 29 : {
374 [ + - ]: 29 : std::unique_ptr<CBlockTemplate> blocktemplate{miner.createNewBlock(coinbase_script, /*use_mempool=*/false)};
375 [ - + ]: 29 : if (!blocktemplate) {
376 [ # # # # ]: 0 : throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block");
377 : : }
378 [ + - ]: 29 : block = blocktemplate->block;
379 : 29 : }
380 : :
381 [ + - ]: 29 : CHECK_NONFATAL(block.vtx.size() == 1);
382 : :
383 : : // Add transactions
384 [ + - ]: 29 : block.vtx.insert(block.vtx.end(), txs.begin(), txs.end());
385 [ + - ]: 29 : RegenerateCommitments(block, chainman);
386 : :
387 : 29 : {
388 [ + - ]: 29 : BlockValidationState state;
389 [ + - + + ]: 29 : if (!miner.testBlockValidity(block, /*check_merkle_root=*/false, state)) {
390 [ + - + - : 2 : throw JSONRPCError(RPC_VERIFY_ERROR, strprintf("testBlockValidity failed: %s", state.ToString()));
+ - ]
391 : : }
392 : 1 : }
393 : :
394 : 28 : std::shared_ptr<const CBlock> block_out;
395 : 28 : uint64_t max_tries{DEFAULT_MAX_TRIES};
396 : :
397 [ + - + - : 28 : if (!GenerateBlock(chainman, miner, block, max_tries, block_out, process_new_block) || !block_out) {
- + ]
398 [ # # # # ]: 0 : throw JSONRPCError(RPC_MISC_ERROR, "Failed to make block.");
399 : : }
400 : :
401 : 28 : UniValue obj(UniValue::VOBJ);
402 [ + - + - : 56 : obj.pushKV("hash", block_out->GetHash().GetHex());
+ - + - +
- ]
403 [ + + ]: 28 : if (!process_new_block) {
404 [ + - ]: 1 : DataStream block_ser;
405 [ + - ]: 1 : block_ser << TX_WITH_WITNESS(*block_out);
406 [ + - + - : 2 : obj.pushKV("hex", HexStr(block_ser));
+ - + - ]
407 : 1 : }
408 [ + - ]: 28 : return obj;
409 : 46 : },
410 [ + - + - : 36615 : };
+ - + - +
- + - + -
+ - + + -
- ]
411 [ + - + - : 34174 : }
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + + -
- - - -
- ]
412 : :
413 : 2422 : static RPCHelpMan getmininginfo()
414 : : {
415 : 2422 : return RPCHelpMan{"getmininginfo",
416 : : "\nReturns a json object containing mining-related information.",
417 : : {},
418 [ + - + - : 26642 : RPCResult{
+ + - - ]
419 : : RPCResult::Type::OBJ, "", "",
420 : : {
421 : : {RPCResult::Type::NUM, "blocks", "The current block"},
422 : : {RPCResult::Type::NUM, "currentblockweight", /*optional=*/true, "The block weight of the last assembled block (only present if a block was ever assembled)"},
423 : : {RPCResult::Type::NUM, "currentblocktx", /*optional=*/true, "The number of block transactions of the last assembled block (only present if a block was ever assembled)"},
424 : : {RPCResult::Type::NUM, "difficulty", "The current difficulty"},
425 : : {RPCResult::Type::NUM, "networkhashps", "The network hashes per second"},
426 : : {RPCResult::Type::NUM, "pooledtx", "The size of the mempool"},
427 : : {RPCResult::Type::STR, "chain", "current network name (main, test, signet, regtest)"},
428 [ + - + - : 2422 : (IsDeprecatedRPCEnabled("warnings") ?
- + ]
429 [ # # # # : 0 : RPCResult{RPCResult::Type::STR, "warnings", "any network and blockchain warnings (DEPRECATED)"} :
# # ]
430 : : RPCResult{RPCResult::Type::ARR, "warnings", "any network and blockchain warnings (run with `-deprecatedrpc=warnings` to return the latest warning as a single string)",
431 : : {
432 : : {RPCResult::Type::STR, "", "warning"},
433 : : }
434 [ + - + - : 4844 : }
+ - + - +
- ]
435 : : ),
436 [ + - + - : 21798 : }},
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - ]
437 [ + - ]: 4844 : RPCExamples{
438 [ + - + - : 4844 : HelpExampleCli("getmininginfo", "")
+ - ]
439 [ + - + - : 9688 : + HelpExampleRpc("getmininginfo", "")
+ - + - ]
440 : : },
441 : 7 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
442 : : {
443 : 7 : NodeContext& node = EnsureAnyNodeContext(request.context);
444 : 7 : const CTxMemPool& mempool = EnsureMemPool(node);
445 : 7 : ChainstateManager& chainman = EnsureChainman(node);
446 : 7 : LOCK(cs_main);
447 [ + - ]: 7 : const CChain& active_chain = chainman.ActiveChain();
448 : :
449 : 7 : UniValue obj(UniValue::VOBJ);
450 [ + - + - : 14 : obj.pushKV("blocks", active_chain.Height());
+ - ]
451 [ + + + - : 10 : if (BlockAssembler::m_last_block_weight) obj.pushKV("currentblockweight", *BlockAssembler::m_last_block_weight);
+ - + - ]
452 [ + + + - : 10 : if (BlockAssembler::m_last_block_num_txs) obj.pushKV("currentblocktx", *BlockAssembler::m_last_block_num_txs);
+ - + - ]
453 [ + - + - : 21 : obj.pushKV("difficulty", GetDifficulty(*CHECK_NONFATAL(active_chain.Tip())));
+ - + - +
- + - ]
454 [ + - + - : 14 : obj.pushKV("networkhashps", getnetworkhashps().HandleRequest(request));
+ - + - ]
455 [ + - + - : 14 : obj.pushKV("pooledtx", (uint64_t)mempool.size());
+ - + - ]
456 [ + - + - : 14 : obj.pushKV("chain", chainman.GetParams().GetChainTypeString());
+ - + - ]
457 [ + - + - : 14 : obj.pushKV("warnings", node::GetWarningsForRpc(*CHECK_NONFATAL(node.warnings), IsDeprecatedRPCEnabled("warnings")));
+ - + - +
- + - ]
458 [ + - ]: 7 : return obj;
459 : 7 : },
460 [ + - + - : 14532 : };
+ - + - ]
461 [ + - + - : 41174 : }
+ - + - +
- + - + -
+ - - - +
- + - + -
+ - + - +
+ + - + -
+ - + - +
- - + - +
- + - - -
- - - - -
- - - - -
- - - - -
- - - - -
- ]
462 : :
463 : :
464 : : // NOTE: Unlike wallet RPC (which use BTC values), mining RPCs follow GBT (BIP 22) in using satoshi amounts
465 : 3133 : static RPCHelpMan prioritisetransaction()
466 : : {
467 : 3133 : return RPCHelpMan{"prioritisetransaction",
468 : : "Accepts the transaction into mined blocks at a higher (or lower) priority\n",
469 : : {
470 [ + - ]: 3133 : {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id."},
471 [ + - ]: 3133 : {"dummy", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "API-Compatibility for previous API. Must be zero or null.\n"
472 : : " DEPRECATED. For forward compatibility use named arguments and omit this parameter."},
473 [ + - ]: 3133 : {"fee_delta", RPCArg::Type::NUM, RPCArg::Optional::NO, "The fee value (in satoshis) to add (or subtract, if negative).\n"
474 : : " Note, that this value is not a fee rate. It is a value to modify absolute fee of the TX.\n"
475 : : " The fee is not actually paid, only the algorithm for selecting transactions into a block\n"
476 : : " considers the transaction as it would have paid a higher (or lower) fee."},
477 : : },
478 [ + - ]: 6266 : RPCResult{
479 [ + - + - : 3133 : RPCResult::Type::BOOL, "", "Returns true"},
+ - ]
480 [ + - ]: 6266 : RPCExamples{
481 [ + - + - : 6266 : HelpExampleCli("prioritisetransaction", "\"txid\" 0.0 10000")
+ - ]
482 [ + - + - : 12532 : + HelpExampleRpc("prioritisetransaction", "\"txid\", 0.0, 10000")
+ - + - ]
483 : : },
484 : 712 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
485 : : {
486 : 712 : LOCK(cs_main);
487 : :
488 [ + - + + ]: 712 : uint256 hash(ParseHashV(request.params[0], "txid"));
489 [ + - ]: 710 : const auto dummy{self.MaybeArg<double>("dummy")};
490 [ + - + - ]: 710 : CAmount nAmount = request.params[2].getInt<int64_t>();
491 : :
492 [ + + + + ]: 710 : if (dummy && *dummy != 0) {
493 [ + - + - ]: 2 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Priority is no longer supported, dummy argument to prioritisetransaction must be 0.");
494 : : }
495 : :
496 [ + - + - ]: 709 : EnsureAnyMemPool(request.context).PrioritiseTransaction(hash, nAmount);
497 [ + - ]: 709 : return true;
498 : 709 : },
499 [ + - + - : 40729 : };
+ - + - +
- + - + -
+ + - - ]
500 [ + - + - : 18798 : }
+ - + - +
- + - + -
- - ]
501 : :
502 : 2442 : static RPCHelpMan getprioritisedtransactions()
503 : : {
504 : 2442 : return RPCHelpMan{"getprioritisedtransactions",
505 : : "Returns a map of all user-created (see prioritisetransaction) fee deltas by txid, and whether the tx is present in mempool.",
506 : : {},
507 [ + - + - : 9768 : RPCResult{
+ + - - ]
508 : : RPCResult::Type::OBJ_DYN, "", "prioritisation keyed by txid",
509 : : {
510 : : {RPCResult::Type::OBJ, "<transactionid>", "", {
511 : : {RPCResult::Type::NUM, "fee_delta", "transaction fee delta in satoshis"},
512 : : {RPCResult::Type::BOOL, "in_mempool", "whether this transaction is currently in mempool"},
513 : : {RPCResult::Type::NUM, "modified_fee", /*optional=*/true, "modified fee in satoshis. Only returned if in_mempool=true"},
514 : : }}
515 : : },
516 [ + - + - : 12210 : },
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - ]
517 [ + - ]: 4884 : RPCExamples{
518 [ + - + - : 4884 : HelpExampleCli("getprioritisedtransactions", "")
+ - ]
519 [ + - + - : 9768 : + HelpExampleRpc("getprioritisedtransactions", "")
+ - + - ]
520 : : },
521 : 26 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
522 : : {
523 : 26 : NodeContext& node = EnsureAnyNodeContext(request.context);
524 : 26 : CTxMemPool& mempool = EnsureMemPool(node);
525 : 26 : UniValue rpc_result{UniValue::VOBJ};
526 [ + - + + ]: 54 : for (const auto& delta_info : mempool.GetPrioritisedTransactions()) {
527 : 28 : UniValue result_inner{UniValue::VOBJ};
528 [ + - + - : 56 : result_inner.pushKV("fee_delta", delta_info.delta);
+ - ]
529 [ + - + - : 56 : result_inner.pushKV("in_mempool", delta_info.in_mempool);
+ - ]
530 [ + + ]: 28 : if (delta_info.in_mempool) {
531 [ + - + - : 36 : result_inner.pushKV("modified_fee", *delta_info.modified_fee);
+ - ]
532 : : }
533 [ + - + - ]: 56 : rpc_result.pushKV(delta_info.txid.GetHex(), std::move(result_inner));
534 : 28 : }
535 : 26 : return rpc_result;
536 : 0 : },
537 [ + - + - : 14652 : };
+ - + - ]
538 [ + - + - : 19536 : }
+ - + - +
- + - + +
- - - - ]
539 : :
540 : :
541 : : // NOTE: Assumes a conclusive result; if result is inconclusive, it must be handled by caller
542 : 6276 : static UniValue BIP22ValidationResult(const BlockValidationState& state)
543 : : {
544 [ + + ]: 6276 : if (state.IsValid())
545 : 3763 : return UniValue::VNULL;
546 : :
547 [ - + ]: 2513 : if (state.IsError())
548 [ # # # # ]: 0 : throw JSONRPCError(RPC_VERIFY_ERROR, state.ToString());
549 [ + - ]: 2513 : if (state.IsInvalid())
550 : : {
551 : 2513 : std::string strRejectReason = state.GetRejectReason();
552 [ - + ]: 2513 : if (strRejectReason.empty())
553 [ # # ]: 0 : return "rejected";
554 [ + - ]: 2513 : return strRejectReason;
555 : 2513 : }
556 : : // Should be impossible
557 : 0 : return "valid?";
558 : : }
559 : :
560 : 4126 : static std::string gbt_vb_name(const Consensus::DeploymentPos pos) {
561 : 4126 : const struct VBDeploymentInfo& vbinfo = VersionBitsDeploymentInfo[pos];
562 : 4126 : std::string s = vbinfo.name;
563 [ - + ]: 4126 : if (!vbinfo.gbt_force) {
564 [ # # ]: 0 : s.insert(s.begin(), '!');
565 : : }
566 : 4126 : return s;
567 : 0 : }
568 : :
569 : 4496 : static RPCHelpMan getblocktemplate()
570 : : {
571 : 4496 : return RPCHelpMan{"getblocktemplate",
572 : : "\nIf the request parameters include a 'mode' key, that is used to explicitly select between the default 'template' request or a 'proposal'.\n"
573 : : "It returns data needed to construct a block to work on.\n"
574 : : "For full specification, see BIPs 22, 23, 9, and 145:\n"
575 : : " https://github.com/bitcoin/bips/blob/master/bip-0022.mediawiki\n"
576 : : " https://github.com/bitcoin/bips/blob/master/bip-0023.mediawiki\n"
577 : : " https://github.com/bitcoin/bips/blob/master/bip-0009.mediawiki#getblocktemplate_changes\n"
578 : : " https://github.com/bitcoin/bips/blob/master/bip-0145.mediawiki\n",
579 : : {
580 [ + - ]: 4496 : {"template_request", RPCArg::Type::OBJ, RPCArg::Optional::NO, "Format of the template",
581 : : {
582 [ + - ]: 4496 : {"mode", RPCArg::Type::STR, /* treat as named arg */ RPCArg::Optional::OMITTED, "This must be set to \"template\", \"proposal\" (see BIP 23), or omitted"},
583 [ + - ]: 4496 : {"capabilities", RPCArg::Type::ARR, /* treat as named arg */ RPCArg::Optional::OMITTED, "A list of strings",
584 : : {
585 [ + - ]: 4496 : {"str", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "client side supported feature, 'longpoll', 'coinbasevalue', 'proposal', 'serverlist', 'workid'"},
586 : : }},
587 [ + - ]: 4496 : {"rules", RPCArg::Type::ARR, RPCArg::Optional::NO, "A list of strings",
588 : : {
589 [ + - ]: 4496 : {"segwit", RPCArg::Type::STR, RPCArg::Optional::NO, "(literal) indicates client side segwit support"},
590 [ + - ]: 4496 : {"str", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "other client side supported softfork deployment"},
591 : : }},
592 [ + - ]: 4496 : {"longpollid", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "delay processing request until the result would vary significantly from the \"longpollid\" of a prior template"},
593 [ + - ]: 4496 : {"data", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "proposed block data to check, encoded in hexadecimal; valid only for mode=\"proposal\""},
594 : : },
595 : : },
596 : : },
597 : : {
598 [ + - + - : 4496 : RPCResult{"If the proposal was accepted with mode=='proposal'", RPCResult::Type::NONE, "", ""},
+ - + - ]
599 [ + - + - : 4496 : RPCResult{"If the proposal was not accepted with mode=='proposal'", RPCResult::Type::STR, "", "According to BIP22"},
+ - + - ]
600 : : RPCResult{"Otherwise", RPCResult::Type::OBJ, "", "",
601 : : {
602 : : {RPCResult::Type::NUM, "version", "The preferred block version"},
603 : : {RPCResult::Type::ARR, "rules", "specific block rules that are to be enforced",
604 : : {
605 : : {RPCResult::Type::STR, "", "name of a rule the client must understand to some extent; see BIP 9 for format"},
606 : : }},
607 : : {RPCResult::Type::OBJ_DYN, "vbavailable", "set of pending, supported versionbit (BIP 9) softfork deployments",
608 : : {
609 : : {RPCResult::Type::NUM, "rulename", "identifies the bit number as indicating acceptance and readiness for the named softfork rule"},
610 : : }},
611 : : {RPCResult::Type::ARR, "capabilities", "",
612 : : {
613 : : {RPCResult::Type::STR, "value", "A supported feature, for example 'proposal'"},
614 : : }},
615 : : {RPCResult::Type::NUM, "vbrequired", "bit mask of versionbits the server requires set in submissions"},
616 : : {RPCResult::Type::STR, "previousblockhash", "The hash of current highest block"},
617 : : {RPCResult::Type::ARR, "transactions", "contents of non-coinbase transactions that should be included in the next block",
618 : : {
619 : : {RPCResult::Type::OBJ, "", "",
620 : : {
621 : : {RPCResult::Type::STR_HEX, "data", "transaction data encoded in hexadecimal (byte-for-byte)"},
622 : : {RPCResult::Type::STR_HEX, "txid", "transaction id encoded in little-endian hexadecimal"},
623 : : {RPCResult::Type::STR_HEX, "hash", "hash encoded in little-endian hexadecimal (including witness data)"},
624 : : {RPCResult::Type::ARR, "depends", "array of numbers",
625 : : {
626 : : {RPCResult::Type::NUM, "", "transactions before this one (by 1-based index in 'transactions' list) that must be present in the final block if this one is"},
627 : : }},
628 : : {RPCResult::Type::NUM, "fee", "difference in value between transaction inputs and outputs (in satoshis); for coinbase transactions, this is a negative Number of the total collected block fees (ie, not including the block subsidy); if key is not present, fee is unknown and clients MUST NOT assume there isn't one"},
629 : : {RPCResult::Type::NUM, "sigops", "total SigOps cost, as counted for purposes of block limits; if key is not present, sigop cost is unknown and clients MUST NOT assume it is zero"},
630 : : {RPCResult::Type::NUM, "weight", "total transaction weight, as counted for purposes of block limits"},
631 : : }},
632 : : }},
633 : : {RPCResult::Type::OBJ_DYN, "coinbaseaux", "data that should be included in the coinbase's scriptSig content",
634 : : {
635 : : {RPCResult::Type::STR_HEX, "key", "values must be in the coinbase (keys may be ignored)"},
636 : : }},
637 : : {RPCResult::Type::NUM, "coinbasevalue", "maximum allowable input to coinbase transaction, including the generation award and transaction fees (in satoshis)"},
638 : : {RPCResult::Type::STR, "longpollid", "an id to include with a request to longpoll on an update to this template"},
639 : : {RPCResult::Type::STR, "target", "The hash target"},
640 [ + - ]: 8992 : {RPCResult::Type::NUM_TIME, "mintime", "The minimum timestamp appropriate for the next block time, expressed in " + UNIX_EPOCH_TIME},
641 : : {RPCResult::Type::ARR, "mutable", "list of ways the block template may be changed",
642 : : {
643 : : {RPCResult::Type::STR, "value", "A way the block template may be changed, e.g. 'time', 'transactions', 'prevblock'"},
644 : : }},
645 : : {RPCResult::Type::STR_HEX, "noncerange", "A range of valid nonces"},
646 : : {RPCResult::Type::NUM, "sigoplimit", "limit of sigops in blocks"},
647 : : {RPCResult::Type::NUM, "sizelimit", "limit of block size"},
648 : : {RPCResult::Type::NUM, "weightlimit", /*optional=*/true, "limit of block weight"},
649 [ + - ]: 8992 : {RPCResult::Type::NUM_TIME, "curtime", "current timestamp in " + UNIX_EPOCH_TIME},
650 : : {RPCResult::Type::STR, "bits", "compressed target of next block"},
651 : : {RPCResult::Type::NUM, "height", "The height of the next block"},
652 : : {RPCResult::Type::STR_HEX, "signet_challenge", /*optional=*/true, "Only on signet"},
653 : : {RPCResult::Type::STR_HEX, "default_witness_commitment", /*optional=*/true, "a valid witness commitment for the unmodified block template"},
654 [ + - + - : 206816 : }},
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - ]
655 : : },
656 [ + - ]: 8992 : RPCExamples{
657 [ + - + - : 8992 : HelpExampleCli("getblocktemplate", "'{\"rules\": [\"segwit\"]}'")
+ - ]
658 [ + - + - : 17984 : + HelpExampleRpc("getblocktemplate", "{\"rules\": [\"segwit\"]}")
+ - + - ]
659 : : },
660 : 2081 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
661 : : {
662 : 2081 : NodeContext& node = EnsureAnyNodeContext(request.context);
663 : 2081 : ChainstateManager& chainman = EnsureChainman(node);
664 : 2081 : Mining& miner = EnsureMining(node);
665 : 2081 : LOCK(cs_main);
666 [ + - + - : 2081 : uint256 tip{CHECK_NONFATAL(miner.getTipHash()).value()};
+ - ]
667 : :
668 [ + - ]: 2081 : std::string strMode = "template";
669 [ + - ]: 2081 : UniValue lpval = NullUniValue;
670 [ + - ]: 2081 : std::set<std::string> setClientRules;
671 [ + - + - ]: 2081 : if (!request.params[0].isNull())
672 : : {
673 [ + - + - ]: 2081 : const UniValue& oparam = request.params[0].get_obj();
674 [ + - ]: 2081 : const UniValue& modeval = oparam.find_value("mode");
675 [ + + ]: 2081 : if (modeval.isStr())
676 [ + - + - ]: 12 : strMode = modeval.get_str();
677 [ - + ]: 2069 : else if (modeval.isNull())
678 : : {
679 : : /* Do nothing */
680 : : }
681 : : else
682 [ # # # # ]: 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode");
683 [ + - + - ]: 2081 : lpval = oparam.find_value("longpollid");
684 : :
685 [ + + ]: 2081 : if (strMode == "proposal")
686 : : {
687 [ + - ]: 12 : const UniValue& dataval = oparam.find_value("data");
688 [ - + ]: 12 : if (!dataval.isStr())
689 [ # # # # ]: 0 : throw JSONRPCError(RPC_TYPE_ERROR, "Missing data String key for proposal");
690 : :
691 : 12 : CBlock block;
692 [ + - + - : 12 : if (!DecodeHexBlk(block, dataval.get_str()))
+ + ]
693 [ + - + - ]: 4 : throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed");
694 : :
695 [ + - ]: 10 : uint256 hash = block.GetHash();
696 [ + - ]: 10 : const CBlockIndex* pindex = chainman.m_blockman.LookupBlockIndex(hash);
697 [ - + ]: 10 : if (pindex) {
698 [ # # # # ]: 0 : if (pindex->IsValid(BLOCK_VALID_SCRIPTS))
699 [ # # ]: 0 : return "duplicate";
700 [ # # ]: 0 : if (pindex->nStatus & BLOCK_FAILED_MASK)
701 [ # # ]: 0 : return "duplicate-invalid";
702 [ # # ]: 0 : return "duplicate-inconclusive";
703 : : }
704 : :
705 : : // testBlockValidity only supports blocks built on the current Tip
706 [ + + ]: 10 : if (block.hashPrevBlock != tip) {
707 [ + - ]: 1 : return "inconclusive-not-best-prevblk";
708 : : }
709 [ + - ]: 9 : BlockValidationState state;
710 [ + - ]: 9 : miner.testBlockValidity(block, /*check_merkle_root=*/true, state);
711 [ + - ]: 9 : return BIP22ValidationResult(state);
712 : 21 : }
713 : :
714 [ + - ]: 2069 : const UniValue& aClientRules = oparam.find_value("rules");
715 [ + + ]: 2069 : if (aClientRules.isArray()) {
716 [ + + ]: 4138 : for (unsigned int i = 0; i < aClientRules.size(); ++i) {
717 [ + - ]: 2070 : const UniValue& v = aClientRules[i];
718 [ + - + - ]: 2070 : setClientRules.insert(v.get_str());
719 : : }
720 : : }
721 : : }
722 : :
723 [ - + ]: 2069 : if (strMode != "template")
724 [ # # # # ]: 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode");
725 : :
726 [ + - - + ]: 2069 : if (!miner.isTestChain()) {
727 [ # # ]: 0 : const CConnman& connman = EnsureConnman(node);
728 [ # # # # ]: 0 : if (connman.GetNodeCount(ConnectionDirection::Both) == 0) {
729 [ # # # # ]: 0 : throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, PACKAGE_NAME " is not connected!");
730 : : }
731 : :
732 [ # # # # ]: 0 : if (miner.isInitialBlockDownload()) {
733 [ # # # # ]: 0 : throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, PACKAGE_NAME " is in initial sync and waiting for blocks...");
734 : : }
735 : : }
736 : :
737 : 2069 : static unsigned int nTransactionsUpdatedLast;
738 : :
739 [ + + ]: 2069 : if (!lpval.isNull())
740 : : {
741 : : // Wait to respond until either the best block changes, OR a minute has passed and there are more transactions
742 : 3 : uint256 hashWatchedChain;
743 : 3 : std::chrono::steady_clock::time_point checktxtime;
744 : 3 : unsigned int nTransactionsUpdatedLastLP;
745 : :
746 [ + - ]: 3 : if (lpval.isStr())
747 : : {
748 : : // Format: <hashBestChain><nTransactionsUpdatedLast>
749 [ + - ]: 3 : const std::string& lpstr = lpval.get_str();
750 : :
751 [ + - + - : 3 : hashWatchedChain = ParseHashV(lpstr.substr(0, 64), "longpollid");
+ - ]
752 [ + - + - ]: 3 : nTransactionsUpdatedLastLP = LocaleIndependentAtoi<int64_t>(lpstr.substr(64));
753 : : }
754 : : else
755 : : {
756 : : // NOTE: Spec does not specify behaviour for non-string longpollid, but this makes testing easier
757 : 0 : hashWatchedChain = tip;
758 : 0 : nTransactionsUpdatedLastLP = nTransactionsUpdatedLast;
759 : : }
760 : :
761 : : // Release lock while waiting
762 : 3 : LEAVE_CRITICAL_SECTION(cs_main);
763 : 3 : {
764 : 3 : checktxtime = std::chrono::steady_clock::now() + std::chrono::minutes(1);
765 : :
766 [ + - ]: 3 : WAIT_LOCK(g_best_block_mutex, lock);
767 [ + + + - : 5 : while (g_best_block == hashWatchedChain && IsRPCRunning())
+ - ]
768 : : {
769 [ + + + - ]: 3 : if (g_best_block_cv.wait_until(lock, checktxtime) == std::cv_status::timeout)
770 : : {
771 : : // Timeout: Check transactions for update
772 : : // without holding the mempool lock to avoid deadlocks
773 [ + - - + ]: 1 : if (miner.getTransactionsUpdated() != nTransactionsUpdatedLastLP)
774 : : break;
775 : 0 : checktxtime += std::chrono::seconds(10);
776 : : }
777 : : }
778 : 0 : }
779 [ + - ]: 3 : ENTER_CRITICAL_SECTION(cs_main);
780 : :
781 [ + - ]: 3 : std::optional<uint256> maybe_tip{miner.getTipHash()};
782 [ + - ]: 3 : CHECK_NONFATAL(maybe_tip);
783 [ + - ]: 3 : tip = maybe_tip.value();
784 : :
785 [ + - - + ]: 3 : if (!IsRPCRunning())
786 [ # # # # ]: 0 : throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Shutting down");
787 : : // TODO: Maybe recheck connections/IBD and (if something wrong) send an expires-immediately template to stop miners?
788 : : }
789 : :
790 [ + + ]: 2069 : const Consensus::Params& consensusParams = chainman.GetParams().GetConsensus();
791 : :
792 : : // GBT must be called with 'signet' set in the rules for signet chains
793 [ + + + - : 2073 : if (consensusParams.signet_blocks && setClientRules.count("signet") != 1) {
+ - + + -
+ ]
794 [ # # # # ]: 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "getblocktemplate must be called with the signet rule set (call with {\"rules\": [\"segwit\", \"signet\"]})");
795 : : }
796 : :
797 : : // GBT must be called with 'segwit' set in the rules
798 [ + - + + ]: 2069 : if (setClientRules.count("segwit") != 1) {
799 [ + - + - ]: 2 : throw JSONRPCError(RPC_INVALID_PARAMETER, "getblocktemplate must be called with the segwit rule set (call with {\"rules\": [\"segwit\"]})");
800 : : }
801 : :
802 : : // Update block
803 : 2068 : static CBlockIndex* pindexPrev;
804 : 2068 : static int64_t time_start;
805 [ + + + - ]: 2068 : static std::unique_ptr<CBlockTemplate> pblocktemplate;
806 [ + + + + ]: 2068 : if (!pindexPrev || pindexPrev->GetBlockHash() != tip ||
807 [ + - + + : 2010 : (miner.getTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - time_start > 5))
+ - + + ]
808 : : {
809 : : // Clear pindexPrev so future calls make a new block, despite any failures from here on
810 : 60 : pindexPrev = nullptr;
811 : :
812 : : // Store the pindexBest used before createNewBlock, to avoid races
813 [ + - ]: 60 : nTransactionsUpdatedLast = miner.getTransactionsUpdated();
814 [ + - ]: 60 : CBlockIndex* pindexPrevNew = chainman.m_blockman.LookupBlockIndex(tip);
815 [ + - ]: 60 : time_start = GetTime();
816 : :
817 : : // Create new block
818 [ + - ]: 60 : CScript scriptDummy = CScript() << OP_TRUE;
819 [ + - ]: 120 : pblocktemplate = miner.createNewBlock(scriptDummy);
820 [ - + ]: 60 : if (!pblocktemplate) {
821 [ # # # # ]: 0 : throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory");
822 : : }
823 : :
824 : : // Need to update only after we know createNewBlock succeeded
825 : 60 : pindexPrev = pindexPrevNew;
826 : 60 : }
827 [ + - ]: 2068 : CHECK_NONFATAL(pindexPrev);
828 [ + - ]: 2068 : CBlock* pblock = &pblocktemplate->block; // pointer for convenience
829 : :
830 : : // Update nTime
831 [ + - ]: 2068 : UpdateTime(pblock, consensusParams, pindexPrev);
832 : 2068 : pblock->nNonce = 0;
833 : :
834 : : // NOTE: If at some point we support pre-segwit miners post-segwit-activation, this needs to take segwit support into consideration
835 : 2068 : const bool fPreSegWit = !DeploymentActiveAfter(pindexPrev, chainman, Consensus::DEPLOYMENT_SEGWIT);
836 : :
837 [ + - + - ]: 2068 : UniValue aCaps(UniValue::VARR); aCaps.push_back("proposal");
838 : :
839 : 2068 : UniValue transactions(UniValue::VARR);
840 : 2068 : std::map<uint256, int64_t> setTxIndex;
841 : 2068 : int i = 0;
842 [ + + ]: 4200 : for (const auto& it : pblock->vtx) {
843 [ + - ]: 2132 : const CTransaction& tx = *it;
844 [ + - ]: 2132 : uint256 txHash = tx.GetHash();
845 [ + - ]: 2132 : setTxIndex[txHash] = i++;
846 : :
847 [ + + ]: 2132 : if (tx.IsCoinBase())
848 : 2068 : continue;
849 : :
850 : 64 : UniValue entry(UniValue::VOBJ);
851 : :
852 [ + - + - : 128 : entry.pushKV("data", EncodeHexTx(tx));
+ - + - ]
853 [ + - + - : 128 : entry.pushKV("txid", txHash.GetHex());
+ - + - ]
854 [ + - + - : 128 : entry.pushKV("hash", tx.GetWitnessHash().GetHex());
+ - + - ]
855 : :
856 : 64 : UniValue deps(UniValue::VARR);
857 [ + + ]: 128 : for (const CTxIn &in : tx.vin)
858 : : {
859 [ + + ]: 64 : if (setTxIndex.count(in.prevout.hash))
860 [ + - + - : 8 : deps.push_back(setTxIndex[in.prevout.hash]);
+ - ]
861 : : }
862 [ + - + - ]: 128 : entry.pushKV("depends", std::move(deps));
863 : :
864 : 64 : int index_in_template = i - 1;
865 [ + - + - : 128 : entry.pushKV("fee", pblocktemplate->vTxFees[index_in_template]);
+ - ]
866 [ + + ]: 64 : int64_t nTxSigOps = pblocktemplate->vTxSigOpsCost[index_in_template];
867 [ + + ]: 64 : if (fPreSegWit) {
868 [ + - ]: 6 : CHECK_NONFATAL(nTxSigOps % WITNESS_SCALE_FACTOR == 0);
869 : 6 : nTxSigOps /= WITNESS_SCALE_FACTOR;
870 : : }
871 [ + - + - : 128 : entry.pushKV("sigops", nTxSigOps);
+ - ]
872 [ + - + - : 128 : entry.pushKV("weight", GetTransactionWeight(tx));
+ - ]
873 : :
874 [ + - ]: 64 : transactions.push_back(std::move(entry));
875 : 64 : }
876 : :
877 : 2068 : UniValue aux(UniValue::VOBJ);
878 : :
879 [ + - ]: 4136 : arith_uint256 hashTarget = arith_uint256().SetCompact(pblock->nBits);
880 : :
881 : 2068 : UniValue aMutable(UniValue::VARR);
882 [ + - + - ]: 2068 : aMutable.push_back("time");
883 [ + - + - ]: 2068 : aMutable.push_back("transactions");
884 [ + - + - ]: 2068 : aMutable.push_back("prevblock");
885 : :
886 : 2068 : UniValue result(UniValue::VOBJ);
887 [ + - + - ]: 4136 : result.pushKV("capabilities", std::move(aCaps));
888 : :
889 : 2068 : UniValue aRules(UniValue::VARR);
890 [ + - + - ]: 2068 : aRules.push_back("csv");
891 [ + + + - : 2068 : if (!fPreSegWit) aRules.push_back("!segwit");
+ - ]
892 [ + + ]: 2068 : if (consensusParams.signet_blocks) {
893 : : // indicate to miner that they must understand signet rules
894 : : // when attempting to mine with this template
895 [ + - + - ]: 2 : aRules.push_back("!signet");
896 : : }
897 : :
898 : 2068 : UniValue vbavailable(UniValue::VOBJ);
899 [ + + ]: 6204 : for (int j = 0; j < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++j) {
900 : 4136 : Consensus::DeploymentPos pos = Consensus::DeploymentPos(j);
901 [ + - ]: 4136 : ThresholdState state = chainman.m_versionbitscache.State(pindexPrev, consensusParams, pos);
902 [ + + + + ]: 4136 : switch (state) {
903 : : case ThresholdState::DEFINED:
904 : : case ThresholdState::FAILED:
905 : : // Not exposed to GBT at all
906 : : break;
907 : 2 : case ThresholdState::LOCKED_IN:
908 : : // Ensure bit is set in block version
909 [ + - ]: 2 : pblock->nVersion |= chainman.m_versionbitscache.Mask(consensusParams, pos);
910 : 58 : [[fallthrough]];
911 : 58 : case ThresholdState::STARTED:
912 : 58 : {
913 : 58 : const struct VBDeploymentInfo& vbinfo = VersionBitsDeploymentInfo[pos];
914 [ + - + - : 116 : vbavailable.pushKV(gbt_vb_name(pos), consensusParams.vDeployments[pos].bit);
+ - ]
915 [ + - + - ]: 58 : if (setClientRules.find(vbinfo.name) == setClientRules.end()) {
916 [ - + ]: 58 : if (!vbinfo.gbt_force) {
917 : : // If the client doesn't support this, don't indicate it in the [default] version
918 [ # # ]: 0 : pblock->nVersion &= ~chainman.m_versionbitscache.Mask(consensusParams, pos);
919 : : }
920 : : }
921 : : break;
922 : : }
923 : 4068 : case ThresholdState::ACTIVE:
924 : 4068 : {
925 : : // Add to rules only
926 : 4068 : const struct VBDeploymentInfo& vbinfo = VersionBitsDeploymentInfo[pos];
927 [ + - + - : 4068 : aRules.push_back(gbt_vb_name(pos));
+ - ]
928 [ + - + - ]: 4068 : if (setClientRules.find(vbinfo.name) == setClientRules.end()) {
929 : : // Not supported by the client; make sure it's safe to proceed
930 [ - + ]: 4068 : if (!vbinfo.gbt_force) {
931 [ # # # # ]: 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Support for '%s' rule requires explicit client support", vbinfo.name));
932 : : }
933 : : }
934 : : break;
935 : : }
936 : : }
937 : : }
938 [ + - + - : 4136 : result.pushKV("version", pblock->nVersion);
+ - ]
939 [ + - + - ]: 4136 : result.pushKV("rules", std::move(aRules));
940 [ + - + - ]: 4136 : result.pushKV("vbavailable", std::move(vbavailable));
941 [ + - + - : 4136 : result.pushKV("vbrequired", int(0));
+ - ]
942 : :
943 [ + - + - : 4136 : result.pushKV("previousblockhash", pblock->hashPrevBlock.GetHex());
+ - + - ]
944 [ + - + - ]: 4136 : result.pushKV("transactions", std::move(transactions));
945 [ + - + - ]: 4136 : result.pushKV("coinbaseaux", std::move(aux));
946 [ + - + - : 4136 : result.pushKV("coinbasevalue", (int64_t)pblock->vtx[0]->vout[0].nValue);
+ - ]
947 [ + - + - : 4136 : result.pushKV("longpollid", tip.GetHex() + ToString(nTransactionsUpdatedLast));
+ - + - +
- + - ]
948 [ + - + - : 4136 : result.pushKV("target", hashTarget.GetHex());
+ - + - ]
949 [ + - + - : 4136 : result.pushKV("mintime", (int64_t)pindexPrev->GetMedianTimePast()+1);
+ - ]
950 [ + - + - ]: 4136 : result.pushKV("mutable", std::move(aMutable));
951 [ + - + - : 4136 : result.pushKV("noncerange", "00000000ffffffff");
+ - ]
952 : 2068 : int64_t nSigOpLimit = MAX_BLOCK_SIGOPS_COST;
953 : 2068 : int64_t nSizeLimit = MAX_BLOCK_SERIALIZED_SIZE;
954 [ + + ]: 2068 : if (fPreSegWit) {
955 [ + - ]: 5 : CHECK_NONFATAL(nSigOpLimit % WITNESS_SCALE_FACTOR == 0);
956 : 5 : nSigOpLimit /= WITNESS_SCALE_FACTOR;
957 [ + - ]: 5 : CHECK_NONFATAL(nSizeLimit % WITNESS_SCALE_FACTOR == 0);
958 : 5 : nSizeLimit /= WITNESS_SCALE_FACTOR;
959 : : }
960 [ + - + - : 4136 : result.pushKV("sigoplimit", nSigOpLimit);
+ - ]
961 [ + - + - : 4136 : result.pushKV("sizelimit", nSizeLimit);
+ - ]
962 [ + + ]: 2068 : if (!fPreSegWit) {
963 [ + - + - : 4126 : result.pushKV("weightlimit", (int64_t)MAX_BLOCK_WEIGHT);
+ - ]
964 : : }
965 [ + - + - : 4136 : result.pushKV("curtime", pblock->GetBlockTime());
+ - ]
966 [ + - + - : 4136 : result.pushKV("bits", strprintf("%08x", pblock->nBits));
+ - + - ]
967 [ + - + - : 4136 : result.pushKV("height", (int64_t)(pindexPrev->nHeight+1));
+ - ]
968 : :
969 [ + + ]: 2068 : if (consensusParams.signet_blocks) {
970 [ + - + - : 4 : result.pushKV("signet_challenge", HexStr(consensusParams.signet_challenge));
+ - + - ]
971 : : }
972 : :
973 [ + - ]: 2068 : if (!pblocktemplate->vchCoinbaseCommitment.empty()) {
974 [ + - + - : 4136 : result.pushKV("default_witness_commitment", HexStr(pblocktemplate->vchCoinbaseCommitment));
+ - + - ]
975 : : }
976 : :
977 : 2068 : return result;
978 [ + - ]: 6230 : },
979 [ + - + - : 130384 : };
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
+ + + - -
- - ]
980 [ + - + - : 431616 : }
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + - - -
- - - - -
- - - - -
- - - - -
- - - - -
- - - - -
- - - - -
- ]
981 : :
982 : : class submitblock_StateCatcher final : public CValidationInterface
983 : : {
984 : : public:
985 : : uint256 hash;
986 : : bool found{false};
987 : : BlockValidationState state;
988 : :
989 : 6288 : explicit submitblock_StateCatcher(const uint256 &hashIn) : hash(hashIn), state() {}
990 : :
991 : : protected:
992 : 6270 : void BlockChecked(const CBlock& block, const BlockValidationState& stateIn) override {
993 [ + + ]: 6270 : if (block.GetHash() != hash)
994 : : return;
995 : 6267 : found = true;
996 : 6267 : state = stateIn;
997 : : }
998 : : };
999 : :
1000 : 8831 : static RPCHelpMan submitblock()
1001 : : {
1002 : : // We allow 2 arguments for compliance with BIP22. Argument 2 is ignored.
1003 : 8831 : return RPCHelpMan{"submitblock",
1004 : : "\nAttempts to submit new block to network.\n"
1005 : : "See https://en.bitcoin.it/wiki/BIP_0022 for full specification.\n",
1006 : : {
1007 [ + - ]: 8831 : {"hexdata", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "the hex-encoded block data to submit"},
1008 : 8831 : {"dummy", RPCArg::Type::STR, RPCArg::DefaultHint{"ignored"}, "dummy value, for compatibility with BIP22. This value is ignored."},
1009 : : },
1010 : : {
1011 [ + - + - : 8831 : RPCResult{"If the block was accepted", RPCResult::Type::NONE, "", ""},
+ - + - ]
1012 [ + - + - : 8831 : RPCResult{"Otherwise", RPCResult::Type::STR, "", "According to BIP22"},
+ - + - ]
1013 : : },
1014 [ + - ]: 17662 : RPCExamples{
1015 [ + - + - : 17662 : HelpExampleCli("submitblock", "\"mydata\"")
+ - ]
1016 [ + - + - : 35324 : + HelpExampleRpc("submitblock", "\"mydata\"")
+ - + - ]
1017 : : },
1018 : 6416 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
1019 : : {
1020 : 6416 : std::shared_ptr<CBlock> blockptr = std::make_shared<CBlock>();
1021 [ + - ]: 6416 : CBlock& block = *blockptr;
1022 [ + - + - : 6416 : if (!DecodeHexBlk(block, request.params[0].get_str())) {
+ - + + ]
1023 [ + - + - ]: 2 : throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed");
1024 : : }
1025 : :
1026 [ + + + + ]: 6415 : if (block.vtx.empty() || !block.vtx[0]->IsCoinBase()) {
1027 [ + - + - ]: 4 : throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block does not start with a coinbase");
1028 : : }
1029 : :
1030 [ + - ]: 6413 : ChainstateManager& chainman = EnsureAnyChainman(request.context);
1031 [ + - ]: 6413 : uint256 hash = block.GetHash();
1032 : 6413 : {
1033 [ + - ]: 6413 : LOCK(cs_main);
1034 [ + - ]: 6413 : const CBlockIndex* pindex = chainman.m_blockman.LookupBlockIndex(hash);
1035 [ + + ]: 6413 : if (pindex) {
1036 [ + + + + ]: 131 : if (pindex->IsValid(BLOCK_VALID_SCRIPTS)) {
1037 [ + - ]: 122 : return "duplicate";
1038 : : }
1039 [ + + ]: 9 : if (pindex->nStatus & BLOCK_FAILED_MASK) {
1040 [ + - ]: 3 : return "duplicate-invalid";
1041 : : }
1042 : : }
1043 : 125 : }
1044 : :
1045 : 6288 : {
1046 [ + - ]: 6288 : LOCK(cs_main);
1047 [ + - ]: 6288 : const CBlockIndex* pindex = chainman.m_blockman.LookupBlockIndex(block.hashPrevBlock);
1048 [ + + ]: 6288 : if (pindex) {
1049 [ + - ]: 6285 : chainman.UpdateUncommittedBlockStructures(block, pindex);
1050 : : }
1051 : 0 : }
1052 : :
1053 [ + - ]: 6288 : NodeContext& node = EnsureAnyNodeContext(request.context);
1054 [ + - ]: 6288 : Mining& miner = EnsureMining(node);
1055 : :
1056 : 6288 : bool new_block;
1057 [ + - ]: 6288 : auto sc = std::make_shared<submitblock_StateCatcher>(block.GetHash());
1058 [ + - + - : 12576 : CHECK_NONFATAL(chainman.m_options.signals)->RegisterSharedValidationInterface(sc);
+ - ]
1059 [ + - + - : 12576 : bool accepted = miner.processNewBlock(blockptr, /*new_block=*/&new_block);
+ - ]
1060 [ + - + - : 12576 : CHECK_NONFATAL(chainman.m_options.signals)->UnregisterSharedValidationInterface(sc);
+ - ]
1061 [ + + + + ]: 6288 : if (!new_block && accepted) {
1062 [ + - ]: 1 : return "duplicate";
1063 : : }
1064 [ + + ]: 6287 : if (!sc->found) {
1065 [ + - ]: 20 : return "inconclusive";
1066 : : }
1067 [ + - ]: 6267 : return BIP22ValidationResult(sc->state);
1068 : 12701 : },
1069 [ + - + - : 123634 : };
+ - + - +
- + - + -
+ - + + +
+ - - -
- ]
1070 [ + - + - : 79479 : }
+ - + - +
- + - + -
+ - - - -
- ]
1071 : :
1072 : 3724 : static RPCHelpMan submitheader()
1073 : : {
1074 : 3724 : return RPCHelpMan{"submitheader",
1075 : : "\nDecode the given hexdata as a header and submit it as a candidate chain tip if valid."
1076 : : "\nThrows when the header is invalid.\n",
1077 : : {
1078 [ + - ]: 3724 : {"hexdata", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "the hex-encoded block header data"},
1079 : : },
1080 [ + - ]: 7448 : RPCResult{
1081 [ + - + - : 3724 : RPCResult::Type::NONE, "", "None"},
+ - ]
1082 [ + - ]: 7448 : RPCExamples{
1083 [ + - + - : 7448 : HelpExampleCli("submitheader", "\"aabbcc\"") +
+ - + - ]
1084 [ + - + - : 7448 : HelpExampleRpc("submitheader", "\"aabbcc\"")
+ - ]
1085 : : },
1086 : 1309 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
1087 : : {
1088 : 1309 : CBlockHeader h;
1089 [ + + ]: 1309 : if (!DecodeHexBlockHeader(h, request.params[0].get_str())) {
1090 [ + - + - ]: 4 : throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block header decode failed");
1091 : : }
1092 : 1307 : ChainstateManager& chainman = EnsureAnyChainman(request.context);
1093 : 1307 : {
1094 : 1307 : LOCK(cs_main);
1095 [ + - + + ]: 1307 : if (!chainman.m_blockman.LookupBlockIndex(h.hashPrevBlock)) {
1096 [ + - + - : 3 : throw JSONRPCError(RPC_VERIFY_ERROR, "Must submit previous header (" + h.hashPrevBlock.GetHex() + ") first");
+ - ]
1097 : : }
1098 : 1 : }
1099 : :
1100 [ + - ]: 1306 : BlockValidationState state;
1101 [ + - + - : 2612 : chainman.ProcessNewBlockHeaders({h}, /*min_pow_checked=*/true, state);
+ + ]
1102 [ + + ]: 1306 : if (state.IsValid()) return UniValue::VNULL;
1103 [ - + ]: 4 : if (state.IsError()) {
1104 [ # # # # ]: 0 : throw JSONRPCError(RPC_VERIFY_ERROR, state.ToString());
1105 : : }
1106 [ + - + - ]: 8 : throw JSONRPCError(RPC_VERIFY_ERROR, state.GetRejectReason());
1107 : 1302 : },
1108 [ + - + - : 33516 : };
+ - + - +
- + + -
- ]
1109 [ + - + - : 7448 : }
+ - ]
1110 : :
1111 : 1325 : void RegisterMiningRPCCommands(CRPCTable& t)
1112 : : {
1113 : 1325 : static const CRPCCommand commands[]{
1114 : : {"mining", &getnetworkhashps},
1115 : : {"mining", &getmininginfo},
1116 : : {"mining", &prioritisetransaction},
1117 : : {"mining", &getprioritisedtransactions},
1118 : : {"mining", &getblocktemplate},
1119 : : {"mining", &submitblock},
1120 : : {"mining", &submitheader},
1121 : :
1122 : : {"hidden", &generatetoaddress},
1123 : : {"hidden", &generatetodescriptor},
1124 : : {"hidden", &generateblock},
1125 : : {"hidden", &generate},
1126 [ + + + - : 1325 : };
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - -
- ]
1127 [ + + ]: 15900 : for (const auto& c : commands) {
1128 : 14575 : t.appendCommand(c.name, &c);
1129 : : }
1130 : 1325 : }
|