Add a FindBlocks request to get initial block hashes.

Bitcoin does this either with `getblocks` (returns up to 500 following block
hashes) or `getheaders` (returns up to 2000 following block headers, not
just hashes).  However, Bitcoin headers are much smaller than Zcash
headers, which contain a giant Equihash solution block, and many Zcash
blocks don't have many transactions in them, so the block header is
often similarly sized to the block itself.  Because we're
aiming to have a highly parallel network layer, it seems better to use
`getblocks` to implement `FindBlocks` (which is necessarily sequential)
and parallelize the processing of the block downloads.
This commit is contained in:
Henry de Valence 2020-02-08 10:49:35 -08:00 committed by Deirdre Connolly
parent 47cafc630f
commit 7049f9d891
5 changed files with 53 additions and 4 deletions

View File

@ -18,7 +18,7 @@ use zebra_chain::{
use crate::{
constants,
protocol::{
external::{types::Nonce, Message},
external::{types::Nonce, InventoryHash, Message},
internal::{Request, Response},
},
BoxedStdError,
@ -35,6 +35,7 @@ pub(super) enum Handler {
hashes: HashSet<BlockHeaderHash>,
blocks: Vec<Block>,
},
FindBlocks,
}
impl Handler {
@ -81,6 +82,15 @@ impl Handler {
Finished(Err(Arc::new(PeerError::WrongBlock).into()))
}
}
(FindBlocks, Message::Inv(inv_hashes)) => Finished(Ok(Response::BlockHeaderHashes(
inv_hashes
.into_iter()
.filter_map(|inv| match inv {
InventoryHash::Block(hash) => Some(hash),
_ => None,
})
.collect(),
))),
// By default, messages are not responses.
(state, msg) => {
ignored_msg = Some(msg);
@ -317,6 +327,15 @@ where
tx,
)
}),
(AwaitingRequest, FindBlocks { known_blocks, stop }) => self
.peer_tx
.send(Message::GetBlocks {
block_locator_hashes: known_blocks,
hash_stop: stop.unwrap_or(BlockHeaderHash([0; 32])),
})
.await
.map_err(|e| e.into())
.map(|()| AwaitingResponse(Handler::FindBlocks, tx)),
} {
Ok(new_state) => {
self.state = new_state;
@ -421,6 +440,15 @@ where
}
}
}
Response::BlockHeaderHashes(hashes) => {
if let Err(e) = self
.peer_tx
.send(Message::Inv(hashes.into_iter().map(Into::into).collect()))
.await
{
self.fail_with(e.into())
}
}
}
}
}

View File

@ -2,7 +2,7 @@ use std::sync::{Arc, Mutex};
use thiserror::Error;
use zebra_chain::{serialization::SerializationError};
use zebra_chain::serialization::SerializationError;
/// A wrapper around `Arc<PeerError>` that implements `Error`.
#[derive(Error, Debug, Clone)]

View File

@ -28,4 +28,13 @@ pub enum Request {
/// didn't start with a `Vec` but with, e.g., an iterator, they can collect
/// directly into a `HashSet` and save work.
BlocksByHash(HashSet<BlockHeaderHash>),
/// Request block hashes of subsequent blocks in the chain, giving hashes of
/// known blocks.
FindBlocks {
/// Hashes of known blocks, ordered from highest height to lowest height.
known_blocks: Vec<BlockHeaderHash>,
/// Optionally, the last header to request.
stop: Option<BlockHeaderHash>,
},
}

View File

@ -1,5 +1,5 @@
// XXX clean module layout of zebra_chain
use zebra_chain::block::Block;
use zebra_chain::block::{Block, BlockHeaderHash};
use crate::meta_addr::MetaAddr;
@ -14,4 +14,7 @@ pub enum Response {
/// A list of blocks.
Blocks(Vec<Block>),
}
/// A list of block hashes.
BlockHeaderHashes(Vec<BlockHeaderHash>),
}

View File

@ -112,6 +112,15 @@ impl ConnectCmd {
info!(?rsp);
}
peer_set.ready().await.unwrap();
let mut rsp = peer_set
.call(Request::FindBlocks {
known_blocks: Vec::new(),
stop: None,
})
.await;
info!(?rsp);
let eternity = future::pending::<()>();
eternity.await;