From 0a85be285dac1c4083dab3ccb1a2ac5f561dfd81 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Wed, 25 Sep 2019 16:20:02 -0700 Subject: [PATCH] Add addr, getaddr serialization. --- zebra-network/src/meta_addr.rs | 28 ++++++++++++++++++++++- zebra-network/src/protocol/codec.rs | 35 ++++++++++++++++++++++++++--- 2 files changed, 59 insertions(+), 4 deletions(-) diff --git a/zebra-network/src/meta_addr.rs b/zebra-network/src/meta_addr.rs index 8af5fb387..6f014dbea 100644 --- a/zebra-network/src/meta_addr.rs +++ b/zebra-network/src/meta_addr.rs @@ -1,8 +1,15 @@ //! An address-with-metadata type used in Bitcoin networking. -use chrono::{DateTime, Utc}; +use std::io::{Read, Write}; use std::net::SocketAddr; +use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; +use chrono::{DateTime, TimeZone, Utc}; + +use zebra_chain::serialization::{ + ReadZcashExt, SerializationError, WriteZcashExt, ZcashDeserialize, ZcashSerialize, +}; + use crate::protocol::types::Services; /// An address with metadata on its advertised services and last-seen time. @@ -20,3 +27,22 @@ pub struct MetaAddr { /// When the peer was last seen. pub last_seen: DateTime, } + +impl ZcashSerialize for MetaAddr { + fn zcash_serialize(&self, mut writer: W) -> Result<(), SerializationError> { + writer.write_u32::(self.last_seen.timestamp() as u32)?; + writer.write_u64::(self.services.0)?; + writer.write_socket_addr(self.addr)?; + Ok(()) + } +} + +impl ZcashDeserialize for MetaAddr { + fn zcash_deserialize(mut reader: R) -> Result { + Ok(MetaAddr { + last_seen: Utc.timestamp(reader.read_u32::()? as i64, 0), + services: Services(reader.read_u64::()?), + addr: reader.read_socket_addr()?, + }) + } +} diff --git a/zebra-network/src/protocol/codec.rs b/zebra-network/src/protocol/codec.rs index a1ffeed6e..330d38346 100644 --- a/zebra-network/src/protocol/codec.rs +++ b/zebra-network/src/protocol/codec.rs @@ -197,6 +197,13 @@ impl Codec { Pong(nonce) => { writer.write_u64::(nonce.0)?; } + GetAddr => { /* Empty payload -- no-op */ } + 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 { @@ -372,9 +379,31 @@ impl Codec { bail!("unimplemented message type") } - fn read_addr(&self, mut _reader: R) -> Result { - trace!("addr"); - bail!("unimplemented message type") + 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)?); + } + + Ok(Message::Addr(addrs)) } fn read_getaddr(&self, mut _reader: R) -> Result {