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
This commit is contained in:
teor 2020-11-17 10:55:24 +10:00
parent c8e6f5843f
commit d7d15984eb
2 changed files with 64 additions and 30 deletions

View File

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

View File

@ -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<C>(
block: &Block,
network: Network,
finalized_tip_height: Option<block::Height>,
relevant_chain: C,
) -> Result<(), ValidateContextError>
where
C: IntoIterator,
C::Item: AsRef<Block>,
{
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(