Branch data Line data Source code
# 1 : : // Copyright (c) 2018-2021 The Bitcoin Core developers
# 2 : : // Distributed under the MIT software license, see the accompanying
# 3 : : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
# 4 : :
# 5 : : #include <chainparams.h>
# 6 : : #include <core_io.h>
# 7 : : #include <psbt.h>
# 8 : : #include <util/strencodings.h>
# 9 : : #include <util/system.h>
# 10 : : #include <external_signer.h>
# 11 : :
# 12 : : #include <algorithm>
# 13 : : #include <stdexcept>
# 14 : : #include <string>
# 15 : : #include <vector>
# 16 : :
# 17 : 12 : ExternalSigner::ExternalSigner(const std::string& command, const std::string chain, const std::string& fingerprint, const std::string name): m_command(command), m_chain(chain), m_fingerprint(fingerprint), m_name(name) {}
# 18 : :
# 19 : : const std::string ExternalSigner::NetworkArg() const
# 20 : 5 : {
# 21 : 5 : return " --chain " + m_chain;
# 22 : 5 : }
# 23 : :
# 24 : : bool ExternalSigner::Enumerate(const std::string& command, std::vector<ExternalSigner>& signers, const std::string chain)
# 25 : 10 : {
# 26 : : // Call <command> enumerate
# 27 : 10 : const UniValue result = RunCommandParseJSON(command + " enumerate");
# 28 [ - + ]: 10 : if (!result.isArray()) {
# 29 : 0 : throw std::runtime_error(strprintf("'%s' received invalid response, expected array of signers", command));
# 30 : 0 : }
# 31 [ + + ]: 13 : for (UniValue signer : result.getValues()) {
# 32 : : // Check for error
# 33 : 13 : const UniValue& error = find_value(signer, "error");
# 34 [ + + ]: 13 : if (!error.isNull()) {
# 35 [ - + ]: 1 : if (!error.isStr()) {
# 36 : 0 : throw std::runtime_error(strprintf("'%s' error", command));
# 37 : 0 : }
# 38 : 1 : throw std::runtime_error(strprintf("'%s' error: %s", command, error.getValStr()));
# 39 : 1 : }
# 40 : : // Check if fingerprint is present
# 41 : 12 : const UniValue& fingerprint = find_value(signer, "fingerprint");
# 42 [ - + ]: 12 : if (fingerprint.isNull()) {
# 43 : 0 : throw std::runtime_error(strprintf("'%s' received invalid response, missing signer fingerprint", command));
# 44 : 0 : }
# 45 : 12 : const std::string fingerprintStr = fingerprint.get_str();
# 46 : : // Skip duplicate signer
# 47 : 12 : bool duplicate = false;
# 48 [ + + ]: 12 : for (const ExternalSigner& signer : signers) {
# 49 [ - + ]: 6 : if (signer.m_fingerprint.compare(fingerprintStr) == 0) duplicate = true;
# 50 : 6 : }
# 51 [ - + ]: 12 : if (duplicate) break;
# 52 : 12 : std::string name = "";
# 53 : 12 : const UniValue& model_field = find_value(signer, "model");
# 54 [ + + ][ + - ]: 12 : if (model_field.isStr() && model_field.getValStr() != "") {
# 55 : 6 : name += model_field.getValStr();
# 56 : 6 : }
# 57 : 12 : signers.push_back(ExternalSigner(command, chain, fingerprintStr, name));
# 58 : 12 : }
# 59 : 9 : return true;
# 60 : 10 : }
# 61 : :
# 62 : : UniValue ExternalSigner::DisplayAddress(const std::string& descriptor) const
# 63 : 1 : {
# 64 : 1 : return RunCommandParseJSON(m_command + " --fingerprint \"" + m_fingerprint + "\"" + NetworkArg() + " displayaddress --desc \"" + descriptor + "\"");
# 65 : 1 : }
# 66 : :
# 67 : : UniValue ExternalSigner::GetDescriptors(const int account)
# 68 : 2 : {
# 69 : 2 : return RunCommandParseJSON(m_command + " --fingerprint \"" + m_fingerprint + "\"" + NetworkArg() + " getdescriptors --account " + strprintf("%d", account));
# 70 : 2 : }
# 71 : :
# 72 : : bool ExternalSigner::SignTransaction(PartiallySignedTransaction& psbtx, std::string& error)
# 73 : 2 : {
# 74 : : // Serialize the PSBT
# 75 : 2 : CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
# 76 : 2 : ssTx << psbtx;
# 77 : :
# 78 : : // Check if signer fingerprint matches any input master key fingerprint
# 79 : 2 : auto matches_signer_fingerprint = [&](const PSBTInput& input) {
# 80 [ + - ]: 2 : for (const auto& entry : input.hd_keypaths) {
# 81 [ + - ]: 2 : if (m_fingerprint == strprintf("%08x", ReadBE32(entry.second.fingerprint))) return true;
# 82 : 2 : }
# 83 : 0 : return false;
# 84 : 2 : };
# 85 : :
# 86 [ - + ]: 2 : if (!std::any_of(psbtx.inputs.begin(), psbtx.inputs.end(), matches_signer_fingerprint)) {
# 87 : 0 : error = "Signer fingerprint " + m_fingerprint + " does not match any of the inputs:\n" + EncodeBase64(ssTx.str());
# 88 : 0 : return false;
# 89 : 0 : }
# 90 : :
# 91 : 2 : const std::string command = m_command + " --stdin --fingerprint \"" + m_fingerprint + "\"" + NetworkArg();
# 92 : 2 : const std::string stdinStr = "signtx \"" + EncodeBase64(ssTx.str()) + "\"";
# 93 : :
# 94 : 2 : const UniValue signer_result = RunCommandParseJSON(command, stdinStr);
# 95 : :
# 96 [ - + ]: 2 : if (find_value(signer_result, "error").isStr()) {
# 97 : 0 : error = find_value(signer_result, "error").get_str();
# 98 : 0 : return false;
# 99 : 0 : }
# 100 : :
# 101 [ - + ]: 2 : if (!find_value(signer_result, "psbt").isStr()) {
# 102 : 0 : error = "Unexpected result from signer";
# 103 : 0 : return false;
# 104 : 0 : }
# 105 : :
# 106 : 2 : PartiallySignedTransaction signer_psbtx;
# 107 : 2 : std::string signer_psbt_error;
# 108 [ - + ]: 2 : if (!DecodeBase64PSBT(signer_psbtx, find_value(signer_result, "psbt").get_str(), signer_psbt_error)) {
# 109 : 0 : error = strprintf("TX decode failed %s", signer_psbt_error);
# 110 : 0 : return false;
# 111 : 0 : }
# 112 : :
# 113 : 2 : psbtx = signer_psbtx;
# 114 : :
# 115 : 2 : return true;
# 116 : 2 : }
|