Skip to content

Commit 82a30fa

Browse files
committed
Move key and script filling and signing from CWallet::FillPSBT to ScriptPubKeyMan::FillPSBT
Instead of fetching a SigningProvider from ScriptPubKeyMan in order to fill and sign the keys and scripts for a PSBT, just pass that PSBT to a new FillPSBT function that does all that for us.
1 parent 3d70dd9 commit 82a30fa

File tree

4 files changed

+82
-20
lines changed

4 files changed

+82
-20
lines changed

src/wallet/scriptpubkeyman.cpp

+42
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,48 @@ bool LegacyScriptPubKeyMan::SignTransaction(CMutableTransaction& tx, const std::
511511
return ::SignTransaction(tx, this, coins, sighash, input_errors);
512512
}
513513

514+
TransactionError LegacyScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbtx, int sighash_type, bool sign, bool bip32derivs) const
515+
{
516+
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
517+
const CTxIn& txin = psbtx.tx->vin[i];
518+
PSBTInput& input = psbtx.inputs.at(i);
519+
520+
if (PSBTInputSigned(input)) {
521+
continue;
522+
}
523+
524+
// Verify input looks sane. This will check that we have at most one uxto, witness or non-witness.
525+
if (!input.IsSane()) {
526+
return TransactionError::INVALID_PSBT;
527+
}
528+
529+
// Get the Sighash type
530+
if (sign && input.sighash_type > 0 && input.sighash_type != sighash_type) {
531+
return TransactionError::SIGHASH_MISMATCH;
532+
}
533+
534+
// Check non_witness_utxo has specified prevout
535+
if (input.non_witness_utxo) {
536+
if (txin.prevout.n >= input.non_witness_utxo->vout.size()) {
537+
return TransactionError::MISSING_INPUTS;
538+
}
539+
} else if (input.witness_utxo.IsNull()) {
540+
// There's no UTXO so we can just skip this now
541+
continue;
542+
}
543+
SignatureData sigdata;
544+
input.FillSignatureData(sigdata);
545+
SignPSBTInput(HidingSigningProvider(this, !sign, !bip32derivs), psbtx, i, sighash_type);
546+
}
547+
548+
// Fill in the bip32 keypaths and redeemscripts for the outputs so that hardware wallets can identify change
549+
for (unsigned int i = 0; i < psbtx.tx->vout.size(); ++i) {
550+
UpdatePSBTOutput(HidingSigningProvider(this, true, !bip32derivs), psbtx, i);
551+
}
552+
553+
return TransactionError::OK;
554+
}
555+
514556
const CKeyMetadata* LegacyScriptPubKeyMan::GetMetadata(const CTxDestination& dest) const
515557
{
516558
LOCK(cs_KeyStore);

src/wallet/scriptpubkeyman.h

+5
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@
55
#ifndef BITCOIN_WALLET_SCRIPTPUBKEYMAN_H
66
#define BITCOIN_WALLET_SCRIPTPUBKEYMAN_H
77

8+
#include <psbt.h>
89
#include <script/signingprovider.h>
910
#include <script/standard.h>
11+
#include <util/error.h>
1012
#include <wallet/crypter.h>
1113
#include <wallet/ismine.h>
1214
#include <wallet/walletdb.h>
@@ -212,6 +214,8 @@ class ScriptPubKeyMan
212214

213215
/** Creates new signatures and adds them to the transaction. Returns whether all inputs were signed */
214216
virtual bool SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, std::string>& input_errors) const { return false; }
217+
/** Adds script and derivation path information to a PSBT, and optionally signs it. */
218+
virtual TransactionError FillPSBT(PartiallySignedTransaction& psbt, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false) const { return TransactionError::INVALID_PSBT; }
215219

216220
virtual uint256 GetID() const { return uint256(); }
217221

@@ -354,6 +358,7 @@ class LegacyScriptPubKeyMan : public ScriptPubKeyMan, public FillableSigningProv
354358
bool CanProvide(const CScript& script, SignatureData& sigdata) override;
355359

356360
bool SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, std::string>& input_errors) const override;
361+
TransactionError FillPSBT(PartiallySignedTransaction& psbt, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false) const override;
357362

358363
uint256 GetID() const override;
359364

src/wallet/test/psbt_wallet_tests.cpp

+1-3
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,7 @@ BOOST_AUTO_TEST_CASE(psbt_updater_test)
7373

7474
// Try to sign the mutated input
7575
SignatureData sigdata;
76-
psbtx.inputs[0].FillSignatureData(sigdata);
77-
const std::unique_ptr<SigningProvider> provider = m_wallet.GetSigningProvider(ws1, sigdata);
78-
BOOST_CHECK(!SignPSBTInput(*provider, psbtx, 0, SIGHASH_ALL));
76+
BOOST_CHECK(spk_man->FillPSBT(psbtx, SIGHASH_ALL, true, true) != TransactionError::OK);
7977
}
8078

