Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions substrate/state-machine/src/ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,9 @@ where
H::Out: Ord + Encodable
{
fn storage(&self, key: &[u8]) -> Option<Vec<u8>> {
self.overlay.storage(key).map(|x| x.map(|x| x.to_vec())).unwrap_or_else(||
self.backend.storage(key).expect("Externalities not allowed to fail within runtime"))
use try_read_overlay_value;
try_read_overlay_value(self.overlay, self.backend, key)
.expect("Externalities not allowed to fail within runtime")
}

fn exists_storage(&self, key: &[u8]) -> bool {
Expand Down
53 changes: 43 additions & 10 deletions substrate/state-machine/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,8 @@ impl Error for ExecutionError {}
/// and as a transition away from the pre-existing framework.
#[derive(Debug, Eq, PartialEq)]
pub enum ExecutionError {
/// Backend error.
Backend(String),
/// The entry `:code` doesn't exist in storage so there's no way we can execute anything.
CodeEntryDoesNotExist,
/// Backend is incompatible with execution proof generation process.
Expand Down Expand Up @@ -324,11 +326,13 @@ where
let strategy: ExecutionStrategy = (&manager).into();

// make a copy.
let code = ext::Ext::new(overlay, backend).storage(b":code")
let code = try_read_overlay_value(overlay, backend, b":code")
.map_err(|err| Box::new(ExecutionError::Backend(format!("{}", err))) as Box<Error>)?
.ok_or_else(|| Box::new(ExecutionError::CodeEntryDoesNotExist) as Box<Error>)?
.to_vec();

let heap_pages = ext::Ext::new(overlay, backend).storage(b":heappages")
let heap_pages = try_read_overlay_value(overlay, backend, b":heappages")
.map_err(|err| Box::new(ExecutionError::Backend(format!("{}", err))) as Box<Error>)?
.and_then(|v| u64::decode(&mut &v[..])).unwrap_or(8) as usize;

let result = {
Expand Down Expand Up @@ -435,10 +439,10 @@ pub fn execution_proof_check<H, C, Exec>(
call_data: &[u8],
) -> Result<(Vec<u8>, memorydb::MemoryDB<H>), Box<Error>>
where
H: Hasher,
C: NodeCodec<H>,
Exec: CodeExecutor<H>,
H::Out: Ord + Encodable + HeapSizeOf,
H: Hasher,
C: NodeCodec<H>,
Exec: CodeExecutor<H>,
H::Out: Ord + Encodable + HeapSizeOf,
{
let backend = proving_backend::create_proof_check_backend::<H, C>(root.into(), proof)?;
execute::<H, C, _, _>(&backend, overlay, exec, method, call_data, ExecutionStrategy::NativeWhenPossible)
Expand Down Expand Up @@ -477,6 +481,19 @@ where
backend.storage(key).map_err(|e| Box::new(e) as Box<Error>)
}

/// Reads storage value from overlay or from the backend.
pub(crate) fn try_read_overlay_value<H, C, B>(overlay: &OverlayedChanges, backend: &B, key: &[u8]) -> Result<Option<Vec<u8>>, B::Error>
where
H: Hasher,
C: NodeCodec<H>,
B: Backend<H, C>,
{
match overlay.storage(key).map(|x| x.map(|x| x.to_vec())) {
Some(value) => Ok(value),
None => backend.storage(key),
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down Expand Up @@ -578,7 +595,7 @@ mod tests {
#[test]
fn execute_works() {
assert_eq!(execute(
&trie_backend::tests::test_trie(),
&trie_backend::tests::test_trie(true),
&mut Default::default(),
&DummyCodeExecutor {
native_available: true,
Expand All @@ -591,11 +608,27 @@ mod tests {
).unwrap().0, vec![66]);
}

#[test]
fn execute_should_not_panic_when_there_is_no_code_entry() {
assert!(execute(
&trie_backend::tests::test_trie(false),
&mut Default::default(),
&DummyCodeExecutor {
native_available: true,
native_succeeds: true,
fallback_succeeds: true,
},
"test",
&[],
ExecutionStrategy::NativeWhenPossible
).is_err());
}

#[test]
fn dual_execution_strategy_detects_consensus_failure() {
let mut consensus_failed = false;
assert!(execute_using_consensus_failure_handler(
&trie_backend::tests::test_trie(),
&trie_backend::tests::test_trie(true),
&mut Default::default(),
&DummyCodeExecutor {
native_available: true,
Expand All @@ -622,7 +655,7 @@ mod tests {
};

// fetch execution proof from 'remote' full node
let remote_backend = trie_backend::tests::test_trie();
let remote_backend = trie_backend::tests::test_trie(true);
let remote_root = remote_backend.storage_root(::std::iter::empty()).0;
let (remote_result, remote_proof, _) = prove_execution(remote_backend,
&mut Default::default(), &executor, "test", &[]).unwrap();
Expand Down Expand Up @@ -679,7 +712,7 @@ mod tests {
#[test]
fn prove_read_and_proof_check_works() {
// fetch read proof from 'remote' full node
let remote_backend = trie_backend::tests::test_trie();
let remote_backend = trie_backend::tests::test_trie(true);
let remote_root = remote_backend.storage_root(::std::iter::empty()).0;
let remote_proof = prove_read(remote_backend, b"value2").unwrap().1;
// check proof locally
Expand Down
4 changes: 2 additions & 2 deletions substrate/state-machine/src/proving_backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ mod tests {
use primitives::{KeccakHasher, RlpCodec};

fn test_proving() -> ProvingBackend<KeccakHasher, RlpCodec> {
ProvingBackend::new(test_trie())
ProvingBackend::new(test_trie(true))
}

#[test]
Expand All @@ -148,7 +148,7 @@ mod tests {

#[test]
fn passes_throgh_backend_calls() {
let trie_backend = test_trie();
let trie_backend = test_trie(true);
let proving_backend = test_proving();
assert_eq!(trie_backend.storage(b"key").unwrap(), proving_backend.storage(b"key").unwrap());
assert_eq!(trie_backend.pairs(), proving_backend.pairs());
Expand Down
26 changes: 14 additions & 12 deletions substrate/state-machine/src/trie_backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -282,40 +282,42 @@ pub mod tests {
use std::collections::HashSet;
use primitives::{KeccakHasher, RlpCodec, H256};

fn test_db() -> (MemoryDB<KeccakHasher>, H256) {
fn test_db(has_code: bool) -> (MemoryDB<KeccakHasher>, H256) {
let mut root = H256::default();
let mut mdb = MemoryDB::<KeccakHasher>::new();
{
let mut trie = TrieDBMut::<_, RlpCodec>::new(&mut mdb, &mut root);
trie.insert(b"key", b"value").expect("insert failed");
trie.insert(b"value1", &[42]).expect("insert failed");
trie.insert(b"value2", &[24]).expect("insert failed");
trie.insert(b":code", b"return 42").expect("insert failed");
if has_code {
trie.insert(b":code", b"return 42").expect("insert failed");
}
for i in 128u8..255u8 {
trie.insert(&[i], &[i]).unwrap();
}
}
(mdb, root)
}

pub(crate) fn test_trie() -> TrieBackend<KeccakHasher, RlpCodec> {
let (mdb, root) = test_db();
pub(crate) fn test_trie(has_code: bool) -> TrieBackend<KeccakHasher, RlpCodec> {
let (mdb, root) = test_db(has_code);
TrieBackend::with_memorydb(mdb, root)
}

#[test]
fn read_from_storage_returns_some() {
assert_eq!(test_trie().storage(b"key").unwrap(), Some(b"value".to_vec()));
assert_eq!(test_trie(true).storage(b"key").unwrap(), Some(b"value".to_vec()));
}

#[test]
fn read_from_storage_returns_none() {
assert_eq!(test_trie().storage(b"non-existing-key").unwrap(), None);
assert_eq!(test_trie(true).storage(b"non-existing-key").unwrap(), None);
}

#[test]
fn pairs_are_not_empty_on_non_empty_storage() {
assert!(!test_trie().pairs().is_empty());
assert!(!test_trie(true).pairs().is_empty());
}

#[test]
Expand All @@ -329,24 +331,24 @@ pub mod tests {

#[test]
fn storage_root_is_non_default() {
assert!(test_trie().storage_root(::std::iter::empty()).0 != H256([0; 32]));
assert!(test_trie(true).storage_root(::std::iter::empty()).0 != H256([0; 32]));
}

#[test]
fn storage_root_transaction_is_empty() {
assert!(test_trie().storage_root(::std::iter::empty()).1.drain().is_empty());
assert!(test_trie(true).storage_root(::std::iter::empty()).1.drain().is_empty());
}

#[test]
fn storage_root_transaction_is_non_empty() {
let (new_root, mut tx) = test_trie().storage_root(vec![(b"new-key".to_vec(), Some(b"new-value".to_vec()))]);
let (new_root, mut tx) = test_trie(true).storage_root(vec![(b"new-key".to_vec(), Some(b"new-value".to_vec()))]);
assert!(!tx.drain().is_empty());
assert!(new_root != test_trie().storage_root(::std::iter::empty()).0);
assert!(new_root != test_trie(true).storage_root(::std::iter::empty()).0);
}

#[test]
fn prefix_walking_works() {
let trie = test_trie();
let trie = test_trie(true);

let mut seen = HashSet::new();
trie.for_keys_with_prefix(b"value", |key| {
Expand Down