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
This commit is contained in:
Jane Lusby 2020-07-22 18:01:31 -07:00 committed by GitHub
parent 49aa41544d
commit 7d4e717182
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 115 additions and 19 deletions

View File

@ -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 {

View File

@ -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(())

View File

@ -60,6 +60,7 @@ impl Service<Request> 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<Request> 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()
}
}
}
}

View File

@ -40,12 +40,11 @@ impl BlockIndex {
.cloned()
}
pub(super) fn get_tip(&self) -> Option<BlockHeaderHash> {
pub(super) fn get_tip(&self) -> Option<Arc<Block>> {
self.by_height
.iter()
.next_back()
.map(|(_key, value)| value)
.map(|block| block.as_ref().into())
.map(|(_key, value)| value.clone())
}
}

View File

@ -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<Block>,
},
/// The response to a `GetBlockLocator` request
BlockLocator {
/// The set of blocks that make up the block locator
block_locator: Vec<BlockHeaderHash>,
},
/// 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<u32>,
),
}
/// Get the heights of the blocks for constructing a block_locator list
fn block_locator_heights(tip_height: BlockHeight) -> impl Iterator<Item = BlockHeight> {
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)))
}

View File

@ -161,6 +161,39 @@ impl Service<Request> 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::<Result<_, _>>()?;
Ok(Response::BlockLocator { block_locator })
}
.boxed()
}
}
}
}

View File

@ -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<Item = BlockHeight> {
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<dyn std::error::Error + Send + Sync + 'static>;