use std::io;
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use crate::{
block::{self, merkle::AuthDataRoot},
ironwood, orchard, sapling,
serialization::{SerializationError, ZcashDeserialize, ZcashSerialize},
};
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct BlockCommitmentRoots {
pub height: block::Height,
pub sapling_root: sapling::tree::Root,
pub orchard_root: orchard::tree::Root,
pub ironwood_root: ironwood::tree::Root,
pub sapling_tx: u64,
pub orchard_tx: u64,
pub ironwood_tx: u64,
pub auth_data_root: AuthDataRoot,
}
impl ZcashSerialize for BlockCommitmentRoots {
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
writer.write_u32::<LittleEndian>(self.height.0)?;
self.sapling_root.zcash_serialize(&mut writer)?;
self.orchard_root.zcash_serialize(&mut writer)?;
self.ironwood_root.zcash_serialize(&mut writer)?;
writer.write_u64::<LittleEndian>(self.sapling_tx)?;
writer.write_u64::<LittleEndian>(self.orchard_tx)?;
writer.write_u64::<LittleEndian>(self.ironwood_tx)?;
writer.write_all(&<[u8; 32]>::from(self.auth_data_root))?;
Ok(())
}
}
impl ZcashDeserialize for BlockCommitmentRoots {
fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
let height = block::Height(reader.read_u32::<LittleEndian>()?);
let sapling_root = sapling::tree::Root::zcash_deserialize(&mut reader)?;
let orchard_root = orchard::tree::Root::zcash_deserialize(&mut reader)?;
let ironwood_root = ironwood::tree::Root::zcash_deserialize(&mut reader)?;
let sapling_tx = reader.read_u64::<LittleEndian>()?;
let orchard_tx = reader.read_u64::<LittleEndian>()?;
let ironwood_tx = reader.read_u64::<LittleEndian>()?;
let mut auth_data_root = [0u8; 32];
reader.read_exact(&mut auth_data_root)?;
let auth_data_root = AuthDataRoot::from(auth_data_root);
Ok(BlockCommitmentRoots {
height,
sapling_root,
orchard_root,
ironwood_root,
sapling_tx,
orchard_tx,
ironwood_tx,
auth_data_root,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::serialization::ZcashDeserializeInto;
#[test]
fn block_commitment_roots_round_trip() {
let roots = BlockCommitmentRoots {
height: block::Height(1_687_200),
sapling_root: sapling::tree::NoteCommitmentTree::default().root(),
orchard_root: orchard::tree::NoteCommitmentTree::default().root(),
auth_data_root: AuthDataRoot::from([7u8; 32]),
ironwood_root: ironwood::tree::NoteCommitmentTree::default().root(),
sapling_tx: 3,
orchard_tx: 5,
ironwood_tx: 7,
};
let bytes = roots
.zcash_serialize_to_vec()
.expect("serialization to a vec does not fail");
let parsed: BlockCommitmentRoots = bytes
.zcash_deserialize_into()
.expect("round-trips back to the original");
assert_eq!(
parsed, roots,
"BlockCommitmentRoots round-trips on the wire"
);
}
}