Offchain execution extensions (#4145)

* Pass Extensions instead of individual objects.

* Move TransactionPool to a separate ExternalitiesExtension.

* Fix compilation.?

* Clean up.

* Refactor testing utilities.

* Add docs, fix tests.

* Fix doctest.

* Fix formatting and add some logs.

* Add some docs.

* Remove unused files.
This commit is contained in:
Tomasz Drwięga
2019-11-22 17:10:23 +01:00
committed by Gavin Wood
parent f000392cc0
commit 86b6ac5571
39 changed files with 554 additions and 360 deletions
+13 -26
View File
@@ -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<B, E> {
backend: Arc<B>,
executor: E,
keystore: Option<primitives::traits::BareCryptoStorePtr>,
}
impl<B, E> LocalCallExecutor<B, E> {
@@ -48,12 +47,10 @@ impl<B, E> LocalCallExecutor<B, E> {
pub fn new(
backend: Arc<B>,
executor: E,
keystore: Option<primitives::traits::BareCryptoStorePtr>,
) -> Self {
LocalCallExecutor {
backend,
executor,
keystore,
}
}
}
@@ -63,7 +60,6 @@ impl<B, E> Clone for LocalCallExecutor<B, E> 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<OffchainExt>,
extensions: Option<Extensions>,
) -> error::Result<Vec<u8>> {
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<EM>,
native_call: Option<NC>,
side_effects_handler: Option<OffchainExt>,
recorder: &Option<ProofRecorder<Block>>,
enable_keystore: bool,
extensions: Option<Extensions>,
) -> Result<NativeOrEncoded<R>, error::Error> where ExecutionManager<EM>: 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<F>,
native_call: Option<NC>,
side_effects_handler: Option<OffchainExt>,
extensions: Option<Extensions>,
) -> error::Result<(
NativeOrEncoded<R>,
(S::Transaction, <Blake2Hasher as Hasher>::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)
}
+20 -37
View File
@@ -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<B, E, Block, RA> where Block: BlockT {
// holds the block hash currently being imported. TODO: replace this with block queue
importing_block: RwLock<Option<Block::Hash>>,
fork_blocks: ForkBlocks<Block>,
execution_strategies: ExecutionStrategies,
execution_extensions: ExecutionExtensions<Block>,
_phantom: PhantomData<RA>,
}
@@ -171,8 +173,9 @@ pub fn new_with_backend<B, E, Block, S, RA>(
Block: BlockT<Hash=H256>,
B: backend::LocalBackend<Block, Blake2Hasher>
{
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<B, E, Block, RA> BlockOf for Client<B, E, Block, RA> where
@@ -194,7 +197,7 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
executor: E,
build_genesis_storage: S,
fork_blocks: ForkBlocks<Block>,
execution_strategies: ExecutionStrategies
execution_extensions: ExecutionExtensions<Block>,
) -> error::Result<Self> {
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<B, E, Block, RA> Client<B, E, Block, RA> 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<Block> {
&self.execution_extensions
}
/// Get a reference to the state at a given block.
@@ -993,9 +996,9 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> 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<B, E, Block, RA> CallRuntimeAt<Block> for Client<B, E, Block, RA> where
context: ExecutionContext,
recorder: &Option<ProofRecorder<Block>>,
) -> error::Result<NativeOrEncoded<R>> {
let manager = match context {
ExecutionContext::BlockConstruction =>
self.execution_strategies.block_construction.get_manager(),
ExecutionContext::Syncing =>
self.execution_strategies.syncing.get_manager(),
ExecutionContext::Importing =>
self.execution_strategies.importing.get_manager(),
ExecutionContext::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<B, E, Block, RA> CallRuntimeAt<Block> for Client<B, E, Block, RA> where
initialize_block,
manager,
native_call,
offchain_extensions,
recorder,
capabilities.has(offchain::Capability::Keystore),
Some(extensions),
)
}
+6 -12
View File
@@ -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,
);
+4 -4
View File
@@ -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;
+10 -9
View File
@@ -63,7 +63,6 @@
//! LocalCallExecutor::new(
//! backend.clone(),
//! NativeExecutor::<LocalExecutor>::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;
+3 -3
View File
@@ -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;
+10 -12
View File
@@ -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<Block, B, Local> CallExecutor<Block, Blake2Hasher> for
method: &str,
call_data: &[u8],
strategy: ExecutionStrategy,
side_effects_handler: Option<OffchainExt>,
extensions: Option<Extensions>,
) -> ClientResult<Vec<u8>> {
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<Block, B, Local> CallExecutor<Block, Blake2Hasher> for
initialize_block: InitializeBlock<'a, Block>,
_manager: ExecutionManager<EM>,
native_call: Option<NC>,
side_effects_handler: Option<OffchainExt>,
recorder: &Option<ProofRecorder<Block>>,
enable_keystore: bool,
extensions: Option<Extensions>,
) -> ClientResult<NativeOrEncoded<R>> where ExecutionManager<EM>: Clone {
// there's no actual way/need to specify native/wasm execution strategy on light node
// => we can safely ignore passed values
@@ -137,9 +137,8 @@ impl<Block, B, Local> CallExecutor<Block, Blake2Hasher> 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<Block, B, Local> CallExecutor<Block, Blake2Hasher> for
_call_data: &[u8],
_manager: ExecutionManager<FF>,
_native_call: Option<NC>,
_side_effects_handler: Option<OffchainExt>,
_extensions: Option<Extensions>,
) -> ClientResult<(
NativeOrEncoded<R>,
(S::Transaction, <Blake2Hasher as Hasher>::Out),
@@ -313,7 +312,7 @@ mod tests {
_method: &str,
_call_data: &[u8],
_strategy: ExecutionStrategy,
_side_effects_handler: Option<OffchainExt>,
_extensions: Option<Extensions>,
) -> Result<Vec<u8>, ClientError> {
Ok(vec![42])
}
@@ -337,9 +336,8 @@ mod tests {
_initialize_block: InitializeBlock<'a, Block>,
_execution_manager: ExecutionManager<EM>,
_native_call: Option<NC>,
_side_effects_handler: Option<OffchainExt>,
_proof_recorder: &Option<ProofRecorder<Block>>,
_enable_keystore: bool,
_extensions: Option<Extensions>,
) -> ClientResult<NativeOrEncoded<R>> where ExecutionManager<EM>: Clone {
unreachable!()
}
@@ -363,7 +361,7 @@ mod tests {
_call_data: &[u8],
_manager: ExecutionManager<F>,
_native_call: Option<NC>,
_side_effects_handler: Option<OffchainExt>,
_extensions: Option<Extensions>,
) -> Result<
(
NativeOrEncoded<R>,
+1 -1
View File
@@ -68,7 +68,7 @@ pub fn new_light<B, S, GS, RA, E>(
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())
}