// Copyright 2017-2020 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};
use log::{warn, trace};
use hash_db::Hasher;
use codec::{Decode, Encode, Codec};
use sp_core::{
storage::ChildInfo, NativeOrEncoded, NeverNativeValue, hexdisplay::HexDisplay,
traits::{CodeExecutor, CallInWasmExt, RuntimeCode},
};
use overlayed_changes::OverlayedChangeSet;
use sp_externalities::Extensions;
pub mod backend;
mod in_memory_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;
mod stats;
pub use sp_trie::{trie_types::{Layout, TrieDBMut}, StorageProof, 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,
State as ChangesTrieState,
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, key_changes_proof_check_with_db,
prune as prune_changes_tries,
disabled_state as disabled_changes_trie_state,
BlockNumber as ChangesTrieBlockNumber,
};
pub use overlayed_changes::{
OverlayedChanges, StorageChanges, StorageTransactionCache, StorageKey, StorageValue,
StorageCollection, ChildStorageCollection,
};
pub use proving_backend::{
create_proof_check_backend, ProofRecorder, ProvingBackend, ProvingBackendRecorder,
};
pub use trie_backend_essence::{TrieBackendStorage, Storage};
pub use trie_backend::TrieBackend;
pub use error::{Error, ExecutionError};
pub use in_memory_backend::InMemory as InMemoryBackend;
pub use stats::{UsageInfo, UsageUnit, StateMachineStats};
pub use sp_core::traits::CloneableSpawn;
type CallResult = Result, E>;
/// Default handler of the execution manager.
pub 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 discrepancy 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 discrepancy.
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,
);
warn!(" Native result {:?}", native_result);
warn!(" Wasm result {:?}", wasm_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, Exec>
where
H: Hasher,
B: Backend,
N: ChangesTrieBlockNumber,
{
backend: &'a B,
exec: &'a Exec,
method: &'a str,
call_data: &'a [u8],
overlay: &'a mut OverlayedChanges,
extensions: Extensions,
changes_trie_state: Option>,
storage_transaction_cache: Option<&'a mut StorageTransactionCache>,
runtime_code: &'a RuntimeCode<'a>,
stats: StateMachineStats,
}
impl<'a, B, H, N, Exec> Drop for StateMachine<'a, B, H, N, Exec> where
H: Hasher,
B: Backend,
N: ChangesTrieBlockNumber,
{
fn drop(&mut self) {
self.backend.register_overlay_stats(&self.stats);
}
}
impl<'a, B, H, N, Exec> StateMachine<'a, B, H, N, Exec> where
H: Hasher,
H::Out: Ord + 'static + codec::Codec,
Exec: CodeExecutor + Clone + 'static,
B: Backend,
N: crate::changes_trie::BlockNumber,
{
/// Creates new substrate state machine.
pub fn new(
backend: &'a B,
changes_trie_state: Option>,
overlay: &'a mut OverlayedChanges,
exec: &'a Exec,
method: &'a str,
call_data: &'a [u8],
mut extensions: Extensions,
runtime_code: &'a RuntimeCode,
spawn_handle: Box,
) -> Self {
extensions.register(CallInWasmExt::new(exec.clone()));
extensions.register(sp_core::traits::TaskExecutorExt::new(spawn_handle));
Self {
backend,
exec,
method,
call_data,
extensions,
overlay,
changes_trie_state,
storage_transaction_cache: None,
runtime_code,
stats: StateMachineStats::default(),
}
}
/// Use given `cache` as storage transaction cache.
///
/// The cache will be used to cache storage transactions that can be build while executing a
/// function in the runtime. For example, when calculating the storage root a transaction is
/// build that will be cached.
pub fn with_storage_transaction_cache(
mut self,
cache: Option<&'a mut StorageTransactionCache>,
) -> Self {
self.storage_transaction_cache = cache;
self
}
/// Execute a call using the given state backend, overlayed changes, and call executor.
///
/// 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.
///
/// Returns the SCALE encoded result of the executed function.
pub fn execute(&mut self, strategy: ExecutionStrategy) -> Result, 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(),
None,
).map(NativeOrEncoded::into_encoded)
}
fn execute_aux(
&mut self,
use_native: bool,
native_call: Option,
) -> (
CallResult,
bool,
) where
R: Decode + Encode + PartialEq,
NC: FnOnce() -> result::Result + UnwindSafe,
{
let mut cache = StorageTransactionCache::default();
let cache = match self.storage_transaction_cache.as_mut() {
Some(cache) => cache,
None => &mut cache,
};
let mut ext = Ext::new(
self.overlay,
cache,
self.backend,
self.changes_trie_state.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.runtime_code,
self.method,
self.call_data,
use_native,
native_call,
);
trace!(
target: "state-trace", "{:04x}: Return. Native={:?}, Result={:?}",
id,
was_native,
result,
);
(result, was_native)
}
fn execute_call_with_both_strategy(
&mut self,
mut native_call: Option,
orig_prospective: OverlayedChangeSet,
on_consensus_failure: Handler,
) -> CallResult
where
R: Decode + Encode + PartialEq,
NC: FnOnce() -> result::Result + UnwindSafe,
Handler: FnOnce(
CallResult,
CallResult,
) -> CallResult
{
let (result, was_native) = self.execute_aux(true, native_call.take());
if was_native {
self.overlay.prospective = orig_prospective.clone();
let (wasm_result, _) = self.execute_aux(
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
} else {
on_consensus_failure(wasm_result, result)
}
} else {
result
}
}
fn execute_call_with_native_else_wasm_strategy(
&mut self,
mut native_call: Option,
orig_prospective: OverlayedChangeSet,
) -> CallResult
where
R: Decode + Encode + PartialEq,
NC: FnOnce() -> result::Result + UnwindSafe,
{
let (result, was_native) = self.execute_aux(
true,
native_call.take(),
);
if !was_native || result.is_ok() {
result
} else {
self.overlay.prospective = orig_prospective.clone();
let (wasm_result, _) = self.execute_aux(
false,
native_call,
);
wasm_result
}
}
/// Execute a call using the given state backend, overlayed changes, and call executor.
///
/// 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.
///
/// Returns the result of the executed function either in native representation `R` or
/// in SCALE encoded representation.
pub fn execute_using_consensus_failure_handler(
&mut self,
manager: ExecutionManager,
mut native_call: Option,
) -> Result, Box>
where
R: Decode + Encode + PartialEq,
NC: FnOnce() -> result::Result + UnwindSafe,
Handler: FnOnce(
CallResult,
CallResult,
) -> CallResult
{
let changes_tries_enabled = self.changes_trie_state.is_some();
self.overlay.set_collect_extrinsics(changes_tries_enabled);
let result = {
let orig_prospective = self.overlay.prospective.clone();
match manager {
ExecutionManager::Both(on_consensus_failure) => {
self.execute_call_with_both_strategy(
native_call.take(),
orig_prospective,
on_consensus_failure,
)
},
ExecutionManager::NativeElseWasm => {
self.execute_call_with_native_else_wasm_strategy(
native_call.take(),
orig_prospective,
)
},
ExecutionManager::AlwaysWasm(trust_level) => {
let _abort_guard = match trust_level {
BackendTrustLevel::Trusted => None,
BackendTrustLevel::Untrusted => Some(sp_panic_handler::AbortGuard::never_abort()),
};
self.execute_aux(false, native_call).0
},
ExecutionManager::NativeWhenPossible => {
self.execute_aux(true, native_call).0
},
}
};
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,
spawn_handle: Box,
method: &str,
call_data: &[u8],
runtime_code: &RuntimeCode,
) -> Result<(Vec, StorageProof), Box>
where
B: Backend,
H: Hasher,
H::Out: Ord + 'static + codec::Codec,
Exec: CodeExecutor + Clone + 'static,
N: crate::changes_trie::BlockNumber,
{
let trie_backend = backend.as_trie_backend()
.ok_or_else(|| Box::new(ExecutionError::UnableToGenerateProof) as Box)?;
prove_execution_on_trie_backend::<_, _, N, _>(
trie_backend,
overlay,
exec,
spawn_handle,
method,
call_data,
runtime_code,
)
}
/// 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,
spawn_handle: Box,
method: &str,
call_data: &[u8],
runtime_code: &RuntimeCode,
) -> Result<(Vec, StorageProof), Box>
where
S: trie_backend_essence::TrieBackendStorage,
H: Hasher,
H::Out: Ord + 'static + codec::Codec,
Exec: CodeExecutor + 'static + Clone,
N: crate::changes_trie::BlockNumber,
{
let proving_backend = proving_backend::ProvingBackend::new(trie_backend);
let mut sm = StateMachine::<_, H, N, Exec>::new(
&proving_backend,
None,
overlay,
exec,
method,
call_data,
Extensions::default(),
runtime_code,
spawn_handle,
);
let result = sm.execute_using_consensus_failure_handler::<_, NeverNativeValue, fn() -> _>(
always_wasm(),
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: StorageProof,
overlay: &mut OverlayedChanges,
exec: &Exec,
spawn_handle: Box,
method: &str,
call_data: &[u8],
runtime_code: &RuntimeCode,
) -> Result, Box>
where
H: Hasher,
Exec: CodeExecutor + Clone + 'static,
H::Out: Ord + 'static + codec::Codec,
N: crate::changes_trie::BlockNumber,
{
let trie_backend = create_proof_check_backend::(root.into(), proof)?;
execution_proof_check_on_trie_backend::<_, N, _>(
&trie_backend,
overlay,
exec,
spawn_handle,
method,
call_data,
runtime_code,
)
}
/// 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,
spawn_handle: Box,
method: &str,
call_data: &[u8],
runtime_code: &RuntimeCode,
) -> Result, Box>
where
H: Hasher,
H::Out: Ord + 'static + codec::Codec,
Exec: CodeExecutor + Clone + 'static,
N: crate::changes_trie::BlockNumber,
{
let mut sm = StateMachine::<_, H, N, Exec>::new(
trie_backend,
None,
overlay,
exec,
method,
call_data,
Extensions::default(),
runtime_code,
spawn_handle,
);
sm.execute_using_consensus_failure_handler::<_, NeverNativeValue, fn() -> _>(
always_untrusted_wasm(),
None,
).map(NativeOrEncoded::into_encoded)
}
/// Generate storage read proof.
pub fn prove_read(
mut backend: B,
keys: I,
) -> Result>
where
B: Backend,
H: Hasher,
H::Out: Ord + Codec,
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],
child_info: ChildInfo,
keys: I,
) -> Result>
where
B: Backend,
H: Hasher,
H::Out: Ord + Codec,
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, child_info, keys)
}
/// Generate storage read proof on pre-created trie backend.
pub fn prove_read_on_trie_backend(
trie_backend: &TrieBackend,
keys: I,
) -> Result>
where
S: trie_backend_essence::TrieBackendStorage,
H: Hasher,
H::Out: Ord + Codec,
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],
child_info: ChildInfo,
keys: I,
) -> Result>
where
S: trie_backend_essence::TrieBackendStorage,
H: Hasher,
H::Out: Ord + Codec,
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, child_info.clone(), 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: StorageProof,
keys: I,
) -> Result, Option>>, Box>
where
H: Hasher,
H::Out: Ord + Codec,
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: StorageProof,
storage_key: &[u8],
keys: I,
) -> Result, Option>>, Box>
where
H: Hasher,
H::Out: Ord + Codec,
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