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