Checkpoint Verifier Tests (#517)
* Add basic checkpoint verifier tests * Refactor a checkpoint verifier condition * Remove some outdated comments
This commit is contained in:
parent
246e7cd2a9
commit
03b8453b8a
|
|
@ -74,14 +74,12 @@ where
|
||||||
return Err("the checkpoint list is empty".into());
|
return Err("the checkpoint list is empty".into());
|
||||||
};
|
};
|
||||||
|
|
||||||
let block_height = match block.coinbase_height() {
|
let block_height = block
|
||||||
Some(height) => height,
|
.coinbase_height()
|
||||||
None => return Err("the block does not have a coinbase height".into()),
|
.ok_or("the block does not have a coinbase height")?;
|
||||||
};
|
|
||||||
|
|
||||||
// TODO(teor):
|
// TODO(teor):
|
||||||
// - implement chaining from checkpoints to their ancestors
|
// - implement chaining from checkpoints to their ancestors
|
||||||
// - if chaining is expensive, move this check to the Future
|
|
||||||
// - should the state contain a mapping from previous_block_hash to block?
|
// - should the state contain a mapping from previous_block_hash to block?
|
||||||
let checkpoint_hash = match checkpoint_list.get(&block_height) {
|
let checkpoint_hash = match checkpoint_list.get(&block_height) {
|
||||||
Some(&hash) => hash,
|
Some(&hash) => hash,
|
||||||
|
|
@ -97,7 +95,6 @@ where
|
||||||
// `Tower::Buffer` requires a 1:1 relationship between `poll()`s
|
// `Tower::Buffer` requires a 1:1 relationship between `poll()`s
|
||||||
// and `call()`s, because it reserves a buffer slot in each
|
// and `call()`s, because it reserves a buffer slot in each
|
||||||
// `call()`.
|
// `call()`.
|
||||||
// TODO(teor): what happens if the await fails?
|
|
||||||
let add_block = state_service
|
let add_block = state_service
|
||||||
.ready_and()
|
.ready_and()
|
||||||
.await?
|
.await?
|
||||||
|
|
@ -155,4 +152,231 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(teor): tests
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
use color_eyre::Report;
|
||||||
|
use eyre::{bail, ensure, eyre};
|
||||||
|
use tower::{util::ServiceExt, Service};
|
||||||
|
|
||||||
|
use zebra_chain::serialization::ZcashDeserialize;
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
#[spandoc::spandoc]
|
||||||
|
async fn checkpoint_single_item_list() -> Result<(), Report> {
|
||||||
|
let block0 =
|
||||||
|
Arc::<Block>::zcash_deserialize(&zebra_test_vectors::BLOCK_MAINNET_GENESIS_BYTES[..])?;
|
||||||
|
let hash0: BlockHeaderHash = block0.as_ref().into();
|
||||||
|
|
||||||
|
// Make a checkpoint list containing only the genesis block
|
||||||
|
let genesis_checkpoint_list: HashMap<BlockHeight, BlockHeaderHash> =
|
||||||
|
[(block0.coinbase_height().unwrap(), hash0)]
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut state_service = Box::new(zebra_state::in_memory::init());
|
||||||
|
let mut checkpoint_verifier = super::init(state_service.clone(), genesis_checkpoint_list);
|
||||||
|
|
||||||
|
// Verify block 0
|
||||||
|
let verify_response = checkpoint_verifier
|
||||||
|
.ready_and()
|
||||||
|
.await
|
||||||
|
.map_err(|e| eyre!(e))?
|
||||||
|
.call(block0.clone())
|
||||||
|
.await
|
||||||
|
.map_err(|e| eyre!(e))?;
|
||||||
|
|
||||||
|
ensure!(
|
||||||
|
verify_response == hash0,
|
||||||
|
"unexpected response kind: {:?}",
|
||||||
|
verify_response
|
||||||
|
);
|
||||||
|
|
||||||
|
let state_response = state_service
|
||||||
|
.ready_and()
|
||||||
|
.await
|
||||||
|
.map_err(|e| eyre!(e))?
|
||||||
|
.call(zebra_state::Request::GetBlock { hash: hash0 })
|
||||||
|
.await
|
||||||
|
.map_err(|e| eyre!(e))?;
|
||||||
|
|
||||||
|
match state_response {
|
||||||
|
zebra_state::Response::Block {
|
||||||
|
block: returned_block,
|
||||||
|
} => assert_eq!(block0, returned_block),
|
||||||
|
_ => bail!("unexpected response kind: {:?}", state_response),
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
#[spandoc::spandoc]
|
||||||
|
async fn checkpoint_list_empty_fail() -> Result<(), Report> {
|
||||||
|
let block0 =
|
||||||
|
Arc::<Block>::zcash_deserialize(&zebra_test_vectors::BLOCK_MAINNET_GENESIS_BYTES[..])?;
|
||||||
|
|
||||||
|
let mut state_service = Box::new(zebra_state::in_memory::init());
|
||||||
|
let mut checkpoint_verifier = super::init(
|
||||||
|
state_service.clone(),
|
||||||
|
<HashMap<BlockHeight, BlockHeaderHash>>::new(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Try to verify the block, and expect failure
|
||||||
|
let verify_result = checkpoint_verifier
|
||||||
|
.ready_and()
|
||||||
|
.await
|
||||||
|
.map_err(|e| eyre!(e))?
|
||||||
|
.call(block0.clone())
|
||||||
|
.await;
|
||||||
|
|
||||||
|
ensure!(
|
||||||
|
// TODO(teor || jlusby): check error string
|
||||||
|
verify_result.is_err(),
|
||||||
|
"unexpected result kind: {:?}",
|
||||||
|
verify_result
|
||||||
|
);
|
||||||
|
|
||||||
|
// Now make sure the block isn't in the state
|
||||||
|
let state_result = state_service
|
||||||
|
.ready_and()
|
||||||
|
.await
|
||||||
|
.map_err(|e| eyre!(e))?
|
||||||
|
.call(zebra_state::Request::GetBlock {
|
||||||
|
hash: block0.as_ref().into(),
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
ensure!(
|
||||||
|
// TODO(teor || jlusby): check error string
|
||||||
|
state_result.is_err(),
|
||||||
|
"unexpected result kind: {:?}",
|
||||||
|
verify_result
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
#[spandoc::spandoc]
|
||||||
|
async fn checkpoint_not_present_fail() -> Result<(), Report> {
|
||||||
|
let block0 =
|
||||||
|
Arc::<Block>::zcash_deserialize(&zebra_test_vectors::BLOCK_MAINNET_GENESIS_BYTES[..])?;
|
||||||
|
let block415000 =
|
||||||
|
Arc::<Block>::zcash_deserialize(&zebra_test_vectors::BLOCK_MAINNET_415000_BYTES[..])?;
|
||||||
|
|
||||||
|
// Make a checkpoint list containing only the genesis block
|
||||||
|
let genesis_checkpoint_list: HashMap<BlockHeight, BlockHeaderHash> =
|
||||||
|
[(block0.coinbase_height().unwrap(), block0.as_ref().into())]
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut state_service = Box::new(zebra_state::in_memory::init());
|
||||||
|
let mut checkpoint_verifier = super::init(state_service.clone(), genesis_checkpoint_list);
|
||||||
|
|
||||||
|
// Try to verify block 415000, and expect failure
|
||||||
|
let verify_result = checkpoint_verifier
|
||||||
|
.ready_and()
|
||||||
|
.await
|
||||||
|
.map_err(|e| eyre!(e))?
|
||||||
|
.call(block415000.clone())
|
||||||
|
.await;
|
||||||
|
|
||||||
|
ensure!(
|
||||||
|
// TODO(teor || jlusby): check error string
|
||||||
|
verify_result.is_err(),
|
||||||
|
"unexpected result kind: {:?}",
|
||||||
|
verify_result
|
||||||
|
);
|
||||||
|
|
||||||
|
// Now make sure neither block is in the state
|
||||||
|
let state_result = state_service
|
||||||
|
.ready_and()
|
||||||
|
.await
|
||||||
|
.map_err(|e| eyre!(e))?
|
||||||
|
.call(zebra_state::Request::GetBlock {
|
||||||
|
hash: block415000.as_ref().into(),
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
ensure!(
|
||||||
|
// TODO(teor || jlusby): check error string
|
||||||
|
state_result.is_err(),
|
||||||
|
"unexpected result kind: {:?}",
|
||||||
|
verify_result
|
||||||
|
);
|
||||||
|
|
||||||
|
let state_result = state_service
|
||||||
|
.ready_and()
|
||||||
|
.await
|
||||||
|
.map_err(|e| eyre!(e))?
|
||||||
|
.call(zebra_state::Request::GetBlock {
|
||||||
|
hash: block0.as_ref().into(),
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
ensure!(
|
||||||
|
// TODO(teor || jlusby): check error string
|
||||||
|
state_result.is_err(),
|
||||||
|
"unexpected result kind: {:?}",
|
||||||
|
verify_result
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
#[spandoc::spandoc]
|
||||||
|
async fn checkpoint_wrong_hash_fail() -> Result<(), Report> {
|
||||||
|
let block0 =
|
||||||
|
Arc::<Block>::zcash_deserialize(&zebra_test_vectors::BLOCK_MAINNET_GENESIS_BYTES[..])?;
|
||||||
|
|
||||||
|
// Make a checkpoint list containing the genesis block height,
|
||||||
|
// but use the wrong hash
|
||||||
|
let genesis_checkpoint_list: HashMap<BlockHeight, BlockHeaderHash> =
|
||||||
|
[(block0.coinbase_height().unwrap(), BlockHeaderHash([0; 32]))]
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut state_service = Box::new(zebra_state::in_memory::init());
|
||||||
|
let mut checkpoint_verifier = super::init(state_service.clone(), genesis_checkpoint_list);
|
||||||
|
|
||||||
|
// Try to verify block 0, and expect failure
|
||||||
|
let verify_result = checkpoint_verifier
|
||||||
|
.ready_and()
|
||||||
|
.await
|
||||||
|
.map_err(|e| eyre!(e))?
|
||||||
|
.call(block0.clone())
|
||||||
|
.await;
|
||||||
|
|
||||||
|
ensure!(
|
||||||
|
// TODO(teor || jlusby): check error string
|
||||||
|
verify_result.is_err(),
|
||||||
|
"unexpected result kind: {:?}",
|
||||||
|
verify_result
|
||||||
|
);
|
||||||
|
|
||||||
|
// Now make sure block 0 is not in the state
|
||||||
|
let state_result = state_service
|
||||||
|
.ready_and()
|
||||||
|
.await
|
||||||
|
.map_err(|e| eyre!(e))?
|
||||||
|
.call(zebra_state::Request::GetBlock {
|
||||||
|
hash: block0.as_ref().into(),
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
ensure!(
|
||||||
|
// TODO(teor || jlusby): check error string
|
||||||
|
state_result.is_err(),
|
||||||
|
"unexpected result kind: {:?}",
|
||||||
|
verify_result
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue