Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add NodeHandle enum for node references within Node #35

Merged
merged 3 commits into from
Nov 19, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 38 additions & 35 deletions test-support/reference-trie/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use std::ops::Range;
use parity_scale_codec::{Decode, Input, Output, Encode, Compact, Error as CodecError};
use trie_root::Hasher;
use trie_db::{
node::{NibbleSlicePlan, NodePlan},
node::{NibbleSlicePlan, NodePlan, NodeHandlePlan},
triedbmut::ChildReference,
DBValue,
trie_visit,
Expand All @@ -47,8 +47,8 @@ pub struct ExtensionLayout;

impl TrieLayout for ExtensionLayout {
const USE_EXTENSION: bool = true;
type Hash = keccak_hasher::KeccakHasher;
type Codec = ReferenceNodeCodec;
type Hash = KeccakHasher;
type Codec = ReferenceNodeCodec<KeccakHasher>;
}

impl TrieConfiguration for ExtensionLayout { }
Expand All @@ -60,7 +60,7 @@ pub struct GenericNoExtensionLayout<H>(PhantomData<H>);
impl<H: Hasher> TrieLayout for GenericNoExtensionLayout<H> {
const USE_EXTENSION: bool = false;
type Hash = H;
type Codec = ReferenceNodeCodecNoExt;
type Codec = ReferenceNodeCodecNoExt<H>;
}

impl<H: Hasher> TrieConfiguration for GenericNoExtensionLayout<H> { }
Expand Down Expand Up @@ -481,14 +481,14 @@ impl Decode for NodeHeaderNoExt {

/// Simple reference implementation of a `NodeCodec`.
#[derive(Default, Clone)]
pub struct ReferenceNodeCodec;
pub struct ReferenceNodeCodec<H>(PhantomData<H>);

/// Simple reference implementation of a `NodeCodec`.
/// Even if implementation follows initial specification of
/// https://github.com/w3f/polkadot-re-spec/issues/8, this may
/// not follow it in the future, it is mainly the testing codec without extension node.
#[derive(Default, Clone)]
pub struct ReferenceNodeCodecNoExt;
pub struct ReferenceNodeCodecNoExt<H>(PhantomData<H>);

fn partial_to_key(partial: Partial, offset: u8, over: u8) -> Vec<u8> {
let number_nibble_encoded = (partial.0).0 as usize;
Expand Down Expand Up @@ -613,11 +613,12 @@ impl<'a> Input for ByteSliceInput<'a> {
// but due to the current limitations of Rust const evaluation we can't do
// `const HASHED_NULL_NODE: <KeccakHasher as Hasher>::Out = <KeccakHasher as Hasher>::Out( … … )`.
// Perhaps one day soon?
impl<H: Hasher> NodeCodec<H> for ReferenceNodeCodec {
impl<H: Hasher> NodeCodec for ReferenceNodeCodec<H> {
type Error = CodecError;
type HashOut = H::Out;

fn hashed_null_node() -> <H as Hasher>::Out {
H::hash(<Self as NodeCodec<H>>::empty_node())
H::hash(<Self as NodeCodec>::empty_node())
}

fn decode_plan(data: &[u8]) -> ::std::result::Result<NodePlan, Self::Error> {
Expand All @@ -641,7 +642,12 @@ impl<H: Hasher> NodeCodec<H> for ReferenceNodeCodec {
for i in 0..nibble_ops::NIBBLE_LENGTH {
if bitmap.value_at(i) {
let count = <Compact<u32>>::decode(&mut input)?.0 as usize;
children[i] = Some(input.take(count)?);
let range = input.take(count)?;
children[i] = Some(if count == H::LENGTH {
NodeHandlePlan::Hash(range)
} else {
NodeHandlePlan::Inline(range)
});
}
}
Ok(NodePlan::Branch { value, children })
Expand All @@ -652,7 +658,12 @@ impl<H: Hasher> NodeCodec<H> for ReferenceNodeCodec {
)?;
let partial_padding = nibble_ops::number_padding(nibble_count);
let count = <Compact<u32>>::decode(&mut input)?.0 as usize;
let child = input.take(count)?;
let range = input.take(count)?;
let child = if count == H::LENGTH {
NodeHandlePlan::Hash(range)
} else {
NodeHandlePlan::Inline(range)
};
Ok(NodePlan::Extension {
partial: NibbleSlicePlan::new(partial, partial_padding),
child
Expand All @@ -673,18 +684,8 @@ impl<H: Hasher> NodeCodec<H> for ReferenceNodeCodec {
}
}

fn try_decode_hash(data: &[u8]) -> Option<<H as Hasher>::Out> {
if data.len() == H::LENGTH {
let mut r = <H as Hasher>::Out::default();
r.as_mut().copy_from_slice(data);
Some(r)
} else {
None
}
}

fn is_empty_node(data: &[u8]) -> bool {
data == <Self as NodeCodec<H>>::empty_node()
data == <Self as NodeCodec>::empty_node()
}

fn empty_node() -> &'static[u8] {
Expand All @@ -700,7 +701,7 @@ impl<H: Hasher> NodeCodec<H> for ReferenceNodeCodec {
fn extension_node(
partial: impl Iterator<Item = u8>,
number_nibble: usize,
child: ChildReference<<H as Hasher>::Out>,
child: ChildReference<Self::HashOut>,
) -> Vec<u8> {
let mut output = partial_from_iterator_to_key(
partial,
Expand All @@ -717,7 +718,7 @@ impl<H: Hasher> NodeCodec<H> for ReferenceNodeCodec {
}

fn branch_node(
children: impl Iterator<Item = impl Borrow<Option<ChildReference<<H as Hasher>::Out>>>>,
children: impl Iterator<Item = impl Borrow<Option<ChildReference<Self::HashOut>>>>,
maybe_value: Option<&[u8]>,
) -> Vec<u8> {
let mut output = vec![0; BITMAP_LENGTH + 1];
Expand Down Expand Up @@ -747,18 +748,19 @@ impl<H: Hasher> NodeCodec<H> for ReferenceNodeCodec {
fn branch_node_nibbled(
_partial: impl Iterator<Item = u8>,
_number_nibble: usize,
_children: impl Iterator<Item = impl Borrow<Option<ChildReference<<H as Hasher>::Out>>>>,
_children: impl Iterator<Item = impl Borrow<Option<ChildReference<Self::HashOut>>>>,
_maybe_value: Option<&[u8]>) -> Vec<u8> {
unreachable!()
}

}

impl<H: Hasher> NodeCodec<H> for ReferenceNodeCodecNoExt {
impl<H: Hasher> NodeCodec for ReferenceNodeCodecNoExt<H> {
type Error = CodecError;
type HashOut = <H as Hasher>::Out;

fn hashed_null_node() -> <H as Hasher>::Out {
H::hash(<Self as NodeCodec<H>>::empty_node())
H::hash(<Self as NodeCodec>::empty_node())
}

fn decode_plan(data: &[u8]) -> ::std::result::Result<NodePlan, Self::Error> {
Expand Down Expand Up @@ -790,7 +792,12 @@ impl<H: Hasher> NodeCodec<H> for ReferenceNodeCodecNoExt {
for i in 0..nibble_ops::NIBBLE_LENGTH {
if bitmap.value_at(i) {
let count = <Compact<u32>>::decode(&mut input)?.0 as usize;
children[i] = Some(input.take(count)?);
let range = input.take(count)?;
children[i] = Some(if count == H::LENGTH {
NodeHandlePlan::Hash(range)
} else {
NodeHandlePlan::Inline(range)
});
}
}
Ok(NodePlan::NibbledBranch {
Expand Down Expand Up @@ -819,12 +826,8 @@ impl<H: Hasher> NodeCodec<H> for ReferenceNodeCodecNoExt {
}
}

fn try_decode_hash(data: &[u8]) -> Option<<H as Hasher>::Out> {
<ReferenceNodeCodec as NodeCodec<H>>::try_decode_hash(data)
}

fn is_empty_node(data: &[u8]) -> bool {
data == <Self as NodeCodec<H>>::empty_node()
data == <Self as NodeCodec>::empty_node()
}

fn empty_node() -> &'static [u8] {
Expand Down Expand Up @@ -855,7 +858,7 @@ impl<H: Hasher> NodeCodec<H> for ReferenceNodeCodecNoExt {
fn branch_node_nibbled(
partial: impl Iterator<Item = u8>,
number_nibble: usize,
children: impl Iterator<Item = impl Borrow<Option<ChildReference<<H as Hasher>::Out>>>>,
children: impl Iterator<Item = impl Borrow<Option<ChildReference<Self::HashOut>>>>,
maybe_value: Option<&[u8]>,
) -> Vec<u8> {
let mut output = if maybe_value.is_some() {
Expand Down Expand Up @@ -1211,9 +1214,9 @@ mod tests {
fn too_big_nibble_length() {
// + 1 for 0 added byte of nibble encode
let input = vec![0u8; (NIBBLE_SIZE_BOUND_NO_EXT as usize + 1) / 2 + 1];
let enc = <ReferenceNodeCodecNoExt as NodeCodec<KeccakHasher>>
let enc = <ReferenceNodeCodecNoExt<KeccakHasher> as NodeCodec>
::leaf_node(((0, 0), &input), &[1]);
let dec = <ReferenceNodeCodecNoExt as NodeCodec<KeccakHasher>>
let dec = <ReferenceNodeCodecNoExt<KeccakHasher> as NodeCodec>
::decode(&enc).unwrap();
let o_sl = if let Node::Leaf(sl, _) = dec {
Some(sl)
Expand Down
39 changes: 30 additions & 9 deletions trie-db/src/iterator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
use super::{CError, DBValue, Result, Trie, TrieHash, TrieIterator, TrieLayout};
use hash_db::{Hasher, EMPTY_PREFIX};
use triedb::TrieDB;
use node::{NodePlan, OwnedNode};
use node::{NodePlan, NodeHandle, OwnedNode};
use nibble::{NibbleSlice, NibbleVec, nibble_ops};

#[cfg(feature = "std")]
Expand Down Expand Up @@ -76,7 +76,11 @@ impl<'a, L: TrieLayout> TrieDBNodeIterator<'a, L> {
trail: Vec::with_capacity(8),
key_nibbles: NibbleVec::new(),
};
let (root_node, root_hash) = db.get_raw_or_lookup(db.root().as_ref(), EMPTY_PREFIX)?;
let (root_node, root_hash) = db.get_raw_or_lookup(
*db.root(),
NodeHandle::Hash(db.root().as_ref()),
EMPTY_PREFIX
)?;
r.descend(root_node, root_hash);
Ok(r)
}
Expand All @@ -97,8 +101,11 @@ impl<'a, L: TrieLayout> TrieIterator<L> for TrieDBNodeIterator<'a, L> {
self.key_nibbles.clear();
let key = NibbleSlice::new(key);

let (mut node, mut node_hash) =
self.db.get_raw_or_lookup(self.db.root().as_ref(), EMPTY_PREFIX)?;
let (mut node, mut node_hash) = self.db.get_raw_or_lookup(
<TrieHash<L>>::default(),
NodeHandle::Hash(self.db.root().as_ref()),
EMPTY_PREFIX
)?;
let mut partial = key;
let mut full_key_nibbles = 0;
loop {
Expand Down Expand Up @@ -135,7 +142,11 @@ impl<'a, L: TrieLayout> TrieIterator<L> for TrieDBNodeIterator<'a, L> {
self.key_nibbles.append_partial(slice.right());

let prefix = key.back(full_key_nibbles);
self.db.get_raw_or_lookup(&node_data[child.clone()], prefix.left())?
self.db.get_raw_or_lookup(
node_hash.unwrap_or_default(),
child.build(node_data),
prefix.left()
)?
},
NodePlan::Branch { value: _, children } => {
if partial.is_empty() {
Expand All @@ -151,7 +162,11 @@ impl<'a, L: TrieLayout> TrieIterator<L> for TrieDBNodeIterator<'a, L> {
partial = partial.mid(1);

let prefix = key.back(full_key_nibbles);
self.db.get_raw_or_lookup(&node_data[child.clone()], prefix.left())?
self.db.get_raw_or_lookup(
node_hash.unwrap_or_default(),
child.build(node_data),
prefix.left()
)?
} else {
return Ok(())
}
Expand Down Expand Up @@ -184,7 +199,11 @@ impl<'a, L: TrieLayout> TrieIterator<L> for TrieDBNodeIterator<'a, L> {
partial = partial.mid(1);

let prefix = key.back(full_key_nibbles);
self.db.get_raw_or_lookup(&node_data[child.clone()], prefix.left())?
self.db.get_raw_or_lookup(
node_hash.unwrap_or_default(),
child.build(node_data),
prefix.left()
)?
} else {
return Ok(())
}
Expand Down Expand Up @@ -239,7 +258,8 @@ impl<'a, L: TrieLayout> Iterator for TrieDBNodeIterator<'a, L> {
self.key_nibbles.append_partial(partial.right());
IterStep::Descend::<TrieHash<L>, CError<L>>(
self.db.get_raw_or_lookup(
&node_data[child.clone()],
b.hash.unwrap_or_default(),
child.build(node_data),
self.key_nibbles.as_prefix()
)
)
Expand All @@ -261,7 +281,8 @@ impl<'a, L: TrieLayout> Iterator for TrieDBNodeIterator<'a, L> {
self.key_nibbles.push(i as u8);
IterStep::Descend::<TrieHash<L>, CError<L>>(
self.db.get_raw_or_lookup(
&node_data[child.clone()],
b.hash.unwrap_or_default(),
child.build(node_data),
self.key_nibbles.as_prefix()
)
)
Expand Down
14 changes: 10 additions & 4 deletions trie-db/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ pub enum TrieError<T, E> {
ValueAtIncompleteKey(Vec<u8>, u8),
/// Corrupt Trie item
DecoderError(T, E),
InvalidHash(T, Vec<u8>),
}

#[cfg(feature = "std")]
Expand All @@ -138,6 +139,12 @@ impl<T, E> fmt::Display for TrieError<T, E> where T: MaybeDebug, E: MaybeDebug {
TrieError::DecoderError(ref hash, ref decoder_err) => {
write!(f, "Decoding failed for hash {:?}; err: {:?}", hash, decoder_err)
}
TrieError::InvalidHash(ref hash, ref data) =>
write!(
f,
"Encoded node {:?} contains invalid hash reference with length: {}",
hash, data.len()
),
}
}
}
Expand All @@ -150,6 +157,7 @@ impl<T, E> Error for TrieError<T, E> where T: fmt::Debug, E: Error {
TrieError::IncompleteDatabase(_) => "Incomplete database",
TrieError::ValueAtIncompleteKey(_, _) => "Value at incomplete key",
TrieError::DecoderError(_, ref err) => err.description(),
TrieError::InvalidHash(_, _) => "Encoded node contains invalid hash reference",
}
}
}
Expand Down Expand Up @@ -415,7 +423,7 @@ pub trait TrieLayout {
/// Hasher to use for this trie.
type Hash: Hasher;
/// Codec to use (needs to match hasher and nibble ops).
type Codec: NodeCodec<Self::Hash>;
type Codec: NodeCodec<HashOut=<Self::Hash as Hasher>::Out>;
}

/// This traits associates a trie definition with prefered methods.
Expand Down Expand Up @@ -477,6 +485,4 @@ pub trait TrieConfiguration: Sized + TrieLayout {
/// Alias accessor to hasher hash output type from a `TrieLayout`.
pub type TrieHash<L> = <<L as TrieLayout>::Hash as Hasher>::Out;
/// Alias accessor to `NodeCodec` associated `Error` type from a `TrieLayout`.
pub type CError<L> = <
<L as TrieLayout>::Codec as NodeCodec<<L as TrieLayout>::Hash>
>::Error;
pub type CError<L> = <<L as TrieLayout>::Codec as NodeCodec>::Error;
Loading