Skip to content

Commit b7c7c7b

Browse files
committed
partial bitcoin#21365: Basic Taproot signing support for descriptor wallets
includes: - e841fb5 - ce93531 - 5cb6502 - fd3f689 - 49487bc
1 parent 4a08920 commit b7c7c7b

File tree

12 files changed

+95
-29
lines changed

12 files changed

+95
-29
lines changed

src/node/psbt.cpp

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

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

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

6163
// Figure out what is missing
6264
SignatureData outdata;
63-
bool complete = SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, 1, &outdata);
65+
bool complete = SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, &txdata, 1, &outdata);
6466

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

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

src/psbt.cpp

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,15 @@ bool PartiallySignedTransaction::AddOutput(const CTxOut& txout, const PSBTOutput
6363

6464
bool PartiallySignedTransaction::GetInputUTXO(CTxOut& utxo, int input_index) const
6565
{
66-
PSBTInput input = inputs[input_index];
66+
const PSBTInput& input = inputs[input_index];
6767
uint32_t prevout_index = tx->vin[input_index].prevout.n;
6868
if (input.non_witness_utxo) {
6969
if (prevout_index >= input.non_witness_utxo->vout.size()) {
7070
return false;
7171
}
72+
if (input.non_witness_utxo->GetHash() != tx->vin[input_index].prevout.hash) {
73+
return false;
74+
}
7275
utxo = input.non_witness_utxo->vout[prevout_index];
7376
} else {
7477
return false;
@@ -202,7 +205,24 @@ bool PSBTInputSigned(const PSBTInput& input)
202205
return !input.final_script_sig.empty();
203206
}
204207

205-
bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, int sighash, SignatureData* out_sigdata, bool use_dummy)
208+
PrecomputedTransactionData PrecomputePSBTData(const PartiallySignedTransaction& psbt)
209+
{
210+
const CMutableTransaction& tx = *psbt.tx;
211+
bool have_all_spent_outputs = true;
212+
std::vector<CTxOut> utxos(tx.vin.size());
213+
for (size_t idx = 0; idx < tx.vin.size(); ++idx) {
214+
if (!psbt.GetInputUTXO(utxos[idx], idx)) have_all_spent_outputs = false;
215+
}
216+
PrecomputedTransactionData txdata;
217+
if (have_all_spent_outputs) {
218+
txdata.Init(tx, std::move(utxos), true);
219+
} else {
220+
txdata.Init(tx, {}, true);
221+
}
222+
return txdata;
223+
}
224+
225+
bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, const PrecomputedTransactionData* txdata, int sighash, SignatureData* out_sigdata)
206226
{
207227
PSBTInput& input = psbt.inputs.at(index);
208228
const CMutableTransaction& tx = *psbt.tx;
@@ -233,10 +253,10 @@ bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction&
233253
}
234254

235255
bool sig_complete;
236-
if (use_dummy) {
256+
if (txdata == nullptr) {
237257
sig_complete = ProduceSignature(provider, DUMMY_SIGNATURE_CREATOR, utxo.scriptPubKey, sigdata);
238258
} else {
239-
MutableTransactionSignatureCreator creator(&tx, index, utxo.nValue, sighash);
259+
MutableTransactionSignatureCreator creator(&tx, index, utxo.nValue, txdata, sighash);
240260
sig_complete = ProduceSignature(provider, creator, utxo.scriptPubKey, sigdata);
241261
}
242262
input.FromSignatureData(sigdata);
@@ -258,8 +278,9 @@ bool FinalizePSBT(PartiallySignedTransaction& psbtx)
258278
// PartiallySignedTransaction did not understand them), this will combine them into a final
259279
// script.
260280
bool complete = true;
281+
const PrecomputedTransactionData txdata = PrecomputePSBTData(psbtx);
261282
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
262-
complete &= SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, SIGHASH_ALL);
283+
complete &= SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, &txdata, SIGHASH_ALL);
263284
}
264285

265286
return complete;

src/psbt.h

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

494494
std::string PSBTRoleName(PSBTRole role);
495495

