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 " + FormatMoney(MIN_TXOUT_AMOUNT)
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 " + FormatMoney(MIN_TXOUT_AMOUNT)
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 static void MaybePushAddress(Object & entry, const CTxDestination &dest)
991 CBitcoinAddress addr;
993 entry.push_back(Pair("address", addr.ToString()));
996 void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret)
998 int64 nGeneratedImmature, nGeneratedMature, nFee;
999 string strSentAccount;
1000 list<pair<CTxDestination, int64> > listReceived;
1001 list<pair<CTxDestination, int64> > listSent;
1003 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount);
1005 bool fAllAccounts = (strAccount == string("*"));
1007 // Generated blocks assigned to account ""
1008 if ((nGeneratedMature+nGeneratedImmature) != 0 && (fAllAccounts || strAccount == ""))
1011 entry.push_back(Pair("account", string("")));
1012 if (nGeneratedImmature)
1014 entry.push_back(Pair("category", wtx.GetDepthInMainChain() ? "immature" : "orphan"));
1015 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedImmature)));
1019 entry.push_back(Pair("category", "generate"));
1020 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedMature)));
1023 WalletTxToJSON(wtx, entry);
1024 ret.push_back(entry);
1028 if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount))
1030 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& s, listSent)
1033 entry.push_back(Pair("account", strSentAccount));
1034 MaybePushAddress(entry, s.first);
1036 if (wtx.GetDepthInMainChain() < 0) {
1037 entry.push_back(Pair("category", "conflicted"));
1039 entry.push_back(Pair("category", "send"));
1042 entry.push_back(Pair("amount", ValueFromAmount(-s.second)));
1043 entry.push_back(Pair("fee", ValueFromAmount(-nFee)));
1045 WalletTxToJSON(wtx, entry);
1046 ret.push_back(entry);
1051 if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth)
1053 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& r, listReceived)
1056 if (pwalletMain->mapAddressBook.count(r.first))
1057 account = pwalletMain->mapAddressBook[r.first];
1058 if (fAllAccounts || (account == strAccount))
1061 entry.push_back(Pair("account", account));
1062 MaybePushAddress(entry, r.first);
1063 if (wtx.IsCoinBase())
1065 if (wtx.GetDepthInMainChain() < 1)
1066 entry.push_back(Pair("category", "orphan"));
1067 else if (wtx.GetBlocksToMaturity() > 0)
1068 entry.push_back(Pair("category", "immature"));
1070 entry.push_back(Pair("category", "generate"));
1073 entry.push_back(Pair("category", "receive"));
1074 entry.push_back(Pair("amount", ValueFromAmount(r.second)));
1076 WalletTxToJSON(wtx, entry);
1077 ret.push_back(entry);
1083 void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Array& ret)
1085 bool fAllAccounts = (strAccount == string("*"));
1087 if (fAllAccounts || acentry.strAccount == strAccount)
1090 entry.push_back(Pair("account", acentry.strAccount));
1091 entry.push_back(Pair("category", "move"));
1092 entry.push_back(Pair("time", (boost::int64_t)acentry.nTime));
1093 entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit)));
1094 entry.push_back(Pair("otheraccount", acentry.strOtherAccount));
1095 entry.push_back(Pair("comment", acentry.strComment));
1096 ret.push_back(entry);
1100 Value listtransactions(const Array& params, bool fHelp)
1102 if (fHelp || params.size() > 3)
1103 throw runtime_error(
1104 "listtransactions [account] [count=10] [from=0]\n"
1105 "Returns up to [count] most recent transactions skipping the first [from] transactions for account [account].");
1107 string strAccount = "*";
1108 if (params.size() > 0)
1109 strAccount = params[0].get_str();
1111 if (params.size() > 1)
1112 nCount = params[1].get_int();
1114 if (params.size() > 2)
1115 nFrom = params[2].get_int();
1118 throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count");
1120 throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative from");
1124 std::list<CAccountingEntry> acentries;
1125 CWallet::TxItems txOrdered = pwalletMain->OrderedTxItems(acentries, strAccount);
1127 // iterate backwards until we have nCount items to return:
1128 for (CWallet::TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
1130 CWalletTx *const pwtx = (*it).second.first;
1132 ListTransactions(*pwtx, strAccount, 0, true, ret);
1133 CAccountingEntry *const pacentry = (*it).second.second;
1135 AcentryToJSON(*pacentry, strAccount, ret);
1137 if ((int)ret.size() >= (nCount+nFrom)) break;
1139 // ret is newest to oldest
1141 if (nFrom > (int)ret.size())
1143 if ((nFrom + nCount) > (int)ret.size())
1144 nCount = ret.size() - nFrom;
1145 Array::iterator first = ret.begin();
1146 std::advance(first, nFrom);
1147 Array::iterator last = ret.begin();
1148 std::advance(last, nFrom+nCount);
1150 if (last != ret.end()) ret.erase(last, ret.end());
1151 if (first != ret.begin()) ret.erase(ret.begin(), first);
1153 std::reverse(ret.begin(), ret.end()); // Return oldest to newest
1158 Value listaccounts(const Array& params, bool fHelp)
1160 if (fHelp || params.size() > 1)
1161 throw runtime_error(
1162 "listaccounts [minconf=1]\n"
1163 "Returns Object that has account names as keys, account balances as values.");
1166 if (params.size() > 0)
1167 nMinDepth = params[0].get_int();
1169 map<string, int64> mapAccountBalances;
1170 BOOST_FOREACH(const PAIRTYPE(CTxDestination, string)& entry, pwalletMain->mapAddressBook) {
1171 if (IsMine(*pwalletMain, entry.first)) // This address belongs to me
1172 mapAccountBalances[entry.second] = 0;
1175 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
1177 const CWalletTx& wtx = (*it).second;
1178 int64 nGeneratedImmature, nGeneratedMature, nFee;
1179 string strSentAccount;
1180 list<pair<CTxDestination, int64> > listReceived;
1181 list<pair<CTxDestination, int64> > listSent;
1182 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount);
1183 mapAccountBalances[strSentAccount] -= nFee;
1184 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& s, listSent)
1185 mapAccountBalances[strSentAccount] -= s.second;
1186 if (wtx.GetDepthInMainChain() >= nMinDepth)
1188 mapAccountBalances[""] += nGeneratedMature;
1189 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& r, listReceived)
1190 if (pwalletMain->mapAddressBook.count(r.first))
1191 mapAccountBalances[pwalletMain->mapAddressBook[r.first]] += r.second;
1193 mapAccountBalances[""] += r.second;
1197 list<CAccountingEntry> acentries;
1198 CWalletDB(pwalletMain->strWalletFile).ListAccountCreditDebit("*", acentries);
1199 BOOST_FOREACH(const CAccountingEntry& entry, acentries)
1200 mapAccountBalances[entry.strAccount] += entry.nCreditDebit;
1203 BOOST_FOREACH(const PAIRTYPE(string, int64)& accountBalance, mapAccountBalances) {
1204 ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second)));
1209 Value listsinceblock(const Array& params, bool fHelp)
1212 throw runtime_error(
1213 "listsinceblock [blockhash] [target-confirmations]\n"
1214 "Get all transactions in blocks since block [blockhash], or all transactions if omitted");
1216 CBlockIndex *pindex = NULL;
1217 int target_confirms = 1;
1219 if (params.size() > 0)
1221 uint256 blockId = 0;
1223 blockId.SetHex(params[0].get_str());
1224 pindex = CBlockLocator(blockId).GetBlockIndex();
1227 if (params.size() > 1)
1229 target_confirms = params[1].get_int();
1231 if (target_confirms < 1)
1232 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter");
1235 int depth = pindex ? (1 + nBestHeight - pindex->nHeight) : -1;
1239 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); it++)
1241 CWalletTx tx = (*it).second;
1243 if (depth == -1 || tx.GetDepthInMainChain() < depth)
1244 ListTransactions(tx, "*", 0, true, transactions);
1249 if (target_confirms == 1)
1251 lastblock = hashBestChain;
1255 int target_height = pindexBest->nHeight + 1 - target_confirms;
1258 for (block = pindexBest;
1259 block && block->nHeight > target_height;
1260 block = block->pprev) { }
1262 lastblock = block ? block->GetBlockHash() : 0;
1266 ret.push_back(Pair("transactions", transactions));
1267 ret.push_back(Pair("lastblock", lastblock.GetHex()));
1272 Value gettransaction(const Array& params, bool fHelp)
1274 if (fHelp || params.size() != 1)
1275 throw runtime_error(
1276 "gettransaction <txid>\n"
1277 "Get detailed information about <txid>");
1280 hash.SetHex(params[0].get_str());
1284 if (pwalletMain->mapWallet.count(hash))
1286 const CWalletTx& wtx = pwalletMain->mapWallet[hash];
1288 TxToJSON(wtx, 0, entry);
1290 int64 nCredit = wtx.GetCredit();
1291 int64 nDebit = wtx.GetDebit();
1292 int64 nNet = nCredit - nDebit;
1293 int64 nFee = (wtx.IsFromMe() ? wtx.GetValueOut() - nDebit : 0);
1295 entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee)));
1297 entry.push_back(Pair("fee", ValueFromAmount(nFee)));
1299 WalletTxToJSON(wtx, entry);
1302 ListTransactions(pwalletMain->mapWallet[hash], "*", 0, false, details);
1303 entry.push_back(Pair("details", details));
1308 uint256 hashBlock = 0;
1309 if (GetTransaction(hash, tx, hashBlock))
1311 TxToJSON(tx, 0, entry);
1313 entry.push_back(Pair("confirmations", 0));
1316 entry.push_back(Pair("blockhash", hashBlock.GetHex()));
1317 map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashBlock);
1318 if (mi != mapBlockIndex.end() && (*mi).second)
1320 CBlockIndex* pindex = (*mi).second;
1321 if (pindex->IsInMainChain())
1322 entry.push_back(Pair("confirmations", 1 + nBestHeight - pindex->nHeight));
1324 entry.push_back(Pair("confirmations", 0));
1329 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction");
1336 Value backupwallet(const Array& params, bool fHelp)
1338 if (fHelp || params.size() != 1)
1339 throw runtime_error(
1340 "backupwallet <destination>\n"
1341 "Safely copies wallet.dat to destination, which can be a directory or a path with filename.");
1343 string strDest = params[0].get_str();
1344 if (!BackupWallet(*pwalletMain, strDest))
1345 throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet backup failed!");
1351 Value keypoolrefill(const Array& params, bool fHelp)
1353 if (fHelp || params.size() > 1)
1354 throw runtime_error(
1355 "keypoolrefill [new-size]\n"
1356 "Fills the keypool."
1357 + HelpRequiringPassphrase());
1359 unsigned int nSize = max(GetArg("-keypool", 100), 0LL);
1360 if (params.size() > 0) {
1361 if (params[0].get_int() < 0)
1362 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected valid size");
1363 nSize = (unsigned int) params[0].get_int();
1366 EnsureWalletIsUnlocked();
1368 pwalletMain->TopUpKeyPool(nSize);
1370 if (pwalletMain->GetKeyPoolSize() < nSize)
1371 throw JSONRPCError(RPC_WALLET_ERROR, "Error refreshing keypool.");
1377 void ThreadTopUpKeyPool(void* parg)
1379 // Make this thread recognisable as the key-topping-up thread
1380 RenameThread("bitcoin-key-top");
1382 pwalletMain->TopUpKeyPool();
1385 void ThreadCleanWalletPassphrase(void* parg)
1387 // Make this thread recognisable as the wallet relocking thread
1388 RenameThread("bitcoin-lock-wa");
1390 int64 nMyWakeTime = GetTimeMillis() + *((int64*)parg) * 1000;
1392 ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime);
1394 if (nWalletUnlockTime == 0)
1396 nWalletUnlockTime = nMyWakeTime;
1400 if (nWalletUnlockTime==0)
1402 int64 nToSleep = nWalletUnlockTime - GetTimeMillis();
1406 LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime);
1408 ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime);
1412 if (nWalletUnlockTime)
1414 nWalletUnlockTime = 0;
1415 pwalletMain->Lock();
1420 if (nWalletUnlockTime < nMyWakeTime)
1421 nWalletUnlockTime = nMyWakeTime;
1424 LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime);
1426 delete (int64*)parg;
1429 Value walletpassphrase(const Array& params, bool fHelp)
1431 if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 3))
1432 throw runtime_error(
1433 "walletpassphrase <passphrase> <timeout> [mintonly]\n"
1434 "Stores the wallet decryption key in memory for <timeout> seconds.\n"
1435 "mintonly is optional true/false allowing only block minting.");
1438 if (!pwalletMain->IsCrypted())
1439 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called.");
1441 if (!pwalletMain->IsLocked())
1442 throw JSONRPCError(RPC_WALLET_ALREADY_UNLOCKED, "Error: Wallet is already unlocked, use walletlock first if need to change unlock settings.");
1443 // Note that the walletpassphrase is stored in params[0] which is not mlock()ed
1444 SecureString strWalletPass;
1445 strWalletPass.reserve(100);
1446 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1447 // Alternately, find a way to make params[0] mlock()'d to begin with.
1448 strWalletPass = params[0].get_str().c_str();
1450 if (strWalletPass.length() > 0)
1452 if (!pwalletMain->Unlock(strWalletPass))
1453 throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
1456 throw runtime_error(
1457 "walletpassphrase <passphrase> <timeout>\n"
1458 "Stores the wallet decryption key in memory for <timeout> seconds.");
1460 NewThread(ThreadTopUpKeyPool, NULL);
1461 int64* pnSleepTime = new int64(params[1].get_int64());
1462 NewThread(ThreadCleanWalletPassphrase, pnSleepTime);
1464 // ppcoin: if user OS account compromised prevent trivial sendmoney commands
1465 if (params.size() > 2)
1466 fWalletUnlockMintOnly = params[2].get_bool();
1468 fWalletUnlockMintOnly = false;
1474 Value walletpassphrasechange(const Array& params, bool fHelp)
1476 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2))
1477 throw runtime_error(
1478 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1479 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1482 if (!pwalletMain->IsCrypted())
1483 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrasechange was called.");
1485 // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string)
1486 // Alternately, find a way to make params[0] mlock()'d to begin with.
1487 SecureString strOldWalletPass;
1488 strOldWalletPass.reserve(100);
1489 strOldWalletPass = params[0].get_str().c_str();
1491 SecureString strNewWalletPass;
1492 strNewWalletPass.reserve(100);
1493 strNewWalletPass = params[1].get_str().c_str();
1495 if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1)
1496 throw runtime_error(
1497 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1498 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1500 if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass))
1501 throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
1507 Value walletlock(const Array& params, bool fHelp)
1509 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 0))
1510 throw runtime_error(
1512 "Removes the wallet encryption key from memory, locking the wallet.\n"
1513 "After calling this method, you will need to call walletpassphrase again\n"
1514 "before being able to call any methods which require the wallet to be unlocked.");
1517 if (!pwalletMain->IsCrypted())
1518 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletlock was called.");
1521 LOCK(cs_nWalletUnlockTime);
1522 pwalletMain->Lock();
1523 nWalletUnlockTime = 0;
1530 Value encryptwallet(const Array& params, bool fHelp)
1532 if (!pwalletMain->IsCrypted() && (fHelp || params.size() != 1))
1533 throw runtime_error(
1534 "encryptwallet <passphrase>\n"
1535 "Encrypts the wallet with <passphrase>.");
1538 if (pwalletMain->IsCrypted())
1539 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an encrypted wallet, but encryptwallet was called.");
1541 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1542 // Alternately, find a way to make params[0] mlock()'d to begin with.
1543 SecureString strWalletPass;
1544 strWalletPass.reserve(100);
1545 strWalletPass = params[0].get_str().c_str();
1547 if (strWalletPass.length() < 1)
1548 throw runtime_error(
1549 "encryptwallet <passphrase>\n"
1550 "Encrypts the wallet with <passphrase>.");
1552 if (!pwalletMain->EncryptWallet(strWalletPass))
1553 throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: Failed to encrypt the wallet.");
1555 // BDB seems to have a bad habit of writing old data into
1556 // slack space in .dat files; that is bad if the old data is
1557 // unencrypted private keys. So:
1559 return "wallet encrypted; NovaCoin server stopping, restart to run with encrypted wallet. The keypool has been flushed, you need to make a new backup.";
1562 class DescribeAddressVisitor : public boost::static_visitor<Object>
1565 Object operator()(const CNoDestination &dest) const { return Object(); }
1567 Object operator()(const CKeyID &keyID) const {
1570 pwalletMain->GetPubKey(keyID, vchPubKey);
1571 obj.push_back(Pair("isscript", false));
1572 obj.push_back(Pair("pubkey", HexStr(vchPubKey.Raw())));
1573 obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed()));
1577 Object operator()(const CScriptID &scriptID) const {
1579 obj.push_back(Pair("isscript", true));
1581 pwalletMain->GetCScript(scriptID, subscript);
1582 std::vector<CTxDestination> addresses;
1583 txnouttype whichType;
1585 ExtractDestinations(subscript, whichType, addresses, nRequired);
1586 obj.push_back(Pair("script", GetTxnOutputType(whichType)));
1587 obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end())));
1589 BOOST_FOREACH(const CTxDestination& addr, addresses)
1590 a.push_back(CBitcoinAddress(addr).ToString());
1591 obj.push_back(Pair("addresses", a));
1592 if (whichType == TX_MULTISIG)
1593 obj.push_back(Pair("sigsrequired", nRequired));
1598 Value validateaddress(const Array& params, bool fHelp)
1600 if (fHelp || params.size() != 1)
1601 throw runtime_error(
1602 "validateaddress <novacoinaddress>\n"
1603 "Return information about <novacoinaddress>.");
1605 CBitcoinAddress address(params[0].get_str());
1606 bool isValid = address.IsValid();
1609 ret.push_back(Pair("isvalid", isValid));
1612 CTxDestination dest = address.Get();
1613 string currentAddress = address.ToString();
1614 ret.push_back(Pair("address", currentAddress));
1615 bool fMine = IsMine(*pwalletMain, dest);
1616 ret.push_back(Pair("ismine", fMine));
1618 Object detail = boost::apply_visitor(DescribeAddressVisitor(), dest);
1619 ret.insert(ret.end(), detail.begin(), detail.end());
1621 if (pwalletMain->mapAddressBook.count(dest))
1622 ret.push_back(Pair("account", pwalletMain->mapAddressBook[dest]));
1627 Value validatepubkey(const Array& params, bool fHelp)
1629 if (fHelp || !params.size() || params.size() > 2)
1630 throw runtime_error(
1631 "validatepubkey <novacoinpubkey>\n"
1632 "Return information about <novacoinpubkey>.");
1634 std::vector<unsigned char> vchPubKey = ParseHex(params[0].get_str());
1635 CPubKey pubKey(vchPubKey);
1637 bool isValid = pubKey.IsValid();
1638 bool isCompressed = pubKey.IsCompressed();
1639 CKeyID keyID = pubKey.GetID();
1641 CBitcoinAddress address;
1645 ret.push_back(Pair("isvalid", isValid));
1648 CTxDestination dest = address.Get();
1649 string currentAddress = address.ToString();
1650 ret.push_back(Pair("address", currentAddress));
1651 bool fMine = IsMine(*pwalletMain, dest);
1652 ret.push_back(Pair("ismine", fMine));
1653 ret.push_back(Pair("iscompressed", isCompressed));
1655 Object detail = boost::apply_visitor(DescribeAddressVisitor(), dest);
1656 ret.insert(ret.end(), detail.begin(), detail.end());
1658 if (pwalletMain->mapAddressBook.count(dest))
1659 ret.push_back(Pair("account", pwalletMain->mapAddressBook[dest]));
1664 // ppcoin: reserve balance from being staked for network protection
1665 Value reservebalance(const Array& params, bool fHelp)
1667 if (fHelp || params.size() > 2)
1668 throw runtime_error(
1669 "reservebalance [<reserve> [amount]]\n"
1670 "<reserve> is true or false to turn balance reserve on or off.\n"
1671 "<amount> is a real and rounded to cent.\n"
1672 "Set reserve amount not participating in network protection.\n"
1673 "If no parameters provided current setting is printed.\n");
1675 if (params.size() > 0)
1677 bool fReserve = params[0].get_bool();
1680 if (params.size() == 1)
1681 throw runtime_error("must provide amount to reserve balance.\n");
1682 int64 nAmount = AmountFromValue(params[1]);
1683 nAmount = (nAmount / CENT) * CENT; // round to cent
1685 throw runtime_error("amount cannot be negative.\n");
1686 mapArgs["-reservebalance"] = FormatMoney(nAmount).c_str();
1690 if (params.size() > 1)
1691 throw runtime_error("cannot specify amount to turn off reserve.\n");
1692 mapArgs["-reservebalance"] = "0";
1697 int64 nReserveBalance = 0;
1698 if (mapArgs.count("-reservebalance") && !ParseMoney(mapArgs["-reservebalance"], nReserveBalance))
1699 throw runtime_error("invalid reserve balance amount\n");
1700 result.push_back(Pair("reserve", (nReserveBalance > 0)));
1701 result.push_back(Pair("amount", ValueFromAmount(nReserveBalance)));
1706 // ppcoin: check wallet integrity
1707 Value checkwallet(const Array& params, bool fHelp)
1709 if (fHelp || params.size() > 0)
1710 throw runtime_error(
1712 "Check wallet for integrity.\n");
1715 int64 nBalanceInQuestion;
1716 pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion, true);
1718 if (nMismatchSpent == 0)
1719 result.push_back(Pair("wallet check passed", true));
1722 result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1723 result.push_back(Pair("amount in question", ValueFromAmount(nBalanceInQuestion)));
1729 // ppcoin: repair wallet
1730 Value repairwallet(const Array& params, bool fHelp)
1732 if (fHelp || params.size() > 0)
1733 throw runtime_error(
1735 "Repair wallet if checkwallet reports any problem.\n");
1738 int64 nBalanceInQuestion;
1739 pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion);
1741 if (nMismatchSpent == 0)
1742 result.push_back(Pair("wallet check passed", true));
1745 result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1746 result.push_back(Pair("amount affected by repair", ValueFromAmount(nBalanceInQuestion)));
1751 // NovaCoin: resend unconfirmed wallet transactions
1752 Value resendtx(const Array& params, bool fHelp)
1754 if (fHelp || params.size() > 1)
1755 throw runtime_error(
1757 "Re-send unconfirmed transactions.\n"
1760 ResendWalletTransactions();
1765 // ppcoin: make a public-private key pair
1766 Value makekeypair(const Array& params, bool fHelp)
1768 if (fHelp || params.size() > 1)
1769 throw runtime_error(
1770 "makekeypair [prefix]\n"
1771 "Make a public/private key pair.\n"
1772 "[prefix] is optional preferred prefix for the public key.\n");
1774 string strPrefix = "";
1775 if (params.size() > 0)
1776 strPrefix = params[0].get_str();
1779 key.MakeNewKey(false);
1781 CPrivKey vchPrivKey = key.GetPrivKey();
1783 result.push_back(Pair("PrivateKey", HexStr<CPrivKey::iterator>(vchPrivKey.begin(), vchPrivKey.end())));
1784 result.push_back(Pair("PublicKey", HexStr(key.GetPubKey().Raw())));