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