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 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 nAmount = AmountFromValue(params[0]);
276 int64 nMinValue = AmountFromValue(params[1]);
279 int64 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 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> 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 GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth, const isminefilter& filter)
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 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 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.
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 allGeneratedImmature, allGeneratedMature, allFee;
594 allGeneratedImmature = allGeneratedMature = allFee = 0;
596 string strSentAccount;
597 list<pair<CTxDestination, int64> > listReceived;
598 list<pair<CTxDestination, int64> > listSent;
599 wtx.GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount, filter);
600 if (wtx.GetDepthInMainChain() >= nMinDepth)
602 BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64)& r, listReceived)
603 nBalance += r.second;
605 BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64)& r, listSent)
606 nBalance -= r.second;
608 nBalance += allGeneratedMature;
610 return ValueFromAmount(nBalance);
613 string strAccount = AccountFromValue(params[0]);
615 int64 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 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 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 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 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> > vecSend;
741 int64 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 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 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 nFeeRequired = 0;
774 bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired);
777 int64 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);
850 CScriptID innerID = inner.GetID();
851 pwalletMain->AddCScript(inner);
853 pwalletMain->SetAddressBookName(innerID, strAccount);
854 return CBitcoinAddress(innerID).ToString();
857 Value addredeemscript(const Array& params, bool fHelp)
859 if (fHelp || params.size() < 1 || params.size() > 2)
861 string msg = "addredeemscript <redeemScript> [account]\n"
862 "Add a P2SH address with a specified redeemScript to the wallet.\n"
863 "If [account] is specified, assign address to [account].";
864 throw runtime_error(msg);
868 if (params.size() > 1)
869 strAccount = AccountFromValue(params[1]);
871 // Construct using pay-to-script-hash:
872 vector<unsigned char> innerData = ParseHexV(params[0], "redeemScript");
873 CScript inner(innerData.begin(), innerData.end());
874 CScriptID innerID = inner.GetID();
875 pwalletMain->AddCScript(inner);
877 pwalletMain->SetAddressBookName(innerID, strAccount);
878 return CBitcoinAddress(innerID).ToString();
888 nConf = std::numeric_limits<int>::max();
892 Value ListReceived(const Array& params, bool fByAccounts)
894 // Minimum confirmations
896 if (params.size() > 0)
897 nMinDepth = params[0].get_int();
899 // Whether to include empty accounts
900 bool fIncludeEmpty = false;
901 if (params.size() > 1)
902 fIncludeEmpty = params[1].get_bool();
905 map<CBitcoinAddress, tallyitem> mapTally;
906 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
908 const CWalletTx& wtx = (*it).second;
910 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
913 int nDepth = wtx.GetDepthInMainChain();
914 if (nDepth < nMinDepth)
917 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
919 CTxDestination address;
920 if (!ExtractDestination(txout.scriptPubKey, address) || !IsMine(*pwalletMain, address))
923 tallyitem& item = mapTally[address];
924 item.nAmount += txout.nValue;
925 item.nConf = min(item.nConf, nDepth);
931 map<string, tallyitem> mapAccountTally;
932 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
934 const CBitcoinAddress& address = item.first;
935 const string& strAccount = item.second;
936 map<CBitcoinAddress, tallyitem>::iterator it = mapTally.find(address);
937 if (it == mapTally.end() && !fIncludeEmpty)
941 int nConf = std::numeric_limits<int>::max();
942 if (it != mapTally.end())
944 nAmount = (*it).second.nAmount;
945 nConf = (*it).second.nConf;
950 tallyitem& item = mapAccountTally[strAccount];
951 item.nAmount += nAmount;
952 item.nConf = min(item.nConf, nConf);
957 obj.push_back(Pair("address", address.ToString()));
958 obj.push_back(Pair("account", strAccount));
959 obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
960 obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf)));
967 for (map<string, tallyitem>::iterator it = mapAccountTally.begin(); it != mapAccountTally.end(); ++it)
969 int64 nAmount = (*it).second.nAmount;
970 int nConf = (*it).second.nConf;
972 obj.push_back(Pair("account", (*it).first));
973 obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
974 obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf)));
982 Value listreceivedbyaddress(const Array& params, bool fHelp)
984 if (fHelp || params.size() > 2)
986 "listreceivedbyaddress [minconf=1] [includeempty=false]\n"
987 "[minconf] is the minimum number of confirmations before payments are included.\n"
988 "[includeempty] whether to include addresses that haven't received any payments.\n"
989 "Returns an array of objects containing:\n"
990 " \"address\" : receiving address\n"
991 " \"account\" : the account of the receiving address\n"
992 " \"amount\" : total amount received by the address\n"
993 " \"confirmations\" : number of confirmations of the most recent transaction included");
995 return ListReceived(params, false);
998 Value listreceivedbyaccount(const Array& params, bool fHelp)
1000 if (fHelp || params.size() > 2)
1001 throw runtime_error(
1002 "listreceivedbyaccount [minconf=1] [includeempty=false]\n"
1003 "[minconf] is the minimum number of confirmations before payments are included.\n"
1004 "[includeempty] whether to include accounts that haven't received any payments.\n"
1005 "Returns an array of objects containing:\n"
1006 " \"account\" : the account of the receiving addresses\n"
1007 " \"amount\" : total amount received by addresses with this account\n"
1008 " \"confirmations\" : number of confirmations of the most recent transaction included");
1010 return ListReceived(params, true);
1013 static void MaybePushAddress(Object & entry, const CTxDestination &dest)
1015 CBitcoinAddress addr;
1017 entry.push_back(Pair("address", addr.ToString()));
1020 void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret, const isminefilter& filter)
1022 int64 nGeneratedImmature, nGeneratedMature, nFee;
1023 string strSentAccount;
1024 list<pair<CTxDestination, int64> > listReceived;
1025 list<pair<CTxDestination, int64> > listSent;
1027 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount, filter);
1029 bool fAllAccounts = (strAccount == string("*"));
1030 bool involvesWatchonly = wtx.IsFromMe(MINE_WATCH_ONLY);
1032 // Generated blocks assigned to account ""
1033 if ((nGeneratedMature+nGeneratedImmature) != 0 && (fAllAccounts || strAccount == ""))
1036 entry.push_back(Pair("account", string("")));
1037 if (nGeneratedImmature)
1039 entry.push_back(Pair("category", wtx.GetDepthInMainChain() ? "immature" : "orphan"));
1040 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedImmature)));
1044 entry.push_back(Pair("category", "generate"));
1045 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedMature)));
1048 WalletTxToJSON(wtx, entry);
1049 ret.push_back(entry);
1053 if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount))
1055 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& s, listSent)
1058 entry.push_back(Pair("account", strSentAccount));
1059 if(involvesWatchonly || (::IsMine(*pwalletMain, s.first) & MINE_WATCH_ONLY))
1060 entry.push_back(Pair("involvesWatchonly", true));
1061 MaybePushAddress(entry, s.first);
1063 if (wtx.GetDepthInMainChain() < 0) {
1064 entry.push_back(Pair("category", "conflicted"));
1066 entry.push_back(Pair("category", "send"));
1069 entry.push_back(Pair("amount", ValueFromAmount(-s.second)));
1070 entry.push_back(Pair("fee", ValueFromAmount(-nFee)));
1072 WalletTxToJSON(wtx, entry);
1073 ret.push_back(entry);
1078 if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth)
1080 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& r, listReceived)
1083 if (pwalletMain->mapAddressBook.count(r.first))
1084 account = pwalletMain->mapAddressBook[r.first];
1085 if (fAllAccounts || (account == strAccount))
1088 entry.push_back(Pair("account", account));
1089 if(involvesWatchonly || (::IsMine(*pwalletMain, r.first) & MINE_WATCH_ONLY))
1090 entry.push_back(Pair("involvesWatchonly", true));
1091 MaybePushAddress(entry, r.first);
1092 if (wtx.IsCoinBase())
1094 if (wtx.GetDepthInMainChain() < 1)
1095 entry.push_back(Pair("category", "orphan"));
1096 else if (wtx.GetBlocksToMaturity() > 0)
1097 entry.push_back(Pair("category", "immature"));
1099 entry.push_back(Pair("category", "generate"));
1102 entry.push_back(Pair("category", "receive"));
1103 entry.push_back(Pair("amount", ValueFromAmount(r.second)));
1105 WalletTxToJSON(wtx, entry);
1106 ret.push_back(entry);
1112 void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Array& ret)
1114 bool fAllAccounts = (strAccount == string("*"));
1116 if (fAllAccounts || acentry.strAccount == strAccount)
1119 entry.push_back(Pair("account", acentry.strAccount));
1120 entry.push_back(Pair("category", "move"));
1121 entry.push_back(Pair("time", (boost::int64_t)acentry.nTime));
1122 entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit)));
1123 entry.push_back(Pair("otheraccount", acentry.strOtherAccount));
1124 entry.push_back(Pair("comment", acentry.strComment));
1125 ret.push_back(entry);
1129 Value listtransactions(const Array& params, bool fHelp)
1131 if (fHelp || params.size() > 3)
1132 throw runtime_error(
1133 "listtransactions [account] [count=10] [from=0]\n"
1134 "Returns up to [count] most recent transactions skipping the first [from] transactions for account [account].");
1136 string strAccount = "*";
1137 if (params.size() > 0)
1138 strAccount = params[0].get_str();
1140 if (params.size() > 1)
1141 nCount = params[1].get_int();
1143 if (params.size() > 2)
1144 nFrom = params[2].get_int();
1146 isminefilter filter = MINE_SPENDABLE;
1147 if(params.size() > 3)
1148 if(params[3].get_bool())
1149 filter = filter | MINE_WATCH_ONLY;
1152 throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count");
1154 throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative from");
1158 std::list<CAccountingEntry> acentries;
1159 CWallet::TxItems txOrdered = pwalletMain->OrderedTxItems(acentries, strAccount);
1161 // iterate backwards until we have nCount items to return:
1162 for (CWallet::TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
1164 CWalletTx *const pwtx = (*it).second.first;
1166 ListTransactions(*pwtx, strAccount, 0, true, ret, filter);
1167 CAccountingEntry *const pacentry = (*it).second.second;
1169 AcentryToJSON(*pacentry, strAccount, ret);
1171 if ((int)ret.size() >= (nCount+nFrom)) break;
1173 // ret is newest to oldest
1175 if (nFrom > (int)ret.size())
1177 if ((nFrom + nCount) > (int)ret.size())
1178 nCount = ret.size() - nFrom;
1179 Array::iterator first = ret.begin();
1180 std::advance(first, nFrom);
1181 Array::iterator last = ret.begin();
1182 std::advance(last, nFrom+nCount);
1184 if (last != ret.end()) ret.erase(last, ret.end());
1185 if (first != ret.begin()) ret.erase(ret.begin(), first);
1187 std::reverse(ret.begin(), ret.end()); // Return oldest to newest
1192 Value listaccounts(const Array& params, bool fHelp)
1194 if (fHelp || params.size() > 1)
1195 throw runtime_error(
1196 "listaccounts [minconf=1]\n"
1197 "Returns Object that has account names as keys, account balances as values.");
1200 if (params.size() > 0)
1201 nMinDepth = params[0].get_int();
1203 isminefilter includeWatchonly = MINE_SPENDABLE;
1204 if(params.size() > 1)
1205 if(params[1].get_bool())
1206 includeWatchonly = includeWatchonly | MINE_WATCH_ONLY;
1209 map<string, int64> mapAccountBalances;
1210 BOOST_FOREACH(const PAIRTYPE(CTxDestination, string)& entry, pwalletMain->mapAddressBook) {
1211 if (IsMine(*pwalletMain, entry.first)) // This address belongs to me
1212 mapAccountBalances[entry.second] = 0;
1215 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
1217 const CWalletTx& wtx = (*it).second;
1218 int64 nGeneratedImmature, nGeneratedMature, nFee;
1219 string strSentAccount;
1220 list<pair<CTxDestination, int64> > listReceived;
1221 list<pair<CTxDestination, int64> > listSent;
1222 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount, includeWatchonly);
1223 mapAccountBalances[strSentAccount] -= nFee;
1224 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& s, listSent)
1225 mapAccountBalances[strSentAccount] -= s.second;
1226 if (wtx.GetDepthInMainChain() >= nMinDepth)
1228 mapAccountBalances[""] += nGeneratedMature;
1229 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& r, listReceived)
1230 if (pwalletMain->mapAddressBook.count(r.first))
1231 mapAccountBalances[pwalletMain->mapAddressBook[r.first]] += r.second;
1233 mapAccountBalances[""] += r.second;
1237 list<CAccountingEntry> acentries;
1238 CWalletDB(pwalletMain->strWalletFile).ListAccountCreditDebit("*", acentries);
1239 BOOST_FOREACH(const CAccountingEntry& entry, acentries)
1240 mapAccountBalances[entry.strAccount] += entry.nCreditDebit;
1243 BOOST_FOREACH(const PAIRTYPE(string, int64)& accountBalance, mapAccountBalances) {
1244 ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second)));
1249 Value listsinceblock(const Array& params, bool fHelp)
1252 throw runtime_error(
1253 "listsinceblock [blockhash] [target-confirmations]\n"
1254 "Get all transactions in blocks since block [blockhash], or all transactions if omitted");
1256 CBlockIndex *pindex = NULL;
1257 int target_confirms = 1;
1258 isminefilter filter = MINE_SPENDABLE;
1260 if (params.size() > 0)
1262 uint256 blockId = 0;
1264 blockId.SetHex(params[0].get_str());
1265 pindex = CBlockLocator(blockId).GetBlockIndex();
1268 if (params.size() > 1)
1270 target_confirms = params[1].get_int();
1272 if (target_confirms < 1)
1273 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter");
1276 if(params.size() > 2)
1277 if(params[2].get_bool())
1278 filter = filter | MINE_WATCH_ONLY;
1280 int depth = pindex ? (1 + nBestHeight - pindex->nHeight) : -1;
1284 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); it++)
1286 CWalletTx tx = (*it).second;
1288 if (depth == -1 || tx.GetDepthInMainChain() < depth)
1289 ListTransactions(tx, "*", 0, true, transactions, filter);
1294 if (target_confirms == 1)
1296 lastblock = hashBestChain;
1300 int target_height = pindexBest->nHeight + 1 - target_confirms;
1303 for (block = pindexBest;
1304 block && block->nHeight > target_height;
1305 block = block->pprev) { }
1307 lastblock = block ? block->GetBlockHash() : 0;
1311 ret.push_back(Pair("transactions", transactions));
1312 ret.push_back(Pair("lastblock", lastblock.GetHex()));
1317 Value gettransaction(const Array& params, bool fHelp)
1319 if (fHelp || params.size() != 1)
1320 throw runtime_error(
1321 "gettransaction <txid>\n"
1322 "Get detailed information about <txid>");
1325 hash.SetHex(params[0].get_str());
1327 isminefilter filter = MINE_SPENDABLE;
1328 if(params.size() > 1)
1329 if(params[1].get_bool())
1330 filter = filter | MINE_WATCH_ONLY;
1334 if (pwalletMain->mapWallet.count(hash))
1336 const CWalletTx& wtx = pwalletMain->mapWallet[hash];
1338 TxToJSON(wtx, 0, entry);
1340 int64 nCredit = wtx.GetCredit(filter);
1341 int64 nDebit = wtx.GetDebit(filter);
1342 int64 nNet = nCredit - nDebit;
1343 int64 nFee = (wtx.IsFromMe(filter) ? wtx.GetValueOut() - nDebit : 0);
1345 entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee)));
1346 if (wtx.IsFromMe(filter))
1347 entry.push_back(Pair("fee", ValueFromAmount(nFee)));
1349 WalletTxToJSON(wtx, entry);
1352 ListTransactions(pwalletMain->mapWallet[hash], "*", 0, false, details, filter);
1353 entry.push_back(Pair("details", details));
1358 uint256 hashBlock = 0;
1359 if (GetTransaction(hash, tx, hashBlock))
1361 TxToJSON(tx, 0, entry);
1363 entry.push_back(Pair("confirmations", 0));
1366 entry.push_back(Pair("blockhash", hashBlock.GetHex()));
1367 map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashBlock);
1368 if (mi != mapBlockIndex.end() && (*mi).second)
1370 CBlockIndex* pindex = (*mi).second;
1371 if (pindex->IsInMainChain())
1372 entry.push_back(Pair("confirmations", 1 + nBestHeight - pindex->nHeight));
1374 entry.push_back(Pair("confirmations", 0));
1379 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction");
1386 Value backupwallet(const Array& params, bool fHelp)
1388 if (fHelp || params.size() != 1)
1389 throw runtime_error(
1390 "backupwallet <destination>\n"
1391 "Safely copies wallet.dat to destination, which can be a directory or a path with filename.");
1393 string strDest = params[0].get_str();
1394 if (!BackupWallet(*pwalletMain, strDest))
1395 throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet backup failed!");
1401 Value keypoolrefill(const Array& params, bool fHelp)
1403 if (fHelp || params.size() > 1)
1404 throw runtime_error(
1405 "keypoolrefill [new-size]\n"
1406 "Fills the keypool."
1407 + HelpRequiringPassphrase());
1409 unsigned int nSize = max(GetArg("-keypool", 100), 0LL);
1410 if (params.size() > 0) {
1411 if (params[0].get_int() < 0)
1412 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected valid size");
1413 nSize = (unsigned int) params[0].get_int();
1416 EnsureWalletIsUnlocked();
1418 pwalletMain->TopUpKeyPool(nSize);
1420 if (pwalletMain->GetKeyPoolSize() < nSize)
1421 throw JSONRPCError(RPC_WALLET_ERROR, "Error refreshing keypool.");
1427 void ThreadTopUpKeyPool(void* parg)
1429 // Make this thread recognisable as the key-topping-up thread
1430 RenameThread("novacoin-key-top");
1432 pwalletMain->TopUpKeyPool();
1435 void ThreadCleanWalletPassphrase(void* parg)
1437 // Make this thread recognisable as the wallet relocking thread
1438 RenameThread("novacoin-lock-wa");
1440 int64 nMyWakeTime = GetTimeMillis() + *((int64*)parg) * 1000;
1442 ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime);
1444 if (nWalletUnlockTime == 0)
1446 nWalletUnlockTime = nMyWakeTime;
1450 if (nWalletUnlockTime==0)
1452 int64 nToSleep = nWalletUnlockTime - GetTimeMillis();
1456 LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime);
1458 ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime);
1462 if (nWalletUnlockTime)
1464 nWalletUnlockTime = 0;
1465 pwalletMain->Lock();
1470 if (nWalletUnlockTime < nMyWakeTime)
1471 nWalletUnlockTime = nMyWakeTime;
1474 LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime);
1476 delete (int64*)parg;
1479 Value walletpassphrase(const Array& params, bool fHelp)
1481 if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 3))
1482 throw runtime_error(
1483 "walletpassphrase <passphrase> <timeout> [mintonly]\n"
1484 "Stores the wallet decryption key in memory for <timeout> seconds.\n"
1485 "mintonly is optional true/false allowing only block minting.");
1488 if (!pwalletMain->IsCrypted())
1489 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called.");
1491 if (!pwalletMain->IsLocked())
1492 throw JSONRPCError(RPC_WALLET_ALREADY_UNLOCKED, "Error: Wallet is already unlocked, use walletlock first if need to change unlock settings.");
1493 // Note that the walletpassphrase is stored in params[0] which is not mlock()ed
1494 SecureString strWalletPass;
1495 strWalletPass.reserve(100);
1496 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1497 // Alternately, find a way to make params[0] mlock()'d to begin with.
1498 strWalletPass = params[0].get_str().c_str();
1500 if (strWalletPass.length() > 0)
1502 if (!pwalletMain->Unlock(strWalletPass))
1503 throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
1506 throw runtime_error(
1507 "walletpassphrase <passphrase> <timeout>\n"
1508 "Stores the wallet decryption key in memory for <timeout> seconds.");
1510 NewThread(ThreadTopUpKeyPool, NULL);
1511 int64* pnSleepTime = new int64(params[1].get_int64());
1512 NewThread(ThreadCleanWalletPassphrase, pnSleepTime);
1514 // ppcoin: if user OS account compromised prevent trivial sendmoney commands
1515 if (params.size() > 2)
1516 fWalletUnlockMintOnly = params[2].get_bool();
1518 fWalletUnlockMintOnly = false;
1524 Value walletpassphrasechange(const Array& params, bool fHelp)
1526 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2))
1527 throw runtime_error(
1528 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1529 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1532 if (!pwalletMain->IsCrypted())
1533 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrasechange was called.");
1535 // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string)
1536 // Alternately, find a way to make params[0] mlock()'d to begin with.
1537 SecureString strOldWalletPass;
1538 strOldWalletPass.reserve(100);
1539 strOldWalletPass = params[0].get_str().c_str();
1541 SecureString strNewWalletPass;
1542 strNewWalletPass.reserve(100);
1543 strNewWalletPass = params[1].get_str().c_str();
1545 if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1)
1546 throw runtime_error(
1547 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1548 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1550 if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass))
1551 throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
1557 Value walletlock(const Array& params, bool fHelp)
1559 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 0))
1560 throw runtime_error(
1562 "Removes the wallet encryption key from memory, locking the wallet.\n"
1563 "After calling this method, you will need to call walletpassphrase again\n"
1564 "before being able to call any methods which require the wallet to be unlocked.");
1567 if (!pwalletMain->IsCrypted())
1568 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletlock was called.");
1571 LOCK(cs_nWalletUnlockTime);
1572 pwalletMain->Lock();
1573 nWalletUnlockTime = 0;
1580 Value encryptwallet(const Array& params, bool fHelp)
1582 if (!pwalletMain->IsCrypted() && (fHelp || params.size() != 1))
1583 throw runtime_error(
1584 "encryptwallet <passphrase>\n"
1585 "Encrypts the wallet with <passphrase>.");
1588 if (pwalletMain->IsCrypted())
1589 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an encrypted wallet, but encryptwallet was called.");
1591 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1592 // Alternately, find a way to make params[0] mlock()'d to begin with.
1593 SecureString strWalletPass;
1594 strWalletPass.reserve(100);
1595 strWalletPass = params[0].get_str().c_str();
1597 if (strWalletPass.length() < 1)
1598 throw runtime_error(
1599 "encryptwallet <passphrase>\n"
1600 "Encrypts the wallet with <passphrase>.");
1602 if (!pwalletMain->EncryptWallet(strWalletPass))
1603 throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: Failed to encrypt the wallet.");
1605 // BDB seems to have a bad habit of writing old data into
1606 // slack space in .dat files; that is bad if the old data is
1607 // unencrypted private keys. So:
1609 return "wallet encrypted; NovaCoin server stopping, restart to run with encrypted wallet. The keypool has been flushed, you need to make a new backup.";
1612 class DescribeAddressVisitor : public boost::static_visitor<Object>
1617 DescribeAddressVisitor(isminetype mineIn) : mine(mineIn) {}
1619 Object operator()(const CNoDestination &dest) const { return Object(); }
1620 Object operator()(const CKeyID &keyID) const {
1623 pwalletMain->GetPubKey(keyID, vchPubKey);
1624 obj.push_back(Pair("isscript", false));
1625 if (mine == MINE_SPENDABLE) {
1626 pwalletMain->GetPubKey(keyID, vchPubKey);
1627 obj.push_back(Pair("pubkey", HexStr(vchPubKey.Raw())));
1628 obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed()));
1633 Object operator()(const CScriptID &scriptID) const {
1635 obj.push_back(Pair("isscript", true));
1636 if (mine == MINE_SPENDABLE) {
1638 pwalletMain->GetCScript(scriptID, subscript);
1639 std::vector<CTxDestination> addresses;
1640 txnouttype whichType;
1642 ExtractDestinations(subscript, whichType, addresses, nRequired);
1643 obj.push_back(Pair("script", GetTxnOutputType(whichType)));
1644 obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end())));
1646 BOOST_FOREACH(const CTxDestination& addr, addresses)
1647 a.push_back(CBitcoinAddress(addr).ToString());
1648 obj.push_back(Pair("addresses", a));
1649 if (whichType == TX_MULTISIG)
1650 obj.push_back(Pair("sigsrequired", nRequired));
1656 Value validateaddress(const Array& params, bool fHelp)
1658 if (fHelp || params.size() != 1)
1659 throw runtime_error(
1660 "validateaddress <novacoinaddress>\n"
1661 "Return information about <novacoinaddress>.");
1663 CBitcoinAddress address(params[0].get_str());
1664 bool isValid = address.IsValid();
1667 ret.push_back(Pair("isvalid", isValid));
1670 CTxDestination dest = address.Get();
1671 string currentAddress = address.ToString();
1672 ret.push_back(Pair("address", currentAddress));
1673 isminetype mine = pwalletMain ? IsMine(*pwalletMain, dest) : MINE_NO;
1674 ret.push_back(Pair("ismine", mine != MINE_NO));
1675 if (mine != MINE_NO) {
1676 ret.push_back(Pair("watchonly", mine == MINE_WATCH_ONLY));
1677 Object detail = boost::apply_visitor(DescribeAddressVisitor(mine), dest);
1678 ret.insert(ret.end(), detail.begin(), detail.end());
1680 if (pwalletMain->mapAddressBook.count(dest))
1681 ret.push_back(Pair("account", pwalletMain->mapAddressBook[dest]));
1686 // ppcoin: reserve balance from being staked for network protection
1687 Value reservebalance(const Array& params, bool fHelp)
1689 if (fHelp || params.size() > 2)
1690 throw runtime_error(
1691 "reservebalance [<reserve> [amount]]\n"
1692 "<reserve> is true or false to turn balance reserve on or off.\n"
1693 "<amount> is a real and rounded to cent.\n"
1694 "Set reserve amount not participating in network protection.\n"
1695 "If no parameters provided current setting is printed.\n");
1697 if (params.size() > 0)
1699 bool fReserve = params[0].get_bool();
1702 if (params.size() == 1)
1703 throw runtime_error("must provide amount to reserve balance.\n");
1704 int64 nAmount = AmountFromValue(params[1]);
1705 nAmount = (nAmount / CENT) * CENT; // round to cent
1707 throw runtime_error("amount cannot be negative.\n");
1708 mapArgs["-reservebalance"] = FormatMoney(nAmount).c_str();
1712 if (params.size() > 1)
1713 throw runtime_error("cannot specify amount to turn off reserve.\n");
1714 mapArgs["-reservebalance"] = "0";
1719 int64 nReserveBalance = 0;
1720 if (mapArgs.count("-reservebalance") && !ParseMoney(mapArgs["-reservebalance"], nReserveBalance))
1721 throw runtime_error("invalid reserve balance amount\n");
1722 result.push_back(Pair("reserve", (nReserveBalance > 0)));
1723 result.push_back(Pair("amount", ValueFromAmount(nReserveBalance)));
1728 // ppcoin: check wallet integrity
1729 Value checkwallet(const Array& params, bool fHelp)
1731 if (fHelp || params.size() > 0)
1732 throw runtime_error(
1734 "Check wallet for integrity.\n");
1737 int64 nBalanceInQuestion;
1738 pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion, true);
1740 if (nMismatchSpent == 0)
1741 result.push_back(Pair("wallet check passed", true));
1744 result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1745 result.push_back(Pair("amount in question", ValueFromAmount(nBalanceInQuestion)));
1751 // ppcoin: repair wallet
1752 Value repairwallet(const Array& params, bool fHelp)
1754 if (fHelp || params.size() > 0)
1755 throw runtime_error(
1757 "Repair wallet if checkwallet reports any problem.\n");
1760 int64 nBalanceInQuestion;
1761 pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion);
1763 if (nMismatchSpent == 0)
1764 result.push_back(Pair("wallet check passed", true));
1767 result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1768 result.push_back(Pair("amount affected by repair", ValueFromAmount(nBalanceInQuestion)));
1773 // NovaCoin: resend unconfirmed wallet transactions
1774 Value resendtx(const Array& params, bool fHelp)
1776 if (fHelp || params.size() > 1)
1777 throw runtime_error(
1779 "Re-send unconfirmed transactions.\n"
1782 ResendWalletTransactions();
1787 // ppcoin: make a public-private key pair
1788 Value makekeypair(const Array& params, bool fHelp)
1790 if (fHelp || params.size() > 1)
1791 throw runtime_error(
1792 "makekeypair [prefix]\n"
1793 "Make a public/private key pair.\n"
1794 "[prefix] is optional preferred prefix for the public key.\n");
1796 string strPrefix = "";
1797 if (params.size() > 0)
1798 strPrefix = params[0].get_str();
1801 key.MakeNewKey(false);
1803 CPrivKey vchPrivKey = key.GetPrivKey();
1805 result.push_back(Pair("PrivateKey", HexStr<CPrivKey::iterator>(vchPrivKey.begin(), vchPrivKey.end())));
1806 result.push_back(Pair("PublicKey", HexStr(key.GetPubKey().Raw())));