use alloc::{boxed::Box, vec::Vec};
use core::{array, iter, ops};
use super::storage_diff::TrieDiff;
use crate::trie;
pub use trie::{Nibble, TrieEntryVersion};
mod tests;
pub struct Config {
pub diff: TrieDiff,
pub diff_trie_entries_version: TrieEntryVersion,
pub max_trie_recalculation_depth_hint: usize,
}
pub fn trie_root_calculator(config: Config) -> InProgress {
Box::new(Inner {
stack: Vec::with_capacity(config.max_trie_recalculation_depth_hint),
diff: config.diff,
diff_trie_entries_version: config.diff_trie_entries_version,
})
.next()
}
pub enum InProgress {
Finished {
trie_root_hash: [u8; 32],
},
ClosestDescendant(ClosestDescendant),
StorageValue(StorageValue),
ClosestDescendantMerkleValue(ClosestDescendantMerkleValue),
TrieNodeInsertUpdateEvent(TrieNodeInsertUpdateEvent),
TrieNodeRemoveEvent(TrieNodeRemoveEvent),
}
pub struct ClosestDescendant {
inner: Box<Inner>,
key_extra_nibbles: Vec<Nibble>,
diff_inserts_lcd: Option<Vec<Nibble>>,
}
impl ClosestDescendant {
pub fn key(&self) -> impl Iterator<Item = impl AsRef<[Nibble]>> {
self.inner
.current_node_full_key()
.map(either::Left)
.chain(iter::once(either::Right(&self.key_extra_nibbles)))
}
pub fn key_as_vec(&self) -> Vec<Nibble> {
self.key().fold(Vec::new(), |mut a, b| {
a.extend_from_slice(b.as_ref());
a
})
}
pub fn inject(
mut self,
closest_descendant: Option<impl Iterator<Item = Nibble>>,
) -> InProgress {
debug_assert!(
self.inner
.stack
.last()
.map_or(true, |n| n.children.len() != 16)
);
let iter_key_len = self
.inner
.current_node_full_key()
.fold(0, |acc, k| acc + k.as_ref().len());
let (maybe_node_partial_key, children_partial_key_changed) =
match (self.diff_inserts_lcd, closest_descendant) {
(Some(inserted_key), Some(base_trie_key)) => {
let mut inserted_key_iter = inserted_key.iter().copied().skip(iter_key_len);
let mut base_trie_key = base_trie_key.skip(iter_key_len);
let mut maybe_node_pk =
Vec::with_capacity(inserted_key.len() - iter_key_len + 8);
let mut children_pk_changed = false;
loop {
match (inserted_key_iter.next(), base_trie_key.next()) {
(Some(inib), Some(bnib)) if inib == bnib => maybe_node_pk.push(inib),
(Some(_), Some(_)) | (None, Some(_)) => {
children_pk_changed = true;
break;
}
(Some(_), None) | (None, None) => break,
}
}
(maybe_node_pk, children_pk_changed)
}
(Some(inserted_key), None) => (
inserted_key
.into_iter()
.skip(iter_key_len)
.collect::<Vec<_>>(),
true,
),
(None, Some(base_trie_key)) => (base_trie_key.skip(iter_key_len).collect(), false),
(None, None) => {
return if let Some(parent_node) = self.inner.stack.last_mut() {
debug_assert_ne!(parent_node.children.len(), 16);
parent_node.children.push(None);
self.inner.next()
} else {
InProgress::Finished {
trie_root_hash: trie::EMPTY_BLAKE2_TRIE_MERKLE_VALUE,
}
};
}
};
self.inner.stack.push(InProgressNode {
partial_key: maybe_node_partial_key,
children_partial_key_changed,
children: arrayvec::ArrayVec::new(),
});
self.inner.next()
}
}
pub struct StorageValue(Box<Inner>);
impl StorageValue {
pub fn key(&self) -> impl Iterator<Item = impl AsRef<[Nibble]>> {
self.0.current_node_full_key()
}
pub fn key_as_vec(&self) -> Vec<Nibble> {
self.key().fold(Vec::new(), |mut a, b| {
a.extend_from_slice(b.as_ref());
a
})
}
pub fn inject_value(
mut self,
base_trie_storage_value: Option<(&[u8], TrieEntryVersion)>,
) -> InProgress {
let maybe_storage_value = if self.key().fold(0, |a, b| a + b.as_ref().len()) % 2 == 0 {
let key_nibbles = self.key().fold(Vec::new(), |mut a, b| {
a.extend_from_slice(b.as_ref());
a
});
debug_assert_eq!(key_nibbles.len() % 2, 0);
let key_as_u8 =
trie::nibbles_to_bytes_suffix_extend(key_nibbles.into_iter()).collect::<Vec<_>>();
if let Some((value, _)) = self.0.diff.diff_get(&key_as_u8) {
value.map(|v| (v, self.0.diff_trie_entries_version))
} else {
base_trie_storage_value
}
} else {
base_trie_storage_value
};
let calculated_elem = self.0.stack.pop().unwrap_or_else(|| panic!());
debug_assert_eq!(calculated_elem.children.len(), 16);
let node_num_children = calculated_elem
.children
.iter()
.filter(|c| c.is_some())
.count();
let parent_node = self.0.stack.last_mut();
debug_assert!(parent_node.map_or(true, |p| p.children.len() != 16));
match (
base_trie_storage_value,
maybe_storage_value,
node_num_children,
self.0.stack.last_mut(),
) {
(_, None, 0, Some(_parent_node)) => {
InProgress::TrieNodeRemoveEvent(TrieNodeRemoveEvent {
inner: self.0,
calculated_elem,
ty: TrieNodeRemoveEventTy::NoChildrenLeft,
})
}
(_, None, 1, _parent_node) if !calculated_elem.children_partial_key_changed => {
InProgress::TrieNodeRemoveEvent(TrieNodeRemoveEvent {
inner: self.0,
calculated_elem,
ty: TrieNodeRemoveEventTy::ReplacedWithSingleChild,
})
}
(_, None, 1, _parent_node) => {
TrieNodeRemoveEvent {
inner: self.0,
calculated_elem,
ty: TrieNodeRemoveEventTy::ReplacedWithSingleChild,
}
.resume()
}
(Some(_), None, 0, None) => {
InProgress::TrieNodeRemoveEvent(TrieNodeRemoveEvent {
inner: self.0,
calculated_elem,
ty: TrieNodeRemoveEventTy::NoChildrenLeft,
})
}
(None, None, 0, None) => {
InProgress::Finished {
trie_root_hash: trie::EMPTY_BLAKE2_TRIE_MERKLE_VALUE,
}
}
(_, _, _, parent_node) => {
let storage_value_hash =
if let Some((value, TrieEntryVersion::V1)) = maybe_storage_value {
if value.len() >= 33 {
Some(blake2_rfc::blake2b::blake2b(32, &[], value))
} else {
None
}
} else {
None
};
let merkle_value = trie::trie_node::calculate_merkle_value(
trie::trie_node::Decoded {
children: array::from_fn(|n| calculated_elem.children[n].as_ref()),
partial_key: calculated_elem.partial_key.iter().copied(),
storage_value: match (maybe_storage_value, storage_value_hash.as_ref()) {
(_, Some(storage_value_hash)) => trie::trie_node::StorageValue::Hashed(
<&[u8; 32]>::try_from(storage_value_hash.as_bytes())
.unwrap_or_else(|_| panic!()),
),
(Some((value, _)), None) => {
trie::trie_node::StorageValue::Unhashed(value)
}
(None, _) => trie::trie_node::StorageValue::None,
},
},
trie::HashFunction::Blake2,
parent_node.is_none(),
)
.unwrap_or_else(|_| panic!());
InProgress::TrieNodeInsertUpdateEvent(TrieNodeInsertUpdateEvent {
inner: self.0,
children: calculated_elem.children,
inserted_elem_partial_key: calculated_elem.partial_key,
merkle_value,
})
}
}
}
}
pub struct ClosestDescendantMerkleValue {
inner: Box<Inner>,
}
impl ClosestDescendantMerkleValue {
pub fn key(&self) -> impl Iterator<Item = impl AsRef<[Nibble]>> {
self.inner.current_node_full_key()
}
pub fn key_as_vec(&self) -> Vec<Nibble> {
self.key().fold(Vec::new(), |mut a, b| {
a.extend_from_slice(b.as_ref());
a
})
}
pub fn resume_unknown(self) -> InProgress {
InProgress::ClosestDescendant(ClosestDescendant {
inner: self.inner,
key_extra_nibbles: Vec::new(),
diff_inserts_lcd: None,
})
}
pub fn inject_merkle_value(mut self, merkle_value: &[u8]) -> InProgress {
debug_assert!(merkle_value.len() == 32 || trie::trie_node::decode(merkle_value).is_ok());
if let Some(parent_node) = self.inner.stack.last_mut() {
debug_assert_ne!(parent_node.children.len(), 16);
parent_node
.children
.push(Some(trie::trie_node::MerkleValueOutput::from_bytes(
AsRef::as_ref(&merkle_value),
)));
self.inner.next()
} else {
debug_assert_eq!(self.inner.diff.diff_range_ordered::<Vec<u8>>(..).count(), 0);
let trie_root_hash = <[u8; 32]>::try_from(AsRef::as_ref(&merkle_value))
.unwrap_or_else(|_| panic!("invalid node value provided"));
InProgress::Finished { trie_root_hash }
}
}
}
pub struct TrieNodeInsertUpdateEvent {
inner: Box<Inner>,
inserted_elem_partial_key: Vec<Nibble>,
children: arrayvec::ArrayVec<Option<trie::trie_node::MerkleValueOutput>, 16>,
merkle_value: trie::trie_node::MerkleValueOutput,
}
impl TrieNodeInsertUpdateEvent {
pub fn key(&self) -> impl Iterator<Item = impl AsRef<[Nibble]>> {
self.inner
.current_node_full_key()
.map(either::Left)
.chain(iter::once(either::Right(&self.inserted_elem_partial_key)))
}
pub fn key_as_vec(&self) -> Vec<Nibble> {
self.key().fold(Vec::new(), |mut a, b| {
a.extend_from_slice(b.as_ref());
a
})
}
pub fn merkle_value(&self) -> &[u8] {
self.merkle_value.as_ref()
}
pub fn children_merkle_values(&self) -> impl ExactSizeIterator<Item = Option<&[u8]>> {
self.children
.iter()
.map(|child| child.as_ref().map(|merkle_value| merkle_value.as_ref()))
}
pub fn partial_key(&self) -> &[Nibble] {
&self.inserted_elem_partial_key
}
pub fn resume(mut self) -> InProgress {
if let Some(parent_node) = self.inner.stack.last_mut() {
parent_node.children.push(Some(self.merkle_value));
self.inner.next()
} else {
InProgress::Finished {
trie_root_hash: self.merkle_value.try_into().unwrap_or_else(|_| panic!()),
}
}
}
}
pub struct TrieNodeRemoveEvent {
inner: Box<Inner>,
calculated_elem: InProgressNode,
ty: TrieNodeRemoveEventTy,
}
enum TrieNodeRemoveEventTy {
NoChildrenLeft,
ReplacedWithSingleChild,
}
impl TrieNodeRemoveEvent {
pub fn key(&self) -> impl Iterator<Item = impl AsRef<[Nibble]>> {
self.inner
.current_node_full_key()
.map(either::Left)
.chain(iter::once(either::Right(&self.calculated_elem.partial_key)))
}
pub fn key_as_vec(&self) -> Vec<Nibble> {
self.key().fold(Vec::new(), |mut a, b| {
a.extend_from_slice(b.as_ref());
a
})
}
pub fn resume(mut self) -> InProgress {
match self.ty {
TrieNodeRemoveEventTy::NoChildrenLeft => {
if let Some(parent_node) = self.inner.stack.last_mut() {
parent_node.children.push(None);
self.inner.next()
} else {
InProgress::Finished {
trie_root_hash: trie::EMPTY_BLAKE2_TRIE_MERKLE_VALUE,
}
}
}
TrieNodeRemoveEventTy::ReplacedWithSingleChild => {
let child_index = self
.calculated_elem
.children
.iter()
.position(|c| c.is_some())
.unwrap_or_else(|| panic!());
let mut partial_key = self.calculated_elem.partial_key;
partial_key.push(
Nibble::try_from(u8::try_from(child_index).unwrap_or_else(|_| panic!()))
.unwrap_or_else(|_| panic!()),
);
let (_, diff_inserts_lcd) = diff_inserts_least_common_denominator(
&self.inner.diff,
self.inner
.current_node_full_key()
.map(either::Left)
.chain(iter::once(either::Right(&partial_key))),
);
InProgress::ClosestDescendant(ClosestDescendant {
inner: self.inner,
key_extra_nibbles: partial_key,
diff_inserts_lcd,
})
}
}
}
}
struct Inner {
stack: Vec<InProgressNode>,
diff: TrieDiff,
diff_trie_entries_version: TrieEntryVersion,
}
#[derive(Debug)]
struct InProgressNode {
partial_key: Vec<Nibble>,
children_partial_key_changed: bool,
children: arrayvec::ArrayVec<Option<trie::trie_node::MerkleValueOutput>, 16>,
}
impl Inner {
fn next(self: Box<Self>) -> InProgress {
if self.stack.last().map_or(false, |n| n.children.len() == 16) {
return InProgress::StorageValue(StorageValue(self));
}
let (force_recalculate, diff_inserts_lcd) =
diff_inserts_least_common_denominator(&self.diff, self.current_node_full_key());
if !force_recalculate
&& diff_inserts_lcd.is_none()
&& self
.stack
.last()
.map_or(true, |n| !n.children_partial_key_changed)
{
return InProgress::ClosestDescendantMerkleValue(ClosestDescendantMerkleValue {
inner: self,
});
}
InProgress::ClosestDescendant(ClosestDescendant {
inner: self,
key_extra_nibbles: Vec::new(),
diff_inserts_lcd,
})
}
fn current_node_full_key(&self) -> impl Iterator<Item = impl AsRef<[Nibble]>> {
self.stack.iter().flat_map(move |node| {
let maybe_child_nibble_u8 =
u8::try_from(node.children.len()).unwrap_or_else(|_| unreachable!());
let child_nibble = Nibble::try_from(maybe_child_nibble_u8).ok();
iter::once(either::Right(&node.partial_key))
.chain(child_nibble.map(|n| either::Left([n])))
})
}
}
fn diff_inserts_least_common_denominator(
diff: &TrieDiff,
prefix: impl Iterator<Item = impl AsRef<[Nibble]>>,
) -> (bool, Option<Vec<Nibble>>) {
let mut force_recalculate = false;
let mut diff_inserts_lcd = None::<Vec<Nibble>>;
let prefix_nibbles = prefix.fold(Vec::new(), |mut a, b| {
a.extend_from_slice(b.as_ref());
a
});
let prefix =
trie::nibbles_to_bytes_suffix_extend(prefix_nibbles.iter().copied()).collect::<Vec<_>>();
for (key, inserts_entry) in
diff.diff_range_ordered::<[u8]>((ops::Bound::Included(&prefix[..]), ops::Bound::Unbounded))
{
let key = trie::bytes_to_nibbles(key.iter().copied()).collect::<Vec<_>>();
if !key.starts_with(&prefix_nibbles) {
break;
}
force_recalculate = true;
if inserts_entry {
diff_inserts_lcd = match diff_inserts_lcd.take() {
Some(mut v) => {
let lcd = key.iter().zip(v.iter()).take_while(|(a, b)| a == b).count();
debug_assert!(lcd >= prefix_nibbles.len());
debug_assert_eq!(&key[..lcd], &v[..lcd]);
v.truncate(lcd);
Some(v)
}
None => Some(key),
};
}
}
(force_recalculate, diff_inserts_lcd)
}