use std::sync::{Weak, Arc};
use codec::Decode;
use tet_core::{
ExecutionContext,
offchain::{self, OffchainExt, TransactionPoolExt},
};
use tp_keystore::{KeystoreExt, SyncCryptoStorePtr};
use tp_runtime::{
generic::BlockId,
traits,
};
use tp_state_machine::{ExecutionStrategy, ExecutionManager, DefaultHandler};
use externalities::Extensions;
use parking_lot::RwLock;
#[derive(Debug, Clone)]
pub struct ExecutionStrategies {
pub syncing: ExecutionStrategy,
pub importing: ExecutionStrategy,
pub block_construction: ExecutionStrategy,
pub offchain_worker: ExecutionStrategy,
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,
}
}
}
pub trait ExtensionsFactory: Send + Sync {
fn extensions_for(&self, capabilities: offchain::Capabilities) -> Extensions;
}
impl ExtensionsFactory for () {
fn extensions_for(&self, _capabilities: offchain::Capabilities) -> Extensions {
Extensions::new()
}
}
pub struct ExecutionExtensions<Block: traits::Block> {
strategies: ExecutionStrategies,
keystore: Option<SyncCryptoStorePtr>,
transaction_pool: RwLock<Option<Weak<dyn tp_transaction_pool::OffchainSubmitTransaction<Block>>>>,
extensions_factory: RwLock<Box<dyn ExtensionsFactory>>,
}
impl<Block: traits::Block> Default for ExecutionExtensions<Block> {
fn default() -> Self {
Self {
strategies: Default::default(),
keystore: None,
transaction_pool: RwLock::new(None),
extensions_factory: RwLock::new(Box::new(())),
}
}
}
impl<Block: traits::Block> ExecutionExtensions<Block> {
pub fn new(
strategies: ExecutionStrategies,
keystore: Option<SyncCryptoStorePtr>,
) -> Self {
let transaction_pool = RwLock::new(None);
let extensions_factory = Box::new(());
Self {
strategies,
keystore,
extensions_factory: RwLock::new(extensions_factory),
transaction_pool,
}
}
pub fn strategies(&self) -> &ExecutionStrategies {
&self.strategies
}
pub fn set_extensions_factory(&self, maker: Box<dyn ExtensionsFactory>) {
*self.extensions_factory.write() = maker;
}
pub fn register_transaction_pool<T>(&self, pool: &Arc<T>)
where T: tp_transaction_pool::OffchainSubmitTransaction<Block> + 'static
{
*self.transaction_pool.write() = Some(Arc::downgrade(&pool) as _);
}
pub fn extensions(&self, at: &BlockId<Block>, context: ExecutionContext) -> Extensions {
let capabilities = context.capabilities();
let mut extensions = self.extensions_factory.read().extensions_for(capabilities);
if capabilities.has(offchain::Capability::Keystore) {
if let Some(ref keystore) = self.keystore {
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)),
);
}
extensions
}
pub fn manager_and_extensions<E: std::fmt::Debug, R: codec::Codec>(
&self,
at: &BlockId<Block>,
context: ExecutionContext,
) -> (
ExecutionManager<DefaultHandler<R, E>>,
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(),
};
(manager, self.extensions(at, context))
}
}
struct TransactionPoolAdapter<Block: traits::Block> {
at: BlockId<Block>,
pool: Arc<dyn tp_transaction_pool::OffchainSubmitTransaction<Block>>,
}
impl<Block: traits::Block> offchain::TransactionPool for TransactionPoolAdapter<Block> {
fn submit_transaction(&mut self, data: Vec<u8>) -> Result<(), ()> {
let xt = match Block::Extrinsic::decode(&mut &*data) {
Ok(xt) => xt,
Err(e) => {
log::warn!("Unable to decode extrinsic: {:?}: {}", data, e);
return Err(());
},
};
self.pool.submit_at(&self.at, xt)
}
}