mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-12-05 10:12:48 +01:00
rpc: signer: add enumeratesigners to list external signers
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user