Do not call initialize_block before any runtime api (#8953)

* Do not call `initialize_block` before any runtime api

Before this change we always called `initialize_block` before calling
into the runtime. There was already support with `skip_initialize` to skip
the initialization. Almost no runtime_api requires that
`initialize_block` is called before. Actually this only leads to higher
execution times most of the time, because all runtime modules are
initialized and this is especially expensive when the block contained a
runtime upgrade.

TLDR: Do not call `initialize_block` before calling a runtime api.

* Change `validate_transaction` interface

* Fix rpc test

* Fixes and comments

* Some docs
This commit is contained in:
Bastian Köcher
2021-07-01 17:50:42 +02:00
committed by GitHub
parent 73a6e3effc
commit d489bd70b5
23 changed files with 192 additions and 301 deletions
@@ -30,7 +30,7 @@ use sp_externalities::Extensions;
use sp_core::{
NativeOrEncoded, NeverNativeValue, traits::{CodeExecutor, SpawnNamed, RuntimeCode},
};
use sp_api::{ProofRecorder, InitializeBlock, StorageTransactionCache};
use sp_api::{ProofRecorder, StorageTransactionCache};
use sc_client_api::{backend, call_executor::CallExecutor};
use super::{client::ClientConfig, wasm_override::WasmOverride, wasm_substitutes::WasmSubstitutes};
@@ -173,8 +173,6 @@ where
}
fn contextual_call<
'a,
IB: Fn() -> sp_blockchain::Result<()>,
EM: Fn(
Result<NativeOrEncoded<R>, Self::Error>,
Result<NativeOrEncoded<R>, Self::Error>
@@ -183,7 +181,6 @@ where
NC: FnOnce() -> result::Result<R, sp_api::ApiError> + UnwindSafe,
>(
&self,
initialize_block_fn: IB,
at: &BlockId<Block>,
method: &str,
call_data: &[u8],
@@ -191,21 +188,11 @@ where
storage_transaction_cache: Option<&RefCell<
StorageTransactionCache<Block, B::State>
>>,
initialize_block: InitializeBlock<'a, Block>,
execution_manager: ExecutionManager<EM>,
native_call: Option<NC>,
recorder: &Option<ProofRecorder<Block>>,
extensions: Option<Extensions>,
) -> Result<NativeOrEncoded<R>, sp_blockchain::Error> where ExecutionManager<EM>: Clone {
match initialize_block {
InitializeBlock::Do(ref init_block)
if init_block.borrow().as_ref().map(|id| id != at).unwrap_or(true) => {
initialize_block_fn()?;
},
// We don't need to initialize the runtime at a block.
_ => {},
}
let changes_trie_state = backend::changes_tries_state_at_block(at, self.backend.changes_trie_storage())?;
let mut storage_transaction_cache = storage_transaction_cache.map(|c| c.borrow_mut());
+2 -22
View File
@@ -1243,18 +1243,6 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
trace!("Collected {} uncles", uncles.len());
Ok(uncles)
}
/// Prepare in-memory header that is used in execution environment.
fn prepare_environment_block(&self, parent: &BlockId<Block>) -> sp_blockchain::Result<Block::Header> {
let parent_hash = self.backend.blockchain().expect_block_hash_from_id(parent)?;
Ok(<<Block as BlockT>::Header as HeaderT>::new(
self.backend.blockchain().expect_block_number_from_id(parent)? + One::one(),
Default::default(),
Default::default(),
parent_hash,
Default::default(),
))
}
}
impl<B, E, Block, RA> UsageProvider<Block> for Client<B, E, Block, RA> where
@@ -1313,10 +1301,8 @@ impl<B, E, Block, RA> ProofProvider<Block> for Client<B, E, Block, RA> where
)?;
let state = self.state_at(id)?;
let header = self.prepare_environment_block(id)?;
prove_execution(
state,
header,
&self.executor,
method,
call_data,
@@ -1782,12 +1768,10 @@ impl<B, E, Block, RA> CallApiAt<Block> for Client<B, E, Block, RA> where
'a,
R: Encode + Decode + PartialEq,
NC: FnOnce() -> result::Result<R, sp_api::ApiError> + UnwindSafe,
C: CoreApi<Block>,
>(
&self,
params: CallApiAtParams<'a, Block, C, NC, B::State>,
params: CallApiAtParams<'a, Block, NC, B::State>,
) -> Result<NativeOrEncoded<R>, sp_api::ApiError> {
let core_api = params.core_api;
let at = params.at;
let (manager, extensions) = self.execution_extensions.manager_and_extensions(
@@ -1795,16 +1779,12 @@ impl<B, E, Block, RA> CallApiAt<Block> for Client<B, E, Block, RA> where
params.context,
);
self.executor.contextual_call::<_, fn(_,_) -> _,_,_>(
|| core_api
.initialize_block(at, &self.prepare_environment_block(at)?)
.map_err(Error::RuntimeApiError),
self.executor.contextual_call::<fn(_,_) -> _, _, _>(
at,
params.function,
&params.arguments,
params.overlayed_changes,
Some(params.storage_transaction_cache),
params.initialize_block,
manager,
params.native_call,
params.recorder,
@@ -20,7 +20,6 @@ use sc_light::{
call_executor::{
GenesisCallExecutor,
check_execution_proof,
check_execution_proof_with_make_header,
},
fetcher::LightDataChecker,
blockchain::{BlockchainCache, Blockchain},
@@ -37,7 +36,7 @@ use parking_lot::Mutex;
use substrate_test_runtime_client::{
runtime::{Hash, Block, Header}, TestClient, ClientBlockImportExt,
};
use sp_api::{InitializeBlock, StorageTransactionCache, ProofRecorder};
use sp_api::{StorageTransactionCache, ProofRecorder};
use sp_consensus::BlockOrigin;
use sc_executor::{NativeExecutor, WasmExecutionMethod, RuntimeVersion, NativeVersion};
use sp_core::{H256, NativeOrEncoded, testing::TaskExecutor};
@@ -209,8 +208,6 @@ impl CallExecutor<Block> for DummyCallExecutor {
}
fn contextual_call<
'a,
IB: Fn() -> ClientResult<()>,
EM: Fn(
Result<NativeOrEncoded<R>, Self::Error>,
Result<NativeOrEncoded<R>, Self::Error>
@@ -219,7 +216,6 @@ impl CallExecutor<Block> for DummyCallExecutor {
NC: FnOnce() -> Result<R, sp_api::ApiError> + UnwindSafe,
>(
&self,
_initialize_block_fn: IB,
_at: &BlockId<Block>,
_method: &str,
_call_data: &[u8],
@@ -230,7 +226,6 @@ impl CallExecutor<Block> for DummyCallExecutor {
<Self::Backend as sc_client_api::backend::Backend<Block>>::State,
>
>>,
_initialize_block: InitializeBlock<'a, Block>,
_execution_manager: ExecutionManager<EM>,
_native_call: Option<NC>,
_proof_recorder: &Option<ProofRecorder<Block>>,
@@ -333,36 +328,41 @@ fn execution_proof_is_generated_and_checked() {
(remote_result, local_result)
}
fn execute_with_proof_failure(remote_client: &TestClient, at: u64, method: &'static str) {
fn execute_with_proof_failure(remote_client: &TestClient, at: u64) {
let remote_block_id = BlockId::Number(at);
let remote_header = remote_client.header(&remote_block_id).unwrap().unwrap();
// 'fetch' execution proof from remote node
let (_, remote_execution_proof) = remote_client.execution_proof(
&remote_block_id,
method,
&[]
"Core_initialize_block",
&Header::new(
at,
Default::default(),
Default::default(),
Default::default(),
Default::default(),
).encode(),
).unwrap();
// check remote execution proof locally
let execution_result = check_execution_proof_with_make_header::<_, _, BlakeTwo256, _>(
let execution_result = check_execution_proof::<_, _, BlakeTwo256>(
&local_executor(),
Box::new(TaskExecutor::new()),
&RemoteCallRequest {
block: substrate_test_runtime_client::runtime::Hash::default(),
header: remote_header,
method: method.into(),
call_data: vec![],
header: remote_header.clone(),
method: "Core_initialize_block".into(),
call_data: Header::new(
at + 1,
Default::default(),
Default::default(),
remote_header.hash(),
remote_header.digest().clone(), // this makes next header wrong
).encode(),
retry_count: None,
},
remote_execution_proof,
|header| <Header as HeaderT>::new(
at + 1,
Default::default(),
Default::default(),
header.hash(),
header.digest().clone(), // this makes next header wrong
),
);
match execution_result {
Err(sp_blockchain::Error::Execution(_)) => (),
@@ -389,21 +389,12 @@ fn execution_proof_is_generated_and_checked() {
let (remote, local) = execute(&remote_client, 2, "Core_version");
assert_eq!(remote, local);
// check method that requires environment
let (_, block) = execute(&remote_client, 0, "BlockBuilder_finalize_block");
let local_block: Header = Decode::decode(&mut &block[..]).unwrap();
assert_eq!(local_block.number, 1);
let (_, block) = execute(&remote_client, 2, "BlockBuilder_finalize_block");
let local_block: Header = Decode::decode(&mut &block[..]).unwrap();
assert_eq!(local_block.number, 3);
// check that proof check doesn't panic even if proof is incorrect AND no panic handler is set
execute_with_proof_failure(&remote_client, 2, "Core_version");
execute_with_proof_failure(&remote_client, 2);
// check that proof check doesn't panic even if proof is incorrect AND panic handler is set
sp_panic_handler::set("TEST", "1.2.3");
execute_with_proof_failure(&remote_client, 2, "Core_version");
execute_with_proof_failure(&remote_client, 2);
}
#[test]