From 74f22a0a67f95ec6dc993cc550b3122d4527bd42 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Mon, 23 Jun 2025 20:13:55 +0000 Subject: [PATCH] psbt: validate that non_witness_utxo txids match the input txids This requires updating a couple serde test vectors -- we are tightening validation so that some existing serde-serialized PSBTs no longer deserialize. I believe this is the correct thing to do. --- bitcoin/src/psbt/mod.rs | 1 + bitcoin/src/psbt/serialize.rs | 16 ++++++++++++++-- bitcoin/tests/data/serde/psbt_base64.json | 2 +- bitcoin/tests/data/serde/psbt_bincode | Bin 810 -> 810 bytes bitcoin/tests/serde.rs | 1 + 5 files changed, 17 insertions(+), 3 deletions(-) diff --git a/bitcoin/src/psbt/mod.rs b/bitcoin/src/psbt/mod.rs index e78328fee..e55480b88 100644 --- a/bitcoin/src/psbt/mod.rs +++ b/bitcoin/src/psbt/mod.rs @@ -1667,6 +1667,7 @@ mod tests { }, unsigned_tx: { let mut unsigned = tx.clone(); + unsigned.input[0].previous_output.txid = tx.compute_txid(); unsigned.input[0].script_sig = ScriptBuf::new(); unsigned.input[0].witness = Witness::default(); unsigned diff --git a/bitcoin/src/psbt/serialize.rs b/bitcoin/src/psbt/serialize.rs index 542c9798f..468afae2d 100644 --- a/bitcoin/src/psbt/serialize.rs +++ b/bitcoin/src/psbt/serialize.rs @@ -103,8 +103,20 @@ impl Psbt { let mut inputs: Vec = Vec::with_capacity(inputs_len); - for _ in 0..inputs_len { - inputs.push(Input::decode(r)?); + for i in 0..inputs_len { + let input = Input::decode(r)?; + if let Some(ref tx) = input.non_witness_utxo { + let input_outpoint = global.unsigned_tx.input[i].previous_output; + let txid = tx.compute_txid(); + if txid != input_outpoint.txid { + return Err(Error::IncorrectNonWitnessUtxo { + index: i, + input_outpoint, + non_witness_utxo_txid: txid, + }); + } + } + inputs.push(input); } inputs diff --git a/bitcoin/tests/data/serde/psbt_base64.json b/bitcoin/tests/data/serde/psbt_base64.json index 6bb318f10..d05155177 100644 --- a/bitcoin/tests/data/serde/psbt_base64.json +++ b/bitcoin/tests/data/serde/psbt_base64.json @@ -1 +1 @@ -"cHNidP8BAFMBAAAAAYmjxx6rTSDgNxu7pMxpj6KVyUY6+i45f4UzzLYvlWflAQAAAAD/////AXL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAAAAAE8BBIiyHgAAAAAAAAAAAIc9/4HAL1JWI/0f5RZ+rDpVoEnePTFLtC7iJ//tN9UIAzmjYBMwFZfa70H75ZOgLMUT0LVVJ+wt8QUOLo/0nIXCDN6tvu8AAACAAQAAABD8BXByZWZ4KnRlc3Rfa2V5AwUGBwMJAAEDAwQFAAEAjwEAAAAAAQGJo8ceq00g4Dcbu6TMaY+ilclGOvouOX+FM8y2L5Vn5QEAAAAXFgAUvhjRUqmwEgOdrz2n3k9TNJ7suYX/////AXL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUAAAAAAQEgcv74TiwAAAAXqRQzlyW6Ie/WKsdTqbzQZ9bHpqOdBYciAgM5iA3JI5S3NV49BDn6KDwx3nWQgS6gEcQkXAZ0poXog0cwRAIgT2fir7dhQtRPrliiSV0zo0GdqibNDbjQTzRStjKJrA8CIBB2Kp+2fpTMXK2QJvbcmf9/Bw9CeNMPvH0Mhp3TjH/nAQEDBIMAAAABBAFRIgYDOYgNySOUtzVePQQ5+ig8Md51kIEuoBHEJFwGdKaF6IMM3q2+7wAAAIABAAAAAQgGAgIBAwEFFQoYn3yLGjhv/o7tkbODDHp7zR53jAIBAiELoShx/uIQ+4YZKR6uoZRYHL0lMeSyN1nSJfaAaSP2MiICAQIVDBXMSeGRy8Ug2RlEYApct3r2qjKRAgECIQ12pWrO2RXSUT3NhMLDeLLoqlzWMrW3HKLyrFsOOmSb2wIBAhD8BXByZWZ4KnRlc3Rfa2V5AwUGBwMJAAEDAwQFACICAzmIDckjlLc1Xj0EOfooPDHedZCBLqARxCRcBnSmheiDDN6tvu8AAACAAQAAABD8BXByZWZ4KnRlc3Rfa2V5AwUGBwMJAAEDAwQFAA==" \ No newline at end of file +"cHNidP8BAFMBAAAAATkUkZZWjQ4TAMqaOkez2dl2+5yBsfd38qS6x8fkjesmAQAAAAD/////AXL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAAAAAE8BBIiyHgAAAAAAAAAAAIc9/4HAL1JWI/0f5RZ+rDpVoEnePTFLtC7iJ//tN9UIAzmjYBMwFZfa70H75ZOgLMUT0LVVJ+wt8QUOLo/0nIXCDN6tvu8AAACAAQAAABD8BXByZWZ4KnRlc3Rfa2V5AwUGBwMJAAEDAwQFAAEAjwEAAAAAAQGJo8ceq00g4Dcbu6TMaY+ilclGOvouOX+FM8y2L5Vn5QEAAAAXFgAUvhjRUqmwEgOdrz2n3k9TNJ7suYX/////AXL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUAAAAAAQEgcv74TiwAAAAXqRQzlyW6Ie/WKsdTqbzQZ9bHpqOdBYciAgM5iA3JI5S3NV49BDn6KDwx3nWQgS6gEcQkXAZ0poXog0cwRAIgT2fir7dhQtRPrliiSV0zo0GdqibNDbjQTzRStjKJrA8CIBB2Kp+2fpTMXK2QJvbcmf9/Bw9CeNMPvH0Mhp3TjH/nAQEDBIMAAAABBAFRIgYDOYgNySOUtzVePQQ5+ig8Md51kIEuoBHEJFwGdKaF6IMM3q2+7wAAAIABAAAAAQgGAgIBAwEFFQoYn3yLGjhv/o7tkbODDHp7zR53jAIBAiELoShx/uIQ+4YZKR6uoZRYHL0lMeSyN1nSJfaAaSP2MiICAQIVDBXMSeGRy8Ug2RlEYApct3r2qjKRAgECIQ12pWrO2RXSUT3NhMLDeLLoqlzWMrW3HKLyrFsOOmSb2wIBAhD8BXByZWZ4KnRlc3Rfa2V5AwUGBwMJAAEDAwQFACICAzmIDckjlLc1Xj0EOfooPDHedZCBLqARxCRcBnSmheiDDN6tvu8AAACAAQAAABD8BXByZWZ4KnRlc3Rfa2V5AwUGBwMJAAEDAwQFAA==" \ No newline at end of file diff --git a/bitcoin/tests/data/serde/psbt_bincode b/bitcoin/tests/data/serde/psbt_bincode index 35d580f4b0ddd26395b7974739e0cd48ad82fbe4..0423835c0ee0459e4560ca54f4c3583e246b7664 100644 GIT binary patch delta 43 zcmV+`0M!4g2C4>-6(Bhjk(O4C4if;%nmR|b+1YmcoPn|Tck-mV$H(N2>n5=^^#Y#~ B7196z delta 13 VcmZ3*wu)_n=)@N)8!bOF0RSbZ1%Lnm diff --git a/bitcoin/tests/serde.rs b/bitcoin/tests/serde.rs index 3d2b4a28a..0a4f4bbb1 100644 --- a/bitcoin/tests/serde.rs +++ b/bitcoin/tests/serde.rs @@ -285,6 +285,7 @@ fn serde_regression_psbt() { }, unsigned_tx: { let mut unsigned = tx.clone(); + unsigned.input[0].previous_output.txid = tx.compute_txid(); unsigned.input[0].script_sig = ScriptBuf::new(); unsigned.input[0].witness = Witness::default(); unsigned