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