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::