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