Line data Source code
1 : // Copyright (c) 2010 Satoshi Nakamoto
2 : // Copyright (c) 2009-2021 The Bitcoin Core developers
3 : // Copyright (c) 2014-2025 The Dash Core developers
4 : // Distributed under the MIT software license, see the accompanying
5 : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
6 :
7 : #include <chainparams.h>
8 : #include <consensus/consensus.h>
9 : #include <evo/mnauth.h>
10 : #include <httpserver.h>
11 : #include <index/addressindex.h>
12 : #include <index/blockfilterindex.h>
13 : #include <index/coinstatsindex.h>
14 : #include <index/spentindex.h>
15 : #include <index/timestampindex.h>
16 : #include <index/txindex.h>
17 : #include <init.h>
18 : #include <interfaces/chain.h>
19 : #include <interfaces/echo.h>
20 : #include <interfaces/init.h>
21 : #include <interfaces/ipc.h>
22 : #include <key_io.h>
23 : #include <net.h>
24 : #include <net_processing.h>
25 : #include <node/context.h>
26 : #include <rpc/server.h>
27 : #include <rpc/server_util.h>
28 : #include <rpc/util.h>
29 : #include <scheduler.h>
30 : #include <txmempool.h>
31 : #include <univalue.h>
32 : #include <util/check.h>
33 : #include <util/system.h>
34 : #include <validation.h>
35 :
36 : #include <masternode/sync.h>
37 : #include <spork.h>
38 :
39 : #include <stdint.h>
40 : #ifdef HAVE_MALLOC_INFO
41 : #include <malloc.h>
42 : #endif
43 :
44 : using node::NodeContext;
45 :
46 6162 : static RPCHelpMan debug()
47 : {
48 12324 : return RPCHelpMan{"debug",
49 : "Change debug category on the fly. Specify single category or use '+' to specify many.\n"
50 6162 : "The valid logging categories are: " + LogInstance().LogCategoriesString() + ".\n"
51 : "libevent logging is configured on startup and cannot be modified by this RPC during runtime.\n"
52 : "There are also a few meta-categories:\n"
53 : " - \"all\", \"1\" and \"\" activate all categories at once;\n"
54 : " - \"dash\" activates all Dash-specific categories at once;\n"
55 : " - \"none\" (or \"0\") deactivates all categories at once.\n"
56 : "Note: If specified category doesn't match any of the above, no error is thrown.\n"
57 : "Note: Consider using 'logging' RPC which has more features.\n"
58 : " - For 'debug all': logging [\\\"all\\\"]\n"
59 : " - For 'debug none': logging []\n"
60 : " - For 'debug X+Y': logging '[\"X\", \"Y\"]'",
61 12324 : {
62 6162 : {"category", RPCArg::Type::STR, RPCArg::Optional::NO, "The name of the debug category to turn on."},
63 : },
64 6162 : RPCResult{
65 6162 : RPCResult::Type::STR, "result", "\"Debug mode: \" followed by the specified category",
66 : },
67 6162 : RPCExamples {
68 6162 : HelpExampleCli("debug", "dash")
69 6162 : + HelpExampleRpc("debug", "dash+net")
70 : },
71 6166 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
72 : {
73 :
74 4 : std::string strMode = request.params[0].get_str();
75 4 : LogInstance().DisableCategory(BCLog::ALL);
76 :
77 4 : std::vector<std::string> categories = SplitString(strMode, '+');
78 4 : if (std::find(categories.begin(), categories.end(), std::string("0")) == categories.end()) {
79 8 : for (const auto& cat : categories) {
80 4 : LogInstance().EnableCategory(cat);
81 : }
82 4 : }
83 :
84 4 : return "Debug mode: " + LogInstance().LogCategoriesString(/*enabled_only=*/true);
85 4 : },
86 : };
87 0 : }
88 :
89 12070 : static RPCHelpMan mnsync()
90 : {
91 24140 : return RPCHelpMan{"mnsync",
92 12070 : "Returns the sync status, updates to the next step or resets it entirely.\n",
93 24140 : {
94 12070 : {"mode", RPCArg::Type::STR, RPCArg::Optional::NO, "[status|next|reset]"},
95 : },
96 36210 : {
97 24140 : RPCResult{"for mode = status",
98 12070 : RPCResult::Type::OBJ, "", "",
99 84490 : {
100 12070 : {RPCResult::Type::NUM, "AssetID", "The asset ID"},
101 12070 : {RPCResult::Type::STR, "AssetName", "The asset name"},
102 12070 : {RPCResult::Type::NUM, "AssetStartTime", "The asset start time"},
103 12070 : {RPCResult::Type::NUM, "Attempt", "The attempt"},
104 12070 : {RPCResult::Type::BOOL, "IsBlockchainSynced", "true if the blockchain synced"},
105 12070 : {RPCResult::Type::BOOL, "IsSynced", "true if synced"},
106 : }},
107 24140 : RPCResult{"for mode = next|reset",
108 12070 : RPCResult::Type::STR, "", ""},
109 : },
110 12070 : RPCExamples{""},
111 17984 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
112 : {
113 :
114 5914 : std::string strMode = request.params[0].get_str();
115 :
116 5914 : const NodeContext& node = EnsureAnyNodeContext(request.context);
117 5914 : CHECK_NONFATAL(node.mn_sync);
118 5914 : auto& mn_sync = *node.mn_sync;
119 :
120 5914 : if(strMode == "status") {
121 4348 : UniValue objStatus(UniValue::VOBJ);
122 4348 : objStatus.pushKV("AssetID", mn_sync.GetAssetID());
123 4348 : objStatus.pushKV("AssetName", mn_sync.GetAssetName());
124 4348 : objStatus.pushKV("AssetStartTime", mn_sync.GetAssetStartTime());
125 4348 : objStatus.pushKV("Attempt", mn_sync.GetAttempt());
126 4348 : objStatus.pushKV("IsBlockchainSynced", mn_sync.IsBlockchainSynced());
127 4348 : objStatus.pushKV("IsSynced", mn_sync.IsSynced());
128 4348 : return objStatus;
129 4348 : }
130 :
131 1566 : if(strMode == "next")
132 : {
133 1542 : mn_sync.SwitchToNextAsset();
134 1542 : return "sync updated to " + mn_sync.GetAssetName();
135 : }
136 :
137 24 : if(strMode == "reset")
138 : {
139 24 : mn_sync.Reset(true);
140 24 : return "success";
141 : }
142 0 : return "failure";
143 5914 : },
144 : };
145 0 : }
146 :
147 : /*
148 : Used for updating/reading spork settings on the network
149 : */
150 11874 : static RPCHelpMan spork()
151 : {
152 : // default help, for basic mode
153 23748 : return RPCHelpMan{"spork",
154 11874 : "\nShows information about current state of sporks\n",
155 23748 : {
156 11874 : {"command", RPCArg::Type::STR, RPCArg::Optional::NO, "'show' to show all current spork values, 'active' to show which sporks are active"},
157 : },
158 35622 : {
159 23748 : RPCResult{"For 'show'",
160 11874 : RPCResult::Type::OBJ_DYN, "", "keys are the sporks, and values indicates its value",
161 23748 : {
162 11874 : {RPCResult::Type::NUM, "SPORK_NAME", "The value of the specific spork with the name SPORK_NAME"},
163 : }},
164 23748 : RPCResult{"For 'active'",
165 11874 : RPCResult::Type::OBJ_DYN, "", "keys are the sporks, and values indicates its status",
166 23748 : {
167 11874 : {RPCResult::Type::BOOL, "SPORK_NAME", "'true' for time-based sporks if spork is active and 'false' otherwise"},
168 : }},
169 : },
170 11874 : RPCExamples {
171 11874 : HelpExampleCli("spork", "show")
172 11874 : + HelpExampleRpc("spork", "\"show\"")
173 : },
174 17592 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
175 : {
176 :
177 : // basic mode, show info
178 5718 : std:: string strCommand = request.params[0].get_str();
179 5718 : const NodeContext& node = EnsureAnyNodeContext(request.context);
180 5718 : CHECK_NONFATAL(node.sporkman);
181 5718 : if (strCommand == "show") {
182 5692 : UniValue ret(UniValue::VOBJ);
183 34152 : for (const auto& sporkDef : sporkDefs) {
184 28460 : ret.pushKV(std::string(sporkDef.name), node.sporkman->GetSporkValue(sporkDef.sporkId));
185 : }
186 5692 : return ret;
187 5718 : } else if(strCommand == "active"){
188 26 : UniValue ret(UniValue::VOBJ);
189 156 : for (const auto& sporkDef : sporkDefs) {
190 130 : ret.pushKV(std::string(sporkDef.name), node.sporkman->IsSporkActive(sporkDef.sporkId));
191 : }
192 26 : return ret;
193 26 : }
194 :
195 0 : return UniValue::VNULL;
196 5718 : },
197 : };
198 0 : }
199 :
200 6561 : static RPCHelpMan sporkupdate()
201 : {
202 13122 : return RPCHelpMan{"sporkupdate",
203 6561 : "\nUpdate the value of the specific spork. Requires \"-sporkkey\" to be set to sign the message.\n",
204 19683 : {
205 6561 : {"name", RPCArg::Type::STR, RPCArg::Optional::NO, "The name of the spork to update"},
206 6561 : {"value", RPCArg::Type::NUM, RPCArg::Optional::NO, "The new desired value of the spork"},
207 : },
208 6561 : RPCResult{
209 6561 : RPCResult::Type::STR, "result", "\"success\" if spork value was updated or this help otherwise"
210 : },
211 6561 : RPCExamples{
212 6561 : HelpExampleCli("sporkupdate", "SPORK_2_INSTANTSEND_ENABLED 4070908800")
213 6561 : + HelpExampleRpc("sporkupdate", "\"SPORK_2_INSTANTSEND_ENABLED\", 4070908800")
214 : },
215 6966 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
216 : {
217 :
218 : // advanced mode, update spork values
219 405 : SporkId nSporkID = CSporkManager::GetSporkIDByName(request.params[0].get_str());
220 405 : if (nSporkID == SPORK_INVALID) {
221 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid spork name");
222 : }
223 :
224 405 : const NodeContext& node = EnsureAnyNodeContext(request.context);
225 405 : CHECK_NONFATAL(node.sporkman);
226 :
227 : // SPORK VALUE
228 405 : int64_t nValue = request.params[1].getInt<int64_t>();
229 :
230 405 : auto inv{node.sporkman->UpdateSpork(nSporkID, nValue)};
231 405 : if (inv.has_value()) {
232 405 : PeerManager& peerman = EnsurePeerman(node);
233 405 : peerman.RelayInv(inv.value());
234 405 : return "success";
235 : }
236 :
237 0 : return UniValue::VNULL;
238 405 : },
239 : };
240 0 : }
241 :
242 51067 : static RPCHelpMan setmocktime()
243 : {
244 102134 : return RPCHelpMan{"setmocktime",
245 51067 : "\nSet the local time to given timestamp (-regtest only)\n",
246 102134 : {
247 51067 : {"timestamp", RPCArg::Type::NUM, RPCArg::Optional::NO, UNIX_EPOCH_TIME + "\n"
248 : "Pass 0 to go back to using the system time."},
249 : },
250 51067 : RPCResult{RPCResult::Type::NONE, "", ""},
251 51067 : RPCExamples{""},
252 95994 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
253 : {
254 :
255 44927 : if (!Params().IsMockableChain()) {
256 2 : throw std::runtime_error("setmocktime is for regression testing (-regtest mode) only");
257 : }
258 :
259 : // For now, don't change mocktime if we're in the middle of validation, as
260 : // this could have an effect on mempool time-based eviction, as well as
261 : // IsCurrentForFeeEstimation() and IsInitialBlockDownload().
262 : // TODO: figure out the right way to synchronize around mocktime, and
263 : // ensure all call sites of GetTime() are accessing this safely.
264 44927 : LOCK(cs_main);
265 :
266 44927 : RPCTypeCheck(request.params, {UniValue::VNUM});
267 44927 : const int64_t time{request.params[0].getInt<int64_t>()};
268 44927 : if (time < 0) {
269 2 : throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Mocktime cannot be negative: %s.", time));
270 : }
271 44925 : SetMockTime(time);
272 44925 : if (auto* node_context = GetContext<NodeContext>(request.context)) {
273 54688 : for (const auto& chain_client : node_context->chain_clients) {
274 9763 : chain_client->setMockTime(time);
275 : }
276 44925 : }
277 :
278 44925 : return UniValue::VNULL;
279 44929 : },
280 : };
281 0 : }
282 :
283 6226 : static RPCHelpMan mnauth()
284 : {
285 12452 : return RPCHelpMan{"mnauth",
286 6226 : "\nOverride MNAUTH processing results for the specified node with a user provided data (-regtest only).\n",
287 24904 : {
288 6226 : {"nodeId", RPCArg::Type::NUM, RPCArg::Optional::NO, "Internal peer id of the node the mock data gets added to."},
289 6226 : {"proTxHash", RPCArg::Type::STR, RPCArg::Optional::NO, "The authenticated proTxHash as hex string."},
290 6226 : {"publicKey", RPCArg::Type::STR, RPCArg::Optional::NO, "The authenticated public key as hex string."},
291 : },
292 6226 : RPCResult{
293 6226 : RPCResult::Type::BOOL, "result", "true, if the node was updated"
294 : },
295 6226 : RPCExamples{""},
296 6312 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
297 : {
298 :
299 86 : if (!Params().MineBlocksOnDemand())
300 4 : throw std::runtime_error("mnauth for regression testing (-regtest mode) only");
301 :
302 86 : int64_t nodeId = request.params[0].getInt<int64_t>();
303 86 : uint256 proTxHash = ParseHashV(request.params[1], "proTxHash");
304 86 : if (proTxHash.IsNull()) {
305 2 : throw JSONRPCError(RPC_INVALID_PARAMETER, "proTxHash invalid");
306 : }
307 :
308 84 : CBLSPublicKey publicKey;
309 84 : publicKey.SetHexStr(request.params[2].get_str(), /*specificLegacyScheme=*/false);
310 84 : if (!publicKey.IsValid()) {
311 2 : throw JSONRPCError(RPC_INVALID_PARAMETER, "publicKey invalid");
312 : }
313 :
314 82 : const NodeContext& node = EnsureAnyNodeContext(request.context);
315 162 : bool fSuccess = node.connman->ForNode(nodeId, CConnman::AllNodes, [&](CNode* pNode){
316 80 : pNode->SetVerifiedProRegTxHash(proTxHash);
317 80 : pNode->SetVerifiedPubKeyHash(publicKey.GetHash());
318 80 : return true;
319 : });
320 :
321 82 : return fSuccess;
322 4 : },
323 : };
324 0 : }
325 :
326 40 : static bool getAddressFromIndex(const AddressType& type, const uint160 &hash, std::string &address)
327 : {
328 40 : if (type == AddressType::P2SH) {
329 0 : address = EncodeDestination(ScriptHash(hash));
330 40 : } else if (type == AddressType::P2PK_OR_P2PKH) {
331 40 : address = EncodeDestination(PKHash(hash));
332 40 : } else {
333 0 : return false;
334 : }
335 40 : return true;
336 40 : }
337 :
338 48 : static bool getIndexKey(const std::string& str, uint160& hashBytes, AddressType& type)
339 : {
340 48 : CTxDestination dest = DecodeDestination(str);
341 48 : if (!IsValidDestination(dest)) {
342 0 : type = AddressType::UNKNOWN;
343 0 : return false;
344 : }
345 48 : const PKHash *pkhash = std::get_if<PKHash>(&dest);
346 48 : const ScriptHash *scriptID = std::get_if<ScriptHash>(&dest);
347 48 : type = pkhash ? AddressType::P2PK_OR_P2PKH : AddressType::P2SH;
348 48 : hashBytes = pkhash ? uint160(*pkhash) : uint160(*scriptID);
349 48 : return true;
350 48 : }
351 :
352 46 : static bool getAddressesFromParams(const UniValue& params, std::vector<std::pair<uint160, AddressType> > &addresses)
353 : {
354 46 : if (params[0].isStr()) {
355 20 : uint160 hashBytes;
356 20 : AddressType type{AddressType::UNKNOWN};
357 20 : if (!getIndexKey(params[0].get_str(), hashBytes, type)) {
358 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
359 : }
360 20 : addresses.push_back(std::make_pair(hashBytes, type));
361 46 : } else if (params[0].isObject()) {
362 :
363 26 : UniValue addressValues = params[0].get_obj().find_value("addresses");
364 26 : if (!addressValues.isArray()) {
365 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Addresses is expected to be an array");
366 : }
367 :
368 54 : for (const auto& address : addressValues.getValues()) {
369 28 : uint160 hashBytes;
370 28 : AddressType type{AddressType::UNKNOWN};
371 28 : if (!getIndexKey(address.get_str(), hashBytes, type)) {
372 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
373 : }
374 28 : addresses.push_back(std::make_pair(hashBytes, type));
375 : }
376 26 : } else {
377 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
378 : }
379 :
380 46 : return true;
381 0 : }
382 :
383 6164 : static RPCHelpMan getaddressmempool()
384 : {
385 12328 : return RPCHelpMan{"getaddressmempool",
386 6164 : "\nReturns all mempool deltas for an address (requires addressindex to be enabled).\n",
387 12328 : {
388 12328 : {"addresses", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "",
389 12328 : {
390 6164 : {"address", RPCArg::Type::STR, RPCArg::Default{""}, "The base58check encoded address"},
391 : },
392 : },
393 : },
394 6164 : RPCResult{
395 6164 : RPCResult::Type::ARR, "", "",
396 12328 : {
397 12328 : {RPCResult::Type::OBJ, "", "",
398 49312 : {
399 6164 : {RPCResult::Type::STR, "address", "The base58check encoded address"},
400 6164 : {RPCResult::Type::STR_HEX, "txid", "The related txid"},
401 6164 : {RPCResult::Type::NUM, "index", "The related input or output index"},
402 6164 : {RPCResult::Type::NUM, "satoshis", "The difference of duffs"},
403 6164 : {RPCResult::Type::NUM_TIME, "timestamp", "The time the transaction entered the mempool (seconds)"},
404 6164 : {RPCResult::Type::STR_HEX, "prevtxid", "The previous txid (if spending)"},
405 6164 : {RPCResult::Type::NUM, "prevout", "The previous transaction output index (if spending)"},
406 : }},
407 : }},
408 6164 : RPCExamples{
409 6164 : HelpExampleCli("getaddressmempool", "'{\"addresses\": [\"" + EXAMPLE_ADDRESS[0] + "\"]}'")
410 6164 : + HelpExampleRpc("getaddressmempool", "{\"addresses\": [\"" + EXAMPLE_ADDRESS[0] + "\"]}")
411 : },
412 6172 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
413 : {
414 8 : if (!g_addressindex) {
415 0 : throw JSONRPCError(RPC_MISC_ERROR, "Address index is not enabled. Start with -addressindex to enable.");
416 : }
417 :
418 8 : CTxMemPool& mempool = EnsureAnyMemPool(request.context);
419 :
420 8 : std::vector<std::pair<uint160, AddressType>> addresses;
421 8 : if (!getAddressesFromParams(request.params, addresses)) {
422 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
423 : }
424 :
425 8 : std::vector<CMempoolAddressDeltaKey> input_addresses;
426 8 : std::vector<CMempoolAddressDeltaEntry> indexes;
427 16 : for (const auto& [hash, type] : addresses) {
428 16 : input_addresses.push_back({type, hash});
429 : }
430 :
431 8 : mempool.getAddressIndex(input_addresses, indexes);
432 :
433 8 : std::sort(indexes.begin(), indexes.end(),
434 8 : [](const CMempoolAddressDeltaEntry &a, const CMempoolAddressDeltaEntry &b) {
435 8 : return a.second.m_time < b.second.m_time;
436 : });
437 :
438 8 : UniValue result(UniValue::VARR);
439 :
440 104 : for (const auto& [mempoolAddressKey, mempoolAddressDelta] : indexes) {
441 14 : std::string address;
442 28 : if (!getAddressFromIndex(mempoolAddressKey.m_address_type, mempoolAddressKey.m_address_bytes, address)) {
443 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unknown address type");
444 : }
445 :
446 14 : UniValue delta(UniValue::VOBJ);
447 14 : delta.pushKV("address", address);
448 14 : delta.pushKV("txid", mempoolAddressKey.m_tx_hash.GetHex());
449 14 : delta.pushKV("index", mempoolAddressKey.m_tx_index);
450 14 : delta.pushKV("satoshis", mempoolAddressDelta.m_amount);
451 14 : delta.pushKV("timestamp", count_seconds(mempoolAddressDelta.m_time));
452 14 : if (mempoolAddressDelta.m_amount < 0) {
453 6 : delta.pushKV("prevtxid", mempoolAddressDelta.m_prev_hash.GetHex());
454 6 : delta.pushKV("prevout", mempoolAddressDelta.m_prev_out);
455 6 : }
456 14 : result.push_back(delta);
457 14 : }
458 :
459 8 : return result;
460 8 : },
461 : };
462 0 : }
463 :
464 6164 : static RPCHelpMan getaddressutxos()
465 : {
466 12328 : return RPCHelpMan{"getaddressutxos",
467 6164 : "\nReturns all unspent outputs for an address (requires addressindex to be enabled).\n",
468 12328 : {
469 12328 : {"addresses", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "",
470 12328 : {
471 6164 : {"address", RPCArg::Type::STR, RPCArg::Default{""}, "The base58check encoded address"},
472 : },
473 : },
474 : },
475 6164 : RPCResult{
476 6164 : RPCResult::Type::ARR, "", "",
477 12328 : {
478 12328 : {RPCResult::Type::OBJ, "", "",
479 43148 : {
480 6164 : {RPCResult::Type::STR, "address", "The address base58check encoded"},
481 6164 : {RPCResult::Type::STR_HEX, "txid", "The output txid"},
482 6164 : {RPCResult::Type::NUM, "index", "The output index"},
483 6164 : {RPCResult::Type::STR_HEX, "script", "The script hex-encoded"},
484 6164 : {RPCResult::Type::NUM, "satoshis", "The number of duffs of the output"},
485 6164 : {RPCResult::Type::NUM, "height", "The block height"},
486 : }},
487 : }},
488 6164 : RPCExamples{
489 6164 : HelpExampleCli("getaddressutxos", "'{\"addresses\": [\"" + EXAMPLE_ADDRESS[0] + "\"]}'")
490 6164 : + HelpExampleRpc("getaddressutxos", "{\"addresses\": [\"" + EXAMPLE_ADDRESS[0] + "\"]}")
491 : },
492 6172 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
493 : {
494 8 : std::vector<std::pair<uint160, AddressType> > addresses;
495 8 : if (!getAddressesFromParams(request.params, addresses)) {
496 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
497 : }
498 :
499 8 : if (!g_addressindex) {
500 0 : throw JSONRPCError(RPC_MISC_ERROR, "Address index is not enabled. Start with -addressindex to enable.");
501 : }
502 :
503 8 : if (!g_addressindex->BlockUntilSyncedToCurrentChain()) {
504 0 : throw JSONRPCError(RPC_MISC_ERROR, strprintf("Address index is syncing. Current height: %d", g_addressindex->GetSummary().best_block_height));
505 : }
506 :
507 8 : std::vector<CAddressUnspentIndexEntry> unspentOutputs;
508 :
509 16 : for (const auto& address : addresses) {
510 8 : if (!g_addressindex->GetAddressUnspentIndex(address.first, address.second, unspentOutputs,
511 : /* height_sort = */ true)) {
512 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for address");
513 : }
514 : }
515 :
516 8 : UniValue result(UniValue::VARR);
517 :
518 80 : for (const auto& [unspentKey, unspentValue] : unspentOutputs) {
519 12 : UniValue output(UniValue::VOBJ);
520 12 : std::string address;
521 24 : if (!getAddressFromIndex(unspentKey.m_address_type, unspentKey.m_address_bytes, address)) {
522 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unknown address type");
523 : }
524 :
525 12 : output.pushKV("address", address);
526 12 : output.pushKV("txid", unspentKey.m_tx_hash.GetHex());
527 12 : output.pushKV("outputIndex", unspentKey.m_tx_index);
528 12 : output.pushKV("script", HexStr(unspentValue.m_tx_script));
529 12 : output.pushKV("satoshis", unspentValue.m_amount);
530 12 : output.pushKV("height", unspentValue.m_block_height);
531 12 : result.push_back(output);
532 12 : }
533 :
534 8 : return result;
535 8 : },
536 : };
537 0 : }
538 :
539 6162 : static RPCHelpMan getaddressdeltas()
540 : {
541 12324 : return RPCHelpMan{"getaddressdeltas",
542 6162 : "\nReturns all changes for an address (requires addressindex to be enabled).\n",
543 12324 : {
544 12324 : {"addresses", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "",
545 12324 : {
546 6162 : {"address", RPCArg::Type::STR, RPCArg::Default{""}, "The base58check encoded address"},
547 : },
548 : },
549 : },
550 6162 : RPCResult{
551 6162 : RPCResult::Type::ARR, "", "",
552 12324 : {
553 12324 : {RPCResult::Type::OBJ, "", "",
554 43134 : {
555 6162 : {RPCResult::Type::NUM, "satoshis", "The difference of duffs"},
556 6162 : {RPCResult::Type::STR_HEX, "txid", "The related txid"},
557 6162 : {RPCResult::Type::NUM, "index", "The related input or output index"},
558 6162 : {RPCResult::Type::NUM, "blockindex", "The related block index"},
559 6162 : {RPCResult::Type::NUM, "height", "The block height"},
560 6162 : {RPCResult::Type::STR, "address", "The base58check encoded address"},
561 : }},
562 : }},
563 6162 : RPCExamples{
564 6162 : HelpExampleCli("getaddressdeltas", "'{\"addresses\": [\"" + EXAMPLE_ADDRESS[0] + "\"]}'")
565 6162 : + HelpExampleRpc("getaddressdeltas", "{\"addresses\": [\"" + EXAMPLE_ADDRESS[0] + "\"]}")
566 : },
567 6168 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
568 : {
569 6 : UniValue startValue = request.params[0].get_obj().find_value("start");
570 6 : UniValue endValue = request.params[0].get_obj().find_value("end");
571 :
572 6 : int start = 0;
573 6 : int end = 0;
574 :
575 6 : if (startValue.isNum() && endValue.isNum()) {
576 4 : start = startValue.getInt<int>();
577 4 : end = endValue.getInt<int>();
578 4 : if (end < start) {
579 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "End value is expected to be greater than start");
580 : }
581 4 : }
582 :
583 6 : std::vector<std::pair<uint160, AddressType> > addresses;
584 :
585 6 : if (!getAddressesFromParams(request.params, addresses)) {
586 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
587 : }
588 :
589 6 : std::vector<CAddressIndexEntry> addressIndex;
590 :
591 6 : if (!g_addressindex) {
592 0 : throw JSONRPCError(RPC_MISC_ERROR, "Address index is not enabled. Start with -addressindex to enable.");
593 : }
594 :
595 6 : if (!g_addressindex->BlockUntilSyncedToCurrentChain()) {
596 0 : throw JSONRPCError(RPC_MISC_ERROR, strprintf("Address index is syncing. Current height: %d", g_addressindex->GetSummary().best_block_height));
597 : }
598 :
599 12 : for (const auto& address : addresses) {
600 6 : if (start <= 0 || end <= 0) { start = 0; end = 0; }
601 12 : if (!g_addressindex->GetAddressIndex(address.first, address.second,
602 6 : addressIndex, start, end)) {
603 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for address");
604 : }
605 : }
606 :
607 6 : UniValue result(UniValue::VARR);
608 :
609 90 : for (const auto& [indexKey, indexDelta] : addressIndex) {
610 14 : std::string address;
611 28 : if (!getAddressFromIndex(indexKey.m_address_type, indexKey.m_address_bytes, address)) {
612 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unknown address type");
613 : }
614 :
615 14 : UniValue delta(UniValue::VOBJ);
616 14 : delta.pushKV("satoshis", indexDelta);
617 14 : delta.pushKV("txid", indexKey.m_tx_hash.GetHex());
618 14 : delta.pushKV("index", indexKey.m_tx_index);
619 14 : delta.pushKV("blockindex", indexKey.m_block_tx_pos);
620 14 : delta.pushKV("height", indexKey.m_block_height);
621 14 : delta.pushKV("address", address);
622 14 : result.push_back(delta);
623 14 : }
624 :
625 6 : return result;
626 6 : },
627 : };
628 0 : }
629 :
630 6170 : static RPCHelpMan getaddressbalance()
631 : {
632 12340 : return RPCHelpMan{"getaddressbalance",
633 6170 : "\nReturns the balance for an address(es) (requires addressindex to be enabled).\n",
634 12340 : {
635 12340 : {"addresses", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "",
636 12340 : {
637 6170 : {"address", RPCArg::Type::STR, RPCArg::Default{""}, "The base58check encoded address"},
638 : },
639 : },
640 : },
641 6170 : RPCResult{
642 6170 : RPCResult::Type::OBJ, "", "",
643 30850 : {
644 6170 : {RPCResult::Type::NUM, "balance", "The current total balance in duffs"},
645 6170 : {RPCResult::Type::NUM, "balance_immature", "The current immature balance in duffs"},
646 6170 : {RPCResult::Type::NUM, "balance_spendable", "The current spendable balance in duffs"},
647 6170 : {RPCResult::Type::NUM, "received", "The total number of duffs received (including change)"},
648 : }},
649 6170 : RPCExamples{
650 6170 : HelpExampleCli("getaddressbalance", "'{\"addresses\": [\"" + EXAMPLE_ADDRESS[0] + "\"]}'")
651 6170 : + HelpExampleRpc("getaddressbalance", "{\"addresses\": [\"" + EXAMPLE_ADDRESS[0] + "\"]}")
652 : },
653 6184 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
654 : {
655 14 : std::vector<std::pair<uint160, AddressType> > addresses;
656 :
657 14 : if (!getAddressesFromParams(request.params, addresses)) {
658 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
659 : }
660 :
661 14 : std::vector<CAddressIndexEntry> addressIndex;
662 :
663 14 : if (!g_addressindex) {
664 0 : throw JSONRPCError(RPC_MISC_ERROR, "Address index is not enabled. Start with -addressindex to enable.");
665 : }
666 :
667 14 : if (!g_addressindex->BlockUntilSyncedToCurrentChain()) {
668 0 : throw JSONRPCError(RPC_MISC_ERROR, strprintf("Address index is syncing. Current height: %d", g_addressindex->GetSummary().best_block_height));
669 : }
670 :
671 : int nHeight;
672 : {
673 14 : LOCK(::cs_main);
674 28 : for (const auto& address : addresses) {
675 14 : if (!g_addressindex->GetAddressIndex(address.first, address.second, addressIndex,
676 : /*start=*/0, /*end=*/0)) {
677 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for address");
678 : }
679 : }
680 14 : nHeight = g_addressindex->GetSummary().best_block_height;
681 14 : }
682 :
683 :
684 14 : CAmount balance = 0;
685 14 : CAmount balance_spendable = 0;
686 14 : CAmount balance_immature = 0;
687 14 : CAmount received = 0;
688 :
689 250 : for (const auto& [indexKey, indexDelta] : addressIndex) {
690 236 : if (indexDelta > 0) {
691 234 : received += indexDelta;
692 234 : }
693 236 : if (indexKey.m_block_tx_pos == 0 && nHeight - indexKey.m_block_height < COINBASE_MATURITY) {
694 200 : balance_immature += indexDelta;
695 200 : } else {
696 36 : balance_spendable += indexDelta;
697 : }
698 236 : balance += indexDelta;
699 : }
700 :
701 14 : UniValue result(UniValue::VOBJ);
702 14 : result.pushKV("balance", balance);
703 14 : result.pushKV("balance_immature", balance_immature);
704 14 : result.pushKV("balance_spendable", balance_spendable);
705 14 : result.pushKV("received", received);
706 :
707 14 : return result;
708 :
709 14 : },
710 : };
711 0 : }
712 :
713 6166 : static RPCHelpMan getaddresstxids()
714 : {
715 12332 : return RPCHelpMan{"getaddresstxids",
716 6166 : "\nReturns the txids for an address(es) (requires addressindex to be enabled).\n",
717 12332 : {
718 12332 : {"addresses", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "",
719 12332 : {
720 6166 : {"address", RPCArg::Type::STR, RPCArg::Default{""}, "The base58check encoded address"},
721 : },
722 : },
723 : },
724 6166 : RPCResult{
725 6166 : RPCResult::Type::ARR, "", "",
726 6166 : {{RPCResult::Type::STR_HEX, "transactionid", "The transaction id"}}
727 : },
728 6166 : RPCExamples{
729 6166 : HelpExampleCli("getaddresstxids", "'{\"addresses\": [\"" + EXAMPLE_ADDRESS[0] + "\"]}'")
730 6166 : + HelpExampleRpc("getaddresstxids", "{\"addresses\": [\"" + EXAMPLE_ADDRESS[0] + "\"]}")
731 : },
732 6176 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
733 : {
734 10 : std::vector<std::pair<uint160, AddressType> > addresses;
735 :
736 10 : if (!getAddressesFromParams(request.params, addresses)) {
737 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
738 : }
739 :
740 10 : int start = 0;
741 10 : int end = 0;
742 10 : if (request.params[0].isObject()) {
743 4 : UniValue startValue = request.params[0].get_obj().find_value("start");
744 4 : UniValue endValue = request.params[0].get_obj().find_value("end");
745 4 : if (startValue.isNum() && endValue.isNum()) {
746 2 : start = startValue.getInt<int>();
747 2 : end = endValue.getInt<int>();
748 2 : }
749 4 : }
750 :
751 10 : std::vector<CAddressIndexEntry> addressIndex;
752 :
753 10 : if (!g_addressindex) {
754 0 : throw JSONRPCError(RPC_MISC_ERROR, "Address index is not enabled. Start with -addressindex to enable.");
755 : }
756 :
757 10 : if (!g_addressindex->BlockUntilSyncedToCurrentChain()) {
758 0 : throw JSONRPCError(RPC_MISC_ERROR, strprintf("Address index is syncing. Current height: %d", g_addressindex->GetSummary().best_block_height));
759 : }
760 :
761 22 : for (const auto& address : addresses) {
762 12 : if (start <= 0 || end <= 0) { start = 0; end = 0; }
763 24 : if (!g_addressindex->GetAddressIndex(address.first, address.second,
764 12 : addressIndex, start, end)) {
765 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for address");
766 : }
767 : }
768 :
769 10 : std::set<std::pair<int, std::string> > txids;
770 10 : UniValue result(UniValue::VARR);
771 :
772 48 : for (const auto& [indexKey, _]: addressIndex) {
773 38 : int height = indexKey.m_block_height;
774 38 : std::string txid = indexKey.m_tx_hash.GetHex();
775 :
776 38 : if (addresses.size() > 1) {
777 12 : txids.insert(std::make_pair(height, txid));
778 12 : } else {
779 26 : if (txids.insert(std::make_pair(height, txid)).second) {
780 24 : result.push_back(txid);
781 24 : }
782 : }
783 38 : }
784 :
785 10 : if (addresses.size() > 1) {
786 14 : for (const auto& tx : txids) {
787 12 : result.push_back(tx.second);
788 : }
789 2 : }
790 :
791 10 : return result;
792 :
793 10 : },
794 : };
795 0 : }
796 :
797 6166 : static RPCHelpMan getspentinfo()
798 : {
799 12332 : return RPCHelpMan{"getspentinfo",
800 6166 : "\nReturns the txid and index where an output is spent.\n",
801 12332 : {
802 12332 : {"request", RPCArg::Type::OBJ, RPCArg::Default{UniValue::VOBJ}, "",
803 18498 : {
804 6166 : {"txid", RPCArg::Type::STR_HEX, RPCArg::Default{""}, "The hex string of the txid"},
805 6166 : {"index", RPCArg::Type::NUM, RPCArg::Default{0}, "The start block height"},
806 : },
807 : },
808 : },
809 6166 : RPCResult{
810 6166 : RPCResult::Type::OBJ, "", "",
811 18498 : {
812 6166 : {RPCResult::Type::STR_HEX, "txid", "The transaction id"},
813 6166 : {RPCResult::Type::NUM, "index", "The spending input index"},
814 : }},
815 6166 : RPCExamples{
816 6166 : HelpExampleCli("getspentinfo", "'{\"txid\": \"0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9\", \"index\": 0}'")
817 6166 : + HelpExampleRpc("getspentinfo", "{\"txid\": \"0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9\", \"index\": 0}")
818 : },
819 6176 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
820 : {
821 10 : UniValue txidValue = request.params[0].get_obj().find_value("txid");
822 10 : UniValue indexValue = request.params[0].get_obj().find_value("index");
823 :
824 10 : if (!txidValue.isStr() || !indexValue.isNum()) {
825 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid txid or index");
826 : }
827 :
828 10 : uint256 txid = ParseHashV(txidValue, "txid");
829 10 : int outputIndex = indexValue.getInt<int>();
830 :
831 10 : if (outputIndex < 0) {
832 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid index (must be non-negative)");
833 : }
834 :
835 10 : if (!g_spentindex) {
836 0 : throw JSONRPCError(RPC_MISC_ERROR, "Spent index is not enabled. Start with -spentindex to enable.");
837 : }
838 :
839 10 : CSpentIndexKey key(txid, outputIndex);
840 10 : CSpentIndexValue value;
841 10 : bool found = false;
842 :
843 : // Sync the index to the current chain tip before querying.
844 : // We don't fail here if not synced — the result may still come from mempool below.
845 10 : g_spentindex->BlockUntilSyncedToCurrentChain();
846 :
847 10 : if (g_spentindex->GetSpentInfo(key, value)) {
848 6 : found = true;
849 6 : }
850 :
851 : // Check mempool for unconfirmed spends.
852 : //
853 : // Mempool entry overwrites index entry if present.
854 : // During reorg, mempool may have newer state than index (which updates asynchronously).
855 10 : CTxMemPool& mempool = EnsureAnyMemPool(request.context);
856 : {
857 10 : LOCK(mempool.cs);
858 10 : CSpentIndexValue mempoolValue;
859 10 : if (mempool.getSpentIndex(key, mempoolValue)) {
860 4 : value = mempoolValue; // Overwrite with mempool entry (current state)
861 4 : found = true;
862 4 : }
863 10 : }
864 :
865 10 : if (!found) {
866 0 : const IndexSummary summary = g_spentindex->GetSummary();
867 0 : if (!summary.synced) {
868 0 : throw JSONRPCError(RPC_MISC_ERROR, strprintf("Unable to get spent info. Spent index is syncing, current height: %d", summary.best_block_height));
869 : }
870 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unable to get spent info");
871 0 : }
872 :
873 10 : UniValue obj(UniValue::VOBJ);
874 10 : obj.pushKV("txid", value.m_tx_hash.GetHex());
875 10 : obj.pushKV("index", (int)value.m_tx_index);
876 10 : obj.pushKV("height", value.m_block_height);
877 :
878 10 : return obj;
879 10 : },
880 : };
881 0 : }
882 :
883 49364 : static RPCHelpMan mockscheduler()
884 : {
885 98728 : return RPCHelpMan{"mockscheduler",
886 49364 : "\nBump the scheduler into the future (-regtest only)\n",
887 98728 : {
888 49364 : {"delta_time", RPCArg::Type::NUM, RPCArg::Optional::NO, "Number of seconds to forward the scheduler into the future." },
889 : },
890 49364 : RPCResult{RPCResult::Type::NONE, "", ""},
891 49364 : RPCExamples{""},
892 92588 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
893 : {
894 43224 : if (!Params().IsMockableChain()) {
895 0 : throw std::runtime_error("mockscheduler is for regression testing (-regtest mode) only");
896 : }
897 :
898 : // check params are valid values
899 43224 : RPCTypeCheck(request.params, {UniValue::VNUM});
900 43224 : int64_t delta_seconds = request.params[0].getInt<int64_t>();
901 43224 : if (delta_seconds <= 0 || delta_seconds > 3600) {
902 0 : throw std::runtime_error("delta_time must be between 1 and 3600 seconds (1 hr)");
903 : }
904 :
905 43224 : auto* node_context = CHECK_NONFATAL(GetContext<NodeContext>(request.context));
906 : // protect against null pointer dereference
907 43224 : CHECK_NONFATAL(node_context->scheduler);
908 43224 : node_context->scheduler->MockForward(std::chrono::seconds(delta_seconds));
909 :
910 43224 : return UniValue::VNULL;
911 0 : },
912 : };
913 0 : }
914 :
915 2 : static UniValue RPCLockedMemoryInfo()
916 : {
917 2 : LockedPool::Stats stats = LockedPoolManager::Instance().stats();
918 2 : UniValue obj(UniValue::VOBJ);
919 2 : obj.pushKV("used", uint64_t(stats.used));
920 2 : obj.pushKV("free", uint64_t(stats.free));
921 2 : obj.pushKV("total", uint64_t(stats.total));
922 2 : obj.pushKV("locked", uint64_t(stats.locked));
923 2 : obj.pushKV("chunks_used", uint64_t(stats.chunks_used));
924 2 : obj.pushKV("chunks_free", uint64_t(stats.chunks_free));
925 2 : return obj;
926 2 : }
927 :
928 : #ifdef HAVE_MALLOC_INFO
929 : static std::string RPCMallocInfo()
930 : {
931 : char *ptr = nullptr;
932 : size_t size = 0;
933 : FILE *f = open_memstream(&ptr, &size);
934 : if (f) {
935 : malloc_info(0, f);
936 : fclose(f);
937 : if (ptr) {
938 : std::string rv(ptr, size);
939 : free(ptr);
940 : return rv;
941 : }
942 : }
943 : return "";
944 : }
945 : #endif
946 :
947 6164 : static RPCHelpMan getmemoryinfo()
948 : {
949 : /* Please, avoid using the word "pool" here in the RPC interface or help,
950 : * as users will undoubtedly confuse it with the other "memory pool"
951 : */
952 12328 : return RPCHelpMan{"getmemoryinfo",
953 6164 : "Returns an object containing information about memory usage.\n",
954 12328 : {
955 6164 : {"mode", RPCArg::Type::STR, RPCArg::Default{"stats"}, "determines what kind of information is returned.\n"
956 : " - \"stats\" returns general statistics about memory usage in the daemon.\n"
957 : " - \"mallocinfo\" returns an XML string describing low-level heap state (only available if compiled with glibc)."},
958 : },
959 18492 : {
960 12328 : RPCResult{"mode \"stats\"",
961 6164 : RPCResult::Type::OBJ, "", "",
962 12328 : {
963 12328 : {RPCResult::Type::OBJ, "locked", "Information about locked memory manager",
964 43148 : {
965 6164 : {RPCResult::Type::NUM, "used", "Number of bytes used"},
966 6164 : {RPCResult::Type::NUM, "free", "Number of bytes available in current arenas"},
967 6164 : {RPCResult::Type::NUM, "total", "Total number of bytes managed"},
968 6164 : {RPCResult::Type::NUM, "locked", "Amount of bytes that succeeded locking. If this number is smaller than total, locking pages failed at some point and key data could be swapped to disk."},
969 6164 : {RPCResult::Type::NUM, "chunks_used", "Number allocated chunks"},
970 6164 : {RPCResult::Type::NUM, "chunks_free", "Number unused chunks"},
971 : }},
972 : }
973 : },
974 12328 : RPCResult{"mode \"mallocinfo\"",
975 6164 : RPCResult::Type::STR, "", "\"<malloc version=\"1\">...\""
976 : },
977 : },
978 6164 : RPCExamples{
979 6164 : HelpExampleCli("getmemoryinfo", "")
980 6164 : + HelpExampleRpc("getmemoryinfo", "")
981 : },
982 6172 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
983 : {
984 :
985 8 : std::string mode = request.params[0].isNull() ? "stats" : request.params[0].get_str();
986 8 : if (mode == "stats") {
987 2 : UniValue obj(UniValue::VOBJ);
988 2 : obj.pushKV("locked", RPCLockedMemoryInfo());
989 2 : return obj;
990 8 : } else if (mode == "mallocinfo") {
991 : #ifdef HAVE_MALLOC_INFO
992 : return RPCMallocInfo();
993 : #else
994 4 : throw JSONRPCError(RPC_INVALID_PARAMETER, "mallocinfo mode not available");
995 : #endif
996 : } else {
997 2 : throw JSONRPCError(RPC_INVALID_PARAMETER, "unknown mode " + mode);
998 : }
999 14 : },
1000 : };
1001 0 : }
1002 :
1003 4 : static void EnableOrDisableLogCategories(UniValue cats, bool enable) {
1004 4 : cats = cats.get_array();
1005 8 : for (unsigned int i = 0; i < cats.size(); ++i) {
1006 4 : std::string cat = cats[i].get_str();
1007 :
1008 : bool success;
1009 4 : if (enable) {
1010 2 : success = LogInstance().EnableCategory(cat);
1011 2 : } else {
1012 2 : success = LogInstance().DisableCategory(cat);
1013 : }
1014 :
1015 4 : if (!success) {
1016 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "unknown logging category " + cat);
1017 : }
1018 4 : }
1019 4 : }
1020 :
1021 6176 : static RPCHelpMan logging()
1022 : {
1023 12352 : return RPCHelpMan{"logging",
1024 : "Gets and sets the logging configuration.\n"
1025 : "When called without an argument, returns the list of categories with status that are currently being debug logged or not.\n"
1026 : "When called with arguments, adds or removes categories from debug logging and return the lists above.\n"
1027 : "The arguments are evaluated in order \"include\", \"exclude\".\n"
1028 : "If an item is both included and excluded, it will thus end up being excluded.\n"
1029 6176 : "The valid logging categories are: " + LogInstance().LogCategoriesString() + "\n"
1030 : "In addition, the following are available as category names with special meanings:\n"
1031 : " - \"all\", \"1\" : represent all logging categories.\n"
1032 : " - \"dash\" activates all Dash-specific categories at once.\n"
1033 : "To deactivate all categories at once you can specify \"all\" in <exclude>.\n"
1034 : " - \"none\", \"0\" : even if other logging categories are specified, ignore all of them.\n"
1035 : ,
1036 18528 : {
1037 12352 : {"include", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "The of categories to add to debug logging",
1038 12352 : {
1039 6176 : {"include_category", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "the valid logging category"},
1040 : }},
1041 12352 : {"exclude", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "The categories to remove from debug logging",
1042 12352 : {
1043 6176 : {"exclude_category", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "the valid logging category"},
1044 : }},
1045 : },
1046 6176 : RPCResult{
1047 6176 : RPCResult::Type::OBJ_DYN, "", "keys are the logging categories, and values indicates its status",
1048 12352 : {
1049 6176 : {RPCResult::Type::BOOL, "category", "if being debug logged or not. false:inactive, true:active"},
1050 : }
1051 : },
1052 6176 : RPCExamples{
1053 6176 : HelpExampleCli("logging", "\"[\\\"all\\\"]\" \"[\\\"http\\\"]\"")
1054 6176 : + HelpExampleCli("logging", "'[\"dash\"]' '[\"llmq\",\"zmq\"]'")
1055 6176 : + HelpExampleRpc("logging", "[\"all\"], \"[libevent]\"")
1056 : },
1057 6194 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
1058 : {
1059 :
1060 18 : uint64_t original_log_categories = LogInstance().GetCategoryMask();
1061 18 : if (request.params[0].isArray()) {
1062 2 : EnableOrDisableLogCategories(request.params[0], true);
1063 2 : }
1064 18 : if (request.params[1].isArray()) {
1065 2 : EnableOrDisableLogCategories(request.params[1], false);
1066 2 : }
1067 18 : uint64_t updated_log_categories = LogInstance().GetCategoryMask();
1068 18 : uint64_t changed_log_categories = original_log_categories ^ updated_log_categories;
1069 :
1070 : // Update libevent logging if BCLog::LIBEVENT has changed.
1071 18 : if (changed_log_categories & BCLog::LIBEVENT) {
1072 0 : UpdateHTTPServerLogging(LogInstance().WillLogCategory(BCLog::LIBEVENT));
1073 0 : }
1074 :
1075 18 : UniValue result(UniValue::VOBJ);
1076 738 : for (const auto& logCatActive : LogInstance().LogCategoriesList()) {
1077 720 : result.pushKV(logCatActive.category, logCatActive.active);
1078 : }
1079 :
1080 18 : return result;
1081 18 : },
1082 : };
1083 0 : }
1084 :
1085 12306 : static RPCHelpMan echo(const std::string& name)
1086 : {
1087 24612 : return RPCHelpMan{name,
1088 12306 : "\nSimply echo back the input arguments. This command is for testing.\n"
1089 : "\nIt will return an internal bug report when arg9='trigger_internal_bug' is passed.\n"
1090 : "\nThe difference between echo and echojson is that echojson has argument conversion enabled in the client-side table in "
1091 : "dash-cli and the GUI. There is no server-side difference.",
1092 135366 : {
1093 12306 : {"arg0", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, ""},
1094 12306 : {"arg1", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, ""},
1095 12306 : {"arg2", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, ""},
1096 12306 : {"arg3", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, ""},
1097 12306 : {"arg4", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, ""},
1098 12306 : {"arg5", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, ""},
1099 12306 : {"arg6", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, ""},
1100 12306 : {"arg7", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, ""},
1101 12306 : {"arg8", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, ""},
1102 12306 : {"arg9", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, ""},
1103 : },
1104 12306 : RPCResult{RPCResult::Type::ANY, "", "Returns whatever was passed in"},
1105 12306 : RPCExamples{""},
1106 12332 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
1107 : {
1108 26 : if (request.params[9].isStr()) {
1109 2 : CHECK_NONFATAL(request.params[9].get_str() != "trigger_internal_bug");
1110 2 : }
1111 :
1112 26 : return request.params;
1113 : },
1114 : };
1115 0 : }
1116 :
1117 6166 : static RPCHelpMan echo() { return echo("echo"); }
1118 6140 : static RPCHelpMan echojson() { return echo("echojson"); }
1119 :
1120 6142 : static RPCHelpMan echoipc()
1121 : {
1122 6142 : return RPCHelpMan{
1123 6142 : "echoipc",
1124 6142 : "\nEcho back the input argument, passing it through a spawned process in a multiprocess build.\n"
1125 : "This command is for testing.\n",
1126 6142 : {{"arg", RPCArg::Type::STR, RPCArg::Optional::NO, "The string to echo",}},
1127 6142 : RPCResult{RPCResult::Type::STR, "echo", "The echoed string."},
1128 12284 : RPCExamples{HelpExampleCli("echo", "\"Hello world\"") +
1129 6142 : HelpExampleRpc("echo", "\"Hello world\"")},
1130 6144 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue {
1131 2 : interfaces::Init& local_init = *EnsureAnyNodeContext(request.context).init;
1132 2 : std::unique_ptr<interfaces::Echo> echo;
1133 2 : if (interfaces::Ipc* ipc = local_init.ipc()) {
1134 : // Spawn a new bitcoin-node process and call makeEcho to get a
1135 : // client pointer to a interfaces::Echo instance running in
1136 : // that process. This is just for testing. A slightly more
1137 : // realistic test spawning a different executable instead of
1138 : // the same executable would add a new bitcoin-echo executable,
1139 : // and spawn bitcoin-echo below instead of bitcoin-node. But
1140 : // using bitcoin-node avoids the need to build and install a
1141 : // new executable just for this one test.
1142 0 : auto init = ipc->spawnProcess("dash-node");
1143 0 : echo = init->makeEcho();
1144 0 : ipc->addCleanup(*echo, [init = init.release()] { delete init; });
1145 0 : } else {
1146 : // IPC support is not available because this is a bitcoind
1147 : // process not a bitcoind-node process, so just create a local
1148 : // interfaces::Echo object and return it so the `echoipc` RPC
1149 : // method will work, and the python test calling `echoipc`
1150 : // can expect the same result.
1151 2 : echo = local_init.makeEcho();
1152 : }
1153 2 : return echo->echo(request.params[0].get_str());
1154 2 : },
1155 : };
1156 0 : }
1157 :
1158 696 : static UniValue SummaryToJSON(const IndexSummary&& summary, std::string index_name)
1159 : {
1160 696 : UniValue ret_summary(UniValue::VOBJ);
1161 696 : if (!index_name.empty() && index_name != summary.name) return ret_summary;
1162 :
1163 624 : UniValue entry(UniValue::VOBJ);
1164 624 : entry.pushKV("synced", summary.synced);
1165 624 : entry.pushKV("best_block_height", summary.best_block_height);
1166 624 : ret_summary.pushKV(summary.name, entry);
1167 624 : return ret_summary;
1168 696 : }
1169 :
1170 6280 : static RPCHelpMan getindexinfo()
1171 : {
1172 12560 : return RPCHelpMan{"getindexinfo",
1173 6280 : "\nReturns the status of one or all available indices currently running in the node.\n",
1174 12560 : {
1175 6280 : {"index_name", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "Filter results for an index with a specific name."},
1176 : },
1177 6280 : RPCResult{
1178 12560 : RPCResult::Type::OBJ_DYN, "", "", {
1179 6280 : {
1180 6280 : RPCResult::Type::OBJ, "name", "The name of the index",
1181 18840 : {
1182 6280 : {RPCResult::Type::BOOL, "synced", "Whether the index is synced or not"},
1183 6280 : {RPCResult::Type::NUM, "best_block_height", "The block height to which the index is synced"},
1184 : }
1185 : },
1186 : },
1187 : },
1188 6280 : RPCExamples{
1189 6280 : HelpExampleCli("getindexinfo", "")
1190 6280 : + HelpExampleRpc("getindexinfo", "")
1191 6280 : + HelpExampleCli("getindexinfo", "txindex")
1192 6280 : + HelpExampleRpc("getindexinfo", "txindex")
1193 : },
1194 6404 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
1195 : {
1196 124 : UniValue result(UniValue::VOBJ);
1197 124 : const std::string index_name = request.params[0].isNull() ? "" : request.params[0].get_str();
1198 :
1199 124 : if (g_txindex) {
1200 122 : result.pushKVs(SummaryToJSON(g_txindex->GetSummary(), index_name));
1201 122 : }
1202 :
1203 124 : if (g_coin_stats_index) {
1204 114 : result.pushKVs(SummaryToJSON(g_coin_stats_index->GetSummary(), index_name));
1205 114 : }
1206 :
1207 242 : ForEachBlockFilterIndex([&result, &index_name](const BlockFilterIndex& index) {
1208 118 : result.pushKVs(SummaryToJSON(index.GetSummary(), index_name));
1209 118 : });
1210 :
1211 124 : if (g_addressindex) {
1212 114 : result.pushKVs(SummaryToJSON(g_addressindex->GetSummary(), index_name));
1213 114 : }
1214 :
1215 124 : if (g_timestampindex) {
1216 114 : result.pushKVs(SummaryToJSON(g_timestampindex->GetSummary(), index_name));
1217 114 : }
1218 :
1219 124 : if (g_spentindex) {
1220 114 : result.pushKVs(SummaryToJSON(g_spentindex->GetSummary(), index_name));
1221 114 : }
1222 :
1223 124 : return result;
1224 124 : },
1225 : };
1226 0 : }
1227 :
1228 3201 : void RegisterNodeRPCCommands(CRPCTable &t)
1229 : {
1230 6270 : static const CRPCCommand commands[] =
1231 61380 : { // category actor (function)
1232 : // --------------------- ------------------------
1233 3069 : { "control", &debug, },
1234 3069 : { "control", &getmemoryinfo, },
1235 3069 : { "control", &logging, },
1236 3069 : { "util", &getindexinfo, },
1237 3069 : { "blockchain", &getspentinfo, },
1238 :
1239 : /* Address index */
1240 3069 : { "addressindex", &getaddressmempool, },
1241 3069 : { "addressindex", &getaddressutxos, },
1242 3069 : { "addressindex", &getaddressdeltas, },
1243 3069 : { "addressindex", &getaddresstxids, },
1244 3069 : { "addressindex", &getaddressbalance, },
1245 :
1246 : /* Dash features */
1247 3069 : { "dash", &mnsync, },
1248 3069 : { "dash", &spork, },
1249 3069 : { "dash", &sporkupdate, },
1250 :
1251 : /* Not shown in help */
1252 3069 : { "hidden", &setmocktime, },
1253 3069 : { "hidden", &mockscheduler, },
1254 3069 : { "hidden", &echo, },
1255 3069 : { "hidden", &echojson, },
1256 3069 : { "hidden", &echoipc, },
1257 3069 : { "hidden", &mnauth, },
1258 : };
1259 : // clang-format on
1260 64020 : for (const auto& c : commands) {
1261 60819 : t.appendCommand(c.name, &c);
1262 : }
1263 3201 : }
|