From d7d15984ebcdfe27e1b6caeb8309824ad81e4125 Mon Sep 17 00:00:00 2001 From: teor Date: Tue, 17 Nov 2020 10:55:24 +1000 Subject: [PATCH] Move all contextual validation code into its own function This change has two benefits: * reduces conflicts with the sled refactor and any replacement * allows the function to be called independently for testing --- zebra-state/src/service.rs | 37 +++++---------------- zebra-state/src/service/check.rs | 57 +++++++++++++++++++++++++++++++- 2 files changed, 64 insertions(+), 30 deletions(-) diff --git a/zebra-state/src/service.rs b/zebra-state/src/service.rs index fd0faa2d8..6ca069e95 100644 --- a/zebra-state/src/service.rs +++ b/zebra-state/src/service.rs @@ -185,37 +185,16 @@ impl StateService { } } - /// Check that `block` is contextually valid based on the committed finalized - /// and non-finalized state. + /// Check that `block` is contextually valid for the configured network, + /// based on the committed finalized and non-finalized state. fn check_contextual_validity(&mut self, block: &Block) -> Result<(), ValidateContextError> { - let height = block - .coinbase_height() - .expect("semantically valid blocks have a coinbase height"); - let hash = block.hash(); + check::block_is_contextually_valid( + block, + self.network, + self.sled.finalized_tip_height(), + self.chain(block.header.previous_block_hash), + )?; - let span = tracing::info_span!("StateService::check_contextual_validity", - ?height, network = ?self.network, ?hash); - let _entered = span.enter(); - - let finalized_tip_height = self.sled.finalized_tip_height().expect( - "finalized state must contain at least one block to use the non-finalized state", - ); - check::block_is_not_orphaned(finalized_tip_height, block)?; - - let mut relevant_chain = self.chain(block.header.previous_block_hash); - let parent_block = relevant_chain - .next() - .expect("state must contain parent block to do contextual validation"); - let parent_height = parent_block - .coinbase_height() - .expect("valid blocks have a coinbase height"); - let parent_hash = parent_block.hash(); - check::height_one_more_than_parent_height(parent_height, block)?; - // should be impossible by design, so no handleable error is thrown - assert_eq!(parent_hash, block.header.previous_block_hash); - - // TODO: validate difficulty adjustment - // TODO: other contextual validation design and implelentation Ok(()) } diff --git a/zebra-state/src/service/check.rs b/zebra-state/src/service/check.rs index b62cb61da..f5a72700f 100644 --- a/zebra-state/src/service/check.rs +++ b/zebra-state/src/service/check.rs @@ -1,9 +1,64 @@ //! Consensus critical contextual checks -use zebra_chain::block::{self, Block}; +use zebra_chain::{ + block::{self, Block}, + parameters::Network, +}; use crate::ValidateContextError; +use super::check; + +/// Check that `block` is contextually valid for `network`, based on the +/// `finalized_tip_height` and `relevant_chain`. +/// +/// The relevant chain is an iterator over the ancestors of `block`, starting +/// with its parent block. +pub(crate) fn block_is_contextually_valid( + block: &Block, + network: Network, + finalized_tip_height: Option, + relevant_chain: C, +) -> Result<(), ValidateContextError> +where + C: IntoIterator, + C::Item: AsRef, +{ + let height = block + .coinbase_height() + .expect("semantically valid blocks have a coinbase height"); + let hash = block.hash(); + + let span = tracing::info_span!( + "StateService::check_contextual_validity", + ?height, + ?network, + ?hash + ); + let _entered = span.enter(); + + let finalized_tip_height = finalized_tip_height + .expect("finalized state must contain at least one block to use the non-finalized state"); + check::block_is_not_orphaned(finalized_tip_height, block)?; + + let mut relevant_chain = relevant_chain.into_iter(); + let parent_block = relevant_chain + .next() + .expect("state must contain parent block to do contextual validation"); + let parent_block = parent_block.as_ref(); + let parent_height = parent_block + .coinbase_height() + .expect("valid blocks have a coinbase height"); + let parent_hash = parent_block.hash(); + check::height_one_more_than_parent_height(parent_height, block)?; + // should be impossible by design, so no handleable error is thrown + assert_eq!(parent_hash, block.header.previous_block_hash); + + // TODO: validate difficulty adjustment + // TODO: other contextual validation design and implelentation + Ok(()) +} + /// Returns `ValidateContextError::OrphanedBlock` if the height of the given /// block is less than or equal to the finalized tip height. pub(super) fn block_is_not_orphaned(