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("newmint", ValueFromAmount(pwalletMain->GetNewMint())));
78 obj.push_back(Pair("stake", ValueFromAmount(pwalletMain->GetStake())));
79 obj.push_back(Pair("blocks", (int)nBestHeight));
80 obj.push_back(Pair("timeoffset", (boost::int64_t)GetTimeOffset()));
81 obj.push_back(Pair("moneysupply", ValueFromAmount(pindexBest->nMoneySupply)));
82 obj.push_back(Pair("connections", (int)vNodes.size()));
83 obj.push_back(Pair("proxy", (proxy.first.IsValid() ? proxy.first.ToStringIPPort() : string())));
84 obj.push_back(Pair("ip", addrSeenByPeer.ToStringIP()));
86 diff.push_back(Pair("proof-of-work", GetDifficulty()));
87 diff.push_back(Pair("proof-of-stake", GetDifficulty(GetLastBlockIndex(pindexBest, true))));
88 obj.push_back(Pair("difficulty", diff));
90 obj.push_back(Pair("testnet", fTestNet));
91 obj.push_back(Pair("keypoololdest", (boost::int64_t)pwalletMain->GetOldestKeyPoolTime()));
92 obj.push_back(Pair("keypoolsize", (int)pwalletMain->GetKeyPoolSize()));
93 obj.push_back(Pair("paytxfee", ValueFromAmount(nTransactionFee)));
94 obj.push_back(Pair("mininput", ValueFromAmount(nMinimumInputValue)));
95 if (pwalletMain->IsCrypted())
96 obj.push_back(Pair("unlocked_until", (boost::int64_t)nWalletUnlockTime / 1000));
97 obj.push_back(Pair("errors", GetWarnings("statusbar")));
102 Value getnewpubkey(const Array& params, bool fHelp)
104 if (fHelp || params.size() > 1)
106 "getnewpubkey [account]\n"
107 "Returns new public key for coinbase generation.");
109 // Parse the account first so we don't generate a key if there's an error
111 if (params.size() > 0)
112 strAccount = AccountFromValue(params[0]);
114 if (!pwalletMain->IsLocked())
115 pwalletMain->TopUpKeyPool();
117 // Generate a new key that is added to wallet
119 if (!pwalletMain->GetKeyFromPool(newKey, false))
120 throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
121 CKeyID keyID = newKey.GetID();
123 pwalletMain->SetAddressBookName(keyID, strAccount);
124 vector<unsigned char> vchPubKey = newKey.Raw();
126 return HexStr(vchPubKey.begin(), vchPubKey.end());
130 Value getnewaddress(const Array& params, bool fHelp)
132 if (fHelp || params.size() > 1)
134 "getnewaddress [account]\n"
135 "Returns a new NovaCoin address for receiving payments. "
136 "If [account] is specified (recommended), it is added to the address book "
137 "so payments received with the address will be credited to [account].");
139 // Parse the account first so we don't generate a key if there's an error
141 if (params.size() > 0)
142 strAccount = AccountFromValue(params[0]);
144 if (!pwalletMain->IsLocked())
145 pwalletMain->TopUpKeyPool();
147 // Generate a new key that is added to wallet
149 if (!pwalletMain->GetKeyFromPool(newKey, false))
150 throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
151 CKeyID keyID = newKey.GetID();
153 pwalletMain->SetAddressBookName(keyID, strAccount);
155 return CBitcoinAddress(keyID).ToString();
159 CBitcoinAddress GetAccountAddress(string strAccount, bool bForceNew=false)
161 CWalletDB walletdb(pwalletMain->strWalletFile);
164 walletdb.ReadAccount(strAccount, account);
166 bool bKeyUsed = false;
168 // Check if the current key has been used
169 if (account.vchPubKey.IsValid())
171 CScript scriptPubKey;
172 scriptPubKey.SetDestination(account.vchPubKey.GetID());
173 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin();
174 it != pwalletMain->mapWallet.end() && account.vchPubKey.IsValid();
177 const CWalletTx& wtx = (*it).second;
178 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
179 if (txout.scriptPubKey == scriptPubKey)
184 // Generate a new key
185 if (!account.vchPubKey.IsValid() || bForceNew || bKeyUsed)
187 if (!pwalletMain->GetKeyFromPool(account.vchPubKey, false))
188 throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
190 pwalletMain->SetAddressBookName(account.vchPubKey.GetID(), strAccount);
191 walletdb.WriteAccount(strAccount, account);
194 return CBitcoinAddress(account.vchPubKey.GetID());
197 Value getaccountaddress(const Array& params, bool fHelp)
199 if (fHelp || params.size() != 1)
201 "getaccountaddress <account>\n"
202 "Returns the current NovaCoin address for receiving payments to this account.");
204 // Parse the account first so we don't generate a key if there's an error
205 string strAccount = AccountFromValue(params[0]);
209 ret = GetAccountAddress(strAccount).ToString();
216 Value setaccount(const Array& params, bool fHelp)
218 if (fHelp || params.size() < 1 || params.size() > 2)
220 "setaccount <novacoinaddress> <account>\n"
221 "Sets the account associated with the given address.");
223 CBitcoinAddress address(params[0].get_str());
224 if (!address.IsValid())
225 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
229 if (params.size() > 1)
230 strAccount = AccountFromValue(params[1]);
232 // Detect when changing the account of an address that is the 'unused current key' of another account:
233 if (pwalletMain->mapAddressBook.count(address.Get()))
235 string strOldAccount = pwalletMain->mapAddressBook[address.Get()];
236 if (address == GetAccountAddress(strOldAccount))
237 GetAccountAddress(strOldAccount, true);
240 pwalletMain->SetAddressBookName(address.Get(), strAccount);
246 Value getaccount(const Array& params, bool fHelp)
248 if (fHelp || params.size() != 1)
250 "getaccount <novacoinaddress>\n"
251 "Returns the account associated with the given address.");
253 CBitcoinAddress address(params[0].get_str());
254 if (!address.IsValid())
255 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
258 map<CTxDestination, string>::iterator mi = pwalletMain->mapAddressBook.find(address.Get());
259 if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.empty())
260 strAccount = (*mi).second;
265 Value getaddressesbyaccount(const Array& params, bool fHelp)
267 if (fHelp || params.size() != 1)
269 "getaddressesbyaccount <account>\n"
270 "Returns the list of addresses for the given account.");
272 string strAccount = AccountFromValue(params[0]);
274 // Find all addresses that have the given account
276 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
278 const CBitcoinAddress& address = item.first;
279 const string& strName = item.second;
280 if (strName == strAccount)
281 ret.push_back(address.ToString());
286 Value sendtoaddress(const Array& params, bool fHelp)
288 if (fHelp || params.size() < 2 || params.size() > 4)
290 "sendtoaddress <novacoinaddress> <amount> [comment] [comment-to]\n"
291 "<amount> is a real and is rounded to the nearest 0.000001"
292 + HelpRequiringPassphrase());
294 CBitcoinAddress address(params[0].get_str());
295 if (!address.IsValid())
296 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
299 int64 nAmount = AmountFromValue(params[1]);
301 if (nAmount < MIN_TXOUT_AMOUNT)
302 throw JSONRPCError(-101, "Send amount too small");
306 if (params.size() > 2 && params[2].type() != null_type && !params[2].get_str().empty())
307 wtx.mapValue["comment"] = params[2].get_str();
308 if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
309 wtx.mapValue["to"] = params[3].get_str();
311 if (pwalletMain->IsLocked())
312 throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first.");
314 string strError = pwalletMain->SendMoneyToDestination(address.Get(), nAmount, wtx);
316 throw JSONRPCError(RPC_WALLET_ERROR, strError);
318 return wtx.GetHash().GetHex();
321 Value listaddressgroupings(const Array& params, bool fHelp)
325 "listaddressgroupings\n"
326 "Lists groups of addresses which have had their common ownership\n"
327 "made public by common use as inputs or as the resulting change\n"
328 "in past transactions");
331 map<CTxDestination, int64> balances = pwalletMain->GetAddressBalances();
332 BOOST_FOREACH(set<CTxDestination> grouping, pwalletMain->GetAddressGroupings())
335 BOOST_FOREACH(CTxDestination address, grouping)
338 addressInfo.push_back(CBitcoinAddress(address).ToString());
339 addressInfo.push_back(ValueFromAmount(balances[address]));
341 LOCK(pwalletMain->cs_wallet);
342 if (pwalletMain->mapAddressBook.find(CBitcoinAddress(address).Get()) != pwalletMain->mapAddressBook.end())
343 addressInfo.push_back(pwalletMain->mapAddressBook.find(CBitcoinAddress(address).Get())->second);
345 jsonGrouping.push_back(addressInfo);
347 jsonGroupings.push_back(jsonGrouping);
349 return jsonGroupings;
352 Value signmessage(const Array& params, bool fHelp)
354 if (fHelp || params.size() != 2)
356 "signmessage <novacoinaddress> <message>\n"
357 "Sign a message with the private key of an address");
359 EnsureWalletIsUnlocked();
361 string strAddress = params[0].get_str();
362 string strMessage = params[1].get_str();
364 CBitcoinAddress addr(strAddress);
366 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
369 if (!addr.GetKeyID(keyID))
370 throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
373 if (!pwalletMain->GetKey(keyID, key))
374 throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available");
376 CDataStream ss(SER_GETHASH, 0);
377 ss << strMessageMagic;
380 vector<unsigned char> vchSig;
381 if (!key.SignCompact(Hash(ss.begin(), ss.end()), vchSig))
382 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed");
384 return EncodeBase64(&vchSig[0], vchSig.size());
387 Value verifymessage(const Array& params, bool fHelp)
389 if (fHelp || params.size() != 3)
391 "verifymessage <novacoinaddress> <signature> <message>\n"
392 "Verify a signed message");
394 string strAddress = params[0].get_str();
395 string strSign = params[1].get_str();
396 string strMessage = params[2].get_str();
398 CBitcoinAddress addr(strAddress);
400 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
403 if (!addr.GetKeyID(keyID))
404 throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
406 bool fInvalid = false;
407 vector<unsigned char> vchSig = DecodeBase64(strSign.c_str(), &fInvalid);
410 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Malformed base64 encoding");
412 CDataStream ss(SER_GETHASH, 0);
413 ss << strMessageMagic;
417 if (!key.SetCompactSignature(Hash(ss.begin(), ss.end()), vchSig))
420 return (key.GetPubKey().GetID() == keyID);
424 Value getreceivedbyaddress(const Array& params, bool fHelp)
426 if (fHelp || params.size() < 1 || params.size() > 2)
428 "getreceivedbyaddress <novacoinaddress> [minconf=1]\n"
429 "Returns the total amount received by <novacoinaddress> in transactions with at least [minconf] confirmations.");
432 CBitcoinAddress address = CBitcoinAddress(params[0].get_str());
433 CScript scriptPubKey;
434 if (!address.IsValid())
435 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
436 scriptPubKey.SetDestination(address.Get());
437 if (!IsMine(*pwalletMain,scriptPubKey))
440 // Minimum confirmations
442 if (params.size() > 1)
443 nMinDepth = params[1].get_int();
447 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
449 const CWalletTx& wtx = (*it).second;
450 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
453 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
454 if (txout.scriptPubKey == scriptPubKey)
455 if (wtx.GetDepthInMainChain() >= nMinDepth)
456 nAmount += txout.nValue;
459 return ValueFromAmount(nAmount);
463 void GetAccountAddresses(string strAccount, set<CTxDestination>& setAddress)
465 BOOST_FOREACH(const PAIRTYPE(CTxDestination, string)& item, pwalletMain->mapAddressBook)
467 const CTxDestination& address = item.first;
468 const string& strName = item.second;
469 if (strName == strAccount)
470 setAddress.insert(address);
474 Value getreceivedbyaccount(const Array& params, bool fHelp)
476 if (fHelp || params.size() < 1 || params.size() > 2)
478 "getreceivedbyaccount <account> [minconf=1]\n"
479 "Returns the total amount received by addresses with <account> in transactions with at least [minconf] confirmations.");
481 // Minimum confirmations
483 if (params.size() > 1)
484 nMinDepth = params[1].get_int();
486 // Get the set of pub keys assigned to account
487 string strAccount = AccountFromValue(params[0]);
488 set<CTxDestination> setAddress;
489 GetAccountAddresses(strAccount, setAddress);
493 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
495 const CWalletTx& wtx = (*it).second;
496 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
499 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
501 CTxDestination address;
502 if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*pwalletMain, address) && setAddress.count(address))
503 if (wtx.GetDepthInMainChain() >= nMinDepth)
504 nAmount += txout.nValue;
508 return (double)nAmount / (double)COIN;
512 int64 GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth)
516 // Tally wallet transactions
517 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
519 const CWalletTx& wtx = (*it).second;
523 int64 nGenerated, nReceived, nSent, nFee;
524 wtx.GetAccountAmounts(strAccount, nGenerated, nReceived, nSent, nFee);
526 if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth)
527 nBalance += nReceived;
528 nBalance += nGenerated - nSent - nFee;
531 // Tally internal accounting entries
532 nBalance += walletdb.GetAccountCreditDebit(strAccount);
537 int64 GetAccountBalance(const string& strAccount, int nMinDepth)
539 CWalletDB walletdb(pwalletMain->strWalletFile);
540 return GetAccountBalance(walletdb, strAccount, nMinDepth);
544 Value getbalance(const Array& params, bool fHelp)
546 if (fHelp || params.size() > 2)
548 "getbalance [account] [minconf=1]\n"
549 "If [account] is not specified, returns the server's total available balance.\n"
550 "If [account] is specified, returns the balance in the account.");
552 if (params.size() == 0)
553 return ValueFromAmount(pwalletMain->GetBalance());
556 if (params.size() > 1)
557 nMinDepth = params[1].get_int();
559 if (params[0].get_str() == "*") {
560 // Calculate total balance a different way from GetBalance()
561 // (GetBalance() sums up all unspent TxOuts)
562 // getbalance and getbalance '*' 0 should return the same number.
564 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
566 const CWalletTx& wtx = (*it).second;
567 if (!wtx.IsTrusted())
570 int64 allGeneratedImmature, allGeneratedMature, allFee;
571 allGeneratedImmature = allGeneratedMature = allFee = 0;
573 string strSentAccount;
574 list<pair<CTxDestination, int64> > listReceived;
575 list<pair<CTxDestination, int64> > listSent;
576 wtx.GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount);
577 if (wtx.GetDepthInMainChain() >= nMinDepth)
579 BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64)& r, listReceived)
580 nBalance += r.second;
582 BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64)& r, listSent)
583 nBalance -= r.second;
585 nBalance += allGeneratedMature;
587 return ValueFromAmount(nBalance);
590 string strAccount = AccountFromValue(params[0]);
592 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
594 return ValueFromAmount(nBalance);
598 Value movecmd(const Array& params, bool fHelp)
600 if (fHelp || params.size() < 3 || params.size() > 5)
602 "move <fromaccount> <toaccount> <amount> [minconf=1] [comment]\n"
603 "Move from one account in your wallet to another.");
605 string strFrom = AccountFromValue(params[0]);
606 string strTo = AccountFromValue(params[1]);
607 int64 nAmount = AmountFromValue(params[2]);
609 if (nAmount < MIN_TXOUT_AMOUNT)
610 throw JSONRPCError(-101, "Send amount too small");
612 if (params.size() > 3)
613 // unused parameter, used to be nMinDepth, keep type-checking it though
614 (void)params[3].get_int();
616 if (params.size() > 4)
617 strComment = params[4].get_str();
619 CWalletDB walletdb(pwalletMain->strWalletFile);
620 if (!walletdb.TxnBegin())
621 throw JSONRPCError(RPC_DATABASE_ERROR, "database error");
623 int64 nNow = GetAdjustedTime();
626 CAccountingEntry debit;
627 debit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb);
628 debit.strAccount = strFrom;
629 debit.nCreditDebit = -nAmount;
631 debit.strOtherAccount = strTo;
632 debit.strComment = strComment;
633 walletdb.WriteAccountingEntry(debit);
636 CAccountingEntry credit;
637 credit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb);
638 credit.strAccount = strTo;
639 credit.nCreditDebit = nAmount;
641 credit.strOtherAccount = strFrom;
642 credit.strComment = strComment;
643 walletdb.WriteAccountingEntry(credit);
645 if (!walletdb.TxnCommit())
646 throw JSONRPCError(RPC_DATABASE_ERROR, "database error");
652 Value sendfrom(const Array& params, bool fHelp)
654 if (fHelp || params.size() < 3 || params.size() > 6)
656 "sendfrom <fromaccount> <tonovacoinaddress> <amount> [minconf=1] [comment] [comment-to]\n"
657 "<amount> is a real and is rounded to the nearest 0.000001"
658 + HelpRequiringPassphrase());
660 string strAccount = AccountFromValue(params[0]);
661 CBitcoinAddress address(params[1].get_str());
662 if (!address.IsValid())
663 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
664 int64 nAmount = AmountFromValue(params[2]);
666 if (nAmount < MIN_TXOUT_AMOUNT)
667 throw JSONRPCError(-101, "Send amount too small");
670 if (params.size() > 3)
671 nMinDepth = params[3].get_int();
674 wtx.strFromAccount = strAccount;
675 if (params.size() > 4 && params[4].type() != null_type && !params[4].get_str().empty())
676 wtx.mapValue["comment"] = params[4].get_str();
677 if (params.size() > 5 && params[5].type() != null_type && !params[5].get_str().empty())
678 wtx.mapValue["to"] = params[5].get_str();
680 EnsureWalletIsUnlocked();
683 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
684 if (nAmount > nBalance)
685 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds");
688 string strError = pwalletMain->SendMoneyToDestination(address.Get(), nAmount, wtx);
690 throw JSONRPCError(RPC_WALLET_ERROR, strError);
692 return wtx.GetHash().GetHex();
696 Value sendmany(const Array& params, bool fHelp)
698 if (fHelp || params.size() < 2 || params.size() > 4)
700 "sendmany <fromaccount> {address:amount,...} [minconf=1] [comment]\n"
701 "amounts are double-precision floating point numbers"
702 + HelpRequiringPassphrase());
704 string strAccount = AccountFromValue(params[0]);
705 Object sendTo = params[1].get_obj();
707 if (params.size() > 2)
708 nMinDepth = params[2].get_int();
711 wtx.strFromAccount = strAccount;
712 if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
713 wtx.mapValue["comment"] = params[3].get_str();
715 set<CBitcoinAddress> setAddress;
716 vector<pair<CScript, int64> > vecSend;
718 int64 totalAmount = 0;
719 BOOST_FOREACH(const Pair& s, sendTo)
721 CBitcoinAddress address(s.name_);
722 if (!address.IsValid())
723 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid NovaCoin address: ")+s.name_);
725 if (setAddress.count(address))
726 throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+s.name_);
727 setAddress.insert(address);
729 CScript scriptPubKey;
730 scriptPubKey.SetDestination(address.Get());
731 int64 nAmount = AmountFromValue(s.value_);
733 if (nAmount < MIN_TXOUT_AMOUNT)
734 throw JSONRPCError(-101, "Send amount too small");
736 totalAmount += nAmount;
738 vecSend.push_back(make_pair(scriptPubKey, nAmount));
741 EnsureWalletIsUnlocked();
744 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
745 if (totalAmount > nBalance)
746 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds");
749 CReserveKey keyChange(pwalletMain);
750 int64 nFeeRequired = 0;
751 bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired);
754 if (totalAmount + nFeeRequired > pwalletMain->GetBalance())
755 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds");
756 throw JSONRPCError(RPC_WALLET_ERROR, "Transaction creation failed");
758 if (!pwalletMain->CommitTransaction(wtx, keyChange))
759 throw JSONRPCError(RPC_WALLET_ERROR, "Transaction commit failed");
761 return wtx.GetHash().GetHex();
764 Value addmultisigaddress(const Array& params, bool fHelp)
766 if (fHelp || params.size() < 2 || params.size() > 3)
768 string msg = "addmultisigaddress <nrequired> <'[\"key\",\"key\"]'> [account]\n"
769 "Add a nrequired-to-sign multisignature address to the wallet\"\n"
770 "each key is a NovaCoin address or hex-encoded public key\n"
771 "If [account] is specified, assign address to [account].";
772 throw runtime_error(msg);
775 int nRequired = params[0].get_int();
776 const Array& keys = params[1].get_array();
778 if (params.size() > 2)
779 strAccount = AccountFromValue(params[2]);
781 // Gather public keys
783 throw runtime_error("a multisignature address must require at least one key to redeem");
784 if ((int)keys.size() < nRequired)
786 strprintf("not enough keys supplied "
787 "(got %"PRIszu" keys, but need at least %d to redeem)", keys.size(), nRequired));
788 std::vector<CKey> pubkeys;
789 pubkeys.resize(keys.size());
790 for (unsigned int i = 0; i < keys.size(); i++)
792 const std::string& ks = keys[i].get_str();
794 // Case 1: Bitcoin address and we have full public key:
795 CBitcoinAddress address(ks);
796 if (address.IsValid())
799 if (!address.GetKeyID(keyID))
801 strprintf("%s does not refer to a key",ks.c_str()));
803 if (!pwalletMain->GetPubKey(keyID, vchPubKey))
805 strprintf("no full public key for address %s",ks.c_str()));
806 if (!vchPubKey.IsValid() || !pubkeys[i].SetPubKey(vchPubKey))
807 throw runtime_error(" Invalid public key: "+ks);
810 // Case 2: hex public key
813 CPubKey vchPubKey(ParseHex(ks));
814 if (!vchPubKey.IsValid() || !pubkeys[i].SetPubKey(vchPubKey))
815 throw runtime_error(" Invalid public key: "+ks);
819 throw runtime_error(" Invalid public key: "+ks);
823 // Construct using pay-to-script-hash:
825 inner.SetMultisig(nRequired, pubkeys);
826 CScriptID innerID = inner.GetID();
827 pwalletMain->AddCScript(inner);
829 pwalletMain->SetAddressBookName(innerID, strAccount);
830 return CBitcoinAddress(innerID).ToString();
833 Value addredeemscript(const Array& params, bool fHelp)
835 if (fHelp || params.size() < 1 || params.size() > 2)
837 string msg = "addredeemscript <redeemScript> [account]\n"
838 "Add a P2SH address with a specified redeemScript to the wallet.\n"
839 "If [account] is specified, assign address to [account].";
840 throw runtime_error(msg);
844 if (params.size() > 1)
845 strAccount = AccountFromValue(params[1]);
847 // Construct using pay-to-script-hash:
848 vector<unsigned char> innerData = ParseHexV(params[0], "redeemScript");
849 CScript inner(innerData.begin(), innerData.end());
850 CScriptID innerID = inner.GetID();
851 pwalletMain->AddCScript(inner);
853 pwalletMain->SetAddressBookName(innerID, strAccount);
854 return CBitcoinAddress(innerID).ToString();
864 nConf = std::numeric_limits<int>::max();
868 Value ListReceived(const Array& params, bool fByAccounts)
870 // Minimum confirmations
872 if (params.size() > 0)
873 nMinDepth = params[0].get_int();
875 // Whether to include empty accounts
876 bool fIncludeEmpty = false;
877 if (params.size() > 1)
878 fIncludeEmpty = params[1].get_bool();
881 map<CBitcoinAddress, tallyitem> mapTally;
882 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
884 const CWalletTx& wtx = (*it).second;
886 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
889 int nDepth = wtx.GetDepthInMainChain();
890 if (nDepth < nMinDepth)
893 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
895 CTxDestination address;
896 if (!ExtractDestination(txout.scriptPubKey, address) || !IsMine(*pwalletMain, address))
899 tallyitem& item = mapTally[address];
900 item.nAmount += txout.nValue;
901 item.nConf = min(item.nConf, nDepth);
907 map<string, tallyitem> mapAccountTally;
908 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
910 const CBitcoinAddress& address = item.first;
911 const string& strAccount = item.second;
912 map<CBitcoinAddress, tallyitem>::iterator it = mapTally.find(address);
913 if (it == mapTally.end() && !fIncludeEmpty)
917 int nConf = std::numeric_limits<int>::max();
918 if (it != mapTally.end())
920 nAmount = (*it).second.nAmount;
921 nConf = (*it).second.nConf;
926 tallyitem& item = mapAccountTally[strAccount];
927 item.nAmount += nAmount;
928 item.nConf = min(item.nConf, nConf);
933 obj.push_back(Pair("address", address.ToString()));
934 obj.push_back(Pair("account", strAccount));
935 obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
936 obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf)));
943 for (map<string, tallyitem>::iterator it = mapAccountTally.begin(); it != mapAccountTally.end(); ++it)
945 int64 nAmount = (*it).second.nAmount;
946 int nConf = (*it).second.nConf;
948 obj.push_back(Pair("account", (*it).first));
949 obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
950 obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf)));
958 Value listreceivedbyaddress(const Array& params, bool fHelp)
960 if (fHelp || params.size() > 2)
962 "listreceivedbyaddress [minconf=1] [includeempty=false]\n"
963 "[minconf] is the minimum number of confirmations before payments are included.\n"
964 "[includeempty] whether to include addresses that haven't received any payments.\n"
965 "Returns an array of objects containing:\n"
966 " \"address\" : receiving address\n"
967 " \"account\" : the account of the receiving address\n"
968 " \"amount\" : total amount received by the address\n"
969 " \"confirmations\" : number of confirmations of the most recent transaction included");
971 return ListReceived(params, false);
974 Value listreceivedbyaccount(const Array& params, bool fHelp)
976 if (fHelp || params.size() > 2)
978 "listreceivedbyaccount [minconf=1] [includeempty=false]\n"
979 "[minconf] is the minimum number of confirmations before payments are included.\n"
980 "[includeempty] whether to include accounts that haven't received any payments.\n"
981 "Returns an array of objects containing:\n"
982 " \"account\" : the account of the receiving addresses\n"
983 " \"amount\" : total amount received by addresses with this account\n"
984 " \"confirmations\" : number of confirmations of the most recent transaction included");
986 return ListReceived(params, true);
989 void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret)
991 int64 nGeneratedImmature, nGeneratedMature, nFee;
992 string strSentAccount;
993 list<pair<CTxDestination, int64> > listReceived;
994 list<pair<CTxDestination, int64> > listSent;
996 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount);
998 bool fAllAccounts = (strAccount == string("*"));
1000 // Generated blocks assigned to account ""
1001 if ((nGeneratedMature+nGeneratedImmature) != 0 && (fAllAccounts || strAccount == ""))
1004 entry.push_back(Pair("account", string("")));
1005 if (nGeneratedImmature)
1007 entry.push_back(Pair("category", wtx.GetDepthInMainChain() ? "immature" : "orphan"));
1008 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedImmature)));
1012 entry.push_back(Pair("category", "generate"));
1013 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedMature)));
1016 WalletTxToJSON(wtx, entry);
1017 ret.push_back(entry);
1021 if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount))
1023 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& s, listSent)
1026 entry.push_back(Pair("account", strSentAccount));
1027 entry.push_back(Pair("address", CBitcoinAddress(s.first).ToString()));
1028 entry.push_back(Pair("category", "send"));
1029 entry.push_back(Pair("amount", ValueFromAmount(-s.second)));
1030 entry.push_back(Pair("fee", ValueFromAmount(-nFee)));
1032 WalletTxToJSON(wtx, entry);
1033 ret.push_back(entry);
1038 if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth)
1040 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& r, listReceived)
1043 if (pwalletMain->mapAddressBook.count(r.first))
1044 account = pwalletMain->mapAddressBook[r.first];
1045 if (fAllAccounts || (account == strAccount))
1048 entry.push_back(Pair("account", account));
1049 entry.push_back(Pair("address", CBitcoinAddress(r.first).ToString()));
1050 if (wtx.IsCoinBase())
1052 if (wtx.GetDepthInMainChain() < 1)
1053 entry.push_back(Pair("category", "orphan"));
1054 else if (wtx.GetBlocksToMaturity() > 0)
1055 entry.push_back(Pair("category", "immature"));
1057 entry.push_back(Pair("category", "generate"));
1060 entry.push_back(Pair("category", "receive"));
1061 entry.push_back(Pair("amount", ValueFromAmount(r.second)));
1063 WalletTxToJSON(wtx, entry);
1064 ret.push_back(entry);
1070 void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Array& ret)
1072 bool fAllAccounts = (strAccount == string("*"));
1074 if (fAllAccounts || acentry.strAccount == strAccount)
1077 entry.push_back(Pair("account", acentry.strAccount));
1078 entry.push_back(Pair("category", "move"));
1079 entry.push_back(Pair("time", (boost::int64_t)acentry.nTime));
1080 entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit)));
1081 entry.push_back(Pair("otheraccount", acentry.strOtherAccount));
1082 entry.push_back(Pair("comment", acentry.strComment));
1083 ret.push_back(entry);
1087 Value listtransactions(const Array& params, bool fHelp)
1089 if (fHelp || params.size() > 3)
1090 throw runtime_error(
1091 "listtransactions [account] [count=10] [from=0]\n"
1092 "Returns up to [count] most recent transactions skipping the first [from] transactions for account [account].");
1094 string strAccount = "*";
1095 if (params.size() > 0)
1096 strAccount = params[0].get_str();
1098 if (params.size() > 1)
1099 nCount = params[1].get_int();
1101 if (params.size() > 2)
1102 nFrom = params[2].get_int();
1105 throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count");
1107 throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative from");
1111 std::list<CAccountingEntry> acentries;
1112 CWallet::TxItems txOrdered = pwalletMain->OrderedTxItems(acentries, strAccount);
1114 // iterate backwards until we have nCount items to return:
1115 for (CWallet::TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
1117 CWalletTx *const pwtx = (*it).second.first;
1119 ListTransactions(*pwtx, strAccount, 0, true, ret);
1120 CAccountingEntry *const pacentry = (*it).second.second;
1122 AcentryToJSON(*pacentry, strAccount, ret);
1124 if ((int)ret.size() >= (nCount+nFrom)) break;
1126 // ret is newest to oldest
1128 if (nFrom > (int)ret.size())
1130 if ((nFrom + nCount) > (int)ret.size())
1131 nCount = ret.size() - nFrom;
1132 Array::iterator first = ret.begin();
1133 std::advance(first, nFrom);
1134 Array::iterator last = ret.begin();
1135 std::advance(last, nFrom+nCount);
1137 if (last != ret.end()) ret.erase(last, ret.end());
1138 if (first != ret.begin()) ret.erase(ret.begin(), first);
1140 std::reverse(ret.begin(), ret.end()); // Return oldest to newest
1145 Value listaccounts(const Array& params, bool fHelp)
1147 if (fHelp || params.size() > 1)
1148 throw runtime_error(
1149 "listaccounts [minconf=1]\n"
1150 "Returns Object that has account names as keys, account balances as values.");
1153 if (params.size() > 0)
1154 nMinDepth = params[0].get_int();
1156 map<string, int64> mapAccountBalances;
1157 BOOST_FOREACH(const PAIRTYPE(CTxDestination, string)& entry, pwalletMain->mapAddressBook) {
1158 if (IsMine(*pwalletMain, entry.first)) // This address belongs to me
1159 mapAccountBalances[entry.second] = 0;
1162 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
1164 const CWalletTx& wtx = (*it).second;
1165 int64 nGeneratedImmature, nGeneratedMature, nFee;
1166 string strSentAccount;
1167 list<pair<CTxDestination, int64> > listReceived;
1168 list<pair<CTxDestination, int64> > listSent;
1169 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount);
1170 mapAccountBalances[strSentAccount] -= nFee;
1171 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& s, listSent)
1172 mapAccountBalances[strSentAccount] -= s.second;
1173 if (wtx.GetDepthInMainChain() >= nMinDepth)
1175 mapAccountBalances[""] += nGeneratedMature;
1176 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& r, listReceived)
1177 if (pwalletMain->mapAddressBook.count(r.first))
1178 mapAccountBalances[pwalletMain->mapAddressBook[r.first]] += r.second;
1180 mapAccountBalances[""] += r.second;
1184 list<CAccountingEntry> acentries;
1185 CWalletDB(pwalletMain->strWalletFile).ListAccountCreditDebit("*", acentries);
1186 BOOST_FOREACH(const CAccountingEntry& entry, acentries)
1187 mapAccountBalances[entry.strAccount] += entry.nCreditDebit;
1190 BOOST_FOREACH(const PAIRTYPE(string, int64)& accountBalance, mapAccountBalances) {
1191 ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second)));
1196 Value listsinceblock(const Array& params, bool fHelp)
1199 throw runtime_error(
1200 "listsinceblock [blockhash] [target-confirmations]\n"
1201 "Get all transactions in blocks since block [blockhash], or all transactions if omitted");
1203 CBlockIndex *pindex = NULL;
1204 int target_confirms = 1;
1206 if (params.size() > 0)
1208 uint256 blockId = 0;
1210 blockId.SetHex(params[0].get_str());
1211 pindex = CBlockLocator(blockId).GetBlockIndex();
1214 if (params.size() > 1)
1216 target_confirms = params[1].get_int();
1218 if (target_confirms < 1)
1219 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter");
1222 int depth = pindex ? (1 + nBestHeight - pindex->nHeight) : -1;
1226 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); it++)
1228 CWalletTx tx = (*it).second;
1230 if (depth == -1 || tx.GetDepthInMainChain() < depth)
1231 ListTransactions(tx, "*", 0, true, transactions);
1236 if (target_confirms == 1)
1238 lastblock = hashBestChain;
1242 int target_height = pindexBest->nHeight + 1 - target_confirms;
1245 for (block = pindexBest;
1246 block && block->nHeight > target_height;
1247 block = block->pprev) { }
1249 lastblock = block ? block->GetBlockHash() : 0;
1253 ret.push_back(Pair("transactions", transactions));
1254 ret.push_back(Pair("lastblock", lastblock.GetHex()));
1259 Value gettransaction(const Array& params, bool fHelp)
1261 if (fHelp || params.size() != 1)
1262 throw runtime_error(
1263 "gettransaction <txid>\n"
1264 "Get detailed information about <txid>");
1267 hash.SetHex(params[0].get_str());
1271 if (pwalletMain->mapWallet.count(hash))
1273 const CWalletTx& wtx = pwalletMain->mapWallet[hash];
1275 TxToJSON(wtx, 0, entry);
1277 int64 nCredit = wtx.GetCredit();
1278 int64 nDebit = wtx.GetDebit();
1279 int64 nNet = nCredit - nDebit;
1280 int64 nFee = (wtx.IsFromMe() ? wtx.GetValueOut() - nDebit : 0);
1282 entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee)));
1284 entry.push_back(Pair("fee", ValueFromAmount(nFee)));
1286 WalletTxToJSON(wtx, entry);
1289 ListTransactions(pwalletMain->mapWallet[hash], "*", 0, false, details);
1290 entry.push_back(Pair("details", details));
1295 uint256 hashBlock = 0;
1296 if (GetTransaction(hash, tx, hashBlock))
1298 TxToJSON(tx, 0, entry);
1300 entry.push_back(Pair("confirmations", 0));
1303 entry.push_back(Pair("blockhash", hashBlock.GetHex()));
1304 map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashBlock);
1305 if (mi != mapBlockIndex.end() && (*mi).second)
1307 CBlockIndex* pindex = (*mi).second;
1308 if (pindex->IsInMainChain())
1309 entry.push_back(Pair("confirmations", 1 + nBestHeight - pindex->nHeight));
1311 entry.push_back(Pair("confirmations", 0));
1316 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction");
1323 Value backupwallet(const Array& params, bool fHelp)
1325 if (fHelp || params.size() != 1)
1326 throw runtime_error(
1327 "backupwallet <destination>\n"
1328 "Safely copies wallet.dat to destination, which can be a directory or a path with filename.");
1330 string strDest = params[0].get_str();
1331 if (!BackupWallet(*pwalletMain, strDest))
1332 throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet backup failed!");
1338 Value keypoolrefill(const Array& params, bool fHelp)
1340 if (fHelp || params.size() > 1)
1341 throw runtime_error(
1342 "keypoolrefill [new-size]\n"
1343 "Fills the keypool."
1344 + HelpRequiringPassphrase());
1346 unsigned int nSize = max(GetArg("-keypool", 100), 0LL);
1347 if (params.size() > 0) {
1348 if (params[0].get_int() < 0)
1349 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected valid size");
1350 nSize = (unsigned int) params[0].get_int();
1353 EnsureWalletIsUnlocked();
1355 pwalletMain->TopUpKeyPool(nSize);
1357 if (pwalletMain->GetKeyPoolSize() < nSize)
1358 throw JSONRPCError(RPC_WALLET_ERROR, "Error refreshing keypool.");
1364 void ThreadTopUpKeyPool(void* parg)
1366 // Make this thread recognisable as the key-topping-up thread
1367 RenameThread("bitcoin-key-top");
1369 pwalletMain->TopUpKeyPool();
1372 void ThreadCleanWalletPassphrase(void* parg)
1374 // Make this thread recognisable as the wallet relocking thread
1375 RenameThread("bitcoin-lock-wa");
1377 int64 nMyWakeTime = GetTimeMillis() + *((int64*)parg) * 1000;
1379 ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime);
1381 if (nWalletUnlockTime == 0)
1383 nWalletUnlockTime = nMyWakeTime;
1387 if (nWalletUnlockTime==0)
1389 int64 nToSleep = nWalletUnlockTime - GetTimeMillis();
1393 LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime);
1395 ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime);
1399 if (nWalletUnlockTime)
1401 nWalletUnlockTime = 0;
1402 pwalletMain->Lock();
1407 if (nWalletUnlockTime < nMyWakeTime)
1408 nWalletUnlockTime = nMyWakeTime;
1411 LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime);
1413 delete (int64*)parg;
1416 Value walletpassphrase(const Array& params, bool fHelp)
1418 if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 3))
1419 throw runtime_error(
1420 "walletpassphrase <passphrase> <timeout> [mintonly]\n"
1421 "Stores the wallet decryption key in memory for <timeout> seconds.\n"
1422 "mintonly is optional true/false allowing only block minting.");
1425 if (!pwalletMain->IsCrypted())
1426 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called.");
1428 if (!pwalletMain->IsLocked())
1429 throw JSONRPCError(RPC_WALLET_ALREADY_UNLOCKED, "Error: Wallet is already unlocked, use walletlock first if need to change unlock settings.");
1430 // Note that the walletpassphrase is stored in params[0] which is not mlock()ed
1431 SecureString strWalletPass;
1432 strWalletPass.reserve(100);
1433 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1434 // Alternately, find a way to make params[0] mlock()'d to begin with.
1435 strWalletPass = params[0].get_str().c_str();
1437 if (strWalletPass.length() > 0)
1439 if (!pwalletMain->Unlock(strWalletPass))
1440 throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
1443 throw runtime_error(
1444 "walletpassphrase <passphrase> <timeout>\n"
1445 "Stores the wallet decryption key in memory for <timeout> seconds.");
1447 NewThread(ThreadTopUpKeyPool, NULL);
1448 int64* pnSleepTime = new int64(params[1].get_int64());
1449 NewThread(ThreadCleanWalletPassphrase, pnSleepTime);
1451 // ppcoin: if user OS account compromised prevent trivial sendmoney commands
1452 if (params.size() > 2)
1453 fWalletUnlockMintOnly = params[2].get_bool();
1455 fWalletUnlockMintOnly = false;
1461 Value walletpassphrasechange(const Array& params, bool fHelp)
1463 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2))
1464 throw runtime_error(
1465 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1466 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1469 if (!pwalletMain->IsCrypted())
1470 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrasechange was called.");
1472 // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string)
1473 // Alternately, find a way to make params[0] mlock()'d to begin with.
1474 SecureString strOldWalletPass;
1475 strOldWalletPass.reserve(100);
1476 strOldWalletPass = params[0].get_str().c_str();
1478 SecureString strNewWalletPass;
1479 strNewWalletPass.reserve(100);
1480 strNewWalletPass = params[1].get_str().c_str();
1482 if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1)
1483 throw runtime_error(
1484 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1485 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1487 if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass))
1488 throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
1494 Value walletlock(const Array& params, bool fHelp)
1496 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 0))
1497 throw runtime_error(
1499 "Removes the wallet encryption key from memory, locking the wallet.\n"
1500 "After calling this method, you will need to call walletpassphrase again\n"
1501 "before being able to call any methods which require the wallet to be unlocked.");
1504 if (!pwalletMain->IsCrypted())
1505 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletlock was called.");
1508 LOCK(cs_nWalletUnlockTime);
1509 pwalletMain->Lock();
1510 nWalletUnlockTime = 0;
1517 Value encryptwallet(const Array& params, bool fHelp)
1519 if (!pwalletMain->IsCrypted() && (fHelp || params.size() != 1))
1520 throw runtime_error(
1521 "encryptwallet <passphrase>\n"
1522 "Encrypts the wallet with <passphrase>.");
1525 if (pwalletMain->IsCrypted())
1526 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an encrypted wallet, but encryptwallet was called.");
1528 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1529 // Alternately, find a way to make params[0] mlock()'d to begin with.
1530 SecureString strWalletPass;
1531 strWalletPass.reserve(100);
1532 strWalletPass = params[0].get_str().c_str();
1534 if (strWalletPass.length() < 1)
1535 throw runtime_error(
1536 "encryptwallet <passphrase>\n"
1537 "Encrypts the wallet with <passphrase>.");
1539 if (!pwalletMain->EncryptWallet(strWalletPass))
1540 throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: Failed to encrypt the wallet.");
1542 // BDB seems to have a bad habit of writing old data into
1543 // slack space in .dat files; that is bad if the old data is
1544 // unencrypted private keys. So:
1546 return "wallet encrypted; NovaCoin server stopping, restart to run with encrypted wallet. The keypool has been flushed, you need to make a new backup.";
1549 class DescribeAddressVisitor : public boost::static_visitor<Object>
1552 Object operator()(const CNoDestination &dest) const { return Object(); }
1554 Object operator()(const CKeyID &keyID) const {
1557 pwalletMain->GetPubKey(keyID, vchPubKey);
1558 obj.push_back(Pair("isscript", false));
1559 obj.push_back(Pair("pubkey", HexStr(vchPubKey.Raw())));
1560 obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed()));
1564 Object operator()(const CScriptID &scriptID) const {
1566 obj.push_back(Pair("isscript", true));
1568 pwalletMain->GetCScript(scriptID, subscript);
1569 std::vector<CTxDestination> addresses;
1570 txnouttype whichType;
1572 ExtractDestinations(subscript, whichType, addresses, nRequired);
1573 obj.push_back(Pair("script", GetTxnOutputType(whichType)));
1574 obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end())));
1576 BOOST_FOREACH(const CTxDestination& addr, addresses)
1577 a.push_back(CBitcoinAddress(addr).ToString());
1578 obj.push_back(Pair("addresses", a));
1579 if (whichType == TX_MULTISIG)
1580 obj.push_back(Pair("sigsrequired", nRequired));
1585 Value validateaddress(const Array& params, bool fHelp)
1587 if (fHelp || params.size() != 1)
1588 throw runtime_error(
1589 "validateaddress <novacoinaddress>\n"
1590 "Return information about <novacoinaddress>.");
1592 CBitcoinAddress address(params[0].get_str());
1593 bool isValid = address.IsValid();
1596 ret.push_back(Pair("isvalid", isValid));
1599 CTxDestination dest = address.Get();
1600 string currentAddress = address.ToString();
1601 ret.push_back(Pair("address", currentAddress));
1602 bool fMine = IsMine(*pwalletMain, dest);
1603 ret.push_back(Pair("ismine", fMine));
1605 Object detail = boost::apply_visitor(DescribeAddressVisitor(), dest);
1606 ret.insert(ret.end(), detail.begin(), detail.end());
1608 if (pwalletMain->mapAddressBook.count(dest))
1609 ret.push_back(Pair("account", pwalletMain->mapAddressBook[dest]));
1614 Value validatepubkey(const Array& params, bool fHelp)
1616 if (fHelp || !params.size() || params.size() > 2)
1617 throw runtime_error(
1618 "validatepubkey <novacoinpubkey>\n"
1619 "Return information about <novacoinpubkey>.");
1621 std::vector<unsigned char> vchPubKey = ParseHex(params[0].get_str());
1622 CPubKey pubKey(vchPubKey);
1624 bool isValid = pubKey.IsValid();
1625 bool isCompressed = pubKey.IsCompressed();
1626 CKeyID keyID = pubKey.GetID();
1628 CBitcoinAddress address;
1632 ret.push_back(Pair("isvalid", isValid));
1635 CTxDestination dest = address.Get();
1636 string currentAddress = address.ToString();
1637 ret.push_back(Pair("address", currentAddress));
1638 bool fMine = IsMine(*pwalletMain, dest);
1639 ret.push_back(Pair("ismine", fMine));
1640 ret.push_back(Pair("iscompressed", isCompressed));
1642 Object detail = boost::apply_visitor(DescribeAddressVisitor(), dest);
1643 ret.insert(ret.end(), detail.begin(), detail.end());
1645 if (pwalletMain->mapAddressBook.count(dest))
1646 ret.push_back(Pair("account", pwalletMain->mapAddressBook[dest]));
1651 // ppcoin: reserve balance from being staked for network protection
1652 Value reservebalance(const Array& params, bool fHelp)
1654 if (fHelp || params.size() > 2)
1655 throw runtime_error(
1656 "reservebalance [<reserve> [amount]]\n"
1657 "<reserve> is true or false to turn balance reserve on or off.\n"
1658 "<amount> is a real and rounded to cent.\n"
1659 "Set reserve amount not participating in network protection.\n"
1660 "If no parameters provided current setting is printed.\n");
1662 if (params.size() > 0)
1664 bool fReserve = params[0].get_bool();
1667 if (params.size() == 1)
1668 throw runtime_error("must provide amount to reserve balance.\n");
1669 int64 nAmount = AmountFromValue(params[1]);
1670 nAmount = (nAmount / CENT) * CENT; // round to cent
1672 throw runtime_error("amount cannot be negative.\n");
1673 mapArgs["-reservebalance"] = FormatMoney(nAmount).c_str();
1677 if (params.size() > 1)
1678 throw runtime_error("cannot specify amount to turn off reserve.\n");
1679 mapArgs["-reservebalance"] = "0";
1684 int64 nReserveBalance = 0;
1685 if (mapArgs.count("-reservebalance") && !ParseMoney(mapArgs["-reservebalance"], nReserveBalance))
1686 throw runtime_error("invalid reserve balance amount\n");
1687 result.push_back(Pair("reserve", (nReserveBalance > 0)));
1688 result.push_back(Pair("amount", ValueFromAmount(nReserveBalance)));
1693 // ppcoin: check wallet integrity
1694 Value checkwallet(const Array& params, bool fHelp)
1696 if (fHelp || params.size() > 0)
1697 throw runtime_error(
1699 "Check wallet for integrity.\n");
1702 int64 nBalanceInQuestion;
1703 pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion, true);
1705 if (nMismatchSpent == 0)
1706 result.push_back(Pair("wallet check passed", true));
1709 result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1710 result.push_back(Pair("amount in question", ValueFromAmount(nBalanceInQuestion)));
1716 // ppcoin: repair wallet
1717 Value repairwallet(const Array& params, bool fHelp)
1719 if (fHelp || params.size() > 0)
1720 throw runtime_error(
1722 "Repair wallet if checkwallet reports any problem.\n");
1725 int64 nBalanceInQuestion;
1726 pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion);
1728 if (nMismatchSpent == 0)
1729 result.push_back(Pair("wallet check passed", true));
1732 result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1733 result.push_back(Pair("amount affected by repair", ValueFromAmount(nBalanceInQuestion)));
1738 // NovaCoin: resend unconfirmed wallet transactions
1739 Value resendtx(const Array& params, bool fHelp)
1741 if (fHelp || params.size() > 1)
1742 throw runtime_error(
1744 "Re-send unconfirmed transactions.\n"
1747 ResendWalletTransactions();
1752 // ppcoin: make a public-private key pair
1753 Value makekeypair(const Array& params, bool fHelp)
1755 if (fHelp || params.size() > 1)
1756 throw runtime_error(
1757 "makekeypair [prefix]\n"
1758 "Make a public/private key pair.\n"
1759 "[prefix] is optional preferred prefix for the public key.\n");
1761 string strPrefix = "";
1762 if (params.size() > 0)
1763 strPrefix = params[0].get_str();
1766 key.MakeNewKey(false);
1768 CPrivKey vchPrivKey = key.GetPrivKey();
1770 result.push_back(Pair("PrivateKey", HexStr<CPrivKey::iterator>(vchPrivKey.begin(), vchPrivKey.end())));
1771 result.push_back(Pair("PublicKey", HexStr(key.GetPubKey().Raw())));