external_signer_scriptpubkeyman.cpp raw

   1  // Copyright (c) 2020-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 <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  std::unique_ptr<ExternalSignerScriptPubKeyMan> ExternalSignerScriptPubKeyMan::LoadFromStorage(WalletStorage& storage, WalletDescriptor& descriptor, int64_t keypool_size, const KeyMap& keys, const CryptedKeyMap& ckeys)
  25  {
  26      return std::unique_ptr<ExternalSignerScriptPubKeyMan>(new ExternalSignerScriptPubKeyMan(storage, descriptor, keypool_size, keys, ckeys));
  27  }
  28  
  29  std::unique_ptr<ExternalSignerScriptPubKeyMan> ExternalSignerScriptPubKeyMan::CreateNew(WalletStorage& storage, WalletBatch& batch, int64_t keypool_size, std::unique_ptr<Descriptor> desc)
  30  {
  31      auto spkm = std::unique_ptr<ExternalSignerScriptPubKeyMan>(new ExternalSignerScriptPubKeyMan(storage, keypool_size));
  32  
  33      LOCK(spkm->cs_desc_man);
  34      assert(storage.IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS));
  35      assert(storage.IsWalletFlagSet(WALLET_FLAG_EXTERNAL_SIGNER));
  36  
  37      int64_t creation_time = GetTime();
  38  
  39      // Make the descriptor
  40      WalletDescriptor w_desc(std::move(desc), creation_time, 0, 0, 0);
  41      spkm->m_wallet_descriptor = w_desc;
  42  
  43      // Store the descriptor
  44      if (!batch.WriteDescriptor(spkm->GetID(), spkm->m_wallet_descriptor)) {
  45          throw std::runtime_error(std::string(__func__) + ": writing descriptor failed");
  46      }
  47  
  48      // TopUp
  49      spkm->TopUpWithDB(batch);
  50  
  51      storage.UnsetBlankWalletFlag(batch);
  52      return spkm;
  53  }
  54  
  55   util::Result<ExternalSigner> ExternalSignerScriptPubKeyMan::GetExternalSigner() {
  56      const std::string command = gArgs.GetArg("-signer", "");
  57      if (command == "") return util::Error{Untranslated("restart bitcoind with -signer=<cmd>")};
  58      std::vector<ExternalSigner> signers;
  59      ExternalSigner::Enumerate(command, signers, Params().GetChainTypeString());
  60      if (signers.empty()) return util::Error{Untranslated("No external signers found")};
  61      // TODO: add fingerprint argument instead of failing in case of multiple signers.
  62      if (signers.size() > 1) return util::Error{Untranslated("More than one external signer found. Please connect only one at a time.")};
  63      return signers[0];
  64  }
  65  
  66  util::Result<void> ExternalSignerScriptPubKeyMan::DisplayAddress(const CTxDestination& dest, const ExternalSigner &signer) const
  67  {
  68      // TODO: avoid the need to infer a descriptor from inside a descriptor wallet
  69      const CScript& scriptPubKey = GetScriptForDestination(dest);
  70      auto provider = GetSolvingProvider(scriptPubKey);
  71      auto descriptor = InferDescriptor(scriptPubKey, *provider);
  72  
  73      const UniValue& result = signer.DisplayAddress(descriptor->ToString());
  74  
  75      const UniValue& error = result.find_value("error");
  76      if (error.isStr()) return util::Error{strprintf(_("Signer returned error: %s"), error.getValStr())};
  77  
  78      const UniValue& ret_address = result.find_value("address");
  79      if (!ret_address.isStr()) return util::Error{_("Signer did not echo address")};
  80  
  81      if (ret_address.getValStr() != EncodeDestination(dest)) {
  82          return util::Error{strprintf(_("Signer echoed unexpected address %s"), ret_address.getValStr())};
  83      }
  84  
  85      return util::Result<void>();
  86  }
  87  
  88  // If sign is true, transaction must previously have been filled
  89  std::optional<PSBTError> ExternalSignerScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata, const common::PSBTFillOptions& options, int* n_signed) const
  90  {
  91      if (!options.sign) {
  92          return DescriptorScriptPubKeyMan::FillPSBT(psbt, txdata, options, n_signed);
  93      }
  94  
  95      // Already complete if every input is now signed
  96      bool complete = true;
  97      for (const auto& input : psbt.inputs) {
  98          complete &= PSBTInputSigned(input);
  99      }
 100      if (complete) return {};
 101  
 102      auto signer{GetExternalSigner()};
 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 (options.finalize) FinalizePSBT(psbt); // This won't work in a multisig setup
 114      return {};
 115  }
 116  } // namespace wallet
 117