use std::sync::Arc;
use tokio::sync::oneshot;
use zebra_chain::{block::Block, serialization::ZcashDeserializeInto};
use zebra_test::prelude::*;
use crate::{
arbitrary::Prepare,
service::queued_blocks::{QueuedBlocks, QueuedSemanticallyVerified, SentHashes},
tests::FakeChainHelper,
};
trait IntoQueued {
fn into_queued(self) -> QueuedSemanticallyVerified;
}
impl IntoQueued for Arc<Block> {
fn into_queued(self) -> QueuedSemanticallyVerified {
let (rsp_tx, _) = oneshot::channel();
(self.prepare(), rsp_tx)
}
}
#[test]
fn dequeue_gives_right_children() -> Result<()> {
let _init_guard = zebra_test::init();
let block1: Arc<Block> =
zebra_test::vectors::BLOCK_MAINNET_419200_BYTES.zcash_deserialize_into()?;
let child1: Arc<Block> =
zebra_test::vectors::BLOCK_MAINNET_419201_BYTES.zcash_deserialize_into()?;
let child2 = block1.make_fake_child();
let parent = block1.header.previous_block_hash;
let mut queue = QueuedBlocks::default();
assert_eq!(0, queue.blocks.len());
assert_eq!(0, queue.by_parent.len());
assert_eq!(0, queue.by_height.len());
assert_eq!(0, queue.known_utxos.len());
queue.queue(block1.clone().into_queued());
assert_eq!(1, queue.blocks.len());
assert_eq!(1, queue.by_parent.len());
assert_eq!(1, queue.by_height.len());
assert_eq!(2, queue.known_utxos.len());
queue.queue(child1.clone().into_queued());
assert_eq!(2, queue.blocks.len());
assert_eq!(2, queue.by_parent.len());
assert_eq!(2, queue.by_height.len());
assert_eq!(632, queue.known_utxos.len());
queue.queue(child2.clone().into_queued());
assert_eq!(3, queue.blocks.len());
assert_eq!(2, queue.by_parent.len());
assert_eq!(2, queue.by_height.len());
assert_eq!(634, queue.known_utxos.len());
let children = queue.dequeue_children(parent);
assert_eq!(1, children.len());
assert_eq!(block1, children[0].0.block);
assert_eq!(2, queue.blocks.len());
assert_eq!(1, queue.by_parent.len());
assert_eq!(1, queue.by_height.len());
assert_eq!(632, queue.known_utxos.len());
let parent = children[0].0.block.hash();
let children = queue.dequeue_children(parent);
assert_eq!(2, children.len());
assert!(children
.iter()
.any(|(block, _)| block.hash == child1.hash()));
assert!(children
.iter()
.any(|(block, _)| block.hash == child2.hash()));
assert_eq!(0, queue.blocks.len());
assert_eq!(0, queue.by_parent.len());
assert_eq!(0, queue.by_height.len());
assert_eq!(0, queue.known_utxos.len());
Ok(())
}
#[test]
fn prune_removes_right_children() -> Result<()> {
let _init_guard = zebra_test::init();
let block1: Arc<Block> =
zebra_test::vectors::BLOCK_MAINNET_419200_BYTES.zcash_deserialize_into()?;
let child1: Arc<Block> =
zebra_test::vectors::BLOCK_MAINNET_419201_BYTES.zcash_deserialize_into()?;
let child2 = block1.make_fake_child();
let mut queue = QueuedBlocks::default();
queue.queue(block1.clone().into_queued());
queue.queue(child1.clone().into_queued());
queue.queue(child2.clone().into_queued());
assert_eq!(3, queue.blocks.len());
assert_eq!(2, queue.by_parent.len());
assert_eq!(2, queue.by_height.len());
assert_eq!(634, queue.known_utxos.len());
queue.prune_by_height(block1.coinbase_height().unwrap());
assert_eq!(2, queue.blocks.len());
assert_eq!(1, queue.by_parent.len());
assert_eq!(1, queue.by_height.len());
assert!(queue.get_mut(&block1.hash()).is_none());
assert!(queue.get_mut(&child1.hash()).is_some());
assert!(queue.get_mut(&child2.hash()).is_some());
assert_eq!(632, queue.known_utxos.len());
queue.prune_by_height(child1.coinbase_height().unwrap());
assert_eq!(0, queue.blocks.len());
assert_eq!(0, queue.by_parent.len());
assert_eq!(0, queue.by_height.len());
assert!(queue.get_mut(&child1.hash()).is_none());
assert!(queue.get_mut(&child2.hash()).is_none());
assert_eq!(0, queue.known_utxos.len());
Ok(())
}
#[test]
fn sent_hashes_remove_drops_rejected_hash_and_utxos() -> Result<()> {
let _init_guard = zebra_test::init();
let block1: Arc<Block> =
zebra_test::vectors::BLOCK_MAINNET_419200_BYTES.zcash_deserialize_into()?;
let block2: Arc<Block> =
zebra_test::vectors::BLOCK_MAINNET_419201_BYTES.zcash_deserialize_into()?;
let prepared1 = block1.clone().prepare();
let prepared2 = block2.clone().prepare();
let mut sent = SentHashes::default();
sent.add(&prepared1);
sent.add(&prepared2);
let utxos_after_add = sent.known_utxos.len();
assert!(sent.contains(&prepared1.hash));
assert!(sent.contains(&prepared2.hash));
assert!(utxos_after_add > 0);
let block1_utxos = prepared1.new_outputs.len();
sent.remove(&prepared1.hash);
assert!(
!sent.contains(&prepared1.hash),
"removed hash must not satisfy contains()"
);
assert!(sent.contains(&prepared2.hash));
assert_eq!(
sent.known_utxos.len(),
utxos_after_add - block1_utxos,
"remove must drop only the removed block's outpoints"
);
assert!(
!sent.curr_buf.iter().any(|(h, _)| h == &prepared1.hash),
"remove must drop the (hash, height) entry from curr_buf"
);
assert!(sent.curr_buf.iter().any(|(h, _)| h == &prepared2.hash));
let block3 = block1.make_fake_child();
sent.remove(&block3.hash());
assert!(sent.contains(&prepared2.hash));
Ok(())
}