-
Notifications
You must be signed in to change notification settings - Fork 2.6k
trie: add ChildTrie trait to simplify future child trie types addition #3421
Changes from 8 commits
78c88bd
23012c9
2f038a9
1e0386c
a8174b5
0e4467e
49155a9
2fc0eef
eff2a9e
19ff1ba
8795ccb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,112 @@ | ||
| // Copyright 2015-2019 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/>. | ||
|
|
||
| //! Default child trie declaration. That is, the child trie that uses | ||
| //! the same structure of root trie. | ||
|
|
||
| use super::ChildTrie; | ||
| use crate::{TrieHash, TrieError, TrieDBMut, TrieDB}; | ||
| use rstd::boxed::Box; | ||
| use rstd::vec::Vec; | ||
| use trie_db::{TrieConfiguration, DBValue, Query, Trie, TrieMut}; | ||
| use hash_db::{HashDB, HashDBRef, PlainDB, PlainDBRef}; | ||
|
|
||
| /// Default child trie. | ||
| pub struct DefaultChildTrie; | ||
|
|
||
| impl ChildTrie for DefaultChildTrie { | ||
| fn default_root<L: TrieConfiguration>(&self) -> Vec<u8> { | ||
| L::trie_root::<_, Vec<u8>, Vec<u8>>(core::iter::empty()).as_ref().iter().cloned().collect() | ||
| } | ||
|
|
||
| fn root<L: TrieConfiguration, I, A, B>(&self, input: I) -> Vec<u8> where | ||
| I: IntoIterator<Item = (A, B)>, | ||
| A: AsRef<[u8]> + Ord, | ||
| B: AsRef<[u8]> | ||
| { | ||
| L::trie_root(input).as_ref().iter().cloned().collect() | ||
| } | ||
|
|
||
| fn delta_root<L: TrieConfiguration, I, A, B, DB>( | ||
| &self, db: &mut DB, root_vec: Vec<u8>, delta: I | ||
| ) -> Result<Vec<u8>, Box<TrieError<L>>> where | ||
| I: IntoIterator<Item = (A, Option<B>)>, | ||
| A: AsRef<[u8]> + Ord, | ||
| B: AsRef<[u8]>, | ||
| DB: HashDB<L::Hash, DBValue> + PlainDB<TrieHash<L>, 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_vec); | ||
|
|
||
| { | ||
| let mut trie = TrieDBMut::<L>::from_existing(&mut *db, &mut root)?; | ||
|
|
||
| for (key, change) in delta { | ||
| match change { | ||
| Some(val) => trie.insert(key.as_ref(), val.as_ref())?, | ||
| None => trie.remove(key.as_ref())?, | ||
| }; | ||
| } | ||
| } | ||
|
|
||
| Ok(root.as_ref().to_vec()) | ||
| } | ||
|
|
||
| fn for_keys<L: TrieConfiguration, F: FnMut(&[u8]), DB>( | ||
| &self, db: &DB, root_slice: &[u8], mut f: F | ||
| ) -> Result<(), Box<TrieError<L>>> where | ||
| DB: HashDBRef<L::Hash, DBValue> + PlainDBRef<TrieHash<L>, 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 trie = TrieDB::<L>::new(&*db, &root)?; | ||
| let iter = trie.iter()?; | ||
|
|
||
| for x in iter { | ||
| let (key, _) = x?; | ||
| f(&key); | ||
| } | ||
|
|
||
| Ok(()) | ||
| } | ||
|
|
||
| fn read_value<L: TrieConfiguration, DB>( | ||
| &self, db: &DB, root_slice: &[u8], key: &[u8] | ||
| ) -> Result<Option<Vec<u8>>, Box<TrieError<L>>> where | ||
| DB: HashDBRef<L::Hash, DBValue> + PlainDBRef<TrieHash<L>, 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); | ||
|
|
||
| Ok(TrieDB::<L>::new(&*db, &root)?.get(key).map(|x| x.map(|val| val.to_vec()))?) | ||
| } | ||
|
|
||
| fn read_value_with<L: TrieConfiguration, Q: Query<L::Hash, Item=DBValue>, DB>( | ||
| &self, db: &DB, root_slice: &[u8], key: &[u8], query: Q | ||
| ) -> Result<Option<Vec<u8>>, Box<TrieError<L>>> where | ||
| DB: HashDBRef<L::Hash, DBValue> + PlainDBRef<TrieHash<L>, 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); | ||
|
|
||
| Ok(TrieDB::<L>::new(&*db, &root)?.get_with(key, query).map(|x| x.map(|val| val.to_vec()))?) | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| // Copyright 2015-2019 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/>. | ||
|
|
||
| //! Child trie declarations. | ||
|
|
||
| mod default; | ||
|
|
||
| pub use self::default::DefaultChildTrie; | ||
|
|
||
| use crate::{TrieHash, TrieError}; | ||
| use rstd::boxed::Box; | ||
| use rstd::vec::Vec; | ||
| use trie_db::{TrieConfiguration, DBValue, Query}; | ||
| use hash_db::{HashDB, HashDBRef, PlainDB, PlainDBRef}; | ||
|
|
||
| /// Definition for a child trie. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Useless docs. |
||
| pub trait ChildTrie { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if we may rename or |
||
| /// Default root of the child trie. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Useless docs |
||
| fn default_root<L: TrieConfiguration>(&self) -> Vec<u8>; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. for all those function I would consider removing type parameter L, and pushing it into the struct instead of Actually this comes with some constraint (probably associated type for prover and and memorydb), so it may not be worth it at this point. |
||
|
|
||
| /// Given its ordered contents, closed form, calculate a child trie's root. | ||
| fn root<L: TrieConfiguration, I, A, B>(&self, input: I) -> Vec<u8> where | ||
| I: IntoIterator<Item = (A, B)>, | ||
| A: AsRef<[u8]> + Ord, | ||
| B: AsRef<[u8]>; | ||
|
|
||
| /// Given delta values, calculate the updated child trie root. | ||
| fn delta_root<L: TrieConfiguration, I, A, B, DB>( | ||
| &self, db: &mut DB, root_vec: Vec<u8>, delta: I | ||
| ) -> Result<Vec<u8>, Box<TrieError<L>>> where | ||
| I: IntoIterator<Item = (A, Option<B>)>, | ||
| A: AsRef<[u8]> + Ord, | ||
| B: AsRef<[u8]>, | ||
| DB: HashDB<L::Hash, DBValue> + PlainDB<TrieHash<L>, DBValue>; | ||
|
|
||
| /// Call `f` for all keys in a child trie. | ||
| fn for_keys<L: TrieConfiguration, F: FnMut(&[u8]), DB>( | ||
| &self, db: &DB, root_slice: &[u8], f: F | ||
| ) -> Result<(), Box<TrieError<L>>> where | ||
| DB: HashDBRef<L::Hash, DBValue> + PlainDBRef<TrieHash<L>, DBValue>; | ||
|
|
||
| /// Read a value from the child trie. | ||
| fn read_value<L: TrieConfiguration, DB>( | ||
| &self, db: &DB, root_slice: &[u8], key: &[u8] | ||
| ) -> Result<Option<Vec<u8>>, Box<TrieError<L>>> where | ||
| DB: HashDBRef<L::Hash, DBValue> + PlainDBRef<TrieHash<L>, DBValue>; | ||
|
|
||
| /// Read a value from the child trie with given query. | ||
| fn read_value_with<L: TrieConfiguration, Q: Query<L::Hash, Item=DBValue>, DB>( | ||
| &self, db: &DB, root_slice: &[u8], key: &[u8], query: Q | ||
| ) -> Result<Option<Vec<u8>>, Box<TrieError<L>>> where | ||
| DB: HashDBRef<L::Hash, DBValue> + PlainDBRef<TrieHash<L>, DBValue>; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22,10 +22,13 @@ mod error; | |
| mod node_header; | ||
| mod node_codec; | ||
| mod trie_stream; | ||
| mod child_tries; | ||
|
|
||
| use rstd::boxed::Box; | ||
| use rstd::vec::Vec; | ||
| use hash_db::Hasher; | ||
| use child_tries::{ChildTrie, DefaultChildTrie}; | ||
|
|
||
| /// Our `NodeCodec`-specific error. | ||
| pub use error::Error; | ||
| /// The Substrate format implementation of `TrieStream`. | ||
|
|
@@ -41,6 +44,26 @@ pub use memory_db::prefixed_key; | |
| /// Various re-exports from the `hash-db` crate. | ||
| pub use hash_db::{HashDB as HashDBT, EMPTY_PREFIX}; | ||
|
|
||
| macro_rules! with_child_trie { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note that it will be good to have a constant reference instead of b"child_storage:default:" and if we put a function/constant in ChildTrie trait to define its type prefix, it should also be use. |
||
| ( $ident:ident, $storage_key:expr, $expr:expr, $default:expr ) => { | ||
| if $storage_key.starts_with(b":child_storage:default:") { | ||
| #[allow(unused_variables)] | ||
| let $ident = DefaultChildTrie; | ||
| $expr | ||
| } else { | ||
| $default | ||
| } | ||
| }; | ||
|
|
||
| ( $ident:ident, $storage_key:expr, $expr:expr ) => { | ||
| with_child_trie!( | ||
| $ident, $storage_key, $expr, | ||
| panic!("all child trie operations starts by calling is_child_trie_key_valid; | ||
| is_child_trie_key checked that the key is always valid; | ||
| qed") | ||
| ) | ||
| }; | ||
| } | ||
|
|
||
| #[derive(Default)] | ||
| /// substrate trie layout | ||
|
|
@@ -173,7 +196,8 @@ pub fn read_trie_value_with< | |
| /// `child_trie_root` and `child_delta_trie_root` can panic if invalid value is provided to them. | ||
| pub fn is_child_trie_key_valid<L: TrieConfiguration>(storage_key: &[u8]) -> bool { | ||
| use primitives::storage::well_known_keys; | ||
| let has_right_prefix = storage_key.starts_with(b":child_storage:default:"); | ||
| let has_right_prefix = with_child_trie!(trie, storage_key, true, false); | ||
|
|
||
| if has_right_prefix { | ||
| // This is an attempt to catch a change of `is_child_storage_key`, which | ||
| // just checks if the key has prefix `:child_storage:` at the moment of writing. | ||
|
|
@@ -186,25 +210,25 @@ pub fn is_child_trie_key_valid<L: TrieConfiguration>(storage_key: &[u8]) -> bool | |
| } | ||
|
|
||
| /// Determine the default child trie root. | ||
| pub fn default_child_trie_root<L: TrieConfiguration>(_storage_key: &[u8]) -> Vec<u8> { | ||
| L::trie_root::<_, Vec<u8>, Vec<u8>>(core::iter::empty()).as_ref().iter().cloned().collect() | ||
| pub fn default_child_trie_root<L: TrieConfiguration>(storage_key: &[u8]) -> Vec<u8> { | ||
| with_child_trie!(trie, storage_key, trie.default_root::<L>()) | ||
| } | ||
|
|
||
| /// 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>(_storage_key: &[u8], input: I) -> Vec<u8> | ||
| pub fn child_trie_root<L: TrieConfiguration, I, A, B>(storage_key: &[u8], input: I) -> Vec<u8> | ||
| where | ||
| I: IntoIterator<Item = (A, B)>, | ||
| A: AsRef<[u8]> + Ord, | ||
| B: AsRef<[u8]>, | ||
| { | ||
| L::trie_root(input).as_ref().iter().cloned().collect() | ||
| with_child_trie!(trie, storage_key, trie.root::<L, _, _, _>(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>( | ||
| _storage_key: &[u8], | ||
| storage_key: &[u8], | ||
| db: &mut DB, | ||
| root_vec: Vec<u8>, | ||
| delta: I | ||
|
|
@@ -216,48 +240,21 @@ pub fn child_delta_trie_root<L: TrieConfiguration, I, A, B, DB>( | |
| DB: hash_db::HashDB<L::Hash, trie_db::DBValue> | ||
| + hash_db::PlainDB<TrieHash<L>, 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_vec); | ||
|
|
||
| { | ||
| let mut trie = TrieDBMut::<L>::from_existing(&mut *db, &mut root)?; | ||
|
|
||
| for (key, change) in delta { | ||
| match change { | ||
| Some(val) => trie.insert(key.as_ref(), val.as_ref())?, | ||
| None => trie.remove(key.as_ref())?, | ||
| }; | ||
| } | ||
| } | ||
|
|
||
| Ok(root.as_ref().to_vec()) | ||
| with_child_trie!(trie, storage_key, trie.delta_root::<L, _, _, _, _>(db, root_vec, delta)) | ||
| } | ||
|
|
||
| /// Call `f` for all keys in a child trie. | ||
| pub fn for_keys_in_child_trie<L: TrieConfiguration, F: FnMut(&[u8]), DB>( | ||
| _storage_key: &[u8], | ||
| storage_key: &[u8], | ||
| db: &DB, | ||
| root_slice: &[u8], | ||
| mut f: F | ||
| f: F | ||
| ) -> Result<(), Box<TrieError<L>>> | ||
| where | ||
| DB: hash_db::HashDBRef<L::Hash, trie_db::DBValue> | ||
| + hash_db::PlainDBRef<TrieHash<L>, 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 trie = TrieDB::<L>::new(&*db, &root)?; | ||
| let iter = trie.iter()?; | ||
|
|
||
| for x in iter { | ||
| let (key, _) = x?; | ||
| f(&key); | ||
| } | ||
|
|
||
| Ok(()) | ||
| with_child_trie!(trie, storage_key, trie.for_keys::<L, _, _>(db, root_slice, f)) | ||
| } | ||
|
|
||
| /// Record all keys for a given root. | ||
|
|
@@ -285,7 +282,7 @@ pub fn record_all_keys<L: TrieConfiguration, DB>( | |
|
|
||
| /// Read a value from the child trie. | ||
| pub fn read_child_trie_value<L: TrieConfiguration, DB>( | ||
| _storage_key: &[u8], | ||
| storage_key: &[u8], | ||
| db: &DB, | ||
| root_slice: &[u8], | ||
| key: &[u8] | ||
|
|
@@ -294,16 +291,12 @@ pub fn read_child_trie_value<L: TrieConfiguration, DB>( | |
| DB: hash_db::HashDBRef<L::Hash, trie_db::DBValue> | ||
| + hash_db::PlainDBRef<TrieHash<L>, 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); | ||
|
|
||
| Ok(TrieDB::<L>::new(&*db, &root)?.get(key).map(|x| x.map(|val| val.to_vec()))?) | ||
| with_child_trie!(trie, storage_key, trie.read_value::<L, _>(db, root_slice, key)) | ||
| } | ||
|
|
||
| /// 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>( | ||
| _storage_key: &[u8], | ||
| storage_key: &[u8], | ||
| db: &DB, | ||
| root_slice: &[u8], | ||
| key: &[u8], | ||
|
|
@@ -313,11 +306,7 @@ pub fn read_child_trie_value_with<L: TrieConfiguration, Q: Query<L::Hash, Item=D | |
| DB: hash_db::HashDBRef<L::Hash, trie_db::DBValue> | ||
| + hash_db::PlainDBRef<TrieHash<L>, 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); | ||
|
|
||
| Ok(TrieDB::<L>::new(&*db, &root)?.get_with(key, query).map(|x| x.map(|val| val.to_vec()))?) | ||
| with_child_trie!(trie, storage_key, trie.read_value_with::<L, _, _>(db, root_slice, key, query)) | ||
| } | ||
|
|
||
| /// Constants used into trie simplification codec. | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Useless docs.