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"
12 using namespace json_spirit;
15 int64_t nWalletUnlockTime;
16 static CCriticalSection cs_nWalletUnlockTime;
18 extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, json_spirit::Object& entry);
20 std::string HelpRequiringPassphrase()
22 return pwalletMain->IsCrypted()
23 ? "\nrequires wallet passphrase to be set with walletpassphrase first"
27 void EnsureWalletIsUnlocked()
29 if (pwalletMain->IsLocked())
30 throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first.");
31 if (fWalletUnlockMintOnly)
32 throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Wallet unlocked for block minting only.");
35 void WalletTxToJSON(const CWalletTx& wtx, Object& entry)
37 int confirms = wtx.GetDepthInMainChain();
38 entry.push_back(Pair("confirmations", confirms));
39 if (wtx.IsCoinBase() || wtx.IsCoinStake())
40 entry.push_back(Pair("generated", true));
43 entry.push_back(Pair("blockhash", wtx.hashBlock.GetHex()));
44 entry.push_back(Pair("blockindex", wtx.nIndex));
45 entry.push_back(Pair("blocktime", (boost::int64_t)(mapBlockIndex[wtx.hashBlock]->nTime)));
47 entry.push_back(Pair("txid", wtx.GetHash().GetHex()));
48 entry.push_back(Pair("time", (boost::int64_t)wtx.GetTxTime()));
49 entry.push_back(Pair("timereceived", (boost::int64_t)wtx.nTimeReceived));
50 BOOST_FOREACH(const PAIRTYPE(string,string)& item, wtx.mapValue)
51 entry.push_back(Pair(item.first, item.second));
54 string AccountFromValue(const Value& value)
56 string strAccount = value.get_str();
57 if (strAccount == "*")
58 throw JSONRPCError(RPC_WALLET_INVALID_ACCOUNT_NAME, "Invalid account name");
62 Value getinfo(const Array& params, bool fHelp)
64 if (fHelp || params.size() != 0)
67 "Returns an object containing various state info.");
70 GetProxy(NET_IPV4, proxy);
73 obj.push_back(Pair("version", FormatFullVersion()));
74 obj.push_back(Pair("protocolversion",(int)PROTOCOL_VERSION));
75 obj.push_back(Pair("walletversion", pwalletMain->GetVersion()));
76 obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance())));
77 obj.push_back(Pair("unspendable", ValueFromAmount(pwalletMain->GetWatchOnlyBalance())));
78 obj.push_back(Pair("newmint", ValueFromAmount(pwalletMain->GetNewMint())));
79 obj.push_back(Pair("stake", ValueFromAmount(pwalletMain->GetStake())));
80 obj.push_back(Pair("blocks", (int)nBestHeight));
81 obj.push_back(Pair("timeoffset", (boost::int64_t)GetTimeOffset()));
82 obj.push_back(Pair("moneysupply", ValueFromAmount(pindexBest->nMoneySupply)));
83 obj.push_back(Pair("connections", (int)vNodes.size()));
84 obj.push_back(Pair("proxy", (proxy.first.IsValid() ? proxy.first.ToStringIPPort() : string())));
85 obj.push_back(Pair("ip", addrSeenByPeer.ToStringIP()));
87 diff.push_back(Pair("proof-of-work", GetDifficulty()));
88 diff.push_back(Pair("proof-of-stake", GetDifficulty(GetLastBlockIndex(pindexBest, true))));
89 obj.push_back(Pair("difficulty", diff));
91 obj.push_back(Pair("testnet", fTestNet));
92 obj.push_back(Pair("keypoololdest", (boost::int64_t)pwalletMain->GetOldestKeyPoolTime()));
93 obj.push_back(Pair("keypoolsize", (int)pwalletMain->GetKeyPoolSize()));
94 obj.push_back(Pair("paytxfee", ValueFromAmount(nTransactionFee)));
95 obj.push_back(Pair("mininput", ValueFromAmount(nMinimumInputValue)));
96 if (pwalletMain->IsCrypted())
97 obj.push_back(Pair("unlocked_until", (boost::int64_t)nWalletUnlockTime / 1000));
98 obj.push_back(Pair("errors", GetWarnings("statusbar")));
102 Value getnewaddress(const Array& params, bool fHelp)
104 if (fHelp || params.size() > 1)
106 "getnewaddress [account]\n"
107 "Returns a new NovaCoin address for receiving payments. "
108 "If [account] is specified (recommended), it is added to the address book "
109 "so payments received with the address will be credited to [account].");
111 // Parse the account first so we don't generate a key if there's an error
113 if (params.size() > 0)
114 strAccount = AccountFromValue(params[0]);
116 if (!pwalletMain->IsLocked())
117 pwalletMain->TopUpKeyPool();
119 // Generate a new key that is added to wallet
121 if (!pwalletMain->GetKeyFromPool(newKey, false))
122 throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
123 CKeyID keyID = newKey.GetID();
125 pwalletMain->SetAddressBookName(keyID, strAccount);
127 return CBitcoinAddress(keyID).ToString();
131 CBitcoinAddress GetAccountAddress(string strAccount, bool bForceNew=false)
133 CWalletDB walletdb(pwalletMain->strWalletFile);
136 walletdb.ReadAccount(strAccount, account);
138 bool bKeyUsed = false;
140 // Check if the current key has been used
141 if (account.vchPubKey.IsValid())
143 CScript scriptPubKey;
144 scriptPubKey.SetDestination(account.vchPubKey.GetID());
145 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin();
146 it != pwalletMain->mapWallet.end() && account.vchPubKey.IsValid();
149 const CWalletTx& wtx = (*it).second;
150 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
151 if (txout.scriptPubKey == scriptPubKey)
156 // Generate a new key
157 if (!account.vchPubKey.IsValid() || bForceNew || bKeyUsed)
159 if (!pwalletMain->GetKeyFromPool(account.vchPubKey, false))
160 throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
162 pwalletMain->SetAddressBookName(account.vchPubKey.GetID(), strAccount);
163 walletdb.WriteAccount(strAccount, account);
166 return CBitcoinAddress(account.vchPubKey.GetID());
169 Value getaccountaddress(const Array& params, bool fHelp)
171 if (fHelp || params.size() != 1)
173 "getaccountaddress <account>\n"
174 "Returns the current NovaCoin address for receiving payments to this account.");
176 // Parse the account first so we don't generate a key if there's an error
177 string strAccount = AccountFromValue(params[0]);
181 ret = GetAccountAddress(strAccount).ToString();
188 Value setaccount(const Array& params, bool fHelp)
190 if (fHelp || params.size() < 1 || params.size() > 2)
192 "setaccount <novacoinaddress> <account>\n"
193 "Sets the account associated with the given address.");
195 CBitcoinAddress address(params[0].get_str());
196 if (!address.IsValid())
197 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
201 if (params.size() > 1)
202 strAccount = AccountFromValue(params[1]);
204 // Detect when changing the account of an address that is the 'unused current key' of another account:
205 if (pwalletMain->mapAddressBook.count(address.Get()))
207 string strOldAccount = pwalletMain->mapAddressBook[address.Get()];
208 if (address == GetAccountAddress(strOldAccount))
209 GetAccountAddress(strOldAccount, true);
212 pwalletMain->SetAddressBookName(address.Get(), strAccount);
218 Value getaccount(const Array& params, bool fHelp)
220 if (fHelp || params.size() != 1)
222 "getaccount <novacoinaddress>\n"
223 "Returns the account associated with the given address.");
225 CBitcoinAddress address(params[0].get_str());
226 if (!address.IsValid())
227 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
230 map<CTxDestination, string>::iterator mi = pwalletMain->mapAddressBook.find(address.Get());
231 if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.empty())
232 strAccount = (*mi).second;
237 Value getaddressesbyaccount(const Array& params, bool fHelp)
239 if (fHelp || params.size() != 1)
241 "getaddressesbyaccount <account>\n"
242 "Returns the list of addresses for the given account.");
244 string strAccount = AccountFromValue(params[0]);
246 // Find all addresses that have the given account
248 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
250 const CBitcoinAddress& address = item.first;
251 const string& strName = item.second;
252 if (strName == strAccount)
253 ret.push_back(address.ToString());
258 Value mergecoins(const Array& params, bool fHelp)
260 if (fHelp || params.size() != 3)
262 "mergecoins <amount> <minvalue> <outputvalue>\n"
263 "<amount> is resulting inputs sum\n"
264 "<minvalue> is minimum value of inputs which are used in join process\n"
265 "<outputvalue> is resulting value of inputs which will be created\n"
266 "All values are real and and rounded to the nearest " + FormatMoney(MIN_TXOUT_AMOUNT)
267 + HelpRequiringPassphrase());
269 if (pwalletMain->IsLocked())
270 throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first.");
273 int64_t nAmount = AmountFromValue(params[0]);
276 int64_t nMinValue = AmountFromValue(params[1]);
279 int64_t nOutputValue = AmountFromValue(params[2]);
281 if (nAmount < MIN_TXOUT_AMOUNT)
282 throw JSONRPCError(-101, "Send amount too small");
284 if (nMinValue < MIN_TXOUT_AMOUNT)
285 throw JSONRPCError(-101, "Max value too small");
287 if (nOutputValue < MIN_TXOUT_AMOUNT)
288 throw JSONRPCError(-101, "Output value too small");
290 if (nOutputValue < nMinValue)
291 throw JSONRPCError(-101, "Output value is lower than min value");
293 list<uint256> listMerged;
294 if (!pwalletMain->MergeCoins(nAmount, nMinValue, nOutputValue, listMerged))
298 BOOST_FOREACH(const uint256 txHash, listMerged)
299 mergedHashes.push_back(txHash.GetHex());
304 Value sendtoaddress(const Array& params, bool fHelp)
306 if (fHelp || params.size() < 2 || params.size() > 4)
308 "sendtoaddress <novacoinaddress> <amount> [comment] [comment-to]\n"
309 "<amount> is a real and is rounded to the nearest " + FormatMoney(MIN_TXOUT_AMOUNT)
310 + HelpRequiringPassphrase());
312 CBitcoinAddress address(params[0].get_str());
313 if (!address.IsValid())
314 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
317 int64_t nAmount = AmountFromValue(params[1]);
319 if (nAmount < MIN_TXOUT_AMOUNT)
320 throw JSONRPCError(-101, "Send amount too small");
324 if (params.size() > 2 && params[2].type() != null_type && !params[2].get_str().empty())
325 wtx.mapValue["comment"] = params[2].get_str();
326 if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
327 wtx.mapValue["to"] = params[3].get_str();
329 if (pwalletMain->IsLocked())
330 throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first.");
332 string strError = pwalletMain->SendMoneyToDestination(address.Get(), nAmount, wtx);
334 throw JSONRPCError(RPC_WALLET_ERROR, strError);
336 return wtx.GetHash().GetHex();
339 Value listaddressgroupings(const Array& params, bool fHelp)
343 "listaddressgroupings\n"
344 "Lists groups of addresses which have had their common ownership\n"
345 "made public by common use as inputs or as the resulting change\n"
346 "in past transactions");
349 map<CTxDestination, int64_t> balances = pwalletMain->GetAddressBalances();
350 BOOST_FOREACH(set<CTxDestination> grouping, pwalletMain->GetAddressGroupings())
353 BOOST_FOREACH(CTxDestination address, grouping)
356 addressInfo.push_back(CBitcoinAddress(address).ToString());
357 addressInfo.push_back(ValueFromAmount(balances[address]));
359 LOCK(pwalletMain->cs_wallet);
360 if (pwalletMain->mapAddressBook.find(CBitcoinAddress(address).Get()) != pwalletMain->mapAddressBook.end())
361 addressInfo.push_back(pwalletMain->mapAddressBook.find(CBitcoinAddress(address).Get())->second);
363 jsonGrouping.push_back(addressInfo);
365 jsonGroupings.push_back(jsonGrouping);
367 return jsonGroupings;
370 Value signmessage(const Array& params, bool fHelp)
372 if (fHelp || params.size() != 2)
374 "signmessage <novacoinaddress> <message>\n"
375 "Sign a message with the private key of an address");
377 EnsureWalletIsUnlocked();
379 string strAddress = params[0].get_str();
380 string strMessage = params[1].get_str();
382 CBitcoinAddress addr(strAddress);
384 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
387 if (!addr.GetKeyID(keyID))
388 throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
391 if (!pwalletMain->GetKey(keyID, key))
392 throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available");
394 CDataStream ss(SER_GETHASH, 0);
395 ss << strMessageMagic;
398 vector<unsigned char> vchSig;
399 if (!key.SignCompact(Hash(ss.begin(), ss.end()), vchSig))
400 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed");
402 return EncodeBase64(&vchSig[0], vchSig.size());
405 Value verifymessage(const Array& params, bool fHelp)
407 if (fHelp || params.size() != 3)
409 "verifymessage <novacoinaddress> <signature> <message>\n"
410 "Verify a signed message");
412 string strAddress = params[0].get_str();
413 string strSign = params[1].get_str();
414 string strMessage = params[2].get_str();
416 CBitcoinAddress addr(strAddress);
418 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
421 if (!addr.GetKeyID(keyID))
422 throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
424 bool fInvalid = false;
425 vector<unsigned char> vchSig = DecodeBase64(strSign.c_str(), &fInvalid);
428 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Malformed base64 encoding");
430 CDataStream ss(SER_GETHASH, 0);
431 ss << strMessageMagic;
435 if (!key.SetCompactSignature(Hash(ss.begin(), ss.end()), vchSig))
438 return (key.GetPubKey().GetID() == keyID);
442 Value getreceivedbyaddress(const Array& params, bool fHelp)
444 if (fHelp || params.size() < 1 || params.size() > 2)
446 "getreceivedbyaddress <novacoinaddress> [minconf=1]\n"
447 "Returns the total amount received by <novacoinaddress> in transactions with at least [minconf] confirmations.");
450 CBitcoinAddress address = CBitcoinAddress(params[0].get_str());
451 CScript scriptPubKey;
452 if (!address.IsValid())
453 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
454 scriptPubKey.SetDestination(address.Get());
455 if (!IsMine(*pwalletMain,scriptPubKey))
458 // Minimum confirmations
460 if (params.size() > 1)
461 nMinDepth = params[1].get_int();
465 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
467 const CWalletTx& wtx = (*it).second;
468 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
471 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
472 if (txout.scriptPubKey == scriptPubKey)
473 if (wtx.GetDepthInMainChain() >= nMinDepth)
474 nAmount += txout.nValue;
477 return ValueFromAmount(nAmount);
481 void GetAccountAddresses(string strAccount, set<CTxDestination>& setAddress)
483 BOOST_FOREACH(const PAIRTYPE(CTxDestination, string)& item, pwalletMain->mapAddressBook)
485 const CTxDestination& address = item.first;
486 const string& strName = item.second;
487 if (strName == strAccount)
488 setAddress.insert(address);
492 Value getreceivedbyaccount(const Array& params, bool fHelp)
494 if (fHelp || params.size() < 1 || params.size() > 2)
496 "getreceivedbyaccount <account> [minconf=1]\n"
497 "Returns the total amount received by addresses with <account> in transactions with at least [minconf] confirmations.");
499 // Minimum confirmations
501 if (params.size() > 1)
502 nMinDepth = params[1].get_int();
504 // Get the set of pub keys assigned to account
505 string strAccount = AccountFromValue(params[0]);
506 set<CTxDestination> setAddress;
507 GetAccountAddresses(strAccount, setAddress);
511 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
513 const CWalletTx& wtx = (*it).second;
514 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
517 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
519 CTxDestination address;
520 if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*pwalletMain, address) && setAddress.count(address))
521 if (wtx.GetDepthInMainChain() >= nMinDepth)
522 nAmount += txout.nValue;
526 return (double)nAmount / (double)COIN;
530 int64_t GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth, const isminefilter& filter)
532 int64_t nBalance = 0;
534 // Tally wallet transactions
535 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
537 const CWalletTx& wtx = (*it).second;
541 int64_t nGenerated, nReceived, nSent, nFee;
542 wtx.GetAccountAmounts(strAccount, nGenerated, nReceived, nSent, nFee, filter);
544 if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth)
545 nBalance += nReceived;
546 nBalance += nGenerated - nSent - nFee;
549 // Tally internal accounting entries
550 nBalance += walletdb.GetAccountCreditDebit(strAccount);
555 int64_t GetAccountBalance(const string& strAccount, int nMinDepth, const isminefilter& filter)
557 CWalletDB walletdb(pwalletMain->strWalletFile);
558 return GetAccountBalance(walletdb, strAccount, nMinDepth, filter);
562 Value getbalance(const Array& params, bool fHelp)
564 if (fHelp || params.size() > 2)
566 "getbalance [account] [minconf=1] [watchonly=0]\n"
567 "If [account] is not specified, returns the server's total available balance.\n"
568 "If [account] is specified, returns the balance in the account.\n"
569 "if [includeWatchonly] is specified, include balance in watchonly addresses (see 'importaddress').");
571 if (params.size() == 0)
572 return ValueFromAmount(pwalletMain->GetBalance());
575 if (params.size() > 1)
576 nMinDepth = params[1].get_int();
577 isminefilter filter = MINE_SPENDABLE;
578 if(params.size() > 2)
579 if(params[2].get_bool())
580 filter = filter | MINE_WATCH_ONLY;
582 if (params[0].get_str() == "*") {
583 // Calculate total balance a different way from GetBalance()
584 // (GetBalance() sums up all unspent TxOuts)
585 // getbalance and getbalance '*' 0 should return the same number.
586 int64_t nBalance = 0;
587 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
589 const CWalletTx& wtx = (*it).second;
590 if (!wtx.IsTrusted())
593 int64_t allGeneratedImmature, allGeneratedMature, allFee;
594 allGeneratedImmature = allGeneratedMature = allFee = 0;
596 string strSentAccount;
597 list<pair<CTxDestination, int64_t> > listReceived;
598 list<pair<CTxDestination, int64_t> > listSent;
599 wtx.GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount, filter);
600 if (wtx.GetDepthInMainChain() >= nMinDepth)
602 BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64_t)& r, listReceived)
603 nBalance += r.second;
605 BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64_t)& r, listSent)
606 nBalance -= r.second;
608 nBalance += allGeneratedMature;
610 return ValueFromAmount(nBalance);
613 string strAccount = AccountFromValue(params[0]);
615 int64_t nBalance = GetAccountBalance(strAccount, nMinDepth, filter);
617 return ValueFromAmount(nBalance);
621 Value movecmd(const Array& params, bool fHelp)
623 if (fHelp || params.size() < 3 || params.size() > 5)
625 "move <fromaccount> <toaccount> <amount> [minconf=1] [comment]\n"
626 "Move from one account in your wallet to another.");
628 string strFrom = AccountFromValue(params[0]);
629 string strTo = AccountFromValue(params[1]);
630 int64_t nAmount = AmountFromValue(params[2]);
632 if (nAmount < MIN_TXOUT_AMOUNT)
633 throw JSONRPCError(-101, "Send amount too small");
635 if (params.size() > 3)
636 // unused parameter, used to be nMinDepth, keep type-checking it though
637 (void)params[3].get_int();
639 if (params.size() > 4)
640 strComment = params[4].get_str();
642 CWalletDB walletdb(pwalletMain->strWalletFile);
643 if (!walletdb.TxnBegin())
644 throw JSONRPCError(RPC_DATABASE_ERROR, "database error");
646 int64_t nNow = GetAdjustedTime();
649 CAccountingEntry debit;
650 debit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb);
651 debit.strAccount = strFrom;
652 debit.nCreditDebit = -nAmount;
654 debit.strOtherAccount = strTo;
655 debit.strComment = strComment;
656 walletdb.WriteAccountingEntry(debit);
659 CAccountingEntry credit;
660 credit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb);
661 credit.strAccount = strTo;
662 credit.nCreditDebit = nAmount;
664 credit.strOtherAccount = strFrom;
665 credit.strComment = strComment;
666 walletdb.WriteAccountingEntry(credit);
668 if (!walletdb.TxnCommit())
669 throw JSONRPCError(RPC_DATABASE_ERROR, "database error");
675 Value sendfrom(const Array& params, bool fHelp)
677 if (fHelp || params.size() < 3 || params.size() > 6)
679 "sendfrom <fromaccount> <tonovacoinaddress> <amount> [minconf=1] [comment] [comment-to]\n"
680 "<amount> is a real and is rounded to the nearest " + FormatMoney(MIN_TXOUT_AMOUNT)
681 + HelpRequiringPassphrase());
683 string strAccount = AccountFromValue(params[0]);
684 CBitcoinAddress address(params[1].get_str());
685 if (!address.IsValid())
686 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
687 int64_t nAmount = AmountFromValue(params[2]);
689 if (nAmount < MIN_TXOUT_AMOUNT)
690 throw JSONRPCError(-101, "Send amount too small");
693 if (params.size() > 3)
694 nMinDepth = params[3].get_int();
697 wtx.strFromAccount = strAccount;
698 if (params.size() > 4 && params[4].type() != null_type && !params[4].get_str().empty())
699 wtx.mapValue["comment"] = params[4].get_str();
700 if (params.size() > 5 && params[5].type() != null_type && !params[5].get_str().empty())
701 wtx.mapValue["to"] = params[5].get_str();
703 EnsureWalletIsUnlocked();
706 int64_t nBalance = GetAccountBalance(strAccount, nMinDepth, MINE_SPENDABLE);
707 if (nAmount > nBalance)
708 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds");
711 string strError = pwalletMain->SendMoneyToDestination(address.Get(), nAmount, wtx);
713 throw JSONRPCError(RPC_WALLET_ERROR, strError);
715 return wtx.GetHash().GetHex();
719 Value sendmany(const Array& params, bool fHelp)
721 if (fHelp || params.size() < 2 || params.size() > 4)
723 "sendmany <fromaccount> {address:amount,...} [minconf=1] [comment]\n"
724 "amounts are double-precision floating point numbers"
725 + HelpRequiringPassphrase());
727 string strAccount = AccountFromValue(params[0]);
728 Object sendTo = params[1].get_obj();
730 if (params.size() > 2)
731 nMinDepth = params[2].get_int();
734 wtx.strFromAccount = strAccount;
735 if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
736 wtx.mapValue["comment"] = params[3].get_str();
738 set<CBitcoinAddress> setAddress;
739 vector<pair<CScript, int64_t> > vecSend;
741 int64_t totalAmount = 0;
742 BOOST_FOREACH(const Pair& s, sendTo)
744 CBitcoinAddress address(s.name_);
745 if (!address.IsValid())
746 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid NovaCoin address: ")+s.name_);
748 if (setAddress.count(address))
749 throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+s.name_);
750 setAddress.insert(address);
752 CScript scriptPubKey;
753 scriptPubKey.SetDestination(address.Get());
754 int64_t nAmount = AmountFromValue(s.value_);
756 if (nAmount < MIN_TXOUT_AMOUNT)
757 throw JSONRPCError(-101, "Send amount too small");
759 totalAmount += nAmount;
761 vecSend.push_back(make_pair(scriptPubKey, nAmount));
764 EnsureWalletIsUnlocked();
767 int64_t nBalance = GetAccountBalance(strAccount, nMinDepth, MINE_SPENDABLE);
768 if (totalAmount > nBalance)
769 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds");
772 CReserveKey keyChange(pwalletMain);
773 int64_t nFeeRequired = 0;
774 bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired);
777 int64_t nTotal = pwalletMain->GetBalance(), nWatchOnly = pwalletMain->GetWatchOnlyBalance();
778 if (totalAmount + nFeeRequired > nTotal - nWatchOnly)
779 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds");
780 throw JSONRPCError(RPC_WALLET_ERROR, "Transaction creation failed");
782 if (!pwalletMain->CommitTransaction(wtx, keyChange))
783 throw JSONRPCError(RPC_WALLET_ERROR, "Transaction commit failed");
785 return wtx.GetHash().GetHex();
788 Value addmultisigaddress(const Array& params, bool fHelp)
790 if (fHelp || params.size() < 2 || params.size() > 3)
792 string msg = "addmultisigaddress <nrequired> <'[\"key\",\"key\"]'> [account]\n"
793 "Add a nrequired-to-sign multisignature address to the wallet\"\n"
794 "each key is a NovaCoin address or hex-encoded public key\n"
795 "If [account] is specified, assign address to [account].";
796 throw runtime_error(msg);
799 int nRequired = params[0].get_int();
800 const Array& keys = params[1].get_array();
802 if (params.size() > 2)
803 strAccount = AccountFromValue(params[2]);
805 // Gather public keys
807 throw runtime_error("a multisignature address must require at least one key to redeem");
808 if ((int)keys.size() < nRequired)
810 strprintf("not enough keys supplied "
811 "(got %" PRIszu " keys, but need at least %d to redeem)", keys.size(), nRequired));
812 std::vector<CKey> pubkeys;
813 pubkeys.resize(keys.size());
814 for (unsigned int i = 0; i < keys.size(); i++)
816 const std::string& ks = keys[i].get_str();
818 // Case 1: Bitcoin address and we have full public key:
819 CBitcoinAddress address(ks);
820 if (address.IsValid())
823 if (!address.GetKeyID(keyID))
825 strprintf("%s does not refer to a key",ks.c_str()));
827 if (!pwalletMain->GetPubKey(keyID, vchPubKey))
829 strprintf("no full public key for address %s",ks.c_str()));
830 if (!vchPubKey.IsValid() || !pubkeys[i].SetPubKey(vchPubKey))
831 throw runtime_error(" Invalid public key: "+ks);
834 // Case 2: hex public key
837 CPubKey vchPubKey(ParseHex(ks));
838 if (!vchPubKey.IsValid() || !pubkeys[i].SetPubKey(vchPubKey))
839 throw runtime_error(" Invalid public key: "+ks);
843 throw runtime_error(" Invalid public key: "+ks);
847 // Construct using pay-to-script-hash:
849 inner.SetMultisig(nRequired, pubkeys);
851 if (inner.size() > MAX_SCRIPT_ELEMENT_SIZE)
853 strprintf("redeemScript exceeds size limit: %" PRIszu " > %d", inner.size(), MAX_SCRIPT_ELEMENT_SIZE));
855 CScriptID innerID = inner.GetID();
856 pwalletMain->AddCScript(inner);
858 pwalletMain->SetAddressBookName(innerID, strAccount);
859 return CBitcoinAddress(innerID).ToString();
862 Value addredeemscript(const Array& params, bool fHelp)
864 if (fHelp || params.size() < 1 || params.size() > 2)
866 string msg = "addredeemscript <redeemScript> [account]\n"
867 "Add a P2SH address with a specified redeemScript to the wallet.\n"
868 "If [account] is specified, assign address to [account].";
869 throw runtime_error(msg);
873 if (params.size() > 1)
874 strAccount = AccountFromValue(params[1]);
876 // Construct using pay-to-script-hash:
877 vector<unsigned char> innerData = ParseHexV(params[0], "redeemScript");
878 CScript inner(innerData.begin(), innerData.end());
879 CScriptID innerID = inner.GetID();
880 pwalletMain->AddCScript(inner);
882 pwalletMain->SetAddressBookName(innerID, strAccount);
883 return CBitcoinAddress(innerID).ToString();
893 nConf = std::numeric_limits<int>::max();
897 Value ListReceived(const Array& params, bool fByAccounts)
899 // Minimum confirmations
901 if (params.size() > 0)
902 nMinDepth = params[0].get_int();
904 // Whether to include empty accounts
905 bool fIncludeEmpty = false;
906 if (params.size() > 1)
907 fIncludeEmpty = params[1].get_bool();
910 map<CBitcoinAddress, tallyitem> mapTally;
911 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
913 const CWalletTx& wtx = (*it).second;
915 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
918 int nDepth = wtx.GetDepthInMainChain();
919 if (nDepth < nMinDepth)
922 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
924 CTxDestination address;
925 if (!ExtractDestination(txout.scriptPubKey, address) || !IsMine(*pwalletMain, address))
928 tallyitem& item = mapTally[address];
929 item.nAmount += txout.nValue;
930 item.nConf = min(item.nConf, nDepth);
936 map<string, tallyitem> mapAccountTally;
937 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
939 const CBitcoinAddress& address = item.first;
940 const string& strAccount = item.second;
941 map<CBitcoinAddress, tallyitem>::iterator it = mapTally.find(address);
942 if (it == mapTally.end() && !fIncludeEmpty)
946 int nConf = std::numeric_limits<int>::max();
947 if (it != mapTally.end())
949 nAmount = (*it).second.nAmount;
950 nConf = (*it).second.nConf;
955 tallyitem& item = mapAccountTally[strAccount];
956 item.nAmount += nAmount;
957 item.nConf = min(item.nConf, nConf);
962 obj.push_back(Pair("address", address.ToString()));
963 obj.push_back(Pair("account", strAccount));
964 obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
965 obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf)));
972 for (map<string, tallyitem>::iterator it = mapAccountTally.begin(); it != mapAccountTally.end(); ++it)
974 int64_t nAmount = (*it).second.nAmount;
975 int nConf = (*it).second.nConf;
977 obj.push_back(Pair("account", (*it).first));
978 obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
979 obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf)));
987 Value listreceivedbyaddress(const Array& params, bool fHelp)
989 if (fHelp || params.size() > 2)
991 "listreceivedbyaddress [minconf=1] [includeempty=false]\n"
992 "[minconf] is the minimum number of confirmations before payments are included.\n"
993 "[includeempty] whether to include addresses that haven't received any payments.\n"
994 "Returns an array of objects containing:\n"
995 " \"address\" : receiving address\n"
996 " \"account\" : the account of the receiving address\n"
997 " \"amount\" : total amount received by the address\n"
998 " \"confirmations\" : number of confirmations of the most recent transaction included");
1000 return ListReceived(params, false);
1003 Value listreceivedbyaccount(const Array& params, bool fHelp)
1005 if (fHelp || params.size() > 2)
1006 throw runtime_error(
1007 "listreceivedbyaccount [minconf=1] [includeempty=false]\n"
1008 "[minconf] is the minimum number of confirmations before payments are included.\n"
1009 "[includeempty] whether to include accounts that haven't received any payments.\n"
1010 "Returns an array of objects containing:\n"
1011 " \"account\" : the account of the receiving addresses\n"
1012 " \"amount\" : total amount received by addresses with this account\n"
1013 " \"confirmations\" : number of confirmations of the most recent transaction included");
1015 return ListReceived(params, true);
1018 static void MaybePushAddress(Object & entry, const CTxDestination &dest)
1020 CBitcoinAddress addr;
1022 entry.push_back(Pair("address", addr.ToString()));
1025 void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret, const isminefilter& filter)
1027 int64_t nGeneratedImmature, nGeneratedMature, nFee;
1028 string strSentAccount;
1029 list<pair<CTxDestination, int64_t> > listReceived;
1030 list<pair<CTxDestination, int64_t> > listSent;
1032 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount, filter);
1034 bool fAllAccounts = (strAccount == string("*"));
1035 bool involvesWatchonly = wtx.IsFromMe(MINE_WATCH_ONLY);
1037 // Generated blocks assigned to account ""
1038 if ((nGeneratedMature+nGeneratedImmature) != 0 && (fAllAccounts || strAccount == ""))
1041 entry.push_back(Pair("account", string("")));
1042 if (nGeneratedImmature)
1044 entry.push_back(Pair("category", wtx.GetDepthInMainChain() ? "immature" : "orphan"));
1045 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedImmature)));
1049 entry.push_back(Pair("category", "generate"));
1050 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedMature)));
1053 WalletTxToJSON(wtx, entry);
1054 ret.push_back(entry);
1058 if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount))
1060 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& s, listSent)
1063 entry.push_back(Pair("account", strSentAccount));
1064 if(involvesWatchonly || (::IsMine(*pwalletMain, s.first) & MINE_WATCH_ONLY))
1065 entry.push_back(Pair("involvesWatchonly", true));
1066 MaybePushAddress(entry, s.first);
1068 if (wtx.GetDepthInMainChain() < 0) {
1069 entry.push_back(Pair("category", "conflicted"));
1071 entry.push_back(Pair("category", "send"));
1074 entry.push_back(Pair("amount", ValueFromAmount(-s.second)));
1075 entry.push_back(Pair("fee", ValueFromAmount(-nFee)));
1077 WalletTxToJSON(wtx, entry);
1078 ret.push_back(entry);
1083 if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth)
1085 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& r, listReceived)
1088 if (pwalletMain->mapAddressBook.count(r.first))
1089 account = pwalletMain->mapAddressBook[r.first];
1090 if (fAllAccounts || (account == strAccount))
1093 entry.push_back(Pair("account", account));
1094 if(involvesWatchonly || (::IsMine(*pwalletMain, r.first) & MINE_WATCH_ONLY))
1095 entry.push_back(Pair("involvesWatchonly", true));
1096 MaybePushAddress(entry, r.first);
1097 if (wtx.IsCoinBase())
1099 if (wtx.GetDepthInMainChain() < 1)
1100 entry.push_back(Pair("category", "orphan"));
1101 else if (wtx.GetBlocksToMaturity() > 0)
1102 entry.push_back(Pair("category", "immature"));
1104 entry.push_back(Pair("category", "generate"));
1107 entry.push_back(Pair("category", "receive"));
1108 entry.push_back(Pair("amount", ValueFromAmount(r.second)));
1110 WalletTxToJSON(wtx, entry);
1111 ret.push_back(entry);
1117 void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Array& ret)
1119 bool fAllAccounts = (strAccount == string("*"));
1121 if (fAllAccounts || acentry.strAccount == strAccount)
1124 entry.push_back(Pair("account", acentry.strAccount));
1125 entry.push_back(Pair("category", "move"));
1126 entry.push_back(Pair("time", (boost::int64_t)acentry.nTime));
1127 entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit)));
1128 entry.push_back(Pair("otheraccount", acentry.strOtherAccount));
1129 entry.push_back(Pair("comment", acentry.strComment));
1130 ret.push_back(entry);
1134 Value listtransactions(const Array& params, bool fHelp)
1136 if (fHelp || params.size() > 3)
1137 throw runtime_error(
1138 "listtransactions [account] [count=10] [from=0]\n"
1139 "Returns up to [count] most recent transactions skipping the first [from] transactions for account [account].");
1141 string strAccount = "*";
1142 if (params.size() > 0)
1143 strAccount = params[0].get_str();
1145 if (params.size() > 1)
1146 nCount = params[1].get_int();
1148 if (params.size() > 2)
1149 nFrom = params[2].get_int();
1151 isminefilter filter = MINE_SPENDABLE;
1152 if(params.size() > 3)
1153 if(params[3].get_bool())
1154 filter = filter | MINE_WATCH_ONLY;
1157 throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count");
1159 throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative from");
1163 std::list<CAccountingEntry> acentries;
1164 CWallet::TxItems txOrdered = pwalletMain->OrderedTxItems(acentries, strAccount);
1166 // iterate backwards until we have nCount items to return:
1167 for (CWallet::TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
1169 CWalletTx *const pwtx = (*it).second.first;
1171 ListTransactions(*pwtx, strAccount, 0, true, ret, filter);
1172 CAccountingEntry *const pacentry = (*it).second.second;
1174 AcentryToJSON(*pacentry, strAccount, ret);
1176 if ((int)ret.size() >= (nCount+nFrom)) break;
1178 // ret is newest to oldest
1180 if (nFrom > (int)ret.size())
1182 if ((nFrom + nCount) > (int)ret.size())
1183 nCount = ret.size() - nFrom;
1184 Array::iterator first = ret.begin();
1185 std::advance(first, nFrom);
1186 Array::iterator last = ret.begin();
1187 std::advance(last, nFrom+nCount);
1189 if (last != ret.end()) ret.erase(last, ret.end());
1190 if (first != ret.begin()) ret.erase(ret.begin(), first);
1192 std::reverse(ret.begin(), ret.end()); // Return oldest to newest
1197 Value listaccounts(const Array& params, bool fHelp)
1199 if (fHelp || params.size() > 1)
1200 throw runtime_error(
1201 "listaccounts [minconf=1]\n"
1202 "Returns Object that has account names as keys, account balances as values.");
1205 if (params.size() > 0)
1206 nMinDepth = params[0].get_int();
1208 isminefilter includeWatchonly = MINE_SPENDABLE;
1209 if(params.size() > 1)
1210 if(params[1].get_bool())
1211 includeWatchonly = includeWatchonly | MINE_WATCH_ONLY;
1214 map<string, int64_t> mapAccountBalances;
1215 BOOST_FOREACH(const PAIRTYPE(CTxDestination, string)& entry, pwalletMain->mapAddressBook) {
1216 if (IsMine(*pwalletMain, entry.first)) // This address belongs to me
1217 mapAccountBalances[entry.second] = 0;
1220 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
1222 const CWalletTx& wtx = (*it).second;
1223 int64_t nGeneratedImmature, nGeneratedMature, nFee;
1224 string strSentAccount;
1225 list<pair<CTxDestination, int64_t> > listReceived;
1226 list<pair<CTxDestination, int64_t> > listSent;
1227 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount, includeWatchonly);
1228 mapAccountBalances[strSentAccount] -= nFee;
1229 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& s, listSent)
1230 mapAccountBalances[strSentAccount] -= s.second;
1231 if (wtx.GetDepthInMainChain() >= nMinDepth)
1233 mapAccountBalances[""] += nGeneratedMature;
1234 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& r, listReceived)
1235 if (pwalletMain->mapAddressBook.count(r.first))
1236 mapAccountBalances[pwalletMain->mapAddressBook[r.first]] += r.second;
1238 mapAccountBalances[""] += r.second;
1242 list<CAccountingEntry> acentries;
1243 CWalletDB(pwalletMain->strWalletFile).ListAccountCreditDebit("*", acentries);
1244 BOOST_FOREACH(const CAccountingEntry& entry, acentries)
1245 mapAccountBalances[entry.strAccount] += entry.nCreditDebit;
1248 BOOST_FOREACH(const PAIRTYPE(string, int64_t)& accountBalance, mapAccountBalances) {
1249 ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second)));
1254 Value listsinceblock(const Array& params, bool fHelp)
1257 throw runtime_error(
1258 "listsinceblock [blockhash] [target-confirmations]\n"
1259 "Get all transactions in blocks since block [blockhash], or all transactions if omitted");
1261 CBlockIndex *pindex = NULL;
1262 int target_confirms = 1;
1263 isminefilter filter = MINE_SPENDABLE;
1265 if (params.size() > 0)
1267 uint256 blockId = 0;
1269 blockId.SetHex(params[0].get_str());
1270 pindex = CBlockLocator(blockId).GetBlockIndex();
1273 if (params.size() > 1)
1275 target_confirms = params[1].get_int();
1277 if (target_confirms < 1)
1278 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter");
1281 if(params.size() > 2)
1282 if(params[2].get_bool())
1283 filter = filter | MINE_WATCH_ONLY;
1285 int depth = pindex ? (1 + nBestHeight - pindex->nHeight) : -1;
1289 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); it++)
1291 CWalletTx tx = (*it).second;
1293 if (depth == -1 || tx.GetDepthInMainChain() < depth)
1294 ListTransactions(tx, "*", 0, true, transactions, filter);
1299 if (target_confirms == 1)
1301 lastblock = hashBestChain;
1305 int target_height = pindexBest->nHeight + 1 - target_confirms;
1308 for (block = pindexBest;
1309 block && block->nHeight > target_height;
1310 block = block->pprev) { }
1312 lastblock = block ? block->GetBlockHash() : 0;
1316 ret.push_back(Pair("transactions", transactions));
1317 ret.push_back(Pair("lastblock", lastblock.GetHex()));
1322 Value gettransaction(const Array& params, bool fHelp)
1324 if (fHelp || params.size() != 1)
1325 throw runtime_error(
1326 "gettransaction <txid>\n"
1327 "Get detailed information about <txid>");
1330 hash.SetHex(params[0].get_str());
1332 isminefilter filter = MINE_SPENDABLE;
1333 if(params.size() > 1)
1334 if(params[1].get_bool())
1335 filter = filter | MINE_WATCH_ONLY;
1339 if (pwalletMain->mapWallet.count(hash))
1341 const CWalletTx& wtx = pwalletMain->mapWallet[hash];
1343 TxToJSON(wtx, 0, entry);
1345 int64_t nCredit = wtx.GetCredit(filter);
1346 int64_t nDebit = wtx.GetDebit(filter);
1347 int64_t nNet = nCredit - nDebit;
1348 int64_t nFee = (wtx.IsFromMe(filter) ? wtx.GetValueOut() - nDebit : 0);
1350 entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee)));
1351 if (wtx.IsFromMe(filter))
1352 entry.push_back(Pair("fee", ValueFromAmount(nFee)));
1354 WalletTxToJSON(wtx, entry);
1357 ListTransactions(pwalletMain->mapWallet[hash], "*", 0, false, details, filter);
1358 entry.push_back(Pair("details", details));
1363 uint256 hashBlock = 0;
1364 if (GetTransaction(hash, tx, hashBlock))
1366 TxToJSON(tx, 0, entry);
1368 entry.push_back(Pair("confirmations", 0));
1371 entry.push_back(Pair("blockhash", hashBlock.GetHex()));
1372 map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashBlock);
1373 if (mi != mapBlockIndex.end() && (*mi).second)
1375 CBlockIndex* pindex = (*mi).second;
1376 if (pindex->IsInMainChain())
1377 entry.push_back(Pair("confirmations", 1 + nBestHeight - pindex->nHeight));
1379 entry.push_back(Pair("confirmations", 0));
1384 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction");
1391 Value backupwallet(const Array& params, bool fHelp)
1393 if (fHelp || params.size() != 1)
1394 throw runtime_error(
1395 "backupwallet <destination>\n"
1396 "Safely copies wallet.dat to destination, which can be a directory or a path with filename.");
1398 string strDest = params[0].get_str();
1399 if (!BackupWallet(*pwalletMain, strDest))
1400 throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet backup failed!");
1406 Value keypoolrefill(const Array& params, bool fHelp)
1408 if (fHelp || params.size() > 1)
1409 throw runtime_error(
1410 "keypoolrefill [new-size]\n"
1411 "Fills the keypool."
1412 + HelpRequiringPassphrase());
1414 unsigned int nSize = max<unsigned int>(GetArg("-keypool", 100), 0);
1415 if (params.size() > 0) {
1416 if (params[0].get_int() < 0)
1417 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected valid size");
1418 nSize = (unsigned int) params[0].get_int();
1421 EnsureWalletIsUnlocked();
1423 pwalletMain->TopUpKeyPool(nSize);
1425 if (pwalletMain->GetKeyPoolSize() < nSize)
1426 throw JSONRPCError(RPC_WALLET_ERROR, "Error refreshing keypool.");
1432 void ThreadTopUpKeyPool(void* parg)
1434 // Make this thread recognisable as the key-topping-up thread
1435 RenameThread("novacoin-key-top");
1437 pwalletMain->TopUpKeyPool();
1440 void ThreadCleanWalletPassphrase(void* parg)
1442 // Make this thread recognisable as the wallet relocking thread
1443 RenameThread("novacoin-lock-wa");
1445 int64_t nMyWakeTime = GetTimeMillis() + *((int64_t*)parg) * 1000;
1447 ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime);
1449 if (nWalletUnlockTime == 0)
1451 nWalletUnlockTime = nMyWakeTime;
1455 if (nWalletUnlockTime==0)
1457 int64_t nToSleep = nWalletUnlockTime - GetTimeMillis();
1461 LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime);
1463 ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime);
1467 if (nWalletUnlockTime)
1469 nWalletUnlockTime = 0;
1470 pwalletMain->Lock();
1475 if (nWalletUnlockTime < nMyWakeTime)
1476 nWalletUnlockTime = nMyWakeTime;
1479 LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime);
1481 delete (int64_t*)parg;
1484 Value walletpassphrase(const Array& params, bool fHelp)
1486 if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 3))
1487 throw runtime_error(
1488 "walletpassphrase <passphrase> <timeout> [mintonly]\n"
1489 "Stores the wallet decryption key in memory for <timeout> seconds.\n"
1490 "mintonly is optional true/false allowing only block minting.");
1493 if (!pwalletMain->IsCrypted())
1494 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called.");
1496 if (!pwalletMain->IsLocked())
1497 throw JSONRPCError(RPC_WALLET_ALREADY_UNLOCKED, "Error: Wallet is already unlocked, use walletlock first if need to change unlock settings.");
1498 // Note that the walletpassphrase is stored in params[0] which is not mlock()ed
1499 SecureString strWalletPass;
1500 strWalletPass.reserve(100);
1501 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1502 // Alternately, find a way to make params[0] mlock()'d to begin with.
1503 strWalletPass = params[0].get_str().c_str();
1505 if (strWalletPass.length() > 0)
1507 if (!pwalletMain->Unlock(strWalletPass))
1508 throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
1511 throw runtime_error(
1512 "walletpassphrase <passphrase> <timeout>\n"
1513 "Stores the wallet decryption key in memory for <timeout> seconds.");
1515 NewThread(ThreadTopUpKeyPool, NULL);
1516 int64_t* pnSleepTime = new int64_t(params[1].get_int64());
1517 NewThread(ThreadCleanWalletPassphrase, pnSleepTime);
1519 // ppcoin: if user OS account compromised prevent trivial sendmoney commands
1520 if (params.size() > 2)
1521 fWalletUnlockMintOnly = params[2].get_bool();
1523 fWalletUnlockMintOnly = false;
1529 Value walletpassphrasechange(const Array& params, bool fHelp)
1531 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2))
1532 throw runtime_error(
1533 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1534 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1537 if (!pwalletMain->IsCrypted())
1538 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrasechange was called.");
1540 // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string)
1541 // Alternately, find a way to make params[0] mlock()'d to begin with.
1542 SecureString strOldWalletPass;
1543 strOldWalletPass.reserve(100);
1544 strOldWalletPass = params[0].get_str().c_str();
1546 SecureString strNewWalletPass;
1547 strNewWalletPass.reserve(100);
1548 strNewWalletPass = params[1].get_str().c_str();
1550 if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1)
1551 throw runtime_error(
1552 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1553 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1555 if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass))
1556 throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
1562 Value walletlock(const Array& params, bool fHelp)
1564 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 0))
1565 throw runtime_error(
1567 "Removes the wallet encryption key from memory, locking the wallet.\n"
1568 "After calling this method, you will need to call walletpassphrase again\n"
1569 "before being able to call any methods which require the wallet to be unlocked.");
1572 if (!pwalletMain->IsCrypted())
1573 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletlock was called.");
1576 LOCK(cs_nWalletUnlockTime);
1577 pwalletMain->Lock();
1578 nWalletUnlockTime = 0;
1585 Value encryptwallet(const Array& params, bool fHelp)
1587 if (!pwalletMain->IsCrypted() && (fHelp || params.size() != 1))
1588 throw runtime_error(
1589 "encryptwallet <passphrase>\n"
1590 "Encrypts the wallet with <passphrase>.");
1593 if (pwalletMain->IsCrypted())
1594 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an encrypted wallet, but encryptwallet was called.");
1596 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1597 // Alternately, find a way to make params[0] mlock()'d to begin with.
1598 SecureString strWalletPass;
1599 strWalletPass.reserve(100);
1600 strWalletPass = params[0].get_str().c_str();
1602 if (strWalletPass.length() < 1)
1603 throw runtime_error(
1604 "encryptwallet <passphrase>\n"
1605 "Encrypts the wallet with <passphrase>.");
1607 if (!pwalletMain->EncryptWallet(strWalletPass))
1608 throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: Failed to encrypt the wallet.");
1610 // BDB seems to have a bad habit of writing old data into
1611 // slack space in .dat files; that is bad if the old data is
1612 // unencrypted private keys. So:
1614 return "wallet encrypted; NovaCoin server stopping, restart to run with encrypted wallet. The keypool has been flushed, you need to make a new backup.";
1617 class DescribeAddressVisitor : public boost::static_visitor<Object>
1622 DescribeAddressVisitor(isminetype mineIn) : mine(mineIn) {}
1624 Object operator()(const CNoDestination &dest) const { return Object(); }
1625 Object operator()(const CKeyID &keyID) const {
1628 pwalletMain->GetPubKey(keyID, vchPubKey);
1629 obj.push_back(Pair("isscript", false));
1630 if (mine == MINE_SPENDABLE) {
1631 pwalletMain->GetPubKey(keyID, vchPubKey);
1632 obj.push_back(Pair("pubkey", HexStr(vchPubKey.Raw())));
1633 obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed()));
1638 Object operator()(const CScriptID &scriptID) const {
1640 obj.push_back(Pair("isscript", true));
1641 if (mine == MINE_SPENDABLE) {
1643 pwalletMain->GetCScript(scriptID, subscript);
1644 std::vector<CTxDestination> addresses;
1645 txnouttype whichType;
1647 ExtractDestinations(subscript, whichType, addresses, nRequired);
1648 obj.push_back(Pair("script", GetTxnOutputType(whichType)));
1649 obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end())));
1651 BOOST_FOREACH(const CTxDestination& addr, addresses)
1652 a.push_back(CBitcoinAddress(addr).ToString());
1653 obj.push_back(Pair("addresses", a));
1654 if (whichType == TX_MULTISIG)
1655 obj.push_back(Pair("sigsrequired", nRequired));
1661 Value validateaddress(const Array& params, bool fHelp)
1663 if (fHelp || params.size() != 1)
1664 throw runtime_error(
1665 "validateaddress <novacoinaddress>\n"
1666 "Return information about <novacoinaddress>.");
1668 CBitcoinAddress address(params[0].get_str());
1669 bool isValid = address.IsValid();
1672 ret.push_back(Pair("isvalid", isValid));
1675 CTxDestination dest = address.Get();
1676 string currentAddress = address.ToString();
1677 ret.push_back(Pair("address", currentAddress));
1678 isminetype mine = pwalletMain ? IsMine(*pwalletMain, dest) : MINE_NO;
1679 ret.push_back(Pair("ismine", mine != MINE_NO));
1680 if (mine != MINE_NO) {
1681 ret.push_back(Pair("watchonly", mine == MINE_WATCH_ONLY));
1682 Object detail = boost::apply_visitor(DescribeAddressVisitor(mine), dest);
1683 ret.insert(ret.end(), detail.begin(), detail.end());
1685 if (pwalletMain->mapAddressBook.count(dest))
1686 ret.push_back(Pair("account", pwalletMain->mapAddressBook[dest]));
1691 // ppcoin: reserve balance from being staked for network protection
1692 Value reservebalance(const Array& params, bool fHelp)
1694 if (fHelp || params.size() > 2)
1695 throw runtime_error(
1696 "reservebalance [<reserve> [amount]]\n"
1697 "<reserve> is true or false to turn balance reserve on or off.\n"
1698 "<amount> is a real and rounded to cent.\n"
1699 "Set reserve amount not participating in network protection.\n"
1700 "If no parameters provided current setting is printed.\n");
1702 if (params.size() > 0)
1704 bool fReserve = params[0].get_bool();
1707 if (params.size() == 1)
1708 throw runtime_error("must provide amount to reserve balance.\n");
1709 int64_t nAmount = AmountFromValue(params[1]);
1710 nAmount = (nAmount / CENT) * CENT; // round to cent
1712 throw runtime_error("amount cannot be negative.\n");
1713 mapArgs["-reservebalance"] = FormatMoney(nAmount).c_str();
1717 if (params.size() > 1)
1718 throw runtime_error("cannot specify amount to turn off reserve.\n");
1719 mapArgs["-reservebalance"] = "0";
1724 int64_t nReserveBalance = 0;
1725 if (mapArgs.count("-reservebalance") && !ParseMoney(mapArgs["-reservebalance"], nReserveBalance))
1726 throw runtime_error("invalid reserve balance amount\n");
1727 result.push_back(Pair("reserve", (nReserveBalance > 0)));
1728 result.push_back(Pair("amount", ValueFromAmount(nReserveBalance)));
1733 // ppcoin: check wallet integrity
1734 Value checkwallet(const Array& params, bool fHelp)
1736 if (fHelp || params.size() > 0)
1737 throw runtime_error(
1739 "Check wallet for integrity.\n");
1742 int64_t nBalanceInQuestion;
1743 pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion, true);
1745 if (nMismatchSpent == 0)
1746 result.push_back(Pair("wallet check passed", true));
1749 result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1750 result.push_back(Pair("amount in question", ValueFromAmount(nBalanceInQuestion)));
1756 // ppcoin: repair wallet
1757 Value repairwallet(const Array& params, bool fHelp)
1759 if (fHelp || params.size() > 0)
1760 throw runtime_error(
1762 "Repair wallet if checkwallet reports any problem.\n");
1765 int64_t nBalanceInQuestion;
1766 pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion);
1768 if (nMismatchSpent == 0)
1769 result.push_back(Pair("wallet check passed", true));
1772 result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1773 result.push_back(Pair("amount affected by repair", ValueFromAmount(nBalanceInQuestion)));
1778 // NovaCoin: resend unconfirmed wallet transactions
1779 Value resendtx(const Array& params, bool fHelp)
1781 if (fHelp || params.size() > 1)
1782 throw runtime_error(
1784 "Re-send unconfirmed transactions.\n"
1787 ResendWalletTransactions();
1792 // ppcoin: make a public-private key pair
1793 Value makekeypair(const Array& params, bool fHelp)
1795 if (fHelp || params.size() > 1)
1796 throw runtime_error(
1797 "makekeypair [prefix]\n"
1798 "Make a public/private key pair.\n"
1799 "[prefix] is optional preferred prefix for the public key.\n");
1801 string strPrefix = "";
1802 if (params.size() > 0)
1803 strPrefix = params[0].get_str();
1806 key.MakeNewKey(false);
1808 CPrivKey vchPrivKey = key.GetPrivKey();
1810 result.push_back(Pair("PrivateKey", HexStr<CPrivKey::iterator>(vchPrivKey.begin(), vchPrivKey.end())));
1811 result.push_back(Pair("PublicKey", HexStr(key.GetPubKey().Raw())));