Inside Bitcoin
Opening the selected file…
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1// Copyright (c) 2020-present The Bitcoin Core developers2// Distributed under the MIT software license, see the accompanying3// file COPYING or http://www.opensource.org/licenses/mit-license.php.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>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>21using common::PSBTError;23namespace wallet {24std::unique_ptr<ExternalSignerScriptPubKeyMan> ExternalSignerScriptPubKeyMan::LoadFromStorage(WalletStorage& storage, const uint256& id, WalletDescriptor& descriptor, int64_t keypool_size, const KeyMap& keys, const CryptedKeyMap& ckeys)25{26return std::unique_ptr<ExternalSignerScriptPubKeyMan>(new ExternalSignerScriptPubKeyMan(storage, id, descriptor, keypool_size, keys, ckeys));27}29std::unique_ptr<ExternalSignerScriptPubKeyMan> ExternalSignerScriptPubKeyMan::CreateNew(WalletStorage& storage, WalletBatch& batch, int64_t keypool_size, std::unique_ptr<Descriptor> desc)30{31int64_t creation_time = GetTime();33// Make the descriptor34WalletDescriptor w_desc(std::move(desc), creation_time, 0, 0, 0);36auto spkm = std::unique_ptr<ExternalSignerScriptPubKeyMan>(new ExternalSignerScriptPubKeyMan(storage, w_desc, keypool_size));38LOCK(spkm->cs_desc_man);39assert(storage.IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS));40assert(storage.IsWalletFlagSet(WALLET_FLAG_EXTERNAL_SIGNER));42// Store the descriptor43if (!batch.WriteDescriptor(spkm->GetID(), spkm->m_wallet_descriptor)) {44throw std::runtime_error(std::string(__func__) + ": writing descriptor failed");45}47// TopUp48spkm->TopUpWithDB(batch);50storage.UnsetBlankWalletFlag(batch);51return spkm;52}54util::Result<ExternalSigner> ExternalSignerScriptPubKeyMan::GetExternalSigner() {55const std::string command = gArgs.GetArg("-signer", "");56if (command == "") return util::Error{Untranslated("restart bitcoind with -signer=<cmd>")};57std::vector<ExternalSigner> signers;58ExternalSigner::Enumerate(command, signers, Params().GetChainTypeString());59if (signers.empty()) return util::Error{Untranslated("No external signers found")};60// TODO: add fingerprint argument instead of failing in case of multiple signers.61if (signers.size() > 1) return util::Error{Untranslated("More than one external signer found. Please connect only one at a time.")};62return signers[0];63}65util::Result<void> ExternalSignerScriptPubKeyMan::DisplayAddress(const CTxDestination& dest, const ExternalSigner &signer) const66{67// TODO: avoid the need to infer a descriptor from inside a descriptor wallet68const CScript& scriptPubKey = GetScriptForDestination(dest);69auto provider = GetSolvingProvider(scriptPubKey);70auto descriptor = InferDescriptor(scriptPubKey, *provider);72const UniValue& result = signer.DisplayAddress(descriptor->ToString());74const UniValue& error = result.find_value("error");75if (error.isStr()) return util::Error{strprintf(_("Signer returned error: %s"), error.getValStr())};77const UniValue& ret_address = result.find_value("address");78if (!ret_address.isStr()) return util::Error{_("Signer did not echo address")};80if (ret_address.getValStr() != EncodeDestination(dest)) {81return util::Error{strprintf(_("Signer echoed unexpected address %s"), ret_address.getValStr())};82}84return util::Result<void>();85}87// If sign is true, transaction must previously have been filled88std::optional<PSBTError> ExternalSignerScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata, const common::PSBTFillOptions& options, int* n_signed) const89{90if (!options.sign) {91return DescriptorScriptPubKeyMan::FillPSBT(psbt, txdata, options, n_signed);92}94// Already complete if every input is now signed95bool complete = true;96for (const auto& input : psbt.inputs) {97complete &= PSBTInputSigned(input);98}99if (complete) return {};101auto signer{GetExternalSigner()};102if (!signer) {103LogWarning("%s", util::ErrorString(signer).original);104return PSBTError::EXTERNAL_SIGNER_NOT_FOUND;105}107std::string failure_reason;108if(!signer->SignTransaction(psbt, failure_reason)) {109LogWarning("Failed to sign: %s\n", failure_reason);110return PSBTError::EXTERNAL_SIGNER_FAILED;111}112if (options.finalize) FinalizePSBT(psbt); // This won't work in a multisig setup113return {};114}115} // namespace wallet