zebra_chain/serialization/compact_size.rs
1//! Types and serialization for the Bitcoin `CompactSize` format.
2//!
3//! Zebra decodes `CompactSize` fields into two different types,
4//! depending on the consensus rules that apply to that type:
5//! - [`CompactSizeMessage`] for sizes that must be less than the network message limit, and
6//! - [`CompactSize64`] for flags, arbitrary counts, and sizes that span multiple blocks.
7
8use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
9
10use crate::serialization::{
11 SerializationError, ZcashDeserialize, ZcashDeserializeInto, ZcashSerialize,
12 MAX_PROTOCOL_MESSAGE_LEN,
13};
14
15#[cfg(any(test, feature = "proptest-impl"))]
16use proptest_derive::Arbitrary;
17
18/// A CompactSize-encoded field that is limited to [`MAX_PROTOCOL_MESSAGE_LEN`].
19/// Used for sizes or counts of objects that are sent in network messages.
20///
21/// # Security
22///
23/// Deserialized sizes must be validated before being used to allocate memory.
24///
25/// Preallocating vectors using untrusted `CompactSize`s allows memory
26/// denial of service attacks. Valid sizes must be less than
27/// `MAX_PROTOCOL_MESSAGE_LEN / min_serialized_item_bytes` (or a lower limit
28/// specified by the Zcash consensus rules or Bitcoin network protocol).
29///
30/// As a defence-in-depth for memory preallocation attacks,
31/// Zebra rejects sizes greater than the protocol message length limit.
32/// (These sizes should be impossible, because each array items takes at
33/// least one byte.)
34///
35/// # Serialization Examples
36///
37/// ```
38/// use zebra_chain::serialization::{CompactSizeMessage, ZcashSerialize, MAX_PROTOCOL_MESSAGE_LEN};
39/// use std::convert::{TryFrom, TryInto};
40///
41/// let size = CompactSizeMessage::try_from(0x12).unwrap();
42/// let buf = size.zcash_serialize_to_vec().unwrap();
43/// assert_eq!(buf, b"\x12");
44///
45/// let size = CompactSizeMessage::try_from(0xfd).unwrap();
46/// let buf = size.zcash_serialize_to_vec().unwrap();
47/// assert_eq!(buf, b"\xfd\xfd\x00");
48///
49/// let size = CompactSizeMessage::try_from(0xaafd).unwrap();
50/// let buf = size.zcash_serialize_to_vec().unwrap();
51/// assert_eq!(buf, b"\xfd\xfd\xaa");
52///
53/// let max_size: usize = MAX_PROTOCOL_MESSAGE_LEN.try_into().unwrap();
54/// let size = CompactSizeMessage::try_from(max_size).unwrap();
55/// let buf = size.zcash_serialize_to_vec().unwrap();
56/// assert_eq!(buf, b"\xfe\x00\x00\x20\x00");
57/// ```
58///
59/// [`CompactSizeMessage`]s greater than the maximum network message length
60/// can not be constructed:
61/// ```
62/// # use zebra_chain::serialization::{CompactSizeMessage, MAX_PROTOCOL_MESSAGE_LEN};
63/// # use std::convert::{TryFrom, TryInto};
64/// # let max_size: usize = MAX_PROTOCOL_MESSAGE_LEN.try_into().unwrap();
65/// assert!(CompactSizeMessage::try_from(max_size + 1).is_err());
66///
67/// assert!(CompactSizeMessage::try_from(0xbbaafd).is_err());
68///
69/// assert!(CompactSizeMessage::try_from(0x22ccbbaafd).is_err());
70/// ```
71///
72/// # Deserialization Examples
73///
74/// ```
75/// use zebra_chain::serialization::{CompactSizeMessage, ZcashDeserializeInto, MAX_PROTOCOL_MESSAGE_LEN};
76/// use std::{convert::{TryFrom, TryInto}, io::Cursor};
77///
78/// assert_eq!(
79/// CompactSizeMessage::try_from(0x12).unwrap(),
80/// Cursor::new(b"\x12").zcash_deserialize_into().unwrap(),
81/// );
82///
83/// assert_eq!(
84/// CompactSizeMessage::try_from(0xfd).unwrap(),
85/// Cursor::new(b"\xfd\xfd\x00").zcash_deserialize_into().unwrap(),
86/// );
87///
88/// assert_eq!(
89/// CompactSizeMessage::try_from(0xaafd).unwrap(),
90/// Cursor::new(b"\xfd\xfd\xaa").zcash_deserialize_into().unwrap(),
91/// );
92///
93/// let max_size: usize = MAX_PROTOCOL_MESSAGE_LEN.try_into().unwrap();
94/// assert_eq!(
95/// CompactSizeMessage::try_from(max_size).unwrap(),
96/// Cursor::new(b"\xfe\x00\x00\x20\x00").zcash_deserialize_into().unwrap(),
97/// );
98/// ```
99///
100/// [`CompactSizeMessage`]s greater than the maximum network message length are invalid.
101/// They return a [`SerializationError::Parse`]:
102/// ```
103/// # use zebra_chain::serialization::{CompactSizeMessage, ZcashDeserialize, MAX_PROTOCOL_MESSAGE_LEN};
104/// # use std::{convert::TryFrom, io::Cursor};
105/// let max_size_plus_one = Cursor::new(b"\xfe\x01\x00\x20\x00");
106/// assert!(CompactSizeMessage::zcash_deserialize(max_size_plus_one).is_err());
107///
108/// let bytes = Cursor::new(b"\xfe\xfd\xaa\xbb\x00");
109/// assert!(CompactSizeMessage::zcash_deserialize(bytes).is_err());
110///
111/// let bytes = Cursor::new(b"\xff\xfd\xaa\xbb\xcc\x22\x00\x00\x00");
112/// assert!(CompactSizeMessage::zcash_deserialize(bytes).is_err());
113/// ```
114#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
115pub struct CompactSizeMessage(
116 /// The number of items in a Zcash message.
117 ///
118 /// This field is private to enforce the [`MAX_PROTOCOL_MESSAGE_LEN`] limit.
119 u32,
120);
121
122/// An arbitrary CompactSize-encoded field.
123/// Used for flags, arbitrary counts, and sizes that span multiple blocks.
124///
125/// # Security
126///
127/// Deserialized sizes must be validated before being used to allocate memory.
128///
129/// Most allocation sizes should use [`CompactSizeMessage`],
130/// because it is limited to [`MAX_PROTOCOL_MESSAGE_LEN`].
131///
132/// # Serialization Examples
133///
134/// ```
135/// use zebra_chain::serialization::{CompactSize64, ZcashSerialize, MAX_PROTOCOL_MESSAGE_LEN};
136/// use std::convert::TryFrom;
137///
138/// let size = CompactSize64::from(0x12);
139/// let buf = size.zcash_serialize_to_vec().unwrap();
140/// assert_eq!(buf, b"\x12");
141///
142/// let size = CompactSize64::from(0xfd);
143/// let buf = size.zcash_serialize_to_vec().unwrap();
144/// assert_eq!(buf, b"\xfd\xfd\x00");
145///
146/// let size = CompactSize64::from(0xaafd);
147/// let buf = size.zcash_serialize_to_vec().unwrap();
148/// assert_eq!(buf, b"\xfd\xfd\xaa");
149///
150/// let max_size = u64::try_from(MAX_PROTOCOL_MESSAGE_LEN).unwrap();
151/// let size = CompactSize64::from(max_size);
152/// let buf = size.zcash_serialize_to_vec().unwrap();
153/// assert_eq!(buf, b"\xfe\x00\x00\x20\x00");
154///
155/// let size = CompactSize64::from(max_size + 1);
156/// let buf = size.zcash_serialize_to_vec().unwrap();
157/// assert_eq!(buf, b"\xfe\x01\x00\x20\x00");
158///
159/// let size = CompactSize64::from(0xbbaafd);
160/// let buf = size.zcash_serialize_to_vec().unwrap();
161/// assert_eq!(buf, b"\xfe\xfd\xaa\xbb\x00");
162///
163/// let size = CompactSize64::from(0x22ccbbaafd);
164/// let buf = size.zcash_serialize_to_vec().unwrap();
165/// assert_eq!(buf, b"\xff\xfd\xaa\xbb\xcc\x22\x00\x00\x00");
166/// ```
167///
168/// # Deserialization Examples
169///
170/// ```
171/// use zebra_chain::serialization::{CompactSize64, ZcashDeserializeInto, MAX_PROTOCOL_MESSAGE_LEN};
172/// use std::{convert::TryFrom, io::Cursor};
173///
174/// assert_eq!(
175/// CompactSize64::from(0x12),
176/// Cursor::new(b"\x12").zcash_deserialize_into().unwrap(),
177/// );
178///
179/// assert_eq!(
180/// CompactSize64::from(0xfd),
181/// Cursor::new(b"\xfd\xfd\x00").zcash_deserialize_into().unwrap(),
182/// );
183///
184/// assert_eq!(
185/// CompactSize64::from(0xaafd),
186/// Cursor::new(b"\xfd\xfd\xaa").zcash_deserialize_into().unwrap(),
187/// );
188///
189/// let max_size = u64::try_from(MAX_PROTOCOL_MESSAGE_LEN).unwrap();
190/// assert_eq!(
191/// CompactSize64::from(max_size),
192/// Cursor::new(b"\xfe\x00\x00\x20\x00").zcash_deserialize_into().unwrap(),
193/// );
194///
195/// assert_eq!(
196/// CompactSize64::from(max_size + 1),
197/// Cursor::new(b"\xfe\x01\x00\x20\x00").zcash_deserialize_into().unwrap(),
198/// );
199///
200/// assert_eq!(
201/// CompactSize64::from(0xbbaafd),
202/// Cursor::new(b"\xfe\xfd\xaa\xbb\x00").zcash_deserialize_into().unwrap(),
203/// );
204///
205/// assert_eq!(
206/// CompactSize64::from(0x22ccbbaafd),
207/// Cursor::new(b"\xff\xfd\xaa\xbb\xcc\x22\x00\x00\x00").zcash_deserialize_into().unwrap(),
208/// );
209///```
210#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
211#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))]
212pub struct CompactSize64(
213 /// A numeric value.
214 ///
215 /// This field is private for consistency with [`CompactSizeMessage`].
216 u64,
217);
218
219// `CompactSizeMessage` are item counts, so we expect them to be used with `usize`
220// `CompactSize64` are arbitrary integers, so we expect it to be used with `u64`
221//
222// We don't implement conversions between CompactSizeMessage and CompactSize64,
223// because we want to distinguish fields with different numeric constraints.
224//
225// We don't implement From<CompactSizeMessage> for u16 or u8,
226// because we want all values to go through the same code paths.
227// (And we don't want to accidentally truncate using `as`.)
228// It would also make integer literal type inference fail.
229//
230// We don't implement `PartialEq` or `Ord` with integers,
231// because it makes type inference fail.
232
233impl TryFrom<usize> for CompactSizeMessage {
234 type Error = SerializationError;
235
236 #[inline]
237 #[allow(clippy::unwrap_in_result)]
238 fn try_from(size: usize) -> Result<Self, Self::Error> {
239 use SerializationError::Parse;
240
241 let size: u32 = size.try_into()?;
242
243 // # Security
244 // Defence-in-depth for memory DoS via preallocation.
245 if size
246 > MAX_PROTOCOL_MESSAGE_LEN
247 .try_into()
248 .expect("MAX_PROTOCOL_MESSAGE_LEN fits in u32")
249 {
250 // This could be invalid data from peers, so we return a parse error.
251 Err(Parse("CompactSize larger than protocol message limit"))?;
252 }
253
254 Ok(CompactSizeMessage(size))
255 }
256}
257
258impl From<CompactSizeMessage> for usize {
259 #[inline]
260 fn from(size: CompactSizeMessage) -> Self {
261 size.0.try_into().expect("u32 fits in usize")
262 }
263}
264
265impl From<CompactSize64> for u64 {
266 #[inline]
267 fn from(size: CompactSize64) -> Self {
268 size.0
269 }
270}
271
272impl From<u64> for CompactSize64 {
273 #[inline]
274 fn from(size: u64) -> Self {
275 CompactSize64(size)
276 }
277}
278
279impl ZcashSerialize for CompactSizeMessage {
280 /// Serialize a CompactSizeMessage into the Zcash consensus-critical format.
281 ///
282 /// # Panics
283 ///
284 /// If the value exceeds `MAX_PROTOCOL_MESSAGE_LEN`.
285 #[inline]
286 #[allow(clippy::unwrap_in_result)]
287 fn zcash_serialize<W: std::io::Write>(&self, writer: W) -> Result<(), std::io::Error> {
288 // # Security
289 // Defence-in-depth for memory DoS via preallocation.
290 //
291 // This is validated data inside Zebra, so we panic.
292 assert!(
293 self.0
294 <= MAX_PROTOCOL_MESSAGE_LEN
295 .try_into()
296 .expect("usize fits in u64"),
297 "CompactSize larger than protocol message limit"
298 );
299
300 // Use the same serialization format as CompactSize64.
301 let size: u64 = self.0.into();
302 CompactSize64(size).zcash_serialize(writer)
303 }
304}
305
306impl ZcashDeserialize for CompactSizeMessage {
307 #[inline]
308 fn zcash_deserialize<R: std::io::Read>(reader: R) -> Result<Self, SerializationError> {
309 // Use the same deserialization format as CompactSize64.
310 let size: CompactSize64 = reader.zcash_deserialize_into()?;
311
312 let size: usize = size.0.try_into()?;
313 size.try_into()
314 }
315}
316
317impl ZcashSerialize for CompactSize64 {
318 #[inline]
319 fn zcash_serialize<W: std::io::Write>(&self, mut writer: W) -> Result<(), std::io::Error> {
320 let n = self.0;
321 match n {
322 0x00..=0xfc => writer.write_u8(n as u8),
323 0x00fd..=0xffff => {
324 writer.write_u8(0xfd)?;
325 writer.write_u16::<LittleEndian>(n as u16)
326 }
327 0x0001_0000..=0xffff_ffff => {
328 writer.write_u8(0xfe)?;
329 writer.write_u32::<LittleEndian>(n as u32)
330 }
331 _ => {
332 writer.write_u8(0xff)?;
333 writer.write_u64::<LittleEndian>(n)
334 }
335 }
336 }
337}
338
339impl ZcashDeserialize for CompactSize64 {
340 #[inline]
341 fn zcash_deserialize<R: std::io::Read>(mut reader: R) -> Result<Self, SerializationError> {
342 use SerializationError::Parse;
343
344 let flag_byte = reader.read_u8()?;
345 let size = match flag_byte {
346 n @ 0x00..=0xfc => Ok(n as u64),
347 0xfd => match reader.read_u16::<LittleEndian>()? {
348 n @ 0x00fd..=u16::MAX => Ok(n as u64),
349 _ => Err(Parse("non-canonical CompactSize")),
350 },
351 0xfe => match reader.read_u32::<LittleEndian>()? {
352 n @ 0x0001_0000..=u32::MAX => Ok(n as u64),
353 _ => Err(Parse("non-canonical CompactSize")),
354 },
355 0xff => match reader.read_u64::<LittleEndian>()? {
356 n @ 0x0000_0001_0000_0000..=u64::MAX => Ok(n),
357 _ => Err(Parse("non-canonical CompactSize")),
358 },
359 }?;
360
361 Ok(CompactSize64(size))
362 }
363}