diff --git a/zebra-network/src/message.rs b/zebra-network/src/message.rs index 8989846b..90ebce5c 100644 --- a/zebra-network/src/message.rs +++ b/zebra-network/src/message.rs @@ -2,11 +2,13 @@ use std::io; +use byteorder::{LittleEndian, WriteBytesExt}; use chrono::{DateTime, Utc}; use zebra_chain::types::Sha256dChecksum; use crate::meta_addr::MetaAddr; +use crate::serialization::{ReadZcashExt, SerializationError, WriteZcashExt, ZcashSerialization}; use crate::types::*; /// A Bitcoin-like network message for the Zcash protocol. @@ -263,14 +265,39 @@ pub enum RejectReason { // Maybe just write some functions and refactor later? impl Message { - /// Serialize `self` into the given writer. - pub fn write(&self, mut writer: W, magic: Magic) -> io::Result<()> { - use byteorder::{LittleEndian, WriteBytesExt}; + /// Write the body of the message into the given writer. This allows writing + /// the message body prior to writing the header, so that the header can + /// contain a checksum of the message body. + fn write_body( + &self, + _writer: W, + _magic: Magic, + _version: Version, + ) -> Result<(), SerializationError> { + unimplemented!() + } + /// Try to deserialize a [`Message`] from the given reader. + pub fn try_read_body( + _reader: R, + _magic: Magic, + _version: Version, + ) -> Result { + unimplemented!() + } +} + +impl ZcashSerialization for Message { + fn write( + &self, + mut writer: W, + magic: Magic, + version: Version, + ) -> Result<(), SerializationError> { // Because the header contains a checksum of // the body data, it must be written first. let mut body = Vec::new(); - self.write_body(&mut body)?; + self.write_body(&mut body, magic, version)?; use Message::*; // Note: because all match arms must have @@ -306,18 +333,17 @@ impl Message { writer.write_all(command)?; writer.write_u32::(body.len() as u32)?; writer.write_all(&Sha256dChecksum::from(&body[..]).0)?; - writer.write_all(&body) + writer.write_all(&body)?; + + Ok(()) } - /// Write the body of the message into the given writer. This allows writing - /// the message body prior to writing the header, so that the header can - /// contain a checksum of the message body. - fn write_body(&self, _writer: W) -> io::Result<()> { - unimplemented!() - } - - /// Try to deserialize a [`Message`] from the given reader. - pub fn try_read(_reader: R) -> Result { + /// Try to read `self` from the given `reader`. + fn try_read( + _reader: R, + _magic: Magic, + _version: Version, + ) -> Result { unimplemented!() } } diff --git a/zebra-network/src/serialization.rs b/zebra-network/src/serialization.rs index a89136b8..0871d0c8 100644 --- a/zebra-network/src/serialization.rs +++ b/zebra-network/src/serialization.rs @@ -131,3 +131,39 @@ pub trait ReadZcashExt: io::Read { /// Mark all types implementing `Read` as implementing the extension. impl ReadZcashExt for R {} + +/// Consensus-critical (de)serialization for Zcash. +/// +/// This trait provides a generic (de)serialization for consensus-critical +/// formats, such as network messages, transactions, blocks, etc. It is intended +/// for use only in consensus-critical contexts; in other contexts, such as +/// internal storage, it would be preferable to use Serde. +/// +/// # Questions +/// +/// ## Should this live here in `zebra-network` or in `zebra-chain`? +/// +/// This is a proxy question for "is this serialization logic required outside of +/// networking contexts", which requires mapping out the "network context" +/// concept more precisely. +/// +/// ## Should the `version` and `magic` parameters always be passed? +/// +/// These are required for, e.g., serializing message headers, but possibly not +/// for serializing transactions? +pub trait ZcashSerialization: Sized { + /// Write `self` to the given `writer` using the canonical format. + fn write( + &self, + mut writer: W, + magic: Magic, + version: Version, + ) -> Result<(), SerializationError>; + + /// Try to read `self` from the given `reader`. + fn try_read( + reader: R, + magic: Magic, + version: Version, + ) -> Result; +}