LCOV - code coverage report
Current view: top level - src/rpc - node.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 711 782 90.9 %
Date: 2026-06-25 07:23:43 Functions: 49 50 98.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        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 : }

Generated by: LCOV version 1.16