Skip to content

Commit

Permalink
psbt: Check sighash types in SignPSBTInput and take sighash as optional
Browse files Browse the repository at this point in the history
  • Loading branch information
achow101 committed Jan 9, 2025
1 parent e19eac3 commit cc550cb
Show file tree
Hide file tree
Showing 10 changed files with 38 additions and 22 deletions.
4 changes: 2 additions & 2 deletions src/node/psbt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx)

// Figure out what is missing
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
if (!complete) {
Expand Down Expand Up @@ -124,7 +124,7 @@ PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx)
PSBTInput& input = psbtx.inputs[i];
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;
break;
} else {
Expand Down
19 changes: 16 additions & 3 deletions src/psbt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ PrecomputedTransactionData PrecomputePSBTData(const PartiallySignedTransaction&
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);
const CMutableTransaction& tx = *psbt.tx;
Expand Down Expand Up @@ -413,12 +413,25 @@ PSBTError SignPSBTInput(const SigningProvider& provider, PartiallySignedTransact
return PSBTError::MISSING_INPUTS;
}

// Get the sighash type
// If both the field and the paramter are provided, they must match
// If only the psbt field is provided, refuse to sign. For user safety, the desired sighash must
// be provided if the PSBT wants something other than DEFAULT
// If only the paramter is provider, 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 (!sighash) sighash = utxo.scriptPubKey.IsPayToTaproot() ? SIGHASH_DEFAULT : SIGHASH_ALL;
if (input.sighash_type && input.sighash_type != sighash) {
return PSBTError::SIGHASH_MISMATCH;
}
Assert(sighash.has_value());

sigdata.witness = false;
bool sig_complete;
if (txdata == nullptr) {
sig_complete = ProduceSignature(provider, DUMMY_SIGNATURE_CREATOR, utxo.scriptPubKey, sigdata);
} 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);
}
// Verify that a witness signature was produced in case one was required.
Expand Down Expand Up @@ -489,7 +502,7 @@ bool FinalizePSBT(PartiallySignedTransaction& psbtx)
bool complete = true;
const PrecomputedTransactionData txdata = PrecomputePSBTData(psbtx);
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;
Expand Down
2 changes: 1 addition & 1 deletion src/psbt.h
Original file line number Diff line number Diff line change
Expand Up @@ -1239,7 +1239,7 @@ bool PSBTInputSignedAndVerified(const PartiallySignedTransaction psbt, unsigned
* 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.
**/
PSBTError SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, const PrecomputedTransactionData* txdata, int sighash = SIGHASH_ALL, SignatureData* out_sigdata = nullptr, bool finalize = true);
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. */
void RemoveUnnecessaryTransactions(PartiallySignedTransaction& psbtx, const int& sighash_type);
Expand Down
6 changes: 3 additions & 3 deletions src/rpc/rawtransaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
// 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
PartiallySignedTransaction psbtx;
Expand Down Expand Up @@ -1690,7 +1690,7 @@ static RPCHelpMan utxoupdatepsbt()
request.params[0].get_str(),
request.context,
HidingSigningProvider(&provider, /*hide_secret=*/true, /*hide_origin=*/false),
/*sighash_type=*/SIGHASH_ALL,
/*sighash_type=*/std::nullopt,
/*finalize=*/false);

DataStream ssTx{};
Expand Down Expand Up @@ -1957,7 +1957,7 @@ RPCHelpMan descriptorprocesspsbt()
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 finalize = request.params[4].isNull() ? true : request.params[4].get_bool();

Expand Down
7 changes: 5 additions & 2 deletions src/rpc/rawtransaction_util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
int nHashType = ParseSighashString(hashType);
std::optional<int> nHashType = ParseSighashString(hashType);
if (!nHashType) {
nHashType = SIGHASH_DEFAULT;
}

// Script verification 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);
}

Expand Down
4 changes: 2 additions & 2 deletions src/rpc/util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -376,10 +376,10 @@ UniValue DescribeAddress(const CTxDestination& dest)
*
* @pre The sighash argument should be string or null.
*/
int ParseSighashString(const UniValue& sighash)
std::optional<int> ParseSighashString(const UniValue& sighash)
{
if (sighash.isNull()) {
return SIGHASH_DEFAULT;
return std::nullopt;
}
const auto result{SighashFromStr(sighash.get_str())};
if (!result) {
Expand Down
2 changes: 1 addition & 1 deletion src/rpc/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ CTxDestination AddAndGetMultisigDestination(const int required, const std::vecto
UniValue DescribeAddress(const CTxDestination& dest);

/** 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.
unsigned int ParseConfirmTarget(const UniValue& value, unsigned int max_target);
Expand Down
9 changes: 6 additions & 3 deletions src/wallet/rpc/spend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -951,12 +951,15 @@ RPCHelpMan signrawtransactionwithwallet()
// Parse the prevtxs array
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
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);
SignTransactionResultToJSON(mtx, complete, coins, input_errors, result);
return result;
Expand Down Expand Up @@ -1621,7 +1624,7 @@ RPCHelpMan walletprocesspsbt()
}

// 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
bool sign = request.params[1].isNull() ? true : request.params[1].get_bool();
Expand Down
6 changes: 2 additions & 4 deletions src/wallet/scriptpubkeyman.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -637,7 +637,6 @@ std::optional<PSBTError> LegacyScriptPubKeyMan::FillPSBT(PartiallySignedTransact
if (n_signed) {
*n_signed = 0;
}
if (!sighash_type) sighash_type = SIGHASH_DEFAULT;
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
const CTxIn& txin = psbtx.tx->vin[i];
PSBTInput& input = psbtx.inputs.at(i);
Expand All @@ -660,7 +659,7 @@ std::optional<PSBTError> LegacyScriptPubKeyMan::FillPSBT(PartiallySignedTransact
// There's no UTXO so we can just skip this now
continue;
}
PSBTError res = SignPSBTInput(HidingSigningProvider(this, !sign, !bip32derivs), psbtx, i, &txdata, *sighash_type, nullptr, finalize);
PSBTError res = SignPSBTInput(HidingSigningProvider(this, !sign, !bip32derivs), psbtx, i, &txdata, sighash_type, nullptr, finalize);
if (res != PSBTError::OK && res != PSBTError::INCOMPLETE) {
return res;
}
Expand Down Expand Up @@ -2538,7 +2537,6 @@ std::optional<PSBTError> DescriptorScriptPubKeyMan::FillPSBT(PartiallySignedTran
if (n_signed) {
*n_signed = 0;
}
if (!sighash_type) sighash_type = SIGHASH_DEFAULT;
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
const CTxIn& txin = psbtx.tx->vin[i];
PSBTInput& input = psbtx.inputs.at(i);
Expand Down Expand Up @@ -2609,7 +2607,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) {
return res;
}
Expand Down
1 change: 0 additions & 1 deletion src/wallet/wallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2196,7 +2196,6 @@ std::optional<PSBTError> CWallet::FillPSBT(PartiallySignedTransaction& psbtx, bo
if (n_signed) {
*n_signed = 0;
}
if (!sighash_type) sighash_type = SIGHASH_DEFAULT;
LOCK(cs_wallet);
// Get all of the previous transactions
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
Expand Down

0 comments on commit cc550cb

Please sign in to comment.