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)]
|
||||
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)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
|
|||
|
|
@ -3,3 +3,5 @@
|
|||
pub mod codec;
|
||||
pub mod message;
|
||||
pub mod types;
|
||||
|
||||
pub mod inv;
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use failure::Error;
|
|||
use tokio::codec::{Decoder, Encoder};
|
||||
|
||||
use zebra_chain::{
|
||||
serialization::{ReadZcashExt, WriteZcashExt, ZcashSerialize},
|
||||
serialization::{ReadZcashExt, WriteZcashExt, ZcashDeserialize, ZcashSerialize},
|
||||
types::{BlockHeight, Sha256dChecksum},
|
||||
};
|
||||
|
||||
|
|
@ -197,6 +197,12 @@ impl Codec {
|
|||
Pong(nonce) => {
|
||||
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
|
||||
.zcash_serialize(&mut writer)
|
||||
|
|
@ -395,9 +401,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
|
||||
// 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> {
|
||||
|
|
|
|||
|
|
@ -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 super::inv::InventoryHash;
|
||||
use super::types::*;
|
||||
|
||||
/// 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
|
||||
// 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>,
|
||||
},
|
||||
Inventory(Vec<InventoryHash>),
|
||||
|
||||
/// A `getdata` message.
|
||||
///
|
||||
|
|
@ -177,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>),
|
||||
|
||||
/// 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.
|
||||
///
|
||||
|
|
|
|||
Loading…
Reference in New Issue