2020-07-09 23:51:01 -07:00
|
|
|
//! Tests for checkpoint-based block verification
|
|
|
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
use super::types::Progress::*;
|
|
|
|
use super::types::Target::*;
|
|
|
|
|
|
|
|
use color_eyre::eyre::{eyre, Report};
|
2020-07-13 09:29:21 -07:00
|
|
|
use futures::{future::TryFutureExt, stream::FuturesUnordered};
|
2020-07-09 23:51:01 -07:00
|
|
|
use std::{cmp::min, mem::drop, time::Duration};
|
2020-07-13 09:29:21 -07:00
|
|
|
use tokio::{stream::StreamExt, time::timeout};
|
2020-07-09 23:51:01 -07:00
|
|
|
use tower::{Service, ServiceExt};
|
2020-07-13 09:29:21 -07:00
|
|
|
use tracing_futures::Instrument;
|
2020-07-09 23:51:01 -07:00
|
|
|
|
2020-09-02 21:23:57 -07:00
|
|
|
use zebra_chain::parameters::Network::*;
|
2020-07-09 23:51:01 -07:00
|
|
|
use zebra_chain::serialization::ZcashDeserialize;
|
|
|
|
|
|
|
|
/// The timeout we apply to each verify future during testing.
|
|
|
|
///
|
|
|
|
/// The checkpoint verifier uses `tokio::sync::oneshot` channels as futures.
|
|
|
|
/// If the verifier doesn't send a message on the channel, any tests that
|
|
|
|
/// await the channel future will hang.
|
|
|
|
///
|
|
|
|
/// This value is set to a large value, to avoid spurious failures due to
|
|
|
|
/// high system load.
|
|
|
|
const VERIFY_TIMEOUT_SECONDS: u64 = 10;
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn single_item_checkpoint_list_test() -> Result<(), Report> {
|
|
|
|
single_item_checkpoint_list().await
|
|
|
|
}
|
|
|
|
|
|
|
|
#[spandoc::spandoc]
|
|
|
|
async fn single_item_checkpoint_list() -> Result<(), Report> {
|
|
|
|
zebra_test::init();
|
|
|
|
|
|
|
|
let block0 =
|
|
|
|
Arc::<Block>::zcash_deserialize(&zebra_test::vectors::BLOCK_MAINNET_GENESIS_BYTES[..])?;
|
2020-08-15 23:20:01 -07:00
|
|
|
let hash0 = block0.hash();
|
2020-07-09 23:51:01 -07:00
|
|
|
|
|
|
|
// Make a checkpoint list containing only the genesis block
|
2020-08-16 11:42:02 -07:00
|
|
|
let genesis_checkpoint_list: BTreeMap<block::Height, block::Hash> =
|
2020-07-09 23:51:01 -07:00
|
|
|
[(block0.coinbase_height().unwrap(), hash0)]
|
|
|
|
.iter()
|
|
|
|
.cloned()
|
|
|
|
.collect();
|
|
|
|
|
2020-09-02 21:23:57 -07:00
|
|
|
let state_service = zebra_state::init(zebra_state::Config::ephemeral(), Mainnet);
|
2020-07-09 23:51:01 -07:00
|
|
|
let mut checkpoint_verifier =
|
2020-09-02 21:23:57 -07:00
|
|
|
CheckpointVerifier::from_list(genesis_checkpoint_list, None, state_service)
|
|
|
|
.map_err(|e| eyre!(e))?;
|
2020-07-09 23:51:01 -07:00
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.previous_checkpoint_height(),
|
|
|
|
BeforeGenesis
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.target_checkpoint_height(),
|
|
|
|
WaitingForBlocks
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.checkpoint_list.max_height(),
|
2020-08-16 11:42:02 -07:00
|
|
|
block::Height(0)
|
2020-07-09 23:51:01 -07:00
|
|
|
);
|
|
|
|
|
|
|
|
/// SPANDOC: Make sure the verifier service is ready
|
|
|
|
let ready_verifier_service = checkpoint_verifier
|
|
|
|
.ready_and()
|
|
|
|
.map_err(|e| eyre!(e))
|
|
|
|
.await?;
|
|
|
|
/// SPANDOC: Set up the future for block 0
|
|
|
|
let verify_future = timeout(
|
|
|
|
Duration::from_secs(VERIFY_TIMEOUT_SECONDS),
|
|
|
|
ready_verifier_service.call(block0.clone()),
|
|
|
|
);
|
|
|
|
/// SPANDOC: Wait for the response for block 0
|
|
|
|
// TODO(teor || jlusby): check error kind
|
|
|
|
let verify_response = verify_future
|
|
|
|
.map_err(|e| eyre!(e))
|
|
|
|
.await
|
|
|
|
.expect("timeout should not happen")
|
|
|
|
.expect("block should verify");
|
|
|
|
|
|
|
|
assert_eq!(verify_response, hash0);
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.previous_checkpoint_height(),
|
|
|
|
FinalCheckpoint
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.target_checkpoint_height(),
|
|
|
|
FinishedVerifying
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.checkpoint_list.max_height(),
|
2020-08-16 11:42:02 -07:00
|
|
|
block::Height(0)
|
2020-07-09 23:51:01 -07:00
|
|
|
);
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn multi_item_checkpoint_list_test() -> Result<(), Report> {
|
|
|
|
multi_item_checkpoint_list().await
|
|
|
|
}
|
|
|
|
|
|
|
|
#[spandoc::spandoc]
|
|
|
|
async fn multi_item_checkpoint_list() -> Result<(), Report> {
|
|
|
|
zebra_test::init();
|
|
|
|
|
|
|
|
// Parse all the blocks
|
|
|
|
let mut checkpoint_data = Vec::new();
|
|
|
|
for b in &[
|
2020-07-09 23:33:43 -07:00
|
|
|
// This list is used as a checkpoint list, and as a list of blocks to
|
|
|
|
// verify. So it must be continuous.
|
2020-07-09 23:51:01 -07:00
|
|
|
&zebra_test::vectors::BLOCK_MAINNET_GENESIS_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-09 23:51:01 -07:00
|
|
|
checkpoint_data.push((block.clone(), block.coinbase_height().unwrap(), 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
|
2020-07-09 23:51:01 -07:00
|
|
|
.iter()
|
|
|
|
.map(|(_block, height, hash)| (*height, *hash))
|
|
|
|
.collect();
|
|
|
|
|
2020-09-02 21:23:57 -07:00
|
|
|
let state_service = zebra_state::init(zebra_state::Config::ephemeral(), Mainnet);
|
2020-07-15 02:51:54 -07:00
|
|
|
let mut checkpoint_verifier =
|
2020-09-02 21:23:57 -07:00
|
|
|
CheckpointVerifier::from_list(checkpoint_list, None, state_service)
|
|
|
|
.map_err(|e| eyre!(e))?;
|
2020-07-09 23:51:01 -07:00
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.previous_checkpoint_height(),
|
|
|
|
BeforeGenesis
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.target_checkpoint_height(),
|
|
|
|
WaitingForBlocks
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.checkpoint_list.max_height(),
|
2020-08-16 11:42:02 -07:00
|
|
|
block::Height(1)
|
2020-07-09 23:51:01 -07:00
|
|
|
);
|
|
|
|
|
|
|
|
// Now verify each block
|
|
|
|
for (block, height, hash) in checkpoint_data {
|
|
|
|
/// SPANDOC: Make sure the verifier service is ready
|
|
|
|
let ready_verifier_service = checkpoint_verifier
|
|
|
|
.ready_and()
|
|
|
|
.map_err(|e| eyre!(e))
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
/// SPANDOC: Set up the future for block {?height}
|
|
|
|
let verify_future = timeout(
|
|
|
|
Duration::from_secs(VERIFY_TIMEOUT_SECONDS),
|
|
|
|
ready_verifier_service.call(block.clone()),
|
|
|
|
);
|
|
|
|
/// SPANDOC: Wait for the response for block {?height}
|
|
|
|
// TODO(teor || jlusby): check error kind
|
|
|
|
let verify_response = verify_future
|
|
|
|
.map_err(|e| eyre!(e))
|
|
|
|
.await
|
|
|
|
.expect("timeout should not happen")
|
|
|
|
.expect("future should succeed");
|
|
|
|
|
|
|
|
assert_eq!(verify_response, hash);
|
|
|
|
|
|
|
|
if height < checkpoint_verifier.checkpoint_list.max_height() {
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.previous_checkpoint_height(),
|
|
|
|
PreviousCheckpoint(height)
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.target_checkpoint_height(),
|
|
|
|
WaitingForBlocks
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.previous_checkpoint_height(),
|
|
|
|
FinalCheckpoint
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.target_checkpoint_height(),
|
|
|
|
FinishedVerifying
|
|
|
|
);
|
|
|
|
}
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.checkpoint_list.max_height(),
|
2020-08-16 11:42:02 -07:00
|
|
|
block::Height(1)
|
2020-07-09 23:51:01 -07:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.previous_checkpoint_height(),
|
|
|
|
FinalCheckpoint
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.target_checkpoint_height(),
|
|
|
|
FinishedVerifying
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.checkpoint_list.max_height(),
|
2020-08-16 11:42:02 -07:00
|
|
|
block::Height(1)
|
2020-07-09 23:51:01 -07:00
|
|
|
);
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2020-07-13 09:29:21 -07:00
|
|
|
#[tokio::test]
|
2020-09-04 02:50:25 -07:00
|
|
|
// Temporarily ignore this test, until the state can handle out-of-order blocks
|
|
|
|
#[ignore]
|
2020-07-13 09:29:21 -07:00
|
|
|
async fn continuous_blockchain_test() -> Result<(), Report> {
|
2020-07-23 18:47:48 -07:00
|
|
|
continuous_blockchain(None).await?;
|
|
|
|
for height in 0..=10 {
|
2020-08-16 11:42:02 -07:00
|
|
|
continuous_blockchain(Some(block::Height(height))).await?;
|
2020-07-23 18:47:48 -07:00
|
|
|
}
|
|
|
|
Ok(())
|
2020-07-13 09:29:21 -07:00
|
|
|
}
|
|
|
|
|
2020-07-23 18:47:48 -07:00
|
|
|
/// Test a continuous blockchain, restarting verification at `restart_height`.
|
2020-07-13 09:29:21 -07:00
|
|
|
#[spandoc::spandoc]
|
2020-08-16 11:42:02 -07:00
|
|
|
async fn continuous_blockchain(restart_height: Option<block::Height>) -> Result<(), Report> {
|
2020-07-13 09:29:21 -07:00
|
|
|
zebra_test::init();
|
|
|
|
|
|
|
|
// A continuous blockchain
|
|
|
|
let mut blockchain = Vec::new();
|
|
|
|
for b in &[
|
|
|
|
&zebra_test::vectors::BLOCK_MAINNET_GENESIS_BYTES[..],
|
|
|
|
&zebra_test::vectors::BLOCK_MAINNET_1_BYTES[..],
|
|
|
|
&zebra_test::vectors::BLOCK_MAINNET_2_BYTES[..],
|
|
|
|
&zebra_test::vectors::BLOCK_MAINNET_3_BYTES[..],
|
|
|
|
&zebra_test::vectors::BLOCK_MAINNET_4_BYTES[..],
|
|
|
|
&zebra_test::vectors::BLOCK_MAINNET_5_BYTES[..],
|
|
|
|
&zebra_test::vectors::BLOCK_MAINNET_6_BYTES[..],
|
|
|
|
&zebra_test::vectors::BLOCK_MAINNET_7_BYTES[..],
|
|
|
|
&zebra_test::vectors::BLOCK_MAINNET_8_BYTES[..],
|
|
|
|
&zebra_test::vectors::BLOCK_MAINNET_9_BYTES[..],
|
|
|
|
&zebra_test::vectors::BLOCK_MAINNET_10_BYTES[..],
|
|
|
|
] {
|
|
|
|
let block = Arc::<Block>::zcash_deserialize(*b)?;
|
2020-08-15 23:20:01 -07:00
|
|
|
let hash = block.hash();
|
2020-07-13 09:29:21 -07:00
|
|
|
blockchain.push((block.clone(), block.coinbase_height().unwrap(), hash));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse only some blocks as checkpoints
|
|
|
|
let mut checkpoints = Vec::new();
|
|
|
|
for b in &[
|
|
|
|
&zebra_test::vectors::BLOCK_MAINNET_GENESIS_BYTES[..],
|
|
|
|
&zebra_test::vectors::BLOCK_MAINNET_5_BYTES[..],
|
2020-07-23 18:47:48 -07:00
|
|
|
&zebra_test::vectors::BLOCK_MAINNET_9_BYTES[..],
|
2020-07-13 09:29:21 -07:00
|
|
|
] {
|
|
|
|
let block = Arc::<Block>::zcash_deserialize(*b)?;
|
2020-08-15 23:20:01 -07:00
|
|
|
let hash = block.hash();
|
2020-07-13 09:29:21 -07:00
|
|
|
checkpoints.push((block.clone(), block.coinbase_height().unwrap(), hash));
|
|
|
|
}
|
|
|
|
|
2020-07-23 18:47:48 -07:00
|
|
|
// The checkpoint list will contain only block 0, 5 and 9
|
2020-08-16 11:42:02 -07:00
|
|
|
let checkpoint_list: BTreeMap<block::Height, block::Hash> = checkpoints
|
2020-07-13 09:29:21 -07:00
|
|
|
.iter()
|
|
|
|
.map(|(_block, height, hash)| (*height, *hash))
|
|
|
|
.collect();
|
|
|
|
|
2020-07-23 18:47:48 -07:00
|
|
|
/// SPANDOC: Verify blocks, restarting at {?restart_height}
|
|
|
|
{
|
|
|
|
let initial_tip = restart_height
|
2020-08-16 11:42:02 -07:00
|
|
|
.map(|block::Height(height)| &blockchain[height as usize].0)
|
2020-07-23 18:47:48 -07:00
|
|
|
.cloned();
|
2020-09-02 21:23:57 -07:00
|
|
|
let state_service = zebra_state::init(zebra_state::Config::ephemeral(), Mainnet);
|
2020-07-23 18:47:48 -07:00
|
|
|
let mut checkpoint_verifier =
|
2020-09-04 02:34:40 -07:00
|
|
|
CheckpointVerifier::from_list(checkpoint_list, initial_tip, state_service.clone())
|
2020-09-02 21:23:57 -07:00
|
|
|
.map_err(|e| eyre!(e))?;
|
2020-07-23 18:47:48 -07:00
|
|
|
|
|
|
|
// Setup checks
|
|
|
|
if restart_height
|
|
|
|
.map(|h| h >= checkpoint_verifier.checkpoint_list.max_height())
|
|
|
|
.unwrap_or(false)
|
|
|
|
{
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.previous_checkpoint_height(),
|
|
|
|
FinalCheckpoint
|
|
|
|
);
|
2020-07-13 09:29:21 -07:00
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.target_checkpoint_height(),
|
2020-07-23 18:47:48 -07:00
|
|
|
FinishedVerifying
|
2020-07-13 09:29:21 -07:00
|
|
|
);
|
|
|
|
} else {
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.previous_checkpoint_height(),
|
2020-07-23 18:47:48 -07:00
|
|
|
restart_height.map(InitialTip).unwrap_or(BeforeGenesis)
|
2020-07-13 09:29:21 -07:00
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.target_checkpoint_height(),
|
2020-07-23 18:47:48 -07:00
|
|
|
WaitingForBlocks
|
2020-07-13 09:29:21 -07:00
|
|
|
);
|
|
|
|
}
|
2020-07-23 18:47:48 -07:00
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.checkpoint_list.max_height(),
|
2020-08-16 11:42:02 -07:00
|
|
|
block::Height(9)
|
2020-07-23 18:47:48 -07:00
|
|
|
);
|
2020-07-13 09:29:21 -07:00
|
|
|
|
2020-07-23 18:47:48 -07:00
|
|
|
let mut handles = FuturesUnordered::new();
|
|
|
|
|
|
|
|
// Now verify each block
|
|
|
|
for (block, height, _hash) in blockchain {
|
|
|
|
if let Some(restart_height) = restart_height {
|
|
|
|
if height <= restart_height {
|
2020-09-04 02:34:40 -07:00
|
|
|
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))?;
|
2020-07-23 18:47:48 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if height > checkpoint_verifier.checkpoint_list.max_height() {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2020-07-23 20:17:39 -07:00
|
|
|
/// SPANDOC: Make sure the verifier service is ready for block {?height}
|
2020-07-23 18:47:48 -07:00
|
|
|
let ready_verifier_service = checkpoint_verifier
|
|
|
|
.ready_and()
|
|
|
|
.map_err(|e| eyre!(e))
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
/// SPANDOC: Set up the future for block {?height}
|
|
|
|
let verify_future = timeout(
|
|
|
|
Duration::from_secs(VERIFY_TIMEOUT_SECONDS),
|
|
|
|
ready_verifier_service.call(block.clone()),
|
|
|
|
);
|
2020-07-13 09:29:21 -07:00
|
|
|
|
2020-07-23 20:17:39 -07:00
|
|
|
/// SPANDOC: spawn verification future in the background for block {?height}
|
2020-07-23 18:47:48 -07:00
|
|
|
let handle = tokio::spawn(verify_future.in_current_span());
|
|
|
|
handles.push(handle);
|
|
|
|
|
|
|
|
// Execution checks
|
|
|
|
if height < checkpoint_verifier.checkpoint_list.max_height() {
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.target_checkpoint_height(),
|
|
|
|
WaitingForBlocks
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.previous_checkpoint_height(),
|
|
|
|
FinalCheckpoint
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.target_checkpoint_height(),
|
|
|
|
FinishedVerifying
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
while let Some(result) = handles.next().await {
|
|
|
|
result??.map_err(|e| eyre!(e))?;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Final checks
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.previous_checkpoint_height(),
|
|
|
|
FinalCheckpoint
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.target_checkpoint_height(),
|
|
|
|
FinishedVerifying
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.checkpoint_list.max_height(),
|
2020-08-16 11:42:02 -07:00
|
|
|
block::Height(9)
|
2020-07-23 18:47:48 -07:00
|
|
|
);
|
|
|
|
}
|
2020-07-13 09:29:21 -07:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2020-07-09 23:51:01 -07:00
|
|
|
#[tokio::test]
|
|
|
|
async fn block_higher_than_max_checkpoint_fail_test() -> Result<(), Report> {
|
|
|
|
block_higher_than_max_checkpoint_fail().await
|
|
|
|
}
|
|
|
|
|
|
|
|
#[spandoc::spandoc]
|
|
|
|
async fn block_higher_than_max_checkpoint_fail() -> Result<(), Report> {
|
|
|
|
zebra_test::init();
|
|
|
|
|
|
|
|
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
|
2020-08-16 11:42:02 -07:00
|
|
|
let genesis_checkpoint_list: BTreeMap<block::Height, block::Hash> =
|
2020-07-09 23:51:01 -07:00
|
|
|
[(block0.coinbase_height().unwrap(), block0.as_ref().into())]
|
|
|
|
.iter()
|
|
|
|
.cloned()
|
|
|
|
.collect();
|
|
|
|
|
2020-09-02 21:23:57 -07:00
|
|
|
let state_service = zebra_state::init(zebra_state::Config::ephemeral(), Mainnet);
|
2020-07-09 23:51:01 -07:00
|
|
|
let mut checkpoint_verifier =
|
2020-09-02 21:23:57 -07:00
|
|
|
CheckpointVerifier::from_list(genesis_checkpoint_list, None, state_service)
|
|
|
|
.map_err(|e| eyre!(e))?;
|
2020-07-09 23:51:01 -07:00
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.previous_checkpoint_height(),
|
|
|
|
BeforeGenesis
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.target_checkpoint_height(),
|
|
|
|
WaitingForBlocks
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.checkpoint_list.max_height(),
|
2020-08-16 11:42:02 -07:00
|
|
|
block::Height(0)
|
2020-07-09 23:51:01 -07:00
|
|
|
);
|
|
|
|
|
|
|
|
/// SPANDOC: Make sure the verifier service is ready
|
|
|
|
let ready_verifier_service = checkpoint_verifier
|
|
|
|
.ready_and()
|
|
|
|
.map_err(|e| eyre!(e))
|
|
|
|
.await?;
|
|
|
|
/// SPANDOC: Set up the future for block 415000
|
|
|
|
let verify_future = timeout(
|
|
|
|
Duration::from_secs(VERIFY_TIMEOUT_SECONDS),
|
|
|
|
ready_verifier_service.call(block415000.clone()),
|
|
|
|
);
|
|
|
|
/// SPANDOC: Wait for the response for block 415000, and expect failure
|
|
|
|
// TODO(teor || jlusby): check error kind
|
|
|
|
let _ = verify_future
|
|
|
|
.map_err(|e| eyre!(e))
|
|
|
|
.await
|
|
|
|
.expect("timeout should not happen")
|
|
|
|
.expect_err("bad block hash should fail");
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.previous_checkpoint_height(),
|
|
|
|
BeforeGenesis
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.target_checkpoint_height(),
|
|
|
|
WaitingForBlocks
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.checkpoint_list.max_height(),
|
2020-08-16 11:42:02 -07:00
|
|
|
block::Height(0)
|
2020-07-09 23:51:01 -07:00
|
|
|
);
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn wrong_checkpoint_hash_fail_test() -> Result<(), Report> {
|
|
|
|
wrong_checkpoint_hash_fail().await
|
|
|
|
}
|
|
|
|
|
|
|
|
#[spandoc::spandoc]
|
|
|
|
async fn wrong_checkpoint_hash_fail() -> Result<(), Report> {
|
|
|
|
zebra_test::init();
|
|
|
|
|
|
|
|
let good_block0 =
|
|
|
|
Arc::<Block>::zcash_deserialize(&zebra_test::vectors::BLOCK_MAINNET_GENESIS_BYTES[..])?;
|
2020-08-15 23:20:01 -07:00
|
|
|
let good_block0_hash = good_block0.hash();
|
2020-07-09 23:51:01 -07:00
|
|
|
// Change the header hash
|
|
|
|
let mut bad_block0 = good_block0.clone();
|
|
|
|
let mut bad_block0 = Arc::make_mut(&mut bad_block0);
|
|
|
|
bad_block0.header.version = 0;
|
|
|
|
let bad_block0: Arc<Block> = bad_block0.clone().into();
|
|
|
|
|
|
|
|
// Make a checkpoint list containing the genesis block checkpoint
|
2020-08-16 11:42:02 -07:00
|
|
|
let genesis_checkpoint_list: BTreeMap<block::Height, block::Hash> =
|
2020-07-09 23:51:01 -07:00
|
|
|
[(good_block0.coinbase_height().unwrap(), good_block0_hash)]
|
|
|
|
.iter()
|
|
|
|
.cloned()
|
|
|
|
.collect();
|
|
|
|
|
2020-09-02 21:23:57 -07:00
|
|
|
let state_service = zebra_state::init(zebra_state::Config::ephemeral(), Mainnet);
|
2020-07-09 23:51:01 -07:00
|
|
|
let mut checkpoint_verifier =
|
2020-09-02 21:23:57 -07:00
|
|
|
CheckpointVerifier::from_list(genesis_checkpoint_list, None, state_service)
|
|
|
|
.map_err(|e| eyre!(e))?;
|
2020-07-09 23:51:01 -07:00
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.previous_checkpoint_height(),
|
|
|
|
BeforeGenesis
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.target_checkpoint_height(),
|
|
|
|
WaitingForBlocks
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.checkpoint_list.max_height(),
|
2020-08-16 11:42:02 -07:00
|
|
|
block::Height(0)
|
2020-07-09 23:51:01 -07:00
|
|
|
);
|
|
|
|
|
|
|
|
/// SPANDOC: Make sure the verifier service is ready (1/3)
|
|
|
|
let ready_verifier_service = checkpoint_verifier
|
|
|
|
.ready_and()
|
|
|
|
.map_err(|e| eyre!(e))
|
|
|
|
.await?;
|
|
|
|
/// SPANDOC: Set up the future for bad block 0 (1/3)
|
|
|
|
// TODO(teor || jlusby): check error kind
|
|
|
|
let bad_verify_future_1 = timeout(
|
|
|
|
Duration::from_secs(VERIFY_TIMEOUT_SECONDS),
|
|
|
|
ready_verifier_service.call(bad_block0.clone()),
|
|
|
|
);
|
|
|
|
// We can't await the future yet, because bad blocks aren't cleared
|
|
|
|
// until the chain is verified
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.previous_checkpoint_height(),
|
|
|
|
BeforeGenesis
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.target_checkpoint_height(),
|
|
|
|
WaitingForBlocks
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.checkpoint_list.max_height(),
|
2020-08-16 11:42:02 -07:00
|
|
|
block::Height(0)
|
2020-07-09 23:51:01 -07:00
|
|
|
);
|
|
|
|
|
|
|
|
/// SPANDOC: Make sure the verifier service is ready (2/3)
|
|
|
|
let ready_verifier_service = checkpoint_verifier
|
|
|
|
.ready_and()
|
|
|
|
.map_err(|e| eyre!(e))
|
|
|
|
.await?;
|
|
|
|
/// SPANDOC: Set up the future for bad block 0 again (2/3)
|
|
|
|
// TODO(teor || jlusby): check error kind
|
|
|
|
let bad_verify_future_2 = timeout(
|
|
|
|
Duration::from_secs(VERIFY_TIMEOUT_SECONDS),
|
|
|
|
ready_verifier_service.call(bad_block0.clone()),
|
|
|
|
);
|
|
|
|
// We can't await the future yet, because bad blocks aren't cleared
|
|
|
|
// until the chain is verified
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.previous_checkpoint_height(),
|
|
|
|
BeforeGenesis
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.target_checkpoint_height(),
|
|
|
|
WaitingForBlocks
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.checkpoint_list.max_height(),
|
2020-08-16 11:42:02 -07:00
|
|
|
block::Height(0)
|
2020-07-09 23:51:01 -07:00
|
|
|
);
|
|
|
|
|
|
|
|
/// SPANDOC: Make sure the verifier service is ready (3/3)
|
|
|
|
let ready_verifier_service = checkpoint_verifier
|
|
|
|
.ready_and()
|
|
|
|
.map_err(|e| eyre!(e))
|
|
|
|
.await?;
|
|
|
|
/// SPANDOC: Set up the future for good block 0 (3/3)
|
|
|
|
let good_verify_future = timeout(
|
|
|
|
Duration::from_secs(VERIFY_TIMEOUT_SECONDS),
|
|
|
|
ready_verifier_service.call(good_block0.clone()),
|
|
|
|
);
|
|
|
|
/// SPANDOC: Wait for the response for good block 0, and expect success (3/3)
|
|
|
|
// TODO(teor || jlusby): check error kind
|
|
|
|
let verify_response = good_verify_future
|
|
|
|
.map_err(|e| eyre!(e))
|
|
|
|
.await
|
|
|
|
.expect("timeout should not happen")
|
|
|
|
.expect("future should succeed");
|
|
|
|
|
|
|
|
assert_eq!(verify_response, good_block0_hash);
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.previous_checkpoint_height(),
|
|
|
|
FinalCheckpoint
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.target_checkpoint_height(),
|
|
|
|
FinishedVerifying
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.checkpoint_list.max_height(),
|
2020-08-16 11:42:02 -07:00
|
|
|
block::Height(0)
|
2020-07-09 23:51:01 -07:00
|
|
|
);
|
|
|
|
|
|
|
|
// Now, await the bad futures, which should have completed
|
|
|
|
|
|
|
|
/// SPANDOC: Wait for the response for block 0, and expect failure (1/3)
|
|
|
|
// TODO(teor || jlusby): check error kind
|
|
|
|
let _ = bad_verify_future_1
|
|
|
|
.map_err(|e| eyre!(e))
|
|
|
|
.await
|
|
|
|
.expect("timeout should not happen")
|
|
|
|
.expect_err("bad block hash should fail");
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.previous_checkpoint_height(),
|
|
|
|
FinalCheckpoint
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.target_checkpoint_height(),
|
|
|
|
FinishedVerifying
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.checkpoint_list.max_height(),
|
2020-08-16 11:42:02 -07:00
|
|
|
block::Height(0)
|
2020-07-09 23:51:01 -07:00
|
|
|
);
|
|
|
|
|
|
|
|
/// SPANDOC: Wait for the response for block 0, and expect failure again (2/3)
|
|
|
|
// TODO(teor || jlusby): check error kind
|
|
|
|
let _ = bad_verify_future_2
|
|
|
|
.map_err(|e| eyre!(e))
|
|
|
|
.await
|
|
|
|
.expect("timeout should not happen")
|
|
|
|
.expect_err("bad block hash should fail");
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.previous_checkpoint_height(),
|
|
|
|
FinalCheckpoint
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.target_checkpoint_height(),
|
|
|
|
FinishedVerifying
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.checkpoint_list.max_height(),
|
2020-08-16 11:42:02 -07:00
|
|
|
block::Height(0)
|
2020-07-09 23:51:01 -07:00
|
|
|
);
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn checkpoint_drop_cancel_test() -> Result<(), Report> {
|
|
|
|
checkpoint_drop_cancel().await
|
|
|
|
}
|
|
|
|
|
|
|
|
#[spandoc::spandoc]
|
|
|
|
async fn checkpoint_drop_cancel() -> Result<(), Report> {
|
|
|
|
zebra_test::init();
|
|
|
|
|
|
|
|
// Parse all the blocks
|
|
|
|
let mut checkpoint_data = Vec::new();
|
|
|
|
for b in &[
|
2020-07-09 23:33:43 -07:00
|
|
|
// Continous blocks are verified
|
2020-07-09 23:51:01 -07:00
|
|
|
&zebra_test::vectors::BLOCK_MAINNET_GENESIS_BYTES[..],
|
|
|
|
&zebra_test::vectors::BLOCK_MAINNET_1_BYTES[..],
|
2020-07-09 23:33:43 -07:00
|
|
|
// Other blocks can't verify, so they are rejected on drop
|
2020-07-09 23:51:01 -07:00
|
|
|
&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-09 23:51:01 -07:00
|
|
|
checkpoint_data.push((block.clone(), block.coinbase_height().unwrap(), 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
|
2020-07-09 23:51:01 -07:00
|
|
|
.iter()
|
|
|
|
.map(|(_block, height, hash)| (*height, *hash))
|
|
|
|
.collect();
|
|
|
|
|
2020-09-02 21:23:57 -07:00
|
|
|
let state_service = zebra_state::init(zebra_state::Config::ephemeral(), Mainnet);
|
2020-07-15 02:51:54 -07:00
|
|
|
let mut checkpoint_verifier =
|
2020-09-02 21:23:57 -07:00
|
|
|
CheckpointVerifier::from_list(checkpoint_list, None, state_service)
|
|
|
|
.map_err(|e| eyre!(e))?;
|
2020-07-09 23:51:01 -07:00
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.previous_checkpoint_height(),
|
|
|
|
BeforeGenesis
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.target_checkpoint_height(),
|
|
|
|
WaitingForBlocks
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.checkpoint_list.max_height(),
|
2020-08-16 11:42:02 -07:00
|
|
|
block::Height(434873)
|
2020-07-09 23:51:01 -07:00
|
|
|
);
|
|
|
|
|
|
|
|
let mut futures = Vec::new();
|
|
|
|
// Now collect verify futures for each block
|
|
|
|
for (block, height, hash) in checkpoint_data {
|
|
|
|
/// SPANDOC: Make sure the verifier service is ready
|
|
|
|
let ready_verifier_service = checkpoint_verifier
|
|
|
|
.ready_and()
|
|
|
|
.map_err(|e| eyre!(e))
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
/// SPANDOC: Set up the future for block {?height}
|
|
|
|
let verify_future = timeout(
|
|
|
|
Duration::from_secs(VERIFY_TIMEOUT_SECONDS),
|
|
|
|
ready_verifier_service.call(block.clone()),
|
|
|
|
);
|
|
|
|
|
|
|
|
futures.push((verify_future, height, hash));
|
|
|
|
|
|
|
|
// Only continuous checkpoints verify
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.previous_checkpoint_height(),
|
2020-08-16 11:42:02 -07:00
|
|
|
PreviousCheckpoint(block::Height(min(height.0, 1)))
|
2020-07-09 23:51:01 -07:00
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.target_checkpoint_height(),
|
|
|
|
WaitingForBlocks
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.checkpoint_list.max_height(),
|
2020-08-16 11:42:02 -07:00
|
|
|
block::Height(434873)
|
2020-07-09 23:51:01 -07:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now drop the verifier, to cancel the futures
|
|
|
|
drop(checkpoint_verifier);
|
|
|
|
|
|
|
|
for (verify_future, height, hash) in futures {
|
|
|
|
/// SPANDOC: Check the response for block {?height}
|
|
|
|
let verify_response = verify_future
|
|
|
|
.map_err(|e| eyre!(e))
|
|
|
|
.await
|
|
|
|
.expect("timeout should not happen");
|
|
|
|
|
2020-08-16 11:42:02 -07:00
|
|
|
if height <= block::Height(1) {
|
2020-07-09 23:51:01 -07:00
|
|
|
let verify_hash =
|
|
|
|
verify_response.expect("Continuous checkpoints should have succeeded before drop");
|
|
|
|
assert_eq!(verify_hash, hash);
|
|
|
|
} else {
|
|
|
|
// TODO(teor || jlusby): check error kind
|
|
|
|
verify_response.expect_err("Pending futures should fail on drop");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2020-07-21 06:11:51 -07:00
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn hard_coded_mainnet_test() -> Result<(), Report> {
|
|
|
|
hard_coded_mainnet().await
|
|
|
|
}
|
|
|
|
|
|
|
|
#[spandoc::spandoc]
|
|
|
|
async fn hard_coded_mainnet() -> Result<(), Report> {
|
|
|
|
zebra_test::init();
|
|
|
|
|
|
|
|
let block0 =
|
|
|
|
Arc::<Block>::zcash_deserialize(&zebra_test::vectors::BLOCK_MAINNET_GENESIS_BYTES[..])?;
|
2020-08-15 23:20:01 -07:00
|
|
|
let hash0 = block0.hash();
|
2020-07-21 06:11:51 -07:00
|
|
|
|
2020-09-02 21:23:57 -07:00
|
|
|
let state_service = zebra_state::init(zebra_state::Config::ephemeral(), Mainnet);
|
2020-07-21 06:11:51 -07:00
|
|
|
// Use the hard-coded checkpoint list
|
2020-09-02 21:23:57 -07:00
|
|
|
let mut checkpoint_verifier = CheckpointVerifier::new(Network::Mainnet, None, state_service);
|
2020-07-21 06:11:51 -07:00
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.previous_checkpoint_height(),
|
|
|
|
BeforeGenesis
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.target_checkpoint_height(),
|
|
|
|
WaitingForBlocks
|
|
|
|
);
|
2020-08-16 11:42:02 -07:00
|
|
|
assert!(checkpoint_verifier.checkpoint_list.max_height() > block::Height(0));
|
2020-07-21 06:11:51 -07:00
|
|
|
|
|
|
|
/// SPANDOC: Make sure the verifier service is ready
|
|
|
|
let ready_verifier_service = checkpoint_verifier
|
|
|
|
.ready_and()
|
|
|
|
.map_err(|e| eyre!(e))
|
|
|
|
.await?;
|
|
|
|
/// SPANDOC: Set up the future for block 0
|
|
|
|
let verify_future = timeout(
|
|
|
|
Duration::from_secs(VERIFY_TIMEOUT_SECONDS),
|
|
|
|
ready_verifier_service.call(block0.clone()),
|
|
|
|
);
|
|
|
|
/// SPANDOC: Wait for the response for block 0
|
|
|
|
// TODO(teor || jlusby): check error kind
|
|
|
|
let verify_response = verify_future
|
|
|
|
.map_err(|e| eyre!(e))
|
|
|
|
.await
|
|
|
|
.expect("timeout should not happen")
|
|
|
|
.expect("block should verify");
|
|
|
|
|
|
|
|
assert_eq!(verify_response, hash0);
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.previous_checkpoint_height(),
|
2020-08-16 11:42:02 -07:00
|
|
|
PreviousCheckpoint(block::Height(0))
|
2020-07-21 06:11:51 -07:00
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
checkpoint_verifier.target_checkpoint_height(),
|
|
|
|
WaitingForBlocks
|
|
|
|
);
|
|
|
|
// The lists will get bigger over time, so we just pick a recent height
|
2020-08-16 11:42:02 -07:00
|
|
|
assert!(checkpoint_verifier.checkpoint_list.max_height() > block::Height(900_000));
|
2020-07-21 06:11:51 -07:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|