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