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