LCOV - code coverage report
Current view: top level - src/rpc - coinjoin.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 296 334 88.6 %
Date: 2026-06-25 07:23:43 Functions: 24 26 92.3 %

          Line data    Source code
       1             : // Copyright (c) 2019-2025 The Dash Core developers
       2             : // Distributed under the MIT/X11 software license, see the accompanying
       3             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       4             : 
       5             : #include <node/context.h>
       6             : #include <rpc/server.h>
       7             : #include <rpc/server_util.h>
       8             : #include <rpc/util.h>
       9             : #include <util/check.h>
      10             : #include <wallet/receive.h>
      11             : #include <wallet/rpc/util.h>
      12             : #include <walletinitinterface.h>
      13             : 
      14             : #include <active/context.h>
      15             : #include <coinjoin/server.h>
      16             : #include <coinjoin/walletman.h>
      17             : 
      18             : #ifdef ENABLE_WALLET
      19             : #include <coinjoin/options.h>
      20             : #include <interfaces/coinjoin.h>
      21             : #endif // ENABLE_WALLET
      22             : 
      23             : #include <univalue.h>
      24             : 
      25             : using node::NodeContext;
      26             : #ifdef ENABLE_WALLET
      27             : using wallet::CWallet;
      28             : using wallet::GetWalletForJSONRPCRequest;
      29             : using wallet::DEFAULT_DISABLE_WALLET;
      30             : using wallet::WALLET_FLAG_DISABLE_PRIVATE_KEYS;
      31             : #endif // ENABLE_WALLET
      32             : 
      33             : #ifdef ENABLE_WALLET
      34             : namespace {
      35          18 : void ValidateCoinJoinArguments()
      36             : {
      37             :     /* If CoinJoin is enabled, everything is working as expected, we can bail */
      38          18 :     if (CCoinJoinClientOptions::IsEnabled())
      39          18 :         return;
      40             : 
      41             :     /* CoinJoin is on by default, unless a command line argument says otherwise */
      42           0 :     if (!gArgs.GetBoolArg("-enablecoinjoin", true)) {
      43           0 :         throw JSONRPCError(RPC_INTERNAL_ERROR, "Mixing is disabled via -enablecoinjoin=0 command line option, remove it to enable mixing again");
      44             :     }
      45             : 
      46             :     /* Most likely something bad happened and we disabled it while running the wallet */
      47           0 :     throw JSONRPCError(RPC_INTERNAL_ERROR, "Mixing is disabled due to an internal error");
      48           0 : }
      49             : } // anonymous namespace
      50             : 
      51        2888 : static RPCHelpMan coinjoin()
      52             : {
      53        5776 :     return RPCHelpMan{"coinjoin",
      54        2888 :         "\nAvailable commands:\n"
      55             :         "  start       - Start mixing\n"
      56             :         "  status      - Get mixing status\n"
      57             :         "  stop        - Stop mixing\n"
      58             :         "  reset       - Reset mixing",
      59        5776 :         {
      60        2888 :             {"command", RPCArg::Type::STR, RPCArg::Optional::NO, "The command to execute"},
      61             :         },
      62        2888 :         RPCResult{RPCResult::Type::NONE, "", ""},
      63        2888 :         RPCExamples{""},
      64        2888 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
      65             : {
      66           0 :     throw JSONRPCError(RPC_INVALID_PARAMETER, "Must be a valid command");
      67           0 : },
      68             :     };
      69           0 : }
      70             : 
      71        2874 : static RPCHelpMan coinjoin_reset()
      72             : {
      73        5748 :     return RPCHelpMan{"coinjoin reset",
      74        2874 :         "\nReset CoinJoin mixing\n",
      75        2874 :         {},
      76        2874 :         RPCResult{
      77        2874 :             RPCResult::Type::STR, "", "Status of request"
      78             :         },
      79        2874 :         RPCExamples{
      80        2874 :             HelpExampleCli("coinjoin reset", "")
      81        2874 :           + HelpExampleRpc("coinjoin reset", "")
      82             :         },
      83        2876 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
      84             : {
      85           2 :     const std::shared_ptr<const CWallet> wallet = GetWalletForJSONRPCRequest(request);
      86           2 :     if (!wallet) return UniValue::VNULL;
      87             : 
      88           2 :     const NodeContext& node = EnsureAnyNodeContext(request.context);
      89             : 
      90           2 :     if (node.active_ctx) {
      91           0 :         throw JSONRPCError(RPC_INTERNAL_ERROR, "Client-side mixing is not supported on masternodes");
      92             :     }
      93             : 
      94           2 :     ValidateCoinJoinArguments();
      95             : 
      96           2 :     auto cj_clientman = CHECK_NONFATAL(node.coinjoin_loader)->GetClient(wallet->GetName());
      97           2 :     CHECK_NONFATAL(cj_clientman)->resetPool();
      98             : 
      99           2 :     return "Mixing was reset";
     100           2 : },
     101             :     };
     102           0 : }
     103             : 
     104        2878 : static RPCHelpMan coinjoin_start()
     105             : {
     106        5756 :     return RPCHelpMan{"coinjoin start",
     107        2878 :         "\nStart CoinJoin mixing\n"
     108             :         "Wallet must be unlocked for mixing\n",
     109        2878 :         {},
     110        2878 :         RPCResult{
     111        2878 :             RPCResult::Type::STR, "", "Status of request"
     112             :         },
     113        2878 :         RPCExamples{
     114        2878 :             HelpExampleCli("coinjoin start", "")
     115        2878 :           + HelpExampleRpc("coinjoin start", "")
     116             :         },
     117        2884 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     118             : {
     119           6 :     const std::shared_ptr<const CWallet> wallet = GetWalletForJSONRPCRequest(request);
     120           6 :     if (!wallet) return UniValue::VNULL;
     121             : 
     122           6 :     const NodeContext& node = EnsureAnyNodeContext(request.context);
     123             : 
     124           6 :     if (node.active_ctx) {
     125           0 :         throw JSONRPCError(RPC_INTERNAL_ERROR, "Client-side mixing is not supported on masternodes");
     126             :     }
     127             : 
     128           6 :     ValidateCoinJoinArguments();
     129             : 
     130             :     {
     131           6 :         LOCK(wallet->cs_wallet);
     132           6 :         if (wallet->IsLocked(true))
     133           0 :             throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please unlock wallet for mixing with walletpassphrase first.");
     134           6 :     }
     135             : 
     136           6 :     auto cj_clientman = CHECK_NONFATAL(CHECK_NONFATAL(node.coinjoin_loader)->GetClient(wallet->GetName()));
     137           6 :     if (!cj_clientman->startMixing()) {
     138           2 :         throw JSONRPCError(RPC_INTERNAL_ERROR, "Mixing has been started already.");
     139             :     }
     140             : 
     141           4 :     return "Mixing requested";
     142           8 : },
     143             :     };
     144           0 : }
     145             : 
     146        2876 : static RPCHelpMan coinjoin_status()
     147             : {
     148        5752 :     return RPCHelpMan{"coinjoin status",
     149        2876 :         "\nGet status on CoinJoin mixing sessions\n",
     150        2876 :         {},
     151        2876 :         RPCResult{
     152        2876 :             RPCResult::Type::ARR, "", "",
     153        2876 :             {{RPCResult::Type::STR, "", "Status of mixing session"}}},
     154        2876 :         RPCExamples{
     155        2876 :             HelpExampleCli("coinjoin status", "")
     156        2876 :           + HelpExampleRpc("coinjoin status", "")
     157             :         },
     158        2880 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     159             : {
     160           4 :     const std::shared_ptr<const CWallet> wallet = GetWalletForJSONRPCRequest(request);
     161           4 :     if (!wallet) return UniValue::VNULL;
     162             : 
     163           4 :     const NodeContext& node = EnsureAnyNodeContext(request.context);
     164             : 
     165           4 :     if (node.active_ctx) {
     166           0 :         throw JSONRPCError(RPC_INTERNAL_ERROR, "Client-side mixing is not supported on masternodes");
     167             :     }
     168             : 
     169           4 :     ValidateCoinJoinArguments();
     170             : 
     171           4 :     auto cj_clientman = CHECK_NONFATAL(node.coinjoin_loader)->GetClient(wallet->GetName());
     172           4 :     if (!CHECK_NONFATAL(cj_clientman)->isMixing()) {
     173           2 :         throw JSONRPCError(RPC_INTERNAL_ERROR, "No ongoing mix session");
     174             :     }
     175             : 
     176           2 :     UniValue ret(UniValue::VARR);
     177           2 :     for (const auto& str_status : cj_clientman->getSessionStatuses()) {
     178           0 :         ret.push_back(str_status);
     179             :     }
     180           2 :     return ret;
     181           6 : },
     182             :     };
     183           0 : }
     184             : 
     185        2878 : static RPCHelpMan coinjoin_stop()
     186             : {
     187        5756 :     return RPCHelpMan{"coinjoin stop",
     188        2878 :         "\nStop CoinJoin mixing\n",
     189        2878 :         {},
     190        2878 :         RPCResult{
     191        2878 :             RPCResult::Type::STR, "", "Status of request"
     192             :         },
     193        2878 :         RPCExamples{
     194        2878 :             HelpExampleCli("coinjoin stop", "")
     195        2878 :           + HelpExampleRpc("coinjoin stop", "")
     196             :         },
     197        2884 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     198             : {
     199           6 :     const std::shared_ptr<const CWallet> wallet = GetWalletForJSONRPCRequest(request);
     200           6 :     if (!wallet) return UniValue::VNULL;
     201             : 
     202           6 :     const NodeContext& node = EnsureAnyNodeContext(request.context);
     203             : 
     204           6 :     if (node.active_ctx) {
     205           0 :         throw JSONRPCError(RPC_INTERNAL_ERROR, "Client-side mixing is not supported on masternodes");
     206             :     }
     207             : 
     208           6 :     ValidateCoinJoinArguments();
     209             : 
     210           6 :     CHECK_NONFATAL(node.coinjoin_loader);
     211           6 :     auto cj_clientman = node.coinjoin_loader->GetClient(wallet->GetName());
     212             : 
     213           6 :     CHECK_NONFATAL(cj_clientman);
     214           6 :     if (!cj_clientman->isMixing()) {
     215           2 :         throw JSONRPCError(RPC_INTERNAL_ERROR, "No mix session to stop");
     216             :     }
     217           4 :     cj_clientman->stopMixing();
     218             : 
     219           4 :     return "Mixing was stopped";
     220           8 : },
     221             :     };
     222           0 : }
     223             : 
     224        2888 : static RPCHelpMan coinjoinsalt()
     225             : {
     226        5776 :     return RPCHelpMan{"coinjoinsalt",
     227        2888 :         "\nAvailable commands:\n"
     228             :         "  generate  - Generate new CoinJoin salt\n"
     229             :         "  get       - Fetch existing CoinJoin salt\n"
     230             :         "  set       - Set new CoinJoin salt\n",
     231        5776 :         {
     232        2888 :             {"command", RPCArg::Type::STR, RPCArg::Optional::NO, "The command to execute"},
     233             :         },
     234        2888 :         RPCResult{RPCResult::Type::NONE, "", ""},
     235        2888 :         RPCExamples{""},
     236        2888 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     237             : {
     238           0 :     throw JSONRPCError(RPC_INVALID_PARAMETER, "Must be a valid command");
     239           0 : },
     240             :     };
     241           0 : }
     242             : 
     243        2882 : static RPCHelpMan coinjoinsalt_generate()
     244             : {
     245        5764 :     return RPCHelpMan{"coinjoinsalt generate",
     246        2882 :         "\nGenerate new CoinJoin salt and store it in the wallet database\n"
     247             :         "Cannot generate new salt if CoinJoin mixing is in process or wallet has private keys disabled.\n",
     248        5764 :         {
     249        2882 :             {"overwrite", RPCArg::Type::BOOL, RPCArg::Default{false}, "Generate new salt even if there is an existing salt and/or there is CoinJoin balance"},
     250             :         },
     251        2882 :         RPCResult{
     252        2882 :             RPCResult::Type::BOOL, "", "Status of CoinJoin salt generation and commitment"
     253             :         },
     254        2882 :         RPCExamples{
     255        2882 :             HelpExampleCli("coinjoinsalt generate", "")
     256        2882 :           + HelpExampleRpc("coinjoinsalt generate", "")
     257             :         },
     258        2892 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     259             : {
     260          10 :     std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
     261          10 :     if (!wallet) return UniValue::VNULL;
     262             : 
     263          10 :     const auto str_wallet = wallet->GetName();
     264          10 :     if (wallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
     265           2 :         throw JSONRPCError(RPC_INVALID_REQUEST,
     266           2 :                            strprintf("Wallet \"%s\" has private keys disabled, cannot perform CoinJoin!", str_wallet));
     267             :     }
     268             : 
     269           8 :     bool enable_overwrite{false}; // Default value
     270           8 :     if (!request.params[0].isNull()) {
     271           6 :         enable_overwrite = ParseBoolV(request.params[0], "overwrite");
     272           6 :     }
     273             : 
     274           8 :     if (!enable_overwrite && !wallet->GetCoinJoinSalt().IsNull()) {
     275           2 :         throw JSONRPCError(RPC_INVALID_REQUEST,
     276           2 :                            strprintf("Wallet \"%s\" already has set CoinJoin salt!", str_wallet));
     277             :     }
     278             : 
     279           6 :     const NodeContext& node = EnsureAnyNodeContext(request.context);
     280           6 :     if (node.coinjoin_loader != nullptr) {
     281           6 :         auto cj_clientman = node.coinjoin_loader->GetClient(wallet->GetName());
     282           6 :         if (cj_clientman != nullptr && cj_clientman->isMixing()) {
     283           2 :             throw JSONRPCError(RPC_WALLET_ERROR,
     284           2 :                                strprintf("Wallet \"%s\" is currently mixing, cannot change salt!", str_wallet));
     285             :         }
     286           6 :     }
     287             : 
     288           4 :     const auto wallet_balance{GetBalance(*wallet)};
     289           8 :     const bool has_balance{(wallet_balance.m_anonymized
     290           4 :                           + wallet_balance.m_denominated_trusted
     291           4 :                           + wallet_balance.m_denominated_untrusted_pending) > 0};
     292           4 :     if (!enable_overwrite && has_balance) {
     293           0 :         throw JSONRPCError(RPC_WALLET_ERROR,
     294           0 :                            strprintf("Wallet \"%s\" has CoinJoin balance, cannot continue!", str_wallet));
     295             :     }
     296             : 
     297           4 :     if (!wallet->SetCoinJoinSalt(GetRandHash())) {
     298           0 :         throw JSONRPCError(RPC_INVALID_REQUEST,
     299           0 :                            strprintf("Unable to set new CoinJoin salt for wallet \"%s\"!", str_wallet));
     300             :     }
     301             : 
     302           4 :     wallet->ClearCoinJoinRoundsCache();
     303             : 
     304           4 :     return true;
     305          16 : },
     306             :     };
     307           0 : }
     308             : 
     309        2886 : static RPCHelpMan coinjoinsalt_get()
     310             : {
     311        5772 :     return RPCHelpMan{"coinjoinsalt get",
     312        2886 :         "\nFetch existing CoinJoin salt\n"
     313             :         "Cannot fetch salt if wallet has private keys disabled.\n",
     314        2886 :         {},
     315        2886 :         RPCResult{
     316        2886 :             RPCResult::Type::STR_HEX, "", "CoinJoin salt"
     317             :         },
     318        2886 :         RPCExamples{
     319        2886 :             HelpExampleCli("coinjoinsalt get", "")
     320        2886 :           + HelpExampleRpc("coinjoinsalt get", "")
     321             :         },
     322        2900 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     323             : {
     324          14 :     std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
     325          14 :     if (!wallet) return UniValue::VNULL;
     326             : 
     327          14 :     const auto str_wallet = wallet->GetName();
     328          14 :     if (wallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
     329           2 :         throw JSONRPCError(RPC_INVALID_REQUEST,
     330           2 :                            strprintf("Wallet \"%s\" has private keys disabled, cannot perform CoinJoin!", str_wallet));
     331             :     }
     332             : 
     333          12 :     const auto salt{wallet->GetCoinJoinSalt()};
     334          12 :     if (salt.IsNull()) {
     335           0 :         throw JSONRPCError(RPC_WALLET_ERROR,
     336           0 :                            strprintf("Wallet \"%s\" has no CoinJoin salt!", str_wallet));
     337             :     }
     338          12 :     return salt.GetHex();
     339          16 : },
     340             :     };
     341           0 : }
     342             : 
     343        2884 : static RPCHelpMan coinjoinsalt_set()
     344             : {
     345        5768 :     return RPCHelpMan{"coinjoinsalt set",
     346        2884 :         "\nSet new CoinJoin salt\n"
     347             :         "Cannot set salt if CoinJoin mixing is in process or wallet has private keys disabled.\n"
     348             :         "Will overwrite existing salt. The presence of a CoinJoin balance will cause the wallet to rescan.\n",
     349        8652 :         {
     350        2884 :             {"salt", RPCArg::Type::STR, RPCArg::Optional::NO, "Desired CoinJoin salt value for the wallet"},
     351        2884 :             {"overwrite", RPCArg::Type::BOOL, RPCArg::Default{false}, "Overwrite salt even if CoinJoin balance present"},
     352             :         },
     353        2884 :         RPCResult{
     354        2884 :             RPCResult::Type::BOOL, "", "Status of CoinJoin salt change request"
     355             :         },
     356        2884 :         RPCExamples{
     357        2884 :             HelpExampleCli("coinjoinsalt set", "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16")
     358        2884 :           + HelpExampleRpc("coinjoinsalt set", "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16")
     359             :         },
     360        2896 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     361             : {
     362          12 :     std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
     363          12 :     if (!wallet) return UniValue::VNULL;
     364             : 
     365          12 :     const auto salt{ParseHashV(request.params[0], "salt")};
     366          10 :     if (salt == uint256::ZERO) {
     367           2 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid CoinJoin salt value");
     368             :     }
     369             : 
     370           8 :     bool enable_overwrite{false}; // Default value
     371           8 :     if (!request.params[1].isNull()) {
     372           4 :         enable_overwrite = ParseBoolV(request.params[1], "overwrite");
     373           4 :     }
     374             : 
     375           8 :     const auto str_wallet = wallet->GetName();
     376           8 :     if (wallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
     377           2 :         throw JSONRPCError(RPC_INVALID_REQUEST,
     378           2 :                            strprintf("Wallet \"%s\" has private keys disabled, cannot perform CoinJoin!", str_wallet));
     379             :     }
     380             : 
     381           6 :     const NodeContext& node = EnsureAnyNodeContext(request.context);
     382           6 :     if (node.coinjoin_loader != nullptr) {
     383           6 :         auto cj_clientman = node.coinjoin_loader->GetClient(wallet->GetName());
     384           6 :         if (cj_clientman != nullptr && cj_clientman->isMixing()) {
     385           2 :             throw JSONRPCError(RPC_WALLET_ERROR,
     386           2 :                                strprintf("Wallet \"%s\" is currently mixing, cannot change salt!", str_wallet));
     387             :         }
     388           6 :     }
     389             : 
     390           4 :     const auto wallet_balance{GetBalance(*wallet)};
     391           8 :     const bool has_balance{(wallet_balance.m_anonymized
     392           4 :                           + wallet_balance.m_denominated_trusted
     393           4 :                           + wallet_balance.m_denominated_untrusted_pending) > 0};
     394           4 :     if (has_balance && !enable_overwrite) {
     395           0 :         throw JSONRPCError(RPC_WALLET_ERROR,
     396           0 :                            strprintf("Wallet \"%s\" has CoinJoin balance, cannot continue!", str_wallet));
     397             :     }
     398             : 
     399           4 :     if (!wallet->SetCoinJoinSalt(salt)) {
     400           0 :         throw JSONRPCError(RPC_WALLET_ERROR,
     401           0 :                            strprintf("Unable to set new CoinJoin salt for wallet \"%s\"!", str_wallet));
     402             :     }
     403             : 
     404           4 :     wallet->ClearCoinJoinRoundsCache();
     405             : 
     406           4 :     return true;
     407          18 : },
     408             :     };
     409           0 : }
     410             : #endif // ENABLE_WALLET
     411             : 
     412        9050 : static RPCHelpMan getcoinjoininfo()
     413             : {
     414       18100 :             return RPCHelpMan{"getcoinjoininfo",
     415        9050 :                 "Returns an object containing an information about CoinJoin settings and state.\n",
     416        9050 :                 {},
     417       27150 :                 {
     418       18100 :                     RPCResult{"for regular nodes",
     419        9050 :                         RPCResult::Type::OBJ, "", "",
     420      117650 :                         {
     421        9050 :                             {RPCResult::Type::BOOL, "enabled", "Whether mixing functionality is enabled"},
     422        9050 :                             {RPCResult::Type::BOOL, "multisession", "Whether CoinJoin Multisession option is enabled"},
     423        9050 :                             {RPCResult::Type::NUM, "max_sessions", "How many parallel mixing sessions can there be at once"},
     424        9050 :                             {RPCResult::Type::NUM, "max_rounds", "How many rounds to mix"},
     425        9050 :                             {RPCResult::Type::NUM, "max_amount", "Target CoinJoin balance in " + CURRENCY_UNIT + ""},
     426        9050 :                             {RPCResult::Type::NUM, "denoms_goal", "How many inputs of each denominated amount to target"},
     427        9050 :                             {RPCResult::Type::NUM, "denoms_hardcap", "Maximum limit of how many inputs of each denominated amount to create"},
     428        9050 :                             {RPCResult::Type::NUM, "queue_size", "How many queues there are currently on the network"},
     429        9050 :                             {RPCResult::Type::BOOL, "running", "Whether mixing is currently running"},
     430       18100 :                             {RPCResult::Type::ARR, "sessions", "",
     431       18100 :                             {
     432       18100 :                                 {RPCResult::Type::OBJ, "", "",
     433       72400 :                                 {
     434        9050 :                                     {RPCResult::Type::STR_HEX, "protxhash", "The ProTxHash of the masternode"},
     435        9050 :                                     GetRpcResult("outpoint"),
     436        9050 :                                     {RPCResult::Type::STR, "service", "The IP address and port of the masternode (DEPRECATED, returned only if config option -deprecatedrpc=service is passed)"},
     437       18100 :                                     {RPCResult::Type::ARR, "addrs_core_p2p", "Network addresses of the masternode used for protocol P2P",
     438       18100 :                                         {
     439        9050 :                                             {RPCResult::Type::STR, "address", ""},
     440             :                                         }
     441             :                                     },
     442        9050 :                                     {RPCResult::Type::NUM, "denomination", "The denomination of the mixing session in " + CURRENCY_UNIT + ""},
     443        9050 :                                     {RPCResult::Type::STR_HEX, "state", "Current state of the mixing session"},
     444        9050 :                                     {RPCResult::Type::NUM, "entries_count", "The number of entries in the mixing session"},
     445             :                                 }},
     446             :                             }},
     447        9050 :                             {RPCResult::Type::NUM, "keys_left", /*optional=*/true, "How many new keys are left since last automatic backup (if applicable)"},
     448        9050 :                             {RPCResult::Type::STR, "warnings", "Warnings if any"},
     449             :                         }},
     450       18100 :                     RPCResult{"for masternodes",
     451        9050 :                         RPCResult::Type::OBJ, "", "",
     452       45250 :                         {
     453        9050 :                             {RPCResult::Type::NUM, "queue_size", "How many queues there are currently on the network"},
     454        9050 :                             {RPCResult::Type::NUM, "denomination", "The denomination of the mixing session in " + CURRENCY_UNIT + ""},
     455        9050 :                             {RPCResult::Type::STR_HEX, "state", "Current state of the mixing session"},
     456        9050 :                             {RPCResult::Type::NUM, "entries_count", "The number of entries in the mixing session"},
     457             :                         }},
     458             :                 },
     459        9050 :                 RPCExamples{
     460        9050 :                     HelpExampleCli("getcoinjoininfo", "")
     461        9050 :             + HelpExampleRpc("getcoinjoininfo", "")
     462             :                 },
     463        9072 :                 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     464             : {
     465          22 :     UniValue obj(UniValue::VOBJ);
     466             : 
     467          22 :     const NodeContext& node = EnsureAnyNodeContext(request.context);
     468          22 :     if (node.active_ctx) {
     469           0 :         node.active_ctx->GetCJServer().GetJsonInfo(obj);
     470           0 :         return obj;
     471             :     }
     472             : 
     473             : #ifdef ENABLE_WALLET
     474          22 :     CCoinJoinClientOptions::GetJsonInfo(obj);
     475             : 
     476          22 :     if (node.cj_walletman) {
     477          22 :         if (auto queue_size = node.cj_walletman->getQueueSize()) {
     478          22 :             obj.pushKV("queue_size", queue_size.value());
     479          22 :         }
     480          22 :     }
     481             : 
     482          22 :     const std::shared_ptr<const CWallet> wallet = GetWalletForJSONRPCRequest(request);
     483          22 :     if (!wallet) {
     484           0 :         return obj;
     485             :     }
     486             : 
     487          22 :     auto cj_clientman = CHECK_NONFATAL(node.coinjoin_loader)->GetClient(wallet->GetName());
     488          22 :     CHECK_NONFATAL(cj_clientman)->getJsonInfo(obj);
     489             : 
     490          22 :     std::string warning_msg;
     491          22 :     if (wallet->IsLegacy()) {
     492          18 :         obj.pushKV("keys_left", wallet->nKeysLeftSinceAutoBackup);
     493          18 :         if (wallet->nKeysLeftSinceAutoBackup < COINJOIN_KEYS_THRESHOLD_WARNING) {
     494          18 :             warning_msg = "WARNING: keypool is almost depleted!";
     495          18 :         }
     496          18 :     }
     497          22 :     obj.pushKV("warnings", warning_msg);
     498             : #endif // ENABLE_WALLET
     499             : 
     500          22 :     return obj;
     501          22 : },
     502             :     };
     503           0 : }
     504             : 
     505             : #ifdef ENABLE_WALLET
     506        1436 : Span<const CRPCCommand> GetWalletCoinJoinRPCCommands()
     507             : {
     508       15796 :     static const CRPCCommand commands[]{
     509        1436 :         {"dash", &coinjoin},
     510        1436 :         {"dash", &coinjoin_reset},
     511        1436 :         {"dash", &coinjoin_start},
     512        1436 :         {"dash", &coinjoin_status},
     513        1436 :         {"dash", &coinjoin_stop},
     514        1436 :         {"dash", &coinjoinsalt},
     515        1436 :         {"dash", &coinjoinsalt_generate},
     516        1436 :         {"dash", &coinjoinsalt_get},
     517        1436 :         {"dash", &coinjoinsalt_set},
     518        1436 :         {"dash", &getcoinjoininfo},
     519             :     };
     520        1436 :     return commands;
     521           0 : }
     522             : #endif // ENABLE_WALLET
     523             : 
     524        3201 : void RegisterCoinJoinRPCCommands(CRPCTable& t)
     525             : {
     526        6270 :     static const CRPCCommand commands_wallet[]{
     527        3069 :         {"dash", &getcoinjoininfo},
     528             :     };
     529             :     // If we aren't compiling with wallet support, we still need to register RPCs that are
     530             :     // capable of working without wallet support. We have to do this even if wallet support
     531             :     // is compiled in but is disabled at runtime because runtime disablement prohibits
     532             :     // registering wallet RPCs. We still want the reduced functionality RPC to be registered.
     533             :     // TODO: Spin off these hybrid RPCs into dedicated wallet-only and/or wallet-free RPCs
     534             :     //       and get rid of this workaround.
     535        6402 :     if (!g_wallet_init_interface.HasWalletSupport()
     536             : #ifdef ENABLE_WALLET
     537        3201 :         || gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)
     538             : #endif // ENABLE_WALLET
     539             :     ) {
     540        3174 :         for (const auto& command : commands_wallet) {
     541        1587 :             tableRPC.appendCommand(command.name, &command);
     542             :         }
     543        1587 :     }
     544        3201 : }

Generated by: LCOV version 1.16