This repository was archived by the owner on Nov 15, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
trie: add ChildTrie trait to simplify future child trie types addition #3421
Closed
Closed
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
78c88bd
trie: add ChildTrie trait to make define additional child tries easier
sorpaas 23012c9
trie: add missing license header
sorpaas 2f038a9
typo: fix unnecessary tab
sorpaas 1e0386c
trie: fix no_std compile
sorpaas a8174b5
rpc: fix child trie test
sorpaas 0e4467e
Merge branch 'master' of github.com:paritytech/substrate into sp-chil…
sorpaas 49155a9
rpc: fix builder child storage key
sorpaas 2fc0eef
state-machine: support multi-type default child trie root
sorpaas eff2a9e
Fix useless docs
sorpaas 19ff1ba
Fix useless docs for ChildTrie
sorpaas 8795ccb
Fix useless docs for ChildTrie::default_root
sorpaas File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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}; | ||
|
|
||
| /// Child trie with the same configuration as the root 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()))?) | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,73 @@ | ||
| // 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}; | ||
|
|
||
| /// Trait for a child trie configuration struct to implement. One | ||
| /// child trie should have one corresponding `ChildTrie` definition. | ||
| /// | ||
| /// To use this, add the resulting struct type into `with_child_trie` | ||
| /// macro. | ||
| pub trait ChildTrie { | ||
| /// The hash value which should be returned for an empty child trie. | ||
| /// In current Substrate's child trie implementation, empty child trie | ||
| /// value are never written into the root storage. Instead, when this | ||
| /// value is encountered, it is removed from the root trie. | ||
| 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>; | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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. | ||
|
|
||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
I wonder if we may rename
ChildTriewith something a bit more generic, I would sayChildStatebut that is not really related to the following methods either, so here to meKeyValueStateis what makes the more sense.Similarily we can remove the 'child' info here: there is nothing related to the
childnature.Still If we add method or associated const such as
or