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"
14 using namespace json_spirit;
17 int64_t nWalletUnlockTime;
18 static CCriticalSection cs_nWalletUnlockTime;
20 extern int64_t nReserveBalance;
21 extern void TxToJSON(const CTransaction& tx, const uint256& hashBlock, json_spirit::Object& entry);
23 std::string HelpRequiringPassphrase()
25 return pwalletMain->IsCrypted()
26 ? "\n\nRequires wallet passphrase to be set with walletpassphrase first"
30 void EnsureWalletIsUnlocked()
32 if (pwalletMain->IsLocked())
33 throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first.");
34 if (fWalletUnlockMintOnly)
35 throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Wallet unlocked for block minting only.");
38 void WalletTxToJSON(const CWalletTx& wtx, Object& entry)
40 int confirms = wtx.GetDepthInMainChain();
41 entry.push_back(Pair("confirmations", confirms));
42 if (wtx.IsCoinBase() || wtx.IsCoinStake())
43 entry.push_back(Pair("generated", true));
46 entry.push_back(Pair("blockhash", wtx.hashBlock.GetHex()));
47 entry.push_back(Pair("blockindex", wtx.nIndex));
48 entry.push_back(Pair("blocktime", (int64_t)(mapBlockIndex[wtx.hashBlock]->nTime)));
50 entry.push_back(Pair("txid", wtx.GetHash().GetHex()));
51 entry.push_back(Pair("time", (int64_t)wtx.GetTxTime()));
52 entry.push_back(Pair("timereceived", (int64_t)wtx.nTimeReceived));
53 BOOST_FOREACH(const PAIRTYPE(string,string)& item, wtx.mapValue)
54 entry.push_back(Pair(item.first, item.second));
57 string AccountFromValue(const Value& value)
59 string strAccount = value.get_str();
60 if (strAccount == "*")
61 throw JSONRPCError(RPC_WALLET_INVALID_ACCOUNT_NAME, "Invalid account name");
65 Value getinfo(const Array& params, bool fHelp)
67 if (fHelp || params.size() != 0)
70 "Returns an object containing various state info.");
73 GetProxy(NET_IPV4, proxy);
75 Object obj, diff, timestamping;
76 obj.push_back(Pair("version", FormatFullVersion()));
77 obj.push_back(Pair("protocolversion",(int)PROTOCOL_VERSION));
78 obj.push_back(Pair("walletversion", pwalletMain->GetVersion()));
79 obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance())));
80 obj.push_back(Pair("unspendable", ValueFromAmount(pwalletMain->GetWatchOnlyBalance())));
81 obj.push_back(Pair("newmint", ValueFromAmount(pwalletMain->GetNewMint())));
82 obj.push_back(Pair("stake", ValueFromAmount(pwalletMain->GetStake())));
83 obj.push_back(Pair("blocks", (int)nBestHeight));
85 timestamping.push_back(Pair("systemclock", GetTime()));
86 timestamping.push_back(Pair("adjustedtime", GetAdjustedTime()));
88 int64_t nNtpOffset = GetNtpOffset(),
89 nP2POffset = GetNodesOffset();
91 timestamping.push_back(Pair("ntpoffset", nNtpOffset != INT64_MAX ? nNtpOffset : Value::null));
92 timestamping.push_back(Pair("p2poffset", nP2POffset != INT64_MAX ? nP2POffset : Value::null));
94 obj.push_back(Pair("timestamping", timestamping));
96 obj.push_back(Pair("moneysupply", ValueFromAmount(pindexBest->nMoneySupply)));
97 obj.push_back(Pair("connections", (int)vNodes.size()));
98 obj.push_back(Pair("proxy", (proxy.first.IsValid() ? proxy.first.ToStringIPPort() : string())));
99 obj.push_back(Pair("ip", addrSeenByPeer.ToStringIP()));
101 diff.push_back(Pair("proof-of-work", GetDifficulty()));
102 diff.push_back(Pair("proof-of-stake", GetDifficulty(GetLastBlockIndex(pindexBest, true))));
103 obj.push_back(Pair("difficulty", diff));
105 obj.push_back(Pair("testnet", fTestNet));
106 obj.push_back(Pair("keypoololdest", (int64_t)pwalletMain->GetOldestKeyPoolTime()));
107 obj.push_back(Pair("keypoolsize", (int)pwalletMain->GetKeyPoolSize()));
108 obj.push_back(Pair("paytxfee", ValueFromAmount(nTransactionFee)));
109 obj.push_back(Pair("mininput", ValueFromAmount(nMinimumInputValue)));
110 if (pwalletMain->IsCrypted())
111 obj.push_back(Pair("unlocked_until", (int64_t)nWalletUnlockTime / 1000));
112 obj.push_back(Pair("errors", GetWarnings("statusbar")));
116 Value getnewaddress(const Array& params, bool fHelp)
118 if (fHelp || params.size() > 1)
120 "getnewaddress [account]\n"
121 "Returns a new NovaCoin address for receiving payments. "
122 "If [account] is specified (recommended), it is added to the address book "
123 "so payments received with the address will be credited to [account].");
125 // Parse the account first so we don't generate a key if there's an error
127 if (params.size() > 0)
128 strAccount = AccountFromValue(params[0]);
130 if (!pwalletMain->IsLocked())
131 pwalletMain->TopUpKeyPool();
133 // Generate a new key that is added to wallet
135 if (!pwalletMain->GetKeyFromPool(newKey, false))
136 throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
137 CKeyID keyID = newKey.GetID();
139 pwalletMain->SetAddressBookName(keyID, strAccount);
141 return CBitcoinAddress(keyID).ToString();
145 CBitcoinAddress GetAccountAddress(string strAccount, bool bForceNew=false)
147 CWalletDB walletdb(pwalletMain->strWalletFile);
150 walletdb.ReadAccount(strAccount, account);
152 bool bKeyUsed = false;
154 // Check if the current key has been used
155 if (account.vchPubKey.IsValid())
157 CScript scriptPubKey;
158 scriptPubKey.SetDestination(account.vchPubKey.GetID());
159 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin();
160 it != pwalletMain->mapWallet.end() && account.vchPubKey.IsValid();
163 const CWalletTx& wtx = (*it).second;
164 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
165 if (txout.scriptPubKey == scriptPubKey)
170 // Generate a new key
171 if (!account.vchPubKey.IsValid() || bForceNew || bKeyUsed)
173 if (!pwalletMain->GetKeyFromPool(account.vchPubKey, false))
174 throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
176 pwalletMain->SetAddressBookName(account.vchPubKey.GetID(), strAccount);
177 walletdb.WriteAccount(strAccount, account);
180 return CBitcoinAddress(account.vchPubKey.GetID());
183 Value getaccountaddress(const Array& params, bool fHelp)
185 if (fHelp || params.size() != 1)
187 "getaccountaddress <account>\n"
188 "Returns the current NovaCoin address for receiving payments to this account.");
190 // Parse the account first so we don't generate a key if there's an error
191 string strAccount = AccountFromValue(params[0]);
195 ret = GetAccountAddress(strAccount).ToString();
202 Value setaccount(const Array& params, bool fHelp)
204 if (fHelp || params.size() < 1 || params.size() > 2)
206 "setaccount <novacoinaddress> <account>\n"
207 "Sets the account associated with the given address.");
209 CBitcoinAddress address(params[0].get_str());
210 if (!address.IsValid())
211 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
215 if (params.size() > 1)
216 strAccount = AccountFromValue(params[1]);
218 // Detect when changing the account of an address that is the 'unused current key' of another account:
219 if (pwalletMain->mapAddressBook.count(address.Get()))
221 string strOldAccount = pwalletMain->mapAddressBook[address.Get()];
222 if (address == GetAccountAddress(strOldAccount))
223 GetAccountAddress(strOldAccount, true);
226 pwalletMain->SetAddressBookName(address.Get(), strAccount);
232 Value getaccount(const Array& params, bool fHelp)
234 if (fHelp || params.size() != 1)
236 "getaccount <novacoinaddress>\n"
237 "Returns the account associated with the given address.");
239 CBitcoinAddress address(params[0].get_str());
240 if (!address.IsValid())
241 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
244 map<CTxDestination, string>::iterator mi = pwalletMain->mapAddressBook.find(address.Get());
245 if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.empty())
246 strAccount = (*mi).second;
251 Value getaddressesbyaccount(const Array& params, bool fHelp)
253 if (fHelp || params.size() != 1)
255 "getaddressesbyaccount <account>\n"
256 "Returns the list of addresses for the given account.");
258 string strAccount = AccountFromValue(params[0]);
260 // Find all addresses that have the given account
262 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
264 const CBitcoinAddress& address = item.first;
265 const string& strName = item.second;
266 if (strName == strAccount)
267 ret.push_back(address.ToString());
272 Value mergecoins(const Array& params, bool fHelp)
274 if (fHelp || params.size() != 3)
276 "mergecoins <amount> <minvalue> <outputvalue>\n"
277 "<amount> is resulting inputs sum\n"
278 "<minvalue> is minimum value of inputs which are used in join process\n"
279 "<outputvalue> is resulting value of inputs which will be created\n"
280 "All values are real and and rounded to the nearest " + FormatMoney(nMinimumInputValue)
281 + HelpRequiringPassphrase());
283 if (pwalletMain->IsLocked())
284 throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first.");
287 int64_t nAmount = AmountFromValue(params[0]);
290 int64_t nMinValue = AmountFromValue(params[1]);
293 int64_t nOutputValue = AmountFromValue(params[2]);
295 if (nAmount < nMinimumInputValue)
296 throw JSONRPCError(-101, "Send amount too small");
298 if (nMinValue < nMinimumInputValue)
299 throw JSONRPCError(-101, "Max value too small");
301 if (nOutputValue < nMinimumInputValue)
302 throw JSONRPCError(-101, "Output value too small");
304 if (nOutputValue < nMinValue)
305 throw JSONRPCError(-101, "Output value is lower than min value");
307 list<uint256> listMerged;
308 if (!pwalletMain->MergeCoins(nAmount, nMinValue, nOutputValue, listMerged))
312 BOOST_FOREACH(const uint256 txHash, listMerged)
313 mergedHashes.push_back(txHash.GetHex());
318 Value sendtoaddress(const Array& params, bool fHelp)
320 if (fHelp || params.size() < 2 || params.size() > 4)
322 "sendtoaddress <novacoinaddress> <amount> [comment] [comment-to]\n"
323 "<amount> is a real and is rounded to the nearest " + FormatMoney(nMinimumInputValue)
324 + HelpRequiringPassphrase());
327 CScript scriptPubKey;
328 string strAddress = params[0].get_str();
330 CBitcoinAddress address(strAddress);
331 if (address.IsValid())
332 scriptPubKey.SetAddress(address);
334 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
337 int64_t nAmount = AmountFromValue(params[1]);
339 if (nAmount < nMinimumInputValue)
340 throw JSONRPCError(-101, "Send amount too small");
344 if (params.size() > 2 && params[2].type() != null_type && !params[2].get_str().empty())
345 wtx.mapValue["comment"] = params[2].get_str();
346 if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
347 wtx.mapValue["to"] = params[3].get_str();
349 if (pwalletMain->IsLocked())
350 throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first.");
352 string strError = pwalletMain->SendMoney(scriptPubKey, nAmount, wtx);
354 throw JSONRPCError(RPC_WALLET_ERROR, strError);
356 return wtx.GetHash().GetHex();
359 Value listaddressgroupings(const Array& params, bool fHelp)
363 "listaddressgroupings\n"
364 "Lists groups of addresses which have had their common ownership\n"
365 "made public by common use as inputs or as the resulting change\n"
366 "in past transactions");
369 map<CTxDestination, int64_t> balances = pwalletMain->GetAddressBalances();
370 BOOST_FOREACH(set<CTxDestination> grouping, pwalletMain->GetAddressGroupings())
373 BOOST_FOREACH(CTxDestination address, grouping)
376 addressInfo.push_back(CBitcoinAddress(address).ToString());
377 addressInfo.push_back(ValueFromAmount(balances[address]));
379 LOCK(pwalletMain->cs_wallet);
380 if (pwalletMain->mapAddressBook.find(CBitcoinAddress(address).Get()) != pwalletMain->mapAddressBook.end())
381 addressInfo.push_back(pwalletMain->mapAddressBook.find(CBitcoinAddress(address).Get())->second);
383 jsonGrouping.push_back(addressInfo);
385 jsonGroupings.push_back(jsonGrouping);
387 return jsonGroupings;
390 Value signmessage(const Array& params, bool fHelp)
392 if (fHelp || params.size() != 2)
394 "signmessage <novacoinaddress> <message>\n"
395 "Sign a message with the private key of an address");
397 EnsureWalletIsUnlocked();
399 string strAddress = params[0].get_str();
400 string strMessage = params[1].get_str();
402 CBitcoinAddress addr(strAddress);
404 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
407 if (!addr.GetKeyID(keyID))
408 throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
411 if (!pwalletMain->GetKey(keyID, key))
412 throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available");
414 CDataStream ss(SER_GETHASH, 0);
415 ss << strMessageMagic;
418 vector<unsigned char> vchSig;
419 if (!key.SignCompact(Hash(ss.begin(), ss.end()), vchSig))
420 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed");
422 return EncodeBase64(&vchSig[0], vchSig.size());
425 Value verifymessage(const Array& params, bool fHelp)
427 if (fHelp || params.size() != 3)
429 "verifymessage <novacoinaddress> <signature> <message>\n"
430 "Verify a signed message");
432 string strAddress = params[0].get_str();
433 string strSign = params[1].get_str();
434 string strMessage = params[2].get_str();
436 CBitcoinAddress addr(strAddress);
438 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
441 if (!addr.GetKeyID(keyID))
442 throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
444 bool fInvalid = false;
445 vector<unsigned char> vchSig = DecodeBase64(strSign.c_str(), &fInvalid);
448 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Malformed base64 encoding");
450 CDataStream ss(SER_GETHASH, 0);
451 ss << strMessageMagic;
455 if (!key.SetCompactSignature(Hash(ss.begin(), ss.end()), vchSig))
458 return (key.GetPubKey().GetID() == keyID);
462 Value getreceivedbyaddress(const Array& params, bool fHelp)
464 if (fHelp || params.size() < 1 || params.size() > 2)
466 "getreceivedbyaddress <novacoinaddress> [minconf=1]\n"
467 "Returns the total amount received by <novacoinaddress> in transactions with at least [minconf] confirmations.");
470 CBitcoinAddress address = CBitcoinAddress(params[0].get_str());
471 if (!address.IsValid())
472 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
473 if (!IsMine(*pwalletMain,address))
476 // Minimum confirmations
478 if (params.size() > 1)
479 nMinDepth = params[1].get_int();
482 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
484 const CWalletTx& wtx = (*it).second;
485 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
487 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
489 CBitcoinAddress addressRet;
490 if (!pwalletMain->ExtractAddress(txout.scriptPubKey, addressRet))
492 if (addressRet == address)
493 if (wtx.GetDepthInMainChain() >= nMinDepth)
494 nAmount += txout.nValue;
498 return ValueFromAmount(nAmount);
501 void GetAccountAddresses(string strAccount, set<CTxDestination>& setAddress)
503 BOOST_FOREACH(const PAIRTYPE(CTxDestination, string)& item, pwalletMain->mapAddressBook)
505 const CTxDestination& address = item.first;
506 const string& strName = item.second;
507 if (strName == strAccount)
508 setAddress.insert(address);
512 Value getreceivedbyaccount(const Array& params, bool fHelp)
514 if (fHelp || params.size() < 1 || params.size() > 2)
516 "getreceivedbyaccount <account> [minconf=1]\n"
517 "Returns the total amount received by addresses with <account> in transactions with at least [minconf] confirmations.");
519 // Minimum confirmations
521 if (params.size() > 1)
522 nMinDepth = params[1].get_int();
524 // Get the set of pub keys assigned to account
525 string strAccount = AccountFromValue(params[0]);
526 set<CTxDestination> setAddress;
527 GetAccountAddresses(strAccount, setAddress);
531 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
533 const CWalletTx& wtx = (*it).second;
534 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
537 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
539 CTxDestination address;
540 if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*pwalletMain, address) && setAddress.count(address))
541 if (wtx.GetDepthInMainChain() >= nMinDepth)
542 nAmount += txout.nValue;
546 return (double)nAmount / (double)COIN;
550 int64_t GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth, const isminefilter& filter)
552 int64_t nBalance = 0;
554 // Tally wallet transactions
555 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
557 const CWalletTx& wtx = (*it).second;
561 int64_t nGenerated, nReceived, nSent, nFee;
562 wtx.GetAccountAmounts(strAccount, nGenerated, nReceived, nSent, nFee, filter);
564 if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth)
565 nBalance += nReceived;
566 nBalance += nGenerated - nSent - nFee;
569 // Tally internal accounting entries
570 nBalance += walletdb.GetAccountCreditDebit(strAccount);
575 int64_t GetAccountBalance(const string& strAccount, int nMinDepth, const isminefilter& filter)
577 CWalletDB walletdb(pwalletMain->strWalletFile);
578 return GetAccountBalance(walletdb, strAccount, nMinDepth, filter);
582 Value getbalance(const Array& params, bool fHelp)
584 if (fHelp || params.size() > 2)
586 "getbalance [account] [minconf=1] [watchonly=0]\n"
587 "If [account] is not specified, returns the server's total available balance.\n"
588 "If [account] is specified, returns the balance in the account.\n"
589 "if [includeWatchonly] is specified, include balance in watchonly addresses (see 'importaddress').");
591 if (params.size() == 0)
592 return ValueFromAmount(pwalletMain->GetBalance());
595 if (params.size() > 1)
596 nMinDepth = params[1].get_int();
597 isminefilter filter = MINE_SPENDABLE;
598 if(params.size() > 2)
599 if(params[2].get_bool())
600 filter = filter | MINE_WATCH_ONLY;
602 if (params[0].get_str() == "*") {
603 // Calculate total balance a different way from GetBalance()
604 // (GetBalance() sums up all unspent TxOuts)
605 // getbalance and getbalance '*' 0 should return the same number.
606 int64_t nBalance = 0;
607 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
609 const CWalletTx& wtx = (*it).second;
610 if (!wtx.IsTrusted())
613 int64_t allGeneratedImmature, allGeneratedMature, allFee;
614 allGeneratedImmature = allGeneratedMature = allFee = 0;
616 string strSentAccount;
617 list<pair<CTxDestination, int64_t> > listReceived;
618 list<pair<CTxDestination, int64_t> > listSent;
619 wtx.GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount, filter);
620 if (wtx.GetDepthInMainChain() >= nMinDepth)
622 BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64_t)& r, listReceived)
623 nBalance += r.second;
625 BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64_t)& r, listSent)
626 nBalance -= r.second;
628 nBalance += allGeneratedMature;
630 return ValueFromAmount(nBalance);
633 string strAccount = AccountFromValue(params[0]);
635 int64_t nBalance = GetAccountBalance(strAccount, nMinDepth, filter);
637 return ValueFromAmount(nBalance);
641 Value movecmd(const Array& params, bool fHelp)
643 if (fHelp || params.size() < 3 || params.size() > 5)
645 "move <fromaccount> <toaccount> <amount> [minconf=1] [comment]\n"
646 "Move from one account in your wallet to another.");
648 string strFrom = AccountFromValue(params[0]);
649 string strTo = AccountFromValue(params[1]);
650 int64_t nAmount = AmountFromValue(params[2]);
652 if (nAmount < nMinimumInputValue)
653 throw JSONRPCError(-101, "Send amount too small");
655 if (params.size() > 3)
656 // unused parameter, used to be nMinDepth, keep type-checking it though
657 (void)params[3].get_int();
659 if (params.size() > 4)
660 strComment = params[4].get_str();
662 CWalletDB walletdb(pwalletMain->strWalletFile);
663 if (!walletdb.TxnBegin())
664 throw JSONRPCError(RPC_DATABASE_ERROR, "database error");
666 int64_t nNow = GetAdjustedTime();
669 CAccountingEntry debit;
670 debit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb);
671 debit.strAccount = strFrom;
672 debit.nCreditDebit = -nAmount;
674 debit.strOtherAccount = strTo;
675 debit.strComment = strComment;
676 walletdb.WriteAccountingEntry(debit);
679 CAccountingEntry credit;
680 credit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb);
681 credit.strAccount = strTo;
682 credit.nCreditDebit = nAmount;
684 credit.strOtherAccount = strFrom;
685 credit.strComment = strComment;
686 walletdb.WriteAccountingEntry(credit);
688 if (!walletdb.TxnCommit())
689 throw JSONRPCError(RPC_DATABASE_ERROR, "database error");
695 Value sendfrom(const Array& params, bool fHelp)
697 if (fHelp || params.size() < 3 || params.size() > 6)
699 "sendfrom <fromaccount> <tonovacoinaddress> <amount> [minconf=1] [comment] [comment-to]\n"
700 "<amount> is a real and is rounded to the nearest " + FormatMoney(nMinimumInputValue)
701 + HelpRequiringPassphrase());
703 string strAccount = AccountFromValue(params[0]);
706 CScript scriptPubKey;
707 string strAddress = params[0].get_str();
709 CBitcoinAddress address(strAddress);
710 if (address.IsValid())
711 scriptPubKey.SetAddress(address);
713 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address");
716 int64_t nAmount = AmountFromValue(params[2]);
718 if (nAmount < nMinimumInputValue)
719 throw JSONRPCError(-101, "Send amount too small");
722 if (params.size() > 3)
723 nMinDepth = params[3].get_int();
726 wtx.strFromAccount = strAccount;
727 if (params.size() > 4 && params[4].type() != null_type && !params[4].get_str().empty())
728 wtx.mapValue["comment"] = params[4].get_str();
729 if (params.size() > 5 && params[5].type() != null_type && !params[5].get_str().empty())
730 wtx.mapValue["to"] = params[5].get_str();
732 EnsureWalletIsUnlocked();
735 int64_t nBalance = GetAccountBalance(strAccount, nMinDepth, MINE_SPENDABLE);
736 if (nAmount > nBalance)
737 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds");
740 string strError = pwalletMain->SendMoney(scriptPubKey, nAmount, wtx);
742 throw JSONRPCError(RPC_WALLET_ERROR, strError);
744 return wtx.GetHash().GetHex();
748 Value sendmany(const Array& params, bool fHelp)
750 if (fHelp || params.size() < 2 || params.size() > 4)
752 "sendmany <fromaccount> '{address:amount,...}' [minconf=1] [comment]\n"
753 "amounts are double-precision floating point numbers"
754 + HelpRequiringPassphrase());
756 string strAccount = AccountFromValue(params[0]);
757 Object sendTo = params[1].get_obj();
759 if (params.size() > 2)
760 nMinDepth = params[2].get_int();
763 wtx.strFromAccount = strAccount;
764 if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
765 wtx.mapValue["comment"] = params[3].get_str();
767 set<CBitcoinAddress> setAddress;
768 vector<pair<CScript, int64_t> > vecSend;
770 int64_t totalAmount = 0;
771 BOOST_FOREACH(const Pair& s, sendTo)
773 CBitcoinAddress address(s.name_);
774 if (!address.IsValid())
775 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid NovaCoin address: ")+s.name_);
777 if (!address.IsPair())
779 if (setAddress.count(address))
780 throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+s.name_);
781 setAddress.insert(address);
784 CScript scriptPubKey;
785 scriptPubKey.SetAddress(address);
786 int64_t nAmount = AmountFromValue(s.value_);
788 if (nAmount < nMinimumInputValue)
789 throw JSONRPCError(-101, "Send amount too small");
791 totalAmount += nAmount;
793 vecSend.push_back(make_pair(scriptPubKey, nAmount));
796 EnsureWalletIsUnlocked();
799 int64_t nBalance = GetAccountBalance(strAccount, nMinDepth, MINE_SPENDABLE);
800 if (totalAmount > nBalance)
801 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds");
804 CReserveKey keyChange(pwalletMain);
805 int64_t nFeeRequired = 0;
806 bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired);
809 int64_t nTotal = pwalletMain->GetBalance(), nWatchOnly = pwalletMain->GetWatchOnlyBalance();
810 if (totalAmount + nFeeRequired > nTotal - nWatchOnly)
811 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds");
812 throw JSONRPCError(RPC_WALLET_ERROR, "Transaction creation failed");
814 if (!pwalletMain->CommitTransaction(wtx, keyChange))
815 throw JSONRPCError(RPC_WALLET_ERROR, "Transaction commit failed");
817 return wtx.GetHash().GetHex();
820 Value addmultisigaddress(const Array& params, bool fHelp)
822 if (fHelp || params.size() < 2 || params.size() > 3)
824 string msg = "addmultisigaddress <nrequired> <'[\"key\",\"key\"]'> [account]\n"
825 "Add a nrequired-to-sign multisignature address to the wallet\"\n"
826 "each key is a NovaCoin address or hex-encoded public key\n"
827 "If [account] is specified, assign address to [account].";
828 throw runtime_error(msg);
831 int nRequired = params[0].get_int();
832 const Array& keys = params[1].get_array();
834 if (params.size() > 2)
835 strAccount = AccountFromValue(params[2]);
837 // Gather public keys
839 throw runtime_error("a multisignature address must require at least one key to redeem");
840 if ((int)keys.size() < nRequired)
842 strprintf("not enough keys supplied "
843 "(got %" PRIszu " keys, but need at least %d to redeem)", keys.size(), nRequired));
844 if (keys.size() > 16)
845 throw runtime_error("Number of addresses involved in the multisignature address creation > 16\nReduce the number");
846 std::vector<CKey> pubkeys;
847 pubkeys.resize(keys.size());
848 for (unsigned int i = 0; i < keys.size(); i++)
850 const std::string& ks = keys[i].get_str();
852 // Case 1: Bitcoin address and we have full public key:
853 CBitcoinAddress address(ks);
854 if (address.IsValid())
857 if (!address.GetKeyID(keyID))
859 strprintf("%s does not refer to a key",ks.c_str()));
861 if (!pwalletMain->GetPubKey(keyID, vchPubKey))
863 strprintf("no full public key for address %s",ks.c_str()));
864 if (!vchPubKey.IsValid() || !pubkeys[i].SetPubKey(vchPubKey))
865 throw runtime_error(" Invalid public key: "+ks);
868 // Case 2: hex public key
871 CPubKey vchPubKey(ParseHex(ks));
872 if (!vchPubKey.IsValid() || !pubkeys[i].SetPubKey(vchPubKey))
873 throw runtime_error(" Invalid public key: "+ks);
877 throw runtime_error(" Invalid public key: "+ks);
881 // Construct using pay-to-script-hash:
883 inner.SetMultisig(nRequired, pubkeys);
885 if (inner.size() > MAX_SCRIPT_ELEMENT_SIZE)
887 strprintf("redeemScript exceeds size limit: %" PRIszu " > %d", inner.size(), MAX_SCRIPT_ELEMENT_SIZE));
889 CScriptID innerID = inner.GetID();
890 pwalletMain->AddCScript(inner);
892 pwalletMain->SetAddressBookName(innerID, strAccount);
893 return CBitcoinAddress(innerID).ToString();
896 Value addredeemscript(const Array& params, bool fHelp)
898 if (fHelp || params.size() < 1 || params.size() > 2)
900 string msg = "addredeemscript <redeemScript> [account]\n"
901 "Add a P2SH address with a specified redeemScript to the wallet.\n"
902 "If [account] is specified, assign address to [account].";
903 throw runtime_error(msg);
907 if (params.size() > 1)
908 strAccount = AccountFromValue(params[1]);
910 // Construct using pay-to-script-hash:
911 vector<unsigned char> innerData = ParseHexV(params[0], "redeemScript");
912 CScript inner(innerData.begin(), innerData.end());
913 CScriptID innerID = inner.GetID();
914 pwalletMain->AddCScript(inner);
916 pwalletMain->SetAddressBookName(innerID, strAccount);
917 return CBitcoinAddress(innerID).ToString();
927 nConf = std::numeric_limits<int>::max();
931 Value ListReceived(const Array& params, bool fByAccounts)
933 // Minimum confirmations
935 if (params.size() > 0)
936 nMinDepth = params[0].get_int();
938 // Whether to include empty accounts
939 bool fIncludeEmpty = false;
940 if (params.size() > 1)
941 fIncludeEmpty = params[1].get_bool();
944 map<CBitcoinAddress, tallyitem> mapTally;
945 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
947 const CWalletTx& wtx = (*it).second;
949 if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
952 int nDepth = wtx.GetDepthInMainChain();
953 if (nDepth < nMinDepth)
956 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
958 CTxDestination address;
959 if (!ExtractDestination(txout.scriptPubKey, address) || !IsMine(*pwalletMain, address))
962 tallyitem& item = mapTally[address];
963 item.nAmount += txout.nValue;
964 item.nConf = min(item.nConf, nDepth);
970 map<string, tallyitem> mapAccountTally;
971 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
973 const CBitcoinAddress& address = item.first;
974 const string& strAccount = item.second;
975 map<CBitcoinAddress, tallyitem>::iterator it = mapTally.find(address);
976 if (it == mapTally.end() && !fIncludeEmpty)
980 int nConf = std::numeric_limits<int>::max();
981 if (it != mapTally.end())
983 nAmount = (*it).second.nAmount;
984 nConf = (*it).second.nConf;
989 tallyitem& item = mapAccountTally[strAccount];
990 item.nAmount += nAmount;
991 item.nConf = min(item.nConf, nConf);
996 obj.push_back(Pair("address", address.ToString()));
997 obj.push_back(Pair("account", strAccount));
998 obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
999 obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf)));
1006 for (map<string, tallyitem>::iterator it = mapAccountTally.begin(); it != mapAccountTally.end(); ++it)
1008 int64_t nAmount = (*it).second.nAmount;
1009 int nConf = (*it).second.nConf;
1011 obj.push_back(Pair("account", (*it).first));
1012 obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
1013 obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf)));
1021 Value listreceivedbyaddress(const Array& params, bool fHelp)
1023 if (fHelp || params.size() > 2)
1024 throw runtime_error(
1025 "listreceivedbyaddress [minconf=1] [includeempty=false]\n"
1026 "[minconf] is the minimum number of confirmations before payments are included.\n"
1027 "[includeempty] whether to include addresses that haven't received any payments.\n"
1028 "Returns an array of objects containing:\n"
1029 " \"address\" : receiving address\n"
1030 " \"account\" : the account of the receiving address\n"
1031 " \"amount\" : total amount received by the address\n"
1032 " \"confirmations\" : number of confirmations of the most recent transaction included");
1034 return ListReceived(params, false);
1037 Value listreceivedbyaccount(const Array& params, bool fHelp)
1039 if (fHelp || params.size() > 2)
1040 throw runtime_error(
1041 "listreceivedbyaccount [minconf=1] [includeempty=false]\n"
1042 "[minconf] is the minimum number of confirmations before payments are included.\n"
1043 "[includeempty] whether to include accounts that haven't received any payments.\n"
1044 "Returns an array of objects containing:\n"
1045 " \"account\" : the account of the receiving addresses\n"
1046 " \"amount\" : total amount received by addresses with this account\n"
1047 " \"confirmations\" : number of confirmations of the most recent transaction included");
1049 return ListReceived(params, true);
1052 static void MaybePushAddress(Object & entry, const CTxDestination &dest)
1054 CBitcoinAddress addr;
1056 entry.push_back(Pair("address", addr.ToString()));
1059 void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret, const isminefilter& filter)
1061 int64_t nGeneratedImmature, nGeneratedMature, nFee;
1062 string strSentAccount;
1063 list<pair<CTxDestination, int64_t> > listReceived;
1064 list<pair<CTxDestination, int64_t> > listSent;
1066 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount, filter);
1068 bool fAllAccounts = (strAccount == string("*"));
1069 bool involvesWatchonly = wtx.IsFromMe(MINE_WATCH_ONLY);
1071 // Generated blocks assigned to account ""
1072 if ((nGeneratedMature+nGeneratedImmature) != 0 && (fAllAccounts || strAccount == ""))
1075 entry.push_back(Pair("account", string("")));
1076 if (nGeneratedImmature)
1078 entry.push_back(Pair("category", wtx.GetDepthInMainChain() ? "immature" : "orphan"));
1079 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedImmature)));
1083 entry.push_back(Pair("category", "generate"));
1084 entry.push_back(Pair("amount", ValueFromAmount(nGeneratedMature)));
1087 WalletTxToJSON(wtx, entry);
1088 ret.push_back(entry);
1092 if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount))
1094 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& s, listSent)
1097 entry.push_back(Pair("account", strSentAccount));
1098 if(involvesWatchonly || (::IsMine(*pwalletMain, s.first) & MINE_WATCH_ONLY))
1099 entry.push_back(Pair("involvesWatchonly", true));
1100 MaybePushAddress(entry, s.first);
1102 if (wtx.GetDepthInMainChain() < 0) {
1103 entry.push_back(Pair("category", "conflicted"));
1105 entry.push_back(Pair("category", "send"));
1108 entry.push_back(Pair("amount", ValueFromAmount(-s.second)));
1109 entry.push_back(Pair("fee", ValueFromAmount(-nFee)));
1111 WalletTxToJSON(wtx, entry);
1112 ret.push_back(entry);
1117 if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth)
1119 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& r, listReceived)
1122 if (pwalletMain->mapAddressBook.count(r.first))
1123 account = pwalletMain->mapAddressBook[r.first];
1124 if (fAllAccounts || (account == strAccount))
1127 entry.push_back(Pair("account", account));
1128 if(involvesWatchonly || (::IsMine(*pwalletMain, r.first) & MINE_WATCH_ONLY))
1129 entry.push_back(Pair("involvesWatchonly", true));
1130 MaybePushAddress(entry, r.first);
1131 if (wtx.IsCoinBase())
1133 if (wtx.GetDepthInMainChain() < 1)
1134 entry.push_back(Pair("category", "orphan"));
1135 else if (wtx.GetBlocksToMaturity() > 0)
1136 entry.push_back(Pair("category", "immature"));
1138 entry.push_back(Pair("category", "generate"));
1141 entry.push_back(Pair("category", "receive"));
1142 entry.push_back(Pair("amount", ValueFromAmount(r.second)));
1144 WalletTxToJSON(wtx, entry);
1145 ret.push_back(entry);
1151 void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Array& ret)
1153 bool fAllAccounts = (strAccount == string("*"));
1155 if (fAllAccounts || acentry.strAccount == strAccount)
1158 entry.push_back(Pair("account", acentry.strAccount));
1159 entry.push_back(Pair("category", "move"));
1160 entry.push_back(Pair("time", (int64_t)acentry.nTime));
1161 entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit)));
1162 entry.push_back(Pair("otheraccount", acentry.strOtherAccount));
1163 entry.push_back(Pair("comment", acentry.strComment));
1164 ret.push_back(entry);
1168 Value listtransactions(const Array& params, bool fHelp)
1170 if (fHelp || params.size() > 3)
1171 throw runtime_error(
1172 "listtransactions [account] [count=10] [from=0]\n"
1173 "Returns up to [count] most recent transactions skipping the first [from] transactions for account [account].");
1175 string strAccount = "*";
1176 if (params.size() > 0)
1177 strAccount = params[0].get_str();
1179 if (params.size() > 1)
1180 nCount = params[1].get_int();
1182 if (params.size() > 2)
1183 nFrom = params[2].get_int();
1185 isminefilter filter = MINE_SPENDABLE;
1186 if(params.size() > 3)
1187 if(params[3].get_bool())
1188 filter = filter | MINE_WATCH_ONLY;
1191 throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count");
1193 throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative from");
1197 std::list<CAccountingEntry> acentries;
1198 CWallet::TxItems txOrdered = pwalletMain->OrderedTxItems(acentries, strAccount);
1200 // iterate backwards until we have nCount items to return:
1201 for (CWallet::TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
1203 CWalletTx *const pwtx = (*it).second.first;
1205 ListTransactions(*pwtx, strAccount, 0, true, ret, filter);
1206 CAccountingEntry *const pacentry = (*it).second.second;
1208 AcentryToJSON(*pacentry, strAccount, ret);
1210 if ((int)ret.size() >= (nCount+nFrom)) break;
1212 // ret is newest to oldest
1214 if (nFrom > (int)ret.size())
1216 if ((nFrom + nCount) > (int)ret.size())
1217 nCount = ret.size() - nFrom;
1218 Array::iterator first = ret.begin();
1219 std::advance(first, nFrom);
1220 Array::iterator last = ret.begin();
1221 std::advance(last, nFrom+nCount);
1223 if (last != ret.end()) ret.erase(last, ret.end());
1224 if (first != ret.begin()) ret.erase(ret.begin(), first);
1226 std::reverse(ret.begin(), ret.end()); // Return oldest to newest
1231 Value listaccounts(const Array& params, bool fHelp)
1233 if (fHelp || params.size() > 1)
1234 throw runtime_error(
1235 "listaccounts [minconf=1]\n"
1236 "Returns Object that has account names as keys, account balances as values.");
1239 if (params.size() > 0)
1240 nMinDepth = params[0].get_int();
1242 isminefilter includeWatchonly = MINE_SPENDABLE;
1243 if(params.size() > 1)
1244 if(params[1].get_bool())
1245 includeWatchonly = includeWatchonly | MINE_WATCH_ONLY;
1248 map<string, int64_t> mapAccountBalances;
1249 BOOST_FOREACH(const PAIRTYPE(CTxDestination, string)& entry, pwalletMain->mapAddressBook) {
1250 if (IsMine(*pwalletMain, entry.first)) // This address belongs to me
1251 mapAccountBalances[entry.second] = 0;
1254 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
1256 const CWalletTx& wtx = (*it).second;
1257 int64_t nGeneratedImmature, nGeneratedMature, nFee;
1258 string strSentAccount;
1259 list<pair<CTxDestination, int64_t> > listReceived;
1260 list<pair<CTxDestination, int64_t> > listSent;
1261 wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount, includeWatchonly);
1262 mapAccountBalances[strSentAccount] -= nFee;
1263 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& s, listSent)
1264 mapAccountBalances[strSentAccount] -= s.second;
1265 if (wtx.GetDepthInMainChain() >= nMinDepth)
1267 mapAccountBalances[""] += nGeneratedMature;
1268 BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& r, listReceived)
1269 if (pwalletMain->mapAddressBook.count(r.first))
1270 mapAccountBalances[pwalletMain->mapAddressBook[r.first]] += r.second;
1272 mapAccountBalances[""] += r.second;
1276 list<CAccountingEntry> acentries;
1277 CWalletDB(pwalletMain->strWalletFile).ListAccountCreditDebit("*", acentries);
1278 BOOST_FOREACH(const CAccountingEntry& entry, acentries)
1279 mapAccountBalances[entry.strAccount] += entry.nCreditDebit;
1282 BOOST_FOREACH(const PAIRTYPE(string, int64_t)& accountBalance, mapAccountBalances) {
1283 ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second)));
1288 Value listsinceblock(const Array& params, bool fHelp)
1291 throw runtime_error(
1292 "listsinceblock [blockhash] [target-confirmations]\n"
1293 "Get all transactions in blocks since block [blockhash], or all transactions if omitted");
1295 CBlockIndex *pindex = NULL;
1296 int target_confirms = 1;
1297 isminefilter filter = MINE_SPENDABLE;
1299 if (params.size() > 0)
1301 uint256 blockId = 0;
1303 blockId.SetHex(params[0].get_str());
1304 pindex = CBlockLocator(blockId).GetBlockIndex();
1307 if (params.size() > 1)
1309 target_confirms = params[1].get_int();
1311 if (target_confirms < 1)
1312 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter");
1315 if(params.size() > 2)
1316 if(params[2].get_bool())
1317 filter = filter | MINE_WATCH_ONLY;
1319 int depth = pindex ? (1 + nBestHeight - pindex->nHeight) : -1;
1323 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); it++)
1325 CWalletTx tx = (*it).second;
1327 if (depth == -1 || tx.GetDepthInMainChain() < depth)
1328 ListTransactions(tx, "*", 0, true, transactions, filter);
1333 if (target_confirms == 1)
1335 lastblock = hashBestChain;
1339 int target_height = pindexBest->nHeight + 1 - target_confirms;
1342 for (block = pindexBest;
1343 block && block->nHeight > target_height;
1344 block = block->pprev) { }
1346 lastblock = block ? block->GetBlockHash() : 0;
1350 ret.push_back(Pair("transactions", transactions));
1351 ret.push_back(Pair("lastblock", lastblock.GetHex()));
1356 Value gettransaction(const Array& params, bool fHelp)
1358 if (fHelp || params.size() != 1)
1359 throw runtime_error(
1360 "gettransaction <txid>\n"
1361 "Get detailed information about <txid>");
1364 hash.SetHex(params[0].get_str());
1366 isminefilter filter = MINE_SPENDABLE;
1367 if(params.size() > 1)
1368 if(params[1].get_bool())
1369 filter = filter | MINE_WATCH_ONLY;
1373 if (pwalletMain->mapWallet.count(hash))
1375 const CWalletTx& wtx = pwalletMain->mapWallet[hash];
1377 TxToJSON(wtx, 0, entry);
1379 int64_t nCredit = wtx.GetCredit(filter);
1380 int64_t nDebit = wtx.GetDebit(filter);
1381 int64_t nNet = nCredit - nDebit;
1382 int64_t nFee = (wtx.IsFromMe(filter) ? wtx.GetValueOut() - nDebit : 0);
1384 entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee)));
1385 if (wtx.IsFromMe(filter))
1386 entry.push_back(Pair("fee", ValueFromAmount(nFee)));
1388 WalletTxToJSON(wtx, entry);
1391 ListTransactions(pwalletMain->mapWallet[hash], "*", 0, false, details, filter);
1392 entry.push_back(Pair("details", details));
1397 uint256 hashBlock = 0;
1398 if (GetTransaction(hash, tx, hashBlock))
1400 TxToJSON(tx, 0, entry);
1402 entry.push_back(Pair("confirmations", 0));
1405 entry.push_back(Pair("blockhash", hashBlock.GetHex()));
1406 map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashBlock);
1407 if (mi != mapBlockIndex.end() && (*mi).second)
1409 CBlockIndex* pindex = (*mi).second;
1410 if (pindex->IsInMainChain())
1411 entry.push_back(Pair("confirmations", 1 + nBestHeight - pindex->nHeight));
1413 entry.push_back(Pair("confirmations", 0));
1418 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction");
1425 Value backupwallet(const Array& params, bool fHelp)
1427 if (fHelp || params.size() != 1)
1428 throw runtime_error(
1429 "backupwallet <destination>\n"
1430 "Safely copies wallet.dat to destination, which can be a directory or a path with filename.");
1432 string strDest = params[0].get_str();
1433 if (!BackupWallet(*pwalletMain, strDest))
1434 throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet backup failed!");
1440 Value keypoolrefill(const Array& params, bool fHelp)
1442 if (fHelp || params.size() > 1)
1443 throw runtime_error(
1444 "keypoolrefill [new-size]\n"
1445 "Fills the keypool.\n"
1446 "IMPORTANT: Any previous backups you have made of your wallet file "
1447 "should be replaced with the newly generated one."
1448 + HelpRequiringPassphrase());
1450 unsigned int nSize = max<unsigned int>(GetArgUInt("-keypool", 100), 0);
1451 if (params.size() > 0) {
1452 if (params[0].get_int() < 0)
1453 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected valid size");
1454 nSize = (unsigned int) params[0].get_int();
1457 EnsureWalletIsUnlocked();
1459 pwalletMain->TopUpKeyPool(nSize);
1461 if (pwalletMain->GetKeyPoolSize() < nSize)
1462 throw JSONRPCError(RPC_WALLET_ERROR, "Error refreshing keypool.");
1467 Value keypoolreset(const Array& params, bool fHelp)
1469 if (fHelp || params.size() > 1)
1470 throw runtime_error(
1471 "keypoolreset [new-size]\n"
1472 "Resets the keypool.\n"
1473 "IMPORTANT: Any previous backups you have made of your wallet file "
1474 "should be replaced with the newly generated one."
1475 + HelpRequiringPassphrase());
1477 unsigned int nSize = max<unsigned int>(GetArgUInt("-keypool", 100), 0);
1478 if (params.size() > 0) {
1479 if (params[0].get_int() < 0)
1480 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected valid size");
1481 nSize = (unsigned int) params[0].get_int();
1484 EnsureWalletIsUnlocked();
1486 pwalletMain->NewKeyPool(nSize);
1488 if (pwalletMain->GetKeyPoolSize() < nSize)
1489 throw JSONRPCError(RPC_WALLET_ERROR, "Error refreshing keypool.");
1495 void ThreadTopUpKeyPool(void* parg)
1497 // Make this thread recognisable as the key-topping-up thread
1498 RenameThread("novacoin-key-top");
1500 pwalletMain->TopUpKeyPool();
1503 void ThreadCleanWalletPassphrase(void* parg)
1505 // Make this thread recognisable as the wallet relocking thread
1506 RenameThread("novacoin-lock-wa");
1508 int64_t nMyWakeTime = GetTimeMillis() + *((int64_t*)parg) * 1000;
1510 ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime);
1512 if (nWalletUnlockTime == 0)
1514 nWalletUnlockTime = nMyWakeTime;
1518 if (nWalletUnlockTime==0)
1520 int64_t nToSleep = nWalletUnlockTime - GetTimeMillis();
1524 LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime);
1526 ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime);
1530 if (nWalletUnlockTime)
1532 nWalletUnlockTime = 0;
1533 pwalletMain->Lock();
1538 if (nWalletUnlockTime < nMyWakeTime)
1539 nWalletUnlockTime = nMyWakeTime;
1542 LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime);
1544 delete (int64_t*)parg;
1547 Value walletpassphrase(const Array& params, bool fHelp)
1549 if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 3))
1550 throw runtime_error(
1551 "walletpassphrase <passphrase> <timeout> [mintonly]\n"
1552 "Stores the wallet decryption key in memory for <timeout> seconds.\n"
1553 "mintonly is optional true/false allowing only block minting.");
1556 if (!pwalletMain->IsCrypted())
1557 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called.");
1559 if (!pwalletMain->IsLocked())
1560 throw JSONRPCError(RPC_WALLET_ALREADY_UNLOCKED, "Error: Wallet is already unlocked, use walletlock first if need to change unlock settings.");
1561 // Note that the walletpassphrase is stored in params[0] which is not mlock()ed
1562 SecureString strWalletPass;
1563 strWalletPass.reserve(100);
1564 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1565 // Alternately, find a way to make params[0] mlock()'d to begin with.
1566 strWalletPass = params[0].get_str().c_str();
1568 if (strWalletPass.length() > 0)
1570 if (!pwalletMain->Unlock(strWalletPass))
1571 throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
1574 throw runtime_error(
1575 "walletpassphrase <passphrase> <timeout>\n"
1576 "Stores the wallet decryption key in memory for <timeout> seconds.");
1578 NewThread(ThreadTopUpKeyPool, NULL);
1579 int64_t* pnSleepTime = new int64_t(params[1].get_int64());
1580 NewThread(ThreadCleanWalletPassphrase, pnSleepTime);
1582 // ppcoin: if user OS account compromised prevent trivial sendmoney commands
1583 if (params.size() > 2)
1584 fWalletUnlockMintOnly = params[2].get_bool();
1586 fWalletUnlockMintOnly = false;
1592 Value walletpassphrasechange(const Array& params, bool fHelp)
1594 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2))
1595 throw runtime_error(
1596 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1597 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1600 if (!pwalletMain->IsCrypted())
1601 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrasechange was called.");
1603 // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string)
1604 // Alternately, find a way to make params[0] mlock()'d to begin with.
1605 SecureString strOldWalletPass;
1606 strOldWalletPass.reserve(100);
1607 strOldWalletPass = params[0].get_str().c_str();
1609 SecureString strNewWalletPass;
1610 strNewWalletPass.reserve(100);
1611 strNewWalletPass = params[1].get_str().c_str();
1613 if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1)
1614 throw runtime_error(
1615 "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
1616 "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
1618 if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass))
1619 throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
1625 Value walletlock(const Array& params, bool fHelp)
1627 if (pwalletMain->IsCrypted() && (fHelp || params.size() != 0))
1628 throw runtime_error(
1630 "Removes the wallet encryption key from memory, locking the wallet.\n"
1631 "After calling this method, you will need to call walletpassphrase again\n"
1632 "before being able to call any methods which require the wallet to be unlocked.");
1635 if (!pwalletMain->IsCrypted())
1636 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletlock was called.");
1639 LOCK(cs_nWalletUnlockTime);
1640 pwalletMain->Lock();
1641 nWalletUnlockTime = 0;
1648 Value encryptwallet(const Array& params, bool fHelp)
1650 if (!pwalletMain->IsCrypted() && (fHelp || params.size() != 1))
1651 throw runtime_error(
1652 "encryptwallet <passphrase>\n"
1653 "Encrypts the wallet with <passphrase>.");
1656 if (pwalletMain->IsCrypted())
1657 throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an encrypted wallet, but encryptwallet was called.");
1659 // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1660 // Alternately, find a way to make params[0] mlock()'d to begin with.
1661 SecureString strWalletPass;
1662 strWalletPass.reserve(100);
1663 strWalletPass = params[0].get_str().c_str();
1665 if (strWalletPass.length() < 1)
1666 throw runtime_error(
1667 "encryptwallet <passphrase>\n"
1668 "Encrypts the wallet with <passphrase>.");
1670 if (!pwalletMain->EncryptWallet(strWalletPass))
1671 throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: Failed to encrypt the wallet.");
1673 // BDB seems to have a bad habit of writing old data into
1674 // slack space in .dat files; that is bad if the old data is
1675 // unencrypted private keys. So:
1677 return "wallet encrypted; NovaCoin server stopping, restart to run with encrypted wallet. The keypool has been flushed, you need to make a new backup.";
1680 class DescribeAddressVisitor : public boost::static_visitor<Object>
1685 DescribeAddressVisitor(isminetype mineIn) : mine(mineIn) {}
1687 Object operator()(const CNoDestination &dest) const { return Object(); }
1688 Object operator()(const CKeyID &keyID) const {
1691 pwalletMain->GetPubKey(keyID, vchPubKey);
1692 obj.push_back(Pair("isscript", false));
1693 if (mine == MINE_SPENDABLE) {
1694 pwalletMain->GetPubKey(keyID, vchPubKey);
1695 obj.push_back(Pair("pubkey", HexStr(vchPubKey.Raw())));
1696 obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed()));
1701 Object operator()(const CScriptID &scriptID) const {
1703 obj.push_back(Pair("isscript", true));
1704 if (mine == MINE_SPENDABLE) {
1706 pwalletMain->GetCScript(scriptID, subscript);
1707 std::vector<CTxDestination> addresses;
1708 txnouttype whichType;
1710 ExtractDestinations(subscript, whichType, addresses, nRequired);
1711 obj.push_back(Pair("script", GetTxnOutputType(whichType)));
1712 obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end())));
1714 BOOST_FOREACH(const CTxDestination& addr, addresses)
1715 a.push_back(CBitcoinAddress(addr).ToString());
1716 obj.push_back(Pair("addresses", a));
1717 if (whichType == TX_MULTISIG)
1718 obj.push_back(Pair("sigsrequired", nRequired));
1724 Value validateaddress(const Array& params, bool fHelp)
1726 if (fHelp || params.size() != 1)
1727 throw runtime_error(
1728 "validateaddress <novacoinaddress>\n"
1729 "Return information about <novacoinaddress>.");
1731 CBitcoinAddress address(params[0].get_str());
1732 bool isValid = address.IsValid();
1735 ret.push_back(Pair("isvalid", isValid));
1738 if (address.IsPair())
1740 CMalleablePubKey mpk;
1741 mpk.setvch(address.GetData());
1742 ret.push_back(Pair("ispair", true));
1744 CMalleableKeyView view;
1745 bool isMine = pwalletMain->GetMalleableView(mpk, view);
1746 ret.push_back(Pair("ismine", isMine));
1749 ret.push_back(Pair("KeyView", view.ToString()));
1753 CTxDestination dest = address.Get();
1754 string currentAddress = address.ToString();
1755 ret.push_back(Pair("address", currentAddress));
1756 isminetype mine = pwalletMain ? IsMine(*pwalletMain, dest) : MINE_NO;
1757 ret.push_back(Pair("ismine", mine != MINE_NO));
1758 if (mine != MINE_NO) {
1759 ret.push_back(Pair("watchonly", mine == MINE_WATCH_ONLY));
1760 Object detail = boost::apply_visitor(DescribeAddressVisitor(mine), dest);
1761 ret.insert(ret.end(), detail.begin(), detail.end());
1763 if (pwalletMain->mapAddressBook.count(dest))
1764 ret.push_back(Pair("account", pwalletMain->mapAddressBook[dest]));
1770 // ppcoin: reserve balance from being staked for network protection
1771 Value reservebalance(const Array& params, bool fHelp)
1773 if (fHelp || params.size() > 2)
1774 throw runtime_error(
1775 "reservebalance [<reserve> [amount]]\n"
1776 "<reserve> is true or false to turn balance reserve on or off.\n"
1777 "<amount> is a real and rounded to cent.\n"
1778 "Set reserve amount not participating in network protection.\n"
1779 "If no parameters provided current setting is printed.\n");
1781 if (params.size() > 0)
1783 bool fReserve = params[0].get_bool();
1786 if (params.size() == 1)
1787 throw runtime_error("must provide amount to reserve balance.\n");
1788 int64_t nAmount = AmountFromValue(params[1]);
1789 nAmount = (nAmount / CENT) * CENT; // round to cent
1791 throw runtime_error("amount cannot be negative.\n");
1792 mapArgs["-reservebalance"] = FormatMoney(nAmount);
1796 if (params.size() > 1)
1797 throw runtime_error("cannot specify amount to turn off reserve.\n");
1798 mapArgs["-reservebalance"] = "0";
1803 if (mapArgs.count("-reservebalance") && !ParseMoney(mapArgs["-reservebalance"], nReserveBalance))
1804 throw runtime_error("invalid reserve balance amount\n");
1805 result.push_back(Pair("reserve", (nReserveBalance > 0)));
1806 result.push_back(Pair("amount", ValueFromAmount(nReserveBalance)));
1811 // ppcoin: check wallet integrity
1812 Value checkwallet(const Array& params, bool fHelp)
1814 if (fHelp || params.size() > 0)
1815 throw runtime_error(
1817 "Check wallet for integrity.\n");
1820 int64_t nBalanceInQuestion;
1821 pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion, true);
1823 if (nMismatchSpent == 0)
1824 result.push_back(Pair("wallet check passed", true));
1827 result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1828 result.push_back(Pair("amount in question", ValueFromAmount(nBalanceInQuestion)));
1834 // ppcoin: repair wallet
1835 Value repairwallet(const Array& params, bool fHelp)
1837 if (fHelp || params.size() > 0)
1838 throw runtime_error(
1840 "Repair wallet if checkwallet reports any problem.\n");
1843 int64_t nBalanceInQuestion;
1844 pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion);
1846 if (nMismatchSpent == 0)
1847 result.push_back(Pair("wallet check passed", true));
1850 result.push_back(Pair("mismatched spent coins", nMismatchSpent));
1851 result.push_back(Pair("amount affected by repair", ValueFromAmount(nBalanceInQuestion)));
1856 // NovaCoin: resend unconfirmed wallet transactions
1857 Value resendtx(const Array& params, bool fHelp)
1859 if (fHelp || params.size() > 1)
1860 throw runtime_error(
1862 "Re-send unconfirmed transactions.\n"
1865 ResendWalletTransactions();
1870 // Make a public-private key pair
1871 Value makekeypair(const Array& params, bool fHelp)
1873 if (fHelp || params.size() > 0)
1874 throw runtime_error(
1876 "Make a public/private key pair.\n");
1878 string strPrefix = "";
1879 if (params.size() > 0)
1880 strPrefix = params[0].get_str();
1883 key.MakeNewKey(true);
1885 CPrivKey vchPrivKey = key.GetPrivKey();
1887 result.push_back(Pair("PrivateKey", HexStr<CPrivKey::iterator>(vchPrivKey.begin(), vchPrivKey.end())));
1890 CSecret vchSecret = key.GetSecret(fCompressed);
1891 result.push_back(Pair("Secret", HexStr<CSecret::iterator>(vchSecret.begin(), vchSecret.end())));
1892 result.push_back(Pair("PublicKey", HexStr(key.GetPubKey().Raw())));
1896 Value newmalleablekey(const Array& params, bool fHelp)
1898 if (fHelp || params.size() > 0)
1899 throw runtime_error(
1901 "Make a malleable public/private key pair.\n");
1903 if (!(fDebug || fTestNet) && GetTime() < SMALLDATA_SWITCH_TIME)
1904 throw runtime_error("This feature has been disabled for mainNet clients");
1906 CMalleableKeyView keyView = pwalletMain->GenerateNewMalleableKey();
1909 if (!pwalletMain->GetMalleableKey(keyView, mKey))
1910 throw runtime_error("Unable to generate new malleable key");
1912 CMalleablePubKey mPubKey = mKey.GetMalleablePubKey();
1915 result.push_back(Pair("PublicPair", mPubKey.ToString()));
1916 result.push_back(Pair("PublicBytes", HexStr(mPubKey.Raw())));
1917 result.push_back(Pair("Address", CBitcoinAddress(mPubKey).ToString()));
1918 result.push_back(Pair("KeyView", keyView.ToString()));
1923 Value adjustmalleablekey(const Array& params, bool fHelp)
1925 if (fHelp || params.size() != 3)
1926 throw runtime_error(
1927 "adjustmalleablekey <Malleable key data> <Public key variant data> <R data>\n"
1928 "Calculate new private key using provided malleable key, public key and R data.\n");
1930 CMalleableKey malleableKey;
1931 malleableKey.SetString(params[0].get_str());
1933 CKey privKeyVariant;
1934 CPubKey vchPubKeyVariant = CPubKey(ParseHex(params[1].get_str()));
1936 CPubKey R(ParseHex(params[2].get_str()));
1938 if (!malleableKey.CheckKeyVariant(R,vchPubKeyVariant, privKeyVariant)) {
1939 throw runtime_error("Unable to calculate the private key");
1944 CSecret vchPrivKeyVariant = privKeyVariant.GetSecret(fCompressed);
1946 result.push_back(Pair("PrivateKey", CBitcoinSecret(vchPrivKeyVariant, fCompressed).ToString()));
1951 Value adjustmalleablepubkey(const Array& params, bool fHelp)
1953 if (fHelp || params.size() > 2 || params.size() == 0)
1954 throw runtime_error(
1955 "adjustmalleablepubkey <Malleable public key data>\n"
1956 "Calculate new public key using provided malleable public key data.\n");
1958 string pubKeyPair = params[0].get_str();
1959 CMalleablePubKey malleablePubKey;
1961 if (pubKeyPair.size() == 136) {
1962 malleablePubKey.setvch(ParseHex(pubKeyPair));
1964 malleablePubKey.SetString(pubKeyPair);
1966 CPubKey R, vchPubKeyVariant;
1967 malleablePubKey.GetVariant(R, vchPubKeyVariant);
1970 result.push_back(Pair("R", HexStr(R.Raw())));
1971 result.push_back(Pair("PubkeyVariant", HexStr(vchPubKeyVariant.Raw())));
1972 result.push_back(Pair("KeyVariantID", CBitcoinAddress(vchPubKeyVariant.GetID()).ToString()));
1977 Value listmalleableviews(const Array& params, bool fHelp)
1979 if (fHelp || params.size() != 0)
1980 throw runtime_error(
1981 "listmalleableviews\n"
1982 "Get list of views for generated malleable keys.\n");
1984 std::list<CMalleableKeyView> keyViewList;
1985 pwalletMain->ListMalleableViews(keyViewList);
1988 BOOST_FOREACH(const CMalleableKeyView &keyView, keyViewList)
1990 result.push_back(keyView.ToString());