Add cool helpers for using zcash_serialize / zcash_deserialize (#586)

Co-authored-by: Dimitris Apostolou <dimitris.apostolou@icloud.com>
This commit is contained in:
Jane Lusby 2020-07-09 12:29:31 -07:00 committed by GitHub
parent 8b72781fe1
commit 0474a79669
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 75 additions and 59 deletions

View File

@ -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<R: io::Read>(reader: R) -> Result<Self, SerializationError> {
// 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()?,
})
}
}

View File

@ -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::<BlockHeader>()
.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::<Block>()
.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::<Block>()
.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::<Block>()
.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::<Block>()
.expect("block test vector should deserialize");
}
@ -193,39 +194,32 @@ proptest! {
#[test]
fn blockheaderhash_roundtrip(hash in any::<BlockHeaderHash>()) {
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::<BlockHeader>()) {
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::<Block>()) {
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::<Block>()
.expect_err("blocks larger than the maximum size should fail");
match serialization_err {
SerializationError::Io(io_err) => {

View File

@ -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<Self>;
}
proptest! {
#[test]
fn equihash_solution_roundtrip(solution in any::<EquihashSolution>()) {
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::<EquihashSolution>())| {
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::<EquihashSolution>()
.expect("Test vector EquihashSolution should deserialize");
let mut data = Vec::new();

View File

@ -50,6 +50,13 @@ pub trait ZcashSerialize: Sized {
/// In other words, any type implementing `ZcashSerialize` must make illegal
/// states unrepresentable.
fn zcash_serialize<W: io::Write>(&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<Vec<u8>, 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<R: io::Read + ?Sized> ReadZcashExt for R {}
/// Helper for deserializing more succinctly via type inference
pub trait ZcashDeserializeInto {
/// Deserialize based on type inference
fn zcash_deserialize_into<T>(self) -> Result<T, SerializationError>
where
T: ZcashDeserialize;
}
impl<R: io::Read> ZcashDeserializeInto for R {
fn zcash_deserialize_into<T>(self) -> Result<T, SerializationError>
where
T: ZcashDeserialize,
{
T::zcash_deserialize(self)
}
}
#[cfg(test)]
#[allow(clippy::unnecessary_operation)]
mod tests {

View File

@ -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::<Transaction>()) {
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];
}