Skip to content

Commit 27d2485

Browse files
committed
Merge bitcoin#25595: Verify PSBT inputs rather than check for fields being empty
Backport of bitcoin#25595 Original commit: 2ac71d2 Adapted for Dash: - Removed witness_utxo check (Dash does not support SegWit) - Removed final_script_witness from PSBTInputSigned (not in Dash) - Adapted VerifyScript calls for Dash API (no witness parameter) - Test changes omitted: requires test_framework/psbt.py from bitcoin#25625
1 parent 9cf919c commit 27d2485

File tree

4 files changed

+34
-3
lines changed

4 files changed

+34
-3
lines changed

src/node/psbt.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx)
5858
}
5959

6060
// Check if it is final
61-
if (!utxo.IsNull() && !PSBTInputSigned(input)) {
61+
if (!PSBTInputSignedAndVerified(psbtx, i, &txdata)) {
6262
input_analysis.is_final = false;
6363

6464
// Figure out what is missing

src/psbt.cpp

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,33 @@ void PSBTOutput::Merge(const PSBTOutput& output)
181181
if (redeem_script.empty() && !output.redeem_script.empty()) redeem_script = output.redeem_script;
182182
}
183183

184+
bool PSBTInputSignedAndVerified(const PartiallySignedTransaction psbt, unsigned int input_index, const PrecomputedTransactionData* txdata)
185+
{
186+
CTxOut utxo;
187+
assert(psbt.inputs.size() >= input_index);
188+
const PSBTInput& input = psbt.inputs[input_index];
189+
190+
if (input.non_witness_utxo) {
191+
// If we're taking our information from a non-witness UTXO, verify that it matches the prevout.
192+
COutPoint prevout = psbt.tx->vin[input_index].prevout;
193+
if (prevout.n >= input.non_witness_utxo->vout.size()) {
194+
return false;
195+
}
196+
if (input.non_witness_utxo->GetHash() != prevout.hash) {
197+
return false;
198+
}
199+
utxo = input.non_witness_utxo->vout[prevout.n];
200+
} else {
201+
return false;
202+
}
203+
204+
if (txdata) {
205+
return VerifyScript(input.final_script_sig, utxo.scriptPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker{&(*psbt.tx), input_index, utxo.nValue, *txdata, MissingDataBehavior::FAIL});
206+
} else {
207+
return VerifyScript(input.final_script_sig, utxo.scriptPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker{&(*psbt.tx), input_index, utxo.nValue, MissingDataBehavior::FAIL});
208+
}
209+
}
210+
184211
size_t CountPSBTUnsignedInputs(const PartiallySignedTransaction& psbt) {
185212
size_t count = 0;
186213
for (const auto& input : psbt.inputs) {
@@ -238,7 +265,7 @@ bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction&
238265
PSBTInput& input = psbt.inputs.at(index);
239266
const CMutableTransaction& tx = *psbt.tx;
240267

241-
if (PSBTInputSigned(input)) {
268+
if (PSBTInputSignedAndVerified(psbt, index, txdata)) {
242269
return true;
243270
}
244271

src/psbt.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -865,9 +865,12 @@ std::string PSBTRoleName(PSBTRole role);
865865
/** Compute a PrecomputedTransactionData object from a psbt. */
866866
PrecomputedTransactionData PrecomputePSBTData(const PartiallySignedTransaction& psbt);
867867

868-
/** Checks whether a PSBTInput is already signed. */
868+
/** Checks whether a PSBTInput is already signed by checking for non-null finalized fields. */
869869
bool PSBTInputSigned(const PSBTInput& input);
870870

871+
/** Checks whether a PSBTInput is already signed by doing script verification using final fields. */
872+
bool PSBTInputSignedAndVerified(const PartiallySignedTransaction psbt, unsigned int input_index, const PrecomputedTransactionData* txdata);
873+
871874
/** Signs a PSBTInput, verifying that all provided data matches what is being signed.
872875
*
873876
* txdata should be the output of PrecomputePSBTData (which can be shared across

test/functional/rpc_psbt.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,5 +504,6 @@ def test_psbt_input_keys(psbt_input, keys):
504504
self.log.info("Test we don't crash when making a 0-value funded transaction at 0 fee without forcing an input selection")
505505
assert_raises_rpc_error(-4, "Transaction requires one destination of non-0 value, a non-0 feerate, or a pre-selected input", self.nodes[0].walletcreatefundedpsbt, [], [{"data": "deadbeef"}], 0, {"fee_rate": "0"})
506506

507+
507508
if __name__ == '__main__':
508509
PSBTTest().main()

0 commit comments

Comments
 (0)