Never panic during execution proof check (#3504)

* do not panic during execution proof check

* Update core/state-machine/src/lib.rs

Co-Authored-By: DemiMarie-parity <48690212+DemiMarie-parity@users.noreply.github.com>

* Update core/state-machine/src/lib.rs

Co-Authored-By: DemiMarie-parity <48690212+DemiMarie-parity@users.noreply.github.com>

* Update core/state-machine/src/lib.rs

Co-Authored-By: DemiMarie-parity <48690212+DemiMarie-parity@users.noreply.github.com>

* BackendTrustLevel enum

* up runtime version

* Update core/state-machine/src/lib.rs

Co-Authored-By: DemiMarie-parity <48690212+DemiMarie-parity@users.noreply.github.com>

* Update core/state-machine/src/lib.rs

Co-Authored-By: DemiMarie-parity <48690212+DemiMarie-parity@users.noreply.github.com>

* fixed some grumbles

* Update core/state-machine/src/testing.rs

Co-Authored-By: Gavin Wood <gavin@parity.io>

* Update core/state-machine/src/lib.rs

Co-Authored-By: Gavin Wood <gavin@parity.io>

* mov where

* spaces -> tabs (to restart build)
This commit is contained in:
Svyatoslav Nikolsky
2019-09-11 10:27:32 +03:00
committed by GitHub
parent 9607afd629
commit 010395e620
18 changed files with 254 additions and 78 deletions
+2 -2
View File
@@ -43,7 +43,7 @@ use state_machine::{
DBValue, Backend as StateBackend, ChangesTrieAnchorBlockId, ExecutionStrategy, ExecutionManager,
prove_read, prove_child_read, ChangesTrieRootsStorage, ChangesTrieStorage,
ChangesTrieTransaction, ChangesTrieConfigurationRange, key_changes, key_changes_proof,
OverlayedChanges,
OverlayedChanges, BackendTrustLevel,
};
use executor::{RuntimeVersion, RuntimeInfo};
use consensus::{
@@ -1043,7 +1043,7 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
let get_execution_manager = |execution_strategy: ExecutionStrategy| {
match execution_strategy {
ExecutionStrategy::NativeElseWasm => ExecutionManager::NativeElseWasm,
ExecutionStrategy::AlwaysWasm => ExecutionManager::AlwaysWasm,
ExecutionStrategy::AlwaysWasm => ExecutionManager::AlwaysWasm(BackendTrustLevel::Trusted),
ExecutionStrategy::NativeWhenPossible => ExecutionManager::NativeWhenPossible,
ExecutionStrategy::Both => ExecutionManager::Both(|wasm_result, native_result| {
let header = import_headers.post();
@@ -460,6 +460,32 @@ pub fn check_execution_proof<Header, E, H>(
E: CodeExecutor<H>,
H: Hasher,
H::Out: Ord + 'static,
{
check_execution_proof_with_make_header(
executor,
request,
remote_proof,
|header| <Header as HeaderT>::new(
*header.number() + One::one(),
Default::default(),
Default::default(),
header.hash(),
Default::default(),
),
)
}
fn check_execution_proof_with_make_header<Header, E, H, MakeNextHeader: Fn(&Header) -> Header>(
executor: &E,
request: &RemoteCallRequest<Header>,
remote_proof: Vec<Vec<u8>>,
make_next_header: MakeNextHeader,
) -> ClientResult<Vec<u8>>
where
Header: HeaderT,
E: CodeExecutor<H>,
H: Hasher,
H::Out: Ord + 'static,
{
let local_state_root = request.header.state_root();
let root: H::Out = convert_hash(&local_state_root);
@@ -467,19 +493,13 @@ pub fn check_execution_proof<Header, E, H>(
// prepare execution environment + check preparation proof
let mut changes = OverlayedChanges::default();
let trie_backend = create_proof_check_backend(root, remote_proof)?;
let next_block = <Header as HeaderT>::new(
*request.header.number() + One::one(),
Default::default(),
Default::default(),
request.header.hash(),
Default::default(),
);
let next_header = make_next_header(&request.header);
execution_proof_check_on_trie_backend::<H, _>(
&trie_backend,
&mut changes,
executor,
"Core_initialize_block",
&next_block.encode(),
&next_header.encode(),
None,
)?;
@@ -530,6 +550,43 @@ mod tests {
(remote_result, local_result)
}
fn execute_with_proof_failure(remote_client: &TestClient, at: u64, method: &'static str) {
let remote_block_id = BlockId::Number(at);
let remote_header = remote_client.header(&remote_block_id).unwrap().unwrap();
// 'fetch' execution proof from remote node
let (_, remote_execution_proof) = remote_client.execution_proof(
&remote_block_id,
method,
&[]
).unwrap();
// check remote execution proof locally
let local_executor = NativeExecutor::<test_client::LocalExecutor>::new(None);
let execution_result = check_execution_proof_with_make_header(
&local_executor,
&RemoteCallRequest {
block: test_client::runtime::Hash::default(),
header: remote_header,
method: method.into(),
call_data: vec![],
retry_count: None,
},
remote_execution_proof,
|header| <Header as HeaderT>::new(
at + 1,
Default::default(),
Default::default(),
header.hash(),
header.digest().clone(), // this makes next header wrong
),
);
match execution_result {
Err(crate::error::Error::Execution(_)) => (),
_ => panic!("Unexpected execution result: {:?}", execution_result),
}
}
// prepare remote client
let remote_client = test_client::new();
for i in 1u32..3u32 {
@@ -546,15 +603,24 @@ mod tests {
let (remote, local) = execute(&remote_client, 0, "Core_version");
assert_eq!(remote, local);
let (remote, local) = execute(&remote_client, 2, "Core_version");
assert_eq!(remote, local);
// check method that requires environment
let (_, block) = execute(&remote_client, 0, "BlockBuilder_finalize_block");
let local_block: Header = Decode::decode(&mut &block[..]).unwrap();
assert_eq!(local_block.number, 1);
// check method that requires environment
let (_, block) = execute(&remote_client, 2, "BlockBuilder_finalize_block");
let local_block: Header = Decode::decode(&mut &block[..]).unwrap();
assert_eq!(local_block.number, 3);
// check that proof check doesn't panic even if proof is incorrect AND no panic handler is set
execute_with_proof_failure(&remote_client, 2, "Core_version");
// check that proof check doesn't panic even if proof is incorrect AND panic handler is set
panic_handler::set("TEST");
execute_with_proof_failure(&remote_client, 2, "Core_version");
}
#[test]