2020-10-16 16:44:30 -07:00
|
|
|
//! Consensus-based block verification.
|
2020-07-09 23:51:01 -07:00
|
|
|
//!
|
2020-10-16 16:44:30 -07:00
|
|
|
//! In contrast to checkpoint verification, which only checks hardcoded
|
|
|
|
//! hashes, block verification checks all Zcash consensus rules.
|
2020-07-09 23:51:01 -07:00
|
|
|
//!
|
2020-10-16 16:44:30 -07:00
|
|
|
//! The block verifier performs all of the semantic validation checks.
|
|
|
|
//! If accepted, the block is sent to the state service for contextual
|
|
|
|
//! verification, where it may be accepted or rejected.
|
2020-07-09 23:51:01 -07:00
|
|
|
|
|
|
|
use std::{
|
2020-11-20 19:47:30 -08:00
|
|
|
collections::HashMap,
|
2020-07-09 23:51:01 -07:00
|
|
|
future::Future,
|
|
|
|
pin::Pin,
|
|
|
|
sync::Arc,
|
|
|
|
task::{Context, Poll},
|
|
|
|
};
|
2020-09-09 18:53:40 -07:00
|
|
|
|
|
|
|
use chrono::Utc;
|
2020-10-16 15:17:49 -07:00
|
|
|
use futures::stream::FuturesUnordered;
|
2020-09-09 18:53:40 -07:00
|
|
|
use futures_util::FutureExt;
|
2020-09-21 11:54:06 -07:00
|
|
|
use thiserror::Error;
|
2020-09-09 20:16:11 -07:00
|
|
|
use tower::{Service, ServiceExt};
|
2020-11-19 16:55:36 -08:00
|
|
|
use tracing::Instrument;
|
2020-07-09 23:51:01 -07:00
|
|
|
|
2020-09-21 11:54:06 -07:00
|
|
|
use zebra_chain::{
|
|
|
|
block::{self, Block},
|
2020-10-12 13:54:48 -07:00
|
|
|
parameters::Network,
|
2020-10-16 15:17:49 -07:00
|
|
|
parameters::NetworkUpgrade,
|
2020-11-20 19:47:30 -08:00
|
|
|
transparent,
|
2020-09-21 11:54:06 -07:00
|
|
|
work::equihash,
|
|
|
|
};
|
2020-09-09 18:53:40 -07:00
|
|
|
use zebra_state as zs;
|
|
|
|
|
2020-10-26 23:42:27 -07:00
|
|
|
use crate::{error::*, transaction};
|
2020-10-16 15:17:49 -07:00
|
|
|
use crate::{script, BoxError};
|
2020-09-09 18:53:40 -07:00
|
|
|
|
|
|
|
mod check;
|
2020-10-12 13:54:48 -07:00
|
|
|
mod subsidy;
|
2020-09-09 18:53:40 -07:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests;
|
2020-07-09 23:51:01 -07:00
|
|
|
|
2020-10-16 16:44:30 -07:00
|
|
|
/// Asynchronous block verification.
|
2020-07-30 17:21:20 -07:00
|
|
|
#[derive(Debug)]
|
2020-10-16 15:17:49 -07:00
|
|
|
pub struct BlockVerifier<S> {
|
2020-10-12 13:54:48 -07:00
|
|
|
/// The network to be verified.
|
|
|
|
network: Network,
|
2020-07-09 23:51:01 -07:00
|
|
|
state_service: S,
|
2020-10-16 15:17:49 -07:00
|
|
|
transaction_verifier: transaction::Verifier<S>,
|
2020-07-09 23:51:01 -07:00
|
|
|
}
|
|
|
|
|
2020-10-26 23:42:27 -07:00
|
|
|
// TODO: dedupe with crate::error::BlockError
|
2020-09-21 11:54:06 -07:00
|
|
|
#[non_exhaustive]
|
|
|
|
#[derive(Debug, Error)]
|
|
|
|
pub enum VerifyBlockError {
|
|
|
|
#[error("unable to verify depth for block {hash} from chain state during block verification")]
|
|
|
|
Depth { source: BoxError, hash: block::Hash },
|
2020-10-26 23:42:27 -07:00
|
|
|
|
2020-09-21 11:54:06 -07:00
|
|
|
#[error(transparent)]
|
|
|
|
Block {
|
|
|
|
#[from]
|
|
|
|
source: BlockError,
|
|
|
|
},
|
2020-10-26 23:42:27 -07:00
|
|
|
|
2020-09-21 11:54:06 -07:00
|
|
|
#[error(transparent)]
|
|
|
|
Equihash {
|
|
|
|
#[from]
|
|
|
|
source: equihash::Error,
|
|
|
|
},
|
2020-10-26 23:42:27 -07:00
|
|
|
|
2020-09-21 11:54:06 -07:00
|
|
|
#[error(transparent)]
|
2020-10-12 14:33:32 -07:00
|
|
|
Time(zebra_chain::block::BlockTimeError),
|
2020-10-26 23:42:27 -07:00
|
|
|
|
2020-09-21 11:54:06 -07:00
|
|
|
#[error("unable to commit block after semantic verification")]
|
|
|
|
Commit(#[source] BoxError),
|
2020-10-26 23:42:27 -07:00
|
|
|
|
2020-10-16 15:17:49 -07:00
|
|
|
#[error("invalid transaction")]
|
2020-10-26 23:42:27 -07:00
|
|
|
Transaction(#[source] TransactionError),
|
2020-09-21 11:54:06 -07:00
|
|
|
}
|
|
|
|
|
2020-09-09 20:16:11 -07:00
|
|
|
impl<S> BlockVerifier<S>
|
|
|
|
where
|
|
|
|
S: Service<zs::Request, Response = zs::Response, Error = BoxError> + Send + Clone + 'static,
|
|
|
|
S::Future: Send + 'static,
|
|
|
|
{
|
2020-10-12 13:54:48 -07:00
|
|
|
pub fn new(network: Network, state_service: S) -> Self {
|
2020-10-16 15:17:49 -07:00
|
|
|
let branch = NetworkUpgrade::Sapling.branch_id().unwrap();
|
|
|
|
let script_verifier = script::Verifier::new(state_service.clone(), branch);
|
|
|
|
let transaction_verifier = transaction::Verifier::new(script_verifier);
|
|
|
|
|
2020-10-12 13:54:48 -07:00
|
|
|
Self {
|
|
|
|
network,
|
|
|
|
state_service,
|
2020-10-16 15:17:49 -07:00
|
|
|
transaction_verifier,
|
2020-10-12 13:54:48 -07:00
|
|
|
}
|
2020-09-09 20:16:11 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-09 23:51:01 -07:00
|
|
|
impl<S> Service<Arc<Block>> for BlockVerifier<S>
|
|
|
|
where
|
2020-09-09 18:53:40 -07:00
|
|
|
S: Service<zs::Request, Response = zs::Response, Error = BoxError> + Send + Clone + 'static,
|
2020-07-09 23:51:01 -07:00
|
|
|
S::Future: Send + 'static,
|
|
|
|
{
|
2020-08-15 23:20:01 -07:00
|
|
|
type Response = block::Hash;
|
2020-09-21 11:54:06 -07:00
|
|
|
type Error = VerifyBlockError;
|
2020-07-09 23:51:01 -07:00
|
|
|
type Future =
|
|
|
|
Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send + 'static>>;
|
|
|
|
|
|
|
|
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
2020-07-20 18:31:01 -07:00
|
|
|
// We use the state for contextual verification, and we expect those
|
|
|
|
// queries to be fast. So we don't need to call
|
|
|
|
// `state_service.poll_ready()` here.
|
2020-07-09 23:51:01 -07:00
|
|
|
Poll::Ready(Ok(()))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn call(&mut self, block: Arc<Block>) -> Self::Future {
|
2020-09-02 21:23:57 -07:00
|
|
|
let mut state_service = self.state_service.clone();
|
2020-10-16 15:17:49 -07:00
|
|
|
let mut transaction_verifier = self.transaction_verifier.clone();
|
2020-10-12 13:54:48 -07:00
|
|
|
let network = self.network;
|
2020-07-09 23:51:01 -07:00
|
|
|
|
2020-11-19 16:55:36 -08:00
|
|
|
// We don't include the block hash, because it's likely already in a parent span
|
|
|
|
let span = tracing::debug_span!("block", height = ?block.coinbase_height());
|
|
|
|
|
2020-07-23 20:17:39 -07:00
|
|
|
// TODO(jlusby): Error = Report, handle errors from state_service.
|
2020-07-09 23:51:01 -07:00
|
|
|
async move {
|
2020-11-19 16:55:36 -08:00
|
|
|
let hash = block.hash();
|
2020-09-09 18:53:40 -07:00
|
|
|
// Check that this block is actually a new block.
|
2020-11-20 15:17:36 -08:00
|
|
|
tracing::trace!("checking that block is not already in state");
|
2020-09-21 11:54:06 -07:00
|
|
|
match state_service
|
|
|
|
.ready_and()
|
|
|
|
.await
|
|
|
|
.map_err(|source| VerifyBlockError::Depth { source, hash })?
|
|
|
|
.call(zs::Request::Depth(hash))
|
|
|
|
.await
|
|
|
|
.map_err(|source| VerifyBlockError::Depth { source, hash })?
|
|
|
|
{
|
2020-09-09 18:53:40 -07:00
|
|
|
zs::Response::Depth(Some(depth)) => {
|
2020-09-21 11:54:06 -07:00
|
|
|
return Err(BlockError::AlreadyInChain(hash, depth).into())
|
|
|
|
}
|
|
|
|
zs::Response::Depth(None) => {}
|
2020-09-09 18:53:40 -07:00
|
|
|
_ => unreachable!("wrong response to Request::Depth"),
|
|
|
|
}
|
|
|
|
|
2020-11-20 15:17:36 -08:00
|
|
|
tracing::trace!("performing block checks");
|
2020-08-05 22:26:26 -07:00
|
|
|
let height = block
|
|
|
|
.coinbase_height()
|
2020-09-28 18:10:51 -07:00
|
|
|
.ok_or(BlockError::MissingHeight(hash))?;
|
2020-08-16 11:42:02 -07:00
|
|
|
if height > block::Height::MAX {
|
2020-09-21 11:54:06 -07:00
|
|
|
Err(BlockError::MaxHeight(height, hash, block::Height::MAX))?;
|
2020-08-05 22:26:26 -07:00
|
|
|
}
|
|
|
|
|
2020-08-03 00:37:08 -07:00
|
|
|
// Do the difficulty checks first, to raise the threshold for
|
|
|
|
// attacks that use any other fields.
|
2020-10-12 15:17:40 -07:00
|
|
|
check::difficulty_is_valid(&block.header, network, &height, &hash)?;
|
2020-10-12 13:54:48 -07:00
|
|
|
check::equihash_solution_is_valid(&block.header)?;
|
2020-08-03 00:37:08 -07:00
|
|
|
|
2020-07-09 23:51:01 -07:00
|
|
|
// Since errors cause an early exit, try to do the
|
|
|
|
// quick checks first.
|
|
|
|
|
2020-08-03 00:37:08 -07:00
|
|
|
// Field validity and structure checks
|
2020-07-09 23:51:01 -07:00
|
|
|
let now = Utc::now();
|
2020-10-12 14:33:32 -07:00
|
|
|
check::time_is_valid_at(&block.header, now, &height, &hash)
|
|
|
|
.map_err(VerifyBlockError::Time)?;
|
2020-10-12 13:54:48 -07:00
|
|
|
check::coinbase_is_first(&block)?;
|
2020-10-12 14:41:24 -07:00
|
|
|
check::subsidy_is_valid(&block, network)?;
|
2020-07-09 23:51:01 -07:00
|
|
|
|
2020-09-02 19:50:16 -07:00
|
|
|
// TODO: context-free header verification: merkle root
|
2020-08-03 00:37:08 -07:00
|
|
|
|
2020-10-16 15:17:49 -07:00
|
|
|
let mut async_checks = FuturesUnordered::new();
|
|
|
|
|
2020-11-20 19:47:30 -08:00
|
|
|
let known_utxos = known_utxos(&block);
|
2020-10-16 15:17:49 -07:00
|
|
|
for transaction in &block.transactions {
|
|
|
|
let rsp = transaction_verifier
|
|
|
|
.ready_and()
|
|
|
|
.await
|
|
|
|
.expect("transaction verifier is always ready")
|
2020-11-20 19:47:30 -08:00
|
|
|
.call(transaction::Request::Block {
|
|
|
|
transaction: transaction.clone(),
|
|
|
|
known_utxos: known_utxos.clone(),
|
|
|
|
});
|
2020-10-16 15:17:49 -07:00
|
|
|
async_checks.push(rsp);
|
|
|
|
}
|
2020-11-20 15:17:36 -08:00
|
|
|
tracing::trace!(len = async_checks.len(), "built async tx checks");
|
2020-10-16 15:17:49 -07:00
|
|
|
|
|
|
|
use futures::StreamExt;
|
|
|
|
while let Some(result) = async_checks.next().await {
|
2020-11-20 15:17:36 -08:00
|
|
|
tracing::trace!(?result, remaining = async_checks.len());
|
2020-10-16 15:17:49 -07:00
|
|
|
result.map_err(VerifyBlockError::Transaction)?;
|
|
|
|
}
|
2020-10-26 06:11:52 -07:00
|
|
|
|
2020-10-26 05:38:35 -07:00
|
|
|
// Update the metrics after all the validation is finished
|
2020-10-26 05:32:57 -07:00
|
|
|
tracing::trace!("verified block");
|
|
|
|
metrics::gauge!("block.verified.block.height", height.0 as _);
|
|
|
|
metrics::counter!("block.verified.block.count", 1);
|
2020-10-16 15:17:49 -07:00
|
|
|
|
2020-09-09 18:53:40 -07:00
|
|
|
// Finally, submit the block for contextual verification.
|
2020-09-10 10:34:59 -07:00
|
|
|
match state_service
|
|
|
|
.ready_and()
|
2020-09-21 11:54:06 -07:00
|
|
|
.await
|
|
|
|
.map_err(VerifyBlockError::Commit)?
|
2020-09-10 10:34:59 -07:00
|
|
|
.call(zs::Request::CommitBlock { block })
|
2020-09-21 11:54:06 -07:00
|
|
|
.await
|
|
|
|
.map_err(VerifyBlockError::Commit)?
|
2020-09-10 10:34:59 -07:00
|
|
|
{
|
2020-09-09 18:53:40 -07:00
|
|
|
zs::Response::Committed(committed_hash) => {
|
2020-09-10 10:34:59 -07:00
|
|
|
assert_eq!(committed_hash, hash, "state must commit correct hash");
|
2020-09-02 21:23:57 -07:00
|
|
|
Ok(hash)
|
|
|
|
}
|
2020-09-10 10:34:59 -07:00
|
|
|
_ => unreachable!("wrong response for CommitBlock"),
|
2020-09-02 21:23:57 -07:00
|
|
|
}
|
2020-07-09 23:51:01 -07:00
|
|
|
}
|
2020-11-19 16:55:36 -08:00
|
|
|
.instrument(span)
|
2020-07-09 23:51:01 -07:00
|
|
|
.boxed()
|
|
|
|
}
|
|
|
|
}
|
2020-11-20 19:47:30 -08:00
|
|
|
|
|
|
|
fn known_utxos(block: &Block) -> Arc<HashMap<transparent::OutPoint, transparent::Output>> {
|
|
|
|
let mut map = HashMap::default();
|
|
|
|
for transaction in &block.transactions {
|
|
|
|
let hash = transaction.hash();
|
|
|
|
for (index, output) in transaction.outputs().iter().cloned().enumerate() {
|
|
|
|
let index = index as u32;
|
|
|
|
map.insert(transparent::OutPoint { hash, index }, output);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Arc::new(map)
|
|
|
|
}
|