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->GetWatchOnlyBalance())));
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 mergecoins(const Array& params, bool fHelp)
260 if (fHelp || params.size() != 3)
262 "mergecoins <amount> <outputvalue> <maxvalue>\n"
263 "<amount> is resulting inputs sum\n"
264 "<outputvalue> is resulting value of inputs which will be created\n"
265 "<maxvalue> is maximum value of inputs which are used in join process\n"
266 "All values are real and and rounded to the nearest " + FormatMoney(MIN_TXOUT_AMOUNT)
267 + HelpRequiringPassphrase());
269 if (pwalletMain->IsLocked())
270 throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first.");
273 int64 nAmount = AmountFromValue(params[0]);
276 int64 nOutputValue = AmountFromValue(params[1]);
279 int64 nMaxValue = AmountFromValue(params[2]);
281 if (nAmount < MIN_TXOUT_AMOUNT)
282 throw JSONRPCError(-101, "Send amount too small");
284 if (nOutputValue < MIN_TXOUT_AMOUNT)
285 throw JSONRPCError(-101, "Output value too small");
287 if (nMaxValue < MIN_TXOUT_AMOUNT)
288 throw JSONRPCError(-101, "Max value too small");
290 if (nOutputValue < nMaxValue)
291 throw JSONRPCError(-101, "Output value is lower than max value");
294 list<uint256> listMerged;
295 if (!pwalletMain->MergeCoins(nAmount, nMaxValue, nOutputValue, listMerged))
299 BOOST_FOREACH(const uint256 txHash, listMerged)
300 mergedHashes.push_back(txHash.GetHex());
305 Value sendtoaddress(const Array& params, bool fHelp)
307 if (fHelp || params.size() < 2 || params.size() > 4)
309 "sendtoaddress <novacoinaddress> <amount> [comment] [comment-to]\n"
310 "<amount> is a real and is rounded to the nearest " + FormatMoney(MIN_TXOUT_AMOUNT)
311 + HelpRequiringPassphrase());
313 CBitcoinAddress address(params[0].get_str());
314 if (!address.IsValid())
315 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
318 int64 nAmount = AmountFromValue(params[1]);
320 if (nAmount < MIN_TXOUT_AMOUNT)
321 throw JSONRPCError(-101, "Send amount too small");
325 if (params.size() > 2 && params[2].type() != null_type && !params[2].get_str().empty())
326 wtx.mapValue["comment"] = params[2].get_str();
327 if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
328 wtx.mapValue["to"] = params[3].get_str();
330 if (pwalletMain->IsLocked())
331 throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first.");
333 string strError = pwalletMain->SendMoneyToDestination(address.Get(), nAmount, wtx);
335 throw JSONRPCError(RPC_WALLET_ERROR, strError);
337 return wtx.GetHash().GetHex();
340 Value listaddressgroupings(const Array& params, bool fHelp)
344 "listaddressgroupings\n"
345 "Lists groups of addresses which have had their common ownership\n"
346 "made public by common use as inputs or as the resulting change\n"
347 "in past transactions");
350 map<CTxDestination, int64> balances = pwalletMain->GetAddressBalances();
351 BOOST_FOREACH(set<CTxDestination> grouping, pwalletMain->GetAddressGroupings())
354 BOOST_FOREACH(CTxDestination address, grouping)
357 addressInfo.push_back(CBitcoinAddress(address).ToString());
358 addressInfo.push_back(ValueFromAmount(balances[address]));
360 LOCK(pwalletMain->cs_wallet);
361 if (pwalletMain->mapAddressBook.find(CBitcoinAddress(address).Get()) != pwalletMain->mapAddressBook.end())
362 addressInfo.push_back(pwalletMain->mapAddressBook.find(CBitcoinAddress(address).Get())->second);
364 jsonGrouping.push_back(addressInfo);
366 jsonGroupings.push_back(jsonGrouping);
368 return jsonGroupings;
371 Value signmessage(const Array& params, bool fHelp)
373 if (fHelp || params.size() != 2)
375 "signmessage <novacoinaddress> <message>\n"
376 "Sign a message with the private key of an address");
378 EnsureWalletIsUnlocked();
380 string strAddress = params[0].get_str();
381 string strMessage = params[1].get_str();
383 CBitcoinAddress addr(strAddress);
385 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
388 if (!addr.GetKeyID(keyID))
389 throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
392 if (!pwalletMain->GetKey(keyID, key))
393 throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available");
395 CDataStream ss(SER_GETHASH, 0);
396 ss << strMessageMagic;
399 vector<unsigned char> vchSig;
400 if (!key.SignCompact(Hash(ss.begin(), ss.end()), vchSig))
401 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed");
403 return EncodeBase64(&vchSig[0], vchSig.size());
406 Value verifymessage(const Array& params, bool fHelp)
408 if (fHelp || params.size() != 3)
410 "verifymessage <novacoinaddress> <signature> <message>\n"
411 "Verify a signed message");
413 string strAddress = params[0].get_str();
414 string strSign = params[1].get_str();
415 string strMessage = params[2].get_str();
417 CBitcoinAddress addr(strAddress);
419 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
422 if (!addr.GetKeyID(keyID))
423 throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
425 bool fInvalid = false;
426 vector<unsigned char> vchSig = DecodeBase64(strSign.c_str(), &fInvalid);
429 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Malformed base64 encoding");
431 CDataStream ss(SER_GETHASH, 0);
432 ss << strMessageMagic;
436 if (!key.SetCompactSignature(Hash(ss.begin(), ss.end()), vchSig))
439 return (key.GetPubKey().GetID() == keyID);
443 Value getreceivedbyaddress(const Array& params, bool fHelp)
445 if (fHelp || params.size() < 1 || params.size() > 2)
447 "getreceivedbyaddress <novacoinaddress> [minconf=1]\n"
448 "Returns the total amount received by <novacoinaddress> in transactions with at least [minconf] confirmations.");
451 CBitcoinAddress address = CBitcoinAddress(params[0].get_str());
452 CScript scriptPubKey;
453 if (!address.IsValid())
454 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
455 scriptPubKey.SetDestination(address.Get());
456 if (!IsMine(*pwalletMain,scriptPubKey))
459 // Minimum confirmations
461 if (params.size() > 1)
462 nMinDepth = params[1].get_int();
466 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
468 const CWalletTx& wtx = (*it).second;
469 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
472 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
473 if (txout.scriptPubKey == scriptPubKey)
474 if (wtx.GetDepthInMainChain() >= nMinDepth)
475 nAmount += txout.nValue;
478 return ValueFromAmount(nAmount);
482 void GetAccountAddresses(string strAccount, set<CTxDestination>& setAddress)
484 BOOST_FOREACH(const PAIRTYPE(CTxDestination, string)& item, pwalletMain->mapAddressBook)
486 const CTxDestination& address = item.first;
487 const string& strName = item.second;
488 if (strName == strAccount)
489 setAddress.insert(address);
493 Value getreceivedbyaccount(const Array& params, bool fHelp)
495 if (fHelp || params.size() < 1 || params.size() > 2)
497 "getreceivedbyaccount <account> [minconf=1]\n"
498 "Returns the total amount received by addresses with <account> in transactions with at least [minconf] confirmations.");
500 // Minimum confirmations
502 if (params.size() > 1)
503 nMinDepth = params[1].get_int();
505 // Get the set of pub keys assigned to account
506 string strAccount = AccountFromValue(params[0]);
507 set<CTxDestination> setAddress;
508 GetAccountAddresses(strAccount, setAddress);
512 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
514 const CWalletTx& wtx = (*it).second;
515 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
518 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
520 CTxDestination address;
521 if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*pwalletMain, address) && setAddress.count(address))
522 if (wtx.GetDepthInMainChain() >= nMinDepth)
523 nAmount += txout.nValue;
527 return (double)nAmount / (double)COIN;
531 int64 GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth, const isminefilter& filter)
535 // Tally wallet transactions
536 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
538 const CWalletTx& wtx = (*it).second;
542 int64 nGenerated, nReceived, nSent, nFee;
543 wtx.GetAccountAmounts(strAccount, nGenerated, nReceived, nSent, nFee, filter);
545 if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth)
546 nBalance += nReceived;
547 nBalance += nGenerated - nSent - nFee;
550 // Tally internal accounting entries
551 nBalance += walletdb.GetAccountCreditDebit(strAccount);
556 int64 GetAccountBalance(const string& strAccount, int nMinDepth, const isminefilter& filter)
558 CWalletDB walletdb(pwalletMain->strWalletFile);
559 return GetAccountBalance(walletdb, strAccount, nMinDepth, filter);
563 Value getbalance(const Array& params, bool fHelp)
565 if (fHelp || params.size() > 2)
567 "getbalance [account] [minconf=1] [watchonly=0]\n"
568 "If [account] is not specified, returns the server's total available balance.\n"
569 "If [account] is specified, returns the balance in the account.\n"
570 "if [includeWatchonly] is specified, include balance in watchonly addresses (see 'importaddress').");
572 if (params.size() == 0)
573 return ValueFromAmount(pwalletMain->GetBalance());
576 if (params.size() > 1)
577 nMinDepth = params[1].get_int();
578 isminefilter filter = MINE_SPENDABLE;
579 if(params.size() > 2)
580 if(params[2].get_bool())
581 filter = filter | MINE_WATCH_ONLY;
583 if (params[0].get_str() == "*") {
584 // Calculate total balance a different way from GetBalance()
585 // (GetBalance() sums up all unspent TxOuts)
586 // getbalance and getbalance '*' 0 should return the same number.
588 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
590 const CWalletTx& wtx = (*it).second;
591 if (!wtx.IsTrusted())
594 int64 allGeneratedImmature, allGeneratedMature, allFee;
595 allGeneratedImmature = allGeneratedMature = allFee = 0;
597 string strSentAccount;
598 list<pair<CTxDestination, int64> > listReceived;
599 list<pair<CTxDestination, int64> > listSent;
600 wtx.GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount, filter);
601 if (wtx.GetDepthInMainChain() >= nMinDepth)
603 BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64)& r, listReceived)
604 nBalance += r.second;
606 BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64)& r, listSent)
607 nBalance -= r.second;
609 nBalance += allGeneratedMature;
611 return ValueFromAmount(nBalance);
614 string strAccount = AccountFromValue(params[0]);
616 int64 nBalance = GetAccountBalance(strAccount, nMinDepth, filter);
618 return ValueFromAmount(nBalance);
622 Value movecmd(const Array& params, bool fHelp)
624 if (fHelp || params.size() < 3 || params.size() > 5)
626 "move <fromaccount> <toaccount> <amount> [minconf=1] [comment]\n"
627 "Move from one account in your wallet to another.");
629 string strFrom = AccountFromValue(params[0]);
630 string strTo = AccountFromValue(params[1]);
631 int64 nAmount = AmountFromValue(params[2]);
633 if (nAmount < MIN_TXOUT_AMOUNT)
634 throw JSONRPCError(-101, "Send amount too small");
636 if (params.size() > 3)
637 // unused parameter, used to be nMinDepth, keep type-checking it though
638 (void)params[3].get_int();
640 if (params.size() > 4)
641 strComment = params[4].get_str();
643 CWalletDB walletdb(pwalletMain->strWalletFile);
644 if (!walletdb.TxnBegin())
645 throw JSONRPCError(RPC_DATABASE_ERROR, "database error");
647 int64 nNow = GetAdjustedTime();
650 CAccountingEntry debit;
651 debit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb);
652 debit.strAccount = strFrom;
653 debit.nCreditDebit = -nAmount;
655 debit.strOtherAccount = strTo;
656 debit.strComment = strComment;
657 walletdb.WriteAccountingEntry(debit);
660 CAccountingEntry credit;
661 credit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb);
662 credit.strAccount = strTo;
663 credit.nCreditDebit = nAmount;
665 credit.strOtherAccount = strFrom;
666 credit.strComment = strComment;
667 walletdb.WriteAccountingEntry(credit);
669 if (!walletdb.TxnCommit())
670 throw JSONRPCError(RPC_DATABASE_ERROR, "database error");
676 Value sendfrom(const Array& params, bool fHelp)
678 if (fHelp || params.size() < 3 || params.size() > 6)
680 "sendfrom <fromaccount> <tonovacoinaddress> <amount> [minconf=1] [comment] [comment-to]\n"
681 "<amount> is a real and is rounded to the nearest " + FormatMoney(MIN_TXOUT_AMOUNT)
682 + HelpRequiringPassphrase());
684 string strAccount = AccountFromValue(params[0]);
685 CBitcoinAddress address(params[1].get_str());
686 if (!address.IsValid())
687 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
688 int64 nAmount = AmountFromValue(params[2]);
690 if (nAmount < MIN_TXOUT_AMOUNT)
691 throw JSONRPCError(-101, "Send amount too small");
694 if (params.size() > 3)
695 nMinDepth = params[3].get_int();
698 wtx.strFromAccount = strAccount;
699 if (params.size() > 4 && params[4].type() != null_type && !params[4].get_str().empty())
700 wtx.mapValue["comment"] = params[4].get_str();
701 if (params.size() > 5 && params[5].type() != null_type && !params[5].get_str().empty())
702 wtx.mapValue["to"] = params[5].get_str();
704 EnsureWalletIsUnlocked();
707 int64 nBalance = GetAccountBalance(strAccount, nMinDepth, MINE_SPENDABLE);
708 if (nAmount > nBalance)
709 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds");
712 string strError = pwalletMain->SendMoneyToDestination(address.Get(), nAmount, wtx);
714 throw JSONRPCError(RPC_WALLET_ERROR, strError);
716 return wtx.GetHash().GetHex();
720 Value sendmany(const Array& params, bool fHelp)
722 if (fHelp || params.size() < 2 || params.size() > 4)
724 "sendmany <fromaccount> {address:amount,...} [minconf=1] [comment]\n"
725 "amounts are double-precision floating point numbers"
726 + HelpRequiringPassphrase());
728 string strAccount = AccountFromValue(params[0]);
729 Object sendTo = params[1].get_obj();
731 if (params.size() > 2)
732 nMinDepth = params[2].get_int();
735 wtx.strFromAccount = strAccount;
736 if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
737 wtx.mapValue["comment"] = params[3].get_str();
739 set<CBitcoinAddress> setAddress;
740 vector<pair<CScript, int64> > vecSend;
742 int64 totalAmount = 0;
743 BOOST_FOREACH(const Pair& s, sendTo)
745 CBitcoinAddress address(s.name_);
746 if (!address.IsValid())
747 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid NovaCoin address: ")+s.name_);
749 if (setAddress.count(address))
750 throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+s.name_);
751 setAddress.insert(address);
753 CScript scriptPubKey;
754 scriptPubKey.SetDestination(address.Get());
755 int64 nAmount = AmountFromValue(s.value_);
757 if (nAmount < MIN_TXOUT_AMOUNT)
758 throw JSONRPCError(-101, "Send amount too small");
760 totalAmount += nAmount;
762 vecSend.push_back(make_pair(scriptPubKey, nAmount));
765 EnsureWalletIsUnlocked();
768 int64 nBalance = GetAccountBalance(strAccount, nMinDepth, MINE_SPENDABLE);
769 if (totalAmount > nBalance)
770 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds");
773 CReserveKey keyChange(pwalletMain);
774 int64 nFeeRequired = 0;
775 bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired);
778 int64 nTotal = pwalletMain->GetBalance(), nWatchOnly = pwalletMain->GetWatchOnlyBalance();
779 if (totalAmount + nFeeRequired > nTotal - nWatchOnly)
780 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds");
781 throw JSONRPCError(RPC_WALLET_ERROR, "Transaction creation failed");
783 if (!pwalletMain->CommitTransaction(wtx, keyChange))
784 throw JSONRPCError(RPC_WALLET_ERROR, "Transaction commit failed");
786 return wtx.GetHash().GetHex();
789 Value addmultisigaddress(const Array& params, bool fHelp)
791 if (fHelp || params.size() < 2 || params.size() > 3)
793 string msg = "addmultisigaddress <nrequired> <'[\"key\",\"key\"]'> [account]\n"
794 "Add a nrequired-to-sign multisignature address to the wallet\"\n"
795 "each key is a NovaCoin address or hex-encoded public key\n"
796 "If [account] is specified, assign address to [account].";
797 throw runtime_error(msg);
800 int nRequired = params[0].get_int();
801 const Array& keys = params[1].get_array();
803 if (params.size() > 2)
804 strAccount = AccountFromValue(params[2]);
806 // Gather public keys
808 throw runtime_error("a multisignature address must require at least one key to redeem");
809 if ((int)keys.size() < nRequired)
811 strprintf("not enough keys supplied "
812 "(got %"PRIszu" keys, but need at least %d to redeem)", keys.size(), nRequired));
813 std::vector<CKey> pubkeys;
814 pubkeys.resize(keys.size());
815 for (unsigned int i = 0; i < keys.size(); i++)
817 const std::string& ks = keys[i].get_str();
819 // Case 1: Bitcoin address and we have full public key:
820 CBitcoinAddress address(ks);
821 if (address.IsValid())
824 if (!address.GetKeyID(keyID))
826 strprintf("%s does not refer to a key",ks.c_str()));
828 if (!pwalletMain->GetPubKey(keyID, vchPubKey))
830 strprintf("no full public key for address %s",ks.c_str()));
831 if (!vchPubKey.IsValid() || !pubkeys[i].SetPubKey(vchPubKey))
832 throw runtime_error(" Invalid public key: "+ks);
835 // Case 2: hex public key
838 CPubKey vchPubKey(ParseHex(ks));
839 if (!vchPubKey.IsValid() || !pubkeys[i].SetPubKey(vchPubKey))
840 throw runtime_error(" Invalid public key: "+ks);
844 throw runtime_error(" Invalid public key: "+ks);
848 // Construct using pay-to-script-hash:
850 inner.SetMultisig(nRequired, pubkeys);
851 CScriptID innerID = inner.GetID();
852 pwalletMain->AddCScript(inner);
854 pwalletMain->SetAddressBookName(innerID, strAccount);
855 return CBitcoinAddress(innerID).ToString();
858 Value addredeemscript(const Array& params, bool fHelp)
860 if (fHelp || params.size() < 1 || params.size() > 2)
862 string msg = "addredeemscript <redeemScript> [account]\n"
863 "Add a P2SH address with a specified redeemScript to the wallet.\n"
864 "If [account] is specified, assign address to [account].";
865 throw runtime_error(msg);
869 if (params.size() > 1)
870 strAccount = AccountFromValue(params[1]);
872 // Construct using pay-to-script-hash:
873 vector<unsigned char> innerData = ParseHexV(params[0], "redeemScript");
874 CScript inner(innerData.begin(), innerData.end());
875 CScriptID innerID = inner.GetID();
876 pwalletMain->AddCScript(inner);
878 pwalletMain->SetAddressBookName(innerID, strAccount);
879 return CBitcoinAddress(innerID).ToString();
889 nConf = std::numeric_limits<int>::max();
893 Value ListReceived(const Array& params, bool fByAccounts)
895 // Minimum confirmations
897 if (params.size() > 0)
898 nMinDepth = params[0].get_int();
900 // Whether to include empty accounts
901 bool fIncludeEmpty = false;
902 if (params.size() > 1)
903 fIncludeEmpty = params[1].get_bool();
906 map<CBitcoinAddress, tallyitem> mapTally;
907 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
909 const CWalletTx& wtx = (*it).second;
911 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
914 int nDepth = wtx.GetDepthInMainChain();
915 if (nDepth < nMinDepth)
918 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
920 CTxDestination address;
921 if (!ExtractDestination(txout.scriptPubKey, address) || !IsMine(*pwalletMain, address))
924 tallyitem& item = mapTally[address];
925 item.nAmount += txout.nValue;
926 item.nConf = min(item.nConf, nDepth);
932 map<string, tallyitem> mapAccountTally;
933 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
935 const CBitcoinAddress& address = item.first;
936 const string& strAccount = item.second;
937 map<CBitcoinAddress, tallyitem>::iterator it = mapTally.find(address);
938 if (it == mapTally.end() && !fIncludeEmpty)
942 int nConf = std::numeric_limits<int>::max();
943 if (it != mapTally.end())
945 nAmount = (*it).second.nAmount;
946 nConf = (*it).second.nConf;
951 tallyitem& item = mapAccountTally[strAccount];
952 item.nAmount += nAmount;
953 item.nConf = min(item.nConf, nConf);
958 obj.push_back(Pair("address", address.ToString()));
959 obj.push_back(Pair("account", strAccount));
960 obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
961 obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf)));
968 for (map<string, tallyitem>::iterator it = mapAccountTally.begin(); it != mapAccountTally.end(); ++it)
970 int64 nAmount = (*it).second.nAmount;
971 int nConf = (*it).second.nConf;
973 obj.push_back(Pair("account", (*it).first));
974 obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
975 obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf)));
983 Value listreceivedbyaddress(const Array& params, bool fHelp)
985 if (fHelp || params.size() > 2)
987 "listreceivedbyaddress [minconf=1] [includeempty=false]\n"
988 "[minconf] is the minimum number of confirmations before payments are included.\n"
989 "[includeempty] whether to include addresses that haven't received any payments.\n"
990 "Returns an array of objects containing:\n"
991 " \"address\" : receiving address\n"
992 " \"account\" : the account of the receiving address\n"
993 " \"amount\" : total amount received by the address\n"
994 " \"confirmations\" : number of confirmations of the most recent transaction included");
996 return ListReceived(params, false);
999 Value listreceivedbyaccount(const Array& params, bool fHelp)
1001 if (fHelp || params.size() > 2)
1002 throw runtime_error(
1003 "listreceivedbyaccount [minconf=1] [includeempty=false]\n"
1004 "[minconf] is the minimum number of confirmations before payments are included.\n"
1005 "[includeempty] whether to include accounts that haven't received any payments.\n"
1006 "Returns an array of objects containing:\n"
1007 " \"account\" : the account of the receiving addresses\n"
1008 " \"amount\" : total amount received by addresses with this account\n"
1009 " \"confirmations\" : number of confirmations of the most recent transaction included");
1011 return ListReceived(params, true);
1014 static void MaybePushAddress(Object & entry, const CTxDestination &dest)
1016 CBitcoinAddress addr;
1018 entry.push_back(Pair("address", addr.ToString()));
1021 void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret, const isminefilter& filter)
1023 int64 nGeneratedImmature, nGeneratedMature, nFee;
1024 string strSentAccount;
1025 list<pair<CTxDestination, int64> > listReceived;
1026 list<pair<CTxDestination, int64> > listSent;
1028 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount, filter);
1030 bool fAllAccounts = (strAccount == string("*"));
1031 bool involvesWatchonly = wtx.IsFromMe(MINE_WATCH_ONLY);
1033 // Generated blocks assigned to account ""
1034 if ((nGeneratedMature+nGeneratedImmature) != 0 && (fAllAccounts || strAccount == ""))
1037 entry.push_back(Pair("account", string("")));
1038 if (nGeneratedImmature)
1040 entry.push_back(Pair("category", wtx.GetDepthInMainChain() ? "immature" : "orphan"));
1041 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedImmature)));
1045 entry.push_back(Pair("category", "generate"));
1046 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedMature)));
1049 WalletTxToJSON(wtx, entry);
1050 ret.push_back(entry);
1054 if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount))
1056 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& s, listSent)
1059 entry.push_back(Pair("account", strSentAccount));
1060 if(involvesWatchonly || (::IsMine(*pwalletMain, s.first) & MINE_WATCH_ONLY))
1061 entry.push_back(Pair("involvesWatchonly", true));
1062 MaybePushAddress(entry, s.first);
1064 if (wtx.GetDepthInMainChain() < 0) {
1065 entry.push_back(Pair("category", "conflicted"));
1067 entry.push_back(Pair("category", "send"));
1070 entry.push_back(Pair("amount", ValueFromAmount(-s.second)));
1071 entry.push_back(Pair("fee", ValueFromAmount(-nFee)));
1073 WalletTxToJSON(wtx, entry);
1074 ret.push_back(entry);
1079 if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth)
1081 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& r, listReceived)
1084 if (pwalletMain->mapAddressBook.count(r.first))
1085 account = pwalletMain->mapAddressBook[r.first];
1086 if (fAllAccounts || (account == strAccount))
1089 entry.push_back(Pair("account", account));
1090 if(involvesWatchonly || (::IsMine(*pwalletMain, r.first) & MINE_WATCH_ONLY))
1091 entry.push_back(Pair("involvesWatchonly", true));
1092 MaybePushAddress(entry, r.first);
1093 if (wtx.IsCoinBase())
1095 if (wtx.GetDepthInMainChain() < 1)
1096 entry.push_back(Pair("category", "orphan"));
1097 else if (wtx.GetBlocksToMaturity() > 0)
1098 entry.push_back(Pair("category", "immature"));
1100 entry.push_back(Pair("category", "generate"));
1103 entry.push_back(Pair("category", "receive"));
1104 entry.push_back(Pair("amount", ValueFromAmount(r.second)));
1106 WalletTxToJSON(wtx, entry);
1107 ret.push_back(entry);
1113 void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Array& ret)
1115 bool fAllAccounts = (strAccount == string("*"));
1117 if (fAllAccounts || acentry.strAccount == strAccount)
1120 entry.push_back(Pair("account", acentry.strAccount));
1121 entry.push_back(Pair("category", "move"));
1122 entry.push_back(Pair("time", (boost::int64_t)acentry.nTime));
1123 entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit)));
1124 entry.push_back(Pair("otheraccount", acentry.strOtherAccount));
1125 entry.push_back(Pair("comment", acentry.strComment));
1126 ret.push_back(entry);
1130 Value listtransactions(const Array& params, bool fHelp)
1132 if (fHelp || params.size() > 3)
1133 throw runtime_error(
1134 "listtransactions [account] [count=10] [from=0]\n"
1135 "Returns up to [count] most recent transactions skipping the first [from] transactions for account [account].");
1137 string strAccount = "*";
1138 if (params.size() > 0)
1139 strAccount = params[0].get_str();
1141 if (params.size() > 1)
1142 nCount = params[1].get_int();
1144 if (params.size() > 2)
1145 nFrom = params[2].get_int();
1147 isminefilter filter = MINE_SPENDABLE;
1148 if(params.size() > 3)
1149 if(params[3].get_bool())
1150 filter = filter | MINE_WATCH_ONLY;
1153 throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count");
1155 throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative from");
1159 std::list<CAccountingEntry> acentries;
1160 CWallet::TxItems txOrdered = pwalletMain->OrderedTxItems(acentries, strAccount);
1162 // iterate backwards until we have nCount items to return:
1163 for (CWallet::TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
1165 CWalletTx *const pwtx = (*it).second.first;
1167 ListTransactions(*pwtx, strAccount, 0, true, ret, filter);
1168 CAccountingEntry *const pacentry = (*it).second.second;
1170 AcentryToJSON(*pacentry, strAccount, ret);
1172 if ((int)ret.size() >= (nCount+nFrom)) break;
1174 // ret is newest to oldest
1176 if (nFrom > (int)ret.size())
1178 if ((nFrom + nCount) > (int)ret.size())
1179 nCount = ret.size() - nFrom;
1180 Array::iterator first = ret.begin();
1181 std::advance(first, nFrom);
1182 Array::iterator last = ret.begin();
1183 std::advance(last, nFrom+nCount);
1185 if (last != ret.end()) ret.erase(last, ret.end());
1186 if (first != ret.begin()) ret.erase(ret.begin(), first);
1188 std::reverse(ret.begin(), ret.end()); // Return oldest to newest
1193 Value listaccounts(const Array& params, bool fHelp)
1195 if (fHelp || params.size() > 1)
1196 throw runtime_error(
1197 "listaccounts [minconf=1]\n"
1198 "Returns Object that has account names as keys, account balances as values.");
1201 if (params.size() > 0)
1202 nMinDepth = params[0].get_int();
1204 isminefilter includeWatchonly = MINE_SPENDABLE;
1205 if(params.size() > 1)
1206 if(params[1].get_bool())
1207 includeWatchonly = includeWatchonly | MINE_WATCH_ONLY;
1210 map<string, int64> mapAccountBalances;
1211 BOOST_FOREACH(const PAIRTYPE(CTxDestination, string)& entry, pwalletMain->mapAddressBook) {
1212 if (IsMine(*pwalletMain, entry.first)) // This address belongs to me
1213 mapAccountBalances[entry.second] = 0;
1216 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
1218 const CWalletTx& wtx = (*it).second;
1219 int64 nGeneratedImmature, nGeneratedMature, nFee;
1220 string strSentAccount;
1221 list<pair<CTxDestination, int64> > listReceived;
1222 list<pair<CTxDestination, int64> > listSent;
1223 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount, includeWatchonly);
1224 mapAccountBalances[strSentAccount] -= nFee;
1225 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& s, listSent)
1226 mapAccountBalances[strSentAccount] -= s.second;
1227 if (wtx.GetDepthInMainChain() >= nMinDepth)
1229 mapAccountBalances[""] += nGeneratedMature;
1230 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& r, listReceived)
1231 if (pwalletMain->mapAddressBook.count(r.first))
1232 mapAccountBalances[pwalletMain->mapAddressBook[r.first]] += r.second;
1234 mapAccountBalances[""] += r.second;
1238 list<CAccountingEntry> acentries;
1239 CWalletDB(pwalletMain->strWalletFile).ListAccountCreditDebit("*", acentries);
1240 BOOST_FOREACH(const CAccountingEntry& entry, acentries)
1241 mapAccountBalances[entry.strAccount] += entry.nCreditDebit;
1244 BOOST_FOREACH(const PAIRTYPE(string, int64)& accountBalance, mapAccountBalances) {
1245 ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second)));
1250 Value listsinceblock(const Array& params, bool fHelp)
1253 throw runtime_error(
1254 "listsinceblock [blockhash] [target-confirmations]\n"
1255 "Get all transactions in blocks since block [blockhash], or all transactions if omitted");
1257 CBlockIndex *pindex = NULL;
1258 int target_confirms = 1;
1259 isminefilter filter = MINE_SPENDABLE;
1261 if (params.size() > 0)
1263 uint256 blockId = 0;
1265 blockId.SetHex(params[0].get_str());
1266 pindex = CBlockLocator(blockId).GetBlockIndex();
1269 if (params.size() > 1)
1271 target_confirms = params[1].get_int();
1273 if (target_confirms < 1)
1274 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter");
1277 if(params.size() > 2)
1278 if(params[2].get_bool())
1279 filter = filter | MINE_WATCH_ONLY;
1281 int depth = pindex ? (1 + nBestHeight - pindex->nHeight) : -1;
1285 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); it++)
1287 CWalletTx tx = (*it).second;
1289 if (depth == -1 || tx.GetDepthInMainChain() < depth)
1290 ListTransactions(tx, "*", 0, true, transactions, filter);
1295 if (target_confirms == 1)
1297 lastblock = hashBestChain;
1301 int target_height = pindexBest->nHeight + 1 - target_confirms;
1304 for (block = pindexBest;
1305 block && block->nHeight > target_height;
1306 block = block->pprev) { }
1308 lastblock = block ? block->GetBlockHash() : 0;
1312 ret.push_back(Pair("transactions", transactions));
1313 ret.push_back(Pair("lastblock", lastblock.GetHex()));
1318 Value gettransaction(const Array& params, bool fHelp)
1320 if (fHelp || params.size() != 1)
1321 throw runtime_error(
1322 "gettransaction <txid>\n"
1323 "Get detailed information about <txid>");
1326 hash.SetHex(params[0].get_str());
1328 isminefilter filter = MINE_SPENDABLE;
1329 if(params.size() > 1)
1330 if(params[1].get_bool())
1331 filter = filter | MINE_WATCH_ONLY;
1335 if (pwalletMain->mapWallet.count(hash))
1337 const CWalletTx& wtx = pwalletMain->mapWallet[hash];
1339 TxToJSON(wtx, 0, entry);
1341 int64 nCredit = wtx.GetCredit(filter);
1342 int64 nDebit = wtx.GetDebit(filter);
1343 int64 nNet = nCredit - nDebit;
1344 int64 nFee = (wtx.IsFromMe(filter) ? wtx.GetValueOut() - nDebit : 0);
1346 entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee)));
1347 if (wtx.IsFromMe(filter))
1348 entry.push_back(Pair("fee", ValueFromAmount(nFee)));
1350 WalletTxToJSON(wtx, entry);
1353 ListTransactions(pwalletMain->mapWallet[hash], "*", 0, false, details, filter);
1354 entry.push_back(Pair("details", details));
1359 uint256 hashBlock = 0;
1360 if (GetTransaction(hash, tx, hashBlock))
1362 TxToJSON(tx, 0, entry);
1364 entry.push_back(Pair("confirmations", 0));
1367 entry.push_back(Pair("blockhash", hashBlock.GetHex()));
1368 map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashBlock);
1369 if (mi != mapBlockIndex.end() && (*mi).second)
1371 CBlockIndex* pindex = (*mi).second;
1372 if (pindex->IsInMainChain())
1373 entry.push_back(Pair("confirmations", 1 + nBestHeight - pindex->nHeight));
1375 entry.push_back(Pair("confirmations", 0));
1380 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction");
1387 Value backupwallet(const Array& params, bool fHelp)
1389 if (fHelp || params.size() != 1)
1390 throw runtime_error(
1391 "backupwallet <destination>\n"
1392 "Safely copies wallet.dat to destination, which can be a directory or a path with filename.");
1394 string strDest = params[0].get_str();
1395 if (!BackupWallet(*pwalletMain, strDest))
1396 throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet backup failed!");
1402 Value keypoolrefill(const Array& params, bool fHelp)
1404 if (fHelp || params.size() > 1)
1405 throw runtime_error(
1406 "keypoolrefill [new-size]\n"
1407 "Fills the keypool."
1408 + HelpRequiringPassphrase());
1410 unsigned int nSize = max(GetArg("-keypool", 100), 0LL);
1411 if (params.size() > 0) {
1412 if (params[0].get_int() < 0)
1413 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected valid size");
1414 nSize = (unsigned int) params[0].get_int();
1417 EnsureWalletIsUnlocked();
1419 pwalletMain->TopUpKeyPool(nSize);
1421 if (pwalletMain->GetKeyPoolSize() < nSize)
1422 throw JSONRPCError(RPC_WALLET_ERROR, "Error refreshing keypool.");
1428 void ThreadTopUpKeyPool(void* parg)
1430 // Make this thread recognisable as the key-topping-up thread
1431 RenameThread("novacoin-key-top");
1433 pwalletMain->TopUpKeyPool();
1436 void ThreadCleanWalletPassphrase(void* parg)
1438 // Make this thread recognisable as the wallet relocking thread
1439 RenameThread("novacoin-lock-wa");
1441 int64 nMyWakeTime = GetTimeMillis() + *((int64*)parg) * 1000;
1443 ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime);
1445 if (nWalletUnlockTime == 0)
1447 nWalletUnlockTime = nMyWakeTime;
1451 if (nWalletUnlockTime==0)
1453 int64 nToSleep = nWalletUnlockTime - GetTimeMillis();
1457 LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime);
1459 ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime);
1463 if (nWalletUnlockTime)
1465 nWalletUnlockTime = 0;
1466 pwalletMain->Lock();
1471 if (nWalletUnlockTime < nMyWakeTime)
1472 nWalletUnlockTime = nMyWakeTime;
1475 LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime);
1477 delete (int64*)parg;
1480 Value walletpassphrase(const Array& params, bool fHelp)
1482 if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 3))
1483 throw runtime_error(
1484 "walletpassphrase <passphrase> <timeout> [mintonly]\n"
1485 "Stores the wallet decryption key in memory for <timeout> seconds.\n"
1486 "mintonly is optional true/false allowing only block minting.");
1489 if (!pwalletMain->IsCrypted())
1490 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called.");
1492 if (!pwalletMain->IsLocked())
1493 throw JSONRPCError(RPC_WALLET_ALREADY_UNLOCKED, "Error: Wallet is already unlocked, use walletlock first if need to change unlock settings.");
1494 // Note that the walletpassphrase is stored in params[0] which is not mlock()ed
1495 SecureString strWalletPass;
1496 strWalletPass.reserve(100);
1497 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1498 // Alternately, find a way to make params[0] mlock()'d to begin with.
1499 strWalletPass = params[0].get_str().c_str();
1501 if (strWalletPass.length() > 0)
1503 if (!pwalletMain->Unlock(strWalletPass))
1504 throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
1507 throw runtime_error(
1508 "walletpassphrase <passphrase> <timeout>\n"
1509 "Stores the wallet decryption key in memory for <timeout> seconds.");
1511 NewThread(ThreadTopUpKeyPool, NULL);
1512 int64* pnSleepTime = new int64(params[1].get_int64());
1513 NewThread(ThreadCleanWalletPassphrase, pnSleepTime);
1515 // ppcoin: if user OS account compromised prevent trivial sendmoney commands
1516 if (params.size() > 2)
1517 fWalletUnlockMintOnly = params[2].get_bool();
1519 fWalletUnlockMintOnly = false;
1525 Value walletpassphrasechange(const Array& params, bool fHelp)
1527 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2))
1528 throw runtime_error(
1529 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1530 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1533 if (!pwalletMain->IsCrypted())
1534 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrasechange was called.");
1536 // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string)
1537 // Alternately, find a way to make params[0] mlock()'d to begin with.
1538 SecureString strOldWalletPass;
1539 strOldWalletPass.reserve(100);
1540 strOldWalletPass = params[0].get_str().c_str();
1542 SecureString strNewWalletPass;
1543 strNewWalletPass.reserve(100);
1544 strNewWalletPass = params[1].get_str().c_str();
1546 if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1)
1547 throw runtime_error(
1548 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1549 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1551 if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass))
1552 throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
1558 Value walletlock(const Array& params, bool fHelp)
1560 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 0))
1561 throw runtime_error(
1563 "Removes the wallet encryption key from memory, locking the wallet.\n"
1564 "After calling this method, you will need to call walletpassphrase again\n"
1565 "before being able to call any methods which require the wallet to be unlocked.");
1568 if (!pwalletMain->IsCrypted())
1569 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletlock was called.");
1572 LOCK(cs_nWalletUnlockTime);
1573 pwalletMain->Lock();
1574 nWalletUnlockTime = 0;
1581 Value encryptwallet(const Array& params, bool fHelp)
1583 if (!pwalletMain->IsCrypted() && (fHelp || params.size() != 1))
1584 throw runtime_error(
1585 "encryptwallet <passphrase>\n"
1586 "Encrypts the wallet with <passphrase>.");
1589 if (pwalletMain->IsCrypted())
1590 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an encrypted wallet, but encryptwallet was called.");
1592 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1593 // Alternately, find a way to make params[0] mlock()'d to begin with.
1594 SecureString strWalletPass;
1595 strWalletPass.reserve(100);
1596 strWalletPass = params[0].get_str().c_str();
1598 if (strWalletPass.length() < 1)
1599 throw runtime_error(
1600 "encryptwallet <passphrase>\n"
1601 "Encrypts the wallet with <passphrase>.");
1603 if (!pwalletMain->EncryptWallet(strWalletPass))
1604 throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: Failed to encrypt the wallet.");
1606 // BDB seems to have a bad habit of writing old data into
1607 // slack space in .dat files; that is bad if the old data is
1608 // unencrypted private keys. So:
1610 return "wallet encrypted; NovaCoin server stopping, restart to run with encrypted wallet. The keypool has been flushed, you need to make a new backup.";
1613 class DescribeAddressVisitor : public boost::static_visitor<Object>
1618 DescribeAddressVisitor(isminetype mineIn) : mine(mineIn) {}
1620 Object operator()(const CNoDestination &dest) const { return Object(); }
1621 Object operator()(const CKeyID &keyID) const {
1624 pwalletMain->GetPubKey(keyID, vchPubKey);
1625 obj.push_back(Pair("isscript", false));
1626 if (mine == MINE_SPENDABLE) {
1627 pwalletMain->GetPubKey(keyID, vchPubKey);
1628 obj.push_back(Pair("pubkey", HexStr(vchPubKey.Raw())));
1629 obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed()));
1634 Object operator()(const CScriptID &scriptID) const {
1636 obj.push_back(Pair("isscript", true));
1637 if (mine == MINE_SPENDABLE) {
1639 pwalletMain->GetCScript(scriptID, subscript);
1640 std::vector<CTxDestination> addresses;
1641 txnouttype whichType;
1643 ExtractDestinations(subscript, whichType, addresses, nRequired);
1644 obj.push_back(Pair("script", GetTxnOutputType(whichType)));
1645 obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end())));
1647 BOOST_FOREACH(const CTxDestination& addr, addresses)
1648 a.push_back(CBitcoinAddress(addr).ToString());
1649 obj.push_back(Pair("addresses", a));
1650 if (whichType == TX_MULTISIG)
1651 obj.push_back(Pair("sigsrequired", nRequired));
1657 Value validateaddress(const Array& params, bool fHelp)
1659 if (fHelp || params.size() != 1)
1660 throw runtime_error(
1661 "validateaddress <novacoinaddress>\n"
1662 "Return information about <novacoinaddress>.");
1664 CBitcoinAddress address(params[0].get_str());
1665 bool isValid = address.IsValid();
1668 ret.push_back(Pair("isvalid", isValid));
1671 CTxDestination dest = address.Get();
1672 string currentAddress = address.ToString();
1673 ret.push_back(Pair("address", currentAddress));
1674 isminetype mine = pwalletMain ? IsMine(*pwalletMain, dest) : MINE_NO;
1675 ret.push_back(Pair("ismine", mine != MINE_NO));
1676 if (mine != MINE_NO) {
1677 ret.push_back(Pair("watchonly", mine == MINE_WATCH_ONLY));
1678 Object detail = boost::apply_visitor(DescribeAddressVisitor(mine), dest);
1679 ret.insert(ret.end(), detail.begin(), detail.end());
1681 if (pwalletMain->mapAddressBook.count(dest))
1682 ret.push_back(Pair("account", pwalletMain->mapAddressBook[dest]));
1687 // ppcoin: reserve balance from being staked for network protection
1688 Value reservebalance(const Array& params, bool fHelp)
1690 if (fHelp || params.size() > 2)
1691 throw runtime_error(
1692 "reservebalance [<reserve> [amount]]\n"
1693 "<reserve> is true or false to turn balance reserve on or off.\n"
1694 "<amount> is a real and rounded to cent.\n"
1695 "Set reserve amount not participating in network protection.\n"
1696 "If no parameters provided current setting is printed.\n");
1698 if (params.size() > 0)
1700 bool fReserve = params[0].get_bool();
1703 if (params.size() == 1)
1704 throw runtime_error("must provide amount to reserve balance.\n");
1705 int64 nAmount = AmountFromValue(params[1]);
1706 nAmount = (nAmount / CENT) * CENT; // round to cent
1708 throw runtime_error("amount cannot be negative.\n");
1709 mapArgs["-reservebalance"] = FormatMoney(nAmount).c_str();
1713 if (params.size() > 1)
1714 throw runtime_error("cannot specify amount to turn off reserve.\n");
1715 mapArgs["-reservebalance"] = "0";
1720 int64 nReserveBalance = 0;
1721 if (mapArgs.count("-reservebalance") && !ParseMoney(mapArgs["-reservebalance"], nReserveBalance))
1722 throw runtime_error("invalid reserve balance amount\n");
1723 result.push_back(Pair("reserve", (nReserveBalance > 0)));
1724 result.push_back(Pair("amount", ValueFromAmount(nReserveBalance)));
1729 // ppcoin: check wallet integrity
1730 Value checkwallet(const Array& params, bool fHelp)
1732 if (fHelp || params.size() > 0)
1733 throw runtime_error(
1735 "Check wallet for integrity.\n");
1738 int64 nBalanceInQuestion;
1739 pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion, true);
1741 if (nMismatchSpent == 0)
1742 result.push_back(Pair("wallet check passed", true));
1745 result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1746 result.push_back(Pair("amount in question", ValueFromAmount(nBalanceInQuestion)));
1752 // ppcoin: repair wallet
1753 Value repairwallet(const Array& params, bool fHelp)
1755 if (fHelp || params.size() > 0)
1756 throw runtime_error(
1758 "Repair wallet if checkwallet reports any problem.\n");
1761 int64 nBalanceInQuestion;
1762 pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion);
1764 if (nMismatchSpent == 0)
1765 result.push_back(Pair("wallet check passed", true));
1768 result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1769 result.push_back(Pair("amount affected by repair", ValueFromAmount(nBalanceInQuestion)));
1774 // NovaCoin: resend unconfirmed wallet transactions
1775 Value resendtx(const Array& params, bool fHelp)
1777 if (fHelp || params.size() > 1)
1778 throw runtime_error(
1780 "Re-send unconfirmed transactions.\n"
1783 ResendWalletTransactions();
1788 // ppcoin: make a public-private key pair
1789 Value makekeypair(const Array& params, bool fHelp)
1791 if (fHelp || params.size() > 1)
1792 throw runtime_error(
1793 "makekeypair [prefix]\n"
1794 "Make a public/private key pair.\n"
1795 "[prefix] is optional preferred prefix for the public key.\n");
1797 string strPrefix = "";
1798 if (params.size() > 0)
1799 strPrefix = params[0].get_str();
1802 key.MakeNewKey(false);
1804 CPrivKey vchPrivKey = key.GetPrivKey();
1806 result.push_back(Pair("PrivateKey", HexStr<CPrivKey::iterator>(vchPrivKey.begin(), vchPrivKey.end())));
1807 result.push_back(Pair("PublicKey", HexStr(key.GetPubKey().Raw())));