Pin states in memory so that they are not pruned away while still referenced (#2761)

* State pinning in client

* Canonicalization queue

* Fixed prioritization queue

* possible fix of "hash mismatch"

* Check for pinned discarded states

* Release state before finalization

* Style

* Style
This commit is contained in:
Arkadiy Paronyan
2019-06-04 10:01:12 +02:00
committed by Gavin Wood
parent 6ce7c1c8c8
commit 3b26453047
13 changed files with 392 additions and 194 deletions
+19 -16
View File
@@ -106,7 +106,7 @@ pub trait Backend<H: Hasher> {
}
/// Try convert into trie backend.
fn try_into_trie_backend(self) -> Option<TrieBackend<Self::TrieBackendStorage, H>>;
fn as_trie_backend(&mut self) -> Option<&TrieBackend<Self::TrieBackendStorage, H>>;
/// 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.
@@ -185,31 +185,33 @@ impl error::Error for Void {
/// In-memory backend. Fully recomputes tries on each commit but useful for
/// tests.
#[derive(Eq)]
pub struct InMemory<H> {
pub struct InMemory<H: Hasher> {
inner: HashMap<Option<Vec<u8>>, HashMap<Vec<u8>, Vec<u8>>>,
trie: Option<TrieBackend<MemoryDB<H>, H>>,
_hasher: PhantomData<H>,
}
impl<H> Default for InMemory<H> {
impl<H: Hasher> Default for InMemory<H> {
fn default() -> Self {
InMemory {
inner: Default::default(),
trie: None,
_hasher: PhantomData,
}
}
}
impl<H> Clone for InMemory<H> {
impl<H: Hasher> Clone for InMemory<H> {
fn clone(&self) -> Self {
InMemory {
inner: self.inner.clone(),
trie: None,
_hasher: PhantomData,
}
}
}
impl<H> PartialEq for InMemory<H> {
impl<H: Hasher> PartialEq for InMemory<H> {
fn eq(&self, other: &Self) -> bool {
self.inner.eq(&other.inner)
}
@@ -230,27 +232,29 @@ impl<H: Hasher> InMemory<H> {
}
}
impl<H> From<HashMap<Option<Vec<u8>>, HashMap<Vec<u8>, Vec<u8>>>> for InMemory<H> {
impl<H: Hasher> From<HashMap<Option<Vec<u8>>, HashMap<Vec<u8>, Vec<u8>>>> for InMemory<H> {
fn from(inner: HashMap<Option<Vec<u8>>, HashMap<Vec<u8>, Vec<u8>>>) -> Self {
InMemory {
inner: inner,
trie: None,
_hasher: PhantomData,
}
}
}
impl<H> From<HashMap<Vec<u8>, Vec<u8>>> for InMemory<H> {
impl<H: Hasher> From<HashMap<Vec<u8>, Vec<u8>>> for InMemory<H> {
fn from(inner: HashMap<Vec<u8>, Vec<u8>>) -> Self {
let mut expanded = HashMap::new();
expanded.insert(None, inner);
InMemory {
inner: expanded,
trie: None,
_hasher: PhantomData,
}
}
}
impl<H> From<Vec<(Option<Vec<u8>>, Vec<u8>, Option<Vec<u8>>)>> for InMemory<H> {
impl<H: Hasher> From<Vec<(Option<Vec<u8>>, Vec<u8>, Option<Vec<u8>>)>> for InMemory<H> {
fn from(inner: Vec<(Option<Vec<u8>>, Vec<u8>, Option<Vec<u8>>)>) -> Self {
let mut expanded: HashMap<Option<Vec<u8>>, HashMap<Vec<u8>, Vec<u8>>> = HashMap::new();
for (child_key, key, value) in inner {
@@ -365,16 +369,14 @@ impl<H: Hasher> Backend<H> for InMemory<H> {
.collect()
}
fn try_into_trie_backend(
self
)-> Option<TrieBackend<Self::TrieBackendStorage, H>> {
fn as_trie_backend(&mut self)-> Option<&TrieBackend<Self::TrieBackendStorage, H>> {
let mut mdb = MemoryDB::default();
let mut root = None;
let mut new_child_roots = Vec::new();
let mut root_map = None;
for (storage_key, map) in self.inner {
for (storage_key, map) in &self.inner {
if let Some(storage_key) = storage_key.as_ref() {
let ch = insert_into_memory_db::<H, _>(&mut mdb, map.into_iter())?;
let ch = insert_into_memory_db::<H, _>(&mut mdb, map.clone().into_iter())?;
new_child_roots.push((storage_key.clone(), ch.as_ref().into()));
} else {
root_map = Some(map);
@@ -384,14 +386,15 @@ impl<H: Hasher> Backend<H> for InMemory<H> {
if let Some(map) = root_map.take() {
root = Some(insert_into_memory_db::<H, _>(
&mut mdb,
map.into_iter().chain(new_child_roots.into_iter())
map.clone().into_iter().chain(new_child_roots.into_iter())
)?);
}
let root = match root {
Some(root) => root,
None => insert_into_memory_db::<H, _>(&mut mdb, ::std::iter::empty())?,
};
Some(TrieBackend::new(mdb, root))
self.trie = Some(TrieBackend::new(mdb, root));
self.trie.as_ref()
}
}
+15 -13
View File
@@ -669,7 +669,7 @@ impl<'a, H, N, B, T, O, Exec> StateMachine<'a, H, N, B, T, O, Exec> where
/// Prove execution using the given state backend, overlayed changes, and call executor.
pub fn prove_execution<B, H, Exec>(
backend: B,
mut backend: B,
overlay: &mut OverlayedChanges,
exec: &Exec,
method: &str,
@@ -681,9 +681,9 @@ where
Exec: CodeExecutor<H>,
H::Out: Ord + 'static,
{
let trie_backend = backend.try_into_trie_backend()
let trie_backend = backend.as_trie_backend()
.ok_or_else(|| Box::new(ExecutionError::UnableToGenerateProof) as Box<Error>)?;
prove_execution_on_trie_backend(&trie_backend, overlay, exec, method, call_data)
prove_execution_on_trie_backend(trie_backend, overlay, exec, method, call_data)
}
/// Prove execution using the given trie backend, overlayed changes, and call executor.
@@ -778,7 +778,7 @@ where
/// Generate storage read proof.
pub fn prove_read<B, H>(
backend: B,
mut backend: B,
key: &[u8]
) -> Result<(Option<Vec<u8>>, Vec<Vec<u8>>), Box<Error>>
where
@@ -786,16 +786,16 @@ where
H: Hasher,
H::Out: Ord
{
let trie_backend = backend.try_into_trie_backend()
let trie_backend = backend.as_trie_backend()
.ok_or_else(
||Box::new(ExecutionError::UnableToGenerateProof) as Box<Error>
)?;
prove_read_on_trie_backend(&trie_backend, key)
prove_read_on_trie_backend(trie_backend, key)
}
/// Generate child storage read proof.
pub fn prove_child_read<B, H>(
backend: B,
mut backend: B,
storage_key: &[u8],
key: &[u8],
) -> Result<(Option<Vec<u8>>, Vec<Vec<u8>>), Box<Error>>
@@ -804,9 +804,9 @@ where
H: Hasher,
H::Out: Ord
{
let trie_backend = backend.try_into_trie_backend()
let trie_backend = backend.as_trie_backend()
.ok_or_else(|| Box::new(ExecutionError::UnableToGenerateProof) as Box<Error>)?;
prove_child_read_on_trie_backend(&trie_backend, storage_key, key)
prove_child_read_on_trie_backend(trie_backend, storage_key, key)
}
@@ -1100,7 +1100,8 @@ mod tests {
b"abc".to_vec() => b"2".to_vec(),
b"bbb".to_vec() => b"3".to_vec()
];
let backend = InMemory::<Blake2Hasher>::from(initial).try_into_trie_backend().unwrap();
let mut state = InMemory::<Blake2Hasher>::from(initial);
let backend = state.as_trie_backend().unwrap();
let mut overlay = OverlayedChanges {
committed: map![
b"aba".to_vec() => OverlayedValue::from(Some(b"1312".to_vec())),
@@ -1115,7 +1116,7 @@ mod tests {
{
let changes_trie_storage = InMemoryChangesTrieStorage::<Blake2Hasher, u64>::new();
let mut ext = Ext::new(&mut overlay, &backend, Some(&changes_trie_storage), NeverOffchainExt::new());
let mut ext = Ext::new(&mut overlay, backend, Some(&changes_trie_storage), NeverOffchainExt::new());
ext.clear_prefix(b"ab");
}
overlay.commit_prospective();
@@ -1136,12 +1137,13 @@ mod tests {
#[test]
fn set_child_storage_works() {
let backend = InMemory::<Blake2Hasher>::default().try_into_trie_backend().unwrap();
let mut state = InMemory::<Blake2Hasher>::default();
let backend = state.as_trie_backend().unwrap();
let changes_trie_storage = InMemoryChangesTrieStorage::<Blake2Hasher, u64>::new();
let mut overlay = OverlayedChanges::default();
let mut ext = Ext::new(
&mut overlay,
&backend,
backend,
Some(&changes_trie_storage),
NeverOffchainExt::new()
);
@@ -184,7 +184,7 @@ impl<'a, S, H> Backend<H> for ProvingBackend<'a, S, H>
self.backend.child_storage_root(storage_key, delta)
}
fn try_into_trie_backend(self) -> Option<TrieBackend<Self::TrieBackendStorage, H>> {
fn as_trie_backend(&mut self) -> Option<&TrieBackend<Self::TrieBackendStorage, H>> {
None
}
}
@@ -269,16 +269,16 @@ mod tests {
fn proof_recorded_and_checked() {
let contents = (0..64).map(|i| (None, vec![i], Some(vec![i]))).collect::<Vec<_>>();
let in_memory = InMemory::<Blake2Hasher>::default();
let in_memory = in_memory.update(contents);
let mut in_memory = in_memory.update(contents);
let in_memory_root = in_memory.storage_root(::std::iter::empty()).0;
(0..64).for_each(|i| assert_eq!(in_memory.storage(&[i]).unwrap().unwrap(), vec![i]));
let trie = in_memory.try_into_trie_backend().unwrap();
let trie = in_memory.as_trie_backend().unwrap();
let trie_root = trie.storage_root(::std::iter::empty()).0;
assert_eq!(in_memory_root, trie_root);
(0..64).for_each(|i| assert_eq!(trie.storage(&[i]).unwrap().unwrap(), vec![i]));
let proving = ProvingBackend::new(&trie);
let proving = ProvingBackend::new(trie);
assert_eq!(proving.storage(&[42]).unwrap().unwrap(), vec![42]);
let proof = proving.extract_proof();
@@ -302,7 +302,7 @@ mod tests {
.chain((10..15).map(|i| (Some(own2.clone()), vec![i], Some(vec![i]))))
.collect::<Vec<_>>();
let in_memory = InMemory::<Blake2Hasher>::default();
let in_memory = in_memory.update(contents);
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()))
@@ -320,7 +320,7 @@ mod tests {
vec![i]
));
let trie = in_memory.try_into_trie_backend().unwrap();
let trie = in_memory.as_trie_backend().unwrap();
let trie_root = trie.storage_root(::std::iter::empty()).0;
assert_eq!(in_memory_root, trie_root);
(0..64).for_each(|i| assert_eq!(
@@ -328,7 +328,7 @@ mod tests {
vec![i]
));
let proving = ProvingBackend::new(&trie);
let proving = ProvingBackend::new(trie);
assert_eq!(proving.storage(&[42]).unwrap().unwrap(), vec![42]);
let proof = proving.extract_proof();
@@ -343,7 +343,7 @@ mod tests {
assert_eq!(proof_check.storage(&[41]).unwrap().unwrap(), vec![41]);
assert_eq!(proof_check.storage(&[64]).unwrap(), None);
let proving = ProvingBackend::new(&trie);
let proving = ProvingBackend::new(trie);
assert_eq!(proving.child_storage(&own1[..], &[64]), Ok(Some(vec![64])));
let proof = proving.extract_proof();
@@ -179,7 +179,7 @@ impl<S: TrieBackendStorage<H>, H: Hasher> Backend<H> for TrieBackend<S, H> where
(root, is_default, write_overlay)
}
fn try_into_trie_backend(self) -> Option<TrieBackend<Self::TrieBackendStorage, H>> {
fn as_trie_backend(&mut self) -> Option<&TrieBackend<Self::TrieBackendStorage, H>> {
Some(self)
}
}