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_t 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 ? "\n\nRequires wallet passphrase to be set with walletpassphrase first"
27 static void accountingAllowed()
29 if (!GetBoolArg("-accounts", false))
31 "Accounting API is deprecated and its removal is planned in the future.\n"
32 "It can easily result in negative or odd balances if misused or misunderstood.\n"
33 "If you still want to enable it then add accounts=1 to your options.\n");
36 void EnsureWalletIsUnlocked()
38 if (pwalletMain->IsLocked())
39 throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first.");
40 if (fWalletUnlockMintOnly)
41 throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Wallet unlocked for block minting only.");
44 void WalletTxToJSON(const CWalletTx& wtx, Object& entry)
46 int confirms = wtx.GetDepthInMainChain();
47 entry.push_back(Pair("confirmations", confirms));
48 if (wtx.IsCoinBase() || wtx.IsCoinStake())
49 entry.push_back(Pair("generated", true));
52 entry.push_back(Pair("blockhash", wtx.hashBlock.GetHex()));
53 entry.push_back(Pair("blockindex", wtx.nIndex));
54 entry.push_back(Pair("blocktime", (boost::int64_t)(mapBlockIndex[wtx.hashBlock]->nTime)));
56 entry.push_back(Pair("txid", wtx.GetHash().GetHex()));
57 entry.push_back(Pair("time", (boost::int64_t)wtx.GetTxTime()));
58 entry.push_back(Pair("timereceived", (boost::int64_t)wtx.nTimeReceived));
59 BOOST_FOREACH(const PAIRTYPE(string,string)& item, wtx.mapValue)
60 entry.push_back(Pair(item.first, item.second));
63 string AccountFromValue(const Value& value)
65 string strAccount = value.get_str();
66 if (strAccount == "*")
67 throw JSONRPCError(RPC_WALLET_INVALID_ACCOUNT_NAME, "Invalid account name");
71 Value getinfo(const Array& params, bool fHelp)
73 if (fHelp || params.size() != 0)
76 "Returns an object containing various state info.");
79 GetProxy(NET_IPV4, proxy);
82 obj.push_back(Pair("version", FormatFullVersion()));
83 obj.push_back(Pair("protocolversion",(int)PROTOCOL_VERSION));
84 obj.push_back(Pair("walletversion", pwalletMain->GetVersion()));
85 obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance())));
86 obj.push_back(Pair("unspendable", ValueFromAmount(pwalletMain->GetWatchOnlyBalance())));
87 obj.push_back(Pair("newmint", ValueFromAmount(pwalletMain->GetNewMint())));
88 obj.push_back(Pair("stake", ValueFromAmount(pwalletMain->GetStake())));
89 obj.push_back(Pair("blocks", (int)nBestHeight));
90 obj.push_back(Pair("timeoffset", (boost::int64_t)GetTimeOffset()));
91 obj.push_back(Pair("moneysupply", ValueFromAmount(pindexBest->nMoneySupply)));
92 obj.push_back(Pair("connections", (int)vNodes.size()));
93 obj.push_back(Pair("proxy", (proxy.first.IsValid() ? proxy.first.ToStringIPPort() : string())));
94 obj.push_back(Pair("ip", addrSeenByPeer.ToStringIP()));
96 diff.push_back(Pair("proof-of-work", GetDifficulty()));
97 diff.push_back(Pair("proof-of-stake", GetDifficulty(GetLastBlockIndex(pindexBest, true))));
98 obj.push_back(Pair("difficulty", diff));
100 obj.push_back(Pair("testnet", fTestNet));
101 obj.push_back(Pair("keypoololdest", (boost::int64_t)pwalletMain->GetOldestKeyPoolTime()));
102 obj.push_back(Pair("keypoolsize", (int)pwalletMain->GetKeyPoolSize()));
103 obj.push_back(Pair("paytxfee", ValueFromAmount(nTransactionFee)));
104 obj.push_back(Pair("mininput", ValueFromAmount(nMinimumInputValue)));
105 if (pwalletMain->IsCrypted())
106 obj.push_back(Pair("unlocked_until", (boost::int64_t)nWalletUnlockTime / 1000));
107 obj.push_back(Pair("errors", GetWarnings("statusbar")));
111 Value getnewaddress(const Array& params, bool fHelp)
113 if (fHelp || params.size() > 1)
115 "getnewaddress [account]\n"
116 "Returns a new NovaCoin address for receiving payments. "
117 "If [account] is specified (recommended), it is added to the address book "
118 "so payments received with the address will be credited to [account].");
120 // Parse the account first so we don't generate a key if there's an error
122 if (params.size() > 0)
123 strAccount = AccountFromValue(params[0]);
125 if (!pwalletMain->IsLocked())
126 pwalletMain->TopUpKeyPool();
128 // Generate a new key that is added to wallet
130 if (!pwalletMain->GetKeyFromPool(newKey, false))
131 throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
132 CKeyID keyID = newKey.GetID();
134 pwalletMain->SetAddressBookName(keyID, strAccount);
136 return CBitcoinAddress(keyID).ToString();
140 CBitcoinAddress GetAccountAddress(string strAccount, bool bForceNew=false)
142 CWalletDB walletdb(pwalletMain->strWalletFile);
145 walletdb.ReadAccount(strAccount, account);
147 bool bKeyUsed = false;
149 // Check if the current key has been used
150 if (account.vchPubKey.IsValid())
152 CScript scriptPubKey;
153 scriptPubKey.SetDestination(account.vchPubKey.GetID());
154 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin();
155 it != pwalletMain->mapWallet.end() && account.vchPubKey.IsValid();
158 const CWalletTx& wtx = (*it).second;
159 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
160 if (txout.scriptPubKey == scriptPubKey)
165 // Generate a new key
166 if (!account.vchPubKey.IsValid() || bForceNew || bKeyUsed)
168 if (!pwalletMain->GetKeyFromPool(account.vchPubKey, false))
169 throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
171 pwalletMain->SetAddressBookName(account.vchPubKey.GetID(), strAccount);
172 walletdb.WriteAccount(strAccount, account);
175 return CBitcoinAddress(account.vchPubKey.GetID());
178 Value getaccountaddress(const Array& params, bool fHelp)
180 if (fHelp || params.size() != 1)
182 "getaccountaddress <account>\n"
183 "Returns the current NovaCoin address for receiving payments to this account.");
188 // Parse the account first so we don't generate a key if there's an error
189 string strAccount = AccountFromValue(params[0]);
193 ret = GetAccountAddress(strAccount).ToString();
200 Value setaccount(const Array& params, bool fHelp)
202 if (fHelp || params.size() < 1 || params.size() > 2)
204 "setaccount <novacoinaddress> <account>\n"
205 "Sets the account associated with the given address.");
207 CBitcoinAddress address(params[0].get_str());
208 if (!address.IsValid())
209 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
213 if (params.size() > 1)
214 strAccount = AccountFromValue(params[1]);
216 // Detect when changing the account of an address that is the 'unused current key' of another account:
217 if (pwalletMain->mapAddressBook.count(address.Get()))
219 string strOldAccount = pwalletMain->mapAddressBook[address.Get()];
220 if (address == GetAccountAddress(strOldAccount))
221 GetAccountAddress(strOldAccount, true);
224 pwalletMain->SetAddressBookName(address.Get(), strAccount);
230 Value getaccount(const Array& params, bool fHelp)
232 if (fHelp || params.size() != 1)
234 "getaccount <novacoinaddress>\n"
235 "Returns the account associated with the given address.");
237 CBitcoinAddress address(params[0].get_str());
238 if (!address.IsValid())
239 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
242 map<CTxDestination, string>::iterator mi = pwalletMain->mapAddressBook.find(address.Get());
243 if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.empty())
244 strAccount = (*mi).second;
249 Value getaddressesbyaccount(const Array& params, bool fHelp)
251 if (fHelp || params.size() != 1)
253 "getaddressesbyaccount <account>\n"
254 "Returns the list of addresses for the given account.");
256 string strAccount = AccountFromValue(params[0]);
258 // Find all addresses that have the given account
260 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
262 const CBitcoinAddress& address = item.first;
263 const string& strName = item.second;
264 if (strName == strAccount)
265 ret.push_back(address.ToString());
270 Value mergecoins(const Array& params, bool fHelp)
272 if (fHelp || params.size() != 3)
274 "mergecoins <amount> <minvalue> <outputvalue>\n"
275 "<amount> is resulting inputs sum\n"
276 "<minvalue> is minimum value of inputs which are used in join process\n"
277 "<outputvalue> is resulting value of inputs which will be created\n"
278 "All values are real and and rounded to the nearest " + FormatMoney(MIN_TXOUT_AMOUNT)
279 + HelpRequiringPassphrase());
281 if (pwalletMain->IsLocked())
282 throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first.");
285 int64_t nAmount = AmountFromValue(params[0]);
288 int64_t nMinValue = AmountFromValue(params[1]);
291 int64_t nOutputValue = AmountFromValue(params[2]);
293 if (nAmount < MIN_TXOUT_AMOUNT)
294 throw JSONRPCError(-101, "Send amount too small");
296 if (nMinValue < MIN_TXOUT_AMOUNT)
297 throw JSONRPCError(-101, "Max value too small");
299 if (nOutputValue < MIN_TXOUT_AMOUNT)
300 throw JSONRPCError(-101, "Output value too small");
302 if (nOutputValue < nMinValue)
303 throw JSONRPCError(-101, "Output value is lower than min value");
305 list<uint256> listMerged;
306 if (!pwalletMain->MergeCoins(nAmount, nMinValue, nOutputValue, listMerged))
310 BOOST_FOREACH(const uint256 txHash, listMerged)
311 mergedHashes.push_back(txHash.GetHex());
316 Value sendtoaddress(const Array& params, bool fHelp)
318 if (fHelp || params.size() < 2 || params.size() > 4)
320 "sendtoaddress <novacoinaddress> <amount> [comment] [comment-to]\n"
321 "<amount> is a real and is rounded to the nearest " + FormatMoney(MIN_TXOUT_AMOUNT)
322 + HelpRequiringPassphrase());
324 CBitcoinAddress address(params[0].get_str());
325 if (!address.IsValid())
326 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
329 int64_t nAmount = AmountFromValue(params[1]);
331 if (nAmount < MIN_TXOUT_AMOUNT)
332 throw JSONRPCError(-101, "Send amount too small");
336 if (params.size() > 2 && params[2].type() != null_type && !params[2].get_str().empty())
337 wtx.mapValue["comment"] = params[2].get_str();
338 if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
339 wtx.mapValue["to"] = params[3].get_str();
341 if (pwalletMain->IsLocked())
342 throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first.");
344 string strError = pwalletMain->SendMoneyToDestination(address.Get(), nAmount, wtx);
346 throw JSONRPCError(RPC_WALLET_ERROR, strError);
348 return wtx.GetHash().GetHex();
351 Value listaddressgroupings(const Array& params, bool fHelp)
355 "listaddressgroupings\n"
356 "Lists groups of addresses which have had their common ownership\n"
357 "made public by common use as inputs or as the resulting change\n"
358 "in past transactions");
361 map<CTxDestination, int64_t> balances = pwalletMain->GetAddressBalances();
362 BOOST_FOREACH(set<CTxDestination> grouping, pwalletMain->GetAddressGroupings())
365 BOOST_FOREACH(CTxDestination address, grouping)
368 addressInfo.push_back(CBitcoinAddress(address).ToString());
369 addressInfo.push_back(ValueFromAmount(balances[address]));
371 LOCK(pwalletMain->cs_wallet);
372 if (pwalletMain->mapAddressBook.find(CBitcoinAddress(address).Get()) != pwalletMain->mapAddressBook.end())
373 addressInfo.push_back(pwalletMain->mapAddressBook.find(CBitcoinAddress(address).Get())->second);
375 jsonGrouping.push_back(addressInfo);
377 jsonGroupings.push_back(jsonGrouping);
379 return jsonGroupings;
382 Value signmessage(const Array& params, bool fHelp)
384 if (fHelp || params.size() != 2)
386 "signmessage <novacoinaddress> <message>\n"
387 "Sign a message with the private key of an address");
389 EnsureWalletIsUnlocked();
391 string strAddress = params[0].get_str();
392 string strMessage = params[1].get_str();
394 CBitcoinAddress addr(strAddress);
396 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
399 if (!addr.GetKeyID(keyID))
400 throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
403 if (!pwalletMain->GetKey(keyID, key))
404 throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available");
406 CDataStream ss(SER_GETHASH, 0);
407 ss << strMessageMagic;
410 vector<unsigned char> vchSig;
411 if (!key.SignCompact(Hash(ss.begin(), ss.end()), vchSig))
412 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed");
414 return EncodeBase64(&vchSig[0], vchSig.size());
417 Value verifymessage(const Array& params, bool fHelp)
419 if (fHelp || params.size() != 3)
421 "verifymessage <novacoinaddress> <signature> <message>\n"
422 "Verify a signed message");
424 string strAddress = params[0].get_str();
425 string strSign = params[1].get_str();
426 string strMessage = params[2].get_str();
428 CBitcoinAddress addr(strAddress);
430 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
433 if (!addr.GetKeyID(keyID))
434 throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
436 bool fInvalid = false;
437 vector<unsigned char> vchSig = DecodeBase64(strSign.c_str(), &fInvalid);
440 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Malformed base64 encoding");
442 CDataStream ss(SER_GETHASH, 0);
443 ss << strMessageMagic;
447 if (!key.SetCompactSignature(Hash(ss.begin(), ss.end()), vchSig))
450 return (key.GetPubKey().GetID() == keyID);
454 Value getreceivedbyaddress(const Array& params, bool fHelp)
456 if (fHelp || params.size() < 1 || params.size() > 2)
458 "getreceivedbyaddress <novacoinaddress> [minconf=1]\n"
459 "Returns the total amount received by <novacoinaddress> in transactions with at least [minconf] confirmations.");
462 CBitcoinAddress address = CBitcoinAddress(params[0].get_str());
463 CScript scriptPubKey;
464 if (!address.IsValid())
465 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
466 scriptPubKey.SetDestination(address.Get());
467 if (!IsMine(*pwalletMain,scriptPubKey))
470 // Minimum confirmations
472 if (params.size() > 1)
473 nMinDepth = params[1].get_int();
477 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
479 const CWalletTx& wtx = (*it).second;
480 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
483 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
484 if (txout.scriptPubKey == scriptPubKey)
485 if (wtx.GetDepthInMainChain() >= nMinDepth)
486 nAmount += txout.nValue;
489 return ValueFromAmount(nAmount);
493 void GetAccountAddresses(string strAccount, set<CTxDestination>& setAddress)
495 BOOST_FOREACH(const PAIRTYPE(CTxDestination, string)& item, pwalletMain->mapAddressBook)
497 const CTxDestination& address = item.first;
498 const string& strName = item.second;
499 if (strName == strAccount)
500 setAddress.insert(address);
504 Value getreceivedbyaccount(const Array& params, bool fHelp)
506 if (fHelp || params.size() < 1 || params.size() > 2)
508 "getreceivedbyaccount <account> [minconf=1]\n"
509 "Returns the total amount received by addresses with <account> in transactions with at least [minconf] confirmations.");
514 // Minimum confirmations
516 if (params.size() > 1)
517 nMinDepth = params[1].get_int();
519 // Get the set of pub keys assigned to account
520 string strAccount = AccountFromValue(params[0]);
521 set<CTxDestination> setAddress;
522 GetAccountAddresses(strAccount, setAddress);
526 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
528 const CWalletTx& wtx = (*it).second;
529 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
532 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
534 CTxDestination address;
535 if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*pwalletMain, address) && setAddress.count(address))
536 if (wtx.GetDepthInMainChain() >= nMinDepth)
537 nAmount += txout.nValue;
541 return (double)nAmount / (double)COIN;
545 int64_t GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth, const isminefilter& filter)
547 int64_t nBalance = 0;
549 // Tally wallet transactions
550 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
552 const CWalletTx& wtx = (*it).second;
556 int64_t nGenerated, nReceived, nSent, nFee;
557 wtx.GetAccountAmounts(strAccount, nGenerated, nReceived, nSent, nFee, filter);
559 if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth)
560 nBalance += nReceived;
561 nBalance += nGenerated - nSent - nFee;
564 // Tally internal accounting entries
565 nBalance += walletdb.GetAccountCreditDebit(strAccount);
570 int64_t GetAccountBalance(const string& strAccount, int nMinDepth, const isminefilter& filter)
572 CWalletDB walletdb(pwalletMain->strWalletFile);
573 return GetAccountBalance(walletdb, strAccount, nMinDepth, filter);
577 Value getbalance(const Array& params, bool fHelp)
579 if (fHelp || params.size() > 2)
581 "getbalance [account] [minconf=1] [watchonly=0]\n"
582 "If [account] is not specified, returns the server's total available balance.\n"
583 "If [account] is specified, returns the balance in the account.\n"
584 "if [includeWatchonly] is specified, include balance in watchonly addresses (see 'importaddress').");
586 if (params.size() == 0)
587 return ValueFromAmount(pwalletMain->GetBalance());
590 if (params.size() > 1)
591 nMinDepth = params[1].get_int();
592 isminefilter filter = MINE_SPENDABLE;
593 if(params.size() > 2)
594 if(params[2].get_bool())
595 filter = filter | MINE_WATCH_ONLY;
597 if (params[0].get_str() == "*") {
598 // Calculate total balance a different way from GetBalance()
599 // (GetBalance() sums up all unspent TxOuts)
600 // getbalance and getbalance '*' 0 should return the same number.
601 int64_t nBalance = 0;
602 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
604 const CWalletTx& wtx = (*it).second;
605 if (!wtx.IsTrusted())
608 int64_t allGeneratedImmature, allGeneratedMature, allFee;
609 allGeneratedImmature = allGeneratedMature = allFee = 0;
611 string strSentAccount;
612 list<pair<CTxDestination, int64_t> > listReceived;
613 list<pair<CTxDestination, int64_t> > listSent;
614 wtx.GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount, filter);
615 if (wtx.GetDepthInMainChain() >= nMinDepth)
617 BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64_t)& r, listReceived)
618 nBalance += r.second;
620 BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64_t)& r, listSent)
621 nBalance -= r.second;
623 nBalance += allGeneratedMature;
625 return ValueFromAmount(nBalance);
631 string strAccount = AccountFromValue(params[0]);
633 int64_t nBalance = GetAccountBalance(strAccount, nMinDepth, filter);
635 return ValueFromAmount(nBalance);
639 Value movecmd(const Array& params, bool fHelp)
641 if (fHelp || params.size() < 3 || params.size() > 5)
643 "move <fromaccount> <toaccount> <amount> [minconf=1] [comment]\n"
644 "Move from one account in your wallet to another.");
649 string strFrom = AccountFromValue(params[0]);
650 string strTo = AccountFromValue(params[1]);
651 int64_t nAmount = AmountFromValue(params[2]);
653 if (nAmount < MIN_TXOUT_AMOUNT)
654 throw JSONRPCError(-101, "Send amount too small");
656 if (params.size() > 3)
657 // unused parameter, used to be nMinDepth, keep type-checking it though
658 (void)params[3].get_int();
660 if (params.size() > 4)
661 strComment = params[4].get_str();
663 CWalletDB walletdb(pwalletMain->strWalletFile);
664 if (!walletdb.TxnBegin())
665 throw JSONRPCError(RPC_DATABASE_ERROR, "database error");
667 int64_t nNow = GetAdjustedTime();
670 CAccountingEntry debit;
671 debit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb);
672 debit.strAccount = strFrom;
673 debit.nCreditDebit = -nAmount;
675 debit.strOtherAccount = strTo;
676 debit.strComment = strComment;
677 walletdb.WriteAccountingEntry(debit);
680 CAccountingEntry credit;
681 credit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb);
682 credit.strAccount = strTo;
683 credit.nCreditDebit = nAmount;
685 credit.strOtherAccount = strFrom;
686 credit.strComment = strComment;
687 walletdb.WriteAccountingEntry(credit);
689 if (!walletdb.TxnCommit())
690 throw JSONRPCError(RPC_DATABASE_ERROR, "database error");
696 Value sendfrom(const Array& params, bool fHelp)
698 if (fHelp || params.size() < 3 || params.size() > 6)
700 "sendfrom <fromaccount> <tonovacoinaddress> <amount> [minconf=1] [comment] [comment-to]\n"
701 "<amount> is a real and is rounded to the nearest " + FormatMoney(MIN_TXOUT_AMOUNT)
702 + HelpRequiringPassphrase());
707 string strAccount = AccountFromValue(params[0]);
708 CBitcoinAddress address(params[1].get_str());
709 if (!address.IsValid())
710 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
711 int64_t nAmount = AmountFromValue(params[2]);
713 if (nAmount < MIN_TXOUT_AMOUNT)
714 throw JSONRPCError(-101, "Send amount too small");
717 if (params.size() > 3)
718 nMinDepth = params[3].get_int();
721 wtx.strFromAccount = strAccount;
722 if (params.size() > 4 && params[4].type() != null_type && !params[4].get_str().empty())
723 wtx.mapValue["comment"] = params[4].get_str();
724 if (params.size() > 5 && params[5].type() != null_type && !params[5].get_str().empty())
725 wtx.mapValue["to"] = params[5].get_str();
727 EnsureWalletIsUnlocked();
730 int64_t nBalance = GetAccountBalance(strAccount, nMinDepth, MINE_SPENDABLE);
731 if (nAmount > nBalance)
732 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds");
735 string strError = pwalletMain->SendMoneyToDestination(address.Get(), nAmount, wtx);
737 throw JSONRPCError(RPC_WALLET_ERROR, strError);
739 return wtx.GetHash().GetHex();
743 Value sendmany(const Array& params, bool fHelp)
745 if (fHelp || params.size() < 2 || params.size() > 4)
747 "sendmany <fromaccount> {address:amount,...} [minconf=1] [comment]\n"
748 "amounts are double-precision floating point numbers"
749 + HelpRequiringPassphrase());
754 string strAccount = AccountFromValue(params[0]);
755 Object sendTo = params[1].get_obj();
757 if (params.size() > 2)
758 nMinDepth = params[2].get_int();
761 wtx.strFromAccount = strAccount;
762 if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
763 wtx.mapValue["comment"] = params[3].get_str();
765 set<CBitcoinAddress> setAddress;
766 vector<pair<CScript, int64_t> > vecSend;
768 int64_t totalAmount = 0;
769 BOOST_FOREACH(const Pair& s, sendTo)
771 CBitcoinAddress address(s.name_);
772 if (!address.IsValid())
773 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid NovaCoin address: ")+s.name_);
775 if (setAddress.count(address))
776 throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+s.name_);
777 setAddress.insert(address);
779 CScript scriptPubKey;
780 scriptPubKey.SetDestination(address.Get());
781 int64_t nAmount = AmountFromValue(s.value_);
783 if (nAmount < MIN_TXOUT_AMOUNT)
784 throw JSONRPCError(-101, "Send amount too small");
786 totalAmount += nAmount;
788 vecSend.push_back(make_pair(scriptPubKey, nAmount));
791 EnsureWalletIsUnlocked();
793 if (GetBoolArg("-accounts", false))
796 int64_t nBalance = GetAccountBalance(strAccount, nMinDepth, MINE_SPENDABLE);
797 if (totalAmount > nBalance)
798 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds");
802 CReserveKey keyChange(pwalletMain);
803 int64_t nFeeRequired = 0;
804 bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired);
807 int64_t nTotal = pwalletMain->GetBalance(), nWatchOnly = pwalletMain->GetWatchOnlyBalance();
808 if (totalAmount + nFeeRequired > nTotal - nWatchOnly)
809 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds");
810 throw JSONRPCError(RPC_WALLET_ERROR, "Transaction creation failed");
812 if (!pwalletMain->CommitTransaction(wtx, keyChange))
813 throw JSONRPCError(RPC_WALLET_ERROR, "Transaction commit failed");
815 return wtx.GetHash().GetHex();
818 Value addmultisigaddress(const Array& params, bool fHelp)
820 if (fHelp || params.size() < 2 || params.size() > 3)
822 string msg = "addmultisigaddress <nrequired> <'[\"key\",\"key\"]'> [account]\n"
823 "Add a nrequired-to-sign multisignature address to the wallet\"\n"
824 "each key is a NovaCoin address or hex-encoded public key\n"
825 "If [account] is specified, assign address to [account].";
826 throw runtime_error(msg);
829 int nRequired = params[0].get_int();
830 const Array& keys = params[1].get_array();
832 if (params.size() > 2)
833 strAccount = AccountFromValue(params[2]);
835 // Gather public keys
837 throw runtime_error("a multisignature address must require at least one key to redeem");
838 if ((int)keys.size() < nRequired)
840 strprintf("not enough keys supplied "
841 "(got %" PRIszu " keys, but need at least %d to redeem)", keys.size(), nRequired));
842 if (keys.size() > 16)
843 throw runtime_error("Number of addresses involved in the multisignature address creation > 16\nReduce the number");
844 std::vector<CKey> pubkeys;
845 pubkeys.resize(keys.size());
846 for (unsigned int i = 0; i < keys.size(); i++)
848 const std::string& ks = keys[i].get_str();
850 // Case 1: Bitcoin address and we have full public key:
851 CBitcoinAddress address(ks);
852 if (address.IsValid())
855 if (!address.GetKeyID(keyID))
857 strprintf("%s does not refer to a key",ks.c_str()));
859 if (!pwalletMain->GetPubKey(keyID, vchPubKey))
861 strprintf("no full public key for address %s",ks.c_str()));
862 if (!vchPubKey.IsValid() || !pubkeys[i].SetPubKey(vchPubKey))
863 throw runtime_error(" Invalid public key: "+ks);
866 // Case 2: hex public key
869 CPubKey vchPubKey(ParseHex(ks));
870 if (!vchPubKey.IsValid() || !pubkeys[i].SetPubKey(vchPubKey))
871 throw runtime_error(" Invalid public key: "+ks);
875 throw runtime_error(" Invalid public key: "+ks);
879 // Construct using pay-to-script-hash:
881 inner.SetMultisig(nRequired, pubkeys);
883 if (inner.size() > MAX_SCRIPT_ELEMENT_SIZE)
885 strprintf("redeemScript exceeds size limit: %" PRIszu " > %d", inner.size(), MAX_SCRIPT_ELEMENT_SIZE));
887 CScriptID innerID = inner.GetID();
888 pwalletMain->AddCScript(inner);
890 pwalletMain->SetAddressBookName(innerID, strAccount);
891 return CBitcoinAddress(innerID).ToString();
894 Value addredeemscript(const Array& params, bool fHelp)
896 if (fHelp || params.size() < 1 || params.size() > 2)
898 string msg = "addredeemscript <redeemScript> [account]\n"
899 "Add a P2SH address with a specified redeemScript to the wallet.\n"
900 "If [account] is specified, assign address to [account].";
901 throw runtime_error(msg);
905 if (params.size() > 1)
906 strAccount = AccountFromValue(params[1]);
908 // Construct using pay-to-script-hash:
909 vector<unsigned char> innerData = ParseHexV(params[0], "redeemScript");
910 CScript inner(innerData.begin(), innerData.end());
911 CScriptID innerID = inner.GetID();
912 pwalletMain->AddCScript(inner);
914 pwalletMain->SetAddressBookName(innerID, strAccount);
915 return CBitcoinAddress(innerID).ToString();
925 nConf = std::numeric_limits<int>::max();
929 Value ListReceived(const Array& params, bool fByAccounts)
931 // Minimum confirmations
933 if (params.size() > 0)
934 nMinDepth = params[0].get_int();
936 // Whether to include empty accounts
937 bool fIncludeEmpty = false;
938 if (params.size() > 1)
939 fIncludeEmpty = params[1].get_bool();
942 map<CBitcoinAddress, tallyitem> mapTally;
943 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
945 const CWalletTx& wtx = (*it).second;
947 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
950 int nDepth = wtx.GetDepthInMainChain();
951 if (nDepth < nMinDepth)
954 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
956 CTxDestination address;
957 if (!ExtractDestination(txout.scriptPubKey, address) || !IsMine(*pwalletMain, address))
960 tallyitem& item = mapTally[address];
961 item.nAmount += txout.nValue;
962 item.nConf = min(item.nConf, nDepth);
968 map<string, tallyitem> mapAccountTally;
969 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
971 const CBitcoinAddress& address = item.first;
972 const string& strAccount = item.second;
973 map<CBitcoinAddress, tallyitem>::iterator it = mapTally.find(address);
974 if (it == mapTally.end() && !fIncludeEmpty)
978 int nConf = std::numeric_limits<int>::max();
979 if (it != mapTally.end())
981 nAmount = (*it).second.nAmount;
982 nConf = (*it).second.nConf;
987 tallyitem& item = mapAccountTally[strAccount];
988 item.nAmount += nAmount;
989 item.nConf = min(item.nConf, nConf);
994 obj.push_back(Pair("address", address.ToString()));
995 obj.push_back(Pair("account", strAccount));
996 obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
997 obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf)));
1004 for (map<string, tallyitem>::iterator it = mapAccountTally.begin(); it != mapAccountTally.end(); ++it)
1006 int64_t nAmount = (*it).second.nAmount;
1007 int nConf = (*it).second.nConf;
1009 obj.push_back(Pair("account", (*it).first));
1010 obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
1011 obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf)));
1019 Value listreceivedbyaddress(const Array& params, bool fHelp)
1021 if (fHelp || params.size() > 2)
1022 throw runtime_error(
1023 "listreceivedbyaddress [minconf=1] [includeempty=false]\n"
1024 "[minconf] is the minimum number of confirmations before payments are included.\n"
1025 "[includeempty] whether to include addresses that haven't received any payments.\n"
1026 "Returns an array of objects containing:\n"
1027 " \"address\" : receiving address\n"
1028 " \"account\" : the account of the receiving address\n"
1029 " \"amount\" : total amount received by the address\n"
1030 " \"confirmations\" : number of confirmations of the most recent transaction included");
1032 return ListReceived(params, false);
1035 Value listreceivedbyaccount(const Array& params, bool fHelp)
1037 if (fHelp || params.size() > 2)
1038 throw runtime_error(
1039 "listreceivedbyaccount [minconf=1] [includeempty=false]\n"
1040 "[minconf] is the minimum number of confirmations before payments are included.\n"
1041 "[includeempty] whether to include accounts that haven't received any payments.\n"
1042 "Returns an array of objects containing:\n"
1043 " \"account\" : the account of the receiving addresses\n"
1044 " \"amount\" : total amount received by addresses with this account\n"
1045 " \"confirmations\" : number of confirmations of the most recent transaction included");
1047 // Deprecation check
1048 accountingAllowed();
1050 return ListReceived(params, true);
1053 static void MaybePushAddress(Object & entry, const CTxDestination &dest)
1055 CBitcoinAddress addr;
1057 entry.push_back(Pair("address", addr.ToString()));
1060 void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret, const isminefilter& filter)
1062 int64_t nGeneratedImmature, nGeneratedMature, nFee;
1063 string strSentAccount;
1064 list<pair<CTxDestination, int64_t> > listReceived;
1065 list<pair<CTxDestination, int64_t> > listSent;
1067 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount, filter);
1069 bool fAllAccounts = (strAccount == string("*"));
1070 bool involvesWatchonly = wtx.IsFromMe(MINE_WATCH_ONLY);
1072 // Generated blocks assigned to account ""
1073 if ((nGeneratedMature+nGeneratedImmature) != 0 && (fAllAccounts || strAccount == ""))
1076 entry.push_back(Pair("account", string("")));
1077 if (nGeneratedImmature)
1079 entry.push_back(Pair("category", wtx.GetDepthInMainChain() ? "immature" : "orphan"));
1080 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedImmature)));
1084 entry.push_back(Pair("category", "generate"));
1085 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedMature)));
1088 WalletTxToJSON(wtx, entry);
1089 ret.push_back(entry);
1093 if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount))
1095 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& s, listSent)
1098 entry.push_back(Pair("account", strSentAccount));
1099 if(involvesWatchonly || (::IsMine(*pwalletMain, s.first) & MINE_WATCH_ONLY))
1100 entry.push_back(Pair("involvesWatchonly", true));
1101 MaybePushAddress(entry, s.first);
1103 if (wtx.GetDepthInMainChain() < 0) {
1104 entry.push_back(Pair("category", "conflicted"));
1106 entry.push_back(Pair("category", "send"));
1109 entry.push_back(Pair("amount", ValueFromAmount(-s.second)));
1110 entry.push_back(Pair("fee", ValueFromAmount(-nFee)));
1112 WalletTxToJSON(wtx, entry);
1113 ret.push_back(entry);
1118 if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth)
1120 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& r, listReceived)
1123 if (pwalletMain->mapAddressBook.count(r.first))
1124 account = pwalletMain->mapAddressBook[r.first];
1125 if (fAllAccounts || (account == strAccount))
1128 entry.push_back(Pair("account", account));
1129 if(involvesWatchonly || (::IsMine(*pwalletMain, r.first) & MINE_WATCH_ONLY))
1130 entry.push_back(Pair("involvesWatchonly", true));
1131 MaybePushAddress(entry, r.first);
1132 if (wtx.IsCoinBase())
1134 if (wtx.GetDepthInMainChain() < 1)
1135 entry.push_back(Pair("category", "orphan"));
1136 else if (wtx.GetBlocksToMaturity() > 0)
1137 entry.push_back(Pair("category", "immature"));
1139 entry.push_back(Pair("category", "generate"));
1142 entry.push_back(Pair("category", "receive"));
1143 entry.push_back(Pair("amount", ValueFromAmount(r.second)));
1145 WalletTxToJSON(wtx, entry);
1146 ret.push_back(entry);
1152 void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Array& ret)
1154 bool fAllAccounts = (strAccount == string("*"));
1156 if (fAllAccounts || acentry.strAccount == strAccount)
1159 entry.push_back(Pair("account", acentry.strAccount));
1160 entry.push_back(Pair("category", "move"));
1161 entry.push_back(Pair("time", (boost::int64_t)acentry.nTime));
1162 entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit)));
1163 entry.push_back(Pair("otheraccount", acentry.strOtherAccount));
1164 entry.push_back(Pair("comment", acentry.strComment));
1165 ret.push_back(entry);
1169 Value listtransactions(const Array& params, bool fHelp)
1171 if (fHelp || params.size() > 3)
1172 throw runtime_error(
1173 "listtransactions [account] [count=10] [from=0]\n"
1174 "Returns up to [count] most recent transactions skipping the first [from] transactions for account [account].");
1176 string strAccount = "*";
1177 if (params.size() > 0)
1178 strAccount = params[0].get_str();
1180 if (params.size() > 1)
1181 nCount = params[1].get_int();
1183 if (params.size() > 2)
1184 nFrom = params[2].get_int();
1186 isminefilter filter = MINE_SPENDABLE;
1187 if(params.size() > 3)
1188 if(params[3].get_bool())
1189 filter = filter | MINE_WATCH_ONLY;
1192 throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count");
1194 throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative from");
1198 std::list<CAccountingEntry> acentries;
1199 CWallet::TxItems txOrdered = pwalletMain->OrderedTxItems(acentries, strAccount);
1201 // iterate backwards until we have nCount items to return:
1202 for (CWallet::TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
1204 CWalletTx *const pwtx = (*it).second.first;
1206 ListTransactions(*pwtx, strAccount, 0, true, ret, filter);
1207 CAccountingEntry *const pacentry = (*it).second.second;
1209 AcentryToJSON(*pacentry, strAccount, ret);
1211 if ((int)ret.size() >= (nCount+nFrom)) break;
1213 // ret is newest to oldest
1215 if (nFrom > (int)ret.size())
1217 if ((nFrom + nCount) > (int)ret.size())
1218 nCount = ret.size() - nFrom;
1219 Array::iterator first = ret.begin();
1220 std::advance(first, nFrom);
1221 Array::iterator last = ret.begin();
1222 std::advance(last, nFrom+nCount);
1224 if (last != ret.end()) ret.erase(last, ret.end());
1225 if (first != ret.begin()) ret.erase(ret.begin(), first);
1227 std::reverse(ret.begin(), ret.end()); // Return oldest to newest
1232 Value listaccounts(const Array& params, bool fHelp)
1234 if (fHelp || params.size() > 1)
1235 throw runtime_error(
1236 "listaccounts [minconf=1]\n"
1237 "Returns Object that has account names as keys, account balances as values.");
1239 // Deprecation check
1240 accountingAllowed();
1243 if (params.size() > 0)
1244 nMinDepth = params[0].get_int();
1246 isminefilter includeWatchonly = MINE_SPENDABLE;
1247 if(params.size() > 1)
1248 if(params[1].get_bool())
1249 includeWatchonly = includeWatchonly | MINE_WATCH_ONLY;
1252 map<string, int64_t> mapAccountBalances;
1253 BOOST_FOREACH(const PAIRTYPE(CTxDestination, string)& entry, pwalletMain->mapAddressBook) {
1254 if (IsMine(*pwalletMain, entry.first)) // This address belongs to me
1255 mapAccountBalances[entry.second] = 0;
1258 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
1260 const CWalletTx& wtx = (*it).second;
1261 int64_t nGeneratedImmature, nGeneratedMature, nFee;
1262 string strSentAccount;
1263 list<pair<CTxDestination, int64_t> > listReceived;
1264 list<pair<CTxDestination, int64_t> > listSent;
1265 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount, includeWatchonly);
1266 mapAccountBalances[strSentAccount] -= nFee;
1267 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& s, listSent)
1268 mapAccountBalances[strSentAccount] -= s.second;
1269 if (wtx.GetDepthInMainChain() >= nMinDepth)
1271 mapAccountBalances[""] += nGeneratedMature;
1272 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& r, listReceived)
1273 if (pwalletMain->mapAddressBook.count(r.first))
1274 mapAccountBalances[pwalletMain->mapAddressBook[r.first]] += r.second;
1276 mapAccountBalances[""] += r.second;
1280 list<CAccountingEntry> acentries;
1281 CWalletDB(pwalletMain->strWalletFile).ListAccountCreditDebit("*", acentries);
1282 BOOST_FOREACH(const CAccountingEntry& entry, acentries)
1283 mapAccountBalances[entry.strAccount] += entry.nCreditDebit;
1286 BOOST_FOREACH(const PAIRTYPE(string, int64_t)& accountBalance, mapAccountBalances) {
1287 ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second)));
1292 Value listsinceblock(const Array& params, bool fHelp)
1295 throw runtime_error(
1296 "listsinceblock [blockhash] [target-confirmations]\n"
1297 "Get all transactions in blocks since block [blockhash], or all transactions if omitted");
1299 CBlockIndex *pindex = NULL;
1300 int target_confirms = 1;
1301 isminefilter filter = MINE_SPENDABLE;
1303 if (params.size() > 0)
1305 uint256 blockId = 0;
1307 blockId.SetHex(params[0].get_str());
1308 pindex = CBlockLocator(blockId).GetBlockIndex();
1311 if (params.size() > 1)
1313 target_confirms = params[1].get_int();
1315 if (target_confirms < 1)
1316 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter");
1319 if(params.size() > 2)
1320 if(params[2].get_bool())
1321 filter = filter | MINE_WATCH_ONLY;
1323 int depth = pindex ? (1 + nBestHeight - pindex->nHeight) : -1;
1327 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); it++)
1329 CWalletTx tx = (*it).second;
1331 if (depth == -1 || tx.GetDepthInMainChain() < depth)
1332 ListTransactions(tx, "*", 0, true, transactions, filter);
1337 if (target_confirms == 1)
1339 lastblock = hashBestChain;
1343 int target_height = pindexBest->nHeight + 1 - target_confirms;
1346 for (block = pindexBest;
1347 block && block->nHeight > target_height;
1348 block = block->pprev) { }
1350 lastblock = block ? block->GetBlockHash() : 0;
1354 ret.push_back(Pair("transactions", transactions));
1355 ret.push_back(Pair("lastblock", lastblock.GetHex()));
1360 Value gettransaction(const Array& params, bool fHelp)
1362 if (fHelp || params.size() != 1)
1363 throw runtime_error(
1364 "gettransaction <txid>\n"
1365 "Get detailed information about <txid>");
1368 hash.SetHex(params[0].get_str());
1370 isminefilter filter = MINE_SPENDABLE;
1371 if(params.size() > 1)
1372 if(params[1].get_bool())
1373 filter = filter | MINE_WATCH_ONLY;
1377 if (pwalletMain->mapWallet.count(hash))
1379 const CWalletTx& wtx = pwalletMain->mapWallet[hash];
1381 TxToJSON(wtx, 0, entry);
1383 int64_t nCredit = wtx.GetCredit(filter);
1384 int64_t nDebit = wtx.GetDebit(filter);
1385 int64_t nNet = nCredit - nDebit;
1386 int64_t nFee = (wtx.IsFromMe(filter) ? wtx.GetValueOut() - nDebit : 0);
1388 entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee)));
1389 if (wtx.IsFromMe(filter))
1390 entry.push_back(Pair("fee", ValueFromAmount(nFee)));
1392 WalletTxToJSON(wtx, entry);
1395 ListTransactions(pwalletMain->mapWallet[hash], "*", 0, false, details, filter);
1396 entry.push_back(Pair("details", details));
1401 uint256 hashBlock = 0;
1402 if (GetTransaction(hash, tx, hashBlock))
1404 TxToJSON(tx, 0, entry);
1406 entry.push_back(Pair("confirmations", 0));
1409 entry.push_back(Pair("blockhash", hashBlock.GetHex()));
1410 map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashBlock);
1411 if (mi != mapBlockIndex.end() && (*mi).second)
1413 CBlockIndex* pindex = (*mi).second;
1414 if (pindex->IsInMainChain())
1415 entry.push_back(Pair("confirmations", 1 + nBestHeight - pindex->nHeight));
1417 entry.push_back(Pair("confirmations", 0));
1422 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction");
1429 Value backupwallet(const Array& params, bool fHelp)
1431 if (fHelp || params.size() != 1)
1432 throw runtime_error(
1433 "backupwallet <destination>\n"
1434 "Safely copies wallet.dat to destination, which can be a directory or a path with filename.");
1436 string strDest = params[0].get_str();
1437 if (!BackupWallet(*pwalletMain, strDest))
1438 throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet backup failed!");
1444 Value keypoolrefill(const Array& params, bool fHelp)
1446 if (fHelp || params.size() > 1)
1447 throw runtime_error(
1448 "keypoolrefill [new-size]\n"
1449 "Fills the keypool.\n"
1450 "IMPORTANT: Any previous backups you have made of your wallet file "
1451 "should be replaced with the newly generated one."
1452 + HelpRequiringPassphrase());
1454 unsigned int nSize = max<unsigned int>(GetArg("-keypool", 100), 0);
1455 if (params.size() > 0) {
1456 if (params[0].get_int() < 0)
1457 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected valid size");
1458 nSize = (unsigned int) params[0].get_int();
1461 EnsureWalletIsUnlocked();
1463 pwalletMain->TopUpKeyPool(nSize);
1465 if (pwalletMain->GetKeyPoolSize() < nSize)
1466 throw JSONRPCError(RPC_WALLET_ERROR, "Error refreshing keypool.");
1471 Value keypoolreset(const Array& params, bool fHelp)
1473 if (fHelp || params.size() > 1)
1474 throw runtime_error(
1475 "keypoolreset [new-size]\n"
1476 "Resets the keypool.\n"
1477 "IMPORTANT: Any previous backups you have made of your wallet file "
1478 "should be replaced with the newly generated one."
1479 + HelpRequiringPassphrase());
1481 unsigned int nSize = max<unsigned int>(GetArg("-keypool", 100), 0);
1482 if (params.size() > 0) {
1483 if (params[0].get_int() < 0)
1484 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected valid size");
1485 nSize = (unsigned int) params[0].get_int();
1488 EnsureWalletIsUnlocked();
1490 pwalletMain->NewKeyPool(nSize);
1492 if (pwalletMain->GetKeyPoolSize() < nSize)
1493 throw JSONRPCError(RPC_WALLET_ERROR, "Error refreshing keypool.");
1499 void ThreadTopUpKeyPool(void* parg)
1501 // Make this thread recognisable as the key-topping-up thread
1502 RenameThread("novacoin-key-top");
1504 pwalletMain->TopUpKeyPool();
1507 void ThreadCleanWalletPassphrase(void* parg)
1509 // Make this thread recognisable as the wallet relocking thread
1510 RenameThread("novacoin-lock-wa");
1512 int64_t nMyWakeTime = GetTimeMillis() + *((int64_t*)parg) * 1000;
1514 ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime);
1516 if (nWalletUnlockTime == 0)
1518 nWalletUnlockTime = nMyWakeTime;
1522 if (nWalletUnlockTime==0)
1524 int64_t nToSleep = nWalletUnlockTime - GetTimeMillis();
1528 LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime);
1530 ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime);
1534 if (nWalletUnlockTime)
1536 nWalletUnlockTime = 0;
1537 pwalletMain->Lock();
1542 if (nWalletUnlockTime < nMyWakeTime)
1543 nWalletUnlockTime = nMyWakeTime;
1546 LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime);
1548 delete (int64_t*)parg;
1551 Value walletpassphrase(const Array& params, bool fHelp)
1553 if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 3))
1554 throw runtime_error(
1555 "walletpassphrase <passphrase> <timeout> [mintonly]\n"
1556 "Stores the wallet decryption key in memory for <timeout> seconds.\n"
1557 "mintonly is optional true/false allowing only block minting.");
1560 if (!pwalletMain->IsCrypted())
1561 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called.");
1563 if (!pwalletMain->IsLocked())
1564 throw JSONRPCError(RPC_WALLET_ALREADY_UNLOCKED, "Error: Wallet is already unlocked, use walletlock first if need to change unlock settings.");
1565 // Note that the walletpassphrase is stored in params[0] which is not mlock()ed
1566 SecureString strWalletPass;
1567 strWalletPass.reserve(100);
1568 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1569 // Alternately, find a way to make params[0] mlock()'d to begin with.
1570 strWalletPass = params[0].get_str().c_str();
1572 if (strWalletPass.length() > 0)
1574 if (!pwalletMain->Unlock(strWalletPass))
1575 throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
1578 throw runtime_error(
1579 "walletpassphrase <passphrase> <timeout>\n"
1580 "Stores the wallet decryption key in memory for <timeout> seconds.");
1582 NewThread(ThreadTopUpKeyPool, NULL);
1583 int64_t* pnSleepTime = new int64_t(params[1].get_int64());
1584 NewThread(ThreadCleanWalletPassphrase, pnSleepTime);
1586 // ppcoin: if user OS account compromised prevent trivial sendmoney commands
1587 if (params.size() > 2)
1588 fWalletUnlockMintOnly = params[2].get_bool();
1590 fWalletUnlockMintOnly = false;
1596 Value walletpassphrasechange(const Array& params, bool fHelp)
1598 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2))
1599 throw runtime_error(
1600 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1601 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1604 if (!pwalletMain->IsCrypted())
1605 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrasechange was called.");
1607 // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string)
1608 // Alternately, find a way to make params[0] mlock()'d to begin with.
1609 SecureString strOldWalletPass;
1610 strOldWalletPass.reserve(100);
1611 strOldWalletPass = params[0].get_str().c_str();
1613 SecureString strNewWalletPass;
1614 strNewWalletPass.reserve(100);
1615 strNewWalletPass = params[1].get_str().c_str();
1617 if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1)
1618 throw runtime_error(
1619 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1620 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1622 if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass))
1623 throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
1629 Value walletlock(const Array& params, bool fHelp)
1631 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 0))
1632 throw runtime_error(
1634 "Removes the wallet encryption key from memory, locking the wallet.\n"
1635 "After calling this method, you will need to call walletpassphrase again\n"
1636 "before being able to call any methods which require the wallet to be unlocked.");
1639 if (!pwalletMain->IsCrypted())
1640 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletlock was called.");
1643 LOCK(cs_nWalletUnlockTime);
1644 pwalletMain->Lock();
1645 nWalletUnlockTime = 0;
1652 Value encryptwallet(const Array& params, bool fHelp)
1654 if (!pwalletMain->IsCrypted() && (fHelp || params.size() != 1))
1655 throw runtime_error(
1656 "encryptwallet <passphrase>\n"
1657 "Encrypts the wallet with <passphrase>.");
1660 if (pwalletMain->IsCrypted())
1661 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an encrypted wallet, but encryptwallet was called.");
1663 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1664 // Alternately, find a way to make params[0] mlock()'d to begin with.
1665 SecureString strWalletPass;
1666 strWalletPass.reserve(100);
1667 strWalletPass = params[0].get_str().c_str();
1669 if (strWalletPass.length() < 1)
1670 throw runtime_error(
1671 "encryptwallet <passphrase>\n"
1672 "Encrypts the wallet with <passphrase>.");
1674 if (!pwalletMain->EncryptWallet(strWalletPass))
1675 throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: Failed to encrypt the wallet.");
1677 // BDB seems to have a bad habit of writing old data into
1678 // slack space in .dat files; that is bad if the old data is
1679 // unencrypted private keys. So:
1681 return "wallet encrypted; NovaCoin server stopping, restart to run with encrypted wallet. The keypool has been flushed, you need to make a new backup.";
1684 class DescribeAddressVisitor : public boost::static_visitor<Object>
1689 DescribeAddressVisitor(isminetype mineIn) : mine(mineIn) {}
1691 Object operator()(const CNoDestination &dest) const { return Object(); }
1692 Object operator()(const CKeyID &keyID) const {
1695 pwalletMain->GetPubKey(keyID, vchPubKey);
1696 obj.push_back(Pair("isscript", false));
1697 if (mine == MINE_SPENDABLE) {
1698 pwalletMain->GetPubKey(keyID, vchPubKey);
1699 obj.push_back(Pair("pubkey", HexStr(vchPubKey.Raw())));
1700 obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed()));
1705 Object operator()(const CScriptID &scriptID) const {
1707 obj.push_back(Pair("isscript", true));
1708 if (mine == MINE_SPENDABLE) {
1710 pwalletMain->GetCScript(scriptID, subscript);
1711 std::vector<CTxDestination> addresses;
1712 txnouttype whichType;
1714 ExtractDestinations(subscript, whichType, addresses, nRequired);
1715 obj.push_back(Pair("script", GetTxnOutputType(whichType)));
1716 obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end())));
1718 BOOST_FOREACH(const CTxDestination& addr, addresses)
1719 a.push_back(CBitcoinAddress(addr).ToString());
1720 obj.push_back(Pair("addresses", a));
1721 if (whichType == TX_MULTISIG)
1722 obj.push_back(Pair("sigsrequired", nRequired));
1728 Value validateaddress(const Array& params, bool fHelp)
1730 if (fHelp || params.size() != 1)
1731 throw runtime_error(
1732 "validateaddress <novacoinaddress>\n"
1733 "Return information about <novacoinaddress>.");
1735 CBitcoinAddress address(params[0].get_str());
1736 bool isValid = address.IsValid();
1739 ret.push_back(Pair("isvalid", isValid));
1742 CTxDestination dest = address.Get();
1743 string currentAddress = address.ToString();
1744 ret.push_back(Pair("address", currentAddress));
1745 isminetype mine = pwalletMain ? IsMine(*pwalletMain, dest) : MINE_NO;
1746 ret.push_back(Pair("ismine", mine != MINE_NO));
1747 if (mine != MINE_NO) {
1748 ret.push_back(Pair("watchonly", mine == MINE_WATCH_ONLY));
1749 Object detail = boost::apply_visitor(DescribeAddressVisitor(mine), dest);
1750 ret.insert(ret.end(), detail.begin(), detail.end());
1752 if (pwalletMain->mapAddressBook.count(dest))
1753 ret.push_back(Pair("account", pwalletMain->mapAddressBook[dest]));
1758 // ppcoin: reserve balance from being staked for network protection
1759 Value reservebalance(const Array& params, bool fHelp)
1761 if (fHelp || params.size() > 2)
1762 throw runtime_error(
1763 "reservebalance [<reserve> [amount]]\n"
1764 "<reserve> is true or false to turn balance reserve on or off.\n"
1765 "<amount> is a real and rounded to cent.\n"
1766 "Set reserve amount not participating in network protection.\n"
1767 "If no parameters provided current setting is printed.\n");
1769 if (params.size() > 0)
1771 bool fReserve = params[0].get_bool();
1774 if (params.size() == 1)
1775 throw runtime_error("must provide amount to reserve balance.\n");
1776 int64_t nAmount = AmountFromValue(params[1]);
1777 nAmount = (nAmount / CENT) * CENT; // round to cent
1779 throw runtime_error("amount cannot be negative.\n");
1780 mapArgs["-reservebalance"] = FormatMoney(nAmount).c_str();
1784 if (params.size() > 1)
1785 throw runtime_error("cannot specify amount to turn off reserve.\n");
1786 mapArgs["-reservebalance"] = "0";
1791 int64_t nReserveBalance = 0;
1792 if (mapArgs.count("-reservebalance") && !ParseMoney(mapArgs["-reservebalance"], nReserveBalance))
1793 throw runtime_error("invalid reserve balance amount\n");
1794 result.push_back(Pair("reserve", (nReserveBalance > 0)));
1795 result.push_back(Pair("amount", ValueFromAmount(nReserveBalance)));
1800 // ppcoin: check wallet integrity
1801 Value checkwallet(const Array& params, bool fHelp)
1803 if (fHelp || params.size() > 0)
1804 throw runtime_error(
1806 "Check wallet for integrity.\n");
1809 int64_t nBalanceInQuestion;
1810 pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion, true);
1812 if (nMismatchSpent == 0)
1813 result.push_back(Pair("wallet check passed", true));
1816 result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1817 result.push_back(Pair("amount in question", ValueFromAmount(nBalanceInQuestion)));
1823 // ppcoin: repair wallet
1824 Value repairwallet(const Array& params, bool fHelp)
1826 if (fHelp || params.size() > 0)
1827 throw runtime_error(
1829 "Repair wallet if checkwallet reports any problem.\n");
1832 int64_t nBalanceInQuestion;
1833 pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion);
1835 if (nMismatchSpent == 0)
1836 result.push_back(Pair("wallet check passed", true));
1839 result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1840 result.push_back(Pair("amount affected by repair", ValueFromAmount(nBalanceInQuestion)));
1845 // NovaCoin: resend unconfirmed wallet transactions
1846 Value resendtx(const Array& params, bool fHelp)
1848 if (fHelp || params.size() > 1)
1849 throw runtime_error(
1851 "Re-send unconfirmed transactions.\n"
1854 ResendWalletTransactions();
1859 // ppcoin: make a public-private key pair
1860 Value makekeypair(const Array& params, bool fHelp)
1862 if (fHelp || params.size() > 1)
1863 throw runtime_error(
1864 "makekeypair [prefix]\n"
1865 "Make a public/private key pair.\n"
1866 "[prefix] is optional preferred prefix for the public key.\n");
1868 string strPrefix = "";
1869 if (params.size() > 0)
1870 strPrefix = params[0].get_str();
1873 key.MakeNewKey(false);
1875 CPrivKey vchPrivKey = key.GetPrivKey();
1877 result.push_back(Pair("PrivateKey", HexStr<CPrivKey::iterator>(vchPrivKey.begin(), vchPrivKey.end())));
1878 result.push_back(Pair("PublicKey", HexStr(key.GetPubKey().Raw())));