Initial: Offchain Workers (#1942)

* Refactor state-machine stuff.

* Fix tests.

* WiP

* WiP2

* Service support for offchain workers.

* Service support for offchain workers.

* Testing offchain worker.

* Initial version working.

* Pass side effects in call.

* Pass OffchainExt in context.

* Submit extrinsics to the pool.

* Support inherents.

* Insert to inherents pool.

* Inserting to the pool asynchronously.

* Add test to offchain worker.

* Implement convenience syntax for modules.

* Dispatching offchain worker through executive.

* Fix offchain test.

* Remove offchain worker from timestamp.

* Update Cargo.lock.

* Address review comments.

* Use latest patch version for futures.

* Add CLI parameter for offchain worker.

* Fix compilation.

* Fix test.

* Fix extrinsics format for tests.

* Fix RPC test.

* Bump spec version.

* Fix executive.

* Fix support macro.

* Address grumbles.

* Bump runtime
This commit is contained in:
Tomasz Drwięga
2019-03-25 23:22:11 +01:00
committed by Gav Wood
parent 3ee0e69463
commit e2f5e40876
58 changed files with 1158 additions and 178 deletions
+32
View File
@@ -1974,6 +1974,7 @@ dependencies = [
"substrate-client 0.1.0", "substrate-client 0.1.0",
"substrate-consensus-aura-primitives 0.1.0", "substrate-consensus-aura-primitives 0.1.0",
"substrate-keyring 0.1.0", "substrate-keyring 0.1.0",
"substrate-offchain-primitives 0.1.0",
"substrate-primitives 0.1.0", "substrate-primitives 0.1.0",
] ]
@@ -2029,6 +2030,7 @@ dependencies = [
"srml-timestamp 0.1.0", "srml-timestamp 0.1.0",
"substrate-client 0.1.0", "substrate-client 0.1.0",
"substrate-consensus-aura-primitives 0.1.0", "substrate-consensus-aura-primitives 0.1.0",
"substrate-offchain-primitives 0.1.0",
"substrate-primitives 0.1.0", "substrate-primitives 0.1.0",
] ]
@@ -3978,6 +3980,33 @@ dependencies = [
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "substrate-offchain"
version = "0.1.0"
dependencies = [
"env_logger 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-codec 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"sr-primitives 0.1.0",
"substrate-client 0.1.0",
"substrate-consensus-common 0.1.0",
"substrate-inherents 0.1.0",
"substrate-offchain-primitives 0.1.0",
"substrate-primitives 0.1.0",
"substrate-test-client 0.1.0",
"substrate-transaction-pool 0.1.0",
"tokio 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "substrate-offchain-primitives"
version = "0.1.0"
dependencies = [
"sr-primitives 0.1.0",
"substrate-client 0.1.0",
]
[[package]] [[package]]
name = "substrate-panic-handler" name = "substrate-panic-handler"
version = "0.1.0" version = "0.1.0"
@@ -4110,8 +4139,10 @@ dependencies = [
"substrate-client-db 0.1.0", "substrate-client-db 0.1.0",
"substrate-consensus-common 0.1.0", "substrate-consensus-common 0.1.0",
"substrate-executor 0.1.0", "substrate-executor 0.1.0",
"substrate-inherents 0.1.0",
"substrate-keystore 0.1.0", "substrate-keystore 0.1.0",
"substrate-network 0.1.0", "substrate-network 0.1.0",
"substrate-offchain 0.1.0",
"substrate-primitives 0.1.0", "substrate-primitives 0.1.0",
"substrate-rpc-servers 0.1.0", "substrate-rpc-servers 0.1.0",
"substrate-telemetry 0.3.1", "substrate-telemetry 0.3.1",
@@ -4222,6 +4253,7 @@ dependencies = [
"substrate-executor 0.1.0", "substrate-executor 0.1.0",
"substrate-inherents 0.1.0", "substrate-inherents 0.1.0",
"substrate-keyring 0.1.0", "substrate-keyring 0.1.0",
"substrate-offchain-primitives 0.1.0",
"substrate-primitives 0.1.0", "substrate-primitives 0.1.0",
"substrate-test-client 0.1.0", "substrate-test-client 0.1.0",
] ]
@@ -20,7 +20,7 @@
// //
use std::{self, time, sync::Arc}; use std::{self, time, sync::Arc};
use log::{info, debug}; use log::{info, debug, warn};
use client::{ use client::{
self, error, Client as SubstrateClient, CallExecutor, self, error, Client as SubstrateClient, CallExecutor,
@@ -28,15 +28,14 @@ use client::{
}; };
use codec::Decode; use codec::Decode;
use consensus_common::{self, evaluation}; use consensus_common::{self, evaluation};
use primitives::{H256, Blake2Hasher}; use primitives::{H256, Blake2Hasher, ExecutionContext};
use runtime_primitives::traits::{ use runtime_primitives::traits::{
Block as BlockT, Hash as HashT, Header as HeaderT, ProvideRuntimeApi, AuthorityIdFor Block as BlockT, Hash as HashT, Header as HeaderT, ProvideRuntimeApi, AuthorityIdFor
}; };
use runtime_primitives::ExecutionContext;
use runtime_primitives::generic::BlockId; use runtime_primitives::generic::BlockId;
use runtime_primitives::ApplyError; use runtime_primitives::ApplyError;
use transaction_pool::txpool::{self, Pool as TransactionPool}; use transaction_pool::txpool::{self, Pool as TransactionPool};
use inherents::InherentData; use inherents::{InherentData, pool::InherentsPool};
use substrate_telemetry::{telemetry, CONSENSUS_INFO}; use substrate_telemetry::{telemetry, CONSENSUS_INFO};
/// Build new blocks. /// Build new blocks.
@@ -115,6 +114,8 @@ pub struct ProposerFactory<C, A> where A: txpool::ChainApi {
pub client: Arc<C>, pub client: Arc<C>,
/// The transaction pool. /// The transaction pool.
pub transaction_pool: Arc<TransactionPool<A>>, pub transaction_pool: Arc<TransactionPool<A>>,
/// The inherents pool
pub inherents_pool: Arc<InherentsPool<<A::Block as BlockT>::Extrinsic>>,
} }
impl<C, A> consensus_common::Environment<<C as AuthoringApi>::Block> for ProposerFactory<C, A> where impl<C, A> consensus_common::Environment<<C as AuthoringApi>::Block> for ProposerFactory<C, A> where
@@ -144,6 +145,7 @@ impl<C, A> consensus_common::Environment<<C as AuthoringApi>::Block> for Propose
parent_id: id, parent_id: id,
parent_number: *parent_header.number(), parent_number: *parent_header.number(),
transaction_pool: self.transaction_pool.clone(), transaction_pool: self.transaction_pool.clone(),
inherents_pool: self.inherents_pool.clone(),
now: Box::new(time::Instant::now), now: Box::new(time::Instant::now),
}; };
@@ -158,6 +160,7 @@ pub struct Proposer<Block: BlockT, C, A: txpool::ChainApi> {
parent_id: BlockId<Block>, parent_id: BlockId<Block>,
parent_number: <<Block as BlockT>::Header as HeaderT>::Number, parent_number: <<Block as BlockT>::Header as HeaderT>::Number,
transaction_pool: Arc<TransactionPool<A>>, transaction_pool: Arc<TransactionPool<A>>,
inherents_pool: Arc<InherentsPool<<Block as BlockT>::Extrinsic>>,
now: Box<Fn() -> time::Instant>, now: Box<Fn() -> time::Instant>,
} }
@@ -201,11 +204,23 @@ impl<Block, C, A> Proposer<Block, C, A> where
&self.parent_id, &self.parent_id,
inherent_data, inherent_data,
|block_builder| { |block_builder| {
// Add inherents from the internal pool
let inherents = self.inherents_pool.drain();
debug!("Pushing {} queued inherents.", inherents.len());
for i in inherents {
if let Err(e) = block_builder.push_extrinsic(i) {
warn!("Error while pushing inherent extrinsic from the pool: {:?}", e);
}
}
// proceed with transactions
let mut is_first = true; let mut is_first = true;
let mut skipped = 0; let mut skipped = 0;
let mut unqueue_invalid = Vec::new(); let mut unqueue_invalid = Vec::new();
let pending_iterator = self.transaction_pool.ready(); let pending_iterator = self.transaction_pool.ready();
debug!("Attempting to push transactions from the pool.");
for pending in pending_iterator { for pending in pending_iterator {
if (self.now)() > deadline { if (self.now)() > deadline {
debug!("Consensus deadline reached when pushing block transactions, proceeding with proposing."); debug!("Consensus deadline reached when pushing block transactions, proceeding with proposing.");
@@ -303,6 +318,7 @@ mod tests {
let proposer_factory = ProposerFactory { let proposer_factory = ProposerFactory {
client: client.clone(), client: client.clone(),
transaction_pool: txpool.clone(), transaction_pool: txpool.clone(),
inherents_pool: Default::default(),
}; };
let mut proposer = proposer_factory.init( let mut proposer = proposer_factory.init(
@@ -325,4 +341,32 @@ mod tests {
assert_eq!(txpool.ready().count(), 2); assert_eq!(txpool.ready().count(), 2);
} }
#[test]
fn should_include_inherents_from_the_pool() {
// given
let client = Arc::new(test_client::new());
let chain_api = transaction_pool::ChainApi::new(client.clone());
let txpool = Arc::new(TransactionPool::new(Default::default(), chain_api));
let inpool = Arc::new(InherentsPool::default());
let proposer_factory = ProposerFactory {
client: client.clone(),
transaction_pool: txpool.clone(),
inherents_pool: inpool.clone(),
};
inpool.add(extrinsic(0));
let proposer = proposer_factory.init(
&client.header(&BlockId::number(0)).unwrap().unwrap(),
&[]
).unwrap();
// when
let deadline = time::Duration::from_secs(3);
let block = proposer.propose(Default::default(), deadline).unwrap();
// then
assert_eq!(block.extrinsics().len(), 1);
}
} }
+13 -4
View File
@@ -419,11 +419,20 @@ where
service::Roles::FULL service::Roles::FULL
}; };
let exec = cli.execution_strategies;
config.execution_strategies = ExecutionStrategies { config.execution_strategies = ExecutionStrategies {
syncing: cli.syncing_execution.into(), syncing: exec.syncing_execution.into(),
importing: cli.importing_execution.into(), importing: exec.importing_execution.into(),
block_construction: cli.block_construction_execution.into(), block_construction: exec.block_construction_execution.into(),
other: cli.other_execution.into(), offchain_worker: exec.offchain_worker_execution.into(),
other: exec.other_execution.into(),
};
config.offchain_worker = match (cli.offchain_worker, role) {
(params::OffchainWorkerEnabled::WhenValidating, service::Roles::AUTHORITY) => true,
(params::OffchainWorkerEnabled::Always, _) => true,
(params::OffchainWorkerEnabled::Never, _) => false,
(params::OffchainWorkerEnabled::WhenValidating, _) => false,
}; };
config.roles = role; config.roles = role;
+84 -41
View File
@@ -53,6 +53,16 @@ impl Into<client::ExecutionStrategy> for ExecutionStrategy {
} }
} }
arg_enum! {
/// How to execute blocks
#[derive(Debug, Clone)]
pub enum OffchainWorkerEnabled {
Always,
Never,
WhenValidating,
}
}
/// Shared parameters used by all `CoreParams`. /// Shared parameters used by all `CoreParams`.
#[derive(Debug, StructOpt, Clone)] #[derive(Debug, StructOpt, Clone)]
pub struct SharedParams { pub struct SharedParams {
@@ -205,6 +215,70 @@ pub struct TransactionPoolParams {
pub pool_kbytes: usize, pub pool_kbytes: usize,
} }
/// Execution strategies parameters.
#[derive(Debug, StructOpt, Clone)]
pub struct ExecutionStrategies {
/// The means of execution used when calling into the runtime while syncing blocks.
#[structopt(
long = "syncing-execution",
value_name = "STRATEGY",
raw(
possible_values = "&ExecutionStrategy::variants()",
case_insensitive = "true",
default_value = r#""NativeElseWasm""#
)
)]
pub syncing_execution: ExecutionStrategy,
/// The means of execution used when calling into the runtime while importing blocks.
#[structopt(
long = "importing-execution",
value_name = "STRATEGY",
raw(
possible_values = "&ExecutionStrategy::variants()",
case_insensitive = "true",
default_value = r#""NativeElseWasm""#
)
)]
pub importing_execution: ExecutionStrategy,
/// The means of execution used when calling into the runtime while constructing blocks.
#[structopt(
long = "block-construction-execution",
value_name = "STRATEGY",
raw(
possible_values = "&ExecutionStrategy::variants()",
case_insensitive = "true",
default_value = r#""Wasm""#
)
)]
pub block_construction_execution: ExecutionStrategy,
/// The means of execution used when calling into the runtime while constructing blocks.
#[structopt(
long = "offchain-worker-execution",
value_name = "STRATEGY",
raw(
possible_values = "&ExecutionStrategy::variants()",
case_insensitive = "true",
default_value = r#""NativeWhenPossible""#
)
)]
pub offchain_worker_execution: ExecutionStrategy,
/// The means of execution used when calling into the runtime while not syncing, importing or constructing blocks.
#[structopt(
long = "other-execution",
value_name = "STRATEGY",
raw(
possible_values = "&ExecutionStrategy::variants()",
case_insensitive = "true",
default_value = r#""Wasm""#
)
)]
pub other_execution: ExecutionStrategy,
}
/// The `run` command used to run a node. /// The `run` command used to run a node.
#[derive(Debug, StructOpt, Clone)] #[derive(Debug, StructOpt, Clone)]
pub struct RunCmd { pub struct RunCmd {
@@ -266,53 +340,22 @@ pub struct RunCmd {
#[structopt(long = "telemetry-url", value_name = "URL VERBOSITY", parse(try_from_str = "parse_telemetry_endpoints"))] #[structopt(long = "telemetry-url", value_name = "URL VERBOSITY", parse(try_from_str = "parse_telemetry_endpoints"))]
pub telemetry_endpoints: Vec<(String, u8)>, pub telemetry_endpoints: Vec<(String, u8)>,
/// The means of execution used when calling into the runtime while syncing blocks. /// Should execute offchain workers on every block. By default it's only enabled for nodes that are authoring new
/// blocks.
#[structopt( #[structopt(
long = "syncing-execution", long = "offchain-worker",
value_name = "STRATEGY", value_name = "ENABLED",
raw( raw(
possible_values = "&ExecutionStrategy::variants()", possible_values = "&OffchainWorkerEnabled::variants()",
case_insensitive = "true", case_insensitive = "true",
default_value = r#""NativeElseWasm""# default_value = r#""WhenValidating""#
) )
)] )]
pub syncing_execution: ExecutionStrategy, pub offchain_worker: OffchainWorkerEnabled,
/// The means of execution used when calling into the runtime while importing blocks. #[allow(missing_docs)]
#[structopt( #[structopt(flatten)]
long = "importing-execution", pub execution_strategies: ExecutionStrategies,
value_name = "STRATEGY",
raw(
possible_values = "&ExecutionStrategy::variants()",
case_insensitive = "true",
default_value = r#""NativeElseWasm""#
)
)]
pub importing_execution: ExecutionStrategy,
/// The means of execution used when calling into the runtime while constructing blocks.
#[structopt(
long = "block-construction-execution",
value_name = "STRATEGY",
raw(
possible_values = "&ExecutionStrategy::variants()",
case_insensitive = "true",
default_value = r#""Wasm""#
)
)]
pub block_construction_execution: ExecutionStrategy,
/// The means of execution used when calling into the runtime while not syncing, importing or constructing blocks.
#[structopt(
long = "other-execution",
value_name = "STRATEGY",
raw(
possible_values = "&ExecutionStrategy::variants()",
case_insensitive = "true",
default_value = r#""Wasm""#
)
)]
pub other_execution: ExecutionStrategy,
#[allow(missing_docs)] #[allow(missing_docs)]
#[structopt(flatten)] #[structopt(flatten)]
@@ -17,15 +17,15 @@
use super::api::BlockBuilder as BlockBuilderApi; use super::api::BlockBuilder as BlockBuilderApi;
use std::vec::Vec; use std::vec::Vec;
use parity_codec::Encode; use parity_codec::Encode;
use crate::blockchain::HeaderBackend; use runtime_primitives::ApplyOutcome;
use runtime_primitives::generic::BlockId;
use runtime_primitives::traits::{ use runtime_primitives::traits::{
Header as HeaderT, Hash, Block as BlockT, One, HashFor, ProvideRuntimeApi, ApiRef Header as HeaderT, Hash, Block as BlockT, One, HashFor, ProvideRuntimeApi, ApiRef
}; };
use primitives::H256; use primitives::{H256, ExecutionContext};
use runtime_primitives::generic::BlockId; use crate::blockchain::HeaderBackend;
use crate::runtime_api::Core; use crate::runtime_api::Core;
use crate::error; use crate::error;
use runtime_primitives::{ApplyOutcome, ExecutionContext};
/// Utility for building new (valid) blocks from a stream of extrinsics. /// Utility for building new (valid) blocks from a stream of extrinsics.
+26 -7
View File
@@ -19,12 +19,12 @@ use parity_codec::{Encode, Decode};
use runtime_primitives::generic::BlockId; use runtime_primitives::generic::BlockId;
use runtime_primitives::traits::Block as BlockT; use runtime_primitives::traits::Block as BlockT;
use state_machine::{ use state_machine::{
self, OverlayedChanges, Ext, CodeExecutor, ExecutionManager, ExecutionStrategy self, OverlayedChanges, Ext, CodeExecutor, ExecutionManager, ExecutionStrategy, NeverOffchainExt,
}; };
use executor::{RuntimeVersion, RuntimeInfo, NativeVersion}; use executor::{RuntimeVersion, RuntimeInfo, NativeVersion};
use hash_db::Hasher; use hash_db::Hasher;
use trie::MemoryDB; use trie::MemoryDB;
use primitives::{H256, Blake2Hasher, NativeOrEncoded, NeverNativeValue}; use primitives::{H256, Blake2Hasher, NativeOrEncoded, NeverNativeValue, OffchainExt};
use crate::backend; use crate::backend;
use crate::error; use crate::error;
@@ -42,12 +42,15 @@ where
/// Execute a call to a contract on top of state in a block of given hash. /// Execute a call to a contract on top of state in a block of given hash.
/// ///
/// No changes are made. /// No changes are made.
fn call( fn call<
O: OffchainExt,
>(
&self, &self,
id: &BlockId<B>, id: &BlockId<B>,
method: &str, method: &str,
call_data: &[u8], call_data: &[u8],
strategy: ExecutionStrategy, strategy: ExecutionStrategy,
side_effects_handler: Option<&mut O>,
) -> Result<Vec<u8>, error::Error>; ) -> Result<Vec<u8>, error::Error>;
/// Execute a contextual call on top of state in a block of a given hash. /// Execute a contextual call on top of state in a block of a given hash.
@@ -56,6 +59,7 @@ where
/// Before executing the method, passed header is installed as the current header /// Before executing the method, passed header is installed as the current header
/// of the execution context. /// of the execution context.
fn contextual_call< fn contextual_call<
O: OffchainExt,
PB: Fn() -> error::Result<B::Header>, PB: Fn() -> error::Result<B::Header>,
EM: Fn( EM: Fn(
Result<NativeOrEncoded<R>, Self::Error>, Result<NativeOrEncoded<R>, Self::Error>,
@@ -73,6 +77,7 @@ where
prepare_environment_block: PB, prepare_environment_block: PB,
execution_manager: ExecutionManager<EM>, execution_manager: ExecutionManager<EM>,
native_call: Option<NC>, native_call: Option<NC>,
side_effects_handler: Option<&mut O>,
) -> error::Result<NativeOrEncoded<R>> where ExecutionManager<EM>: Clone; ) -> error::Result<NativeOrEncoded<R>> where ExecutionManager<EM>: Clone;
/// Extract RuntimeVersion of given block /// Extract RuntimeVersion of given block
@@ -84,6 +89,7 @@ where
/// ///
/// No changes are made. /// No changes are made.
fn call_at_state< fn call_at_state<
O: OffchainExt,
S: state_machine::Backend<H>, S: state_machine::Backend<H>,
F: FnOnce( F: FnOnce(
Result<NativeOrEncoded<R>, Self::Error>, Result<NativeOrEncoded<R>, Self::Error>,
@@ -98,6 +104,7 @@ where
call_data: &[u8], call_data: &[u8],
manager: ExecutionManager<F>, manager: ExecutionManager<F>,
native_call: Option<NC>, native_call: Option<NC>,
side_effects_handler: Option<&mut O>,
) -> Result<(NativeOrEncoded<R>, S::Transaction, Option<MemoryDB<H>>), error::Error>; ) -> Result<(NativeOrEncoded<R>, S::Transaction, Option<MemoryDB<H>>), error::Error>;
/// Execute a call to a contract on top of given state, gathering execution proof. /// Execute a call to a contract on top of given state, gathering execution proof.
@@ -140,7 +147,10 @@ pub struct LocalCallExecutor<B, E> {
impl<B, E> LocalCallExecutor<B, E> { impl<B, E> LocalCallExecutor<B, E> {
/// Creates new instance of local call executor. /// Creates new instance of local call executor.
pub fn new(backend: Arc<B>, executor: E) -> Self { pub fn new(backend: Arc<B>, executor: E) -> Self {
LocalCallExecutor { backend, executor } LocalCallExecutor {
backend,
executor,
}
} }
} }
@@ -161,17 +171,19 @@ where
{ {
type Error = E::Error; type Error = E::Error;
fn call(&self, fn call<O: OffchainExt>(&self,
id: &BlockId<Block>, id: &BlockId<Block>,
method: &str, method: &str,
call_data: &[u8], call_data: &[u8],
strategy: ExecutionStrategy strategy: ExecutionStrategy,
side_effects_handler: Option<&mut O>,
) -> error::Result<Vec<u8>> { ) -> error::Result<Vec<u8>> {
let mut changes = OverlayedChanges::default(); let mut changes = OverlayedChanges::default();
let state = self.backend.state_at(*id)?; let state = self.backend.state_at(*id)?;
let return_data = state_machine::new( let return_data = state_machine::new(
&state, &state,
self.backend.changes_trie_storage(), self.backend.changes_trie_storage(),
side_effects_handler,
&mut changes, &mut changes,
&self.executor, &self.executor,
method, method,
@@ -187,6 +199,7 @@ where
} }
fn contextual_call< fn contextual_call<
O: OffchainExt,
PB: Fn() -> error::Result<Block::Header>, PB: Fn() -> error::Result<Block::Header>,
EM: Fn( EM: Fn(
Result<NativeOrEncoded<R>, Self::Error>, Result<NativeOrEncoded<R>, Self::Error>,
@@ -204,6 +217,7 @@ where
prepare_environment_block: PB, prepare_environment_block: PB,
execution_manager: ExecutionManager<EM>, execution_manager: ExecutionManager<EM>,
native_call: Option<NC>, native_call: Option<NC>,
mut side_effects_handler: Option<&mut O>,
) -> Result<NativeOrEncoded<R>, error::Error> where ExecutionManager<EM>: Clone { ) -> Result<NativeOrEncoded<R>, error::Error> where ExecutionManager<EM>: Clone {
let state = self.backend.state_at(*at)?; let state = self.backend.state_at(*at)?;
if method != "Core_initialise_block" && initialised_block.map(|id| id != *at).unwrap_or(true) { if method != "Core_initialise_block" && initialised_block.map(|id| id != *at).unwrap_or(true) {
@@ -211,6 +225,7 @@ where
state_machine::new( state_machine::new(
&state, &state,
self.backend.changes_trie_storage(), self.backend.changes_trie_storage(),
side_effects_handler.as_mut().map(|x| &mut **x),
changes, changes,
&self.executor, &self.executor,
"Core_initialise_block", "Core_initialise_block",
@@ -226,6 +241,7 @@ where
let result = state_machine::new( let result = state_machine::new(
&state, &state,
self.backend.changes_trie_storage(), self.backend.changes_trie_storage(),
side_effects_handler,
changes, changes,
&self.executor, &self.executor,
method, method,
@@ -248,12 +264,13 @@ where
fn runtime_version(&self, id: &BlockId<Block>) -> error::Result<RuntimeVersion> { fn runtime_version(&self, id: &BlockId<Block>) -> error::Result<RuntimeVersion> {
let mut overlay = OverlayedChanges::default(); let mut overlay = OverlayedChanges::default();
let state = self.backend.state_at(*id)?; let state = self.backend.state_at(*id)?;
let mut ext = Ext::new(&mut overlay, &state, self.backend.changes_trie_storage()); let mut ext = Ext::new(&mut overlay, &state, self.backend.changes_trie_storage(), NeverOffchainExt::new());
self.executor.runtime_version(&mut ext) self.executor.runtime_version(&mut ext)
.ok_or(error::ErrorKind::VersionInvalid.into()) .ok_or(error::ErrorKind::VersionInvalid.into())
} }
fn call_at_state< fn call_at_state<
O: OffchainExt,
S: state_machine::Backend<Blake2Hasher>, S: state_machine::Backend<Blake2Hasher>,
F: FnOnce( F: FnOnce(
Result<NativeOrEncoded<R>, Self::Error>, Result<NativeOrEncoded<R>, Self::Error>,
@@ -268,10 +285,12 @@ where
call_data: &[u8], call_data: &[u8],
manager: ExecutionManager<F>, manager: ExecutionManager<F>,
native_call: Option<NC>, native_call: Option<NC>,
side_effects_handler: Option<&mut O>,
) -> error::Result<(NativeOrEncoded<R>, S::Transaction, Option<MemoryDB<Blake2Hasher>>)> { ) -> error::Result<(NativeOrEncoded<R>, S::Transaction, Option<MemoryDB<Blake2Hasher>>)> {
state_machine::new( state_machine::new(
state, state,
self.backend.changes_trie_storage(), self.backend.changes_trie_storage(),
side_effects_handler,
changes, changes,
&self.executor, &self.executor,
method, method,
+21 -8
View File
@@ -33,9 +33,9 @@ use runtime_primitives::traits::{
Block as BlockT, Header as HeaderT, Zero, As, NumberFor, CurrentHeight, BlockNumberToHash, Block as BlockT, Header as HeaderT, Zero, As, NumberFor, CurrentHeight, BlockNumberToHash,
ApiRef, ProvideRuntimeApi, Digest, DigestItem, AuthorityIdFor ApiRef, ProvideRuntimeApi, Digest, DigestItem, AuthorityIdFor
}; };
use runtime_primitives::{BuildStorage, ExecutionContext}; use runtime_primitives::BuildStorage;
use crate::runtime_api::{CallRuntimeAt, ConstructRuntimeApi}; use crate::runtime_api::{CallRuntimeAt, ConstructRuntimeApi};
use primitives::{Blake2Hasher, H256, ChangesTrieConfiguration, convert_hash, NeverNativeValue}; use primitives::{Blake2Hasher, H256, ChangesTrieConfiguration, convert_hash, NeverNativeValue, ExecutionContext};
use primitives::storage::{StorageKey, StorageData}; use primitives::storage::{StorageKey, StorageData};
use primitives::storage::well_known_keys; use primitives::storage::well_known_keys;
use parity_codec::{Encode, Decode}; use parity_codec::{Encode, Decode};
@@ -43,7 +43,7 @@ use state_machine::{
DBValue, Backend as StateBackend, CodeExecutor, ChangesTrieAnchorBlockId, DBValue, Backend as StateBackend, CodeExecutor, ChangesTrieAnchorBlockId,
ExecutionStrategy, ExecutionManager, prove_read, ExecutionStrategy, ExecutionManager, prove_read,
ChangesTrieRootsStorage, ChangesTrieStorage, ChangesTrieRootsStorage, ChangesTrieStorage,
key_changes, key_changes_proof, OverlayedChanges, key_changes, key_changes_proof, OverlayedChanges, NeverOffchainExt,
}; };
use hash_db::Hasher; use hash_db::Hasher;
@@ -84,6 +84,8 @@ pub struct ExecutionStrategies {
pub importing: ExecutionStrategy, pub importing: ExecutionStrategy,
/// Execution strategy used when constructing blocks. /// Execution strategy used when constructing blocks.
pub block_construction: ExecutionStrategy, pub block_construction: ExecutionStrategy,
/// Execution strategy used for offchain workers.
pub offchain_worker: ExecutionStrategy,
/// Execution strategy used in other cases. /// Execution strategy used in other cases.
pub other: ExecutionStrategy, pub other: ExecutionStrategy,
} }
@@ -94,6 +96,7 @@ impl Default for ExecutionStrategies {
syncing: ExecutionStrategy::NativeElseWasm, syncing: ExecutionStrategy::NativeElseWasm,
importing: ExecutionStrategy::NativeElseWasm, importing: ExecutionStrategy::NativeElseWasm,
block_construction: ExecutionStrategy::AlwaysWasm, block_construction: ExecutionStrategy::AlwaysWasm,
offchain_worker: ExecutionStrategy::NativeWhenPossible,
other: ExecutionStrategy::NativeElseWasm, other: ExecutionStrategy::NativeElseWasm,
} }
} }
@@ -343,7 +346,7 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
pub fn authorities_at(&self, id: &BlockId<Block>) -> error::Result<Vec<AuthorityIdFor<Block>>> { pub fn authorities_at(&self, id: &BlockId<Block>) -> error::Result<Vec<AuthorityIdFor<Block>>> {
match self.backend.blockchain().cache().and_then(|cache| cache.authorities_at(*id)) { match self.backend.blockchain().cache().and_then(|cache| cache.authorities_at(*id)) {
Some(cached_value) => Ok(cached_value), Some(cached_value) => Ok(cached_value),
None => self.executor.call(id, "Core_authorities", &[], ExecutionStrategy::NativeElseWasm) None => self.executor.call(id, "Core_authorities", &[], ExecutionStrategy::NativeElseWasm, NeverOffchainExt::new())
.and_then(|r| Vec::<AuthorityIdFor<Block>>::decode(&mut &r[..]) .and_then(|r| Vec::<AuthorityIdFor<Block>>::decode(&mut &r[..])
.ok_or_else(|| error::ErrorKind::InvalidAuthoritiesSet.into())) .ok_or_else(|| error::ErrorKind::InvalidAuthoritiesSet.into()))
} }
@@ -871,7 +874,7 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
}), }),
} }
}; };
let (_, storage_update, changes_update) = self.executor.call_at_state::<_, _, NeverNativeValue, fn() -> _>( let (_, storage_update, changes_update) = self.executor.call_at_state::<_, _, _, NeverNativeValue, fn() -> _>(
transaction_state, transaction_state,
&mut overlay, &mut overlay,
"Core_execute_block", "Core_execute_block",
@@ -881,6 +884,7 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
_ => get_execution_manager(self.execution_strategies().importing), _ => get_execution_manager(self.execution_strategies().importing),
}, },
None, None,
NeverOffchainExt::new(),
)?; )?;
overlay.commit_prospective(); overlay.commit_prospective();
@@ -1339,7 +1343,8 @@ impl<B, E, Block, RA> CallRuntimeAt<Block> for Client<B, E, Block, RA> where
Block: BlockT<Hash=H256> Block: BlockT<Hash=H256>
{ {
fn call_api_at< fn call_api_at<
R: Encode + Decode + PartialEq, NC: FnOnce() -> result::Result<R, &'static str> + UnwindSafe R: Encode + Decode + PartialEq,
NC: FnOnce() -> result::Result<R, &'static str> + UnwindSafe,
>( >(
&self, &self,
at: &BlockId<Block>, at: &BlockId<Block>,
@@ -1348,15 +1353,22 @@ impl<B, E, Block, RA> CallRuntimeAt<Block> for Client<B, E, Block, RA> where
changes: &mut OverlayedChanges, changes: &mut OverlayedChanges,
initialised_block: &mut Option<BlockId<Block>>, initialised_block: &mut Option<BlockId<Block>>,
native_call: Option<NC>, native_call: Option<NC>,
context: ExecutionContext context: ExecutionContext,
) -> error::Result<NativeOrEncoded<R>> { ) -> error::Result<NativeOrEncoded<R>> {
let manager = match context { let manager = match context {
ExecutionContext::BlockConstruction => self.execution_strategies.block_construction.get_manager(), ExecutionContext::BlockConstruction => self.execution_strategies.block_construction.get_manager(),
ExecutionContext::Syncing => self.execution_strategies.syncing.get_manager(), ExecutionContext::Syncing => self.execution_strategies.syncing.get_manager(),
ExecutionContext::Importing => self.execution_strategies.importing.get_manager(), ExecutionContext::Importing => self.execution_strategies.importing.get_manager(),
ExecutionContext::OffchainWorker(_) => self.execution_strategies.offchain_worker.get_manager(),
ExecutionContext::Other => self.execution_strategies.other.get_manager(), ExecutionContext::Other => self.execution_strategies.other.get_manager(),
}; };
self.executor.contextual_call::<_, fn(_,_) -> _,_,_>(
let mut offchain_extensions = match context {
ExecutionContext::OffchainWorker(ext) => Some(ext),
_ => None,
};
self.executor.contextual_call::<_, _, fn(_,_) -> _,_,_>(
at, at,
function, function,
&args, &args,
@@ -1365,6 +1377,7 @@ impl<B, E, Block, RA> CallRuntimeAt<Block> for Client<B, E, Block, RA> where
|| self.prepare_environment_block(at), || self.prepare_environment_block(at),
manager, manager,
native_call, native_call,
offchain_extensions.as_mut(),
) )
} }
+6
View File
@@ -90,6 +90,7 @@ mod tests {
state_machine::new( state_machine::new(
backend, backend,
Some(&InMemoryChangesTrieStorage::new()), Some(&InMemoryChangesTrieStorage::new()),
state_machine::NeverOffchainExt::new(),
&mut overlay, &mut overlay,
&executor(), &executor(),
"Core_initialise_block", "Core_initialise_block",
@@ -102,6 +103,7 @@ mod tests {
state_machine::new( state_machine::new(
backend, backend,
Some(&InMemoryChangesTrieStorage::new()), Some(&InMemoryChangesTrieStorage::new()),
state_machine::NeverOffchainExt::new(),
&mut overlay, &mut overlay,
&executor(), &executor(),
"BlockBuilder_apply_extrinsic", "BlockBuilder_apply_extrinsic",
@@ -114,6 +116,7 @@ mod tests {
let (ret_data, _, _) = state_machine::new( let (ret_data, _, _) = state_machine::new(
backend, backend,
Some(&InMemoryChangesTrieStorage::new()), Some(&InMemoryChangesTrieStorage::new()),
state_machine::NeverOffchainExt::new(),
&mut overlay, &mut overlay,
&executor(), &executor(),
"BlockBuilder_finalise_block", "BlockBuilder_finalise_block",
@@ -160,6 +163,7 @@ mod tests {
let _ = state_machine::new( let _ = state_machine::new(
&backend, &backend,
Some(&InMemoryChangesTrieStorage::new()), Some(&InMemoryChangesTrieStorage::new()),
state_machine::NeverOffchainExt::new(),
&mut overlay, &mut overlay,
&executor(), &executor(),
"Core_execute_block", "Core_execute_block",
@@ -188,6 +192,7 @@ mod tests {
let _ = state_machine::new( let _ = state_machine::new(
&backend, &backend,
Some(&InMemoryChangesTrieStorage::new()), Some(&InMemoryChangesTrieStorage::new()),
state_machine::NeverOffchainExt::new(),
&mut overlay, &mut overlay,
&executor(), &executor(),
"Core_execute_block", "Core_execute_block",
@@ -216,6 +221,7 @@ mod tests {
let r = state_machine::new( let r = state_machine::new(
&backend, &backend,
Some(&InMemoryChangesTrieStorage::new()), Some(&InMemoryChangesTrieStorage::new()),
state_machine::NeverOffchainExt::new(),
&mut overlay, &mut overlay,
&Executor::new(None), &Executor::new(None),
"Core_execute_block", "Core_execute_block",
@@ -21,11 +21,11 @@ use std::{collections::HashSet, sync::Arc, panic::UnwindSafe, result, marker::Ph
use futures::{IntoFuture, Future}; use futures::{IntoFuture, Future};
use parity_codec::{Encode, Decode}; use parity_codec::{Encode, Decode};
use primitives::{H256, Blake2Hasher, convert_hash, NativeOrEncoded}; use primitives::{H256, Blake2Hasher, convert_hash, NativeOrEncoded, OffchainExt};
use runtime_primitives::generic::BlockId; use runtime_primitives::generic::BlockId;
use runtime_primitives::traits::{As, Block as BlockT, Header as HeaderT}; use runtime_primitives::traits::{As, Block as BlockT, Header as HeaderT};
use state_machine::{self, Backend as StateBackend, CodeExecutor, OverlayedChanges, ExecutionStrategy, use state_machine::{self, Backend as StateBackend, CodeExecutor, OverlayedChanges, ExecutionStrategy,
create_proof_check_backend, execution_proof_check_on_trie_backend, ExecutionManager}; create_proof_check_backend, execution_proof_check_on_trie_backend, ExecutionManager, NeverOffchainExt};
use hash_db::Hasher; use hash_db::Hasher;
use crate::backend::RemoteBackend; use crate::backend::RemoteBackend;
@@ -80,7 +80,16 @@ where
{ {
type Error = ClientError; type Error = ClientError;
fn call(&self, id: &BlockId<Block>, method: &str, call_data: &[u8], _strategy: ExecutionStrategy) fn call<
O: OffchainExt,
>(
&self,
id: &BlockId<Block>,
method: &str,
call_data: &[u8],
_strategy: ExecutionStrategy,
_side_effects_handler: Option<&mut O>,
)
-> ClientResult<Vec<u8>> { -> ClientResult<Vec<u8>> {
let block_hash = self.blockchain.expect_block_hash_from_id(id)?; let block_hash = self.blockchain.expect_block_hash_from_id(id)?;
let block_header = self.blockchain.expect_header(id.clone())?; let block_header = self.blockchain.expect_header(id.clone())?;
@@ -95,6 +104,7 @@ where
} }
fn contextual_call< fn contextual_call<
O: OffchainExt,
PB: Fn() -> ClientResult<Block::Header>, PB: Fn() -> ClientResult<Block::Header>,
EM: Fn( EM: Fn(
Result<NativeOrEncoded<R>, Self::Error>, Result<NativeOrEncoded<R>, Self::Error>,
@@ -112,22 +122,24 @@ where
_prepare_environment_block: PB, _prepare_environment_block: PB,
execution_manager: ExecutionManager<EM>, execution_manager: ExecutionManager<EM>,
_native_call: Option<NC>, _native_call: Option<NC>,
side_effects_handler: Option<&mut O>,
) -> ClientResult<NativeOrEncoded<R>> where ExecutionManager<EM>: Clone { ) -> ClientResult<NativeOrEncoded<R>> where ExecutionManager<EM>: Clone {
// it is only possible to execute contextual call if changes are empty // it is only possible to execute contextual call if changes are empty
if !changes.is_empty() || initialised_block.is_some() { if !changes.is_empty() || initialised_block.is_some() {
return Err(ClientErrorKind::NotAvailableOnLightClient.into()); return Err(ClientErrorKind::NotAvailableOnLightClient.into());
} }
self.call(at, method, call_data, (&execution_manager).into()).map(NativeOrEncoded::Encoded) self.call(at, method, call_data, (&execution_manager).into(), side_effects_handler).map(NativeOrEncoded::Encoded)
} }
fn runtime_version(&self, id: &BlockId<Block>) -> ClientResult<RuntimeVersion> { fn runtime_version(&self, id: &BlockId<Block>) -> ClientResult<RuntimeVersion> {
let call_result = self.call(id, "version", &[], ExecutionStrategy::NativeElseWasm)?; let call_result = self.call(id, "version", &[], ExecutionStrategy::NativeElseWasm, NeverOffchainExt::new())?;
RuntimeVersion::decode(&mut call_result.as_slice()) RuntimeVersion::decode(&mut call_result.as_slice())
.ok_or_else(|| ClientErrorKind::VersionInvalid.into()) .ok_or_else(|| ClientErrorKind::VersionInvalid.into())
} }
fn call_at_state< fn call_at_state<
O: OffchainExt,
S: StateBackend<Blake2Hasher>, S: StateBackend<Blake2Hasher>,
FF: FnOnce( FF: FnOnce(
Result<NativeOrEncoded<R>, Self::Error>, Result<NativeOrEncoded<R>, Self::Error>,
@@ -142,6 +154,7 @@ where
_call_data: &[u8], _call_data: &[u8],
_m: ExecutionManager<FF>, _m: ExecutionManager<FF>,
_native_call: Option<NC>, _native_call: Option<NC>,
_side_effects_handler: Option<&mut O>,
) -> ClientResult<(NativeOrEncoded<R>, S::Transaction, Option<MemoryDB<Blake2Hasher>>)> { ) -> ClientResult<(NativeOrEncoded<R>, S::Transaction, Option<MemoryDB<Blake2Hasher>>)> {
Err(ClientErrorKind::NotAvailableOnLightClient.into()) Err(ClientErrorKind::NotAvailableOnLightClient.into())
} }
@@ -201,15 +214,24 @@ impl<Block, B, Remote, Local> CallExecutor<Block, Blake2Hasher> for
{ {
type Error = ClientError; type Error = ClientError;
fn call(&self, id: &BlockId<Block>, method: &str, call_data: &[u8], strategy: ExecutionStrategy) fn call<
-> ClientResult<Vec<u8>> { O: OffchainExt,
>(
&self,
id: &BlockId<Block>,
method: &str,
call_data: &[u8],
strategy: ExecutionStrategy,
side_effects_handler: Option<&mut O>,
) -> ClientResult<Vec<u8>> {
match self.backend.is_local_state_available(id) { match self.backend.is_local_state_available(id) {
true => self.local.call(id, method, call_data, strategy), true => self.local.call(id, method, call_data, strategy, side_effects_handler),
false => self.remote.call(id, method, call_data, strategy), false => self.remote.call(id, method, call_data, strategy, side_effects_handler),
} }
} }
fn contextual_call< fn contextual_call<
O: OffchainExt,
PB: Fn() -> ClientResult<Block::Header>, PB: Fn() -> ClientResult<Block::Header>,
EM: Fn( EM: Fn(
Result<NativeOrEncoded<R>, Self::Error>, Result<NativeOrEncoded<R>, Self::Error>,
@@ -227,12 +249,14 @@ impl<Block, B, Remote, Local> CallExecutor<Block, Blake2Hasher> for
prepare_environment_block: PB, prepare_environment_block: PB,
_manager: ExecutionManager<EM>, _manager: ExecutionManager<EM>,
native_call: Option<NC>, native_call: Option<NC>,
side_effects_handler: Option<&mut O>,
) -> ClientResult<NativeOrEncoded<R>> where ExecutionManager<EM>: Clone { ) -> ClientResult<NativeOrEncoded<R>> where ExecutionManager<EM>: Clone {
// there's no actual way/need to specify native/wasm execution strategy on light node // there's no actual way/need to specify native/wasm execution strategy on light node
// => we can safely ignore passed values // => we can safely ignore passed values
match self.backend.is_local_state_available(at) { match self.backend.is_local_state_available(at) {
true => CallExecutor::contextual_call::< true => CallExecutor::contextual_call::<
_,
_, _,
fn( fn(
Result<NativeOrEncoded<R>, Local::Error>, Result<NativeOrEncoded<R>, Local::Error>,
@@ -250,8 +274,10 @@ impl<Block, B, Remote, Local> CallExecutor<Block, Blake2Hasher> for
prepare_environment_block, prepare_environment_block,
ExecutionManager::NativeWhenPossible, ExecutionManager::NativeWhenPossible,
native_call, native_call,
side_effects_handler,
).map_err(|e| ClientErrorKind::Execution(Box::new(e.to_string())).into()), ).map_err(|e| ClientErrorKind::Execution(Box::new(e.to_string())).into()),
false => CallExecutor::contextual_call::< false => CallExecutor::contextual_call::<
_,
_, _,
fn( fn(
Result<NativeOrEncoded<R>, Remote::Error>, Result<NativeOrEncoded<R>, Remote::Error>,
@@ -269,6 +295,7 @@ impl<Block, B, Remote, Local> CallExecutor<Block, Blake2Hasher> for
prepare_environment_block, prepare_environment_block,
ExecutionManager::NativeWhenPossible, ExecutionManager::NativeWhenPossible,
native_call, native_call,
side_effects_handler,
).map_err(|e| ClientErrorKind::Execution(Box::new(e.to_string())).into()), ).map_err(|e| ClientErrorKind::Execution(Box::new(e.to_string())).into()),
} }
} }
@@ -281,6 +308,7 @@ impl<Block, B, Remote, Local> CallExecutor<Block, Blake2Hasher> for
} }
fn call_at_state< fn call_at_state<
O: OffchainExt,
S: StateBackend<Blake2Hasher>, S: StateBackend<Blake2Hasher>,
FF: FnOnce( FF: FnOnce(
Result<NativeOrEncoded<R>, Self::Error>, Result<NativeOrEncoded<R>, Self::Error>,
@@ -295,11 +323,13 @@ impl<Block, B, Remote, Local> CallExecutor<Block, Blake2Hasher> for
call_data: &[u8], call_data: &[u8],
_manager: ExecutionManager<FF>, _manager: ExecutionManager<FF>,
native_call: Option<NC>, native_call: Option<NC>,
side_effects_handler: Option<&mut O>,
) -> ClientResult<(NativeOrEncoded<R>, S::Transaction, Option<MemoryDB<Blake2Hasher>>)> { ) -> ClientResult<(NativeOrEncoded<R>, S::Transaction, Option<MemoryDB<Blake2Hasher>>)> {
// there's no actual way/need to specify native/wasm execution strategy on light node // there's no actual way/need to specify native/wasm execution strategy on light node
// => we can safely ignore passed values // => we can safely ignore passed values
CallExecutor::call_at_state::< CallExecutor::call_at_state::<
_,
_, _,
fn( fn(
Result<NativeOrEncoded<R>, Remote::Error>, Result<NativeOrEncoded<R>, Remote::Error>,
@@ -315,6 +345,7 @@ impl<Block, B, Remote, Local> CallExecutor<Block, Blake2Hasher> for
call_data, call_data,
ExecutionManager::NativeWhenPossible, ExecutionManager::NativeWhenPossible,
native_call, native_call,
side_effects_handler,
).map_err(|e| ClientErrorKind::Execution(Box::new(e.to_string())).into()) ).map_err(|e| ClientErrorKind::Execution(Box::new(e.to_string())).into())
} }
@@ -509,7 +540,7 @@ mod tests {
let local_executor = RemoteCallExecutor::new(Arc::new(backend.blockchain().clone()), Arc::new(OkCallFetcher::new(vec![1]))); let local_executor = RemoteCallExecutor::new(Arc::new(backend.blockchain().clone()), Arc::new(OkCallFetcher::new(vec![1])));
let remote_executor = RemoteCallExecutor::new(Arc::new(backend.blockchain().clone()), Arc::new(OkCallFetcher::new(vec![2]))); let remote_executor = RemoteCallExecutor::new(Arc::new(backend.blockchain().clone()), Arc::new(OkCallFetcher::new(vec![2])));
let remote_or_local = RemoteOrLocalCallExecutor::new(backend, remote_executor, local_executor); let remote_or_local = RemoteOrLocalCallExecutor::new(backend, remote_executor, local_executor);
assert_eq!(remote_or_local.call(&BlockId::Number(0), "test_method", &[], ExecutionStrategy::NativeElseWasm).unwrap(), vec![1]); assert_eq!(remote_or_local.call(&BlockId::Number(0), "test_method", &[], ExecutionStrategy::NativeElseWasm, NeverOffchainExt::new()).unwrap(), vec![1]);
assert_eq!(remote_or_local.call(&BlockId::Number(1), "test_method", &[], ExecutionStrategy::NativeElseWasm).unwrap(), vec![2]); assert_eq!(remote_or_local.call(&BlockId::Number(1), "test_method", &[], ExecutionStrategy::NativeElseWasm, NeverOffchainExt::new()).unwrap(), vec![2]);
} }
} }
+10 -4
View File
@@ -24,10 +24,12 @@ pub use state_machine::OverlayedChanges;
pub use primitives::NativeOrEncoded; pub use primitives::NativeOrEncoded;
#[doc(hidden)] #[doc(hidden)]
pub use runtime_primitives::{ pub use runtime_primitives::{
traits::{AuthorityIdFor, Block as BlockT, GetNodeBlockType, GetRuntimeBlockType, ApiRef, RuntimeApiInfo}, traits::{AuthorityIdFor, Block as BlockT, GetNodeBlockType, GetRuntimeBlockType, Header as HeaderT, ApiRef, RuntimeApiInfo},
generic::BlockId, transaction_validity::TransactionValidity, ExecutionContext, generic::BlockId, transaction_validity::TransactionValidity,
}; };
#[doc(hidden)] #[doc(hidden)]
pub use primitives::{ExecutionContext, OffchainExt};
#[doc(hidden)]
pub use runtime_version::{ApiId, RuntimeVersion, ApisVec, create_apis_vec}; pub use runtime_version::{ApiId, RuntimeVersion, ApisVec, create_apis_vec};
#[doc(hidden)] #[doc(hidden)]
pub use rstd::{slice, mem}; pub use rstd::{slice, mem};
@@ -91,7 +93,10 @@ pub trait ApiExt<Block: BlockT> {
pub trait CallRuntimeAt<Block: BlockT> { pub trait CallRuntimeAt<Block: BlockT> {
/// Calls the given api function with the given encoded arguments at the given block /// Calls the given api function with the given encoded arguments at the given block
/// and returns the encoded result. /// and returns the encoded result.
fn call_api_at<R: Encode + Decode + PartialEq, NC: FnOnce() -> result::Result<R, &'static str> + UnwindSafe>( fn call_api_at<
R: Encode + Decode + PartialEq,
NC: FnOnce() -> result::Result<R, &'static str> + UnwindSafe,
>(
&self, &self,
at: &BlockId<Block>, at: &BlockId<Block>,
function: &'static str, function: &'static str,
@@ -99,7 +104,7 @@ pub trait CallRuntimeAt<Block: BlockT> {
changes: &mut OverlayedChanges, changes: &mut OverlayedChanges,
initialised_block: &mut Option<BlockId<Block>>, initialised_block: &mut Option<BlockId<Block>>,
native_call: Option<NC>, native_call: Option<NC>,
context: ExecutionContext context: ExecutionContext,
) -> error::Result<NativeOrEncoded<R>>; ) -> error::Result<NativeOrEncoded<R>>;
/// Returns the runtime version at the given block. /// Returns the runtime version at the given block.
@@ -132,3 +137,4 @@ decl_runtime_apis! {
fn validate_transaction(tx: <Block as BlockT>::Extrinsic) -> TransactionValidity; fn validate_transaction(tx: <Block as BlockT>::Extrinsic) -> TransactionValidity;
} }
} }
@@ -520,6 +520,15 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
Ok(0) Ok(0)
}, },
ext_submit_extrinsic(msg_data: *const u8, len: u32) => {
let extrinsic = this.memory.get(msg_data, len as usize)
.map_err(|_| UserError("OOB while ext_submit_extrinsic: wasm"))?;
this.ext.submit_extrinsic(extrinsic)
.map_err(|_| UserError("Calling unavailable API ext_submit_extrinsic: wasm"))?;
Ok(())
},
ext_sandbox_instantiate( ext_sandbox_instantiate(
dispatch_thunk_idx: usize, dispatch_thunk_idx: usize,
wasm_ptr: *const u8, wasm_ptr: *const u8,
+1 -2
View File
@@ -35,8 +35,7 @@ use std::collections::{HashMap, HashSet};
use std::result; use std::result;
use runtime_primitives::traits::{ApiRef, ProvideRuntimeApi}; use runtime_primitives::traits::{ApiRef, ProvideRuntimeApi};
use runtime_primitives::generic::BlockId; use runtime_primitives::generic::BlockId;
use runtime_primitives::ExecutionContext; use substrate_primitives::{NativeOrEncoded, ExecutionContext};
use substrate_primitives::NativeOrEncoded;
use authorities::AuthoritySet; use authorities::AuthoritySet;
use consensus_changes::ConsensusChanges; use consensus_changes::ConsensusChanges;
+4
View File
@@ -31,6 +31,7 @@
//! information on how that is done. //! information on how that is done.
#![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(not(feature = "std"), no_std)]
#![warn(missing_docs)]
use parity_codec as codec; use parity_codec as codec;
use codec::{Encode, Decode}; use codec::{Encode, Decode};
@@ -43,6 +44,9 @@ use parking_lot::RwLock;
#[cfg(feature = "std")] #[cfg(feature = "std")]
use std::{sync::Arc, format}; use std::{sync::Arc, format};
#[cfg(feature = "std")]
pub mod pool;
pub use runtime_primitives::RuntimeString; pub use runtime_primitives::RuntimeString;
/// An identifier for an inherent. /// An identifier for an inherent.
+75
View File
@@ -0,0 +1,75 @@
// 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/>.
//! Inherents Pool
use std::{fmt, mem, vec};
use parking_lot::Mutex;
/// Inherents Pool
///
/// The pool is responsible to collect inherents asynchronously generated
/// by some other parts of the code and make them ready for the next block production.
pub struct InherentsPool<T> {
data: Mutex<Vec<T>>,
}
impl<T> Default for InherentsPool<T> {
fn default() -> Self {
InherentsPool {
data: Default::default(),
}
}
}
impl<T: fmt::Debug> fmt::Debug for InherentsPool<T> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
let mut builder = fmt.debug_struct("InherentsPool");
if let Some(data) = self.data.try_lock() {
builder.field("data", &*data);
}
builder.finish()
}
}
impl<T> InherentsPool<T> {
/// Add inherent extrinsic to the pool.
///
/// This inherent will be appended to the next produced block.
pub fn add(&self, extrinsic: T) {
self.data.lock().push(extrinsic);
}
/// Drain all currently queued inherents.
pub fn drain(&self) -> Vec<T> {
mem::replace(&mut *self.data.lock(), vec![])
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn should_drain_inherents_to_given_data() {
let pool = InherentsPool::default();
pool.add(5);
pool.add(7);
assert_eq!(pool.drain(), vec![5, 7]);
assert_eq!(pool.drain(), vec![]);
}
}
+27
View File
@@ -0,0 +1,27 @@
[package]
description = "Substrate offchain workers"
name = "substrate-offchain"
version = "0.1.0"
license = "GPL-3.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"
[dependencies]
client = { package = "substrate-client", path = "../../core/client" }
consensus = { package = "substrate-consensus-common", path = "../../core/consensus/common" }
futures = "0.1.25"
inherents = { package = "substrate-inherents", path = "../../core/inherents" }
log = "0.4"
offchain-primitives = { package = "substrate-offchain-primitives", path = "./primitives" }
parity-codec = { version = "3.1", features = ["derive"] }
primitives = { package = "substrate-primitives", path = "../../core/primitives" }
runtime_primitives = { package = "sr-primitives", path = "../../core/sr-primitives" }
tokio = "0.1.7"
transaction_pool = { package = "substrate-transaction-pool", path = "../../core/transaction-pool" }
[dev-dependencies]
env_logger = "0.6"
test_client = { package = "substrate-test-client", path = "../../core/test-client" }
[features]
default = []
@@ -0,0 +1,18 @@
[package]
description = "Substrate offchain workers primitives"
name = "substrate-offchain-primitives"
version = "0.1.0"
license = "GPL-3.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"
[dependencies]
client = { package = "substrate-client", path = "../../client", default-features = false }
runtime_primitives = { package = "sr-primitives", path = "../../sr-primitives", default-features = false }
[features]
default = ["std"]
std = [
"client/std",
"runtime_primitives/std"
]
@@ -0,0 +1,31 @@
// Copyright 2018 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/>.
//! The Offchain Worker runtime api primitives.
#![cfg_attr(not(feature = "std"), no_std)]
#![warn(missing_docs)]
use client::decl_runtime_apis;
use runtime_primitives::traits::NumberFor;
decl_runtime_apis! {
/// The offchain worker api.
pub trait OffchainWorkerApi {
/// Starts the off-chain task for given block number.
fn offchain_worker(number: NumberFor<Block>);
}
}
+99
View File
@@ -0,0 +1,99 @@
// 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/>.
use std::sync::Arc;
use futures::{Stream, Future, sync::mpsc};
use inherents::pool::InherentsPool;
use log::{info, debug, warn};
use parity_codec::Decode;
use primitives::OffchainExt;
use runtime_primitives::{
generic::BlockId,
traits::{self, Extrinsic},
};
use transaction_pool::txpool::{Pool, ChainApi};
/// A message between the offchain extension and the processing thread.
enum ExtMessage {
SubmitExtrinsic(Vec<u8>),
}
/// Asynchronous offchain API.
///
/// NOTE this is done to prevent recursive calls into the runtime (which are not supported currently).
pub(crate) struct AsyncApi(mpsc::UnboundedSender<ExtMessage>);
impl OffchainExt for AsyncApi {
fn submit_extrinsic(&mut self, ext: Vec<u8>) {
let _ = self.0.unbounded_send(ExtMessage::SubmitExtrinsic(ext));
}
}
/// Offchain extensions implementation API
pub(crate) struct Api<A: ChainApi> {
receiver: Option<mpsc::UnboundedReceiver<ExtMessage>>,
transaction_pool: Arc<Pool<A>>,
inherents_pool: Arc<InherentsPool<<A::Block as traits::Block>::Extrinsic>>,
at: BlockId<A::Block>,
}
impl<A: ChainApi> Api<A> {
pub fn new(
transaction_pool: Arc<Pool<A>>,
inherents_pool: Arc<InherentsPool<<A::Block as traits::Block>::Extrinsic>>,
at: BlockId<A::Block>,
) -> (AsyncApi, Self) {
let (tx, rx) = mpsc::unbounded();
let api = Self {
receiver: Some(rx),
transaction_pool,
inherents_pool,
at,
};
(AsyncApi(tx), api)
}
/// Run a processing task for the API
pub fn process(mut self) -> impl Future<Item=(), Error=()> {
let receiver = self.receiver.take().expect("Take invoked only once.");
receiver.for_each(move |msg| {
match msg {
ExtMessage::SubmitExtrinsic(ext) => self.submit_extrinsic(ext),
}
Ok(())
})
}
fn submit_extrinsic(&mut self, ext: Vec<u8>) {
let xt = match <A::Block as traits::Block>::Extrinsic::decode(&mut &*ext) {
Some(xt) => xt,
None => {
warn!("Unable to decode extrinsic: {:?}", ext);
return
},
};
info!("Submitting to the pool: {:?} (isSigned: {:?})", xt, xt.is_signed());
match self.transaction_pool.submit_one(&self.at, xt.clone()) {
Ok(hash) => debug!("[{:?}] Offchain transaction added to the pool.", hash),
Err(_) => {
debug!("Offchain inherent added to the pool.");
self.inherents_pool.add(xt);
},
}
}
}
+132
View File
@@ -0,0 +1,132 @@
// 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/>.
//! Substrate offchain workers.
//!
//! The offchain workers is a special function of the runtime that
//! gets executed after block is imported. During execution
//! it's able to asynchronously submit extrinsics that will either
//! be propagated to other nodes (transactions) or will be
//! added to the next block produced by the node as inherents.
//!
//! Offchain workers can be used for computation-heavy tasks
//! that are not feasible for execution during regular block processing.
//! It can either be tasks that no consensus is required for,
//! or some form of consensus over the data can be built on-chain
//! for instance via:
//! 1. Challenge period for incorrect computations
//! 2. Majority voting for results
//! 3. etc
#![warn(missing_docs)]
use std::{
marker::PhantomData,
sync::Arc,
};
use client::runtime_api::ApiExt;
use inherents::pool::InherentsPool;
use log::{debug, warn};
use primitives::ExecutionContext;
use runtime_primitives::{
generic::BlockId,
traits::{self, ProvideRuntimeApi},
};
use tokio::runtime::TaskExecutor;
use transaction_pool::txpool::{Pool, ChainApi};
mod api;
pub use offchain_primitives::OffchainWorkerApi;
/// An offchain workers manager.
#[derive(Debug)]
pub struct OffchainWorkers<C, Block: traits::Block> {
client: Arc<C>,
inherents_pool: Arc<InherentsPool<<Block as traits::Block>::Extrinsic>>,
executor: TaskExecutor,
_block: PhantomData<Block>,
}
impl<C, Block: traits::Block> OffchainWorkers<C, Block> {
/// Creates new `OffchainWorkers`.
pub fn new(
client: Arc<C>,
inherents_pool: Arc<InherentsPool<<Block as traits::Block>::Extrinsic>>,
executor: TaskExecutor,
) -> Self {
Self {
client,
inherents_pool,
executor,
_block: PhantomData,
}
}
}
impl<C, Block> OffchainWorkers<C, Block> where
Block: traits::Block,
C: ProvideRuntimeApi,
C::Api: OffchainWorkerApi<Block>,
{
/// Start the offchain workers after given block.
pub fn on_block_imported<A>(
&self,
number: &<Block::Header as traits::Header>::Number,
pool: &Arc<Pool<A>>,
) where
A: ChainApi<Block=Block> + 'static,
{
let runtime = self.client.runtime_api();
let at = BlockId::number(*number);
let has_api = runtime.has_api::<OffchainWorkerApi<Block>>(&at);
debug!("Checking offchain workers at {:?}: {:?}", at, has_api);
if has_api.unwrap_or(false) {
let (api, runner) = api::Api::new(pool.clone(), self.inherents_pool.clone(), at.clone());
self.executor.spawn(runner.process());
debug!("Running offchain workers at {:?}", at);
let api = Box::new(api);
runtime.offchain_worker_with_context(&at, ExecutionContext::OffchainWorker(api), *number).unwrap();
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use futures::Future;
#[test]
fn should_call_into_runtime_and_produce_extrinsic() {
// given
let _ = env_logger::try_init();
let runtime = tokio::runtime::Runtime::new().unwrap();
let client = Arc::new(test_client::new());
let pool = Arc::new(Pool::new(Default::default(), ::transaction_pool::ChainApi::new(client.clone())));
let inherents = Arc::new(InherentsPool::default());
// when
let offchain = OffchainWorkers::new(client, inherents.clone(), runtime.executor());
offchain.on_block_imported(&0u64, &pool);
// then
runtime.shutdown_on_idle().wait().unwrap();
assert_eq!(inherents.drain().len(), 1);
}
}
+29
View File
@@ -76,6 +76,35 @@ pub use hash_db::Hasher;
// pub use self::hasher::blake::BlakeHasher; // pub use self::hasher::blake::BlakeHasher;
pub use self::hasher::blake2::Blake2Hasher; pub use self::hasher::blake2::Blake2Hasher;
/// Context for executing a call into the runtime.
#[repr(u8)]
pub enum ExecutionContext {
/// Context for general importing (including own blocks).
Importing,
/// Context used when syncing the blockchain.
Syncing,
/// Context used for block construction.
BlockConstruction,
/// Offchain worker context.
OffchainWorker(Box<OffchainExt>),
/// Context used for other calls.
Other,
}
/// An extended externalities for offchain workers.
pub trait OffchainExt {
/// Submits an extrinsics.
///
/// The extrinsic will either go to the pool (signed)
/// or to the next produced block (inherent).
fn submit_extrinsic(&mut self, extrinsic: Vec<u8>);
}
impl<T: OffchainExt + ?Sized> OffchainExt for Box<T> {
fn submit_extrinsic(&mut self, ex: Vec<u8>) {
(&mut **self).submit_extrinsic(ex)
}
}
/// Hex-serialised shim for `Vec<u8>`. /// Hex-serialised shim for `Vec<u8>`.
#[derive(PartialEq, Eq, Clone)] #[derive(PartialEq, Eq, Clone)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug, Hash, PartialOrd, Ord))] #[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug, Hash, PartialOrd, Ord))]
+2 -2
View File
@@ -35,7 +35,7 @@ use crate::rpc::futures::{stream, Future, Sink, Stream};
use runtime_primitives::generic::BlockId; use runtime_primitives::generic::BlockId;
use runtime_primitives::traits::{Block as BlockT, Header, ProvideRuntimeApi, As, NumberFor}; use runtime_primitives::traits::{Block as BlockT, Header, ProvideRuntimeApi, As, NumberFor};
use runtime_version::RuntimeVersion; use runtime_version::RuntimeVersion;
use state_machine::ExecutionStrategy; use state_machine::{self, ExecutionStrategy};
use crate::subscriptions::Subscriptions; use crate::subscriptions::Subscriptions;
@@ -298,7 +298,7 @@ impl<B, E, Block, RA> StateApi<Block::Hash> for State<B, E, Block, RA> where
.executor() .executor()
.call( .call(
&BlockId::Hash(block), &BlockId::Hash(block),
&method, &data.0, ExecutionStrategy::NativeElseWasm &method, &data.0, ExecutionStrategy::NativeElseWasm, state_machine::NeverOffchainExt::new(),
)?; )?;
Ok(Bytes(return_data)) Ok(Bytes(return_data))
} }
+1 -1
View File
@@ -221,7 +221,7 @@ fn should_return_runtime_version() {
assert_eq!( assert_eq!(
::serde_json::to_string(&api.runtime_version(None.into()).unwrap()).unwrap(), ::serde_json::to_string(&api.runtime_version(None.into()).unwrap()).unwrap(),
r#"{"specName":"test","implName":"parity-test","authoringVersion":1,"specVersion":1,"implVersion":1,"apis":[["0xdf6acb689907609b",1],["0x37e397fc7c91f5e4",1],["0xd2bc9897eed08f15",1],["0x40fe3ad401f8959a",2],["0xc6e9a76309f39b09",1],["0xdd718d5cc53262d4",1]]}"# r#"{"specName":"test","implName":"parity-test","authoringVersion":1,"specVersion":1,"implVersion":1,"apis":[["0xdf6acb689907609b",1],["0x37e397fc7c91f5e4",1],["0xd2bc9897eed08f15",1],["0x40fe3ad401f8959a",2],["0xc6e9a76309f39b09",1],["0xdd718d5cc53262d4",1],["0xf78b278be53f454c",1]]}"#
); );
} }
+2
View File
@@ -17,6 +17,7 @@ serde = "1.0"
serde_json = "1.0" serde_json = "1.0"
serde_derive = "1.0" serde_derive = "1.0"
target_info = "0.1" target_info = "0.1"
inherents = { package = "substrate-inherents", path = "../../core/inherents" }
keystore = { package = "substrate-keystore", path = "../../core/keystore" } keystore = { package = "substrate-keystore", path = "../../core/keystore" }
sr-io = { path = "../../core/sr-io" } sr-io = { path = "../../core/sr-io" }
runtime_primitives = { package = "sr-primitives", path = "../../core/sr-primitives" } runtime_primitives = { package = "sr-primitives", path = "../../core/sr-primitives" }
@@ -30,6 +31,7 @@ substrate-executor = { path = "../../core/executor" }
transaction_pool = { package = "substrate-transaction-pool", path = "../../core/transaction-pool" } transaction_pool = { package = "substrate-transaction-pool", path = "../../core/transaction-pool" }
rpc = { package = "substrate-rpc-servers", path = "../../core/rpc-servers" } rpc = { package = "substrate-rpc-servers", path = "../../core/rpc-servers" }
tel = { package = "substrate-telemetry", path = "../../core/telemetry" } tel = { package = "substrate-telemetry", path = "../../core/telemetry" }
offchain = { package = "substrate-offchain", path = "../../core/offchain" }
[dev-dependencies] [dev-dependencies]
substrate-test-client = { path = "../test-client" } substrate-test-client = { path = "../test-client" }
+8
View File
@@ -116,35 +116,43 @@ impl<G: RuntimeGenesis> Clone for ChainSpec<G> {
} }
impl<G: RuntimeGenesis> ChainSpec<G> { impl<G: RuntimeGenesis> ChainSpec<G> {
/// A list of bootnode addresses.
pub fn boot_nodes(&self) -> &[String] { pub fn boot_nodes(&self) -> &[String] {
&self.spec.boot_nodes &self.spec.boot_nodes
} }
/// Spec name.
pub fn name(&self) -> &str { pub fn name(&self) -> &str {
&self.spec.name &self.spec.name
} }
/// Spec id.
pub fn id(&self) -> &str { pub fn id(&self) -> &str {
&self.spec.id &self.spec.id
} }
/// Telemetry endpoints (if any)
pub fn telemetry_endpoints(&self) -> &Option<TelemetryEndpoints> { pub fn telemetry_endpoints(&self) -> &Option<TelemetryEndpoints> {
&self.spec.telemetry_endpoints &self.spec.telemetry_endpoints
} }
/// Network protocol id.
pub fn protocol_id(&self) -> Option<&str> { pub fn protocol_id(&self) -> Option<&str> {
self.spec.protocol_id.as_ref().map(String::as_str) self.spec.protocol_id.as_ref().map(String::as_str)
} }
/// Name of the consensus engine.
pub fn consensus_engine(&self) -> Option<&str> { pub fn consensus_engine(&self) -> Option<&str> {
self.spec.consensus_engine.as_ref().map(String::as_str) self.spec.consensus_engine.as_ref().map(String::as_str)
} }
/// Additional loosly-typed properties of the chain.
pub fn properties(&self) -> Properties { pub fn properties(&self) -> Properties {
// Return an empty JSON object if 'properties' not defined in config // Return an empty JSON object if 'properties' not defined in config
self.spec.properties.as_ref().unwrap_or(&json::map::Map::new()).clone() self.spec.properties.as_ref().unwrap_or(&json::map::Map::new()).clone()
} }
/// Add a bootnode to the list.
pub fn add_boot_node(&mut self, addr: Multiaddr) { pub fn add_boot_node(&mut self, addr: Multiaddr) {
self.spec.boot_nodes.push(addr.to_string()) self.spec.boot_nodes.push(addr.to_string())
} }
+44 -19
View File
@@ -21,7 +21,7 @@ use serde::{Serialize, de::DeserializeOwned};
use tokio::runtime::TaskExecutor; use tokio::runtime::TaskExecutor;
use crate::chain_spec::ChainSpec; use crate::chain_spec::ChainSpec;
use client_db; use client_db;
use client::{self, Client, runtime_api::{Metadata, TaggedTransactionQueue}}; use client::{self, Client, runtime_api};
use crate::{error, Service, maybe_start_server}; use crate::{error, Service, maybe_start_server};
use consensus_common::import_queue::ImportQueue; use consensus_common::import_queue::ImportQueue;
use network::{self, OnDemand}; use network::{self, OnDemand};
@@ -150,7 +150,7 @@ pub trait StartRPC<C: Components> {
impl<C: Components> StartRPC<Self> for C where impl<C: Components> StartRPC<Self> for C where
ComponentClient<C>: ProvideRuntimeApi, ComponentClient<C>: ProvideRuntimeApi,
<ComponentClient<C> as ProvideRuntimeApi>::Api: Metadata<ComponentBlock<C>>, <ComponentClient<C> as ProvideRuntimeApi>::Api: runtime_api::Metadata<ComponentBlock<C>>,
{ {
type ServersHandle = (Option<rpc::HttpServer>, Option<Mutex<rpc::WsServer>>); type ServersHandle = (Option<rpc::HttpServer>, Option<Mutex<rpc::WsServer>>);
@@ -192,14 +192,14 @@ impl<C: Components> StartRPC<Self> for C where
/// Something that can maintain transaction pool on every imported block. /// Something that can maintain transaction pool on every imported block.
pub trait MaintainTransactionPool<C: Components> { pub trait MaintainTransactionPool<C: Components> {
fn on_block_imported( fn maintain_transaction_pool(
id: &BlockId<ComponentBlock<C>>, id: &BlockId<ComponentBlock<C>>,
client: &ComponentClient<C>, client: &ComponentClient<C>,
transaction_pool: &TransactionPool<C::TransactionPoolApi>, transaction_pool: &TransactionPool<C::TransactionPoolApi>,
) -> error::Result<()>; ) -> error::Result<()>;
} }
fn on_block_imported<Api, Backend, Block, Executor, PoolApi>( fn maintain_transaction_pool<Api, Backend, Block, Executor, PoolApi>(
id: &BlockId<Block>, id: &BlockId<Block>,
client: &Client<Backend, Executor, Block, Api>, client: &Client<Backend, Executor, Block, Api>,
transaction_pool: &TransactionPool<PoolApi>, transaction_pool: &TransactionPool<PoolApi>,
@@ -207,7 +207,7 @@ fn on_block_imported<Api, Backend, Block, Executor, PoolApi>(
Block: BlockT<Hash = <Blake2Hasher as ::primitives::Hasher>::Out>, Block: BlockT<Hash = <Blake2Hasher as ::primitives::Hasher>::Out>,
Backend: client::backend::Backend<Block, Blake2Hasher>, Backend: client::backend::Backend<Block, Blake2Hasher>,
Client<Backend, Executor, Block, Api>: ProvideRuntimeApi, Client<Backend, Executor, Block, Api>: ProvideRuntimeApi,
<Client<Backend, Executor, Block, Api> as ProvideRuntimeApi>::Api: TaggedTransactionQueue<Block>, <Client<Backend, Executor, Block, Api> as ProvideRuntimeApi>::Api: runtime_api::TaggedTransactionQueue<Block>,
Executor: client::CallExecutor<Block, Blake2Hasher>, Executor: client::CallExecutor<Block, Blake2Hasher>,
PoolApi: txpool::ChainApi<Hash = Block::Hash, Block = Block>, PoolApi: txpool::ChainApi<Hash = Block::Hash, Block = Block>,
{ {
@@ -227,14 +227,35 @@ fn on_block_imported<Api, Backend, Block, Executor, PoolApi>(
impl<C: Components> MaintainTransactionPool<Self> for C where impl<C: Components> MaintainTransactionPool<Self> for C where
ComponentClient<C>: ProvideRuntimeApi, ComponentClient<C>: ProvideRuntimeApi,
<ComponentClient<C> as ProvideRuntimeApi>::Api: TaggedTransactionQueue<ComponentBlock<C>>, <ComponentClient<C> as ProvideRuntimeApi>::Api: runtime_api::TaggedTransactionQueue<ComponentBlock<C>>,
{ {
fn on_block_imported( fn maintain_transaction_pool(
id: &BlockId<ComponentBlock<C>>, id: &BlockId<ComponentBlock<C>>,
client: &ComponentClient<C>, client: &ComponentClient<C>,
transaction_pool: &TransactionPool<C::TransactionPoolApi>, transaction_pool: &TransactionPool<C::TransactionPoolApi>,
) -> error::Result<()> { ) -> error::Result<()> {
on_block_imported(id, client, transaction_pool) maintain_transaction_pool(id, client, transaction_pool)
}
}
pub trait OffchainWorker<C: Components> {
fn offchain_workers(
number: &FactoryBlockNumber<C::Factory>,
offchain: &offchain::OffchainWorkers<ComponentClient<C>, ComponentBlock<C>>,
pool: &Arc<TransactionPool<C::TransactionPoolApi>>,
) -> error::Result<()>;
}
impl<C: Components> OffchainWorker<Self> for C where
ComponentClient<C>: ProvideRuntimeApi,
<ComponentClient<C> as ProvideRuntimeApi>::Api: offchain::OffchainWorkerApi<ComponentBlock<C>>,
{
fn offchain_workers(
number: &FactoryBlockNumber<C::Factory>,
offchain: &offchain::OffchainWorkers<ComponentClient<C>, ComponentBlock<C>>,
pool: &Arc<TransactionPool<C::TransactionPoolApi>>,
) -> error::Result<()> {
Ok(offchain.on_block_imported(number, pool))
} }
} }
@@ -246,9 +267,16 @@ pub trait ServiceTrait<C: Components>:
+ 'static + 'static
+ StartRPC<C> + StartRPC<C>
+ MaintainTransactionPool<C> + MaintainTransactionPool<C>
+ OffchainWorker<C>
{} {}
impl<C: Components, T> ServiceTrait<C> for T where impl<C: Components, T> ServiceTrait<C> for T where
T: Deref<Target = Service<C>> + Send + Sync + 'static + StartRPC<C> + MaintainTransactionPool<C> T: Deref<Target = Service<C>>
+ Send
+ Sync
+ 'static
+ StartRPC<C>
+ MaintainTransactionPool<C>
+ OffchainWorker<C>
{} {}
/// A collection of types and methods to build a service on top of the substrate service. /// A collection of types and methods to build a service on top of the substrate service.
@@ -338,17 +366,14 @@ pub trait Components: Sized + 'static {
type Executor: 'static + client::CallExecutor<FactoryBlock<Self::Factory>, Blake2Hasher> + Send + Sync + Clone; type Executor: 'static + client::CallExecutor<FactoryBlock<Self::Factory>, Blake2Hasher> + Send + Sync + Clone;
/// The type that implements the runtime API. /// The type that implements the runtime API.
type RuntimeApi: Send + Sync; type RuntimeApi: Send + Sync;
/// A type that can start the RPC. /// A type that can start all runtime-dependent services.
type RPC: StartRPC<Self>; type RuntimeServices: ServiceTrait<Self>;
// TODO: Traitify transaction pool and allow people to implement their own. (#1242) // TODO: Traitify transaction pool and allow people to implement their own. (#1242)
/// A type that can maintain transaction pool.
type TransactionPool: MaintainTransactionPool<Self>;
/// Extrinsic pool type. /// Extrinsic pool type.
type TransactionPoolApi: 'static + txpool::ChainApi< type TransactionPoolApi: 'static + txpool::ChainApi<
Hash = <FactoryBlock<Self::Factory> as BlockT>::Hash, Hash = <FactoryBlock<Self::Factory> as BlockT>::Hash,
Block = FactoryBlock<Self::Factory> Block = FactoryBlock<Self::Factory>
>; >;
/// Our Import Queue /// Our Import Queue
type ImportQueue: ImportQueue<FactoryBlock<Self::Factory>> + 'static; type ImportQueue: ImportQueue<FactoryBlock<Self::Factory>> + 'static;
@@ -382,6 +407,7 @@ pub struct FullComponents<Factory: ServiceFactory> {
} }
impl<Factory: ServiceFactory> FullComponents<Factory> { impl<Factory: ServiceFactory> FullComponents<Factory> {
/// Create new `FullComponents`
pub fn new( pub fn new(
config: FactoryFullConfiguration<Factory>, config: FactoryFullConfiguration<Factory>,
task_executor: TaskExecutor task_executor: TaskExecutor
@@ -416,8 +442,7 @@ impl<Factory: ServiceFactory> Components for FullComponents<Factory> {
type TransactionPoolApi = <Factory as ServiceFactory>::FullTransactionPoolApi; type TransactionPoolApi = <Factory as ServiceFactory>::FullTransactionPoolApi;
type ImportQueue = Factory::FullImportQueue; type ImportQueue = Factory::FullImportQueue;
type RuntimeApi = Factory::RuntimeApi; type RuntimeApi = Factory::RuntimeApi;
type RPC = Factory::FullService; type RuntimeServices = Factory::FullService;
type TransactionPool = Factory::FullService;
fn build_client( fn build_client(
config: &FactoryFullConfiguration<Factory>, config: &FactoryFullConfiguration<Factory>,
@@ -462,6 +487,7 @@ pub struct LightComponents<Factory: ServiceFactory> {
} }
impl<Factory: ServiceFactory> LightComponents<Factory> { impl<Factory: ServiceFactory> LightComponents<Factory> {
/// Create new `LightComponents`
pub fn new( pub fn new(
config: FactoryFullConfiguration<Factory>, config: FactoryFullConfiguration<Factory>,
task_executor: TaskExecutor task_executor: TaskExecutor
@@ -490,8 +516,7 @@ impl<Factory: ServiceFactory> Components for LightComponents<Factory> {
type TransactionPoolApi = <Factory as ServiceFactory>::LightTransactionPoolApi; type TransactionPoolApi = <Factory as ServiceFactory>::LightTransactionPoolApi;
type ImportQueue = <Factory as ServiceFactory>::LightImportQueue; type ImportQueue = <Factory as ServiceFactory>::LightImportQueue;
type RuntimeApi = Factory::RuntimeApi; type RuntimeApi = Factory::RuntimeApi;
type RPC = Factory::LightService; type RuntimeServices = Factory::LightService;
type TransactionPool = Factory::LightService;
fn build_client( fn build_client(
config: &FactoryFullConfiguration<Factory>, config: &FactoryFullConfiguration<Factory>,
@@ -564,7 +589,7 @@ mod tests {
// fire notification - this should clean up the queue // fire notification - this should clean up the queue
assert_eq!(pool.status().ready, 1); assert_eq!(pool.status().ready, 1);
on_block_imported( maintain_transaction_pool(
&id, &id,
&client, &client,
&pool, &pool,
+3
View File
@@ -68,6 +68,8 @@ pub struct Configuration<C, G: Serialize + DeserializeOwned + BuildStorage> {
pub telemetry_endpoints: Option<TelemetryEndpoints>, pub telemetry_endpoints: Option<TelemetryEndpoints>,
/// The default number of 64KB pages to allocate for Wasm execution /// The default number of 64KB pages to allocate for Wasm execution
pub default_heap_pages: Option<u64>, pub default_heap_pages: Option<u64>,
/// Should offchain workers be executed.
pub offchain_worker: bool,
/// Enable authoring even when offline. /// Enable authoring even when offline.
pub force_authoring: bool, pub force_authoring: bool,
/// Disable GRANDPA when running in validator mode /// Disable GRANDPA when running in validator mode
@@ -97,6 +99,7 @@ impl<C: Default, G: Serialize + DeserializeOwned + BuildStorage> Configuration<C
rpc_ws: None, rpc_ws: None,
telemetry_endpoints: None, telemetry_endpoints: None,
default_heap_pages: None, default_heap_pages: None,
offchain_worker: Default::default(),
force_authoring: false, force_authoring: false,
disable_grandpa: false, disable_grandpa: false,
}; };
+51 -17
View File
@@ -17,7 +17,7 @@
//! Substrate service. Starts a thread that spins up the network, client, and extrinsic pool. //! Substrate service. Starts a thread that spins up the network, client, and extrinsic pool.
//! Manages communication between them. //! Manages communication between them.
#![warn(unused_extern_crates)] #![warn(missing_docs)]
mod components; mod components;
mod error; mod error;
@@ -28,20 +28,18 @@ pub mod chain_ops;
use std::io; use std::io;
use std::net::SocketAddr; use std::net::SocketAddr;
use std::collections::HashMap; use std::collections::HashMap;
#[doc(hidden)]
pub use std::{ops::Deref, result::Result, sync::Arc};
use log::{info, warn, debug};
use futures::prelude::*;
use keystore::Store as Keystore;
use client::BlockchainEvents; use client::BlockchainEvents;
use exit_future::Signal;
use futures::prelude::*;
use inherents::pool::InherentsPool;
use keystore::Store as Keystore;
use log::{info, warn, debug};
use parity_codec::{Encode, Decode};
use primitives::Pair; use primitives::Pair;
use runtime_primitives::generic::BlockId; use runtime_primitives::generic::BlockId;
use runtime_primitives::traits::{Header, As}; use runtime_primitives::traits::{Header, As};
use exit_future::Signal;
#[doc(hidden)]
pub use tokio::runtime::TaskExecutor;
use substrate_executor::NativeExecutor; use substrate_executor::NativeExecutor;
use parity_codec::{Encode, Decode};
use tel::{telemetry, SUBSTRATE_INFO}; use tel::{telemetry, SUBSTRATE_INFO};
pub use self::error::{ErrorKind, Error}; pub use self::error::{ErrorKind, Error};
@@ -59,9 +57,13 @@ pub use components::{ServiceFactory, FullBackend, FullExecutor, LightBackend,
FactoryFullConfiguration, RuntimeGenesis, FactoryGenesis, FactoryFullConfiguration, RuntimeGenesis, FactoryGenesis,
ComponentExHash, ComponentExtrinsic, FactoryExtrinsic ComponentExHash, ComponentExtrinsic, FactoryExtrinsic
}; };
use components::{StartRPC, MaintainTransactionPool}; use components::{StartRPC, MaintainTransactionPool, OffchainWorker};
#[doc(hidden)]
pub use std::{ops::Deref, result::Result, sync::Arc};
#[doc(hidden)] #[doc(hidden)]
pub use network::OnDemand; pub use network::OnDemand;
#[doc(hidden)]
pub use tokio::runtime::TaskExecutor;
const DEFAULT_PROTOCOL_ID: &str = "sup"; const DEFAULT_PROTOCOL_ID: &str = "sup";
@@ -70,6 +72,7 @@ pub struct Service<Components: components::Components> {
client: Arc<ComponentClient<Components>>, client: Arc<ComponentClient<Components>>,
network: Option<Arc<components::NetworkService<Components::Factory>>>, network: Option<Arc<components::NetworkService<Components::Factory>>>,
transaction_pool: Arc<TransactionPool<Components::TransactionPoolApi>>, transaction_pool: Arc<TransactionPool<Components::TransactionPoolApi>>,
inherents_pool: Arc<InherentsPool<ComponentExtrinsic<Components>>>,
keystore: Keystore, keystore: Keystore,
exit: ::exit_future::Exit, exit: ::exit_future::Exit,
signal: Option<Signal>, signal: Option<Signal>,
@@ -77,6 +80,7 @@ pub struct Service<Components: components::Components> {
pub config: FactoryFullConfiguration<Components::Factory>, pub config: FactoryFullConfiguration<Components::Factory>,
_rpc: Box<::std::any::Any + Send + Sync>, _rpc: Box<::std::any::Any + Send + Sync>,
_telemetry: Option<Arc<tel::Telemetry>>, _telemetry: Option<Arc<tel::Telemetry>>,
_offchain_workers: Option<Arc<offchain::OffchainWorkers<ComponentClient<Components>, ComponentBlock<Components>>>>,
} }
/// Creates bare client without any networking. /// Creates bare client without any networking.
@@ -96,9 +100,7 @@ impl<Components: components::Components> Service<Components> {
pub fn new( pub fn new(
mut config: FactoryFullConfiguration<Components::Factory>, mut config: FactoryFullConfiguration<Components::Factory>,
task_executor: TaskExecutor, task_executor: TaskExecutor,
) ) -> Result<Self, error::Error> {
-> Result<Self, error::Error>
{
let (signal, exit) = ::exit_future::signal(); let (signal, exit) = ::exit_future::signal();
// Create client // Create client
@@ -169,24 +171,48 @@ impl<Components: components::Components> Service<Components> {
)?; )?;
on_demand.map(|on_demand| on_demand.set_network_sender(network_chan)); on_demand.map(|on_demand| on_demand.set_network_sender(network_chan));
let inherents_pool = Arc::new(InherentsPool::default());
let offchain_workers = if config.offchain_worker {
Some(Arc::new(offchain::OffchainWorkers::new(
client.clone(),
inherents_pool.clone(),
task_executor.clone(),
)))
} else {
None
};
{ {
// block notifications // block notifications
let network = Arc::downgrade(&network); let network = Arc::downgrade(&network);
let txpool = Arc::downgrade(&transaction_pool); let txpool = Arc::downgrade(&transaction_pool);
let wclient = Arc::downgrade(&client); let wclient = Arc::downgrade(&client);
let offchain = offchain_workers.as_ref().map(Arc::downgrade);
let events = client.import_notification_stream() let events = client.import_notification_stream()
.for_each(move |notification| { .for_each(move |notification| {
let number = *notification.header.number();
if let Some(network) = network.upgrade() { if let Some(network) = network.upgrade() {
network.on_block_imported(notification.hash, notification.header); network.on_block_imported(notification.hash, notification.header);
} }
if let (Some(txpool), Some(client)) = (txpool.upgrade(), wclient.upgrade()) { if let (Some(txpool), Some(client)) = (txpool.upgrade(), wclient.upgrade()) {
Components::TransactionPool::on_block_imported( Components::RuntimeServices::maintain_transaction_pool(
&BlockId::hash(notification.hash), &BlockId::hash(notification.hash),
&*client, &*client,
&*txpool, &*txpool,
).map_err(|e| warn!("Pool error processing new block: {:?}", e))?; ).map_err(|e| warn!("Pool error processing new block: {:?}", e))?;
} }
if let (Some(txpool), Some(offchain)) = (txpool.upgrade(), offchain.as_ref().and_then(|o| o.upgrade())) {
Components::RuntimeServices::offchain_workers(
&number,
&offchain,
&txpool,
).map_err(|e| warn!("Offchain workers error processing new block: {:?}", e))?;
}
Ok(()) Ok(())
}) })
.select(exit.clone()) .select(exit.clone())
@@ -264,7 +290,7 @@ impl<Components: components::Components> Service<Components> {
impl_version: config.impl_version.into(), impl_version: config.impl_version.into(),
properties: config.chain_spec.properties(), properties: config.chain_spec.properties(),
}; };
let rpc = Components::RPC::start_rpc( let rpc = Components::RuntimeServices::start_rpc(
client.clone(), network.clone(), has_bootnodes, system_info, config.rpc_http, client.clone(), network.clone(), has_bootnodes, system_info, config.rpc_http,
config.rpc_ws, task_executor.clone(), transaction_pool.clone(), config.rpc_ws, task_executor.clone(), transaction_pool.clone(),
)?; )?;
@@ -299,12 +325,14 @@ impl<Components: components::Components> Service<Components> {
client, client,
network: Some(network), network: Some(network),
transaction_pool, transaction_pool,
inherents_pool,
signal: Some(signal), signal: Some(signal),
keystore, keystore,
config, config,
exit, exit,
_rpc: Box::new(rpc), _rpc: Box::new(rpc),
_telemetry: telemetry, _telemetry: telemetry,
_offchain_workers: offchain_workers,
}) })
} }
@@ -321,6 +349,7 @@ impl<Components: components::Components> Service<Components> {
} }
} }
/// return a shared instance of Telemtry (if enabled)
pub fn telemetry(&self) -> Option<Arc<tel::Telemetry>> { pub fn telemetry(&self) -> Option<Arc<tel::Telemetry>> {
self._telemetry.as_ref().map(|t| t.clone()) self._telemetry.as_ref().map(|t| t.clone())
} }
@@ -337,11 +366,16 @@ impl<Components> Service<Components> where Components: components::Components {
self.network.as_ref().expect("self.network always Some").clone() self.network.as_ref().expect("self.network always Some").clone()
} }
/// Get shared extrinsic pool instance. /// Get shared transaction pool instance.
pub fn transaction_pool(&self) -> Arc<TransactionPool<Components::TransactionPoolApi>> { pub fn transaction_pool(&self) -> Arc<TransactionPool<Components::TransactionPoolApi>> {
self.transaction_pool.clone() self.transaction_pool.clone()
} }
/// Get shared inherents pool instance.
pub fn inherents_pool(&self) -> Arc<InherentsPool<ComponentExtrinsic<Components>>> {
self.inherents_pool.clone()
}
/// Get shared keystore. /// Get shared keystore.
pub fn keystore(&self) -> &Keystore { pub fn keystore(&self) -> &Keystore {
&self.keystore &self.keystore
+1
View File
@@ -120,6 +120,7 @@ fn node_config<F: ServiceFactory> (
rpc_ws: None, rpc_ws: None,
telemetry_endpoints: None, telemetry_endpoints: None,
default_heap_pages: None, default_heap_pages: None,
offchain_worker: false,
force_authoring: false, force_authoring: false,
disable_grandpa: false, disable_grandpa: false,
} }
+8
View File
@@ -218,6 +218,14 @@ pub fn secp256k1_ecdsa_recover(sig: &[u8; 65], msg: &[u8; 32]) -> Result<[u8; 64
Ok(res) Ok(res)
} }
/// Submit extrinsic.
pub fn submit_extrinsic<T: codec::Encode>(data: &T) {
ext::with(|ext| ext
.submit_extrinsic(codec::Encode::encode(data))
.expect("submit_extrinsic can be called only in offchain worker context")
).expect("submit_extrinsic cannot be called outside of an Externalities-provided environment.")
}
/// Execute the given closure with global function available whose functionality routes into the /// Execute the given closure with global function available whose functionality routes into the
/// externalities `ext`. Forwards the value that the closure returns. /// externalities `ext`. Forwards the value that the closure returns.
// NOTE: need a concrete hasher here due to limitations of the `environmental!` macro, otherwise a type param would have been fine I think. // NOTE: need a concrete hasher here due to limitations of the `environmental!` macro, otherwise a type param would have been fine I think.
+19
View File
@@ -283,6 +283,13 @@ extern_functions! {
fn ext_sr25519_verify(msg_data: *const u8, msg_len: u32, sig_data: *const u8, pubkey_data: *const u8) -> u32; fn ext_sr25519_verify(msg_data: *const u8, msg_len: u32, sig_data: *const u8, pubkey_data: *const u8) -> u32;
/// Note: ext_secp256k1_ecdsa_recover returns 0 if the signature is correct, nonzero otherwise. /// Note: ext_secp256k1_ecdsa_recover returns 0 if the signature is correct, nonzero otherwise.
fn ext_secp256k1_ecdsa_recover(msg_data: *const u8, sig_data: *const u8, pubkey_data: *mut u8) -> u32; fn ext_secp256k1_ecdsa_recover(msg_data: *const u8, sig_data: *const u8, pubkey_data: *mut u8) -> u32;
//================================
// Offchain-worker Context
//================================
/// Submit extrinsic.
fn ext_submit_extrinsic(data: *const u8, len: u32);
} }
/// Ensures we use the right crypto when calling into native /// Ensures we use the right crypto when calling into native
@@ -594,6 +601,18 @@ pub fn secp256k1_ecdsa_recover(sig: &[u8; 65], msg: &[u8; 32]) -> Result<[u8; 64
} }
} }
/// Submit extrinsic from the runtime.
///
/// Depending on the kind of extrinsic it will either be:
/// 1. scheduled to be included in the next produced block (inherent)
/// 2. added to the pool and propagated (transaction)
pub fn submit_extrinsic<T: codec::Encode>(data: &T) {
let encoded_data = codec::Encode::encode(data);
unsafe {
ext_submit_extrinsic.get()(encoded_data.as_ptr(), encoded_data.len() as u32)
}
}
/// Trait for things which can be printed. /// Trait for things which can be printed.
pub trait Printable { pub trait Printable {
fn print(self); fn print(self);
-15
View File
@@ -393,21 +393,6 @@ impl From<sr25519::Signature> for AnySignature {
} }
} }
/// Context for executing a call into the runtime.
#[derive(Copy, Clone, Eq, PartialEq, Encode, Decode)]
#[cfg_attr(feature = "std", derive(Debug, Serialize))]
#[repr(u8)]
pub enum ExecutionContext {
/// Context for general importing (including own blocks).
Importing,
/// Context used when syncing the blockchain.
Syncing,
/// Context used for block construction.
BlockConstruction,
/// Context used for other calls.
Other,
}
#[derive(Eq, PartialEq, Clone, Copy, Decode)] #[derive(Eq, PartialEq, Clone, Copy, Decode)]
#[cfg_attr(feature = "std", derive(Debug, Serialize))] #[cfg_attr(feature = "std", derive(Debug, Serialize))]
#[repr(u8)] #[repr(u8)]
@@ -270,6 +270,24 @@ pub trait OnInitialise<BlockNumber> {
impl<N> OnInitialise<N> for () {} impl<N> OnInitialise<N> for () {}
/// Off-chain computation trait.
///
/// Implementing this trait on a module allows you to perform a long-running tasks
/// that make validators generate extrinsics (either transactions or inherents)
/// with results of those long-running computations.
///
/// NOTE: This function runs off-chain, so it can access the block state,
/// but cannot preform any alterations.
pub trait OffchainWorker<BlockNumber> {
/// This function is being called on every block.
///
/// Implement this and use special `extern`s to generate transactions or inherents.
/// Any state alterations are lost and are not persisted.
fn generate_extrinsics(_n: BlockNumber) {}
}
impl<N> OffchainWorker<N> for () {}
macro_rules! tuple_impl { macro_rules! tuple_impl {
($one:ident,) => { ($one:ident,) => {
impl<Number: Copy, $one: OnFinalise<Number>> OnFinalise<Number> for ($one,) { impl<Number: Copy, $one: OnFinalise<Number>> OnFinalise<Number> for ($one,) {
@@ -282,6 +300,11 @@ macro_rules! tuple_impl {
$one::on_initialise(n); $one::on_initialise(n);
} }
} }
impl<Number: Copy, $one: OffchainWorker<Number>> OffchainWorker<Number> for ($one,) {
fn generate_extrinsics(n: Number) {
$one::generate_extrinsics(n);
}
}
}; };
($first:ident, $($rest:ident,)+) => { ($first:ident, $($rest:ident,)+) => {
impl< impl<
@@ -304,6 +327,16 @@ macro_rules! tuple_impl {
$($rest::on_initialise(n);)+ $($rest::on_initialise(n);)+
} }
} }
impl<
Number: Copy,
$first: OffchainWorker<Number>,
$($rest: OffchainWorker<Number>),+
> OffchainWorker<Number> for ($first, $($rest),+) {
fn generate_extrinsics(n: Number) {
$first::generate_extrinsics(n);
$($rest::generate_extrinsics(n);)+
}
}
tuple_impl!($($rest,)+); tuple_impl!($($rest,)+);
} }
} }
@@ -24,6 +24,7 @@ use trie::trie_root;
use primitives::storage::well_known_keys::{CHANGES_TRIE_CONFIG, CODE, HEAP_PAGES}; use primitives::storage::well_known_keys::{CHANGES_TRIE_CONFIG, CODE, HEAP_PAGES};
use parity_codec::Encode; use parity_codec::Encode;
use super::{Externalities, OverlayedChanges}; use super::{Externalities, OverlayedChanges};
use log::warn;
/// Simple HashMap-based Externalities impl. /// Simple HashMap-based Externalities impl.
pub struct BasicExternalities { pub struct BasicExternalities {
@@ -151,6 +152,11 @@ impl<H: Hasher> Externalities<H> for BasicExternalities where H::Out: Ord + Heap
fn storage_changes_root(&mut self, _parent: H::Out, _parent_num: u64) -> Option<H::Out> { fn storage_changes_root(&mut self, _parent: H::Out, _parent_num: u64) -> Option<H::Out> {
None None
} }
fn submit_extrinsic(&mut self, _extrinsic: Vec<u8>) -> Result<(), ()> {
warn!("Call to submit_extrinsic without offchain externalities set.");
Err(())
}
} }
#[cfg(test)] #[cfg(test)]
+35 -12
View File
@@ -20,7 +20,7 @@ use std::{error, fmt, cmp::Ord};
use log::warn; use log::warn;
use crate::backend::{Backend, Consolidate}; use crate::backend::{Backend, Consolidate};
use crate::changes_trie::{AnchorBlockId, Storage as ChangesTrieStorage, compute_changes_trie_root}; use crate::changes_trie::{AnchorBlockId, Storage as ChangesTrieStorage, compute_changes_trie_root};
use crate::{Externalities, OverlayedChanges}; use crate::{Externalities, OverlayedChanges, OffchainExt};
use hash_db::Hasher; use hash_db::Hasher;
use primitives::storage::well_known_keys::is_child_storage_key; use primitives::storage::well_known_keys::is_child_storage_key;
use trie::{MemoryDB, TrieDBMut, TrieMut, default_child_trie_root, is_child_trie_key_valid}; use trie::{MemoryDB, TrieDBMut, TrieMut, default_child_trie_root, is_child_trie_key_valid};
@@ -58,12 +58,11 @@ impl<B: error::Error, E: error::Error> error::Error for Error<B, E> {
} }
/// Wraps a read-only backend, call executor, and current overlayed changes. /// Wraps a read-only backend, call executor, and current overlayed changes.
pub struct Ext<'a, H, B, T> pub struct Ext<'a, H, B, T, O>
where where
H: Hasher, H: Hasher,
B: 'a + Backend<H>, B: 'a + Backend<H>,
T: 'a + ChangesTrieStorage<H>,
{ {
/// The overlayed changes to write to. /// The overlayed changes to write to.
overlay: &'a mut OverlayedChanges, overlay: &'a mut OverlayedChanges,
@@ -81,23 +80,34 @@ where
/// `storage_changes_root` is called matters + we need to remember additional /// `storage_changes_root` is called matters + we need to remember additional
/// data at this moment (block number). /// data at this moment (block number).
changes_trie_transaction: Option<(u64, MemoryDB<H>, H::Out)>, changes_trie_transaction: Option<(u64, MemoryDB<H>, H::Out)>,
/// Additional externalities for offchain workers.
///
/// If None, some methods from the trait might not supported.
offchain_externalities: Option<&'a mut O>,
} }
impl<'a, H, B, T> Ext<'a, H, B, T> impl<'a, H, B, T, O> Ext<'a, H, B, T, O>
where where
H: Hasher, H: Hasher,
B: 'a + Backend<H>, B: 'a + Backend<H>,
T: 'a + ChangesTrieStorage<H>, T: 'a + ChangesTrieStorage<H>,
O: 'a + OffchainExt,
H::Out: Ord + HeapSizeOf, H::Out: Ord + HeapSizeOf,
{ {
/// Create a new `Ext` from overlayed changes and read-only backend /// Create a new `Ext` from overlayed changes and read-only backend
pub fn new(overlay: &'a mut OverlayedChanges, backend: &'a B, changes_trie_storage: Option<&'a T>) -> Self { pub fn new(
overlay: &'a mut OverlayedChanges,
backend: &'a B,
changes_trie_storage: Option<&'a T>,
offchain_externalities: Option<&'a mut O>,
) -> Self {
Ext { Ext {
overlay, overlay,
backend, backend,
storage_transaction: None, storage_transaction: None,
changes_trie_storage, changes_trie_storage,
changes_trie_transaction: None, changes_trie_transaction: None,
offchain_externalities,
} }
} }
@@ -152,12 +162,13 @@ where
} }
#[cfg(test)] #[cfg(test)]
impl<'a, H, B, T> Ext<'a, H, B, T> impl<'a, H, B, T, O> Ext<'a, H, B, T, O>
where where
H: Hasher, H: Hasher,
B: 'a + Backend<H>, B: 'a + Backend<H>,
T: 'a + ChangesTrieStorage<H>, T: 'a + ChangesTrieStorage<H>,
O: 'a + OffchainExt,
{ {
pub fn storage_pairs(&self) -> Vec<(Vec<u8>, Vec<u8>)> { pub fn storage_pairs(&self) -> Vec<(Vec<u8>, Vec<u8>)> {
use std::collections::HashMap; use std::collections::HashMap;
@@ -173,11 +184,12 @@ where
} }
} }
impl<'a, B: 'a, T: 'a, H> Externalities<H> for Ext<'a, H, B, T> impl<'a, B, T, H, O> Externalities<H> for Ext<'a, H, B, T, O>
where where
H: Hasher, H: Hasher,
B: 'a + Backend<H>, B: 'a + Backend<H>,
T: 'a + ChangesTrieStorage<H>, T: 'a + ChangesTrieStorage<H>,
O: 'a + OffchainExt,
H::Out: Ord + HeapSizeOf, H::Out: Ord + HeapSizeOf,
{ {
fn storage(&self, key: &[u8]) -> Option<Vec<u8>> { fn storage(&self, key: &[u8]) -> Option<Vec<u8>> {
@@ -329,6 +341,17 @@ where
self.changes_trie_transaction = root_and_tx; self.changes_trie_transaction = root_and_tx;
root root
} }
fn submit_extrinsic(&mut self, extrinsic: Vec<u8>) -> Result<(), ()> {
let _guard = panic_handler::AbortGuard::new(true);
if let Some(ext) = self.offchain_externalities.as_mut() {
ext.submit_extrinsic(extrinsic);
Ok(())
} else {
warn!("Call to submit_extrinsic without offchain externalities set.");
Err(())
}
}
} }
#[cfg(test)] #[cfg(test)]
@@ -345,7 +368,7 @@ mod tests {
type TestBackend = InMemory<Blake2Hasher>; type TestBackend = InMemory<Blake2Hasher>;
type TestChangesTrieStorage = InMemoryChangesTrieStorage<Blake2Hasher>; type TestChangesTrieStorage = InMemoryChangesTrieStorage<Blake2Hasher>;
type TestExt<'a> = Ext<'a, Blake2Hasher, TestBackend, TestChangesTrieStorage>; type TestExt<'a> = Ext<'a, Blake2Hasher, TestBackend, TestChangesTrieStorage, crate::NeverOffchainExt>;
fn prepare_overlay_with_changes() -> OverlayedChanges { fn prepare_overlay_with_changes() -> OverlayedChanges {
OverlayedChanges { OverlayedChanges {
@@ -371,7 +394,7 @@ mod tests {
fn storage_changes_root_is_none_when_storage_is_not_provided() { fn storage_changes_root_is_none_when_storage_is_not_provided() {
let mut overlay = prepare_overlay_with_changes(); let mut overlay = prepare_overlay_with_changes();
let backend = TestBackend::default(); let backend = TestBackend::default();
let mut ext = TestExt::new(&mut overlay, &backend, None); let mut ext = TestExt::new(&mut overlay, &backend, None, None);
assert_eq!(ext.storage_changes_root(Default::default(), 100), None); assert_eq!(ext.storage_changes_root(Default::default(), 100), None);
} }
@@ -381,7 +404,7 @@ mod tests {
overlay.changes_trie_config = None; overlay.changes_trie_config = None;
let storage = TestChangesTrieStorage::new(); let storage = TestChangesTrieStorage::new();
let backend = TestBackend::default(); let backend = TestBackend::default();
let mut ext = TestExt::new(&mut overlay, &backend, Some(&storage)); let mut ext = TestExt::new(&mut overlay, &backend, Some(&storage), None);
assert_eq!(ext.storage_changes_root(Default::default(), 100), None); assert_eq!(ext.storage_changes_root(Default::default(), 100), None);
} }
@@ -390,7 +413,7 @@ mod tests {
let mut overlay = prepare_overlay_with_changes(); let mut overlay = prepare_overlay_with_changes();
let storage = TestChangesTrieStorage::new(); let storage = TestChangesTrieStorage::new();
let backend = TestBackend::default(); let backend = TestBackend::default();
let mut ext = TestExt::new(&mut overlay, &backend, Some(&storage)); let mut ext = TestExt::new(&mut overlay, &backend, Some(&storage), None);
assert_eq!(ext.storage_changes_root(Default::default(), 99), assert_eq!(ext.storage_changes_root(Default::default(), 99),
Some(hex!("5b829920b9c8d554a19ee2a1ba593c4f2ee6fc32822d083e04236d693e8358d5").into())); Some(hex!("5b829920b9c8d554a19ee2a1ba593c4f2ee6fc32822d083e04236d693e8358d5").into()));
} }
@@ -401,7 +424,7 @@ mod tests {
overlay.prospective.top.get_mut(&vec![1]).unwrap().value = None; overlay.prospective.top.get_mut(&vec![1]).unwrap().value = None;
let storage = TestChangesTrieStorage::new(); let storage = TestChangesTrieStorage::new();
let backend = TestBackend::default(); let backend = TestBackend::default();
let mut ext = TestExt::new(&mut overlay, &backend, Some(&storage)); let mut ext = TestExt::new(&mut overlay, &backend, Some(&storage), None);
assert_eq!(ext.storage_changes_root(Default::default(), 99), assert_eq!(ext.storage_changes_root(Default::default(), 99),
Some(hex!("bcf494e41e29a15c9ae5caa053fe3cb8b446ee3e02a254efbdec7a19235b76e4").into())); Some(hex!("bcf494e41e29a15c9ae5caa053fe3cb8b446ee3e02a254efbdec7a19235b76e4").into()));
} }
+44 -8
View File
@@ -23,7 +23,7 @@ use log::warn;
use hash_db::Hasher; use hash_db::Hasher;
use heapsize::HeapSizeOf; use heapsize::HeapSizeOf;
use parity_codec::{Decode, Encode}; use parity_codec::{Decode, Encode};
use primitives::{storage::well_known_keys, NativeOrEncoded, NeverNativeValue}; use primitives::{storage::well_known_keys, NativeOrEncoded, NeverNativeValue, OffchainExt};
pub mod backend; pub mod backend;
mod changes_trie; mod changes_trie;
@@ -153,6 +153,25 @@ pub trait Externalities<H: Hasher> {
/// Get the change trie root of the current storage overlay at a block with given parent. /// Get the change trie root of the current storage overlay at a block with given parent.
fn storage_changes_root(&mut self, parent: H::Out, parent_num: u64) -> Option<H::Out> where H::Out: Ord; fn storage_changes_root(&mut self, parent: H::Out, parent_num: u64) -> Option<H::Out> where H::Out: Ord;
/// Submit extrinsic.
///
/// Returns an error in case the API is not available.
fn submit_extrinsic(&mut self, extrinsic: Vec<u8>) -> Result<(), ()>;
}
/// An implementation of offchain extensions that should never be triggered.
pub enum NeverOffchainExt {}
impl NeverOffchainExt {
/// Create new offchain extensions.
pub fn new<'a>() -> Option<&'a mut Self> {
None
}
}
impl OffchainExt for NeverOffchainExt {
fn submit_extrinsic(&mut self, _extrinsic: Vec<u8>) { unreachable!() }
} }
/// Code execution engine. /// Code execution engine.
@@ -252,17 +271,19 @@ pub fn always_wasm<E, R: Decode>() -> ExecutionManager<DefaultHandler<R, E>> {
} }
/// Creates new substrate state machine. /// Creates new substrate state machine.
pub fn new<'a, H, B, T, Exec>( pub fn new<'a, H, B, T, O, Exec>(
backend: &'a B, backend: &'a B,
changes_trie_storage: Option<&'a T>, changes_trie_storage: Option<&'a T>,
offchain_ext: Option<&'a mut O>,
overlay: &'a mut OverlayedChanges, overlay: &'a mut OverlayedChanges,
exec: &'a Exec, exec: &'a Exec,
method: &'a str, method: &'a str,
call_data: &'a [u8], call_data: &'a [u8],
) -> StateMachine<'a, H, B, T, Exec> { ) -> StateMachine<'a, H, B, T, O, Exec> {
StateMachine { StateMachine {
backend, backend,
changes_trie_storage, changes_trie_storage,
offchain_ext,
overlay, overlay,
exec, exec,
method, method,
@@ -272,9 +293,10 @@ pub fn new<'a, H, B, T, Exec>(
} }
/// The substrate state machine. /// The substrate state machine.
pub struct StateMachine<'a, H, B, T, Exec> { pub struct StateMachine<'a, H, B, T, O, Exec> {
backend: &'a B, backend: &'a B,
changes_trie_storage: Option<&'a T>, changes_trie_storage: Option<&'a T>,
offchain_ext: Option<&'a mut O>,
overlay: &'a mut OverlayedChanges, overlay: &'a mut OverlayedChanges,
exec: &'a Exec, exec: &'a Exec,
method: &'a str, method: &'a str,
@@ -282,11 +304,12 @@ pub struct StateMachine<'a, H, B, T, Exec> {
_hasher: PhantomData<H>, _hasher: PhantomData<H>,
} }
impl<'a, H, B, T, Exec> StateMachine<'a, H, B, T, Exec> where impl<'a, H, B, T, O, Exec> StateMachine<'a, H, B, T, O, Exec> where
H: Hasher, H: Hasher,
Exec: CodeExecutor<H>, Exec: CodeExecutor<H>,
B: Backend<H>, B: Backend<H>,
T: ChangesTrieStorage<H>, T: ChangesTrieStorage<H>,
O: OffchainExt,
H::Out: Ord + HeapSizeOf, H::Out: Ord + HeapSizeOf,
{ {
/// Execute a call using the given state backend, overlayed changes, and call executor. /// Execute a call using the given state backend, overlayed changes, and call executor.
@@ -324,7 +347,13 @@ impl<'a, H, B, T, Exec> StateMachine<'a, H, B, T, Exec> where
R: Decode + Encode + PartialEq, R: Decode + Encode + PartialEq,
NC: FnOnce() -> result::Result<R, &'static str> + UnwindSafe, NC: FnOnce() -> result::Result<R, &'static str> + UnwindSafe,
{ {
let mut externalities = ext::Ext::new(self.overlay, self.backend, self.changes_trie_storage); let offchain = self.offchain_ext.as_mut();
let mut externalities = ext::Ext::new(
self.overlay,
self.backend,
self.changes_trie_storage,
offchain.map(|x| &mut **x),
);
let (result, was_native) = self.exec.call( let (result, was_native) = self.exec.call(
&mut externalities, &mut externalities,
self.method, self.method,
@@ -505,6 +534,7 @@ where
let mut sm = StateMachine { let mut sm = StateMachine {
backend: &proving_backend, backend: &proving_backend,
changes_trie_storage: None as Option<&changes_trie::InMemoryStorage<H>>, changes_trie_storage: None as Option<&changes_trie::InMemoryStorage<H>>,
offchain_ext: NeverOffchainExt::new(),
overlay, overlay,
exec, exec,
method, method,
@@ -554,6 +584,7 @@ where
let mut sm = StateMachine { let mut sm = StateMachine {
backend: trie_backend, backend: trie_backend,
changes_trie_storage: None as Option<&changes_trie::InMemoryStorage<H>>, changes_trie_storage: None as Option<&changes_trie::InMemoryStorage<H>>,
offchain_ext: NeverOffchainExt::new(),
overlay, overlay,
exec, exec,
method, method,
@@ -730,6 +761,7 @@ mod tests {
assert_eq!(new( assert_eq!(new(
&trie_backend::tests::test_trie(), &trie_backend::tests::test_trie(),
Some(&InMemoryChangesTrieStorage::new()), Some(&InMemoryChangesTrieStorage::new()),
NeverOffchainExt::new(),
&mut Default::default(), &mut Default::default(),
&DummyCodeExecutor { &DummyCodeExecutor {
change_changes_trie_config: false, change_changes_trie_config: false,
@@ -750,6 +782,7 @@ mod tests {
assert_eq!(new( assert_eq!(new(
&trie_backend::tests::test_trie(), &trie_backend::tests::test_trie(),
Some(&InMemoryChangesTrieStorage::new()), Some(&InMemoryChangesTrieStorage::new()),
NeverOffchainExt::new(),
&mut Default::default(), &mut Default::default(),
&DummyCodeExecutor { &DummyCodeExecutor {
change_changes_trie_config: false, change_changes_trie_config: false,
@@ -770,6 +803,7 @@ mod tests {
assert!(new( assert!(new(
&trie_backend::tests::test_trie(), &trie_backend::tests::test_trie(),
Some(&InMemoryChangesTrieStorage::new()), Some(&InMemoryChangesTrieStorage::new()),
NeverOffchainExt::new(),
&mut Default::default(), &mut Default::default(),
&DummyCodeExecutor { &DummyCodeExecutor {
change_changes_trie_config: false, change_changes_trie_config: false,
@@ -838,7 +872,7 @@ mod tests {
{ {
let changes_trie_storage = InMemoryChangesTrieStorage::new(); let changes_trie_storage = InMemoryChangesTrieStorage::new();
let mut ext = Ext::new(&mut overlay, &backend, Some(&changes_trie_storage)); let mut ext = Ext::new(&mut overlay, &backend, Some(&changes_trie_storage), NeverOffchainExt::new());
ext.clear_prefix(b"ab"); ext.clear_prefix(b"ab");
} }
overlay.commit_prospective(); overlay.commit_prospective();
@@ -862,7 +896,7 @@ mod tests {
let backend = InMemory::<Blake2Hasher>::default().try_into_trie_backend().unwrap(); let backend = InMemory::<Blake2Hasher>::default().try_into_trie_backend().unwrap();
let changes_trie_storage = InMemoryChangesTrieStorage::new(); let changes_trie_storage = InMemoryChangesTrieStorage::new();
let mut overlay = OverlayedChanges::default(); let mut overlay = OverlayedChanges::default();
let mut ext = Ext::new(&mut overlay, &backend, Some(&changes_trie_storage)); let mut ext = Ext::new(&mut overlay, &backend, Some(&changes_trie_storage), NeverOffchainExt::new());
assert!(ext.set_child_storage(b":child_storage:testchild".to_vec(), b"abc".to_vec(), b"def".to_vec())); assert!(ext.set_child_storage(b":child_storage:testchild".to_vec(), b"abc".to_vec(), b"def".to_vec()));
assert_eq!(ext.child_storage(b":child_storage:testchild", b"abc"), Some(b"def".to_vec())); assert_eq!(ext.child_storage(b":child_storage:testchild", b"abc"), Some(b"def".to_vec()));
@@ -889,6 +923,7 @@ mod tests {
assert!(new( assert!(new(
&trie_backend::tests::test_trie(), &trie_backend::tests::test_trie(),
Some(&InMemoryChangesTrieStorage::new()), Some(&InMemoryChangesTrieStorage::new()),
NeverOffchainExt::new(),
&mut Default::default(), &mut Default::default(),
&DummyCodeExecutor { &DummyCodeExecutor {
change_changes_trie_config: true, change_changes_trie_config: true,
@@ -908,6 +943,7 @@ mod tests {
assert!(new( assert!(new(
&trie_backend::tests::test_trie(), &trie_backend::tests::test_trie(),
Some(&InMemoryChangesTrieStorage::new()), Some(&InMemoryChangesTrieStorage::new()),
NeverOffchainExt::new(),
&mut Default::default(), &mut Default::default(),
&DummyCodeExecutor { &DummyCodeExecutor {
change_changes_trie_config: true, change_changes_trie_config: true,
@@ -375,7 +375,12 @@ mod tests {
}; };
let changes_trie_storage = InMemoryChangesTrieStorage::new(); let changes_trie_storage = InMemoryChangesTrieStorage::new();
let mut ext = Ext::new(&mut overlay, &backend, Some(&changes_trie_storage)); let mut ext = Ext::new(
&mut overlay,
&backend,
Some(&changes_trie_storage),
crate::NeverOffchainExt::new(),
);
const ROOT: [u8; 32] = hex!("0b41e488cccbd67d1f1089592c2c235f5c5399b053f7fe9152dd4b5f279914cd"); const ROOT: [u8; 32] = hex!("0b41e488cccbd67d1f1089592c2c235f5c5399b053f7fe9152dd4b5f279914cd");
assert_eq!(ext.storage_root(), H256::from(ROOT)); assert_eq!(ext.storage_root(), H256::from(ROOT));
} }
@@ -168,6 +168,10 @@ impl<H: Hasher> Externalities<H> for TestExternalities<H> where H::Out: Ord + He
&AnchorBlockId { hash: parent, number: parent_num }, &AnchorBlockId { hash: parent, number: parent_num },
).map(|(root, _)| root.clone()) ).map(|(root, _)| root.clone())
} }
fn submit_extrinsic(&mut self, _extrinsic: Vec<u8>) -> Result<(), ()> {
unimplemented!()
}
} }
#[cfg(test)] #[cfg(test)]
+1
View File
@@ -128,6 +128,7 @@ pub fn new_with_execution_strategy(
syncing: execution_strategy, syncing: execution_strategy,
importing: execution_strategy, importing: execution_strategy,
block_construction: execution_strategy, block_construction: execution_strategy,
offchain_worker: execution_strategy,
other: execution_strategy, other: execution_strategy,
}; };
+2
View File
@@ -20,6 +20,7 @@ runtime_io = { package = "sr-io", path = "../sr-io", default-features = false }
runtime_primitives = { package = "sr-primitives", path = "../sr-primitives", default-features = false } runtime_primitives = { package = "sr-primitives", path = "../sr-primitives", default-features = false }
runtime_version = { package = "sr-version", path = "../sr-version", default-features = false } runtime_version = { package = "sr-version", path = "../sr-version", default-features = false }
runtime_support = { package = "srml-support", path = "../../srml/support", default-features = false } runtime_support = { package = "srml-support", path = "../../srml/support", default-features = false }
offchain-primitives = { package = "substrate-offchain-primitives", path = "../offchain/primitives", default-features = false}
executive = { package = "srml-executive", path = "../../srml/executive", default-features = false } executive = { package = "srml-executive", path = "../../srml/executive", default-features = false }
cfg-if = "0.1.6" cfg-if = "0.1.6"
@@ -45,5 +46,6 @@ std = [
"runtime_primitives/std", "runtime_primitives/std",
"runtime_version/std", "runtime_version/std",
"consensus_aura/std", "consensus_aura/std",
"offchain-primitives/std",
"executive/std", "executive/std",
] ]
+16
View File
@@ -94,6 +94,7 @@ impl Transfer {
pub enum Extrinsic { pub enum Extrinsic {
AuthoritiesChange(Vec<AuthorityId>), AuthoritiesChange(Vec<AuthorityId>),
Transfer(Transfer, AccountSignature), Transfer(Transfer, AccountSignature),
IncludeData(Vec<u8>),
} }
#[cfg(feature = "std")] #[cfg(feature = "std")]
@@ -117,6 +118,7 @@ impl BlindCheckable for Extrinsic {
Err(runtime_primitives::BAD_SIGNATURE) Err(runtime_primitives::BAD_SIGNATURE)
} }
}, },
Extrinsic::IncludeData(data) => Ok(Extrinsic::IncludeData(data)),
} }
} }
} }
@@ -377,6 +379,13 @@ cfg_if! {
impl consensus_aura::AuraApi<Block> for Runtime { impl consensus_aura::AuraApi<Block> for Runtime {
fn slot_duration() -> u64 { 1 } fn slot_duration() -> u64 { 1 }
} }
impl offchain_primitives::OffchainWorkerApi<Block> for Runtime {
fn offchain_worker(block: u64) {
let ex = Extrinsic::IncludeData(block.encode());
runtime_io::submit_extrinsic(&ex)
}
}
} }
} else { } else {
impl_runtime_apis! { impl_runtime_apis! {
@@ -480,6 +489,13 @@ cfg_if! {
impl consensus_aura::AuraApi<Block> for Runtime { impl consensus_aura::AuraApi<Block> for Runtime {
fn slot_duration() -> u64 { 1 } fn slot_duration() -> u64 { 1 }
} }
impl offchain_primitives::OffchainWorkerApi<Block> for Runtime {
fn offchain_worker(block: u64) {
let ex = Extrinsic::IncludeData(block.encode());
runtime_io::submit_extrinsic(&ex)
}
}
} }
} }
} }
@@ -242,6 +242,7 @@ fn execute_transaction_backend(utx: &Extrinsic) -> ApplyResult {
match utx { match utx {
Extrinsic::Transfer(ref transfer, _) => execute_transfer_backend(transfer), Extrinsic::Transfer(ref transfer, _) => execute_transfer_backend(transfer),
Extrinsic::AuthoritiesChange(ref new_auth) => execute_new_authorities_backend(new_auth), Extrinsic::AuthoritiesChange(ref new_auth) => execute_new_authorities_backend(new_auth),
Extrinsic::IncludeData(_) => Ok(ApplyOutcome::Success),
} }
} }
+9
View File
@@ -2346,6 +2346,14 @@ dependencies = [
"substrate-primitives 0.1.0", "substrate-primitives 0.1.0",
] ]
[[package]]
name = "substrate-offchain-primitives"
version = "0.1.0"
dependencies = [
"sr-primitives 0.1.0",
"substrate-client 0.1.0",
]
[[package]] [[package]]
name = "substrate-panic-handler" name = "substrate-panic-handler"
version = "0.1.0" version = "0.1.0"
@@ -2446,6 +2454,7 @@ dependencies = [
"substrate-consensus-aura-primitives 0.1.0", "substrate-consensus-aura-primitives 0.1.0",
"substrate-inherents 0.1.0", "substrate-inherents 0.1.0",
"substrate-keyring 0.1.0", "substrate-keyring 0.1.0",
"substrate-offchain-primitives 0.1.0",
"substrate-primitives 0.1.0", "substrate-primitives 0.1.0",
] ]
@@ -25,6 +25,7 @@ sudo = { package = "srml-sudo", path = "../../srml/sudo", default_features = fal
runtime-primitives = { package = "sr-primitives", path = "../../core/sr-primitives", default_features = false } runtime-primitives = { package = "sr-primitives", path = "../../core/sr-primitives", default_features = false }
client = { package = "substrate-client", path = "../../core/client", default_features = false } client = { package = "substrate-client", path = "../../core/client", default_features = false }
consensus-aura = { package = "substrate-consensus-aura-primitives", path = "../../core/consensus/aura/primitives", default_features = false } consensus-aura = { package = "substrate-consensus-aura-primitives", path = "../../core/consensus/aura/primitives", default_features = false }
offchain-primitives = { package = "substrate-offchain-primitives", path = "../../core/offchain/primitives", default-features = false }
[features] [features]
default = ["std"] default = ["std"]
@@ -48,4 +49,5 @@ std = [
"serde", "serde",
"safe-mix/std", "safe-mix/std",
"consensus-aura/std", "consensus-aura/std",
"offchain-primitives/std",
] ]
+7 -1
View File
@@ -14,7 +14,7 @@ use primitives::bytes;
use primitives::{ed25519, OpaqueMetadata}; use primitives::{ed25519, OpaqueMetadata};
use runtime_primitives::{ use runtime_primitives::{
ApplyResult, transaction_validity::TransactionValidity, generic, create_runtime_str, ApplyResult, transaction_validity::TransactionValidity, generic, create_runtime_str,
traits::{self, BlakeTwo256, Block as BlockT, StaticLookup, Verify} traits::{self, NumberFor, BlakeTwo256, Block as BlockT, StaticLookup, Verify}
}; };
use client::{ use client::{
block_builder::api::{CheckInherentsResult, InherentData, self as block_builder_api}, block_builder::api::{CheckInherentsResult, InherentData, self as block_builder_api},
@@ -280,4 +280,10 @@ impl_runtime_apis! {
Aura::slot_duration() Aura::slot_duration()
} }
} }
impl offchain_primitives::OffchainWorkerApi<Block> for Runtime {
fn offchain_worker(n: NumberFor<Block>) {
Executive::offchain_worker(n)
}
}
} }
+9
View File
@@ -1273,6 +1273,7 @@ dependencies = [
"srml-timestamp 0.1.0", "srml-timestamp 0.1.0",
"substrate-client 0.1.0", "substrate-client 0.1.0",
"substrate-consensus-aura-primitives 0.1.0", "substrate-consensus-aura-primitives 0.1.0",
"substrate-offchain-primitives 0.1.0",
"substrate-primitives 0.1.0", "substrate-primitives 0.1.0",
] ]
@@ -2510,6 +2511,14 @@ dependencies = [
"substrate-primitives 0.1.0", "substrate-primitives 0.1.0",
] ]
[[package]]
name = "substrate-offchain-primitives"
version = "0.1.0"
dependencies = [
"sr-primitives 0.1.0",
"substrate-client 0.1.0",
]
[[package]] [[package]]
name = "substrate-panic-handler" name = "substrate-panic-handler"
version = "0.1.0" version = "0.1.0"
+1
View File
@@ -62,6 +62,7 @@ construct_service_factory! {
let proposer = Arc::new(ProposerFactory { let proposer = Arc::new(ProposerFactory {
client: service.client(), client: service.client(),
transaction_pool: service.transaction_pool(), transaction_pool: service.transaction_pool(),
inherents_pool: service.inherents_pool(),
}); });
let client = service.client(); let client = service.client();
executor.spawn(start_aura( executor.spawn(start_aura(
+1
View File
@@ -85,6 +85,7 @@ construct_service_factory! {
let proposer = Arc::new(substrate_basic_authorship::ProposerFactory { let proposer = Arc::new(substrate_basic_authorship::ProposerFactory {
client: service.client(), client: service.client(),
transaction_pool: service.transaction_pool(), transaction_pool: service.transaction_pool(),
inherents_pool: service.inherents_pool(),
}); });
let client = service.client(); let client = service.client();
+1
View File
@@ -12,6 +12,7 @@ substrate-primitives = { path = "../../core/primitives", default-features = fals
client = { package = "substrate-client", path = "../../core/client", default-features = false } client = { package = "substrate-client", path = "../../core/client", default-features = false }
rstd = { package = "sr-std", path = "../../core/sr-std", default-features = false } rstd = { package = "sr-std", path = "../../core/sr-std", default-features = false }
runtime_primitives = { package = "sr-primitives", path = "../../core/sr-primitives", default-features = false } runtime_primitives = { package = "sr-primitives", path = "../../core/sr-primitives", default-features = false }
offchain-primitives = { package = "substrate-offchain-primitives", path = "../../core/offchain/primitives", default-features = false }
version = { package = "sr-version", path = "../../core/sr-version", default-features = false } version = { package = "sr-version", path = "../../core/sr-version", default-features = false }
support = { package = "srml-support", path = "../../srml/support", default-features = false } support = { package = "srml-support", path = "../../srml/support", default-features = false }
aura = { package = "srml-aura", path = "../../srml/aura", default-features = false } aura = { package = "srml-aura", path = "../../srml/aura", default-features = false }
+8 -2
View File
@@ -58,8 +58,8 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
spec_name: create_runtime_str!("node"), spec_name: create_runtime_str!("node"),
impl_name: create_runtime_str!("substrate-node"), impl_name: create_runtime_str!("substrate-node"),
authoring_version: 10, authoring_version: 10,
spec_version: 42, spec_version: 43,
impl_version: 42, impl_version: 43,
apis: RUNTIME_API_VERSIONS, apis: RUNTIME_API_VERSIONS,
}; };
@@ -288,6 +288,12 @@ impl_runtime_apis! {
} }
} }
impl offchain_primitives::OffchainWorkerApi<Block> for Runtime {
fn offchain_worker(number: NumberFor<Block>) {
Executive::offchain_worker(number)
}
}
impl fg_primitives::GrandpaApi<Block> for Runtime { impl fg_primitives::GrandpaApi<Block> for Runtime {
fn grandpa_pending_change(digest: &DigestFor<Block>) fn grandpa_pending_change(digest: &DigestFor<Block>)
-> Option<ScheduledChange<NumberFor<Block>>> -> Option<ScheduledChange<NumberFor<Block>>>
+9
View File
@@ -1297,6 +1297,7 @@ dependencies = [
"substrate-client 0.1.0", "substrate-client 0.1.0",
"substrate-consensus-aura-primitives 0.1.0", "substrate-consensus-aura-primitives 0.1.0",
"substrate-keyring 0.1.0", "substrate-keyring 0.1.0",
"substrate-offchain-primitives 0.1.0",
"substrate-primitives 0.1.0", "substrate-primitives 0.1.0",
] ]
@@ -2666,6 +2667,14 @@ dependencies = [
"substrate-primitives 0.1.0", "substrate-primitives 0.1.0",
] ]
[[package]]
name = "substrate-offchain-primitives"
version = "0.1.0"
dependencies = [
"sr-primitives 0.1.0",
"substrate-client 0.1.0",
]
[[package]] [[package]]
name = "substrate-panic-handler" name = "substrate-panic-handler"
version = "0.1.0" version = "0.1.0"
+9
View File
@@ -214,6 +214,15 @@ decl_module! {
// We just kill our dummy storage item. // We just kill our dummy storage item.
<Dummy<T>>::kill(); <Dummy<T>>::kill();
} }
// A runtime code run after every block and have access to extended set of APIs.
//
// For instance you can generate extrinsics for the upcoming produced block.
fn offchain_worker(_n: T::BlockNumber) {
// We don't do anything here.
// but we could dispatch extrinsic (transaction/inherent) using
// runtime_io::submit_extrinsic
}
} }
} }
+10 -5
View File
@@ -23,7 +23,7 @@ use rstd::marker::PhantomData;
use rstd::result; use rstd::result;
use primitives::traits::{ use primitives::traits::{
self, Header, Zero, One, Checkable, Applyable, CheckEqual, OnFinalise, self, Header, Zero, One, Checkable, Applyable, CheckEqual, OnFinalise,
OnInitialise, Hash, As, Digest, NumberFor, Block as BlockT OnInitialise, Hash, As, Digest, NumberFor, Block as BlockT, OffchainWorker
}; };
use srml_support::{Dispatchable, traits::MakePayment}; use srml_support::{Dispatchable, traits::MakePayment};
use parity_codec::{Codec, Encode}; use parity_codec::{Codec, Encode};
@@ -65,7 +65,7 @@ impl<
Block: traits::Block<Header=System::Header, Hash=System::Hash>, Block: traits::Block<Header=System::Header, Hash=System::Hash>,
Context: Default, Context: Default,
Payment: MakePayment<System::AccountId>, Payment: MakePayment<System::AccountId>,
AllModules: OnInitialise<System::BlockNumber> + OnFinalise<System::BlockNumber>, AllModules: OnInitialise<System::BlockNumber> + OnFinalise<System::BlockNumber> + OffchainWorker<System::BlockNumber>,
> ExecuteBlock<Block> for Executive<System, Block, Context, Payment, AllModules> where > ExecuteBlock<Block> for Executive<System, Block, Context, Payment, AllModules> where
Block::Extrinsic: Checkable<Context> + Codec, Block::Extrinsic: Checkable<Context> + Codec,
<Block::Extrinsic as Checkable<Context>>::Checked: Applyable<Index=System::Index, AccountId=System::AccountId>, <Block::Extrinsic as Checkable<Context>>::Checked: Applyable<Index=System::Index, AccountId=System::AccountId>,
@@ -73,11 +73,11 @@ impl<
<<<Block::Extrinsic as Checkable<Context>>::Checked as Applyable>::Call as Dispatchable>::Origin: From<Option<System::AccountId>> <<<Block::Extrinsic as Checkable<Context>>::Checked as Applyable>::Call as Dispatchable>::Origin: From<Option<System::AccountId>>
{ {
fn execute_block(block: Block) { fn execute_block(block: Block) {
Self::execute_block(block); Executive::<System, Block, Context, Payment, AllModules>::execute_block(block);
} }
fn execute_extrinsics_without_checks(block_number: NumberFor<Block>, extrinsics: Vec<Block::Extrinsic>) { fn execute_extrinsics_without_checks(block_number: NumberFor<Block>, extrinsics: Vec<Block::Extrinsic>) {
Self::execute_extrinsics_without_checks(block_number, extrinsics); Executive::<System, Block, Context, Payment, AllModules>::execute_extrinsics_without_checks(block_number, extrinsics);
} }
} }
@@ -86,7 +86,7 @@ impl<
Block: traits::Block<Header=System::Header, Hash=System::Hash>, Block: traits::Block<Header=System::Header, Hash=System::Hash>,
Context: Default, Context: Default,
Payment: MakePayment<System::AccountId>, Payment: MakePayment<System::AccountId>,
AllModules: OnInitialise<System::BlockNumber> + OnFinalise<System::BlockNumber>, AllModules: OnInitialise<System::BlockNumber> + OnFinalise<System::BlockNumber> + OffchainWorker<System::BlockNumber>,
> Executive<System, Block, Context, Payment, AllModules> where > Executive<System, Block, Context, Payment, AllModules> where
Block::Extrinsic: Checkable<Context> + Codec, Block::Extrinsic: Checkable<Context> + Codec,
<Block::Extrinsic as Checkable<Context>>::Checked: Applyable<Index=System::Index, AccountId=System::AccountId>, <Block::Extrinsic as Checkable<Context>>::Checked: Applyable<Index=System::Index, AccountId=System::AccountId>,
@@ -319,6 +319,11 @@ impl<
}) })
} }
} }
/// Start an offchain worker and generate extrinsics.
pub fn offchain_worker(n: System::BlockNumber) {
<AllModules as OffchainWorker<System::BlockNumber>>::generate_extrinsics(n)
}
} }
#[cfg(test)] #[cfg(test)]
+84
View File
@@ -117,6 +117,7 @@ macro_rules! decl_module {
{} {}
{} {}
{} {}
{}
[] []
$($t)* $($t)*
); );
@@ -135,6 +136,7 @@ macro_rules! decl_module {
{} {}
{} {}
{} {}
{}
[] []
$($t)* $($t)*
); );
@@ -147,6 +149,7 @@ macro_rules! decl_module {
{} {}
{ $( $on_initialise:tt )* } { $( $on_initialise:tt )* }
{ $( $on_finalise:tt )* } { $( $on_finalise:tt )* }
{ $( $offchain:tt )* }
[ $($t:tt)* ] [ $($t:tt)* ]
$(#[doc = $doc_attr:tt])* $(#[doc = $doc_attr:tt])*
$vis:vis fn deposit_event $(<$dpeg:ident $(, $dpeg_instance:ident)?>)* () = default; $vis:vis fn deposit_event $(<$dpeg:ident $(, $dpeg_instance:ident)?>)* () = default;
@@ -159,6 +162,7 @@ macro_rules! decl_module {
{ $vis fn deposit_event $(<$dpeg $(, $dpeg_instance)?>)* () = default; } { $vis fn deposit_event $(<$dpeg $(, $dpeg_instance)?>)* () = default; }
{ $( $on_initialise )* } { $( $on_initialise )* }
{ $( $on_finalise )* } { $( $on_finalise )* }
{ $( $offchain )* }
[ $($t)* ] [ $($t)* ]
$($rest)* $($rest)*
); );
@@ -170,6 +174,7 @@ macro_rules! decl_module {
{} {}
{ $( $on_initialise:tt )* } { $( $on_initialise:tt )* }
{ $( $on_finalise:tt )* } { $( $on_finalise:tt )* }
{ $( $offchain:tt )* }
[ $($t:tt)* ] [ $($t:tt)* ]
$(#[doc = $doc_attr:tt])* $(#[doc = $doc_attr:tt])*
$vis:vis fn deposit_event $(<$dpeg:ident $(, $dpeg_instance:ident)?>)* ( $vis:vis fn deposit_event $(<$dpeg:ident $(, $dpeg_instance:ident)?>)* (
@@ -184,6 +189,7 @@ macro_rules! decl_module {
{ $vis fn deposit_event $(<$dpeg $(, $dpeg_instance)?>)* ($( $param_name: $param ),* ) { $( $impl )* } } { $vis fn deposit_event $(<$dpeg $(, $dpeg_instance)?>)* ($( $param_name: $param ),* ) { $( $impl )* } }
{ $( $on_initialise )* } { $( $on_initialise )* }
{ $( $on_finalise )* } { $( $on_finalise )* }
{ $( $offchain )* }
[ $($t)* ] [ $($t)* ]
$($rest)* $($rest)*
); );
@@ -195,6 +201,7 @@ macro_rules! decl_module {
{ $( $deposit_event:tt )* } { $( $deposit_event:tt )* }
{ $( $on_initialise:tt )* } { $( $on_initialise:tt )* }
{} {}
{ $( $offchain:tt )* }
[ $($t:tt)* ] [ $($t:tt)* ]
$(#[doc = $doc_attr:tt])* $(#[doc = $doc_attr:tt])*
fn on_finalise($($param_name:ident : $param:ty),* ) { $( $impl:tt )* } fn on_finalise($($param_name:ident : $param:ty),* ) { $( $impl:tt )* }
@@ -207,6 +214,7 @@ macro_rules! decl_module {
{ $( $deposit_event )* } { $( $deposit_event )* }
{ $( $on_initialise )* } { $( $on_initialise )* }
{ fn on_finalise( $( $param_name : $param ),* ) { $( $impl )* } } { fn on_finalise( $( $param_name : $param ),* ) { $( $impl )* } }
{ $( $offchain )* }
[ $($t)* ] [ $($t)* ]
$($rest)* $($rest)*
); );
@@ -218,6 +226,7 @@ macro_rules! decl_module {
{ $( $deposit_event:tt )* } { $( $deposit_event:tt )* }
{} {}
{ $( $on_finalise:tt )* } { $( $on_finalise:tt )* }
{ $( $offchain:tt )* }
[ $($t:tt)* ] [ $($t:tt)* ]
$(#[doc = $doc_attr:tt])* $(#[doc = $doc_attr:tt])*
fn on_initialise($($param_name:ident : $param:ty),* ) { $( $impl:tt )* } fn on_initialise($($param_name:ident : $param:ty),* ) { $( $impl:tt )* }
@@ -230,6 +239,32 @@ macro_rules! decl_module {
{ $( $deposit_event )* } { $( $deposit_event )* }
{ fn on_initialise( $( $param_name : $param ),* ) { $( $impl )* } } { fn on_initialise( $( $param_name : $param ),* ) { $( $impl )* } }
{ $( $on_finalise )* } { $( $on_finalise )* }
{ $( $offchain )* }
[ $($t)* ]
$($rest)*
);
};
(@normalize
$(#[$attr:meta])*
pub struct $mod_type:ident<$trait_instance:ident: $trait_name:ident>
for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident
{ $( $deposit_event:tt )* }
{ $( $on_initialise:tt )* }
{ $( $on_finalise:tt )* }
{ }
[ $($t:tt)* ]
$(#[doc = $doc_attr:tt])*
fn offchain_worker($($param_name:ident : $param:ty),* ) { $( $impl:tt )* }
$($rest:tt)*
) => {
decl_module!(@normalize
$(#[$attr])*
pub struct $mod_type<$trait_instance: $trait_name>
for enum $call_type where origin: $origin_type, system = $system
{ $( $deposit_event )* }
{ $( $on_initialise )* }
{ $( $on_finalise )* }
{ fn offchain_worker( $( $param_name : $param ),* ) { $( $impl )* } }
[ $($t)* ] [ $($t)* ]
$($rest)* $($rest)*
); );
@@ -241,6 +276,7 @@ macro_rules! decl_module {
{ $( $deposit_event:tt )* } { $( $deposit_event:tt )* }
{ $( $on_initialise:tt )* } { $( $on_initialise:tt )* }
{ $( $on_finalise:tt )* } { $( $on_finalise:tt )* }
{ $( $offchain:tt )* }
[ $($t:tt)* ] [ $($t:tt)* ]
$(#[doc = $doc_attr:tt])* $(#[doc = $doc_attr:tt])*
$fn_vis:vis fn $fn_name:ident( $fn_vis:vis fn $fn_name:ident(
@@ -255,6 +291,7 @@ macro_rules! decl_module {
{ $( $deposit_event )* } { $( $deposit_event )* }
{ $( $on_initialise )* } { $( $on_initialise )* }
{ $( $on_finalise )* } { $( $on_finalise )* }
{ $( $offchain )* }
[ [
$($t)* $($t)*
$(#[doc = $doc_attr])* $(#[doc = $doc_attr])*
@@ -273,6 +310,7 @@ macro_rules! decl_module {
{ $( $deposit_event:tt )* } { $( $deposit_event:tt )* }
{ $( $on_initialise:tt )* } { $( $on_initialise:tt )* }
{ $( $on_finalise:tt )* } { $( $on_finalise:tt )* }
{ $( $offchain:tt )* }
[ $($t:tt)* ] [ $($t:tt)* ]
$(#[doc = $doc_attr:tt])* $(#[doc = $doc_attr:tt])*
$fn_vis:vis fn $fn_name:ident( $fn_vis:vis fn $fn_name:ident(
@@ -293,6 +331,7 @@ macro_rules! decl_module {
{ $( $deposit_event:tt )* } { $( $deposit_event:tt )* }
{ $( $on_initialise:tt )* } { $( $on_initialise:tt )* }
{ $( $on_finalise:tt )* } { $( $on_finalise:tt )* }
{ $( $offchain:tt )* }
[ $($t:tt)* ] [ $($t:tt)* ]
$(#[doc = $doc_attr:tt])* $(#[doc = $doc_attr:tt])*
$fn_vis:vis fn $fn_name:ident( $fn_vis:vis fn $fn_name:ident(
@@ -313,6 +352,7 @@ macro_rules! decl_module {
{ $( $deposit_event:tt )* } { $( $deposit_event:tt )* }
{ $( $on_initialise:tt )* } { $( $on_initialise:tt )* }
{ $( $on_finalise:tt )* } { $( $on_finalise:tt )* }
{ $( $offchain:tt )* }
[ $($t:tt)* ] [ $($t:tt)* ]
$(#[doc = $doc_attr:tt])* $(#[doc = $doc_attr:tt])*
$fn_vis:vis fn $fn_name:ident( $fn_vis:vis fn $fn_name:ident(
@@ -327,6 +367,7 @@ macro_rules! decl_module {
{ $( $deposit_event )* } { $( $deposit_event )* }
{ $( $on_initialise )* } { $( $on_initialise )* }
{ $( $on_finalise )* } { $( $on_finalise )* }
{ $( $offchain )* }
[ [
$($t)* $($t)*
$(#[doc = $doc_attr])* $(#[doc = $doc_attr])*
@@ -345,6 +386,7 @@ macro_rules! decl_module {
{ $( $deposit_event:tt )* } { $( $deposit_event:tt )* }
{ $( $on_initialise:tt )* } { $( $on_initialise:tt )* }
{ $( $on_finalise:tt )* } { $( $on_finalise:tt )* }
{ $( $offchain:tt )* }
[ $($t:tt)* ] [ $($t:tt)* ]
) => { ) => {
decl_module!(@imp decl_module!(@imp
@@ -356,6 +398,7 @@ macro_rules! decl_module {
{ $( $deposit_event )* } { $( $deposit_event )* }
{ $( $on_initialise )* } { $( $on_initialise )* }
{ $( $on_finalise )* } { $( $on_finalise )* }
{ $( $offchain )* }
); );
}; };
@@ -477,6 +520,39 @@ macro_rules! decl_module {
} }
}; };
(@impl_offchain
$module:ident<$trait_instance:ident: $trait_name:ident$(<I>, $instance:ident: $instantiable:path)?>;
fn offchain_worker() { $( $impl:tt )* }
) => {
impl<$trait_instance: $trait_name$(<I>, $instance: $instantiable)?>
$crate::runtime_primitives::traits::OffchainWorker<$trait_instance::BlockNumber>
for $module<$trait_instance$(, $instance)?>
{
fn generate_extrinsics(_block_number_not_used: $trait_instance::BlockNumber) { $( $impl )* }
}
};
(@impl_offchain
$module:ident<$trait_instance:ident: $trait_name:ident$(<I>, $instance:ident: $instantiable:path)?>;
fn offchain_worker($param:ident : $param_ty:ty) { $( $impl:tt )* }
) => {
impl<$trait_instance: $trait_name$(<I>, $instance: $instantiable)?>
$crate::runtime_primitives::traits::OffchainWorker<$trait_instance::BlockNumber>
for $module<$trait_instance$(, $instance)?>
{
fn generate_extrinsics($param: $param_ty) { $( $impl )* }
}
};
(@impl_offchain
$module:ident<$trait_instance:ident: $trait_name:ident$(<I>, $instance:ident: $instantiable:path)?>;
) => {
impl<$trait_instance: $trait_name$(<I>, $instance: $instantiable)?>
$crate::runtime_primitives::traits::OffchainWorker<$trait_instance::BlockNumber>
for $module<$trait_instance$(, $instance)?>
{}
};
(@impl_function (@impl_function
$module:ident<$trait_instance:ident: $trait_name:ident$(<I>, $instance:ident: $instantiable:path)?>; $module:ident<$trait_instance:ident: $trait_name:ident$(<I>, $instance:ident: $instantiable:path)?>;
$origin_ty:ty; $origin_ty:ty;
@@ -556,6 +632,7 @@ macro_rules! decl_module {
{ $( $deposit_event:tt )* } { $( $deposit_event:tt )* }
{ $( $on_initialise:tt )* } { $( $on_initialise:tt )* }
{ $( $on_finalise:tt )* } { $( $on_finalise:tt )* }
{ $( $offchain:tt )* }
) => { ) => {
// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. // Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted.
#[derive(Clone, Copy, PartialEq, Eq)] #[derive(Clone, Copy, PartialEq, Eq)]
@@ -584,6 +661,12 @@ macro_rules! decl_module {
$( $on_finalise )* $( $on_finalise )*
} }
decl_module! {
@impl_offchain
$mod_type<$trait_instance: $trait_name $(<I>, $instance: $instantiable)?>;
$( $offchain )*
}
decl_module! { decl_module! {
@impl_deposit_event @impl_deposit_event
$mod_type<$trait_instance: $trait_name $(<I>, $instance: $instantiable)?>; $mod_type<$trait_instance: $trait_name $(<I>, $instance: $instantiable)?>;
@@ -1086,6 +1169,7 @@ mod tests {
fn on_initialise(n: T::BlockNumber) { if n.into() == 42 { panic!("on_initialise") } } fn on_initialise(n: T::BlockNumber) { if n.into() == 42 { panic!("on_initialise") } }
fn on_finalise(n: T::BlockNumber) { if n.into() == 42 { panic!("on_finalise") } } fn on_finalise(n: T::BlockNumber) { if n.into() == 42 { panic!("on_finalise") } }
fn offchain_worker() {}
} }
} }
+1 -1
View File
@@ -124,8 +124,8 @@ pub struct EventRecord<E: Parameter + Member> {
pub event: E, pub event: E,
} }
/// Event for the system module.
decl_event!( decl_event!(
/// Event for the system module.
pub enum Event { pub enum Event {
/// An extrinsic completed successfully. /// An extrinsic completed successfully.
ExtrinsicSuccess, ExtrinsicSuccess,
+6 -6
View File
@@ -21,11 +21,11 @@ substrate-primitives = { path = "../../core/primitives" }
[features] [features]
default = ["std"] default = ["std"]
std = [ std = [
"rstd/std",
"srml-support/std",
"runtime_primitives/std",
"serde",
"parity-codec/std",
"system/std",
"inherents/std", "inherents/std",
"parity-codec/std",
"rstd/std",
"runtime_primitives/std",
"srml-support/std",
"serde",
"system/std",
] ]