diff --git a/doc/release-notes.md b/doc/release-notes.md
index 37aeb58b82..587efab3c9 100644
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -1,5 +1,8 @@
Bitcoin ABC version 0.19.2 is now available from:
This release includes the following features and fixes:
+ - Added parameter `include_removed` to `listsinceblock` for better tracking of
+ transactions during a reorg. See `bitcoin-cli help listsinceblock` for more
+ details.
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index 78473fbeb9..32516f8b7d 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -1,225 +1,226 @@
// Copyright (c) 2010 Satoshi Nakamoto
// Copyright (c) 2009-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "rpc/client.h"
#include "rpc/protocol.h"
#include "util.h"
#include
#include
#include
class CRPCConvertParam {
public:
std::string methodName; //!< method whose params want conversion
int paramIdx; //!< 0-based idx of param to convert
std::string paramName; //!< parameter name
};
/**
* Specifiy a (method, idx, name) here if the argument is a non-string RPC
* argument and needs to be converted from JSON.
*
* @note Parameter indexes start from 0.
*/
static const CRPCConvertParam vRPCConvertParams[] = {
{"setmocktime", 0, "timestamp"},
{"generate", 0, "nblocks"},
{"generate", 1, "maxtries"},
{"generatetoaddress", 0, "nblocks"},
{"generatetoaddress", 2, "maxtries"},
{"getnetworkhashps", 0, "nblocks"},
{"getnetworkhashps", 1, "height"},
{"sendtoaddress", 1, "amount"},
{"sendtoaddress", 4, "subtractfeefromamount"},
{"settxfee", 0, "amount"},
{"getreceivedbyaddress", 1, "minconf"},
{"getreceivedbyaccount", 1, "minconf"},
{"getreceivedbylabel", 1, "minconf"},
{"listreceivedbyaddress", 0, "minconf"},
{"listreceivedbyaddress", 1, "include_empty"},
{"listreceivedbyaddress", 2, "include_watchonly"},
{"listreceivedbyaddress", 3, "address_filter"},
{"listreceivedbyaccount", 0, "minconf"},
{"listreceivedbyaccount", 1, "include_empty"},
{"listreceivedbyaccount", 2, "include_watchonly"},
{"listreceivedbylabel", 0, "minconf"},
{"listreceivedbylabel", 1, "include_empty"},
{"listreceivedbylabel", 2, "include_watchonly"},
{"getbalance", 1, "minconf"},
{"getbalance", 2, "include_watchonly"},
{"getblockhash", 0, "height"},
{"waitforblockheight", 0, "height"},
{"waitforblockheight", 1, "timeout"},
{"waitforblock", 1, "timeout"},
{"waitfornewblock", 0, "timeout"},
{"move", 2, "amount"},
{"move", 3, "minconf"},
{"sendfrom", 2, "amount"},
{"sendfrom", 3, "minconf"},
{"listtransactions", 1, "count"},
{"listtransactions", 2, "skip"},
{"listtransactions", 3, "include_watchonly"},
{"listaccounts", 0, "minconf"},
{"listaccounts", 1, "include_watchonly"},
{"walletpassphrase", 1, "timeout"},
{"getblocktemplate", 0, "template_request"},
{"listsinceblock", 1, "target_confirmations"},
{"listsinceblock", 2, "include_watchonly"},
+ {"listsinceblock", 3, "include_removed"},
{"sendmany", 1, "amounts"},
{"sendmany", 2, "minconf"},
{"sendmany", 4, "subtractfeefrom"},
{"addmultisigaddress", 0, "nrequired"},
{"addmultisigaddress", 1, "keys"},
{"createmultisig", 0, "nrequired"},
{"createmultisig", 1, "keys"},
{"listunspent", 0, "minconf"},
{"listunspent", 1, "maxconf"},
{"listunspent", 2, "addresses"},
{"listunspent", 4, "query_options"},
{"getblock", 1, "verbosity"},
{"getblockheader", 1, "verbose"},
{"getchaintxstats", 0, "nblocks"},
{"gettransaction", 1, "include_watchonly"},
{"getrawtransaction", 1, "verbose"},
{"createrawtransaction", 0, "inputs"},
{"createrawtransaction", 1, "outputs"},
{"createrawtransaction", 2, "locktime"},
{"signrawtransaction", 1, "prevtxs"},
{"signrawtransaction", 2, "privkeys"},
{"signrawtransactionwithkey", 1, "privkeys"},
{"signrawtransactionwithkey", 2, "prevtxs"},
{"signrawtransactionwithwallet", 1, "prevtxs"},
{"sendrawtransaction", 1, "allowhighfees"},
{"combinerawtransaction", 0, "txs"},
{"fundrawtransaction", 1, "options"},
{"gettxout", 1, "n"},
{"gettxout", 2, "include_mempool"},
{"gettxoutproof", 0, "txids"},
{"lockunspent", 0, "unlock"},
{"lockunspent", 1, "transactions"},
{"importprivkey", 2, "rescan"},
{"importaddress", 2, "rescan"},
{"importaddress", 3, "p2sh"},
{"importpubkey", 2, "rescan"},
{"importmulti", 0, "requests"},
{"importmulti", 1, "options"},
{"verifychain", 0, "checklevel"},
{"verifychain", 1, "nblocks"},
{"pruneblockchain", 0, "height"},
{"keypoolrefill", 0, "newsize"},
{"getrawmempool", 0, "verbose"},
{"estimatefee", 0, "nblocks"},
{"prioritisetransaction", 1, "priority_delta"},
{"prioritisetransaction", 2, "fee_delta"},
{"setban", 2, "bantime"},
{"setban", 3, "absolute"},
{"setnetworkactive", 0, "state"},
{"getmempoolancestors", 1, "verbose"},
{"getmempooldescendants", 1, "verbose"},
{"disconnectnode", 1, "nodeid"},
// Echo with conversion (For testing only)
{"echojson", 0, "arg0"},
{"echojson", 1, "arg1"},
{"echojson", 2, "arg2"},
{"echojson", 3, "arg3"},
{"echojson", 4, "arg4"},
{"echojson", 5, "arg5"},
{"echojson", 6, "arg6"},
{"echojson", 7, "arg7"},
{"echojson", 8, "arg8"},
{"echojson", 9, "arg9"},
{"rescanblockchain", 0, "start_height"},
{"rescanblockchain", 1, "stop_height"},
};
class CRPCConvertTable {
private:
std::set> members;
std::set> membersByName;
public:
CRPCConvertTable();
bool convert(const std::string &method, int idx) {
return (members.count(std::make_pair(method, idx)) > 0);
}
bool convert(const std::string &method, const std::string &name) {
return (membersByName.count(std::make_pair(method, name)) > 0);
}
};
CRPCConvertTable::CRPCConvertTable() {
const unsigned int n_elem =
(sizeof(vRPCConvertParams) / sizeof(vRPCConvertParams[0]));
for (unsigned int i = 0; i < n_elem; i++) {
members.insert(std::make_pair(vRPCConvertParams[i].methodName,
vRPCConvertParams[i].paramIdx));
membersByName.insert(std::make_pair(vRPCConvertParams[i].methodName,
vRPCConvertParams[i].paramName));
}
}
static CRPCConvertTable rpcCvtTable;
/**
* Non-RFC4627 JSON parser, accepts internal values (such as numbers, true,
* false, null) as well as objects and arrays.
*/
UniValue ParseNonRFCJSONValue(const std::string &strVal) {
UniValue jVal;
if (!jVal.read(std::string("[") + strVal + std::string("]")) ||
!jVal.isArray() || jVal.size() != 1)
throw std::runtime_error(std::string("Error parsing JSON:") + strVal);
return jVal[0];
}
UniValue RPCConvertValues(const std::string &strMethod,
const std::vector &strParams) {
UniValue params(UniValue::VARR);
for (unsigned int idx = 0; idx < strParams.size(); idx++) {
const std::string &strVal = strParams[idx];
if (!rpcCvtTable.convert(strMethod, idx)) {
// insert string value directly
params.push_back(strVal);
} else {
// parse string as JSON, insert bool/number/object/etc. value
params.push_back(ParseNonRFCJSONValue(strVal));
}
}
return params;
}
UniValue RPCConvertNamedValues(const std::string &strMethod,
const std::vector &strParams) {
UniValue params(UniValue::VOBJ);
for (const std::string &s : strParams) {
size_t pos = s.find("=");
if (pos == std::string::npos) {
throw(std::runtime_error("No '=' in named argument '" + s +
"', this needs to be present for every "
"argument (even if it is empty)"));
}
std::string name = s.substr(0, pos);
std::string value = s.substr(pos + 1);
if (!rpcCvtTable.convert(strMethod, name)) {
// insert string value directly
params.pushKV(name, value);
} else {
// parse string as JSON, insert bool/number/object/etc. value
params.pushKV(name, ParseNonRFCJSONValue(value));
}
}
return params;
}
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 4c69e5f1a8..d0cc512461 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -1,3904 +1,3964 @@
// Copyright (c) 2010 Satoshi Nakamoto
// Copyright (c) 2009-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "amount.h"
#include "chain.h"
#include "chainparams.h" // for GetConsensus.
#include "config.h"
#include "consensus/validation.h"
#include "core_io.h"
#include "dstencode.h"
#include "net.h"
#include "policy/fees.h"
#include "policy/policy.h"
#include "rpc/mining.h"
#include "rpc/misc.h"
#include "rpc/rawtransaction.h"
#include "rpc/safemode.h"
#include "rpc/server.h"
#include "timedata.h"
#include "util.h"
#include "utilmoneystr.h"
#include "validation.h"
#include "wallet/coincontrol.h"
#include "wallet/wallet.h"
#include "wallet/walletdb.h"
#include "wallet/walletutil.h"
// Input src/init.h (not wallet/init.h) for StartShutdown
#include
#include
#include
static const std::string WALLET_ENDPOINT_BASE = "/wallet/";
static std::string urlDecode(const std::string &urlEncoded) {
std::string res;
if (!urlEncoded.empty()) {
char *decoded = evhttp_uridecode(urlEncoded.c_str(), false, nullptr);
if (decoded) {
res = std::string(decoded);
free(decoded);
}
}
return res;
}
CWallet *GetWalletForJSONRPCRequest(const JSONRPCRequest &request) {
if (request.URI.substr(0, WALLET_ENDPOINT_BASE.size()) ==
WALLET_ENDPOINT_BASE) {
// wallet endpoint was used
std::string requestedWallet =
urlDecode(request.URI.substr(WALLET_ENDPOINT_BASE.size()));
for (CWalletRef pwallet : ::vpwallets) {
if (pwallet->GetName() == requestedWallet) {
return pwallet;
}
}
throw JSONRPCError(RPC_WALLET_NOT_FOUND,
"Requested wallet does not exist or is not loaded");
}
return ::vpwallets.size() == 1 || (request.fHelp && ::vpwallets.size() > 0)
? ::vpwallets[0]
: nullptr;
}
std::string HelpRequiringPassphrase(CWallet *const pwallet) {
return pwallet && pwallet->IsCrypted() ? "\nRequires wallet passphrase to "
"be set with walletpassphrase "
"call."
: "";
}
bool EnsureWalletIsAvailable(CWallet *const pwallet, bool avoidException) {
if (pwallet) {
return true;
}
if (avoidException) {
return false;
}
if (::vpwallets.empty()) {
// Note: It isn't currently possible to trigger this error because
// wallet RPC methods aren't registered unless a wallet is loaded. But
// this error is being kept as a precaution, because it's possible in
// the future that wallet RPC methods might get or remain registered
// when no wallets are loaded.
throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found (wallet "
"method is disabled because "
"no wallet is loaded)");
}
throw JSONRPCError(RPC_WALLET_NOT_SPECIFIED,
"Wallet file not specified (must request wallet RPC "
"through /wallet/ uri-path).");
}
void EnsureWalletIsUnlocked(CWallet *const pwallet) {
if (pwallet->IsLocked()) {
throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the "
"wallet passphrase with "
"walletpassphrase first.");
}
}
void WalletTxToJSON(const CWalletTx &wtx, UniValue &entry) {
int confirms = wtx.GetDepthInMainChain();
entry.pushKV("confirmations", confirms);
if (wtx.IsCoinBase()) {
entry.pushKV("generated", true);
}
if (confirms > 0) {
entry.pushKV("blockhash", wtx.hashBlock.GetHex());
entry.pushKV("blockindex", wtx.nIndex);
entry.pushKV("blocktime", mapBlockIndex[wtx.hashBlock]->GetBlockTime());
} else {
entry.pushKV("trusted", wtx.IsTrusted());
}
uint256 hash = wtx.GetId();
entry.pushKV("txid", hash.GetHex());
UniValue conflicts(UniValue::VARR);
for (const uint256 &conflict : wtx.GetConflicts()) {
conflicts.push_back(conflict.GetHex());
}
entry.pushKV("walletconflicts", conflicts);
entry.pushKV("time", wtx.GetTxTime());
entry.pushKV("timereceived", (int64_t)wtx.nTimeReceived);
for (const std::pair &item : wtx.mapValue) {
entry.pushKV(item.first, item.second);
}
}
std::string LabelFromValue(const UniValue &value) {
std::string label = value.get_str();
if (label == "*") {
throw JSONRPCError(RPC_WALLET_INVALID_LABEL_NAME, "Invalid label name");
}
return label;
}
static UniValue getnewaddress(const Config &config,
const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() > 1) {
throw std::runtime_error(
"getnewaddress ( \"label\" )\n"
"\nReturns a new Bitcoin address for receiving payments.\n"
"If 'label' is specified, it is added to the address book \n"
"so payments received with the address will be associated with "
"'label'.\n"
"\nArguments:\n"
"1. \"label\" (string, optional) The label name for the "
"address to be linked to. If not provided, the default label \"\" "
"is used. It can also be set to the empty string \"\" to represent "
"the default label. The label does not need to exist, it will be "
"created if there is no label by the given name.\n"
"\nResult:\n"
"\"address\" (string) The new bitcoin address\n"
"\nExamples:\n" +
HelpExampleRpc("getnewaddress", ""));
}
LOCK2(cs_main, pwallet->cs_wallet);
// Parse the label first so we don't generate a key if there's an error
std::string label;
if (!request.params[0].isNull()) {
label = LabelFromValue(request.params[0]);
}
if (!pwallet->IsLocked()) {
pwallet->TopUpKeyPool();
}
// Generate a new key that is added to wallet
CPubKey newKey;
if (!pwallet->GetKeyFromPool(newKey)) {
throw JSONRPCError(
RPC_WALLET_KEYPOOL_RAN_OUT,
"Error: Keypool ran out, please call keypoolrefill first");
}
CKeyID keyID = newKey.GetID();
pwallet->SetAddressBook(keyID, label, "receive");
return EncodeDestination(keyID);
}
CTxDestination GetLabelAddress(CWallet *const pwallet, const std::string &label,
bool bForceNew = false) {
CPubKey pubKey;
if (!pwallet->GetLabelAddress(pubKey, label, bForceNew)) {
throw JSONRPCError(
RPC_WALLET_KEYPOOL_RAN_OUT,
"Error: Keypool ran out, please call keypoolrefill first");
}
return pubKey.GetID();
}
UniValue getlabeladdress(const Config &config, const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
"getlabeladdress \"label\"\n"
"\nReturns the current Bitcoin address for receiving payments to "
"this label.\n"
"\nArguments:\n"
"1. \"label\" (string, required) The label name for the "
"address. It can also be set to the empty string \"\" to represent "
"the default label. The label does not need to exist, it will be "
"created and a new address created if there is no label by the "
"given name.\n"
"\nResult:\n"
"\"address\" (string) The label bitcoin address\n"
"\nExamples:\n" +
HelpExampleCli("getlabeladdress", "") +
HelpExampleCli("getlabeladdress", "\"\"") +
HelpExampleCli("getlabeladdress", "\"mylabel\"") +
HelpExampleRpc("getlabeladdress", "\"mylabel\""));
}
LOCK2(cs_main, pwallet->cs_wallet);
// Parse the label first so we don't generate a key if there's an error
std::string label = LabelFromValue(request.params[0]);
UniValue ret(UniValue::VSTR);
ret = EncodeDestination(GetLabelAddress(pwallet, label));
return ret;
}
static UniValue getrawchangeaddress(const Config &config,
const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() > 0) {
throw std::runtime_error(
"getrawchangeaddress\n"
"\nReturns a new Bitcoin address, for receiving change.\n"
"This is for use with raw transactions, NOT normal use.\n"
"\nResult:\n"
"\"address\" (string) The address\n"
"\nExamples:\n" +
HelpExampleCli("getrawchangeaddress", "") +
HelpExampleRpc("getrawchangeaddress", ""));
}
LOCK2(cs_main, pwallet->cs_wallet);
if (!pwallet->IsLocked()) {
pwallet->TopUpKeyPool();
}
CReserveKey reservekey(pwallet);
CPubKey vchPubKey;
if (!reservekey.GetReservedKey(vchPubKey, true)) {
throw JSONRPCError(
RPC_WALLET_KEYPOOL_RAN_OUT,
"Error: Keypool ran out, please call keypoolrefill first");
}
reservekey.KeepKey();
CKeyID keyID = vchPubKey.GetID();
return EncodeDestination(keyID);
}
UniValue setlabel(const Config &config, const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() < 1 ||
request.params.size() > 2) {
throw std::runtime_error(
"setlabel \"address\" \"label\"\n"
"\nSets the label associated with the given address.\n"
"\nArguments:\n"
"1. \"address\" (string, required) The bitcoin address to "
"be associated with a label.\n"
"2. \"label\" (string, required) The label to assign the "
"address to.\n"
"\nExamples:\n" +
HelpExampleCli("setlabel",
"\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"tabby\"") +
HelpExampleRpc(
"setlabel",
"\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", \"tabby\""));
}
LOCK2(cs_main, pwallet->cs_wallet);
CTxDestination dest =
DecodeDestination(request.params[0].get_str(), config.GetChainParams());
if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"Invalid Bitcoin address");
}
std::string label;
if (!request.params[1].isNull()) {
label = LabelFromValue(request.params[1]);
}
// Only add the label if the address is yours.
if (IsMine(*pwallet, dest)) {
// Detect when changing the label of an address that is the 'unused
// current key' of another label:
if (pwallet->mapAddressBook.count(dest)) {
std::string old_label = pwallet->mapAddressBook[dest].name;
if (dest == GetLabelAddress(pwallet, old_label)) {
GetLabelAddress(pwallet, old_label, true);
}
}
pwallet->SetAddressBook(dest, label, "receive");
} else {
throw JSONRPCError(RPC_MISC_ERROR,
"setlabel can only be used with own address");
}
return NullUniValue;
}
static UniValue getaccount(const Config &config,
const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
"getaccount \"address\"\n"
"\nDEPRECATED. Returns the account associated with the given "
"address.\n"
"\nArguments:\n"
"1. \"address\" (string, required) The bitcoin address for "
"account lookup.\n"
"\nResult:\n"
"\"accountname\" (string) the account address\n"
"\nExamples:\n" +
HelpExampleCli("getaccount",
"\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\"") +
HelpExampleRpc("getaccount",
"\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\""));
}
LOCK2(cs_main, pwallet->cs_wallet);
CTxDestination dest =
DecodeDestination(request.params[0].get_str(), config.GetChainParams());
if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"Invalid Bitcoin address");
}
std::string strAccount;
std::map::iterator mi =
pwallet->mapAddressBook.find(dest);
if (mi != pwallet->mapAddressBook.end() && !(*mi).second.name.empty()) {
strAccount = (*mi).second.name;
}
return strAccount;
}
static UniValue getaddressesbyaccount(const Config &config,
const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
"getaddressesbyaccount \"account\"\n"
"\nDEPRECATED. Returns the list of addresses for the given "
"account.\n"
"\nArguments:\n"
"1. \"account\" (string, required) The account name.\n"
"\nResult:\n"
"[ (json array of string)\n"
" \"address\" (string) a bitcoin address associated with "
"the given account\n"
" ,...\n"
"]\n"
"\nExamples:\n" +
HelpExampleCli("getaddressesbyaccount", "\"tabby\"") +
HelpExampleRpc("getaddressesbyaccount", "\"tabby\""));
}
LOCK2(cs_main, pwallet->cs_wallet);
std::string strAccount = LabelFromValue(request.params[0]);
// Find all addresses that have the given account
UniValue ret(UniValue::VARR);
for (const std::pair &item :
pwallet->mapAddressBook) {
const CTxDestination &dest = item.first;
const std::string &strName = item.second.name;
if (strName == strAccount) {
ret.push_back(EncodeDestination(dest));
}
}
return ret;
}
static CTransactionRef SendMoney(CWallet *const pwallet,
const CTxDestination &address, Amount nValue,
bool fSubtractFeeFromAmount,
mapValue_t mapValue, std::string fromAccount) {
Amount curBalance = pwallet->GetBalance();
// Check amount
if (nValue <= Amount::zero()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid amount");
}
if (nValue > curBalance) {
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds");
}
if (pwallet->GetBroadcastTransactions() && !g_connman) {
throw JSONRPCError(
RPC_CLIENT_P2P_DISABLED,
"Error: Peer-to-peer functionality missing or disabled");
}
// Parse Bitcoin address
CScript scriptPubKey = GetScriptForDestination(address);
// Create and send the transaction
CReserveKey reservekey(pwallet);
Amount nFeeRequired;
std::string strError;
std::vector vecSend;
int nChangePosRet = -1;
CRecipient recipient = {scriptPubKey, nValue, fSubtractFeeFromAmount};
vecSend.push_back(recipient);
CCoinControl coinControl;
CTransactionRef tx;
if (!pwallet->CreateTransaction(vecSend, tx, reservekey, nFeeRequired,
nChangePosRet, strError, coinControl)) {
if (!fSubtractFeeFromAmount && nValue + nFeeRequired > curBalance) {
strError = strprintf("Error: This transaction requires a "
"transaction fee of at least %s",
FormatMoney(nFeeRequired));
}
throw JSONRPCError(RPC_WALLET_ERROR, strError);
}
CValidationState state;
if (!pwallet->CommitTransaction(tx, std::move(mapValue), {} /* orderForm */,
std::move(fromAccount), reservekey,
g_connman.get(), state)) {
strError =
strprintf("Error: The transaction was rejected! Reason given: %s",
state.GetRejectReason());
throw JSONRPCError(RPC_WALLET_ERROR, strError);
}
return tx;
}
static UniValue sendtoaddress(const Config &config,
const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() < 2 ||
request.params.size() > 5) {
throw std::runtime_error(
"sendtoaddress \"address\" amount ( \"comment\" \"comment_to\" "
"subtractfeefromamount )\n"
"\nSend an amount to a given address.\n" +
HelpRequiringPassphrase(pwallet) +
"\nArguments:\n"
"1. \"address\" (string, required) The bitcoin address "
"to send to.\n"
"2. \"amount\" (numeric or string, required) The "
"amount in " +
CURRENCY_UNIT +
" to send. eg 0.1\n"
"3. \"comment\" (string, optional) A comment used to "
"store what the transaction is for. \n"
" This is not part of the transaction, "
"just kept in your wallet.\n"
"4. \"comment_to\" (string, optional) A comment to store "
"the name of the person or organization \n"
" to which you're sending the "
"transaction. This is not part of the \n"
" transaction, just kept in your "
"wallet.\n"
"5. subtractfeefromamount (boolean, optional, default=false) The "
"fee will be deducted from the amount being sent.\n"
" The recipient will receive less "
"bitcoins than you enter in the amount field.\n"
"\nResult:\n"
"\"txid\" (string) The transaction id.\n"
"\nExamples:\n" +
HelpExampleCli("sendtoaddress",
"\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1") +
HelpExampleCli("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvay"
"dd\" 0.1 \"donation\" \"seans "
"outpost\"") +
HelpExampleCli(
"sendtoaddress",
"\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1 \"\" \"\" true") +
HelpExampleRpc("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvay"
"dd\", 0.1, \"donation\", \"seans "
"outpost\""));
}
ObserveSafeMode();
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
LOCK2(cs_main, pwallet->cs_wallet);
CTxDestination dest =
DecodeDestination(request.params[0].get_str(), config.GetChainParams());
if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
}
// Amount
Amount nAmount = AmountFromValue(request.params[1]);
if (nAmount <= Amount::zero()) {
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send");
}
// Wallet comments
mapValue_t mapValue;
if (request.params.size() > 2 && !request.params[2].isNull() &&
!request.params[2].get_str().empty()) {
mapValue["comment"] = request.params[2].get_str();
}
if (request.params.size() > 3 && !request.params[3].isNull() &&
!request.params[3].get_str().empty()) {
mapValue["to"] = request.params[3].get_str();
}
bool fSubtractFeeFromAmount = false;
if (request.params.size() > 4) {
fSubtractFeeFromAmount = request.params[4].get_bool();
}
EnsureWalletIsUnlocked(pwallet);
CTransactionRef tx =
SendMoney(pwallet, dest, nAmount, fSubtractFeeFromAmount,
std::move(mapValue), {} /* fromAccount */);
return tx->GetId().GetHex();
}
static UniValue listaddressgroupings(const Config &config,
const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() != 0) {
throw std::runtime_error(
"listaddressgroupings\n"
"\nLists groups of addresses which have had their common "
"ownership\n"
"made public by common use as inputs or as the resulting change\n"
"in past transactions\n"
"\nResult:\n"
"[\n"
" [\n"
" [\n"
" \"address\", (string) The bitcoin address\n"
" amount, (numeric) The amount in " +
CURRENCY_UNIT +
"\n"
" \"label\" (string, optional) The label\n"
" ]\n"
" ,...\n"
" ]\n"
" ,...\n"
"]\n"
"\nExamples:\n" +
HelpExampleCli("listaddressgroupings", "") +
HelpExampleRpc("listaddressgroupings", ""));
}
ObserveSafeMode();
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
LOCK2(cs_main, pwallet->cs_wallet);
UniValue jsonGroupings(UniValue::VARR);
std::map balances = pwallet->GetAddressBalances();
for (const std::set &grouping :
pwallet->GetAddressGroupings()) {
UniValue jsonGrouping(UniValue::VARR);
for (const CTxDestination &address : grouping) {
UniValue addressInfo(UniValue::VARR);
addressInfo.push_back(EncodeDestination(address));
addressInfo.push_back(ValueFromAmount(balances[address]));
if (pwallet->mapAddressBook.find(address) !=
pwallet->mapAddressBook.end()) {
addressInfo.push_back(
pwallet->mapAddressBook.find(address)->second.name);
}
jsonGrouping.push_back(addressInfo);
}
jsonGroupings.push_back(jsonGrouping);
}
return jsonGroupings;
}
static UniValue signmessage(const Config &config,
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(
"signmessage \"address\" \"message\"\n"
"\nSign a message with the private key of an address" +
HelpRequiringPassphrase(pwallet) +
"\n"
"\nArguments:\n"
"1. \"address\" (string, required) The bitcoin address to "
"use for the private key.\n"
"2. \"message\" (string, required) The message to create a "
"signature of.\n"
"\nResult:\n"
"\"signature\" (string) The signature of the message "
"encoded in base 64\n"
"\nExamples:\n"
"\nUnlock the wallet for 30 seconds\n" +
HelpExampleCli("walletpassphrase", "\"mypassphrase\" 30") +
"\nCreate the signature\n" +
HelpExampleCli(
"signmessage",
"\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"my message\"") +
"\nVerify the signature\n" +
HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4"
"XX\" \"signature\" \"my "
"message\"") +
"\nAs json rpc\n" +
HelpExampleRpc(
"signmessage",
"\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", \"my message\""));
}
LOCK2(cs_main, pwallet->cs_wallet);
EnsureWalletIsUnlocked(pwallet);
std::string strAddress = request.params[0].get_str();
std::string strMessage = request.params[1].get_str();
CTxDestination dest =
DecodeDestination(strAddress, config.GetChainParams());
if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
}
const CKeyID *keyID = boost::get(&dest);
if (!keyID) {
throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
}
CKey key;
if (!pwallet->GetKey(*keyID, key)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available");
}
CHashWriter ss(SER_GETHASH, 0);
ss << strMessageMagic;
ss << strMessage;
std::vector vchSig;
if (!key.SignCompact(ss.GetHash(), vchSig)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed");
}
return EncodeBase64(&vchSig[0], vchSig.size());
}
static UniValue getreceivedbyaddress(const Config &config,
const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() < 1 ||
request.params.size() > 2) {
throw std::runtime_error(
"getreceivedbyaddress \"address\" ( minconf )\n"
"\nReturns the total amount received by the given address in "
"transactions with at least minconf confirmations.\n"
"\nArguments:\n"
"1. \"address\" (string, required) The bitcoin address for "
"transactions.\n"
"2. minconf (numeric, optional, default=1) Only "
"include transactions confirmed at least this many times.\n"
"\nResult:\n"
"amount (numeric) The total amount in " +
CURRENCY_UNIT +
" received at this address.\n"
"\nExamples:\n"
"\nThe amount from transactions with at least 1 confirmation\n" +
HelpExampleCli("getreceivedbyaddress",
"\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\"") +
"\nThe amount including unconfirmed transactions, zero "
"confirmations\n" +
HelpExampleCli("getreceivedbyaddress",
"\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" 0") +
"\nThe amount with at least 6 confirmations\n" +
HelpExampleCli("getreceivedbyaddress",
"\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" 6") +
"\nAs a json rpc call\n" +
HelpExampleRpc("getreceivedbyaddress",
"\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", 6"));
}
ObserveSafeMode();
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
LOCK2(cs_main, pwallet->cs_wallet);
// Bitcoin address
CTxDestination dest =
DecodeDestination(request.params[0].get_str(), config.GetChainParams());
if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"Invalid Bitcoin address");
}
CScript scriptPubKey = GetScriptForDestination(dest);
if (!IsMine(*pwallet, scriptPubKey)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Address not found in wallet");
}
// Minimum confirmations
int nMinDepth = 1;
if (!request.params[1].isNull()) {
nMinDepth = request.params[1].get_int();
}
// Tally
Amount nAmount = Amount::zero();
for (const std::pair &pairWtx : pwallet->mapWallet) {
const CWalletTx &wtx = pairWtx.second;
CValidationState state;
if (wtx.IsCoinBase() || !ContextualCheckTransactionForCurrentBlock(
config, *wtx.tx, state)) {
continue;
}
for (const CTxOut &txout : wtx.tx->vout) {
if (txout.scriptPubKey == scriptPubKey) {
if (wtx.GetDepthInMainChain() >= nMinDepth) {
nAmount += txout.nValue;
}
}
}
}
return ValueFromAmount(nAmount);
}
UniValue getreceivedbylabel(const Config &config,
const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() < 1 ||
request.params.size() > 2) {
throw std::runtime_error(
"getreceivedbylabel \"label\" ( minconf )\n"
"\nReturns the total amount received by addresses with