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 ? "\n\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 if (keys.size() > 16)
813 throw runtime_error("Number of addresses involved in the multisignature address creation > 16\nReduce the number");
814 std::vector<CKey> pubkeys;
815 pubkeys.resize(keys.size());
816 for (unsigned int i = 0; i < keys.size(); i++)
818 const std::string& ks = keys[i].get_str();
820 // Case 1: Bitcoin address and we have full public key:
821 CBitcoinAddress address(ks);
822 if (address.IsValid())
825 if (!address.GetKeyID(keyID))
827 strprintf("%s does not refer to a key",ks.c_str()));
829 if (!pwalletMain->GetPubKey(keyID, vchPubKey))
831 strprintf("no full public key for address %s",ks.c_str()));
832 if (!vchPubKey.IsValid() || !pubkeys[i].SetPubKey(vchPubKey))
833 throw runtime_error(" Invalid public key: "+ks);
836 // Case 2: hex public key
839 CPubKey vchPubKey(ParseHex(ks));
840 if (!vchPubKey.IsValid() || !pubkeys[i].SetPubKey(vchPubKey))
841 throw runtime_error(" Invalid public key: "+ks);
845 throw runtime_error(" Invalid public key: "+ks);
849 // Construct using pay-to-script-hash:
851 inner.SetMultisig(nRequired, pubkeys);
853 if (inner.size() > MAX_SCRIPT_ELEMENT_SIZE)
855 strprintf("redeemScript exceeds size limit: %" PRIszu " > %d", inner.size(), MAX_SCRIPT_ELEMENT_SIZE));
857 CScriptID innerID = inner.GetID();
858 pwalletMain->AddCScript(inner);
860 pwalletMain->SetAddressBookName(innerID, strAccount);
861 return CBitcoinAddress(innerID).ToString();
864 Value addredeemscript(const Array& params, bool fHelp)
866 if (fHelp || params.size() < 1 || params.size() > 2)
868 string msg = "addredeemscript <redeemScript> [account]\n"
869 "Add a P2SH address with a specified redeemScript to the wallet.\n"
870 "If [account] is specified, assign address to [account].";
871 throw runtime_error(msg);
875 if (params.size() > 1)
876 strAccount = AccountFromValue(params[1]);
878 // Construct using pay-to-script-hash:
879 vector<unsigned char> innerData = ParseHexV(params[0], "redeemScript");
880 CScript inner(innerData.begin(), innerData.end());
881 CScriptID innerID = inner.GetID();
882 pwalletMain->AddCScript(inner);
884 pwalletMain->SetAddressBookName(innerID, strAccount);
885 return CBitcoinAddress(innerID).ToString();
895 nConf = std::numeric_limits<int>::max();
899 Value ListReceived(const Array& params, bool fByAccounts)
901 // Minimum confirmations
903 if (params.size() > 0)
904 nMinDepth = params[0].get_int();
906 // Whether to include empty accounts
907 bool fIncludeEmpty = false;
908 if (params.size() > 1)
909 fIncludeEmpty = params[1].get_bool();
912 map<CBitcoinAddress, tallyitem> mapTally;
913 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
915 const CWalletTx& wtx = (*it).second;
917 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
920 int nDepth = wtx.GetDepthInMainChain();
921 if (nDepth < nMinDepth)
924 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
926 CTxDestination address;
927 if (!ExtractDestination(txout.scriptPubKey, address) || !IsMine(*pwalletMain, address))
930 tallyitem& item = mapTally[address];
931 item.nAmount += txout.nValue;
932 item.nConf = min(item.nConf, nDepth);
938 map<string, tallyitem> mapAccountTally;
939 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
941 const CBitcoinAddress& address = item.first;
942 const string& strAccount = item.second;
943 map<CBitcoinAddress, tallyitem>::iterator it = mapTally.find(address);
944 if (it == mapTally.end() && !fIncludeEmpty)
948 int nConf = std::numeric_limits<int>::max();
949 if (it != mapTally.end())
951 nAmount = (*it).second.nAmount;
952 nConf = (*it).second.nConf;
957 tallyitem& item = mapAccountTally[strAccount];
958 item.nAmount += nAmount;
959 item.nConf = min(item.nConf, nConf);
964 obj.push_back(Pair("address", address.ToString()));
965 obj.push_back(Pair("account", strAccount));
966 obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
967 obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf)));
974 for (map<string, tallyitem>::iterator it = mapAccountTally.begin(); it != mapAccountTally.end(); ++it)
976 int64_t nAmount = (*it).second.nAmount;
977 int nConf = (*it).second.nConf;
979 obj.push_back(Pair("account", (*it).first));
980 obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
981 obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf)));
989 Value listreceivedbyaddress(const Array& params, bool fHelp)
991 if (fHelp || params.size() > 2)
993 "listreceivedbyaddress [minconf=1] [includeempty=false]\n"
994 "[minconf] is the minimum number of confirmations before payments are included.\n"
995 "[includeempty] whether to include addresses that haven't received any payments.\n"
996 "Returns an array of objects containing:\n"
997 " \"address\" : receiving address\n"
998 " \"account\" : the account of the receiving address\n"
999 " \"amount\" : total amount received by the address\n"
1000 " \"confirmations\" : number of confirmations of the most recent transaction included");
1002 return ListReceived(params, false);
1005 Value listreceivedbyaccount(const Array& params, bool fHelp)
1007 if (fHelp || params.size() > 2)
1008 throw runtime_error(
1009 "listreceivedbyaccount [minconf=1] [includeempty=false]\n"
1010 "[minconf] is the minimum number of confirmations before payments are included.\n"
1011 "[includeempty] whether to include accounts that haven't received any payments.\n"
1012 "Returns an array of objects containing:\n"
1013 " \"account\" : the account of the receiving addresses\n"
1014 " \"amount\" : total amount received by addresses with this account\n"
1015 " \"confirmations\" : number of confirmations of the most recent transaction included");
1017 return ListReceived(params, true);
1020 static void MaybePushAddress(Object & entry, const CTxDestination &dest)
1022 CBitcoinAddress addr;
1024 entry.push_back(Pair("address", addr.ToString()));
1027 void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret, const isminefilter& filter)
1029 int64_t nGeneratedImmature, nGeneratedMature, nFee;
1030 string strSentAccount;
1031 list<pair<CTxDestination, int64_t> > listReceived;
1032 list<pair<CTxDestination, int64_t> > listSent;
1034 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount, filter);
1036 bool fAllAccounts = (strAccount == string("*"));
1037 bool involvesWatchonly = wtx.IsFromMe(MINE_WATCH_ONLY);
1039 // Generated blocks assigned to account ""
1040 if ((nGeneratedMature+nGeneratedImmature) != 0 && (fAllAccounts || strAccount == ""))
1043 entry.push_back(Pair("account", string("")));
1044 if (nGeneratedImmature)
1046 entry.push_back(Pair("category", wtx.GetDepthInMainChain() ? "immature" : "orphan"));
1047 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedImmature)));
1051 entry.push_back(Pair("category", "generate"));
1052 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedMature)));
1055 WalletTxToJSON(wtx, entry);
1056 ret.push_back(entry);
1060 if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount))
1062 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& s, listSent)
1065 entry.push_back(Pair("account", strSentAccount));
1066 if(involvesWatchonly || (::IsMine(*pwalletMain, s.first) & MINE_WATCH_ONLY))
1067 entry.push_back(Pair("involvesWatchonly", true));
1068 MaybePushAddress(entry, s.first);
1070 if (wtx.GetDepthInMainChain() < 0) {
1071 entry.push_back(Pair("category", "conflicted"));
1073 entry.push_back(Pair("category", "send"));
1076 entry.push_back(Pair("amount", ValueFromAmount(-s.second)));
1077 entry.push_back(Pair("fee", ValueFromAmount(-nFee)));
1079 WalletTxToJSON(wtx, entry);
1080 ret.push_back(entry);
1085 if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth)
1087 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& r, listReceived)
1090 if (pwalletMain->mapAddressBook.count(r.first))
1091 account = pwalletMain->mapAddressBook[r.first];
1092 if (fAllAccounts || (account == strAccount))
1095 entry.push_back(Pair("account", account));
1096 if(involvesWatchonly || (::IsMine(*pwalletMain, r.first) & MINE_WATCH_ONLY))
1097 entry.push_back(Pair("involvesWatchonly", true));
1098 MaybePushAddress(entry, r.first);
1099 if (wtx.IsCoinBase())
1101 if (wtx.GetDepthInMainChain() < 1)
1102 entry.push_back(Pair("category", "orphan"));
1103 else if (wtx.GetBlocksToMaturity() > 0)
1104 entry.push_back(Pair("category", "immature"));
1106 entry.push_back(Pair("category", "generate"));
1109 entry.push_back(Pair("category", "receive"));
1110 entry.push_back(Pair("amount", ValueFromAmount(r.second)));
1112 WalletTxToJSON(wtx, entry);
1113 ret.push_back(entry);
1119 void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Array& ret)
1121 bool fAllAccounts = (strAccount == string("*"));
1123 if (fAllAccounts || acentry.strAccount == strAccount)
1126 entry.push_back(Pair("account", acentry.strAccount));
1127 entry.push_back(Pair("category", "move"));
1128 entry.push_back(Pair("time", (boost::int64_t)acentry.nTime));
1129 entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit)));
1130 entry.push_back(Pair("otheraccount", acentry.strOtherAccount));
1131 entry.push_back(Pair("comment", acentry.strComment));
1132 ret.push_back(entry);
1136 Value listtransactions(const Array& params, bool fHelp)
1138 if (fHelp || params.size() > 3)
1139 throw runtime_error(
1140 "listtransactions [account] [count=10] [from=0]\n"
1141 "Returns up to [count] most recent transactions skipping the first [from] transactions for account [account].");
1143 string strAccount = "*";
1144 if (params.size() > 0)
1145 strAccount = params[0].get_str();
1147 if (params.size() > 1)
1148 nCount = params[1].get_int();
1150 if (params.size() > 2)
1151 nFrom = params[2].get_int();
1153 isminefilter filter = MINE_SPENDABLE;
1154 if(params.size() > 3)
1155 if(params[3].get_bool())
1156 filter = filter | MINE_WATCH_ONLY;
1159 throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count");
1161 throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative from");
1165 std::list<CAccountingEntry> acentries;
1166 CWallet::TxItems txOrdered = pwalletMain->OrderedTxItems(acentries, strAccount);
1168 // iterate backwards until we have nCount items to return:
1169 for (CWallet::TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
1171 CWalletTx *const pwtx = (*it).second.first;
1173 ListTransactions(*pwtx, strAccount, 0, true, ret, filter);
1174 CAccountingEntry *const pacentry = (*it).second.second;
1176 AcentryToJSON(*pacentry, strAccount, ret);
1178 if ((int)ret.size() >= (nCount+nFrom)) break;
1180 // ret is newest to oldest
1182 if (nFrom > (int)ret.size())
1184 if ((nFrom + nCount) > (int)ret.size())
1185 nCount = ret.size() - nFrom;
1186 Array::iterator first = ret.begin();
1187 std::advance(first, nFrom);
1188 Array::iterator last = ret.begin();
1189 std::advance(last, nFrom+nCount);
1191 if (last != ret.end()) ret.erase(last, ret.end());
1192 if (first != ret.begin()) ret.erase(ret.begin(), first);
1194 std::reverse(ret.begin(), ret.end()); // Return oldest to newest
1199 Value listaccounts(const Array& params, bool fHelp)
1201 if (fHelp || params.size() > 1)
1202 throw runtime_error(
1203 "listaccounts [minconf=1]\n"
1204 "Returns Object that has account names as keys, account balances as values.");
1207 if (params.size() > 0)
1208 nMinDepth = params[0].get_int();
1210 isminefilter includeWatchonly = MINE_SPENDABLE;
1211 if(params.size() > 1)
1212 if(params[1].get_bool())
1213 includeWatchonly = includeWatchonly | MINE_WATCH_ONLY;
1216 map<string, int64_t> mapAccountBalances;
1217 BOOST_FOREACH(const PAIRTYPE(CTxDestination, string)& entry, pwalletMain->mapAddressBook) {
1218 if (IsMine(*pwalletMain, entry.first)) // This address belongs to me
1219 mapAccountBalances[entry.second] = 0;
1222 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
1224 const CWalletTx& wtx = (*it).second;
1225 int64_t nGeneratedImmature, nGeneratedMature, nFee;
1226 string strSentAccount;
1227 list<pair<CTxDestination, int64_t> > listReceived;
1228 list<pair<CTxDestination, int64_t> > listSent;
1229 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount, includeWatchonly);
1230 mapAccountBalances[strSentAccount] -= nFee;
1231 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& s, listSent)
1232 mapAccountBalances[strSentAccount] -= s.second;
1233 if (wtx.GetDepthInMainChain() >= nMinDepth)
1235 mapAccountBalances[""] += nGeneratedMature;
1236 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& r, listReceived)
1237 if (pwalletMain->mapAddressBook.count(r.first))
1238 mapAccountBalances[pwalletMain->mapAddressBook[r.first]] += r.second;
1240 mapAccountBalances[""] += r.second;
1244 list<CAccountingEntry> acentries;
1245 CWalletDB(pwalletMain->strWalletFile).ListAccountCreditDebit("*", acentries);
1246 BOOST_FOREACH(const CAccountingEntry& entry, acentries)
1247 mapAccountBalances[entry.strAccount] += entry.nCreditDebit;
1250 BOOST_FOREACH(const PAIRTYPE(string, int64_t)& accountBalance, mapAccountBalances) {
1251 ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second)));
1256 Value listsinceblock(const Array& params, bool fHelp)
1259 throw runtime_error(
1260 "listsinceblock [blockhash] [target-confirmations]\n"
1261 "Get all transactions in blocks since block [blockhash], or all transactions if omitted");
1263 CBlockIndex *pindex = NULL;
1264 int target_confirms = 1;
1265 isminefilter filter = MINE_SPENDABLE;
1267 if (params.size() > 0)
1269 uint256 blockId = 0;
1271 blockId.SetHex(params[0].get_str());
1272 pindex = CBlockLocator(blockId).GetBlockIndex();
1275 if (params.size() > 1)
1277 target_confirms = params[1].get_int();
1279 if (target_confirms < 1)
1280 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter");
1283 if(params.size() > 2)
1284 if(params[2].get_bool())
1285 filter = filter | MINE_WATCH_ONLY;
1287 int depth = pindex ? (1 + nBestHeight - pindex->nHeight) : -1;
1291 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); it++)
1293 CWalletTx tx = (*it).second;
1295 if (depth == -1 || tx.GetDepthInMainChain() < depth)
1296 ListTransactions(tx, "*", 0, true, transactions, filter);
1301 if (target_confirms == 1)
1303 lastblock = hashBestChain;
1307 int target_height = pindexBest->nHeight + 1 - target_confirms;
1310 for (block = pindexBest;
1311 block && block->nHeight > target_height;
1312 block = block->pprev) { }
1314 lastblock = block ? block->GetBlockHash() : 0;
1318 ret.push_back(Pair("transactions", transactions));
1319 ret.push_back(Pair("lastblock", lastblock.GetHex()));
1324 Value gettransaction(const Array& params, bool fHelp)
1326 if (fHelp || params.size() != 1)
1327 throw runtime_error(
1328 "gettransaction <txid>\n"
1329 "Get detailed information about <txid>");
1332 hash.SetHex(params[0].get_str());
1334 isminefilter filter = MINE_SPENDABLE;
1335 if(params.size() > 1)
1336 if(params[1].get_bool())
1337 filter = filter | MINE_WATCH_ONLY;
1341 if (pwalletMain->mapWallet.count(hash))
1343 const CWalletTx& wtx = pwalletMain->mapWallet[hash];
1345 TxToJSON(wtx, 0, entry);
1347 int64_t nCredit = wtx.GetCredit(filter);
1348 int64_t nDebit = wtx.GetDebit(filter);
1349 int64_t nNet = nCredit - nDebit;
1350 int64_t nFee = (wtx.IsFromMe(filter) ? wtx.GetValueOut() - nDebit : 0);
1352 entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee)));
1353 if (wtx.IsFromMe(filter))
1354 entry.push_back(Pair("fee", ValueFromAmount(nFee)));
1356 WalletTxToJSON(wtx, entry);
1359 ListTransactions(pwalletMain->mapWallet[hash], "*", 0, false, details, filter);
1360 entry.push_back(Pair("details", details));
1365 uint256 hashBlock = 0;
1366 if (GetTransaction(hash, tx, hashBlock))
1368 TxToJSON(tx, 0, entry);
1370 entry.push_back(Pair("confirmations", 0));
1373 entry.push_back(Pair("blockhash", hashBlock.GetHex()));
1374 map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashBlock);
1375 if (mi != mapBlockIndex.end() && (*mi).second)
1377 CBlockIndex* pindex = (*mi).second;
1378 if (pindex->IsInMainChain())
1379 entry.push_back(Pair("confirmations", 1 + nBestHeight - pindex->nHeight));
1381 entry.push_back(Pair("confirmations", 0));
1386 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction");
1393 Value backupwallet(const Array& params, bool fHelp)
1395 if (fHelp || params.size() != 1)
1396 throw runtime_error(
1397 "backupwallet <destination>\n"
1398 "Safely copies wallet.dat to destination, which can be a directory or a path with filename.");
1400 string strDest = params[0].get_str();
1401 if (!BackupWallet(*pwalletMain, strDest))
1402 throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet backup failed!");
1408 Value keypoolrefill(const Array& params, bool fHelp)
1410 if (fHelp || params.size() > 1)
1411 throw runtime_error(
1412 "keypoolrefill [new-size]\n"
1413 "Fills the keypool.\n"
1414 "IMPORTANT: Any previous backups you have made of your wallet file "
1415 "should be replaced with the newly generated one."
1416 + HelpRequiringPassphrase());
1418 unsigned int nSize = max<unsigned int>(GetArg("-keypool", 100), 0);
1419 if (params.size() > 0) {
1420 if (params[0].get_int() < 0)
1421 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected valid size");
1422 nSize = (unsigned int) params[0].get_int();
1425 EnsureWalletIsUnlocked();
1427 pwalletMain->TopUpKeyPool(nSize);
1429 if (pwalletMain->GetKeyPoolSize() < nSize)
1430 throw JSONRPCError(RPC_WALLET_ERROR, "Error refreshing keypool.");
1435 Value keypoolreset(const Array& params, bool fHelp)
1437 if (fHelp || params.size() > 1)
1438 throw runtime_error(
1439 "keypoolreset [new-size]\n"
1440 "Resets the keypool.\n"
1441 "IMPORTANT: Any previous backups you have made of your wallet file "
1442 "should be replaced with the newly generated one."
1443 + HelpRequiringPassphrase());
1445 unsigned int nSize = max<unsigned int>(GetArg("-keypool", 100), 0);
1446 if (params.size() > 0) {
1447 if (params[0].get_int() < 0)
1448 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected valid size");
1449 nSize = (unsigned int) params[0].get_int();
1452 EnsureWalletIsUnlocked();
1454 pwalletMain->NewKeyPool(nSize);
1456 if (pwalletMain->GetKeyPoolSize() < nSize)
1457 throw JSONRPCError(RPC_WALLET_ERROR, "Error refreshing keypool.");
1463 void ThreadTopUpKeyPool(void* parg)
1465 // Make this thread recognisable as the key-topping-up thread
1466 RenameThread("novacoin-key-top");
1468 pwalletMain->TopUpKeyPool();
1471 void ThreadCleanWalletPassphrase(void* parg)
1473 // Make this thread recognisable as the wallet relocking thread
1474 RenameThread("novacoin-lock-wa");
1476 int64_t nMyWakeTime = GetTimeMillis() + *((int64_t*)parg) * 1000;
1478 ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime);
1480 if (nWalletUnlockTime == 0)
1482 nWalletUnlockTime = nMyWakeTime;
1486 if (nWalletUnlockTime==0)
1488 int64_t nToSleep = nWalletUnlockTime - GetTimeMillis();
1492 LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime);
1494 ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime);
1498 if (nWalletUnlockTime)
1500 nWalletUnlockTime = 0;
1501 pwalletMain->Lock();
1506 if (nWalletUnlockTime < nMyWakeTime)
1507 nWalletUnlockTime = nMyWakeTime;
1510 LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime);
1512 delete (int64_t*)parg;
1515 Value walletpassphrase(const Array& params, bool fHelp)
1517 if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 3))
1518 throw runtime_error(
1519 "walletpassphrase <passphrase> <timeout> [mintonly]\n"
1520 "Stores the wallet decryption key in memory for <timeout> seconds.\n"
1521 "mintonly is optional true/false allowing only block minting.");
1524 if (!pwalletMain->IsCrypted())
1525 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called.");
1527 if (!pwalletMain->IsLocked())
1528 throw JSONRPCError(RPC_WALLET_ALREADY_UNLOCKED, "Error: Wallet is already unlocked, use walletlock first if need to change unlock settings.");
1529 // Note that the walletpassphrase is stored in params[0] which is not mlock()ed
1530 SecureString strWalletPass;
1531 strWalletPass.reserve(100);
1532 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1533 // Alternately, find a way to make params[0] mlock()'d to begin with.
1534 strWalletPass = params[0].get_str().c_str();
1536 if (strWalletPass.length() > 0)
1538 if (!pwalletMain->Unlock(strWalletPass))
1539 throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
1542 throw runtime_error(
1543 "walletpassphrase <passphrase> <timeout>\n"
1544 "Stores the wallet decryption key in memory for <timeout> seconds.");
1546 NewThread(ThreadTopUpKeyPool, NULL);
1547 int64_t* pnSleepTime = new int64_t(params[1].get_int64());
1548 NewThread(ThreadCleanWalletPassphrase, pnSleepTime);
1550 // ppcoin: if user OS account compromised prevent trivial sendmoney commands
1551 if (params.size() > 2)
1552 fWalletUnlockMintOnly = params[2].get_bool();
1554 fWalletUnlockMintOnly = false;
1560 Value walletpassphrasechange(const Array& params, bool fHelp)
1562 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2))
1563 throw runtime_error(
1564 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1565 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1568 if (!pwalletMain->IsCrypted())
1569 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrasechange was called.");
1571 // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string)
1572 // Alternately, find a way to make params[0] mlock()'d to begin with.
1573 SecureString strOldWalletPass;
1574 strOldWalletPass.reserve(100);
1575 strOldWalletPass = params[0].get_str().c_str();
1577 SecureString strNewWalletPass;
1578 strNewWalletPass.reserve(100);
1579 strNewWalletPass = params[1].get_str().c_str();
1581 if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1)
1582 throw runtime_error(
1583 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1584 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1586 if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass))
1587 throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
1593 Value walletlock(const Array& params, bool fHelp)
1595 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 0))
1596 throw runtime_error(
1598 "Removes the wallet encryption key from memory, locking the wallet.\n"
1599 "After calling this method, you will need to call walletpassphrase again\n"
1600 "before being able to call any methods which require the wallet to be unlocked.");
1603 if (!pwalletMain->IsCrypted())
1604 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletlock was called.");
1607 LOCK(cs_nWalletUnlockTime);
1608 pwalletMain->Lock();
1609 nWalletUnlockTime = 0;
1616 Value encryptwallet(const Array& params, bool fHelp)
1618 if (!pwalletMain->IsCrypted() && (fHelp || params.size() != 1))
1619 throw runtime_error(
1620 "encryptwallet <passphrase>\n"
1621 "Encrypts the wallet with <passphrase>.");
1624 if (pwalletMain->IsCrypted())
1625 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an encrypted wallet, but encryptwallet was called.");
1627 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1628 // Alternately, find a way to make params[0] mlock()'d to begin with.
1629 SecureString strWalletPass;
1630 strWalletPass.reserve(100);
1631 strWalletPass = params[0].get_str().c_str();
1633 if (strWalletPass.length() < 1)
1634 throw runtime_error(
1635 "encryptwallet <passphrase>\n"
1636 "Encrypts the wallet with <passphrase>.");
1638 if (!pwalletMain->EncryptWallet(strWalletPass))
1639 throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: Failed to encrypt the wallet.");
1641 // BDB seems to have a bad habit of writing old data into
1642 // slack space in .dat files; that is bad if the old data is
1643 // unencrypted private keys. So:
1645 return "wallet encrypted; NovaCoin server stopping, restart to run with encrypted wallet. The keypool has been flushed, you need to make a new backup.";
1648 class DescribeAddressVisitor : public boost::static_visitor<Object>
1653 DescribeAddressVisitor(isminetype mineIn) : mine(mineIn) {}
1655 Object operator()(const CNoDestination &dest) const { return Object(); }
1656 Object operator()(const CKeyID &keyID) const {
1659 pwalletMain->GetPubKey(keyID, vchPubKey);
1660 obj.push_back(Pair("isscript", false));
1661 if (mine == MINE_SPENDABLE) {
1662 pwalletMain->GetPubKey(keyID, vchPubKey);
1663 obj.push_back(Pair("pubkey", HexStr(vchPubKey.Raw())));
1664 obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed()));
1669 Object operator()(const CScriptID &scriptID) const {
1671 obj.push_back(Pair("isscript", true));
1672 if (mine == MINE_SPENDABLE) {
1674 pwalletMain->GetCScript(scriptID, subscript);
1675 std::vector<CTxDestination> addresses;
1676 txnouttype whichType;
1678 ExtractDestinations(subscript, whichType, addresses, nRequired);
1679 obj.push_back(Pair("script", GetTxnOutputType(whichType)));
1680 obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end())));
1682 BOOST_FOREACH(const CTxDestination& addr, addresses)
1683 a.push_back(CBitcoinAddress(addr).ToString());
1684 obj.push_back(Pair("addresses", a));
1685 if (whichType == TX_MULTISIG)
1686 obj.push_back(Pair("sigsrequired", nRequired));
1692 Value validateaddress(const Array& params, bool fHelp)
1694 if (fHelp || params.size() != 1)
1695 throw runtime_error(
1696 "validateaddress <novacoinaddress>\n"
1697 "Return information about <novacoinaddress>.");
1699 CBitcoinAddress address(params[0].get_str());
1700 bool isValid = address.IsValid();
1703 ret.push_back(Pair("isvalid", isValid));
1706 CTxDestination dest = address.Get();
1707 string currentAddress = address.ToString();
1708 ret.push_back(Pair("address", currentAddress));
1709 isminetype mine = pwalletMain ? IsMine(*pwalletMain, dest) : MINE_NO;
1710 ret.push_back(Pair("ismine", mine != MINE_NO));
1711 if (mine != MINE_NO) {
1712 ret.push_back(Pair("watchonly", mine == MINE_WATCH_ONLY));
1713 Object detail = boost::apply_visitor(DescribeAddressVisitor(mine), dest);
1714 ret.insert(ret.end(), detail.begin(), detail.end());
1716 if (pwalletMain->mapAddressBook.count(dest))
1717 ret.push_back(Pair("account", pwalletMain->mapAddressBook[dest]));
1722 // ppcoin: reserve balance from being staked for network protection
1723 Value reservebalance(const Array& params, bool fHelp)
1725 if (fHelp || params.size() > 2)
1726 throw runtime_error(
1727 "reservebalance [<reserve> [amount]]\n"
1728 "<reserve> is true or false to turn balance reserve on or off.\n"
1729 "<amount> is a real and rounded to cent.\n"
1730 "Set reserve amount not participating in network protection.\n"
1731 "If no parameters provided current setting is printed.\n");
1733 if (params.size() > 0)
1735 bool fReserve = params[0].get_bool();
1738 if (params.size() == 1)
1739 throw runtime_error("must provide amount to reserve balance.\n");
1740 int64_t nAmount = AmountFromValue(params[1]);
1741 nAmount = (nAmount / CENT) * CENT; // round to cent
1743 throw runtime_error("amount cannot be negative.\n");
1744 mapArgs["-reservebalance"] = FormatMoney(nAmount).c_str();
1748 if (params.size() > 1)
1749 throw runtime_error("cannot specify amount to turn off reserve.\n");
1750 mapArgs["-reservebalance"] = "0";
1755 int64_t nReserveBalance = 0;
1756 if (mapArgs.count("-reservebalance") && !ParseMoney(mapArgs["-reservebalance"], nReserveBalance))
1757 throw runtime_error("invalid reserve balance amount\n");
1758 result.push_back(Pair("reserve", (nReserveBalance > 0)));
1759 result.push_back(Pair("amount", ValueFromAmount(nReserveBalance)));
1764 // ppcoin: check wallet integrity
1765 Value checkwallet(const Array& params, bool fHelp)
1767 if (fHelp || params.size() > 0)
1768 throw runtime_error(
1770 "Check wallet for integrity.\n");
1773 int64_t nBalanceInQuestion;
1774 pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion, true);
1776 if (nMismatchSpent == 0)
1777 result.push_back(Pair("wallet check passed", true));
1780 result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1781 result.push_back(Pair("amount in question", ValueFromAmount(nBalanceInQuestion)));
1787 // ppcoin: repair wallet
1788 Value repairwallet(const Array& params, bool fHelp)
1790 if (fHelp || params.size() > 0)
1791 throw runtime_error(
1793 "Repair wallet if checkwallet reports any problem.\n");
1796 int64_t nBalanceInQuestion;
1797 pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion);
1799 if (nMismatchSpent == 0)
1800 result.push_back(Pair("wallet check passed", true));
1803 result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1804 result.push_back(Pair("amount affected by repair", ValueFromAmount(nBalanceInQuestion)));
1809 // NovaCoin: resend unconfirmed wallet transactions
1810 Value resendtx(const Array& params, bool fHelp)
1812 if (fHelp || params.size() > 1)
1813 throw runtime_error(
1815 "Re-send unconfirmed transactions.\n"
1818 ResendWalletTransactions();
1823 // ppcoin: make a public-private key pair
1824 Value makekeypair(const Array& params, bool fHelp)
1826 if (fHelp || params.size() > 1)
1827 throw runtime_error(
1828 "makekeypair [prefix]\n"
1829 "Make a public/private key pair.\n"
1830 "[prefix] is optional preferred prefix for the public key.\n");
1832 string strPrefix = "";
1833 if (params.size() > 0)
1834 strPrefix = params[0].get_str();
1837 key.MakeNewKey(false);
1839 CPrivKey vchPrivKey = key.GetPrivKey();
1841 result.push_back(Pair("PrivateKey", HexStr<CPrivKey::iterator>(vchPrivKey.begin(), vchPrivKey.end())));
1842 result.push_back(Pair("PublicKey", HexStr(key.GetPubKey().Raw())));