mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-10 10:52:31 -05:00
Merge bitcoin/bitcoin#23640: MOVEONLY: Move helper functions from rpcwallet to wallet/rpc/util
ff945e553a
MOVEONLY: Move utility functions from rpcwallet to wallet/rpc/util (Samuel Dobson)7b04a064f6
Introduce wallet/rpc/util (Samuel Dobson) Pull request description: This is part one of multiple to split up rpcwallet.cpp into smaller, more logical units. See #23622 for context and overall plan. I'll open PRs in stages to hopefully minimise conflicts. Can be reviewed with `--color-moved=dimmed-zebra` The end goal can be seen here: https://github.com/meshcollider/bitcoin/tree/202111_split_walletrpc ACKs for top commit: MarcoFalke: nice, ACKff945e553a
🐰 shaavan: ACKff945e553a
Tree-SHA512: 6e3d1de6db770fe2fca540c8c4f30183ab8258c26e3a1fb46937714d28818a52933eafbfcafe2995f6a6e2551a3f3dd3ec93363b81de7912c0d81f5749d1c86d
This commit is contained in:
commit
af1067c4b7
7 changed files with 165 additions and 139 deletions
|
@ -283,6 +283,7 @@ BITCOIN_CORE_H = \
|
||||||
wallet/load.h \
|
wallet/load.h \
|
||||||
wallet/receive.h \
|
wallet/receive.h \
|
||||||
wallet/rpcwallet.h \
|
wallet/rpcwallet.h \
|
||||||
|
wallet/rpc/util.h \
|
||||||
wallet/salvage.h \
|
wallet/salvage.h \
|
||||||
wallet/scriptpubkeyman.h \
|
wallet/scriptpubkeyman.h \
|
||||||
wallet/spend.h \
|
wallet/spend.h \
|
||||||
|
@ -410,6 +411,7 @@ libbitcoin_wallet_a_SOURCES = \
|
||||||
wallet/load.cpp \
|
wallet/load.cpp \
|
||||||
wallet/receive.cpp \
|
wallet/receive.cpp \
|
||||||
wallet/rpc/signmessage.cpp \
|
wallet/rpc/signmessage.cpp \
|
||||||
|
wallet/rpc/util.cpp \
|
||||||
wallet/rpcdump.cpp \
|
wallet/rpcdump.cpp \
|
||||||
wallet/rpcwallet.cpp \
|
wallet/rpcwallet.cpp \
|
||||||
wallet/scriptpubkeyman.cpp \
|
wallet/scriptpubkeyman.cpp \
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
#include <key_io.h>
|
#include <key_io.h>
|
||||||
#include <rpc/util.h>
|
#include <rpc/util.h>
|
||||||
#include <util/message.h>
|
#include <util/message.h>
|
||||||
#include <wallet/rpcwallet.h>
|
#include <wallet/rpc/util.h>
|
||||||
#include <wallet/wallet.h>
|
#include <wallet/wallet.h>
|
||||||
|
|
||||||
#include <univalue.h>
|
#include <univalue.h>
|
||||||
|
|
122
src/wallet/rpc/util.cpp
Normal file
122
src/wallet/rpc/util.cpp
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
// Copyright (c) 2011-2021 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 <wallet/rpc/util.h>
|
||||||
|
|
||||||
|
#include <rpc/util.h>
|
||||||
|
#include <util/url.h>
|
||||||
|
#include <wallet/context.h>
|
||||||
|
#include <wallet/wallet.h>
|
||||||
|
|
||||||
|
#include <univalue.h>
|
||||||
|
|
||||||
|
static const std::string WALLET_ENDPOINT_BASE = "/wallet/";
|
||||||
|
const std::string HELP_REQUIRING_PASSPHRASE{"\nRequires wallet passphrase to be set with walletpassphrase call if wallet is encrypted.\n"};
|
||||||
|
|
||||||
|
bool GetAvoidReuseFlag(const CWallet& wallet, const UniValue& param) {
|
||||||
|
bool can_avoid_reuse = wallet.IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE);
|
||||||
|
bool avoid_reuse = param.isNull() ? can_avoid_reuse : param.get_bool();
|
||||||
|
|
||||||
|
if (avoid_reuse && !can_avoid_reuse) {
|
||||||
|
throw JSONRPCError(RPC_WALLET_ERROR, "wallet does not have the \"avoid reuse\" feature enabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
return avoid_reuse;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Used by RPC commands that have an include_watchonly parameter.
|
||||||
|
* We default to true for watchonly wallets if include_watchonly isn't
|
||||||
|
* explicitly set.
|
||||||
|
*/
|
||||||
|
bool ParseIncludeWatchonly(const UniValue& include_watchonly, const CWallet& wallet)
|
||||||
|
{
|
||||||
|
if (include_watchonly.isNull()) {
|
||||||
|
// if include_watchonly isn't explicitly set, then check if we have a watchonly wallet
|
||||||
|
return wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise return whatever include_watchonly was set to
|
||||||
|
return include_watchonly.get_bool();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GetWalletNameFromJSONRPCRequest(const JSONRPCRequest& request, std::string& wallet_name)
|
||||||
|
{
|
||||||
|
if (URL_DECODE && request.URI.substr(0, WALLET_ENDPOINT_BASE.size()) == WALLET_ENDPOINT_BASE) {
|
||||||
|
// wallet endpoint was used
|
||||||
|
wallet_name = URL_DECODE(request.URI.substr(WALLET_ENDPOINT_BASE.size()));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<CWallet> GetWalletForJSONRPCRequest(const JSONRPCRequest& request)
|
||||||
|
{
|
||||||
|
CHECK_NONFATAL(request.mode == JSONRPCRequest::EXECUTE);
|
||||||
|
WalletContext& context = EnsureWalletContext(request.context);
|
||||||
|
|
||||||
|
std::string wallet_name;
|
||||||
|
if (GetWalletNameFromJSONRPCRequest(request, wallet_name)) {
|
||||||
|
const std::shared_ptr<CWallet> pwallet = GetWallet(context, wallet_name);
|
||||||
|
if (!pwallet) throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Requested wallet does not exist or is not loaded");
|
||||||
|
return pwallet;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<CWallet>> wallets = GetWallets(context);
|
||||||
|
if (wallets.size() == 1) {
|
||||||
|
return wallets[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wallets.empty()) {
|
||||||
|
throw JSONRPCError(
|
||||||
|
RPC_WALLET_NOT_FOUND, "No wallet is loaded. Load a wallet using loadwallet or create a new one with createwallet. (Note: A default wallet is no longer automatically created)");
|
||||||
|
}
|
||||||
|
throw JSONRPCError(RPC_WALLET_NOT_SPECIFIED,
|
||||||
|
"Wallet file not specified (must request wallet RPC through /wallet/<filename> uri-path).");
|
||||||
|
}
|
||||||
|
|
||||||
|
void EnsureWalletIsUnlocked(const CWallet& wallet)
|
||||||
|
{
|
||||||
|
if (wallet.IsLocked()) {
|
||||||
|
throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WalletContext& EnsureWalletContext(const std::any& context)
|
||||||
|
{
|
||||||
|
auto wallet_context = util::AnyPtr<WalletContext>(context);
|
||||||
|
if (!wallet_context) {
|
||||||
|
throw JSONRPCError(RPC_INTERNAL_ERROR, "Wallet context not found");
|
||||||
|
}
|
||||||
|
return *wallet_context;
|
||||||
|
}
|
||||||
|
|
||||||
|
// also_create should only be set to true only when the RPC is expected to add things to a blank wallet and make it no longer blank
|
||||||
|
LegacyScriptPubKeyMan& EnsureLegacyScriptPubKeyMan(CWallet& wallet, bool also_create)
|
||||||
|
{
|
||||||
|
LegacyScriptPubKeyMan* spk_man = wallet.GetLegacyScriptPubKeyMan();
|
||||||
|
if (!spk_man && also_create) {
|
||||||
|
spk_man = wallet.GetOrCreateLegacyScriptPubKeyMan();
|
||||||
|
}
|
||||||
|
if (!spk_man) {
|
||||||
|
throw JSONRPCError(RPC_WALLET_ERROR, "This type of wallet does not support this command");
|
||||||
|
}
|
||||||
|
return *spk_man;
|
||||||
|
}
|
||||||
|
|
||||||
|
const LegacyScriptPubKeyMan& EnsureConstLegacyScriptPubKeyMan(const CWallet& wallet)
|
||||||
|
{
|
||||||
|
const LegacyScriptPubKeyMan* spk_man = wallet.GetLegacyScriptPubKeyMan();
|
||||||
|
if (!spk_man) {
|
||||||
|
throw JSONRPCError(RPC_WALLET_ERROR, "This type of wallet does not support this command");
|
||||||
|
}
|
||||||
|
return *spk_man;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
38
src/wallet/rpc/util.h
Normal file
38
src/wallet/rpc/util.h
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
// Copyright (c) 2017-2021 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_WALLET_RPC_UTIL_H
|
||||||
|
#define BITCOIN_WALLET_RPC_UTIL_H
|
||||||
|
|
||||||
|
#include <any>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class CWallet;
|
||||||
|
class JSONRPCRequest;
|
||||||
|
class LegacyScriptPubKeyMan;
|
||||||
|
class UniValue;
|
||||||
|
struct WalletContext;
|
||||||
|
|
||||||
|
extern const std::string HELP_REQUIRING_PASSPHRASE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Figures out what wallet, if any, to use for a JSONRPCRequest.
|
||||||
|
*
|
||||||
|
* @param[in] request JSONRPCRequest that wishes to access a wallet
|
||||||
|
* @return nullptr if no wallet should be used, or a pointer to the CWallet
|
||||||
|
*/
|
||||||
|
std::shared_ptr<CWallet> GetWalletForJSONRPCRequest(const JSONRPCRequest& request);
|
||||||
|
bool GetWalletNameFromJSONRPCRequest(const JSONRPCRequest& request, std::string& wallet_name);
|
||||||
|
|
||||||
|
void EnsureWalletIsUnlocked(const CWallet&);
|
||||||
|
WalletContext& EnsureWalletContext(const std::any& context);
|
||||||
|
LegacyScriptPubKeyMan& EnsureLegacyScriptPubKeyMan(CWallet& wallet, bool also_create = false);
|
||||||
|
const LegacyScriptPubKeyMan& EnsureConstLegacyScriptPubKeyMan(const CWallet& wallet);
|
||||||
|
|
||||||
|
bool GetAvoidReuseFlag(const CWallet& wallet, const UniValue& param);
|
||||||
|
bool ParseIncludeWatchonly(const UniValue& include_watchonly, const CWallet& wallet);
|
||||||
|
std::string LabelFromValue(const UniValue& value);
|
||||||
|
|
||||||
|
#endif // BITCOIN_WALLET_RPC_UTIL_H
|
|
@ -17,7 +17,7 @@
|
||||||
#include <util/system.h>
|
#include <util/system.h>
|
||||||
#include <util/time.h>
|
#include <util/time.h>
|
||||||
#include <util/translation.h>
|
#include <util/translation.h>
|
||||||
#include <wallet/rpcwallet.h>
|
#include <wallet/rpc/util.h>
|
||||||
#include <wallet/wallet.h>
|
#include <wallet/wallet.h>
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
#include <wallet/load.h>
|
#include <wallet/load.h>
|
||||||
#include <wallet/receive.h>
|
#include <wallet/receive.h>
|
||||||
#include <wallet/rpcwallet.h>
|
#include <wallet/rpcwallet.h>
|
||||||
|
#include <wallet/rpc/util.h>
|
||||||
#include <wallet/spend.h>
|
#include <wallet/spend.h>
|
||||||
#include <wallet/wallet.h>
|
#include <wallet/wallet.h>
|
||||||
#include <wallet/walletdb.h>
|
#include <wallet/walletdb.h>
|
||||||
|
@ -46,36 +47,6 @@
|
||||||
|
|
||||||
using interfaces::FoundBlock;
|
using interfaces::FoundBlock;
|
||||||
|
|
||||||
static const std::string WALLET_ENDPOINT_BASE = "/wallet/";
|
|
||||||
const std::string HELP_REQUIRING_PASSPHRASE{"\nRequires wallet passphrase to be set with walletpassphrase call if wallet is encrypted.\n"};
|
|
||||||
|
|
||||||
static inline bool GetAvoidReuseFlag(const CWallet& wallet, const UniValue& param) {
|
|
||||||
bool can_avoid_reuse = wallet.IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE);
|
|
||||||
bool avoid_reuse = param.isNull() ? can_avoid_reuse : param.get_bool();
|
|
||||||
|
|
||||||
if (avoid_reuse && !can_avoid_reuse) {
|
|
||||||
throw JSONRPCError(RPC_WALLET_ERROR, "wallet does not have the \"avoid reuse\" feature enabled");
|
|
||||||
}
|
|
||||||
|
|
||||||
return avoid_reuse;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/** Used by RPC commands that have an include_watchonly parameter.
|
|
||||||
* We default to true for watchonly wallets if include_watchonly isn't
|
|
||||||
* explicitly set.
|
|
||||||
*/
|
|
||||||
static bool ParseIncludeWatchonly(const UniValue& include_watchonly, const CWallet& wallet)
|
|
||||||
{
|
|
||||||
if (include_watchonly.isNull()) {
|
|
||||||
// if include_watchonly isn't explicitly set, then check if we have a watchonly wallet
|
|
||||||
return wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
|
|
||||||
}
|
|
||||||
|
|
||||||
// otherwise return whatever include_watchonly was set to
|
|
||||||
return include_watchonly.get_bool();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/** Checks if a CKey is in the given CWallet compressed or otherwise*/
|
/** Checks if a CKey is in the given CWallet compressed or otherwise*/
|
||||||
bool HaveKey(const SigningProvider& wallet, const CKey& key)
|
bool HaveKey(const SigningProvider& wallet, const CKey& key)
|
||||||
|
@ -85,79 +56,6 @@ bool HaveKey(const SigningProvider& wallet, const CKey& key)
|
||||||
return wallet.HaveKey(key.GetPubKey().GetID()) || wallet.HaveKey(key2.GetPubKey().GetID());
|
return wallet.HaveKey(key.GetPubKey().GetID()) || wallet.HaveKey(key2.GetPubKey().GetID());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GetWalletNameFromJSONRPCRequest(const JSONRPCRequest& request, std::string& wallet_name)
|
|
||||||
{
|
|
||||||
if (URL_DECODE && request.URI.substr(0, WALLET_ENDPOINT_BASE.size()) == WALLET_ENDPOINT_BASE) {
|
|
||||||
// wallet endpoint was used
|
|
||||||
wallet_name = URL_DECODE(request.URI.substr(WALLET_ENDPOINT_BASE.size()));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<CWallet> GetWalletForJSONRPCRequest(const JSONRPCRequest& request)
|
|
||||||
{
|
|
||||||
CHECK_NONFATAL(request.mode == JSONRPCRequest::EXECUTE);
|
|
||||||
WalletContext& context = EnsureWalletContext(request.context);
|
|
||||||
|
|
||||||
std::string wallet_name;
|
|
||||||
if (GetWalletNameFromJSONRPCRequest(request, wallet_name)) {
|
|
||||||
const std::shared_ptr<CWallet> pwallet = GetWallet(context, wallet_name);
|
|
||||||
if (!pwallet) throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Requested wallet does not exist or is not loaded");
|
|
||||||
return pwallet;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::shared_ptr<CWallet>> wallets = GetWallets(context);
|
|
||||||
if (wallets.size() == 1) {
|
|
||||||
return wallets[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wallets.empty()) {
|
|
||||||
throw JSONRPCError(
|
|
||||||
RPC_WALLET_NOT_FOUND, "No wallet is loaded. Load a wallet using loadwallet or create a new one with createwallet. (Note: A default wallet is no longer automatically created)");
|
|
||||||
}
|
|
||||||
throw JSONRPCError(RPC_WALLET_NOT_SPECIFIED,
|
|
||||||
"Wallet file not specified (must request wallet RPC through /wallet/<filename> uri-path).");
|
|
||||||
}
|
|
||||||
|
|
||||||
void EnsureWalletIsUnlocked(const CWallet& wallet)
|
|
||||||
{
|
|
||||||
if (wallet.IsLocked()) {
|
|
||||||
throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
WalletContext& EnsureWalletContext(const std::any& context)
|
|
||||||
{
|
|
||||||
auto wallet_context = util::AnyPtr<WalletContext>(context);
|
|
||||||
if (!wallet_context) {
|
|
||||||
throw JSONRPCError(RPC_INTERNAL_ERROR, "Wallet context not found");
|
|
||||||
}
|
|
||||||
return *wallet_context;
|
|
||||||
}
|
|
||||||
|
|
||||||
// also_create should only be set to true only when the RPC is expected to add things to a blank wallet and make it no longer blank
|
|
||||||
LegacyScriptPubKeyMan& EnsureLegacyScriptPubKeyMan(CWallet& wallet, bool also_create)
|
|
||||||
{
|
|
||||||
LegacyScriptPubKeyMan* spk_man = wallet.GetLegacyScriptPubKeyMan();
|
|
||||||
if (!spk_man && also_create) {
|
|
||||||
spk_man = wallet.GetOrCreateLegacyScriptPubKeyMan();
|
|
||||||
}
|
|
||||||
if (!spk_man) {
|
|
||||||
throw JSONRPCError(RPC_WALLET_ERROR, "This type of wallet does not support this command");
|
|
||||||
}
|
|
||||||
return *spk_man;
|
|
||||||
}
|
|
||||||
|
|
||||||
const LegacyScriptPubKeyMan& EnsureConstLegacyScriptPubKeyMan(const CWallet& wallet)
|
|
||||||
{
|
|
||||||
const LegacyScriptPubKeyMan* spk_man = wallet.GetLegacyScriptPubKeyMan();
|
|
||||||
if (!spk_man) {
|
|
||||||
throw JSONRPCError(RPC_WALLET_ERROR, "This type of wallet does not support this command");
|
|
||||||
}
|
|
||||||
return *spk_man;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void WalletTxToJSON(const CWallet& wallet, const CWalletTx& wtx, UniValue& entry)
|
static void WalletTxToJSON(const CWallet& wallet, const CWalletTx& wtx, UniValue& entry)
|
||||||
{
|
{
|
||||||
interfaces::Chain& chain = wallet.chain();
|
interfaces::Chain& chain = wallet.chain();
|
||||||
|
@ -200,13 +98,6 @@ static void WalletTxToJSON(const CWallet& wallet, const CWalletTx& wtx, UniValue
|
||||||
entry.pushKV(item.first, item.second);
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update coin control with fee estimation based on the given parameters
|
* Update coin control with fee estimation based on the given parameters
|
||||||
|
|
|
@ -7,37 +7,10 @@
|
||||||
|
|
||||||
#include <span.h>
|
#include <span.h>
|
||||||
|
|
||||||
#include <any>
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
class CRPCCommand;
|
class CRPCCommand;
|
||||||
class CWallet;
|
|
||||||
class JSONRPCRequest;
|
|
||||||
class LegacyScriptPubKeyMan;
|
|
||||||
class UniValue;
|
|
||||||
class CTransaction;
|
|
||||||
struct PartiallySignedTransaction;
|
|
||||||
struct WalletContext;
|
|
||||||
|
|
||||||
extern const std::string HELP_REQUIRING_PASSPHRASE;
|
|
||||||
|
|
||||||
Span<const CRPCCommand> GetWalletRPCCommands();
|
Span<const CRPCCommand> GetWalletRPCCommands();
|
||||||
|
|
||||||
/**
|
|
||||||
* Figures out what wallet, if any, to use for a JSONRPCRequest.
|
|
||||||
*
|
|
||||||
* @param[in] request JSONRPCRequest that wishes to access a wallet
|
|
||||||
* @return nullptr if no wallet should be used, or a pointer to the CWallet
|
|
||||||
*/
|
|
||||||
std::shared_ptr<CWallet> GetWalletForJSONRPCRequest(const JSONRPCRequest& request);
|
|
||||||
|
|
||||||
void EnsureWalletIsUnlocked(const CWallet&);
|
|
||||||
WalletContext& EnsureWalletContext(const std::any& context);
|
|
||||||
LegacyScriptPubKeyMan& EnsureLegacyScriptPubKeyMan(CWallet& wallet, bool also_create = false);
|
|
||||||
const LegacyScriptPubKeyMan& EnsureConstLegacyScriptPubKeyMan(const CWallet& wallet);
|
|
||||||
|
|
||||||
RPCHelpMan getaddressinfo();
|
RPCHelpMan getaddressinfo();
|
||||||
RPCHelpMan signrawtransactionwithwallet();
|
RPCHelpMan signrawtransactionwithwallet();
|
||||||
#endif // BITCOIN_WALLET_RPCWALLET_H
|
#endif // BITCOIN_WALLET_RPCWALLET_H
|
||||||
|
|
Loading…
Add table
Reference in a new issue