Adds new execution strategy nativeElseWasm (#1546)

* fix: adds new execution strategy nativeElseWasm and replace nativeWhenPossible with it

* feat: adds cmd line params for execution strategies

* fix: uses of cmd line execution strategies

* chore: remove white spaces

* chore: remove println

* chore: remove whitespace

* fix: generating functions with context

* feat: add function to generate with_context declarations

* fix: add implementation for with_context function calls

* fix: add execution context to call_api_at function

* fix: making use of context to select strategy for block_builder

* chore: cleaning up

* fix: merging issues

* fix tests

* add wasm files

* chore: small doc for context fields

* chore: delete redundant docs

* fix: use full path for ExecutionContext

* fix: add context functions from inside fold_item_impl

* chore: remove clone

* fix: moving generative function to utils, remove unused imports

* fix: add missing full path for ExecutionContext

* fix: merge issues

* update wasm files

* fix: update to keep up with changes in master

* chore: remove unused functions, clean up

* fix test

* fix grumbles

* fix: add more tests

* fix: some refactorings

* feat: add execution strategy to call

* chore: small improvements

* fix: add message to panic

* fix tests
This commit is contained in:
Marcio Diaz
2019-02-11 15:22:44 +01:00
committed by GitHub
parent f9975af020
commit b8bd49961a
31 changed files with 837 additions and 544 deletions
+321 -312
View File
File diff suppressed because it is too large Load Diff
@@ -32,6 +32,7 @@ use primitives::{H256, Blake2Hasher};
use runtime_primitives::traits::{ use runtime_primitives::traits::{
Block as BlockT, Hash as HashT, Header as HeaderT, ProvideRuntimeApi, AuthorityIdFor Block as BlockT, Hash as HashT, Header as HeaderT, ProvideRuntimeApi, AuthorityIdFor
}; };
use runtime_primitives::ExecutionContext;
use runtime_primitives::generic::BlockId; use runtime_primitives::generic::BlockId;
use runtime_primitives::ApplyError; use runtime_primitives::ApplyError;
use transaction_pool::txpool::{self, Pool as TransactionPool}; use transaction_pool::txpool::{self, Pool as TransactionPool};
@@ -98,7 +99,7 @@ impl<B, E, Block, RA> AuthoringApi for SubstrateClient<B, E, Block, RA> where
let runtime_api = self.runtime_api(); let runtime_api = self.runtime_api();
// We don't check the API versions any further here since the dispatch compatibility // We don't check the API versions any further here since the dispatch compatibility
// check should be enough. // check should be enough.
runtime_api.inherent_extrinsics(at, inherent_data)? runtime_api.inherent_extrinsics_with_context(at, ExecutionContext::BlockConstruction, inherent_data)?
.into_iter().try_for_each(|i| block_builder.push(i))?; .into_iter().try_for_each(|i| block_builder.push(i))?;
build_ctx(&mut block_builder); build_ctx(&mut block_builder);
@@ -173,7 +174,7 @@ impl<Block, C, A> consensus_common::Proposer<<C as AuthoringApi>::Block> for Pro
-> Result<<C as AuthoringApi>::Block, error::Error> -> Result<<C as AuthoringApi>::Block, error::Error>
{ {
// leave some time for evaluation and block finalisation (33%) // leave some time for evaluation and block finalisation (33%)
let deadline = (self.now)() + max_duration - max_duration / 3; let deadline = (self.now)() + max_duration - max_duration / 3;
self.propose_with(inherent_data, deadline) self.propose_with(inherent_data, deadline)
} }
} }
+1
View File
@@ -27,6 +27,7 @@ network = { package = "substrate-network", path = "../../core/network" }
runtime_primitives = { package = "sr-primitives", path = "../../core/sr-primitives" } runtime_primitives = { package = "sr-primitives", path = "../../core/sr-primitives" }
primitives = { package = "substrate-primitives", path = "../../core/primitives" } primitives = { package = "substrate-primitives", path = "../../core/primitives" }
service = { package = "substrate-service", path = "../../core/service" } service = { package = "substrate-service", path = "../../core/service" }
state-machine = { package = "substrate-state-machine", path = "../../core/state-machine" }
substrate-telemetry = { path = "../../core/telemetry" } substrate-telemetry = { path = "../../core/telemetry" }
names = "0.11.0" names = "0.11.0"
structopt = "0.2" structopt = "0.2"
+8 -8
View File
@@ -25,6 +25,7 @@ mod params;
pub mod error; pub mod error;
pub mod informant; pub mod informant;
use client::ExecutionStrategies;
use runtime_primitives::traits::As; use runtime_primitives::traits::As;
use service::{ use service::{
ServiceFactory, FactoryFullConfiguration, RuntimeGenesis, ServiceFactory, FactoryFullConfiguration, RuntimeGenesis,
@@ -351,17 +352,19 @@ where
let role = let role =
if cli.light { if cli.light {
config.block_execution_strategy = service::ExecutionStrategy::NativeWhenPossible;
service::Roles::LIGHT service::Roles::LIGHT
} else if cli.validator || cli.shared_params.dev { } else if cli.validator || cli.shared_params.dev {
config.block_execution_strategy = service::ExecutionStrategy::Both;
service::Roles::AUTHORITY service::Roles::AUTHORITY
} else { } else {
config.block_execution_strategy = service::ExecutionStrategy::NativeWhenPossible;
service::Roles::FULL service::Roles::FULL
}; };
config.block_execution_strategy = cli.execution.into(); config.execution_strategies = ExecutionStrategies {
syncing: cli.syncing_execution.into(),
importing: cli.importing_execution.into(),
block_construction: cli.block_construction_execution.into(),
other: cli.other_execution.into(),
};
config.roles = role; config.roles = role;
let client_id = config.client_id(); let client_id = config.client_id();
@@ -536,10 +539,7 @@ where
E: IntoExit, E: IntoExit,
S: FnOnce(&str) -> Result<Option<ChainSpec<FactoryGenesis<F>>>, String>, S: FnOnce(&str) -> Result<Option<ChainSpec<FactoryGenesis<F>>>, String>,
{ {
let mut config = create_config_with_db_path::<F, _>(spec_factory, &cli.shared_params, version)?; let config = create_config_with_db_path::<F, _>(spec_factory, &cli.shared_params, version)?;
config.block_execution_strategy = cli.execution.into();
config.api_execution_strategy = cli.api_execution.into();
let file: Box<Read> = match cli.input { let file: Box<Read> = match cli.input {
Some(filename) => Box::new(File::open(filename)?), Some(filename) => Box::new(File::open(filename)?),
+44 -29
View File
@@ -38,6 +38,7 @@ arg_enum! {
Native, Native,
Wasm, Wasm,
Both, Both,
NativeElseWasm,
} }
} }
@@ -47,6 +48,7 @@ impl Into<client::ExecutionStrategy> for ExecutionStrategy {
ExecutionStrategy::Native => client::ExecutionStrategy::NativeWhenPossible, ExecutionStrategy::Native => client::ExecutionStrategy::NativeWhenPossible,
ExecutionStrategy::Wasm => client::ExecutionStrategy::AlwaysWasm, ExecutionStrategy::Wasm => client::ExecutionStrategy::AlwaysWasm,
ExecutionStrategy::Both => client::ExecutionStrategy::Both, ExecutionStrategy::Both => client::ExecutionStrategy::Both,
ExecutionStrategy::NativeElseWasm => client::ExecutionStrategy::NativeElseWasm,
} }
} }
} }
@@ -175,18 +177,55 @@ pub struct RunCmd {
#[structopt(long = "telemetry-url", value_name = "TELEMETRY_URL")] #[structopt(long = "telemetry-url", value_name = "TELEMETRY_URL")]
pub telemetry_url: Option<String>, pub telemetry_url: Option<String>,
/// The means of execution used when calling into the runtime. Can be either wasm, native or both. /// The means of execution used when calling into the runtime while syncing blocks.
#[structopt( #[structopt(
long = "execution", long = "syncing-execution",
value_name = "STRATEGY", value_name = "STRATEGY",
raw( raw(
possible_values = "&ExecutionStrategy::variants()", possible_values = "&ExecutionStrategy::variants()",
case_insensitive = "true", case_insensitive = "true",
default_value = r#""Both""# default_value = r#""NativeElseWasm""#
) )
)] )]
pub execution: ExecutionStrategy, pub syncing_execution: ExecutionStrategy,
/// The means of execution used when calling into the runtime while importing blocks.
#[structopt(
long = "importing-execution",
value_name = "STRATEGY",
raw(
possible_values = "&ExecutionStrategy::variants()",
case_insensitive = "true",
default_value = r#""NativeElseWasm""#
)
)]
pub importing_execution: ExecutionStrategy,
/// The means of execution used when calling into the runtime while constructing blocks.
#[structopt(
long = "block-construction-execution",
value_name = "STRATEGY",
raw(
possible_values = "&ExecutionStrategy::variants()",
case_insensitive = "true",
default_value = r#""Wasm""#
)
)]
pub block_construction_execution: ExecutionStrategy,
/// The means of execution used when calling into the runtime while not syncing, importing or constructing blocks.
#[structopt(
long = "other-execution",
value_name = "STRATEGY",
raw(
possible_values = "&ExecutionStrategy::variants()",
case_insensitive = "true",
default_value = r#""Wasm""#
)
)]
pub other_execution: ExecutionStrategy,
#[allow(missing_docs)] #[allow(missing_docs)]
#[structopt(flatten)] #[structopt(flatten)]
pub shared_params: SharedParams, pub shared_params: SharedParams,
@@ -254,31 +293,7 @@ pub struct ImportBlocksCmd {
#[structopt(parse(from_os_str))] #[structopt(parse(from_os_str))]
pub input: Option<PathBuf>, pub input: Option<PathBuf>,
/// The means of execution used when executing blocks. Can be either wasm, native or both. /// The default number of 64KB pages to ever allocate for Wasm execution. Don't alter this unless you know what you're doing.
#[structopt(
long = "execution",
value_name = "STRATEGY",
raw(
possible_values = "&ExecutionStrategy::variants()",
case_insensitive = "true",
default_value = r#""Both""#
)
)]
pub execution: ExecutionStrategy,
/// The means of execution used when calling into the runtime. Can be either wasm, native or both.
#[structopt(
long = "api-execution",
value_name = "STRATEGY",
raw(
possible_values = "&ExecutionStrategy::variants()",
case_insensitive = "true",
default_value = r#""Both""#
)
)]
pub api_execution: ExecutionStrategy,
/// The default number of 64KB pages to allocate for Wasm execution. Don't alter this unless you know what you're doing.
#[structopt(long = "default-heap-pages", value_name = "COUNT")] #[structopt(long = "default-heap-pages", value_name = "COUNT")]
pub default_heap_pages: Option<u32>, pub default_heap_pages: Option<u32>,
+4 -4
View File
@@ -36,6 +36,7 @@ use std::io;
use client::backend::NewBlockState; use client::backend::NewBlockState;
use client::blockchain::HeaderBackend; use client::blockchain::HeaderBackend;
use client::ExecutionStrategies;
use parity_codec::{Decode, Encode}; use parity_codec::{Decode, Encode};
use hash_db::Hasher; use hash_db::Hasher;
use kvdb::{KeyValueDB, DBTransaction}; use kvdb::{KeyValueDB, DBTransaction};
@@ -48,7 +49,7 @@ use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, As, NumberF
use runtime_primitives::BuildStorage; use runtime_primitives::BuildStorage;
use state_machine::backend::Backend as StateBackend; use state_machine::backend::Backend as StateBackend;
use executor::RuntimeInfo; use executor::RuntimeInfo;
use state_machine::{CodeExecutor, DBValue, ExecutionStrategy}; use state_machine::{CodeExecutor, DBValue};
use crate::utils::{Meta, db_err, meta_keys, open_database, read_db, block_id_to_lookup_key, read_meta}; use crate::utils::{Meta, db_err, meta_keys, open_database, read_db, block_id_to_lookup_key, read_meta};
use client::LeafSet; use client::LeafSet;
use state_db::StateDb; use state_db::StateDb;
@@ -78,8 +79,7 @@ pub fn new_client<E, S, Block, RA>(
settings: DatabaseSettings, settings: DatabaseSettings,
executor: E, executor: E,
genesis_storage: S, genesis_storage: S,
block_execution_strategy: ExecutionStrategy, execution_strategies: ExecutionStrategies,
api_execution_strategy: ExecutionStrategy,
) -> Result<client::Client<Backend<Block>, client::LocalCallExecutor<Backend<Block>, E>, Block, RA>, client::error::Error> ) -> Result<client::Client<Backend<Block>, client::LocalCallExecutor<Backend<Block>, E>, Block, RA>, client::error::Error>
where where
Block: BlockT<Hash=H256>, Block: BlockT<Hash=H256>,
@@ -88,7 +88,7 @@ pub fn new_client<E, S, Block, RA>(
{ {
let backend = Arc::new(Backend::new(settings, CANONICALIZATION_DELAY)?); let backend = Arc::new(Backend::new(settings, CANONICALIZATION_DELAY)?);
let executor = client::LocalCallExecutor::new(backend.clone(), executor); let executor = client::LocalCallExecutor::new(backend.clone(), executor);
Ok(client::Client::new(backend, executor, genesis_storage, block_execution_strategy, api_execution_strategy)?) Ok(client::Client::new(backend, executor, genesis_storage, execution_strategies)?)
} }
mod columns { mod columns {
@@ -25,7 +25,7 @@ use primitives::H256;
use runtime_primitives::generic::BlockId; use runtime_primitives::generic::BlockId;
use crate::runtime_api::Core; use crate::runtime_api::Core;
use crate::error; use crate::error;
use runtime_primitives::ApplyOutcome; use runtime_primitives::{ApplyOutcome, ExecutionContext};
/// Utility for building new (valid) blocks from a stream of extrinsics. /// Utility for building new (valid) blocks from a stream of extrinsics.
@@ -56,7 +56,6 @@ where
let parent_hash = api.block_hash_from_id(block_id)? let parent_hash = api.block_hash_from_id(block_id)?
.ok_or_else(|| error::ErrorKind::UnknownBlock(format!("{}", block_id)))?; .ok_or_else(|| error::ErrorKind::UnknownBlock(format!("{}", block_id)))?;
let header = <<Block as BlockT>::Header as HeaderT>::new( let header = <<Block as BlockT>::Header as HeaderT>::new(
number, number,
Default::default(), Default::default(),
@@ -64,10 +63,8 @@ where
parent_hash, parent_hash,
Default::default() Default::default()
); );
let api = api.runtime_api(); let api = api.runtime_api();
api.initialise_block(block_id, &header)?; api.initialise_block_with_context(block_id, ExecutionContext::BlockConstruction, &header)?;
Ok(BlockBuilder { Ok(BlockBuilder {
header, header,
extrinsics: Vec::new(), extrinsics: Vec::new(),
@@ -86,7 +83,7 @@ where
let extrinsics = &mut self.extrinsics; let extrinsics = &mut self.extrinsics;
self.api.map_api_result(|api| { self.api.map_api_result(|api| {
match api.apply_extrinsic(block_id, xt.clone())? { match api.apply_extrinsic_with_context(block_id, ExecutionContext::BlockConstruction, xt.clone())? {
Ok(ApplyOutcome::Success) | Ok(ApplyOutcome::Fail) => { Ok(ApplyOutcome::Success) | Ok(ApplyOutcome::Fail) => {
extrinsics.push(xt); extrinsics.push(xt);
Ok(()) Ok(())
@@ -100,7 +97,7 @@ where
/// Consume the builder to return a valid `Block` containing all pushed extrinsics. /// Consume the builder to return a valid `Block` containing all pushed extrinsics.
pub fn bake(mut self) -> error::Result<Block> { pub fn bake(mut self) -> error::Result<Block> {
self.header = self.api.finalise_block(&self.block_id)?; self.header = self.api.finalise_block_with_context(&self.block_id, ExecutionContext::BlockConstruction)?;
debug_assert_eq!( debug_assert_eq!(
self.header.extrinsics_root().clone(), self.header.extrinsics_root().clone(),
+8 -6
View File
@@ -19,7 +19,7 @@ use codec::{Encode, Decode};
use runtime_primitives::generic::BlockId; use runtime_primitives::generic::BlockId;
use runtime_primitives::traits::Block as BlockT; use runtime_primitives::traits::Block as BlockT;
use state_machine::{ use state_machine::{
self, OverlayedChanges, Ext, CodeExecutor, ExecutionManager, native_when_possible self, OverlayedChanges, Ext, CodeExecutor, ExecutionManager, ExecutionStrategy
}; };
use executor::{RuntimeVersion, RuntimeInfo, NativeVersion}; use executor::{RuntimeVersion, RuntimeInfo, NativeVersion};
use hash_db::Hasher; use hash_db::Hasher;
@@ -47,6 +47,7 @@ where
id: &BlockId<B>, id: &BlockId<B>,
method: &str, method: &str,
call_data: &[u8], call_data: &[u8],
strategy: ExecutionStrategy,
) -> Result<Vec<u8>, error::Error>; ) -> Result<Vec<u8>, error::Error>;
/// Execute a contextual call on top of state in a block of a given hash. /// Execute a contextual call on top of state in a block of a given hash.
@@ -70,7 +71,7 @@ where
changes: &mut OverlayedChanges, changes: &mut OverlayedChanges,
initialised_block: &mut Option<BlockId<B>>, initialised_block: &mut Option<BlockId<B>>,
prepare_environment_block: PB, prepare_environment_block: PB,
manager: ExecutionManager<EM>, execution_manager: ExecutionManager<EM>,
native_call: Option<NC>, native_call: Option<NC>,
) -> error::Result<NativeOrEncoded<R>> where ExecutionManager<EM>: Clone; ) -> error::Result<NativeOrEncoded<R>> where ExecutionManager<EM>: Clone;
@@ -164,6 +165,7 @@ where
id: &BlockId<Block>, id: &BlockId<Block>,
method: &str, method: &str,
call_data: &[u8], call_data: &[u8],
strategy: ExecutionStrategy
) -> error::Result<Vec<u8>> { ) -> error::Result<Vec<u8>> {
let mut changes = OverlayedChanges::default(); let mut changes = OverlayedChanges::default();
let state = self.backend.state_at(*id)?; let state = self.backend.state_at(*id)?;
@@ -176,7 +178,7 @@ where
&self.executor, &self.executor,
method, method,
call_data, call_data,
native_when_possible(), strategy.get_manager(),
false, false,
None, None,
) )
@@ -201,7 +203,7 @@ where
changes: &mut OverlayedChanges, changes: &mut OverlayedChanges,
initialised_block: &mut Option<BlockId<Block>>, initialised_block: &mut Option<BlockId<Block>>,
prepare_environment_block: PB, prepare_environment_block: PB,
manager: ExecutionManager<EM>, execution_manager: ExecutionManager<EM>,
native_call: Option<NC>, native_call: Option<NC>,
) -> Result<NativeOrEncoded<R>, error::Error> where ExecutionManager<EM>: Clone { ) -> Result<NativeOrEncoded<R>, error::Error> where ExecutionManager<EM>: Clone {
let state = self.backend.state_at(*at)?; let state = self.backend.state_at(*at)?;
@@ -216,7 +218,7 @@ where
&self.executor, &self.executor,
"Core_initialise_block", "Core_initialise_block",
&header.encode(), &header.encode(),
manager.clone(), execution_manager.clone(),
false, false,
None, None,
)?; )?;
@@ -230,7 +232,7 @@ where
&self.executor, &self.executor,
method, method,
call_data, call_data,
manager, execution_manager,
false, false,
native_call, native_call,
).map(|(result, _, _)| result)?; ).map(|(result, _, _)| result)?;
+59 -32
View File
@@ -33,7 +33,7 @@ use runtime_primitives::traits::{
Block as BlockT, Header as HeaderT, Zero, As, NumberFor, CurrentHeight, BlockNumberToHash, Block as BlockT, Header as HeaderT, Zero, As, NumberFor, CurrentHeight, BlockNumberToHash,
ApiRef, ProvideRuntimeApi, Digest, DigestItem, AuthorityIdFor ApiRef, ProvideRuntimeApi, Digest, DigestItem, AuthorityIdFor
}; };
use runtime_primitives::BuildStorage; use runtime_primitives::{BuildStorage, ExecutionContext};
use crate::runtime_api::{CallRuntimeAt, ConstructRuntimeApi}; use crate::runtime_api::{CallRuntimeAt, ConstructRuntimeApi};
use primitives::{Blake2Hasher, H256, ChangesTrieConfiguration, convert_hash, NeverNativeValue}; use primitives::{Blake2Hasher, H256, ChangesTrieConfiguration, convert_hash, NeverNativeValue};
use primitives::storage::{StorageKey, StorageData}; use primitives::storage::{StorageKey, StorageData};
@@ -75,6 +75,30 @@ pub type FinalityNotifications<Block> = mpsc::UnboundedReceiver<FinalityNotifica
type StorageUpdate<B, Block> = <<<B as backend::Backend<Block, Blake2Hasher>>::BlockImportOperation as BlockImportOperation<Block, Blake2Hasher>>::State as state_machine::Backend<Blake2Hasher>>::Transaction; type StorageUpdate<B, Block> = <<<B as backend::Backend<Block, Blake2Hasher>>::BlockImportOperation as BlockImportOperation<Block, Blake2Hasher>>::State as state_machine::Backend<Blake2Hasher>>::Transaction;
type ChangesUpdate = trie::MemoryDB<Blake2Hasher>; type ChangesUpdate = trie::MemoryDB<Blake2Hasher>;
/// Execution strategies settings.
#[derive(Debug, Clone)]
pub struct ExecutionStrategies {
/// Execution strategy used when syncing.
pub syncing: ExecutionStrategy,
/// Execution strategy used when importing blocks.
pub importing: ExecutionStrategy,
/// Execution strategy used when constructing blocks.
pub block_construction: ExecutionStrategy,
/// Execution strategy used in other cases.
pub other: ExecutionStrategy,
}
impl Default for ExecutionStrategies {
fn default() -> ExecutionStrategies {
ExecutionStrategies {
syncing: ExecutionStrategy::NativeElseWasm,
importing: ExecutionStrategy::NativeElseWasm,
block_construction: ExecutionStrategy::AlwaysWasm,
other: ExecutionStrategy::NativeElseWasm,
}
}
}
/// Substrate Client /// Substrate Client
pub struct Client<B, E, Block, RA> where Block: BlockT { pub struct Client<B, E, Block, RA> where Block: BlockT {
backend: Arc<B>, backend: Arc<B>,
@@ -85,8 +109,7 @@ pub struct Client<B, E, Block, RA> where Block: BlockT {
import_lock: Mutex<()>, import_lock: Mutex<()>,
// holds the block hash currently being imported. TODO: replace this with block queue // holds the block hash currently being imported. TODO: replace this with block queue
importing_block: RwLock<Option<Block::Hash>>, importing_block: RwLock<Option<Block::Hash>>,
block_execution_strategy: ExecutionStrategy, execution_strategies: ExecutionStrategies,
api_execution_strategy: ExecutionStrategy,
_phantom: PhantomData<RA>, _phantom: PhantomData<RA>,
} }
@@ -236,7 +259,7 @@ pub fn new_with_backend<B, E, Block, S, RA>(
B: backend::LocalBackend<Block, Blake2Hasher> B: backend::LocalBackend<Block, Blake2Hasher>
{ {
let call_executor = LocalCallExecutor::new(backend.clone(), executor); let call_executor = LocalCallExecutor::new(backend.clone(), executor);
Client::new(backend, call_executor, build_genesis_storage, ExecutionStrategy::NativeWhenPossible, ExecutionStrategy::NativeWhenPossible) Client::new(backend, call_executor, build_genesis_storage, Default::default())
} }
impl<B, E, Block, RA> Client<B, E, Block, RA> where impl<B, E, Block, RA> Client<B, E, Block, RA> where
@@ -249,8 +272,7 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
backend: Arc<B>, backend: Arc<B>,
executor: E, executor: E,
build_genesis_storage: S, build_genesis_storage: S,
block_execution_strategy: ExecutionStrategy, execution_strategies: ExecutionStrategies
api_execution_strategy: ExecutionStrategy,
) -> error::Result<Self> { ) -> error::Result<Self> {
if backend.blockchain().header(BlockId::Number(Zero::zero()))?.is_none() { if backend.blockchain().header(BlockId::Number(Zero::zero()))?.is_none() {
let (genesis_storage, children_genesis_storage) = build_genesis_storage.build_storage()?; let (genesis_storage, children_genesis_storage) = build_genesis_storage.build_storage()?;
@@ -276,12 +298,16 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
finality_notification_sinks: Default::default(), finality_notification_sinks: Default::default(),
import_lock: Default::default(), import_lock: Default::default(),
importing_block: Default::default(), importing_block: Default::default(),
block_execution_strategy, execution_strategies,
api_execution_strategy,
_phantom: Default::default(), _phantom: Default::default(),
}) })
} }
/// Get a reference to the execution strategies.
pub fn execution_strategies(&self) -> &ExecutionStrategies {
&self.execution_strategies
}
/// Get a reference to the state at a given block. /// Get a reference to the state at a given block.
pub fn state_at(&self, block: &BlockId<Block>) -> error::Result<B::State> { pub fn state_at(&self, block: &BlockId<Block>) -> error::Result<B::State> {
self.backend.state_at(*block) self.backend.state_at(*block)
@@ -315,7 +341,7 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
pub fn authorities_at(&self, id: &BlockId<Block>) -> error::Result<Vec<AuthorityIdFor<Block>>> { pub fn authorities_at(&self, id: &BlockId<Block>) -> error::Result<Vec<AuthorityIdFor<Block>>> {
match self.backend.blockchain().cache().and_then(|cache| cache.authorities_at(*id)) { match self.backend.blockchain().cache().and_then(|cache| cache.authorities_at(*id)) {
Some(cached_value) => Ok(cached_value), Some(cached_value) => Ok(cached_value),
None => self.executor.call(id, "Core_authorities", &[]) None => self.executor.call(id, "Core_authorities", &[], ExecutionStrategy::NativeElseWasm)
.and_then(|r| Vec::<AuthorityIdFor<Block>>::decode(&mut &r[..]) .and_then(|r| Vec::<AuthorityIdFor<Block>>::decode(&mut &r[..])
.ok_or_else(|| error::ErrorKind::InvalidAuthoritiesSet.into())) .ok_or_else(|| error::ErrorKind::InvalidAuthoritiesSet.into()))
} }
@@ -803,16 +829,12 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
match transaction.state()? { match transaction.state()? {
Some(transaction_state) => { Some(transaction_state) => {
let mut overlay = Default::default(); let mut overlay = Default::default();
let (_, storage_update, changes_update) = self.executor.call_at_state::<_, _, NeverNativeValue, fn() -> _>( let get_execution_manager = |execution_strategy: ExecutionStrategy| {
transaction_state, match execution_strategy {
&mut overlay, ExecutionStrategy::NativeElseWasm => ExecutionManager::NativeElseWasm,
"Core_execute_block", ExecutionStrategy::AlwaysWasm => ExecutionManager::AlwaysWasm,
&<Block as BlockT>::new(import_headers.pre().clone(), body.unwrap_or_default()).encode(), ExecutionStrategy::NativeWhenPossible => ExecutionManager::NativeWhenPossible,
match (origin, self.block_execution_strategy) { ExecutionStrategy::Both => ExecutionManager::Both(|wasm_result, native_result| {
(BlockOrigin::NetworkInitialSync, _) | (_, ExecutionStrategy::NativeWhenPossible) =>
ExecutionManager::NativeWhenPossible,
(_, ExecutionStrategy::AlwaysWasm) => ExecutionManager::AlwaysWasm,
_ => ExecutionManager::Both(|wasm_result, native_result| {
let header = import_headers.post(); let header = import_headers.post();
warn!("Consensus error between wasm and native block execution at block {}", hash); warn!("Consensus error between wasm and native block execution at block {}", hash);
warn!(" Header {:?}", header); warn!(" Header {:?}", header);
@@ -825,6 +847,16 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
); );
wasm_result wasm_result
}), }),
}
};
let (_, storage_update, changes_update) = self.executor.call_at_state::<_, _, NeverNativeValue, fn() -> _>(
transaction_state,
&mut overlay,
"Core_execute_block",
&<Block as BlockT>::new(import_headers.pre().clone(), body.unwrap_or_default()).encode(),
match origin {
BlockOrigin::NetworkInitialSync => get_execution_manager(self.execution_strategies().syncing),
_ => get_execution_manager(self.execution_strategies().importing),
}, },
None, None,
)?; )?;
@@ -1253,27 +1285,22 @@ impl<B, E, Block, RA> CallRuntimeAt<Block> for Client<B, E, Block, RA> where
changes: &mut OverlayedChanges, changes: &mut OverlayedChanges,
initialised_block: &mut Option<BlockId<Block>>, initialised_block: &mut Option<BlockId<Block>>,
native_call: Option<NC>, native_call: Option<NC>,
context: ExecutionContext
) -> error::Result<NativeOrEncoded<R>> { ) -> error::Result<NativeOrEncoded<R>> {
let execution_manager = match self.api_execution_strategy { let manager = match context {
ExecutionStrategy::NativeWhenPossible => ExecutionManager::NativeWhenPossible, ExecutionContext::BlockConstruction => self.execution_strategies.block_construction.get_manager(),
ExecutionStrategy::AlwaysWasm => ExecutionManager::AlwaysWasm, ExecutionContext::Syncing => self.execution_strategies.syncing.get_manager(),
ExecutionStrategy::Both => ExecutionManager::Both(|wasm_result, native_result| { ExecutionContext::Importing => self.execution_strategies.importing.get_manager(),
warn!("Consensus error between wasm and native runtime execution at block {:?}", at); ExecutionContext::Other => self.execution_strategies.other.get_manager(),
warn!(" Function {:?}", function);
warn!(" Native result {:?}", native_result);
warn!(" Wasm result {:?}", wasm_result);
wasm_result
}),
}; };
self.executor.contextual_call::<_, fn(_,_) -> _,_,_>(
self.executor.contextual_call(
at, at,
function, function,
&args, &args,
changes, changes,
initialised_block, initialised_block,
|| self.prepare_environment_block(at), || self.prepare_environment_block(at),
execution_manager, manager,
native_call, native_call,
) )
} }
+5 -5
View File
@@ -93,7 +93,7 @@ mod tests {
&executor(), &executor(),
"Core_initialise_block", "Core_initialise_block",
&header.encode(), &header.encode(),
ExecutionStrategy::NativeWhenPossible, ExecutionStrategy::NativeElseWasm,
).unwrap(); ).unwrap();
for tx in transactions.iter() { for tx in transactions.iter() {
@@ -104,7 +104,7 @@ mod tests {
&executor(), &executor(),
"BlockBuilder_apply_extrinsic", "BlockBuilder_apply_extrinsic",
&tx.encode(), &tx.encode(),
ExecutionStrategy::NativeWhenPossible, ExecutionStrategy::NativeElseWasm,
).unwrap(); ).unwrap();
} }
@@ -115,7 +115,7 @@ mod tests {
&executor(), &executor(),
"BlockBuilder_finalise_block", "BlockBuilder_finalise_block",
&[], &[],
ExecutionStrategy::NativeWhenPossible, ExecutionStrategy::NativeElseWasm,
).unwrap(); ).unwrap();
header = Header::decode(&mut &ret_data[..]).unwrap(); header = Header::decode(&mut &ret_data[..]).unwrap();
println!("root after: {:?}", header.extrinsics_root); println!("root after: {:?}", header.extrinsics_root);
@@ -159,7 +159,7 @@ mod tests {
&executor(), &executor(),
"Core_execute_block", "Core_execute_block",
&b1data, &b1data,
ExecutionStrategy::NativeWhenPossible, ExecutionStrategy::NativeElseWasm,
).unwrap(); ).unwrap();
} }
@@ -210,7 +210,7 @@ mod tests {
&Executor::new(None), &Executor::new(None),
"Core_execute_block", "Core_execute_block",
&b1data, &b1data,
ExecutionStrategy::NativeWhenPossible, ExecutionStrategy::NativeElseWasm,
).unwrap(); ).unwrap();
} }
} }
+1 -1
View File
@@ -57,7 +57,7 @@ pub use crate::client::{
new_with_backend, new_with_backend,
new_in_mem, new_in_mem,
BlockBody, BlockStatus, ImportNotifications, FinalityNotifications, BlockchainEvents, BlockBody, BlockStatus, ImportNotifications, FinalityNotifications, BlockchainEvents,
BlockImportNotification, Client, ClientInfo, ChainHead, BlockImportNotification, Client, ClientInfo, ChainHead, ExecutionStrategies,
}; };
#[cfg(feature = "std")] #[cfg(feature = "std")]
pub use crate::notifications::{StorageEventStream, StorageChangeSet}; pub use crate::notifications::{StorageEventStream, StorageChangeSet};
@@ -24,7 +24,7 @@ use codec::{Encode, Decode};
use primitives::{H256, Blake2Hasher, convert_hash, NativeOrEncoded}; use primitives::{H256, Blake2Hasher, convert_hash, NativeOrEncoded};
use runtime_primitives::generic::BlockId; use runtime_primitives::generic::BlockId;
use runtime_primitives::traits::{As, Block as BlockT, Header as HeaderT}; use runtime_primitives::traits::{As, Block as BlockT, Header as HeaderT};
use state_machine::{self, Backend as StateBackend, CodeExecutor, OverlayedChanges, use state_machine::{self, Backend as StateBackend, CodeExecutor, OverlayedChanges, ExecutionStrategy,
create_proof_check_backend, execution_proof_check_on_trie_backend, ExecutionManager}; create_proof_check_backend, execution_proof_check_on_trie_backend, ExecutionManager};
use hash_db::Hasher; use hash_db::Hasher;
@@ -80,7 +80,8 @@ where
{ {
type Error = ClientError; type Error = ClientError;
fn call(&self, id: &BlockId<Block>, method: &str, call_data: &[u8]) -> ClientResult<Vec<u8>> { fn call(&self, id: &BlockId<Block>, method: &str, call_data: &[u8], _strategy: ExecutionStrategy)
-> ClientResult<Vec<u8>> {
let block_hash = self.blockchain.expect_block_hash_from_id(id)?; let block_hash = self.blockchain.expect_block_hash_from_id(id)?;
let block_header = self.blockchain.expect_header(id.clone())?; let block_header = self.blockchain.expect_header(id.clone())?;
@@ -109,7 +110,7 @@ where
changes: &mut OverlayedChanges, changes: &mut OverlayedChanges,
initialised_block: &mut Option<BlockId<Block>>, initialised_block: &mut Option<BlockId<Block>>,
_prepare_environment_block: PB, _prepare_environment_block: PB,
_manager: ExecutionManager<EM>, execution_manager: ExecutionManager<EM>,
_native_call: Option<NC>, _native_call: Option<NC>,
) -> ClientResult<NativeOrEncoded<R>> where ExecutionManager<EM>: Clone { ) -> ClientResult<NativeOrEncoded<R>> where ExecutionManager<EM>: Clone {
// it is only possible to execute contextual call if changes are empty // it is only possible to execute contextual call if changes are empty
@@ -117,11 +118,11 @@ where
return Err(ClientErrorKind::NotAvailableOnLightClient.into()); return Err(ClientErrorKind::NotAvailableOnLightClient.into());
} }
self.call(at, method, call_data).map(NativeOrEncoded::Encoded) self.call(at, method, call_data, (&execution_manager).into()).map(NativeOrEncoded::Encoded)
} }
fn runtime_version(&self, id: &BlockId<Block>) -> ClientResult<RuntimeVersion> { fn runtime_version(&self, id: &BlockId<Block>) -> ClientResult<RuntimeVersion> {
let call_result = self.call(id, "version", &[])?; let call_result = self.call(id, "version", &[], ExecutionStrategy::NativeElseWasm)?;
RuntimeVersion::decode(&mut call_result.as_slice()) RuntimeVersion::decode(&mut call_result.as_slice())
.ok_or_else(|| ClientErrorKind::VersionInvalid.into()) .ok_or_else(|| ClientErrorKind::VersionInvalid.into())
} }
@@ -200,10 +201,11 @@ impl<Block, B, Remote, Local> CallExecutor<Block, Blake2Hasher> for
{ {
type Error = ClientError; type Error = ClientError;
fn call(&self, id: &BlockId<Block>, method: &str, call_data: &[u8]) -> ClientResult<Vec<u8>> { fn call(&self, id: &BlockId<Block>, method: &str, call_data: &[u8], strategy: ExecutionStrategy)
-> ClientResult<Vec<u8>> {
match self.backend.is_local_state_available(id) { match self.backend.is_local_state_available(id) {
true => self.local.call(id, method, call_data), true => self.local.call(id, method, call_data, strategy),
false => self.remote.call(id, method, call_data), false => self.remote.call(id, method, call_data, strategy),
} }
} }
@@ -507,7 +509,7 @@ mod tests {
let local_executor = RemoteCallExecutor::new(Arc::new(backend.blockchain().clone()), Arc::new(OkCallFetcher::new(vec![1]))); let local_executor = RemoteCallExecutor::new(Arc::new(backend.blockchain().clone()), Arc::new(OkCallFetcher::new(vec![1])));
let remote_executor = RemoteCallExecutor::new(Arc::new(backend.blockchain().clone()), Arc::new(OkCallFetcher::new(vec![2]))); let remote_executor = RemoteCallExecutor::new(Arc::new(backend.blockchain().clone()), Arc::new(OkCallFetcher::new(vec![2])));
let remote_or_local = RemoteOrLocalCallExecutor::new(backend, remote_executor, local_executor); let remote_or_local = RemoteOrLocalCallExecutor::new(backend, remote_executor, local_executor);
assert_eq!(remote_or_local.call(&BlockId::Number(0), "test_method", &[]).unwrap(), vec![1]); assert_eq!(remote_or_local.call(&BlockId::Number(0), "test_method", &[], ExecutionStrategy::NativeElseWasm).unwrap(), vec![1]);
assert_eq!(remote_or_local.call(&BlockId::Number(1), "test_method", &[]).unwrap(), vec![2]); assert_eq!(remote_or_local.call(&BlockId::Number(1), "test_method", &[], ExecutionStrategy::NativeElseWasm).unwrap(), vec![2]);
} }
} }
+2 -2
View File
@@ -27,7 +27,7 @@ use executor::RuntimeInfo;
use primitives::{H256, Blake2Hasher}; use primitives::{H256, Blake2Hasher};
use runtime_primitives::BuildStorage; use runtime_primitives::BuildStorage;
use runtime_primitives::traits::Block as BlockT; use runtime_primitives::traits::Block as BlockT;
use state_machine::{CodeExecutor, ExecutionStrategy}; use state_machine::CodeExecutor;
use crate::call_executor::LocalCallExecutor; use crate::call_executor::LocalCallExecutor;
use crate::client::Client; use crate::client::Client;
@@ -75,7 +75,7 @@ pub fn new_light<B, S, F, GS, RA, E>(
let remote_executor = RemoteCallExecutor::new(backend.blockchain().clone(), fetcher); let remote_executor = RemoteCallExecutor::new(backend.blockchain().clone(), fetcher);
let local_executor = LocalCallExecutor::new(backend.clone(), code_executor); let local_executor = LocalCallExecutor::new(backend.clone(), code_executor);
let executor = RemoteOrLocalCallExecutor::new(backend.clone(), remote_executor, local_executor); let executor = RemoteOrLocalCallExecutor::new(backend.clone(), remote_executor, local_executor);
Client::new(backend, executor, genesis_storage, ExecutionStrategy::NativeWhenPossible, ExecutionStrategy::NativeWhenPossible) Client::new(backend, executor, genesis_storage, Default::default())
} }
/// Create an instance of fetch data checker. /// Create an instance of fetch data checker.
+2 -1
View File
@@ -25,7 +25,7 @@ pub use primitives::NativeOrEncoded;
#[doc(hidden)] #[doc(hidden)]
pub use runtime_primitives::{ pub use runtime_primitives::{
traits::{AuthorityIdFor, Block as BlockT, GetNodeBlockType, GetRuntimeBlockType, ApiRef, RuntimeApiInfo}, traits::{AuthorityIdFor, Block as BlockT, GetNodeBlockType, GetRuntimeBlockType, ApiRef, RuntimeApiInfo},
generic::BlockId, transaction_validity::TransactionValidity generic::BlockId, transaction_validity::TransactionValidity, ExecutionContext,
}; };
#[doc(hidden)] #[doc(hidden)]
pub use runtime_version::{ApiId, RuntimeVersion, ApisVec, create_apis_vec}; pub use runtime_version::{ApiId, RuntimeVersion, ApisVec, create_apis_vec};
@@ -99,6 +99,7 @@ pub trait CallRuntimeAt<Block: BlockT> {
changes: &mut OverlayedChanges, changes: &mut OverlayedChanges,
initialised_block: &mut Option<BlockId<Block>>, initialised_block: &mut Option<BlockId<Block>>,
native_call: Option<NC>, native_call: Option<NC>,
context: ExecutionContext
) -> error::Result<NativeOrEncoded<R>>; ) -> error::Result<NativeOrEncoded<R>>;
/// Returns the runtime version at the given block. /// Returns the runtime version at the given block.
+13 -6
View File
@@ -36,6 +36,7 @@ use std::collections::{HashMap, HashSet};
use std::result; use std::result;
use runtime_primitives::traits::{ApiRef, ProvideRuntimeApi}; use runtime_primitives::traits::{ApiRef, ProvideRuntimeApi};
use runtime_primitives::generic::BlockId; use runtime_primitives::generic::BlockId;
use runtime_primitives::ExecutionContext;
use substrate_primitives::NativeOrEncoded; use substrate_primitives::NativeOrEncoded;
use authorities::AuthoritySet; use authorities::AuthoritySet;
@@ -266,8 +267,9 @@ impl Core<Block> for RuntimeApi {
fn version_runtime_api_impl( fn version_runtime_api_impl(
&self, &self,
_: &BlockId<Block>, _: &BlockId<Block>,
_: ExecutionContext,
_: Option<()>, _: Option<()>,
_: Vec<u8> _: Vec<u8>,
) -> Result<NativeOrEncoded<RuntimeVersion>> { ) -> Result<NativeOrEncoded<RuntimeVersion>> {
unimplemented!("Not required for testing!") unimplemented!("Not required for testing!")
} }
@@ -275,8 +277,9 @@ impl Core<Block> for RuntimeApi {
fn authorities_runtime_api_impl( fn authorities_runtime_api_impl(
&self, &self,
_: &BlockId<Block>, _: &BlockId<Block>,
_: ExecutionContext,
_: Option<()>, _: Option<()>,
_: Vec<u8> _: Vec<u8>,
) -> Result<NativeOrEncoded<Vec<Ed25519AuthorityId>>> { ) -> Result<NativeOrEncoded<Vec<Ed25519AuthorityId>>> {
unimplemented!("Not required for testing!") unimplemented!("Not required for testing!")
} }
@@ -284,8 +287,9 @@ impl Core<Block> for RuntimeApi {
fn execute_block_runtime_api_impl( fn execute_block_runtime_api_impl(
&self, &self,
_: &BlockId<Block>, _: &BlockId<Block>,
_: ExecutionContext,
_: Option<(Block)>, _: Option<(Block)>,
_: Vec<u8> _: Vec<u8>,
) -> Result<NativeOrEncoded<()>> { ) -> Result<NativeOrEncoded<()>> {
unimplemented!("Not required for testing!") unimplemented!("Not required for testing!")
} }
@@ -293,6 +297,7 @@ impl Core<Block> for RuntimeApi {
fn initialise_block_runtime_api_impl( fn initialise_block_runtime_api_impl(
&self, &self,
_: &BlockId<Block>, _: &BlockId<Block>,
_: ExecutionContext,
_: Option<&<Block as BlockT>::Header>, _: Option<&<Block as BlockT>::Header>,
_: Vec<u8>, _: Vec<u8>,
) -> Result<NativeOrEncoded<()>> { ) -> Result<NativeOrEncoded<()>> {
@@ -317,6 +322,7 @@ impl GrandpaApi<Block> for RuntimeApi {
fn grandpa_authorities_runtime_api_impl( fn grandpa_authorities_runtime_api_impl(
&self, &self,
at: &BlockId<Block>, at: &BlockId<Block>,
_: ExecutionContext,
_: Option<()>, _: Option<()>,
_: Vec<u8>, _: Vec<u8>,
) -> Result<NativeOrEncoded<Vec<(Ed25519AuthorityId, u64)>>> { ) -> Result<NativeOrEncoded<Vec<(Ed25519AuthorityId, u64)>>> {
@@ -330,8 +336,9 @@ impl GrandpaApi<Block> for RuntimeApi {
fn grandpa_pending_change_runtime_api_impl( fn grandpa_pending_change_runtime_api_impl(
&self, &self,
at: &BlockId<Block>, at: &BlockId<Block>,
_: ExecutionContext,
_: Option<(&DigestFor<Block>)>, _: Option<(&DigestFor<Block>)>,
_: Vec<u8> _: Vec<u8>,
) -> Result<NativeOrEncoded<Option<ScheduledChange<NumberFor<Block>>>>> { ) -> Result<NativeOrEncoded<Option<ScheduledChange<NumberFor<Block>>>>> {
let parent_hash = match at { let parent_hash = match at {
&BlockId::Hash(at) => at, &BlockId::Hash(at) => at,
@@ -537,7 +544,7 @@ fn transition_3_voters_twice_1_observer() {
for (i, peer) in net.lock().peers().iter().enumerate() { for (i, peer) in net.lock().peers().iter().enumerate() {
assert_eq!(peer.client().info().unwrap().chain.best_number, 1, assert_eq!(peer.client().info().unwrap().chain.best_number, 1,
"Peer #{} failed to sync", i); "Peer #{} failed to sync", i);
let set_raw = peer.client().backend().get_aux(::AUTHORITY_SET_KEY).unwrap().unwrap(); let set_raw = peer.client().backend().get_aux(::AUTHORITY_SET_KEY).unwrap().unwrap();
let set = AuthoritySet::<Hash, BlockNumber>::decode(&mut &set_raw[..]).unwrap(); let set = AuthoritySet::<Hash, BlockNumber>::decode(&mut &set_raw[..]).unwrap();
@@ -694,7 +701,7 @@ fn justification_is_generated_periodically() {
let net = Arc::new(Mutex::new(net)); let net = Arc::new(Mutex::new(net));
run_to_completion(32, net.clone(), peers); run_to_completion(32, net.clone(), peers);
// when block#32 (justification_period) is finalized, justification // when block#32 (justification_period) is finalized, justification
// is required => generated // is required => generated
for i in 0..3 { for i in 0..3 {
assert!(net.lock().peer(i).client().backend().blockchain() assert!(net.lock().peer(i).client().backend().blockchain()
+1
View File
@@ -19,6 +19,7 @@ client = { package = "substrate-client", path = "../client" }
substrate-executor = { path = "../executor" } substrate-executor = { path = "../executor" }
network = { package = "substrate-network", path = "../network" } network = { package = "substrate-network", path = "../network" }
primitives = { package = "substrate-primitives", path = "../primitives" } primitives = { package = "substrate-primitives", path = "../primitives" }
state_machine = { package = "substrate-state-machine", path = "../state-machine" }
transaction_pool = { package = "substrate-transaction-pool", path = "../transaction-pool" } transaction_pool = { package = "substrate-transaction-pool", path = "../transaction-pool" }
runtime_primitives = { package = "sr-primitives", path = "../sr-primitives" } runtime_primitives = { package = "sr-primitives", path = "../sr-primitives" }
runtime_version = { package = "sr-version", path = "../sr-version" } runtime_version = { package = "sr-version", path = "../sr-version" }
+2 -1
View File
@@ -35,6 +35,7 @@ use crate::rpc::futures::{stream, Future, Sink, Stream};
use runtime_primitives::generic::BlockId; use runtime_primitives::generic::BlockId;
use runtime_primitives::traits::{Block as BlockT, Header, ProvideRuntimeApi, As, NumberFor}; use runtime_primitives::traits::{Block as BlockT, Header, ProvideRuntimeApi, As, NumberFor};
use runtime_version::RuntimeVersion; use runtime_version::RuntimeVersion;
use state_machine::ExecutionStrategy;
use crate::subscriptions::Subscriptions; use crate::subscriptions::Subscriptions;
@@ -297,7 +298,7 @@ impl<B, E, Block, RA> StateApi<Block::Hash> for State<B, E, Block, RA> where
.executor() .executor()
.call( .call(
&BlockId::Hash(block), &BlockId::Hash(block),
&method, &data.0 &method, &data.0, ExecutionStrategy::NativeElseWasm
)?; )?;
Ok(Bytes(return_data)) Ok(Bytes(return_data))
} }
+1 -2
View File
@@ -437,8 +437,7 @@ impl<Factory: ServiceFactory> Components for FullComponents<Factory> {
db_settings, db_settings,
executor, executor,
&config.chain_spec, &config.chain_spec,
config.block_execution_strategy, config.execution_strategies.clone(),
config.api_execution_strategy,
)?), None)) )?), None))
} }
+4 -7
View File
@@ -19,7 +19,7 @@
use std::net::SocketAddr; use std::net::SocketAddr;
use transaction_pool; use transaction_pool;
use crate::chain_spec::ChainSpec; use crate::chain_spec::ChainSpec;
pub use client::ExecutionStrategy; pub use client::ExecutionStrategies;
pub use client_db::PruningMode; pub use client_db::PruningMode;
pub use network::config::{NetworkConfiguration, Roles}; pub use network::config::{NetworkConfiguration, Roles};
use runtime_primitives::BuildStorage; use runtime_primitives::BuildStorage;
@@ -57,10 +57,8 @@ pub struct Configuration<C, G: Serialize + DeserializeOwned + BuildStorage> {
pub custom: C, pub custom: C,
/// Node name. /// Node name.
pub name: String, pub name: String,
/// Block execution strategy. /// Execution strategies.
pub block_execution_strategy: ExecutionStrategy, pub execution_strategies: ExecutionStrategies,
/// Runtime API execution strategy.
pub api_execution_strategy: ExecutionStrategy,
/// RPC over HTTP binding address. `None` if disabled. /// RPC over HTTP binding address. `None` if disabled.
pub rpc_http: Option<SocketAddr>, pub rpc_http: Option<SocketAddr>,
/// RPC over Websockets binding address. `None` if disabled. /// RPC over Websockets binding address. `None` if disabled.
@@ -89,8 +87,7 @@ impl<C: Default, G: Serialize + DeserializeOwned + BuildStorage> Configuration<C
keys: Default::default(), keys: Default::default(),
custom: Default::default(), custom: Default::default(),
pruning: PruningMode::default(), pruning: PruningMode::default(),
block_execution_strategy: ExecutionStrategy::Both, execution_strategies: Default::default(),
api_execution_strategy: ExecutionStrategy::Both,
rpc_http: None, rpc_http: None,
rpc_ws: None, rpc_ws: None,
telemetry_url: None, telemetry_url: None,
+1 -1
View File
@@ -49,7 +49,7 @@ pub use chain_spec::{ChainSpec, Properties};
pub use transaction_pool::txpool::{ pub use transaction_pool::txpool::{
self, Pool as TransactionPool, Options as TransactionPoolOptions, ChainApi, IntoPoolError self, Pool as TransactionPool, Options as TransactionPoolOptions, ChainApi, IntoPoolError
}; };
pub use client::{ExecutionStrategy, FinalityNotifications}; pub use client::FinalityNotifications;
pub use components::{ServiceFactory, FullBackend, FullExecutor, LightBackend, pub use components::{ServiceFactory, FullBackend, FullExecutor, LightBackend,
LightExecutor, Components, PoolApi, ComponentClient, LightExecutor, Components, PoolApi, ComponentClient,
+1 -3
View File
@@ -28,7 +28,6 @@ use tokio::timer::Interval;
use primitives::blake2_256; use primitives::blake2_256;
use service::{ use service::{
ServiceFactory, ServiceFactory,
ExecutionStrategy,
Configuration, Configuration,
FactoryFullConfiguration, FactoryFullConfiguration,
FactoryChainSpec, FactoryChainSpec,
@@ -118,8 +117,7 @@ fn node_config<F: ServiceFactory> (
chain_spec: (*spec).clone(), chain_spec: (*spec).clone(),
custom: Default::default(), custom: Default::default(),
name: format!("Node {}", index), name: format!("Node {}", index),
block_execution_strategy: ExecutionStrategy::NativeWhenPossible, execution_strategies: Default::default(),
api_execution_strategy: ExecutionStrategy::NativeWhenPossible,
rpc_http: None, rpc_http: None,
rpc_ws: None, rpc_ws: None,
telemetry_url: None, telemetry_url: None,
@@ -275,7 +275,7 @@ fn generate_native_call_generators(decl: &ItemTrait) -> Result<TokenStream> {
Ok(quote!( #( #result )* )) Ok(quote!( #( #result )* ))
} }
/// Generate the decleration of the trait for the runtime. /// Generate the declaration of the trait for the runtime.
fn generate_runtime_decls(decls: &[ItemTrait]) -> TokenStream { fn generate_runtime_decls(decls: &[ItemTrait]) -> TokenStream {
let mut result = Vec::new(); let mut result = Vec::new();
@@ -339,8 +339,9 @@ impl<'a> ToClientSideDecl<'a> {
items.into_iter().for_each(|i| match i { items.into_iter().for_each(|i| match i {
TraitItem::Method(method) => { TraitItem::Method(method) => {
let (fn_decl, fn_impl) = self.fold_trait_item_method(method); let (fn_decl, fn_impl, fn_decl_ctx) = self.fold_trait_item_method(method);
result.push(fn_decl.into()); result.push(fn_decl.into());
result.push(fn_decl_ctx.into());
if let Some(fn_impl) = fn_impl { if let Some(fn_impl) = fn_impl {
result.push(fn_impl.into()); result.push(fn_impl.into());
@@ -352,11 +353,25 @@ impl<'a> ToClientSideDecl<'a> {
result result
} }
fn fold_trait_item_method(&mut self, method: TraitItemMethod) -> (TraitItemMethod, Option<TraitItemMethod>) { fn fold_trait_item_method(&mut self, method: TraitItemMethod)
-> (TraitItemMethod, Option<TraitItemMethod>, TraitItemMethod) {
let crate_ = self.crate_;
let context_other = quote!( #crate_::runtime_api::ExecutionContext::Other );
let fn_impl = self.create_method_runtime_api_impl(method.clone()); let fn_impl = self.create_method_runtime_api_impl(method.clone());
let fn_decl = self.create_method_decl(method); let fn_decl = self.create_method_decl(method.clone(), context_other);
let fn_decl_ctx = self.create_method_decl_with_context(method);
(fn_decl, fn_impl) (fn_decl, fn_impl, fn_decl_ctx)
}
fn create_method_decl_with_context(&mut self, method: TraitItemMethod) -> TraitItemMethod {
let crate_ = self.crate_;
let context_arg: syn::FnArg = parse_quote!( context: #crate_::runtime_api::ExecutionContext );
let mut fn_decl_ctx = self.create_method_decl(method, quote!( context ));
fn_decl_ctx.sig.ident = Ident::new(&format!("{}_with_context", &fn_decl_ctx.sig.ident), Span::call_site());
fn_decl_ctx.sig.decl.inputs.insert(2, context_arg);
fn_decl_ctx
} }
/// Takes the given method and creates a `method_runtime_api_impl` method that will be /// Takes the given method and creates a `method_runtime_api_impl` method that will be
@@ -392,8 +407,9 @@ impl<'a> ToClientSideDecl<'a> {
fn #name( fn #name(
&self, &self,
at: &#block_id, at: &#block_id,
context: #crate_::runtime_api::ExecutionContext,
params: Option<( #( #param_types ),* )>, params: Option<( #( #param_types ),* )>,
params_encoded: Vec<u8> params_encoded: Vec<u8>,
) -> #crate_::error::Result<#crate_::runtime_api::NativeOrEncoded<#ret_type>>; ) -> #crate_::error::Result<#crate_::runtime_api::NativeOrEncoded<#ret_type>>;
} }
) )
@@ -402,7 +418,7 @@ impl<'a> ToClientSideDecl<'a> {
/// Takes the method declared by the user and creates the declaration we require for the runtime /// Takes the method declared by the user and creates the declaration we require for the runtime
/// api client side. This method will call by default the `method_runtime_api_impl` for doing /// api client side. This method will call by default the `method_runtime_api_impl` for doing
/// the actual call into the runtime. /// the actual call into the runtime.
fn create_method_decl(&mut self, mut method: TraitItemMethod) -> TraitItemMethod { fn create_method_decl(&mut self, mut method: TraitItemMethod, context: TokenStream) -> TraitItemMethod {
let params = match extract_parameter_names_types_and_borrows(&method.sig.decl) { let params = match extract_parameter_names_types_and_borrows(&method.sig.decl) {
Ok(res) => res.into_iter().map(|v| v.0).collect::<Vec<_>>(), Ok(res) => res.into_iter().map(|v| v.0).collect::<Vec<_>>(),
Err(e) => { Err(e) => {
@@ -462,7 +478,7 @@ impl<'a> ToClientSideDecl<'a> {
let runtime_api_impl_params_encoded = let runtime_api_impl_params_encoded =
#crate_::runtime_api::Encode::encode(&( #( &#params ),* )); #crate_::runtime_api::Encode::encode(&( #( &#params ),* ));
self.#name_impl(at, #param_tuple, runtime_api_impl_params_encoded) self.#name_impl(at, #context, #param_tuple, runtime_api_impl_params_encoded)
.and_then(|r| .and_then(|r|
match r { match r {
#crate_::runtime_api::NativeOrEncoded::Native(n) => { #crate_::runtime_api::NativeOrEncoded::Native(n) => {
@@ -571,7 +587,7 @@ fn generate_runtime_info_impl(trait_: &ItemTrait, version: u64) -> TokenStream {
let (impl_generics, ty_generics, where_clause) = trait_.generics.split_for_impl(); let (impl_generics, ty_generics, where_clause) = trait_.generics.split_for_impl();
quote!( quote!(
#[cfg(any(feature = "std", test))] #[cfg(any(feature = "std", test))]
impl #impl_generics #crate_::runtime_api::RuntimeApiInfo impl #impl_generics #crate_::runtime_api::RuntimeApiInfo
for #trait_name #ty_generics #where_clause for #trait_name #ty_generics #where_clause
{ {
@@ -593,7 +609,7 @@ fn get_api_version(found_attributes: &HashMap<&'static str, Attribute>) -> Resul
found_attributes.get(&API_VERSION_ATTRIBUTE).map(parse_runtime_api_version).unwrap_or(Ok(1)) found_attributes.get(&API_VERSION_ATTRIBUTE).map(parse_runtime_api_version).unwrap_or(Ok(1))
} }
/// Generate the decleration of the trait for the client side. /// Generate the declaration of the trait for the client side.
fn generate_client_side_decls(decls: &[ItemTrait]) -> TokenStream { fn generate_client_side_decls(decls: &[ItemTrait]) -> TokenStream {
let mut result = Vec::new(); let mut result = Vec::new();
@@ -17,7 +17,8 @@
use utils::{ use utils::{
unwrap_or_error, generate_crate_access, generate_hidden_includes, unwrap_or_error, generate_crate_access, generate_hidden_includes,
generate_runtime_mod_name_for_trait, generate_method_runtime_api_impl_name, generate_runtime_mod_name_for_trait, generate_method_runtime_api_impl_name,
extract_parameter_names_types_and_borrows, generate_native_call_generator_fn_name, return_type_extract_type extract_parameter_names_types_and_borrows, generate_native_call_generator_fn_name,
return_type_extract_type
}; };
use proc_macro; use proc_macro;
@@ -337,6 +338,7 @@ fn generate_runtime_api_base_structures(impls: &[ItemImpl]) -> Result<TokenStrea
function: &'static str, function: &'static str,
args: Vec<u8>, args: Vec<u8>,
native_call: Option<NC>, native_call: Option<NC>,
context: #crate_::runtime_api::ExecutionContext
) -> #crate_::error::Result<#crate_::runtime_api::NativeOrEncoded<R>> { ) -> #crate_::error::Result<#crate_::runtime_api::NativeOrEncoded<R>> {
let res = unsafe { let res = unsafe {
self.call.call_api_at( self.call.call_api_at(
@@ -346,6 +348,7 @@ fn generate_runtime_api_base_structures(impls: &[ItemImpl]) -> Result<TokenStrea
&mut *self.changes.borrow_mut(), &mut *self.changes.borrow_mut(),
&mut *self.initialised_block.borrow_mut(), &mut *self.initialised_block.borrow_mut(),
native_call, native_call,
context
) )
}; };
@@ -466,9 +469,11 @@ impl<'a> Fold for ApiRuntimeImplToApiRuntimeApiImpl<'a> {
Err(e) => (Vec::new(), Some(e.to_compile_error())), Err(e) => (Vec::new(), Some(e.to_compile_error())),
}; };
let context_arg: syn::FnArg = parse_quote!( context: #crate_::runtime_api::ExecutionContext );
// Rewrite the input parameters. // Rewrite the input parameters.
input.sig.decl.inputs = parse_quote! { input.sig.decl.inputs = parse_quote! {
&self, at: &#block_id, params: Option<( #( #param_types ),* )>, params_encoded: Vec<u8> &self, at: &#block_id, #context_arg, params: Option<( #( #param_types ),* )>, params_encoded: Vec<u8>
}; };
input.sig.ident = generate_method_runtime_api_impl_name(&input.sig.ident); input.sig.ident = generate_method_runtime_api_impl_name(&input.sig.ident);
@@ -494,7 +499,8 @@ impl<'a> Fold for ApiRuntimeImplToApiRuntimeApiImpl<'a> {
<#runtime, #node_block #(, #trait_generic_arguments )*> ( <#runtime, #node_block #(, #trait_generic_arguments )*> (
#( #param_tuple_access ),* #( #param_tuple_access ),*
) )
}) }),
context,
) )
} }
) )
@@ -561,7 +567,6 @@ fn generate_api_impl_for_runtime_api(impls: &[ItemImpl]) -> Result<TokenStream>
result.push(visitor.fold_item_impl(impl_.clone())); result.push(visitor.fold_item_impl(impl_.clone()));
} }
Ok(quote!( #( #result )* )) Ok(quote!( #( #result )* ))
} }
@@ -611,13 +616,14 @@ fn generate_runtime_api_versions(impls: &[ItemImpl]) -> Result<TokenStream> {
pub fn impl_runtime_apis_impl(input: proc_macro::TokenStream) -> proc_macro::TokenStream { pub fn impl_runtime_apis_impl(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
// Parse all impl blocks // Parse all impl blocks
let RuntimeApiImpls { impls: api_impls } = parse_macro_input!(input as RuntimeApiImpls); let RuntimeApiImpls { impls: api_impls } = parse_macro_input!(input as RuntimeApiImpls);
let dispatch_impl = unwrap_or_error(generate_dispatch_function(&api_impls)); let dispatch_impl = unwrap_or_error(generate_dispatch_function(&api_impls));
let wasm_interface = unwrap_or_error(generate_wasm_interface(&api_impls));
let hidden_includes = generate_hidden_includes(HIDDEN_INCLUDES_ID);
let base_runtime_api = unwrap_or_error(generate_runtime_api_base_structures(&api_impls));
let api_impls_for_runtime = unwrap_or_error(generate_api_impl_for_runtime(&api_impls)); let api_impls_for_runtime = unwrap_or_error(generate_api_impl_for_runtime(&api_impls));
let api_impls_for_runtime_api = unwrap_or_error(generate_api_impl_for_runtime_api(&api_impls)); let base_runtime_api = unwrap_or_error(generate_runtime_api_base_structures(&api_impls));
let hidden_includes = generate_hidden_includes(HIDDEN_INCLUDES_ID);
let runtime_api_versions = unwrap_or_error(generate_runtime_api_versions(&api_impls)); let runtime_api_versions = unwrap_or_error(generate_runtime_api_versions(&api_impls));
let wasm_interface = unwrap_or_error(generate_wasm_interface(&api_impls));
let api_impls_for_runtime_api = unwrap_or_error(generate_api_impl_for_runtime_api(&api_impls));
quote!( quote!(
#hidden_includes #hidden_includes
@@ -24,7 +24,7 @@ use runtime_primitives::{generic::BlockId, traits::ProvideRuntimeApi};
use state_machine::ExecutionStrategy; use state_machine::ExecutionStrategy;
fn calling_function_with_strat(strat: ExecutionStrategy) { fn calling_function_with_strat(strat: ExecutionStrategy) {
let client = test_client::new_with_api_execution_strat(strat); let client = test_client::new_with_execution_strategy(strat);
let runtime_api = client.runtime_api(); let runtime_api = client.runtime_api();
let block_id = BlockId::Number(client.info().unwrap().chain.best_number); let block_id = BlockId::Number(client.info().unwrap().chain.best_number);
@@ -44,7 +44,7 @@ fn calling_wasm_runtime_function() {
#[test] #[test]
#[should_panic(expected = "Could not convert parameter `param` between node and runtime!")] #[should_panic(expected = "Could not convert parameter `param` between node and runtime!")]
fn calling_native_runtime_function_with_non_decodable_parameter() { fn calling_native_runtime_function_with_non_decodable_parameter() {
let client = test_client::new_with_api_execution_strat(ExecutionStrategy::NativeWhenPossible); let client = test_client::new_with_execution_strategy(ExecutionStrategy::NativeWhenPossible);
let runtime_api = client.runtime_api(); let runtime_api = client.runtime_api();
let block_id = BlockId::Number(client.info().unwrap().chain.best_number); let block_id = BlockId::Number(client.info().unwrap().chain.best_number);
runtime_api.fail_convert_parameter(&block_id, DecodeFails::new()).unwrap(); runtime_api.fail_convert_parameter(&block_id, DecodeFails::new()).unwrap();
@@ -53,7 +53,7 @@ fn calling_native_runtime_function_with_non_decodable_parameter() {
#[test] #[test]
#[should_panic(expected = "Could not convert return value from runtime to node!")] #[should_panic(expected = "Could not convert return value from runtime to node!")]
fn calling_native_runtime_function_with_non_decodable_return_value() { fn calling_native_runtime_function_with_non_decodable_return_value() {
let client = test_client::new_with_api_execution_strat(ExecutionStrategy::NativeWhenPossible); let client = test_client::new_with_execution_strategy(ExecutionStrategy::NativeWhenPossible);
let runtime_api = client.runtime_api(); let runtime_api = client.runtime_api();
let block_id = BlockId::Number(client.info().unwrap().chain.best_number); let block_id = BlockId::Number(client.info().unwrap().chain.best_number);
runtime_api.fail_convert_return_value(&block_id).unwrap(); runtime_api.fail_convert_return_value(&block_id).unwrap();
@@ -61,7 +61,7 @@ fn calling_native_runtime_function_with_non_decodable_return_value() {
#[test] #[test]
fn calling_native_runtime_signature_changed_function() { fn calling_native_runtime_signature_changed_function() {
let client = test_client::new_with_api_execution_strat(ExecutionStrategy::NativeWhenPossible); let client = test_client::new_with_execution_strategy(ExecutionStrategy::NativeWhenPossible);
let runtime_api = client.runtime_api(); let runtime_api = client.runtime_api();
let block_id = BlockId::Number(client.info().unwrap().chain.best_number); let block_id = BlockId::Number(client.info().unwrap().chain.best_number);
@@ -70,7 +70,7 @@ fn calling_native_runtime_signature_changed_function() {
#[test] #[test]
fn calling_wasm_runtime_signature_changed_old_function() { fn calling_wasm_runtime_signature_changed_old_function() {
let client = test_client::new_with_api_execution_strat(ExecutionStrategy::AlwaysWasm); let client = test_client::new_with_execution_strategy(ExecutionStrategy::AlwaysWasm);
let runtime_api = client.runtime_api(); let runtime_api = client.runtime_api();
let block_id = BlockId::Number(client.info().unwrap().chain.best_number); let block_id = BlockId::Number(client.info().unwrap().chain.best_number);
@@ -78,3 +78,36 @@ fn calling_wasm_runtime_signature_changed_old_function() {
let res = runtime_api.function_signature_changed_before_version_2(&block_id).unwrap(); let res = runtime_api.function_signature_changed_before_version_2(&block_id).unwrap();
assert_eq!(&res, &[1, 2]); assert_eq!(&res, &[1, 2]);
} }
#[test]
fn calling_with_both_strategy_and_fail_on_wasm_should_return_error() {
let client = test_client::new_with_execution_strategy(ExecutionStrategy::Both);
let runtime_api = client.runtime_api();
let block_id = BlockId::Number(client.info().unwrap().chain.best_number);
assert!(runtime_api.fail_on_wasm(&block_id).is_err());
}
#[test]
fn calling_with_both_strategy_and_fail_on_native_should_work() {
let client = test_client::new_with_execution_strategy(ExecutionStrategy::Both);
let runtime_api = client.runtime_api();
let block_id = BlockId::Number(client.info().unwrap().chain.best_number);
assert_eq!(runtime_api.fail_on_native(&block_id).unwrap(), 1);
}
#[test]
fn calling_with_native_else_wasm_and_faild_on_wasm_should_work() {
let client = test_client::new_with_execution_strategy(ExecutionStrategy::NativeElseWasm);
let runtime_api = client.runtime_api();
let block_id = BlockId::Number(client.info().unwrap().chain.best_number);
assert_eq!(runtime_api.fail_on_wasm(&block_id).unwrap(), 1);
}
#[test]
fn calling_with_native_else_wasm_and_fail_on_native_should_work() {
let client = test_client::new_with_execution_strategy(ExecutionStrategy::NativeElseWasm);
let runtime_api = client.runtime_api();
let block_id = BlockId::Number(client.info().unwrap().chain.best_number);
assert_eq!(runtime_api.fail_on_native(&block_id).unwrap(), 1);
}
+15
View File
@@ -266,6 +266,21 @@ impl From<H512> for Ed25519Signature {
} }
} }
/// Context for executing a call into the runtime.
#[derive(Copy, Clone, Eq, PartialEq, Encode, Decode)]
#[cfg_attr(feature = "std", derive(Debug, Serialize))]
#[repr(u8)]
pub enum ExecutionContext {
/// Context for general importing (including own blocks).
Importing,
/// Context used when syncing the blockchain.
Syncing,
/// Context used for block construction.
BlockConstruction,
/// Context used for other calls.
Other,
}
#[derive(Eq, PartialEq, Clone, Copy, Decode)] #[derive(Eq, PartialEq, Clone, Copy, Decode)]
#[cfg_attr(feature = "std", derive(Debug, Serialize))] #[cfg_attr(feature = "std", derive(Debug, Serialize))]
#[repr(u8)] #[repr(u8)]
+214 -75
View File
@@ -34,6 +34,7 @@ mod proving_backend;
mod trie_backend; mod trie_backend;
mod trie_backend_essence; mod trie_backend_essence;
use overlayed_changes::OverlayedChangeSet;
pub use trie::{TrieMut, TrieDBMut, DBValue, MemoryDB}; pub use trie::{TrieMut, TrieDBMut, DBValue, MemoryDB};
pub use testing::TestExternalities; pub use testing::TestExternalities;
pub use ext::Ext; pub use ext::Ext;
@@ -178,6 +179,8 @@ pub enum ExecutionStrategy {
AlwaysWasm, AlwaysWasm,
/// Run with both the wasm and the native variant (if compatible). Report any discrepency as an error. /// Run with both the wasm and the native variant (if compatible). Report any discrepency as an error.
Both, Both,
/// First native, then if that fails or is not possible, wasm.
NativeElseWasm,
} }
/// Like `ExecutionStrategy` only it also stores a handler in case of consensus failure. /// Like `ExecutionStrategy` only it also stores a handler in case of consensus failure.
@@ -189,6 +192,8 @@ pub enum ExecutionManager<F> {
AlwaysWasm, AlwaysWasm,
/// Run with both the wasm and the native variant (if compatible). Call `F` in the case of any discrepency. /// Run with both the wasm and the native variant (if compatible). Call `F` in the case of any discrepency.
Both(F), Both(F),
/// First native, then if that fails or is not possible, wasm.
NativeElseWasm,
} }
impl<'a, F> From<&'a ExecutionManager<F>> for ExecutionStrategy { impl<'a, F> From<&'a ExecutionManager<F>> for ExecutionStrategy {
@@ -196,11 +201,36 @@ impl<'a, F> From<&'a ExecutionManager<F>> for ExecutionStrategy {
match *s { match *s {
ExecutionManager::NativeWhenPossible => ExecutionStrategy::NativeWhenPossible, ExecutionManager::NativeWhenPossible => ExecutionStrategy::NativeWhenPossible,
ExecutionManager::AlwaysWasm => ExecutionStrategy::AlwaysWasm, ExecutionManager::AlwaysWasm => ExecutionStrategy::AlwaysWasm,
ExecutionManager::NativeElseWasm => ExecutionStrategy::NativeElseWasm,
ExecutionManager::Both(_) => ExecutionStrategy::Both, ExecutionManager::Both(_) => ExecutionStrategy::Both,
} }
} }
} }
impl ExecutionStrategy {
/// Gets the corresponding manager for the execution strategy.
pub fn get_manager<E: std::fmt::Debug, R: Decode + Encode>(self) ->
ExecutionManager<fn(
Result<NativeOrEncoded<R>, E>,
Result<NativeOrEncoded<R>, E>
) -> Result<NativeOrEncoded<R>, E>>
{
match self {
ExecutionStrategy::AlwaysWasm => ExecutionManager::AlwaysWasm,
ExecutionStrategy::NativeWhenPossible => ExecutionManager::NativeWhenPossible,
ExecutionStrategy::NativeElseWasm => ExecutionManager::NativeElseWasm,
ExecutionStrategy::Both => ExecutionManager::Both(|wasm_result, native_result| {
warn!(
"Consensus error between wasm {:?} and native {:?}. Using wasm.",
wasm_result,
native_result
);
wasm_result
}),
}
}
}
/// Evaluate to ExecutionManager::NativeWhenPossible, without having to figure out the type. /// Evaluate to ExecutionManager::NativeWhenPossible, without having to figure out the type.
pub fn native_when_possible<E, R: Decode>() -> pub fn native_when_possible<E, R: Decode>() ->
ExecutionManager< ExecutionManager<
@@ -213,6 +243,18 @@ pub fn native_when_possible<E, R: Decode>() ->
ExecutionManager::NativeWhenPossible ExecutionManager::NativeWhenPossible
} }
/// Evaluate to ExecutionManager::NativeElseWasm, without having to figure out the type.
pub fn native_else_wasm<E, R: Decode>() ->
ExecutionManager<
fn(
Result<NativeOrEncoded<R>, E>,
Result<NativeOrEncoded<R>, E>
) -> Result<NativeOrEncoded<R>, E>
>
{
ExecutionManager::NativeElseWasm
}
/// Evaluate to ExecutionManager::NativeWhenPossible, without having to figure out the type. /// Evaluate to ExecutionManager::NativeWhenPossible, without having to figure out the type.
pub fn always_wasm<E, R: Decode>() -> pub fn always_wasm<E, R: Decode>() ->
ExecutionManager< ExecutionManager<
@@ -258,18 +300,7 @@ where
exec, exec,
method, method,
call_data, call_data,
match strategy { strategy.get_manager(),
ExecutionStrategy::AlwaysWasm => ExecutionManager::AlwaysWasm,
ExecutionStrategy::NativeWhenPossible => ExecutionManager::NativeWhenPossible,
ExecutionStrategy::Both => ExecutionManager::Both(|wasm_result, native_result| {
warn!(
"Consensus error between wasm {:?} and native {:?}. Using wasm.",
wasm_result,
native_result
);
wasm_result
}),
},
true, true,
None, None,
) )
@@ -280,6 +311,119 @@ where
)) ))
} }
fn execute_aux<H, B, T, Exec, R: Decode + Encode + PartialEq,
NC: FnOnce() -> result::Result<R, &'static str> + UnwindSafe>(
overlay: &mut OverlayedChanges,
backend: &B,
changes_trie_storage: Option<&T>,
exec: &Exec,
method: &str,
call_data: &[u8],
compute_tx: bool,
use_native: bool,
native_call: Option<NC>,
) -> (Result<NativeOrEncoded<R>, Exec::Error>, bool, Option<B::Transaction>, Option<MemoryDB<H>>)
where
H: Hasher,
Exec: CodeExecutor<H>,
B: Backend<H>,
T: ChangesTrieStorage<H>,
H::Out: Ord + HeapSizeOf
{
let mut externalities = ext::Ext::new(overlay, backend, changes_trie_storage);
let (result, was_native) = exec.call(
&mut externalities,
method,
call_data,
use_native,
native_call,
);
let (storage_delta, changes_delta) = if compute_tx {
let (storage_delta, changes_delta) = externalities.transaction();
(Some(storage_delta), changes_delta)
} else {
(None, None)
};
(result, was_native, storage_delta, changes_delta)
}
fn execute_call_with_both_strategy<H, B, T, Exec, Handler, R: Decode + Encode + PartialEq,
NC: FnOnce() -> result::Result<R, &'static str> + UnwindSafe>(
overlay: &mut OverlayedChanges,
backend: &B,
changes_trie_storage: Option<&T>,
exec: &Exec,
method: &str,
call_data: &[u8],
compute_tx: bool,
mut native_call: Option<NC>,
orig_prospective: OverlayedChangeSet,
on_consensus_failure: Handler,
) -> (Result<NativeOrEncoded<R>, Exec::Error>, Option<B::Transaction>, Option<MemoryDB<H>>)
where
H: Hasher,
Exec: CodeExecutor<H>,
B: Backend<H>,
T: ChangesTrieStorage<H>,
H::Out: Ord + HeapSizeOf,
Handler: FnOnce(
Result<NativeOrEncoded<R>, Exec::Error>,
Result<NativeOrEncoded<R>, Exec::Error>
) -> Result<NativeOrEncoded<R>, Exec::Error>
{
let (result, was_native, storage_delta, changes_delta) = execute_aux(overlay, backend, changes_trie_storage,
exec, method, call_data, compute_tx, true, native_call.take());
if was_native {
overlay.prospective = orig_prospective.clone();
let (wasm_result, _, wasm_storage_delta, wasm_changes_delta) = execute_aux(overlay, backend, changes_trie_storage,
exec, method, call_data, compute_tx, false, native_call);
if (result.is_ok() && wasm_result.is_ok()
&& result.as_ref().ok() == wasm_result.as_ref().ok())
|| result.is_err() && wasm_result.is_err() {
(result, storage_delta, changes_delta)
} else {
(on_consensus_failure(wasm_result, result), wasm_storage_delta, wasm_changes_delta)
}
} else {
(result, storage_delta, changes_delta)
}
}
fn execute_call_with_native_else_wasm_strategy<H, B, T, Exec, R: Decode + Encode + PartialEq,
NC: FnOnce() -> result::Result<R, &'static str> + UnwindSafe>(
overlay: &mut OverlayedChanges,
backend: &B,
changes_trie_storage: Option<&T>,
exec: &Exec,
method: &str,
call_data: &[u8],
compute_tx: bool,
mut native_call: Option<NC>,
orig_prospective: OverlayedChangeSet,
) -> (Result<NativeOrEncoded<R>, Exec::Error>, Option<B::Transaction>, Option<MemoryDB<H>>)
where
H: Hasher,
Exec: CodeExecutor<H>,
B: Backend<H>,
T: ChangesTrieStorage<H>,
H::Out: Ord + HeapSizeOf,
{
let (result, was_native, storage_delta, changes_delta) = execute_aux(overlay, backend, changes_trie_storage,
exec, method, call_data, compute_tx, true, native_call.take());
if !was_native || result.is_ok() {
(result, storage_delta, changes_delta)
} else {
overlay.prospective = orig_prospective.clone();
let (wasm_result, _, wasm_storage_delta, wasm_changes_delta) = execute_aux(overlay, backend,
changes_trie_storage, exec, method, call_data, compute_tx, false, native_call);
(wasm_result, wasm_storage_delta, wasm_changes_delta)
}
}
/// Execute a call using the given state backend, overlayed changes, and call executor. /// Execute a call using the given state backend, overlayed changes, and call executor.
/// Produces a state-backend-specific "transaction" which can be used to apply the changes /// Produces a state-backend-specific "transaction" which can be used to apply the changes
/// to the backing store, such as the disk. /// to the backing store, such as the disk.
@@ -312,8 +456,6 @@ where
Result<NativeOrEncoded<R>, Exec::Error> Result<NativeOrEncoded<R>, Exec::Error>
) -> Result<NativeOrEncoded<R>, Exec::Error> ) -> Result<NativeOrEncoded<R>, Exec::Error>
{ {
let strategy: ExecutionStrategy = (&manager).into();
// read changes trie configuration. The reason why we're doing it here instead of the // read changes trie configuration. The reason why we're doing it here instead of the
// `OverlayedChanges` constructor is that we need proofs for this read as a part of // `OverlayedChanges` constructor is that we need proofs for this read as a part of
// proof-of-execution on light clients. And the proof is recorded by the backend which // proof-of-execution on light clients. And the proof is recorded by the backend which
@@ -331,71 +473,31 @@ where
let result = { let result = {
let orig_prospective = overlay.prospective.clone(); let orig_prospective = overlay.prospective.clone();
let (result, was_native, storage_delta, changes_delta) = { let (result, storage_delta, changes_delta) = match manager {
let ((result, was_native), (storage_delta, changes_delta)) = { ExecutionManager::Both(on_consensus_failure) => {
let mut externalities = ext::Ext::new(overlay, backend, changes_trie_storage); execute_call_with_both_strategy(overlay, backend, changes_trie_storage,
let retval = exec.call( exec, method, call_data, compute_tx, native_call.take(),
&mut externalities, orig_prospective, on_consensus_failure)
method, },
call_data, ExecutionManager::NativeElseWasm => {
// attempt to run native first, if we're not directed to run wasm only execute_call_with_native_else_wasm_strategy(overlay, backend, changes_trie_storage,
strategy != ExecutionStrategy::AlwaysWasm, exec, method, call_data, compute_tx, native_call.take(), orig_prospective)
native_call.take(), },
); ExecutionManager::AlwaysWasm => {
let (storage_delta, changes_delta) = if compute_tx { let (result, _, storage_delta, changes_delta) = execute_aux(overlay, backend, changes_trie_storage,
let (storage_delta, changes_delta) = externalities.transaction(); exec, method, call_data, compute_tx, false, native_call);
(Some(storage_delta), changes_delta)
} else {
(None, None)
};
(retval, (storage_delta, changes_delta))
};
(result, was_native, storage_delta, changes_delta)
};
// run wasm separately if we did run native the first time and we're meant to run both
let (result, storage_delta, changes_delta) = if let (true, ExecutionManager::Both(on_consensus_failure)) =
(was_native, manager)
{
overlay.prospective = orig_prospective.clone();
let (wasm_result, wasm_storage_delta, wasm_changes_delta) = {
let ((result, _), (storage_delta, changes_delta)) = {
let mut externalities = ext::Ext::new(overlay, backend, changes_trie_storage);
let retval = exec.call(
&mut externalities,
method,
call_data,
false,
native_call,
);
let (storage_delta, changes_delta) = if compute_tx {
let (storage_delta, changes_delta) = externalities.transaction();
(Some(storage_delta), changes_delta)
} else {
(None, None)
};
(retval, (storage_delta, changes_delta))
};
(result, storage_delta, changes_delta) (result, storage_delta, changes_delta)
}; },
ExecutionManager::NativeWhenPossible => {
if (result.is_ok() && wasm_result.is_ok() let (result, _was_native, storage_delta, changes_delta) = execute_aux(overlay, backend, changes_trie_storage,
&& result.as_ref().ok() == wasm_result.as_ref().ok()) exec, method, call_data, compute_tx, true, native_call);
|| result.is_err() && wasm_result.is_err() {
(result, storage_delta, changes_delta) (result, storage_delta, changes_delta)
} else { },
// Consensus error.
(on_consensus_failure(wasm_result, result), wasm_storage_delta, wasm_changes_delta)
}
} else {
(result, storage_delta, changes_delta)
}; };
result.map(move |out| (out, storage_delta, changes_delta)) result.map(move |out| (out, storage_delta, changes_delta))
}; };
// ensure that changes trie config has not been changed
if result.is_ok() { if result.is_ok() {
init_overlay(overlay, true)?; init_overlay(overlay, true)?;
} }
@@ -454,7 +556,7 @@ where
exec, exec,
method, method,
call_data, call_data,
native_when_possible(), native_else_wasm(),
false, false,
None, None,
)?; )?;
@@ -502,7 +604,7 @@ where
exec, exec,
method, method,
call_data, call_data,
native_when_possible(), native_else_wasm(),
false, false,
None, None,
).map(|(result, _, _)| result.into_encoded()) ).map(|(result, _, _)| result.into_encoded())
@@ -684,6 +786,25 @@ mod tests {
).unwrap().0, vec![66]); ).unwrap().0, vec![66]);
} }
#[test]
fn execute_works_with_native_else_wasm() {
assert_eq!(execute(
&trie_backend::tests::test_trie(),
Some(&InMemoryChangesTrieStorage::new()),
&mut Default::default(),
&DummyCodeExecutor {
change_changes_trie_config: false,
native_available: true,
native_succeeds: true,
fallback_succeeds: true,
},
"test",
&[],
ExecutionStrategy::NativeElseWasm
).unwrap().0, vec![66]);
}
#[test] #[test]
fn dual_execution_strategy_detects_consensus_failure() { fn dual_execution_strategy_detects_consensus_failure() {
let mut consensus_failed = false; let mut consensus_failed = false;
@@ -820,4 +941,22 @@ mod tests {
ExecutionStrategy::NativeWhenPossible ExecutionStrategy::NativeWhenPossible
).is_err()); ).is_err());
} }
#[test]
fn cannot_change_changes_trie_config_with_native_else_wasm() {
assert!(execute(
&trie_backend::tests::test_trie(),
Some(&InMemoryChangesTrieStorage::new()),
&mut Default::default(),
&DummyCodeExecutor {
change_changes_trie_config: true,
native_available: false,
native_succeeds: true,
fallback_succeeds: true,
},
"test",
&[],
ExecutionStrategy::NativeElseWasm
).is_err());
}
} }
+11 -4
View File
@@ -25,6 +25,7 @@ mod block_builder_ext;
pub use client_ext::TestClient; pub use client_ext::TestClient;
pub use block_builder_ext::BlockBuilderExt; pub use block_builder_ext::BlockBuilderExt;
pub use client; pub use client;
pub use client::ExecutionStrategies;
pub use client::blockchain; pub use client::blockchain;
pub use client::backend; pub use client::backend;
pub use executor::NativeExecutor; pub use executor::NativeExecutor;
@@ -72,19 +73,25 @@ pub fn new() -> client::Client<Backend, Executor, runtime::Block, runtime::Runti
} }
/// Creates new client instance used for tests with the given api execution strategy. /// Creates new client instance used for tests with the given api execution strategy.
pub fn new_with_api_execution_strat( pub fn new_with_execution_strategy(
api_execution_strategy: ExecutionStrategy execution_strategy: ExecutionStrategy
) -> client::Client<Backend, Executor, runtime::Block, runtime::RuntimeApi> { ) -> client::Client<Backend, Executor, runtime::Block, runtime::RuntimeApi> {
let backend = Arc::new(Backend::new()); let backend = Arc::new(Backend::new());
let executor = NativeExecutor::new(None); let executor = NativeExecutor::new(None);
let executor = LocalCallExecutor::new(backend.clone(), executor); let executor = LocalCallExecutor::new(backend.clone(), executor);
let execution_strategies = ExecutionStrategies {
syncing: execution_strategy,
importing: execution_strategy,
block_construction: execution_strategy,
other: execution_strategy,
};
client::Client::new( client::Client::new(
backend, backend,
executor, executor,
genesis_storage(false), genesis_storage(false),
ExecutionStrategy::NativeWhenPossible, execution_strategies
api_execution_strategy
).expect("Creates new client") ).expect("Creates new client")
} }
+21 -2
View File
@@ -37,8 +37,8 @@ use runtime_primitives::{
create_runtime_str, create_runtime_str,
traits::{ traits::{
BlindCheckable, BlakeTwo256, Block as BlockT, Extrinsic as ExtrinsicT, BlindCheckable, BlakeTwo256, Block as BlockT, Extrinsic as ExtrinsicT,
GetNodeBlockType, GetRuntimeBlockType GetNodeBlockType, GetRuntimeBlockType,
} },
}; };
use runtime_version::RuntimeVersion; use runtime_version::RuntimeVersion;
pub use primitives::hash::H256; pub use primitives::hash::H256;
@@ -215,6 +215,8 @@ cfg_if! {
fn function_signature_changed() -> Vec<u64>; fn function_signature_changed() -> Vec<u64>;
/// The new signature. /// The new signature.
fn function_signature_changed() -> u64; fn function_signature_changed() -> u64;
fn fail_on_native() -> u64;
fn fail_on_wasm() -> u64;
} }
} }
} else { } else {
@@ -233,6 +235,8 @@ cfg_if! {
fn fail_convert_return_value() -> DecodeFails<Block>; fn fail_convert_return_value() -> DecodeFails<Block>;
/// In wasm we just emulate the old behavior. /// In wasm we just emulate the old behavior.
fn function_signature_changed() -> Vec<u64>; fn function_signature_changed() -> Vec<u64>;
fn fail_on_native() -> u64;
fn fail_on_wasm() -> u64;
} }
} }
} }
@@ -327,6 +331,13 @@ cfg_if! {
fn function_signature_changed() -> u64 { fn function_signature_changed() -> u64 {
1 1
} }
fn fail_on_native() -> u64 {
panic!("Failing because we are on native")
}
fn fail_on_wasm() -> u64 {
1
}
} }
impl consensus_aura::AuraApi<Block> for Runtime { impl consensus_aura::AuraApi<Block> for Runtime {
@@ -414,6 +425,14 @@ cfg_if! {
vec.push(2); vec.push(2);
vec vec
} }
fn fail_on_native() -> u64 {
1
}
fn fail_on_wasm() -> u64 {
panic!("Failing because we are on wasm")
}
} }
impl consensus_aura::AuraApi<Block> for Runtime { impl consensus_aura::AuraApi<Block> for Runtime {
+1 -1
View File
@@ -65,7 +65,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
impl_name: create_runtime_str!("substrate-node"), impl_name: create_runtime_str!("substrate-node"),
authoring_version: 10, authoring_version: 10,
spec_version: 26, spec_version: 26,
impl_version: 26, impl_version: 27,
apis: RUNTIME_API_VERSIONS, apis: RUNTIME_API_VERSIONS,
}; };