496+
/** Compute a PrecomputedTransactionData object from a psbt. */
497+
PrecomputedTransactionData PrecomputePSBTData(const PartiallySignedTransaction& psbt);
498+
496499
/** Checks whether a PSBTInput is already signed. */
497500
bool PSBTInputSigned(const PSBTInput& input);
498501

499-
/** Signs a PSBTInput, verifying that all provided data matches what is being signed. */
500-
bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, int sighash = SIGHASH_ALL, SignatureData* out_sigdata = nullptr, bool use_dummy = false);
502+
/** Signs a PSBTInput, verifying that all provided data matches what is being signed.
503+
*
504+
* txdata should be the output of PrecomputePSBTData (which can be shared across
505+
* multiple SignPSBTInput calls). If it is nullptr, a dummy signature will be created.
506+
**/
507+
bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, const PrecomputedTransactionData* txdata, int sighash = SIGHASH_ALL, SignatureData* out_sigdata = nullptr);
501508

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

src/rpc/rawtransaction.cpp

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

18691869
// Fill the inputs
1870+
const PrecomputedTransactionData txdata = PrecomputePSBTData(psbtx);
18701871
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
18711872
PSBTInput& input = psbtx.inputs.at(i);
18721873

@@ -1877,7 +1878,7 @@ static RPCHelpMan utxoupdatepsbt()
18771878
// Update script/keypath information using descriptor data.
18781879
// Note that SignPSBTInput does a lot more than just constructing ECDSA signatures
18791880
// we don't actually care about those here, in fact.
1880-
SignPSBTInput(public_provider, psbtx, i, /*sighash=*/ SIGHASH_ALL);
1881+
SignPSBTInput(public_provider, psbtx, i, &txdata, /*sighash=*/SIGHASH_ALL);
18811882
}
18821883

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

src/script/interpreter.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1500,7 +1500,7 @@ uint256 GetOutputsSHA256(const T& txTo)
15001500
} // namespace
15011501

15021502
template <class T>
1503-
void PrecomputedTransactionData::Init(const T& txTo, std::vector<CTxOut>&& spent_outputs)
1503+
void PrecomputedTransactionData::Init(const T& txTo, std::vector<CTxOut>&& spent_outputs, bool force)
15041504
{
15051505
assert(!m_ready);
15061506

@@ -1516,8 +1516,8 @@ PrecomputedTransactionData::PrecomputedTransactionData(const T& txTo)
15161516
}
15171517

15181518
// explicit instantiation
1519-
template void PrecomputedTransactionData::Init(const CTransaction& txTo, std::vector<CTxOut>&& spent_outputs);
1520-
template void PrecomputedTransactionData::Init(const CMutableTransaction& txTo, std::vector<CTxOut>&& spent_outputs);
1519+
template void PrecomputedTransactionData::Init(const CTransaction& txTo, std::vector<CTxOut>&& spent_outputs, bool force);
1520+
template void PrecomputedTransactionData::Init(const CMutableTransaction& txTo, std::vector<CTxOut>&& spent_outputs, bool force);
15211521
template PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo);
15221522
template PrecomputedTransactionData::PrecomputedTransactionData(const CMutableTransaction& txTo);
15231523

src/script/interpreter.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ struct PrecomputedTransactionData
121121
PrecomputedTransactionData() = default;
122122

123123
template <class T>
124-
void Init(const T& tx, std::vector<CTxOut>&& spent_outputs);
124+
void Init(const T& tx, std::vector<CTxOut>&& spent_outputs, bool force = false);
125125

126126
template <class T>
127127
explicit PrecomputedTransactionData(const T& tx);

src/script/sign.cpp

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,27 @@
1616

1717
typedef std::vector<unsigned char> valtype;
1818

