Skip to content

Commit fd3f689

Browse files
committed
Construct and use PrecomputedTransactionData in PSBT signing
1 parent 5cb6502 commit fd3f689

10 files changed

+50
-21
lines changed

src/node/psbt.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx)
2323

2424
result.inputs.resize(psbtx.tx->vin.size());
2525

26+
const PrecomputedTransactionData txdata = PrecomputePSBTData(psbtx);
27+
2628
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
2729
PSBTInput& input = psbtx.inputs[i];
2830
PSBTInputAnalysis& input_analysis = result.inputs[i];
@@ -61,7 +63,7 @@ PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx)
6163

6264
// Figure out what is missing
6365
SignatureData outdata;
64-
bool complete = SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, 1, &outdata);
66+
bool complete = SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, &txdata, 1, &outdata);
6567

6668
// Things are missing
6769
if (!complete) {
@@ -121,7 +123,7 @@ PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx)
121123
PSBTInput& input = psbtx.inputs[i];
122124
Coin newcoin;
123125

124-
if (!SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, 1, nullptr, true) || !psbtx.GetInputUTXO(newcoin.out, i)) {
126+
if (!SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, nullptr, 1) || !psbtx.GetInputUTXO(newcoin.out, i)) {
125127
success = false;
126128
break;
127129
} else {

src/psbt.cpp

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,24 @@ void UpdatePSBTOutput(const SigningProvider& provider, PartiallySignedTransactio
227227
psbt_out.FromSignatureData(sigdata);
228228
}
229229

230-
bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, int sighash, SignatureData* out_sigdata, bool use_dummy)
230+
PrecomputedTransactionData PrecomputePSBTData(const PartiallySignedTransaction& psbt)
231+
{
232+
const CMutableTransaction& tx = *psbt.tx;
233+
bool have_all_spent_outputs = true;
234+
std::vector<CTxOut> utxos(tx.vin.size());
235+
for (size_t idx = 0; idx < tx.vin.size(); ++idx) {
236+
if (!psbt.GetInputUTXO(utxos[idx], idx)) have_all_spent_outputs = false;
237+
}
238+
PrecomputedTransactionData txdata;
239+
if (have_all_spent_outputs) {
240+
txdata.Init(tx, std::move(utxos), true);
241+
} else {
242+
txdata.Init(tx, {}, true);
243+
}
244+
return txdata;
245+
}
246+
247+
bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, const PrecomputedTransactionData* txdata, int sighash, SignatureData* out_sigdata)
231248
{
232249
PSBTInput& input = psbt.inputs.at(index);
233250
const CMutableTransaction& tx = *psbt.tx;
@@ -267,10 +284,10 @@ bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction&
267284

268285
sigdata.witness = false;
269286
bool sig_complete;
270-
if (use_dummy) {
287+
if (txdata == nullptr) {
271288
sig_complete = ProduceSignature(provider, DUMMY_SIGNATURE_CREATOR, utxo.scriptPubKey, sigdata);
272289
} else {
273-
MutableTransactionSignatureCreator creator(&tx, index, utxo.nValue, sighash);
290+
MutableTransactionSignatureCreator creator(&tx, index, utxo.nValue, txdata, sighash);
274291
sig_complete = ProduceSignature(provider, creator, utxo.scriptPubKey, sigdata);
275292
}
276293
// Verify that a witness signature was produced in case one was required.
@@ -302,8 +319,9 @@ bool FinalizePSBT(PartiallySignedTransaction& psbtx)
302319
// PartiallySignedTransaction did not understand them), this will combine them into a final
303320
// script.
304321
bool complete = true;
322+
const PrecomputedTransactionData txdata = PrecomputePSBTData(psbtx);
305323
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
306-
complete &= SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, SIGHASH_ALL);
324+
complete &= SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, &txdata, SIGHASH_ALL);
307325
}
308326

309327
return complete;

