// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
// This program 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.
// This program 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 this program. 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 or added to the next block
//! produced by the node as unsigned transactions.
//!
//! 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::{fmt, sync::Arc};
use futures::{
future::{ready, Future},
prelude::*,
};
use parking_lot::Mutex;
use sc_client_api::BlockchainEvents;
use sc_network::{NetworkPeers, NetworkStateInfo};
use sc_transaction_pool_api::OffchainTransactionPoolFactory;
use sp_api::{ApiExt, ProvideRuntimeApi};
use sp_core::{offchain, traits::SpawnNamed};
use sp_externalities::Extension;
use sp_keystore::{KeystoreExt, KeystorePtr};
use sp_runtime::traits::{self, Header};
use threadpool::ThreadPool;
mod api;
pub use sp_core::offchain::storage::OffchainDb;
pub use sp_offchain::{OffchainWorkerApi, STORAGE_PREFIX};
const LOG_TARGET: &str = "offchain-worker";
/// NetworkProvider provides [`OffchainWorkers`] with all necessary hooks into the
/// underlying Substrate networking.
pub trait NetworkProvider: NetworkStateInfo + NetworkPeers {}
impl NetworkProvider for T where T: NetworkStateInfo + NetworkPeers {}
/// Special type that implements [`OffchainStorage`](offchain::OffchainStorage).
///
/// This type can not be constructed and should only be used when passing `None` as `offchain_db` to
/// [`OffchainWorkerOptions`] to make the compiler happy.
#[derive(Clone)]
pub enum NoOffchainStorage {}
impl offchain::OffchainStorage for NoOffchainStorage {
fn set(&mut self, _: &[u8], _: &[u8], _: &[u8]) {
unimplemented!("`NoOffchainStorage` can not be constructed!")
}
fn remove(&mut self, _: &[u8], _: &[u8]) {
unimplemented!("`NoOffchainStorage` can not be constructed!")
}
fn get(&self, _: &[u8], _: &[u8]) -> Option> {
unimplemented!("`NoOffchainStorage` can not be constructed!")
}
fn compare_and_set(&mut self, _: &[u8], _: &[u8], _: Option<&[u8]>, _: &[u8]) -> bool {
unimplemented!("`NoOffchainStorage` can not be constructed!")
}
}
/// Options for [`OffchainWorkers`]
pub struct OffchainWorkerOptions {
/// Provides access to the runtime api.
pub runtime_api_provider: Arc,
/// Provides access to the keystore.
pub keystore: Option,
/// Provides access to the offchain database.
///
/// Use [`NoOffchainStorage`] as type when passing `None` to have some type that works.
pub offchain_db: Option,
/// Provides access to the transaction pool.
pub transaction_pool: Option>,
/// Provides access to network information.
pub network_provider: Arc,
/// Is the node running as validator?
pub is_validator: bool,
/// Enable http requests from offchain workers?
///
/// If not enabled, any http request will panic.
pub enable_http_requests: bool,
/// Callback to create custom [`Extension`]s that should be registered for the
/// `offchain_worker` runtime call.
///
/// These [`Extension`]s are registered along-side the default extensions and are accessible in
/// the host functions.
///
/// # Example:
///
/// ```nocompile
/// custom_extensions: |block_hash| {
/// vec![MyCustomExtension::new()]
/// }
/// ```
pub custom_extensions: CE,
}
/// An offchain workers manager.
pub struct OffchainWorkers {
runtime_api_provider: Arc,
thread_pool: Mutex,
shared_http_client: api::SharedClient,
enable_http_requests: bool,
keystore: Option,
offchain_db: Option>,
transaction_pool: Option>,
network_provider: Arc,
is_validator: bool,
custom_extensions: Box Vec> + Send>,
}
impl OffchainWorkers {
/// Creates new [`OffchainWorkers`].
pub fn new Vec> + Send + 'static>(
OffchainWorkerOptions {
runtime_api_provider,
keystore,
offchain_db,
transaction_pool,
network_provider,
is_validator,
enable_http_requests,
custom_extensions,
}: OffchainWorkerOptions,
) -> Self {
Self {
runtime_api_provider,
thread_pool: Mutex::new(ThreadPool::with_name(
"offchain-worker".into(),
num_cpus::get(),
)),
shared_http_client: api::SharedClient::new(),
enable_http_requests,
keystore,
offchain_db: offchain_db.map(OffchainDb::new),
transaction_pool,
is_validator,
network_provider,
custom_extensions: Box::new(custom_extensions),
}
}
}
impl fmt::Debug
for OffchainWorkers
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("OffchainWorkers").finish()
}
}
impl OffchainWorkers
where
Block: traits::Block,
RA: ProvideRuntimeApi + Send + Sync + 'static,
RA::Api: OffchainWorkerApi,
Storage: offchain::OffchainStorage + 'static,
{
/// Run the offchain workers on every block import.
pub async fn run>(
self,
import_events: Arc,
spawner: impl SpawnNamed,
) {
import_events
.import_notification_stream()
.for_each(move |n| {
if n.is_new_best {
spawner.spawn(
"offchain-on-block",
Some("offchain-worker"),
self.on_block_imported(&n.header).boxed(),
);
} else {
tracing::debug!(
target: LOG_TARGET,
"Skipping offchain workers for non-canon block: {:?}",
n.header,
)
}
ready(())
})
.await;
}
/// Start the offchain workers after given block.
#[must_use]
fn on_block_imported(&self, header: &Block::Header) -> impl Future