-
Notifications
You must be signed in to change notification settings - Fork 2.7k
avoid key collision on child trie and proof on child trie #2209
Changes from 4 commits
f6c4bb2
878a7ae
a464936
4de73d7
be0e340
7f3a282
4d208db
6d7f355
25bcb4c
9eaef35
37d9536
67c03a2
99dbb5a
3be1802
fec73d0
20d168d
d797bd0
27096aa
3560acc
c3fc432
3e417eb
5af490f
5a8dddd
8f04f00
8051179
8eac118
a65f9f2
d495ef5
197d77a
2ea3c89
99c45ea
3553ab2
47984fe
2bf2d7b
bc7165c
21c3acf
98b2fc3
432cb10
e266dfe
7f64652
999a26e
7bbd681
87f03b7
f18e002
0eaeca0
b57319d
1f848d2
403df51
305b60a
423cfb1
a0ffa31
6e3bed7
7411146
331be51
bc2935c
5a87b6a
f40400b
9bc1ab7
c073b21
d15ca49
0736b96
9e0485d
586b50e
65d7485
95a69b2
d089693
ec69ae0
76ea14d
b2050c8
5a0cbe1
6e84810
7f5694f
5006d73
b85508e
0c14777
10c4f58
453927b
349f9a5
fc034fb
45cfbd6
4f9717e
7cb2d84
c821a08
c3ba830
acf9641
0050457
c36b91b
9d3d9e2
3db4da8
4348d70
134a4bf
5325621
97118e8
0ed7f80
8a6986a
7652de9
94c629e
7675740
90fba8a
d8c58c6
661ba2e
833e9ff
71bda12
08b3062
89f3cd5
834f52a
0445228
75add99
e70edab
b8a0cd4
1514171
a7da811
b96c523
865672d
b6d7705
b45344c
517f95c
79a07de
a027fb0
b92655c
8a875f4
fcd8bdb
3241fc4
ecac03b
10369b9
15a2967
acc94e4
7d96338
ba7bdcb
aacea85
6789641
2b37160
b1183bd
bc5653c
5ecfd19
baf89c8
0f8bff9
5a8576b
045ee32
58e6e41
70555c5
130e5e4
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 |
|---|---|---|
|
|
@@ -19,8 +19,6 @@ | |
| use parity_codec::{Encode, Decode, Compact}; | ||
| use rstd::prelude::*; | ||
| use rstd::ptr; | ||
| use rstd::ops::Sub; | ||
| use rstd::convert::{TryInto, TryFrom}; | ||
| use crate::storage::well_known_keys::CHILD_STORAGE_KEY_PREFIX; | ||
| #[cfg(feature = "std")] | ||
| pub use impl_serde::serialize as bytes; | ||
|
|
@@ -33,55 +31,23 @@ use hash_db::Hasher; | |
| /// this child trie. | ||
| /// The `KeySpace` of a child trie must be unique for the canonical chain | ||
| /// in order to avoid key collision at a key value database level. | ||
| /// Child trie variant (start with 1u8), is currently build by using the `ParentTrie` | ||
| /// path of the child trie at its creation and the block number at its creation | ||
| /// (only child trie in their creation block need to update this value when moved). | ||
| /// No keyspace variant is only 0u8. | ||
| /// This id is unique as for a block number state we cannot have | ||
| /// two created child trie with the same `ParentTrie`. | ||
| /// This id is unique is currently build from a simple counter in state. | ||
| pub type KeySpace = Vec<u8>; | ||
|
|
||
|
|
||
| #[cfg(not(feature = "legacy-trie"))] | ||
| /// Keyspace to use for the parent trie key. | ||
| /// Block number 0 and no path. | ||
| pub const NO_CHILD_KEYSPACE: [u8;2] = [0, 0]; | ||
| pub const NO_CHILD_KEYSPACE: [u8;1] = [0]; | ||
| #[cfg(feature = "legacy-trie")] | ||
| // Keyspace to use for the parent trie key. | ||
| const NO_CHILD_KEYSPACE: [u8;0] = []; | ||
|
|
||
|
|
||
| /// Generate a new keyspace for a child trie. | ||
| pub fn generate_keyspace<N>(block_nb: &N, parent_trie: &ParentTrie) -> Vec<u8> | ||
| where | ||
| N: TryInto<u128>, | ||
| N: Sub<N, Output = N>, | ||
| N: TryFrom<u128>, | ||
| N: Clone, | ||
| { | ||
| // using 12 for block number and additional encoding targeting ~u64 | ||
| let mut result = Vec::with_capacity(parent_trie.len() + 12); | ||
| parity_codec::Encode::encode_to(ChildTrie::parent_key_slice(parent_trie), &mut result); | ||
|
|
||
| let mut block_nb = block_nb.clone(); | ||
| // compact number of child. | ||
| result.push(1); | ||
| // Note that this algo only work if conversion failure are related to out of bound and | ||
| // implemented when possible. | ||
| loop { | ||
| if let Ok(v) = block_nb.clone().try_into() { | ||
| if v < u128::max_value() { | ||
| parity_codec::Encode::encode_to(&Compact(v), &mut result); | ||
| break; | ||
| } | ||
| } | ||
| parity_codec::Encode::encode_to(&Compact(u128::max_value()), &mut result); | ||
| if let Ok(max) = N::try_from(u128::max_value()) { | ||
| block_nb = block_nb - max; | ||
| } else { | ||
| unreachable!("Previously fail with bigger value; qed"); | ||
| } | ||
| } | ||
| pub fn generate_keyspace(child_counter: u128) -> Vec<u8> { | ||
| // using 8 for block number and additional encoding targeting ~u64 | ||
| let mut result = Vec::with_capacity(8); | ||
| parity_codec::Encode::encode_to(&Compact(child_counter), &mut result); | ||
| result | ||
| } | ||
|
|
||
|
|
@@ -227,23 +193,17 @@ impl ChildTrie { | |
| /// or for performance purpose (later write). | ||
| /// | ||
| /// We also provide an encodable value specific to the creation state (block number). | ||
| pub fn fetch_or_new<N>( | ||
| pub fn fetch_or_new( | ||
| parent_fetcher: impl FnOnce(&[u8]) -> Option<Self>, | ||
|
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. this is for incoming multiple level trie ? because otherwise the only function to put here is runtime_io::child_trie no ?
Contributor
Author
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. Not really, it is just to keep things generic (but yes it should be runtime_io::child_trie that is call for general srml module).
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. Then maybe I would put a new method in externalities to have fetch_or_new without this argument ?
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. if empty child trie can't exist then we can't do better than that I think |
||
| child_trie_update: impl FnOnce(ChildTrie), | ||
| parent: &[u8], | ||
| block_nb: &N, | ||
| ) -> Self | ||
| where | ||
| N: TryInto<u128>, | ||
| N: Sub<N, Output = N>, | ||
| N: TryFrom<u128>, | ||
| N: Clone, | ||
| { | ||
| child_trie_counter: u128, | ||
| ) -> Self { | ||
| parent_fetcher(parent) | ||
| .unwrap_or_else(|| { | ||
| let parent = Self::prefix_parent_key(parent); | ||
| let ct = ChildTrie { | ||
| keyspace: generate_keyspace(block_nb, &parent), | ||
| keyspace: generate_keyspace(child_trie_counter), | ||
| root: Default::default(), | ||
| parent, | ||
| extension: Default::default(), | ||
|
|
@@ -405,3 +365,11 @@ impl AsRef<ChildTrie> for ChildTrie { | |
| self | ||
| } | ||
| } | ||
|
|
||
| #[test] | ||
| fn encode_empty_prefix() { | ||
| let empt = generate_keyspace(0); | ||
|
|
||
| // this ensure root trie can be move to be a child trie | ||
| assert_eq!(&NO_CHILD_KEYSPACE[..], &empt[..]); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -288,8 +288,8 @@ mod tests { | |
|
|
||
| #[test] | ||
|
Contributor
Author
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 link |
||
| fn proof_recorded_and_checked_with_child() { | ||
| let child_trie1 = ChildTrie::fetch_or_new(|_| None, |_| (), b"sub1", &0u64); | ||
| let child_trie2 = ChildTrie::fetch_or_new(|_| None, |_| (), b"sub2", &0u64); | ||
| let child_trie1 = ChildTrie::fetch_or_new(|_| None, |_| (), b"sub1", 1u128); | ||
| let child_trie2 = ChildTrie::fetch_or_new(|_| None, |_| (), b"sub2", 2u128); | ||
| let contents = (0..64).map(|i| (None, vec![i], Some(vec![i]))) | ||
| .chain((28..65).map(|i| (Some(child_trie1.clone()), vec![i], Some(vec![i])))) | ||
| .chain((10..15).map(|i| (Some(child_trie2.clone()), vec![i], Some(vec![i])))) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -20,6 +20,7 @@ use crate::rstd::prelude::*; | |
| use crate::rstd::borrow::Borrow; | ||
| use substrate_primitives::child_trie::ChildTrie; | ||
| use substrate_primitives::child_trie::ChildTrieReadRef; | ||
| use substrate_primitives::storage::well_known_keys; | ||
| use codec::{Codec, Encode, Decode, KeyedVec, Input, EncodeAppend}; | ||
| use hashed::generator::{HashedStorage, StorageHasher}; | ||
| use unhashed::generator::UnhashedStorage; | ||
|
|
@@ -446,22 +447,23 @@ where | |
| /// Note that `storage_key` must be unique and strong (strong in the sense of being long enough to | ||
| /// avoid collision from a resistant hash function (which unique implies)). | ||
| pub mod child { | ||
| use sr_std::ops::Sub; | ||
| use sr_std::convert::{TryInto, TryFrom}; | ||
| use super::{runtime_io, Codec, Decode, Vec, IncrementalChildInput, ChildTrie, | ||
| ChildTrieReadRef}; | ||
| ChildTrieReadRef, well_known_keys}; | ||
|
|
||
| /// Method for fetching or initiating a new child trie. | ||
| pub fn fetch_or_new<N>( | ||
| pub fn next_keyspace() -> u128 { | ||
| let key = well_known_keys::CHILD_STORAGE_KEYSPACE_COUNTER; | ||
| // do start at 1 (0 is reserved for top trie) | ||
| let previous = super::unhashed::get(key).unwrap_or(0u128); | ||
| let new = previous + 1; | ||
| super::unhashed::put(key, &new); | ||
| new | ||
| } | ||
|
|
||
| /// Method for fetching or initiating a new child trie. | ||
| pub fn fetch_or_new( | ||
| parent: &[u8], | ||
| block_nb: &N, | ||
| ) -> ChildTrie | ||
| where | ||
| N: TryInto<u128>, | ||
| N: Sub<N, Output = N>, | ||
| N: TryFrom<u128>, | ||
| N: Clone, | ||
| { | ||
| ) -> ChildTrie { | ||
| ChildTrie::fetch_or_new( | ||
| |pk| { child_trie(pk) }, | ||
| |ct| { | ||
|
|
@@ -472,7 +474,7 @@ pub mod child { | |
| debug_assert!(updated); | ||
| }, | ||
| parent, | ||
| block_nb, | ||
| next_keyspace(), | ||
|
||
| ) | ||
| } | ||
|
|
||
|
|
||
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.
Just use
Encode::encodeto create the vector instead of creating it above.