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 obj.push_back(Pair("mininput", ValueFromAmount(nMinimumInputValue)));
91 if (pwalletMain->IsCrypted())
92 obj.push_back(Pair("unlocked_until", (boost::int64_t)nWalletUnlockTime / 1000));
93 obj.push_back(Pair("errors", GetWarnings("statusbar")));
98 Value getnewpubkey(const Array& params, bool fHelp)
100 if (fHelp || params.size() > 1)
102 "getnewpubkey [account]\n"
103 "Returns new public key for coinbase generation.");
105 // Parse the account first so we don't generate a key if there's an error
107 if (params.size() > 0)
108 strAccount = AccountFromValue(params[0]);
110 if (!pwalletMain->IsLocked())
111 pwalletMain->TopUpKeyPool();
113 // Generate a new key that is added to wallet
115 if (!pwalletMain->GetKeyFromPool(newKey, false))
116 throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
117 CKeyID keyID = newKey.GetID();
119 pwalletMain->SetAddressBookName(keyID, strAccount);
120 vector<unsigned char> vchPubKey = newKey.Raw();
122 return HexStr(vchPubKey.begin(), vchPubKey.end());
126 Value getnewaddress(const Array& params, bool fHelp)
128 if (fHelp || params.size() > 1)
130 "getnewaddress [account]\n"
131 "Returns a new NovaCoin address for receiving payments. "
132 "If [account] is specified (recommended), it is added to the address book "
133 "so payments received with the address will be credited to [account].");
135 // Parse the account first so we don't generate a key if there's an error
137 if (params.size() > 0)
138 strAccount = AccountFromValue(params[0]);
140 if (!pwalletMain->IsLocked())
141 pwalletMain->TopUpKeyPool();
143 // Generate a new key that is added to wallet
145 if (!pwalletMain->GetKeyFromPool(newKey, false))
146 throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
147 CKeyID keyID = newKey.GetID();
149 pwalletMain->SetAddressBookName(keyID, strAccount);
151 return CBitcoinAddress(keyID).ToString();
155 CBitcoinAddress GetAccountAddress(string strAccount, bool bForceNew=false)
157 CWalletDB walletdb(pwalletMain->strWalletFile);
160 walletdb.ReadAccount(strAccount, account);
162 bool bKeyUsed = false;
164 // Check if the current key has been used
165 if (account.vchPubKey.IsValid())
167 CScript scriptPubKey;
168 scriptPubKey.SetDestination(account.vchPubKey.GetID());
169 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin();
170 it != pwalletMain->mapWallet.end() && account.vchPubKey.IsValid();
173 const CWalletTx& wtx = (*it).second;
174 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
175 if (txout.scriptPubKey == scriptPubKey)
180 // Generate a new key
181 if (!account.vchPubKey.IsValid() || bForceNew || bKeyUsed)
183 if (!pwalletMain->GetKeyFromPool(account.vchPubKey, false))
184 throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
186 pwalletMain->SetAddressBookName(account.vchPubKey.GetID(), strAccount);
187 walletdb.WriteAccount(strAccount, account);
190 return CBitcoinAddress(account.vchPubKey.GetID());
193 Value getaccountaddress(const Array& params, bool fHelp)
195 if (fHelp || params.size() != 1)
197 "getaccountaddress <account>\n"
198 "Returns the current NovaCoin address for receiving payments to this account.");
200 // Parse the account first so we don't generate a key if there's an error
201 string strAccount = AccountFromValue(params[0]);
205 ret = GetAccountAddress(strAccount).ToString();
212 Value setaccount(const Array& params, bool fHelp)
214 if (fHelp || params.size() < 1 || params.size() > 2)
216 "setaccount <novacoinaddress> <account>\n"
217 "Sets the account associated with the given address.");
219 CBitcoinAddress address(params[0].get_str());
220 if (!address.IsValid())
221 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
225 if (params.size() > 1)
226 strAccount = AccountFromValue(params[1]);
228 // Detect when changing the account of an address that is the 'unused current key' of another account:
229 if (pwalletMain->mapAddressBook.count(address.Get()))
231 string strOldAccount = pwalletMain->mapAddressBook[address.Get()];
232 if (address == GetAccountAddress(strOldAccount))
233 GetAccountAddress(strOldAccount, true);
236 pwalletMain->SetAddressBookName(address.Get(), strAccount);
242 Value getaccount(const Array& params, bool fHelp)
244 if (fHelp || params.size() != 1)
246 "getaccount <novacoinaddress>\n"
247 "Returns the account associated with the given address.");
249 CBitcoinAddress address(params[0].get_str());
250 if (!address.IsValid())
251 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
254 map<CTxDestination, string>::iterator mi = pwalletMain->mapAddressBook.find(address.Get());
255 if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.empty())
256 strAccount = (*mi).second;
261 Value getaddressesbyaccount(const Array& params, bool fHelp)
263 if (fHelp || params.size() != 1)
265 "getaddressesbyaccount <account>\n"
266 "Returns the list of addresses for the given account.");
268 string strAccount = AccountFromValue(params[0]);
270 // Find all addresses that have the given account
272 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
274 const CBitcoinAddress& address = item.first;
275 const string& strName = item.second;
276 if (strName == strAccount)
277 ret.push_back(address.ToString());
282 Value sendtoaddress(const Array& params, bool fHelp)
284 if (fHelp || params.size() < 2 || params.size() > 4)
286 "sendtoaddress <novacoinaddress> <amount> [comment] [comment-to]\n"
287 "<amount> is a real and is rounded to the nearest 0.000001"
288 + HelpRequiringPassphrase());
290 CBitcoinAddress address(params[0].get_str());
291 if (!address.IsValid())
292 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
295 int64 nAmount = AmountFromValue(params[1]);
297 if (nAmount < MIN_TXOUT_AMOUNT)
298 throw JSONRPCError(-101, "Send amount too small");
302 if (params.size() > 2 && params[2].type() != null_type && !params[2].get_str().empty())
303 wtx.mapValue["comment"] = params[2].get_str();
304 if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
305 wtx.mapValue["to"] = params[3].get_str();
307 if (pwalletMain->IsLocked())
308 throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first.");
310 string strError = pwalletMain->SendMoneyToDestination(address.Get(), nAmount, wtx);
312 throw JSONRPCError(RPC_WALLET_ERROR, strError);
314 return wtx.GetHash().GetHex();
317 Value listaddressgroupings(const Array& params, bool fHelp)
321 "listaddressgroupings\n"
322 "Lists groups of addresses which have had their common ownership\n"
323 "made public by common use as inputs or as the resulting change\n"
324 "in past transactions");
327 map<CTxDestination, int64> balances = pwalletMain->GetAddressBalances();
328 BOOST_FOREACH(set<CTxDestination> grouping, pwalletMain->GetAddressGroupings())
331 BOOST_FOREACH(CTxDestination address, grouping)
334 addressInfo.push_back(CBitcoinAddress(address).ToString());
335 addressInfo.push_back(ValueFromAmount(balances[address]));
337 LOCK(pwalletMain->cs_wallet);
338 if (pwalletMain->mapAddressBook.find(CBitcoinAddress(address).Get()) != pwalletMain->mapAddressBook.end())
339 addressInfo.push_back(pwalletMain->mapAddressBook.find(CBitcoinAddress(address).Get())->second);
341 jsonGrouping.push_back(addressInfo);
343 jsonGroupings.push_back(jsonGrouping);
345 return jsonGroupings;
348 Value signmessage(const Array& params, bool fHelp)
350 if (fHelp || params.size() != 2)
352 "signmessage <novacoinaddress> <message>\n"
353 "Sign a message with the private key of an address");
355 EnsureWalletIsUnlocked();
357 string strAddress = params[0].get_str();
358 string strMessage = params[1].get_str();
360 CBitcoinAddress addr(strAddress);
362 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
365 if (!addr.GetKeyID(keyID))
366 throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
369 if (!pwalletMain->GetKey(keyID, key))
370 throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available");
372 CDataStream ss(SER_GETHASH, 0);
373 ss << strMessageMagic;
376 vector<unsigned char> vchSig;
377 if (!key.SignCompact(Hash(ss.begin(), ss.end()), vchSig))
378 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed");
380 return EncodeBase64(&vchSig[0], vchSig.size());
383 Value verifymessage(const Array& params, bool fHelp)
385 if (fHelp || params.size() != 3)
387 "verifymessage <novacoinaddress> <signature> <message>\n"
388 "Verify a signed message");
390 string strAddress = params[0].get_str();
391 string strSign = params[1].get_str();
392 string strMessage = params[2].get_str();
394 CBitcoinAddress addr(strAddress);
396 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
399 if (!addr.GetKeyID(keyID))
400 throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
402 bool fInvalid = false;
403 vector<unsigned char> vchSig = DecodeBase64(strSign.c_str(), &fInvalid);
406 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Malformed base64 encoding");
408 CDataStream ss(SER_GETHASH, 0);
409 ss << strMessageMagic;
413 if (!key.SetCompactSignature(Hash(ss.begin(), ss.end()), vchSig))
416 return (key.GetPubKey().GetID() == keyID);
420 Value getreceivedbyaddress(const Array& params, bool fHelp)
422 if (fHelp || params.size() < 1 || params.size() > 2)
424 "getreceivedbyaddress <novacoinaddress> [minconf=1]\n"
425 "Returns the total amount received by <novacoinaddress> in transactions with at least [minconf] confirmations.");
428 CBitcoinAddress address = CBitcoinAddress(params[0].get_str());
429 CScript scriptPubKey;
430 if (!address.IsValid())
431 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
432 scriptPubKey.SetDestination(address.Get());
433 if (!IsMine(*pwalletMain,scriptPubKey))
436 // Minimum confirmations
438 if (params.size() > 1)
439 nMinDepth = params[1].get_int();
443 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
445 const CWalletTx& wtx = (*it).second;
446 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
449 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
450 if (txout.scriptPubKey == scriptPubKey)
451 if (wtx.GetDepthInMainChain() >= nMinDepth)
452 nAmount += txout.nValue;
455 return ValueFromAmount(nAmount);
459 void GetAccountAddresses(string strAccount, set<CTxDestination>& setAddress)
461 BOOST_FOREACH(const PAIRTYPE(CTxDestination, string)& item, pwalletMain->mapAddressBook)
463 const CTxDestination& address = item.first;
464 const string& strName = item.second;
465 if (strName == strAccount)
466 setAddress.insert(address);
470 Value getreceivedbyaccount(const Array& params, bool fHelp)
472 if (fHelp || params.size() < 1 || params.size() > 2)
474 "getreceivedbyaccount <account> [minconf=1]\n"
475 "Returns the total amount received by addresses with <account> in transactions with at least [minconf] confirmations.");
477 // Minimum confirmations
479 if (params.size() > 1)
480 nMinDepth = params[1].get_int();
482 // Get the set of pub keys assigned to account
483 string strAccount = AccountFromValue(params[0]);
484 set<CTxDestination> setAddress;
485 GetAccountAddresses(strAccount, setAddress);
489 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
491 const CWalletTx& wtx = (*it).second;
492 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
495 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
497 CTxDestination address;
498 if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*pwalletMain, address) && setAddress.count(address))
499 if (wtx.GetDepthInMainChain() >= nMinDepth)
500 nAmount += txout.nValue;
504 return (double)nAmount / (double)COIN;
508 int64 GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth)
512 // Tally wallet transactions
513 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
515 const CWalletTx& wtx = (*it).second;
519 int64 nGenerated, nReceived, nSent, nFee;
520 wtx.GetAccountAmounts(strAccount, nGenerated, nReceived, nSent, nFee);
522 if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth)
523 nBalance += nReceived;
524 nBalance += nGenerated - nSent - nFee;
527 // Tally internal accounting entries
528 nBalance += walletdb.GetAccountCreditDebit(strAccount);
533 int64 GetAccountBalance(const string& strAccount, int nMinDepth)
535 CWalletDB walletdb(pwalletMain->strWalletFile);
536 return GetAccountBalance(walletdb, strAccount, nMinDepth);
540 Value getbalance(const Array& params, bool fHelp)
542 if (fHelp || params.size() > 2)
544 "getbalance [account] [minconf=1]\n"
545 "If [account] is not specified, returns the server's total available balance.\n"
546 "If [account] is specified, returns the balance in the account.");
548 if (params.size() == 0)
549 return ValueFromAmount(pwalletMain->GetBalance());
552 if (params.size() > 1)
553 nMinDepth = params[1].get_int();
555 if (params[0].get_str() == "*") {
556 // Calculate total balance a different way from GetBalance()
557 // (GetBalance() sums up all unspent TxOuts)
558 // getbalance and getbalance '*' 0 should return the same number.
560 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
562 const CWalletTx& wtx = (*it).second;
563 if (!wtx.IsConfirmed())
566 int64 allGeneratedImmature, allGeneratedMature, allFee;
567 allGeneratedImmature = allGeneratedMature = allFee = 0;
569 string strSentAccount;
570 list<pair<CTxDestination, int64> > listReceived;
571 list<pair<CTxDestination, int64> > listSent;
572 wtx.GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount);
573 if (wtx.GetDepthInMainChain() >= nMinDepth)
575 BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64)& r, listReceived)
576 nBalance += r.second;
578 BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64)& r, listSent)
579 nBalance -= r.second;
581 nBalance += allGeneratedMature;
583 return ValueFromAmount(nBalance);
586 string strAccount = AccountFromValue(params[0]);
588 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
590 return ValueFromAmount(nBalance);
594 Value movecmd(const Array& params, bool fHelp)
596 if (fHelp || params.size() < 3 || params.size() > 5)
598 "move <fromaccount> <toaccount> <amount> [minconf=1] [comment]\n"
599 "Move from one account in your wallet to another.");
601 string strFrom = AccountFromValue(params[0]);
602 string strTo = AccountFromValue(params[1]);
603 int64 nAmount = AmountFromValue(params[2]);
605 if (nAmount < MIN_TXOUT_AMOUNT)
606 throw JSONRPCError(-101, "Send amount too small");
608 if (params.size() > 3)
609 // unused parameter, used to be nMinDepth, keep type-checking it though
610 (void)params[3].get_int();
612 if (params.size() > 4)
613 strComment = params[4].get_str();
615 CWalletDB walletdb(pwalletMain->strWalletFile);
616 if (!walletdb.TxnBegin())
617 throw JSONRPCError(RPC_DATABASE_ERROR, "database error");
619 int64 nNow = GetAdjustedTime();
622 CAccountingEntry debit;
623 debit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb);
624 debit.strAccount = strFrom;
625 debit.nCreditDebit = -nAmount;
627 debit.strOtherAccount = strTo;
628 debit.strComment = strComment;
629 walletdb.WriteAccountingEntry(debit);
632 CAccountingEntry credit;
633 credit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb);
634 credit.strAccount = strTo;
635 credit.nCreditDebit = nAmount;
637 credit.strOtherAccount = strFrom;
638 credit.strComment = strComment;
639 walletdb.WriteAccountingEntry(credit);
641 if (!walletdb.TxnCommit())
642 throw JSONRPCError(RPC_DATABASE_ERROR, "database error");
648 Value sendfrom(const Array& params, bool fHelp)
650 if (fHelp || params.size() < 3 || params.size() > 6)
652 "sendfrom <fromaccount> <tonovacoinaddress> <amount> [minconf=1] [comment] [comment-to]\n"
653 "<amount> is a real and is rounded to the nearest 0.000001"
654 + HelpRequiringPassphrase());
656 string strAccount = AccountFromValue(params[0]);
657 CBitcoinAddress address(params[1].get_str());
658 if (!address.IsValid())
659 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
660 int64 nAmount = AmountFromValue(params[2]);
662 if (nAmount < MIN_TXOUT_AMOUNT)
663 throw JSONRPCError(-101, "Send amount too small");
666 if (params.size() > 3)
667 nMinDepth = params[3].get_int();
670 wtx.strFromAccount = strAccount;
671 if (params.size() > 4 && params[4].type() != null_type && !params[4].get_str().empty())
672 wtx.mapValue["comment"] = params[4].get_str();
673 if (params.size() > 5 && params[5].type() != null_type && !params[5].get_str().empty())
674 wtx.mapValue["to"] = params[5].get_str();
676 EnsureWalletIsUnlocked();
679 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
680 if (nAmount > nBalance)
681 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds");
684 string strError = pwalletMain->SendMoneyToDestination(address.Get(), nAmount, wtx);
686 throw JSONRPCError(RPC_WALLET_ERROR, strError);
688 return wtx.GetHash().GetHex();
692 Value sendmany(const Array& params, bool fHelp)
694 if (fHelp || params.size() < 2 || params.size() > 4)
696 "sendmany <fromaccount> {address:amount,...} [minconf=1] [comment]\n"
697 "amounts are double-precision floating point numbers"
698 + HelpRequiringPassphrase());
700 string strAccount = AccountFromValue(params[0]);
701 Object sendTo = params[1].get_obj();
703 if (params.size() > 2)
704 nMinDepth = params[2].get_int();
707 wtx.strFromAccount = strAccount;
708 if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
709 wtx.mapValue["comment"] = params[3].get_str();
711 set<CBitcoinAddress> setAddress;
712 vector<pair<CScript, int64> > vecSend;
714 int64 totalAmount = 0;
715 BOOST_FOREACH(const Pair& s, sendTo)
717 CBitcoinAddress address(s.name_);
718 if (!address.IsValid())
719 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid NovaCoin address: ")+s.name_);
721 if (setAddress.count(address))
722 throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+s.name_);
723 setAddress.insert(address);
725 CScript scriptPubKey;
726 scriptPubKey.SetDestination(address.Get());
727 int64 nAmount = AmountFromValue(s.value_);
729 if (nAmount < MIN_TXOUT_AMOUNT)
730 throw JSONRPCError(-101, "Send amount too small");
732 totalAmount += nAmount;
734 vecSend.push_back(make_pair(scriptPubKey, nAmount));
737 EnsureWalletIsUnlocked();
740 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
741 if (totalAmount > nBalance)
742 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds");
745 CReserveKey keyChange(pwalletMain);
746 int64 nFeeRequired = 0;
747 bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired);
750 if (totalAmount + nFeeRequired > pwalletMain->GetBalance())
751 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds");
752 throw JSONRPCError(RPC_WALLET_ERROR, "Transaction creation failed");
754 if (!pwalletMain->CommitTransaction(wtx, keyChange))
755 throw JSONRPCError(RPC_WALLET_ERROR, "Transaction commit failed");
757 return wtx.GetHash().GetHex();
760 Value addmultisigaddress(const Array& params, bool fHelp)
762 if (fHelp || params.size() < 2 || params.size() > 3)
764 string msg = "addmultisigaddress <nrequired> <'[\"key\",\"key\"]'> [account]\n"
765 "Add a nrequired-to-sign multisignature address to the wallet\"\n"
766 "each key is a NovaCoin address or hex-encoded public key\n"
767 "If [account] is specified, assign address to [account].";
768 throw runtime_error(msg);
771 int nRequired = params[0].get_int();
772 const Array& keys = params[1].get_array();
774 if (params.size() > 2)
775 strAccount = AccountFromValue(params[2]);
777 // Gather public keys
779 throw runtime_error("a multisignature address must require at least one key to redeem");
780 if ((int)keys.size() < nRequired)
782 strprintf("not enough keys supplied "
783 "(got %"PRIszu" keys, but need at least %d to redeem)", keys.size(), nRequired));
784 std::vector<CKey> pubkeys;
785 pubkeys.resize(keys.size());
786 for (unsigned int i = 0; i < keys.size(); i++)
788 const std::string& ks = keys[i].get_str();
790 // Case 1: Bitcoin address and we have full public key:
791 CBitcoinAddress address(ks);
792 if (address.IsValid())
795 if (!address.GetKeyID(keyID))
797 strprintf("%s does not refer to a key",ks.c_str()));
799 if (!pwalletMain->GetPubKey(keyID, vchPubKey))
801 strprintf("no full public key for address %s",ks.c_str()));
802 if (!vchPubKey.IsValid() || !pubkeys[i].SetPubKey(vchPubKey))
803 throw runtime_error(" Invalid public key: "+ks);
806 // Case 2: hex public key
809 CPubKey vchPubKey(ParseHex(ks));
810 if (!vchPubKey.IsValid() || !pubkeys[i].SetPubKey(vchPubKey))
811 throw runtime_error(" Invalid public key: "+ks);
815 throw runtime_error(" Invalid public key: "+ks);
819 // Construct using pay-to-script-hash:
821 inner.SetMultisig(nRequired, pubkeys);
822 CScriptID innerID = inner.GetID();
823 pwalletMain->AddCScript(inner);
825 pwalletMain->SetAddressBookName(innerID, strAccount);
826 return CBitcoinAddress(innerID).ToString();
829 Value addredeemscript(const Array& params, bool fHelp)
831 if (fHelp || params.size() < 1 || params.size() > 2)
833 string msg = "addredeemscript <redeemScript> [account]\n"
834 "Add a P2SH address with a specified redeemScript to the wallet.\n"
835 "If [account] is specified, assign address to [account].";
836 throw runtime_error(msg);
840 if (params.size() > 1)
841 strAccount = AccountFromValue(params[1]);
843 // Construct using pay-to-script-hash:
844 vector<unsigned char> innerData = ParseHexV(params[0], "redeemScript");
845 CScript inner(innerData.begin(), innerData.end());
846 CScriptID innerID = inner.GetID();
847 pwalletMain->AddCScript(inner);
849 pwalletMain->SetAddressBookName(innerID, strAccount);
850 return CBitcoinAddress(innerID).ToString();
860 nConf = std::numeric_limits<int>::max();
864 Value ListReceived(const Array& params, bool fByAccounts)
866 // Minimum confirmations
868 if (params.size() > 0)
869 nMinDepth = params[0].get_int();
871 // Whether to include empty accounts
872 bool fIncludeEmpty = false;
873 if (params.size() > 1)
874 fIncludeEmpty = params[1].get_bool();
877 map<CBitcoinAddress, tallyitem> mapTally;
878 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
880 const CWalletTx& wtx = (*it).second;
882 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
885 int nDepth = wtx.GetDepthInMainChain();
886 if (nDepth < nMinDepth)
889 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
891 CTxDestination address;
892 if (!ExtractDestination(txout.scriptPubKey, address) || !IsMine(*pwalletMain, address))
895 tallyitem& item = mapTally[address];
896 item.nAmount += txout.nValue;
897 item.nConf = min(item.nConf, nDepth);
903 map<string, tallyitem> mapAccountTally;
904 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
906 const CBitcoinAddress& address = item.first;
907 const string& strAccount = item.second;
908 map<CBitcoinAddress, tallyitem>::iterator it = mapTally.find(address);
909 if (it == mapTally.end() && !fIncludeEmpty)
913 int nConf = std::numeric_limits<int>::max();
914 if (it != mapTally.end())
916 nAmount = (*it).second.nAmount;
917 nConf = (*it).second.nConf;
922 tallyitem& item = mapAccountTally[strAccount];
923 item.nAmount += nAmount;
924 item.nConf = min(item.nConf, nConf);
929 obj.push_back(Pair("address", address.ToString()));
930 obj.push_back(Pair("account", strAccount));
931 obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
932 obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf)));
939 for (map<string, tallyitem>::iterator it = mapAccountTally.begin(); it != mapAccountTally.end(); ++it)
941 int64 nAmount = (*it).second.nAmount;
942 int nConf = (*it).second.nConf;
944 obj.push_back(Pair("account", (*it).first));
945 obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
946 obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf)));
954 Value listreceivedbyaddress(const Array& params, bool fHelp)
956 if (fHelp || params.size() > 2)
958 "listreceivedbyaddress [minconf=1] [includeempty=false]\n"
959 "[minconf] is the minimum number of confirmations before payments are included.\n"
960 "[includeempty] whether to include addresses that haven't received any payments.\n"
961 "Returns an array of objects containing:\n"
962 " \"address\" : receiving address\n"
963 " \"account\" : the account of the receiving address\n"
964 " \"amount\" : total amount received by the address\n"
965 " \"confirmations\" : number of confirmations of the most recent transaction included");
967 return ListReceived(params, false);
970 Value listreceivedbyaccount(const Array& params, bool fHelp)
972 if (fHelp || params.size() > 2)
974 "listreceivedbyaccount [minconf=1] [includeempty=false]\n"
975 "[minconf] is the minimum number of confirmations before payments are included.\n"
976 "[includeempty] whether to include accounts that haven't received any payments.\n"
977 "Returns an array of objects containing:\n"
978 " \"account\" : the account of the receiving addresses\n"
979 " \"amount\" : total amount received by addresses with this account\n"
980 " \"confirmations\" : number of confirmations of the most recent transaction included");
982 return ListReceived(params, true);
985 void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret)
987 int64 nGeneratedImmature, nGeneratedMature, nFee;
988 string strSentAccount;
989 list<pair<CTxDestination, int64> > listReceived;
990 list<pair<CTxDestination, int64> > listSent;
992 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount);
994 bool fAllAccounts = (strAccount == string("*"));
996 // Generated blocks assigned to account ""
997 if ((nGeneratedMature+nGeneratedImmature) != 0 && (fAllAccounts || strAccount == ""))
1000 entry.push_back(Pair("account", string("")));
1001 if (nGeneratedImmature)
1003 entry.push_back(Pair("category", wtx.GetDepthInMainChain() ? "immature" : "orphan"));
1004 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedImmature)));
1008 entry.push_back(Pair("category", "generate"));
1009 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedMature)));
1012 WalletTxToJSON(wtx, entry);
1013 ret.push_back(entry);
1017 if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount))
1019 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& s, listSent)
1022 entry.push_back(Pair("account", strSentAccount));
1023 entry.push_back(Pair("address", CBitcoinAddress(s.first).ToString()));
1024 entry.push_back(Pair("category", "send"));
1025 entry.push_back(Pair("amount", ValueFromAmount(-s.second)));
1026 entry.push_back(Pair("fee", ValueFromAmount(-nFee)));
1028 WalletTxToJSON(wtx, entry);
1029 ret.push_back(entry);
1034 if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth)
1036 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& r, listReceived)
1039 if (pwalletMain->mapAddressBook.count(r.first))
1040 account = pwalletMain->mapAddressBook[r.first];
1041 if (fAllAccounts || (account == strAccount))
1044 entry.push_back(Pair("account", account));
1045 entry.push_back(Pair("address", CBitcoinAddress(r.first).ToString()));
1046 if (wtx.IsCoinBase())
1048 if (wtx.GetDepthInMainChain() < 1)
1049 entry.push_back(Pair("category", "orphan"));
1050 else if (wtx.GetBlocksToMaturity() > 0)
1051 entry.push_back(Pair("category", "immature"));
1053 entry.push_back(Pair("category", "generate"));
1056 entry.push_back(Pair("category", "receive"));
1057 entry.push_back(Pair("amount", ValueFromAmount(r.second)));
1059 WalletTxToJSON(wtx, entry);
1060 ret.push_back(entry);
1066 void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Array& ret)
1068 bool fAllAccounts = (strAccount == string("*"));
1070 if (fAllAccounts || acentry.strAccount == strAccount)
1073 entry.push_back(Pair("account", acentry.strAccount));
1074 entry.push_back(Pair("category", "move"));
1075 entry.push_back(Pair("time", (boost::int64_t)acentry.nTime));
1076 entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit)));
1077 entry.push_back(Pair("otheraccount", acentry.strOtherAccount));
1078 entry.push_back(Pair("comment", acentry.strComment));
1079 ret.push_back(entry);
1083 Value listtransactions(const Array& params, bool fHelp)
1085 if (fHelp || params.size() > 3)
1086 throw runtime_error(
1087 "listtransactions [account] [count=10] [from=0]\n"
1088 "Returns up to [count] most recent transactions skipping the first [from] transactions for account [account].");
1090 string strAccount = "*";
1091 if (params.size() > 0)
1092 strAccount = params[0].get_str();
1094 if (params.size() > 1)
1095 nCount = params[1].get_int();
1097 if (params.size() > 2)
1098 nFrom = params[2].get_int();
1101 throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count");
1103 throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative from");
1107 std::list<CAccountingEntry> acentries;
1108 CWallet::TxItems txOrdered = pwalletMain->OrderedTxItems(acentries, strAccount);
1110 // iterate backwards until we have nCount items to return:
1111 for (CWallet::TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
1113 CWalletTx *const pwtx = (*it).second.first;
1115 ListTransactions(*pwtx, strAccount, 0, true, ret);
1116 CAccountingEntry *const pacentry = (*it).second.second;
1118 AcentryToJSON(*pacentry, strAccount, ret);
1120 if ((int)ret.size() >= (nCount+nFrom)) break;
1122 // ret is newest to oldest
1124 if (nFrom > (int)ret.size())
1126 if ((nFrom + nCount) > (int)ret.size())
1127 nCount = ret.size() - nFrom;
1128 Array::iterator first = ret.begin();
1129 std::advance(first, nFrom);
1130 Array::iterator last = ret.begin();
1131 std::advance(last, nFrom+nCount);
1133 if (last != ret.end()) ret.erase(last, ret.end());
1134 if (first != ret.begin()) ret.erase(ret.begin(), first);
1136 std::reverse(ret.begin(), ret.end()); // Return oldest to newest
1141 Value listaccounts(const Array& params, bool fHelp)
1143 if (fHelp || params.size() > 1)
1144 throw runtime_error(
1145 "listaccounts [minconf=1]\n"
1146 "Returns Object that has account names as keys, account balances as values.");
1149 if (params.size() > 0)
1150 nMinDepth = params[0].get_int();
1152 map<string, int64> mapAccountBalances;
1153 BOOST_FOREACH(const PAIRTYPE(CTxDestination, string)& entry, pwalletMain->mapAddressBook) {
1154 if (IsMine(*pwalletMain, entry.first)) // This address belongs to me
1155 mapAccountBalances[entry.second] = 0;
1158 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
1160 const CWalletTx& wtx = (*it).second;
1161 int64 nGeneratedImmature, nGeneratedMature, nFee;
1162 string strSentAccount;
1163 list<pair<CTxDestination, int64> > listReceived;
1164 list<pair<CTxDestination, int64> > listSent;
1165 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount);
1166 mapAccountBalances[strSentAccount] -= nFee;
1167 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& s, listSent)
1168 mapAccountBalances[strSentAccount] -= s.second;
1169 if (wtx.GetDepthInMainChain() >= nMinDepth)
1171 mapAccountBalances[""] += nGeneratedMature;
1172 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& r, listReceived)
1173 if (pwalletMain->mapAddressBook.count(r.first))
1174 mapAccountBalances[pwalletMain->mapAddressBook[r.first]] += r.second;
1176 mapAccountBalances[""] += r.second;
1180 list<CAccountingEntry> acentries;
1181 CWalletDB(pwalletMain->strWalletFile).ListAccountCreditDebit("*", acentries);
1182 BOOST_FOREACH(const CAccountingEntry& entry, acentries)
1183 mapAccountBalances[entry.strAccount] += entry.nCreditDebit;
1186 BOOST_FOREACH(const PAIRTYPE(string, int64)& accountBalance, mapAccountBalances) {
1187 ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second)));
1192 Value listsinceblock(const Array& params, bool fHelp)
1195 throw runtime_error(
1196 "listsinceblock [blockhash] [target-confirmations]\n"
1197 "Get all transactions in blocks since block [blockhash], or all transactions if omitted");
1199 CBlockIndex *pindex = NULL;
1200 int target_confirms = 1;
1202 if (params.size() > 0)
1204 uint256 blockId = 0;
1206 blockId.SetHex(params[0].get_str());
1207 pindex = CBlockLocator(blockId).GetBlockIndex();
1210 if (params.size() > 1)
1212 target_confirms = params[1].get_int();
1214 if (target_confirms < 1)
1215 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter");
1218 int depth = pindex ? (1 + nBestHeight - pindex->nHeight) : -1;
1222 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); it++)
1224 CWalletTx tx = (*it).second;
1226 if (depth == -1 || tx.GetDepthInMainChain() < depth)
1227 ListTransactions(tx, "*", 0, true, transactions);
1232 if (target_confirms == 1)
1234 lastblock = hashBestChain;
1238 int target_height = pindexBest->nHeight + 1 - target_confirms;
1241 for (block = pindexBest;
1242 block && block->nHeight > target_height;
1243 block = block->pprev) { }
1245 lastblock = block ? block->GetBlockHash() : 0;
1249 ret.push_back(Pair("transactions", transactions));
1250 ret.push_back(Pair("lastblock", lastblock.GetHex()));
1255 Value gettransaction(const Array& params, bool fHelp)
1257 if (fHelp || params.size() != 1)
1258 throw runtime_error(
1259 "gettransaction <txid>\n"
1260 "Get detailed information about <txid>");
1263 hash.SetHex(params[0].get_str());
1267 if (pwalletMain->mapWallet.count(hash))
1269 const CWalletTx& wtx = pwalletMain->mapWallet[hash];
1271 TxToJSON(wtx, 0, entry);
1273 int64 nCredit = wtx.GetCredit();
1274 int64 nDebit = wtx.GetDebit();
1275 int64 nNet = nCredit - nDebit;
1276 int64 nFee = (wtx.IsFromMe() ? wtx.GetValueOut() - nDebit : 0);
1278 entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee)));
1280 entry.push_back(Pair("fee", ValueFromAmount(nFee)));
1282 WalletTxToJSON(wtx, entry);
1285 ListTransactions(pwalletMain->mapWallet[hash], "*", 0, false, details);
1286 entry.push_back(Pair("details", details));
1291 uint256 hashBlock = 0;
1292 if (GetTransaction(hash, tx, hashBlock))
1294 entry.push_back(Pair("txid", hash.GetHex()));
1295 TxToJSON(tx, 0, entry);
1297 entry.push_back(Pair("confirmations", 0));
1300 entry.push_back(Pair("blockhash", hashBlock.GetHex()));
1301 map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashBlock);
1302 if (mi != mapBlockIndex.end() && (*mi).second)
1304 CBlockIndex* pindex = (*mi).second;
1305 if (pindex->IsInMainChain())
1307 entry.push_back(Pair("confirmations", 1 + nBestHeight - pindex->nHeight));
1308 entry.push_back(Pair("txntime", (boost::int64_t)tx.nTime));
1309 entry.push_back(Pair("time", (boost::int64_t)pindex->nTime));
1312 entry.push_back(Pair("confirmations", 0));
1317 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction");
1324 Value backupwallet(const Array& params, bool fHelp)
1326 if (fHelp || params.size() != 1)
1327 throw runtime_error(
1328 "backupwallet <destination>\n"
1329 "Safely copies wallet.dat to destination, which can be a directory or a path with filename.");
1331 string strDest = params[0].get_str();
1332 if (!BackupWallet(*pwalletMain, strDest))
1333 throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet backup failed!");
1339 Value keypoolrefill(const Array& params, bool fHelp)
1341 if (fHelp || params.size() > 1)
1342 throw runtime_error(
1343 "keypoolrefill [new-size]\n"
1344 "Fills the keypool."
1345 + HelpRequiringPassphrase());
1347 unsigned int nSize = max(GetArg("-keypool", 100), 0LL);
1348 if (params.size() > 0) {
1349 if (params[0].get_int() < 0)
1350 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected valid size");
1351 nSize = (unsigned int) params[0].get_int();
1354 EnsureWalletIsUnlocked();
1356 pwalletMain->TopUpKeyPool(nSize);
1358 if (pwalletMain->GetKeyPoolSize() < nSize)
1359 throw JSONRPCError(RPC_WALLET_ERROR, "Error refreshing keypool.");
1365 void ThreadTopUpKeyPool(void* parg)
1367 // Make this thread recognisable as the key-topping-up thread
1368 RenameThread("bitcoin-key-top");
1370 pwalletMain->TopUpKeyPool();
1373 void ThreadCleanWalletPassphrase(void* parg)
1375 // Make this thread recognisable as the wallet relocking thread
1376 RenameThread("bitcoin-lock-wa");
1378 int64 nMyWakeTime = GetTimeMillis() + *((int64*)parg) * 1000;
1380 ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime);
1382 if (nWalletUnlockTime == 0)
1384 nWalletUnlockTime = nMyWakeTime;
1388 if (nWalletUnlockTime==0)
1390 int64 nToSleep = nWalletUnlockTime - GetTimeMillis();
1394 LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime);
1396 ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime);
1400 if (nWalletUnlockTime)
1402 nWalletUnlockTime = 0;
1403 pwalletMain->Lock();
1408 if (nWalletUnlockTime < nMyWakeTime)
1409 nWalletUnlockTime = nMyWakeTime;
1412 LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime);
1414 delete (int64*)parg;
1417 Value walletpassphrase(const Array& params, bool fHelp)
1419 if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 3))
1420 throw runtime_error(
1421 "walletpassphrase <passphrase> <timeout> [mintonly]\n"
1422 "Stores the wallet decryption key in memory for <timeout> seconds.\n"
1423 "mintonly is optional true/false allowing only block minting.");
1426 if (!pwalletMain->IsCrypted())
1427 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called.");
1429 if (!pwalletMain->IsLocked())
1430 throw JSONRPCError(RPC_WALLET_ALREADY_UNLOCKED, "Error: Wallet is already unlocked, use walletlock first if need to change unlock settings.");
1431 // Note that the walletpassphrase is stored in params[0] which is not mlock()ed
1432 SecureString strWalletPass;
1433 strWalletPass.reserve(100);
1434 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1435 // Alternately, find a way to make params[0] mlock()'d to begin with.
1436 strWalletPass = params[0].get_str().c_str();
1438 if (strWalletPass.length() > 0)
1440 if (!pwalletMain->Unlock(strWalletPass))
1441 throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
1444 throw runtime_error(
1445 "walletpassphrase <passphrase> <timeout>\n"
1446 "Stores the wallet decryption key in memory for <timeout> seconds.");
1448 NewThread(ThreadTopUpKeyPool, NULL);
1449 int64* pnSleepTime = new int64(params[1].get_int64());
1450 NewThread(ThreadCleanWalletPassphrase, pnSleepTime);
1452 // ppcoin: if user OS account compromised prevent trivial sendmoney commands
1453 if (params.size() > 2)
1454 fWalletUnlockMintOnly = params[2].get_bool();
1456 fWalletUnlockMintOnly = false;
1462 Value walletpassphrasechange(const Array& params, bool fHelp)
1464 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2))
1465 throw runtime_error(
1466 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1467 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1470 if (!pwalletMain->IsCrypted())
1471 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrasechange was called.");
1473 // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string)
1474 // Alternately, find a way to make params[0] mlock()'d to begin with.
1475 SecureString strOldWalletPass;
1476 strOldWalletPass.reserve(100);
1477 strOldWalletPass = params[0].get_str().c_str();
1479 SecureString strNewWalletPass;
1480 strNewWalletPass.reserve(100);
1481 strNewWalletPass = params[1].get_str().c_str();
1483 if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1)
1484 throw runtime_error(
1485 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1486 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1488 if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass))
1489 throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
1495 Value walletlock(const Array& params, bool fHelp)
1497 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 0))
1498 throw runtime_error(
1500 "Removes the wallet encryption key from memory, locking the wallet.\n"
1501 "After calling this method, you will need to call walletpassphrase again\n"
1502 "before being able to call any methods which require the wallet to be unlocked.");
1505 if (!pwalletMain->IsCrypted())
1506 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletlock was called.");
1509 LOCK(cs_nWalletUnlockTime);
1510 pwalletMain->Lock();
1511 nWalletUnlockTime = 0;
1518 Value encryptwallet(const Array& params, bool fHelp)
1520 if (!pwalletMain->IsCrypted() && (fHelp || params.size() != 1))
1521 throw runtime_error(
1522 "encryptwallet <passphrase>\n"
1523 "Encrypts the wallet with <passphrase>.");
1526 if (pwalletMain->IsCrypted())
1527 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an encrypted wallet, but encryptwallet was called.");
1529 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1530 // Alternately, find a way to make params[0] mlock()'d to begin with.
1531 SecureString strWalletPass;
1532 strWalletPass.reserve(100);
1533 strWalletPass = params[0].get_str().c_str();
1535 if (strWalletPass.length() < 1)
1536 throw runtime_error(
1537 "encryptwallet <passphrase>\n"
1538 "Encrypts the wallet with <passphrase>.");
1540 if (!pwalletMain->EncryptWallet(strWalletPass))
1541 throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: Failed to encrypt the wallet.");
1543 // BDB seems to have a bad habit of writing old data into
1544 // slack space in .dat files; that is bad if the old data is
1545 // unencrypted private keys. So:
1547 return "wallet encrypted; NovaCoin server stopping, restart to run with encrypted wallet. The keypool has been flushed, you need to make a new backup.";
1550 class DescribeAddressVisitor : public boost::static_visitor<Object>
1553 Object operator()(const CNoDestination &dest) const { return Object(); }
1555 Object operator()(const CKeyID &keyID) const {
1558 pwalletMain->GetPubKey(keyID, vchPubKey);
1559 obj.push_back(Pair("isscript", false));
1560 obj.push_back(Pair("pubkey", HexStr(vchPubKey.Raw())));
1561 obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed()));
1565 Object operator()(const CScriptID &scriptID) const {
1567 obj.push_back(Pair("isscript", true));
1569 pwalletMain->GetCScript(scriptID, subscript);
1570 std::vector<CTxDestination> addresses;
1571 txnouttype whichType;
1573 ExtractDestinations(subscript, whichType, addresses, nRequired);
1574 obj.push_back(Pair("script", GetTxnOutputType(whichType)));
1575 obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end())));
1577 BOOST_FOREACH(const CTxDestination& addr, addresses)
1578 a.push_back(CBitcoinAddress(addr).ToString());
1579 obj.push_back(Pair("addresses", a));
1580 if (whichType == TX_MULTISIG)
1581 obj.push_back(Pair("sigsrequired", nRequired));
1586 Value validateaddress(const Array& params, bool fHelp)
1588 if (fHelp || params.size() != 1)
1589 throw runtime_error(
1590 "validateaddress <novacoinaddress>\n"
1591 "Return information about <novacoinaddress>.");
1593 CBitcoinAddress address(params[0].get_str());
1594 bool isValid = address.IsValid();
1597 ret.push_back(Pair("isvalid", isValid));
1600 CTxDestination dest = address.Get();
1601 string currentAddress = address.ToString();
1602 ret.push_back(Pair("address", currentAddress));
1603 bool fMine = IsMine(*pwalletMain, dest);
1604 ret.push_back(Pair("ismine", fMine));
1606 Object detail = boost::apply_visitor(DescribeAddressVisitor(), dest);
1607 ret.insert(ret.end(), detail.begin(), detail.end());
1609 if (pwalletMain->mapAddressBook.count(dest))
1610 ret.push_back(Pair("account", pwalletMain->mapAddressBook[dest]));
1615 Value validatepubkey(const Array& params, bool fHelp)
1617 if (fHelp || !params.size() || params.size() > 2)
1618 throw runtime_error(
1619 "validatepubkey <novacoinpubkey>\n"
1620 "Return information about <novacoinpubkey>.");
1622 std::vector<unsigned char> vchPubKey = ParseHex(params[0].get_str());
1623 CPubKey pubKey(vchPubKey);
1625 bool isValid = pubKey.IsValid();
1626 bool isCompressed = pubKey.IsCompressed();
1627 CKeyID keyID = pubKey.GetID();
1629 CBitcoinAddress address;
1633 ret.push_back(Pair("isvalid", isValid));
1636 CTxDestination dest = address.Get();
1637 string currentAddress = address.ToString();
1638 ret.push_back(Pair("address", currentAddress));
1639 bool fMine = IsMine(*pwalletMain, dest);
1640 ret.push_back(Pair("ismine", fMine));
1641 ret.push_back(Pair("iscompressed", isCompressed));
1643 Object detail = boost::apply_visitor(DescribeAddressVisitor(), dest);
1644 ret.insert(ret.end(), detail.begin(), detail.end());
1646 if (pwalletMain->mapAddressBook.count(dest))
1647 ret.push_back(Pair("account", pwalletMain->mapAddressBook[dest]));
1652 // ppcoin: reserve balance from being staked for network protection
1653 Value reservebalance(const Array& params, bool fHelp)
1655 if (fHelp || params.size() > 2)
1656 throw runtime_error(
1657 "reservebalance [<reserve> [amount]]\n"
1658 "<reserve> is true or false to turn balance reserve on or off.\n"
1659 "<amount> is a real and rounded to cent.\n"
1660 "Set reserve amount not participating in network protection.\n"
1661 "If no parameters provided current setting is printed.\n");
1663 if (params.size() > 0)
1665 bool fReserve = params[0].get_bool();
1668 if (params.size() == 1)
1669 throw runtime_error("must provide amount to reserve balance.\n");
1670 int64 nAmount = AmountFromValue(params[1]);
1671 nAmount = (nAmount / CENT) * CENT; // round to cent
1673 throw runtime_error("amount cannot be negative.\n");
1674 mapArgs["-reservebalance"] = FormatMoney(nAmount).c_str();
1678 if (params.size() > 1)
1679 throw runtime_error("cannot specify amount to turn off reserve.\n");
1680 mapArgs["-reservebalance"] = "0";
1685 int64 nReserveBalance = 0;
1686 if (mapArgs.count("-reservebalance") && !ParseMoney(mapArgs["-reservebalance"], nReserveBalance))
1687 throw runtime_error("invalid reserve balance amount\n");
1688 result.push_back(Pair("reserve", (nReserveBalance > 0)));
1689 result.push_back(Pair("amount", ValueFromAmount(nReserveBalance)));
1694 // ppcoin: check wallet integrity
1695 Value checkwallet(const Array& params, bool fHelp)
1697 if (fHelp || params.size() > 0)
1698 throw runtime_error(
1700 "Check wallet for integrity.\n");
1703 int64 nBalanceInQuestion;
1704 pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion, true);
1706 if (nMismatchSpent == 0)
1707 result.push_back(Pair("wallet check passed", true));
1710 result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1711 result.push_back(Pair("amount in question", ValueFromAmount(nBalanceInQuestion)));
1717 // ppcoin: repair wallet
1718 Value repairwallet(const Array& params, bool fHelp)
1720 if (fHelp || params.size() > 0)
1721 throw runtime_error(
1723 "Repair wallet if checkwallet reports any problem.\n");
1726 int64 nBalanceInQuestion;
1727 pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion);
1729 if (nMismatchSpent == 0)
1730 result.push_back(Pair("wallet check passed", true));
1733 result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1734 result.push_back(Pair("amount affected by repair", ValueFromAmount(nBalanceInQuestion)));
1739 // NovaCoin: resend unconfirmed wallet transactions
1740 Value resendtx(const Array& params, bool fHelp)
1742 if (fHelp || params.size() > 1)
1743 throw runtime_error(
1745 "Re-send unconfirmed transactions.\n"
1748 ResendWalletTransactions();
1753 // ppcoin: make a public-private key pair
1754 Value makekeypair(const Array& params, bool fHelp)
1756 if (fHelp || params.size() > 1)
1757 throw runtime_error(
1758 "makekeypair [prefix]\n"
1759 "Make a public/private key pair.\n"
1760 "[prefix] is optional preferred prefix for the public key.\n");
1762 string strPrefix = "";
1763 if (params.size() > 0)
1764 strPrefix = params[0].get_str();
1767 key.MakeNewKey(false);
1769 CPrivKey vchPrivKey = key.GetPrivKey();
1771 result.push_back(Pair("PrivateKey", HexStr<CPrivKey::iterator>(vchPrivKey.begin(), vchPrivKey.end())));
1772 result.push_back(Pair("PublicKey", HexStr(key.GetPubKey().Raw())));