Files
pezkuwi-subxt/substrate/primitives/trie/src/lib.rs
T
Bastian Köcher 9ca2300b05 Order delta before calculating the storage root (#6780)
We need to order the delta before calculating the storage root, because
the order is important if the storage root is calculated using a storage
proof. The problem is arises when the delta is different than at the
time the storage root was recorded, because we may require a different
node that is not part of the proof and so, the storage root can not be
calculated. The problem is solved by always order the delta to use the
same order when calculating the storage root while recording the
stroage proof and when calculating the storage root using
the storage proof.

To prevent this bug in future again, a regression test is added.

Fixes: https://github.com/paritytech/cumulus/issues/146
2020-07-31 14:58:38 +02:00

886 lines
27 KiB
Rust

// Copyright 2015-2020 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/>.
//! Utility functions to interact with Substrate's Base-16 Modified Merkle Patricia tree ("trie").
#![cfg_attr(not(feature = "std"), no_std)]
mod error;
mod node_header;
mod node_codec;
mod storage_proof;
mod trie_stream;
use sp_std::{boxed::Box, marker::PhantomData, vec::Vec, borrow::Borrow};
use hash_db::{Hasher, Prefix};
use trie_db::proof::{generate_proof, verify_proof};
pub use trie_db::proof::VerifyError;
/// 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;
pub use storage_proof::StorageProof;
/// Various re-exports from the `trie-db` crate.
pub use trie_db::{
Trie, TrieMut, DBValue, Recorder, CError, Query, TrieLayout, TrieConfiguration, nibble_ops, TrieDBIterator,
};
/// Various re-exports from the `memory-db` crate.
pub use memory_db::KeyFunction;
pub use memory_db::prefixed_key;
/// Various re-exports from the `hash-db` crate.
pub use hash_db::{HashDB as HashDBT, EMPTY_PREFIX};
#[derive(Default)]
/// substrate trie layout
pub struct Layout<H>(sp_std::marker::PhantomData<H>);
impl<H: Hasher> TrieLayout for Layout<H> {
const USE_EXTENSION: bool = false;
const ALLOW_EMPTY: bool = true;
type Hash = H;
type Codec = NodeCodec<Self::Hash>;
}
impl<H: Hasher> TrieConfiguration for Layout<H> {
fn trie_root<I, A, B>(input: I) -> <Self::Hash as Hasher>::Out where
I: IntoIterator<Item = (A, B)>,
A: AsRef<[u8]> + Ord,
B: AsRef<[u8]>,
{
trie_root::trie_root_no_extension::<H, TrieStream, _, _, _>(input)
}
fn trie_root_unhashed<I, A, B>(input: I) -> Vec<u8> where
I: IntoIterator<Item = (A, B)>,
A: AsRef<[u8]> + Ord,
B: AsRef<[u8]>,
{
trie_root::unhashed_trie_no_extension::<H, TrieStream, _, _, _>(input)
}
fn encode_index(input: u32) -> Vec<u8> {
codec::Encode::encode(&codec::Compact(input))
}
}
#[cfg(not(feature = "memory-tracker"))]
type MemTracker = memory_db::NoopTracker<trie_db::DBValue>;
#[cfg(feature = "memory-tracker")]
type MemTracker = memory_db::MemCounter<trie_db::DBValue>;
/// TrieDB error over `TrieConfiguration` trait.
pub type TrieError<L> = trie_db::TrieError<TrieHash<L>, CError<L>>;
/// Reexport from `hash_db`, with genericity set for `Hasher` trait.
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 {}
/// Reexport from `hash_db`, with genericity set for `Hasher` trait.
pub type HashDB<'a, H> = dyn hash_db::HashDB<H, trie_db::DBValue> + 'a;
/// Reexport from `hash_db`, with genericity set for `Hasher` trait.
/// This uses a `KeyFunction` for prefixing keys internally (avoiding
/// key conflict for non random keys).
pub type PrefixedMemoryDB<H> = memory_db::MemoryDB<
H, memory_db::PrefixedKey<H>, trie_db::DBValue, MemTracker
>;
/// Reexport from `hash_db`, with genericity set for `Hasher` trait.
/// This uses a noops `KeyFunction` (key addressing must be hashed or using
/// an encoding scheme that avoid key conflict).
pub type MemoryDB<H> = memory_db::MemoryDB<
H, memory_db::HashKey<H>, trie_db::DBValue, MemTracker,
>;
/// Reexport from `hash_db`, with genericity set for `Hasher` trait.
pub type GenericMemoryDB<H, KF> = memory_db::MemoryDB<
H, KF, trie_db::DBValue, MemTracker
>;
/// Persistent trie database read-access interface for the a given hasher.
pub type TrieDB<'a, L> = trie_db::TrieDB<'a, L>;
/// Persistent trie database write-access interface for the a given hasher.
pub type TrieDBMut<'a, L> = trie_db::TrieDBMut<'a, L>;
/// Querying interface, as in `trie_db` but less generic.
pub type Lookup<'a, L, Q> = trie_db::Lookup<'a, L, Q>;
/// Hash type for a trie layout.
pub type TrieHash<L> = <<L as TrieLayout>::Hash as Hasher>::Out;
/// This module is for non generic definition of trie type.
/// Only the `Hasher` trait is generic in this case.
pub mod trie_types {
pub type Layout<H> = super::Layout<H>;
/// Persistent trie database read-access interface for the a given hasher.
pub type TrieDB<'a, H> = super::TrieDB<'a, Layout<H>>;
/// Persistent trie database write-access interface for the a given hasher.
pub type TrieDBMut<'a, H> = super::TrieDBMut<'a, Layout<H>>;
/// Querying interface, as in `trie_db` but less generic.
pub type Lookup<'a, H, Q> = trie_db::Lookup<'a, Layout<H>, Q>;
/// As in `trie_db`, but less generic, error type for the crate.
pub type TrieError<H> = trie_db::TrieError<H, super::Error>;
}
/// Create a proof for a subset of keys in a trie.
///
/// The `keys` may contain any set of keys regardless of each one of them is included
/// in the `db`.
///
/// For a key `K` that is included in the `db` a proof of inclusion is generated.
/// For a key `K` that is not included in the `db` a proof of non-inclusion is generated.
/// These can be later checked in `verify_trie_proof`.
pub fn generate_trie_proof<'a, L: TrieConfiguration, I, K, DB>(
db: &DB,
root: TrieHash<L>,
keys: I,
) -> Result<Vec<Vec<u8>>, Box<TrieError<L>>> where
I: IntoIterator<Item=&'a K>,
K: 'a + AsRef<[u8]>,
DB: hash_db::HashDBRef<L::Hash, trie_db::DBValue>,
{
let trie = TrieDB::<L>::new(db, &root)?;
generate_proof(&trie, keys)
}
/// Verify a set of key-value pairs against a trie root and a proof.
///
/// Checks a set of keys with optional values for inclusion in the proof that was generated by
/// `generate_trie_proof`.
/// If the value in the pair is supplied (`(key, Some(value))`), this key-value pair will be
/// checked for inclusion in the proof.
/// If the value is omitted (`(key, None)`), this key will be checked for non-inclusion in the
/// proof.
pub fn verify_trie_proof<'a, L: TrieConfiguration, I, K, V>(
root: &TrieHash<L>,
proof: &[Vec<u8>],
items: I,
) -> Result<(), VerifyError<TrieHash<L>, error::Error>> where
I: IntoIterator<Item=&'a (K, Option<V>)>,
K: 'a + AsRef<[u8]>,
V: 'a + AsRef<[u8]>,
{
verify_proof::<Layout<L::Hash>, _, _, _>(root, proof, items)
}
/// Determine a trie root given a hash DB and delta values.
pub fn delta_trie_root<L: TrieConfiguration, I, A, B, DB, V>(
db: &mut DB,
mut root: TrieHash<L>,
delta: I
) -> Result<TrieHash<L>, Box<TrieError<L>>> where
I: IntoIterator<Item = (A, B)>,
A: Borrow<[u8]>,
B: Borrow<Option<V>>,
V: Borrow<[u8]>,
DB: hash_db::HashDB<L::Hash, trie_db::DBValue>,
{
{
let mut trie = TrieDBMut::<L>::from_existing(&mut *db, &mut root)?;
let mut delta = delta.into_iter().collect::<Vec<_>>();
delta.sort_by(|l, r| l.0.borrow().cmp(r.0.borrow()));
for (key, change) in delta {
match change.borrow() {
Some(val) => trie.insert(key.borrow(), val.borrow())?,
None => trie.remove(key.borrow())?,
};
}
}
Ok(root)
}
/// Read a value from the trie.
pub fn read_trie_value<L: TrieConfiguration, DB: hash_db::HashDBRef<L::Hash, trie_db::DBValue>>(
db: &DB,
root: &TrieHash<L>,
key: &[u8]
) -> Result<Option<Vec<u8>>, Box<TrieError<L>>> {
Ok(TrieDB::<L>::new(&*db, root)?.get(key).map(|x| x.map(|val| val.to_vec()))?)
}
/// Read a value from the trie with given Query.
pub fn read_trie_value_with<
L: TrieConfiguration,
Q: Query<L::Hash, Item=DBValue>,
DB: hash_db::HashDBRef<L::Hash, trie_db::DBValue>
>(
db: &DB,
root: &TrieHash<L>,
key: &[u8],
query: Q
) -> Result<Option<Vec<u8>>, Box<TrieError<L>>> {
Ok(TrieDB::<L>::new(&*db, root)?.get_with(key, query).map(|x| x.map(|val| val.to_vec()))?)
}
/// Determine the empty child trie root.
pub fn empty_child_trie_root<L: TrieConfiguration>(
) -> <L::Hash as Hasher>::Out {
L::trie_root::<_, Vec<u8>, Vec<u8>>(core::iter::empty())
}
/// Determine a child trie root given its ordered contents, closed form. H is the default hasher,
/// but a generic implementation may ignore this type parameter and use other hashers.
pub fn child_trie_root<L: TrieConfiguration, I, A, B>(
input: I,
) -> <L::Hash as Hasher>::Out
where
I: IntoIterator<Item = (A, B)>,
A: AsRef<[u8]> + Ord,
B: AsRef<[u8]>,
{
L::trie_root(input)
}
/// Determine a child trie root given a hash DB and delta values. H is the default hasher,
/// but a generic implementation may ignore this type parameter and use other hashers.
pub fn child_delta_trie_root<L: TrieConfiguration, I, A, B, DB, RD, V>(
keyspace: &[u8],
db: &mut DB,
root_data: RD,
delta: I,
) -> Result<<L::Hash as Hasher>::Out, Box<TrieError<L>>>
where
I: IntoIterator<Item = (A, B)>,
A: Borrow<[u8]>,
B: Borrow<Option<V>>,
V: Borrow<[u8]>,
RD: AsRef<[u8]>,
DB: hash_db::HashDB<L::Hash, trie_db::DBValue>
{
let mut root = TrieHash::<L>::default();
// root is fetched from DB, not writable by runtime, so it's always valid.
root.as_mut().copy_from_slice(root_data.as_ref());
let mut db = KeySpacedDBMut::new(&mut *db, keyspace);
delta_trie_root::<L, _, _, _, _, _>(
&mut db,
root,
delta,
)
}
/// Call `f` for all keys in a child trie.
pub fn for_keys_in_child_trie<L: TrieConfiguration, F: FnMut(&[u8]), DB>(
keyspace: &[u8],
db: &DB,
root_slice: &[u8],
mut f: F
) -> Result<(), Box<TrieError<L>>>
where
DB: hash_db::HashDBRef<L::Hash, trie_db::DBValue>
{
let mut root = TrieHash::<L>::default();
// root is fetched from DB, not writable by runtime, so it's always valid.
root.as_mut().copy_from_slice(root_slice);
let db = KeySpacedDB::new(&*db, keyspace);
let trie = TrieDB::<L>::new(&db, &root)?;
let iter = trie.iter()?;
for x in iter {
let (key, _) = x?;
f(&key);
}
Ok(())
}
/// Record all keys for a given root.
pub fn record_all_keys<L: TrieConfiguration, DB>(
db: &DB,
root: &TrieHash<L>,
recorder: &mut Recorder<TrieHash<L>>
) -> Result<(), Box<TrieError<L>>> where
DB: hash_db::HashDBRef<L::Hash, trie_db::DBValue>
{
let trie = TrieDB::<L>::new(&*db, root)?;
let iter = trie.iter()?;
for x in iter {
let (key, _) = x?;
// there's currently no API like iter_with()
// => use iter to enumerate all keys AND lookup each
// key using get_with
trie.get_with(&key, &mut *recorder)?;
}
Ok(())
}
/// Read a value from the child trie.
pub fn read_child_trie_value<L: TrieConfiguration, DB>(
keyspace: &[u8],
db: &DB,
root_slice: &[u8],
key: &[u8]
) -> Result<Option<Vec<u8>>, Box<TrieError<L>>>
where
DB: hash_db::HashDBRef<L::Hash, trie_db::DBValue>
{
let mut root = TrieHash::<L>::default();
// root is fetched from DB, not writable by runtime, so it's always valid.
root.as_mut().copy_from_slice(root_slice);
let db = KeySpacedDB::new(&*db, keyspace);
Ok(TrieDB::<L>::new(&db, &root)?.get(key).map(|x| x.map(|val| val.to_vec()))?)
}
/// Read a value from the child trie with given query.
pub fn read_child_trie_value_with<L: TrieConfiguration, Q: Query<L::Hash, Item=DBValue>, DB>(
keyspace: &[u8],
db: &DB,
root_slice: &[u8],
key: &[u8],
query: Q
) -> Result<Option<Vec<u8>>, Box<TrieError<L>>>
where
DB: hash_db::HashDBRef<L::Hash, trie_db::DBValue>
{
let mut root = TrieHash::<L>::default();
// root is fetched from DB, not writable by runtime, so it's always valid.
root.as_mut().copy_from_slice(root_slice);
let db = KeySpacedDB::new(&*db, keyspace);
Ok(TrieDB::<L>::new(&db, &root)?.get_with(key, query).map(|x| x.map(|val| val.to_vec()))?)
}
/// `HashDB` implementation that append a encoded prefix (unique id bytes) in addition to the
/// prefix of every key value.
pub struct KeySpacedDB<'a, DB, H>(&'a DB, &'a [u8], PhantomData<H>);
/// `HashDBMut` implementation that append a encoded prefix (unique id bytes) in addition to the
/// prefix of every key value.
///
/// Mutable variant of `KeySpacedDB`, see [`KeySpacedDB`].
pub struct KeySpacedDBMut<'a, DB, H>(&'a mut DB, &'a [u8], PhantomData<H>);
/// Utility function used to merge some byte data (keyspace) and `prefix` data
/// before calling key value database primitives.
fn keyspace_as_prefix_alloc(ks: &[u8], prefix: Prefix) -> (Vec<u8>, Option<u8>) {
let mut result = sp_std::vec![0; ks.len() + prefix.0.len()];
result[..ks.len()].copy_from_slice(ks);
result[ks.len()..].copy_from_slice(prefix.0);
(result, prefix.1)
}
impl<'a, DB, H> KeySpacedDB<'a, DB, H> where
H: Hasher,
{
/// instantiate new keyspaced db
pub fn new(db: &'a DB, ks: &'a [u8]) -> Self {
KeySpacedDB(db, ks, PhantomData)
}
}
impl<'a, DB, H> KeySpacedDBMut<'a, DB, H> where
H: Hasher,
{
/// instantiate new keyspaced db
pub fn new(db: &'a mut DB, ks: &'a [u8]) -> Self {
KeySpacedDBMut(db, ks, PhantomData)
}
}
impl<'a, DB, H, T> hash_db::HashDBRef<H, T> for KeySpacedDB<'a, DB, H> where
DB: hash_db::HashDBRef<H, T>,
H: Hasher,
T: From<&'static [u8]>,
{
fn get(&self, key: &H::Out, prefix: Prefix) -> Option<T> {
let derived_prefix = keyspace_as_prefix_alloc(self.1, prefix);
self.0.get(key, (&derived_prefix.0, derived_prefix.1))
}
fn contains(&self, key: &H::Out, prefix: Prefix) -> bool {
let derived_prefix = keyspace_as_prefix_alloc(self.1, prefix);
self.0.contains(key, (&derived_prefix.0, derived_prefix.1))
}
}
impl<'a, DB, H, T> hash_db::HashDB<H, T> for KeySpacedDBMut<'a, DB, H> where
DB: hash_db::HashDB<H, T>,
H: Hasher,
T: Default + PartialEq<T> + for<'b> From<&'b [u8]> + Clone + Send + Sync,
{
fn get(&self, key: &H::Out, prefix: Prefix) -> Option<T> {
let derived_prefix = keyspace_as_prefix_alloc(self.1, prefix);
self.0.get(key, (&derived_prefix.0, derived_prefix.1))
}
fn contains(&self, key: &H::Out, prefix: Prefix) -> bool {
let derived_prefix = keyspace_as_prefix_alloc(self.1, prefix);
self.0.contains(key, (&derived_prefix.0, derived_prefix.1))
}
fn insert(&mut self, prefix: Prefix, value: &[u8]) -> H::Out {
let derived_prefix = keyspace_as_prefix_alloc(self.1, prefix);
self.0.insert((&derived_prefix.0, derived_prefix.1), value)
}
fn emplace(&mut self, key: H::Out, prefix: Prefix, value: T) {
let derived_prefix = keyspace_as_prefix_alloc(self.1, prefix);
self.0.emplace(key, (&derived_prefix.0, derived_prefix.1), value)
}
fn remove(&mut self, key: &H::Out, prefix: Prefix) {
let derived_prefix = keyspace_as_prefix_alloc(self.1, prefix);
self.0.remove(key, (&derived_prefix.0, derived_prefix.1))
}
}
impl<'a, DB, H, T> hash_db::AsHashDB<H, T> for KeySpacedDBMut<'a, DB, H> where
DB: hash_db::HashDB<H, T>,
H: Hasher,
T: Default + PartialEq<T> + for<'b> From<&'b [u8]> + Clone + Send + Sync,
{
fn as_hash_db(&self) -> &dyn hash_db::HashDB<H, T> { &*self }
fn as_hash_db_mut<'b>(&'b mut self) -> &'b mut (dyn hash_db::HashDB<H, T> + 'b) {
&mut *self
}
}
/// Constants used into trie simplification codec.
mod trie_constants {
pub const EMPTY_TRIE: u8 = 0;
pub const NIBBLE_SIZE_BOUND: usize = u16::max_value() as usize;
pub const LEAF_PREFIX_MASK: u8 = 0b_01 << 6;
pub const BRANCH_WITHOUT_MASK: u8 = 0b_10 << 6;
pub const BRANCH_WITH_MASK: u8 = 0b_11 << 6;
}
#[cfg(test)]
mod tests {
use super::*;
use codec::{Encode, Decode, Compact};
use sp_core::Blake2Hasher;
use hash_db::{HashDB, Hasher};
use trie_db::{DBValue, TrieMut, Trie, NodeCodec as NodeCodecT};
use trie_standardmap::{Alphabet, ValueMode, StandardMap};
use hex_literal::hex;
type Layout = super::Layout<Blake2Hasher>;
fn hashed_null_node<T: TrieConfiguration>() -> TrieHash<T> {
<T::Codec as NodeCodecT>::hashed_null_node()
}
fn check_equivalent<T: TrieConfiguration>(input: &Vec<(&[u8], &[u8])>) {
{
let closed_form = T::trie_root(input.clone());
let d = T::trie_root_unhashed(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::<T>::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<T: TrieConfiguration>(input: &Vec<(&[u8], &[u8])>) {
let mut memdb = MemoryDB::default();
let mut root = Default::default();
{
let mut t = TrieDBMut::<T>::new(&mut memdb, &mut root);
for (x, y) in input.clone() {
t.insert(x, y).unwrap();
}
}
{
let t = TrieDB::<T>::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 default_trie_root() {
let mut db = MemoryDB::default();
let mut root = TrieHash::<Layout>::default();
let mut empty = TrieDBMut::<Layout>::new(&mut db, &mut root);
empty.commit();
let root1 = empty.root().as_ref().to_vec();
let root2: Vec<u8> = Layout::trie_root::<_, Vec<u8>, Vec<u8>>(
std::iter::empty(),
).as_ref().iter().cloned().collect();
assert_eq!(root1, root2);
}
#[test]
fn empty_is_equivalent() {
let input: Vec<(&[u8], &[u8])> = vec![];
check_equivalent::<Layout>(&input);
check_iteration::<Layout>(&input);
}
#[test]
fn leaf_is_equivalent() {
let input: Vec<(&[u8], &[u8])> = vec![(&[0xaa][..], &[0xbb][..])];
check_equivalent::<Layout>(&input);
check_iteration::<Layout>(&input);
}
#[test]
fn branch_is_equivalent() {
let input: Vec<(&[u8], &[u8])> = vec![
(&[0xaa][..], &[0x10][..]),
(&[0xba][..], &[0x11][..]),
];
check_equivalent::<Layout>(&input);
check_iteration::<Layout>(&input);
}
#[test]
fn extension_and_branch_is_equivalent() {
let input: Vec<(&[u8], &[u8])> = vec![
(&[0xaa][..], &[0x10][..]),
(&[0xab][..], &[0x11][..]),
];
check_equivalent::<Layout>(&input);
check_iteration::<Layout>(&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_by(|&(ref a, _), &(ref b, _)| a.cmp(b));
let dr = d.iter().map(|v| (&v.0[..], &v.1[..])).collect();
check_equivalent::<Layout>(&dr);
check_iteration::<Layout>(&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::<Layout>(&input);
check_iteration::<Layout>(&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::<Layout>(&input);
check_iteration::<Layout>(&input);
}
#[test]
fn single_long_leaf_is_equivalent() {
let input: Vec<(&[u8], &[u8])> = vec![
(&[0xaa][..], &b"ABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABC"[..]),
(&[0xba][..], &[0x11][..]),
];
check_equivalent::<Layout>(&input);
check_iteration::<Layout>(&input);
}
#[test]
fn two_long_leaves_is_equivalent() {
let input: Vec<(&[u8], &[u8])> = vec![
(&[0xaa][..], &b"ABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABC"[..]),
(&[0xba][..], &b"ABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABC"[..])
];
check_equivalent::<Layout>(&input);
check_iteration::<Layout>(&input);
}
fn populate_trie<'db, T: TrieConfiguration>(
db: &'db mut dyn HashDB<T::Hash, DBValue>,
root: &'db mut TrieHash<T>,
v: &[(Vec<u8>, Vec<u8>)]
) -> TrieDBMut<'db, T> {
let mut t = TrieDBMut::<T>::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: TrieConfiguration>(
t: &mut TrieDBMut<'db, T>,
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::zero();
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(seed.as_fixed_bytes_mut());
let real = Layout::trie_root(x.clone());
let mut memdb = MemoryDB::default();
let mut root = Default::default();
let mut memtrie = populate_trie::<Layout>(&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::<Layout>(&mut memtrie, &x);
memtrie.commit();
let hashed_null_node = hashed_null_node::<Layout>();
if *memtrie.root() != hashed_null_node {
println!("- TRIE MISMATCH");
println!("");
println!("{:?} vs {:?}", memtrie.root(), hashed_null_node);
for i in &x {
println!("{:#x?} -> {:#x?}", i.0, i.1);
}
}
assert_eq!(*memtrie.root(), 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 = Layout::trie_root_unhashed::<_, _, _>(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 = Layout::trie_root_unhashed::<_, _, _>(input);
println!("trie: {:#x?}", trie);
assert_eq!(trie, vec![
0x42, // leaf 0x40 (2^6) 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 = Layout::trie_root_unhashed::<_, _, _>(input);
println!("trie: {:#x?}", trie);
let mut ex = Vec::<u8>::new();
ex.push(0x80); // branch, no value (0b_10..) no nibble
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(0x43); // leaf 0x40 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(0x43); // 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::<Layout>(&mut mdb, &mut root, &pairs);
let trie = TrieDB::<Layout>::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);
}
#[test]
fn proof_non_inclusion_works() {
let pairs = vec![
(hex!("0102").to_vec(), hex!("01").to_vec()),
(hex!("0203").to_vec(), hex!("0405").to_vec()),
];
let mut memdb = MemoryDB::default();
let mut root = Default::default();
populate_trie::<Layout>(&mut memdb, &mut root, &pairs);
let non_included_key: Vec<u8> = hex!("0909").to_vec();
let proof = generate_trie_proof::<Layout, _, _, _>(
&memdb,
root,
&[non_included_key.clone()]
).unwrap();
// Verifying that the K was not included into the trie should work.
assert!(verify_trie_proof::<Layout, _, _, Vec<u8>>(
&root,
&proof,
&[(non_included_key.clone(), None)],
).is_ok()
);
// Verifying that the K was included into the trie should fail.
assert!(verify_trie_proof::<Layout, _, _, Vec<u8>>(
&root,
&proof,
&[(non_included_key, Some(hex!("1010").to_vec()))],
).is_err()
);
}
#[test]
fn proof_inclusion_works() {
let pairs = vec![
(hex!("0102").to_vec(), hex!("01").to_vec()),
(hex!("0203").to_vec(), hex!("0405").to_vec()),
];
let mut memdb = MemoryDB::default();
let mut root = Default::default();
populate_trie::<Layout>(&mut memdb, &mut root, &pairs);
let proof = generate_trie_proof::<Layout, _, _, _>(
&memdb,
root,
&[pairs[0].0.clone()]
).unwrap();
// Check that a K, V included into the proof are verified.
assert!(verify_trie_proof::<Layout, _, _, _>(
&root,
&proof,
&[(pairs[0].0.clone(), Some(pairs[0].1.clone()))]
).is_ok()
);
// Absence of the V is not verified with the proof that has K, V included.
assert!(verify_trie_proof::<Layout, _, _, Vec<u8>>(
&root,
&proof,
&[(pairs[0].0.clone(), None)]
).is_err()
);
// K not included into the trie is not verified.
assert!(verify_trie_proof::<Layout, _, _, _>(
&root,
&proof,
&[(hex!("4242").to_vec(), Some(pairs[0].1.clone()))]
).is_err()
);
// K included into the trie but not included into the proof is not verified.
assert!(verify_trie_proof::<Layout, _, _, _>(
&root,
&proof,
&[(pairs[1].0.clone(), Some(pairs[1].1.clone()))]
).is_err()
);
}
#[test]
fn generate_storage_root_with_proof_works_independently_from_the_delta_order() {
let proof = StorageProof::decode(&mut &include_bytes!("../test-res/proof")[..]).unwrap();
let storage_root = sp_core::H256::decode(
&mut &include_bytes!("../test-res/storage_root")[..],
).unwrap();
// Delta order that is "invalid" so that it would require a different proof.
let invalid_delta = Vec::<(Vec<u8>, Option<Vec<u8>>)>::decode(
&mut &include_bytes!("../test-res/invalid-delta-order")[..],
).unwrap();
// Delta order that is "valid"
let valid_delta = Vec::<(Vec<u8>, Option<Vec<u8>>)>::decode(
&mut &include_bytes!("../test-res/valid-delta-order")[..],
).unwrap();
let proof_db = proof.into_memory_db::<Blake2Hasher>();
let first_storage_root = delta_trie_root::<Layout, _, _, _, _, _>(
&mut proof_db.clone(),
storage_root,
valid_delta,
).unwrap();
let second_storage_root = delta_trie_root::<Layout, _, _, _, _, _>(
&mut proof_db.clone(),
storage_root,
invalid_delta,
).unwrap();
assert_eq!(first_storage_root, second_storage_root);
}
}