// Copyright 2017-2019 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see .
//! Substrate state machine implementation.
#![warn(missing_docs)]
use std::{fmt, result, collections::HashMap, panic::UnwindSafe, marker::PhantomData};
use log::{warn, trace};
use hash_db::Hasher;
use codec::{Decode, Encode};
use primitives::{
storage::well_known_keys, NativeOrEncoded, NeverNativeValue, offchain::OffchainExt,
traits::{KeystoreExt, CodeExecutor}, hexdisplay::HexDisplay, hash::H256,
};
use overlayed_changes::OverlayedChangeSet;
use externalities::Extensions;
pub mod backend;
mod changes_trie;
mod error;
mod ext;
mod testing;
mod basic;
mod overlayed_changes;
mod proving_backend;
mod trie_backend;
mod trie_backend_essence;
pub use trie::{trie_types::{Layout, TrieDBMut}, TrieMut, DBValue, MemoryDB};
pub use testing::TestExternalities;
pub use basic::BasicExternalities;
pub use ext::Ext;
pub use backend::Backend;
pub use changes_trie::{
AnchorBlockId as ChangesTrieAnchorBlockId,
Storage as ChangesTrieStorage,
RootsStorage as ChangesTrieRootsStorage,
InMemoryStorage as InMemoryChangesTrieStorage,
BuildCache as ChangesTrieBuildCache,
CacheAction as ChangesTrieCacheAction,
ConfigurationRange as ChangesTrieConfigurationRange,
key_changes, key_changes_proof, key_changes_proof_check,
prune as prune_changes_tries,
oldest_non_pruned_trie as oldest_non_pruned_changes_trie,
};
pub use overlayed_changes::OverlayedChanges;
pub use proving_backend::{
create_proof_check_backend, create_proof_check_backend_storage,
Recorder as ProofRecorder, ProvingBackend,
};
pub use trie_backend_essence::{TrieBackendStorage, Storage};
pub use trie_backend::TrieBackend;
pub use error::{Error, ExecutionError};
type CallResult = Result, E>;
type DefaultHandler = fn(CallResult, CallResult) -> CallResult;
/// Type of changes trie transaction.
pub type ChangesTrieTransaction = (
MemoryDB,
ChangesTrieCacheAction<::Out, N>,
);
/// Strategy for executing a call into the runtime.
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum ExecutionStrategy {
/// Execute with the native equivalent if it is compatible with the given wasm module; otherwise fall back to the wasm.
NativeWhenPossible,
/// Use the given wasm module.
AlwaysWasm,
/// Run with both the wasm and the native variant (if compatible). Report any discrepency as an error.
Both,
/// First native, then if that fails or is not possible, wasm.
NativeElseWasm,
}
/// Storage backend trust level.
#[derive(Debug, Clone)]
pub enum BackendTrustLevel {
/// Panics from trusted backends are considered justified, and never caught.
Trusted,
/// Panics from untrusted backend are caught and interpreted as runtime error.
/// Untrusted backend may be missing some parts of the trie, so panics are not considered
/// fatal.
Untrusted,
}
/// Like `ExecutionStrategy` only it also stores a handler in case of consensus failure.
#[derive(Clone)]
pub enum ExecutionManager {
/// Execute with the native equivalent if it is compatible with the given wasm module; otherwise fall back to the wasm.
NativeWhenPossible,
/// Use the given wasm module. The backend on which code is executed code could be
/// trusted to provide all storage or not (i.e. the light client cannot be trusted to provide
/// for all storage queries since the storage entries it has come from an external node).
AlwaysWasm(BackendTrustLevel),
/// Run with both the wasm and the native variant (if compatible). Call `F` in the case of any discrepency.
Both(F),
/// First native, then if that fails or is not possible, wasm.
NativeElseWasm,
}
impl<'a, F> From<&'a ExecutionManager> for ExecutionStrategy {
fn from(s: &'a ExecutionManager) -> Self {
match *s {
ExecutionManager::NativeWhenPossible => ExecutionStrategy::NativeWhenPossible,
ExecutionManager::AlwaysWasm(_) => ExecutionStrategy::AlwaysWasm,
ExecutionManager::NativeElseWasm => ExecutionStrategy::NativeElseWasm,
ExecutionManager::Both(_) => ExecutionStrategy::Both,
}
}
}
impl ExecutionStrategy {
/// Gets the corresponding manager for the execution strategy.
pub fn get_manager(
self,
) -> ExecutionManager> {
match self {
ExecutionStrategy::AlwaysWasm => ExecutionManager::AlwaysWasm(BackendTrustLevel::Trusted),
ExecutionStrategy::NativeWhenPossible => ExecutionManager::NativeWhenPossible,
ExecutionStrategy::NativeElseWasm => ExecutionManager::NativeElseWasm,
ExecutionStrategy::Both => ExecutionManager::Both(|wasm_result, native_result| {
warn!(
"Consensus error between wasm {:?} and native {:?}. Using wasm.",
wasm_result,
native_result
);
wasm_result
}),
}
}
}
/// Evaluate to ExecutionManager::NativeElseWasm, without having to figure out the type.
pub fn native_else_wasm() -> ExecutionManager> {
ExecutionManager::NativeElseWasm
}
/// Evaluate to ExecutionManager::AlwaysWasm with trusted backend, without having to figure out the type.
fn always_wasm() -> ExecutionManager> {
ExecutionManager::AlwaysWasm(BackendTrustLevel::Trusted)
}
/// Evaluate ExecutionManager::AlwaysWasm with untrusted backend, without having to figure out the type.
fn always_untrusted_wasm() -> ExecutionManager> {
ExecutionManager::AlwaysWasm(BackendTrustLevel::Untrusted)
}
/// The substrate state machine.
pub struct StateMachine<'a, B, H, N, T, Exec> where H: Hasher, B: Backend {
backend: &'a B,
exec: &'a Exec,
method: &'a str,
call_data: &'a [u8],
overlay: &'a mut OverlayedChanges,
extensions: Extensions,
changes_trie_storage: Option<&'a T>,
_marker: PhantomData<(H, N)>,
}
impl<'a, B, H, N, T, Exec> StateMachine<'a, B, H, N, T, Exec> where
H: Hasher,
Exec: CodeExecutor,
B: Backend,
T: ChangesTrieStorage,
N: crate::changes_trie::BlockNumber,
{
/// Creates new substrate state machine.
pub fn new(
backend: &'a B,
changes_trie_storage: Option<&'a T>,
offchain_ext: Option,
overlay: &'a mut OverlayedChanges,
exec: &'a Exec,
method: &'a str,
call_data: &'a [u8],
keystore: Option,
) -> Self {
let mut extensions = Extensions::new();
if let Some(keystore) = keystore {
extensions.register(keystore);
}
if let Some(offchain) = offchain_ext {
extensions.register(offchain);
}
Self {
backend,
exec,
method,
call_data,
extensions,
overlay,
changes_trie_storage,
_marker: PhantomData,
}
}
/// Execute a call using the given state backend, overlayed changes, and call executor.
/// Produces a state-backend-specific "transaction" which can be used to apply the changes
/// to the backing store, such as the disk.
///
/// On an error, no prospective changes are written to the overlay.
///
/// Note: changes to code will be in place if this call is made again. For running partial
/// blocks (e.g. a transaction at a time), ensure a different method is used.
pub fn execute(&mut self, strategy: ExecutionStrategy) -> Result<
(Vec, (B::Transaction, H::Out), Option>),
Box,
> {
// We are not giving a native call and thus we are sure that the result can never be a native
// value.
self.execute_using_consensus_failure_handler::<_, NeverNativeValue, fn() -> _>(
strategy.get_manager(),
true,
None,
)
.map(|(result, storage_tx, changes_tx)| (
result.into_encoded(),
storage_tx.expect("storage_tx is always computed when compute_tx is true; qed"),
changes_tx,
))
}
fn execute_aux(
&mut self,
compute_tx: bool,
use_native: bool,
native_call: Option,
) -> (
CallResult,
bool,
Option<(B::Transaction, H::Out)>,
Option>,
) where
R: Decode + Encode + PartialEq,
NC: FnOnce() -> result::Result + UnwindSafe,
{
let mut ext = Ext::new(
self.overlay,
self.backend,
self.changes_trie_storage.clone(),
Some(&mut self.extensions),
);
let id = ext.id;
trace!(
target: "state-trace", "{:04x}: Call {} at {:?}. Input={:?}",
id,
self.method,
self.backend,
HexDisplay::from(&self.call_data),
);
let (result, was_native) = self.exec.call(
&mut ext,
self.method,
self.call_data,
use_native,
native_call,
);
let (storage_delta, changes_delta) = if compute_tx {
let (storage_delta, changes_delta) = ext.transaction();
(Some(storage_delta), changes_delta)
} else {
(None, None)
};
trace!(
target: "state-trace", "{:04x}: Return. Native={:?}, Result={:?}",
id,
was_native,
result,
);
(result, was_native, storage_delta, changes_delta)
}
fn execute_call_with_both_strategy(
&mut self,
compute_tx: bool,
mut native_call: Option,
orig_prospective: OverlayedChangeSet,
on_consensus_failure: Handler,
) -> (
CallResult,
Option<(B::Transaction, H::Out)>,
Option>,
) where
R: Decode + Encode + PartialEq,
NC: FnOnce() -> result::Result + UnwindSafe,
Handler: FnOnce(
CallResult,
CallResult,
) -> CallResult
{
let (result, was_native, storage_delta, changes_delta) = self.execute_aux(
compute_tx,
true,
native_call.take(),
);
if was_native {
self.overlay.prospective = orig_prospective.clone();
let (wasm_result, _, wasm_storage_delta, wasm_changes_delta) = self.execute_aux(
compute_tx,
false,
native_call,
);
if (result.is_ok() && wasm_result.is_ok()
&& result.as_ref().ok() == wasm_result.as_ref().ok())
|| result.is_err() && wasm_result.is_err()
{
(result, storage_delta, changes_delta)
} else {
(on_consensus_failure(wasm_result, result), wasm_storage_delta, wasm_changes_delta)
}
} else {
(result, storage_delta, changes_delta)
}
}
fn execute_call_with_native_else_wasm_strategy(
&mut self,
compute_tx: bool,
mut native_call: Option,
orig_prospective: OverlayedChangeSet,
) -> (
CallResult,
Option<(B::Transaction, H::Out)>,
Option>,
) where
R: Decode + Encode + PartialEq,
NC: FnOnce() -> result::Result + UnwindSafe,
{
let (result, was_native, storage_delta, changes_delta) = self.execute_aux(
compute_tx,
true,
native_call.take(),
);
if !was_native || result.is_ok() {
(result, storage_delta, changes_delta)
} else {
self.overlay.prospective = orig_prospective.clone();
let (wasm_result, _, wasm_storage_delta, wasm_changes_delta) = self.execute_aux(
compute_tx,
false,
native_call,
);
(wasm_result, wasm_storage_delta, wasm_changes_delta)
}
}
/// Execute a call using the given state backend, overlayed changes, and call executor.
/// Produces a state-backend-specific "transaction" which can be used to apply the changes
/// to the backing store, such as the disk.
///
/// On an error, no prospective changes are written to the overlay.
///
/// Note: changes to code will be in place if this call is made again. For running partial
/// blocks (e.g. a transaction at a time), ensure a different method is used.
pub fn execute_using_consensus_failure_handler(
&mut self,
manager: ExecutionManager,
compute_tx: bool,
mut native_call: Option,
) -> Result<(
NativeOrEncoded,
Option<(B::Transaction, H::Out)>,
Option>,
), Box> where
R: Decode + Encode + PartialEq,
NC: FnOnce() -> result::Result + UnwindSafe,
Handler: FnOnce(
CallResult,
CallResult,
) -> CallResult
{
// read changes trie configuration. The reason why we're doing it here instead of the
// `OverlayedChanges` constructor is that we need proofs for this read as a part of
// proof-of-execution on light clients. And the proof is recorded by the backend which
// is created after OverlayedChanges
let init_overlay = |overlay: &mut OverlayedChanges, final_check: bool, backend: &B| {
let changes_trie_config = try_read_overlay_value(
overlay,
backend,
well_known_keys::CHANGES_TRIE_CONFIG
)?;
set_changes_trie_config(overlay, changes_trie_config, final_check)
};
init_overlay(self.overlay, false, &self.backend)?;
let result = {
let orig_prospective = self.overlay.prospective.clone();
let (result, storage_delta, changes_delta) = match manager {
ExecutionManager::Both(on_consensus_failure) => {
self.execute_call_with_both_strategy(
compute_tx,
native_call.take(),
orig_prospective,
on_consensus_failure,
)
},
ExecutionManager::NativeElseWasm => {
self.execute_call_with_native_else_wasm_strategy(
compute_tx,
native_call.take(),
orig_prospective,
)
},
ExecutionManager::AlwaysWasm(trust_level) => {
let _abort_guard = match trust_level {
BackendTrustLevel::Trusted => None,
BackendTrustLevel::Untrusted => Some(panic_handler::AbortGuard::never_abort()),
};
let res = self.execute_aux(compute_tx, false, native_call);
(res.0, res.2, res.3)
},
ExecutionManager::NativeWhenPossible => {
let res = self.execute_aux(compute_tx, true, native_call);
(res.0, res.2, res.3)
},
};
result.map(move |out| (out, storage_delta, changes_delta))
};
if result.is_ok() {
init_overlay(self.overlay, true, self.backend)?;
}
result.map_err(|e| Box::new(e) as _)
}
}
/// Prove execution using the given state backend, overlayed changes, and call executor.
pub fn prove_execution(
mut backend: B,
overlay: &mut OverlayedChanges,
exec: &Exec,
method: &str,
call_data: &[u8],
keystore: Option,
) -> Result<(Vec, Vec>), Box>
where
B: Backend,
H: Hasher,
Exec: CodeExecutor,
{
let trie_backend = backend.as_trie_backend()
.ok_or_else(|| Box::new(ExecutionError::UnableToGenerateProof) as Box)?;
prove_execution_on_trie_backend(trie_backend, overlay, exec, method, call_data, keystore)
}
/// Prove execution using the given trie backend, overlayed changes, and call executor.
/// Produces a state-backend-specific "transaction" which can be used to apply the changes
/// to the backing store, such as the disk.
/// Execution proof is the set of all 'touched' storage DBValues from the backend.
///
/// On an error, no prospective changes are written to the overlay.
///
/// Note: changes to code will be in place if this call is made again. For running partial
/// blocks (e.g. a transaction at a time), ensure a different method is used.
pub fn prove_execution_on_trie_backend(
trie_backend: &TrieBackend,
overlay: &mut OverlayedChanges,
exec: &Exec,
method: &str,
call_data: &[u8],
keystore: Option,
) -> Result<(Vec, Vec>), Box>
where
S: trie_backend_essence::TrieBackendStorage,
H: Hasher,
Exec: CodeExecutor,
{
let proving_backend = proving_backend::ProvingBackend::new(trie_backend);
let mut sm = StateMachine::<_, H, _, InMemoryChangesTrieStorage, Exec>::new(
&proving_backend, None, None, overlay, exec, method, call_data, keystore,
);
let (result, _, _) = sm.execute_using_consensus_failure_handler::<_, NeverNativeValue, fn() -> _>(
always_wasm(),
false,
None,
)?;
let proof = sm.backend.extract_proof();
Ok((result.into_encoded(), proof))
}
/// Check execution proof, generated by `prove_execution` call.
pub fn execution_proof_check(
root: H::Out,
proof: Vec>,
overlay: &mut OverlayedChanges,
exec: &Exec,
method: &str,
call_data: &[u8],
keystore: Option,
) -> Result, Box>
where
H: Hasher,
Exec: CodeExecutor,
H::Out: Ord + 'static,
{
let trie_backend = create_proof_check_backend::(root.into(), proof)?;
execution_proof_check_on_trie_backend(&trie_backend, overlay, exec, method, call_data, keystore)
}
/// Check execution proof on proving backend, generated by `prove_execution` call.
pub fn execution_proof_check_on_trie_backend(
trie_backend: &TrieBackend, H>,
overlay: &mut OverlayedChanges,
exec: &Exec,
method: &str,
call_data: &[u8],
keystore: Option,
) -> Result, Box>
where
H: Hasher,
Exec: CodeExecutor,
{
let mut sm = StateMachine::<_, H, _, InMemoryChangesTrieStorage, Exec>::new(
trie_backend, None, None, overlay, exec, method, call_data, keystore,
);
sm.execute_using_consensus_failure_handler::<_, NeverNativeValue, fn() -> _>(
always_untrusted_wasm(),
false,
None,
).map(|(result, _, _)| result.into_encoded())
}
/// Generate storage read proof.
pub fn prove_read(
mut backend: B,
keys: I,
) -> Result>, Box>
where
B: Backend,
H: Hasher,
H::Out: Ord,
I: IntoIterator,
I::Item: AsRef<[u8]>,
{
let trie_backend = backend.as_trie_backend()
.ok_or_else(
|| Box::new(ExecutionError::UnableToGenerateProof) as Box
)?;
prove_read_on_trie_backend(trie_backend, keys)
}
/// Generate child storage read proof.
pub fn prove_child_read(
mut backend: B,
storage_key: &[u8],
keys: I,
) -> Result>, Box>
where
B: Backend,
H: Hasher,
H::Out: Ord,
I: IntoIterator,
I::Item: AsRef<[u8]>,
{
let trie_backend = backend.as_trie_backend()
.ok_or_else(|| Box::new(ExecutionError::UnableToGenerateProof) as Box)?;
prove_child_read_on_trie_backend(trie_backend, storage_key, keys)
}
/// Generate storage read proof on pre-created trie backend.
pub fn prove_read_on_trie_backend(
trie_backend: &TrieBackend,
keys: I,
) -> Result>, Box>
where
S: trie_backend_essence::TrieBackendStorage,
H: Hasher,
H::Out: Ord,
I: IntoIterator,
I::Item: AsRef<[u8]>,
{
let proving_backend = proving_backend::ProvingBackend::<_, H>::new(trie_backend);
for key in keys.into_iter() {
proving_backend
.storage(key.as_ref())
.map_err(|e| Box::new(e) as Box)?;
}
Ok(proving_backend.extract_proof())
}
/// Generate storage read proof on pre-created trie backend.
pub fn prove_child_read_on_trie_backend(
trie_backend: &TrieBackend,
storage_key: &[u8],
keys: I,
) -> Result>, Box>
where
S: trie_backend_essence::TrieBackendStorage,
H: Hasher,
H::Out: Ord,
I: IntoIterator,
I::Item: AsRef<[u8]>,
{
let proving_backend = proving_backend::ProvingBackend::<_, H>::new(trie_backend);
for key in keys.into_iter() {
proving_backend
.child_storage(storage_key, key.as_ref())
.map_err(|e| Box::new(e) as Box)?;
}
Ok(proving_backend.extract_proof())
}
/// Check storage read proof, generated by `prove_read` call.
pub fn read_proof_check(
root: H::Out,
proof: Vec>,
keys: I,
) -> Result, Option>>, Box>
where
H: Hasher,
H::Out: Ord,
I: IntoIterator,
I::Item: AsRef<[u8]>,
{
let proving_backend = create_proof_check_backend::(root, proof)?;
let mut result = HashMap::new();
for key in keys.into_iter() {
let value = read_proof_check_on_proving_backend(&proving_backend, key.as_ref())?;
result.insert(key.as_ref().to_vec(), value);
}
Ok(result)
}
/// Check child storage read proof, generated by `prove_child_read` call.
pub fn read_child_proof_check(
root: H::Out,
proof: Vec>,
storage_key: &[u8],
keys: I,
) -> Result, Option>>, Box>
where
H: Hasher,
H::Out: Ord,
I: IntoIterator,
I::Item: AsRef<[u8]>,
{
let proving_backend = create_proof_check_backend::(root, proof)?;
let mut result = HashMap::new();
for key in keys.into_iter() {
let value = read_child_proof_check_on_proving_backend(
&proving_backend,
storage_key,
key.as_ref(),
)?;
result.insert(key.as_ref().to_vec(), value);
}
Ok(result)
}
/// Check storage read proof on pre-created proving backend.
pub fn read_proof_check_on_proving_backend(
proving_backend: &TrieBackend, H>,
key: &[u8],
) -> Result