mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-07-21 21:59:56 +02:00
451fdd26a4test: wallet: Constructing a DSPKM that can't TopUp() throws. (David Gumberg)32946e0291wallet: Setup new autogenerated descriptors on construction (Ava Chow)e20aaff70fwallet: Construct ExternalSignerSPKM with the new descriptor (Ava Chow)aa4f7823aawallet: include keys when constructing DescriptorSPKM during import (Ava Chow)6538f69135fuzz: Skip adding descriptor to wallet if it cannot be expanded (Ava Chow)8be5ee554btest: wallet: Check that loading wallet with both unencrypted and encrypted keys fails. (David Gumberg)80b0c25992wallet: Load everything into DescSPKM on construction (Ava Chow)f713fd1725refactor: wallet: Don't reuse WALLET_BLANK flag for born-encrypted wallets. (David Gumberg)cd912c4e10wallet: Consolidate generation setup callers into one function (Ava Chow)0301c758eawallet migration, fuzz: Migrate hd seed once (Ava Chow) Pull request description: Instead of constructing ScriptPubKeyMans with no data, and then loading data as we find it, we should gather everything first and then load it all on construction. If there actually is no data and we want to setup generation, then that should also occur in a constructor rather than afterwards. This change is only applied to DescriptorScriptPubKeyMan and ExternalSignerScriptPubKeyMan, and should be done for any ScriptPubKeyMans added in the future. I don't think it's really worth it to do this for LegacyScriptPubKeyMan since it would make loading performance worse (or cause layer violations) and it's (supposed to be) going away soon. ACKs for top commit: polespinasa: ACK451fdd26a4davidgumberg: re crACK451fdd26a4w0xlt: ACK451fdd26a4Tree-SHA512: 58a889bf7c77d5da78041907a76a1958207f95a19bec8dc4d86d4e4108d256a729e0949c0973f7d447178f78a7fd4268cda71d358cae4dec5a76dc453b5283af
117 lines
4.6 KiB
C++
117 lines
4.6 KiB
C++
// Copyright (c) 2020-present The Bitcoin Core developers
|
|
// Distributed under the MIT software license, see the accompanying
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
#include <chainparams.h>
|
|
#include <common/args.h>
|
|
#include <common/system.h>
|
|
#include <external_signer.h>
|
|
#include <node/types.h>
|
|
#include <wallet/external_signer_scriptpubkeyman.h>
|
|
|
|
#include <iostream>
|
|
#include <key_io.h>
|
|
#include <memory>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#include <univalue.h>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
using common::PSBTError;
|
|
|
|
namespace wallet {
|
|
std::unique_ptr<ExternalSignerScriptPubKeyMan> ExternalSignerScriptPubKeyMan::LoadFromStorage(WalletStorage& storage, WalletDescriptor& descriptor, int64_t keypool_size, const KeyMap& keys, const CryptedKeyMap& ckeys)
|
|
{
|
|
return std::unique_ptr<ExternalSignerScriptPubKeyMan>(new ExternalSignerScriptPubKeyMan(storage, descriptor, keypool_size, keys, ckeys));
|
|
}
|
|
|
|
std::unique_ptr<ExternalSignerScriptPubKeyMan> ExternalSignerScriptPubKeyMan::CreateNew(WalletStorage& storage, WalletBatch& batch, int64_t keypool_size, std::unique_ptr<Descriptor> desc)
|
|
{
|
|
auto spkm = std::unique_ptr<ExternalSignerScriptPubKeyMan>(new ExternalSignerScriptPubKeyMan(storage, keypool_size));
|
|
|
|
LOCK(spkm->cs_desc_man);
|
|
assert(storage.IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS));
|
|
assert(storage.IsWalletFlagSet(WALLET_FLAG_EXTERNAL_SIGNER));
|
|
|
|
int64_t creation_time = GetTime();
|
|
|
|
// Make the descriptor
|
|
WalletDescriptor w_desc(std::move(desc), creation_time, 0, 0, 0);
|
|
spkm->m_wallet_descriptor = w_desc;
|
|
|
|
// Store the descriptor
|
|
if (!batch.WriteDescriptor(spkm->GetID(), spkm->m_wallet_descriptor)) {
|
|
throw std::runtime_error(std::string(__func__) + ": writing descriptor failed");
|
|
}
|
|
|
|
// TopUp
|
|
spkm->TopUpWithDB(batch);
|
|
|
|
storage.UnsetBlankWalletFlag(batch);
|
|
return spkm;
|
|
}
|
|
|
|
util::Result<ExternalSigner> ExternalSignerScriptPubKeyMan::GetExternalSigner() {
|
|
const std::string command = gArgs.GetArg("-signer", "");
|
|
if (command == "") return util::Error{Untranslated("restart bitcoind with -signer=<cmd>")};
|
|
std::vector<ExternalSigner> signers;
|
|
ExternalSigner::Enumerate(command, signers, Params().GetChainTypeString());
|
|
if (signers.empty()) return util::Error{Untranslated("No external signers found")};
|
|
// TODO: add fingerprint argument instead of failing in case of multiple signers.
|
|
if (signers.size() > 1) return util::Error{Untranslated("More than one external signer found. Please connect only one at a time.")};
|
|
return signers[0];
|
|
}
|
|
|
|
util::Result<void> ExternalSignerScriptPubKeyMan::DisplayAddress(const CTxDestination& dest, const ExternalSigner &signer) const
|
|
{
|
|
// TODO: avoid the need to infer a descriptor from inside a descriptor wallet
|
|
const CScript& scriptPubKey = GetScriptForDestination(dest);
|
|
auto provider = GetSolvingProvider(scriptPubKey);
|
|
auto descriptor = InferDescriptor(scriptPubKey, *provider);
|
|
|
|
const UniValue& result = signer.DisplayAddress(descriptor->ToString());
|
|
|
|
const UniValue& error = result.find_value("error");
|
|
if (error.isStr()) return util::Error{strprintf(_("Signer returned error: %s"), error.getValStr())};
|
|
|
|
const UniValue& ret_address = result.find_value("address");
|
|
if (!ret_address.isStr()) return util::Error{_("Signer did not echo address")};
|
|
|
|
if (ret_address.getValStr() != EncodeDestination(dest)) {
|
|
return util::Error{strprintf(_("Signer echoed unexpected address %s"), ret_address.getValStr())};
|
|
}
|
|
|
|
return util::Result<void>();
|
|
}
|
|
|
|
// If sign is true, transaction must previously have been filled
|
|
std::optional<PSBTError> ExternalSignerScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata, const common::PSBTFillOptions& options, int* n_signed) const
|
|
{
|
|
if (!options.sign) {
|
|
return DescriptorScriptPubKeyMan::FillPSBT(psbt, txdata, options, n_signed);
|
|
}
|
|
|
|
// Already complete if every input is now signed
|
|
bool complete = true;
|
|
for (const auto& input : psbt.inputs) {
|
|
complete &= PSBTInputSigned(input);
|
|
}
|
|
if (complete) return {};
|
|
|
|
auto signer{GetExternalSigner()};
|
|
if (!signer) {
|
|
LogWarning("%s", util::ErrorString(signer).original);
|
|
return PSBTError::EXTERNAL_SIGNER_NOT_FOUND;
|
|
}
|
|
|
|
std::string failure_reason;
|
|
if(!signer->SignTransaction(psbt, failure_reason)) {
|
|
LogWarning("Failed to sign: %s\n", failure_reason);
|
|
return PSBTError::EXTERNAL_SIGNER_FAILED;
|
|
}
|
|
if (options.finalize) FinalizePSBT(psbt); // This won't work in a multisig setup
|
|
return {};
|
|
}
|
|
} // namespace wallet
|