diff options
Diffstat (limited to 'src/wallet/rpcwallet.cpp')
-rw-r--r-- | src/wallet/rpcwallet.cpp | 515 |
1 files changed, 406 insertions, 109 deletions
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 29f3eda15d..8d88933878 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -21,11 +21,9 @@ #include <boost/assign/list_of.hpp> -#include "json/json_spirit_utils.h" -#include "json/json_spirit_value.h" +#include "univalue/univalue.h" using namespace std; -using namespace json_spirit; int64_t nWalletUnlockTime; static CCriticalSection cs_nWalletUnlockTime; @@ -37,13 +35,25 @@ std::string HelpRequiringPassphrase() : ""; } +bool EnsureWalletIsAvailable(bool avoidException) +{ + if (!pwalletMain) + { + if (!avoidException) + throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found (disabled)"); + else + return false; + } + return true; +} + void EnsureWalletIsUnlocked() { if (pwalletMain->IsLocked()) throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first."); } -void WalletTxToJSON(const CWalletTx& wtx, Object& entry) +void WalletTxToJSON(const CWalletTx& wtx, UniValue& entry) { int confirms = wtx.GetDepthInMainChain(); entry.push_back(Pair("confirmations", confirms)); @@ -57,7 +67,7 @@ void WalletTxToJSON(const CWalletTx& wtx, Object& entry) } uint256 hash = wtx.GetHash(); entry.push_back(Pair("txid", hash.GetHex())); - Array conflicts; + UniValue conflicts(UniValue::VARR); BOOST_FOREACH(const uint256& conflict, wtx.GetConflicts()) conflicts.push_back(conflict.GetHex()); entry.push_back(Pair("walletconflicts", conflicts)); @@ -67,7 +77,7 @@ void WalletTxToJSON(const CWalletTx& wtx, Object& entry) entry.push_back(Pair(item.first, item.second)); } -string AccountFromValue(const Value& value) +string AccountFromValue(const UniValue& value) { string strAccount = value.get_str(); if (strAccount == "*") @@ -75,8 +85,11 @@ string AccountFromValue(const Value& value) return strAccount; } -Value getnewaddress(const Array& params, bool fHelp) +UniValue getnewaddress(const UniValue& params, bool fHelp) { + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + if (fHelp || params.size() > 1) throw runtime_error( "getnewaddress ( \"account\" )\n" @@ -151,8 +164,11 @@ CBitcoinAddress GetAccountAddress(string strAccount, bool bForceNew=false) return CBitcoinAddress(account.vchPubKey.GetID()); } -Value getaccountaddress(const Array& params, bool fHelp) +UniValue getaccountaddress(const UniValue& params, bool fHelp) { + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + if (fHelp || params.size() != 1) throw runtime_error( "getaccountaddress \"account\"\n" @@ -173,15 +189,18 @@ Value getaccountaddress(const Array& params, bool fHelp) // Parse the account first so we don't generate a key if there's an error string strAccount = AccountFromValue(params[0]); - Value ret; + UniValue ret(UniValue::VSTR); ret = GetAccountAddress(strAccount).ToString(); return ret; } -Value getrawchangeaddress(const Array& params, bool fHelp) +UniValue getrawchangeaddress(const UniValue& params, bool fHelp) { + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + if (fHelp || params.size() > 1) throw runtime_error( "getrawchangeaddress\n" @@ -212,8 +231,11 @@ Value getrawchangeaddress(const Array& params, bool fHelp) } -Value setaccount(const Array& params, bool fHelp) +UniValue setaccount(const UniValue& params, bool fHelp) { + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + if (fHelp || params.size() < 1 || params.size() > 2) throw runtime_error( "setaccount \"bitcoinaddress\" \"account\"\n" @@ -251,12 +273,15 @@ Value setaccount(const Array& params, bool fHelp) else throw JSONRPCError(RPC_MISC_ERROR, "setaccount can only be used with own address"); - return Value::null; + return NullUniValue; } -Value getaccount(const Array& params, bool fHelp) +UniValue getaccount(const UniValue& params, bool fHelp) { + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + if (fHelp || params.size() != 1) throw runtime_error( "getaccount \"bitcoinaddress\"\n" @@ -284,8 +309,11 @@ Value getaccount(const Array& params, bool fHelp) } -Value getaddressesbyaccount(const Array& params, bool fHelp) +UniValue getaddressesbyaccount(const UniValue& params, bool fHelp) { + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + if (fHelp || params.size() != 1) throw runtime_error( "getaddressesbyaccount \"account\"\n" @@ -307,7 +335,7 @@ Value getaddressesbyaccount(const Array& params, bool fHelp) string strAccount = AccountFromValue(params[0]); // Find all addresses that have the given account - Array ret; + UniValue ret(UniValue::VARR); BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, CAddressBookData)& item, pwalletMain->mapAddressBook) { const CBitcoinAddress& address = item.first; @@ -349,8 +377,11 @@ static void SendMoney(const CTxDestination &address, CAmount nValue, bool fSubtr throw JSONRPCError(RPC_WALLET_ERROR, "Error: The transaction was rejected! This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here."); } -Value sendtoaddress(const Array& params, bool fHelp) +UniValue sendtoaddress(const UniValue& params, bool fHelp) { + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + if (fHelp || params.size() < 2 || params.size() > 5) throw runtime_error( "sendtoaddress \"bitcoinaddress\" amount ( \"comment\" \"comment-to\" subtractfeefromamount )\n" @@ -383,12 +414,14 @@ Value sendtoaddress(const Array& params, bool fHelp) // Amount CAmount nAmount = AmountFromValue(params[1]); + if (nAmount <= 0) + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); // Wallet comments CWalletTx wtx; - if (params.size() > 2 && params[2].type() != null_type && !params[2].get_str().empty()) + if (params.size() > 2 && !params[2].isNull() && !params[2].get_str().empty()) wtx.mapValue["comment"] = params[2].get_str(); - if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty()) + if (params.size() > 3 && !params[3].isNull() && !params[3].get_str().empty()) wtx.mapValue["to"] = params[3].get_str(); bool fSubtractFeeFromAmount = false; @@ -402,8 +435,11 @@ Value sendtoaddress(const Array& params, bool fHelp) return wtx.GetHash().GetHex(); } -Value listaddressgroupings(const Array& params, bool fHelp) +UniValue listaddressgroupings(const UniValue& params, bool fHelp) { + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + if (fHelp) throw runtime_error( "listaddressgroupings\n" @@ -429,14 +465,14 @@ Value listaddressgroupings(const Array& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); - Array jsonGroupings; + UniValue jsonGroupings(UniValue::VARR); map<CTxDestination, CAmount> balances = pwalletMain->GetAddressBalances(); BOOST_FOREACH(set<CTxDestination> grouping, pwalletMain->GetAddressGroupings()) { - Array jsonGrouping; + UniValue jsonGrouping(UniValue::VARR); BOOST_FOREACH(CTxDestination address, grouping) { - Array addressInfo; + UniValue addressInfo(UniValue::VARR); addressInfo.push_back(CBitcoinAddress(address).ToString()); addressInfo.push_back(ValueFromAmount(balances[address])); { @@ -451,8 +487,11 @@ Value listaddressgroupings(const Array& params, bool fHelp) return jsonGroupings; } -Value signmessage(const Array& params, bool fHelp) +UniValue signmessage(const UniValue& params, bool fHelp) { + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + if (fHelp || params.size() != 2) throw runtime_error( "signmessage \"bitcoinaddress\" \"message\"\n" @@ -504,8 +543,11 @@ Value signmessage(const Array& params, bool fHelp) return EncodeBase64(&vchSig[0], vchSig.size()); } -Value getreceivedbyaddress(const Array& params, bool fHelp) +UniValue getreceivedbyaddress(const UniValue& params, bool fHelp) { + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + if (fHelp || params.size() < 1 || params.size() > 2) throw runtime_error( "getreceivedbyaddress \"bitcoinaddress\" ( minconf )\n" @@ -546,7 +588,7 @@ Value getreceivedbyaddress(const Array& params, bool fHelp) for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { const CWalletTx& wtx = (*it).second; - if (wtx.IsCoinBase() || !IsFinalTx(wtx)) + if (wtx.IsCoinBase() || !CheckFinalTx(wtx)) continue; BOOST_FOREACH(const CTxOut& txout, wtx.vout) @@ -559,8 +601,11 @@ Value getreceivedbyaddress(const Array& params, bool fHelp) } -Value getreceivedbyaccount(const Array& params, bool fHelp) +UniValue getreceivedbyaccount(const UniValue& params, bool fHelp) { + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + if (fHelp || params.size() < 1 || params.size() > 2) throw runtime_error( "getreceivedbyaccount \"account\" ( minconf )\n" @@ -597,7 +642,7 @@ Value getreceivedbyaccount(const Array& params, bool fHelp) for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { const CWalletTx& wtx = (*it).second; - if (wtx.IsCoinBase() || !IsFinalTx(wtx)) + if (wtx.IsCoinBase() || !CheckFinalTx(wtx)) continue; BOOST_FOREACH(const CTxOut& txout, wtx.vout) @@ -621,7 +666,7 @@ CAmount GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMi for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { const CWalletTx& wtx = (*it).second; - if (!IsFinalTx(wtx) || wtx.GetBlocksToMaturity() > 0 || wtx.GetDepthInMainChain() < 0) + if (!CheckFinalTx(wtx) || wtx.GetBlocksToMaturity() > 0 || wtx.GetDepthInMainChain() < 0) continue; CAmount nReceived, nSent, nFee; @@ -645,8 +690,11 @@ CAmount GetAccountBalance(const string& strAccount, int nMinDepth, const isminef } -Value getbalance(const Array& params, bool fHelp) +UniValue getbalance(const UniValue& params, bool fHelp) { + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + if (fHelp || params.size() > 3) throw runtime_error( "getbalance ( \"account\" minconf includeWatchonly )\n" @@ -685,12 +733,12 @@ Value getbalance(const Array& params, bool fHelp) if (params[0].get_str() == "*") { // Calculate total balance a different way from GetBalance() // (GetBalance() sums up all unspent TxOuts) - // getbalance and getbalance '*' 0 should return the same number + // getbalance and "getbalance * 1 true" should return the same number CAmount nBalance = 0; for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { const CWalletTx& wtx = (*it).second; - if (!wtx.IsTrusted() || wtx.GetBlocksToMaturity() > 0) + if (!CheckFinalTx(wtx) || wtx.GetBlocksToMaturity() > 0 || wtx.GetDepthInMainChain() < 0) continue; CAmount allFee; @@ -717,8 +765,11 @@ Value getbalance(const Array& params, bool fHelp) return ValueFromAmount(nBalance); } -Value getunconfirmedbalance(const Array ¶ms, bool fHelp) +UniValue getunconfirmedbalance(const UniValue ¶ms, bool fHelp) { + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + if (fHelp || params.size() > 0) throw runtime_error( "getunconfirmedbalance\n" @@ -730,8 +781,11 @@ Value getunconfirmedbalance(const Array ¶ms, bool fHelp) } -Value movecmd(const Array& params, bool fHelp) +UniValue movecmd(const UniValue& params, bool fHelp) { + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + if (fHelp || params.size() < 3 || params.size() > 5) throw runtime_error( "move \"fromaccount\" \"toaccount\" amount ( minconf \"comment\" )\n" @@ -757,6 +811,8 @@ Value movecmd(const Array& params, bool fHelp) string strFrom = AccountFromValue(params[0]); string strTo = AccountFromValue(params[1]); CAmount nAmount = AmountFromValue(params[2]); + if (nAmount <= 0) + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); if (params.size() > 3) // unused parameter, used to be nMinDepth, keep type-checking it though (void)params[3].get_int(); @@ -797,8 +853,11 @@ Value movecmd(const Array& params, bool fHelp) } -Value sendfrom(const Array& params, bool fHelp) +UniValue sendfrom(const UniValue& params, bool fHelp) { + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + if (fHelp || params.size() < 3 || params.size() > 6) throw runtime_error( "sendfrom \"fromaccount\" \"tobitcoinaddress\" amount ( minconf \"comment\" \"comment-to\" )\n" @@ -833,15 +892,17 @@ Value sendfrom(const Array& params, bool fHelp) if (!address.IsValid()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); CAmount nAmount = AmountFromValue(params[2]); + if (nAmount <= 0) + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); int nMinDepth = 1; if (params.size() > 3) nMinDepth = params[3].get_int(); CWalletTx wtx; wtx.strFromAccount = strAccount; - if (params.size() > 4 && params[4].type() != null_type && !params[4].get_str().empty()) + if (params.size() > 4 && !params[4].isNull() && !params[4].get_str().empty()) wtx.mapValue["comment"] = params[4].get_str(); - if (params.size() > 5 && params[5].type() != null_type && !params[5].get_str().empty()) + if (params.size() > 5 && !params[5].isNull() && !params[5].get_str().empty()) wtx.mapValue["to"] = params[5].get_str(); EnsureWalletIsUnlocked(); @@ -857,8 +918,11 @@ Value sendfrom(const Array& params, bool fHelp) } -Value sendmany(const Array& params, bool fHelp) +UniValue sendmany(const UniValue& params, bool fHelp) { + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + if (fHelp || params.size() < 2 || params.size() > 5) throw runtime_error( "sendmany \"fromaccount\" {\"address\":amount,...} ( minconf \"comment\" [\"address\",...] )\n" @@ -898,17 +962,17 @@ Value sendmany(const Array& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); string strAccount = AccountFromValue(params[0]); - Object sendTo = params[1].get_obj(); + UniValue sendTo = params[1].get_obj(); int nMinDepth = 1; if (params.size() > 2) nMinDepth = params[2].get_int(); CWalletTx wtx; wtx.strFromAccount = strAccount; - if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty()) + if (params.size() > 3 && !params[3].isNull() && !params[3].get_str().empty()) wtx.mapValue["comment"] = params[3].get_str(); - Array subtractFeeFromAmount; + UniValue subtractFeeFromAmount(UniValue::VARR); if (params.size() > 4) subtractFeeFromAmount = params[4].get_array(); @@ -916,24 +980,29 @@ Value sendmany(const Array& params, bool fHelp) vector<CRecipient> vecSend; CAmount totalAmount = 0; - BOOST_FOREACH(const Pair& s, sendTo) + vector<string> keys = sendTo.getKeys(); + BOOST_FOREACH(const string& name_, keys) { - CBitcoinAddress address(s.name_); + CBitcoinAddress address(name_); if (!address.IsValid()) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Bitcoin address: ")+s.name_); + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Bitcoin address: ")+name_); if (setAddress.count(address)) - throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+s.name_); + throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+name_); setAddress.insert(address); CScript scriptPubKey = GetScriptForDestination(address.Get()); - CAmount nAmount = AmountFromValue(s.value_); + CAmount nAmount = AmountFromValue(sendTo[name_]); + if (nAmount <= 0) + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); totalAmount += nAmount; bool fSubtractFeeFromAmount = false; - BOOST_FOREACH(const Value& addr, subtractFeeFromAmount) - if (addr.get_str() == s.name_) + for (unsigned int idx = 0; idx < subtractFeeFromAmount.size(); idx++) { + const UniValue& addr = subtractFeeFromAmount[idx]; + if (addr.get_str() == name_) fSubtractFeeFromAmount = true; + } CRecipient recipient = {scriptPubKey, nAmount, fSubtractFeeFromAmount}; vecSend.push_back(recipient); @@ -961,10 +1030,13 @@ Value sendmany(const Array& params, bool fHelp) } // Defined in rpcmisc.cpp -extern CScript _createmultisig_redeemScript(const Array& params); +extern CScript _createmultisig_redeemScript(const UniValue& params); -Value addmultisigaddress(const Array& params, bool fHelp) +UniValue addmultisigaddress(const UniValue& params, bool fHelp) { + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + if (fHelp || params.size() < 2 || params.size() > 3) { string msg = "addmultisigaddress nrequired [\"key\",...] ( \"account\" )\n" @@ -1023,7 +1095,7 @@ struct tallyitem } }; -Value ListReceived(const Array& params, bool fByAccounts) +UniValue ListReceived(const UniValue& params, bool fByAccounts) { // Minimum confirmations int nMinDepth = 1; @@ -1046,7 +1118,7 @@ Value ListReceived(const Array& params, bool fByAccounts) { const CWalletTx& wtx = (*it).second; - if (wtx.IsCoinBase() || !IsFinalTx(wtx)) + if (wtx.IsCoinBase() || !CheckFinalTx(wtx)) continue; int nDepth = wtx.GetDepthInMainChain(); @@ -1073,7 +1145,7 @@ Value ListReceived(const Array& params, bool fByAccounts) } // Reply - Array ret; + UniValue ret(UniValue::VARR); map<string, tallyitem> mapAccountTally; BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, CAddressBookData)& item, pwalletMain->mapAddressBook) { @@ -1102,14 +1174,14 @@ Value ListReceived(const Array& params, bool fByAccounts) } else { - Object obj; + UniValue obj(UniValue::VOBJ); if(fIsWatchonly) obj.push_back(Pair("involvesWatchonly", true)); obj.push_back(Pair("address", address.ToString())); obj.push_back(Pair("account", strAccount)); obj.push_back(Pair("amount", ValueFromAmount(nAmount))); obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf))); - Array transactions; + UniValue transactions(UniValue::VARR); if (it != mapTally.end()) { BOOST_FOREACH(const uint256& item, (*it).second.txids) @@ -1128,7 +1200,7 @@ Value ListReceived(const Array& params, bool fByAccounts) { CAmount nAmount = (*it).second.nAmount; int nConf = (*it).second.nConf; - Object obj; + UniValue obj(UniValue::VOBJ); if((*it).second.fIsWatchonly) obj.push_back(Pair("involvesWatchonly", true)); obj.push_back(Pair("account", (*it).first)); @@ -1141,8 +1213,11 @@ Value ListReceived(const Array& params, bool fByAccounts) return ret; } -Value listreceivedbyaddress(const Array& params, bool fHelp) +UniValue listreceivedbyaddress(const UniValue& params, bool fHelp) { + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + if (fHelp || params.size() > 3) throw runtime_error( "listreceivedbyaddress ( minconf includeempty includeWatchonly)\n" @@ -1175,8 +1250,11 @@ Value listreceivedbyaddress(const Array& params, bool fHelp) return ListReceived(params, false); } -Value listreceivedbyaccount(const Array& params, bool fHelp) +UniValue listreceivedbyaccount(const UniValue& params, bool fHelp) { + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + if (fHelp || params.size() > 3) throw runtime_error( "listreceivedbyaccount ( minconf includeempty includeWatchonly)\n" @@ -1208,14 +1286,14 @@ Value listreceivedbyaccount(const Array& params, bool fHelp) return ListReceived(params, true); } -static void MaybePushAddress(Object & entry, const CTxDestination &dest) +static void MaybePushAddress(UniValue & entry, const CTxDestination &dest) { CBitcoinAddress addr; if (addr.Set(dest)) entry.push_back(Pair("address", addr.ToString())); } -void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret, const isminefilter& filter) +void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, UniValue& ret, const isminefilter& filter) { CAmount nFee; string strSentAccount; @@ -1232,7 +1310,7 @@ void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDe { BOOST_FOREACH(const COutputEntry& s, listSent) { - Object entry; + UniValue entry(UniValue::VOBJ); if(involvesWatchonly || (::IsMine(*pwalletMain, s.destination) & ISMINE_WATCH_ONLY)) entry.push_back(Pair("involvesWatchonly", true)); entry.push_back(Pair("account", strSentAccount)); @@ -1257,7 +1335,7 @@ void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDe account = pwalletMain->mapAddressBook[r.destination].name; if (fAllAccounts || (account == strAccount)) { - Object entry; + UniValue entry(UniValue::VOBJ); if(involvesWatchonly || (::IsMine(*pwalletMain, r.destination) & ISMINE_WATCH_ONLY)) entry.push_back(Pair("involvesWatchonly", true)); entry.push_back(Pair("account", account)); @@ -1285,13 +1363,13 @@ void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDe } } -void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Array& ret) +void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, UniValue& ret) { bool fAllAccounts = (strAccount == string("*")); if (fAllAccounts || acentry.strAccount == strAccount) { - Object entry; + UniValue entry(UniValue::VOBJ); entry.push_back(Pair("account", acentry.strAccount)); entry.push_back(Pair("category", "move")); entry.push_back(Pair("time", acentry.nTime)); @@ -1302,8 +1380,11 @@ void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Ar } } -Value listtransactions(const Array& params, bool fHelp) +UniValue listtransactions(const UniValue& params, bool fHelp) { + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + if (fHelp || params.size() > 4) throw runtime_error( "listtransactions ( \"account\" count from includeWatchonly)\n" @@ -1377,7 +1458,7 @@ Value listtransactions(const Array& params, bool fHelp) if (nFrom < 0) throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative from"); - Array ret; + UniValue ret(UniValue::VARR); std::list<CAccountingEntry> acentries; CWallet::TxItems txOrdered = pwalletMain->OrderedTxItems(acentries, strAccount); @@ -1400,21 +1481,31 @@ Value listtransactions(const Array& params, bool fHelp) nFrom = ret.size(); if ((nFrom + nCount) > (int)ret.size()) nCount = ret.size() - nFrom; - Array::iterator first = ret.begin(); + + vector<UniValue> arrTmp = ret.getValues(); + + vector<UniValue>::iterator first = arrTmp.begin(); std::advance(first, nFrom); - Array::iterator last = ret.begin(); + vector<UniValue>::iterator last = arrTmp.begin(); std::advance(last, nFrom+nCount); - if (last != ret.end()) ret.erase(last, ret.end()); - if (first != ret.begin()) ret.erase(ret.begin(), first); + if (last != arrTmp.end()) arrTmp.erase(last, arrTmp.end()); + if (first != arrTmp.begin()) arrTmp.erase(arrTmp.begin(), first); + + std::reverse(arrTmp.begin(), arrTmp.end()); // Return oldest to newest - std::reverse(ret.begin(), ret.end()); // Return oldest to newest + ret.clear(); + ret.setArray(); + ret.push_backV(arrTmp); return ret; } -Value listaccounts(const Array& params, bool fHelp) +UniValue listaccounts(const UniValue& params, bool fHelp) { + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + if (fHelp || params.size() > 2) throw runtime_error( "listaccounts ( minconf includeWatchonly)\n" @@ -1483,15 +1574,18 @@ Value listaccounts(const Array& params, bool fHelp) BOOST_FOREACH(const CAccountingEntry& entry, acentries) mapAccountBalances[entry.strAccount] += entry.nCreditDebit; - Object ret; + UniValue ret(UniValue::VOBJ); BOOST_FOREACH(const PAIRTYPE(string, CAmount)& accountBalance, mapAccountBalances) { ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second))); } return ret; } -Value listsinceblock(const Array& params, bool fHelp) +UniValue listsinceblock(const UniValue& params, bool fHelp) { + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + if (fHelp) throw runtime_error( "listsinceblock ( \"blockhash\" target-confirmations includeWatchonly)\n" @@ -1558,7 +1652,7 @@ Value listsinceblock(const Array& params, bool fHelp) int depth = pindex ? (1 + chainActive.Height() - pindex->nHeight) : -1; - Array transactions; + UniValue transactions(UniValue::VARR); for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); it++) { @@ -1571,15 +1665,18 @@ Value listsinceblock(const Array& params, bool fHelp) CBlockIndex *pblockLast = chainActive[chainActive.Height() + 1 - target_confirms]; uint256 lastblock = pblockLast ? pblockLast->GetBlockHash() : uint256(); - Object ret; + UniValue ret(UniValue::VOBJ); ret.push_back(Pair("transactions", transactions)); ret.push_back(Pair("lastblock", lastblock.GetHex())); return ret; } -Value gettransaction(const Array& params, bool fHelp) +UniValue gettransaction(const UniValue& params, bool fHelp) { + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + if (fHelp || params.size() < 1 || params.size() > 2) throw runtime_error( "gettransaction \"txid\" ( includeWatchonly )\n" @@ -1626,7 +1723,7 @@ Value gettransaction(const Array& params, bool fHelp) if(params[1].get_bool()) filter = filter | ISMINE_WATCH_ONLY; - Object entry; + UniValue entry(UniValue::VOBJ); if (!pwalletMain->mapWallet.count(hash)) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id"); const CWalletTx& wtx = pwalletMain->mapWallet[hash]; @@ -1642,7 +1739,7 @@ Value gettransaction(const Array& params, bool fHelp) WalletTxToJSON(wtx, entry); - Array details; + UniValue details(UniValue::VARR); ListTransactions(wtx, "*", 0, false, details, filter); entry.push_back(Pair("details", details)); @@ -1653,8 +1750,11 @@ Value gettransaction(const Array& params, bool fHelp) } -Value backupwallet(const Array& params, bool fHelp) +UniValue backupwallet(const UniValue& params, bool fHelp) { + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + if (fHelp || params.size() != 1) throw runtime_error( "backupwallet \"destination\"\n" @@ -1672,12 +1772,15 @@ Value backupwallet(const Array& params, bool fHelp) if (!BackupWallet(*pwalletMain, strDest)) throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet backup failed!"); - return Value::null; + return NullUniValue; } -Value keypoolrefill(const Array& params, bool fHelp) +UniValue keypoolrefill(const UniValue& params, bool fHelp) { + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + if (fHelp || params.size() > 1) throw runtime_error( "keypoolrefill ( newsize )\n" @@ -1706,7 +1809,7 @@ Value keypoolrefill(const Array& params, bool fHelp) if (pwalletMain->GetKeyPoolSize() < kpSize) throw JSONRPCError(RPC_WALLET_ERROR, "Error refreshing keypool."); - return Value::null; + return NullUniValue; } @@ -1717,8 +1820,11 @@ static void LockWallet(CWallet* pWallet) pWallet->Lock(); } -Value walletpassphrase(const Array& params, bool fHelp) +UniValue walletpassphrase(const UniValue& params, bool fHelp) { + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2)) throw runtime_error( "walletpassphrase \"passphrase\" timeout\n" @@ -1770,12 +1876,15 @@ Value walletpassphrase(const Array& params, bool fHelp) nWalletUnlockTime = GetTime() + nSleepTime; RPCRunLater("lockwallet", boost::bind(LockWallet, pwalletMain), nSleepTime); - return Value::null; + return NullUniValue; } -Value walletpassphrasechange(const Array& params, bool fHelp) +UniValue walletpassphrasechange(const UniValue& params, bool fHelp) { + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2)) throw runtime_error( "walletpassphrasechange \"oldpassphrase\" \"newpassphrase\"\n" @@ -1813,12 +1922,15 @@ Value walletpassphrasechange(const Array& params, bool fHelp) if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass)) throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect."); - return Value::null; + return NullUniValue; } -Value walletlock(const Array& params, bool fHelp) +UniValue walletlock(const UniValue& params, bool fHelp) { + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + if (pwalletMain->IsCrypted() && (fHelp || params.size() != 0)) throw runtime_error( "walletlock\n" @@ -1849,12 +1961,15 @@ Value walletlock(const Array& params, bool fHelp) nWalletUnlockTime = 0; } - return Value::null; + return NullUniValue; } -Value encryptwallet(const Array& params, bool fHelp) +UniValue encryptwallet(const UniValue& params, bool fHelp) { + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + if (!pwalletMain->IsCrypted() && (fHelp || params.size() != 1)) throw runtime_error( "encryptwallet \"passphrase\"\n" @@ -1907,8 +2022,11 @@ Value encryptwallet(const Array& params, bool fHelp) return "wallet encrypted; Bitcoin server stopping, restart to run with encrypted wallet. The keypool has been flushed, you need to make a new backup."; } -Value lockunspent(const Array& params, bool fHelp) +UniValue lockunspent(const UniValue& params, bool fHelp) { + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + if (fHelp || params.size() < 1 || params.size() > 2) throw runtime_error( "lockunspent unlock [{\"txid\":\"txid\",\"vout\":n},...]\n" @@ -1948,9 +2066,9 @@ Value lockunspent(const Array& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); if (params.size() == 1) - RPCTypeCheck(params, boost::assign::list_of(bool_type)); + RPCTypeCheck(params, boost::assign::list_of(UniValue::VBOOL)); else - RPCTypeCheck(params, boost::assign::list_of(bool_type)(array_type)); + RPCTypeCheck(params, boost::assign::list_of(UniValue::VBOOL)(UniValue::VARR)); bool fUnlock = params[0].get_bool(); @@ -1960,14 +2078,14 @@ Value lockunspent(const Array& params, bool fHelp) return true; } - Array outputs = params[1].get_array(); - BOOST_FOREACH(Value& output, outputs) - { - if (output.type() != obj_type) + UniValue outputs = params[1].get_array(); + for (unsigned int idx = 0; idx < outputs.size(); idx++) { + const UniValue& output = outputs[idx]; + if (!output.isObject()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected object"); - const Object& o = output.get_obj(); + const UniValue& o = output.get_obj(); - RPCTypeCheck(o, boost::assign::map_list_of("txid", str_type)("vout", int_type)); + RPCTypeCheckObj(o, boost::assign::map_list_of("txid", UniValue::VSTR)("vout", UniValue::VNUM)); string txid = find_value(o, "txid").get_str(); if (!IsHex(txid)) @@ -1988,8 +2106,11 @@ Value lockunspent(const Array& params, bool fHelp) return true; } -Value listlockunspent(const Array& params, bool fHelp) +UniValue listlockunspent(const UniValue& params, bool fHelp) { + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + if (fHelp || params.size() > 0) throw runtime_error( "listlockunspent\n" @@ -2021,10 +2142,10 @@ Value listlockunspent(const Array& params, bool fHelp) vector<COutPoint> vOutpts; pwalletMain->ListLockedCoins(vOutpts); - Array ret; + UniValue ret(UniValue::VARR); BOOST_FOREACH(COutPoint &outpt, vOutpts) { - Object o; + UniValue o(UniValue::VOBJ); o.push_back(Pair("txid", outpt.hash.GetHex())); o.push_back(Pair("vout", (int)outpt.n)); @@ -2034,8 +2155,11 @@ Value listlockunspent(const Array& params, bool fHelp) return ret; } -Value settxfee(const Array& params, bool fHelp) +UniValue settxfee(const UniValue& params, bool fHelp) { + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + if (fHelp || params.size() < 1 || params.size() > 1) throw runtime_error( "settxfee amount\n" @@ -2052,16 +2176,17 @@ Value settxfee(const Array& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); // Amount - CAmount nAmount = 0; - if (params[0].get_real() != 0.0) - nAmount = AmountFromValue(params[0]); // rejects 0.0 amounts + CAmount nAmount = AmountFromValue(params[0]); payTxFee = CFeeRate(nAmount, 1000); return true; } -Value getwalletinfo(const Array& params, bool fHelp) +UniValue getwalletinfo(const UniValue& params, bool fHelp) { + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + if (fHelp || params.size() != 0) throw runtime_error( "getwalletinfo\n" @@ -2076,6 +2201,7 @@ Value getwalletinfo(const Array& params, bool fHelp) " \"keypoololdest\": xxxxxx, (numeric) the timestamp (seconds since GMT epoch) of the oldest pre-generated key in the key pool\n" " \"keypoolsize\": xxxx, (numeric) how many new keys are pre-generated\n" " \"unlocked_until\": ttt, (numeric) the timestamp in seconds since epoch (midnight Jan 1 1970 GMT) that the wallet is unlocked for transfers, or 0 if the wallet is locked\n" + " \"paytxfee\": x.xxxx, (numeric) the transaction fee configuration, set in btc/kb\n" "}\n" "\nExamples:\n" + HelpExampleCli("getwalletinfo", "") @@ -2084,7 +2210,7 @@ Value getwalletinfo(const Array& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); - Object obj; + UniValue obj(UniValue::VOBJ); obj.push_back(Pair("walletversion", pwalletMain->GetVersion())); obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance()))); obj.push_back(Pair("unconfirmed_balance", ValueFromAmount(pwalletMain->GetUnconfirmedBalance()))); @@ -2094,11 +2220,15 @@ Value getwalletinfo(const Array& params, bool fHelp) obj.push_back(Pair("keypoolsize", (int)pwalletMain->GetKeyPoolSize())); if (pwalletMain->IsCrypted()) obj.push_back(Pair("unlocked_until", nWalletUnlockTime)); + obj.push_back(Pair("paytxfee", ValueFromAmount(payTxFee.GetFeePerK()))); return obj; } -Value resendwallettransactions(const Array& params, bool fHelp) +UniValue resendwallettransactions(const UniValue& params, bool fHelp) { + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + if (fHelp || params.size() != 0) throw runtime_error( "resendwallettransactions\n" @@ -2111,10 +2241,177 @@ Value resendwallettransactions(const Array& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); std::vector<uint256> txids = pwalletMain->ResendWalletTransactionsBefore(GetTime()); - Array result; + UniValue result(UniValue::VARR); BOOST_FOREACH(const uint256& txid, txids) { result.push_back(txid.ToString()); } return result; } + +UniValue listunspent(const UniValue& params, bool fHelp) +{ + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + + if (fHelp || params.size() > 3) + throw runtime_error( + "listunspent ( minconf maxconf [\"address\",...] )\n" + "\nReturns array of unspent transaction outputs\n" + "with between minconf and maxconf (inclusive) confirmations.\n" + "Optionally filter to only include txouts paid to specified addresses.\n" + "Results are an array of Objects, each of which has:\n" + "{txid, vout, scriptPubKey, amount, confirmations}\n" + "\nArguments:\n" + "1. minconf (numeric, optional, default=1) The minimum confirmations to filter\n" + "2. maxconf (numeric, optional, default=9999999) The maximum confirmations to filter\n" + "3. \"addresses\" (string) A json array of bitcoin addresses to filter\n" + " [\n" + " \"address\" (string) bitcoin address\n" + " ,...\n" + " ]\n" + "\nResult\n" + "[ (array of json object)\n" + " {\n" + " \"txid\" : \"txid\", (string) the transaction id \n" + " \"vout\" : n, (numeric) the vout value\n" + " \"address\" : \"address\", (string) the bitcoin address\n" + " \"account\" : \"account\", (string) DEPRECATED. The associated account, or \"\" for the default account\n" + " \"scriptPubKey\" : \"key\", (string) the script key\n" + " \"amount\" : x.xxx, (numeric) the transaction amount in btc\n" + " \"confirmations\" : n (numeric) The number of confirmations\n" + " }\n" + " ,...\n" + "]\n" + + "\nExamples\n" + + HelpExampleCli("listunspent", "") + + HelpExampleCli("listunspent", "6 9999999 \"[\\\"1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\",\\\"1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"") + + HelpExampleRpc("listunspent", "6, 9999999 \"[\\\"1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\",\\\"1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"") + ); + + RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM)(UniValue::VNUM)(UniValue::VARR)); + + int nMinDepth = 1; + if (params.size() > 0) + nMinDepth = params[0].get_int(); + + int nMaxDepth = 9999999; + if (params.size() > 1) + nMaxDepth = params[1].get_int(); + + set<CBitcoinAddress> setAddress; + if (params.size() > 2) { + UniValue inputs = params[2].get_array(); + for (unsigned int idx = 0; idx < inputs.size(); idx++) { + const UniValue& input = inputs[idx]; + CBitcoinAddress address(input.get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Bitcoin address: ")+input.get_str()); + if (setAddress.count(address)) + throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+input.get_str()); + setAddress.insert(address); + } + } + + UniValue results(UniValue::VARR); + vector<COutput> vecOutputs; + assert(pwalletMain != NULL); + LOCK2(cs_main, pwalletMain->cs_wallet); + pwalletMain->AvailableCoins(vecOutputs, false, NULL, true); + BOOST_FOREACH(const COutput& out, vecOutputs) { + if (out.nDepth < nMinDepth || out.nDepth > nMaxDepth) + continue; + + if (setAddress.size()) { + CTxDestination address; + if (!ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) + continue; + + if (!setAddress.count(address)) + continue; + } + + CAmount nValue = out.tx->vout[out.i].nValue; + const CScript& pk = out.tx->vout[out.i].scriptPubKey; + UniValue entry(UniValue::VOBJ); + entry.push_back(Pair("txid", out.tx->GetHash().GetHex())); + entry.push_back(Pair("vout", out.i)); + CTxDestination address; + if (ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) { + entry.push_back(Pair("address", CBitcoinAddress(address).ToString())); + if (pwalletMain->mapAddressBook.count(address)) + entry.push_back(Pair("account", pwalletMain->mapAddressBook[address].name)); + } + entry.push_back(Pair("scriptPubKey", HexStr(pk.begin(), pk.end()))); + if (pk.IsPayToScriptHash()) { + CTxDestination address; + if (ExtractDestination(pk, address)) { + const CScriptID& hash = boost::get<CScriptID>(address); + CScript redeemScript; + if (pwalletMain->GetCScript(hash, redeemScript)) + entry.push_back(Pair("redeemScript", HexStr(redeemScript.begin(), redeemScript.end()))); + } + } + entry.push_back(Pair("amount",ValueFromAmount(nValue))); + entry.push_back(Pair("confirmations",out.nDepth)); + entry.push_back(Pair("spendable", out.fSpendable)); + results.push_back(entry); + } + + return results; +} + +UniValue fundrawtransaction(const UniValue& params, bool fHelp) +{ + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + + if (fHelp || params.size() != 1) + throw runtime_error( + "fundrawtransaction \"hexstring\"\n" + "\nAdd inputs to a transaction until it has enough in value to meet its out value.\n" + "This will not modify existing inputs, and will add one change output to the outputs.\n" + "Note that inputs which were signed may need to be resigned after completion since in/outputs have been added.\n" + "The inputs added will not be signed, use signrawtransaction for that.\n" + "\nArguments:\n" + "1. \"hexstring\" (string, required) The hex string of the raw transaction\n" + "\nResult:\n" + "{\n" + " \"hex\": \"value\", (string) The resulting raw transaction (hex-encoded string)\n" + " \"fee\": n, (numeric) The fee added to the transaction\n" + " \"changepos\": n (numeric) The position of the added change output, or -1\n" + "}\n" + "\"hex\" \n" + "\nExamples:\n" + "\nCreate a transaction with no inputs\n" + + HelpExampleCli("createrawtransaction", "\"[]\" \"{\\\"myaddress\\\":0.01}\"") + + "\nAdd sufficient unsigned inputs to meet the output value\n" + + HelpExampleCli("fundrawtransaction", "\"rawtransactionhex\"") + + "\nSign the transaction\n" + + HelpExampleCli("signrawtransaction", "\"fundedtransactionhex\"") + + "\nSend the transaction\n" + + HelpExampleCli("sendrawtransaction", "\"signedtransactionhex\"") + ); + + RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)); + + // parse hex string from parameter + CTransaction origTx; + if (!DecodeHexTx(origTx, params[0].get_str())) + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); + + CMutableTransaction tx(origTx); + CAmount nFee; + string strFailReason; + int nChangePos = -1; + if(!pwalletMain->FundTransaction(tx, nFee, nChangePos, strFailReason)) + throw JSONRPCError(RPC_INTERNAL_ERROR, strFailReason); + + UniValue result(UniValue::VOBJ); + result.push_back(Pair("hex", EncodeHexTx(tx))); + result.push_back(Pair("changepos", nChangePos)); + result.push_back(Pair("fee", ValueFromAmount(nFee))); + + return result; +} |