Skip to content

Commit 2c18ec2

Browse files
committed
Merge rust-bitcoin#4637: psbt: check that non-witness UTXOs' txids match the input txid
53b9332 psbt: add test vector where non-witness UTXO TXID does not match input txid (Andrew Poelstra) 74f22a0 psbt: validate that non_witness_utxo txids match the input txids (Andrew Poelstra) 3337b7a psbt: introduce IncorrectNonWitnessUtxo error variant (Andrew Poelstra) Pull request description: Fixes rust-bitcoin#4617 ACKs for top commit: tcharding: ACK 53b9332 Tree-SHA512: bf841708493fe38a37c7448cc02a26061ea66fd3c7acdbf9df4bbdfe07d8611075dceed20813de2b7cd63864ad80a9f66eaf8b869249c3a2028e1be25b48c5ae
2 parents 8d3b69d + 53b9332 commit 2c18ec2

File tree

6 files changed

+57
-3
lines changed

6 files changed

+57
-3
lines changed

bitcoin/src/psbt/error.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,16 @@ pub enum Error {
8080
NegativeFee,
8181
/// Integer overflow in fee calculation
8282
FeeOverflow,
83+
/// Non-witness UTXO (which is a complete transaction) has [`crate::Txid`] that
84+
/// does not match the transaction input.
85+
IncorrectNonWitnessUtxo {
86+
/// The index of the input in question.
87+
index: usize,
88+
/// The outpoint of the input, as it appears in the unsigned transaction.
89+
input_outpoint: crate::OutPoint,
90+
/// The ['crate::Txid`] of the non-witness UTXO.
91+
non_witness_utxo_txid: crate::Txid,
92+
},
8393
/// Parsing error indicating invalid public keys
8494
InvalidPublicKey(crate::crypto::key::FromSliceError),
8595
/// Parsing error indicating invalid secp256k1 public keys
@@ -154,6 +164,13 @@ impl fmt::Display for Error {
154164
write_err!(f, "error parsing bitcoin consensus encoded object"; e),
155165
NegativeFee => f.write_str("PSBT has a negative fee which is not allowed"),
156166
FeeOverflow => f.write_str("integer overflow in fee calculation"),
167+
IncorrectNonWitnessUtxo { index, input_outpoint, non_witness_utxo_txid } => {
168+
write!(
169+
f,
170+
"non-witness utxo txid is {}, which does not match input {}'s outpoint {}",
171+
non_witness_utxo_txid, index, input_outpoint
172+
)
173+
}
157174
InvalidPublicKey(ref e) => write_err!(f, "invalid public key"; e),
158175
InvalidSecp256k1PublicKey(ref e) => write_err!(f, "invalid secp256k1 public key"; e),
159176
InvalidXOnlyPublicKey => f.write_str("invalid xonly public key"),
@@ -200,6 +217,7 @@ impl std::error::Error for Error {
200217
| CombineInconsistentKeySources(_)
201218
| NegativeFee
202219
| FeeOverflow
220+
| IncorrectNonWitnessUtxo { .. }
203221
| InvalidPublicKey(_)
204222
| InvalidSecp256k1PublicKey(_)
205223
| InvalidXOnlyPublicKey

bitcoin/src/psbt/mod.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1666,6 +1666,7 @@ mod tests {
16661666
},
16671667
unsigned_tx: {
16681668
let mut unsigned = tx.clone();
1669+
unsigned.input[0].previous_output.txid = tx.compute_txid();
16691670
unsigned.input[0].script_sig = ScriptBuf::new();
16701671
unsigned.input[0].witness = Witness::default();
16711672
unsigned
@@ -2111,6 +2112,28 @@ mod tests {
21112112
}
21122113
}
21132114

2115+
#[test]
2116+
fn invalid_vector_4617() {
2117+
let err = hex_psbt("70736274ff01007374ff0103010000000000000000002e2873007374ff0107736205000000000000000000000000000000000006060005feffffff74ff01000a000000000000002cc760008530b38dac0100030500000074ff01070100000000000000000000000000c0316888e006000600050000736274ff00d90001007374ff41030100000000000a0a06002e2873007374ff01070100000000000000000000000000000000ff0000060600050000736274ff01000a0080000000000024c7600005193b1e400700030500000074ff0107010000000000a9c7df3f07000570ed62c76004c3ca95c5f90200010742420a0a000000000000").unwrap_err();
2118+
match err {
2119+
Error::IncorrectNonWitnessUtxo { index: 0, input_outpoint, non_witness_utxo_txid } => {
2120+
assert_eq!(
2121+
input_outpoint,
2122+
"00000000000000000000000562730701ff74730073282e000000000000000000:0"
2123+
.parse()
2124+
.unwrap(),
2125+
);
2126+
assert_eq!(
2127+
non_witness_utxo_txid,
2128+
"9ed45fd3f73b038649bee6e763dbd70868745c48a0d2b0299f42c68f957995f4"
2129+
.parse()
2130+
.unwrap(),
2131+
);
2132+
}
2133+
_ => panic!("expected output hash mismatch error, got {}", err),
2134+
}
2135+
}
2136+
21142137
#[test]
21152138
fn serialize_and_deserialize_preimage_psbt() {
21162139
// create a sha preimage map

bitcoin/src/psbt/serialize.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,20 @@ impl Psbt {
103103

104104
let mut inputs: Vec<Input> = Vec::with_capacity(inputs_len);
105105

106-
for _ in 0..inputs_len {
107-
inputs.push(Input::decode(r)?);
106+
for i in 0..inputs_len {
107+
let input = Input::decode(r)?;
108+
if let Some(ref tx) = input.non_witness_utxo {
109+
let input_outpoint = global.unsigned_tx.input[i].previous_output;
110+
let txid = tx.compute_txid();
111+
if txid != input_outpoint.txid {
112+
return Err(Error::IncorrectNonWitnessUtxo {
113+
index: i,
114+
input_outpoint,
115+
non_witness_utxo_txid: txid,
116+
});
117+
}
118+
}
119+
inputs.push(input);
108120
}
109121

110122
inputs
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +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=="
1+
"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=="
0 Bytes
Binary file not shown.

bitcoin/tests/serde.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,7 @@ fn serde_regression_psbt() {
285285
},
286286
unsigned_tx: {
287287
let mut unsigned = tx.clone();
288+
unsigned.input[0].previous_output.txid = tx.compute_txid();
288289
unsigned.input[0].script_sig = ScriptBuf::new();
289290
unsigned.input[0].witness = Witness::default();
290291
unsigned

0 commit comments

Comments
 (0)