mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 10:01:17 +00:00
Switch to shiny new fast, RLP-less trie (#795)
* Bump codec * Fix tests * Patricia trie builds * Introduce trie * Some yak shaving. * Some fixes * Remove RLP ref * Fixes * It builds! * Some tests fixed * Another test fix * Rejig more hashes * substrate-trie::iterator_works test * Update lock * Polish * Docs * Undo incorrect "fix" for tests * Fix nits
This commit is contained in:
committed by
Arkadiy Paronyan
parent
b02c274374
commit
82d6ca3484
@@ -0,0 +1,29 @@
|
||||
// Copyright 2015-2017 Parity Technologies
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use std::fmt;
|
||||
use std::error::Error as StdError;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
/// Error concerning the Parity-Codec based decoder.
|
||||
pub enum Error {
|
||||
/// Bad format.
|
||||
BadFormat,
|
||||
}
|
||||
|
||||
impl StdError for Error {
|
||||
fn description(&self) -> &str {
|
||||
"codec error"
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt::Debug::fmt(&self, f)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,421 @@
|
||||
// Copyright 2015-2018 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Substrate-format Base-16 Modified Merkle Patricia Tree (Trie).
|
||||
|
||||
// TODO: no_std
|
||||
|
||||
extern crate trie_root;
|
||||
extern crate parity_codec as codec;
|
||||
extern crate trie_db;
|
||||
extern crate hash_db;
|
||||
extern crate memory_db;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate substrate_primitives;
|
||||
#[cfg(test)]
|
||||
extern crate trie_standardmap;
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
extern crate hex_literal;
|
||||
|
||||
mod error;
|
||||
mod node_header;
|
||||
mod node_codec;
|
||||
mod trie_stream;
|
||||
|
||||
use hash_db::Hasher;
|
||||
/// Our `NodeCodec`-specific error.
|
||||
pub use error::Error;
|
||||
/// The Substrate format implementation of `TrieStream`.
|
||||
pub use trie_stream::TrieStream;
|
||||
/// The Substrate format implementation of `NodeCodec`.
|
||||
pub use node_codec::NodeCodec;
|
||||
/// Various re-exports from the `trie-db` crate.
|
||||
pub use trie_db::{Trie, TrieMut, DBValue, Recorder};
|
||||
|
||||
/// As in `trie_db`, but less generic, error type for the crate.
|
||||
pub type TrieError<H> = trie_db::TrieError<H, Error>;
|
||||
/// As in `hash_db`, but less generic, trait exposed.
|
||||
pub trait AsHashDB<H: Hasher>: hash_db::AsHashDB<H, trie_db::DBValue> {}
|
||||
impl<H: Hasher, T: hash_db::AsHashDB<H, trie_db::DBValue>> AsHashDB<H> for T {}
|
||||
/// As in `hash_db`, but less generic, trait exposed.
|
||||
pub type HashDB<H> = hash_db::HashDB<H, trie_db::DBValue>;
|
||||
/// As in `memory_db`, but less generic, trait exposed.
|
||||
pub type MemoryDB<H> = memory_db::MemoryDB<H, trie_db::DBValue>;
|
||||
|
||||
/// Persistent trie database read-access interface for the a given hasher.
|
||||
pub type TrieDB<'a, H> = trie_db::TrieDB<'a, H, NodeCodec<H>>;
|
||||
/// Persistent trie database write-access interface for the a given hasher.
|
||||
pub type TrieDBMut<'a, H> = trie_db::TrieDBMut<'a, H, NodeCodec<H>>;
|
||||
/// Querying interface, as in `trie_db` but less generic.
|
||||
pub type Lookup<'a, H, Q> = trie_db::Lookup<'a, H, NodeCodec<H>, Q>;
|
||||
|
||||
/// Determine a trie root given its ordered contents, closed form.
|
||||
pub fn trie_root<H: Hasher, I, A, B>(input: I) -> H::Out where
|
||||
I: IntoIterator<Item = (A, B)>,
|
||||
A: AsRef<[u8]> + Ord,
|
||||
B: AsRef<[u8]>,
|
||||
{
|
||||
trie_root::trie_root::<H, TrieStream, _, _, _>(input)
|
||||
}
|
||||
|
||||
/// Determine a trie root node's data given its ordered contents, closed form.
|
||||
pub fn unhashed_trie<H: Hasher, I, A, B>(input: I) -> Vec<u8> where
|
||||
I: IntoIterator<Item = (A, B)>,
|
||||
A: AsRef<[u8]> + Ord,
|
||||
B: AsRef<[u8]>,
|
||||
{
|
||||
trie_root::unhashed_trie::<H, TrieStream, _, _, _>(input)
|
||||
}
|
||||
|
||||
/// A trie root formed from the items, with keys attached according to their
|
||||
/// compact-encoded index (using `parity-codec` crate).
|
||||
pub fn ordered_trie_root<H: Hasher, I, A>(input: I) -> H::Out
|
||||
where
|
||||
I: IntoIterator<Item = A> + Iterator<Item = A>,
|
||||
A: AsRef<[u8]>,
|
||||
{
|
||||
trie_root::<H, _, _, _>(input
|
||||
.enumerate()
|
||||
.map(|(i, v)| (codec::Encode::encode(&codec::Compact(i as u32)), v))
|
||||
)
|
||||
}
|
||||
|
||||
// Utilities (not exported):
|
||||
|
||||
const EMPTY_TRIE: u8 = 0;
|
||||
const LEAF_NODE_OFFSET: u8 = 1;
|
||||
const LEAF_NODE_BIG: u8 = 127;
|
||||
const EXTENSION_NODE_OFFSET: u8 = 128;
|
||||
const EXTENSION_NODE_BIG: u8 = 253;
|
||||
const BRANCH_NODE_NO_VALUE: u8 = 254;
|
||||
const BRANCH_NODE_WITH_VALUE: u8 = 255;
|
||||
const LEAF_NODE_THRESHOLD: u8 = LEAF_NODE_BIG - LEAF_NODE_OFFSET;
|
||||
const EXTENSION_NODE_THRESHOLD: u8 = EXTENSION_NODE_BIG - EXTENSION_NODE_OFFSET; //125
|
||||
const LEAF_NODE_SMALL_MAX: u8 = LEAF_NODE_BIG - 1;
|
||||
const EXTENSION_NODE_SMALL_MAX: u8 = EXTENSION_NODE_BIG - 1;
|
||||
|
||||
fn take<'a>(input: &mut &'a[u8], count: usize) -> Option<&'a[u8]> {
|
||||
if input.len() < count {
|
||||
return None
|
||||
}
|
||||
let r = &(*input)[..count];
|
||||
*input = &(*input)[count..];
|
||||
Some(r)
|
||||
}
|
||||
|
||||
fn partial_to_key(partial: &[u8], offset: u8, big: u8) -> Vec<u8> {
|
||||
let nibble_count = (partial.len() - 1) * 2 + if partial[0] & 16 == 16 { 1 } else { 0 };
|
||||
let (first_byte_small, big_threshold) = (offset, (big - offset) as usize);
|
||||
let mut output = vec![first_byte_small + nibble_count.min(big_threshold) as u8];
|
||||
if nibble_count >= big_threshold { output.push((nibble_count - big_threshold) as u8) }
|
||||
if nibble_count % 2 == 1 {
|
||||
output.push(partial[0] & 0x0f);
|
||||
}
|
||||
output.extend_from_slice(&partial[1..]);
|
||||
output
|
||||
}
|
||||
|
||||
fn branch_node(has_value: bool, has_children: impl Iterator<Item = bool>) -> [u8; 3] {
|
||||
let first = if has_value {
|
||||
BRANCH_NODE_WITH_VALUE
|
||||
} else {
|
||||
BRANCH_NODE_NO_VALUE
|
||||
};
|
||||
let mut bitmap: u16 = 0;
|
||||
let mut cursor: u16 = 1;
|
||||
for v in has_children {
|
||||
if v { bitmap |= cursor }
|
||||
cursor <<= 1;
|
||||
}
|
||||
[first, (bitmap % 256 ) as u8, (bitmap / 256 ) as u8]
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use codec::{Encode, Compact};
|
||||
use substrate_primitives::Blake2Hasher;
|
||||
use memory_db::MemoryDB;
|
||||
use hash_db::{HashDB, Hasher};
|
||||
use trie_db::{DBValue, TrieMut, Trie};
|
||||
use trie_standardmap::{Alphabet, ValueMode, StandardMap};
|
||||
|
||||
fn check_equivalent(input: &Vec<(&[u8], &[u8])>) {
|
||||
{
|
||||
let closed_form = trie_root::<Blake2Hasher, _, _, _>(input.clone());
|
||||
let d = unhashed_trie::<Blake2Hasher, _, _, _>(input.clone());
|
||||
println!("Data: {:#x?}, {:#x?}", d, Blake2Hasher::hash(&d[..]));
|
||||
let persistent = {
|
||||
let mut memdb = MemoryDB::default();
|
||||
let mut root = Default::default();
|
||||
let mut t = TrieDBMut::<Blake2Hasher>::new(&mut memdb, &mut root);
|
||||
for (x, y) in input.iter().rev() {
|
||||
t.insert(x, y).unwrap();
|
||||
}
|
||||
t.root().clone()
|
||||
};
|
||||
assert_eq!(closed_form, persistent);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_iteration(input: &Vec<(&[u8], &[u8])>) {
|
||||
let mut memdb = MemoryDB::default();
|
||||
let mut root = Default::default();
|
||||
{
|
||||
let mut t = TrieDBMut::<Blake2Hasher>::new(&mut memdb, &mut root);
|
||||
for (x, y) in input.clone() {
|
||||
t.insert(x, y).unwrap();
|
||||
}
|
||||
}
|
||||
{
|
||||
let t = TrieDB::<Blake2Hasher>::new(&mut memdb, &root).unwrap();
|
||||
assert_eq!(
|
||||
input.iter().map(|(i, j)| (i.to_vec(), j.to_vec())).collect::<Vec<_>>(),
|
||||
t.iter().unwrap().map(|x| x.map(|y| (y.0, y.1.to_vec())).unwrap()).collect::<Vec<_>>()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_is_equivalent() {
|
||||
let input: Vec<(&[u8], &[u8])> = vec![];
|
||||
check_equivalent(&input);
|
||||
check_iteration(&input);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn leaf_is_equivalent() {
|
||||
let input: Vec<(&[u8], &[u8])> = vec![(&[0xaa][..], &[0xbb][..])];
|
||||
check_equivalent(&input);
|
||||
check_iteration(&input);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn branch_is_equivalent() {
|
||||
let input: Vec<(&[u8], &[u8])> = vec![(&[0xaa][..], &[0x10][..]), (&[0xba][..], &[0x11][..])];
|
||||
check_equivalent(&input);
|
||||
check_iteration(&input);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extension_and_branch_is_equivalent() {
|
||||
let input: Vec<(&[u8], &[u8])> = vec![(&[0xaa][..], &[0x10][..]), (&[0xab][..], &[0x11][..])];
|
||||
check_equivalent(&input);
|
||||
check_iteration(&input);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn standard_is_equivalent() {
|
||||
let st = StandardMap {
|
||||
alphabet: Alphabet::All,
|
||||
min_key: 32,
|
||||
journal_key: 0,
|
||||
value_mode: ValueMode::Random,
|
||||
count: 1000,
|
||||
};
|
||||
let mut d = st.make();
|
||||
d.sort_unstable_by(|&(ref a, _), &(ref b, _)| a.cmp(b));
|
||||
let dr = d.iter().map(|v| (&v.0[..], &v.1[..])).collect();
|
||||
check_equivalent(&dr);
|
||||
check_iteration(&dr);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extension_and_branch_with_value_is_equivalent() {
|
||||
let input: Vec<(&[u8], &[u8])> = vec![
|
||||
(&[0xaa][..], &[0xa0][..]),
|
||||
(&[0xaa, 0xaa][..], &[0xaa][..]),
|
||||
(&[0xaa, 0xbb][..], &[0xab][..])
|
||||
];
|
||||
check_equivalent(&input);
|
||||
check_iteration(&input);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bigger_extension_and_branch_with_value_is_equivalent() {
|
||||
let input: Vec<(&[u8], &[u8])> = vec![
|
||||
(&[0xaa][..], &[0xa0][..]),
|
||||
(&[0xaa, 0xaa][..], &[0xaa][..]),
|
||||
(&[0xaa, 0xbb][..], &[0xab][..]),
|
||||
(&[0xbb][..], &[0xb0][..]),
|
||||
(&[0xbb, 0xbb][..], &[0xbb][..]),
|
||||
(&[0xbb, 0xcc][..], &[0xbc][..]),
|
||||
];
|
||||
check_equivalent(&input);
|
||||
check_iteration(&input);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn single_long_leaf_is_equivalent() {
|
||||
let input: Vec<(&[u8], &[u8])> = vec![(&[0xaa][..], &b"ABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABC"[..]), (&[0xba][..], &[0x11][..])];
|
||||
check_equivalent(&input);
|
||||
check_iteration(&input);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn two_long_leaves_is_equivalent() {
|
||||
let input: Vec<(&[u8], &[u8])> = vec![
|
||||
(&[0xaa][..], &b"ABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABC"[..]),
|
||||
(&[0xba][..], &b"ABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABC"[..])
|
||||
];
|
||||
check_equivalent(&input);
|
||||
check_iteration(&input);
|
||||
}
|
||||
|
||||
fn populate_trie<'db>(
|
||||
db: &'db mut HashDB<Blake2Hasher, DBValue>,
|
||||
root: &'db mut <Blake2Hasher as Hasher>::Out,
|
||||
v: &[(Vec<u8>, Vec<u8>)]
|
||||
) -> TrieDBMut<'db, Blake2Hasher> {
|
||||
let mut t = TrieDBMut::<Blake2Hasher>::new(db, root);
|
||||
for i in 0..v.len() {
|
||||
let key: &[u8]= &v[i].0;
|
||||
let val: &[u8] = &v[i].1;
|
||||
t.insert(key, val).unwrap();
|
||||
}
|
||||
t
|
||||
}
|
||||
|
||||
fn unpopulate_trie<'db>(t: &mut TrieDBMut<'db, Blake2Hasher>, v: &[(Vec<u8>, Vec<u8>)]) {
|
||||
for i in v {
|
||||
let key: &[u8]= &i.0;
|
||||
t.remove(key).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn random_should_work() {
|
||||
let mut seed = <Blake2Hasher as Hasher>::Out::new();
|
||||
for test_i in 0..10000 {
|
||||
if test_i % 50 == 0 {
|
||||
println!("{:?} of 10000 stress tests done", test_i);
|
||||
}
|
||||
let x = StandardMap {
|
||||
alphabet: Alphabet::Custom(b"@QWERTYUIOPASDFGHJKLZXCVBNM[/]^_".to_vec()),
|
||||
min_key: 5,
|
||||
journal_key: 0,
|
||||
value_mode: ValueMode::Index,
|
||||
count: 100,
|
||||
}.make_with(&mut seed.0);
|
||||
|
||||
let real = trie_root::<Blake2Hasher,_, _, _>(x.clone());
|
||||
let mut memdb = MemoryDB::default();
|
||||
let mut root = Default::default();
|
||||
let mut memtrie = populate_trie(&mut memdb, &mut root, &x);
|
||||
|
||||
memtrie.commit();
|
||||
if *memtrie.root() != real {
|
||||
println!("TRIE MISMATCH");
|
||||
println!("");
|
||||
println!("{:?} vs {:?}", memtrie.root(), real);
|
||||
for i in &x {
|
||||
println!("{:#x?} -> {:#x?}", i.0, i.1);
|
||||
}
|
||||
}
|
||||
assert_eq!(*memtrie.root(), real);
|
||||
unpopulate_trie(&mut memtrie, &x);
|
||||
memtrie.commit();
|
||||
if *memtrie.root() != <NodeCodec<Blake2Hasher> as trie_db::NodeCodec<Blake2Hasher>>::hashed_null_node() {
|
||||
println!("- TRIE MISMATCH");
|
||||
println!("");
|
||||
println!("{:?} vs {:?}", memtrie.root(), <NodeCodec<Blake2Hasher> as trie_db::NodeCodec<Blake2Hasher>>::hashed_null_node());
|
||||
for i in &x {
|
||||
println!("{:#x?} -> {:#x?}", i.0, i.1);
|
||||
}
|
||||
}
|
||||
assert_eq!(*memtrie.root(), <NodeCodec<Blake2Hasher> as trie_db::NodeCodec<Blake2Hasher>>::hashed_null_node());
|
||||
}
|
||||
}
|
||||
|
||||
fn to_compact(n: u8) -> u8 {
|
||||
Compact(n).encode()[0]
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn codec_trie_empty() {
|
||||
let input: Vec<(&[u8], &[u8])> = vec![];
|
||||
let trie = unhashed_trie::<Blake2Hasher, _, _, _>(input);
|
||||
println!("trie: {:#x?}", trie);
|
||||
assert_eq!(trie, vec![0x0]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn codec_trie_single_tuple() {
|
||||
let input = vec![
|
||||
(vec![0xaa], vec![0xbb])
|
||||
];
|
||||
let trie = unhashed_trie::<Blake2Hasher, _, _, _>(input);
|
||||
println!("trie: {:#x?}", trie);
|
||||
|
||||
assert_eq!(trie, vec![
|
||||
0x03, // leaf (0x01) with (+) key of 2 nibbles (0x02)
|
||||
0xaa, // key data
|
||||
to_compact(1), // length of value in bytes as Compact
|
||||
0xbb // value data
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn codec_trie_two_tuples_disjoint_keys() {
|
||||
let input = vec![(&[0x48, 0x19], &[0xfe]), (&[0x13, 0x14], &[0xff])];
|
||||
let trie = unhashed_trie::<Blake2Hasher, _, _, _>(input);
|
||||
println!("trie: {:#x?}", trie);
|
||||
|
||||
let mut ex = Vec::<u8>::new();
|
||||
ex.push(0xfe); // branch, no value
|
||||
ex.push(0x12); // slots 1 & 4 are taken from 0-7
|
||||
ex.push(0x00); // no slots from 8-15
|
||||
ex.push(to_compact(0x05)); // first slot: LEAF, 5 bytes long.
|
||||
ex.push(0x04); // leaf with 3 nibbles
|
||||
ex.push(0x03); // first nibble
|
||||
ex.push(0x14); // second & third nibble
|
||||
ex.push(to_compact(0x01)); // 1 byte data
|
||||
ex.push(0xff); // value data
|
||||
ex.push(to_compact(0x05)); // second slot: LEAF, 5 bytes long.
|
||||
ex.push(0x04); // leaf with 3 nibbles
|
||||
ex.push(0x08); // first nibble
|
||||
ex.push(0x19); // second & third nibble
|
||||
ex.push(to_compact(0x01)); // 1 byte data
|
||||
ex.push(0xfe); // value data
|
||||
|
||||
assert_eq!(trie, ex);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn iterator_works() {
|
||||
let pairs = vec![
|
||||
(hex!("0103000000000000000464").to_vec(), hex!("0400000000").to_vec()),
|
||||
(hex!("0103000000000000000469").to_vec(), hex!("0401000000").to_vec()),
|
||||
];
|
||||
|
||||
let mut mdb = MemoryDB::default();
|
||||
let mut root = Default::default();
|
||||
let _ = populate_trie(&mut mdb, &mut root, &pairs);
|
||||
|
||||
let trie = TrieDB::<Blake2Hasher>::new(&mdb, &root).unwrap();
|
||||
|
||||
let iter = trie.iter().unwrap();
|
||||
let mut iter_pairs = Vec::new();
|
||||
for pair in iter {
|
||||
let (key, value) = pair.unwrap();
|
||||
iter_pairs.push((key, value.to_vec()));
|
||||
}
|
||||
|
||||
assert_eq!(pairs, iter_pairs);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
// Copyright 2015-2018 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! `NodeCodec` implementation for Substrate's trie format.
|
||||
|
||||
use std::marker::PhantomData;
|
||||
use codec::{Encode, Decode, Compact};
|
||||
use hash_db::Hasher;
|
||||
use trie_db::{self, DBValue, NibbleSlice, node::Node, ChildReference};
|
||||
use error::Error;
|
||||
use super::{EMPTY_TRIE, LEAF_NODE_OFFSET, LEAF_NODE_BIG, EXTENSION_NODE_OFFSET,
|
||||
EXTENSION_NODE_BIG, take, partial_to_key, node_header::NodeHeader, branch_node};
|
||||
|
||||
/// Concrete implementation of a `NodeCodec` with Parity Codec encoding, generic over the `Hasher`
|
||||
#[derive(Default, Clone)]
|
||||
pub struct NodeCodec<H: Hasher>(PhantomData<H>);
|
||||
|
||||
// NOTE: what we'd really like here is:
|
||||
// `impl<H: Hasher> NodeCodec<H> for RlpNodeCodec<H> where H::Out: Decodable`
|
||||
// but due to the current limitations of Rust const evaluation we can't
|
||||
// do `const HASHED_NULL_NODE: H::Out = H::Out( … … )`. Perhaps one day soon?
|
||||
impl<H: Hasher> trie_db::NodeCodec<H> for NodeCodec<H> {
|
||||
type Error = Error;
|
||||
|
||||
fn hashed_null_node() -> H::Out {
|
||||
H::hash(&[0u8][..])
|
||||
}
|
||||
|
||||
fn decode(data: &[u8]) -> ::std::result::Result<Node, Self::Error> {
|
||||
use Error::BadFormat;
|
||||
let input = &mut &*data;
|
||||
match NodeHeader::decode(input).ok_or(BadFormat)? {
|
||||
NodeHeader::Null => Ok(Node::Empty),
|
||||
NodeHeader::Branch(has_value) => {
|
||||
let bitmap = u16::decode(input).ok_or(BadFormat)?;
|
||||
let value = if has_value {
|
||||
let count = <Compact<u32>>::decode(input).ok_or(BadFormat)?.0 as usize;
|
||||
Some(take(input, count).ok_or(BadFormat)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let mut children = [None; 16];
|
||||
let mut pot_cursor = 1;
|
||||
for i in 0..16 {
|
||||
if bitmap & pot_cursor != 0 {
|
||||
let count = <Compact<u32>>::decode(input).ok_or(BadFormat)?.0 as usize;
|
||||
children[i] = Some(take(input, count).ok_or(BadFormat)?);
|
||||
}
|
||||
pot_cursor <<= 1;
|
||||
}
|
||||
Ok(Node::Branch(children, value))
|
||||
}
|
||||
NodeHeader::Extension(nibble_count) => {
|
||||
let nibble_data = take(input, (nibble_count + 1) / 2).ok_or(BadFormat)?;
|
||||
let nibble_slice = NibbleSlice::new_offset(nibble_data, nibble_count % 2);
|
||||
let count = <Compact<u32>>::decode(input).ok_or(BadFormat)?.0 as usize;
|
||||
Ok(Node::Extension(nibble_slice, take(input, count).ok_or(BadFormat)?))
|
||||
}
|
||||
NodeHeader::Leaf(nibble_count) => {
|
||||
let nibble_data = take(input, (nibble_count + 1) / 2).ok_or(BadFormat)?;
|
||||
let nibble_slice = NibbleSlice::new_offset(nibble_data, nibble_count % 2);
|
||||
let count = <Compact<u32>>::decode(input).ok_or(BadFormat)?.0 as usize;
|
||||
Ok(Node::Leaf(nibble_slice, take(input, count).ok_or(BadFormat)?))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn try_decode_hash(data: &[u8]) -> Option<H::Out> {
|
||||
if data.len() == H::LENGTH {
|
||||
let mut r = H::Out::default();
|
||||
r.as_mut().copy_from_slice(data);
|
||||
Some(r)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn is_empty_node(data: &[u8]) -> bool {
|
||||
data == &[EMPTY_TRIE][..]
|
||||
}
|
||||
fn empty_node() -> Vec<u8> {
|
||||
vec![EMPTY_TRIE]
|
||||
}
|
||||
|
||||
// TODO: refactor this so that `partial` isn't already encoded with HPE. Should just be an `impl Iterator<Item=u8>`.
|
||||
fn leaf_node(partial: &[u8], value: &[u8]) -> Vec<u8> {
|
||||
let mut output = partial_to_key(partial, LEAF_NODE_OFFSET, LEAF_NODE_BIG);
|
||||
value.encode_to(&mut output);
|
||||
output
|
||||
}
|
||||
|
||||
// TODO: refactor this so that `partial` isn't already encoded with HPE. Should just be an `impl Iterator<Item=u8>`.
|
||||
fn ext_node(partial: &[u8], child: ChildReference<H::Out>) -> Vec<u8> {
|
||||
let mut output = partial_to_key(partial, EXTENSION_NODE_OFFSET, EXTENSION_NODE_BIG);
|
||||
match child {
|
||||
ChildReference::Hash(h) =>
|
||||
h.as_ref().encode_to(&mut output),
|
||||
ChildReference::Inline(inline_data, len) =>
|
||||
(&AsRef::<[u8]>::as_ref(&inline_data)[..len]).encode_to(&mut output),
|
||||
};
|
||||
output
|
||||
}
|
||||
|
||||
fn branch_node<I>(children: I, maybe_value: Option<DBValue>) -> Vec<u8>
|
||||
where I: IntoIterator<Item=Option<ChildReference<H::Out>>> + Iterator<Item=Option<ChildReference<H::Out>>>
|
||||
{
|
||||
let mut output = vec![0, 0, 0];
|
||||
let have_value = if let Some(value) = maybe_value {
|
||||
(&*value).encode_to(&mut output);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
let prefix = branch_node(have_value, children.map(|maybe_child| match maybe_child {
|
||||
Some(ChildReference::Hash(h)) => {
|
||||
h.as_ref().encode_to(&mut output);
|
||||
true
|
||||
}
|
||||
Some(ChildReference::Inline(inline_data, len)) => {
|
||||
(&AsRef::<[u8]>::as_ref(&inline_data)[..len]).encode_to(&mut output);
|
||||
true
|
||||
}
|
||||
None => false,
|
||||
}));
|
||||
output[0..3].copy_from_slice(&prefix[..]);
|
||||
output
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
// Copyright 2015-2018 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! The node header.
|
||||
|
||||
use codec::{Encode, Decode, Input, Output};
|
||||
use super::{EMPTY_TRIE, LEAF_NODE_OFFSET, LEAF_NODE_BIG, EXTENSION_NODE_OFFSET,
|
||||
EXTENSION_NODE_BIG, BRANCH_NODE_NO_VALUE, BRANCH_NODE_WITH_VALUE, LEAF_NODE_THRESHOLD,
|
||||
EXTENSION_NODE_THRESHOLD, LEAF_NODE_SMALL_MAX, EXTENSION_NODE_SMALL_MAX};
|
||||
|
||||
/// A node header.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
pub enum NodeHeader {
|
||||
Null,
|
||||
Branch(bool),
|
||||
Extension(usize),
|
||||
Leaf(usize),
|
||||
}
|
||||
|
||||
impl Encode for NodeHeader {
|
||||
fn encode_to<T: Output>(&self, output: &mut T) {
|
||||
match self {
|
||||
NodeHeader::Null => output.push_byte(EMPTY_TRIE),
|
||||
|
||||
NodeHeader::Branch(true) => output.push_byte(BRANCH_NODE_WITH_VALUE),
|
||||
NodeHeader::Branch(false) => output.push_byte(BRANCH_NODE_NO_VALUE),
|
||||
|
||||
NodeHeader::Leaf(nibble_count) if *nibble_count < LEAF_NODE_THRESHOLD as usize =>
|
||||
output.push_byte(LEAF_NODE_OFFSET + *nibble_count as u8),
|
||||
NodeHeader::Leaf(nibble_count) => {
|
||||
output.push_byte(LEAF_NODE_BIG);
|
||||
output.push_byte((*nibble_count - LEAF_NODE_THRESHOLD as usize) as u8);
|
||||
}
|
||||
|
||||
NodeHeader::Extension(nibble_count) if *nibble_count < EXTENSION_NODE_THRESHOLD as usize =>
|
||||
output.push_byte(EXTENSION_NODE_OFFSET + *nibble_count as u8),
|
||||
NodeHeader::Extension(nibble_count) => {
|
||||
output.push_byte(EXTENSION_NODE_BIG);
|
||||
output.push_byte((*nibble_count - EXTENSION_NODE_THRESHOLD as usize) as u8);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode for NodeHeader {
|
||||
fn decode<I: Input>(input: &mut I) -> Option<Self> {
|
||||
Some(match input.read_byte()? {
|
||||
EMPTY_TRIE => NodeHeader::Null, // 0
|
||||
|
||||
i @ LEAF_NODE_OFFSET ... LEAF_NODE_SMALL_MAX => // 1 ... (127 - 1)
|
||||
NodeHeader::Leaf((i - LEAF_NODE_OFFSET) as usize),
|
||||
LEAF_NODE_BIG => // 127
|
||||
NodeHeader::Leaf(input.read_byte()? as usize + LEAF_NODE_THRESHOLD as usize),
|
||||
|
||||
i @ EXTENSION_NODE_OFFSET ... EXTENSION_NODE_SMALL_MAX =>// 128 ... (253 - 1)
|
||||
NodeHeader::Extension((i - EXTENSION_NODE_OFFSET) as usize),
|
||||
EXTENSION_NODE_BIG => // 253
|
||||
NodeHeader::Extension(input.read_byte()? as usize + EXTENSION_NODE_THRESHOLD as usize),
|
||||
|
||||
BRANCH_NODE_NO_VALUE => NodeHeader::Branch(false), // 254
|
||||
BRANCH_NODE_WITH_VALUE => NodeHeader::Branch(true), // 255
|
||||
|
||||
_ => unreachable!(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
// Copyright 2015-2018 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! `TrieStream` implementation for Substrate's trie format.
|
||||
|
||||
use std::iter::once;
|
||||
use hash_db::Hasher;
|
||||
use trie_root;
|
||||
use codec::Encode;
|
||||
|
||||
use super::{EMPTY_TRIE, LEAF_NODE_OFFSET, LEAF_NODE_BIG, EXTENSION_NODE_OFFSET,
|
||||
EXTENSION_NODE_BIG, branch_node};
|
||||
|
||||
/// Codec-flavoured TrieStream
|
||||
pub struct TrieStream {
|
||||
buffer: Vec<u8>,
|
||||
}
|
||||
|
||||
impl TrieStream {
|
||||
// useful for debugging but not used otherwise
|
||||
pub fn as_raw(&self) -> &[u8] { &self.buffer }
|
||||
}
|
||||
|
||||
/// Create a leaf/extension node, encoding a number of nibbles. Note that this
|
||||
/// cannot handle a number of nibbles that is zero or greater than 127 and if
|
||||
/// you attempt to do so *IT WILL PANIC*.
|
||||
fn fuse_nibbles_node<'a>(nibbles: &'a [u8], leaf: bool) -> impl Iterator<Item = u8> + 'a {
|
||||
debug_assert!(nibbles.len() < 255 + 126, "nibbles length too long. what kind of size of key are you trying to include in the trie!?!");
|
||||
// We use two ranges of possible values; one for leafs and the other for extensions.
|
||||
// Each range encodes zero following nibbles up to some maximum. If the maximum is
|
||||
// reached, then it is considered "big" and a second byte follows it in order to
|
||||
// encode a further offset to the number of nibbles of up to 255. Beyond that, we
|
||||
// cannot encode. This shouldn't be a problem though since that allows for keys of
|
||||
// up to 380 nibbles (190 bytes) and we expect key sizes to be generally 128-bit (16
|
||||
// bytes) or, at a push, 384-bit (48 bytes).
|
||||
|
||||
let (first_byte_small, big_threshold) = if leaf {
|
||||
(LEAF_NODE_OFFSET, (LEAF_NODE_BIG - LEAF_NODE_OFFSET) as usize)
|
||||
} else {
|
||||
(EXTENSION_NODE_OFFSET, (EXTENSION_NODE_BIG - EXTENSION_NODE_OFFSET) as usize)
|
||||
};
|
||||
let first_byte = first_byte_small + nibbles.len().min(big_threshold) as u8;
|
||||
once(first_byte)
|
||||
.chain(if nibbles.len() >= big_threshold { Some((nibbles.len() - big_threshold) as u8) } else { None })
|
||||
.chain(if nibbles.len() % 2 == 1 { Some(nibbles[0]) } else { None })
|
||||
.chain(nibbles[nibbles.len() % 2..].chunks(2).map(|ch| ch[0] << 4 | ch[1]))
|
||||
}
|
||||
|
||||
impl trie_root::TrieStream for TrieStream {
|
||||
fn new() -> Self { Self {buffer: Vec::new() } }
|
||||
fn append_empty_data(&mut self) {
|
||||
self.buffer.push(EMPTY_TRIE);
|
||||
}
|
||||
|
||||
fn append_leaf(&mut self, key: &[u8], value: &[u8]) {
|
||||
self.buffer.extend(fuse_nibbles_node(key, true));
|
||||
// OPTIMISATION: I'd like to do `hpe.encode_to(&mut self.buffer);` here; need an `impl<'a> Encode for impl Iterator<Item = u8> + 'a`?
|
||||
value.encode_to(&mut self.buffer);
|
||||
}
|
||||
fn begin_branch(&mut self, maybe_value: Option<&[u8]>, has_children: impl Iterator<Item = bool>) {
|
||||
// println!("[begin_branch] pushing BRANCH_NODE");
|
||||
self.buffer.extend(&branch_node(maybe_value.is_some(), has_children));
|
||||
// Push the value if one exists.
|
||||
if let Some(value) = maybe_value {
|
||||
value.encode_to(&mut self.buffer);
|
||||
}
|
||||
// println!("[begin_branch] buffer so far: {:#x?}", self.buffer);
|
||||
}
|
||||
fn append_extension(&mut self, key: &[u8]) {
|
||||
self.buffer.extend(fuse_nibbles_node(key, false));
|
||||
}
|
||||
fn append_substream<H: Hasher>(&mut self, other: Self) {
|
||||
let data = other.out();
|
||||
// println!("[append_substream] START own buffer: {:x?}", self.buffer);
|
||||
// println!("[append_substream] START other buffer: {:x?}", data);
|
||||
match data.len() {
|
||||
0...31 => {
|
||||
// println!("[append_substream] appending data, because data.len() = {}", data.len());
|
||||
data.encode_to(&mut self.buffer)
|
||||
},
|
||||
_ => {
|
||||
// println!("[append_substream] would have hashed, because data.len() = {}", data.len());
|
||||
// data.encode_to(&mut self.buffer)
|
||||
// TODO: re-enable hashing before merging
|
||||
H::hash(&data).as_ref().encode_to(&mut self.buffer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn out(self) -> Vec<u8> { self.buffer }
|
||||
}
|
||||
Reference in New Issue
Block a user