19-
MutableTransactionSignatureCreator::MutableTransactionSignatureCreator(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn) : txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), amount(amountIn), checker(txTo, nIn, amountIn, MissingDataBehavior::FAIL) {}
19+
MutableTransactionSignatureCreator::MutableTransactionSignatureCreator(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn)
20+
: txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), amount(amountIn), checker(txTo, nIn, amountIn, MissingDataBehavior::FAIL),
21+
m_txdata(nullptr)
22+
{
23+
}
24+
25+
MutableTransactionSignatureCreator::MutableTransactionSignatureCreator(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, const PrecomputedTransactionData* txdata, int nHashTypeIn)
26+
: txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), amount(amountIn),
27+
checker(txdata ? MutableTransactionSignatureChecker(txTo, nIn, amount, *txdata, MissingDataBehavior::FAIL) :
28+
MutableTransactionSignatureChecker(txTo, nIn, amount, MissingDataBehavior::FAIL)),
29+
m_txdata(txdata)
30+
{
31+
}
2032

2133
bool MutableTransactionSignatureCreator::CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& address, const CScript& scriptCode, SigVersion sigversion) const
2234
{
2335
CKey key;
2436
if (!provider.GetKey(address, key))
2537
return false;
2638

27-
uint256 hash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, sigversion);
39+
uint256 hash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, sigversion, m_txdata);
2840
if (!key.Sign(hash, vchSig))
2941
return false;
3042
vchSig.push_back((unsigned char)nHashType);
@@ -394,6 +406,26 @@ bool SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore,
394406
// Use CTransaction for the constant parts of the
395407
// transaction to avoid rehashing.
396408
const CTransaction txConst(mtx);
409+
410+
PrecomputedTransactionData txdata;
411+
std::vector<CTxOut> spent_outputs;
412+
spent_outputs.resize(mtx.vin.size());
413+
bool have_all_spent_outputs = true;
414+
for (unsigned int i = 0; i < mtx.vin.size(); i++) {
415+
CTxIn& txin = mtx.vin[i];
416+
auto coin = coins.find(txin.prevout);
417+
if (coin == coins.end() || coin->second.IsSpent()) {
418+
have_all_spent_outputs = false;
419+
} else {
420+
spent_outputs[i] = CTxOut(coin->second.out.nValue, coin->second.out.scriptPubKey);
421+
}
422+
}
423+
if (have_all_spent_outputs) {
424+
txdata.Init(txConst, std::move(spent_outputs), true);
425+
} else {
426+
txdata.Init(txConst, {}, true);
427+
}
428+
397429
// Sign what we can:
398430
for (unsigned int i = 0; i < mtx.vin.size(); i++) {
399431
CTxIn& txin = mtx.vin[i];
@@ -408,13 +440,13 @@ bool SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore,
408440
SignatureData sigdata = DataFromTransaction(mtx, i, coin->second.out);
409441
// Only sign SIGHASH_SINGLE if there's a corresponding output:
410442
if (!fHashSingle || (i < mtx.vout.size())) {
411-
ProduceSignature(*keystore, MutableTransactionSignatureCreator(&mtx, i, amount, nHashType), prevPubKey, sigdata);
443+
ProduceSignature(*keystore, MutableTransactionSignatureCreator(&mtx, i, amount, &txdata, nHashType), prevPubKey, sigdata);
412444
}
413445

414446
UpdateInput(txin, sigdata);
415447

416448
ScriptError serror = SCRIPT_ERR_OK;
417-
if (!VerifyScript(txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount, MissingDataBehavior::FAIL), &serror)) {
449+
if (!VerifyScript(txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount, txdata, MissingDataBehavior::FAIL), &serror)) {
418450
if (serror == SCRIPT_ERR_INVALID_STACK_OPERATION) {
419451
// Unable to sign input and verification failed (possible attempt to partially sign).
420452
input_errors[i] = Untranslated("Unable to sign input, invalid stack size (possibly missing key)");

src/script/sign.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,12 @@ class MutableTransactionSignatureCreator : public BaseSignatureCreator {
4040
int nHashType;
4141
CAmount amount;
4242
const MutableTransactionSignatureChecker checker;
43+
const PrecomputedTransactionData* m_txdata;
4344

4445
public:
4546
MutableTransactionSignatureCreator(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn = SIGHASH_ALL);
46-
const BaseSignatureChecker& Checker() const override{ return checker; }
47+
MutableTransactionSignatureCreator(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, const PrecomputedTransactionData* txdata, int nHashTypeIn = SIGHASH_ALL);
48+
const BaseSignatureChecker& Checker() const override { return checker; }
4749
bool CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const override;
4850
};
4951

src/wallet/scriptpubkeyman.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -733,7 +733,7 @@ bool LegacyScriptPubKeyMan::SignSpecialTxPayload(const uint256& hash, const CKey
733733
return CHashSigner::SignHash(hash, key, vchSig);
734734
}
735735

736-
TransactionError LegacyScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbtx, int sighash_type, bool sign, bool bip32derivs, int* n_signed) const
736+
TransactionError LegacyScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbtx, const PrecomputedTransactionData& txdata, int sighash_type, bool sign, bool bip32derivs, int* n_signed) const
737737
{
738738
if (n_signed) {
739739
*n_signed = 0;
@@ -762,7 +762,7 @@ TransactionError LegacyScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psb
762762
}
763763
SignatureData sigdata;
764764
input.FillSignatureData(sigdata);
765-
SignPSBTInput(HidingSigningProvider(this, !sign, !bip32derivs), psbtx, i, sighash_type);
765+
SignPSBTInput(HidingSigningProvider(this, !sign, !bip32derivs), psbtx, i, &txdata, sighash_type);
766766

767767
bool signed_one = PSBTInputSigned(input);
768768
if (n_signed && (signed_one || !sign)) {
@@ -2229,7 +2229,7 @@ bool DescriptorScriptPubKeyMan::SignSpecialTxPayload(const uint256& hash, const
22292229
return CHashSigner::SignHash(hash, key, vchSig);
22302230
}
22312231

2232-
TransactionError DescriptorScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbtx, int sighash_type, bool sign, bool bip32derivs, int* n_signed) const
2232+
TransactionError DescriptorScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbtx, const PrecomputedTransactionData& txdata, int sighash_type, bool sign, bool bip32derivs, int* n_signed) const
22332233
{
22342234
if (n_signed) {
22352235
*n_signed = 0;
@@ -2277,7 +2277,8 @@ TransactionError DescriptorScriptPubKeyMan::FillPSBT(PartiallySignedTransaction&
22772277
}
22782278
}
22792279

2280-
SignPSBTInput(HidingSigningProvider(keys.get(), !sign, !bip32derivs), psbtx, i, sighash_type);
2280+
SignPSBTInput(HidingSigningProvider(keys.get(), !sign, !bip32derivs), psbtx, i, &txdata, sighash_type);
2281+
22812282
bool signed_one = PSBTInputSigned(input);
22822283
if (n_signed && (signed_one || !sign)) {
22832284
// If sign is false, we assume that we _could_ sign if we get here. This

src/wallet/scriptpubkeyman.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ class ScriptPubKeyMan
214214
virtual SigningResult SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const { return SigningResult::SIGNING_FAILED; };
215215
virtual bool SignSpecialTxPayload(const uint256& hash, const CKeyID& keyid, std::vector<unsigned char>& vchSig) const { return false; }
216216
/** Adds script and derivation path information to a PSBT, and optionally signs it. */
217-
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; }
217+
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; }
218218

219219
virtual uint256 GetID() const { return uint256(); }
220220

@@ -358,7 +358,7 @@ class LegacyScriptPubKeyMan : public ScriptPubKeyMan, public FillableSigningProv
358358
bool SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, bilingual_str>& input_errors) const override;
359359
SigningResult SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const override;
360360
bool SignSpecialTxPayload(const uint256& hash, const CKeyID& keyid, std::vector<unsigned char>& vchSig) const override;
361-
TransactionError FillPSBT(PartiallySignedTransaction& psbt, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr) const override;
361+
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;
362362

363363
uint256 GetID() const override;
364364

@@ -582,7 +582,7 @@ class DescriptorScriptPubKeyMan : public ScriptPubKeyMan
582582
bool SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, bilingual_str>& input_errors) const override;
583583
SigningResult SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const override;
584584
bool SignSpecialTxPayload(const uint256& hash, const CKeyID& keyid, std::vector<unsigned char>& vchSig) const override;
585-
TransactionError FillPSBT(PartiallySignedTransaction& psbt, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr) const override;
585+
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;
586586

587587
uint256 GetID() const override;
588588

0 commit comments

Comments
 (0)