Line data Source code
1 : // Copyright (c) 2010 Satoshi Nakamoto
2 : // Copyright (c) 2009-2014 The Bitcoin developers
3 : // Copyright (c) 2014-2015 The Dash developers
4 : // Copyright (c) 2015-2022 The PIVX Core developers
5 : // Distributed under the MIT software license, see the accompanying
6 : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
7 :
8 : #include "wallet/rpcwallet.h"
9 :
10 : #include "addressbook.h"
11 : #include "amount.h"
12 : #include "coincontrol.h"
13 : #include "core_io.h"
14 : #include "destination_io.h"
15 : #include "httpserver.h"
16 : #include "key_io.h"
17 : #include "masternode-sync.h"
18 : #include "messagesigner.h"
19 : #include "net.h"
20 : #include "policy/feerate.h"
21 : #include "primitives/transaction.h"
22 : #include "rpc/server.h"
23 : #include "sapling/key_io_sapling.h"
24 : #include "sapling/sapling_operation.h"
25 : #include "shutdown.h"
26 : #include "spork.h"
27 : #include "timedata.h"
28 : #include "utilmoneystr.h"
29 : #include "wallet/wallet.h"
30 : #include "wallet/walletdb.h"
31 : #include "wallet/walletutil.h"
32 :
33 : #include <stdint.h>
34 : #include <univalue.h>
35 :
36 :
37 : static const std::string WALLET_ENDPOINT_BASE = "/wallet/";
38 :
39 115699 : CWallet* GetWalletForJSONRPCRequest(const JSONRPCRequest& request)
40 : {
41 115699 : if (request.URI.substr(0, WALLET_ENDPOINT_BASE.size()) == WALLET_ENDPOINT_BASE) {
42 : // wallet endpoint was used
43 55 : std::string requestedWallet = urlDecode(request.URI.substr(WALLET_ENDPOINT_BASE.size()));
44 93 : for (CWalletRef pwallet : ::vpwallets) {
45 92 : if (pwallet->GetName() == requestedWallet) {
46 27 : return pwallet;
47 : }
48 : }
49 2 : throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Requested wallet does not exist or is not loaded");
50 : }
51 115671 : return ::vpwallets.size() == 1 || (request.fHelp && ::vpwallets.size() > 0) ? ::vpwallets[0] : nullptr;
52 : }
53 :
54 8 : std::string HelpRequiringPassphrase(CWallet* const pwallet)
55 : {
56 15 : return pwallet && pwallet->IsCrypted() ? "\nRequires wallet passphrase to be set with walletpassphrase call." : "";
57 : }
58 :
59 11677 : bool EnsureWalletIsAvailable(CWallet* const pwallet, bool avoidException)
60 : {
61 11677 : if (pwallet) return true;
62 2 : if (avoidException) return false;
63 2 : if (::vpwallets.empty()) {
64 : // Wallet RPC methods are disabled if no wallets are loaded.
65 0 : throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found (disabled)");
66 : }
67 2 : throw JSONRPCError(RPC_WALLET_NOT_SPECIFIED,
68 4 : "Wallet file not specified (must request wallet RPC through /wallet/<filename> uri-path).");
69 : }
70 :
71 107395 : void EnsureWalletIsUnlocked(CWallet* const pwallet, bool fAllowAnonOnly)
72 : {
73 107395 : if (pwallet->IsLocked() || (!fAllowAnonOnly && pwallet->fWalletUnlockStaking))
74 10 : throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first.");
75 107390 : }
76 :
77 393 : static void WalletTxToJSON(const CWalletTx& wtx, UniValue& entry) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
78 : {
79 393 : AssertLockHeld(cs_main);
80 :
81 393 : int confirms = wtx.GetDepthInMainChain();
82 393 : entry.pushKV("confirmations", confirms);
83 393 : entry.pushKV("bcconfirmations", confirms); // DEPRECATED in 4.3.99
84 576 : if (wtx.IsCoinBase() || wtx.IsCoinStake())
85 420 : entry.pushKV("generated", true);
86 393 : if (confirms > 0) {
87 638 : entry.pushKV("blockhash", wtx.m_confirm.hashBlock.GetHex());
88 319 : entry.pushKV("blockindex", wtx.m_confirm.nIndex);
89 319 : entry.pushKV("blocktime", LookupBlockIndex(wtx.m_confirm.hashBlock)->GetBlockTime());
90 : } else {
91 148 : entry.pushKV("trusted", wtx.IsTrusted());
92 : }
93 393 : uint256 hash = wtx.GetHash();
94 786 : entry.pushKV("txid", hash.GetHex());
95 786 : UniValue conflicts(UniValue::VARR);
96 402 : for (const uint256& conflict : wtx.GetConflicts())
97 18 : conflicts.push_back(conflict.GetHex());
98 393 : entry.pushKV("walletconflicts", conflicts);
99 393 : entry.pushKV("time", wtx.GetTxTime());
100 393 : entry.pushKV("timereceived", (int64_t)wtx.nTimeReceived);
101 410 : for (const std::pair<std::string, std::string> & item : wtx.mapValue)
102 17 : entry.pushKV(item.first, item.second);
103 393 : }
104 :
105 355 : std::string LabelFromValue(const UniValue& value)
106 : {
107 355 : std::string label = value.get_str();
108 355 : if (label == "*")
109 0 : throw JSONRPCError(RPC_WALLET_INVALID_LABEL_NAME, "Invalid label name");
110 355 : return label;
111 : }
112 :
113 2279 : static CTxDestination GetNewAddressFromLabel(CWallet* const pwallet, const std::string purpose, const UniValue ¶ms,
114 : const CChainParams::Base58Type addrType = CChainParams::PUBKEY_ADDRESS)
115 : {
116 4558 : LOCK2(cs_main, pwallet->cs_wallet);
117 : // Parse the label first so we don't generate a key if there's an error
118 4558 : std::string label;
119 2279 : if (!params.isNull() && params.size() > 0)
120 257 : label = LabelFromValue(params[0]);
121 :
122 6840 : auto r = pwallet->getNewAddress(label, purpose, addrType);
123 2279 : if(!r)
124 6 : throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, r.getError());
125 4552 : return *r.getObjResult();
126 : }
127 :
128 : /** Convert CAddressBookData to JSON record. */
129 613 : static UniValue AddressBookDataToJSON(const AddressBook::CAddressBookData& data, const bool verbose)
130 : {
131 613 : UniValue ret(UniValue::VOBJ);
132 613 : if (verbose) {
133 854 : ret.pushKV("name", data.name);
134 : }
135 613 : ret.pushKV("purpose", data.purpose);
136 613 : return ret;
137 : }
138 :
139 : /** Checks if a CKey is in the given CWallet compressed or otherwise*/
140 4 : bool HaveKey(const CWallet* wallet, const CKey& key)
141 : {
142 4 : CKey key2;
143 8 : key2.Set(key.begin(), key.end(), !key.IsCompressed());
144 8 : return wallet->HaveKey(key.GetPubKey().GetID()) || wallet->HaveKey(key2.GetPubKey().GetID());
145 : }
146 :
147 550 : UniValue getaddressinfo(const JSONRPCRequest& request)
148 : {
149 550 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
150 :
151 550 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
152 0 : return NullUniValue;
153 :
154 1100 : const std::string example_address = "\"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\"";
155 :
156 550 : if (request.fHelp || request.params.size() > 1)
157 0 : throw std::runtime_error(
158 : "getaddressinfo ( \"address\" )\n"
159 : "\nReturn information about the given PIVX address.\n"
160 : "Some of the information will only be present if the address is in the active wallet.\n"
161 : "Metadata for shield addresses is available only if the wallet is unlocked.\n"
162 : "{Result:\n"
163 : " \"address\" : \"address\", (string) The PIVX address validated.\n"
164 : " \"isshield\" : true|false, (boolean) If the address is shield or transparent.\n"
165 : " \"scriptPubKey\" : \"hex\", (string, only if isshield=false) The hex-encoded scriptPubKey generated by the address.\n"
166 : " \"ischange\" : true|false, (boolean) If the transparent address was used for change output.\n"
167 : " \"ismine\" : true|false, (boolean) If the address is yours.\n"
168 : " \"iswatchonly\" : true|false, (boolean) If the address is watchonly.\n"
169 : " \"label\" : \"label\" (string) The label associated with the address, \"\" is the default label.\n"
170 : " \"timestamp\" : timestamp, (number, optional) The creation time of the key, if available, expressed in the UNIX epoch time.\n"
171 : " \"hdkeypath\" : \"keypath\" (string, optional) The HD keypath, if the key is HD and available.\n"
172 : " \"hdseedid\" : \"<hash160>\" (string, optional) The Hash160 of the HD seed.\n"
173 : " \"hdmasterfingerprint\" : \"<hash160>\" (string, optional) The fingerprint of the master key.\n"
174 : " \"labels\" (json object) An array of labels associated with the address. Currently limited to one label but returned\n"
175 : " as an array to keep the API stable if multiple labels are enabled in the future.\n"
176 : " [\n"
177 : " { (json object of label data)\n"
178 : " \"name\" : \"labelname\" (string) The label.\n"
179 : " \"purpose\" : \"purpose\" (string) The purpose of the associated address (send or receive).\n"
180 : " }\n"
181 : " ]\n"
182 : "}\n"
183 :
184 0 : "\nExamples:\n" +
185 0 : HelpExampleCli("getaddressinfo", example_address) + HelpExampleRpc("getaddressinfo", example_address)
186 0 : );
187 :
188 1100 : LOCK(pwallet->cs_wallet);
189 :
190 550 : const std::string& strAdd = request.params[0].get_str();
191 1100 : UniValue ret(UniValue::VOBJ);
192 550 : ret.pushKV("address", strAdd);
193 :
194 1100 : const CWDestination& dest = Standard::DecodeDestination(strAdd);
195 : // Make sure the destination is valid
196 550 : if (!Standard::IsValidDestination(dest)) {
197 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
198 : }
199 :
200 550 : const CTxDestination* pTransDest = Standard::GetTransparentDestination(dest);
201 550 : ret.pushKV("isshield", pTransDest == nullptr);
202 :
203 550 : if (pTransDest) {
204 984 : CScript scriptPubKey = GetScriptForDestination(*pTransDest);
205 1476 : ret.pushKV("scriptPubKey", HexStr(scriptPubKey));
206 984 : ret.pushKV("ischange", pwallet->IsChange(scriptPubKey));
207 : }
208 :
209 550 : isminetype mine = IsMine(*pwallet, dest);
210 550 : ret.pushKV("ismine", bool(mine & ISMINE_SPENDABLE_ALL));
211 550 : ret.pushKV("iswatchonly", bool(mine & ISMINE_WATCH_ONLY_ALL));
212 :
213 : // Return label field if existing. Currently only one label can be
214 : // associated with an address, so the label should be equivalent to the
215 : // value of the name key/value pair in the labels array below.
216 550 : if (pwallet->HasAddressBook(dest)) {
217 854 : ret.pushKV("label", pwallet->GetNameForAddressBookEntry(dest));
218 : }
219 :
220 550 : ScriptPubKeyMan* spk_man = pwallet->GetScriptPubKeyMan();
221 550 : SaplingScriptPubKeyMan* sspk_man = pwallet->GetSaplingScriptPubKeyMan();
222 550 : CKeyMetadata* meta = nullptr;
223 :
224 550 : if (spk_man && pTransDest) {
225 : // transparent destination
226 492 : const CKeyID* keyID = boost::get<CKeyID>(pTransDest);
227 492 : if (keyID) {
228 474 : auto it = pwallet->mapKeyMetadata.find(*keyID);
229 474 : if(it != pwallet->mapKeyMetadata.end()) {
230 474 : meta = &it->second;
231 : }
232 : }
233 58 : } else if (sspk_man && !pTransDest) {
234 : // shield destination
235 58 : const libzcash::SaplingPaymentAddress pa = *Standard::GetShieldedDestination(dest);
236 58 : libzcash::SaplingExtendedSpendingKey extsk;
237 58 : if (pwallet->GetSaplingExtendedSpendingKey(pa, extsk)) {
238 58 : const auto& ivk = extsk.expsk.full_viewing_key().in_viewing_key();
239 58 : auto it = sspk_man->mapSaplingZKeyMetadata.find(ivk);
240 58 : if (it != sspk_man->mapSaplingZKeyMetadata.end()) {
241 58 : meta = &it->second;
242 : }
243 : }
244 : }
245 :
246 : // Add metadata
247 532 : if (meta) {
248 532 : ret.pushKV("timestamp", meta->nCreateTime);
249 532 : if (meta->HasKeyOrigin()) {
250 740 : ret.pushKV("hdkeypath", meta->key_origin.pathToString());
251 740 : ret.pushKV("hdseedid", meta->hd_seed_id.GetHex());
252 740 : ret.pushKV("hdmasterfingerprint", HexStr(meta->key_origin.fingerprint));
253 : }
254 : }
255 :
256 : // Return a `labels` array containing the label associated with the address,
257 : // equivalent to the `label` field above. Currently only one label can be
258 : // associated with an address, but we return an array so the API remains
259 : // stable if we allow multiple labels to be associated with an address in
260 : // the future.
261 : //
262 : // DEPRECATED: The previous behavior of returning an array containing a JSON
263 : // object of `name` and `purpose` key/value pairs has been deprecated.
264 1100 : UniValue labels(UniValue::VARR);
265 1100 : auto addrBookData = pwallet->GetAddressBookEntry(dest);
266 550 : if (addrBookData) {
267 427 : labels.push_back(AddressBookDataToJSON(*addrBookData, true));
268 : }
269 550 : ret.pushKV("labels", std::move(labels));
270 :
271 550 : return ret;
272 : }
273 :
274 61 : UniValue getaddressesbylabel(const JSONRPCRequest& request)
275 : {
276 61 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
277 :
278 61 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
279 0 : return NullUniValue;
280 :
281 61 : if (request.fHelp || request.params.size() != 1)
282 0 : throw std::runtime_error(
283 : "getaddressesbylabel \"label\"\n"
284 : "\nReturns the list of addresses assigned the specified label.\n"
285 :
286 : "\nArguments:\n"
287 : "1. \"label\" (string, required) The label.\n"
288 :
289 : "\nResult:\n"
290 : "{ (json object with addresses as keys)\n"
291 : " \"address\": { (json object with information about address)\n"
292 : " \"purpose\": \"string\" (string) Purpose of address (\"send\" for sending address, \"receive\" for receiving address)\n"
293 : " },...\n"
294 : "}\n"
295 :
296 : "\nExamples:\n"
297 0 : + HelpExampleCli("getaddressesbylabel", "\"tabby\"")
298 0 : + HelpExampleRpc("getaddressesbylabel", "\"tabby\"")
299 0 : );
300 :
301 107 : LOCK(pwallet->cs_wallet);
302 :
303 122 : std::string label = LabelFromValue(request.params[0]);
304 :
305 : // Find all addresses that have the given label
306 122 : UniValue ret(UniValue::VOBJ);
307 2746 : for (auto it = pwallet->NewAddressBookIterator(); it.IsValid(); it.Next()) {
308 2624 : auto addrBook = it.GetValue();
309 1312 : if (addrBook.name == label) {
310 186 : if (!addrBook.isShielded()) {
311 304 : ret.pushKV(EncodeDestination(*it.GetCTxDestKey(), AddressBook::IsColdStakingPurpose(addrBook.purpose), AddressBook::IsExchangePurpose(addrBook.purpose)), AddressBookDataToJSON(addrBook, false));
312 : } else {
313 68 : ret.pushKV(Standard::EncodeDestination(*it.GetShieldedDestKey()), AddressBookDataToJSON(addrBook, false));
314 : }
315 : }
316 : }
317 :
318 61 : if (ret.empty()) {
319 30 : throw JSONRPCError(RPC_WALLET_INVALID_LABEL_NAME, std::string("No addresses with label " + label));
320 : }
321 :
322 46 : return ret;
323 : }
324 :
325 1 : UniValue listlabels(const JSONRPCRequest& request)
326 : {
327 1 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
328 :
329 1 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
330 0 : return NullUniValue;
331 :
332 1 : if (request.fHelp || request.params.size() > 1)
333 0 : throw std::runtime_error(
334 : "listlabels ( \"purpose\" )\n"
335 : "\nReturns the list of all labels, or labels that are assigned to addresses with a specific purpose.\n"
336 :
337 : "\nArguments:\n"
338 : "1. \"purpose\" (string, optional) Address purpose to list labels for ('send','receive', 'delegable', 'delegator', 'coldstaking', 'coldstaking_send', 'refund'). An empty string is the same as not providing this argument.\n"
339 :
340 : "\nResult:\n"
341 : "[ (json array of string)\n"
342 : " \"label\", (string) Label name\n"
343 : " ...\n"
344 : "]\n"
345 :
346 : "\nExamples:\n"
347 : "\nList all labels\n"
348 0 : + HelpExampleCli("listlabels", "") +
349 : "\nList labels that have receiving addresses\n"
350 0 : + HelpExampleCli("listlabels", "receive") +
351 : "\nList labels that have sending addresses\n"
352 0 : + HelpExampleCli("listlabels", "send") +
353 : "\nAs json rpc call\n"
354 0 : + HelpExampleRpc("listlabels", "receive")
355 0 : );
356 :
357 2 : LOCK(pwallet->cs_wallet);
358 :
359 2 : std::string purpose;
360 1 : if (!request.params[0].isNull()) {
361 0 : purpose = request.params[0].get_str();
362 : }
363 :
364 : // Add to a set to sort by label name, then insert into Univalue array
365 2 : std::set<std::string> label_set;
366 12 : for (auto it = pwallet->NewAddressBookIterator(); it.IsValid(); it.Next()) {
367 5 : auto addrBook = it.GetValue();
368 5 : if (purpose.empty() || addrBook.purpose == purpose) {
369 10 : label_set.insert(addrBook.name);
370 : }
371 : }
372 :
373 2 : UniValue ret(UniValue::VARR);
374 6 : for (const std::string& name : label_set) {
375 5 : ret.push_back(name);
376 : }
377 :
378 1 : return ret;
379 : }
380 :
381 5 : CPubKey parseWIFKey(std::string strKey, CWallet* pwallet)
382 : {
383 5 : CKey key = KeyIO::DecodeSecret(strKey);
384 5 : if (!key.IsValid()) {
385 2 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key");
386 : }
387 :
388 4 : if (HaveKey(pwallet, key)) {
389 4 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Already have this key (either as an HD seed or as a loose private key)");
390 : }
391 4 : return pwallet->GetScriptPubKeyMan()->DeriveNewSeed(key);
392 : }
393 :
394 1 : UniValue upgradewallet(const JSONRPCRequest& request)
395 : {
396 1 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
397 :
398 1 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
399 0 : return NullUniValue;
400 :
401 1 : if (request.fHelp || request.params.size() != 0)
402 0 : throw std::runtime_error("upgradewallet\n"
403 : "Bump the wallet features to the latest supported version. Non-HD wallets will be upgraded to HD wallet functionality. "
404 : "Marking all the previous keys as pre-split keys and managing them separately. Once the last key in the pre-split keypool gets marked as used (received balance), the wallet will automatically start using the HD generated keys.\n"
405 : "The upgraded HD wallet will have a new HD seed set so that new keys added to the keypool will be derived from this new seed.\n"
406 : "Wallets that are already running the latest HD version will be upgraded to Sapling support\n"
407 : "Enabling the Sapling key manager. Sapling keys will be deterministically derived by the same HD wallet seed.\n"
408 : "Wallets that are running the latest Sapling version will not be upgraded"
409 : "\nNote that you will need to MAKE A NEW BACKUP of your wallet after upgrade it.\n"
410 0 : + HelpRequiringPassphrase(pwallet) + "\n"
411 0 : + HelpExampleCli("upgradewallet", "") + HelpExampleRpc("upgradewallet", "")
412 0 : );
413 :
414 3 : LOCK2(cs_main, pwallet->cs_wallet);
415 :
416 : // Do not do anything to wallets already upgraded
417 1 : if (pwallet->CanSupportFeature(FEATURE_LATEST)) {
418 0 : throw JSONRPCError(RPC_WALLET_ERROR, "Cannot upgrade the wallet. The wallet is already running the latest version");
419 : }
420 :
421 1 : EnsureWalletIsUnlocked(pwallet);
422 :
423 : // Get version
424 1 : int prev_version = pwallet->GetVersion();
425 : // Upgrade wallet's version
426 1 : pwallet->SetMinVersion(FEATURE_LATEST);
427 1 : pwallet->SetMaxVersion(FEATURE_LATEST);
428 :
429 : // Upgrade to HD
430 2 : std::string upgradeError;
431 1 : if (!pwallet->Upgrade(upgradeError, prev_version)) {
432 0 : upgradeError = strprintf("Error: Cannot upgrade wallet, %s", upgradeError);
433 0 : throw JSONRPCError(RPC_WALLET_ERROR, upgradeError);
434 : }
435 :
436 1 : return NullUniValue;
437 : }
438 :
439 9 : UniValue sethdseed(const JSONRPCRequest& request)
440 : {
441 9 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
442 :
443 9 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
444 0 : return NullUniValue;
445 :
446 9 : if (request.fHelp || request.params.size() > 2)
447 1 : throw std::runtime_error("sethdseed ( newkeypool \"seed\" )\n"
448 : "Set or generate a new HD wallet seed. Non-HD wallets will not be upgraded to being a HD wallet. Wallets that are already\n"
449 : "HD will have a new HD seed set so that new keys added to the keypool will be derived from this new seed.\n"
450 : "\nNote that you will need to MAKE A NEW BACKUP of your wallet after setting the HD wallet seed.\n"
451 2 : + HelpRequiringPassphrase(pwallet) + "\n"
452 :
453 : "\nArguments:\n"
454 : "1. newkeypool (boolean, optional, default true): Whether to flush old unused addresses, including change addresses, from the keypool and regenerate it.\n"
455 : " If true, the next address from getnewaddress and change address from getrawchangeaddress will be from this new seed.\n"
456 : " If false, addresses (including change addresses if the wallet already had HD Chain Split enabled) from the existing\n"
457 : " keypool will be used until it has been depleted."
458 : "2. \"seed\" (string, optional, default random seed): The WIF private key to use as the new HD seed.\n"
459 : " The seed value can be retrieved using the dumpwallet command. It is the private key marked hdseed=1"
460 4 : + HelpExampleCli("sethdseed", "")
461 4 : + HelpExampleCli("sethdseed", "false")
462 4 : + HelpExampleCli("sethdseed", "true \"wifkey\"")
463 4 : + HelpExampleRpc("sethdseed", "true, \"wifkey\"")
464 3 : );
465 :
466 8 : if (IsInitialBlockDownload()) {
467 0 : throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Cannot set a new HD seed while still in Initial Block Download");
468 : }
469 :
470 : // Make sure the results are valid at least up to the most recent block
471 : // the user could have gotten from another RPC command prior to now
472 8 : pwallet->BlockUntilSyncedToCurrentChain();
473 :
474 19 : LOCK2(cs_main, pwallet->cs_wallet);
475 :
476 : // Do not do anything to non-HD wallets
477 8 : if (!pwallet->CanSupportFeature(FEATURE_PRE_SPLIT_KEYPOOL)) {
478 0 : throw JSONRPCError(RPC_WALLET_ERROR, "Cannot set a HD seed on a non-HD wallet. Start with -upgradewallet in order to upgrade a non-HD wallet to HD");
479 : }
480 :
481 8 : EnsureWalletIsUnlocked(pwallet);
482 :
483 8 : bool flush_key_pool = true;
484 8 : if (!request.params[0].isNull()) {
485 7 : flush_key_pool = request.params[0].get_bool();
486 : }
487 :
488 7 : ScriptPubKeyMan* spk_man = pwallet->GetScriptPubKeyMan();
489 7 : CPubKey master_pub_key = request.params[1].isNull() ?
490 17 : spk_man->GenerateNewSeed() : parseWIFKey(request.params[1].get_str(), pwallet);
491 :
492 3 : spk_man->SetHDSeed(master_pub_key, true);
493 3 : if (flush_key_pool) spk_man->NewKeyPool();
494 :
495 : // Update Sapling chain
496 3 : SaplingScriptPubKeyMan* sspk_man = pwallet->CanSupportFeature(FEATURE_SAPLING) ?
497 6 : pwallet->GetSaplingScriptPubKeyMan() : nullptr;
498 3 : if (sspk_man) {
499 3 : sspk_man->SetHDSeed(master_pub_key, true);
500 : }
501 :
502 3 : return NullUniValue;
503 : }
504 :
505 2255 : UniValue getnewaddress(const JSONRPCRequest& request)
506 : {
507 2255 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
508 :
509 2255 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
510 0 : return NullUniValue;
511 :
512 2255 : if (request.fHelp || request.params.size() > 1)
513 0 : throw std::runtime_error(
514 : "getnewaddress ( \"label\" )\n"
515 : "\nReturns a new PIVX address for receiving payments.\n"
516 : "If 'label' is specified, it is added to the address book \n"
517 : "so payments received with the address will be associated with 'label'.\n"
518 :
519 : "\nArguments:\n"
520 : "1. \"label\" (string, optional) The label name for the address to be linked to. if not provided, the default label \"\" is used. It can also be set to the empty string \"\" to represent the default label. The label does not need to exist, it will be created if there is no label by the given name.\n"
521 :
522 : "\nResult:\n"
523 : "\"pivxaddress\" (string) The new pivx address\n"
524 :
525 0 : "\nExamples:\n" +
526 0 : HelpExampleCli("getnewaddress", "") + HelpExampleRpc("getnewaddress", ""));
527 :
528 4507 : return EncodeDestination(GetNewAddressFromLabel(pwallet, AddressBook::AddressBookPurpose::RECEIVE, request.params));
529 : }
530 :
531 2 : UniValue getnewexchangeaddress(const JSONRPCRequest& request)
532 : {
533 2 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
534 :
535 2 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
536 0 : return NullUniValue;
537 : }
538 :
539 2 : if (request.fHelp || request.params.size() > 1)
540 0 : throw std::runtime_error(
541 : "getnewexchangeaddress ( \"label\" )\n"
542 : "\nReturns a new PIVX exchange address for receiving transparent funds only.\n"
543 :
544 : "\nArguments:\n"
545 : "1. \"label\" (string, optional) The label name for the address to be linked to. if not provided, the default label \"\" is used. It can also be set to the empty string \"\" to represent the default label. The label does not need to exist, it will be created if there is no label by the given name.\n"
546 :
547 :
548 : "\nResult:\n"
549 : "\"pivxaddress\" (string) The new pivx exchange address\n"
550 :
551 0 : "\nExamples:\n" +
552 0 : HelpExampleCli("getnewexchangeaddress", "") + HelpExampleRpc("getnewexchangeaddress", ""));
553 :
554 6 : LOCK2(cs_main, pwallet->cs_wallet);
555 :
556 4 : return EncodeDestination(GetNewAddressFromLabel(pwallet, "exchange", request.params, CChainParams::EXCHANGE_ADDRESS), CChainParams::EXCHANGE_ADDRESS);
557 : }
558 :
559 14 : UniValue getnewstakingaddress(const JSONRPCRequest& request)
560 : {
561 14 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
562 :
563 14 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
564 0 : return NullUniValue;
565 :
566 14 : if (request.fHelp || request.params.size() > 1)
567 0 : throw std::runtime_error(
568 : "getnewstakingaddress ( \"label\" )\n"
569 : "\nReturns a new PIVX cold staking address for receiving delegated cold stakes.\n"
570 :
571 : "\nArguments:\n"
572 : "1. \"label\" (string, optional) The label name for the address to be linked to. if not provided, the default label \"\" is used. It can also be set to the empty string \"\" to represent the default label. The label does not need to exist, it will be created if there is no label by the given name.\n"
573 :
574 :
575 : "\nResult:\n"
576 : "\"pivxaddress\" (string) The new pivx address\n"
577 :
578 0 : "\nExamples:\n" +
579 0 : HelpExampleCli("getnewstakingaddress", "") + HelpExampleRpc("getnewstakingaddress", ""));
580 :
581 28 : return EncodeDestination(GetNewAddressFromLabel(pwallet, "coldstaking", request.params, CChainParams::STAKING_ADDRESS), CChainParams::STAKING_ADDRESS);
582 : }
583 :
584 236 : UniValue getnewshieldaddress(const JSONRPCRequest& request)
585 : {
586 236 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
587 :
588 236 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
589 0 : return NullUniValue;
590 :
591 236 : if (request.fHelp || request.params.size() > 1)
592 1 : throw std::runtime_error(
593 : "getnewshieldaddress ( \"label\" )\n"
594 : "\nReturns a new shield address for receiving payments.\n"
595 : "If 'label' is specified, it is added to the address book \n"
596 : "so payments received with the shield address will be associated with 'label'.\n"
597 2 : + HelpRequiringPassphrase(pwallet) + "\n"
598 :
599 : "\nArguments:\n"
600 : "1. \"label\" (string, optional) The label name for the address to be linked to. if not provided, the default label \"\" is used. It can also be set to the empty string \"\" to represent the default label. The label does not need to exist, it will be created if there is no label by the given name.\n"
601 :
602 : "\nResult:\n"
603 : "\"address\" (string) The new shield address.\n"
604 :
605 : "\nExamples:\n"
606 5 : + HelpExampleCli("getnewshieldaddress", "")
607 5 : + HelpExampleRpc("getnewshieldaddress", "")
608 3 : );
609 :
610 469 : std::string label;
611 235 : if (!request.params.empty()) {
612 0 : label = LabelFromValue(request.params[0]);
613 : }
614 :
615 705 : LOCK2(cs_main, pwallet->cs_wallet);
616 :
617 235 : EnsureWalletIsUnlocked(pwallet);
618 :
619 703 : return KeyIO::EncodePaymentAddress(pwallet->GenerateNewSaplingZKey(label));
620 : }
621 :
622 379 : static inline std::string HexStrTrimmed(std::array<unsigned char, ZC_MEMO_SIZE> vch)
623 : {
624 758 : return HexStr(std::vector<unsigned char>(vch.begin(), FindFirstNonZero(vch.rbegin(), vch.rend()).base()));
625 : }
626 :
627 18 : UniValue listshieldunspent(const JSONRPCRequest& request)
628 : {
629 18 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
630 :
631 18 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
632 0 : return NullUniValue;
633 :
634 18 : if (request.fHelp || request.params.size() > 4)
635 1 : throw std::runtime_error(
636 : "listshieldunspent ( minconf maxconf include_watchonly [\"shield_addr\",...] )\n"
637 : "\nReturns array of unspent shield notes with between minconf and maxconf (inclusive) confirmations.\n"
638 : "Optionally filter to only include notes sent to specified addresses.\n"
639 : "When minconf is 0, unspent notes with zero confirmations are returned, even though they are not immediately spendable.\n"
640 :
641 : "\nArguments:\n"
642 : "1. minconf (numeric, optional, default=1) The minimum confirmations to filter\n"
643 : "2. maxconf (numeric, optional, default=9999999) The maximum confirmations to filter\n"
644 : "3. include_watchonly (bool, optional, default=false) Also include watchonly addresses (see 'importsaplingviewingkey')\n"
645 : "4. \"addresses\" (string) A json array of shield addrs to filter on. Duplicate addresses not allowed.\n"
646 : " [\n"
647 : " \"address\" (string) shield addr\n"
648 : " ,...\n"
649 : " ]\n"
650 :
651 : "\nResult:\n"
652 : "[ (array of json object)\n"
653 : " {\n"
654 : " \"txid\" : \"txid\", (string) the transaction id \n"
655 : " \"outindex\" (sapling) : n, (numeric) the output index\n"
656 : " \"confirmations\" : n, (numeric) the number of confirmations\n"
657 : " \"spendable\" : true|false, (boolean) true if note can be spent by wallet, false if address is watchonly\n"
658 : " \"address\" : \"address\", (string) the shield address\n"
659 : " \"amount\": xxxxx, (numeric) the amount of value in the note\n"
660 : " \"memo\": xxxxx, (string) hexadecimal string representation of memo field\n"
661 : " \"change\": true|false, (boolean) true if the address that received the note is also one of the sending addresses\n"
662 : " \"nullifier\": xxxxx, (string) the note's nullifier, hex encoded"
663 : " }\n"
664 : " ,...\n"
665 : "]\n"
666 :
667 : "\nExamples\n"
668 3 : + HelpExampleCli("listshieldunspent", "")
669 6 : + HelpExampleCli("listshieldunspent", "6 9999999 false \"[\\\"ptestsapling1h0w73csah2aq0a32h42kr7tq4htlt5wfn4ejxfnm56f6ehjvek7k4e244g6v8v3pgylmz5ea8jh\\\",\\\"ptestsapling1h0w73csah2aq0a32h42kr7tq4htlt5wfn4ejxfnm56f6ehjvek7k4e244g6v8v3pgylmz5ea8jh\\\"]\"")
670 5 : + HelpExampleRpc("listshieldunspent", "6 9999999 false \"[\\\"ptestsapling1h0w73csah2aq0a32h42kr7tq4htlt5wfn4ejxfnm56f6ehjvek7k4e244g6v8v3pgylmz5ea8jh\\\",\\\"ptestsapling1h0w73csah2aq0a32h42kr7tq4htlt5wfn4ejxfnm56f6ehjvek7k4e244g6v8v3pgylmz5ea8jh\\\"]\"")
671 3 : );
672 :
673 17 : RPCTypeCheck(request.params, {UniValue::VNUM, UniValue::VNUM, UniValue::VBOOL, UniValue::VARR});
674 :
675 17 : int nMinDepth = request.params.size() > 0 ? request.params[0].get_int() : 1;
676 8 : if (nMinDepth < 0) {
677 2 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Minimum number of confirmations cannot be less than 0");
678 : }
679 :
680 16 : int nMaxDepth = request.params.size() > 1 ? request.params[1].get_int() : 9999999;
681 15 : if (nMaxDepth < nMinDepth) {
682 2 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Maximum number of confirmations must be greater or equal to the minimum number of confirmations");
683 : }
684 :
685 25 : std::set<libzcash::PaymentAddress> shieldAddrs = {};
686 14 : bool fIncludeWatchonly = request.params.size() > 2 && request.params[2].get_bool();
687 :
688 42 : LOCK2(cs_main, pwallet->cs_wallet);
689 :
690 : // User has supplied shield addrs to filter on
691 14 : if (request.params.size() > 3) {
692 8 : UniValue addresses = request.params[3].get_array();
693 5 : if (addresses.size()==0)
694 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, addresses array is empty.");
695 :
696 : // Keep track of addresses to spot duplicates
697 10 : std::set<std::string> setAddress;
698 :
699 : // Sources
700 7 : for (const UniValue& o : addresses.getValues()) {
701 5 : if (!o.isStr()) {
702 2 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected string");
703 : }
704 8 : std::string address = o.get_str();
705 8 : auto shieldAddr = KeyIO::DecodePaymentAddress(address);
706 4 : if (!IsValidPaymentAddress(shieldAddr)) {
707 2 : throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, address is not a valid shield address: ") + address);
708 : }
709 3 : libzcash::SaplingPaymentAddress paymentAddress = *boost::get<libzcash::SaplingPaymentAddress>(&shieldAddr);
710 3 : bool hasSpendingKey = pwallet->HaveSpendingKeyForPaymentAddress(paymentAddress);
711 3 : if (!fIncludeWatchonly && !hasSpendingKey) {
712 2 : throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, spending key for address does not belong to wallet: ") + address);
713 : }
714 2 : shieldAddrs.insert(shieldAddr);
715 :
716 2 : if (setAddress.count(address)) {
717 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ") + address);
718 : }
719 2 : setAddress.insert(address);
720 : }
721 : } else {
722 : // User did not provide shield addrs, so use default i.e. all addresses
723 18 : std::set<libzcash::SaplingPaymentAddress> saplingzaddrs = {};
724 9 : pwallet->GetSaplingPaymentAddresses(saplingzaddrs);
725 9 : shieldAddrs.insert(saplingzaddrs.begin(), saplingzaddrs.end());
726 : }
727 :
728 22 : UniValue results(UniValue::VARR);
729 :
730 11 : if (shieldAddrs.size() > 0) {
731 22 : std::vector<SaplingNoteEntry> saplingEntries;
732 11 : pwallet->GetSaplingScriptPubKeyMan()->GetFilteredNotes(saplingEntries, shieldAddrs, nMinDepth, nMaxDepth, true, !fIncludeWatchonly, false);
733 22 : std::set<std::pair<libzcash::PaymentAddress, uint256>> nullifierSet = pwallet->GetSaplingScriptPubKeyMan()->GetNullifiersForAddresses(shieldAddrs);
734 :
735 349 : for (const auto& entry : saplingEntries) {
736 676 : UniValue obj(UniValue::VOBJ);
737 676 : obj.pushKV("txid", entry.op.hash.ToString());
738 338 : obj.pushKV("outindex", (int)entry.op.n);
739 338 : obj.pushKV("confirmations", entry.confirmations);
740 338 : bool hasSaplingSpendingKey = pwallet->HaveSpendingKeyForPaymentAddress(entry.address);
741 338 : obj.pushKV("spendable", hasSaplingSpendingKey);
742 1014 : obj.pushKV("address", KeyIO::EncodePaymentAddress(entry.address));
743 676 : obj.pushKV("amount", ValueFromAmount(CAmount(entry.note.value()))); // note.value() is equivalent to plaintext.value()
744 676 : obj.pushKV("memo", HexStrTrimmed(entry.memo));
745 338 : if (hasSaplingSpendingKey) {
746 676 : obj.pushKV("change", pwallet->GetSaplingScriptPubKeyMan()->IsNoteSaplingChange(nullifierSet, entry.address, entry.op));
747 : }
748 338 : const auto& nd = pwallet->mapWallet.at(entry.op.hash).mapSaplingNoteData.at(entry.op);
749 338 : if (nd.nullifier) {
750 1014 : obj.pushKV("nullifier", nd.nullifier->ToString());
751 : }
752 338 : results.push_back(obj);
753 : }
754 : }
755 :
756 11 : return results;
757 : }
758 :
759 2 : UniValue delegatoradd(const JSONRPCRequest& request)
760 : {
761 2 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
762 :
763 2 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
764 0 : return NullUniValue;
765 :
766 2 : if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
767 0 : throw std::runtime_error(
768 : "delegatoradd \"address\" ( \"label\" )\n"
769 : "\nAdd the provided address <address> into the allowed delegators AddressBook.\n"
770 : "This enables the staking of coins delegated to this wallet, owned by <addr>\n"
771 :
772 : "\nArguments:\n"
773 : "1. \"address\" (string, required) The address to whitelist\n"
774 : "2. \"label\" (string, optional) A label for the address to whitelist\n"
775 :
776 : "\nResult:\n"
777 : "true|false (boolean) true if successful.\n"
778 :
779 0 : "\nExamples:\n" +
780 0 : HelpExampleCli("delegatoradd", "DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6") +
781 0 : HelpExampleRpc("delegatoradd", "\"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\"") +
782 0 : HelpExampleRpc("delegatoradd", "\"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\" \"myPaperWallet\""));
783 :
784 2 : bool isStaking = false;
785 2 : bool isExchange = false;
786 4 : CTxDestination dest = DecodeDestination(request.params[0].get_str(), isStaking, isExchange);
787 2 : if (!IsValidDestination(dest) || isStaking)
788 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid PIVX address");
789 :
790 4 : const std::string strLabel = (request.params.size() > 1 ? request.params[1].get_str() : "");
791 :
792 2 : const CKeyID* keyID = boost::get<CKeyID>(&dest);
793 2 : if (!keyID)
794 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unable to get KeyID from PIVX address");
795 :
796 4 : return pwallet->SetAddressBook(*keyID, strLabel, AddressBook::AddressBookPurpose::DELEGATOR);
797 : }
798 :
799 1 : UniValue delegatorremove(const JSONRPCRequest& request)
800 : {
801 1 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
802 :
803 1 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
804 0 : return NullUniValue;
805 :
806 1 : if (request.fHelp || request.params.size() != 1)
807 0 : throw std::runtime_error(
808 : "delegatorremove \"address\"\n"
809 : "\nUpdates the provided address <address> from the allowed delegators keystore to a \"delegable\" status.\n"
810 : "This disables the staking of coins delegated to this wallet, owned by <addr>\n"
811 :
812 : "\nArguments:\n"
813 : "1. \"address\" (string, required) The address to blacklist\n"
814 :
815 : "\nResult:\n"
816 : "true|false (boolean) true if successful.\n"
817 :
818 0 : "\nExamples:\n" +
819 0 : HelpExampleCli("delegatorremove", "DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6") +
820 0 : HelpExampleRpc("delegatorremove", "\"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\""));
821 :
822 1 : bool isStaking = false;
823 1 : bool isExchange = false;
824 2 : CTxDestination dest = DecodeDestination(request.params[0].get_str(), isStaking, isExchange);
825 1 : if (!IsValidDestination(dest) || isStaking)
826 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid PIVX address");
827 :
828 1 : const CKeyID* keyID = boost::get<CKeyID>(&dest);
829 1 : if (!keyID)
830 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unable to get KeyID from PIVX address");
831 :
832 1 : if (!pwallet->HasAddressBook(*keyID))
833 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unable to get PIVX address from addressBook");
834 :
835 2 : std::string label = "";
836 2 : auto optAdd = pwallet->GetAddressBookEntry(dest);
837 1 : if (optAdd) {
838 1 : label = optAdd->name;
839 : }
840 :
841 2 : return pwallet->SetAddressBook(*keyID, label, AddressBook::AddressBookPurpose::DELEGABLE);
842 : }
843 :
844 6 : static UniValue ListaddressesForPurpose(CWallet* const pwallet, const std::string strPurpose)
845 : {
846 6 : CChainParams::Base58Type addrType;
847 6 : if (AddressBook::IsColdStakingPurpose(strPurpose)) {
848 : addrType = CChainParams::STAKING_ADDRESS;
849 5 : } else if (AddressBook::IsExchangePurpose(strPurpose)) {
850 : addrType = CChainParams::EXCHANGE_ADDRESS;
851 : } else {
852 5 : addrType = CChainParams::PUBKEY_ADDRESS;
853 : }
854 6 : UniValue ret(UniValue::VARR);
855 6 : {
856 6 : LOCK(pwallet->cs_wallet);
857 38 : for (auto it = pwallet->NewAddressBookIterator(); it.IsValid(); it.Next()) {
858 17 : auto addrBook = it.GetValue();
859 13 : if (addrBook.purpose != strPurpose) continue;
860 4 : auto dest = it.GetCTxDestKey();
861 4 : if (!dest) continue;
862 8 : UniValue entry(UniValue::VOBJ);
863 4 : entry.pushKV("label", addrBook.name);
864 8 : entry.pushKV("address", EncodeDestination(*dest, addrType));
865 4 : ret.push_back(entry);
866 : }
867 : }
868 :
869 6 : return ret;
870 : }
871 :
872 5 : UniValue listdelegators(const JSONRPCRequest& request)
873 : {
874 5 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
875 :
876 5 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
877 0 : return NullUniValue;
878 :
879 5 : if (request.fHelp || request.params.size() > 1)
880 0 : throw std::runtime_error(
881 : "listdelegators ( blacklist )\n"
882 : "\nShows the list of allowed delegator addresses for cold staking.\n"
883 :
884 : "\nArguments:\n"
885 : "1. blacklist (boolean, optional, default = false) Show addresses removed\n"
886 : " from the delegators whitelist\n"
887 :
888 : "\nResult:\n"
889 : "[\n"
890 : " {\n"
891 : " \"label\": \"yyy\", (string) Address label\n"
892 : " \"address\": \"xxx\", (string) PIVX address string\n"
893 : " }\n"
894 : " ...\n"
895 : "]\n"
896 :
897 0 : "\nExamples:\n" +
898 0 : HelpExampleCli("listdelegators" , "") +
899 0 : HelpExampleRpc("listdelegators", ""));
900 :
901 5 : const bool fBlacklist = (request.params.size() > 0 ? request.params[0].get_bool() : false);
902 5 : return (fBlacklist ?
903 : ListaddressesForPurpose(pwallet, AddressBook::AddressBookPurpose::DELEGABLE) :
904 10 : ListaddressesForPurpose(pwallet, AddressBook::AddressBookPurpose::DELEGATOR));
905 : }
906 :
907 1 : UniValue liststakingaddresses(const JSONRPCRequest& request)
908 : {
909 1 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
910 :
911 1 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
912 0 : return NullUniValue;
913 :
914 1 : if (request.fHelp || !request.params.empty())
915 0 : throw std::runtime_error(
916 : "liststakingaddresses\n"
917 : "\nShows the list of staking addresses for this wallet.\n"
918 :
919 : "\nResult:\n"
920 : "[\n"
921 : " {\n"
922 : " \"label\": \"yyy\", (string) Address label\n"
923 : " \"address\": \"xxx\", (string) PIVX address string\n"
924 : " }\n"
925 : " ...\n"
926 : "]\n"
927 :
928 0 : "\nExamples:\n" +
929 0 : HelpExampleCli("liststakingaddresses" , "") +
930 0 : HelpExampleRpc("liststakingaddresses", ""));
931 :
932 2 : return ListaddressesForPurpose(pwallet, AddressBook::AddressBookPurpose::COLD_STAKING);
933 : }
934 :
935 13 : UniValue listshieldaddresses(const JSONRPCRequest& request)
936 : {
937 13 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
938 :
939 13 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
940 0 : return NullUniValue;
941 :
942 13 : if (request.fHelp || request.params.size() > 1)
943 0 : throw std::runtime_error(
944 : "listshieldaddresses ( include_watchonly )\n"
945 : "\nReturns the list of shield addresses belonging to the wallet.\n"
946 :
947 : "\nArguments:\n"
948 : "1. include_watchonly (bool, optional, default=false) Also include watchonly addresses (see 'importviewingkey')\n"
949 :
950 : "\nResult:\n"
951 : "[ (json array of string)\n"
952 : " \"addr\" (string) a shield address belonging to the wallet\n"
953 : " ,...\n"
954 : "]\n"
955 :
956 : "\nExamples:\n"
957 0 : + HelpExampleCli("listshieldaddresses", "")
958 0 : + HelpExampleRpc("listshieldaddresses", "")
959 0 : );
960 :
961 39 : LOCK2(cs_main, pwallet->cs_wallet);
962 :
963 13 : bool fIncludeWatchonly = false;
964 13 : if (request.params.size() > 0) {
965 1 : fIncludeWatchonly = request.params[0].get_bool();
966 : }
967 :
968 26 : UniValue ret(UniValue::VARR);
969 :
970 26 : std::set<libzcash::SaplingPaymentAddress> addresses;
971 13 : pwallet->GetSaplingPaymentAddresses(addresses);
972 13 : libzcash::SaplingIncomingViewingKey ivk;
973 13 : libzcash::SaplingExtendedFullViewingKey extfvk;
974 3323 : for (libzcash::SaplingPaymentAddress addr : addresses) {
975 3310 : if (fIncludeWatchonly || pwallet->HaveSpendingKeyForPaymentAddress(addr)) {
976 6618 : ret.push_back(KeyIO::EncodePaymentAddress(addr));
977 : }
978 : }
979 13 : return ret;
980 : }
981 :
982 10 : UniValue getrawchangeaddress(const JSONRPCRequest& request)
983 : {
984 10 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
985 :
986 10 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
987 0 : return NullUniValue;
988 :
989 10 : if (request.fHelp || !request.params.empty())
990 0 : throw std::runtime_error(
991 : "getrawchangeaddress\n"
992 : "\nReturns a new PIVX address, for receiving change.\n"
993 : "This is for use with raw transactions, NOT normal use.\n"
994 :
995 : "\nResult:\n"
996 : "\"address\" (string) The address\n"
997 :
998 0 : "\nExamples:\n" +
999 0 : HelpExampleCli("getrawchangeaddress", "") + HelpExampleRpc("getrawchangeaddress", ""));
1000 :
1001 29 : LOCK2(cs_main, pwallet->cs_wallet);
1002 :
1003 10 : if (!pwallet->IsLocked())
1004 2 : pwallet->TopUpKeyPool();
1005 :
1006 20 : CReserveKey reservekey(pwallet);
1007 10 : CPubKey vchPubKey;
1008 10 : if (!reservekey.GetReservedKey(vchPubKey, true))
1009 2 : throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
1010 :
1011 9 : reservekey.KeepKey();
1012 :
1013 9 : CKeyID keyID = vchPubKey.GetID();
1014 :
1015 19 : return EncodeDestination(keyID);
1016 : }
1017 :
1018 :
1019 18 : UniValue setlabel(const JSONRPCRequest& request)
1020 : {
1021 18 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
1022 :
1023 18 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
1024 0 : return NullUniValue;
1025 :
1026 18 : if (request.fHelp || request.params.size() != 2)
1027 0 : throw std::runtime_error(
1028 : "setlabel \"address\" \"label\"\n"
1029 : "\nSets the label associated with the given address.\n"
1030 :
1031 : "\nArguments:\n"
1032 : "1. \"address\" (string, required) The pivx address to be associated with a label.\n"
1033 : "2. \"label\" (string, required) The label to assign to the address.\n"
1034 :
1035 0 : "\nExamples:\n" +
1036 0 : HelpExampleCli("setlabel", "\"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\" \"tabby\"") + HelpExampleRpc("setlabel", "\"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\", \"tabby\""));
1037 :
1038 54 : LOCK2(cs_main, pwallet->cs_wallet);
1039 :
1040 18 : bool isStaking = false, isExchange = false, isShielded = false;
1041 36 : const CWDestination& dest = Standard::DecodeDestination(request.params[0].get_str(), isStaking, isExchange, isShielded);
1042 : // Make sure the destination is valid
1043 18 : if (!Standard::IsValidDestination(dest)) {
1044 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
1045 : }
1046 :
1047 36 : std::string old_label = pwallet->GetNameForAddressBookEntry(dest);
1048 36 : std::string label = LabelFromValue(request.params[1]);
1049 :
1050 18 : pwallet->SetAddressBook(dest, label, "");
1051 :
1052 18 : return NullUniValue;
1053 : }
1054 :
1055 304 : static void SendMoney(CWallet* const pwallet, const CTxDestination& address, CAmount nValue, bool fSubtractFeeFromAmount, CTransactionRef& tx)
1056 : {
1057 608 : LOCK2(cs_main, pwallet->cs_wallet);
1058 :
1059 : // Check amount
1060 304 : if (nValue <= 0)
1061 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid amount");
1062 :
1063 304 : if (nValue > pwallet->GetAvailableBalance())
1064 0 : throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds");
1065 :
1066 304 : if (!g_connman)
1067 0 : throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
1068 :
1069 : // Parse PIVX address
1070 608 : CScript scriptPubKey = GetScriptForDestination(address);
1071 :
1072 : // Create and send the transaction
1073 608 : CReserveKey reservekey(pwallet);
1074 304 : CAmount nFeeRequired;
1075 608 : std::string strError;
1076 608 : std::vector<CRecipient> vecSend;
1077 304 : int nChangePosRet = -1;
1078 608 : CRecipient recipient = {scriptPubKey, nValue, fSubtractFeeFromAmount};
1079 304 : vecSend.push_back(recipient);
1080 304 : if (!pwallet->CreateTransaction(vecSend, tx, reservekey, nFeeRequired, nChangePosRet, strError)) {
1081 1 : if (!fSubtractFeeFromAmount && nValue + nFeeRequired > pwallet->GetAvailableBalance())
1082 0 : strError = strprintf("Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds!", FormatMoney(nFeeRequired));
1083 1 : LogPrintf("%s: %s\n", __func__, strError);
1084 1 : throw JSONRPCError(RPC_WALLET_ERROR, strError);
1085 : }
1086 607 : const CWallet::CommitResult&& res = pwallet->CommitTransaction(tx, reservekey, g_connman.get());
1087 303 : if (res.status != CWallet::CommitStatus::OK)
1088 0 : throw JSONRPCError(RPC_WALLET_ERROR, res.ToString());
1089 303 : }
1090 :
1091 : static SaplingOperation CreateShieldedTransaction(CWallet* const pwallet, const JSONRPCRequest& request);
1092 :
1093 : /*
1094 : * redirect sendtoaddress/sendmany inputs to shieldsendmany implementation (CreateShieldedTransaction)
1095 : */
1096 17 : static UniValue ShieldSendManyTo(CWallet * const pwallet,
1097 : const UniValue& sendTo,
1098 : const std::string& commentStr,
1099 : const std::string& toStr,
1100 : int nMinDepth,
1101 : bool fIncludeDelegated,
1102 : UniValue subtractFeeFromAmount)
1103 : {
1104 : // convert params to 'shieldsendmany' format
1105 34 : JSONRPCRequest req;
1106 17 : req.params = UniValue(UniValue::VARR);
1107 17 : if (!fIncludeDelegated) {
1108 17 : req.params.push_back(UniValue("from_transparent"));
1109 : } else {
1110 0 : req.params.push_back(UniValue("from_trans_cold"));
1111 : }
1112 34 : UniValue recipients(UniValue::VARR);
1113 37 : for (const std::string& key : sendTo.getKeys()) {
1114 40 : UniValue recipient(UniValue::VOBJ);
1115 20 : recipient.pushKV("address", key);
1116 20 : recipient.pushKV("amount", sendTo[key]);
1117 20 : recipients.push_back(recipient);
1118 : }
1119 17 : req.params.push_back(recipients);
1120 17 : req.params.push_back(nMinDepth);
1121 17 : req.params.push_back(0);
1122 17 : req.params.push_back(subtractFeeFromAmount);
1123 :
1124 : // send
1125 17 : SaplingOperation operation = CreateShieldedTransaction(pwallet, req);
1126 34 : std::string txid;
1127 34 : auto res = operation.send(txid);
1128 17 : if (!res)
1129 0 : throw JSONRPCError(RPC_WALLET_ERROR, res.getError());
1130 :
1131 : // add comments
1132 17 : const uint256& txHash = uint256S(txid);
1133 17 : auto it = pwallet->mapWallet.find(txHash);
1134 17 : assert(it != pwallet->mapWallet.end());
1135 17 : if (!commentStr.empty()) {
1136 0 : it->second.mapValue["comment"] = commentStr;
1137 : }
1138 17 : if (!toStr.empty()) {
1139 0 : it->second.mapValue["to"] = toStr;
1140 : }
1141 :
1142 34 : return txid;
1143 : }
1144 :
1145 320 : UniValue sendtoaddress(const JSONRPCRequest& request)
1146 : {
1147 320 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
1148 :
1149 320 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
1150 0 : return NullUniValue;
1151 :
1152 320 : if (request.fHelp || request.params.size() < 2 || request.params.size() > 5)
1153 0 : throw std::runtime_error(
1154 : "sendtoaddress \"address\" amount ( \"comment\" \"comment-to\" subtract_fee )\n"
1155 0 : "\nSend an amount to a given address. The amount is a real and is rounded to the nearest 0.00000001\n" +
1156 0 : HelpRequiringPassphrase(pwallet) + "\n"
1157 :
1158 : "\nArguments:\n"
1159 : "1. \"address\" (string, required) The pivx address to send to.\n"
1160 : "2. \"amount\" (numeric, required) The amount in PIV to send. eg 0.1\n"
1161 : "3. \"comment\" (string, optional) A comment used to store what the transaction is for. \n"
1162 : " This is not part of the transaction, just kept in your wallet.\n"
1163 : "4. \"comment-to\" (string, optional) A comment to store the name of the person or organization \n"
1164 : " to which you're sending the transaction. This is not part of the \n"
1165 : " transaction, just kept in your wallet.\n"
1166 : "5. subtract_fee (boolean, optional, default=false) The fee will be deducted from the amount being sent.\n"
1167 : " The recipient will receive less PIVs than you enter in the amount field.\n"
1168 :
1169 : "\nResult:\n"
1170 : "\"transactionid\" (string) The transaction id.\n"
1171 :
1172 0 : "\nExamples:\n" +
1173 0 : HelpExampleCli("sendtoaddress", "\"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\" 0.1") +
1174 0 : HelpExampleCli("sendtoaddress", "\"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\" 0.1 \"donation\" \"seans outpost\"") +
1175 0 : HelpExampleCli("sendtoaddress", "\"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\" 0.1 \"\" \"\" true") +
1176 0 : HelpExampleRpc("sendtoaddress", "\"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\", 0.1, \"donation\", \"seans outpost\""));
1177 :
1178 320 : EnsureWalletIsUnlocked(pwallet);
1179 :
1180 : // Make sure the results are valid at least up to the most recent block
1181 : // the user could have gotten from another RPC command prior to now
1182 319 : pwallet->BlockUntilSyncedToCurrentChain();
1183 :
1184 319 : bool isStaking = false, isExchange = false, isShielded = false;
1185 637 : const std::string addrStr = request.params[0].get_str();
1186 638 : const CWDestination& destination = Standard::DecodeDestination(addrStr, isStaking, isExchange, isShielded);
1187 319 : if (!Standard::IsValidDestination(destination) || isStaking)
1188 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid PIVX address");
1189 319 : const std::string commentStr = (request.params.size() > 2 && !request.params[2].isNull()) ?
1190 642 : request.params[2].get_str() : "";
1191 319 : const std::string toStr = (request.params.size() > 3 && !request.params[3].isNull()) ?
1192 642 : request.params[3].get_str() : "";
1193 319 : bool fSubtractFeeFromAmount = request.params.size() > 4 && request.params[4].get_bool();
1194 :
1195 319 : if (isShielded) {
1196 30 : UniValue sendTo(UniValue::VOBJ);
1197 15 : sendTo.pushKV(addrStr, request.params[1]);
1198 15 : UniValue subtractFeeFromAmount(UniValue::VARR);
1199 15 : if (fSubtractFeeFromAmount) {
1200 1 : subtractFeeFromAmount.push_back(addrStr);
1201 : }
1202 15 : return ShieldSendManyTo(pwallet, sendTo, commentStr, toStr, 1, false, subtractFeeFromAmount);
1203 : }
1204 :
1205 304 : const CTxDestination& address = *Standard::GetTransparentDestination(destination);
1206 :
1207 : // Amount
1208 304 : CAmount nAmount = AmountFromValue(request.params[1]);
1209 :
1210 319 : CTransactionRef tx;
1211 304 : SendMoney(pwallet, address, nAmount, fSubtractFeeFromAmount, tx);
1212 :
1213 : // Wallet comments
1214 303 : CWalletTx& wtx = pwallet->mapWallet.at(tx->GetHash());
1215 303 : if (!commentStr.empty())
1216 0 : wtx.mapValue["comment"] = commentStr;
1217 303 : if (!toStr.empty())
1218 0 : wtx.mapValue["to"] = toStr;
1219 :
1220 607 : return wtx.GetHash().GetHex();
1221 : }
1222 :
1223 33 : static UniValue CreateColdStakeDelegation(CWallet* const pwallet, const UniValue& params, CTransactionRef& txNew, CReserveKey& reservekey)
1224 : {
1225 66 : LOCK2(cs_main, pwallet->cs_wallet);
1226 :
1227 : // Check that Cold Staking has been enforced or fForceNotEnabled = true
1228 33 : bool fForceNotEnabled = false;
1229 33 : if (params.size() > 6 && !params[6].isNull())
1230 1 : fForceNotEnabled = params[6].get_bool();
1231 :
1232 33 : if (sporkManager.IsSporkActive(SPORK_19_COLDSTAKING_MAINTENANCE) && !fForceNotEnabled) {
1233 0 : std::string errMsg = "Cold Staking temporarily disabled with SPORK 19.\n"
1234 : "You may force the stake delegation setting fForceNotEnabled to true.\n"
1235 2 : "WARNING: If relayed before activation, this tx will be rejected resulting in a ban.\n";
1236 0 : throw JSONRPCError(RPC_WALLET_ERROR, errMsg);
1237 : }
1238 :
1239 : // Get Staking Address
1240 33 : bool isStaking{false};
1241 66 : CTxDestination stakeAddr = DecodeDestination(params[0].get_str());
1242 33 : if (!IsValidDestination(stakeAddr) || isStaking)
1243 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid PIVX staking address");
1244 :
1245 33 : CKeyID* stakeKey = boost::get<CKeyID>(&stakeAddr);
1246 33 : if (!stakeKey)
1247 0 : throw JSONRPCError(RPC_WALLET_ERROR, "Unable to get stake pubkey hash from stakingaddress");
1248 :
1249 : // Get Amount
1250 33 : CAmount nValue = AmountFromValue(params[1]);
1251 33 : if (nValue < MIN_COLDSTAKING_AMOUNT)
1252 1 : throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid amount (%d). Min amount: %d",
1253 3 : nValue, MIN_COLDSTAKING_AMOUNT));
1254 :
1255 : // include already delegated coins
1256 32 : bool fUseDelegated = false;
1257 32 : if (params.size() > 4 && !params[4].isNull())
1258 2 : fUseDelegated = params[4].get_bool();
1259 :
1260 : // Check amount
1261 32 : CAmount currBalance = pwallet->GetAvailableBalance() + (fUseDelegated ? pwallet->GetDelegatedBalance() : 0);
1262 32 : if (nValue > currBalance)
1263 0 : throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds");
1264 :
1265 64 : std::string strError;
1266 :
1267 : // Get Owner Address
1268 32 : std::string ownerAddressStr;
1269 32 : CKeyID ownerKey;
1270 32 : bool isStakingAddress = false;
1271 32 : bool isExchange = false;
1272 32 : if (params.size() > 2 && !params[2].isNull() && !params[2].get_str().empty()) {
1273 : // Address provided
1274 48 : CTxDestination dest = DecodeDestination(params[2].get_str(), isStakingAddress, isExchange);
1275 24 : if (!IsValidDestination(dest) || isStakingAddress)
1276 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid PIVX spending address");
1277 24 : ownerKey = *boost::get<CKeyID>(&dest);
1278 : // Check that the owner address belongs to this wallet, or fForceExternalAddr is true
1279 24 : bool fForceExternalAddr = params.size() > 3 && !params[3].isNull() ? params[3].get_bool() : false;
1280 23 : if (!fForceExternalAddr && !pwallet->HaveKey(ownerKey)) {
1281 2 : std::string errMsg = strprintf("The provided owneraddress \"%s\" is not present in this wallet.\n", params[2].get_str());
1282 1 : errMsg += "Set 'fExternalOwner' argument to true, in order to force the stake delegation to an external owner address.\n"
1283 : "e.g. delegatestake stakingaddress amount owneraddress true.\n"
1284 1 : "WARNING: Only the owner of the key to owneraddress will be allowed to spend these coins after the delegation.";
1285 1 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, errMsg);
1286 : }
1287 23 : ownerAddressStr = params[2].get_str();
1288 : } else {
1289 : // Get new owner address from keypool
1290 16 : CTxDestination ownerAddr = GetNewAddressFromLabel(pwallet, "delegated", NullUniValue);
1291 8 : CKeyID* pOwnerKey = boost::get<CKeyID>(&ownerAddr);
1292 8 : assert(pOwnerKey);
1293 8 : ownerKey = *pOwnerKey;
1294 8 : ownerAddressStr = EncodeDestination(ownerAddr);
1295 : }
1296 :
1297 : // Use new opcode after v6.0 enforcement (!TODO: remove after enforcement)
1298 31 : bool fV6Enforced = Params().GetConsensus().NetworkUpgradeActive(chainActive.Height(), Consensus::UPGRADE_V6_0);
1299 :
1300 : // Create the transaction
1301 31 : const bool fUseShielded = (params.size() > 5) && params[5].get_bool();
1302 31 : if (!fUseShielded) {
1303 : // Delegate transparent coins
1304 30 : CAmount nFeeRequired;
1305 30 : CScript scriptPubKey = fV6Enforced ? GetScriptForStakeDelegation(*stakeKey, ownerKey)
1306 60 : : GetScriptForStakeDelegationLOF(*stakeKey, ownerKey);
1307 60 : if (!pwallet->CreateTransaction(scriptPubKey, nValue, txNew, reservekey, nFeeRequired, strError, nullptr, (CAmount)0, fUseDelegated)) {
1308 0 : if (nValue + nFeeRequired > currBalance)
1309 0 : strError = strprintf("Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds!", FormatMoney(nFeeRequired));
1310 0 : LogPrintf("%s : %s\n", __func__, strError);
1311 0 : throw JSONRPCError(RPC_WALLET_ERROR, strError);
1312 : }
1313 : } else {
1314 : // Delegate shield coins
1315 1 : const Consensus::Params& consensus = Params().GetConsensus();
1316 : // Check network status
1317 1 : if (sporkManager.IsSporkActive(SPORK_20_SAPLING_MAINTENANCE)) {
1318 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "SHIELD in maintenance (SPORK 20)");
1319 : }
1320 4 : std::vector<SendManyRecipient> recipients = {SendManyRecipient(ownerKey, *stakeKey, nValue, fV6Enforced)};
1321 1 : SaplingOperation operation(consensus, pwallet);
1322 1 : OperationResult res = operation.setSelectShieldedCoins(true)
1323 : ->setRecipients(recipients)
1324 2 : ->build();
1325 1 : if (!res) throw JSONRPCError(RPC_WALLET_ERROR, res.getError());
1326 1 : txNew = MakeTransactionRef(operation.getFinalTx());
1327 : }
1328 :
1329 31 : UniValue result(UniValue::VOBJ);
1330 31 : result.pushKV("owner_address", ownerAddressStr);
1331 62 : result.pushKV("staker_address", EncodeDestination(stakeAddr, true, false));
1332 62 : return result;
1333 : }
1334 :
1335 31 : UniValue delegatestake(const JSONRPCRequest& request)
1336 : {
1337 31 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
1338 :
1339 31 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
1340 0 : return NullUniValue;
1341 :
1342 31 : if (request.fHelp || request.params.size() < 2 || request.params.size() > 7)
1343 0 : throw std::runtime_error(
1344 : "delegatestake \"staking_addr\" amount ( \"owner_addr\" ext_owner include_delegated from_shield force )\n"
1345 0 : "\nDelegate an amount to a given address for cold staking. The amount is a real and is rounded to the nearest 0.00000001\n" +
1346 0 : HelpRequiringPassphrase(pwallet) + "\n"
1347 :
1348 : "\nArguments:\n"
1349 : "1. \"staking_addr\" (string, required) The pivx staking address to delegate.\n"
1350 : "2. \"amount\" (numeric, required) The amount in PIV to delegate for staking. eg 100\n"
1351 : "3. \"owner_addr\" (string, optional) The pivx address corresponding to the key that will be able to spend the stake.\n"
1352 : " If not provided, or empty string, a new wallet address is generated.\n"
1353 : "4. \"ext_owner\" (boolean, optional, default = false) use the provided 'owneraddress' anyway, even if not present in this wallet.\n"
1354 : " WARNING: The owner of the keys to 'owneraddress' will be the only one allowed to spend these coins.\n"
1355 : "5. \"include_delegated\" (boolean, optional, default = false) include already delegated inputs if needed.\n"
1356 : "6. \"from_shield\" (boolean, optional, default = false) delegate shield funds.\n"
1357 : "7. \"force\" (boolean, optional, default = false) ONLY FOR TESTING: force the creation even if SPORK 17 is disabled.\n"
1358 :
1359 : "\nResult:\n"
1360 : "{\n"
1361 : " \"owner_address\": \"xxx\" (string) The owner (delegator) owneraddress.\n"
1362 : " \"staker_address\": \"xxx\" (string) The cold staker (delegate) stakingaddress.\n"
1363 : " \"txid\": \"xxx\" (string) The stake delegation transaction id.\n"
1364 : "}\n"
1365 :
1366 0 : "\nExamples:\n" +
1367 0 : HelpExampleCli("delegatestake", "\"S1t2a3kab9c8c71VA78xxxy4MxZg6vgeS6\" 100") +
1368 0 : HelpExampleCli("delegatestake", "\"S1t2a3kab9c8c71VA78xxxy4MxZg6vgeS6\" 1000 \"DMJRSsuU9zfyrvxVaAEFQqK4MxZg34fk\"") +
1369 0 : HelpExampleRpc("delegatestake", "\"S1t2a3kab9c8c71VA78xxxy4MxZg6vgeS6\", 1000, \"DMJRSsuU9zfyrvxVaAEFQqK4MxZg34fk\""));
1370 :
1371 31 : EnsureWalletIsUnlocked(pwallet);
1372 :
1373 : // Make sure the results are valid at least up to the most recent block
1374 : // the user could have gotten from another RPC command prior to now
1375 31 : pwallet->BlockUntilSyncedToCurrentChain();
1376 :
1377 90 : LOCK2(cs_main, pwallet->cs_wallet);
1378 :
1379 31 : CTransactionRef wtx;
1380 62 : CReserveKey reservekey(pwallet);
1381 59 : UniValue ret = CreateColdStakeDelegation(pwallet, request.params, wtx, reservekey);
1382 :
1383 58 : const CWallet::CommitResult& res = pwallet->CommitTransaction(wtx, reservekey, g_connman.get());
1384 29 : if (res.status != CWallet::CommitStatus::OK)
1385 2 : throw JSONRPCError(RPC_WALLET_ERROR, res.ToString());
1386 :
1387 57 : ret.pushKV("txid", wtx->GetHash().GetHex());
1388 28 : return ret;
1389 : }
1390 :
1391 2 : UniValue rawdelegatestake(const JSONRPCRequest& request)
1392 : {
1393 2 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
1394 :
1395 2 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
1396 0 : return NullUniValue;
1397 :
1398 2 : if (request.fHelp || request.params.size() < 2 || request.params.size() > 7)
1399 0 : throw std::runtime_error(
1400 : "rawdelegatestake \"staking_addr\" amount ( \"owner_addr\" ext_owner include_delegated from_shield )\n"
1401 : "\nDelegate an amount to a given address for cold staking. The amount is a real and is rounded to the nearest 0.00000001\n"
1402 0 : "\nDelegate transaction is returned as json object." +
1403 0 : HelpRequiringPassphrase(pwallet) + "\n"
1404 :
1405 : "\nArguments:\n"
1406 : "1. \"staking_addr\" (string, required) The pivx staking address to delegate.\n"
1407 : "2. \"amount\" (numeric, required) The amount in PIV to delegate for staking. eg 100\n"
1408 : "3. \"owner_addr\" (string, optional) The pivx address corresponding to the key that will be able to spend the stake.\n"
1409 : " If not provided, or empty string, a new wallet address is generated.\n"
1410 : "4. \"ext_owner\" (boolean, optional, default = false) use the provided 'owneraddress' anyway, even if not present in this wallet.\n"
1411 : " WARNING: The owner of the keys to 'owneraddress' will be the only one allowed to spend these coins.\n"
1412 : "5. \"include_delegated\" (boolean, optional, default = false) include already delegated inputs if needed.\n"
1413 : "6. \"from_shield\" (boolean, optional, default = false) delegate shield funds.\n"
1414 : "7. \"force\" (boolean, optional, default = false) ONLY FOR TESTING: force the creation even if SPORK 17 is disabled (for tests).\n"
1415 :
1416 : "\nResult:\n"
1417 : "\"transaction\" (string) hex string of the transaction\n"
1418 :
1419 0 : "\nExamples:\n" +
1420 0 : HelpExampleCli("rawdelegatestake", "\"S1t2a3kab9c8c71VA78xxxy4MxZg6vgeS6\" 100") +
1421 0 : HelpExampleCli("rawdelegatestake", "\"S1t2a3kab9c8c71VA78xxxy4MxZg6vgeS6\" 1000 \"DMJRSsuU9zfyrvxVaAEFQqK4MxZg34fk\"") +
1422 0 : HelpExampleRpc("rawdelegatestake", "\"S1t2a3kab9c8c71VA78xxxy4MxZg6vgeS6\", 1000, \"DMJRSsuU9zfyrvxVaAEFQqK4MxZg34fk\""));
1423 :
1424 2 : EnsureWalletIsUnlocked(pwallet);
1425 :
1426 : // Make sure the results are valid at least up to the most recent block
1427 : // the user could have gotten from another RPC command prior to now
1428 2 : pwallet->BlockUntilSyncedToCurrentChain();
1429 :
1430 6 : LOCK2(cs_main, pwallet->cs_wallet);
1431 :
1432 2 : CTransactionRef wtx;
1433 4 : CReserveKey reservekey(pwallet);
1434 2 : CreateColdStakeDelegation(pwallet, request.params, wtx, reservekey);
1435 :
1436 4 : return EncodeHexTx(*wtx);
1437 : }
1438 :
1439 :
1440 49 : static CAmount getBalanceShieldedAddr(CWallet* const pwallet, Optional<libzcash::SaplingPaymentAddress>& filterAddress, int minDepth = 1, bool ignoreUnspendable=true) {
1441 49 : CAmount balance = 0;
1442 49 : std::vector<SaplingNoteEntry> saplingEntries;
1443 147 : LOCK2(cs_main, pwallet->cs_wallet);
1444 49 : pwallet->GetSaplingScriptPubKeyMan()->GetFilteredNotes(saplingEntries, filterAddress, minDepth, true, ignoreUnspendable);
1445 121 : for (auto & entry : saplingEntries) {
1446 72 : balance += CAmount(entry.note.value());
1447 : }
1448 98 : return balance;
1449 : }
1450 :
1451 54 : UniValue getshieldbalance(const JSONRPCRequest& request)
1452 : {
1453 54 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
1454 :
1455 54 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
1456 0 : return NullUniValue;
1457 :
1458 54 : if (request.fHelp || request.params.size() > 3)
1459 0 : throw std::runtime_error(
1460 : "getshieldbalance \"address\" ( minconf include_watchonly )\n"
1461 : "\nReturn the total shield value of funds stored in the node's wallet or if an address was given,"
1462 : "\nreturns the balance of the shield addr belonging to the node's wallet.\n"
1463 : "\nCAUTION: If the wallet contains any addresses for which it only has incoming viewing keys,"
1464 : "\nthe returned private balance may be larger than the actual balance, because spends cannot"
1465 : "\nbe detected with incoming viewing keys.\n"
1466 :
1467 : "\nArguments:\n"
1468 : "1. \"address\" (string, optional) The selected address. If non empty nor \"*\", it must be a Sapling address\n"
1469 : "2. minconf (numeric, optional, default=1) Only include private and transparent transactions confirmed at least this many times.\n"
1470 : "3. include_watchonly (bool, optional, default=false) Also include balance in watchonly addresses (see 'importaddress' and 'importsaplingviewingkey')\n"
1471 :
1472 : "\nResult:\n"
1473 : "amount (numeric) the total balance of shield funds (in Sapling addresses)\n"
1474 :
1475 : "\nExamples:\n"
1476 : "\nThe total amount in the wallet\n"
1477 0 : + HelpExampleCli("getshieldbalance", "")
1478 0 : + HelpExampleCli("getshieldbalance", "ptestsapling1h0w73csah2aq0a32h42kr7tq4htlt5wfn4ejxfnm56f6ehjvek7k4e244g6v8v3pgylmz5ea8jh") +
1479 : "\nThe total amount in the wallet at least 5 blocks confirmed\n"
1480 0 : + HelpExampleCli("getshieldbalance", "\"*\" \"5\"") +
1481 : "\nAs a json rpc call\n"
1482 0 : + HelpExampleRpc("getshieldbalance", "\"*\" \"5\"")
1483 0 : );
1484 :
1485 157 : LOCK2(cs_main, pwallet->cs_wallet);
1486 :
1487 108 : Optional<libzcash::SaplingPaymentAddress> address;
1488 54 : if (request.params.size() > 0) {
1489 94 : std::string addressStr = request.params[0].get_str();
1490 94 : if (addressStr.empty() || addressStr != "*") {
1491 84 : address = KeyIO::DecodeSaplingPaymentAddress(addressStr);
1492 42 : if (!address) {
1493 6 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid shield address");
1494 : }
1495 : }
1496 : }
1497 :
1498 51 : const int nMinDepth = request.params.size() > 1 ? request.params[1].get_int() : 1;
1499 9 : if (nMinDepth < 0) {
1500 4 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Minimum number of confirmations cannot be less than 0");
1501 : }
1502 :
1503 49 : const bool fIncludeWatchonly = request.params.size() > 2 && request.params[2].get_bool();
1504 49 : CAmount nBalance = getBalanceShieldedAddr(pwallet, address, nMinDepth, !fIncludeWatchonly);
1505 49 : return ValueFromAmount(nBalance);
1506 : }
1507 :
1508 11 : UniValue viewshieldtransaction(const JSONRPCRequest& request)
1509 : {
1510 11 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
1511 :
1512 11 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
1513 0 : return NullUniValue;
1514 :
1515 11 : if (request.fHelp || request.params.size() != 1)
1516 0 : throw std::runtime_error(
1517 : "viewshieldtransaction \"txid\"\n"
1518 : "\nGet detailed shield information about in-wallet transaction \"txid\"\n"
1519 0 : + HelpRequiringPassphrase(pwallet) + "\n"
1520 :
1521 : "\nArguments:\n"
1522 : "1. \"txid\" (string, required) The transaction id\n"
1523 : "\nResult:\n"
1524 : "{\n"
1525 : " \"txid\" : \"transactionid\", (string) The transaction id\n"
1526 0 : " \"fee\" : x.xxx, (numeric) The transaction fee in " + CURRENCY_UNIT + "\n"
1527 : " \"spends\" : [\n"
1528 : " {\n"
1529 : " \"spend\" : n, (numeric, sapling) the index of the spend within vShieldedSpend\n"
1530 : " \"txidPrev\" : \"transactionid\", (string) The id for the transaction this note was created in\n"
1531 : " \"outputPrev\" : n, (numeric, sapling) the index of the output within the vShieldedOutput\n"
1532 : " \"address\" : \"pivxaddress\", (string) The PIVX address involved in the transaction\n"
1533 0 : " \"value\" : x.xxx (numeric) The amount in " + CURRENCY_UNIT + "\n"
1534 : " \"valueSat\" : xxxx (numeric) The amount in satoshis\n"
1535 : " }\n"
1536 : " ,...\n"
1537 : " ],\n"
1538 : " \"outputs\" : [\n"
1539 : " {\n"
1540 : " \"output\" : n, (numeric, sapling) the index of the output within the vShieldedOutput\n"
1541 : " \"address\" : \"pivxaddress\", (string) The PIVX address involved in the transaction\n"
1542 : " \"outgoing\" : true|false (boolean, sapling) True if the output is not for an address in the wallet\n"
1543 0 : " \"value\" : x.xxx (numeric) The amount in " + CURRENCY_UNIT + "\n"
1544 : " \"valueSat\" : xxxx (numeric) The amount in satoshis\n"
1545 : " \"memo\" : \"hexmemo\", (string) Hexadecimal string representation of the memo field\n"
1546 : " \"memoStr\" : \"memo\", (string) Only returned if memo contains valid UTF-8 text.\n"
1547 : " }\n"
1548 : " ,...\n"
1549 : " ],\n"
1550 : "}\n"
1551 :
1552 : "\nExamples:\n"
1553 0 : + HelpExampleCli("viewshieldtransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"")
1554 0 : + HelpExampleRpc("viewshieldtransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"")
1555 0 : );
1556 :
1557 11 : if (!pwallet->HasSaplingSPKM()) {
1558 0 : throw JSONRPCError(RPC_WALLET_ERROR, "Sapling wallet not initialized.");
1559 : }
1560 :
1561 11 : EnsureWalletIsUnlocked(pwallet);
1562 :
1563 : // Make sure the results are valid at least up to the most recent block
1564 : // the user could have gotten from another RPC command prior to now
1565 11 : pwallet->BlockUntilSyncedToCurrentChain();
1566 :
1567 33 : LOCK2(cs_main, pwallet->cs_wallet);
1568 :
1569 11 : uint256 hash(ParseHashV(request.params[0], "txid"));
1570 :
1571 22 : UniValue entry(UniValue::VOBJ);
1572 11 : auto it = pwallet->mapWallet.find(hash);
1573 11 : if (it == pwallet->mapWallet.end())
1574 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id");
1575 11 : const CWalletTx& wtx = it->second;
1576 :
1577 11 : if (!wtx.tx->IsShieldedTx()) {
1578 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid transaction, no shield data available");
1579 : }
1580 :
1581 22 : entry.pushKV("txid", hash.GetHex());
1582 :
1583 22 : UniValue spends(UniValue::VARR);
1584 11 : UniValue outputs(UniValue::VARR);
1585 :
1586 29 : auto addMemo = [](UniValue& entry, const Optional<std::array<unsigned char, ZC_MEMO_SIZE>>& optMemo) {
1587 : // empty memo
1588 18 : if (!static_cast<bool>(optMemo)) {
1589 15 : const std::array<unsigned char, 1> memo {0xF6};
1590 30 : entry.pushKV("memo", HexStr(memo));
1591 15 : return;
1592 : }
1593 3 : const auto& memo = *optMemo;
1594 3 : auto end = FindFirstNonZero(memo.rbegin(), memo.rend());
1595 9 : entry.pushKV("memo", HexStr(std::vector<unsigned char>(memo.begin(), end.base())));
1596 : // If the leading byte is 0xF4 or lower, the memo field should be interpreted as a
1597 : // UTF-8-encoded text string.
1598 3 : if (memo[0] <= 0xf4) {
1599 6 : std::string memoStr(memo.begin(), end.base());
1600 3 : if (IsValidUTF8(memoStr)) {
1601 6 : entry.pushKV("memoStr", memoStr);
1602 : }
1603 : }
1604 : };
1605 :
1606 11 : auto sspkm = pwallet->GetSaplingScriptPubKeyMan();
1607 :
1608 : // Collect OutgoingViewingKeys for recovering output information
1609 22 : std::set<uint256> ovks;
1610 : // Get the common OVK for recovering t->shield outputs.
1611 : // If not already databased, a new one will be generated from the HD seed.
1612 : // It is safe to do it here, as the wallet is unlocked.
1613 11 : ovks.insert(sspkm->getCommonOVK());
1614 :
1615 : // Sapling spends
1616 20 : for (size_t i = 0; i < wtx.tx->sapData->vShieldedSpend.size(); ++i) {
1617 9 : const auto& spend = wtx.tx->sapData->vShieldedSpend[i];
1618 :
1619 : // Fetch the note that is being spent
1620 9 : auto res = sspkm->mapSaplingNullifiersToNotes.find(spend.nullifier);
1621 9 : if (res == sspkm->mapSaplingNullifiersToNotes.end()) {
1622 0 : continue;
1623 : }
1624 9 : const auto& op = res->second;
1625 18 : std::string addrStr = "unknown";
1626 18 : UniValue amountStr = UniValue("unknown");
1627 9 : CAmount amount = 0;
1628 9 : auto wtxPrevIt = pwallet->mapWallet.find(op.hash);
1629 9 : if (wtxPrevIt != pwallet->mapWallet.end()) {
1630 9 : const auto ndIt = wtxPrevIt->second.mapSaplingNoteData.find(op);
1631 9 : if (ndIt != wtxPrevIt->second.mapSaplingNoteData.end()) {
1632 : // get cached address and amount
1633 9 : if (ndIt->second.address) {
1634 9 : addrStr = KeyIO::EncodePaymentAddress(*(ndIt->second.address));
1635 : }
1636 9 : if (ndIt->second.amount) {
1637 9 : amount = *(ndIt->second.amount);
1638 9 : amountStr = ValueFromAmount(amount);
1639 : }
1640 : }
1641 : }
1642 :
1643 18 : UniValue entry_(UniValue::VOBJ);
1644 9 : entry_.pushKV("spend", (int)i);
1645 18 : entry_.pushKV("txidPrev", op.hash.GetHex());
1646 9 : entry_.pushKV("outputPrev", (int)op.n);
1647 9 : entry_.pushKV("address", addrStr);
1648 9 : entry_.pushKV("value", amountStr);
1649 9 : entry_.pushKV("valueSat", amount);
1650 9 : spends.push_back(entry_);
1651 : }
1652 :
1653 : // Sapling outputs
1654 29 : for (uint32_t i = 0; i < wtx.tx->sapData->vShieldedOutput.size(); ++i) {
1655 18 : auto op = SaplingOutPoint(hash, i);
1656 18 : auto it = wtx.mapSaplingNoteData.find(op);
1657 18 : if (it == wtx.mapSaplingNoteData.end()) continue;
1658 18 : const auto& nd = it->second;
1659 :
1660 18 : const bool isOutgoing = !nd.IsMyNote();
1661 36 : std::string addrStr = "unknown";
1662 36 : UniValue amountStr = UniValue("unknown");
1663 18 : CAmount amount = 0;
1664 18 : if (nd.address) {
1665 18 : addrStr = KeyIO::EncodePaymentAddress(*(nd.address));
1666 : }
1667 18 : if (nd.amount) {
1668 18 : amount = *(nd.amount);
1669 18 : amountStr = ValueFromAmount(amount);
1670 : }
1671 :
1672 36 : UniValue entry_(UniValue::VOBJ);
1673 18 : entry_.pushKV("output", (int)op.n);
1674 18 : entry_.pushKV("outgoing", isOutgoing);
1675 18 : entry_.pushKV("address", addrStr);
1676 18 : entry_.pushKV("value", amountStr);
1677 18 : entry_.pushKV("valueSat", amount);
1678 18 : addMemo(entry_, nd.memo);
1679 :
1680 18 : outputs.push_back(entry_);
1681 : }
1682 :
1683 22 : entry.pushKV("fee", FormatMoney(pcoinsTip->GetValueIn(*wtx.tx) - wtx.tx->GetValueOut()));
1684 11 : entry.pushKV("spends", spends);
1685 11 : entry.pushKV("outputs", outputs);
1686 :
1687 11 : return entry;
1688 : }
1689 :
1690 1088 : static SaplingOperation CreateShieldedTransaction(CWallet* const pwallet, const JSONRPCRequest& request)
1691 : {
1692 2176 : LOCK2(cs_main, pwallet->cs_wallet);
1693 1088 : SaplingOperation operation(Params().GetConsensus(), pwallet);
1694 :
1695 : // Param 0: source of funds. Can either be a valid address, sapling address,
1696 : // or the string "from_transparent"|"from_trans_cold"|"from_shield"
1697 1088 : bool fromSapling = false;
1698 2176 : std::string sendFromStr = request.params[0].get_str();
1699 1088 : if (sendFromStr == "from_transparent") {
1700 : // send from any transparent address
1701 1035 : operation.setSelectTransparentCoins(true);
1702 53 : } else if (sendFromStr == "from_trans_cold") {
1703 : // send from any transparent address + delegations
1704 0 : operation.setSelectTransparentCoins(true, true);
1705 53 : } else if (sendFromStr == "from_shield") {
1706 : // send from any shield address
1707 12 : operation.setSelectShieldedCoins(true);
1708 12 : fromSapling = true;
1709 : } else {
1710 82 : CTxDestination fromTAddressDest = DecodeDestination(sendFromStr);
1711 41 : if (!IsValidDestination(fromTAddressDest)) {
1712 54 : auto res = KeyIO::DecodePaymentAddress(sendFromStr);
1713 27 : if (!IsValidPaymentAddress(res)) {
1714 2 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid from address, should be a taddr or shield addr.");
1715 : }
1716 26 : libzcash::SaplingPaymentAddress fromShieldedAddress = *boost::get<libzcash::SaplingPaymentAddress>(&res);
1717 26 : if (!pwallet->HaveSpendingKeyForPaymentAddress(fromShieldedAddress)) {
1718 2 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "From address does not belong to this node, shield addr spending key not found.");
1719 : }
1720 : // send from user-supplied shield address
1721 25 : operation.setFromAddress(fromShieldedAddress);
1722 25 : fromSapling = true;
1723 : } else {
1724 : // send from user-supplied transparent address
1725 14 : operation.setFromAddress(fromTAddressDest);
1726 : }
1727 : }
1728 :
1729 : // Param 4: subtractFeeFromAmount addresses
1730 2172 : const UniValue subtractFeeFromAmount = request.params[4];
1731 :
1732 : // Param 1: array of outputs
1733 1092 : UniValue outputs = request.params[1].get_array();
1734 1086 : if (outputs.empty())
1735 2 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, amounts array is empty.");
1736 :
1737 : // Keep track of addresses to spot duplicates
1738 2170 : std::set<std::string> setAddress;
1739 : // Recipients
1740 2170 : std::vector<SendManyRecipient> recipients;
1741 1085 : CAmount nTotalOut = 0;
1742 1085 : bool containsSaplingOutput = false;
1743 :
1744 2238 : for (const UniValue& o : outputs.getValues()) {
1745 1153 : if (!o.isObject())
1746 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected object");
1747 :
1748 : // sanity check, report error if unknown key-value pairs
1749 3463 : for (const std::string& name_ : o.getKeys()) {
1750 3471 : if (name_ != "address" && name_ != "amount" && name_!="memo")
1751 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, unknown key: ")+name_);
1752 : }
1753 :
1754 3459 : std::string address = find_value(o, "address").get_str();
1755 2306 : CTxDestination taddr = DecodeDestination(address);
1756 2306 : Optional<libzcash::SaplingPaymentAddress> saddr;
1757 :
1758 1153 : if (!IsValidDestination(taddr)) {
1759 2262 : const auto& addr = KeyIO::DecodePaymentAddress(address);
1760 1131 : if (IsValidPaymentAddress(addr)) {
1761 1131 : saddr = *(boost::get<libzcash::SaplingPaymentAddress>(&addr));
1762 1131 : containsSaplingOutput = true;
1763 : } else {
1764 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, unknown address format: ")+address );
1765 : }
1766 : }
1767 :
1768 1153 : if (setAddress.count(address))
1769 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ")+address);
1770 1153 : setAddress.insert(address);
1771 :
1772 2306 : UniValue memoValue = find_value(o, "memo");
1773 2306 : std::string memo;
1774 1153 : if (!memoValue.isNull()) {
1775 4 : memo = memoValue.get_str();
1776 4 : if (!saddr) {
1777 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Memo cannot be used with a taddr. It can only be used with a shield addr.");
1778 : }
1779 4 : if (memo.length() > ZC_MEMO_SIZE*2) {
1780 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid parameter, size of memo is larger than maximum allowed %d", ZC_MEMO_SIZE ));
1781 : }
1782 : }
1783 :
1784 2306 : UniValue av = find_value(o, "amount");
1785 1153 : CAmount nAmount = AmountFromValue(av);
1786 1153 : if (nAmount < 0)
1787 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, amount must be positive");
1788 :
1789 1153 : bool fSubtractFeeFromAmount = false;
1790 1155 : for (unsigned int idx = 0; idx < subtractFeeFromAmount.size(); idx++) {
1791 6 : const UniValue& addr = subtractFeeFromAmount[idx];
1792 6 : if (addr.get_str() == address) {
1793 4 : fSubtractFeeFromAmount = true;
1794 4 : break;
1795 : }
1796 : }
1797 :
1798 1153 : if (saddr) {
1799 1131 : recipients.emplace_back(*saddr, nAmount, memo, fSubtractFeeFromAmount);
1800 : } else {
1801 22 : recipients.emplace_back(taddr, nAmount, fSubtractFeeFromAmount);
1802 : }
1803 :
1804 1153 : nTotalOut += nAmount;
1805 : }
1806 :
1807 : // Check network status
1808 1085 : if (sporkManager.IsSporkActive(SPORK_20_SAPLING_MAINTENANCE)) {
1809 : // If Sapling is disabled, do not allow sending from or sending to Sapling addresses.
1810 1 : if (fromSapling || containsSaplingOutput) {
1811 2 : throw JSONRPCError(RPC_INVALID_PARAMETER, "SHIELD in maintenance (SPORK 20)");
1812 : }
1813 : }
1814 :
1815 : // Now check the transaction
1816 2168 : auto opResult = CheckTransactionSize(recipients, !fromSapling);
1817 1084 : if (!opResult) {
1818 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, opResult.getError());
1819 : }
1820 :
1821 : // Param 2: Minimum confirmations
1822 1084 : int nMinDepth = request.params.size() > 2 ? request.params[2].get_int() : 1;
1823 72 : if (nMinDepth < 0) {
1824 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Minimum number of confirmations cannot be less than 0");
1825 : }
1826 :
1827 : // Param 3: Fee
1828 : // If not set, SaplingOperation will set the minimum fee (based on minRelayFee and tx size)
1829 1084 : if (request.params.size() > 3) {
1830 51 : CAmount nFee = AmountFromValue(request.params[3]);
1831 51 : if (nFee < 0) {
1832 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid fee. Must be positive.");
1833 51 : } else if (nFee > 0) {
1834 : // If the user-selected fee is not enough (or too much), the build operation will fail.
1835 32 : operation.setFee(nFee);
1836 : }
1837 : // If nFee=0 leave the default (build operation will compute the minimum fee)
1838 : }
1839 :
1840 1084 : if (fromSapling && nMinDepth == 0) {
1841 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Minconf cannot be zero when sending from shield addr");
1842 : }
1843 :
1844 1084 : if (nMinDepth < 0) {
1845 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Minconf cannot be negative");
1846 : }
1847 :
1848 : // Build the send operation
1849 1084 : OperationResult res = operation.setMinDepth(nMinDepth)
1850 : ->setRecipients(recipients)
1851 2168 : ->build();
1852 1088 : if (!res) throw JSONRPCError(RPC_WALLET_ERROR, res.getError());
1853 2160 : return operation;
1854 : }
1855 :
1856 1055 : UniValue shieldsendmany(const JSONRPCRequest& request)
1857 : {
1858 1055 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
1859 :
1860 1055 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
1861 0 : return NullUniValue;
1862 :
1863 1055 : if (request.fHelp || request.params.size() < 2 || request.params.size() > 5)
1864 2 : throw std::runtime_error(
1865 : "shieldsendmany \"fromaddress\" [{\"address\":... ,\"amount\":...},...] ( minconf fee subtract_fee_from )\n"
1866 : "\nSend to many recipients. Amounts are decimal numbers with at most 8 digits of precision."
1867 : "\nChange generated from a transparent addr flows to a new transparent addr address, while change generated from a shield addr returns to itself."
1868 : "\nWhen sending coinbase UTXOs to a shield addr, change is not allowed. The entire value of the UTXO(s) must be consumed."
1869 4 : + HelpRequiringPassphrase(pwallet) + "\n"
1870 :
1871 : "\nArguments:\n"
1872 : "1. \"fromaddress\" (string, required) The transparent addr or shield addr to send the funds from.\n"
1873 : " It can also be the string \"from_transparent\"|\"from_shield\" to send the funds\n"
1874 : " from any transparent|shield address available.\n"
1875 : " Additionally, it can be the string \"from_trans_cold\" to select transparent funds,\n"
1876 : " possibly including delegated coins, if needed.\n"
1877 : "2. \"amounts\" (array, required) An array of json objects representing the amounts to send.\n"
1878 : " [{\n"
1879 : " \"address\":address (string, required) The address is a transparent addr or shield addr\n"
1880 4 : " \"amount\":amount (numeric, required) The numeric amount in " + "PIV" + " is the value\n"
1881 : " \"memo\":memo (string, optional) If the address is a shield addr, message string of max 512 bytes\n"
1882 : " }, ... ]\n"
1883 : "3. minconf (numeric, optional, default=1) Only use funds confirmed at least this many times.\n"
1884 : "4. fee (numeric, optional), The fee amount to attach to this transaction.\n"
1885 : " If not specified, or set to 0, the wallet will try to compute the minimum possible fee for a shield TX,\n"
1886 : " based on the expected transaction size and the current value of -minRelayTxFee.\n"
1887 : "5. subtract_fee_from (array, optional) A json array with addresses.\n"
1888 : " The fee will be equally deducted from the amount of each selected address.\n"
1889 : " Those recipients will receive less PIV than you enter in their corresponding amount field.\n"
1890 : " If no addresses are specified here, the sender pays the fee.\n"
1891 : " [\n"
1892 : " \"address\" (string) Subtract fee from this address\n"
1893 : " ,...\n"
1894 : " ]\n"
1895 : "\nResult:\n"
1896 : "\"id\" (string) transaction hash in the network\n"
1897 : "\nExamples:\n"
1898 10 : + HelpExampleCli("shieldsendmany",
1899 : "\"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\" '[{\"address\": \"ps1ra969yfhvhp73rw5ak2xvtcm9fkuqsnmad7qln79mphhdrst3lwu9vvv03yuyqlh42p42st47qd\" ,\"amount\": 5.0}]'")
1900 8 : + HelpExampleRpc("shieldsendmany",
1901 : "\"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\", [{\"address\": \"ps1ra969yfhvhp73rw5ak2xvtcm9fkuqsnmad7qln79mphhdrst3lwu9vvv03yuyqlh42p42st47qd\" ,\"amount\": 5.0}]")
1902 6 : );
1903 :
1904 1053 : EnsureWalletIsUnlocked(pwallet);
1905 :
1906 : // Make sure the results are valid at least up to the most recent block
1907 : // the user could have gotten from another RPC command prior to now
1908 1053 : pwallet->BlockUntilSyncedToCurrentChain();
1909 :
1910 1054 : SaplingOperation operation = CreateShieldedTransaction(pwallet, request);
1911 2094 : std::string txHash;
1912 2094 : auto res = operation.send(txHash);
1913 1047 : if (!res)
1914 2 : throw JSONRPCError(RPC_WALLET_ERROR, res.getError());
1915 1046 : return txHash;
1916 : }
1917 :
1918 18 : UniValue rawshieldsendmany(const JSONRPCRequest& request)
1919 : {
1920 18 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
1921 :
1922 18 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
1923 0 : return NullUniValue;
1924 :
1925 18 : if (request.fHelp || request.params.size() < 2 || request.params.size() > 4)
1926 0 : throw std::runtime_error(
1927 : "rawshieldsendmany \"fromaddress\" [{\"address\":... ,\"amount\":...},...] ( minconf fee )\n"
1928 : "\nCreates a transaction sending to many recipients (without committing it), and returns the hex string."
1929 : "\nAmounts are decimal numbers with at most 8 digits of precision."
1930 : "\nChange generated from a transparent addr flows to a new transparent addr address, while change generated from a shield addr returns to itself."
1931 : "\nWhen sending coinbase UTXOs to a shield addr, change is not allowed. The entire value of the UTXO(s) must be consumed."
1932 0 : + HelpRequiringPassphrase(pwallet) + "\n"
1933 :
1934 : "\nArguments:\n"
1935 : "1. \"fromaddress\" (string, required) The transparent addr or shield addr to send the funds from.\n"
1936 : " It can also be the string \"from_transparent\"|\"from_shield\" to send the funds\n"
1937 : " from any transparent|shield address available.\n"
1938 : " Additionally, it can be the string \"from_trans_cold\" to select transparent funds,\n"
1939 : " possibly including delegated coins, if needed.\n"
1940 : "2. \"amounts\" (array, required) An array of json objects representing the amounts to send.\n"
1941 : " [{\n"
1942 : " \"address\":address (string, required) The address is a transparent addr or shield addr\n"
1943 0 : " \"amount\":amount (numeric, required) The numeric amount in " + "PIV" + " is the value\n"
1944 : " \"memo\":memo (string, optional) If the address is a shield addr, message string of max 512 bytes\n"
1945 : " }, ... ]\n"
1946 : "3. minconf (numeric, optional, default=1) Only use funds confirmed at least this many times.\n"
1947 : "4. fee (numeric, optional), The fee amount to attach to this transaction.\n"
1948 : " If not specified, the wallet will try to compute the minimum possible fee for a shield TX,\n"
1949 : " based on the expected transaction size and the current value of -minRelayTxFee.\n"
1950 : "\nResult:\n"
1951 : "\"transaction\" (string) hex string of the transaction\n"
1952 :
1953 : "\nExamples:\n"
1954 0 : + HelpExampleCli("rawshieldsendmany",
1955 : "\"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\" '[{\"address\": \"ps1ra969yfhvhp73rw5ak2xvtcm9fkuqsnmad7qln79mphhdrst3lwu9vvv03yuyqlh42p42st47qd\" ,\"amount\": 5.0}]'")
1956 0 : + HelpExampleRpc("rawshieldsendmany",
1957 : "\"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\", [{\"address\": \"ps1ra969yfhvhp73rw5ak2xvtcm9fkuqsnmad7qln79mphhdrst3lwu9vvv03yuyqlh42p42st47qd\" ,\"amount\": 5.0}]")
1958 0 : );
1959 :
1960 18 : EnsureWalletIsUnlocked(pwallet);
1961 :
1962 : // Make sure the results are valid at least up to the most recent block
1963 : // the user could have gotten from another RPC command prior to now
1964 18 : pwallet->BlockUntilSyncedToCurrentChain();
1965 :
1966 34 : CTransaction tx = CreateShieldedTransaction(pwallet, request).getFinalTx();
1967 32 : return EncodeHexTx(tx);
1968 : }
1969 :
1970 1 : UniValue listaddressgroupings(const JSONRPCRequest& request)
1971 : {
1972 1 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
1973 :
1974 1 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
1975 0 : return NullUniValue;
1976 :
1977 1 : if (request.fHelp || !request.params.empty())
1978 0 : throw std::runtime_error(
1979 : "listaddressgroupings\n"
1980 : "\nLists groups of addresses which have had their common ownership\n"
1981 : "made public by common use as inputs or as the resulting change\n"
1982 : "in past transactions\n"
1983 :
1984 : "\nResult:\n"
1985 : "[\n"
1986 : " [\n"
1987 : " [\n"
1988 : " \"pivxaddress\", (string) The pivx address\n"
1989 : " amount, (numeric) The amount in PIV\n"
1990 : " \"label\" (string, optional) The label\n"
1991 : " ]\n"
1992 : " ,...\n"
1993 : " ]\n"
1994 : " ,...\n"
1995 : "]\n"
1996 :
1997 0 : "\nExamples:\n" +
1998 0 : HelpExampleCli("listaddressgroupings", "") + HelpExampleRpc("listaddressgroupings", ""));
1999 :
2000 : // Make sure the results are valid at least up to the most recent block
2001 : // the user could have gotten from another RPC command prior to now
2002 1 : pwallet->BlockUntilSyncedToCurrentChain();
2003 :
2004 3 : LOCK2(cs_main, pwallet->cs_wallet);
2005 :
2006 2 : UniValue jsonGroupings(UniValue::VARR);
2007 2 : std::map<CTxDestination, CAmount> balances = pwallet->GetAddressBalances();
2008 3 : for (std::set<CTxDestination> grouping : pwallet->GetAddressGroupings()) {
2009 4 : UniValue jsonGrouping(UniValue::VARR);
2010 4 : for (CTxDestination address : grouping) {
2011 4 : UniValue addressInfo(UniValue::VARR);
2012 2 : addressInfo.push_back(EncodeDestination(address));
2013 2 : addressInfo.push_back(ValueFromAmount(balances[address]));
2014 4 : auto optAdd = pwallet->GetAddressBookEntry(address);
2015 2 : if (optAdd) {
2016 0 : addressInfo.push_back(optAdd->name);
2017 : }
2018 2 : jsonGrouping.push_back(addressInfo);
2019 : }
2020 2 : jsonGroupings.push_back(jsonGrouping);
2021 : }
2022 1 : return jsonGroupings;
2023 : }
2024 :
2025 18 : UniValue signmessage(const JSONRPCRequest& request)
2026 : {
2027 18 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
2028 :
2029 18 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
2030 0 : return NullUniValue;
2031 :
2032 18 : if (request.fHelp || request.params.size() != 2)
2033 0 : throw std::runtime_error(
2034 : "signmessage \"address\" \"message\"\n"
2035 0 : "\nSign a message with the private key of an address" +
2036 0 : HelpRequiringPassphrase(pwallet) + "\n"
2037 :
2038 : "\nArguments:\n"
2039 : "1. \"address\" (string, required) The pivx address to use for the private key.\n"
2040 : "2. \"message\" (string, required) The message to create a signature of.\n"
2041 :
2042 : "\nResult:\n"
2043 : "\"signature\" (string) The signature of the message encoded in base 64\n"
2044 :
2045 : "\nExamples:\n"
2046 0 : "\nUnlock the wallet for 30 seconds\n" +
2047 0 : HelpExampleCli("walletpassphrase", "\"mypassphrase\" 30") +
2048 0 : "\nCreate the signature\n" +
2049 0 : HelpExampleCli("signmessage", "\"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\" \"my message\"") +
2050 0 : "\nVerify the signature\n" +
2051 0 : HelpExampleCli("verifymessage", "\"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\" \"signature\" \"my message\"") +
2052 0 : "\nAs json rpc\n" +
2053 0 : HelpExampleRpc("signmessage", "\"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\", \"my message\""));
2054 :
2055 54 : LOCK2(cs_main, pwallet->cs_wallet);
2056 :
2057 18 : EnsureWalletIsUnlocked(pwallet);
2058 :
2059 36 : std::string strAddress = request.params[0].get_str();
2060 36 : std::string strMessage = request.params[1].get_str();
2061 :
2062 36 : CTxDestination dest = DecodeDestination(strAddress);
2063 18 : if (!IsValidDestination(dest))
2064 0 : throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
2065 :
2066 18 : const CKeyID* keyID = boost::get<CKeyID>(&dest);
2067 18 : if (!keyID)
2068 0 : throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
2069 :
2070 36 : CKey key;
2071 18 : if (!pwallet->GetKey(*keyID, key))
2072 0 : throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available");
2073 :
2074 36 : std::vector<unsigned char> vchSig;
2075 18 : if (!CMessageSigner::SignMessage(strMessage, vchSig, key)) {
2076 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed");
2077 : }
2078 :
2079 36 : return EncodeBase64(vchSig);
2080 : }
2081 :
2082 16 : UniValue getreceivedbyaddress(const JSONRPCRequest& request)
2083 : {
2084 16 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
2085 :
2086 16 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
2087 0 : return NullUniValue;
2088 :
2089 16 : if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
2090 0 : throw std::runtime_error(
2091 : "getreceivedbyaddress \"address\" ( minconf )\n"
2092 : "\nReturns the total amount received by the given pivxaddress in transactions with at least minconf confirmations.\n"
2093 :
2094 : "\nArguments:\n"
2095 : "1. \"address\" (string, required) The pivx address for transactions.\n"
2096 : "2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n"
2097 :
2098 : "\nResult:\n"
2099 : "amount (numeric) The total amount in PIV received at this address.\n"
2100 :
2101 : "\nExamples:\n"
2102 0 : "\nThe amount from transactions with at least 1 confirmation\n" +
2103 0 : HelpExampleCli("getreceivedbyaddress", "\"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\"") +
2104 0 : "\nThe amount including unconfirmed transactions, zero confirmations\n" +
2105 0 : HelpExampleCli("getreceivedbyaddress", "\"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\" 0") +
2106 0 : "\nThe amount with at least 6 confirmation, very safe\n" +
2107 0 : HelpExampleCli("getreceivedbyaddress", "\"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\" 6") +
2108 0 : "\nAs a json rpc call\n" +
2109 0 : HelpExampleRpc("getreceivedbyaddress", "\"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\", 6"));
2110 :
2111 : // Make sure the results are valid at least up to the most recent block
2112 : // the user could have gotten from another RPC command prior to now
2113 16 : pwallet->BlockUntilSyncedToCurrentChain();
2114 :
2115 47 : LOCK2(cs_main, pwallet->cs_wallet);
2116 16 : int nBlockHeight = chainActive.Height();
2117 :
2118 : // pivx address
2119 32 : CTxDestination address = DecodeDestination(request.params[0].get_str());
2120 16 : if (!IsValidDestination(address))
2121 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid PIVX address");
2122 32 : CScript scriptPubKey = GetScriptForDestination(address);
2123 16 : if (!IsMine(*pwallet, scriptPubKey))
2124 2 : throw JSONRPCError(RPC_WALLET_ERROR, "Address not found in wallet");
2125 :
2126 : // Minimum confirmations
2127 15 : int nMinDepth = 1;
2128 15 : if (request.params.size() > 1)
2129 1 : nMinDepth = request.params[1].get_int();
2130 :
2131 : // Tally
2132 15 : CAmount nAmount = 0;
2133 1027 : for (const auto& entry : pwallet->mapWallet) {
2134 1012 : const CWalletTx& wtx = entry.second;
2135 1012 : if (wtx.IsCoinBase() || !IsFinalTx(wtx.tx, nBlockHeight))
2136 960 : continue;
2137 :
2138 136 : for (const CTxOut& txout : wtx.tx->vout)
2139 84 : if (txout.scriptPubKey == scriptPubKey)
2140 11 : if (wtx.GetDepthInMainChain() >= nMinDepth)
2141 10 : nAmount += txout.nValue;
2142 : }
2143 :
2144 15 : return ValueFromAmount(nAmount);
2145 : }
2146 :
2147 :
2148 14 : UniValue getreceivedbylabel(const JSONRPCRequest& request)
2149 : {
2150 14 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
2151 :
2152 14 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
2153 0 : return NullUniValue;
2154 :
2155 14 : if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
2156 0 : throw std::runtime_error(
2157 : "getreceivedbylabel \"label\" ( minconf )\n"
2158 : "\nReturns the total amount received by addresses with <label> in transactions with at least [minconf] confirmations.\n"
2159 :
2160 : "\nArguments:\n"
2161 : "1. \"label\" (string, required) The selected label, may be the default label using \"\".\n"
2162 : "2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n"
2163 :
2164 : "\nResult:\n"
2165 : "amount (numeric) The total amount in PIV received for this label.\n"
2166 :
2167 : "\nExamples:\n"
2168 0 : "\nAmount received by the default label with at least 1 confirmation\n" +
2169 0 : HelpExampleCli("getreceivedbylabel", "\"\"") +
2170 0 : "\nAmount received at the tabby label including unconfirmed amounts with zero confirmations\n" +
2171 0 : HelpExampleCli("getreceivedbylabel", "\"tabby\" 0") +
2172 0 : "\nThe amount with at least 6 confirmation, very safe\n" +
2173 0 : HelpExampleCli("getreceivedbylabel", "\"tabby\" 6") +
2174 0 : "\nAs a json rpc call\n" +
2175 0 : HelpExampleRpc("getreceivedbylabel", "\"tabby\", 6"));
2176 :
2177 : // Make sure the results are valid at least up to the most recent block
2178 : // the user could have gotten from another RPC command prior to now
2179 14 : pwallet->BlockUntilSyncedToCurrentChain();
2180 :
2181 42 : LOCK2(cs_main, pwallet->cs_wallet);
2182 14 : int nBlockHeight = chainActive.Height();
2183 :
2184 : // Minimum confirmations
2185 14 : int nMinDepth = 1;
2186 14 : if (request.params.size() > 1)
2187 0 : nMinDepth = request.params[1].get_int();
2188 :
2189 : // Get the set of pub keys assigned to label
2190 28 : std::string label = LabelFromValue(request.params[0]);
2191 28 : std::set<CTxDestination> setAddress = pwallet->GetLabelAddresses(label);
2192 :
2193 : // Tally
2194 14 : CAmount nAmount = 0;
2195 1459 : for (const auto& entry : pwallet->mapWallet) {
2196 1445 : const CWalletTx& wtx = entry.second;
2197 1445 : if (wtx.IsCoinBase() || !IsFinalTx(wtx.tx, nBlockHeight))
2198 1345 : continue;
2199 :
2200 300 : for (const CTxOut& txout : wtx.tx->vout) {
2201 400 : CTxDestination address;
2202 349 : if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*pwallet, address) && setAddress.count(address))
2203 26 : if (wtx.GetDepthInMainChain() >= nMinDepth)
2204 25 : nAmount += txout.nValue;
2205 : }
2206 : }
2207 :
2208 14 : return (double)nAmount / (double)COIN;
2209 : }
2210 :
2211 137 : UniValue getbalance(const JSONRPCRequest& request)
2212 : {
2213 137 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
2214 :
2215 137 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
2216 0 : return NullUniValue;
2217 :
2218 137 : if (request.fHelp || (request.params.size() > 4 ))
2219 0 : throw std::runtime_error(
2220 : "getbalance ( minconf include_watchonly include_delegated include_shield )\n"
2221 : "\nReturns the server's total available balance.\n"
2222 : "The available balance is what the wallet considers currently spendable, and is\n"
2223 : "thus affected by options which limit spendability such as -spendzeroconfchange.\n"
2224 :
2225 : "\nArguments:\n"
2226 : "1. minconf (numeric, optional, default=0) Only include transactions confirmed at least this many times.\n"
2227 : "2. include_watchonly (bool, optional, default=false) Also include balance in watchonly addresses (see 'importaddress')\n"
2228 : "3. include_delegated (bool, optional, default=true) Also include balance delegated to cold stakers\n"
2229 : "4. include_shield (bool, optional, default=true) Also include shield balance\n"
2230 :
2231 : "\nResult:\n"
2232 : "amount (numeric) The total amount in PIV received for this wallet.\n"
2233 :
2234 : "\nExamples:\n"
2235 0 : "\nThe total amount in the wallet\n" +
2236 0 : HelpExampleCli("getbalance", "") +
2237 0 : "\nThe total amount in the wallet, with at least 5 confirmations\n" +
2238 0 : HelpExampleCli("getbalance", "6") +
2239 0 : "\nAs a json rpc call\n" +
2240 0 : HelpExampleRpc("getbalance", "6"));
2241 :
2242 : // Make sure the results are valid at least up to the most recent block
2243 : // the user could have gotten from another RPC command prior to now
2244 137 : pwallet->BlockUntilSyncedToCurrentChain();
2245 :
2246 411 : LOCK2(cs_main, pwallet->cs_wallet);
2247 :
2248 137 : const int paramsSize = request.params.size();
2249 137 : const int nMinDepth = paramsSize > 0 ? request.params[0].get_int() : 0;
2250 137 : bool fIncludeWatchOnly = paramsSize > 1 && request.params[1].get_bool();
2251 137 : bool fIncludeDelegated = paramsSize <= 2 || request.params[2].get_bool();
2252 137 : bool fIncludeShielded = paramsSize <= 3 || request.params[3].get_bool();
2253 :
2254 137 : isminefilter filter = ISMINE_SPENDABLE | (fIncludeWatchOnly ?
2255 : (fIncludeShielded ? ISMINE_WATCH_ONLY_ALL : ISMINE_WATCH_ONLY) : ISMINE_NO);
2256 137 : filter |= fIncludeDelegated ? ISMINE_SPENDABLE_DELEGATED : ISMINE_NO;
2257 137 : filter |= fIncludeShielded ? ISMINE_SPENDABLE_SHIELDED : ISMINE_NO;
2258 137 : return ValueFromAmount(pwallet->GetAvailableBalance(filter, true, nMinDepth));
2259 : }
2260 :
2261 9 : UniValue getcoldstakingbalance(const JSONRPCRequest& request)
2262 : {
2263 9 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
2264 :
2265 9 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
2266 0 : return NullUniValue;
2267 :
2268 9 : if (request.fHelp || (request.params.size() != 0))
2269 0 : throw std::runtime_error(
2270 : "getcoldstakingbalance\n"
2271 : "\nReturns the server's total available cold balance.\n"
2272 :
2273 : "\nResult:\n"
2274 : "amount (numeric) The total amount in PIV received for this wallet in P2CS contracts.\n"
2275 :
2276 : "\nExamples:\n"
2277 0 : "\nThe total amount in the wallet\n" +
2278 0 : HelpExampleCli("getcoldstakingbalance", "") +
2279 0 : "\nAs a json rpc call\n" +
2280 0 : HelpExampleRpc("getcoldstakingbalance", "\"*\""));
2281 :
2282 : // Make sure the results are valid at least up to the most recent block
2283 : // the user could have gotten from another RPC command prior to now
2284 9 : pwallet->BlockUntilSyncedToCurrentChain();
2285 :
2286 27 : LOCK2(cs_main, pwallet->cs_wallet);
2287 :
2288 9 : return ValueFromAmount(pwallet->GetColdStakingBalance());
2289 : }
2290 :
2291 8 : UniValue getdelegatedbalance(const JSONRPCRequest& request)
2292 : {
2293 8 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
2294 :
2295 8 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
2296 0 : return NullUniValue;
2297 :
2298 8 : if (request.fHelp || (request.params.size() != 0))
2299 0 : throw std::runtime_error(
2300 : "getdelegatedbalance\n"
2301 : "\nReturns the server's total available delegated balance (sum of all utxos delegated\n"
2302 : "to a cold staking address to stake on behalf of addresses of this wallet).\n"
2303 :
2304 : "\nResult:\n"
2305 : "amount (numeric) The total amount in PIV received for this wallet in P2CS contracts.\n"
2306 :
2307 : "\nExamples:\n"
2308 0 : "\nThe total amount in the wallet\n" +
2309 0 : HelpExampleCli("getdelegatedbalance", "") +
2310 0 : "\nAs a json rpc call\n" +
2311 0 : HelpExampleRpc("getdelegatedbalance", "\"*\""));
2312 :
2313 : // Make sure the results are valid at least up to the most recent block
2314 : // the user could have gotten from another RPC command prior to now
2315 8 : pwallet->BlockUntilSyncedToCurrentChain();
2316 :
2317 24 : LOCK2(cs_main, pwallet->cs_wallet);
2318 :
2319 8 : return ValueFromAmount(pwallet->GetDelegatedBalance());
2320 : }
2321 :
2322 2 : UniValue getunconfirmedbalance(const JSONRPCRequest& request)
2323 : {
2324 2 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
2325 :
2326 2 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
2327 0 : return NullUniValue;
2328 :
2329 2 : if (request.fHelp || request.params.size() > 0)
2330 0 : throw std::runtime_error(
2331 : "getunconfirmedbalance\n"
2332 0 : "Returns the server's total unconfirmed balance\n");
2333 :
2334 : // Make sure the results are valid at least up to the most recent block
2335 : // the user could have gotten from another RPC command prior to now
2336 2 : pwallet->BlockUntilSyncedToCurrentChain();
2337 :
2338 6 : LOCK2(cs_main, pwallet->cs_wallet);
2339 :
2340 2 : return ValueFromAmount(pwallet->GetUnconfirmedBalance());
2341 : }
2342 :
2343 : /*
2344 : * Only used for t->t transactions (via sendmany RPC)
2345 : */
2346 6 : static UniValue legacy_sendmany(CWallet* const pwallet, const UniValue& sendTo, int nMinDepth, std::string comment, bool fIncludeDelegated, const UniValue& subtractFeeFromAmount)
2347 : {
2348 12 : LOCK2(cs_main, pwallet->cs_wallet);
2349 :
2350 6 : if (!g_connman)
2351 0 : throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
2352 :
2353 6 : isminefilter filter = ISMINE_SPENDABLE | (fIncludeDelegated ? ISMINE_SPENDABLE_DELEGATED : ISMINE_NO);
2354 :
2355 6 : CTransactionRef txNew;
2356 12 : std::set<CTxDestination> setAddress;
2357 12 : std::vector<CRecipient> vecSend;
2358 :
2359 6 : CAmount totalAmount = 0;
2360 6 : std::vector<std::string> keys = sendTo.getKeys();
2361 21 : for (const std::string& name_ : keys) {
2362 15 : bool isStaking = false;
2363 15 : bool isExchange = false;
2364 30 : CTxDestination dest = DecodeDestination(name_, isStaking, isExchange);
2365 15 : if (!IsValidDestination(dest) || isStaking)
2366 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid PIVX address: ")+name_);
2367 :
2368 15 : if (setAddress.count(dest))
2369 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ")+name_);
2370 15 : setAddress.insert(dest);
2371 :
2372 30 : CScript scriptPubKey = GetScriptForDestination(dest);
2373 15 : CAmount nAmount = AmountFromValue(sendTo[name_]);
2374 15 : totalAmount += nAmount;
2375 :
2376 15 : bool fSubtractFeeFromAmount = false;
2377 15 : for (unsigned int idx = 0; idx < subtractFeeFromAmount.size(); idx++) {
2378 1 : const UniValue& addr = subtractFeeFromAmount[idx];
2379 1 : if (addr.get_str() == name_) {
2380 1 : fSubtractFeeFromAmount = true;
2381 1 : break;
2382 : }
2383 : }
2384 :
2385 15 : vecSend.emplace_back(scriptPubKey, nAmount, fSubtractFeeFromAmount);
2386 : }
2387 :
2388 : // Check funds
2389 6 : if (totalAmount > pwallet->GetLegacyBalance(filter, nMinDepth)) {
2390 0 : throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Wallet has insufficient funds");
2391 : }
2392 :
2393 : // Send
2394 12 : CReserveKey keyChange(pwallet);
2395 6 : CAmount nFeeRequired = 0;
2396 12 : std::string strFailReason;
2397 6 : int nChangePosInOut = -1;
2398 6 : bool fCreated = pwallet->CreateTransaction(vecSend, txNew, keyChange, nFeeRequired, nChangePosInOut, strFailReason,
2399 : nullptr, // coinControl
2400 : true, // sign
2401 : 0, // nFeePay
2402 : fIncludeDelegated,
2403 : nullptr, // fStakeDelegationVoided
2404 : 0, // default extra size
2405 : nMinDepth);
2406 6 : if (!fCreated)
2407 0 : throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strFailReason);
2408 12 : const CWallet::CommitResult& res = pwallet->CommitTransaction(txNew, keyChange, g_connman.get());
2409 6 : if (res.status != CWallet::CommitStatus::OK)
2410 0 : throw JSONRPCError(RPC_WALLET_ERROR, res.ToString());
2411 :
2412 : // Set comment
2413 6 : CWalletTx& wtx = pwallet->mapWallet.at(txNew->GetHash());
2414 6 : if (!comment.empty()) {
2415 0 : wtx.mapValue["comment"] = comment;
2416 : }
2417 :
2418 18 : return wtx.GetHash().GetHex();
2419 : }
2420 :
2421 : /*
2422 : * This function uses [legacy_sendmany] in the background.
2423 : * If any recipient is a shield address, instead it uses [shieldsendmany "from_transparent"].
2424 : */
2425 8 : UniValue sendmany(const JSONRPCRequest& request)
2426 : {
2427 8 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
2428 :
2429 8 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
2430 0 : return NullUniValue;
2431 :
2432 8 : if (request.fHelp || request.params.size() < 2 || request.params.size() > 6)
2433 0 : throw std::runtime_error(
2434 : "sendmany \"\" {\"address\":amount,...} ( minconf \"comment\" include_delegated )\n"
2435 : "\nSend to multiple destinations. Recipients are transparent or shield PIVX addresses.\n"
2436 : "\nAmounts are double-precision floating point numbers.\n"
2437 0 : + HelpRequiringPassphrase(pwallet) + "\n"
2438 :
2439 : "\nArguments:\n"
2440 : "1. \"dummy\" (string, required) Must be set to \"\" for backwards compatibility.\n"
2441 : "2. \"amounts\" (string, required) A json object with addresses and amounts\n"
2442 : " {\n"
2443 : " \"address\":amount (numeric) The pivx address (either transparent or shield) is the key,\n"
2444 : " the numeric amount in PIV is the value\n"
2445 : " ,...\n"
2446 : " }\n"
2447 : "3. minconf (numeric, optional, default=1) Only use the balance confirmed at least this many times.\n"
2448 : "4. \"comment\" (string, optional) A comment\n"
2449 : "5. include_delegated (bool, optional, default=false) Also include balance delegated to cold stakers\n"
2450 : "6. subtract_fee_from (array, optional) A json array with addresses.\n"
2451 : " The fee will be equally deducted from the amount of each selected address.\n"
2452 : " Those recipients will receive less PIV than you enter in their corresponding amount field.\n"
2453 : " If no addresses are specified here, the sender pays the fee.\n"
2454 : " [\n"
2455 : " \"address\" (string) Subtract fee from this address\n"
2456 : " ,...\n"
2457 : " ]\n"
2458 :
2459 : "\nResult:\n"
2460 : "\"transactionid\" (string) The transaction id for the send. Only 1 transaction is created regardless of \n"
2461 : " the number of addresses.\n"
2462 :
2463 : "\nExamples:\n"
2464 0 : "\nSend two amounts to two different addresses:\n" +
2465 0 : HelpExampleCli("sendmany", "\"\" \"{\\\"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\\\":0.01,\\\"DAD3Y6ivr8nPQLT1NEPX84DxGCw9jz9Jvg\\\":0.02}\"") +
2466 0 : "\nSend two amounts to two different addresses setting the confirmation and comment:\n" +
2467 0 : HelpExampleCli("sendmany", "\"\" \"{\\\"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\\\":0.01,\\\"DAD3Y6ivr8nPQLT1NEPX84DxGCw9jz9Jvg\\\":0.02}\" 6 \"testing\"") +
2468 0 : "\nSend to shield address:\n" +
2469 0 : HelpExampleCli("sendmany", "\"\" \"{\\\"ps1u87kylcmn28yclnx2uy0psnvuhs2xn608ukm6n2nshrpg2nzyu3n62ls8j77m9cgp40dx40evej\\\":10}\"") +
2470 0 : "\nAs a json rpc call\n" +
2471 0 : HelpExampleRpc("sendmany", "\"\", \"{\\\"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\\\":0.01,\\\"DAD3Y6ivr8nPQLT1NEPX84DxGCw9jz9Jvg\\\":0.02}\", 6, \"testing\"")
2472 0 : );
2473 :
2474 8 : EnsureWalletIsUnlocked(pwallet);
2475 :
2476 : // Make sure the results are valid at least up to the most recent block
2477 : // the user could have gotten from another RPC command prior to now
2478 8 : pwallet->BlockUntilSyncedToCurrentChain();
2479 :
2480 : // Read Params
2481 8 : if (!request.params[0].isNull() && !request.params[0].get_str().empty()) {
2482 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Dummy value must be set to \"\"");
2483 : }
2484 8 : const UniValue sendTo = request.params[1].get_obj();
2485 8 : const int nMinDepth = request.params.size() > 2 ? request.params[2].get_int() : 1;
2486 8 : const std::string comment = (request.params.size() > 3 && !request.params[3].isNull() && !request.params[3].get_str().empty()) ?
2487 16 : request.params[3].get_str() : "";
2488 8 : const bool fIncludeDelegated = (request.params.size() > 4 && request.params[4].get_bool());
2489 :
2490 16 : UniValue subtractFeeFromAmount(UniValue::VARR);
2491 8 : if (request.params.size() > 5 && !request.params[5].isNull())
2492 2 : subtractFeeFromAmount = request.params[5].get_array();
2493 :
2494 : // Check if any recipient address is shield
2495 8 : bool fShieldSend = false;
2496 23 : for (const std::string& key : sendTo.getKeys()) {
2497 17 : bool isStaking = false, isExchange = false, isShielded = false;
2498 17 : Standard::DecodeDestination(key, isStaking, isExchange, isShielded);
2499 17 : if (isShielded) {
2500 2 : fShieldSend = true;
2501 2 : break;
2502 : }
2503 : }
2504 :
2505 8 : if (fShieldSend) {
2506 4 : return ShieldSendManyTo(pwallet, sendTo, comment, "", nMinDepth, fIncludeDelegated, subtractFeeFromAmount);
2507 : }
2508 :
2509 : // All recipients are transparent: use Legacy sendmany t->t
2510 18 : return legacy_sendmany(pwallet, sendTo, nMinDepth, comment, fIncludeDelegated, subtractFeeFromAmount);
2511 : }
2512 :
2513 : // Defined in rpc/misc.cpp
2514 : extern CScript _createmultisig_redeemScript(CWallet* const pwallet, const UniValue& params);
2515 :
2516 12 : UniValue addmultisigaddress(const JSONRPCRequest& request)
2517 : {
2518 12 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
2519 :
2520 12 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
2521 0 : return NullUniValue;
2522 :
2523 12 : if (request.fHelp || request.params.size() < 2 || request.params.size() > 3)
2524 0 : throw std::runtime_error(
2525 : "addmultisigaddress nrequired [\"key\",...] ( \"label\" )\n"
2526 : "\nAdd a nrequired-to-sign multisignature address to the wallet. Requires a new wallet backup.\n"
2527 : "Each key is a PIVX address or hex-encoded public key.\n"
2528 : "If 'label' is specified, assign address to that label.\n"
2529 :
2530 : "\nArguments:\n"
2531 : "1. nrequired (numeric, required) The number of required signatures out of the n keys or addresses.\n"
2532 : "2. \"keys\" (string, required) A json array of pivx addresses or hex-encoded public keys\n"
2533 : " [\n"
2534 : " \"address\" (string) pivx address or hex-encoded public key\n"
2535 : " ...,\n"
2536 : " ]\n"
2537 : "3. \"label\" (string, optional) A label to assign the addresses to.\n"
2538 :
2539 : "\nResult:\n"
2540 : "\"pivxaddress\" (string) A pivx address associated with the keys.\n"
2541 :
2542 : "\nExamples:\n"
2543 0 : "\nAdd a multisig address from 2 addresses\n" +
2544 0 : HelpExampleCli("addmultisigaddress", "2 \"[\\\"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\\\",\\\"DAD3Y6ivr8nPQLT1NEPX84DxGCw9jz9Jvg\\\"]\"") +
2545 0 : "\nAs json rpc call\n" +
2546 0 : HelpExampleRpc("addmultisigaddress", "2, \"[\\\"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\\\",\\\"DAD3Y6ivr8nPQLT1NEPX84DxGCw9jz9Jvg\\\"]\""));
2547 :
2548 36 : LOCK2(cs_main, pwallet->cs_wallet);
2549 :
2550 24 : std::string label;
2551 12 : if (request.params.size() > 2)
2552 5 : label = LabelFromValue(request.params[2]);
2553 :
2554 : // Construct using pay-to-script-hash:
2555 24 : CScript inner = _createmultisig_redeemScript(pwallet, request.params);
2556 12 : CScriptID innerID(inner);
2557 12 : pwallet->AddCScript(inner);
2558 :
2559 12 : pwallet->SetAddressBook(innerID, label, AddressBook::AddressBookPurpose::SEND);
2560 24 : return EncodeDestination(innerID);
2561 : }
2562 :
2563 :
2564 : struct tallyitem {
2565 : CAmount nAmount;
2566 : int nConf;
2567 : std::vector<uint256> txids;
2568 : bool fIsWatchonly;
2569 641 : tallyitem()
2570 5 : {
2571 641 : nAmount = 0;
2572 641 : nConf = std::numeric_limits<int>::max();
2573 5 : fIsWatchonly = false;
2574 : }
2575 : };
2576 :
2577 79 : static UniValue ListReceived(CWallet* const pwallet, const UniValue& params, bool by_label, int nBlockHeight)
2578 : {
2579 : // Minimum confirmations
2580 79 : int nMinDepth = 1;
2581 79 : if (params.size() > 0)
2582 74 : nMinDepth = params[0].get_int();
2583 :
2584 : // Whether to include empty labels
2585 79 : bool fIncludeEmpty = false;
2586 79 : if (params.size() > 1)
2587 72 : fIncludeEmpty = params[1].get_bool();
2588 :
2589 79 : isminefilter filter = ISMINE_SPENDABLE_ALL;
2590 79 : if (params.size() > 2)
2591 70 : if (params[2].get_bool())
2592 44 : filter = filter | ISMINE_WATCH_ONLY;
2593 :
2594 79 : bool has_filtered_address = false;
2595 79 : CTxDestination filtered_address = CNoDestination();
2596 79 : if (!by_label && params.size() > 3) {
2597 136 : CTxDestination dest = DecodeDestination(params[3].get_str());
2598 68 : if (!IsValidDestination(dest)) {
2599 2 : throw JSONRPCError(RPC_WALLET_ERROR, "address_filter parameter was invalid");
2600 : }
2601 67 : filtered_address = dest;
2602 67 : has_filtered_address = true;
2603 : }
2604 :
2605 : // Tally
2606 156 : std::map<CTxDestination, tallyitem> mapTally;
2607 4966 : for (const auto& entry : pwallet->mapWallet) {
2608 4888 : const CWalletTx& wtx = entry.second;
2609 :
2610 4888 : if (!IsFinalTx(wtx.tx, nBlockHeight)) {
2611 17 : continue;
2612 : }
2613 :
2614 4888 : int nDepth = wtx.GetDepthInMainChain();
2615 4888 : if (nDepth < nMinDepth) {
2616 17 : continue;
2617 : }
2618 :
2619 11572 : for (const CTxOut& txout : wtx.tx->vout) {
2620 7627 : CTxDestination address;
2621 6701 : if (!ExtractDestination(txout.scriptPubKey, address)) {
2622 5837 : continue;
2623 : }
2624 :
2625 6639 : if (has_filtered_address && !(filtered_address == address)) {
2626 5693 : continue;
2627 : }
2628 :
2629 946 : isminefilter mine = IsMine(*pwallet, address);
2630 946 : if (!(mine & filter)) {
2631 20 : continue;
2632 : }
2633 :
2634 926 : tallyitem& item = mapTally[address];
2635 926 : item.nAmount += txout.nValue;
2636 926 : item.nConf = std::min(item.nConf, nDepth);
2637 926 : item.txids.push_back(wtx.GetHash());
2638 926 : if (mine & ISMINE_WATCH_ONLY)
2639 20 : item.fIsWatchonly = true;
2640 : }
2641 : }
2642 :
2643 : // Create mapAddressBook iterator
2644 : // If we aren't filtering, go from begin() to end()
2645 78 : auto itAddr = pwallet->NewAddressBookIterator();
2646 : // If we are filtering, find() the applicable entry
2647 78 : if (has_filtered_address) {
2648 67 : itAddr.SetFilter(filtered_address);
2649 : }
2650 :
2651 : // Reply
2652 78 : UniValue ret(UniValue::VARR);
2653 156 : std::map<std::string, tallyitem> label_tally;
2654 :
2655 350 : for (auto& itAddrBook = itAddr; itAddrBook.IsValid(); itAddrBook.Next()) {
2656 :
2657 97 : auto* dest = itAddrBook.GetCTxDestKey();
2658 105 : if (!dest) continue;
2659 :
2660 97 : const auto &address = *dest;
2661 186 : const std::string &label = itAddrBook.GetValue().name;
2662 97 : auto it = mapTally.find(address);
2663 97 : if (it == mapTally.end() && !fIncludeEmpty) {
2664 8 : continue;
2665 : }
2666 :
2667 89 : CAmount nAmount = 0;
2668 89 : int nConf = std::numeric_limits<int>::max();
2669 89 : bool fIsWatchonly = false;
2670 89 : if (it != mapTally.end()) {
2671 71 : nAmount = (*it).second.nAmount;
2672 71 : nConf = (*it).second.nConf;
2673 71 : fIsWatchonly = (*it).second.fIsWatchonly;
2674 : }
2675 :
2676 89 : if (by_label) {
2677 15 : tallyitem& _item = label_tally[label];
2678 15 : _item.nAmount += nAmount;
2679 15 : _item.nConf = std::min(_item.nConf, nConf);
2680 15 : _item.fIsWatchonly = fIsWatchonly;
2681 : } else {
2682 148 : UniValue obj(UniValue::VOBJ);
2683 74 : if (fIsWatchonly)
2684 32 : obj.pushKV("involvesWatchonly", true);
2685 148 : obj.pushKV("address", EncodeDestination(address, AddressBook::IsColdStakingPurpose(label), AddressBook::IsExchangePurpose(label)));
2686 148 : obj.pushKV("amount", ValueFromAmount(nAmount));
2687 74 : if (nConf == std::numeric_limits<int>::max()) nConf = 0;
2688 74 : obj.pushKV("confirmations", nConf);
2689 74 : obj.pushKV("bcconfirmations", nConf); // DEPRECATED in 4.3.99
2690 74 : obj.pushKV("label", label);
2691 148 : UniValue transactions(UniValue::VARR);
2692 74 : if (it != mapTally.end()) {
2693 275 : for (const uint256 &item : (*it).second.txids) {
2694 432 : transactions.push_back(item.GetHex());
2695 : }
2696 : }
2697 74 : obj.pushKV("txids", transactions);
2698 74 : ret.push_back(obj);
2699 : }
2700 : }
2701 :
2702 78 : if (by_label) {
2703 9 : for (const auto& entry : label_tally) {
2704 5 : CAmount nAmount = entry.second.nAmount;
2705 5 : int nConf = entry.second.nConf;
2706 10 : UniValue obj(UniValue::VOBJ);
2707 5 : if (entry.second.fIsWatchonly)
2708 0 : obj.pushKV("involvesWatchonly", true);
2709 10 : obj.pushKV("amount", ValueFromAmount(nAmount));
2710 6 : obj.pushKV("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf));
2711 5 : obj.pushKV("label", entry.first);
2712 5 : ret.push_back(obj);
2713 : }
2714 : }
2715 :
2716 156 : return ret;
2717 : }
2718 :
2719 75 : UniValue listreceivedbyaddress(const JSONRPCRequest& request)
2720 : {
2721 75 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
2722 :
2723 75 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
2724 0 : return NullUniValue;
2725 :
2726 75 : if (request.fHelp || request.params.size() > 4)
2727 0 : throw std::runtime_error(
2728 : "listreceivedbyaddress ( minconf include_empty include_watchonly filter)\n"
2729 : "\nList balances by receiving address.\n"
2730 :
2731 : "\nArguments:\n"
2732 : "1. minconf (numeric, optional, default=1) The minimum number of confirmations before payments are included.\n"
2733 : "2. include_empty (numeric, optional, default=false) Whether to include addresses that haven't received any payments.\n"
2734 : "3. include_watchonly (bool, optional, default=false) Whether to include watchonly addresses (see 'importaddress').\n"
2735 : "4. filter (string, optional) If present, only return information on this address.\n"
2736 :
2737 : "\nResult:\n"
2738 : "[\n"
2739 : " {\n"
2740 : " \"involvesWatchonly\" : \"true\", (bool) Only returned if imported addresses were involved in transaction\n"
2741 : " \"address\" : \"receivingaddress\", (string) The receiving address\n"
2742 : " \"amount\" : x.xxx, (numeric) The total amount in PIV received by the address\n"
2743 : " \"confirmations\" : n (numeric) The number of confirmations of the most recent transaction included\n"
2744 : " \"bcconfirmations\" : n, (numeric) DEPRECATED: Will be removed in a future release\n"
2745 : " \"label\" : \"label\", (string) The label of the receiving address. The default label is \"\".\n"
2746 : " }\n"
2747 : " ,...\n"
2748 : "]\n"
2749 :
2750 0 : "\nExamples:\n" +
2751 0 : HelpExampleCli("listreceivedbyaddress", "") +
2752 0 : HelpExampleCli("listreceivedbyaddress", "6 true") +
2753 0 : HelpExampleRpc("listreceivedbyaddress", "6, true, true") +
2754 0 : HelpExampleRpc("listreceivedbyaddress", "6, true, true, \"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\""));
2755 :
2756 : // Make sure the results are valid at least up to the most recent block
2757 : // the user could have gotten from another RPC command prior to now
2758 75 : pwallet->BlockUntilSyncedToCurrentChain();
2759 :
2760 224 : LOCK2(cs_main, pwallet->cs_wallet);
2761 75 : int nBlockHeight = chainActive.Height();
2762 75 : return ListReceived(pwallet, request.params, false, nBlockHeight);
2763 : }
2764 :
2765 17 : UniValue listreceivedbyshieldaddress(const JSONRPCRequest& request)
2766 : {
2767 17 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
2768 :
2769 17 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
2770 0 : return NullUniValue;
2771 :
2772 17 : if (request.fHelp || request.params.size()==0 || request.params.size() >2)
2773 0 : throw std::runtime_error(
2774 : "listreceivedbyshieldaddress \"address\" ( minconf )\n"
2775 : "\nReturn a list of amounts received by a shield addr belonging to the node's wallet.\n"
2776 :
2777 : "\nArguments:\n"
2778 : "1. \"address\" (string) The private address.\n"
2779 : "2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n"
2780 :
2781 : "\nResult:\n"
2782 : "{\n"
2783 : " \"txid\": \"txid\", (string) the transaction id\n"
2784 : " \"amount\": xxxxx, (numeric) the amount of value in the note\n"
2785 : " \"memo\": xxxxx, (string) hexadecimal string representation of memo field\n"
2786 : " \"confirmations\" : n, (numeric) the number of confirmations\n"
2787 : " \"blockheight\": n, (numeric) The block height containing the transaction\n"
2788 : " \"blockindex\": n, (numeric) The block index containing the transaction.\n"
2789 : " \"blocktime\": xxx, (numeric) The transaction time in seconds since epoch (midnight Jan 1 1970 GMT).\n"
2790 : " \"outindex\" (sapling) : n, (numeric) the output index\n"
2791 : " \"change\": true|false, (boolean) true if the address that received the note is also one of the sending addresses\n"
2792 : "}\n"
2793 :
2794 : "\nExamples:\n"
2795 0 : + HelpExampleCli("listreceivedbyshieldaddress", "\"ps1ra969yfhvhp73rw5ak2xvtcm9fkuqsnmad7qln79mphhdrst3lwu9vvv03yuyqlh42p42st47qd\"")
2796 0 : + HelpExampleRpc("listreceivedbyshieldaddress", "\"ps1ra969yfhvhp73rw5ak2xvtcm9fkuqsnmad7qln79mphhdrst3lwu9vvv03yuyqlh42p42st47qd\"")
2797 0 : );
2798 :
2799 : // Make sure the results are valid at least up to the most recent block
2800 : // the user could have gotten from another RPC command prior to now
2801 17 : pwallet->BlockUntilSyncedToCurrentChain();
2802 :
2803 48 : LOCK2(cs_main, pwallet->cs_wallet);
2804 :
2805 17 : int nMinDepth = 1;
2806 17 : if (request.params.size() > 1) {
2807 6 : nMinDepth = request.params[1].get_int();
2808 : }
2809 6 : if (nMinDepth < 0) {
2810 2 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Minimum number of confirmations cannot be less than 0");
2811 : }
2812 :
2813 : // Check that the from address is valid.
2814 32 : auto fromaddress = request.params[0].get_str();
2815 :
2816 30 : auto zaddr = KeyIO::DecodeSaplingPaymentAddress(fromaddress);
2817 16 : if (!zaddr) {
2818 2 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid addr.");
2819 : }
2820 15 : libzcash::SaplingPaymentAddress shieldAddr = *zaddr;
2821 :
2822 15 : auto sspkm = pwallet->GetSaplingScriptPubKeyMan();
2823 : // Visitor to support Sapling addrs
2824 15 : if (!sspkm->PaymentAddressBelongsToWallet(shieldAddr)) {
2825 2 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "From address does not belong to this node, shield addr spending key or viewing key not found.");
2826 : }
2827 :
2828 28 : UniValue result(UniValue::VARR);
2829 28 : std::vector<SaplingNoteEntry> saplingEntries;
2830 14 : sspkm->GetFilteredNotes(saplingEntries, zaddr, nMinDepth, false, false);
2831 :
2832 28 : std::set<std::pair<libzcash::PaymentAddress, uint256>> nullifierSet;
2833 14 : bool hasSpendingKey = pwallet->HaveSpendingKeyForPaymentAddress(shieldAddr);
2834 14 : if (hasSpendingKey) {
2835 39 : nullifierSet = sspkm->GetNullifiersForAddresses({*zaddr});
2836 : }
2837 :
2838 55 : for (const SaplingNoteEntry& entry : saplingEntries) {
2839 82 : UniValue obj(UniValue::VOBJ);
2840 82 : obj.pushKV("txid", entry.op.hash.ToString());
2841 82 : obj.pushKV("amount", ValueFromAmount(CAmount(entry.note.value())));
2842 82 : obj.pushKV("memo", HexStrTrimmed(entry.memo));
2843 41 : obj.pushKV("outindex", (int)entry.op.n);
2844 41 : obj.pushKV("confirmations", entry.confirmations);
2845 :
2846 41 : int height = 0;
2847 41 : int index = -1;
2848 41 : int64_t time = 0;
2849 :
2850 41 : auto it = pwallet->mapWallet.find(entry.op.hash);
2851 41 : if (it != pwallet->mapWallet.end()) {
2852 41 : const CWalletTx& wtx = it->second;
2853 82 : if (!wtx.m_confirm.hashBlock.IsNull())
2854 40 : height = mapBlockIndex[wtx.m_confirm.hashBlock]->nHeight;
2855 41 : index = wtx.m_confirm.nIndex;
2856 41 : time = wtx.GetTxTime();
2857 : }
2858 :
2859 41 : obj.pushKV("blockheight", height);
2860 41 : obj.pushKV("blockindex", index);
2861 41 : obj.pushKV("blocktime", time);
2862 :
2863 41 : if (hasSpendingKey) {
2864 76 : obj.pushKV("change", sspkm->IsNoteSaplingChange(nullifierSet, entry.address, entry.op));
2865 : }
2866 41 : result.push_back(obj);
2867 : }
2868 14 : return result;
2869 : }
2870 :
2871 4 : UniValue listreceivedbylabel(const JSONRPCRequest& request)
2872 : {
2873 4 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
2874 :
2875 4 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
2876 0 : return NullUniValue;
2877 :
2878 4 : if (request.fHelp || request.params.size() > 3)
2879 0 : throw std::runtime_error(
2880 : "listreceivedbylabel ( minconf include_empty include_watchonly)\n"
2881 : "\nList received transactions by label.\n"
2882 :
2883 : "\nArguments:\n"
2884 : "1. minconf (numeric, optional, default=1) The minimum number of confirmations before payments are included.\n"
2885 : "2. include_empty (boolean, optional, default=false) Whether to include labels that haven't received any payments.\n"
2886 : "3. include_watchonly (bool, optional, default=false) Whether to include watchonly addresses (see 'importaddress').\n"
2887 :
2888 : "\nResult:\n"
2889 : "[\n"
2890 : " {\n"
2891 : " \"involvesWatchonly\" : \"true\", (bool) Only returned if imported addresses were involved in transaction\n"
2892 : " \"amount\" : x.xxx, (numeric) The total amount received by addresses with this label\n"
2893 : " \"confirmations\" : n (numeric) The number of confirmations of the most recent transaction included\n"
2894 : " \"bcconfirmations\" : n, (numeric) DEPRECATED: Will be removed in a future release\n"
2895 : " \"label\" : \"label\" (string) The label of the receiving address. The default label is \"\".\n"
2896 : " }\n"
2897 : " ,...\n"
2898 : "]\n"
2899 :
2900 0 : "\nExamples:\n" +
2901 0 : HelpExampleCli("listreceivedbylabel", "") + HelpExampleCli("listreceivedbylabel", "6 true") + HelpExampleRpc("listreceivedbylabel", "6, true, true"));
2902 :
2903 : // Make sure the results are valid at least up to the most recent block
2904 : // the user could have gotten from another RPC command prior to now
2905 4 : pwallet->BlockUntilSyncedToCurrentChain();
2906 :
2907 12 : LOCK2(cs_main, pwallet->cs_wallet);
2908 4 : int nBlockHeight = chainActive.Height();
2909 4 : return ListReceived(pwallet, request.params, true, nBlockHeight);
2910 : }
2911 :
2912 3 : UniValue listcoldutxos(const JSONRPCRequest& request)
2913 : {
2914 3 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
2915 :
2916 3 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
2917 0 : return NullUniValue;
2918 :
2919 3 : if (request.fHelp || request.params.size() > 1)
2920 0 : throw std::runtime_error(
2921 : "listcoldutxos ( not_whitelisted )\n"
2922 : "\nList P2CS unspent outputs received by this wallet as cold-staker-\n"
2923 :
2924 : "\nArguments:\n"
2925 : "1. not_whitelisted (boolean, optional, default=false) Whether to exclude P2CS from whitelisted delegators.\n"
2926 :
2927 : "\nResult:\n"
2928 : "[\n"
2929 : " {\n"
2930 : " \"txid\" : \"true\", (string) The transaction id of the P2CS utxo\n"
2931 : " \"txidn\" : n (numeric) The output number of the P2CS utxo\n"
2932 : " \"amount\" : x.xxx, (numeric) The amount of the P2CS utxo\n"
2933 : " \"confirmations\" : n (numeric) The number of confirmations of the P2CS utxo\n"
2934 : " \"cold-staker\" : \"address\" (string) The cold-staker address of the P2CS utxo\n"
2935 : " \"coin-owner\" : \"address\" (string) The coin-owner address of the P2CS utxo\n"
2936 : " \"whitelisted\" : \"true\" (boolean) \"true\"/\"false\" coin-owner in delegator whitelist\n"
2937 : " }\n"
2938 : " ,...\n"
2939 : "]\n"
2940 :
2941 0 : "\nExamples:\n" +
2942 0 : HelpExampleCli("listcoldutxos", "") + HelpExampleCli("listcoldutxos", "true"));
2943 :
2944 : // Make sure the results are valid at least up to the most recent block
2945 : // the user could have gotten from another RPC command prior to now
2946 3 : pwallet->BlockUntilSyncedToCurrentChain();
2947 :
2948 9 : LOCK2(cs_main, pwallet->cs_wallet);
2949 :
2950 3 : bool fExcludeWhitelisted = false;
2951 3 : if (request.params.size() > 0)
2952 0 : fExcludeWhitelisted = request.params[0].get_bool();
2953 6 : UniValue results(UniValue::VARR);
2954 :
2955 264 : for (const auto& entry : pwallet->mapWallet) {
2956 261 : const uint256& wtxid = entry.first;
2957 261 : const CWalletTx* pcoin = &entry.second;
2958 261 : if (!CheckFinalTx(pcoin->tx) || !pcoin->IsTrusted())
2959 2 : continue;
2960 :
2961 : // if this tx has no unspent P2CS outputs for us, skip it
2962 259 : if(pcoin->GetColdStakingCredit() == 0 && pcoin->GetStakeDelegationCredit() == 0)
2963 216 : continue;
2964 :
2965 127 : for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++) {
2966 84 : const CTxOut& out = pcoin->tx->vout[i];
2967 84 : isminetype mine = pwallet->IsMine(out);
2968 84 : if (!bool(mine & ISMINE_COLD) && !bool(mine & ISMINE_SPENDABLE_DELEGATED))
2969 41 : continue;
2970 43 : txnouttype type;
2971 86 : std::vector<CTxDestination> addresses;
2972 43 : int nRequired;
2973 43 : if (!ExtractDestinations(out.scriptPubKey, type, addresses, nRequired))
2974 0 : continue;
2975 43 : const bool fWhitelisted = pwallet->HasAddressBook(addresses[1]) > 0;
2976 43 : if (fExcludeWhitelisted && fWhitelisted)
2977 0 : continue;
2978 86 : UniValue entry(UniValue::VOBJ);
2979 86 : entry.pushKV("txid", wtxid.GetHex());
2980 43 : entry.pushKV("txidn", (int)i);
2981 86 : entry.pushKV("amount", ValueFromAmount(out.nValue));
2982 43 : entry.pushKV("confirmations", pcoin->GetDepthInMainChain());
2983 86 : entry.pushKV("cold-staker", EncodeDestination(addresses[0], CChainParams::STAKING_ADDRESS));
2984 86 : entry.pushKV("coin-owner", EncodeDestination(addresses[1]));
2985 47 : entry.pushKV("whitelisted", fWhitelisted ? "true" : "false");
2986 43 : results.push_back(entry);
2987 : }
2988 : }
2989 :
2990 3 : return results;
2991 : }
2992 :
2993 424 : static void MaybePushAddress(UniValue & entry, const CTxDestination &dest)
2994 : {
2995 424 : if (IsValidDestination(dest))
2996 1209 : entry.pushKV("address", EncodeDestination(dest));
2997 424 : }
2998 :
2999 342 : static void ListTransactions(CWallet* const pwallet, const CWalletTx& wtx, int nMinDepth, bool fLong, UniValue& ret, const isminefilter& filter) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
3000 : {
3001 342 : AssertLockHeld(cs_main);
3002 :
3003 342 : CAmount nFee;
3004 342 : std::list<COutputEntry> listReceived;
3005 342 : std::list<COutputEntry> listSent;
3006 :
3007 342 : wtx.GetAmounts(listReceived, listSent, nFee, filter);
3008 :
3009 342 : bool involvesWatchonly = wtx.IsFromMe(ISMINE_WATCH_ONLY);
3010 :
3011 : // Sent
3012 342 : if ((!listSent.empty() || nFee != 0)) {
3013 241 : for (const COutputEntry& s : listSent) {
3014 266 : UniValue entry(UniValue::VOBJ);
3015 133 : if (involvesWatchonly || (::IsMine(*pwallet, s.destination) & ISMINE_WATCH_ONLY))
3016 0 : entry.pushKV("involvesWatchonly", true);
3017 133 : MaybePushAddress(entry, s.destination);
3018 133 : entry.pushKV("category", "send");
3019 266 : entry.pushKV("amount", ValueFromAmount(-s.amount));
3020 133 : if (pwallet->HasAddressBook(s.destination)) {
3021 110 : entry.pushKV("label", pwallet->GetNameForAddressBookEntry(s.destination));
3022 : }
3023 133 : entry.pushKV("vout", s.vout);
3024 266 : entry.pushKV("fee", ValueFromAmount(-nFee));
3025 133 : if (fLong)
3026 55 : WalletTxToJSON(wtx, entry);
3027 133 : ret.push_back(entry);
3028 : }
3029 : }
3030 :
3031 : // Received
3032 342 : int depth = wtx.GetDepthInMainChain();
3033 342 : if (listReceived.size() > 0 && depth >= nMinDepth) {
3034 568 : for (const COutputEntry& r : listReceived) {
3035 582 : std::string label;
3036 291 : if (pwallet->HasAddressBook(r.destination))
3037 81 : label = pwallet->GetNameForAddressBookEntry(r.destination);
3038 582 : UniValue entry(UniValue::VOBJ);
3039 291 : if (involvesWatchonly || (::IsMine(*pwallet, r.destination) & ISMINE_WATCH_ONLY))
3040 2 : entry.pushKV("involvesWatchonly", true);
3041 291 : MaybePushAddress(entry, r.destination);
3042 291 : if (wtx.IsCoinBase()) {
3043 210 : if (depth < 1)
3044 0 : entry.pushKV("category", "orphan");
3045 210 : else if (wtx.GetBlocksToMaturity() > 0)
3046 312 : entry.pushKV("category", "immature");
3047 : else
3048 108 : entry.pushKV("category", "generate");
3049 : } else {
3050 162 : entry.pushKV("category", "receive");
3051 : }
3052 582 : entry.pushKV("amount", ValueFromAmount(r.amount));
3053 291 : if (pwallet->HasAddressBook(r.destination)) {
3054 162 : entry.pushKV("label", label);
3055 : }
3056 291 : entry.pushKV("vout", r.vout);
3057 291 : if (fLong)
3058 261 : WalletTxToJSON(wtx, entry);
3059 291 : ret.push_back(entry);
3060 : }
3061 : }
3062 342 : }
3063 :
3064 23 : UniValue listtransactions(const JSONRPCRequest& request)
3065 : {
3066 23 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
3067 :
3068 23 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
3069 0 : return NullUniValue;
3070 :
3071 23 : if (request.fHelp || request.params.size() > 6) throw std::runtime_error(
3072 : "listtransactions ( \"dummy\" count from include_watchonly include_delegated include_cold)\n"
3073 : "\nReturns up to 'count' most recent transactions skipping the first 'from' transactions.\n"
3074 :
3075 : "\nArguments:\n"
3076 : "1. \"dummy\" (string, optional) If set, should be \"*\" for backwards compatibility.\n"
3077 : "2. count (numeric, optional, default=10) The number of transactions to return\n"
3078 : "3. from (numeric, optional, default=0) The number of transactions to skip\n"
3079 : "4. include_watchonly (bool, optional, default=false) Include transactions to watchonly addresses (see 'importaddress')\n"
3080 : "5. include_delegated (bool, optional, default=true) Also include balance delegated to cold stakers\n"
3081 : "6. include_cold (bool, optional, default=true) Also include delegated balance received as cold-staker by this node\n"
3082 :
3083 : "\nResult:\n"
3084 : "[\n"
3085 : " {\n"
3086 : " \"address\":\"pivxaddress\", (string) The pivx address of the transaction.\n"
3087 : " \"category\":\"category\", (string) The transaction category (send|receive|orphan|immature|generate).\n"
3088 : " \"amount\": x.xxx, (numeric) The amount in PIV. This is negative for the 'send' category, and positive\n"
3089 : " for the 'receive' category,\n"
3090 : " \"vout\" : n, (numeric) the vout value\n"
3091 : " \"fee\": x.xxx, (numeric) The amount of the fee in PIV. This is negative and only available for the \n"
3092 : " 'send' category of transactions.\n"
3093 : " \"confirmations\": n, (numeric) The number of blockchain confirmations for the transaction. Available for 'send'\n"
3094 : " 'receive' category of transactions. Negative confirmations indicate the\n"
3095 : " transaction conflicts with the block chain\n"
3096 : " \"bcconfirmations\" : n, (numeric) DEPRECATED: Will be removed in a future release\n"
3097 : " \"trusted\": xxx (bool) Whether we consider the outputs of this unconfirmed transaction safe to spend.\n"
3098 : " and 'receive' category of transactions.\n"
3099 : " \"blockhash\": \"hashvalue\", (string) The block hash containing the transaction. Available for 'send' and 'receive'\n"
3100 : " category of transactions.\n"
3101 : " \"blockindex\": n, (numeric) The block index containing the transaction. Available for 'send' and 'receive'\n"
3102 : " category of transactions.\n"
3103 : " \"txid\": \"transactionid\", (string) The transaction id. Available for 'send' and 'receive' category of transactions.\n"
3104 : " \"time\": xxx, (numeric) The transaction time in seconds since epoch (midnight Jan 1 1970 GMT).\n"
3105 : " \"timereceived\": xxx, (numeric) The time received in seconds since epoch (midnight Jan 1 1970 GMT). Available \n"
3106 : " for 'send' and 'receive' category of transactions.\n"
3107 : " \"comment\": \"...\", (string) If a comment is associated with the transaction.\n"
3108 : " }\n"
3109 : "]\n"
3110 :
3111 : "\nExamples:\n"
3112 0 : "\nList the most recent 10 transactions in the systems\n" +
3113 0 : HelpExampleCli("listtransactions", "") +
3114 0 : "\nList transactions 100 to 120\n" +
3115 0 : HelpExampleCli("listtransactions", "\"*\" 20 100") +
3116 0 : "\nAs a json rpc call\n" +
3117 0 : HelpExampleRpc("listtransactions", "\"*\", 20, 100")
3118 0 : );
3119 :
3120 : // Make sure the results are valid at least up to the most recent block
3121 : // the user could have gotten from another RPC command prior to now
3122 23 : pwallet->BlockUntilSyncedToCurrentChain();
3123 :
3124 69 : LOCK2(cs_main, pwallet->cs_wallet);
3125 :
3126 23 : if (!request.params[0].isNull() && request.params[0].get_str() != "*") {
3127 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Dummy value must be set to \"*\"");
3128 : }
3129 23 : int nCount = 10;
3130 23 : if (request.params.size() > 1)
3131 4 : nCount = request.params[1].get_int();
3132 23 : int nFrom = 0;
3133 23 : if (request.params.size() > 2)
3134 2 : nFrom = request.params[2].get_int();
3135 23 : isminefilter filter = ISMINE_SPENDABLE;
3136 23 : if ( request.params.size() > 3 && request.params[3].get_bool() )
3137 1 : filter = filter | ISMINE_WATCH_ONLY;
3138 23 : if ( !(request.params.size() > 4) || request.params[4].get_bool() )
3139 23 : filter = filter | ISMINE_SPENDABLE_DELEGATED;
3140 23 : if ( !(request.params.size() > 5) || request.params[5].get_bool() )
3141 23 : filter = filter | ISMINE_COLD;
3142 :
3143 23 : if (nCount < 0)
3144 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count");
3145 23 : if (nFrom < 0)
3146 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative from");
3147 :
3148 46 : UniValue ret(UniValue::VARR);
3149 :
3150 23 : const CWallet::TxItems & txOrdered = pwallet->wtxOrdered;
3151 :
3152 : // iterate backwards until we have nCount items to return:
3153 267 : for (CWallet::TxItems::const_reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it) {
3154 264 : CWalletTx* const pwtx = (*it).second;
3155 264 : ListTransactions(pwallet, *pwtx, 0, true, ret, filter);
3156 264 : if ((int)ret.size() >= (nCount + nFrom)) break;
3157 : }
3158 : // ret is newest to oldest
3159 :
3160 23 : if (nFrom > (int)ret.size())
3161 0 : nFrom = ret.size();
3162 23 : if ((nFrom + nCount) > (int)ret.size())
3163 3 : nCount = ret.size() - nFrom;
3164 :
3165 46 : std::vector<UniValue> arrTmp = ret.getValues();
3166 :
3167 23 : std::vector<UniValue>::iterator first = arrTmp.begin();
3168 23 : std::advance(first, nFrom);
3169 23 : std::vector<UniValue>::iterator last = arrTmp.begin();
3170 23 : std::advance(last, nFrom+nCount);
3171 :
3172 23 : if (last != arrTmp.end()) arrTmp.erase(last, arrTmp.end());
3173 23 : if (first != arrTmp.begin()) arrTmp.erase(arrTmp.begin(), first);
3174 :
3175 23 : std::reverse(arrTmp.begin(), arrTmp.end()); // Return oldest to newest
3176 :
3177 23 : ret.clear();
3178 23 : ret.setArray();
3179 23 : ret.push_backV(arrTmp);
3180 :
3181 23 : return ret;
3182 : }
3183 :
3184 2 : UniValue listsinceblock(const JSONRPCRequest& request)
3185 : {
3186 2 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
3187 :
3188 2 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
3189 0 : return NullUniValue;
3190 :
3191 2 : if (request.fHelp || request.params.size() > 3)
3192 0 : throw std::runtime_error(
3193 : "listsinceblock ( \"blockhash\" target_confirmations include_watchonly)\n"
3194 : "\nGet all transactions in blocks since block [blockhash], or all transactions if omitted\n"
3195 :
3196 : "\nArguments:\n"
3197 : "1. \"blockhash\" (string, optional) The block hash to list transactions since\n"
3198 : "2. target_confirmations: (numeric, optional) The confirmations required, must be 1 or more\n"
3199 : "3. include_watchonly: (bool, optional, default=false) Include transactions to watchonly addresses (see 'importaddress')"
3200 :
3201 : "\nResult:\n"
3202 : "{\n"
3203 : " \"transactions\": [\n"
3204 : " \"address\":\"pivxaddress\", (string) The pivx address of the transaction. Not present for move transactions (category = move).\n"
3205 : " \"category\":\"send|receive\", (string) The transaction category. 'send' has negative amounts, 'receive' has positive amounts.\n"
3206 : " \"amount\": x.xxx, (numeric) The amount in PIV. This is negative for the 'send' category, and for the 'move' category for moves \n"
3207 : " outbound. It is positive for the 'receive' category, and for the 'move' category for inbound funds.\n"
3208 : " \"vout\" : n, (numeric) the vout value\n"
3209 : " \"fee\": x.xxx, (numeric) The amount of the fee in PIV. This is negative and only available for the 'send' category of transactions.\n"
3210 : " \"confirmations\": n, (numeric) The number of confirmations for the transaction. Available for 'send' and 'receive' category of transactions.\n"
3211 : " \"bcconfirmations\" : n, (numeric) DEPRECATED: Will be removed in a future release\n"
3212 : " \"blockhash\": \"hashvalue\", (string) The block hash containing the transaction. Available for 'send' and 'receive' category of transactions.\n"
3213 : " \"blockindex\": n, (numeric) The block index containing the transaction. Available for 'send' and 'receive' category of transactions.\n"
3214 : " \"blocktime\": xxx, (numeric) The block time in seconds since epoch (1 Jan 1970 GMT).\n"
3215 : " \"txid\": \"transactionid\", (string) The transaction id. Available for 'send' and 'receive' category of transactions.\n"
3216 : " \"time\": xxx, (numeric) The transaction time in seconds since epoch (Jan 1 1970 GMT).\n"
3217 : " \"timereceived\": xxx, (numeric) The time received in seconds since epoch (Jan 1 1970 GMT). Available for 'send' and 'receive' category of transactions.\n"
3218 : " \"comment\": \"...\", (string) If a comment is associated with the transaction.\n"
3219 : " \"label\" : \"label\" (string) A comment for the address/transaction, if any\n"
3220 : " \"to\": \"...\", (string) If a comment to is associated with the transaction.\n"
3221 : " ],\n"
3222 : " \"lastblock\": \"lastblockhash\" (string) The hash of the last block\n"
3223 : "}\n"
3224 :
3225 0 : "\nExamples:\n" +
3226 0 : HelpExampleCli("listsinceblock", "") +
3227 0 : HelpExampleCli("listsinceblock", "\"000000000000000bacf66f7497b7dc45ef753ee9a7d38571037cdb1a57f663ad\" 6") +
3228 0 : HelpExampleRpc("listsinceblock", "\"000000000000000bacf66f7497b7dc45ef753ee9a7d38571037cdb1a57f663ad\", 6"));
3229 :
3230 : // Make sure the results are valid at least up to the most recent block
3231 : // the user could have gotten from another RPC command prior to now
3232 2 : pwallet->BlockUntilSyncedToCurrentChain();
3233 :
3234 6 : LOCK2(cs_main, pwallet->cs_wallet);
3235 :
3236 2 : CBlockIndex* pindex = nullptr;
3237 2 : int target_confirms = 1;
3238 2 : isminefilter filter = ISMINE_SPENDABLE_ALL | ISMINE_COLD;
3239 :
3240 2 : if (request.params.size() > 0) {
3241 2 : uint256 blockId(ParseHashV(request.params[0], "blockhash"));
3242 2 : pindex = LookupBlockIndex(blockId);
3243 : }
3244 :
3245 2 : if (request.params.size() > 1) {
3246 0 : target_confirms = request.params[1].get_int();
3247 :
3248 0 : if (target_confirms < 1)
3249 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter");
3250 : }
3251 :
3252 2 : if (request.params.size() > 2)
3253 0 : if (request.params[2].get_bool())
3254 0 : filter = filter | ISMINE_WATCH_ONLY;
3255 :
3256 2 : int depth = pindex ? (1 + chainActive.Height() - pindex->nHeight) : -1;
3257 :
3258 4 : UniValue transactions(UniValue::VARR);
3259 :
3260 22 : for (const auto& entry : pwallet->mapWallet) {
3261 20 : const CWalletTx& tx = entry.second;
3262 :
3263 20 : if (depth == -1 || tx.GetDepthInMainChain() < depth)
3264 1 : ListTransactions(pwallet, tx, 0, true, transactions, filter);
3265 : }
3266 :
3267 2 : CBlockIndex* pblockLast = chainActive[chainActive.Height() + 1 - target_confirms];
3268 2 : uint256 lastblock = pblockLast ? pblockLast->GetBlockHash() : UINT256_ZERO;
3269 :
3270 4 : UniValue ret(UniValue::VOBJ);
3271 2 : ret.pushKV("transactions", transactions);
3272 4 : ret.pushKV("lastblock", lastblock.GetHex());
3273 :
3274 2 : return ret;
3275 : }
3276 :
3277 79 : UniValue gettransaction(const JSONRPCRequest& request)
3278 : {
3279 79 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
3280 :
3281 79 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
3282 0 : return NullUniValue;
3283 :
3284 79 : if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
3285 0 : throw std::runtime_error(
3286 : "gettransaction \"txid\" ( include_watchonly )\n"
3287 : "\nGet detailed information about in-wallet transaction \"txid\"\n"
3288 :
3289 : "\nArguments:\n"
3290 : "1. \"txid\" (string, required) The transaction id\n"
3291 : "2. \"include_watchonly\" (bool, optional, default=false) Whether to include watchonly addresses in balance calculation and details[]\n"
3292 :
3293 : "\nResult:\n"
3294 : "{\n"
3295 : " \"amount\" : x.xxx, (numeric) The transaction amount in PIV\n"
3296 : " \"confirmations\" : n, (numeric) The number of confirmations\n"
3297 : " \"bcconfirmations\" : n, (numeric) DEPRECATED: Will be removed in a future release\n"
3298 : " \"blockhash\" : \"hash\", (string) The block hash\n"
3299 : " \"blockindex\" : xx, (numeric) The block index\n"
3300 : " \"blocktime\" : ttt, (numeric) The time in seconds since epoch (1 Jan 1970 GMT)\n"
3301 : " \"txid\" : \"transactionid\", (string) The transaction id.\n"
3302 : " \"time\" : ttt, (numeric) The transaction time in seconds since epoch (1 Jan 1970 GMT)\n"
3303 : " \"timereceived\" : ttt, (numeric) The time received in seconds since epoch (1 Jan 1970 GMT)\n"
3304 : " \"details\" : [\n"
3305 : " {\n"
3306 : " \"address\" : \"pivxaddress\", (string) The pivx address involved in the transaction\n"
3307 : " \"category\" : \"send|receive\", (string) The category, either 'send' or 'receive'\n"
3308 : " \"amount\" : x.xxx (numeric) The amount in PIV\n"
3309 : " \"label\" : \"label\", (string) A comment for the address/transaction, if any\n"
3310 : " \"vout\" : n, (numeric) the vout value\n"
3311 : " }\n"
3312 : " ,...\n"
3313 : " ],\n"
3314 : " \"hex\" : \"data\" (string) Raw data for transaction\n"
3315 : "}\n"
3316 :
3317 0 : "\nExamples:\n" +
3318 0 : HelpExampleCli("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"") +
3319 0 : HelpExampleCli("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\" true") +
3320 0 : HelpExampleRpc("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\""));
3321 :
3322 : // Make sure the results are valid at least up to the most recent block
3323 : // the user could have gotten from another RPC command prior to now
3324 79 : pwallet->BlockUntilSyncedToCurrentChain();
3325 :
3326 235 : LOCK2(cs_main, pwallet->cs_wallet);
3327 :
3328 79 : uint256 hash(ParseHashV(request.params[0], "txid"));
3329 :
3330 79 : isminefilter filter = ISMINE_SPENDABLE_ALL | ISMINE_COLD;
3331 79 : if (request.params.size() > 1)
3332 2 : if (request.params[1].get_bool())
3333 2 : filter = filter | ISMINE_WATCH_ONLY;
3334 :
3335 156 : UniValue entry(UniValue::VOBJ);
3336 79 : auto it = pwallet->mapWallet.find(hash);
3337 79 : if (it == pwallet->mapWallet.end()) {
3338 4 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id");
3339 : }
3340 77 : const CWalletTx& wtx = it->second;
3341 :
3342 77 : CAmount nCredit = wtx.GetCredit(filter);
3343 77 : CAmount nDebit = wtx.GetDebit(filter);
3344 77 : CAmount nNet = nCredit - nDebit;
3345 77 : CAmount nFee = (wtx.IsFromMe(filter) ? wtx.tx->GetValueOut() - nDebit : 0);
3346 :
3347 154 : entry.pushKV("amount", ValueFromAmount(nNet - nFee));
3348 77 : if (wtx.IsFromMe(filter))
3349 156 : entry.pushKV("fee", ValueFromAmount(nFee));
3350 :
3351 77 : WalletTxToJSON(wtx, entry);
3352 :
3353 77 : UniValue details(UniValue::VARR);
3354 77 : ListTransactions(pwallet, wtx, 0, false, details, filter);
3355 77 : entry.pushKV("details", details);
3356 :
3357 154 : std::string strHex = EncodeHexTx(*wtx.tx);
3358 77 : entry.pushKV("hex", strHex);
3359 :
3360 77 : return entry;
3361 : }
3362 :
3363 3 : UniValue abandontransaction(const JSONRPCRequest& request)
3364 : {
3365 3 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
3366 :
3367 3 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
3368 0 : return NullUniValue;
3369 :
3370 3 : if (request.fHelp || request.params.size() != 1)
3371 0 : throw std::runtime_error(
3372 : "abandontransaction \"txid\"\n"
3373 : "\nMark in-wallet transaction \"txid\" as abandoned\n"
3374 : "This will mark this transaction and all its in-wallet descendants as abandoned which will allow\n"
3375 : "for their inputs to be respent. It can be used to replace \"stuck\" or evicted transactions.\n"
3376 : "It only works on transactions which are not included in a block and are not currently in the mempool.\n"
3377 : "It has no effect on transactions which are already abandoned.\n"
3378 : "\nArguments:\n"
3379 : "1. \"txid\" (string, required) The transaction id\n"
3380 : "\nResult:\n"
3381 : "\nExamples:\n"
3382 0 : + HelpExampleCli("abandontransaction",
3383 : "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"")
3384 0 : + HelpExampleRpc("abandontransaction",
3385 : "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"")
3386 0 : );
3387 :
3388 : // Make sure the results are valid at least up to the most recent block
3389 : // the user could have gotten from another RPC command prior to now
3390 3 : pwallet->BlockUntilSyncedToCurrentChain();
3391 :
3392 7 : LOCK2(cs_main, pwallet->cs_wallet);
3393 :
3394 3 : uint256 hash(ParseHashV(request.params[0], "txid"));
3395 :
3396 3 : if (!pwallet->mapWallet.count(hash))
3397 2 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id");
3398 2 : if (!pwallet->AbandonTransaction(hash))
3399 2 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not eligible for abandonment");
3400 :
3401 1 : return NullUniValue;
3402 : }
3403 :
3404 :
3405 9 : UniValue backupwallet(const JSONRPCRequest& request)
3406 : {
3407 9 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
3408 :
3409 9 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
3410 0 : return NullUniValue;
3411 :
3412 9 : if (request.fHelp || request.params.size() != 1)
3413 0 : throw std::runtime_error(
3414 : "backupwallet \"destination\"\n"
3415 : "\nSafely copies wallet file to destination, which can be a directory or a path with filename.\n"
3416 :
3417 : "\nArguments:\n"
3418 : "1. \"destination\" (string) The destination directory or file\n"
3419 :
3420 0 : "\nExamples:\n" +
3421 0 : HelpExampleCli("backupwallet", "\"backup.dat\"") + HelpExampleRpc("backupwallet", "\"backup.dat\""));
3422 :
3423 : // Make sure the results are valid at least up to the most recent block
3424 : // the user could have gotten from another RPC command prior to now
3425 9 : pwallet->BlockUntilSyncedToCurrentChain();
3426 :
3427 23 : LOCK2(cs_main, pwallet->cs_wallet);
3428 :
3429 18 : std::string strDest = request.params[0].get_str();
3430 9 : if (!pwallet->BackupWallet(strDest))
3431 8 : throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet backup failed!");
3432 :
3433 5 : return NullUniValue;
3434 : }
3435 :
3436 :
3437 8 : UniValue keypoolrefill(const JSONRPCRequest& request)
3438 : {
3439 8 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
3440 :
3441 8 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
3442 0 : return NullUniValue;
3443 :
3444 8 : if (request.fHelp || request.params.size() > 1)
3445 0 : throw std::runtime_error(
3446 : "keypoolrefill ( newsize )\n"
3447 0 : "\nFills the keypool." +
3448 0 : HelpRequiringPassphrase(pwallet) + "\n"
3449 :
3450 : "\nArguments\n"
3451 : "1. newsize (numeric, optional, default=100) The new keypool size\n"
3452 :
3453 0 : "\nExamples:\n" +
3454 0 : HelpExampleCli("keypoolrefill", "") + HelpExampleRpc("keypoolrefill", ""));
3455 :
3456 24 : LOCK2(cs_main, pwallet->cs_wallet);
3457 :
3458 : // 0 is interpreted by TopUpKeyPool() as the default keypool size given by -keypool
3459 8 : unsigned int kpSize = 0;
3460 8 : if (request.params.size() > 0) {
3461 6 : if (request.params[0].get_int() < 0)
3462 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected valid size.");
3463 6 : kpSize = (unsigned int)request.params[0].get_int();
3464 : }
3465 :
3466 8 : EnsureWalletIsUnlocked(pwallet);
3467 8 : pwallet->TopUpKeyPool(kpSize);
3468 :
3469 8 : if (pwallet->GetKeyPoolSize() < kpSize)
3470 0 : throw JSONRPCError(RPC_WALLET_ERROR, "Error refreshing keypool.");
3471 :
3472 8 : return NullUniValue;
3473 : }
3474 :
3475 :
3476 2 : static void LockWallet(CWallet* pWallet)
3477 : {
3478 2 : LOCK(pWallet->cs_wallet);
3479 2 : pWallet->nRelockTime = 0;
3480 2 : pWallet->fWalletUnlockStaking = false;
3481 2 : pWallet->Lock();
3482 2 : }
3483 :
3484 18 : UniValue walletpassphrase(const JSONRPCRequest& request)
3485 : {
3486 18 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
3487 :
3488 18 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
3489 0 : return NullUniValue;
3490 :
3491 18 : if (request.fHelp || request.params.size() < 2 || request.params.size() > 3) {
3492 0 : throw std::runtime_error(
3493 : "walletpassphrase \"passphrase\" timeout ( staking_only )\n"
3494 : "\nStores the wallet decryption key in memory for 'timeout' seconds.\n"
3495 : "This is needed prior to performing transactions related to private keys such as sending PIVs\n"
3496 :
3497 : "\nArguments:\n"
3498 : "1. \"passphrase\" (string, required) The wallet passphrase\n"
3499 : "2. timeout (numeric, required) The time to keep the decryption key in seconds.\n"
3500 : "3. staking_only (boolean, optional, default=false) If is true sending functions are disabled."
3501 :
3502 : "\nNote:\n"
3503 : "Issuing the walletpassphrase command while the wallet is already unlocked will set a new unlock\n"
3504 : "time that overrides the old one. A timeout of \"0\" unlocks until the wallet is closed.\n"
3505 :
3506 : "\nExamples:\n"
3507 0 : "\nUnlock the wallet for 60 seconds\n" +
3508 0 : HelpExampleCli("walletpassphrase", "\"my pass phrase\" 60") +
3509 0 : "\nUnlock the wallet for 60 seconds but allow staking only\n" +
3510 0 : HelpExampleCli("walletpassphrase", "\"my pass phrase\" 60 true") +
3511 0 : "\nLock the wallet again (before 60 seconds)\n" +
3512 0 : HelpExampleCli("walletlock", "") +
3513 0 : "\nAs json rpc call\n" +
3514 0 : HelpExampleRpc("walletpassphrase", "\"my pass phrase\", 60"));
3515 : }
3516 :
3517 50 : LOCK2(cs_main, pwallet->cs_wallet);
3518 :
3519 18 : if (request.fHelp)
3520 0 : return true;
3521 18 : if (!pwallet->IsCrypted())
3522 0 : throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called.");
3523 :
3524 : // Note that the walletpassphrase is stored in params[0] which is not mlock()ed
3525 36 : SecureString strWalletPass;
3526 18 : strWalletPass.reserve(100);
3527 : // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
3528 : // Alternately, find a way to make request.params[0] mlock()'d to begin with.
3529 18 : strWalletPass = request.params[0].get_str().c_str();
3530 :
3531 18 : bool stakingOnly = false;
3532 18 : if (request.params.size() == 3)
3533 2 : stakingOnly = request.params[2].get_bool();
3534 :
3535 18 : if (!pwallet->IsLocked() && pwallet->fWalletUnlockStaking && stakingOnly)
3536 2 : throw JSONRPCError(RPC_WALLET_ALREADY_UNLOCKED, "Error: Wallet is already unlocked.");
3537 :
3538 : // Get the timeout
3539 17 : int64_t nSleepTime = request.params[1].get_int64();
3540 : // Timeout cannot be negative, otherwise it will relock immediately
3541 17 : if (nSleepTime < 0) {
3542 2 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Timeout cannot be negative.");
3543 : }
3544 : // Clamp timeout
3545 16 : constexpr int64_t MAX_SLEEP_TIME = 100000000; // larger values trigger a macos/libevent bug?
3546 16 : if (nSleepTime > MAX_SLEEP_TIME) {
3547 1 : nSleepTime = MAX_SLEEP_TIME;
3548 : }
3549 :
3550 16 : if (!pwallet->Unlock(strWalletPass, stakingOnly))
3551 4 : throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
3552 :
3553 14 : pwallet->TopUpKeyPool();
3554 :
3555 14 : if (nSleepTime > 0) {
3556 14 : pwallet->nRelockTime = GetTime () + nSleepTime;
3557 46 : RPCRunLater (strprintf("lockwallet(%s)", pwallet->GetName()), std::bind (LockWallet, pwallet), nSleepTime);
3558 : }
3559 :
3560 14 : return NullUniValue;
3561 : }
3562 :
3563 :
3564 1 : UniValue walletpassphrasechange(const JSONRPCRequest& request)
3565 : {
3566 1 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
3567 :
3568 1 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
3569 0 : return NullUniValue;
3570 :
3571 1 : if (request.fHelp || request.params.size() != 2) {
3572 0 : throw std::runtime_error(
3573 : "walletpassphrasechange \"oldpassphrase\" \"newpassphrase\"\n"
3574 : "\nChanges the wallet passphrase from 'oldpassphrase' to 'newpassphrase'.\n"
3575 :
3576 : "\nArguments:\n"
3577 : "1. \"oldpassphrase\" (string) The current passphrase\n"
3578 : "2. \"newpassphrase\" (string) The new passphrase\n"
3579 :
3580 0 : "\nExamples:\n" +
3581 0 : HelpExampleCli("walletpassphrasechange", "\"old one\" \"new one\"") + HelpExampleRpc("walletpassphrasechange", "\"old one\", \"new one\""));
3582 : }
3583 :
3584 3 : LOCK2(cs_main, pwallet->cs_wallet);
3585 :
3586 1 : if (request.fHelp)
3587 0 : return true;
3588 1 : if (!pwallet->IsCrypted())
3589 0 : throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrasechange was called.");
3590 :
3591 : // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string)
3592 : // Alternately, find a way to make request.params[0] mlock()'d to begin with.
3593 2 : SecureString strOldWalletPass;
3594 1 : strOldWalletPass.reserve(100);
3595 1 : strOldWalletPass = request.params[0].get_str().c_str();
3596 :
3597 2 : SecureString strNewWalletPass;
3598 1 : strNewWalletPass.reserve(100);
3599 1 : strNewWalletPass = request.params[1].get_str().c_str();
3600 :
3601 1 : if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1)
3602 0 : throw std::runtime_error(
3603 : "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
3604 0 : "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
3605 :
3606 1 : if (!pwallet->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass))
3607 0 : throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
3608 :
3609 1 : return NullUniValue;
3610 : }
3611 :
3612 :
3613 5 : UniValue walletlock(const JSONRPCRequest& request)
3614 : {
3615 5 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
3616 :
3617 5 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
3618 0 : return NullUniValue;
3619 :
3620 5 : if (request.fHelp || !request.params.empty()) {
3621 0 : throw std::runtime_error(
3622 : "walletlock\n"
3623 : "\nRemoves the wallet encryption key from memory, locking the wallet.\n"
3624 : "After calling this method, you will need to call walletpassphrase again\n"
3625 : "before being able to call any methods which require the wallet to be unlocked.\n"
3626 :
3627 : "\nExamples:\n"
3628 0 : "\nSet the passphrase for 2 minutes to perform a transaction\n" +
3629 0 : HelpExampleCli("walletpassphrase", "\"my pass phrase\" 120") +
3630 0 : "\nPerform a send (requires passphrase set)\n" +
3631 0 : HelpExampleCli("sendtoaddress", "\"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\" 1.0") +
3632 0 : "\nClear the passphrase since we are done before 2 minutes is up\n" +
3633 0 : HelpExampleCli("walletlock", "") +
3634 0 : "\nAs json rpc call\n" +
3635 0 : HelpExampleRpc("walletlock", ""));
3636 : }
3637 :
3638 : // Make sure the results are valid at least up to the most recent block
3639 : // the user could have gotten from another RPC command prior to now
3640 5 : pwallet->BlockUntilSyncedToCurrentChain();
3641 :
3642 15 : LOCK2(cs_main, pwallet->cs_wallet);
3643 :
3644 5 : if (request.fHelp)
3645 0 : return true;
3646 5 : if (!pwallet->IsCrypted())
3647 0 : throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletlock was called.");
3648 :
3649 5 : pwallet->Lock();
3650 5 : pwallet->nRelockTime = 0;
3651 :
3652 5 : return NullUniValue;
3653 : }
3654 :
3655 :
3656 7 : UniValue encryptwallet(const JSONRPCRequest& request)
3657 : {
3658 7 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
3659 :
3660 7 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
3661 0 : return NullUniValue;
3662 :
3663 7 : if (request.fHelp || request.params.size() != 1) {
3664 0 : throw std::runtime_error(
3665 : "encryptwallet \"passphrase\"\n"
3666 : "\nEncrypts the wallet with 'passphrase'. This is for first time encryption.\n"
3667 : "After this, any calls that interact with private keys such as sending or signing \n"
3668 : "will require the passphrase to be set prior the making these calls.\n"
3669 : "Use the walletpassphrase call for this, and then walletlock call.\n"
3670 : "If the wallet is already encrypted, use the walletpassphrasechange call.\n"
3671 :
3672 : "\nArguments:\n"
3673 : "1. \"passphrase\" (string) The pass phrase to encrypt the wallet with. It must be at least 1 character, but should be long.\n"
3674 :
3675 : "\nExamples:\n"
3676 0 : "\nEncrypt you wallet\n" +
3677 0 : HelpExampleCli("encryptwallet", "\"my pass phrase\"") +
3678 0 : "\nNow set the passphrase to use the wallet, such as for signing or sending PIVs\n" +
3679 0 : HelpExampleCli("walletpassphrase", "\"my pass phrase\"") +
3680 0 : "\nNow we can so something like sign\n" +
3681 0 : HelpExampleCli("signmessage", "\"pivxaddress\" \"test message\"") +
3682 0 : "\nNow lock the wallet again by removing the passphrase\n" +
3683 0 : HelpExampleCli("walletlock", "") +
3684 0 : "\nAs a json rpc call\n" +
3685 0 : HelpExampleRpc("encryptwallet", "\"my pass phrase\""));
3686 : }
3687 :
3688 20 : LOCK2(cs_main, pwallet->cs_wallet);
3689 :
3690 7 : if (request.fHelp)
3691 0 : return true;
3692 7 : if (pwallet->IsCrypted())
3693 2 : throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an encrypted wallet, but encryptwallet was called.");
3694 :
3695 : // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
3696 : // Alternately, find a way to make request.params[0] mlock()'d to begin with.
3697 12 : SecureString strWalletPass;
3698 6 : strWalletPass.reserve(100);
3699 6 : strWalletPass = request.params[0].get_str().c_str();
3700 :
3701 6 : if (strWalletPass.length() < 1)
3702 0 : throw std::runtime_error(
3703 : "encryptwallet <passphrase>\n"
3704 0 : "Encrypts the wallet with <passphrase>.");
3705 :
3706 6 : if (!pwallet->EncryptWallet(strWalletPass))
3707 0 : throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: Failed to encrypt the wallet.");
3708 :
3709 6 : return "wallet encrypted; The keypool has been flushed, you need to make a new backup.";
3710 : }
3711 :
3712 150 : UniValue listunspent(const JSONRPCRequest& request)
3713 : {
3714 150 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
3715 :
3716 150 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
3717 0 : return NullUniValue;
3718 :
3719 150 : if (request.fHelp || request.params.size() > 6)
3720 0 : throw std::runtime_error(
3721 : "listunspent ( minconf maxconf [\"address\",...] watchonly_config [query_options] include_unsafe)\n"
3722 : "\nReturns array of unspent transaction outputs\n"
3723 : "with between minconf and maxconf (inclusive) confirmations.\n"
3724 : "Optionally filter to only include txouts paid to specified addresses.\n"
3725 : "Results are an array of Objects, each of which has:\n"
3726 : "{txid, vout, scriptPubKey, amount, confirmations, spendable}\n"
3727 :
3728 : "\nArguments:\n"
3729 : "1. minconf (numeric, optional, default=1) The minimum confirmations to filter\n"
3730 : "2. maxconf (numeric, optional, default=9999999) The maximum confirmations to filter\n"
3731 : "3. \"addresses\" (string) A json array of pivx addresses to filter\n"
3732 : " [\n"
3733 : " \"address\" (string) pivx address\n"
3734 : " ,...\n"
3735 : " ]\n"
3736 : "4. watchonly_config (numeric, optional, default=1) 1 = list regular unspent transactions, 2 = list all unspent transactions (including watchonly)\n"
3737 : "5. query_options (json, optional) JSON with query options\n"
3738 : " {\n"
3739 0 : " \"minimumAmount\" (numeric or string, default=0) Minimum value of each UTXO in " + CURRENCY_UNIT + "\n"
3740 0 : " \"maximumAmount\" (numeric or string, default=unlimited) Maximum value of each UTXO in " + CURRENCY_UNIT + "\n"
3741 : " \"maximumCount\" (numeric or string, default=unlimited) Maximum number of UTXOs\n"
3742 0 : " \"minimumSumAmount\" (numeric or string, default=unlimited) Minimum sum value of all UTXOs in " + CURRENCY_UNIT + "\n"
3743 : " }\n"
3744 : "6. include_unsafe (bool, optional, default=true) Include outputs that are not safe to spend\n"
3745 : " See description of \"safe\" attribute below.\n"
3746 :
3747 : "\nResult\n"
3748 : "[ (array of json object)\n"
3749 : " {\n"
3750 : " \"txid\" : \"txid\", (string) the transaction id\n"
3751 : " \"generated\" : true|false (boolean) true if txout is a coinstake transaction output\n"
3752 : " \"vout\" : n, (numeric) the vout value\n"
3753 : " \"address\" : \"address\", (string) the pivx address\n"
3754 : " \"label\" : \"label\", (string) The associated label, or \"\" for the default label\n"
3755 : " \"scriptPubKey\" : \"key\", (string) the script key\n"
3756 : " \"redeemScript\" : \"key\", (string) the redeemscript key\n"
3757 : " \"amount\" : x.xxx, (numeric) the transaction amount in PIV\n"
3758 : " \"confirmations\" : n, (numeric) The number of confirmations\n"
3759 : " \"spendable\" : true|false (boolean) Whether we have the private keys to spend this output\n"
3760 : " \"solvable\" : xxx (boolean) Whether we know how to spend this output, ignoring the lack of keys\n"
3761 : " \"safe\" : xxx (boolean) Whether this output is considered safe to spend. Unconfirmed transactions\n"
3762 : " from outside keys and unconfirmed replacement transactions are considered unsafe\n"
3763 : " and are not eligible for spending by fundrawtransaction and sendtoaddress.\n"
3764 : " }\n"
3765 : " ,...\n"
3766 : "]\n"
3767 :
3768 0 : "\nExamples\n" +
3769 0 : HelpExampleCli("listunspent", "") + HelpExampleCli("listunspent", "6 9999999 \"[\\\"1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\",\\\"1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"")
3770 0 : + HelpExampleRpc("listunspent", "6, 9999999 \"[\\\"1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\",\\\"1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"")
3771 0 : + HelpExampleCli("listunspent", "6 9999999 '[]' 1 '{ \"minimumAmount\": 0.005 }'")
3772 0 : + HelpExampleCli("listunspent", "6 9999999 '[]' 1 '{ \"minimumAmount\": 0.005 }' false")
3773 0 : + HelpExampleRpc("listunspent", "6, 9999999, [] , 1, { \"minimumAmount\": 0.005 } ")
3774 0 : );
3775 :
3776 : // Make sure the results are valid at least up to the most recent block
3777 : // the user could have gotten from another RPC command prior to now
3778 150 : pwallet->BlockUntilSyncedToCurrentChain();
3779 :
3780 150 : int nMinDepth = 1;
3781 150 : if (request.params.size() > 0) {
3782 39 : RPCTypeCheckArgument(request.params[0], UniValue::VNUM);
3783 39 : nMinDepth = request.params[0].get_int();
3784 : }
3785 :
3786 150 : int nMaxDepth = 9999999;
3787 150 : if (request.params.size() > 1) {
3788 34 : RPCTypeCheckArgument(request.params[1], UniValue::VNUM);
3789 34 : nMaxDepth = request.params[1].get_int();
3790 : }
3791 :
3792 150 : CWallet::AvailableCoinsFilter coinFilter;
3793 :
3794 300 : std::set<CTxDestination> destinations;
3795 150 : if (request.params.size() > 2) {
3796 33 : RPCTypeCheckArgument(request.params[2], UniValue::VARR);
3797 33 : UniValue inputs = request.params[2].get_array();
3798 36 : for (unsigned int inx = 0; inx < inputs.size(); inx++) {
3799 3 : const UniValue& input = inputs[inx];
3800 6 : CTxDestination dest = DecodeDestination(input.get_str());
3801 3 : if (!IsValidDestination(dest))
3802 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid PIVX address: ") + input.get_str());
3803 3 : if (destinations.count(dest))
3804 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ") + input.get_str());
3805 3 : destinations.insert(dest);
3806 : }
3807 33 : coinFilter.onlyFilteredDest = &destinations;
3808 : }
3809 :
3810 : // List watch only utxo
3811 150 : int nWatchonlyConfig = 1;
3812 150 : if(request.params.size() > 3) {
3813 30 : RPCTypeCheckArgument(request.params[3], UniValue::VNUM);
3814 30 : nWatchonlyConfig = request.params[3].get_int();
3815 30 : if (nWatchonlyConfig > 2 || nWatchonlyConfig < 1)
3816 0 : nWatchonlyConfig = 1;
3817 : }
3818 :
3819 150 : if (request.params.size() > 4) {
3820 29 : const UniValue& options = request.params[4].get_obj();
3821 :
3822 174 : RPCTypeCheckObj(options,
3823 : {
3824 29 : {"minimumAmount", UniValueType()},
3825 29 : {"maximumAmount", UniValueType()},
3826 29 : {"minimumSumAmount", UniValueType()},
3827 29 : {"maximumCount", UniValueType(UniValue::VNUM)},
3828 : },
3829 145 : true, true);
3830 :
3831 58 : if (options.exists("minimumAmount")) {
3832 17 : coinFilter.nMinOutValue = AmountFromValue(options["minimumAmount"]);
3833 : }
3834 :
3835 58 : if (options.exists("maximumAmount")) {
3836 13 : coinFilter.nMaxOutValue = AmountFromValue(options["maximumAmount"]);
3837 : }
3838 :
3839 87 : if (options.exists("minimumSumAmount")) {
3840 8 : coinFilter.nMinimumSumAmount = AmountFromValue(options["minimumSumAmount"]);
3841 : }
3842 :
3843 58 : if (options.exists("maximumCount"))
3844 4 : coinFilter.nMaximumCount = options["maximumCount"].get_int64();
3845 : }
3846 :
3847 300 : CCoinControl coinControl;
3848 150 : coinControl.fAllowWatchOnly = nWatchonlyConfig == 2;
3849 :
3850 150 : bool include_unsafe = request.params.size() < 6 || request.params[5].get_bool();
3851 150 : coinFilter.fOnlySafe = !include_unsafe;
3852 :
3853 300 : UniValue results(UniValue::VARR);
3854 300 : std::vector<COutput> vecOutputs;
3855 :
3856 450 : LOCK2(cs_main, pwallet->cs_wallet);
3857 150 : pwallet->AvailableCoins(&vecOutputs, &coinControl, coinFilter);
3858 1968590 : for (const COutput& out : vecOutputs) {
3859 1968440 : if (out.nDepth < nMinDepth || out.nDepth > nMaxDepth)
3860 70 : continue;
3861 :
3862 1968370 : UniValue entry(UniValue::VOBJ);
3863 3936750 : entry.pushKV("txid", out.tx->GetHash().GetHex());
3864 1968370 : entry.pushKV("vout", out.i);
3865 1972530 : entry.pushKV("generated", out.tx->IsCoinStake() || out.tx->IsCoinBase());
3866 3936750 : CTxDestination address;
3867 1968370 : const CScript& scriptPubKey = out.tx->tx->vout[out.i].scriptPubKey;
3868 1968370 : if (ExtractDestination(scriptPubKey, address)) {
3869 3936750 : entry.pushKV("address", EncodeDestination(address));
3870 1968370 : if (pwallet->HasAddressBook(address)) {
3871 3849220 : entry.pushKV("label", pwallet->GetNameForAddressBookEntry(address));
3872 : }
3873 1968370 : if (scriptPubKey.IsPayToScriptHash()) {
3874 5 : const CScriptID& hash = boost::get<CScriptID>(address);
3875 10 : CScript redeemScript;
3876 5 : if (pwallet->GetCScript(hash, redeemScript))
3877 16 : entry.pushKV("redeemScript", HexStr(redeemScript));
3878 : }
3879 : }
3880 5905120 : entry.pushKV("scriptPubKey", HexStr(scriptPubKey));
3881 3936750 : entry.pushKV("amount", ValueFromAmount(out.tx->tx->vout[out.i].nValue));
3882 1968370 : entry.pushKV("confirmations", out.nDepth);
3883 1968370 : entry.pushKV("spendable", out.fSpendable);
3884 1968370 : entry.pushKV("solvable", out.fSolvable);
3885 1968370 : entry.pushKV("safe", out.fSafe);
3886 1968370 : results.push_back(entry);
3887 : }
3888 :
3889 150 : return results;
3890 : }
3891 :
3892 39 : UniValue fundrawtransaction(const JSONRPCRequest& request)
3893 : {
3894 39 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
3895 :
3896 39 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
3897 0 : return NullUniValue;
3898 :
3899 39 : if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
3900 0 : throw std::runtime_error(
3901 : "fundrawtransaction \"hexstring\" ( options )\n"
3902 : "\nAdd inputs to a transaction until it has enough in value to meet its out value.\n"
3903 : "This will not modify existing inputs, and will add one change output to the outputs.\n"
3904 : "Note that inputs which were signed may need to be resigned after completion since in/outputs have been added.\n"
3905 : "The inputs added will not be signed, use signrawtransaction for that.\n"
3906 : "Note that all existing inputs must have their previous output transaction be in the wallet.\n"
3907 : "Note that all inputs selected must be of standard form and P2SH scripts must be "
3908 : "in the wallet using importaddress or addmultisigaddress (to calculate fees).\n"
3909 : "You can see whether this is the case by checking the \"solvable\" field in the listunspent output.\n"
3910 : "Only pay-to-pubkey, multisig, and P2SH versions thereof are currently supported for watch-only\n"
3911 :
3912 : "\nArguments:\n"
3913 : "1. \"hexstring\" (string, required) The hex string of the raw transaction\n"
3914 : "2. options (object, optional)\n"
3915 : " {\n"
3916 : " \"changeAddress\" (string, optional, default pool address) The PIVX address to receive the change\n"
3917 : " \"changePosition\" (numeric, optional, default random) The index of the change output\n"
3918 : " \"includeWatching\" (boolean, optional, default false) Also select inputs which are watch only\n"
3919 : " \"lockUnspents\" (boolean, optional, default false) Lock selected unspent outputs\n"
3920 : " \"feeRate\" (numeric, optional, default 0=estimate) Set a specific feerate (PIV per KB)\n"
3921 : " \"subtractFeeFromOutputs\" (array, optional) A json array of integers.\n"
3922 : " The fee will be equally deducted from the amount of each specified output.\n"
3923 : " The outputs are specified by their zero-based index, before any change output is added.\n"
3924 : " Those recipients will receive less PIV than you enter in their corresponding amount field.\n"
3925 : " If no outputs are specified here, the sender pays the fee.\n"
3926 : " [vout_index,...]\n"
3927 : " }\n"
3928 : "\nResult:\n"
3929 : "{\n"
3930 : " \"hex\": \"value\", (string) The resulting raw transaction (hex-encoded string)\n"
3931 : " \"fee\": n, (numeric) The fee added to the transaction\n"
3932 : " \"changepos\": n (numeric) The position of the added change output, or -1\n"
3933 : "}\n"
3934 : "\"hex\" \n"
3935 : "\nExamples:\n"
3936 : "\nCreate a transaction with no inputs\n"
3937 0 : + HelpExampleCli("createrawtransaction", "\"[]\" \"{\\\"myaddress\\\":0.01}\"") +
3938 : "\nAdd sufficient unsigned inputs to meet the output value\n"
3939 0 : + HelpExampleCli("fundrawtransaction", "\"rawtransactionhex\"") +
3940 : "\nSign the transaction\n"
3941 0 : + HelpExampleCli("signrawtransaction", "\"fundedtransactionhex\"") +
3942 : "\nSend the transaction\n"
3943 0 : + HelpExampleCli("sendrawtransaction", "\"signedtransactionhex\"")
3944 0 : );
3945 :
3946 : // Make sure the results are valid at least up to the most recent block
3947 : // the user could have gotten from another RPC command prior to now
3948 39 : pwallet->BlockUntilSyncedToCurrentChain();
3949 :
3950 39 : RPCTypeCheck(request.params, {UniValue::VSTR});
3951 :
3952 73 : CTxDestination changeAddress = CNoDestination();
3953 39 : int changePosition = -1;
3954 39 : bool includeWatching = false;
3955 39 : bool lockUnspents = false;
3956 78 : UniValue subtractFeeFromOutputs;
3957 78 : std::set<int> setSubtractFeeFromOutputs;
3958 39 : CFeeRate feeRate = CFeeRate(0);
3959 39 : bool overrideEstimatedFeerate = false;
3960 :
3961 39 : if (request.params.size() > 1) {
3962 20 : RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VOBJ});
3963 28 : UniValue options = request.params[1];
3964 120 : RPCTypeCheckObj(options,
3965 : {
3966 15 : {"changeAddress", UniValueType(UniValue::VSTR)},
3967 15 : {"changePosition", UniValueType(UniValue::VNUM)},
3968 15 : {"includeWatching", UniValueType(UniValue::VBOOL)},
3969 15 : {"lockUnspents", UniValueType(UniValue::VBOOL)},
3970 15 : {"feeRate", UniValueType()}, // will be checked below
3971 17 : {"subtractFeeFromOutputs", UniValueType(UniValue::VARR)},
3972 : },
3973 105 : true, true);
3974 :
3975 28 : if (options.exists("changeAddress")) {
3976 6 : changeAddress = DecodeDestination(options["changeAddress"].get_str());
3977 :
3978 3 : if (!IsValidDestination(changeAddress))
3979 2 : throw JSONRPCError(RPC_INVALID_PARAMETER, "changeAddress must be a valid PIVX address");
3980 : }
3981 :
3982 26 : if (options.exists("changePosition"))
3983 4 : changePosition = options["changePosition"].get_int();
3984 :
3985 26 : if (options.exists("includeWatching"))
3986 2 : includeWatching = options["includeWatching"].get_bool();
3987 :
3988 26 : if (options.exists("lockUnspents"))
3989 0 : lockUnspents = options["lockUnspents"].get_bool();
3990 :
3991 26 : if (options.exists("feeRate")) {
3992 4 : feeRate = CFeeRate(AmountFromValue(options["feeRate"]));
3993 4 : overrideEstimatedFeerate = true;
3994 : }
3995 :
3996 39 : if (options.exists("subtractFeeFromOutputs")) {
3997 10 : subtractFeeFromOutputs = options["subtractFeeFromOutputs"].get_array();
3998 : }
3999 : }
4000 :
4001 : // parse hex string from parameter
4002 74 : CMutableTransaction origTx;
4003 37 : if (!DecodeHexTx(origTx, request.params[0].get_str()))
4004 0 : throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
4005 :
4006 37 : if (origTx.vout.size() == 0)
4007 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "TX must have at least one output");
4008 :
4009 37 : if (changePosition != -1 && (changePosition < 0 || (unsigned int) changePosition > origTx.vout.size()))
4010 2 : throw JSONRPCError(RPC_INVALID_PARAMETER, "changePosition out of bounds");
4011 :
4012 42 : for (unsigned int idx = 0; idx < subtractFeeFromOutputs.size(); idx++) {
4013 6 : int pos = subtractFeeFromOutputs[idx].get_int();
4014 6 : if (setSubtractFeeFromOutputs.count(pos))
4015 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid parameter, duplicated position: %d", pos));
4016 6 : if (pos < 0)
4017 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid parameter, negative position: %d", pos));
4018 6 : if (pos >= int(origTx.vout.size()))
4019 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid parameter, position too large: %d", pos));
4020 6 : setSubtractFeeFromOutputs.insert(pos);
4021 : }
4022 :
4023 38 : CMutableTransaction tx(origTx);
4024 36 : CAmount nFeeOut;
4025 72 : std::string strFailReason;
4026 36 : if(!pwallet->FundTransaction(tx, nFeeOut, overrideEstimatedFeerate, feeRate,
4027 : changePosition, strFailReason, includeWatching,
4028 : lockUnspents, setSubtractFeeFromOutputs, changeAddress)) {
4029 2 : throw JSONRPCError(RPC_INTERNAL_ERROR, strFailReason);
4030 : }
4031 :
4032 68 : UniValue result(UniValue::VOBJ);
4033 102 : result.pushKV("hex", EncodeHexTx(tx));
4034 34 : result.pushKV("changepos", changePosition);
4035 68 : result.pushKV("fee", ValueFromAmount(nFeeOut));
4036 :
4037 34 : return result;
4038 : }
4039 :
4040 22 : UniValue lockunspent(const JSONRPCRequest& request)
4041 : {
4042 22 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
4043 :
4044 22 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
4045 0 : return NullUniValue;
4046 :
4047 22 : if (request.fHelp || request.params.size() < 2 || request.params.size() > 3)
4048 0 : throw std::runtime_error(
4049 : "lockunspent unlock transparent [{\"txid\":\"txid\",\"vout\":n},...]\n"
4050 : "\nUpdates list of temporarily unspendable outputs.\n"
4051 : "Temporarily lock (unlock=false) or unlock (unlock=true) specified transparent (transparent=true) or shielded (transparent=false) transaction outputs.\n"
4052 : "A locked transaction output will not be chosen by automatic coin selection, when spending PIVs.\n"
4053 : "Locks are stored in memory only. Nodes start with zero locked outputs, and the locked output list\n"
4054 : "is always cleared (by virtue of process exit) when a node stops or fails.\n"
4055 : "Also see the listunspent call\n"
4056 :
4057 : "\nArguments:\n"
4058 : "1. unlock (boolean, required) Whether to unlock (true) or lock (false) the specified transactions\n"
4059 : "2. transparent (boolean, required) Whether the given transaction outputs are transparent (true) or shielded (false)\n"
4060 : "2. \"transactions\" (string, required) A json array of objects. Each object the txid (string) vout (numeric)\n"
4061 : " [ (json array of json objects)\n"
4062 : " {\n"
4063 : " \"txid\":\"id\", (string) The transaction id\n"
4064 : " \"vout\": n (numeric) The output number\n"
4065 : " }\n"
4066 : " ,...\n"
4067 : " ]\n"
4068 :
4069 : "\nResult:\n"
4070 : "true|false (boolean) Whether the command was successful or not\n"
4071 :
4072 : "\nExamples:\n"
4073 0 : "\nList the unspent transactions\n" +
4074 0 : HelpExampleCli("listunspent", "") +
4075 0 : "\nLock an unspent transaction\n" +
4076 0 : HelpExampleCli("lockunspent", "false true \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") +
4077 0 : "\nList the locked transactions\n" +
4078 0 : HelpExampleCli("listlockunspent", "") +
4079 0 : "\nUnlock the transaction again\n" +
4080 0 : HelpExampleCli("lockunspent", "true true \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") +
4081 0 : "\nAs a json rpc call\n" +
4082 0 : HelpExampleRpc("lockunspent", "false false, \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\""));
4083 :
4084 : // Make sure the results are valid at least up to the most recent block
4085 : // the user could have gotten from another RPC command prior to now
4086 22 : pwallet->BlockUntilSyncedToCurrentChain();
4087 :
4088 66 : LOCK2(cs_main, pwallet->cs_wallet);
4089 :
4090 22 : if (request.params.size() == 2)
4091 0 : RPCTypeCheck(request.params, {UniValue::VBOOL, UniValue::VBOOL});
4092 : else
4093 44 : RPCTypeCheck(request.params, {UniValue::VBOOL, UniValue::VBOOL, UniValue::VARR});
4094 :
4095 22 : bool fUnlock = request.params[0].get_bool();
4096 22 : bool transparent = request.params[1].get_bool();
4097 :
4098 22 : if (request.params.size() == 2) {
4099 0 : if (fUnlock) transparent ? pwallet->UnlockAllCoins() : pwallet->UnlockAllNotes();
4100 0 : return true;
4101 : }
4102 :
4103 22 : UniValue output_request = request.params[2].get_array();
4104 :
4105 : // Create and validate the COutPoints first.
4106 44 : std::vector<COutPoint> outputs;
4107 22 : std::vector<SaplingOutPoint> saplingOutputs;
4108 22 : transparent ? outputs.reserve(output_request.size()) : saplingOutputs.reserve(output_request.size());
4109 :
4110 44 : for (unsigned int idx = 0; idx < output_request.size(); idx++) {
4111 22 : const UniValue& output = output_request[idx];
4112 22 : if (!output.isObject())
4113 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected object");
4114 22 : const UniValue& o = output.get_obj();
4115 :
4116 88 : RPCTypeCheckObj(o,
4117 : {
4118 22 : {"txid", UniValueType(UniValue::VSTR)},
4119 22 : {"vout", UniValueType(UniValue::VNUM)},
4120 66 : });
4121 :
4122 22 : const uint256 txid(ParseHashO(o, "txid"));
4123 :
4124 22 : const int nOutput = find_value(o, "vout").get_int();
4125 22 : if (nOutput < 0) {
4126 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive");
4127 : }
4128 :
4129 22 : bool is_locked = false;
4130 :
4131 22 : const auto it = pwallet->mapWallet.find(txid);
4132 22 : if (it == pwallet->mapWallet.end()) {
4133 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, unknown transaction");
4134 : }
4135 :
4136 22 : const CWalletTx& wtx = it->second;
4137 22 : if (transparent) {
4138 20 : const COutPoint outpt(txid, nOutput);
4139 20 : if (outpt.n >= wtx.tx->vout.size()) {
4140 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout index out of bounds");
4141 : }
4142 :
4143 20 : if (pwallet->IsSpent(outpt.hash, outpt.n)) {
4144 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected unspent output");
4145 : }
4146 20 : is_locked = pwallet->IsLockedCoin(outpt.hash, outpt.n);
4147 : } else {
4148 2 : const SaplingOutPoint op(txid, nOutput);
4149 2 : if (op.n >= wtx.tx->sapData->vShieldedOutput.size()) {
4150 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout index out of bounds");
4151 : }
4152 2 : if (pwallet->IsSaplingSpent(op)) {
4153 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected unspent output");
4154 : }
4155 2 : is_locked = pwallet->IsLockedNote(op);
4156 : }
4157 :
4158 22 : if (fUnlock && !is_locked) {
4159 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected locked output");
4160 : }
4161 :
4162 22 : if (!fUnlock && is_locked) {
4163 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, output already locked");
4164 : }
4165 :
4166 22 : transparent ? outputs.push_back(COutPoint(txid, nOutput)) : saplingOutputs.push_back(SaplingOutPoint(txid, nOutput));
4167 : }
4168 :
4169 : // Atomically set (un)locked status for the outputs.
4170 42 : for (const COutPoint& outpt : outputs) {
4171 20 : if (fUnlock) pwallet->UnlockCoin(outpt);
4172 19 : else pwallet->LockCoin(outpt);
4173 : }
4174 :
4175 24 : for (const SaplingOutPoint& op : saplingOutputs) {
4176 2 : if (fUnlock) pwallet->UnlockNote(op);
4177 : else
4178 1 : pwallet->LockNote(op);
4179 : }
4180 :
4181 22 : return true;
4182 : }
4183 :
4184 52 : UniValue listlockunspent(const JSONRPCRequest& request)
4185 : {
4186 52 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
4187 :
4188 52 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
4189 0 : return NullUniValue;
4190 :
4191 52 : if (request.fHelp || request.params.size() > 0)
4192 0 : throw std::runtime_error(
4193 : "listlockunspent\n"
4194 : "\nReturns list of temporarily unspendable outputs.\n"
4195 : "See the lockunspent call to lock and unlock transactions for spending.\n"
4196 :
4197 : "\nResult:\n"
4198 : "{\n"
4199 : " \"transparent\": [ (array of json objects)\n"
4200 : " {\n"
4201 : " \"txid\": \"transactionid\" (string) The transaction id locked\n"
4202 : " \"vout\": n (numeric) The vout value\n"
4203 : " },\n"
4204 : " ...\n"
4205 : " ],\n"
4206 : " \"shielded\": [ (array of json objects)\n"
4207 : " {\n"
4208 : " \"txid\": \"transactionid\" (string) The transaction id locked\n"
4209 : " \"vShieldedOutput\": n (numeric) The vout value\n"
4210 : " },\n"
4211 : " ...\n"
4212 : " ],\n"
4213 : "}\n"
4214 :
4215 : "\nExamples:\n"
4216 0 : "\nList the unspent transactions\n" +
4217 0 : HelpExampleCli("listunspent", "") +
4218 0 : "\nLock an unspent transaction\n" +
4219 0 : HelpExampleCli("lockunspent", "false true \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") +
4220 0 : "\nList the locked transactions\n" +
4221 0 : HelpExampleCli("listlockunspent", "") +
4222 0 : "\nUnlock the transaction again\n" +
4223 0 : HelpExampleCli("lockunspent", "true true \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") +
4224 0 : "\nAs a json rpc call\n" +
4225 0 : HelpExampleRpc("listlockunspent", ""));
4226 :
4227 156 : LOCK2(cs_main, pwallet->cs_wallet);
4228 :
4229 :
4230 104 : UniValue ret(UniValue::VOBJ);
4231 104 : UniValue transparent(UniValue::VARR);
4232 52 : UniValue shielded(UniValue::VARR);
4233 :
4234 104 : std::set<COutPoint> vOutpts = pwallet->ListLockedCoins();
4235 220 : for (const COutPoint& outpt : vOutpts) {
4236 336 : UniValue o(UniValue::VOBJ);
4237 :
4238 336 : o.pushKV("txid", outpt.hash.GetHex());
4239 168 : o.pushKV("vout", (int)outpt.n);
4240 168 : transparent.push_back(o);
4241 : }
4242 :
4243 104 : std::set<SaplingOutPoint> sOps = pwallet->ListLockedNotes();
4244 54 : for (const SaplingOutPoint& op : sOps) {
4245 4 : UniValue o(UniValue::VOBJ);
4246 :
4247 4 : o.pushKV("txid", op.hash.GetHex());
4248 2 : o.pushKV("vShieldedOutput", (int)op.n);
4249 2 : shielded.push_back(o);
4250 : }
4251 52 : ret.pushKV("transparent", transparent);
4252 52 : ret.pushKV("shielded", shielded);
4253 52 : return ret;
4254 : }
4255 :
4256 7 : UniValue settxfee(const JSONRPCRequest& request)
4257 : {
4258 7 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
4259 :
4260 7 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
4261 0 : return NullUniValue;
4262 :
4263 7 : if (request.fHelp || request.params.size() < 1 || request.params.size() > 1)
4264 0 : throw std::runtime_error(
4265 : "settxfee amount\n"
4266 : "\nSet the transaction fee per kB.\n"
4267 :
4268 : "\nArguments:\n"
4269 : "1. amount (numeric, required) The transaction fee in PIV/kB rounded to the nearest 0.00000001\n"
4270 :
4271 : "\nResult\n"
4272 : "true|false (boolean) Returns true if successful\n"
4273 0 : "\nExamples:\n" +
4274 0 : HelpExampleCli("settxfee", "0.00001") + HelpExampleRpc("settxfee", "0.00001"));
4275 :
4276 21 : LOCK2(cs_main, pwallet->cs_wallet);
4277 :
4278 : // Amount
4279 7 : CAmount nAmount = 0;
4280 7 : if (request.params[0].get_real() != 0.0)
4281 6 : nAmount = AmountFromValue(request.params[0]); // rejects 0.0 amounts
4282 :
4283 7 : payTxFee = CFeeRate(nAmount, 1000);
4284 7 : return true;
4285 : }
4286 :
4287 69 : UniValue getwalletinfo(const JSONRPCRequest& request)
4288 : {
4289 69 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
4290 :
4291 68 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
4292 0 : return NullUniValue;
4293 :
4294 66 : if (request.fHelp || request.params.size() != 0)
4295 0 : throw std::runtime_error(
4296 : "getwalletinfo\n"
4297 : "Returns an object containing various wallet state info.\n"
4298 :
4299 : "\nResult:\n"
4300 : "{\n"
4301 : " \"walletname\": xxxxx, (string) the wallet name\n"
4302 : " \"walletversion\": xxxxx, (numeric) the wallet version\n"
4303 : " \"balance\": xxxxxxx, (numeric) the total PIV balance of the wallet (cold balance excluded)\n"
4304 : " \"delegated_balance\": xxxxx, (numeric) the PIV balance held in P2CS (cold staking) contracts\n"
4305 : " \"cold_staking_balance\": xx, (numeric) the PIV balance held in cold staking addresses\n"
4306 : " \"unconfirmed_balance\": xxx, (numeric) the total unconfirmed balance of the wallet in PIV\n"
4307 : " \"immature_delegated_balance\": xxxxxx, (numeric) the delegated immature balance of the wallet in PIV\n"
4308 : " \"immature_cold_staking_balance\": xxxxxx, (numeric) the cold-staking immature balance of the wallet in PIV\n"
4309 : " \"immature_balance\": xxxxxx, (numeric) the total immature balance of the wallet in PIV\n"
4310 : " \"txcount\": xxxxxxx, (numeric) the total number of transactions in the wallet\n"
4311 : " \"autocombine_enabled\": true|false, (boolean) true if autocombine is enabled, otherwise false\n"
4312 : " \"autocombine_threshold\": x.xxx, (numeric) the current autocombine threshold in PIV\n"
4313 : " \"keypoololdest\": xxxxxx, (numeric) the timestamp (seconds since GMT epoch) of the oldest pre-generated key in the key pool\n"
4314 : " \"keypoolsize\": xxxx, (numeric) how many new keys are pre-generated (only counts external keys)\n"
4315 : " \"keypoolsize_hd_internal\": xxxx, (numeric) how many new keys are pre-generated for internal use (used for change outputs, only appears if the wallet is using this feature, otherwise external keys are used)\n"
4316 : " \"keypoolsize_hd_staking\": xxxx, (numeric) how many new keys are pre-generated for staking use (used for staking contracts, only appears if the wallet is using this feature)\n"
4317 : " \"unlocked_until\": ttt, (numeric) the timestamp in seconds since epoch (midnight Jan 1 1970 GMT) that the wallet is unlocked for transfers, or 0 if the wallet is locked\n"
4318 : " \"paytxfee\": x.xxxx (numeric) the transaction fee configuration, set in PIV/kB\n"
4319 : " \"hdseedid\": \"<hash160>\" (string, optional) the Hash160 of the HD seed (only present when HD is enabled)\n"
4320 : " \"last_processed_block\": xxxxx, (numeric) the last block processed block height\n"
4321 : "}\n"
4322 :
4323 0 : "\nExamples:\n" +
4324 0 : HelpExampleCli("getwalletinfo", "") + HelpExampleRpc("getwalletinfo", ""));
4325 :
4326 : // Make sure the results are valid at least up to the most recent block
4327 : // the user could have gotten from another RPC command prior to now
4328 66 : pwallet->BlockUntilSyncedToCurrentChain();
4329 :
4330 198 : LOCK2(cs_main, pwallet->cs_wallet);
4331 :
4332 132 : UniValue obj(UniValue::VOBJ);
4333 66 : obj.pushKV("walletname", pwallet->GetName());
4334 66 : obj.pushKV("walletversion", pwallet->GetVersion());
4335 132 : obj.pushKV("balance", ValueFromAmount(pwallet->GetAvailableBalance()));
4336 132 : obj.pushKV("delegated_balance", ValueFromAmount(pwallet->GetDelegatedBalance()));
4337 132 : obj.pushKV("cold_staking_balance", ValueFromAmount(pwallet->GetColdStakingBalance()));
4338 132 : obj.pushKV("unconfirmed_balance", ValueFromAmount(pwallet->GetUnconfirmedBalance()));
4339 132 : obj.pushKV("immature_balance", ValueFromAmount(pwallet->GetImmatureBalance()));
4340 132 : obj.pushKV("immature_delegated_balance", ValueFromAmount(pwallet->GetImmatureDelegatedBalance()));
4341 132 : obj.pushKV("immature_cold_staking_balance", ValueFromAmount(pwallet->GetImmatureColdStakingBalance()));
4342 66 : obj.pushKV("txcount", (int)pwallet->mapWallet.size());
4343 :
4344 : // Autocombine settings
4345 66 : obj.pushKV("autocombine_enabled", pwallet->fCombineDust);
4346 132 : obj.pushKV("autocombine_threshold", ValueFromAmount(pwallet->nAutoCombineThreshold));
4347 66 : obj.pushKV("autocombine_frequency", pwallet->frequency);
4348 :
4349 : // Keypool information
4350 66 : obj.pushKV("keypoololdest", pwallet->GetOldestKeyPoolTime());
4351 66 : size_t kpExternalSize = pwallet->KeypoolCountExternalKeys();
4352 66 : obj.pushKV("keypoolsize", (int64_t)kpExternalSize);
4353 :
4354 66 : ScriptPubKeyMan* spk_man = pwallet->GetScriptPubKeyMan();
4355 66 : if (spk_man) {
4356 66 : const CKeyID& seed_id = spk_man->GetHDChain().GetID();
4357 132 : if (!seed_id.IsNull()) {
4358 195 : obj.pushKV("hdseedid", seed_id.GetHex());
4359 : }
4360 : }
4361 66 : if (pwallet->IsHDEnabled()) {
4362 65 : obj.pushKV("keypoolsize_hd_internal", (int64_t)(pwallet->GetKeyPoolSize() - kpExternalSize));
4363 130 : obj.pushKV("keypoolsize_hd_staking", (int64_t)(pwallet->GetStakingKeyPoolSize()));
4364 : }
4365 :
4366 66 : if (pwallet->IsCrypted())
4367 10 : obj.pushKV("unlocked_until", pwallet->nRelockTime);
4368 132 : obj.pushKV("paytxfee", ValueFromAmount(payTxFee.GetFeePerK()));
4369 66 : obj.pushKV("last_processed_block", pwallet->GetLastBlockHeight());
4370 66 : return obj;
4371 : }
4372 :
4373 3 : UniValue listwallets(const JSONRPCRequest& request)
4374 : {
4375 3 : if (request.fHelp || !request.params.empty())
4376 0 : throw std::runtime_error(
4377 : "listwallets\n"
4378 : "Returns a list of currently loaded wallets.\n"
4379 : "For full information on the wallet, use \"getwalletinfo\"\n"
4380 : "\nResult:\n"
4381 : "[ (json array of strings)\n"
4382 : " \"walletname\" (string) the wallet name\n"
4383 : " ...\n"
4384 : "]\n"
4385 : "\nExamples:\n"
4386 0 : + HelpExampleCli("listwallets", "")
4387 0 : + HelpExampleRpc("listwallets", "")
4388 0 : );
4389 :
4390 6 : UniValue obj(UniValue::VARR);
4391 :
4392 16 : for (CWalletRef pwallet : vpwallets) {
4393 13 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
4394 0 : return NullUniValue;
4395 : }
4396 :
4397 26 : LOCK(pwallet->cs_wallet);
4398 13 : obj.push_back(pwallet->GetName());
4399 : }
4400 :
4401 3 : return obj;
4402 : }
4403 :
4404 561 : UniValue getstakingstatus(const JSONRPCRequest& request)
4405 : {
4406 561 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
4407 :
4408 561 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
4409 0 : return NullUniValue;
4410 :
4411 561 : if (request.fHelp || request.params.size() != 0)
4412 0 : throw std::runtime_error(
4413 : "getstakingstatus\n"
4414 : "\nReturns an object containing various staking information.\n"
4415 :
4416 : "\nResult:\n"
4417 : "{\n"
4418 : " \"staking_status\": true|false, (boolean) whether the wallet is staking or not\n"
4419 : " \"staking_enabled\": true|false, (boolean) whether staking is enabled/disabled in pivx.conf\n"
4420 : " \"coldstaking_enabled\": true|false, (boolean) whether cold-staking is enabled/disabled in pivx.conf\n"
4421 : " \"haveconnections\": true|false, (boolean) whether network connections are present\n"
4422 : " \"mnsync\": true|false, (boolean) whether the required masternode/spork data is synced\n"
4423 : " \"walletunlocked\": true|false, (boolean) whether the wallet is unlocked\n"
4424 : " \"stakeablecoins\": n (numeric) number of stakeable UTXOs\n"
4425 : " \"stakingbalance\": d (numeric) PIV value of the stakeable coins (minus reserve balance, if any)\n"
4426 : " \"stakesplitthreshold\": d (numeric) value of the current threshold for stake split\n"
4427 : " \"lastattempt_age\": n (numeric) seconds since last stake attempt\n"
4428 : " \"lastattempt_depth\": n (numeric) depth of the block on top of which the last stake attempt was made\n"
4429 : " \"lastattempt_hash\": xxx (hex string) hash of the block on top of which the last stake attempt was made\n"
4430 : " \"lastattempt_coins\": n (numeric) number of stakeable coins available during last stake attempt\n"
4431 : " \"lastattempt_tries\": n (numeric) number of stakeable coins checked during last stake attempt\n"
4432 : "}\n"
4433 :
4434 0 : "\nExamples:\n" +
4435 0 : HelpExampleCli("getstakingstatus", "") + HelpExampleRpc("getstakingstatus", ""));
4436 :
4437 :
4438 561 : if (!pwallet)
4439 0 : throw JSONRPCError(RPC_IN_WARMUP, "Try again after active chain is loaded");
4440 561 : {
4441 1683 : LOCK2(cs_main, &pwallet->cs_wallet);
4442 1122 : UniValue obj(UniValue::VOBJ);
4443 561 : obj.pushKV("staking_status", pwallet->pStakerStatus->IsActive());
4444 1122 : obj.pushKV("staking_enabled", gArgs.GetBoolArg("-staking", DEFAULT_STAKING));
4445 561 : bool fColdStaking = gArgs.GetBoolArg("-coldstaking", true);
4446 561 : obj.pushKV("coldstaking_enabled", fColdStaking);
4447 561 : obj.pushKV("haveconnections", (g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL) > 0));
4448 561 : obj.pushKV("mnsync", !masternodeSync.NotCompleted());
4449 561 : obj.pushKV("walletunlocked", !pwallet->IsLocked());
4450 1122 : std::vector<CStakeableOutput> vCoins;
4451 561 : pwallet->StakeableCoins(&vCoins);
4452 561 : obj.pushKV("stakeablecoins", (int)vCoins.size());
4453 1122 : obj.pushKV("stakingbalance", ValueFromAmount(pwallet->GetStakingBalance(fColdStaking)));
4454 1122 : obj.pushKV("stakesplitthreshold", ValueFromAmount(pwallet->nStakeSplitThreshold));
4455 561 : CStakerStatus* ss = pwallet->pStakerStatus;
4456 561 : if (ss) {
4457 561 : obj.pushKV("lastattempt_age", (int)(GetTime() - ss->GetLastTime()));
4458 1107 : obj.pushKV("lastattempt_depth", (chainActive.Height() - ss->GetLastHeight()));
4459 1683 : obj.pushKV("lastattempt_hash", ss->GetLastHash().GetHex());
4460 561 : obj.pushKV("lastattempt_coins", ss->GetLastCoins());
4461 1122 : obj.pushKV("lastattempt_tries", ss->GetLastTries());
4462 : }
4463 561 : return obj;
4464 : }
4465 : }
4466 :
4467 0 : UniValue setstakesplitthreshold(const JSONRPCRequest& request)
4468 : {
4469 0 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
4470 :
4471 0 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
4472 0 : return NullUniValue;
4473 :
4474 0 : if (request.fHelp || request.params.size() != 1)
4475 0 : throw std::runtime_error(
4476 : "setstakesplitthreshold value\n\n"
4477 : "This will set the stake-split threshold value.\n"
4478 : "Whenever a successful stake is found, the stake amount is split across as many outputs (each with a value\n"
4479 : "higher than the threshold) as possible.\n"
4480 : "E.g. If the coinstake input + the block reward is 2000, and the split threshold is 499, the corresponding\n"
4481 : "coinstake transaction will have 4 outputs (of 500 PIV each)."
4482 0 : + HelpRequiringPassphrase(pwallet) + "\n"
4483 :
4484 : "\nArguments:\n"
4485 : "1. value (numeric, required) Threshold value (in PIV).\n"
4486 : " Set to 0 to disable stake-splitting\n"
4487 0 : " If > 0, it must be >= " + FormatMoney(CWallet::minStakeSplitThreshold) + "\n"
4488 :
4489 : "\nResult:\n"
4490 : "{\n"
4491 : " \"threshold\": n, (numeric) Threshold value set\n"
4492 : " \"saved\": true|false (boolean) 'true' if successfully saved to the wallet file\n"
4493 : "}\n"
4494 :
4495 0 : "\nExamples:\n" +
4496 0 : HelpExampleCli("setstakesplitthreshold", "500.12") + HelpExampleRpc("setstakesplitthreshold", "500.12"));
4497 :
4498 0 : CAmount nStakeSplitThreshold = AmountFromValue(request.params[0]);
4499 0 : if (nStakeSplitThreshold > 0 && nStakeSplitThreshold < CWallet::minStakeSplitThreshold)
4500 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf(_("The threshold value cannot be less than %s"),
4501 0 : FormatMoney(CWallet::minStakeSplitThreshold)));
4502 :
4503 0 : UniValue result(UniValue::VOBJ);
4504 0 : result.pushKV("threshold", request.params[0]);
4505 0 : result.pushKV("saved", pwallet->SetStakeSplitThreshold(nStakeSplitThreshold));
4506 0 : return result;
4507 : }
4508 :
4509 0 : UniValue getstakesplitthreshold(const JSONRPCRequest& request)
4510 : {
4511 0 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
4512 :
4513 0 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
4514 0 : return NullUniValue;
4515 :
4516 0 : if (request.fHelp || request.params.size() != 0)
4517 0 : throw std::runtime_error(
4518 : "getstakesplitthreshold\n"
4519 : "Returns the threshold for stake splitting\n"
4520 :
4521 : "\nResult:\n"
4522 : "n (numeric) Threshold value\n"
4523 :
4524 0 : "\nExamples:\n" +
4525 0 : HelpExampleCli("getstakesplitthreshold", "") + HelpExampleRpc("getstakesplitthreshold", ""));
4526 :
4527 0 : return ValueFromAmount(pwallet->GetStakeSplitThreshold());
4528 : }
4529 :
4530 6 : UniValue setautocombinethreshold(const JSONRPCRequest& request)
4531 : {
4532 6 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
4533 :
4534 6 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
4535 0 : return NullUniValue;
4536 :
4537 6 : if (request.fHelp || request.params.empty() || request.params.size() > 3)
4538 0 : throw std::runtime_error(
4539 : "setautocombinethreshold enable ( value )\n"
4540 : "\nThis will set the auto-combine threshold value.\n"
4541 : "\nWallet will automatically monitor for any coins with value below the threshold amount, and combine them if they reside with the same PIVX address\n"
4542 : "When auto-combine runs it will create a transaction, and therefore will be subject to transaction fees.\n"
4543 :
4544 : "\nArguments:\n"
4545 : "1. enable (boolean, required) Enable auto combine (true) or disable (false).\n"
4546 : "2. threshold (numeric, optional. required if enable is true) Threshold amount. Must be greater than 1.\n"
4547 : "3. frequency (numeric, optional. default value is 30 if not provided). Check for UTXOs to autocombine each N blocks where N is the frequency.\n"
4548 :
4549 : "\nResult:\n"
4550 : "{\n"
4551 : " \"enabled\": true|false, (boolean) true if auto-combine is enabled, otherwise false\n"
4552 : " \"threshold\": n.nnn, (numeric) auto-combine threshold in PIV\n"
4553 : " \"frequency\": n.nnn, (numeric) auto-combine frequency in blocks\n"
4554 : " \"saved\": true|false (boolean) true if setting was saved to the database, otherwise false\n"
4555 : "}\n"
4556 :
4557 0 : "\nExamples:\n" +
4558 0 : HelpExampleCli("setautocombinethreshold", "true 500.12 40") + HelpExampleRpc("setautocombinethreshold", "true, 500.12, 40"));
4559 :
4560 6 : RPCTypeCheck(request.params, {UniValue::VBOOL, UniValue::VNUM, UniValue::VNUM});
4561 :
4562 6 : bool fEnable = request.params[0].get_bool();
4563 6 : CAmount nThreshold = 0;
4564 6 : int frequency = 30;
4565 :
4566 6 : if (fEnable) {
4567 5 : if (request.params.size() < 2) {
4568 2 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Missing threshold value");
4569 : }
4570 4 : nThreshold = AmountFromValue(request.params[1]);
4571 4 : if (nThreshold < COIN)
4572 4 : throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("The threshold value cannot be less than %s", FormatMoney(COIN)));
4573 2 : if (request.params.size() == 3) {
4574 2 : frequency = request.params[2].get_int();
4575 2 : if (frequency <= 0) {
4576 2 : throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Frequency must be greater than 0"));
4577 : }
4578 : }
4579 : }
4580 :
4581 4 : WalletBatch batch(pwallet->GetDBHandle());
4582 :
4583 2 : {
4584 4 : LOCK(pwallet->cs_wallet);
4585 2 : pwallet->fCombineDust = fEnable;
4586 2 : pwallet->nAutoCombineThreshold = nThreshold;
4587 2 : pwallet->frequency = frequency;
4588 :
4589 4 : UniValue result(UniValue::VOBJ);
4590 2 : result.pushKV("enabled", fEnable);
4591 4 : result.pushKV("threshold", ValueFromAmount(pwallet->nAutoCombineThreshold));
4592 2 : result.pushKV("frequency", frequency);
4593 2 : if (batch.WriteAutoCombineSettings(fEnable, nThreshold, frequency)) {
4594 4 : result.pushKV("saved", "true");
4595 : } else {
4596 0 : result.pushKV("saved", "false");
4597 : }
4598 :
4599 2 : return result;
4600 : }
4601 : }
4602 :
4603 2 : UniValue getautocombinethreshold(const JSONRPCRequest& request)
4604 : {
4605 2 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
4606 :
4607 2 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
4608 0 : return NullUniValue;
4609 :
4610 2 : if (request.fHelp || !request.params.empty())
4611 0 : throw std::runtime_error(
4612 : "getautocombinethreshold\n"
4613 : "\nReturns the current threshold and frequency for auto combining UTXOs, if any\n"
4614 :
4615 : "\nResult:\n"
4616 : "{\n"
4617 : " \"enabled\": true|false, (boolean) true if auto-combine is enabled, otherwise false\n"
4618 : " \"threshold\": n.nnn (numeric) the auto-combine threshold amount in PIV\n"
4619 : " \"frequency\": n.nnn (numeric) the auto-combine frequency in blocks\n"
4620 : "}\n"
4621 :
4622 0 : "\nExamples:\n" +
4623 0 : HelpExampleCli("getautocombinethreshold", "") + HelpExampleRpc("getautocombinethreshold", ""));
4624 :
4625 4 : LOCK(pwallet->cs_wallet);
4626 :
4627 4 : UniValue result(UniValue::VOBJ);
4628 2 : result.pushKV("enabled", pwallet->fCombineDust);
4629 4 : result.pushKV("threshold", ValueFromAmount(pwallet->nAutoCombineThreshold));
4630 2 : result.pushKV("frequency", pwallet->frequency);
4631 :
4632 2 : return result;
4633 : }
4634 :
4635 3 : UniValue getsaplingnotescount(const JSONRPCRequest& request)
4636 : {
4637 3 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
4638 :
4639 3 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
4640 0 : return NullUniValue;
4641 :
4642 3 : if (request.fHelp || request.params.size() > 1)
4643 0 : throw std::runtime_error(
4644 : "getsaplingnotescount ( minconf )\n"
4645 : "Returns the number of sapling notes available in the wallet.\n"
4646 :
4647 : "\nArguments:\n"
4648 : "1. minconf (numeric, optional, default=1) Only include notes in transactions confirmed at least this many times.\n"
4649 :
4650 : "\nResult:\n"
4651 : "num (numeric) the number of sapling notes in the wallet\n"
4652 :
4653 : "\nExamples:\n"
4654 0 : + HelpExampleCli("getsaplingnotescount", "0")
4655 0 : + HelpExampleRpc("getsaplingnotescount", "0")
4656 0 : );
4657 :
4658 : // Make sure the results are valid at least up to the most recent block
4659 : // the user could have gotten from another RPC command prior to now
4660 3 : pwallet->BlockUntilSyncedToCurrentChain();
4661 :
4662 9 : LOCK2(cs_main, pwallet->cs_wallet);
4663 :
4664 3 : int nMinDepth = !request.params.empty() ? request.params[0].get_int() : 1;
4665 3 : int count = 0;
4666 160 : for (const auto& wtx : pwallet->mapWallet) {
4667 157 : if (wtx.second.GetDepthInMainChain() >= nMinDepth) {
4668 162 : for (const auto& nd : wtx.second.mapSaplingNoteData) {
4669 6 : if (nd.second.IsMyNote()) count++;
4670 : }
4671 : }
4672 : }
4673 3 : return count;
4674 : }
4675 :
4676 2 : UniValue rescanblockchain(const JSONRPCRequest& request)
4677 : {
4678 2 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
4679 :
4680 2 : if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
4681 0 : return NullUniValue;
4682 :
4683 2 : if (request.fHelp || request.params.size() > 2) {
4684 0 : throw std::runtime_error(
4685 : "rescanblockchain (start_height) (stop_height)\n"
4686 : "\nRescan the local blockchain for wallet related transactions.\n"
4687 : "\nArguments:\n"
4688 : "1. start_height (numeric, optional) block height where the rescan should start\n"
4689 : "2. stop_height (numeric, optional) the last block height that should be scanned\n"
4690 : "\nResult:\n"
4691 : "{\n"
4692 : " start_height (numeric) The block height where the rescan has started. If omitted, rescan started from the genesis block.\n"
4693 : " stop_height (numeric) The height of the last rescanned block. If omitted, rescan stopped at the chain tip.\n"
4694 : "}\n"
4695 : "\nExamples:\n"
4696 0 : + HelpExampleCli("rescanblockchain", "100000 120000")
4697 0 : + HelpExampleRpc("rescanblockchain", "100000 120000")
4698 0 : );
4699 : }
4700 :
4701 4 : WalletRescanReserver reserver(pwallet);
4702 2 : if (!reserver.reserve()) {
4703 0 : throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
4704 : }
4705 :
4706 2 : CBlockIndex *pindexStart = nullptr;
4707 2 : CBlockIndex *pindexStop = nullptr;
4708 2 : CBlockIndex *pChainTip = nullptr;
4709 2 : {
4710 2 : LOCK(cs_main);
4711 2 : pindexStart = chainActive.Genesis();
4712 2 : pChainTip = chainActive.Tip();
4713 :
4714 2 : if (!request.params[0].isNull()) {
4715 1 : pindexStart = chainActive[request.params[0].get_int()];
4716 1 : if (!pindexStart) {
4717 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid start_height");
4718 : }
4719 : }
4720 :
4721 2 : if (!request.params[1].isNull()) {
4722 1 : pindexStop = chainActive[request.params[1].get_int()];
4723 1 : if (!pindexStop) {
4724 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid stop_height");
4725 : }
4726 1 : else if (pindexStop->nHeight < pindexStart->nHeight) {
4727 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "stop_height must be greater then start_height");
4728 : }
4729 : }
4730 : }
4731 :
4732 2 : CBlockIndex *stopBlock = pwallet->ScanForWalletTransactions(pindexStart, pindexStop, reserver, true);
4733 2 : if (!stopBlock) {
4734 2 : if (pwallet->IsAbortingRescan()) {
4735 0 : throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted.");
4736 : }
4737 : // if we got a nullptr returned, ScanForWalletTransactions did rescan up to the requested stopindex
4738 2 : stopBlock = pindexStop ? pindexStop : pChainTip;
4739 : }
4740 : else {
4741 0 : throw JSONRPCError(RPC_MISC_ERROR, "Rescan failed. Potentially corrupted data files.");
4742 : }
4743 :
4744 4 : UniValue response(UniValue::VOBJ);
4745 2 : response.pushKV("start_height", pindexStart->nHeight);
4746 2 : response.pushKV("stop_height", stopBlock->nHeight);
4747 2 : return response;
4748 : }
4749 :
4750 : extern UniValue abortrescan(const JSONRPCRequest& request); // in rpcdump.cpp
4751 : extern UniValue dumpprivkey(const JSONRPCRequest& request); // in rpcdump.cpp
4752 : extern UniValue importprivkey(const JSONRPCRequest& request);
4753 : extern UniValue importaddress(const JSONRPCRequest& request);
4754 : extern UniValue importpubkey(const JSONRPCRequest& request);
4755 : extern UniValue dumpwallet(const JSONRPCRequest& request);
4756 : extern UniValue importwallet(const JSONRPCRequest& request);
4757 : extern UniValue importmulti(const JSONRPCRequest& request);
4758 : extern UniValue bip38encrypt(const JSONRPCRequest& request);
4759 : extern UniValue bip38decrypt(const JSONRPCRequest& request);
4760 :
4761 : extern UniValue exportsaplingkey(const JSONRPCRequest& request);
4762 : extern UniValue importsaplingkey(const JSONRPCRequest& request);
4763 : extern UniValue importsaplingviewingkey(const JSONRPCRequest& request);
4764 : extern UniValue exportsaplingviewingkey(const JSONRPCRequest& request);
4765 :
4766 : // clang-format off
4767 : static const CRPCCommand commands[] =
4768 : { // category name actor (function) okSafe argNames
4769 : // --------------------- ------------------------ ----------------------- ------ --------
4770 : { "wallet", "getaddressinfo", &getaddressinfo, true, {"address"} },
4771 : { "wallet", "setautocombinethreshold", &setautocombinethreshold, false, {"enable","threshold"} },
4772 : { "wallet", "getautocombinethreshold", &getautocombinethreshold, false, {} },
4773 : { "wallet", "abandontransaction", &abandontransaction, false, {"txid"} },
4774 : { "wallet", "abortrescan", &abortrescan, false, {} },
4775 : { "wallet", "addmultisigaddress", &addmultisigaddress, true, {"nrequired","keys","label"} },
4776 : { "wallet", "backupwallet", &backupwallet, true, {"destination"} },
4777 : { "wallet", "delegatestake", &delegatestake, false, {"staking_addr","amount","owner_addr","ext_owner","include_delegated","from_shield","force"} },
4778 : { "wallet", "dumpprivkey", &dumpprivkey, true, {"address"} },
4779 : { "wallet", "dumpwallet", &dumpwallet, true, {"filename"} },
4780 : { "wallet", "encryptwallet", &encryptwallet, true, {"passphrase"} },
4781 : { "wallet", "fundrawtransaction", &fundrawtransaction, false, {"hexstring","options"} },
4782 : { "wallet", "getbalance", &getbalance, false, {"minconf","include_watchonly","include_delegated","include_shield"} },
4783 : { "wallet", "getcoldstakingbalance", &getcoldstakingbalance, false, {} },
4784 : { "wallet", "getdelegatedbalance", &getdelegatedbalance, false, {} },
4785 : { "wallet", "upgradewallet", &upgradewallet, true, {} },
4786 : { "wallet", "sethdseed", &sethdseed, true, {"newkeypool","seed"} },
4787 : { "wallet", "getnewaddress", &getnewaddress, true, {"label"} },
4788 : { "wallet", "getnewexchangeaddress", &getnewexchangeaddress, true, {"label"} },
4789 : { "wallet", "getnewstakingaddress", &getnewstakingaddress, true, {"label"} },
4790 : { "wallet", "getrawchangeaddress", &getrawchangeaddress, true, {} },
4791 : { "wallet", "getreceivedbyaddress", &getreceivedbyaddress, false, {"address","minconf"} },
4792 : { "wallet", "gettransaction", &gettransaction, false, {"txid","include_watchonly"} },
4793 : { "wallet", "getstakesplitthreshold", &getstakesplitthreshold, false, {} },
4794 : { "wallet", "getunconfirmedbalance", &getunconfirmedbalance, false, {} },
4795 : { "wallet", "getwalletinfo", &getwalletinfo, false, {} },
4796 : { "wallet", "getstakingstatus", &getstakingstatus, false, {} },
4797 : { "wallet", "importprivkey", &importprivkey, true, {"privkey","label","rescan","is_staking_address"} },
4798 : { "wallet", "importwallet", &importwallet, true, {"filename"} },
4799 : { "wallet", "importaddress", &importaddress, true, {"address","label","rescan","p2sh"} },
4800 : { "wallet", "importpubkey", &importpubkey, true, {"pubkey","label","rescan"} },
4801 : { "wallet", "importmulti", &importmulti, true, {"requests","options"} },
4802 : { "wallet", "keypoolrefill", &keypoolrefill, true, {"newsize"} },
4803 : { "wallet", "listaddressgroupings", &listaddressgroupings, false, {} },
4804 : { "wallet", "listdelegators", &listdelegators, false, {"blacklist"} },
4805 : { "wallet", "liststakingaddresses", &liststakingaddresses, false, {} },
4806 : { "wallet", "listcoldutxos", &listcoldutxos, false, {"not_whitelisted"} },
4807 : { "wallet", "listlockunspent", &listlockunspent, false, {} },
4808 : { "wallet", "listreceivedbyaddress", &listreceivedbyaddress, false, {"minconf","include_empty","include_watchonly","filter"} },
4809 : { "wallet", "listsinceblock", &listsinceblock, false, {"blockhash","target_confirmations","include_watchonly"} },
4810 : { "wallet", "listtransactions", &listtransactions, false, {"dummy","count","from","include_watchonly","include_delegated","include_cold"} },
4811 : { "wallet", "listunspent", &listunspent, false, {"minconf","maxconf","addresses","watchonly_config","query_options","include_unsafe" } },
4812 : { "wallet", "listwallets", &listwallets, true, {} },
4813 : { "wallet", "lockunspent", &lockunspent, true, {"unlock", "transparent", "transactions"} },
4814 : { "wallet", "rawdelegatestake", &rawdelegatestake, false, {"staking_addr","amount","owner_addr","ext_owner","include_delegated","from_shield","force"} },
4815 : { "wallet", "sendmany", &sendmany, false, {"dummy","amounts","minconf","comment","include_delegated","subtract_fee_from"} },
4816 : { "wallet", "sendtoaddress", &sendtoaddress, false, {"address","amount","comment","comment-to","subtract_fee"} },
4817 : { "wallet", "settxfee", &settxfee, true, {"amount"} },
4818 : { "wallet", "setstakesplitthreshold", &setstakesplitthreshold, false, {"value"} },
4819 : { "wallet", "signmessage", &signmessage, true, {"address","message"} },
4820 : { "wallet", "walletlock", &walletlock, true, {} },
4821 : { "wallet", "walletpassphrasechange", &walletpassphrasechange, true, {"oldpassphrase","newpassphrase"} },
4822 : { "wallet", "walletpassphrase", &walletpassphrase, true, {"passphrase","timeout","staking_only"} },
4823 : { "wallet", "rescanblockchain", &rescanblockchain, true, {"start_height","stop_height"} },
4824 : { "wallet", "delegatoradd", &delegatoradd, true, {"address","label"} },
4825 : { "wallet", "delegatorremove", &delegatorremove, true, {"address"} },
4826 : { "wallet", "bip38encrypt", &bip38encrypt, true, {"address","passphrase"} },
4827 : { "wallet", "bip38decrypt", &bip38decrypt, true, {"encrypted_key","passphrase"} },
4828 :
4829 : /** Sapling functions */
4830 : { "wallet", "getnewshieldaddress", &getnewshieldaddress, true, {"label"} },
4831 : { "wallet", "listshieldaddresses", &listshieldaddresses, false, {"include_watchonly"} },
4832 : { "wallet", "exportsaplingkey", &exportsaplingkey, true, {"shield_addr"} },
4833 : { "wallet", "importsaplingkey", &importsaplingkey, true, {"key","rescan","height"} },
4834 : { "wallet", "importsaplingviewingkey", &importsaplingviewingkey, true, {"vkey","rescan","height"} },
4835 : { "wallet", "exportsaplingviewingkey", &exportsaplingviewingkey, true, {"shield_addr"} },
4836 : { "wallet", "getshieldbalance", &getshieldbalance, false, {"address","minconf","include_watchonly"} },
4837 : { "wallet", "listshieldunspent", &listshieldunspent, false, {"minconf","maxconf","include_watchonly","addresses"} },
4838 : { "wallet", "rawshieldsendmany", &rawshieldsendmany, false, {"fromaddress","amounts","minconf","fee"} },
4839 : { "wallet", "shieldsendmany", &shieldsendmany, false, {"fromaddress","amounts","minconf","fee","subtract_fee_from"} },
4840 : { "wallet", "listreceivedbyshieldaddress", &listreceivedbyshieldaddress, false, {"address","minconf"} },
4841 : { "wallet", "viewshieldtransaction", &viewshieldtransaction, false, {"txid"} },
4842 : { "wallet", "getsaplingnotescount", &getsaplingnotescount, false, {"minconf"} },
4843 :
4844 : /** Label functions (to replace non-balance account functions) */
4845 : { "wallet", "getaddressesbylabel", &getaddressesbylabel, true, {"label"} },
4846 : { "wallet", "getreceivedbylabel", &getreceivedbylabel, false, {"label","minconf"} },
4847 : { "wallet", "listlabels", &listlabels, false, {"purpose"} },
4848 : { "wallet", "listreceivedbylabel", &listreceivedbylabel, false, {"minconf","include_empty","include_watchonly"} },
4849 : { "wallet", "setlabel", &setlabel, true, {"address","label"} },
4850 : };
4851 : // clang-format on
4852 :
4853 411 : void RegisterWalletRPCCommands(CRPCTable &tableRPC)
4854 : {
4855 411 : if (gArgs.GetBoolArg("-disablewallet", false)) {
4856 : return;
4857 : }
4858 30800 : for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++) {
4859 30400 : tableRPC.appendCommand(commands[vcidx].name, &commands[vcidx]);
4860 : }
4861 : }
|