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;