diff --git a/zebra-network/src/lib.rs b/zebra-network/src/lib.rs index 8fa2ce77..92028e05 100644 --- a/zebra-network/src/lib.rs +++ b/zebra-network/src/lib.rs @@ -2,6 +2,10 @@ #![deny(missing_docs)] +#[macro_use] +extern crate failure; + +pub mod serialization; pub mod message; pub mod types; mod constants; diff --git a/zebra-network/src/serialization.rs b/zebra-network/src/serialization.rs new file mode 100644 index 00000000..a89136b8 --- /dev/null +++ b/zebra-network/src/serialization.rs @@ -0,0 +1,133 @@ +//! Serializat + +use std::io; + +use crate::types::{Magic, Version}; + +use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; + +/// A serialization error. +// XXX refine error types -- better to use boxed errors? +#[derive(Fail, Debug)] +pub enum SerializationError { + /// An underlying IO error. + #[fail(display = "io error {}", _0)] + IoError(io::Error), + /// The data to be deserialized was malformed. + #[fail(display = "parse error")] + ParseError, +} + +// Allow upcasting io::Error to SerializationError +impl From for SerializationError { + fn from(e: io::Error) -> Self { + Self::IoError(e) + } +} + +/// Extends [`Write`] with methods for writing Zcash/Bitcoin types. +/// +/// [`Write`]: https://doc.rust-lang.org/std/io/trait.Write.html +pub trait WriteZcashExt: io::Write { + /// Writes a `u64` using the Bitcoin `CompactSize` encoding. + /// + /// # Examples + /// + /// ```rust + /// use zebra_network::serialization::WriteZcashExt; + /// + /// let mut buf = Vec::new(); + /// buf.write_compactsize(0x12).unwrap(); + /// assert_eq!(buf, b"\x12"); + /// + /// let mut buf = Vec::new(); + /// buf.write_compactsize(0xfd).unwrap(); + /// assert_eq!(buf, b"\xfd\xfd\x00"); + /// + /// let mut buf = Vec::new(); + /// buf.write_compactsize(0xaafd).unwrap(); + /// assert_eq!(buf, b"\xfd\xfd\xaa"); + /// + /// let mut buf = Vec::new(); + /// buf.write_compactsize(0xbbaafd).unwrap(); + /// assert_eq!(buf, b"\xfe\xfd\xaa\xbb\x00"); + /// + /// let mut buf = Vec::new(); + /// buf.write_compactsize(0x22ccbbaafd).unwrap(); + /// assert_eq!(buf, b"\xff\xfd\xaa\xbb\xcc\x22\x00\x00\x00"); + /// ``` + #[inline] + fn write_compactsize(&mut self, n: u64) -> io::Result<()> { + match n { + 0x0000_0000..=0x0000_00fc => self.write_u8(n as u8), + 0x0000_00fd..=0x0001_0000 => { + self.write_u8(0xfd)?; + self.write_u16::(n as u16) + } + 0x0001_0000..=0xffff_ffff => { + self.write_u8(0xfe)?; + self.write_u32::(n as u32) + } + _ => { + self.write_u8(0xff)?; + self.write_u64::(n) + } + } + } +} + +/// Mark all types implementing `Write` as implementing the extension. +impl WriteZcashExt for W {} + +/// Extends [`Read`] with methods for writing Zcash/Bitcoin types. +/// +/// [`Read`]: https://doc.rust-lang.org/std/io/trait.Read.html +pub trait ReadZcashExt: io::Read { + /// Reads a `u64` using the Bitcoin `CompactSize` encoding. + /// + /// # Examples + /// + /// ```rust + /// use zebra_network::serialization::ReadZcashExt; + /// + /// use std::io::Cursor; + /// assert_eq!( + /// 0x12, + /// Cursor::new(b"\x12") + /// .read_compactsize().unwrap() + /// ); + /// assert_eq!( + /// 0xfd, + /// Cursor::new(b"\xfd\xfd\x00") + /// .read_compactsize().unwrap() + /// ); + /// assert_eq!( + /// 0xaafd, + /// Cursor::new(b"\xfd\xfd\xaa") + /// .read_compactsize().unwrap() + /// ); + /// assert_eq!( + /// 0xbbaafd, + /// Cursor::new(b"\xfe\xfd\xaa\xbb\x00") + /// .read_compactsize().unwrap() + /// ); + /// assert_eq!( + /// 0x22ccbbaafd, + /// Cursor::new(b"\xff\xfd\xaa\xbb\xcc\x22\x00\x00\x00") + /// .read_compactsize().unwrap() + /// ); + /// ``` + #[inline] + fn read_compactsize(&mut self) -> Result { + let flag_byte = self.read_u8()?; + match flag_byte { + 0xff => Ok(self.read_u64::()?), + 0xfe => Ok(self.read_u32::()? as u64), + 0xfd => Ok(self.read_u16::()? as u64), + n => Ok(n as u64), + } + } +} + +/// Mark all types implementing `Read` as implementing the extension. +impl ReadZcashExt for R {}