src/psbt.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -567,11 +567,18 @@ enum class PSBTRole {
567567

568568
std::string PSBTRoleName(PSBTRole role);
569569

570+
/** Compute a PrecomputedTransactionData object from a psbt. */
571+
PrecomputedTransactionData PrecomputePSBTData(const PartiallySignedTransaction& psbt);
572+
570573
/** Checks whether a PSBTInput is already signed. */
571574
bool PSBTInputSigned(const PSBTInput& input);
572575

573-
/** Signs a PSBTInput, verifying that all provided data matches what is being signed. */
574-
bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, int sighash = SIGHASH_ALL, SignatureData* out_sigdata = nullptr, bool use_dummy = false);
576+
/** Signs a PSBTInput, verifying that all provided data matches what is being signed.
577+
*
578+
* txdata should be the output of PrecomputePSBTData (which can be shared across
579+
* multiple SignPSBTInput calls). If it is nullptr, a dummy signature will be created.
580+
**/
581+
bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, const PrecomputedTransactionData* txdata, int sighash = SIGHASH_ALL, SignatureData* out_sigdata = nullptr);
575582

576583
/** Counts the unsigned inputs of a PSBT. */
577584
size_t CountPSBTUnsignedInputs(const PartiallySignedTransaction& psbt);

src/rpc/rawtransaction.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1655,6 +1655,7 @@ static RPCHelpMan utxoupdatepsbt()
16551655
}
16561656

16571657
// Fill the inputs
1658+
const PrecomputedTransactionData txdata = PrecomputePSBTData(psbtx);
16581659
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
16591660
PSBTInput& input = psbtx.inputs.at(i);
16601661

@@ -1671,7 +1672,7 @@ static RPCHelpMan utxoupdatepsbt()
16711672
// Update script/keypath information using descriptor data.
16721673
// Note that SignPSBTInput does a lot more than just constructing ECDSA signatures
16731674
// we don't actually care about those here, in fact.
1674-
SignPSBTInput(public_provider, psbtx, i, /* sighash_type */ 1);
1675+
SignPSBTInput(public_provider, psbtx, i, &txdata, /* sighash_type */ 1);
16751676
}
16761677

16771678
// Update script/keypath information using descriptor data.

src/wallet/external_signer_scriptpubkeyman.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,10 @@ bool ExternalSignerScriptPubKeyMan::DisplayAddress(const CScript scriptPubKey, c
6262
}
6363

6464
// If sign is true, transaction must previously have been filled
65-
TransactionError ExternalSignerScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbt, int sighash_type, bool sign, bool bip32derivs, int* n_signed) const
65+
TransactionError ExternalSignerScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata, int sighash_type, bool sign, bool bip32derivs, int* n_signed) const
6666
{
6767
if (!sign) {
68-
return DescriptorScriptPubKeyMan::FillPSBT(psbt, sighash_type, false, bip32derivs, n_signed);
68+
return DescriptorScriptPubKeyMan::FillPSBT(psbt, txdata, sighash_type, false, bip32derivs, n_signed);
6969
}
7070

7171
// Already complete if every input is now signed

src/wallet/external_signer_scriptpubkeyman.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ class ExternalSignerScriptPubKeyMan : public DescriptorScriptPubKeyMan
2929

3030
bool DisplayAddress(const CScript scriptPubKey, const ExternalSigner &signer) const;
3131

32-
TransactionError FillPSBT(PartiallySignedTransaction& psbt, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr) const override;
32+
TransactionError FillPSBT(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr) const override;
3333
};
3434
#endif
3535

src/wallet/scriptpubkeyman.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -597,7 +597,7 @@ SigningResult LegacyScriptPubKeyMan::SignMessage(const std::string& message, con
597597
return SigningResult::SIGNING_FAILED;
598598
}
599599

