From 2c06d30bcfca6295e4edf0c3644be0d76d8ede1e Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 12 Jul 2019 21:34:19 +0200 Subject: [PATCH 01/12] Initial implementation, some redundancy is awkward and there is some useless computation (but there is a pending pr for that). Next are tests. --- core/client/db/src/lib.rs | 4 + core/client/db/src/storage_cache.rs | 4 + core/client/src/light/backend.rs | 13 ++ core/state-machine/src/backend.rs | 14 +- core/state-machine/src/changes_trie/build.rs | 179 ++++++++++++++---- core/state-machine/src/changes_trie/input.rs | 39 ++++ core/state-machine/src/changes_trie/mod.rs | 21 +- core/state-machine/src/ext.rs | 20 +- core/state-machine/src/overlayed_changes.rs | 60 +++--- core/state-machine/src/proving_backend.rs | 6 +- core/state-machine/src/testing.rs | 10 +- core/state-machine/src/trie_backend.rs | 4 + .../state-machine/src/trie_backend_essence.rs | 33 ++++ 13 files changed, 329 insertions(+), 78 deletions(-) diff --git a/core/client/db/src/lib.rs b/core/client/db/src/lib.rs index 840a62dcd2db6..43b4d51d36767 100644 --- a/core/client/db/src/lib.rs +++ b/core/client/db/src/lib.rs @@ -140,6 +140,10 @@ impl StateBackend for RefTrackingState { self.state.for_keys_with_prefix(prefix, f) } + fn for_key_values_with_prefix(&self, prefix: &[u8], f: F) { + self.state.for_key_values_with_prefix(prefix, f) + } + fn for_keys_in_child_storage(&self, storage_key: &[u8], f: F) { self.state.for_keys_in_child_storage(storage_key, f) } diff --git a/core/client/db/src/storage_cache.rs b/core/client/db/src/storage_cache.rs index 53e4594856f20..5247f7fcf9a66 100644 --- a/core/client/db/src/storage_cache.rs +++ b/core/client/db/src/storage_cache.rs @@ -523,6 +523,10 @@ impl, B:Block> StateBackend for CachingState(&self, prefix: &[u8], f: F) { + self.state.for_key_values_with_prefix(prefix, f) + } + fn for_keys_in_child_storage(&self, storage_key: &[u8], f: F) { self.state.for_keys_in_child_storage(storage_key, f) } diff --git a/core/client/src/light/backend.rs b/core/client/src/light/backend.rs index 6f6bde2418373..a02e863408d94 100644 --- a/core/client/src/light/backend.rs +++ b/core/client/src/light/backend.rs @@ -377,6 +377,10 @@ where // whole state is not available on light node } + fn for_key_values_with_prefix(&self, _prefix: &[u8], _action: A) { + // whole state is not available on light node + } + fn for_keys_in_child_storage(&self, _storage_key: &[u8], _action: A) { // whole state is not available on light node } @@ -448,6 +452,15 @@ where } } + fn for_key_values_with_prefix(&self, prefix: &[u8], action: A) { + match *self { + OnDemandOrGenesisState::OnDemand(ref state) => + StateBackend::::for_key_values_with_prefix(state, prefix, action), + OnDemandOrGenesisState::Genesis(ref state) => state.for_key_values_with_prefix(prefix, action), + } + } + + fn for_keys_in_child_storage(&self, storage_key: &[u8], action: A) { match *self { OnDemandOrGenesisState::OnDemand(ref state) => diff --git a/core/state-machine/src/backend.rs b/core/state-machine/src/backend.rs index c86c802bfbd1b..1ebe012c068cf 100644 --- a/core/state-machine/src/backend.rs +++ b/core/state-machine/src/backend.rs @@ -71,7 +71,14 @@ pub trait Backend { /// Retrieve all entries keys of which start with the given prefix and /// call `f` for each of those keys. - fn for_keys_with_prefix(&self, prefix: &[u8], f: F); + fn for_keys_with_prefix(&self, prefix: &[u8], mut f: F) { + self.for_key_values_with_prefix(prefix, |k, _v| f(k)) + } + + /// Retrieve all entries keys and values of which start with the given prefix and + /// call `f` for each of those keys. + fn for_key_values_with_prefix(&self, prefix: &[u8], f: F); + /// Calculate the storage root, with given delta over what is already stored in /// the backend, and produce a "transaction" that can be used to commit. @@ -301,6 +308,11 @@ impl Backend for InMemory { self.inner.get(&None).map(|map| map.keys().filter(|key| key.starts_with(prefix)).map(|k| &**k).for_each(f)); } + fn for_key_values_with_prefix(&self, prefix: &[u8], mut f: F) { + self.inner.get(&None).map(|map| map.iter().filter(|(key, _val)| key.starts_with(prefix)) + .for_each(|(k, v)| f(k, v))); + } + fn for_keys_in_child_storage(&self, storage_key: &[u8], mut f: F) { self.inner.get(&Some(storage_key.to_vec())).map(|map| map.keys().for_each(|k| f(&k))); } diff --git a/core/state-machine/src/changes_trie/build.rs b/core/state-machine/src/changes_trie/build.rs index 96d2278cc2a76..66ab587f5dee0 100644 --- a/core/state-machine/src/changes_trie/build.rs +++ b/core/state-machine/src/changes_trie/build.rs @@ -24,7 +24,7 @@ use crate::backend::Backend; use crate::overlayed_changes::OverlayedChanges; use crate::trie_backend_essence::TrieBackendEssence; use crate::changes_trie::build_iterator::digest_build_iterator; -use crate::changes_trie::input::{InputKey, InputPair, DigestIndex, ExtrinsicIndex}; +use crate::changes_trie::input::{InputKey, InputPair, DigestIndex, ExtrinsicIndex, ChildIndex}; use crate::changes_trie::{AnchorBlockId, Configuration, Storage, BlockNumber}; /// Prepare input pairs for building a changes trie of given block. @@ -39,24 +39,36 @@ pub fn prepare_input<'a, B, S, H, Number>( config: &'a Configuration, changes: &OverlayedChanges, parent: &'a AnchorBlockId, -) -> Result>>, String> +) -> Result>, + Vec<(ChildIndex, Vec>)>, + )>, String> where B: Backend, S: Storage, H: Hasher, Number: BlockNumber, { - let mut input = Vec::new(); - input.extend(prepare_extrinsics_input( + let mut top = Vec::new(); + let mut children = Vec::new(); + let prepared = prepare_extrinsics_input( backend, parent.number.clone() + 1.into(), - changes)?); - input.extend(prepare_digest_input::<_, H, Number>( + changes)?; + top.extend(prepared.0); + // Note iterator use is odd, but there is an optimization pr for it. + children.extend(prepared.1.map(|(k,i)| (k, i.collect()))); + let prepared = prepare_digest_input::<_, H, Number>( parent, config, - storage)?); - - Ok(Some(input)) + storage)?; + top.extend(prepared.0); + children.extend(prepared.1.map(|(k,i)| (k, i.collect()))); + + Ok(Some(( + top, + children, + ))) } /// Prepare ExtrinsicIndex input pairs. @@ -64,7 +76,10 @@ fn prepare_extrinsics_input( backend: &B, block: Number, changes: &OverlayedChanges, -) -> Result>, String> +) -> Result<( + impl Iterator>, + impl Iterator, impl Iterator>)>, + ), String> where B: Backend, H: Hasher, @@ -89,11 +104,52 @@ fn prepare_extrinsics_input( .extend(extrinsics.iter().cloned()); } - Ok(extrinsic_map.into_iter() - .map(move |(key, extrinsics)| InputPair::ExtrinsicIndex(ExtrinsicIndex { - block: block.clone(), - key, - }, extrinsics.iter().cloned().collect()))) + let mut children_extrinsic_map = BTreeMap::, BTreeMap, BTreeSet>>::new(); + for (storage_key, _) in changes.prospective.children.iter() + .chain(changes.committed.children.iter()) { + children_extrinsic_map.insert(storage_key.clone(), Default::default()); + } + for (storage_key, extrinsic_map) in children_extrinsic_map.iter_mut() { + let prospective = changes.prospective.children.get(storage_key); + let committed = changes.committed.children.get(storage_key); + for (key, val) in prospective.iter().flat_map(|m| m.iter()) + .chain(committed.iter().flat_map(|m| m.iter())) { + let extrinsics = match val.extrinsics { + Some(ref extrinsics) => extrinsics, + None => continue, + }; + + // ignore values that have null value at the end of operation AND are not in storage + // at the beginning of operation + if !changes.storage(key).map(|v| v.is_some()).unwrap_or_default() { + if !backend.exists_storage(key).map_err(|e| format!("{}", e))? { + continue; + } + } + + extrinsic_map.entry(key.clone()).or_default() + .extend(extrinsics.iter().cloned()); + } + } + + + let block1 = block.clone(); + let block2 = block.clone(); + Ok(( + extrinsic_map.into_iter() + .map(move |(key, extrinsics)| InputPair::ExtrinsicIndex(ExtrinsicIndex { + block: block1.clone(), + key, + }, extrinsics.iter().cloned().collect())), + children_extrinsic_map.into_iter().map(move |(storage_key, extrinsic_map)| { + let block3 = block2.clone(); + (ChildIndex { block: block2.clone(), storage_key }, extrinsic_map.into_iter() + .map(move |(key, extrinsics)| InputPair::ExtrinsicIndex(ExtrinsicIndex { + block: block3.clone(), + key, + }, extrinsics.iter().cloned().collect()))) + }) + )) } /// Prepare DigestIndex input pairs. @@ -101,7 +157,10 @@ fn prepare_digest_input<'a, S, H, Number>( parent: &'a AnchorBlockId, config: &Configuration, storage: &'a S -) -> Result> + 'a, String> +) -> Result<( + impl Iterator> + 'a, + impl Iterator, impl Iterator> + 'a)> + 'a, + ), String> where S: Storage, H: Hasher, @@ -109,34 +168,82 @@ fn prepare_digest_input<'a, S, H, Number>( Number: BlockNumber, { let mut digest_map = BTreeMap::, BTreeSet>::new(); + let mut children_digest_map = BTreeMap::new(); for digest_build_block in digest_build_iterator(config, parent.number.clone() + One::one()) { + let extrinsic_prefix = ExtrinsicIndex::key_neutral_prefix(digest_build_block.clone()); + let digest_prefix = DigestIndex::key_neutral_prefix(digest_build_block.clone()); let trie_root = storage.root(parent, digest_build_block.clone())?; let trie_root = trie_root.ok_or_else(|| format!("No changes trie root for block {}", digest_build_block.clone()))?; - let trie_storage = TrieBackendEssence::<_, H>::new( - crate::changes_trie::TrieBackendStorageAdapter(storage), - trie_root, - ); + let children_roots = { + let trie_storage = TrieBackendEssence::<_, H>::new( + crate::changes_trie::TrieBackendStorageAdapter(storage), + trie_root, + ); - let extrinsic_prefix = ExtrinsicIndex::key_neutral_prefix(digest_build_block.clone()); - trie_storage.for_keys_with_prefix(&extrinsic_prefix, |key| - if let Some(InputKey::ExtrinsicIndex::(trie_key)) = Decode::decode(&mut &key[..]) { - digest_map.entry(trie_key.key).or_default() - .insert(digest_build_block.clone()); + let child_prefix = ChildIndex::key_neutral_prefix(digest_build_block.clone()); + let mut children_roots = BTreeMap::, _>::new(); + trie_storage.for_key_values_with_prefix(&child_prefix, |key, value| { + if let Some(InputKey::ChildIndex::(trie_key)) = Decode::decode(&mut &key[..]) { + let mut trie_root = ::Out::default(); + trie_root.as_mut().copy_from_slice(value); + children_roots.insert(trie_key.storage_key, trie_root); + } }); - let digest_prefix = DigestIndex::key_neutral_prefix(digest_build_block.clone()); - trie_storage.for_keys_with_prefix(&digest_prefix, |key| - if let Some(InputKey::DigestIndex::(trie_key)) = Decode::decode(&mut &key[..]) { - digest_map.entry(trie_key.key).or_default() - .insert(digest_build_block.clone()); - }); + + trie_storage.for_keys_with_prefix(&extrinsic_prefix, |key| + if let Some(InputKey::ExtrinsicIndex::(trie_key)) = Decode::decode(&mut &key[..]) { + digest_map.entry(trie_key.key).or_default() + .insert(digest_build_block.clone()); + }); + + trie_storage.for_keys_with_prefix(&digest_prefix, |key| + if let Some(InputKey::DigestIndex::(trie_key)) = Decode::decode(&mut &key[..]) { + digest_map.entry(trie_key.key).or_default() + .insert(digest_build_block.clone()); + }); + children_roots + }; + for (storage_key, trie_root) in children_roots.into_iter() { + let mut digest_map = BTreeMap::, BTreeSet>::new(); + let trie_storage = TrieBackendEssence::<_, H>::new( + crate::changes_trie::TrieBackendStorageAdapter(storage), + trie_root, + ); + + trie_storage.for_keys_with_prefix(&extrinsic_prefix, |key| + if let Some(InputKey::ExtrinsicIndex::(trie_key)) = Decode::decode(&mut &key[..]) { + digest_map.entry(trie_key.key).or_default() + .insert(digest_build_block.clone()); + }); + + trie_storage.for_keys_with_prefix(&digest_prefix, |key| + if let Some(InputKey::DigestIndex::(trie_key)) = Decode::decode(&mut &key[..]) { + digest_map.entry(trie_key.key).or_default() + .insert(digest_build_block.clone()); + }); + + let child_index = ChildIndex:: { + block: parent.number.clone() + One::one(), + storage_key, + }; + children_digest_map.insert(child_index, digest_map); + } } - Ok(digest_map.into_iter() - .map(move |(key, set)| InputPair::DigestIndex(DigestIndex { - block: parent.number.clone() + One::one(), - key - }, set.into_iter().collect()))) + Ok(( + digest_map.into_iter() + .map(move |(key, set)| InputPair::DigestIndex(DigestIndex { + block: parent.number.clone() + One::one(), + key + }, set.into_iter().collect())), + children_digest_map.into_iter().map(move |(storage_key, digest_map)| + (storage_key, digest_map.into_iter() + .map(move |(key, set)| InputPair::DigestIndex(DigestIndex { + block: parent.number.clone() + One::one(), + key + }, set.into_iter().collect())))) + )) } #[cfg(test)] diff --git a/core/state-machine/src/changes_trie/input.rs b/core/state-machine/src/changes_trie/input.rs index ae939c028b3e1..aca41db1acc87 100644 --- a/core/state-machine/src/changes_trie/input.rs +++ b/core/state-machine/src/changes_trie/input.rs @@ -40,9 +40,22 @@ pub struct DigestIndex { pub key: Vec, } +/// Key of { childtrie key => Childchange trie } mapping. +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct ChildIndex { + /// Block at which this key has been inserted in the trie. + pub block: Number, + /// Storage key this node is responsible for. + pub storage_key: Vec, +} + /// Value of { changed key => block/digest block numbers } mapping. pub type DigestIndexValue = Vec; +/// Value of { changed key => block/digest block numbers } mapping. +/// That is the root of the child change trie. +pub type ChildIndexValue = Vec; + /// Single input pair of changes trie. #[derive(Clone, Debug, PartialEq, Eq)] pub enum InputPair { @@ -50,6 +63,8 @@ pub enum InputPair { ExtrinsicIndex(ExtrinsicIndex, ExtrinsicIndexValue), /// Element of { key => set of blocks/digest blocks where key has been changed } element mapping. DigestIndex(DigestIndex, DigestIndexValue), + /// Element of { childtrie key => Childchange trie } where key has been changed } element mapping. + ChildIndex(ChildIndex, ChildIndexValue), } /// Single input key of changes trie. @@ -59,6 +74,8 @@ pub enum InputKey { ExtrinsicIndex(ExtrinsicIndex), /// Key of { key => set of blocks/digest blocks where key has been changed } element mapping. DigestIndex(DigestIndex), + /// Key of { childtrie key => Childchange trie } where key has been changed } element mapping. + ChildIndex(ChildIndex), } impl Into<(Vec, Vec)> for InputPair { @@ -66,6 +83,7 @@ impl Into<(Vec, Vec)> for InputPair { match self { InputPair::ExtrinsicIndex(key, value) => (key.encode(), value.encode()), InputPair::DigestIndex(key, value) => (key.encode(), value.encode()), + InputPair::ChildIndex(key, value) => (key.encode(), value.encode()), } } } @@ -75,6 +93,7 @@ impl Into> for InputPair { match self { InputPair::ExtrinsicIndex(key, _) => InputKey::ExtrinsicIndex(key), InputPair::DigestIndex(key, _) => InputKey::DigestIndex(key), + InputPair::ChildIndex(key, _) => InputKey::ChildIndex(key), } } } @@ -112,6 +131,22 @@ impl Encode for DigestIndex { } } +impl ChildIndex { + pub fn key_neutral_prefix(block: Number) -> Vec { + let mut prefix = vec![3]; + prefix.extend(block.encode()); + prefix + } +} + +impl Encode for ChildIndex { + fn encode_to(&self, dest: &mut W) { + dest.push_byte(3); + self.block.encode_to(dest); + self.storage_key.encode_to(dest); + } +} + impl Decode for InputKey { fn decode(input: &mut I) -> Option { match input.read_byte()? { @@ -123,6 +158,10 @@ impl Decode for InputKey { block: Decode::decode(input)?, key: Decode::decode(input)?, })), + 3 => Some(InputKey::ChildIndex(ChildIndex { + block: Decode::decode(input)?, + storage_key: Decode::decode(input)?, + })), _ => None, } } diff --git a/core/state-machine/src/changes_trie/mod.rs b/core/state-machine/src/changes_trie/mod.rs index 7dc95fb5a7ba7..7fc58c4ef56f0 100644 --- a/core/state-machine/src/changes_trie/mod.rs +++ b/core/state-machine/src/changes_trie/mod.rs @@ -32,8 +32,6 @@ //! the last N*digest_level-1 blocks (except for genesis block), mapping these keys //! to the set of lower-level digest blocks. //! -//! Changes trie only contains the top level storage changes. Sub-level changes -//! are propagated through its storage root on the top level storage. mod build; mod build_iterator; @@ -134,7 +132,7 @@ pub fn compute_changes_trie_root<'a, B: Backend, S: Storage, H: Ha storage: Option<&'a S>, changes: &OverlayedChanges, parent_hash: H::Out, -) -> Result, Vec)>)>, ()> +) -> Result, Vec)>, Vec<(Vec, Vec<(Vec, Vec)>)>)>, ()> where H::Out: Ord + 'static, { @@ -151,12 +149,21 @@ pub fn compute_changes_trie_root<'a, B: Backend, S: Storage, H: Ha .expect("storage is not allowed to fail within runtime"); match input_pairs { Some(input_pairs) => { - let transaction = input_pairs.into_iter() + let mut top_transaction = input_pairs.0.into_iter() .map(Into::into) .collect::>(); - let root = trie_root::(transaction.iter().map(|(k, v)| (&*k, &*v))); - - Ok(Some((root, transaction))) + let children_transaction = input_pairs.1.into_iter().map(|(storage_key, child_pairs)| { + let child_pairs = child_pairs.into_iter().map(Into::into).collect::>(); + let root = trie_root::(child_pairs.iter().map(|(k, v)| (&*k, &*v))); + let encoded_storage_key = storage_key.encode(); + let root_pair = input::InputPair::ChildIndex(storage_key, root.as_ref().to_vec()); + top_transaction.push(root_pair.into()); + (encoded_storage_key, child_pairs) + }).collect(); + + let root = trie_root::(top_transaction.iter().map(|(k, v)| (&*k, &*v))); + + Ok(Some((root, top_transaction, children_transaction))) }, None => Ok(None), } diff --git a/core/state-machine/src/ext.rs b/core/state-machine/src/ext.rs index d8619d1506d68..a3dfc2d263a23 100644 --- a/core/state-machine/src/ext.rs +++ b/core/state-machine/src/ext.rs @@ -276,10 +276,10 @@ where let child_delta_iter = child_storage_keys.map(|storage_key| (storage_key.clone(), self.overlay.committed.children.get(storage_key) .into_iter() - .flat_map(|map| map.1.iter().map(|(k, v)| (k.clone(), v.clone()))) + .flat_map(|map| map.iter().map(|(k, v)| (k.clone(), v.value.clone()))) .chain(self.overlay.prospective.children.get(storage_key) .into_iter() - .flat_map(|map| map.1.iter().map(|(k, v)| (k.clone(), v.clone())))))); + .flat_map(|map| map.iter().map(|(k, v)| (k.clone(), v.value.clone())))))); // compute and memoize @@ -304,10 +304,10 @@ where let delta = self.overlay.committed.children.get(storage_key) .into_iter() - .flat_map(|map| map.1.iter().map(|(k, v)| (k.clone(), v.clone()))) + .flat_map(|map| map.iter().map(|(k, v)| (k.clone(), v.value.clone()))) .chain(self.overlay.prospective.children.get(storage_key) .into_iter() - .flat_map(|map| map.1.clone().into_iter())); + .flat_map(|map| map.clone().into_iter().map(|(k, v)| (k.clone(), v.value.clone())))); let root = self.backend.child_storage_root(storage_key, delta).0; @@ -326,12 +326,20 @@ where self.overlay, parent_hash, )?; - let root_and_tx = root_and_tx.map(|(root, changes)| { + let root_and_tx = root_and_tx.map(|(root, top_changes, children_changes)| { let mut calculated_root = Default::default(); let mut mdb = MemoryDB::default(); + for (_storage_root, child_changes) in children_changes { + let mut calculated_root = Default::default(); // calculated previously (see PR for optim) + let mut trie = TrieDBMut::::new(&mut mdb, &mut calculated_root); + for (key, value) in child_changes { + trie.insert(&key, &value).expect(EXT_NOT_ALLOWED_TO_FAIL); + } + } + { let mut trie = TrieDBMut::::new(&mut mdb, &mut calculated_root); - for (key, value) in changes { + for (key, value) in top_changes { trie.insert(&key, &value).expect(EXT_NOT_ALLOWED_TO_FAIL); } } diff --git a/core/state-machine/src/overlayed_changes.rs b/core/state-machine/src/overlayed_changes.rs index 7d6d6081bd26b..8dbc42944ffd5 100644 --- a/core/state-machine/src/overlayed_changes.rs +++ b/core/state-machine/src/overlayed_changes.rs @@ -55,7 +55,7 @@ pub struct OverlayedChangeSet { /// Top level storage changes. pub top: HashMap, OverlayedValue>, /// Child storage changes. - pub children: HashMap, (Option>, HashMap, Option>>)>, + pub children: HashMap, HashMap, OverlayedValue>>, } #[cfg(test)] @@ -117,14 +117,14 @@ impl OverlayedChanges { /// value has been set. pub fn child_storage(&self, storage_key: &[u8], key: &[u8]) -> Option> { if let Some(map) = self.prospective.children.get(storage_key) { - if let Some(val) = map.1.get(key) { - return Some(val.as_ref().map(AsRef::as_ref)); + if let Some(val) = map.get(key) { + return Some(val.value.as_ref().map(AsRef::as_ref)); } } if let Some(map) = self.committed.children.get(storage_key) { - if let Some(val) = map.1.get(key) { - return Some(val.as_ref().map(AsRef::as_ref)); + if let Some(val) = map.get(key) { + return Some(val.value.as_ref().map(AsRef::as_ref)); } } @@ -151,10 +151,11 @@ impl OverlayedChanges { pub(crate) fn set_child_storage(&mut self, storage_key: Vec, key: Vec, val: Option>) { let extrinsic_index = self.extrinsic_index(); let map_entry = self.prospective.children.entry(storage_key).or_default(); - map_entry.1.insert(key, val); + let entry = map_entry.entry(key).or_default(); + entry.value = val; if let Some(extrinsic) = extrinsic_index { - map_entry.0.get_or_insert_with(Default::default) + entry.extrinsics.get_or_insert_with(Default::default) .insert(extrinsic); } } @@ -169,16 +170,27 @@ impl OverlayedChanges { let extrinsic_index = self.extrinsic_index(); let map_entry = self.prospective.children.entry(storage_key.to_vec()).or_default(); - if let Some(extrinsic) = extrinsic_index { - map_entry.0.get_or_insert_with(Default::default) - .insert(extrinsic); - } + map_entry.values_mut().for_each(|e| { + if let Some(extrinsic) = extrinsic_index { + e.extrinsics.get_or_insert_with(Default::default) + .insert(extrinsic); + } - map_entry.1.values_mut().for_each(|e| *e = None); + e.value = None; + }); - if let Some((_, committed_map)) = self.committed.children.get(storage_key) { + if let Some(committed_map) = self.committed.children.get(storage_key) { for (key, _) in committed_map.iter() { - map_entry.1.insert(key.clone(), None); + if !map_entry.contains_key(key) { + map_entry.insert(key.clone(), OverlayedValue { + value: None, + extrinsics: extrinsic_index.map(|i| { + let mut e = HashSet::default(); + e.insert(i); + e + }), + }); + } } } } @@ -239,13 +251,16 @@ impl OverlayedChanges { .extend(prospective_extrinsics); } } - for (storage_key, map) in self.prospective.children.drain() { - let entry = self.committed.children.entry(storage_key).or_default(); - entry.1.extend(map.1.iter().map(|(k, v)| (k.clone(), v.clone()))); - - if let Some(prospective_extrinsics) = map.0 { - entry.0.get_or_insert_with(Default::default) - .extend(prospective_extrinsics); + for (storage_key, mut map) in self.prospective.children.drain() { + let map_dest = self.committed.children.entry(storage_key).or_default(); + for (key, val) in map.drain() { + let entry = map_dest.entry(key).or_default(); + entry.value = val.value; + + if let Some(prospective_extrinsics) = val.extrinsics { + entry.extrinsics.get_or_insert_with(Default::default) + .extend(prospective_extrinsics); + } } } } @@ -261,7 +276,8 @@ impl OverlayedChanges { ){ assert!(self.prospective.is_empty()); (self.committed.top.into_iter().map(|(k, v)| (k, v.value)), - self.committed.children.into_iter().map(|(sk, v)| (sk, v.1.into_iter()))) + self.committed.children.into_iter() + .map(|(sk, v)| (sk, v.into_iter().map(|(k, v)| (k, v.value))))) } /// Inserts storage entry responsible for current extrinsic index. diff --git a/core/state-machine/src/proving_backend.rs b/core/state-machine/src/proving_backend.rs index 19f779e067b2c..6a1cc0d1eb570 100644 --- a/core/state-machine/src/proving_backend.rs +++ b/core/state-machine/src/proving_backend.rs @@ -158,6 +158,10 @@ impl<'a, S, H> Backend for ProvingBackend<'a, S, H> self.backend.for_keys_with_prefix(prefix, f) } + fn for_key_values_with_prefix(&self, prefix: &[u8], f: F) { + self.backend.for_key_values_with_prefix(prefix, f) + } + fn pairs(&self) -> Vec<(Vec, Vec)> { self.backend.pairs() } @@ -307,7 +311,7 @@ mod tests { let mut in_memory = in_memory.update(contents); let in_memory_root = in_memory.full_storage_root::<_, Vec<_>, _>( ::std::iter::empty(), - in_memory.child_storage_keys().map(|k|(k.to_vec(), Vec::new())) + in_memory.child_storage_keys().map(|k|(k.to_vec(), Vec::new())) ).0; (0..64).for_each(|i| assert_eq!( in_memory.storage(&[i]).unwrap().unwrap(), diff --git a/core/state-machine/src/testing.rs b/core/state-machine/src/testing.rs index 4a0e221809004..1fe98b2ff70e3 100644 --- a/core/state-machine/src/testing.rs +++ b/core/state-machine/src/testing.rs @@ -111,8 +111,8 @@ impl TestExternalities { let children = self.overlay.committed.children.clone().into_iter() .chain(self.overlay.prospective.children.clone().into_iter()) .flat_map(|(keyspace, map)| { - map.1.into_iter() - .map(|(k, v)| (Some(keyspace.clone()), k, v)) + map.into_iter() + .map(|(k, v)| (Some(keyspace.clone()), k, v.value)) .collect::>() }); @@ -238,10 +238,10 @@ impl Externalities for TestExternalities let (root, _, _) = { let delta = self.overlay.committed.children.get(storage_key) .into_iter() - .flat_map(|map| map.1.iter().map(|(k, v)| (k.clone(), v.clone()))) + .flat_map(|map| map.clone().into_iter().map(|(k, v)| (k, v.value))) .chain(self.overlay.prospective.children.get(storage_key) .into_iter() - .flat_map(|map| map.1.clone().into_iter())); + .flat_map(|map| map.clone().into_iter().map(|(k, v)| (k, v.value)))); self.backend.child_storage_root(storage_key, delta) }; @@ -255,7 +255,7 @@ impl Externalities for TestExternalities Some(&self.changes_trie_storage), &self.overlay, parent, - )?.map(|(root, _)| root.clone())) + )?.map(|(root, _, _)| root.clone())) } fn offchain(&mut self) -> Option<&mut dyn offchain::Externalities> { diff --git a/core/state-machine/src/trie_backend.rs b/core/state-machine/src/trie_backend.rs index 0c57cf3682fba..7139d1f507c71 100644 --- a/core/state-machine/src/trie_backend.rs +++ b/core/state-machine/src/trie_backend.rs @@ -77,6 +77,10 @@ impl, H: Hasher> Backend for TrieBackend where self.essence.for_keys_with_prefix(prefix, f) } + fn for_key_values_with_prefix(&self, prefix: &[u8], f: F) { + self.essence.for_key_values_with_prefix(prefix, f) + } + fn for_keys_in_child_storage(&self, storage_key: &[u8], f: F) { self.essence.for_keys_in_child_storage(storage_key, f) } diff --git a/core/state-machine/src/trie_backend_essence.rs b/core/state-machine/src/trie_backend_essence.rs index cad150d1bc1f6..f290084903f19 100644 --- a/core/state-machine/src/trie_backend_essence.rs +++ b/core/state-machine/src/trie_backend_essence.rs @@ -143,6 +143,39 @@ impl, H: Hasher> TrieBackendEssence { debug!(target: "trie", "Error while iterating by prefix: {}", e); } } + + /// Execute given closure for all key and values starting with prefix. + pub fn for_key_values_with_prefix(&self, prefix: &[u8], mut f: F) { + let mut read_overlay = S::Overlay::default(); + let eph = Ephemeral { + storage: &self.storage, + overlay: &mut read_overlay, + }; + + let mut iter = move || -> Result<(), Box>> { + let trie = TrieDB::::new(&eph, &self.root)?; + let mut iter = trie.iter()?; + + iter.seek(prefix)?; + + for x in iter { + let (key, value) = x?; + + if !key.starts_with(prefix) { + break; + } + + f(&key, &value); + } + + Ok(()) + }; + + if let Err(e) = iter() { + debug!(target: "trie", "Error while iterating by prefix: {}", e); + } + } + } pub(crate) struct Ephemeral<'a, S: 'a + TrieBackendStorage, H: 'a + Hasher> { From c93875d14be145192f58cddff9d204f54dbef599 Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 15 Jul 2019 13:08:36 +0200 Subject: [PATCH 02/12] Minimal tests and fix extend child. --- core/state-machine/src/changes_trie/build.rs | 139 +++++++++++++++--- .../src/changes_trie/changes_iterator.rs | 4 +- core/state-machine/src/changes_trie/prune.rs | 3 + .../state-machine/src/changes_trie/storage.rs | 28 +++- 4 files changed, 149 insertions(+), 25 deletions(-) diff --git a/core/state-machine/src/changes_trie/build.rs b/core/state-machine/src/changes_trie/build.rs index 66ab587f5dee0..63546b30c8457 100644 --- a/core/state-machine/src/changes_trie/build.rs +++ b/core/state-machine/src/changes_trie/build.rs @@ -50,7 +50,7 @@ pub fn prepare_input<'a, B, S, H, Number>( Number: BlockNumber, { let mut top = Vec::new(); - let mut children = Vec::new(); + let mut children = Vec::<(ChildIndex, Vec>)>::new(); let prepared = prepare_extrinsics_input( backend, parent.number.clone() + 1.into(), @@ -63,7 +63,14 @@ pub fn prepare_input<'a, B, S, H, Number>( config, storage)?; top.extend(prepared.0); - children.extend(prepared.1.map(|(k,i)| (k, i.collect()))); + // TODO EMCH should zip with extrinsics (let's wait optim merge). + for (storage_key, child) in prepared.1 { + if let Some(ix) = children.iter().position(|v| v.0 == storage_key) { + children[ix].1.extend(child); + } else { + children.push((storage_key, child.collect())); + } + } Ok(Some(( top, @@ -184,9 +191,11 @@ fn prepare_digest_input<'a, S, H, Number>( let mut children_roots = BTreeMap::, _>::new(); trie_storage.for_key_values_with_prefix(&child_prefix, |key, value| { if let Some(InputKey::ChildIndex::(trie_key)) = Decode::decode(&mut &key[..]) { - let mut trie_root = ::Out::default(); - trie_root.as_mut().copy_from_slice(value); - children_roots.insert(trie_key.storage_key, trie_root); + if let Some(value) = >::decode(&mut &value[..]) { + let mut trie_root = ::Out::default(); + trie_root.as_mut().copy_from_slice(&value[..]); + children_roots.insert(trie_key.storage_key, trie_root); + } } }); @@ -222,7 +231,7 @@ fn prepare_digest_input<'a, S, H, Number>( digest_map.entry(trie_key.key).or_default() .insert(digest_build_block.clone()); }); - + let child_index = ChildIndex:: { block: parent.number.clone() + One::one(), storage_key, @@ -250,12 +259,17 @@ fn prepare_digest_input<'a, S, H, Number>( mod test { use parity_codec::Encode; use primitives::Blake2Hasher; - use primitives::storage::well_known_keys::EXTRINSIC_INDEX; + use primitives::storage::well_known_keys::{EXTRINSIC_INDEX, CHILD_STORAGE_KEY_PREFIX}; use crate::backend::InMemory; use crate::changes_trie::storage::InMemoryStorage; - use crate::overlayed_changes::OverlayedValue; + use crate::overlayed_changes::{OverlayedValue, OverlayedChangeSet}; use super::*; + fn child_trie_key(k: &[u8]) -> Vec { + let mut res = CHILD_STORAGE_KEY_PREFIX.to_vec(); + res.extend_from_slice(k); + res + } fn prepare_for_build() -> (InMemory, InMemoryStorage, OverlayedChanges) { let backend: InMemory<_> = vec![ (vec![100], vec![255]), @@ -265,6 +279,8 @@ mod test { (vec![104], vec![255]), (vec![105], vec![255]), ].into_iter().collect::<::std::collections::HashMap<_, _>>().into(); + let child_trie_key1 = child_trie_key(b"1"); + let child_trie_key2 = child_trie_key(b"2"); let storage = InMemoryStorage::with_inputs(vec![ (1, vec![ InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 1, key: vec![100] }, vec![1, 3]), @@ -298,9 +314,24 @@ mod test { ]), (9, Vec::new()), (10, Vec::new()), (11, Vec::new()), (12, Vec::new()), (13, Vec::new()), (14, Vec::new()), (15, Vec::new()), + ], vec![(child_trie_key1.clone(), 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]), + InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 1, key: vec![105] }, vec![0, 2, 4]), + ]), + (2, vec![ + InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 2, key: vec![102] }, vec![0]), + ]), + (4, vec![ + InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 2, key: vec![102] }, vec![0, 3]), + + InputPair::DigestIndex(DigestIndex { block: 4, key: vec![102] }, vec![2]), + ]), + ]), ]); let changes = OverlayedChanges { - prospective: vec![ + prospective: OverlayedChangeSet { top: vec![ (vec![100], OverlayedValue { value: Some(vec![200]), extrinsics: Some(vec![0, 2].into_iter().collect()) @@ -310,7 +341,22 @@ mod test { extrinsics: Some(vec![0, 1].into_iter().collect()) }), ].into_iter().collect(), - committed: vec![ + children: vec![ + (child_trie_key1.clone(), vec![ + (vec![100], OverlayedValue { + value: Some(vec![200]), + extrinsics: Some(vec![0, 2].into_iter().collect()) + }) + ].into_iter().collect()), + (child_trie_key2, vec![ + (vec![100], OverlayedValue { + value: Some(vec![200]), + extrinsics: Some(vec![0, 2].into_iter().collect()) + }) + ].into_iter().collect()), + ].into_iter().collect() + }, + committed: OverlayedChangeSet { top: vec![ (EXTRINSIC_INDEX.to_vec(), OverlayedValue { value: Some(3u32.encode()), extrinsics: None, @@ -324,6 +370,15 @@ mod test { extrinsics: Some(vec![1].into_iter().collect()) }), ].into_iter().collect(), + children: vec![ + (child_trie_key1, vec![ + (vec![100], OverlayedValue { + value: Some(vec![202]), + extrinsics: Some(vec![3].into_iter().collect()) + }) + ].into_iter().collect()), + ].into_iter().collect(), + }, changes_trie_config: Some(Configuration { digest_interval: 4, digest_levels: 2 }), }; @@ -341,11 +396,20 @@ mod test { &changes, &AnchorBlockId { hash: Default::default(), number: 4 }, ).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]), - InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 5, key: vec![103] }, vec![0, 1]), - ])); + 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]), + InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 5, key: vec![103] }, vec![0, 1]), + ], vec![ + (ChildIndex { block: 5, storage_key: child_trie_key(&b"1"[..]) }, + vec![ + InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 5, key: vec![100] }, vec![0, 2, 3]), + ]), + (ChildIndex { block: 5, storage_key: child_trie_key(&b"2"[..]) }, + vec![ + InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 5, key: vec![100] }, vec![0, 2]), + ]), + ]))); } #[test] @@ -359,7 +423,7 @@ mod test { &changes, &AnchorBlockId { hash: Default::default(), number: 3 }, ).unwrap(); - assert_eq!(changes_trie_nodes, Some(vec![ + 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]), InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 4, key: vec![103] }, vec![0, 1]), @@ -368,7 +432,18 @@ mod test { InputPair::DigestIndex(DigestIndex { block: 4, key: vec![101] }, vec![1]), InputPair::DigestIndex(DigestIndex { block: 4, key: vec![102] }, vec![2]), InputPair::DigestIndex(DigestIndex { block: 4, key: vec![105] }, vec![1, 3]), - ])); + ], vec![ + (ChildIndex { block: 4, storage_key: child_trie_key(&b"1"[..]) }, + vec![ + InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 4, key: vec![100] }, vec![0, 2, 3]), + + InputPair::DigestIndex(DigestIndex { block: 4, key: vec![102] }, vec![2]), + ]), + (ChildIndex { block: 4, storage_key: child_trie_key(&b"2"[..]) }, + vec![ + InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 4, key: vec![100] }, vec![0, 2]), + ]), + ]))); } #[test] @@ -382,7 +457,7 @@ mod test { &changes, &AnchorBlockId { hash: Default::default(), number: 15 }, ).unwrap(); - assert_eq!(changes_trie_nodes, Some(vec![ + 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]), InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 16, key: vec![103] }, vec![0, 1]), @@ -392,7 +467,18 @@ mod test { InputPair::DigestIndex(DigestIndex { block: 16, key: vec![102] }, vec![4]), InputPair::DigestIndex(DigestIndex { block: 16, key: vec![103] }, vec![4]), InputPair::DigestIndex(DigestIndex { block: 16, key: vec![105] }, vec![4, 8]), - ])); + ], vec![ + (ChildIndex { block: 16, storage_key: child_trie_key(&b"1"[..]) }, + vec![ + InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 16, key: vec![100] }, vec![0, 2, 3]), + + InputPair::DigestIndex(DigestIndex { block: 16, key: vec![102] }, vec![4]), + ]), + (ChildIndex { block: 16, storage_key: child_trie_key(&b"2"[..]) }, + vec![ + InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 16, key: vec![100] }, vec![0, 2]), + ]), + ]))); } #[test] @@ -413,7 +499,7 @@ mod test { &changes, &AnchorBlockId { hash: Default::default(), number: 3 }, ).unwrap(); - assert_eq!(changes_trie_nodes, Some(vec![ + 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]), InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 4, key: vec![103] }, vec![0, 1]), @@ -422,6 +508,17 @@ mod test { InputPair::DigestIndex(DigestIndex { block: 4, key: vec![101] }, vec![1]), InputPair::DigestIndex(DigestIndex { block: 4, key: vec![102] }, vec![2]), InputPair::DigestIndex(DigestIndex { block: 4, key: vec![105] }, vec![1, 3]), - ])); + ], vec![ + (ChildIndex { block: 4, storage_key: child_trie_key(&b"1"[..]) }, + vec![ + InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 4, key: vec![100] }, vec![0, 2, 3]), + + InputPair::DigestIndex(DigestIndex { block: 4, key: vec![102] }, vec![2]), + ]), + (ChildIndex { block: 4, storage_key: child_trie_key(&b"2"[..]) }, + vec![ + InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 4, key: vec![100] }, vec![0, 2]), + ]), + ]))); } } diff --git a/core/state-machine/src/changes_trie/changes_iterator.rs b/core/state-machine/src/changes_trie/changes_iterator.rs index ee4b9cc19b8f0..a42732c4dfca3 100644 --- a/core/state-machine/src/changes_trie/changes_iterator.rs +++ b/core/state-machine/src/changes_trie/changes_iterator.rs @@ -29,6 +29,8 @@ use crate::changes_trie::storage::{TrieBackendAdapter, InMemoryStorage}; use crate::proving_backend::ProvingBackendEssence; use crate::trie_backend_essence::{TrieBackendEssence}; +// TODO EMCH adapt iterator for children -> at a given storage_key + /// Return changes of given key at given blocks range. /// `max` is the number of best known block. /// Changes are returned in descending order (i.e. last block comes first). @@ -482,7 +484,7 @@ mod tests { (16, vec![ InputPair::DigestIndex(DigestIndex { block: 16, key: vec![42] }, vec![4, 8]), ]), - ]); + ], vec![]); (config, backend) } diff --git a/core/state-machine/src/changes_trie/prune.rs b/core/state-machine/src/changes_trie/prune.rs index 08e7c02b8c73f..7b8c9869e0f17 100644 --- a/core/state-machine/src/changes_trie/prune.rs +++ b/core/state-machine/src/changes_trie/prune.rs @@ -54,6 +54,9 @@ pub fn prune, H: Hasher, Number: BlockNumber, F: FnMut(H:: current_block: &AnchorBlockId, mut remove_trie_node: F, ) { + +// TODO EMCH need iter prune children!!! + // select range for pruning let (first, last) = match pruning_range(config, min_blocks_to_keep, current_block.number.clone()) { Some((first, last)) => (first, last), diff --git a/core/state-machine/src/changes_trie/storage.rs b/core/state-machine/src/changes_trie/storage.rs index 8da205251532c..17efa40712321 100644 --- a/core/state-machine/src/changes_trie/storage.rs +++ b/core/state-machine/src/changes_trie/storage.rs @@ -29,7 +29,7 @@ use std::collections::HashSet; #[cfg(test)] use crate::backend::insert_into_memory_db; #[cfg(test)] -use crate::changes_trie::input::InputPair; +use crate::changes_trie::input::{InputPair, ChildIndex}; /// In-memory implementation of changes trie storage. pub struct InMemoryStorage { @@ -74,10 +74,32 @@ impl InMemoryStorage { } #[cfg(test)] - pub fn with_inputs(inputs: Vec<(Number, Vec>)>) -> Self { + pub fn with_inputs( + mut top_inputs: Vec<(Number, Vec>)>, + children_inputs: Vec<(Vec, Vec<(Number, Vec>)>)>, + ) -> Self { let mut mdb = MemoryDB::default(); let mut roots = BTreeMap::new(); - for (block, pairs) in inputs { + for (storage_key, child_input) in children_inputs { + for (block, pairs) in child_input { + let root = insert_into_memory_db::(&mut mdb, pairs.into_iter().map(Into::into)); + + if let Some(root) = root { + let ix = if let Some(ix) = top_inputs.iter().position(|v| v.0 == block) { + ix + } else { + top_inputs.push((block.clone(), Default::default())); + top_inputs.len() - 1 + }; + top_inputs[ix].1.push(InputPair::ChildIndex( + ChildIndex { block: block.clone(), storage_key: storage_key.clone() }, + root.as_ref().to_vec(), + )); + } + } + } + + for (block, pairs) in top_inputs { let root = insert_into_memory_db::(&mut mdb, pairs.into_iter().map(Into::into)); if let Some(root) = root { roots.insert(block, root); From a5483cb6fc065f47cc0d9344acb201482ce2cba2 Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 15 Jul 2019 16:42:36 +0200 Subject: [PATCH 03/12] implement iterator for change child trie. --- core/client/src/client.rs | 10 +- core/client/src/light/fetcher.rs | 3 + core/network/src/chain.rs | 6 +- core/network/src/protocol.rs | 20 +++- core/network/src/protocol/message.rs | 2 + core/network/src/protocol/on_demand.rs | 6 +- core/rpc/src/state/mod.rs | 2 +- core/state-machine/src/changes_trie/build.rs | 30 +++-- .../src/changes_trie/changes_iterator.rs | 106 +++++++++++++++--- 9 files changed, 141 insertions(+), 44 deletions(-) diff --git a/core/client/src/client.rs b/core/client/src/client.rs index f2802e75fcf53..21e340be3acaf 100644 --- a/core/client/src/client.rs +++ b/core/client/src/client.rs @@ -531,6 +531,7 @@ impl Client where &self, first: NumberFor, last: BlockId, + storage_key: Option<&StorageKey>, key: &StorageKey ) -> error::Result, u32)>> { let (config, storage) = self.require_changes_trie()?; @@ -546,6 +547,7 @@ impl Client where number: last_number, }, self.backend.blockchain().info().best_number, + storage_key.as_ref().map(|sk| sk.0.as_slice()), &key.0) .and_then(|r| r.map(|r| r.map(|(block, tx)| (block, tx))).collect::>()) .map_err(|err| error::Error::ChangesTrieAccessFailed(err)) @@ -563,13 +565,15 @@ impl Client where last: Block::Hash, min: Block::Hash, max: Block::Hash, - key: &StorageKey + storage_key: Option<&StorageKey>, + key: &StorageKey, ) -> error::Result> { self.key_changes_proof_with_cht_size( first, last, min, max, + storage_key, key, cht::size(), ) @@ -582,6 +586,7 @@ impl Client where last: Block::Hash, min: Block::Hash, max: Block::Hash, + storage_key: Option<&StorageKey>, key: &StorageKey, cht_size: NumberFor, ) -> error::Result> { @@ -648,7 +653,8 @@ impl Client where number: last_number, }, max_number, - &key.0 + storage_key.as_ref().map(|sk| sk.0.as_slice()), + &key.0, ) .map_err(|err| error::Error::from(error::Error::ChangesTrieAccessFailed(err)))?; diff --git a/core/client/src/light/fetcher.rs b/core/client/src/light/fetcher.rs index dd718001cd265..1768b59aada29 100644 --- a/core/client/src/light/fetcher.rs +++ b/core/client/src/light/fetcher.rs @@ -107,6 +107,8 @@ pub struct RemoteChangesRequest { /// Known changes trie roots for the range of blocks [tries_roots.0..max_block]. /// Proofs for roots of ascendants of tries_roots.0 are provided by the remote node. pub tries_roots: (Header::Number, Header::Hash, Vec), + /// Optional Child Storage key to read. + pub storage_key: Option>, /// Storage key to read. pub key: Vec, /// Number of times to retry request. None means that default RETRY_COUNT is used. @@ -297,6 +299,7 @@ impl, F> LightDataChecker: Send + Sync { last: Block::Hash, min: Block::Hash, max: Block::Hash, + storage_key: Option<&StorageKey>, key: &StorageKey ) -> Result, Error>; @@ -123,9 +124,10 @@ impl Client for SubstrateClient where last: Block::Hash, min: Block::Hash, max: Block::Hash, - key: &StorageKey + storage_key: Option<&StorageKey>, + key: &StorageKey, ) -> Result, Error> { - (self as &SubstrateClient).key_changes_proof(first, last, min, max, key) + (self as &SubstrateClient).key_changes_proof(first, last, min, max, storage_key, key) } fn is_descendent_of(&self, base: &Block::Hash, block: &Block::Hash) -> Result { diff --git a/core/network/src/protocol.rs b/core/network/src/protocol.rs index c0f40d43326ad..2ffd98351b987 100644 --- a/core/network/src/protocol.rs +++ b/core/network/src/protocol.rs @@ -228,7 +228,8 @@ impl<'a, B: BlockT> OnDemandNetwork for OnDemandIn<'a, B> { last: ::Hash, min: ::Hash, max: ::Hash, - key: Vec + storage_key: Option>, + key: Vec, ) { let message = message::generic::Message::RemoteChangesRequest(message::RemoteChangesRequest { id, @@ -236,6 +237,7 @@ impl<'a, B: BlockT> OnDemandNetwork for OnDemandIn<'a, B> { last, min, max, + storage_key, key, }); @@ -1379,24 +1381,34 @@ impl, H: ExHashT> Protocol { trace!(target: "sync", "Remote changes proof request {} from {} for key {} ({}..{})", request.id, who, - request.key.to_hex::(), + if let Some(sk) = request.storage_key.as_ref() { + format!("{} : {}", sk.to_hex::(), request.key.to_hex::()) + } else { + request.key.to_hex::() + }, request.first, request.last ); + let storage_key = request.storage_key.map(|sk| StorageKey(sk)); let key = StorageKey(request.key); let proof = match self.context_data.chain.key_changes_proof( request.first, request.last, request.min, request.max, - &key + storage_key.as_ref(), + &key, ) { Ok(proof) => proof, Err(error) => { trace!(target: "sync", "Remote changes proof request {} from {} for key {} ({}..{}) failed with: {}", request.id, who, - key.0.to_hex::(), + if let Some(sk) = storage_key { + format!("{} : {}", sk.0.to_hex::(), key.0.to_hex::()) + } else { + key.0.to_hex::() + }, request.first, request.last, error diff --git a/core/network/src/protocol/message.rs b/core/network/src/protocol/message.rs index 7b9b684cd8bfe..156a2e4520717 100644 --- a/core/network/src/protocol/message.rs +++ b/core/network/src/protocol/message.rs @@ -345,6 +345,8 @@ pub mod generic { pub min: H, /// Hash of the last block that we can use when querying changes. pub max: H, + /// Storage child node key which changes are requested. + pub storage_key: Option>, /// Storage key which changes are requested. pub key: Vec, } diff --git a/core/network/src/protocol/on_demand.rs b/core/network/src/protocol/on_demand.rs index 76c926df107c8..3dd31b9b1b897 100644 --- a/core/network/src/protocol/on_demand.rs +++ b/core/network/src/protocol/on_demand.rs @@ -81,7 +81,8 @@ pub trait OnDemandNetwork { last: ::Hash, min: ::Hash, max: ::Hash, - key: Vec + storage_key: Option>, + key: Vec, ); /// Send to `who` a body request. @@ -601,6 +602,7 @@ impl Request { data.last_block.1.clone(), data.tries_roots.1.clone(), data.max_block.1.clone(), + data.storage_key.clone(), data.key.clone(), ), RequestData::RemoteBody(ref data, _) => @@ -757,7 +759,7 @@ pub mod tests { _: Vec) {} fn send_call_request(&mut self, _: &PeerId, _: RequestId, _: ::Hash, _: String, _: Vec) {} fn send_changes_request(&mut self, _: &PeerId, _: RequestId, _: ::Hash, _: ::Hash, - _: ::Hash, _: ::Hash, _: Vec) {} + _: ::Hash, _: ::Hash, _: Option>, _: Vec) {} fn send_body_request(&mut self, _: &PeerId, _: RequestId, _: BlockAttributes, _: FromBlock<::Hash, <::Header as HeaderT>::Number>, _: Option, _: Direction, _: Option) {} } diff --git a/core/rpc/src/state/mod.rs b/core/rpc/src/state/mod.rs index 40ee94fdb292e..96a00a9bc3f31 100644 --- a/core/rpc/src/state/mod.rs +++ b/core/rpc/src/state/mod.rs @@ -305,7 +305,7 @@ impl State where for key in keys { let mut last_block = None; let mut last_value = last_values.get(key).cloned().unwrap_or_default(); - for (block, _) in self.client.key_changes(begin, end, key)?.into_iter().rev() { + for (block, _) in self.client.key_changes(begin, end, None, key)?.into_iter().rev() { if last_block == Some(block) { continue; } diff --git a/core/state-machine/src/changes_trie/build.rs b/core/state-machine/src/changes_trie/build.rs index 63546b30c8457..b3bd4af026f6b 100644 --- a/core/state-machine/src/changes_trie/build.rs +++ b/core/state-machine/src/changes_trie/build.rs @@ -63,7 +63,8 @@ pub fn prepare_input<'a, B, S, H, Number>( config, storage)?; top.extend(prepared.0); - // TODO EMCH should zip with extrinsics (let's wait optim merge). + // zipping with extrinsics could be faster, but requires iterator + // to stay ordered. for (storage_key, child) in prepared.1 { if let Some(ix) = children.iter().position(|v| v.0 == storage_key) { children[ix].1.extend(child); @@ -259,17 +260,12 @@ fn prepare_digest_input<'a, S, H, Number>( mod test { use parity_codec::Encode; use primitives::Blake2Hasher; - use primitives::storage::well_known_keys::{EXTRINSIC_INDEX, CHILD_STORAGE_KEY_PREFIX}; + use primitives::storage::well_known_keys::{EXTRINSIC_INDEX}; use crate::backend::InMemory; use crate::changes_trie::storage::InMemoryStorage; use crate::overlayed_changes::{OverlayedValue, OverlayedChangeSet}; use super::*; - fn child_trie_key(k: &[u8]) -> Vec { - let mut res = CHILD_STORAGE_KEY_PREFIX.to_vec(); - res.extend_from_slice(k); - res - } fn prepare_for_build() -> (InMemory, InMemoryStorage, OverlayedChanges) { let backend: InMemory<_> = vec![ (vec![100], vec![255]), @@ -279,8 +275,8 @@ mod test { (vec![104], vec![255]), (vec![105], vec![255]), ].into_iter().collect::<::std::collections::HashMap<_, _>>().into(); - let child_trie_key1 = child_trie_key(b"1"); - let child_trie_key2 = child_trie_key(b"2"); + let child_trie_key1 = b"1".to_vec(); + let child_trie_key2 = b"2".to_vec(); let storage = InMemoryStorage::with_inputs(vec![ (1, vec![ InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 1, key: vec![100] }, vec![1, 3]), @@ -401,11 +397,11 @@ mod test { InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 5, key: vec![101] }, vec![1]), InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 5, key: vec![103] }, vec![0, 1]), ], vec![ - (ChildIndex { block: 5, storage_key: child_trie_key(&b"1"[..]) }, + (ChildIndex { block: 5, storage_key: b"1".to_vec() }, vec![ InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 5, key: vec![100] }, vec![0, 2, 3]), ]), - (ChildIndex { block: 5, storage_key: child_trie_key(&b"2"[..]) }, + (ChildIndex { block: 5, storage_key: b"2".to_vec() }, vec![ InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 5, key: vec![100] }, vec![0, 2]), ]), @@ -433,13 +429,13 @@ mod test { InputPair::DigestIndex(DigestIndex { block: 4, key: vec![102] }, vec![2]), InputPair::DigestIndex(DigestIndex { block: 4, key: vec![105] }, vec![1, 3]), ], vec![ - (ChildIndex { block: 4, storage_key: child_trie_key(&b"1"[..]) }, + (ChildIndex { block: 4, storage_key: b"1".to_vec() }, vec![ InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 4, key: vec![100] }, vec![0, 2, 3]), InputPair::DigestIndex(DigestIndex { block: 4, key: vec![102] }, vec![2]), ]), - (ChildIndex { block: 4, storage_key: child_trie_key(&b"2"[..]) }, + (ChildIndex { block: 4, storage_key: b"2".to_vec() }, vec![ InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 4, key: vec![100] }, vec![0, 2]), ]), @@ -468,13 +464,13 @@ mod test { InputPair::DigestIndex(DigestIndex { block: 16, key: vec![103] }, vec![4]), InputPair::DigestIndex(DigestIndex { block: 16, key: vec![105] }, vec![4, 8]), ], vec![ - (ChildIndex { block: 16, storage_key: child_trie_key(&b"1"[..]) }, + (ChildIndex { block: 16, storage_key: b"1".to_vec() }, vec![ InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 16, key: vec![100] }, vec![0, 2, 3]), InputPair::DigestIndex(DigestIndex { block: 16, key: vec![102] }, vec![4]), ]), - (ChildIndex { block: 16, storage_key: child_trie_key(&b"2"[..]) }, + (ChildIndex { block: 16, storage_key: b"2".to_vec() }, vec![ InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 16, key: vec![100] }, vec![0, 2]), ]), @@ -509,13 +505,13 @@ mod test { InputPair::DigestIndex(DigestIndex { block: 4, key: vec![102] }, vec![2]), InputPair::DigestIndex(DigestIndex { block: 4, key: vec![105] }, vec![1, 3]), ], vec![ - (ChildIndex { block: 4, storage_key: child_trie_key(&b"1"[..]) }, + (ChildIndex { block: 4, storage_key: b"1".to_vec() }, vec![ InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 4, key: vec![100] }, vec![0, 2, 3]), InputPair::DigestIndex(DigestIndex { block: 4, key: vec![102] }, vec![2]), ]), - (ChildIndex { block: 4, storage_key: child_trie_key(&b"2"[..]) }, + (ChildIndex { block: 4, storage_key: b"2".to_vec() }, vec![ InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 4, key: vec![100] }, vec![0, 2]), ]), diff --git a/core/state-machine/src/changes_trie/changes_iterator.rs b/core/state-machine/src/changes_trie/changes_iterator.rs index a42732c4dfca3..9118452f1c261 100644 --- a/core/state-machine/src/changes_trie/changes_iterator.rs +++ b/core/state-machine/src/changes_trie/changes_iterator.rs @@ -24,13 +24,11 @@ use hash_db::{HashDB, Hasher}; use num_traits::One; use trie::{Recorder, MemoryDB}; use crate::changes_trie::{AnchorBlockId, Configuration, RootsStorage, Storage, BlockNumber}; -use crate::changes_trie::input::{DigestIndex, ExtrinsicIndex, DigestIndexValue, ExtrinsicIndexValue}; +use crate::changes_trie::input::{DigestIndex, ExtrinsicIndex, DigestIndexValue, ExtrinsicIndexValue, ChildIndex}; use crate::changes_trie::storage::{TrieBackendAdapter, InMemoryStorage}; use crate::proving_backend::ProvingBackendEssence; use crate::trie_backend_essence::{TrieBackendEssence}; -// TODO EMCH adapt iterator for children -> at a given storage_key - /// Return changes of given key at given blocks range. /// `max` is the number of best known block. /// Changes are returned in descending order (i.e. last block comes first). @@ -40,6 +38,7 @@ pub fn key_changes<'a, S: Storage, H: Hasher, Number: BlockNumber>( begin: Number, end: &'a AnchorBlockId, max: Number, + storage_key: Option<&'a [u8]>, key: &'a [u8], ) -> Result, String> { // we can't query any roots before root @@ -47,6 +46,7 @@ pub fn key_changes<'a, S: Storage, H: Hasher, Number: BlockNumber>( Ok(DrilldownIterator { essence: DrilldownIteratorEssence { + storage_key, key, roots_storage: storage, storage, @@ -62,6 +62,7 @@ pub fn key_changes<'a, S: Storage, H: Hasher, Number: BlockNumber>( }) } + /// Returns proof of changes of given key at given blocks range. /// `max` is the number of best known block. pub fn key_changes_proof, H: Hasher, Number: BlockNumber>( @@ -70,6 +71,7 @@ pub fn key_changes_proof, H: Hasher, Number: BlockNumber>( begin: Number, end: &AnchorBlockId, max: Number, + storage_key: Option<&[u8]>, key: &[u8], ) -> Result>, String> { // we can't query any roots before root @@ -77,6 +79,7 @@ pub fn key_changes_proof, H: Hasher, Number: BlockNumber>( let mut iter = ProvingDrilldownIterator { essence: DrilldownIteratorEssence { + storage_key, key, roots_storage: storage.clone(), storage, @@ -110,6 +113,7 @@ pub fn key_changes_proof_check, H: Hasher, Number: Bl begin: Number, end: &AnchorBlockId, max: Number, + storage_key: Option<&[u8]>, key: &[u8] ) -> Result, String> { // we can't query any roots before root @@ -123,6 +127,7 @@ pub fn key_changes_proof_check, H: Hasher, Number: Bl let proof_db = InMemoryStorage::with_db(proof_db); DrilldownIterator { essence: DrilldownIteratorEssence { + storage_key, key, roots_storage, storage: &proof_db, @@ -195,6 +200,7 @@ pub struct DrilldownIteratorEssence<'a, RS, S, H, Number> Number: BlockNumber, H::Out: 'a, { + storage_key: Option<&'a [u8]>, key: &'a [u8], roots_storage: &'a RS, storage: &'a S, @@ -242,6 +248,25 @@ impl<'a, RS, S, H, Number> DrilldownIteratorEssence<'a, RS, S, H, Number> // AND trie roots for old blocks are known (both on full + light node) let trie_root = self.roots_storage.root(&self.end, block.clone())? .ok_or_else(|| format!("Changes trie root for block {} is not found", block.clone()))?; + let trie_root = if let Some(storage_key) = self.storage_key { + let child_key = ChildIndex { + block: block.clone(), + storage_key: storage_key.to_vec(), + }.encode(); + if let Some(trie_root) = trie_reader(&self.storage, trie_root, &child_key)? + .and_then(|v| >::decode(&mut &v[..])) + .map(|v| { + let mut hash = H::Out::default(); + hash.as_mut().copy_from_slice(&v[..]); + hash + }) { + trie_root + } else { + continue; + } + } else { + trie_root + }; // only return extrinsics for blocks before self.max // most of blocks will be filtered out before pushing to `self.blocks` @@ -454,8 +479,10 @@ mod tests { let config = Configuration { digest_interval: 4, digest_levels: 2 }; let backend = InMemoryStorage::with_inputs(vec![ // digest: 1..4 => [(3, 0)] - (1, vec![]), - (2, vec![]), + (1, vec![ + ]), + (2, vec![ + ]), (3, vec![ InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 3, key: vec![42] }, vec![0]), ]), @@ -484,7 +511,20 @@ mod tests { (16, vec![ InputPair::DigestIndex(DigestIndex { block: 16, key: vec![42] }, vec![4, 8]), ]), - ], vec![]); + ], vec![(b"1".to_vec(), vec![ + (1, vec![ + InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 1, key: vec![42] }, vec![0]), + ]), + (2, vec![ + InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 2, key: vec![42] }, vec![3]), + ]), + (16, vec![ + InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 16, key: vec![42] }, vec![5]), + + InputPair::DigestIndex(DigestIndex { block: 16, key: vec![42] }, vec![2]), + ]), + ]), + ]); (config, backend) } @@ -493,27 +533,38 @@ mod tests { fn drilldown_iterator_works() { let (config, storage) = prepare_for_drilldown(); let drilldown_result = key_changes::, Blake2Hasher, u64>( - &config, &storage, 0, &AnchorBlockId { hash: Default::default(), number: 16 }, 16, &[42]) + &config, &storage, 0, + &AnchorBlockId { hash: Default::default(), number: 16 }, 16, None, &[42]) .and_then(Result::from_iter); assert_eq!(drilldown_result, Ok(vec![(8, 2), (8, 1), (6, 3), (3, 0)])); let drilldown_result = key_changes::, Blake2Hasher, u64>( - &config, &storage, 0, &AnchorBlockId { hash: Default::default(), number: 2 }, 4, &[42]) + &config, &storage, 0, + &AnchorBlockId { hash: Default::default(), number: 16 }, 16, Some(&b"1"[..]), &[42]) + .and_then(Result::from_iter); + assert_eq!(drilldown_result, Ok(vec![(16, 5), (2, 3)])); + + let drilldown_result = key_changes::, Blake2Hasher, u64>( + &config, &storage, 0, + &AnchorBlockId { hash: Default::default(), number: 2 }, 4, None, &[42]) .and_then(Result::from_iter); assert_eq!(drilldown_result, Ok(vec![])); let drilldown_result = key_changes::, Blake2Hasher, u64>( - &config, &storage, 0, &AnchorBlockId { hash: Default::default(), number: 3 }, 4, &[42]) + &config, &storage, 0, + &AnchorBlockId { hash: Default::default(), number: 3 }, 4, None, &[42]) .and_then(Result::from_iter); assert_eq!(drilldown_result, Ok(vec![(3, 0)])); let drilldown_result = key_changes::, Blake2Hasher, u64>( - &config, &storage, 7, &AnchorBlockId { hash: Default::default(), number: 8 }, 8, &[42]) + &config, &storage, 7, + &AnchorBlockId { hash: Default::default(), number: 8 }, 8, None, &[42]) .and_then(Result::from_iter); assert_eq!(drilldown_result, Ok(vec![(8, 2), (8, 1)])); let drilldown_result = key_changes::, Blake2Hasher, u64>( - &config, &storage, 5, &AnchorBlockId { hash: Default::default(), number: 7 }, 8, &[42]) + &config, &storage, 5, + &AnchorBlockId { hash: Default::default(), number: 7 }, 8, None, &[42]) .and_then(Result::from_iter); assert_eq!(drilldown_result, Ok(vec![(6, 3)])); } @@ -524,17 +575,26 @@ mod tests { storage.clear_storage(); assert!(key_changes::, Blake2Hasher, u64>( - &config, &storage, 0, &AnchorBlockId { hash: Default::default(), number: 100 }, 1000, &[42]) + &config, &storage, 0, + &AnchorBlockId { hash: Default::default(), number: 100 }, 1000, None, &[42]) + .and_then(|i| i.collect::, _>>()).is_err()); + + assert!(key_changes::, Blake2Hasher, u64>( + &config, &storage, 0, + &AnchorBlockId { hash: Default::default(), number: 100 }, 1000, Some(&b"1"[..]), &[42]) .and_then(|i| i.collect::, _>>()).is_err()); + } #[test] fn drilldown_iterator_fails_when_range_is_invalid() { let (config, storage) = prepare_for_drilldown(); assert!(key_changes::, Blake2Hasher, u64>( - &config, &storage, 0, &AnchorBlockId { hash: Default::default(), number: 100 }, 50, &[42]).is_err()); + &config, &storage, 0, + &AnchorBlockId { hash: Default::default(), number: 100 }, 50, None, &[42]).is_err()); assert!(key_changes::, Blake2Hasher, u64>( - &config, &storage, 20, &AnchorBlockId { hash: Default::default(), number: 10 }, 100, &[42]).is_err()); + &config, &storage, 20, + &AnchorBlockId { hash: Default::default(), number: 10 }, 100, None, &[42]).is_err()); } @@ -546,7 +606,12 @@ mod tests { let (remote_config, remote_storage) = prepare_for_drilldown(); let remote_proof = key_changes_proof::, Blake2Hasher, u64>( &remote_config, &remote_storage, - 0, &AnchorBlockId { hash: Default::default(), number: 16 }, 16, &[42]).unwrap(); + 0, &AnchorBlockId { hash: Default::default(), number: 16 }, 16, None, &[42]).unwrap(); + + let (remote_config, remote_storage) = prepare_for_drilldown(); + let remote_proof_child = key_changes_proof::, Blake2Hasher, u64>( + &remote_config, &remote_storage, + 0, &AnchorBlockId { hash: Default::default(), number: 16 }, 16, Some(&b"1"[..]), &[42]).unwrap(); // happens on local light node: @@ -555,9 +620,18 @@ mod tests { local_storage.clear_storage(); let local_result = key_changes_proof_check::, Blake2Hasher, u64>( &local_config, &local_storage, remote_proof, - 0, &AnchorBlockId { hash: Default::default(), number: 16 }, 16, &[42]); + 0, &AnchorBlockId { hash: Default::default(), number: 16 }, 16, None, &[42]); + + let (local_config, local_storage) = prepare_for_drilldown(); + local_storage.clear_storage(); + let local_result_child = key_changes_proof_check::, Blake2Hasher, u64>( + &local_config, &local_storage, remote_proof_child, + 0, &AnchorBlockId { hash: Default::default(), number: 16 }, 16, Some(&b"1"[..]), &[42]); + + // check that drilldown result is the same as if it was happening at the full node assert_eq!(local_result, Ok(vec![(8, 2), (8, 1), (6, 3), (3, 0)])); + assert_eq!(local_result_child, Ok(vec![(16, 5), (2, 3)])); } } From b9b532f7c23495c1db3e5eec1ce38c861d077ef0 Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 15 Jul 2019 17:33:12 +0200 Subject: [PATCH 04/12] prune child trie. --- core/state-machine/src/changes_trie/prune.rs | 77 +++++++++++++++----- 1 file changed, 59 insertions(+), 18 deletions(-) diff --git a/core/state-machine/src/changes_trie/prune.rs b/core/state-machine/src/changes_trie/prune.rs index 7b8c9869e0f17..d07ef3760eb8d 100644 --- a/core/state-machine/src/changes_trie/prune.rs +++ b/core/state-machine/src/changes_trie/prune.rs @@ -24,6 +24,8 @@ use crate::proving_backend::ProvingBackendEssence; use crate::trie_backend_essence::TrieBackendEssence; use crate::changes_trie::{AnchorBlockId, Configuration, Storage, BlockNumber}; use crate::changes_trie::storage::TrieBackendAdapter; +use crate::changes_trie::input::{ChildIndex, InputKey}; +use parity_codec::Decode; /// Get number of oldest block for which changes trie is not pruned /// given changes trie configuration, pruning parameter and number of @@ -55,8 +57,6 @@ pub fn prune, H: Hasher, Number: BlockNumber, F: FnMut(H:: mut remove_trie_node: F, ) { -// TODO EMCH need iter prune children!!! - // select range for pruning let (first, last) = match pruning_range(config, min_blocks_to_keep, current_block.number.clone()) { Some((first, last)) => (first, last), @@ -84,23 +84,55 @@ pub fn prune, H: Hasher, Number: BlockNumber, F: FnMut(H:: continue; }, }; - - // enumerate all changes trie' keys, recording all nodes that have been 'touched' - // (effectively - all changes trie nodes) - let mut proof_recorder: Recorder = Default::default(); - { - let mut trie = ProvingBackendEssence::<_, H> { - backend: &TrieBackendEssence::new(TrieBackendAdapter::new(storage), root), - proof_recorder: &mut proof_recorder, - }; - trie.record_all_keys(); + let children_roots = { + let trie_storage = TrieBackendEssence::<_, H>::new( + crate::changes_trie::TrieBackendStorageAdapter(storage), + root, + ); + let child_prefix = ChildIndex::key_neutral_prefix(block.clone()); + let mut children_roots = Vec::new(); + trie_storage.for_key_values_with_prefix(&child_prefix, |key, value| { + if let Some(InputKey::ChildIndex::(_trie_key)) = Decode::decode(&mut &key[..]) { + if let Some(value) = >::decode(&mut &value[..]) { + let mut trie_root = ::Out::default(); + trie_root.as_mut().copy_from_slice(&value[..]); + children_roots.push(trie_root); + } + } + }); + + children_roots + }; + for root in children_roots.into_iter() { + prune_trie(storage, root, &mut remove_trie_node); } - // all nodes of this changes trie should be pruned - remove_trie_node(root); - for node in proof_recorder.drain().into_iter().map(|n| n.hash) { - remove_trie_node(node); - } + prune_trie(storage, root, &mut remove_trie_node); + } +} + +// Prune a trie. +fn prune_trie, H: Hasher, Number: BlockNumber, F: FnMut(H::Out)>( + storage: &S, + root: H::Out, + remove_trie_node: &mut F, +) { + + // enumerate all changes trie' keys, recording all nodes that have been 'touched' + // (effectively - all changes trie nodes) + let mut proof_recorder: Recorder = Default::default(); + { + let mut trie = ProvingBackendEssence::<_, H> { + backend: &TrieBackendEssence::new(TrieBackendAdapter::new(storage), root), + proof_recorder: &mut proof_recorder, + }; + trie.record_all_keys(); + } + + // all nodes of this changes trie should be pruned + remove_trie_node(root); + for node in proof_recorder.drain().into_iter().map(|n| n.hash) { + remove_trie_node(node); } } @@ -172,6 +204,7 @@ mod tests { use primitives::Blake2Hasher; use crate::backend::insert_into_memory_db; use crate::changes_trie::storage::InMemoryStorage; + use parity_codec::Encode; use super::*; fn config(interval: u32, levels: u32) -> Configuration { @@ -196,12 +229,20 @@ mod tests { #[test] fn prune_works() { fn prepare_storage() -> InMemoryStorage { + + let child_prefix = ChildIndex::key_neutral_prefix(67u64); let mut mdb1 = MemoryDB::::default(); let root1 = insert_into_memory_db::(&mut mdb1, vec![(vec![10], vec![20])]).unwrap(); let mut mdb2 = MemoryDB::::default(); let root2 = insert_into_memory_db::(&mut mdb2, vec![(vec![11], vec![21]), (vec![12], vec![22])]).unwrap(); + let mut ch_mdb3 = MemoryDB::::default(); + let ch_root3 = insert_into_memory_db::(&mut ch_mdb3, vec![(vec![110], vec![120])]).unwrap(); let mut mdb3 = MemoryDB::::default(); - let root3 = insert_into_memory_db::(&mut mdb3, vec![(vec![13], vec![23]), (vec![14], vec![24])]).unwrap(); + let root3 = insert_into_memory_db::(&mut mdb3, vec![ + (vec![13], vec![23]), + (vec![14], vec![24]), + (child_prefix, ch_root3.as_ref().encode()), + ]).unwrap(); let mut mdb4 = MemoryDB::::default(); let root4 = insert_into_memory_db::(&mut mdb4, vec![(vec![15], vec![25])]).unwrap(); let storage = InMemoryStorage::new(); From 4bd0fbc79eeffdb207471a4621588f722d1627f3 Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 15 Jul 2019 17:54:14 +0200 Subject: [PATCH 05/12] Fix pruning test. --- core/state-machine/src/changes_trie/prune.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/core/state-machine/src/changes_trie/prune.rs b/core/state-machine/src/changes_trie/prune.rs index d07ef3760eb8d..cb0aa87e7bba0 100644 --- a/core/state-machine/src/changes_trie/prune.rs +++ b/core/state-machine/src/changes_trie/prune.rs @@ -230,18 +230,17 @@ mod tests { fn prune_works() { fn prepare_storage() -> InMemoryStorage { - let child_prefix = ChildIndex::key_neutral_prefix(67u64); + let child_key = ChildIndex { block: 67u64, storage_key: b"1".to_vec() }.encode(); let mut mdb1 = MemoryDB::::default(); let root1 = insert_into_memory_db::(&mut mdb1, vec![(vec![10], vec![20])]).unwrap(); let mut mdb2 = MemoryDB::::default(); let root2 = insert_into_memory_db::(&mut mdb2, vec![(vec![11], vec![21]), (vec![12], vec![22])]).unwrap(); - let mut ch_mdb3 = MemoryDB::::default(); - let ch_root3 = insert_into_memory_db::(&mut ch_mdb3, vec![(vec![110], vec![120])]).unwrap(); let mut mdb3 = MemoryDB::::default(); + let ch_root3 = insert_into_memory_db::(&mut mdb3, vec![(vec![110], vec![120])]).unwrap(); let root3 = insert_into_memory_db::(&mut mdb3, vec![ (vec![13], vec![23]), (vec![14], vec![24]), - (child_prefix, ch_root3.as_ref().encode()), + (child_key, ch_root3.as_ref().encode()), ]).unwrap(); let mut mdb4 = MemoryDB::::default(); let root4 = insert_into_memory_db::(&mut mdb4, vec![(vec![15], vec![25])]).unwrap(); From ed8cc0d73b5367e810cb0e85b94a64fe82bbb226 Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 15 Jul 2019 17:56:31 +0200 Subject: [PATCH 06/12] bump spec version. --- node/runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/runtime/src/lib.rs b/node/runtime/src/lib.rs index 72a746f20f46c..601f49566b341 100644 --- a/node/runtime/src/lib.rs +++ b/node/runtime/src/lib.rs @@ -69,7 +69,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // and set impl_version to equal spec_version. If only runtime // implementation changes and behavior does not, then leave spec_version as // is and increment impl_version. - spec_version: 110, + spec_version: 111, impl_version: 111, apis: RUNTIME_API_VERSIONS, }; From cd0abc69b77851beb3904ba6ed97e9c242d2ecc8 Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 24 Jul 2019 11:49:00 +0200 Subject: [PATCH 07/12] Avoid empty child trie (could also be checked before) --- core/state-machine/src/changes_trie/mod.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/state-machine/src/changes_trie/mod.rs b/core/state-machine/src/changes_trie/mod.rs index 399e7c12fc2f9..350eb932264dc 100644 --- a/core/state-machine/src/changes_trie/mod.rs +++ b/core/state-machine/src/changes_trie/mod.rs @@ -150,15 +150,19 @@ pub fn build_changes_trie<'a, B: Backend, S: Storage, H: Hasher, N let mut mdb = MemoryDB::default(); let mut child_roots = Vec::with_capacity(child_input_pairs.len()); for (child_index, input_pairs) in child_input_pairs { + let mut not_empty = false; let mut root = Default::default(); { let mut trie = TrieDBMut::::new(&mut mdb, &mut root); for (key, value) in input_pairs.map(Into::into) { + not_empty = true; trie.insert(&key, &value) .expect("changes trie: insertion to trie is not allowed to fail within runtime"); } } - child_roots.push(input::InputPair::ChildIndex(child_index, root.as_ref().to_vec())); + if not_empty { + child_roots.push(input::InputPair::ChildIndex(child_index, root.as_ref().to_vec())); + } } let mut root = Default::default(); { From 58eeee3caa32afaabef3314d6b9cd3a4f7219899 Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 24 Jul 2019 17:53:03 +0200 Subject: [PATCH 08/12] tabs. --- core/network/src/protocol.rs | 16 ++++++++-------- core/state-machine/src/changes_trie/build.rs | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/core/network/src/protocol.rs b/core/network/src/protocol.rs index 9079f147ad429..09f7a2d90eba0 100644 --- a/core/network/src/protocol.rs +++ b/core/network/src/protocol.rs @@ -1351,10 +1351,10 @@ impl, H: ExHashT> Protocol { request.id, who, if let Some(sk) = request.storage_key.as_ref() { - format!("{} : {}", sk.to_hex::(), request.key.to_hex::()) - } else { - request.key.to_hex::() - }, + format!("{} : {}", sk.to_hex::(), request.key.to_hex::()) + } else { + request.key.to_hex::() + }, request.first, request.last ); @@ -1374,10 +1374,10 @@ impl, H: ExHashT> Protocol { request.id, who, if let Some(sk) = storage_key { - format!("{} : {}", sk.0.to_hex::(), key.0.to_hex::()) - } else { - key.0.to_hex::() - }, + format!("{} : {}", sk.0.to_hex::(), key.0.to_hex::()) + } else { + key.0.to_hex::() + }, request.first, request.last, error diff --git a/core/state-machine/src/changes_trie/build.rs b/core/state-machine/src/changes_trie/build.rs index 9da3dae679049..c2ae1c6af6486 100644 --- a/core/state-machine/src/changes_trie/build.rs +++ b/core/state-machine/src/changes_trie/build.rs @@ -290,8 +290,8 @@ fn prepare_digest_input<'a, S, H, Number>( }) .map(|(pairs, child_pairs)| ( - pairs.into_iter().map(|(_, (k, v))| InputPair::DigestIndex(k, v)), - child_pairs, + pairs.into_iter().map(|(_, (k, v))| InputPair::DigestIndex(k, v)), + child_pairs, )) } From c00ca20285c070f9ce91e9ee1264a00c7084d22f Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 24 Jul 2019 19:15:23 +0200 Subject: [PATCH 09/12] Fix child digest overriding each others. --- core/state-machine/src/changes_trie/build.rs | 23 ++++++++++++-------- core/state-machine/src/overlayed_changes.rs | 5 +++-- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/core/state-machine/src/changes_trie/build.rs b/core/state-machine/src/changes_trie/build.rs index c2ae1c6af6486..774d0e1c9963e 100644 --- a/core/state-machine/src/changes_trie/build.rs +++ b/core/state-machine/src/changes_trie/build.rs @@ -263,7 +263,12 @@ fn prepare_digest_input<'a, S, H, Number>( for (storage_key, trie_root) in children_roots.into_iter() { - let mut map = BTreeMap::, _>::new(); + let child_index = ChildIndex:: { + block: block.clone(), + storage_key, + }; + + let mut map = child_map.entry(child_index).or_insert_with(|| BTreeMap::, _>::new()); let trie_storage = TrieBackendEssence::<_, H>::new( crate::changes_trie::TrieBackendStorageAdapter(storage), trie_root, @@ -277,13 +282,6 @@ fn prepare_digest_input<'a, S, H, Number>( if let Some(InputKey::DigestIndex::(trie_key)) = Decode::decode(&mut &key[..]) { insert_to_map(&mut map, trie_key.key); }); - - - let child_index = ChildIndex:: { - block: block.clone(), - storage_key, - }; - child_map.insert(child_index, map.into_iter().map(|(_, (k, v))| InputPair::DigestIndex(k, v))); } Ok((map, child_map)) @@ -291,7 +289,8 @@ fn prepare_digest_input<'a, S, H, Number>( .map(|(pairs, child_pairs)| ( pairs.into_iter().map(|(_, (k, v))| InputPair::DigestIndex(k, v)), - child_pairs, + child_pairs.into_iter().map(|(sk, pairs)| + (sk, pairs.into_iter().map(|(_, (k, v))| InputPair::DigestIndex(k, v)))).collect(), )) } @@ -478,7 +477,10 @@ mod test { vec![ InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 4u64, key: vec![100] }, vec![0, 2, 3]), + InputPair::DigestIndex(DigestIndex { block: 4, key: vec![100] }, vec![1]), + InputPair::DigestIndex(DigestIndex { block: 4, key: vec![101] }, vec![1]), InputPair::DigestIndex(DigestIndex { block: 4, key: vec![102] }, vec![2]), + InputPair::DigestIndex(DigestIndex { block: 4, key: vec![105] }, vec![1]), ]), (ChildIndex { block: 4, storage_key: b"2".to_vec() }, vec![ @@ -560,7 +562,10 @@ mod test { vec![ InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 4u64, key: vec![100] }, vec![0, 2, 3]), + InputPair::DigestIndex(DigestIndex { block: 4, key: vec![100] }, vec![1]), + InputPair::DigestIndex(DigestIndex { block: 4, key: vec![101] }, vec![1]), InputPair::DigestIndex(DigestIndex { block: 4, key: vec![102] }, vec![2]), + InputPair::DigestIndex(DigestIndex { block: 4, key: vec![105] }, vec![1]), ]), (ChildIndex { block: 4, storage_key: b"2".to_vec() }, vec![ diff --git a/core/state-machine/src/overlayed_changes.rs b/core/state-machine/src/overlayed_changes.rs index e44cf81de2686..005d886ef210b 100644 --- a/core/state-machine/src/overlayed_changes.rs +++ b/core/state-machine/src/overlayed_changes.rs @@ -180,12 +180,13 @@ impl OverlayedChanges { }); if let Some(committed_map) = self.committed.children.get(storage_key) { - for (key, _) in committed_map.iter() { + for (key, value) in committed_map.iter() { if !map_entry.contains_key(key) { map_entry.insert(key.clone(), OverlayedValue { value: None, extrinsics: extrinsic_index.map(|i| { - let mut e = BTreeSet::default(); + let mut e = value.extrinsics.clone() + .unwrap_or_else(|| BTreeSet::default()); e.insert(i); e }), From bebab64d2804c0caf7a72f294002675f98f9ab51 Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 29 Aug 2019 15:35:07 +0200 Subject: [PATCH 10/12] Restore doc deleted on merge. --- core/state-machine/src/changes_trie/mod.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/core/state-machine/src/changes_trie/mod.rs b/core/state-machine/src/changes_trie/mod.rs index 22c0107fa9c9d..41234b3428678 100644 --- a/core/state-machine/src/changes_trie/mod.rs +++ b/core/state-machine/src/changes_trie/mod.rs @@ -14,6 +14,24 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . +//! Changes trie related structures and functions. +//! +//! Changes trie is a trie built of { storage key => extrinsiscs } pairs +//! at the end of each block. For every changed storage key it contains +//! a pair, mapping key to the set of extrinsics where it has been changed. +//! +//! Optionally, every N blocks, additional level1-digest nodes are appended +//! to the changes trie, containing pairs { storage key => blocks }. For every +//! storage key that has been changed in PREVIOUS N-1 blocks (except for genesis +//! block) it contains a pair, mapping this key to the set of blocks where it +//! has been changed. +//! +//! Optionally, every N^digest_level (where digest_level > 1) blocks, additional +//! digest_level digest is created. It is built out of pairs { storage key => digest +//! block }, containing entries for every storage key that has been changed in +//! the last N*digest_level-1 blocks (except for genesis block), mapping these keys +//! to the set of lower-level digest blocks. +//! //! Changes trie configuration could change within a time. The range of blocks, where //! configuration has been active, is given by two blocks: zero and end. Zero block is //! the block where configuration has been set. But the first changes trie that uses From 90e1b8f50197a23d5eb1be3eb7ecf48f90b38eec Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 30 Aug 2019 08:58:06 +0200 Subject: [PATCH 11/12] Check correct child value on extrinsics build. --- core/state-machine/src/changes_trie/build.rs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/core/state-machine/src/changes_trie/build.rs b/core/state-machine/src/changes_trie/build.rs index 96cc87d5482d6..e5f539f483d65 100644 --- a/core/state-machine/src/changes_trie/build.rs +++ b/core/state-machine/src/changes_trie/build.rs @@ -141,16 +141,19 @@ fn prepare_extrinsics_input_inner<'a, B, H, Number>( Entry::Vacant(entry) => { // ignore temporary values (values that have null value at the end of operation // AND are not in storage at the beginning of operation - let existing = if let Some(sk) = storage_key.as_ref() { - changes.child_storage(sk, k) + if let Some(sk) = storage_key.as_ref() { + if !changes.child_storage(sk, k).map(|v| v.is_some()).unwrap_or_default() { + if !backend.exists_child_storage(sk, k).map_err(|e| format!("{}", e))? { + return Ok(map); + } + } } else { - changes.storage(k) - }; - if !existing.map(|v| v.is_some()).unwrap_or_default() { - if !backend.exists_storage(k).map_err(|e| format!("{}", e))? { - return Ok(map); + if !changes.storage(k).map(|v| v.is_some()).unwrap_or_default() { + if !backend.exists_storage(k).map_err(|e| format!("{}", e))? { + return Ok(map); + } } - } + }; let extrinsics = v.extrinsics.as_ref() .expect("filtered by filter() call above; qed") From 92240c93bd0b9e56ae42bbb30918167ac307a3b3 Mon Sep 17 00:00:00 2001 From: Emeric Chevalier Date: Mon, 2 Sep 2019 10:00:24 +0200 Subject: [PATCH 12/12] Revert runtime version update. --- node/runtime/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/node/runtime/src/lib.rs b/node/runtime/src/lib.rs index 72d81ee2941ad..5dc8d02eccde7 100644 --- a/node/runtime/src/lib.rs +++ b/node/runtime/src/lib.rs @@ -80,8 +80,8 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // and set impl_version to equal spec_version. If only runtime // implementation changes and behavior does not, then leave spec_version as // is and increment impl_version. - spec_version: 155, - impl_version: 155, + spec_version: 154, + impl_version: 158, apis: RUNTIME_API_VERSIONS, };