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() > 2)
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;
708 auto strAddress = params[0].get_str();
710 CBitcoinAddress address(strAddress);
711 if (address.IsValid())
712 scriptPubKey.SetAddress(address);
714 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
717 auto nAmount = AmountFromValue(params[2]);
719 if (nAmount < nMinimumInputValue)
720 throw JSONRPCError(-101, "Send amount too small");
723 if (params.size() > 3)
724 nMinDepth = params[3].get_int();
727 wtx.strFromAccount = strAccount;
728 if (params.size() > 4 && params[4].type() != null_type && !params[4].get_str().empty())
729 wtx.mapValue["comment"] = params[4].get_str();
730 if (params.size() > 5 && params[5].type() != null_type && !params[5].get_str().empty())
731 wtx.mapValue["to"] = params[5].get_str();
733 EnsureWalletIsUnlocked();
736 auto nBalance = GetAccountBalance(strAccount, nMinDepth, MINE_SPENDABLE);
737 if (nAmount > nBalance)
738 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds");
741 auto strError = pwalletMain->SendMoney(scriptPubKey, nAmount, wtx);
742 if (!strError.empty())
743 throw JSONRPCError(RPC_WALLET_ERROR, strError);
745 return wtx.GetHash().GetHex();
749 Value sendmany(const Array& params, bool fHelp)
751 if (fHelp || params.size() < 2 || params.size() > 4)
753 "sendmany <fromaccount> '{address:amount,...}' [minconf=1] [comment]\n"
754 "amounts are double-precision floating point numbers"
755 + HelpRequiringPassphrase());
757 auto strAccount = AccountFromValue(params[0]);
758 auto sendTo = params[1].get_obj();
760 if (params.size() > 2)
761 nMinDepth = params[2].get_int();
764 wtx.strFromAccount = strAccount;
765 if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
766 wtx.mapValue["comment"] = params[3].get_str();
768 set<CBitcoinAddress> setAddress;
769 vector<pair<CScript, int64_t> > vecSend;
771 int64_t totalAmount = 0;
772 for(const Pair& s : sendTo)
774 CBitcoinAddress address(s.name_);
775 if (!address.IsValid())
776 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid NovaCoin address: ")+s.name_);
778 if (!address.IsPair())
780 if (setAddress.count(address))
781 throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+s.name_);
782 setAddress.insert(address);
785 CScript scriptPubKey;
786 scriptPubKey.SetAddress(address);
787 auto nAmount = AmountFromValue(s.value_);
789 if (nAmount < nMinimumInputValue)
790 throw JSONRPCError(-101, "Send amount too small");
792 totalAmount += nAmount;
794 vecSend.push_back({ scriptPubKey, nAmount });
797 EnsureWalletIsUnlocked();
800 auto nBalance = GetAccountBalance(strAccount, nMinDepth, MINE_SPENDABLE);
801 if (totalAmount > nBalance)
802 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds");
805 CReserveKey keyChange(pwalletMain);
806 int64_t nFeeRequired = 0;
807 bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired);
810 auto nTotal = pwalletMain->GetBalance(), nWatchOnly = pwalletMain->GetWatchOnlyBalance();
811 if (totalAmount + nFeeRequired > nTotal - nWatchOnly)
812 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds");
813 throw JSONRPCError(RPC_WALLET_ERROR, "Transaction creation failed");
815 if (!pwalletMain->CommitTransaction(wtx, keyChange))
816 throw JSONRPCError(RPC_WALLET_ERROR, "Transaction commit failed");
818 return wtx.GetHash().GetHex();
821 Value addmultisigaddress(const Array& params, bool fHelp)
823 if (fHelp || params.size() < 2 || params.size() > 3)
825 string msg = "addmultisigaddress <nrequired> <'[\"key\",\"key\"]'> [account]\n"
826 "Add a nrequired-to-sign multisignature address to the wallet\"\n"
827 "each key is a NovaCoin address or hex-encoded public key\n"
828 "If [account] is specified, assign address to [account].";
829 throw runtime_error(msg);
832 int nRequired = params[0].get_int();
833 const Array& keys = params[1].get_array();
835 if (params.size() > 2)
836 strAccount = AccountFromValue(params[2]);
838 // Gather public keys
840 throw runtime_error("a multisignature address must require at least one key to redeem");
841 if ((int)keys.size() < nRequired)
843 strprintf("not enough keys supplied "
844 "(got %" PRIszu " keys, but need at least %d to redeem)", keys.size(), nRequired));
845 if (keys.size() > 16)
846 throw runtime_error("Number of addresses involved in the multisignature address creation > 16\nReduce the number");
847 vector<CPubKey> pubkeys;
848 pubkeys.resize(keys.size());
849 for (unsigned int i = 0; i < keys.size(); i++)
851 const auto& ks = keys[i].get_str();
853 // Case 1: Bitcoin address and we have full public key:
854 CBitcoinAddress address(ks);
855 if (address.IsValid())
858 if (!address.GetKeyID(keyID))
860 strprintf("%s does not refer to a key",ks.c_str()));
862 if (!pwalletMain->GetPubKey(keyID, vchPubKey))
864 strprintf("no full public key for address %s",ks.c_str()));
865 if (!vchPubKey.IsValid())
866 throw runtime_error(" Invalid public key: "+ks);
867 pubkeys[i] = vchPubKey;
870 // Case 2: hex public key
873 CPubKey vchPubKey(ParseHex(ks));
874 if (!vchPubKey.IsValid())
875 throw runtime_error(" Invalid public key: "+ks);
876 pubkeys[i] = vchPubKey;
880 throw runtime_error(" Invalid public key: "+ks);
884 // Construct using pay-to-script-hash:
886 inner.SetMultisig(nRequired, pubkeys);
888 if (inner.size() > MAX_SCRIPT_ELEMENT_SIZE)
890 strprintf("redeemScript exceeds size limit: %" PRIszu " > %d", inner.size(), MAX_SCRIPT_ELEMENT_SIZE));
892 pwalletMain->AddCScript(inner);
893 CBitcoinAddress address{ CScriptID(inner) }; // "most vexing parse"
895 pwalletMain->SetAddressBookName(address, strAccount);
896 return address.ToString();
899 Value addredeemscript(const Array& params, bool fHelp)
901 if (fHelp || params.size() < 1 || params.size() > 2)
903 string msg = "addredeemscript <redeemScript> [account]\n"
904 "Add a P2SH address with a specified redeemScript to the wallet.\n"
905 "If [account] is specified, assign address to [account].";
906 throw runtime_error(msg);
910 if (params.size() > 1)
911 strAccount = AccountFromValue(params[1]);
913 // Construct using pay-to-script-hash:
914 auto innerData = ParseHexV(params[0], "redeemScript");
915 CScript inner(innerData.begin(), innerData.end());
916 pwalletMain->AddCScript(inner);
917 CBitcoinAddress address{ CScriptID(inner) }; // "most vexing parse"
919 pwalletMain->SetAddressBookName(address, strAccount);
920 return address.ToString();
930 nConf = numeric_limits<int>::max();
934 Value ListReceived(const Array& params, bool fByAccounts)
936 // Minimum confirmations
938 if (params.size() > 0)
939 nMinDepth = params[0].get_int();
941 // Whether to include empty accounts
942 bool fIncludeEmpty = false;
943 if (params.size() > 1)
944 fIncludeEmpty = params[1].get_bool();
947 map<CBitcoinAddress, tallyitem> mapTally;
948 for (const auto &wit : pwalletMain->mapWallet)
950 const auto& wtx = wit.second;
952 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
955 int nDepth = wtx.GetDepthInMainChain();
956 if (nDepth < nMinDepth)
959 for(const auto& txout : wtx.vout)
961 CTxDestination address;
962 if (!ExtractDestination(txout.scriptPubKey, address) || !IsMine(*pwalletMain, address))
965 auto& item = mapTally[address];
966 item.nAmount += txout.nValue;
967 item.nConf = min(item.nConf, nDepth);
973 map<string, tallyitem> mapAccountTally;
974 for(const auto& item : pwalletMain->mapAddressBook)
976 const auto& address = item.first;
977 const auto& strAccount = item.second;
978 auto it = mapTally.find(address);
979 if (it == mapTally.end() && !fIncludeEmpty)
983 int nConf = numeric_limits<int>::max();
984 if (it != mapTally.end())
986 nAmount = (*it).second.nAmount;
987 nConf = (*it).second.nConf;
992 auto& item = mapAccountTally[strAccount];
993 item.nAmount += nAmount;
994 item.nConf = min(item.nConf, nConf);
999 obj.push_back(Pair("address", address.ToString()));
1000 obj.push_back(Pair("account", strAccount));
1001 obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
1002 obj.push_back(Pair("confirmations", (nConf == numeric_limits<int>::max() ? 0 : nConf)));
1009 for (const auto &acc : mapAccountTally)
1011 auto nAmount = acc.second.nAmount;
1012 int nConf = acc.second.nConf;
1014 obj.push_back(Pair("account", acc.first));
1015 obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
1016 obj.push_back(Pair("confirmations", (nConf == numeric_limits<int>::max() ? 0 : nConf)));
1024 Value listreceivedbyaddress(const Array& params, bool fHelp)
1026 if (fHelp || params.size() > 2)
1027 throw runtime_error(
1028 "listreceivedbyaddress [minconf=1] [includeempty=false]\n"
1029 "[minconf] is the minimum number of confirmations before payments are included.\n"
1030 "[includeempty] whether to include addresses that haven't received any payments.\n"
1031 "Returns an array of objects containing:\n"
1032 " \"address\" : receiving address\n"
1033 " \"account\" : the account of the receiving address\n"
1034 " \"amount\" : total amount received by the address\n"
1035 " \"confirmations\" : number of confirmations of the most recent transaction included");
1037 return ListReceived(params, false);
1040 Value listreceivedbyaccount(const Array& params, bool fHelp)
1042 if (fHelp || params.size() > 2)
1043 throw runtime_error(
1044 "listreceivedbyaccount [minconf=1] [includeempty=false]\n"
1045 "[minconf] is the minimum number of confirmations before payments are included.\n"
1046 "[includeempty] whether to include accounts that haven't received any payments.\n"
1047 "Returns an array of objects containing:\n"
1048 " \"account\" : the account of the receiving addresses\n"
1049 " \"amount\" : total amount received by addresses with this account\n"
1050 " \"confirmations\" : number of confirmations of the most recent transaction included");
1052 return ListReceived(params, true);
1055 static void MaybePushAddress(Object & entry, const CBitcoinAddress &dest)
1057 entry.push_back(Pair("address", dest.ToString()));
1060 void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret, const isminefilter& filter)
1062 int64_t nGeneratedImmature, nGeneratedMature, nFee;
1063 string strSentAccount;
1064 list<pair<CBitcoinAddress, int64_t> > listReceived;
1065 list<pair<CBitcoinAddress, int64_t> > listSent;
1067 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount, filter);
1069 bool fAllAccounts = (strAccount == string("*"));
1070 bool involvesWatchonly = wtx.IsFromMe(MINE_WATCH_ONLY);
1072 // Generated blocks assigned to account ""
1073 if ((nGeneratedMature+nGeneratedImmature) != 0 && (fAllAccounts || strAccount.empty()))
1076 entry.push_back(Pair("account", string("")));
1077 if (nGeneratedImmature)
1079 entry.push_back(Pair("category", wtx.GetDepthInMainChain() ? "immature" : "orphan"));
1080 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedImmature)));
1084 entry.push_back(Pair("category", "generate"));
1085 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedMature)));
1088 WalletTxToJSON(wtx, entry);
1089 ret.push_back(entry);
1093 if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount))
1095 for(const auto& s : listSent)
1098 entry.push_back(Pair("account", strSentAccount));
1099 if(involvesWatchonly || (::IsMine(*pwalletMain, s.first) & MINE_WATCH_ONLY))
1100 entry.push_back(Pair("involvesWatchonly", true));
1101 MaybePushAddress(entry, s.first);
1103 if (wtx.GetDepthInMainChain() < 0) {
1104 entry.push_back(Pair("category", "conflicted"));
1106 entry.push_back(Pair("category", "send"));
1109 entry.push_back(Pair("amount", ValueFromAmount(-s.second)));
1110 entry.push_back(Pair("fee", ValueFromAmount(-nFee)));
1112 WalletTxToJSON(wtx, entry);
1113 ret.push_back(entry);
1118 if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth)
1120 for(const auto& r : listReceived)
1123 if (pwalletMain->mapAddressBook.count(r.first))
1124 account = pwalletMain->mapAddressBook[r.first];
1125 if (fAllAccounts || (account == strAccount))
1128 entry.push_back(Pair("account", account));
1129 if(involvesWatchonly || (::IsMine(*pwalletMain, r.first) & MINE_WATCH_ONLY))
1130 entry.push_back(Pair("involvesWatchonly", true));
1131 MaybePushAddress(entry, r.first);
1132 if (wtx.IsCoinBase())
1134 if (wtx.GetDepthInMainChain() < 1)
1135 entry.push_back(Pair("category", "orphan"));
1136 else if (wtx.GetBlocksToMaturity() > 0)
1137 entry.push_back(Pair("category", "immature"));
1139 entry.push_back(Pair("category", "generate"));
1142 entry.push_back(Pair("category", "receive"));
1143 entry.push_back(Pair("amount", ValueFromAmount(r.second)));
1145 WalletTxToJSON(wtx, entry);
1146 ret.push_back(entry);
1152 void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Array& ret)
1154 bool fAllAccounts = (strAccount == string("*"));
1156 if (fAllAccounts || acentry.strAccount == strAccount)
1159 entry.push_back(Pair("account", acentry.strAccount));
1160 entry.push_back(Pair("category", "move"));
1161 entry.push_back(Pair("time", (int64_t)acentry.nTime));
1162 entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit)));
1163 entry.push_back(Pair("otheraccount", acentry.strOtherAccount));
1164 entry.push_back(Pair("comment", acentry.strComment));
1165 ret.push_back(entry);
1169 Value listtransactions(const Array& params, bool fHelp)
1171 if (fHelp || params.size() > 3)
1172 throw runtime_error(
1173 "listtransactions [account] [count=10] [from=0]\n"
1174 "Returns up to [count] most recent transactions skipping the first [from] transactions for account [account].");
1176 string strAccount = "*";
1177 if (params.size() > 0)
1178 strAccount = params[0].get_str();
1180 if (params.size() > 1)
1181 nCount = params[1].get_int();
1183 if (params.size() > 2)
1184 nFrom = params[2].get_int();
1186 isminefilter filter = MINE_SPENDABLE;
1187 if(params.size() > 3)
1188 if(params[3].get_bool())
1189 filter = filter | MINE_WATCH_ONLY;
1192 throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count");
1194 throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative from");
1198 list<CAccountingEntry> acentries;
1199 auto txOrdered = pwalletMain->OrderedTxItems(acentries, strAccount);
1201 // iterate backwards until we have nCount items to return:
1202 for (auto it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
1204 CWalletTx *const pwtx = (*it).second.first;
1206 ListTransactions(*pwtx, strAccount, 0, true, ret, filter);
1207 CAccountingEntry *const pacentry = (*it).second.second;
1209 AcentryToJSON(*pacentry, strAccount, ret);
1211 if ((int)ret.size() >= (nCount+nFrom)) break;
1213 // ret is newest to oldest
1215 if (nFrom > (int)ret.size())
1217 if ((nFrom + nCount) > (int)ret.size())
1218 nCount = ret.size() - nFrom;
1219 auto first = ret.begin();
1220 advance(first, nFrom);
1221 auto last = ret.begin();
1222 advance(last, nFrom+nCount);
1224 if (last != ret.end()) ret.erase(last, ret.end());
1225 if (first != ret.begin()) ret.erase(ret.begin(), first);
1227 reverse(ret.begin(), ret.end()); // Return oldest to newest
1232 Value listaccounts(const Array& params, bool fHelp)
1234 if (fHelp || params.size() > 1)
1235 throw runtime_error(
1236 "listaccounts [minconf=1]\n"
1237 "Returns Object that has account names as keys, account balances as values.");
1240 if (params.size() > 0)
1241 nMinDepth = params[0].get_int();
1243 isminefilter includeWatchonly = MINE_SPENDABLE;
1244 if(params.size() > 1)
1245 if(params[1].get_bool())
1246 includeWatchonly = includeWatchonly | MINE_WATCH_ONLY;
1249 map<string, int64_t> mapAccountBalances;
1250 for(const auto& entry : pwalletMain->mapAddressBook) {
1251 if (IsMine(*pwalletMain, entry.first)) // This address belongs to me
1252 mapAccountBalances[entry.second] = 0;
1255 for (auto it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
1257 const CWalletTx& wtx = (*it).second;
1258 int64_t nGeneratedImmature, nGeneratedMature, nFee;
1259 string strSentAccount;
1260 list<pair<CBitcoinAddress, int64_t> > listReceived;
1261 list<pair<CBitcoinAddress, int64_t> > listSent;
1262 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount, includeWatchonly);
1263 mapAccountBalances[strSentAccount] -= nFee;
1264 for(const auto& s : listSent)
1265 mapAccountBalances[strSentAccount] -= s.second;
1266 if (wtx.GetDepthInMainChain() >= nMinDepth)
1268 mapAccountBalances[""] += nGeneratedMature;
1269 for(const auto& r : listReceived)
1270 if (pwalletMain->mapAddressBook.count(r.first))
1271 mapAccountBalances[pwalletMain->mapAddressBook[r.first]] += r.second;
1273 mapAccountBalances[""] += r.second;
1277 list<CAccountingEntry> acentries;
1278 CWalletDB(pwalletMain->strWalletFile).ListAccountCreditDebit("*", acentries);
1279 for(const auto& entry : acentries)
1280 mapAccountBalances[entry.strAccount] += entry.nCreditDebit;
1283 for(const auto& accountBalance : mapAccountBalances) {
1284 ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second)));
1289 Value listsinceblock(const Array& params, bool fHelp)
1292 throw runtime_error(
1293 "listsinceblock [blockhash] [target-confirmations]\n"
1294 "Get all transactions in blocks since block [blockhash], or all transactions if omitted");
1296 CBlockIndex *pindex = NULL;
1297 int target_confirms = 1;
1298 isminefilter filter = MINE_SPENDABLE;
1300 if (params.size() > 0)
1302 uint256 blockId = 0;
1304 blockId.SetHex(params[0].get_str());
1305 pindex = CBlockLocator(blockId).GetBlockIndex();
1308 if (params.size() > 1)
1310 target_confirms = params[1].get_int();
1312 if (target_confirms < 1)
1313 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter");
1316 if(params.size() > 2)
1317 if(params[2].get_bool())
1318 filter = filter | MINE_WATCH_ONLY;
1320 int depth = pindex ? (1 + nBestHeight - pindex->nHeight) : -1;
1324 for (auto it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); it++)
1326 auto tx = (*it).second;
1328 if (depth == -1 || tx.GetDepthInMainChain() < depth)
1329 ListTransactions(tx, "*", 0, true, transactions, filter);
1334 if (target_confirms == 1)
1336 lastblock = hashBestChain;
1340 int target_height = pindexBest->nHeight + 1 - target_confirms;
1343 for (block = pindexBest;
1344 block && block->nHeight > target_height;
1345 block = block->pprev) { }
1347 lastblock = block ? block->GetBlockHash() : 0;
1351 ret.push_back(Pair("transactions", transactions));
1352 ret.push_back(Pair("lastblock", lastblock.GetHex()));
1357 Value gettransaction(const Array& params, bool fHelp)
1359 if (fHelp || params.size() != 1)
1360 throw runtime_error(
1361 "gettransaction <txid>\n"
1362 "Get detailed information about <txid>");
1365 hash.SetHex(params[0].get_str());
1367 isminefilter filter = MINE_SPENDABLE;
1368 if(params.size() > 1)
1369 if(params[1].get_bool())
1370 filter = filter | MINE_WATCH_ONLY;
1374 if (pwalletMain->mapWallet.count(hash))
1376 const CWalletTx& wtx = pwalletMain->mapWallet[hash];
1378 TxToJSON(wtx, 0, entry);
1380 auto nCredit = wtx.GetCredit(filter);
1381 auto nDebit = wtx.GetDebit(filter);
1382 auto nNet = nCredit - nDebit;
1383 auto nFee = (wtx.IsFromMe(filter) ? wtx.GetValueOut() - nDebit : 0);
1385 entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee)));
1386 if (wtx.IsFromMe(filter))
1387 entry.push_back(Pair("fee", ValueFromAmount(nFee)));
1389 WalletTxToJSON(wtx, entry);
1392 ListTransactions(pwalletMain->mapWallet[hash], "*", 0, false, details, filter);
1393 entry.push_back(Pair("details", details));
1398 uint256 hashBlock = 0;
1399 if (GetTransaction(hash, tx, hashBlock))
1401 TxToJSON(tx, 0, entry);
1403 entry.push_back(Pair("confirmations", 0));
1406 entry.push_back(Pair("blockhash", hashBlock.GetHex()));
1407 auto mi = mapBlockIndex.find(hashBlock);
1408 if (mi != mapBlockIndex.end() && (*mi).second)
1410 auto pindex = (*mi).second;
1411 if (pindex->IsInMainChain())
1412 entry.push_back(Pair("confirmations", 1 + nBestHeight - pindex->nHeight));
1414 entry.push_back(Pair("confirmations", 0));
1419 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction");
1426 Value backupwallet(const Array& params, bool fHelp)
1428 if (fHelp || params.size() != 1)
1429 throw runtime_error(
1430 "backupwallet <destination>\n"
1431 "Safely copies wallet.dat to destination, which can be a directory or a path with filename.");
1433 auto strDest = params[0].get_str();
1434 if (!BackupWallet(*pwalletMain, strDest))
1435 throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet backup failed!");
1441 Value keypoolrefill(const Array& params, bool fHelp)
1443 if (fHelp || params.size() > 1)
1444 throw runtime_error(
1445 "keypoolrefill [new-size]\n"
1446 "Fills the keypool.\n"
1447 "IMPORTANT: Any previous backups you have made of your wallet file "
1448 "should be replaced with the newly generated one."
1449 + HelpRequiringPassphrase());
1451 unsigned int nSize = max<unsigned int>(GetArgUInt("-keypool", 100), 0);
1452 if (params.size() > 0) {
1453 if (params[0].get_int() < 0)
1454 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected valid size");
1455 nSize = (unsigned int) params[0].get_int();
1458 EnsureWalletIsUnlocked();
1460 pwalletMain->TopUpKeyPool(nSize);
1462 if (pwalletMain->GetKeyPoolSize() < nSize)
1463 throw JSONRPCError(RPC_WALLET_ERROR, "Error refreshing keypool.");
1468 Value keypoolreset(const Array& params, bool fHelp)
1470 if (fHelp || params.size() > 1)
1471 throw runtime_error(
1472 "keypoolreset [new-size]\n"
1473 "Resets the keypool.\n"
1474 "IMPORTANT: Any previous backups you have made of your wallet file "
1475 "should be replaced with the newly generated one."
1476 + HelpRequiringPassphrase());
1478 unsigned int nSize = max<unsigned int>(GetArgUInt("-keypool", 100), 0);
1479 if (params.size() > 0) {
1480 if (params[0].get_int() < 0)
1481 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected valid size");
1482 nSize = (unsigned int) params[0].get_int();
1485 EnsureWalletIsUnlocked();
1487 pwalletMain->NewKeyPool(nSize);
1489 if (pwalletMain->GetKeyPoolSize() < nSize)
1490 throw JSONRPCError(RPC_WALLET_ERROR, "Error refreshing keypool.");
1496 void ThreadTopUpKeyPool(void* parg)
1498 // Make this thread recognisable as the key-topping-up thread
1499 RenameThread("novacoin-key-top");
1501 pwalletMain->TopUpKeyPool();
1504 void ThreadCleanWalletPassphrase(void* parg)
1506 // Make this thread recognisable as the wallet relocking thread
1507 RenameThread("novacoin-lock-wa");
1509 auto nMyWakeTime = GetTimeMillis() + *((int64_t*)parg) * 1000;
1511 ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime);
1513 if (nWalletUnlockTime == 0)
1515 nWalletUnlockTime = nMyWakeTime;
1519 if (nWalletUnlockTime==0)
1521 auto nToSleep = nWalletUnlockTime - GetTimeMillis();
1525 LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime);
1527 ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime);
1531 if (nWalletUnlockTime)
1533 nWalletUnlockTime = 0;
1534 pwalletMain->Lock();
1539 if (nWalletUnlockTime < nMyWakeTime)
1540 nWalletUnlockTime = nMyWakeTime;
1543 LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime);
1545 delete (int64_t*)parg;
1548 Value walletpassphrase(const Array& params, bool fHelp)
1550 if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 3))
1551 throw runtime_error(
1552 "walletpassphrase <passphrase> <timeout> [mintonly]\n"
1553 "Stores the wallet decryption key in memory for <timeout> seconds.\n"
1554 "mintonly is optional true/false allowing only block minting.");
1557 if (!pwalletMain->IsCrypted())
1558 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called.");
1560 if (!pwalletMain->IsLocked())
1561 throw JSONRPCError(RPC_WALLET_ALREADY_UNLOCKED, "Error: Wallet is already unlocked, use walletlock first if need to change unlock settings.");
1562 // Note that the walletpassphrase is stored in params[0] which is not mlock()ed
1563 SecureString strWalletPass;
1564 strWalletPass.reserve(100);
1565 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1566 // Alternately, find a way to make params[0] mlock()'d to begin with.
1567 strWalletPass = params[0].get_str().c_str();
1569 if (strWalletPass.length() > 0)
1571 if (!pwalletMain->Unlock(strWalletPass))
1572 throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
1575 throw runtime_error(
1576 "walletpassphrase <passphrase> <timeout>\n"
1577 "Stores the wallet decryption key in memory for <timeout> seconds.");
1579 NewThread(ThreadTopUpKeyPool, NULL);
1580 int64_t* pnSleepTime = new int64_t(params[1].get_int64());
1581 NewThread(ThreadCleanWalletPassphrase, pnSleepTime);
1583 // ppcoin: if user OS account compromised prevent trivial sendmoney commands
1584 if (params.size() > 2)
1585 fWalletUnlockMintOnly = params[2].get_bool();
1587 fWalletUnlockMintOnly = false;
1593 Value walletpassphrasechange(const Array& params, bool fHelp)
1595 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2))
1596 throw runtime_error(
1597 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1598 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1601 if (!pwalletMain->IsCrypted())
1602 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrasechange was called.");
1604 // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string)
1605 // Alternately, find a way to make params[0] mlock()'d to begin with.
1606 SecureString strOldWalletPass;
1607 strOldWalletPass.reserve(100);
1608 strOldWalletPass = params[0].get_str().c_str();
1610 SecureString strNewWalletPass;
1611 strNewWalletPass.reserve(100);
1612 strNewWalletPass = params[1].get_str().c_str();
1614 if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1)
1615 throw runtime_error(
1616 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1617 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1619 if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass))
1620 throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
1626 Value walletlock(const Array& params, bool fHelp)
1628 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 0))
1629 throw runtime_error(
1631 "Removes the wallet encryption key from memory, locking the wallet.\n"
1632 "After calling this method, you will need to call walletpassphrase again\n"
1633 "before being able to call any methods which require the wallet to be unlocked.");
1636 if (!pwalletMain->IsCrypted())
1637 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletlock was called.");
1640 LOCK(cs_nWalletUnlockTime);
1641 pwalletMain->Lock();
1642 nWalletUnlockTime = 0;
1649 Value encryptwallet(const Array& params, bool fHelp)
1651 if (!pwalletMain->IsCrypted() && (fHelp || params.size() != 1))
1652 throw runtime_error(
1653 "encryptwallet <passphrase>\n"
1654 "Encrypts the wallet with <passphrase>.");
1657 if (pwalletMain->IsCrypted())
1658 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an encrypted wallet, but encryptwallet was called.");
1660 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1661 // Alternately, find a way to make params[0] mlock()'d to begin with.
1662 SecureString strWalletPass;
1663 strWalletPass.reserve(100);
1664 strWalletPass = params[0].get_str().c_str();
1666 if (strWalletPass.length() < 1)
1667 throw runtime_error(
1668 "encryptwallet <passphrase>\n"
1669 "Encrypts the wallet with <passphrase>.");
1671 if (!pwalletMain->EncryptWallet(strWalletPass))
1672 throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: Failed to encrypt the wallet.");
1674 // BDB seems to have a bad habit of writing old data into
1675 // slack space in .dat files; that is bad if the old data is
1676 // unencrypted private keys. So:
1678 return "wallet encrypted; NovaCoin server stopping, restart to run with encrypted wallet. The keypool has been flushed, you need to make a new backup.";
1681 class DescribeAddressVisitor : public boost::static_visitor<Object>
1686 DescribeAddressVisitor(isminetype mineIn) : mine(mineIn) {}
1688 Object operator()(const CNoDestination &dest) const { return Object(); }
1689 Object operator()(const CKeyID &keyID) const {
1692 pwalletMain->GetPubKey(keyID, vchPubKey);
1693 obj.push_back(Pair("isscript", false));
1694 if (mine == MINE_SPENDABLE) {
1695 pwalletMain->GetPubKey(keyID, vchPubKey);
1696 obj.push_back(Pair("pubkey", HexStr(vchPubKey.begin(), vchPubKey.end())));
1697 obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed()));
1702 Object operator()(const CScriptID &scriptID) const {
1704 obj.push_back(Pair("isscript", true));
1705 if (mine == MINE_SPENDABLE) {
1707 pwalletMain->GetCScript(scriptID, subscript);
1708 vector<CTxDestination> addresses;
1709 txnouttype whichType;
1711 ExtractDestinations(subscript, whichType, addresses, nRequired);
1712 obj.push_back(Pair("script", GetTxnOutputType(whichType)));
1713 obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end())));
1715 for(const auto& addr : addresses)
1716 a.push_back(CBitcoinAddress(addr).ToString());
1717 obj.push_back(Pair("addresses", a));
1718 if (whichType == TX_MULTISIG)
1719 obj.push_back(Pair("sigsrequired", nRequired));
1725 Value validateaddress(const Array& params, bool fHelp)
1727 if (fHelp || params.size() != 1)
1728 throw runtime_error(
1729 "validateaddress <novacoinaddress>\n"
1730 "Return information about <novacoinaddress>.");
1732 CBitcoinAddress address(params[0].get_str());
1733 bool isValid = address.IsValid();
1736 ret.push_back(Pair("isvalid", isValid));
1739 if (address.IsPair())
1741 CMalleablePubKey mpk;
1742 mpk.setvch(address.GetData());
1743 ret.push_back(Pair("ispair", true));
1745 CMalleableKeyView view;
1746 bool isMine = pwalletMain->GetMalleableView(mpk, view);
1747 ret.push_back(Pair("ismine", isMine));
1748 ret.push_back(Pair("PubkeyPair", mpk.ToString()));
1751 ret.push_back(Pair("KeyView", view.ToString()));
1755 auto currentAddress = address.ToString();
1756 auto dest = address.Get();
1757 ret.push_back(Pair("address", currentAddress));
1758 auto mine = IsMine(*pwalletMain, address);
1759 ret.push_back(Pair("ismine", mine != MINE_NO));
1760 if (mine != MINE_NO) {
1761 ret.push_back(Pair("watchonly", mine == MINE_WATCH_ONLY));
1762 Object detail = boost::apply_visitor(DescribeAddressVisitor(mine), dest);
1763 ret.insert(ret.end(), detail.begin(), detail.end());
1765 if (pwalletMain->mapAddressBook.count(address))
1766 ret.push_back(Pair("account", pwalletMain->mapAddressBook[address]));
1772 // ppcoin: reserve balance from being staked for network protection
1773 Value reservebalance(const Array& params, bool fHelp)
1775 if (fHelp || params.size() > 2)
1776 throw runtime_error(
1777 "reservebalance [<reserve> [amount]]\n"
1778 "<reserve> is true or false to turn balance reserve on or off.\n"
1779 "<amount> is a real and rounded to cent.\n"
1780 "Set reserve amount not participating in network protection.\n"
1781 "If no parameters provided current setting is printed.\n");
1783 if (params.size() > 0)
1785 bool fReserve = params[0].get_bool();
1788 if (params.size() == 1)
1789 throw runtime_error("must provide amount to reserve balance.\n");
1790 auto nAmount = AmountFromValue(params[1]);
1791 nAmount = (nAmount / CENT) * CENT; // round to cent
1793 throw runtime_error("amount cannot be negative.\n");
1794 mapArgs["-reservebalance"] = FormatMoney(nAmount);
1798 if (params.size() > 1)
1799 throw runtime_error("cannot specify amount to turn off reserve.\n");
1800 mapArgs["-reservebalance"] = "0";
1805 if (mapArgs.count("-reservebalance") && !ParseMoney(mapArgs["-reservebalance"], nReserveBalance))
1806 throw runtime_error("invalid reserve balance amount\n");
1807 result.push_back(Pair("reserve", (nReserveBalance > 0)));
1808 result.push_back(Pair("amount", ValueFromAmount(nReserveBalance)));
1813 // ppcoin: check wallet integrity
1814 Value checkwallet(const Array& params, bool fHelp)
1816 if (fHelp || params.size() > 0)
1817 throw runtime_error(
1819 "Check wallet for integrity.\n");
1822 int64_t nBalanceInQuestion;
1823 pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion, true);
1825 if (nMismatchSpent == 0)
1826 result.push_back(Pair("wallet check passed", true));
1829 result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1830 result.push_back(Pair("amount in question", ValueFromAmount(nBalanceInQuestion)));
1836 // ppcoin: repair wallet
1837 Value repairwallet(const Array& params, bool fHelp)
1839 if (fHelp || params.size() > 0)
1840 throw runtime_error(
1842 "Repair wallet if checkwallet reports any problem.\n");
1845 int64_t nBalanceInQuestion;
1846 pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion);
1848 if (nMismatchSpent == 0)
1849 result.push_back(Pair("wallet check passed", true));
1852 result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1853 result.push_back(Pair("amount affected by repair", ValueFromAmount(nBalanceInQuestion)));
1858 // NovaCoin: resend unconfirmed wallet transactions
1859 Value resendtx(const Array& params, bool fHelp)
1861 if (fHelp || params.size() > 1)
1862 throw runtime_error(
1864 "Re-send unconfirmed transactions.\n"
1867 ResendWalletTransactions(true);
1872 Value resendwallettransactions(const Array& params, bool fHelp)
1874 if (fHelp || params.size() != 0)
1875 throw runtime_error(
1876 "resendwallettransactions\n"
1877 "Immediately re-broadcast unconfirmed wallet transactions to all peers.\n"
1878 "Intended only for testing; the wallet code periodically re-broadcasts\n"
1880 "Returns array of transaction ids that were re-broadcast.\n"
1883 LOCK2(cs_main, pwalletMain->cs_wallet);
1885 auto txids = pwalletMain->ResendWalletTransactionsBefore(GetTime());
1887 for(const auto& txid : txids)
1889 result.push_back(txid.ToString());
1895 // Make a public-private key pair
1896 Value makekeypair(const Array& params, bool fHelp)
1898 if (fHelp || params.size() > 0)
1899 throw runtime_error(
1901 "Make a public/private key pair.\n");
1903 string strPrefix = "";
1904 if (params.size() > 0)
1905 strPrefix = params[0].get_str();
1908 key.MakeNewKey(true);
1910 auto vchPrivKey = key.GetPrivKey();
1912 result.push_back(Pair("PrivateKey", HexStr<CPrivKey::iterator>(vchPrivKey.begin(), vchPrivKey.end())));
1915 auto vchSecret = key.GetSecret(fCompressed);
1916 auto vchPubKey = key.GetPubKey();
1917 result.push_back(Pair("Secret", HexStr<CSecret::iterator>(vchSecret.begin(), vchSecret.end())));
1918 result.push_back(Pair("PublicKey", HexStr(vchPubKey.begin(), vchPubKey.end())));
1922 Value newmalleablekey(const Array& params, bool fHelp)
1924 if (fHelp || params.size() > 1)
1925 throw runtime_error(
1927 "Make a malleable public/private key pair.\n");
1929 // Parse the account first so we don't generate a key if there's an error
1931 if (params.size() > 0)
1932 strAccount = AccountFromValue(params[0]);
1934 auto keyView = pwalletMain->GenerateNewMalleableKey();
1937 if (!pwalletMain->GetMalleableKey(keyView, mKey))
1938 throw runtime_error("Unable to generate new malleable key");
1940 auto mPubKey = mKey.GetMalleablePubKey();
1941 CBitcoinAddress address(mPubKey);
1943 pwalletMain->SetAddressBookName(address, strAccount);
1946 result.push_back(Pair("PublicPair", mPubKey.ToString()));
1947 result.push_back(Pair("PublicBytes", HexStr(mPubKey.Raw())));
1948 result.push_back(Pair("Address", address.ToString()));
1949 result.push_back(Pair("KeyView", keyView.ToString()));
1954 Value adjustmalleablekey(const Array& params, bool fHelp)
1956 if (fHelp || params.size() != 3)
1957 throw runtime_error(
1958 "adjustmalleablekey <Malleable key data> <Public key variant data> <R data>\n"
1959 "Calculate new private key using provided malleable key, public key and R data.\n");
1961 CMalleableKey malleableKey;
1962 malleableKey.SetString(params[0].get_str());
1964 CKey privKeyVariant;
1965 auto vchPubKeyVariant = CPubKey(ParseHex(params[1].get_str()));
1967 CPubKey R(ParseHex(params[2].get_str()));
1969 if (!malleableKey.CheckKeyVariant(R,vchPubKeyVariant, privKeyVariant)) {
1970 throw runtime_error("Unable to calculate the private key");
1975 auto vchPrivKeyVariant = privKeyVariant.GetSecret(fCompressed);
1977 result.push_back(Pair("PrivateKey", CBitcoinSecret(vchPrivKeyVariant, fCompressed).ToString()));
1982 Value adjustmalleablepubkey(const Array& params, bool fHelp)
1984 if (fHelp || params.size() > 2 || params.size() == 0)
1985 throw runtime_error(
1986 "adjustmalleablepubkey <Malleable address, key view or public key pair>\n"
1987 "Calculate new public key using provided data.\n");
1989 auto strData = params[0].get_str();
1990 CMalleablePubKey malleablePubKey;
1994 CBitcoinAddress addr(strData);
1995 if (addr.IsValid() && addr.IsPair())
1997 // Initialize malleable pubkey with address data
1998 malleablePubKey = CMalleablePubKey(addr.GetData());
2001 CMalleableKeyView viewTmp(strData);
2002 if (viewTmp.IsValid())
2004 // Shazaam, we have a valid key view here.
2005 malleablePubKey = viewTmp.GetMalleablePubKey();
2008 if (malleablePubKey.SetString(strData))
2009 break; // A valid public key pair
2011 throw runtime_error("Though your data seems a valid Base58 string, we were unable to recognize it.");
2015 CPubKey R, vchPubKeyVariant;
2016 malleablePubKey.GetVariant(R, vchPubKeyVariant);
2019 result.push_back(Pair("R", HexStr(R.begin(), R.end())));
2020 result.push_back(Pair("PubkeyVariant", HexStr(vchPubKeyVariant.begin(), vchPubKeyVariant.end())));
2021 result.push_back(Pair("KeyVariantID", CBitcoinAddress(vchPubKeyVariant.GetID()).ToString()));
2026 Value listmalleableviews(const Array& params, bool fHelp)
2028 if (fHelp || params.size() != 0)
2029 throw runtime_error(
2030 "listmalleableviews\n"
2031 "Get list of views for generated malleable keys.\n");
2033 list<CMalleableKeyView> keyViewList;
2034 pwalletMain->ListMalleableViews(keyViewList);
2037 for(const auto &keyView : keyViewList)
2038 result.push_back(keyView.ToString());