-
Notifications
You must be signed in to change notification settings - Fork 111
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Parse inv, addr, getaddr messages. #31
Changes from all commits
cee79cb
e6fd044
7de0cd8
8af845a
f69d57a
e5cbd0f
119f881
c2154a5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,3 +2,6 @@ | |
|
||
pub mod codec; | ||
pub mod message; | ||
pub mod types; | ||
|
||
pub mod inv; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,13 +9,13 @@ use failure::Error; | |
use tokio::codec::{Decoder, Encoder}; | ||
|
||
use zebra_chain::{ | ||
serialization::{ReadZcashExt, WriteZcashExt, ZcashSerialize}, | ||
serialization::{ReadZcashExt, WriteZcashExt, ZcashDeserialize, ZcashSerialize}, | ||
types::{BlockHeight, Sha256dChecksum}, | ||
}; | ||
|
||
use crate::{constants, types::*, Network}; | ||
use crate::{constants, Network}; | ||
|
||
use super::message::Message; | ||
use super::{message::Message, types::*}; | ||
|
||
/// The length of a Bitcoin message header. | ||
const HEADER_LEN: usize = 24usize; | ||
|
@@ -126,7 +126,7 @@ impl Encoder for Codec { | |
GetBlocks { .. } => b"getblocks\0\0\0", | ||
Headers { .. } => b"headers\0\0\0\0\0", | ||
GetHeaders { .. } => b"getheaders\0\0", | ||
Inventory { .. } => b"inv\0\0\0\0\0\0\0\0\0", // XXX Inventory -> Inv ? | ||
Inv { .. } => b"inv\0\0\0\0\0\0\0\0\0", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No strong opinions on this -- I'm happy to drop this commit from the PR but aligning names with bitcoin like this makes the table line up nicer. |
||
GetData { .. } => b"getdata\0\0\0\0\0", | ||
NotFound { .. } => b"notfound\0\0\0\0", | ||
Tx { .. } => b"tx\0\0\0\0\0\0\0\0\0\0", | ||
|
@@ -197,6 +197,19 @@ impl Codec { | |
Pong(nonce) => { | ||
writer.write_u64::<LittleEndian>(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) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this needs to be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (Perhaps the |
||
writer.write_compactsize(hashes.len() as u64)?; | ||
for hash in hashes { | ||
hash.zcash_serialize(&mut writer)?; | ||
} | ||
} | ||
Block { ref block } => { | ||
block | ||
.zcash_serialize(&mut writer) | ||
|
@@ -279,7 +292,8 @@ impl Decoder for Codec { | |
} | ||
|
||
// Now that we know we have the full body, split off the body, | ||
// and reset the decoder state for the next message. | ||
// and reset the decoder state for the next message. Otherwise | ||
// we will attempt to read the next header as the current body. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixup from #22 (comment) |
||
let body = src.split_to(body_len); | ||
self.state = DecodeState::Head; | ||
|
||
|
@@ -365,9 +379,31 @@ impl Codec { | |
bail!("unimplemented message type") | ||
} | ||
|
||
fn read_addr<R: Read>(&self, mut _reader: R) -> Result<Message, Error> { | ||
trace!("addr"); | ||
bail!("unimplemented message type") | ||
fn read_addr<R: Read>(&self, mut reader: R) -> Result<Message, Error> { | ||
use crate::meta_addr::MetaAddr; | ||
|
||
// XXX we may want to factor this logic out into | ||
// fn read_vec<R: Read, T: ZcashDeserialize>(reader: R) -> Result<Vec<T>, 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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
// 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)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
|
||
for _ in 0..count { | ||
addrs.push(MetaAddr::zcash_deserialize(&mut reader)?); | ||
} | ||
|
||
Ok(Message::Addr(addrs)) | ||
} | ||
|
||
fn read_getaddr<R: Read>(&self, mut _reader: R) -> Result<Message, Error> { | ||
|
@@ -395,9 +431,28 @@ impl Codec { | |
bail!("unimplemented message type") | ||
} | ||
|
||
fn read_inv<R: Read>(&self, mut _reader: R) -> Result<Message, Error> { | ||
trace!("inv"); | ||
bail!("unimplemented message type") | ||
fn read_inv<R: Read>(&self, mut reader: R) -> Result<Message, Error> { | ||
use super::inv::InventoryHash; | ||
|
||
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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
// the codec's max_len using ENCODED_INVHASH_SIZE. | ||
// | ||
// 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)?); | ||
} | ||
|
||
Ok(Message::Inv(hashes)) | ||
} | ||
|
||
fn read_getdata<R: Read>(&self, mut _reader: R) -> Result<Message, Error> { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
//! Inventory items for the Bitcoin protocol. | ||
|
||
// XXX the exact optimal arrangement of all of these parts is a little unclear | ||
// until we have more pieces in place the optimal global arrangement of items is | ||
// a little unclear. | ||
|
||
use std::io::{Read, Write}; | ||
|
||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; | ||
|
||
use zebra_chain::serialization::{ | ||
ReadZcashExt, SerializationError, ZcashDeserialize, ZcashSerialize, | ||
}; | ||
|
||
/// Stub-- delete later. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
#[derive(Copy, Clone, Debug, Eq, PartialEq)] | ||
pub struct TxHash(pub [u8; 32]); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So the replacement for this would live in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep, and the |
||
/// Stub-- delete later. | ||
#[derive(Copy, Clone, Debug, Eq, PartialEq)] | ||
pub struct BlockHash(pub [u8; 32]); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These need to be replaced with types from #21 before merging; these are just placeholders. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
||
/// An inventory hash which refers to some advertised or requested data. | ||
/// | ||
/// Bitcoin calls this an "inventory vector" but it is just a typed hash, not a | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
/// container, so we do not use that term to avoid confusion with `Vec<T>`. | ||
/// | ||
/// [Bitcoin·reference](https://en.bitcoin.it/wiki/Protocol_documentation#Inventory_Vectors) | ||
#[derive(Copy, Clone, Debug, Eq, PartialEq)] | ||
pub enum InventoryHash { | ||
/// An error. | ||
/// | ||
/// The Bitcoin wiki just says "Any data of with this number may be ignored", | ||
/// so we don't include a typed hash. | ||
Error, | ||
/// A hash of a transaction. | ||
Tx(TxHash), | ||
/// A hash of a block. | ||
Block(BlockHash), | ||
/// A hash of a filtered block. | ||
/// | ||
/// The Bitcoin wiki says: Hash of a block header, but only to be used in | ||
/// getdata message. Indicates the reply should be a merkleblock message | ||
/// rather than a block message; this only works if a bloom filter has been | ||
/// set. | ||
FilteredBlock(BlockHash), | ||
} | ||
|
||
impl ZcashSerialize for InventoryHash { | ||
fn zcash_serialize<W: Write>(&self, mut writer: W) -> Result<(), SerializationError> { | ||
let (code, bytes) = match *self { | ||
InventoryHash::Error => (0, [0; 32]), | ||
InventoryHash::Tx(hash) => (1, hash.0), | ||
InventoryHash::Block(hash) => (2, hash.0), | ||
InventoryHash::FilteredBlock(hash) => (3, hash.0), | ||
}; | ||
writer.write_u32::<LittleEndian>(code)?; | ||
writer.write_all(&bytes)?; | ||
Ok(()) | ||
} | ||
} | ||
|
||
impl ZcashDeserialize for InventoryHash { | ||
fn zcash_deserialize<R: Read>(mut reader: R) -> Result<Self, SerializationError> { | ||
let code = reader.read_u32::<LittleEndian>()?; | ||
let bytes = reader.read_32_bytes()?; | ||
match code { | ||
0 => Ok(InventoryHash::Error), | ||
1 => Ok(InventoryHash::Tx(TxHash(bytes))), | ||
2 => Ok(InventoryHash::Block(BlockHash(bytes))), | ||
3 => Ok(InventoryHash::FilteredBlock(BlockHash(bytes))), | ||
_ => Err(SerializationError::ParseError("invalid inventory code")), | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,7 +8,9 @@ use zebra_chain::block::Block; | |
use zebra_chain::{transaction::Transaction, types::BlockHeight}; | ||
|
||
use crate::meta_addr::MetaAddr; | ||
use crate::types::*; | ||
|
||
use super::inv::InventoryHash; | ||
use super::types::*; | ||
|
||
/// A Bitcoin-like network message for the Zcash protocol. | ||
/// | ||
|
@@ -161,13 +163,7 @@ pub enum Message { | |
// 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 | ||
// having serialization be async? | ||
Inventory { | ||
/// Number of inventory entries. | ||
count: u64, | ||
|
||
/// Inventory vectors. | ||
inventory: Vec<zebra_chain::types::InventoryVector>, | ||
}, | ||
Inv(Vec<InventoryHash>), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
|
||
/// A `getdata` message. | ||
/// | ||
|
@@ -176,25 +172,13 @@ pub enum Message { | |
/// packet, after filtering known elements. | ||
/// | ||
/// [Bitcoin reference](https://en.bitcoin.it/wiki/Protocol_documentation#getdata) | ||
GetData { | ||
/// Number of inventory entries. | ||
count: u64, | ||
|
||
/// Inventory vectors. | ||
inventory: Vec<zebra_chain::types::InventoryVector>, | ||
}, | ||
GetData(Vec<InventoryHash>), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
|
||
/// A `notfound` message. | ||
/// | ||
/// [Bitcoin reference](https://en.bitcoin.it/wiki/Protocol_documentation#notfound) | ||
// See note above on `Inventory`. | ||
NotFound { | ||
/// Number of inventory entries. | ||
count: u64, | ||
|
||
/// Inventory vectors. | ||
inventory: Vec<zebra_chain::types::InventoryVector>, | ||
}, | ||
NotFound(Vec<InventoryHash>), | ||
|
||
/// A `tx` message. | ||
/// | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My suspicion is that we will want to move it into
protocol
but I'd rather wait a bit to see how things settle, WDYT?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good to me, more metadata thingies related to the protocol.