diff options
Diffstat (limited to 'src/wallet/rpc')
-rw-r--r-- | src/wallet/rpc/addresses.cpp | 21 | ||||
-rw-r--r-- | src/wallet/rpc/backup.cpp | 28 | ||||
-rw-r--r-- | src/wallet/rpc/coins.cpp | 6 | ||||
-rw-r--r-- | src/wallet/rpc/encrypt.cpp | 2 | ||||
-rw-r--r-- | src/wallet/rpc/signmessage.cpp | 2 | ||||
-rw-r--r-- | src/wallet/rpc/spend.cpp | 85 | ||||
-rw-r--r-- | src/wallet/rpc/transactions.cpp | 6 | ||||
-rw-r--r-- | src/wallet/rpc/util.cpp | 20 | ||||
-rw-r--r-- | src/wallet/rpc/util.h | 10 | ||||
-rw-r--r-- | src/wallet/rpc/wallet.cpp | 14 | ||||
-rw-r--r-- | src/wallet/rpc/wallet.h | 4 |
11 files changed, 154 insertions, 44 deletions
diff --git a/src/wallet/rpc/addresses.cpp b/src/wallet/rpc/addresses.cpp index e570c18099..51587a64a3 100644 --- a/src/wallet/rpc/addresses.cpp +++ b/src/wallet/rpc/addresses.cpp @@ -13,6 +13,7 @@ #include <univalue.h> +namespace wallet { RPCHelpMan getnewaddress() { return RPCHelpMan{"getnewaddress", @@ -238,6 +239,10 @@ RPCHelpMan addmultisigaddress() {RPCResult::Type::STR, "address", "The value of the new multisig address"}, {RPCResult::Type::STR_HEX, "redeemScript", "The string value of the hex-encoded redemption script"}, {RPCResult::Type::STR, "descriptor", "The descriptor for this multisig"}, + {RPCResult::Type::ARR, "warnings", /* optional */ true, "Any warnings resulting from the creation of this multisig", + { + {RPCResult::Type::STR, "", ""}, + }}, } }, RPCExamples{ @@ -295,6 +300,14 @@ RPCHelpMan addmultisigaddress() result.pushKV("address", EncodeDestination(dest)); result.pushKV("redeemScript", HexStr(inner)); result.pushKV("descriptor", descriptor->ToString()); + + UniValue warnings(UniValue::VARR); + if (!request.params[3].isNull() && OutputTypeFromDestination(dest) != output_type) { + // Only warns if the user has explicitly chosen an address type we cannot generate + warnings.push_back("Unable to make chosen address type, please ensure no uncompressed public keys are present."); + } + if (warnings.size()) result.pushKV("warnings", warnings); + return result; }, }; @@ -347,7 +360,12 @@ RPCHelpMan keypoolrefill() RPCHelpMan newkeypool() { return RPCHelpMan{"newkeypool", - "\nEntirely clears and refills the keypool."+ + "\nEntirely clears and refills the keypool.\n" + "WARNING: On non-HD wallets, this will require a new backup immediately, to include the new keys.\n" + "When restoring a backup of an HD wallet created before the newkeypool command is run, funds received to\n" + "new addresses may not appear automatically. They have not been lost, but the wallet may not find them.\n" + "This can be fixed by running the newkeypool command on the backup and then rescanning, so the wallet\n" + "re-generates the required keys." + HELP_REQUIRING_PASSPHRASE, {}, RPCResult{RPCResult::Type::NONE, "", ""}, @@ -785,3 +803,4 @@ RPCHelpMan walletdisplayaddress() }; } #endif // ENABLE_EXTERNAL_SIGNER +} // namespace wallet diff --git a/src/wallet/rpc/backup.cpp b/src/wallet/rpc/backup.cpp index e3daae9cea..c0912ffc70 100644 --- a/src/wallet/rpc/backup.cpp +++ b/src/wallet/rpc/backup.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2009-2020 The Bitcoin Core developers +// Copyright (c) 2009-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. @@ -31,6 +31,7 @@ using interfaces::FoundBlock; +namespace wallet { std::string static EncodeDumpString(const std::string &str) { std::stringstream ret; for (const unsigned char c : str) { @@ -1595,7 +1596,7 @@ RPCHelpMan importdescriptors() /* oneline_description */ "", {"timestamp | \"now\"", "integer / string"} }, {"internal", RPCArg::Type::BOOL, RPCArg::Default{false}, "Whether matching outputs should be treated as not incoming payments (e.g. change)"}, - {"label", RPCArg::Type::STR, RPCArg::Default{""}, "Label to assign to the address, only allowed with internal=false"}, + {"label", RPCArg::Type::STR, RPCArg::Default{""}, "Label to assign to the address, only allowed with internal=false. Disabled for ranged descriptors"}, }, }, }, @@ -1879,27 +1880,17 @@ RPCHelpMan restorewallet() auto backup_file = fs::u8path(request.params[1].get_str()); - if (!fs::exists(backup_file)) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Backup file does not exist"); - } - std::string wallet_name = request.params[0].get_str(); - const fs::path wallet_path = fsbridge::AbsPathJoin(GetWalletDir(), fs::u8path(wallet_name)); - - if (fs::exists(wallet_path)) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Wallet name already exists."); - } - - if (!TryCreateDirectories(wallet_path)) { - throw JSONRPCError(RPC_WALLET_ERROR, strprintf("Failed to create database path '%s'. Database already exists.", wallet_path.u8string())); - } + std::optional<bool> load_on_start = request.params[2].isNull() ? std::nullopt : std::optional<bool>(request.params[2].get_bool()); - auto wallet_file = wallet_path / "wallet.dat"; + DatabaseStatus status; + bilingual_str error; + std::vector<bilingual_str> warnings; - fs::copy_file(backup_file, wallet_file, fs::copy_option::fail_if_exists); + const std::shared_ptr<CWallet> wallet = RestoreWallet(context, backup_file, wallet_name, load_on_start, status, error, warnings); - auto [wallet, warnings] = LoadWalletHelper(context, request.params[2], wallet_name); + HandleWalletError(wallet, status, error); UniValue obj(UniValue::VOBJ); obj.pushKV("name", wallet->GetName()); @@ -1910,3 +1901,4 @@ RPCHelpMan restorewallet() }, }; } +} // namespace wallet diff --git a/src/wallet/rpc/coins.cpp b/src/wallet/rpc/coins.cpp index f3294b4570..035541babd 100644 --- a/src/wallet/rpc/coins.cpp +++ b/src/wallet/rpc/coins.cpp @@ -15,6 +15,7 @@ #include <univalue.h> +namespace wallet { static CAmount GetReceived(const CWallet& wallet, const UniValue& params, bool by_label) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet) { std::set<CTxDestination> address_set; @@ -59,8 +60,8 @@ static CAmount GetReceived(const CWallet& wallet, const UniValue& params, bool b if (depth < min_depth // Coinbase with less than 1 confirmation is no longer in the main chain || (wtx.IsCoinBase() && (depth < 1 || !include_coinbase)) - || (wallet.IsTxImmatureCoinBase(wtx) && !include_immature_coinbase) - || !wallet.chain().checkFinalTx(*wtx.tx)) { + || (wallet.IsTxImmatureCoinBase(wtx) && !include_immature_coinbase)) + { continue; } @@ -731,3 +732,4 @@ RPCHelpMan listunspent() }, }; } +} // namespace wallet diff --git a/src/wallet/rpc/encrypt.cpp b/src/wallet/rpc/encrypt.cpp index e659f434a3..2b6a2a198d 100644 --- a/src/wallet/rpc/encrypt.cpp +++ b/src/wallet/rpc/encrypt.cpp @@ -7,6 +7,7 @@ #include <wallet/wallet.h> +namespace wallet { RPCHelpMan walletpassphrase() { return RPCHelpMan{"walletpassphrase", @@ -246,3 +247,4 @@ RPCHelpMan encryptwallet() }, }; } +} // namespace wallet diff --git a/src/wallet/rpc/signmessage.cpp b/src/wallet/rpc/signmessage.cpp index bb8d7fc13f..438d290030 100644 --- a/src/wallet/rpc/signmessage.cpp +++ b/src/wallet/rpc/signmessage.cpp @@ -10,6 +10,7 @@ #include <univalue.h> +namespace wallet { RPCHelpMan signmessage() { return RPCHelpMan{"signmessage", @@ -66,3 +67,4 @@ RPCHelpMan signmessage() }, }; } +} // namespace wallet diff --git a/src/wallet/rpc/spend.cpp b/src/wallet/rpc/spend.cpp index a92ecd37db..433b5a1815 100644 --- a/src/wallet/rpc/spend.cpp +++ b/src/wallet/rpc/spend.cpp @@ -2,6 +2,7 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include <consensus/validation.h> #include <core_io.h> #include <key_io.h> #include <policy/policy.h> @@ -19,6 +20,7 @@ #include <univalue.h> +namespace wallet { static void ParseRecipients(const UniValue& address_amounts, const UniValue& subtract_fee_outputs, std::vector<CRecipient> &recipients) { std::set<CTxDestination> destinations; int i = 0; @@ -428,6 +430,7 @@ void FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& fee_out, {"replaceable", UniValueType(UniValue::VBOOL)}, {"conf_target", UniValueType(UniValue::VNUM)}, {"estimate_mode", UniValueType(UniValue::VSTR)}, + {"input_weights", UniValueType(UniValue::VARR)}, }, true, true); @@ -547,6 +550,37 @@ void FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& fee_out, } } + if (options.exists("input_weights")) { + for (const UniValue& input : options["input_weights"].get_array().getValues()) { + uint256 txid = ParseHashO(input, "txid"); + + const UniValue& vout_v = find_value(input, "vout"); + if (!vout_v.isNum()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key"); + } + int vout = vout_v.get_int(); + if (vout < 0) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout cannot be negative"); + } + + const UniValue& weight_v = find_value(input, "weight"); + if (!weight_v.isNum()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing weight key"); + } + int64_t weight = weight_v.get_int64(); + const int64_t min_input_weight = GetTransactionInputWeight(CTxIn()); + CHECK_NONFATAL(min_input_weight == 165); + if (weight < min_input_weight) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, weight cannot be less than 165 (41 bytes (size of outpoint + sequence + empty scriptSig) * 4 (witness scaling factor)) + 1 (empty witness)"); + } + if (weight > MAX_STANDARD_TX_WEIGHT) { + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid parameter, weight cannot be greater than the maximum standard tx weight of %d", MAX_STANDARD_TX_WEIGHT)); + } + + coinControl.SetInputWeight(COutPoint(txid, vout), weight); + } + } + if (tx.vout.size() == 0) throw JSONRPCError(RPC_INVALID_PARAMETER, "TX must have at least one output"); @@ -584,6 +618,23 @@ void FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& fee_out, } } +static void SetOptionsInputWeights(const UniValue& inputs, UniValue& options) +{ + if (options.exists("input_weights")) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Input weights should be specified in inputs rather than in options."); + } + if (inputs.size() == 0) { + return; + } + UniValue weights(UniValue::VARR); + for (const UniValue& input : inputs.getValues()) { + if (input.exists("weight")) { + weights.push_back(input); + } + } + options.pushKV("input_weights", weights); +} + RPCHelpMan fundrawtransaction() { return RPCHelpMan{"fundrawtransaction", @@ -625,6 +676,17 @@ RPCHelpMan fundrawtransaction() {"vout_index", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "The zero-based output index, before a change output is added."}, }, }, + {"input_weights", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "Inputs and their corresponding weights", + { + {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"}, + {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output index"}, + {"weight", RPCArg::Type::NUM, RPCArg::Optional::NO, "The maximum weight for this input, " + "including the weight of the outpoint and sequence number. " + "Note that serialized signature sizes are not guaranteed to be consistent, " + "so the maximum DER signatures size of 73 bytes should be used when considering ECDSA signatures." + "Remember to convert serialized sizes to weight units when necessary."}, + }, + }, }, FundTxDoc()), "options"}, @@ -709,7 +771,7 @@ RPCHelpMan signrawtransactionwithwallet() }, }, }, - {"sighashtype", RPCArg::Type::STR, RPCArg::Default{"DEFAULT"}, "The signature hash type. Must be one of\n" + {"sighashtype", RPCArg::Type::STR, RPCArg::Default{"DEFAULT for Taproot, ALL otherwise"}, "The signature hash type. Must be one of\n" " \"DEFAULT\"\n" " \"ALL\"\n" " \"NONE\"\n" @@ -1006,6 +1068,11 @@ RPCHelpMan send() {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"}, {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"}, {"sequence", RPCArg::Type::NUM, RPCArg::Optional::NO, "The sequence number"}, + {"weight", RPCArg::Type::NUM, RPCArg::DefaultHint{"Calculated from wallet and solving data"}, "The maximum weight for this input, " + "including the weight of the outpoint and sequence number. " + "Note that signature sizes are not guaranteed to be consistent, " + "so the maximum DER signatures size of 73 bytes should be used when considering ECDSA signatures." + "Remember to convert serialized sizes to weight units when necessary."}, }, }, {"locktime", RPCArg::Type::NUM, RPCArg::Default{0}, "Raw locktime. Non-0 value also locktime-activates inputs"}, @@ -1109,6 +1176,7 @@ RPCHelpMan send() // Automatically select coins, unless at least one is manually selected. Can // be overridden by options.add_inputs. coin_control.m_add_inputs = rawTx.vin.size() == 0; + SetOptionsInputWeights(options["inputs"], options); FundTransaction(*pwallet, rawTx, fee, change_position, options, coin_control, /* override_min_fee */ false); bool add_to_wallet = true; @@ -1167,7 +1235,7 @@ RPCHelpMan walletprocesspsbt() { {"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "The transaction base64 string"}, {"sign", RPCArg::Type::BOOL, RPCArg::Default{true}, "Also sign the transaction when updating (requires wallet to be unlocked)"}, - {"sighashtype", RPCArg::Type::STR, RPCArg::Default{"DEFAULT"}, "The signature hash type to sign with if not specified by the PSBT. Must be one of\n" + {"sighashtype", RPCArg::Type::STR, RPCArg::Default{"DEFAULT for Taproot, ALL otherwise"}, "The signature hash type to sign with if not specified by the PSBT. Must be one of\n" " \"DEFAULT\"\n" " \"ALL\"\n" " \"NONE\"\n" @@ -1249,6 +1317,11 @@ RPCHelpMan walletcreatefundedpsbt() {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"}, {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"}, {"sequence", RPCArg::Type::NUM, RPCArg::DefaultHint{"depends on the value of the 'locktime' and 'options.replaceable' arguments"}, "The sequence number"}, + {"weight", RPCArg::Type::NUM, RPCArg::DefaultHint{"Calculated from wallet and solving data"}, "The maximum weight for this input, " + "including the weight of the outpoint and sequence number. " + "Note that signature sizes are not guaranteed to be consistent, " + "so the maximum DER signatures size of 73 bytes should be used when considering ECDSA signatures." + "Remember to convert serialized sizes to weight units when necessary."}, }, }, }, @@ -1329,10 +1402,12 @@ RPCHelpMan walletcreatefundedpsbt() }, true ); + UniValue options = request.params[3]; + CAmount fee; int change_position; bool rbf{wallet.m_signal_rbf}; - const UniValue &replaceable_arg = request.params[3]["replaceable"]; + const UniValue &replaceable_arg = options["replaceable"]; if (!replaceable_arg.isNull()) { RPCTypeCheckArgument(replaceable_arg, UniValue::VBOOL); rbf = replaceable_arg.isTrue(); @@ -1342,7 +1417,8 @@ RPCHelpMan walletcreatefundedpsbt() // Automatically select coins, unless at least one is manually selected. Can // be overridden by options.add_inputs. coin_control.m_add_inputs = rawTx.vin.size() == 0; - FundTransaction(wallet, rawTx, fee, change_position, request.params[3], coin_control, /* override_min_fee */ true); + SetOptionsInputWeights(request.params[0], options); + FundTransaction(wallet, rawTx, fee, change_position, options, coin_control, /* override_min_fee */ true); // Make a blank psbt PartiallySignedTransaction psbtx(rawTx); @@ -1367,3 +1443,4 @@ RPCHelpMan walletcreatefundedpsbt() }, }; } +} // namespace wallet diff --git a/src/wallet/rpc/transactions.cpp b/src/wallet/rpc/transactions.cpp index 8a1c0885ac..eef2c13ee1 100644 --- a/src/wallet/rpc/transactions.cpp +++ b/src/wallet/rpc/transactions.cpp @@ -13,6 +13,7 @@ using interfaces::FoundBlock; +namespace wallet { static void WalletTxToJSON(const CWallet& wallet, const CWalletTx& wtx, UniValue& entry) { interfaces::Chain& chain = wallet.chain(); @@ -113,8 +114,8 @@ static UniValue ListReceived(const CWallet& wallet, const UniValue& params, cons // Coinbase with less than 1 confirmation is no longer in the main chain if ((wtx.IsCoinBase() && (nDepth < 1 || !include_coinbase)) - || (wallet.IsTxImmatureCoinBase(wtx) && !include_immature_coinbase) - || !wallet.chain().checkFinalTx(*wtx.tx)) { + || (wallet.IsTxImmatureCoinBase(wtx) && !include_immature_coinbase)) + { continue; } @@ -958,3 +959,4 @@ RPCHelpMan abortrescan() }, }; } +} // namespace wallet diff --git a/src/wallet/rpc/util.cpp b/src/wallet/rpc/util.cpp index e2126b7236..59683c5fd8 100644 --- a/src/wallet/rpc/util.cpp +++ b/src/wallet/rpc/util.cpp @@ -12,6 +12,7 @@ #include <univalue.h> +namespace wallet { 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"}; @@ -122,16 +123,8 @@ std::string LabelFromValue(const UniValue& value) return label; } -std::tuple<std::shared_ptr<CWallet>, std::vector<bilingual_str>> LoadWalletHelper(WalletContext& context, UniValue load_on_start_param, const std::string wallet_name) +void HandleWalletError(const std::shared_ptr<CWallet> wallet, DatabaseStatus& status, bilingual_str& error) { - DatabaseOptions options; - DatabaseStatus status; - options.require_existing = true; - bilingual_str error; - std::vector<bilingual_str> warnings; - std::optional<bool> load_on_start = load_on_start_param.isNull() ? std::nullopt : std::optional<bool>(load_on_start_param.get_bool()); - std::shared_ptr<CWallet> const wallet = LoadWallet(context, wallet_name, load_on_start, options, status, error, warnings); - if (!wallet) { // Map bad format to not found, since bad format is returned when the // wallet directory exists, but doesn't contain a data file. @@ -144,11 +137,16 @@ std::tuple<std::shared_ptr<CWallet>, std::vector<bilingual_str>> LoadWalletHelpe case DatabaseStatus::FAILED_ALREADY_LOADED: code = RPC_WALLET_ALREADY_LOADED; break; + case DatabaseStatus::FAILED_ALREADY_EXISTS: + code = RPC_WALLET_ALREADY_EXISTS; + break; + case DatabaseStatus::FAILED_INVALID_BACKUP_FILE: + code = RPC_INVALID_PARAMETER; + break; default: // RPC_WALLET_ERROR is returned for all other cases. break; } throw JSONRPCError(code, error.original); } - - return { wallet, warnings }; } +} // namespace wallet diff --git a/src/wallet/rpc/util.h b/src/wallet/rpc/util.h index a1fa4d49b1..7b810eb06e 100644 --- a/src/wallet/rpc/util.h +++ b/src/wallet/rpc/util.h @@ -10,11 +10,14 @@ #include <string> #include <vector> +class JSONRPCRequest; +class UniValue; struct bilingual_str; + +namespace wallet { class CWallet; -class JSONRPCRequest; class LegacyScriptPubKeyMan; -class UniValue; +enum class DatabaseStatus; struct WalletContext; extern const std::string HELP_REQUIRING_PASSPHRASE; @@ -37,6 +40,7 @@ bool GetAvoidReuseFlag(const CWallet& wallet, const UniValue& param); bool ParseIncludeWatchonly(const UniValue& include_watchonly, const CWallet& wallet); std::string LabelFromValue(const UniValue& value); -std::tuple<std::shared_ptr<CWallet>, std::vector<bilingual_str>> LoadWalletHelper(WalletContext& context, UniValue load_on_start_param, const std::string wallet_name); +void HandleWalletError(const std::shared_ptr<CWallet> wallet, DatabaseStatus& status, bilingual_str& error); +} // namespace wallet #endif // BITCOIN_WALLET_RPC_UTIL_H diff --git a/src/wallet/rpc/wallet.cpp b/src/wallet/rpc/wallet.cpp index 09f50137c6..33ec715b51 100644 --- a/src/wallet/rpc/wallet.cpp +++ b/src/wallet/rpc/wallet.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2020 The Bitcoin Core developers +// Copyright (c) 2009-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. @@ -18,6 +18,7 @@ #include <univalue.h> +namespace wallet { /** Checks if a CKey is in the given CWallet compressed or otherwise*/ bool HaveKey(const SigningProvider& wallet, const CKey& key) { @@ -215,7 +216,15 @@ static RPCHelpMan loadwallet() WalletContext& context = EnsureWalletContext(request.context); const std::string name(request.params[0].get_str()); - auto [wallet, warnings] = LoadWalletHelper(context, request.params[1], name); + DatabaseOptions options; + DatabaseStatus status; + options.require_existing = true; + bilingual_str error; + std::vector<bilingual_str> warnings; + std::optional<bool> load_on_start = request.params[1].isNull() ? std::nullopt : std::optional<bool>(request.params[1].get_bool()); + std::shared_ptr<CWallet> const wallet = LoadWallet(context, name, load_on_start, options, status, error, warnings); + + HandleWalletError(wallet, status, error); UniValue obj(UniValue::VOBJ); obj.pushKV("name", wallet->GetName()); @@ -721,3 +730,4 @@ static const CRPCCommand commands[] = // clang-format on return commands; } +} // namespace wallet diff --git a/src/wallet/rpc/wallet.h b/src/wallet/rpc/wallet.h index cdc9a8cc92..423fc892b2 100644 --- a/src/wallet/rpc/wallet.h +++ b/src/wallet/rpc/wallet.h @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2020 The Bitcoin Core developers +// Copyright (c) 2016-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. @@ -9,6 +9,8 @@ class CRPCCommand; +namespace wallet { Span<const CRPCCommand> GetWalletRPCCommands(); +} // namespace wallet #endif // BITCOIN_WALLET_RPC_WALLET_H |