external_signer.cpp
1 // Copyright (c) 2018-present 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 <external_signer.h> 6 7 #include <chainparams.h> 8 #include <common/run_command.h> 9 #include <core_io.h> 10 #include <psbt.h> 11 #include <util/strencodings.h> 12 #include <util/subprocess.h> 13 14 #include <algorithm> 15 #include <stdexcept> 16 #include <string> 17 #include <vector> 18 19 ExternalSigner::ExternalSigner(std::vector<std::string> command, std::string chain, std::string fingerprint, std::string name) 20 : m_command{std::move(command)}, m_chain{std::move(chain)}, m_fingerprint{std::move(fingerprint)}, m_name{std::move(name)} {} 21 22 std::vector<std::string> ExternalSigner::NetworkArg() const 23 { 24 return {"--chain", m_chain}; 25 } 26 27 bool ExternalSigner::Enumerate(const std::string& command, std::vector<ExternalSigner>& signers, const std::string& chain) 28 { 29 // Call <command> enumerate 30 std::vector<std::string> cmd_args = Cat(subprocess::util::split(command), {"enumerate"}); 31 32 const UniValue result = RunCommandParseJSON(cmd_args, ""); 33 if (!result.isArray()) { 34 throw std::runtime_error(strprintf("'%s' received invalid response, expected array of signers", command)); 35 } 36 for (const UniValue& signer : result.getValues()) { 37 // Check for error 38 const UniValue& error = signer.find_value("error"); 39 if (!error.isNull()) { 40 if (!error.isStr()) { 41 throw std::runtime_error(strprintf("'%s' error", command)); 42 } 43 throw std::runtime_error(strprintf("'%s' error: %s", command, error.getValStr())); 44 } 45 // Check if fingerprint is present 46 const UniValue& fingerprint = signer.find_value("fingerprint"); 47 if (fingerprint.isNull()) { 48 throw std::runtime_error(strprintf("'%s' received invalid response, missing signer fingerprint", command)); 49 } 50 const std::string& fingerprintStr{fingerprint.get_str()}; 51 // Skip duplicate signer 52 bool duplicate = false; 53 for (const ExternalSigner& signer : signers) { 54 if (signer.m_fingerprint.compare(fingerprintStr) == 0) duplicate = true; 55 } 56 if (duplicate) break; 57 std::string name; 58 const UniValue& model_field = signer.find_value("model"); 59 if (model_field.isStr() && model_field.getValStr() != "") { 60 name += model_field.getValStr(); 61 } 62 signers.emplace_back(subprocess::util::split(command), chain, fingerprintStr, name); 63 } 64 return true; 65 } 66 67 UniValue ExternalSigner::DisplayAddress(const std::string& descriptor) const 68 { 69 return RunCommandParseJSON(Cat(m_command, Cat(Cat({"--fingerprint", m_fingerprint}, NetworkArg()), {"displayaddress", "--desc", descriptor})), ""); 70 } 71 72 UniValue ExternalSigner::GetDescriptors(const int account) 73 { 74 return RunCommandParseJSON(Cat(m_command, Cat(Cat({"--fingerprint", m_fingerprint}, NetworkArg()), {"getdescriptors", "--account", strprintf("%d", account)})), ""); 75 } 76 77 bool ExternalSigner::SignTransaction(PartiallySignedTransaction& psbtx, std::string& error) 78 { 79 // Serialize the PSBT 80 DataStream ssTx{}; 81 ssTx << psbtx; 82 // parse ExternalSigner master fingerprint 83 std::vector<unsigned char> parsed_m_fingerprint = ParseHex(m_fingerprint); 84 // Check if signer fingerprint matches any input master key fingerprint 85 auto matches_signer_fingerprint = [&](const PSBTInput& input) { 86 for (const auto& entry : input.hd_keypaths) { 87 if (std::ranges::equal(parsed_m_fingerprint, entry.second.fingerprint)) return true; 88 } 89 for (const auto& entry : input.m_tap_bip32_paths) { 90 if (std::ranges::equal(parsed_m_fingerprint, entry.second.second.fingerprint)) return true; 91 } 92 return false; 93 }; 94 95 if (!std::any_of(psbtx.inputs.begin(), psbtx.inputs.end(), matches_signer_fingerprint)) { 96 error = "Signer fingerprint " + m_fingerprint + " does not match any of the inputs:\n" + EncodeBase64(ssTx.str()); 97 return false; 98 } 99 100 const std::vector<std::string> command = Cat(m_command, Cat({"--stdin", "--fingerprint", m_fingerprint}, NetworkArg())); 101 const std::string stdinStr = "signtx " + EncodeBase64(ssTx.str()); 102 103 const UniValue signer_result = RunCommandParseJSON(command, stdinStr); 104 105 if (signer_result.find_value("error").isStr()) { 106 error = signer_result.find_value("error").get_str(); 107 return false; 108 } 109 110 if (!signer_result.find_value("psbt").isStr()) { 111 error = "Unexpected result from signer"; 112 return false; 113 } 114 115 PartiallySignedTransaction signer_psbtx; 116 std::string signer_psbt_error; 117 if (!DecodeBase64PSBT(signer_psbtx, signer_result.find_value("psbt").get_str(), signer_psbt_error)) { 118 error = strprintf("TX decode failed %s", signer_psbt_error); 119 return false; 120 } 121 122 psbtx = signer_psbtx; 123 124 return true; 125 }