1 // Copyright (c) 2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2012 The Bitcoin developers
3 // Distributed under the MIT/X11 software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
8 #include "bitcoinrpc.h"
12 using namespace json_spirit;
15 int64 nWalletUnlockTime;
16 static CCriticalSection cs_nWalletUnlockTime;
18 extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, json_spirit::Object& entry);
20 std::string HelpRequiringPassphrase()
22 return pwalletMain->IsCrypted()
23 ? "\nrequires wallet passphrase to be set with walletpassphrase first"
27 void EnsureWalletIsUnlocked()
29 if (pwalletMain->IsLocked())
30 throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first.");
31 if (fWalletUnlockMintOnly)
32 throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Wallet unlocked for block minting only.");
35 void WalletTxToJSON(const CWalletTx& wtx, Object& entry)
37 int confirms = wtx.GetDepthInMainChain();
38 entry.push_back(Pair("confirmations", confirms));
39 if (wtx.IsCoinBase() || wtx.IsCoinStake())
40 entry.push_back(Pair("generated", true));
43 entry.push_back(Pair("blockhash", wtx.hashBlock.GetHex()));
44 entry.push_back(Pair("blockindex", wtx.nIndex));
45 entry.push_back(Pair("blocktime", (boost::int64_t)(mapBlockIndex[wtx.hashBlock]->nTime)));
47 uint256 hash = wtx.GetHash();
48 entry.push_back(Pair("txid", hash.GetHex()));
50 BOOST_FOREACH(const uint256& conflict, wtx.GetConflicts())
51 conflicts.push_back(conflict.GetHex());
52 entry.push_back(Pair("walletconflicts", conflicts));
53 entry.push_back(Pair("time", (boost::int64_t)wtx.GetTxTime()));
54 entry.push_back(Pair("timereceived", (boost::int64_t)wtx.nTimeReceived));
55 BOOST_FOREACH(const PAIRTYPE(string,string)& item, wtx.mapValue)
56 entry.push_back(Pair(item.first, item.second));
59 string AccountFromValue(const Value& value)
61 string strAccount = value.get_str();
62 if (strAccount == "*")
63 throw JSONRPCError(RPC_WALLET_INVALID_ACCOUNT_NAME, "Invalid account name");
67 Value getinfo(const Array& params, bool fHelp)
69 if (fHelp || params.size() != 0)
72 "Returns an object containing various state info.");
75 GetProxy(NET_IPV4, proxy);
78 obj.push_back(Pair("version", FormatFullVersion()));
79 obj.push_back(Pair("protocolversion",(int)PROTOCOL_VERSION));
80 obj.push_back(Pair("walletversion", pwalletMain->GetVersion()));
81 obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance())));
82 obj.push_back(Pair("newmint", ValueFromAmount(pwalletMain->GetNewMint())));
83 obj.push_back(Pair("stake", ValueFromAmount(pwalletMain->GetStake())));
84 obj.push_back(Pair("blocks", (int)nBestHeight));
85 obj.push_back(Pair("timeoffset", (boost::int64_t)GetTimeOffset()));
86 obj.push_back(Pair("moneysupply", ValueFromAmount(pindexBest->nMoneySupply)));
87 obj.push_back(Pair("connections", (int)vNodes.size()));
88 obj.push_back(Pair("proxy", (proxy.first.IsValid() ? proxy.first.ToStringIPPort() : string())));
89 obj.push_back(Pair("ip", addrSeenByPeer.ToStringIP()));
91 diff.push_back(Pair("proof-of-work", GetDifficulty()));
92 diff.push_back(Pair("proof-of-stake", GetDifficulty(GetLastBlockIndex(pindexBest, true))));
93 obj.push_back(Pair("difficulty", diff));
95 obj.push_back(Pair("testnet", fTestNet));
96 obj.push_back(Pair("keypoololdest", (boost::int64_t)pwalletMain->GetOldestKeyPoolTime()));
97 obj.push_back(Pair("keypoolsize", (int)pwalletMain->GetKeyPoolSize()));
98 obj.push_back(Pair("paytxfee", ValueFromAmount(nTransactionFee)));
99 obj.push_back(Pair("mininput", ValueFromAmount(nMinimumInputValue)));
100 if (pwalletMain->IsCrypted())
101 obj.push_back(Pair("unlocked_until", (boost::int64_t)nWalletUnlockTime / 1000));
102 obj.push_back(Pair("errors", GetWarnings("statusbar")));
107 Value getnewpubkey(const Array& params, bool fHelp)
109 if (fHelp || params.size() > 1)
111 "getnewpubkey [account]\n"
112 "Returns new public key for coinbase generation.");
114 // Parse the account first so we don't generate a key if there's an error
116 if (params.size() > 0)
117 strAccount = AccountFromValue(params[0]);
119 if (!pwalletMain->IsLocked())
120 pwalletMain->TopUpKeyPool();
122 // Generate a new key that is added to wallet
124 if (!pwalletMain->GetKeyFromPool(newKey, false))
125 throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
126 CKeyID keyID = newKey.GetID();
128 pwalletMain->SetAddressBookName(keyID, strAccount);
129 vector<unsigned char> vchPubKey = newKey.Raw();
131 return HexStr(vchPubKey.begin(), vchPubKey.end());
135 Value getnewaddress(const Array& params, bool fHelp)
137 if (fHelp || params.size() > 1)
139 "getnewaddress [account]\n"
140 "Returns a new NovaCoin address for receiving payments. "
141 "If [account] is specified (recommended), it is added to the address book "
142 "so payments received with the address will be credited to [account].");
144 // Parse the account first so we don't generate a key if there's an error
146 if (params.size() > 0)
147 strAccount = AccountFromValue(params[0]);
149 if (!pwalletMain->IsLocked())
150 pwalletMain->TopUpKeyPool();
152 // Generate a new key that is added to wallet
154 if (!pwalletMain->GetKeyFromPool(newKey, false))
155 throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
156 CKeyID keyID = newKey.GetID();
158 pwalletMain->SetAddressBookName(keyID, strAccount);
160 return CBitcoinAddress(keyID).ToString();
164 CBitcoinAddress GetAccountAddress(string strAccount, bool bForceNew=false)
166 CWalletDB walletdb(pwalletMain->strWalletFile);
169 walletdb.ReadAccount(strAccount, account);
171 bool bKeyUsed = false;
173 // Check if the current key has been used
174 if (account.vchPubKey.IsValid())
176 CScript scriptPubKey;
177 scriptPubKey.SetDestination(account.vchPubKey.GetID());
178 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin();
179 it != pwalletMain->mapWallet.end() && account.vchPubKey.IsValid();
182 const CWalletTx& wtx = (*it).second;
183 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
184 if (txout.scriptPubKey == scriptPubKey)
189 // Generate a new key
190 if (!account.vchPubKey.IsValid() || bForceNew || bKeyUsed)
192 if (!pwalletMain->GetKeyFromPool(account.vchPubKey, false))
193 throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
195 pwalletMain->SetAddressBookName(account.vchPubKey.GetID(), strAccount);
196 walletdb.WriteAccount(strAccount, account);
199 return CBitcoinAddress(account.vchPubKey.GetID());
202 Value getaccountaddress(const Array& params, bool fHelp)
204 if (fHelp || params.size() != 1)
206 "getaccountaddress <account>\n"
207 "Returns the current NovaCoin address for receiving payments to this account.");
209 // Parse the account first so we don't generate a key if there's an error
210 string strAccount = AccountFromValue(params[0]);
214 ret = GetAccountAddress(strAccount).ToString();
221 Value setaccount(const Array& params, bool fHelp)
223 if (fHelp || params.size() < 1 || params.size() > 2)
225 "setaccount <novacoinaddress> <account>\n"
226 "Sets the account associated with the given address.");
228 CBitcoinAddress address(params[0].get_str());
229 if (!address.IsValid())
230 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
234 if (params.size() > 1)
235 strAccount = AccountFromValue(params[1]);
237 // Detect when changing the account of an address that is the 'unused current key' of another account:
238 if (pwalletMain->mapAddressBook.count(address.Get()))
240 string strOldAccount = pwalletMain->mapAddressBook[address.Get()];
241 if (address == GetAccountAddress(strOldAccount))
242 GetAccountAddress(strOldAccount, true);
245 pwalletMain->SetAddressBookName(address.Get(), strAccount);
251 Value getaccount(const Array& params, bool fHelp)
253 if (fHelp || params.size() != 1)
255 "getaccount <novacoinaddress>\n"
256 "Returns the account associated with the given address.");
258 CBitcoinAddress address(params[0].get_str());
259 if (!address.IsValid())
260 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
263 map<CTxDestination, string>::iterator mi = pwalletMain->mapAddressBook.find(address.Get());
264 if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.empty())
265 strAccount = (*mi).second;
270 Value getaddressesbyaccount(const Array& params, bool fHelp)
272 if (fHelp || params.size() != 1)
274 "getaddressesbyaccount <account>\n"
275 "Returns the list of addresses for the given account.");
277 string strAccount = AccountFromValue(params[0]);
279 // Find all addresses that have the given account
281 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
283 const CBitcoinAddress& address = item.first;
284 const string& strName = item.second;
285 if (strName == strAccount)
286 ret.push_back(address.ToString());
291 Value sendtoaddress(const Array& params, bool fHelp)
293 if (fHelp || params.size() < 2 || params.size() > 4)
295 "sendtoaddress <novacoinaddress> <amount> [comment] [comment-to]\n"
296 "<amount> is a real and is rounded to the nearest 0.000001"
297 + HelpRequiringPassphrase());
299 CBitcoinAddress address(params[0].get_str());
300 if (!address.IsValid())
301 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
304 int64 nAmount = AmountFromValue(params[1]);
306 if (nAmount < MIN_TXOUT_AMOUNT)
307 throw JSONRPCError(-101, "Send amount too small");
311 if (params.size() > 2 && params[2].type() != null_type && !params[2].get_str().empty())
312 wtx.mapValue["comment"] = params[2].get_str();
313 if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
314 wtx.mapValue["to"] = params[3].get_str();
316 if (pwalletMain->IsLocked())
317 throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first.");
319 string strError = pwalletMain->SendMoneyToDestination(address.Get(), nAmount, wtx);
321 throw JSONRPCError(RPC_WALLET_ERROR, strError);
323 return wtx.GetHash().GetHex();
326 Value listaddressgroupings(const Array& params, bool fHelp)
330 "listaddressgroupings\n"
331 "Lists groups of addresses which have had their common ownership\n"
332 "made public by common use as inputs or as the resulting change\n"
333 "in past transactions");
336 map<CTxDestination, int64> balances = pwalletMain->GetAddressBalances();
337 BOOST_FOREACH(set<CTxDestination> grouping, pwalletMain->GetAddressGroupings())
340 BOOST_FOREACH(CTxDestination address, grouping)
343 addressInfo.push_back(CBitcoinAddress(address).ToString());
344 addressInfo.push_back(ValueFromAmount(balances[address]));
346 LOCK(pwalletMain->cs_wallet);
347 if (pwalletMain->mapAddressBook.find(CBitcoinAddress(address).Get()) != pwalletMain->mapAddressBook.end())
348 addressInfo.push_back(pwalletMain->mapAddressBook.find(CBitcoinAddress(address).Get())->second);
350 jsonGrouping.push_back(addressInfo);
352 jsonGroupings.push_back(jsonGrouping);
354 return jsonGroupings;
357 Value signmessage(const Array& params, bool fHelp)
359 if (fHelp || params.size() != 2)
361 "signmessage <novacoinaddress> <message>\n"
362 "Sign a message with the private key of an address");
364 EnsureWalletIsUnlocked();
366 string strAddress = params[0].get_str();
367 string strMessage = params[1].get_str();
369 CBitcoinAddress addr(strAddress);
371 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
374 if (!addr.GetKeyID(keyID))
375 throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
378 if (!pwalletMain->GetKey(keyID, key))
379 throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available");
381 CDataStream ss(SER_GETHASH, 0);
382 ss << strMessageMagic;
385 vector<unsigned char> vchSig;
386 if (!key.SignCompact(Hash(ss.begin(), ss.end()), vchSig))
387 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed");
389 return EncodeBase64(&vchSig[0], vchSig.size());
392 Value verifymessage(const Array& params, bool fHelp)
394 if (fHelp || params.size() != 3)
396 "verifymessage <novacoinaddress> <signature> <message>\n"
397 "Verify a signed message");
399 string strAddress = params[0].get_str();
400 string strSign = params[1].get_str();
401 string strMessage = params[2].get_str();
403 CBitcoinAddress addr(strAddress);
405 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
408 if (!addr.GetKeyID(keyID))
409 throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
411 bool fInvalid = false;
412 vector<unsigned char> vchSig = DecodeBase64(strSign.c_str(), &fInvalid);
415 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Malformed base64 encoding");
417 CDataStream ss(SER_GETHASH, 0);
418 ss << strMessageMagic;
422 if (!key.SetCompactSignature(Hash(ss.begin(), ss.end()), vchSig))
425 return (key.GetPubKey().GetID() == keyID);
429 Value getreceivedbyaddress(const Array& params, bool fHelp)
431 if (fHelp || params.size() < 1 || params.size() > 2)
433 "getreceivedbyaddress <novacoinaddress> [minconf=1]\n"
434 "Returns the total amount received by <novacoinaddress> in transactions with at least [minconf] confirmations.");
437 CBitcoinAddress address = CBitcoinAddress(params[0].get_str());
438 CScript scriptPubKey;
439 if (!address.IsValid())
440 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
441 scriptPubKey.SetDestination(address.Get());
442 if (!IsMine(*pwalletMain,scriptPubKey))
445 // Minimum confirmations
447 if (params.size() > 1)
448 nMinDepth = params[1].get_int();
452 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
454 const CWalletTx& wtx = (*it).second;
455 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
458 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
459 if (txout.scriptPubKey == scriptPubKey)
460 if (wtx.GetDepthInMainChain() >= nMinDepth)
461 nAmount += txout.nValue;
464 return ValueFromAmount(nAmount);
468 void GetAccountAddresses(string strAccount, set<CTxDestination>& setAddress)
470 BOOST_FOREACH(const PAIRTYPE(CTxDestination, string)& item, pwalletMain->mapAddressBook)
472 const CTxDestination& address = item.first;
473 const string& strName = item.second;
474 if (strName == strAccount)
475 setAddress.insert(address);
479 Value getreceivedbyaccount(const Array& params, bool fHelp)
481 if (fHelp || params.size() < 1 || params.size() > 2)
483 "getreceivedbyaccount <account> [minconf=1]\n"
484 "Returns the total amount received by addresses with <account> in transactions with at least [minconf] confirmations.");
486 // Minimum confirmations
488 if (params.size() > 1)
489 nMinDepth = params[1].get_int();
491 // Get the set of pub keys assigned to account
492 string strAccount = AccountFromValue(params[0]);
493 set<CTxDestination> setAddress;
494 GetAccountAddresses(strAccount, setAddress);
498 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
500 const CWalletTx& wtx = (*it).second;
501 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
504 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
506 CTxDestination address;
507 if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*pwalletMain, address) && setAddress.count(address))
508 if (wtx.GetDepthInMainChain() >= nMinDepth)
509 nAmount += txout.nValue;
513 return (double)nAmount / (double)COIN;
517 int64 GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth)
521 // Tally wallet transactions
522 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
524 const CWalletTx& wtx = (*it).second;
528 int64 nGenerated, nReceived, nSent, nFee;
529 wtx.GetAccountAmounts(strAccount, nGenerated, nReceived, nSent, nFee);
531 if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth)
532 nBalance += nReceived;
533 nBalance += nGenerated - nSent - nFee;
536 // Tally internal accounting entries
537 nBalance += walletdb.GetAccountCreditDebit(strAccount);
542 int64 GetAccountBalance(const string& strAccount, int nMinDepth)
544 CWalletDB walletdb(pwalletMain->strWalletFile);
545 return GetAccountBalance(walletdb, strAccount, nMinDepth);
549 Value getbalance(const Array& params, bool fHelp)
551 if (fHelp || params.size() > 2)
553 "getbalance [account] [minconf=1]\n"
554 "If [account] is not specified, returns the server's total available balance.\n"
555 "If [account] is specified, returns the balance in the account.");
557 if (params.size() == 0)
558 return ValueFromAmount(pwalletMain->GetBalance());
561 if (params.size() > 1)
562 nMinDepth = params[1].get_int();
564 if (params[0].get_str() == "*") {
565 // Calculate total balance a different way from GetBalance()
566 // (GetBalance() sums up all unspent TxOuts)
567 // getbalance and getbalance '*' 0 should return the same number.
569 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
571 const CWalletTx& wtx = (*it).second;
572 if (!wtx.IsTrusted() || wtx.GetBlocksToMaturity() > 0)
575 int64 allGeneratedImmature, allGeneratedMature, allFee;
576 allGeneratedImmature = allGeneratedMature = allFee = 0;
578 string strSentAccount;
579 list<pair<CTxDestination, int64> > listReceived;
580 list<pair<CTxDestination, int64> > listSent;
581 wtx.GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount);
582 if (wtx.GetDepthInMainChain() >= nMinDepth)
584 BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64)& r, listReceived)
585 nBalance += r.second;
587 BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64)& r, listSent)
588 nBalance -= r.second;
590 nBalance += allGeneratedMature;
592 return ValueFromAmount(nBalance);
595 string strAccount = AccountFromValue(params[0]);
597 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
599 return ValueFromAmount(nBalance);
602 Value getunconfirmedbalance(const Array ¶ms, bool fHelp)
604 if (fHelp || params.size() > 0)
606 "getunconfirmedbalance\n"
607 "Returns the server's total unconfirmed balance\n");
608 return ValueFromAmount(pwalletMain->GetUnconfirmedBalance());
611 Value movecmd(const Array& params, bool fHelp)
613 if (fHelp || params.size() < 3 || params.size() > 5)
615 "move <fromaccount> <toaccount> <amount> [minconf=1] [comment]\n"
616 "Move from one account in your wallet to another.");
618 string strFrom = AccountFromValue(params[0]);
619 string strTo = AccountFromValue(params[1]);
620 int64 nAmount = AmountFromValue(params[2]);
622 if (nAmount < MIN_TXOUT_AMOUNT)
623 throw JSONRPCError(-101, "Send amount too small");
625 if (params.size() > 3)
626 // unused parameter, used to be nMinDepth, keep type-checking it though
627 (void)params[3].get_int();
629 if (params.size() > 4)
630 strComment = params[4].get_str();
632 CWalletDB walletdb(pwalletMain->strWalletFile);
633 if (!walletdb.TxnBegin())
634 throw JSONRPCError(RPC_DATABASE_ERROR, "database error");
636 int64 nNow = GetAdjustedTime();
639 CAccountingEntry debit;
640 debit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb);
641 debit.strAccount = strFrom;
642 debit.nCreditDebit = -nAmount;
644 debit.strOtherAccount = strTo;
645 debit.strComment = strComment;
646 walletdb.WriteAccountingEntry(debit);
649 CAccountingEntry credit;
650 credit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb);
651 credit.strAccount = strTo;
652 credit.nCreditDebit = nAmount;
654 credit.strOtherAccount = strFrom;
655 credit.strComment = strComment;
656 walletdb.WriteAccountingEntry(credit);
658 if (!walletdb.TxnCommit())
659 throw JSONRPCError(RPC_DATABASE_ERROR, "database error");
665 Value sendfrom(const Array& params, bool fHelp)
667 if (fHelp || params.size() < 3 || params.size() > 6)
669 "sendfrom <fromaccount> <tonovacoinaddress> <amount> [minconf=1] [comment] [comment-to]\n"
670 "<amount> is a real and is rounded to the nearest 0.000001"
671 + HelpRequiringPassphrase());
673 string strAccount = AccountFromValue(params[0]);
674 CBitcoinAddress address(params[1].get_str());
675 if (!address.IsValid())
676 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
677 int64 nAmount = AmountFromValue(params[2]);
679 if (nAmount < MIN_TXOUT_AMOUNT)
680 throw JSONRPCError(-101, "Send amount too small");
683 if (params.size() > 3)
684 nMinDepth = params[3].get_int();
687 wtx.strFromAccount = strAccount;
688 if (params.size() > 4 && params[4].type() != null_type && !params[4].get_str().empty())
689 wtx.mapValue["comment"] = params[4].get_str();
690 if (params.size() > 5 && params[5].type() != null_type && !params[5].get_str().empty())
691 wtx.mapValue["to"] = params[5].get_str();
693 EnsureWalletIsUnlocked();
696 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
697 if (nAmount > nBalance)
698 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds");
701 string strError = pwalletMain->SendMoneyToDestination(address.Get(), nAmount, wtx);
703 throw JSONRPCError(RPC_WALLET_ERROR, strError);
705 return wtx.GetHash().GetHex();
709 Value sendmany(const Array& params, bool fHelp)
711 if (fHelp || params.size() < 2 || params.size() > 4)
713 "sendmany <fromaccount> {address:amount,...} [minconf=1] [comment]\n"
714 "amounts are double-precision floating point numbers"
715 + HelpRequiringPassphrase());
717 string strAccount = AccountFromValue(params[0]);
718 Object sendTo = params[1].get_obj();
720 if (params.size() > 2)
721 nMinDepth = params[2].get_int();
724 wtx.strFromAccount = strAccount;
725 if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
726 wtx.mapValue["comment"] = params[3].get_str();
728 set<CBitcoinAddress> setAddress;
729 vector<pair<CScript, int64> > vecSend;
731 int64 totalAmount = 0;
732 BOOST_FOREACH(const Pair& s, sendTo)
734 CBitcoinAddress address(s.name_);
735 if (!address.IsValid())
736 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid NovaCoin address: ")+s.name_);
738 if (setAddress.count(address))
739 throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+s.name_);
740 setAddress.insert(address);
742 CScript scriptPubKey;
743 scriptPubKey.SetDestination(address.Get());
744 int64 nAmount = AmountFromValue(s.value_);
746 if (nAmount < MIN_TXOUT_AMOUNT)
747 throw JSONRPCError(-101, "Send amount too small");
749 totalAmount += nAmount;
751 vecSend.push_back(make_pair(scriptPubKey, nAmount));
754 EnsureWalletIsUnlocked();
757 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
758 if (totalAmount > nBalance)
759 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds");
762 CReserveKey keyChange(pwalletMain);
763 int64 nFeeRequired = 0;
764 bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired);
767 if (totalAmount + nFeeRequired > pwalletMain->GetBalance())
768 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds");
769 throw JSONRPCError(RPC_WALLET_ERROR, "Transaction creation failed");
771 if (!pwalletMain->CommitTransaction(wtx, keyChange))
772 throw JSONRPCError(RPC_WALLET_ERROR, "Transaction commit failed");
774 return wtx.GetHash().GetHex();
777 Value addmultisigaddress(const Array& params, bool fHelp)
779 if (fHelp || params.size() < 2 || params.size() > 3)
781 string msg = "addmultisigaddress <nrequired> <'[\"key\",\"key\"]'> [account]\n"
782 "Add a nrequired-to-sign multisignature address to the wallet\"\n"
783 "each key is a NovaCoin address or hex-encoded public key\n"
784 "If [account] is specified, assign address to [account].";
785 throw runtime_error(msg);
788 int nRequired = params[0].get_int();
789 const Array& keys = params[1].get_array();
791 if (params.size() > 2)
792 strAccount = AccountFromValue(params[2]);
794 // Gather public keys
796 throw runtime_error("a multisignature address must require at least one key to redeem");
797 if ((int)keys.size() < nRequired)
799 strprintf("not enough keys supplied "
800 "(got %"PRIszu" keys, but need at least %d to redeem)", keys.size(), nRequired));
801 std::vector<CKey> pubkeys;
802 pubkeys.resize(keys.size());
803 for (unsigned int i = 0; i < keys.size(); i++)
805 const std::string& ks = keys[i].get_str();
807 // Case 1: Bitcoin address and we have full public key:
808 CBitcoinAddress address(ks);
809 if (address.IsValid())
812 if (!address.GetKeyID(keyID))
814 strprintf("%s does not refer to a key",ks.c_str()));
816 if (!pwalletMain->GetPubKey(keyID, vchPubKey))
818 strprintf("no full public key for address %s",ks.c_str()));
819 if (!vchPubKey.IsValid() || !pubkeys[i].SetPubKey(vchPubKey))
820 throw runtime_error(" Invalid public key: "+ks);
823 // Case 2: hex public key
826 CPubKey vchPubKey(ParseHex(ks));
827 if (!vchPubKey.IsValid() || !pubkeys[i].SetPubKey(vchPubKey))
828 throw runtime_error(" Invalid public key: "+ks);
832 throw runtime_error(" Invalid public key: "+ks);
836 // Construct using pay-to-script-hash:
838 inner.SetMultisig(nRequired, pubkeys);
839 CScriptID innerID = inner.GetID();
840 pwalletMain->AddCScript(inner);
842 pwalletMain->SetAddressBookName(innerID, strAccount);
843 return CBitcoinAddress(innerID).ToString();
846 Value addredeemscript(const Array& params, bool fHelp)
848 if (fHelp || params.size() < 1 || params.size() > 2)
850 string msg = "addredeemscript <redeemScript> [account]\n"
851 "Add a P2SH address with a specified redeemScript to the wallet.\n"
852 "If [account] is specified, assign address to [account].";
853 throw runtime_error(msg);
857 if (params.size() > 1)
858 strAccount = AccountFromValue(params[1]);
860 // Construct using pay-to-script-hash:
861 vector<unsigned char> innerData = ParseHexV(params[0], "redeemScript");
862 CScript inner(innerData.begin(), innerData.end());
863 CScriptID innerID = inner.GetID();
864 pwalletMain->AddCScript(inner);
866 pwalletMain->SetAddressBookName(innerID, strAccount);
867 return CBitcoinAddress(innerID).ToString();
877 nConf = std::numeric_limits<int>::max();
881 Value ListReceived(const Array& params, bool fByAccounts)
883 // Minimum confirmations
885 if (params.size() > 0)
886 nMinDepth = params[0].get_int();
888 // Whether to include empty accounts
889 bool fIncludeEmpty = false;
890 if (params.size() > 1)
891 fIncludeEmpty = params[1].get_bool();
894 map<CBitcoinAddress, tallyitem> mapTally;
895 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
897 const CWalletTx& wtx = (*it).second;
899 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
902 int nDepth = wtx.GetDepthInMainChain();
903 if (nDepth < nMinDepth)
906 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
908 CTxDestination address;
909 if (!ExtractDestination(txout.scriptPubKey, address) || !IsMine(*pwalletMain, address))
912 tallyitem& item = mapTally[address];
913 item.nAmount += txout.nValue;
914 item.nConf = min(item.nConf, nDepth);
920 map<string, tallyitem> mapAccountTally;
921 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
923 const CBitcoinAddress& address = item.first;
924 const string& strAccount = item.second;
925 map<CBitcoinAddress, tallyitem>::iterator it = mapTally.find(address);
926 if (it == mapTally.end() && !fIncludeEmpty)
930 int nConf = std::numeric_limits<int>::max();
931 if (it != mapTally.end())
933 nAmount = (*it).second.nAmount;
934 nConf = (*it).second.nConf;
939 tallyitem& item = mapAccountTally[strAccount];
940 item.nAmount += nAmount;
941 item.nConf = min(item.nConf, nConf);
946 obj.push_back(Pair("address", address.ToString()));
947 obj.push_back(Pair("account", strAccount));
948 obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
949 obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf)));
956 for (map<string, tallyitem>::iterator it = mapAccountTally.begin(); it != mapAccountTally.end(); ++it)
958 int64 nAmount = (*it).second.nAmount;
959 int nConf = (*it).second.nConf;
961 obj.push_back(Pair("account", (*it).first));
962 obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
963 obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf)));
971 Value listreceivedbyaddress(const Array& params, bool fHelp)
973 if (fHelp || params.size() > 2)
975 "listreceivedbyaddress [minconf=1] [includeempty=false]\n"
976 "[minconf] is the minimum number of confirmations before payments are included.\n"
977 "[includeempty] whether to include addresses that haven't received any payments.\n"
978 "Returns an array of objects containing:\n"
979 " \"address\" : receiving address\n"
980 " \"account\" : the account of the receiving address\n"
981 " \"amount\" : total amount received by the address\n"
982 " \"confirmations\" : number of confirmations of the most recent transaction included");
984 return ListReceived(params, false);
987 Value listreceivedbyaccount(const Array& params, bool fHelp)
989 if (fHelp || params.size() > 2)
991 "listreceivedbyaccount [minconf=1] [includeempty=false]\n"
992 "[minconf] is the minimum number of confirmations before payments are included.\n"
993 "[includeempty] whether to include accounts that haven't received any payments.\n"
994 "Returns an array of objects containing:\n"
995 " \"account\" : the account of the receiving addresses\n"
996 " \"amount\" : total amount received by addresses with this account\n"
997 " \"confirmations\" : number of confirmations of the most recent transaction included");
999 return ListReceived(params, true);
1002 void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret)
1004 int64 nGeneratedImmature, nGeneratedMature, nFee;
1005 string strSentAccount;
1006 list<pair<CTxDestination, int64> > listReceived;
1007 list<pair<CTxDestination, int64> > listSent;
1009 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount);
1011 bool fAllAccounts = (strAccount == string("*"));
1013 // Generated blocks assigned to account ""
1014 if ((nGeneratedMature+nGeneratedImmature) != 0 && (fAllAccounts || strAccount == ""))
1017 entry.push_back(Pair("account", string("")));
1018 if (nGeneratedImmature)
1020 entry.push_back(Pair("category", wtx.GetDepthInMainChain() ? "immature" : "orphan"));
1021 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedImmature)));
1025 entry.push_back(Pair("category", "generate"));
1026 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedMature)));
1029 WalletTxToJSON(wtx, entry);
1030 ret.push_back(entry);
1034 if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount))
1036 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& s, listSent)
1039 entry.push_back(Pair("account", strSentAccount));
1040 entry.push_back(Pair("address", CBitcoinAddress(s.first).ToString()));
1042 if (wtx.GetDepthInMainChain() < 0) {
1043 entry.push_back(Pair("category", "conflicted"));
1045 entry.push_back(Pair("category", "send"));
1048 entry.push_back(Pair("amount", ValueFromAmount(-s.second)));
1049 entry.push_back(Pair("fee", ValueFromAmount(-nFee)));
1051 WalletTxToJSON(wtx, entry);
1052 ret.push_back(entry);
1057 if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth)
1059 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& r, listReceived)
1062 if (pwalletMain->mapAddressBook.count(r.first))
1063 account = pwalletMain->mapAddressBook[r.first];
1064 if (fAllAccounts || (account == strAccount))
1067 entry.push_back(Pair("account", account));
1068 entry.push_back(Pair("address", CBitcoinAddress(r.first).ToString()));
1069 if (wtx.IsCoinBase())
1071 if (wtx.GetDepthInMainChain() < 1)
1072 entry.push_back(Pair("category", "orphan"));
1073 else if (wtx.GetBlocksToMaturity() > 0)
1074 entry.push_back(Pair("category", "immature"));
1076 entry.push_back(Pair("category", "generate"));
1079 if (wtx.GetDepthInMainChain() < 0) {
1080 entry.push_back(Pair("category", "conflicted"));
1082 entry.push_back(Pair("category", "receive"));
1085 entry.push_back(Pair("amount", ValueFromAmount(r.second)));
1087 WalletTxToJSON(wtx, entry);
1088 ret.push_back(entry);
1094 void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Array& ret)
1096 bool fAllAccounts = (strAccount == string("*"));
1098 if (fAllAccounts || acentry.strAccount == strAccount)
1101 entry.push_back(Pair("account", acentry.strAccount));
1102 entry.push_back(Pair("category", "move"));
1103 entry.push_back(Pair("time", (boost::int64_t)acentry.nTime));
1104 entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit)));
1105 entry.push_back(Pair("otheraccount", acentry.strOtherAccount));
1106 entry.push_back(Pair("comment", acentry.strComment));
1107 ret.push_back(entry);
1111 Value listtransactions(const Array& params, bool fHelp)
1113 if (fHelp || params.size() > 3)
1114 throw runtime_error(
1115 "listtransactions [account] [count=10] [from=0]\n"
1116 "Returns up to [count] most recent transactions skipping the first [from] transactions for account [account].");
1118 string strAccount = "*";
1119 if (params.size() > 0)
1120 strAccount = params[0].get_str();
1122 if (params.size() > 1)
1123 nCount = params[1].get_int();
1125 if (params.size() > 2)
1126 nFrom = params[2].get_int();
1129 throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count");
1131 throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative from");
1135 std::list<CAccountingEntry> acentries;
1136 CWallet::TxItems txOrdered = pwalletMain->OrderedTxItems(acentries, strAccount);
1138 // iterate backwards until we have nCount items to return:
1139 for (CWallet::TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
1141 CWalletTx *const pwtx = (*it).second.first;
1143 ListTransactions(*pwtx, strAccount, 0, true, ret);
1144 CAccountingEntry *const pacentry = (*it).second.second;
1146 AcentryToJSON(*pacentry, strAccount, ret);
1148 if ((int)ret.size() >= (nCount+nFrom)) break;
1150 // ret is newest to oldest
1152 if (nFrom > (int)ret.size())
1154 if ((nFrom + nCount) > (int)ret.size())
1155 nCount = ret.size() - nFrom;
1156 Array::iterator first = ret.begin();
1157 std::advance(first, nFrom);
1158 Array::iterator last = ret.begin();
1159 std::advance(last, nFrom+nCount);
1161 if (last != ret.end()) ret.erase(last, ret.end());
1162 if (first != ret.begin()) ret.erase(ret.begin(), first);
1164 std::reverse(ret.begin(), ret.end()); // Return oldest to newest
1169 Value listaccounts(const Array& params, bool fHelp)
1171 if (fHelp || params.size() > 1)
1172 throw runtime_error(
1173 "listaccounts [minconf=1]\n"
1174 "Returns Object that has account names as keys, account balances as values.");
1177 if (params.size() > 0)
1178 nMinDepth = params[0].get_int();
1180 map<string, int64> mapAccountBalances;
1181 BOOST_FOREACH(const PAIRTYPE(CTxDestination, string)& entry, pwalletMain->mapAddressBook) {
1182 if (IsMine(*pwalletMain, entry.first)) // This address belongs to me
1183 mapAccountBalances[entry.second] = 0;
1186 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
1188 const CWalletTx& wtx = (*it).second;
1189 int64 nGeneratedImmature, nGeneratedMature, nFee;
1190 string strSentAccount;
1191 list<pair<CTxDestination, int64> > listReceived;
1192 list<pair<CTxDestination, int64> > listSent;
1193 if (wtx.GetBlocksToMaturity() > 0)
1195 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount);
1196 mapAccountBalances[strSentAccount] -= nFee;
1197 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& s, listSent)
1198 mapAccountBalances[strSentAccount] -= s.second;
1199 if (wtx.GetDepthInMainChain() >= nMinDepth)
1201 mapAccountBalances[""] += nGeneratedMature;
1202 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& r, listReceived)
1203 if (pwalletMain->mapAddressBook.count(r.first))
1204 mapAccountBalances[pwalletMain->mapAddressBook[r.first]] += r.second;
1206 mapAccountBalances[""] += r.second;
1210 list<CAccountingEntry> acentries;
1211 CWalletDB(pwalletMain->strWalletFile).ListAccountCreditDebit("*", acentries);
1212 BOOST_FOREACH(const CAccountingEntry& entry, acentries)
1213 mapAccountBalances[entry.strAccount] += entry.nCreditDebit;
1216 BOOST_FOREACH(const PAIRTYPE(string, int64)& accountBalance, mapAccountBalances) {
1217 ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second)));
1222 Value listsinceblock(const Array& params, bool fHelp)
1225 throw runtime_error(
1226 "listsinceblock [blockhash] [target-confirmations]\n"
1227 "Get all transactions in blocks since block [blockhash], or all transactions if omitted");
1229 CBlockIndex *pindex = NULL;
1230 int target_confirms = 1;
1232 if (params.size() > 0)
1234 uint256 blockId = 0;
1236 blockId.SetHex(params[0].get_str());
1237 pindex = CBlockLocator(blockId).GetBlockIndex();
1240 if (params.size() > 1)
1242 target_confirms = params[1].get_int();
1244 if (target_confirms < 1)
1245 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter");
1248 int depth = pindex ? (1 + nBestHeight - pindex->nHeight) : -1;
1252 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); it++)
1254 CWalletTx tx = (*it).second;
1256 if (depth == -1 || tx.GetDepthInMainChain() < depth)
1257 ListTransactions(tx, "*", 0, true, transactions);
1262 if (target_confirms == 1)
1264 lastblock = hashBestChain;
1268 int target_height = pindexBest->nHeight + 1 - target_confirms;
1271 for (block = pindexBest;
1272 block && block->nHeight > target_height;
1273 block = block->pprev) { }
1275 lastblock = block ? block->GetBlockHash() : 0;
1279 ret.push_back(Pair("transactions", transactions));
1280 ret.push_back(Pair("lastblock", lastblock.GetHex()));
1285 Value gettransaction(const Array& params, bool fHelp)
1287 if (fHelp || params.size() != 1)
1288 throw runtime_error(
1289 "gettransaction <txid>\n"
1290 "Get detailed information about <txid>");
1293 hash.SetHex(params[0].get_str());
1297 if (pwalletMain->mapWallet.count(hash))
1299 const CWalletTx& wtx = pwalletMain->mapWallet[hash];
1301 TxToJSON(wtx, 0, entry);
1303 int64 nCredit = wtx.GetCredit();
1304 int64 nDebit = wtx.GetDebit();
1305 int64 nNet = nCredit - nDebit;
1306 int64 nFee = (wtx.IsFromMe() ? wtx.GetValueOut() - nDebit : 0);
1308 entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee)));
1310 entry.push_back(Pair("fee", ValueFromAmount(nFee)));
1312 WalletTxToJSON(wtx, entry);
1315 ListTransactions(pwalletMain->mapWallet[hash], "*", 0, false, details);
1316 entry.push_back(Pair("details", details));
1318 CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
1320 string strHex = HexStr(ssTx.begin(), ssTx.end());
1321 entry.push_back(Pair("hex", strHex));
1326 uint256 hashBlock = 0;
1327 if (GetTransaction(hash, tx, hashBlock, true))
1329 TxToJSON(tx, 0, entry);
1331 entry.push_back(Pair("confirmations", 0));
1334 entry.push_back(Pair("blockhash", hashBlock.GetHex()));
1335 map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashBlock);
1336 if (mi != mapBlockIndex.end() && (*mi).second)
1338 CBlockIndex* pindex = (*mi).second;
1339 if (pindex->IsInMainChain())
1340 entry.push_back(Pair("confirmations", 1 + nBestHeight - pindex->nHeight));
1342 entry.push_back(Pair("confirmations", 0));
1347 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction");
1354 Value backupwallet(const Array& params, bool fHelp)
1356 if (fHelp || params.size() != 1)
1357 throw runtime_error(
1358 "backupwallet <destination>\n"
1359 "Safely copies wallet.dat to destination, which can be a directory or a path with filename.");
1361 string strDest = params[0].get_str();
1362 if (!BackupWallet(*pwalletMain, strDest))
1363 throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet backup failed!");
1369 Value keypoolrefill(const Array& params, bool fHelp)
1371 if (fHelp || params.size() > 1)
1372 throw runtime_error(
1373 "keypoolrefill [new-size]\n"
1374 "Fills the keypool."
1375 + HelpRequiringPassphrase());
1377 unsigned int nSize = max(GetArg("-keypool", 100), 0LL);
1378 if (params.size() > 0) {
1379 if (params[0].get_int() < 0)
1380 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected valid size");
1381 nSize = (unsigned int) params[0].get_int();
1384 EnsureWalletIsUnlocked();
1386 pwalletMain->TopUpKeyPool(nSize);
1388 if (pwalletMain->GetKeyPoolSize() < nSize)
1389 throw JSONRPCError(RPC_WALLET_ERROR, "Error refreshing keypool.");
1395 void ThreadTopUpKeyPool(void* parg)
1397 // Make this thread recognisable as the key-topping-up thread
1398 RenameThread("bitcoin-key-top");
1400 pwalletMain->TopUpKeyPool();
1403 void ThreadCleanWalletPassphrase(void* parg)
1405 // Make this thread recognisable as the wallet relocking thread
1406 RenameThread("bitcoin-lock-wa");
1408 int64 nMyWakeTime = GetTimeMillis() + *((int64*)parg) * 1000;
1410 ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime);
1412 if (nWalletUnlockTime == 0)
1414 nWalletUnlockTime = nMyWakeTime;
1418 if (nWalletUnlockTime==0)
1420 int64 nToSleep = nWalletUnlockTime - GetTimeMillis();
1424 LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime);
1426 ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime);
1430 if (nWalletUnlockTime)
1432 nWalletUnlockTime = 0;
1433 pwalletMain->Lock();
1438 if (nWalletUnlockTime < nMyWakeTime)
1439 nWalletUnlockTime = nMyWakeTime;
1442 LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime);
1444 delete (int64*)parg;
1447 Value walletpassphrase(const Array& params, bool fHelp)
1449 if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 3))
1450 throw runtime_error(
1451 "walletpassphrase <passphrase> <timeout> [mintonly]\n"
1452 "Stores the wallet decryption key in memory for <timeout> seconds.\n"
1453 "mintonly is optional true/false allowing only block minting.");
1456 if (!pwalletMain->IsCrypted())
1457 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called.");
1459 if (!pwalletMain->IsLocked())
1460 throw JSONRPCError(RPC_WALLET_ALREADY_UNLOCKED, "Error: Wallet is already unlocked, use walletlock first if need to change unlock settings.");
1461 // Note that the walletpassphrase is stored in params[0] which is not mlock()ed
1462 SecureString strWalletPass;
1463 strWalletPass.reserve(100);
1464 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1465 // Alternately, find a way to make params[0] mlock()'d to begin with.
1466 strWalletPass = params[0].get_str().c_str();
1468 if (strWalletPass.length() > 0)
1470 if (!pwalletMain->Unlock(strWalletPass))
1471 throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
1474 throw runtime_error(
1475 "walletpassphrase <passphrase> <timeout>\n"
1476 "Stores the wallet decryption key in memory for <timeout> seconds.");
1478 NewThread(ThreadTopUpKeyPool, NULL);
1479 int64* pnSleepTime = new int64(params[1].get_int64());
1480 NewThread(ThreadCleanWalletPassphrase, pnSleepTime);
1482 // ppcoin: if user OS account compromised prevent trivial sendmoney commands
1483 if (params.size() > 2)
1484 fWalletUnlockMintOnly = params[2].get_bool();
1486 fWalletUnlockMintOnly = false;
1492 Value walletpassphrasechange(const Array& params, bool fHelp)
1494 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2))
1495 throw runtime_error(
1496 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1497 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1500 if (!pwalletMain->IsCrypted())
1501 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrasechange was called.");
1503 // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string)
1504 // Alternately, find a way to make params[0] mlock()'d to begin with.
1505 SecureString strOldWalletPass;
1506 strOldWalletPass.reserve(100);
1507 strOldWalletPass = params[0].get_str().c_str();
1509 SecureString strNewWalletPass;
1510 strNewWalletPass.reserve(100);
1511 strNewWalletPass = params[1].get_str().c_str();
1513 if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1)
1514 throw runtime_error(
1515 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1516 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1518 if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass))
1519 throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
1525 Value walletlock(const Array& params, bool fHelp)
1527 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 0))
1528 throw runtime_error(
1530 "Removes the wallet encryption key from memory, locking the wallet.\n"
1531 "After calling this method, you will need to call walletpassphrase again\n"
1532 "before being able to call any methods which require the wallet to be unlocked.");
1535 if (!pwalletMain->IsCrypted())
1536 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletlock was called.");
1539 LOCK(cs_nWalletUnlockTime);
1540 pwalletMain->Lock();
1541 nWalletUnlockTime = 0;
1548 Value encryptwallet(const Array& params, bool fHelp)
1550 if (!pwalletMain->IsCrypted() && (fHelp || params.size() != 1))
1551 throw runtime_error(
1552 "encryptwallet <passphrase>\n"
1553 "Encrypts the wallet with <passphrase>.");
1556 if (pwalletMain->IsCrypted())
1557 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an encrypted wallet, but encryptwallet was called.");
1559 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1560 // Alternately, find a way to make params[0] mlock()'d to begin with.
1561 SecureString strWalletPass;
1562 strWalletPass.reserve(100);
1563 strWalletPass = params[0].get_str().c_str();
1565 if (strWalletPass.length() < 1)
1566 throw runtime_error(
1567 "encryptwallet <passphrase>\n"
1568 "Encrypts the wallet with <passphrase>.");
1570 if (!pwalletMain->EncryptWallet(strWalletPass))
1571 throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: Failed to encrypt the wallet.");
1573 // BDB seems to have a bad habit of writing old data into
1574 // slack space in .dat files; that is bad if the old data is
1575 // unencrypted private keys. So:
1577 return "wallet encrypted; NovaCoin server stopping, restart to run with encrypted wallet. The keypool has been flushed, you need to make a new backup.";
1580 class DescribeAddressVisitor : public boost::static_visitor<Object>
1583 Object operator()(const CNoDestination &dest) const { return Object(); }
1585 Object operator()(const CKeyID &keyID) const {
1588 pwalletMain->GetPubKey(keyID, vchPubKey);
1589 obj.push_back(Pair("isscript", false));
1590 obj.push_back(Pair("pubkey", HexStr(vchPubKey.Raw())));
1591 obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed()));
1595 Object operator()(const CScriptID &scriptID) const {
1597 obj.push_back(Pair("isscript", true));
1599 pwalletMain->GetCScript(scriptID, subscript);
1600 std::vector<CTxDestination> addresses;
1601 txnouttype whichType;
1603 ExtractDestinations(subscript, whichType, addresses, nRequired);
1604 obj.push_back(Pair("script", GetTxnOutputType(whichType)));
1605 obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end())));
1607 BOOST_FOREACH(const CTxDestination& addr, addresses)
1608 a.push_back(CBitcoinAddress(addr).ToString());
1609 obj.push_back(Pair("addresses", a));
1610 if (whichType == TX_MULTISIG)
1611 obj.push_back(Pair("sigsrequired", nRequired));
1616 Value validateaddress(const Array& params, bool fHelp)
1618 if (fHelp || params.size() != 1)
1619 throw runtime_error(
1620 "validateaddress <novacoinaddress>\n"
1621 "Return information about <novacoinaddress>.");
1623 CBitcoinAddress address(params[0].get_str());
1624 bool isValid = address.IsValid();
1627 ret.push_back(Pair("isvalid", isValid));
1630 CTxDestination dest = address.Get();
1631 string currentAddress = address.ToString();
1632 ret.push_back(Pair("address", currentAddress));
1633 bool fMine = IsMine(*pwalletMain, dest);
1634 ret.push_back(Pair("ismine", fMine));
1636 Object detail = boost::apply_visitor(DescribeAddressVisitor(), dest);
1637 ret.insert(ret.end(), detail.begin(), detail.end());
1639 if (pwalletMain->mapAddressBook.count(dest))
1640 ret.push_back(Pair("account", pwalletMain->mapAddressBook[dest]));
1645 Value validatepubkey(const Array& params, bool fHelp)
1647 if (fHelp || !params.size() || params.size() > 2)
1648 throw runtime_error(
1649 "validatepubkey <novacoinpubkey>\n"
1650 "Return information about <novacoinpubkey>.");
1652 std::vector<unsigned char> vchPubKey = ParseHex(params[0].get_str());
1653 CPubKey pubKey(vchPubKey);
1655 bool isValid = pubKey.IsValid();
1656 bool isCompressed = pubKey.IsCompressed();
1657 CKeyID keyID = pubKey.GetID();
1659 CBitcoinAddress address;
1663 ret.push_back(Pair("isvalid", isValid));
1666 CTxDestination dest = address.Get();
1667 string currentAddress = address.ToString();
1668 ret.push_back(Pair("address", currentAddress));
1669 bool fMine = IsMine(*pwalletMain, dest);
1670 ret.push_back(Pair("ismine", fMine));
1671 ret.push_back(Pair("iscompressed", isCompressed));
1673 Object detail = boost::apply_visitor(DescribeAddressVisitor(), dest);
1674 ret.insert(ret.end(), detail.begin(), detail.end());
1676 if (pwalletMain->mapAddressBook.count(dest))
1677 ret.push_back(Pair("account", pwalletMain->mapAddressBook[dest]));
1682 // reserve balance from being staked for network protection
1683 Value reservebalance(const Array& params, bool fHelp)
1685 if (fHelp || params.size() > 2)
1686 throw runtime_error(
1687 "reservebalance [<reserve> [amount]]\n"
1688 "<reserve> is true or false to turn balance reserve on or off.\n"
1689 "<amount> is a real and rounded to cent.\n"
1690 "Set reserve amount not participating in network protection.\n"
1691 "If no parameters provided current setting is printed.\n");
1693 if (params.size() > 0)
1695 bool fReserve = params[0].get_bool();
1698 if (params.size() == 1)
1699 throw runtime_error("must provide amount to reserve balance.\n");
1700 int64 nAmount = AmountFromValue(params[1]);
1701 nAmount = (nAmount / CENT) * CENT; // round to cent
1703 throw runtime_error("amount cannot be negative.\n");
1704 mapArgs["-reservebalance"] = FormatMoney(nAmount).c_str();
1708 if (params.size() > 1)
1709 throw runtime_error("cannot specify amount to turn off reserve.\n");
1710 mapArgs["-reservebalance"] = "0";
1715 int64 nReserveBalance = 0;
1716 if (mapArgs.count("-reservebalance") && !ParseMoney(mapArgs["-reservebalance"], nReserveBalance))
1717 throw runtime_error("invalid reserve balance amount\n");
1718 result.push_back(Pair("reserve", (nReserveBalance > 0)));
1719 result.push_back(Pair("amount", ValueFromAmount(nReserveBalance)));
1724 // check wallet integrity
1725 Value checkwallet(const Array& params, bool fHelp)
1727 if (fHelp || params.size() > 0)
1728 throw runtime_error(
1730 "Check wallet for integrity.\n");
1733 int64 nBalanceInQuestion;
1734 pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion, true);
1736 if (nMismatchSpent == 0)
1737 result.push_back(Pair("wallet check passed", true));
1740 result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1741 result.push_back(Pair("amount in question", ValueFromAmount(nBalanceInQuestion)));
1748 Value repairwallet(const Array& params, bool fHelp)
1750 if (fHelp || params.size() > 0)
1751 throw runtime_error(
1753 "Repair wallet if checkwallet reports any problem.\n");
1756 int64 nBalanceInQuestion;
1757 pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion);
1759 if (nMismatchSpent == 0)
1760 result.push_back(Pair("wallet check passed", true));
1763 result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1764 result.push_back(Pair("amount affected by repair", ValueFromAmount(nBalanceInQuestion)));
1769 // resend unconfirmed wallet transactions
1770 Value resendtx(const Array& params, bool fHelp)
1772 if (fHelp || params.size() > 1)
1773 throw runtime_error(
1775 "Re-send unconfirmed transactions.\n"
1778 ResendWalletTransactions();
1783 // make a public-private key pair
1784 Value makekeypair(const Array& params, bool fHelp)
1786 if (fHelp || params.size() > 1)
1787 throw runtime_error(
1788 "makekeypair [prefix]\n"
1789 "Make a public/private key pair.\n"
1790 "[prefix] is optional preferred prefix for the public key.\n");
1792 string strPrefix = "";
1793 if (params.size() > 0)
1794 strPrefix = params[0].get_str();
1797 key.MakeNewKey(false);
1799 CPrivKey vchPrivKey = key.GetPrivKey();
1801 result.push_back(Pair("PrivateKey", HexStr<CPrivKey::iterator>(vchPrivKey.begin(), vchPrivKey.end())));
1802 result.push_back(Pair("PublicKey", HexStr(key.GetPubKey().Raw())));