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()));
85 obj.push_back(Pair("difficulty", (double)GetDifficulty()));
86 obj.push_back(Pair("testnet", fTestNet));
87 obj.push_back(Pair("keypoololdest", (boost::int64_t)pwalletMain->GetOldestKeyPoolTime()));
88 obj.push_back(Pair("keypoolsize", (int)pwalletMain->GetKeyPoolSize()));
89 obj.push_back(Pair("paytxfee", ValueFromAmount(nTransactionFee)));
90 if (pwalletMain->IsCrypted())
91 obj.push_back(Pair("unlocked_until", (boost::int64_t)nWalletUnlockTime / 1000));
92 obj.push_back(Pair("errors", GetWarnings("statusbar")));
97 Value getnewpubkey(const Array& params, bool fHelp)
99 if (fHelp || params.size() > 1)
101 "getnewpubkey [account]\n"
102 "Returns new public key for coinbase generation.");
104 // Parse the account first so we don't generate a key if there's an error
106 if (params.size() > 0)
107 strAccount = AccountFromValue(params[0]);
109 if (!pwalletMain->IsLocked())
110 pwalletMain->TopUpKeyPool();
112 // Generate a new key that is added to wallet
114 if (!pwalletMain->GetKeyFromPool(newKey, false))
115 throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
116 CKeyID keyID = newKey.GetID();
118 pwalletMain->SetAddressBookName(keyID, strAccount);
119 vector<unsigned char> vchPubKey = newKey.Raw();
121 return HexStr(vchPubKey.begin(), vchPubKey.end());
125 Value getnewaddress(const Array& params, bool fHelp)
127 if (fHelp || params.size() > 1)
129 "getnewaddress [account]\n"
130 "Returns a new NovaCoin address for receiving payments. "
131 "If [account] is specified (recommended), it is added to the address book "
132 "so payments received with the address will be credited to [account].");
134 // Parse the account first so we don't generate a key if there's an error
136 if (params.size() > 0)
137 strAccount = AccountFromValue(params[0]);
139 if (!pwalletMain->IsLocked())
140 pwalletMain->TopUpKeyPool();
142 // Generate a new key that is added to wallet
144 if (!pwalletMain->GetKeyFromPool(newKey, false))
145 throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
146 CKeyID keyID = newKey.GetID();
148 pwalletMain->SetAddressBookName(keyID, strAccount);
150 return CBitcoinAddress(keyID).ToString();
154 CBitcoinAddress GetAccountAddress(string strAccount, bool bForceNew=false)
156 CWalletDB walletdb(pwalletMain->strWalletFile);
159 walletdb.ReadAccount(strAccount, account);
161 bool bKeyUsed = false;
163 // Check if the current key has been used
164 if (account.vchPubKey.IsValid())
166 CScript scriptPubKey;
167 scriptPubKey.SetDestination(account.vchPubKey.GetID());
168 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin();
169 it != pwalletMain->mapWallet.end() && account.vchPubKey.IsValid();
172 const CWalletTx& wtx = (*it).second;
173 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
174 if (txout.scriptPubKey == scriptPubKey)
179 // Generate a new key
180 if (!account.vchPubKey.IsValid() || bForceNew || bKeyUsed)
182 if (!pwalletMain->GetKeyFromPool(account.vchPubKey, false))
183 throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
185 pwalletMain->SetAddressBookName(account.vchPubKey.GetID(), strAccount);
186 walletdb.WriteAccount(strAccount, account);
189 return CBitcoinAddress(account.vchPubKey.GetID());
192 Value getaccountaddress(const Array& params, bool fHelp)
194 if (fHelp || params.size() != 1)
196 "getaccountaddress <account>\n"
197 "Returns the current NovaCoin address for receiving payments to this account.");
199 // Parse the account first so we don't generate a key if there's an error
200 string strAccount = AccountFromValue(params[0]);
204 ret = GetAccountAddress(strAccount).ToString();
211 Value setaccount(const Array& params, bool fHelp)
213 if (fHelp || params.size() < 1 || params.size() > 2)
215 "setaccount <novacoinaddress> <account>\n"
216 "Sets the account associated with the given address.");
218 CBitcoinAddress address(params[0].get_str());
219 if (!address.IsValid())
220 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
224 if (params.size() > 1)
225 strAccount = AccountFromValue(params[1]);
227 // Detect when changing the account of an address that is the 'unused current key' of another account:
228 if (pwalletMain->mapAddressBook.count(address.Get()))
230 string strOldAccount = pwalletMain->mapAddressBook[address.Get()];
231 if (address == GetAccountAddress(strOldAccount))
232 GetAccountAddress(strOldAccount, true);
235 pwalletMain->SetAddressBookName(address.Get(), strAccount);
241 Value getaccount(const Array& params, bool fHelp)
243 if (fHelp || params.size() != 1)
245 "getaccount <novacoinaddress>\n"
246 "Returns the account associated with the given address.");
248 CBitcoinAddress address(params[0].get_str());
249 if (!address.IsValid())
250 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
253 map<CTxDestination, string>::iterator mi = pwalletMain->mapAddressBook.find(address.Get());
254 if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.empty())
255 strAccount = (*mi).second;
260 Value getaddressesbyaccount(const Array& params, bool fHelp)
262 if (fHelp || params.size() != 1)
264 "getaddressesbyaccount <account>\n"
265 "Returns the list of addresses for the given account.");
267 string strAccount = AccountFromValue(params[0]);
269 // Find all addresses that have the given account
271 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
273 const CBitcoinAddress& address = item.first;
274 const string& strName = item.second;
275 if (strName == strAccount)
276 ret.push_back(address.ToString());
281 Value sendtoaddress(const Array& params, bool fHelp)
283 if (fHelp || params.size() < 2 || params.size() > 4)
285 "sendtoaddress <novacoinaddress> <amount> [comment] [comment-to]\n"
286 "<amount> is a real and is rounded to the nearest 0.000001"
287 + HelpRequiringPassphrase());
289 CBitcoinAddress address(params[0].get_str());
290 if (!address.IsValid())
291 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
294 int64 nAmount = AmountFromValue(params[1]);
296 if (nAmount < MIN_TXOUT_AMOUNT)
297 throw JSONRPCError(-101, "Send amount too small");
301 if (params.size() > 2 && params[2].type() != null_type && !params[2].get_str().empty())
302 wtx.mapValue["comment"] = params[2].get_str();
303 if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
304 wtx.mapValue["to"] = params[3].get_str();
306 if (pwalletMain->IsLocked())
307 throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first.");
309 string strError = pwalletMain->SendMoneyToDestination(address.Get(), nAmount, wtx);
311 throw JSONRPCError(RPC_WALLET_ERROR, strError);
313 return wtx.GetHash().GetHex();
316 Value listaddressgroupings(const Array& params, bool fHelp)
320 "listaddressgroupings\n"
321 "Lists groups of addresses which have had their common ownership\n"
322 "made public by common use as inputs or as the resulting change\n"
323 "in past transactions");
326 map<CTxDestination, int64> balances = pwalletMain->GetAddressBalances();
327 BOOST_FOREACH(set<CTxDestination> grouping, pwalletMain->GetAddressGroupings())
330 BOOST_FOREACH(CTxDestination address, grouping)
333 addressInfo.push_back(CBitcoinAddress(address).ToString());
334 addressInfo.push_back(ValueFromAmount(balances[address]));
336 LOCK(pwalletMain->cs_wallet);
337 if (pwalletMain->mapAddressBook.find(CBitcoinAddress(address).Get()) != pwalletMain->mapAddressBook.end())
338 addressInfo.push_back(pwalletMain->mapAddressBook.find(CBitcoinAddress(address).Get())->second);
340 jsonGrouping.push_back(addressInfo);
342 jsonGroupings.push_back(jsonGrouping);
344 return jsonGroupings;
347 Value signmessage(const Array& params, bool fHelp)
349 if (fHelp || params.size() != 2)
351 "signmessage <novacoinaddress> <message>\n"
352 "Sign a message with the private key of an address");
354 EnsureWalletIsUnlocked();
356 string strAddress = params[0].get_str();
357 string strMessage = params[1].get_str();
359 CBitcoinAddress addr(strAddress);
361 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
364 if (!addr.GetKeyID(keyID))
365 throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
368 if (!pwalletMain->GetKey(keyID, key))
369 throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available");
371 CDataStream ss(SER_GETHASH, 0);
372 ss << strMessageMagic;
375 vector<unsigned char> vchSig;
376 if (!key.SignCompact(Hash(ss.begin(), ss.end()), vchSig))
377 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed");
379 return EncodeBase64(&vchSig[0], vchSig.size());
382 Value verifymessage(const Array& params, bool fHelp)
384 if (fHelp || params.size() != 3)
386 "verifymessage <novacoinaddress> <signature> <message>\n"
387 "Verify a signed message");
389 string strAddress = params[0].get_str();
390 string strSign = params[1].get_str();
391 string strMessage = params[2].get_str();
393 CBitcoinAddress addr(strAddress);
395 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
398 if (!addr.GetKeyID(keyID))
399 throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
401 bool fInvalid = false;
402 vector<unsigned char> vchSig = DecodeBase64(strSign.c_str(), &fInvalid);
405 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Malformed base64 encoding");
407 CDataStream ss(SER_GETHASH, 0);
408 ss << strMessageMagic;
412 if (!key.SetCompactSignature(Hash(ss.begin(), ss.end()), vchSig))
415 return (key.GetPubKey().GetID() == keyID);
419 Value getreceivedbyaddress(const Array& params, bool fHelp)
421 if (fHelp || params.size() < 1 || params.size() > 2)
423 "getreceivedbyaddress <novacoinaddress> [minconf=1]\n"
424 "Returns the total amount received by <novacoinaddress> in transactions with at least [minconf] confirmations.");
427 CBitcoinAddress address = CBitcoinAddress(params[0].get_str());
428 CScript scriptPubKey;
429 if (!address.IsValid())
430 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
431 scriptPubKey.SetDestination(address.Get());
432 if (!IsMine(*pwalletMain,scriptPubKey))
435 // Minimum confirmations
437 if (params.size() > 1)
438 nMinDepth = params[1].get_int();
442 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
444 const CWalletTx& wtx = (*it).second;
445 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
448 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
449 if (txout.scriptPubKey == scriptPubKey)
450 if (wtx.GetDepthInMainChain() >= nMinDepth)
451 nAmount += txout.nValue;
454 return ValueFromAmount(nAmount);
458 void GetAccountAddresses(string strAccount, set<CTxDestination>& setAddress)
460 BOOST_FOREACH(const PAIRTYPE(CTxDestination, string)& item, pwalletMain->mapAddressBook)
462 const CTxDestination& address = item.first;
463 const string& strName = item.second;
464 if (strName == strAccount)
465 setAddress.insert(address);
469 Value getreceivedbyaccount(const Array& params, bool fHelp)
471 if (fHelp || params.size() < 1 || params.size() > 2)
473 "getreceivedbyaccount <account> [minconf=1]\n"
474 "Returns the total amount received by addresses with <account> in transactions with at least [minconf] confirmations.");
476 // Minimum confirmations
478 if (params.size() > 1)
479 nMinDepth = params[1].get_int();
481 // Get the set of pub keys assigned to account
482 string strAccount = AccountFromValue(params[0]);
483 set<CTxDestination> setAddress;
484 GetAccountAddresses(strAccount, setAddress);
488 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
490 const CWalletTx& wtx = (*it).second;
491 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
494 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
496 CTxDestination address;
497 if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*pwalletMain, address) && setAddress.count(address))
498 if (wtx.GetDepthInMainChain() >= nMinDepth)
499 nAmount += txout.nValue;
503 return (double)nAmount / (double)COIN;
507 int64 GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth)
511 // Tally wallet transactions
512 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
514 const CWalletTx& wtx = (*it).second;
518 int64 nGenerated, nReceived, nSent, nFee;
519 wtx.GetAccountAmounts(strAccount, nGenerated, nReceived, nSent, nFee);
521 if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth)
522 nBalance += nReceived;
523 nBalance += nGenerated - nSent - nFee;
526 // Tally internal accounting entries
527 nBalance += walletdb.GetAccountCreditDebit(strAccount);
532 int64 GetAccountBalance(const string& strAccount, int nMinDepth)
534 CWalletDB walletdb(pwalletMain->strWalletFile);
535 return GetAccountBalance(walletdb, strAccount, nMinDepth);
539 Value getbalance(const Array& params, bool fHelp)
541 if (fHelp || params.size() > 2)
543 "getbalance [account] [minconf=1]\n"
544 "If [account] is not specified, returns the server's total available balance.\n"
545 "If [account] is specified, returns the balance in the account.");
547 if (params.size() == 0)
548 return ValueFromAmount(pwalletMain->GetBalance());
551 if (params.size() > 1)
552 nMinDepth = params[1].get_int();
554 if (params[0].get_str() == "*") {
555 // Calculate total balance a different way from GetBalance()
556 // (GetBalance() sums up all unspent TxOuts)
557 // getbalance and getbalance '*' 0 should return the same number.
559 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
561 const CWalletTx& wtx = (*it).second;
562 if (!wtx.IsConfirmed())
565 int64 allGeneratedImmature, allGeneratedMature, allFee;
566 allGeneratedImmature = allGeneratedMature = allFee = 0;
568 string strSentAccount;
569 list<pair<CTxDestination, int64> > listReceived;
570 list<pair<CTxDestination, int64> > listSent;
571 wtx.GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount);
572 if (wtx.GetDepthInMainChain() >= nMinDepth)
574 BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64)& r, listReceived)
575 nBalance += r.second;
577 BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64)& r, listSent)
578 nBalance -= r.second;
580 nBalance += allGeneratedMature;
582 return ValueFromAmount(nBalance);
585 string strAccount = AccountFromValue(params[0]);
587 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
589 return ValueFromAmount(nBalance);
593 Value movecmd(const Array& params, bool fHelp)
595 if (fHelp || params.size() < 3 || params.size() > 5)
597 "move <fromaccount> <toaccount> <amount> [minconf=1] [comment]\n"
598 "Move from one account in your wallet to another.");
600 string strFrom = AccountFromValue(params[0]);
601 string strTo = AccountFromValue(params[1]);
602 int64 nAmount = AmountFromValue(params[2]);
604 if (nAmount < MIN_TXOUT_AMOUNT)
605 throw JSONRPCError(-101, "Send amount too small");
607 if (params.size() > 3)
608 // unused parameter, used to be nMinDepth, keep type-checking it though
609 (void)params[3].get_int();
611 if (params.size() > 4)
612 strComment = params[4].get_str();
614 CWalletDB walletdb(pwalletMain->strWalletFile);
615 if (!walletdb.TxnBegin())
616 throw JSONRPCError(RPC_DATABASE_ERROR, "database error");
618 int64 nNow = GetAdjustedTime();
621 CAccountingEntry debit;
622 debit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb);
623 debit.strAccount = strFrom;
624 debit.nCreditDebit = -nAmount;
626 debit.strOtherAccount = strTo;
627 debit.strComment = strComment;
628 walletdb.WriteAccountingEntry(debit);
631 CAccountingEntry credit;
632 credit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb);
633 credit.strAccount = strTo;
634 credit.nCreditDebit = nAmount;
636 credit.strOtherAccount = strFrom;
637 credit.strComment = strComment;
638 walletdb.WriteAccountingEntry(credit);
640 if (!walletdb.TxnCommit())
641 throw JSONRPCError(RPC_DATABASE_ERROR, "database error");
647 Value sendfrom(const Array& params, bool fHelp)
649 if (fHelp || params.size() < 3 || params.size() > 6)
651 "sendfrom <fromaccount> <tonovacoinaddress> <amount> [minconf=1] [comment] [comment-to]\n"
652 "<amount> is a real and is rounded to the nearest 0.000001"
653 + HelpRequiringPassphrase());
655 string strAccount = AccountFromValue(params[0]);
656 CBitcoinAddress address(params[1].get_str());
657 if (!address.IsValid())
658 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
659 int64 nAmount = AmountFromValue(params[2]);
661 if (nAmount < MIN_TXOUT_AMOUNT)
662 throw JSONRPCError(-101, "Send amount too small");
665 if (params.size() > 3)
666 nMinDepth = params[3].get_int();
669 wtx.strFromAccount = strAccount;
670 if (params.size() > 4 && params[4].type() != null_type && !params[4].get_str().empty())
671 wtx.mapValue["comment"] = params[4].get_str();
672 if (params.size() > 5 && params[5].type() != null_type && !params[5].get_str().empty())
673 wtx.mapValue["to"] = params[5].get_str();
675 EnsureWalletIsUnlocked();
678 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
679 if (nAmount > nBalance)
680 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds");
683 string strError = pwalletMain->SendMoneyToDestination(address.Get(), nAmount, wtx);
685 throw JSONRPCError(RPC_WALLET_ERROR, strError);
687 return wtx.GetHash().GetHex();
691 Value sendmany(const Array& params, bool fHelp)
693 if (fHelp || params.size() < 2 || params.size() > 4)
695 "sendmany <fromaccount> {address:amount,...} [minconf=1] [comment]\n"
696 "amounts are double-precision floating point numbers"
697 + HelpRequiringPassphrase());
699 string strAccount = AccountFromValue(params[0]);
700 Object sendTo = params[1].get_obj();
702 if (params.size() > 2)
703 nMinDepth = params[2].get_int();
706 wtx.strFromAccount = strAccount;
707 if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
708 wtx.mapValue["comment"] = params[3].get_str();
710 set<CBitcoinAddress> setAddress;
711 vector<pair<CScript, int64> > vecSend;
713 int64 totalAmount = 0;
714 BOOST_FOREACH(const Pair& s, sendTo)
716 CBitcoinAddress address(s.name_);
717 if (!address.IsValid())
718 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid NovaCoin address: ")+s.name_);
720 if (setAddress.count(address))
721 throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+s.name_);
722 setAddress.insert(address);
724 CScript scriptPubKey;
725 scriptPubKey.SetDestination(address.Get());
726 int64 nAmount = AmountFromValue(s.value_);
728 if (nAmount < MIN_TXOUT_AMOUNT)
729 throw JSONRPCError(-101, "Send amount too small");
731 totalAmount += nAmount;
733 vecSend.push_back(make_pair(scriptPubKey, nAmount));
736 EnsureWalletIsUnlocked();
739 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
740 if (totalAmount > nBalance)
741 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds");
744 CReserveKey keyChange(pwalletMain);
745 int64 nFeeRequired = 0;
746 bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired);
749 if (totalAmount + nFeeRequired > pwalletMain->GetBalance())
750 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds");
751 throw JSONRPCError(RPC_WALLET_ERROR, "Transaction creation failed");
753 if (!pwalletMain->CommitTransaction(wtx, keyChange))
754 throw JSONRPCError(RPC_WALLET_ERROR, "Transaction commit failed");
756 return wtx.GetHash().GetHex();
759 Value addmultisigaddress(const Array& params, bool fHelp)
761 if (fHelp || params.size() < 2 || params.size() > 3)
763 string msg = "addmultisigaddress <nrequired> <'[\"key\",\"key\"]'> [account]\n"
764 "Add a nrequired-to-sign multisignature address to the wallet\"\n"
765 "each key is a NovaCoin address or hex-encoded public key\n"
766 "If [account] is specified, assign address to [account].";
767 throw runtime_error(msg);
770 int nRequired = params[0].get_int();
771 const Array& keys = params[1].get_array();
773 if (params.size() > 2)
774 strAccount = AccountFromValue(params[2]);
776 // Gather public keys
778 throw runtime_error("a multisignature address must require at least one key to redeem");
779 if ((int)keys.size() < nRequired)
781 strprintf("not enough keys supplied "
782 "(got %"PRIszu" keys, but need at least %d to redeem)", keys.size(), nRequired));
783 std::vector<CKey> pubkeys;
784 pubkeys.resize(keys.size());
785 for (unsigned int i = 0; i < keys.size(); i++)
787 const std::string& ks = keys[i].get_str();
789 // Case 1: Bitcoin address and we have full public key:
790 CBitcoinAddress address(ks);
791 if (address.IsValid())
794 if (!address.GetKeyID(keyID))
796 strprintf("%s does not refer to a key",ks.c_str()));
798 if (!pwalletMain->GetPubKey(keyID, vchPubKey))
800 strprintf("no full public key for address %s",ks.c_str()));
801 if (!vchPubKey.IsValid() || !pubkeys[i].SetPubKey(vchPubKey))
802 throw runtime_error(" Invalid public key: "+ks);
805 // Case 2: hex public key
808 CPubKey vchPubKey(ParseHex(ks));
809 if (!vchPubKey.IsValid() || !pubkeys[i].SetPubKey(vchPubKey))
810 throw runtime_error(" Invalid public key: "+ks);
814 throw runtime_error(" Invalid public key: "+ks);
818 // Construct using pay-to-script-hash:
820 inner.SetMultisig(nRequired, pubkeys);
821 CScriptID innerID = inner.GetID();
822 pwalletMain->AddCScript(inner);
824 pwalletMain->SetAddressBookName(innerID, strAccount);
825 return CBitcoinAddress(innerID).ToString();
828 Value addredeemscript(const Array& params, bool fHelp)
830 if (fHelp || params.size() < 1 || params.size() > 2)
832 string msg = "addredeemscript <redeemScript> [account]\n"
833 "Add a P2SH address with a specified redeemScript to the wallet.\n"
834 "If [account] is specified, assign address to [account].";
835 throw runtime_error(msg);
839 if (params.size() > 1)
840 strAccount = AccountFromValue(params[1]);
842 // Construct using pay-to-script-hash:
843 vector<unsigned char> innerData = ParseHexV(params[0], "redeemScript");
844 CScript inner(innerData.begin(), innerData.end());
845 CScriptID innerID = inner.GetID();
846 pwalletMain->AddCScript(inner);
848 pwalletMain->SetAddressBookName(innerID, strAccount);
849 return CBitcoinAddress(innerID).ToString();
859 nConf = std::numeric_limits<int>::max();
863 Value ListReceived(const Array& params, bool fByAccounts)
865 // Minimum confirmations
867 if (params.size() > 0)
868 nMinDepth = params[0].get_int();
870 // Whether to include empty accounts
871 bool fIncludeEmpty = false;
872 if (params.size() > 1)
873 fIncludeEmpty = params[1].get_bool();
876 map<CBitcoinAddress, tallyitem> mapTally;
877 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
879 const CWalletTx& wtx = (*it).second;
881 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
884 int nDepth = wtx.GetDepthInMainChain();
885 if (nDepth < nMinDepth)
888 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
890 CTxDestination address;
891 if (!ExtractDestination(txout.scriptPubKey, address) || !IsMine(*pwalletMain, address))
894 tallyitem& item = mapTally[address];
895 item.nAmount += txout.nValue;
896 item.nConf = min(item.nConf, nDepth);
902 map<string, tallyitem> mapAccountTally;
903 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
905 const CBitcoinAddress& address = item.first;
906 const string& strAccount = item.second;
907 map<CBitcoinAddress, tallyitem>::iterator it = mapTally.find(address);
908 if (it == mapTally.end() && !fIncludeEmpty)
912 int nConf = std::numeric_limits<int>::max();
913 if (it != mapTally.end())
915 nAmount = (*it).second.nAmount;
916 nConf = (*it).second.nConf;
921 tallyitem& item = mapAccountTally[strAccount];
922 item.nAmount += nAmount;
923 item.nConf = min(item.nConf, nConf);
928 obj.push_back(Pair("address", address.ToString()));
929 obj.push_back(Pair("account", strAccount));
930 obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
931 obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf)));
938 for (map<string, tallyitem>::iterator it = mapAccountTally.begin(); it != mapAccountTally.end(); ++it)
940 int64 nAmount = (*it).second.nAmount;
941 int nConf = (*it).second.nConf;
943 obj.push_back(Pair("account", (*it).first));
944 obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
945 obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf)));
953 Value listreceivedbyaddress(const Array& params, bool fHelp)
955 if (fHelp || params.size() > 2)
957 "listreceivedbyaddress [minconf=1] [includeempty=false]\n"
958 "[minconf] is the minimum number of confirmations before payments are included.\n"
959 "[includeempty] whether to include addresses that haven't received any payments.\n"
960 "Returns an array of objects containing:\n"
961 " \"address\" : receiving address\n"
962 " \"account\" : the account of the receiving address\n"
963 " \"amount\" : total amount received by the address\n"
964 " \"confirmations\" : number of confirmations of the most recent transaction included");
966 return ListReceived(params, false);
969 Value listreceivedbyaccount(const Array& params, bool fHelp)
971 if (fHelp || params.size() > 2)
973 "listreceivedbyaccount [minconf=1] [includeempty=false]\n"
974 "[minconf] is the minimum number of confirmations before payments are included.\n"
975 "[includeempty] whether to include accounts that haven't received any payments.\n"
976 "Returns an array of objects containing:\n"
977 " \"account\" : the account of the receiving addresses\n"
978 " \"amount\" : total amount received by addresses with this account\n"
979 " \"confirmations\" : number of confirmations of the most recent transaction included");
981 return ListReceived(params, true);
984 void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret)
986 int64 nGeneratedImmature, nGeneratedMature, nFee;
987 string strSentAccount;
988 list<pair<CTxDestination, int64> > listReceived;
989 list<pair<CTxDestination, int64> > listSent;
991 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount);
993 bool fAllAccounts = (strAccount == string("*"));
995 // Generated blocks assigned to account ""
996 if ((nGeneratedMature+nGeneratedImmature) != 0 && (fAllAccounts || strAccount == ""))
999 entry.push_back(Pair("account", string("")));
1000 if (nGeneratedImmature)
1002 entry.push_back(Pair("category", wtx.GetDepthInMainChain() ? "immature" : "orphan"));
1003 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedImmature)));
1007 entry.push_back(Pair("category", "generate"));
1008 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedMature)));
1011 WalletTxToJSON(wtx, entry);
1012 ret.push_back(entry);
1016 if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount))
1018 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& s, listSent)
1021 entry.push_back(Pair("account", strSentAccount));
1022 entry.push_back(Pair("address", CBitcoinAddress(s.first).ToString()));
1023 entry.push_back(Pair("category", "send"));
1024 entry.push_back(Pair("amount", ValueFromAmount(-s.second)));
1025 entry.push_back(Pair("fee", ValueFromAmount(-nFee)));
1027 WalletTxToJSON(wtx, entry);
1028 ret.push_back(entry);
1033 if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth)
1035 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& r, listReceived)
1038 if (pwalletMain->mapAddressBook.count(r.first))
1039 account = pwalletMain->mapAddressBook[r.first];
1040 if (fAllAccounts || (account == strAccount))
1043 entry.push_back(Pair("account", account));
1044 entry.push_back(Pair("address", CBitcoinAddress(r.first).ToString()));
1045 if (wtx.IsCoinBase())
1047 if (wtx.GetDepthInMainChain() < 1)
1048 entry.push_back(Pair("category", "orphan"));
1049 else if (wtx.GetBlocksToMaturity() > 0)
1050 entry.push_back(Pair("category", "immature"));
1052 entry.push_back(Pair("category", "generate"));
1055 entry.push_back(Pair("category", "receive"));
1056 entry.push_back(Pair("amount", ValueFromAmount(r.second)));
1058 WalletTxToJSON(wtx, entry);
1059 ret.push_back(entry);
1065 void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Array& ret)
1067 bool fAllAccounts = (strAccount == string("*"));
1069 if (fAllAccounts || acentry.strAccount == strAccount)
1072 entry.push_back(Pair("account", acentry.strAccount));
1073 entry.push_back(Pair("category", "move"));
1074 entry.push_back(Pair("time", (boost::int64_t)acentry.nTime));
1075 entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit)));
1076 entry.push_back(Pair("otheraccount", acentry.strOtherAccount));
1077 entry.push_back(Pair("comment", acentry.strComment));
1078 ret.push_back(entry);
1082 Value listtransactions(const Array& params, bool fHelp)
1084 if (fHelp || params.size() > 3)
1085 throw runtime_error(
1086 "listtransactions [account] [count=10] [from=0]\n"
1087 "Returns up to [count] most recent transactions skipping the first [from] transactions for account [account].");
1089 string strAccount = "*";
1090 if (params.size() > 0)
1091 strAccount = params[0].get_str();
1093 if (params.size() > 1)
1094 nCount = params[1].get_int();
1096 if (params.size() > 2)
1097 nFrom = params[2].get_int();
1100 throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count");
1102 throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative from");
1106 std::list<CAccountingEntry> acentries;
1107 CWallet::TxItems txOrdered = pwalletMain->OrderedTxItems(acentries, strAccount);
1109 // iterate backwards until we have nCount items to return:
1110 for (CWallet::TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
1112 CWalletTx *const pwtx = (*it).second.first;
1114 ListTransactions(*pwtx, strAccount, 0, true, ret);
1115 CAccountingEntry *const pacentry = (*it).second.second;
1117 AcentryToJSON(*pacentry, strAccount, ret);
1119 if ((int)ret.size() >= (nCount+nFrom)) break;
1121 // ret is newest to oldest
1123 if (nFrom > (int)ret.size())
1125 if ((nFrom + nCount) > (int)ret.size())
1126 nCount = ret.size() - nFrom;
1127 Array::iterator first = ret.begin();
1128 std::advance(first, nFrom);
1129 Array::iterator last = ret.begin();
1130 std::advance(last, nFrom+nCount);
1132 if (last != ret.end()) ret.erase(last, ret.end());
1133 if (first != ret.begin()) ret.erase(ret.begin(), first);
1135 std::reverse(ret.begin(), ret.end()); // Return oldest to newest
1140 Value listaccounts(const Array& params, bool fHelp)
1142 if (fHelp || params.size() > 1)
1143 throw runtime_error(
1144 "listaccounts [minconf=1]\n"
1145 "Returns Object that has account names as keys, account balances as values.");
1148 if (params.size() > 0)
1149 nMinDepth = params[0].get_int();
1151 map<string, int64> mapAccountBalances;
1152 BOOST_FOREACH(const PAIRTYPE(CTxDestination, string)& entry, pwalletMain->mapAddressBook) {
1153 if (IsMine(*pwalletMain, entry.first)) // This address belongs to me
1154 mapAccountBalances[entry.second] = 0;
1157 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
1159 const CWalletTx& wtx = (*it).second;
1160 int64 nGeneratedImmature, nGeneratedMature, nFee;
1161 string strSentAccount;
1162 list<pair<CTxDestination, int64> > listReceived;
1163 list<pair<CTxDestination, int64> > listSent;
1164 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount);
1165 mapAccountBalances[strSentAccount] -= nFee;
1166 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& s, listSent)
1167 mapAccountBalances[strSentAccount] -= s.second;
1168 if (wtx.GetDepthInMainChain() >= nMinDepth)
1170 mapAccountBalances[""] += nGeneratedMature;
1171 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& r, listReceived)
1172 if (pwalletMain->mapAddressBook.count(r.first))
1173 mapAccountBalances[pwalletMain->mapAddressBook[r.first]] += r.second;
1175 mapAccountBalances[""] += r.second;
1179 list<CAccountingEntry> acentries;
1180 CWalletDB(pwalletMain->strWalletFile).ListAccountCreditDebit("*", acentries);
1181 BOOST_FOREACH(const CAccountingEntry& entry, acentries)
1182 mapAccountBalances[entry.strAccount] += entry.nCreditDebit;
1185 BOOST_FOREACH(const PAIRTYPE(string, int64)& accountBalance, mapAccountBalances) {
1186 ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second)));
1191 Value listsinceblock(const Array& params, bool fHelp)
1194 throw runtime_error(
1195 "listsinceblock [blockhash] [target-confirmations]\n"
1196 "Get all transactions in blocks since block [blockhash], or all transactions if omitted");
1198 CBlockIndex *pindex = NULL;
1199 int target_confirms = 1;
1201 if (params.size() > 0)
1203 uint256 blockId = 0;
1205 blockId.SetHex(params[0].get_str());
1206 pindex = CBlockLocator(blockId).GetBlockIndex();
1209 if (params.size() > 1)
1211 target_confirms = params[1].get_int();
1213 if (target_confirms < 1)
1214 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter");
1217 int depth = pindex ? (1 + nBestHeight - pindex->nHeight) : -1;
1221 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); it++)
1223 CWalletTx tx = (*it).second;
1225 if (depth == -1 || tx.GetDepthInMainChain() < depth)
1226 ListTransactions(tx, "*", 0, true, transactions);
1231 if (target_confirms == 1)
1233 lastblock = hashBestChain;
1237 int target_height = pindexBest->nHeight + 1 - target_confirms;
1240 for (block = pindexBest;
1241 block && block->nHeight > target_height;
1242 block = block->pprev) { }
1244 lastblock = block ? block->GetBlockHash() : 0;
1248 ret.push_back(Pair("transactions", transactions));
1249 ret.push_back(Pair("lastblock", lastblock.GetHex()));
1254 Value gettransaction(const Array& params, bool fHelp)
1256 if (fHelp || params.size() != 1)
1257 throw runtime_error(
1258 "gettransaction <txid>\n"
1259 "Get detailed information about <txid>");
1262 hash.SetHex(params[0].get_str());
1266 if (pwalletMain->mapWallet.count(hash))
1268 const CWalletTx& wtx = pwalletMain->mapWallet[hash];
1270 TxToJSON(wtx, 0, entry);
1272 int64 nCredit = wtx.GetCredit();
1273 int64 nDebit = wtx.GetDebit();
1274 int64 nNet = nCredit - nDebit;
1275 int64 nFee = (wtx.IsFromMe() ? wtx.GetValueOut() - nDebit : 0);
1277 entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee)));
1279 entry.push_back(Pair("fee", ValueFromAmount(nFee)));
1281 WalletTxToJSON(wtx, entry);
1284 ListTransactions(pwalletMain->mapWallet[hash], "*", 0, false, details);
1285 entry.push_back(Pair("details", details));
1290 uint256 hashBlock = 0;
1291 if (GetTransaction(hash, tx, hashBlock))
1293 entry.push_back(Pair("txid", hash.GetHex()));
1294 TxToJSON(tx, 0, entry);
1296 entry.push_back(Pair("confirmations", 0));
1299 entry.push_back(Pair("blockhash", hashBlock.GetHex()));
1300 map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashBlock);
1301 if (mi != mapBlockIndex.end() && (*mi).second)
1303 CBlockIndex* pindex = (*mi).second;
1304 if (pindex->IsInMainChain())
1306 entry.push_back(Pair("confirmations", 1 + nBestHeight - pindex->nHeight));
1307 entry.push_back(Pair("txntime", (boost::int64_t)tx.nTime));
1308 entry.push_back(Pair("time", (boost::int64_t)pindex->nTime));
1311 entry.push_back(Pair("confirmations", 0));
1316 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction");
1323 Value backupwallet(const Array& params, bool fHelp)
1325 if (fHelp || params.size() != 1)
1326 throw runtime_error(
1327 "backupwallet <destination>\n"
1328 "Safely copies wallet.dat to destination, which can be a directory or a path with filename.");
1330 string strDest = params[0].get_str();
1331 if (!BackupWallet(*pwalletMain, strDest))
1332 throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet backup failed!");
1338 Value keypoolrefill(const Array& params, bool fHelp)
1340 if (fHelp || params.size() > 1)
1341 throw runtime_error(
1342 "keypoolrefill [new-size]\n"
1343 "Fills the keypool."
1344 + HelpRequiringPassphrase());
1346 unsigned int nSize = max(GetArg("-keypool", 100), 0LL);
1347 if (params.size() > 0) {
1348 if (params[0].get_int() < 0)
1349 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected valid size");
1350 nSize = (unsigned int) params[0].get_int();
1353 EnsureWalletIsUnlocked();
1355 pwalletMain->TopUpKeyPool(nSize);
1357 if (pwalletMain->GetKeyPoolSize() < nSize)
1358 throw JSONRPCError(RPC_WALLET_ERROR, "Error refreshing keypool.");
1364 void ThreadTopUpKeyPool(void* parg)
1366 // Make this thread recognisable as the key-topping-up thread
1367 RenameThread("bitcoin-key-top");
1369 pwalletMain->TopUpKeyPool();
1372 void ThreadCleanWalletPassphrase(void* parg)
1374 // Make this thread recognisable as the wallet relocking thread
1375 RenameThread("bitcoin-lock-wa");
1377 int64 nMyWakeTime = GetTimeMillis() + *((int64*)parg) * 1000;
1379 ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime);
1381 if (nWalletUnlockTime == 0)
1383 nWalletUnlockTime = nMyWakeTime;
1387 if (nWalletUnlockTime==0)
1389 int64 nToSleep = nWalletUnlockTime - GetTimeMillis();
1393 LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime);
1395 ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime);
1399 if (nWalletUnlockTime)
1401 nWalletUnlockTime = 0;
1402 pwalletMain->Lock();
1407 if (nWalletUnlockTime < nMyWakeTime)
1408 nWalletUnlockTime = nMyWakeTime;
1411 LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime);
1413 delete (int64*)parg;
1416 Value walletpassphrase(const Array& params, bool fHelp)
1418 if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 3))
1419 throw runtime_error(
1420 "walletpassphrase <passphrase> <timeout> [mintonly]\n"
1421 "Stores the wallet decryption key in memory for <timeout> seconds.\n"
1422 "mintonly is optional true/false allowing only block minting.");
1425 if (!pwalletMain->IsCrypted())
1426 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called.");
1428 if (!pwalletMain->IsLocked())
1429 throw JSONRPCError(RPC_WALLET_ALREADY_UNLOCKED, "Error: Wallet is already unlocked, use walletlock first if need to change unlock settings.");
1430 // Note that the walletpassphrase is stored in params[0] which is not mlock()ed
1431 SecureString strWalletPass;
1432 strWalletPass.reserve(100);
1433 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1434 // Alternately, find a way to make params[0] mlock()'d to begin with.
1435 strWalletPass = params[0].get_str().c_str();
1437 if (strWalletPass.length() > 0)
1439 if (!pwalletMain->Unlock(strWalletPass))
1440 throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
1443 throw runtime_error(
1444 "walletpassphrase <passphrase> <timeout>\n"
1445 "Stores the wallet decryption key in memory for <timeout> seconds.");
1447 NewThread(ThreadTopUpKeyPool, NULL);
1448 int64* pnSleepTime = new int64(params[1].get_int64());
1449 NewThread(ThreadCleanWalletPassphrase, pnSleepTime);
1451 // ppcoin: if user OS account compromised prevent trivial sendmoney commands
1452 if (params.size() > 2)
1453 fWalletUnlockMintOnly = params[2].get_bool();
1455 fWalletUnlockMintOnly = false;
1461 Value walletpassphrasechange(const Array& params, bool fHelp)
1463 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2))
1464 throw runtime_error(
1465 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1466 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1469 if (!pwalletMain->IsCrypted())
1470 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrasechange was called.");
1472 // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string)
1473 // Alternately, find a way to make params[0] mlock()'d to begin with.
1474 SecureString strOldWalletPass;
1475 strOldWalletPass.reserve(100);
1476 strOldWalletPass = params[0].get_str().c_str();
1478 SecureString strNewWalletPass;
1479 strNewWalletPass.reserve(100);
1480 strNewWalletPass = params[1].get_str().c_str();
1482 if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1)
1483 throw runtime_error(
1484 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1485 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1487 if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass))
1488 throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
1494 Value walletlock(const Array& params, bool fHelp)
1496 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 0))
1497 throw runtime_error(
1499 "Removes the wallet encryption key from memory, locking the wallet.\n"
1500 "After calling this method, you will need to call walletpassphrase again\n"
1501 "before being able to call any methods which require the wallet to be unlocked.");
1504 if (!pwalletMain->IsCrypted())
1505 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletlock was called.");
1508 LOCK(cs_nWalletUnlockTime);
1509 pwalletMain->Lock();
1510 nWalletUnlockTime = 0;
1517 Value encryptwallet(const Array& params, bool fHelp)
1519 if (!pwalletMain->IsCrypted() && (fHelp || params.size() != 1))
1520 throw runtime_error(
1521 "encryptwallet <passphrase>\n"
1522 "Encrypts the wallet with <passphrase>.");
1525 if (pwalletMain->IsCrypted())
1526 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an encrypted wallet, but encryptwallet was called.");
1528 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1529 // Alternately, find a way to make params[0] mlock()'d to begin with.
1530 SecureString strWalletPass;
1531 strWalletPass.reserve(100);
1532 strWalletPass = params[0].get_str().c_str();
1534 if (strWalletPass.length() < 1)
1535 throw runtime_error(
1536 "encryptwallet <passphrase>\n"
1537 "Encrypts the wallet with <passphrase>.");
1539 if (!pwalletMain->EncryptWallet(strWalletPass))
1540 throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: Failed to encrypt the wallet.");
1542 // BDB seems to have a bad habit of writing old data into
1543 // slack space in .dat files; that is bad if the old data is
1544 // unencrypted private keys. So:
1546 return "wallet encrypted; NovaCoin server stopping, restart to run with encrypted wallet. The keypool has been flushed, you need to make a new backup.";
1549 class DescribeAddressVisitor : public boost::static_visitor<Object>
1552 Object operator()(const CNoDestination &dest) const { return Object(); }
1554 Object operator()(const CKeyID &keyID) const {
1557 pwalletMain->GetPubKey(keyID, vchPubKey);
1558 obj.push_back(Pair("isscript", false));
1559 obj.push_back(Pair("pubkey", HexStr(vchPubKey.Raw())));
1560 obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed()));
1564 Object operator()(const CScriptID &scriptID) const {
1566 obj.push_back(Pair("isscript", true));
1568 pwalletMain->GetCScript(scriptID, subscript);
1569 std::vector<CTxDestination> addresses;
1570 txnouttype whichType;
1572 ExtractDestinations(subscript, whichType, addresses, nRequired);
1573 obj.push_back(Pair("script", GetTxnOutputType(whichType)));
1574 obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end())));
1576 BOOST_FOREACH(const CTxDestination& addr, addresses)
1577 a.push_back(CBitcoinAddress(addr).ToString());
1578 obj.push_back(Pair("addresses", a));
1579 if (whichType == TX_MULTISIG)
1580 obj.push_back(Pair("sigsrequired", nRequired));
1585 Value validateaddress(const Array& params, bool fHelp)
1587 if (fHelp || params.size() != 1)
1588 throw runtime_error(
1589 "validateaddress <novacoinaddress>\n"
1590 "Return information about <novacoinaddress>.");
1592 CBitcoinAddress address(params[0].get_str());
1593 bool isValid = address.IsValid();
1596 ret.push_back(Pair("isvalid", isValid));
1599 CTxDestination dest = address.Get();
1600 string currentAddress = address.ToString();
1601 ret.push_back(Pair("address", currentAddress));
1602 bool fMine = IsMine(*pwalletMain, dest);
1603 ret.push_back(Pair("ismine", fMine));
1605 Object detail = boost::apply_visitor(DescribeAddressVisitor(), dest);
1606 ret.insert(ret.end(), detail.begin(), detail.end());
1608 if (pwalletMain->mapAddressBook.count(dest))
1609 ret.push_back(Pair("account", pwalletMain->mapAddressBook[dest]));
1614 Value validatepubkey(const Array& params, bool fHelp)
1616 if (fHelp || !params.size() || params.size() > 2)
1617 throw runtime_error(
1618 "validatepubkey <novacoinpubkey>\n"
1619 "Return information about <novacoinpubkey>.");
1621 std::vector<unsigned char> vchPubKey = ParseHex(params[0].get_str());
1622 CPubKey pubKey(vchPubKey);
1624 bool isValid = pubKey.IsValid();
1625 bool isCompressed = pubKey.IsCompressed();
1626 CKeyID keyID = pubKey.GetID();
1628 CBitcoinAddress address;
1632 ret.push_back(Pair("isvalid", isValid));
1635 CTxDestination dest = address.Get();
1636 string currentAddress = address.ToString();
1637 ret.push_back(Pair("address", currentAddress));
1638 bool fMine = IsMine(*pwalletMain, dest);
1639 ret.push_back(Pair("ismine", fMine));
1640 ret.push_back(Pair("iscompressed", isCompressed));
1642 Object detail = boost::apply_visitor(DescribeAddressVisitor(), dest);
1643 ret.insert(ret.end(), detail.begin(), detail.end());
1645 if (pwalletMain->mapAddressBook.count(dest))
1646 ret.push_back(Pair("account", pwalletMain->mapAddressBook[dest]));
1651 // ppcoin: reserve balance from being staked for network protection
1652 Value reservebalance(const Array& params, bool fHelp)
1654 if (fHelp || params.size() > 2)
1655 throw runtime_error(
1656 "reservebalance [<reserve> [amount]]\n"
1657 "<reserve> is true or false to turn balance reserve on or off.\n"
1658 "<amount> is a real and rounded to cent.\n"
1659 "Set reserve amount not participating in network protection.\n"
1660 "If no parameters provided current setting is printed.\n");
1662 if (params.size() > 0)
1664 bool fReserve = params[0].get_bool();
1667 if (params.size() == 1)
1668 throw runtime_error("must provide amount to reserve balance.\n");
1669 int64 nAmount = AmountFromValue(params[1]);
1670 nAmount = (nAmount / CENT) * CENT; // round to cent
1672 throw runtime_error("amount cannot be negative.\n");
1673 mapArgs["-reservebalance"] = FormatMoney(nAmount).c_str();
1677 if (params.size() > 1)
1678 throw runtime_error("cannot specify amount to turn off reserve.\n");
1679 mapArgs["-reservebalance"] = "0";
1684 int64 nReserveBalance = 0;
1685 if (mapArgs.count("-reservebalance") && !ParseMoney(mapArgs["-reservebalance"], nReserveBalance))
1686 throw runtime_error("invalid reserve balance amount\n");
1687 result.push_back(Pair("reserve", (nReserveBalance > 0)));
1688 result.push_back(Pair("amount", ValueFromAmount(nReserveBalance)));
1693 // ppcoin: check wallet integrity
1694 Value checkwallet(const Array& params, bool fHelp)
1696 if (fHelp || params.size() > 0)
1697 throw runtime_error(
1699 "Check wallet for integrity.\n");
1702 int64 nBalanceInQuestion;
1703 pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion, true);
1705 if (nMismatchSpent == 0)
1706 result.push_back(Pair("wallet check passed", true));
1709 result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1710 result.push_back(Pair("amount in question", ValueFromAmount(nBalanceInQuestion)));
1716 // ppcoin: repair wallet
1717 Value repairwallet(const Array& params, bool fHelp)
1719 if (fHelp || params.size() > 0)
1720 throw runtime_error(
1722 "Repair wallet if checkwallet reports any problem.\n");
1725 int64 nBalanceInQuestion;
1726 pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion);
1728 if (nMismatchSpent == 0)
1729 result.push_back(Pair("wallet check passed", true));
1732 result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1733 result.push_back(Pair("amount affected by repair", ValueFromAmount(nBalanceInQuestion)));
1738 // NovaCoin: resend unconfirmed wallet transactions
1739 Value resendtx(const Array& params, bool fHelp)
1741 if (fHelp || params.size() > 1)
1742 throw runtime_error(
1744 "Re-send unconfirmed transactions.\n"
1747 ResendWalletTransactions();
1752 // ppcoin: make a public-private key pair
1753 Value makekeypair(const Array& params, bool fHelp)
1755 if (fHelp || params.size() > 1)
1756 throw runtime_error(
1757 "makekeypair [prefix]\n"
1758 "Make a public/private key pair.\n"
1759 "[prefix] is optional preferred prefix for the public key.\n");
1761 string strPrefix = "";
1762 if (params.size() > 0)
1763 strPrefix = params[0].get_str();
1766 key.MakeNewKey(false);
1768 CPrivKey vchPrivKey = key.GetPrivKey();
1770 result.push_back(Pair("PrivateKey", HexStr<CPrivKey::iterator>(vchPrivKey.begin(), vchPrivKey.end())));
1771 result.push_back(Pair("PublicKey", HexStr(key.GetPubKey().Raw())));