aboutsummaryrefslogtreecommitdiff
path: root/src/wallet
diff options
context:
space:
mode:
authorSjors Provoost <sjors@sprovoost.nl>2019-02-15 12:54:29 +0100
committerSjors Provoost <sjors@sprovoost.nl>2021-02-23 14:34:31 +0100
commit2700f09c4130af6167ce71f46960e92ca800e205 (patch)
tree2b4c23317bc24a39e3e7e3dab598359249238613 /src/wallet
parent07b7c940a7da138d55a484ef83fee19ebf58a867 (diff)
rpc: signer: add enumeratesigners to list external signers
Diffstat (limited to 'src/wallet')
-rw-r--r--src/wallet/external_signer.cpp54
-rw-r--r--src/wallet/external_signer.h23
-rw-r--r--src/wallet/rpcsigner.cpp59
3 files changed, 120 insertions, 16 deletions
diff --git a/src/wallet/external_signer.cpp b/src/wallet/external_signer.cpp
index 6f850e4779..c06b178bc1 100644
--- a/src/wallet/external_signer.cpp
+++ b/src/wallet/external_signer.cpp
@@ -2,7 +2,57 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <chainparams.h>
#include <wallet/external_signer.h>
-#include <util/system.h>
-ExternalSigner::ExternalSigner(const std::string& command, const std::string& fingerprint): m_command(command), m_fingerprint(fingerprint) {}
+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;
+}
+
+#endif
diff --git a/src/wallet/external_signer.h b/src/wallet/external_signer.h
index 08fb0c9f39..9d32fe1e04 100644
--- a/src/wallet/external_signer.h
+++ b/src/wallet/external_signer.h
@@ -8,6 +8,7 @@
#include <stdexcept>
#include <string>
#include <univalue.h>
+#include <util/system.h>
class ExternalSignerException : public std::runtime_error {
public:
@@ -25,10 +26,30 @@ private:
public:
//! @param[in] command the command which handles interaction with the external signer
//! @param[in] fingerprint master key fingerprint of the signer
- ExternalSigner(const std::string& command, const std::string& fingerprint);
+ //! @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);
+
+#endif
};
#endif // BITCOIN_WALLET_EXTERNAL_SIGNER_H
diff --git a/src/wallet/rpcsigner.cpp b/src/wallet/rpcsigner.cpp
index d2478908df..76f4f3c6aa 100644
--- a/src/wallet/rpcsigner.cpp
+++ b/src/wallet/rpcsigner.cpp
@@ -2,36 +2,69 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <chainparamsbase.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
-// CRPCCommand table won't compile with an empty array
-static RPCHelpMan dummy()
+static RPCHelpMan enumeratesigners()
{
- return RPCHelpMan{"dummy",
- "\nDoes nothing.\n"
- "",
- {},
- RPCResult{RPCResult::Type::NONE, "", ""},
- RPCExamples{""},
- [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
-{
- return NullUniValue;
-},
+ 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 {
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+
+ 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;
+ }
};
}
Span<const CRPCCommand> GetSignerRPCCommands()
{
+
// clang-format off
static const CRPCCommand commands[] =
{ // category actor (function)
// --------------------- ------------------------
- { "signer", &dummy, },
+ { "signer", &enumeratesigners, },
};
// clang-format on
return MakeSpan(commands);