use hash_db::HashDB;
use crate::{
CError, ChildReference, DBValue, NibbleVec, NodeCodec, Result,
TrieHash, TrieError, TrieDB, TrieDBNodeIterator, TrieLayout,
nibble_ops::NIBBLE_LENGTH, node::{Node, NodeHandle, NodeHandlePlan, NodePlan, OwnedNode},
};
use crate::rstd::{
boxed::Box, convert::TryInto, marker::PhantomData, rc::Rc, result, vec, vec::Vec,
};
struct EncoderStackEntry<C: NodeCodec> {
prefix: NibbleVec,
node: Rc<OwnedNode<DBValue>>,
child_index: usize,
omit_children: Vec<bool>,
output_index: usize,
_marker: PhantomData<C>,
}
impl<C: NodeCodec> EncoderStackEntry<C> {
fn advance_child_index(&mut self, child_prefix: &NibbleVec)
-> result::Result<(), &'static str>
{
match self.node.node_plan() {
NodePlan::Empty | NodePlan::Leaf { .. } =>
return Err("empty and leaf nodes have no children"),
NodePlan::Extension { .. } => {
if self.child_index != 0 {
return Err("extension node cannot have multiple children")
}
}
NodePlan::Branch { .. } => {
if child_prefix.len() <= self.prefix.len() {
return Err("child_prefix does not contain prefix");
}
let child_index = child_prefix.at(self.prefix.len()) as usize;
if child_index < self.child_index {
return Err("iterator returned children in non-ascending order by prefix");
}
self.child_index = child_index;
}
NodePlan::NibbledBranch { partial, .. } => {
if child_prefix.len() <= self.prefix.len() + partial.len() {
return Err("child_prefix does not contain prefix and node partial");
}
let child_index = child_prefix.at(self.prefix.len() + partial.len()) as usize;
if child_index < self.child_index {
return Err("iterator returned children in non-ascending order by prefix");
}
self.child_index = child_index;
}
}
Ok(())
}
fn encode_node(&self) -> Result<Vec<u8>, C::HashOut, C::Error> {
let node_data = self.node.data();
Ok(match self.node.node_plan() {
NodePlan::Empty | NodePlan::Leaf { .. } => node_data.to_vec(),
NodePlan::Extension { partial, child: _ } => {
if !self.omit_children[0] {
node_data.to_vec()
} else {
let partial = partial.build(node_data);
let empty_child = ChildReference::Inline(C::HashOut::default(), 0);
C::extension_node(partial.right_iter(), partial.len(), empty_child)
}
}
NodePlan::Branch { value, children } => {
C::branch_node(
Self::branch_children(node_data, &children, &self.omit_children)?.iter(),
value.clone().map(|range| &node_data[range])
)
}
NodePlan::NibbledBranch { partial, value, children } => {
let partial = partial.build(node_data);
C::branch_node_nibbled(
partial.right_iter(),
partial.len(),
Self::branch_children(node_data, &children, &self.omit_children)?.iter(),
value.clone().map(|range| &node_data[range])
)
}
})
}
fn branch_children(
node_data: &[u8],
child_handles: &[Option<NodeHandlePlan>; NIBBLE_LENGTH],
omit_children: &[bool],
) -> Result<[Option<ChildReference<C::HashOut>>; NIBBLE_LENGTH], C::HashOut, C::Error>
{
let empty_child = ChildReference::Inline(C::HashOut::default(), 0);
let mut children = [None; NIBBLE_LENGTH];
for i in 0..NIBBLE_LENGTH {
children[i] = if omit_children[i] {
Some(empty_child)
} else if let Some(child_plan) = &child_handles[i] {
let child_ref = child_plan
.build(node_data)
.try_into()
.map_err(|hash| Box::new(
TrieError::InvalidHash(C::HashOut::default(), hash)
))?;
Some(child_ref)
} else {
None
};
}
Ok(children)
}
}
pub fn encode_compact<L>(db: &TrieDB<L>) -> Result<Vec<Vec<u8>>, TrieHash<L>, CError<L>>
where
L: TrieLayout
{
let mut output = Vec::new();
let mut stack: Vec<EncoderStackEntry<L::Codec>> = Vec::new();
let iter = TrieDBNodeIterator::new(db)?;
for item in iter {
match item {
Ok((prefix, node_hash, node)) => {
if node_hash.is_none() {
continue;
}
while let Some(mut last_entry) = stack.pop() {
if prefix.starts_with(&last_entry.prefix) {
last_entry.advance_child_index(&prefix)
.expect(
"all errors from advance_child_index indicate bugs with \
TrieDBNodeIterator or this function"
);
last_entry.omit_children[last_entry.child_index] = true;
last_entry.child_index += 1;
stack.push(last_entry);
break;
} else {
output[last_entry.output_index] = last_entry.encode_node()?;
}
}
let children_len = match node.node_plan() {
NodePlan::Empty | NodePlan::Leaf { .. } => 0,
NodePlan::Extension { .. } => 1,
NodePlan::Branch { .. } | NodePlan::NibbledBranch { .. } => NIBBLE_LENGTH,
};
stack.push(EncoderStackEntry {
prefix,
node,
child_index: 0,
omit_children: vec![false; children_len],
output_index: output.len(),
_marker: PhantomData::default(),
});
output.push(Vec::new());
}
Err(err) => match *err {
TrieError::IncompleteDatabase(_) => {},
_ => return Err(err),
}
}
}
while let Some(entry) = stack.pop() {
output[entry.output_index] = entry.encode_node()?;
}
Ok(output)
}
struct DecoderStackEntry<'a, C: NodeCodec> {
node: Node<'a>,
child_index: usize,
children: Vec<Option<ChildReference<C::HashOut>>>,
_marker: PhantomData<C>,
}
impl<'a, C: NodeCodec> DecoderStackEntry<'a, C> {
fn advance_child_index(&mut self) -> Result<bool, C::HashOut, C::Error> {
match self.node {
Node::Extension(_, child) if self.child_index == 0 => {
match child {
NodeHandle::Inline(data) if data.is_empty() =>
return Ok(false),
_ => {
let child_ref = child.try_into()
.map_err(|hash| Box::new(
TrieError::InvalidHash(C::HashOut::default(), hash)
))?;
self.children[self.child_index] = Some(child_ref);
}
}
self.child_index += 1;
}
Node::Branch(children, _) | Node::NibbledBranch(_, children, _) => {
while self.child_index < NIBBLE_LENGTH {
match children[self.child_index] {
Some(NodeHandle::Inline(data)) if data.is_empty() =>
return Ok(false),
Some(child) => {
let child_ref = child.try_into()
.map_err(|hash| Box::new(
TrieError::InvalidHash(C::HashOut::default(), hash)
))?;
self.children[self.child_index] = Some(child_ref);
}
None => {}
}
self.child_index += 1;
}
}
_ => {}
}
Ok(true)
}
fn push_to_prefix(&self, prefix: &mut NibbleVec) {
match self.node {
Node::Empty => {}
Node::Leaf(partial, _) | Node::Extension(partial, _) => {
prefix.append_partial(partial.right());
}
Node::Branch(_, _) => {
prefix.push(self.child_index as u8);
}
Node::NibbledBranch(partial, _, _) => {
prefix.append_partial(partial.right());
prefix.push(self.child_index as u8);
}
}
}
fn pop_from_prefix(&self, prefix: &mut NibbleVec) {
match self.node {
Node::Empty => {}
Node::Leaf(partial, _) | Node::Extension(partial, _) => {
prefix.drop_lasts(partial.len());
}
Node::Branch(_, _) => {
prefix.pop();
}
Node::NibbledBranch(partial, _, _) => {
prefix.pop();
prefix.drop_lasts(partial.len());
}
}
}
fn encode_node(self) -> Vec<u8> {
match self.node {
Node::Empty =>
C::empty_node().to_vec(),
Node::Leaf(partial, value) =>
C::leaf_node(partial.right(), value),
Node::Extension(partial, _) =>
C::extension_node(
partial.right_iter(),
partial.len(),
self.children[0]
.expect("required by method precondition; qed"),
),
Node::Branch(_, value) =>
C::branch_node(self.children.into_iter(), value),
Node::NibbledBranch(partial, _, value) =>
C::branch_node_nibbled(
partial.right_iter(),
partial.len(),
self.children.iter(),
value,
),
}
}
}
pub fn decode_compact<L, DB, T>(db: &mut DB, encoded: &[Vec<u8>])
-> Result<(TrieHash<L>, usize), TrieHash<L>, CError<L>>
where
L: TrieLayout,
DB: HashDB<L::Hash, T>,
{
let mut stack: Vec<DecoderStackEntry<L::Codec>> = Vec::new();
let mut prefix = NibbleVec::new();
for (i, encoded_node) in encoded.iter().enumerate() {
let node = L::Codec::decode(encoded_node)
.map_err(|err| Box::new(TrieError::DecoderError(<TrieHash<L>>::default(), err)))?;
let children_len = match node {
Node::Empty | Node::Leaf(..) => 0,
Node::Extension(..) => 1,
Node::Branch(..) | Node::NibbledBranch(..) => NIBBLE_LENGTH,
};
let mut last_entry = DecoderStackEntry {
node,
child_index: 0,
children: vec![None; children_len],
_marker: PhantomData::default(),
};
loop {
if !last_entry.advance_child_index()? {
last_entry.push_to_prefix(&mut prefix);
stack.push(last_entry);
break;
}
let node_data = last_entry.encode_node();
let node_hash = db.insert(prefix.as_prefix(), node_data.as_ref());
if let Some(entry) = stack.pop() {
last_entry = entry;
last_entry.pop_from_prefix(&mut prefix);
last_entry.children[last_entry.child_index] =
Some(ChildReference::Hash(node_hash));
last_entry.child_index += 1;
} else {
return Ok((node_hash, i + 1));
}
}
}
Err(Box::new(TrieError::IncompleteDatabase(<TrieHash<L>>::default())))
}
#[cfg(test)]
mod tests {
use crate::DBValue;
use hash_db::{HashDB, Hasher, EMPTY_PREFIX};
use reference_trie::{
ExtensionLayout, NoExtensionLayout,
Trie, TrieMut, TrieDB, TrieError, TrieDBMut, TrieLayout, Recorder,
encode_compact, decode_compact,
};
type MemoryDB<H> = memory_db::MemoryDB<H, memory_db::HashKey<H>, DBValue>;
fn test_encode_compact<L: TrieLayout>(
entries: Vec<(&'static [u8], &'static [u8])>,
keys: Vec<&'static [u8]>,
) -> (<L::Hash as Hasher>::Out, Vec<Vec<u8>>, Vec<(&'static [u8], Option<DBValue>)>)
{
let (db, root) = {
let mut db = <MemoryDB<L::Hash>>::default();
let mut root = Default::default();
{
let mut trie = <TrieDBMut<L>>::new(&mut db, &mut root);
for (key, value) in entries.iter() {
trie.insert(key, value).unwrap();
}
}
(db, root)
};
let mut recorder = Recorder::new();
let items = {
let mut items = Vec::with_capacity(keys.len());
let trie = <TrieDB<L>>::new(&db, &root).unwrap();
for key in keys {
let value = trie.get_with(key, &mut recorder).unwrap();
items.push((key, value));
}
items
};
let mut partial_db = MemoryDB::default();
for record in recorder.drain() {
partial_db.insert(EMPTY_PREFIX, &record.data);
}
let compact_trie = {
let trie = <TrieDB<L>>::new(&partial_db, &root).unwrap();
encode_compact::<L>(&trie).unwrap()
};
(root, compact_trie, items)
}
fn test_decode_compact<L: TrieLayout>(
encoded: &[Vec<u8>],
items: Vec<(&'static [u8], Option<DBValue>)>,
expected_root: <L::Hash as Hasher>::Out,
expected_used: usize,
) {
let mut db = MemoryDB::default();
let (root, used) = decode_compact::<L, _, _>(&mut db, encoded).unwrap();
assert_eq!(root, expected_root);
assert_eq!(used, expected_used);
let trie = <TrieDB<L>>::new(&db, &root).unwrap();
for (key, expected_value) in items {
assert_eq!(trie.get(key).unwrap(), expected_value);
}
}
#[test]
fn trie_compact_encoding_works_with_ext() {
let (root, mut encoded, items) = test_encode_compact::<ExtensionLayout>(
vec![
(b"alfa", &[0; 32]),
(b"bravo", b"bravo"),
(b"do", b"verb"),
(b"dog", b"puppy"),
(b"doge", &[0; 32]),
(b"horse", b"stallion"),
(b"house", b"building"),
],
vec![
b"do",
b"dog",
b"doge",
b"bravo",
b"d",
b"do\x10",
b"halp",
],
);
encoded.push(Vec::new());
test_decode_compact::<ExtensionLayout>(&encoded, items, root, encoded.len() - 1);
}
#[test]
fn trie_compact_encoding_works_without_ext() {
let (root, mut encoded, items) = test_encode_compact::<NoExtensionLayout>(
vec![
(b"alfa", &[0; 32]),
(b"bravo", b"bravo"),
(b"do", b"verb"),
(b"dog", b"puppy"),
(b"doge", &[0; 32]),
(b"horse", b"stallion"),
(b"house", b"building"),
],
vec![
b"do",
b"dog",
b"doge",
b"bravo",
b"d",
b"do\x10",
b"halp",
],
);
encoded.push(Vec::new());
test_decode_compact::<NoExtensionLayout>(&encoded, items, root, encoded.len() - 1);
}
#[test]
fn trie_decoding_fails_with_incomplete_database() {
let (_, encoded, _) = test_encode_compact::<ExtensionLayout>(
vec![
(b"alfa", &[0; 32]),
(b"bravo", b"bravo"),
],
vec![
b"alfa",
],
);
assert!(encoded.len() > 1);
let mut db = MemoryDB::default();
match decode_compact::<ExtensionLayout, _, _>(&mut db, &encoded[..encoded.len() - 1]) {
Err(err) => match *err {
TrieError::IncompleteDatabase(_) => {}
_ => panic!("got unexpected TrieError"),
}
_ => panic!("decode was unexpectedly successful"),
}
}
}