zebra_chain/serialization/
zcash_deserialize.rs

1//! Converting bytes into Zcash consensus-critical data structures.
2
3use std::{io, net::Ipv6Addr, sync::Arc};
4
5use super::{AtLeastOne, CompactSizeMessage, SerializationError, MAX_PROTOCOL_MESSAGE_LEN};
6
7/// Consensus-critical deserialization for Zcash.
8///
9/// This trait provides a generic deserialization for consensus-critical
10/// formats, such as network messages, transactions, blocks, etc.
11///
12/// It is intended for use only for consensus-critical formats.
13/// Internal deserialization can freely use `serde`, or any other format.
14pub trait ZcashDeserialize: Sized {
15    /// Try to read `self` from the given `reader`.
16    ///
17    /// This function has a `zcash_` prefix to alert the reader that the
18    /// serialization in use is consensus-critical serialization, rather than
19    /// some other kind of serialization.
20    fn zcash_deserialize<R: io::Read>(reader: R) -> Result<Self, SerializationError>;
21}
22
23/// Deserialize a `Vec`, where the number of items is set by a CompactSize
24/// prefix in the data. This is the most common format in Zcash.
25///
26/// See `zcash_deserialize_external_count` for more details, and usage
27/// information.
28impl<T: ZcashDeserialize + TrustedPreallocate> ZcashDeserialize for Vec<T> {
29    fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
30        let len: CompactSizeMessage = (&mut reader).zcash_deserialize_into()?;
31        zcash_deserialize_external_count(len.into(), reader)
32    }
33}
34
35/// Deserialize an `AtLeastOne` vector, where the number of items is set by a
36/// CompactSize prefix in the data. This is the most common format in Zcash.
37impl<T: ZcashDeserialize + TrustedPreallocate> ZcashDeserialize for AtLeastOne<T> {
38    fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
39        let v: Vec<T> = (&mut reader).zcash_deserialize_into()?;
40        v.try_into()
41    }
42}
43
44/// Implement ZcashDeserialize for `Vec<u8>` directly instead of using the blanket Vec implementation
45///
46/// This allows us to optimize the inner loop into a single call to `read_exact()`
47/// Note that we don't implement TrustedPreallocate for u8.
48/// This allows the optimization without relying on specialization.
49impl ZcashDeserialize for Vec<u8> {
50    fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
51        let len: CompactSizeMessage = (&mut reader).zcash_deserialize_into()?;
52        zcash_deserialize_bytes_external_count(len.into(), reader)
53    }
54}
55
56/// Deserialize a `Vec` containing `external_count` items.
57///
58/// In Zcash, most arrays are stored as a CompactSize, followed by that number
59/// of items of type `T`. But in `Transaction::V5`, some types are serialized as
60/// multiple arrays in different locations, with a single CompactSize before the
61/// first array.
62///
63/// ## Usage
64///
65/// Use `zcash_deserialize_external_count` when the array count is determined by
66/// other data, or a consensus rule.
67///
68/// Use `Vec::zcash_deserialize` for data that contains CompactSize count,
69/// followed by the data array.
70///
71/// For example, when a single count applies to multiple arrays:
72/// 1. Use `Vec::zcash_deserialize` for the array that has a data count.
73/// 2. Use `zcash_deserialize_external_count` for the arrays with no count in the
74///    data, passing the length of the first array.
75///
76/// This function has a `zcash_` prefix to alert the reader that the
77/// serialization in use is consensus-critical serialization, rather than
78/// some other kind of serialization.
79pub fn zcash_deserialize_external_count<R: io::Read, T: ZcashDeserialize + TrustedPreallocate>(
80    external_count: usize,
81    mut reader: R,
82) -> Result<Vec<T>, SerializationError> {
83    match u64::try_from(external_count) {
84        Ok(external_count) if external_count > T::max_allocation() => {
85            return Err(SerializationError::Parse(
86                "Vector longer than max_allocation",
87            ))
88        }
89        Ok(_) => {}
90        // As of 2021, usize is less than or equal to 64 bits on all (or almost all?) supported Rust platforms.
91        // So in practice this error is impossible. (But the check is required, because Rust is future-proof
92        // for 128 bit memory spaces.)
93        Err(_) => return Err(SerializationError::Parse("Vector longer than u64::MAX")),
94    }
95    let mut vec = Vec::with_capacity(external_count);
96    for _ in 0..external_count {
97        vec.push(T::zcash_deserialize(&mut reader)?);
98    }
99    Ok(vec)
100}
101
102/// `zcash_deserialize_external_count`, specialised for raw bytes.
103///
104/// This allows us to optimize the inner loop into a single call to `read_exact()`.
105///
106/// This function has a `zcash_` prefix to alert the reader that the
107/// serialization in use is consensus-critical serialization, rather than
108/// some other kind of serialization.
109pub fn zcash_deserialize_bytes_external_count<R: io::Read>(
110    external_count: usize,
111    mut reader: R,
112) -> Result<Vec<u8>, SerializationError> {
113    if external_count > MAX_U8_ALLOCATION {
114        return Err(SerializationError::Parse(
115            "Byte vector longer than MAX_U8_ALLOCATION",
116        ));
117    }
118    let mut vec = vec![0u8; external_count];
119    reader.read_exact(&mut vec)?;
120    Ok(vec)
121}
122
123/// `zcash_deserialize_external_count`, specialised for [`String`].
124/// The external count is in bytes. (Not UTF-8 characters.)
125///
126/// This allows us to optimize the inner loop into a single call to `read_exact()`.
127///
128/// This function has a `zcash_` prefix to alert the reader that the
129/// serialization in use is consensus-critical serialization, rather than
130/// some other kind of serialization.
131pub fn zcash_deserialize_string_external_count<R: io::Read>(
132    external_byte_count: usize,
133    reader: R,
134) -> Result<String, SerializationError> {
135    let bytes = zcash_deserialize_bytes_external_count(external_byte_count, reader)?;
136
137    String::from_utf8(bytes).map_err(|_| SerializationError::Parse("invalid utf-8"))
138}
139
140/// Read a Bitcoin-encoded UTF-8 string.
141impl ZcashDeserialize for String {
142    fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
143        let byte_count: CompactSizeMessage = (&mut reader).zcash_deserialize_into()?;
144        zcash_deserialize_string_external_count(byte_count.into(), reader)
145    }
146}
147
148// We don't impl ZcashDeserialize for Ipv4Addr or SocketAddrs,
149// because the IPv4 and port formats are different in addr (v1) and addrv2 messages.
150
151/// Read a Bitcoin-encoded IPv6 address.
152impl ZcashDeserialize for Ipv6Addr {
153    fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
154        let mut ipv6_addr = [0u8; 16];
155        reader.read_exact(&mut ipv6_addr)?;
156
157        Ok(Ipv6Addr::from(ipv6_addr))
158    }
159}
160
161/// Helper for deserializing more succinctly via type inference
162pub trait ZcashDeserializeInto {
163    /// Deserialize based on type inference
164    fn zcash_deserialize_into<T>(self) -> Result<T, SerializationError>
165    where
166        T: ZcashDeserialize;
167}
168
169impl<R: io::Read> ZcashDeserializeInto for R {
170    fn zcash_deserialize_into<T>(self) -> Result<T, SerializationError>
171    where
172        T: ZcashDeserialize,
173    {
174        T::zcash_deserialize(self)
175    }
176}
177
178/// Blind preallocation of a `Vec<T: TrustedPreallocate>` is based on a bounded length. This is in contrast
179/// to blind preallocation of a generic `Vec<T>`, which is a DOS vector.
180///
181/// The max_allocation() function provides a loose upper bound on the size of the `Vec<T: TrustedPreallocate>`
182/// which can possibly be received from an honest peer. If this limit is too low, Zebra may reject valid messages.
183/// In the worst case, setting the lower bound too low could cause Zebra to fall out of consensus by rejecting all messages containing a valid block.
184pub trait TrustedPreallocate {
185    /// Provides a ***loose upper bound*** on the size of the `Vec<T: TrustedPreallocate>`
186    /// which can possibly be received from an honest peer.
187    fn max_allocation() -> u64;
188}
189
190impl<T> TrustedPreallocate for Arc<T>
191where
192    T: TrustedPreallocate,
193{
194    fn max_allocation() -> u64 {
195        T::max_allocation()
196    }
197}
198
199/// The length of the longest valid `Vec<u8>` that can be received over the network
200///
201/// It takes 5 bytes to encode a CompactSize representing any number netween 2^16 and (2^32 - 1)
202/// MAX_PROTOCOL_MESSAGE_LEN is ~2^21, so the largest `Vec<u8>` that can be received from an honest peer is
203/// (MAX_PROTOCOL_MESSAGE_LEN - 5);
204pub(crate) const MAX_U8_ALLOCATION: usize = MAX_PROTOCOL_MESSAGE_LEN - 5;