8179
BOOST_AUTO_TEST_CASE(parse_hd_keypath)

src/wallet/wallet.cpp

+34-17
Original file line numberDiff line numberDiff line change
@@ -2481,7 +2481,6 @@ TransactionError CWallet::FillPSBT(PartiallySignedTransaction& psbtx, bool& comp
24812481
{
24822482
LOCK(cs_wallet);
24832483
// Get all of the previous transactions
2484-
complete = true;
24852484
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
24862485
const CTxIn& txin = psbtx.tx->vin[i];
24872486
PSBTInput& input = psbtx.inputs.at(i);
@@ -2506,13 +2505,22 @@ TransactionError CWallet::FillPSBT(PartiallySignedTransaction& psbtx, bool& comp
25062505
input.non_witness_utxo = wtx.tx;
25072506
}
25082507
}
2508+
}
2509+
2510+
// Fill in information from ScriptPubKeyMans
2511+
// Because each ScriptPubKeyMan may be able to fill more than one input, we need to keep track of each ScriptPubKeyMan that has filled this psbt.
2512+
// Each iteration, we may fill more inputs than the input that is specified in that iteration.
2513+
// We assume that each input is filled by only one ScriptPubKeyMan
2514+
std::set<uint256> visited_spk_mans;
2515+
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
2516+
const CTxIn& txin = psbtx.tx->vin[i];
2517+
PSBTInput& input = psbtx.inputs.at(i);
25092518

2510-
// Get the Sighash type
2511-
if (sign && input.sighash_type > 0 && input.sighash_type != sighash_type) {
2512-
return TransactionError::SIGHASH_MISMATCH;
2519+
if (PSBTInputSigned(input)) {
2520+
continue;
25132521
}
25142522

2515-
// Get the scriptPubKey to know which SigningProvider to use
2523+
// Get the scriptPubKey to know which ScriptPubKeyMan to use
25162524
CScript script;
25172525
if (!input.witness_utxo.IsNull()) {
25182526
script = input.witness_utxo.scriptPubKey;
@@ -2523,29 +2531,38 @@ TransactionError CWallet::FillPSBT(PartiallySignedTransaction& psbtx, bool& comp
25232531
script = input.non_witness_utxo->vout[txin.prevout.n].scriptPubKey;
25242532
} else {
25252533
// There's no UTXO so we can just skip this now
2526-
complete = false;
25272534
continue;
25282535
}
25292536
SignatureData sigdata;
25302537
input.FillSignatureData(sigdata);
2531-
std::unique_ptr<SigningProvider> provider = GetSigningProvider(script, sigdata);
2532-
if (!provider) {
2533-
complete = false;
2538+
std::set<ScriptPubKeyMan*> spk_mans = GetScriptPubKeyMans(script, sigdata);
2539+
if (spk_mans.size() == 0) {
25342540
continue;
25352541
}
25362542

2537-
complete &= SignPSBTInput(HidingSigningProvider(provider.get(), !sign, !bip32derivs), psbtx, i, sighash_type);
2538-
}
2543+
for (auto& spk_man : spk_mans) {
2544+
// If we've already been signed by this spk_man, skip it
2545+
if (visited_spk_mans.count(spk_man->GetID()) > 0) {
2546+
continue;
2547+
}
2548+
2549+
// Fill in the information from the spk_man
2550+
TransactionError res = spk_man->FillPSBT(psbtx, sighash_type, sign, bip32derivs);
2551+
if (res != TransactionError::OK) {
2552+
return res;
2553+
}
25392554

2540-
// Fill in the bip32 keypaths and redeemscripts for the outputs so that hardware wallets can identify change
2541-
for (unsigned int i = 0; i < psbtx.tx->vout.size(); ++i) {
2542-
const CTxOut& out = psbtx.tx->vout.at(i);
2543-
std::unique_ptr<SigningProvider> provider = GetSigningProvider(out.scriptPubKey);
2544-
if (provider) {
2545-
UpdatePSBTOutput(HidingSigningProvider(provider.get(), true, !bip32derivs), psbtx, i);
2555+
// Add this spk_man to visited_spk_mans so we can skip it later
2556+
visited_spk_mans.insert(spk_man->GetID());
25462557
}
25472558
}
25482559

2560+
// Complete if every input is now signed
2561+
complete = true;
2562+
for (const auto& input : psbtx.inputs) {
2563+
complete &= PSBTInputSigned(input);
2564+
}
2565+
25492566
return TransactionError::OK;
25502567
}
25512568

0 commit comments

Comments
 (0)