600-
TransactionError LegacyScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbtx, int sighash_type, bool sign, bool bip32derivs, int* n_signed) const
600+
TransactionError LegacyScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbtx, const PrecomputedTransactionData& txdata, int sighash_type, bool sign, bool bip32derivs, int* n_signed) const
601601
{
602602
if (n_signed) {
603603
*n_signed = 0;
@@ -626,7 +626,7 @@ TransactionError LegacyScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psb
626626
}
627627
SignatureData sigdata;
628628
input.FillSignatureData(sigdata);
629-
SignPSBTInput(HidingSigningProvider(this, !sign, !bip32derivs), psbtx, i, sighash_type);
629+
SignPSBTInput(HidingSigningProvider(this, !sign, !bip32derivs), psbtx, i, &txdata, sighash_type);
630630

631631
bool signed_one = PSBTInputSigned(input);
632632
if (n_signed && (signed_one || !sign)) {
@@ -2083,7 +2083,7 @@ SigningResult DescriptorScriptPubKeyMan::SignMessage(const std::string& message,
20832083
return SigningResult::OK;
20842084
}
20852085

2086-
TransactionError DescriptorScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbtx, int sighash_type, bool sign, bool bip32derivs, int* n_signed) const
2086+
TransactionError DescriptorScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbtx, const PrecomputedTransactionData& txdata, int sighash_type, bool sign, bool bip32derivs, int* n_signed) const
20872087
{
20882088
if (n_signed) {
20892089
*n_signed = 0;
@@ -2133,7 +2133,7 @@ TransactionError DescriptorScriptPubKeyMan::FillPSBT(PartiallySignedTransaction&
21332133
}
21342134
}
21352135

2136-
SignPSBTInput(HidingSigningProvider(keys.get(), !sign, !bip32derivs), psbtx, i, sighash_type);
2136+
SignPSBTInput(HidingSigningProvider(keys.get(), !sign, !bip32derivs), psbtx, i, &txdata, sighash_type);
21372137

21382138
bool signed_one = PSBTInputSigned(input);
21392139
if (n_signed && (signed_one || !sign)) {

src/wallet/scriptpubkeyman.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ class ScriptPubKeyMan
235235
/** Sign a message with the given script */
236236
virtual SigningResult SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const { return SigningResult::SIGNING_FAILED; };
237237
/** Adds script and derivation path information to a PSBT, and optionally signs it. */
238-
virtual TransactionError FillPSBT(PartiallySignedTransaction& psbt, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr) const { return TransactionError::INVALID_PSBT; }
238+
virtual TransactionError FillPSBT(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr) const { return TransactionError::INVALID_PSBT; }
239239

240240
virtual uint256 GetID() const { return uint256(); }
241241

@@ -394,7 +394,7 @@ class LegacyScriptPubKeyMan : public ScriptPubKeyMan, public FillableSigningProv
394394

395395
bool SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, std::string>& input_errors) const override;
396396
SigningResult SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const override;
397-
TransactionError FillPSBT(PartiallySignedTransaction& psbt, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr) const override;
397+
TransactionError FillPSBT(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr) const override;
398398

399399
uint256 GetID() const override;
400400

@@ -605,7 +605,7 @@ class DescriptorScriptPubKeyMan : public ScriptPubKeyMan
605605

606606
bool SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, std::string>& input_errors) const override;
607607
SigningResult SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const override;
608-
TransactionError FillPSBT(PartiallySignedTransaction& psbt, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr) const override;
608+
TransactionError FillPSBT(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr) const override;
609609

610610
uint256 GetID() const override;
611611

src/wallet/test/psbt_wallet_tests.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ BOOST_AUTO_TEST_CASE(psbt_updater_test)
7171

7272
// Try to sign the mutated input
7373
SignatureData sigdata;
74-
BOOST_CHECK(spk_man->FillPSBT(psbtx, SIGHASH_ALL, true, true) != TransactionError::OK);
74+
BOOST_CHECK(spk_man->FillPSBT(psbtx, PrecomputePSBTData(psbtx), SIGHASH_ALL, true, true) != TransactionError::OK);
7575
}
7676

7777
BOOST_AUTO_TEST_CASE(parse_hd_keypath)

src/wallet/wallet.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1830,6 +1830,7 @@ TransactionError CWallet::FillPSBT(PartiallySignedTransaction& psbtx, bool& comp
18301830
if (n_signed) {
18311831
*n_signed = 0;
18321832
}
1833+
const PrecomputedTransactionData txdata = PrecomputePSBTData(psbtx);
18331834
LOCK(cs_wallet);
18341835
// Get all of the previous transactions
18351836
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
@@ -1856,7 +1857,7 @@ TransactionError CWallet::FillPSBT(PartiallySignedTransaction& psbtx, bool& comp
18561857
// Fill in information from ScriptPubKeyMans
18571858
for (ScriptPubKeyMan* spk_man : GetAllScriptPubKeyMans()) {
18581859
int n_signed_this_spkm = 0;
1859-
TransactionError res = spk_man->FillPSBT(psbtx, sighash_type, sign, bip32derivs, &n_signed_this_spkm);
1860+
TransactionError res = spk_man->FillPSBT(psbtx, txdata, sighash_type, sign, bip32derivs, &n_signed_this_spkm);
18601861
if (res != TransactionError::OK) {
18611862
return res;
18621863
}

0 commit comments

Comments
 (0)