From 4957567409fbfdb70ef7417e317905ec95cabf32 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Fri, 7 Feb 2020 13:05:04 -0800 Subject: [PATCH] Represent coinbase inputs explicitly. Coinbase inputs are handled differently from other inputs and have different consensus rules, so they should be represented differently in the source code. This lets us discard extraneous details (for instance, it's not necessary to maintain the all-zero hash) and specialize logic. --- zebra-chain/src/block.rs | 12 ++--- zebra-chain/src/transaction/serialize.rs | 54 ++++++++++++++++++---- zebra-chain/src/transaction/tests.rs | 27 ++++++++++- zebra-chain/src/transaction/transparent.rs | 29 +++++++----- 4 files changed, 91 insertions(+), 31 deletions(-) diff --git a/zebra-chain/src/block.rs b/zebra-chain/src/block.rs index dfe7bba7..2c38b0d3 100644 --- a/zebra-chain/src/block.rs +++ b/zebra-chain/src/block.rs @@ -152,19 +152,13 @@ impl ZcashDeserialize for BlockHeader { } } -/// A block in your blockchain. -/// -/// A block is a data structure with two fields: -/// -/// Block header: a data structure containing the block's metadata -/// Transactions: an array (vector in Rust) of transactions +/// A Zcash block, containing a [`BlockHeader`] and a sequence of +/// [`Transaction`]s. #[derive(Clone, Debug, Eq, PartialEq)] #[cfg_attr(test, derive(Arbitrary))] pub struct Block { - /// First 80 bytes of the block as defined by the encoding used by - /// "block" messages. + /// The block header, containing block metadata. pub header: BlockHeader, - /// The block transactions. pub transactions: Vec, } diff --git a/zebra-chain/src/transaction/serialize.rs b/zebra-chain/src/transaction/serialize.rs index 89df7738..41c55080 100644 --- a/zebra-chain/src/transaction/serialize.rs +++ b/zebra-chain/src/transaction/serialize.rs @@ -2,7 +2,7 @@ //! transaction types, so that all of the serialization logic is in one place. use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; -use std::io; +use std::io::{self, Read}; use crate::proofs::ZkSnarkProof; use crate::serialization::{ @@ -34,20 +34,56 @@ impl ZcashDeserialize for OutPoint { impl ZcashSerialize for TransparentInput { fn zcash_serialize(&self, mut writer: W) -> Result<(), io::Error> { - self.previous_output.zcash_serialize(&mut writer)?; - self.signature_script.zcash_serialize(&mut writer)?; - writer.write_u32::(self.sequence)?; + match self { + TransparentInput::PrevOut { + outpoint, + script, + sequence, + } => { + outpoint.zcash_serialize(&mut writer)?; + script.zcash_serialize(&mut writer)?; + writer.write_u32::(*sequence)?; + } + TransparentInput::Coinbase { data, sequence } => { + writer.write_all(&[0; 32][..])?; + writer.write_u32::(0xffff_ffff)?; + assert!(data.len() <= 100); + writer.write_compactsize(data.len() as u64)?; + writer.write_all(&data[..])?; + writer.write_u32::(*sequence)?; + } + } Ok(()) } } impl ZcashDeserialize for TransparentInput { fn zcash_deserialize(mut reader: R) -> Result { - Ok(TransparentInput { - previous_output: OutPoint::zcash_deserialize(&mut reader)?, - signature_script: Script::zcash_deserialize(&mut reader)?, - sequence: reader.read_u32::()?, - }) + // This inlines the OutPoint deserialization to peek at the hash value + // and detect whether we have a coinbase input. + let bytes = reader.read_32_bytes()?; + if bytes == [0; 32] { + if reader.read_u32::()? != 0xffff_ffff { + return Err(SerializationError::Parse("wrong index in coinbase")); + } + let len = reader.read_compactsize()?; + if len > 100 { + return Err(SerializationError::Parse("coinbase has too much data")); + } + let mut data = Vec::with_capacity(len as usize); + (&mut reader).take(len).read_to_end(&mut data)?; + let sequence = reader.read_u32::()?; + Ok(TransparentInput::Coinbase { data, sequence }) + } else { + Ok(TransparentInput::PrevOut { + outpoint: OutPoint { + hash: TransactionHash(bytes), + index: reader.read_u32::()?, + }, + script: Script::zcash_deserialize(&mut reader)?, + sequence: reader.read_u32::()?, + }) + } } } diff --git a/zebra-chain/src/transaction/tests.rs b/zebra-chain/src/transaction/tests.rs index 2bb61b27..ced6ad73 100644 --- a/zebra-chain/src/transaction/tests.rs +++ b/zebra-chain/src/transaction/tests.rs @@ -7,7 +7,7 @@ use proptest::{ use crate::{ serialization::{ZcashDeserialize, ZcashSerialize}, - types::LockTime, + types::{LockTime, Script}, }; use super::*; @@ -116,6 +116,31 @@ impl Arbitrary for Transaction { type Strategy = BoxedStrategy; } +#[cfg(test)] +impl Arbitrary for TransparentInput { + type Parameters = (); + + fn arbitrary_with(_args: ()) -> Self::Strategy { + prop_oneof![ + (any::(), any::