Skip to content

Commit 4c7d767

Browse files
committed
psbt: Check sighash types in SignPSBTInput and take sighash as optional
1 parent a118256 commit 4c7d767

File tree

10 files changed

+42
-25
lines changed

10 files changed

+42
-25
lines changed

src/node/psbt.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx)
6464

6565
// Figure out what is missing
6666
SignatureData outdata;
67-
bool complete = SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, &txdata, 1, &outdata) == PSBTError::OK;
67+
bool complete = SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, &txdata, std::nullopt, &outdata) == PSBTError::OK;
6868

6969
// Things are missing
7070
if (!complete) {
@@ -124,7 +124,7 @@ PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx)
124124
PSBTInput& input = psbtx.inputs[i];
125125
Coin newcoin;
126126

127-
if (SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, nullptr, 1) != PSBTError::OK || !psbtx.GetInputUTXO(newcoin.out, i)) {
127+
if (SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, nullptr, std::nullopt) != PSBTError::OK || !psbtx.GetInputUTXO(newcoin.out, i)) {
128128
success = false;
129129
break;
130130
} else {

src/psbt.cpp

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,7 @@ PrecomputedTransactionData PrecomputePSBTData(const PartiallySignedTransaction&
375375
return txdata;
376376
}
377377

378-
PSBTError SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, const PrecomputedTransactionData* txdata, int sighash, SignatureData* out_sigdata, bool finalize)
378+
PSBTError SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, const PrecomputedTransactionData* txdata, std::optional<int> sighash, SignatureData* out_sigdata, bool finalize)
379379
{
380380
PSBTInput& input = psbt.inputs.at(index);
381381
const CMutableTransaction& tx = *psbt.tx;
@@ -413,12 +413,24 @@ PSBTError SignPSBTInput(const SigningProvider& provider, PartiallySignedTransact
413413
return PSBTError::MISSING_INPUTS;
414414
}
415415

416+
// Get the sighash type
417+
// If both the field and the parameter are provided, they must match
418+
// If only the parameter is provided, use it and add it to the PSBT if it is other than SIGHASH_DEFAULT
419+
// for all input types, and not SIGHASH_ALL for non-taproot input types.
420+
// If neither are provided, use SIGHASH_DEFAULT if it is taproot, and SIGHASH_ALL for everything else.
421+
if (!sighash) sighash = utxo.scriptPubKey.IsPayToTaproot() ? SIGHASH_DEFAULT : SIGHASH_ALL;
422+
Assert(sighash.has_value());
423+
// For user safety, the desired sighash must be provided if the PSBT wants something other than the default set in the previous line.
424+
if (input.sighash_type && input.sighash_type != sighash) {
425+
return PSBTError::SIGHASH_MISMATCH;
426+
}
427+
416428
sigdata.witness = false;
417429
bool sig_complete;
418430
if (txdata == nullptr) {
419431
sig_complete = ProduceSignature(provider, DUMMY_SIGNATURE_CREATOR, utxo.scriptPubKey, sigdata);
420432
} else {
421-
MutableTransactionSignatureCreator creator(tx, index, utxo.nValue, txdata, sighash);
433+
MutableTransactionSignatureCreator creator(tx, index, utxo.nValue, txdata, *sighash);
422434
sig_complete = ProduceSignature(provider, creator, utxo.scriptPubKey, sigdata);
423435
}
424436
// Verify that a witness signature was produced in case one was required.
@@ -448,10 +460,11 @@ PSBTError SignPSBTInput(const SigningProvider& provider, PartiallySignedTransact
448460
return sig_complete ? PSBTError::OK : PSBTError::INCOMPLETE;
449461
}
450462

451-
void RemoveUnnecessaryTransactions(PartiallySignedTransaction& psbtx, const int& sighash_type)
463+
void RemoveUnnecessaryTransactions(PartiallySignedTransaction& psbtx, std::optional<int> sighash_type)
452464
{
465+
if (!sighash_type) sighash_type = SIGHASH_DEFAULT;
453466
// Only drop non_witness_utxos if sighash_type != SIGHASH_ANYONECANPAY
454-
if ((sighash_type & 0x80) != SIGHASH_ANYONECANPAY) {
467+
if ((*sighash_type & 0x80) != SIGHASH_ANYONECANPAY) {
455468
// Figure out if any non_witness_utxos should be dropped
456469
std::vector<unsigned int> to_drop;
457470
for (unsigned int i = 0; i < psbtx.inputs.size(); ++i) {
@@ -489,7 +502,7 @@ bool FinalizePSBT(PartiallySignedTransaction& psbtx)
489502
bool complete = true;
490503
const PrecomputedTransactionData txdata = PrecomputePSBTData(psbtx);
491504
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
492-
complete &= (SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, &txdata, SIGHASH_ALL, nullptr, true) == PSBTError::OK);
505+
complete &= (SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, &txdata, std::nullopt, nullptr, true) == PSBTError::OK);
493506
}
494507

495508
return complete;

src/psbt.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1404,10 +1404,10 @@ bool PSBTInputSignedAndVerified(const PartiallySignedTransaction psbt, unsigned
14041404
* txdata should be the output of PrecomputePSBTData (which can be shared across
14051405
* multiple SignPSBTInput calls). If it is nullptr, a dummy signature will be created.
14061406
**/
1407-
[[nodiscard]] PSBTError SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, const PrecomputedTransactionData* txdata, int sighash = SIGHASH_ALL, SignatureData* out_sigdata = nullptr, bool finalize = true);
1407+
[[nodiscard]] PSBTError SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, const PrecomputedTransactionData* txdata, std::optional<int> sighash = std::nullopt, SignatureData* out_sigdata = nullptr, bool finalize = true);
14081408

14091409
/** Reduces the size of the PSBT by dropping unnecessary `non_witness_utxos` (i.e. complete previous transactions) from a psbt when all inputs are segwit v1. */
1410-
void RemoveUnnecessaryTransactions(PartiallySignedTransaction& psbtx, const int& sighash_type);
1410+
void RemoveUnnecessaryTransactions(PartiallySignedTransaction& psbtx, std::optional<int> sighash_type);
14111411

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

src/rpc/rawtransaction.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ static std::vector<RPCArg> CreateTxDoc()
163163

164164
// Update PSBT with information from the mempool, the UTXO set, the txindex, and the provided descriptors.
165165
// Optionally, sign the inputs that we can using information from the descriptors.
166-
PartiallySignedTransaction ProcessPSBT(const std::string& psbt_string, const std::any& context, const HidingSigningProvider& provider, int sighash_type, bool finalize)
166+
PartiallySignedTransaction ProcessPSBT(const std::string& psbt_string, const std::any& context, const HidingSigningProvider& provider, std::optional<int> sighash_type, bool finalize)
167167
{
168168
// Unserialize the transactions
169169
PartiallySignedTransaction psbtx;
@@ -244,7 +244,7 @@ PartiallySignedTransaction ProcessPSBT(const std::string& psbt_string, const std
244244
UpdatePSBTOutput(provider, psbtx, i);
245245
}
246246

247-
RemoveUnnecessaryTransactions(psbtx, /*sighash_type=*/1);
247+
RemoveUnnecessaryTransactions(psbtx, /*sighash_type=*/std::nullopt);
248248

249249
return psbtx;
250250
}
@@ -1796,7 +1796,7 @@ static RPCHelpMan utxoupdatepsbt()
17961796
request.params[0].get_str(),
17971797
request.context,
17981798
HidingSigningProvider(&provider, /*hide_secret=*/true, /*hide_origin=*/false),
1799-
/*sighash_type=*/SIGHASH_ALL,
1799+
/*sighash_type=*/std::nullopt,
18001800
/*finalize=*/false);
18011801

