From 7d4e7171825cee458f9108db980361eca11fe95e Mon Sep 17 00:00:00 2001 From: Jane Lusby Date: Wed, 22 Jul 2020 18:01:31 -0700 Subject: [PATCH] Add block locator request to state layer (#712) * Add block locator request to state layer * pass genesis in request * Update zebrad/src/commands/start/sync.rs * fix errors --- zebra-chain/src/block.rs | 5 ++++ zebra-consensus/src/redjubjub/tests.rs | 4 +-- zebra-state/src/in_memory.rs | 30 +++++++++++++++++++++ zebra-state/src/in_memory/block_index.rs | 5 ++-- zebra-state/src/lib.rs | 25 ++++++++++++++++-- zebra-state/src/on_disk.rs | 33 ++++++++++++++++++++++++ zebrad/src/commands/start/sync.rs | 32 ++++++++++++++--------- 7 files changed, 115 insertions(+), 19 deletions(-) diff --git a/zebra-chain/src/block.rs b/zebra-chain/src/block.rs index ca52157ba..4bd485a46 100644 --- a/zebra-chain/src/block.rs +++ b/zebra-chain/src/block.rs @@ -84,6 +84,11 @@ impl Block { Err("no coinbase transaction in block")? } } + + /// Get the hash for the current block + pub fn hash(&self) -> BlockHeaderHash { + BlockHeaderHash::from(self) + } } impl<'a> From<&'a Block> for BlockHeaderHash { diff --git a/zebra-consensus/src/redjubjub/tests.rs b/zebra-consensus/src/redjubjub/tests.rs index 4bb14bf56..ece7abda1 100644 --- a/zebra-consensus/src/redjubjub/tests.rs +++ b/zebra-consensus/src/redjubjub/tests.rs @@ -54,7 +54,7 @@ async fn batch_flushes_on_max_items() -> Result<()> { // flushing is happening based on hitting max_items. let verifier = Batch::new(Verifier::new(), 10, Duration::from_secs(1000)); timeout(Duration::from_secs(5), sign_and_verify(verifier, 100)) - .await + .await? .map_err(|e| eyre!(e))?; Ok(()) @@ -69,7 +69,7 @@ async fn batch_flushes_on_max_latency() -> Result<()> { // flushing is happening based on hitting max_latency. let verifier = Batch::new(Verifier::new(), 100, Duration::from_millis(500)); timeout(Duration::from_secs(5), sign_and_verify(verifier, 10)) - .await + .await? .map_err(|e| eyre!(e))?; Ok(()) diff --git a/zebra-state/src/in_memory.rs b/zebra-state/src/in_memory.rs index 06c9b2515..12e0e5f82 100644 --- a/zebra-state/src/in_memory.rs +++ b/zebra-state/src/in_memory.rs @@ -60,6 +60,7 @@ impl Service for InMemoryState { let result = self .index .get_tip() + .map(|block| block.hash()) .map(|hash| Response::Tip { hash }) .ok_or_else(|| "zebra-state contains no blocks".into()); @@ -75,6 +76,35 @@ impl Service for InMemoryState { } .boxed() } + Request::GetBlockLocator { genesis } => { + let tip = self.index.get_tip(); + let tip = match tip { + Some(tip) => tip, + None => { + return async move { + Ok(Response::BlockLocator { + block_locator: vec![genesis], + }) + } + .boxed() + } + }; + + let tip_height = tip + .coinbase_height() + .expect("tip block will have a coinbase height"); + + let block_locator = crate::block_locator_heights(tip_height) + .map(|height| { + self.index + .get(height) + .expect("there should be no holes in the chain") + .hash() + }) + .collect(); + + async move { Ok(Response::BlockLocator { block_locator }) }.boxed() + } } } } diff --git a/zebra-state/src/in_memory/block_index.rs b/zebra-state/src/in_memory/block_index.rs index 885b24af2..b08e2f3c1 100644 --- a/zebra-state/src/in_memory/block_index.rs +++ b/zebra-state/src/in_memory/block_index.rs @@ -40,12 +40,11 @@ impl BlockIndex { .cloned() } - pub(super) fn get_tip(&self) -> Option { + pub(super) fn get_tip(&self) -> Option> { self.by_height .iter() .next_back() - .map(|(_key, value)| value) - .map(|block| block.as_ref().into()) + .map(|(_key, value)| value.clone()) } } diff --git a/zebra-state/src/lib.rs b/zebra-state/src/lib.rs index bf6b27323..efbe8f80c 100644 --- a/zebra-state/src/lib.rs +++ b/zebra-state/src/lib.rs @@ -16,8 +16,11 @@ #![allow(clippy::try_err)] use serde::{Deserialize, Serialize}; use std::path::PathBuf; -use std::sync::Arc; -use zebra_chain::block::{Block, BlockHeaderHash}; +use std::{iter, sync::Arc}; +use zebra_chain::{ + block::{Block, BlockHeaderHash}, + types::BlockHeight, +}; pub mod in_memory; pub mod on_disk; @@ -58,6 +61,11 @@ pub enum Request { /// The hash used to identify the block hash: BlockHeaderHash, }, + /// Get a block locator list for the current best chain + GetBlockLocator { + /// The genesis block of the current best chain + genesis: BlockHeaderHash, + }, /// Get the block that is the tip of the current chain GetTip, /// Ask the state if the given hash is part of the current best chain @@ -81,6 +89,11 @@ pub enum Response { /// The block that was requested block: Arc, }, + /// The response to a `GetBlockLocator` request + BlockLocator { + /// The set of blocks that make up the block locator + block_locator: Vec, + }, /// The response to a `GetTip` request Tip { /// The hash of the block at the tip of the current chain @@ -93,3 +106,11 @@ pub enum Response { Option, ), } + +/// Get the heights of the blocks for constructing a block_locator list +fn block_locator_heights(tip_height: BlockHeight) -> impl Iterator { + iter::successors(Some(1u32), |h| h.checked_mul(2)) + .flat_map(move |step| tip_height.0.checked_sub(step)) + .map(BlockHeight) + .chain(iter::once(BlockHeight(0))) +} diff --git a/zebra-state/src/on_disk.rs b/zebra-state/src/on_disk.rs index 85dcc2330..1192882ed 100644 --- a/zebra-state/src/on_disk.rs +++ b/zebra-state/src/on_disk.rs @@ -161,6 +161,39 @@ impl Service for SledState { } .boxed() } + Request::GetBlockLocator { genesis } => { + let storage = self.clone(); + + async move { + let tip = match storage.get_tip()? { + Some(tip) => tip, + None => { + return Ok(Response::BlockLocator { + block_locator: vec![genesis], + }) + } + }; + + let tip_height = tip + .coinbase_height() + .expect("tip of the current chain will have a coinbase height"); + + let heights = crate::block_locator_heights(tip_height); + + let block_locator = heights + .map(|height| { + storage.get(height).map(|block| { + block + .expect("there should be no holes in the current chain") + .hash() + }) + }) + .collect::>()?; + + Ok(Response::BlockLocator { block_locator }) + } + .boxed() + } } } } diff --git a/zebrad/src/commands/start/sync.rs b/zebrad/src/commands/start/sync.rs index 40be76810..738fa252d 100644 --- a/zebrad/src/commands/start/sync.rs +++ b/zebrad/src/commands/start/sync.rs @@ -8,7 +8,7 @@ use tracing_futures::{Instrument, Instrumented}; use zebra_chain::{ block::{Block, BlockHeaderHash}, - types::BlockHeight, + Network, }; use zebra_consensus::checkpoint; use zebra_network::{self as zn, RetryLimit}; @@ -118,8 +118,25 @@ where // Query the current state to construct the sequence of hashes: handled by // the caller // - // TODO(jlusby): get the block_locator from the state - let block_locator = vec![super::GENESIS]; + // TODO(teor): get the real network + let network = Network::Mainnet; + let block_locator = self + .state + .ready_and() + .await + .map_err(|e| eyre!(e))? + .call(zebra_state::Request::GetBlockLocator { + genesis: zebra_consensus::parameters::genesis_hash(network), + }) + .await + .map(|response| match response { + zebra_state::Response::BlockLocator { block_locator } => block_locator, + _ => unreachable!( + "GetBlockLocator request can only result in Response::BlockLocator" + ), + }) + .map_err(|e| eyre!(e))?; + tracing::info!(?block_locator, "trying to obtain new chain tips"); // ObtainTips Step 2 @@ -349,13 +366,4 @@ where } } -/// Get the heights of the blocks for constructing a block_locator list -#[allow(dead_code)] -pub fn block_locator_heights(tip_height: BlockHeight) -> impl Iterator { - iter::successors(Some(1u32), |h| h.checked_mul(2)) - .flat_map(move |step| tip_height.0.checked_sub(step)) - .map(BlockHeight) - .chain(iter::once(BlockHeight(0))) -} - type Error = Box;