LCOV - code coverage report
Current view: top level - src/rpc - node.cpp (source / functions) Hit Total Coverage
Test: test_dash_coverage.info Lines: 316 782 40.4 %
Date: 2026-06-25 07:23:51 Functions: 22 50 44.0 %

          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          92 : static RPCHelpMan debug()
      47             : {
      48         184 :     return RPCHelpMan{"debug",
      49             :         "Change debug category on the fly. Specify single category or use '+' to specify many.\n"
      50          92 :         "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         184 :         {
      62          92 :             {"category", RPCArg::Type::STR, RPCArg::Optional::NO, "The name of the debug category to turn on."},
      63             :         },
      64          92 :         RPCResult{
      65          92 :             RPCResult::Type::STR, "result", "\"Debug mode: \" followed by the specified category",
      66             :         },
      67          92 :         RPCExamples {
      68          92 :             HelpExampleCli("debug", "dash")
      69          92 :     + HelpExampleRpc("debug", "dash+net")
      70             :         },
      71          92 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
      72             : {
      73             : 
      74           0 :     std::string strMode = request.params[0].get_str();
      75           0 :     LogInstance().DisableCategory(BCLog::ALL);
      76             : 
      77           0 :     std::vector<std::string> categories = SplitString(strMode, '+');
      78           0 :     if (std::find(categories.begin(), categories.end(), std::string("0")) == categories.end()) {
      79           0 :         for (const auto& cat : categories) {
      80           0 :             LogInstance().EnableCategory(cat);
      81             :         }
      82           0 :     }
      83             : 
      84           0 :     return "Debug mode: " + LogInstance().LogCategoriesString(/*enabled_only=*/true);
      85           0 : },
      86             :     };
      87           0 : }
      88             : 
      89          92 : static RPCHelpMan mnsync()
      90             : {
      91         184 :     return RPCHelpMan{"mnsync",
      92          92 :         "Returns the sync status, updates to the next step or resets it entirely.\n",
      93         184 :         {
      94          92 :             {"mode", RPCArg::Type::STR, RPCArg::Optional::NO, "[status|next|reset]"},
      95             :         },
      96         276 :         {
      97         184 :             RPCResult{"for mode = status",
      98          92 :                 RPCResult::Type::OBJ, "", "",
      99         644 :                 {
     100          92 :                     {RPCResult::Type::NUM, "AssetID", "The asset ID"},
     101          92 :                     {RPCResult::Type::STR, "AssetName", "The asset name"},
     102          92 :                     {RPCResult::Type::NUM, "AssetStartTime", "The asset start time"},
     103          92 :                     {RPCResult::Type::NUM, "Attempt", "The attempt"},
     104          92 :                     {RPCResult::Type::BOOL, "IsBlockchainSynced", "true if the blockchain synced"},
     105          92 :                     {RPCResult::Type::BOOL, "IsSynced", "true if synced"},
     106             :                 }},
     107         184 :             RPCResult{"for mode = next|reset",
     108          92 :                 RPCResult::Type::STR, "", ""},
     109             :         },
     110          92 :         RPCExamples{""},
     111          92 :     [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     112             : {
     113             : 
     114           0 :     std::string strMode = request.params[0].get_str();
     115             : 
     116           0 :     const NodeContext& node = EnsureAnyNodeContext(request.context);
     117           0 :     CHECK_NONFATAL(node.mn_sync);
     118           0 :     auto& mn_sync = *node.mn_sync;
     119             : 
     120           0 :     if(strMode == "status") {
     121           0 :         UniValue objStatus(UniValue::VOBJ);
     122           0 :         objStatus.pushKV("AssetID", mn_sync.GetAssetID());
     123           0 :         objStatus.pushKV("AssetName", mn_sync.GetAssetName());
     124           0 :         objStatus.pushKV("AssetStartTime", mn_sync.GetAssetStartTime());
     125           0 :         objStatus.pushKV("Attempt", mn_sync.GetAttempt());
     126           0 :         objStatus.pushKV("IsBlockchainSynced", mn_sync.IsBlockchainSynced());
     127           0 :         objStatus.pushKV("IsSynced", mn_sync.IsSynced());
     128           0 :         return objStatus;
     129           0 :     }
     130             : 
     131           0 :     if(strMode == "next")
     132             :     {
     133           0 :         mn_sync.SwitchToNextAsset();
     134           0 :         return "sync updated to " + mn_sync.GetAssetName();
     135             :     }
     136             : 
     137           0 :     if(strMode == "reset")
     138             :     {
     139           0 :         mn_sync.Reset(true);
     140           0 :         return "success";
     141             :     }
     142           0 :     return "failure";
     143           0 : },
     144             :     };
     145           0 : }
     146             : 
     147             : /*
     148             :     Used for updating/reading spork settings on the network
     149             : */
     150          92 : static RPCHelpMan spork()
     151             : {
     152             :     // default help, for basic mode
     153         184 :     return RPCHelpMan{"spork",
     154          92 :         "\nShows information about current state of sporks\n",
     155         184 :         {
     156          92 :             {"command", RPCArg::Type::STR, RPCArg::Optional::NO, "'show' to show all current spork values, 'active' to show which sporks are active"},
     157             :         },
     158         276 :         {
     159         184 :             RPCResult{"For 'show'",
     160          92 :                 RPCResult::Type::OBJ_DYN, "", "keys are the sporks, and values indicates its value",
     161         184 :                 {
     162          92 :                     {RPCResult::Type::NUM, "SPORK_NAME", "The value of the specific spork with the name SPORK_NAME"},
     163             :                 }},
     164         184 :             RPCResult{"For 'active'",
     165          92 :                 RPCResult::Type::OBJ_DYN, "", "keys are the sporks, and values indicates its status",
     166         184 :                 {
     167          92 :                     {RPCResult::Type::BOOL, "SPORK_NAME", "'true' for time-based sporks if spork is active and 'false' otherwise"},
     168             :                 }},
     169             :         },
     170          92 :         RPCExamples {
     171          92 :             HelpExampleCli("spork", "show")
     172          92 :             + HelpExampleRpc("spork", "\"show\"")
     173             :         },
     174          92 :     [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     175             : {
     176             : 
     177             :     // basic mode, show info
     178           0 :     std:: string strCommand = request.params[0].get_str();
     179           0 :     const NodeContext& node = EnsureAnyNodeContext(request.context);
     180           0 :     CHECK_NONFATAL(node.sporkman);
     181           0 :     if (strCommand == "show") {
     182           0 :         UniValue ret(UniValue::VOBJ);
     183           0 :         for (const auto& sporkDef : sporkDefs) {
     184           0 :             ret.pushKV(std::string(sporkDef.name), node.sporkman->GetSporkValue(sporkDef.sporkId));
     185             :         }
     186           0 :         return ret;
     187           0 :     } else if(strCommand == "active"){
     188           0 :         UniValue ret(UniValue::VOBJ);
     189           0 :         for (const auto& sporkDef : sporkDefs) {
     190           0 :             ret.pushKV(std::string(sporkDef.name), node.sporkman->IsSporkActive(sporkDef.sporkId));
     191             :         }
     192           0 :         return ret;
     193           0 :     }
     194             : 
     195           0 :     return UniValue::VNULL;
     196           0 : },
     197             :     };
     198           0 : }
     199             : 
     200          92 : static RPCHelpMan sporkupdate()
     201             : {
     202         184 :     return RPCHelpMan{"sporkupdate",
     203          92 :         "\nUpdate the value of the specific spork. Requires \"-sporkkey\" to be set to sign the message.\n",
     204         276 :         {
     205          92 :             {"name", RPCArg::Type::STR, RPCArg::Optional::NO, "The name of the spork to update"},
     206          92 :             {"value", RPCArg::Type::NUM, RPCArg::Optional::NO, "The new desired value of the spork"},
     207             :         },
     208          92 :         RPCResult{
     209          92 :             RPCResult::Type::STR, "result", "\"success\" if spork value was updated or this help otherwise"
     210             :         },
     211          92 :         RPCExamples{
     212          92 :             HelpExampleCli("sporkupdate", "SPORK_2_INSTANTSEND_ENABLED 4070908800")
     213          92 :             + HelpExampleRpc("sporkupdate", "\"SPORK_2_INSTANTSEND_ENABLED\", 4070908800")
     214             :         },
     215          92 :     [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     216             : {
     217             : 
     218             :     // advanced mode, update spork values
     219           0 :     SporkId nSporkID = CSporkManager::GetSporkIDByName(request.params[0].get_str());
     220           0 :     if (nSporkID == SPORK_INVALID) {
     221           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid spork name");
     222             :     }
     223             : 
     224           0 :     const NodeContext& node = EnsureAnyNodeContext(request.context);
     225           0 :     CHECK_NONFATAL(node.sporkman);
     226             : 
     227             :     // SPORK VALUE
     228           0 :     int64_t nValue = request.params[1].getInt<int64_t>();
     229             : 
     230           0 :     auto inv{node.sporkman->UpdateSpork(nSporkID, nValue)};
     231           0 :     if (inv.has_value()) {
     232           0 :         PeerManager& peerman = EnsurePeerman(node);
     233           0 :         peerman.RelayInv(inv.value());
     234           0 :         return "success";
     235             :     }
     236             : 
     237           0 :     return UniValue::VNULL;
     238           0 : },
     239             :     };
     240           0 : }
     241             : 
     242          92 : static RPCHelpMan setmocktime()
     243             : {
     244         184 :     return RPCHelpMan{"setmocktime",
     245          92 :         "\nSet the local time to given timestamp (-regtest only)\n",
     246         184 :         {
     247          92 :             {"timestamp", RPCArg::Type::NUM, RPCArg::Optional::NO, UNIX_EPOCH_TIME + "\n"
     248             :              "Pass 0 to go back to using the system time."},
     249             :         },
     250          92 :         RPCResult{RPCResult::Type::NONE, "", ""},
     251          92 :         RPCExamples{""},
     252          92 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     253             : {
     254             : 
     255           0 :     if (!Params().IsMockableChain()) {
     256           0 :         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           0 :     LOCK(cs_main);
     265             : 
     266           0 :     RPCTypeCheck(request.params, {UniValue::VNUM});
     267           0 :     const int64_t time{request.params[0].getInt<int64_t>()};
     268           0 :     if (time < 0) {
     269           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Mocktime cannot be negative: %s.", time));
     270             :     }
     271           0 :     SetMockTime(time);
     272           0 :     if (auto* node_context = GetContext<NodeContext>(request.context)) {
     273           0 :         for (const auto& chain_client : node_context->chain_clients) {
     274           0 :             chain_client->setMockTime(time);
     275             :         }
     276           0 :     }
     277             : 
     278           0 :     return UniValue::VNULL;
     279           0 : },
     280             :     };
     281           0 : }
     282             : 
     283          92 : static RPCHelpMan mnauth()
     284             : {
     285         184 :     return RPCHelpMan{"mnauth",
     286          92 :         "\nOverride MNAUTH processing results for the specified node with a user provided data (-regtest only).\n",
     287         368 :         {
     288          92 :             {"nodeId", RPCArg::Type::NUM, RPCArg::Optional::NO, "Internal peer id of the node the mock data gets added to."},
     289          92 :             {"proTxHash", RPCArg::Type::STR, RPCArg::Optional::NO, "The authenticated proTxHash as hex string."},
     290          92 :             {"publicKey", RPCArg::Type::STR, RPCArg::Optional::NO, "The authenticated public key as hex string."},
     291             :         },
     292          92 :         RPCResult{
     293          92 :             RPCResult::Type::BOOL, "result", "true, if the node was updated"
     294             :         },
     295          92 :         RPCExamples{""},
     296          92 :     [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     297             : {
     298             : 
     299           0 :     if (!Params().MineBlocksOnDemand())
     300           0 :         throw std::runtime_error("mnauth for regression testing (-regtest mode) only");
     301             : 
     302           0 :     int64_t nodeId = request.params[0].getInt<int64_t>();
     303           0 :     uint256 proTxHash = ParseHashV(request.params[1], "proTxHash");
     304           0 :     if (proTxHash.IsNull()) {
     305           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "proTxHash invalid");
     306             :     }
     307             : 
     308           0 :     CBLSPublicKey publicKey;
     309           0 :     publicKey.SetHexStr(request.params[2].get_str(), /*specificLegacyScheme=*/false);
     310           0 :     if (!publicKey.IsValid()) {
     311           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "publicKey invalid");
     312             :     }
     313             : 
     314           0 :     const NodeContext& node = EnsureAnyNodeContext(request.context);
     315           0 :     bool fSuccess = node.connman->ForNode(nodeId, CConnman::AllNodes, [&](CNode* pNode){
     316           0 :         pNode->SetVerifiedProRegTxHash(proTxHash);
     317           0 :         pNode->SetVerifiedPubKeyHash(publicKey.GetHash());
     318           0 :         return true;
     319             :     });
     320             : 
     321           0 :     return fSuccess;
     322           0 : },
     323             :     };
     324           0 : }
     325             : 
     326           0 : static bool getAddressFromIndex(const AddressType& type, const uint160 &hash, std::string &address)
     327             : {
     328           0 :     if (type == AddressType::P2SH) {
     329           0 :         address = EncodeDestination(ScriptHash(hash));
     330           0 :     } else if (type == AddressType::P2PK_OR_P2PKH) {
     331           0 :         address = EncodeDestination(PKHash(hash));
     332           0 :     } else {
     333           0 :         return false;
     334             :     }
     335           0 :     return true;
     336           0 : }
     337             : 
     338           0 : static bool getIndexKey(const std::string& str, uint160& hashBytes, AddressType& type)
     339             : {
     340           0 :     CTxDestination dest = DecodeDestination(str);
     341           0 :     if (!IsValidDestination(dest)) {
     342           0 :         type = AddressType::UNKNOWN;
     343           0 :         return false;
     344             :     }
     345           0 :     const PKHash *pkhash = std::get_if<PKHash>(&dest);
     346           0 :     const ScriptHash *scriptID = std::get_if<ScriptHash>(&dest);
     347           0 :     type = pkhash ? AddressType::P2PK_OR_P2PKH : AddressType::P2SH;
     348           0 :     hashBytes = pkhash ? uint160(*pkhash) : uint160(*scriptID);
     349           0 :     return true;
     350           0 : }
     351             : 
     352           0 : static bool getAddressesFromParams(const UniValue& params, std::vector<std::pair<uint160, AddressType> > &addresses)
     353             : {
     354           0 :     if (params[0].isStr()) {
     355           0 :         uint160 hashBytes;
     356           0 :         AddressType type{AddressType::UNKNOWN};
     357           0 :         if (!getIndexKey(params[0].get_str(), hashBytes, type)) {
     358           0 :             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
     359             :         }
     360           0 :         addresses.push_back(std::make_pair(hashBytes, type));
     361           0 :     } else if (params[0].isObject()) {
     362             : 
     363           0 :         UniValue addressValues = params[0].get_obj().find_value("addresses");
     364           0 :         if (!addressValues.isArray()) {
     365           0 :             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Addresses is expected to be an array");
     366             :         }
     367             : 
     368           0 :         for (const auto& address : addressValues.getValues()) {
     369           0 :             uint160 hashBytes;
     370           0 :             AddressType type{AddressType::UNKNOWN};
     371           0 :             if (!getIndexKey(address.get_str(), hashBytes, type)) {
     372           0 :                 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
     373             :             }
     374           0 :             addresses.push_back(std::make_pair(hashBytes, type));
     375             :         }
     376           0 :     } else {
     377           0 :         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
     378             :     }
     379             : 
     380           0 :     return true;
     381           0 : }
     382             : 
     383          92 : static RPCHelpMan getaddressmempool()
     384             : {
     385         184 :     return RPCHelpMan{"getaddressmempool",
     386          92 :         "\nReturns all mempool deltas for an address (requires addressindex to be enabled).\n",
     387         184 :         {
     388         184 :             {"addresses", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "",
     389         184 :                 {
     390          92 :                     {"address", RPCArg::Type::STR, RPCArg::Default{""}, "The base58check encoded address"},
     391             :                 },
     392             :             },
     393             :         },
     394          92 :         RPCResult{
     395          92 :             RPCResult::Type::ARR, "", "",
     396         184 :             {
     397         184 :                 {RPCResult::Type::OBJ, "", "",
     398         736 :                 {
     399          92 :                     {RPCResult::Type::STR, "address", "The base58check encoded address"},
     400          92 :                     {RPCResult::Type::STR_HEX, "txid", "The related txid"},
     401          92 :                     {RPCResult::Type::NUM, "index", "The related input or output index"},
     402          92 :                     {RPCResult::Type::NUM, "satoshis", "The difference of duffs"},
     403          92 :                     {RPCResult::Type::NUM_TIME, "timestamp", "The time the transaction entered the mempool (seconds)"},
     404          92 :                     {RPCResult::Type::STR_HEX, "prevtxid", "The previous txid (if spending)"},
     405          92 :                     {RPCResult::Type::NUM, "prevout", "The previous transaction output index (if spending)"},
     406             :                 }},
     407             :             }},
     408          92 :         RPCExamples{
     409          92 :             HelpExampleCli("getaddressmempool", "'{\"addresses\": [\"" + EXAMPLE_ADDRESS[0] + "\"]}'")
     410          92 :     + HelpExampleRpc("getaddressmempool", "{\"addresses\": [\"" + EXAMPLE_ADDRESS[0] + "\"]}")
     411             :         },
     412          92 :     [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     413             : {
     414           0 :     if (!g_addressindex) {
     415           0 :         throw JSONRPCError(RPC_MISC_ERROR, "Address index is not enabled. Start with -addressindex to enable.");
     416             :     }
     417             : 
     418           0 :     CTxMemPool& mempool = EnsureAnyMemPool(request.context);
     419             : 
     420           0 :     std::vector<std::pair<uint160, AddressType>> addresses;
     421           0 :     if (!getAddressesFromParams(request.params, addresses)) {
     422           0 :         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
     423             :     }
     424             : 
     425           0 :     std::vector<CMempoolAddressDeltaKey> input_addresses;
     426           0 :     std::vector<CMempoolAddressDeltaEntry> indexes;
     427           0 :     for (const auto& [hash, type] : addresses) {
     428           0 :         input_addresses.push_back({type, hash});
     429             :     }
     430             : 
     431           0 :     mempool.getAddressIndex(input_addresses, indexes);
     432             : 
     433           0 :     std::sort(indexes.begin(), indexes.end(),
     434           0 :               [](const CMempoolAddressDeltaEntry &a, const CMempoolAddressDeltaEntry &b) {
     435           0 :                     return a.second.m_time < b.second.m_time;
     436             :               });
     437             : 
     438           0 :     UniValue result(UniValue::VARR);
     439             : 
     440           0 :     for (const auto& [mempoolAddressKey, mempoolAddressDelta] : indexes) {
     441           0 :         std::string address;
     442           0 :         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           0 :         UniValue delta(UniValue::VOBJ);
     447           0 :         delta.pushKV("address", address);
     448           0 :         delta.pushKV("txid", mempoolAddressKey.m_tx_hash.GetHex());
     449           0 :         delta.pushKV("index", mempoolAddressKey.m_tx_index);
     450           0 :         delta.pushKV("satoshis", mempoolAddressDelta.m_amount);
     451           0 :         delta.pushKV("timestamp", count_seconds(mempoolAddressDelta.m_time));
     452           0 :         if (mempoolAddressDelta.m_amount < 0) {
     453           0 :             delta.pushKV("prevtxid", mempoolAddressDelta.m_prev_hash.GetHex());
     454           0 :             delta.pushKV("prevout", mempoolAddressDelta.m_prev_out);
     455           0 :         }
     456           0 :         result.push_back(delta);
     457           0 :     }
     458             : 
     459           0 :     return result;
     460           0 : },
     461             :     };
     462           0 : }
     463             : 
     464          92 : static RPCHelpMan getaddressutxos()
     465             : {
     466         184 :     return RPCHelpMan{"getaddressutxos",
     467          92 :         "\nReturns all unspent outputs for an address (requires addressindex to be enabled).\n",
     468         184 :         {
     469         184 :             {"addresses", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "",
     470         184 :                 {
     471          92 :                     {"address", RPCArg::Type::STR, RPCArg::Default{""}, "The base58check encoded address"},
     472             :                 },
     473             :             },
     474             :         },
     475          92 :         RPCResult{
     476          92 :             RPCResult::Type::ARR, "", "",
     477         184 :             {
     478         184 :                 {RPCResult::Type::OBJ, "", "",
     479         644 :                 {
     480          92 :                     {RPCResult::Type::STR, "address", "The address base58check encoded"},
     481          92 :                     {RPCResult::Type::STR_HEX, "txid", "The output txid"},
     482          92 :                     {RPCResult::Type::NUM, "index", "The output index"},
     483          92 :                     {RPCResult::Type::STR_HEX, "script", "The script hex-encoded"},
     484          92 :                     {RPCResult::Type::NUM, "satoshis", "The number of duffs of the output"},
     485          92 :                     {RPCResult::Type::NUM, "height", "The block height"},
     486             :                 }},
     487             :             }},
     488          92 :         RPCExamples{
     489          92 :             HelpExampleCli("getaddressutxos", "'{\"addresses\": [\"" + EXAMPLE_ADDRESS[0] + "\"]}'")
     490          92 :     + HelpExampleRpc("getaddressutxos", "{\"addresses\": [\"" + EXAMPLE_ADDRESS[0] + "\"]}")
     491             :         },
     492          92 :     [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     493             : {
     494           0 :     std::vector<std::pair<uint160, AddressType> > addresses;
     495           0 :     if (!getAddressesFromParams(request.params, addresses)) {
     496           0 :         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
     497             :     }
     498             : 
     499           0 :     if (!g_addressindex) {
     500           0 :         throw JSONRPCError(RPC_MISC_ERROR, "Address index is not enabled. Start with -addressindex to enable.");
     501             :     }
     502             : 
     503           0 :     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           0 :     std::vector<CAddressUnspentIndexEntry> unspentOutputs;
     508             : 
     509           0 :     for (const auto& address : addresses) {
     510           0 :         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           0 :     UniValue result(UniValue::VARR);
     517             : 
     518           0 :     for (const auto& [unspentKey, unspentValue] : unspentOutputs) {
     519           0 :         UniValue output(UniValue::VOBJ);
     520           0 :         std::string address;
     521           0 :         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           0 :         output.pushKV("address", address);
     526           0 :         output.pushKV("txid", unspentKey.m_tx_hash.GetHex());
     527           0 :         output.pushKV("outputIndex", unspentKey.m_tx_index);
     528           0 :         output.pushKV("script", HexStr(unspentValue.m_tx_script));
     529           0 :         output.pushKV("satoshis", unspentValue.m_amount);
     530           0 :         output.pushKV("height", unspentValue.m_block_height);
     531           0 :         result.push_back(output);
     532           0 :     }
     533             : 
     534           0 :     return result;
     535           0 : },
     536             :     };
     537           0 : }
     538             : 
     539          92 : static RPCHelpMan getaddressdeltas()
     540             : {
     541         184 :     return RPCHelpMan{"getaddressdeltas",
     542          92 :         "\nReturns all changes for an address (requires addressindex to be enabled).\n",
     543         184 :         {
     544         184 :             {"addresses", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "",
     545         184 :                 {
     546          92 :                     {"address", RPCArg::Type::STR, RPCArg::Default{""}, "The base58check encoded address"},
     547             :                 },
     548             :             },
     549             :         },
     550          92 :         RPCResult{
     551          92 :             RPCResult::Type::ARR, "", "",
     552         184 :             {
     553         184 :                 {RPCResult::Type::OBJ, "", "",
     554         644 :                 {
     555          92 :                     {RPCResult::Type::NUM, "satoshis", "The difference of duffs"},
     556          92 :                     {RPCResult::Type::STR_HEX, "txid", "The related txid"},
     557          92 :                     {RPCResult::Type::NUM, "index", "The related input or output index"},
     558          92 :                     {RPCResult::Type::NUM, "blockindex", "The related block index"},
     559          92 :                     {RPCResult::Type::NUM, "height", "The block height"},
     560          92 :                     {RPCResult::Type::STR, "address", "The base58check encoded address"},
     561             :                 }},
     562             :             }},
     563          92 :         RPCExamples{
     564          92 :             HelpExampleCli("getaddressdeltas", "'{\"addresses\": [\"" + EXAMPLE_ADDRESS[0] + "\"]}'")
     565          92 :     + HelpExampleRpc("getaddressdeltas", "{\"addresses\": [\"" + EXAMPLE_ADDRESS[0] + "\"]}")
     566             :         },
     567          92 :     [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     568             : {
     569           0 :     UniValue startValue = request.params[0].get_obj().find_value("start");
     570           0 :     UniValue endValue = request.params[0].get_obj().find_value("end");
     571             : 
     572           0 :     int start = 0;
     573           0 :     int end = 0;
     574             : 
     575           0 :     if (startValue.isNum() && endValue.isNum()) {
     576           0 :         start = startValue.getInt<int>();
     577           0 :         end = endValue.getInt<int>();
     578           0 :         if (end < start) {
     579           0 :             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "End value is expected to be greater than start");
     580             :         }
     581           0 :     }
     582             : 
     583           0 :     std::vector<std::pair<uint160, AddressType> > addresses;
     584             : 
     585           0 :     if (!getAddressesFromParams(request.params, addresses)) {
     586           0 :         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
     587             :     }
     588             : 
     589           0 :     std::vector<CAddressIndexEntry> addressIndex;
     590             : 
     591           0 :     if (!g_addressindex) {
     592           0 :         throw JSONRPCError(RPC_MISC_ERROR, "Address index is not enabled. Start with -addressindex to enable.");
     593             :     }
     594             : 
     595           0 :     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           0 :     for (const auto& address : addresses) {
     600           0 :         if (start <= 0 || end <= 0) { start = 0; end = 0; }
     601           0 :         if (!g_addressindex->GetAddressIndex(address.first, address.second,
     602           0 :                                              addressIndex, start, end)) {
     603           0 :             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for address");
     604             :         }
     605             :     }
     606             : 
     607           0 :     UniValue result(UniValue::VARR);
     608             : 
     609           0 :     for (const auto& [indexKey, indexDelta] : addressIndex) {
     610           0 :         std::string address;
     611           0 :         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           0 :         UniValue delta(UniValue::VOBJ);
     616           0 :         delta.pushKV("satoshis", indexDelta);
     617           0 :         delta.pushKV("txid", indexKey.m_tx_hash.GetHex());
     618           0 :         delta.pushKV("index", indexKey.m_tx_index);
     619           0 :         delta.pushKV("blockindex", indexKey.m_block_tx_pos);
     620           0 :         delta.pushKV("height", indexKey.m_block_height);
     621           0 :         delta.pushKV("address", address);
     622           0 :         result.push_back(delta);
     623           0 :     }
     624             : 
     625           0 :     return result;
     626           0 : },
     627             :     };
     628           0 : }
     629             : 
     630          92 : static RPCHelpMan getaddressbalance()
     631             : {
     632         184 :     return RPCHelpMan{"getaddressbalance",
     633          92 :         "\nReturns the balance for an address(es) (requires addressindex to be enabled).\n",
     634         184 :         {
     635         184 :             {"addresses", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "",
     636         184 :                 {
     637          92 :                     {"address", RPCArg::Type::STR, RPCArg::Default{""}, "The base58check encoded address"},
     638             :                 },
     639             :             },
     640             :         },
     641          92 :         RPCResult{
     642          92 :             RPCResult::Type::OBJ, "", "",
     643         460 :                 {
     644          92 :                     {RPCResult::Type::NUM, "balance", "The current total balance in duffs"},
     645          92 :                     {RPCResult::Type::NUM, "balance_immature", "The current immature balance in duffs"},
     646          92 :                     {RPCResult::Type::NUM, "balance_spendable", "The current spendable balance in duffs"},
     647          92 :                     {RPCResult::Type::NUM, "received", "The total number of duffs received (including change)"},
     648             :                 }},
     649          92 :         RPCExamples{
     650          92 :             HelpExampleCli("getaddressbalance", "'{\"addresses\": [\"" + EXAMPLE_ADDRESS[0] + "\"]}'")
     651          92 :     + HelpExampleRpc("getaddressbalance", "{\"addresses\": [\"" + EXAMPLE_ADDRESS[0] + "\"]}")
     652             :         },
     653          92 :     [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     654             : {
     655           0 :     std::vector<std::pair<uint160, AddressType> > addresses;
     656             : 
     657           0 :     if (!getAddressesFromParams(request.params, addresses)) {
     658           0 :         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
     659             :     }
     660             : 
     661           0 :     std::vector<CAddressIndexEntry> addressIndex;
     662             : 
     663           0 :     if (!g_addressindex) {
     664           0 :         throw JSONRPCError(RPC_MISC_ERROR, "Address index is not enabled. Start with -addressindex to enable.");
     665             :     }
     666             : 
     667           0 :     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           0 :         LOCK(::cs_main);
     674           0 :         for (const auto& address : addresses) {
     675           0 :             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           0 :         nHeight = g_addressindex->GetSummary().best_block_height;
     681           0 :     }
     682             : 
     683             : 
     684           0 :     CAmount balance = 0;
     685           0 :     CAmount balance_spendable = 0;
     686           0 :     CAmount balance_immature = 0;
     687           0 :     CAmount received = 0;
     688             : 
     689           0 :     for (const auto& [indexKey, indexDelta] : addressIndex) {
     690           0 :         if (indexDelta > 0) {
     691           0 :             received += indexDelta;
     692           0 :         }
     693           0 :         if (indexKey.m_block_tx_pos == 0 && nHeight - indexKey.m_block_height < COINBASE_MATURITY) {
     694           0 :             balance_immature += indexDelta;
     695           0 :         } else {
     696           0 :             balance_spendable += indexDelta;
     697             :         }
     698           0 :         balance += indexDelta;
     699             :     }
     700             : 
     701           0 :     UniValue result(UniValue::VOBJ);
     702           0 :     result.pushKV("balance", balance);
     703           0 :     result.pushKV("balance_immature", balance_immature);
     704           0 :     result.pushKV("balance_spendable", balance_spendable);
     705           0 :     result.pushKV("received", received);
     706             : 
     707           0 :     return result;
     708             : 
     709           0 : },
     710             :     };
     711           0 : }
     712             : 
     713          92 : static RPCHelpMan getaddresstxids()
     714             : {
     715         184 :     return RPCHelpMan{"getaddresstxids",
     716          92 :         "\nReturns the txids for an address(es) (requires addressindex to be enabled).\n",
     717         184 :         {
     718         184 :             {"addresses", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "",
     719         184 :                 {
     720          92 :                     {"address", RPCArg::Type::STR, RPCArg::Default{""}, "The base58check encoded address"},
     721             :                 },
     722             :             },
     723             :         },
     724          92 :         RPCResult{
     725          92 :             RPCResult::Type::ARR, "", "",
     726          92 :             {{RPCResult::Type::STR_HEX, "transactionid", "The transaction id"}}
     727             :         },
     728          92 :         RPCExamples{
     729          92 :             HelpExampleCli("getaddresstxids", "'{\"addresses\": [\"" + EXAMPLE_ADDRESS[0] + "\"]}'")
     730          92 :     + HelpExampleRpc("getaddresstxids", "{\"addresses\": [\"" + EXAMPLE_ADDRESS[0] + "\"]}")
     731             :         },
     732          92 :     [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     733             : {
     734           0 :     std::vector<std::pair<uint160, AddressType> > addresses;
     735             : 
     736           0 :     if (!getAddressesFromParams(request.params, addresses)) {
     737           0 :         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
     738             :     }
     739             : 
     740           0 :     int start = 0;
     741           0 :     int end = 0;
     742           0 :     if (request.params[0].isObject()) {
     743           0 :         UniValue startValue = request.params[0].get_obj().find_value("start");
     744           0 :         UniValue endValue = request.params[0].get_obj().find_value("end");
     745           0 :         if (startValue.isNum() && endValue.isNum()) {
     746           0 :             start = startValue.getInt<int>();
     747           0 :             end = endValue.getInt<int>();
     748           0 :         }
     749           0 :     }
     750             : 
     751           0 :     std::vector<CAddressIndexEntry> addressIndex;
     752             : 
     753           0 :     if (!g_addressindex) {
     754           0 :         throw JSONRPCError(RPC_MISC_ERROR, "Address index is not enabled. Start with -addressindex to enable.");
     755             :     }
     756             : 
     757           0 :     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           0 :     for (const auto& address : addresses) {
     762           0 :         if (start <= 0 || end <= 0) { start = 0; end = 0; }
     763           0 :         if (!g_addressindex->GetAddressIndex(address.first, address.second,
     764           0 :                                              addressIndex, start, end)) {
     765           0 :             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for address");
     766             :         }
     767             :     }
     768             : 
     769           0 :     std::set<std::pair<int, std::string> > txids;
     770           0 :     UniValue result(UniValue::VARR);
     771             : 
     772           0 :     for (const auto& [indexKey, _]: addressIndex) {
     773           0 :         int height = indexKey.m_block_height;
     774           0 :         std::string txid = indexKey.m_tx_hash.GetHex();
     775             : 
     776           0 :         if (addresses.size() > 1) {
     777           0 :             txids.insert(std::make_pair(height, txid));
     778           0 :         } else {
     779           0 :             if (txids.insert(std::make_pair(height, txid)).second) {
     780           0 :                 result.push_back(txid);
     781           0 :             }
     782             :         }
     783           0 :     }
     784             : 
     785           0 :     if (addresses.size() > 1) {
     786           0 :         for (const auto& tx : txids) {
     787           0 :             result.push_back(tx.second);
     788             :         }
     789           0 :     }
     790             : 
     791           0 :     return result;
     792             : 
     793           0 : },
     794             :     };
     795           0 : }
     796             : 
     797          92 : static RPCHelpMan getspentinfo()
     798             : {
     799         184 :     return RPCHelpMan{"getspentinfo",
     800          92 :         "\nReturns the txid and index where an output is spent.\n",
     801         184 :         {
     802         184 :             {"request", RPCArg::Type::OBJ, RPCArg::Default{UniValue::VOBJ}, "",
     803         276 :                 {
     804          92 :                     {"txid", RPCArg::Type::STR_HEX, RPCArg::Default{""}, "The hex string of the txid"},
     805          92 :                     {"index", RPCArg::Type::NUM, RPCArg::Default{0}, "The start block height"},
     806             :                 },
     807             :             },
     808             :         },
     809          92 :         RPCResult{
     810          92 :             RPCResult::Type::OBJ, "", "",
     811         276 :             {
     812          92 :                 {RPCResult::Type::STR_HEX, "txid", "The transaction id"},
     813          92 :                 {RPCResult::Type::NUM, "index", "The spending input index"},
     814             :             }},
     815          92 :         RPCExamples{
     816          92 :             HelpExampleCli("getspentinfo", "'{\"txid\": \"0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9\", \"index\": 0}'")
     817          92 :     + HelpExampleRpc("getspentinfo", "{\"txid\": \"0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9\", \"index\": 0}")
     818             :         },
     819          92 :     [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     820             : {
     821           0 :     UniValue txidValue = request.params[0].get_obj().find_value("txid");
     822           0 :     UniValue indexValue = request.params[0].get_obj().find_value("index");
     823             : 
     824           0 :     if (!txidValue.isStr() || !indexValue.isNum()) {
     825           0 :         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid txid or index");
     826             :     }
     827             : 
     828           0 :     uint256 txid = ParseHashV(txidValue, "txid");
     829           0 :     int outputIndex = indexValue.getInt<int>();
     830             : 
     831           0 :     if (outputIndex < 0) {
     832           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid index (must be non-negative)");
     833             :     }
     834             : 
     835           0 :     if (!g_spentindex) {
     836           0 :         throw JSONRPCError(RPC_MISC_ERROR, "Spent index is not enabled. Start with -spentindex to enable.");
     837             :     }
     838             : 
     839           0 :     CSpentIndexKey key(txid, outputIndex);
     840           0 :     CSpentIndexValue value;
     841           0 :     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           0 :     g_spentindex->BlockUntilSyncedToCurrentChain();
     846             : 
     847           0 :     if (g_spentindex->GetSpentInfo(key, value)) {
     848           0 :         found = true;
     849           0 :     }
     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           0 :     CTxMemPool& mempool = EnsureAnyMemPool(request.context);
     856             :     {
     857           0 :         LOCK(mempool.cs);
     858           0 :         CSpentIndexValue mempoolValue;
     859           0 :         if (mempool.getSpentIndex(key, mempoolValue)) {
     860           0 :             value = mempoolValue;  // Overwrite with mempool entry (current state)
     861           0 :             found = true;
     862           0 :         }
     863           0 :     }
     864             : 
     865           0 :     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           0 :     UniValue obj(UniValue::VOBJ);
     874           0 :     obj.pushKV("txid", value.m_tx_hash.GetHex());
     875           0 :     obj.pushKV("index", (int)value.m_tx_index);
     876           0 :     obj.pushKV("height", value.m_block_height);
     877             : 
     878           0 :     return obj;
     879           0 : },
     880             :     };
     881           0 : }
     882             : 
     883          92 : static RPCHelpMan mockscheduler()
     884             : {
     885         184 :     return RPCHelpMan{"mockscheduler",
     886          92 :         "\nBump the scheduler into the future (-regtest only)\n",
     887         184 :         {
     888          92 :             {"delta_time", RPCArg::Type::NUM, RPCArg::Optional::NO, "Number of seconds to forward the scheduler into the future." },
     889             :         },
     890          92 :         RPCResult{RPCResult::Type::NONE, "", ""},
     891          92 :         RPCExamples{""},
     892          92 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     893             : {
     894           0 :     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           0 :     RPCTypeCheck(request.params, {UniValue::VNUM});
     900           0 :     int64_t delta_seconds = request.params[0].getInt<int64_t>();
     901           0 :     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           0 :     auto* node_context = CHECK_NONFATAL(GetContext<NodeContext>(request.context));
     906             :     // protect against null pointer dereference
     907           0 :     CHECK_NONFATAL(node_context->scheduler);
     908           0 :     node_context->scheduler->MockForward(std::chrono::seconds(delta_seconds));
     909             : 
     910           0 :     return UniValue::VNULL;
     911           0 : },
     912             :     };
     913           0 : }
     914             : 
     915           0 : static UniValue RPCLockedMemoryInfo()
     916             : {
     917           0 :     LockedPool::Stats stats = LockedPoolManager::Instance().stats();
     918           0 :     UniValue obj(UniValue::VOBJ);
     919           0 :     obj.pushKV("used", uint64_t(stats.used));
     920           0 :     obj.pushKV("free", uint64_t(stats.free));
     921           0 :     obj.pushKV("total", uint64_t(stats.total));
     922           0 :     obj.pushKV("locked", uint64_t(stats.locked));
     923           0 :     obj.pushKV("chunks_used", uint64_t(stats.chunks_used));
     924           0 :     obj.pushKV("chunks_free", uint64_t(stats.chunks_free));
     925           0 :     return obj;
     926           0 : }
     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          92 : 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         184 :     return RPCHelpMan{"getmemoryinfo",
     953          92 :                 "Returns an object containing information about memory usage.\n",
     954         184 :                 {
     955          92 :                     {"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         276 :                 {
     960         184 :                     RPCResult{"mode \"stats\"",
     961          92 :                         RPCResult::Type::OBJ, "", "",
     962         184 :                         {
     963         184 :                             {RPCResult::Type::OBJ, "locked", "Information about locked memory manager",
     964         644 :                             {
     965          92 :                                 {RPCResult::Type::NUM, "used", "Number of bytes used"},
     966          92 :                                 {RPCResult::Type::NUM, "free", "Number of bytes available in current arenas"},
     967          92 :                                 {RPCResult::Type::NUM, "total", "Total number of bytes managed"},
     968          92 :                                 {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          92 :                                 {RPCResult::Type::NUM, "chunks_used", "Number allocated chunks"},
     970          92 :                                 {RPCResult::Type::NUM, "chunks_free", "Number unused chunks"},
     971             :                             }},
     972             :                         }
     973             :                     },
     974         184 :                     RPCResult{"mode \"mallocinfo\"",
     975          92 :                         RPCResult::Type::STR, "", "\"<malloc version=\"1\">...\""
     976             :                     },
     977             :                 },
     978          92 :                 RPCExamples{
     979          92 :                     HelpExampleCli("getmemoryinfo", "")
     980          92 :             + HelpExampleRpc("getmemoryinfo", "")
     981             :                 },
     982          92 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     983             : {
     984             : 
     985           0 :     std::string mode = request.params[0].isNull() ? "stats" : request.params[0].get_str();
     986           0 :     if (mode == "stats") {
     987           0 :         UniValue obj(UniValue::VOBJ);
     988           0 :         obj.pushKV("locked", RPCLockedMemoryInfo());
     989           0 :         return obj;
     990           0 :     } else if (mode == "mallocinfo") {
     991             : #ifdef HAVE_MALLOC_INFO
     992             :         return RPCMallocInfo();
     993             : #else
     994           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "mallocinfo mode not available");
     995             : #endif
     996             :     } else {
     997           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "unknown mode " + mode);
     998             :     }
     999           0 : },
    1000             :     };
    1001           0 : }
    1002             : 
    1003           0 : static void EnableOrDisableLogCategories(UniValue cats, bool enable) {
    1004           0 :     cats = cats.get_array();
    1005           0 :     for (unsigned int i = 0; i < cats.size(); ++i) {
    1006           0 :         std::string cat = cats[i].get_str();
    1007             : 
    1008             :         bool success;
    1009           0 :         if (enable) {
    1010           0 :             success = LogInstance().EnableCategory(cat);
    1011           0 :         } else {
    1012           0 :             success = LogInstance().DisableCategory(cat);
    1013             :         }
    1014             : 
    1015           0 :         if (!success) {
    1016           0 :             throw JSONRPCError(RPC_INVALID_PARAMETER, "unknown logging category " + cat);
    1017             :         }
    1018           0 :     }
    1019           0 : }
    1020             : 
    1021          92 : static RPCHelpMan logging()
    1022             : {
    1023         184 :     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          92 :             "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         276 :                 {
    1037         184 :                     {"include", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "The of categories to add to debug logging",
    1038         184 :                         {
    1039          92 :                             {"include_category", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "the valid logging category"},
    1040             :                         }},
    1041         184 :                     {"exclude", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "The categories to remove from debug logging",
    1042         184 :                         {
    1043          92 :                             {"exclude_category", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "the valid logging category"},
    1044             :                         }},
    1045             :                 },
    1046          92 :                 RPCResult{
    1047          92 :                     RPCResult::Type::OBJ_DYN, "", "keys are the logging categories, and values indicates its status",
    1048         184 :                     {
    1049          92 :                         {RPCResult::Type::BOOL, "category", "if being debug logged or not. false:inactive, true:active"},
    1050             :                     }
    1051             :                 },
    1052          92 :                 RPCExamples{
    1053          92 :                     HelpExampleCli("logging", "\"[\\\"all\\\"]\" \"[\\\"http\\\"]\"")
    1054          92 :             + HelpExampleCli("logging", "'[\"dash\"]' '[\"llmq\",\"zmq\"]'")
    1055          92 :             + HelpExampleRpc("logging", "[\"all\"], \"[libevent]\"")
    1056             :                 },
    1057          92 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
    1058             : {
    1059             : 
    1060           0 :     uint64_t original_log_categories = LogInstance().GetCategoryMask();
    1061           0 :     if (request.params[0].isArray()) {
    1062           0 :         EnableOrDisableLogCategories(request.params[0], true);
    1063           0 :     }
    1064           0 :     if (request.params[1].isArray()) {
    1065           0 :         EnableOrDisableLogCategories(request.params[1], false);
    1066           0 :     }
    1067           0 :     uint64_t updated_log_categories = LogInstance().GetCategoryMask();
    1068           0 :     uint64_t changed_log_categories = original_log_categories ^ updated_log_categories;
    1069             : 
    1070             :     // Update libevent logging if BCLog::LIBEVENT has changed.
    1071           0 :     if (changed_log_categories & BCLog::LIBEVENT) {
    1072           0 :         UpdateHTTPServerLogging(LogInstance().WillLogCategory(BCLog::LIBEVENT));
    1073           0 :     }
    1074             : 
    1075           0 :     UniValue result(UniValue::VOBJ);
    1076           0 :     for (const auto& logCatActive : LogInstance().LogCategoriesList()) {
    1077           0 :         result.pushKV(logCatActive.category, logCatActive.active);
    1078             :     }
    1079             : 
    1080           0 :     return result;
    1081           0 : },
    1082             :     };
    1083           0 : }
    1084             : 
    1085         184 : static RPCHelpMan echo(const std::string& name)
    1086             : {
    1087         368 :     return RPCHelpMan{name,
    1088         184 :                 "\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        2024 :         {
    1093         184 :             {"arg0", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, ""},
    1094         184 :             {"arg1", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, ""},
    1095         184 :             {"arg2", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, ""},
    1096         184 :             {"arg3", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, ""},
    1097         184 :             {"arg4", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, ""},
    1098         184 :             {"arg5", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, ""},
    1099         184 :             {"arg6", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, ""},
    1100         184 :             {"arg7", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, ""},
    1101         184 :             {"arg8", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, ""},
    1102         184 :             {"arg9", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, ""},
    1103             :         },
    1104         184 :                 RPCResult{RPCResult::Type::ANY, "", "Returns whatever was passed in"},
    1105         184 :                 RPCExamples{""},
    1106         184 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
    1107             : {
    1108           0 :     if (request.params[9].isStr()) {
    1109           0 :         CHECK_NONFATAL(request.params[9].get_str() != "trigger_internal_bug");
    1110           0 :     }
    1111             : 
    1112           0 :     return request.params;
    1113             : },
    1114             :     };
    1115           0 : }
    1116             : 
    1117          92 : static RPCHelpMan echo() { return echo("echo"); }
    1118          92 : static RPCHelpMan echojson() { return echo("echojson"); }
    1119             : 
    1120          92 : static RPCHelpMan echoipc()
    1121             : {
    1122          92 :     return RPCHelpMan{
    1123          92 :         "echoipc",
    1124          92 :         "\nEcho back the input argument, passing it through a spawned process in a multiprocess build.\n"
    1125             :         "This command is for testing.\n",
    1126          92 :         {{"arg", RPCArg::Type::STR, RPCArg::Optional::NO, "The string to echo",}},
    1127          92 :         RPCResult{RPCResult::Type::STR, "echo", "The echoed string."},
    1128         184 :         RPCExamples{HelpExampleCli("echo", "\"Hello world\"") +
    1129          92 :                     HelpExampleRpc("echo", "\"Hello world\"")},
    1130          92 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue {
    1131           0 :             interfaces::Init& local_init = *EnsureAnyNodeContext(request.context).init;
    1132           0 :             std::unique_ptr<interfaces::Echo> echo;
    1133           0 :             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           0 :                 echo = local_init.makeEcho();
    1152             :             }
    1153           0 :             return echo->echo(request.params[0].get_str());
    1154           0 :         },
    1155             :     };
    1156           0 : }
    1157             : 
    1158           0 : static UniValue SummaryToJSON(const IndexSummary&& summary, std::string index_name)
    1159             : {
    1160           0 :     UniValue ret_summary(UniValue::VOBJ);
    1161           0 :     if (!index_name.empty() && index_name != summary.name) return ret_summary;
    1162             : 
    1163           0 :     UniValue entry(UniValue::VOBJ);
    1164           0 :     entry.pushKV("synced", summary.synced);
    1165           0 :     entry.pushKV("best_block_height", summary.best_block_height);
    1166           0 :     ret_summary.pushKV(summary.name, entry);
    1167           0 :     return ret_summary;
    1168           0 : }
    1169             : 
    1170          92 : static RPCHelpMan getindexinfo()
    1171             : {
    1172         184 :     return RPCHelpMan{"getindexinfo",
    1173          92 :                 "\nReturns the status of one or all available indices currently running in the node.\n",
    1174         184 :                 {
    1175          92 :                     {"index_name", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "Filter results for an index with a specific name."},
    1176             :                 },
    1177          92 :                 RPCResult{
    1178         184 :                     RPCResult::Type::OBJ_DYN, "", "", {
    1179          92 :                         {
    1180          92 :                             RPCResult::Type::OBJ, "name", "The name of the index",
    1181         276 :                             {
    1182          92 :                                 {RPCResult::Type::BOOL, "synced", "Whether the index is synced or not"},
    1183          92 :                                 {RPCResult::Type::NUM, "best_block_height", "The block height to which the index is synced"},
    1184             :                             }
    1185             :                         },
    1186             :                     },
    1187             :                 },
    1188          92 :                 RPCExamples{
    1189          92 :                     HelpExampleCli("getindexinfo", "")
    1190          92 :                   + HelpExampleRpc("getindexinfo", "")
    1191          92 :                   + HelpExampleCli("getindexinfo", "txindex")
    1192          92 :                   + HelpExampleRpc("getindexinfo", "txindex")
    1193             :                 },
    1194          92 :                 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
    1195             : {
    1196           0 :     UniValue result(UniValue::VOBJ);
    1197           0 :     const std::string index_name = request.params[0].isNull() ? "" : request.params[0].get_str();
    1198             : 
    1199           0 :     if (g_txindex) {
    1200           0 :         result.pushKVs(SummaryToJSON(g_txindex->GetSummary(), index_name));
    1201           0 :     }
    1202             : 
    1203           0 :     if (g_coin_stats_index) {
    1204           0 :         result.pushKVs(SummaryToJSON(g_coin_stats_index->GetSummary(), index_name));
    1205           0 :     }
    1206             : 
    1207           0 :     ForEachBlockFilterIndex([&result, &index_name](const BlockFilterIndex& index) {
    1208           0 :         result.pushKVs(SummaryToJSON(index.GetSummary(), index_name));
    1209           0 :     });
    1210             : 
    1211           0 :     if (g_addressindex) {
    1212           0 :         result.pushKVs(SummaryToJSON(g_addressindex->GetSummary(), index_name));
    1213           0 :     }
    1214             : 
    1215           0 :     if (g_timestampindex) {
    1216           0 :         result.pushKVs(SummaryToJSON(g_timestampindex->GetSummary(), index_name));
    1217           0 :     }
    1218             : 
    1219           0 :     if (g_spentindex) {
    1220           0 :         result.pushKVs(SummaryToJSON(g_spentindex->GetSummary(), index_name));
    1221           0 :     }
    1222             : 
    1223           0 :     return result;
    1224           0 : },
    1225             :     };
    1226           0 : }
    1227             : 
    1228         178 : void RegisterNodeRPCCommands(CRPCTable &t)
    1229             : {
    1230         224 : static const CRPCCommand commands[] =
    1231         920 : { //  category              actor (function)
    1232             :   //  --------------------- ------------------------
    1233          46 :     { "control",            &debug,                   },
    1234          46 :     { "control",            &getmemoryinfo,           },
    1235          46 :     { "control",            &logging,                 },
    1236          46 :     { "util",               &getindexinfo,            },
    1237          46 :     { "blockchain",         &getspentinfo,            },
    1238             : 
    1239             :     /* Address index */
    1240          46 :     { "addressindex",       &getaddressmempool,       },
    1241          46 :     { "addressindex",       &getaddressutxos,         },
    1242          46 :     { "addressindex",       &getaddressdeltas,        },
    1243          46 :     { "addressindex",       &getaddresstxids,         },
    1244          46 :     { "addressindex",       &getaddressbalance,       },
    1245             : 
    1246             :     /* Dash features */
    1247          46 :     { "dash",               &mnsync,                  },
    1248          46 :     { "dash",               &spork,                   },
    1249          46 :     { "dash",               &sporkupdate,             },
    1250             : 
    1251             :     /* Not shown in help */
    1252          46 :     { "hidden",             &setmocktime,             },
    1253          46 :     { "hidden",             &mockscheduler,           },
    1254          46 :     { "hidden",             &echo,                    },
    1255          46 :     { "hidden",             &echojson,                },
    1256          46 :     { "hidden",             &echoipc,                 },
    1257          46 :     { "hidden",             &mnauth,                  },
    1258             : };
    1259             : // clang-format on
    1260        3560 :     for (const auto& c : commands) {
    1261        3382 :         t.appendCommand(c.name, &c);
    1262             :     }
    1263         178 : }

Generated by: LCOV version 1.16