Don't include :code by default in storage proofs (#5060)

* Adds test to verify that the runtime currently is always contained in
the proof

* Start passing the runtime wasm code from the outside

* Fix compilation

* More build fixes

* Make the test work as expected now :)

* Last fixes

* Fixes benchmarks

* Review feedback

* Apply suggestions from code review

Co-Authored-By: Sergei Pepyakin <sergei@parity.io>

* Review feedback

* Fix compilation

Co-authored-by: Sergei Pepyakin <s.pepyakin@gmail.com>
This commit is contained in:
Benjamin Kampmann
2020-03-04 20:26:16 +01:00
committed by GitHub
parent 67837c6233
commit 6ee39261c8
31 changed files with 480 additions and 183 deletions
+4
View File
@@ -1478,6 +1478,7 @@ dependencies = [
"sc-executor", "sc-executor",
"sc-service", "sc-service",
"sp-runtime", "sp-runtime",
"sp-state-machine",
"structopt", "structopt",
] ]
@@ -5652,6 +5653,8 @@ dependencies = [
"sp-core", "sp-core",
"sp-runtime", "sp-runtime",
"sp-state-machine", "sp-state-machine",
"sp-trie",
"substrate-test-runtime-client",
] ]
[[package]] [[package]]
@@ -6955,6 +6958,7 @@ dependencies = [
"sp-api", "sp-api",
"sp-blockchain", "sp-blockchain",
"sp-consensus", "sp-consensus",
"sp-core",
"sp-runtime", "sp-runtime",
"sp-state-machine", "sp-state-machine",
"sp-version", "sp-version",
+13 -3
View File
@@ -25,8 +25,8 @@ use node_runtime::constants::currency::*;
use node_testing::keyring::*; use node_testing::keyring::*;
use sp_core::{Blake2Hasher, NativeOrEncoded, NeverNativeValue}; use sp_core::{Blake2Hasher, NativeOrEncoded, NeverNativeValue};
use sp_core::storage::well_known_keys; use sp_core::storage::well_known_keys;
use sp_core::traits::CodeExecutor; use sp_core::traits::{CodeExecutor, RuntimeCode};
use frame_support::Hashable; use frame_support::Hashable;
use sp_state_machine::TestExternalities as CoreTestExternalities; use sp_state_machine::TestExternalities as CoreTestExternalities;
use sc_executor::{NativeExecutor, RuntimeInfo, WasmExecutionMethod, Externalities}; use sc_executor::{NativeExecutor, RuntimeInfo, WasmExecutionMethod, Externalities};
@@ -89,9 +89,12 @@ fn construct_block<E: Externalities>(
digest: Default::default(), digest: Default::default(),
}; };
let runtime_code = RuntimeCode::from_externalities(ext).expect("`ext` provides `:code`");
// execute the block to get the real header. // execute the block to get the real header.
executor.call::<_, NeverNativeValue, fn() -> _>( executor.call::<_, NeverNativeValue, fn() -> _>(
ext, ext,
&runtime_code,
"Core_initialize_block", "Core_initialize_block",
&header.encode(), &header.encode(),
true, true,
@@ -101,6 +104,7 @@ fn construct_block<E: Externalities>(
for i in extrinsics.iter() { for i in extrinsics.iter() {
executor.call::<_, NeverNativeValue, fn() -> _>( executor.call::<_, NeverNativeValue, fn() -> _>(
ext, ext,
&runtime_code,
"BlockBuilder_apply_extrinsic", "BlockBuilder_apply_extrinsic",
&i.encode(), &i.encode(),
true, true,
@@ -110,6 +114,7 @@ fn construct_block<E: Externalities>(
let header = match executor.call::<_, NeverNativeValue, fn() -> _>( let header = match executor.call::<_, NeverNativeValue, fn() -> _>(
ext, ext,
&runtime_code,
"BlockBuilder_finalize_block", "BlockBuilder_finalize_block",
&[0u8;0], &[0u8;0],
true, true,
@@ -165,7 +170,9 @@ fn bench_execute_block(c: &mut Criterion) {
// Get the runtime version to initialize the runtimes cache. // Get the runtime version to initialize the runtimes cache.
{ {
let mut test_ext = new_test_ext(&genesis_config); let mut test_ext = new_test_ext(&genesis_config);
executor.runtime_version(&mut test_ext.ext()); let runtime_code = RuntimeCode::from_externalities(&test_ext.ext())
.expect("`test_ext` provides `:code`");
executor.runtime_version(&mut test_ext.ext(), &runtime_code).unwrap();
} }
let blocks = test_blocks(&genesis_config, &executor); let blocks = test_blocks(&genesis_config, &executor);
@@ -173,9 +180,12 @@ fn bench_execute_block(c: &mut Criterion) {
b.iter_batched_ref( b.iter_batched_ref(
|| new_test_ext(&genesis_config), || new_test_ext(&genesis_config),
|test_ext| { |test_ext| {
let runtime_code = RuntimeCode::from_externalities(&test_ext.ext())
.expect("`test_ext` provides `:code`");
for block in blocks.iter() { for block in blocks.iter() {
executor.call::<_, NeverNativeValue, fn() -> _>( executor.call::<_, NeverNativeValue, fn() -> _>(
&mut test_ext.ext(), &mut test_ext.ext(),
&runtime_code,
"Core_execute_block", "Core_execute_block",
&block.0, &block.0,
use_native, use_native,
+4 -2
View File
@@ -18,8 +18,7 @@ use codec::{Encode, Decode};
use frame_support::Hashable; use frame_support::Hashable;
use sp_state_machine::TestExternalities as CoreTestExternalities; use sp_state_machine::TestExternalities as CoreTestExternalities;
use sp_core::{ use sp_core::{
Blake2Hasher, NeverNativeValue, NativeOrEncoded, Blake2Hasher, NeverNativeValue, NativeOrEncoded, traits::{CodeExecutor, RuntimeCode},
traits::CodeExecutor,
}; };
use sp_runtime::{ApplyExtrinsicResult, traits::Header as HeaderT}; use sp_runtime::{ApplyExtrinsicResult, traits::Header as HeaderT};
use sc_executor::{NativeExecutor, WasmExecutionMethod}; use sc_executor::{NativeExecutor, WasmExecutionMethod};
@@ -74,8 +73,11 @@ pub fn executor_call<
native_call: Option<NC>, native_call: Option<NC>,
) -> (Result<NativeOrEncoded<R>>, bool) { ) -> (Result<NativeOrEncoded<R>>, bool) {
let mut t = t.ext(); let mut t = t.ext();
let runtime_code = RuntimeCode::from_externalities(&t)
.expect("Code should be part of the externalities");
executor().call::<_, R, NC>( executor().call::<_, R, NC>(
&mut t, &mut t,
&runtime_code,
method, method,
data, data,
use_native, use_native,
@@ -231,7 +231,8 @@ impl<A, B, Block, C> ProposerInner<B, Block, C, A>
debug!("[{:?}] Pushed to the block.", pending_tx_hash); debug!("[{:?}] Pushed to the block.", pending_tx_hash);
} }
Err(sp_blockchain::Error::ApplyExtrinsicFailed(sp_blockchain::ApplyExtrinsicFailed::Validity(e))) Err(sp_blockchain::Error::ApplyExtrinsicFailed(sp_blockchain::ApplyExtrinsicFailed::Validity(e)))
if e.exhausted_resources() => { if e.exhausted_resources() =>
{
if is_first { if is_first {
debug!("[{:?}] Invalid transaction: FullBlock on empty block", pending_tx_hash); debug!("[{:?}] Invalid transaction: FullBlock on empty block", pending_tx_hash);
unqueue_invalid.push(pending_tx_hash); unqueue_invalid.push(pending_tx_hash);
@@ -19,3 +19,7 @@ sp-core = { version = "2.0.0-alpha.2", path = "../../primitives/core" }
sp-block-builder = { version = "2.0.0-alpha.2", path = "../../primitives/block-builder" } sp-block-builder = { version = "2.0.0-alpha.2", path = "../../primitives/block-builder" }
sc-client-api = { version = "2.0.0-alpha.2", path = "../api" } sc-client-api = { version = "2.0.0-alpha.2", path = "../api" }
codec = { package = "parity-scale-codec", version = "1.2.0", features = ["derive"] } codec = { package = "parity-scale-codec", version = "1.2.0", features = ["derive"] }
[dev-dependencies]
substrate-test-runtime-client = { path = "../../test-utils/runtime/client" }
sp-trie = { version = "2.0.0-alpha.2", path = "../../primitives/trie" }
+50 -4
View File
@@ -152,12 +152,20 @@ where
/// Push onto the block's list of extrinsics. /// Push onto the block's list of extrinsics.
/// ///
/// This will treat incoming extrinsic `xt` as trusted and skip signature check (for signed transactions). /// This will treat incoming extrinsic `xt` as trusted and skip signature check
pub fn push_trusted(&mut self, xt: <Block as BlockT>::Extrinsic) -> Result<(), ApiErrorFor<A, Block>> { /// (for signed transactions).
pub fn push_trusted(
&mut self,
xt: <Block as BlockT>::Extrinsic,
) -> Result<(), ApiErrorFor<A, Block>> {
self.push_internal(xt, true) self.push_internal(xt, true)
} }
fn push_internal(&mut self, xt: <Block as BlockT>::Extrinsic, skip_signature: bool) -> Result<(), ApiErrorFor<A, Block>> { fn push_internal(
&mut self,
xt: <Block as BlockT>::Extrinsic,
skip_signature: bool,
) -> Result<(), ApiErrorFor<A, Block>> {
let block_id = &self.block_id; let block_id = &self.block_id;
let extrinsics = &mut self.extrinsics; let extrinsics = &mut self.extrinsics;
@@ -175,7 +183,7 @@ where
ExecutionContext::BlockConstruction, ExecutionContext::BlockConstruction,
xt.clone(), xt.clone(),
)? )?
} else { } else {
api.apply_extrinsic_with_context( api.apply_extrinsic_with_context(
block_id, block_id,
ExecutionContext::BlockConstruction, ExecutionContext::BlockConstruction,
@@ -241,3 +249,41 @@ where
}) })
} }
} }
#[cfg(test)]
mod tests {
use super::*;
use sp_blockchain::HeaderBackend;
use sp_core::Blake2Hasher;
use sp_state_machine::Backend;
use substrate_test_runtime_client::{DefaultTestClientBuilderExt, TestClientBuilderExt};
#[test]
fn block_building_storage_proof_does_not_include_runtime_by_default() {
let builder = substrate_test_runtime_client::TestClientBuilder::new();
let backend = builder.backend();
let client = builder.build();
let block = BlockBuilder::new(
&client,
client.info().best_hash,
client.info().best_number,
RecordProof::Yes,
Default::default(),
&*backend,
).unwrap().build().unwrap();
let proof = block.proof.expect("Proof is build on request");
let backend = sp_state_machine::create_proof_check_backend::<Blake2Hasher>(
block.storage_changes.transaction_storage_root,
proof,
).unwrap();
assert!(
backend.storage(&sp_core::storage::well_known_keys::CODE)
.unwrap_err()
.contains("Database missing expected key"),
);
}
}
+6 -2
View File
@@ -110,8 +110,12 @@ pub trait RuntimeInfo {
/// Native runtime information. /// Native runtime information.
fn native_version(&self) -> &NativeVersion; fn native_version(&self) -> &NativeVersion;
/// Extract RuntimeVersion of given :code block /// Extract [`RuntimeVersion`](sp_version::RuntimeVersion) of the given `runtime_code`.
fn runtime_version<E: Externalities> (&self, ext: &mut E) -> error::Result<RuntimeVersion>; fn runtime_version<E: Externalities>(
&self,
ext: &mut E,
runtime_code: &sp_core::traits::RuntimeCode,
) -> error::Result<RuntimeVersion>;
} }
#[cfg(test)] #[cfg(test)]
@@ -20,7 +20,7 @@ use crate::{
}; };
use sp_version::{NativeVersion, RuntimeVersion}; use sp_version::{NativeVersion, RuntimeVersion};
use codec::{Decode, Encode}; use codec::{Decode, Encode};
use sp_core::{NativeOrEncoded, traits::{CodeExecutor, Externalities}}; use sp_core::{NativeOrEncoded, traits::{CodeExecutor, Externalities, RuntimeCode}};
use log::trace; use log::trace;
use std::{result, cell::RefCell, panic::{UnwindSafe, AssertUnwindSafe}, sync::Arc}; use std::{result, cell::RefCell, panic::{UnwindSafe, AssertUnwindSafe}, sync::Arc};
use sp_wasm_interface::{HostFunctions, Function}; use sp_wasm_interface::{HostFunctions, Function};
@@ -130,6 +130,7 @@ impl<D: NativeExecutionDispatch> NativeExecutor<D> {
fn with_runtime<E, R>( fn with_runtime<E, R>(
&self, &self,
ext: &mut E, ext: &mut E,
runtime_code: &RuntimeCode,
f: impl for<'a> FnOnce( f: impl for<'a> FnOnce(
AssertUnwindSafe<&'a mut (dyn WasmRuntime + 'static)>, AssertUnwindSafe<&'a mut (dyn WasmRuntime + 'static)>,
&'a RuntimeVersion, &'a RuntimeVersion,
@@ -138,8 +139,9 @@ impl<D: NativeExecutionDispatch> NativeExecutor<D> {
) -> Result<R> where E: Externalities { ) -> Result<R> where E: Externalities {
RUNTIMES_CACHE.with(|cache| { RUNTIMES_CACHE.with(|cache| {
let mut cache = cache.borrow_mut(); let mut cache = cache.borrow_mut();
let (runtime, version, code_hash) = cache.fetch_runtime( let (runtime, version) = cache.fetch_runtime(
ext, ext,
runtime_code,
self.fallback_method, self.fallback_method,
self.default_heap_pages, self.default_heap_pages,
&*self.host_functions, &*self.host_functions,
@@ -151,7 +153,7 @@ impl<D: NativeExecutionDispatch> NativeExecutor<D> {
match f(runtime, version, ext) { match f(runtime, version, ext) {
Ok(res) => res, Ok(res) => res,
Err(e) => { Err(e) => {
cache.invalidate_runtime(self.fallback_method, code_hash); cache.invalidate_runtime(self.fallback_method, runtime_code.hash.clone());
Err(e) Err(e)
} }
} }
@@ -179,8 +181,9 @@ impl<D: NativeExecutionDispatch> RuntimeInfo for NativeExecutor<D> {
fn runtime_version<E: Externalities>( fn runtime_version<E: Externalities>(
&self, &self,
ext: &mut E, ext: &mut E,
runtime_code: &RuntimeCode,
) -> Result<RuntimeVersion> { ) -> Result<RuntimeVersion> {
self.with_runtime(ext, |_runtime, version, _ext| Ok(Ok(version.clone()))) self.with_runtime(ext, runtime_code, |_runtime, version, _ext| Ok(Ok(version.clone())))
} }
} }
@@ -195,13 +198,14 @@ impl<D: NativeExecutionDispatch + 'static> CodeExecutor for NativeExecutor<D> {
>( >(
&self, &self,
ext: &mut E, ext: &mut E,
runtime_code: &RuntimeCode,
method: &str, method: &str,
data: &[u8], data: &[u8],
use_native: bool, use_native: bool,
native_call: Option<NC>, native_call: Option<NC>,
) -> (Result<NativeOrEncoded<R>>, bool){ ) -> (Result<NativeOrEncoded<R>>, bool) {
let mut used_native = false; let mut used_native = false;
let result = self.with_runtime(ext, |mut runtime, onchain_version, mut ext| { let result = self.with_runtime(ext, runtime_code, |mut runtime, onchain_version, mut ext| {
match ( match (
use_native, use_native,
onchain_version.can_call_with(&self.native_version.runtime_version), onchain_version.can_call_with(&self.native_version.runtime_version),
+21 -20
View File
@@ -22,7 +22,7 @@
use crate::error::{Error, WasmError}; use crate::error::{Error, WasmError};
use log::{trace, warn}; use log::{trace, warn};
use codec::Decode; use codec::Decode;
use sp_core::{storage::well_known_keys, traits::Externalities}; use sp_core::traits::{Externalities, RuntimeCode};
use sp_version::RuntimeVersion; use sp_version::RuntimeVersion;
use std::{collections::hash_map::{Entry, HashMap}, panic::AssertUnwindSafe}; use std::{collections::hash_map::{Entry, HashMap}, panic::AssertUnwindSafe};
use sc_executor_common::wasm_runtime::WasmRuntime; use sc_executor_common::wasm_runtime::WasmRuntime;
@@ -86,8 +86,9 @@ impl RuntimesCache {
/// ///
/// # Parameters /// # Parameters
/// ///
/// `ext` - Externalities to use for the runtime. This is used for setting /// `ext` - Externalities to use for the getting the runtime's version call.
/// up an initial runtime instance. ///
/// `runtime_code` - The runtime wasm code used setup the runtime.
/// ///
/// `default_heap_pages` - Number of 64KB pages to allocate for Wasm execution. /// `default_heap_pages` - Number of 64KB pages to allocate for Wasm execution.
/// ///
@@ -95,8 +96,8 @@ impl RuntimesCache {
/// ///
/// # Return value /// # Return value
/// ///
/// If no error occurred a tuple `(&mut WasmRuntime, H256)` is /// If no error occurred a tuple `(&mut WasmRuntime, RuntimeVerion)` is
/// returned. `H256` is the hash of the runtime code. /// returned.
/// ///
/// In case of failure one of two errors can be returned: /// In case of failure one of two errors can be returned:
/// ///
@@ -107,20 +108,14 @@ impl RuntimesCache {
pub fn fetch_runtime<E: Externalities>( pub fn fetch_runtime<E: Externalities>(
&mut self, &mut self,
ext: &mut E, ext: &mut E,
runtime_code: &RuntimeCode,
wasm_method: WasmExecutionMethod, wasm_method: WasmExecutionMethod,
default_heap_pages: u64, default_heap_pages: u64,
host_functions: &[&'static dyn Function], host_functions: &[&'static dyn Function],
) -> Result<(&mut (dyn WasmRuntime + 'static), &RuntimeVersion, Vec<u8>), Error> { ) -> Result<(&mut (dyn WasmRuntime + 'static), &RuntimeVersion), Error> {
let code_hash = ext let heap_pages = runtime_code.heap_pages.unwrap_or(default_heap_pages);
.original_storage_hash(well_known_keys::CODE)
.ok_or(Error::InvalidCode("`CODE` not found in storage.".into()))?;
let heap_pages = ext let result = match self.instances.entry((wasm_method, runtime_code.hash.clone())) {
.storage(well_known_keys::HEAP_PAGES)
.and_then(|pages| u64::decode(&mut &pages[..]).ok())
.unwrap_or(default_heap_pages);
let result = match self.instances.entry((wasm_method, code_hash.clone())) {
Entry::Occupied(o) => { Entry::Occupied(o) => {
let result = o.into_mut(); let result = o.into_mut();
if let Ok(ref mut cached_runtime) = result { if let Ok(ref mut cached_runtime) = result {
@@ -142,6 +137,7 @@ impl RuntimesCache {
*result = create_versioned_wasm_runtime( *result = create_versioned_wasm_runtime(
ext, ext,
wasm_method, wasm_method,
runtime_code,
heap_pages, heap_pages,
host_functions.into(), host_functions.into(),
); );
@@ -157,6 +153,7 @@ impl RuntimesCache {
let result = create_versioned_wasm_runtime( let result = create_versioned_wasm_runtime(
ext, ext,
wasm_method, wasm_method,
runtime_code,
heap_pages, heap_pages,
host_functions.into(), host_functions.into(),
); );
@@ -168,7 +165,7 @@ impl RuntimesCache {
}; };
result.as_mut() result.as_mut()
.map(|entry| (entry.runtime.as_mut(), &entry.version, code_hash)) .map(|entry| (entry.runtime.as_mut(), &entry.version))
.map_err(|ref e| Error::InvalidCode(format!("{:?}", e))) .map_err(|ref e| Error::InvalidCode(format!("{:?}", e)))
} }
@@ -209,13 +206,17 @@ pub fn create_wasm_runtime_with_code(
fn create_versioned_wasm_runtime<E: Externalities>( fn create_versioned_wasm_runtime<E: Externalities>(
ext: &mut E, ext: &mut E,
wasm_method: WasmExecutionMethod, wasm_method: WasmExecutionMethod,
runtime_code: &RuntimeCode,
heap_pages: u64, heap_pages: u64,
host_functions: Vec<&'static dyn Function>, host_functions: Vec<&'static dyn Function>,
) -> Result<VersionedRuntime, WasmError> { ) -> Result<VersionedRuntime, WasmError> {
let code = ext let mut runtime = create_wasm_runtime_with_code(
.original_storage(well_known_keys::CODE) wasm_method,
.ok_or(WasmError::CodeNotFound)?; heap_pages,
let mut runtime = create_wasm_runtime_with_code(wasm_method, heap_pages, &code, host_functions, false)?; &runtime_code.code,
host_functions,
false,
)?;
// Call to determine runtime version. // Call to determine runtime version.
let version_result = { let version_result = {
+15 -7
View File
@@ -62,7 +62,12 @@ pub trait Client<Block: BlockT>: Send + Sync {
) -> Result<StorageProof, Error>; ) -> Result<StorageProof, Error>;
/// Get method execution proof. /// Get method execution proof.
fn execution_proof(&self, block: &Block::Hash, method: &str, data: &[u8]) -> Result<(Vec<u8>, StorageProof), Error>; fn execution_proof(
&self,
block: &Block::Hash,
method: &str,
data: &[u8],
) -> Result<(Vec<u8>, StorageProof), Error>;
/// Get key changes proof. /// Get key changes proof.
fn key_changes_proof( fn key_changes_proof(
@@ -152,11 +157,7 @@ impl<B, E, Block, RA> Client<Block> for SubstrateClient<B, E, Block, RA> where
method: &str, method: &str,
data: &[u8], data: &[u8],
) -> Result<(Vec<u8>, StorageProof), Error> { ) -> Result<(Vec<u8>, StorageProof), Error> {
(self as &SubstrateClient<B, E, Block, RA>).execution_proof( SubstrateClient::execution_proof(self, &BlockId::Hash(block.clone()), method, data)
&BlockId::Hash(block.clone()),
method,
data,
)
} }
fn key_changes_proof( fn key_changes_proof(
@@ -168,7 +169,14 @@ impl<B, E, Block, RA> Client<Block> for SubstrateClient<B, E, Block, RA> where
storage_key: Option<&StorageKey>, storage_key: Option<&StorageKey>,
key: &StorageKey, key: &StorageKey,
) -> Result<ChangesProof<Block::Header>, Error> { ) -> Result<ChangesProof<Block::Header>, Error> {
(self as &SubstrateClient<B, E, Block, RA>).key_changes_proof(first, last, min, max, storage_key, key) (self as &SubstrateClient<B, E, Block, RA>).key_changes_proof(
first,
last,
min,
max,
storage_key,
key,
)
} }
fn is_descendent_of(&self, base: &Block::Hash, block: &Block::Hash) -> Result<bool, Error> { fn is_descendent_of(&self, base: &Block::Hash, block: &Block::Hash) -> Result<bool, Error> {
@@ -254,13 +254,12 @@ where
B: Block, B: Block,
{ {
/// Construct a new light client handler. /// Construct a new light client handler.
pub fn new pub fn new(
( cfg: Config cfg: Config,
, chain: Arc<dyn Client<B>> chain: Arc<dyn Client<B>>,
, checker: Arc<dyn fetcher::FetchChecker<B>> checker: Arc<dyn fetcher::FetchChecker<B>>,
, peerset: sc_peerset::PeersetHandle peerset: sc_peerset::PeersetHandle,
) -> Self ) -> Self {
{
LightClientHandler { LightClientHandler {
config: cfg, config: cfg,
chain, chain,
@@ -425,7 +424,8 @@ where
log::trace!("remote call request from {} ({} at {:?})", log::trace!("remote call request from {} ({} at {:?})",
peer, peer,
request.method, request.method,
request.block); request.block,
);
let block = Decode::decode(&mut request.block.as_ref())?; let block = Decode::decode(&mut request.block.as_ref())?;
@@ -436,7 +436,8 @@ where
peer, peer,
request.method, request.method,
request.block, request.block,
e); e,
);
StorageProof::empty() StorageProof::empty()
} }
}; };
@@ -750,7 +750,11 @@ pub mod tests {
} }
} }
fn check_execution_proof(&self, _: &RemoteCallRequest<B::Header>, _: StorageProof) -> ClientResult<Vec<u8>> { fn check_execution_proof(
&self,
_: &RemoteCallRequest<B::Header>,
_: StorageProof,
) -> ClientResult<Vec<u8>> {
match self.ok { match self.ok {
true => Ok(vec![42]), true => Ok(vec![42]),
false => Err(ClientError::Backend("Test error".into())), false => Err(ClientError::Backend("Test error".into())),
+9 -2
View File
@@ -25,7 +25,7 @@ use sp_state_machine::{
}; };
use sc_executor::{RuntimeVersion, RuntimeInfo, NativeVersion}; use sc_executor::{RuntimeVersion, RuntimeInfo, NativeVersion};
use sp_externalities::Extensions; use sp_externalities::Extensions;
use sp_core::{NativeOrEncoded, NeverNativeValue, traits::CodeExecutor}; use sp_core::{NativeOrEncoded, NeverNativeValue, traits::{CodeExecutor, RuntimeCode}};
use sp_api::{ProofRecorder, InitializeBlock, StorageTransactionCache}; use sp_api::{ProofRecorder, InitializeBlock, StorageTransactionCache};
use sc_client_api::{backend, call_executor::CallExecutor}; use sc_client_api::{backend, call_executor::CallExecutor};
@@ -90,6 +90,7 @@ where
method, method,
call_data, call_data,
extensions.unwrap_or_default(), extensions.unwrap_or_default(),
&sp_state_machine::backend::get_runtime_code(&state)?,
).execute_using_consensus_failure_handler::<_, NeverNativeValue, fn() -> _>( ).execute_using_consensus_failure_handler::<_, NeverNativeValue, fn() -> _>(
strategy.get_manager(), strategy.get_manager(),
None, None,
@@ -140,6 +141,8 @@ where
// make sure to destroy state before exiting this function // make sure to destroy state before exiting this function
let mut state = self.backend.state_at(*at)?; let mut state = self.backend.state_at(*at)?;
let runtime_code = sp_state_machine::backend::get_runtime_code(&state)?;
let result = match recorder { let result = match recorder {
Some(recorder) => state.as_trie_backend() Some(recorder) => state.as_trie_backend()
.ok_or_else(|| .ok_or_else(||
@@ -160,6 +163,7 @@ where
method, method,
call_data, call_data,
extensions.unwrap_or_default(), extensions.unwrap_or_default(),
&runtime_code,
) )
// TODO: https://github.com/paritytech/substrate/issues/4455 // TODO: https://github.com/paritytech/substrate/issues/4455
// .with_storage_transaction_cache(storage_transaction_cache.as_mut().map(|c| &mut **c)) // .with_storage_transaction_cache(storage_transaction_cache.as_mut().map(|c| &mut **c))
@@ -173,6 +177,7 @@ where
method, method,
call_data, call_data,
extensions.unwrap_or_default(), extensions.unwrap_or_default(),
&runtime_code,
) )
.with_storage_transaction_cache(storage_transaction_cache.as_mut().map(|c| &mut **c)) .with_storage_transaction_cache(storage_transaction_cache.as_mut().map(|c| &mut **c))
.execute_using_consensus_failure_handler(execution_manager, native_call) .execute_using_consensus_failure_handler(execution_manager, native_call)
@@ -197,7 +202,8 @@ where
changes_trie_state, changes_trie_state,
None, None,
); );
let version = self.executor.runtime_version(&mut ext); let wasm_code = RuntimeCode::from_externalities(&ext).map_err(|e| e.to_string().into());
let version = wasm_code.and_then(|c| self.executor.runtime_version(&mut ext, &c));
{ {
let _lock = self.backend.get_import_lock().read(); let _lock = self.backend.get_import_lock().read();
self.backend.destroy_state(state)?; self.backend.destroy_state(state)?;
@@ -218,6 +224,7 @@ where
&self.executor, &self.executor,
method, method,
call_data, call_data,
&sp_state_machine::backend::get_runtime_code(trie_state)?,
) )
.map_err(Into::into) .map_err(Into::into)
} }
+13 -3
View File
@@ -41,8 +41,7 @@ use sp_runtime::{
use sp_state_machine::{ use sp_state_machine::{
DBValue, Backend as StateBackend, ChangesTrieAnchorBlockId, DBValue, Backend as StateBackend, ChangesTrieAnchorBlockId,
prove_read, prove_child_read, ChangesTrieRootsStorage, ChangesTrieStorage, prove_read, prove_child_read, ChangesTrieRootsStorage, ChangesTrieStorage,
ChangesTrieConfigurationRange, key_changes, key_changes_proof, StorageProof, ChangesTrieConfigurationRange, key_changes, key_changes_proof,
merge_storage_proofs,
}; };
use sc_executor::{RuntimeVersion, RuntimeInfo}; use sc_executor::{RuntimeVersion, RuntimeInfo};
use sp_consensus::{ use sp_consensus::{
@@ -55,6 +54,7 @@ use sp_blockchain::{self as blockchain,
well_known_cache_keys::Id as CacheKeyId, well_known_cache_keys::Id as CacheKeyId,
HeaderMetadata, CachedHeaderMetadata, HeaderMetadata, CachedHeaderMetadata,
}; };
use sp_trie::StorageProof;
use sp_api::{ use sp_api::{
CallApiAt, ConstructRuntimeApi, Core as CoreApi, ApiExt, ApiRef, ProvideRuntimeApi, CallApiAt, ConstructRuntimeApi, Core as CoreApi, ApiExt, ApiRef, ProvideRuntimeApi,
@@ -482,9 +482,19 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
method: &str, method: &str,
call_data: &[u8] call_data: &[u8]
) -> sp_blockchain::Result<(Vec<u8>, StorageProof)> { ) -> sp_blockchain::Result<(Vec<u8>, StorageProof)> {
// Make sure we include the `:code` and `:heap_pages` in the execution proof to be
// backwards compatible.
//
// TODO: Remove when solved: https://github.com/paritytech/substrate/issues/5047
let code_proof = self.read_proof(
id,
&[well_known_keys::CODE.to_vec(), well_known_keys::HEAP_PAGES.to_vec()],
)?;
let state = self.state_at(id)?; let state = self.state_at(id)?;
let header = self.prepare_environment_block(id)?; let header = self.prepare_environment_block(id)?;
prove_execution(state, header, &self.executor, method, call_data) prove_execution(state, header, &self.executor, method, call_data)
.map(|p| (p.0, StorageProof::merge(vec![p.1, code_proof])))
} }
/// Reads given header and generates CHT-based header proof. /// Reads given header and generates CHT-based header proof.
@@ -769,7 +779,7 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
Ok(()) Ok(())
}, ())?; }, ())?;
Ok(merge_storage_proofs(proofs)) Ok(StorageProof::merge(proofs))
} }
/// Generates CHT-based proof for roots of changes tries at given blocks (that are part of single CHT). /// Generates CHT-based proof for roots of changes tries at given blocks (that are part of single CHT).
+14
View File
@@ -89,6 +89,8 @@ mod tests {
}; };
let hash = header.hash(); let hash = header.hash();
let mut overlay = OverlayedChanges::default(); let mut overlay = OverlayedChanges::default();
let runtime_code = sp_state_machine::backend::get_runtime_code(&backend)
.expect("Code is part of the backend");
StateMachine::new( StateMachine::new(
backend, backend,
@@ -98,6 +100,7 @@ mod tests {
"Core_initialize_block", "Core_initialize_block",
&header.encode(), &header.encode(),
Default::default(), Default::default(),
&runtime_code,
).execute( ).execute(
ExecutionStrategy::NativeElseWasm, ExecutionStrategy::NativeElseWasm,
).unwrap(); ).unwrap();
@@ -111,6 +114,7 @@ mod tests {
"BlockBuilder_apply_extrinsic", "BlockBuilder_apply_extrinsic",
&tx.encode(), &tx.encode(),
Default::default(), Default::default(),
&runtime_code,
).execute( ).execute(
ExecutionStrategy::NativeElseWasm, ExecutionStrategy::NativeElseWasm,
).unwrap(); ).unwrap();
@@ -124,6 +128,7 @@ mod tests {
"BlockBuilder_finalize_block", "BlockBuilder_finalize_block",
&[], &[],
Default::default(), Default::default(),
&runtime_code,
).execute( ).execute(
ExecutionStrategy::NativeElseWasm, ExecutionStrategy::NativeElseWasm,
).unwrap(); ).unwrap();
@@ -161,6 +166,8 @@ mod tests {
let backend = InMemoryBackend::from(storage); let backend = InMemoryBackend::from(storage);
let (b1data, _b1hash) = block1(genesis_hash, &backend); let (b1data, _b1hash) = block1(genesis_hash, &backend);
let runtime_code = sp_state_machine::backend::get_runtime_code(&backend)
.expect("Code is part of the backend");
let mut overlay = OverlayedChanges::default(); let mut overlay = OverlayedChanges::default();
let _ = StateMachine::new( let _ = StateMachine::new(
@@ -171,6 +178,7 @@ mod tests {
"Core_execute_block", "Core_execute_block",
&b1data, &b1data,
Default::default(), Default::default(),
&runtime_code,
).execute( ).execute(
ExecutionStrategy::NativeElseWasm, ExecutionStrategy::NativeElseWasm,
).unwrap(); ).unwrap();
@@ -189,6 +197,8 @@ mod tests {
let backend = InMemoryBackend::from(storage); let backend = InMemoryBackend::from(storage);
let (b1data, _b1hash) = block1(genesis_hash, &backend); let (b1data, _b1hash) = block1(genesis_hash, &backend);
let runtime_code = sp_state_machine::backend::get_runtime_code(&backend)
.expect("Code is part of the backend");
let mut overlay = OverlayedChanges::default(); let mut overlay = OverlayedChanges::default();
let _ = StateMachine::new( let _ = StateMachine::new(
@@ -199,6 +209,7 @@ mod tests {
"Core_execute_block", "Core_execute_block",
&b1data, &b1data,
Default::default(), Default::default(),
&runtime_code,
).execute( ).execute(
ExecutionStrategy::AlwaysWasm, ExecutionStrategy::AlwaysWasm,
).unwrap(); ).unwrap();
@@ -217,6 +228,8 @@ mod tests {
let backend = InMemoryBackend::from(storage); let backend = InMemoryBackend::from(storage);
let (b1data, _b1hash) = block1(genesis_hash, &backend); let (b1data, _b1hash) = block1(genesis_hash, &backend);
let runtime_code = sp_state_machine::backend::get_runtime_code(&backend)
.expect("Code is part of the backend");
let mut overlay = OverlayedChanges::default(); let mut overlay = OverlayedChanges::default();
let r = StateMachine::new( let r = StateMachine::new(
@@ -227,6 +240,7 @@ mod tests {
"Core_execute_block", "Core_execute_block",
&b1data, &b1data,
Default::default(), Default::default(),
&runtime_code,
).execute( ).execute(
ExecutionStrategy::NativeElseWasm, ExecutionStrategy::NativeElseWasm,
); );
+2 -2
View File
@@ -172,9 +172,9 @@ impl<S, Block> ClientBackend<Block> for Backend<S, HasherFor<Block>>
match maybe_val { match maybe_val {
Some(val) => self.blockchain.storage().insert_aux( Some(val) => self.blockchain.storage().insert_aux(
&[(&key[..], &val[..])], &[(&key[..], &val[..])],
::std::iter::empty(), std::iter::empty(),
)?, )?,
None => self.blockchain.storage().insert_aux(::std::iter::empty(), &[&key[..]])?, None => self.blockchain.storage().insert_aux(std::iter::empty(), &[&key[..]])?,
} }
} }
} }
+9 -3
View File
@@ -29,7 +29,6 @@ use sp_externalities::Extensions;
use sp_state_machine::{ use sp_state_machine::{
self, Backend as StateBackend, OverlayedChanges, ExecutionStrategy, create_proof_check_backend, self, Backend as StateBackend, OverlayedChanges, ExecutionStrategy, create_proof_check_backend,
execution_proof_check_on_trie_backend, ExecutionManager, StorageProof, execution_proof_check_on_trie_backend, ExecutionManager, StorageProof,
merge_storage_proofs,
}; };
use hash_db::Hasher; use hash_db::Hasher;
@@ -206,7 +205,7 @@ pub fn prove_execution<Block, S, E>(
method, method,
call_data, call_data,
)?; )?;
let total_proof = merge_storage_proofs(vec![init_proof, exec_proof]); let total_proof = StorageProof::merge(vec![init_proof, exec_proof]);
Ok((result, total_proof)) Ok((result, total_proof))
} }
@@ -259,12 +258,17 @@ fn check_execution_proof_with_make_header<Header, E, H, MakeNextHeader: Fn(&Head
let mut changes = OverlayedChanges::default(); let mut changes = OverlayedChanges::default();
let trie_backend = create_proof_check_backend(root, remote_proof)?; let trie_backend = create_proof_check_backend(root, remote_proof)?;
let next_header = make_next_header(&request.header); let next_header = make_next_header(&request.header);
// TODO: Remove when solved: https://github.com/paritytech/substrate/issues/5047
let runtime_code = sp_state_machine::backend::get_runtime_code(&trie_backend)?;
execution_proof_check_on_trie_backend::<H, Header::Number, _>( execution_proof_check_on_trie_backend::<H, Header::Number, _>(
&trie_backend, &trie_backend,
&mut changes, &mut changes,
executor, executor,
"Core_initialize_block", "Core_initialize_block",
&next_header.encode(), &next_header.encode(),
&runtime_code,
)?; )?;
// execute method // execute method
@@ -274,7 +278,9 @@ fn check_execution_proof_with_make_header<Header, E, H, MakeNextHeader: Fn(&Head
executor, executor,
&request.method, &request.method,
&request.call_data, &request.call_data,
).map_err(Into::into) &runtime_code,
)
.map_err(Into::into)
} }
#[cfg(test)] #[cfg(test)]
+6 -5
View File
@@ -30,7 +30,7 @@ use sp_runtime::traits::{
use sp_state_machine::{ use sp_state_machine::{
ChangesTrieRootsStorage, ChangesTrieAnchorBlockId, ChangesTrieConfigurationRange, ChangesTrieRootsStorage, ChangesTrieAnchorBlockId, ChangesTrieConfigurationRange,
InMemoryChangesTrieStorage, TrieBackend, read_proof_check, key_changes_proof_check_with_db, InMemoryChangesTrieStorage, TrieBackend, read_proof_check, key_changes_proof_check_with_db,
create_proof_check_backend_storage, read_child_proof_check, read_child_proof_check,
}; };
pub use sp_state_machine::StorageProof; pub use sp_state_machine::StorageProof;
use sp_blockchain::{Error as ClientError, Result as ClientResult}; use sp_blockchain::{Error as ClientError, Result as ClientResult};
@@ -155,7 +155,7 @@ impl<E, H, B: BlockT, S: BlockchainStorage<B>> LightDataChecker<E, H, B, S> {
H::Out: Ord + codec::Codec, H::Out: Ord + codec::Codec,
{ {
// all the checks are sharing the same storage // all the checks are sharing the same storage
let storage = create_proof_check_backend_storage(remote_roots_proof); let storage = remote_roots_proof.into_memory_db();
// remote_roots.keys() are sorted => we can use this to group changes tries roots // remote_roots.keys() are sorted => we can use this to group changes tries roots
// that are belongs to the same CHT // that are belongs to the same CHT
@@ -187,7 +187,8 @@ impl<E, H, B: BlockT, S: BlockchainStorage<B>> LightDataChecker<E, H, B, S> {
local_cht_root, local_cht_root,
block, block,
remote_changes_trie_root, remote_changes_trie_root,
&proving_backend)?; &proving_backend,
)?;
// and return the storage to use in following checks // and return the storage to use in following checks
storage = proving_backend.into_storage(); storage = proving_backend.into_storage();
@@ -270,7 +271,7 @@ impl<E, Block, H, S> FetchChecker<Block> for LightDataChecker<E, H, Block, S>
body: Vec<Block::Extrinsic> body: Vec<Block::Extrinsic>
) -> ClientResult<Vec<Block::Extrinsic>> { ) -> ClientResult<Vec<Block::Extrinsic>> {
// TODO: #2621 // TODO: #2621
let extrinsics_root = HashFor::<Block>::ordered_trie_root( let extrinsics_root = HashFor::<Block>::ordered_trie_root(
body.iter().map(Encode::encode).collect(), body.iter().map(Encode::encode).collect(),
); );
if *request.header.extrinsics_root() == extrinsics_root { if *request.header.extrinsics_root() == extrinsics_root {
@@ -294,7 +295,7 @@ struct RootsStorage<'a, Number: AtLeast32Bit, Hash: 'a> {
impl<'a, H, Number, Hash> ChangesTrieRootsStorage<H, Number> for RootsStorage<'a, Number, Hash> impl<'a, H, Number, Hash> ChangesTrieRootsStorage<H, Number> for RootsStorage<'a, Number, Hash>
where where
H: Hasher, H: Hasher,
Number: ::std::fmt::Display + ::std::hash::Hash + Clone + AtLeast32Bit + Encode + Decode + Send + Sync + 'static, Number: std::fmt::Display + std::hash::Hash + Clone + AtLeast32Bit + Encode + Decode + Send + Sync + 'static,
Hash: 'a + Send + Sync + Clone + AsRef<[u8]>, Hash: 'a + Send + Sync + Clone + AsRef<[u8]>,
{ {
fn build_anchor( fn build_anchor(
+1 -1
View File
@@ -18,7 +18,7 @@ use criterion::{Criterion, criterion_group, criterion_main, black_box};
use frame_system as system; use frame_system as system;
use frame_support::{decl_module, decl_event, impl_outer_origin, impl_outer_event, weights::Weight}; use frame_support::{decl_module, decl_event, impl_outer_origin, impl_outer_event, weights::Weight};
use sp_core::H256; use sp_core::H256;
use sp_runtime::{Perbill, PerThing, traits::{BlakeTwo256, IdentityLookup}, testing::Header}; use sp_runtime::{Perbill, traits::{BlakeTwo256, IdentityLookup}, testing::Header};
mod module { mod module {
use super::*; use super::*;
+1
View File
@@ -24,6 +24,7 @@ rustversion = "1.0.0"
[dev-dependencies] [dev-dependencies]
criterion = "0.3.0" criterion = "0.3.0"
substrate-test-runtime-client = { version = "2.0.0-dev", path = "../../../test-utils/runtime/client" } substrate-test-runtime-client = { version = "2.0.0-dev", path = "../../../test-utils/runtime/client" }
sp-core = { version = "2.0.0-alpha.1", path = "../../core" }
[[bench]] [[bench]]
name = "bench" name = "bench"
@@ -164,6 +164,12 @@ fn record_proof_works() {
let block_id = BlockId::Number(client.chain_info().best_number); let block_id = BlockId::Number(client.chain_info().best_number);
let storage_root = longest_chain.best_chain().unwrap().state_root().clone(); let storage_root = longest_chain.best_chain().unwrap().state_root().clone();
let runtime_code = sp_core::traits::RuntimeCode {
code: client.code_at(&block_id).unwrap(),
hash: vec![1],
heap_pages: None,
};
let transaction = Transfer { let transaction = Transfer {
amount: 1000, amount: 1000,
nonce: 0, nonce: 0,
@@ -192,5 +198,6 @@ fn record_proof_works() {
&executor, &executor,
"Core_execute_block", "Core_execute_block",
&block.encode(), &block.encode(),
&runtime_code,
).expect("Executes block while using the proof backend"); ).expect("Executes block while using the proof backend");
} }
+65
View File
@@ -98,6 +98,7 @@ pub trait CodeExecutor: Sized + Send + Sync + CallInWasm + Clone + 'static {
>( >(
&self, &self,
ext: &mut E, ext: &mut E,
runtime_code: &RuntimeCode,
method: &str, method: &str,
data: &[u8], data: &[u8],
use_native: bool, use_native: bool,
@@ -105,6 +106,70 @@ pub trait CodeExecutor: Sized + Send + Sync + CallInWasm + Clone + 'static {
) -> (Result<crate::NativeOrEncoded<R>, Self::Error>, bool); ) -> (Result<crate::NativeOrEncoded<R>, Self::Error>, bool);
} }
/// The Wasm code of a Substrate runtime.
#[derive(Debug, Clone, codec::Encode, codec::Decode)]
pub struct RuntimeCode {
/// The actual Wasm code as binary blob.
pub code: Vec<u8>,
/// The optional heap pages this `code` should be executed with.
///
/// If `None` are given, the default value of the executor will be used.
pub heap_pages: Option<u64>,
/// The SCALE encoded hash of `code`.
///
/// The hashing algorithm isn't that important, as long as all runtime
/// code instances use the same.
pub hash: Vec<u8>,
}
impl PartialEq for RuntimeCode {
fn eq(&self, other: &Self) -> bool {
self.hash == other.hash
}
}
impl RuntimeCode {
/// Create an `RuntimeCode` instance from the given `Externalities`.
///
/// Extracts the code and the heap pages using the well known keys.
///
/// Returns an error if the code could not be found.
pub fn from_externalities(ext: &dyn Externalities) -> Result<Self, CodeNotFound> {
let code = ext.storage(sp_storage::well_known_keys::CODE).ok_or(CodeNotFound)?;
let hash = ext.storage_hash(sp_storage::well_known_keys::CODE).ok_or(CodeNotFound)?;
let heap_pages = ext.storage(sp_storage::well_known_keys::HEAP_PAGES)
.and_then(|hp| codec::Decode::decode(&mut &hp[..]).ok());
Ok(Self {
code,
hash,
heap_pages,
})
}
/// Create an empty instance.
///
/// This is only useful for tests that don't want to execute any code.
pub fn empty() -> Self {
Self {
code: Vec::new(),
hash: Vec::new(),
heap_pages: None,
}
}
}
/// Could not find the `:code` in the externalities while initializing the [`RuntimeCode`].
#[derive(Debug)]
pub struct CodeNotFound;
impl std::fmt::Display for CodeNotFound {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
write!(f, "the storage entry `:code` doesn't have any code")
}
}
/// Something that can call a method in a WASM blob. /// Something that can call a method in a WASM blob.
pub trait CallInWasm: Send + Sync { pub trait CallInWasm: Send + Sync {
/// Call the given `method` in the given `wasm_blob` using `call_data` (SCALE encoded arguments) /// Call the given `method` in the given `wasm_blob` using `call_data` (SCALE encoded arguments)
@@ -25,7 +25,10 @@ use serde::{Deserialize, Serialize};
use sp_std::prelude::*; use sp_std::prelude::*;
use sp_core::RuntimeDebug; use sp_core::RuntimeDebug;
use crate::codec::{Codec, Encode, Decode}; use crate::codec::{Codec, Encode, Decode};
use crate::traits::{self, Member, Block as BlockT, Header as HeaderT, MaybeSerialize, MaybeMallocSizeOf}; use crate::traits::{
self, Member, Block as BlockT, Header as HeaderT, MaybeSerialize, MaybeMallocSizeOf,
NumberFor,
};
use crate::Justification; use crate::Justification;
/// Something to identify a block. /// Something to identify a block.
@@ -35,9 +38,9 @@ use crate::Justification;
#[cfg_attr(feature = "std", serde(deny_unknown_fields))] #[cfg_attr(feature = "std", serde(deny_unknown_fields))]
pub enum BlockId<Block: BlockT> { pub enum BlockId<Block: BlockT> {
/// Identify by block header hash. /// Identify by block header hash.
Hash(<<Block as BlockT>::Header as HeaderT>::Hash), Hash(Block::Hash),
/// Identify by block number. /// Identify by block number.
Number(<<Block as BlockT>::Header as HeaderT>::Number), Number(NumberFor<Block>),
} }
impl<Block: BlockT> BlockId<Block> { impl<Block: BlockT> BlockId<Block> {
@@ -47,7 +50,7 @@ impl<Block: BlockT> BlockId<Block> {
} }
/// Create a block ID from a number. /// Create a block ID from a number.
pub fn number(number: <Block::Header as HeaderT>::Number) -> Self { pub fn number(number: NumberFor<Block>) -> Self {
BlockId::Number(number) BlockId::Number(number)
} }
} }
@@ -18,9 +18,9 @@
use log::warn; use log::warn;
use hash_db::Hasher; use hash_db::Hasher;
use codec::Encode; use codec::{Decode, Encode};
use sp_core::storage::{ChildInfo, OwnedChildInfo}; use sp_core::{traits::RuntimeCode, storage::{ChildInfo, OwnedChildInfo, well_known_keys}};
use sp_trie::{TrieMut, MemoryDB, trie_types::TrieDBMut}; use sp_trie::{TrieMut, MemoryDB, trie_types::TrieDBMut};
use crate::{ use crate::{
@@ -359,3 +359,22 @@ pub(crate) fn insert_into_memory_db<H, I>(mdb: &mut MemoryDB<H>, input: I) -> Op
Some(root) Some(root)
} }
/// Get the runtime code from the given `backend`.
///
/// Returns an error if the `:code` could not be found.
pub fn get_runtime_code<H: Hasher, B: Backend<H>>(backend: &B) -> Result<RuntimeCode, &'static str>
where H::Out: Encode,
{
let code = backend.storage(well_known_keys::CODE)
.ok()
.flatten()
.ok_or("`:code` not found")?;
let hash = H::hash(&code).encode();
let heap_pages = backend.storage(well_known_keys::HEAP_PAGES)
.ok()
.flatten()
.and_then(|d| Decode::decode(&mut &d[..]).ok());
Ok(RuntimeCode { code, hash, heap_pages })
}
+56 -10
View File
@@ -23,8 +23,8 @@ use log::{warn, trace};
use hash_db::Hasher; use hash_db::Hasher;
use codec::{Decode, Encode, Codec}; use codec::{Decode, Encode, Codec};
use sp_core::{ use sp_core::{
storage::ChildInfo, NativeOrEncoded, NeverNativeValue, storage::ChildInfo, NativeOrEncoded, NeverNativeValue, hexdisplay::HexDisplay,
traits::{CodeExecutor, CallInWasmExt}, hexdisplay::HexDisplay, traits::{CodeExecutor, CallInWasmExt, RuntimeCode},
}; };
use overlayed_changes::OverlayedChangeSet; use overlayed_changes::OverlayedChangeSet;
use sp_externalities::Extensions; use sp_externalities::Extensions;
@@ -42,7 +42,7 @@ mod trie_backend;
mod trie_backend_essence; mod trie_backend_essence;
mod stats; mod stats;
pub use sp_trie::{trie_types::{Layout, TrieDBMut}, TrieMut, DBValue, MemoryDB}; pub use sp_trie::{trie_types::{Layout, TrieDBMut}, StorageProof, TrieMut, DBValue, MemoryDB};
pub use testing::TestExternalities; pub use testing::TestExternalities;
pub use basic::BasicExternalities; pub use basic::BasicExternalities;
pub use ext::Ext; pub use ext::Ext;
@@ -67,8 +67,7 @@ pub use overlayed_changes::{
StorageCollection, ChildStorageCollection, StorageCollection, ChildStorageCollection,
}; };
pub use proving_backend::{ pub use proving_backend::{
create_proof_check_backend, create_proof_check_backend_storage, merge_storage_proofs, create_proof_check_backend, ProofRecorder, ProvingBackend, ProvingBackendRecorder,
ProofRecorder, ProvingBackend, ProvingBackendRecorder, StorageProof,
}; };
pub use trie_backend_essence::{TrieBackendStorage, Storage}; pub use trie_backend_essence::{TrieBackendStorage, Storage};
pub use trie_backend::TrieBackend; pub use trie_backend::TrieBackend;
@@ -191,6 +190,7 @@ pub struct StateMachine<'a, B, H, N, Exec>
changes_trie_state: Option<ChangesTrieState<'a, H, N>>, changes_trie_state: Option<ChangesTrieState<'a, H, N>>,
_marker: PhantomData<(H, N)>, _marker: PhantomData<(H, N)>,
storage_transaction_cache: Option<&'a mut StorageTransactionCache<B::Transaction, H, N>>, storage_transaction_cache: Option<&'a mut StorageTransactionCache<B::Transaction, H, N>>,
runtime_code: &'a RuntimeCode,
} }
impl<'a, B, H, N, Exec> StateMachine<'a, B, H, N, Exec> where impl<'a, B, H, N, Exec> StateMachine<'a, B, H, N, Exec> where
@@ -209,6 +209,7 @@ impl<'a, B, H, N, Exec> StateMachine<'a, B, H, N, Exec> where
method: &'a str, method: &'a str,
call_data: &'a [u8], call_data: &'a [u8],
mut extensions: Extensions, mut extensions: Extensions,
runtime_code: &'a RuntimeCode,
) -> Self { ) -> Self {
extensions.register(CallInWasmExt::new(exec.clone())); extensions.register(CallInWasmExt::new(exec.clone()));
@@ -222,6 +223,7 @@ impl<'a, B, H, N, Exec> StateMachine<'a, B, H, N, Exec> where
changes_trie_state, changes_trie_state,
_marker: PhantomData, _marker: PhantomData,
storage_transaction_cache: None, storage_transaction_cache: None,
runtime_code,
} }
} }
@@ -292,6 +294,7 @@ impl<'a, B, H, N, Exec> StateMachine<'a, B, H, N, Exec> where
let (result, was_native) = self.exec.call( let (result, was_native) = self.exec.call(
&mut ext, &mut ext,
self.runtime_code,
self.method, self.method,
self.call_data, self.call_data,
use_native, use_native,
@@ -436,6 +439,7 @@ pub fn prove_execution<B, H, N, Exec>(
exec: &Exec, exec: &Exec,
method: &str, method: &str,
call_data: &[u8], call_data: &[u8],
runtime_code: &RuntimeCode,
) -> Result<(Vec<u8>, StorageProof), Box<dyn Error>> ) -> Result<(Vec<u8>, StorageProof), Box<dyn Error>>
where where
B: Backend<H>, B: Backend<H>,
@@ -446,7 +450,14 @@ where
{ {
let trie_backend = backend.as_trie_backend() let trie_backend = backend.as_trie_backend()
.ok_or_else(|| Box::new(ExecutionError::UnableToGenerateProof) as Box<dyn Error>)?; .ok_or_else(|| Box::new(ExecutionError::UnableToGenerateProof) as Box<dyn Error>)?;
prove_execution_on_trie_backend::<_, _, N, _>(trie_backend, overlay, exec, method, call_data) prove_execution_on_trie_backend::<_, _, N, _>(
trie_backend,
overlay,
exec,
method,
call_data,
runtime_code,
)
} }
/// Prove execution using the given trie backend, overlayed changes, and call executor. /// Prove execution using the given trie backend, overlayed changes, and call executor.
@@ -464,6 +475,7 @@ pub fn prove_execution_on_trie_backend<S, H, N, Exec>(
exec: &Exec, exec: &Exec,
method: &str, method: &str,
call_data: &[u8], call_data: &[u8],
runtime_code: &RuntimeCode,
) -> Result<(Vec<u8>, StorageProof), Box<dyn Error>> ) -> Result<(Vec<u8>, StorageProof), Box<dyn Error>>
where where
S: trie_backend_essence::TrieBackendStorage<H>, S: trie_backend_essence::TrieBackendStorage<H>,
@@ -474,7 +486,14 @@ where
{ {
let proving_backend = proving_backend::ProvingBackend::new(trie_backend); let proving_backend = proving_backend::ProvingBackend::new(trie_backend);
let mut sm = StateMachine::<_, H, N, Exec>::new( let mut sm = StateMachine::<_, H, N, Exec>::new(
&proving_backend, None, overlay, exec, method, call_data, Extensions::default(), &proving_backend,
None,
overlay,
exec,
method,
call_data,
Extensions::default(),
runtime_code,
); );
let result = sm.execute_using_consensus_failure_handler::<_, NeverNativeValue, fn() -> _>( let result = sm.execute_using_consensus_failure_handler::<_, NeverNativeValue, fn() -> _>(
@@ -493,6 +512,7 @@ pub fn execution_proof_check<H, N, Exec>(
exec: &Exec, exec: &Exec,
method: &str, method: &str,
call_data: &[u8], call_data: &[u8],
runtime_code: &RuntimeCode,
) -> Result<Vec<u8>, Box<dyn Error>> ) -> Result<Vec<u8>, Box<dyn Error>>
where where
H: Hasher, H: Hasher,
@@ -501,7 +521,14 @@ where
N: crate::changes_trie::BlockNumber, N: crate::changes_trie::BlockNumber,
{ {
let trie_backend = create_proof_check_backend::<H>(root.into(), proof)?; let trie_backend = create_proof_check_backend::<H>(root.into(), proof)?;
execution_proof_check_on_trie_backend::<_, N, _>(&trie_backend, overlay, exec, method, call_data) execution_proof_check_on_trie_backend::<_, N, _>(
&trie_backend,
overlay,
exec,
method,
call_data,
runtime_code,
)
} }
/// Check execution proof on proving backend, generated by `prove_execution` call. /// Check execution proof on proving backend, generated by `prove_execution` call.
@@ -511,6 +538,7 @@ pub fn execution_proof_check_on_trie_backend<H, N, Exec>(
exec: &Exec, exec: &Exec,
method: &str, method: &str,
call_data: &[u8], call_data: &[u8],
runtime_code: &RuntimeCode,
) -> Result<Vec<u8>, Box<dyn Error>> ) -> Result<Vec<u8>, Box<dyn Error>>
where where
H: Hasher, H: Hasher,
@@ -519,7 +547,14 @@ where
N: crate::changes_trie::BlockNumber, N: crate::changes_trie::BlockNumber,
{ {
let mut sm = StateMachine::<_, H, N, Exec>::new( let mut sm = StateMachine::<_, H, N, Exec>::new(
trie_backend, None, overlay, exec, method, call_data, Extensions::default(), trie_backend,
None,
overlay,
exec,
method,
call_data,
Extensions::default(),
runtime_code,
); );
sm.execute_using_consensus_failure_handler::<_, NeverNativeValue, fn() -> _>( sm.execute_using_consensus_failure_handler::<_, NeverNativeValue, fn() -> _>(
@@ -692,7 +727,9 @@ mod tests {
use super::*; use super::*;
use super::ext::Ext; use super::ext::Ext;
use super::changes_trie::Configuration as ChangesTrieConfig; use super::changes_trie::Configuration as ChangesTrieConfig;
use sp_core::{Blake2Hasher, map, traits::Externalities, storage::ChildStorageKey}; use sp_core::{
Blake2Hasher, map, traits::{Externalities, RuntimeCode}, storage::ChildStorageKey,
};
#[derive(Clone)] #[derive(Clone)]
struct DummyCodeExecutor { struct DummyCodeExecutor {
@@ -714,6 +751,7 @@ mod tests {
>( >(
&self, &self,
ext: &mut E, ext: &mut E,
_: &RuntimeCode,
_method: &str, _method: &str,
_data: &[u8], _data: &[u8],
use_native: bool, use_native: bool,
@@ -767,6 +805,7 @@ mod tests {
fn execute_works() { fn execute_works() {
let backend = trie_backend::tests::test_trie(); let backend = trie_backend::tests::test_trie();
let mut overlayed_changes = Default::default(); let mut overlayed_changes = Default::default();
let wasm_code = RuntimeCode::empty();
let mut state_machine = StateMachine::new( let mut state_machine = StateMachine::new(
&backend, &backend,
@@ -781,6 +820,7 @@ mod tests {
"test", "test",
&[], &[],
Default::default(), Default::default(),
&wasm_code,
); );
assert_eq!( assert_eq!(
@@ -794,6 +834,7 @@ mod tests {
fn execute_works_with_native_else_wasm() { fn execute_works_with_native_else_wasm() {
let backend = trie_backend::tests::test_trie(); let backend = trie_backend::tests::test_trie();
let mut overlayed_changes = Default::default(); let mut overlayed_changes = Default::default();
let wasm_code = RuntimeCode::empty();
let mut state_machine = StateMachine::new( let mut state_machine = StateMachine::new(
&backend, &backend,
@@ -808,6 +849,7 @@ mod tests {
"test", "test",
&[], &[],
Default::default(), Default::default(),
&wasm_code,
); );
assert_eq!(state_machine.execute(ExecutionStrategy::NativeElseWasm).unwrap(), vec![66]); assert_eq!(state_machine.execute(ExecutionStrategy::NativeElseWasm).unwrap(), vec![66]);
@@ -818,6 +860,7 @@ mod tests {
let mut consensus_failed = false; let mut consensus_failed = false;
let backend = trie_backend::tests::test_trie(); let backend = trie_backend::tests::test_trie();
let mut overlayed_changes = Default::default(); let mut overlayed_changes = Default::default();
let wasm_code = RuntimeCode::empty();
let mut state_machine = StateMachine::new( let mut state_machine = StateMachine::new(
&backend, &backend,
@@ -832,6 +875,7 @@ mod tests {
"test", "test",
&[], &[],
Default::default(), Default::default(),
&wasm_code,
); );
assert!( assert!(
@@ -864,6 +908,7 @@ mod tests {
&executor, &executor,
"test", "test",
&[], &[],
&RuntimeCode::empty(),
).unwrap(); ).unwrap();
// check proof locally // check proof locally
@@ -874,6 +919,7 @@ mod tests {
&executor, &executor,
"test", "test",
&[], &[],
&RuntimeCode::empty(),
).unwrap(); ).unwrap();
// check that both results are correct // check that both results are correct
@@ -18,19 +18,19 @@
use std::sync::Arc; use std::sync::Arc;
use parking_lot::RwLock; use parking_lot::RwLock;
use codec::{Decode, Encode, Codec}; use codec::{Decode, Codec};
use log::debug; use log::debug;
use hash_db::{Hasher, HashDB, EMPTY_PREFIX, Prefix}; use hash_db::{Hasher, HashDB, EMPTY_PREFIX, Prefix};
use sp_trie::{ use sp_trie::{
MemoryDB, default_child_trie_root, read_trie_value_with, read_child_trie_value_with, MemoryDB, default_child_trie_root, read_trie_value_with, read_child_trie_value_with,
record_all_keys record_all_keys, StorageProof,
}; };
pub use sp_trie::Recorder; pub use sp_trie::Recorder;
pub use sp_trie::trie_types::{Layout, TrieError}; pub use sp_trie::trie_types::{Layout, TrieError};
use crate::trie_backend::TrieBackend; use crate::trie_backend::TrieBackend;
use crate::trie_backend_essence::{Ephemeral, TrieBackendEssence, TrieBackendStorage}; use crate::trie_backend_essence::{Ephemeral, TrieBackendEssence, TrieBackendStorage};
use crate::{Error, ExecutionError, Backend}; use crate::{Error, ExecutionError, Backend};
use std::collections::{HashMap, HashSet}; use std::collections::HashMap;
use crate::DBValue; use crate::DBValue;
use sp_core::storage::ChildInfo; use sp_core::storage::ChildInfo;
@@ -40,82 +40,6 @@ pub struct ProvingBackendRecorder<'a, S: 'a + TrieBackendStorage<H>, H: 'a + Has
pub(crate) proof_recorder: &'a mut Recorder<H::Out>, pub(crate) proof_recorder: &'a mut Recorder<H::Out>,
} }
/// A proof that some set of key-value pairs are included in the storage trie. The proof contains
/// the storage values so that the partial storage backend can be reconstructed by a verifier that
/// does not already have access to the key-value pairs.
///
/// The proof consists of the set of serialized nodes in the storage trie accessed when looking up
/// the keys covered by the proof. Verifying the proof requires constructing the partial trie from
/// the serialized nodes and performing the key lookups.
#[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)]
pub struct StorageProof {
trie_nodes: Vec<Vec<u8>>,
}
impl StorageProof {
/// Constructs a storage proof from a subset of encoded trie nodes in a storage backend.
pub fn new(trie_nodes: Vec<Vec<u8>>) -> Self {
StorageProof { trie_nodes }
}
/// Returns a new empty proof.
///
/// An empty proof is capable of only proving trivial statements (ie. that an empty set of
/// key-value pairs exist in storage).
pub fn empty() -> Self {
StorageProof {
trie_nodes: Vec::new(),
}
}
/// Returns whether this is an empty proof.
pub fn is_empty(&self) -> bool {
self.trie_nodes.is_empty()
}
/// Create an iterator over trie nodes constructed from the proof. The nodes are not guaranteed
/// to be traversed in any particular order.
pub fn iter_nodes(self) -> StorageProofNodeIterator {
StorageProofNodeIterator::new(self)
}
}
/// An iterator over trie nodes constructed from a storage proof. The nodes are not guaranteed to
/// be traversed in any particular order.
pub struct StorageProofNodeIterator {
inner: <Vec<Vec<u8>> as IntoIterator>::IntoIter,
}
impl StorageProofNodeIterator {
fn new(proof: StorageProof) -> Self {
StorageProofNodeIterator {
inner: proof.trie_nodes.into_iter(),
}
}
}
impl Iterator for StorageProofNodeIterator {
type Item = Vec<u8>;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
}
/// Merges multiple storage proofs covering potentially different sets of keys into one proof
/// covering all keys. The merged proof output may be smaller than the aggregate size of the input
/// proofs due to deduplication of trie nodes.
pub fn merge_storage_proofs<I>(proofs: I) -> StorageProof
where I: IntoIterator<Item=StorageProof>
{
let trie_nodes = proofs.into_iter()
.flat_map(|proof| proof.iter_nodes())
.collect::<HashSet<_>>()
.into_iter()
.collect();
StorageProof { trie_nodes }
}
impl<'a, S, H> ProvingBackendRecorder<'a, S, H> impl<'a, S, H> ProvingBackendRecorder<'a, S, H>
where where
S: TrieBackendStorage<H>, S: TrieBackendStorage<H>,
@@ -222,7 +146,7 @@ impl<'a, S: 'a + TrieBackendStorage<H>, H: 'a + Hasher> ProvingBackend<'a, S, H>
let root = essence.root().clone(); let root = essence.root().clone();
let recorder = ProofRecorderBackend { let recorder = ProofRecorderBackend {
backend: essence.backend_storage(), backend: essence.backend_storage(),
proof_recorder: proof_recorder, proof_recorder,
}; };
ProvingBackend(TrieBackend::new(recorder, root)) ProvingBackend(TrieBackend::new(recorder, root))
} }
@@ -370,7 +294,7 @@ where
H: Hasher, H: Hasher,
H::Out: Codec, H::Out: Codec,
{ {
let db = create_proof_check_backend_storage(proof); let db = proof.into_memory_db();
if db.contains(&root, EMPTY_PREFIX) { if db.contains(&root, EMPTY_PREFIX) {
Ok(TrieBackend::new(db, root)) Ok(TrieBackend::new(db, root))
@@ -379,20 +303,6 @@ where
} }
} }
/// Create in-memory storage of proof check backend.
pub fn create_proof_check_backend_storage<H>(
proof: StorageProof,
) -> MemoryDB<H>
where
H: Hasher,
{
let mut db = MemoryDB::default();
for item in proof.iter_nodes() {
db.insert(EMPTY_PREFIX, &item);
}
db
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::InMemoryBackend; use crate::InMemoryBackend;
+2
View File
@@ -21,6 +21,7 @@
mod error; mod error;
mod node_header; mod node_header;
mod node_codec; mod node_codec;
mod storage_proof;
mod trie_stream; mod trie_stream;
use sp_std::boxed::Box; use sp_std::boxed::Box;
@@ -35,6 +36,7 @@ pub use error::Error;
pub use trie_stream::TrieStream; pub use trie_stream::TrieStream;
/// The Substrate format implementation of `NodeCodec`. /// The Substrate format implementation of `NodeCodec`.
pub use node_codec::NodeCodec; pub use node_codec::NodeCodec;
pub use storage_proof::StorageProof;
/// Various re-exports from the `trie-db` crate. /// Various re-exports from the `trie-db` crate.
pub use trie_db::{ pub use trie_db::{
Trie, TrieMut, DBValue, Recorder, CError, Query, TrieLayout, TrieConfiguration, nibble_ops, TrieDBIterator, Trie, TrieMut, DBValue, Recorder, CError, Query, TrieLayout, TrieConfiguration, nibble_ops, TrieDBIterator,
@@ -0,0 +1,109 @@
// Copyright 2020 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Parity 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.
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
use sp_std::vec::Vec;
use codec::{Encode, Decode};
use hash_db::{Hasher, HashDB};
/// A proof that some set of key-value pairs are included in the storage trie. The proof contains
/// the storage values so that the partial storage backend can be reconstructed by a verifier that
/// does not already have access to the key-value pairs.
///
/// The proof consists of the set of serialized nodes in the storage trie accessed when looking up
/// the keys covered by the proof. Verifying the proof requires constructing the partial trie from
/// the serialized nodes and performing the key lookups.
#[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)]
pub struct StorageProof {
trie_nodes: Vec<Vec<u8>>,
}
impl StorageProof {
/// Constructs a storage proof from a subset of encoded trie nodes in a storage backend.
pub fn new(trie_nodes: Vec<Vec<u8>>) -> Self {
StorageProof { trie_nodes }
}
/// Returns a new empty proof.
///
/// An empty proof is capable of only proving trivial statements (ie. that an empty set of
/// key-value pairs exist in storage).
pub fn empty() -> Self {
StorageProof {
trie_nodes: Vec::new(),
}
}
/// Returns whether this is an empty proof.
pub fn is_empty(&self) -> bool {
self.trie_nodes.is_empty()
}
/// Create an iterator over trie nodes constructed from the proof. The nodes are not guaranteed
/// to be traversed in any particular order.
pub fn iter_nodes(self) -> StorageProofNodeIterator {
StorageProofNodeIterator::new(self)
}
/// Creates a `MemoryDB` from `Self`.
pub fn into_memory_db<H: Hasher>(self) -> crate::MemoryDB<H> {
self.into()
}
/// Merges multiple storage proofs covering potentially different sets of keys into one proof
/// covering all keys. The merged proof output may be smaller than the aggregate size of the input
/// proofs due to deduplication of trie nodes.
pub fn merge<I>(proofs: I) -> Self where I: IntoIterator<Item=Self> {
let trie_nodes = proofs.into_iter()
.flat_map(|proof| proof.iter_nodes())
.collect::<sp_std::collections::btree_set::BTreeSet<_>>()
.into_iter()
.collect();
Self { trie_nodes }
}
}
/// An iterator over trie nodes constructed from a storage proof. The nodes are not guaranteed to
/// be traversed in any particular order.
pub struct StorageProofNodeIterator {
inner: <Vec<Vec<u8>> as IntoIterator>::IntoIter,
}
impl StorageProofNodeIterator {
fn new(proof: StorageProof) -> Self {
StorageProofNodeIterator {
inner: proof.trie_nodes.into_iter(),
}
}
}
impl Iterator for StorageProofNodeIterator {
type Item = Vec<u8>;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
}
impl<H: Hasher> From<StorageProof> for crate::MemoryDB<H> {
fn from(proof: StorageProof) -> Self {
let mut db = crate::MemoryDB::default();
for item in proof.iter_nodes() {
db.insert(crate::EMPTY_PREFIX, &item);
}
db
}
}
+7 -1
View File
@@ -338,7 +338,7 @@ mod tests {
use sp_io::TestExternalities; use sp_io::TestExternalities;
use substrate_test_runtime_client::{AccountKeyring, Sr25519Keyring}; use substrate_test_runtime_client::{AccountKeyring, Sr25519Keyring};
use crate::{Header, Transfer, WASM_BINARY}; use crate::{Header, Transfer, WASM_BINARY};
use sp_core::{NeverNativeValue, map, traits::CodeExecutor}; use sp_core::{NeverNativeValue, map, traits::{CodeExecutor, RuntimeCode}};
use sc_executor::{NativeExecutor, WasmExecutionMethod, native_executor_instance}; use sc_executor::{NativeExecutor, WasmExecutionMethod, native_executor_instance};
use sp_io::hashing::twox_128; use sp_io::hashing::twox_128;
@@ -401,8 +401,11 @@ mod tests {
fn block_import_works_wasm() { fn block_import_works_wasm() {
block_import_works(|b, ext| { block_import_works(|b, ext| {
let mut ext = ext.ext(); let mut ext = ext.ext();
let runtime_code = RuntimeCode::from_externalities(&ext)
.expect("Code is part of the externalities");
executor().call::<_, NeverNativeValue, fn() -> _>( executor().call::<_, NeverNativeValue, fn() -> _>(
&mut ext, &mut ext,
&runtime_code,
"Core_execute_block", "Core_execute_block",
&b.encode(), &b.encode(),
false, false,
@@ -494,8 +497,11 @@ mod tests {
fn block_import_with_transaction_works_wasm() { fn block_import_with_transaction_works_wasm() {
block_import_with_transaction_works(|b, ext| { block_import_with_transaction_works(|b, ext| {
let mut ext = ext.ext(); let mut ext = ext.ext();
let runtime_code = RuntimeCode::from_externalities(&ext)
.expect("Code is part of the externalities");
executor().call::<_, NeverNativeValue, fn() -> _>( executor().call::<_, NeverNativeValue, fn() -> _>(
&mut ext, &mut ext,
&runtime_code,
"Core_execute_block", "Core_execute_block",
&b.encode(), &b.encode(),
false, false,
@@ -16,5 +16,6 @@ sc-client = { version = "0.8.0-alpha.2", path = "../../../client" }
sc-client-db = { version = "0.8.0-alpha.2", path = "../../../client/db" } sc-client-db = { version = "0.8.0-alpha.2", path = "../../../client/db" }
sc-executor = { version = "0.8.0-alpha.2", path = "../../../client/executor" } sc-executor = { version = "0.8.0-alpha.2", path = "../../../client/executor" }
sp-runtime = { version = "2.0.0-alpha.2", path = "../../../primitives/runtime" } sp-runtime = { version = "2.0.0-alpha.2", path = "../../../primitives/runtime" }
sp-state-machine = { version = "0.8.0-alpha.2", path = "../../../primitives/state-machine" }
structopt = "0.3.8" structopt = "0.3.8"
codec = { version = "1.2.0", package = "parity-scale-codec" } codec = { version = "1.2.0", package = "parity-scale-codec" }
@@ -121,6 +121,7 @@ impl BenchmarkCmd {
self.repeat, self.repeat,
).encode(), ).encode(),
Default::default(), Default::default(),
&sp_state_machine::backend::get_runtime_code(&state)?,
) )
.execute(strategy.into()) .execute(strategy.into())
.map_err(|e| format!("Error executing runtime benchmark: {:?}", e))?; .map_err(|e| format!("Error executing runtime benchmark: {:?}", e))?;