diff --git a/zebra-network/src/protocol/codec.rs b/zebra-network/src/protocol/codec.rs index 327f4ab1..28f08393 100644 --- a/zebra-network/src/protocol/codec.rs +++ b/zebra-network/src/protocol/codec.rs @@ -9,6 +9,7 @@ use failure::Error; use tokio::codec::{Decoder, Encoder}; use zebra_chain::{ + block::BlockHeader, serialization::{ReadZcashExt, WriteZcashExt, ZcashDeserialize, ZcashSerialize}, types::{BlockHeight, Sha256dChecksum}, }; @@ -420,9 +421,29 @@ impl Codec { bail!("unimplemented message type") } - fn read_headers(&self, mut _reader: R) -> Result { - trace!("headers"); - bail!("unimplemented message type") + /// Deserialize a `headers` message. + /// + /// See [Zcash block header] for the enumeration of these fields. + /// + /// [Zcash block header](https://zips.z.cash/protocol/protocol.pdf#page=84) + fn read_headers(&self, mut reader: R) -> Result { + let count = reader.read_compactsize()? as usize; + // Preallocate a buffer, performing a single allocation in the honest + // case. Although the size of the recieved data buffer is bounded by the + // codec's max_len field, it's still possible for someone to send a + // short message with a large count field, so if we naively trust + // the count field we could be tricked into preallocating a large + // buffer. Instead, calculate the maximum count for a valid message from + // the codec's max_len using ENCODED_HEADER_SIZE. + const ENCODED_HEADER_SIZE: usize = 4 + 32 + 32 + 32 + 4 + 4 + 32 + 3 + 1344; + let max_count = self.builder.max_len / ENCODED_HEADER_SIZE; + let mut headers = Vec::with_capacity(std::cmp::min(count, max_count)); + + for _ in 0..count { + headers.push(BlockHeader::zcash_deserialize(&mut reader)?); + } + + Ok(Message::Headers(headers)) } fn read_getheaders(&self, mut _reader: R) -> Result { diff --git a/zebra-network/src/protocol/message.rs b/zebra-network/src/protocol/message.rs index 290c59b0..3d0d4b12 100644 --- a/zebra-network/src/protocol/message.rs +++ b/zebra-network/src/protocol/message.rs @@ -4,7 +4,7 @@ use std::net; use chrono::{DateTime, Utc}; -use zebra_chain::block::Block; +use zebra_chain::block::{Block, BlockHeader}; use zebra_chain::{transaction::Transaction, types::BlockHeight}; use crate::meta_addr::MetaAddr; @@ -149,8 +149,14 @@ pub enum Message { /// A `headers` message. /// + /// Returns block headers in response to a getheaders packet. + /// /// [Bitcoin reference](https://en.bitcoin.it/wiki/Protocol_documentation#headers) - Headers {/* XXX add fields */}, + // Note that the block headers in this packet include a + // transaction count (a var_int, so there can be more than 81 + // bytes per header) as opposed to the block headers that are + // hashed by miners. + Headers(Vec), /// A `getheaders` message. /// @@ -159,6 +165,10 @@ pub enum Message { /// An `inv` message. /// + /// Allows a node to advertise its knowledge of one or more + /// objects. It can be received unsolicited, or in reply to + /// `getblocks`. + /// /// [Bitcoin reference](https://en.bitcoin.it/wiki/Protocol_documentation#inv) // XXX the bitcoin reference above suggests this can be 1.8 MB in bitcoin -- maybe // larger in Zcash, since Zcash objects could be bigger (?) -- does this tilt towards @@ -167,9 +177,9 @@ pub enum Message { /// A `getdata` message. /// - /// `getdata` is used in response to `inv`, to retrieve the content of - /// a specific object, and is usually sent after receiving an `inv` - /// packet, after filtering known elements. + /// `getdata` is used in response to `inv`, to retrieve the + /// content of a specific object, and is usually sent after + /// receiving an `inv` packet, after filtering known elements. /// /// [Bitcoin reference](https://en.bitcoin.it/wiki/Protocol_documentation#getdata) GetData(Vec),