diff --git a/src/blockdata/transaction.rs b/src/blockdata/transaction.rs index 849b54e..a0b28d2 100644 --- a/src/blockdata/transaction.rs +++ b/src/blockdata/transaction.rs @@ -128,7 +128,18 @@ impl Default for TxOut { } } -/// A Bitcoin transaction, which describes an authenticated movement of coins +/// A Bitcoin transaction, which describes an authenticated movement of coins. +/// +/// If any inputs have nonempty witnesses, the entire transaction is serialized +/// in the post-BIP141 Segwit format which includes a list of witnesses. If all +/// inputs have empty witnesses, the transaction is serialized in the pre-BIP141 +/// format. +/// +/// There is one major exception to this: to avoid deserialization ambiguity, +/// if the transaction has no inputs, it is serialized in the BIP141 style. Be +/// aware that this differs from the transaction format in PSBT, which _never_ +/// uses BIP141. (Ordinarily there is no conflict, since in PSBT transactions +/// are always unsigned and therefore their inputs have empty witnesses.) #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub struct Transaction { /// The protocol version, should always be 1. @@ -357,7 +368,7 @@ impl Decodable for TxIn { impl Encodable for Transaction { fn consensus_encode(&self, s: &mut S) -> Result <(), encode::Error> { self.version.consensus_encode(s)?; - let mut have_witness = false; + let mut have_witness = self.input.is_empty(); for input in &self.input { if !input.witness.is_empty() { have_witness = true; @@ -495,7 +506,6 @@ mod tests { use super::{Transaction, TxIn}; use blockdata::script::Script; - #[cfg(all(feature = "serde", feature = "strason"))] use consensus::encode::serialize; use consensus::encode::deserialize; use util::hash::{BitcoinHash, Sha256dHash}; @@ -542,6 +552,20 @@ mod tests { assert_eq!(realtx.get_weight(), 193*4); } + #[test] + fn tx_no_input_deserialization() { + let hex_tx = hex_bytes( + "010000000001000100e1f505000000001976a9140389035a9225b3839e2bbf32d826a1e222031fd888ac00000000" + ).unwrap(); + let tx: Transaction = deserialize(&hex_tx).expect("deserialize tx"); + + assert_eq!(tx.input.len(), 0); + assert_eq!(tx.output.len(), 1); + + let reser = serialize(&tx); + assert_eq!(hex_tx, reser); + } + #[test] fn test_ntxid() { let hex_tx = hex_bytes("0100000001a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff0100e1f505000000001976a9140389035a9225b3839e2bbf32d826a1e222031fd888ac00000000").unwrap();