Parse inv messages, refactor inventory vectors.
This removes the inventory vector structs from `zebra-chain` (as they are really part of the network protocol) and refactors them into a single `InventoryHash` type. This corresponds to Bitcoin's "inventory vector" but with a different, better name (it's not a vector, it's just a typed hash of some other item).
This commit is contained in:
parent
64b210b53c
commit
958fca8e68
|
|
@ -19,34 +19,6 @@ impl<'a> From<&'a [u8]> for Sha256dChecksum {
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
pub struct BlockHeight(pub u32);
|
pub struct BlockHeight(pub u32);
|
||||||
|
|
||||||
/// InventoryType
|
|
||||||
///
|
|
||||||
/// [Bitcoin·reference](https://en.bitcoin.it/wiki/Protocol_documentation#Inventory_Vectors)
|
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
|
||||||
#[repr(u8)]
|
|
||||||
pub enum InventoryType {
|
|
||||||
/// Any data of with this number may be ignored.
|
|
||||||
Error = 0x00,
|
|
||||||
|
|
||||||
/// Hash is related to a transaction.
|
|
||||||
MsgTx = 0x01,
|
|
||||||
|
|
||||||
/// Hash is related to a data block.
|
|
||||||
MsgBlock = 0x02,
|
|
||||||
|
|
||||||
/// 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.
|
|
||||||
// XXX: Since we don't intend to include the bloom filter to
|
|
||||||
// start, do we need this?
|
|
||||||
MsgFilteredBlock = 0x03,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Inventory Vector
|
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
|
||||||
pub struct InventoryVector(pub InventoryType, pub [u8; 32]);
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
||||||
|
|
@ -3,3 +3,5 @@
|
||||||
pub mod codec;
|
pub mod codec;
|
||||||
pub mod message;
|
pub mod message;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|
||||||
|
pub mod inv;
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ use failure::Error;
|
||||||
use tokio::codec::{Decoder, Encoder};
|
use tokio::codec::{Decoder, Encoder};
|
||||||
|
|
||||||
use zebra_chain::{
|
use zebra_chain::{
|
||||||
serialization::{ReadZcashExt, WriteZcashExt, ZcashSerialize},
|
serialization::{ReadZcashExt, WriteZcashExt, ZcashDeserialize, ZcashSerialize},
|
||||||
types::{BlockHeight, Sha256dChecksum},
|
types::{BlockHeight, Sha256dChecksum},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -197,6 +197,12 @@ impl Codec {
|
||||||
Pong(nonce) => {
|
Pong(nonce) => {
|
||||||
writer.write_u64::<LittleEndian>(nonce.0)?;
|
writer.write_u64::<LittleEndian>(nonce.0)?;
|
||||||
}
|
}
|
||||||
|
Inv(ref hashes) => {
|
||||||
|
writer.write_compactsize(hashes.len() as u64)?;
|
||||||
|
for hash in hashes {
|
||||||
|
hash.zcash_serialize(&mut writer)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
Block { ref block } => {
|
Block { ref block } => {
|
||||||
block
|
block
|
||||||
.zcash_serialize(&mut writer)
|
.zcash_serialize(&mut writer)
|
||||||
|
|
@ -395,9 +401,28 @@ impl Codec {
|
||||||
bail!("unimplemented message type")
|
bail!("unimplemented message type")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_inv<R: Read>(&self, mut _reader: R) -> Result<Message, Error> {
|
fn read_inv<R: Read>(&self, mut reader: R) -> Result<Message, Error> {
|
||||||
trace!("inv");
|
use super::inv::InventoryHash;
|
||||||
bail!("unimplemented message type")
|
|
||||||
|
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_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::Inventory(hashes))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_getdata<R: Read>(&self, mut _reader: R) -> Result<Message, Error> {
|
fn read_getdata<R: Read>(&self, mut _reader: R) -> Result<Message, Error> {
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub struct TxHash(pub [u8; 32]);
|
||||||
|
/// Stub-- delete later.
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub struct BlockHash(pub [u8; 32]);
|
||||||
|
|
||||||
|
/// 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
|
||||||
|
/// 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")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -9,6 +9,7 @@ use zebra_chain::{transaction::Transaction, types::BlockHeight};
|
||||||
|
|
||||||
use crate::meta_addr::MetaAddr;
|
use crate::meta_addr::MetaAddr;
|
||||||
|
|
||||||
|
use super::inv::InventoryHash;
|
||||||
use super::types::*;
|
use super::types::*;
|
||||||
|
|
||||||
/// A Bitcoin-like network message for the Zcash protocol.
|
/// A Bitcoin-like network message for the Zcash protocol.
|
||||||
|
|
@ -162,13 +163,7 @@ pub enum Message {
|
||||||
// XXX the bitcoin reference above suggests this can be 1.8 MB in bitcoin -- maybe
|
// 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
|
// larger in Zcash, since Zcash objects could be bigger (?) -- does this tilt towards
|
||||||
// having serialization be async?
|
// having serialization be async?
|
||||||
Inventory {
|
Inventory(Vec<InventoryHash>),
|
||||||
/// Number of inventory entries.
|
|
||||||
count: u64,
|
|
||||||
|
|
||||||
/// Inventory vectors.
|
|
||||||
inventory: Vec<zebra_chain::types::InventoryVector>,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// A `getdata` message.
|
/// A `getdata` message.
|
||||||
///
|
///
|
||||||
|
|
@ -177,25 +172,13 @@ pub enum Message {
|
||||||
/// packet, after filtering known elements.
|
/// packet, after filtering known elements.
|
||||||
///
|
///
|
||||||
/// [Bitcoin reference](https://en.bitcoin.it/wiki/Protocol_documentation#getdata)
|
/// [Bitcoin reference](https://en.bitcoin.it/wiki/Protocol_documentation#getdata)
|
||||||
GetData {
|
GetData(Vec<InventoryHash>),
|
||||||
/// Number of inventory entries.
|
|
||||||
count: u64,
|
|
||||||
|
|
||||||
/// Inventory vectors.
|
|
||||||
inventory: Vec<zebra_chain::types::InventoryVector>,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// A `notfound` message.
|
/// A `notfound` message.
|
||||||
///
|
///
|
||||||
/// [Bitcoin reference](https://en.bitcoin.it/wiki/Protocol_documentation#notfound)
|
/// [Bitcoin reference](https://en.bitcoin.it/wiki/Protocol_documentation#notfound)
|
||||||
// See note above on `Inventory`.
|
// See note above on `Inventory`.
|
||||||
NotFound {
|
NotFound(Vec<InventoryHash>),
|
||||||
/// Number of inventory entries.
|
|
||||||
count: u64,
|
|
||||||
|
|
||||||
/// Inventory vectors.
|
|
||||||
inventory: Vec<zebra_chain::types::InventoryVector>,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// A `tx` message.
|
/// A `tx` message.
|
||||||
///
|
///
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue