network: correct data modeling for headers messages
We modeled a Bitcoin `headers` message as being a list of block headers. However, the actual data structure is slightly different: it's a list of (block header, transaction count) pairs. This caused zcashd to reject our headers messages. To fix this, introduce a new `CountedHeader` struct with a `block::Header` and transaction count `usize`, then thread it through the inbound service and the state. I tested this locally by running Zebra with these changes and inspecting a trace-level log of the span of a peer connection that requested a nontrivial headers packet from us, and verified that it did not reject our message.
This commit is contained in:
parent
dc77163524
commit
b449fe93b2
|
|
@ -18,7 +18,7 @@ use std::fmt;
|
||||||
|
|
||||||
pub use hash::Hash;
|
pub use hash::Hash;
|
||||||
pub use header::BlockTimeError;
|
pub use header::BlockTimeError;
|
||||||
pub use header::Header;
|
pub use header::{CountedHeader, Header};
|
||||||
pub use height::Height;
|
pub use height::Height;
|
||||||
pub use root_hash::RootHash;
|
pub use root_hash::RootHash;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -109,3 +109,12 @@ impl Header {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A header with a count of the number of transactions in its block.
|
||||||
|
///
|
||||||
|
/// This structure is used in the Bitcoin network protocol.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct CountedHeader {
|
||||||
|
pub header: Header,
|
||||||
|
pub transaction_count: usize,
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,17 @@
|
||||||
|
use std::{convert::TryInto, io};
|
||||||
|
|
||||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||||
use chrono::{TimeZone, Utc};
|
use chrono::{TimeZone, Utc};
|
||||||
use std::io;
|
|
||||||
|
|
||||||
use crate::serialization::ZcashDeserializeInto;
|
use crate::{
|
||||||
use crate::serialization::{ReadZcashExt, SerializationError, ZcashDeserialize, ZcashSerialize};
|
serialization::{
|
||||||
use crate::work::{difficulty::CompactDifficulty, equihash};
|
ReadZcashExt, SerializationError, WriteZcashExt, ZcashDeserialize, ZcashDeserializeInto,
|
||||||
|
ZcashSerialize,
|
||||||
|
},
|
||||||
|
work::{difficulty::CompactDifficulty, equihash},
|
||||||
|
};
|
||||||
|
|
||||||
use super::{merkle, Block, Hash, Header};
|
use super::{merkle, Block, CountedHeader, Hash, Header};
|
||||||
|
|
||||||
/// The maximum size of a Zcash block, in bytes.
|
/// The maximum size of a Zcash block, in bytes.
|
||||||
///
|
///
|
||||||
|
|
@ -79,6 +84,23 @@ impl ZcashDeserialize for Header {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ZcashSerialize for CountedHeader {
|
||||||
|
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
|
||||||
|
self.header.zcash_serialize(&mut writer)?;
|
||||||
|
writer.write_compactsize(self.transaction_count as u64)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ZcashDeserialize for CountedHeader {
|
||||||
|
fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
|
||||||
|
Ok(CountedHeader {
|
||||||
|
header: (&mut reader).zcash_deserialize_into()?,
|
||||||
|
transaction_count: reader.read_compactsize()?.try_into().unwrap(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ZcashSerialize for Block {
|
impl ZcashSerialize for Block {
|
||||||
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
|
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
|
||||||
// All block structs are validated when they are parsed.
|
// All block structs are validated when they are parsed.
|
||||||
|
|
|
||||||
|
|
@ -182,12 +182,10 @@ pub enum Message {
|
||||||
///
|
///
|
||||||
/// Returns block headers in response to a getheaders packet.
|
/// Returns block headers in response to a getheaders packet.
|
||||||
///
|
///
|
||||||
|
/// Each block header is accompanied by a transaction count.
|
||||||
|
///
|
||||||
/// [Bitcoin reference](https://en.bitcoin.it/wiki/Protocol_documentation#headers)
|
/// [Bitcoin reference](https://en.bitcoin.it/wiki/Protocol_documentation#headers)
|
||||||
// Note that the block headers in this packet include a
|
Headers(Vec<block::CountedHeader>),
|
||||||
// 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<block::Header>),
|
|
||||||
|
|
||||||
/// A `getdata` message.
|
/// A `getdata` message.
|
||||||
///
|
///
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ pub enum Response {
|
||||||
BlockHashes(Vec<block::Hash>),
|
BlockHashes(Vec<block::Hash>),
|
||||||
|
|
||||||
/// A list of block headers.
|
/// A list of block headers.
|
||||||
BlockHeaders(Vec<block::Header>),
|
BlockHeaders(Vec<block::CountedHeader>),
|
||||||
|
|
||||||
/// A list of transactions.
|
/// A list of transactions.
|
||||||
Transactions(Vec<Arc<Transaction>>),
|
Transactions(Vec<Arc<Transaction>>),
|
||||||
|
|
|
||||||
|
|
@ -40,5 +40,5 @@ pub enum Response {
|
||||||
BlockHashes(Vec<block::Hash>),
|
BlockHashes(Vec<block::Hash>),
|
||||||
|
|
||||||
/// The response to a `FindBlockHeaders` request.
|
/// The response to a `FindBlockHeaders` request.
|
||||||
BlockHeaders(Vec<block::Header>),
|
BlockHeaders(Vec<block::CountedHeader>),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -644,9 +644,13 @@ impl Service<Request> for StateService {
|
||||||
let res: Vec<_> = res
|
let res: Vec<_> = res
|
||||||
.iter()
|
.iter()
|
||||||
.map(|&hash| {
|
.map(|&hash| {
|
||||||
self.best_block(hash.into())
|
let block = self
|
||||||
.expect("block for found hash is in the best chain")
|
.best_block(hash.into())
|
||||||
.header
|
.expect("block for found hash is in the best chain");
|
||||||
|
block::CountedHeader {
|
||||||
|
transaction_count: block.transactions.len(),
|
||||||
|
header: block.header,
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
async move { Ok(Response::BlockHeaders(res)) }.boxed()
|
async move { Ok(Response::BlockHeaders(res)) }.boxed()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue