1 // Copyright (c) 2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2012 The Bitcoin developers
3 // Distributed under the MIT/X11 software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
8 #include "bitcoinrpc.h"
15 using namespace json_spirit;
18 int64_t nWalletUnlockTime;
19 static CCriticalSection cs_nWalletUnlockTime;
21 extern int64_t nReserveBalance;
22 extern void TxToJSON(const CTransaction& tx, const uint256& hashBlock, json_spirit::Object& entry);
24 string HelpRequiringPassphrase()
26 return pwalletMain->IsCrypted()
27 ? "\n\nRequires wallet passphrase to be set with walletpassphrase first"
31 void EnsureWalletIsUnlocked()
33 if (pwalletMain->IsLocked())
34 throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first.");
35 if (fWalletUnlockMintOnly)
36 throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Wallet unlocked for block minting only.");
39 void WalletTxToJSON(const CWalletTx& wtx, Object& entry)
41 int confirms = wtx.GetDepthInMainChain();
42 entry.push_back(Pair("confirmations", confirms));
43 if (wtx.IsCoinBase() || wtx.IsCoinStake())
44 entry.push_back(Pair("generated", true));
47 entry.push_back(Pair("blockhash", wtx.hashBlock.GetHex()));
48 entry.push_back(Pair("blockindex", wtx.nIndex));
49 entry.push_back(Pair("blocktime", (int64_t)(mapBlockIndex[wtx.hashBlock]->nTime)));
51 entry.push_back(Pair("txid", wtx.GetHash().GetHex()));
52 entry.push_back(Pair("time", (int64_t)wtx.GetTxTime()));
53 entry.push_back(Pair("timereceived", (int64_t)wtx.nTimeReceived));
54 for(const auto& item : wtx.mapValue)
55 entry.push_back(Pair(item.first, item.second));
58 string AccountFromValue(const Value& value)
60 auto strAccount = value.get_str();
61 if (strAccount == "*")
62 throw JSONRPCError(RPC_WALLET_INVALID_ACCOUNT_NAME, "Invalid account name");
66 Value getinfo(const Array& params, bool fHelp)
68 if (fHelp || params.size() != 0)
71 "Returns an object containing various state info.");
74 GetProxy(NET_IPV4, proxy);
76 Object obj, diff, timestamping;
77 obj.push_back(Pair("version", FormatFullVersion()));
78 obj.push_back(Pair("protocolversion",(int)PROTOCOL_VERSION));
79 obj.push_back(Pair("walletversion", pwalletMain->GetVersion()));
80 obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance())));
81 obj.push_back(Pair("unspendable", ValueFromAmount(pwalletMain->GetWatchOnlyBalance())));
82 obj.push_back(Pair("newmint", ValueFromAmount(pwalletMain->GetNewMint())));
83 obj.push_back(Pair("stake", ValueFromAmount(pwalletMain->GetStake())));
84 obj.push_back(Pair("blocks", (int)nBestHeight));
86 timestamping.push_back(Pair("systemclock", GetTime()));
87 timestamping.push_back(Pair("adjustedtime", GetAdjustedTime()));
89 auto nNtpOffset = GetNtpOffset(),
90 nP2POffset = GetNodesOffset();
92 timestamping.push_back(Pair("ntpoffset", nNtpOffset != numeric_limits<int64_t>::max() ? nNtpOffset : Value::null));
93 timestamping.push_back(Pair("p2poffset", nP2POffset != numeric_limits<int64_t>::max() ? nP2POffset : Value::null));
95 obj.push_back(Pair("timestamping", timestamping));
97 obj.push_back(Pair("moneysupply", ValueFromAmount(pindexBest->nMoneySupply)));
98 obj.push_back(Pair("connections", (int)vNodes.size()));
99 obj.push_back(Pair("proxy", (proxy.IsValid() ? proxy.ToStringIPPort() : string())));
100 obj.push_back(Pair("ip", addrSeenByPeer.ToStringIP()));
102 diff.push_back(Pair("proof-of-work", GetDifficulty()));
103 diff.push_back(Pair("proof-of-stake", GetDifficulty(GetLastBlockIndex(pindexBest, true))));
104 obj.push_back(Pair("difficulty", diff));
106 obj.push_back(Pair("testnet", fTestNet));
107 obj.push_back(Pair("keypoololdest", (int64_t)pwalletMain->GetOldestKeyPoolTime()));
108 obj.push_back(Pair("keypoolsize", (int)pwalletMain->GetKeyPoolSize()));
109 obj.push_back(Pair("paytxfee", ValueFromAmount(nTransactionFee)));
110 obj.push_back(Pair("mininput", ValueFromAmount(nMinimumInputValue)));
111 if (pwalletMain->IsCrypted())
112 obj.push_back(Pair("unlocked_until", (int64_t)nWalletUnlockTime / 1000));
113 obj.push_back(Pair("errors", GetWarnings("statusbar")));
117 Value getnewaddress(const Array& params, bool fHelp)
119 if (fHelp || params.size() > 1)
121 "getnewaddress [account]\n"
122 "Returns a new NovaCoin address for receiving payments. "
123 "If [account] is specified (recommended), it is added to the address book "
124 "so payments received with the address will be credited to [account].");
126 // Parse the account first so we don't generate a key if there's an error
128 if (params.size() > 0)
129 strAccount = AccountFromValue(params[0]);
131 if (!pwalletMain->IsLocked())
132 pwalletMain->TopUpKeyPool();
134 // Generate a new key that is added to wallet
136 if (!pwalletMain->GetKeyFromPool(newKey, false))
137 throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
138 CBitcoinAddress address(newKey.GetID());
140 pwalletMain->SetAddressBookName(address, strAccount);
142 return address.ToString();
146 CBitcoinAddress GetAccountAddress(string strAccount, bool bForceNew=false)
148 CWalletDB walletdb(pwalletMain->strWalletFile);
151 walletdb.ReadAccount(strAccount, account);
153 bool bKeyUsed = false;
155 // Check if the current key has been used
156 if (account.vchPubKey.IsValid())
158 CScript scriptPubKey;
159 scriptPubKey.SetDestination(account.vchPubKey.GetID());
160 for (auto it = pwalletMain->mapWallet.begin();
161 it != pwalletMain->mapWallet.end() && account.vchPubKey.IsValid();
164 const CWalletTx& wtx = (*it).second;
165 for(const CTxOut& txout : wtx.vout)
166 if (txout.scriptPubKey == scriptPubKey)
171 // Generate a new key
172 if (!account.vchPubKey.IsValid() || bForceNew || bKeyUsed)
174 if (!pwalletMain->GetKeyFromPool(account.vchPubKey, false))
175 throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
177 pwalletMain->SetAddressBookName(account.vchPubKey.GetID(), strAccount);
178 walletdb.WriteAccount(strAccount, account);
181 return CBitcoinAddress(account.vchPubKey.GetID());
184 Value getaccountaddress(const Array& params, bool fHelp)
186 if (fHelp || params.size() != 1)
188 "getaccountaddress <account>\n"
189 "Returns the current NovaCoin address for receiving payments to this account.");
191 // Parse the account first so we don't generate a key if there's an error
192 auto strAccount = AccountFromValue(params[0]);
196 ret = GetAccountAddress(strAccount).ToString();
203 Value setaccount(const Array& params, bool fHelp)
205 if (fHelp || params.size() < 1 || params.size() > 2)
207 "setaccount <novacoinaddress> <account>\n"
208 "Sets the account associated with the given address.");
210 CBitcoinAddress address(params[0].get_str());
211 if (!address.IsValid())
212 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
216 if (params.size() > 1)
217 strAccount = AccountFromValue(params[1]);
219 // Detect when changing the account of an address that is the 'unused current key' of another account:
220 if (pwalletMain->mapAddressBook.count(address))
222 auto strOldAccount = pwalletMain->mapAddressBook[address];
223 if (address == GetAccountAddress(strOldAccount))
224 GetAccountAddress(strOldAccount, true);
227 pwalletMain->SetAddressBookName(address, strAccount);
233 Value getaccount(const Array& params, bool fHelp)
235 if (fHelp || params.size() != 1)
237 "getaccount <novacoinaddress>\n"
238 "Returns the account associated with the given address.");
240 CBitcoinAddress address(params[0].get_str());
241 if (!address.IsValid())
242 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
245 auto mi = pwalletMain->mapAddressBook.find(address);
246 if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.empty())
247 strAccount = (*mi).second;
252 Value getaddressesbyaccount(const Array& params, bool fHelp)
254 if (fHelp || params.size() != 1)
256 "getaddressesbyaccount <account>\n"
257 "Returns the list of addresses for the given account.");
259 auto strAccount = AccountFromValue(params[0]);
261 // Find all addresses that have the given account
263 for(const auto& item : pwalletMain->mapAddressBook)
265 const CBitcoinAddress& address = item.first;
266 const string& strName = item.second;
267 if (strName == strAccount)
268 ret.push_back(address.ToString());
273 Value mergecoins(const Array& params, bool fHelp)
275 if (fHelp || params.size() != 3)
277 "mergecoins <amount> <minvalue> <outputvalue>\n"
278 "<amount> is resulting inputs sum\n"
279 "<minvalue> is minimum value of inputs which are used in join process\n"
280 "<outputvalue> is resulting value of inputs which will be created\n"
281 "All values are real and and rounded to the nearest " + FormatMoney(nMinimumInputValue)
282 + HelpRequiringPassphrase());
284 if (pwalletMain->IsLocked())
285 throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first.");
288 auto nAmount = AmountFromValue(params[0]);
291 auto nMinValue = AmountFromValue(params[1]);
294 auto nOutputValue = AmountFromValue(params[2]);
296 if (nAmount < nMinimumInputValue)
297 throw JSONRPCError(-101, "Send amount too small");
299 if (nMinValue < nMinimumInputValue)
300 throw JSONRPCError(-101, "Max value too small");
302 if (nOutputValue < nMinimumInputValue)
303 throw JSONRPCError(-101, "Output value too small");
305 if (nOutputValue < nMinValue)
306 throw JSONRPCError(-101, "Output value is lower than min value");
308 list<uint256> listMerged;
309 if (!pwalletMain->MergeCoins(nAmount, nMinValue, nOutputValue, listMerged))
313 for(const uint256 txHash : listMerged)
314 mergedHashes.push_back(txHash.GetHex());
319 Value sendtoaddress(const Array& params, bool fHelp)
321 if (fHelp || params.size() < 2 || params.size() > 4)
323 "sendtoaddress <novacoinaddress> <amount> [comment] [comment-to]\n"
324 "<amount> is a real and is rounded to the nearest " + FormatMoney(nMinimumInputValue)
325 + HelpRequiringPassphrase());
328 CScript scriptPubKey;
329 auto strAddress = params[0].get_str();
331 CBitcoinAddress address(strAddress);
332 if (address.IsValid())
333 scriptPubKey.SetAddress(address);
335 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
338 auto nAmount = AmountFromValue(params[1]);
340 if (nAmount < nMinimumInputValue)
341 throw JSONRPCError(-101, "Send amount too small");
345 if (params.size() > 2 && params[2].type() != null_type && !params[2].get_str().empty())
346 wtx.mapValue["comment"] = params[2].get_str();
347 if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
348 wtx.mapValue["to"] = params[3].get_str();
350 if (pwalletMain->IsLocked())
351 throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first.");
353 auto strError = pwalletMain->SendMoney(scriptPubKey, nAmount, wtx);
354 if (!strError.empty())
355 throw JSONRPCError(RPC_WALLET_ERROR, strError);
357 return wtx.GetHash().GetHex();
360 Value listaddressgroupings(const Array& params, bool fHelp)
364 "listaddressgroupings\n"
365 "Lists groups of addresses which have had their common ownership\n"
366 "made public by common use as inputs or as the resulting change\n"
367 "in past transactions");
370 map<CBitcoinAddress, int64_t> balances = pwalletMain->GetAddressBalances();
371 for(auto grouping : pwalletMain->GetAddressGroupings())
374 for(CBitcoinAddress address : grouping)
377 addressInfo.push_back(address.ToString());
378 addressInfo.push_back(ValueFromAmount(balances[address]));
380 LOCK(pwalletMain->cs_wallet);
381 if (pwalletMain->mapAddressBook.find(address) != pwalletMain->mapAddressBook.end())
382 addressInfo.push_back(pwalletMain->mapAddressBook.find(address)->second);
384 jsonGrouping.push_back(addressInfo);
386 jsonGroupings.push_back(jsonGrouping);
388 return jsonGroupings;
391 Value signmessage(const Array& params, bool fHelp)
393 if (fHelp || params.size() != 2)
395 "signmessage <novacoinaddress> <message>\n"
396 "Sign a message with the private key of an address");
398 EnsureWalletIsUnlocked();
400 auto strAddress = params[0].get_str();
401 auto strMessage = params[1].get_str();
403 CBitcoinAddress addr(strAddress);
405 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
408 if (!addr.GetKeyID(keyID))
409 throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
412 if (!pwalletMain->GetKey(keyID, key))
413 throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available");
415 CDataStream ss(SER_GETHASH, 0);
416 ss << strMessageMagic;
419 vector<unsigned char> vchSig;
420 if (!key.SignCompact(Hash(ss.begin(), ss.end()), vchSig))
421 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed");
423 return EncodeBase64(&vchSig[0], vchSig.size());
426 Value verifymessage(const Array& params, bool fHelp)
428 if (fHelp || params.size() != 3)
430 "verifymessage <novacoinaddress> <signature> <message>\n"
431 "Verify a signed message");
433 auto strAddress = params[0].get_str();
434 auto strSign = params[1].get_str();
435 auto strMessage = params[2].get_str();
437 CBitcoinAddress addr(strAddress);
439 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
442 if (!addr.GetKeyID(keyID))
443 throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
445 bool fInvalid = false;
446 vector<unsigned char> vchSig = DecodeBase64(strSign.c_str(), &fInvalid);
449 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Malformed base64 encoding");
451 CDataStream ss(SER_GETHASH, 0);
452 ss << strMessageMagic;
456 if (!key.SetCompactSignature(Hash(ss.begin(), ss.end()), vchSig))
459 return (key.GetID() == keyID);
463 Value getreceivedbyaddress(const Array& params, bool fHelp)
465 if (fHelp || params.size() < 1 || params.size() > 2)
467 "getreceivedbyaddress <novacoinaddress> [minconf=1]\n"
468 "Returns the total amount received by <novacoinaddress> in transactions with at least [minconf] confirmations.");
471 auto address = CBitcoinAddress(params[0].get_str());
472 if (!address.IsValid())
473 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
474 if (!IsMine(*pwalletMain,address))
477 // Minimum confirmations
479 if (params.size() > 1)
480 nMinDepth = params[1].get_int();
483 for (auto it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
485 const CWalletTx& wtx = (*it).second;
486 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
488 for(const CTxOut& txout : wtx.vout)
490 CBitcoinAddress addressRet;
491 if (!ExtractAddress(*pwalletMain, txout.scriptPubKey, addressRet))
493 if (addressRet == address)
494 if (wtx.GetDepthInMainChain() >= nMinDepth)
495 nAmount += txout.nValue;
499 return ValueFromAmount(nAmount);
502 void GetAccountAddresses(string strAccount, set<CBitcoinAddress>& setAddress)
504 for(const auto& item : pwalletMain->mapAddressBook)
506 const CBitcoinAddress& address = item.first;
507 const string& strName = item.second;
508 if (strName == strAccount)
509 setAddress.insert(address);
513 Value getreceivedbyaccount(const Array& params, bool fHelp)
515 if (fHelp || params.size() < 1 || params.size() > 2)
517 "getreceivedbyaccount <account> [minconf=1]\n"
518 "Returns the total amount received by addresses with <account> in transactions with at least [minconf] confirmations.");
520 // Minimum confirmations
522 if (params.size() > 1)
523 nMinDepth = params[1].get_int();
525 // Get the set of pub keys assigned to account
526 auto strAccount = AccountFromValue(params[0]);
527 set<CBitcoinAddress> setAddress;
528 GetAccountAddresses(strAccount, setAddress);
532 for (auto it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
534 const CWalletTx& wtx = (*it).second;
535 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
538 for(const CTxOut& txout : wtx.vout)
540 CBitcoinAddress address;
541 if (ExtractAddress(*pwalletMain, txout.scriptPubKey, address) && IsMine(*pwalletMain, address) && setAddress.count(address))
542 if (wtx.GetDepthInMainChain() >= nMinDepth)
543 nAmount += txout.nValue;
547 return (double)nAmount / (double)COIN;
551 int64_t GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth, const isminefilter& filter)
553 int64_t nBalance = 0;
555 // Tally wallet transactions
556 for (auto it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
558 const CWalletTx& wtx = (*it).second;
562 int64_t nGenerated, nReceived, nSent, nFee;
563 wtx.GetAccountAmounts(strAccount, nGenerated, nReceived, nSent, nFee, filter);
565 if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth)
566 nBalance += nReceived;
567 nBalance += nGenerated - nSent - nFee;
570 // Tally internal accounting entries
571 nBalance += walletdb.GetAccountCreditDebit(strAccount);
576 int64_t GetAccountBalance(const string& strAccount, int nMinDepth, const isminefilter& filter)
578 CWalletDB walletdb(pwalletMain->strWalletFile);
579 return GetAccountBalance(walletdb, strAccount, nMinDepth, filter);
583 Value getbalance(const Array& params, bool fHelp)
585 if (fHelp || params.size() > 3)
587 "getbalance [account] [minconf=1] [watchonly=0]\n"
588 "If [account] is not specified, returns the server's total available balance.\n"
589 "If [account] is specified, returns the balance in the account.\n"
590 "if [includeWatchonly] is specified, include balance in watchonly addresses (see 'importaddress').");
592 if (params.size() == 0)
593 return ValueFromAmount(pwalletMain->GetBalance());
596 if (params.size() > 1)
597 nMinDepth = params[1].get_int();
598 isminefilter filter = MINE_SPENDABLE;
599 if(params.size() > 2)
600 if(params[2].get_bool())
601 filter = filter | MINE_WATCH_ONLY;
603 if (params[0].get_str() == "*") {
604 // Calculate total balance a different way from GetBalance()
605 // (GetBalance() sums up all unspent TxOuts)
606 // getbalance and getbalance '*' 0 should return the same number.
608 for (auto it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
610 const CWalletTx& wtx = (*it).second;
611 if (!wtx.IsTrusted())
614 int64_t allGeneratedImmature, allGeneratedMature, allFee;
615 allGeneratedImmature = allGeneratedMature = allFee = 0;
617 string strSentAccount;
618 list<pair<CBitcoinAddress, int64_t> > listReceived;
619 list<pair<CBitcoinAddress, int64_t> > listSent;
620 wtx.GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount, filter);
621 if (wtx.GetDepthInMainChain() >= nMinDepth)
623 for(const auto& r : listReceived)
624 nBalance += r.second;
626 for(const auto& r : listSent)
627 nBalance -= r.second;
629 nBalance += allGeneratedMature;
631 return ValueFromAmount(nBalance);
634 auto strAccount = AccountFromValue(params[0]);
636 auto nBalance = GetAccountBalance(strAccount, nMinDepth, filter);
638 return ValueFromAmount(nBalance);
642 Value movecmd(const Array& params, bool fHelp)
644 if (fHelp || params.size() < 3 || params.size() > 5)
646 "move <fromaccount> <toaccount> <amount> [minconf=1] [comment]\n"
647 "Move from one account in your wallet to another.");
649 auto strFrom = AccountFromValue(params[0]);
650 auto strTo = AccountFromValue(params[1]);
651 auto nAmount = AmountFromValue(params[2]);
653 if (nAmount < nMinimumInputValue)
654 throw JSONRPCError(-101, "Send amount too small");
656 if (params.size() > 3)
657 // unused parameter, used to be nMinDepth, keep type-checking it though
658 (void)params[3].get_int();
660 if (params.size() > 4)
661 strComment = params[4].get_str();
663 CWalletDB walletdb(pwalletMain->strWalletFile);
664 if (!walletdb.TxnBegin())
665 throw JSONRPCError(RPC_DATABASE_ERROR, "database error");
667 auto nNow = GetAdjustedTime();
670 CAccountingEntry debit;
671 debit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb);
672 debit.strAccount = strFrom;
673 debit.nCreditDebit = -nAmount;
675 debit.strOtherAccount = strTo;
676 debit.strComment = strComment;
677 walletdb.WriteAccountingEntry(debit);
680 CAccountingEntry credit;
681 credit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb);
682 credit.strAccount = strTo;
683 credit.nCreditDebit = nAmount;
685 credit.strOtherAccount = strFrom;
686 credit.strComment = strComment;
687 walletdb.WriteAccountingEntry(credit);
689 if (!walletdb.TxnCommit())
690 throw JSONRPCError(RPC_DATABASE_ERROR, "database error");
696 Value sendfrom(const Array& params, bool fHelp)
698 if (fHelp || params.size() < 3 || params.size() > 6)
700 "sendfrom <fromaccount> <tonovacoinaddress> <amount> [minconf=1] [comment] [comment-to]\n"
701 "<amount> is a real and is rounded to the nearest " + FormatMoney(nMinimumInputValue)
702 + HelpRequiringPassphrase());
704 auto strAccount = AccountFromValue(params[0]);
707 CScript scriptPubKey;
709 CBitcoinAddress address(params[1].get_str());
710 if (address.IsValid())
711 scriptPubKey.SetAddress(address);
713 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
716 auto nAmount = AmountFromValue(params[2]);
718 if (nAmount < nMinimumInputValue)
719 throw JSONRPCError(-101, "Send amount too small");
722 if (params.size() > 3)
723 nMinDepth = params[3].get_int();
726 wtx.strFromAccount = strAccount;
727 if (params.size() > 4 && params[4].type() != null_type && !params[4].get_str().empty())
728 wtx.mapValue["comment"] = params[4].get_str();
729 if (params.size() > 5 && params[5].type() != null_type && !params[5].get_str().empty())
730 wtx.mapValue["to"] = params[5].get_str();
732 EnsureWalletIsUnlocked();
735 auto nBalance = GetAccountBalance(strAccount, nMinDepth, MINE_SPENDABLE);
736 if (nAmount > nBalance)
737 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds");
740 auto strError = pwalletMain->SendMoney(scriptPubKey, nAmount, wtx);
741 if (!strError.empty())
742 throw JSONRPCError(RPC_WALLET_ERROR, strError);
744 return wtx.GetHash().GetHex();
748 Value sendmany(const Array& params, bool fHelp)
750 if (fHelp || params.size() < 2 || params.size() > 4)
752 "sendmany <fromaccount> '{address:amount,...}' [minconf=1] [comment]\n"
753 "amounts are double-precision floating point numbers"
754 + HelpRequiringPassphrase());
756 auto strAccount = AccountFromValue(params[0]);
757 auto sendTo = params[1].get_obj();
759 if (params.size() > 2)
760 nMinDepth = params[2].get_int();
763 wtx.strFromAccount = strAccount;
764 if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
765 wtx.mapValue["comment"] = params[3].get_str();
767 set<CBitcoinAddress> setAddress;
768 vector<pair<CScript, int64_t> > vecSend;
770 int64_t totalAmount = 0;
771 for(const Pair& s : sendTo)
773 CBitcoinAddress address(s.name_);
774 if (!address.IsValid())
775 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid NovaCoin address: ")+s.name_);
777 if (!address.IsPair())
779 if (setAddress.count(address))
780 throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+s.name_);
781 setAddress.insert(address);
784 CScript scriptPubKey;
785 scriptPubKey.SetAddress(address);
786 auto nAmount = AmountFromValue(s.value_);
788 if (nAmount < nMinimumInputValue)
789 throw JSONRPCError(-101, "Send amount too small");
791 totalAmount += nAmount;
793 vecSend.push_back({ scriptPubKey, nAmount });
796 EnsureWalletIsUnlocked();
799 auto nBalance = GetAccountBalance(strAccount, nMinDepth, MINE_SPENDABLE);
800 if (totalAmount > nBalance)
801 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds");
804 CReserveKey keyChange(pwalletMain);
805 int64_t nFeeRequired = 0;
806 bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired);
809 auto nTotal = pwalletMain->GetBalance(), nWatchOnly = pwalletMain->GetWatchOnlyBalance();
810 if (totalAmount + nFeeRequired > nTotal - nWatchOnly)
811 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds");
812 throw JSONRPCError(RPC_WALLET_ERROR, "Transaction creation failed");
814 if (!pwalletMain->CommitTransaction(wtx, keyChange))
815 throw JSONRPCError(RPC_WALLET_ERROR, "Transaction commit failed");
817 return wtx.GetHash().GetHex();
820 Value addmultisigaddress(const Array& params, bool fHelp)
822 if (fHelp || params.size() < 2 || params.size() > 3)
824 string msg = "addmultisigaddress <nrequired> <'[\"key\",\"key\"]'> [account]\n"
825 "Add a nrequired-to-sign multisignature address to the wallet\"\n"
826 "each key is a NovaCoin address or hex-encoded public key\n"
827 "If [account] is specified, assign address to [account].";
828 throw runtime_error(msg);
831 int nRequired = params[0].get_int();
832 const Array& keys = params[1].get_array();
834 if (params.size() > 2)
835 strAccount = AccountFromValue(params[2]);
837 // Gather public keys
839 throw runtime_error("a multisignature address must require at least one key to redeem");
840 if ((int)keys.size() < nRequired)
842 strprintf("not enough keys supplied "
843 "(got %" PRIszu " keys, but need at least %d to redeem)", keys.size(), nRequired));
844 if (keys.size() > 16)
845 throw runtime_error("Number of addresses involved in the multisignature address creation > 16\nReduce the number");
846 vector<CPubKey> pubkeys;
847 pubkeys.resize(keys.size());
848 for (unsigned int i = 0; i < keys.size(); i++)
850 const auto& ks = keys[i].get_str();
852 // Case 1: Bitcoin address and we have full public key:
853 CBitcoinAddress address(ks);
854 if (address.IsValid())
857 if (!address.GetKeyID(keyID))
859 strprintf("%s does not refer to a key",ks.c_str()));
861 if (!pwalletMain->GetPubKey(keyID, vchPubKey))
863 strprintf("no full public key for address %s",ks.c_str()));
864 if (!vchPubKey.IsValid())
865 throw runtime_error(" Invalid public key: "+ks);
866 pubkeys[i] = vchPubKey;
869 // Case 2: hex public key
872 CPubKey vchPubKey(ParseHex(ks));
873 if (!vchPubKey.IsValid())
874 throw runtime_error(" Invalid public key: "+ks);
875 pubkeys[i] = vchPubKey;
879 throw runtime_error(" Invalid public key: "+ks);
883 // Construct using pay-to-script-hash:
885 inner.SetMultisig(nRequired, pubkeys);
887 if (inner.size() > MAX_SCRIPT_ELEMENT_SIZE)
889 strprintf("redeemScript exceeds size limit: %" PRIszu " > %d", inner.size(), MAX_SCRIPT_ELEMENT_SIZE));
891 pwalletMain->AddCScript(inner);
892 CBitcoinAddress address{ CScriptID(inner) }; // "most vexing parse"
894 pwalletMain->SetAddressBookName(address, strAccount);
895 return address.ToString();
898 Value addredeemscript(const Array& params, bool fHelp)
900 if (fHelp || params.size() < 1 || params.size() > 2)
902 string msg = "addredeemscript <redeemScript> [account]\n"
903 "Add a P2SH address with a specified redeemScript to the wallet.\n"
904 "If [account] is specified, assign address to [account].";
905 throw runtime_error(msg);
909 if (params.size() > 1)
910 strAccount = AccountFromValue(params[1]);
912 // Construct using pay-to-script-hash:
913 auto innerData = ParseHexV(params[0], "redeemScript");
914 CScript inner(innerData.begin(), innerData.end());
915 pwalletMain->AddCScript(inner);
916 CBitcoinAddress address{ CScriptID(inner) }; // "most vexing parse"
918 pwalletMain->SetAddressBookName(address, strAccount);
919 return address.ToString();
929 nConf = numeric_limits<int>::max();
933 Value ListReceived(const Array& params, bool fByAccounts)
935 // Minimum confirmations
937 if (params.size() > 0)
938 nMinDepth = params[0].get_int();
940 // Whether to include empty accounts
941 bool fIncludeEmpty = false;
942 if (params.size() > 1)
943 fIncludeEmpty = params[1].get_bool();
946 map<CBitcoinAddress, tallyitem> mapTally;
947 for (const auto &wit : pwalletMain->mapWallet)
949 const auto& wtx = wit.second;
951 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
954 int nDepth = wtx.GetDepthInMainChain();
955 if (nDepth < nMinDepth)
958 for(const auto& txout : wtx.vout)
960 CTxDestination address;
961 if (!ExtractDestination(txout.scriptPubKey, address) || !IsMine(*pwalletMain, address))
964 auto& item = mapTally[address];
965 item.nAmount += txout.nValue;
966 item.nConf = min(item.nConf, nDepth);
972 map<string, tallyitem> mapAccountTally;
973 for(const auto& item : pwalletMain->mapAddressBook)
975 const auto& address = item.first;
976 const auto& strAccount = item.second;
977 auto it = mapTally.find(address);
978 if (it == mapTally.end() && !fIncludeEmpty)
982 int nConf = numeric_limits<int>::max();
983 if (it != mapTally.end())
985 nAmount = (*it).second.nAmount;
986 nConf = (*it).second.nConf;
991 auto& item = mapAccountTally[strAccount];
992 item.nAmount += nAmount;
993 item.nConf = min(item.nConf, nConf);
998 obj.push_back(Pair("address", address.ToString()));
999 obj.push_back(Pair("account", strAccount));
1000 obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
1001 obj.push_back(Pair("confirmations", (nConf == numeric_limits<int>::max() ? 0 : nConf)));
1008 for (const auto &acc : mapAccountTally)
1010 auto nAmount = acc.second.nAmount;
1011 int nConf = acc.second.nConf;
1013 obj.push_back(Pair("account", acc.first));
1014 obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
1015 obj.push_back(Pair("confirmations", (nConf == numeric_limits<int>::max() ? 0 : nConf)));
1023 Value listreceivedbyaddress(const Array& params, bool fHelp)
1025 if (fHelp || params.size() > 2)
1026 throw runtime_error(
1027 "listreceivedbyaddress [minconf=1] [includeempty=false]\n"
1028 "[minconf] is the minimum number of confirmations before payments are included.\n"
1029 "[includeempty] whether to include addresses that haven't received any payments.\n"
1030 "Returns an array of objects containing:\n"
1031 " \"address\" : receiving address\n"
1032 " \"account\" : the account of the receiving address\n"
1033 " \"amount\" : total amount received by the address\n"
1034 " \"confirmations\" : number of confirmations of the most recent transaction included");
1036 return ListReceived(params, false);
1039 Value listreceivedbyaccount(const Array& params, bool fHelp)
1041 if (fHelp || params.size() > 2)
1042 throw runtime_error(
1043 "listreceivedbyaccount [minconf=1] [includeempty=false]\n"
1044 "[minconf] is the minimum number of confirmations before payments are included.\n"
1045 "[includeempty] whether to include accounts that haven't received any payments.\n"
1046 "Returns an array of objects containing:\n"
1047 " \"account\" : the account of the receiving addresses\n"
1048 " \"amount\" : total amount received by addresses with this account\n"
1049 " \"confirmations\" : number of confirmations of the most recent transaction included");
1051 return ListReceived(params, true);
1054 static void MaybePushAddress(Object & entry, const CBitcoinAddress &dest)
1056 entry.push_back(Pair("address", dest.ToString()));
1059 void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret, const isminefilter& filter)
1061 int64_t nGeneratedImmature, nGeneratedMature, nFee;
1062 string strSentAccount;
1063 list<pair<CBitcoinAddress, int64_t> > listReceived;
1064 list<pair<CBitcoinAddress, int64_t> > listSent;
1066 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount, filter);
1068 bool fAllAccounts = (strAccount == string("*"));
1069 bool involvesWatchonly = wtx.IsFromMe(MINE_WATCH_ONLY);
1071 // Generated blocks assigned to account ""
1072 if ((nGeneratedMature+nGeneratedImmature) != 0 && (fAllAccounts || strAccount.empty()))
1075 entry.push_back(Pair("account", string("")));
1076 if (nGeneratedImmature)
1078 entry.push_back(Pair("category", wtx.GetDepthInMainChain() ? "immature" : "orphan"));
1079 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedImmature)));
1083 entry.push_back(Pair("category", "generate"));
1084 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedMature)));
1087 WalletTxToJSON(wtx, entry);
1088 ret.push_back(entry);
1092 if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount))
1094 for(const auto& s : listSent)
1097 entry.push_back(Pair("account", strSentAccount));
1098 if(involvesWatchonly || (::IsMine(*pwalletMain, s.first) & MINE_WATCH_ONLY))
1099 entry.push_back(Pair("involvesWatchonly", true));
1100 MaybePushAddress(entry, s.first);
1102 if (wtx.GetDepthInMainChain() < 0) {
1103 entry.push_back(Pair("category", "conflicted"));
1105 entry.push_back(Pair("category", "send"));
1108 entry.push_back(Pair("amount", ValueFromAmount(-s.second)));
1109 entry.push_back(Pair("fee", ValueFromAmount(-nFee)));
1111 WalletTxToJSON(wtx, entry);
1112 ret.push_back(entry);
1117 if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth)
1119 for(const auto& r : listReceived)
1122 if (pwalletMain->mapAddressBook.count(r.first))
1123 account = pwalletMain->mapAddressBook[r.first];
1124 if (fAllAccounts || (account == strAccount))
1127 entry.push_back(Pair("account", account));
1128 if(involvesWatchonly || (::IsMine(*pwalletMain, r.first) & MINE_WATCH_ONLY))
1129 entry.push_back(Pair("involvesWatchonly", true));
1130 MaybePushAddress(entry, r.first);
1131 if (wtx.IsCoinBase())
1133 if (wtx.GetDepthInMainChain() < 1)
1134 entry.push_back(Pair("category", "orphan"));
1135 else if (wtx.GetBlocksToMaturity() > 0)
1136 entry.push_back(Pair("category", "immature"));
1138 entry.push_back(Pair("category", "generate"));
1141 entry.push_back(Pair("category", "receive"));
1142 entry.push_back(Pair("amount", ValueFromAmount(r.second)));
1144 WalletTxToJSON(wtx, entry);
1145 ret.push_back(entry);
1151 void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Array& ret)
1153 bool fAllAccounts = (strAccount == string("*"));
1155 if (fAllAccounts || acentry.strAccount == strAccount)
1158 entry.push_back(Pair("account", acentry.strAccount));
1159 entry.push_back(Pair("category", "move"));
1160 entry.push_back(Pair("time", (int64_t)acentry.nTime));
1161 entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit)));
1162 entry.push_back(Pair("otheraccount", acentry.strOtherAccount));
1163 entry.push_back(Pair("comment", acentry.strComment));
1164 ret.push_back(entry);
1168 Value listtransactions(const Array& params, bool fHelp)
1170 if (fHelp || params.size() > 3)
1171 throw runtime_error(
1172 "listtransactions [account] [count=10] [from=0]\n"
1173 "Returns up to [count] most recent transactions skipping the first [from] transactions for account [account].");
1175 string strAccount = "*";
1176 if (params.size() > 0)
1177 strAccount = params[0].get_str();
1179 if (params.size() > 1)
1180 nCount = params[1].get_int();
1182 if (params.size() > 2)
1183 nFrom = params[2].get_int();
1185 isminefilter filter = MINE_SPENDABLE;
1186 if(params.size() > 3)
1187 if(params[3].get_bool())
1188 filter = filter | MINE_WATCH_ONLY;
1191 throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count");
1193 throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative from");
1197 list<CAccountingEntry> acentries;
1198 auto txOrdered = pwalletMain->OrderedTxItems(acentries, strAccount);
1200 // iterate backwards until we have nCount items to return:
1201 for (auto it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
1203 CWalletTx *const pwtx = (*it).second.first;
1205 ListTransactions(*pwtx, strAccount, 0, true, ret, filter);
1206 CAccountingEntry *const pacentry = (*it).second.second;
1208 AcentryToJSON(*pacentry, strAccount, ret);
1210 if ((int)ret.size() >= (nCount+nFrom)) break;
1212 // ret is newest to oldest
1214 if (nFrom > (int)ret.size())
1216 if ((nFrom + nCount) > (int)ret.size())
1217 nCount = ret.size() - nFrom;
1218 auto first = ret.begin();
1219 advance(first, nFrom);
1220 auto last = ret.begin();
1221 advance(last, nFrom+nCount);
1223 if (last != ret.end()) ret.erase(last, ret.end());
1224 if (first != ret.begin()) ret.erase(ret.begin(), first);
1226 reverse(ret.begin(), ret.end()); // Return oldest to newest
1231 Value listaccounts(const Array& params, bool fHelp)
1233 if (fHelp || params.size() > 1)
1234 throw runtime_error(
1235 "listaccounts [minconf=1]\n"
1236 "Returns Object that has account names as keys, account balances as values.");
1239 if (params.size() > 0)
1240 nMinDepth = params[0].get_int();
1242 isminefilter includeWatchonly = MINE_SPENDABLE;
1243 if(params.size() > 1)
1244 if(params[1].get_bool())
1245 includeWatchonly = includeWatchonly | MINE_WATCH_ONLY;
1248 map<string, int64_t> mapAccountBalances;
1249 for(const auto& entry : pwalletMain->mapAddressBook) {
1250 if (IsMine(*pwalletMain, entry.first)) // This address belongs to me
1251 mapAccountBalances[entry.second] = 0;
1254 for (auto it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
1256 const CWalletTx& wtx = (*it).second;
1257 int64_t nGeneratedImmature, nGeneratedMature, nFee;
1258 string strSentAccount;
1259 list<pair<CBitcoinAddress, int64_t> > listReceived;
1260 list<pair<CBitcoinAddress, int64_t> > listSent;
1261 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount, includeWatchonly);
1262 mapAccountBalances[strSentAccount] -= nFee;
1263 for(const auto& s : listSent)
1264 mapAccountBalances[strSentAccount] -= s.second;
1265 if (wtx.GetDepthInMainChain() >= nMinDepth)
1267 mapAccountBalances[""] += nGeneratedMature;
1268 for(const auto& r : listReceived)
1269 if (pwalletMain->mapAddressBook.count(r.first))
1270 mapAccountBalances[pwalletMain->mapAddressBook[r.first]] += r.second;
1272 mapAccountBalances[""] += r.second;
1276 list<CAccountingEntry> acentries;
1277 CWalletDB(pwalletMain->strWalletFile).ListAccountCreditDebit("*", acentries);
1278 for(const auto& entry : acentries)
1279 mapAccountBalances[entry.strAccount] += entry.nCreditDebit;
1282 for(const auto& accountBalance : mapAccountBalances) {
1283 ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second)));
1288 Value listsinceblock(const Array& params, bool fHelp)
1291 throw runtime_error(
1292 "listsinceblock [blockhash] [target-confirmations]\n"
1293 "Get all transactions in blocks since block [blockhash], or all transactions if omitted");
1295 CBlockIndex *pindex = NULL;
1296 int target_confirms = 1;
1297 isminefilter filter = MINE_SPENDABLE;
1299 if (params.size() > 0)
1301 uint256 blockId = 0;
1303 blockId.SetHex(params[0].get_str());
1304 pindex = CBlockLocator(blockId).GetBlockIndex();
1307 if (params.size() > 1)
1309 target_confirms = params[1].get_int();
1311 if (target_confirms < 1)
1312 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter");
1315 if(params.size() > 2)
1316 if(params[2].get_bool())
1317 filter = filter | MINE_WATCH_ONLY;
1319 int depth = pindex ? (1 + nBestHeight - pindex->nHeight) : -1;
1323 for (auto it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); it++)
1325 auto tx = (*it).second;
1327 if (depth == -1 || tx.GetDepthInMainChain() < depth)
1328 ListTransactions(tx, "*", 0, true, transactions, filter);
1333 if (target_confirms == 1)
1335 lastblock = hashBestChain;
1339 int target_height = pindexBest->nHeight + 1 - target_confirms;
1342 for (block = pindexBest;
1343 block && block->nHeight > target_height;
1344 block = block->pprev) { }
1346 lastblock = block ? block->GetBlockHash() : 0;
1350 ret.push_back(Pair("transactions", transactions));
1351 ret.push_back(Pair("lastblock", lastblock.GetHex()));
1356 Value gettransaction(const Array& params, bool fHelp)
1358 if (fHelp || params.size() != 1)
1359 throw runtime_error(
1360 "gettransaction <txid>\n"
1361 "Get detailed information about <txid>");
1364 hash.SetHex(params[0].get_str());
1366 isminefilter filter = MINE_SPENDABLE;
1367 if(params.size() > 1)
1368 if(params[1].get_bool())
1369 filter = filter | MINE_WATCH_ONLY;
1373 if (pwalletMain->mapWallet.count(hash))
1375 const CWalletTx& wtx = pwalletMain->mapWallet[hash];
1377 TxToJSON(wtx, 0, entry);
1379 auto nCredit = wtx.GetCredit(filter);
1380 auto nDebit = wtx.GetDebit(filter);
1381 auto nNet = nCredit - nDebit;
1382 auto nFee = (wtx.IsFromMe(filter) ? wtx.GetValueOut() - nDebit : 0);
1384 entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee)));
1385 if (wtx.IsFromMe(filter))
1386 entry.push_back(Pair("fee", ValueFromAmount(nFee)));
1388 WalletTxToJSON(wtx, entry);
1391 ListTransactions(pwalletMain->mapWallet[hash], "*", 0, false, details, filter);
1392 entry.push_back(Pair("details", details));
1397 uint256 hashBlock = 0;
1398 if (GetTransaction(hash, tx, hashBlock))
1400 TxToJSON(tx, 0, entry);
1402 entry.push_back(Pair("confirmations", 0));
1405 entry.push_back(Pair("blockhash", hashBlock.GetHex()));
1406 auto mi = mapBlockIndex.find(hashBlock);
1407 if (mi != mapBlockIndex.end() && (*mi).second)
1409 auto pindex = (*mi).second;
1410 if (pindex->IsInMainChain())
1411 entry.push_back(Pair("confirmations", 1 + nBestHeight - pindex->nHeight));
1413 entry.push_back(Pair("confirmations", 0));
1418 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction");
1425 Value backupwallet(const Array& params, bool fHelp)
1427 if (fHelp || params.size() != 1)
1428 throw runtime_error(
1429 "backupwallet <destination>\n"
1430 "Safely copies wallet.dat to destination, which can be a directory or a path with filename.");
1432 auto strDest = params[0].get_str();
1433 if (!BackupWallet(*pwalletMain, strDest))
1434 throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet backup failed!");
1440 Value keypoolrefill(const Array& params, bool fHelp)
1442 if (fHelp || params.size() > 1)
1443 throw runtime_error(
1444 "keypoolrefill [new-size]\n"
1445 "Fills the keypool.\n"
1446 "IMPORTANT: Any previous backups you have made of your wallet file "
1447 "should be replaced with the newly generated one."
1448 + HelpRequiringPassphrase());
1450 unsigned int nSize = max<unsigned int>(GetArgUInt("-keypool", 100), 0);
1451 if (params.size() > 0) {
1452 if (params[0].get_int() < 0)
1453 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected valid size");
1454 nSize = (unsigned int) params[0].get_int();
1457 EnsureWalletIsUnlocked();
1459 pwalletMain->TopUpKeyPool(nSize);
1461 if (pwalletMain->GetKeyPoolSize() < nSize)
1462 throw JSONRPCError(RPC_WALLET_ERROR, "Error refreshing keypool.");
1467 Value keypoolreset(const Array& params, bool fHelp)
1469 if (fHelp || params.size() > 1)
1470 throw runtime_error(
1471 "keypoolreset [new-size]\n"
1472 "Resets the keypool.\n"
1473 "IMPORTANT: Any previous backups you have made of your wallet file "
1474 "should be replaced with the newly generated one."
1475 + HelpRequiringPassphrase());
1477 unsigned int nSize = max<unsigned int>(GetArgUInt("-keypool", 100), 0);
1478 if (params.size() > 0) {
1479 if (params[0].get_int() < 0)
1480 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected valid size");
1481 nSize = (unsigned int) params[0].get_int();
1484 EnsureWalletIsUnlocked();
1486 pwalletMain->NewKeyPool(nSize);
1488 if (pwalletMain->GetKeyPoolSize() < nSize)
1489 throw JSONRPCError(RPC_WALLET_ERROR, "Error refreshing keypool.");
1495 void ThreadTopUpKeyPool(void* parg)
1497 // Make this thread recognisable as the key-topping-up thread
1498 RenameThread("novacoin-key-top");
1500 pwalletMain->TopUpKeyPool();
1503 void ThreadCleanWalletPassphrase(void* parg)
1505 // Make this thread recognisable as the wallet relocking thread
1506 RenameThread("novacoin-lock-wa");
1508 auto nMyWakeTime = GetTimeMillis() + *((int64_t*)parg) * 1000;
1510 ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime);
1512 if (nWalletUnlockTime == 0)
1514 nWalletUnlockTime = nMyWakeTime;
1518 if (nWalletUnlockTime==0)
1520 auto nToSleep = nWalletUnlockTime - GetTimeMillis();
1524 LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime);
1526 ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime);
1530 if (nWalletUnlockTime)
1532 nWalletUnlockTime = 0;
1533 pwalletMain->Lock();
1538 if (nWalletUnlockTime < nMyWakeTime)
1539 nWalletUnlockTime = nMyWakeTime;
1542 LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime);
1544 delete (int64_t*)parg;
1547 Value walletpassphrase(const Array& params, bool fHelp)
1549 if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 3))
1550 throw runtime_error(
1551 "walletpassphrase <passphrase> <timeout> [mintonly]\n"
1552 "Stores the wallet decryption key in memory for <timeout> seconds.\n"
1553 "mintonly is optional true/false allowing only block minting.");
1556 if (!pwalletMain->IsCrypted())
1557 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called.");
1559 if (!pwalletMain->IsLocked())
1560 throw JSONRPCError(RPC_WALLET_ALREADY_UNLOCKED, "Error: Wallet is already unlocked, use walletlock first if need to change unlock settings.");
1561 // Note that the walletpassphrase is stored in params[0] which is not mlock()ed
1562 SecureString strWalletPass;
1563 strWalletPass.reserve(100);
1564 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1565 // Alternately, find a way to make params[0] mlock()'d to begin with.
1566 strWalletPass = params[0].get_str().c_str();
1568 if (strWalletPass.length() > 0)
1570 if (!pwalletMain->Unlock(strWalletPass))
1571 throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
1574 throw runtime_error(
1575 "walletpassphrase <passphrase> <timeout>\n"
1576 "Stores the wallet decryption key in memory for <timeout> seconds.");
1578 NewThread(ThreadTopUpKeyPool, NULL);
1579 int64_t* pnSleepTime = new int64_t(params[1].get_int64());
1580 NewThread(ThreadCleanWalletPassphrase, pnSleepTime);
1582 // ppcoin: if user OS account compromised prevent trivial sendmoney commands
1583 if (params.size() > 2)
1584 fWalletUnlockMintOnly = params[2].get_bool();
1586 fWalletUnlockMintOnly = false;
1592 Value walletpassphrasechange(const Array& params, bool fHelp)
1594 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2))
1595 throw runtime_error(
1596 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1597 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1600 if (!pwalletMain->IsCrypted())
1601 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrasechange was called.");
1603 // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string)
1604 // Alternately, find a way to make params[0] mlock()'d to begin with.
1605 SecureString strOldWalletPass;
1606 strOldWalletPass.reserve(100);
1607 strOldWalletPass = params[0].get_str().c_str();
1609 SecureString strNewWalletPass;
1610 strNewWalletPass.reserve(100);
1611 strNewWalletPass = params[1].get_str().c_str();
1613 if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1)
1614 throw runtime_error(
1615 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1616 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1618 if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass))
1619 throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
1625 Value walletlock(const Array& params, bool fHelp)
1627 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 0))
1628 throw runtime_error(
1630 "Removes the wallet encryption key from memory, locking the wallet.\n"
1631 "After calling this method, you will need to call walletpassphrase again\n"
1632 "before being able to call any methods which require the wallet to be unlocked.");
1635 if (!pwalletMain->IsCrypted())
1636 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletlock was called.");
1639 LOCK(cs_nWalletUnlockTime);
1640 pwalletMain->Lock();
1641 nWalletUnlockTime = 0;
1648 Value encryptwallet(const Array& params, bool fHelp)
1650 if (!pwalletMain->IsCrypted() && (fHelp || params.size() != 1))
1651 throw runtime_error(
1652 "encryptwallet <passphrase>\n"
1653 "Encrypts the wallet with <passphrase>.");
1656 if (pwalletMain->IsCrypted())
1657 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an encrypted wallet, but encryptwallet was called.");
1659 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1660 // Alternately, find a way to make params[0] mlock()'d to begin with.
1661 SecureString strWalletPass;
1662 strWalletPass.reserve(100);
1663 strWalletPass = params[0].get_str().c_str();
1665 if (strWalletPass.length() < 1)
1666 throw runtime_error(
1667 "encryptwallet <passphrase>\n"
1668 "Encrypts the wallet with <passphrase>.");
1670 if (!pwalletMain->EncryptWallet(strWalletPass))
1671 throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: Failed to encrypt the wallet.");
1673 // BDB seems to have a bad habit of writing old data into
1674 // slack space in .dat files; that is bad if the old data is
1675 // unencrypted private keys. So:
1677 return "wallet encrypted; NovaCoin server stopping, restart to run with encrypted wallet. The keypool has been flushed, you need to make a new backup.";
1680 class DescribeAddressVisitor : public boost::static_visitor<Object>
1685 DescribeAddressVisitor(isminetype mineIn) : mine(mineIn) {}
1687 Object operator()(const CNoDestination &dest) const { return Object(); }
1688 Object operator()(const CKeyID &keyID) const {
1691 pwalletMain->GetPubKey(keyID, vchPubKey);
1692 obj.push_back(Pair("isscript", false));
1693 if (mine == MINE_SPENDABLE) {
1694 pwalletMain->GetPubKey(keyID, vchPubKey);
1695 obj.push_back(Pair("pubkey", HexStr(vchPubKey.begin(), vchPubKey.end())));
1696 obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed()));
1701 Object operator()(const CScriptID &scriptID) const {
1703 obj.push_back(Pair("isscript", true));
1704 if (mine == MINE_SPENDABLE) {
1706 pwalletMain->GetCScript(scriptID, subscript);
1707 vector<CTxDestination> addresses;
1708 txnouttype whichType;
1710 ExtractDestinations(subscript, whichType, addresses, nRequired);
1711 obj.push_back(Pair("script", GetTxnOutputType(whichType)));
1712 obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end())));
1714 for(const auto& addr : addresses)
1715 a.push_back(CBitcoinAddress(addr).ToString());
1716 obj.push_back(Pair("addresses", a));
1717 if (whichType == TX_MULTISIG)
1718 obj.push_back(Pair("sigsrequired", nRequired));
1724 Value validateaddress(const Array& params, bool fHelp)
1726 if (fHelp || params.size() != 1)
1727 throw runtime_error(
1728 "validateaddress <novacoinaddress>\n"
1729 "Return information about <novacoinaddress>.");
1731 CBitcoinAddress address(params[0].get_str());
1732 bool isValid = address.IsValid();
1735 ret.push_back(Pair("isvalid", isValid));
1738 if (address.IsPair())
1740 CMalleablePubKey mpk;
1741 mpk.setvch(address.GetData());
1742 ret.push_back(Pair("ispair", true));
1744 CMalleableKeyView view;
1745 bool isMine = pwalletMain->GetMalleableView(mpk, view);
1746 ret.push_back(Pair("ismine", isMine));
1747 ret.push_back(Pair("PubkeyPair", mpk.ToString()));
1750 ret.push_back(Pair("KeyView", view.ToString()));
1754 auto currentAddress = address.ToString();
1755 auto dest = address.Get();
1756 ret.push_back(Pair("address", currentAddress));
1757 auto mine = IsMine(*pwalletMain, address);
1758 ret.push_back(Pair("ismine", mine != MINE_NO));
1759 if (mine != MINE_NO) {
1760 ret.push_back(Pair("watchonly", mine == MINE_WATCH_ONLY));
1761 Object detail = boost::apply_visitor(DescribeAddressVisitor(mine), dest);
1762 ret.insert(ret.end(), detail.begin(), detail.end());
1764 if (pwalletMain->mapAddressBook.count(address))
1765 ret.push_back(Pair("account", pwalletMain->mapAddressBook[address]));
1771 // ppcoin: reserve balance from being staked for network protection
1772 Value reservebalance(const Array& params, bool fHelp)
1774 if (fHelp || params.size() > 2)
1775 throw runtime_error(
1776 "reservebalance [<reserve> [amount]]\n"
1777 "<reserve> is true or false to turn balance reserve on or off.\n"
1778 "<amount> is a real and rounded to cent.\n"
1779 "Set reserve amount not participating in network protection.\n"
1780 "If no parameters provided current setting is printed.\n");
1782 if (params.size() > 0)
1784 bool fReserve = params[0].get_bool();
1787 if (params.size() == 1)
1788 throw runtime_error("must provide amount to reserve balance.\n");
1789 auto nAmount = AmountFromValue(params[1]);
1790 nAmount = (nAmount / CENT) * CENT; // round to cent
1792 throw runtime_error("amount cannot be negative.\n");
1793 mapArgs["-reservebalance"] = FormatMoney(nAmount);
1797 if (params.size() > 1)
1798 throw runtime_error("cannot specify amount to turn off reserve.\n");
1799 mapArgs["-reservebalance"] = "0";
1804 if (mapArgs.count("-reservebalance") && !ParseMoney(mapArgs["-reservebalance"], nReserveBalance))
1805 throw runtime_error("invalid reserve balance amount\n");
1806 result.push_back(Pair("reserve", (nReserveBalance > 0)));
1807 result.push_back(Pair("amount", ValueFromAmount(nReserveBalance)));
1812 // ppcoin: check wallet integrity
1813 Value checkwallet(const Array& params, bool fHelp)
1815 if (fHelp || params.size() > 0)
1816 throw runtime_error(
1818 "Check wallet for integrity.\n");
1821 int64_t nBalanceInQuestion;
1822 pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion, true);
1824 if (nMismatchSpent == 0)
1825 result.push_back(Pair("wallet check passed", true));
1828 result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1829 result.push_back(Pair("amount in question", ValueFromAmount(nBalanceInQuestion)));
1835 // ppcoin: repair wallet
1836 Value repairwallet(const Array& params, bool fHelp)
1838 if (fHelp || params.size() > 0)
1839 throw runtime_error(
1841 "Repair wallet if checkwallet reports any problem.\n");
1844 int64_t nBalanceInQuestion;
1845 pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion);
1847 if (nMismatchSpent == 0)
1848 result.push_back(Pair("wallet check passed", true));
1851 result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1852 result.push_back(Pair("amount affected by repair", ValueFromAmount(nBalanceInQuestion)));
1857 // NovaCoin: resend unconfirmed wallet transactions
1858 Value resendtx(const Array& params, bool fHelp)
1860 if (fHelp || params.size() > 1)
1861 throw runtime_error(
1863 "Re-send unconfirmed transactions.\n"
1866 ResendWalletTransactions(true);
1871 Value resendwallettransactions(const Array& params, bool fHelp)
1873 if (fHelp || params.size() != 0)
1874 throw runtime_error(
1875 "resendwallettransactions\n"
1876 "Immediately re-broadcast unconfirmed wallet transactions to all peers.\n"
1877 "Intended only for testing; the wallet code periodically re-broadcasts\n"
1879 "Returns array of transaction ids that were re-broadcast.\n"
1882 LOCK2(cs_main, pwalletMain->cs_wallet);
1884 auto txids = pwalletMain->ResendWalletTransactionsBefore(GetTime());
1886 for(const auto& txid : txids)
1888 result.push_back(txid.ToString());
1894 // Make a public-private key pair
1895 Value makekeypair(const Array& params, bool fHelp)
1897 if (fHelp || params.size() > 0)
1898 throw runtime_error(
1900 "Make a public/private key pair.\n");
1902 string strPrefix = "";
1903 if (params.size() > 0)
1904 strPrefix = params[0].get_str();
1907 key.MakeNewKey(true);
1909 auto vchPrivKey = key.GetPrivKey();
1911 result.push_back(Pair("PrivateKey", HexStr<CPrivKey::iterator>(vchPrivKey.begin(), vchPrivKey.end())));
1914 auto vchSecret = key.GetSecret(fCompressed);
1915 auto vchPubKey = key.GetPubKey();
1916 result.push_back(Pair("Secret", HexStr<CSecret::iterator>(vchSecret.begin(), vchSecret.end())));
1917 result.push_back(Pair("PublicKey", HexStr(vchPubKey.begin(), vchPubKey.end())));
1921 Value newmalleablekey(const Array& params, bool fHelp)
1923 if (fHelp || params.size() > 1)
1924 throw runtime_error(
1926 "Make a malleable public/private key pair.\n");
1928 // Parse the account first so we don't generate a key if there's an error
1930 if (params.size() > 0)
1931 strAccount = AccountFromValue(params[0]);
1933 auto keyView = pwalletMain->GenerateNewMalleableKey();
1936 if (!pwalletMain->GetMalleableKey(keyView, mKey))
1937 throw runtime_error("Unable to generate new malleable key");
1939 auto mPubKey = mKey.GetMalleablePubKey();
1940 CBitcoinAddress address(mPubKey);
1942 pwalletMain->SetAddressBookName(address, strAccount);
1945 result.push_back(Pair("PublicPair", mPubKey.ToString()));
1946 result.push_back(Pair("PublicBytes", HexStr(mPubKey.Raw())));
1947 result.push_back(Pair("Address", address.ToString()));
1948 result.push_back(Pair("KeyView", keyView.ToString()));
1953 Value adjustmalleablekey(const Array& params, bool fHelp)
1955 if (fHelp || params.size() != 3)
1956 throw runtime_error(
1957 "adjustmalleablekey <Malleable key data> <Public key variant data> <R data>\n"
1958 "Calculate new private key using provided malleable key, public key and R data.\n");
1960 CMalleableKey malleableKey;
1961 malleableKey.SetString(params[0].get_str());
1963 CKey privKeyVariant;
1964 auto vchPubKeyVariant = CPubKey(ParseHex(params[1].get_str()));
1966 CPubKey R(ParseHex(params[2].get_str()));
1968 if (!malleableKey.CheckKeyVariant(R,vchPubKeyVariant, privKeyVariant)) {
1969 throw runtime_error("Unable to calculate the private key");
1974 auto vchPrivKeyVariant = privKeyVariant.GetSecret(fCompressed);
1976 result.push_back(Pair("PrivateKey", CBitcoinSecret(vchPrivKeyVariant, fCompressed).ToString()));
1981 Value adjustmalleablepubkey(const Array& params, bool fHelp)
1983 if (fHelp || params.size() > 2 || params.size() == 0)
1984 throw runtime_error(
1985 "adjustmalleablepubkey <Malleable address, key view or public key pair>\n"
1986 "Calculate new public key using provided data.\n");
1988 auto strData = params[0].get_str();
1989 CMalleablePubKey malleablePubKey;
1993 CBitcoinAddress addr(strData);
1994 if (addr.IsValid() && addr.IsPair())
1996 // Initialize malleable pubkey with address data
1997 malleablePubKey = CMalleablePubKey(addr.GetData());
2000 CMalleableKeyView viewTmp(strData);
2001 if (viewTmp.IsValid())
2003 // Shazaam, we have a valid key view here.
2004 malleablePubKey = viewTmp.GetMalleablePubKey();
2007 if (malleablePubKey.SetString(strData))
2008 break; // A valid public key pair
2010 throw runtime_error("Though your data seems a valid Base58 string, we were unable to recognize it.");
2014 CPubKey R, vchPubKeyVariant;
2015 malleablePubKey.GetVariant(R, vchPubKeyVariant);
2018 result.push_back(Pair("R", HexStr(R.begin(), R.end())));
2019 result.push_back(Pair("PubkeyVariant", HexStr(vchPubKeyVariant.begin(), vchPubKeyVariant.end())));
2020 result.push_back(Pair("KeyVariantID", CBitcoinAddress(vchPubKeyVariant.GetID()).ToString()));
2025 Value listmalleableviews(const Array& params, bool fHelp)
2027 if (fHelp || params.size() != 0)
2028 throw runtime_error(
2029 "listmalleableviews\n"
2030 "Get list of views for generated malleable keys.\n");
2032 list<CMalleableKeyView> keyViewList;
2033 pwalletMain->ListMalleableViews(keyViewList);
2036 for(const auto &keyView : keyViewList)
2037 result.push_back(keyView.ToString());