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 uint256 hash = wtx.GetHash();
48 uint256 metahash = wtx.GetMetaHash();
49 entry.push_back(Pair("txid", hash.GetHex()));
50 entry.push_back(Pair("metahash", metahash.GetHex()));
52 BOOST_FOREACH(const uint256& conflict, wtx.GetConflicts())
53 conflicts.push_back(conflict.GetHex());
54 entry.push_back(Pair("walletconflicts", conflicts));
55 entry.push_back(Pair("time", (boost::int64_t)wtx.GetTxTime()));
56 entry.push_back(Pair("timereceived", (boost::int64_t)wtx.nTimeReceived));
57 BOOST_FOREACH(const PAIRTYPE(string,string)& item, wtx.mapValue)
58 entry.push_back(Pair(item.first, item.second));
61 string AccountFromValue(const Value& value)
63 string strAccount = value.get_str();
64 if (strAccount == "*")
65 throw JSONRPCError(RPC_WALLET_INVALID_ACCOUNT_NAME, "Invalid account name");
69 Value getinfo(const Array& params, bool fHelp)
71 if (fHelp || params.size() != 0)
74 "Returns an object containing various state info.");
77 GetProxy(NET_IPV4, proxy);
80 obj.push_back(Pair("version", FormatFullVersion()));
81 obj.push_back(Pair("protocolversion",(int)PROTOCOL_VERSION));
82 obj.push_back(Pair("walletversion", pwalletMain->GetVersion()));
83 obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance())));
84 obj.push_back(Pair("newmint", ValueFromAmount(pwalletMain->GetNewMint())));
85 obj.push_back(Pair("stake", ValueFromAmount(pwalletMain->GetStake())));
86 obj.push_back(Pair("blocks", (int)nBestHeight));
87 obj.push_back(Pair("timeoffset", (boost::int64_t)GetTimeOffset()));
88 obj.push_back(Pair("moneysupply", ValueFromAmount(pindexBest->nMoneySupply)));
89 obj.push_back(Pair("connections", (int)vNodes.size()));
90 obj.push_back(Pair("proxy", (proxy.first.IsValid() ? proxy.first.ToStringIPPort() : string())));
91 obj.push_back(Pair("ip", addrSeenByPeer.ToStringIP()));
93 diff.push_back(Pair("proof-of-work", GetDifficulty()));
94 diff.push_back(Pair("proof-of-stake", GetDifficulty(GetLastBlockIndex(pindexBest, true))));
95 obj.push_back(Pair("difficulty", diff));
97 obj.push_back(Pair("testnet", fTestNet));
98 obj.push_back(Pair("keypoololdest", (boost::int64_t)pwalletMain->GetOldestKeyPoolTime()));
99 obj.push_back(Pair("keypoolsize", (int)pwalletMain->GetKeyPoolSize()));
100 obj.push_back(Pair("paytxfee", ValueFromAmount(nTransactionFee)));
101 obj.push_back(Pair("mininput", ValueFromAmount(nMinimumInputValue)));
102 if (pwalletMain->IsCrypted())
103 obj.push_back(Pair("unlocked_until", (boost::int64_t)nWalletUnlockTime / 1000));
104 obj.push_back(Pair("errors", GetWarnings("statusbar")));
109 Value getnewpubkey(const Array& params, bool fHelp)
111 if (fHelp || params.size() > 1)
113 "getnewpubkey [account]\n"
114 "Returns new public key for coinbase generation.");
116 // Parse the account first so we don't generate a key if there's an error
118 if (params.size() > 0)
119 strAccount = AccountFromValue(params[0]);
121 if (!pwalletMain->IsLocked())
122 pwalletMain->TopUpKeyPool();
124 // Generate a new key that is added to wallet
126 if (!pwalletMain->GetKeyFromPool(newKey, false))
127 throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
128 CKeyID keyID = newKey.GetID();
130 pwalletMain->SetAddressBookName(keyID, strAccount);
131 vector<unsigned char> vchPubKey = newKey.Raw();
133 return HexStr(vchPubKey.begin(), vchPubKey.end());
137 Value getnewaddress(const Array& params, bool fHelp)
139 if (fHelp || params.size() > 1)
141 "getnewaddress [account]\n"
142 "Returns a new NovaCoin address for receiving payments. "
143 "If [account] is specified (recommended), it is added to the address book "
144 "so payments received with the address will be credited to [account].");
146 // Parse the account first so we don't generate a key if there's an error
148 if (params.size() > 0)
149 strAccount = AccountFromValue(params[0]);
151 if (!pwalletMain->IsLocked())
152 pwalletMain->TopUpKeyPool();
154 // Generate a new key that is added to wallet
156 if (!pwalletMain->GetKeyFromPool(newKey, false))
157 throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
158 CKeyID keyID = newKey.GetID();
160 pwalletMain->SetAddressBookName(keyID, strAccount);
162 return CBitcoinAddress(keyID).ToString();
166 CBitcoinAddress GetAccountAddress(string strAccount, bool bForceNew=false)
168 CWalletDB walletdb(pwalletMain->strWalletFile);
171 walletdb.ReadAccount(strAccount, account);
173 bool bKeyUsed = false;
175 // Check if the current key has been used
176 if (account.vchPubKey.IsValid())
178 CScript scriptPubKey;
179 scriptPubKey.SetDestination(account.vchPubKey.GetID());
180 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin();
181 it != pwalletMain->mapWallet.end() && account.vchPubKey.IsValid();
184 const CWalletTx& wtx = (*it).second;
185 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
186 if (txout.scriptPubKey == scriptPubKey)
191 // Generate a new key
192 if (!account.vchPubKey.IsValid() || bForceNew || bKeyUsed)
194 if (!pwalletMain->GetKeyFromPool(account.vchPubKey, false))
195 throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
197 pwalletMain->SetAddressBookName(account.vchPubKey.GetID(), strAccount);
198 walletdb.WriteAccount(strAccount, account);
201 return CBitcoinAddress(account.vchPubKey.GetID());
204 Value getaccountaddress(const Array& params, bool fHelp)
206 if (fHelp || params.size() != 1)
208 "getaccountaddress <account>\n"
209 "Returns the current NovaCoin address for receiving payments to this account.");
211 // Parse the account first so we don't generate a key if there's an error
212 string strAccount = AccountFromValue(params[0]);
216 ret = GetAccountAddress(strAccount).ToString();
223 Value setaccount(const Array& params, bool fHelp)
225 if (fHelp || params.size() < 1 || params.size() > 2)
227 "setaccount <novacoinaddress> <account>\n"
228 "Sets the account associated with the given address.");
230 CBitcoinAddress address(params[0].get_str());
231 if (!address.IsValid())
232 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
236 if (params.size() > 1)
237 strAccount = AccountFromValue(params[1]);
239 // Detect when changing the account of an address that is the 'unused current key' of another account:
240 if (pwalletMain->mapAddressBook.count(address.Get()))
242 string strOldAccount = pwalletMain->mapAddressBook[address.Get()];
243 if (address == GetAccountAddress(strOldAccount))
244 GetAccountAddress(strOldAccount, true);
247 pwalletMain->SetAddressBookName(address.Get(), strAccount);
253 Value getaccount(const Array& params, bool fHelp)
255 if (fHelp || params.size() != 1)
257 "getaccount <novacoinaddress>\n"
258 "Returns the account associated with the given address.");
260 CBitcoinAddress address(params[0].get_str());
261 if (!address.IsValid())
262 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
265 map<CTxDestination, string>::iterator mi = pwalletMain->mapAddressBook.find(address.Get());
266 if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.empty())
267 strAccount = (*mi).second;
272 Value getaddressesbyaccount(const Array& params, bool fHelp)
274 if (fHelp || params.size() != 1)
276 "getaddressesbyaccount <account>\n"
277 "Returns the list of addresses for the given account.");
279 string strAccount = AccountFromValue(params[0]);
281 // Find all addresses that have the given account
283 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
285 const CBitcoinAddress& address = item.first;
286 const string& strName = item.second;
287 if (strName == strAccount)
288 ret.push_back(address.ToString());
293 Value sendtoaddress(const Array& params, bool fHelp)
295 if (fHelp || params.size() < 2 || params.size() > 4)
297 "sendtoaddress <novacoinaddress> <amount> [comment] [comment-to]\n"
298 "<amount> is a real and is rounded to the nearest 0.000001"
299 + HelpRequiringPassphrase());
301 CBitcoinAddress address(params[0].get_str());
302 if (!address.IsValid())
303 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
306 int64 nAmount = AmountFromValue(params[1]);
308 if (nAmount < MIN_TXOUT_AMOUNT)
309 throw JSONRPCError(-101, "Send amount too small");
313 if (params.size() > 2 && params[2].type() != null_type && !params[2].get_str().empty())
314 wtx.mapValue["comment"] = params[2].get_str();
315 if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
316 wtx.mapValue["to"] = params[3].get_str();
318 if (pwalletMain->IsLocked())
319 throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first.");
321 string strError = pwalletMain->SendMoneyToDestination(address.Get(), nAmount, wtx);
323 throw JSONRPCError(RPC_WALLET_ERROR, strError);
325 return wtx.GetHash().GetHex();
328 Value listaddressgroupings(const Array& params, bool fHelp)
332 "listaddressgroupings\n"
333 "Lists groups of addresses which have had their common ownership\n"
334 "made public by common use as inputs or as the resulting change\n"
335 "in past transactions");
338 map<CTxDestination, int64> balances = pwalletMain->GetAddressBalances();
339 BOOST_FOREACH(set<CTxDestination> grouping, pwalletMain->GetAddressGroupings())
342 BOOST_FOREACH(CTxDestination address, grouping)
345 addressInfo.push_back(CBitcoinAddress(address).ToString());
346 addressInfo.push_back(ValueFromAmount(balances[address]));
348 LOCK(pwalletMain->cs_wallet);
349 if (pwalletMain->mapAddressBook.find(CBitcoinAddress(address).Get()) != pwalletMain->mapAddressBook.end())
350 addressInfo.push_back(pwalletMain->mapAddressBook.find(CBitcoinAddress(address).Get())->second);
352 jsonGrouping.push_back(addressInfo);
354 jsonGroupings.push_back(jsonGrouping);
356 return jsonGroupings;
359 Value signmessage(const Array& params, bool fHelp)
361 if (fHelp || params.size() != 2)
363 "signmessage <novacoinaddress> <message>\n"
364 "Sign a message with the private key of an address");
366 EnsureWalletIsUnlocked();
368 string strAddress = params[0].get_str();
369 string strMessage = params[1].get_str();
371 CBitcoinAddress addr(strAddress);
373 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
376 if (!addr.GetKeyID(keyID))
377 throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
380 if (!pwalletMain->GetKey(keyID, key))
381 throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available");
383 CDataStream ss(SER_GETHASH, 0);
384 ss << strMessageMagic;
387 vector<unsigned char> vchSig;
388 if (!key.SignCompact(Hash(ss.begin(), ss.end()), vchSig))
389 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed");
391 return EncodeBase64(&vchSig[0], vchSig.size());
394 Value verifymessage(const Array& params, bool fHelp)
396 if (fHelp || params.size() != 3)
398 "verifymessage <novacoinaddress> <signature> <message>\n"
399 "Verify a signed message");
401 string strAddress = params[0].get_str();
402 string strSign = params[1].get_str();
403 string strMessage = params[2].get_str();
405 CBitcoinAddress addr(strAddress);
407 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
410 if (!addr.GetKeyID(keyID))
411 throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
413 bool fInvalid = false;
414 vector<unsigned char> vchSig = DecodeBase64(strSign.c_str(), &fInvalid);
417 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Malformed base64 encoding");
419 CDataStream ss(SER_GETHASH, 0);
420 ss << strMessageMagic;
424 if (!key.SetCompactSignature(Hash(ss.begin(), ss.end()), vchSig))
427 return (key.GetPubKey().GetID() == keyID);
431 Value getreceivedbyaddress(const Array& params, bool fHelp)
433 if (fHelp || params.size() < 1 || params.size() > 2)
435 "getreceivedbyaddress <novacoinaddress> [minconf=1]\n"
436 "Returns the total amount received by <novacoinaddress> in transactions with at least [minconf] confirmations.");
439 CBitcoinAddress address = CBitcoinAddress(params[0].get_str());
440 CScript scriptPubKey;
441 if (!address.IsValid())
442 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
443 scriptPubKey.SetDestination(address.Get());
444 if (!IsMine(*pwalletMain,scriptPubKey))
447 // Minimum confirmations
449 if (params.size() > 1)
450 nMinDepth = params[1].get_int();
454 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
456 const CWalletTx& wtx = (*it).second;
457 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
460 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
461 if (txout.scriptPubKey == scriptPubKey)
462 if (wtx.GetDepthInMainChain() >= nMinDepth)
463 nAmount += txout.nValue;
466 return ValueFromAmount(nAmount);
470 void GetAccountAddresses(string strAccount, set<CTxDestination>& setAddress)
472 BOOST_FOREACH(const PAIRTYPE(CTxDestination, string)& item, pwalletMain->mapAddressBook)
474 const CTxDestination& address = item.first;
475 const string& strName = item.second;
476 if (strName == strAccount)
477 setAddress.insert(address);
481 Value getreceivedbyaccount(const Array& params, bool fHelp)
483 if (fHelp || params.size() < 1 || params.size() > 2)
485 "getreceivedbyaccount <account> [minconf=1]\n"
486 "Returns the total amount received by addresses with <account> in transactions with at least [minconf] confirmations.");
488 // Minimum confirmations
490 if (params.size() > 1)
491 nMinDepth = params[1].get_int();
493 // Get the set of pub keys assigned to account
494 string strAccount = AccountFromValue(params[0]);
495 set<CTxDestination> setAddress;
496 GetAccountAddresses(strAccount, setAddress);
500 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
502 const CWalletTx& wtx = (*it).second;
503 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
506 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
508 CTxDestination address;
509 if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*pwalletMain, address) && setAddress.count(address))
510 if (wtx.GetDepthInMainChain() >= nMinDepth)
511 nAmount += txout.nValue;
515 return (double)nAmount / (double)COIN;
519 int64 GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth)
523 // Tally wallet transactions
524 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
526 const CWalletTx& wtx = (*it).second;
530 int64 nGenerated, nReceived, nSent, nFee;
531 wtx.GetAccountAmounts(strAccount, nGenerated, nReceived, nSent, nFee);
533 if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth)
534 nBalance += nReceived;
535 nBalance += nGenerated - nSent - nFee;
538 // Tally internal accounting entries
539 nBalance += walletdb.GetAccountCreditDebit(strAccount);
544 int64 GetAccountBalance(const string& strAccount, int nMinDepth)
546 CWalletDB walletdb(pwalletMain->strWalletFile);
547 return GetAccountBalance(walletdb, strAccount, nMinDepth);
551 Value getbalance(const Array& params, bool fHelp)
553 if (fHelp || params.size() > 2)
555 "getbalance [account] [minconf=1]\n"
556 "If [account] is not specified, returns the server's total available balance.\n"
557 "If [account] is specified, returns the balance in the account.");
559 if (params.size() == 0)
560 return ValueFromAmount(pwalletMain->GetBalance());
563 if (params.size() > 1)
564 nMinDepth = params[1].get_int();
566 if (params[0].get_str() == "*") {
567 // Calculate total balance a different way from GetBalance()
568 // (GetBalance() sums up all unspent TxOuts)
569 // getbalance and getbalance '*' 0 should return the same number.
571 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
573 const CWalletTx& wtx = (*it).second;
574 if (!wtx.IsTrusted() || wtx.GetBlocksToMaturity() > 0)
577 int64 allGeneratedImmature, allGeneratedMature, allFee;
578 allGeneratedImmature = allGeneratedMature = allFee = 0;
580 string strSentAccount;
581 list<pair<CTxDestination, int64> > listReceived;
582 list<pair<CTxDestination, int64> > listSent;
583 wtx.GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount);
584 if (wtx.GetDepthInMainChain() >= nMinDepth)
586 BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64)& r, listReceived)
587 nBalance += r.second;
589 BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64)& r, listSent)
590 nBalance -= r.second;
592 nBalance += allGeneratedMature;
594 return ValueFromAmount(nBalance);
597 string strAccount = AccountFromValue(params[0]);
599 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
601 return ValueFromAmount(nBalance);
604 Value getunconfirmedbalance(const Array ¶ms, bool fHelp)
606 if (fHelp || params.size() > 0)
608 "getunconfirmedbalance\n"
609 "Returns the server's total unconfirmed balance\n");
610 return ValueFromAmount(pwalletMain->GetUnconfirmedBalance());
613 Value movecmd(const Array& params, bool fHelp)
615 if (fHelp || params.size() < 3 || params.size() > 5)
617 "move <fromaccount> <toaccount> <amount> [minconf=1] [comment]\n"
618 "Move from one account in your wallet to another.");
620 string strFrom = AccountFromValue(params[0]);
621 string strTo = AccountFromValue(params[1]);
622 int64 nAmount = AmountFromValue(params[2]);
624 if (nAmount < MIN_TXOUT_AMOUNT)
625 throw JSONRPCError(-101, "Send amount too small");
627 if (params.size() > 3)
628 // unused parameter, used to be nMinDepth, keep type-checking it though
629 (void)params[3].get_int();
631 if (params.size() > 4)
632 strComment = params[4].get_str();
634 CWalletDB walletdb(pwalletMain->strWalletFile);
635 if (!walletdb.TxnBegin())
636 throw JSONRPCError(RPC_DATABASE_ERROR, "database error");
638 int64 nNow = GetAdjustedTime();
641 CAccountingEntry debit;
642 debit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb);
643 debit.strAccount = strFrom;
644 debit.nCreditDebit = -nAmount;
646 debit.strOtherAccount = strTo;
647 debit.strComment = strComment;
648 walletdb.WriteAccountingEntry(debit);
651 CAccountingEntry credit;
652 credit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb);
653 credit.strAccount = strTo;
654 credit.nCreditDebit = nAmount;
656 credit.strOtherAccount = strFrom;
657 credit.strComment = strComment;
658 walletdb.WriteAccountingEntry(credit);
660 if (!walletdb.TxnCommit())
661 throw JSONRPCError(RPC_DATABASE_ERROR, "database error");
667 Value sendfrom(const Array& params, bool fHelp)
669 if (fHelp || params.size() < 3 || params.size() > 6)
671 "sendfrom <fromaccount> <tonovacoinaddress> <amount> [minconf=1] [comment] [comment-to]\n"
672 "<amount> is a real and is rounded to the nearest 0.000001"
673 + HelpRequiringPassphrase());
675 string strAccount = AccountFromValue(params[0]);
676 CBitcoinAddress address(params[1].get_str());
677 if (!address.IsValid())
678 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
679 int64 nAmount = AmountFromValue(params[2]);
681 if (nAmount < MIN_TXOUT_AMOUNT)
682 throw JSONRPCError(-101, "Send amount too small");
685 if (params.size() > 3)
686 nMinDepth = params[3].get_int();
689 wtx.strFromAccount = strAccount;
690 if (params.size() > 4 && params[4].type() != null_type && !params[4].get_str().empty())
691 wtx.mapValue["comment"] = params[4].get_str();
692 if (params.size() > 5 && params[5].type() != null_type && !params[5].get_str().empty())
693 wtx.mapValue["to"] = params[5].get_str();
695 EnsureWalletIsUnlocked();
698 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
699 if (nAmount > nBalance)
700 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds");
703 string strError = pwalletMain->SendMoneyToDestination(address.Get(), nAmount, wtx);
705 throw JSONRPCError(RPC_WALLET_ERROR, strError);
707 return wtx.GetHash().GetHex();
711 Value sendmany(const Array& params, bool fHelp)
713 if (fHelp || params.size() < 2 || params.size() > 4)
715 "sendmany <fromaccount> {address:amount,...} [minconf=1] [comment]\n"
716 "amounts are double-precision floating point numbers"
717 + HelpRequiringPassphrase());
719 string strAccount = AccountFromValue(params[0]);
720 Object sendTo = params[1].get_obj();
722 if (params.size() > 2)
723 nMinDepth = params[2].get_int();
726 wtx.strFromAccount = strAccount;
727 if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
728 wtx.mapValue["comment"] = params[3].get_str();
730 set<CBitcoinAddress> setAddress;
731 vector<pair<CScript, int64> > vecSend;
733 int64 totalAmount = 0;
734 BOOST_FOREACH(const Pair& s, sendTo)
736 CBitcoinAddress address(s.name_);
737 if (!address.IsValid())
738 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid NovaCoin address: ")+s.name_);
740 if (setAddress.count(address))
741 throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+s.name_);
742 setAddress.insert(address);
744 CScript scriptPubKey;
745 scriptPubKey.SetDestination(address.Get());
746 int64 nAmount = AmountFromValue(s.value_);
748 if (nAmount < MIN_TXOUT_AMOUNT)
749 throw JSONRPCError(-101, "Send amount too small");
751 totalAmount += nAmount;
753 vecSend.push_back(make_pair(scriptPubKey, nAmount));
756 EnsureWalletIsUnlocked();
759 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
760 if (totalAmount > nBalance)
761 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds");
764 CReserveKey keyChange(pwalletMain);
765 int64 nFeeRequired = 0;
766 bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired);
769 if (totalAmount + nFeeRequired > pwalletMain->GetBalance())
770 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds");
771 throw JSONRPCError(RPC_WALLET_ERROR, "Transaction creation failed");
773 if (!pwalletMain->CommitTransaction(wtx, keyChange))
774 throw JSONRPCError(RPC_WALLET_ERROR, "Transaction commit failed");
776 return wtx.GetHash().GetHex();
779 Value addmultisigaddress(const Array& params, bool fHelp)
781 if (fHelp || params.size() < 2 || params.size() > 3)
783 string msg = "addmultisigaddress <nrequired> <'[\"key\",\"key\"]'> [account]\n"
784 "Add a nrequired-to-sign multisignature address to the wallet\"\n"
785 "each key is a NovaCoin address or hex-encoded public key\n"
786 "If [account] is specified, assign address to [account].";
787 throw runtime_error(msg);
790 int nRequired = params[0].get_int();
791 const Array& keys = params[1].get_array();
793 if (params.size() > 2)
794 strAccount = AccountFromValue(params[2]);
796 // Gather public keys
798 throw runtime_error("a multisignature address must require at least one key to redeem");
799 if ((int)keys.size() < nRequired)
801 strprintf("not enough keys supplied "
802 "(got %"PRIszu" keys, but need at least %d to redeem)", keys.size(), nRequired));
803 std::vector<CKey> pubkeys;
804 pubkeys.resize(keys.size());
805 for (unsigned int i = 0; i < keys.size(); i++)
807 const std::string& ks = keys[i].get_str();
809 // Case 1: Bitcoin address and we have full public key:
810 CBitcoinAddress address(ks);
811 if (address.IsValid())
814 if (!address.GetKeyID(keyID))
816 strprintf("%s does not refer to a key",ks.c_str()));
818 if (!pwalletMain->GetPubKey(keyID, vchPubKey))
820 strprintf("no full public key for address %s",ks.c_str()));
821 if (!vchPubKey.IsValid() || !pubkeys[i].SetPubKey(vchPubKey))
822 throw runtime_error(" Invalid public key: "+ks);
825 // Case 2: hex public key
828 CPubKey vchPubKey(ParseHex(ks));
829 if (!vchPubKey.IsValid() || !pubkeys[i].SetPubKey(vchPubKey))
830 throw runtime_error(" Invalid public key: "+ks);
834 throw runtime_error(" Invalid public key: "+ks);
838 // Construct using pay-to-script-hash:
840 inner.SetMultisig(nRequired, pubkeys);
841 CScriptID innerID = inner.GetID();
842 pwalletMain->AddCScript(inner);
844 pwalletMain->SetAddressBookName(innerID, strAccount);
845 return CBitcoinAddress(innerID).ToString();
848 Value addredeemscript(const Array& params, bool fHelp)
850 if (fHelp || params.size() < 1 || params.size() > 2)
852 string msg = "addredeemscript <redeemScript> [account]\n"
853 "Add a P2SH address with a specified redeemScript to the wallet.\n"
854 "If [account] is specified, assign address to [account].";
855 throw runtime_error(msg);
859 if (params.size() > 1)
860 strAccount = AccountFromValue(params[1]);
862 // Construct using pay-to-script-hash:
863 vector<unsigned char> innerData = ParseHexV(params[0], "redeemScript");
864 CScript inner(innerData.begin(), innerData.end());
865 CScriptID innerID = inner.GetID();
866 pwalletMain->AddCScript(inner);
868 pwalletMain->SetAddressBookName(innerID, strAccount);
869 return CBitcoinAddress(innerID).ToString();
879 nConf = std::numeric_limits<int>::max();
883 Value ListReceived(const Array& params, bool fByAccounts)
885 // Minimum confirmations
887 if (params.size() > 0)
888 nMinDepth = params[0].get_int();
890 // Whether to include empty accounts
891 bool fIncludeEmpty = false;
892 if (params.size() > 1)
893 fIncludeEmpty = params[1].get_bool();
896 map<CBitcoinAddress, tallyitem> mapTally;
897 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
899 const CWalletTx& wtx = (*it).second;
901 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
904 int nDepth = wtx.GetDepthInMainChain();
905 if (nDepth < nMinDepth)
908 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
910 CTxDestination address;
911 if (!ExtractDestination(txout.scriptPubKey, address) || !IsMine(*pwalletMain, address))
914 tallyitem& item = mapTally[address];
915 item.nAmount += txout.nValue;
916 item.nConf = min(item.nConf, nDepth);
922 map<string, tallyitem> mapAccountTally;
923 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
925 const CBitcoinAddress& address = item.first;
926 const string& strAccount = item.second;
927 map<CBitcoinAddress, tallyitem>::iterator it = mapTally.find(address);
928 if (it == mapTally.end() && !fIncludeEmpty)
932 int nConf = std::numeric_limits<int>::max();
933 if (it != mapTally.end())
935 nAmount = (*it).second.nAmount;
936 nConf = (*it).second.nConf;
941 tallyitem& item = mapAccountTally[strAccount];
942 item.nAmount += nAmount;
943 item.nConf = min(item.nConf, nConf);
948 obj.push_back(Pair("address", address.ToString()));
949 obj.push_back(Pair("account", strAccount));
950 obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
951 obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf)));
958 for (map<string, tallyitem>::iterator it = mapAccountTally.begin(); it != mapAccountTally.end(); ++it)
960 int64 nAmount = (*it).second.nAmount;
961 int nConf = (*it).second.nConf;
963 obj.push_back(Pair("account", (*it).first));
964 obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
965 obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf)));
973 Value listreceivedbyaddress(const Array& params, bool fHelp)
975 if (fHelp || params.size() > 2)
977 "listreceivedbyaddress [minconf=1] [includeempty=false]\n"
978 "[minconf] is the minimum number of confirmations before payments are included.\n"
979 "[includeempty] whether to include addresses that haven't received any payments.\n"
980 "Returns an array of objects containing:\n"
981 " \"address\" : receiving address\n"
982 " \"account\" : the account of the receiving address\n"
983 " \"amount\" : total amount received by the address\n"
984 " \"confirmations\" : number of confirmations of the most recent transaction included");
986 return ListReceived(params, false);
989 Value listreceivedbyaccount(const Array& params, bool fHelp)
991 if (fHelp || params.size() > 2)
993 "listreceivedbyaccount [minconf=1] [includeempty=false]\n"
994 "[minconf] is the minimum number of confirmations before payments are included.\n"
995 "[includeempty] whether to include accounts that haven't received any payments.\n"
996 "Returns an array of objects containing:\n"
997 " \"account\" : the account of the receiving addresses\n"
998 " \"amount\" : total amount received by addresses with this account\n"
999 " \"confirmations\" : number of confirmations of the most recent transaction included");
1001 return ListReceived(params, true);
1004 void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret)
1006 int64 nGeneratedImmature, nGeneratedMature, nFee;
1007 string strSentAccount;
1008 list<pair<CTxDestination, int64> > listReceived;
1009 list<pair<CTxDestination, int64> > listSent;
1011 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount);
1013 bool fAllAccounts = (strAccount == string("*"));
1015 // Generated blocks assigned to account ""
1016 if ((nGeneratedMature+nGeneratedImmature) != 0 && (fAllAccounts || strAccount == ""))
1019 entry.push_back(Pair("account", string("")));
1020 if (nGeneratedImmature)
1022 entry.push_back(Pair("category", wtx.GetDepthInMainChain() ? "immature" : "orphan"));
1023 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedImmature)));
1027 entry.push_back(Pair("category", "generate"));
1028 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedMature)));
1031 WalletTxToJSON(wtx, entry);
1032 ret.push_back(entry);
1036 if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount))
1038 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& s, listSent)
1041 entry.push_back(Pair("account", strSentAccount));
1042 entry.push_back(Pair("address", CBitcoinAddress(s.first).ToString()));
1044 if (wtx.GetDepthInMainChain() < 0) {
1045 entry.push_back(Pair("category", "conflicted"));
1047 entry.push_back(Pair("category", "send"));
1050 entry.push_back(Pair("amount", ValueFromAmount(-s.second)));
1051 entry.push_back(Pair("fee", ValueFromAmount(-nFee)));
1053 WalletTxToJSON(wtx, entry);
1054 ret.push_back(entry);
1059 if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth)
1061 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& r, listReceived)
1064 if (pwalletMain->mapAddressBook.count(r.first))
1065 account = pwalletMain->mapAddressBook[r.first];
1066 if (fAllAccounts || (account == strAccount))
1069 entry.push_back(Pair("account", account));
1070 entry.push_back(Pair("address", CBitcoinAddress(r.first).ToString()));
1071 if (wtx.IsCoinBase())
1073 if (wtx.GetDepthInMainChain() < 1)
1074 entry.push_back(Pair("category", "orphan"));
1075 else if (wtx.GetBlocksToMaturity() > 0)
1076 entry.push_back(Pair("category", "immature"));
1078 entry.push_back(Pair("category", "generate"));
1081 if (wtx.GetDepthInMainChain() < 0) {
1082 entry.push_back(Pair("category", "conflicted"));
1084 entry.push_back(Pair("category", "receive"));
1087 entry.push_back(Pair("amount", ValueFromAmount(r.second)));
1089 WalletTxToJSON(wtx, entry);
1090 ret.push_back(entry);
1096 void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Array& ret)
1098 bool fAllAccounts = (strAccount == string("*"));
1100 if (fAllAccounts || acentry.strAccount == strAccount)
1103 entry.push_back(Pair("account", acentry.strAccount));
1104 entry.push_back(Pair("category", "move"));
1105 entry.push_back(Pair("time", (boost::int64_t)acentry.nTime));
1106 entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit)));
1107 entry.push_back(Pair("otheraccount", acentry.strOtherAccount));
1108 entry.push_back(Pair("comment", acentry.strComment));
1109 ret.push_back(entry);
1113 Value listtransactions(const Array& params, bool fHelp)
1115 if (fHelp || params.size() > 3)
1116 throw runtime_error(
1117 "listtransactions [account] [count=10] [from=0]\n"
1118 "Returns up to [count] most recent transactions skipping the first [from] transactions for account [account].");
1120 string strAccount = "*";
1121 if (params.size() > 0)
1122 strAccount = params[0].get_str();
1124 if (params.size() > 1)
1125 nCount = params[1].get_int();
1127 if (params.size() > 2)
1128 nFrom = params[2].get_int();
1131 throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count");
1133 throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative from");
1137 std::list<CAccountingEntry> acentries;
1138 CWallet::TxItems txOrdered = pwalletMain->OrderedTxItems(acentries, strAccount);
1140 // iterate backwards until we have nCount items to return:
1141 for (CWallet::TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
1143 CWalletTx *const pwtx = (*it).second.first;
1145 ListTransactions(*pwtx, strAccount, 0, true, ret);
1146 CAccountingEntry *const pacentry = (*it).second.second;
1148 AcentryToJSON(*pacentry, strAccount, ret);
1150 if ((int)ret.size() >= (nCount+nFrom)) break;
1152 // ret is newest to oldest
1154 if (nFrom > (int)ret.size())
1156 if ((nFrom + nCount) > (int)ret.size())
1157 nCount = ret.size() - nFrom;
1158 Array::iterator first = ret.begin();
1159 std::advance(first, nFrom);
1160 Array::iterator last = ret.begin();
1161 std::advance(last, nFrom+nCount);
1163 if (last != ret.end()) ret.erase(last, ret.end());
1164 if (first != ret.begin()) ret.erase(ret.begin(), first);
1166 std::reverse(ret.begin(), ret.end()); // Return oldest to newest
1171 Value listaccounts(const Array& params, bool fHelp)
1173 if (fHelp || params.size() > 1)
1174 throw runtime_error(
1175 "listaccounts [minconf=1]\n"
1176 "Returns Object that has account names as keys, account balances as values.");
1179 if (params.size() > 0)
1180 nMinDepth = params[0].get_int();
1182 map<string, int64> mapAccountBalances;
1183 BOOST_FOREACH(const PAIRTYPE(CTxDestination, string)& entry, pwalletMain->mapAddressBook) {
1184 if (IsMine(*pwalletMain, entry.first)) // This address belongs to me
1185 mapAccountBalances[entry.second] = 0;
1188 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
1190 const CWalletTx& wtx = (*it).second;
1191 int64 nGeneratedImmature, nGeneratedMature, nFee;
1192 string strSentAccount;
1193 list<pair<CTxDestination, int64> > listReceived;
1194 list<pair<CTxDestination, int64> > listSent;
1195 if (wtx.GetBlocksToMaturity() > 0)
1197 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount);
1198 mapAccountBalances[strSentAccount] -= nFee;
1199 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& s, listSent)
1200 mapAccountBalances[strSentAccount] -= s.second;
1201 if (wtx.GetDepthInMainChain() >= nMinDepth)
1203 mapAccountBalances[""] += nGeneratedMature;
1204 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& r, listReceived)
1205 if (pwalletMain->mapAddressBook.count(r.first))
1206 mapAccountBalances[pwalletMain->mapAddressBook[r.first]] += r.second;
1208 mapAccountBalances[""] += r.second;
1212 list<CAccountingEntry> acentries;
1213 CWalletDB(pwalletMain->strWalletFile).ListAccountCreditDebit("*", acentries);
1214 BOOST_FOREACH(const CAccountingEntry& entry, acentries)
1215 mapAccountBalances[entry.strAccount] += entry.nCreditDebit;
1218 BOOST_FOREACH(const PAIRTYPE(string, int64)& accountBalance, mapAccountBalances) {
1219 ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second)));
1224 Value listsinceblock(const Array& params, bool fHelp)
1227 throw runtime_error(
1228 "listsinceblock [blockhash] [target-confirmations]\n"
1229 "Get all transactions in blocks since block [blockhash], or all transactions if omitted");
1231 CBlockIndex *pindex = NULL;
1232 int target_confirms = 1;
1234 if (params.size() > 0)
1236 uint256 blockId = 0;
1238 blockId.SetHex(params[0].get_str());
1239 pindex = CBlockLocator(blockId).GetBlockIndex();
1242 if (params.size() > 1)
1244 target_confirms = params[1].get_int();
1246 if (target_confirms < 1)
1247 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter");
1250 int depth = pindex ? (1 + nBestHeight - pindex->nHeight) : -1;
1254 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); it++)
1256 CWalletTx tx = (*it).second;
1258 if (depth == -1 || tx.GetDepthInMainChain() < depth)
1259 ListTransactions(tx, "*", 0, true, transactions);
1264 if (target_confirms == 1)
1266 lastblock = hashBestChain;
1270 int target_height = pindexBest->nHeight + 1 - target_confirms;
1273 for (block = pindexBest;
1274 block && block->nHeight > target_height;
1275 block = block->pprev) { }
1277 lastblock = block ? block->GetBlockHash() : 0;
1281 ret.push_back(Pair("transactions", transactions));
1282 ret.push_back(Pair("lastblock", lastblock.GetHex()));
1287 Value gettransaction(const Array& params, bool fHelp)
1289 if (fHelp || params.size() != 1)
1290 throw runtime_error(
1291 "gettransaction <txid>\n"
1292 "Get detailed information about <txid>");
1295 hash.SetHex(params[0].get_str());
1299 if (pwalletMain->mapWallet.count(hash))
1301 const CWalletTx& wtx = pwalletMain->mapWallet[hash];
1303 TxToJSON(wtx, 0, entry);
1305 int64 nCredit = wtx.GetCredit();
1306 int64 nDebit = wtx.GetDebit();
1307 int64 nNet = nCredit - nDebit;
1308 int64 nFee = (wtx.IsFromMe() ? wtx.GetValueOut() - nDebit : 0);
1310 entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee)));
1312 entry.push_back(Pair("fee", ValueFromAmount(nFee)));
1314 WalletTxToJSON(wtx, entry);
1317 ListTransactions(pwalletMain->mapWallet[hash], "*", 0, false, details);
1318 entry.push_back(Pair("details", details));
1320 CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
1322 string strHex = HexStr(ssTx.begin(), ssTx.end());
1323 entry.push_back(Pair("hex", strHex));
1328 uint256 hashBlock = 0;
1329 if (GetTransaction(hash, tx, hashBlock, true))
1331 TxToJSON(tx, 0, entry);
1333 entry.push_back(Pair("confirmations", 0));
1336 entry.push_back(Pair("blockhash", hashBlock.GetHex()));
1337 map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashBlock);
1338 if (mi != mapBlockIndex.end() && (*mi).second)
1340 CBlockIndex* pindex = (*mi).second;
1341 if (pindex->IsInMainChain())
1342 entry.push_back(Pair("confirmations", 1 + nBestHeight - pindex->nHeight));
1344 entry.push_back(Pair("confirmations", 0));
1349 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction");
1356 Value backupwallet(const Array& params, bool fHelp)
1358 if (fHelp || params.size() != 1)
1359 throw runtime_error(
1360 "backupwallet <destination>\n"
1361 "Safely copies wallet.dat to destination, which can be a directory or a path with filename.");
1363 string strDest = params[0].get_str();
1364 if (!BackupWallet(*pwalletMain, strDest))
1365 throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet backup failed!");
1371 Value keypoolrefill(const Array& params, bool fHelp)
1373 if (fHelp || params.size() > 1)
1374 throw runtime_error(
1375 "keypoolrefill [new-size]\n"
1376 "Fills the keypool."
1377 + HelpRequiringPassphrase());
1379 unsigned int nSize = max(GetArg("-keypool", 100), 0LL);
1380 if (params.size() > 0) {
1381 if (params[0].get_int() < 0)
1382 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected valid size");
1383 nSize = (unsigned int) params[0].get_int();
1386 EnsureWalletIsUnlocked();
1388 pwalletMain->TopUpKeyPool(nSize);
1390 if (pwalletMain->GetKeyPoolSize() < nSize)
1391 throw JSONRPCError(RPC_WALLET_ERROR, "Error refreshing keypool.");
1397 void ThreadTopUpKeyPool(void* parg)
1399 // Make this thread recognisable as the key-topping-up thread
1400 RenameThread("bitcoin-key-top");
1402 pwalletMain->TopUpKeyPool();
1405 void ThreadCleanWalletPassphrase(void* parg)
1407 // Make this thread recognisable as the wallet relocking thread
1408 RenameThread("bitcoin-lock-wa");
1410 int64 nMyWakeTime = GetTimeMillis() + *((int64*)parg) * 1000;
1412 ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime);
1414 if (nWalletUnlockTime == 0)
1416 nWalletUnlockTime = nMyWakeTime;
1420 if (nWalletUnlockTime==0)
1422 int64 nToSleep = nWalletUnlockTime - GetTimeMillis();
1426 LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime);
1428 ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime);
1432 if (nWalletUnlockTime)
1434 nWalletUnlockTime = 0;
1435 pwalletMain->Lock();
1440 if (nWalletUnlockTime < nMyWakeTime)
1441 nWalletUnlockTime = nMyWakeTime;
1444 LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime);
1446 delete (int64*)parg;
1449 Value walletpassphrase(const Array& params, bool fHelp)
1451 if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 3))
1452 throw runtime_error(
1453 "walletpassphrase <passphrase> <timeout> [mintonly]\n"
1454 "Stores the wallet decryption key in memory for <timeout> seconds.\n"
1455 "mintonly is optional true/false allowing only block minting.");
1458 if (!pwalletMain->IsCrypted())
1459 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called.");
1461 if (!pwalletMain->IsLocked())
1462 throw JSONRPCError(RPC_WALLET_ALREADY_UNLOCKED, "Error: Wallet is already unlocked, use walletlock first if need to change unlock settings.");
1463 // Note that the walletpassphrase is stored in params[0] which is not mlock()ed
1464 SecureString strWalletPass;
1465 strWalletPass.reserve(100);
1466 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1467 // Alternately, find a way to make params[0] mlock()'d to begin with.
1468 strWalletPass = params[0].get_str().c_str();
1470 if (strWalletPass.length() > 0)
1472 if (!pwalletMain->Unlock(strWalletPass))
1473 throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
1476 throw runtime_error(
1477 "walletpassphrase <passphrase> <timeout>\n"
1478 "Stores the wallet decryption key in memory for <timeout> seconds.");
1480 NewThread(ThreadTopUpKeyPool, NULL);
1481 int64* pnSleepTime = new int64(params[1].get_int64());
1482 NewThread(ThreadCleanWalletPassphrase, pnSleepTime);
1484 // ppcoin: if user OS account compromised prevent trivial sendmoney commands
1485 if (params.size() > 2)
1486 fWalletUnlockMintOnly = params[2].get_bool();
1488 fWalletUnlockMintOnly = false;
1494 Value walletpassphrasechange(const Array& params, bool fHelp)
1496 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2))
1497 throw runtime_error(
1498 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1499 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1502 if (!pwalletMain->IsCrypted())
1503 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrasechange was called.");
1505 // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string)
1506 // Alternately, find a way to make params[0] mlock()'d to begin with.
1507 SecureString strOldWalletPass;
1508 strOldWalletPass.reserve(100);
1509 strOldWalletPass = params[0].get_str().c_str();
1511 SecureString strNewWalletPass;
1512 strNewWalletPass.reserve(100);
1513 strNewWalletPass = params[1].get_str().c_str();
1515 if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1)
1516 throw runtime_error(
1517 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1518 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1520 if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass))
1521 throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
1527 Value walletlock(const Array& params, bool fHelp)
1529 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 0))
1530 throw runtime_error(
1532 "Removes the wallet encryption key from memory, locking the wallet.\n"
1533 "After calling this method, you will need to call walletpassphrase again\n"
1534 "before being able to call any methods which require the wallet to be unlocked.");
1537 if (!pwalletMain->IsCrypted())
1538 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletlock was called.");
1541 LOCK(cs_nWalletUnlockTime);
1542 pwalletMain->Lock();
1543 nWalletUnlockTime = 0;
1550 Value encryptwallet(const Array& params, bool fHelp)
1552 if (!pwalletMain->IsCrypted() && (fHelp || params.size() != 1))
1553 throw runtime_error(
1554 "encryptwallet <passphrase>\n"
1555 "Encrypts the wallet with <passphrase>.");
1558 if (pwalletMain->IsCrypted())
1559 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an encrypted wallet, but encryptwallet was called.");
1561 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1562 // Alternately, find a way to make params[0] mlock()'d to begin with.
1563 SecureString strWalletPass;
1564 strWalletPass.reserve(100);
1565 strWalletPass = params[0].get_str().c_str();
1567 if (strWalletPass.length() < 1)
1568 throw runtime_error(
1569 "encryptwallet <passphrase>\n"
1570 "Encrypts the wallet with <passphrase>.");
1572 if (!pwalletMain->EncryptWallet(strWalletPass))
1573 throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: Failed to encrypt the wallet.");
1575 // BDB seems to have a bad habit of writing old data into
1576 // slack space in .dat files; that is bad if the old data is
1577 // unencrypted private keys. So:
1579 return "wallet encrypted; NovaCoin server stopping, restart to run with encrypted wallet. The keypool has been flushed, you need to make a new backup.";
1582 class DescribeAddressVisitor : public boost::static_visitor<Object>
1585 Object operator()(const CNoDestination &dest) const { return Object(); }
1587 Object operator()(const CKeyID &keyID) const {
1590 pwalletMain->GetPubKey(keyID, vchPubKey);
1591 obj.push_back(Pair("isscript", false));
1592 obj.push_back(Pair("pubkey", HexStr(vchPubKey.Raw())));
1593 obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed()));
1597 Object operator()(const CScriptID &scriptID) const {
1599 obj.push_back(Pair("isscript", true));
1601 pwalletMain->GetCScript(scriptID, subscript);
1602 std::vector<CTxDestination> addresses;
1603 txnouttype whichType;
1605 ExtractDestinations(subscript, whichType, addresses, nRequired);
1606 obj.push_back(Pair("script", GetTxnOutputType(whichType)));
1607 obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end())));
1609 BOOST_FOREACH(const CTxDestination& addr, addresses)
1610 a.push_back(CBitcoinAddress(addr).ToString());
1611 obj.push_back(Pair("addresses", a));
1612 if (whichType == TX_MULTISIG)
1613 obj.push_back(Pair("sigsrequired", nRequired));
1618 Value validateaddress(const Array& params, bool fHelp)
1620 if (fHelp || params.size() != 1)
1621 throw runtime_error(
1622 "validateaddress <novacoinaddress>\n"
1623 "Return information about <novacoinaddress>.");
1625 CBitcoinAddress address(params[0].get_str());
1626 bool isValid = address.IsValid();
1629 ret.push_back(Pair("isvalid", isValid));
1632 CTxDestination dest = address.Get();
1633 string currentAddress = address.ToString();
1634 ret.push_back(Pair("address", currentAddress));
1635 bool fMine = IsMine(*pwalletMain, dest);
1636 ret.push_back(Pair("ismine", fMine));
1638 Object detail = boost::apply_visitor(DescribeAddressVisitor(), dest);
1639 ret.insert(ret.end(), detail.begin(), detail.end());
1641 if (pwalletMain->mapAddressBook.count(dest))
1642 ret.push_back(Pair("account", pwalletMain->mapAddressBook[dest]));
1647 Value validatepubkey(const Array& params, bool fHelp)
1649 if (fHelp || !params.size() || params.size() > 2)
1650 throw runtime_error(
1651 "validatepubkey <novacoinpubkey>\n"
1652 "Return information about <novacoinpubkey>.");
1654 std::vector<unsigned char> vchPubKey = ParseHex(params[0].get_str());
1655 CPubKey pubKey(vchPubKey);
1657 bool isValid = pubKey.IsValid();
1658 bool isCompressed = pubKey.IsCompressed();
1659 CKeyID keyID = pubKey.GetID();
1661 CBitcoinAddress address;
1665 ret.push_back(Pair("isvalid", isValid));
1668 CTxDestination dest = address.Get();
1669 string currentAddress = address.ToString();
1670 ret.push_back(Pair("address", currentAddress));
1671 bool fMine = IsMine(*pwalletMain, dest);
1672 ret.push_back(Pair("ismine", fMine));
1673 ret.push_back(Pair("iscompressed", isCompressed));
1675 Object detail = boost::apply_visitor(DescribeAddressVisitor(), dest);
1676 ret.insert(ret.end(), detail.begin(), detail.end());
1678 if (pwalletMain->mapAddressBook.count(dest))
1679 ret.push_back(Pair("account", pwalletMain->mapAddressBook[dest]));
1684 // reserve balance from being staked for network protection
1685 Value reservebalance(const Array& params, bool fHelp)
1687 if (fHelp || params.size() > 2)
1688 throw runtime_error(
1689 "reservebalance [<reserve> [amount]]\n"
1690 "<reserve> is true or false to turn balance reserve on or off.\n"
1691 "<amount> is a real and rounded to cent.\n"
1692 "Set reserve amount not participating in network protection.\n"
1693 "If no parameters provided current setting is printed.\n");
1695 if (params.size() > 0)
1697 bool fReserve = params[0].get_bool();
1700 if (params.size() == 1)
1701 throw runtime_error("must provide amount to reserve balance.\n");
1702 int64 nAmount = AmountFromValue(params[1]);
1703 nAmount = (nAmount / CENT) * CENT; // round to cent
1705 throw runtime_error("amount cannot be negative.\n");
1706 mapArgs["-reservebalance"] = FormatMoney(nAmount).c_str();
1710 if (params.size() > 1)
1711 throw runtime_error("cannot specify amount to turn off reserve.\n");
1712 mapArgs["-reservebalance"] = "0";
1717 int64 nReserveBalance = 0;
1718 if (mapArgs.count("-reservebalance") && !ParseMoney(mapArgs["-reservebalance"], nReserveBalance))
1719 throw runtime_error("invalid reserve balance amount\n");
1720 result.push_back(Pair("reserve", (nReserveBalance > 0)));
1721 result.push_back(Pair("amount", ValueFromAmount(nReserveBalance)));
1726 // check wallet integrity
1727 Value checkwallet(const Array& params, bool fHelp)
1729 if (fHelp || params.size() > 0)
1730 throw runtime_error(
1732 "Check wallet for integrity.\n");
1735 int64 nBalanceInQuestion;
1736 pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion, true);
1738 if (nMismatchSpent == 0)
1739 result.push_back(Pair("wallet check passed", true));
1742 result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1743 result.push_back(Pair("amount in question", ValueFromAmount(nBalanceInQuestion)));
1750 Value repairwallet(const Array& params, bool fHelp)
1752 if (fHelp || params.size() > 0)
1753 throw runtime_error(
1755 "Repair wallet if checkwallet reports any problem.\n");
1758 int64 nBalanceInQuestion;
1759 pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion);
1761 if (nMismatchSpent == 0)
1762 result.push_back(Pair("wallet check passed", true));
1765 result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1766 result.push_back(Pair("amount affected by repair", ValueFromAmount(nBalanceInQuestion)));
1771 // resend unconfirmed wallet transactions
1772 Value resendtx(const Array& params, bool fHelp)
1774 if (fHelp || params.size() > 1)
1775 throw runtime_error(
1777 "Re-send unconfirmed transactions.\n"
1780 ResendWalletTransactions();
1785 // make a public-private key pair
1786 Value makekeypair(const Array& params, bool fHelp)
1788 if (fHelp || params.size() > 1)
1789 throw runtime_error(
1790 "makekeypair [prefix]\n"
1791 "Make a public/private key pair.\n"
1792 "[prefix] is optional preferred prefix for the public key.\n");
1794 string strPrefix = "";
1795 if (params.size() > 0)
1796 strPrefix = params[0].get_str();
1799 key.MakeNewKey(false);
1801 CPrivKey vchPrivKey = key.GetPrivKey();
1803 result.push_back(Pair("PrivateKey", HexStr<CPrivKey::iterator>(vchPrivKey.begin(), vchPrivKey.end())));
1804 result.push_back(Pair("PublicKey", HexStr(key.GetPubKey().Raw())));