use std::{
fmt::{Debug, Display},
ops::Range,
};
use crate::prelude::*;
#[doc(inline)]
pub use crate::internal::hash::{Forgotten, Hash};
pub(crate) trait Any<'tree>: GetHash + sealed::Sealed {
fn children(&'tree self) -> Vec<HashOrNode<'tree>>;
fn kind(&self) -> Kind;
fn forgotten(&self) -> Forgotten;
}
impl GetHash for &dyn Any<'_> {
fn hash(&self) -> Hash {
(**self).hash()
}
fn cached_hash(&self) -> Option<Hash> {
(**self).cached_hash()
}
fn clear_cached_hash(&self) {
(**self).clear_cached_hash()
}
}
impl<'tree, T: Any<'tree>> Any<'tree> for &T {
fn kind(&self) -> Kind {
(**self).kind()
}
fn forgotten(&self) -> Forgotten {
(**self).forgotten()
}
fn children(&'tree self) -> Vec<HashOrNode<'tree>> {
(**self).children()
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Kind {
Leaf {
commitment: Option<StateCommitment>,
},
Internal {
height: u8,
},
}
impl Display for Kind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Kind::Leaf { .. } => write!(f, "Leaf",),
Kind::Internal { .. } => write!(f, "Node"),
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Place {
Complete,
Frontier,
}
impl Display for Place {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Place::Frontier => write!(f, "frontier"),
Place::Complete => write!(f, "complete"),
}
}
}
#[derive(Clone, Copy)]
pub struct Node<'tree> {
offset: u64,
global_position: Option<Position>,
this: HashOrNode<'tree>,
}
impl GetHash for Node<'_> {
fn hash(&self) -> Hash {
self.this.hash()
}
fn cached_hash(&self) -> Option<Hash> {
self.this.cached_hash()
}
fn clear_cached_hash(&self) {
self.this.clear_cached_hash()
}
}
#[derive(Clone, Copy)]
pub(crate) enum HashOrNode<'tree> {
Hash(HashedNode),
Node(&'tree dyn Any<'tree>),
}
impl GetHash for HashOrNode<'_> {
fn hash(&self) -> Hash {
match self {
HashOrNode::Hash(hashed) => hashed.hash(),
HashOrNode::Node(node) => node.hash(),
}
}
fn cached_hash(&self) -> Option<Hash> {
match self {
HashOrNode::Hash(hashed) => Some(hashed.hash()),
HashOrNode::Node(node) => node.cached_hash(),
}
}
fn clear_cached_hash(&self) {
if let HashOrNode::Node(node) = self {
node.clear_cached_hash()
}
}
}
#[derive(Clone, Copy)]
pub(crate) struct HashedNode {
pub hash: Hash,
pub height: u8,
pub forgotten: Forgotten,
}
impl GetHash for HashedNode {
fn hash(&self) -> Hash {
self.hash
}
fn cached_hash(&self) -> Option<Hash> {
Some(self.hash)
}
fn clear_cached_hash(&self) {}
}
impl Debug for Node<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let name = format!("{}::{}", self.place(), self.kind());
let mut s = f.debug_struct(&name);
if self.height() != 0 {
s.field("height", &(*self).height());
}
s.field("position", &u64::from(self.position()));
if self.forgotten() != Forgotten::default() {
s.field("forgotten", &self.forgotten());
}
if let Some(hash) = self.cached_hash() {
s.field("hash", &hash);
}
if let Kind::Leaf {
commitment: Some(commitment),
} = self.kind()
{
s.field("commitment", &commitment);
}
let children = self.children();
if !children.is_empty() {
s.field("children", &children);
}
s.finish()
}
}
impl Display for Node<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct(&format!("{}::{}", self.place(), self.kind()))
.field("height", &self.height())
.field("position", &self.position())
.finish_non_exhaustive()
}
}
impl<'tree> Node<'tree> {
pub(crate) fn root<R: Any<'tree> + GetPosition>(node: &'tree R) -> Self {
Self {
offset: 0,
global_position: node.position().map(Into::into),
this: HashOrNode::Node(node),
}
}
pub fn hash(&self) -> Hash {
self.this.hash()
}
pub fn cached_hash(&self) -> Option<Hash> {
self.this.cached_hash()
}
pub fn kind(&self) -> Kind {
match self.this {
HashOrNode::Hash(HashedNode { height, .. }) => Kind::Internal { height },
HashOrNode::Node(node) => node.kind(),
}
}
pub fn forgotten(&self) -> Forgotten {
match self.this {
HashOrNode::Hash(HashedNode { forgotten, .. }) => forgotten,
HashOrNode::Node(node) => node.forgotten(),
}
}
pub fn children(&self) -> Vec<Node<'tree>> {
match self.this {
HashOrNode::Hash(_) => Vec::new(),
HashOrNode::Node(node) => node
.children()
.into_iter()
.enumerate()
.map(|(i, hash_or_node)| Node {
global_position: self.global_position,
offset: self.offset * 4 + (i as u64),
this: hash_or_node,
})
.collect(),
}
}
pub fn index(&self) -> u64 {
self.offset
}
pub fn height(&self) -> u8 {
match self.kind() {
Kind::Internal { height } => height,
Kind::Leaf { .. } => 0,
}
}
pub fn position(&self) -> Position {
(4u64.pow(self.height() as u32) * self.index()).into()
}
pub fn stride(&self) -> u64 {
4u64.pow(self.height() as u32)
}
pub fn range(&self) -> Range<Position> {
let position: u64 = self.position().into();
position.into()..(position + self.stride()).min(4u64.pow(24) - 1).into()
}
pub fn global_position(&self) -> Option<Position> {
self.global_position
}
pub fn place(&self) -> Place {
if let Some(global_position) = self.global_position() {
if let Some(frontier_tip) = u64::from(global_position).checked_sub(1) {
let height = self.height();
let position = u64::from(self.position());
if position >> (height * 2) == frontier_tip >> (height * 2) {
Place::Frontier
} else {
Place::Complete
}
} else {
Place::Frontier
}
} else {
Place::Complete
}
}
}
mod sealed {
use super::*;
pub trait Sealed: Send + Sync {}
impl<T: Sealed> Sealed for &T {}
impl Sealed for Node<'_> {}
impl Sealed for complete::Item {}
impl<T: Sealed> Sealed for complete::Leaf<T> {}
impl<T: Sealed + Clone> Sealed for complete::Node<T> {}
impl<T: Sealed + Height + GetHash + Clone> Sealed for complete::Tier<T> {}
impl<T: Sealed + Height + GetHash + Clone> Sealed for complete::Top<T> {}
impl Sealed for frontier::Item {}
impl<T: Sealed> Sealed for frontier::Leaf<T> {}
impl<T: Sealed + Focus> Sealed for frontier::Node<T> where T::Complete: Send + Sync {}
impl<T: Sealed + Height + GetHash + Focus + Clone> Sealed for frontier::Tier<T> where
T::Complete: Send + Sync + Clone
{
}
impl<T: Sealed + Height + GetHash + Focus + Clone> Sealed for frontier::Top<T> where
T::Complete: Send + Sync + Clone
{
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn indexing_correct() {
const MAX_SIZE_TO_TEST: u16 = 100;
let mut top: frontier::Top<Item> = frontier::Top::new(frontier::TrackForgotten::No);
for i in 0..MAX_SIZE_TO_TEST {
top.insert(StateCommitment(i.into()).into()).unwrap();
}
fn check_leaves(index: &mut [u64; 9], node: Node) {
assert_eq!(node.index(), index[usize::from(node.height())], "{node}");
index[usize::from(node.height())] += 1;
for child in node.children() {
check_leaves(index, child);
}
}
check_leaves(&mut [0; 9], Node::root(&top));
}
#[test]
fn place_correct() {
const MAX_SIZE_TO_TEST: u16 = 100;
let mut top: frontier::Top<Item> = frontier::Top::new(frontier::TrackForgotten::No);
for i in 0..MAX_SIZE_TO_TEST {
top.insert(StateCommitment(i.into()).into()).unwrap();
let root = Node::root(&top);
check(root, Place::Frontier);
}
fn check(node: Node, expected: Place) {
assert_eq!(node.place(), expected);
match node.children().as_slice() {
[] => {}
[a] => {
check(*a, expected);
}
[a, b] => {
check(*a, Place::Complete);
check(*b, expected);
}
[a, b, c] => {
check(*a, Place::Complete);
check(*b, Place::Complete);
check(*c, expected);
}
[a, b, c, d] => {
check(*a, Place::Complete);
check(*b, Place::Complete);
check(*c, Place::Complete);
check(*d, expected);
}
_ => unreachable!("nodes can't have > 4 children"),
}
}
}
#[test]
fn height_correct() {
const MAX_SIZE_TO_TEST: u16 = 100;
let mut tree = crate::Tree::new();
for i in 0..MAX_SIZE_TO_TEST {
tree.insert(crate::Witness::Keep, StateCommitment(i.into()))
.unwrap();
let root = tree.structure();
check(root, 24);
}
fn check(node: Node, expected: u8) {
assert_eq!(node.height(), expected, "{node}");
for child in node.children() {
check(child, expected - 1);
}
}
}
}