zebra_chain/block/
serialize.rs

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