2020-07-29 04:12:53 -07:00
|
|
|
//! Tests for CheckpointList
|
|
|
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
use std::sync::Arc;
|
|
|
|
|
2021-06-17 17:05:28 -07:00
|
|
|
use zebra_chain::parameters::{Network, Network::*};
|
2020-08-15 23:20:01 -07:00
|
|
|
use zebra_chain::{
|
|
|
|
block::{self, Block},
|
|
|
|
serialization::ZcashDeserialize,
|
|
|
|
};
|
2020-07-29 04:12:53 -07:00
|
|
|
|
|
|
|
/// Make a checkpoint list containing only the genesis block
|
|
|
|
#[test]
|
2020-09-21 11:54:06 -07:00
|
|
|
fn checkpoint_list_genesis() -> Result<(), BoxError> {
|
2020-07-29 04:12:53 -07:00
|
|
|
zebra_test::init();
|
|
|
|
|
|
|
|
// Parse the genesis block
|
|
|
|
let mut checkpoint_data = Vec::new();
|
|
|
|
let block =
|
|
|
|
Arc::<Block>::zcash_deserialize(&zebra_test::vectors::BLOCK_MAINNET_GENESIS_BYTES[..])?;
|
2020-08-15 23:20:01 -07:00
|
|
|
let hash = block.hash();
|
2020-07-29 04:12:53 -07:00
|
|
|
checkpoint_data.push((
|
|
|
|
block.coinbase_height().expect("test block has height"),
|
|
|
|
hash,
|
|
|
|
));
|
|
|
|
|
|
|
|
// Make a checkpoint list containing the genesis block
|
2020-08-16 11:42:02 -07:00
|
|
|
let checkpoint_list: BTreeMap<block::Height, block::Hash> =
|
2020-07-29 04:12:53 -07:00
|
|
|
checkpoint_data.iter().cloned().collect();
|
|
|
|
let _ = CheckpointList::from_list(checkpoint_list)?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Make a checkpoint list containing multiple blocks
|
|
|
|
#[test]
|
2020-09-21 11:54:06 -07:00
|
|
|
fn checkpoint_list_multiple() -> Result<(), BoxError> {
|
2020-07-29 04:12:53 -07:00
|
|
|
zebra_test::init();
|
|
|
|
|
|
|
|
// Parse all the blocks
|
|
|
|
let mut checkpoint_data = Vec::new();
|
|
|
|
for b in &[
|
|
|
|
&zebra_test::vectors::BLOCK_MAINNET_GENESIS_BYTES[..],
|
|
|
|
&zebra_test::vectors::BLOCK_MAINNET_1_BYTES[..],
|
|
|
|
&zebra_test::vectors::BLOCK_MAINNET_415000_BYTES[..],
|
|
|
|
&zebra_test::vectors::BLOCK_MAINNET_434873_BYTES[..],
|
|
|
|
] {
|
|
|
|
let block = Arc::<Block>::zcash_deserialize(*b)?;
|
2020-08-15 23:20:01 -07:00
|
|
|
let hash = block.hash();
|
2020-07-29 04:12:53 -07:00
|
|
|
checkpoint_data.push((
|
|
|
|
block.coinbase_height().expect("test block has height"),
|
|
|
|
hash,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make a checkpoint list containing all the blocks
|
2020-08-16 11:42:02 -07:00
|
|
|
let checkpoint_list: BTreeMap<block::Height, block::Hash> =
|
|
|
|
checkpoint_data.iter().cloned().collect();
|
2020-07-29 04:12:53 -07:00
|
|
|
let _ = CheckpointList::from_list(checkpoint_list)?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Make sure that an empty checkpoint list fails
|
|
|
|
#[test]
|
2020-09-21 11:54:06 -07:00
|
|
|
fn checkpoint_list_empty_fail() -> Result<(), BoxError> {
|
2020-07-29 04:12:53 -07:00
|
|
|
zebra_test::init();
|
|
|
|
|
|
|
|
let _ = CheckpointList::from_list(Vec::new()).expect_err("empty checkpoint lists should fail");
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Make sure a checkpoint list that doesn't contain the genesis block fails
|
|
|
|
#[test]
|
2020-09-21 11:54:06 -07:00
|
|
|
fn checkpoint_list_no_genesis_fail() -> Result<(), BoxError> {
|
2020-07-29 04:12:53 -07:00
|
|
|
zebra_test::init();
|
|
|
|
|
|
|
|
// Parse a non-genesis block
|
|
|
|
let mut checkpoint_data = Vec::new();
|
|
|
|
let block = Arc::<Block>::zcash_deserialize(&zebra_test::vectors::BLOCK_MAINNET_1_BYTES[..])?;
|
2020-08-15 23:20:01 -07:00
|
|
|
let hash = block.hash();
|
2020-07-29 04:12:53 -07:00
|
|
|
checkpoint_data.push((
|
|
|
|
block.coinbase_height().expect("test block has height"),
|
|
|
|
hash,
|
|
|
|
));
|
|
|
|
|
|
|
|
// Make a checkpoint list containing the non-genesis block
|
2020-08-16 11:42:02 -07:00
|
|
|
let checkpoint_list: BTreeMap<block::Height, block::Hash> =
|
|
|
|
checkpoint_data.iter().cloned().collect();
|
2020-07-29 04:12:53 -07:00
|
|
|
let _ = CheckpointList::from_list(checkpoint_list)
|
|
|
|
.expect_err("a checkpoint list with no genesis block should fail");
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Make sure a checkpoint list that contains a null hash fails
|
|
|
|
#[test]
|
2020-09-21 11:54:06 -07:00
|
|
|
fn checkpoint_list_null_hash_fail() -> Result<(), BoxError> {
|
2020-07-29 04:12:53 -07:00
|
|
|
zebra_test::init();
|
|
|
|
|
2020-08-16 11:42:02 -07:00
|
|
|
let checkpoint_data = vec![(block::Height(0), block::Hash([0; 32]))];
|
2020-07-29 04:12:53 -07:00
|
|
|
|
|
|
|
// Make a checkpoint list containing the non-genesis block
|
2020-08-16 11:42:02 -07:00
|
|
|
let checkpoint_list: BTreeMap<block::Height, block::Hash> =
|
|
|
|
checkpoint_data.iter().cloned().collect();
|
2020-07-29 04:12:53 -07:00
|
|
|
let _ = CheckpointList::from_list(checkpoint_list)
|
|
|
|
.expect_err("a checkpoint list with a null block hash should fail");
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Make sure a checkpoint list that contains an invalid block height fails
|
|
|
|
#[test]
|
2020-09-21 11:54:06 -07:00
|
|
|
fn checkpoint_list_bad_height_fail() -> Result<(), BoxError> {
|
2020-07-29 04:12:53 -07:00
|
|
|
zebra_test::init();
|
|
|
|
|
2020-08-16 11:42:02 -07:00
|
|
|
let checkpoint_data = vec![(
|
|
|
|
block::Height(block::Height::MAX.0 + 1),
|
|
|
|
block::Hash([1; 32]),
|
|
|
|
)];
|
2020-07-29 04:12:53 -07:00
|
|
|
|
|
|
|
// Make a checkpoint list containing the non-genesis block
|
2020-08-16 11:42:02 -07:00
|
|
|
let checkpoint_list: BTreeMap<block::Height, block::Hash> =
|
|
|
|
checkpoint_data.iter().cloned().collect();
|
2020-07-29 04:12:53 -07:00
|
|
|
let _ = CheckpointList::from_list(checkpoint_list).expect_err(
|
2020-08-16 11:42:02 -07:00
|
|
|
"a checkpoint list with an invalid block height (block::Height::MAX + 1) should fail",
|
2020-07-29 04:12:53 -07:00
|
|
|
);
|
|
|
|
|
2020-08-16 11:42:02 -07:00
|
|
|
let checkpoint_data = vec![(block::Height(u32::MAX), block::Hash([1; 32]))];
|
2020-07-29 04:12:53 -07:00
|
|
|
|
|
|
|
// Make a checkpoint list containing the non-genesis block
|
2020-08-16 11:42:02 -07:00
|
|
|
let checkpoint_list: BTreeMap<block::Height, block::Hash> =
|
|
|
|
checkpoint_data.iter().cloned().collect();
|
2020-07-29 04:12:53 -07:00
|
|
|
let _ = CheckpointList::from_list(checkpoint_list)
|
|
|
|
.expect_err("a checkpoint list with an invalid block height (u32::MAX) should fail");
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Make sure that a checkpoint list containing duplicate blocks fails
|
|
|
|
#[test]
|
2020-09-21 11:54:06 -07:00
|
|
|
fn checkpoint_list_duplicate_blocks_fail() -> Result<(), BoxError> {
|
2020-07-29 04:12:53 -07:00
|
|
|
zebra_test::init();
|
|
|
|
|
|
|
|
// Parse some blocks twice
|
|
|
|
let mut checkpoint_data = Vec::new();
|
|
|
|
for b in &[
|
|
|
|
&zebra_test::vectors::BLOCK_MAINNET_GENESIS_BYTES[..],
|
|
|
|
&zebra_test::vectors::BLOCK_MAINNET_1_BYTES[..],
|
|
|
|
&zebra_test::vectors::BLOCK_MAINNET_1_BYTES[..],
|
|
|
|
] {
|
|
|
|
let block = Arc::<Block>::zcash_deserialize(*b)?;
|
2020-08-15 23:20:01 -07:00
|
|
|
let hash = block.hash();
|
2020-07-29 04:12:53 -07:00
|
|
|
checkpoint_data.push((
|
|
|
|
block.coinbase_height().expect("test block has height"),
|
|
|
|
hash,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make a checkpoint list containing some duplicate blocks
|
|
|
|
let _ = CheckpointList::from_list(checkpoint_data)
|
|
|
|
.expect_err("checkpoint lists with duplicate blocks should fail");
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Make sure that a checkpoint list containing duplicate heights
|
|
|
|
/// (with different hashes) fails
|
|
|
|
#[test]
|
2020-09-21 11:54:06 -07:00
|
|
|
fn checkpoint_list_duplicate_heights_fail() -> Result<(), BoxError> {
|
2020-07-29 04:12:53 -07:00
|
|
|
zebra_test::init();
|
|
|
|
|
|
|
|
// Parse the genesis block
|
|
|
|
let mut checkpoint_data = Vec::new();
|
2022-02-15 08:31:36 -08:00
|
|
|
let block =
|
|
|
|
Arc::<Block>::zcash_deserialize(&zebra_test::vectors::BLOCK_MAINNET_GENESIS_BYTES[..])?;
|
|
|
|
let hash = block.hash();
|
|
|
|
checkpoint_data.push((
|
|
|
|
block.coinbase_height().expect("test block has height"),
|
|
|
|
hash,
|
|
|
|
));
|
2020-07-29 04:12:53 -07:00
|
|
|
|
|
|
|
// Then add some fake entries with duplicate heights
|
2020-08-16 11:42:02 -07:00
|
|
|
checkpoint_data.push((block::Height(1), block::Hash([0xaa; 32])));
|
|
|
|
checkpoint_data.push((block::Height(1), block::Hash([0xbb; 32])));
|
2020-07-29 04:12:53 -07:00
|
|
|
|
|
|
|
// Make a checkpoint list containing some duplicate blocks
|
|
|
|
let _ = CheckpointList::from_list(checkpoint_data)
|
|
|
|
.expect_err("checkpoint lists with duplicate heights should fail");
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Make sure that a checkpoint list containing duplicate hashes
|
|
|
|
/// (at different heights) fails
|
|
|
|
#[test]
|
2020-09-21 11:54:06 -07:00
|
|
|
fn checkpoint_list_duplicate_hashes_fail() -> Result<(), BoxError> {
|
2020-07-29 04:12:53 -07:00
|
|
|
zebra_test::init();
|
|
|
|
|
|
|
|
// Parse the genesis block
|
|
|
|
let mut checkpoint_data = Vec::new();
|
2022-02-15 08:31:36 -08:00
|
|
|
let block =
|
|
|
|
Arc::<Block>::zcash_deserialize(&zebra_test::vectors::BLOCK_MAINNET_GENESIS_BYTES[..])?;
|
|
|
|
let hash = block.hash();
|
|
|
|
checkpoint_data.push((
|
|
|
|
block.coinbase_height().expect("test block has height"),
|
|
|
|
hash,
|
|
|
|
));
|
2020-07-29 04:12:53 -07:00
|
|
|
|
|
|
|
// Then add some fake entries with duplicate hashes
|
2020-08-16 11:42:02 -07:00
|
|
|
checkpoint_data.push((block::Height(1), block::Hash([0xcc; 32])));
|
|
|
|
checkpoint_data.push((block::Height(2), block::Hash([0xcc; 32])));
|
2020-07-29 04:12:53 -07:00
|
|
|
|
|
|
|
// Make a checkpoint list containing some duplicate blocks
|
|
|
|
let _ = CheckpointList::from_list(checkpoint_data)
|
|
|
|
.expect_err("checkpoint lists with duplicate hashes should fail");
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Parse and check the hard-coded Mainnet and Testnet lists
|
|
|
|
#[test]
|
2020-09-21 11:54:06 -07:00
|
|
|
fn checkpoint_list_load_hard_coded() -> Result<(), BoxError> {
|
2020-07-29 04:12:53 -07:00
|
|
|
zebra_test::init();
|
|
|
|
|
|
|
|
let _: CheckpointList = MAINNET_CHECKPOINTS
|
|
|
|
.parse()
|
|
|
|
.expect("hard-coded Mainnet checkpoint list should parse");
|
|
|
|
let _: CheckpointList = TESTNET_CHECKPOINTS
|
|
|
|
.parse()
|
|
|
|
.expect("hard-coded Testnet checkpoint list should parse");
|
|
|
|
|
2020-08-19 19:28:21 -07:00
|
|
|
let _ = CheckpointList::new(Mainnet);
|
|
|
|
let _ = CheckpointList::new(Testnet);
|
2020-07-29 04:12:53 -07:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2021-06-17 17:05:28 -07:00
|
|
|
fn checkpoint_list_hard_coded_mandatory_mainnet() -> Result<(), BoxError> {
|
|
|
|
checkpoint_list_hard_coded_mandatory(Mainnet)
|
2020-07-29 04:12:53 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2021-06-17 17:05:28 -07:00
|
|
|
fn checkpoint_list_hard_coded_mandatory_testnet() -> Result<(), BoxError> {
|
|
|
|
checkpoint_list_hard_coded_mandatory(Testnet)
|
2020-07-29 04:12:53 -07:00
|
|
|
}
|
|
|
|
|
2021-06-17 17:05:28 -07:00
|
|
|
/// Check that the hard-coded lists cover the mandatory checkpoint
|
|
|
|
fn checkpoint_list_hard_coded_mandatory(network: Network) -> Result<(), BoxError> {
|
2020-07-29 04:12:53 -07:00
|
|
|
zebra_test::init();
|
|
|
|
|
2021-06-17 17:05:28 -07:00
|
|
|
let mandatory_checkpoint = network.mandatory_checkpoint_height();
|
2020-07-29 04:12:53 -07:00
|
|
|
|
|
|
|
let list = CheckpointList::new(network);
|
|
|
|
|
|
|
|
assert!(
|
2021-06-17 17:05:28 -07:00
|
|
|
list.max_height() >= mandatory_checkpoint,
|
|
|
|
"Mandatory checkpoint block must be verified by checkpoints"
|
2020-07-29 04:12:53 -07:00
|
|
|
);
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2020-10-25 21:42:11 -07:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn checkpoint_list_hard_coded_max_gap_mainnet() -> Result<(), BoxError> {
|
|
|
|
checkpoint_list_hard_coded_max_gap(Mainnet)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn checkpoint_list_hard_coded_max_gap_testnet() -> Result<(), BoxError> {
|
|
|
|
checkpoint_list_hard_coded_max_gap(Testnet)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Check that the hard-coded checkpoints are within `MAX_CHECKPOINT_HEIGHT_GAP`.
|
|
|
|
///
|
|
|
|
/// We can't test the block byte limit, because we don't have access to the entire
|
|
|
|
/// blockchain in the tests. But that's ok, because the byte limit only impacts
|
|
|
|
/// performance.
|
|
|
|
fn checkpoint_list_hard_coded_max_gap(network: Network) -> Result<(), BoxError> {
|
|
|
|
zebra_test::init();
|
|
|
|
|
|
|
|
let list = CheckpointList::new(network);
|
|
|
|
let mut heights = list.0.keys();
|
|
|
|
|
|
|
|
// Check that we start at the genesis height
|
|
|
|
let mut previous_height = block::Height(0);
|
|
|
|
assert_eq!(heights.next(), Some(&previous_height));
|
|
|
|
|
|
|
|
for height in heights {
|
|
|
|
let height_limit = (previous_height + (crate::MAX_CHECKPOINT_HEIGHT_GAP as i32)).unwrap();
|
|
|
|
assert!(
|
|
|
|
height <= &height_limit,
|
|
|
|
"Checkpoint gaps must be within MAX_CHECKPOINT_HEIGHT_GAP"
|
|
|
|
);
|
|
|
|
previous_height = *height;
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|