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 : }
|