2020-06-07 21:27:38 -07:00
|
|
|
//! Block verification and chain state updates for Zebra.
|
|
|
|
//!
|
|
|
|
//! Verification occurs in multiple stages:
|
|
|
|
//! - getting blocks (disk- or network-bound)
|
|
|
|
//! - context-free verification of signatures, proofs, and scripts (CPU-bound)
|
|
|
|
//! - context-dependent verification of the chain state (awaits a verified parent block)
|
|
|
|
//!
|
|
|
|
//! Verification is provided via a `tower::Service`, to support backpressure and batch
|
|
|
|
//! verification.
|
|
|
|
|
2020-06-11 05:09:51 -07:00
|
|
|
use futures_util::FutureExt;
|
2020-06-07 21:27:38 -07:00
|
|
|
use std::{
|
2020-06-09 21:54:35 -07:00
|
|
|
error,
|
2020-06-07 21:27:38 -07:00
|
|
|
future::Future,
|
|
|
|
pin::Pin,
|
2020-06-11 16:46:44 -07:00
|
|
|
sync::Arc,
|
2020-06-07 21:27:38 -07:00
|
|
|
task::{Context, Poll},
|
|
|
|
};
|
|
|
|
use tower::{buffer::Buffer, Service};
|
|
|
|
|
2020-06-09 21:54:35 -07:00
|
|
|
use zebra_chain::block::{Block, BlockHeaderHash};
|
2020-06-07 21:27:38 -07:00
|
|
|
|
2020-06-09 21:09:11 -07:00
|
|
|
mod script;
|
2020-06-09 04:17:50 -07:00
|
|
|
mod transaction;
|
|
|
|
|
2020-06-11 16:46:44 -07:00
|
|
|
struct BlockVerifier<S> {
|
|
|
|
state_service: S,
|
2020-06-10 03:43:55 -07:00
|
|
|
}
|
2020-06-07 21:27:38 -07:00
|
|
|
|
|
|
|
/// The error type for the BlockVerifier Service.
|
|
|
|
// TODO(jlusby): Error = Report ?
|
2020-06-09 21:54:35 -07:00
|
|
|
type Error = Box<dyn error::Error + Send + Sync + 'static>;
|
2020-06-07 21:27:38 -07:00
|
|
|
|
|
|
|
/// The BlockVerifier service implementation.
|
2020-06-11 02:49:32 -07:00
|
|
|
///
|
|
|
|
/// After verification, blocks are added to the underlying state service.
|
2020-06-11 16:46:44 -07:00
|
|
|
impl<S> Service<Arc<Block>> for BlockVerifier<S>
|
2020-06-10 03:43:55 -07:00
|
|
|
where
|
2020-06-11 16:46:44 -07:00
|
|
|
S: Service<zebra_state::Request, Response = zebra_state::Response, Error = Error>,
|
|
|
|
S::Future: Send + 'static,
|
2020-06-10 03:43:55 -07:00
|
|
|
{
|
2020-06-11 16:46:44 -07:00
|
|
|
type Response = BlockHeaderHash;
|
2020-06-09 21:54:35 -07:00
|
|
|
type Error = Error;
|
2020-06-07 21:27:38 -07:00
|
|
|
type Future =
|
|
|
|
Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send + 'static>>;
|
|
|
|
|
|
|
|
fn poll_ready(&mut self, context: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
2020-06-10 03:43:55 -07:00
|
|
|
self.state_service.poll_ready(context)
|
2020-06-07 21:27:38 -07:00
|
|
|
}
|
|
|
|
|
2020-06-11 16:46:44 -07:00
|
|
|
fn call(&mut self, block: Arc<Block>) -> Self::Future {
|
2020-06-09 21:54:35 -07:00
|
|
|
// 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)
|
Get the round trip test to pass.
The reason the test failed is that the future returned by `call` on the state
service was immediately dropped, rather than being driven to completion.
Instead, we link the state update future with the verification future by
.awaiting it in an async block.
Note that the state update future is constructed outside of the async block
returned by the verification service. Why? Because calling
`self.state_service.call` requires mutable access to `state_service`, which we
only have in the body of the verification service's `call` method, and not
during execution of the async block it returns (which could happen at some
later point, or never).
In Tower's model, the `call` method itself only does the work necessary to
construct a future that represents completion of the service call, and the rest
of the work is done in the future itself. In particular, the fact that
`Service::call` takes `&mut self` means two things:
1. the service's state can be mutated while setting up the future, but not
during the future's subsequent execution,
2. any nested service calls made *by* the service *to* sub-services (e.g., the
verification service calling the state service) must either be made upfront,
while constructing the response future, or must be made to a clone of the
sub-service owned by the the response future.
2020-06-11 10:45:04 -07:00
|
|
|
|
|
|
|
// `state_service.call` is OK here because we already called
|
|
|
|
// `state_service.poll_ready` in our `poll_ready`.
|
2020-06-11 16:46:44 -07:00
|
|
|
let add_block = self
|
|
|
|
.state_service
|
|
|
|
.call(zebra_state::Request::AddBlock { block });
|
2020-06-07 21:27:38 -07:00
|
|
|
|
Get the round trip test to pass.
The reason the test failed is that the future returned by `call` on the state
service was immediately dropped, rather than being driven to completion.
Instead, we link the state update future with the verification future by
.awaiting it in an async block.
Note that the state update future is constructed outside of the async block
returned by the verification service. Why? Because calling
`self.state_service.call` requires mutable access to `state_service`, which we
only have in the body of the verification service's `call` method, and not
during execution of the async block it returns (which could happen at some
later point, or never).
In Tower's model, the `call` method itself only does the work necessary to
construct a future that represents completion of the service call, and the rest
of the work is done in the future itself. In particular, the fact that
`Service::call` takes `&mut self` means two things:
1. the service's state can be mutated while setting up the future, but not
during the future's subsequent execution,
2. any nested service calls made *by* the service *to* sub-services (e.g., the
verification service calling the state service) must either be made upfront,
while constructing the response future, or must be made to a clone of the
sub-service owned by the the response future.
2020-06-11 10:45:04 -07:00
|
|
|
async move {
|
2020-06-11 21:52:27 -07:00
|
|
|
match add_block.await? {
|
|
|
|
zebra_state::Response::Added { hash } => Ok(hash),
|
|
|
|
_ => Err("adding block to zebra-state failed".into()),
|
|
|
|
}
|
Get the round trip test to pass.
The reason the test failed is that the future returned by `call` on the state
service was immediately dropped, rather than being driven to completion.
Instead, we link the state update future with the verification future by
.awaiting it in an async block.
Note that the state update future is constructed outside of the async block
returned by the verification service. Why? Because calling
`self.state_service.call` requires mutable access to `state_service`, which we
only have in the body of the verification service's `call` method, and not
during execution of the async block it returns (which could happen at some
later point, or never).
In Tower's model, the `call` method itself only does the work necessary to
construct a future that represents completion of the service call, and the rest
of the work is done in the future itself. In particular, the fact that
`Service::call` takes `&mut self` means two things:
1. the service's state can be mutated while setting up the future, but not
during the future's subsequent execution,
2. any nested service calls made *by* the service *to* sub-services (e.g., the
verification service calling the state service) must either be made upfront,
while constructing the response future, or must be made to a clone of the
sub-service owned by the the response future.
2020-06-11 10:45:04 -07:00
|
|
|
}
|
|
|
|
.boxed()
|
2020-06-07 21:27:38 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-11 16:46:44 -07:00
|
|
|
/// Return a block verification service, using the provided state service.
|
2020-06-11 02:49:32 -07:00
|
|
|
///
|
2020-06-11 16:46:44 -07:00
|
|
|
/// The block verifier holds a state service of type `S`, used as context for
|
|
|
|
/// block validation and to which newly verified blocks will be committed. This
|
|
|
|
/// state is pluggable to allow for testing or instrumentation.
|
|
|
|
///
|
|
|
|
/// The returned type is opaque to allow instrumentation or other wrappers, but
|
|
|
|
/// can be boxed for storage. It is also `Clone` to allow sharing of a
|
|
|
|
/// verification service.
|
|
|
|
///
|
|
|
|
/// This function should be called only once for a particular state service (and
|
|
|
|
/// the result be shared) rather than constructing multiple verification services
|
|
|
|
/// backed by the same state layer.
|
|
|
|
pub fn init<S>(
|
|
|
|
state_service: S,
|
2020-06-10 03:43:55 -07:00
|
|
|
) -> impl Service<
|
2020-06-11 16:46:44 -07:00
|
|
|
Arc<Block>,
|
|
|
|
Response = BlockHeaderHash,
|
2020-06-09 21:54:35 -07:00
|
|
|
Error = Error,
|
2020-06-11 16:46:44 -07:00
|
|
|
Future = impl Future<Output = Result<BlockHeaderHash, Error>>,
|
2020-06-07 21:27:38 -07:00
|
|
|
> + Send
|
|
|
|
+ Clone
|
2020-06-10 03:43:55 -07:00
|
|
|
+ 'static
|
|
|
|
where
|
2020-06-11 16:46:44 -07:00
|
|
|
S: Service<zebra_state::Request, Response = zebra_state::Response, Error = Error>
|
|
|
|
+ Send
|
|
|
|
+ 'static,
|
|
|
|
S::Future: Send + 'static,
|
2020-06-10 03:43:55 -07:00
|
|
|
{
|
2020-06-11 16:46:44 -07:00
|
|
|
Buffer::new(BlockVerifier { state_service }, 1)
|
2020-06-07 21:27:38 -07:00
|
|
|
}
|
2020-06-11 02:49:32 -07:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
use color_eyre::Report;
|
2020-06-11 05:14:07 -07:00
|
|
|
use eyre::{bail, ensure, eyre};
|
2020-06-11 02:49:32 -07:00
|
|
|
use tower::{util::ServiceExt, Service};
|
|
|
|
use zebra_chain::serialization::ZcashDeserialize;
|
|
|
|
|
|
|
|
fn install_tracing() {
|
|
|
|
use tracing_error::ErrorLayer;
|
|
|
|
use tracing_subscriber::prelude::*;
|
|
|
|
use tracing_subscriber::{fmt, EnvFilter};
|
|
|
|
|
|
|
|
let fmt_layer = fmt::layer().with_target(false);
|
|
|
|
let filter_layer = EnvFilter::try_from_default_env()
|
|
|
|
.or_else(|_| EnvFilter::try_new("info"))
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
tracing_subscriber::registry()
|
|
|
|
.with(filter_layer)
|
|
|
|
.with(fmt_layer)
|
|
|
|
.with(ErrorLayer::default())
|
|
|
|
.init();
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
2020-06-11 05:14:07 -07:00
|
|
|
#[spandoc::spandoc]
|
2020-06-11 02:49:32 -07:00
|
|
|
async fn verify() -> Result<(), Report> {
|
2020-06-11 16:46:44 -07:00
|
|
|
let block =
|
|
|
|
Arc::<Block>::zcash_deserialize(&zebra_test_vectors::BLOCK_MAINNET_415000_BYTES[..])?;
|
|
|
|
let hash: BlockHeaderHash = block.as_ref().into();
|
2020-06-11 02:49:32 -07:00
|
|
|
|
|
|
|
let state_service = Box::new(zebra_state::in_memory::init());
|
|
|
|
let mut block_verifier = super::init(state_service);
|
|
|
|
|
|
|
|
let verify_response = block_verifier
|
|
|
|
.ready_and()
|
|
|
|
.await
|
|
|
|
.map_err(|e| eyre!(e))?
|
|
|
|
.call(block.clone())
|
|
|
|
.await
|
|
|
|
.map_err(|e| eyre!(e))?;
|
|
|
|
|
|
|
|
ensure!(
|
2020-06-11 16:46:44 -07:00
|
|
|
verify_response == hash,
|
2020-06-11 02:49:32 -07:00
|
|
|
"unexpected response kind: {:?}",
|
|
|
|
verify_response
|
|
|
|
);
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2020-06-11 05:14:07 -07:00
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
#[spandoc::spandoc]
|
|
|
|
async fn round_trip() -> Result<(), Report> {
|
2020-06-11 21:52:27 -07:00
|
|
|
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());
|
|
|
|
|
|
|
|
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),
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
#[spandoc::spandoc]
|
|
|
|
async fn verify_fail_add_block() -> Result<(), Report> {
|
2020-06-11 05:14:07 -07:00
|
|
|
install_tracing();
|
|
|
|
|
2020-06-11 16:46:44 -07:00
|
|
|
let block =
|
|
|
|
Arc::<Block>::zcash_deserialize(&zebra_test_vectors::BLOCK_MAINNET_415000_BYTES[..])?;
|
|
|
|
let hash: BlockHeaderHash = block.as_ref().into();
|
2020-06-11 05:14:07 -07:00
|
|
|
|
2020-06-11 16:46:44 -07:00
|
|
|
let mut state_service = zebra_state::in_memory::init();
|
|
|
|
let mut block_verifier = super::init(state_service.clone());
|
2020-06-11 05:14:07 -07:00
|
|
|
|
2020-06-11 21:52:27 -07:00
|
|
|
// Add the block for the first time
|
2020-06-11 05:14:07 -07:00
|
|
|
let verify_response = block_verifier
|
|
|
|
.ready_and()
|
|
|
|
.await
|
|
|
|
.map_err(|e| eyre!(e))?
|
|
|
|
.call(block.clone())
|
|
|
|
.await
|
|
|
|
.map_err(|e| eyre!(e))?;
|
|
|
|
|
|
|
|
ensure!(
|
2020-06-11 16:46:44 -07:00
|
|
|
verify_response == hash,
|
2020-06-11 05:14:07 -07:00
|
|
|
"unexpected response kind: {:?}",
|
|
|
|
verify_response
|
|
|
|
);
|
|
|
|
|
2020-06-11 16:46:44 -07:00
|
|
|
let state_response = state_service
|
2020-06-11 05:14:07 -07:00
|
|
|
.ready_and()
|
|
|
|
.await
|
|
|
|
.map_err(|e| eyre!(e))?
|
2020-06-11 16:46:44 -07:00
|
|
|
.call(zebra_state::Request::GetBlock { hash })
|
2020-06-11 05:14:07 -07:00
|
|
|
.await
|
|
|
|
.map_err(|e| eyre!(e))?;
|
|
|
|
|
|
|
|
match state_response {
|
|
|
|
zebra_state::Response::Block {
|
|
|
|
block: returned_block,
|
2020-06-11 16:46:44 -07:00
|
|
|
} => assert_eq!(block, returned_block),
|
2020-06-11 05:14:07 -07:00
|
|
|
_ => bail!("unexpected response kind: {:?}", state_response),
|
|
|
|
}
|
|
|
|
|
2020-06-11 21:52:27 -07:00
|
|
|
// 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),
|
|
|
|
}
|
|
|
|
|
2020-06-11 05:14:07 -07:00
|
|
|
Ok(())
|
|
|
|
}
|
2020-06-11 02:49:32 -07:00
|
|
|
}
|