diff options
author | Sjors Provoost <sjors@sprovoost.nl> | 2019-02-15 12:54:29 +0100 |
---|---|---|
committer | Sjors Provoost <sjors@sprovoost.nl> | 2021-02-23 14:34:31 +0100 |
commit | 2700f09c4130af6167ce71f46960e92ca800e205 (patch) | |
tree | 2b4c23317bc24a39e3e7e3dab598359249238613 /src/wallet | |
parent | 07b7c940a7da138d55a484ef83fee19ebf58a867 (diff) |
rpc: signer: add enumeratesigners to list external signers
Diffstat (limited to 'src/wallet')
-rw-r--r-- | src/wallet/external_signer.cpp | 54 | ||||
-rw-r--r-- | src/wallet/external_signer.h | 23 | ||||
-rw-r--r-- | src/wallet/rpcsigner.cpp | 59 |
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); |