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("moneysupply", ValueFromAmount(pindexBest->nMoneySupply)));
81 obj.push_back(Pair("connections", (int)vNodes.size()));
82 obj.push_back(Pair("proxy", (proxy.first.IsValid() ? proxy.first.ToStringIPPort() : string())));
83 obj.push_back(Pair("ip", addrSeenByPeer.ToStringIP()));
84 obj.push_back(Pair("difficulty", (double)GetDifficulty()));
85 obj.push_back(Pair("testnet", fTestNet));
86 obj.push_back(Pair("keypoololdest", (boost::int64_t)pwalletMain->GetOldestKeyPoolTime()));
87 obj.push_back(Pair("keypoolsize", pwalletMain->GetKeyPoolSize()));
88 obj.push_back(Pair("paytxfee", ValueFromAmount(nTransactionFee)));
89 if (pwalletMain->IsCrypted())
90 obj.push_back(Pair("unlocked_until", (boost::int64_t)nWalletUnlockTime / 1000));
91 obj.push_back(Pair("errors", GetWarnings("statusbar")));
97 Value getnewaddress(const Array& params, bool fHelp)
99 if (fHelp || params.size() > 1)
101 "getnewaddress [account]\n"
102 "Returns a new NovaCoin address for receiving payments. "
103 "If [account] is specified (recommended), it is added to the address book "
104 "so payments received with the address will be credited to [account].");
106 // Parse the account first so we don't generate a key if there's an error
108 if (params.size() > 0)
109 strAccount = AccountFromValue(params[0]);
111 if (!pwalletMain->IsLocked())
112 pwalletMain->TopUpKeyPool();
114 // Generate a new key that is added to wallet
116 if (!pwalletMain->GetKeyFromPool(newKey, false))
117 throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
118 CKeyID keyID = newKey.GetID();
120 pwalletMain->SetAddressBookName(keyID, strAccount);
122 return CBitcoinAddress(keyID).ToString();
126 CBitcoinAddress GetAccountAddress(string strAccount, bool bForceNew=false)
128 CWalletDB walletdb(pwalletMain->strWalletFile);
131 walletdb.ReadAccount(strAccount, account);
133 bool bKeyUsed = false;
135 // Check if the current key has been used
136 if (account.vchPubKey.IsValid())
138 CScript scriptPubKey;
139 scriptPubKey.SetDestination(account.vchPubKey.GetID());
140 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin();
141 it != pwalletMain->mapWallet.end() && account.vchPubKey.IsValid();
144 const CWalletTx& wtx = (*it).second;
145 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
146 if (txout.scriptPubKey == scriptPubKey)
151 // Generate a new key
152 if (!account.vchPubKey.IsValid() || bForceNew || bKeyUsed)
154 if (!pwalletMain->GetKeyFromPool(account.vchPubKey, false))
155 throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
157 pwalletMain->SetAddressBookName(account.vchPubKey.GetID(), strAccount);
158 walletdb.WriteAccount(strAccount, account);
161 return CBitcoinAddress(account.vchPubKey.GetID());
164 Value getaccountaddress(const Array& params, bool fHelp)
166 if (fHelp || params.size() != 1)
168 "getaccountaddress <account>\n"
169 "Returns the current NovaCoin address for receiving payments to this account.");
171 // Parse the account first so we don't generate a key if there's an error
172 string strAccount = AccountFromValue(params[0]);
176 ret = GetAccountAddress(strAccount).ToString();
183 Value setaccount(const Array& params, bool fHelp)
185 if (fHelp || params.size() < 1 || params.size() > 2)
187 "setaccount <novacoinaddress> <account>\n"
188 "Sets the account associated with the given address.");
190 CBitcoinAddress address(params[0].get_str());
191 if (!address.IsValid())
192 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
196 if (params.size() > 1)
197 strAccount = AccountFromValue(params[1]);
199 // Detect when changing the account of an address that is the 'unused current key' of another account:
200 if (pwalletMain->mapAddressBook.count(address.Get()))
202 string strOldAccount = pwalletMain->mapAddressBook[address.Get()];
203 if (address == GetAccountAddress(strOldAccount))
204 GetAccountAddress(strOldAccount, true);
207 pwalletMain->SetAddressBookName(address.Get(), strAccount);
213 Value getaccount(const Array& params, bool fHelp)
215 if (fHelp || params.size() != 1)
217 "getaccount <novacoinaddress>\n"
218 "Returns the account associated with the given address.");
220 CBitcoinAddress address(params[0].get_str());
221 if (!address.IsValid())
222 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
225 map<CTxDestination, string>::iterator mi = pwalletMain->mapAddressBook.find(address.Get());
226 if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.empty())
227 strAccount = (*mi).second;
232 Value getaddressesbyaccount(const Array& params, bool fHelp)
234 if (fHelp || params.size() != 1)
236 "getaddressesbyaccount <account>\n"
237 "Returns the list of addresses for the given account.");
239 string strAccount = AccountFromValue(params[0]);
241 // Find all addresses that have the given account
243 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
245 const CBitcoinAddress& address = item.first;
246 const string& strName = item.second;
247 if (strName == strAccount)
248 ret.push_back(address.ToString());
253 Value sendtoaddress(const Array& params, bool fHelp)
255 if (fHelp || params.size() < 2 || params.size() > 4)
257 "sendtoaddress <novacoinaddress> <amount> [comment] [comment-to]\n"
258 "<amount> is a real and is rounded to the nearest 0.000001"
259 + HelpRequiringPassphrase());
261 CBitcoinAddress address(params[0].get_str());
262 if (!address.IsValid())
263 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
266 int64 nAmount = AmountFromValue(params[1]);
268 if (nAmount < MIN_TXOUT_AMOUNT)
269 throw JSONRPCError(-101, "Send amount too small");
273 if (params.size() > 2 && params[2].type() != null_type && !params[2].get_str().empty())
274 wtx.mapValue["comment"] = params[2].get_str();
275 if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
276 wtx.mapValue["to"] = params[3].get_str();
278 if (pwalletMain->IsLocked())
279 throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first.");
281 string strError = pwalletMain->SendMoneyToDestination(address.Get(), nAmount, wtx);
283 throw JSONRPCError(RPC_WALLET_ERROR, strError);
285 return wtx.GetHash().GetHex();
288 Value listaddressgroupings(const Array& params, bool fHelp)
292 "listaddressgroupings\n"
293 "Lists groups of addresses which have had their common ownership\n"
294 "made public by common use as inputs or as the resulting change\n"
295 "in past transactions");
298 map<CTxDestination, int64> balances = pwalletMain->GetAddressBalances();
299 BOOST_FOREACH(set<CTxDestination> grouping, pwalletMain->GetAddressGroupings())
302 BOOST_FOREACH(CTxDestination address, grouping)
305 addressInfo.push_back(CBitcoinAddress(address).ToString());
306 addressInfo.push_back(ValueFromAmount(balances[address]));
308 LOCK(pwalletMain->cs_wallet);
309 if (pwalletMain->mapAddressBook.find(CBitcoinAddress(address).Get()) != pwalletMain->mapAddressBook.end())
310 addressInfo.push_back(pwalletMain->mapAddressBook.find(CBitcoinAddress(address).Get())->second);
312 jsonGrouping.push_back(addressInfo);
314 jsonGroupings.push_back(jsonGrouping);
316 return jsonGroupings;
319 Value signmessage(const Array& params, bool fHelp)
321 if (fHelp || params.size() != 2)
323 "signmessage <novacoinaddress> <message>\n"
324 "Sign a message with the private key of an address");
326 EnsureWalletIsUnlocked();
328 string strAddress = params[0].get_str();
329 string strMessage = params[1].get_str();
331 CBitcoinAddress addr(strAddress);
333 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
336 if (!addr.GetKeyID(keyID))
337 throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
340 if (!pwalletMain->GetKey(keyID, key))
341 throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available");
343 CDataStream ss(SER_GETHASH, 0);
344 ss << strMessageMagic;
347 vector<unsigned char> vchSig;
348 if (!key.SignCompact(Hash(ss.begin(), ss.end()), vchSig))
349 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed");
351 return EncodeBase64(&vchSig[0], vchSig.size());
354 Value verifymessage(const Array& params, bool fHelp)
356 if (fHelp || params.size() != 3)
358 "verifymessage <novacoinaddress> <signature> <message>\n"
359 "Verify a signed message");
361 string strAddress = params[0].get_str();
362 string strSign = params[1].get_str();
363 string strMessage = params[2].get_str();
365 CBitcoinAddress addr(strAddress);
367 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
370 if (!addr.GetKeyID(keyID))
371 throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
373 bool fInvalid = false;
374 vector<unsigned char> vchSig = DecodeBase64(strSign.c_str(), &fInvalid);
377 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Malformed base64 encoding");
379 CDataStream ss(SER_GETHASH, 0);
380 ss << strMessageMagic;
384 if (!key.SetCompactSignature(Hash(ss.begin(), ss.end()), vchSig))
387 return (key.GetPubKey().GetID() == keyID);
391 Value getreceivedbyaddress(const Array& params, bool fHelp)
393 if (fHelp || params.size() < 1 || params.size() > 2)
395 "getreceivedbyaddress <novacoinaddress> [minconf=1]\n"
396 "Returns the total amount received by <novacoinaddress> in transactions with at least [minconf] confirmations.");
399 CBitcoinAddress address = CBitcoinAddress(params[0].get_str());
400 CScript scriptPubKey;
401 if (!address.IsValid())
402 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
403 scriptPubKey.SetDestination(address.Get());
404 if (!IsMine(*pwalletMain,scriptPubKey))
407 // Minimum confirmations
409 if (params.size() > 1)
410 nMinDepth = params[1].get_int();
414 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
416 const CWalletTx& wtx = (*it).second;
417 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
420 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
421 if (txout.scriptPubKey == scriptPubKey)
422 if (wtx.GetDepthInMainChain() >= nMinDepth)
423 nAmount += txout.nValue;
426 return ValueFromAmount(nAmount);
430 void GetAccountAddresses(string strAccount, set<CTxDestination>& setAddress)
432 BOOST_FOREACH(const PAIRTYPE(CTxDestination, string)& item, pwalletMain->mapAddressBook)
434 const CTxDestination& address = item.first;
435 const string& strName = item.second;
436 if (strName == strAccount)
437 setAddress.insert(address);
441 Value getreceivedbyaccount(const Array& params, bool fHelp)
443 if (fHelp || params.size() < 1 || params.size() > 2)
445 "getreceivedbyaccount <account> [minconf=1]\n"
446 "Returns the total amount received by addresses with <account> in transactions with at least [minconf] confirmations.");
448 // Minimum confirmations
450 if (params.size() > 1)
451 nMinDepth = params[1].get_int();
453 // Get the set of pub keys assigned to account
454 string strAccount = AccountFromValue(params[0]);
455 set<CTxDestination> setAddress;
456 GetAccountAddresses(strAccount, setAddress);
460 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
462 const CWalletTx& wtx = (*it).second;
463 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
466 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
468 CTxDestination address;
469 if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*pwalletMain, address) && setAddress.count(address))
470 if (wtx.GetDepthInMainChain() >= nMinDepth)
471 nAmount += txout.nValue;
475 return (double)nAmount / (double)COIN;
479 int64 GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth)
483 // Tally wallet transactions
484 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
486 const CWalletTx& wtx = (*it).second;
490 int64 nGenerated, nReceived, nSent, nFee;
491 wtx.GetAccountAmounts(strAccount, nGenerated, nReceived, nSent, nFee);
493 if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth)
494 nBalance += nReceived;
495 nBalance += nGenerated - nSent - nFee;
498 // Tally internal accounting entries
499 nBalance += walletdb.GetAccountCreditDebit(strAccount);
504 int64 GetAccountBalance(const string& strAccount, int nMinDepth)
506 CWalletDB walletdb(pwalletMain->strWalletFile);
507 return GetAccountBalance(walletdb, strAccount, nMinDepth);
511 Value getbalance(const Array& params, bool fHelp)
513 if (fHelp || params.size() > 2)
515 "getbalance [account] [minconf=1]\n"
516 "If [account] is not specified, returns the server's total available balance.\n"
517 "If [account] is specified, returns the balance in the account.");
519 if (params.size() == 0)
520 return ValueFromAmount(pwalletMain->GetBalance());
523 if (params.size() > 1)
524 nMinDepth = params[1].get_int();
526 if (params[0].get_str() == "*") {
527 // Calculate total balance a different way from GetBalance()
528 // (GetBalance() sums up all unspent TxOuts)
529 // getbalance and getbalance '*' should always return the same number.
531 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
533 const CWalletTx& wtx = (*it).second;
537 int64 allGeneratedImmature, allGeneratedMature, allFee;
538 allGeneratedImmature = allGeneratedMature = allFee = 0;
540 string strSentAccount;
541 list<pair<CTxDestination, int64> > listReceived;
542 list<pair<CTxDestination, int64> > listSent;
543 wtx.GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount);
544 if (wtx.GetDepthInMainChain() >= nMinDepth)
546 BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64)& r, listReceived)
547 nBalance += r.second;
549 BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64)& r, listSent)
550 nBalance -= r.second;
552 nBalance += allGeneratedMature;
554 return ValueFromAmount(nBalance);
557 string strAccount = AccountFromValue(params[0]);
559 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
561 return ValueFromAmount(nBalance);
565 Value movecmd(const Array& params, bool fHelp)
567 if (fHelp || params.size() < 3 || params.size() > 5)
569 "move <fromaccount> <toaccount> <amount> [minconf=1] [comment]\n"
570 "Move from one account in your wallet to another.");
572 string strFrom = AccountFromValue(params[0]);
573 string strTo = AccountFromValue(params[1]);
574 int64 nAmount = AmountFromValue(params[2]);
576 if (nAmount < MIN_TXOUT_AMOUNT)
577 throw JSONRPCError(-101, "Send amount too small");
579 if (params.size() > 3)
580 // unused parameter, used to be nMinDepth, keep type-checking it though
581 (void)params[3].get_int();
583 if (params.size() > 4)
584 strComment = params[4].get_str();
586 CWalletDB walletdb(pwalletMain->strWalletFile);
587 if (!walletdb.TxnBegin())
588 throw JSONRPCError(RPC_DATABASE_ERROR, "database error");
590 int64 nNow = GetAdjustedTime();
593 CAccountingEntry debit;
594 debit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb);
595 debit.strAccount = strFrom;
596 debit.nCreditDebit = -nAmount;
598 debit.strOtherAccount = strTo;
599 debit.strComment = strComment;
600 walletdb.WriteAccountingEntry(debit);
603 CAccountingEntry credit;
604 credit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb);
605 credit.strAccount = strTo;
606 credit.nCreditDebit = nAmount;
608 credit.strOtherAccount = strFrom;
609 credit.strComment = strComment;
610 walletdb.WriteAccountingEntry(credit);
612 if (!walletdb.TxnCommit())
613 throw JSONRPCError(RPC_DATABASE_ERROR, "database error");
619 Value sendfrom(const Array& params, bool fHelp)
621 if (fHelp || params.size() < 3 || params.size() > 6)
623 "sendfrom <fromaccount> <tonovacoinaddress> <amount> [minconf=1] [comment] [comment-to]\n"
624 "<amount> is a real and is rounded to the nearest 0.000001"
625 + HelpRequiringPassphrase());
627 string strAccount = AccountFromValue(params[0]);
628 CBitcoinAddress address(params[1].get_str());
629 if (!address.IsValid())
630 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
631 int64 nAmount = AmountFromValue(params[2]);
633 if (nAmount < MIN_TXOUT_AMOUNT)
634 throw JSONRPCError(-101, "Send amount too small");
637 if (params.size() > 3)
638 nMinDepth = params[3].get_int();
641 wtx.strFromAccount = strAccount;
642 if (params.size() > 4 && params[4].type() != null_type && !params[4].get_str().empty())
643 wtx.mapValue["comment"] = params[4].get_str();
644 if (params.size() > 5 && params[5].type() != null_type && !params[5].get_str().empty())
645 wtx.mapValue["to"] = params[5].get_str();
647 EnsureWalletIsUnlocked();
650 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
651 if (nAmount > nBalance)
652 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds");
655 string strError = pwalletMain->SendMoneyToDestination(address.Get(), nAmount, wtx);
657 throw JSONRPCError(RPC_WALLET_ERROR, strError);
659 return wtx.GetHash().GetHex();
663 Value sendmany(const Array& params, bool fHelp)
665 if (fHelp || params.size() < 2 || params.size() > 4)
667 "sendmany <fromaccount> {address:amount,...} [minconf=1] [comment]\n"
668 "amounts are double-precision floating point numbers"
669 + HelpRequiringPassphrase());
671 string strAccount = AccountFromValue(params[0]);
672 Object sendTo = params[1].get_obj();
674 if (params.size() > 2)
675 nMinDepth = params[2].get_int();
678 wtx.strFromAccount = strAccount;
679 if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
680 wtx.mapValue["comment"] = params[3].get_str();
682 set<CBitcoinAddress> setAddress;
683 vector<pair<CScript, int64> > vecSend;
685 int64 totalAmount = 0;
686 BOOST_FOREACH(const Pair& s, sendTo)
688 CBitcoinAddress address(s.name_);
689 if (!address.IsValid())
690 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid NovaCoin address: ")+s.name_);
692 if (setAddress.count(address))
693 throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+s.name_);
694 setAddress.insert(address);
696 CScript scriptPubKey;
697 scriptPubKey.SetDestination(address.Get());
698 int64 nAmount = AmountFromValue(s.value_);
700 if (nAmount < MIN_TXOUT_AMOUNT)
701 throw JSONRPCError(-101, "Send amount too small");
703 totalAmount += nAmount;
705 vecSend.push_back(make_pair(scriptPubKey, nAmount));
708 EnsureWalletIsUnlocked();
711 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
712 if (totalAmount > nBalance)
713 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds");
716 CReserveKey keyChange(pwalletMain);
717 int64 nFeeRequired = 0;
718 bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired);
721 if (totalAmount + nFeeRequired > pwalletMain->GetBalance())
722 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds");
723 throw JSONRPCError(RPC_WALLET_ERROR, "Transaction creation failed");
725 if (!pwalletMain->CommitTransaction(wtx, keyChange))
726 throw JSONRPCError(RPC_WALLET_ERROR, "Transaction commit failed");
728 return wtx.GetHash().GetHex();
731 Value addmultisigaddress(const Array& params, bool fHelp)
733 if (fHelp || params.size() < 2 || params.size() > 3)
735 string msg = "addmultisigaddress <nrequired> <'[\"key\",\"key\"]'> [account]\n"
736 "Add a nrequired-to-sign multisignature address to the wallet\"\n"
737 "each key is a NovaCoin address or hex-encoded public key\n"
738 "If [account] is specified, assign address to [account].";
739 throw runtime_error(msg);
742 int nRequired = params[0].get_int();
743 const Array& keys = params[1].get_array();
745 if (params.size() > 2)
746 strAccount = AccountFromValue(params[2]);
748 // Gather public keys
750 throw runtime_error("a multisignature address must require at least one key to redeem");
751 if ((int)keys.size() < nRequired)
753 strprintf("not enough keys supplied "
754 "(got %"PRIszu" keys, but need at least %d to redeem)", keys.size(), nRequired));
755 std::vector<CKey> pubkeys;
756 pubkeys.resize(keys.size());
757 for (unsigned int i = 0; i < keys.size(); i++)
759 const std::string& ks = keys[i].get_str();
761 // Case 1: Bitcoin address and we have full public key:
762 CBitcoinAddress address(ks);
763 if (address.IsValid())
766 if (!address.GetKeyID(keyID))
768 strprintf("%s does not refer to a key",ks.c_str()));
770 if (!pwalletMain->GetPubKey(keyID, vchPubKey))
772 strprintf("no full public key for address %s",ks.c_str()));
773 if (!vchPubKey.IsValid() || !pubkeys[i].SetPubKey(vchPubKey))
774 throw runtime_error(" Invalid public key: "+ks);
777 // Case 2: hex public key
780 CPubKey vchPubKey(ParseHex(ks));
781 if (!vchPubKey.IsValid() || !pubkeys[i].SetPubKey(vchPubKey))
782 throw runtime_error(" Invalid public key: "+ks);
786 throw runtime_error(" Invalid public key: "+ks);
790 // Construct using pay-to-script-hash:
792 inner.SetMultisig(nRequired, pubkeys);
793 CScriptID innerID = inner.GetID();
794 pwalletMain->AddCScript(inner);
796 pwalletMain->SetAddressBookName(innerID, strAccount);
797 return CBitcoinAddress(innerID).ToString();
808 nConf = std::numeric_limits<int>::max();
812 Value ListReceived(const Array& params, bool fByAccounts)
814 // Minimum confirmations
816 if (params.size() > 0)
817 nMinDepth = params[0].get_int();
819 // Whether to include empty accounts
820 bool fIncludeEmpty = false;
821 if (params.size() > 1)
822 fIncludeEmpty = params[1].get_bool();
825 map<CBitcoinAddress, tallyitem> mapTally;
826 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
828 const CWalletTx& wtx = (*it).second;
830 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
833 int nDepth = wtx.GetDepthInMainChain();
834 if (nDepth < nMinDepth)
837 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
839 CTxDestination address;
840 if (!ExtractDestination(txout.scriptPubKey, address) || !IsMine(*pwalletMain, address))
843 tallyitem& item = mapTally[address];
844 item.nAmount += txout.nValue;
845 item.nConf = min(item.nConf, nDepth);
851 map<string, tallyitem> mapAccountTally;
852 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
854 const CBitcoinAddress& address = item.first;
855 const string& strAccount = item.second;
856 map<CBitcoinAddress, tallyitem>::iterator it = mapTally.find(address);
857 if (it == mapTally.end() && !fIncludeEmpty)
861 int nConf = std::numeric_limits<int>::max();
862 if (it != mapTally.end())
864 nAmount = (*it).second.nAmount;
865 nConf = (*it).second.nConf;
870 tallyitem& item = mapAccountTally[strAccount];
871 item.nAmount += nAmount;
872 item.nConf = min(item.nConf, nConf);
877 obj.push_back(Pair("address", address.ToString()));
878 obj.push_back(Pair("account", strAccount));
879 obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
880 obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf)));
887 for (map<string, tallyitem>::iterator it = mapAccountTally.begin(); it != mapAccountTally.end(); ++it)
889 int64 nAmount = (*it).second.nAmount;
890 int nConf = (*it).second.nConf;
892 obj.push_back(Pair("account", (*it).first));
893 obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
894 obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf)));
902 Value listreceivedbyaddress(const Array& params, bool fHelp)
904 if (fHelp || params.size() > 2)
906 "listreceivedbyaddress [minconf=1] [includeempty=false]\n"
907 "[minconf] is the minimum number of confirmations before payments are included.\n"
908 "[includeempty] whether to include addresses that haven't received any payments.\n"
909 "Returns an array of objects containing:\n"
910 " \"address\" : receiving address\n"
911 " \"account\" : the account of the receiving address\n"
912 " \"amount\" : total amount received by the address\n"
913 " \"confirmations\" : number of confirmations of the most recent transaction included");
915 return ListReceived(params, false);
918 Value listreceivedbyaccount(const Array& params, bool fHelp)
920 if (fHelp || params.size() > 2)
922 "listreceivedbyaccount [minconf=1] [includeempty=false]\n"
923 "[minconf] is the minimum number of confirmations before payments are included.\n"
924 "[includeempty] whether to include accounts that haven't received any payments.\n"
925 "Returns an array of objects containing:\n"
926 " \"account\" : the account of the receiving addresses\n"
927 " \"amount\" : total amount received by addresses with this account\n"
928 " \"confirmations\" : number of confirmations of the most recent transaction included");
930 return ListReceived(params, true);
933 void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret)
935 int64 nGeneratedImmature, nGeneratedMature, nFee;
936 string strSentAccount;
937 list<pair<CTxDestination, int64> > listReceived;
938 list<pair<CTxDestination, int64> > listSent;
940 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount);
942 bool fAllAccounts = (strAccount == string("*"));
944 // Generated blocks assigned to account ""
945 if ((nGeneratedMature+nGeneratedImmature) != 0 && (fAllAccounts || strAccount == ""))
948 entry.push_back(Pair("account", string("")));
949 if (nGeneratedImmature)
951 entry.push_back(Pair("category", wtx.GetDepthInMainChain() ? "immature" : "orphan"));
952 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedImmature)));
956 entry.push_back(Pair("category", "generate"));
957 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedMature)));
960 WalletTxToJSON(wtx, entry);
961 ret.push_back(entry);
965 if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount))
967 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& s, listSent)
970 entry.push_back(Pair("account", strSentAccount));
971 entry.push_back(Pair("address", CBitcoinAddress(s.first).ToString()));
972 entry.push_back(Pair("category", "send"));
973 entry.push_back(Pair("amount", ValueFromAmount(-s.second)));
974 entry.push_back(Pair("fee", ValueFromAmount(-nFee)));
976 WalletTxToJSON(wtx, entry);
977 ret.push_back(entry);
982 if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth)
984 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& r, listReceived)
987 if (pwalletMain->mapAddressBook.count(r.first))
988 account = pwalletMain->mapAddressBook[r.first];
989 if (fAllAccounts || (account == strAccount))
992 entry.push_back(Pair("account", account));
993 entry.push_back(Pair("address", CBitcoinAddress(r.first).ToString()));
994 if (wtx.IsCoinBase())
996 if (wtx.GetDepthInMainChain() < 1)
997 entry.push_back(Pair("category", "orphan"));
998 else if (wtx.GetBlocksToMaturity() > 0)
999 entry.push_back(Pair("category", "immature"));
1001 entry.push_back(Pair("category", "generate"));
1004 entry.push_back(Pair("category", "receive"));
1005 entry.push_back(Pair("amount", ValueFromAmount(r.second)));
1007 WalletTxToJSON(wtx, entry);
1008 ret.push_back(entry);
1014 void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Array& ret)
1016 bool fAllAccounts = (strAccount == string("*"));
1018 if (fAllAccounts || acentry.strAccount == strAccount)
1021 entry.push_back(Pair("account", acentry.strAccount));
1022 entry.push_back(Pair("category", "move"));
1023 entry.push_back(Pair("time", (boost::int64_t)acentry.nTime));
1024 entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit)));
1025 entry.push_back(Pair("otheraccount", acentry.strOtherAccount));
1026 entry.push_back(Pair("comment", acentry.strComment));
1027 ret.push_back(entry);
1031 Value listtransactions(const Array& params, bool fHelp)
1033 if (fHelp || params.size() > 3)
1034 throw runtime_error(
1035 "listtransactions [account] [count=10] [from=0]\n"
1036 "Returns up to [count] most recent transactions skipping the first [from] transactions for account [account].");
1038 string strAccount = "*";
1039 if (params.size() > 0)
1040 strAccount = params[0].get_str();
1042 if (params.size() > 1)
1043 nCount = params[1].get_int();
1045 if (params.size() > 2)
1046 nFrom = params[2].get_int();
1049 throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count");
1051 throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative from");
1055 std::list<CAccountingEntry> acentries;
1056 CWallet::TxItems txOrdered = pwalletMain->OrderedTxItems(acentries, strAccount);
1058 // iterate backwards until we have nCount items to return:
1059 for (CWallet::TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
1061 CWalletTx *const pwtx = (*it).second.first;
1063 ListTransactions(*pwtx, strAccount, 0, true, ret);
1064 CAccountingEntry *const pacentry = (*it).second.second;
1066 AcentryToJSON(*pacentry, strAccount, ret);
1068 if ((int)ret.size() >= (nCount+nFrom)) break;
1070 // ret is newest to oldest
1072 if (nFrom > (int)ret.size())
1074 if ((nFrom + nCount) > (int)ret.size())
1075 nCount = ret.size() - nFrom;
1076 Array::iterator first = ret.begin();
1077 std::advance(first, nFrom);
1078 Array::iterator last = ret.begin();
1079 std::advance(last, nFrom+nCount);
1081 if (last != ret.end()) ret.erase(last, ret.end());
1082 if (first != ret.begin()) ret.erase(ret.begin(), first);
1084 std::reverse(ret.begin(), ret.end()); // Return oldest to newest
1089 Value listaccounts(const Array& params, bool fHelp)
1091 if (fHelp || params.size() > 1)
1092 throw runtime_error(
1093 "listaccounts [minconf=1]\n"
1094 "Returns Object that has account names as keys, account balances as values.");
1097 if (params.size() > 0)
1098 nMinDepth = params[0].get_int();
1100 map<string, int64> mapAccountBalances;
1101 BOOST_FOREACH(const PAIRTYPE(CTxDestination, string)& entry, pwalletMain->mapAddressBook) {
1102 if (IsMine(*pwalletMain, entry.first)) // This address belongs to me
1103 mapAccountBalances[entry.second] = 0;
1106 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
1108 const CWalletTx& wtx = (*it).second;
1109 int64 nGeneratedImmature, nGeneratedMature, nFee;
1110 string strSentAccount;
1111 list<pair<CTxDestination, int64> > listReceived;
1112 list<pair<CTxDestination, int64> > listSent;
1113 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount);
1114 mapAccountBalances[strSentAccount] -= nFee;
1115 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& s, listSent)
1116 mapAccountBalances[strSentAccount] -= s.second;
1117 if (wtx.GetDepthInMainChain() >= nMinDepth)
1119 mapAccountBalances[""] += nGeneratedMature;
1120 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& r, listReceived)
1121 if (pwalletMain->mapAddressBook.count(r.first))
1122 mapAccountBalances[pwalletMain->mapAddressBook[r.first]] += r.second;
1124 mapAccountBalances[""] += r.second;
1128 list<CAccountingEntry> acentries;
1129 CWalletDB(pwalletMain->strWalletFile).ListAccountCreditDebit("*", acentries);
1130 BOOST_FOREACH(const CAccountingEntry& entry, acentries)
1131 mapAccountBalances[entry.strAccount] += entry.nCreditDebit;
1134 BOOST_FOREACH(const PAIRTYPE(string, int64)& accountBalance, mapAccountBalances) {
1135 ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second)));
1140 Value listsinceblock(const Array& params, bool fHelp)
1143 throw runtime_error(
1144 "listsinceblock [blockhash] [target-confirmations]\n"
1145 "Get all transactions in blocks since block [blockhash], or all transactions if omitted");
1147 CBlockIndex *pindex = NULL;
1148 int target_confirms = 1;
1150 if (params.size() > 0)
1152 uint256 blockId = 0;
1154 blockId.SetHex(params[0].get_str());
1155 pindex = CBlockLocator(blockId).GetBlockIndex();
1158 if (params.size() > 1)
1160 target_confirms = params[1].get_int();
1162 if (target_confirms < 1)
1163 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter");
1166 int depth = pindex ? (1 + nBestHeight - pindex->nHeight) : -1;
1170 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); it++)
1172 CWalletTx tx = (*it).second;
1174 if (depth == -1 || tx.GetDepthInMainChain() < depth)
1175 ListTransactions(tx, "*", 0, true, transactions);
1180 if (target_confirms == 1)
1182 lastblock = hashBestChain;
1186 int target_height = pindexBest->nHeight + 1 - target_confirms;
1189 for (block = pindexBest;
1190 block && block->nHeight > target_height;
1191 block = block->pprev) { }
1193 lastblock = block ? block->GetBlockHash() : 0;
1197 ret.push_back(Pair("transactions", transactions));
1198 ret.push_back(Pair("lastblock", lastblock.GetHex()));
1203 Value gettransaction(const Array& params, bool fHelp)
1205 if (fHelp || params.size() != 1)
1206 throw runtime_error(
1207 "gettransaction <txid>\n"
1208 "Get detailed information about <txid>");
1211 hash.SetHex(params[0].get_str());
1215 if (pwalletMain->mapWallet.count(hash))
1217 const CWalletTx& wtx = pwalletMain->mapWallet[hash];
1219 TxToJSON(wtx, 0, entry);
1221 int64 nCredit = wtx.GetCredit();
1222 int64 nDebit = wtx.GetDebit();
1223 int64 nNet = nCredit - nDebit;
1224 int64 nFee = (wtx.IsFromMe() ? wtx.GetValueOut() - nDebit : 0);
1226 entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee)));
1228 entry.push_back(Pair("fee", ValueFromAmount(nFee)));
1230 WalletTxToJSON(wtx, entry);
1233 ListTransactions(pwalletMain->mapWallet[hash], "*", 0, false, details);
1234 entry.push_back(Pair("details", details));
1239 uint256 hashBlock = 0;
1240 if (GetTransaction(hash, tx, hashBlock))
1242 entry.push_back(Pair("txid", hash.GetHex()));
1243 TxToJSON(tx, 0, entry);
1245 entry.push_back(Pair("confirmations", 0));
1248 entry.push_back(Pair("blockhash", hashBlock.GetHex()));
1249 map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashBlock);
1250 if (mi != mapBlockIndex.end() && (*mi).second)
1252 CBlockIndex* pindex = (*mi).second;
1253 if (pindex->IsInMainChain())
1255 entry.push_back(Pair("confirmations", 1 + nBestHeight - pindex->nHeight));
1256 entry.push_back(Pair("txntime", (boost::int64_t)tx.nTime));
1257 entry.push_back(Pair("time", (boost::int64_t)pindex->nTime));
1260 entry.push_back(Pair("confirmations", 0));
1265 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction");
1272 Value backupwallet(const Array& params, bool fHelp)
1274 if (fHelp || params.size() != 1)
1275 throw runtime_error(
1276 "backupwallet <destination>\n"
1277 "Safely copies wallet.dat to destination, which can be a directory or a path with filename.");
1279 string strDest = params[0].get_str();
1280 if (!BackupWallet(*pwalletMain, strDest))
1281 throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet backup failed!");
1287 Value keypoolrefill(const Array& params, bool fHelp)
1289 if (fHelp || params.size() > 0)
1290 throw runtime_error(
1292 "Fills the keypool."
1293 + HelpRequiringPassphrase());
1295 EnsureWalletIsUnlocked();
1297 pwalletMain->TopUpKeyPool();
1299 if (pwalletMain->GetKeyPoolSize() < GetArg("-keypool", 100))
1300 throw JSONRPCError(RPC_WALLET_ERROR, "Error refreshing keypool.");
1306 void ThreadTopUpKeyPool(void* parg)
1308 // Make this thread recognisable as the key-topping-up thread
1309 RenameThread("bitcoin-key-top");
1311 pwalletMain->TopUpKeyPool();
1314 void ThreadCleanWalletPassphrase(void* parg)
1316 // Make this thread recognisable as the wallet relocking thread
1317 RenameThread("bitcoin-lock-wa");
1319 int64 nMyWakeTime = GetTimeMillis() + *((int64*)parg) * 1000;
1321 ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime);
1323 if (nWalletUnlockTime == 0)
1325 nWalletUnlockTime = nMyWakeTime;
1329 if (nWalletUnlockTime==0)
1331 int64 nToSleep = nWalletUnlockTime - GetTimeMillis();
1335 LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime);
1337 ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime);
1341 if (nWalletUnlockTime)
1343 nWalletUnlockTime = 0;
1344 pwalletMain->Lock();
1349 if (nWalletUnlockTime < nMyWakeTime)
1350 nWalletUnlockTime = nMyWakeTime;
1353 LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime);
1355 delete (int64*)parg;
1358 Value walletpassphrase(const Array& params, bool fHelp)
1360 if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 3))
1361 throw runtime_error(
1362 "walletpassphrase <passphrase> <timeout> [mintonly]\n"
1363 "Stores the wallet decryption key in memory for <timeout> seconds.\n"
1364 "mintonly is optional true/false allowing only block minting.");
1367 if (!pwalletMain->IsCrypted())
1368 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called.");
1370 if (!pwalletMain->IsLocked())
1371 throw JSONRPCError(RPC_WALLET_ALREADY_UNLOCKED, "Error: Wallet is already unlocked, use walletlock first if need to change unlock settings.");
1372 // Note that the walletpassphrase is stored in params[0] which is not mlock()ed
1373 SecureString strWalletPass;
1374 strWalletPass.reserve(100);
1375 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1376 // Alternately, find a way to make params[0] mlock()'d to begin with.
1377 strWalletPass = params[0].get_str().c_str();
1379 if (strWalletPass.length() > 0)
1381 if (!pwalletMain->Unlock(strWalletPass))
1382 throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
1385 throw runtime_error(
1386 "walletpassphrase <passphrase> <timeout>\n"
1387 "Stores the wallet decryption key in memory for <timeout> seconds.");
1389 NewThread(ThreadTopUpKeyPool, NULL);
1390 int64* pnSleepTime = new int64(params[1].get_int64());
1391 NewThread(ThreadCleanWalletPassphrase, pnSleepTime);
1393 // ppcoin: if user OS account compromised prevent trivial sendmoney commands
1394 if (params.size() > 2)
1395 fWalletUnlockMintOnly = params[2].get_bool();
1397 fWalletUnlockMintOnly = false;
1403 Value walletpassphrasechange(const Array& params, bool fHelp)
1405 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2))
1406 throw runtime_error(
1407 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1408 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1411 if (!pwalletMain->IsCrypted())
1412 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrasechange was called.");
1414 // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string)
1415 // Alternately, find a way to make params[0] mlock()'d to begin with.
1416 SecureString strOldWalletPass;
1417 strOldWalletPass.reserve(100);
1418 strOldWalletPass = params[0].get_str().c_str();
1420 SecureString strNewWalletPass;
1421 strNewWalletPass.reserve(100);
1422 strNewWalletPass = params[1].get_str().c_str();
1424 if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1)
1425 throw runtime_error(
1426 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1427 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1429 if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass))
1430 throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
1436 Value walletlock(const Array& params, bool fHelp)
1438 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 0))
1439 throw runtime_error(
1441 "Removes the wallet encryption key from memory, locking the wallet.\n"
1442 "After calling this method, you will need to call walletpassphrase again\n"
1443 "before being able to call any methods which require the wallet to be unlocked.");
1446 if (!pwalletMain->IsCrypted())
1447 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletlock was called.");
1450 LOCK(cs_nWalletUnlockTime);
1451 pwalletMain->Lock();
1452 nWalletUnlockTime = 0;
1459 Value encryptwallet(const Array& params, bool fHelp)
1461 if (!pwalletMain->IsCrypted() && (fHelp || params.size() != 1))
1462 throw runtime_error(
1463 "encryptwallet <passphrase>\n"
1464 "Encrypts the wallet with <passphrase>.");
1467 if (pwalletMain->IsCrypted())
1468 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an encrypted wallet, but encryptwallet was called.");
1470 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1471 // Alternately, find a way to make params[0] mlock()'d to begin with.
1472 SecureString strWalletPass;
1473 strWalletPass.reserve(100);
1474 strWalletPass = params[0].get_str().c_str();
1476 if (strWalletPass.length() < 1)
1477 throw runtime_error(
1478 "encryptwallet <passphrase>\n"
1479 "Encrypts the wallet with <passphrase>.");
1481 if (!pwalletMain->EncryptWallet(strWalletPass))
1482 throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: Failed to encrypt the wallet.");
1484 // BDB seems to have a bad habit of writing old data into
1485 // slack space in .dat files; that is bad if the old data is
1486 // unencrypted private keys. So:
1488 return "wallet encrypted; NovaCoin server stopping, restart to run with encrypted wallet. The keypool has been flushed, you need to make a new backup.";
1491 class DescribeAddressVisitor : public boost::static_visitor<Object>
1494 Object operator()(const CNoDestination &dest) const { return Object(); }
1496 Object operator()(const CKeyID &keyID) const {
1499 pwalletMain->GetPubKey(keyID, vchPubKey);
1500 obj.push_back(Pair("isscript", false));
1501 obj.push_back(Pair("pubkey", HexStr(vchPubKey.Raw())));
1502 obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed()));
1506 Object operator()(const CScriptID &scriptID) const {
1508 obj.push_back(Pair("isscript", true));
1510 pwalletMain->GetCScript(scriptID, subscript);
1511 std::vector<CTxDestination> addresses;
1512 txnouttype whichType;
1514 ExtractDestinations(subscript, whichType, addresses, nRequired);
1515 obj.push_back(Pair("script", GetTxnOutputType(whichType)));
1517 BOOST_FOREACH(const CTxDestination& addr, addresses)
1518 a.push_back(CBitcoinAddress(addr).ToString());
1519 obj.push_back(Pair("addresses", a));
1520 if (whichType == TX_MULTISIG)
1521 obj.push_back(Pair("sigsrequired", nRequired));
1526 Value validateaddress(const Array& params, bool fHelp)
1528 if (fHelp || params.size() != 1)
1529 throw runtime_error(
1530 "validateaddress <novacoinaddress>\n"
1531 "Return information about <novacoinaddress>.");
1533 CBitcoinAddress address(params[0].get_str());
1534 bool isValid = address.IsValid();
1537 ret.push_back(Pair("isvalid", isValid));
1540 CTxDestination dest = address.Get();
1541 string currentAddress = address.ToString();
1542 ret.push_back(Pair("address", currentAddress));
1543 bool fMine = IsMine(*pwalletMain, dest);
1544 ret.push_back(Pair("ismine", fMine));
1546 Object detail = boost::apply_visitor(DescribeAddressVisitor(), dest);
1547 ret.insert(ret.end(), detail.begin(), detail.end());
1549 if (pwalletMain->mapAddressBook.count(dest))
1550 ret.push_back(Pair("account", pwalletMain->mapAddressBook[dest]));
1555 // ppcoin: reserve balance from being staked for network protection
1556 Value reservebalance(const Array& params, bool fHelp)
1558 if (fHelp || params.size() > 2)
1559 throw runtime_error(
1560 "reservebalance [<reserve> [amount]]\n"
1561 "<reserve> is true or false to turn balance reserve on or off.\n"
1562 "<amount> is a real and rounded to cent.\n"
1563 "Set reserve amount not participating in network protection.\n"
1564 "If no parameters provided current setting is printed.\n");
1566 if (params.size() > 0)
1568 bool fReserve = params[0].get_bool();
1571 if (params.size() == 1)
1572 throw runtime_error("must provide amount to reserve balance.\n");
1573 int64 nAmount = AmountFromValue(params[1]);
1574 nAmount = (nAmount / CENT) * CENT; // round to cent
1576 throw runtime_error("amount cannot be negative.\n");
1577 mapArgs["-reservebalance"] = FormatMoney(nAmount).c_str();
1581 if (params.size() > 1)
1582 throw runtime_error("cannot specify amount to turn off reserve.\n");
1583 mapArgs["-reservebalance"] = "0";
1588 int64 nReserveBalance = 0;
1589 if (mapArgs.count("-reservebalance") && !ParseMoney(mapArgs["-reservebalance"], nReserveBalance))
1590 throw runtime_error("invalid reserve balance amount\n");
1591 result.push_back(Pair("reserve", (nReserveBalance > 0)));
1592 result.push_back(Pair("amount", ValueFromAmount(nReserveBalance)));
1597 // ppcoin: check wallet integrity
1598 Value checkwallet(const Array& params, bool fHelp)
1600 if (fHelp || params.size() > 0)
1601 throw runtime_error(
1603 "Check wallet for integrity.\n");
1606 int64 nBalanceInQuestion;
1607 pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion, true);
1609 if (nMismatchSpent == 0)
1610 result.push_back(Pair("wallet check passed", true));
1613 result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1614 result.push_back(Pair("amount in question", ValueFromAmount(nBalanceInQuestion)));
1620 // ppcoin: repair wallet
1621 Value repairwallet(const Array& params, bool fHelp)
1623 if (fHelp || params.size() > 0)
1624 throw runtime_error(
1626 "Repair wallet if checkwallet reports any problem.\n");
1629 int64 nBalanceInQuestion;
1630 pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion);
1632 if (nMismatchSpent == 0)
1633 result.push_back(Pair("wallet check passed", true));
1636 result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1637 result.push_back(Pair("amount affected by repair", ValueFromAmount(nBalanceInQuestion)));
1642 // NovaCoin: resend unconfirmed wallet transactions
1643 Value resendtx(const Array& params, bool fHelp)
1645 if (fHelp || params.size() > 1)
1646 throw runtime_error(
1648 "Re-send unconfirmed transactions.\n"
1651 ResendWalletTransactions();
1656 // ppcoin: make a public-private key pair
1657 Value makekeypair(const Array& params, bool fHelp)
1659 if (fHelp || params.size() > 1)
1660 throw runtime_error(
1661 "makekeypair [prefix]\n"
1662 "Make a public/private key pair.\n"
1663 "[prefix] is optional preferred prefix for the public key.\n");
1665 string strPrefix = "";
1666 if (params.size() > 0)
1667 strPrefix = params[0].get_str();
1670 key.MakeNewKey(false);
1672 CPrivKey vchPrivKey = key.GetPrivKey();
1674 result.push_back(Pair("PrivateKey", HexStr<CPrivKey::iterator>(vchPrivKey.begin(), vchPrivKey.end())));
1675 result.push_back(Pair("PublicKey", HexStr(key.GetPubKey().Raw())));