diff --git a/Cargo.lock b/Cargo.lock index e67e0e365bf68..ae14b6aca6446 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3622,7 +3622,6 @@ dependencies = [ "sr-staking-primitives 2.0.0", "sr-std 2.0.0", "substrate-application-crypto 2.0.0", - "substrate-offchain 2.0.0", "substrate-primitives 2.0.0", ] @@ -5138,7 +5137,6 @@ dependencies = [ "sr-std 2.0.0", "substrate-application-crypto 2.0.0", "substrate-inherents 2.0.0", - "substrate-offchain 2.0.0", "substrate-primitives 2.0.0", ] @@ -5467,6 +5465,7 @@ dependencies = [ "substrate-client-db 2.0.0", "substrate-consensus-common 2.0.0", "substrate-executor 2.0.0", + "substrate-externalities 2.0.0", "substrate-header-metadata 2.0.0", "substrate-inherents 2.0.0", "substrate-keyring 2.0.0", @@ -5503,6 +5502,7 @@ dependencies = [ "substrate-client-db 2.0.0", "substrate-consensus-common 2.0.0", "substrate-executor 2.0.0", + "substrate-externalities 2.0.0", "substrate-header-metadata 2.0.0", "substrate-inherents 2.0.0", "substrate-keyring 2.0.0", diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 7018ed10eccb5..36ab0f21a110d 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -472,7 +472,7 @@ impl system::offchain::CreateTransaction for Runtim type Public = ::Signer; type Signature = Signature; - fn create_transaction>( + fn create_transaction>( call: Call, public: Self::Public, account: AccountId, @@ -491,7 +491,7 @@ impl system::offchain::CreateTransaction for Runtim Default::default(), ); let raw_payload = SignedPayload::new(call, extra).ok()?; - let signature = F::sign(public, &raw_payload)?; + let signature = TSigner::sign(public, &raw_payload)?; let address = Indices::unlookup(account); let (call, extra, _) = raw_payload.deconstruct(); Some((call, (address, signature, extra))) diff --git a/client/Cargo.toml b/client/Cargo.toml index 194d3689cb9ee..94745473d0905 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -5,13 +5,13 @@ authors = ["Parity Technologies "] edition = "2018" [dependencies] -client-api = { package = "substrate-client-api", path = "api" } block-builder = { package = "substrate-block-builder", path = "block-builder" } +client-api = { package = "substrate-client-api", path = "api" } codec = { package = "parity-scale-codec", version = "1.0.0", features = ["derive"] } consensus = { package = "substrate-consensus-common", path = "../primitives/consensus/common" } -kvdb = { git = "https://github.com/paritytech/parity-common", rev="b0317f649ab2c665b7987b8475878fc4d2e1f81d" } derive_more = { version = "0.15.0" } executor = { package = "substrate-executor", path = "executor" } +externalities = { package = "substrate-externalities", path = "../primitives/externalities" } fnv = { version = "1.0.6" } futures = { version = "0.1.29" } futures03 = { package = "futures-preview", version = "0.3.0-alpha.19", features = ["compat"] } @@ -20,6 +20,7 @@ header-metadata = { package = "substrate-header-metadata", path = "header-metada hex-literal = { version = "0.2.1" } inherents = { package = "substrate-inherents", path = "../primitives/inherents" } keyring = { package = "substrate-keyring", path = "../primitives/keyring" } +kvdb = { git = "https://github.com/paritytech/parity-common", rev="b0317f649ab2c665b7987b8475878fc4d2e1f81d" } log = { version = "0.4.8" } parking_lot = { version = "0.9.0" } primitives = { package = "substrate-primitives", path = "../primitives/core" } diff --git a/client/api/Cargo.toml b/client/api/Cargo.toml index 9a122ad0f8591..243fdf0c41723 100644 --- a/client/api/Cargo.toml +++ b/client/api/Cargo.toml @@ -10,6 +10,7 @@ codec = { package = "parity-scale-codec", version = "1.0.0", default-features = consensus = { package = "substrate-consensus-common", path = "../../primitives/consensus/common" } derive_more = { version = "0.15.0" } executor = { package = "substrate-executor", path = "../executor" } +externalities = { package = "substrate-externalities", path = "../../primitives/externalities" } fnv = { version = "1.0.6" } futures = { version = "0.1.29" } futures03 = { package = "futures-preview", version = "0.3.0-alpha.19", features = ["compat"] } diff --git a/client/api/src/backend.rs b/client/api/src/backend.rs index 03cd183ad2c21..a00c5a3ac89f5 100644 --- a/client/api/src/backend.rs +++ b/client/api/src/backend.rs @@ -19,6 +19,7 @@ use std::sync::Arc; use std::collections::HashMap; use primitives::ChangesTrieConfiguration; +use primitives::offchain::OffchainStorage; use sr_primitives::{generic::BlockId, Justification, StorageOverlay, ChildrenStorageOverlay}; use sr_primitives::traits::{Block as BlockT, NumberFor}; use state_machine::backend::Backend as StateBackend; @@ -27,7 +28,6 @@ use crate::{ blockchain::{ Backend as BlockchainBackend, well_known_cache_keys }, - offchain::OffchainStorage, error, light::RemoteBlockchain, }; @@ -41,12 +41,22 @@ pub type StorageCollection = Vec<(Vec, Option>)>; /// In memory arrays of storage values for multiple child tries. pub type ChildStorageCollection = Vec<(Vec, StorageCollection)>; +/// Import operation summary. +/// +/// Contains information about the block that just got imported, +/// including storage changes, reorged blocks, etc. pub struct ImportSummary { + /// Block hash of the imported block. pub hash: Block::Hash, + /// Import origin. pub origin: BlockOrigin, + /// Header of the imported block. pub header: Block::Header, + /// Is this block a new best block. pub is_new_best: bool, + /// Optional storage changes. pub storage_changes: Option<(StorageCollection, ChildStorageCollection)>, + /// Blocks that got retracted because of this one got imported. pub retracted: Vec, } @@ -56,8 +66,11 @@ pub struct ClientImportOperation< H: Hasher, B: Backend, > { + /// DB Operation. pub op: B::BlockImportOperation, + /// Summary of imported block. pub notify_imported: Option>, + /// A list of hashes of blocks that got finalized. pub notify_finalized: Vec, } diff --git a/client/api/src/call_executor.rs b/client/api/src/call_executor.rs index c143999047d5e..73273336330ae 100644 --- a/client/api/src/call_executor.rs +++ b/client/api/src/call_executor.rs @@ -14,6 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . +//! A method call executor interface. + use std::{cmp::Ord, panic::UnwindSafe, result, cell::RefCell}; use codec::{Encode, Decode}; use sr_primitives::{ @@ -24,8 +26,9 @@ use state_machine::{ ChangesTrieTransaction, StorageProof, }; use executor::{RuntimeVersion, NativeVersion}; +use externalities::Extensions; use hash_db::Hasher; -use primitives::{offchain::OffchainExt, Blake2Hasher, NativeOrEncoded}; +use primitives::{Blake2Hasher, NativeOrEncoded}; use sr_api::{ProofRecorder, InitializeBlock}; use crate::error; @@ -49,7 +52,7 @@ where method: &str, call_data: &[u8], strategy: ExecutionStrategy, - side_effects_handler: Option, + extensions: Option, ) -> Result, error::Error>; /// Execute a contextual call on top of state in a block of a given hash. @@ -76,9 +79,8 @@ where initialize_block: InitializeBlock<'a, B>, execution_manager: ExecutionManager, native_call: Option, - side_effects_handler: Option, proof_recorder: &Option>, - enable_keystore: bool, + extensions: Option, ) -> error::Result> where ExecutionManager: Clone; /// Extract RuntimeVersion of given block @@ -104,7 +106,7 @@ where call_data: &[u8], manager: ExecutionManager, native_call: Option, - side_effects_handler: Option, + extensions: Option, ) -> Result< ( NativeOrEncoded, diff --git a/client/api/src/client.rs b/client/api/src/client.rs index a51fc9f03f875..675a53974c8d8 100644 --- a/client/api/src/client.rs +++ b/client/api/src/client.rs @@ -14,10 +14,11 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . +//! A set of APIs supported by the client along with their primitives. + use std::collections::HashMap; use futures03::channel::mpsc; use primitives::storage::StorageKey; -use state_machine::ExecutionStrategy; use sr_primitives::{ traits::{Block as BlockT, NumberFor}, generic::BlockId @@ -39,33 +40,6 @@ pub type FinalityNotifications = mpsc::UnboundedReceiver = Option, ::Hash>>; -/// 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 for offchain workers. - pub offchain_worker: 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, - offchain_worker: ExecutionStrategy::NativeWhenPossible, - other: ExecutionStrategy::NativeElseWasm, - } - } -} - /// Figure out the block type for a given type (for now, just a `Client`). pub trait BlockOf { /// The type of the block. diff --git a/client/api/src/execution_extensions.rs b/client/api/src/execution_extensions.rs new file mode 100644 index 0000000000000..83d70998e16aa --- /dev/null +++ b/client/api/src/execution_extensions.rs @@ -0,0 +1,184 @@ +// Copyright 2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Execution extensions for runtime calls. +//! +//! This module is responsible for defining the execution +//! strategy for the runtime calls and provide the right `Externalities` +//! extensions to support APIs for particular execution context & capabilities. + +use std::sync::{Weak, Arc}; +use codec::Decode; +use primitives::{ + ExecutionContext, + offchain::{self, OffchainExt, TransactionPoolExt}, + traits::{BareCryptoStorePtr, KeystoreExt}, +}; +use sr_primitives::{ + generic::BlockId, + traits, + offchain::{TransactionPool}, +}; +use state_machine::{ExecutionStrategy, ExecutionManager, DefaultHandler}; +use externalities::Extensions; +use parking_lot::RwLock; + +/// 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 for offchain workers. + pub offchain_worker: 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, + offchain_worker: ExecutionStrategy::NativeWhenPossible, + other: ExecutionStrategy::NativeElseWasm, + } + } +} + +/// A producer of execution extensions for offchain calls. +/// +/// This crate aggregates extensions available for the offchain calls +/// and is responsbile to produce a right `Extensions` object +/// for each call, based on required `Capabilities`. +pub struct ExecutionExtensions { + strategies: ExecutionStrategies, + keystore: Option, + transaction_pool: RwLock>>>, +} + +impl Default for ExecutionExtensions { + fn default() -> Self { + Self { + strategies: Default::default(), + keystore: None, + transaction_pool: RwLock::new(None), + } + } +} + +impl ExecutionExtensions { + /// Create new `ExecutionExtensions` given a `keystore` and `ExecutionStrategies`. + pub fn new( + strategies: ExecutionStrategies, + keystore: Option, + ) -> Self { + let transaction_pool = RwLock::new(None); + Self { strategies, keystore, transaction_pool } + } + + /// Get a reference to the execution strategies. + pub fn strategies(&self) -> &ExecutionStrategies { + &self.strategies + } + + /// Register transaction pool extension. + /// + /// To break retain cycle between `Client` and `TransactionPool` we require this + /// extension to be a `Weak` reference. + /// That's also the reason why it's being registered lazily instead of + /// during initialisation. + pub fn register_transaction_pool(&self, pool: Weak>) { + *self.transaction_pool.write() = Some(pool); + } + + /// Create `ExecutionManager` and `Extensions` for given offchain call. + /// + /// Based on the execution context and capabilities it produces + /// the right manager and extensions object to support desired set of APIs. + pub fn manager_and_extensions( + &self, + at: &BlockId, + context: ExecutionContext, + ) -> ( + ExecutionManager>, + Extensions, + ) { + let manager = match context { + ExecutionContext::BlockConstruction => + self.strategies.block_construction.get_manager(), + ExecutionContext::Syncing => + self.strategies.syncing.get_manager(), + ExecutionContext::Importing => + self.strategies.importing.get_manager(), + ExecutionContext::OffchainCall(Some((_, capabilities))) if capabilities.has_all() => + self.strategies.offchain_worker.get_manager(), + ExecutionContext::OffchainCall(_) => + self.strategies.other.get_manager(), + }; + + let capabilities = context.capabilities(); + + let mut extensions = Extensions::new(); + + if capabilities.has(offchain::Capability::Keystore) { + if let Some(keystore) = self.keystore.as_ref() { + extensions.register(KeystoreExt(keystore.clone())); + } + } + + if capabilities.has(offchain::Capability::TransactionPool) { + if let Some(pool) = self.transaction_pool.read().as_ref().and_then(|x| x.upgrade()) { + extensions.register(TransactionPoolExt(Box::new(TransactionPoolAdapter { + at: *at, + pool, + }) as _)); + } + } + + if let ExecutionContext::OffchainCall(Some(ext)) = context { + extensions.register( + OffchainExt::new(offchain::LimitedExternalities::new(capabilities, ext.0)) + ) + } + + (manager, extensions) + } +} + +/// A wrapper type to pass `BlockId` to the actual transaction pool. +struct TransactionPoolAdapter { + at: BlockId, + pool: Arc>, +} + +impl offchain::TransactionPool for TransactionPoolAdapter { + fn submit_transaction(&mut self, data: Vec) -> Result<(), ()> { + let xt = match Block::Extrinsic::decode(&mut &*data) { + Ok(xt) => xt, + Err(e) => { + log::warn!("Unable to decode extrinsic: {:?}: {}", data, e.what()); + return Err(()); + }, + }; + + self.pool.submit_at(&self.at, xt) + } +} diff --git a/client/api/src/lib.rs b/client/api/src/lib.rs index 4163320546db4..04abc86308545 100644 --- a/client/api/src/lib.rs +++ b/client/api/src/lib.rs @@ -15,25 +15,25 @@ // along with Substrate. If not, see . //! Substrate client interfaces. +#![warn(missing_docs)] -// TODO: make internal -pub mod error; pub mod backend; pub mod blockchain; -pub mod light; -pub mod notifications; pub mod call_executor; pub mod client; -pub mod offchain; +pub mod error; +pub mod execution_extensions; +pub mod light; +pub mod notifications; -pub use error::*; +// TODO: avoid re-exports pub use backend::*; pub use blockchain::*; -pub use light::*; -pub use notifications::*; pub use call_executor::*; -pub use offchain::*; pub use client::*; +pub use error::*; +pub use light::*; +pub use notifications::*; pub use state_machine::{StorageProof, ExecutionStrategy}; diff --git a/client/cli/src/lib.rs b/client/cli/src/lib.rs index 696a6eaeae0d2..aa60c1d7d8074 100644 --- a/client/cli/src/lib.rs +++ b/client/cli/src/lib.rs @@ -26,7 +26,7 @@ mod execution_strategy; pub mod error; pub mod informant; -use client_api::ExecutionStrategies; +use client_api::execution_extensions::ExecutionStrategies; use service::{ config::{Configuration, DatabaseConfig}, ServiceBuilderExport, ServiceBuilderImport, ServiceBuilderRevert, diff --git a/client/db/src/lib.rs b/client/db/src/lib.rs index 90d8aadccc1ae..155d2aaeab7b5 100644 --- a/client/db/src/lib.rs +++ b/client/db/src/lib.rs @@ -39,11 +39,12 @@ use std::path::PathBuf; use std::io; use std::collections::{HashMap, HashSet}; +use client_api::ForkBlocks; use client_api::backend::NewBlockState; -use client_api::blockchain::{well_known_cache_keys, HeaderBackend}; -use client_api::{ForkBlocks, ExecutionStrategies}; use client_api::backend::{StorageCollection, ChildStorageCollection}; +use client_api::blockchain::{well_known_cache_keys, HeaderBackend}; use client_api::error::{Result as ClientResult, Error as ClientError}; +use client_api::execution_extensions::ExecutionExtensions; use codec::{Decode, Encode}; use hash_db::{Hasher, Prefix}; use kvdb::{KeyValueDB, DBTransaction}; @@ -224,8 +225,7 @@ pub fn new_client( executor: E, genesis_storage: S, fork_blocks: ForkBlocks, - execution_strategies: ExecutionStrategies, - keystore: Option, + execution_extensions: ExecutionExtensions, ) -> Result<( client::Client< Backend, @@ -243,9 +243,9 @@ pub fn new_client( S: BuildStorage, { let backend = Arc::new(Backend::new(settings, CANONICALIZATION_DELAY)?); - let executor = client::LocalCallExecutor::new(backend.clone(), executor, keystore); + let executor = client::LocalCallExecutor::new(backend.clone(), executor); Ok(( - client::Client::new(backend.clone(), executor, genesis_storage, fork_blocks, execution_strategies)?, + client::Client::new(backend.clone(), executor, genesis_storage, fork_blocks, execution_extensions)?, backend, )) } diff --git a/client/db/src/offchain.rs b/client/db/src/offchain.rs index 09578f5f6b1d0..0435a1c865bda 100644 --- a/client/db/src/offchain.rs +++ b/client/db/src/offchain.rs @@ -56,7 +56,7 @@ impl LocalStorage { } } -impl client_api::OffchainStorage for LocalStorage { +impl primitives::offchain::OffchainStorage for LocalStorage { fn set(&mut self, prefix: &[u8], key: &[u8], value: &[u8]) { let key: Vec = prefix.iter().chain(key).cloned().collect(); let mut tx = self.db.transaction(); @@ -117,7 +117,7 @@ impl client_api::OffchainStorage for LocalStorage { #[cfg(test)] mod tests { use super::*; - use client_api::OffchainStorage; + use primitives::offchain::OffchainStorage; #[test] fn should_compare_and_set_and_clear_the_locks_map() { diff --git a/client/executor/src/integration_tests/mod.rs b/client/executor/src/integration_tests/mod.rs index 71df0b8d8cc9e..32424c6065ef7 100644 --- a/client/executor/src/integration_tests/mod.rs +++ b/client/executor/src/integration_tests/mod.rs @@ -19,12 +19,12 @@ mod sandbox; use codec::{Encode, Decode}; use hex_literal::hex; use primitives::{ - Blake2Hasher, blake2_128, blake2_256, ed25519, sr25519, map, Pair, offchain::OffchainExt, + Blake2Hasher, blake2_128, blake2_256, ed25519, sr25519, map, Pair, + offchain::{OffchainExt, testing}, traits::Externalities, }; use runtime_test::WASM_BINARY; use state_machine::TestExternalities as CoreTestExternalities; -use substrate_offchain::testing; use test_case::test_case; use trie::{TrieConfiguration, trie_types::Layout}; @@ -401,7 +401,7 @@ fn ordered_trie_root_should_work(wasm_method: WasmExecutionMethod) { #[test_case(WasmExecutionMethod::Interpreted)] #[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] fn offchain_local_storage_should_work(wasm_method: WasmExecutionMethod) { - use client_api::OffchainStorage; + use primitives::offchain::OffchainStorage; let mut ext = TestExternalities::default(); let (offchain, state) = testing::TestOffchainExt::new(); diff --git a/client/offchain/Cargo.toml b/client/offchain/Cargo.toml index 29621d8502bc1..c6de81317fa9c 100644 --- a/client/offchain/Cargo.toml +++ b/client/offchain/Cargo.toml @@ -23,7 +23,6 @@ parking_lot = "0.9.0" primitives = { package = "substrate-primitives", path = "../../primitives/core" } rand = "0.7.2" sr-primitives = { path = "../../primitives/sr-primitives" } -transaction_pool = { package = "substrate-transaction-pool", path = "../transaction-pool" } network = { package = "substrate-network", path = "../network" } keystore = { package = "substrate-keystore", path = "../keystore" } @@ -32,10 +31,11 @@ hyper = "0.12.35" hyper-rustls = "0.17.1" [dev-dependencies] -env_logger = "0.7.0" client-db = { package = "substrate-client-db", path = "../db/", default-features = true } +env_logger = "0.7.0" test-client = { package = "substrate-test-runtime-client", path = "../../test/utils/runtime/client" } tokio = "0.1.22" +transaction_pool = { package = "substrate-transaction-pool", path = "../transaction-pool" } [features] default = [] diff --git a/client/offchain/src/api.rs b/client/offchain/src/api.rs index 25d4fc267ae4d..ff2a5a433a354 100644 --- a/client/offchain/src/api.rs +++ b/client/offchain/src/api.rs @@ -21,9 +21,9 @@ use std::{ thread::sleep, }; -use client_api::OffchainStorage; -use futures::{StreamExt as _, Future, FutureExt as _, future, channel::mpsc}; -use log::{info, debug, warn, error}; +use primitives::offchain::OffchainStorage; +use futures::Future; +use log::error; use network::{PeerId, Multiaddr, NetworkStateInfo}; use codec::{Encode, Decode}; use primitives::offchain::{ @@ -31,8 +31,6 @@ use primitives::offchain::{ OpaqueNetworkState, OpaquePeerId, OpaqueMultiaddr, StorageKind, }; pub use offchain_primitives::STORAGE_PREFIX; -use sr_primitives::{generic::BlockId, traits::{self, Extrinsic}}; -use transaction_pool::txpool::{Pool, ChainApi}; #[cfg(not(target_os = "unknown"))] mod http; @@ -44,19 +42,14 @@ mod http_dummy; mod timestamp; -/// A message between the offchain extension and the processing thread. -enum ExtMessage { - SubmitExtrinsic(Vec), -} - /// Asynchronous offchain API. /// /// NOTE this is done to prevent recursive calls into the runtime (which are not supported currently). -pub(crate) struct Api { - sender: mpsc::UnboundedSender, +pub(crate) struct Api { + /// Offchain Workers database. db: Storage, + /// A NetworkState provider. network_state: Arc, - _at: BlockId, /// Is this node a potential validator? is_validator: bool, /// Everything HTTP-related is handled by a different struct. @@ -73,22 +66,11 @@ fn unavailable_yet(name: &str) -> R { const LOCAL_DB: &str = "LOCAL (fork-aware) DB"; -impl OffchainExt for Api -where - Storage: OffchainStorage, - Block: traits::Block, -{ +impl OffchainExt for Api { fn is_validator(&self) -> bool { self.is_validator } - fn submit_transaction(&mut self, ext: Vec) -> Result<(), ()> { - self.sender - .unbounded_send(ExtMessage::SubmitExtrinsic(ext)) - .map(|_| ()) - .map_err(|_| ()) - } - fn network_state(&self) -> Result { let external_addresses = self.network_state.external_addresses(); @@ -260,40 +242,28 @@ impl TryFrom for NetworkState { /// Offchain extensions implementation API /// /// This is the asynchronous processing part of the API. -pub(crate) struct AsyncApi { - receiver: Option>, - transaction_pool: Arc>, - at: BlockId, +pub(crate) struct AsyncApi { /// Everything HTTP-related is handled by a different struct. http: Option, } -impl AsyncApi { +impl AsyncApi { /// Creates new Offchain extensions API implementation an the asynchronous processing part. pub fn new( - transaction_pool: Arc>, db: S, - at: BlockId, network_state: Arc, is_validator: bool, - ) -> (Api, AsyncApi) { - let (sender, rx) = mpsc::unbounded(); - + ) -> (Api, AsyncApi) { let (http_api, http_worker) = http::http(); let api = Api { - sender, db, network_state, - _at: at, is_validator, http: http_api, }; let async_api = AsyncApi { - receiver: Some(rx), - transaction_pool, - at, http: Some(http_worker), }; @@ -302,35 +272,9 @@ impl AsyncApi { /// Run a processing task for the API pub fn process(mut self) -> impl Future { - let receiver = self.receiver.take().expect("Take invoked only once."); let http = self.http.take().expect("Take invoked only once."); - let extrinsics = receiver.for_each(move |msg| { - match msg { - ExtMessage::SubmitExtrinsic(ext) => self.submit_extrinsic(ext), - } - }); - - future::join(extrinsics, http) - .map(|((), ())| ()) - } - - fn submit_extrinsic(&mut self, ext: Vec) -> impl Future { - let xt = match ::Extrinsic::decode(&mut &*ext) { - Ok(xt) => xt, - Err(e) => { - warn!("Unable to decode extrinsic: {:?}: {}", ext, e.what()); - return future::Either::Left(future::ready(())) - }, - }; - - info!("Submitting transaction to the pool: {:?} (isSigned: {:?})", xt, xt.is_signed()); - future::Either::Right(self.transaction_pool - .submit_one(&self.at, xt.clone()) - .map(|result| match result { - Ok(hash) => { debug!("[{:?}] Offchain transaction added to the pool.", hash); }, - Err(e) => { warn!("Couldn't submit offchain transaction: {:?}", e); }, - })) + http } } @@ -338,10 +282,8 @@ impl AsyncApi { mod tests { use super::*; use std::{convert::{TryFrom, TryInto}, time::SystemTime}; - use sr_primitives::traits::Zero; use client_db::offchain::LocalStorage; use network::PeerId; - use test_client::runtime::Block; struct MockNetworkStateInfo(); @@ -355,19 +297,13 @@ mod tests { } } - fn offchain_api() -> (Api, AsyncApi) { + fn offchain_api() -> (Api, AsyncApi) { let _ = env_logger::try_init(); let db = LocalStorage::new_test(); - let client = Arc::new(test_client::new()); - let pool = Arc::new( - Pool::new(Default::default(), transaction_pool::FullChainApi::new(client.clone())) - ); - let mock = Arc::new(MockNetworkStateInfo()); + AsyncApi::new( - pool, db, - BlockId::Number(Zero::zero()), mock, false, ) diff --git a/client/offchain/src/lib.rs b/client/offchain/src/lib.rs index e253a6d9234fb..a1db2456bd9e7 100644 --- a/client/offchain/src/lib.rs +++ b/client/offchain/src/lib.rs @@ -41,15 +41,11 @@ use sr_api::ApiExt; use futures::future::Future; use log::{debug, warn}; use network::NetworkStateInfo; -use primitives::{offchain, ExecutionContext}; +use primitives::{offchain::{self, OffchainStorage}, ExecutionContext}; use sr_primitives::{generic::BlockId, traits::{self, ProvideRuntimeApi}}; -use transaction_pool::txpool::{Pool, ChainApi}; -use client_api::{OffchainStorage}; mod api; -pub mod testing; - pub use offchain_primitives::{OffchainWorkerApi, STORAGE_PREFIX}; /// An offchain workers manager. @@ -94,13 +90,12 @@ impl OffchainWorkers< { /// Start the offchain workers after given block. #[must_use] - pub fn on_block_imported( + pub fn on_block_imported( &self, number: &::Number, - pool: &Arc>, network_state: Arc, is_validator: bool, - ) -> impl Future where A: ChainApi + 'static { + ) -> impl Future { let runtime = self.client.runtime_api(); let at = BlockId::number(*number); let has_api = runtime.has_api::>(&at); @@ -108,9 +103,7 @@ impl OffchainWorkers< if has_api.unwrap_or(false) { let (api, runner) = api::AsyncApi::new( - pool.clone(), self.db.clone(), - at.clone(), network_state.clone(), is_validator, ); @@ -153,6 +146,8 @@ impl OffchainWorkers< mod tests { use super::*; use network::{Multiaddr, PeerId}; + use std::sync::Arc; + use transaction_pool::txpool::Pool; struct MockNetworkStateInfo(); @@ -171,13 +166,18 @@ mod tests { // given let _ = env_logger::try_init(); let client = Arc::new(test_client::new()); - let pool = Arc::new(Pool::new(Default::default(), transaction_pool::FullChainApi::new(client.clone()))); + let pool = Arc::new(Pool::new( + Default::default(), + transaction_pool::FullChainApi::new(client.clone()) + )); + client.execution_extensions() + .register_transaction_pool(Arc::downgrade(&pool.clone()) as _); let db = client_db::offchain::LocalStorage::new_test(); let network_state = Arc::new(MockNetworkStateInfo()); // when let offchain = OffchainWorkers::new(client, db); - futures::executor::block_on(offchain.on_block_imported(&0u64, &pool, network_state, false)); + futures::executor::block_on(offchain.on_block_imported(&0u64, network_state, false)); // then assert_eq!(pool.status().ready, 1); diff --git a/client/service/src/builder.rs b/client/service/src/builder.rs index 9d2b1ebb8f1aa..3d7359fa1bebc 100644 --- a/client/service/src/builder.rs +++ b/client/service/src/builder.rs @@ -193,13 +193,17 @@ where TGen: RuntimeGenesis, TCSExt: Extension { }, }; + let extensions = client_api::execution_extensions::ExecutionExtensions::new( + config.execution_strategies.clone(), + Some(keystore.clone()), + ); + client_db::new_client( db_config, executor, &config.chain_spec, fork_blocks, - config.execution_strategies.clone(), - Some(keystore.clone()), + extensions, )? }; @@ -829,6 +833,10 @@ ServiceBuilder< "best" => ?chain_info.best_hash ); + // make transaction pool available for off-chain runtime calls. + client.execution_extensions() + .register_transaction_pool(Arc::downgrade(&transaction_pool) as _); + let transaction_pool_adapter = Arc::new(TransactionPoolAdapter { imports_external_transactions: !config.roles.is_light(), pool: transaction_pool.clone(), @@ -909,8 +917,8 @@ ServiceBuilder< } let offchain = offchain.as_ref().and_then(|o| o.upgrade()); - if let (Some(txpool), Some(offchain)) = (txpool, offchain) { - let future = offchain.on_block_imported(&number, &txpool, network_state_info.clone(), is_validator) + if let Some(offchain) = offchain { + let future = offchain.on_block_imported(&number, network_state_info.clone(), is_validator) .map(|()| Ok(())); let _ = to_spawn_tx_.unbounded_send(Box::new(Compat::new(future))); } diff --git a/client/src/call_executor.rs b/client/src/call_executor.rs index 9e7c89c8390dd..e03293f2a722b 100644 --- a/client/src/call_executor.rs +++ b/client/src/call_executor.rs @@ -24,12 +24,12 @@ use state_machine::{ backend::Backend as _, ChangesTrieTransaction, StorageProof, }; use executor::{RuntimeVersion, RuntimeInfo, NativeVersion}; +use externalities::Extensions; use hash_db::Hasher; use primitives::{ - offchain::OffchainExt, H256, Blake2Hasher, NativeOrEncoded, NeverNativeValue, - traits::{CodeExecutor, KeystoreExt}, + H256, Blake2Hasher, NativeOrEncoded, NeverNativeValue, + traits::CodeExecutor, }; - use sr_api::{ProofRecorder, InitializeBlock}; use client_api::{ error, backend, call_executor::CallExecutor, @@ -40,7 +40,6 @@ use client_api::{ pub struct LocalCallExecutor { backend: Arc, executor: E, - keystore: Option, } impl LocalCallExecutor { @@ -48,12 +47,10 @@ impl LocalCallExecutor { pub fn new( backend: Arc, executor: E, - keystore: Option, ) -> Self { LocalCallExecutor { backend, executor, - keystore, } } } @@ -63,7 +60,6 @@ impl Clone for LocalCallExecutor where E: Clone { LocalCallExecutor { backend: self.backend.clone(), executor: self.executor.clone(), - keystore: self.keystore.clone(), } } } @@ -82,19 +78,18 @@ where method: &str, call_data: &[u8], strategy: ExecutionStrategy, - side_effects_handler: Option, + extensions: Option, ) -> error::Result> { let mut changes = OverlayedChanges::default(); let state = self.backend.state_at(*id)?; let return_data = StateMachine::new( &state, self.backend.changes_trie_storage(), - side_effects_handler, &mut changes, &self.executor, method, call_data, - self.keystore.clone().map(KeystoreExt), + extensions.unwrap_or_default(), ).execute_using_consensus_failure_handler::<_, NeverNativeValue, fn() -> _>( strategy.get_manager(), false, @@ -124,9 +119,8 @@ where initialize_block: InitializeBlock<'a, Block>, execution_manager: ExecutionManager, native_call: Option, - side_effects_handler: Option, recorder: &Option>, - enable_keystore: bool, + extensions: Option, ) -> Result, error::Error> where ExecutionManager: Clone { match initialize_block { InitializeBlock::Do(ref init_block) @@ -137,12 +131,6 @@ where _ => {}, } - let keystore = if enable_keystore { - self.keystore.clone().map(KeystoreExt) - } else { - None - }; - let mut state = self.backend.state_at(*at)?; let result = match recorder { @@ -161,12 +149,11 @@ where StateMachine::new( &backend, self.backend.changes_trie_storage(), - side_effects_handler, &mut *changes.borrow_mut(), &self.executor, method, call_data, - keystore, + extensions.unwrap_or_default(), ) .execute_using_consensus_failure_handler( execution_manager, @@ -179,12 +166,11 @@ where None => StateMachine::new( &state, self.backend.changes_trie_storage(), - side_effects_handler, &mut *changes.borrow_mut(), &self.executor, method, call_data, - keystore, + extensions.unwrap_or_default(), ) .execute_using_consensus_failure_handler( execution_manager, @@ -227,7 +213,7 @@ where call_data: &[u8], manager: ExecutionManager, native_call: Option, - side_effects_handler: Option, + extensions: Option, ) -> error::Result<( NativeOrEncoded, (S::Transaction, ::Out), @@ -236,12 +222,11 @@ where StateMachine::new( state, self.backend.changes_trie_storage(), - side_effects_handler, changes, &self.executor, method, call_data, - self.keystore.clone().map(KeystoreExt), + extensions.unwrap_or_default(), ).execute_using_consensus_failure_handler( manager, true, @@ -268,7 +253,9 @@ where &self.executor, method, call_data, - self.keystore.clone().map(KeystoreExt), + // Passing `None` here, since we don't really want to prove anything + // about our local keys. + None, ) .map_err(Into::into) } diff --git a/client/src/client.rs b/client/src/client.rs index def038fb3e022..abcf8fd2d1188 100644 --- a/client/src/client.rs +++ b/client/src/client.rs @@ -26,9 +26,10 @@ use parking_lot::{Mutex, RwLock}; use codec::{Encode, Decode}; use hash_db::{Hasher, Prefix}; use primitives::{ - Blake2Hasher, H256, ChangesTrieConfiguration, convert_hash, NeverNativeValue, ExecutionContext, - NativeOrEncoded, storage::{StorageKey, StorageData, well_known_keys}, - offchain::{OffchainExt, self}, traits::CodeExecutor, + Blake2Hasher, H256, ChangesTrieConfiguration, convert_hash, + NeverNativeValue, ExecutionContext, NativeOrEncoded, + storage::{StorageKey, StorageData, well_known_keys}, + traits::CodeExecutor, }; use substrate_telemetry::{telemetry, SUBSTRATE_INFO}; use sr_primitives::{ @@ -68,13 +69,14 @@ pub use client_api::{ }, client::{ ImportNotifications, FinalityNotification, FinalityNotifications, BlockImportNotification, - ClientInfo, BlockchainEvents, BlockBody, ProvideUncles, ExecutionStrategies, ForkBlocks, + ClientInfo, BlockchainEvents, BlockBody, ProvideUncles, ForkBlocks, BlockOf, }, + execution_extensions::{ExecutionExtensions, ExecutionStrategies}, notifications::{StorageNotifications, StorageEventStream}, error::Error, error, - CallExecutor + CallExecutor, }; use crate::{ @@ -100,7 +102,7 @@ pub struct Client where Block: BlockT { // holds the block hash currently being imported. TODO: replace this with block queue importing_block: RwLock>, fork_blocks: ForkBlocks, - execution_strategies: ExecutionStrategies, + execution_extensions: ExecutionExtensions, _phantom: PhantomData, } @@ -171,8 +173,9 @@ pub fn new_with_backend( Block: BlockT, B: backend::LocalBackend { - let call_executor = LocalCallExecutor::new(backend.clone(), executor, keystore); - Client::new(backend, call_executor, build_genesis_storage, Default::default(), Default::default()) + let call_executor = LocalCallExecutor::new(backend.clone(), executor); + let extensions = ExecutionExtensions::new(Default::default(), keystore); + Client::new(backend, call_executor, build_genesis_storage, Default::default(), extensions) } impl BlockOf for Client where @@ -194,7 +197,7 @@ impl Client where executor: E, build_genesis_storage: S, fork_blocks: ForkBlocks, - execution_strategies: ExecutionStrategies + execution_extensions: ExecutionExtensions, ) -> error::Result { if backend.blockchain().header(BlockId::Number(Zero::zero()))?.is_none() { let (genesis_storage, children_genesis_storage) = build_genesis_storage.build_storage()?; @@ -223,14 +226,14 @@ impl Client where finality_notification_sinks: Default::default(), importing_block: Default::default(), fork_blocks, - execution_strategies, + execution_extensions, _phantom: Default::default(), }) } - /// Get a reference to the execution strategies. - pub fn execution_strategies(&self) -> &ExecutionStrategies { - &self.execution_strategies + /// Get a reference to the execution extensions. + pub fn execution_extensions(&self) -> &ExecutionExtensions { + &self.execution_extensions } /// Get a reference to the state at a given block. @@ -993,9 +996,9 @@ impl Client where &encoded_block, match origin { BlockOrigin::NetworkInitialSync => get_execution_manager( - self.execution_strategies().syncing, + self.execution_extensions().strategies().syncing, ), - _ => get_execution_manager(self.execution_strategies().importing), + _ => get_execution_manager(self.execution_extensions().strategies().importing), }, None, None, @@ -1395,26 +1398,7 @@ impl CallRuntimeAt for Client where context: ExecutionContext, recorder: &Option>, ) -> error::Result> { - 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::OffchainCall(Some((_, capabilities))) if capabilities.has_all() => - self.execution_strategies.offchain_worker.get_manager(), - ExecutionContext::OffchainCall(_) => - self.execution_strategies.other.get_manager(), - }; - - let capabilities = context.capabilities(); - let offchain_extensions = if let ExecutionContext::OffchainCall(Some(ext)) = context { - Some(OffchainExt::new(offchain::LimitedExternalities::new(capabilities, ext.0))) - } else { - None - }; - + let (manager, extensions) = self.execution_extensions.manager_and_extensions(at, context); self.executor.contextual_call::<_, fn(_,_) -> _,_,_>( || core_api.initialize_block(at, &self.prepare_environment_block(at)?), at, @@ -1424,9 +1408,8 @@ impl CallRuntimeAt for Client where initialize_block, manager, native_call, - offchain_extensions, recorder, - capabilities.has(offchain::Capability::Keystore), + Some(extensions), ) } diff --git a/client/src/genesis.rs b/client/src/genesis.rs index 031b2dc0ad7df..94822466bbff5 100644 --- a/client/src/genesis.rs +++ b/client/src/genesis.rs @@ -93,12 +93,11 @@ mod tests { StateMachine::new( backend, Some(&InMemoryChangesTrieStorage::<_, u64>::new()), - None, &mut overlay, &executor(), "Core_initialize_block", &header.encode(), - None, + Default::default(), ).execute( ExecutionStrategy::NativeElseWasm, ).unwrap(); @@ -107,12 +106,11 @@ mod tests { StateMachine::new( backend, Some(&InMemoryChangesTrieStorage::<_, u64>::new()), - None, &mut overlay, &executor(), "BlockBuilder_apply_extrinsic", &tx.encode(), - None, + Default::default(), ).execute( ExecutionStrategy::NativeElseWasm, ).unwrap(); @@ -121,12 +119,11 @@ mod tests { let (ret_data, _, _) = StateMachine::new( backend, Some(&InMemoryChangesTrieStorage::<_, u64>::new()), - None, &mut overlay, &executor(), "BlockBuilder_finalize_block", &[], - None, + Default::default(), ).execute( ExecutionStrategy::NativeElseWasm, ).unwrap(); @@ -169,12 +166,11 @@ mod tests { let _ = StateMachine::new( &backend, Some(&InMemoryChangesTrieStorage::<_, u64>::new()), - None, &mut overlay, &executor(), "Core_execute_block", &b1data, - None, + Default::default(), ).execute( ExecutionStrategy::NativeElseWasm, ).unwrap(); @@ -199,12 +195,11 @@ mod tests { let _ = StateMachine::new( &backend, Some(&InMemoryChangesTrieStorage::<_, u64>::new()), - None, &mut overlay, &executor(), "Core_execute_block", &b1data, - None, + Default::default(), ).execute( ExecutionStrategy::AlwaysWasm, ).unwrap(); @@ -229,12 +224,11 @@ mod tests { let r = StateMachine::new( &backend, Some(&InMemoryChangesTrieStorage::<_, u64>::new()), - None, &mut overlay, &executor(), "Core_execute_block", &b1data, - None, + Default::default(), ).execute( ExecutionStrategy::NativeElseWasm, ); diff --git a/client/src/in_mem.rs b/client/src/in_mem.rs index 4c74943189020..efab895794eea 100644 --- a/client/src/in_mem.rs +++ b/client/src/in_mem.rs @@ -20,6 +20,9 @@ use std::collections::{HashMap, HashSet}; use std::sync::Arc; use parking_lot::{RwLock, Mutex}; use primitives::{ChangesTrieConfiguration, storage::well_known_keys}; +use primitives::offchain::storage::{ + InMemOffchainStorage as OffchainStorage +}; use sr_primitives::generic::{BlockId, DigestItem}; use sr_primitives::traits::{Block as BlockT, Header as HeaderT, Zero, NumberFor}; use sr_primitives::{Justification, StorageOverlay, ChildrenStorageOverlay}; @@ -35,9 +38,6 @@ use client_api::{ blockchain::{ self, BlockStatus, HeaderBackend, well_known_cache_keys::Id as CacheKeyId }, - offchain::{ - InMemOffchainStorage as OffchainStorage - } }; use crate::leaves::LeafSet; @@ -811,7 +811,7 @@ pub fn check_genesis_storage(top: &StorageOverlay, children: &ChildrenStorageOve #[cfg(test)] mod tests { - use client_api::offchain::{OffchainStorage, InMemOffchainStorage}; + use primitives::offchain::{OffchainStorage, storage::InMemOffchainStorage}; use std::sync::Arc; use test_client; use primitives::Blake2Hasher; diff --git a/client/src/lib.rs b/client/src/lib.rs index 21a1c415d54a5..71127d3cb061e 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -63,7 +63,6 @@ //! LocalCallExecutor::new( //! backend.clone(), //! NativeExecutor::::new(WasmExecutionMethod::Interpreted, None), -//! None, //! ), //! // This parameter provides the storage for the chain genesis. //! <(StorageOverlay, ChildrenStorageOverlay)>::default(), @@ -93,13 +92,15 @@ pub use client_api::{ call_executor::CallExecutor, utils, }; -pub use crate::call_executor::LocalCallExecutor; -pub use crate::client::{ - new_with_backend, - new_in_mem, - BlockBody, ImportNotifications, FinalityNotifications, BlockchainEvents, - BlockImportNotification, Client, ClientInfo, ExecutionStrategies, FinalityNotification, - LongestChain, BlockOf, ProvideUncles, ForkBlocks, apply_aux, +pub use crate::{ + call_executor::LocalCallExecutor, + client::{ + new_with_backend, + new_in_mem, + BlockBody, ImportNotifications, FinalityNotifications, BlockchainEvents, + BlockImportNotification, Client, ClientInfo, ExecutionStrategies, FinalityNotification, + LongestChain, BlockOf, ProvideUncles, ForkBlocks, apply_aux, + }, + leaves::LeafSet, }; pub use state_machine::{ExecutionStrategy, StorageProof}; -pub use crate::leaves::LeafSet; diff --git a/client/src/light/backend.rs b/client/src/light/backend.rs index ec4543191896e..5db0c55d5e979 100644 --- a/client/src/light/backend.rs +++ b/client/src/light/backend.rs @@ -21,8 +21,9 @@ use std::collections::HashMap; use std::sync::Arc; use parking_lot::{RwLock, Mutex}; -use sr_primitives::{generic::BlockId, Justification, StorageOverlay, ChildrenStorageOverlay}; use state_machine::{Backend as StateBackend, TrieBackend, backend::InMemory as InMemoryState, ChangesTrieTransaction}; +use primitives::offchain::storage::InMemOffchainStorage; +use sr_primitives::{generic::BlockId, Justification, StorageOverlay, ChildrenStorageOverlay}; use sr_primitives::traits::{Block as BlockT, NumberFor, Zero, Header}; use crate::in_mem::{self, check_genesis_storage}; use client_api::{ @@ -37,9 +38,8 @@ use client_api::{ Error as ClientError, Result as ClientResult }, light::Storage as BlockchainStorage, - InMemOffchainStorage, }; -use crate::light::blockchain::{Blockchain}; +use crate::light::blockchain::Blockchain; use hash_db::Hasher; use trie::MemoryDB; diff --git a/client/src/light/call_executor.rs b/client/src/light/call_executor.rs index 9d48cfebbe53d..a4a8a549de93c 100644 --- a/client/src/light/call_executor.rs +++ b/client/src/light/call_executor.rs @@ -22,12 +22,13 @@ use std::{ use codec::{Encode, Decode}; use primitives::{ - offchain::OffchainExt, H256, Blake2Hasher, convert_hash, NativeOrEncoded, + H256, Blake2Hasher, convert_hash, NativeOrEncoded, traits::CodeExecutor, }; use sr_primitives::{ generic::BlockId, traits::{One, Block as BlockT, Header as HeaderT, NumberFor}, }; +use externalities::Extensions; use state_machine::{ self, Backend as StateBackend, OverlayedChanges, ExecutionStrategy, create_proof_check_backend, execution_proof_check_on_trie_backend, ExecutionManager, ChangesTrieTransaction, StorageProof, @@ -84,10 +85,10 @@ impl CallExecutor for method: &str, call_data: &[u8], strategy: ExecutionStrategy, - side_effects_handler: Option, + extensions: Option, ) -> ClientResult> { match self.backend.is_local_state_available(id) { - true => self.local.call(id, method, call_data, strategy, side_effects_handler), + true => self.local.call(id, method, call_data, strategy, extensions), false => Err(ClientError::NotAvailableOnLightClient), } } @@ -111,9 +112,8 @@ impl CallExecutor for initialize_block: InitializeBlock<'a, Block>, _manager: ExecutionManager, native_call: Option, - side_effects_handler: Option, recorder: &Option>, - enable_keystore: bool, + extensions: Option, ) -> ClientResult> where ExecutionManager: Clone { // there's no actual way/need to specify native/wasm execution strategy on light node // => we can safely ignore passed values @@ -137,9 +137,8 @@ impl CallExecutor for initialize_block, ExecutionManager::NativeWhenPossible, native_call, - side_effects_handler, recorder, - enable_keystore, + extensions, ).map_err(|e| ClientError::Execution(Box::new(e.to_string()))), false => Err(ClientError::NotAvailableOnLightClient), } @@ -167,7 +166,7 @@ impl CallExecutor for _call_data: &[u8], _manager: ExecutionManager, _native_call: Option, - _side_effects_handler: Option, + _extensions: Option, ) -> ClientResult<( NativeOrEncoded, (S::Transaction, ::Out), @@ -313,7 +312,7 @@ mod tests { _method: &str, _call_data: &[u8], _strategy: ExecutionStrategy, - _side_effects_handler: Option, + _extensions: Option, ) -> Result, ClientError> { Ok(vec![42]) } @@ -337,9 +336,8 @@ mod tests { _initialize_block: InitializeBlock<'a, Block>, _execution_manager: ExecutionManager, _native_call: Option, - _side_effects_handler: Option, _proof_recorder: &Option>, - _enable_keystore: bool, + _extensions: Option, ) -> ClientResult> where ExecutionManager: Clone { unreachable!() } @@ -363,7 +361,7 @@ mod tests { _call_data: &[u8], _manager: ExecutionManager, _native_call: Option, - _side_effects_handler: Option, + _extensions: Option, ) -> Result< ( NativeOrEncoded, diff --git a/client/src/light/mod.rs b/client/src/light/mod.rs index 7db1bff58900f..787e48e449a1e 100644 --- a/client/src/light/mod.rs +++ b/client/src/light/mod.rs @@ -68,7 +68,7 @@ pub fn new_light( GS: BuildStorage, E: CodeExecutor + RuntimeInfo, { - let local_executor = LocalCallExecutor::new(backend.clone(), code_executor, None); + let local_executor = LocalCallExecutor::new(backend.clone(), code_executor); let executor = GenesisCallExecutor::new(backend.clone(), local_executor); Client::new(backend, executor, genesis_storage, Default::default(), Default::default()) } diff --git a/client/transaction-pool/graph/src/pool.rs b/client/transaction-pool/graph/src/pool.rs index c6e33223282ff..514ce174a1ef2 100644 --- a/client/transaction-pool/graph/src/pool.rs +++ b/client/transaction-pool/graph/src/pool.rs @@ -388,6 +388,29 @@ impl Clone for Pool { } } +impl sr_primitives::offchain::TransactionPool for Pool { + fn submit_at( + &self, + at: &BlockId, + extrinsic: ::Extrinsic, + ) -> Result<(), ()> { + log::debug!( + target: "txpool", + "(offchain call) Submitting a transaction to the pool: {:?}", + extrinsic + ); + + let result = futures::executor::block_on(self.submit_one(&at, extrinsic)); + + result.map(|_| ()) + .map_err(|e| log::warn!( + target: "txpool", + "(offchain call) Error submitting a transaction to the pool: {:?}", + e + )) + } +} + #[cfg(test)] mod tests { use std::{ diff --git a/palette/im-online/Cargo.toml b/palette/im-online/Cargo.toml index 72832b8aeb95b..8839f79d25bf1 100644 --- a/palette/im-online/Cargo.toml +++ b/palette/im-online/Cargo.toml @@ -18,9 +18,6 @@ sr-staking-primitives = { path = "../../primitives/sr-staking-primitives", defau support = { package = "palette-support", path = "../support", default-features = false } system = { package = "palette-system", path = "../system", default-features = false } -[dev-dependencies] -offchain = { package = "substrate-offchain", path = "../../client/offchain" } - [features] default = ["std", "session/historical"] std = [ diff --git a/palette/im-online/src/tests.rs b/palette/im-online/src/tests.rs index 382eb4f1d1f04..edfdc34bc00d2 100644 --- a/palette/im-online/src/tests.rs +++ b/palette/im-online/src/tests.rs @@ -20,8 +20,12 @@ use super::*; use crate::mock::*; -use offchain::testing::TestOffchainExt; -use primitives::offchain::{OpaquePeerId, OffchainExt}; +use primitives::offchain::{ + OpaquePeerId, + OffchainExt, + TransactionPoolExt, + testing::{TestOffchainExt, TestTransactionPoolExt}, +}; use support::{dispatch, assert_noop}; use sr_primitives::testing::UintAuthorityId; @@ -183,8 +187,10 @@ fn late_heartbeat_should_fail() { #[test] fn should_generate_heartbeats() { let mut ext = new_test_ext(); - let (offchain, state) = TestOffchainExt::new(); + let (offchain, _state) = TestOffchainExt::new(); + let (pool, state) = TestTransactionPoolExt::new(); ext.register_extension(OffchainExt::new(offchain)); + ext.register_extension(TransactionPoolExt::new(pool)); ext.execute_with(|| { // given @@ -284,8 +290,10 @@ fn should_not_send_a_report_if_already_online() { use authorship::EventHandler; let mut ext = new_test_ext(); - let (offchain, state) = TestOffchainExt::new(); + let (offchain, _state) = TestOffchainExt::new(); + let (pool, pool_state) = TestTransactionPoolExt::new(); ext.register_extension(OffchainExt::new(offchain)); + ext.register_extension(TransactionPoolExt::new(pool)); ext.execute_with(|| { advance_session(); @@ -304,9 +312,9 @@ fn should_not_send_a_report_if_already_online() { ImOnline::offchain(4); // then - let transaction = state.write().transactions.pop().unwrap(); + let transaction = pool_state.write().transactions.pop().unwrap(); // All validators have `0` as their session key, but we should only produce 1 hearbeat. - assert_eq!(state.read().transactions.len(), 0); + assert_eq!(pool_state.read().transactions.len(), 0); // check stuff about the transaction. let ex: Extrinsic = Decode::decode(&mut &*transaction).unwrap(); let heartbeat = match ex.1 { diff --git a/primitives/core/src/offchain.rs b/primitives/core/src/offchain/mod.rs similarity index 94% rename from primitives/core/src/offchain.rs rename to primitives/core/src/offchain/mod.rs index 7b24e5ee72c98..8afabc0392a2a 100644 --- a/primitives/core/src/offchain.rs +++ b/primitives/core/src/offchain/mod.rs @@ -23,6 +23,31 @@ use runtime_interface::pass_by::{PassByCodec, PassByInner, PassByEnum}; pub use crate::crypto::KeyTypeId; +#[cfg(feature = "std")] +pub mod storage; +#[cfg(feature = "std")] +pub mod testing; + +/// Offchain workers local storage. +pub trait OffchainStorage: Clone + Send + Sync { + /// Persist a value in storage under given key and prefix. + fn set(&mut self, prefix: &[u8], key: &[u8], value: &[u8]); + + /// Retrieve a value from storage under given key and prefix. + fn get(&self, prefix: &[u8], key: &[u8]) -> Option>; + + /// Replace the value in storage if given old_value matches the current one. + /// + /// Returns `true` if the value has been set and false otherwise. + fn compare_and_set( + &mut self, + prefix: &[u8], + key: &[u8], + old_value: Option<&[u8]>, + new_value: &[u8], + ) -> bool; +} + /// A type of supported crypto. #[derive(Clone, Copy, PartialEq, Eq, Encode, Decode, RuntimeDebug, PassByEnum)] #[repr(C)] @@ -296,10 +321,6 @@ pub trait Externalities: Send { /// Even if this function returns `true`, it does not mean that any keys are configured /// and that the validator is registered in the chain. fn is_validator(&self) -> bool; - /// Submit transaction. - /// - /// The transaction will end up in the pool and be propagated to others. - fn submit_transaction(&mut self, extrinsic: Vec) -> Result<(), ()>; /// Returns information about the local node's network state. fn network_state(&self) -> Result; @@ -466,10 +487,6 @@ impl Externalities for Box { (& **self).is_validator() } - fn submit_transaction(&mut self, ex: Vec) -> Result<(), ()> { - (&mut **self).submit_transaction(ex) - } - fn network_state(&self) -> Result { (& **self).network_state() } @@ -569,11 +586,6 @@ impl Externalities for LimitedExternalities { self.externalities.is_validator() } - fn submit_transaction(&mut self, ex: Vec) -> Result<(), ()> { - self.check(Capability::TransactionPool, "submit_transaction"); - self.externalities.submit_transaction(ex) - } - fn network_state(&self) -> Result { self.check(Capability::NetworkState, "network_state"); self.externalities.network_state() @@ -670,6 +682,34 @@ impl OffchainExt { } } +/// Abstraction over transaction pool. +/// +/// This trait is currently used within the `ExternalitiesExtension` +/// to provide offchain calls with access to the transaction pool without +/// tight coupling with any pool implementation. +#[cfg(feature = "std")] +pub trait TransactionPool { + /// Submit transaction. + /// + /// The transaction will end up in the pool and be propagated to others. + fn submit_transaction(&mut self, extrinsic: Vec) -> Result<(), ()>; +} + +#[cfg(feature = "std")] +externalities::decl_extension! { + /// An externalities extension to submit transactions to the pool. + pub struct TransactionPoolExt(Box); +} + +#[cfg(feature = "std")] +impl TransactionPoolExt { + /// Create a new instance of `TransactionPoolExt`. + pub fn new(pool: O) -> Self { + Self(Box::new(pool)) + } +} + + #[cfg(test)] mod tests { use super::*; diff --git a/client/api/src/offchain.rs b/primitives/core/src/offchain/storage.rs similarity index 74% rename from client/api/src/offchain.rs rename to primitives/core/src/offchain/storage.rs index 761241efe7996..60339f70055f2 100644 --- a/client/api/src/offchain.rs +++ b/primitives/core/src/offchain/storage.rs @@ -14,27 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -use std::collections::hash_map::{HashMap, Entry}; - -/// Offchain workers local storage. -pub trait OffchainStorage: Clone + Send + Sync { - /// Persist a value in storage under given key and prefix. - fn set(&mut self, prefix: &[u8], key: &[u8], value: &[u8]); - - /// Retrieve a value from storage under given key and prefix. - fn get(&self, prefix: &[u8], key: &[u8]) -> Option>; +//! In-memory implementation of offchain workers database. - /// Replace the value in storage if given old_value matches the current one. - /// - /// Returns `true` if the value has been set and false otherwise. - fn compare_and_set( - &mut self, - prefix: &[u8], - key: &[u8], - old_value: Option<&[u8]>, - new_value: &[u8], - ) -> bool; -} +use std::collections::hash_map::{HashMap, Entry}; +use crate::offchain::OffchainStorage; /// In-memory storage for offchain workers. #[derive(Debug, Clone, Default)] @@ -74,4 +57,4 @@ impl OffchainStorage for InMemOffchainStorage { _ => false, } } -} \ No newline at end of file +} diff --git a/client/offchain/src/testing.rs b/primitives/core/src/offchain/testing.rs similarity index 85% rename from client/offchain/src/testing.rs rename to primitives/core/src/offchain/testing.rs index b1699a94ca37f..0b15c2f314ab3 100644 --- a/client/offchain/src/testing.rs +++ b/primitives/core/src/offchain/testing.rs @@ -14,23 +14,28 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -//! Offchain Externalities implementation for tests. +//! Utilities for offchain calls testing. +//! +//! Namely all ExecutionExtensions that allow mocking +//! the extra APIs. use std::{ collections::BTreeMap, sync::Arc, }; -use client_api::{OffchainStorage, InMemOffchainStorage}; -use parking_lot::RwLock; -use primitives::offchain::{ +use crate::offchain::{ self, + storage::InMemOffchainStorage, HttpError, HttpRequestId as RequestId, HttpRequestStatus as RequestStatus, Timestamp, StorageKind, OpaqueNetworkState, + TransactionPool, + OffchainStorage, }; +use parking_lot::RwLock; /// Pending request. #[derive(Debug, Default, PartialEq, Eq)] @@ -59,7 +64,7 @@ pub struct PendingRequest { /// /// This can be used in tests to respond or assert stuff about interactions. #[derive(Debug, Default)] -pub struct State { +pub struct OffchainState { /// A list of pending requests. pub requests: BTreeMap, expected_requests: BTreeMap, @@ -67,11 +72,9 @@ pub struct State { pub persistent_storage: InMemOffchainStorage, /// Local storage pub local_storage: InMemOffchainStorage, - /// A vector of transactions submitted from the runtime. - pub transactions: Vec>, } -impl State { +impl OffchainState { /// Asserts that pending request has been submitted and fills it's response. pub fn fulfill_pending_request( &mut self, @@ -117,7 +120,7 @@ impl State { } } -impl Drop for State { +impl Drop for OffchainState { fn drop(&mut self) { // If we panic! while we are already in a panic, the test dies with an illegal instruction. if !self.expected_requests.is_empty() && !std::thread::panicking() { @@ -128,11 +131,11 @@ impl Drop for State { /// Implementation of offchain externalities used for tests. #[derive(Clone, Default, Debug)] -pub struct TestOffchainExt(pub Arc>); +pub struct TestOffchainExt(pub Arc>); impl TestOffchainExt { /// Create new `TestOffchainExt` and a reference to the internal state. - pub fn new() -> (Self, Arc>) { + pub fn new() -> (Self, Arc>) { let ext = Self::default(); let state = ext.0.clone(); (ext, state) @@ -144,12 +147,6 @@ impl offchain::Externalities for TestOffchainExt { unimplemented!("not needed in tests so far") } - fn submit_transaction(&mut self, ex: Vec) -> Result<(), ()> { - let mut state = self.0.write(); - state.transactions.push(ex); - Ok(()) - } - fn network_state(&self) -> Result { Ok(OpaqueNetworkState { peer_id: Default::default(), @@ -305,3 +302,38 @@ impl offchain::Externalities for TestOffchainExt { } } } + +/// The internal state of the fake transaction pool. +#[derive(Default)] +pub struct PoolState { + /// A vector of transactions submitted from the runtime. + pub transactions: Vec>, +} + +/// Implementation of transaction pool used for test. +/// +/// Note that this implementation does not verify correctness +/// of sent extrinsics. It's meant to be used in contexts +/// where an actual runtime is not known. +/// +/// It's advised to write integration tests that include the +/// actual transaction pool to make sure the produced +/// transactions are valid. +#[derive(Default)] +pub struct TestTransactionPoolExt(Arc>); + +impl TestTransactionPoolExt { + /// Create new `TestTransactionPoolExt` and a reference to the internal state. + pub fn new() -> (Self, Arc>) { + let ext = Self::default(); + let state = ext.0.clone(); + (ext, state) + } +} + +impl TransactionPool for TestTransactionPoolExt { + fn submit_transaction(&mut self, extrinsic: Vec) -> Result<(), ()> { + self.0.write().transactions.push(extrinsic); + Ok(()) + } +} diff --git a/primitives/core/src/traits.rs b/primitives/core/src/traits.rs index 1ef665032eed4..f1b13408d1f0c 100644 --- a/primitives/core/src/traits.rs +++ b/primitives/core/src/traits.rs @@ -18,7 +18,11 @@ use crate::{crypto::KeyTypeId, ed25519, sr25519}; -use std::{fmt::{Debug, Display}, panic::UnwindSafe}; +use std::{ + fmt::{Debug, Display}, + panic::UnwindSafe, + sync::Arc, +}; pub use externalities::{Externalities, ExternalitiesExt}; @@ -68,7 +72,7 @@ pub trait BareCryptoStore: Send + Sync { } /// A pointer to the key store. -pub type BareCryptoStorePtr = std::sync::Arc>; +pub type BareCryptoStorePtr = Arc>; externalities::decl_extension! { /// The keystore extension to register/retrieve from the externalities. diff --git a/primitives/sr-io/src/lib.rs b/primitives/sr-io/src/lib.rs index 080d0b245b738..2b40670c450cb 100644 --- a/primitives/sr-io/src/lib.rs +++ b/primitives/sr-io/src/lib.rs @@ -35,7 +35,10 @@ use rstd::ops::Deref; #[cfg(feature = "std")] use primitives::{ - crypto::Pair, traits::KeystoreExt, offchain::OffchainExt, hexdisplay::HexDisplay, + crypto::Pair, + traits::KeystoreExt, + offchain::{OffchainExt, TransactionPoolExt}, + hexdisplay::HexDisplay, storage::ChildStorageKey, }; @@ -424,8 +427,9 @@ pub trait Offchain { /// /// The transaction will end up in the pool. fn submit_transaction(&mut self, data: Vec) -> Result<(), ()> { - self.extension::() - .expect("submit_transaction can be called only in the offchain worker context") + self.extension::() + .expect("submit_transaction can be called only in the offchain call context with + TransactionPool capabilities enabled") .submit_transaction(data) } diff --git a/primitives/sr-primitives/Cargo.toml b/primitives/sr-primitives/Cargo.toml index e1412cf53407e..bd709c68bd4d9 100644 --- a/primitives/sr-primitives/Cargo.toml +++ b/primitives/sr-primitives/Cargo.toml @@ -21,7 +21,6 @@ inherents = { package = "substrate-inherents", path = "../inherents", default-fe [dev-dependencies] serde_json = "1.0.41" rand = "0.7.2" -substrate-offchain = { path = "../../client/offchain" } [features] bench = [] diff --git a/primitives/sr-primitives/src/offchain/http.rs b/primitives/sr-primitives/src/offchain/http.rs index 8024437075924..50d536fc14dbb 100644 --- a/primitives/sr-primitives/src/offchain/http.rs +++ b/primitives/sr-primitives/src/offchain/http.rs @@ -516,8 +516,10 @@ impl<'a> HeadersIterator<'a> { mod tests { use super::*; use runtime_io::TestExternalities; - use substrate_offchain::testing; - use primitives::offchain::OffchainExt; + use primitives::offchain::{ + OffchainExt, + testing, + }; #[test] fn should_send_a_basic_request_and_get_response() { diff --git a/primitives/sr-primitives/src/offchain/mod.rs b/primitives/sr-primitives/src/offchain/mod.rs index 6b82f77111999..51dc19bc12d60 100644 --- a/primitives/sr-primitives/src/offchain/mod.rs +++ b/primitives/sr-primitives/src/offchain/mod.rs @@ -14,6 +14,29 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -//! A collection of higher lever helpers for offchain workers. +//! A collection of higher lever helpers for offchain calls. + +use crate::{ + traits, + generic::BlockId, +}; pub mod http; + +/// An abstraction for transaction pool. +/// +/// This trait is used by offchain calls to be able to submit transactions. +/// The main use case is for offchain workers, to feed back the results of computations, +/// but since the transaction pool access is a separate `ExternalitiesExtension` it can +/// be also used in context of other offchain calls. For one may generate and submit +/// a transaction for some misbehavior reports (say equivocation). +pub trait TransactionPool: Send + Sync { + /// Submit transaction. + /// + /// The transaction will end up in the pool and be propagated to others. + fn submit_at( + &self, + at: &BlockId, + extrinsic: Block::Extrinsic, + ) -> Result<(), ()>; +} diff --git a/primitives/state-machine/src/lib.rs b/primitives/state-machine/src/lib.rs index 85fa02f7ffe8b..9095be1877b37 100644 --- a/primitives/state-machine/src/lib.rs +++ b/primitives/state-machine/src/lib.rs @@ -23,7 +23,7 @@ use log::{warn, trace}; use hash_db::Hasher; use codec::{Decode, Encode}; use primitives::{ - storage::well_known_keys, NativeOrEncoded, NeverNativeValue, offchain::OffchainExt, + storage::well_known_keys, NativeOrEncoded, NeverNativeValue, traits::{KeystoreExt, CodeExecutor}, hexdisplay::HexDisplay, hash::H256, }; use overlayed_changes::OverlayedChangeSet; @@ -68,7 +68,8 @@ pub use error::{Error, ExecutionError}; type CallResult = Result, E>; -type DefaultHandler = fn(CallResult, CallResult) -> CallResult; +/// Default handler of the execution manager. +pub type DefaultHandler = fn(CallResult, CallResult) -> CallResult; /// Type of changes trie transaction. pub type ChangesTrieTransaction = ( @@ -185,23 +186,12 @@ impl<'a, B, H, N, T, Exec> StateMachine<'a, B, H, N, T, Exec> where pub fn new( backend: &'a B, changes_trie_storage: Option<&'a T>, - offchain_ext: Option, overlay: &'a mut OverlayedChanges, exec: &'a Exec, method: &'a str, call_data: &'a [u8], - keystore: Option, + extensions: Extensions, ) -> Self { - let mut extensions = Extensions::new(); - - if let Some(keystore) = keystore { - extensions.register(keystore); - } - - if let Some(offchain) = offchain_ext { - extensions.register(offchain); - } - Self { backend, exec, @@ -497,8 +487,12 @@ where Exec: CodeExecutor, { let proving_backend = proving_backend::ProvingBackend::new(trie_backend); + let mut extensions = Extensions::new(); + if let Some(keystore) = keystore { + extensions.register(keystore); + } let mut sm = StateMachine::<_, H, _, InMemoryChangesTrieStorage, Exec>::new( - &proving_backend, None, None, overlay, exec, method, call_data, keystore, + &proving_backend, None, overlay, exec, method, call_data, extensions, ); let (result, _, _) = sm.execute_using_consensus_failure_handler::<_, NeverNativeValue, fn() -> _>( @@ -542,8 +536,12 @@ where H: Hasher, Exec: CodeExecutor, { + let mut extensions = Extensions::new(); + if let Some(keystore) = keystore { + extensions.register(keystore); + } let mut sm = StateMachine::<_, H, _, InMemoryChangesTrieStorage, Exec>::new( - trie_backend, None, None, overlay, exec, method, call_data, keystore, + trie_backend, None, overlay, exec, method, call_data, extensions, ); sm.execute_using_consensus_failure_handler::<_, NeverNativeValue, fn() -> _>( @@ -820,7 +818,6 @@ mod tests { let mut state_machine = StateMachine::new( &backend, Some(&changes_trie_storage), - None, &mut overlayed_changes, &DummyCodeExecutor { change_changes_trie_config: false, @@ -830,7 +827,7 @@ mod tests { }, "test", &[], - None, + Default::default(), ); assert_eq!( @@ -849,7 +846,6 @@ mod tests { let mut state_machine = StateMachine::new( &backend, Some(&changes_trie_storage), - None, &mut overlayed_changes, &DummyCodeExecutor { change_changes_trie_config: false, @@ -859,7 +855,7 @@ mod tests { }, "test", &[], - None, + Default::default(), ); assert_eq!(state_machine.execute(ExecutionStrategy::NativeElseWasm).unwrap().0, vec![66]); @@ -875,7 +871,6 @@ mod tests { let mut state_machine = StateMachine::new( &backend, Some(&changes_trie_storage), - None, &mut overlayed_changes, &DummyCodeExecutor { change_changes_trie_config: false, @@ -885,7 +880,7 @@ mod tests { }, "test", &[], - None, + Default::default(), ); assert!( @@ -1085,7 +1080,6 @@ mod tests { let mut state_machine = StateMachine::new( &backend, Some(&changes_trie_storage), - None, &mut overlayed_changes, &DummyCodeExecutor { change_changes_trie_config: true, @@ -1095,7 +1089,7 @@ mod tests { }, "test", &[], - None, + Default::default(), ); assert!(state_machine.execute(ExecutionStrategy::NativeWhenPossible).is_err()); @@ -1110,7 +1104,6 @@ mod tests { let mut state_machine = StateMachine::new( &backend, Some(&changes_trie_storage), - None, &mut overlayed_changes, &DummyCodeExecutor { change_changes_trie_config: true, @@ -1120,7 +1113,7 @@ mod tests { }, "test", &[], - None, + Default::default(), ); assert!(state_machine.execute(ExecutionStrategy::NativeElseWasm).is_err()); diff --git a/test/utils/client/src/lib.rs b/test/utils/client/src/lib.rs index 7143b67599199..c4f97aec1daad 100644 --- a/test/utils/client/src/lib.rs +++ b/test/utils/client/src/lib.rs @@ -20,7 +20,8 @@ pub mod client_ext; -pub use client::{ExecutionStrategies, blockchain, self}; +pub use client::{blockchain, self}; +pub use client_api::execution_extensions::{ExecutionStrategies, ExecutionExtensions}; pub use client_db::{Backend, self}; pub use client_ext::ClientExt; pub use consensus; @@ -194,7 +195,10 @@ impl TestClientBuilder executor, storage, Default::default(), - self.execution_strategies, + ExecutionExtensions::new( + self.execution_strategies, + self.keystore.clone(), + ) ).expect("Creates new client"); let longest_chain = client::LongestChain::new(self.backend); @@ -210,7 +214,7 @@ impl TestClientBuilder< > { /// Build the test client with the given native executor. pub fn build_with_native_executor( - mut self, + self, executor: I, ) -> ( client::Client< @@ -229,7 +233,7 @@ impl TestClientBuilder< let executor = executor.into().unwrap_or_else(|| NativeExecutor::new(WasmExecutionMethod::Interpreted, None) ); - let executor = LocalCallExecutor::new(self.backend.clone(), executor, self.keystore.take()); + let executor = LocalCallExecutor::new(self.backend.clone(), executor); self.build_with_executor(executor) } diff --git a/test/utils/runtime/client/src/lib.rs b/test/utils/runtime/client/src/lib.rs index b5f3833e52f07..d56bcfa770b5d 100644 --- a/test/utils/runtime/client/src/lib.rs +++ b/test/utils/runtime/client/src/lib.rs @@ -262,7 +262,7 @@ pub fn new_light() -> ( let blockchain = Arc::new(client::light::blockchain::Blockchain::new(storage)); let backend = Arc::new(LightBackend::new(blockchain.clone())); let executor = NativeExecutor::new(WasmExecutionMethod::Interpreted, None); - let local_call_executor = client::LocalCallExecutor::new(backend.clone(), executor, None); + let local_call_executor = client::LocalCallExecutor::new(backend.clone(), executor); let call_executor = LightExecutor::new( backend.clone(), local_call_executor,