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