state: Make Response::Added return the block header hash

Move block header hashing from zebra-consensus to zebra-state.
Handle zebra-state AddBlock errors in zebra-consensus BlockVerifier.
Add unit tests for BlockVerifier state error handling.

Part of #428.
This commit is contained in:
teor 2020-06-12 14:52:27 +10:00 committed by Henry de Valence
parent cbb50ea506
commit 26a58b23de
3 changed files with 95 additions and 16 deletions

View File

@ -49,14 +49,10 @@ where
}
fn call(&mut self, block: Arc<Block>) -> Self::Future {
let header_hash: BlockHeaderHash = block.as_ref().into();
// Ignore errors for now.
// TODO(jlusby): Error = Report, handle errors from state_service.
// TODO(teor):
// - handle chain reorgs, adjust state_service "unique block height" conditions
// - handle block validation errors (including errors in the block's transactions)
// - handle state_service AddBlock errors, and add unit tests for those errors
// `state_service.call` is OK here because we already called
// `state_service.poll_ready` in our `poll_ready`.
@ -65,8 +61,10 @@ where
.call(zebra_state::Request::AddBlock { block });
async move {
add_block.await?;
Ok(header_hash)
match add_block.await? {
zebra_state::Response::Added { hash } => Ok(hash),
_ => Err("adding block to zebra-state failed".into()),
}
}
.boxed()
}
@ -159,8 +157,6 @@ mod tests {
#[tokio::test]
#[spandoc::spandoc]
async fn round_trip() -> Result<(), Report> {
install_tracing();
let block =
Arc::<Block>::zcash_deserialize(&zebra_test_vectors::BLOCK_MAINNET_415000_BYTES[..])?;
let hash: BlockHeaderHash = block.as_ref().into();
@ -199,4 +195,84 @@ mod tests {
Ok(())
}
#[tokio::test]
#[spandoc::spandoc]
async fn verify_fail_add_block() -> Result<(), Report> {
install_tracing();
let block =
Arc::<Block>::zcash_deserialize(&zebra_test_vectors::BLOCK_MAINNET_415000_BYTES[..])?;
let hash: BlockHeaderHash = block.as_ref().into();
let mut state_service = zebra_state::in_memory::init();
let mut block_verifier = super::init(state_service.clone());
// Add the block for the first time
let verify_response = block_verifier
.ready_and()
.await
.map_err(|e| eyre!(e))?
.call(block.clone())
.await
.map_err(|e| eyre!(e))?;
ensure!(
verify_response == hash,
"unexpected response kind: {:?}",
verify_response
);
let state_response = state_service
.ready_and()
.await
.map_err(|e| eyre!(e))?
.call(zebra_state::Request::GetBlock { hash })
.await
.map_err(|e| eyre!(e))?;
match state_response {
zebra_state::Response::Block {
block: returned_block,
} => assert_eq!(block, returned_block),
_ => bail!("unexpected response kind: {:?}", state_response),
}
// Now try to add the block again, verify should fail
// TODO(teor): ignore duplicate block verifies?
let verify_result = block_verifier
.ready_and()
.await
.map_err(|e| eyre!(e))?
.call(block.clone())
.await;
ensure!(
match verify_result {
Ok(_) => false,
// TODO(teor || jlusby): check error string
_ => true,
},
"unexpected result kind: {:?}",
verify_result
);
// But the state should still return the original block we added
let state_response = state_service
.ready_and()
.await
.map_err(|e| eyre!(e))?
.call(zebra_state::Request::GetBlock { hash })
.await
.map_err(|e| eyre!(e))?;
match state_response {
zebra_state::Response::Block {
block: returned_block,
} => assert_eq!(block, returned_block),
_ => bail!("unexpected response kind: {:?}", state_response),
}
Ok(())
}
}

View File

@ -30,7 +30,8 @@ impl Service<Request> for ZebraState {
fn call(&mut self, req: Request) -> Self::Future {
match req {
Request::AddBlock { block } => {
let result = self.index.insert(block).map(|_| Response::Added);
let hash = block.as_ref().into();
let result = self.index.insert(block).map(|_| Response::Added { hash });
async { result }.boxed()
}

View File

@ -6,7 +6,7 @@ use zebra_chain::block::{Block, BlockHeaderHash};
pub mod in_memory;
#[derive(Debug)]
#[derive(Debug, PartialEq)]
pub enum Request {
// TODO(jlusby): deprecate in the future based on our validation story
AddBlock { block: Arc<Block> },
@ -14,9 +14,9 @@ pub enum Request {
GetTip,
}
#[derive(Debug)]
#[derive(Debug, PartialEq)]
pub enum Response {
Added,
Added { hash: BlockHeaderHash },
Block { block: Arc<Block> },
Tip { hash: BlockHeaderHash },
}
@ -62,7 +62,7 @@ mod tests {
.map_err(|e| eyre!(e))?;
ensure!(
matches!(response, Response::Added),
response == Response::Added { hash },
"unexpected response kind: {:?}",
response
);
@ -92,7 +92,9 @@ mod tests {
let block1: Arc<_> =
Block::zcash_deserialize(&zebra_test_vectors::BLOCK_MAINNET_1_BYTES[..])?.into();
let expected_hash: BlockHeaderHash = block1.as_ref().into();
let block0_hash: BlockHeaderHash = block0.as_ref().into();
let block1_hash: BlockHeaderHash = block1.as_ref().into();
let expected_hash: BlockHeaderHash = block1_hash;
let mut service = in_memory::init();
@ -103,7 +105,7 @@ mod tests {
.map_err(|e| eyre!(e))?;
ensure!(
matches!(response, Response::Added),
response == Response::Added { hash: block1_hash },
"unexpected response kind: {:?}",
response
);
@ -117,7 +119,7 @@ mod tests {
.map_err(|e| eyre!(e))?;
ensure!(
matches!(response, Response::Added),
response == Response::Added { hash: block0_hash },
"unexpected response kind: {:?}",
response
);