LCOV - code coverage report
Current view: top level - src - external_signer.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 50 64 78.1 %
Date: 2026-06-25 07:23:43 Functions: 9 9 100.0 %

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

Generated by: LCOV version 1.16