#![allow(dead_code)]
use alloc::collections::{BTreeMap, btree_map::Entry};
use core::{fmt, iter, mem};
pub struct DisjointBlocks<TBl> {
blocks: BTreeMap<(u64, [u8; 32]), Block<TBl>>,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
struct Block<TBl> {
parent_hash: Option<[u8; 32]>,
bad: bool,
user_data: TBl,
}
impl<TBl> DisjointBlocks<TBl> {
pub fn new() -> Self {
Self::with_capacity(0)
}
pub fn with_capacity(_capacity: usize) -> Self {
DisjointBlocks {
blocks: BTreeMap::default(),
}
}
pub fn is_empty(&self) -> bool {
self.blocks.is_empty()
}
pub fn len(&self) -> usize {
self.blocks.len()
}
pub fn iter(&self) -> impl Iterator<Item = (u64, &[u8; 32], &TBl)> {
self.blocks
.iter()
.map(|((he, ha), bl)| (*he, ha, &bl.user_data))
}
pub fn contains(&self, height: u64, hash: &[u8; 32]) -> bool {
self.blocks.contains_key(&(height, *hash))
}
pub fn insert(
&mut self,
height: u64,
hash: [u8; 32],
parent_hash: Option<[u8; 32]>,
user_data: TBl,
) -> Option<TBl> {
let previous_user_data = match self.blocks.entry((height, hash)) {
Entry::Occupied(entry) => {
let entry = entry.into_mut();
match (parent_hash, &mut entry.parent_hash) {
(Some(parent_hash), &mut Some(ph)) if ph != parent_hash => panic!(),
_ => {}
}
Some(mem::replace(&mut entry.user_data, user_data))
}
Entry::Vacant(entry) => {
entry.insert(Block {
parent_hash: None,
bad: false,
user_data,
});
None
}
};
if let Some(parent_hash) = parent_hash {
self.set_parent_hash(height, &hash, parent_hash);
}
previous_user_data
}
#[track_caller]
pub fn remove(&mut self, height: u64, hash: &[u8; 32]) -> TBl {
self.blocks.remove(&(height, *hash)).unwrap().user_data
}
pub fn remove_below_height(
&mut self,
threshold: u64,
) -> impl ExactSizeIterator<Item = (u64, [u8; 32], TBl)> + use<TBl> {
let above_threshold = self.blocks.split_off(&(threshold, [0; 32]));
let below_threshold = mem::replace(&mut self.blocks, above_threshold);
below_threshold
.into_iter()
.map(|((he, ha), v)| (he, ha, v.user_data))
}
pub fn user_data(&self, height: u64, hash: &[u8; 32]) -> Option<&TBl> {
Some(&self.blocks.get(&(height, *hash))?.user_data)
}
pub fn user_data_mut(&mut self, height: u64, hash: &[u8; 32]) -> Option<&mut TBl> {
Some(&mut self.blocks.get_mut(&(height, *hash))?.user_data)
}
pub fn parent_hash(&self, height: u64, hash: &[u8; 32]) -> Option<&[u8; 32]> {
self.blocks
.get(&(height, *hash))
.and_then(|b| b.parent_hash.as_ref())
}
pub fn children(
&self,
height: u64,
hash: &[u8; 32],
) -> impl Iterator<Item = (u64, &[u8; 32], &TBl)> {
let hash = *hash;
self.blocks
.range((height + 1, [0; 32])..=(height + 1, [0xff; 32]))
.filter(move |((_maybe_child_height, _), maybe_child)| {
debug_assert_eq!(*_maybe_child_height, height + 1);
maybe_child.parent_hash.as_ref() == Some(&hash)
})
.map(|((he, ha), bl)| (*he, ha, &bl.user_data))
}
#[track_caller]
pub fn set_parent_hash(&mut self, height: u64, hash: &[u8; 32], parent_hash: [u8; 32]) {
let parent_is_bad = match height.checked_sub(1) {
Some(parent_height) => self
.blocks
.get(&(parent_height, parent_hash))
.map_or(false, |b| b.bad),
None => false,
};
let block = self.blocks.get_mut(&(height, *hash)).unwrap();
match &mut block.parent_hash {
&mut Some(ph) => assert_eq!(ph, parent_hash),
ph @ &mut None => *ph = Some(parent_hash),
}
if parent_is_bad {
self.set_block_bad(height, hash);
}
}
#[track_caller]
pub fn set_block_bad(&mut self, mut height: u64, hash: &[u8; 32]) {
let mut blocks =
hashbrown::HashSet::with_capacity_and_hasher(1, fnv::FnvBuildHasher::default());
blocks.insert(*hash);
while !blocks.is_empty() {
let mut children = hashbrown::HashSet::with_capacity_and_hasher(
blocks.len() * 4,
fnv::FnvBuildHasher::default(),
);
for ((_maybe_child_height, maybe_child_hash), maybe_child) in self
.blocks
.range((height + 1, [0; 32])..=(height + 1, [0xff; 32]))
{
debug_assert_eq!(*_maybe_child_height, height + 1);
if maybe_child
.parent_hash
.as_ref()
.map_or(false, |p| blocks.contains(p))
{
children.insert(*maybe_child_hash);
}
}
for hash in blocks {
self.blocks.get_mut(&(height, hash)).unwrap().bad = true;
}
blocks = children;
height += 1;
}
}
pub fn good_tree_roots(&self) -> impl Iterator<Item = TreeRoot> {
self.blocks
.iter()
.filter(|(_, block)| !block.bad)
.filter_map(move |((height, hash), block)| {
let parent_hash = block.parent_hash.as_ref()?;
if self.blocks.contains_key(&(*height - 1, *parent_hash)) {
return None;
}
Some(TreeRoot {
block_hash: *hash,
block_number: *height,
parent_block_hash: *parent_hash,
})
})
}
pub fn unknown_blocks(&self) -> impl Iterator<Item = (u64, &[u8; 32])> {
let mut iter1 = self
.blocks
.iter()
.filter(|(_, s)| !s.bad)
.filter(|(_, s)| s.parent_hash.is_none())
.map(|((n, h), _)| (*n, h))
.peekable();
let mut iter2 = self
.blocks
.iter()
.filter(|(_, s)| !s.bad)
.filter_map(|((n, _), s)| s.parent_hash.as_ref().map(|h| (n - 1, h)))
.filter(move |(n, h)| !self.blocks.contains_key(&(*n, **h)))
.peekable();
iter::from_fn(move || match (iter1.peek(), iter2.peek()) {
(Some((b1, _)), Some((b2, _))) if b1 > b2 => iter2.next(),
(Some(_), Some(_)) => iter1.next(),
(Some(_), None) => iter1.next(),
(None, Some(_)) => iter2.next(),
(None, None) => None,
})
}
pub fn is_bad(&self, height: u64, hash: &[u8; 32]) -> Option<bool> {
Some(self.blocks.get(&(height, *hash))?.bad)
}
pub fn is_parent_bad(&self, height: u64, hash: &[u8; 32]) -> Option<bool> {
let parent_hash = self.blocks.get(&(height, *hash))?.parent_hash?;
let parent_height = match height.checked_sub(1) {
Some(h) => h,
None => return Some(false), };
Some(
self.blocks
.get(&(parent_height, parent_hash))
.map_or(false, |parent| parent.bad),
)
}
}
impl<TBl: fmt::Debug> fmt::Debug for DisjointBlocks<TBl> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(&self.blocks, f)
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct TreeRoot {
pub block_hash: [u8; 32],
pub block_number: u64,
pub parent_block_hash: [u8; 32],
}
#[cfg(test)]
mod tests {
#[test]
fn insert_doesnt_override_bad() {
let mut collection = super::DisjointBlocks::new();
assert!(collection.insert(1, [1; 32], Some([0; 32]), ()).is_none());
assert_eq!(collection.unknown_blocks().count(), 1);
collection.set_block_bad(1, &[1; 32]);
assert_eq!(collection.unknown_blocks().count(), 0);
assert!(collection.insert(1, [1; 32], Some([0; 32]), ()).is_some());
assert_eq!(collection.unknown_blocks().count(), 0);
}
#[test]
fn insert_doesnt_override_parent() {
let mut collection = super::DisjointBlocks::new();
assert!(collection.insert(1, [1; 32], Some([0; 32]), ()).is_none());
assert_eq!(
collection.unknown_blocks().collect::<Vec<_>>(),
vec![(0, &[0; 32])]
);
assert!(collection.insert(1, [1; 32], None, ()).is_some());
assert_eq!(
collection.unknown_blocks().collect::<Vec<_>>(),
vec![(0, &[0; 32])]
);
}
#[test]
fn set_parent_hash_updates_bad() {
let mut collection = super::DisjointBlocks::new();
collection.insert(1, [1; 32], Some([0; 32]), ());
assert_eq!(collection.unknown_blocks().count(), 1);
collection.set_block_bad(1, &[1; 32]);
assert_eq!(collection.unknown_blocks().count(), 0);
collection.insert(2, [2; 32], None, ());
assert!(!collection.is_bad(2, &[2; 32]).unwrap());
collection.insert(3, [3; 32], Some([2; 32]), ());
assert!(!collection.is_bad(3, &[3; 32]).unwrap());
collection.insert(3, [31; 32], Some([2; 32]), ());
assert!(!collection.is_bad(3, &[31; 32]).unwrap());
collection.insert(4, [4; 32], Some([3; 32]), ());
assert!(!collection.is_bad(4, &[4; 32]).unwrap());
assert_eq!(collection.unknown_blocks().count(), 1);
collection.set_parent_hash(2, &[2; 32], [1; 32]);
assert_eq!(collection.unknown_blocks().count(), 0);
assert!(collection.is_bad(2, &[2; 32]).unwrap());
assert!(collection.is_bad(3, &[3; 32]).unwrap());
assert!(collection.is_bad(3, &[31; 32]).unwrap());
assert!(collection.is_bad(4, &[4; 32]).unwrap());
}
#[test]
fn insert_updates_bad() {
let mut collection = super::DisjointBlocks::new();
collection.insert(1, [1; 32], Some([0; 32]), ());
collection.set_block_bad(1, &[1; 32]);
assert_eq!(collection.unknown_blocks().count(), 0);
collection.insert(2, [2; 32], Some([1; 32]), ());
assert_eq!(collection.unknown_blocks().count(), 0);
collection.insert(1, [0x80; 32], Some([0; 32]), ());
assert_eq!(collection.unknown_blocks().count(), 1);
}
#[test]
fn insert_filling_gap_updates_bad() {
let mut collection = super::DisjointBlocks::new();
collection.insert(1, [1; 32], Some([0; 32]), ());
collection.set_block_bad(1, &[1; 32]);
assert!(collection.is_bad(1, &[1; 32]).unwrap());
collection.insert(3, [3; 32], Some([2; 32]), ());
assert!(!collection.is_bad(3, &[3; 32]).unwrap());
collection.insert(2, [2; 32], Some([1; 32]), ());
assert!(collection.is_bad(2, &[2; 32]).unwrap());
assert!(collection.is_bad(3, &[3; 32]).unwrap());
}
}