transaction: make 0-input de/serialization always use Segwit

This commit is contained in:
Andrew Poelstra 2018-08-24 19:39:22 +00:00
parent 45140a3251
commit 98e39b4383
1 changed files with 27 additions and 3 deletions

View File

@ -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)] #[derive(Clone, PartialEq, Eq, Debug, Hash)]
pub struct Transaction { pub struct Transaction {
/// The protocol version, should always be 1. /// The protocol version, should always be 1.
@ -357,7 +368,7 @@ impl<D: Decoder> Decodable<D> for TxIn {
impl<S: Encoder> Encodable<S> for Transaction { impl<S: Encoder> Encodable<S> for Transaction {
fn consensus_encode(&self, s: &mut S) -> Result <(), encode::Error> { fn consensus_encode(&self, s: &mut S) -> Result <(), encode::Error> {
self.version.consensus_encode(s)?; self.version.consensus_encode(s)?;
let mut have_witness = false; let mut have_witness = self.input.is_empty();
for input in &self.input { for input in &self.input {
if !input.witness.is_empty() { if !input.witness.is_empty() {
have_witness = true; have_witness = true;
@ -495,7 +506,6 @@ mod tests {
use super::{Transaction, TxIn}; use super::{Transaction, TxIn};
use blockdata::script::Script; use blockdata::script::Script;
#[cfg(all(feature = "serde", feature = "strason"))]
use consensus::encode::serialize; use consensus::encode::serialize;
use consensus::encode::deserialize; use consensus::encode::deserialize;
use util::hash::{BitcoinHash, Sha256dHash}; use util::hash::{BitcoinHash, Sha256dHash};
@ -542,6 +552,20 @@ mod tests {
assert_eq!(realtx.get_weight(), 193*4); 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] #[test]
fn test_ntxid() { fn test_ntxid() {
let hex_tx = hex_bytes("0100000001a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff0100e1f505000000001976a9140389035a9225b3839e2bbf32d826a1e222031fd888ac00000000").unwrap(); let hex_tx = hex_bytes("0100000001a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff0100e1f505000000001976a9140389035a9225b3839e2bbf32d826a1e222031fd888ac00000000").unwrap();