mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-28 15:37:56 +00:00
Cumulus changes version 2 (#2313)
* ensure imbalances are properly accounted for (#2183) * ensure imbalances are properly accounted for * bump runtime version * Update node/runtime/src/lib.rs * implement contract events (#2161) * implement contract events * update runtime * renaming * update test code hash * improve complexity details * add deposit event base cost * add test * Revert "add deposit event base cost" This reverts commit 58ec010c0f4f4f0e16935ad41da32aedd17a8c57. * update test * Revert "update test" This reverts commit 6fe61a593ccf0d41f09a0b97472b28ed8751a999. * Revert "Revert "add deposit event base cost"" This reverts commit 145e8a9bac15313a4c380aa66b94fd4d36fa3f6d. * Fix format a bit * Replace Vec<u8> with [u8; 32] for contract storage key (#2184) * Replace Vec<u8> with [u8; 32] for contract storage key * Read storage keys from sandbox memory into fixed size buffer * Increment `impl_version` * Remove redundant Ok(()) and explicitly specify StorageKey buffer type (#2188) * Switch to `derive(Encode, Decode)` for `Call` (#2178) * Add some tests * More tests * Switch to `derive(Encode, Decode)` for `Call` * Update lock files * Simplify the macro cases * Cache changes trie config in db storage (#2170) * cache changes trie config in db storage * Update core/client/db/src/lib.rs Co-Authored-By: svyatonik <svyatonik@gmail.com> * Update core/client/db/src/lib.rs Co-Authored-By: svyatonik <svyatonik@gmail.com> * Fix version check for renamed runtime api methods (#2190) * Add feature to disable including the test-runtime wasm blob * Enable `std` feature for `consensus_authorities` * Implement `skip_initialize_block` and `initialize_block` for runtime api * Add test and fixes bug * Begin to implement support for passing the `ProofRecorder` * Make sure proof generation works as intended * Fixes tests * Make `BlockBuilder` generate proofs on request. * Adds `TestClientBuilder` to simplify creating a test client * Add `include-wasm-blob` to `test-client` as well * Make `test-client` compile without including the wasm file * Disable more stuff in test-client without wasm * Reorganize the re-exports * Use correct bounds * Update docs * Update core/client/src/block_builder/block_builder.rs Co-Authored-By: bkchr <bkchr@users.noreply.github.com> * Extend test to actually generated proof * Switch to enum for `skip_initialize_block` * Some wasm files updates
This commit is contained in:
@@ -24,10 +24,9 @@ use runtime_primitives::traits::{
|
||||
};
|
||||
use primitives::{H256, ExecutionContext};
|
||||
use crate::blockchain::HeaderBackend;
|
||||
use crate::runtime_api::Core;
|
||||
use crate::runtime_api::{Core, ApiExt};
|
||||
use crate::error;
|
||||
|
||||
|
||||
/// Utility for building new (valid) blocks from a stream of extrinsics.
|
||||
pub struct BlockBuilder<'a, Block, A: ProvideRuntimeApi> where Block: BlockT {
|
||||
header: <Block as BlockT>::Header,
|
||||
@@ -44,12 +43,22 @@ where
|
||||
{
|
||||
/// Create a new instance of builder from the given client, building on the latest block.
|
||||
pub fn new(api: &'a A) -> error::Result<Self> {
|
||||
api.info().and_then(|i| Self::at_block(&BlockId::Hash(i.best_hash), api))
|
||||
api.info().and_then(|i|
|
||||
Self::at_block(&BlockId::Hash(i.best_hash), api, false)
|
||||
)
|
||||
}
|
||||
|
||||
/// Create a new instance of builder from the given client using a particular block's ID to
|
||||
/// build upon.
|
||||
pub fn at_block(block_id: &BlockId<Block>, api: &'a A) -> error::Result<Self> {
|
||||
/// Create a new instance of builder from the given client using a
|
||||
/// particular block's ID to build upon with optional proof recording enabled.
|
||||
///
|
||||
/// While proof recording is enabled, all accessed trie nodes are saved.
|
||||
/// These recorded trie nodes can be used by a third party to proof the
|
||||
/// output of this block builder without having access to the full storage.
|
||||
pub fn at_block(
|
||||
block_id: &BlockId<Block>,
|
||||
api: &'a A,
|
||||
proof_recording: bool
|
||||
) -> error::Result<Self> {
|
||||
let number = api.block_number_from_id(block_id)?
|
||||
.ok_or_else(|| error::Error::UnknownBlock(format!("{}", block_id)))?
|
||||
+ One::one();
|
||||
@@ -63,8 +72,17 @@ where
|
||||
parent_hash,
|
||||
Default::default()
|
||||
);
|
||||
let api = api.runtime_api();
|
||||
api.initialize_block_with_context(block_id, ExecutionContext::BlockConstruction, &header)?;
|
||||
|
||||
let mut api = api.runtime_api();
|
||||
|
||||
if proof_recording {
|
||||
api.record_proof();
|
||||
}
|
||||
|
||||
api.initialize_block_with_context(
|
||||
block_id, ExecutionContext::BlockConstruction, &header
|
||||
)?;
|
||||
|
||||
Ok(BlockBuilder {
|
||||
header,
|
||||
extrinsics: Vec::new(),
|
||||
@@ -77,13 +95,15 @@ where
|
||||
///
|
||||
/// This will ensure the extrinsic can be validly executed (by executing it);
|
||||
pub fn push(&mut self, xt: <Block as BlockT>::Extrinsic) -> error::Result<()> {
|
||||
use crate::runtime_api::ApiExt;
|
||||
|
||||
let block_id = &self.block_id;
|
||||
let extrinsics = &mut self.extrinsics;
|
||||
|
||||
self.api.map_api_result(|api| {
|
||||
match api.apply_extrinsic_with_context(block_id, ExecutionContext::BlockConstruction, xt.clone())? {
|
||||
match api.apply_extrinsic_with_context(
|
||||
block_id,
|
||||
ExecutionContext::BlockConstruction,
|
||||
xt.clone()
|
||||
)? {
|
||||
Ok(ApplyOutcome::Success) | Ok(ApplyOutcome::Fail) => {
|
||||
extrinsics.push(xt);
|
||||
Ok(())
|
||||
@@ -97,13 +117,34 @@ where
|
||||
|
||||
/// Consume the builder to return a valid `Block` containing all pushed extrinsics.
|
||||
pub fn bake(mut self) -> error::Result<Block> {
|
||||
self.header = self.api.finalize_block_with_context(&self.block_id, ExecutionContext::BlockConstruction)?;
|
||||
self.bake_impl()?;
|
||||
Ok(<Block as BlockT>::new(self.header, self.extrinsics))
|
||||
}
|
||||
|
||||
fn bake_impl(&mut self) -> error::Result<()> {
|
||||
self.header = self.api.finalize_block_with_context(
|
||||
&self.block_id, ExecutionContext::BlockConstruction
|
||||
)?;
|
||||
|
||||
debug_assert_eq!(
|
||||
self.header.extrinsics_root().clone(),
|
||||
HashFor::<Block>::ordered_trie_root(self.extrinsics.iter().map(Encode::encode)),
|
||||
HashFor::<Block>::ordered_trie_root(
|
||||
self.extrinsics.iter().map(Encode::encode)
|
||||
),
|
||||
);
|
||||
|
||||
Ok(<Block as BlockT>::new(self.header, self.extrinsics))
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Consume the builder to return a valid `Block` containing all pushed extrinsics
|
||||
/// and the generated proof.
|
||||
///
|
||||
/// The proof will be `Some(_)`, if proof recording was enabled while creating
|
||||
/// the block builder.
|
||||
pub fn bake_and_extract_proof(mut self) -> error::Result<(Block, Option<Vec<Vec<u8>>>)> {
|
||||
self.bake_impl()?;
|
||||
|
||||
let proof = self.api.extract_proof();
|
||||
Ok((<Block as BlockT>::new(self.header, self.extrinsics), proof))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,21 +14,25 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::{sync::Arc, cmp::Ord, panic::UnwindSafe, result};
|
||||
use std::{sync::Arc, cmp::Ord, panic::UnwindSafe, result, cell::RefCell, rc::Rc};
|
||||
use parity_codec::{Encode, Decode};
|
||||
use runtime_primitives::generic::BlockId;
|
||||
use runtime_primitives::traits::{Block as BlockT, RuntimeApiInfo};
|
||||
use runtime_primitives::{
|
||||
generic::BlockId, traits::Block as BlockT,
|
||||
};
|
||||
use state_machine::{
|
||||
self, OverlayedChanges, Ext, CodeExecutor, ExecutionManager, ExecutionStrategy, NeverOffchainExt,
|
||||
self, OverlayedChanges, Ext, CodeExecutor, ExecutionManager,
|
||||
ExecutionStrategy, NeverOffchainExt, backend::Backend as _,
|
||||
};
|
||||
use executor::{RuntimeVersion, RuntimeInfo, NativeVersion};
|
||||
use hash_db::Hasher;
|
||||
use trie::MemoryDB;
|
||||
use primitives::{H256, Blake2Hasher, NativeOrEncoded, NeverNativeValue, OffchainExt};
|
||||
use primitives::{
|
||||
H256, Blake2Hasher, NativeOrEncoded, NeverNativeValue, OffchainExt
|
||||
};
|
||||
|
||||
use crate::runtime_api::{ProofRecorder, InitializeBlock};
|
||||
use crate::backend;
|
||||
use crate::error;
|
||||
use crate::runtime_api::Core as CoreApi;
|
||||
|
||||
/// Method call executor.
|
||||
pub trait CallExecutor<B, H>
|
||||
@@ -60,8 +64,9 @@ where
|
||||
/// Before executing the method, passed header is installed as the current header
|
||||
/// of the execution context.
|
||||
fn contextual_call<
|
||||
'a,
|
||||
O: OffchainExt,
|
||||
PB: Fn() -> error::Result<B::Header>,
|
||||
IB: Fn() -> error::Result<()>,
|
||||
EM: Fn(
|
||||
Result<NativeOrEncoded<R>, Self::Error>,
|
||||
Result<NativeOrEncoded<R>, Self::Error>
|
||||
@@ -70,15 +75,16 @@ where
|
||||
NC: FnOnce() -> result::Result<R, &'static str> + UnwindSafe,
|
||||
>(
|
||||
&self,
|
||||
initialize_block_fn: IB,
|
||||
at: &BlockId<B>,
|
||||
method: &str,
|
||||
call_data: &[u8],
|
||||
changes: &mut OverlayedChanges,
|
||||
initialized_block: &mut Option<BlockId<B>>,
|
||||
prepare_environment_block: PB,
|
||||
changes: &RefCell<OverlayedChanges>,
|
||||
initialize_block: InitializeBlock<'a, B>,
|
||||
execution_manager: ExecutionManager<EM>,
|
||||
native_call: Option<NC>,
|
||||
side_effects_handler: Option<&mut O>,
|
||||
proof_recorder: &Option<Rc<RefCell<ProofRecorder<B>>>>,
|
||||
) -> error::Result<NativeOrEncoded<R>> where ExecutionManager<EM>: Clone;
|
||||
|
||||
/// Extract RuntimeVersion of given block
|
||||
@@ -119,7 +125,10 @@ where
|
||||
call_data: &[u8]
|
||||
) -> Result<(Vec<u8>, Vec<Vec<u8>>), error::Error> {
|
||||
let trie_state = state.try_into_trie_backend()
|
||||
.ok_or_else(|| Box::new(state_machine::ExecutionError::UnableToGenerateProof) as Box<state_machine::Error>)?;
|
||||
.ok_or_else(||
|
||||
Box::new(state_machine::ExecutionError::UnableToGenerateProof)
|
||||
as Box<state_machine::Error>
|
||||
)?;
|
||||
self.prove_at_trie_state(&trie_state, overlay, method, call_data)
|
||||
}
|
||||
|
||||
@@ -172,7 +181,8 @@ where
|
||||
{
|
||||
type Error = E::Error;
|
||||
|
||||
fn call<O: OffchainExt>(&self,
|
||||
fn call<O: OffchainExt>(
|
||||
&self,
|
||||
id: &BlockId<Block>,
|
||||
method: &str,
|
||||
call_data: &[u8],
|
||||
@@ -200,8 +210,9 @@ where
|
||||
}
|
||||
|
||||
fn contextual_call<
|
||||
'a,
|
||||
O: OffchainExt,
|
||||
PB: Fn() -> error::Result<Block::Header>,
|
||||
IB: Fn() -> error::Result<()>,
|
||||
EM: Fn(
|
||||
Result<NativeOrEncoded<R>, Self::Error>,
|
||||
Result<NativeOrEncoded<R>, Self::Error>
|
||||
@@ -210,64 +221,75 @@ where
|
||||
NC: FnOnce() -> result::Result<R, &'static str> + UnwindSafe,
|
||||
>(
|
||||
&self,
|
||||
initialize_block_fn: IB,
|
||||
at: &BlockId<Block>,
|
||||
method: &str,
|
||||
call_data: &[u8],
|
||||
changes: &mut OverlayedChanges,
|
||||
initialized_block: &mut Option<BlockId<Block>>,
|
||||
prepare_environment_block: PB,
|
||||
changes: &RefCell<OverlayedChanges>,
|
||||
initialize_block: InitializeBlock<'a, Block>,
|
||||
execution_manager: ExecutionManager<EM>,
|
||||
native_call: Option<NC>,
|
||||
mut side_effects_handler: Option<&mut O>,
|
||||
side_effects_handler: Option<&mut O>,
|
||||
recorder: &Option<Rc<RefCell<ProofRecorder<Block>>>>,
|
||||
) -> Result<NativeOrEncoded<R>, error::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 state = self.backend.state_at(*at)?;
|
||||
|
||||
let core_version = self.runtime_version(at)?.apis.iter().find(|a| a.0 == CoreApi::<Block>::ID).map(|a| a.1);
|
||||
let init_block_function = if core_version < Some(2) {
|
||||
"Core_initialise_block"
|
||||
} else {
|
||||
"Core_initialize_block"
|
||||
};
|
||||
match recorder {
|
||||
Some(recorder) => {
|
||||
let trie_state = state.try_into_trie_backend()
|
||||
.ok_or_else(||
|
||||
Box::new(state_machine::ExecutionError::UnableToGenerateProof)
|
||||
as Box<state_machine::Error>
|
||||
)?;
|
||||
|
||||
if method != init_block_function && initialized_block.map(|id| id != *at).unwrap_or(true) {
|
||||
let header = prepare_environment_block()?;
|
||||
state_machine::new(
|
||||
let backend = state_machine::ProvingBackend::new_with_recorder(
|
||||
&trie_state,
|
||||
recorder.clone()
|
||||
);
|
||||
|
||||
state_machine::new(
|
||||
&backend,
|
||||
self.backend.changes_trie_storage(),
|
||||
side_effects_handler,
|
||||
&mut *changes.borrow_mut(),
|
||||
&self.executor,
|
||||
method,
|
||||
call_data,
|
||||
)
|
||||
.execute_using_consensus_failure_handler(
|
||||
execution_manager,
|
||||
false,
|
||||
native_call,
|
||||
)
|
||||
.map(|(result, _, _)| result)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
None => state_machine::new(
|
||||
&state,
|
||||
self.backend.changes_trie_storage(),
|
||||
side_effects_handler.as_mut().map(|x| &mut **x),
|
||||
changes,
|
||||
side_effects_handler,
|
||||
&mut *changes.borrow_mut(),
|
||||
&self.executor,
|
||||
init_block_function,
|
||||
&header.encode(),
|
||||
).execute_using_consensus_failure_handler::<_, R, fn() -> _>(
|
||||
execution_manager.clone(),
|
||||
method,
|
||||
call_data,
|
||||
)
|
||||
.execute_using_consensus_failure_handler(
|
||||
execution_manager,
|
||||
false,
|
||||
None,
|
||||
)?;
|
||||
*initialized_block = Some(*at);
|
||||
native_call,
|
||||
)
|
||||
.map(|(result, _, _)| result)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
let result = state_machine::new(
|
||||
&state,
|
||||
self.backend.changes_trie_storage(),
|
||||
side_effects_handler,
|
||||
changes,
|
||||
&self.executor,
|
||||
method,
|
||||
call_data,
|
||||
).execute_using_consensus_failure_handler(
|
||||
execution_manager,
|
||||
false,
|
||||
native_call,
|
||||
).map(|(result, _, _)| result)?;
|
||||
|
||||
// If the method is `initialize_block` we need to set the `initialized_block`
|
||||
if method == init_block_function {
|
||||
*initialized_block = Some(*at);
|
||||
}
|
||||
|
||||
self.backend.destroy_state(state)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn runtime_version(&self, id: &BlockId<Block>) -> error::Result<RuntimeVersion> {
|
||||
|
||||
@@ -16,7 +16,10 @@
|
||||
|
||||
//! Substrate Client
|
||||
|
||||
use std::{marker::PhantomData, collections::{HashSet, BTreeMap, HashMap}, sync::Arc, panic::UnwindSafe, result};
|
||||
use std::{
|
||||
marker::PhantomData, collections::{HashSet, BTreeMap, HashMap}, sync::Arc,
|
||||
panic::UnwindSafe, result, cell::RefCell, rc::Rc,
|
||||
};
|
||||
use crate::error::Error;
|
||||
use futures::sync::mpsc;
|
||||
use parking_lot::{Mutex, RwLock};
|
||||
@@ -26,16 +29,23 @@ use runtime_primitives::{
|
||||
generic::{BlockId, SignedBlock},
|
||||
};
|
||||
use consensus::{
|
||||
Error as ConsensusError, ErrorKind as ConsensusErrorKind, ImportBlock, ImportResult,
|
||||
BlockOrigin, ForkChoiceStrategy, well_known_cache_keys::Id as CacheKeyId,
|
||||
Error as ConsensusError, ErrorKind as ConsensusErrorKind, ImportBlock,
|
||||
ImportResult, BlockOrigin, ForkChoiceStrategy,
|
||||
well_known_cache_keys::Id as CacheKeyId,
|
||||
};
|
||||
use runtime_primitives::traits::{
|
||||
Block as BlockT, Header as HeaderT, Zero, As, NumberFor, CurrentHeight, BlockNumberToHash,
|
||||
ApiRef, ProvideRuntimeApi, Digest, DigestItem
|
||||
Block as BlockT, Header as HeaderT, Zero, As, NumberFor, CurrentHeight,
|
||||
BlockNumberToHash, ApiRef, ProvideRuntimeApi, Digest, DigestItem
|
||||
};
|
||||
use runtime_primitives::BuildStorage;
|
||||
use crate::runtime_api::{CallRuntimeAt, ConstructRuntimeApi};
|
||||
use primitives::{Blake2Hasher, H256, ChangesTrieConfiguration, convert_hash, NeverNativeValue, ExecutionContext};
|
||||
use crate::runtime_api::{
|
||||
CallRuntimeAt, ConstructRuntimeApi, Core as CoreApi, ProofRecorder,
|
||||
InitializeBlock,
|
||||
};
|
||||
use primitives::{
|
||||
Blake2Hasher, H256, ChangesTrieConfiguration, convert_hash,
|
||||
NeverNativeValue, ExecutionContext
|
||||
};
|
||||
use primitives::storage::{StorageKey, StorageData};
|
||||
use primitives::storage::well_known_keys;
|
||||
use parity_codec::{Encode, Decode};
|
||||
@@ -49,8 +59,8 @@ use hash_db::Hasher;
|
||||
|
||||
use crate::backend::{self, BlockImportOperation, PrunableStateChangesTrieStorage};
|
||||
use crate::blockchain::{
|
||||
self, Info as ChainInfo, Backend as ChainBackend, HeaderBackend as ChainHeaderBackend,
|
||||
ProvideCache, Cache,
|
||||
self, Info as ChainInfo, Backend as ChainBackend,
|
||||
HeaderBackend as ChainHeaderBackend, ProvideCache, Cache,
|
||||
};
|
||||
use crate::call_executor::{CallExecutor, LocalCallExecutor};
|
||||
use executor::{RuntimeVersion, RuntimeInfo};
|
||||
@@ -610,7 +620,23 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
|
||||
Self: ProvideRuntimeApi,
|
||||
<Self as ProvideRuntimeApi>::Api: BlockBuilderAPI<Block>
|
||||
{
|
||||
block_builder::BlockBuilder::at_block(parent, &self)
|
||||
block_builder::BlockBuilder::at_block(parent, &self, false)
|
||||
}
|
||||
|
||||
/// Create a new block, built on top of `parent` with proof recording enabled.
|
||||
///
|
||||
/// While proof recording is enabled, all accessed trie nodes are saved.
|
||||
/// These recorded trie nodes can be used by a third party to proof the
|
||||
/// output of this block builder without having access to the full storage.
|
||||
pub fn new_block_at_with_proof_recording(
|
||||
&self, parent: &BlockId<Block>
|
||||
) -> error::Result<block_builder::BlockBuilder<Block, Self>> where
|
||||
E: Clone + Send + Sync,
|
||||
RA: Send + Sync,
|
||||
Self: ProvideRuntimeApi,
|
||||
<Self as ProvideRuntimeApi>::Api: BlockBuilderAPI<Block>
|
||||
{
|
||||
block_builder::BlockBuilder::at_block(parent, &self, true)
|
||||
}
|
||||
|
||||
/// Lock the import lock, and run operations inside.
|
||||
@@ -1340,27 +1366,36 @@ impl<B, E, Block, RA> ProvideRuntimeApi for Client<B, E, Block, RA> where
|
||||
impl<B, E, Block, RA> CallRuntimeAt<Block> for Client<B, E, Block, RA> where
|
||||
B: backend::Backend<Block, Blake2Hasher>,
|
||||
E: CallExecutor<Block, Blake2Hasher> + Clone + Send + Sync,
|
||||
Block: BlockT<Hash=H256>
|
||||
Block: BlockT<Hash=H256>,
|
||||
{
|
||||
fn call_api_at<
|
||||
'a,
|
||||
R: Encode + Decode + PartialEq,
|
||||
NC: FnOnce() -> result::Result<R, &'static str> + UnwindSafe,
|
||||
C: CoreApi<Block>,
|
||||
>(
|
||||
&self,
|
||||
core_api: &C,
|
||||
at: &BlockId<Block>,
|
||||
function: &'static str,
|
||||
args: Vec<u8>,
|
||||
changes: &mut OverlayedChanges,
|
||||
initialized_block: &mut Option<BlockId<Block>>,
|
||||
changes: &RefCell<OverlayedChanges>,
|
||||
initialize_block: InitializeBlock<'a, Block>,
|
||||
native_call: Option<NC>,
|
||||
context: ExecutionContext,
|
||||
recorder: &Option<Rc<RefCell<ProofRecorder<Block>>>>,
|
||||
) -> error::Result<NativeOrEncoded<R>> {
|
||||
let manager = match context {
|
||||
ExecutionContext::BlockConstruction => self.execution_strategies.block_construction.get_manager(),
|
||||
ExecutionContext::Syncing => self.execution_strategies.syncing.get_manager(),
|
||||
ExecutionContext::Importing => self.execution_strategies.importing.get_manager(),
|
||||
ExecutionContext::OffchainWorker(_) => self.execution_strategies.offchain_worker.get_manager(),
|
||||
ExecutionContext::Other => self.execution_strategies.other.get_manager(),
|
||||
ExecutionContext::BlockConstruction =>
|
||||
self.execution_strategies.block_construction.get_manager(),
|
||||
ExecutionContext::Syncing =>
|
||||
self.execution_strategies.syncing.get_manager(),
|
||||
ExecutionContext::Importing =>
|
||||
self.execution_strategies.importing.get_manager(),
|
||||
ExecutionContext::OffchainWorker(_) =>
|
||||
self.execution_strategies.offchain_worker.get_manager(),
|
||||
ExecutionContext::Other =>
|
||||
self.execution_strategies.other.get_manager(),
|
||||
};
|
||||
|
||||
let mut offchain_extensions = match context {
|
||||
@@ -1369,15 +1404,16 @@ impl<B, E, Block, RA> CallRuntimeAt<Block> for Client<B, E, Block, RA> where
|
||||
};
|
||||
|
||||
self.executor.contextual_call::<_, _, fn(_,_) -> _,_,_>(
|
||||
|| core_api.initialize_block(at, &self.prepare_environment_block(at)?),
|
||||
at,
|
||||
function,
|
||||
&args,
|
||||
changes,
|
||||
initialized_block,
|
||||
|| self.prepare_environment_block(at),
|
||||
initialize_block,
|
||||
manager,
|
||||
native_call,
|
||||
offchain_extensions.as_mut(),
|
||||
recorder,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -17,17 +17,24 @@
|
||||
//! Light client call executor. Executes methods on remote full nodes, fetching
|
||||
//! execution proof and checking it locally.
|
||||
|
||||
use std::{collections::HashSet, sync::Arc, panic::UnwindSafe, result, marker::PhantomData};
|
||||
use std::{
|
||||
collections::HashSet, sync::Arc, panic::UnwindSafe, result,
|
||||
marker::PhantomData, cell::RefCell, rc::Rc,
|
||||
};
|
||||
use futures::{IntoFuture, Future};
|
||||
|
||||
use parity_codec::{Encode, Decode};
|
||||
use primitives::{H256, Blake2Hasher, convert_hash, NativeOrEncoded, OffchainExt};
|
||||
use runtime_primitives::generic::BlockId;
|
||||
use runtime_primitives::traits::{As, Block as BlockT, Header as HeaderT};
|
||||
use state_machine::{self, Backend as StateBackend, CodeExecutor, OverlayedChanges, ExecutionStrategy,
|
||||
create_proof_check_backend, execution_proof_check_on_trie_backend, ExecutionManager, NeverOffchainExt};
|
||||
use state_machine::{
|
||||
self, Backend as StateBackend, CodeExecutor, OverlayedChanges,
|
||||
ExecutionStrategy, create_proof_check_backend,
|
||||
execution_proof_check_on_trie_backend, ExecutionManager, NeverOffchainExt
|
||||
};
|
||||
use hash_db::Hasher;
|
||||
|
||||
use crate::runtime_api::{ProofRecorder, InitializeBlock};
|
||||
use crate::backend::RemoteBackend;
|
||||
use crate::blockchain::Backend as ChainBackend;
|
||||
use crate::call_executor::CallExecutor;
|
||||
@@ -104,8 +111,9 @@ where
|
||||
}
|
||||
|
||||
fn contextual_call<
|
||||
'a,
|
||||
O: OffchainExt,
|
||||
PB: Fn() -> ClientResult<Block::Header>,
|
||||
IB: Fn() -> ClientResult<()>,
|
||||
EM: Fn(
|
||||
Result<NativeOrEncoded<R>, Self::Error>,
|
||||
Result<NativeOrEncoded<R>, Self::Error>
|
||||
@@ -114,18 +122,26 @@ where
|
||||
NC,
|
||||
>(
|
||||
&self,
|
||||
_initialize_block_fn: IB,
|
||||
at: &BlockId<Block>,
|
||||
method: &str,
|
||||
call_data: &[u8],
|
||||
changes: &mut OverlayedChanges,
|
||||
initialized_block: &mut Option<BlockId<Block>>,
|
||||
_prepare_environment_block: PB,
|
||||
changes: &RefCell<OverlayedChanges>,
|
||||
initialize_block: InitializeBlock<'a, Block>,
|
||||
execution_manager: ExecutionManager<EM>,
|
||||
_native_call: Option<NC>,
|
||||
side_effects_handler: Option<&mut O>,
|
||||
_recorder: &Option<Rc<RefCell<ProofRecorder<Block>>>>,
|
||||
) -> ClientResult<NativeOrEncoded<R>> where ExecutionManager<EM>: Clone {
|
||||
let block_initialized = match initialize_block {
|
||||
InitializeBlock::Do(ref init_block) => {
|
||||
init_block.borrow().is_some()
|
||||
},
|
||||
InitializeBlock::Skip => false,
|
||||
};
|
||||
|
||||
// it is only possible to execute contextual call if changes are empty
|
||||
if !changes.is_empty() || initialized_block.is_some() {
|
||||
if !changes.borrow().is_empty() || block_initialized {
|
||||
return Err(ClientError::NotAvailableOnLightClient.into());
|
||||
}
|
||||
|
||||
@@ -231,8 +247,9 @@ impl<Block, B, Remote, Local> CallExecutor<Block, Blake2Hasher> for
|
||||
}
|
||||
|
||||
fn contextual_call<
|
||||
'a,
|
||||
O: OffchainExt,
|
||||
PB: Fn() -> ClientResult<Block::Header>,
|
||||
IB: Fn() -> ClientResult<()>,
|
||||
EM: Fn(
|
||||
Result<NativeOrEncoded<R>, Self::Error>,
|
||||
Result<NativeOrEncoded<R>, Self::Error>
|
||||
@@ -241,15 +258,16 @@ impl<Block, B, Remote, Local> CallExecutor<Block, Blake2Hasher> for
|
||||
NC: FnOnce() -> result::Result<R, &'static str> + UnwindSafe,
|
||||
>(
|
||||
&self,
|
||||
initialize_block_fn: IB,
|
||||
at: &BlockId<Block>,
|
||||
method: &str,
|
||||
call_data: &[u8],
|
||||
changes: &mut OverlayedChanges,
|
||||
initialized_block: &mut Option<BlockId<Block>>,
|
||||
prepare_environment_block: PB,
|
||||
changes: &RefCell<OverlayedChanges>,
|
||||
initialize_block: InitializeBlock<'a, Block>,
|
||||
_manager: ExecutionManager<EM>,
|
||||
native_call: Option<NC>,
|
||||
side_effects_handler: Option<&mut O>,
|
||||
recorder: &Option<Rc<RefCell<ProofRecorder<Block>>>>,
|
||||
) -> ClientResult<NativeOrEncoded<R>> where ExecutionManager<EM>: Clone {
|
||||
// there's no actual way/need to specify native/wasm execution strategy on light node
|
||||
// => we can safely ignore passed values
|
||||
@@ -266,15 +284,16 @@ impl<Block, B, Remote, Local> CallExecutor<Block, Blake2Hasher> for
|
||||
NC
|
||||
>(
|
||||
&self.local,
|
||||
initialize_block_fn,
|
||||
at,
|
||||
method,
|
||||
call_data,
|
||||
changes,
|
||||
initialized_block,
|
||||
prepare_environment_block,
|
||||
initialize_block,
|
||||
ExecutionManager::NativeWhenPossible,
|
||||
native_call,
|
||||
side_effects_handler,
|
||||
recorder,
|
||||
).map_err(|e| ClientError::Execution(Box::new(e.to_string()))),
|
||||
false => CallExecutor::contextual_call::<
|
||||
_,
|
||||
@@ -287,15 +306,16 @@ impl<Block, B, Remote, Local> CallExecutor<Block, Blake2Hasher> for
|
||||
NC
|
||||
>(
|
||||
&self.remote,
|
||||
initialize_block_fn,
|
||||
at,
|
||||
method,
|
||||
call_data,
|
||||
changes,
|
||||
initialized_block,
|
||||
prepare_environment_block,
|
||||
initialize_block,
|
||||
ExecutionManager::NativeWhenPossible,
|
||||
native_call,
|
||||
side_effects_handler,
|
||||
recorder,
|
||||
).map_err(|e| ClientError::Execution(Box::new(e.to_string()))),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,10 @@ pub use state_machine::OverlayedChanges;
|
||||
pub use primitives::NativeOrEncoded;
|
||||
#[doc(hidden)]
|
||||
pub use runtime_primitives::{
|
||||
traits::{AuthorityIdFor, Block as BlockT, GetNodeBlockType, GetRuntimeBlockType, Header as HeaderT, ApiRef, RuntimeApiInfo},
|
||||
traits::{
|
||||
AuthorityIdFor, Block as BlockT, GetNodeBlockType, GetRuntimeBlockType,
|
||||
Header as HeaderT, ApiRef, RuntimeApiInfo, Hash as HashT,
|
||||
},
|
||||
generic::BlockId, transaction_validity::TransactionValidity,
|
||||
};
|
||||
#[doc(hidden)]
|
||||
@@ -42,8 +45,16 @@ use crate::error;
|
||||
use sr_api_macros::decl_runtime_apis;
|
||||
use primitives::OpaqueMetadata;
|
||||
#[cfg(feature = "std")]
|
||||
use std::panic::UnwindSafe;
|
||||
use std::{panic::UnwindSafe, cell::RefCell, rc::Rc};
|
||||
use rstd::vec::Vec;
|
||||
#[cfg(feature = "std")]
|
||||
use primitives::Hasher as HasherT;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
/// A type that records all accessed trie nodes and generates a proof out of it.
|
||||
pub type ProofRecorder<B> = state_machine::ProofRecorder<
|
||||
<<<<B as BlockT>::Header as HeaderT>::Hashing as HashT>::Hasher as HasherT>::Out
|
||||
>;
|
||||
|
||||
/// Something that can be constructed to a runtime api.
|
||||
#[cfg(feature = "std")]
|
||||
@@ -87,6 +98,35 @@ pub trait ApiExt<Block: BlockT> {
|
||||
|
||||
/// Returns the runtime version at the given block id.
|
||||
fn runtime_version_at(&self, at: &BlockId<Block>) -> error::Result<RuntimeVersion>;
|
||||
|
||||
/// Start recording all accessed trie nodes for generating proofs.
|
||||
fn record_proof(&mut self);
|
||||
|
||||
/// Extract the recorded proof.
|
||||
/// This stops the proof recording.
|
||||
fn extract_proof(&mut self) -> Option<Vec<Vec<u8>>>;
|
||||
}
|
||||
|
||||
/// Before calling any runtime api function, the runtime need to be initialized
|
||||
/// at the requested block. However, some functions like `execute_block` or
|
||||
/// `initialize_block` itself don't require to have the runtime initialized
|
||||
/// at the requested block.
|
||||
///
|
||||
/// `call_api_at` is instructed by this enum to do the initialization or to skip
|
||||
/// it.
|
||||
#[cfg(feature = "std")]
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum InitializeBlock<'a, Block: BlockT> {
|
||||
/// Skip initializing the runtime for a given block.
|
||||
///
|
||||
/// This is used by functions who do the initialization by themself or don't
|
||||
/// require it.
|
||||
Skip,
|
||||
/// Initialize the runtime for a given block.
|
||||
///
|
||||
/// If the stored `BlockId` is `Some(_)`, the runtime is currently initialized
|
||||
/// at this block.
|
||||
Do(&'a RefCell<Option<BlockId<Block>>>),
|
||||
}
|
||||
|
||||
/// Something that can call into the runtime at a given block.
|
||||
@@ -95,17 +135,21 @@ pub trait CallRuntimeAt<Block: BlockT> {
|
||||
/// Calls the given api function with the given encoded arguments at the given block
|
||||
/// and returns the encoded result.
|
||||
fn call_api_at<
|
||||
'a,
|
||||
R: Encode + Decode + PartialEq,
|
||||
NC: FnOnce() -> result::Result<R, &'static str> + UnwindSafe,
|
||||
C: Core<Block>,
|
||||
>(
|
||||
&self,
|
||||
core_api: &C,
|
||||
at: &BlockId<Block>,
|
||||
function: &'static str,
|
||||
args: Vec<u8>,
|
||||
changes: &mut OverlayedChanges,
|
||||
initialized_block: &mut Option<BlockId<Block>>,
|
||||
changes: &RefCell<OverlayedChanges>,
|
||||
initialize_block: InitializeBlock<'a, Block>,
|
||||
native_call: Option<NC>,
|
||||
context: ExecutionContext,
|
||||
recorder: &Option<Rc<RefCell<ProofRecorder<Block>>>>,
|
||||
) -> error::Result<NativeOrEncoded<R>>;
|
||||
|
||||
/// Returns the runtime version at the given block.
|
||||
@@ -120,9 +164,12 @@ decl_runtime_apis! {
|
||||
/// Returns the version of the runtime.
|
||||
fn version() -> RuntimeVersion;
|
||||
/// Execute the given block.
|
||||
#[skip_initialize_block]
|
||||
fn execute_block(block: Block);
|
||||
/// Initialize a block with the given header.
|
||||
#[renamed("initialise_block", 2)]
|
||||
#[skip_initialize_block]
|
||||
#[initialize_block]
|
||||
fn initialize_block(header: &<Block as BlockT>::Header);
|
||||
/// Returns the authorities.
|
||||
#[deprecated(since = "1.0", note = "Please switch to `AuthoritiesApi`.")]
|
||||
|
||||
Reference in New Issue
Block a user