psbt: Check sighash types in SignPSBTInput and take sighash as optional

This commit is contained in:
Ava Chow committed 2025-05-14 14:00:43 -07:00
1 parent a118256948
commit 4c7d767e49
10 files changed
+42 -25

No files matched your search

+2 -2
View File
@@ -64,7 +64,7 @@ PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx)
// Figure out what is missing // Figure out what is missing
SignatureData outdata; SignatureData outdata;
bool complete = SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, &txdata, 1, &outdata) == PSBTError::OK; bool complete = SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, &txdata, std::nullopt, &outdata) == PSBTError::OK;
// Things are missing // Things are missing
if (!complete) { if (!complete) {
@@ -124,7 +124,7 @@ PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx)
PSBTInput& input = psbtx.inputs[i]; PSBTInput& input = psbtx.inputs[i];
Coin newcoin; Coin newcoin;
if (SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, nullptr, 1) != PSBTError::OK || !psbtx.GetInputUTXO(newcoin.out, i)) { if (SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, nullptr, std::nullopt) != PSBTError::OK || !psbtx.GetInputUTXO(newcoin.out, i)) {
success = false; success = false;
break; break;
} else { } else {
+18 -5
View File
@@ -375,7 +375,7 @@ PrecomputedTransactionData PrecomputePSBTData(const PartiallySignedTransaction&
return txdata; return txdata;
} }
PSBTError SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, const PrecomputedTransactionData* txdata, int sighash, SignatureData* out_sigdata, bool finalize) PSBTError SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, const PrecomputedTransactionData* txdata, std::optional<int> sighash, SignatureData* out_sigdata, bool finalize)
{ {
PSBTInput& input = psbt.inputs.at(index); PSBTInput& input = psbt.inputs.at(index);
const CMutableTransaction& tx = *psbt.tx; const CMutableTransaction& tx = *psbt.tx;
@@ -413,12 +413,24 @@ PSBTError SignPSBTInput(const SigningProvider& provider, PartiallySignedTransact
return PSBTError::MISSING_INPUTS; return PSBTError::MISSING_INPUTS;
} }
// Get the sighash type
// If both the field and the parameter are provided, they must match
// If only the parameter is provided, use it and add it to the PSBT if it is other than SIGHASH_DEFAULT
// for all input types, and not SIGHASH_ALL for non-taproot input types.
// If neither are provided, use SIGHASH_DEFAULT if it is taproot, and SIGHASH_ALL for everything else.
if (!sighash) sighash = utxo.scriptPubKey.IsPayToTaproot() ? SIGHASH_DEFAULT : SIGHASH_ALL;
Assert(sighash.has_value());
// For user safety, the desired sighash must be provided if the PSBT wants something other than the default set in the previous line.
if (input.sighash_type && input.sighash_type != sighash) {
return PSBTError::SIGHASH_MISMATCH;
}
sigdata.witness = false; sigdata.witness = false;
bool sig_complete; bool sig_complete;
if (txdata == nullptr) { if (txdata == nullptr) {
sig_complete = ProduceSignature(provider, DUMMY_SIGNATURE_CREATOR, utxo.scriptPubKey, sigdata); sig_complete = ProduceSignature(provider, DUMMY_SIGNATURE_CREATOR, utxo.scriptPubKey, sigdata);
} else { } else {
MutableTransactionSignatureCreator creator(tx, index, utxo.nValue, txdata, sighash); MutableTransactionSignatureCreator creator(tx, index, utxo.nValue, txdata, *sighash);
sig_complete = ProduceSignature(provider, creator, utxo.scriptPubKey, sigdata); sig_complete = ProduceSignature(provider, creator, utxo.scriptPubKey, sigdata);
} }
// Verify that a witness signature was produced in case one was required. // Verify that a witness signature was produced in case one was required.
@@ -448,10 +460,11 @@ PSBTError SignPSBTInput(const SigningProvider& provider, PartiallySignedTransact
return sig_complete ? PSBTError::OK : PSBTError::INCOMPLETE; return sig_complete ? PSBTError::OK : PSBTError::INCOMPLETE;
} }
void RemoveUnnecessaryTransactions(PartiallySignedTransaction& psbtx, const int& sighash_type) void RemoveUnnecessaryTransactions(PartiallySignedTransaction& psbtx, std::optional<int> sighash_type)
{ {
if (!sighash_type) sighash_type = SIGHASH_DEFAULT;
// Only drop non_witness_utxos if sighash_type != SIGHASH_ANYONECANPAY // Only drop non_witness_utxos if sighash_type != SIGHASH_ANYONECANPAY
if ((sighash_type & 0x80) != SIGHASH_ANYONECANPAY) { if ((*sighash_type & 0x80) != SIGHASH_ANYONECANPAY) {
// Figure out if any non_witness_utxos should be dropped // Figure out if any non_witness_utxos should be dropped
std::vector<unsigned int> to_drop; std::vector<unsigned int> to_drop;
for (unsigned int i = 0; i < psbtx.inputs.size(); ++i) { for (unsigned int i = 0; i < psbtx.inputs.size(); ++i) {
@@ -489,7 +502,7 @@ bool FinalizePSBT(PartiallySignedTransaction& psbtx)
bool complete = true; bool complete = true;
const PrecomputedTransactionData txdata = PrecomputePSBTData(psbtx); const PrecomputedTransactionData txdata = PrecomputePSBTData(psbtx);
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) { for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
complete &= (SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, &txdata, SIGHASH_ALL, nullptr, true) == PSBTError::OK); complete &= (SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, &txdata, std::nullopt, nullptr, true) == PSBTError::OK);
} }
return complete; return complete;
+2 -2
View File
@@ -1404,10 +1404,10 @@ bool PSBTInputSignedAndVerified(const PartiallySignedTransaction psbt, unsigned
* txdata should be the output of PrecomputePSBTData (which can be shared across * txdata should be the output of PrecomputePSBTData (which can be shared across
* multiple SignPSBTInput calls). If it is nullptr, a dummy signature will be created. * multiple SignPSBTInput calls). If it is nullptr, a dummy signature will be created.
**/ **/
[[nodiscard]] PSBTError SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, const PrecomputedTransactionData* txdata, int sighash = SIGHASH_ALL, SignatureData* out_sigdata = nullptr, bool finalize = true); [[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);
/** 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. */ /** 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. */
void RemoveUnnecessaryTransactions(PartiallySignedTransaction& psbtx, const int& sighash_type); void RemoveUnnecessaryTransactions(PartiallySignedTransaction& psbtx, std::optional<int> sighash_type);
/** Counts the unsigned inputs of a PSBT. */ /** Counts the unsigned inputs of a PSBT. */
size_t CountPSBTUnsignedInputs(const PartiallySignedTransaction& psbt); size_t CountPSBTUnsignedInputs(const PartiallySignedTransaction& psbt);
+4 -4
View File
@@ -163,7 +163,7 @@ static std::vector<RPCArg> CreateTxDoc()
// Update PSBT with information from the mempool, the UTXO set, the txindex, and the provided descriptors. // Update PSBT with information from the mempool, the UTXO set, the txindex, and the provided descriptors.
// Optionally, sign the inputs that we can using information from the descriptors. // Optionally, sign the inputs that we can using information from the descriptors.
PartiallySignedTransaction ProcessPSBT(const std::string& psbt_string, const std::any& context, const HidingSigningProvider& provider, int sighash_type, bool finalize) PartiallySignedTransaction ProcessPSBT(const std::string& psbt_string, const std::any& context, const HidingSigningProvider& provider, std::optional<int> sighash_type, bool finalize)
{ {
// Unserialize the transactions // Unserialize the transactions
PartiallySignedTransaction psbtx; PartiallySignedTransaction psbtx;
@@ -244,7 +244,7 @@ PartiallySignedTransaction ProcessPSBT(const std::string& psbt_string, const std
UpdatePSBTOutput(provider, psbtx, i); UpdatePSBTOutput(provider, psbtx, i);
} }
RemoveUnnecessaryTransactions(psbtx, /*sighash_type=*/1); RemoveUnnecessaryTransactions(psbtx, /*sighash_type=*/std::nullopt);
return psbtx; return psbtx;
} }
@@ -1796,7 +1796,7 @@ static RPCHelpMan utxoupdatepsbt()
request.params[0].get_str(), request.params[0].get_str(),
request.context, request.context,
HidingSigningProvider(&provider, /*hide_secret=*/true, /*hide_origin=*/false), HidingSigningProvider(&provider, /*hide_secret=*/true, /*hide_origin=*/false),
/*sighash_type=*/SIGHASH_ALL, /*sighash_type=*/std::nullopt,
/*finalize=*/false); /*finalize=*/false);
DataStream ssTx{}; DataStream ssTx{};
@@ -2063,7 +2063,7 @@ RPCHelpMan descriptorprocesspsbt()
EvalDescriptorStringOrObject(descs[i], provider, /*expand_priv=*/true); EvalDescriptorStringOrObject(descs[i], provider, /*expand_priv=*/true);
} }
int sighash_type = ParseSighashString(request.params[2]); std::optional<int> sighash_type = ParseSighashString(request.params[2]);
bool bip32derivs = request.params[3].isNull() ? true : request.params[3].get_bool(); bool bip32derivs = request.params[3].isNull() ? true : request.params[3].get_bool();
bool finalize = request.params[4].isNull() ? true : request.params[4].get_bool(); bool finalize = request.params[4].isNull() ? true : request.params[4].get_bool();
+5 -2
View File
@@ -304,12 +304,15 @@ void ParsePrevouts(const UniValue& prevTxsUnival, FlatSigningProvider* keystore,
void SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore, const std::map<COutPoint, Coin>& coins, const UniValue& hashType, UniValue& result) void SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore, const std::map<COutPoint, Coin>& coins, const UniValue& hashType, UniValue& result)
{ {
int nHashType = ParseSighashString(hashType); std::optional<int> nHashType = ParseSighashString(hashType);
if (!nHashType) {
nHashType = SIGHASH_DEFAULT;
}
// Script verification errors // Script verification errors
std::map<int, bilingual_str> input_errors; std::map<int, bilingual_str> input_errors;
bool complete = SignTransaction(mtx, keystore, coins, nHashType, input_errors); bool complete = SignTransaction(mtx, keystore, coins, *nHashType, input_errors);
SignTransactionResultToJSON(mtx, complete, coins, input_errors, result); SignTransactionResultToJSON(mtx, complete, coins, input_errors, result);
} }
+2 -2
View File
@@ -378,10 +378,10 @@ UniValue DescribeAddress(const CTxDestination& dest)
* *
* @pre The sighash argument should be string or null. * @pre The sighash argument should be string or null.
*/ */
int ParseSighashString(const UniValue& sighash) std::optional<int> ParseSighashString(const UniValue& sighash)
{ {
if (sighash.isNull()) { if (sighash.isNull()) {
return SIGHASH_DEFAULT; return std::nullopt;
} }
const auto result{SighashFromStr(sighash.get_str())}; const auto result{SighashFromStr(sighash.get_str())};
if (!result) { if (!result) {
+1 -1
View File
@@ -138,7 +138,7 @@ CTxDestination AddAndGetMultisigDestination(const int required, const std::vecto
UniValue DescribeAddress(const CTxDestination& dest); UniValue DescribeAddress(const CTxDestination& dest);
/** Parse a sighash string representation and raise an RPC error if it is invalid. */ /** Parse a sighash string representation and raise an RPC error if it is invalid. */
int ParseSighashString(const UniValue& sighash); std::optional<int> ParseSighashString(const UniValue& sighash);
//! Parse a confirm target option and raise an RPC error if it is invalid. //! Parse a confirm target option and raise an RPC error if it is invalid.
unsigned int ParseConfirmTarget(const UniValue& value, unsigned int max_target); unsigned int ParseConfirmTarget(const UniValue& value, unsigned int max_target);
+6 -3
View File
@@ -956,12 +956,15 @@ RPCHelpMan signrawtransactionwithwallet()
// Parse the prevtxs array // Parse the prevtxs array
ParsePrevouts(request.params[1], nullptr, coins); ParsePrevouts(request.params[1], nullptr, coins);
int nHashType = ParseSighashString(request.params[2]); std::optional<int> nHashType = ParseSighashString(request.params[2]);
if (!nHashType) {
nHashType = SIGHASH_DEFAULT;
}
// Script verification errors // Script verification errors
std::map<int, bilingual_str> input_errors; std::map<int, bilingual_str> input_errors;
bool complete = pwallet->SignTransaction(mtx, coins, nHashType, input_errors); bool complete = pwallet->SignTransaction(mtx, coins, *nHashType, input_errors);
UniValue result(UniValue::VOBJ); UniValue result(UniValue::VOBJ);
SignTransactionResultToJSON(mtx, complete, coins, input_errors, result); SignTransactionResultToJSON(mtx, complete, coins, input_errors, result);
return result; return result;
@@ -1629,7 +1632,7 @@ RPCHelpMan walletprocesspsbt()
} }
// Get the sighash type // Get the sighash type
int nHashType = ParseSighashString(request.params[2]); std::optional<int> nHashType = ParseSighashString(request.params[2]);
// Fill transaction with our data and also sign // Fill transaction with our data and also sign
bool sign = request.params[1].isNull() ? true : request.params[1].get_bool(); bool sign = request.params[1].isNull() ? true : request.params[1].get_bool();
+1 -2
View File
@@ -1316,7 +1316,6 @@ std::optional<PSBTError> DescriptorScriptPubKeyMan::FillPSBT(PartiallySignedTran
if (n_signed) { if (n_signed) {
*n_signed = 0; *n_signed = 0;
} }
if (!sighash_type) sighash_type = SIGHASH_DEFAULT;
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) { for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
const CTxIn& txin = psbtx.tx->vin[i]; const CTxIn& txin = psbtx.tx->vin[i];
PSBTInput& input = psbtx.inputs.at(i); PSBTInput& input = psbtx.inputs.at(i);
@@ -1387,7 +1386,7 @@ std::optional<PSBTError> DescriptorScriptPubKeyMan::FillPSBT(PartiallySignedTran
} }
} }
PSBTError res = SignPSBTInput(HidingSigningProvider(keys.get(), /*hide_secret=*/!sign, /*hide_origin=*/!bip32derivs), psbtx, i, &txdata, *sighash_type, nullptr, finalize); PSBTError res = SignPSBTInput(HidingSigningProvider(keys.get(), /*hide_secret=*/!sign, /*hide_origin=*/!bip32derivs), psbtx, i, &txdata, sighash_type, nullptr, finalize);
if (res != PSBTError::OK && res != PSBTError::INCOMPLETE) { if (res != PSBTError::OK && res != PSBTError::INCOMPLETE) {
return res; return res;
} }
+1 -2
View File
@@ -2106,7 +2106,6 @@ std::optional<PSBTError> CWallet::FillPSBT(PartiallySignedTransaction& psbtx, bo
if (n_signed) { if (n_signed) {
*n_signed = 0; *n_signed = 0;
} }
if (!sighash_type) sighash_type = SIGHASH_DEFAULT;
LOCK(cs_wallet); LOCK(cs_wallet);
// Get all of the previous transactions // Get all of the previous transactions
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) { for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
@@ -2145,7 +2144,7 @@ std::optional<PSBTError> CWallet::FillPSBT(PartiallySignedTransaction& psbtx, bo
} }
} }
RemoveUnnecessaryTransactions(psbtx, *sighash_type); RemoveUnnecessaryTransactions(psbtx, sighash_type);
// Complete if every input is now signed // Complete if every input is now signed
complete = true; complete = true;