use ckb_shared::types::HeaderIndexView;
use ckb_types::{
U256,
core::{BlockNumber, EpochNumberWithFraction, HeaderBuilder},
packed::Byte32,
};
use rand::{Rng, thread_rng};
use std::{
collections::{BTreeMap, HashMap},
sync::atomic::{
AtomicUsize,
Ordering::{Acquire, SeqCst},
},
};
use crate::types::{FILTER_TTL, TtlFilter};
const SKIPLIST_LENGTH: u64 = 10_000;
#[test]
fn test_get_ancestor_use_skip_list() {
let mut header_map: HashMap<Byte32, HeaderIndexView> = HashMap::default();
let mut hashes: BTreeMap<BlockNumber, Byte32> = BTreeMap::default();
let mut parent_hash = None;
for number in 0..SKIPLIST_LENGTH {
let mut header_builder =
HeaderBuilder::default()
.number(number)
.epoch(EpochNumberWithFraction::new(
number / 1000,
number % 1000,
1000,
));
if let Some(parent_hash) = parent_hash.take() {
header_builder = header_builder.parent_hash(parent_hash);
}
let header = header_builder.build();
hashes.insert(number, header.hash());
parent_hash = Some(header.hash());
let mut view: HeaderIndexView = (header, U256::zero()).into();
view.build_skip(0, |hash, _| header_map.get(hash).cloned(), |_, _| None);
header_map.insert(view.hash(), view);
}
for (number, hash) in &hashes {
if *number > 0 {
let skip_view = header_map
.get(hash)
.and_then(|view| header_map.get(view.skip_hash().unwrap()))
.unwrap();
assert_eq!(
Some(skip_view.hash()).as_ref(),
hashes.get(&skip_view.number())
);
assert!(skip_view.number() < *number);
} else {
assert!(header_map[hash].skip_hash().is_none());
}
}
let mut rng = thread_rng();
let a_to_b = |a, b, limit| {
let count = AtomicUsize::new(0);
let header = header_map
.get(&hashes[&a])
.cloned()
.unwrap()
.get_ancestor(
0,
b,
|hash, _| {
count.fetch_add(1, SeqCst);
header_map.get(hash).cloned()
},
|_, _| None,
)
.unwrap();
assert!(count.load(Acquire) <= limit);
header
};
for _ in 0..100 {
let from: u64 = rng.gen_range(0..SKIPLIST_LENGTH);
let to: u64 = rng.gen_range(0..=from);
let view_from = &header_map[&hashes[&from]];
let view_to = &header_map[&hashes[&to]];
let view_0 = &header_map[&hashes[&0]];
let found_from_header = a_to_b(SKIPLIST_LENGTH - 1, from, 120);
assert_eq!(found_from_header.hash(), view_from.hash());
let found_to_header = a_to_b(from, to, 120);
assert_eq!(found_to_header.hash(), view_to.hash());
let found_0_header = a_to_b(from, 0, 120);
assert_eq!(found_0_header.hash(), view_0.hash());
}
}
#[test]
fn ttl_filter() {
let mut filter = TtlFilter::default();
let mut _faketime_guard = ckb_systemtime::faketime();
_faketime_guard.set_faketime(0);
filter.insert(1);
let mut _faketime_guard = ckb_systemtime::faketime();
_faketime_guard.set_faketime(FILTER_TTL * 1000 + 1000);
filter.insert(2);
filter.remove_expired();
assert!(!filter.contains(&1));
assert!(filter.contains(&2));
}