external_signer_scriptpubkeyman.cpp
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 <wallet/external_signer_scriptpubkeyman.h> 10 11 #include <iostream> 12 #include <memory> 13 #include <stdexcept> 14 #include <string> 15 #include <utility> 16 #include <vector> 17 18 namespace wallet { 19 bool ExternalSignerScriptPubKeyMan::SetupDescriptor(WalletBatch& batch, std::unique_ptr<Descriptor> desc) 20 { 21 LOCK(cs_desc_man); 22 assert(m_storage.IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)); 23 assert(m_storage.IsWalletFlagSet(WALLET_FLAG_EXTERNAL_SIGNER)); 24 25 int64_t creation_time = GetTime(); 26 27 // Make the descriptor 28 WalletDescriptor w_desc(std::move(desc), creation_time, 0, 0, 0); 29 m_wallet_descriptor = w_desc; 30 31 // Store the descriptor 32 if (!batch.WriteDescriptor(GetID(), m_wallet_descriptor)) { 33 throw std::runtime_error(std::string(__func__) + ": writing descriptor failed"); 34 } 35 36 // TopUp 37 TopUpWithDB(batch); 38 39 m_storage.UnsetBlankWalletFlag(batch); 40 return true; 41 } 42 43 ExternalSigner ExternalSignerScriptPubKeyMan::GetExternalSigner() { 44 const std::string command = gArgs.GetArg("-signer", ""); 45 if (command == "") throw std::runtime_error(std::string(__func__) + ": restart bitcoind with -signer=<cmd>"); 46 std::vector<ExternalSigner> signers; 47 ExternalSigner::Enumerate(command, signers, Params().GetChainTypeString()); 48 if (signers.empty()) throw std::runtime_error(std::string(__func__) + ": No external signers found"); 49 // TODO: add fingerprint argument instead of failing in case of multiple signers. 50 if (signers.size() > 1) throw std::runtime_error(std::string(__func__) + ": More than one external signer found. Please connect only one at a time."); 51 return signers[0]; 52 } 53 54 bool ExternalSignerScriptPubKeyMan::DisplayAddress(const CScript scriptPubKey, const ExternalSigner &signer) const 55 { 56 // TODO: avoid the need to infer a descriptor from inside a descriptor wallet 57 auto provider = GetSolvingProvider(scriptPubKey); 58 auto descriptor = InferDescriptor(scriptPubKey, *provider); 59 60 signer.DisplayAddress(descriptor->ToString()); 61 // TODO inspect result 62 return true; 63 } 64 65 // If sign is true, transaction must previously have been filled 66 TransactionError ExternalSignerScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata, int sighash_type, bool sign, bool bip32derivs, int* n_signed, bool finalize) const 67 { 68 if (!sign) { 69 return DescriptorScriptPubKeyMan::FillPSBT(psbt, txdata, sighash_type, false, bip32derivs, n_signed, finalize); 70 } 71 72 // Already complete if every input is now signed 73 bool complete = true; 74 for (const auto& input : psbt.inputs) { 75 // TODO: for multisig wallets, we should only care if all _our_ inputs are signed 76 complete &= PSBTInputSigned(input); 77 } 78 if (complete) return TransactionError::OK; 79 80 std::string strFailReason; 81 if(!GetExternalSigner().SignTransaction(psbt, strFailReason)) { 82 tfm::format(std::cerr, "Failed to sign: %s\n", strFailReason); 83 return TransactionError::EXTERNAL_SIGNER_FAILED; 84 } 85 if (finalize) FinalizePSBT(psbt); // This won't work in a multisig setup 86 return TransactionError::OK; 87 } 88 } // namespace wallet