diff options
author | Sjors Provoost <sjors@sprovoost.nl> | 2021-03-18 14:17:39 +0100 |
---|---|---|
committer | Sjors Provoost <sjors@sprovoost.nl> | 2021-04-08 17:56:00 +0200 |
commit | b54b2e7b1a171203404bd41853372c73f2c64532 (patch) | |
tree | f6d0854fa8a582e536bf8cdd8c0cf51ab51e8250 /src/wallet | |
parent | 6664211be2b664dd471d7aeea12fcf2859dba860 (diff) |
Move external signer out of wallet module
This commit moves the ExternalSigner class and RPC methods out of the wallet module.
The enumeratesigners RPC can be used without a wallet since #21417.
With additional modifications external signers could be used without a wallet in general, e.g. via signrawtransaction.
The signerdisplayaddress RPC is ranamed to walletdisplayaddress because it requires wallet context.
A future displayaddress RPC call without wallet context could take a descriptor argument.
This commit fixes a rpc_help.py failure when configured with --disable-wallet.
Diffstat (limited to 'src/wallet')
-rw-r--r-- | src/wallet/external_signer.cpp | 119 | ||||
-rw-r--r-- | src/wallet/external_signer.h | 73 | ||||
-rw-r--r-- | src/wallet/external_signer_scriptpubkeyman.cpp | 2 | ||||
-rw-r--r-- | src/wallet/interfaces.cpp | 12 | ||||
-rw-r--r-- | src/wallet/rpcsigner.cpp | 108 | ||||
-rw-r--r-- | src/wallet/rpcsigner.h | 25 | ||||
-rw-r--r-- | src/wallet/rpcwallet.cpp | 45 | ||||
-rw-r--r-- | src/wallet/scriptpubkeyman.cpp | 2 | ||||
-rw-r--r-- | src/wallet/wallet.h | 2 |
9 files changed, 48 insertions, 340 deletions
diff --git a/src/wallet/external_signer.cpp b/src/wallet/external_signer.cpp deleted file mode 100644 index 3396111760..0000000000 --- a/src/wallet/external_signer.cpp +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright (c) 2018-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 <chainparams.h> -#include <core_io.h> -#include <psbt.h> -#include <util/strencodings.h> -#include <util/system.h> -#include <wallet/external_signer.h> - -ExternalSigner::ExternalSigner(const std::string& command, const std::string& fingerprint, std::string chain, std::string name): m_command(command), m_fingerprint(fingerprint), m_chain(chain), m_name(name) {} - -const std::string ExternalSigner::NetworkArg() const -{ - return " --chain " + m_chain; -} - -#ifdef ENABLE_EXTERNAL_SIGNER - -bool ExternalSigner::Enumerate(const std::string& command, std::vector<ExternalSigner>& signers, std::string chain, bool ignore_errors) -{ - // Call <command> enumerate - const UniValue result = RunCommandParseJSON(command + " enumerate"); - if (!result.isArray()) { - if (ignore_errors) return false; - throw ExternalSignerException(strprintf("'%s' received invalid response, expected array of signers", command)); - } - for (UniValue signer : result.getValues()) { - // Check for error - const UniValue& error = find_value(signer, "error"); - if (!error.isNull()) { - if (ignore_errors) return false; - if (!error.isStr()) { - throw ExternalSignerException(strprintf("'%s' error", command)); - } - throw ExternalSignerException(strprintf("'%s' error: %s", command, error.getValStr())); - } - // Check if fingerprint is present - const UniValue& fingerprint = find_value(signer, "fingerprint"); - if (fingerprint.isNull()) { - if (ignore_errors) return false; - throw ExternalSignerException(strprintf("'%s' received invalid response, missing signer fingerprint", command)); - } - std::string fingerprintStr = fingerprint.get_str(); - // Skip duplicate signer - bool duplicate = false; - for (ExternalSigner signer : signers) { - if (signer.m_fingerprint.compare(fingerprintStr) == 0) duplicate = true; - } - if (duplicate) break; - std::string name = ""; - const UniValue& model_field = find_value(signer, "model"); - if (model_field.isStr() && model_field.getValStr() != "") { - name += model_field.getValStr(); - } - signers.push_back(ExternalSigner(command, fingerprintStr, chain, name)); - } - return true; -} - -UniValue ExternalSigner::DisplayAddress(const std::string& descriptor) const -{ - return RunCommandParseJSON(m_command + " --fingerprint \"" + m_fingerprint + "\"" + NetworkArg() + " displayaddress --desc \"" + descriptor + "\""); -} - -UniValue ExternalSigner::GetDescriptors(int account) -{ - return RunCommandParseJSON(m_command + " --fingerprint \"" + m_fingerprint + "\"" + NetworkArg() + " getdescriptors --account " + strprintf("%d", account)); -} - -bool ExternalSigner::SignTransaction(PartiallySignedTransaction& psbtx, std::string& error) -{ - // Serialize the PSBT - CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); - ssTx << psbtx; - - // Check if signer fingerprint matches any input master key fingerprint - bool match = false; - for (unsigned int i = 0; i < psbtx.inputs.size(); ++i) { - const PSBTInput& input = psbtx.inputs[i]; - for (auto entry : input.hd_keypaths) { - if (m_fingerprint == strprintf("%08x", ReadBE32(entry.second.fingerprint))) match = true; - } - } - - if (!match) { - error = "Signer fingerprint " + m_fingerprint + " does not match any of the inputs:\n" + EncodeBase64(ssTx.str()); - return false; - } - - std::string command = m_command + " --stdin --fingerprint \"" + m_fingerprint + "\"" + NetworkArg(); - std::string stdinStr = "signtx \"" + EncodeBase64(ssTx.str()) + "\""; - - const UniValue signer_result = RunCommandParseJSON(command, stdinStr); - - if (find_value(signer_result, "error").isStr()) { - error = find_value(signer_result, "error").get_str(); - return false; - } - - if (!find_value(signer_result, "psbt").isStr()) { - error = "Unexpected result from signer"; - return false; - } - - PartiallySignedTransaction signer_psbtx; - std::string signer_psbt_error; - if (!DecodeBase64PSBT(signer_psbtx, find_value(signer_result, "psbt").get_str(), signer_psbt_error)) { - error = strprintf("TX decode failed %s", signer_psbt_error); - return false; - } - - psbtx = signer_psbtx; - - return true; -} - -#endif diff --git a/src/wallet/external_signer.h b/src/wallet/external_signer.h deleted file mode 100644 index 4b9711107b..0000000000 --- a/src/wallet/external_signer.h +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright (c) 2018-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_EXTERNAL_SIGNER_H -#define BITCOIN_WALLET_EXTERNAL_SIGNER_H - -#include <stdexcept> -#include <string> -#include <univalue.h> -#include <util/system.h> - -struct PartiallySignedTransaction; - -class ExternalSignerException : public std::runtime_error { -public: - using std::runtime_error::runtime_error; -}; - -//! Enables interaction with an external signing device or service, such as -//! a hardware wallet. See doc/external-signer.md -class ExternalSigner -{ -private: - //! The command which handles interaction with the external signer. - std::string m_command; - -public: - //! @param[in] command the command which handles interaction with the external signer - //! @param[in] fingerprint master key fingerprint of the signer - //! @param[in] chain "main", "test", "regtest" or "signet" - //! @param[in] name device name - ExternalSigner(const std::string& command, const std::string& fingerprint, std::string chain, std::string name); - - //! Master key fingerprint of the signer - std::string m_fingerprint; - - //! Bitcoin mainnet, testnet, etc - std::string m_chain; - - //! Name of signer - std::string m_name; - - const std::string NetworkArg() const; - -#ifdef ENABLE_EXTERNAL_SIGNER - //! Obtain a list of signers. Calls `<command> enumerate`. - //! @param[in] command the command which handles interaction with the external signer - //! @param[in,out] signers vector to which new signers (with a unique master key fingerprint) are added - //! @param chain "main", "test", "regtest" or "signet" - //! @param[out] success Boolean - static bool Enumerate(const std::string& command, std::vector<ExternalSigner>& signers, std::string chain, bool ignore_errors = false); - - //! Display address on the device. Calls `<command> displayaddress --desc <descriptor>`. - //! @param[in] descriptor Descriptor specifying which address to display. - //! Must include a public key or xpub, as well as key origin. - UniValue DisplayAddress(const std::string& descriptor) const; - - //! Get receive and change Descriptor(s) from device for a given account. - //! Calls `<command> getdescriptors --account <account>` - //! @param[in] account which BIP32 account to use (e.g. `m/44'/0'/account'`) - //! @param[out] UniValue see doc/external-signer.md - UniValue GetDescriptors(int account); - - //! Sign PartiallySignedTransaction on the device. - //! Calls `<command> signtransaction` and passes the PSBT via stdin. - //! @param[in,out] psbt PartiallySignedTransaction to be signed - bool SignTransaction(PartiallySignedTransaction& psbt, std::string& error); - -#endif -}; - -#endif // BITCOIN_WALLET_EXTERNAL_SIGNER_H diff --git a/src/wallet/external_signer_scriptpubkeyman.cpp b/src/wallet/external_signer_scriptpubkeyman.cpp index a2071e521a..a113c128d7 100644 --- a/src/wallet/external_signer_scriptpubkeyman.cpp +++ b/src/wallet/external_signer_scriptpubkeyman.cpp @@ -3,7 +3,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include <chainparams.h> -#include <wallet/external_signer.h> +#include <external_signer.h> #include <wallet/external_signer_scriptpubkeyman.h> #ifdef ENABLE_EXTERNAL_SIGNER diff --git a/src/wallet/interfaces.cpp b/src/wallet/interfaces.cpp index 23bdaa671a..64ce09d1d1 100644 --- a/src/wallet/interfaces.cpp +++ b/src/wallet/interfaces.cpp @@ -22,7 +22,6 @@ #include <wallet/fees.h> #include <wallet/ismine.h> #include <wallet/load.h> -#include <wallet/rpcsigner.h> #include <wallet/rpcwallet.h> #include <wallet/wallet.h> @@ -520,17 +519,6 @@ public: }, command.argNames, command.unique_id); m_rpc_handlers.emplace_back(m_context.chain->handleRpc(m_rpc_commands.back())); } - -#ifdef ENABLE_EXTERNAL_SIGNER - for (const CRPCCommand& command : GetSignerRPCCommands()) { - m_rpc_commands.emplace_back(command.category, command.name, [this, &command](const JSONRPCRequest& request, UniValue& result, bool last_handler) { - JSONRPCRequest wallet_request = request; - wallet_request.context = &m_context; - return command.actor(wallet_request, result, last_handler); - }, command.argNames, command.unique_id); - m_rpc_handlers.emplace_back(m_context.chain->handleRpc(m_rpc_commands.back())); - } -#endif } bool verify() override { return VerifyWallets(*m_context.chain); } bool load() override { return LoadWallets(*m_context.chain); } diff --git a/src/wallet/rpcsigner.cpp b/src/wallet/rpcsigner.cpp deleted file mode 100644 index 696c74d665..0000000000 --- a/src/wallet/rpcsigner.cpp +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright (c) 2018-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 <chainparamsbase.h> -#include <key_io.h> -#include <rpc/server.h> -#include <rpc/util.h> -#include <util/strencodings.h> -#include <wallet/rpcsigner.h> -#include <wallet/rpcwallet.h> -#include <wallet/wallet.h> - -#ifdef ENABLE_EXTERNAL_SIGNER - -static RPCHelpMan enumeratesigners() -{ - return RPCHelpMan{ - "enumeratesigners", - "Returns a list of external signers from -signer.", - {}, - RPCResult{ - RPCResult::Type::OBJ, "", "", - { - {RPCResult::Type::ARR, "signers", /* optional */ false, "", - { - {RPCResult::Type::STR_HEX, "masterkeyfingerprint", "Master key fingerprint"}, - {RPCResult::Type::STR, "name", "Device name"}, - }, - } - } - }, - RPCExamples{""}, - [](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { - const std::string command = gArgs.GetArg("-signer", ""); - if (command == "") throw JSONRPCError(RPC_WALLET_ERROR, "Error: restart bitcoind with -signer=<cmd>"); - std::string chain = gArgs.GetChainName(); - UniValue signers_res = UniValue::VARR; - try { - std::vector<ExternalSigner> signers; - ExternalSigner::Enumerate(command, signers, chain); - for (ExternalSigner signer : signers) { - UniValue signer_res = UniValue::VOBJ; - signer_res.pushKV("fingerprint", signer.m_fingerprint); - signer_res.pushKV("name", signer.m_name); - signers_res.push_back(signer_res); - } - } catch (const ExternalSignerException& e) { - throw JSONRPCError(RPC_WALLET_ERROR, e.what()); - } - UniValue result(UniValue::VOBJ); - result.pushKV("signers", signers_res); - return result; - } - }; -} - -static RPCHelpMan signerdisplayaddress() -{ - return RPCHelpMan{ - "signerdisplayaddress", - "Display address on an external signer for verification.\n", - { - {"address", RPCArg::Type::STR, RPCArg::Optional::NO, /* default_val */ "", "bitcoin address to display"}, - }, - RPCResult{RPCResult::Type::NONE,"",""}, - RPCExamples{""}, - [](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { - std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); - if (!wallet) return NullUniValue; - CWallet* const pwallet = wallet.get(); - - LOCK(pwallet->cs_wallet); - - CTxDestination dest = DecodeDestination(request.params[0].get_str()); - - // Make sure the destination is valid - if (!IsValidDestination(dest)) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address"); - } - - if (!pwallet->DisplayAddress(dest)) { - throw JSONRPCError(RPC_WALLET_ERROR, "Failed to display address"); - } - - UniValue result(UniValue::VOBJ); - result.pushKV("address", request.params[0].get_str()); - return result; - } - }; -} - -Span<const CRPCCommand> GetSignerRPCCommands() -{ - -// clang-format off -static const CRPCCommand commands[] = -{ // category actor (function) - // --------------------- ------------------------ - { "signer", &enumeratesigners, }, - { "signer", &signerdisplayaddress, }, -}; -// clang-format on - return MakeSpan(commands); -} - - -#endif // ENABLE_EXTERNAL_SIGNER diff --git a/src/wallet/rpcsigner.h b/src/wallet/rpcsigner.h deleted file mode 100644 index f3ab83c428..0000000000 --- a/src/wallet/rpcsigner.h +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) 2018-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_RPCSIGNER_H -#define BITCOIN_WALLET_RPCSIGNER_H - -#include <span.h> -#include <util/system.h> -#include <vector> - -#ifdef ENABLE_EXTERNAL_SIGNER - -class CRPCCommand; - -namespace interfaces { -class Chain; -class Handler; -} - -Span<const CRPCCommand> GetSignerRPCCommands(); - -#endif // ENABLE_EXTERNAL_SIGNER - -#endif //BITCOIN_WALLET_RPCSIGNER_H diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 672ee3e7a4..dc821dc06d 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -4526,6 +4526,48 @@ static RPCHelpMan upgradewallet() }; } +#ifdef ENABLE_EXTERNAL_SIGNER +static RPCHelpMan walletdisplayaddress() +{ + return RPCHelpMan{ + "walletdisplayaddress", + "Display address on an external signer for verification.\n", + { + {"address", RPCArg::Type::STR, RPCArg::Optional::NO, /* default_val */ "", "bitcoin address to display"}, + }, + RPCResult{ + RPCResult::Type::OBJ,"","", + { + {RPCResult::Type::STR, "address", "The address as confirmed by the signer"}, + } + }, + RPCExamples{""}, + [](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + if (!wallet) return NullUniValue; + CWallet* const pwallet = wallet.get(); + + LOCK(pwallet->cs_wallet); + + CTxDestination dest = DecodeDestination(request.params[0].get_str()); + + // Make sure the destination is valid + if (!IsValidDestination(dest)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address"); + } + + if (!pwallet->DisplayAddress(dest)) { + throw JSONRPCError(RPC_MISC_ERROR, "Failed to display address"); + } + + UniValue result(UniValue::VOBJ); + result.pushKV("address", request.params[0].get_str()); + return result; + } + }; +} +#endif // ENABLE_EXTERNAL_SIGNER + RPCHelpMan abortrescan(); RPCHelpMan dumpprivkey(); RPCHelpMan importprivkey(); @@ -4602,6 +4644,9 @@ static const CRPCCommand commands[] = { "wallet", &unloadwallet, }, { "wallet", &upgradewallet, }, { "wallet", &walletcreatefundedpsbt, }, +#ifdef ENABLE_EXTERNAL_SIGNER + { "wallet", &walletdisplayaddress, }, +#endif // ENABLE_EXTERNAL_SIGNER { "wallet", &walletlock, }, { "wallet", &walletpassphrase, }, { "wallet", &walletpassphrasechange, }, diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp index 9a75002b6c..149549410c 100644 --- a/src/wallet/scriptpubkeyman.cpp +++ b/src/wallet/scriptpubkeyman.cpp @@ -13,7 +13,7 @@ #include <util/system.h> #include <util/time.h> #include <util/translation.h> -#include <wallet/external_signer.h> +#include <external_signer.h> #include <wallet/scriptpubkeyman.h> #include <optional> diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 192fd60dc2..5bf3c91bec 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -22,7 +22,7 @@ #include <wallet/coinselection.h> #include <wallet/crypter.h> #include <wallet/scriptpubkeyman.h> -#include <wallet/external_signer.h> +#include <external_signer.h> #include <wallet/walletdb.h> #include <wallet/walletutil.h> |