From 0474a79669fa876e371068190e5f069cfdbc2c70 Mon Sep 17 00:00:00 2001 From: Jane Lusby Date: Thu, 9 Jul 2020 12:29:31 -0700 Subject: [PATCH] Add cool helpers for using zcash_serialize / zcash_deserialize (#586) Co-authored-by: Dimitris Apostolou --- zebra-chain/src/block/serialize.rs | 7 +-- zebra-chain/src/block/tests.rs | 68 +++++++++++++--------------- zebra-chain/src/equihash_solution.rs | 25 +++++----- zebra-chain/src/serialization.rs | 24 ++++++++++ zebra-chain/src/transaction/tests.rs | 10 ++-- 5 files changed, 75 insertions(+), 59 deletions(-) diff --git a/zebra-chain/src/block/serialize.rs b/zebra-chain/src/block/serialize.rs index c5eafa449..ee0768979 100644 --- a/zebra-chain/src/block/serialize.rs +++ b/zebra-chain/src/block/serialize.rs @@ -5,6 +5,7 @@ use std::io; use crate::equihash_solution::EquihashSolution; use crate::merkle_tree::MerkleTreeRootHash; use crate::note_commitment_tree::SaplingNoteTreeRootHash; +use crate::serialization::ZcashDeserializeInto; use crate::serialization::{ReadZcashExt, SerializationError, ZcashDeserialize, ZcashSerialize}; use super::Block; @@ -89,10 +90,10 @@ impl ZcashSerialize for Block { impl ZcashDeserialize for Block { fn zcash_deserialize(reader: R) -> Result { // If the limit is reached, we'll get an UnexpectedEof error - let mut limited_reader = reader.take(MAX_BLOCK_BYTES); + let limited_reader = &mut reader.take(MAX_BLOCK_BYTES); Ok(Block { - header: BlockHeader::zcash_deserialize(&mut limited_reader)?, - transactions: Vec::zcash_deserialize(&mut limited_reader)?, + header: limited_reader.zcash_deserialize_into()?, + transactions: limited_reader.zcash_deserialize_into()?, }) } } diff --git a/zebra-chain/src/block/tests.rs b/zebra-chain/src/block/tests.rs index 8a6893112..e0d2f2f93 100644 --- a/zebra-chain/src/block/tests.rs +++ b/zebra-chain/src/block/tests.rs @@ -1,22 +1,18 @@ +use super::*; +use crate::equihash_solution::EquihashSolution; +use crate::merkle_tree::MerkleTreeRootHash; +use crate::note_commitment_tree::SaplingNoteTreeRootHash; +use crate::serialization::{ + SerializationError, ZcashDeserialize, ZcashDeserializeInto, ZcashSerialize, +}; +use crate::{sha256d_writer::Sha256dWriter, test::generate}; use chrono::{TimeZone, Utc}; -use std::io::{Cursor, ErrorKind, Write}; - use proptest::{ arbitrary::{any, Arbitrary}, prelude::*, }; +use std::io::{Cursor, ErrorKind, Write}; -use crate::equihash_solution::EquihashSolution; -use crate::merkle_tree::MerkleTreeRootHash; -use crate::note_commitment_tree::SaplingNoteTreeRootHash; -use crate::serialization::{SerializationError, ZcashDeserialize, ZcashSerialize}; -use crate::sha256d_writer::Sha256dWriter; - -use crate::test::generate; - -use super::*; - -#[cfg(test)] impl Arbitrary for BlockHeader { type Parameters = (); @@ -92,7 +88,8 @@ fn blockheaderhash_from_blockheader() { .expect("these bytes to serialize from a blockheader without issue"); bytes.set_position(0); - let other_header = BlockHeader::zcash_deserialize(&mut bytes) + let other_header = bytes + .zcash_deserialize_into() .expect("these bytes to deserialize into a blockheader without issue"); assert_eq!(blockheader, other_header); @@ -101,23 +98,27 @@ fn blockheaderhash_from_blockheader() { #[test] fn deserialize_blockheader() { // https://explorer.zcha.in/blocks/415000 - let _header = - BlockHeader::zcash_deserialize(&zebra_test::vectors::HEADER_MAINNET_415000_BYTES[..]) - .expect("blockheader test vector should deserialize"); + let _header = zebra_test::vectors::HEADER_MAINNET_415000_BYTES + .zcash_deserialize_into::() + .expect("blockheader test vector should deserialize"); } #[test] fn deserialize_block() { - Block::zcash_deserialize(&zebra_test::vectors::BLOCK_MAINNET_GENESIS_BYTES[..]) + zebra_test::vectors::BLOCK_MAINNET_GENESIS_BYTES + .zcash_deserialize_into::() .expect("block test vector should deserialize"); - Block::zcash_deserialize(&zebra_test::vectors::BLOCK_MAINNET_1_BYTES[..]) + zebra_test::vectors::BLOCK_MAINNET_1_BYTES + .zcash_deserialize_into::() .expect("block test vector should deserialize"); // https://explorer.zcha.in/blocks/415000 - Block::zcash_deserialize(&zebra_test::vectors::BLOCK_MAINNET_415000_BYTES[..]) + zebra_test::vectors::BLOCK_MAINNET_415000_BYTES + .zcash_deserialize_into::() .expect("block test vector should deserialize"); // https://explorer.zcha.in/blocks/434873 // this one has a bad version field - Block::zcash_deserialize(&zebra_test::vectors::BLOCK_MAINNET_434873_BYTES[..]) + zebra_test::vectors::BLOCK_MAINNET_434873_BYTES + .zcash_deserialize_into::() .expect("block test vector should deserialize"); } @@ -193,39 +194,32 @@ proptest! { #[test] fn blockheaderhash_roundtrip(hash in any::()) { - let mut bytes = Cursor::new(Vec::new()); - hash.zcash_serialize(&mut bytes)?; - - bytes.set_position(0); - let other_hash = BlockHeaderHash::zcash_deserialize(&mut bytes)?; + let bytes = hash.zcash_serialize_to_vec()?; + let other_hash = bytes.zcash_deserialize_into()?; prop_assert_eq![hash, other_hash]; } #[test] fn blockheader_roundtrip(header in any::()) { - let mut bytes = Cursor::new(Vec::new()); - header.zcash_serialize(&mut bytes)?; - - bytes.set_position(0); - let other_header = BlockHeader::zcash_deserialize(&mut bytes)?; + let bytes = header.zcash_serialize_to_vec()?; + let other_header = bytes.zcash_deserialize_into()?; prop_assert_eq![header, other_header]; } #[test] fn block_roundtrip(block in any::()) { - let mut bytes = Cursor::new(Vec::new()); - block.zcash_serialize(&mut bytes)?; + let bytes = block.zcash_serialize_to_vec()?; + let bytes = &mut bytes.as_slice(); // Check the block size limit - if bytes.position() <= MAX_BLOCK_BYTES { - bytes.set_position(0); - let other_block = Block::zcash_deserialize(&mut bytes)?; + if bytes.len() <= MAX_BLOCK_BYTES as _ { + let other_block = bytes.zcash_deserialize_into()?; prop_assert_eq![block, other_block]; } else { - let serialization_err = Block::zcash_deserialize(&mut bytes) + let serialization_err = bytes.zcash_deserialize_into::() .expect_err("blocks larger than the maximum size should fail"); match serialization_err { SerializationError::Io(io_err) => { diff --git a/zebra-chain/src/equihash_solution.rs b/zebra-chain/src/equihash_solution.rs index cfbe3edfa..c411e62d4 100644 --- a/zebra-chain/src/equihash_solution.rs +++ b/zebra-chain/src/equihash_solution.rs @@ -84,6 +84,7 @@ impl ZcashDeserialize for EquihashSolution { mod tests { use super::*; use crate::block::{Block, BlockHeader}; + use crate::serialization::ZcashDeserializeInto; use proptest::{arbitrary::Arbitrary, collection::vec, prelude::*}; impl Arbitrary for EquihashSolution { @@ -102,20 +103,18 @@ mod tests { type Strategy = BoxedStrategy; } - proptest! { - - #[test] - fn equihash_solution_roundtrip(solution in any::()) { - zebra_test::init(); - let mut data = Vec::new(); - - solution.zcash_serialize(&mut data).expect("randomized EquihashSolution should serialize"); - - let solution2 = EquihashSolution::zcash_deserialize(&data[..]) + #[test] + fn equihash_solution_roundtrip() { + proptest!(|(solution in any::())| { + let data = solution + .zcash_serialize_to_vec() + .expect("randomized EquihashSolution should serialize"); + let solution2 = data + .zcash_deserialize_into() .expect("randomized EquihashSolution should deserialize"); prop_assert_eq![solution, solution2]; - } + }); } const EQUIHASH_SOLUTION_BLOCK_OFFSET: usize = EquihashSolution::INPUT_LENGTH + 32; @@ -125,7 +124,9 @@ mod tests { zebra_test::init(); let solution_bytes = &zebra_test::vectors::HEADER_MAINNET_415000_BYTES[EQUIHASH_SOLUTION_BLOCK_OFFSET..]; - let solution = EquihashSolution::zcash_deserialize(solution_bytes) + + let solution = solution_bytes + .zcash_deserialize_into::() .expect("Test vector EquihashSolution should deserialize"); let mut data = Vec::new(); diff --git a/zebra-chain/src/serialization.rs b/zebra-chain/src/serialization.rs index e60762a15..8481dab4b 100644 --- a/zebra-chain/src/serialization.rs +++ b/zebra-chain/src/serialization.rs @@ -50,6 +50,13 @@ pub trait ZcashSerialize: Sized { /// In other words, any type implementing `ZcashSerialize` must make illegal /// states unrepresentable. fn zcash_serialize(&self, writer: W) -> Result<(), io::Error>; + + /// Helper function to construct a vec to serialize the current struct into + fn zcash_serialize_to_vec(&self) -> Result, io::Error> { + let mut data = Vec::new(); + self.zcash_serialize(&mut data)?; + Ok(data) + } } /// Consensus-critical serialization for Zcash. @@ -299,6 +306,23 @@ pub trait ReadZcashExt: io::Read { /// Mark all types implementing `Read` as implementing the extension. impl ReadZcashExt for R {} +/// Helper for deserializing more succinctly via type inference +pub trait ZcashDeserializeInto { + /// Deserialize based on type inference + fn zcash_deserialize_into(self) -> Result + where + T: ZcashDeserialize; +} + +impl ZcashDeserializeInto for R { + fn zcash_deserialize_into(self) -> Result + where + T: ZcashDeserialize, + { + T::zcash_deserialize(self) + } +} + #[cfg(test)] #[allow(clippy::unnecessary_operation)] mod tests { diff --git a/zebra-chain/src/transaction/tests.rs b/zebra-chain/src/transaction/tests.rs index 31e9227b7..2c86683a7 100644 --- a/zebra-chain/src/transaction/tests.rs +++ b/zebra-chain/src/transaction/tests.rs @@ -1,7 +1,7 @@ use proptest::{arbitrary::any, collection::vec, option, prelude::*}; use crate::{ - serialization::{ZcashDeserialize, ZcashSerialize}, + serialization::{ZcashDeserialize, ZcashDeserializeInto, ZcashSerialize}, types::LockTime, }; @@ -110,12 +110,8 @@ proptest! { #[test] fn transaction_roundtrip(tx in any::()) { - - let mut data = Vec::new(); - - tx.zcash_serialize(&mut data).expect("tx should serialize"); - - let tx2 = Transaction::zcash_deserialize(&data[..]).expect("randomized tx should deserialize"); + let data = tx.zcash_serialize_to_vec().expect("tx should serialize"); + let tx2 = data.zcash_deserialize_into().expect("randomized tx should deserialize"); prop_assert_eq![tx, tx2]; }