From a81f709bf4f8d91408024ce8e76d9a7cd76c6b32 Mon Sep 17 00:00:00 2001 From: Deirdre Connolly Date: Thu, 3 Oct 2019 18:28:47 -0400 Subject: [PATCH 01/16] Support a response message --- zebra-network/src/protocol/codec.rs | 27 ++++++++++++++++++++++++--- zebra-network/src/protocol/message.rs | 20 +++++++++++++++----- 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/zebra-network/src/protocol/codec.rs b/zebra-network/src/protocol/codec.rs index 327f4ab11ff..28f0839353d 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 290c59b0b8a..3d0d4b12b61 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), From 212d46e835abd5211ecea794d22e474250c53781 Mon Sep 17 00:00:00 2001 From: Deirdre Connolly Date: Thu, 3 Oct 2019 19:59:39 -0400 Subject: [PATCH 02/16] Codec::read_tx() --- zebra-network/src/protocol/codec.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/zebra-network/src/protocol/codec.rs b/zebra-network/src/protocol/codec.rs index 28f0839353d..bb815446dcf 100644 --- a/zebra-network/src/protocol/codec.rs +++ b/zebra-network/src/protocol/codec.rs @@ -11,6 +11,7 @@ use tokio::codec::{Decoder, Encoder}; use zebra_chain::{ block::BlockHeader, serialization::{ReadZcashExt, WriteZcashExt, ZcashDeserialize, ZcashSerialize}, + transaction::Transaction, types::{BlockHeight, Sha256dChecksum}, }; @@ -485,9 +486,12 @@ impl Codec { bail!("unimplemented message type") } - fn read_tx(&self, mut _reader: R) -> Result { - trace!("tx"); - bail!("unimplemented message type") + fn read_tx(&self, mut reader: R) -> Result { + Ok(Message::Tx { + version: Version(reader.read_u32::()?), + + transaction: Transaction::zcash_deserialize(&mut reader)?, + }) } fn read_mempool(&self, mut _reader: R) -> Result { From 4b2e540736d21ebb2ab20c3fb383beb544a2f21c Mon Sep 17 00:00:00 2001 From: Deirdre Connolly Date: Thu, 3 Oct 2019 21:57:29 -0400 Subject: [PATCH 03/16] Abstract the common case of a message with a Vec --- zebra-network/src/protocol/codec.rs | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/zebra-network/src/protocol/codec.rs b/zebra-network/src/protocol/codec.rs index bb815446dcf..9cdaad25150 100644 --- a/zebra-network/src/protocol/codec.rs +++ b/zebra-network/src/protocol/codec.rs @@ -17,7 +17,7 @@ use zebra_chain::{ use crate::{constants, Network}; -use super::{message::Message, types::*}; +use super::{inv::InventoryHash, message::Message, types::*}; /// The length of a Bitcoin message header. const HEADER_LEN: usize = 24usize; @@ -452,9 +452,10 @@ impl Codec { bail!("unimplemented message type") } - fn read_inv(&self, mut reader: R) -> Result { - use super::inv::InventoryHash; - + fn _read_generic_inventory_hash_vector( + &self, + mut reader: R, + ) -> Result, Error> { 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 @@ -473,17 +474,22 @@ impl Codec { hashes.push(InventoryHash::zcash_deserialize(&mut reader)?); } + return Ok(hashes); + } + + fn read_inv(&self, reader: R) -> Result { + let hashes = self._read_generic_inventory_hash_vector(reader)?; Ok(Message::Inv(hashes)) } - fn read_getdata(&self, mut _reader: R) -> Result { - trace!("getdata"); - bail!("unimplemented message type") + fn read_getdata(&self, reader: R) -> Result { + let hashes = self._read_generic_inventory_hash_vector(reader)?; + Ok(Message::GetData(hashes)) } - fn read_notfound(&self, mut _reader: R) -> Result { - trace!("notfound"); - bail!("unimplemented message type") + fn read_notfound(&self, reader: R) -> Result { + let hashes = self._read_generic_inventory_hash_vector(reader)?; + Ok(Message::GetData(hashes)) } fn read_tx(&self, mut reader: R) -> Result { From d8c1053d2b455b982b98bbe65dc376fe16d6b54e Mon Sep 17 00:00:00 2001 From: Deirdre Connolly Date: Fri, 4 Oct 2019 01:45:59 -0400 Subject: [PATCH 04/16] Parse GetBlock messages --- zebra-network/src/protocol/codec.rs | 25 ++++++++++++++++----- zebra-network/src/protocol/message.rs | 32 +++++++++++++++++++++++++-- 2 files changed, 50 insertions(+), 7 deletions(-) diff --git a/zebra-network/src/protocol/codec.rs b/zebra-network/src/protocol/codec.rs index 9cdaad25150..3e73a8cfb79 100644 --- a/zebra-network/src/protocol/codec.rs +++ b/zebra-network/src/protocol/codec.rs @@ -9,7 +9,7 @@ use failure::Error; use tokio::codec::{Decoder, Encoder}; use zebra_chain::{ - block::BlockHeader, + block::{BlockHeader, BlockHeaderHash}, serialization::{ReadZcashExt, WriteZcashExt, ZcashDeserialize, ZcashSerialize}, transaction::Transaction, types::{BlockHeight, Sha256dChecksum}, @@ -417,9 +417,24 @@ impl Codec { bail!("unimplemented message type") } - fn read_getblocks(&self, mut _reader: R) -> Result { - trace!("getblocks"); - bail!("unimplemented message type") + fn read_getblocks(&self, mut reader: R) -> Result { + let version = Version(reader.read_u32::()?); + + let count = reader.read_compactsize()? as usize; + let max_count = self.builder.max_len / 32; + let mut block_locator_hashes = Vec::with_capacity(std::cmp::min(count, max_count)); + + for _ in 0..count { + block_locator_hashes.push(BlockHeaderHash(reader.read_32_bytes()?)); + } + + let hash_stop = BlockHeaderHash(reader.read_32_bytes()?); + + Ok(Message::GetBlocks { + version, + block_locator_hashes, + hash_stop, + }) } /// Deserialize a `headers` message. @@ -458,7 +473,7 @@ impl Codec { ) -> Result, Error> { 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 + // case. Although the size of the received 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 diff --git a/zebra-network/src/protocol/message.rs b/zebra-network/src/protocol/message.rs index 3d0d4b12b61..f855f021bf2 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, BlockHeader}; +use zebra_chain::block::{Block, BlockHeader, BlockHeaderHash}; use zebra_chain::{transaction::Transaction, types::BlockHeight}; use crate::meta_addr::MetaAddr; @@ -144,8 +144,36 @@ pub enum Message { /// A `getblocks` message. /// + /// Requests the list of blocks starting right after the last + /// known hash in `block_locator_hashes`, up to `hash_stop` or 500 + /// blocks, whichever comes first. + /// + /// You can send in fewer known hashes down to a minimum of just + /// one hash. However, the purpose of the block locator object is + /// to detect a wrong branch in the caller's main chain. If the + /// peer detects that you are off the main chain, it will send in + /// block hashes which are earlier than your last known block. So + /// if you just send in your last known hash and it is off the + /// main chain, the peer starts over at block #1. + /// /// [Bitcoin reference](https://en.bitcoin.it/wiki/Protocol_documentation#getblocks) - GetBlocks {/* XXX add fields */}, + // The locator hashes are processed by a node in the order as they + // appear in the message. If a block hash is found in the node's + // main chain, the list of its children is returned back via the + // inv message and the remaining locators are ignored, no matter + // if the requested limit was reached, or not. + GetBlocks { + /// The protocol version. + version: Version, + + /// Block locators, from newest back to genesis block. + block_locator_hashes: Vec, + + /// `BlockHeaderHash` of the last desired block. + /// + /// Set to zero to get as many blocks as possible (500). + hash_stop: BlockHeaderHash, + }, /// A `headers` message. /// From 40a87c28623ec0d8b83966c499fdd8be023218cf Mon Sep 17 00:00:00 2001 From: Deirdre Connolly Date: Fri, 4 Oct 2019 02:20:01 -0400 Subject: [PATCH 05/16] Parse messages --- zebra-network/src/protocol/codec.rs | 24 ++++++++++++++++++++---- zebra-network/src/protocol/message.rs | 25 ++++++++++++++++++++++++- 2 files changed, 44 insertions(+), 5 deletions(-) diff --git a/zebra-network/src/protocol/codec.rs b/zebra-network/src/protocol/codec.rs index 3e73a8cfb79..3e5455da940 100644 --- a/zebra-network/src/protocol/codec.rs +++ b/zebra-network/src/protocol/codec.rs @@ -445,7 +445,7 @@ impl Codec { 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 + // case. Although the size of the received 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 @@ -462,9 +462,25 @@ impl Codec { Ok(Message::Headers(headers)) } - fn read_getheaders(&self, mut _reader: R) -> Result { - trace!("getheaders"); - bail!("unimplemented message type") + // XXX This and `read_getblocks` are pretty similar, abstract away? + fn read_getheaders(&self, mut reader: R) -> Result { + let version = Version(reader.read_u32::()?); + + let count = reader.read_compactsize()? as usize; + let max_count = self.builder.max_len / 32; + let mut block_locator_hashes = Vec::with_capacity(std::cmp::min(count, max_count)); + + for _ in 0..count { + block_locator_hashes.push(BlockHeaderHash(reader.read_32_bytes()?)); + } + + let hash_stop = BlockHeaderHash(reader.read_32_bytes()?); + + Ok(Message::GetHeaders { + version, + block_locator_hashes, + hash_stop, + }) } fn _read_generic_inventory_hash_vector( diff --git a/zebra-network/src/protocol/message.rs b/zebra-network/src/protocol/message.rs index f855f021bf2..b7610e1e657 100644 --- a/zebra-network/src/protocol/message.rs +++ b/zebra-network/src/protocol/message.rs @@ -188,8 +188,31 @@ pub enum Message { /// A `getheaders` message. /// + /// Requests a series of block headers starting right after the + /// last known hash in `block_locator_hashes`, up to `hash_stop` + /// or 2000 blocks, whichever comes first. + /// + /// You can send in fewer known hashes down to a minimum of just + /// one hash. However, the purpose of the block locator object is + /// to detect a wrong branch in the caller's main chain. If the + /// peer detects that you are off the main chain, it will send in + /// block hashes which are earlier than your last known block. So + /// if you just send in your last known hash and it is off the + /// main chain, the peer starts over at block #1. + /// /// [Bitcoin reference](https://en.bitcoin.it/wiki/Protocol_documentation#getheaders) - GetHeaders {/* XXX add fields */}, + GetHeaders { + /// The protocol version. + version: Version, + + /// Block locators, from newest back to genesis block. + block_locator_hashes: Vec, + + /// `BlockHeaderHash` of the last desired block header. + /// + /// Set to zero to get as many block headers as possible (2000). + hash_stop: BlockHeaderHash, + }, /// An `inv` message. /// From 0090f95895aa7d09203b415fea8288ca0baaf73f Mon Sep 17 00:00:00 2001 From: Deirdre Connolly Date: Tue, 8 Oct 2019 18:21:53 -0400 Subject: [PATCH 06/16] Add read_list to ReadZcashExt --- zebra-chain/src/serialization.rs | 30 +++++++++++++++ zebra-network/src/protocol/codec.rs | 59 ++++++++--------------------- 2 files changed, 46 insertions(+), 43 deletions(-) diff --git a/zebra-chain/src/serialization.rs b/zebra-chain/src/serialization.rs index 971b7d1d73f..08bec886e08 100644 --- a/zebra-chain/src/serialization.rs +++ b/zebra-chain/src/serialization.rs @@ -244,6 +244,36 @@ pub trait ReadZcashExt: io::Read { self.read_exact(&mut bytes)?; Ok(bytes) } + + /// Convenience method to read a `Vec` with a leading count in a safer manner. + #[inline] + fn read_list( + &mut self, + max_count: usize, + ) -> Result, SerializationError> { + // This prevents the inferred type for zcash_deserialize from + // taking ownership of &mut self. This wouldn't really be an + // issue if the target impl's `Copy`, but we need to own it. + let mut self2 = self; + + let count = self2.read_compactsize()? as usize; + + // Preallocate a buffer, performing a single allocation in the + // honest case. Although the size of the received 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_type_size. + let mut items = Vec::with_capacity(std::cmp::min(count, max_count)); + + for _ in 0..count { + items.push(T::zcash_deserialize(&mut self2)?); + } + + return Ok(items); + } } /// Mark all types implementing `Read` as implementing the extension. diff --git a/zebra-network/src/protocol/codec.rs b/zebra-network/src/protocol/codec.rs index 3e5455da940..a8f29f03e52 100644 --- a/zebra-network/src/protocol/codec.rs +++ b/zebra-network/src/protocol/codec.rs @@ -383,26 +383,11 @@ impl Codec { fn read_addr(&self, mut reader: R) -> Result { use crate::meta_addr::MetaAddr; - // XXX we may want to factor this logic out into - // fn read_vec(reader: R) -> Result, Error> - // on ReadZcashExt (and similarly for WriteZcashExt) - 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 addr 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_ADDR_SIZE. - // // addrs are encoded as: timestamp + services + ipv6 + port const ENCODED_ADDR_SIZE: usize = 4 + 8 + 16 + 2; let max_count = self.builder.max_len / ENCODED_ADDR_SIZE; - let mut addrs = Vec::with_capacity(std::cmp::min(count, max_count)); - for _ in 0..count { - addrs.push(MetaAddr::zcash_deserialize(&mut reader)?); - } + let addrs: Vec = reader.read_list(max_count)?; Ok(Message::Addr(addrs)) } @@ -483,43 +468,31 @@ impl Codec { }) } - fn _read_generic_inventory_hash_vector( - &self, - mut reader: R, - ) -> Result, Error> { - let count = reader.read_compactsize()? as usize; - // Preallocate a buffer, performing a single allocation in the honest - // case. Although the size of the received 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_INVHASH_SIZE. - // + fn read_inv(&self, mut reader: R) -> Result { // encoding: 4 byte type tag + 32 byte hash const ENCODED_INVHASH_SIZE: usize = 4 + 32; let max_count = self.builder.max_len / ENCODED_INVHASH_SIZE; - let mut hashes = Vec::with_capacity(std::cmp::min(count, max_count)); - for _ in 0..count { - hashes.push(InventoryHash::zcash_deserialize(&mut reader)?); - } - - return Ok(hashes); - } - - fn read_inv(&self, reader: R) -> Result { - let hashes = self._read_generic_inventory_hash_vector(reader)?; + let hashes: Vec = reader.read_list(max_count)?; Ok(Message::Inv(hashes)) } - fn read_getdata(&self, reader: R) -> Result { - let hashes = self._read_generic_inventory_hash_vector(reader)?; + fn read_getdata(&self, mut reader: R) -> Result { + // encoding: 4 byte type tag + 32 byte hash + const ENCODED_INVHASH_SIZE: usize = 4 + 32; + let max_count = self.builder.max_len / ENCODED_INVHASH_SIZE; + + let hashes: Vec = reader.read_list(max_count)?; Ok(Message::GetData(hashes)) } - fn read_notfound(&self, reader: R) -> Result { - let hashes = self._read_generic_inventory_hash_vector(reader)?; + fn read_notfound(&self, mut reader: R) -> Result { + // encoding: 4 byte type tag + 32 byte hash + const ENCODED_INVHASH_SIZE: usize = 4 + 32; + let max_count = self.builder.max_len / ENCODED_INVHASH_SIZE; + + let hashes: Vec = reader.read_list(max_count)?; + Ok(Message::GetData(hashes)) } From f1fb6ef6d6d890032739fb21243447497723483f Mon Sep 17 00:00:00 2001 From: Deirdre Connolly Date: Tue, 8 Oct 2019 19:39:35 -0400 Subject: [PATCH 07/16] Implement Zcash(De)Serialize for BlockHeaderHash, use general read_list for getheaders/headers --- zebra-chain/src/block.rs | 16 +++++++++++++++- zebra-network/src/protocol/codec.rs | 25 +++---------------------- 2 files changed, 18 insertions(+), 23 deletions(-) diff --git a/zebra-chain/src/block.rs b/zebra-chain/src/block.rs index eb61867e44b..6e9bb057633 100644 --- a/zebra-chain/src/block.rs +++ b/zebra-chain/src/block.rs @@ -5,7 +5,7 @@ use std::io; use crate::merkle_tree::MerkleTreeRootHash; use crate::note_commitment_tree::SaplingNoteTreeRootHash; -use crate::serialization::{SerializationError, ZcashDeserialize, ZcashSerialize}; +use crate::serialization::{ReadZcashExt, SerializationError, ZcashDeserialize, ZcashSerialize}; use crate::sha256d_writer::Sha256dWriter; use crate::transaction::Transaction; @@ -34,6 +34,20 @@ impl From for BlockHeaderHash { } } +impl ZcashSerialize for BlockHeaderHash { + fn zcash_serialize(&self, mut writer: W) -> Result<(), SerializationError> { + writer.write_all(&self.0)?; + Ok(()) + } +} + +impl ZcashDeserialize for BlockHeaderHash { + fn zcash_deserialize(mut reader: R) -> Result { + let bytes = reader.read_32_bytes()?; + Ok(BlockHeaderHash(bytes)) + } +} + /// Block header. /// /// How are blocks chained together? They are chained together via the diff --git a/zebra-network/src/protocol/codec.rs b/zebra-network/src/protocol/codec.rs index a8f29f03e52..f452a04beaa 100644 --- a/zebra-network/src/protocol/codec.rs +++ b/zebra-network/src/protocol/codec.rs @@ -405,13 +405,9 @@ impl Codec { fn read_getblocks(&self, mut reader: R) -> Result { let version = Version(reader.read_u32::()?); - let count = reader.read_compactsize()? as usize; let max_count = self.builder.max_len / 32; - let mut block_locator_hashes = Vec::with_capacity(std::cmp::min(count, max_count)); - for _ in 0..count { - block_locator_hashes.push(BlockHeaderHash(reader.read_32_bytes()?)); - } + let block_locator_hashes: Vec = reader.read_list(max_count)?; let hash_stop = BlockHeaderHash(reader.read_32_bytes()?); @@ -428,21 +424,10 @@ impl Codec { /// /// [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 received 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)?); - } + let headers: Vec = reader.read_list(max_count)?; Ok(Message::Headers(headers)) } @@ -451,13 +436,9 @@ impl Codec { fn read_getheaders(&self, mut reader: R) -> Result { let version = Version(reader.read_u32::()?); - let count = reader.read_compactsize()? as usize; let max_count = self.builder.max_len / 32; - let mut block_locator_hashes = Vec::with_capacity(std::cmp::min(count, max_count)); - for _ in 0..count { - block_locator_hashes.push(BlockHeaderHash(reader.read_32_bytes()?)); - } + let block_locator_hashes: Vec = reader.read_list(max_count)?; let hash_stop = BlockHeaderHash(reader.read_32_bytes()?); From 414cca6dd5ab855dd67d9804143d2212a663f635 Mon Sep 17 00:00:00 2001 From: Deirdre Connolly Date: Tue, 8 Oct 2019 23:45:03 -0400 Subject: [PATCH 08/16] Remove some defunct XXXs --- zebra-network/src/protocol/codec.rs | 1 - zebra-network/src/protocol/message.rs | 1 - 2 files changed, 2 deletions(-) diff --git a/zebra-network/src/protocol/codec.rs b/zebra-network/src/protocol/codec.rs index f452a04beaa..f2c7928efa1 100644 --- a/zebra-network/src/protocol/codec.rs +++ b/zebra-network/src/protocol/codec.rs @@ -432,7 +432,6 @@ impl Codec { Ok(Message::Headers(headers)) } - // XXX This and `read_getblocks` are pretty similar, abstract away? fn read_getheaders(&self, mut reader: R) -> Result { let version = Version(reader.read_u32::()?); diff --git a/zebra-network/src/protocol/message.rs b/zebra-network/src/protocol/message.rs index b7610e1e657..6e28667f713 100644 --- a/zebra-network/src/protocol/message.rs +++ b/zebra-network/src/protocol/message.rs @@ -251,7 +251,6 @@ pub enum Message { version: Version, /// The `Transaction` type itself. - // XXX Is this ~aesthetic~? transaction: Transaction, }, From 898620cc8baef410647c28daa6c98ac153c3b1c1 Mon Sep 17 00:00:00 2001 From: Deirdre Connolly Date: Wed, 9 Oct 2019 19:42:52 -0400 Subject: [PATCH 09/16] Add comments about the 500 blocks /2000 headers max numbers are speculative based on Bitcoin docs --- zebra-network/src/protocol/message.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/zebra-network/src/protocol/message.rs b/zebra-network/src/protocol/message.rs index 6e28667f713..29c98f99c19 100644 --- a/zebra-network/src/protocol/message.rs +++ b/zebra-network/src/protocol/message.rs @@ -162,6 +162,11 @@ pub enum Message { // main chain, the list of its children is returned back via the // inv message and the remaining locators are ignored, no matter // if the requested limit was reached, or not. + // + // The 500 headers number is from the Bitcoin docs, we are not + // certain (yet) that other implementations of Zcash obey this + // restriction, or if they don't, what happens if we send them too + // many results. GetBlocks { /// The protocol version. version: Version, @@ -201,6 +206,10 @@ pub enum Message { /// main chain, the peer starts over at block #1. /// /// [Bitcoin reference](https://en.bitcoin.it/wiki/Protocol_documentation#getheaders) + // The 2000 headers number is from the Bitcoin docs, we are not + // certain (yet) that other implementations of Zcash obey this + // restriction, or if they don't, what happens if we send them too + // many results. GetHeaders { /// The protocol version. version: Version, From ecd16f6f5513a9324c8a17132d9439a6230f1bda Mon Sep 17 00:00:00 2001 From: Deirdre Connolly Date: Wed, 9 Oct 2019 21:24:21 -0400 Subject: [PATCH 10/16] Implement read_getaddr --- zebra-network/src/protocol/codec.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/zebra-network/src/protocol/codec.rs b/zebra-network/src/protocol/codec.rs index f2c7928efa1..83e497d9de7 100644 --- a/zebra-network/src/protocol/codec.rs +++ b/zebra-network/src/protocol/codec.rs @@ -393,8 +393,7 @@ impl Codec { } fn read_getaddr(&self, mut _reader: R) -> Result { - trace!("getaddr"); - bail!("unimplemented message type") + Ok(Message::Verack) } fn read_block(&self, mut _reader: R) -> Result { From f91f18af52c4164042b092c518199604fe0c5ff1 Mon Sep 17 00:00:00 2001 From: Deirdre Connolly Date: Wed, 9 Oct 2019 21:26:48 -0400 Subject: [PATCH 11/16] Fill out write_body for GetBlocks, GetHeaders, Headers messages --- zebra-network/src/protocol/codec.rs | 31 +++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/zebra-network/src/protocol/codec.rs b/zebra-network/src/protocol/codec.rs index 83e497d9de7..4ba97b037d1 100644 --- a/zebra-network/src/protocol/codec.rs +++ b/zebra-network/src/protocol/codec.rs @@ -216,6 +216,37 @@ impl Codec { .zcash_serialize(&mut writer) .expect("Blocks must serialize."); } + GetBlocks { + ref version, + ref block_locator_hashes, + ref hash_stop, + } => { + writer.write_u32::(version.0)?; + writer.write_compactsize(block_locator_hashes.len() as u64)?; + for hash in block_locator_hashes { + hash.zcash_serialize(&mut writer)?; + } + hash_stop.zcash_serialize(&mut writer)?; + } + GetHeaders { + ref version, + ref block_locator_hashes, + ref hash_stop, + } => { + writer.write_u32::(version.0)?; + writer.write_compactsize(block_locator_hashes.len() as u64)?; + for hash in block_locator_hashes { + hash.zcash_serialize(&mut writer)?; + } + hash_stop.zcash_serialize(&mut writer)?; + } + Headers(ref headers) => { + writer.write_compactsize(headers.len() as u64)?; + for header in headers { + header.zcash_serialize(&mut writer)?; + } + } + _ => bail!("unimplemented message type"), } Ok(()) From 2ee4f0e37f0d59f4a70c9a097e25208932670ad6 Mon Sep 17 00:00:00 2001 From: Deirdre Connolly Date: Wed, 9 Oct 2019 21:47:53 -0400 Subject: [PATCH 12/16] Add Block msg reader and writers, and Tx msg writer --- zebra-network/src/protocol/codec.rs | 27 +++++++++++++++++++++------ zebra-network/src/protocol/message.rs | 3 +++ 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/zebra-network/src/protocol/codec.rs b/zebra-network/src/protocol/codec.rs index 4ba97b037d1..ed20640459a 100644 --- a/zebra-network/src/protocol/codec.rs +++ b/zebra-network/src/protocol/codec.rs @@ -9,7 +9,7 @@ use failure::Error; use tokio::codec::{Decoder, Encoder}; use zebra_chain::{ - block::{BlockHeader, BlockHeaderHash}, + block::{Block, BlockHeader, BlockHeaderHash}, serialization::{ReadZcashExt, WriteZcashExt, ZcashDeserialize, ZcashSerialize}, transaction::Transaction, types::{BlockHeight, Sha256dChecksum}, @@ -211,7 +211,11 @@ impl Codec { hash.zcash_serialize(&mut writer)?; } } - Block { ref block } => { + Block { + ref version, + ref block, + } => { + writer.write_u32::(version.0)?; block .zcash_serialize(&mut writer) .expect("Blocks must serialize."); @@ -246,7 +250,15 @@ impl Codec { header.zcash_serialize(&mut writer)?; } } - + Tx { + ref version, + ref transaction, + } => { + writer.write_u32::(version.0)?; + transaction + .zcash_serialize(&mut writer) + .expect("Transactions must serialize."); + } _ => bail!("unimplemented message type"), } Ok(()) @@ -427,9 +439,12 @@ impl Codec { Ok(Message::Verack) } - fn read_block(&self, mut _reader: R) -> Result { - trace!("block"); - bail!("unimplemented message type") + fn read_block(&self, mut reader: R) -> Result { + Ok(Message::Block { + version: Version(reader.read_u32::()?), + + block: Block::zcash_deserialize(&mut reader)?, + }) } fn read_getblocks(&self, mut reader: R) -> Result { diff --git a/zebra-network/src/protocol/message.rs b/zebra-network/src/protocol/message.rs index 29c98f99c19..7c9bde4a674 100644 --- a/zebra-network/src/protocol/message.rs +++ b/zebra-network/src/protocol/message.rs @@ -138,6 +138,9 @@ pub enum Message { /// /// [Bitcoin reference](https://en.bitcoin.it/wiki/Protocol_documentation#block) Block { + /// Transaction data format version (note, this is signed). + version: Version, + /// The block itself. block: Block, }, From ac4f753f70b54ef0b4bd3ffd02348aacc3f8fa27 Mon Sep 17 00:00:00 2001 From: Deirdre Connolly Date: Wed, 9 Oct 2019 21:58:44 -0400 Subject: [PATCH 13/16] Add write_body implementations for GetData, NotFound, and a placeholder for Reject --- zebra-network/src/protocol/codec.rs | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/zebra-network/src/protocol/codec.rs b/zebra-network/src/protocol/codec.rs index ed20640459a..1afb8777a66 100644 --- a/zebra-network/src/protocol/codec.rs +++ b/zebra-network/src/protocol/codec.rs @@ -198,19 +198,14 @@ impl Codec { Pong(nonce) => { writer.write_u64::(nonce.0)?; } - GetAddr => { /* Empty payload -- no-op */ } + // Reject {} => {} Addr(ref addrs) => { writer.write_compactsize(addrs.len() as u64)?; for addr in addrs { addr.zcash_serialize(&mut writer)?; } } - Inv(ref hashes) => { - writer.write_compactsize(hashes.len() as u64)?; - for hash in hashes { - hash.zcash_serialize(&mut writer)?; - } - } + GetAddr => { /* Empty payload -- no-op */ } Block { ref version, ref block, @@ -250,6 +245,24 @@ impl Codec { header.zcash_serialize(&mut writer)?; } } + Inv(ref hashes) => { + writer.write_compactsize(hashes.len() as u64)?; + for hash in hashes { + hash.zcash_serialize(&mut writer)?; + } + } + GetData(ref hashes) => { + writer.write_compactsize(hashes.len() as u64)?; + for hash in hashes { + hash.zcash_serialize(&mut writer)?; + } + } + NotFound(ref hashes) => { + writer.write_compactsize(hashes.len() as u64)?; + for hash in hashes { + hash.zcash_serialize(&mut writer)?; + } + } Tx { ref version, ref transaction, From 43c9eddb1659adc7358208647c38a5778be2c372 Mon Sep 17 00:00:00 2001 From: Deirdre Connolly Date: Wed, 9 Oct 2019 22:02:05 -0400 Subject: [PATCH 14/16] Add placeholders for remaining messages in write_body --- zebra-network/src/protocol/codec.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/zebra-network/src/protocol/codec.rs b/zebra-network/src/protocol/codec.rs index 1afb8777a66..0196f2f258e 100644 --- a/zebra-network/src/protocol/codec.rs +++ b/zebra-network/src/protocol/codec.rs @@ -272,6 +272,11 @@ impl Codec { .zcash_serialize(&mut writer) .expect("Transactions must serialize."); } + // Mempool => {} + // FilterLoad => {} + // FilterAdd => {} + // FilterClear => {} + // MerkleBlock => {} _ => bail!("unimplemented message type"), } Ok(()) From fc2f04c4357b59027429d79310640c350665e263 Mon Sep 17 00:00:00 2001 From: Deirdre Connolly Date: Wed, 9 Oct 2019 22:12:03 -0400 Subject: [PATCH 15/16] Add details about max_count to read_list docstring --- zebra-chain/src/serialization.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/zebra-chain/src/serialization.rs b/zebra-chain/src/serialization.rs index 08bec886e08..44ec9903815 100644 --- a/zebra-chain/src/serialization.rs +++ b/zebra-chain/src/serialization.rs @@ -245,7 +245,16 @@ pub trait ReadZcashExt: io::Read { Ok(bytes) } - /// Convenience method to read a `Vec` with a leading count in a safer manner. + /// Convenience method to read a `Vec` with a leading count in + /// a safer manner. + /// + /// This method preallocates a buffer, performing a single + /// allocation in the honest case. It's 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, we rely on the passed + /// maximum count for a valid message and select the min of the + /// two values. #[inline] fn read_list( &mut self, @@ -258,14 +267,6 @@ pub trait ReadZcashExt: io::Read { let count = self2.read_compactsize()? as usize; - // Preallocate a buffer, performing a single allocation in the - // honest case. Although the size of the received 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_type_size. let mut items = Vec::with_capacity(std::cmp::min(count, max_count)); for _ in 0..count { From 1dcb12c8940973daefd0f82287e247ac21c99705 Mon Sep 17 00:00:00 2001 From: Deirdre Connolly Date: Wed, 9 Oct 2019 22:16:13 -0400 Subject: [PATCH 16/16] Remove .expect()s for block and transaction, they might fail for writer reasons --- zebra-network/src/protocol/codec.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/zebra-network/src/protocol/codec.rs b/zebra-network/src/protocol/codec.rs index 0196f2f258e..25c6f8f8a82 100644 --- a/zebra-network/src/protocol/codec.rs +++ b/zebra-network/src/protocol/codec.rs @@ -211,9 +211,7 @@ impl Codec { ref block, } => { writer.write_u32::(version.0)?; - block - .zcash_serialize(&mut writer) - .expect("Blocks must serialize."); + block.zcash_serialize(&mut writer)? } GetBlocks { ref version, @@ -268,9 +266,7 @@ impl Codec { ref transaction, } => { writer.write_u32::(version.0)?; - transaction - .zcash_serialize(&mut writer) - .expect("Transactions must serialize."); + transaction.zcash_serialize(&mut writer)? } // Mempool => {} // FilterLoad => {}