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 1 commit
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};

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are the {s needed here?

try_read_overlay_value(self.overlay, self.backend, key)
.expect("Externalities not allowed to fail within runtime")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can probably just do .map_err(|e| error!(target: "state-machine", "Externalities call failed! {:?}", e)).ok().and_then(|inner| inner), which then will at least avoid the panic. better would be to shuffle the Err through into the wasm interpreter so the execution can be early-exited. not sure if that's possible yet, though.

}

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