From a6bd77e98a3625e8953ec0bda5bed59bea0ef84e Mon Sep 17 00:00:00 2001 From: Jane Lusby Date: Mon, 16 Nov 2020 15:53:33 -0800 Subject: [PATCH] Add check to ensure heights in state service are sequential (#1290) * Add check to ensure heights in state service are sequential Co-authored-by: teor --- zebra-state/src/service.rs | 3 +++ zebra-state/src/sled_state.rs | 38 ++++++++++++++++++++++++++++++++--- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/zebra-state/src/service.rs b/zebra-state/src/service.rs index 6b446d99..90769ed9 100644 --- a/zebra-state/src/service.rs +++ b/zebra-state/src/service.rs @@ -196,7 +196,10 @@ impl StateService { 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: contextual validation design and implementation Ok(()) diff --git a/zebra-state/src/sled_state.rs b/zebra-state/src/sled_state.rs index 002f2b80..62a3a2b0 100644 --- a/zebra-state/src/sled_state.rs +++ b/zebra-state/src/sled_state.rs @@ -148,11 +148,45 @@ impl FinalizedState { let height = queued_block.block.coinbase_height().unwrap(); self.queued_by_prev_hash.insert(prev_hash, queued_block); - while let Some(queued_block) = self.queued_by_prev_hash.remove(&self.finalized_tip_hash()) { + loop { + let finalized_tip_hash = self.finalized_tip_hash(); + let queued_block = + if let Some(queued_block) = self.queued_by_prev_hash.remove(&finalized_tip_hash) { + queued_block + } else { + break; + }; + let height = queued_block .block .coinbase_height() .expect("valid blocks must have a height"); + + if self.block_by_height.is_empty() { + assert_eq!( + block::Hash([0; 32]), + prev_hash, + "the first block added to an empty state must be a genesis block" + ); + assert_eq!( + block::Height(0), + height, + "cannot commit genesis: invalid height" + ); + } else { + assert_eq!( + self.finalized_tip_height() + .expect("state must have a genesis block committed") + + 1, + Some(height) + ); + + assert_eq!( + finalized_tip_hash, + queued_block.block.header.previous_block_hash + ); + } + self.commit_finalized(queued_block); metrics::counter!("state.finalized.committed.block.count", 1); metrics::gauge!("state.finalized.committed.block.height", height.0 as _); @@ -216,8 +250,6 @@ impl FinalizedState { sprout_nullifiers, sapling_nullifiers, )| { - // TODO: check highest entry of hash_by_height as in RFC - // Index the block hash_by_height.zs_insert(height, hash)?; height_by_hash.zs_insert(hash, height)?;