18021802
DataStream ssTx{};
@@ -2063,7 +2063,7 @@ RPCHelpMan descriptorprocesspsbt()
20632063
EvalDescriptorStringOrObject(descs[i], provider, /*expand_priv=*/true);
20642064
}
20652065

2066-
int sighash_type = ParseSighashString(request.params[2]);
2066+
std::optional<int> sighash_type = ParseSighashString(request.params[2]);
20672067
bool bip32derivs = request.params[3].isNull() ? true : request.params[3].get_bool();
20682068
bool finalize = request.params[4].isNull() ? true : request.params[4].get_bool();
20692069

src/rpc/rawtransaction_util.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -304,12 +304,15 @@ void ParsePrevouts(const UniValue& prevTxsUnival, FlatSigningProvider* keystore,
304304

305305
void SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore, const std::map<COutPoint, Coin>& coins, const UniValue& hashType, UniValue& result)
306306
{
307-
int nHashType = ParseSighashString(hashType);
307+
std::optional<int> nHashType = ParseSighashString(hashType);
308+
if (!nHashType) {
309+
nHashType = SIGHASH_DEFAULT;
310+
}
308311

309312
// Script verification errors
310313
std::map<int, bilingual_str> input_errors;
311314

312-
bool complete = SignTransaction(mtx, keystore, coins, nHashType, input_errors);
315+
bool complete = SignTransaction(mtx, keystore, coins, *nHashType, input_errors);
313316
SignTransactionResultToJSON(mtx, complete, coins, input_errors, result);
314317
}
315318

