diff --git a/substrate/Cargo.lock b/substrate/Cargo.lock index 319b9c4cda..23cd0ed6f1 100644 --- a/substrate/Cargo.lock +++ b/substrate/Cargo.lock @@ -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", ] diff --git a/substrate/core/basic-authorship/src/basic_authorship.rs b/substrate/core/basic-authorship/src/basic_authorship.rs index 579ac17fb1..be8ccc5356 100644 --- a/substrate/core/basic-authorship/src/basic_authorship.rs +++ b/substrate/core/basic-authorship/src/basic_authorship.rs @@ -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 where A: txpool::ChainApi { pub client: Arc, /// The transaction pool. pub transaction_pool: Arc>, + /// The inherents pool + pub inherents_pool: Arc::Extrinsic>>, } impl consensus_common::Environment<::Block> for ProposerFactory where @@ -144,6 +145,7 @@ impl consensus_common::Environment<::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 { parent_id: BlockId, parent_number: <::Header as HeaderT>::Number, transaction_pool: Arc>, + inherents_pool: Arc::Extrinsic>>, now: Box time::Instant>, } @@ -201,11 +204,23 @@ impl Proposer 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); + } } diff --git a/substrate/core/cli/src/lib.rs b/substrate/core/cli/src/lib.rs index 1d335c9b64..8fa133ea79 100644 --- a/substrate/core/cli/src/lib.rs +++ b/substrate/core/cli/src/lib.rs @@ -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; diff --git a/substrate/core/cli/src/params.rs b/substrate/core/cli/src/params.rs index 7b9b224da9..ee014e1e8e 100644 --- a/substrate/core/cli/src/params.rs +++ b/substrate/core/cli/src/params.rs @@ -53,6 +53,16 @@ impl Into 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)] diff --git a/substrate/core/client/src/block_builder/block_builder.rs b/substrate/core/client/src/block_builder/block_builder.rs index 39969fc157..63e18e8279 100644 --- a/substrate/core/client/src/block_builder/block_builder.rs +++ b/substrate/core/client/src/block_builder/block_builder.rs @@ -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. diff --git a/substrate/core/client/src/call_executor.rs b/substrate/core/client/src/call_executor.rs index c787271874..0dad56be07 100644 --- a/substrate/core/client/src/call_executor.rs +++ b/substrate/core/client/src/call_executor.rs @@ -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, method: &str, call_data: &[u8], strategy: ExecutionStrategy, + side_effects_handler: Option<&mut O>, ) -> Result, 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, EM: Fn( Result, Self::Error>, @@ -73,6 +77,7 @@ where prepare_environment_block: PB, execution_manager: ExecutionManager, native_call: Option, + side_effects_handler: Option<&mut O>, ) -> error::Result> where ExecutionManager: 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, F: FnOnce( Result, Self::Error>, @@ -98,6 +104,7 @@ where call_data: &[u8], manager: ExecutionManager, native_call: Option, + side_effects_handler: Option<&mut O>, ) -> Result<(NativeOrEncoded, S::Transaction, Option>), error::Error>; /// Execute a call to a contract on top of given state, gathering execution proof. @@ -140,7 +147,10 @@ pub struct LocalCallExecutor { impl LocalCallExecutor { /// Creates new instance of local call executor. pub fn new(backend: Arc, executor: E) -> Self { - LocalCallExecutor { backend, executor } + LocalCallExecutor { + backend, + executor, + } } } @@ -161,17 +171,19 @@ where { type Error = E::Error; - fn call(&self, + fn call(&self, id: &BlockId, method: &str, call_data: &[u8], - strategy: ExecutionStrategy + strategy: ExecutionStrategy, + side_effects_handler: Option<&mut O>, ) -> error::Result> { 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, EM: Fn( Result, Self::Error>, @@ -204,6 +217,7 @@ where prepare_environment_block: PB, execution_manager: ExecutionManager, native_call: Option, + mut side_effects_handler: Option<&mut O>, ) -> Result, error::Error> where ExecutionManager: 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) -> error::Result { 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, F: FnOnce( Result, Self::Error>, @@ -268,10 +285,12 @@ where call_data: &[u8], manager: ExecutionManager, native_call: Option, + side_effects_handler: Option<&mut O>, ) -> error::Result<(NativeOrEncoded, S::Transaction, Option>)> { state_machine::new( state, self.backend.changes_trie_storage(), + side_effects_handler, changes, &self.executor, method, diff --git a/substrate/core/client/src/client.rs b/substrate/core/client/src/client.rs index 37ee1b5ce0..26ba126b85 100644 --- a/substrate/core/client/src/client.rs +++ b/substrate/core/client/src/client.rs @@ -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 Client where pub fn authorities_at(&self, id: &BlockId) -> error::Result>> { 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::>::decode(&mut &r[..]) .ok_or_else(|| error::ErrorKind::InvalidAuthoritiesSet.into())) } @@ -871,7 +874,7 @@ impl Client 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 Client where _ => get_execution_manager(self.execution_strategies().importing), }, None, + NeverOffchainExt::new(), )?; overlay.commit_prospective(); @@ -1339,7 +1343,8 @@ impl CallRuntimeAt for Client where Block: BlockT { fn call_api_at< - R: Encode + Decode + PartialEq, NC: FnOnce() -> result::Result + UnwindSafe + R: Encode + Decode + PartialEq, + NC: FnOnce() -> result::Result + UnwindSafe, >( &self, at: &BlockId, @@ -1348,15 +1353,22 @@ impl CallRuntimeAt for Client where changes: &mut OverlayedChanges, initialised_block: &mut Option>, native_call: Option, - context: ExecutionContext + context: ExecutionContext, ) -> error::Result> { 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 CallRuntimeAt for Client where || self.prepare_environment_block(at), manager, native_call, + offchain_extensions.as_mut(), ) } diff --git a/substrate/core/client/src/genesis.rs b/substrate/core/client/src/genesis.rs index 9ee0033685..7ebae4c558 100644 --- a/substrate/core/client/src/genesis.rs +++ b/substrate/core/client/src/genesis.rs @@ -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", diff --git a/substrate/core/client/src/light/call_executor.rs b/substrate/core/client/src/light/call_executor.rs index 5bc77112f8..00a3d8895b 100644 --- a/substrate/core/client/src/light/call_executor.rs +++ b/substrate/core/client/src/light/call_executor.rs @@ -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, method: &str, call_data: &[u8], _strategy: ExecutionStrategy) + fn call< + O: OffchainExt, + >( + &self, + id: &BlockId, + method: &str, + call_data: &[u8], + _strategy: ExecutionStrategy, + _side_effects_handler: Option<&mut O>, + ) -> ClientResult> { 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, EM: Fn( Result, Self::Error>, @@ -112,22 +122,24 @@ where _prepare_environment_block: PB, execution_manager: ExecutionManager, _native_call: Option, + side_effects_handler: Option<&mut O>, ) -> ClientResult> where ExecutionManager: 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) -> ClientResult { - 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, FF: FnOnce( Result, Self::Error>, @@ -142,6 +154,7 @@ where _call_data: &[u8], _m: ExecutionManager, _native_call: Option, + _side_effects_handler: Option<&mut O>, ) -> ClientResult<(NativeOrEncoded, S::Transaction, Option>)> { Err(ClientErrorKind::NotAvailableOnLightClient.into()) } @@ -201,15 +214,24 @@ impl CallExecutor for { type Error = ClientError; - fn call(&self, id: &BlockId, method: &str, call_data: &[u8], strategy: ExecutionStrategy) - -> ClientResult> { + fn call< + O: OffchainExt, + >( + &self, + id: &BlockId, + method: &str, + call_data: &[u8], + strategy: ExecutionStrategy, + side_effects_handler: Option<&mut O>, + ) -> ClientResult> { 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, EM: Fn( Result, Self::Error>, @@ -227,12 +249,14 @@ impl CallExecutor for prepare_environment_block: PB, _manager: ExecutionManager, native_call: Option, + side_effects_handler: Option<&mut O>, ) -> ClientResult> where ExecutionManager: 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, Local::Error>, @@ -250,8 +274,10 @@ impl CallExecutor 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, Remote::Error>, @@ -269,6 +295,7 @@ impl CallExecutor 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 CallExecutor for } fn call_at_state< + O: OffchainExt, S: StateBackend, FF: FnOnce( Result, Self::Error>, @@ -295,11 +323,13 @@ impl CallExecutor for call_data: &[u8], _manager: ExecutionManager, native_call: Option, + side_effects_handler: Option<&mut O>, ) -> ClientResult<(NativeOrEncoded, S::Transaction, Option>)> { // 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, Remote::Error>, @@ -315,6 +345,7 @@ impl CallExecutor 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]); } } diff --git a/substrate/core/client/src/runtime_api.rs b/substrate/core/client/src/runtime_api.rs index 435a7d70e6..77d733f64e 100644 --- a/substrate/core/client/src/runtime_api.rs +++ b/substrate/core/client/src/runtime_api.rs @@ -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 { pub trait CallRuntimeAt { /// Calls the given api function with the given encoded arguments at the given block /// and returns the encoded result. - fn call_api_at result::Result + UnwindSafe>( + fn call_api_at< + R: Encode + Decode + PartialEq, + NC: FnOnce() -> result::Result + UnwindSafe, + >( &self, at: &BlockId, function: &'static str, @@ -99,7 +104,7 @@ pub trait CallRuntimeAt { changes: &mut OverlayedChanges, initialised_block: &mut Option>, native_call: Option, - context: ExecutionContext + context: ExecutionContext, ) -> error::Result>; /// Returns the runtime version at the given block. @@ -132,3 +137,4 @@ decl_runtime_apis! { fn validate_transaction(tx: ::Extrinsic) -> TransactionValidity; } } + diff --git a/substrate/core/executor/src/wasm_executor.rs b/substrate/core/executor/src/wasm_executor.rs index 7e6833bb78..42af29e9ba 100644 --- a/substrate/core/executor/src/wasm_executor.rs +++ b/substrate/core/executor/src/wasm_executor.rs @@ -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, diff --git a/substrate/core/finality-grandpa/src/tests.rs b/substrate/core/finality-grandpa/src/tests.rs index 6daddea562..7e0537ed7b 100644 --- a/substrate/core/finality-grandpa/src/tests.rs +++ b/substrate/core/finality-grandpa/src/tests.rs @@ -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; diff --git a/substrate/core/inherents/src/lib.rs b/substrate/core/inherents/src/lib.rs index eadd45ba9e..7d2324bc93 100644 --- a/substrate/core/inherents/src/lib.rs +++ b/substrate/core/inherents/src/lib.rs @@ -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. diff --git a/substrate/core/inherents/src/pool.rs b/substrate/core/inherents/src/pool.rs new file mode 100644 index 0000000000..2c7e953696 --- /dev/null +++ b/substrate/core/inherents/src/pool.rs @@ -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 . + +//! 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 { + data: Mutex>, +} + +impl Default for InherentsPool { + fn default() -> Self { + InherentsPool { + data: Default::default(), + } + } +} + +impl fmt::Debug for InherentsPool { + 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 InherentsPool { + /// 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 { + 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![]); + } +} diff --git a/substrate/core/offchain/Cargo.toml b/substrate/core/offchain/Cargo.toml new file mode 100644 index 0000000000..8cd1d2edc5 --- /dev/null +++ b/substrate/core/offchain/Cargo.toml @@ -0,0 +1,27 @@ +[package] +description = "Substrate offchain workers" +name = "substrate-offchain" +version = "0.1.0" +license = "GPL-3.0" +authors = ["Parity Technologies "] +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 = [] diff --git a/substrate/core/offchain/primitives/Cargo.toml b/substrate/core/offchain/primitives/Cargo.toml new file mode 100644 index 0000000000..4d10e08f92 --- /dev/null +++ b/substrate/core/offchain/primitives/Cargo.toml @@ -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 "] +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" +] diff --git a/substrate/core/offchain/primitives/src/lib.rs b/substrate/core/offchain/primitives/src/lib.rs new file mode 100644 index 0000000000..c05e8dceb9 --- /dev/null +++ b/substrate/core/offchain/primitives/src/lib.rs @@ -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 . + +//! 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); + } +} diff --git a/substrate/core/offchain/src/api.rs b/substrate/core/offchain/src/api.rs new file mode 100644 index 0000000000..5d2a636be3 --- /dev/null +++ b/substrate/core/offchain/src/api.rs @@ -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 . + +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), +} + +/// 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); + +impl OffchainExt for AsyncApi { + fn submit_extrinsic(&mut self, ext: Vec) { + let _ = self.0.unbounded_send(ExtMessage::SubmitExtrinsic(ext)); + } +} + +/// Offchain extensions implementation API +pub(crate) struct Api { + receiver: Option>, + transaction_pool: Arc>, + inherents_pool: Arc::Extrinsic>>, + at: BlockId, +} + +impl Api { + pub fn new( + transaction_pool: Arc>, + inherents_pool: Arc::Extrinsic>>, + at: BlockId, + ) -> (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 { + 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) { + let xt = match ::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); + }, + } + } +} diff --git a/substrate/core/offchain/src/lib.rs b/substrate/core/offchain/src/lib.rs new file mode 100644 index 0000000000..cac960f250 --- /dev/null +++ b/substrate/core/offchain/src/lib.rs @@ -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 . + +//! 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 { + client: Arc, + inherents_pool: Arc::Extrinsic>>, + executor: TaskExecutor, + _block: PhantomData, +} + +impl OffchainWorkers { + /// Creates new `OffchainWorkers`. + pub fn new( + client: Arc, + inherents_pool: Arc::Extrinsic>>, + executor: TaskExecutor, + ) -> Self { + Self { + client, + inherents_pool, + executor, + _block: PhantomData, + } + } +} + +impl OffchainWorkers where + Block: traits::Block, + C: ProvideRuntimeApi, + C::Api: OffchainWorkerApi, +{ + /// Start the offchain workers after given block. + pub fn on_block_imported( + &self, + number: &::Number, + pool: &Arc>, + ) where + A: ChainApi + 'static, + { + let runtime = self.client.runtime_api(); + let at = BlockId::number(*number); + let has_api = runtime.has_api::>(&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); + } +} diff --git a/substrate/core/primitives/src/lib.rs b/substrate/core/primitives/src/lib.rs index 67b99f7ebc..38ecec0fa4 100644 --- a/substrate/core/primitives/src/lib.rs +++ b/substrate/core/primitives/src/lib.rs @@ -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), + /// 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); +} +impl OffchainExt for Box { + fn submit_extrinsic(&mut self, ex: Vec) { + (&mut **self).submit_extrinsic(ex) + } +} + /// Hex-serialised shim for `Vec`. #[derive(PartialEq, Eq, Clone)] #[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug, Hash, PartialOrd, Ord))] diff --git a/substrate/core/rpc/src/state/mod.rs b/substrate/core/rpc/src/state/mod.rs index c2e5f885c9..168c0bd692 100644 --- a/substrate/core/rpc/src/state/mod.rs +++ b/substrate/core/rpc/src/state/mod.rs @@ -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 StateApi for State where .executor() .call( &BlockId::Hash(block), - &method, &data.0, ExecutionStrategy::NativeElseWasm + &method, &data.0, ExecutionStrategy::NativeElseWasm, state_machine::NeverOffchainExt::new(), )?; Ok(Bytes(return_data)) } diff --git a/substrate/core/rpc/src/state/tests.rs b/substrate/core/rpc/src/state/tests.rs index 436d413b1a..f28e63b15d 100644 --- a/substrate/core/rpc/src/state/tests.rs +++ b/substrate/core/rpc/src/state/tests.rs @@ -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]]}"# ); } diff --git a/substrate/core/service/Cargo.toml b/substrate/core/service/Cargo.toml index 353a3a9973..b2f431a5db 100644 --- a/substrate/core/service/Cargo.toml +++ b/substrate/core/service/Cargo.toml @@ -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" } diff --git a/substrate/core/service/src/chain_spec.rs b/substrate/core/service/src/chain_spec.rs index 45b61f0eeb..78aad64dd0 100644 --- a/substrate/core/service/src/chain_spec.rs +++ b/substrate/core/service/src/chain_spec.rs @@ -116,35 +116,43 @@ impl Clone for ChainSpec { } impl ChainSpec { + /// 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 { &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()) } diff --git a/substrate/core/service/src/components.rs b/substrate/core/service/src/components.rs index 94f5e69c8f..e89921742b 100644 --- a/substrate/core/service/src/components.rs +++ b/substrate/core/service/src/components.rs @@ -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 { impl StartRPC for C where ComponentClient: ProvideRuntimeApi, - as ProvideRuntimeApi>::Api: Metadata>, + as ProvideRuntimeApi>::Api: runtime_api::Metadata>, { type ServersHandle = (Option, Option>); @@ -192,14 +192,14 @@ impl StartRPC for C where /// Something that can maintain transaction pool on every imported block. pub trait MaintainTransactionPool { - fn on_block_imported( + fn maintain_transaction_pool( id: &BlockId>, client: &ComponentClient, transaction_pool: &TransactionPool, ) -> error::Result<()>; } -fn on_block_imported( +fn maintain_transaction_pool( id: &BlockId, client: &Client, transaction_pool: &TransactionPool, @@ -207,7 +207,7 @@ fn on_block_imported( Block: BlockT::Out>, Backend: client::backend::Backend, Client: ProvideRuntimeApi, - as ProvideRuntimeApi>::Api: TaggedTransactionQueue, + as ProvideRuntimeApi>::Api: runtime_api::TaggedTransactionQueue, Executor: client::CallExecutor, PoolApi: txpool::ChainApi, { @@ -227,14 +227,35 @@ fn on_block_imported( impl MaintainTransactionPool for C where ComponentClient: ProvideRuntimeApi, - as ProvideRuntimeApi>::Api: TaggedTransactionQueue>, + as ProvideRuntimeApi>::Api: runtime_api::TaggedTransactionQueue>, { - fn on_block_imported( + fn maintain_transaction_pool( id: &BlockId>, client: &ComponentClient, transaction_pool: &TransactionPool, ) -> error::Result<()> { - on_block_imported(id, client, transaction_pool) + maintain_transaction_pool(id, client, transaction_pool) + } +} + +pub trait OffchainWorker { + fn offchain_workers( + number: &FactoryBlockNumber, + offchain: &offchain::OffchainWorkers, ComponentBlock>, + pool: &Arc>, + ) -> error::Result<()>; +} + +impl OffchainWorker for C where + ComponentClient: ProvideRuntimeApi, + as ProvideRuntimeApi>::Api: offchain::OffchainWorkerApi>, +{ + fn offchain_workers( + number: &FactoryBlockNumber, + offchain: &offchain::OffchainWorkers, ComponentBlock>, + pool: &Arc>, + ) -> error::Result<()> { + Ok(offchain.on_block_imported(number, pool)) } } @@ -246,9 +267,16 @@ pub trait ServiceTrait: + 'static + StartRPC + MaintainTransactionPool + + OffchainWorker {} impl ServiceTrait for T where - T: Deref> + Send + Sync + 'static + StartRPC + MaintainTransactionPool + T: Deref> + + Send + + Sync + + 'static + + StartRPC + + MaintainTransactionPool + + OffchainWorker {} /// 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, 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; + /// A type that can start all runtime-dependent services. + type RuntimeServices: ServiceTrait; // TODO: Traitify transaction pool and allow people to implement their own. (#1242) - /// A type that can maintain transaction pool. - type TransactionPool: MaintainTransactionPool; /// Extrinsic pool type. type TransactionPoolApi: 'static + txpool::ChainApi< Hash = as BlockT>::Hash, Block = FactoryBlock >; - /// Our Import Queue type ImportQueue: ImportQueue> + 'static; @@ -382,6 +407,7 @@ pub struct FullComponents { } impl FullComponents { + /// Create new `FullComponents` pub fn new( config: FactoryFullConfiguration, task_executor: TaskExecutor @@ -416,8 +442,7 @@ impl Components for FullComponents { type TransactionPoolApi = ::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, @@ -462,6 +487,7 @@ pub struct LightComponents { } impl LightComponents { + /// Create new `LightComponents` pub fn new( config: FactoryFullConfiguration, task_executor: TaskExecutor @@ -490,8 +516,7 @@ impl Components for LightComponents { type TransactionPoolApi = ::LightTransactionPoolApi; type ImportQueue = ::LightImportQueue; type RuntimeApi = Factory::RuntimeApi; - type RPC = Factory::LightService; - type TransactionPool = Factory::LightService; + type RuntimeServices = Factory::LightService; fn build_client( config: &FactoryFullConfiguration, @@ -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, diff --git a/substrate/core/service/src/config.rs b/substrate/core/service/src/config.rs index 7bafa4c83e..b7a3b8ba14 100644 --- a/substrate/core/service/src/config.rs +++ b/substrate/core/service/src/config.rs @@ -68,6 +68,8 @@ pub struct Configuration { pub telemetry_endpoints: Option, /// The default number of 64KB pages to allocate for Wasm execution pub default_heap_pages: Option, + /// 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 Configuration { client: Arc>, network: Option>>, transaction_pool: Arc>, + inherents_pool: Arc>>, keystore: Keystore, exit: ::exit_future::Exit, signal: Option, @@ -77,6 +80,7 @@ pub struct Service { pub config: FactoryFullConfiguration, _rpc: Box<::std::any::Any + Send + Sync>, _telemetry: Option>, + _offchain_workers: Option, ComponentBlock>>>, } /// Creates bare client without any networking. @@ -96,9 +100,7 @@ impl Service { pub fn new( mut config: FactoryFullConfiguration, task_executor: TaskExecutor, - ) - -> Result - { + ) -> Result { let (signal, exit) = ::exit_future::signal(); // Create client @@ -169,24 +171,48 @@ impl Service { )?; 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 Service { 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 Service { 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 Service { } } + /// return a shared instance of Telemtry (if enabled) pub fn telemetry(&self) -> Option> { self._telemetry.as_ref().map(|t| t.clone()) } @@ -337,11 +366,16 @@ impl Service 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> { self.transaction_pool.clone() } + /// Get shared inherents pool instance. + pub fn inherents_pool(&self) -> Arc>> { + self.inherents_pool.clone() + } + /// Get shared keystore. pub fn keystore(&self) -> &Keystore { &self.keystore diff --git a/substrate/core/service/test/src/lib.rs b/substrate/core/service/test/src/lib.rs index 03047a3a9f..1735382bb2 100644 --- a/substrate/core/service/test/src/lib.rs +++ b/substrate/core/service/test/src/lib.rs @@ -120,6 +120,7 @@ fn node_config ( rpc_ws: None, telemetry_endpoints: None, default_heap_pages: None, + offchain_worker: false, force_authoring: false, disable_grandpa: false, } diff --git a/substrate/core/sr-api-macros/src/impl_runtime_apis.rs b/substrate/core/sr-api-macros/src/impl_runtime_apis.rs index 5cfb5fcc93..8bdf977303 100644 --- a/substrate/core/sr-api-macros/src/impl_runtime_apis.rs +++ b/substrate/core/sr-api-macros/src/impl_runtime_apis.rs @@ -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 @@ -615,7 +615,7 @@ fn generate_runtime_api_versions(impls: &[ItemImpl]) -> Result { 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)); diff --git a/substrate/core/sr-io/with_std.rs b/substrate/core/sr-io/with_std.rs index 8f9a67c167..1f4ce56fc9 100644 --- a/substrate/core/sr-io/with_std.rs +++ b/substrate/core/sr-io/with_std.rs @@ -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(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. diff --git a/substrate/core/sr-io/without_std.rs b/substrate/core/sr-io/without_std.rs index e3cf8318a6..a4d62df30b 100644 --- a/substrate/core/sr-io/without_std.rs +++ b/substrate/core/sr-io/without_std.rs @@ -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(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); diff --git a/substrate/core/sr-primitives/src/lib.rs b/substrate/core/sr-primitives/src/lib.rs index efaf4146fc..2a53c7ba4c 100644 --- a/substrate/core/sr-primitives/src/lib.rs +++ b/substrate/core/sr-primitives/src/lib.rs @@ -393,21 +393,6 @@ impl From 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)] diff --git a/substrate/core/sr-primitives/src/traits.rs b/substrate/core/sr-primitives/src/traits.rs index e5a58850e7..ef3f133df6 100644 --- a/substrate/core/sr-primitives/src/traits.rs +++ b/substrate/core/sr-primitives/src/traits.rs @@ -270,6 +270,24 @@ pub trait OnInitialise { impl OnInitialise 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 { + /// 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 OffchainWorker for () {} + macro_rules! tuple_impl { ($one:ident,) => { impl> OnFinalise for ($one,) { @@ -282,6 +300,11 @@ macro_rules! tuple_impl { $one::on_initialise(n); } } + impl> OffchainWorker 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, + $($rest: OffchainWorker),+ + > OffchainWorker for ($first, $($rest),+) { + fn generate_extrinsics(n: Number) { + $first::generate_extrinsics(n); + $($rest::generate_extrinsics(n);)+ + } + } tuple_impl!($($rest,)+); } } diff --git a/substrate/core/state-machine/src/basic.rs b/substrate/core/state-machine/src/basic.rs index ead191a3c0..7b2a95464e 100644 --- a/substrate/core/state-machine/src/basic.rs +++ b/substrate/core/state-machine/src/basic.rs @@ -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 Externalities for BasicExternalities where H::Out: Ord + Heap fn storage_changes_root(&mut self, _parent: H::Out, _parent_num: u64) -> Option { None } + + fn submit_extrinsic(&mut self, _extrinsic: Vec) -> Result<(), ()> { + warn!("Call to submit_extrinsic without offchain externalities set."); + Err(()) + } } #[cfg(test)] diff --git a/substrate/core/state-machine/src/ext.rs b/substrate/core/state-machine/src/ext.rs index abfde7ee7b..33074c7059 100644 --- a/substrate/core/state-machine/src/ext.rs +++ b/substrate/core/state-machine/src/ext.rs @@ -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 error::Error for Error { } /// 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, - T: 'a + ChangesTrieStorage, { /// 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::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, T: 'a + ChangesTrieStorage, + 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, T: 'a + ChangesTrieStorage, + O: 'a + OffchainExt, { pub fn storage_pairs(&self) -> Vec<(Vec, Vec)> { use std::collections::HashMap; @@ -173,11 +184,12 @@ where } } -impl<'a, B: 'a, T: 'a, H> Externalities for Ext<'a, H, B, T> +impl<'a, B, T, H, O> Externalities for Ext<'a, H, B, T, O> where H: Hasher, B: 'a + Backend, T: 'a + ChangesTrieStorage, + O: 'a + OffchainExt, H::Out: Ord + HeapSizeOf, { fn storage(&self, key: &[u8]) -> Option> { @@ -329,6 +341,17 @@ where self.changes_trie_transaction = root_and_tx; root } + + fn submit_extrinsic(&mut self, extrinsic: Vec) -> 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; type TestChangesTrieStorage = InMemoryChangesTrieStorage; - 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())); } diff --git a/substrate/core/state-machine/src/lib.rs b/substrate/core/state-machine/src/lib.rs index 135c1dfc6b..0500aa72cf 100644 --- a/substrate/core/state-machine/src/lib.rs +++ b/substrate/core/state-machine/src/lib.rs @@ -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 { /// 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 where H::Out: Ord; + + /// Submit extrinsic. + /// + /// Returns an error in case the API is not available. + fn submit_extrinsic(&mut self, extrinsic: Vec) -> 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) { unreachable!() } } /// Code execution engine. @@ -252,17 +271,19 @@ pub fn always_wasm() -> ExecutionManager> { } /// 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, } -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, B: Backend, T: ChangesTrieStorage, + 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 + 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>, + 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>, + 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::::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, diff --git a/substrate/core/state-machine/src/overlayed_changes.rs b/substrate/core/state-machine/src/overlayed_changes.rs index 865ee86105..56e69323e8 100644 --- a/substrate/core/state-machine/src/overlayed_changes.rs +++ b/substrate/core/state-machine/src/overlayed_changes.rs @@ -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)); } diff --git a/substrate/core/state-machine/src/testing.rs b/substrate/core/state-machine/src/testing.rs index a9ed13eb8e..d03cc8e76d 100644 --- a/substrate/core/state-machine/src/testing.rs +++ b/substrate/core/state-machine/src/testing.rs @@ -168,6 +168,10 @@ impl Externalities for TestExternalities where H::Out: Ord + He &AnchorBlockId { hash: parent, number: parent_num }, ).map(|(root, _)| root.clone()) } + + fn submit_extrinsic(&mut self, _extrinsic: Vec) -> Result<(), ()> { + unimplemented!() + } } #[cfg(test)] diff --git a/substrate/core/test-client/src/lib.rs b/substrate/core/test-client/src/lib.rs index 6eb6db4d4b..4a99df65a5 100644 --- a/substrate/core/test-client/src/lib.rs +++ b/substrate/core/test-client/src/lib.rs @@ -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, }; diff --git a/substrate/core/test-runtime/Cargo.toml b/substrate/core/test-runtime/Cargo.toml index 812dd4bc28..6132cbbb1a 100644 --- a/substrate/core/test-runtime/Cargo.toml +++ b/substrate/core/test-runtime/Cargo.toml @@ -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", ] diff --git a/substrate/core/test-runtime/src/lib.rs b/substrate/core/test-runtime/src/lib.rs index 36950bfb1b..74ec56240c 100644 --- a/substrate/core/test-runtime/src/lib.rs +++ b/substrate/core/test-runtime/src/lib.rs @@ -94,6 +94,7 @@ impl Transfer { pub enum Extrinsic { AuthoritiesChange(Vec), Transfer(Transfer, AccountSignature), + IncludeData(Vec), } #[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 for Runtime { fn slot_duration() -> u64 { 1 } } + + impl offchain_primitives::OffchainWorkerApi 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 for Runtime { fn slot_duration() -> u64 { 1 } } + + impl offchain_primitives::OffchainWorkerApi for Runtime { + fn offchain_worker(block: u64) { + let ex = Extrinsic::IncludeData(block.encode()); + runtime_io::submit_extrinsic(&ex) + } + } } } } \ No newline at end of file diff --git a/substrate/core/test-runtime/src/system.rs b/substrate/core/test-runtime/src/system.rs index d48fd153da..ffc01182d4 100644 --- a/substrate/core/test-runtime/src/system.rs +++ b/substrate/core/test-runtime/src/system.rs @@ -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), } } diff --git a/substrate/core/test-runtime/wasm/Cargo.lock b/substrate/core/test-runtime/wasm/Cargo.lock index 028bd07427..d8eda0e88e 100644 --- a/substrate/core/test-runtime/wasm/Cargo.lock +++ b/substrate/core/test-runtime/wasm/Cargo.lock @@ -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", ] diff --git a/substrate/core/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm b/substrate/core/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm index 7b12560812..a7de97ffb9 100644 Binary files a/substrate/core/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm and b/substrate/core/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm differ diff --git a/substrate/node-template/runtime/Cargo.toml b/substrate/node-template/runtime/Cargo.toml index 8155865730..2ba64a3a9c 100644 --- a/substrate/node-template/runtime/Cargo.toml +++ b/substrate/node-template/runtime/Cargo.toml @@ -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", ] diff --git a/substrate/node-template/runtime/src/lib.rs b/substrate/node-template/runtime/src/lib.rs index 888a9b0dc4..fd82b399d3 100644 --- a/substrate/node-template/runtime/src/lib.rs +++ b/substrate/node-template/runtime/src/lib.rs @@ -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 for Runtime { + fn offchain_worker(n: NumberFor) { + Executive::offchain_worker(n) + } + } } diff --git a/substrate/node-template/runtime/wasm/Cargo.lock b/substrate/node-template/runtime/wasm/Cargo.lock index dc4429be3d..34fc8036ed 100644 --- a/substrate/node-template/runtime/wasm/Cargo.lock +++ b/substrate/node-template/runtime/wasm/Cargo.lock @@ -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" diff --git a/substrate/node-template/src/service.rs b/substrate/node-template/src/service.rs index 5f1a375181..2ff5041f76 100644 --- a/substrate/node-template/src/service.rs +++ b/substrate/node-template/src/service.rs @@ -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( diff --git a/substrate/node/cli/src/service.rs b/substrate/node/cli/src/service.rs index 37de34e402..c4dd70fc51 100644 --- a/substrate/node/cli/src/service.rs +++ b/substrate/node/cli/src/service.rs @@ -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(); diff --git a/substrate/node/runtime/Cargo.toml b/substrate/node/runtime/Cargo.toml index f72a525846..6c0307d47e 100644 --- a/substrate/node/runtime/Cargo.toml +++ b/substrate/node/runtime/Cargo.toml @@ -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 } diff --git a/substrate/node/runtime/src/lib.rs b/substrate/node/runtime/src/lib.rs index 9f2d877292..0ca1aff7cb 100644 --- a/substrate/node/runtime/src/lib.rs +++ b/substrate/node/runtime/src/lib.rs @@ -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 for Runtime { + fn offchain_worker(number: NumberFor) { + Executive::offchain_worker(number) + } + } + impl fg_primitives::GrandpaApi for Runtime { fn grandpa_pending_change(digest: &DigestFor) -> Option>> diff --git a/substrate/node/runtime/wasm/Cargo.lock b/substrate/node/runtime/wasm/Cargo.lock index 356ee5f62e..4bc3dcf6da 100644 --- a/substrate/node/runtime/wasm/Cargo.lock +++ b/substrate/node/runtime/wasm/Cargo.lock @@ -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" diff --git a/substrate/node/runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm b/substrate/node/runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm index 30e0da2028..7dfb5fb461 100644 Binary files a/substrate/node/runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm and b/substrate/node/runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm differ diff --git a/substrate/srml/example/src/lib.rs b/substrate/srml/example/src/lib.rs index 81a4a7a4ef..8b5347d1a2 100644 --- a/substrate/srml/example/src/lib.rs +++ b/substrate/srml/example/src/lib.rs @@ -214,6 +214,15 @@ decl_module! { // We just kill our dummy storage item. >::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 + } } } diff --git a/substrate/srml/executive/src/lib.rs b/substrate/srml/executive/src/lib.rs index f2073ff738..0fe4b9e33d 100644 --- a/substrate/srml/executive/src/lib.rs +++ b/substrate/srml/executive/src/lib.rs @@ -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, Context: Default, Payment: MakePayment, - AllModules: OnInitialise + OnFinalise, + AllModules: OnInitialise + OnFinalise + OffchainWorker, > ExecuteBlock for Executive where Block::Extrinsic: Checkable + Codec, >::Checked: Applyable, @@ -73,11 +73,11 @@ impl< <<>::Checked as Applyable>::Call as Dispatchable>::Origin: From> { fn execute_block(block: Block) { - Self::execute_block(block); + Executive::::execute_block(block); } fn execute_extrinsics_without_checks(block_number: NumberFor, extrinsics: Vec) { - Self::execute_extrinsics_without_checks(block_number, extrinsics); + Executive::::execute_extrinsics_without_checks(block_number, extrinsics); } } @@ -86,7 +86,7 @@ impl< Block: traits::Block, Context: Default, Payment: MakePayment, - AllModules: OnInitialise + OnFinalise, + AllModules: OnInitialise + OnFinalise + OffchainWorker, > Executive where Block::Extrinsic: Checkable + Codec, >::Checked: Applyable, @@ -319,6 +319,11 @@ impl< }) } } + + /// Start an offchain worker and generate extrinsics. + pub fn offchain_worker(n: System::BlockNumber) { + >::generate_extrinsics(n) + } } #[cfg(test)] diff --git a/substrate/srml/support/src/dispatch.rs b/substrate/srml/support/src/dispatch.rs index c6151d2390..10ef979b61 100644 --- a/substrate/srml/support/src/dispatch.rs +++ b/substrate/srml/support/src/dispatch.rs @@ -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$(, $instance:ident: $instantiable:path)?>; + fn offchain_worker() { $( $impl:tt )* } + ) => { + impl<$trait_instance: $trait_name$(, $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$(, $instance:ident: $instantiable:path)?>; + fn offchain_worker($param:ident : $param_ty:ty) { $( $impl:tt )* } + ) => { + impl<$trait_instance: $trait_name$(, $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$(, $instance:ident: $instantiable:path)?>; + ) => { + impl<$trait_instance: $trait_name$(, $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$(, $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 $(, $instance: $instantiable)?>; + $( $offchain )* + } + decl_module! { @impl_deposit_event $mod_type<$trait_instance: $trait_name $(, $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() {} } } diff --git a/substrate/srml/system/src/lib.rs b/substrate/srml/system/src/lib.rs index c1a5ff9d9a..62bdc5156a 100644 --- a/substrate/srml/system/src/lib.rs +++ b/substrate/srml/system/src/lib.rs @@ -124,8 +124,8 @@ pub struct EventRecord { pub event: E, } -/// Event for the system module. decl_event!( + /// Event for the system module. pub enum Event { /// An extrinsic completed successfully. ExtrinsicSuccess, diff --git a/substrate/srml/timestamp/Cargo.toml b/substrate/srml/timestamp/Cargo.toml index 413c487214..30c3779f31 100644 --- a/substrate/srml/timestamp/Cargo.toml +++ b/substrate/srml/timestamp/Cargo.toml @@ -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", ]