LCOV - code coverage report
Current view: top level - src/rpc - coinjoin.cpp (source / functions) Hit Total Coverage
Test: test_dash_coverage.info Lines: 49 334 14.7 %
Date: 2026-06-25 07:23:51 Functions: 5 26 19.2 %

          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           0 : void ValidateCoinJoinArguments()
      36             : {
      37             :     /* If CoinJoin is enabled, everything is working as expected, we can bail */
      38           0 :     if (CCoinJoinClientOptions::IsEnabled())
      39           0 :         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           0 : static RPCHelpMan coinjoin()
      52             : {
      53           0 :     return RPCHelpMan{"coinjoin",
      54           0 :         "\nAvailable commands:\n"
      55             :         "  start       - Start mixing\n"
      56             :         "  status      - Get mixing status\n"
      57             :         "  stop        - Stop mixing\n"
      58             :         "  reset       - Reset mixing",
      59           0 :         {
      60           0 :             {"command", RPCArg::Type::STR, RPCArg::Optional::NO, "The command to execute"},
      61             :         },
      62           0 :         RPCResult{RPCResult::Type::NONE, "", ""},
      63           0 :         RPCExamples{""},
      64           0 :         [&](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           0 : static RPCHelpMan coinjoin_reset()
      72             : {
      73           0 :     return RPCHelpMan{"coinjoin reset",
      74           0 :         "\nReset CoinJoin mixing\n",
      75           0 :         {},
      76           0 :         RPCResult{
      77           0 :             RPCResult::Type::STR, "", "Status of request"
      78             :         },
      79           0 :         RPCExamples{
      80           0 :             HelpExampleCli("coinjoin reset", "")
      81           0 :           + HelpExampleRpc("coinjoin reset", "")
      82             :         },
      83           0 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
      84             : {
      85           0 :     const std::shared_ptr<const CWallet> wallet = GetWalletForJSONRPCRequest(request);
      86           0 :     if (!wallet) return UniValue::VNULL;
      87             : 
      88           0 :     const NodeContext& node = EnsureAnyNodeContext(request.context);
      89             : 
      90           0 :     if (node.active_ctx) {
      91           0 :         throw JSONRPCError(RPC_INTERNAL_ERROR, "Client-side mixing is not supported on masternodes");
      92             :     }
      93             : 
      94           0 :     ValidateCoinJoinArguments();
      95             : 
      96           0 :     auto cj_clientman = CHECK_NONFATAL(node.coinjoin_loader)->GetClient(wallet->GetName());
      97           0 :     CHECK_NONFATAL(cj_clientman)->resetPool();
      98             : 
      99           0 :     return "Mixing was reset";
     100           0 : },
     101             :     };
     102           0 : }
     103             : 
     104           0 : static RPCHelpMan coinjoin_start()
     105             : {
     106           0 :     return RPCHelpMan{"coinjoin start",
     107           0 :         "\nStart CoinJoin mixing\n"
     108             :         "Wallet must be unlocked for mixing\n",
     109           0 :         {},
     110           0 :         RPCResult{
     111           0 :             RPCResult::Type::STR, "", "Status of request"
     112             :         },
     113           0 :         RPCExamples{
     114           0 :             HelpExampleCli("coinjoin start", "")
     115           0 :           + HelpExampleRpc("coinjoin start", "")
     116             :         },
     117           0 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     118             : {
     119           0 :     const std::shared_ptr<const CWallet> wallet = GetWalletForJSONRPCRequest(request);
     120           0 :     if (!wallet) return UniValue::VNULL;
     121             : 
     122           0 :     const NodeContext& node = EnsureAnyNodeContext(request.context);
     123             : 
     124           0 :     if (node.active_ctx) {
     125           0 :         throw JSONRPCError(RPC_INTERNAL_ERROR, "Client-side mixing is not supported on masternodes");
     126             :     }
     127             : 
     128           0 :     ValidateCoinJoinArguments();
     129             : 
     130             :     {
     131           0 :         LOCK(wallet->cs_wallet);
     132           0 :         if (wallet->IsLocked(true))
     133           0 :             throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please unlock wallet for mixing with walletpassphrase first.");
     134           0 :     }
     135             : 
     136           0 :     auto cj_clientman = CHECK_NONFATAL(CHECK_NONFATAL(node.coinjoin_loader)->GetClient(wallet->GetName()));
     137           0 :     if (!cj_clientman->startMixing()) {
     138           0 :         throw JSONRPCError(RPC_INTERNAL_ERROR, "Mixing has been started already.");
     139             :     }
     140             : 
     141           0 :     return "Mixing requested";
     142           0 : },
     143             :     };
     144           0 : }
     145             : 
     146           0 : static RPCHelpMan coinjoin_status()
     147             : {
     148           0 :     return RPCHelpMan{"coinjoin status",
     149           0 :         "\nGet status on CoinJoin mixing sessions\n",
     150           0 :         {},
     151           0 :         RPCResult{
     152           0 :             RPCResult::Type::ARR, "", "",
     153           0 :             {{RPCResult::Type::STR, "", "Status of mixing session"}}},
     154           0 :         RPCExamples{
     155           0 :             HelpExampleCli("coinjoin status", "")
     156           0 :           + HelpExampleRpc("coinjoin status", "")
     157             :         },
     158           0 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     159             : {
     160           0 :     const std::shared_ptr<const CWallet> wallet = GetWalletForJSONRPCRequest(request);
     161           0 :     if (!wallet) return UniValue::VNULL;
     162             : 
     163           0 :     const NodeContext& node = EnsureAnyNodeContext(request.context);
     164             : 
     165           0 :     if (node.active_ctx) {
     166           0 :         throw JSONRPCError(RPC_INTERNAL_ERROR, "Client-side mixing is not supported on masternodes");
     167             :     }
     168             : 
     169           0 :     ValidateCoinJoinArguments();
     170             : 
     171           0 :     auto cj_clientman = CHECK_NONFATAL(node.coinjoin_loader)->GetClient(wallet->GetName());
     172           0 :     if (!CHECK_NONFATAL(cj_clientman)->isMixing()) {
     173           0 :         throw JSONRPCError(RPC_INTERNAL_ERROR, "No ongoing mix session");
     174             :     }
     175             : 
     176           0 :     UniValue ret(UniValue::VARR);
     177           0 :     for (const auto& str_status : cj_clientman->getSessionStatuses()) {
     178           0 :         ret.push_back(str_status);
     179             :     }
     180           0 :     return ret;
     181           0 : },
     182             :     };
     183           0 : }
     184             : 
     185           0 : static RPCHelpMan coinjoin_stop()
     186             : {
     187           0 :     return RPCHelpMan{"coinjoin stop",
     188           0 :         "\nStop CoinJoin mixing\n",
     189           0 :         {},
     190           0 :         RPCResult{
     191           0 :             RPCResult::Type::STR, "", "Status of request"
     192             :         },
     193           0 :         RPCExamples{
     194           0 :             HelpExampleCli("coinjoin stop", "")
     195           0 :           + HelpExampleRpc("coinjoin stop", "")
     196             :         },
     197           0 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     198             : {
     199           0 :     const std::shared_ptr<const CWallet> wallet = GetWalletForJSONRPCRequest(request);
     200           0 :     if (!wallet) return UniValue::VNULL;
     201             : 
     202           0 :     const NodeContext& node = EnsureAnyNodeContext(request.context);
     203             : 
     204           0 :     if (node.active_ctx) {
     205           0 :         throw JSONRPCError(RPC_INTERNAL_ERROR, "Client-side mixing is not supported on masternodes");
     206             :     }
     207             : 
     208           0 :     ValidateCoinJoinArguments();
     209             : 
     210           0 :     CHECK_NONFATAL(node.coinjoin_loader);
     211           0 :     auto cj_clientman = node.coinjoin_loader->GetClient(wallet->GetName());
     212             : 
     213           0 :     CHECK_NONFATAL(cj_clientman);
     214           0 :     if (!cj_clientman->isMixing()) {
     215           0 :         throw JSONRPCError(RPC_INTERNAL_ERROR, "No mix session to stop");
     216             :     }
     217           0 :     cj_clientman->stopMixing();
     218             : 
     219           0 :     return "Mixing was stopped";
     220           0 : },
     221             :     };
     222           0 : }
     223             : 
     224           0 : static RPCHelpMan coinjoinsalt()
     225             : {
     226           0 :     return RPCHelpMan{"coinjoinsalt",
     227           0 :         "\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           0 :         {
     232           0 :             {"command", RPCArg::Type::STR, RPCArg::Optional::NO, "The command to execute"},
     233             :         },
     234           0 :         RPCResult{RPCResult::Type::NONE, "", ""},
     235           0 :         RPCExamples{""},
     236           0 :         [&](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           0 : static RPCHelpMan coinjoinsalt_generate()
     244             : {
     245           0 :     return RPCHelpMan{"coinjoinsalt generate",
     246           0 :         "\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           0 :         {
     249           0 :             {"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           0 :         RPCResult{
     252           0 :             RPCResult::Type::BOOL, "", "Status of CoinJoin salt generation and commitment"
     253             :         },
     254           0 :         RPCExamples{
     255           0 :             HelpExampleCli("coinjoinsalt generate", "")
     256           0 :           + HelpExampleRpc("coinjoinsalt generate", "")
     257             :         },
     258           0 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     259             : {
     260           0 :     std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
     261           0 :     if (!wallet) return UniValue::VNULL;
     262             : 
     263           0 :     const auto str_wallet = wallet->GetName();
     264           0 :     if (wallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
     265           0 :         throw JSONRPCError(RPC_INVALID_REQUEST,
     266           0 :                            strprintf("Wallet \"%s\" has private keys disabled, cannot perform CoinJoin!", str_wallet));
     267             :     }
     268             : 
     269           0 :     bool enable_overwrite{false}; // Default value
     270           0 :     if (!request.params[0].isNull()) {
     271           0 :         enable_overwrite = ParseBoolV(request.params[0], "overwrite");
     272           0 :     }
     273             : 
     274           0 :     if (!enable_overwrite && !wallet->GetCoinJoinSalt().IsNull()) {
     275           0 :         throw JSONRPCError(RPC_INVALID_REQUEST,
     276           0 :                            strprintf("Wallet \"%s\" already has set CoinJoin salt!", str_wallet));
     277             :     }
     278             : 
     279           0 :     const NodeContext& node = EnsureAnyNodeContext(request.context);
     280           0 :     if (node.coinjoin_loader != nullptr) {
     281           0 :         auto cj_clientman = node.coinjoin_loader->GetClient(wallet->GetName());
     282           0 :         if (cj_clientman != nullptr && cj_clientman->isMixing()) {
     283           0 :             throw JSONRPCError(RPC_WALLET_ERROR,
     284           0 :                                strprintf("Wallet \"%s\" is currently mixing, cannot change salt!", str_wallet));
     285             :         }
     286           0 :     }
     287             : 
     288           0 :     const auto wallet_balance{GetBalance(*wallet)};
     289           0 :     const bool has_balance{(wallet_balance.m_anonymized
     290           0 :                           + wallet_balance.m_denominated_trusted
     291           0 :                           + wallet_balance.m_denominated_untrusted_pending) > 0};
     292           0 :     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           0 :     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           0 :     wallet->ClearCoinJoinRoundsCache();
     303             : 
     304           0 :     return true;
     305           0 : },
     306             :     };
     307           0 : }
     308             : 
     309           0 : static RPCHelpMan coinjoinsalt_get()
     310             : {
     311           0 :     return RPCHelpMan{"coinjoinsalt get",
     312           0 :         "\nFetch existing CoinJoin salt\n"
     313             :         "Cannot fetch salt if wallet has private keys disabled.\n",
     314           0 :         {},
     315           0 :         RPCResult{
     316           0 :             RPCResult::Type::STR_HEX, "", "CoinJoin salt"
     317             :         },
     318           0 :         RPCExamples{
     319           0 :             HelpExampleCli("coinjoinsalt get", "")
     320           0 :           + HelpExampleRpc("coinjoinsalt get", "")
     321             :         },
     322           0 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     323             : {
     324           0 :     std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
     325           0 :     if (!wallet) return UniValue::VNULL;
     326             : 
     327           0 :     const auto str_wallet = wallet->GetName();
     328           0 :     if (wallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
     329           0 :         throw JSONRPCError(RPC_INVALID_REQUEST,
     330           0 :                            strprintf("Wallet \"%s\" has private keys disabled, cannot perform CoinJoin!", str_wallet));
     331             :     }
     332             : 
     333           0 :     const auto salt{wallet->GetCoinJoinSalt()};
     334           0 :     if (salt.IsNull()) {
     335           0 :         throw JSONRPCError(RPC_WALLET_ERROR,
     336           0 :                            strprintf("Wallet \"%s\" has no CoinJoin salt!", str_wallet));
     337             :     }
     338           0 :     return salt.GetHex();
     339           0 : },
     340             :     };
     341           0 : }
     342             : 
     343           0 : static RPCHelpMan coinjoinsalt_set()
     344             : {
     345           0 :     return RPCHelpMan{"coinjoinsalt set",
     346           0 :         "\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           0 :         {
     350           0 :             {"salt", RPCArg::Type::STR, RPCArg::Optional::NO, "Desired CoinJoin salt value for the wallet"},
     351           0 :             {"overwrite", RPCArg::Type::BOOL, RPCArg::Default{false}, "Overwrite salt even if CoinJoin balance present"},
     352             :         },
     353           0 :         RPCResult{
     354           0 :             RPCResult::Type::BOOL, "", "Status of CoinJoin salt change request"
     355             :         },
     356           0 :         RPCExamples{
     357           0 :             HelpExampleCli("coinjoinsalt set", "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16")
     358           0 :           + HelpExampleRpc("coinjoinsalt set", "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16")
     359             :         },
     360           0 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     361             : {
     362           0 :     std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
     363           0 :     if (!wallet) return UniValue::VNULL;
     364             : 
     365           0 :     const auto salt{ParseHashV(request.params[0], "salt")};
     366           0 :     if (salt == uint256::ZERO) {
     367           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid CoinJoin salt value");
     368             :     }
     369             : 
     370           0 :     bool enable_overwrite{false}; // Default value
     371           0 :     if (!request.params[1].isNull()) {
     372           0 :         enable_overwrite = ParseBoolV(request.params[1], "overwrite");
     373           0 :     }
     374             : 
     375           0 :     const auto str_wallet = wallet->GetName();
     376           0 :     if (wallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
     377           0 :         throw JSONRPCError(RPC_INVALID_REQUEST,
     378           0 :                            strprintf("Wallet \"%s\" has private keys disabled, cannot perform CoinJoin!", str_wallet));
     379             :     }
     380             : 
     381           0 :     const NodeContext& node = EnsureAnyNodeContext(request.context);
     382           0 :     if (node.coinjoin_loader != nullptr) {
     383           0 :         auto cj_clientman = node.coinjoin_loader->GetClient(wallet->GetName());
     384           0 :         if (cj_clientman != nullptr && cj_clientman->isMixing()) {
     385           0 :             throw JSONRPCError(RPC_WALLET_ERROR,
     386           0 :                                strprintf("Wallet \"%s\" is currently mixing, cannot change salt!", str_wallet));
     387             :         }
     388           0 :     }
     389             : 
     390           0 :     const auto wallet_balance{GetBalance(*wallet)};
     391           0 :     const bool has_balance{(wallet_balance.m_anonymized
     392           0 :                           + wallet_balance.m_denominated_trusted
     393           0 :                           + wallet_balance.m_denominated_untrusted_pending) > 0};
     394           0 :     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           0 :     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           0 :     wallet->ClearCoinJoinRoundsCache();
     405             : 
     406           0 :     return true;
     407           0 : },
     408             :     };
     409           0 : }
     410             : #endif // ENABLE_WALLET
     411             : 
     412          92 : static RPCHelpMan getcoinjoininfo()
     413             : {
     414         184 :             return RPCHelpMan{"getcoinjoininfo",
     415          92 :                 "Returns an object containing an information about CoinJoin settings and state.\n",
     416          92 :                 {},
     417         276 :                 {
     418         184 :                     RPCResult{"for regular nodes",
     419          92 :                         RPCResult::Type::OBJ, "", "",
     420        1196 :                         {
     421          92 :                             {RPCResult::Type::BOOL, "enabled", "Whether mixing functionality is enabled"},
     422          92 :                             {RPCResult::Type::BOOL, "multisession", "Whether CoinJoin Multisession option is enabled"},
     423          92 :                             {RPCResult::Type::NUM, "max_sessions", "How many parallel mixing sessions can there be at once"},
     424          92 :                             {RPCResult::Type::NUM, "max_rounds", "How many rounds to mix"},
     425          92 :                             {RPCResult::Type::NUM, "max_amount", "Target CoinJoin balance in " + CURRENCY_UNIT + ""},
     426          92 :                             {RPCResult::Type::NUM, "denoms_goal", "How many inputs of each denominated amount to target"},
     427          92 :                             {RPCResult::Type::NUM, "denoms_hardcap", "Maximum limit of how many inputs of each denominated amount to create"},
     428          92 :                             {RPCResult::Type::NUM, "queue_size", "How many queues there are currently on the network"},
     429          92 :                             {RPCResult::Type::BOOL, "running", "Whether mixing is currently running"},
     430         184 :                             {RPCResult::Type::ARR, "sessions", "",
     431         184 :                             {
     432         184 :                                 {RPCResult::Type::OBJ, "", "",
     433         736 :                                 {
     434          92 :                                     {RPCResult::Type::STR_HEX, "protxhash", "The ProTxHash of the masternode"},
     435          92 :                                     GetRpcResult("outpoint"),
     436          92 :                                     {RPCResult::Type::STR, "service", "The IP address and port of the masternode (DEPRECATED, returned only if config option -deprecatedrpc=service is passed)"},
     437         184 :                                     {RPCResult::Type::ARR, "addrs_core_p2p", "Network addresses of the masternode used for protocol P2P",
     438         184 :                                         {
     439          92 :                                             {RPCResult::Type::STR, "address", ""},
     440             :                                         }
     441             :                                     },
     442          92 :                                     {RPCResult::Type::NUM, "denomination", "The denomination of the mixing session in " + CURRENCY_UNIT + ""},
     443          92 :                                     {RPCResult::Type::STR_HEX, "state", "Current state of the mixing session"},
     444          92 :                                     {RPCResult::Type::NUM, "entries_count", "The number of entries in the mixing session"},
     445             :                                 }},
     446             :                             }},
     447          92 :                             {RPCResult::Type::NUM, "keys_left", /*optional=*/true, "How many new keys are left since last automatic backup (if applicable)"},
     448          92 :                             {RPCResult::Type::STR, "warnings", "Warnings if any"},
     449             :                         }},
     450         184 :                     RPCResult{"for masternodes",
     451          92 :                         RPCResult::Type::OBJ, "", "",
     452         460 :                         {
     453          92 :                             {RPCResult::Type::NUM, "queue_size", "How many queues there are currently on the network"},
     454          92 :                             {RPCResult::Type::NUM, "denomination", "The denomination of the mixing session in " + CURRENCY_UNIT + ""},
     455          92 :                             {RPCResult::Type::STR_HEX, "state", "Current state of the mixing session"},
     456          92 :                             {RPCResult::Type::NUM, "entries_count", "The number of entries in the mixing session"},
     457             :                         }},
     458             :                 },
     459          92 :                 RPCExamples{
     460          92 :                     HelpExampleCli("getcoinjoininfo", "")
     461          92 :             + HelpExampleRpc("getcoinjoininfo", "")
     462             :                 },
     463          92 :                 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     464             : {
     465           0 :     UniValue obj(UniValue::VOBJ);
     466             : 
     467           0 :     const NodeContext& node = EnsureAnyNodeContext(request.context);
     468           0 :     if (node.active_ctx) {
     469           0 :         node.active_ctx->GetCJServer().GetJsonInfo(obj);
     470           0 :         return obj;
     471             :     }
     472             : 
     473             : #ifdef ENABLE_WALLET
     474           0 :     CCoinJoinClientOptions::GetJsonInfo(obj);
     475             : 
     476           0 :     if (node.cj_walletman) {
     477           0 :         if (auto queue_size = node.cj_walletman->getQueueSize()) {
     478           0 :             obj.pushKV("queue_size", queue_size.value());
     479           0 :         }
     480           0 :     }
     481             : 
     482           0 :     const std::shared_ptr<const CWallet> wallet = GetWalletForJSONRPCRequest(request);
     483           0 :     if (!wallet) {
     484           0 :         return obj;
     485             :     }
     486             : 
     487           0 :     auto cj_clientman = CHECK_NONFATAL(node.coinjoin_loader)->GetClient(wallet->GetName());
     488           0 :     CHECK_NONFATAL(cj_clientman)->getJsonInfo(obj);
     489             : 
     490           0 :     std::string warning_msg;
     491           0 :     if (wallet->IsLegacy()) {
     492           0 :         obj.pushKV("keys_left", wallet->nKeysLeftSinceAutoBackup);
     493           0 :         if (wallet->nKeysLeftSinceAutoBackup < COINJOIN_KEYS_THRESHOLD_WARNING) {
     494           0 :             warning_msg = "WARNING: keypool is almost depleted!";
     495           0 :         }
     496           0 :     }
     497           0 :     obj.pushKV("warnings", warning_msg);
     498             : #endif // ENABLE_WALLET
     499             : 
     500           0 :     return obj;
     501           0 : },
     502             :     };
     503           0 : }
     504             : 
     505             : #ifdef ENABLE_WALLET
     506           0 : Span<const CRPCCommand> GetWalletCoinJoinRPCCommands()
     507             : {
     508           0 :     static const CRPCCommand commands[]{
     509           0 :         {"dash", &coinjoin},
     510           0 :         {"dash", &coinjoin_reset},
     511           0 :         {"dash", &coinjoin_start},
     512           0 :         {"dash", &coinjoin_status},
     513           0 :         {"dash", &coinjoin_stop},
     514           0 :         {"dash", &coinjoinsalt},
     515           0 :         {"dash", &coinjoinsalt_generate},
     516           0 :         {"dash", &coinjoinsalt_get},
     517           0 :         {"dash", &coinjoinsalt_set},
     518           0 :         {"dash", &getcoinjoininfo},
     519             :     };
     520           0 :     return commands;
     521           0 : }
     522             : #endif // ENABLE_WALLET
     523             : 
     524         178 : void RegisterCoinJoinRPCCommands(CRPCTable& t)
     525             : {
     526         224 :     static const CRPCCommand commands_wallet[]{
     527          46 :         {"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         356 :     if (!g_wallet_init_interface.HasWalletSupport()
     536             : #ifdef ENABLE_WALLET
     537         178 :         || gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)
     538             : #endif // ENABLE_WALLET
     539             :     ) {
     540           0 :         for (const auto& command : commands_wallet) {
     541           0 :             tableRPC.appendCommand(command.name, &command);
     542             :         }
     543           0 :     }
     544         178 : }

Generated by: LCOV version 1.16