1//! Serialization and deserialization for Zcash blocks.
23use std::{borrow::Borrow, io};
45use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
6use chrono::{TimeZone, Utc};
78use crate::{
9 block::{header::ZCASH_BLOCK_VERSION, merkle, Block, CountedHeader, Hash, Header},
10 serialization::{
11 CompactSizeMessage, ReadZcashExt, SerializationError, ZcashDeserialize,
12 ZcashDeserializeInto, ZcashSerialize,
13 },
14 work::{difficulty::CompactDifficulty, equihash},
15};
1617/// The maximum size of a Zcash block, in bytes.
18///
19/// Post-Sapling, this is also the maximum size of a transaction
20/// in the Zcash specification. (But since blocks also contain a
21/// block header and transaction count, the maximum size of a
22/// transaction in the chain is approximately 1.5 kB smaller.)
23pub const MAX_BLOCK_BYTES: u64 = 2_000_000;
2425/// Checks if a block header version is valid.
26///
27/// Zebra could encounter a [`Header`] with an invalid version when serializing a block header constructed
28/// in memory with the wrong version in tests or the getblocktemplate RPC.
29///
30/// The getblocktemplate RPC generates a template with version 4. The miner generates the actual block,
31/// and then we deserialize it and do this check.
32///
33/// All other blocks are deserialized when we receive them, and never modified,
34/// so the deserialisation would pick up any errors.
35fn check_version(version: u32) -> Result<(), &'static str> {
36match version {
37// The Zcash specification says that:
38 // "The current and only defined block version number for Zcash is 4."
39 // but this is not actually part of the consensus rules, and in fact
40 // broken mining software created blocks that do not have version 4.
41 // There are approximately 4,000 blocks with version 536870912; this
42 // is the bit-reversal of the value 4, indicating that that mining pool
43 // reversed bit-ordering of the version field. Because the version field
44 // was not properly validated, these blocks were added to the chain.
45 //
46 // The only possible way to work around this is to do a similar hack
47 // as the overwintered field in transaction parsing, which we do here:
48 // treat the high bit (which zcashd interprets as a sign bit) as an
49 // indicator that the version field is meaningful.
50version if version >> 31 != 0 => Err("high bit was set in version field"),
5152// # Consensus
53 //
54 // > The block version number MUST be greater than or equal to 4.
55 //
56 // https://zips.z.cash/protocol/protocol.pdf#blockheader
57version if version < ZCASH_BLOCK_VERSION => Err("version must be at least 4"),
5859_ => Ok(()),
60 }
61}
6263impl ZcashSerialize for Header {
64#[allow(clippy::unwrap_in_result)]
65fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
66 check_version(self.version).map_err(|msg| io::Error::new(io::ErrorKind::Other, msg))?;
6768 writer.write_u32::<LittleEndian>(self.version)?;
69self.previous_block_hash.zcash_serialize(&mut writer)?;
70 writer.write_all(&self.merkle_root.0[..])?;
71 writer.write_all(&self.commitment_bytes[..])?;
72 writer.write_u32::<LittleEndian>(
73self.time
74 .timestamp()
75 .try_into()
76 .expect("deserialized and generated timestamps are u32 values"),
77 )?;
78 writer.write_u32::<LittleEndian>(self.difficulty_threshold.0)?;
79 writer.write_all(&self.nonce[..])?;
80self.solution.zcash_serialize(&mut writer)?;
81Ok(())
82 }
83}
8485impl ZcashDeserialize for Header {
86fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
87let version = reader.read_u32::<LittleEndian>()?;
88 check_version(version).map_err(SerializationError::Parse)?;
8990Ok(Header {
91 version,
92 previous_block_hash: Hash::zcash_deserialize(&mut reader)?,
93 merkle_root: merkle::Root(reader.read_32_bytes()?),
94 commitment_bytes: reader.read_32_bytes()?.into(),
95// This can't panic, because all u32 values are valid `Utc.timestamp`s
96time: Utc
97 .timestamp_opt(reader.read_u32::<LittleEndian>()?.into(), 0)
98 .single()
99 .ok_or(SerializationError::Parse(
100"out-of-range number of seconds and/or invalid nanosecond",
101 ))?,
102 difficulty_threshold: CompactDifficulty(reader.read_u32::<LittleEndian>()?),
103 nonce: reader.read_32_bytes()?.into(),
104 solution: equihash::Solution::zcash_deserialize(reader)?,
105 })
106 }
107}
108109impl ZcashSerialize for CountedHeader {
110#[allow(clippy::unwrap_in_result)]
111fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
112self.header.zcash_serialize(&mut writer)?;
113114// A header-only message has zero transactions in it.
115let transaction_count =
116 CompactSizeMessage::try_from(0).expect("0 is below the message size limit");
117 transaction_count.zcash_serialize(&mut writer)?;
118119Ok(())
120 }
121}
122123impl ZcashDeserialize for CountedHeader {
124fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
125let header = CountedHeader {
126 header: (&mut reader).zcash_deserialize_into()?,
127 };
128129// We ignore the number of transactions in a header-only message,
130 // it should always be zero.
131let _transaction_count: CompactSizeMessage = (&mut reader).zcash_deserialize_into()?;
132133Ok(header)
134 }
135}
136137impl ZcashSerialize for Block {
138fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
139// All block structs are validated when they are parsed.
140 // So we don't need to check MAX_BLOCK_BYTES here, until
141 // we start generating our own blocks (see #483).
142self.header.zcash_serialize(&mut writer)?;
143self.transactions.zcash_serialize(&mut writer)?;
144Ok(())
145 }
146}
147148impl ZcashDeserialize for Block {
149fn zcash_deserialize<R: io::Read>(reader: R) -> Result<Self, SerializationError> {
150// # Consensus
151 //
152 // > The size of a block MUST be less than or equal to 2000000 bytes.
153 //
154 // https://zips.z.cash/protocol/protocol.pdf#blockheader
155 //
156 // If the limit is reached, we'll get an UnexpectedEof error
157let limited_reader = &mut reader.take(MAX_BLOCK_BYTES);
158Ok(Block {
159 header: limited_reader.zcash_deserialize_into()?,
160 transactions: limited_reader.zcash_deserialize_into()?,
161 })
162 }
163}
164165/// A serialized block.
166///
167/// Stores bytes that are guaranteed to be deserializable into a [`Block`].
168#[derive(Clone, Debug, Eq, Hash, PartialEq)]
169pub struct SerializedBlock {
170 bytes: Vec<u8>,
171}
172173/// Build a [`SerializedBlock`] by serializing a block.
174impl<B: Borrow<Block>> From<B> for SerializedBlock {
175fn from(block: B) -> Self {
176 SerializedBlock {
177 bytes: block
178 .borrow()
179 .zcash_serialize_to_vec()
180 .expect("Writing to a `Vec` should never fail"),
181 }
182 }
183}
184185/// Access the serialized bytes of a [`SerializedBlock`].
186impl AsRef<[u8]> for SerializedBlock {
187fn as_ref(&self) -> &[u8] {
188self.bytes.as_ref()
189 }
190}
191192impl From<Vec<u8>> for SerializedBlock {
193fn from(bytes: Vec<u8>) -> Self {
194Self { bytes }
195 }
196}