external_signer_scriptpubkeyman.cpp raw

   1  // Copyright (c) 2020-2022 The Limenka 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/args.h>
   7  #include <common/system.h>
   8  #include <external_signer.h>
   9  #include <node/types.h>
  10  #include <wallet/external_signer_scriptpubkeyman.h>
  11  
  12  #include <iostream>
  13  #include <key_io.h>
  14  #include <memory>
  15  #include <stdexcept>
  16  #include <string>
  17  #include <univalue.h>
  18  #include <utility>
  19  #include <vector>
  20  
  21  using common::PSBTError;
  22  
  23  namespace wallet {
  24  bool ExternalSignerScriptPubKeyMan::SetupDescriptor(WalletBatch& batch, std::unique_ptr<Descriptor> desc)
  25  {
  26      LOCK(cs_desc_man);
  27      assert(m_storage.IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS));
  28      assert(m_storage.IsWalletFlagSet(WALLET_FLAG_EXTERNAL_SIGNER));
  29  
  30      int64_t creation_time = GetTime();
  31  
  32      // Make the descriptor
  33      WalletDescriptor w_desc(std::move(desc), creation_time, 0, 0, 0);
  34      m_wallet_descriptor = w_desc;
  35  
  36      // Store the descriptor
  37      if (!batch.WriteDescriptor(GetID(), m_wallet_descriptor)) {
  38          throw std::runtime_error(std::string(__func__) + ": writing descriptor failed");
  39      }
  40  
  41      // TopUp
  42      TopUpWithDB(batch);
  43  
  44      m_storage.UnsetBlankWalletFlag(batch);
  45      return true;
  46  }
  47  
  48  ExternalSigner ExternalSignerScriptPubKeyMan::GetExternalSigner() {
  49      auto signer{GetExternalSigner2()};
  50      if (!signer) throw std::runtime_error(util::ErrorString(signer).original);
  51      return *signer;
  52  }
  53  
  54  util::Result<ExternalSigner> ExternalSignerScriptPubKeyMan::GetExternalSigner2() {
  55      const std::string command = gArgs.GetArg("-signer", "");
  56      if (command == "") return util::Error{Untranslated("restart limenkad with -signer=<cmd>")};
  57      std::vector<ExternalSigner> signers;
  58      ExternalSigner::Enumerate(command, signers, Params().GetChainTypeString());
  59      if (signers.empty()) return util::Error{Untranslated("No external signers found")};
  60      // TODO: add fingerprint argument instead of failing in case of multiple signers.
  61      if (signers.size() > 1) return util::Error{Untranslated("More than one external signer found. Please connect only one at a time.")};
  62      return signers[0];
  63  }
  64  
  65  util::Result<void> ExternalSignerScriptPubKeyMan::DisplayAddress(const CTxDestination& dest, const ExternalSigner &signer) const
  66  {
  67      // TODO: avoid the need to infer a descriptor from inside a descriptor wallet
  68      const CScript& scriptPubKey = GetScriptForDestination(dest);
  69      auto provider = GetSolvingProvider(scriptPubKey);
  70      auto descriptor = InferDescriptor(scriptPubKey, *provider);
  71  
  72      const UniValue& result = signer.DisplayAddress(descriptor->ToString());
  73  
  74      const UniValue& error = result.find_value("error");
  75      if (error.isStr()) return util::Error{strprintf(_("Signer returned error: %s"), error.getValStr())};
  76  
  77      const UniValue& ret_address = result.find_value("address");
  78      if (!ret_address.isStr()) return util::Error{_("Signer did not echo address")};
  79  
  80      if (ret_address.getValStr() != EncodeDestination(dest)) {
  81          return util::Error{strprintf(_("Signer echoed unexpected address %s"), ret_address.getValStr())};
  82      }
  83  
  84      return util::Result<void>();
  85  }
  86  
  87  // If sign is true, transaction must previously have been filled
  88  std::optional<PSBTError> ExternalSignerScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata, int sighash_type, bool sign, bool bip32derivs, int* n_signed, bool finalize) const
  89  {
  90      if (!sign) {
  91          return DescriptorScriptPubKeyMan::FillPSBT(psbt, txdata, sighash_type, false, bip32derivs, n_signed, finalize);
  92      }
  93  
  94      // Already complete if every input is now signed
  95      bool complete = true;
  96      for (const auto& input : psbt.inputs) {
  97          // TODO: for multisig wallets, we should only care if all _our_ inputs are signed
  98          complete &= PSBTInputSigned(input);
  99      }
 100      if (complete) return {};
 101  
 102      auto signer{GetExternalSigner2()};
 103      if (!signer) {
 104          LogWarning("%s", util::ErrorString(signer).original);
 105          return PSBTError::EXTERNAL_SIGNER_NOT_FOUND;
 106      }
 107  
 108      std::string failure_reason;
 109      if(!signer->SignTransaction(psbt, failure_reason)) {
 110          LogWarning("Failed to sign: %s\n", failure_reason);
 111          return PSBTError::EXTERNAL_SIGNER_FAILED;
 112      }
 113      if (finalize) FinalizePSBT(psbt); // This won't work in a multisig setup
 114      return {};
 115  }
 116  } // namespace wallet
 117