src/rpc/util.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -378,10 +378,10 @@ UniValue DescribeAddress(const CTxDestination& dest)
378378
*
379379
* @pre The sighash argument should be string or null.
380380
*/
381-
int ParseSighashString(const UniValue& sighash)
381+
std::optional<int> ParseSighashString(const UniValue& sighash)
382382
{
383383
if (sighash.isNull()) {
384-
return SIGHASH_DEFAULT;
384+
return std::nullopt;
385385
}
386386
const auto result{SighashFromStr(sighash.get_str())};
387387
if (!result) {

src/rpc/util.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ CTxDestination AddAndGetMultisigDestination(const int required, const std::vecto
138138
UniValue DescribeAddress(const CTxDestination& dest);
139139

140140
/** Parse a sighash string representation and raise an RPC error if it is invalid. */
141-
int ParseSighashString(const UniValue& sighash);
141+
std::optional<int> ParseSighashString(const UniValue& sighash);
142142

143143
//! Parse a confirm target option and raise an RPC error if it is invalid.
144144
unsigned int ParseConfirmTarget(const UniValue& value, unsigned int max_target);

src/wallet/rpc/spend.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -956,12 +956,15 @@ RPCHelpMan signrawtransactionwithwallet()
956956
// Parse the prevtxs array
957957
ParsePrevouts(request.params[1], nullptr, coins);
958958

959-
int nHashType = ParseSighashString(request.params[2]);
959+
std::optional<int> nHashType = ParseSighashString(request.params[2]);
960+
if (!nHashType) {
961+
nHashType = SIGHASH_DEFAULT;
962+
}
960963

961964
// Script verification errors
962965
std::map<int, bilingual_str> input_errors;
963966

964-
bool complete = pwallet->SignTransaction(mtx, coins, nHashType, input_errors);
967+
bool complete = pwallet->SignTransaction(mtx, coins, *nHashType, input_errors);
965968
UniValue result(UniValue::VOBJ);
966969
SignTransactionResultToJSON(mtx, complete, coins, input_errors, result);
967970
return result;
@@ -1629,7 +1632,7 @@ RPCHelpMan walletprocesspsbt()
16291632
}
16301633

16311634
// Get the sighash type
1632-
int nHashType = ParseSighashString(request.params[2]);
1635+
std::optional<int> nHashType = ParseSighashString(request.params[2]);
16331636

16341637
// Fill transaction with our data and also sign
16351638
bool sign = request.params[1].isNull() ? true : request.params[1].get_bool();

src/wallet/scriptpubkeyman.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1316,7 +1316,6 @@ std::optional<PSBTError> DescriptorScriptPubKeyMan::FillPSBT(PartiallySignedTran
13161316
if (n_signed) {
13171317
*n_signed = 0;
13181318
}
1319-
if (!sighash_type) sighash_type = SIGHASH_DEFAULT;
13201319
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
13211320
const CTxIn& txin = psbtx.tx->vin[i];
13221321
PSBTInput& input = psbtx.inputs.at(i);
@@ -1387,7 +1386,7 @@ std::optional<PSBTError> DescriptorScriptPubKeyMan::FillPSBT(PartiallySignedTran
13871386
}
13881387
}
13891388

1390-
PSBTError res = SignPSBTInput(HidingSigningProvider(keys.get(), /*hide_secret=*/!sign, /*hide_origin=*/!bip32derivs), psbtx, i, &txdata, *sighash_type, nullptr, finalize);
1389+
PSBTError res = SignPSBTInput(HidingSigningProvider(keys.get(), /*hide_secret=*/!sign, /*hide_origin=*/!bip32derivs), psbtx, i, &txdata, sighash_type, nullptr, finalize);
13911390
if (res != PSBTError::OK && res != PSBTError::INCOMPLETE) {
13921391
return res;
13931392
}

src/wallet/wallet.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2106,7 +2106,6 @@ std::optional<PSBTError> CWallet::FillPSBT(PartiallySignedTransaction& psbtx, bo
21062106
if (n_signed) {
21072107
*n_signed = 0;
21082108
}
2109-
if (!sighash_type) sighash_type = SIGHASH_DEFAULT;
21102109
LOCK(cs_wallet);
21112110
// Get all of the previous transactions
21122111
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
@@ -2145,7 +2144,7 @@ std::optional<PSBTError> CWallet::FillPSBT(PartiallySignedTransaction& psbtx, bo
21452144
}
21462145
}
21472146

2148-
RemoveUnnecessaryTransactions(psbtx, *sighash_type);
2147+
RemoveUnnecessaryTransactions(psbtx, sighash_type);
21492148

21502149
// Complete if every input is now signed
21512150
complete = true;

0 commit comments

Comments
 (0)