1 // Copyright (c) 2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2020 The Bitcoin Core developers
3 // Distributed under the MIT software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5
6 #include <amount.h>
7 #include <core_io.h>
8 #include <interfaces/chain.h>
9 #include <key_io.h>
10 #include <node/context.h>
11 #include <outputtype.h>
12 #include <policy/feerate.h>
13 #include <policy/fees.h>
14 #include <policy/rbf.h>
15 #include <rpc/rawtransaction_util.h>
16 #include <rpc/server.h>
17 #include <rpc/util.h>
18 #include <script/descriptor.h>
19 #include <script/sign.h>
20 #include <util/bip32.h>
21 #include <util/fees.h>
22 #include <util/message.h> // For MessageSign()
23 #include <util/moneystr.h>
24 #include <util/string.h>
25 #include <util/system.h>
26 #include <util/url.h>
27 #include <util/vector.h>
28 #include <wallet/coincontrol.h>
29 #include <wallet/feebumper.h>
30 #include <wallet/rpcwallet.h>
31 #include <wallet/wallet.h>
32 #include <wallet/walletdb.h>
33 #include <wallet/walletutil.h>
34 #include <qtum/qtumdelegation.h>
35 #include <util/signstr.h>
36 #include <interfaces/wallet.h>
37 #include <rpc/contract_util.h>
38 #include <util/tokenstr.h>
39
40 #include <stdint.h>
41
42 #include <univalue.h>
43
44
45 static const std::string WALLET_ENDPOINT_BASE = "/wallet/";
46 static const std::string HELP_REQUIRING_PASSPHRASE{"\nRequires wallet passphrase to be set with walletpassphrase call if wallet is encrypted.\n"};
47
GetAvoidReuseFlag(const CWallet * const pwallet,const UniValue & param)48 static inline bool GetAvoidReuseFlag(const CWallet* const pwallet, const UniValue& param) {
49 bool can_avoid_reuse = pwallet->IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE);
50 bool avoid_reuse = param.isNull() ? can_avoid_reuse : param.get_bool();
51
52 if (avoid_reuse && !can_avoid_reuse) {
53 throw JSONRPCError(RPC_WALLET_ERROR, "wallet does not have the \"avoid reuse\" feature enabled");
54 }
55
56 return avoid_reuse;
57 }
58
59
60 /** Used by RPC commands that have an include_watchonly parameter.
61 * We default to true for watchonly wallets if include_watchonly isn't
62 * explicitly set.
63 */
ParseIncludeWatchonly(const UniValue & include_watchonly,const CWallet & pwallet)64 static bool ParseIncludeWatchonly(const UniValue& include_watchonly, const CWallet& pwallet)
65 {
66 if (include_watchonly.isNull()) {
67 // if include_watchonly isn't explicitly set, then check if we have a watchonly wallet
68 return pwallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
69 }
70
71 // otherwise return whatever include_watchonly was set to
72 return include_watchonly.get_bool();
73 }
74
75
76 /** Checks if a CKey is in the given CWallet compressed or otherwise*/
HaveKey(const SigningProvider & wallet,const CKey & key)77 bool HaveKey(const SigningProvider& wallet, const CKey& key)
78 {
79 CKey key2;
80 key2.Set(key.begin(), key.end(), !key.IsCompressed());
81 return wallet.HaveKey(key.GetPubKey().GetID()) || wallet.HaveKey(key2.GetPubKey().GetID());
82 }
83
GetWalletNameFromJSONRPCRequest(const JSONRPCRequest & request,std::string & wallet_name)84 bool GetWalletNameFromJSONRPCRequest(const JSONRPCRequest& request, std::string& wallet_name)
85 {
86 if (request.URI.substr(0, WALLET_ENDPOINT_BASE.size()) == WALLET_ENDPOINT_BASE) {
87 // wallet endpoint was used
88 wallet_name = urlDecode(request.URI.substr(WALLET_ENDPOINT_BASE.size()));
89 return true;
90 }
91 return false;
92 }
93
GetWalletForJSONRPCRequest(const JSONRPCRequest & request)94 std::shared_ptr<CWallet> GetWalletForJSONRPCRequest(const JSONRPCRequest& request)
95 {
96 std::string wallet_name;
97 if (GetWalletNameFromJSONRPCRequest(request, wallet_name)) {
98 std::shared_ptr<CWallet> pwallet = GetWallet(wallet_name);
99 if (!pwallet) throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Requested wallet does not exist or is not loaded");
100 return pwallet;
101 }
102
103 std::vector<std::shared_ptr<CWallet>> wallets = GetWallets();
104 return wallets.size() == 1 || (request.fHelp && wallets.size() > 0) ? wallets[0] : nullptr;
105 }
106
GetSenderDest(CWallet * const pwallet,const CTransactionRef & tx,CTxDestination & txSenderDest)107 bool GetSenderDest(CWallet * const pwallet, const CTransactionRef& tx, CTxDestination& txSenderDest)
108 {
109 // Initialize variables
110 CScript senderPubKey;
111
112 // Get sender destination
113 if(tx->HasOpSender())
114 {
115 // Get destination from the outputs
116 for(CTxOut out : tx->vout)
117 {
118 if(out.scriptPubKey.HasOpSender())
119 {
120 ExtractSenderData(out.scriptPubKey, &senderPubKey, 0);
121 break;
122 }
123 }
124 }
125 else
126 {
127 // Get destination from the inputs
128 senderPubKey = pwallet->mapWallet.at(tx->vin[0].prevout.hash).tx->vout[tx->vin[0].prevout.n].scriptPubKey;
129 }
130
131 // Extract destination from script
132 return ExtractDestination(senderPubKey, txSenderDest);
133 }
134
EnsureWalletIsAvailable(const CWallet * pwallet,bool avoidException)135 bool EnsureWalletIsAvailable(const CWallet* pwallet, bool avoidException)
136 {
137 if (pwallet) return true;
138 if (avoidException) return false;
139 if (!HasWallets()) {
140 throw JSONRPCError(
141 RPC_METHOD_NOT_FOUND, "Method not found (wallet method is disabled because no wallet is loaded)");
142 }
143 throw JSONRPCError(RPC_WALLET_NOT_SPECIFIED,
144 "Wallet file not specified (must request wallet RPC through /wallet/<filename> uri-path).");
145 }
146
EnsureWalletIsUnlocked(const CWallet * pwallet)147 void EnsureWalletIsUnlocked(const CWallet* pwallet)
148 {
149 if (pwallet->IsLocked()) {
150 throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first.");
151 }
152 if (pwallet->m_wallet_unlock_staking_only) {
153 throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Wallet is unlocked for staking only.");
154 }
155 }
156
157 // also_create should only be set to true only when the RPC is expected to add things to a blank wallet and make it no longer blank
EnsureLegacyScriptPubKeyMan(CWallet & wallet,bool also_create)158 LegacyScriptPubKeyMan& EnsureLegacyScriptPubKeyMan(CWallet& wallet, bool also_create)
159 {
160 LegacyScriptPubKeyMan* spk_man = wallet.GetLegacyScriptPubKeyMan();
161 if (!spk_man && also_create) {
162 spk_man = wallet.GetOrCreateLegacyScriptPubKeyMan();
163 }
164 if (!spk_man) {
165 throw JSONRPCError(RPC_WALLET_ERROR, "This type of wallet does not support this command");
166 }
167 return *spk_man;
168 }
169
WalletTxToJSON(interfaces::Chain & chain,interfaces::Chain::Lock & locked_chain,const CWalletTx & wtx,UniValue & entry)170 static void WalletTxToJSON(interfaces::Chain& chain, interfaces::Chain::Lock& locked_chain, const CWalletTx& wtx, UniValue& entry)
171 {
172 int confirms = wtx.GetDepthInMainChain();
173 entry.pushKV("confirmations", confirms);
174 if (wtx.IsCoinBase() || wtx.IsCoinStake())
175 entry.pushKV("generated", true);
176 if (confirms > 0)
177 {
178 entry.pushKV("blockhash", wtx.m_confirm.hashBlock.GetHex());
179 entry.pushKV("blockheight", wtx.m_confirm.block_height);
180 entry.pushKV("blockindex", wtx.m_confirm.nIndex);
181 int64_t block_time;
182 bool found_block = chain.findBlock(wtx.m_confirm.hashBlock, nullptr /* block */, &block_time);
183 CHECK_NONFATAL(found_block);
184 entry.pushKV("blocktime", block_time);
185 } else {
186 entry.pushKV("trusted", wtx.IsTrusted(locked_chain));
187 }
188 uint256 hash = wtx.GetHash();
189 entry.pushKV("txid", hash.GetHex());
190 UniValue conflicts(UniValue::VARR);
191 for (const uint256& conflict : wtx.GetConflicts())
192 conflicts.push_back(conflict.GetHex());
193 entry.pushKV("walletconflicts", conflicts);
194 entry.pushKV("time", wtx.GetTxTime());
195 entry.pushKV("timereceived", (int64_t)wtx.nTimeReceived);
196
197 // Add opt-in RBF status
198 std::string rbfStatus = "no";
199 if (confirms <= 0) {
200 RBFTransactionState rbfState = chain.isRBFOptIn(*wtx.tx);
201 if (rbfState == RBFTransactionState::UNKNOWN)
202 rbfStatus = "unknown";
203 else if (rbfState == RBFTransactionState::REPLACEABLE_BIP125)
204 rbfStatus = "yes";
205 }
206 entry.pushKV("bip125-replaceable", rbfStatus);
207
208 for (const std::pair<const std::string, std::string>& item : wtx.mapValue)
209 entry.pushKV(item.first, item.second);
210 }
211
LabelFromValue(const UniValue & value)212 static std::string LabelFromValue(const UniValue& value)
213 {
214 std::string label = value.get_str();
215 if (label == "*")
216 throw JSONRPCError(RPC_WALLET_INVALID_LABEL_NAME, "Invalid label name");
217 return label;
218 }
219
SetDefaultPayForContractAddress(CWallet * const pwallet,interfaces::Chain::Lock & locked_chain,CCoinControl & coinControl)220 bool SetDefaultPayForContractAddress(CWallet* const pwallet, interfaces::Chain::Lock& locked_chain, CCoinControl & coinControl)
221 {
222 // Set default coin to pay for the contract
223 // Select any valid unspent output that can be used to pay for the contract
224 std::vector<COutput> vecOutputs;
225 coinControl.fAllowOtherInputs=true;
226
227 assert(pwallet != NULL);
228 pwallet->AvailableCoins(locked_chain, vecOutputs, false, NULL, true);
229
230 for (const COutput& out : vecOutputs) {
231 CTxDestination destAdress;
232 const CScript& scriptPubKey = out.tx->tx->vout[out.i].scriptPubKey;
233 bool fValidAddress = ExtractDestination(scriptPubKey, destAdress)
234 && IsValidContractSenderAddress(destAdress);
235
236 if (!fValidAddress)
237 continue;
238
239 coinControl.Select(COutPoint(out.tx->GetHash(),out.i));
240 break;
241 }
242
243 return coinControl.HasSelected();
244 }
245
SetDefaultSignSenderAddress(CWallet * const pwallet,interfaces::Chain::Lock & locked_chain,CTxDestination & destAdress)246 bool SetDefaultSignSenderAddress(CWallet* const pwallet, interfaces::Chain::Lock& locked_chain, CTxDestination& destAdress)
247 {
248 // Set default sender address if none provided
249 // Select any valid unspent output that can be used for contract sender address
250 std::vector<COutput> vecOutputs;
251
252 assert(pwallet != NULL);
253 pwallet->AvailableCoins(locked_chain, vecOutputs, false, NULL, true);
254
255 for (const COutput& out : vecOutputs) {
256 const CScript& scriptPubKey = out.tx->tx->vout[out.i].scriptPubKey;
257 bool fValidAddress = ExtractDestination(scriptPubKey, destAdress)
258 && IsValidContractSenderAddress(destAdress);
259
260 if (!fValidAddress)
261 continue;
262 break;
263 }
264
265 return !boost::get<CNoDestination>(&destAdress);
266 }
267
getnewaddress(const JSONRPCRequest & request)268 static UniValue getnewaddress(const JSONRPCRequest& request)
269 {
270 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
271 CWallet* const pwallet = wallet.get();
272
273 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
274 return NullUniValue;
275 }
276
277 RPCHelpMan{"getnewaddress",
278 "\nReturns a new Qtum address for receiving payments.\n"
279 "If 'label' is specified, it is added to the address book \n"
280 "so payments received with the address will be associated with 'label'.\n",
281 {
282 {"label", RPCArg::Type::STR, /* default */ "\"\"", "The label name for the address to be linked to. 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."},
283 {"address_type", RPCArg::Type::STR, /* default */ "set by -addresstype", "The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
284 },
285 RPCResult{
286 RPCResult::Type::STR, "address", "The new qtum address"
287 },
288 RPCExamples{
289 HelpExampleCli("getnewaddress", "")
290 + HelpExampleRpc("getnewaddress", "")
291 },
292 }.Check(request);
293
294 LOCK(pwallet->cs_wallet);
295
296 if (!pwallet->CanGetAddresses()) {
297 throw JSONRPCError(RPC_WALLET_ERROR, "Error: This wallet has no available keys");
298 }
299
300 // Parse the label first so we don't generate a key if there's an error
301 std::string label;
302 if (!request.params[0].isNull())
303 label = LabelFromValue(request.params[0]);
304
305 OutputType output_type = pwallet->m_default_address_type;
306 if (!request.params[1].isNull()) {
307 if (!ParseOutputType(request.params[1].get_str(), output_type)) {
308 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[1].get_str()));
309 }
310 }
311
312 CTxDestination dest;
313 std::string error;
314 if (!pwallet->GetNewDestination(output_type, label, dest, error)) {
315 throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, error);
316 }
317
318 return EncodeDestination(dest);
319 }
320
getrawchangeaddress(const JSONRPCRequest & request)321 static UniValue getrawchangeaddress(const JSONRPCRequest& request)
322 {
323 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
324 CWallet* const pwallet = wallet.get();
325
326 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
327 return NullUniValue;
328 }
329
330 RPCHelpMan{"getrawchangeaddress",
331 "\nReturns a new Qtum address, for receiving change.\n"
332 "This is for use with raw transactions, NOT normal use.\n",
333 {
334 {"address_type", RPCArg::Type::STR, /* default */ "set by -changetype", "The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
335 },
336 RPCResult{
337 RPCResult::Type::STR, "address", "The address"
338 },
339 RPCExamples{
340 HelpExampleCli("getrawchangeaddress", "")
341 + HelpExampleRpc("getrawchangeaddress", "")
342 },
343 }.Check(request);
344
345 LOCK(pwallet->cs_wallet);
346
347 if (!pwallet->CanGetAddresses(true)) {
348 throw JSONRPCError(RPC_WALLET_ERROR, "Error: This wallet has no available keys");
349 }
350
351 OutputType output_type = pwallet->m_default_change_type != OutputType::CHANGE_AUTO ? pwallet->m_default_change_type : pwallet->m_default_address_type;
352 if (!request.params[0].isNull()) {
353 if (!ParseOutputType(request.params[0].get_str(), output_type)) {
354 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[0].get_str()));
355 }
356 }
357
358 CTxDestination dest;
359 std::string error;
360 if (!pwallet->GetNewChangeDestination(output_type, dest, error)) {
361 throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, error);
362 }
363 return EncodeDestination(dest);
364 }
365
366
setlabel(const JSONRPCRequest & request)367 static UniValue setlabel(const JSONRPCRequest& request)
368 {
369 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
370 CWallet* const pwallet = wallet.get();
371
372 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
373 return NullUniValue;
374 }
375
376 RPCHelpMan{"setlabel",
377 "\nSets the label associated with the given address.\n",
378 {
379 {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The qtum address to be associated with a label."},
380 {"label", RPCArg::Type::STR, RPCArg::Optional::NO, "The label to assign to the address."},
381 },
382 RPCResult{RPCResult::Type::NONE, "", ""},
383 RPCExamples{
384 HelpExampleCli("setlabel", "\"" + EXAMPLE_ADDRESS[0] + "\" \"tabby\"")
385 + HelpExampleRpc("setlabel", "\"" + EXAMPLE_ADDRESS[0] + "\", \"tabby\"")
386 },
387 }.Check(request);
388
389 LOCK(pwallet->cs_wallet);
390
391 CTxDestination dest = DecodeDestination(request.params[0].get_str());
392 if (!IsValidDestination(dest)) {
393 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Qtum address");
394 }
395
396 std::string label = LabelFromValue(request.params[1]);
397
398 if (pwallet->IsMine(dest)) {
399 pwallet->SetAddressBook(dest, label, "receive");
400 } else {
401 pwallet->SetAddressBook(dest, label, "send");
402 }
403
404 return NullUniValue;
405 }
406
407
SendMoney(interfaces::Chain::Lock & locked_chain,CWallet * const pwallet,const CTxDestination & address,CAmount nValue,bool fSubtractFeeFromAmount,const CCoinControl & coin_control,mapValue_t mapValue,bool hasSender)408 static CTransactionRef SendMoney(interfaces::Chain::Lock& locked_chain, CWallet * const pwallet, const CTxDestination &address, CAmount nValue, bool fSubtractFeeFromAmount, const CCoinControl& coin_control, mapValue_t mapValue, bool hasSender)
409 {
410 CAmount curBalance = pwallet->GetBalance(0, coin_control.m_avoid_address_reuse).m_mine_trusted;
411
412 // Check amount
413 if (nValue <= 0)
414 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid amount");
415
416 if (nValue > curBalance)
417 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds");
418
419 if (pwallet->m_wallet_unlock_staking_only)
420 {
421 throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet unlocked for staking only, unable to create transaction.");
422 }
423
424 // Parse Bitcoin address
425 CScript scriptPubKey = GetScriptForDestination(address);
426
427 // Create and send the transaction
428 CAmount nFeeRequired = 0;
429 std::string strError;
430 std::vector<CRecipient> vecSend;
431 int nChangePosRet = -1;
432 CRecipient recipient = {scriptPubKey, nValue, fSubtractFeeFromAmount};
433 vecSend.push_back(recipient);
434 CTransactionRef tx;
435 if (!pwallet->CreateTransaction(locked_chain, vecSend, tx, nFeeRequired, nChangePosRet, strError, coin_control, true, 0, hasSender)) {
436 if (!fSubtractFeeFromAmount && nValue + nFeeRequired > curBalance)
437 strError = strprintf("Error: This transaction requires a transaction fee of at least %s", FormatMoney(nFeeRequired));
438 throw JSONRPCError(RPC_WALLET_ERROR, strError);
439 }
440 pwallet->CommitTransaction(tx, std::move(mapValue), {} /* orderForm */);
441 return tx;
442 }
443
SplitRemainder(std::vector<CRecipient> & vecSend,CAmount & remainder,CAmount maxValue)444 void SplitRemainder(std::vector<CRecipient>& vecSend, CAmount& remainder, CAmount maxValue)
445 {
446 if(remainder > 0)
447 {
448 for(int i = vecSend.size() - 1; i >= 0 ; i--)
449 {
450 CAmount diffAmount = maxValue - vecSend[i].nAmount;
451 if(diffAmount > 0)
452 {
453 if((remainder - diffAmount) > 0)
454 {
455 vecSend[i].nAmount = vecSend[i].nAmount + diffAmount;
456 remainder -= diffAmount;
457 }
458 else
459 {
460 vecSend[i].nAmount = vecSend[i].nAmount + remainder;
461 remainder = 0;
462 }
463 }
464
465 if(remainder <= 0)
466 break;
467 }
468 }
469 }
470
SplitUTXOs(interfaces::Chain::Lock & locked_chain,CWallet * const pwallet,const CTxDestination & address,CAmount nValue,CAmount maxValue,const CCoinControl & coin_control,CAmount nTotal,int maxOutputs,CAmount & nSplited)471 static CTransactionRef SplitUTXOs(interfaces::Chain::Lock& locked_chain, CWallet * const pwallet, const CTxDestination &address, CAmount nValue, CAmount maxValue, const CCoinControl& coin_control, CAmount nTotal, int maxOutputs, CAmount& nSplited)
472 {
473 // Check amount
474 if (nValue <= 0)
475 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid amount");
476
477 if (nValue > nTotal)
478 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds");
479
480 // Parse Qtum address
481 CScript scriptPubKey = GetScriptForDestination(address);
482
483 // Split into utxos with nValue
484 CAmount nFeeRequired = 0;
485 std::string strError;
486 std::vector<CRecipient> vecSend;
487 int nChangePosRet = -1;
488 int numOfRecipients = static_cast<int>(nTotal / nValue);
489
490 // Compute the number of recipients
491 CAmount remainder = nTotal % nValue;
492 if(remainder == 0 && numOfRecipients > 0)
493 {
494 numOfRecipients -= 1;
495 remainder = nValue;
496 }
497 if(numOfRecipients > maxOutputs)
498 {
499 numOfRecipients = maxOutputs;
500 remainder = 0;
501 }
502
503 // Split coins between recipients
504 CAmount nTxAmount = 0;
505 nSplited = 0;
506 CRecipient recipient = {scriptPubKey, nValue, false};
507 for(int i = 0; i < numOfRecipients; i++) {
508 vecSend.push_back(recipient);
509 }
510 SplitRemainder(vecSend, remainder, maxValue);
511
512 // Get the total amount of the outputs
513 for(CRecipient rec : vecSend)
514 {
515 nTxAmount += rec.nAmount;
516 }
517
518 // Create the transaction
519 CTransactionRef tx;
520 if((nTxAmount + pwallet->m_default_max_tx_fee) <= nTotal)
521 {
522 if (!pwallet->CreateTransaction(locked_chain, vecSend, tx, nFeeRequired, nChangePosRet, strError, coin_control, true, 0, true)) {
523 throw JSONRPCError(RPC_WALLET_ERROR, strError);
524 }
525 nSplited = nFeeRequired;
526 }
527 else if (vecSend.size() > 0)
528 {
529 // Pay the fee for the tx with the last recipient
530 CRecipient lastRecipient = vecSend[vecSend.size() - 1];
531 lastRecipient.fSubtractFeeFromAmount = true;
532 vecSend[vecSend.size() - 1] = lastRecipient;
533 if (!pwallet->CreateTransaction(locked_chain, vecSend, tx, nFeeRequired, nChangePosRet, strError, coin_control, true, 0, true)) {
534 throw JSONRPCError(RPC_WALLET_ERROR, strError);
535 }
536
537 // Combine the last 2 outputs when the last output have value less than nValue due to paying the fee
538 if(vecSend.size() >= 2)
539 {
540 if((lastRecipient.nAmount - nFeeRequired) < nValue)
541 {
542 bool payFeeRemainder = (nTotal - nTxAmount) > nFeeRequired * 1.1;
543 if(payFeeRemainder)
544 {
545 // Pay the fee with the remainder
546 lastRecipient.fSubtractFeeFromAmount = false;
547 vecSend.pop_back();
548 vecSend.push_back(lastRecipient);
549 }
550 else
551 {
552 // Combine the last 2 outputs
553 CAmount nValueLast2 = lastRecipient.nAmount + vecSend[vecSend.size() - 2].nAmount;
554 lastRecipient.nAmount = lastRecipient.nAmount + nFeeRequired;
555 lastRecipient.fSubtractFeeFromAmount = true;
556 nValueLast2 -= lastRecipient.nAmount;
557 vecSend.pop_back();
558 vecSend.pop_back();
559 vecSend.push_back(lastRecipient);
560
561 // Split the rest with the others
562 SplitRemainder(vecSend, nValueLast2, maxValue);
563 }
564
565 if((!pwallet->CreateTransaction(locked_chain, vecSend, tx, nFeeRequired, nChangePosRet, strError, coin_control, true, 0, true))) {
566 throw JSONRPCError(RPC_WALLET_ERROR, strError);
567 }
568 if(payFeeRemainder)
569 {
570 nSplited = nFeeRequired;
571 }
572 }
573 }
574 }
575
576 // Compute the splited amount
577 for(CRecipient rec : vecSend)
578 {
579 nSplited += rec.nAmount;
580 }
581
582 // Send the transaction
583 pwallet->CommitTransaction(tx, {} /* mapValue */, {} /* orderForm */);
584
585 return tx;
586 }
587
sendtoaddress(const JSONRPCRequest & request)588 static UniValue sendtoaddress(const JSONRPCRequest& request)
589 {
590 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
591 CWallet* const pwallet = wallet.get();
592
593 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
594 return NullUniValue;
595 }
596
597 RPCHelpMan{"sendtoaddress",
598 "\nSend an amount to a given address." +
599 HELP_REQUIRING_PASSPHRASE,
600 {
601 {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The qtum address to send to."},
602 {"amount", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "The amount in " + CURRENCY_UNIT + " to send. eg 0.1"},
603 {"comment", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "A comment used to store what the transaction is for.\n"
604 " This is not part of the transaction, just kept in your wallet."},
605 {"comment_to", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "A comment to store the name of the person or organization\n"
606 " to which you're sending the transaction. This is not part of the \n"
607 " transaction, just kept in your wallet."},
608 {"subtractfeefromamount", RPCArg::Type::BOOL, /* default */ "false", "The fee will be deducted from the amount being sent.\n"
609 " The recipient will receive less qtums than you enter in the amount field."},
610 {"replaceable", RPCArg::Type::BOOL, /* default */ "wallet default", "Allow this transaction to be replaced by a transaction with higher fees via BIP 125"},
611 {"conf_target", RPCArg::Type::NUM, /* default */ "wallet default", "Confirmation target (in blocks)"},
612 {"estimate_mode", RPCArg::Type::STR, /* default */ "UNSET", "The fee estimate mode, must be one of:\n"
613 " \"UNSET\"\n"
614 " \"ECONOMICAL\"\n"
615 " \"CONSERVATIVE\""},
616 {"avoid_reuse", RPCArg::Type::BOOL, /* default */ "true", "(only available if avoid_reuse wallet flag is set) Avoid spending from dirty addresses; addresses are considered\n"
617 " dirty if they have previously been used in a transaction."},
618 {"senderaddress", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "The qtum address that will be used to send money from."},
619 {"changeToSender", RPCArg::Type::BOOL, /* default */ "false", "Return the change to the sender."},
620 },
621 RPCResult{
622 RPCResult::Type::STR_HEX, "txid", "The transaction id."
623 },
624 RPCExamples{
625 HelpExampleCli("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 0.1")
626 + HelpExampleCli("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 0.1 \"donation\" \"seans outpost\"")
627 + HelpExampleCli("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 0.1 \"\" \"\" true")
628 + HelpExampleCli("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\", 0.1, \"donation\", \"seans outpost\", false, null, null, \"\", false, \"" + EXAMPLE_ADDRESS[1] + "\", true")
629 + HelpExampleRpc("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\", 0.1, \"donation\", \"seans outpost\"")
630 + HelpExampleRpc("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\", 0.1, \"donation\", \"seans outpost\", false, null, null, \"\", false, \"" + EXAMPLE_ADDRESS[1] + "\", true")
631 },
632 }.Check(request);
633
634 // Make sure the results are valid at least up to the most recent block
635 // the user could have gotten from another RPC command prior to now
636 pwallet->BlockUntilSyncedToCurrentChain();
637
638 auto locked_chain = pwallet->chain().lock();
639 LOCK(pwallet->cs_wallet);
640
641 CTxDestination dest = DecodeDestination(request.params[0].get_str());
642 if (!IsValidDestination(dest)) {
643 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Qtum address");
644 }
645
646 // Amount
647 CAmount nAmount = AmountFromValue(request.params[1]);
648 if (nAmount <= 0)
649 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send");
650
651 // Wallet comments
652 mapValue_t mapValue;
653 if (!request.params[2].isNull() && !request.params[2].get_str().empty())
654 mapValue["comment"] = request.params[2].get_str();
655 if (!request.params[3].isNull() && !request.params[3].get_str().empty())
656 mapValue["to"] = request.params[3].get_str();
657
658 bool fSubtractFeeFromAmount = false;
659 if (!request.params[4].isNull()) {
660 fSubtractFeeFromAmount = request.params[4].get_bool();
661 }
662
663 CCoinControl coin_control;
664 if (!request.params[5].isNull()) {
665 coin_control.m_signal_bip125_rbf = request.params[5].get_bool();
666 }
667
668 if (!request.params[6].isNull()) {
669 coin_control.m_confirm_target = ParseConfirmTarget(request.params[6], pwallet->chain().estimateMaxBlocks());
670 }
671
672 if (request.params.size() > 7 && !request.params[7].isNull()) {
673 std::string estimate_mode = request.params[7].get_str();
674 if (!estimate_mode.empty() && !FeeModeFromString(estimate_mode, coin_control.m_fee_mode)) {
675 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid estimate_mode parameter");
676 }
677 }
678
679 coin_control.m_avoid_address_reuse = GetAvoidReuseFlag(pwallet, request.params[8]);
680 // We also enable partial spend avoidance if reuse avoidance is set.
681 coin_control.m_avoid_partial_spends |= coin_control.m_avoid_address_reuse;
682
683 bool fHasSender=false;
684 CTxDestination senderAddress;
685 if (request.params.size() > 9 && !request.params[9].isNull()){
686 senderAddress = DecodeDestination(request.params[9].get_str());
687 if (!IsValidDestination(senderAddress))
688 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Qtum address to send from");
689 else
690 fHasSender=true;
691 }
692
693 bool fChangeToSender=false;
694 if (request.params.size() > 10 && !request.params[10].isNull()){
695 fChangeToSender=request.params[10].get_bool();
696 }
697
698 if(fHasSender){
699 //find a UTXO with sender address
700
701 UniValue results(UniValue::VARR);
702 std::vector<COutput> vecOutputs;
703
704 coin_control.fAllowOtherInputs=true;
705
706 assert(pwallet != NULL);
707 pwallet->AvailableCoins(*locked_chain, vecOutputs, false, NULL, true);
708
709 for(const COutput& out : vecOutputs) {
710 CTxDestination destAdress;
711 const CScript& scriptPubKey = out.tx->tx->vout[out.i].scriptPubKey;
712 bool fValidAddress = ExtractDestination(scriptPubKey, destAdress);
713
714 if (!fValidAddress || senderAddress != destAdress)
715 continue;
716
717 coin_control.Select(COutPoint(out.tx->GetHash(),out.i));
718
719 break;
720
721 }
722
723 if(!coin_control.HasSelected()){
724 throw JSONRPCError(RPC_TYPE_ERROR, "Sender address does not have any unspent outputs");
725 }
726 if(fChangeToSender){
727 coin_control.destChange=senderAddress;
728 }
729 }
730
731 EnsureWalletIsUnlocked(pwallet);
732
733 CTransactionRef tx = SendMoney(*locked_chain, pwallet, dest, nAmount, fSubtractFeeFromAmount, coin_control, std::move(mapValue), fHasSender);
734 return tx->GetHash().GetHex();
735 }
736
splitutxosforaddress(const JSONRPCRequest & request)737 static UniValue splitutxosforaddress(const JSONRPCRequest& request)
738 {
739 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
740 CWallet* const pwallet = wallet.get();
741
742 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
743 return NullUniValue;
744 }
745
746 RPCHelpMan{"splitutxosforaddress",
747 "\nSplit an address coins into utxo between min and max value." +
748 HELP_REQUIRING_PASSPHRASE,
749 {
750 {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The qtum address to split utxos."},
751 {"minValue", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "Select utxo which value is smaller than value (minimum 0.1 COIN)"},
752 {"maxValue", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "Select utxo which value is greater than value (minimum 0.1 COIN)"},
753 {"maxOutputs", RPCArg::Type::NUM, /* default */ "100", "Maximum outputs to create"},
754 },
755 RPCResult{
756 RPCResult::Type::OBJ, "", "",
757 {
758 {RPCResult::Type::STR_HEX, "txid", "The hex-encoded transaction id"},
759 {RPCResult::Type::STR_AMOUNT, "selected", "Selected amount of coins"},
760 {RPCResult::Type::STR_AMOUNT, "splited", "Splited amount of coins"},
761 }
762 },
763 RPCExamples{
764 HelpExampleCli("splitutxosforaddress", "\"QM72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 100 200")
765 + HelpExampleCli("splitutxosforaddress", "\"QM72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 100 200 100")
766 + HelpExampleRpc("splitutxosforaddress", "\"QM72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 100 200")
767 + HelpExampleRpc("splitutxosforaddress", "\"QM72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 100 200 100")
768 },
769 }.Check(request);
770
771 // Make sure the results are valid at least up to the most recent block
772 // the user could have gotten from another RPC command prior to now
773 pwallet->BlockUntilSyncedToCurrentChain();
774
775 auto locked_chain = pwallet->chain().lock();
776 LOCK(pwallet->cs_wallet);
777
778 // Address
779 CTxDestination address = DecodeDestination(request.params[0].get_str());
780
781 if (!IsValidDestination(address)) {
782 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Qtum address");
783 }
784 CScript scriptPubKey = GetScriptForDestination(address);
785 if (!pwallet->IsMine(scriptPubKey)) {
786 throw JSONRPCError(RPC_WALLET_ERROR, "Address not found in wallet");
787 }
788
789 // minimum value
790 CAmount minValue = AmountFromValue(request.params[1]);
791
792 // maximum value
793 CAmount maxValue = AmountFromValue(request.params[2]);
794
795 if (minValue < COIN/10 || maxValue <= 0 || minValue > maxValue) {
796 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid values for minimum and maximum");
797 }
798
799 // Maximum outputs
800 int maxOutputs = request.params.size() > 3 ? request.params[3].get_int() : 100;
801 if (maxOutputs < 1) {
802 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid value for maximum outputs");
803 }
804
805 // Amount
806 CAmount nSplitAmount = minValue;
807 CAmount nRequiredAmount = nSplitAmount * maxOutputs;
808
809 CCoinControl coin_control;
810 coin_control.destChange = address;
811
812 // Find UTXOs for a address with value smaller than minValue and greater then maxValue
813 std::vector<COutput> vecOutputs;
814 coin_control.fAllowOtherInputs=true;
815
816 assert(pwallet != NULL);
817 pwallet->AvailableCoins(*locked_chain, vecOutputs, false, NULL, true);
818
819 CAmount total = 0;
820 CAmount nSelectedAmount = 0;
821 for(const COutput& out : vecOutputs) {
822 CTxDestination destAdress;
823 const CScript& scriptPubKey = out.tx->tx->vout[out.i].scriptPubKey;
824 bool fValidAddress = ExtractDestination(scriptPubKey, destAdress);
825
826 CAmount val = out.tx->tx.get()->vout[out.i].nValue;
827 if (!fValidAddress || address != destAdress || (val >= minValue && val <= maxValue ) )
828 continue;
829
830 if(nSelectedAmount <= nRequiredAmount)
831 {
832 coin_control.Select(COutPoint(out.tx->GetHash(),out.i));
833 nSelectedAmount += val;
834 }
835 total += val;
836 }
837
838 CAmount splited = 0;
839 UniValue obj(UniValue::VOBJ);
840 if(coin_control.HasSelected() && nSplitAmount < nSelectedAmount){
841 EnsureWalletIsUnlocked(pwallet);
842 CTransactionRef tx = SplitUTXOs(*locked_chain, pwallet, address, nSplitAmount, maxValue, coin_control, nSelectedAmount, maxOutputs, splited);
843 obj.pushKV("txid", tx->GetHash().GetHex());
844 }
845
846 obj.pushKV("selected", FormatMoney(total));
847 obj.pushKV("splited", FormatMoney(splited));
848 return obj;
849 }
850
createcontract(const JSONRPCRequest & request)851 static UniValue createcontract(const JSONRPCRequest& request){
852
853 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
854 CWallet* const pwallet = wallet.get();
855
856 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
857 return NullUniValue;
858 }
859
860 LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*pwallet);
861 auto locked_chain = pwallet->chain().lock();
862 LOCK2(pwallet->cs_wallet, spk_man.cs_KeyStore);
863 QtumDGP qtumDGP(globalState.get(), fGettingValuesDGP);
864 uint64_t blockGasLimit = qtumDGP.getBlockGasLimit(::ChainActive().Height());
865 uint64_t minGasPrice = CAmount(qtumDGP.getMinGasPrice(::ChainActive().Height()));
866 CAmount nGasPrice = (minGasPrice>DEFAULT_GAS_PRICE)?minGasPrice:DEFAULT_GAS_PRICE;
867
868 RPCHelpMan{"createcontract",
869 "\nCreate a contract with bytcode." +
870 HELP_REQUIRING_PASSPHRASE,
871 {
872 {"bytecode", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "contract bytcode."},
873 {"gasLimit", RPCArg::Type::AMOUNT, RPCArg::Optional::OMITTED, "gasLimit, default: "+i64tostr(DEFAULT_GAS_LIMIT_OP_CREATE)+", max: "+i64tostr(blockGasLimit)},
874 {"gasPrice", RPCArg::Type::AMOUNT, RPCArg::Optional::OMITTED, "gasPrice QTUM price per gas unit, default: "+FormatMoney(nGasPrice)+", min:"+FormatMoney(minGasPrice)},
875 {"senderaddress", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "The qtum address that will be used to create the contract."},
876 {"broadcast", RPCArg::Type::BOOL, RPCArg::Optional::OMITTED, "Whether to broadcast the transaction or not."},
877 {"changeToSender", RPCArg::Type::BOOL, RPCArg::Optional::OMITTED, "Return the change to the sender."},
878 },
879 RPCResult{
880 RPCResult::Type::ARR, "", "",
881 {
882 {RPCResult::Type::OBJ, "", "",
883 {
884 {RPCResult::Type::STR_HEX, "txid", "The transaction id"},
885 {RPCResult::Type::STR, "sender", CURRENCY_UNIT + " address of the sender"},
886 {RPCResult::Type::STR_HEX, "hash160", "Ripemd-160 hash of the sender"},
887 {RPCResult::Type::STR, "address", "Expected contract address"},
888 }},
889 }
890 },
891 RPCExamples{
892 HelpExampleCli("createcontract", "\"60606040525b33600060006101000a81548173ffffffffffffffffffffffffffffffffffffffff02191690836c010000000000000000000000009081020402179055506103786001600050819055505b600c80605b6000396000f360606040526008565b600256\"")
893 + HelpExampleCli("createcontract", "\"60606040525b33600060006101000a81548173ffffffffffffffffffffffffffffffffffffffff02191690836c010000000000000000000000009081020402179055506103786001600050819055505b600c80605b6000396000f360606040526008565b600256\" 6000000 "+FormatMoney(minGasPrice)+" \"QM72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" true")
894 },
895 }.Check(request);
896
897
898 std::string bytecode=request.params[0].get_str();
899
900 if(bytecode.size() % 2 != 0 || !CheckHex(bytecode))
901 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid data (data not hex)");
902
903 uint64_t nGasLimit=DEFAULT_GAS_LIMIT_OP_CREATE;
904 if (request.params.size() > 1){
905 nGasLimit = request.params[1].get_int64();
906 if (nGasLimit > blockGasLimit)
907 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid value for gasLimit (Maximum is: "+i64tostr(blockGasLimit)+")");
908 if (nGasLimit < MINIMUM_GAS_LIMIT)
909 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid value for gasLimit (Minimum is: "+i64tostr(MINIMUM_GAS_LIMIT)+")");
910 if (nGasLimit <= 0)
911 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid value for gasLimit");
912 }
913
914 if (request.params.size() > 2){
915 nGasPrice = AmountFromValue(request.params[2]);
916 if (nGasPrice <= 0)
917 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid value for gasPrice");
918 CAmount maxRpcGasPrice = gArgs.GetArg("-rpcmaxgasprice", MAX_RPC_GAS_PRICE);
919 if (nGasPrice > (int64_t)maxRpcGasPrice)
920 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid value for gasPrice, Maximum allowed in RPC calls is: "+FormatMoney(maxRpcGasPrice)+" (use -rpcmaxgasprice to change it)");
921 if (nGasPrice < (int64_t)minGasPrice)
922 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid value for gasPrice (Minimum is: "+FormatMoney(minGasPrice)+")");
923 }
924
925 bool fHasSender=false;
926 CTxDestination senderAddress;
927 if (request.params.size() > 3){
928 senderAddress = DecodeDestination(request.params[3].get_str());
929 if (!IsValidDestination(senderAddress))
930 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Qtum address to send from");
931 if (!IsValidContractSenderAddress(senderAddress))
932 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid contract sender address. Only P2PK and P2PKH allowed");
933 else
934 fHasSender=true;
935 }
936
937 bool fBroadcast=true;
938 if (request.params.size() > 4){
939 fBroadcast=request.params[4].get_bool();
940 }
941
942 bool fChangeToSender=true;
943 if (request.params.size() > 5){
944 fChangeToSender=request.params[5].get_bool();
945 }
946
947 CCoinControl coinControl;
948
949 CTxDestination signSenderAddress = CNoDestination();
950 if(fHasSender){
951 // Find a UTXO with sender address
952 std::vector<COutput> vecOutputs;
953
954 coinControl.fAllowOtherInputs=true;
955
956 assert(pwallet != NULL);
957 pwallet->AvailableCoins(*locked_chain, vecOutputs, false, NULL, true);
958
959 for (const COutput& out : vecOutputs) {
960 CTxDestination destAdress;
961 const CScript& scriptPubKey = out.tx->tx->vout[out.i].scriptPubKey;
962 bool fValidAddress = ExtractDestination(scriptPubKey, destAdress);
963
964 if (!fValidAddress || senderAddress != destAdress)
965 continue;
966
967 coinControl.Select(COutPoint(out.tx->GetHash(),out.i));
968
969 break;
970
971 }
972
973 if(coinControl.HasSelected())
974 {
975 // Change to the sender
976 if(fChangeToSender){
977 coinControl.destChange=senderAddress;
978 }
979 }
980 else
981 {
982 // Create op sender transaction when op sender is activated
983 if(!(::ChainActive().Height() >= Params().GetConsensus().QIP5Height))
984 throw JSONRPCError(RPC_TYPE_ERROR, "Sender address does not have any unspent outputs");
985 }
986
987 if(::ChainActive().Height() >= Params().GetConsensus().QIP5Height)
988 {
989 // Set the sender address
990 signSenderAddress = senderAddress;
991 }
992 }
993 else
994 {
995 if(::ChainActive().Height() >= Params().GetConsensus().QIP5Height)
996 {
997 // If no sender address provided set to the default sender address
998 SetDefaultSignSenderAddress(pwallet, *locked_chain, signSenderAddress);
999 }
1000 }
1001 EnsureWalletIsUnlocked(pwallet);
1002
1003 CAmount nGasFee=nGasPrice*nGasLimit;
1004
1005 CAmount curBalance = pwallet->GetBalance().m_mine_trusted;
1006
1007 // Check amount
1008 if (nGasFee <= 0)
1009 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid amount");
1010
1011 if (nGasFee > curBalance)
1012 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds");
1013
1014 // Select default coin that will pay for the contract if none selected
1015 if(!coinControl.HasSelected() && !SetDefaultPayForContractAddress(pwallet, *locked_chain, coinControl))
1016 throw JSONRPCError(RPC_TYPE_ERROR, "Does not have any P2PK or P2PKH unspent outputs to pay for the contract.");
1017
1018 // Build OP_EXEC script
1019 CScript scriptPubKey = CScript() << CScriptNum(VersionVM::GetEVMDefault().toRaw()) << CScriptNum(nGasLimit) << CScriptNum(nGasPrice) << ParseHex(bytecode) <<OP_CREATE;
1020 if(::ChainActive().Height() >= Params().GetConsensus().QIP5Height)
1021 {
1022 if(IsValidDestination(signSenderAddress))
1023 {
1024 CKeyID key_id = GetKeyForDestination(spk_man, signSenderAddress);
1025 CKey key;
1026 if (!spk_man.GetKey(key_id, key)) {
1027 throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available");
1028 }
1029 std::vector<unsigned char> scriptSig;
1030 scriptPubKey = (CScript() << CScriptNum(addresstype::PUBKEYHASH) << ToByteVector(key_id) << ToByteVector(scriptSig) << OP_SENDER) + scriptPubKey;
1031 }
1032 else
1033 {
1034 // OP_SENDER will always be used when QIP5Height is active
1035 throw JSONRPCError(RPC_TYPE_ERROR, "Sender address fail to set for OP_SENDER.");
1036 }
1037 }
1038
1039 // Create and send the transaction
1040 CAmount nFeeRequired;
1041 std::string strError;
1042 std::vector<CRecipient> vecSend;
1043 int nChangePosRet = -1;
1044 CRecipient recipient = {scriptPubKey, 0, false};
1045 vecSend.push_back(recipient);
1046
1047 CTransactionRef tx;
1048 if (!pwallet->CreateTransaction(*locked_chain, vecSend, tx, nFeeRequired, nChangePosRet, strError, coinControl, true, nGasFee, true, signSenderAddress)) {
1049 if (nFeeRequired > pwallet->GetBalance().m_mine_trusted)
1050 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));
1051 throw JSONRPCError(RPC_WALLET_ERROR, strError);
1052 }
1053
1054 CTxDestination txSenderDest;
1055 GetSenderDest(pwallet, tx, txSenderDest);
1056
1057 if (fHasSender && !(senderAddress == txSenderDest)){
1058 throw JSONRPCError(RPC_TYPE_ERROR, "Sender could not be set, transaction was not committed!");
1059 }
1060
1061 UniValue result(UniValue::VOBJ);
1062 if(fBroadcast){
1063 pwallet->CommitTransaction(tx, {}, {});
1064
1065 std::string txId=tx->GetHash().GetHex();
1066 result.pushKV("txid", txId);
1067
1068 CTxDestination txSenderAdress(txSenderDest);
1069 CKeyID keyid = GetKeyForDestination(spk_man, txSenderAdress);
1070
1071 result.pushKV("sender", EncodeDestination(txSenderAdress));
1072 result.pushKV("hash160", HexStr(valtype(keyid.begin(),keyid.end())));
1073
1074 std::vector<unsigned char> SHA256TxVout(32);
1075 std::vector<unsigned char> contractAddress(20);
1076 std::vector<unsigned char> txIdAndVout(tx->GetHash().begin(), tx->GetHash().end());
1077 uint32_t voutNumber=0;
1078 for (const CTxOut& txout : tx->vout) {
1079 if(txout.scriptPubKey.HasOpCreate()){
1080 std::vector<unsigned char> voutNumberChrs;
1081 if (voutNumberChrs.size() < sizeof(voutNumber))voutNumberChrs.resize(sizeof(voutNumber));
1082 std::memcpy(voutNumberChrs.data(), &voutNumber, sizeof(voutNumber));
1083 txIdAndVout.insert(txIdAndVout.end(),voutNumberChrs.begin(),voutNumberChrs.end());
1084 break;
1085 }
1086 voutNumber++;
1087 }
1088 CSHA256().Write(txIdAndVout.data(), txIdAndVout.size()).Finalize(SHA256TxVout.data());
1089 CRIPEMD160().Write(SHA256TxVout.data(), SHA256TxVout.size()).Finalize(contractAddress.data());
1090 result.pushKV("address", HexStr(contractAddress));
1091 }else{
1092 std::string strHex = EncodeHexTx(*tx, RPCSerializationFlags());
1093 result.pushKV("raw transaction", strHex);
1094 }
1095 return result;
1096 }
1097
SendToContract(interfaces::Chain::Lock & locked_chain,CWallet * const pwallet,LegacyScriptPubKeyMan & spk_man,const UniValue & params)1098 UniValue SendToContract(interfaces::Chain::Lock& locked_chain, CWallet* const pwallet, LegacyScriptPubKeyMan& spk_man, const UniValue& params)
1099 {
1100 QtumDGP qtumDGP(globalState.get(), fGettingValuesDGP);
1101 uint64_t blockGasLimit = qtumDGP.getBlockGasLimit(::ChainActive().Height());
1102 uint64_t minGasPrice = CAmount(qtumDGP.getMinGasPrice(::ChainActive().Height()));
1103 CAmount nGasPrice = (minGasPrice>DEFAULT_GAS_PRICE)?minGasPrice:DEFAULT_GAS_PRICE;
1104
1105 std::string contractaddress = params[0].get_str();
1106 if(contractaddress.size() != 40 || !CheckHex(contractaddress))
1107 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Incorrect contract address");
1108
1109 dev::Address addrAccount(contractaddress);
1110 if(!globalState->addressInUse(addrAccount))
1111 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "contract address does not exist");
1112
1113 std::string datahex = params[1].get_str();
1114 if(datahex.size() % 2 != 0 || !CheckHex(datahex))
1115 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid data (data not hex)");
1116
1117 CAmount nAmount = 0;
1118 if (params.size() > 2){
1119 nAmount = AmountFromValue(params[2]);
1120 if (nAmount < 0)
1121 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send");
1122 }
1123
1124 uint64_t nGasLimit=DEFAULT_GAS_LIMIT_OP_SEND;
1125 if (params.size() > 3){
1126 nGasLimit = params[3].get_int64();
1127 if (nGasLimit > blockGasLimit)
1128 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid value for gasLimit (Maximum is: "+i64tostr(blockGasLimit)+")");
1129 if (nGasLimit < MINIMUM_GAS_LIMIT)
1130 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid value for gasLimit (Minimum is: "+i64tostr(MINIMUM_GAS_LIMIT)+")");
1131 if (nGasLimit <= 0)
1132 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid value for gasLimit");
1133 }
1134
1135 if (params.size() > 4){
1136 nGasPrice = AmountFromValue(params[4]);
1137 if (nGasPrice <= 0)
1138 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid value for gasPrice");
1139 CAmount maxRpcGasPrice = gArgs.GetArg("-rpcmaxgasprice", MAX_RPC_GAS_PRICE);
1140 if (nGasPrice > (int64_t)maxRpcGasPrice)
1141 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid value for gasPrice, Maximum allowed in RPC calls is: "+FormatMoney(maxRpcGasPrice)+" (use -rpcmaxgasprice to change it)");
1142 if (nGasPrice < (int64_t)minGasPrice)
1143 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid value for gasPrice (Minimum is: "+FormatMoney(minGasPrice)+")");
1144 }
1145
1146 bool fHasSender=false;
1147 CTxDestination senderAddress;
1148 if (params.size() > 5){
1149 senderAddress = DecodeDestination(params[5].get_str());
1150 if (!IsValidDestination(senderAddress))
1151 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Qtum address to send from");
1152 if (!IsValidContractSenderAddress(senderAddress))
1153 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid contract sender address. Only P2PK and P2PKH allowed");
1154 else
1155 fHasSender=true;
1156 }
1157
1158 bool fBroadcast=true;
1159 if (params.size() > 6){
1160 fBroadcast=params[6].get_bool();
1161 }
1162
1163 bool fChangeToSender=true;
1164 if (params.size() > 7){
1165 fChangeToSender=params[7].get_bool();
1166 }
1167
1168 CCoinControl coinControl;
1169
1170 CTxDestination signSenderAddress = CNoDestination();
1171 if(fHasSender){
1172 // Find a UTXO with sender address
1173 std::vector<COutput> vecOutputs;
1174
1175 coinControl.fAllowOtherInputs=true;
1176
1177 assert(pwallet != NULL);
1178 pwallet->AvailableCoins(locked_chain, vecOutputs, false, NULL, true);
1179
1180 for (const COutput& out : vecOutputs) {
1181
1182 CTxDestination destAdress;
1183 const CScript& scriptPubKey = out.tx->tx->vout[out.i].scriptPubKey;
1184 bool fValidAddress = ExtractDestination(scriptPubKey, destAdress);
1185
1186 if (!fValidAddress || senderAddress != destAdress)
1187 continue;
1188
1189 coinControl.Select(COutPoint(out.tx->GetHash(),out.i));
1190
1191 break;
1192
1193 }
1194
1195 if(coinControl.HasSelected())
1196 {
1197 // Change to the sender
1198 if(fChangeToSender){
1199 coinControl.destChange=senderAddress;
1200 }
1201 }
1202 else
1203 {
1204 // Create op sender transaction when op sender is activated
1205 if(!(::ChainActive().Height() >= Params().GetConsensus().QIP5Height))
1206 throw JSONRPCError(RPC_TYPE_ERROR, "Sender address does not have any unspent outputs");
1207 }
1208
1209 if(::ChainActive().Height() >= Params().GetConsensus().QIP5Height)
1210 {
1211 // Set the sender address
1212 signSenderAddress = senderAddress;
1213 }
1214 }
1215 else
1216 {
1217 if(::ChainActive().Height() >= Params().GetConsensus().QIP5Height)
1218 {
1219 // If no sender address provided set to the default sender address
1220 SetDefaultSignSenderAddress(pwallet, locked_chain, signSenderAddress);
1221 }
1222 }
1223
1224 EnsureWalletIsUnlocked(pwallet);
1225
1226 CAmount nGasFee=nGasPrice*nGasLimit;
1227
1228 CAmount curBalance = pwallet->GetBalance().m_mine_trusted;
1229
1230 // Check amount
1231 if (nGasFee <= 0)
1232 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid amount for gas fee");
1233
1234 if (nAmount+nGasFee > curBalance)
1235 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds");
1236
1237 // Select default coin that will pay for the contract if none selected
1238 if(!coinControl.HasSelected() && !SetDefaultPayForContractAddress(pwallet, locked_chain, coinControl))
1239 throw JSONRPCError(RPC_TYPE_ERROR, "Does not have any P2PK or P2PKH unspent outputs to pay for the contract.");
1240
1241 // Build OP_EXEC_ASSIGN script
1242 CScript scriptPubKey = CScript() << CScriptNum(VersionVM::GetEVMDefault().toRaw()) << CScriptNum(nGasLimit) << CScriptNum(nGasPrice) << ParseHex(datahex) << ParseHex(contractaddress) << OP_CALL;
1243 if(::ChainActive().Height() >= Params().GetConsensus().QIP5Height)
1244 {
1245 if(IsValidDestination(signSenderAddress))
1246 {
1247 CKeyID key_id = GetKeyForDestination(spk_man, signSenderAddress);
1248 CKey key;
1249 if (!spk_man.GetKey(key_id, key)) {
1250 throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available");
1251 }
1252 std::vector<unsigned char> scriptSig;
1253 scriptPubKey = (CScript() << CScriptNum(addresstype::PUBKEYHASH) << ToByteVector(key_id) << ToByteVector(scriptSig) << OP_SENDER) + scriptPubKey;
1254 }
1255 else
1256 {
1257 // OP_SENDER will always be used when QIP5Height is active
1258 throw JSONRPCError(RPC_TYPE_ERROR, "Sender address fail to set for OP_SENDER.");
1259 }
1260 }
1261
1262 // Create and send the transaction
1263 CAmount nFeeRequired;
1264 std::string strError;
1265 std::vector<CRecipient> vecSend;
1266 int nChangePosRet = -1;
1267 CRecipient recipient = {scriptPubKey, nAmount, false};
1268 vecSend.push_back(recipient);
1269
1270 CTransactionRef tx;
1271 if (!pwallet->CreateTransaction(locked_chain, vecSend, tx, nFeeRequired, nChangePosRet, strError, coinControl, true, nGasFee, true, signSenderAddress)) {
1272 if (nFeeRequired > pwallet->GetBalance().m_mine_trusted)
1273 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));
1274 throw JSONRPCError(RPC_WALLET_ERROR, strError);
1275 }
1276
1277 CTxDestination txSenderDest;
1278 GetSenderDest(pwallet, tx, txSenderDest);
1279
1280 if (fHasSender && !(senderAddress == txSenderDest)){
1281 throw JSONRPCError(RPC_TYPE_ERROR, "Sender could not be set, transaction was not committed!");
1282 }
1283
1284 UniValue result(UniValue::VOBJ);
1285
1286 if(fBroadcast){
1287 pwallet->CommitTransaction(tx, {}, {});
1288
1289 std::string txId=tx->GetHash().GetHex();
1290 result.pushKV("txid", txId);
1291
1292 CTxDestination txSenderAdress(txSenderDest);
1293 CKeyID keyid = GetKeyForDestination(spk_man, txSenderAdress);
1294
1295 result.pushKV("sender", EncodeDestination(txSenderAdress));
1296 result.pushKV("hash160", HexStr(valtype(keyid.begin(),keyid.end())));
1297 }else{
1298 std::string strHex = EncodeHexTx(*tx, RPCSerializationFlags());
1299 result.pushKV("raw transaction", strHex);
1300 }
1301
1302 return result;
1303 }
1304
1305 /**
1306 * @brief The SendToken class Write token data
1307 */
1308 class SendToken : public CallToken
1309 {
1310 public:
SendToken(interfaces::Chain::Lock & _locked_chain,CWallet * const _pwallet,LegacyScriptPubKeyMan & _spk_man)1311 SendToken(interfaces::Chain::Lock& _locked_chain,
1312 CWallet* const _pwallet,
1313 LegacyScriptPubKeyMan& _spk_man):
1314 locked_chain(_locked_chain),
1315 pwallet(_pwallet),
1316 spk_man(_spk_man)
1317 {}
1318
execValid(const int & func,const bool & sendTo)1319 bool execValid(const int& func, const bool& sendTo)
1320 {
1321 return sendTo ? func != -1 : CallToken::execValid(func, sendTo);
1322 }
1323
exec(const bool & sendTo,const std::map<std::string,std::string> & lstParams,std::string & result,std::string & message)1324 bool exec(const bool& sendTo, const std::map<std::string, std::string>& lstParams, std::string& result, std::string& message)
1325 {
1326 if(!sendTo)
1327 return CallToken::exec(sendTo, lstParams, result, message);
1328
1329 UniValue params(UniValue::VARR);
1330
1331 // Set address
1332 auto it = lstParams.find(paramAddress());
1333 if(it != lstParams.end())
1334 params.push_back(it->second);
1335 else
1336 return false;
1337
1338 // Set data
1339 it = lstParams.find(paramDatahex());
1340 if(it != lstParams.end())
1341 params.push_back(it->second);
1342 else
1343 return false;
1344
1345 // Set amount
1346 it = lstParams.find(paramAmount());
1347 if(it != lstParams.end())
1348 {
1349 if(params.size() == 2)
1350 params.push_back(it->second);
1351 else
1352 return false;
1353 }
1354
1355 // Set gas limit
1356 it = lstParams.find(paramGasLimit());
1357 if(it != lstParams.end())
1358 {
1359 if(params.size() == 3) {
1360 UniValue param(UniValue::VNUM);
1361 param.setInt(atoi64(it->second));
1362 params.push_back(param);
1363 }
1364 else
1365 return false;
1366 }
1367
1368 // Set gas price
1369 it = lstParams.find(paramGasPrice());
1370 if(it != lstParams.end())
1371 {
1372 if(params.size() == 4)
1373 params.push_back(it->second);
1374 else
1375 return false;
1376 }
1377
1378 // Set sender
1379 it = lstParams.find(paramSender());
1380 if(it != lstParams.end())
1381 {
1382 if(params.size() == 5)
1383 params.push_back(it->second);
1384 else
1385 return false;
1386 }
1387
1388 // Set broadcast
1389 it = lstParams.find(paramBroadcast());
1390 if(it != lstParams.end())
1391 {
1392 if(params.size() == 6) {
1393 bool val = it->second == "true" ? true : false;
1394 UniValue param(UniValue::VBOOL);
1395 param.setBool(val);
1396 params.push_back(param);
1397 }
1398 else
1399 return false;
1400 }
1401
1402 // Set change to sender
1403 it = lstParams.find(paramChangeToSender());
1404 if(it != lstParams.end())
1405 {
1406 if(params.size() == 7) {
1407 bool val = it->second == "true" ? true : false;
1408 UniValue param(UniValue::VBOOL);
1409 param.setBool(val);
1410 params.push_back(param);
1411 }
1412 else
1413 return false;
1414 }
1415
1416 // Get execution result
1417 UniValue response = SendToContract(locked_chain, pwallet, spk_man, params);
1418 if(!response.isObject() || !response.exists("txid"))
1419 return false;
1420 result = response["txid"].get_str();
1421
1422 return true;
1423 }
1424 private:
1425 interfaces::Chain::Lock& locked_chain;
1426 CWallet* const pwallet;
1427 LegacyScriptPubKeyMan& spk_man;
1428 };
1429
sendtocontract(const JSONRPCRequest & request)1430 static UniValue sendtocontract(const JSONRPCRequest& request){
1431
1432 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
1433 CWallet* const pwallet = wallet.get();
1434
1435 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
1436 return NullUniValue;
1437 }
1438
1439 LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*pwallet);
1440 auto locked_chain = pwallet->chain().lock();
1441 LOCK2(pwallet->cs_wallet, spk_man.cs_KeyStore);
1442 QtumDGP qtumDGP(globalState.get(), fGettingValuesDGP);
1443 uint64_t blockGasLimit = qtumDGP.getBlockGasLimit(::ChainActive().Height());
1444 uint64_t minGasPrice = CAmount(qtumDGP.getMinGasPrice(::ChainActive().Height()));
1445 CAmount nGasPrice = (minGasPrice>DEFAULT_GAS_PRICE)?minGasPrice:DEFAULT_GAS_PRICE;
1446
1447 RPCHelpMan{"sendtocontract",
1448 "\nSend funds and data to a contract." +
1449 HELP_REQUIRING_PASSPHRASE,
1450 {
1451 {"contractaddress", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The contract address that will receive the funds and data."},
1452 {"datahex", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "data to send."},
1453 {"amount", RPCArg::Type::AMOUNT, RPCArg::Optional::OMITTED, "The amount in " + CURRENCY_UNIT + " to send. eg 0.1, default: 0"},
1454 {"gasLimit", RPCArg::Type::AMOUNT, RPCArg::Optional::OMITTED, "gasLimit, default: "+i64tostr(DEFAULT_GAS_LIMIT_OP_SEND)+", max: "+i64tostr(blockGasLimit)},
1455 {"gasPrice", RPCArg::Type::AMOUNT, RPCArg::Optional::OMITTED, "gasPrice Qtum price per gas unit, default: "+FormatMoney(nGasPrice)+", min:"+FormatMoney(minGasPrice)},
1456 {"senderaddress", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "The qtum address that will be used as sender."},
1457 {"broadcast", RPCArg::Type::BOOL, RPCArg::Optional::OMITTED, "Whether to broadcast the transaction or not."},
1458 {"changeToSender", RPCArg::Type::BOOL, RPCArg::Optional::OMITTED, "Return the change to the sender."},
1459 },
1460 RPCResult{
1461 RPCResult::Type::ARR, "", "",
1462 {
1463 {RPCResult::Type::OBJ, "", "",
1464 {
1465 {RPCResult::Type::STR_HEX, "txid", "The transaction id"},
1466 {RPCResult::Type::STR, "sender", CURRENCY_UNIT + " address of the sender"},
1467 {RPCResult::Type::STR_HEX, "hash160", "Ripemd-160 hash of the sender"},
1468 }},
1469 }
1470 },
1471 RPCExamples{
1472 HelpExampleCli("sendtocontract", "\"c6ca2697719d00446d4ea51f6fac8fd1e9310214\" \"54f6127f\"")
1473 + HelpExampleCli("sendtocontract", "\"c6ca2697719d00446d4ea51f6fac8fd1e9310214\" \"54f6127f\" 12.0015 6000000 "+FormatMoney(minGasPrice)+" \"QM72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\"")
1474 },
1475 }.Check(request);
1476
1477 return SendToContract(*locked_chain, pwallet, spk_man, request.params);
1478 }
1479
removedelegationforaddress(const JSONRPCRequest & request)1480 static UniValue removedelegationforaddress(const JSONRPCRequest& request){
1481
1482 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
1483 CWallet* const pwallet = wallet.get();
1484
1485 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
1486 return NullUniValue;
1487 }
1488
1489 LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*pwallet);
1490 auto locked_chain = pwallet->chain().lock();
1491 LOCK2(pwallet->cs_wallet, spk_man.cs_KeyStore);
1492 QtumDGP qtumDGP(globalState.get(), fGettingValuesDGP);
1493 uint64_t blockGasLimit = qtumDGP.getBlockGasLimit(::ChainActive().Height());
1494 uint64_t minGasPrice = CAmount(qtumDGP.getMinGasPrice(::ChainActive().Height()));
1495 CAmount nGasPrice = (minGasPrice>DEFAULT_GAS_PRICE)?minGasPrice:DEFAULT_GAS_PRICE;
1496
1497 RPCHelpMan{"removedelegationforaddress",
1498 "\nRemove delegation for address." +
1499 HELP_REQUIRING_PASSPHRASE,
1500 {
1501 {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The qtum address to remove delegation, the address will be used as sender too."},
1502 {"gasLimit", RPCArg::Type::AMOUNT, RPCArg::Optional::OMITTED, "gasLimit, default: "+i64tostr(DEFAULT_GAS_LIMIT_OP_SEND)+", max: "+i64tostr(blockGasLimit)},
1503 {"gasPrice", RPCArg::Type::AMOUNT, RPCArg::Optional::OMITTED, "gasPrice Qtum price per gas unit, default: "+FormatMoney(nGasPrice)+", min:"+FormatMoney(minGasPrice)},
1504 },
1505 RPCResult{
1506 RPCResult::Type::ARR, "", "",
1507 {
1508 {RPCResult::Type::OBJ, "", "",
1509 {
1510 {RPCResult::Type::STR_HEX, "txid", "The transaction id"},
1511 {RPCResult::Type::STR, "sender", CURRENCY_UNIT + " address of the sender"},
1512 {RPCResult::Type::STR_HEX, "hash160", "Ripemd-160 hash of the sender"},
1513 }},
1514 }
1515 },
1516 RPCExamples{
1517 HelpExampleCli("removedelegationforaddress", " \"QM72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 6000000 "+FormatMoney(minGasPrice))
1518 },
1519 }.Check(request);
1520
1521 // Get send to contract parameters for removing delegation for address
1522 UniValue params(UniValue::VARR);
1523 UniValue contractaddress = HexStr(Params().GetConsensus().delegationsAddress);
1524 UniValue datahex = QtumDelegation::BytecodeRemove();
1525 UniValue amount = 0;
1526 UniValue gasLimit = request.params.size() > 1 ? request.params[1] : DEFAULT_GAS_LIMIT_OP_SEND;
1527 UniValue gasPrice = request.params.size() > 2 ? request.params[2] : FormatMoney(nGasPrice);
1528 UniValue senderaddress = request.params[0];
1529
1530 // Add the send to contract parameters to the list
1531 params.push_back(contractaddress);
1532 params.push_back(datahex);
1533 params.push_back(amount);
1534 params.push_back(gasLimit);
1535 params.push_back(gasPrice);
1536 params.push_back(senderaddress);
1537
1538 // Send to contract
1539 return SendToContract(*locked_chain, pwallet, spk_man, params);
1540 }
1541
setdelegateforaddress(const JSONRPCRequest & request)1542 static UniValue setdelegateforaddress(const JSONRPCRequest& request){
1543
1544 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
1545 CWallet* const pwallet = wallet.get();
1546
1547 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
1548 return NullUniValue;
1549 }
1550
1551 LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*pwallet);
1552 auto locked_chain = pwallet->chain().lock();
1553 LOCK2(pwallet->cs_wallet, spk_man.cs_KeyStore);
1554 QtumDGP qtumDGP(globalState.get(), fGettingValuesDGP);
1555 uint64_t blockGasLimit = qtumDGP.getBlockGasLimit(::ChainActive().Height());
1556 uint64_t minGasPrice = CAmount(qtumDGP.getMinGasPrice(::ChainActive().Height()));
1557 CAmount nGasPrice = (minGasPrice>DEFAULT_GAS_PRICE)?minGasPrice:DEFAULT_GAS_PRICE;
1558
1559 RPCHelpMan{"setdelegateforaddress",
1560 "\nSet delegate for address." +
1561 HELP_REQUIRING_PASSPHRASE,
1562 {
1563 {"staker", RPCArg::Type::STR, RPCArg::Optional::NO, "The qtum address for the staker."},
1564 {"fee", RPCArg::Type::NUM, RPCArg::Optional::NO, "Percentage of the reward that will be paid to the staker."},
1565 {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The qtum address that contain the coins that will be delegated to the staker, the address will be used as sender too."},
1566 {"gasLimit", RPCArg::Type::AMOUNT, RPCArg::Optional::OMITTED, "gasLimit, default: "+i64tostr(DEFAULT_GAS_LIMIT_OP_CREATE)+", max: "+i64tostr(blockGasLimit)},
1567 {"gasPrice", RPCArg::Type::AMOUNT, RPCArg::Optional::OMITTED, "gasPrice Qtum price per gas unit, default: "+FormatMoney(nGasPrice)+", min:"+FormatMoney(minGasPrice)},
1568 },
1569 RPCResult{
1570 RPCResult::Type::ARR, "", "",
1571 {
1572 {RPCResult::Type::OBJ, "", "",
1573 {
1574 {RPCResult::Type::STR_HEX, "txid", "The transaction id"},
1575 {RPCResult::Type::STR, "sender", CURRENCY_UNIT + " address of the sender"},
1576 {RPCResult::Type::STR_HEX, "hash160", "Ripemd-160 hash of the sender"},
1577 }},
1578 }
1579 },
1580 RPCExamples{
1581 HelpExampleCli("setdelegateforaddress", " \"QM72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 10 \"QX1GkJdye9WoUnrE2v6ZQhQ72EUVDtGXQX\" 6000000 "+FormatMoney(minGasPrice))
1582 },
1583 }.Check(request);
1584
1585 // Get send to contract parameters for add delegation for address
1586 UniValue params(UniValue::VARR);
1587 UniValue contractaddress = HexStr(Params().GetConsensus().delegationsAddress);
1588 UniValue amount = 0;
1589 UniValue gasLimit = request.params.size() > 3 ? request.params[3] : DEFAULT_GAS_LIMIT_OP_CREATE;
1590 UniValue gasPrice = request.params.size() > 4 ? request.params[4] : FormatMoney(nGasPrice);
1591 UniValue senderaddress = request.params[2];
1592
1593 // Parse the staker address
1594 CTxDestination destStaker = DecodeDestination(request.params[0].get_str());
1595 const PKHash *pkhStaker = boost::get<PKHash>(&destStaker);
1596 if (!pkhStaker) {
1597 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid contract address for staker. Only P2PK and P2PKH allowed");
1598 }
1599
1600 // Parse the staker fee
1601 int fee = request.params[1].get_int();
1602 if(fee < 0 || fee > 100)
1603 throw JSONRPCError(RPC_PARSE_ERROR, "The staker fee need to be between 0 and 100");
1604
1605 // Parse the sender address
1606 CTxDestination destSender = DecodeDestination(senderaddress.get_str());
1607 const PKHash *pkhSender = boost::get<PKHash>(&destSender);
1608 if (!pkhSender) {
1609 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid contract sender address. Only P2PK and P2PKH allowed");
1610 }
1611
1612 // Get the private key for the sender address
1613 CKey key;
1614 CKeyID keyID(*pkhSender);
1615 if (!spk_man.GetKey(keyID, key)) {
1616 throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available for the sender address");
1617 }
1618
1619 // Sign the staker address
1620 std::vector<unsigned char> PoD;
1621 std::string hexStaker = pkhStaker->GetReverseHex();
1622 if(!SignStr::SignMessage(key, hexStaker, PoD))
1623 throw JSONRPCError(RPC_WALLET_ERROR, "Fail to sign the staker address");
1624
1625 // Serialize the data
1626 std::string datahex;
1627 std::string errorMessage;
1628 if(!QtumDelegation::BytecodeAdd(hexStaker, fee, PoD, datahex, errorMessage))
1629 throw JSONRPCError(RPC_TYPE_ERROR, errorMessage);
1630
1631 // Add the send to contract parameters to the list
1632 params.push_back(contractaddress);
1633 params.push_back(datahex);
1634 params.push_back(amount);
1635 params.push_back(gasLimit);
1636 params.push_back(gasPrice);
1637 params.push_back(senderaddress);
1638
1639 // Send to contract
1640 return SendToContract(*locked_chain, pwallet, spk_man, params);
1641 }
1642
GetJsonSuperStakerConfig(const CSuperStakerInfo & superStaker)1643 UniValue GetJsonSuperStakerConfig(const CSuperStakerInfo& superStaker)
1644 {
1645 // Fill the json object with information
1646 UniValue result(UniValue::VOBJ);
1647 result.pushKV("address", EncodeDestination(PKHash(superStaker.stakerAddress)));
1648 result.pushKV("customconfig", superStaker.fCustomConfig);
1649 if(superStaker.fCustomConfig)
1650 {
1651 result.pushKV("stakingminfee", (int64_t)superStaker.nMinFee);
1652 result.pushKV("stakingminutxovalue", FormatMoney(superStaker.nMinDelegateUtxo));
1653 UniValue addressList(UniValue::VARR);
1654 for(uint160 address : superStaker.delegateAddressList)
1655 {
1656 addressList.push_back(EncodeDestination(PKHash(address)));
1657 }
1658 if(interfaces::AllowList == superStaker.nDelegateAddressType)
1659 {
1660 result.pushKV("allow", addressList);
1661 }
1662 if(interfaces::ExcludeList == superStaker.nDelegateAddressType)
1663 {
1664 result.pushKV("exclude", addressList);
1665 }
1666 }
1667
1668 return result;
1669 }
1670
setsuperstakervaluesforaddress(const JSONRPCRequest & request)1671 static UniValue setsuperstakervaluesforaddress(const JSONRPCRequest& request){
1672
1673 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
1674 CWallet* const pwallet = wallet.get();
1675
1676 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
1677 return NullUniValue;
1678 }
1679
1680 auto locked_chain = pwallet->chain().lock();
1681 LOCK(pwallet->cs_wallet);
1682 RPCHelpMan{"setsuperstakervaluesforaddress",
1683 "\nList super staker configuration values for address." +
1684 HELP_REQUIRING_PASSPHRASE,
1685 {
1686 {"", RPCArg::Type::OBJ, RPCArg::Optional::NO, "",
1687 {
1688 {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The address of the staker"},
1689 {"stakingminutxovalue", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "The output number"},
1690 {"stakingminfee", RPCArg::Type::NUM, RPCArg::Optional::NO, "depends on the value of the 'replaceable' and 'locktime' arguments", "The sequence number"},
1691 {"allow", RPCArg::Type::ARR, RPCArg::Optional::OMITTED, "A json array with allow delegate addresses.",
1692 {
1693 {"address", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "The delegate address"},
1694 },
1695 },
1696 {"exclude", RPCArg::Type::ARR, RPCArg::Optional::OMITTED, "A json array with exclude delegate addresses.",
1697 {
1698 {"address", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "The delegate address"},
1699 },
1700 },
1701 },
1702 },
1703 },
1704 RPCResult{
1705 RPCResult::Type::ARR, "", "",
1706 {
1707 {RPCResult::Type::OBJ, "", "",
1708 {
1709 {RPCResult::Type::STR, "address", "Address of the staker."},
1710 {RPCResult::Type::BOOL, "customconfig", "Custom configuration exist."},
1711 {RPCResult::Type::NUM, "stakingminfee", "Minimum fee for delegate."},
1712 {RPCResult::Type::NUM, "stakingminutxovalue", "Minimum UTXO value for delegate."},
1713 {RPCResult::Type::ARR, "allow", "List of allowed delegate addresses.",
1714 {
1715 {RPCResult::Type::STR, "address", "The delegate address"},
1716 },
1717 },
1718 {RPCResult::Type::ARR, "exclude", "List of excluded delegate addresses.",
1719 {
1720 {RPCResult::Type::STR, "address", "The delegate address"},
1721 },
1722 },
1723 }},
1724 }
1725 },
1726 RPCExamples{
1727 HelpExampleCli("setsuperstakervaluesforaddress", "\"{\\\"address\\\":\\\"QM72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\\\",\\\"stakingminutxovalue\\\": \\\"100\\\",\\\"stakingminfee\\\": 10}\"")
1728 + HelpExampleCli("setsuperstakervaluesforaddress", "\"{\\\"address\\\":\\\"QM72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\\\",\\\"stakingminutxovalue\\\": \\\"100\\\",\\\"stakingminfee\\\": 10,\\\"allow\\\":[\\\"QD1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\"]}\"")
1729 + HelpExampleCli("setsuperstakervaluesforaddress", "\"{\\\"address\\\":\\\"QM72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\\\",\\\"stakingminutxovalue\\\": \\\"100\\\",\\\"stakingminfee\\\": 10,\\\"exclude\\\":[\\\"QD1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\"]}\"")
1730 + HelpExampleRpc("setsuperstakervaluesforaddress", "\"{\\\"address\\\":\\\"QM72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\\\",\\\"stakingminutxovalue\\\": \\\"100\\\",\\\"stakingminfee\\\": 10}\"")
1731 + HelpExampleRpc("setsuperstakervaluesforaddress", "\"{\\\"address\\\":\\\"QM72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\\\",\\\"stakingminutxovalue\\\": \\\"100\\\",\\\"stakingminfee\\\": 10,\\\"allow\\\":[\\\"QD1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\"]}\"")
1732 + HelpExampleRpc("setsuperstakervaluesforaddress", "\"{\\\"address\\\":\\\"QM72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\\\",\\\"stakingminutxovalue\\\": \\\"100\\\",\\\"stakingminfee\\\": 10,\\\"exclude\\\":[\\\"QD1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\"]}\"")
1733 },
1734 }.Check(request);
1735
1736 // Get params for the super staker
1737 UniValue params(UniValue::VOBJ);
1738 params = request.params[0].get_obj();
1739
1740 // Parse the super staker address
1741 if(!params.exists("address"))
1742 throw JSONRPCError(RPC_TYPE_ERROR, "The super staker address doesn't exist");
1743 CTxDestination destStaker = DecodeDestination(params["address"].get_str());
1744 const PKHash *pkhStaker = boost::get<PKHash>(&destStaker);
1745 if (!pkhStaker) {
1746 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address for staker. Only P2PK and P2PKH allowed");
1747 }
1748
1749 // Parse the staking min utxo value
1750 if(!params.exists("stakingminutxovalue"))
1751 throw JSONRPCError(RPC_TYPE_ERROR, "The staking min utxo value doesn't exist");
1752 CAmount nMinUtxoValue = AmountFromValue(params["stakingminutxovalue"]);
1753 if (nMinUtxoValue < 0)
1754 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for staking min utxo value");
1755
1756 // Parse the staking min fee
1757 if(!params.exists("stakingminfee"))
1758 throw JSONRPCError(RPC_TYPE_ERROR, "The staking min fee doesn't exist");
1759 CAmount nMinFee = params["stakingminfee"].get_int();
1760 if (nMinFee < 0 || nMinFee > 100)
1761 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid value for staking min fee");
1762
1763 // Parse the delegation address lists
1764 if(params.exists("allow") && params.exists("exclude"))
1765 throw JSONRPCError(RPC_TYPE_ERROR, "The delegation address lists can be empty, or have either allow list or exclude list");
1766
1767 // Parse the delegation address lists
1768 int nDelegateAddressType = interfaces::AcceptAll;
1769 std::vector<UniValue> addressList;
1770 if(params.exists("allow"))
1771 {
1772 addressList = params["allow"].get_array().getValues();
1773 nDelegateAddressType = interfaces::AllowList;
1774 }
1775 else if(params.exists("exclude"))
1776 {
1777 addressList = params["exclude"].get_array().getValues();
1778 nDelegateAddressType = interfaces::ExcludeList;
1779 }
1780 std::vector<uint160> delegateAddressList;
1781 for(UniValue address : addressList)
1782 {
1783 CTxDestination destAddress = DecodeDestination(address.get_str());
1784 const PKHash *pkhAddress = boost::get<PKHash>(&destAddress);
1785 if (!pkhAddress) {
1786 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address for delegate in allow list or exclude list. Only P2PK and P2PKH allowed");
1787 }
1788 delegateAddressList.push_back(uint160(*pkhAddress));
1789 }
1790
1791 // Search for super staker
1792 CSuperStakerInfo superStaker;
1793 bool found = false;
1794 for(auto item : pwallet->mapSuperStaker)
1795 {
1796 if(PKHash(item.second.stakerAddress) == *pkhStaker)
1797 {
1798 superStaker = item.second;
1799 found = true;
1800 break;
1801 }
1802 }
1803
1804 if(found)
1805 {
1806 // Set custom configuration
1807 superStaker.fCustomConfig = true;
1808 superStaker.nMinFee = nMinFee;
1809 superStaker.nMinDelegateUtxo = nMinUtxoValue;
1810 superStaker.nDelegateAddressType = nDelegateAddressType;
1811 superStaker.delegateAddressList = delegateAddressList;
1812
1813 // Update super staker data
1814 if(!pwallet->AddSuperStakerEntry(superStaker))
1815 throw JSONRPCError(RPC_TYPE_ERROR, "Failed to update the super staker");
1816 }
1817 else
1818 {
1819 throw JSONRPCError(RPC_TYPE_ERROR, "Failed to find the super staker");
1820 }
1821
1822 return GetJsonSuperStakerConfig(superStaker);
1823 }
1824
listsuperstakercustomvalues(const JSONRPCRequest & request)1825 static UniValue listsuperstakercustomvalues(const JSONRPCRequest& request){
1826
1827 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
1828 CWallet* const pwallet = wallet.get();
1829
1830 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
1831 return NullUniValue;
1832 }
1833
1834 auto locked_chain = pwallet->chain().lock();
1835 LOCK(pwallet->cs_wallet);
1836 RPCHelpMan{"listsuperstakercustomvalues",
1837 "\nList custom super staker configurations values." +
1838 HELP_REQUIRING_PASSPHRASE,
1839 {},
1840 RPCResult{
1841 RPCResult::Type::ARR, "", "",
1842 {
1843 {RPCResult::Type::OBJ, "", "",
1844 {
1845 {RPCResult::Type::STR, "address", "Address of the staker."},
1846 {RPCResult::Type::BOOL, "customconfig", "Custom configuration exist."},
1847 {RPCResult::Type::NUM, "stakingminfee", "Minimum fee for delegate."},
1848 {RPCResult::Type::NUM, "stakingminutxovalue", "Minimum UTXO value for delegate."},
1849 {RPCResult::Type::ARR, "allow", "List of allowed delegate addresses.",
1850 {
1851 {RPCResult::Type::STR, "address", "The delegate address"},
1852 },
1853 },
1854 {RPCResult::Type::ARR, "exclude", "List of excluded delegate addresses.",
1855 {
1856 {RPCResult::Type::STR, "address", "The delegate address"},
1857 },
1858 },
1859 }},
1860 }
1861 },
1862 RPCExamples{
1863 HelpExampleCli("listsuperstakercustomvalues", "")
1864 + HelpExampleRpc("listsuperstakercustomvalues", "")
1865 },
1866 }.Check(request);
1867
1868 // Search for super stakers
1869 UniValue result(UniValue::VARR);
1870 for(auto item : pwallet->mapSuperStaker)
1871 {
1872 CSuperStakerInfo superStaker = item.second;
1873 if(superStaker.fCustomConfig)
1874 {
1875 result.push_back(GetJsonSuperStakerConfig(superStaker));
1876 }
1877 }
1878
1879 return result;
1880 }
1881
listsuperstakervaluesforaddress(const JSONRPCRequest & request)1882 static UniValue listsuperstakervaluesforaddress(const JSONRPCRequest& request){
1883
1884 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
1885 CWallet* const pwallet = wallet.get();
1886
1887 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
1888 return NullUniValue;
1889 }
1890
1891 auto locked_chain = pwallet->chain().lock();
1892 LOCK(pwallet->cs_wallet);
1893 RPCHelpMan{"listsuperstakervaluesforaddress",
1894 "\nList super staker configuration values for address." +
1895 HELP_REQUIRING_PASSPHRASE,
1896 {
1897 {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The super staker Qtum address."},
1898 },
1899 RPCResult{
1900 RPCResult::Type::ARR, "", "",
1901 {
1902 {RPCResult::Type::OBJ, "", "",
1903 {
1904 {RPCResult::Type::STR, "address", "Address of the staker."},
1905 {RPCResult::Type::BOOL, "customconfig", "Custom configuration exist."},
1906 {RPCResult::Type::NUM, "stakingminfee", "Minimum fee for delegate."},
1907 {RPCResult::Type::NUM, "stakingminutxovalue", "Minimum UTXO value for delegate."},
1908 {RPCResult::Type::ARR, "allow", "List of allowed delegate addresses.",
1909 {
1910 {RPCResult::Type::STR, "address", "The delegate address"},
1911 },
1912 },
1913 {RPCResult::Type::ARR, "exclude", "List of excluded delegate addresses.",
1914 {
1915 {RPCResult::Type::STR, "address", "The delegate address"},
1916 },
1917 },
1918 }},
1919 }
1920 },
1921 RPCExamples{
1922 HelpExampleCli("listsuperstakervaluesforaddress", "QM72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd")
1923 + HelpExampleRpc("listsuperstakervaluesforaddress", "QM72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd")
1924 },
1925 }.Check(request);
1926
1927 // Parse the super staker address
1928 CTxDestination destStaker = DecodeDestination(request.params[0].get_str());
1929 const PKHash *pkhStaker = boost::get<PKHash>(&destStaker);
1930 if (!pkhStaker) {
1931 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address for staker. Only P2PK and P2PKH allowed");
1932 }
1933
1934 // Search for super staker
1935 CSuperStakerInfo superStaker;
1936 bool found = false;
1937 for(auto item : pwallet->mapSuperStaker)
1938 {
1939 if(PKHash(item.second.stakerAddress) == *pkhStaker)
1940 {
1941 superStaker = item.second;
1942 found = true;
1943 break;
1944 }
1945 }
1946
1947 if(!found)
1948 {
1949 throw JSONRPCError(RPC_TYPE_ERROR, "Failed to find the super staker");
1950 }
1951
1952 return GetJsonSuperStakerConfig(superStaker);
1953 }
1954
removesuperstakervaluesforaddress(const JSONRPCRequest & request)1955 static UniValue removesuperstakervaluesforaddress(const JSONRPCRequest& request){
1956
1957 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
1958 CWallet* const pwallet = wallet.get();
1959
1960 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
1961 return NullUniValue;
1962 }
1963
1964 auto locked_chain = pwallet->chain().lock();
1965 LOCK(pwallet->cs_wallet);
1966 RPCHelpMan{"removesuperstakervaluesforaddress",
1967 "\nRemove super staker configuration values for address." +
1968 HELP_REQUIRING_PASSPHRASE,
1969 {
1970 {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The super staker Qtum address."},
1971 },
1972 RPCResults{},
1973 RPCExamples{
1974 HelpExampleCli("removesuperstakervaluesforaddress", "QM72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd")
1975 + HelpExampleRpc("removesuperstakervaluesforaddress", "QM72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd")
1976 },
1977 }.Check(request);
1978
1979 // Parse the super staker address
1980 CTxDestination destStaker = DecodeDestination(request.params[0].get_str());
1981 const PKHash *pkhStaker = boost::get<PKHash>(&destStaker);
1982 if (!pkhStaker) {
1983 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address for staker. Only P2PK and P2PKH allowed");
1984 }
1985
1986 // Search for super staker
1987 CSuperStakerInfo superStaker;
1988 bool found = false;
1989 for(auto item : pwallet->mapSuperStaker)
1990 {
1991 if(PKHash(item.second.stakerAddress) == *pkhStaker &&
1992 item.second.fCustomConfig)
1993 {
1994 superStaker = item.second;
1995 found = true;
1996 break;
1997 }
1998 }
1999
2000 if(found)
2001 {
2002 // Remove custom configuration
2003 superStaker.fCustomConfig = false;
2004 superStaker.nMinFee = 0;
2005 superStaker.nMinDelegateUtxo = 0;
2006 superStaker.nDelegateAddressType = 0;
2007 superStaker.delegateAddressList.clear();
2008
2009 // Update super staker data
2010 if(!pwallet->AddSuperStakerEntry(superStaker))
2011 throw JSONRPCError(RPC_TYPE_ERROR, "Failed to update the super staker");
2012 }
2013 else
2014 {
2015 throw JSONRPCError(RPC_TYPE_ERROR, "Failed to find the super staker");
2016 }
2017
2018 return NullUniValue;
2019 }
2020
listaddressgroupings(const JSONRPCRequest & request)2021 static UniValue listaddressgroupings(const JSONRPCRequest& request)
2022 {
2023 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
2024 const CWallet* const pwallet = wallet.get();
2025
2026 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
2027 return NullUniValue;
2028 }
2029
2030 RPCHelpMan{"listaddressgroupings",
2031 "\nLists groups of addresses which have had their common ownership\n"
2032 "made public by common use as inputs or as the resulting change\n"
2033 "in past transactions\n",
2034 {},
2035 RPCResult{
2036 RPCResult::Type::ARR, "", "",
2037 {
2038 {RPCResult::Type::ARR, "", "",
2039 {
2040 {RPCResult::Type::ARR, "", "",
2041 {
2042 {RPCResult::Type::STR, "address", "The qtum address"},
2043 {RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT},
2044 {RPCResult::Type::STR, "label", /* optional */ true, "The label"},
2045 }},
2046 }},
2047 }
2048 },
2049 RPCExamples{
2050 HelpExampleCli("listaddressgroupings", "")
2051 + HelpExampleRpc("listaddressgroupings", "")
2052 },
2053 }.Check(request);
2054
2055 // Make sure the results are valid at least up to the most recent block
2056 // the user could have gotten from another RPC command prior to now
2057 pwallet->BlockUntilSyncedToCurrentChain();
2058
2059 auto locked_chain = pwallet->chain().lock();
2060 LOCK(pwallet->cs_wallet);
2061
2062 UniValue jsonGroupings(UniValue::VARR);
2063 std::map<CTxDestination, CAmount> balances = pwallet->GetAddressBalances(*locked_chain);
2064 for (const std::set<CTxDestination>& grouping : pwallet->GetAddressGroupings()) {
2065 UniValue jsonGrouping(UniValue::VARR);
2066 for (const CTxDestination& address : grouping)
2067 {
2068 UniValue addressInfo(UniValue::VARR);
2069 addressInfo.push_back(EncodeDestination(address));
2070 addressInfo.push_back(ValueFromAmount(balances[address]));
2071 {
2072 const auto* address_book_entry = pwallet->FindAddressBookEntry(address);
2073 if (address_book_entry) {
2074 addressInfo.push_back(address_book_entry->GetLabel());
2075 }
2076 }
2077 jsonGrouping.push_back(addressInfo);
2078 }
2079 jsonGroupings.push_back(jsonGrouping);
2080 }
2081 return jsonGroupings;
2082 }
2083
signmessage(const JSONRPCRequest & request)2084 static UniValue signmessage(const JSONRPCRequest& request)
2085 {
2086 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
2087 const CWallet* const pwallet = wallet.get();
2088
2089 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
2090 return NullUniValue;
2091 }
2092
2093 RPCHelpMan{"signmessage",
2094 "\nSign a message with the private key of an address" +
2095 HELP_REQUIRING_PASSPHRASE,
2096 {
2097 {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The qtum address to use for the private key."},
2098 {"message", RPCArg::Type::STR, RPCArg::Optional::NO, "The message to create a signature of."},
2099 },
2100 RPCResult{
2101 RPCResult::Type::STR, "signature", "The signature of the message encoded in base 64"
2102 },
2103 RPCExamples{
2104 "\nUnlock the wallet for 30 seconds\n"
2105 + HelpExampleCli("walletpassphrase", "\"mypassphrase\" 30") +
2106 "\nCreate the signature\n"
2107 + HelpExampleCli("signmessage", "\"QD1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"my message\"") +
2108 "\nVerify the signature\n"
2109 + HelpExampleCli("verifymessage", "\"QD1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"signature\" \"my message\"") +
2110 "\nAs a JSON-RPC call\n"
2111 + HelpExampleRpc("signmessage", "\"QD1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", \"my message\"")
2112 },
2113 }.Check(request);
2114
2115 auto locked_chain = pwallet->chain().lock();
2116 LOCK(pwallet->cs_wallet);
2117
2118 EnsureWalletIsUnlocked(pwallet);
2119
2120 std::string strAddress = request.params[0].get_str();
2121 std::string strMessage = request.params[1].get_str();
2122
2123 CTxDestination dest = DecodeDestination(strAddress);
2124 if (!IsValidDestination(dest)) {
2125 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
2126 }
2127
2128 const PKHash *pkhash = boost::get<PKHash>(&dest);
2129 if (!pkhash) {
2130 throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
2131 }
2132
2133 std::string signature;
2134 SigningResult err = pwallet->SignMessage(strMessage, *pkhash, signature);
2135 if (err == SigningResult::SIGNING_FAILED) {
2136 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, SigningResultString(err));
2137 } else if (err != SigningResult::OK){
2138 throw JSONRPCError(RPC_WALLET_ERROR, SigningResultString(err));
2139 }
2140
2141 return signature;
2142 }
2143
getreceivedbyaddress(const JSONRPCRequest & request)2144 static UniValue getreceivedbyaddress(const JSONRPCRequest& request)
2145 {
2146 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
2147 const CWallet* const pwallet = wallet.get();
2148
2149 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
2150 return NullUniValue;
2151 }
2152
2153 RPCHelpMan{"getreceivedbyaddress",
2154 "\nReturns the total amount received by the given address in transactions with at least minconf confirmations.\n",
2155 {
2156 {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The qtum address for transactions."},
2157 {"minconf", RPCArg::Type::NUM, /* default */ "1", "Only include transactions confirmed at least this many times."},
2158 },
2159 RPCResult{
2160 RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " received at this address."
2161 },
2162 RPCExamples{
2163 "\nThe amount from transactions with at least 1 confirmation\n"
2164 + HelpExampleCli("getreceivedbyaddress", "\"" + EXAMPLE_ADDRESS[0] + "\"") +
2165 "\nThe amount including unconfirmed transactions, zero confirmations\n"
2166 + HelpExampleCli("getreceivedbyaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 0") +
2167 "\nThe amount with at least 6 confirmations, very safe\n"
2168 + HelpExampleCli("getreceivedbyaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 6") +
2169 "\nAs a JSON-RPC call\n"
2170 + HelpExampleRpc("getreceivedbyaddress", "\"" + EXAMPLE_ADDRESS[0] + "\", 6")
2171 },
2172 }.Check(request);
2173
2174 // Make sure the results are valid at least up to the most recent block
2175 // the user could have gotten from another RPC command prior to now
2176 pwallet->BlockUntilSyncedToCurrentChain();
2177
2178 auto locked_chain = pwallet->chain().lock();
2179 LOCK(pwallet->cs_wallet);
2180
2181 // Bitcoin address
2182 CTxDestination dest = DecodeDestination(request.params[0].get_str());
2183 if (!IsValidDestination(dest)) {
2184 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Qtum address");
2185 }
2186 CScript scriptPubKey = GetScriptForDestination(dest);
2187 if (!pwallet->IsMine(scriptPubKey)) {
2188 throw JSONRPCError(RPC_WALLET_ERROR, "Address not found in wallet");
2189 }
2190
2191 // Minimum confirmations
2192 int nMinDepth = 1;
2193 if (!request.params[1].isNull())
2194 nMinDepth = request.params[1].get_int();
2195
2196 // Tally
2197 CAmount nAmount = 0;
2198 for (const std::pair<const uint256, CWalletTx>& pairWtx : pwallet->mapWallet) {
2199 const CWalletTx& wtx = pairWtx.second;
2200 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !locked_chain->checkFinalTx(*wtx.tx)) {
2201 continue;
2202 }
2203
2204 for (const CTxOut& txout : wtx.tx->vout)
2205 if (txout.scriptPubKey == scriptPubKey)
2206 if (wtx.GetDepthInMainChain() >= nMinDepth)
2207 nAmount += txout.nValue;
2208 }
2209
2210 return ValueFromAmount(nAmount);
2211 }
2212
2213
getreceivedbylabel(const JSONRPCRequest & request)2214 static UniValue getreceivedbylabel(const JSONRPCRequest& request)
2215 {
2216 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
2217 const CWallet* const pwallet = wallet.get();
2218
2219 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
2220 return NullUniValue;
2221 }
2222
2223 RPCHelpMan{"getreceivedbylabel",
2224 "\nReturns the total amount received by addresses with <label> in transactions with at least [minconf] confirmations.\n",
2225 {
2226 {"label", RPCArg::Type::STR, RPCArg::Optional::NO, "The selected label, may be the default label using \"\"."},
2227 {"minconf", RPCArg::Type::NUM, /* default */ "1", "Only include transactions confirmed at least this many times."},
2228 },
2229 RPCResult{
2230 RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " received for this label."
2231 },
2232 RPCExamples{
2233 "\nAmount received by the default label with at least 1 confirmation\n"
2234 + HelpExampleCli("getreceivedbylabel", "\"\"") +
2235 "\nAmount received at the tabby label including unconfirmed amounts with zero confirmations\n"
2236 + HelpExampleCli("getreceivedbylabel", "\"tabby\" 0") +
2237 "\nThe amount with at least 6 confirmations\n"
2238 + HelpExampleCli("getreceivedbylabel", "\"tabby\" 6") +
2239 "\nAs a JSON-RPC call\n"
2240 + HelpExampleRpc("getreceivedbylabel", "\"tabby\", 6")
2241 },
2242 }.Check(request);
2243
2244 // Make sure the results are valid at least up to the most recent block
2245 // the user could have gotten from another RPC command prior to now
2246 pwallet->BlockUntilSyncedToCurrentChain();
2247
2248 auto locked_chain = pwallet->chain().lock();
2249 LOCK(pwallet->cs_wallet);
2250
2251 // Minimum confirmations
2252 int nMinDepth = 1;
2253 if (!request.params[1].isNull())
2254 nMinDepth = request.params[1].get_int();
2255
2256 // Get the set of pub keys assigned to label
2257 std::string label = LabelFromValue(request.params[0]);
2258 std::set<CTxDestination> setAddress = pwallet->GetLabelAddresses(label);
2259
2260 // Tally
2261 CAmount nAmount = 0;
2262 for (const std::pair<const uint256, CWalletTx>& pairWtx : pwallet->mapWallet) {
2263 const CWalletTx& wtx = pairWtx.second;
2264 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !locked_chain->checkFinalTx(*wtx.tx)) {
2265 continue;
2266 }
2267
2268 for (const CTxOut& txout : wtx.tx->vout)
2269 {
2270 CTxDestination address;
2271 if (ExtractDestination(txout.scriptPubKey, address) && pwallet->IsMine(address) && setAddress.count(address)) {
2272 if (wtx.GetDepthInMainChain() >= nMinDepth)
2273 nAmount += txout.nValue;
2274 }
2275 }
2276 }
2277
2278 return ValueFromAmount(nAmount);
2279 }
2280
2281
getbalance(const JSONRPCRequest & request)2282 static UniValue getbalance(const JSONRPCRequest& request)
2283 {
2284 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
2285 const CWallet* const pwallet = wallet.get();
2286
2287 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
2288 return NullUniValue;
2289 }
2290
2291 RPCHelpMan{"getbalance",
2292 "\nReturns the total available balance.\n"
2293 "The available balance is what the wallet considers currently spendable, and is\n"
2294 "thus affected by options which limit spendability such as -spendzeroconfchange.\n",
2295 {
2296 {"dummy", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "Remains for backward compatibility. Must be excluded or set to \"*\"."},
2297 {"minconf", RPCArg::Type::NUM, /* default */ "0", "Only include transactions confirmed at least this many times."},
2298 {"include_watchonly", RPCArg::Type::BOOL, /* default */ "true for watch-only wallets, otherwise false", "Also include balance in watch-only addresses (see 'importaddress')"},
2299 {"avoid_reuse", RPCArg::Type::BOOL, /* default */ "true", "(only available if avoid_reuse wallet flag is set) Do not include balance in dirty outputs; addresses are considered dirty if they have previously been used in a transaction."},
2300 },
2301 RPCResult{
2302 RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " received for this wallet."
2303 },
2304 RPCExamples{
2305 "\nThe total amount in the wallet with 1 or more confirmations\n"
2306 + HelpExampleCli("getbalance", "") +
2307 "\nThe total amount in the wallet at least 6 blocks confirmed\n"
2308 + HelpExampleCli("getbalance", "\"*\" 6") +
2309 "\nAs a JSON-RPC call\n"
2310 + HelpExampleRpc("getbalance", "\"*\", 6")
2311 },
2312 }.Check(request);
2313
2314 // Make sure the results are valid at least up to the most recent block
2315 // the user could have gotten from another RPC command prior to now
2316 pwallet->BlockUntilSyncedToCurrentChain();
2317
2318 auto locked_chain = pwallet->chain().lock();
2319 LOCK(pwallet->cs_wallet);
2320
2321 const UniValue& dummy_value = request.params[0];
2322 if (!dummy_value.isNull() && dummy_value.get_str() != "*") {
2323 throw JSONRPCError(RPC_METHOD_DEPRECATED, "dummy first argument must be excluded or set to \"*\".");
2324 }
2325
2326 int min_depth = 0;
2327 if (!request.params[1].isNull()) {
2328 min_depth = request.params[1].get_int();
2329 }
2330
2331 bool include_watchonly = ParseIncludeWatchonly(request.params[2], *pwallet);
2332
2333 bool avoid_reuse = GetAvoidReuseFlag(pwallet, request.params[3]);
2334
2335 const auto bal = pwallet->GetBalance(min_depth, avoid_reuse);
2336
2337 return ValueFromAmount(bal.m_mine_trusted + (include_watchonly ? bal.m_watchonly_trusted : 0));
2338 }
2339
getunconfirmedbalance(const JSONRPCRequest & request)2340 static UniValue getunconfirmedbalance(const JSONRPCRequest &request)
2341 {
2342 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
2343 const CWallet* const pwallet = wallet.get();
2344
2345 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
2346 return NullUniValue;
2347 }
2348
2349 RPCHelpMan{"getunconfirmedbalance",
2350 "DEPRECATED\nIdentical to getbalances().mine.untrusted_pending\n",
2351 {},
2352 RPCResult{RPCResult::Type::NUM, "", "The balance"},
2353 RPCExamples{""},
2354 }.Check(request);
2355
2356 // Make sure the results are valid at least up to the most recent block
2357 // the user could have gotten from another RPC command prior to now
2358 pwallet->BlockUntilSyncedToCurrentChain();
2359
2360 auto locked_chain = pwallet->chain().lock();
2361 LOCK(pwallet->cs_wallet);
2362
2363 return ValueFromAmount(pwallet->GetBalance().m_mine_untrusted_pending);
2364 }
2365
2366
sendmany(const JSONRPCRequest & request)2367 static UniValue sendmany(const JSONRPCRequest& request)
2368 {
2369 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
2370 CWallet* const pwallet = wallet.get();
2371
2372 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
2373 return NullUniValue;
2374 }
2375
2376 RPCHelpMan{"sendmany",
2377 "\nSend multiple times. Amounts are double-precision floating point numbers." +
2378 HELP_REQUIRING_PASSPHRASE,
2379 {
2380 {"dummy", RPCArg::Type::STR, RPCArg::Optional::NO, "Must be set to \"\" for backwards compatibility.", "\"\""},
2381 {"amounts", RPCArg::Type::OBJ, RPCArg::Optional::NO, "The addresses and amounts",
2382 {
2383 {"address", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "The qtum address is the key, the numeric amount (can be string) in " + CURRENCY_UNIT + " is the value"},
2384 },
2385 },
2386 {"minconf", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "Ignored dummy value"},
2387 {"comment", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "A comment"},
2388 {"subtractfeefrom", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "The addresses.\n"
2389 " The fee will be equally deducted from the amount of each selected address.\n"
2390 " Those recipients will receive less qtums than you enter in their corresponding amount field.\n"
2391 " If no addresses are specified here, the sender pays the fee.",
2392 {
2393 {"address", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Subtract fee from this address"},
2394 },
2395 },
2396 {"replaceable", RPCArg::Type::BOOL, /* default */ "wallet default", "Allow this transaction to be replaced by a transaction with higher fees via BIP 125"},
2397 {"conf_target", RPCArg::Type::NUM, /* default */ "wallet default", "Confirmation target (in blocks)"},
2398 {"estimate_mode", RPCArg::Type::STR, /* default */ "UNSET", "The fee estimate mode, must be one of:\n"
2399 " \"UNSET\"\n"
2400 " \"ECONOMICAL\"\n"
2401 " \"CONSERVATIVE\""},
2402 },
2403 RPCResult{
2404 RPCResult::Type::STR_HEX, "txid", "The transaction id for the send. Only 1 transaction is created regardless of\n"
2405 "the number of addresses."
2406 },
2407 RPCExamples{
2408 "\nSend two amounts to two different addresses:\n"
2409 + HelpExampleCli("sendmany", "\"\" \"{\\\"" + EXAMPLE_ADDRESS[0] + "\\\":0.01,\\\"" + EXAMPLE_ADDRESS[1] + "\\\":0.02}\"") +
2410 "\nSend two amounts to two different addresses setting the confirmation and comment:\n"
2411 + HelpExampleCli("sendmany", "\"\" \"{\\\"" + EXAMPLE_ADDRESS[0] + "\\\":0.01,\\\"" + EXAMPLE_ADDRESS[1] + "\\\":0.02}\" 6 \"testing\"") +
2412 "\nSend two amounts to two different addresses, subtract fee from amount:\n"
2413 + HelpExampleCli("sendmany", "\"\" \"{\\\"" + EXAMPLE_ADDRESS[0] + "\\\":0.01,\\\"" + EXAMPLE_ADDRESS[1] + "\\\":0.02}\" 1 \"\" \"[\\\"" + EXAMPLE_ADDRESS[0] + "\\\",\\\"" + EXAMPLE_ADDRESS[1] + "\\\"]\"") +
2414 "\nAs a JSON-RPC call\n"
2415 + HelpExampleRpc("sendmany", "\"\", {\"" + EXAMPLE_ADDRESS[0] + "\":0.01,\"" + EXAMPLE_ADDRESS[1] + "\":0.02}, 6, \"testing\"")
2416 },
2417 }.Check(request);
2418
2419 // Make sure the results are valid at least up to the most recent block
2420 // the user could have gotten from another RPC command prior to now
2421 pwallet->BlockUntilSyncedToCurrentChain();
2422
2423 auto locked_chain = pwallet->chain().lock();
2424 LOCK(pwallet->cs_wallet);
2425
2426 if (!request.params[0].isNull() && !request.params[0].get_str().empty()) {
2427 throw JSONRPCError(RPC_INVALID_PARAMETER, "Dummy value must be set to \"\"");
2428 }
2429 UniValue sendTo = request.params[1].get_obj();
2430
2431 mapValue_t mapValue;
2432 if (!request.params[3].isNull() && !request.params[3].get_str().empty())
2433 mapValue["comment"] = request.params[3].get_str();
2434
2435 UniValue subtractFeeFromAmount(UniValue::VARR);
2436 if (!request.params[4].isNull())
2437 subtractFeeFromAmount = request.params[4].get_array();
2438
2439 CCoinControl coin_control;
2440 if (!request.params[5].isNull()) {
2441 coin_control.m_signal_bip125_rbf = request.params[5].get_bool();
2442 }
2443
2444 if (!request.params[6].isNull()) {
2445 coin_control.m_confirm_target = ParseConfirmTarget(request.params[6], pwallet->chain().estimateMaxBlocks());
2446 }
2447
2448 if (!request.params[7].isNull()) {
2449 if (!FeeModeFromString(request.params[7].get_str(), coin_control.m_fee_mode)) {
2450 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid estimate_mode parameter");
2451 }
2452 }
2453
2454 std::set<CTxDestination> destinations;
2455 std::vector<CRecipient> vecSend;
2456
2457 std::vector<std::string> keys = sendTo.getKeys();
2458 for (const std::string& name_ : keys) {
2459 CTxDestination dest = DecodeDestination(name_);
2460 if (!IsValidDestination(dest)) {
2461 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Qtum address: ") + name_);
2462 }
2463
2464 if (destinations.count(dest)) {
2465 throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ") + name_);
2466 }
2467 destinations.insert(dest);
2468
2469 CScript scriptPubKey = GetScriptForDestination(dest);
2470 CAmount nAmount = AmountFromValue(sendTo[name_]);
2471 if (nAmount <= 0)
2472 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send");
2473
2474 bool fSubtractFeeFromAmount = false;
2475 for (unsigned int idx = 0; idx < subtractFeeFromAmount.size(); idx++) {
2476 const UniValue& addr = subtractFeeFromAmount[idx];
2477 if (addr.get_str() == name_)
2478 fSubtractFeeFromAmount = true;
2479 }
2480
2481 CRecipient recipient = {scriptPubKey, nAmount, fSubtractFeeFromAmount};
2482 vecSend.push_back(recipient);
2483 }
2484
2485 EnsureWalletIsUnlocked(pwallet);
2486
2487 // Shuffle recipient list
2488 std::shuffle(vecSend.begin(), vecSend.end(), FastRandomContext());
2489
2490 // Send
2491 CAmount nFeeRequired = 0;
2492 int nChangePosRet = -1;
2493 std::string strFailReason;
2494 CTransactionRef tx;
2495 bool fCreated = pwallet->CreateTransaction(*locked_chain, vecSend, tx, nFeeRequired, nChangePosRet, strFailReason, coin_control);
2496 if (!fCreated)
2497 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strFailReason);
2498 pwallet->CommitTransaction(tx, std::move(mapValue), {} /* orderForm */);
2499 return tx->GetHash().GetHex();
2500 }
2501
sendmanywithdupes(const JSONRPCRequest & request)2502 static UniValue sendmanywithdupes(const JSONRPCRequest& request)
2503 {
2504 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
2505 CWallet* const pwallet = wallet.get();
2506
2507 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
2508 return NullUniValue;
2509 }
2510
2511 RPCHelpMan{"sendmanywithdupes",
2512 "\nSend multiple times. Amounts are double-precision floating point numbers. Supports duplicate addresses" +
2513 HELP_REQUIRING_PASSPHRASE,
2514 {
2515 {"dummy", RPCArg::Type::STR, RPCArg::Optional::NO, "Must be set to \"\" for backwards compatibility.", "\"\""},
2516 {"amounts", RPCArg::Type::OBJ, RPCArg::Optional::NO, "A json object with addresses and amounts",
2517 {
2518 {"address", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "The qtum address is the key, the numeric amount (can be string) in " + CURRENCY_UNIT + " is the value"},
2519 },
2520 },
2521 {"minconf", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "Ignored dummy value"},
2522 {"comment", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "A comment"},
2523 {"subtractfeefrom", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "A json array with addresses.\n"
2524 " The fee will be equally deducted from the amount of each selected address.\n"
2525 " Those recipients will receive less qtums than you enter in their corresponding amount field.\n"
2526 " If no addresses are specified here, the sender pays the fee.",
2527 {
2528 {"address", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Subtract fee from this address"},
2529 },
2530 },
2531 {"replaceable", RPCArg::Type::BOOL, /* default */ "wallet default", "Allow this transaction to be replaced by a transaction with higher fees via BIP 125"},
2532 {"conf_target", RPCArg::Type::NUM, /* default */ "wallet default", "Confirmation target (in blocks)"},
2533 {"estimate_mode", RPCArg::Type::STR, /* default */ "UNSET", "The fee estimate mode, must be one of:\n"
2534 " \"UNSET\"\n"
2535 " \"ECONOMICAL\"\n"
2536 " \"CONSERVATIVE\""},
2537 },
2538 RPCResult{
2539 RPCResult::Type::STR_HEX, "txid", "The transaction id for the send. Only 1 transaction is created regardless of\n"
2540 "the number of addresses."
2541 },
2542 RPCExamples{
2543 "\nSend two amounts to two different addresses:\n"
2544 + HelpExampleCli("sendmanywithdupes", "\"\" \"{\\\"QD1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,\\\"Q353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\"") +
2545 "\nSend two amounts to two different addresses setting the confirmation and comment:\n"
2546 + HelpExampleCli("sendmanywithdupes", "\"\" \"{\\\"QD1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,\\\"Q353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\" 6 \"testing\"") +
2547 "\nSend two amounts to two different addresses, subtract fee from amount:\n"
2548 + HelpExampleCli("sendmanywithdupes", "\"\" \"{\\\"QD1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,\\\"Q353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\" 1 \"\" \"[\\\"QD1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\",\\\"Q353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\"]\"") +
2549 "\nAs a JSON-RPC call\n"
2550 + HelpExampleRpc("sendmanywithdupes", "\"\", {\"QD1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\":0.01,\"Q353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\":0.02}, 6, \"testing\"")
2551 },
2552 }.Check(request);
2553
2554 // Make sure the results are valid at least up to the most recent block
2555 // the user could have gotten from another RPC command prior to now
2556 pwallet->BlockUntilSyncedToCurrentChain();
2557
2558 auto locked_chain = pwallet->chain().lock();
2559 LOCK(pwallet->cs_wallet);
2560
2561 if (!request.params[0].isNull() && !request.params[0].get_str().empty()) {
2562 throw JSONRPCError(RPC_INVALID_PARAMETER, "Dummy value must be set to \"\"");
2563 }
2564 UniValue sendTo = request.params[1].get_obj();
2565
2566 mapValue_t mapValue;
2567 if (!request.params[3].isNull() && !request.params[3].get_str().empty())
2568 mapValue["comment"] = request.params[3].get_str();
2569
2570 UniValue subtractFeeFromAmount(UniValue::VARR);
2571 if (!request.params[4].isNull())
2572 subtractFeeFromAmount = request.params[4].get_array();
2573
2574 CCoinControl coin_control;
2575 if (!request.params[5].isNull()) {
2576 coin_control.m_signal_bip125_rbf = request.params[5].get_bool();
2577 }
2578
2579 if (!request.params[6].isNull()) {
2580 coin_control.m_confirm_target = ParseConfirmTarget(request.params[6], pwallet->chain().estimateMaxBlocks());
2581 }
2582
2583 if (!request.params[7].isNull()) {
2584 if (!FeeModeFromString(request.params[7].get_str(), coin_control.m_fee_mode)) {
2585 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid estimate_mode parameter");
2586 }
2587 }
2588
2589 std::set<CTxDestination> destinations;
2590 std::vector<CRecipient> vecSend;
2591
2592 std::vector<std::string> keys = sendTo.getKeys();
2593 int i=0;
2594 for (const std::string& name_ : keys) {
2595 CTxDestination dest = DecodeDestination(name_);
2596 if (!IsValidDestination(dest)) {
2597 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Qtum address: ") + name_);
2598 }
2599
2600 destinations.insert(dest);
2601
2602 CScript scriptPubKey = GetScriptForDestination(dest);
2603 CAmount nAmount = AmountFromValue(sendTo[i]);
2604 if (nAmount <= 0)
2605 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send");
2606
2607 bool fSubtractFeeFromAmount = false;
2608 for (unsigned int idx = 0; idx < subtractFeeFromAmount.size(); idx++) {
2609 const UniValue& addr = subtractFeeFromAmount[idx];
2610 if (addr.get_str() == name_)
2611 fSubtractFeeFromAmount = true;
2612 }
2613
2614 CRecipient recipient = {scriptPubKey, nAmount, fSubtractFeeFromAmount};
2615 vecSend.push_back(recipient);
2616 i++;
2617 }
2618
2619 EnsureWalletIsUnlocked(pwallet);
2620
2621 // Shuffle recipient list
2622 std::shuffle(vecSend.begin(), vecSend.end(), FastRandomContext());
2623
2624 // Send
2625 CAmount nFeeRequired = 0;
2626 int nChangePosRet = -1;
2627 std::string strFailReason;
2628 CTransactionRef tx;
2629 bool fCreated = pwallet->CreateTransaction(*locked_chain, vecSend, tx, nFeeRequired, nChangePosRet, strFailReason, coin_control);
2630 if (!fCreated)
2631 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strFailReason);
2632 pwallet->CommitTransaction(tx, std::move(mapValue), {} /* orderForm */);
2633
2634 return tx->GetHash().GetHex();
2635 }
2636
addmultisigaddress(const JSONRPCRequest & request)2637 static UniValue addmultisigaddress(const JSONRPCRequest& request)
2638 {
2639 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
2640 CWallet* const pwallet = wallet.get();
2641
2642 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
2643 return NullUniValue;
2644 }
2645
2646 RPCHelpMan{"addmultisigaddress",
2647 "\nAdd an nrequired-to-sign multisignature address to the wallet. Requires a new wallet backup.\n"
2648 "Each key is a Qtum address or hex-encoded public key.\n"
2649 "This functionality is only intended for use with non-watchonly addresses.\n"
2650 "See `importaddress` for watchonly p2sh address support.\n"
2651 "If 'label' is specified, assign address to that label.\n",
2652 {
2653 {"nrequired", RPCArg::Type::NUM, RPCArg::Optional::NO, "The number of required signatures out of the n keys or addresses."},
2654 {"keys", RPCArg::Type::ARR, RPCArg::Optional::NO, "A json array of qtum addresses or hex-encoded public keys",
2655 {
2656 {"key", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "qtum address or hex-encoded public key"},
2657 },
2658 },
2659 {"label", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "A label to assign the addresses to."},
2660 {"address_type", RPCArg::Type::STR, /* default */ "set by -addresstype", "The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
2661 },
2662 RPCResult{
2663 RPCResult::Type::OBJ, "", "",
2664 {
2665 {RPCResult::Type::STR, "address", "The value of the new multisig address"},
2666 {RPCResult::Type::STR_HEX, "redeemScript", "The string value of the hex-encoded redemption script"},
2667 {RPCResult::Type::STR, "descriptor", "The descriptor for this multisig"},
2668 }
2669 },
2670 RPCExamples{
2671 "\nAdd a multisig address from 2 addresses\n"
2672 + HelpExampleCli("addmultisigaddress", "2 \"[\\\"" + EXAMPLE_ADDRESS[0] + "\\\",\\\"" + EXAMPLE_ADDRESS[1] + "\\\"]\"") +
2673 "\nAs a JSON-RPC call\n"
2674 + HelpExampleRpc("addmultisigaddress", "2, \"[\\\"" + EXAMPLE_ADDRESS[0] + "\\\",\\\"" + EXAMPLE_ADDRESS[1] + "\\\"]\"")
2675 },
2676 }.Check(request);
2677
2678 LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*pwallet);
2679
2680 auto locked_chain = pwallet->chain().lock();
2681 LOCK2(pwallet->cs_wallet, spk_man.cs_KeyStore);
2682
2683 std::string label;
2684 if (!request.params[2].isNull())
2685 label = LabelFromValue(request.params[2]);
2686
2687 int required = request.params[0].get_int();
2688
2689 // Get the public keys
2690 const UniValue& keys_or_addrs = request.params[1].get_array();
2691 std::vector<CPubKey> pubkeys;
2692 for (unsigned int i = 0; i < keys_or_addrs.size(); ++i) {
2693 if (IsHex(keys_or_addrs[i].get_str()) && (keys_or_addrs[i].get_str().length() == 66 || keys_or_addrs[i].get_str().length() == 130)) {
2694 pubkeys.push_back(HexToPubKey(keys_or_addrs[i].get_str()));
2695 } else {
2696 pubkeys.push_back(AddrToPubKey(spk_man, keys_or_addrs[i].get_str()));
2697 }
2698 }
2699
2700 OutputType output_type = pwallet->m_default_address_type;
2701 if (!request.params[3].isNull()) {
2702 if (!ParseOutputType(request.params[3].get_str(), output_type)) {
2703 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[3].get_str()));
2704 }
2705 }
2706
2707 // Construct using pay-to-script-hash:
2708 CScript inner;
2709 CTxDestination dest = AddAndGetMultisigDestination(required, pubkeys, output_type, spk_man, inner);
2710 pwallet->SetAddressBook(dest, label, "send");
2711
2712 // Make the descriptor
2713 std::unique_ptr<Descriptor> descriptor = InferDescriptor(GetScriptForDestination(dest), spk_man);
2714
2715 UniValue result(UniValue::VOBJ);
2716 result.pushKV("address", EncodeDestination(dest));
2717 result.pushKV("redeemScript", HexStr(inner.begin(), inner.end()));
2718 result.pushKV("descriptor", descriptor->ToString());
2719 return result;
2720 }
2721
2722 struct tallyitem
2723 {
2724 CAmount nAmount{0};
2725 int nConf{std::numeric_limits<int>::max()};
2726 std::vector<uint256> txids;
2727 bool fIsWatchonly{false};
tallyitemtallyitem2728 tallyitem()
2729 {
2730 }
2731 };
2732
ListReceived(interfaces::Chain::Lock & locked_chain,const CWallet * const pwallet,const UniValue & params,bool by_label)2733 static UniValue ListReceived(interfaces::Chain::Lock& locked_chain, const CWallet* const pwallet, const UniValue& params, bool by_label) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
2734 {
2735 // Minimum confirmations
2736 int nMinDepth = 1;
2737 if (!params[0].isNull())
2738 nMinDepth = params[0].get_int();
2739
2740 // Whether to include empty labels
2741 bool fIncludeEmpty = false;
2742 if (!params[1].isNull())
2743 fIncludeEmpty = params[1].get_bool();
2744
2745 isminefilter filter = ISMINE_SPENDABLE;
2746
2747 if (ParseIncludeWatchonly(params[2], *pwallet)) {
2748 filter |= ISMINE_WATCH_ONLY;
2749 }
2750
2751 bool has_filtered_address = false;
2752 CTxDestination filtered_address = CNoDestination();
2753 if (!by_label && params.size() > 3) {
2754 if (!IsValidDestinationString(params[3].get_str())) {
2755 throw JSONRPCError(RPC_WALLET_ERROR, "address_filter parameter was invalid");
2756 }
2757 filtered_address = DecodeDestination(params[3].get_str());
2758 has_filtered_address = true;
2759 }
2760
2761 // Tally
2762 std::map<CTxDestination, tallyitem> mapTally;
2763 for (const std::pair<const uint256, CWalletTx>& pairWtx : pwallet->mapWallet) {
2764 const CWalletTx& wtx = pairWtx.second;
2765
2766 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !locked_chain.checkFinalTx(*wtx.tx)) {
2767 continue;
2768 }
2769
2770 int nDepth = wtx.GetDepthInMainChain();
2771 if (nDepth < nMinDepth)
2772 continue;
2773
2774 for (const CTxOut& txout : wtx.tx->vout)
2775 {
2776 CTxDestination address;
2777 if (!ExtractDestination(txout.scriptPubKey, address))
2778 continue;
2779
2780 if (has_filtered_address && !(filtered_address == address)) {
2781 continue;
2782 }
2783
2784 isminefilter mine = pwallet->IsMine(address);
2785 if(!(mine & filter))
2786 continue;
2787
2788 tallyitem& item = mapTally[address];
2789 item.nAmount += txout.nValue;
2790 item.nConf = std::min(item.nConf, nDepth);
2791 item.txids.push_back(wtx.GetHash());
2792 if (mine & ISMINE_WATCH_ONLY)
2793 item.fIsWatchonly = true;
2794 }
2795 }
2796
2797 // Reply
2798 UniValue ret(UniValue::VARR);
2799 std::map<std::string, tallyitem> label_tally;
2800
2801 // Create m_address_book iterator
2802 // If we aren't filtering, go from begin() to end()
2803 auto start = pwallet->m_address_book.begin();
2804 auto end = pwallet->m_address_book.end();
2805 // If we are filtering, find() the applicable entry
2806 if (has_filtered_address) {
2807 start = pwallet->m_address_book.find(filtered_address);
2808 if (start != end) {
2809 end = std::next(start);
2810 }
2811 }
2812
2813 for (auto item_it = start; item_it != end; ++item_it)
2814 {
2815 if (item_it->second.IsChange()) continue;
2816 const CTxDestination& address = item_it->first;
2817 const std::string& label = item_it->second.GetLabel();
2818 auto it = mapTally.find(address);
2819 if (it == mapTally.end() && !fIncludeEmpty)
2820 continue;
2821
2822 CAmount nAmount = 0;
2823 int nConf = std::numeric_limits<int>::max();
2824 bool fIsWatchonly = false;
2825 if (it != mapTally.end())
2826 {
2827 nAmount = (*it).second.nAmount;
2828 nConf = (*it).second.nConf;
2829 fIsWatchonly = (*it).second.fIsWatchonly;
2830 }
2831
2832 if (by_label)
2833 {
2834 tallyitem& _item = label_tally[label];
2835 _item.nAmount += nAmount;
2836 _item.nConf = std::min(_item.nConf, nConf);
2837 _item.fIsWatchonly = fIsWatchonly;
2838 }
2839 else
2840 {
2841 UniValue obj(UniValue::VOBJ);
2842 if(fIsWatchonly)
2843 obj.pushKV("involvesWatchonly", true);
2844 obj.pushKV("address", EncodeDestination(address));
2845 obj.pushKV("amount", ValueFromAmount(nAmount));
2846 obj.pushKV("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf));
2847 obj.pushKV("label", label);
2848 UniValue transactions(UniValue::VARR);
2849 if (it != mapTally.end())
2850 {
2851 for (const uint256& _item : (*it).second.txids)
2852 {
2853 transactions.push_back(_item.GetHex());
2854 }
2855 }
2856 obj.pushKV("txids", transactions);
2857 ret.push_back(obj);
2858 }
2859 }
2860
2861 if (by_label)
2862 {
2863 for (const auto& entry : label_tally)
2864 {
2865 CAmount nAmount = entry.second.nAmount;
2866 int nConf = entry.second.nConf;
2867 UniValue obj(UniValue::VOBJ);
2868 if (entry.second.fIsWatchonly)
2869 obj.pushKV("involvesWatchonly", true);
2870 obj.pushKV("amount", ValueFromAmount(nAmount));
2871 obj.pushKV("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf));
2872 obj.pushKV("label", entry.first);
2873 ret.push_back(obj);
2874 }
2875 }
2876
2877 return ret;
2878 }
2879
listreceivedbyaddress(const JSONRPCRequest & request)2880 static UniValue listreceivedbyaddress(const JSONRPCRequest& request)
2881 {
2882 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
2883 const CWallet* const pwallet = wallet.get();
2884
2885 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
2886 return NullUniValue;
2887 }
2888
2889 RPCHelpMan{"listreceivedbyaddress",
2890 "\nList balances by receiving address.\n",
2891 {
2892 {"minconf", RPCArg::Type::NUM, /* default */ "1", "The minimum number of confirmations before payments are included."},
2893 {"include_empty", RPCArg::Type::BOOL, /* default */ "false", "Whether to include addresses that haven't received any payments."},
2894 {"include_watchonly", RPCArg::Type::BOOL, /* default */ "true for watch-only wallets, otherwise false", "Whether to include watch-only addresses (see 'importaddress')"},
2895 {"address_filter", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "If present, only return information on this address."},
2896 },
2897 RPCResult{
2898 RPCResult::Type::ARR, "", "",
2899 {
2900 {RPCResult::Type::OBJ, "", "",
2901 {
2902 {RPCResult::Type::BOOL, "involvesWatchonly", "Only returns true if imported addresses were involved in transaction"},
2903 {RPCResult::Type::STR, "address", "The receiving address"},
2904 {RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " received by the address"},
2905 {RPCResult::Type::NUM, "confirmations", "The number of confirmations of the most recent transaction included"},
2906 {RPCResult::Type::STR, "label", "The label of the receiving address. The default label is \"\""},
2907 {RPCResult::Type::ARR, "txids", "",
2908 {
2909 {RPCResult::Type::STR_HEX, "txid", "The ids of transactions received with the address"},
2910 }},
2911 }},
2912 }
2913 },
2914 RPCExamples{
2915 HelpExampleCli("listreceivedbyaddress", "")
2916 + HelpExampleCli("listreceivedbyaddress", "6 true")
2917 + HelpExampleRpc("listreceivedbyaddress", "6, true, true")
2918 + HelpExampleRpc("listreceivedbyaddress", "6, true, true, \"" + EXAMPLE_ADDRESS[0] + "\"")
2919 },
2920 }.Check(request);
2921
2922 // Make sure the results are valid at least up to the most recent block
2923 // the user could have gotten from another RPC command prior to now
2924 pwallet->BlockUntilSyncedToCurrentChain();
2925
2926 auto locked_chain = pwallet->chain().lock();
2927 LOCK(pwallet->cs_wallet);
2928
2929 return ListReceived(*locked_chain, pwallet, request.params, false);
2930 }
2931
listreceivedbylabel(const JSONRPCRequest & request)2932 static UniValue listreceivedbylabel(const JSONRPCRequest& request)
2933 {
2934 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
2935 const CWallet* const pwallet = wallet.get();
2936
2937 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
2938 return NullUniValue;
2939 }
2940
2941 RPCHelpMan{"listreceivedbylabel",
2942 "\nList received transactions by label.\n",
2943 {
2944 {"minconf", RPCArg::Type::NUM, /* default */ "1", "The minimum number of confirmations before payments are included."},
2945 {"include_empty", RPCArg::Type::BOOL, /* default */ "false", "Whether to include labels that haven't received any payments."},
2946 {"include_watchonly", RPCArg::Type::BOOL, /* default */ "true for watch-only wallets, otherwise false", "Whether to include watch-only addresses (see 'importaddress')"},
2947 },
2948 RPCResult{
2949 RPCResult::Type::ARR, "", "",
2950 {
2951 {RPCResult::Type::OBJ, "", "",
2952 {
2953 {RPCResult::Type::BOOL, "involvesWatchonly", "Only returns true if imported addresses were involved in transaction"},
2954 {RPCResult::Type::STR_AMOUNT, "amount", "The total amount received by addresses with this label"},
2955 {RPCResult::Type::NUM, "confirmations", "The number of confirmations of the most recent transaction included"},
2956 {RPCResult::Type::STR, "label", "The label of the receiving address. The default label is \"\""},
2957 }},
2958 }
2959 },
2960 RPCExamples{
2961 HelpExampleCli("listreceivedbylabel", "")
2962 + HelpExampleCli("listreceivedbylabel", "6 true")
2963 + HelpExampleRpc("listreceivedbylabel", "6, true, true")
2964 },
2965 }.Check(request);
2966
2967 // Make sure the results are valid at least up to the most recent block
2968 // the user could have gotten from another RPC command prior to now
2969 pwallet->BlockUntilSyncedToCurrentChain();
2970
2971 auto locked_chain = pwallet->chain().lock();
2972 LOCK(pwallet->cs_wallet);
2973
2974 return ListReceived(*locked_chain, pwallet, request.params, true);
2975 }
2976
MaybePushAddress(UniValue & entry,const CTxDestination & dest)2977 static void MaybePushAddress(UniValue & entry, const CTxDestination &dest)
2978 {
2979 if (IsValidDestination(dest)) {
2980 entry.pushKV("address", EncodeDestination(dest));
2981 }
2982 }
2983
2984 /**
2985 * List transactions based on the given criteria.
2986 *
2987 * @param pwallet The wallet.
2988 * @param wtx The wallet transaction.
2989 * @param nMinDepth The minimum confirmation depth.
2990 * @param fLong Whether to include the JSON version of the transaction.
2991 * @param ret The UniValue into which the result is stored.
2992 * @param filter_ismine The "is mine" filter flags.
2993 * @param filter_label Optional label string to filter incoming transactions.
2994 */
ListTransactions(interfaces::Chain::Lock & locked_chain,const CWallet * const pwallet,const CWalletTx & wtx,int nMinDepth,bool fLong,UniValue & ret,const isminefilter & filter_ismine,const std::string * filter_label)2995 static void ListTransactions(interfaces::Chain::Lock& locked_chain, const CWallet* const pwallet, const CWalletTx& wtx, int nMinDepth, bool fLong, UniValue& ret, const isminefilter& filter_ismine, const std::string* filter_label) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
2996 {
2997 CAmount nFee;
2998 std::list<COutputEntry> listReceived;
2999 std::list<COutputEntry> listSent;
3000
3001 wtx.GetAmounts(listReceived, listSent, nFee, filter_ismine);
3002
3003 // Check if the coinstake transactions is mined by the wallet
3004 if(wtx.IsCoinStake() && listSent.size() > 0 && listReceived.size() > 0)
3005 {
3006 // Condense all of the coinstake inputs and outputs into one output and compute its value
3007 CAmount amount = wtx.GetCredit(filter_ismine) - wtx.GetDebit(filter_ismine);
3008 COutputEntry output = *listReceived.begin();
3009 output.amount = amount;
3010 listReceived.clear();
3011 listSent.clear();
3012 listReceived.push_back(output);
3013 }
3014
3015 bool involvesWatchonly = wtx.IsFromMe(ISMINE_WATCH_ONLY);
3016
3017 // Sent
3018 if (!filter_label)
3019 {
3020 for (const COutputEntry& s : listSent)
3021 {
3022 UniValue entry(UniValue::VOBJ);
3023 if (involvesWatchonly || (pwallet->IsMine(s.destination) & ISMINE_WATCH_ONLY)) {
3024 entry.pushKV("involvesWatchonly", true);
3025 }
3026 MaybePushAddress(entry, s.destination);
3027 entry.pushKV("category", "send");
3028 entry.pushKV("amount", ValueFromAmount(-s.amount));
3029 const auto* address_book_entry = pwallet->FindAddressBookEntry(s.destination);
3030 if (address_book_entry) {
3031 entry.pushKV("label", address_book_entry->GetLabel());
3032 }
3033 entry.pushKV("vout", s.vout);
3034 entry.pushKV("fee", ValueFromAmount(-nFee));
3035 if (fLong)
3036 WalletTxToJSON(pwallet->chain(), locked_chain, wtx, entry);
3037 entry.pushKV("abandoned", wtx.isAbandoned());
3038 ret.push_back(entry);
3039 }
3040 }
3041
3042 // Received
3043 if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth) {
3044 for (const COutputEntry& r : listReceived)
3045 {
3046 std::string label;
3047 const auto* address_book_entry = pwallet->FindAddressBookEntry(r.destination);
3048 if (address_book_entry) {
3049 label = address_book_entry->GetLabel();
3050 }
3051 if (filter_label && label != *filter_label) {
3052 continue;
3053 }
3054 UniValue entry(UniValue::VOBJ);
3055 if (involvesWatchonly || (pwallet->IsMine(r.destination) & ISMINE_WATCH_ONLY)) {
3056 entry.pushKV("involvesWatchonly", true);
3057 }
3058 MaybePushAddress(entry, r.destination);
3059 if (wtx.IsCoinBase() || wtx.IsCoinStake())
3060 {
3061 if (wtx.GetDepthInMainChain() < 1)
3062 entry.pushKV("category", "orphan");
3063 else if (wtx.IsImmature())
3064 entry.pushKV("category", "immature");
3065 else
3066 entry.pushKV("category", "generate");
3067 }
3068 else
3069 {
3070 entry.pushKV("category", "receive");
3071 }
3072 entry.pushKV("amount", ValueFromAmount(r.amount));
3073 if (address_book_entry) {
3074 entry.pushKV("label", label);
3075 }
3076 entry.pushKV("vout", r.vout);
3077 if (fLong)
3078 WalletTxToJSON(pwallet->chain(), locked_chain, wtx, entry);
3079 ret.push_back(entry);
3080 }
3081 }
3082 }
3083
TransactionDescriptionString()3084 static const std::vector<RPCResult> TransactionDescriptionString()
3085 {
3086 return{{RPCResult::Type::NUM, "confirmations", "The number of confirmations for the transaction. Negative confirmations means the\n"
3087 "transaction conflicted that many blocks ago."},
3088 {RPCResult::Type::BOOL, "generated", "Only present if transaction only input is a coinbase one."},
3089 {RPCResult::Type::BOOL, "trusted", "Only present if we consider transaction to be trusted and so safe to spend from."},
3090 {RPCResult::Type::STR_HEX, "blockhash", "The block hash containing the transaction."},
3091 {RPCResult::Type::NUM, "blockheight", "The block height containing the transaction."},
3092 {RPCResult::Type::NUM, "blockindex", "The index of the transaction in the block that includes it."},
3093 {RPCResult::Type::NUM_TIME, "blocktime", "The block time expressed in " + UNIX_EPOCH_TIME + "."},
3094 {RPCResult::Type::STR_HEX, "txid", "The transaction id."},
3095 {RPCResult::Type::ARR, "walletconflicts", "Conflicting transaction ids.",
3096 {
3097 {RPCResult::Type::STR_HEX, "txid", "The transaction id."},
3098 }},
3099 {RPCResult::Type::NUM_TIME, "time", "The transaction time expressed in " + UNIX_EPOCH_TIME + "."},
3100 {RPCResult::Type::NUM_TIME, "timereceived", "The time received expressed in " + UNIX_EPOCH_TIME + "."},
3101 {RPCResult::Type::STR, "comment", "If a comment is associated with the transaction, only present if not empty."},
3102 {RPCResult::Type::STR, "bip125-replaceable", "(\"yes|no|unknown\") Whether this transaction could be replaced due to BIP125 (replace-by-fee);\n"
3103 "may be unknown for unconfirmed transactions not in the mempool"}};
3104 }
3105
listtransactions(const JSONRPCRequest & request)3106 UniValue listtransactions(const JSONRPCRequest& request)
3107 {
3108 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
3109 const CWallet* const pwallet = wallet.get();
3110
3111 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
3112 return NullUniValue;
3113 }
3114
3115 RPCHelpMan{"listtransactions",
3116 "\nIf a label name is provided, this will return only incoming transactions paying to addresses with the specified label.\n"
3117 "\nReturns up to 'count' most recent transactions skipping the first 'from' transactions.\n",
3118 {
3119 {"label", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "If set, should be a valid label name to return only incoming transactions\n"
3120 " with the specified label, or \"*\" to disable filtering and return all transactions."},
3121 {"count", RPCArg::Type::NUM, /* default */ "10", "The number of transactions to return"},
3122 {"skip", RPCArg::Type::NUM, /* default */ "0", "The number of transactions to skip"},
3123 {"include_watchonly", RPCArg::Type::BOOL, /* default */ "true for watch-only wallets, otherwise false", "Include transactions to watch-only addresses (see 'importaddress')"},
3124 },
3125 RPCResult{
3126 RPCResult::Type::ARR, "", "",
3127 {
3128 {RPCResult::Type::OBJ, "", "", Cat(Cat<std::vector<RPCResult>>(
3129 {
3130 {RPCResult::Type::BOOL, "involvesWatchonly", "Only returns true if imported addresses were involved in transaction."},
3131 {RPCResult::Type::STR, "address", "The qtum address of the transaction."},
3132 {RPCResult::Type::STR, "category", "The transaction category.\n"
3133 "\"send\" Transactions sent.\n"
3134 "\"receive\" Non-coinbase and non-coinstake transactions received.\n"
3135 "\"generate\" Coinbase or coinstake transactions received with more than 500 confirmations.\n"
3136 "\"immature\" Coinbase or coinstake transactions received with 500 or fewer confirmations.\n"
3137 "\"orphan\" Orphaned coinbase or coinstake transactions received."},
3138 {RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and is positive\n"
3139 "for all other categories"},
3140 {RPCResult::Type::STR, "label", "A comment for the address/transaction, if any"},
3141 {RPCResult::Type::NUM, "vout", "the vout value"},
3142 {RPCResult::Type::STR_AMOUNT, "fee", "The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the\n"
3143 "'send' category of transactions."},
3144 },
3145 TransactionDescriptionString()),
3146 {
3147 {RPCResult::Type::BOOL, "abandoned", "'true' if the transaction has been abandoned (inputs are respendable). Only available for the \n"
3148 "'send' category of transactions."},
3149 })},
3150 }
3151 },
3152 RPCExamples{
3153 "\nList the most recent 10 transactions in the systems\n"
3154 + HelpExampleCli("listtransactions", "") +
3155 "\nList transactions 100 to 120\n"
3156 + HelpExampleCli("listtransactions", "\"*\" 20 100") +
3157 "\nAs a JSON-RPC call\n"
3158 + HelpExampleRpc("listtransactions", "\"*\", 20, 100")
3159 },
3160 }.Check(request);
3161
3162 // Make sure the results are valid at least up to the most recent block
3163 // the user could have gotten from another RPC command prior to now
3164 pwallet->BlockUntilSyncedToCurrentChain();
3165
3166 const std::string* filter_label = nullptr;
3167 if (!request.params[0].isNull() && request.params[0].get_str() != "*") {
3168 filter_label = &request.params[0].get_str();
3169 if (filter_label->empty()) {
3170 throw JSONRPCError(RPC_INVALID_PARAMETER, "Label argument must be a valid label name or \"*\".");
3171 }
3172 }
3173 int nCount = 10;
3174 if (!request.params[1].isNull())
3175 nCount = request.params[1].get_int();
3176 int nFrom = 0;
3177 if (!request.params[2].isNull())
3178 nFrom = request.params[2].get_int();
3179 isminefilter filter = ISMINE_SPENDABLE;
3180
3181 if (ParseIncludeWatchonly(request.params[3], *pwallet)) {
3182 filter |= ISMINE_WATCH_ONLY;
3183 }
3184
3185 if (nCount < 0)
3186 throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count");
3187 if (nFrom < 0)
3188 throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative from");
3189
3190 UniValue ret(UniValue::VARR);
3191
3192 {
3193 auto locked_chain = pwallet->chain().lock();
3194 LOCK(pwallet->cs_wallet);
3195
3196 const CWallet::TxItems & txOrdered = pwallet->wtxOrdered;
3197
3198 // iterate backwards until we have nCount items to return:
3199 for (CWallet::TxItems::const_reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
3200 {
3201 CWalletTx *const pwtx = (*it).second;
3202 ListTransactions(*locked_chain, pwallet, *pwtx, 0, true, ret, filter, filter_label);
3203 if ((int)ret.size() >= (nCount+nFrom)) break;
3204 }
3205 }
3206
3207 // ret is newest to oldest
3208
3209 if (nFrom > (int)ret.size())
3210 nFrom = ret.size();
3211 if ((nFrom + nCount) > (int)ret.size())
3212 nCount = ret.size() - nFrom;
3213
3214 const std::vector<UniValue>& txs = ret.getValues();
3215 UniValue result{UniValue::VARR};
3216 result.push_backV({ txs.rend() - nFrom - nCount, txs.rend() - nFrom }); // Return oldest to newest
3217 return result;
3218 }
3219
listsinceblock(const JSONRPCRequest & request)3220 static UniValue listsinceblock(const JSONRPCRequest& request)
3221 {
3222 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
3223 const CWallet* const pwallet = wallet.get();
3224
3225 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
3226 return NullUniValue;
3227 }
3228
3229 RPCHelpMan{"listsinceblock",
3230 "\nGet all transactions in blocks since block [blockhash], or all transactions if omitted.\n"
3231 "If \"blockhash\" is no longer a part of the main chain, transactions from the fork point onward are included.\n"
3232 "Additionally, if include_removed is set, transactions affecting the wallet which were removed are returned in the \"removed\" array.\n",
3233 {
3234 {"blockhash", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "If set, the block hash to list transactions since, otherwise list all transactions."},
3235 {"target_confirmations", RPCArg::Type::NUM, /* default */ "1", "Return the nth block hash from the main chain. e.g. 1 would mean the best block hash. Note: this is not used as a filter, but only affects [lastblock] in the return value"},
3236 {"include_watchonly", RPCArg::Type::BOOL, /* default */ "true for watch-only wallets, otherwise false", "Include transactions to watch-only addresses (see 'importaddress')"},
3237 {"include_removed", RPCArg::Type::BOOL, /* default */ "true", "Show transactions that were removed due to a reorg in the \"removed\" array\n"
3238 " (not guaranteed to work on pruned nodes)"},
3239 },
3240 RPCResult{
3241 RPCResult::Type::OBJ, "", "",
3242 {
3243 {RPCResult::Type::ARR, "transactions", "",
3244 {
3245 {RPCResult::Type::OBJ, "", "", Cat(Cat<std::vector<RPCResult>>(
3246 {
3247 {RPCResult::Type::BOOL, "involvesWatchonly", "Only returns true if imported addresses were involved in transaction."},
3248 {RPCResult::Type::STR, "address", "The qtum address of the transaction."},
3249 {RPCResult::Type::STR, "category", "The transaction category.\n"
3250 "\"send\" Transactions sent.\n"
3251 "\"receive\" Non-coinbase transactions received.\n"
3252 "\"generate\" Coinbase transactions received with more than 100 confirmations.\n"
3253 "\"immature\" Coinbase transactions received with 100 or fewer confirmations.\n"
3254 "\"orphan\" Orphaned coinbase transactions received."},
3255 {RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and is positive\n"
3256 "for all other categories"},
3257 {RPCResult::Type::NUM, "vout", "the vout value"},
3258 {RPCResult::Type::STR_AMOUNT, "fee", "The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the\n"
3259 "'send' category of transactions."},
3260 },
3261 TransactionDescriptionString()),
3262 {
3263 {RPCResult::Type::BOOL, "abandoned", "'true' if the transaction has been abandoned (inputs are respendable). Only available for the \n"
3264 "'send' category of transactions."},
3265 {RPCResult::Type::STR, "label", "A comment for the address/transaction, if any"},
3266 {RPCResult::Type::STR, "to", "If a comment to is associated with the transaction."},
3267 })},
3268 }},
3269 {RPCResult::Type::ARR, "removed", "<structure is the same as \"transactions\" above, only present if include_removed=true>\n"
3270 "Note: transactions that were re-added in the active chain will appear as-is in this array, and may thus have a positive confirmation count."
3271 , {{RPCResult::Type::ELISION, "", ""},}},
3272 {RPCResult::Type::STR_HEX, "lastblock", "The hash of the block (target_confirmations-1) from the best block on the main chain. This is typically used to feed back into listsinceblock the next time you call it. So you would generally use a target_confirmations of say 6, so you will be continually re-notified of transactions until they've reached 6 confirmations plus any new ones"},
3273 }
3274 },
3275 RPCExamples{
3276 HelpExampleCli("listsinceblock", "")
3277 + HelpExampleCli("listsinceblock", "\"000000000000000bacf66f7497b7dc45ef753ee9a7d38571037cdb1a57f663ad\" 6")
3278 + HelpExampleRpc("listsinceblock", "\"000000000000000bacf66f7497b7dc45ef753ee9a7d38571037cdb1a57f663ad\", 6")
3279 },
3280 }.Check(request);
3281
3282 // Make sure the results are valid at least up to the most recent block
3283 // the user could have gotten from another RPC command prior to now
3284 pwallet->BlockUntilSyncedToCurrentChain();
3285
3286 auto locked_chain = pwallet->chain().lock();
3287 LOCK(pwallet->cs_wallet);
3288
3289 // The way the 'height' is initialized is just a workaround for the gcc bug #47679 since version 4.6.0.
3290 Optional<int> height = MakeOptional(false, int()); // Height of the specified block or the common ancestor, if the block provided was in a deactivated chain.
3291 Optional<int> altheight; // Height of the specified block, even if it's in a deactivated chain.
3292 int target_confirms = 1;
3293 isminefilter filter = ISMINE_SPENDABLE;
3294
3295 uint256 blockId;
3296 if (!request.params[0].isNull() && !request.params[0].get_str().empty()) {
3297 blockId = ParseHashV(request.params[0], "blockhash");
3298 height = locked_chain->findFork(blockId, &altheight);
3299 if (!height) {
3300 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
3301 }
3302 }
3303
3304 if (!request.params[1].isNull()) {
3305 target_confirms = request.params[1].get_int();
3306
3307 if (target_confirms < 1) {
3308 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter");
3309 }
3310 }
3311
3312 if (ParseIncludeWatchonly(request.params[2], *pwallet)) {
3313 filter |= ISMINE_WATCH_ONLY;
3314 }
3315
3316 bool include_removed = (request.params[3].isNull() || request.params[3].get_bool());
3317
3318 const Optional<int> tip_height = locked_chain->getHeight();
3319 int depth = tip_height && height ? (1 + *tip_height - *height) : -1;
3320
3321 UniValue transactions(UniValue::VARR);
3322
3323 for (const std::pair<const uint256, CWalletTx>& pairWtx : pwallet->mapWallet) {
3324 CWalletTx tx = pairWtx.second;
3325
3326 if (depth == -1 || abs(tx.GetDepthInMainChain()) < depth) {
3327 ListTransactions(*locked_chain, pwallet, tx, 0, true, transactions, filter, nullptr /* filter_label */);
3328 }
3329 }
3330
3331 // when a reorg'd block is requested, we also list any relevant transactions
3332 // in the blocks of the chain that was detached
3333 UniValue removed(UniValue::VARR);
3334 while (include_removed && altheight && *altheight > *height) {
3335 CBlock block;
3336 if (!pwallet->chain().findBlock(blockId, &block) || block.IsNull()) {
3337 throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk");
3338 }
3339 for (const CTransactionRef& tx : block.vtx) {
3340 auto it = pwallet->mapWallet.find(tx->GetHash());
3341 if (it != pwallet->mapWallet.end()) {
3342 // We want all transactions regardless of confirmation count to appear here,
3343 // even negative confirmation ones, hence the big negative.
3344 ListTransactions(*locked_chain, pwallet, it->second, -100000000, true, removed, filter, nullptr /* filter_label */);
3345 }
3346 }
3347 blockId = block.hashPrevBlock;
3348 --*altheight;
3349 }
3350
3351 int last_height = tip_height ? *tip_height + 1 - target_confirms : -1;
3352 uint256 lastblock = last_height >= 0 ? locked_chain->getBlockHash(last_height) : uint256();
3353
3354 UniValue ret(UniValue::VOBJ);
3355 ret.pushKV("transactions", transactions);
3356 if (include_removed) ret.pushKV("removed", removed);
3357 ret.pushKV("lastblock", lastblock.GetHex());
3358
3359 return ret;
3360 }
3361
gettransaction(const JSONRPCRequest & request_)3362 static UniValue gettransaction(const JSONRPCRequest& request_)
3363 {
3364 // long-poll
3365 JSONRPCRequest& request = (JSONRPCRequest&) request_;
3366
3367 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
3368 const CWallet* const pwallet = wallet.get();
3369
3370 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
3371 return NullUniValue;
3372 }
3373
3374 RPCHelpMan{"gettransaction",
3375 "\nGet detailed information about in-wallet transaction <txid>\n",
3376 {
3377 {"txid", RPCArg::Type::STR, RPCArg::Optional::NO, "The transaction id"},
3378 {"include_watchonly", RPCArg::Type::BOOL, /* default */ "true for watch-only wallets, otherwise false",
3379 "Whether to include watch-only addresses in balance calculation and details[]"},
3380 {"verbose", RPCArg::Type::BOOL, /* default */ "false",
3381 "Whether to include a `decoded` field containing the decoded transaction (equivalent to RPC decoderawtransaction)"},
3382 {"waitconf", RPCArg::Type::NUM, /* default */ "0", "Wait for enough confirmations before returning."},
3383 },
3384 RPCResult{
3385 RPCResult::Type::OBJ, "", "", Cat(Cat<std::vector<RPCResult>>(
3386 {
3387 {RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT},
3388 {RPCResult::Type::STR_AMOUNT, "fee", "The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the\n"
3389 "'send' category of transactions."},
3390 },
3391 TransactionDescriptionString()),
3392 {
3393 {RPCResult::Type::ARR, "details", "",
3394 {
3395 {RPCResult::Type::OBJ, "", "",
3396 {
3397 {RPCResult::Type::BOOL, "involvesWatchonly", "Only returns true if imported addresses were involved in transaction."},
3398 {RPCResult::Type::STR, "address", "The qtum address involved in the transaction."},
3399 {RPCResult::Type::STR, "category", "The transaction category.\n"
3400 "\"send\" Transactions sent.\n"
3401 "\"receive\" Non-coinbase and non-coinstake transactions received.\n"
3402 "\"generate\" Coinbase or coinstake transactions received with more than 500 confirmations.\n"
3403 "\"immature\" Coinbase or coinstake transactions received with 500 or fewer confirmations.\n"
3404 "\"orphan\" Orphaned coinbase or coinstake transactions received."},
3405 {RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT},
3406 {RPCResult::Type::STR, "label", "A comment for the address/transaction, if any"},
3407 {RPCResult::Type::NUM, "vout", "the vout value"},
3408 {RPCResult::Type::STR_AMOUNT, "fee", "The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the \n"
3409 "'send' category of transactions."},
3410 {RPCResult::Type::BOOL, "abandoned", "'true' if the transaction has been abandoned (inputs are respendable). Only available for the \n"
3411 "'send' category of transactions."},
3412 }},
3413 }},
3414 {RPCResult::Type::STR_HEX, "hex", "Raw data for transaction"},
3415 {RPCResult::Type::OBJ, "decoded", "Optional, the decoded transaction (only present when `verbose` is passed)",
3416 {
3417 {RPCResult::Type::ELISION, "", "Equivalent to the RPC decoderawtransaction method, or the RPC getrawtransaction method when `verbose` is passed."},
3418 }},
3419 })
3420 },
3421 RPCExamples{
3422 HelpExampleCli("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"")
3423 + HelpExampleCli("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\" true")
3424 + HelpExampleCli("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\" false true")
3425 + HelpExampleRpc("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"")
3426 },
3427 }.Check(request);
3428
3429 // Make sure the results are valid at least up to the most recent block
3430 // the user could have gotten from another RPC command prior to now
3431 pwallet->BlockUntilSyncedToCurrentChain();
3432
3433
3434 uint256 hash(ParseHashV(request.params[0], "txid"));
3435
3436 isminefilter filter = ISMINE_SPENDABLE;
3437
3438 if (ParseIncludeWatchonly(request.params[1], *pwallet)) {
3439 filter |= ISMINE_WATCH_ONLY;
3440 }
3441
3442 bool verbose = request.params[2].isNull() ? false : request.params[2].get_bool();
3443
3444 auto it = pwallet->mapWallet.find(hash);
3445 if (it == pwallet->mapWallet.end()) {
3446 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id");
3447 }
3448
3449 int waitconf = 0;
3450 if(request.params.size() > 3) {
3451 waitconf = request.params[3].get_int();
3452 }
3453
3454 bool shouldWaitConf = request.params.size() > 3 && waitconf > 0;
3455
3456 {
3457 auto locked_chain = pwallet->chain().lock();
3458 LOCK(pwallet->cs_wallet);
3459 if (!pwallet->mapWallet.count(hash))
3460 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id");
3461 }
3462
3463 const CWalletTx* _wtx = nullptr;
3464
3465 // avoid long-poll if API caller does not specify waitconf
3466 if (!shouldWaitConf) {
3467 {
3468 auto locked_chain = pwallet->chain().lock();
3469 LOCK(pwallet->cs_wallet);
3470 _wtx = &pwallet->mapWallet.at(hash);
3471 }
3472
3473 } else {
3474 if(!request.req)
3475 throw JSONRPCError(RPC_INTERNAL_ERROR, "No HTTP connection. Waitconf is available from qtum-cli, not qtum-qt");
3476
3477 request.PollStart();
3478 while (true) {
3479 {
3480 auto locked_chain = pwallet->chain().lock();
3481 LOCK(pwallet->cs_wallet);
3482 _wtx = &pwallet->mapWallet.at(hash);
3483
3484 if (_wtx->GetDepthInMainChain() >= waitconf) {
3485 break;
3486 }
3487 }
3488
3489 request.PollPing();
3490
3491 std::unique_lock<std::mutex> lock(cs_blockchange);
3492 cond_blockchange.wait_for(lock, std::chrono::milliseconds(300));
3493
3494 if (!request.PollAlive() || !IsRPCRunning()) {
3495 return NullUniValue;
3496 }
3497 }
3498 }
3499
3500 auto locked_chain = pwallet->chain().lock();
3501 LOCK(pwallet->cs_wallet);
3502 const CWalletTx& wtx = *_wtx;
3503
3504 UniValue entry(UniValue::VOBJ);
3505 CAmount nCredit = wtx.GetCredit(filter);
3506 CAmount nDebit = wtx.GetDebit(filter);
3507 CAmount nNet = nCredit - nDebit;
3508 CAmount nFee = (wtx.IsFromMe(filter) ? wtx.tx->GetValueOut() - nDebit : 0);
3509
3510 if(wtx.IsCoinStake())
3511 {
3512 CAmount amount = nNet;
3513 entry.pushKV("amount", ValueFromAmount(amount));
3514 }
3515 else
3516 {
3517 entry.pushKV("amount", ValueFromAmount(nNet - nFee));
3518 if (wtx.IsFromMe(filter))
3519 entry.pushKV("fee", ValueFromAmount(nFee));
3520 }
3521
3522 WalletTxToJSON(pwallet->chain(), *locked_chain, wtx, entry);
3523
3524 UniValue details(UniValue::VARR);
3525 ListTransactions(*locked_chain, pwallet, wtx, 0, false, details, filter, nullptr /* filter_label */);
3526 entry.pushKV("details", details);
3527
3528 std::string strHex = EncodeHexTx(*wtx.tx, pwallet->chain().rpcSerializationFlags());
3529 entry.pushKV("hex", strHex);
3530
3531 if (verbose) {
3532 UniValue decoded(UniValue::VOBJ);
3533 TxToUniv(*wtx.tx, uint256(), decoded, false);
3534 entry.pushKV("decoded", decoded);
3535 }
3536
3537 return entry;
3538 }
3539
abandontransaction(const JSONRPCRequest & request)3540 static UniValue abandontransaction(const JSONRPCRequest& request)
3541 {
3542 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
3543 CWallet* const pwallet = wallet.get();
3544
3545 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
3546 return NullUniValue;
3547 }
3548
3549 RPCHelpMan{"abandontransaction",
3550 "\nMark in-wallet transaction <txid> as abandoned\n"
3551 "This will mark this transaction and all its in-wallet descendants as abandoned which will allow\n"
3552 "for their inputs to be respent. It can be used to replace \"stuck\" or evicted transactions.\n"
3553 "It only works on transactions which are not included in a block and are not currently in the mempool.\n"
3554 "It has no effect on transactions which are already abandoned.\n",
3555 {
3556 {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
3557 },
3558 RPCResult{RPCResult::Type::NONE, "", ""},
3559 RPCExamples{
3560 HelpExampleCli("abandontransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"")
3561 + HelpExampleRpc("abandontransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"")
3562 },
3563 }.Check(request);
3564
3565 // Make sure the results are valid at least up to the most recent block
3566 // the user could have gotten from another RPC command prior to now
3567 pwallet->BlockUntilSyncedToCurrentChain();
3568
3569 auto locked_chain = pwallet->chain().lock();
3570 LOCK(pwallet->cs_wallet);
3571
3572 uint256 hash(ParseHashV(request.params[0], "txid"));
3573
3574 if (!pwallet->mapWallet.count(hash)) {
3575 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id");
3576 }
3577 if (!pwallet->AbandonTransaction(hash)) {
3578 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not eligible for abandonment");
3579 }
3580
3581 return NullUniValue;
3582 }
3583
3584
backupwallet(const JSONRPCRequest & request)3585 static UniValue backupwallet(const JSONRPCRequest& request)
3586 {
3587 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
3588 const CWallet* const pwallet = wallet.get();
3589
3590 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
3591 return NullUniValue;
3592 }
3593
3594 RPCHelpMan{"backupwallet",
3595 "\nSafely copies current wallet file to destination, which can be a directory or a path with filename.\n",
3596 {
3597 {"destination", RPCArg::Type::STR, RPCArg::Optional::NO, "The destination directory or file"},
3598 },
3599 RPCResult{RPCResult::Type::NONE, "", ""},
3600 RPCExamples{
3601 HelpExampleCli("backupwallet", "\"backup.dat\"")
3602 + HelpExampleRpc("backupwallet", "\"backup.dat\"")
3603 },
3604 }.Check(request);
3605
3606 // Make sure the results are valid at least up to the most recent block
3607 // the user could have gotten from another RPC command prior to now
3608 pwallet->BlockUntilSyncedToCurrentChain();
3609
3610 auto locked_chain = pwallet->chain().lock();
3611 LOCK(pwallet->cs_wallet);
3612
3613 std::string strDest = request.params[0].get_str();
3614 if (!pwallet->BackupWallet(strDest)) {
3615 throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet backup failed!");
3616 }
3617
3618 return NullUniValue;
3619 }
3620
3621
keypoolrefill(const JSONRPCRequest & request)3622 static UniValue keypoolrefill(const JSONRPCRequest& request)
3623 {
3624 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
3625 CWallet* const pwallet = wallet.get();
3626
3627 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
3628 return NullUniValue;
3629 }
3630
3631 RPCHelpMan{"keypoolrefill",
3632 "\nFills the keypool."+
3633 HELP_REQUIRING_PASSPHRASE,
3634 {
3635 {"newsize", RPCArg::Type::NUM, /* default */ "100", "The new keypool size"},
3636 },
3637 RPCResult{RPCResult::Type::NONE, "", ""},
3638 RPCExamples{
3639 HelpExampleCli("keypoolrefill", "")
3640 + HelpExampleRpc("keypoolrefill", "")
3641 },
3642 }.Check(request);
3643
3644 if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
3645 throw JSONRPCError(RPC_WALLET_ERROR, "Error: Private keys are disabled for this wallet");
3646 }
3647
3648 auto locked_chain = pwallet->chain().lock();
3649 LOCK(pwallet->cs_wallet);
3650
3651 // 0 is interpreted by TopUpKeyPool() as the default keypool size given by -keypool
3652 unsigned int kpSize = 0;
3653 if (!request.params[0].isNull()) {
3654 if (request.params[0].get_int() < 0)
3655 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected valid size.");
3656 kpSize = (unsigned int)request.params[0].get_int();
3657 }
3658
3659 EnsureWalletIsUnlocked(pwallet);
3660 pwallet->TopUpKeyPool(kpSize);
3661
3662 if (pwallet->GetKeyPoolSize() < kpSize) {
3663 throw JSONRPCError(RPC_WALLET_ERROR, "Error refreshing keypool.");
3664 }
3665
3666 return NullUniValue;
3667 }
3668
3669
walletpassphrase(const JSONRPCRequest & request)3670 static UniValue walletpassphrase(const JSONRPCRequest& request)
3671 {
3672 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
3673 CWallet* const pwallet = wallet.get();
3674
3675 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
3676 return NullUniValue;
3677 }
3678
3679 RPCHelpMan{"walletpassphrase",
3680 "\nStores the wallet decryption key in memory for 'timeout' seconds.\n"
3681 "This is needed prior to performing transactions related to private keys such as sending QTUM and staking\n"
3682 "\nNote:\n"
3683 "Issuing the walletpassphrase command while the wallet is already unlocked will set a new unlock\n"
3684 "time that overrides the old one.\n",
3685 {
3686 {"passphrase", RPCArg::Type::STR, RPCArg::Optional::NO, "The wallet passphrase"},
3687 {"timeout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The time to keep the decryption key in seconds; capped at 100000000 (~3 years)."},
3688 {"staking only", RPCArg::Type::BOOL, RPCArg::Optional::OMITTED, "Unlock wallet for staking only"},
3689 },
3690 RPCResult{RPCResult::Type::NONE, "", ""},
3691 RPCExamples{
3692 "\nUnlock the wallet for 60 seconds\n"
3693 + HelpExampleCli("walletpassphrase", "\"my pass phrase\" 60") +
3694 "\nLock the wallet again (before 60 seconds)\n"
3695 + HelpExampleCli("walletlock", "") +
3696 "\nUnlock the wallet for staking only, for a long time\n"
3697 + HelpExampleCli("walletpassphrase","\"my pass phrase\" 99999999 true") +
3698 "\nAs a JSON-RPC call\n"
3699 + HelpExampleRpc("walletpassphrase", "\"my pass phrase\", 60")
3700 },
3701 }.Check(request);
3702
3703 int64_t nSleepTime;
3704 int64_t relock_time;
3705 // Prevent concurrent calls to walletpassphrase with the same wallet.
3706 LOCK(pwallet->m_unlock_mutex);
3707 {
3708 auto locked_chain = pwallet->chain().lock();
3709 LOCK(pwallet->cs_wallet);
3710
3711 if (request.fHelp)
3712 return true;
3713
3714 if (!pwallet->IsCrypted()) {
3715 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called.");
3716 }
3717
3718 // Note that the walletpassphrase is stored in request.params[0] which is not mlock()ed
3719 SecureString strWalletPass;
3720 strWalletPass.reserve(100);
3721 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
3722 // Alternately, find a way to make request.params[0] mlock()'d to begin with.
3723 strWalletPass = request.params[0].get_str().c_str();
3724
3725 // Get the timeout
3726 nSleepTime = request.params[1].get_int64();
3727 // Timeout cannot be negative, otherwise it will relock immediately
3728 if (nSleepTime < 0) {
3729 throw JSONRPCError(RPC_INVALID_PARAMETER, "Timeout cannot be negative.");
3730 }
3731 // Clamp timeout
3732 constexpr int64_t MAX_SLEEP_TIME = 100000000; // larger values trigger a macos/libevent bug?
3733 if (nSleepTime > MAX_SLEEP_TIME) {
3734 nSleepTime = MAX_SLEEP_TIME;
3735 }
3736
3737 if (strWalletPass.empty()) {
3738 throw JSONRPCError(RPC_INVALID_PARAMETER, "passphrase can not be empty");
3739 }
3740
3741 // Used to restore m_wallet_unlock_staking_only value in case of unlock failure
3742 bool tmpStakingOnly = pwallet->m_wallet_unlock_staking_only;
3743
3744 // ppcoin: if user OS account compromised prevent trivial sendmoney commands
3745 if (request.params.size() > 2)
3746 pwallet->m_wallet_unlock_staking_only = request.params[2].get_bool();
3747 else
3748 pwallet->m_wallet_unlock_staking_only = false;
3749
3750 if (!pwallet->Unlock(strWalletPass)) {
3751 pwallet->m_wallet_unlock_staking_only = tmpStakingOnly;
3752 throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
3753 }
3754
3755 pwallet->TopUpKeyPool();
3756
3757 pwallet->nRelockTime = GetTime() + nSleepTime;
3758 relock_time = pwallet->nRelockTime;
3759 }
3760
3761 // rpcRunLater must be called without cs_wallet held otherwise a deadlock
3762 // can occur. The deadlock would happen when RPCRunLater removes the
3763 // previous timer (and waits for the callback to finish if already running)
3764 // and the callback locks cs_wallet.
3765 AssertLockNotHeld(wallet->cs_wallet);
3766 // Keep a weak pointer to the wallet so that it is possible to unload the
3767 // wallet before the following callback is called. If a valid shared pointer
3768 // is acquired in the callback then the wallet is still loaded.
3769 std::weak_ptr<CWallet> weak_wallet = wallet;
3770 pwallet->chain().rpcRunLater(strprintf("lockwallet(%s)", pwallet->GetName()), [weak_wallet, relock_time] {
3771 if (auto shared_wallet = weak_wallet.lock()) {
3772 LOCK(shared_wallet->cs_wallet);
3773 // Skip if this is not the most recent rpcRunLater callback.
3774 if (shared_wallet->nRelockTime != relock_time) return;
3775 shared_wallet->Lock();
3776 shared_wallet->nRelockTime = 0;
3777 }
3778 }, nSleepTime);
3779
3780 return NullUniValue;
3781 }
3782
3783
walletpassphrasechange(const JSONRPCRequest & request)3784 static UniValue walletpassphrasechange(const JSONRPCRequest& request)
3785 {
3786 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
3787 CWallet* const pwallet = wallet.get();
3788
3789 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
3790 return NullUniValue;
3791 }
3792
3793 RPCHelpMan{"walletpassphrasechange",
3794 "\nChanges the wallet passphrase from 'oldpassphrase' to 'newpassphrase'.\n",
3795 {
3796 {"oldpassphrase", RPCArg::Type::STR, RPCArg::Optional::NO, "The current passphrase"},
3797 {"newpassphrase", RPCArg::Type::STR, RPCArg::Optional::NO, "The new passphrase"},
3798 },
3799 RPCResult{RPCResult::Type::NONE, "", ""},
3800 RPCExamples{
3801 HelpExampleCli("walletpassphrasechange", "\"old one\" \"new one\"")
3802 + HelpExampleRpc("walletpassphrasechange", "\"old one\", \"new one\"")
3803 },
3804 }.Check(request);
3805
3806 auto locked_chain = pwallet->chain().lock();
3807 LOCK(pwallet->cs_wallet);
3808
3809 if (request.fHelp)
3810 return true;
3811 if (!pwallet->IsCrypted()) {
3812 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrasechange was called.");
3813 }
3814
3815 // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string)
3816 // Alternately, find a way to make request.params[0] mlock()'d to begin with.
3817 SecureString strOldWalletPass;
3818 strOldWalletPass.reserve(100);
3819 strOldWalletPass = request.params[0].get_str().c_str();
3820
3821 SecureString strNewWalletPass;
3822 strNewWalletPass.reserve(100);
3823 strNewWalletPass = request.params[1].get_str().c_str();
3824
3825 if (strOldWalletPass.empty() || strNewWalletPass.empty()) {
3826 throw JSONRPCError(RPC_INVALID_PARAMETER, "passphrase can not be empty");
3827 }
3828
3829 if (!pwallet->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass)) {
3830 throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
3831 }
3832
3833 return NullUniValue;
3834 }
3835
3836
walletlock(const JSONRPCRequest & request)3837 static UniValue walletlock(const JSONRPCRequest& request)
3838 {
3839 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
3840 CWallet* const pwallet = wallet.get();
3841
3842 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
3843 return NullUniValue;
3844 }
3845
3846 RPCHelpMan{"walletlock",
3847 "\nRemoves the wallet encryption key from memory, locking the wallet.\n"
3848 "After calling this method, you will need to call walletpassphrase again\n"
3849 "before being able to call any methods which require the wallet to be unlocked.\n",
3850 {},
3851 RPCResult{RPCResult::Type::NONE, "", ""},
3852 RPCExamples{
3853 "\nSet the passphrase for 2 minutes to perform a transaction\n"
3854 + HelpExampleCli("walletpassphrase", "\"my pass phrase\" 120") +
3855 "\nPerform a send (requires passphrase set)\n"
3856 + HelpExampleCli("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 1.0") +
3857 "\nClear the passphrase since we are done before 2 minutes is up\n"
3858 + HelpExampleCli("walletlock", "") +
3859 "\nAs a JSON-RPC call\n"
3860 + HelpExampleRpc("walletlock", "")
3861 },
3862 }.Check(request);
3863
3864 auto locked_chain = pwallet->chain().lock();
3865 LOCK(pwallet->cs_wallet);
3866
3867 if (request.fHelp)
3868 return true;
3869 if (!pwallet->IsCrypted()) {
3870 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletlock was called.");
3871 }
3872
3873 pwallet->Lock();
3874 pwallet->nRelockTime = 0;
3875
3876 return NullUniValue;
3877 }
3878
3879
encryptwallet(const JSONRPCRequest & request)3880 static UniValue encryptwallet(const JSONRPCRequest& request)
3881 {
3882 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
3883 CWallet* const pwallet = wallet.get();
3884
3885 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
3886 return NullUniValue;
3887 }
3888
3889 RPCHelpMan{"encryptwallet",
3890 "\nEncrypts the wallet with 'passphrase'. This is for first time encryption.\n"
3891 "After this, any calls that interact with private keys such as sending or signing \n"
3892 "will require the passphrase to be set prior the making these calls.\n"
3893 "Use the walletpassphrase call for this, and then walletlock call.\n"
3894 "If the wallet is already encrypted, use the walletpassphrasechange call.\n",
3895 {
3896 {"passphrase", RPCArg::Type::STR, RPCArg::Optional::NO, "The pass phrase to encrypt the wallet with. It must be at least 1 character, but should be long."},
3897 },
3898 RPCResult{RPCResult::Type::STR, "", "A string with further instructions"},
3899 RPCExamples{
3900 "\nEncrypt your wallet\n"
3901 + HelpExampleCli("encryptwallet", "\"my pass phrase\"") +
3902 "\nNow set the passphrase to use the wallet, such as for signing or sending qtum\n"
3903 + HelpExampleCli("walletpassphrase", "\"my pass phrase\"") +
3904 "\nNow we can do something like sign\n"
3905 + HelpExampleCli("signmessage", "\"address\" \"test message\"") +
3906 "\nNow lock the wallet again by removing the passphrase\n"
3907 + HelpExampleCli("walletlock", "") +
3908 "\nAs a JSON-RPC call\n"
3909 + HelpExampleRpc("encryptwallet", "\"my pass phrase\"")
3910 },
3911 }.Check(request);
3912
3913 auto locked_chain = pwallet->chain().lock();
3914 LOCK(pwallet->cs_wallet);
3915
3916 if (request.fHelp)
3917 return true;
3918
3919 if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
3920 throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: wallet does not contain private keys, nothing to encrypt.");
3921 }
3922
3923 if (pwallet->IsCrypted()) {
3924 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an encrypted wallet, but encryptwallet was called.");
3925 }
3926
3927 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
3928 // Alternately, find a way to make request.params[0] mlock()'d to begin with.
3929 SecureString strWalletPass;
3930 strWalletPass.reserve(100);
3931 strWalletPass = request.params[0].get_str().c_str();
3932
3933 if (strWalletPass.empty()) {
3934 throw JSONRPCError(RPC_INVALID_PARAMETER, "passphrase can not be empty");
3935 }
3936
3937 if (!pwallet->EncryptWallet(strWalletPass)) {
3938 throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: Failed to encrypt the wallet.");
3939 }
3940
3941 return "wallet encrypted; The keypool has been flushed and a new HD seed was generated (if you are using HD). You need to make a new backup.";
3942 }
3943
reservebalance(const JSONRPCRequest & request)3944 static UniValue reservebalance(const JSONRPCRequest& request)
3945 {
3946 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
3947 CWallet* const pwallet = wallet.get();
3948
3949 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
3950 return NullUniValue;
3951 }
3952
3953 RPCHelpMan{"reservebalance",
3954 "\nSet reserve amount not participating in network protection."
3955 "\nIf no parameters provided current setting is printed.\n",
3956 {
3957 {"reserve", RPCArg::Type::BOOL, RPCArg::Optional::OMITTED,"is true or false to turn balance reserve on or off."},
3958 {"amount", RPCArg::Type::AMOUNT, RPCArg::Optional::OMITTED, "is a real and rounded to cent."},
3959 },
3960 RPCResults{},
3961 RPCExamples{
3962 "\nSet reserve balance to 100\n"
3963 + HelpExampleCli("reservebalance", "true 100") +
3964 "\nSet reserve balance to 0\n"
3965 + HelpExampleCli("reservebalance", "false") +
3966 "\nGet reserve balance\n"
3967 + HelpExampleCli("reservebalance", "") },
3968 }.Check(request);
3969
3970
3971 if (request.params.size() > 0)
3972 {
3973 bool fReserve = request.params[0].get_bool();
3974 if (fReserve)
3975 {
3976 if (request.params.size() == 1)
3977 throw std::runtime_error("must provide amount to reserve balance.\n");
3978 int64_t nAmount = AmountFromValue(request.params[1]);
3979 nAmount = (nAmount / CENT) * CENT; // round to cent
3980 if (nAmount < 0)
3981 throw std::runtime_error("amount cannot be negative.\n");
3982 pwallet->m_reserve_balance = nAmount;
3983 }
3984 else
3985 {
3986 if (request.params.size() > 1)
3987 throw std::runtime_error("cannot specify amount to turn off reserve.\n");
3988 pwallet->m_reserve_balance = 0;
3989 }
3990 }
3991
3992 UniValue result(UniValue::VOBJ);
3993 result.pushKV("reserve", (pwallet->m_reserve_balance > 0));
3994 result.pushKV("amount", ValueFromAmount(pwallet->m_reserve_balance));
3995 return result;
3996 }
3997
lockunspent(const JSONRPCRequest & request)3998 static UniValue lockunspent(const JSONRPCRequest& request)
3999 {
4000 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
4001 CWallet* const pwallet = wallet.get();
4002
4003 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
4004 return NullUniValue;
4005 }
4006
4007 RPCHelpMan{"lockunspent",
4008 "\nUpdates list of temporarily unspendable outputs.\n"
4009 "Temporarily lock (unlock=false) or unlock (unlock=true) specified transaction outputs.\n"
4010 "If no transaction outputs are specified when unlocking then all current locked transaction outputs are unlocked.\n"
4011 "A locked transaction output will not be chosen by automatic coin selection, when spending qtums.\n"
4012 "Locks are stored in memory only. Nodes start with zero locked outputs, and the locked output list\n"
4013 "is always cleared (by virtue of process exit) when a node stops or fails.\n"
4014 "Also see the listunspent call\n",
4015 {
4016 {"unlock", RPCArg::Type::BOOL, RPCArg::Optional::NO, "Whether to unlock (true) or lock (false) the specified transactions"},
4017 {"transactions", RPCArg::Type::ARR, /* default */ "empty array", "The transaction outputs and within each, the txid (string) vout (numeric).",
4018 {
4019 {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
4020 {
4021 {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
4022 {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
4023 },
4024 },
4025 },
4026 },
4027 },
4028 RPCResult{
4029 RPCResult::Type::BOOL, "", "Whether the command was successful or not"
4030 },
4031 RPCExamples{
4032 "\nList the unspent transactions\n"
4033 + HelpExampleCli("listunspent", "") +
4034 "\nLock an unspent transaction\n"
4035 + HelpExampleCli("lockunspent", "false \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") +
4036 "\nList the locked transactions\n"
4037 + HelpExampleCli("listlockunspent", "") +
4038 "\nUnlock the transaction again\n"
4039 + HelpExampleCli("lockunspent", "true \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") +
4040 "\nAs a JSON-RPC call\n"
4041 + HelpExampleRpc("lockunspent", "false, \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"")
4042 },
4043 }.Check(request);
4044
4045 // Make sure the results are valid at least up to the most recent block
4046 // the user could have gotten from another RPC command prior to now
4047 pwallet->BlockUntilSyncedToCurrentChain();
4048
4049 auto locked_chain = pwallet->chain().lock();
4050 LOCK(pwallet->cs_wallet);
4051
4052 RPCTypeCheckArgument(request.params[0], UniValue::VBOOL);
4053
4054 bool fUnlock = request.params[0].get_bool();
4055
4056 if (request.params[1].isNull()) {
4057 if (fUnlock)
4058 pwallet->UnlockAllCoins();
4059 return true;
4060 }
4061
4062 RPCTypeCheckArgument(request.params[1], UniValue::VARR);
4063
4064 const UniValue& output_params = request.params[1];
4065
4066 // Create and validate the COutPoints first.
4067
4068 std::vector<COutPoint> outputs;
4069 outputs.reserve(output_params.size());
4070
4071 for (unsigned int idx = 0; idx < output_params.size(); idx++) {
4072 const UniValue& o = output_params[idx].get_obj();
4073
4074 RPCTypeCheckObj(o,
4075 {
4076 {"txid", UniValueType(UniValue::VSTR)},
4077 {"vout", UniValueType(UniValue::VNUM)},
4078 });
4079
4080 const uint256 txid(ParseHashO(o, "txid"));
4081 const int nOutput = find_value(o, "vout").get_int();
4082 if (nOutput < 0) {
4083 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive");
4084 }
4085
4086 const COutPoint outpt(txid, nOutput);
4087
4088 const auto it = pwallet->mapWallet.find(outpt.hash);
4089 if (it == pwallet->mapWallet.end()) {
4090 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, unknown transaction");
4091 }
4092
4093 const CWalletTx& trans = it->second;
4094
4095 if (outpt.n >= trans.tx->vout.size()) {
4096 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout index out of bounds");
4097 }
4098
4099 if (pwallet->IsSpent(outpt.hash, outpt.n)) {
4100 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected unspent output");
4101 }
4102
4103 const bool is_locked = pwallet->IsLockedCoin(outpt.hash, outpt.n);
4104
4105 if (fUnlock && !is_locked) {
4106 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected locked output");
4107 }
4108
4109 if (!fUnlock && is_locked) {
4110 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, output already locked");
4111 }
4112
4113 outputs.push_back(outpt);
4114 }
4115
4116 // Atomically set (un)locked status for the outputs.
4117 for (const COutPoint& outpt : outputs) {
4118 if (fUnlock) pwallet->UnlockCoin(outpt);
4119 else pwallet->LockCoin(outpt);
4120 }
4121
4122 return true;
4123 }
4124
listlockunspent(const JSONRPCRequest & request)4125 static UniValue listlockunspent(const JSONRPCRequest& request)
4126 {
4127 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
4128 const CWallet* const pwallet = wallet.get();
4129
4130 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
4131 return NullUniValue;
4132 }
4133
4134 RPCHelpMan{"listlockunspent",
4135 "\nReturns list of temporarily unspendable outputs.\n"
4136 "See the lockunspent call to lock and unlock transactions for spending.\n",
4137 {},
4138 RPCResult{
4139 RPCResult::Type::ARR, "", "",
4140 {
4141 {RPCResult::Type::OBJ, "", "",
4142 {
4143 {RPCResult::Type::STR_HEX, "txid", "The transaction id locked"},
4144 {RPCResult::Type::NUM, "vout", "The vout value"},
4145 }},
4146 }
4147 },
4148 RPCExamples{
4149 "\nList the unspent transactions\n"
4150 + HelpExampleCli("listunspent", "") +
4151 "\nLock an unspent transaction\n"
4152 + HelpExampleCli("lockunspent", "false \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") +
4153 "\nList the locked transactions\n"
4154 + HelpExampleCli("listlockunspent", "") +
4155 "\nUnlock the transaction again\n"
4156 + HelpExampleCli("lockunspent", "true \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") +
4157 "\nAs a JSON-RPC call\n"
4158 + HelpExampleRpc("listlockunspent", "")
4159 },
4160 }.Check(request);
4161
4162 auto locked_chain = pwallet->chain().lock();
4163 LOCK(pwallet->cs_wallet);
4164
4165 std::vector<COutPoint> vOutpts;
4166 pwallet->ListLockedCoins(vOutpts);
4167
4168 UniValue ret(UniValue::VARR);
4169
4170 for (const COutPoint& outpt : vOutpts) {
4171 UniValue o(UniValue::VOBJ);
4172
4173 o.pushKV("txid", outpt.hash.GetHex());
4174 o.pushKV("vout", (int)outpt.n);
4175 ret.push_back(o);
4176 }
4177
4178 return ret;
4179 }
4180
settxfee(const JSONRPCRequest & request)4181 static UniValue settxfee(const JSONRPCRequest& request)
4182 {
4183 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
4184 CWallet* const pwallet = wallet.get();
4185
4186 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
4187 return NullUniValue;
4188 }
4189
4190 RPCHelpMan{"settxfee",
4191 "\nSet the transaction fee per kB for this wallet. Overrides the global -paytxfee command line parameter.\n",
4192 {
4193 {"amount", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "The transaction fee in " + CURRENCY_UNIT + "/kB"},
4194 },
4195 RPCResult{
4196 RPCResult::Type::BOOL, "", "Returns true if successful"
4197 },
4198 RPCExamples{
4199 HelpExampleCli("settxfee", "0.00001")
4200 + HelpExampleRpc("settxfee", "0.00001")
4201 },
4202 }.Check(request);
4203
4204 auto locked_chain = pwallet->chain().lock();
4205 LOCK(pwallet->cs_wallet);
4206
4207 CAmount nAmount = AmountFromValue(request.params[0]);
4208 CFeeRate tx_fee_rate(nAmount, 1000);
4209 if (tx_fee_rate == CFeeRate(0)) {
4210 // automatic selection
4211 } else if (tx_fee_rate < pwallet->chain().relayMinFee()) {
4212 throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("txfee cannot be less than min relay tx fee (%s)", pwallet->chain().relayMinFee().ToString()));
4213 } else if (tx_fee_rate < pwallet->m_min_fee) {
4214 throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("txfee cannot be less than wallet min fee (%s)", pwallet->m_min_fee.ToString()));
4215 }
4216
4217 pwallet->m_pay_tx_fee = tx_fee_rate;
4218 return true;
4219 }
4220
getbalances(const JSONRPCRequest & request)4221 static UniValue getbalances(const JSONRPCRequest& request)
4222 {
4223 std::shared_ptr<CWallet> const rpc_wallet = GetWalletForJSONRPCRequest(request);
4224 if (!EnsureWalletIsAvailable(rpc_wallet.get(), request.fHelp)) {
4225 return NullUniValue;
4226 }
4227 CWallet& wallet = *rpc_wallet;
4228
4229 RPCHelpMan{
4230 "getbalances",
4231 "Returns an object with all balances in " + CURRENCY_UNIT + ".\n",
4232 {},
4233 RPCResult{
4234 RPCResult::Type::OBJ, "", "",
4235 {
4236 {RPCResult::Type::OBJ, "mine", "balances from outputs that the wallet can sign",
4237 {
4238 {RPCResult::Type::STR_AMOUNT, "trusted", "trusted balance (outputs created by the wallet or confirmed outputs)"},
4239 {RPCResult::Type::STR_AMOUNT, "untrusted_pending", "untrusted pending balance (outputs created by others that are in the mempool)"},
4240 {RPCResult::Type::STR_AMOUNT, "immature", "balance from immature coinbase outputs"},
4241 {RPCResult::Type::STR_AMOUNT, "used", "(only present if avoid_reuse is set) balance from coins sent to addresses that were previously spent from (potentially privacy violating)"},
4242 }},
4243 {RPCResult::Type::OBJ, "watchonly", "watchonly balances (not present if wallet does not watch anything)",
4244 {
4245 {RPCResult::Type::STR_AMOUNT, "trusted", "trusted balance (outputs created by the wallet or confirmed outputs)"},
4246 {RPCResult::Type::STR_AMOUNT, "untrusted_pending", "untrusted pending balance (outputs created by others that are in the mempool)"},
4247 {RPCResult::Type::STR_AMOUNT, "immature", "balance from immature coinbase outputs"},
4248 }},
4249 }
4250 },
4251 RPCExamples{
4252 HelpExampleCli("getbalances", "") +
4253 HelpExampleRpc("getbalances", "")},
4254 }.Check(request);
4255
4256 // Make sure the results are valid at least up to the most recent block
4257 // the user could have gotten from another RPC command prior to now
4258 wallet.BlockUntilSyncedToCurrentChain();
4259
4260 auto locked_chain = wallet.chain().lock();
4261 LOCK(wallet.cs_wallet);
4262
4263 const auto bal = wallet.GetBalance();
4264 UniValue balances{UniValue::VOBJ};
4265 {
4266 UniValue balances_mine{UniValue::VOBJ};
4267 balances_mine.pushKV("trusted", ValueFromAmount(bal.m_mine_trusted));
4268 balances_mine.pushKV("untrusted_pending", ValueFromAmount(bal.m_mine_untrusted_pending));
4269 balances_mine.pushKV("immature", ValueFromAmount(bal.m_mine_immature));
4270 if (wallet.IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE)) {
4271 // If the AVOID_REUSE flag is set, bal has been set to just the un-reused address balance. Get
4272 // the total balance, and then subtract bal to get the reused address balance.
4273 const auto full_bal = wallet.GetBalance(0, false);
4274 balances_mine.pushKV("used", ValueFromAmount(full_bal.m_mine_trusted + full_bal.m_mine_untrusted_pending - bal.m_mine_trusted - bal.m_mine_untrusted_pending));
4275 }
4276 balances.pushKV("mine", balances_mine);
4277 }
4278 auto spk_man = wallet.GetLegacyScriptPubKeyMan();
4279 if (spk_man && spk_man->HaveWatchOnly()) {
4280 UniValue balances_watchonly{UniValue::VOBJ};
4281 balances_watchonly.pushKV("trusted", ValueFromAmount(bal.m_watchonly_trusted));
4282 balances_watchonly.pushKV("untrusted_pending", ValueFromAmount(bal.m_watchonly_untrusted_pending));
4283 balances_watchonly.pushKV("immature", ValueFromAmount(bal.m_watchonly_immature));
4284 balances.pushKV("watchonly", balances_watchonly);
4285 }
4286 return balances;
4287 }
4288
getwalletinfo(const JSONRPCRequest & request)4289 static UniValue getwalletinfo(const JSONRPCRequest& request)
4290 {
4291 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
4292 const CWallet* const pwallet = wallet.get();
4293
4294 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
4295 return NullUniValue;
4296 }
4297
4298 RPCHelpMan{"getwalletinfo",
4299 "Returns an object containing various wallet state info.\n",
4300 {},
4301 RPCResult{
4302 RPCResult::Type::OBJ, "", "",
4303 {
4304 {
4305 {RPCResult::Type::STR, "walletname", "the wallet name"},
4306 {RPCResult::Type::NUM, "walletversion", "the wallet version"},
4307 {RPCResult::Type::STR_AMOUNT, "balance", "DEPRECATED. Identical to getbalances().mine.trusted"},
4308 {RPCResult::Type::STR_AMOUNT, "stake", "DEPRECATED. Identical to getbalances().mine.stake"},
4309 {RPCResult::Type::STR_AMOUNT, "unconfirmed_balance", "DEPRECATED. Identical to getbalances().mine.untrusted_pending"},
4310 {RPCResult::Type::STR_AMOUNT, "immature_balance", "DEPRECATED. Identical to getbalances().mine.immature"},
4311 {RPCResult::Type::NUM, "txcount", "the total number of transactions in the wallet"},
4312 {RPCResult::Type::NUM_TIME, "keypoololdest", "the " + UNIX_EPOCH_TIME + " of the oldest pre-generated key in the key pool"},
4313 {RPCResult::Type::NUM, "keypoolsize", "how many new keys are pre-generated (only counts external keys)"},
4314 {RPCResult::Type::NUM, "keypoolsize_hd_internal", "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)"},
4315 {RPCResult::Type::NUM_TIME, "unlocked_until", "the " + UNIX_EPOCH_TIME + " until which the wallet is unlocked for transfers, or 0 if the wallet is locked"},
4316 {RPCResult::Type::STR_AMOUNT, "paytxfee", "the transaction fee configuration, set in " + CURRENCY_UNIT + "/kB"},
4317 {RPCResult::Type::STR_HEX, "hdseedid", /* optional */ true, "the Hash160 of the HD seed (only present when HD is enabled)"},
4318 {RPCResult::Type::BOOL, "private_keys_enabled", "false if privatekeys are disabled for this wallet (enforced watch-only wallet)"},
4319 {RPCResult::Type::BOOL, "avoid_reuse", "whether this wallet tracks clean/dirty coins in terms of reuse"},
4320 {RPCResult::Type::OBJ, "scanning", "current scanning details, or false if no scan is in progress",
4321 {
4322 {RPCResult::Type::NUM, "duration", "elapsed seconds since scan start"},
4323 {RPCResult::Type::NUM, "progress", "scanning progress percentage [0.0, 1.0]"},
4324 }},
4325 }},
4326 },
4327 RPCExamples{
4328 HelpExampleCli("getwalletinfo", "")
4329 + HelpExampleRpc("getwalletinfo", "")
4330 },
4331 }.Check(request);
4332
4333 // Make sure the results are valid at least up to the most recent block
4334 // the user could have gotten from another RPC command prior to now
4335 pwallet->BlockUntilSyncedToCurrentChain();
4336
4337 auto locked_chain = pwallet->chain().lock();
4338 LOCK(pwallet->cs_wallet);
4339
4340 UniValue obj(UniValue::VOBJ);
4341
4342 size_t kpExternalSize = pwallet->KeypoolCountExternalKeys();
4343 const auto bal = pwallet->GetBalance();
4344 obj.pushKV("walletname", pwallet->GetName());
4345 obj.pushKV("walletversion", pwallet->GetVersion());
4346 obj.pushKV("balance", ValueFromAmount(bal.m_mine_trusted));
4347 obj.pushKV("stake", ValueFromAmount(bal.m_mine_stake));
4348 obj.pushKV("unconfirmed_balance", ValueFromAmount(bal.m_mine_untrusted_pending));
4349 obj.pushKV("immature_balance", ValueFromAmount(bal.m_mine_immature));
4350 obj.pushKV("txcount", (int)pwallet->mapWallet.size());
4351 obj.pushKV("keypoololdest", pwallet->GetOldestKeyPoolTime());
4352 obj.pushKV("keypoolsize", (int64_t)kpExternalSize);
4353
4354 LegacyScriptPubKeyMan* spk_man = pwallet->GetLegacyScriptPubKeyMan();
4355 if (spk_man) {
4356 CKeyID seed_id = spk_man->GetHDChain().seed_id;
4357 if (!seed_id.IsNull()) {
4358 obj.pushKV("hdseedid", seed_id.GetHex());
4359 }
4360 }
4361
4362 if (pwallet->CanSupportFeature(FEATURE_HD_SPLIT)) {
4363 obj.pushKV("keypoolsize_hd_internal", (int64_t)(pwallet->GetKeyPoolSize() - kpExternalSize));
4364 }
4365 if (pwallet->IsCrypted()) {
4366 obj.pushKV("unlocked_until", pwallet->nRelockTime);
4367 }
4368 obj.pushKV("paytxfee", ValueFromAmount(pwallet->m_pay_tx_fee.GetFeePerK()));
4369 obj.pushKV("private_keys_enabled", !pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS));
4370 obj.pushKV("avoid_reuse", pwallet->IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE));
4371 if (pwallet->IsScanning()) {
4372 UniValue scanning(UniValue::VOBJ);
4373 scanning.pushKV("duration", pwallet->ScanningDuration() / 1000);
4374 scanning.pushKV("progress", pwallet->ScanningProgress());
4375 obj.pushKV("scanning", scanning);
4376 } else {
4377 obj.pushKV("scanning", false);
4378 }
4379 return obj;
4380 }
4381
listwalletdir(const JSONRPCRequest & request)4382 static UniValue listwalletdir(const JSONRPCRequest& request)
4383 {
4384 RPCHelpMan{"listwalletdir",
4385 "Returns a list of wallets in the wallet directory.\n",
4386 {},
4387 RPCResult{
4388 RPCResult::Type::OBJ, "", "",
4389 {
4390 {RPCResult::Type::ARR, "wallets", "",
4391 {
4392 {RPCResult::Type::OBJ, "", "",
4393 {
4394 {RPCResult::Type::STR, "name", "The wallet name"},
4395 }},
4396 }},
4397 }
4398 },
4399 RPCExamples{
4400 HelpExampleCli("listwalletdir", "")
4401 + HelpExampleRpc("listwalletdir", "")
4402 },
4403 }.Check(request);
4404
4405 UniValue wallets(UniValue::VARR);
4406 for (const auto& path : ListWalletDir()) {
4407 UniValue wallet(UniValue::VOBJ);
4408 wallet.pushKV("name", path.string());
4409 wallets.push_back(wallet);
4410 }
4411
4412 UniValue result(UniValue::VOBJ);
4413 result.pushKV("wallets", wallets);
4414 return result;
4415 }
4416
listwallets(const JSONRPCRequest & request)4417 static UniValue listwallets(const JSONRPCRequest& request)
4418 {
4419 RPCHelpMan{"listwallets",
4420 "Returns a list of currently loaded wallets.\n"
4421 "For full information on the wallet, use \"getwalletinfo\"\n",
4422 {},
4423 RPCResult{
4424 RPCResult::Type::ARR, "", "",
4425 {
4426 {RPCResult::Type::STR, "walletname", "the wallet name"},
4427 }
4428 },
4429 RPCExamples{
4430 HelpExampleCli("listwallets", "")
4431 + HelpExampleRpc("listwallets", "")
4432 },
4433 }.Check(request);
4434
4435 UniValue obj(UniValue::VARR);
4436
4437 for (const std::shared_ptr<CWallet>& wallet : GetWallets()) {
4438 if (!EnsureWalletIsAvailable(wallet.get(), request.fHelp)) {
4439 return NullUniValue;
4440 }
4441
4442 LOCK(wallet->cs_wallet);
4443
4444 obj.push_back(wallet->GetName());
4445 }
4446
4447 return obj;
4448 }
4449
loadwallet(const JSONRPCRequest & request)4450 static UniValue loadwallet(const JSONRPCRequest& request)
4451 {
4452 RPCHelpMan{"loadwallet",
4453 "\nLoads a wallet from a wallet file or directory."
4454 "\nNote that all wallet command-line options used when starting qtumd will be"
4455 "\napplied to the new wallet (eg -zapwallettxes, upgradewallet, rescan, etc).\n",
4456 {
4457 {"filename", RPCArg::Type::STR, RPCArg::Optional::NO, "The wallet directory or .dat file."},
4458 },
4459 RPCResult{
4460 RPCResult::Type::OBJ, "", "",
4461 {
4462 {RPCResult::Type::STR, "name", "The wallet name if loaded successfully."},
4463 {RPCResult::Type::STR, "warning", "Warning message if wallet was not loaded cleanly."},
4464 }
4465 },
4466 RPCExamples{
4467 HelpExampleCli("loadwallet", "\"test.dat\"")
4468 + HelpExampleRpc("loadwallet", "\"test.dat\"")
4469 },
4470 }.Check(request);
4471
4472 WalletLocation location(request.params[0].get_str());
4473
4474 if (!location.Exists()) {
4475 throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Wallet " + location.GetName() + " not found.");
4476 } else if (fs::is_directory(location.GetPath())) {
4477 // The given filename is a directory. Check that there's a wallet.dat file.
4478 fs::path wallet_dat_file = location.GetPath() / "wallet.dat";
4479 if (fs::symlink_status(wallet_dat_file).type() == fs::file_not_found) {
4480 throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Directory " + location.GetName() + " does not contain a wallet.dat file.");
4481 }
4482 }
4483
4484 std::string error;
4485 std::vector<std::string> warning;
4486 std::shared_ptr<CWallet> const wallet = LoadWallet(*g_rpc_chain, location, error, warning);
4487 if (!wallet) throw JSONRPCError(RPC_WALLET_ERROR, error);
4488
4489 UniValue obj(UniValue::VOBJ);
4490 obj.pushKV("name", wallet->GetName());
4491 obj.pushKV("warning", Join(warning, "\n"));
4492
4493 return obj;
4494 }
4495
setwalletflag(const JSONRPCRequest & request)4496 static UniValue setwalletflag(const JSONRPCRequest& request)
4497 {
4498 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
4499 CWallet* const pwallet = wallet.get();
4500
4501 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
4502 return NullUniValue;
4503 }
4504
4505 std::string flags = "";
4506 for (auto& it : WALLET_FLAG_MAP)
4507 if (it.second & MUTABLE_WALLET_FLAGS)
4508 flags += (flags == "" ? "" : ", ") + it.first;
4509 RPCHelpMan{"setwalletflag",
4510 "\nChange the state of the given wallet flag for a wallet.\n",
4511 {
4512 {"flag", RPCArg::Type::STR, RPCArg::Optional::NO, "The name of the flag to change. Current available flags: " + flags},
4513 {"value", RPCArg::Type::BOOL, /* default */ "true", "The new state."},
4514 },
4515 RPCResult{
4516 RPCResult::Type::OBJ, "", "",
4517 {
4518 {RPCResult::Type::STR, "flag_name", "The name of the flag that was modified"},
4519 {RPCResult::Type::BOOL, "flag_state", "The new state of the flag"},
4520 {RPCResult::Type::STR, "warnings", "Any warnings associated with the change"},
4521 }
4522 },
4523 RPCExamples{
4524 HelpExampleCli("setwalletflag", "avoid_reuse")
4525 + HelpExampleRpc("setwalletflag", "\"avoid_reuse\"")
4526 },
4527 }.Check(request);
4528
4529 std::string flag_str = request.params[0].get_str();
4530 bool value = request.params[1].isNull() || request.params[1].get_bool();
4531
4532 if (!WALLET_FLAG_MAP.count(flag_str)) {
4533 throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Unknown wallet flag: %s", flag_str));
4534 }
4535
4536 auto flag = WALLET_FLAG_MAP.at(flag_str);
4537
4538 if (!(flag & MUTABLE_WALLET_FLAGS)) {
4539 throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Wallet flag is immutable: %s", flag_str));
4540 }
4541
4542 UniValue res(UniValue::VOBJ);
4543
4544 if (pwallet->IsWalletFlagSet(flag) == value) {
4545 throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Wallet flag is already set to %s: %s", value ? "true" : "false", flag_str));
4546 }
4547
4548 res.pushKV("flag_name", flag_str);
4549 res.pushKV("flag_state", value);
4550
4551 if (value) {
4552 pwallet->SetWalletFlag(flag);
4553 } else {
4554 pwallet->UnsetWalletFlag(flag);
4555 }
4556
4557 if (flag && value && WALLET_FLAG_CAVEATS.count(flag)) {
4558 res.pushKV("warnings", WALLET_FLAG_CAVEATS.at(flag));
4559 }
4560
4561 return res;
4562 }
4563
createwallet(const JSONRPCRequest & request)4564 static UniValue createwallet(const JSONRPCRequest& request)
4565 {
4566 RPCHelpMan{
4567 "createwallet",
4568 "\nCreates and loads a new wallet.\n",
4569 {
4570 {"wallet_name", RPCArg::Type::STR, RPCArg::Optional::NO, "The name for the new wallet. If this is a path, the wallet will be created at the path location."},
4571 {"disable_private_keys", RPCArg::Type::BOOL, /* default */ "false", "Disable the possibility of private keys (only watchonlys are possible in this mode)."},
4572 {"blank", RPCArg::Type::BOOL, /* default */ "false", "Create a blank wallet. A blank wallet has no keys or HD seed. One can be set using sethdseed."},
4573 {"passphrase", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Encrypt the wallet with this passphrase."},
4574 {"avoid_reuse", RPCArg::Type::BOOL, /* default */ "false", "Keep track of coin reuse, and treat dirty and clean coins differently with privacy considerations in mind."},
4575 },
4576 RPCResult{
4577 RPCResult::Type::OBJ, "", "",
4578 {
4579 {RPCResult::Type::STR, "name", "The wallet name if created successfully. If the wallet was created using a full path, the wallet_name will be the full path."},
4580 {RPCResult::Type::STR, "warning", "Warning message if wallet was not loaded cleanly."},
4581 }
4582 },
4583 RPCExamples{
4584 HelpExampleCli("createwallet", "\"testwallet\"")
4585 + HelpExampleRpc("createwallet", "\"testwallet\"")
4586 },
4587 }.Check(request);
4588
4589 uint64_t flags = 0;
4590 if (!request.params[1].isNull() && request.params[1].get_bool()) {
4591 flags |= WALLET_FLAG_DISABLE_PRIVATE_KEYS;
4592 }
4593
4594 if (!request.params[2].isNull() && request.params[2].get_bool()) {
4595 flags |= WALLET_FLAG_BLANK_WALLET;
4596 }
4597 SecureString passphrase;
4598 passphrase.reserve(100);
4599 std::vector<std::string> warnings;
4600 if (!request.params[3].isNull()) {
4601 passphrase = request.params[3].get_str().c_str();
4602 if (passphrase.empty()) {
4603 // Empty string means unencrypted
4604 warnings.emplace_back("Empty string given as passphrase, wallet will not be encrypted.");
4605 }
4606 }
4607
4608 if (!request.params[4].isNull() && request.params[4].get_bool()) {
4609 flags |= WALLET_FLAG_AVOID_REUSE;
4610 }
4611
4612 std::string error;
4613 std::shared_ptr<CWallet> wallet;
4614 WalletCreationStatus status = CreateWallet(*g_rpc_chain, passphrase, flags, request.params[0].get_str(), error, warnings, wallet);
4615 switch (status) {
4616 case WalletCreationStatus::CREATION_FAILED:
4617 throw JSONRPCError(RPC_WALLET_ERROR, error);
4618 case WalletCreationStatus::ENCRYPTION_FAILED:
4619 throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, error);
4620 case WalletCreationStatus::SUCCESS:
4621 break;
4622 // no default case, so the compiler can warn about missing cases
4623 }
4624
4625 UniValue obj(UniValue::VOBJ);
4626 obj.pushKV("name", wallet->GetName());
4627 obj.pushKV("warning", Join(warnings, "\n"));
4628
4629 return obj;
4630 }
4631
unloadwallet(const JSONRPCRequest & request)4632 static UniValue unloadwallet(const JSONRPCRequest& request)
4633 {
4634 RPCHelpMan{"unloadwallet",
4635 "Unloads the wallet referenced by the request endpoint otherwise unloads the wallet specified in the argument.\n"
4636 "Specifying the wallet name on a wallet endpoint is invalid.",
4637 {
4638 {"wallet_name", RPCArg::Type::STR, /* default */ "the wallet name from the RPC request", "The name of the wallet to unload."},
4639 },
4640 RPCResult{RPCResult::Type::NONE, "", ""},
4641 RPCExamples{
4642 HelpExampleCli("unloadwallet", "wallet_name")
4643 + HelpExampleRpc("unloadwallet", "wallet_name")
4644 },
4645 }.Check(request);
4646
4647 std::string wallet_name;
4648 if (GetWalletNameFromJSONRPCRequest(request, wallet_name)) {
4649 if (!request.params[0].isNull()) {
4650 throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot unload the requested wallet");
4651 }
4652 } else {
4653 wallet_name = request.params[0].get_str();
4654 }
4655
4656 std::shared_ptr<CWallet> wallet = GetWallet(wallet_name);
4657 if (!wallet) {
4658 throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Requested wallet does not exist or is not loaded");
4659 }
4660
4661 // Release the "main" shared pointer and prevent further notifications.
4662 // Note that any attempt to load the same wallet would fail until the wallet
4663 // is destroyed (see CheckUniqueFileid).
4664 if (!RemoveWallet(wallet)) {
4665 throw JSONRPCError(RPC_MISC_ERROR, "Requested wallet already unloaded");
4666 }
4667
4668 UnloadWallet(std::move(wallet));
4669
4670 return NullUniValue;
4671 }
4672
listunspent(const JSONRPCRequest & request)4673 static UniValue listunspent(const JSONRPCRequest& request)
4674 {
4675 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
4676 const CWallet* const pwallet = wallet.get();
4677
4678 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
4679 return NullUniValue;
4680 }
4681
4682 RPCHelpMan{
4683 "listunspent",
4684 "\nReturns array of unspent transaction outputs\n"
4685 "with between minconf and maxconf (inclusive) confirmations.\n"
4686 "Optionally filter to only include txouts paid to specified addresses.\n",
4687 {
4688 {"minconf", RPCArg::Type::NUM, /* default */ "1", "The minimum confirmations to filter"},
4689 {"maxconf", RPCArg::Type::NUM, /* default */ "9999999", "The maximum confirmations to filter"},
4690 {"addresses", RPCArg::Type::ARR, /* default */ "empty array", "A json array of qtum addresses to filter",
4691 {
4692 {"address", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "qtum address"},
4693 },
4694 },
4695 {"include_unsafe", RPCArg::Type::BOOL, /* default */ "true", "Include outputs that are not safe to spend\n"
4696 " See description of \"safe\" attribute below."},
4697 {"query_options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "JSON with query options",
4698 {
4699 {"minimumAmount", RPCArg::Type::AMOUNT, /* default */ "0", "Minimum value of each UTXO in " + CURRENCY_UNIT + ""},
4700 {"maximumAmount", RPCArg::Type::AMOUNT, /* default */ "unlimited", "Maximum value of each UTXO in " + CURRENCY_UNIT + ""},
4701 {"maximumCount", RPCArg::Type::NUM, /* default */ "unlimited", "Maximum number of UTXOs"},
4702 {"minimumSumAmount", RPCArg::Type::AMOUNT, /* default */ "unlimited", "Minimum sum value of all UTXOs in " + CURRENCY_UNIT + ""},
4703 },
4704 "query_options"},
4705 },
4706 RPCResult{
4707 RPCResult::Type::ARR, "", "",
4708 {
4709 {RPCResult::Type::OBJ, "", "",
4710 {
4711 {RPCResult::Type::STR_HEX, "txid", "the transaction id"},
4712 {RPCResult::Type::NUM, "vout", "the vout value"},
4713 {RPCResult::Type::STR, "address", "the qtum address"},
4714 {RPCResult::Type::STR, "label", "The associated label, or \"\" for the default label"},
4715 {RPCResult::Type::STR, "scriptPubKey", "the script key"},
4716 {RPCResult::Type::STR_AMOUNT, "amount", "the transaction output amount in " + CURRENCY_UNIT},
4717 {RPCResult::Type::NUM, "confirmations", "The number of confirmations"},
4718 {RPCResult::Type::STR_HEX, "redeemScript", "The redeemScript if scriptPubKey is P2SH"},
4719 {RPCResult::Type::STR, "witnessScript", "witnessScript if the scriptPubKey is P2WSH or P2SH-P2WSH"},
4720 {RPCResult::Type::BOOL, "spendable", "Whether we have the private keys to spend this output"},
4721 {RPCResult::Type::BOOL, "solvable", "Whether we know how to spend this output, ignoring the lack of keys"},
4722 {RPCResult::Type::BOOL, "reused", "(only present if avoid_reuse is set) Whether this output is reused/dirty (sent to an address that was previously spent from)"},
4723 {RPCResult::Type::STR, "desc", "(only when solvable) A descriptor for spending this output"},
4724 {RPCResult::Type::BOOL, "safe", "Whether this output is considered safe to spend. Unconfirmed transactions\n"
4725 "from outside keys and unconfirmed replacement transactions are considered unsafe\n"
4726 "and are not eligible for spending by fundrawtransaction and sendtoaddress."},
4727 }},
4728 }
4729 },
4730 RPCExamples{
4731 HelpExampleCli("listunspent", "")
4732 + HelpExampleCli("listunspent", "6 9999999 \"[\\\"" + EXAMPLE_ADDRESS[0] + "\\\",\\\"" + EXAMPLE_ADDRESS[1] + "\\\"]\"")
4733 + HelpExampleRpc("listunspent", "6, 9999999 \"[\\\"" + EXAMPLE_ADDRESS[0] + "\\\",\\\"" + EXAMPLE_ADDRESS[1] + "\\\"]\"")
4734 + HelpExampleCli("listunspent", "6 9999999 '[]' true '{ \"minimumAmount\": 0.005 }'")
4735 + HelpExampleRpc("listunspent", "6, 9999999, [] , true, { \"minimumAmount\": 0.005 } ")
4736 },
4737 }.Check(request);
4738
4739 int nMinDepth = 1;
4740 if (!request.params[0].isNull()) {
4741 RPCTypeCheckArgument(request.params[0], UniValue::VNUM);
4742 nMinDepth = request.params[0].get_int();
4743 }
4744
4745 int nMaxDepth = 9999999;
4746 if (!request.params[1].isNull()) {
4747 RPCTypeCheckArgument(request.params[1], UniValue::VNUM);
4748 nMaxDepth = request.params[1].get_int();
4749 }
4750
4751 std::set<CTxDestination> destinations;
4752 if (!request.params[2].isNull()) {
4753 RPCTypeCheckArgument(request.params[2], UniValue::VARR);
4754 UniValue inputs = request.params[2].get_array();
4755 for (unsigned int idx = 0; idx < inputs.size(); idx++) {
4756 const UniValue& input = inputs[idx];
4757 CTxDestination dest = DecodeDestination(input.get_str());
4758 if (!IsValidDestination(dest)) {
4759 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Qtum address: ") + input.get_str());
4760 }
4761 if (!destinations.insert(dest).second) {
4762 throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ") + input.get_str());
4763 }
4764 }
4765 }
4766
4767 bool include_unsafe = true;
4768 if (!request.params[3].isNull()) {
4769 RPCTypeCheckArgument(request.params[3], UniValue::VBOOL);
4770 include_unsafe = request.params[3].get_bool();
4771 }
4772
4773 CAmount nMinimumAmount = 0;
4774 CAmount nMaximumAmount = MAX_MONEY;
4775 CAmount nMinimumSumAmount = MAX_MONEY;
4776 uint64_t nMaximumCount = 0;
4777
4778 if (!request.params[4].isNull()) {
4779 const UniValue& options = request.params[4].get_obj();
4780
4781 if (options.exists("minimumAmount"))
4782 nMinimumAmount = AmountFromValue(options["minimumAmount"]);
4783
4784 if (options.exists("maximumAmount"))
4785 nMaximumAmount = AmountFromValue(options["maximumAmount"]);
4786
4787 if (options.exists("minimumSumAmount"))
4788 nMinimumSumAmount = AmountFromValue(options["minimumSumAmount"]);
4789
4790 if (options.exists("maximumCount"))
4791 nMaximumCount = options["maximumCount"].get_int64();
4792 }
4793
4794 // Make sure the results are valid at least up to the most recent block
4795 // the user could have gotten from another RPC command prior to now
4796 pwallet->BlockUntilSyncedToCurrentChain();
4797
4798 UniValue results(UniValue::VARR);
4799 std::vector<COutput> vecOutputs;
4800 {
4801 CCoinControl cctl;
4802 cctl.m_avoid_address_reuse = false;
4803 cctl.m_min_depth = nMinDepth;
4804 cctl.m_max_depth = nMaxDepth;
4805 auto locked_chain = pwallet->chain().lock();
4806 LOCK(pwallet->cs_wallet);
4807 pwallet->AvailableCoins(*locked_chain, vecOutputs, !include_unsafe, &cctl, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount);
4808 }
4809
4810 LOCK(pwallet->cs_wallet);
4811
4812 const bool avoid_reuse = pwallet->IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE);
4813
4814 for (const COutput& out : vecOutputs) {
4815 CTxDestination address;
4816 const CScript& scriptPubKey = out.tx->tx->vout[out.i].scriptPubKey;
4817 bool fValidAddress = ExtractDestination(scriptPubKey, address);
4818 bool reused = avoid_reuse && pwallet->IsSpentKey(out.tx->GetHash(), out.i);
4819
4820 if (destinations.size() && (!fValidAddress || !destinations.count(address)))
4821 continue;
4822
4823 UniValue entry(UniValue::VOBJ);
4824 entry.pushKV("txid", out.tx->GetHash().GetHex());
4825 entry.pushKV("vout", out.i);
4826
4827 if (fValidAddress) {
4828 entry.pushKV("address", EncodeDestination(address));
4829
4830 const auto* address_book_entry = pwallet->FindAddressBookEntry(address);
4831 if (address_book_entry) {
4832 entry.pushKV("label", address_book_entry->GetLabel());
4833 }
4834
4835 std::unique_ptr<SigningProvider> provider = pwallet->GetSolvingProvider(scriptPubKey);
4836 if (provider) {
4837 if (scriptPubKey.IsPayToScriptHash()) {
4838 const CScriptID& hash = CScriptID(boost::get<ScriptHash>(address));
4839 CScript redeemScript;
4840 if (provider->GetCScript(hash, redeemScript)) {
4841 entry.pushKV("redeemScript", HexStr(redeemScript.begin(), redeemScript.end()));
4842 // Now check if the redeemScript is actually a P2WSH script
4843 CTxDestination witness_destination;
4844 if (redeemScript.IsPayToWitnessScriptHash()) {
4845 bool extracted = ExtractDestination(redeemScript, witness_destination);
4846 CHECK_NONFATAL(extracted);
4847 // Also return the witness script
4848 const WitnessV0ScriptHash& whash = boost::get<WitnessV0ScriptHash>(witness_destination);
4849 CScriptID id;
4850 CRIPEMD160().Write(whash.begin(), whash.size()).Finalize(id.begin());
4851 CScript witnessScript;
4852 if (provider->GetCScript(id, witnessScript)) {
4853 entry.pushKV("witnessScript", HexStr(witnessScript.begin(), witnessScript.end()));
4854 }
4855 }
4856 }
4857 } else if (scriptPubKey.IsPayToWitnessScriptHash()) {
4858 const WitnessV0ScriptHash& whash = boost::get<WitnessV0ScriptHash>(address);
4859 CScriptID id;
4860 CRIPEMD160().Write(whash.begin(), whash.size()).Finalize(id.begin());
4861 CScript witnessScript;
4862 if (provider->GetCScript(id, witnessScript)) {
4863 entry.pushKV("witnessScript", HexStr(witnessScript.begin(), witnessScript.end()));
4864 }
4865 }
4866 }
4867 }
4868
4869 entry.pushKV("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end()));
4870 entry.pushKV("amount", ValueFromAmount(out.tx->tx->vout[out.i].nValue));
4871 entry.pushKV("confirmations", out.nDepth);
4872 entry.pushKV("spendable", out.fSpendable);
4873 entry.pushKV("solvable", out.fSolvable);
4874 if (out.fSolvable) {
4875 std::unique_ptr<SigningProvider> provider = pwallet->GetSolvingProvider(scriptPubKey);
4876 if (provider) {
4877 auto descriptor = InferDescriptor(scriptPubKey, *provider);
4878 entry.pushKV("desc", descriptor->ToString());
4879 }
4880 }
4881 if (avoid_reuse) entry.pushKV("reused", reused);
4882 entry.pushKV("safe", out.fSafe);
4883 results.push_back(entry);
4884 }
4885
4886 return results;
4887 }
4888
FundTransaction(CWallet * const pwallet,CMutableTransaction & tx,CAmount & fee_out,int & change_position,UniValue options)4889 void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& fee_out, int& change_position, UniValue options)
4890 {
4891 // Make sure the results are valid at least up to the most recent block
4892 // the user could have gotten from another RPC command prior to now
4893 pwallet->BlockUntilSyncedToCurrentChain();
4894
4895 CCoinControl coinControl;
4896 change_position = -1;
4897 bool lockUnspents = false;
4898 UniValue subtractFeeFromOutputs;
4899 std::set<int> setSubtractFeeFromOutputs;
4900
4901 if (!options.isNull()) {
4902 if (options.type() == UniValue::VBOOL) {
4903 // backward compatibility bool only fallback
4904 coinControl.fAllowWatchOnly = options.get_bool();
4905 }
4906 else {
4907 RPCTypeCheckArgument(options, UniValue::VOBJ);
4908 RPCTypeCheckObj(options,
4909 {
4910 {"changeAddress", UniValueType(UniValue::VSTR)},
4911 {"changePosition", UniValueType(UniValue::VNUM)},
4912 {"change_type", UniValueType(UniValue::VSTR)},
4913 {"includeWatching", UniValueType(UniValue::VBOOL)},
4914 {"lockUnspents", UniValueType(UniValue::VBOOL)},
4915 {"feeRate", UniValueType()}, // will be checked below
4916 {"subtractFeeFromOutputs", UniValueType(UniValue::VARR)},
4917 {"replaceable", UniValueType(UniValue::VBOOL)},
4918 {"conf_target", UniValueType(UniValue::VNUM)},
4919 {"estimate_mode", UniValueType(UniValue::VSTR)},
4920 },
4921 true, true);
4922
4923 if (options.exists("changeAddress")) {
4924 CTxDestination dest = DecodeDestination(options["changeAddress"].get_str());
4925
4926 if (!IsValidDestination(dest)) {
4927 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "changeAddress must be a valid qtum address");
4928 }
4929
4930 coinControl.destChange = dest;
4931 }
4932
4933 if (options.exists("changePosition"))
4934 change_position = options["changePosition"].get_int();
4935
4936 if (options.exists("change_type")) {
4937 if (options.exists("changeAddress")) {
4938 throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both changeAddress and address_type options");
4939 }
4940 coinControl.m_change_type = pwallet->m_default_change_type;
4941 if (!ParseOutputType(options["change_type"].get_str(), *coinControl.m_change_type)) {
4942 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown change type '%s'", options["change_type"].get_str()));
4943 }
4944 }
4945
4946 coinControl.fAllowWatchOnly = ParseIncludeWatchonly(options["includeWatching"], *pwallet);
4947
4948 if (options.exists("lockUnspents"))
4949 lockUnspents = options["lockUnspents"].get_bool();
4950
4951 if (options.exists("feeRate"))
4952 {
4953 coinControl.m_feerate = CFeeRate(AmountFromValue(options["feeRate"]));
4954 coinControl.fOverrideFeeRate = true;
4955 }
4956
4957 if (options.exists("subtractFeeFromOutputs"))
4958 subtractFeeFromOutputs = options["subtractFeeFromOutputs"].get_array();
4959
4960 if (options.exists("replaceable")) {
4961 coinControl.m_signal_bip125_rbf = options["replaceable"].get_bool();
4962 }
4963 if (options.exists("conf_target")) {
4964 if (options.exists("feeRate")) {
4965 throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both conf_target and feeRate");
4966 }
4967 coinControl.m_confirm_target = ParseConfirmTarget(options["conf_target"], pwallet->chain().estimateMaxBlocks());
4968 }
4969 if (options.exists("estimate_mode")) {
4970 if (options.exists("feeRate")) {
4971 throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both estimate_mode and feeRate");
4972 }
4973 if (!FeeModeFromString(options["estimate_mode"].get_str(), coinControl.m_fee_mode)) {
4974 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid estimate_mode parameter");
4975 }
4976 }
4977 }
4978 } else {
4979 // if options is null and not a bool
4980 coinControl.fAllowWatchOnly = ParseIncludeWatchonly(NullUniValue, *pwallet);
4981 }
4982
4983 if (tx.vout.size() == 0)
4984 throw JSONRPCError(RPC_INVALID_PARAMETER, "TX must have at least one output");
4985
4986 if (change_position != -1 && (change_position < 0 || (unsigned int)change_position > tx.vout.size()))
4987 throw JSONRPCError(RPC_INVALID_PARAMETER, "changePosition out of bounds");
4988
4989 for (unsigned int idx = 0; idx < subtractFeeFromOutputs.size(); idx++) {
4990 int pos = subtractFeeFromOutputs[idx].get_int();
4991 if (setSubtractFeeFromOutputs.count(pos))
4992 throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid parameter, duplicated position: %d", pos));
4993 if (pos < 0)
4994 throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid parameter, negative position: %d", pos));
4995 if (pos >= int(tx.vout.size()))
4996 throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid parameter, position too large: %d", pos));
4997 setSubtractFeeFromOutputs.insert(pos);
4998 }
4999
5000 std::string strFailReason;
5001
5002 if (!pwallet->FundTransaction(tx, fee_out, change_position, strFailReason, lockUnspents, setSubtractFeeFromOutputs, coinControl)) {
5003 throw JSONRPCError(RPC_WALLET_ERROR, strFailReason);
5004 }
5005 }
5006
fundrawtransaction(const JSONRPCRequest & request)5007 static UniValue fundrawtransaction(const JSONRPCRequest& request)
5008 {
5009 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
5010 CWallet* const pwallet = wallet.get();
5011
5012 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
5013 return NullUniValue;
5014 }
5015
5016 RPCHelpMan{"fundrawtransaction",
5017 "\nAdd inputs to a transaction until it has enough in value to meet its out value.\n"
5018 "This will not modify existing inputs, and will add at most one change output to the outputs.\n"
5019 "No existing outputs will be modified unless \"subtractFeeFromOutputs\" is specified.\n"
5020 "Note that inputs which were signed may need to be resigned after completion since in/outputs have been added.\n"
5021 "The inputs added will not be signed, use signrawtransactionwithkey\n"
5022 " or signrawtransactionwithwallet for that.\n"
5023 "Note that all existing inputs must have their previous output transaction be in the wallet.\n"
5024 "Note that all inputs selected must be of standard form and P2SH scripts must be\n"
5025 "in the wallet using importaddress or addmultisigaddress (to calculate fees).\n"
5026 "You can see whether this is the case by checking the \"solvable\" field in the listunspent output.\n"
5027 "Only pay-to-pubkey, multisig, and P2SH versions thereof are currently supported for watch-only\n",
5028 {
5029 {"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex string of the raw transaction"},
5030 {"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "for backward compatibility: passing in a true instead of an object will result in {\"includeWatching\":true}",
5031 {
5032 {"changeAddress", RPCArg::Type::STR, /* default */ "pool address", "The qtum address to receive the change"},
5033 {"changePosition", RPCArg::Type::NUM, /* default */ "random", "The index of the change output"},
5034 {"change_type", RPCArg::Type::STR, /* default */ "set by -changetype", "The output type to use. Only valid if changeAddress is not specified. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
5035 {"includeWatching", RPCArg::Type::BOOL, /* default */ "true for watch-only wallets, otherwise false", "Also select inputs which are watch only.\n"
5036 "Only solvable inputs can be used. Watch-only destinations are solvable if the public key and/or output script was imported,\n"
5037 "e.g. with 'importpubkey' or 'importmulti' with the 'pubkeys' or 'desc' field."},
5038 {"lockUnspents", RPCArg::Type::BOOL, /* default */ "false", "Lock selected unspent outputs"},
5039 {"feeRate", RPCArg::Type::AMOUNT, /* default */ "not set: makes wallet determine the fee", "Set a specific fee rate in " + CURRENCY_UNIT + "/kB"},
5040 {"subtractFeeFromOutputs", RPCArg::Type::ARR, /* default */ "empty array", "The integers.\n"
5041 " The fee will be equally deducted from the amount of each specified output.\n"
5042 " Those recipients will receive less qtums than you enter in their corresponding amount field.\n"
5043 " If no outputs are specified here, the sender pays the fee.",
5044 {
5045 {"vout_index", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "The zero-based output index, before a change output is added."},
5046 },
5047 },
5048 {"replaceable", RPCArg::Type::BOOL, /* default */ "wallet default", "Marks this transaction as BIP125 replaceable.\n"
5049 " Allows this transaction to be replaced by a transaction with higher fees"},
5050 {"conf_target", RPCArg::Type::NUM, /* default */ "wallet default", "Confirmation target (in blocks)"},
5051 {"estimate_mode", RPCArg::Type::STR, /* default */ "UNSET", "The fee estimate mode, must be one of:\n"
5052 " \"UNSET\"\n"
5053 " \"ECONOMICAL\"\n"
5054 " \"CONSERVATIVE\""},
5055 },
5056 "options"},
5057 {"iswitness", RPCArg::Type::BOOL, /* default */ "depends on heuristic tests", "Whether the transaction hex is a serialized witness transaction.\n"
5058 "If iswitness is not present, heuristic tests will be used in decoding.\n"
5059 "If true, only witness deserialization will be tried.\n"
5060 "If false, only non-witness deserialization will be tried.\n"
5061 "This boolean should reflect whether the transaction has inputs\n"
5062 "(e.g. fully valid, or on-chain transactions), if known by the caller."
5063 },
5064 },
5065 RPCResult{
5066 RPCResult::Type::OBJ, "", "",
5067 {
5068 {RPCResult::Type::STR_HEX, "hex", "The resulting raw transaction (hex-encoded string)"},
5069 {RPCResult::Type::STR_AMOUNT, "fee", "Fee in " + CURRENCY_UNIT + " the resulting transaction pays"},
5070 {RPCResult::Type::NUM, "changepos", "The position of the added change output, or -1"},
5071 }
5072 },
5073 RPCExamples{
5074 "\nCreate a transaction with no inputs\n"
5075 + HelpExampleCli("createrawtransaction", "\"[]\" \"{\\\"myaddress\\\":0.01}\"") +
5076 "\nAdd sufficient unsigned inputs to meet the output value\n"
5077 + HelpExampleCli("fundrawtransaction", "\"rawtransactionhex\"") +
5078 "\nSign the transaction\n"
5079 + HelpExampleCli("signrawtransactionwithwallet", "\"fundedtransactionhex\"") +
5080 "\nSend the transaction\n"
5081 + HelpExampleCli("sendrawtransaction", "\"signedtransactionhex\"")
5082 },
5083 }.Check(request);
5084
5085 RPCTypeCheck(request.params, {UniValue::VSTR, UniValueType(), UniValue::VBOOL});
5086
5087 // parse hex string from parameter
5088 CMutableTransaction tx;
5089 bool try_witness = request.params[2].isNull() ? true : request.params[2].get_bool();
5090 bool try_no_witness = request.params[2].isNull() ? true : !request.params[2].get_bool();
5091 if (!DecodeHexTx(tx, request.params[0].get_str(), try_no_witness, try_witness)) {
5092 throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
5093 }
5094
5095 CAmount fee;
5096 int change_position;
5097 FundTransaction(pwallet, tx, fee, change_position, request.params[1]);
5098
5099 UniValue result(UniValue::VOBJ);
5100 result.pushKV("hex", EncodeHexTx(CTransaction(tx)));
5101 result.pushKV("fee", ValueFromAmount(fee));
5102 result.pushKV("changepos", change_position);
5103
5104 return result;
5105 }
5106
signrawtransactionwithwallet(const JSONRPCRequest & request)5107 UniValue signrawtransactionwithwallet(const JSONRPCRequest& request)
5108 {
5109 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
5110 const CWallet* const pwallet = wallet.get();
5111
5112 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
5113 return NullUniValue;
5114 }
5115
5116 RPCHelpMan{"signrawtransactionwithwallet",
5117 "\nSign inputs for raw transaction (serialized, hex-encoded).\n"
5118 "The second optional argument (may be null) is an array of previous transaction outputs that\n"
5119 "this transaction depends on but may not yet be in the block chain." +
5120 HELP_REQUIRING_PASSPHRASE,
5121 {
5122 {"hexstring", RPCArg::Type::STR, RPCArg::Optional::NO, "The transaction hex string"},
5123 {"prevtxs", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "The previous dependent transaction outputs",
5124 {
5125 {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
5126 {
5127 {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
5128 {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
5129 {"scriptPubKey", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "script key"},
5130 {"redeemScript", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "(required for P2SH) redeem script"},
5131 {"witnessScript", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "(required for P2WSH or P2SH-P2WSH) witness script"},
5132 {"amount", RPCArg::Type::AMOUNT, RPCArg::Optional::OMITTED, "(required for Segwit inputs) the amount spent"},
5133 },
5134 },
5135 },
5136 },
5137 {"sighashtype", RPCArg::Type::STR, /* default */ "ALL", "The signature hash type. Must be one of\n"
5138 " \"ALL\"\n"
5139 " \"NONE\"\n"
5140 " \"SINGLE\"\n"
5141 " \"ALL|ANYONECANPAY\"\n"
5142 " \"NONE|ANYONECANPAY\"\n"
5143 " \"SINGLE|ANYONECANPAY\""},
5144 },
5145 RPCResult{
5146 RPCResult::Type::OBJ, "", "",
5147 {
5148 {RPCResult::Type::STR_HEX, "hex", "The hex-encoded raw transaction with signature(s)"},
5149 {RPCResult::Type::BOOL, "complete", "If the transaction has a complete set of signatures"},
5150 {RPCResult::Type::ARR, "errors", "Script verification errors (if there are any)",
5151 {
5152 {RPCResult::Type::OBJ, "", "",
5153 {
5154 {RPCResult::Type::STR_HEX, "txid", "The hash of the referenced, previous transaction"},
5155 {RPCResult::Type::NUM, "vout", "The index of the output to spent and used as input"},
5156 {RPCResult::Type::STR_HEX, "scriptSig", "The hex-encoded signature script"},
5157 {RPCResult::Type::NUM, "sequence", "Script sequence number"},
5158 {RPCResult::Type::STR, "error", "Verification or signing error related to the input"},
5159 }},
5160 }},
5161 }
5162 },
5163 RPCExamples{
5164 HelpExampleCli("signrawtransactionwithwallet", "\"myhex\"")
5165 + HelpExampleRpc("signrawtransactionwithwallet", "\"myhex\"")
5166 },
5167 }.Check(request);
5168
5169 RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR, UniValue::VSTR}, true);
5170
5171 CMutableTransaction mtx;
5172 if (!DecodeHexTx(mtx, request.params[0].get_str(), true)) {
5173 throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
5174 }
5175
5176 // Sign the transaction
5177 auto locked_chain = pwallet->chain().lock();
5178 LOCK(pwallet->cs_wallet);
5179 EnsureWalletIsUnlocked(pwallet);
5180
5181 // Fetch previous transactions (inputs):
5182 std::map<COutPoint, Coin> coins;
5183 for (const CTxIn& txin : mtx.vin) {
5184 coins[txin.prevout]; // Create empty map entry keyed by prevout.
5185 }
5186 pwallet->chain().findCoins(coins);
5187
5188 // Parse the prevtxs array
5189 ParsePrevouts(request.params[1], nullptr, coins);
5190
5191 int nHashType = ParseSighashString(request.params[2]);
5192
5193 // Script verification errors
5194 std::map<int, std::string> input_errors;
5195
5196 CheckSenderSignatures(mtx);
5197 bool complete = pwallet->SignTransaction(mtx, coins, nHashType, input_errors);
5198 UniValue result(UniValue::VOBJ);
5199 SignTransactionResultToJSON(mtx, complete, coins, input_errors, result);
5200 return result;
5201 }
5202
signrawsendertransactionwithwallet(const JSONRPCRequest & request)5203 UniValue signrawsendertransactionwithwallet(const JSONRPCRequest& request)
5204 {
5205 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
5206 CWallet* const pwallet = wallet.get();
5207
5208 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
5209 return NullUniValue;
5210 }
5211
5212 if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
5213 throw std::runtime_error(
5214 RPCHelpMan{"signrawsendertransactionwithwallet",
5215 "\nSign OP_SENDER outputs for raw transaction (serialized, hex-encoded).\n" +
5216 HELP_REQUIRING_PASSPHRASE,
5217 {
5218 {"hexstring", RPCArg::Type::STR, RPCArg::Optional::NO, "The transaction hex string"},
5219 {"sighashtype", RPCArg::Type::STR, /* default */ "ALL", "The signature hash type. Must be one of\n"
5220 " \"ALL\"\n"
5221 " \"NONE\"\n"
5222 " \"SINGLE\"\n"
5223 " \"ALL|ANYONECANPAY\"\n"
5224 " \"NONE|ANYONECANPAY\"\n"
5225 " \"SINGLE|ANYONECANPAY\""},
5226 },
5227 RPCResult{
5228 RPCResult::Type::OBJ, "", "",
5229 {
5230 {RPCResult::Type::STR_HEX, "hex", "The hex-encoded raw transaction with signature(s)"},
5231 {RPCResult::Type::BOOL, "complete", "If the transaction has a complete set of signatures"},
5232 {RPCResult::Type::ARR, "errors", "Script verification errors (if there are any)",
5233 {
5234 {RPCResult::Type::OBJ, "", "",
5235 {
5236 {RPCResult::Type::STR_AMOUNT, "amount", "The amount of the output"},
5237 {RPCResult::Type::STR_HEX, "scriptPubKey", "The hex-encoded public key script of the output"},
5238 {RPCResult::Type::STR, "error", "Verification or signing error related to the output"},
5239 }},
5240 }},
5241 }
5242 },
5243 RPCExamples{
5244 HelpExampleCli("signrawsendertransactionwithwallet", "\"myhex\"")
5245 + HelpExampleRpc("signrawsendertransactionwithwallet", "\"myhex\"")
5246 },
5247 }.ToString());
5248
5249 RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VSTR}, true);
5250
5251 CMutableTransaction mtx;
5252 if (!DecodeHexTx(mtx, request.params[0].get_str(), true)) {
5253 throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
5254 }
5255
5256 // Sign the transaction
5257 LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*pwallet);
5258 auto locked_chain = pwallet->chain().lock();
5259 LOCK2(pwallet->cs_wallet, spk_man.cs_KeyStore);
5260 EnsureWalletIsUnlocked(pwallet);
5261
5262 return SignTransactionSender(mtx, &spk_man, request.params[1]);
5263 }
5264
bumpfee(const JSONRPCRequest & request)5265 static UniValue bumpfee(const JSONRPCRequest& request)
5266 {
5267 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
5268 CWallet* const pwallet = wallet.get();
5269
5270
5271 if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
5272 return NullUniValue;
5273
5274 RPCHelpMan{"bumpfee",
5275 "\nBumps the fee of an opt-in-RBF transaction T, replacing it with a new transaction B.\n"
5276 "An opt-in RBF transaction with the given txid must be in the wallet.\n"
5277 "The command will pay the additional fee by reducing change outputs or adding inputs when necessary. It may add a new change output if one does not already exist.\n"
5278 "All inputs in the original transaction will be included in the replacement transaction.\n"
5279 "The command will fail if the wallet or mempool contains a transaction that spends one of T's outputs.\n"
5280 "By default, the new fee will be calculated automatically using estimatesmartfee.\n"
5281 "The user can specify a confirmation target for estimatesmartfee.\n"
5282 "Alternatively, the user can specify a fee_rate (" + CURRENCY_UNIT + " per kB) for the new transaction.\n"
5283 "At a minimum, the new fee rate must be high enough to pay an additional new relay fee (incrementalfee\n"
5284 "returned by getnetworkinfo) to enter the node's mempool.\n",
5285 {
5286 {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The txid to be bumped"},
5287 {"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "",
5288 {
5289 {"confTarget", RPCArg::Type::NUM, /* default */ "wallet default", "Confirmation target (in blocks)"},
5290 {"fee_rate", RPCArg::Type::NUM, /* default */ "fall back to 'confTarget'", "fee rate (NOT total fee) to pay, in " + CURRENCY_UNIT + " per kB\n"
5291 " Specify a fee rate instead of relying on the built-in fee estimator.\n"
5292 "Must be at least 0.0001 " + CURRENCY_UNIT + " per kB higher than the current transaction fee rate.\n"},
5293 {"replaceable", RPCArg::Type::BOOL, /* default */ "true", "Whether the new transaction should still be\n"
5294 " marked bip-125 replaceable. If true, the sequence numbers in the transaction will\n"
5295 " be left unchanged from the original. If false, any input sequence numbers in the\n"
5296 " original transaction that were less than 0xfffffffe will be increased to 0xfffffffe\n"
5297 " so the new transaction will not be explicitly bip-125 replaceable (though it may\n"
5298 " still be replaceable in practice, for example if it has unconfirmed ancestors which\n"
5299 " are replaceable)."},
5300 {"estimate_mode", RPCArg::Type::STR, /* default */ "UNSET", "The fee estimate mode, must be one of:\n"
5301 " \"UNSET\"\n"
5302 " \"ECONOMICAL\"\n"
5303 " \"CONSERVATIVE\""},
5304 },
5305 "options"},
5306 },
5307 RPCResult{
5308 RPCResult::Type::OBJ, "", "", {
5309 {RPCResult::Type::STR, "psbt", "The base64-encoded unsigned PSBT of the new transaction. Only returned when wallet private keys are disabled."},
5310 {RPCResult::Type::STR_HEX, "txid", "The id of the new transaction. Only returned when wallet private keys are enabled."},
5311 {RPCResult::Type::STR_AMOUNT, "origfee", "The fee of the replaced transaction."},
5312 {RPCResult::Type::STR_AMOUNT, "fee", "The fee of the new transaction."},
5313 {RPCResult::Type::ARR, "errors", "Errors encountered during processing (may be empty).",
5314 {
5315 {RPCResult::Type::STR, "", ""},
5316 }},
5317 }
5318 },
5319 RPCExamples{
5320 "\nBump the fee, get the new transaction\'s txid\n" +
5321 HelpExampleCli("bumpfee", "<txid>")
5322 },
5323 }.Check(request);
5324
5325 RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VOBJ});
5326 uint256 hash(ParseHashV(request.params[0], "txid"));
5327
5328 CCoinControl coin_control;
5329 coin_control.fAllowWatchOnly = pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
5330 // optional parameters
5331 coin_control.m_signal_bip125_rbf = true;
5332
5333 if (!request.params[1].isNull()) {
5334 UniValue options = request.params[1];
5335 RPCTypeCheckObj(options,
5336 {
5337 {"confTarget", UniValueType(UniValue::VNUM)},
5338 {"fee_rate", UniValueType(UniValue::VNUM)},
5339 {"replaceable", UniValueType(UniValue::VBOOL)},
5340 {"estimate_mode", UniValueType(UniValue::VSTR)},
5341 },
5342 true, true);
5343 if (options.exists("confTarget") && options.exists("fee_rate")) {
5344 throw JSONRPCError(RPC_INVALID_PARAMETER, "confTarget can't be set with fee_rate. Please provide either a confirmation target in blocks for automatic fee estimation, or an explicit fee rate.");
5345 } else if (options.exists("confTarget")) { // TODO: alias this to conf_target
5346 coin_control.m_confirm_target = ParseConfirmTarget(options["confTarget"], pwallet->chain().estimateMaxBlocks());
5347 } else if (options.exists("fee_rate")) {
5348 CFeeRate fee_rate(AmountFromValue(options["fee_rate"]));
5349 if (fee_rate <= CFeeRate(0)) {
5350 throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid fee_rate %s (must be greater than 0)", fee_rate.ToString()));
5351 }
5352 coin_control.m_feerate = fee_rate;
5353 }
5354
5355 if (options.exists("replaceable")) {
5356 coin_control.m_signal_bip125_rbf = options["replaceable"].get_bool();
5357 }
5358 if (options.exists("estimate_mode")) {
5359 if (!FeeModeFromString(options["estimate_mode"].get_str(), coin_control.m_fee_mode)) {
5360 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid estimate_mode parameter");
5361 }
5362 }
5363 }
5364
5365 // Make sure the results are valid at least up to the most recent block
5366 // the user could have gotten from another RPC command prior to now
5367 pwallet->BlockUntilSyncedToCurrentChain();
5368
5369 auto locked_chain = pwallet->chain().lock();
5370 LOCK(pwallet->cs_wallet);
5371 EnsureWalletIsUnlocked(pwallet);
5372
5373
5374 std::vector<std::string> errors;
5375 CAmount old_fee;
5376 CAmount new_fee;
5377 CMutableTransaction mtx;
5378 feebumper::Result res;
5379 // Targeting feerate bump.
5380 res = feebumper::CreateRateBumpTransaction(*pwallet, hash, coin_control, errors, old_fee, new_fee, mtx);
5381 if (res != feebumper::Result::OK) {
5382 switch(res) {
5383 case feebumper::Result::INVALID_ADDRESS_OR_KEY:
5384 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, errors[0]);
5385 break;
5386 case feebumper::Result::INVALID_REQUEST:
5387 throw JSONRPCError(RPC_INVALID_REQUEST, errors[0]);
5388 break;
5389 case feebumper::Result::INVALID_PARAMETER:
5390 throw JSONRPCError(RPC_INVALID_PARAMETER, errors[0]);
5391 break;
5392 case feebumper::Result::WALLET_ERROR:
5393 throw JSONRPCError(RPC_WALLET_ERROR, errors[0]);
5394 break;
5395 default:
5396 throw JSONRPCError(RPC_MISC_ERROR, errors[0]);
5397 break;
5398 }
5399 }
5400
5401 UniValue result(UniValue::VOBJ);
5402
5403 // If wallet private keys are enabled, return the new transaction id,
5404 // otherwise return the base64-encoded unsigned PSBT of the new transaction.
5405 if (!pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
5406 if (!feebumper::SignTransaction(*pwallet, mtx)) {
5407 throw JSONRPCError(RPC_WALLET_ERROR, "Can't sign transaction.");
5408 }
5409
5410 uint256 txid;
5411 if (feebumper::CommitTransaction(*pwallet, hash, std::move(mtx), errors, txid) != feebumper::Result::OK) {
5412 throw JSONRPCError(RPC_WALLET_ERROR, errors[0]);
5413 }
5414
5415 result.pushKV("txid", txid.GetHex());
5416 } else {
5417 PartiallySignedTransaction psbtx(mtx);
5418 bool complete = false;
5419 const TransactionError err = pwallet->FillPSBT(psbtx, complete, SIGHASH_ALL, false /* sign */, true /* bip32derivs */);
5420 CHECK_NONFATAL(err == TransactionError::OK);
5421 CHECK_NONFATAL(!complete);
5422 CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
5423 ssTx << psbtx;
5424 result.pushKV("psbt", EncodeBase64(ssTx.str()));
5425 }
5426
5427 result.pushKV("origfee", ValueFromAmount(old_fee));
5428 result.pushKV("fee", ValueFromAmount(new_fee));
5429 UniValue result_errors(UniValue::VARR);
5430 for (const std::string& error : errors) {
5431 result_errors.push_back(error);
5432 }
5433 result.pushKV("errors", result_errors);
5434
5435 return result;
5436 }
5437
rescanblockchain(const JSONRPCRequest & request)5438 UniValue rescanblockchain(const JSONRPCRequest& request)
5439 {
5440 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
5441 CWallet* const pwallet = wallet.get();
5442
5443 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
5444 return NullUniValue;
5445 }
5446
5447 RPCHelpMan{"rescanblockchain",
5448 "\nRescan the local blockchain for wallet related transactions.\n"
5449 "Note: Use \"getwalletinfo\" to query the scanning progress.\n",
5450 {
5451 {"start_height", RPCArg::Type::NUM, /* default */ "0", "block height where the rescan should start"},
5452 {"stop_height", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "the last block height that should be scanned. If none is provided it will rescan up to the tip at return time of this call."},
5453 },
5454 RPCResult{
5455 RPCResult::Type::OBJ, "", "",
5456 {
5457 {RPCResult::Type::NUM, "start_height", "The block height where the rescan started (the requested height or 0)"},
5458 {RPCResult::Type::NUM, "stop_height", "The height of the last rescanned block. May be null in rare cases if there was a reorg and the call didn't scan any blocks because they were already scanned in the background."},
5459 }
5460 },
5461 RPCExamples{
5462 HelpExampleCli("rescanblockchain", "100000 120000")
5463 + HelpExampleRpc("rescanblockchain", "100000, 120000")
5464 },
5465 }.Check(request);
5466
5467 WalletRescanReserver reserver(pwallet);
5468 if (!reserver.reserve()) {
5469 throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
5470 }
5471
5472 int start_height = 0;
5473 uint256 start_block, stop_block;
5474 {
5475 auto locked_chain = pwallet->chain().lock();
5476 Optional<int> tip_height = locked_chain->getHeight();
5477
5478 if (!request.params[0].isNull()) {
5479 start_height = request.params[0].get_int();
5480 if (start_height < 0 || !tip_height || start_height > *tip_height) {
5481 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid start_height");
5482 }
5483 }
5484
5485 Optional<int> stop_height;
5486 if (!request.params[1].isNull()) {
5487 stop_height = request.params[1].get_int();
5488 if (*stop_height < 0 || !tip_height || *stop_height > *tip_height) {
5489 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid stop_height");
5490 }
5491 else if (*stop_height < start_height) {
5492 throw JSONRPCError(RPC_INVALID_PARAMETER, "stop_height must be greater than start_height");
5493 }
5494 }
5495
5496 // We can't rescan beyond non-pruned blocks, stop and throw an error
5497 if (locked_chain->findPruned(start_height, stop_height)) {
5498 throw JSONRPCError(RPC_MISC_ERROR, "Can't rescan beyond pruned data. Use RPC call getblockchaininfo to determine your pruned height.");
5499 }
5500
5501 if (tip_height) {
5502 start_block = locked_chain->getBlockHash(start_height);
5503 // If called with a stop_height, set the stop_height here to
5504 // trigger a rescan to that height.
5505 // If called without a stop height, leave stop_height as null here
5506 // so rescan continues to the tip (even if the tip advances during
5507 // rescan).
5508 if (stop_height) {
5509 stop_block = locked_chain->getBlockHash(*stop_height);
5510 }
5511 }
5512 }
5513
5514 CWallet::ScanResult result =
5515 pwallet->ScanForWalletTransactions(start_block, stop_block, reserver, true /* fUpdate */);
5516 switch (result.status) {
5517 case CWallet::ScanResult::SUCCESS:
5518 break;
5519 case CWallet::ScanResult::FAILURE:
5520 throw JSONRPCError(RPC_MISC_ERROR, "Rescan failed. Potentially corrupted data files.");
5521 case CWallet::ScanResult::USER_ABORT:
5522 throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted.");
5523 // no default case, so the compiler can warn about missing cases
5524 }
5525 UniValue response(UniValue::VOBJ);
5526 response.pushKV("start_height", start_height);
5527 response.pushKV("stop_height", result.last_scanned_height ? *result.last_scanned_height : UniValue());
5528 return response;
5529 }
5530
5531 class DescribeWalletAddressVisitor : public boost::static_visitor<UniValue>
5532 {
5533 public:
5534 const SigningProvider * const provider;
5535
ProcessSubScript(const CScript & subscript,UniValue & obj) const5536 void ProcessSubScript(const CScript& subscript, UniValue& obj) const
5537 {
5538 // Always present: script type and redeemscript
5539 std::vector<std::vector<unsigned char>> solutions_data;
5540 txnouttype which_type = Solver(subscript, solutions_data);
5541 obj.pushKV("script", GetTxnOutputType(which_type));
5542 obj.pushKV("hex", HexStr(subscript.begin(), subscript.end()));
5543
5544 CTxDestination embedded;
5545 if (ExtractDestination(subscript, embedded)) {
5546 // Only when the script corresponds to an address.
5547 UniValue subobj(UniValue::VOBJ);
5548 UniValue detail = DescribeAddress(embedded);
5549 subobj.pushKVs(detail);
5550 UniValue wallet_detail = boost::apply_visitor(*this, embedded);
5551 subobj.pushKVs(wallet_detail);
5552 subobj.pushKV("address", EncodeDestination(embedded));
5553 subobj.pushKV("scriptPubKey", HexStr(subscript.begin(), subscript.end()));
5554 // Always report the pubkey at the top level, so that `getnewaddress()['pubkey']` always works.
5555 if (subobj.exists("pubkey")) obj.pushKV("pubkey", subobj["pubkey"]);
5556 obj.pushKV("embedded", std::move(subobj));
5557 } else if (which_type == TX_MULTISIG) {
5558 // Also report some information on multisig scripts (which do not have a corresponding address).
5559 // TODO: abstract out the common functionality between this logic and ExtractDestinations.
5560 obj.pushKV("sigsrequired", solutions_data[0][0]);
5561 UniValue pubkeys(UniValue::VARR);
5562 for (size_t i = 1; i < solutions_data.size() - 1; ++i) {
5563 CPubKey key(solutions_data[i].begin(), solutions_data[i].end());
5564 pubkeys.push_back(HexStr(key.begin(), key.end()));
5565 }
5566 obj.pushKV("pubkeys", std::move(pubkeys));
5567 }
5568 }
5569
DescribeWalletAddressVisitor(const SigningProvider * _provider)5570 explicit DescribeWalletAddressVisitor(const SigningProvider* _provider) : provider(_provider) {}
5571
operator ()(const CNoDestination & dest) const5572 UniValue operator()(const CNoDestination& dest) const { return UniValue(UniValue::VOBJ); }
5573
operator ()(const PKHash & pkhash) const5574 UniValue operator()(const PKHash& pkhash) const
5575 {
5576 CKeyID keyID(pkhash);
5577 UniValue obj(UniValue::VOBJ);
5578 CPubKey vchPubKey;
5579 if (provider && provider->GetPubKey(keyID, vchPubKey)) {
5580 obj.pushKV("pubkey", HexStr(vchPubKey));
5581 obj.pushKV("iscompressed", vchPubKey.IsCompressed());
5582 }
5583 return obj;
5584 }
5585
operator ()(const ScriptHash & scripthash) const5586 UniValue operator()(const ScriptHash& scripthash) const
5587 {
5588 CScriptID scriptID(scripthash);
5589 UniValue obj(UniValue::VOBJ);
5590 CScript subscript;
5591 if (provider && provider->GetCScript(scriptID, subscript)) {
5592 ProcessSubScript(subscript, obj);
5593 }
5594 return obj;
5595 }
5596
operator ()(const WitnessV0KeyHash & id) const5597 UniValue operator()(const WitnessV0KeyHash& id) const
5598 {
5599 UniValue obj(UniValue::VOBJ);
5600 CPubKey pubkey;
5601 if (provider && provider->GetPubKey(CKeyID(id), pubkey)) {
5602 obj.pushKV("pubkey", HexStr(pubkey));
5603 }
5604 return obj;
5605 }
5606
operator ()(const WitnessV0ScriptHash & id) const5607 UniValue operator()(const WitnessV0ScriptHash& id) const
5608 {
5609 UniValue obj(UniValue::VOBJ);
5610 CScript subscript;
5611 CRIPEMD160 hasher;
5612 uint160 hash;
5613 hasher.Write(id.begin(), 32).Finalize(hash.begin());
5614 if (provider && provider->GetCScript(CScriptID(hash), subscript)) {
5615 ProcessSubScript(subscript, obj);
5616 }
5617 return obj;
5618 }
5619
operator ()(const WitnessUnknown & id) const5620 UniValue operator()(const WitnessUnknown& id) const { return UniValue(UniValue::VOBJ); }
5621 };
5622
DescribeWalletAddress(const CWallet * const pwallet,const CTxDestination & dest)5623 static UniValue DescribeWalletAddress(const CWallet* const pwallet, const CTxDestination& dest)
5624 {
5625 UniValue ret(UniValue::VOBJ);
5626 UniValue detail = DescribeAddress(dest);
5627 CScript script = GetScriptForDestination(dest);
5628 std::unique_ptr<SigningProvider> provider = nullptr;
5629 if (pwallet) {
5630 provider = pwallet->GetSolvingProvider(script);
5631 }
5632 ret.pushKVs(detail);
5633 ret.pushKVs(boost::apply_visitor(DescribeWalletAddressVisitor(provider.get()), dest));
5634 return ret;
5635 }
5636
5637 /** Convert CAddressBookData to JSON record. */
AddressBookDataToJSON(const CAddressBookData & data,const bool verbose)5638 static UniValue AddressBookDataToJSON(const CAddressBookData& data, const bool verbose)
5639 {
5640 UniValue ret(UniValue::VOBJ);
5641 if (verbose) {
5642 ret.pushKV("name", data.GetLabel());
5643 }
5644 ret.pushKV("purpose", data.purpose);
5645 return ret;
5646 }
5647
getaddressinfo(const JSONRPCRequest & request)5648 UniValue getaddressinfo(const JSONRPCRequest& request)
5649 {
5650 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
5651 const CWallet* const pwallet = wallet.get();
5652
5653 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
5654 return NullUniValue;
5655 }
5656
5657 RPCHelpMan{"getaddressinfo",
5658 "\nReturn information about the given qtum address.\n"
5659 "Some of the information will only be present if the address is in the active wallet.\n",
5660 {
5661 {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The qtum address for which to get information."},
5662 },
5663 RPCResult{
5664 RPCResult::Type::OBJ, "", "",
5665 {
5666 {RPCResult::Type::STR, "address", "The qtum address validated."},
5667 {RPCResult::Type::STR_HEX, "scriptPubKey", "The hex-encoded scriptPubKey generated by the address."},
5668 {RPCResult::Type::BOOL, "ismine", "If the address is yours."},
5669 {RPCResult::Type::BOOL, "iswatchonly", "If the address is watchonly."},
5670 {RPCResult::Type::BOOL, "solvable", "If we know how to spend coins sent to this address, ignoring the possible lack of private keys."},
5671 {RPCResult::Type::STR, "desc", /* optional */ true, "A descriptor for spending coins sent to this address (only when solvable)."},
5672 {RPCResult::Type::BOOL, "isscript", "If the key is a script."},
5673 {RPCResult::Type::BOOL, "ischange", "If the address was used for change output."},
5674 {RPCResult::Type::BOOL, "iswitness", "If the address is a witness address."},
5675 {RPCResult::Type::NUM, "witness_version", /* optional */ true, "The version number of the witness program."},
5676 {RPCResult::Type::STR_HEX, "witness_program", /* optional */ true, "The hex value of the witness program."},
5677 {RPCResult::Type::STR, "script", /* optional */ true, "The output script type. Only if isscript is true and the redeemscript is known. Possible\n"
5678 " types: nonstandard, pubkey, pubkeyhash, scripthash, multisig, nulldata, witness_v0_keyhash,\n"
5679 "witness_v0_scripthash, witness_unknown."},
5680 {RPCResult::Type::STR_HEX, "hex", /* optional */ true, "The redeemscript for the p2sh address."},
5681 {RPCResult::Type::ARR, "pubkeys", /* optional */ true, "Array of pubkeys associated with the known redeemscript (only if script is multisig).",
5682 {
5683 {RPCResult::Type::STR, "pubkey", ""},
5684 }},
5685 {RPCResult::Type::NUM, "sigsrequired", /* optional */ true, "The number of signatures required to spend multisig output (only if script is multisig)."},
5686 {RPCResult::Type::STR_HEX, "pubkey", /* optional */ true, "The hex value of the raw public key for single-key addresses (possibly embedded in P2SH or P2WSH)."},
5687 {RPCResult::Type::OBJ, "embedded", /* optional */ true, "Information about the address embedded in P2SH or P2WSH, if relevant and known.",
5688 {
5689 {RPCResult::Type::ELISION, "", "Includes all\n"
5690 " getaddressinfo output fields for the embedded address, excluding metadata (timestamp, hdkeypath,\n"
5691 "hdseedid) and relation to the wallet (ismine, iswatchonly)."},
5692 }},
5693 {RPCResult::Type::BOOL, "iscompressed", /* optional */ true, "If the pubkey is compressed."},
5694 {RPCResult::Type::STR, "label", "DEPRECATED. The label associated with the address. Defaults to \"\". Replaced by the labels array below."},
5695 {RPCResult::Type::NUM_TIME, "timestamp", /* optional */ true, "The creation time of the key, if available, expressed in " + UNIX_EPOCH_TIME + "."},
5696 {RPCResult::Type::STR, "hdkeypath", /* optional */ true, "The HD keypath, if the key is HD and available."},
5697 {RPCResult::Type::STR_HEX, "hdseedid", /* optional */ true, "The Hash160 of the HD seed."},
5698 {RPCResult::Type::STR_HEX, "hdmasterfingerprint", /* optional */ true, "The fingerprint of the master key."},
5699 {RPCResult::Type::ARR, "labels", "Array of labels associated with the address. Currently limited to one label but returned\n"
5700 "as an array to keep the API stable if multiple labels are enabled in the future.",
5701 {
5702 {RPCResult::Type::STR, "label name", "The label name. Defaults to \"\"."},
5703 {RPCResult::Type::OBJ, "", "label data, DEPRECATED, will be removed in 0.21. To re-enable, launch qtumd with `-deprecatedrpc=labelspurpose`",
5704 {
5705 {RPCResult::Type::STR, "name", "The label name. Defaults to \"\"."},
5706 {RPCResult::Type::STR, "purpose", "The purpose of the associated address (send or receive)."},
5707 }},
5708 }},
5709 }
5710 },
5711 RPCExamples{
5712 HelpExampleCli("getaddressinfo", "\"" + EXAMPLE_ADDRESS[0] + "\"") +
5713 HelpExampleRpc("getaddressinfo", "\"" + EXAMPLE_ADDRESS[0] + "\"")
5714 },
5715 }.Check(request);
5716
5717 LOCK(pwallet->cs_wallet);
5718
5719 UniValue ret(UniValue::VOBJ);
5720 CTxDestination dest = DecodeDestination(request.params[0].get_str());
5721 // Make sure the destination is valid
5722 if (!IsValidDestination(dest)) {
5723 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
5724 }
5725
5726 std::string currentAddress = EncodeDestination(dest);
5727 ret.pushKV("address", currentAddress);
5728
5729 CScript scriptPubKey = GetScriptForDestination(dest);
5730 ret.pushKV("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end()));
5731
5732 std::unique_ptr<SigningProvider> provider = pwallet->GetSolvingProvider(scriptPubKey);
5733
5734 isminetype mine = pwallet->IsMine(dest);
5735 ret.pushKV("ismine", bool(mine & ISMINE_SPENDABLE));
5736
5737 bool solvable = provider && IsSolvable(*provider, scriptPubKey);
5738 ret.pushKV("solvable", solvable);
5739
5740 if (solvable) {
5741 ret.pushKV("desc", InferDescriptor(scriptPubKey, *provider)->ToString());
5742 }
5743
5744 ret.pushKV("iswatchonly", bool(mine & ISMINE_WATCH_ONLY));
5745
5746 UniValue detail = DescribeWalletAddress(pwallet, dest);
5747 ret.pushKVs(detail);
5748
5749 // DEPRECATED: Return label field if existing. Currently only one label can
5750 // be associated with an address, so the label should be equivalent to the
5751 // value of the name key/value pair in the labels array below.
5752 const auto* address_book_entry = pwallet->FindAddressBookEntry(dest);
5753 if (pwallet->chain().rpcEnableDeprecated("label") && address_book_entry) {
5754 ret.pushKV("label", address_book_entry->GetLabel());
5755 }
5756
5757 ret.pushKV("ischange", pwallet->IsChange(scriptPubKey));
5758
5759 ScriptPubKeyMan* spk_man = pwallet->GetScriptPubKeyMan(scriptPubKey);
5760 if (spk_man) {
5761 if (const CKeyMetadata* meta = spk_man->GetMetadata(dest)) {
5762 ret.pushKV("timestamp", meta->nCreateTime);
5763 if (meta->has_key_origin) {
5764 ret.pushKV("hdkeypath", WriteHDKeypath(meta->key_origin.path));
5765 ret.pushKV("hdseedid", meta->hd_seed_id.GetHex());
5766 ret.pushKV("hdmasterfingerprint", HexStr(meta->key_origin.fingerprint, meta->key_origin.fingerprint + 4));
5767 }
5768 }
5769 }
5770
5771 // Return a `labels` array containing the label associated with the address,
5772 // equivalent to the `label` field above. Currently only one label can be
5773 // associated with an address, but we return an array so the API remains
5774 // stable if we allow multiple labels to be associated with an address in
5775 // the future.
5776 UniValue labels(UniValue::VARR);
5777 if (address_book_entry) {
5778 // DEPRECATED: The previous behavior of returning an array containing a
5779 // JSON object of `name` and `purpose` key/value pairs is deprecated.
5780 if (pwallet->chain().rpcEnableDeprecated("labelspurpose")) {
5781 labels.push_back(AddressBookDataToJSON(*address_book_entry, true));
5782 } else {
5783 labels.push_back(address_book_entry->GetLabel());
5784 }
5785 }
5786 ret.pushKV("labels", std::move(labels));
5787
5788 return ret;
5789 }
5790
getaddressesbylabel(const JSONRPCRequest & request)5791 static UniValue getaddressesbylabel(const JSONRPCRequest& request)
5792 {
5793 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
5794 const CWallet* const pwallet = wallet.get();
5795
5796 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
5797 return NullUniValue;
5798 }
5799
5800 RPCHelpMan{"getaddressesbylabel",
5801 "\nReturns the list of addresses assigned the specified label.\n",
5802 {
5803 {"label", RPCArg::Type::STR, RPCArg::Optional::NO, "The label."},
5804 },
5805 RPCResult{
5806 RPCResult::Type::OBJ_DYN, "", "json object with addresses as keys",
5807 {
5808 {RPCResult::Type::OBJ, "address", "json object with information about address",
5809 {
5810 {RPCResult::Type::STR, "purpose", "Purpose of address (\"send\" for sending address, \"receive\" for receiving address)"},
5811 }},
5812 }
5813 },
5814 RPCExamples{
5815 HelpExampleCli("getaddressesbylabel", "\"tabby\"")
5816 + HelpExampleRpc("getaddressesbylabel", "\"tabby\"")
5817 },
5818 }.Check(request);
5819
5820 LOCK(pwallet->cs_wallet);
5821
5822 std::string label = LabelFromValue(request.params[0]);
5823
5824 // Find all addresses that have the given label
5825 UniValue ret(UniValue::VOBJ);
5826 std::set<std::string> addresses;
5827 for (const std::pair<const CTxDestination, CAddressBookData>& item : pwallet->m_address_book) {
5828 if (item.second.IsChange()) continue;
5829 if (item.second.GetLabel() == label) {
5830 std::string address = EncodeDestination(item.first);
5831 // CWallet::m_address_book is not expected to contain duplicate
5832 // address strings, but build a separate set as a precaution just in
5833 // case it does.
5834 bool unique = addresses.emplace(address).second;
5835 CHECK_NONFATAL(unique);
5836 // UniValue::pushKV checks if the key exists in O(N)
5837 // and since duplicate addresses are unexpected (checked with
5838 // std::set in O(log(N))), UniValue::__pushKV is used instead,
5839 // which currently is O(1).
5840 ret.__pushKV(address, AddressBookDataToJSON(item.second, false));
5841 }
5842 }
5843
5844 if (ret.empty()) {
5845 throw JSONRPCError(RPC_WALLET_INVALID_LABEL_NAME, std::string("No addresses with label " + label));
5846 }
5847
5848 return ret;
5849 }
5850
listlabels(const JSONRPCRequest & request)5851 static UniValue listlabels(const JSONRPCRequest& request)
5852 {
5853 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
5854 const CWallet* const pwallet = wallet.get();
5855
5856 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
5857 return NullUniValue;
5858 }
5859
5860 RPCHelpMan{"listlabels",
5861 "\nReturns the list of all labels, or labels that are assigned to addresses with a specific purpose.\n",
5862 {
5863 {"purpose", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "Address purpose to list labels for ('send','receive'). An empty string is the same as not providing this argument."},
5864 },
5865 RPCResult{
5866 RPCResult::Type::ARR, "", "",
5867 {
5868 {RPCResult::Type::STR, "label", "Label name"},
5869 }
5870 },
5871 RPCExamples{
5872 "\nList all labels\n"
5873 + HelpExampleCli("listlabels", "") +
5874 "\nList labels that have receiving addresses\n"
5875 + HelpExampleCli("listlabels", "receive") +
5876 "\nList labels that have sending addresses\n"
5877 + HelpExampleCli("listlabels", "send") +
5878 "\nAs a JSON-RPC call\n"
5879 + HelpExampleRpc("listlabels", "receive")
5880 },
5881 }.Check(request);
5882
5883 LOCK(pwallet->cs_wallet);
5884
5885 std::string purpose;
5886 if (!request.params[0].isNull()) {
5887 purpose = request.params[0].get_str();
5888 }
5889
5890 // Add to a set to sort by label name, then insert into Univalue array
5891 std::set<std::string> label_set;
5892 for (const std::pair<const CTxDestination, CAddressBookData>& entry : pwallet->m_address_book) {
5893 if (entry.second.IsChange()) continue;
5894 if (purpose.empty() || entry.second.purpose == purpose) {
5895 label_set.insert(entry.second.GetLabel());
5896 }
5897 }
5898
5899 UniValue ret(UniValue::VARR);
5900 for (const std::string& name : label_set) {
5901 ret.push_back(name);
5902 }
5903
5904 return ret;
5905 }
5906
sethdseed(const JSONRPCRequest & request)5907 UniValue sethdseed(const JSONRPCRequest& request)
5908 {
5909 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
5910 CWallet* const pwallet = wallet.get();
5911
5912 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
5913 return NullUniValue;
5914 }
5915
5916 RPCHelpMan{"sethdseed",
5917 "\nSet or generate a new HD wallet seed. Non-HD wallets will not be upgraded to being a HD wallet. Wallets that are already\n"
5918 "HD will have a new HD seed set so that new keys added to the keypool will be derived from this new seed.\n"
5919 "\nNote that you will need to MAKE A NEW BACKUP of your wallet after setting the HD wallet seed." +
5920 HELP_REQUIRING_PASSPHRASE,
5921 {
5922 {"newkeypool", RPCArg::Type::BOOL, /* default */ "true", "Whether to flush old unused addresses, including change addresses, from the keypool and regenerate it.\n"
5923 " If true, the next address from getnewaddress and change address from getrawchangeaddress will be from this new seed.\n"
5924 " If false, addresses (including change addresses if the wallet already had HD Chain Split enabled) from the existing\n"
5925 " keypool will be used until it has been depleted."},
5926 {"seed", RPCArg::Type::STR, /* default */ "random seed", "The WIF private key to use as the new HD seed.\n"
5927 " The seed value can be retrieved using the dumpwallet command. It is the private key marked hdseed=1"},
5928 },
5929 RPCResult{RPCResult::Type::NONE, "", ""},
5930 RPCExamples{
5931 HelpExampleCli("sethdseed", "")
5932 + HelpExampleCli("sethdseed", "false")
5933 + HelpExampleCli("sethdseed", "true \"wifkey\"")
5934 + HelpExampleRpc("sethdseed", "true, \"wifkey\"")
5935 },
5936 }.Check(request);
5937
5938 LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*pwallet, true);
5939
5940 if (pwallet->chain().isInitialBlockDownload()) {
5941 throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Cannot set a new HD seed while still in Initial Block Download");
5942 }
5943
5944 if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
5945 throw JSONRPCError(RPC_WALLET_ERROR, "Cannot set a HD seed to a wallet with private keys disabled");
5946 }
5947
5948 auto locked_chain = pwallet->chain().lock();
5949 LOCK2(pwallet->cs_wallet, spk_man.cs_KeyStore);
5950
5951 // Do not do anything to non-HD wallets
5952 if (!pwallet->CanSupportFeature(FEATURE_HD)) {
5953 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");
5954 }
5955
5956 EnsureWalletIsUnlocked(pwallet);
5957
5958 bool flush_key_pool = true;
5959 if (!request.params[0].isNull()) {
5960 flush_key_pool = request.params[0].get_bool();
5961 }
5962
5963 CPubKey master_pub_key;
5964 if (request.params[1].isNull()) {
5965 master_pub_key = spk_man.GenerateNewSeed();
5966 } else {
5967 CKey key = DecodeSecret(request.params[1].get_str());
5968 if (!key.IsValid()) {
5969 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key");
5970 }
5971
5972 if (HaveKey(spk_man, key)) {
5973 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Already have this key (either as an HD seed or as a loose private key)");
5974 }
5975
5976 master_pub_key = spk_man.DeriveNewSeed(key);
5977 }
5978
5979 spk_man.SetHDSeed(master_pub_key);
5980 if (flush_key_pool) spk_man.NewKeyPool();
5981
5982 return NullUniValue;
5983 }
5984
walletprocesspsbt(const JSONRPCRequest & request)5985 UniValue walletprocesspsbt(const JSONRPCRequest& request)
5986 {
5987 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
5988 const CWallet* const pwallet = wallet.get();
5989
5990 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
5991 return NullUniValue;
5992 }
5993
5994 RPCHelpMan{"walletprocesspsbt",
5995 "\nUpdate a PSBT with input information from our wallet and then sign inputs\n"
5996 "that we can sign for." +
5997 HELP_REQUIRING_PASSPHRASE,
5998 {
5999 {"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "The transaction base64 string"},
6000 {"sign", RPCArg::Type::BOOL, /* default */ "true", "Also sign the transaction when updating"},
6001 {"sighashtype", RPCArg::Type::STR, /* default */ "ALL", "The signature hash type to sign with if not specified by the PSBT. Must be one of\n"
6002 " \"ALL\"\n"
6003 " \"NONE\"\n"
6004 " \"SINGLE\"\n"
6005 " \"ALL|ANYONECANPAY\"\n"
6006 " \"NONE|ANYONECANPAY\"\n"
6007 " \"SINGLE|ANYONECANPAY\""},
6008 {"bip32derivs", RPCArg::Type::BOOL, /* default */ "true", "Include BIP 32 derivation paths for public keys if we know them"},
6009 },
6010 RPCResult{
6011 RPCResult::Type::OBJ, "", "",
6012 {
6013 {RPCResult::Type::STR, "psbt", "The base64-encoded partially signed transaction"},
6014 {RPCResult::Type::BOOL, "complete", "If the transaction has a complete set of signatures"},
6015 }
6016 },
6017 RPCExamples{
6018 HelpExampleCli("walletprocesspsbt", "\"psbt\"")
6019 },
6020 }.Check(request);
6021
6022 RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL, UniValue::VSTR});
6023
6024 // Unserialize the transaction
6025 PartiallySignedTransaction psbtx;
6026 std::string error;
6027 if (!DecodeBase64PSBT(psbtx, request.params[0].get_str(), error)) {
6028 throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error));
6029 }
6030
6031 // Get the sighash type
6032 int nHashType = ParseSighashString(request.params[2]);
6033
6034 // Fill transaction with our data and also sign
6035 bool sign = request.params[1].isNull() ? true : request.params[1].get_bool();
6036 bool bip32derivs = request.params[3].isNull() ? true : request.params[3].get_bool();
6037 bool complete = true;
6038 const TransactionError err = pwallet->FillPSBT(psbtx, complete, nHashType, sign, bip32derivs);
6039 if (err != TransactionError::OK) {
6040 throw JSONRPCTransactionError(err);
6041 }
6042
6043 UniValue result(UniValue::VOBJ);
6044 CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
6045 ssTx << psbtx;
6046 result.pushKV("psbt", EncodeBase64(ssTx.str()));
6047 result.pushKV("complete", complete);
6048
6049 return result;
6050 }
6051
walletcreatefundedpsbt(const JSONRPCRequest & request)6052 UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
6053 {
6054 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
6055 CWallet* const pwallet = wallet.get();
6056
6057 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
6058 return NullUniValue;
6059 }
6060
6061 RPCHelpMan{"walletcreatefundedpsbt",
6062 "\nCreates and funds a transaction in the Partially Signed Transaction format. Inputs will be added if supplied inputs are not enough\n"
6063 "Implements the Creator and Updater roles.\n",
6064 {
6065 {"inputs", RPCArg::Type::ARR, RPCArg::Optional::NO, "The inputs",
6066 {
6067 {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
6068 {
6069 {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
6070 {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
6071 {"sequence", RPCArg::Type::NUM, RPCArg::Optional::NO, "The sequence number"},
6072 },
6073 },
6074 },
6075 },
6076 {"outputs", RPCArg::Type::ARR, RPCArg::Optional::NO, "The outputs (key-value pairs), where none of the keys are duplicated.\n"
6077 "That is, each address can only appear once and there can only be one 'data' object.\n"
6078 "For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also\n"
6079 " accepted as second parameter.",
6080 {
6081 {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
6082 {
6083 {"address", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "A key-value pair. The key (string) is the qtum address, the value (float or string) is the amount in " + CURRENCY_UNIT + ""},
6084 },
6085 },
6086 {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
6087 {
6088 {"data", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "A key-value pair. The key must be \"data\", the value is hex-encoded data"},
6089 },
6090 },
6091 },
6092 },
6093 {"locktime", RPCArg::Type::NUM, /* default */ "0", "Raw locktime. Non-0 value also locktime-activates inputs"},
6094 {"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "",
6095 {
6096 {"changeAddress", RPCArg::Type::STR_HEX, /* default */ "pool address", "The qtum address to receive the change"},
6097 {"changePosition", RPCArg::Type::NUM, /* default */ "random", "The index of the change output"},
6098 {"change_type", RPCArg::Type::STR, /* default */ "set by -changetype", "The output type to use. Only valid if changeAddress is not specified. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
6099 {"includeWatching", RPCArg::Type::BOOL, /* default */ "true for watch-only wallets, otherwise false", "Also select inputs which are watch only"},
6100 {"lockUnspents", RPCArg::Type::BOOL, /* default */ "false", "Lock selected unspent outputs"},
6101 {"feeRate", RPCArg::Type::AMOUNT, /* default */ "not set: makes wallet determine the fee", "Set a specific fee rate in " + CURRENCY_UNIT + "/kB"},
6102 {"subtractFeeFromOutputs", RPCArg::Type::ARR, /* default */ "empty array", "The outputs to subtract the fee from.\n"
6103 " The fee will be equally deducted from the amount of each specified output.\n"
6104 " Those recipients will receive less qtums than you enter in their corresponding amount field.\n"
6105 " If no outputs are specified here, the sender pays the fee.",
6106 {
6107 {"vout_index", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "The zero-based output index, before a change output is added."},
6108 },
6109 },
6110 {"replaceable", RPCArg::Type::BOOL, /* default */ "wallet default", "Marks this transaction as BIP125 replaceable.\n"
6111 " Allows this transaction to be replaced by a transaction with higher fees"},
6112 {"conf_target", RPCArg::Type::NUM, /* default */ "fall back to wallet's confirmation target (txconfirmtarget)", "Confirmation target (in blocks)"},
6113 {"estimate_mode", RPCArg::Type::STR, /* default */ "UNSET", "The fee estimate mode, must be one of:\n"
6114 " \"UNSET\"\n"
6115 " \"ECONOMICAL\"\n"
6116 " \"CONSERVATIVE\""},
6117 },
6118 "options"},
6119 {"bip32derivs", RPCArg::Type::BOOL, /* default */ "true", "Include BIP 32 derivation paths for public keys if we know them"},
6120 },
6121 RPCResult{
6122 RPCResult::Type::OBJ, "", "",
6123 {
6124 {RPCResult::Type::STR, "psbt", "The resulting raw transaction (base64-encoded string)"},
6125 {RPCResult::Type::STR_AMOUNT, "fee", "Fee in " + CURRENCY_UNIT + " the resulting transaction pays"},
6126 {RPCResult::Type::NUM, "changepos", "The position of the added change output, or -1"},
6127 }
6128 },
6129 RPCExamples{
6130 "\nCreate a transaction with no inputs\n"
6131 + HelpExampleCli("walletcreatefundedpsbt", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"data\\\":\\\"00010203\\\"}]\"")
6132 },
6133 }.Check(request);
6134
6135 RPCTypeCheck(request.params, {
6136 UniValue::VARR,
6137 UniValueType(), // ARR or OBJ, checked later
6138 UniValue::VNUM,
6139 UniValue::VOBJ,
6140 UniValue::VBOOL
6141 }, true
6142 );
6143
6144 CAmount fee;
6145 int change_position;
6146 bool rbf = pwallet->m_signal_rbf;
6147 const UniValue &replaceable_arg = request.params[3]["replaceable"];
6148 if (!replaceable_arg.isNull()) {
6149 RPCTypeCheckArgument(replaceable_arg, UniValue::VBOOL);
6150 rbf = replaceable_arg.isTrue();
6151 }
6152 CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], rbf);
6153 FundTransaction(pwallet, rawTx, fee, change_position, request.params[3]);
6154
6155 // Make a blank psbt
6156 PartiallySignedTransaction psbtx(rawTx);
6157
6158 // Fill transaction with out data but don't sign
6159 bool bip32derivs = request.params[4].isNull() ? true : request.params[4].get_bool();
6160 bool complete = true;
6161 const TransactionError err = pwallet->FillPSBT(psbtx, complete, 1, false, bip32derivs);
6162 if (err != TransactionError::OK) {
6163 throw JSONRPCTransactionError(err);
6164 }
6165
6166 // Serialize the PSBT
6167 CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
6168 ssTx << psbtx;
6169
6170 UniValue result(UniValue::VOBJ);
6171 result.pushKV("psbt", EncodeBase64(ssTx.str()));
6172 result.pushKV("fee", ValueFromAmount(fee));
6173 result.pushKV("changepos", change_position);
6174 return result;
6175 }
6176
qrc20approve(const JSONRPCRequest & request)6177 static UniValue qrc20approve(const JSONRPCRequest& request)
6178 {
6179 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
6180 CWallet* const pwallet = wallet.get();
6181
6182 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
6183 return NullUniValue;
6184 }
6185
6186 LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*pwallet);
6187 auto locked_chain = pwallet->chain().lock();
6188 LOCK2(pwallet->cs_wallet, spk_man.cs_KeyStore);
6189 QtumDGP qtumDGP(globalState.get(), fGettingValuesDGP);
6190 uint64_t blockGasLimit = qtumDGP.getBlockGasLimit(::ChainActive().Height());
6191 uint64_t minGasPrice = CAmount(qtumDGP.getMinGasPrice(::ChainActive().Height()));
6192 CAmount nGasPrice = (minGasPrice>DEFAULT_GAS_PRICE)?minGasPrice:DEFAULT_GAS_PRICE;
6193 uint64_t nGasLimit=DEFAULT_GAS_LIMIT_OP_SEND;
6194 bool fCheckOutputs = true;
6195
6196 RPCHelpMan{"qrc20approve",
6197 "\nOwner approves an address to spend some amount of tokens.\n",
6198 {
6199 {"contractaddress", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The contract address."},
6200 {"owneraddress", RPCArg::Type::STR, RPCArg::Optional::NO, "The token owner qtum address."},
6201 {"spenderaddress", RPCArg::Type::STR, RPCArg::Optional::NO, "The token spender qtum address."},
6202 {"amount", RPCArg::Type::STR, RPCArg::Optional::NO, "The amount of tokens. eg 0.1"},
6203 {"gasLimit", RPCArg::Type::AMOUNT, RPCArg::Optional::OMITTED, "The gas limit, default: "+i64tostr(DEFAULT_GAS_LIMIT_OP_SEND)+", max: "+i64tostr(blockGasLimit)},
6204 {"gasPrice", RPCArg::Type::AMOUNT, RPCArg::Optional::OMITTED, "The qtum price per gas unit, default: "+FormatMoney(nGasPrice)+", min:"+FormatMoney(minGasPrice)},
6205 {"checkOutputs", RPCArg::Type::BOOL, RPCArg::Optional::OMITTED, "Check outputs before send, default: true"},
6206 },
6207 RPCResult{
6208 RPCResult::Type::OBJ, "", "",
6209 {
6210 {RPCResult::Type::STR_HEX, "txid", "The transaction id"},
6211 }
6212 },
6213 RPCExamples{
6214 HelpExampleCli("qrc20approve", "\"eb23c0b3e6042821da281a2e2364feb22dd543e3\" \"QX1GkJdye9WoUnrE2v6ZQhQ72EUVDtGXQX\" \"QM72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1")
6215 + HelpExampleCli("qrc20approve", "\"eb23c0b3e6042821da281a2e2364feb22dd543e3\" \"QX1GkJdye9WoUnrE2v6ZQhQ72EUVDtGXQX\" \"QM72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1 "+i64tostr(DEFAULT_GAS_LIMIT_OP_SEND)+" "+FormatMoney(minGasPrice)+" true")
6216 + HelpExampleRpc("qrc20approve", "\"eb23c0b3e6042821da281a2e2364feb22dd543e3\" \"QX1GkJdye9WoUnrE2v6ZQhQ72EUVDtGXQX\" \"QM72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1")
6217 + HelpExampleRpc("qrc20approve", "\"eb23c0b3e6042821da281a2e2364feb22dd543e3\" \"QX1GkJdye9WoUnrE2v6ZQhQ72EUVDtGXQX\" \"QM72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1 "+i64tostr(DEFAULT_GAS_LIMIT_OP_SEND)+" "+FormatMoney(minGasPrice)+" true")
6218 },
6219 }.Check(request);
6220
6221 // Get mandatory parameters
6222 std::string contract = request.params[0].get_str();
6223 std::string owner = request.params[1].get_str();
6224 std::string spender = request.params[2].get_str();
6225 std::string tokenAmount = request.params[3].get_str();
6226
6227 // Get gas limit
6228 if (request.params.size() > 4){
6229 nGasLimit = request.params[4].get_int64();
6230 }
6231
6232 // Get gas price
6233 if (request.params.size() > 5){
6234 nGasPrice = AmountFromValue(request.params[5]);
6235 }
6236
6237 // Get check outputs flag
6238 if (request.params.size() > 6){
6239 fCheckOutputs = request.params[6].get_bool();
6240 }
6241
6242 // Set token parameters
6243 SendToken token(*locked_chain, pwallet, spk_man);
6244 token.setAddress(contract);
6245 token.setSender(owner);
6246 token.setGasLimit(i64tostr(nGasLimit));
6247 token.setGasPrice(FormatMoney(nGasPrice));
6248
6249 // Get decimals
6250 uint32_t decimals;
6251 if(!token.decimals(decimals))
6252 throw JSONRPCError(RPC_MISC_ERROR, "Fail to get decimals");
6253
6254 // Get token amount to approve
6255 dev::s256 nTokenAmount;
6256 if(!ParseToken(decimals, tokenAmount, nTokenAmount))
6257 throw JSONRPCError(RPC_MISC_ERROR, "Fail to get token amount");
6258
6259 // Check approve offline
6260 std::string value = nTokenAmount.str();
6261 bool success = false;
6262 if(fCheckOutputs)
6263 {
6264 token.setCheckGasForCall(true);
6265 if(!token.approve(spender, value, success) || !success)
6266 throw JSONRPCError(RPC_MISC_ERROR, "Fail offline check for approve token amount for spending");
6267 }
6268
6269 // Approve value to spend
6270 if(!token.approve(spender, value, success, true))
6271 throw JSONRPCError(RPC_MISC_ERROR, "Fail to approve token amount for spending");
6272
6273 UniValue result(UniValue::VOBJ);
6274 result.pushKV("txid", token.getTxId());
6275 return result;
6276 }
6277
qrc20transfer(const JSONRPCRequest & request)6278 static UniValue qrc20transfer(const JSONRPCRequest& request)
6279 {
6280 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
6281 CWallet* const pwallet = wallet.get();
6282
6283 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
6284 return NullUniValue;
6285 }
6286
6287 LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*pwallet);
6288 auto locked_chain = pwallet->chain().lock();
6289 LOCK2(pwallet->cs_wallet, spk_man.cs_KeyStore);
6290 QtumDGP qtumDGP(globalState.get(), fGettingValuesDGP);
6291 uint64_t blockGasLimit = qtumDGP.getBlockGasLimit(::ChainActive().Height());
6292 uint64_t minGasPrice = CAmount(qtumDGP.getMinGasPrice(::ChainActive().Height()));
6293 CAmount nGasPrice = (minGasPrice>DEFAULT_GAS_PRICE)?minGasPrice:DEFAULT_GAS_PRICE;
6294 uint64_t nGasLimit=DEFAULT_GAS_LIMIT_OP_SEND;
6295 bool fCheckOutputs = true;
6296
6297 RPCHelpMan{"qrc20transfer",
6298 "\nSend token amount to a given address.\n",
6299 {
6300 {"contractaddress", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The contract address."},
6301 {"owneraddress", RPCArg::Type::STR, RPCArg::Optional::NO, "The token owner qtum address."},
6302 {"addressto", RPCArg::Type::STR, RPCArg::Optional::NO, "The qtum address to send funds to."},
6303 {"amount", RPCArg::Type::STR, RPCArg::Optional::NO, "The amount of tokens to send. eg 0.1"},
6304 {"gasLimit", RPCArg::Type::AMOUNT, RPCArg::Optional::OMITTED, "The gas limit, default: "+i64tostr(DEFAULT_GAS_LIMIT_OP_SEND)+", max: "+i64tostr(blockGasLimit)},
6305 {"gasPrice", RPCArg::Type::AMOUNT, RPCArg::Optional::OMITTED, "The qtum price per gas unit, default: "+FormatMoney(nGasPrice)+", min:"+FormatMoney(minGasPrice)},
6306 {"checkOutputs", RPCArg::Type::BOOL, RPCArg::Optional::OMITTED, "Check outputs before send, default: true"},
6307 },
6308 RPCResult{
6309 RPCResult::Type::OBJ, "", "",
6310 {
6311 {RPCResult::Type::STR_HEX, "txid", "The transaction id"},
6312 }
6313 },
6314 RPCExamples{
6315 HelpExampleCli("qrc20transfer", "\"eb23c0b3e6042821da281a2e2364feb22dd543e3\" \"QX1GkJdye9WoUnrE2v6ZQhQ72EUVDtGXQX\" \"QM72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1")
6316 + HelpExampleCli("qrc20transfer", "\"eb23c0b3e6042821da281a2e2364feb22dd543e3\" \"QX1GkJdye9WoUnrE2v6ZQhQ72EUVDtGXQX\" \"QM72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1 "+i64tostr(DEFAULT_GAS_LIMIT_OP_SEND)+" "+FormatMoney(minGasPrice)+" true")
6317 + HelpExampleRpc("qrc20transfer", "\"eb23c0b3e6042821da281a2e2364feb22dd543e3\" \"QX1GkJdye9WoUnrE2v6ZQhQ72EUVDtGXQX\" \"QM72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1")
6318 + HelpExampleRpc("qrc20transfer", "\"eb23c0b3e6042821da281a2e2364feb22dd543e3\" \"QX1GkJdye9WoUnrE2v6ZQhQ72EUVDtGXQX\" \"QM72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1 "+i64tostr(DEFAULT_GAS_LIMIT_OP_SEND)+" "+FormatMoney(minGasPrice)+" true")
6319 },
6320 }.Check(request);
6321
6322 // Get mandatory parameters
6323 std::string contract = request.params[0].get_str();
6324 std::string owner = request.params[1].get_str();
6325 std::string address = request.params[2].get_str();
6326 std::string tokenAmount = request.params[3].get_str();
6327
6328 // Get gas limit
6329 if (request.params.size() > 4){
6330 nGasLimit = request.params[4].get_int64();
6331 }
6332
6333 // Get gas price
6334 if (request.params.size() > 5){
6335 nGasPrice = AmountFromValue(request.params[5]);
6336 }
6337
6338 // Get check outputs flag
6339 if (request.params.size() > 6){
6340 fCheckOutputs = request.params[6].get_bool();
6341 }
6342
6343 // Set token parameters
6344 SendToken token(*locked_chain, pwallet, spk_man);
6345 token.setAddress(contract);
6346 token.setSender(owner);
6347 token.setGasLimit(i64tostr(nGasLimit));
6348 token.setGasPrice(FormatMoney(nGasPrice));
6349
6350 // Get decimals
6351 uint32_t decimals;
6352 if(!token.decimals(decimals))
6353 throw JSONRPCError(RPC_MISC_ERROR, "Fail to get decimals");
6354
6355 // Get token amount
6356 dev::s256 nTokenAmount;
6357 if(!ParseToken(decimals, tokenAmount, nTokenAmount))
6358 throw JSONRPCError(RPC_MISC_ERROR, "Fail to get token amount");
6359
6360 // Get token owner balance
6361 std::string strBalance;
6362 if(!token.balanceOf(strBalance))
6363 throw JSONRPCError(RPC_MISC_ERROR, "Fail to get balance");
6364
6365 // Check if balance is enough to cover it
6366 dev::s256 balance(strBalance);
6367 if(balance < nTokenAmount)
6368 throw JSONRPCError(RPC_MISC_ERROR, "Not enough token balance");
6369
6370 // Check transfer offline
6371 std::string value = nTokenAmount.str();
6372 bool success = false;
6373 if(fCheckOutputs)
6374 {
6375 token.setCheckGasForCall(true);
6376 if(!token.transfer(address, value, success) || !success)
6377 throw JSONRPCError(RPC_MISC_ERROR, "Fail offline check for transfer token");
6378 }
6379
6380 // Send token
6381 if(!token.transfer(address, value, success, true))
6382 throw JSONRPCError(RPC_MISC_ERROR, "Fail to transfer token");
6383
6384 UniValue result(UniValue::VOBJ);
6385 result.pushKV("txid", token.getTxId());
6386 return result;
6387 }
6388
qrc20transferfrom(const JSONRPCRequest & request)6389 static UniValue qrc20transferfrom(const JSONRPCRequest& request)
6390 {
6391 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
6392 CWallet* const pwallet = wallet.get();
6393
6394 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
6395 return NullUniValue;
6396 }
6397
6398 LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*pwallet);
6399 auto locked_chain = pwallet->chain().lock();
6400 LOCK2(pwallet->cs_wallet, spk_man.cs_KeyStore);
6401 QtumDGP qtumDGP(globalState.get(), fGettingValuesDGP);
6402 uint64_t blockGasLimit = qtumDGP.getBlockGasLimit(::ChainActive().Height());
6403 uint64_t minGasPrice = CAmount(qtumDGP.getMinGasPrice(::ChainActive().Height()));
6404 CAmount nGasPrice = (minGasPrice>DEFAULT_GAS_PRICE)?minGasPrice:DEFAULT_GAS_PRICE;
6405 uint64_t nGasLimit=DEFAULT_GAS_LIMIT_OP_SEND;
6406 bool fCheckOutputs = true;
6407
6408 RPCHelpMan{"qrc20transferfrom",
6409 "\nSend token amount from selected address to a given address.\n",
6410 {
6411 {"contractaddress", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The contract address."},
6412 {"owneraddress", RPCArg::Type::STR, RPCArg::Optional::NO, "The token owner qtum address."},
6413 {"spenderaddress", RPCArg::Type::STR, RPCArg::Optional::NO, "The token spender qtum address."},
6414 {"receiveraddress", RPCArg::Type::STR, RPCArg::Optional::NO, "The token receiver qtum address."},
6415 {"amount", RPCArg::Type::STR, RPCArg::Optional::NO, "The amount of token to send. eg 0.1"},
6416 {"gasLimit", RPCArg::Type::AMOUNT, RPCArg::Optional::OMITTED, "The gas limit, default: "+i64tostr(DEFAULT_GAS_LIMIT_OP_SEND)+", max: "+i64tostr(blockGasLimit)},
6417 {"gasPrice", RPCArg::Type::AMOUNT, RPCArg::Optional::OMITTED, "The qtum price per gas unit, default: "+FormatMoney(nGasPrice)+", min:"+FormatMoney(minGasPrice)},
6418 {"checkOutputs", RPCArg::Type::BOOL, RPCArg::Optional::OMITTED, "Check outputs before send, default: true"},
6419 },
6420 RPCResult{
6421 RPCResult::Type::OBJ, "", "",
6422 {
6423 {RPCResult::Type::STR_HEX, "txid", "The transaction id"},
6424 }
6425 },
6426 RPCExamples{
6427 HelpExampleCli("qrc20transferfrom", "\"eb23c0b3e6042821da281a2e2364feb22dd543e3\" \"QM72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" \"QX1GkJdye9WoUnrE2v6ZQhQ72EUVDtGXQX\" \"QhZThdumK8EFRX8MziWzvjCdiQWRt7Mxdz\" 0.1")
6428 + HelpExampleCli("qrc20transferfrom", "\"eb23c0b3e6042821da281a2e2364feb22dd543e3\" \"QM72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" \"QX1GkJdye9WoUnrE2v6ZQhQ72EUVDtGXQX\" \"QhZThdumK8EFRX8MziWzvjCdiQWRt7Mxdz\" 0.1 "+i64tostr(DEFAULT_GAS_LIMIT_OP_SEND)+" "+FormatMoney(minGasPrice)+" true")
6429 + HelpExampleRpc("qrc20transferfrom", "\"eb23c0b3e6042821da281a2e2364feb22dd543e3\" \"QM72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" \"QX1GkJdye9WoUnrE2v6ZQhQ72EUVDtGXQX\" \"QhZThdumK8EFRX8MziWzvjCdiQWRt7Mxdz\" 0.1")
6430 + HelpExampleRpc("qrc20transferfrom", "\"eb23c0b3e6042821da281a2e2364feb22dd543e3\" \"QM72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" \"QX1GkJdye9WoUnrE2v6ZQhQ72EUVDtGXQX\" \"QhZThdumK8EFRX8MziWzvjCdiQWRt7Mxdz\" 0.1 "+i64tostr(DEFAULT_GAS_LIMIT_OP_SEND)+" "+FormatMoney(minGasPrice)+" true")
6431 },
6432 }.Check(request);
6433
6434 // Get mandatory parameters
6435 std::string contract = request.params[0].get_str();
6436 std::string owner = request.params[1].get_str();
6437 std::string spender = request.params[2].get_str();
6438 std::string receiver = request.params[3].get_str();
6439 std::string tokenAmount = request.params[4].get_str();
6440
6441 // Get gas limit
6442 if (request.params.size() > 5){
6443 nGasLimit = request.params[5].get_int64();
6444 }
6445
6446 // Get gas price
6447 if (request.params.size() > 6){
6448 nGasPrice = AmountFromValue(request.params[6]);
6449 }
6450
6451 // Get check outputs flag
6452 if (request.params.size() > 7){
6453 fCheckOutputs = request.params[7].get_bool();
6454 }
6455
6456 // Set token parameters
6457 SendToken token(*locked_chain, pwallet, spk_man);
6458 token.setAddress(contract);
6459 token.setSender(spender);
6460 token.setGasLimit(i64tostr(nGasLimit));
6461 token.setGasPrice(FormatMoney(nGasPrice));
6462
6463 // Get decimals
6464 uint32_t decimals;
6465 if(!token.decimals(decimals))
6466 throw JSONRPCError(RPC_MISC_ERROR, "Fail to get decimals");
6467
6468 // Get token amount to spend
6469 dev::s256 nTokenAmount;
6470 if(!ParseToken(decimals, tokenAmount, nTokenAmount))
6471 throw JSONRPCError(RPC_MISC_ERROR, "Fail to get token amount");
6472
6473 // Get token spender allowance
6474 std::string strAllowance;
6475 if(!token.allowance(owner, spender, strAllowance))
6476 throw JSONRPCError(RPC_MISC_ERROR, "Fail to get allowance");
6477
6478 // Check if allowance is enough to cover it
6479 dev::s256 allowance(strAllowance);
6480 if(allowance < nTokenAmount)
6481 throw JSONRPCError(RPC_MISC_ERROR, "Not enough token allowance");
6482
6483 // Check transfer from offline
6484 std::string value = nTokenAmount.str();
6485 bool success = false;
6486 if(fCheckOutputs)
6487 {
6488 token.setCheckGasForCall(true);
6489 if(!token.transferFrom(owner, receiver, value, success) || !success)
6490 throw JSONRPCError(RPC_MISC_ERROR, "Fail offline check for spend token amount from address");
6491 }
6492
6493 // Transfer allowed token amount
6494 if(!token.transferFrom(owner, receiver, value, success, true))
6495 throw JSONRPCError(RPC_MISC_ERROR, "Fail to spend token amount from address");
6496
6497 UniValue result(UniValue::VOBJ);
6498 result.pushKV("txid", token.getTxId());
6499 return result;
6500 }
6501
qrc20burn(const JSONRPCRequest & request)6502 static UniValue qrc20burn(const JSONRPCRequest& request)
6503 {
6504 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
6505 CWallet* const pwallet = wallet.get();
6506
6507 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
6508 return NullUniValue;
6509 }
6510
6511 LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*pwallet);
6512 auto locked_chain = pwallet->chain().lock();
6513 LOCK2(pwallet->cs_wallet, spk_man.cs_KeyStore);
6514 QtumDGP qtumDGP(globalState.get(), fGettingValuesDGP);
6515 uint64_t blockGasLimit = qtumDGP.getBlockGasLimit(::ChainActive().Height());
6516 uint64_t minGasPrice = CAmount(qtumDGP.getMinGasPrice(::ChainActive().Height()));
6517 CAmount nGasPrice = (minGasPrice>DEFAULT_GAS_PRICE)?minGasPrice:DEFAULT_GAS_PRICE;
6518 uint64_t nGasLimit=DEFAULT_GAS_LIMIT_OP_SEND;
6519 bool fCheckOutputs = true;
6520
6521 RPCHelpMan{"qrc20burn",
6522 "\nBurns token amount from owner address.\n",
6523 {
6524 {"contractaddress", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The contract address."},
6525 {"owneraddress", RPCArg::Type::STR, RPCArg::Optional::NO, "The token owner qtum address."},
6526 {"amount", RPCArg::Type::STR, RPCArg::Optional::NO, "The amount of tokens to burn. eg 0.1"},
6527 {"gasLimit", RPCArg::Type::AMOUNT, RPCArg::Optional::OMITTED, "The gas limit, default: "+i64tostr(DEFAULT_GAS_LIMIT_OP_SEND)+", max: "+i64tostr(blockGasLimit)},
6528 {"gasPrice", RPCArg::Type::AMOUNT, RPCArg::Optional::OMITTED, "The qtum price per gas unit, default: "+FormatMoney(nGasPrice)+", min:"+FormatMoney(minGasPrice)},
6529 {"checkOutputs", RPCArg::Type::BOOL, RPCArg::Optional::OMITTED, "Check outputs before send, default: true"},
6530 },
6531 RPCResult{
6532 RPCResult::Type::OBJ, "", "",
6533 {
6534 {RPCResult::Type::STR_HEX, "txid", "The transaction id"},
6535 }
6536 },
6537 RPCExamples{
6538 HelpExampleCli("qrc20burn", "\"eb23c0b3e6042821da281a2e2364feb22dd543e3\" \"QM72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1")
6539 + HelpExampleCli("qrc20burn", "\"eb23c0b3e6042821da281a2e2364feb22dd543e3\" \"QM72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1 "+i64tostr(DEFAULT_GAS_LIMIT_OP_SEND)+" "+FormatMoney(minGasPrice)+" true")
6540 + HelpExampleRpc("qrc20burn", "\"eb23c0b3e6042821da281a2e2364feb22dd543e3\" \"QM72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1")
6541 + HelpExampleRpc("qrc20burn", "\"eb23c0b3e6042821da281a2e2364feb22dd543e3\" \"QM72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1 "+i64tostr(DEFAULT_GAS_LIMIT_OP_SEND)+" "+FormatMoney(minGasPrice)+" true")
6542 },
6543 }.Check(request);
6544
6545 // Get mandatory parameters
6546 std::string contract = request.params[0].get_str();
6547 std::string owner = request.params[1].get_str();
6548 std::string tokenAmount = request.params[2].get_str();
6549
6550 // Get gas limit
6551 if (request.params.size() > 3){
6552 nGasLimit = request.params[3].get_int64();
6553 }
6554
6555 // Get gas price
6556 if (request.params.size() > 4){
6557 nGasPrice = AmountFromValue(request.params[4]);
6558 }
6559
6560 // Get check outputs flag
6561 if (request.params.size() > 5){
6562 fCheckOutputs = request.params[5].get_bool();
6563 }
6564
6565 // Set token parameters
6566 SendToken token(*locked_chain, pwallet, spk_man);
6567 token.setAddress(contract);
6568 token.setSender(owner);
6569 token.setGasLimit(i64tostr(nGasLimit));
6570 token.setGasPrice(FormatMoney(nGasPrice));
6571
6572 // Get decimals
6573 uint32_t decimals;
6574 if(!token.decimals(decimals))
6575 throw JSONRPCError(RPC_MISC_ERROR, "Fail to get decimals");
6576
6577 // Get token amount to burn
6578 dev::s256 nTokenAmount;
6579 if(!ParseToken(decimals, tokenAmount, nTokenAmount))
6580 throw JSONRPCError(RPC_MISC_ERROR, "Fail to get token amount");
6581
6582 // Get token owner balance
6583 std::string strBalance;
6584 if(!token.balanceOf(strBalance))
6585 throw JSONRPCError(RPC_MISC_ERROR, "Fail to get balance");
6586
6587 // Check if balance is enough to cover it
6588 dev::s256 balance(strBalance);
6589 if(balance < nTokenAmount)
6590 throw JSONRPCError(RPC_MISC_ERROR, "Not enough token balance");
6591
6592 // Check burn offline
6593 std::string value = nTokenAmount.str();
6594 bool success = false;
6595 if(fCheckOutputs)
6596 {
6597 token.setCheckGasForCall(true);
6598 if(!token.burn(value, success) || !success)
6599 throw JSONRPCError(RPC_MISC_ERROR, "Fail offline check for burn token amount");
6600 }
6601
6602 // Burn token amount
6603 if(!token.burn(value, success, true))
6604 throw JSONRPCError(RPC_MISC_ERROR, "Fail to burn token amount");
6605
6606 UniValue result(UniValue::VOBJ);
6607 result.pushKV("txid", token.getTxId());
6608 return result;
6609 }
6610
qrc20burnfrom(const JSONRPCRequest & request)6611 static UniValue qrc20burnfrom(const JSONRPCRequest& request)
6612 {
6613 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
6614 CWallet* const pwallet = wallet.get();
6615
6616 if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
6617 return NullUniValue;
6618 }
6619
6620 LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*pwallet);
6621 auto locked_chain = pwallet->chain().lock();
6622 LOCK2(pwallet->cs_wallet, spk_man.cs_KeyStore);
6623 QtumDGP qtumDGP(globalState.get(), fGettingValuesDGP);
6624 uint64_t blockGasLimit = qtumDGP.getBlockGasLimit(::ChainActive().Height());
6625 uint64_t minGasPrice = CAmount(qtumDGP.getMinGasPrice(::ChainActive().Height()));
6626 CAmount nGasPrice = (minGasPrice>DEFAULT_GAS_PRICE)?minGasPrice:DEFAULT_GAS_PRICE;
6627 uint64_t nGasLimit=DEFAULT_GAS_LIMIT_OP_SEND;
6628 bool fCheckOutputs = true;
6629
6630 RPCHelpMan{"qrc20burnfrom",
6631 "\nBurns token amount from a given address.\n",
6632 {
6633 {"contractaddress", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The contract address."},
6634 {"owneraddress", RPCArg::Type::STR, RPCArg::Optional::NO, "The token owner qtum address."},
6635 {"spenderaddress", RPCArg::Type::STR, RPCArg::Optional::NO, "The token spender qtum address."},
6636 {"amount", RPCArg::Type::STR, RPCArg::Optional::NO, "The amount of token to burn. eg 0.1"},
6637 {"gasLimit", RPCArg::Type::AMOUNT, RPCArg::Optional::OMITTED, "The gas limit, default: "+i64tostr(DEFAULT_GAS_LIMIT_OP_SEND)+", max: "+i64tostr(blockGasLimit)},
6638 {"gasPrice", RPCArg::Type::AMOUNT, RPCArg::Optional::OMITTED, "The qtum price per gas unit, default: "+FormatMoney(nGasPrice)+", min:"+FormatMoney(minGasPrice)},
6639 {"checkOutputs", RPCArg::Type::BOOL, RPCArg::Optional::OMITTED, "Check outputs before send, default: true"},
6640 },
6641 RPCResult{
6642 RPCResult::Type::OBJ, "", "",
6643 {
6644 {RPCResult::Type::STR_HEX, "txid", "The transaction id"},
6645 }
6646 },
6647 RPCExamples{
6648 HelpExampleCli("qrc20burnfrom", "\"eb23c0b3e6042821da281a2e2364feb22dd543e3\" \"QX1GkJdye9WoUnrE2v6ZQhQ72EUVDtGXQX\" \"QM72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1")
6649 + HelpExampleCli("qrc20burnfrom", "\"eb23c0b3e6042821da281a2e2364feb22dd543e3\" \"QX1GkJdye9WoUnrE2v6ZQhQ72EUVDtGXQX\" \"QM72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1 "+i64tostr(DEFAULT_GAS_LIMIT_OP_SEND)+" "+FormatMoney(minGasPrice)+" true")
6650 + HelpExampleRpc("qrc20burnfrom", "\"eb23c0b3e6042821da281a2e2364feb22dd543e3\" \"QX1GkJdye9WoUnrE2v6ZQhQ72EUVDtGXQX\" \"QM72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1")
6651 + HelpExampleRpc("qrc20burnfrom", "\"eb23c0b3e6042821da281a2e2364feb22dd543e3\" \"QX1GkJdye9WoUnrE2v6ZQhQ72EUVDtGXQX\" \"QM72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1 "+i64tostr(DEFAULT_GAS_LIMIT_OP_SEND)+" "+FormatMoney(minGasPrice)+" true")
6652 },
6653 }.Check(request);
6654
6655 // Get mandatory parameters
6656 std::string contract = request.params[0].get_str();
6657 std::string owner = request.params[1].get_str();
6658 std::string spender = request.params[2].get_str();
6659 std::string tokenAmount = request.params[3].get_str();
6660
6661 // Get gas limit
6662 if (request.params.size() > 4){
6663 nGasLimit = request.params[4].get_int64();
6664 }
6665
6666 // Get gas price
6667 if (request.params.size() > 5){
6668 nGasPrice = AmountFromValue(request.params[5]);
6669 }
6670
6671 // Get check outputs flag
6672 if (request.params.size() > 6){
6673 fCheckOutputs = request.params[6].get_bool();
6674 }
6675
6676 // Set token parameters
6677 SendToken token(*locked_chain, pwallet, spk_man);
6678 token.setAddress(contract);
6679 token.setSender(spender);
6680 token.setGasLimit(i64tostr(nGasLimit));
6681 token.setGasPrice(FormatMoney(nGasPrice));
6682
6683 // Get decimals
6684 uint32_t decimals;
6685 if(!token.decimals(decimals))
6686 throw JSONRPCError(RPC_MISC_ERROR, "Fail to get decimals");
6687
6688 // Get token amount to burn
6689 dev::s256 nTokenAmount;
6690 if(!ParseToken(decimals, tokenAmount, nTokenAmount))
6691 throw JSONRPCError(RPC_MISC_ERROR, "Fail to get token amount");
6692
6693 // Get token spender allowance
6694 std::string strAllowance;
6695 if(!token.allowance(owner, spender, strAllowance))
6696 throw JSONRPCError(RPC_MISC_ERROR, "Fail to get allowance");
6697
6698 // Check if allowance is enough to cover it
6699 dev::s256 allowance(strAllowance);
6700 if(allowance < nTokenAmount)
6701 throw JSONRPCError(RPC_MISC_ERROR, "Not enough token allowance");
6702
6703 // Check burn from offline
6704 std::string value = nTokenAmount.str();
6705 bool success = false;
6706 if(fCheckOutputs)
6707 {
6708 token.setCheckGasForCall(true);
6709 if(!token.burnFrom(owner, value, success, false) || !success)
6710 throw JSONRPCError(RPC_MISC_ERROR, "Fail offline check for burn token amount from address");
6711 }
6712
6713 // Burn token amount
6714 if(!token.burnFrom(owner, value, success, true))
6715 throw JSONRPCError(RPC_MISC_ERROR, "Fail to burn token amount from address");
6716
6717 UniValue result(UniValue::VOBJ);
6718 result.pushKV("txid", token.getTxId());
6719 return result;
6720 }
6721
6722 UniValue abortrescan(const JSONRPCRequest& request); // in rpcdump.cpp
6723 UniValue dumpprivkey(const JSONRPCRequest& request); // in rpcdump.cpp
6724 UniValue importprivkey(const JSONRPCRequest& request);
6725 UniValue importaddress(const JSONRPCRequest& request);
6726 UniValue importpubkey(const JSONRPCRequest& request);
6727 UniValue dumpwallet(const JSONRPCRequest& request);
6728 UniValue importwallet(const JSONRPCRequest& request);
6729 UniValue importprunedfunds(const JSONRPCRequest& request);
6730 UniValue removeprunedfunds(const JSONRPCRequest& request);
6731 UniValue importmulti(const JSONRPCRequest& request);
6732
RegisterWalletRPCCommands(interfaces::Chain & chain,std::vector<std::unique_ptr<interfaces::Handler>> & handlers)6733 void RegisterWalletRPCCommands(interfaces::Chain& chain, std::vector<std::unique_ptr<interfaces::Handler>>& handlers)
6734 {
6735 // clang-format off
6736 static const CRPCCommand commands[] =
6737 { // category name actor (function) argNames
6738 // --------------------- ------------------------ ----------------------- ----------
6739 { "rawtransactions", "fundrawtransaction", &fundrawtransaction, {"hexstring","options","iswitness"} },
6740 { "wallet", "abandontransaction", &abandontransaction, {"txid"} },
6741 { "wallet", "abortrescan", &abortrescan, {} },
6742 { "wallet", "addmultisigaddress", &addmultisigaddress, {"nrequired","keys","label","address_type"} },
6743 { "wallet", "backupwallet", &backupwallet, {"destination"} },
6744 { "wallet", "bumpfee", &bumpfee, {"txid", "options"} },
6745 { "wallet", "createwallet", &createwallet, {"wallet_name", "disable_private_keys", "blank", "passphrase", "avoid_reuse"} },
6746 { "wallet", "dumpprivkey", &dumpprivkey, {"address"} },
6747 { "wallet", "dumpwallet", &dumpwallet, {"filename"} },
6748 { "wallet", "encryptwallet", &encryptwallet, {"passphrase"} },
6749 { "wallet", "getaddressesbylabel", &getaddressesbylabel, {"label"} },
6750 { "wallet", "getaddressinfo", &getaddressinfo, {"address"} },
6751 { "wallet", "getbalance", &getbalance, {"dummy","minconf","include_watchonly","avoid_reuse"} },
6752 { "wallet", "getnewaddress", &getnewaddress, {"label","address_type"} },
6753 { "wallet", "getrawchangeaddress", &getrawchangeaddress, {"address_type"} },
6754 { "wallet", "getreceivedbyaddress", &getreceivedbyaddress, {"address","minconf"} },
6755 { "wallet", "getreceivedbylabel", &getreceivedbylabel, {"label","minconf"} },
6756 { "wallet", "gettransaction", &gettransaction, {"txid","include_watchonly","verbose", "waitconf"} },
6757 { "wallet", "getunconfirmedbalance", &getunconfirmedbalance, {} },
6758 { "wallet", "getbalances", &getbalances, {} },
6759 { "wallet", "getwalletinfo", &getwalletinfo, {} },
6760 { "wallet", "importaddress", &importaddress, {"address","label","rescan","p2sh"} },
6761 { "wallet", "importmulti", &importmulti, {"requests","options"} },
6762 { "wallet", "importprivkey", &importprivkey, {"privkey","label","rescan"} },
6763 { "wallet", "importprunedfunds", &importprunedfunds, {"rawtransaction","txoutproof"} },
6764 { "wallet", "importpubkey", &importpubkey, {"pubkey","label","rescan"} },
6765 { "wallet", "importwallet", &importwallet, {"filename"} },
6766 { "wallet", "keypoolrefill", &keypoolrefill, {"newsize"} },
6767 { "wallet", "listaddressgroupings", &listaddressgroupings, {} },
6768 { "wallet", "listlabels", &listlabels, {"purpose"} },
6769 { "wallet", "listlockunspent", &listlockunspent, {} },
6770 { "wallet", "listreceivedbyaddress", &listreceivedbyaddress, {"minconf","include_empty","include_watchonly","address_filter"} },
6771 { "wallet", "listreceivedbylabel", &listreceivedbylabel, {"minconf","include_empty","include_watchonly"} },
6772 { "wallet", "listsinceblock", &listsinceblock, {"blockhash","target_confirmations","include_watchonly","include_removed"} },
6773 { "wallet", "listtransactions", &listtransactions, {"label|dummy","count","skip","include_watchonly"} },
6774 { "wallet", "listunspent", &listunspent, {"minconf","maxconf","addresses","include_unsafe","query_options"} },
6775 { "wallet", "listwalletdir", &listwalletdir, {} },
6776 { "wallet", "listwallets", &listwallets, {} },
6777 { "wallet", "loadwallet", &loadwallet, {"filename"} },
6778 { "wallet", "lockunspent", &lockunspent, {"unlock","transactions"} },
6779 { "wallet", "removeprunedfunds", &removeprunedfunds, {"txid"} },
6780 { "wallet", "rescanblockchain", &rescanblockchain, {"start_height", "stop_height"} },
6781 { "wallet", "sendmany", &sendmany, {"dummy","amounts","minconf","comment","subtractfeefrom","replaceable","conf_target","estimate_mode"} },
6782 { "wallet", "sendmanywithdupes", &sendmanywithdupes, {"fromaccount","amounts","minconf","comment","subtractfeefrom"} },
6783 { "wallet", "sendtoaddress", &sendtoaddress, {"address","amount","comment","comment_to","subtractfeefromamount","replaceable","conf_target","estimate_mode","avoid_reuse","senderAddress","changeToSender"} },
6784 { "wallet", "splitutxosforaddress", &splitutxosforaddress, {"address","minValue","maxValue","maxOutputs"} },
6785 { "wallet", "sethdseed", &sethdseed, {"newkeypool","seed"} },
6786 { "wallet", "setlabel", &setlabel, {"address","label"} },
6787 { "wallet", "settxfee", &settxfee, {"amount"} },
6788 { "wallet", "setwalletflag", &setwalletflag, {"flag","value"} },
6789 { "wallet", "signmessage", &signmessage, {"address","message"} },
6790 { "wallet", "signrawtransactionwithwallet", &signrawtransactionwithwallet, {"hexstring","prevtxs","sighashtype"} },
6791 { "wallet", "signrawsendertransactionwithwallet", &signrawsendertransactionwithwallet, {"hexstring","sighashtype"} },
6792 { "wallet", "unloadwallet", &unloadwallet, {"wallet_name"} },
6793 { "wallet", "walletcreatefundedpsbt", &walletcreatefundedpsbt, {"inputs","outputs","locktime","options","bip32derivs"} },
6794 { "wallet", "walletlock", &walletlock, {} },
6795 { "wallet", "walletpassphrase", &walletpassphrase, {"passphrase","timeout","stakingonly"} },
6796 { "wallet", "walletpassphrasechange", &walletpassphrasechange, {"oldpassphrase","newpassphrase"} },
6797 { "wallet", "walletprocesspsbt", &walletprocesspsbt, {"psbt","sign","sighashtype","bip32derivs"} },
6798 { "wallet", "reservebalance", &reservebalance, {"reserve", "amount"} },
6799 { "wallet", "createcontract", &createcontract, {"bytecode", "gasLimit", "gasPrice", "senderAddress", "broadcast", "changeToSender"} },
6800 { "wallet", "sendtocontract", &sendtocontract, {"contractaddress", "bytecode", "amount", "gasLimit", "gasPrice", "senderAddress", "broadcast", "changeToSender"} },
6801 { "wallet", "removedelegationforaddress", &removedelegationforaddress, {"address", "gasLimit", "gasPrice"} },
6802 { "wallet", "setdelegateforaddress", &setdelegateforaddress, {"staker", "fee", "address", "gasLimit", "gasPrice"} },
6803 { "wallet", "setsuperstakervaluesforaddress", &setsuperstakervaluesforaddress, {"params"} },
6804 { "wallet", "listsuperstakercustomvalues", &listsuperstakercustomvalues, {} },
6805 { "wallet", "listsuperstakervaluesforaddress", &listsuperstakervaluesforaddress, {"address"} },
6806 { "wallet", "removesuperstakervaluesforaddress", &removesuperstakervaluesforaddress, {"address"} },
6807 { "wallet", "qrc20approve", &qrc20approve, {"contractaddress", "owneraddress", "spenderaddress", "amount", "gasLimit", "gasPrice", "checkOutputs"} },
6808 { "wallet", "qrc20transfer", &qrc20transfer, {"contractaddress", "owneraddress", "addressto", "amount", "gasLimit", "gasPrice", "checkOutputs"} },
6809 { "wallet", "qrc20transferfrom", &qrc20transferfrom, {"contractaddress", "owneraddress", "spenderaddress", "receiveraddress", "amount", "gasLimit", "gasPrice", "checkOutputs"} },
6810 { "wallet", "qrc20burn", &qrc20burn, {"contractaddress", "owneraddress", "amount", "gasLimit", "gasPrice", "checkOutputs"} },
6811 { "wallet", "qrc20burnfrom", &qrc20burnfrom, {"contractaddress", "owneraddress", "spenderaddress", "amount", "gasLimit", "gasPrice", "checkOutputs"} },
6812 };
6813 // clang-format on
6814
6815 for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++)
6816 handlers.emplace_back(chain.handleRpc(commands[vcidx]));
6817 }
6818
6819 interfaces::Chain* g_rpc_chain = nullptr;
6820