Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Offchain execution extensions #4145

Merged
merged 17 commits into from
Nov 22, 2019
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions bin/node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -472,7 +472,7 @@ impl system::offchain::CreateTransaction<Runtime, UncheckedExtrinsic> for Runtim
type Public = <Signature as traits::Verify>::Signer;
type Signature = Signature;

fn create_transaction<F: system::offchain::Signer<Self::Public, Self::Signature>>(
fn create_transaction<TSigner: system::offchain::Signer<Self::Public, Self::Signature>>(
call: Call,
public: Self::Public,
account: AccountId,
Expand All @@ -491,7 +491,7 @@ impl system::offchain::CreateTransaction<Runtime, UncheckedExtrinsic> 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)))
Expand Down
5 changes: 3 additions & 2 deletions client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ authors = ["Parity Technologies <admin@parity.io>"]
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"] }
Expand All @@ -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" }
Expand Down
1 change: 1 addition & 0 deletions client/api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"] }
Expand Down
2 changes: 1 addition & 1 deletion client/api/src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -27,7 +28,6 @@ use crate::{
blockchain::{
Backend as BlockchainBackend, well_known_cache_keys
},
offchain::OffchainStorage,
error,
light::RemoteBlockchain,
};
Expand Down
10 changes: 5 additions & 5 deletions client/api/src/call_executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,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;
Expand All @@ -49,7 +50,7 @@ where
method: &str,
call_data: &[u8],
strategy: ExecutionStrategy,
side_effects_handler: Option<OffchainExt>,
extensions: Option<Extensions>,
) -> Result<Vec<u8>, error::Error>;

/// Execute a contextual call on top of state in a block of a given hash.
Expand All @@ -76,9 +77,8 @@ where
initialize_block: InitializeBlock<'a, B>,
execution_manager: ExecutionManager<EM>,
native_call: Option<NC>,
side_effects_handler: Option<OffchainExt>,
proof_recorder: &Option<ProofRecorder<B>>,
enable_keystore: bool,
extensions: Option<Extensions>,
) -> error::Result<NativeOrEncoded<R>> where ExecutionManager<EM>: Clone;

/// Extract RuntimeVersion of given block
Expand All @@ -104,7 +104,7 @@ where
call_data: &[u8],
manager: ExecutionManager<F>,
native_call: Option<NC>,
side_effects_handler: Option<OffchainExt>,
extensions: Option<Extensions>,
) -> Result<
(
NativeOrEncoded<R>,
Expand Down
28 changes: 0 additions & 28 deletions client/api/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
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
Expand All @@ -39,33 +38,6 @@ pub type FinalityNotifications<Block> = mpsc::UnboundedReceiver<FinalityNotifica
/// This may be used as chain spec extension to filter out known, unwanted forks.
pub type ForkBlocks<Block> = Option<HashMap<NumberFor<Block>, <Block as BlockT>::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.
Expand Down
184 changes: 184 additions & 0 deletions client/api/src/execution_extensions.rs
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.

//! 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<Block: traits::Block> {
strategies: ExecutionStrategies,
keystore: Option<BareCryptoStorePtr>,
transaction_pool: RwLock<Option<Weak<dyn TransactionPool<Block>>>>,
}

impl<Block: traits::Block> Default for ExecutionExtensions<Block> {
fn default() -> Self {
Self {
strategies: Default::default(),
keystore: None,
transaction_pool: RwLock::new(None),
}
}
}

impl<Block: traits::Block> ExecutionExtensions<Block> {
/// Create new `ExecutionExtensions` given a `keystore` and `ExecutionStrategies`.
pub fn new(
strategies: ExecutionStrategies,
keystore: Option<BareCryptoStorePtr>,
) -> 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<dyn TransactionPool<Block>>) {
*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<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(),
};

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<Block: traits::Block> {
at: BlockId<Block>,
pool: Arc<dyn TransactionPool<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.what());
return Err(());
},
};

self.pool.submit_at(&self.at, xt)
}
}
18 changes: 9 additions & 9 deletions client/api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,25 +15,25 @@
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.

//! 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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this have been handled?

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};

Expand Down
Loading