diff options
Diffstat (limited to 'src/wallet/rpcwallet.cpp')
-rw-r--r-- | src/wallet/rpcwallet.cpp | 542 |
1 files changed, 348 insertions, 194 deletions
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index a6176c3485..d6989add89 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -8,7 +8,6 @@ #include "chain.h" #include "consensus/validation.h" #include "core_io.h" -#include "init.h" #include "httpserver.h" #include "validation.h" #include "net.h" @@ -17,6 +16,7 @@ #include "policy/policy.h" #include "policy/rbf.h" #include "rpc/mining.h" +#include "rpc/safemode.h" #include "rpc/server.h" #include "script/sign.h" #include "timedata.h" @@ -27,6 +27,8 @@ #include "wallet/wallet.h" #include "wallet/walletdb.h" +#include <init.h> // For StartShutdown + #include <stdint.h> #include <univalue.h> @@ -168,18 +170,18 @@ UniValue getnewaddress(const JSONRPCRequest& request) pwallet->SetAddressBook(keyID, strAccount, "receive"); - return CBitcoinAddress(keyID).ToString(); + return EncodeDestination(keyID); } -CBitcoinAddress GetAccountAddress(CWallet* const pwallet, std::string strAccount, bool bForceNew=false) +CTxDestination GetAccountAddress(CWallet* const pwallet, std::string strAccount, bool bForceNew=false) { CPubKey pubKey; if (!pwallet->GetAccountPubkey(pubKey, strAccount, bForceNew)) { throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); } - return CBitcoinAddress(pubKey.GetID()); + return pubKey.GetID(); } UniValue getaccountaddress(const JSONRPCRequest& request) @@ -211,7 +213,7 @@ UniValue getaccountaddress(const JSONRPCRequest& request) UniValue ret(UniValue::VSTR); - ret = GetAccountAddress(pwallet, strAccount).ToString(); + ret = EncodeDestination(GetAccountAddress(pwallet, strAccount)); return ret; } @@ -250,7 +252,7 @@ UniValue getrawchangeaddress(const JSONRPCRequest& request) CKeyID keyID = vchPubKey.GetID(); - return CBitcoinAddress(keyID).ToString(); + return EncodeDestination(keyID); } @@ -275,24 +277,25 @@ UniValue setaccount(const JSONRPCRequest& request) LOCK2(cs_main, pwallet->cs_wallet); - CBitcoinAddress address(request.params[0].get_str()); - if (!address.IsValid()) + CTxDestination dest = DecodeDestination(request.params[0].get_str()); + if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); + } std::string strAccount; - if (request.params.size() > 1) + if (!request.params[1].isNull()) strAccount = AccountFromValue(request.params[1]); // Only add the account if the address is yours. - if (IsMine(*pwallet, address.Get())) { + if (IsMine(*pwallet, dest)) { // Detect when changing the account of an address that is the 'unused current key' of another account: - if (pwallet->mapAddressBook.count(address.Get())) { - std::string strOldAccount = pwallet->mapAddressBook[address.Get()].name; - if (address == GetAccountAddress(pwallet, strOldAccount)) { + if (pwallet->mapAddressBook.count(dest)) { + std::string strOldAccount = pwallet->mapAddressBook[dest].name; + if (dest == GetAccountAddress(pwallet, strOldAccount)) { GetAccountAddress(pwallet, strOldAccount, true); } } - pwallet->SetAddressBook(address.Get(), strAccount, "receive"); + pwallet->SetAddressBook(dest, strAccount, "receive"); } else throw JSONRPCError(RPC_MISC_ERROR, "setaccount can only be used with own address"); @@ -323,12 +326,13 @@ UniValue getaccount(const JSONRPCRequest& request) LOCK2(cs_main, pwallet->cs_wallet); - CBitcoinAddress address(request.params[0].get_str()); - if (!address.IsValid()) + CTxDestination dest = DecodeDestination(request.params[0].get_str()); + if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); + } std::string strAccount; - std::map<CTxDestination, CAddressBookData>::iterator mi = pwallet->mapAddressBook.find(address.Get()); + std::map<CTxDestination, CAddressBookData>::iterator mi = pwallet->mapAddressBook.find(dest); if (mi != pwallet->mapAddressBook.end() && !(*mi).second.name.empty()) { strAccount = (*mi).second.name; } @@ -365,11 +369,12 @@ UniValue getaddressesbyaccount(const JSONRPCRequest& request) // Find all addresses that have the given account UniValue ret(UniValue::VARR); - for (const std::pair<CBitcoinAddress, CAddressBookData>& item : pwallet->mapAddressBook) { - const CBitcoinAddress& address = item.first; + for (const std::pair<CTxDestination, CAddressBookData>& item : pwallet->mapAddressBook) { + const CTxDestination& dest = item.first; const std::string& strName = item.second.name; - if (strName == strAccount) - ret.push_back(address.ToString()); + if (strName == strAccount) { + ret.push_back(EncodeDestination(dest)); + } } return ret; } @@ -449,11 +454,13 @@ UniValue sendtoaddress(const JSONRPCRequest& request) + HelpExampleRpc("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\", 0.1, \"donation\", \"seans outpost\"") ); + ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); - CBitcoinAddress address(request.params[0].get_str()); - if (!address.IsValid()) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); + CTxDestination dest = DecodeDestination(request.params[0].get_str()); + if (!IsValidDestination(dest)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address"); + } // Amount CAmount nAmount = AmountFromValue(request.params[1]); @@ -462,26 +469,26 @@ UniValue sendtoaddress(const JSONRPCRequest& request) // Wallet comments CWalletTx wtx; - if (request.params.size() > 2 && !request.params[2].isNull() && !request.params[2].get_str().empty()) + if (!request.params[2].isNull() && !request.params[2].get_str().empty()) wtx.mapValue["comment"] = request.params[2].get_str(); - if (request.params.size() > 3 && !request.params[3].isNull() && !request.params[3].get_str().empty()) + if (!request.params[3].isNull() && !request.params[3].get_str().empty()) wtx.mapValue["to"] = request.params[3].get_str(); bool fSubtractFeeFromAmount = false; - if (request.params.size() > 4 && !request.params[4].isNull()) { + if (!request.params[4].isNull()) { fSubtractFeeFromAmount = request.params[4].get_bool(); } CCoinControl coin_control; - if (request.params.size() > 5 && !request.params[5].isNull()) { + if (!request.params[5].isNull()) { coin_control.signalRbf = request.params[5].get_bool(); } - if (request.params.size() > 6 && !request.params[6].isNull()) { + if (!request.params[6].isNull()) { coin_control.m_confirm_target = ParseConfirmTarget(request.params[6]); } - if (request.params.size() > 7 && !request.params[7].isNull()) { + if (!request.params[7].isNull()) { if (!FeeModeFromString(request.params[7].get_str(), coin_control.m_fee_mode)) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid estimate_mode parameter"); } @@ -490,7 +497,7 @@ UniValue sendtoaddress(const JSONRPCRequest& request) EnsureWalletIsUnlocked(pwallet); - SendMoney(pwallet, address.Get(), nAmount, fSubtractFeeFromAmount, wtx, coin_control); + SendMoney(pwallet, dest, nAmount, fSubtractFeeFromAmount, wtx, coin_control); return wtx.GetHash().GetHex(); } @@ -525,20 +532,21 @@ UniValue listaddressgroupings(const JSONRPCRequest& request) + HelpExampleRpc("listaddressgroupings", "") ); + ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); UniValue jsonGroupings(UniValue::VARR); std::map<CTxDestination, CAmount> balances = pwallet->GetAddressBalances(); - for (std::set<CTxDestination> grouping : pwallet->GetAddressGroupings()) { + for (const std::set<CTxDestination>& grouping : pwallet->GetAddressGroupings()) { UniValue jsonGrouping(UniValue::VARR); - for (CTxDestination address : grouping) + for (const CTxDestination& address : grouping) { UniValue addressInfo(UniValue::VARR); - addressInfo.push_back(CBitcoinAddress(address).ToString()); + addressInfo.push_back(EncodeDestination(address)); addressInfo.push_back(ValueFromAmount(balances[address])); { - if (pwallet->mapAddressBook.find(CBitcoinAddress(address).Get()) != pwallet->mapAddressBook.end()) { - addressInfo.push_back(pwallet->mapAddressBook.find(CBitcoinAddress(address).Get())->second.name); + if (pwallet->mapAddressBook.find(address) != pwallet->mapAddressBook.end()) { + addressInfo.push_back(pwallet->mapAddressBook.find(address)->second.name); } } jsonGrouping.push_back(addressInfo); @@ -583,16 +591,18 @@ UniValue signmessage(const JSONRPCRequest& request) std::string strAddress = request.params[0].get_str(); std::string strMessage = request.params[1].get_str(); - CBitcoinAddress addr(strAddress); - if (!addr.IsValid()) + CTxDestination dest = DecodeDestination(strAddress); + if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address"); + } - CKeyID keyID; - if (!addr.GetKeyID(keyID)) + const CKeyID *keyID = boost::get<CKeyID>(&dest); + if (!keyID) { throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key"); + } CKey key; - if (!pwallet->GetKey(keyID, key)) { + if (!pwallet->GetKey(*keyID, key)) { throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available"); } @@ -604,7 +614,7 @@ UniValue signmessage(const JSONRPCRequest& request) if (!key.SignCompact(ss.GetHash(), vchSig)) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed"); - return EncodeBase64(&vchSig[0], vchSig.size()); + return EncodeBase64(vchSig.data(), vchSig.size()); } UniValue getreceivedbyaddress(const JSONRPCRequest& request) @@ -634,13 +644,15 @@ UniValue getreceivedbyaddress(const JSONRPCRequest& request) + HelpExampleRpc("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", 6") ); + ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); // Bitcoin address - CBitcoinAddress address = CBitcoinAddress(request.params[0].get_str()); - if (!address.IsValid()) + CTxDestination dest = DecodeDestination(request.params[0].get_str()); + if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); - CScript scriptPubKey = GetScriptForDestination(address.Get()); + } + CScript scriptPubKey = GetScriptForDestination(dest); if (!IsMine(*pwallet, scriptPubKey)) { return ValueFromAmount(0); } @@ -694,6 +706,7 @@ UniValue getreceivedbyaccount(const JSONRPCRequest& request) + HelpExampleRpc("getreceivedbyaccount", "\"tabby\", 6") ); + ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); // Minimum confirmations @@ -766,20 +779,34 @@ UniValue getbalance(const JSONRPCRequest& request) + HelpExampleRpc("getbalance", "\"*\", 6") ); + ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); - if (request.params.size() == 0) - return ValueFromAmount(pwallet->GetBalance()); + const UniValue& account_value = request.params[0]; + const UniValue& minconf = request.params[1]; + const UniValue& include_watchonly = request.params[2]; + + if (account_value.isNull()) { + if (!minconf.isNull()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, + "getbalance minconf option is only currently supported if an account is specified"); + } + if (!include_watchonly.isNull()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, + "getbalance include_watchonly option is only currently supported if an account is specified"); + } + return ValueFromAmount(pwallet->GetBalance()); + } - const std::string& account_param = request.params[0].get_str(); + const std::string& account_param = account_value.get_str(); const std::string* account = account_param != "*" ? &account_param : nullptr; int nMinDepth = 1; - if (!request.params[1].isNull()) - nMinDepth = request.params[1].get_int(); + if (!minconf.isNull()) + nMinDepth = minconf.get_int(); isminefilter filter = ISMINE_SPENDABLE; - if(!request.params[2].isNull()) - if(request.params[2].get_bool()) + if(!include_watchonly.isNull()) + if(include_watchonly.get_bool()) filter = filter | ISMINE_WATCH_ONLY; return ValueFromAmount(pwallet->GetLegacyBalance(filter, nMinDepth, account)); @@ -797,6 +824,7 @@ UniValue getunconfirmedbalance(const JSONRPCRequest &request) "getunconfirmedbalance\n" "Returns the server's total unconfirmed balance\n"); + ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); return ValueFromAmount(pwallet->GetUnconfirmedBalance()); @@ -831,6 +859,7 @@ UniValue movecmd(const JSONRPCRequest& request) + HelpExampleRpc("move", "\"timotei\", \"akiko\", 0.01, 6, \"happy birthday!\"") ); + ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); std::string strFrom = AccountFromValue(request.params[0]); @@ -838,11 +867,11 @@ UniValue movecmd(const JSONRPCRequest& request) CAmount nAmount = AmountFromValue(request.params[2]); if (nAmount <= 0) throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); - if (request.params.size() > 3) + if (!request.params[3].isNull()) // unused parameter, used to be nMinDepth, keep type-checking it though (void)request.params[3].get_int(); std::string strComment; - if (request.params.size() > 4) + if (!request.params[4].isNull()) strComment = request.params[4].get_str(); if (!pwallet->AccountMove(strFrom, strTo, nAmount, strComment)) { @@ -889,24 +918,26 @@ UniValue sendfrom(const JSONRPCRequest& request) + HelpExampleRpc("sendfrom", "\"tabby\", \"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\", 0.01, 6, \"donation\", \"seans outpost\"") ); + ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); std::string strAccount = AccountFromValue(request.params[0]); - CBitcoinAddress address(request.params[1].get_str()); - if (!address.IsValid()) + CTxDestination dest = DecodeDestination(request.params[1].get_str()); + if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); + } CAmount nAmount = AmountFromValue(request.params[2]); if (nAmount <= 0) throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); int nMinDepth = 1; - if (request.params.size() > 3) + if (!request.params[3].isNull()) nMinDepth = request.params[3].get_int(); CWalletTx wtx; wtx.strFromAccount = strAccount; - if (request.params.size() > 4 && !request.params[4].isNull() && !request.params[4].get_str().empty()) + if (!request.params[4].isNull() && !request.params[4].get_str().empty()) wtx.mapValue["comment"] = request.params[4].get_str(); - if (request.params.size() > 5 && !request.params[5].isNull() && !request.params[5].get_str().empty()) + if (!request.params[5].isNull() && !request.params[5].get_str().empty()) wtx.mapValue["to"] = request.params[5].get_str(); EnsureWalletIsUnlocked(pwallet); @@ -917,7 +948,7 @@ UniValue sendfrom(const JSONRPCRequest& request) throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds"); CCoinControl no_coin_control; // This is a deprecated API - SendMoney(pwallet, address.Get(), nAmount, false, wtx, no_coin_control); + SendMoney(pwallet, dest, nAmount, false, wtx, no_coin_control); return wtx.GetHash().GetHex(); } @@ -972,6 +1003,7 @@ UniValue sendmany(const JSONRPCRequest& request) + HelpExampleRpc("sendmany", "\"\", \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\", 6, \"testing\"") ); + ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); if (pwallet->GetBroadcastTransactions() && !g_connman) { @@ -986,44 +1018,45 @@ UniValue sendmany(const JSONRPCRequest& request) CWalletTx wtx; wtx.strFromAccount = strAccount; - if (request.params.size() > 3 && !request.params[3].isNull() && !request.params[3].get_str().empty()) + if (!request.params[3].isNull() && !request.params[3].get_str().empty()) wtx.mapValue["comment"] = request.params[3].get_str(); UniValue subtractFeeFromAmount(UniValue::VARR); - if (request.params.size() > 4 && !request.params[4].isNull()) + if (!request.params[4].isNull()) subtractFeeFromAmount = request.params[4].get_array(); CCoinControl coin_control; - if (request.params.size() > 5 && !request.params[5].isNull()) { + if (!request.params[5].isNull()) { coin_control.signalRbf = request.params[5].get_bool(); } - if (request.params.size() > 6 && !request.params[6].isNull()) { + if (!request.params[6].isNull()) { coin_control.m_confirm_target = ParseConfirmTarget(request.params[6]); } - if (request.params.size() > 7 && !request.params[7].isNull()) { + if (!request.params[7].isNull()) { if (!FeeModeFromString(request.params[7].get_str(), coin_control.m_fee_mode)) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid estimate_mode parameter"); } } - std::set<CBitcoinAddress> setAddress; + std::set<CTxDestination> destinations; std::vector<CRecipient> vecSend; CAmount totalAmount = 0; std::vector<std::string> keys = sendTo.getKeys(); - for (const std::string& name_ : keys) - { - CBitcoinAddress address(name_); - if (!address.IsValid()) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ")+name_); + for (const std::string& name_ : keys) { + CTxDestination dest = DecodeDestination(name_); + if (!IsValidDestination(dest)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ") + name_); + } - if (setAddress.count(address)) - throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ")+name_); - setAddress.insert(address); + if (destinations.count(dest)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ") + name_); + } + destinations.insert(dest); - CScript scriptPubKey = GetScriptForDestination(address.Get()); + CScript scriptPubKey = GetScriptForDestination(dest); CAmount nAmount = AmountFromValue(sendTo[name_]); if (nAmount <= 0) throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); @@ -1105,7 +1138,7 @@ UniValue addmultisigaddress(const JSONRPCRequest& request) LOCK2(cs_main, pwallet->cs_wallet); std::string strAccount; - if (request.params.size() > 2) + if (!request.params[2].isNull()) strAccount = AccountFromValue(request.params[2]); // Construct using pay-to-script-hash: @@ -1114,18 +1147,17 @@ UniValue addmultisigaddress(const JSONRPCRequest& request) pwallet->AddCScript(inner); pwallet->SetAddressBook(innerID, strAccount, "send"); - return CBitcoinAddress(innerID).ToString(); + return EncodeDestination(innerID); } class Witnessifier : public boost::static_visitor<bool> { public: CWallet * const pwallet; - CScriptID result; - - Witnessifier(CWallet *_pwallet) : pwallet(_pwallet) {} + CTxDestination result; + bool already_witness; - bool operator()(const CNoDestination &dest) const { return false; } + explicit Witnessifier(CWallet *_pwallet) : pwallet(_pwallet), already_witness(false) {} bool operator()(const CKeyID &keyID) { if (pwallet) { @@ -1134,14 +1166,12 @@ public: SignatureData sigs; // This check is to make sure that the script we created can actually be solved for and signed by us // if we were to have the private keys. This is just to make sure that the script is valid and that, - // if found in a transaction, we would still accept and relay that transcation. + // if found in a transaction, we would still accept and relay that transaction. if (!ProduceSignature(DummySignatureCreator(pwallet), witscript, sigs) || !VerifyScript(sigs.scriptSig, witscript, &sigs.scriptWitness, MANDATORY_SCRIPT_VERIFY_FLAGS | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, DummySignatureCreator(pwallet).Checker())) { return false; } - pwallet->AddCScript(witscript); - result = CScriptID(witscript); - return true; + return ExtractDestination(witscript, result); } return false; } @@ -1152,24 +1182,40 @@ public: int witnessversion; std::vector<unsigned char> witprog; if (subscript.IsWitnessProgram(witnessversion, witprog)) { - result = scriptID; + ExtractDestination(subscript, result); + already_witness = true; return true; } CScript witscript = GetScriptForWitness(subscript); SignatureData sigs; // This check is to make sure that the script we created can actually be solved for and signed by us // if we were to have the private keys. This is just to make sure that the script is valid and that, - // if found in a transaction, we would still accept and relay that transcation. + // if found in a transaction, we would still accept and relay that transaction. if (!ProduceSignature(DummySignatureCreator(pwallet), witscript, sigs) || !VerifyScript(sigs.scriptSig, witscript, &sigs.scriptWitness, MANDATORY_SCRIPT_VERIFY_FLAGS | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, DummySignatureCreator(pwallet).Checker())) { return false; } - pwallet->AddCScript(witscript); - result = CScriptID(witscript); - return true; + return ExtractDestination(witscript, result); } return false; } + + bool operator()(const WitnessV0KeyHash& id) + { + already_witness = true; + result = id; + return true; + } + + bool operator()(const WitnessV0ScriptHash& id) + { + already_witness = true; + result = id; + return true; + } + + template<typename T> + bool operator()(const T& dest) { return false; } }; UniValue addwitnessaddress(const JSONRPCRequest& request) @@ -1179,17 +1225,18 @@ UniValue addwitnessaddress(const JSONRPCRequest& request) return NullUniValue; } - if (request.fHelp || request.params.size() < 1 || request.params.size() > 1) + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) { - std::string msg = "addwitnessaddress \"address\"\n" + std::string msg = "addwitnessaddress \"address\" ( p2sh )\n" "\nAdd a witness address for a script (with pubkey or redeemscript known).\n" "It returns the witness script.\n" "\nArguments:\n" "1. \"address\" (string, required) An address known to the wallet\n" + "2. p2sh (bool, optional, default=true) Embed inside P2SH\n" "\nResult:\n" - "\"witnessaddress\", (string) The value of the new address (P2SH of witness script).\n" + "\"witnessaddress\", (string) The value of the new address (P2SH or BIP173).\n" "}\n" ; throw std::runtime_error(msg); @@ -1202,20 +1249,38 @@ UniValue addwitnessaddress(const JSONRPCRequest& request) } } - CBitcoinAddress address(request.params[0].get_str()); - if (!address.IsValid()) + CTxDestination dest = DecodeDestination(request.params[0].get_str()); + if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); + } + + bool p2sh = true; + if (!request.params[1].isNull()) { + p2sh = request.params[1].get_bool(); + } Witnessifier w(pwallet); - CTxDestination dest = address.Get(); bool ret = boost::apply_visitor(w, dest); if (!ret) { throw JSONRPCError(RPC_WALLET_ERROR, "Public key or redeemscript not known to wallet, or the key is uncompressed"); } - pwallet->SetAddressBook(w.result, "", "receive"); + CScript witprogram = GetScriptForDestination(w.result); + + if (p2sh) { + w.result = CScriptID(witprogram); + } - return CBitcoinAddress(w.result).ToString(); + if (w.already_witness) { + if (!(dest == w.result)) { + throw JSONRPCError(RPC_WALLET_ERROR, "Cannot convert between witness address types"); + } + } else { + pwallet->AddCScript(witprogram); + pwallet->SetAddressBook(w.result, "", "receive"); + } + + return EncodeDestination(w.result); } struct tallyitem @@ -1250,7 +1315,7 @@ UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bool fByA filter = filter | ISMINE_WATCH_ONLY; // Tally - std::map<CBitcoinAddress, tallyitem> mapTally; + std::map<CTxDestination, tallyitem> mapTally; for (const std::pair<uint256, CWalletTx>& pairWtx : pwallet->mapWallet) { const CWalletTx& wtx = pairWtx.second; @@ -1283,10 +1348,10 @@ UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bool fByA // Reply UniValue ret(UniValue::VARR); std::map<std::string, tallyitem> mapAccountTally; - for (const std::pair<CBitcoinAddress, CAddressBookData>& item : pwallet->mapAddressBook) { - const CBitcoinAddress& address = item.first; + for (const std::pair<CTxDestination, CAddressBookData>& item : pwallet->mapAddressBook) { + const CTxDestination& dest = item.first; const std::string& strAccount = item.second.name; - std::map<CBitcoinAddress, tallyitem>::iterator it = mapTally.find(address); + std::map<CTxDestination, tallyitem>::iterator it = mapTally.find(dest); if (it == mapTally.end() && !fIncludeEmpty) continue; @@ -1312,7 +1377,7 @@ UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bool fByA UniValue obj(UniValue::VOBJ); if(fIsWatchonly) obj.push_back(Pair("involvesWatchonly", true)); - obj.push_back(Pair("address", address.ToString())); + obj.push_back(Pair("address", EncodeDestination(dest))); 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))); @@ -1389,6 +1454,7 @@ UniValue listreceivedbyaddress(const JSONRPCRequest& request) + HelpExampleRpc("listreceivedbyaddress", "6, true, true") ); + ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); return ListReceived(pwallet, request.params, false); @@ -1428,6 +1494,7 @@ UniValue listreceivedbyaccount(const JSONRPCRequest& request) + HelpExampleRpc("listreceivedbyaccount", "6, true, true") ); + ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); return ListReceived(pwallet, request.params, true); @@ -1435,9 +1502,9 @@ UniValue listreceivedbyaccount(const JSONRPCRequest& request) static void MaybePushAddress(UniValue & entry, const CTxDestination &dest) { - CBitcoinAddress addr; - if (addr.Set(dest)) - entry.push_back(Pair("address", addr.ToString())); + if (IsValidDestination(dest)) { + entry.push_back(Pair("address", EncodeDestination(dest))); + } } /** @@ -1615,6 +1682,7 @@ UniValue listtransactions(const JSONRPCRequest& request) + HelpExampleRpc("listtransactions", "\"*\", 20, 100") ); + ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); std::string strAccount = "*"; @@ -1644,10 +1712,10 @@ UniValue listtransactions(const JSONRPCRequest& request) for (CWallet::TxItems::const_reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it) { CWalletTx *const pwtx = (*it).second.first; - if (pwtx != 0) + if (pwtx != nullptr) ListTransactions(pwallet, *pwtx, strAccount, 0, true, ret, filter); CAccountingEntry *const pacentry = (*it).second.second; - if (pacentry != 0) + if (pacentry != nullptr) AcentryToJSON(*pacentry, strAccount, ret); if ((int)ret.size() >= (nCount+nFrom)) break; @@ -1708,13 +1776,14 @@ UniValue listaccounts(const JSONRPCRequest& request) + HelpExampleRpc("listaccounts", "6") ); + ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); int nMinDepth = 1; - if (request.params.size() > 0) + if (!request.params[0].isNull()) nMinDepth = request.params[0].get_int(); isminefilter includeWatchonly = ISMINE_SPENDABLE; - if(request.params.size() > 1) + if(!request.params[1].isNull()) if(request.params[1].get_bool()) includeWatchonly = includeWatchonly | ISMINE_WATCH_ONLY; @@ -1816,6 +1885,7 @@ UniValue listsinceblock(const JSONRPCRequest& request) + HelpExampleRpc("listsinceblock", "\"000000000000000bacf66f7497b7dc45ef753ee9a7d38571037cdb1a57f663ad\", 6") ); + ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); const CBlockIndex* pindex = nullptr; // Block index of the specified block or the common ancestor, if the block provided was in a deactivated chain. @@ -1874,10 +1944,11 @@ UniValue listsinceblock(const JSONRPCRequest& request) throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk"); } for (const CTransactionRef& tx : block.vtx) { - if (pwallet->mapWallet.count(tx->GetHash()) > 0) { + auto it = pwallet->mapWallet.find(tx->GetHash()); + if (it != pwallet->mapWallet.end()) { // We want all transactions regardless of confirmation count to appear here, // even negative confirmation ones, hence the big negative. - ListTransactions(pwallet, pwallet->mapWallet[tx->GetHash()], "*", -100000000, true, removed, filter); + ListTransactions(pwallet, it->second, "*", -100000000, true, removed, filter); } } paltindex = paltindex->pprev; @@ -1946,6 +2017,7 @@ UniValue gettransaction(const JSONRPCRequest& request) + HelpExampleRpc("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"") ); + ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); uint256 hash; @@ -1957,10 +2029,11 @@ UniValue gettransaction(const JSONRPCRequest& request) filter = filter | ISMINE_WATCH_ONLY; UniValue entry(UniValue::VOBJ); - if (!pwallet->mapWallet.count(hash)) { + auto it = pwallet->mapWallet.find(hash); + if (it == pwallet->mapWallet.end()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id"); } - const CWalletTx& wtx = pwallet->mapWallet[hash]; + const CWalletTx& wtx = it->second; CAmount nCredit = wtx.GetCredit(filter); CAmount nDebit = wtx.GetDebit(filter); @@ -2006,6 +2079,7 @@ UniValue abandontransaction(const JSONRPCRequest& request) + HelpExampleRpc("abandontransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"") ); + ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); uint256 hash; @@ -2361,19 +2435,18 @@ UniValue lockunspent(const JSONRPCRequest& request) LOCK2(cs_main, pwallet->cs_wallet); - if (request.params.size() == 1) - RPCTypeCheck(request.params, {UniValue::VBOOL}); - else - RPCTypeCheck(request.params, {UniValue::VBOOL, UniValue::VARR}); + RPCTypeCheckArgument(request.params[0], UniValue::VBOOL); bool fUnlock = request.params[0].get_bool(); - if (request.params.size() == 1) { + if (request.params[1].isNull()) { if (fUnlock) pwallet->UnlockAllCoins(); return true; } + RPCTypeCheckArgument(request.params[1], UniValue::VARR); + UniValue outputs = request.params[1].get_array(); for (unsigned int idx = 0; idx < outputs.size(); idx++) { const UniValue& output = outputs[idx]; @@ -2439,6 +2512,7 @@ UniValue listlockunspent(const JSONRPCRequest& request) + HelpExampleRpc("listlockunspent", "") ); + ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); std::vector<COutPoint> vOutpts; @@ -2517,6 +2591,7 @@ UniValue getwalletinfo(const JSONRPCRequest& request) + HelpExampleRpc("getwalletinfo", "") ); + ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); UniValue obj(UniValue::VOBJ); @@ -2669,35 +2744,38 @@ UniValue listunspent(const JSONRPCRequest& request) + HelpExampleRpc("listunspent", "6, 9999999, [] , true, { \"minimumAmount\": 0.005 } ") ); + ObserveSafeMode(); + int nMinDepth = 1; - if (request.params.size() > 0 && !request.params[0].isNull()) { + if (!request.params[0].isNull()) { RPCTypeCheckArgument(request.params[0], UniValue::VNUM); nMinDepth = request.params[0].get_int(); } int nMaxDepth = 9999999; - if (request.params.size() > 1 && !request.params[1].isNull()) { + if (!request.params[1].isNull()) { RPCTypeCheckArgument(request.params[1], UniValue::VNUM); nMaxDepth = request.params[1].get_int(); } - std::set<CBitcoinAddress> setAddress; - if (request.params.size() > 2 && !request.params[2].isNull()) { + std::set<CTxDestination> destinations; + if (!request.params[2].isNull()) { RPCTypeCheckArgument(request.params[2], UniValue::VARR); UniValue inputs = request.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, std::string("Invalid Bitcoin address: ")+input.get_str()); - if (setAddress.count(address)) - throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ")+input.get_str()); - setAddress.insert(address); + CTxDestination dest = DecodeDestination(input.get_str()); + if (!IsValidDestination(dest)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ") + input.get_str()); + } + if (!destinations.insert(dest).second) { + throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ") + input.get_str()); + } } } bool include_unsafe = true; - if (request.params.size() > 3 && !request.params[3].isNull()) { + if (!request.params[3].isNull()) { RPCTypeCheckArgument(request.params[3], UniValue::VBOOL); include_unsafe = request.params[3].get_bool(); } @@ -2734,7 +2812,7 @@ UniValue listunspent(const JSONRPCRequest& request) const CScript& scriptPubKey = out.tx->tx->vout[out.i].scriptPubKey; bool fValidAddress = ExtractDestination(scriptPubKey, address); - if (setAddress.size() && (!fValidAddress || !setAddress.count(address))) + if (destinations.size() && (!fValidAddress || !destinations.count(address))) continue; UniValue entry(UniValue::VOBJ); @@ -2742,7 +2820,7 @@ UniValue listunspent(const JSONRPCRequest& request) entry.push_back(Pair("vout", out.i)); if (fValidAddress) { - entry.push_back(Pair("address", CBitcoinAddress(address).ToString())); + entry.push_back(Pair("address", EncodeDestination(address))); if (pwallet->mapAddressBook.count(address)) { entry.push_back(Pair("account", pwallet->mapAddressBook[address].name)); @@ -2797,7 +2875,7 @@ UniValue fundrawtransaction(const JSONRPCRequest& request) " \"changePosition\" (numeric, optional, default random) The index of the change output\n" " \"includeWatching\" (boolean, optional, default false) Also select inputs which are watch only\n" " \"lockUnspents\" (boolean, optional, default false) Lock selected unspent outputs\n" - " \"feeRate\" (numeric, optional, default not set: makes wallet determine the fee) Set a specific feerate (" + CURRENCY_UNIT + " per KB)\n" + " \"feeRate\" (numeric, optional, default not set: makes wallet determine the fee) Set a specific fee rate in " + CURRENCY_UNIT + "/kB\n" " \"subtractFeeFromOutputs\" (array, optional) A json array of integers.\n" " The fee will be equally deducted from the amount of each specified output.\n" " The outputs are specified by their zero-based index, before any change output is added.\n" @@ -2830,6 +2908,7 @@ UniValue fundrawtransaction(const JSONRPCRequest& request) + HelpExampleCli("sendrawtransaction", "\"signedtransactionhex\"") ); + ObserveSafeMode(); RPCTypeCheck(request.params, {UniValue::VSTR}); CCoinControl coinControl; @@ -2864,12 +2943,13 @@ UniValue fundrawtransaction(const JSONRPCRequest& request) true, true); if (options.exists("changeAddress")) { - CBitcoinAddress address(options["changeAddress"].get_str()); + CTxDestination dest = DecodeDestination(options["changeAddress"].get_str()); - if (!address.IsValid()) + if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "changeAddress must be a valid bitcoin address"); + } - coinControl.destChange = address.Get(); + coinControl.destChange = dest; } if (options.exists("changePosition")) @@ -3112,7 +3192,7 @@ UniValue generate(const JSONRPCRequest& request) int num_generate = request.params[0].get_int(); uint64_t max_tries = 1000000; - if (request.params.size() > 1 && !request.params[1].isNull()) { + if (!request.params[1].isNull()) { max_tries = request.params[1].get_int(); } @@ -3132,6 +3212,81 @@ UniValue generate(const JSONRPCRequest& request) return generateBlocks(coinbase_script, num_generate, max_tries, true); } +UniValue rescanblockchain(const JSONRPCRequest& request) +{ + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { + return NullUniValue; + } + + if (request.fHelp || request.params.size() > 2) { + throw std::runtime_error( + "rescanblockchain (\"start_height\") (\"stop_height\")\n" + "\nRescan the local blockchain for wallet related transactions.\n" + "\nArguments:\n" + "1. \"start_height\" (numeric, optional) block height where the rescan should start\n" + "2. \"stop_height\" (numeric, optional) the last block height that should be scanned\n" + "\nResult:\n" + "{\n" + " \"start_height\" (numeric) The block height where the rescan has started. If omitted, rescan started from the genesis block.\n" + " \"stop_height\" (numeric) The height of the last rescanned block. If omitted, rescan stopped at the chain tip.\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("rescanblockchain", "100000 120000") + + HelpExampleRpc("rescanblockchain", "100000, 120000") + ); + } + + LOCK2(cs_main, pwallet->cs_wallet); + + CBlockIndex *pindexStart = chainActive.Genesis(); + CBlockIndex *pindexStop = nullptr; + if (!request.params[0].isNull()) { + pindexStart = chainActive[request.params[0].get_int()]; + if (!pindexStart) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid start_height"); + } + } + + if (!request.params[1].isNull()) { + pindexStop = chainActive[request.params[1].get_int()]; + if (!pindexStop) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid stop_height"); + } + else if (pindexStop->nHeight < pindexStart->nHeight) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "stop_height must be greater then start_height"); + } + } + + // We can't rescan beyond non-pruned blocks, stop and throw an error + if (fPruneMode) { + CBlockIndex *block = pindexStop ? pindexStop : chainActive.Tip(); + while (block && block->nHeight >= pindexStart->nHeight) { + if (!(block->nStatus & BLOCK_HAVE_DATA)) { + throw JSONRPCError(RPC_MISC_ERROR, "Can't rescan beyond pruned data. Use RPC call getblockchaininfo to determine your pruned height."); + } + block = block->pprev; + } + } + + CBlockIndex *stopBlock = pwallet->ScanForWalletTransactions(pindexStart, pindexStop, true); + if (!stopBlock) { + if (pwallet->IsAbortingRescan()) { + throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted."); + } + // if we got a nullptr returned, ScanForWalletTransactions did rescan up to the requested stopindex + stopBlock = pindexStop ? pindexStop : chainActive.Tip(); + } + else { + throw JSONRPCError(RPC_MISC_ERROR, "Rescan failed. Potentially corrupted data files."); + } + + UniValue response(UniValue::VOBJ); + response.pushKV("start_height", pindexStart->nHeight); + response.pushKV("stop_height", stopBlock->nHeight); + return response; +} + extern UniValue abortrescan(const JSONRPCRequest& request); // in rpcdump.cpp extern UniValue dumpprivkey(const JSONRPCRequest& request); // in rpcdump.cpp extern UniValue importprivkey(const JSONRPCRequest& request); @@ -3142,69 +3297,68 @@ extern UniValue importwallet(const JSONRPCRequest& request); extern UniValue importprunedfunds(const JSONRPCRequest& request); extern UniValue removeprunedfunds(const JSONRPCRequest& request); extern UniValue importmulti(const JSONRPCRequest& request); +extern UniValue rescanblockchain(const JSONRPCRequest& request); static const CRPCCommand commands[] = -{ // category name actor (function) okSafeMode - // --------------------- ------------------------ ----------------------- ---------- - { "rawtransactions", "fundrawtransaction", &fundrawtransaction, false, {"hexstring","options"} }, - { "hidden", "resendwallettransactions", &resendwallettransactions, true, {} }, - { "wallet", "abandontransaction", &abandontransaction, false, {"txid"} }, - { "wallet", "abortrescan", &abortrescan, false, {} }, - { "wallet", "addmultisigaddress", &addmultisigaddress, true, {"nrequired","keys","account"} }, - { "wallet", "addwitnessaddress", &addwitnessaddress, true, {"address"} }, - { "wallet", "backupwallet", &backupwallet, true, {"destination"} }, - { "wallet", "bumpfee", &bumpfee, true, {"txid", "options"} }, - { "wallet", "dumpprivkey", &dumpprivkey, true, {"address"} }, - { "wallet", "dumpwallet", &dumpwallet, true, {"filename"} }, - { "wallet", "encryptwallet", &encryptwallet, true, {"passphrase"} }, - { "wallet", "getaccountaddress", &getaccountaddress, true, {"account"} }, - { "wallet", "getaccount", &getaccount, true, {"address"} }, - { "wallet", "getaddressesbyaccount", &getaddressesbyaccount, true, {"account"} }, - { "wallet", "getbalance", &getbalance, false, {"account","minconf","include_watchonly"} }, - { "wallet", "getnewaddress", &getnewaddress, true, {"account"} }, - { "wallet", "getrawchangeaddress", &getrawchangeaddress, true, {} }, - { "wallet", "getreceivedbyaccount", &getreceivedbyaccount, false, {"account","minconf"} }, - { "wallet", "getreceivedbyaddress", &getreceivedbyaddress, false, {"address","minconf"} }, - { "wallet", "gettransaction", &gettransaction, false, {"txid","include_watchonly"} }, - { "wallet", "getunconfirmedbalance", &getunconfirmedbalance, false, {} }, - { "wallet", "getwalletinfo", &getwalletinfo, false, {} }, - { "wallet", "importmulti", &importmulti, true, {"requests","options"} }, - { "wallet", "importprivkey", &importprivkey, true, {"privkey","label","rescan"} }, - { "wallet", "importwallet", &importwallet, true, {"filename"} }, - { "wallet", "importaddress", &importaddress, true, {"address","label","rescan","p2sh"} }, - { "wallet", "importprunedfunds", &importprunedfunds, true, {"rawtransaction","txoutproof"} }, - { "wallet", "importpubkey", &importpubkey, true, {"pubkey","label","rescan"} }, - { "wallet", "keypoolrefill", &keypoolrefill, true, {"newsize"} }, - { "wallet", "listaccounts", &listaccounts, false, {"minconf","include_watchonly"} }, - { "wallet", "listaddressgroupings", &listaddressgroupings, false, {} }, - { "wallet", "listlockunspent", &listlockunspent, false, {} }, - { "wallet", "listreceivedbyaccount", &listreceivedbyaccount, false, {"minconf","include_empty","include_watchonly"} }, - { "wallet", "listreceivedbyaddress", &listreceivedbyaddress, false, {"minconf","include_empty","include_watchonly"} }, - { "wallet", "listsinceblock", &listsinceblock, false, {"blockhash","target_confirmations","include_watchonly","include_removed"} }, - { "wallet", "listtransactions", &listtransactions, false, {"account","count","skip","include_watchonly"} }, - { "wallet", "listunspent", &listunspent, false, {"minconf","maxconf","addresses","include_unsafe","query_options"} }, - { "wallet", "listwallets", &listwallets, true, {} }, - { "wallet", "lockunspent", &lockunspent, true, {"unlock","transactions"} }, - { "wallet", "move", &movecmd, false, {"fromaccount","toaccount","amount","minconf","comment"} }, - { "wallet", "sendfrom", &sendfrom, false, {"fromaccount","toaddress","amount","minconf","comment","comment_to"} }, - { "wallet", "sendmany", &sendmany, false, {"fromaccount","amounts","minconf","comment","subtractfeefrom","replaceable","conf_target","estimate_mode"} }, - { "wallet", "sendtoaddress", &sendtoaddress, false, {"address","amount","comment","comment_to","subtractfeefromamount","replaceable","conf_target","estimate_mode"} }, - { "wallet", "setaccount", &setaccount, true, {"address","account"} }, - { "wallet", "settxfee", &settxfee, true, {"amount"} }, - { "wallet", "signmessage", &signmessage, true, {"address","message"} }, - { "wallet", "walletlock", &walletlock, true, {} }, - { "wallet", "walletpassphrasechange", &walletpassphrasechange, true, {"oldpassphrase","newpassphrase"} }, - { "wallet", "walletpassphrase", &walletpassphrase, true, {"passphrase","timeout"} }, - { "wallet", "removeprunedfunds", &removeprunedfunds, true, {"txid"} }, - - { "generating", "generate", &generate, true, {"nblocks","maxtries"} }, +{ // category name actor (function) argNames + // --------------------- ------------------------ ----------------------- ---------- + { "rawtransactions", "fundrawtransaction", &fundrawtransaction, {"hexstring","options"} }, + { "hidden", "resendwallettransactions", &resendwallettransactions, {} }, + { "wallet", "abandontransaction", &abandontransaction, {"txid"} }, + { "wallet", "abortrescan", &abortrescan, {} }, + { "wallet", "addmultisigaddress", &addmultisigaddress, {"nrequired","keys","account"} }, + { "wallet", "addwitnessaddress", &addwitnessaddress, {"address","p2sh"} }, + { "wallet", "backupwallet", &backupwallet, {"destination"} }, + { "wallet", "bumpfee", &bumpfee, {"txid", "options"} }, + { "wallet", "dumpprivkey", &dumpprivkey, {"address"} }, + { "wallet", "dumpwallet", &dumpwallet, {"filename"} }, + { "wallet", "encryptwallet", &encryptwallet, {"passphrase"} }, + { "wallet", "getaccountaddress", &getaccountaddress, {"account"} }, + { "wallet", "getaccount", &getaccount, {"address"} }, + { "wallet", "getaddressesbyaccount", &getaddressesbyaccount, {"account"} }, + { "wallet", "getbalance", &getbalance, {"account","minconf","include_watchonly"} }, + { "wallet", "getnewaddress", &getnewaddress, {"account"} }, + { "wallet", "getrawchangeaddress", &getrawchangeaddress, {} }, + { "wallet", "getreceivedbyaccount", &getreceivedbyaccount, {"account","minconf"} }, + { "wallet", "getreceivedbyaddress", &getreceivedbyaddress, {"address","minconf"} }, + { "wallet", "gettransaction", &gettransaction, {"txid","include_watchonly"} }, + { "wallet", "getunconfirmedbalance", &getunconfirmedbalance, {} }, + { "wallet", "getwalletinfo", &getwalletinfo, {} }, + { "wallet", "importmulti", &importmulti, {"requests","options"} }, + { "wallet", "importprivkey", &importprivkey, {"privkey","label","rescan"} }, + { "wallet", "importwallet", &importwallet, {"filename"} }, + { "wallet", "importaddress", &importaddress, {"address","label","rescan","p2sh"} }, + { "wallet", "importprunedfunds", &importprunedfunds, {"rawtransaction","txoutproof"} }, + { "wallet", "importpubkey", &importpubkey, {"pubkey","label","rescan"} }, + { "wallet", "keypoolrefill", &keypoolrefill, {"newsize"} }, + { "wallet", "listaccounts", &listaccounts, {"minconf","include_watchonly"} }, + { "wallet", "listaddressgroupings", &listaddressgroupings, {} }, + { "wallet", "listlockunspent", &listlockunspent, {} }, + { "wallet", "listreceivedbyaccount", &listreceivedbyaccount, {"minconf","include_empty","include_watchonly"} }, + { "wallet", "listreceivedbyaddress", &listreceivedbyaddress, {"minconf","include_empty","include_watchonly"} }, + { "wallet", "listsinceblock", &listsinceblock, {"blockhash","target_confirmations","include_watchonly","include_removed"} }, + { "wallet", "listtransactions", &listtransactions, {"account","count","skip","include_watchonly"} }, + { "wallet", "listunspent", &listunspent, {"minconf","maxconf","addresses","include_unsafe","query_options"} }, + { "wallet", "listwallets", &listwallets, {} }, + { "wallet", "lockunspent", &lockunspent, {"unlock","transactions"} }, + { "wallet", "move", &movecmd, {"fromaccount","toaccount","amount","minconf","comment"} }, + { "wallet", "sendfrom", &sendfrom, {"fromaccount","toaddress","amount","minconf","comment","comment_to"} }, + { "wallet", "sendmany", &sendmany, {"fromaccount","amounts","minconf","comment","subtractfeefrom","replaceable","conf_target","estimate_mode"} }, + { "wallet", "sendtoaddress", &sendtoaddress, {"address","amount","comment","comment_to","subtractfeefromamount","replaceable","conf_target","estimate_mode"} }, + { "wallet", "setaccount", &setaccount, {"address","account"} }, + { "wallet", "settxfee", &settxfee, {"amount"} }, + { "wallet", "signmessage", &signmessage, {"address","message"} }, + { "wallet", "walletlock", &walletlock, {} }, + { "wallet", "walletpassphrasechange", &walletpassphrasechange, {"oldpassphrase","newpassphrase"} }, + { "wallet", "walletpassphrase", &walletpassphrase, {"passphrase","timeout"} }, + { "wallet", "removeprunedfunds", &removeprunedfunds, {"txid"} }, + { "wallet", "rescanblockchain", &rescanblockchain, {"start_height", "stop_height"} }, + + { "generating", "generate", &generate, {"nblocks","maxtries"} }, }; void RegisterWalletRPCCommands(CRPCTable &t) { - if (gArgs.GetBoolArg("-disablewallet", false)) - return; - for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++) t.appendCommand(commands[vcidx].name, &commands[vcidx]); } |