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:
Gav Wood
2018-09-25 15:32:22 +01:00
committed by Arkadiy Paronyan
parent b02c274374
commit 82d6ca3484
90 changed files with 1977 additions and 1129 deletions
@@ -18,9 +18,8 @@
use std::collections::{BTreeMap, BTreeSet};
use codec::Decode;
use hashdb::Hasher;
use hash_db::Hasher;
use heapsize::HeapSizeOf;
use patricia_trie::NodeCodec;
use backend::Backend;
use overlayed_changes::OverlayedChanges;
use trie_backend_essence::{TrieBackendStorage, TrieBackendEssence};
@@ -34,19 +33,18 @@ use changes_trie::{Configuration, Storage};
/// required data.
/// Returns Ok(None) data required to prepare input pairs is not collected
/// or storage is not provided.
pub fn prepare_input<'a, B, S, H, C>(
pub fn prepare_input<'a, B, S, H>(
backend: &B,
storage: Option<&'a S>,
changes: &OverlayedChanges,
block: u64,
) -> Result<Option<Vec<InputPair>>, String>
where
B: Backend<H, C>,
B: Backend<H>,
S: Storage<H>,
&'a S: TrieBackendStorage<H>,
H: Hasher,
H::Out: HeapSizeOf,
C: NodeCodec<H>,
{
let (storage, config) = match (storage, changes.changes_trie_config.as_ref()) {
(Some(storage), Some(config)) => (storage, config),
@@ -58,7 +56,7 @@ pub fn prepare_input<'a, B, S, H, C>(
backend,
block,
changes)?);
input.extend(prepare_digest_input::<_, H, C>(
input.extend(prepare_digest_input::<_, H>(
block,
config,
storage)?);
@@ -67,15 +65,15 @@ pub fn prepare_input<'a, B, S, H, C>(
}
/// Prepare ExtrinsicIndex input pairs.
fn prepare_extrinsics_input<B, H, C>(
fn prepare_extrinsics_input<B, H>(
backend: &B,
block: u64,
changes: &OverlayedChanges,
) -> Result<impl Iterator<Item=InputPair>, String>
where
B: Backend<H, C>,
B: Backend<H>,
H: Hasher,
C: NodeCodec<H>,
{
let mut extrinsic_map = BTreeMap::<Vec<u8>, BTreeSet<u32>>::new();
for (key, val) in changes.prospective.iter().chain(changes.committed.iter()) {
@@ -104,7 +102,7 @@ fn prepare_extrinsics_input<B, H, C>(
}
/// Prepare DigestIndex input pairs.
fn prepare_digest_input<'a, S, H, C>(
fn prepare_digest_input<'a, S, H>(
block: u64,
config: &Configuration,
storage: &'a S
@@ -114,13 +112,12 @@ fn prepare_digest_input<'a, S, H, C>(
&'a S: TrieBackendStorage<H>,
H: Hasher,
H::Out: HeapSizeOf,
C: NodeCodec<H>,
{
let mut digest_map = BTreeMap::<Vec<u8>, BTreeSet<u64>>::new();
for digest_build_block in digest_build_iterator(config, block) {
let trie_root = storage.root(digest_build_block)?;
let trie_root = trie_root.ok_or_else(|| format!("No changes trie root for block {}", digest_build_block))?;
let trie_storage = TrieBackendEssence::<_, H, C>::new(storage, trie_root);
let trie_storage = TrieBackendEssence::<_, H>::new(storage, trie_root);
let extrinsic_prefix = ExtrinsicIndex::key_neutral_prefix(digest_build_block);
trie_storage.for_keys_with_prefix(&extrinsic_prefix, |key|
@@ -147,15 +144,15 @@ fn prepare_digest_input<'a, S, H, C>(
#[cfg(test)]
mod test {
use codec::Encode;
use primitives::{Blake2Hasher, RlpCodec};
use primitives::Blake2Hasher;
use primitives::storage::well_known_keys::EXTRINSIC_INDEX;
use backend::InMemory;
use changes_trie::storage::InMemoryStorage;
use overlayed_changes::OverlayedValue;
use super::*;
fn prepare_for_build() -> (InMemory<Blake2Hasher, RlpCodec>, InMemoryStorage<Blake2Hasher>, OverlayedChanges) {
let backend: InMemory<_, _> = vec![
fn prepare_for_build() -> (InMemory<Blake2Hasher>, InMemoryStorage<Blake2Hasher>, OverlayedChanges) {
let backend: InMemory<_> = vec![
(vec![100], vec![255]),
(vec![101], vec![255]),
(vec![102], vec![255]),
@@ -163,7 +160,7 @@ mod test {
(vec![104], vec![255]),
(vec![105], vec![255]),
].into_iter().collect::<::std::collections::HashMap<_, _>>().into();
let storage = InMemoryStorage::with_inputs::<RlpCodec>(vec![
let storage = InMemoryStorage::with_inputs(vec![
(1, vec![
InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 1, key: vec![100] }, vec![1, 3]),
InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 1, key: vec![101] }, vec![0, 2]),
@@ -231,7 +228,7 @@ mod test {
#[test]
fn build_changes_trie_nodes_on_non_digest_block() {
let (backend, storage, changes) = prepare_for_build();
let changes_trie_nodes = prepare_input::<_, _, _, RlpCodec>(&backend, Some(&storage), &changes, 5).unwrap();
let changes_trie_nodes = prepare_input(&backend, Some(&storage), &changes, 5).unwrap();
assert_eq!(changes_trie_nodes, Some(vec![
InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 5, key: vec![100] }, vec![0, 2, 3]),
InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 5, key: vec![101] }, vec![1]),
@@ -242,7 +239,7 @@ mod test {
#[test]
fn build_changes_trie_nodes_on_digest_block_l1() {
let (backend, storage, changes) = prepare_for_build();
let changes_trie_nodes = prepare_input::<_, _, _, RlpCodec>(&backend, Some(&storage), &changes, 4).unwrap();
let changes_trie_nodes = prepare_input(&backend, Some(&storage), &changes, 4).unwrap();
assert_eq!(changes_trie_nodes, Some(vec![
InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 4, key: vec![100] }, vec![0, 2, 3]),
InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 4, key: vec![101] }, vec![1]),
@@ -258,7 +255,7 @@ mod test {
#[test]
fn build_changes_trie_nodes_on_digest_block_l2() {
let (backend, storage, changes) = prepare_for_build();
let changes_trie_nodes = prepare_input::<_, _, _, RlpCodec>(&backend, Some(&storage), &changes, 16).unwrap();
let changes_trie_nodes = prepare_input(&backend, Some(&storage), &changes, 16).unwrap();
assert_eq!(changes_trie_nodes, Some(vec![
InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 16, key: vec![100] }, vec![0, 2, 3]),
InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 16, key: vec![101] }, vec![1]),
@@ -282,7 +279,7 @@ mod test {
extrinsics: Some(vec![1].into_iter().collect())
});
let changes_trie_nodes = prepare_input::<_, _, _, RlpCodec>(&backend, Some(&storage), &changes, 4).unwrap();
let changes_trie_nodes = prepare_input(&backend, Some(&storage), &changes, 4).unwrap();
assert_eq!(changes_trie_nodes, Some(vec![
InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 4, key: vec![100] }, vec![0, 2, 3]),
InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 4, key: vec![101] }, vec![1]),
@@ -20,10 +20,9 @@
use std::cell::RefCell;
use std::collections::VecDeque;
use codec::{Decode, Encode};
use hashdb::{HashDB, Hasher};
use hash_db::{HashDB, Hasher};
use heapsize::HeapSizeOf;
use memorydb::MemoryDB;
use patricia_trie::{NodeCodec, Recorder};
use substrate_trie::{Recorder, MemoryDB};
use changes_trie::{Configuration, Storage};
use changes_trie::input::{DigestIndex, ExtrinsicIndex, DigestIndexValue, ExtrinsicIndexValue};
use changes_trie::storage::{TrieBackendAdapter, InMemoryStorage};
@@ -32,7 +31,7 @@ use trie_backend_essence::{TrieBackendEssence};
/// Return changes of given key at given blocks range.
/// `max` is the number of best known block.
pub fn key_changes<S: Storage<H>, H: Hasher, C: NodeCodec<H>>(
pub fn key_changes<S: Storage<H>, H: Hasher>(
config: &Configuration,
storage: &S,
begin: u64,
@@ -52,13 +51,12 @@ pub fn key_changes<S: Storage<H>, H: Hasher, C: NodeCodec<H>>(
_hasher: ::std::marker::PhantomData::<H>::default(),
},
_codec: ::std::marker::PhantomData::<C>::default(),
}.collect()
}
/// Returns proof of changes of given key at given blocks range.
/// `max` is the number of best known block.
pub fn key_changes_proof<S: Storage<H>, H: Hasher, C: NodeCodec<H>>(
pub fn key_changes_proof<S: Storage<H>, H: Hasher>(
config: &Configuration,
storage: &S,
begin: u64,
@@ -79,7 +77,6 @@ pub fn key_changes_proof<S: Storage<H>, H: Hasher, C: NodeCodec<H>>(
_hasher: ::std::marker::PhantomData::<H>::default(),
},
proof_recorder: Default::default(),
_codec: ::std::marker::PhantomData::<C>::default(),
};
// iterate to collect proof
@@ -92,7 +89,7 @@ pub fn key_changes_proof<S: Storage<H>, H: Hasher, C: NodeCodec<H>>(
/// Check key changes proog and return changes of the key at given blocks range.
/// `max` is the number of best known block.
pub fn key_changes_proof_check<S: Storage<H>, H: Hasher, C: NodeCodec<H>>(
pub fn key_changes_proof_check<S: Storage<H>, H: Hasher>(
config: &Configuration,
roots_storage: &S, // TODO: use RootsStorage is only used to gather root
proof: Vec<Vec<u8>>,
@@ -101,7 +98,7 @@ pub fn key_changes_proof_check<S: Storage<H>, H: Hasher, C: NodeCodec<H>>(
max: u64,
key: &[u8]
) -> Result<Vec<(u64, u32)>, String> where H::Out: HeapSizeOf {
let mut proof_db = MemoryDB::<H>::new();
let mut proof_db = MemoryDB::<H>::default(); // TODO: use new for correctness
for item in proof {
proof_db.insert(&item);
}
@@ -119,7 +116,6 @@ pub fn key_changes_proof_check<S: Storage<H>, H: Hasher, C: NodeCodec<H>>(
_hasher: ::std::marker::PhantomData::<H>::default(),
},
_codec: ::std::marker::PhantomData::<C>::default(),
}.collect()
}
@@ -239,28 +235,29 @@ impl<'a, RS: 'a + Storage<H>, S: Storage<H>, H: Hasher> DrilldownIteratorEssence
}
/// Exploring drilldown operator.
struct DrilldownIterator<'a, RS: 'a + Storage<H>, S: 'a + Storage<H>, H: Hasher, C: NodeCodec<H>> {
struct DrilldownIterator<'a, RS: 'a + Storage<H>, S: 'a + Storage<H>, H: Hasher> {
essence: DrilldownIteratorEssence<'a, RS, S, H>,
_codec: ::std::marker::PhantomData<C>,
}
impl<'a, RS: 'a + Storage<H>, S: Storage<H>, H: Hasher, C: NodeCodec<H>> Iterator for DrilldownIterator<'a, RS, S, H, C> where H::Out: HeapSizeOf {
impl<'a, RS: 'a + Storage<H>, S: Storage<H>, H: Hasher> Iterator
for DrilldownIterator<'a, RS, S, H>
where H::Out: HeapSizeOf
{
type Item = Result<(u64, u32), String>;
fn next(&mut self) -> Option<Self::Item> {
self.essence.next(|storage, root, key|
TrieBackendEssence::<_, H, C>::new(TrieBackendAdapter::new(storage), root).storage(key))
TrieBackendEssence::<_, H>::new(TrieBackendAdapter::new(storage), root).storage(key))
}
}
/// Proving drilldown iterator.
struct ProvingDrilldownIterator<'a, RS: 'a + Storage<H>, S: 'a + Storage<H>, H: Hasher, C: NodeCodec<H>> {
struct ProvingDrilldownIterator<'a, RS: 'a + Storage<H>, S: 'a + Storage<H>, H: Hasher> {
essence: DrilldownIteratorEssence<'a, RS, S, H>,
proof_recorder: RefCell<Recorder<H::Out>>,
_codec: ::std::marker::PhantomData<C>,
}
impl<'a, RS: 'a + Storage<H>, S: Storage<H>, H: Hasher, C: NodeCodec<H>> ProvingDrilldownIterator<'a, RS, S, H, C> {
impl<'a, RS: 'a + Storage<H>, S: Storage<H>, H: Hasher> ProvingDrilldownIterator<'a, RS, S, H> {
/// Consume the iterator, extracting the gathered proof in lexicographical order
/// by value.
pub fn extract_proof(self) -> Vec<Vec<u8>> {
@@ -271,14 +268,14 @@ impl<'a, RS: 'a + Storage<H>, S: Storage<H>, H: Hasher, C: NodeCodec<H>> Proving
}
}
impl<'a, RS: 'a + Storage<H>, S: Storage<H>, H: Hasher, C: NodeCodec<H>> Iterator for ProvingDrilldownIterator<'a, RS, S, H, C> where H::Out: HeapSizeOf {
impl<'a, RS: 'a + Storage<H>, S: Storage<H>, H: Hasher> Iterator for ProvingDrilldownIterator<'a, RS, S, H> where H::Out: HeapSizeOf {
type Item = Result<(u64, u32), String>;
fn next(&mut self) -> Option<Self::Item> {
let proof_recorder = &mut *self.proof_recorder.try_borrow_mut()
.expect("only fails when already borrowed; storage() is non-reentrant; qed");
self.essence.next(|storage, root, key|
ProvingBackendEssence::<_, H, C> {
ProvingBackendEssence::<_, H> {
backend: &TrieBackendEssence::new(TrieBackendAdapter::new(storage), root),
proof_recorder,
}.storage(key))
@@ -356,14 +353,14 @@ fn lower_bound_max_digest(
#[cfg(test)]
mod tests {
use primitives::{Blake2Hasher, RlpCodec};
use primitives::Blake2Hasher;
use changes_trie::input::InputPair;
use changes_trie::storage::InMemoryStorage;
use super::*;
fn prepare_for_drilldown() -> (Configuration, InMemoryStorage<Blake2Hasher>) {
let config = Configuration { digest_interval: 4, digest_levels: 2 };
let backend = InMemoryStorage::with_inputs::<RlpCodec>(vec![
let backend = InMemoryStorage::with_inputs(vec![
// digest: 1..4 => [(3, 0)]
(1, vec![]),
(2, vec![]),
@@ -403,7 +400,7 @@ mod tests {
#[test]
fn drilldown_iterator_works() {
let (config, storage) = prepare_for_drilldown();
let drilldown_result = key_changes::<InMemoryStorage<Blake2Hasher>, Blake2Hasher, RlpCodec>(
let drilldown_result = key_changes::<InMemoryStorage<Blake2Hasher>, Blake2Hasher>(
&config, &storage, 0, 100, 1000, &[42]);
assert_eq!(drilldown_result, Ok(vec![(8, 2), (8, 1), (6, 3), (3, 0)]));
@@ -414,16 +411,16 @@ mod tests {
let (config, storage) = prepare_for_drilldown();
storage.clear_storage();
assert!(key_changes::<InMemoryStorage<Blake2Hasher>, Blake2Hasher, RlpCodec>(
assert!(key_changes::<InMemoryStorage<Blake2Hasher>, Blake2Hasher>(
&config, &storage, 0, 100, 1000, &[42]).is_err());
}
#[test]
fn drilldown_iterator_fails_when_range_is_invalid() {
let (config, storage) = prepare_for_drilldown();
assert!(key_changes::<InMemoryStorage<Blake2Hasher>, Blake2Hasher, RlpCodec>(
assert!(key_changes::<InMemoryStorage<Blake2Hasher>, Blake2Hasher>(
&config, &storage, 0, 100, 50, &[42]).is_err());
assert!(key_changes::<InMemoryStorage<Blake2Hasher>, Blake2Hasher, RlpCodec>(
assert!(key_changes::<InMemoryStorage<Blake2Hasher>, Blake2Hasher>(
&config, &storage, 20, 10, 100, &[42]).is_err());
}
@@ -434,7 +431,7 @@ mod tests {
// create drilldown iterator that records all trie nodes during drilldown
let (remote_config, remote_storage) = prepare_for_drilldown();
let remote_proof = key_changes_proof::<InMemoryStorage<Blake2Hasher>, Blake2Hasher, RlpCodec>(
let remote_proof = key_changes_proof::<InMemoryStorage<Blake2Hasher>, Blake2Hasher>(
&remote_config, &remote_storage,
0, 100, 1000, &[42]).unwrap();
@@ -443,7 +440,7 @@ mod tests {
// create drilldown iterator that works the same, but only depends on trie
let (local_config, local_storage) = prepare_for_drilldown();
local_storage.clear_storage();
let local_result = key_changes_proof_check::<InMemoryStorage<Blake2Hasher>, Blake2Hasher, RlpCodec>(
let local_result = key_changes_proof_check::<InMemoryStorage<Blake2Hasher>, Blake2Hasher>(
&local_config, &local_storage, remote_proof,
0, 100, 1000, &[42]);
@@ -41,15 +41,14 @@ mod storage;
pub use self::storage::InMemoryStorage;
pub use self::changes_iterator::{key_changes, key_changes_proof, key_changes_proof_check};
use hashdb::{DBValue, Hasher};
use hash_db::Hasher;
use heapsize::HeapSizeOf;
use patricia_trie::NodeCodec;
use rlp::Encodable;
use backend::Backend;
use primitives;
use changes_trie::build::prepare_input;
use overlayed_changes::OverlayedChanges;
use trie_backend_essence::TrieBackendStorage;
use trie::{DBValue, trie_root};
/// Changes that are made outside of extrinsics are marked with this index;
pub const NO_EXTRINSIC_INDEX: u32 = 0xffffffff;
@@ -68,7 +67,7 @@ pub type Configuration = primitives::ChangesTrieConfiguration;
/// Compute the changes trie root and transaction for given block.
/// Returns None if there's no data to perform computation.
pub fn compute_changes_trie_root<'a, B: Backend<H, C>, S: Storage<H>, H: Hasher, C: NodeCodec<H>>(
pub fn compute_changes_trie_root<'a, B: Backend<H>, S: Storage<H>, H: Hasher>(
backend: &B,
storage: Option<&'a S>,
changes: &OverlayedChanges,
@@ -76,14 +75,14 @@ pub fn compute_changes_trie_root<'a, B: Backend<H, C>, S: Storage<H>, H: Hasher,
) -> Option<(H::Out, Vec<(Vec<u8>, Vec<u8>)>)>
where
&'a S: TrieBackendStorage<H>,
H::Out: Ord + Encodable + HeapSizeOf,
H::Out: Ord + HeapSizeOf,
{
let input_pairs = prepare_input::<B, S, H, C>(backend, storage, changes, block)
let input_pairs = prepare_input::<B, S, H>(backend, storage, changes, block)
.expect("storage is not allowed to fail within runtime")?;
let transaction = input_pairs.into_iter()
.map(Into::into)
.collect::<Vec<_>>();
let root = ::triehash::trie_root::<H, _, _, _>(transaction.iter().map(|(k, v)| (&*k, &*v)));
let root = trie_root::<H, _, _, _>(transaction.iter().map(|(k, v)| (&*k, &*v)));
Some((root, transaction))
}
@@ -17,9 +17,10 @@
//! Changes trie storage utilities.
use std::collections::HashMap;
use hashdb::{Hasher, HashDB, DBValue};
use hash_db::Hasher;
use trie::DBValue;
use heapsize::HeapSizeOf;
use memorydb::MemoryDB;
use trie::MemoryDB;
use parking_lot::RwLock;
use changes_trie::Storage;
use trie_backend_essence::TrieBackendStorage;
@@ -27,8 +28,6 @@ use trie_backend_essence::TrieBackendStorage;
#[cfg(test)]
use backend::insert_into_memory_db;
#[cfg(test)]
use patricia_trie::NodeCodec;
#[cfg(test)]
use changes_trie::input::InputPair;
/// In-memory implementation of changes trie storage.
@@ -64,11 +63,11 @@ impl<H: Hasher> InMemoryStorage<H> where H::Out: HeapSizeOf {
}
#[cfg(test)]
pub fn with_inputs<C: NodeCodec<H>>(inputs: Vec<(u64, Vec<InputPair>)>) -> Self {
pub fn with_inputs(inputs: Vec<(u64, Vec<InputPair>)>) -> Self {
let mut mdb = MemoryDB::default();
let mut roots = HashMap::new();
for (block, pairs) in inputs {
let root = insert_into_memory_db::<H, C, _>(&mut mdb, pairs.into_iter().map(Into::into));
let root = insert_into_memory_db::<H, _>(&mut mdb, pairs.into_iter().map(Into::into));
if let Some(root) = root {
roots.insert(block, root);
}
@@ -84,7 +83,7 @@ impl<H: Hasher> InMemoryStorage<H> where H::Out: HeapSizeOf {
#[cfg(test)]
pub fn clear_storage(&self) {
self.data.write().mdb = MemoryDB::new();
self.data.write().mdb = MemoryDB::default(); // use new to be more correct
}
/// Insert changes trie for given block.
@@ -101,7 +100,7 @@ impl<H: Hasher> Storage<H> for InMemoryStorage<H> where H::Out: HeapSizeOf {
}
fn get(&self, key: &H::Out) -> Result<Option<DBValue>, String> {
Ok(HashDB::<H>::get(&self.data.read().mdb, key))
MemoryDB::<H>::get(&self.data.read().mdb, key)
}
}