rpc: signerdisplayaddress

This commit is contained in:
Sjors Provoost
2020-02-19 14:33:37 +01:00
parent 7ebc7c0215
commit 245b4457cf
9 changed files with 134 additions and 0 deletions

View File

@@ -55,6 +55,11 @@ bool ExternalSigner::Enumerate(const std::string& command, std::vector<ExternalS
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));

View File

@@ -49,6 +49,11 @@ public:
//! @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'`)

View File

@@ -43,4 +43,39 @@ ExternalSigner ExternalSignerScriptPubKeyMan::GetExternalSigner() {
return signers[0];
}
bool ExternalSignerScriptPubKeyMan::DisplayAddress(const CScript scriptPubKey, const ExternalSigner &signer) const
{
// TODO: avoid the need to infer a descriptor from inside a descriptor wallet
auto provider = GetSolvingProvider(scriptPubKey);
auto descriptor = InferDescriptor(scriptPubKey, *provider);
signer.DisplayAddress(descriptor->ToString());
// TODO inspect result
return true;
}
// If sign is true, transaction must previously have been filled
TransactionError ExternalSignerScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbt, int sighash_type, bool sign, bool bip32derivs, int* n_signed) const
{
if (!sign) {
return DescriptorScriptPubKeyMan::FillPSBT(psbt, sighash_type, false, bip32derivs, n_signed);
}
// Already complete if every input is now signed
bool complete = true;
for (const auto& input : psbt.inputs) {
// TODO: for multisig wallets, we should only care if all _our_ inputs are signed
complete &= PSBTInputSigned(input);
}
if (complete) return TransactionError::OK;
std::string strFailReason;
if(!GetExternalSigner().SignTransaction(psbt, strFailReason)) {
tfm::format(std::cerr, "Failed to sign: %s\n", strFailReason);
return TransactionError::EXTERNAL_SIGNER_FAILED;
}
FinalizePSBT(psbt); // This won't work in a multisig setup
return TransactionError::OK;
}
#endif

View File

@@ -25,6 +25,7 @@ class ExternalSignerScriptPubKeyMan : public DescriptorScriptPubKeyMan
static ExternalSigner GetExternalSigner();
bool DisplayAddress(const CScript scriptPubKey, const ExternalSigner &signer) const;
};
#endif

View File

@@ -3,6 +3,7 @@
// 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>
@@ -57,6 +58,41 @@ static RPCHelpMan enumeratesigners()
};
}
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()
{
@@ -65,6 +101,7 @@ static const CRPCCommand commands[] =
{ // category actor (function)
// --------------------- ------------------------
{ "signer", &enumeratesigners, },
{ "signer", &signerdisplayaddress, },
};
// clang-format on
return MakeSpan(commands);

View File

@@ -3587,6 +3587,25 @@ ExternalSigner CWallet::GetExternalSigner()
}
#endif
bool CWallet::DisplayAddress(const CTxDestination& dest)
{
#ifdef ENABLE_EXTERNAL_SIGNER
CScript scriptPubKey = GetScriptForDestination(dest);
const auto spk_man = GetScriptPubKeyMan(scriptPubKey);
if (spk_man == nullptr) {
return false;
}
auto signer_spk_man = dynamic_cast<ExternalSignerScriptPubKeyMan*>(spk_man);
if (signer_spk_man == nullptr) {
return false;
}
ExternalSigner signer = GetExternalSigner(); // TODO: move signer in spk_man
return signer_spk_man->DisplayAddress(scriptPubKey, signer);
#else
return false;
#endif
}
void CWallet::LockCoin(const COutPoint& output)
{
AssertLockHeld(cs_wallet);

View File

@@ -842,6 +842,9 @@ public:
#ifdef ENABLE_EXTERNAL_SIGNER
ExternalSigner GetExternalSigner() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
#endif
/** Display address on an external signer. Returns false if external signer support is not compiled */
bool DisplayAddress(const CTxDestination& dest) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool IsLockedCoin(uint256 hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
void LockCoin(const COutPoint& output) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
void UnlockCoin(const COutPoint& output) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);