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-consensus-aura-primitives 0.1.0",
"substrate-keyring 0.1.0",
"substrate-offchain-primitives 0.1.0",
"substrate-primitives 0.1.0",
]
@@ -2029,6 +2030,7 @@ dependencies = [
"srml-timestamp 0.1.0",
"substrate-client 0.1.0",
"substrate-consensus-aura-primitives 0.1.0",
"substrate-offchain-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)",
]
[[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]]
name = "substrate-panic-handler"
version = "0.1.0"
@@ -4110,8 +4139,10 @@ dependencies = [
"substrate-client-db 0.1.0",
"substrate-consensus-common 0.1.0",
"substrate-executor 0.1.0",
"substrate-inherents 0.1.0",
"substrate-keystore 0.1.0",
"substrate-network 0.1.0",
"substrate-offchain 0.1.0",
"substrate-primitives 0.1.0",
"substrate-rpc-servers 0.1.0",
"substrate-telemetry 0.3.1",
@@ -4222,6 +4253,7 @@ dependencies = [
"substrate-executor 0.1.0",
"substrate-inherents 0.1.0",
"substrate-keyring 0.1.0",
"substrate-offchain-primitives 0.1.0",
"substrate-primitives 0.1.0",
"substrate-test-client 0.1.0",
]
@@ -20,7 +20,7 @@
//
use std::{self, time, sync::Arc};
use log::{info, debug};
use log::{info, debug, warn};
use client::{
self, error, Client as SubstrateClient, CallExecutor,
@@ -28,15 +28,14 @@ use client::{
};
use codec::Decode;
use consensus_common::{self, evaluation};
use primitives::{H256, Blake2Hasher};
use primitives::{H256, Blake2Hasher, ExecutionContext};
use runtime_primitives::traits::{
Block as BlockT, Hash as HashT, Header as HeaderT, ProvideRuntimeApi, AuthorityIdFor
};
use runtime_primitives::ExecutionContext;
use runtime_primitives::generic::BlockId;
use runtime_primitives::ApplyError;
use transaction_pool::txpool::{self, Pool as TransactionPool};
use inherents::InherentData;
use inherents::{InherentData, pool::InherentsPool};
use substrate_telemetry::{telemetry, CONSENSUS_INFO};
/// Build new blocks.
@@ -115,6 +114,8 @@ pub struct ProposerFactory<C, A> where A: txpool::ChainApi {
pub client: Arc<C>,
/// The transaction pool.
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
@@ -144,6 +145,7 @@ impl<C, A> consensus_common::Environment<<C as AuthoringApi>::Block> for Propose
parent_id: id,
parent_number: *parent_header.number(),
transaction_pool: self.transaction_pool.clone(),
inherents_pool: self.inherents_pool.clone(),
now: Box::new(time::Instant::now),
};
@@ -158,6 +160,7 @@ pub struct Proposer<Block: BlockT, C, A: txpool::ChainApi> {
parent_id: BlockId<Block>,
parent_number: <<Block as BlockT>::Header as HeaderT>::Number,
transaction_pool: Arc<TransactionPool<A>>,
inherents_pool: Arc<InherentsPool<<Block as BlockT>::Extrinsic>>,
now: Box<Fn() -> time::Instant>,
}
@@ -201,11 +204,23 @@ impl<Block, C, A> Proposer<Block, C, A> where
&self.parent_id,
inherent_data,
|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 skipped = 0;
let mut unqueue_invalid = Vec::new();
let pending_iterator = self.transaction_pool.ready();
debug!("Attempting to push transactions from the pool.");
for pending in pending_iterator {
if (self.now)() > deadline {
debug!("Consensus deadline reached when pushing block transactions, proceeding with proposing.");
@@ -303,6 +318,7 @@ mod tests {
let proposer_factory = ProposerFactory {
client: client.clone(),
transaction_pool: txpool.clone(),
inherents_pool: Default::default(),
};
let mut proposer = proposer_factory.init(
@@ -325,4 +341,32 @@ mod tests {
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
};
let exec = cli.execution_strategies;
config.execution_strategies = ExecutionStrategies {
syncing: cli.syncing_execution.into(),
importing: cli.importing_execution.into(),
block_construction: cli.block_construction_execution.into(),
other: cli.other_execution.into(),
syncing: exec.syncing_execution.into(),
importing: exec.importing_execution.into(),
block_construction: exec.block_construction_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;
+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`.
#[derive(Debug, StructOpt, Clone)]
pub struct SharedParams {
@@ -205,6 +215,70 @@ pub struct TransactionPoolParams {
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.
#[derive(Debug, StructOpt, Clone)]
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"))]
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(
long = "syncing-execution",
value_name = "STRATEGY",
long = "offchain-worker",
value_name = "ENABLED",
raw(
possible_values = "&ExecutionStrategy::variants()",
possible_values = "&OffchainWorkerEnabled::variants()",
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.
#[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 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)]
#[structopt(flatten)]
pub execution_strategies: ExecutionStrategies,
#[allow(missing_docs)]
#[structopt(flatten)]
@@ -17,15 +17,15 @@
use super::api::BlockBuilder as BlockBuilderApi;
use std::vec::Vec;
use parity_codec::Encode;
use crate::blockchain::HeaderBackend;
use runtime_primitives::ApplyOutcome;
use runtime_primitives::generic::BlockId;
use runtime_primitives::traits::{
Header as HeaderT, Hash, Block as BlockT, One, HashFor, ProvideRuntimeApi, ApiRef
};
use primitives::H256;
use runtime_primitives::generic::BlockId;
use primitives::{H256, ExecutionContext};
use crate::blockchain::HeaderBackend;
use crate::runtime_api::Core;
use crate::error;
use runtime_primitives::{ApplyOutcome, ExecutionContext};
/// 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::traits::Block as BlockT;
use state_machine::{
self, OverlayedChanges, Ext, CodeExecutor, ExecutionManager, ExecutionStrategy
self, OverlayedChanges, Ext, CodeExecutor, ExecutionManager, ExecutionStrategy, NeverOffchainExt,
};
use executor::{RuntimeVersion, RuntimeInfo, NativeVersion};
use hash_db::Hasher;
use trie::MemoryDB;
use primitives::{H256, Blake2Hasher, NativeOrEncoded, NeverNativeValue};
use primitives::{H256, Blake2Hasher, NativeOrEncoded, NeverNativeValue, OffchainExt};
use crate::backend;
use crate::error;
@@ -42,12 +42,15 @@ where
/// Execute a call to a contract on top of state in a block of given hash.
///
/// No changes are made.
fn call(
fn call<
O: OffchainExt,
>(
&self,
id: &BlockId<B>,
method: &str,
call_data: &[u8],
strategy: ExecutionStrategy,
side_effects_handler: Option<&mut O>,
) -> Result<Vec<u8>, error::Error>;
/// 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
/// of the execution context.
fn contextual_call<
O: OffchainExt,
PB: Fn() -> error::Result<B::Header>,
EM: Fn(
Result<NativeOrEncoded<R>, Self::Error>,
@@ -73,6 +77,7 @@ where
prepare_environment_block: PB,
execution_manager: ExecutionManager<EM>,
native_call: Option<NC>,
side_effects_handler: Option<&mut O>,
) -> error::Result<NativeOrEncoded<R>> where ExecutionManager<EM>: Clone;
/// Extract RuntimeVersion of given block
@@ -84,6 +89,7 @@ where
///
/// No changes are made.
fn call_at_state<
O: OffchainExt,
S: state_machine::Backend<H>,
F: FnOnce(
Result<NativeOrEncoded<R>, Self::Error>,
@@ -98,6 +104,7 @@ where
call_data: &[u8],
manager: ExecutionManager<F>,
native_call: Option<NC>,
side_effects_handler: Option<&mut O>,
) -> Result<(NativeOrEncoded<R>, S::Transaction, Option<MemoryDB<H>>), error::Error>;
/// 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> {
/// Creates new instance of local call executor.
pub fn new(backend: Arc<B>, executor: E) -> Self {
LocalCallExecutor { backend, executor }
LocalCallExecutor {
backend,
executor,
}
}
}
@@ -161,17 +171,19 @@ where
{
type Error = E::Error;
fn call(&self,
fn call<O: OffchainExt>(&self,
id: &BlockId<Block>,
method: &str,
call_data: &[u8],
strategy: ExecutionStrategy
strategy: ExecutionStrategy,
side_effects_handler: Option<&mut O>,
) -> error::Result<Vec<u8>> {
let mut changes = OverlayedChanges::default();
let state = self.backend.state_at(*id)?;
let return_data = state_machine::new(
&state,
self.backend.changes_trie_storage(),
side_effects_handler,
&mut changes,
&self.executor,
method,
@@ -187,6 +199,7 @@ where
}
fn contextual_call<
O: OffchainExt,
PB: Fn() -> error::Result<Block::Header>,
EM: Fn(
Result<NativeOrEncoded<R>, Self::Error>,
@@ -204,6 +217,7 @@ where
prepare_environment_block: PB,
execution_manager: ExecutionManager<EM>,
native_call: Option<NC>,
mut side_effects_handler: Option<&mut O>,
) -> Result<NativeOrEncoded<R>, error::Error> where ExecutionManager<EM>: Clone {
let state = self.backend.state_at(*at)?;
if method != "Core_initialise_block" && initialised_block.map(|id| id != *at).unwrap_or(true) {
@@ -211,6 +225,7 @@ where
state_machine::new(
&state,
self.backend.changes_trie_storage(),
side_effects_handler.as_mut().map(|x| &mut **x),
changes,
&self.executor,
"Core_initialise_block",
@@ -226,6 +241,7 @@ where
let result = state_machine::new(
&state,
self.backend.changes_trie_storage(),
side_effects_handler,
changes,
&self.executor,
method,
@@ -248,12 +264,13 @@ where
fn runtime_version(&self, id: &BlockId<Block>) -> error::Result<RuntimeVersion> {
let mut overlay = OverlayedChanges::default();
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)
.ok_or(error::ErrorKind::VersionInvalid.into())
}
fn call_at_state<
O: OffchainExt,
S: state_machine::Backend<Blake2Hasher>,
F: FnOnce(
Result<NativeOrEncoded<R>, Self::Error>,
@@ -268,10 +285,12 @@ where
call_data: &[u8],
manager: ExecutionManager<F>,
native_call: Option<NC>,
side_effects_handler: Option<&mut O>,
) -> error::Result<(NativeOrEncoded<R>, S::Transaction, Option<MemoryDB<Blake2Hasher>>)> {
state_machine::new(
state,
self.backend.changes_trie_storage(),
side_effects_handler,
changes,
&self.executor,
method,
+21 -8
View File
@@ -33,9 +33,9 @@ use runtime_primitives::traits::{
Block as BlockT, Header as HeaderT, Zero, As, NumberFor, CurrentHeight, BlockNumberToHash,
ApiRef, ProvideRuntimeApi, Digest, DigestItem, AuthorityIdFor
};
use runtime_primitives::{BuildStorage, ExecutionContext};
use runtime_primitives::BuildStorage;
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::well_known_keys;
use parity_codec::{Encode, Decode};
@@ -43,7 +43,7 @@ use state_machine::{
DBValue, Backend as StateBackend, CodeExecutor, ChangesTrieAnchorBlockId,
ExecutionStrategy, ExecutionManager, prove_read,
ChangesTrieRootsStorage, ChangesTrieStorage,
key_changes, key_changes_proof, OverlayedChanges,
key_changes, key_changes_proof, OverlayedChanges, NeverOffchainExt,
};
use hash_db::Hasher;
@@ -84,6 +84,8 @@ pub struct ExecutionStrategies {
pub importing: ExecutionStrategy,
/// Execution strategy used when constructing blocks.
pub block_construction: ExecutionStrategy,
/// Execution strategy used for offchain workers.
pub offchain_worker: ExecutionStrategy,
/// Execution strategy used in other cases.
pub other: ExecutionStrategy,
}
@@ -94,6 +96,7 @@ impl Default for ExecutionStrategies {
syncing: ExecutionStrategy::NativeElseWasm,
importing: ExecutionStrategy::NativeElseWasm,
block_construction: ExecutionStrategy::AlwaysWasm,
offchain_worker: ExecutionStrategy::NativeWhenPossible,
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>>> {
match self.backend.blockchain().cache().and_then(|cache| cache.authorities_at(*id)) {
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[..])
.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,
&mut overlay,
"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),
},
None,
NeverOffchainExt::new(),
)?;
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>
{
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,
at: &BlockId<Block>,
@@ -1348,15 +1353,22 @@ impl<B, E, Block, RA> CallRuntimeAt<Block> for Client<B, E, Block, RA> where
changes: &mut OverlayedChanges,
initialised_block: &mut Option<BlockId<Block>>,
native_call: Option<NC>,
context: ExecutionContext
context: ExecutionContext,
) -> error::Result<NativeOrEncoded<R>> {
let manager = match context {
ExecutionContext::BlockConstruction => self.execution_strategies.block_construction.get_manager(),
ExecutionContext::Syncing => self.execution_strategies.syncing.get_manager(),
ExecutionContext::Importing => self.execution_strategies.importing.get_manager(),
ExecutionContext::OffchainWorker(_) => self.execution_strategies.offchain_worker.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,
function,
&args,
@@ -1365,6 +1377,7 @@ impl<B, E, Block, RA> CallRuntimeAt<Block> for Client<B, E, Block, RA> where
|| self.prepare_environment_block(at),
manager,
native_call,
offchain_extensions.as_mut(),
)
}
+6
View File
@@ -90,6 +90,7 @@ mod tests {
state_machine::new(
backend,
Some(&InMemoryChangesTrieStorage::new()),
state_machine::NeverOffchainExt::new(),
&mut overlay,
&executor(),
"Core_initialise_block",
@@ -102,6 +103,7 @@ mod tests {
state_machine::new(
backend,
Some(&InMemoryChangesTrieStorage::new()),
state_machine::NeverOffchainExt::new(),
&mut overlay,
&executor(),
"BlockBuilder_apply_extrinsic",
@@ -114,6 +116,7 @@ mod tests {
let (ret_data, _, _) = state_machine::new(
backend,
Some(&InMemoryChangesTrieStorage::new()),
state_machine::NeverOffchainExt::new(),
&mut overlay,
&executor(),
"BlockBuilder_finalise_block",
@@ -160,6 +163,7 @@ mod tests {
let _ = state_machine::new(
&backend,
Some(&InMemoryChangesTrieStorage::new()),
state_machine::NeverOffchainExt::new(),
&mut overlay,
&executor(),
"Core_execute_block",
@@ -188,6 +192,7 @@ mod tests {
let _ = state_machine::new(
&backend,
Some(&InMemoryChangesTrieStorage::new()),
state_machine::NeverOffchainExt::new(),
&mut overlay,
&executor(),
"Core_execute_block",
@@ -216,6 +221,7 @@ mod tests {
let r = state_machine::new(
&backend,
Some(&InMemoryChangesTrieStorage::new()),
state_machine::NeverOffchainExt::new(),
&mut overlay,
&Executor::new(None),
"Core_execute_block",
@@ -21,11 +21,11 @@ use std::{collections::HashSet, sync::Arc, panic::UnwindSafe, result, marker::Ph
use futures::{IntoFuture, Future};
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::traits::{As, Block as BlockT, Header as HeaderT};
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 crate::backend::RemoteBackend;
@@ -80,7 +80,16 @@ where
{
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>> {
let block_hash = self.blockchain.expect_block_hash_from_id(id)?;
let block_header = self.blockchain.expect_header(id.clone())?;
@@ -95,6 +104,7 @@ where
}
fn contextual_call<
O: OffchainExt,
PB: Fn() -> ClientResult<Block::Header>,
EM: Fn(
Result<NativeOrEncoded<R>, Self::Error>,
@@ -112,22 +122,24 @@ where
_prepare_environment_block: PB,
execution_manager: ExecutionManager<EM>,
_native_call: Option<NC>,
side_effects_handler: Option<&mut O>,
) -> ClientResult<NativeOrEncoded<R>> where ExecutionManager<EM>: Clone {
// it is only possible to execute contextual call if changes are empty
if !changes.is_empty() || initialised_block.is_some() {
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> {
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())
.ok_or_else(|| ClientErrorKind::VersionInvalid.into())
}
fn call_at_state<
O: OffchainExt,
S: StateBackend<Blake2Hasher>,
FF: FnOnce(
Result<NativeOrEncoded<R>, Self::Error>,
@@ -142,6 +154,7 @@ where
_call_data: &[u8],
_m: ExecutionManager<FF>,
_native_call: Option<NC>,
_side_effects_handler: Option<&mut O>,
) -> ClientResult<(NativeOrEncoded<R>, S::Transaction, Option<MemoryDB<Blake2Hasher>>)> {
Err(ClientErrorKind::NotAvailableOnLightClient.into())
}
@@ -201,15 +214,24 @@ impl<Block, B, Remote, Local> CallExecutor<Block, Blake2Hasher> for
{
type Error = ClientError;
fn call(&self, id: &BlockId<Block>, method: &str, call_data: &[u8], strategy: ExecutionStrategy)
-> ClientResult<Vec<u8>> {
fn call<
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) {
true => self.local.call(id, method, call_data, strategy),
false => self.remote.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, side_effects_handler),
}
}
fn contextual_call<
O: OffchainExt,
PB: Fn() -> ClientResult<Block::Header>,
EM: Fn(
Result<NativeOrEncoded<R>, Self::Error>,
@@ -227,12 +249,14 @@ impl<Block, B, Remote, Local> CallExecutor<Block, Blake2Hasher> for
prepare_environment_block: PB,
_manager: ExecutionManager<EM>,
native_call: Option<NC>,
side_effects_handler: Option<&mut O>,
) -> ClientResult<NativeOrEncoded<R>> where ExecutionManager<EM>: Clone {
// there's no actual way/need to specify native/wasm execution strategy on light node
// => we can safely ignore passed values
match self.backend.is_local_state_available(at) {
true => CallExecutor::contextual_call::<
_,
_,
fn(
Result<NativeOrEncoded<R>, Local::Error>,
@@ -250,8 +274,10 @@ impl<Block, B, Remote, Local> CallExecutor<Block, Blake2Hasher> for
prepare_environment_block,
ExecutionManager::NativeWhenPossible,
native_call,
side_effects_handler,
).map_err(|e| ClientErrorKind::Execution(Box::new(e.to_string())).into()),
false => CallExecutor::contextual_call::<
_,
_,
fn(
Result<NativeOrEncoded<R>, Remote::Error>,
@@ -269,6 +295,7 @@ impl<Block, B, Remote, Local> CallExecutor<Block, Blake2Hasher> for
prepare_environment_block,
ExecutionManager::NativeWhenPossible,
native_call,
side_effects_handler,
).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<
O: OffchainExt,
S: StateBackend<Blake2Hasher>,
FF: FnOnce(
Result<NativeOrEncoded<R>, Self::Error>,
@@ -295,11 +323,13 @@ impl<Block, B, Remote, Local> CallExecutor<Block, Blake2Hasher> for
call_data: &[u8],
_manager: ExecutionManager<FF>,
native_call: Option<NC>,
side_effects_handler: Option<&mut O>,
) -> ClientResult<(NativeOrEncoded<R>, S::Transaction, Option<MemoryDB<Blake2Hasher>>)> {
// there's no actual way/need to specify native/wasm execution strategy on light node
// => we can safely ignore passed values
CallExecutor::call_at_state::<
_,
_,
fn(
Result<NativeOrEncoded<R>, Remote::Error>,
@@ -315,6 +345,7 @@ impl<Block, B, Remote, Local> CallExecutor<Block, Blake2Hasher> for
call_data,
ExecutionManager::NativeWhenPossible,
native_call,
side_effects_handler,
).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 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);
assert_eq!(remote_or_local.call(&BlockId::Number(0), "test_method", &[], ExecutionStrategy::NativeElseWasm).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(0), "test_method", &[], ExecutionStrategy::NativeElseWasm, NeverOffchainExt::new()).unwrap(), vec![1]);
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;
#[doc(hidden)]
pub use runtime_primitives::{
traits::{AuthorityIdFor, Block as BlockT, GetNodeBlockType, GetRuntimeBlockType, ApiRef, RuntimeApiInfo},
generic::BlockId, transaction_validity::TransactionValidity, ExecutionContext,
traits::{AuthorityIdFor, Block as BlockT, GetNodeBlockType, GetRuntimeBlockType, Header as HeaderT, ApiRef, RuntimeApiInfo},
generic::BlockId, transaction_validity::TransactionValidity,
};
#[doc(hidden)]
pub use primitives::{ExecutionContext, OffchainExt};
#[doc(hidden)]
pub use runtime_version::{ApiId, RuntimeVersion, ApisVec, create_apis_vec};
#[doc(hidden)]
pub use rstd::{slice, mem};
@@ -91,7 +93,10 @@ pub trait ApiExt<Block: BlockT> {
pub trait CallRuntimeAt<Block: BlockT> {
/// Calls the given api function with the given encoded arguments at the given block
/// 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,
at: &BlockId<Block>,
function: &'static str,
@@ -99,7 +104,7 @@ pub trait CallRuntimeAt<Block: BlockT> {
changes: &mut OverlayedChanges,
initialised_block: &mut Option<BlockId<Block>>,
native_call: Option<NC>,
context: ExecutionContext
context: ExecutionContext,
) -> error::Result<NativeOrEncoded<R>>;
/// Returns the runtime version at the given block.
@@ -132,3 +137,4 @@ decl_runtime_apis! {
fn validate_transaction(tx: <Block as BlockT>::Extrinsic) -> TransactionValidity;
}
}
@@ -520,6 +520,15 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
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(
dispatch_thunk_idx: usize,
wasm_ptr: *const u8,
+1 -2
View File
@@ -35,8 +35,7 @@ use std::collections::{HashMap, HashSet};
use std::result;
use runtime_primitives::traits::{ApiRef, ProvideRuntimeApi};
use runtime_primitives::generic::BlockId;
use runtime_primitives::ExecutionContext;
use substrate_primitives::NativeOrEncoded;
use substrate_primitives::{NativeOrEncoded, ExecutionContext};
use authorities::AuthoritySet;
use consensus_changes::ConsensusChanges;
+4
View File
@@ -31,6 +31,7 @@
//! information on how that is done.
#![cfg_attr(not(feature = "std"), no_std)]
#![warn(missing_docs)]
use parity_codec as codec;
use codec::{Encode, Decode};
@@ -43,6 +44,9 @@ use parking_lot::RwLock;
#[cfg(feature = "std")]
use std::{sync::Arc, format};
#[cfg(feature = "std")]
pub mod pool;
pub use runtime_primitives::RuntimeString;
/// 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::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>`.
#[derive(PartialEq, Eq, Clone)]
#[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::traits::{Block as BlockT, Header, ProvideRuntimeApi, As, NumberFor};
use runtime_version::RuntimeVersion;
use state_machine::ExecutionStrategy;
use state_machine::{self, ExecutionStrategy};
use crate::subscriptions::Subscriptions;
@@ -298,7 +298,7 @@ impl<B, E, Block, RA> StateApi<Block::Hash> for State<B, E, Block, RA> where
.executor()
.call(
&BlockId::Hash(block),
&method, &data.0, ExecutionStrategy::NativeElseWasm
&method, &data.0, ExecutionStrategy::NativeElseWasm, state_machine::NeverOffchainExt::new(),
)?;
Ok(Bytes(return_data))
}
+1 -1
View File
@@ -221,7 +221,7 @@ fn should_return_runtime_version() {
assert_eq!(
::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_derive = "1.0"
target_info = "0.1"
inherents = { package = "substrate-inherents", path = "../../core/inherents" }
keystore = { package = "substrate-keystore", path = "../../core/keystore" }
sr-io = { path = "../../core/sr-io" }
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" }
rpc = { package = "substrate-rpc-servers", path = "../../core/rpc-servers" }
tel = { package = "substrate-telemetry", path = "../../core/telemetry" }
offchain = { package = "substrate-offchain", path = "../../core/offchain" }
[dev-dependencies]
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> {
/// A list of bootnode addresses.
pub fn boot_nodes(&self) -> &[String] {
&self.spec.boot_nodes
}
/// Spec name.
pub fn name(&self) -> &str {
&self.spec.name
}
/// Spec id.
pub fn id(&self) -> &str {
&self.spec.id
}
/// Telemetry endpoints (if any)
pub fn telemetry_endpoints(&self) -> &Option<TelemetryEndpoints> {
&self.spec.telemetry_endpoints
}
/// Network protocol id.
pub fn protocol_id(&self) -> Option<&str> {
self.spec.protocol_id.as_ref().map(String::as_str)
}
/// Name of the consensus engine.
pub fn consensus_engine(&self) -> Option<&str> {
self.spec.consensus_engine.as_ref().map(String::as_str)
}
/// Additional loosly-typed properties of the chain.
pub fn properties(&self) -> Properties {
// Return an empty JSON object if 'properties' not defined in config
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) {
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 crate::chain_spec::ChainSpec;
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 consensus_common::import_queue::ImportQueue;
use network::{self, OnDemand};
@@ -150,7 +150,7 @@ pub trait StartRPC<C: Components> {
impl<C: Components> StartRPC<Self> for C where
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>>);
@@ -192,14 +192,14 @@ impl<C: Components> StartRPC<Self> for C where
/// Something that can maintain transaction pool on every imported block.
pub trait MaintainTransactionPool<C: Components> {
fn on_block_imported(
fn maintain_transaction_pool(
id: &BlockId<ComponentBlock<C>>,
client: &ComponentClient<C>,
transaction_pool: &TransactionPool<C::TransactionPoolApi>,
) -> error::Result<()>;
}
fn on_block_imported<Api, Backend, Block, Executor, PoolApi>(
fn maintain_transaction_pool<Api, Backend, Block, Executor, PoolApi>(
id: &BlockId<Block>,
client: &Client<Backend, Executor, Block, Api>,
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>,
Backend: client::backend::Backend<Block, Blake2Hasher>,
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>,
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
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>>,
client: &ComponentClient<C>,
transaction_pool: &TransactionPool<C::TransactionPoolApi>,
) -> 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
+ StartRPC<C>
+ MaintainTransactionPool<C>
+ OffchainWorker<C>
{}
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.
@@ -338,17 +366,14 @@ pub trait Components: Sized + 'static {
type Executor: 'static + client::CallExecutor<FactoryBlock<Self::Factory>, Blake2Hasher> + Send + Sync + Clone;
/// The type that implements the runtime API.
type RuntimeApi: Send + Sync;
/// A type that can start the RPC.
type RPC: StartRPC<Self>;
/// A type that can start all runtime-dependent services.
type RuntimeServices: ServiceTrait<Self>;
// 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.
type TransactionPoolApi: 'static + txpool::ChainApi<
Hash = <FactoryBlock<Self::Factory> as BlockT>::Hash,
Block = FactoryBlock<Self::Factory>
>;
/// Our Import Queue
type ImportQueue: ImportQueue<FactoryBlock<Self::Factory>> + 'static;
@@ -382,6 +407,7 @@ pub struct FullComponents<Factory: ServiceFactory> {
}
impl<Factory: ServiceFactory> FullComponents<Factory> {
/// Create new `FullComponents`
pub fn new(
config: FactoryFullConfiguration<Factory>,
task_executor: TaskExecutor
@@ -416,8 +442,7 @@ impl<Factory: ServiceFactory> Components for FullComponents<Factory> {
type TransactionPoolApi = <Factory as ServiceFactory>::FullTransactionPoolApi;
type ImportQueue = Factory::FullImportQueue;
type RuntimeApi = Factory::RuntimeApi;
type RPC = Factory::FullService;
type TransactionPool = Factory::FullService;
type RuntimeServices = Factory::FullService;
fn build_client(
config: &FactoryFullConfiguration<Factory>,
@@ -462,6 +487,7 @@ pub struct LightComponents<Factory: ServiceFactory> {
}
impl<Factory: ServiceFactory> LightComponents<Factory> {
/// Create new `LightComponents`
pub fn new(
config: FactoryFullConfiguration<Factory>,
task_executor: TaskExecutor
@@ -490,8 +516,7 @@ impl<Factory: ServiceFactory> Components for LightComponents<Factory> {
type TransactionPoolApi = <Factory as ServiceFactory>::LightTransactionPoolApi;
type ImportQueue = <Factory as ServiceFactory>::LightImportQueue;
type RuntimeApi = Factory::RuntimeApi;
type RPC = Factory::LightService;
type TransactionPool = Factory::LightService;
type RuntimeServices = Factory::LightService;
fn build_client(
config: &FactoryFullConfiguration<Factory>,
@@ -564,7 +589,7 @@ mod tests {
// fire notification - this should clean up the queue
assert_eq!(pool.status().ready, 1);
on_block_imported(
maintain_transaction_pool(
&id,
&client,
&pool,
+3
View File
@@ -68,6 +68,8 @@ pub struct Configuration<C, G: Serialize + DeserializeOwned + BuildStorage> {
pub telemetry_endpoints: Option<TelemetryEndpoints>,
/// The default number of 64KB pages to allocate for Wasm execution
pub default_heap_pages: Option<u64>,
/// Should offchain workers be executed.
pub offchain_worker: bool,
/// Enable authoring even when offline.
pub force_authoring: bool,
/// Disable GRANDPA when running in validator mode
@@ -97,6 +99,7 @@ impl<C: Default, G: Serialize + DeserializeOwned + BuildStorage> Configuration<C
rpc_ws: None,
telemetry_endpoints: None,
default_heap_pages: None,
offchain_worker: Default::default(),
force_authoring: 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.
//! Manages communication between them.
#![warn(unused_extern_crates)]
#![warn(missing_docs)]
mod components;
mod error;
@@ -28,20 +28,18 @@ pub mod chain_ops;
use std::io;
use std::net::SocketAddr;
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 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 runtime_primitives::generic::BlockId;
use runtime_primitives::traits::{Header, As};
use exit_future::Signal;
#[doc(hidden)]
pub use tokio::runtime::TaskExecutor;
use substrate_executor::NativeExecutor;
use parity_codec::{Encode, Decode};
use tel::{telemetry, SUBSTRATE_INFO};
pub use self::error::{ErrorKind, Error};
@@ -59,9 +57,13 @@ pub use components::{ServiceFactory, FullBackend, FullExecutor, LightBackend,
FactoryFullConfiguration, RuntimeGenesis, FactoryGenesis,
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)]
pub use network::OnDemand;
#[doc(hidden)]
pub use tokio::runtime::TaskExecutor;
const DEFAULT_PROTOCOL_ID: &str = "sup";
@@ -70,6 +72,7 @@ pub struct Service<Components: components::Components> {
client: Arc<ComponentClient<Components>>,
network: Option<Arc<components::NetworkService<Components::Factory>>>,
transaction_pool: Arc<TransactionPool<Components::TransactionPoolApi>>,
inherents_pool: Arc<InherentsPool<ComponentExtrinsic<Components>>>,
keystore: Keystore,
exit: ::exit_future::Exit,
signal: Option<Signal>,
@@ -77,6 +80,7 @@ pub struct Service<Components: components::Components> {
pub config: FactoryFullConfiguration<Components::Factory>,
_rpc: Box<::std::any::Any + Send + Sync>,
_telemetry: Option<Arc<tel::Telemetry>>,
_offchain_workers: Option<Arc<offchain::OffchainWorkers<ComponentClient<Components>, ComponentBlock<Components>>>>,
}
/// Creates bare client without any networking.
@@ -96,9 +100,7 @@ impl<Components: components::Components> Service<Components> {
pub fn new(
mut config: FactoryFullConfiguration<Components::Factory>,
task_executor: TaskExecutor,
)
-> Result<Self, error::Error>
{
) -> Result<Self, error::Error> {
let (signal, exit) = ::exit_future::signal();
// Create client
@@ -169,24 +171,48 @@ impl<Components: components::Components> Service<Components> {
)?;
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
let network = Arc::downgrade(&network);
let txpool = Arc::downgrade(&transaction_pool);
let wclient = Arc::downgrade(&client);
let offchain = offchain_workers.as_ref().map(Arc::downgrade);
let events = client.import_notification_stream()
.for_each(move |notification| {
let number = *notification.header.number();
if let Some(network) = network.upgrade() {
network.on_block_imported(notification.hash, notification.header);
}
if let (Some(txpool), Some(client)) = (txpool.upgrade(), wclient.upgrade()) {
Components::TransactionPool::on_block_imported(
Components::RuntimeServices::maintain_transaction_pool(
&BlockId::hash(notification.hash),
&*client,
&*txpool,
).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(())
})
.select(exit.clone())
@@ -264,7 +290,7 @@ impl<Components: components::Components> Service<Components> {
impl_version: config.impl_version.into(),
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,
config.rpc_ws, task_executor.clone(), transaction_pool.clone(),
)?;
@@ -299,12 +325,14 @@ impl<Components: components::Components> Service<Components> {
client,
network: Some(network),
transaction_pool,
inherents_pool,
signal: Some(signal),
keystore,
config,
exit,
_rpc: Box::new(rpc),
_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>> {
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()
}
/// Get shared extrinsic pool instance.
/// Get shared transaction pool instance.
pub fn transaction_pool(&self) -> Arc<TransactionPool<Components::TransactionPoolApi>> {
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.
pub fn keystore(&self) -> &Keystore {
&self.keystore
+1
View File
@@ -120,6 +120,7 @@ fn node_config<F: ServiceFactory> (
rpc_ws: None,
telemetry_endpoints: None,
default_heap_pages: None,
offchain_worker: false,
force_authoring: false,
disable_grandpa: false,
}
@@ -469,7 +469,7 @@ impl<'a> Fold for ApiRuntimeImplToApiRuntimeApiImpl<'a> {
};
let context_arg: syn::FnArg = parse_quote!( context: #crate_::runtime_api::ExecutionContext );
// Rewrite the input parameters.
input.sig.decl.inputs = parse_quote! {
&self, at: &#block_id, #context_arg, params: Option<( #( #param_types ),* )>, params_encoded: Vec<u8>
@@ -615,7 +615,7 @@ fn generate_runtime_api_versions(impls: &[ItemImpl]) -> Result<TokenStream> {
pub fn impl_runtime_apis_impl(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
// Parse all impl blocks
let RuntimeApiImpls { impls: api_impls } = parse_macro_input!(input as RuntimeApiImpls);
let dispatch_impl = unwrap_or_error(generate_dispatch_function(&api_impls));
let api_impls_for_runtime = unwrap_or_error(generate_api_impl_for_runtime(&api_impls));
let base_runtime_api = unwrap_or_error(generate_runtime_api_base_structures(&api_impls));
+8
View File
@@ -218,6 +218,14 @@ pub fn secp256k1_ecdsa_recover(sig: &[u8; 65], msg: &[u8; 32]) -> Result<[u8; 64
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
/// 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.
+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;
/// 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;
//================================
// Offchain-worker Context
//================================
/// Submit extrinsic.
fn ext_submit_extrinsic(data: *const u8, len: u32);
}
/// 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.
pub trait Printable {
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)]
#[cfg_attr(feature = "std", derive(Debug, Serialize))]
#[repr(u8)]
@@ -270,6 +270,24 @@ pub trait OnInitialise<BlockNumber> {
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 {
($one:ident,) => {
impl<Number: Copy, $one: OnFinalise<Number>> OnFinalise<Number> for ($one,) {
@@ -282,6 +300,11 @@ macro_rules! tuple_impl {
$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,)+) => {
impl<
@@ -304,6 +327,16 @@ macro_rules! tuple_impl {
$($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,)+);
}
}
@@ -24,6 +24,7 @@ use trie::trie_root;
use primitives::storage::well_known_keys::{CHANGES_TRIE_CONFIG, CODE, HEAP_PAGES};
use parity_codec::Encode;
use super::{Externalities, OverlayedChanges};
use log::warn;
/// Simple HashMap-based Externalities impl.
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> {
None
}
fn submit_extrinsic(&mut self, _extrinsic: Vec<u8>) -> Result<(), ()> {
warn!("Call to submit_extrinsic without offchain externalities set.");
Err(())
}
}
#[cfg(test)]
+35 -12
View File
@@ -20,7 +20,7 @@ use std::{error, fmt, cmp::Ord};
use log::warn;
use crate::backend::{Backend, Consolidate};
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 primitives::storage::well_known_keys::is_child_storage_key;
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.
pub struct Ext<'a, H, B, T>
pub struct Ext<'a, H, B, T, O>
where
H: Hasher,
B: 'a + Backend<H>,
T: 'a + ChangesTrieStorage<H>,
{
/// The overlayed changes to write to.
overlay: &'a mut OverlayedChanges,
@@ -81,23 +80,34 @@ where
/// `storage_changes_root` is called matters + we need to remember additional
/// data at this moment (block number).
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
H: Hasher,
B: 'a + Backend<H>,
T: 'a + ChangesTrieStorage<H>,
O: 'a + OffchainExt,
H::Out: Ord + HeapSizeOf,
{
/// 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 {
overlay,
backend,
storage_transaction: None,
changes_trie_storage,
changes_trie_transaction: None,
offchain_externalities,
}
}
@@ -152,12 +162,13 @@ where
}
#[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
H: Hasher,
B: 'a + Backend<H>,
T: 'a + ChangesTrieStorage<H>,
O: 'a + OffchainExt,
{
pub fn storage_pairs(&self) -> Vec<(Vec<u8>, Vec<u8>)> {
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
H: Hasher,
B: 'a + Backend<H>,
T: 'a + ChangesTrieStorage<H>,
O: 'a + OffchainExt,
H::Out: Ord + HeapSizeOf,
{
fn storage(&self, key: &[u8]) -> Option<Vec<u8>> {
@@ -329,6 +341,17 @@ where
self.changes_trie_transaction = root_and_tx;
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)]
@@ -345,7 +368,7 @@ mod tests {
type TestBackend = InMemory<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 {
OverlayedChanges {
@@ -371,7 +394,7 @@ mod tests {
fn storage_changes_root_is_none_when_storage_is_not_provided() {
let mut overlay = prepare_overlay_with_changes();
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);
}
@@ -381,7 +404,7 @@ mod tests {
overlay.changes_trie_config = None;
let storage = TestChangesTrieStorage::new();
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);
}
@@ -390,7 +413,7 @@ mod tests {
let mut overlay = prepare_overlay_with_changes();
let storage = TestChangesTrieStorage::new();
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),
Some(hex!("5b829920b9c8d554a19ee2a1ba593c4f2ee6fc32822d083e04236d693e8358d5").into()));
}
@@ -401,7 +424,7 @@ mod tests {
overlay.prospective.top.get_mut(&vec![1]).unwrap().value = None;
let storage = TestChangesTrieStorage::new();
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),
Some(hex!("bcf494e41e29a15c9ae5caa053fe3cb8b446ee3e02a254efbdec7a19235b76e4").into()));
}
+44 -8
View File
@@ -23,7 +23,7 @@ use log::warn;
use hash_db::Hasher;
use heapsize::HeapSizeOf;
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;
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.
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.
@@ -252,17 +271,19 @@ pub fn always_wasm<E, R: Decode>() -> ExecutionManager<DefaultHandler<R, E>> {
}
/// 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,
changes_trie_storage: Option<&'a T>,
offchain_ext: Option<&'a mut O>,
overlay: &'a mut OverlayedChanges,
exec: &'a Exec,
method: &'a str,
call_data: &'a [u8],
) -> StateMachine<'a, H, B, T, Exec> {
) -> StateMachine<'a, H, B, T, O, Exec> {
StateMachine {
backend,
changes_trie_storage,
offchain_ext,
overlay,
exec,
method,
@@ -272,9 +293,10 @@ pub fn new<'a, H, B, T, Exec>(
}
/// The substrate state machine.
pub struct StateMachine<'a, H, B, T, Exec> {
pub struct StateMachine<'a, H, B, T, O, Exec> {
backend: &'a B,
changes_trie_storage: Option<&'a T>,
offchain_ext: Option<&'a mut O>,
overlay: &'a mut OverlayedChanges,
exec: &'a Exec,
method: &'a str,
@@ -282,11 +304,12 @@ pub struct StateMachine<'a, H, B, T, Exec> {
_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,
Exec: CodeExecutor<H>,
B: Backend<H>,
T: ChangesTrieStorage<H>,
O: OffchainExt,
H::Out: Ord + HeapSizeOf,
{
/// 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,
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(
&mut externalities,
self.method,
@@ -505,6 +534,7 @@ where
let mut sm = StateMachine {
backend: &proving_backend,
changes_trie_storage: None as Option<&changes_trie::InMemoryStorage<H>>,
offchain_ext: NeverOffchainExt::new(),
overlay,
exec,
method,
@@ -554,6 +584,7 @@ where
let mut sm = StateMachine {
backend: trie_backend,
changes_trie_storage: None as Option<&changes_trie::InMemoryStorage<H>>,
offchain_ext: NeverOffchainExt::new(),
overlay,
exec,
method,
@@ -730,6 +761,7 @@ mod tests {
assert_eq!(new(
&trie_backend::tests::test_trie(),
Some(&InMemoryChangesTrieStorage::new()),
NeverOffchainExt::new(),
&mut Default::default(),
&DummyCodeExecutor {
change_changes_trie_config: false,
@@ -750,6 +782,7 @@ mod tests {
assert_eq!(new(
&trie_backend::tests::test_trie(),
Some(&InMemoryChangesTrieStorage::new()),
NeverOffchainExt::new(),
&mut Default::default(),
&DummyCodeExecutor {
change_changes_trie_config: false,
@@ -770,6 +803,7 @@ mod tests {
assert!(new(
&trie_backend::tests::test_trie(),
Some(&InMemoryChangesTrieStorage::new()),
NeverOffchainExt::new(),
&mut Default::default(),
&DummyCodeExecutor {
change_changes_trie_config: false,
@@ -838,7 +872,7 @@ mod tests {
{
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");
}
overlay.commit_prospective();
@@ -862,7 +896,7 @@ mod tests {
let backend = InMemory::<Blake2Hasher>::default().try_into_trie_backend().unwrap();
let changes_trie_storage = InMemoryChangesTrieStorage::new();
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_eq!(ext.child_storage(b":child_storage:testchild", b"abc"), Some(b"def".to_vec()));
@@ -889,6 +923,7 @@ mod tests {
assert!(new(
&trie_backend::tests::test_trie(),
Some(&InMemoryChangesTrieStorage::new()),
NeverOffchainExt::new(),
&mut Default::default(),
&DummyCodeExecutor {
change_changes_trie_config: true,
@@ -908,6 +943,7 @@ mod tests {
assert!(new(
&trie_backend::tests::test_trie(),
Some(&InMemoryChangesTrieStorage::new()),
NeverOffchainExt::new(),
&mut Default::default(),
&DummyCodeExecutor {
change_changes_trie_config: true,
@@ -375,7 +375,12 @@ mod tests {
};
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");
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 },
).map(|(root, _)| root.clone())
}
fn submit_extrinsic(&mut self, _extrinsic: Vec<u8>) -> Result<(), ()> {
unimplemented!()
}
}
#[cfg(test)]
+1
View File
@@ -128,6 +128,7 @@ pub fn new_with_execution_strategy(
syncing: execution_strategy,
importing: execution_strategy,
block_construction: execution_strategy,
offchain_worker: 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_version = { package = "sr-version", path = "../sr-version", 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 }
cfg-if = "0.1.6"
@@ -45,5 +46,6 @@ std = [
"runtime_primitives/std",
"runtime_version/std",
"consensus_aura/std",
"offchain-primitives/std",
"executive/std",
]
+16
View File
@@ -94,6 +94,7 @@ impl Transfer {
pub enum Extrinsic {
AuthoritiesChange(Vec<AuthorityId>),
Transfer(Transfer, AccountSignature),
IncludeData(Vec<u8>),
}
#[cfg(feature = "std")]
@@ -117,6 +118,7 @@ impl BlindCheckable for Extrinsic {
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 {
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 {
impl_runtime_apis! {
@@ -480,6 +489,13 @@ cfg_if! {
impl consensus_aura::AuraApi<Block> for Runtime {
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 {
Extrinsic::Transfer(ref transfer, _) => execute_transfer_backend(transfer),
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",
]
[[package]]
name = "substrate-offchain-primitives"
version = "0.1.0"
dependencies = [
"sr-primitives 0.1.0",
"substrate-client 0.1.0",
]
[[package]]
name = "substrate-panic-handler"
version = "0.1.0"
@@ -2446,6 +2454,7 @@ dependencies = [
"substrate-consensus-aura-primitives 0.1.0",
"substrate-inherents 0.1.0",
"substrate-keyring 0.1.0",
"substrate-offchain-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 }
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 }
offchain-primitives = { package = "substrate-offchain-primitives", path = "../../core/offchain/primitives", default-features = false }
[features]
default = ["std"]
@@ -48,4 +49,5 @@ std = [
"serde",
"safe-mix/std",
"consensus-aura/std",
"offchain-primitives/std",
]
+8 -2
View File
@@ -14,7 +14,7 @@ use primitives::bytes;
use primitives::{ed25519, OpaqueMetadata};
use runtime_primitives::{
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::{
block_builder::api::{CheckInherentsResult, InherentData, self as block_builder_api},
@@ -182,7 +182,7 @@ impl sudo::Trait for Runtime {
}
/// Used for the module template in `./template.rs`
impl template::Trait for Runtime {
impl template::Trait for Runtime {
type Event = Event;
}
@@ -280,4 +280,10 @@ impl_runtime_apis! {
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",
"substrate-client 0.1.0",
"substrate-consensus-aura-primitives 0.1.0",
"substrate-offchain-primitives 0.1.0",
"substrate-primitives 0.1.0",
]
@@ -2510,6 +2511,14 @@ dependencies = [
"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]]
name = "substrate-panic-handler"
version = "0.1.0"
+1
View File
@@ -62,6 +62,7 @@ construct_service_factory! {
let proposer = Arc::new(ProposerFactory {
client: service.client(),
transaction_pool: service.transaction_pool(),
inherents_pool: service.inherents_pool(),
});
let client = service.client();
executor.spawn(start_aura(
+1
View File
@@ -85,6 +85,7 @@ construct_service_factory! {
let proposer = Arc::new(substrate_basic_authorship::ProposerFactory {
client: service.client(),
transaction_pool: service.transaction_pool(),
inherents_pool: service.inherents_pool(),
});
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 }
rstd = { package = "sr-std", path = "../../core/sr-std", 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 }
support = { package = "srml-support", path = "../../srml/support", 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"),
impl_name: create_runtime_str!("substrate-node"),
authoring_version: 10,
spec_version: 42,
impl_version: 42,
spec_version: 43,
impl_version: 43,
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 {
fn grandpa_pending_change(digest: &DigestFor<Block>)
-> Option<ScheduledChange<NumberFor<Block>>>
+9
View File
@@ -1297,6 +1297,7 @@ dependencies = [
"substrate-client 0.1.0",
"substrate-consensus-aura-primitives 0.1.0",
"substrate-keyring 0.1.0",
"substrate-offchain-primitives 0.1.0",
"substrate-primitives 0.1.0",
]
@@ -2666,6 +2667,14 @@ dependencies = [
"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]]
name = "substrate-panic-handler"
version = "0.1.0"
+9
View File
@@ -214,6 +214,15 @@ decl_module! {
// We just kill our dummy storage item.
<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 primitives::traits::{
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 parity_codec::{Codec, Encode};
@@ -65,7 +65,7 @@ impl<
Block: traits::Block<Header=System::Header, Hash=System::Hash>,
Context: Default,
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
Block::Extrinsic: Checkable<Context> + Codec,
<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>>
{
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>) {
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>,
Context: Default,
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
Block::Extrinsic: Checkable<Context> + Codec,
<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)]
+84
View File
@@ -117,6 +117,7 @@ macro_rules! decl_module {
{}
{}
{}
{}
[]
$($t)*
);
@@ -135,6 +136,7 @@ macro_rules! decl_module {
{}
{}
{}
{}
[]
$($t)*
);
@@ -147,6 +149,7 @@ macro_rules! decl_module {
{}
{ $( $on_initialise:tt )* }
{ $( $on_finalise:tt )* }
{ $( $offchain:tt )* }
[ $($t:tt)* ]
$(#[doc = $doc_attr:tt])*
$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; }
{ $( $on_initialise )* }
{ $( $on_finalise )* }
{ $( $offchain )* }
[ $($t)* ]
$($rest)*
);
@@ -170,6 +174,7 @@ macro_rules! decl_module {
{}
{ $( $on_initialise:tt )* }
{ $( $on_finalise:tt )* }
{ $( $offchain:tt )* }
[ $($t:tt)* ]
$(#[doc = $doc_attr:tt])*
$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 )* } }
{ $( $on_initialise )* }
{ $( $on_finalise )* }
{ $( $offchain )* }
[ $($t)* ]
$($rest)*
);
@@ -195,6 +201,7 @@ macro_rules! decl_module {
{ $( $deposit_event:tt )* }
{ $( $on_initialise:tt )* }
{}
{ $( $offchain:tt )* }
[ $($t:tt)* ]
$(#[doc = $doc_attr:tt])*
fn on_finalise($($param_name:ident : $param:ty),* ) { $( $impl:tt )* }
@@ -207,6 +214,7 @@ macro_rules! decl_module {
{ $( $deposit_event )* }
{ $( $on_initialise )* }
{ fn on_finalise( $( $param_name : $param ),* ) { $( $impl )* } }
{ $( $offchain )* }
[ $($t)* ]
$($rest)*
);
@@ -218,6 +226,7 @@ macro_rules! decl_module {
{ $( $deposit_event:tt )* }
{}
{ $( $on_finalise:tt )* }
{ $( $offchain:tt )* }
[ $($t:tt)* ]
$(#[doc = $doc_attr:tt])*
fn on_initialise($($param_name:ident : $param:ty),* ) { $( $impl:tt )* }
@@ -230,6 +239,32 @@ macro_rules! decl_module {
{ $( $deposit_event )* }
{ fn on_initialise( $( $param_name : $param ),* ) { $( $impl )* } }
{ $( $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)* ]
$($rest)*
);
@@ -241,6 +276,7 @@ macro_rules! decl_module {
{ $( $deposit_event:tt )* }
{ $( $on_initialise:tt )* }
{ $( $on_finalise:tt )* }
{ $( $offchain:tt )* }
[ $($t:tt)* ]
$(#[doc = $doc_attr:tt])*
$fn_vis:vis fn $fn_name:ident(
@@ -255,6 +291,7 @@ macro_rules! decl_module {
{ $( $deposit_event )* }
{ $( $on_initialise )* }
{ $( $on_finalise )* }
{ $( $offchain )* }
[
$($t)*
$(#[doc = $doc_attr])*
@@ -273,6 +310,7 @@ macro_rules! decl_module {
{ $( $deposit_event:tt )* }
{ $( $on_initialise:tt )* }
{ $( $on_finalise:tt )* }
{ $( $offchain:tt )* }
[ $($t:tt)* ]
$(#[doc = $doc_attr:tt])*
$fn_vis:vis fn $fn_name:ident(
@@ -293,6 +331,7 @@ macro_rules! decl_module {
{ $( $deposit_event:tt )* }
{ $( $on_initialise:tt )* }
{ $( $on_finalise:tt )* }
{ $( $offchain:tt )* }
[ $($t:tt)* ]
$(#[doc = $doc_attr:tt])*
$fn_vis:vis fn $fn_name:ident(
@@ -313,6 +352,7 @@ macro_rules! decl_module {
{ $( $deposit_event:tt )* }
{ $( $on_initialise:tt )* }
{ $( $on_finalise:tt )* }
{ $( $offchain:tt )* }
[ $($t:tt)* ]
$(#[doc = $doc_attr:tt])*
$fn_vis:vis fn $fn_name:ident(
@@ -327,6 +367,7 @@ macro_rules! decl_module {
{ $( $deposit_event )* }
{ $( $on_initialise )* }
{ $( $on_finalise )* }
{ $( $offchain )* }
[
$($t)*
$(#[doc = $doc_attr])*
@@ -345,6 +386,7 @@ macro_rules! decl_module {
{ $( $deposit_event:tt )* }
{ $( $on_initialise:tt )* }
{ $( $on_finalise:tt )* }
{ $( $offchain:tt )* }
[ $($t:tt)* ]
) => {
decl_module!(@imp
@@ -356,6 +398,7 @@ macro_rules! decl_module {
{ $( $deposit_event )* }
{ $( $on_initialise )* }
{ $( $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
$module:ident<$trait_instance:ident: $trait_name:ident$(<I>, $instance:ident: $instantiable:path)?>;
$origin_ty:ty;
@@ -556,6 +632,7 @@ macro_rules! decl_module {
{ $( $deposit_event:tt )* }
{ $( $on_initialise:tt )* }
{ $( $on_finalise:tt )* }
{ $( $offchain:tt )* }
) => {
// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted.
#[derive(Clone, Copy, PartialEq, Eq)]
@@ -584,6 +661,12 @@ macro_rules! decl_module {
$( $on_finalise )*
}
decl_module! {
@impl_offchain
$mod_type<$trait_instance: $trait_name $(<I>, $instance: $instantiable)?>;
$( $offchain )*
}
decl_module! {
@impl_deposit_event
$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_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,
}
/// Event for the system module.
decl_event!(
/// Event for the system module.
pub enum Event {
/// An extrinsic completed successfully.
ExtrinsicSuccess,
+6 -6
View File
@@ -21,11 +21,11 @@ substrate-primitives = { path = "../../core/primitives" }
[features]
default = ["std"]
std = [
"rstd/std",
"srml-support/std",
"runtime_primitives/std",
"serde",
"parity-codec/std",
"system/std",
"inherents/std",
"parity-codec/std",
"rstd/std",
"runtime_primitives/std",
"srml-support/std",
"serde",
"system/std",
]