diff --git a/zebra-consensus/src/checkpoint/tests.rs b/zebra-consensus/src/checkpoint/tests.rs index ca8bc4180..9788563dd 100644 --- a/zebra-consensus/src/checkpoint/tests.rs +++ b/zebra-consensus/src/checkpoint/tests.rs @@ -268,7 +268,7 @@ async fn continuous_blockchain(restart_height: Option) -> Result< .cloned(); let state_service = zebra_state::init(zebra_state::Config::ephemeral(), Mainnet); let mut checkpoint_verifier = - CheckpointVerifier::from_list(checkpoint_list, initial_tip, state_service) + CheckpointVerifier::from_list(checkpoint_list, initial_tip, state_service.clone()) .map_err(|e| eyre!(e))?; // Setup checks @@ -305,7 +305,18 @@ async fn continuous_blockchain(restart_height: Option) -> Result< for (block, height, _hash) in blockchain { if let Some(restart_height) = restart_height { if height <= restart_height { - continue; + let mut state_service = state_service.clone(); + /// SPANDOC: Make sure the state service is ready for block {?height} + let ready_state_service = + state_service.ready_and().map_err(|e| eyre!(e)).await?; + + /// SPANDOC: Add block to the state {?height} + ready_state_service + .call(zebra_state::Request::AddBlock { + block: block.clone(), + }) + .await + .map_err(|e| eyre!(e))?; } } if height > checkpoint_verifier.checkpoint_list.max_height() { diff --git a/zebra-state/src/on_disk.rs b/zebra-state/src/on_disk.rs index 84bb58468..6191a3188 100644 --- a/zebra-state/src/on_disk.rs +++ b/zebra-state/src/on_disk.rs @@ -44,7 +44,42 @@ impl SledState { let block = block.into(); let hash = block.hash(); - let height = block.coinbase_height().unwrap(); + let height = block + .coinbase_height() + .expect("missing height: valid blocks must have a height"); + + // Make sure blocks are inserted in order, as a defence in depth. + // See the state design RFC #0005 for details. + // + // TODO: handle multiple chains + match self.get_tip()? { + None => { + // This is a defence in depth - there is no need to check the + // genesis hash or previous block hash here. + assert_eq!( + height, + block::Height(0), + "out of order block: the first block must be at the genesis height" + ); + } + Some(tip_hash) => { + assert_eq!( + block.header.previous_block_hash, tip_hash, + "out of order block: the next block must be a child of the current tip" + ); + let tip_block = self + .get(tip_hash)? + .expect("missing tip block: tip hashes must have a corresponding block"); + let tip_height = tip_block + .coinbase_height() + .expect("missing height: valid blocks must have a height"); + assert_eq!( + height, + block::Height(tip_height.0 + 1), + "out of order block: the next height must be 1 greater than the tip height" + ); + } + }; let height_map = self.storage.open_tree(b"height_map")?; let by_hash = self.storage.open_tree(b"by_hash")?; diff --git a/zebra-state/tests/basic.rs b/zebra-state/tests/basic.rs index 079a755ac..d08b34bf2 100644 --- a/zebra-state/tests/basic.rs +++ b/zebra-state/tests/basic.rs @@ -10,7 +10,7 @@ use zebra_state::*; static ADD_BLOCK_TRANSCRIPT_MAINNET: Lazy)>> = Lazy::new(|| { let block: Arc<_> = - Block::zcash_deserialize(&zebra_test::vectors::BLOCK_MAINNET_415000_BYTES[..]) + Block::zcash_deserialize(&zebra_test::vectors::BLOCK_MAINNET_GENESIS_BYTES[..]) .unwrap() .into(); let hash = block.as_ref().into(); @@ -28,7 +28,7 @@ static ADD_BLOCK_TRANSCRIPT_MAINNET: Lazy)>> = Lazy::new(|| { let block: Arc<_> = - Block::zcash_deserialize(&zebra_test::vectors::BLOCK_TESTNET_10_BYTES[..]) + Block::zcash_deserialize(&zebra_test::vectors::BLOCK_TESTNET_GENESIS_BYTES[..]) .unwrap() .into(); let hash = block.as_ref().into(); @@ -76,7 +76,7 @@ static GET_TIP_TRANSCRIPT_TESTNET: Lazy = - Block::zcash_deserialize(&zebra_test::vectors::BLOCK_TESTNET_10_BYTES[..]) + Block::zcash_deserialize(&zebra_test::vectors::BLOCK_TESTNET_1_BYTES[..]) .unwrap() .into(); let hash0 = block0.as_ref().into();