diff --git a/doc/release-notes.md b/doc/release-notes.md
index 973a859db..1d3daba93 100644
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -1,16 +1,21 @@
Bitcoin ABC version 0.21.3 is now available from:
This release includes the following features and fixes:
- Fixed a bug where the `listtransactions` RPC was unable to list transactions
by a giving label. This functionality has been restored.
- MacOS versions earlier than 10.12 are no longer supported.
Additionally, Bitcoin ABC does not yet change appearance when macOS
"dark mode" is activated.
+New RPC methods
+------------
+ - `listwalletdir` returns a list of wallets in the wallet directory which is
+ configured with `-walletdir` parameter.
+
Low-level RPC changes
----------------------
- `-usehd` was removed in version 0.16. From that version onwards, all new
wallets created are hierarchical deterministic wallets. Version 0.18 makes
specifying `-usehd` invalid config.
diff --git a/src/dummywallet.cpp b/src/dummywallet.cpp
index 1923fccfe..53f566939 100644
--- a/src/dummywallet.cpp
+++ b/src/dummywallet.cpp
@@ -1,47 +1,55 @@
// Copyright (c) 2018 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
#include
#include
class CWallet;
class DummyWalletInit : public WalletInitInterface {
public:
bool HasWalletSupport() const override { return false; }
void AddWalletOptions() const override;
bool ParameterInteraction() const override { return true; }
void Construct(InitInterfaces &interfaces) const override {
LogPrintf("No wallet support compiled in!\n");
}
};
void DummyWalletInit::AddWalletOptions() const {
std::vector opts = {
"-avoidpartialspends", "-disablewallet", "-fallbackfee=",
"-keypool=", "-maxtxfee=", "-mintxfee=", "-paytxfee=",
"-rescan", "-salvagewallet", "-spendzeroconfchange", "-upgradewallet",
"-wallet=", "-walletbroadcast", "-walletdir=",
"-walletnotify=", "-zapwallettxes=",
// Wallet debug options
"-dblogsize=", "-flushwallet", "-privdb", "-walletrejectlongchains"};
gArgs.AddHiddenArgs(opts);
}
const WalletInitInterface &g_wallet_init_interface = DummyWalletInit();
+fs::path GetWalletDir() {
+ throw std::logic_error("Wallet function called in non-wallet build.");
+}
+
+std::vector ListWalletDir() {
+ throw std::logic_error("Wallet function called in non-wallet build.");
+}
+
std::vector> GetWallets() {
throw std::logic_error("Wallet function called in non-wallet build.");
}
namespace interfaces {
class Wallet;
std::unique_ptr MakeWallet(const std::shared_ptr &wallet) {
throw std::logic_error("Wallet function called in non-wallet build.");
}
} // namespace interfaces
diff --git a/src/interfaces/node.cpp b/src/interfaces/node.cpp
index 674412cc4..936971d57 100644
--- a/src/interfaces/node.cpp
+++ b/src/interfaces/node.cpp
@@ -1,314 +1,324 @@
// Copyright (c) 2018 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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#if defined(HAVE_CONFIG_H)
#include
#endif
#include
#include
class HTTPRPCRequestProcessor;
class CWallet;
+fs::path GetWalletDir();
+std::vector ListWalletDir();
std::vector> GetWallets();
namespace interfaces {
class Wallet;
namespace {
class NodeImpl : public Node {
public:
NodeImpl() { m_interfaces.chain = MakeChain(); }
bool parseParameters(int argc, const char *const argv[],
std::string &error) override {
return gArgs.ParseParameters(argc, argv, error);
}
bool readConfigFiles(std::string &error) override {
return gArgs.ReadConfigFiles(error);
}
bool softSetArg(const std::string &arg,
const std::string &value) override {
return gArgs.SoftSetArg(arg, value);
}
bool softSetBoolArg(const std::string &arg, bool value) override {
return gArgs.SoftSetBoolArg(arg, value);
}
void selectParams(const std::string &network) override {
SelectParams(network);
}
std::string getNetwork() override { return Params().NetworkIDString(); }
void initLogging() override { InitLogging(); }
void initParameterInteraction() override { InitParameterInteraction(); }
std::string getWarnings(const std::string &type) override {
return GetWarnings(type);
}
bool baseInitialize(Config &config) override {
return AppInitBasicSetup() && AppInitParameterInteraction(config) &&
AppInitSanityChecks() && AppInitLockDataDirectory();
}
bool
appInitMain(Config &config, RPCServer &rpcServer,
HTTPRPCRequestProcessor &httpRPCRequestProcessor) override {
return AppInitMain(config, rpcServer, httpRPCRequestProcessor,
m_interfaces);
}
void appShutdown() override {
Interrupt();
Shutdown(m_interfaces);
}
void startShutdown() override { StartShutdown(); }
bool shutdownRequested() override { return ShutdownRequested(); }
void mapPort(bool use_upnp) override {
if (use_upnp) {
StartMapPort();
} else {
InterruptMapPort();
StopMapPort();
}
}
void setupServerArgs() override { return SetupServerArgs(); }
bool getProxy(Network net, proxyType &proxy_info) override {
return GetProxy(net, proxy_info);
}
size_t getNodeCount(CConnman::NumConnections flags) override {
return g_connman ? g_connman->GetNodeCount(flags) : 0;
}
bool getNodesStats(NodesStats &stats) override {
stats.clear();
if (g_connman) {
std::vector stats_temp;
g_connman->GetNodeStats(stats_temp);
stats.reserve(stats_temp.size());
for (auto &node_stats_temp : stats_temp) {
stats.emplace_back(std::move(node_stats_temp), false,
CNodeStateStats());
}
// Try to retrieve the CNodeStateStats for each node.
TRY_LOCK(::cs_main, lockMain);
if (lockMain) {
for (auto &node_stats : stats) {
std::get<1>(node_stats) =
GetNodeStateStats(std::get<0>(node_stats).nodeid,
std::get<2>(node_stats));
}
}
return true;
}
return false;
}
bool getBanned(banmap_t &banmap) override {
if (g_banman) {
g_banman->GetBanned(banmap);
return true;
}
return false;
}
bool ban(const CNetAddr &net_addr, BanReason reason,
int64_t ban_time_offset) override {
if (g_banman) {
g_banman->Ban(net_addr, reason, ban_time_offset);
return true;
}
return false;
}
bool unban(const CSubNet &ip) override {
if (g_banman) {
g_banman->Unban(ip);
return true;
}
return false;
}
bool disconnect(const CNetAddr &net_addr) override {
if (g_connman) {
return g_connman->DisconnectNode(net_addr);
}
return false;
}
bool disconnect(NodeId id) override {
if (g_connman) {
return g_connman->DisconnectNode(id);
}
return false;
}
int64_t getTotalBytesRecv() override {
return g_connman ? g_connman->GetTotalBytesRecv() : 0;
}
int64_t getTotalBytesSent() override {
return g_connman ? g_connman->GetTotalBytesSent() : 0;
}
size_t getMempoolSize() override { return g_mempool.size(); }
size_t getMempoolDynamicUsage() override {
return g_mempool.DynamicMemoryUsage();
}
bool getHeaderTip(int &height, int64_t &block_time) override {
LOCK(::cs_main);
if (::pindexBestHeader) {
height = ::pindexBestHeader->nHeight;
block_time = ::pindexBestHeader->GetBlockTime();
return true;
}
return false;
}
int getNumBlocks() override {
LOCK(::cs_main);
return ::ChainActive().Height();
}
int64_t getLastBlockTime() override {
LOCK(::cs_main);
if (::ChainActive().Tip()) {
return ::ChainActive().Tip()->GetBlockTime();
}
// Genesis block's time of current network
return Params().GenesisBlock().GetBlockTime();
}
double getVerificationProgress() override {
const CBlockIndex *tip;
{
LOCK(::cs_main);
tip = ::ChainActive().Tip();
}
return GuessVerificationProgress(Params().TxData(), tip);
}
bool isInitialBlockDownload() override {
return IsInitialBlockDownload();
}
bool getReindex() override { return ::fReindex; }
bool getImporting() override { return ::fImporting; }
void setNetworkActive(bool active) override {
if (g_connman) {
g_connman->SetNetworkActive(active);
}
}
bool getNetworkActive() override {
return g_connman && g_connman->GetNetworkActive();
}
Amount getMaxTxFee() override { return ::maxTxFee; }
CFeeRate estimateSmartFee() override { return g_mempool.estimateFee(); }
CFeeRate getDustRelayFee() override { return ::dustRelayFee; }
UniValue executeRpc(Config &config, const std::string &command,
const UniValue ¶ms,
const std::string &uri) override {
JSONRPCRequest req;
req.params = params;
req.strMethod = command;
req.URI = uri;
return ::tableRPC.execute(config, req);
}
std::vector listRpcCommands() override {
return ::tableRPC.listCommands();
}
void rpcSetTimerInterfaceIfUnset(RPCTimerInterface *iface) override {
RPCSetTimerInterfaceIfUnset(iface);
}
void rpcUnsetTimerInterface(RPCTimerInterface *iface) override {
RPCUnsetTimerInterface(iface);
}
bool getUnspentOutput(const COutPoint &output, Coin &coin) override {
LOCK(::cs_main);
return ::pcoinsTip->GetCoin(output, coin);
}
+ std::string getWalletDir() override { return GetWalletDir().string(); }
+ std::vector listWalletDir() override {
+ std::vector paths;
+ for (auto &path : ListWalletDir()) {
+ paths.push_back(path.string());
+ }
+ return paths;
+ }
std::vector> getWallets() override {
std::vector> wallets;
for (const std::shared_ptr &wallet : GetWallets()) {
wallets.emplace_back(MakeWallet(wallet));
}
return wallets;
}
std::unique_ptr handleInitMessage(InitMessageFn fn) override {
return MakeHandler(::uiInterface.InitMessage_connect(fn));
}
std::unique_ptr handleMessageBox(MessageBoxFn fn) override {
return MakeHandler(::uiInterface.ThreadSafeMessageBox_connect(fn));
}
std::unique_ptr handleQuestion(QuestionFn fn) override {
return MakeHandler(::uiInterface.ThreadSafeQuestion_connect(fn));
}
std::unique_ptr
handleShowProgress(ShowProgressFn fn) override {
return MakeHandler(::uiInterface.ShowProgress_connect(fn));
}
std::unique_ptr handleLoadWallet(LoadWalletFn fn) override {
return MakeHandler(::uiInterface.LoadWallet_connect(
[fn](std::shared_ptr wallet) {
fn(MakeWallet(wallet));
}));
}
std::unique_ptr handleNotifyNumConnectionsChanged(
NotifyNumConnectionsChangedFn fn) override {
return MakeHandler(
::uiInterface.NotifyNumConnectionsChanged_connect(fn));
}
std::unique_ptr handleNotifyNetworkActiveChanged(
NotifyNetworkActiveChangedFn fn) override {
return MakeHandler(
::uiInterface.NotifyNetworkActiveChanged_connect(fn));
}
std::unique_ptr
handleNotifyAlertChanged(NotifyAlertChangedFn fn) override {
return MakeHandler(::uiInterface.NotifyAlertChanged_connect(fn));
}
std::unique_ptr
handleBannedListChanged(BannedListChangedFn fn) override {
return MakeHandler(::uiInterface.BannedListChanged_connect(fn));
}
std::unique_ptr
handleNotifyBlockTip(NotifyBlockTipFn fn) override {
return MakeHandler(::uiInterface.NotifyBlockTip_connect(
[fn](bool initial_download, const CBlockIndex *block) {
fn(initial_download, block->nHeight, block->GetBlockTime(),
GuessVerificationProgress(Params().TxData(), block));
}));
}
std::unique_ptr
handleNotifyHeaderTip(NotifyHeaderTipFn fn) override {
return MakeHandler(::uiInterface.NotifyHeaderTip_connect(
[fn](bool initial_download, const CBlockIndex *block) {
fn(initial_download, block->nHeight, block->GetBlockTime(),
GuessVerificationProgress(Params().TxData(), block));
}));
}
InitInterfaces m_interfaces;
};
} // namespace
std::unique_ptr MakeNode() {
return std::make_unique();
}
} // namespace interfaces
diff --git a/src/interfaces/node.h b/src/interfaces/node.h
index d9d8ef07d..c7e17bd86 100644
--- a/src/interfaces/node.h
+++ b/src/interfaces/node.h
@@ -1,257 +1,263 @@
// Copyright (c) 2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_INTERFACES_NODE_H
#define BITCOIN_INTERFACES_NODE_H
#include // For banmap_t
#include // For Amount
#include // For CConnman::NumConnections
#include // For Network
#include
#include
#include
#include
#include
#include
#include
class BanMan;
class CCoinControl;
class CFeeRate;
struct CNodeStateStats;
struct CNodeStats;
class Coin;
class Config;
class HTTPRPCRequestProcessor;
class proxyType;
class RPCServer;
class RPCTimerInterface;
class UniValue;
namespace interfaces {
class Handler;
class Wallet;
//! Top-level interface for a bitcoin node (bitcoind process).
class Node {
public:
virtual ~Node() {}
//! Set command line arguments.
virtual bool parseParameters(int argc, const char *const argv[],
std::string &error) = 0;
//! Set a command line argument if it doesn't already have a value
virtual bool softSetArg(const std::string &arg,
const std::string &value) = 0;
//! Set a command line boolean argument if it doesn't already have a value
virtual bool softSetBoolArg(const std::string &arg, bool value) = 0;
//! Load settings from configuration file.
virtual bool readConfigFiles(std::string &error) = 0;
//! Choose network parameters.
virtual void selectParams(const std::string &network) = 0;
//! Get network name.
virtual std::string getNetwork() = 0;
//! Init logging.
virtual void initLogging() = 0;
//! Init parameter interaction.
virtual void initParameterInteraction() = 0;
//! Get warnings.
virtual std::string getWarnings(const std::string &type) = 0;
//! Initialize app dependencies.
virtual bool baseInitialize(Config &config) = 0;
//! Start node.
virtual bool
appInitMain(Config &config, RPCServer &rpcServer,
HTTPRPCRequestProcessor &httpRPCRequestProcessor) = 0;
//! Stop node.
virtual void appShutdown() = 0;
//! Start shutdown.
virtual void startShutdown() = 0;
//! Return whether shutdown was requested.
virtual bool shutdownRequested() = 0;
//! Setup arguments
virtual void setupServerArgs() = 0;
//! Map port.
virtual void mapPort(bool use_upnp) = 0;
//! Get proxy.
virtual bool getProxy(Network net, proxyType &proxy_info) = 0;
//! Get number of connections.
virtual size_t getNodeCount(CConnman::NumConnections flags) = 0;
//! Get stats for connected nodes.
using NodesStats =
std::vector>;
virtual bool getNodesStats(NodesStats &stats) = 0;
//! Get ban map entries.
virtual bool getBanned(banmap_t &banmap) = 0;
//! Ban node.
virtual bool ban(const CNetAddr &net_addr, BanReason reason,
int64_t ban_time_offset) = 0;
//! Unban node.
virtual bool unban(const CSubNet &ip) = 0;
//! Disconnect node by address.
virtual bool disconnect(const CNetAddr &net_addr) = 0;
//! Disconnect node by id.
virtual bool disconnect(NodeId id) = 0;
//! Get total bytes recv.
virtual int64_t getTotalBytesRecv() = 0;
//! Get total bytes sent.
virtual int64_t getTotalBytesSent() = 0;
//! Get mempool size.
virtual size_t getMempoolSize() = 0;
//! Get mempool dynamic usage.
virtual size_t getMempoolDynamicUsage() = 0;
//! Get header tip height and time.
virtual bool getHeaderTip(int &height, int64_t &block_time) = 0;
//! Get num blocks.
virtual int getNumBlocks() = 0;
//! Get last block time.
virtual int64_t getLastBlockTime() = 0;
//! Get verification progress.
virtual double getVerificationProgress() = 0;
//! Is initial block download.
virtual bool isInitialBlockDownload() = 0;
//! Get reindex.
virtual bool getReindex() = 0;
//! Get importing.
virtual bool getImporting() = 0;
//! Set network active.
virtual void setNetworkActive(bool active) = 0;
//! Get network active.
virtual bool getNetworkActive() = 0;
//! Get max tx fee.
virtual Amount getMaxTxFee() = 0;
//! Estimate smart fee.
virtual CFeeRate estimateSmartFee() = 0;
//! Get dust relay fee.
virtual CFeeRate getDustRelayFee() = 0;
//! Execute rpc command.
virtual UniValue executeRpc(Config &config, const std::string &command,
const UniValue ¶ms,
const std::string &uri) = 0;
//! List rpc commands.
virtual std::vector listRpcCommands() = 0;
//! Set RPC timer interface if unset.
virtual void rpcSetTimerInterfaceIfUnset(RPCTimerInterface *iface) = 0;
//! Unset RPC timer interface.
virtual void rpcUnsetTimerInterface(RPCTimerInterface *iface) = 0;
//! Get unspent outputs associated with a transaction.
virtual bool getUnspentOutput(const COutPoint &output, Coin &coin) = 0;
+ //! Return default wallet directory.
+ virtual std::string getWalletDir() = 0;
+
+ //! Return available wallets in wallet directory.
+ virtual std::vector listWalletDir() = 0;
+
//! Return interfaces for accessing wallets (if any).
virtual std::vector> getWallets() = 0;
//! Register handler for init messages.
using InitMessageFn = std::function;
virtual std::unique_ptr handleInitMessage(InitMessageFn fn) = 0;
//! Register handler for message box messages.
using MessageBoxFn =
std::function;
virtual std::unique_ptr handleMessageBox(MessageBoxFn fn) = 0;
//! Register handler for question messages.
using QuestionFn = std::function;
virtual std::unique_ptr handleQuestion(QuestionFn fn) = 0;
//! Register handler for progress messages.
using ShowProgressFn = std::function;
virtual std::unique_ptr handleShowProgress(ShowProgressFn fn) = 0;
//! Register handler for load wallet messages.
using LoadWalletFn = std::function wallet)>;
virtual std::unique_ptr handleLoadWallet(LoadWalletFn fn) = 0;
//! Register handler for number of connections changed messages.
using NotifyNumConnectionsChangedFn =
std::function;
virtual std::unique_ptr
handleNotifyNumConnectionsChanged(NotifyNumConnectionsChangedFn fn) = 0;
//! Register handler for network active messages.
using NotifyNetworkActiveChangedFn =
std::function;
virtual std::unique_ptr
handleNotifyNetworkActiveChanged(NotifyNetworkActiveChangedFn fn) = 0;
//! Register handler for notify alert messages.
using NotifyAlertChangedFn = std::function;
virtual std::unique_ptr
handleNotifyAlertChanged(NotifyAlertChangedFn fn) = 0;
//! Register handler for ban list messages.
using BannedListChangedFn = std::function;
virtual std::unique_ptr
handleBannedListChanged(BannedListChangedFn fn) = 0;
//! Register handler for block tip messages.
using NotifyBlockTipFn =
std::function;
virtual std::unique_ptr
handleNotifyBlockTip(NotifyBlockTipFn fn) = 0;
//! Register handler for header tip messages.
using NotifyHeaderTipFn =
std::function;
virtual std::unique_ptr
handleNotifyHeaderTip(NotifyHeaderTipFn fn) = 0;
};
//! Return implementation of Node interface.
std::unique_ptr MakeNode();
} // namespace interfaces
#endif // BITCOIN_INTERFACES_NODE_H
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 9c6319273..01e99dfb2 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -1,4732 +1,4764 @@
// Copyright (c) 2010 Satoshi Nakamoto
// Copyright (c) 2009-2018 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
#include
#include // for GetConsensus.
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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;
}
bool GetWalletNameFromJSONRPCRequest(const JSONRPCRequest &request,
std::string &wallet_name) {
if (request.URI.substr(0, WALLET_ENDPOINT_BASE.size()) ==
WALLET_ENDPOINT_BASE) {
// wallet endpoint was used
wallet_name =
urlDecode(request.URI.substr(WALLET_ENDPOINT_BASE.size()));
return true;
}
return false;
}
std::shared_ptr
GetWalletForJSONRPCRequest(const JSONRPCRequest &request) {
std::string wallet_name;
if (GetWalletNameFromJSONRPCRequest(request, wallet_name)) {
std::shared_ptr pwallet = GetWallet(wallet_name);
if (!pwallet) {
throw JSONRPCError(
RPC_WALLET_NOT_FOUND,
"Requested wallet does not exist or is not loaded");
}
return pwallet;
}
std::vector> wallets = GetWallets();
return wallets.size() == 1 || (request.fHelp && wallets.size() > 0)
? wallets[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 (!HasWallets()) {
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.");
}
}
static void WalletTxToJSON(interfaces::Chain &chain,
interfaces::Chain::Lock &locked_chain,
const CWalletTx &wtx, UniValue &entry) {
int confirms = wtx.GetDepthInMainChain(locked_chain);
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",
LookupBlockIndex(wtx.hashBlock)->GetBlockTime());
} else {
entry.pushKV("trusted", wtx.IsTrusted(locked_chain));
}
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);
}
}
static 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) {
std::shared_ptr const wallet = GetWalletForJSONRPCRequest(request);
CWallet *const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() > 2) {
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", ""));
}
// Belt and suspenders check for disabled private keys
if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
throw JSONRPCError(RPC_WALLET_ERROR,
"Error: Private keys are disabled for this wallet");
}
LOCK(pwallet->cs_wallet);
if (!pwallet->CanGetAddresses()) {
throw JSONRPCError(RPC_WALLET_ERROR,
"Error: This wallet has no available keys");
}
// 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]);
}
OutputType output_type = pwallet->m_default_address_type;
if (!request.params[1].isNull()) {
if (!ParseOutputType(request.params[1].get_str(), output_type)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
strprintf("Unknown address type '%s'",
request.params[1].get_str()));
}
}
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");
}
pwallet->LearnRelatedScripts(newKey, output_type);
CTxDestination dest = GetDestinationForKey(newKey, output_type);
pwallet->SetAddressBook(dest, label, "receive");
return EncodeDestination(dest, config);
}
static UniValue getrawchangeaddress(const Config &config,
const JSONRPCRequest &request) {
std::shared_ptr const wallet = GetWalletForJSONRPCRequest(request);
CWallet *const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() > 1) {
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", ""));
}
// Belt and suspenders check for disabled private keys
if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
throw JSONRPCError(RPC_WALLET_ERROR,
"Error: Private keys are disabled for this wallet");
}
LOCK(pwallet->cs_wallet);
if (!pwallet->CanGetAddresses(true)) {
throw JSONRPCError(RPC_WALLET_ERROR,
"Error: This wallet has no available keys");
}
if (!pwallet->IsLocked()) {
pwallet->TopUpKeyPool();
}
OutputType output_type =
pwallet->m_default_change_type != OutputType::CHANGE_AUTO
? pwallet->m_default_change_type
: pwallet->m_default_address_type;
if (!request.params[0].isNull()) {
if (!ParseOutputType(request.params[0].get_str(), output_type)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
strprintf("Unknown address type '%s'",
request.params[0].get_str()));
}
}
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();
pwallet->LearnRelatedScripts(vchPubKey, output_type);
CTxDestination dest = GetDestinationForKey(vchPubKey, output_type);
return EncodeDestination(dest, config);
}
static UniValue setlabel(const Config &config, const JSONRPCRequest &request) {
std::shared_ptr const wallet = GetWalletForJSONRPCRequest(request);
CWallet *const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || 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 to "
"the address.\n"
"\nExamples:\n" +
HelpExampleCli("setlabel",
"\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"tabby\"") +
HelpExampleRpc(
"setlabel",
"\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", \"tabby\""));
}
LOCK(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 old_label = pwallet->mapAddressBook[dest].name;
std::string label = LabelFromValue(request.params[1]);
if (IsMine(*pwallet, dest)) {
pwallet->SetAddressBook(dest, label, "receive");
} else {
pwallet->SetAddressBook(dest, label, "send");
}
return NullUniValue;
}
static CTransactionRef SendMoney(interfaces::Chain::Lock &locked_chain,
CWallet *const pwallet,
const CTxDestination &address, Amount nValue,
bool fSubtractFeeFromAmount,
mapValue_t mapValue) {
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(locked_chain, 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 */,
reservekey, g_connman.get(), state)) {
strError =
strprintf("Error: The transaction was rejected! Reason given: %s",
FormatStateMessage(state));
throw JSONRPCError(RPC_WALLET_ERROR, strError);
}
return tx;
}
static UniValue sendtoaddress(const Config &config,
const JSONRPCRequest &request) {
std::shared_ptr const wallet = GetWalletForJSONRPCRequest(request);
CWallet *const pwallet = wallet.get();
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\""));
}
// 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();
auto locked_chain = pwallet->chain().lock();
LOCK(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[2].isNull() && !request.params[2].get_str().empty()) {
mapValue["comment"] = request.params[2].get_str();
}
if (!request.params[3].isNull() && !request.params[3].get_str().empty()) {
mapValue["to"] = request.params[3].get_str();
}
bool fSubtractFeeFromAmount = false;
if (!request.params[4].isNull()) {
fSubtractFeeFromAmount = request.params[4].get_bool();
}
EnsureWalletIsUnlocked(pwallet);
CTransactionRef tx = SendMoney(*locked_chain, pwallet, dest, nAmount,
fSubtractFeeFromAmount, std::move(mapValue));
return tx->GetId().GetHex();
}
static UniValue listaddressgroupings(const Config &config,
const JSONRPCRequest &request) {
std::shared_ptr const wallet = GetWalletForJSONRPCRequest(request);
CWallet *const pwallet = wallet.get();
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", ""));
}
// 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();
auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
UniValue jsonGroupings(UniValue::VARR);
std::map balances =
pwallet->GetAddressBalances(*locked_chain);
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, config));
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) {
std::shared_ptr const wallet = GetWalletForJSONRPCRequest(request);
CWallet *const pwallet = wallet.get();
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 a JSON-RPC call\n" +
HelpExampleRpc(
"signmessage",
"\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", \"my message\""));
}
auto locked_chain = pwallet->chain().lock();
LOCK(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.data(), vchSig.size());
}
static UniValue getreceivedbyaddress(const Config &config,
const JSONRPCRequest &request) {
std::shared_ptr const wallet = GetWalletForJSONRPCRequest(request);
CWallet *const pwallet = wallet.get();
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"));
}
// 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();
// Temporary, for ContextualCheckTransactionForCurrentBlock below. Removed
// in upcoming commit.
LockAnnotation lock(::cs_main);
auto locked_chain = pwallet->chain().lock();
LOCK(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.GetChainParams().GetConsensus(), *wtx.tx, state)) {
continue;
}
for (const CTxOut &txout : wtx.tx->vout) {
if (txout.scriptPubKey == scriptPubKey) {
if (wtx.GetDepthInMainChain(*locked_chain) >= nMinDepth) {
nAmount += txout.nValue;
}
}
}
}
return ValueFromAmount(nAmount);
}
static UniValue getreceivedbylabel(const Config &config,
const JSONRPCRequest &request) {
std::shared_ptr const wallet = GetWalletForJSONRPCRequest(request);
CWallet *const pwallet = wallet.get();
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