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("unspendable", ValueFromAmount(pwalletMain->GetBalanceWatchOnly())));
78 obj.push_back(Pair("newmint", ValueFromAmount(pwalletMain->GetNewMint())));
79 obj.push_back(Pair("stake", ValueFromAmount(pwalletMain->GetStake())));
80 obj.push_back(Pair("blocks", (int)nBestHeight));
81 obj.push_back(Pair("timeoffset", (boost::int64_t)GetTimeOffset()));
82 obj.push_back(Pair("moneysupply", ValueFromAmount(pindexBest->nMoneySupply)));
83 obj.push_back(Pair("connections", (int)vNodes.size()));
84 obj.push_back(Pair("proxy", (proxy.first.IsValid() ? proxy.first.ToStringIPPort() : string())));
85 obj.push_back(Pair("ip", addrSeenByPeer.ToStringIP()));
87 diff.push_back(Pair("proof-of-work", GetDifficulty()));
88 diff.push_back(Pair("proof-of-stake", GetDifficulty(GetLastBlockIndex(pindexBest, true))));
89 obj.push_back(Pair("difficulty", diff));
91 obj.push_back(Pair("testnet", fTestNet));
92 obj.push_back(Pair("keypoololdest", (boost::int64_t)pwalletMain->GetOldestKeyPoolTime()));
93 obj.push_back(Pair("keypoolsize", (int)pwalletMain->GetKeyPoolSize()));
94 obj.push_back(Pair("paytxfee", ValueFromAmount(nTransactionFee)));
95 obj.push_back(Pair("mininput", ValueFromAmount(nMinimumInputValue)));
96 if (pwalletMain->IsCrypted())
97 obj.push_back(Pair("unlocked_until", (boost::int64_t)nWalletUnlockTime / 1000));
98 obj.push_back(Pair("errors", GetWarnings("statusbar")));
102 Value getnewaddress(const Array& params, bool fHelp)
104 if (fHelp || params.size() > 1)
106 "getnewaddress [account]\n"
107 "Returns a new NovaCoin address for receiving payments. "
108 "If [account] is specified (recommended), it is added to the address book "
109 "so payments received with the address will be credited to [account].");
111 // Parse the account first so we don't generate a key if there's an error
113 if (params.size() > 0)
114 strAccount = AccountFromValue(params[0]);
116 if (!pwalletMain->IsLocked())
117 pwalletMain->TopUpKeyPool();
119 // Generate a new key that is added to wallet
121 if (!pwalletMain->GetKeyFromPool(newKey, false))
122 throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
123 CKeyID keyID = newKey.GetID();
125 pwalletMain->SetAddressBookName(keyID, strAccount);
127 return CBitcoinAddress(keyID).ToString();
131 CBitcoinAddress GetAccountAddress(string strAccount, bool bForceNew=false)
133 CWalletDB walletdb(pwalletMain->strWalletFile);
136 walletdb.ReadAccount(strAccount, account);
138 bool bKeyUsed = false;
140 // Check if the current key has been used
141 if (account.vchPubKey.IsValid())
143 CScript scriptPubKey;
144 scriptPubKey.SetDestination(account.vchPubKey.GetID());
145 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin();
146 it != pwalletMain->mapWallet.end() && account.vchPubKey.IsValid();
149 const CWalletTx& wtx = (*it).second;
150 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
151 if (txout.scriptPubKey == scriptPubKey)
156 // Generate a new key
157 if (!account.vchPubKey.IsValid() || bForceNew || bKeyUsed)
159 if (!pwalletMain->GetKeyFromPool(account.vchPubKey, false))
160 throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
162 pwalletMain->SetAddressBookName(account.vchPubKey.GetID(), strAccount);
163 walletdb.WriteAccount(strAccount, account);
166 return CBitcoinAddress(account.vchPubKey.GetID());
169 Value getaccountaddress(const Array& params, bool fHelp)
171 if (fHelp || params.size() != 1)
173 "getaccountaddress <account>\n"
174 "Returns the current NovaCoin address for receiving payments to this account.");
176 // Parse the account first so we don't generate a key if there's an error
177 string strAccount = AccountFromValue(params[0]);
181 ret = GetAccountAddress(strAccount).ToString();
188 Value setaccount(const Array& params, bool fHelp)
190 if (fHelp || params.size() < 1 || params.size() > 2)
192 "setaccount <novacoinaddress> <account>\n"
193 "Sets the account associated with the given address.");
195 CBitcoinAddress address(params[0].get_str());
196 if (!address.IsValid())
197 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
201 if (params.size() > 1)
202 strAccount = AccountFromValue(params[1]);
204 // Detect when changing the account of an address that is the 'unused current key' of another account:
205 if (pwalletMain->mapAddressBook.count(address.Get()))
207 string strOldAccount = pwalletMain->mapAddressBook[address.Get()];
208 if (address == GetAccountAddress(strOldAccount))
209 GetAccountAddress(strOldAccount, true);
212 pwalletMain->SetAddressBookName(address.Get(), strAccount);
218 Value getaccount(const Array& params, bool fHelp)
220 if (fHelp || params.size() != 1)
222 "getaccount <novacoinaddress>\n"
223 "Returns the account associated with the given address.");
225 CBitcoinAddress address(params[0].get_str());
226 if (!address.IsValid())
227 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
230 map<CTxDestination, string>::iterator mi = pwalletMain->mapAddressBook.find(address.Get());
231 if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.empty())
232 strAccount = (*mi).second;
237 Value getaddressesbyaccount(const Array& params, bool fHelp)
239 if (fHelp || params.size() != 1)
241 "getaddressesbyaccount <account>\n"
242 "Returns the list of addresses for the given account.");
244 string strAccount = AccountFromValue(params[0]);
246 // Find all addresses that have the given account
248 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
250 const CBitcoinAddress& address = item.first;
251 const string& strName = item.second;
252 if (strName == strAccount)
253 ret.push_back(address.ToString());
258 Value sendtoaddress(const Array& params, bool fHelp)
260 if (fHelp || params.size() < 2 || params.size() > 4)
262 "sendtoaddress <novacoinaddress> <amount> [comment] [comment-to]\n"
263 "<amount> is a real and is rounded to the nearest " + FormatMoney(MIN_TXOUT_AMOUNT)
264 + HelpRequiringPassphrase());
266 CBitcoinAddress address(params[0].get_str());
267 if (!address.IsValid())
268 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
271 int64 nAmount = AmountFromValue(params[1]);
273 if (nAmount < MIN_TXOUT_AMOUNT)
274 throw JSONRPCError(-101, "Send amount too small");
278 if (params.size() > 2 && params[2].type() != null_type && !params[2].get_str().empty())
279 wtx.mapValue["comment"] = params[2].get_str();
280 if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
281 wtx.mapValue["to"] = params[3].get_str();
283 if (pwalletMain->IsLocked())
284 throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first.");
286 string strError = pwalletMain->SendMoneyToDestination(address.Get(), nAmount, wtx);
288 throw JSONRPCError(RPC_WALLET_ERROR, strError);
290 return wtx.GetHash().GetHex();
293 Value listaddressgroupings(const Array& params, bool fHelp)
297 "listaddressgroupings\n"
298 "Lists groups of addresses which have had their common ownership\n"
299 "made public by common use as inputs or as the resulting change\n"
300 "in past transactions");
303 map<CTxDestination, int64> balances = pwalletMain->GetAddressBalances();
304 BOOST_FOREACH(set<CTxDestination> grouping, pwalletMain->GetAddressGroupings())
307 BOOST_FOREACH(CTxDestination address, grouping)
310 addressInfo.push_back(CBitcoinAddress(address).ToString());
311 addressInfo.push_back(ValueFromAmount(balances[address]));
313 LOCK(pwalletMain->cs_wallet);
314 if (pwalletMain->mapAddressBook.find(CBitcoinAddress(address).Get()) != pwalletMain->mapAddressBook.end())
315 addressInfo.push_back(pwalletMain->mapAddressBook.find(CBitcoinAddress(address).Get())->second);
317 jsonGrouping.push_back(addressInfo);
319 jsonGroupings.push_back(jsonGrouping);
321 return jsonGroupings;
324 Value signmessage(const Array& params, bool fHelp)
326 if (fHelp || params.size() != 2)
328 "signmessage <novacoinaddress> <message>\n"
329 "Sign a message with the private key of an address");
331 EnsureWalletIsUnlocked();
333 string strAddress = params[0].get_str();
334 string strMessage = params[1].get_str();
336 CBitcoinAddress addr(strAddress);
338 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
341 if (!addr.GetKeyID(keyID))
342 throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
345 if (!pwalletMain->GetKey(keyID, key))
346 throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available");
348 CDataStream ss(SER_GETHASH, 0);
349 ss << strMessageMagic;
352 vector<unsigned char> vchSig;
353 if (!key.SignCompact(Hash(ss.begin(), ss.end()), vchSig))
354 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed");
356 return EncodeBase64(&vchSig[0], vchSig.size());
359 Value verifymessage(const Array& params, bool fHelp)
361 if (fHelp || params.size() != 3)
363 "verifymessage <novacoinaddress> <signature> <message>\n"
364 "Verify a signed message");
366 string strAddress = params[0].get_str();
367 string strSign = params[1].get_str();
368 string strMessage = params[2].get_str();
370 CBitcoinAddress addr(strAddress);
372 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
375 if (!addr.GetKeyID(keyID))
376 throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
378 bool fInvalid = false;
379 vector<unsigned char> vchSig = DecodeBase64(strSign.c_str(), &fInvalid);
382 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Malformed base64 encoding");
384 CDataStream ss(SER_GETHASH, 0);
385 ss << strMessageMagic;
389 if (!key.SetCompactSignature(Hash(ss.begin(), ss.end()), vchSig))
392 return (key.GetPubKey().GetID() == keyID);
396 Value getreceivedbyaddress(const Array& params, bool fHelp)
398 if (fHelp || params.size() < 1 || params.size() > 2)
400 "getreceivedbyaddress <novacoinaddress> [minconf=1]\n"
401 "Returns the total amount received by <novacoinaddress> in transactions with at least [minconf] confirmations.");
404 CBitcoinAddress address = CBitcoinAddress(params[0].get_str());
405 CScript scriptPubKey;
406 if (!address.IsValid())
407 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
408 scriptPubKey.SetDestination(address.Get());
409 if (!IsMine(*pwalletMain,scriptPubKey))
412 // Minimum confirmations
414 if (params.size() > 1)
415 nMinDepth = params[1].get_int();
419 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
421 const CWalletTx& wtx = (*it).second;
422 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
425 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
426 if (txout.scriptPubKey == scriptPubKey)
427 if (wtx.GetDepthInMainChain() >= nMinDepth)
428 nAmount += txout.nValue;
431 return ValueFromAmount(nAmount);
435 void GetAccountAddresses(string strAccount, set<CTxDestination>& setAddress)
437 BOOST_FOREACH(const PAIRTYPE(CTxDestination, string)& item, pwalletMain->mapAddressBook)
439 const CTxDestination& address = item.first;
440 const string& strName = item.second;
441 if (strName == strAccount)
442 setAddress.insert(address);
446 Value getreceivedbyaccount(const Array& params, bool fHelp)
448 if (fHelp || params.size() < 1 || params.size() > 2)
450 "getreceivedbyaccount <account> [minconf=1]\n"
451 "Returns the total amount received by addresses with <account> in transactions with at least [minconf] confirmations.");
453 // Minimum confirmations
455 if (params.size() > 1)
456 nMinDepth = params[1].get_int();
458 // Get the set of pub keys assigned to account
459 string strAccount = AccountFromValue(params[0]);
460 set<CTxDestination> setAddress;
461 GetAccountAddresses(strAccount, setAddress);
465 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
467 const CWalletTx& wtx = (*it).second;
468 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
471 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
473 CTxDestination address;
474 if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*pwalletMain, address) && setAddress.count(address))
475 if (wtx.GetDepthInMainChain() >= nMinDepth)
476 nAmount += txout.nValue;
480 return (double)nAmount / (double)COIN;
484 int64 GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth)
488 // Tally wallet transactions
489 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
491 const CWalletTx& wtx = (*it).second;
495 int64 nGenerated, nReceived, nSent, nFee;
496 wtx.GetAccountAmounts(strAccount, nGenerated, nReceived, nSent, nFee);
498 if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth)
499 nBalance += nReceived;
500 nBalance += nGenerated - nSent - nFee;
503 // Tally internal accounting entries
504 nBalance += walletdb.GetAccountCreditDebit(strAccount);
509 int64 GetAccountBalance(const string& strAccount, int nMinDepth)
511 CWalletDB walletdb(pwalletMain->strWalletFile);
512 return GetAccountBalance(walletdb, strAccount, nMinDepth);
516 Value getbalance(const Array& params, bool fHelp)
518 if (fHelp || params.size() > 2)
520 "getbalance [account] [minconf=1]\n"
521 "If [account] is not specified, returns the server's total available balance.\n"
522 "If [account] is specified, returns the balance in the account.");
524 if (params.size() == 0)
525 return ValueFromAmount(pwalletMain->GetBalance());
528 if (params.size() > 1)
529 nMinDepth = params[1].get_int();
531 if (params[0].get_str() == "*") {
532 // Calculate total balance a different way from GetBalance()
533 // (GetBalance() sums up all unspent TxOuts)
534 // getbalance and getbalance '*' 0 should return the same number.
536 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
538 const CWalletTx& wtx = (*it).second;
539 if (!wtx.IsTrusted())
542 int64 allGeneratedImmature, allGeneratedMature, allFee;
543 allGeneratedImmature = allGeneratedMature = allFee = 0;
545 string strSentAccount;
546 list<pair<CTxDestination, int64> > listReceived;
547 list<pair<CTxDestination, int64> > listSent;
548 wtx.GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount);
549 if (wtx.GetDepthInMainChain() >= nMinDepth)
551 BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64)& r, listReceived)
552 nBalance += r.second;
554 BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64)& r, listSent)
555 nBalance -= r.second;
557 nBalance += allGeneratedMature;
559 return ValueFromAmount(nBalance);
562 string strAccount = AccountFromValue(params[0]);
564 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
566 return ValueFromAmount(nBalance);
570 Value movecmd(const Array& params, bool fHelp)
572 if (fHelp || params.size() < 3 || params.size() > 5)
574 "move <fromaccount> <toaccount> <amount> [minconf=1] [comment]\n"
575 "Move from one account in your wallet to another.");
577 string strFrom = AccountFromValue(params[0]);
578 string strTo = AccountFromValue(params[1]);
579 int64 nAmount = AmountFromValue(params[2]);
581 if (nAmount < MIN_TXOUT_AMOUNT)
582 throw JSONRPCError(-101, "Send amount too small");
584 if (params.size() > 3)
585 // unused parameter, used to be nMinDepth, keep type-checking it though
586 (void)params[3].get_int();
588 if (params.size() > 4)
589 strComment = params[4].get_str();
591 CWalletDB walletdb(pwalletMain->strWalletFile);
592 if (!walletdb.TxnBegin())
593 throw JSONRPCError(RPC_DATABASE_ERROR, "database error");
595 int64 nNow = GetAdjustedTime();
598 CAccountingEntry debit;
599 debit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb);
600 debit.strAccount = strFrom;
601 debit.nCreditDebit = -nAmount;
603 debit.strOtherAccount = strTo;
604 debit.strComment = strComment;
605 walletdb.WriteAccountingEntry(debit);
608 CAccountingEntry credit;
609 credit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb);
610 credit.strAccount = strTo;
611 credit.nCreditDebit = nAmount;
613 credit.strOtherAccount = strFrom;
614 credit.strComment = strComment;
615 walletdb.WriteAccountingEntry(credit);
617 if (!walletdb.TxnCommit())
618 throw JSONRPCError(RPC_DATABASE_ERROR, "database error");
624 Value sendfrom(const Array& params, bool fHelp)
626 if (fHelp || params.size() < 3 || params.size() > 6)
628 "sendfrom <fromaccount> <tonovacoinaddress> <amount> [minconf=1] [comment] [comment-to]\n"
629 "<amount> is a real and is rounded to the nearest " + FormatMoney(MIN_TXOUT_AMOUNT)
630 + HelpRequiringPassphrase());
632 string strAccount = AccountFromValue(params[0]);
633 CBitcoinAddress address(params[1].get_str());
634 if (!address.IsValid())
635 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
636 int64 nAmount = AmountFromValue(params[2]);
638 if (nAmount < MIN_TXOUT_AMOUNT)
639 throw JSONRPCError(-101, "Send amount too small");
642 if (params.size() > 3)
643 nMinDepth = params[3].get_int();
646 wtx.strFromAccount = strAccount;
647 if (params.size() > 4 && params[4].type() != null_type && !params[4].get_str().empty())
648 wtx.mapValue["comment"] = params[4].get_str();
649 if (params.size() > 5 && params[5].type() != null_type && !params[5].get_str().empty())
650 wtx.mapValue["to"] = params[5].get_str();
652 EnsureWalletIsUnlocked();
655 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
656 if (nAmount > nBalance)
657 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds");
660 string strError = pwalletMain->SendMoneyToDestination(address.Get(), nAmount, wtx);
662 throw JSONRPCError(RPC_WALLET_ERROR, strError);
664 return wtx.GetHash().GetHex();
668 Value sendmany(const Array& params, bool fHelp)
670 if (fHelp || params.size() < 2 || params.size() > 4)
672 "sendmany <fromaccount> {address:amount,...} [minconf=1] [comment]\n"
673 "amounts are double-precision floating point numbers"
674 + HelpRequiringPassphrase());
676 string strAccount = AccountFromValue(params[0]);
677 Object sendTo = params[1].get_obj();
679 if (params.size() > 2)
680 nMinDepth = params[2].get_int();
683 wtx.strFromAccount = strAccount;
684 if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
685 wtx.mapValue["comment"] = params[3].get_str();
687 set<CBitcoinAddress> setAddress;
688 vector<pair<CScript, int64> > vecSend;
690 int64 totalAmount = 0;
691 BOOST_FOREACH(const Pair& s, sendTo)
693 CBitcoinAddress address(s.name_);
694 if (!address.IsValid())
695 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid NovaCoin address: ")+s.name_);
697 if (setAddress.count(address))
698 throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+s.name_);
699 setAddress.insert(address);
701 CScript scriptPubKey;
702 scriptPubKey.SetDestination(address.Get());
703 int64 nAmount = AmountFromValue(s.value_);
705 if (nAmount < MIN_TXOUT_AMOUNT)
706 throw JSONRPCError(-101, "Send amount too small");
708 totalAmount += nAmount;
710 vecSend.push_back(make_pair(scriptPubKey, nAmount));
713 EnsureWalletIsUnlocked();
716 int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
717 if (totalAmount > nBalance)
718 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds");
721 CReserveKey keyChange(pwalletMain);
722 int64 nFeeRequired = 0;
723 bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired);
726 int64 nTotal = pwalletMain->GetBalance(), nWatchOnly = pwalletMain->GetBalanceWatchOnly();
727 if (totalAmount + nFeeRequired > nTotal - nWatchOnly)
728 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds");
729 throw JSONRPCError(RPC_WALLET_ERROR, "Transaction creation failed");
731 if (!pwalletMain->CommitTransaction(wtx, keyChange))
732 throw JSONRPCError(RPC_WALLET_ERROR, "Transaction commit failed");
734 return wtx.GetHash().GetHex();
737 Value addmultisigaddress(const Array& params, bool fHelp)
739 if (fHelp || params.size() < 2 || params.size() > 3)
741 string msg = "addmultisigaddress <nrequired> <'[\"key\",\"key\"]'> [account]\n"
742 "Add a nrequired-to-sign multisignature address to the wallet\"\n"
743 "each key is a NovaCoin address or hex-encoded public key\n"
744 "If [account] is specified, assign address to [account].";
745 throw runtime_error(msg);
748 int nRequired = params[0].get_int();
749 const Array& keys = params[1].get_array();
751 if (params.size() > 2)
752 strAccount = AccountFromValue(params[2]);
754 // Gather public keys
756 throw runtime_error("a multisignature address must require at least one key to redeem");
757 if ((int)keys.size() < nRequired)
759 strprintf("not enough keys supplied "
760 "(got %"PRIszu" keys, but need at least %d to redeem)", keys.size(), nRequired));
761 std::vector<CKey> pubkeys;
762 pubkeys.resize(keys.size());
763 for (unsigned int i = 0; i < keys.size(); i++)
765 const std::string& ks = keys[i].get_str();
767 // Case 1: Bitcoin address and we have full public key:
768 CBitcoinAddress address(ks);
769 if (address.IsValid())
772 if (!address.GetKeyID(keyID))
774 strprintf("%s does not refer to a key",ks.c_str()));
776 if (!pwalletMain->GetPubKey(keyID, vchPubKey))
778 strprintf("no full public key for address %s",ks.c_str()));
779 if (!vchPubKey.IsValid() || !pubkeys[i].SetPubKey(vchPubKey))
780 throw runtime_error(" Invalid public key: "+ks);
783 // Case 2: hex public key
786 CPubKey vchPubKey(ParseHex(ks));
787 if (!vchPubKey.IsValid() || !pubkeys[i].SetPubKey(vchPubKey))
788 throw runtime_error(" Invalid public key: "+ks);
792 throw runtime_error(" Invalid public key: "+ks);
796 // Construct using pay-to-script-hash:
798 inner.SetMultisig(nRequired, pubkeys);
799 CScriptID innerID = inner.GetID();
800 pwalletMain->AddCScript(inner);
802 pwalletMain->SetAddressBookName(innerID, strAccount);
803 return CBitcoinAddress(innerID).ToString();
806 Value addredeemscript(const Array& params, bool fHelp)
808 if (fHelp || params.size() < 1 || params.size() > 2)
810 string msg = "addredeemscript <redeemScript> [account]\n"
811 "Add a P2SH address with a specified redeemScript to the wallet.\n"
812 "If [account] is specified, assign address to [account].";
813 throw runtime_error(msg);
817 if (params.size() > 1)
818 strAccount = AccountFromValue(params[1]);
820 // Construct using pay-to-script-hash:
821 vector<unsigned char> innerData = ParseHexV(params[0], "redeemScript");
822 CScript inner(innerData.begin(), innerData.end());
823 CScriptID innerID = inner.GetID();
824 pwalletMain->AddCScript(inner);
826 pwalletMain->SetAddressBookName(innerID, strAccount);
827 return CBitcoinAddress(innerID).ToString();
837 nConf = std::numeric_limits<int>::max();
841 Value ListReceived(const Array& params, bool fByAccounts)
843 // Minimum confirmations
845 if (params.size() > 0)
846 nMinDepth = params[0].get_int();
848 // Whether to include empty accounts
849 bool fIncludeEmpty = false;
850 if (params.size() > 1)
851 fIncludeEmpty = params[1].get_bool();
854 map<CBitcoinAddress, tallyitem> mapTally;
855 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
857 const CWalletTx& wtx = (*it).second;
859 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
862 int nDepth = wtx.GetDepthInMainChain();
863 if (nDepth < nMinDepth)
866 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
868 CTxDestination address;
869 if (!ExtractDestination(txout.scriptPubKey, address) || !IsMine(*pwalletMain, address))
872 tallyitem& item = mapTally[address];
873 item.nAmount += txout.nValue;
874 item.nConf = min(item.nConf, nDepth);
880 map<string, tallyitem> mapAccountTally;
881 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
883 const CBitcoinAddress& address = item.first;
884 const string& strAccount = item.second;
885 map<CBitcoinAddress, tallyitem>::iterator it = mapTally.find(address);
886 if (it == mapTally.end() && !fIncludeEmpty)
890 int nConf = std::numeric_limits<int>::max();
891 if (it != mapTally.end())
893 nAmount = (*it).second.nAmount;
894 nConf = (*it).second.nConf;
899 tallyitem& item = mapAccountTally[strAccount];
900 item.nAmount += nAmount;
901 item.nConf = min(item.nConf, nConf);
906 obj.push_back(Pair("address", address.ToString()));
907 obj.push_back(Pair("account", strAccount));
908 obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
909 obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf)));
916 for (map<string, tallyitem>::iterator it = mapAccountTally.begin(); it != mapAccountTally.end(); ++it)
918 int64 nAmount = (*it).second.nAmount;
919 int nConf = (*it).second.nConf;
921 obj.push_back(Pair("account", (*it).first));
922 obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
923 obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf)));
931 Value listreceivedbyaddress(const Array& params, bool fHelp)
933 if (fHelp || params.size() > 2)
935 "listreceivedbyaddress [minconf=1] [includeempty=false]\n"
936 "[minconf] is the minimum number of confirmations before payments are included.\n"
937 "[includeempty] whether to include addresses that haven't received any payments.\n"
938 "Returns an array of objects containing:\n"
939 " \"address\" : receiving address\n"
940 " \"account\" : the account of the receiving address\n"
941 " \"amount\" : total amount received by the address\n"
942 " \"confirmations\" : number of confirmations of the most recent transaction included");
944 return ListReceived(params, false);
947 Value listreceivedbyaccount(const Array& params, bool fHelp)
949 if (fHelp || params.size() > 2)
951 "listreceivedbyaccount [minconf=1] [includeempty=false]\n"
952 "[minconf] is the minimum number of confirmations before payments are included.\n"
953 "[includeempty] whether to include accounts that haven't received any payments.\n"
954 "Returns an array of objects containing:\n"
955 " \"account\" : the account of the receiving addresses\n"
956 " \"amount\" : total amount received by addresses with this account\n"
957 " \"confirmations\" : number of confirmations of the most recent transaction included");
959 return ListReceived(params, true);
962 static void MaybePushAddress(Object & entry, const CTxDestination &dest)
964 CBitcoinAddress addr;
966 entry.push_back(Pair("address", addr.ToString()));
969 void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret)
971 int64 nGeneratedImmature, nGeneratedMature, nFee;
972 string strSentAccount;
973 list<pair<CTxDestination, int64> > listReceived;
974 list<pair<CTxDestination, int64> > listSent;
976 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount);
978 bool fAllAccounts = (strAccount == string("*"));
980 // Generated blocks assigned to account ""
981 if ((nGeneratedMature+nGeneratedImmature) != 0 && (fAllAccounts || strAccount == ""))
984 entry.push_back(Pair("account", string("")));
985 if (nGeneratedImmature)
987 entry.push_back(Pair("category", wtx.GetDepthInMainChain() ? "immature" : "orphan"));
988 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedImmature)));
992 entry.push_back(Pair("category", "generate"));
993 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedMature)));
996 WalletTxToJSON(wtx, entry);
997 ret.push_back(entry);
1001 if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount))
1003 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& s, listSent)
1006 entry.push_back(Pair("account", strSentAccount));
1007 MaybePushAddress(entry, s.first);
1009 if (wtx.GetDepthInMainChain() < 0) {
1010 entry.push_back(Pair("category", "conflicted"));
1012 entry.push_back(Pair("category", "send"));
1015 entry.push_back(Pair("amount", ValueFromAmount(-s.second)));
1016 entry.push_back(Pair("fee", ValueFromAmount(-nFee)));
1018 WalletTxToJSON(wtx, entry);
1019 ret.push_back(entry);
1024 if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth)
1026 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& r, listReceived)
1029 if (pwalletMain->mapAddressBook.count(r.first))
1030 account = pwalletMain->mapAddressBook[r.first];
1031 if (fAllAccounts || (account == strAccount))
1034 entry.push_back(Pair("account", account));
1035 MaybePushAddress(entry, r.first);
1036 if (wtx.IsCoinBase())
1038 if (wtx.GetDepthInMainChain() < 1)
1039 entry.push_back(Pair("category", "orphan"));
1040 else if (wtx.GetBlocksToMaturity() > 0)
1041 entry.push_back(Pair("category", "immature"));
1043 entry.push_back(Pair("category", "generate"));
1046 entry.push_back(Pair("category", "receive"));
1047 entry.push_back(Pair("amount", ValueFromAmount(r.second)));
1049 WalletTxToJSON(wtx, entry);
1050 ret.push_back(entry);
1056 void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Array& ret)
1058 bool fAllAccounts = (strAccount == string("*"));
1060 if (fAllAccounts || acentry.strAccount == strAccount)
1063 entry.push_back(Pair("account", acentry.strAccount));
1064 entry.push_back(Pair("category", "move"));
1065 entry.push_back(Pair("time", (boost::int64_t)acentry.nTime));
1066 entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit)));
1067 entry.push_back(Pair("otheraccount", acentry.strOtherAccount));
1068 entry.push_back(Pair("comment", acentry.strComment));
1069 ret.push_back(entry);
1073 Value listtransactions(const Array& params, bool fHelp)
1075 if (fHelp || params.size() > 3)
1076 throw runtime_error(
1077 "listtransactions [account] [count=10] [from=0]\n"
1078 "Returns up to [count] most recent transactions skipping the first [from] transactions for account [account].");
1080 string strAccount = "*";
1081 if (params.size() > 0)
1082 strAccount = params[0].get_str();
1084 if (params.size() > 1)
1085 nCount = params[1].get_int();
1087 if (params.size() > 2)
1088 nFrom = params[2].get_int();
1091 throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count");
1093 throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative from");
1097 std::list<CAccountingEntry> acentries;
1098 CWallet::TxItems txOrdered = pwalletMain->OrderedTxItems(acentries, strAccount);
1100 // iterate backwards until we have nCount items to return:
1101 for (CWallet::TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
1103 CWalletTx *const pwtx = (*it).second.first;
1105 ListTransactions(*pwtx, strAccount, 0, true, ret);
1106 CAccountingEntry *const pacentry = (*it).second.second;
1108 AcentryToJSON(*pacentry, strAccount, ret);
1110 if ((int)ret.size() >= (nCount+nFrom)) break;
1112 // ret is newest to oldest
1114 if (nFrom > (int)ret.size())
1116 if ((nFrom + nCount) > (int)ret.size())
1117 nCount = ret.size() - nFrom;
1118 Array::iterator first = ret.begin();
1119 std::advance(first, nFrom);
1120 Array::iterator last = ret.begin();
1121 std::advance(last, nFrom+nCount);
1123 if (last != ret.end()) ret.erase(last, ret.end());
1124 if (first != ret.begin()) ret.erase(ret.begin(), first);
1126 std::reverse(ret.begin(), ret.end()); // Return oldest to newest
1131 Value listaccounts(const Array& params, bool fHelp)
1133 if (fHelp || params.size() > 1)
1134 throw runtime_error(
1135 "listaccounts [minconf=1]\n"
1136 "Returns Object that has account names as keys, account balances as values.");
1139 if (params.size() > 0)
1140 nMinDepth = params[0].get_int();
1142 map<string, int64> mapAccountBalances;
1143 BOOST_FOREACH(const PAIRTYPE(CTxDestination, string)& entry, pwalletMain->mapAddressBook) {
1144 if (IsMine(*pwalletMain, entry.first)) // This address belongs to me
1145 mapAccountBalances[entry.second] = 0;
1148 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
1150 const CWalletTx& wtx = (*it).second;
1151 int64 nGeneratedImmature, nGeneratedMature, nFee;
1152 string strSentAccount;
1153 list<pair<CTxDestination, int64> > listReceived;
1154 list<pair<CTxDestination, int64> > listSent;
1155 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount);
1156 mapAccountBalances[strSentAccount] -= nFee;
1157 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& s, listSent)
1158 mapAccountBalances[strSentAccount] -= s.second;
1159 if (wtx.GetDepthInMainChain() >= nMinDepth)
1161 mapAccountBalances[""] += nGeneratedMature;
1162 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& r, listReceived)
1163 if (pwalletMain->mapAddressBook.count(r.first))
1164 mapAccountBalances[pwalletMain->mapAddressBook[r.first]] += r.second;
1166 mapAccountBalances[""] += r.second;
1170 list<CAccountingEntry> acentries;
1171 CWalletDB(pwalletMain->strWalletFile).ListAccountCreditDebit("*", acentries);
1172 BOOST_FOREACH(const CAccountingEntry& entry, acentries)
1173 mapAccountBalances[entry.strAccount] += entry.nCreditDebit;
1176 BOOST_FOREACH(const PAIRTYPE(string, int64)& accountBalance, mapAccountBalances) {
1177 ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second)));
1182 Value listsinceblock(const Array& params, bool fHelp)
1185 throw runtime_error(
1186 "listsinceblock [blockhash] [target-confirmations]\n"
1187 "Get all transactions in blocks since block [blockhash], or all transactions if omitted");
1189 CBlockIndex *pindex = NULL;
1190 int target_confirms = 1;
1192 if (params.size() > 0)
1194 uint256 blockId = 0;
1196 blockId.SetHex(params[0].get_str());
1197 pindex = CBlockLocator(blockId).GetBlockIndex();
1200 if (params.size() > 1)
1202 target_confirms = params[1].get_int();
1204 if (target_confirms < 1)
1205 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter");
1208 int depth = pindex ? (1 + nBestHeight - pindex->nHeight) : -1;
1212 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); it++)
1214 CWalletTx tx = (*it).second;
1216 if (depth == -1 || tx.GetDepthInMainChain() < depth)
1217 ListTransactions(tx, "*", 0, true, transactions);
1222 if (target_confirms == 1)
1224 lastblock = hashBestChain;
1228 int target_height = pindexBest->nHeight + 1 - target_confirms;
1231 for (block = pindexBest;
1232 block && block->nHeight > target_height;
1233 block = block->pprev) { }
1235 lastblock = block ? block->GetBlockHash() : 0;
1239 ret.push_back(Pair("transactions", transactions));
1240 ret.push_back(Pair("lastblock", lastblock.GetHex()));
1245 Value gettransaction(const Array& params, bool fHelp)
1247 if (fHelp || params.size() != 1)
1248 throw runtime_error(
1249 "gettransaction <txid>\n"
1250 "Get detailed information about <txid>");
1253 hash.SetHex(params[0].get_str());
1257 if (pwalletMain->mapWallet.count(hash))
1259 const CWalletTx& wtx = pwalletMain->mapWallet[hash];
1261 TxToJSON(wtx, 0, entry);
1263 int64 nCredit = wtx.GetCredit();
1264 int64 nDebit = wtx.GetDebit();
1265 int64 nNet = nCredit - nDebit;
1266 int64 nFee = (wtx.IsFromMe() ? wtx.GetValueOut() - nDebit : 0);
1268 entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee)));
1270 entry.push_back(Pair("fee", ValueFromAmount(nFee)));
1272 WalletTxToJSON(wtx, entry);
1275 ListTransactions(pwalletMain->mapWallet[hash], "*", 0, false, details);
1276 entry.push_back(Pair("details", details));
1281 uint256 hashBlock = 0;
1282 if (GetTransaction(hash, tx, hashBlock))
1284 TxToJSON(tx, 0, entry);
1286 entry.push_back(Pair("confirmations", 0));
1289 entry.push_back(Pair("blockhash", hashBlock.GetHex()));
1290 map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashBlock);
1291 if (mi != mapBlockIndex.end() && (*mi).second)
1293 CBlockIndex* pindex = (*mi).second;
1294 if (pindex->IsInMainChain())
1295 entry.push_back(Pair("confirmations", 1 + nBestHeight - pindex->nHeight));
1297 entry.push_back(Pair("confirmations", 0));
1302 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction");
1309 Value backupwallet(const Array& params, bool fHelp)
1311 if (fHelp || params.size() != 1)
1312 throw runtime_error(
1313 "backupwallet <destination>\n"
1314 "Safely copies wallet.dat to destination, which can be a directory or a path with filename.");
1316 string strDest = params[0].get_str();
1317 if (!BackupWallet(*pwalletMain, strDest))
1318 throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet backup failed!");
1324 Value keypoolrefill(const Array& params, bool fHelp)
1326 if (fHelp || params.size() > 1)
1327 throw runtime_error(
1328 "keypoolrefill [new-size]\n"
1329 "Fills the keypool."
1330 + HelpRequiringPassphrase());
1332 unsigned int nSize = max(GetArg("-keypool", 100), 0LL);
1333 if (params.size() > 0) {
1334 if (params[0].get_int() < 0)
1335 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected valid size");
1336 nSize = (unsigned int) params[0].get_int();
1339 EnsureWalletIsUnlocked();
1341 pwalletMain->TopUpKeyPool(nSize);
1343 if (pwalletMain->GetKeyPoolSize() < nSize)
1344 throw JSONRPCError(RPC_WALLET_ERROR, "Error refreshing keypool.");
1350 void ThreadTopUpKeyPool(void* parg)
1352 // Make this thread recognisable as the key-topping-up thread
1353 RenameThread("novacoin-key-top");
1355 pwalletMain->TopUpKeyPool();
1358 void ThreadCleanWalletPassphrase(void* parg)
1360 // Make this thread recognisable as the wallet relocking thread
1361 RenameThread("novacoin-lock-wa");
1363 int64 nMyWakeTime = GetTimeMillis() + *((int64*)parg) * 1000;
1365 ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime);
1367 if (nWalletUnlockTime == 0)
1369 nWalletUnlockTime = nMyWakeTime;
1373 if (nWalletUnlockTime==0)
1375 int64 nToSleep = nWalletUnlockTime - GetTimeMillis();
1379 LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime);
1381 ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime);
1385 if (nWalletUnlockTime)
1387 nWalletUnlockTime = 0;
1388 pwalletMain->Lock();
1393 if (nWalletUnlockTime < nMyWakeTime)
1394 nWalletUnlockTime = nMyWakeTime;
1397 LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime);
1399 delete (int64*)parg;
1402 Value walletpassphrase(const Array& params, bool fHelp)
1404 if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 3))
1405 throw runtime_error(
1406 "walletpassphrase <passphrase> <timeout> [mintonly]\n"
1407 "Stores the wallet decryption key in memory for <timeout> seconds.\n"
1408 "mintonly is optional true/false allowing only block minting.");
1411 if (!pwalletMain->IsCrypted())
1412 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called.");
1414 if (!pwalletMain->IsLocked())
1415 throw JSONRPCError(RPC_WALLET_ALREADY_UNLOCKED, "Error: Wallet is already unlocked, use walletlock first if need to change unlock settings.");
1416 // Note that the walletpassphrase is stored in params[0] which is not mlock()ed
1417 SecureString strWalletPass;
1418 strWalletPass.reserve(100);
1419 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1420 // Alternately, find a way to make params[0] mlock()'d to begin with.
1421 strWalletPass = params[0].get_str().c_str();
1423 if (strWalletPass.length() > 0)
1425 if (!pwalletMain->Unlock(strWalletPass))
1426 throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
1429 throw runtime_error(
1430 "walletpassphrase <passphrase> <timeout>\n"
1431 "Stores the wallet decryption key in memory for <timeout> seconds.");
1433 NewThread(ThreadTopUpKeyPool, NULL);
1434 int64* pnSleepTime = new int64(params[1].get_int64());
1435 NewThread(ThreadCleanWalletPassphrase, pnSleepTime);
1437 // ppcoin: if user OS account compromised prevent trivial sendmoney commands
1438 if (params.size() > 2)
1439 fWalletUnlockMintOnly = params[2].get_bool();
1441 fWalletUnlockMintOnly = false;
1447 Value walletpassphrasechange(const Array& params, bool fHelp)
1449 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2))
1450 throw runtime_error(
1451 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1452 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1455 if (!pwalletMain->IsCrypted())
1456 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrasechange was called.");
1458 // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string)
1459 // Alternately, find a way to make params[0] mlock()'d to begin with.
1460 SecureString strOldWalletPass;
1461 strOldWalletPass.reserve(100);
1462 strOldWalletPass = params[0].get_str().c_str();
1464 SecureString strNewWalletPass;
1465 strNewWalletPass.reserve(100);
1466 strNewWalletPass = params[1].get_str().c_str();
1468 if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1)
1469 throw runtime_error(
1470 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1471 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1473 if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass))
1474 throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
1480 Value walletlock(const Array& params, bool fHelp)
1482 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 0))
1483 throw runtime_error(
1485 "Removes the wallet encryption key from memory, locking the wallet.\n"
1486 "After calling this method, you will need to call walletpassphrase again\n"
1487 "before being able to call any methods which require the wallet to be unlocked.");
1490 if (!pwalletMain->IsCrypted())
1491 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletlock was called.");
1494 LOCK(cs_nWalletUnlockTime);
1495 pwalletMain->Lock();
1496 nWalletUnlockTime = 0;
1503 Value encryptwallet(const Array& params, bool fHelp)
1505 if (!pwalletMain->IsCrypted() && (fHelp || params.size() != 1))
1506 throw runtime_error(
1507 "encryptwallet <passphrase>\n"
1508 "Encrypts the wallet with <passphrase>.");
1511 if (pwalletMain->IsCrypted())
1512 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an encrypted wallet, but encryptwallet was called.");
1514 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1515 // Alternately, find a way to make params[0] mlock()'d to begin with.
1516 SecureString strWalletPass;
1517 strWalletPass.reserve(100);
1518 strWalletPass = params[0].get_str().c_str();
1520 if (strWalletPass.length() < 1)
1521 throw runtime_error(
1522 "encryptwallet <passphrase>\n"
1523 "Encrypts the wallet with <passphrase>.");
1525 if (!pwalletMain->EncryptWallet(strWalletPass))
1526 throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: Failed to encrypt the wallet.");
1528 // BDB seems to have a bad habit of writing old data into
1529 // slack space in .dat files; that is bad if the old data is
1530 // unencrypted private keys. So:
1532 return "wallet encrypted; NovaCoin server stopping, restart to run with encrypted wallet. The keypool has been flushed, you need to make a new backup.";
1535 class DescribeAddressVisitor : public boost::static_visitor<Object>
1540 DescribeAddressVisitor(isminetype mineIn) : mine(mineIn) {}
1542 Object operator()(const CNoDestination &dest) const { return Object(); }
1543 Object operator()(const CKeyID &keyID) const {
1546 pwalletMain->GetPubKey(keyID, vchPubKey);
1547 obj.push_back(Pair("isscript", false));
1548 if (mine == MINE_SPENDABLE) {
1549 pwalletMain->GetPubKey(keyID, vchPubKey);
1550 obj.push_back(Pair("pubkey", HexStr(vchPubKey.Raw())));
1551 obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed()));
1556 Object operator()(const CScriptID &scriptID) const {
1558 obj.push_back(Pair("isscript", true));
1559 if (mine == MINE_SPENDABLE) {
1561 pwalletMain->GetCScript(scriptID, subscript);
1562 std::vector<CTxDestination> addresses;
1563 txnouttype whichType;
1565 ExtractDestinations(subscript, whichType, addresses, nRequired);
1566 obj.push_back(Pair("script", GetTxnOutputType(whichType)));
1567 obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end())));
1569 BOOST_FOREACH(const CTxDestination& addr, addresses)
1570 a.push_back(CBitcoinAddress(addr).ToString());
1571 obj.push_back(Pair("addresses", a));
1572 if (whichType == TX_MULTISIG)
1573 obj.push_back(Pair("sigsrequired", nRequired));
1579 Value validateaddress(const Array& params, bool fHelp)
1581 if (fHelp || params.size() != 1)
1582 throw runtime_error(
1583 "validateaddress <novacoinaddress>\n"
1584 "Return information about <novacoinaddress>.");
1586 CBitcoinAddress address(params[0].get_str());
1587 bool isValid = address.IsValid();
1590 ret.push_back(Pair("isvalid", isValid));
1593 CTxDestination dest = address.Get();
1594 string currentAddress = address.ToString();
1595 ret.push_back(Pair("address", currentAddress));
1596 isminetype mine = pwalletMain ? IsMine(*pwalletMain, dest) : MINE_NO;
1597 ret.push_back(Pair("ismine", mine != MINE_NO));
1598 if (mine != MINE_NO) {
1599 ret.push_back(Pair("watchonly", mine == MINE_WATCH_ONLY));
1600 Object detail = boost::apply_visitor(DescribeAddressVisitor(mine), dest);
1601 ret.insert(ret.end(), detail.begin(), detail.end());
1603 if (pwalletMain->mapAddressBook.count(dest))
1604 ret.push_back(Pair("account", pwalletMain->mapAddressBook[dest]));
1609 // ppcoin: reserve balance from being staked for network protection
1610 Value reservebalance(const Array& params, bool fHelp)
1612 if (fHelp || params.size() > 2)
1613 throw runtime_error(
1614 "reservebalance [<reserve> [amount]]\n"
1615 "<reserve> is true or false to turn balance reserve on or off.\n"
1616 "<amount> is a real and rounded to cent.\n"
1617 "Set reserve amount not participating in network protection.\n"
1618 "If no parameters provided current setting is printed.\n");
1620 if (params.size() > 0)
1622 bool fReserve = params[0].get_bool();
1625 if (params.size() == 1)
1626 throw runtime_error("must provide amount to reserve balance.\n");
1627 int64 nAmount = AmountFromValue(params[1]);
1628 nAmount = (nAmount / CENT) * CENT; // round to cent
1630 throw runtime_error("amount cannot be negative.\n");
1631 mapArgs["-reservebalance"] = FormatMoney(nAmount).c_str();
1635 if (params.size() > 1)
1636 throw runtime_error("cannot specify amount to turn off reserve.\n");
1637 mapArgs["-reservebalance"] = "0";
1642 int64 nReserveBalance = 0;
1643 if (mapArgs.count("-reservebalance") && !ParseMoney(mapArgs["-reservebalance"], nReserveBalance))
1644 throw runtime_error("invalid reserve balance amount\n");
1645 result.push_back(Pair("reserve", (nReserveBalance > 0)));
1646 result.push_back(Pair("amount", ValueFromAmount(nReserveBalance)));
1651 // ppcoin: check wallet integrity
1652 Value checkwallet(const Array& params, bool fHelp)
1654 if (fHelp || params.size() > 0)
1655 throw runtime_error(
1657 "Check wallet for integrity.\n");
1660 int64 nBalanceInQuestion;
1661 pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion, true);
1663 if (nMismatchSpent == 0)
1664 result.push_back(Pair("wallet check passed", true));
1667 result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1668 result.push_back(Pair("amount in question", ValueFromAmount(nBalanceInQuestion)));
1674 // ppcoin: repair wallet
1675 Value repairwallet(const Array& params, bool fHelp)
1677 if (fHelp || params.size() > 0)
1678 throw runtime_error(
1680 "Repair wallet if checkwallet reports any problem.\n");
1683 int64 nBalanceInQuestion;
1684 pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion);
1686 if (nMismatchSpent == 0)
1687 result.push_back(Pair("wallet check passed", true));
1690 result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1691 result.push_back(Pair("amount affected by repair", ValueFromAmount(nBalanceInQuestion)));
1696 // NovaCoin: resend unconfirmed wallet transactions
1697 Value resendtx(const Array& params, bool fHelp)
1699 if (fHelp || params.size() > 1)
1700 throw runtime_error(
1702 "Re-send unconfirmed transactions.\n"
1705 ResendWalletTransactions();
1710 // ppcoin: make a public-private key pair
1711 Value makekeypair(const Array& params, bool fHelp)
1713 if (fHelp || params.size() > 1)
1714 throw runtime_error(
1715 "makekeypair [prefix]\n"
1716 "Make a public/private key pair.\n"
1717 "[prefix] is optional preferred prefix for the public key.\n");
1719 string strPrefix = "";
1720 if (params.size() > 0)
1721 strPrefix = params[0].get_str();
1724 key.MakeNewKey(false);
1726 CPrivKey vchPrivKey = key.GetPrivKey();
1728 result.push_back(Pair("PrivateKey", HexStr<CPrivKey::iterator>(vchPrivKey.begin(), vchPrivKey.end())));
1729 result.push_back(Pair("PublicKey", HexStr(key.GetPubKey().Raw())));