Multiple storage root support (#902)

* Implement a non generic version of child delta trie

* Use delta_trie_root in state_machine

* Expand InMemory backend to support multi-storage

* Create Consolidate trait

* Fix all crate compile and remove unused OverlayedChanges::drain

* Implement child storage root support and overlay changes

* Add child storage reader

* Add child storage writer

* Implement child storage cleaning

* Fix light backend compile

* Add all required ext functions for wasm executor

* Add ext def to io

* Add all io functions

* Fix nostd compile

* Add simple test

* Remove unnecessary vec copy in child_storage_root_transaction

* Use values_mut/for_each to make it shorter

* Use extend to shorter a for loop

* Move record_all_keys to trie so it's easier to generic them later

* space -> tab

* Remove to_owned in debug format

* Clean out all to_owned

* Break debug_trace to multiple lines

* Remove 0..

* UserError copy/paste typo

* Replace Vec::from_raw_parts by slice::from_raw_parts

* Use iter::empty()

* Wrap some long lines

* Wrap a missing line

* Remove unnecessary map

https://github.com/paritytech/substrate/pull/856#discussion_r226222663

* Call ext_free after from_raw_parts

* Fix tests in other crates
This commit is contained in:
Wei Tang
2018-10-19 00:54:02 +08:00
committed by Gav Wood
parent 849506daa5
commit 2604474880
18 changed files with 1000 additions and 111 deletions
+102 -10
View File
@@ -17,11 +17,12 @@
//! Conrete externalities implementation.
use std::{error, fmt, cmp::Ord};
use backend::Backend;
use backend::{Backend, Consolidate};
use changes_trie::{Storage as ChangesTrieStorage, compute_changes_trie_root};
use {Externalities, OverlayedChanges};
use hash_db::Hasher;
use substrate_trie::{MemoryDB, TrieDBMut, TrieMut};
use primitives::storage::well_known_keys::is_child_storage_key;
use substrate_trie::{MemoryDB, TrieDBMut, TrieMut, default_child_trie_root, is_child_trie_key_valid};
use heapsize::HeapSizeOf;
const EXT_NOT_ALLOWED_TO_FAIL: &'static str = "Externalities not allowed to fail within runtime";
@@ -122,6 +123,31 @@ where
fn mark_dirty(&mut self) {
self.storage_transaction = None;
}
/// Fetch child storage root together with its transaction.
fn child_storage_root_transaction(&mut self, storage_key: &[u8]) -> (Vec<u8>, B::Transaction) {
self.mark_dirty();
let (root, transaction) = {
let delta = self.overlay.committed.children.get(storage_key)
.into_iter()
.flat_map(|map| map.1.iter().map(|(k, v)| (k.clone(), v.clone())))
.chain(self.overlay.prospective.children.get(storage_key)
.into_iter()
.flat_map(|map| map.1.iter().map(|(k, v)| (k.clone(), v.clone()))));
self.backend.child_storage_root(storage_key, delta)
};
let root_val = if root == default_child_trie_root::<H>(storage_key) {
None
} else {
Some(root.clone())
};
self.overlay.sync_child_storage_root(storage_key, root_val);
(root, transaction)
}
}
#[cfg(test)]
@@ -137,8 +163,8 @@ where
self.backend.pairs().iter()
.map(|&(ref k, ref v)| (k.to_vec(), Some(v.to_vec())))
.chain(self.overlay.committed.clone().into_iter().map(|(k, v)| (k, v.value)))
.chain(self.overlay.prospective.clone().into_iter().map(|(k, v)| (k, v.value)))
.chain(self.overlay.committed.top.clone().into_iter().map(|(k, v)| (k, v.value)))
.chain(self.overlay.prospective.top.clone().into_iter().map(|(k, v)| (k, v.value)))
.collect::<HashMap<_, _>>()
.into_iter()
.filter_map(|(k, maybe_val)| maybe_val.map(|val| (k, val)))
@@ -158,6 +184,11 @@ where
self.backend.storage(key).expect(EXT_NOT_ALLOWED_TO_FAIL))
}
fn child_storage(&self, storage_key: &[u8], key: &[u8]) -> Option<Vec<u8>> {
self.overlay.child_storage(storage_key, key).map(|x| x.map(|x| x.to_vec())).unwrap_or_else(||
self.backend.child_storage(storage_key, key).expect(EXT_NOT_ALLOWED_TO_FAIL))
}
fn exists_storage(&self, key: &[u8]) -> bool {
match self.overlay.storage(key) {
Some(x) => x.is_some(),
@@ -165,12 +196,52 @@ where
}
}
fn exists_child_storage(&self, storage_key: &[u8], key: &[u8]) -> bool {
match self.overlay.child_storage(storage_key, key) {
Some(x) => x.is_some(),
_ => self.backend.exists_child_storage(storage_key, key).expect(EXT_NOT_ALLOWED_TO_FAIL),
}
}
fn place_storage(&mut self, key: Vec<u8>, value: Option<Vec<u8>>) {
if is_child_storage_key(&key) {
warn!(target: "trie", "Refuse to directly set child storage key");
return;
}
self.mark_dirty();
self.overlay.set_storage(key, value);
}
fn place_child_storage(&mut self, storage_key: Vec<u8>, key: Vec<u8>, value: Option<Vec<u8>>) -> bool {
if !is_child_storage_key(&storage_key) || !is_child_trie_key_valid::<H>(&storage_key) {
return false;
}
self.mark_dirty();
self.overlay.set_child_storage(storage_key, key, value);
true
}
fn kill_child_storage(&mut self, storage_key: &[u8]) {
if !is_child_storage_key(storage_key) || !is_child_trie_key_valid::<H>(storage_key) {
return;
}
self.mark_dirty();
self.overlay.clear_child_storage(storage_key);
self.backend.for_keys_in_child_storage(storage_key, |key| {
self.overlay.set_child_storage(storage_key.to_vec(), key.to_vec(), None);
});
}
fn clear_prefix(&mut self, prefix: &[u8]) {
if is_child_storage_key(prefix) {
warn!(target: "trie", "Refuse to directly clear prefix that is part of child storage key");
return;
}
self.mark_dirty();
self.overlay.clear_prefix(prefix);
self.backend.for_keys_with_prefix(prefix, |key| {
@@ -183,19 +254,40 @@ where
}
fn storage_root(&mut self) -> H::Out {
if let Some((_, ref root)) = self.storage_transaction {
if let Some((_, ref root)) = self.storage_transaction {
return root.clone();
}
// compute and memoize
let delta = self.overlay.committed.iter().map(|(k, v)| (k.clone(), v.value.clone()))
.chain(self.overlay.prospective.iter().map(|(k, v)| (k.clone(), v.value.clone())));
let mut transaction = B::Transaction::default();
let child_storage_keys: Vec<_> = self.overlay.prospective.children.keys().cloned().collect();
let (root, transaction) = self.backend.storage_root(delta);
for key in child_storage_keys {
let (_, t) = self.child_storage_root_transaction(&key);
transaction.consolidate(t);
}
// compute and memoize
let delta = self.overlay.committed.top.iter().map(|(k, v)| (k.clone(), v.value.clone()))
.chain(self.overlay.prospective.top.iter().map(|(k, v)| (k.clone(), v.value.clone())));
let (root, t) = self.backend.storage_root(delta);
transaction.consolidate(t);
self.storage_transaction = Some((transaction, root));
root
}
fn child_storage_root(&mut self, storage_key: &[u8]) -> Option<Vec<u8>> {
if !is_child_storage_key(storage_key) || !is_child_trie_key_valid::<H>(storage_key) {
return None;
}
if self.storage_transaction.is_some() {
return Some(self.storage(storage_key).unwrap_or(default_child_trie_root::<H>(storage_key)));
}
Some(self.child_storage_root_transaction(storage_key).0)
}
fn storage_changes_root(&mut self, block: u64) -> Option<H::Out> {
let root_and_tx = compute_changes_trie_root::<_, T, H>(
self.backend,
@@ -287,7 +379,7 @@ mod tests {
#[test]
fn storage_changes_root_is_some_when_extrinsic_changes_are_empty() {
let mut overlay = prepare_overlay_with_changes();
overlay.prospective.get_mut(&vec![1]).unwrap().value = None;
overlay.prospective.top.get_mut(&vec![1]).unwrap().value = None;
let storage = TestChangesTrieStorage::new();
let backend = TestBackend::default();
let mut ext = TestExt::new(&mut overlay, &backend, Some(&storage));