diff --git a/Cargo.lock b/Cargo.lock index fbf7f307962b5..9f29feece6d86 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6673,13 +6673,19 @@ dependencies = [ "parking_lot 0.10.2", "sc-basic-authorship", "sc-client-api", + "sc-consensus-babe", + "sc-consensus-epochs", + "sc-keystore", "sc-transaction-pool", "serde", + "sp-api", "sp-blockchain", "sp-consensus", + "sp-consensus-babe", "sp-core", "sp-inherents", "sp-runtime", + "sp-timestamp", "sp-transaction-pool", "substrate-prometheus-endpoint", "substrate-test-runtime-client", diff --git a/client/consensus/babe/src/aux_schema.rs b/client/consensus/babe/src/aux_schema.rs index 4f26568d83343..74078b4ee7b8a 100644 --- a/client/consensus/babe/src/aux_schema.rs +++ b/client/consensus/babe/src/aux_schema.rs @@ -51,7 +51,7 @@ fn load_decode(backend: &B, key: &[u8]) -> ClientResult> } /// Load or initialize persistent epoch change data from backend. -pub(crate) fn load_epoch_changes( +pub fn load_epoch_changes( backend: &B, config: &BabeGenesisConfiguration, ) -> ClientResult> { diff --git a/client/consensus/babe/src/lib.rs b/client/consensus/babe/src/lib.rs index 67aca1dd43e7a..95f1653d8646d 100644 --- a/client/consensus/babe/src/lib.rs +++ b/client/consensus/babe/src/lib.rs @@ -126,9 +126,10 @@ use schnorrkel::SignatureError; use codec::{Encode, Decode}; use sp_api::ApiExt; -mod aux_schema; mod verification; mod migration; + +pub mod aux_schema; pub mod authorship; #[cfg(test)] mod tests; @@ -1051,7 +1052,7 @@ where } /// Register the babe inherent data provider, if not registered already. -fn register_babe_inherent_data_provider( +pub fn register_babe_inherent_data_provider( inherent_data_providers: &InherentDataProviders, slot_duration: u64, ) -> Result<(), sp_consensus::Error> { diff --git a/client/consensus/manual-seal/Cargo.toml b/client/consensus/manual-seal/Cargo.toml index b557f171c35db..8305f856e09a2 100644 --- a/client/consensus/manual-seal/Cargo.toml +++ b/client/consensus/manual-seal/Cargo.toml @@ -22,20 +22,28 @@ parking_lot = "0.10.0" serde = { version = "1.0", features=["derive"] } assert_matches = "1.3.0" -sc-client-api = { path = "../../../client/api", version = "2.0.0-rc6" } -sc-transaction-pool = { path = "../../transaction-pool", version = "2.0.0-rc6" } -sp-blockchain = { path = "../../../primitives/blockchain", version = "2.0.0-rc6" } -sp-consensus = { package = "sp-consensus", path = "../../../primitives/consensus/common", version = "0.8.0-rc6" } -sp-inherents = { path = "../../../primitives/inherents", version = "2.0.0-rc6" } -sp-runtime = { path = "../../../primitives/runtime", version = "2.0.0-rc6" } -sp-core = { path = "../../../primitives/core", version = "2.0.0-rc6" } -sp-transaction-pool = { path = "../../../primitives/transaction-pool", version = "2.0.0-rc6" } -prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../../utils/prometheus", version = "0.8.0-rc6" } +sc-client-api = { path = "../../api", version = "2.0.0-rc5" } +sc-consensus-babe = { path = "../../consensus/babe", version = "0.8.0-rc5" } +sc-consensus-epochs = { path = "../../consensus/epochs", version = "0.8.0-rc5" } +sp-consensus-babe = { path = "../../../primitives/consensus/babe", version = "0.8.0-rc5" } +sc-keystore = { path = "../../keystore", version = "2.0.0-rc5" } + +sc-transaction-pool = { path = "../../transaction-pool", version = "2.0.0-rc5" } +sp-blockchain = { path = "../../../primitives/blockchain", version = "2.0.0-rc5" } +sp-consensus = { package = "sp-consensus", path = "../../../primitives/consensus/common", version = "0.8.0-rc5" } +sp-inherents = { path = "../../../primitives/inherents", version = "2.0.0-rc5" } +sp-runtime = { path = "../../../primitives/runtime", version = "2.0.0-rc5" } +sp-core = { path = "../../../primitives/core", version = "2.0.0-rc5" } +sp-api = { path = "../../../primitives/api", version = "2.0.0-rc5" } +sp-transaction-pool = { path = "../../../primitives/transaction-pool", version = "2.0.0-rc5" } +sp-timestamp = { path = "../../../primitives/timestamp", version = "2.0.0-rc6" } + +prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../../utils/prometheus", version = "0.8.0-rc5" } [dev-dependencies] +tokio = { version = "0.2", features = ["rt-core", "macros"] } sc-basic-authorship = { path = "../../basic-authorship", version = "0.8.0-rc6" } substrate-test-runtime-client = { path = "../../../test-utils/runtime/client", version = "2.0.0-rc6" } substrate-test-runtime-transaction-pool = { path = "../../../test-utils/runtime/transaction-pool", version = "2.0.0-rc6" } -tokio = { version = "0.2", features = ["rt-core", "macros"] } env_logger = "0.7.0" tempfile = "3.1.0" diff --git a/client/consensus/manual-seal/src/consensus.rs b/client/consensus/manual-seal/src/consensus.rs new file mode 100644 index 0000000000000..7bafeb50207d4 --- /dev/null +++ b/client/consensus/manual-seal/src/consensus.rs @@ -0,0 +1,44 @@ +// This file is part of Substrate. + +// Copyright (C) 2020 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 . + +//! Extensions for manual seal to produce blocks valid for any runtime. +use super::Error; + +use sp_runtime::traits::{Block as BlockT, DigestFor}; +use sp_inherents::InherentData; +use sp_consensus::BlockImportParams; + +pub mod babe; + +/// Consensus data provider, manual seal uses this trait object for authoring blocks valid +/// for any runtime. +pub trait ConsensusDataProvider: Send + Sync { + /// Block import transaction type + type Transaction; + + /// Attempt to create a consensus digest. + fn create_digest(&self, parent: &B::Header, inherents: &InherentData) -> Result, Error>; + + /// set up the neccessary import params. + fn append_block_import( + &self, + parent: &B::Header, + params: &mut BlockImportParams, + inherents: &InherentData + ) -> Result<(), Error>; +} diff --git a/client/consensus/manual-seal/src/consensus/babe.rs b/client/consensus/manual-seal/src/consensus/babe.rs new file mode 100644 index 0000000000000..71dd250733ad9 --- /dev/null +++ b/client/consensus/manual-seal/src/consensus/babe.rs @@ -0,0 +1,197 @@ +// This file is part of Substrate. + +// Copyright (C) 2020 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 . + +//! BABE consensus data provider + +use super::ConsensusDataProvider; +use crate::Error; + +use std::{ + any::Any, + borrow::Cow, + sync::{Arc, atomic}, + time::SystemTime, +}; +use sc_client_api::AuxStore; +use sc_consensus_babe::{ + Config, Epoch, authorship, CompatibleDigestItem, BabeIntermediate, + register_babe_inherent_data_provider, INTERMEDIATE_KEY, +}; +use sc_consensus_epochs::{SharedEpochChanges, descendent_query}; +use sc_keystore::KeyStorePtr; + +use sp_api::{ProvideRuntimeApi, TransactionFor}; +use sp_blockchain::{HeaderBackend, HeaderMetadata}; +use sp_consensus::BlockImportParams; +use sp_consensus_babe::{BabeApi, inherents::BabeInherentData}; +use sp_inherents::{InherentDataProviders, InherentData, ProvideInherentData, InherentIdentifier}; +use sp_runtime::{ + traits::{DigestItemFor, DigestFor, Block as BlockT, Header as _}, + generic::Digest, +}; +use sp_timestamp::{InherentType, InherentError, INHERENT_IDENTIFIER}; + +/// Provides BABE-compatible predigests and BlockImportParams. +/// Intended for use with BABE runtimes. +pub struct BabeConsensusDataProvider { + /// shared reference to keystore + keystore: KeyStorePtr, + + /// Shared reference to the client. + client: Arc, + + /// Shared epoch changes + epoch_changes: SharedEpochChanges, + + /// BABE config, gotten from the runtime. + config: Config, +} + +impl BabeConsensusDataProvider + where + B: BlockT, + C: AuxStore + ProvideRuntimeApi, + C::Api: BabeApi, +{ + pub fn new( + client: Arc, + keystore: KeyStorePtr, + provider: &InherentDataProviders, + epoch_changes: SharedEpochChanges, + ) -> Result { + let config = Config::get_or_compute(&*client)?; + let timestamp_provider = SlotTimestampProvider::new(config.slot_duration)?; + + provider.register_provider(timestamp_provider)?; + register_babe_inherent_data_provider(provider, config.slot_duration)?; + + Ok(Self { + config, + client, + keystore, + epoch_changes, + }) + } +} + +impl ConsensusDataProvider for BabeConsensusDataProvider + where + B: BlockT, + C: AuxStore + HeaderBackend + HeaderMetadata + ProvideRuntimeApi, + C::Api: BabeApi, +{ + type Transaction = TransactionFor; + + fn create_digest(&self, parent: &B::Header, inherents: &InherentData) -> Result, Error> { + let slot_number = inherents.babe_inherent_data()?; + + let epoch_changes = self.epoch_changes.lock(); + let epoch_descriptor = epoch_changes + .epoch_descriptor_for_child_of( + descendent_query(&*self.client), + &parent.hash(), + parent.number().clone(), + slot_number, + ) + .map_err(|e| Error::StringError(format!("failed to fetch epoch_descriptor: {}", e)))? + .ok_or_else(|| sp_consensus::Error::InvalidAuthoritiesSet)?; + + let epoch = epoch_changes + .viable_epoch( + &epoch_descriptor, + |slot| Epoch::genesis(&self.config, slot), + ) + .ok_or_else(|| { + log::info!(target: "babe", "create_digest: no viable_epoch :("); + sp_consensus::Error::InvalidAuthoritiesSet + })?; + + // this is a dev node environment, we should always be able to claim a slot. + let (predigest, _) = authorship::claim_slot(slot_number, epoch.as_ref(), &self.keystore) + .ok_or_else(|| Error::StringError("failed to claim slot for authorship".into()))?; + + Ok(Digest { + logs: vec![ + as CompatibleDigestItem>::babe_pre_digest(predigest), + ], + }) + } + + fn append_block_import( + &self, + parent: &B::Header, + params: &mut BlockImportParams, + inherents: &InherentData + ) -> Result<(), Error> { + let slot_number = inherents.babe_inherent_data()?; + + let epoch_descriptor = self.epoch_changes.lock() + .epoch_descriptor_for_child_of( + descendent_query(&*self.client), + &parent.hash(), + parent.number().clone(), + slot_number, + ) + .map_err(|e| Error::StringError(format!("failed to fetch epoch data: {}", e)))? + .ok_or_else(|| sp_consensus::Error::InvalidAuthoritiesSet)?; + + params.intermediates.insert( + Cow::from(INTERMEDIATE_KEY), + Box::new(BabeIntermediate:: { epoch_descriptor }) as Box, + ); + + Ok(()) + } +} + +/// Provide duration since unix epoch in millisecond for timestamp inherent. +/// Mocks the timestamp inherent to always produce the timestamp for the next babe slot. +struct SlotTimestampProvider { + time: atomic::AtomicU64, + slot_duration: u64 +} + +impl SlotTimestampProvider { + /// create a new mocked time stamp provider. + fn new(slot_duration: u64) -> Result { + let now = SystemTime::now(); + let duration = now.duration_since(SystemTime::UNIX_EPOCH) + .map_err(|err| Error::StringError(format!("{}", err)))?; + Ok(Self { + time: atomic::AtomicU64::new(duration.as_millis() as u64), + slot_duration, + }) + } +} + +impl ProvideInherentData for SlotTimestampProvider { + fn inherent_identifier(&self) -> &'static InherentIdentifier { + &INHERENT_IDENTIFIER + } + + fn provide_inherent_data(&self, inherent_data: &mut InherentData) -> Result<(), sp_inherents::Error> { + // we update the time here. + let duration: InherentType = self.time.fetch_add(self.slot_duration, atomic::Ordering::SeqCst); + inherent_data.put_data(INHERENT_IDENTIFIER, &duration)?; + Ok(()) + } + + fn error_to_string(&self, error: &[u8]) -> Option { + InherentError::try_from(&INHERENT_IDENTIFIER, error).map(|e| format!("{:?}", e)) + } +} \ No newline at end of file diff --git a/client/consensus/manual-seal/src/error.rs b/client/consensus/manual-seal/src/error.rs index 2411a839b027c..e2628008c24c7 100644 --- a/client/consensus/manual-seal/src/error.rs +++ b/client/consensus/manual-seal/src/error.rs @@ -18,6 +18,7 @@ //! A manual sealing engine: the engine listens for rpc calls to seal blocks and create forks. //! This is suitable for a testing environment. + use sp_consensus::{Error as ConsensusError, ImportResult}; use sp_blockchain::Error as BlockchainError; use sp_inherents::Error as InherentsError; diff --git a/client/consensus/manual-seal/src/lib.rs b/client/consensus/manual-seal/src/lib.rs index 36aeffd9794f0..0a8ed28a27c81 100644 --- a/client/consensus/manual-seal/src/lib.rs +++ b/client/consensus/manual-seal/src/lib.rs @@ -21,8 +21,9 @@ use futures::prelude::*; use sp_consensus::{ - Environment, Proposer, ForkChoiceStrategy, BlockImportParams, BlockOrigin, SelectChain, - import_queue::{BasicQueue, CacheKeyId, Verifier, BoxBlockImport}, + Environment, Proposer, SelectChain, BlockImport, + ForkChoiceStrategy, BlockImportParams, BlockOrigin, + import_queue::{Verifier, BasicQueue, CacheKeyId, BoxBlockImport}, }; use sp_blockchain::HeaderBackend; use sp_inherents::InherentDataProviders; @@ -34,17 +35,19 @@ use prometheus_endpoint::Registry; mod error; mod finalize_block; -mod seal_new_block; +mod seal_block; + +pub mod consensus; pub mod rpc; -use self::{ - finalize_block::{finalize_block, FinalizeBlockParams}, - seal_new_block::{seal_new_block, SealBlockParams}, -}; pub use self::{ error::Error, + consensus::ConsensusDataProvider, + finalize_block::{finalize_block, FinalizeBlockParams}, + seal_block::{SealBlockParams, seal_block, MAX_PROPOSAL_DURATION}, rpc::{EngineCommand, CreatedBlock}, }; +use sp_api::{ProvideRuntimeApi, TransactionFor}; /// The verifier for the manual seal engine; instantly finalizes. struct ManualSealVerifier; @@ -87,25 +90,83 @@ pub fn import_queue( ) } +/// Params required to start the instant sealing authorship task. +pub struct ManualSealParams, A: txpool::ChainApi, SC, CS> { + /// Block import instance for well. importing blocks. + pub block_import: BI, + + /// The environment we are producing blocks for. + pub env: E, + + /// Client instance + pub client: Arc, + + /// Shared reference to the transaction pool. + pub pool: Arc>, + + /// Stream, Basically the receiving end of a channel for sending commands to + /// the authorship task. + pub commands_stream: CS, + + /// SelectChain strategy. + pub select_chain: SC, + + /// Digest provider for inclusion in blocks. + pub consensus_data_provider: Option>>>, + + /// Provider for inherents to include in blocks. + pub inherent_data_providers: InherentDataProviders, +} + +/// Params required to start the manual sealing authorship task. +pub struct InstantSealParams, A: txpool::ChainApi, SC> { + /// Block import instance for well. importing blocks. + pub block_import: BI, + + /// The environment we are producing blocks for. + pub env: E, + + /// Client instance + pub client: Arc, + + /// Shared reference to the transaction pool. + pub pool: Arc>, + + /// SelectChain strategy. + pub select_chain: SC, + + /// Digest provider for inclusion in blocks. + pub consensus_data_provider: Option>>>, + + /// Provider for inherents to include in blocks. + pub inherent_data_providers: InherentDataProviders, +} + /// Creates the background authorship task for the manual seal engine. -pub async fn run_manual_seal( - mut block_import: BoxBlockImport, - mut env: E, - client: Arc, - pool: Arc>, - mut commands_stream: S, - select_chain: SC, - inherent_data_providers: InherentDataProviders, +pub async fn run_manual_seal( + ManualSealParams { + mut block_import, + mut env, + client, + pool, + mut commands_stream, + select_chain, + inherent_data_providers, + consensus_data_provider, + .. + }: ManualSealParams ) where A: txpool::ChainApi + 'static, B: BlockT + 'static, - C: HeaderBackend + Finalizer + 'static, + BI: BlockImport> + + Send + Sync + 'static, + C: HeaderBackend + Finalizer + ProvideRuntimeApi + 'static, CB: ClientBackend + 'static, E: Environment + 'static, E::Error: std::fmt::Display, >::Error: std::fmt::Display, - S: Stream::Hash>> + Unpin + 'static, + CS: Stream::Hash>> + Unpin + 'static, SC: SelectChain + 'static, { while let Some(command) = commands_stream.next().await { @@ -116,7 +177,7 @@ pub async fn run_manual_seal( parent_hash, sender, } => { - seal_new_block( + seal_block( SealBlockParams { sender, parent_hash, @@ -126,6 +187,7 @@ pub async fn run_manual_seal( select_chain: &select_chain, block_import: &mut block_import, inherent_data_provider: &inherent_data_providers, + consensus_data_provider: consensus_data_provider.as_ref().map(|p| &**p), pool: pool.clone(), client: client.clone(), } @@ -149,18 +211,24 @@ pub async fn run_manual_seal( /// runs the background authorship task for the instant seal engine. /// instant-seal creates a new block for every transaction imported into /// the transaction pool. -pub async fn run_instant_seal( - block_import: BoxBlockImport, - env: E, - client: Arc, - pool: Arc>, - select_chain: SC, - inherent_data_providers: InherentDataProviders, +pub async fn run_instant_seal( + InstantSealParams { + block_import, + env, + client, + pool, + select_chain, + consensus_data_provider, + inherent_data_providers, + .. + }: InstantSealParams ) where A: txpool::ChainApi + 'static, B: BlockT + 'static, - C: HeaderBackend + Finalizer + 'static, + BI: BlockImport> + + Send + Sync + 'static, + C: HeaderBackend + Finalizer + ProvideRuntimeApi + 'static, CB: ClientBackend + 'static, E: Environment + 'static, E::Error: std::fmt::Display, @@ -181,13 +249,16 @@ pub async fn run_instant_seal( }); run_manual_seal( - block_import, - env, - client, - pool, - commands_stream, - select_chain, - inherent_data_providers, + ManualSealParams { + block_import, + env, + client, + pool, + commands_stream, + select_chain, + consensus_data_provider, + inherent_data_providers, + } ).await } @@ -233,7 +304,7 @@ mod tests { // this test checks that blocks are created as soon as transactions are imported into the pool. let (sender, receiver) = futures::channel::oneshot::channel(); let mut sender = Arc::new(Some(sender)); - let stream = pool.pool().validated_pool().import_notification_stream() + let commands_stream = pool.pool().validated_pool().import_notification_stream() .map(move |_| { // we're only going to submit one tx so this fn will only be called once. let mut_sender = Arc::get_mut(&mut sender).unwrap(); @@ -246,13 +317,16 @@ mod tests { } }); let future = run_manual_seal( - Box::new(client.clone()), - env, - client.clone(), - pool.pool().clone(), - stream, - select_chain, - inherent_data_providers, + ManualSealParams { + block_import: client.clone(), + env, + client: client.clone(), + pool: pool.pool().clone(), + commands_stream, + select_chain, + inherent_data_providers, + consensus_data_provider: None, + } ); std::thread::spawn(|| { let mut rt = tokio::runtime::Runtime::new().unwrap(); @@ -299,15 +373,18 @@ mod tests { None, ); // this test checks that blocks are created as soon as an engine command is sent over the stream. - let (mut sink, stream) = futures::channel::mpsc::channel(1024); + let (mut sink, commands_stream) = futures::channel::mpsc::channel(1024); let future = run_manual_seal( - Box::new(client.clone()), - env, - client.clone(), - pool.pool().clone(), - stream, - select_chain, - inherent_data_providers, + ManualSealParams { + block_import: client.clone(), + env, + client: client.clone(), + pool: pool.pool().clone(), + commands_stream, + select_chain, + consensus_data_provider: None, + inherent_data_providers, + } ); std::thread::spawn(|| { let mut rt = tokio::runtime::Runtime::new().unwrap(); @@ -371,15 +448,18 @@ mod tests { None, ); // this test checks that blocks are created as soon as an engine command is sent over the stream. - let (mut sink, stream) = futures::channel::mpsc::channel(1024); + let (mut sink, commands_stream) = futures::channel::mpsc::channel(1024); let future = run_manual_seal( - Box::new(client.clone()), - env, - client.clone(), - pool.pool().clone(), - stream, - select_chain, - inherent_data_providers, + ManualSealParams { + block_import: client.clone(), + env, + client: client.clone(), + pool: pool.pool().clone(), + commands_stream, + select_chain, + consensus_data_provider: None, + inherent_data_providers, + } ); std::thread::spawn(|| { let mut rt = tokio::runtime::Runtime::new().unwrap(); diff --git a/client/consensus/manual-seal/src/rpc.rs b/client/consensus/manual-seal/src/rpc.rs index f3f0fe4a128ed..690b6c1eb9996 100644 --- a/client/consensus/manual-seal/src/rpc.rs +++ b/client/consensus/manual-seal/src/rpc.rs @@ -14,7 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -//! RPC interface for the ManualSeal Engine. +//! RPC interface for the `ManualSeal` Engine. + use sp_consensus::ImportedAux; use jsonrpc_core::Error; use jsonrpc_derive::rpc; diff --git a/client/consensus/manual-seal/src/seal_new_block.rs b/client/consensus/manual-seal/src/seal_block.rs similarity index 73% rename from client/consensus/manual-seal/src/seal_new_block.rs rename to client/consensus/manual-seal/src/seal_block.rs index c5aea11ced316..58f017f2d41ad 100644 --- a/client/consensus/manual-seal/src/seal_new_block.rs +++ b/client/consensus/manual-seal/src/seal_block.rs @@ -16,7 +16,7 @@ //! Block sealing utilities -use crate::{Error, rpc}; +use crate::{Error, rpc, CreatedBlock, ConsensusDataProvider}; use std::sync::Arc; use sp_runtime::{ traits::{Block as BlockT, Header as HeaderT}, @@ -24,24 +24,21 @@ use sp_runtime::{ }; use futures::prelude::*; use sc_transaction_pool::txpool; -use rpc::CreatedBlock; - use sp_consensus::{ - self, BlockImport, Environment, Proposer, - ForkChoiceStrategy, BlockImportParams, BlockOrigin, - ImportResult, SelectChain, - import_queue::BoxBlockImport, + self, BlockImport, Environment, Proposer, ForkChoiceStrategy, + BlockImportParams, BlockOrigin, ImportResult, SelectChain, }; use sp_blockchain::HeaderBackend; use std::collections::HashMap; use std::time::Duration; use sp_inherents::InherentDataProviders; +use sp_api::{ProvideRuntimeApi, TransactionFor}; /// max duration for creating a proposal in secs -const MAX_PROPOSAL_DURATION: u64 = 10; +pub const MAX_PROPOSAL_DURATION: u64 = 10; /// params for sealing a new block -pub struct SealBlockParams<'a, B: BlockT, SC, HB, E, T, P: txpool::ChainApi> { +pub struct SealBlockParams<'a, B: BlockT, BI, SC, C: ProvideRuntimeApi, E, P: txpool::ChainApi> { /// if true, empty blocks(without extrinsics) will be created. /// otherwise, will return Error::EmptyTransactionPool. pub create_empty: bool, @@ -54,19 +51,21 @@ pub struct SealBlockParams<'a, B: BlockT, SC, HB, E, T, P: txpool::ChainApi> { /// transaction pool pub pool: Arc>, /// header backend - pub client: Arc, + pub client: Arc, /// Environment trait object for creating a proposer pub env: &'a mut E, /// SelectChain object pub select_chain: &'a SC, + /// Digest provider for inclusion in blocks. + pub consensus_data_provider: Option<&'a dyn ConsensusDataProvider>>, /// block import object - pub block_import: &'a mut BoxBlockImport, + pub block_import: &'a mut BI, /// inherent data provider pub inherent_data_provider: &'a InherentDataProviders, } /// seals a new block with the given params -pub async fn seal_new_block( +pub async fn seal_block( SealBlockParams { create_empty, finalize, @@ -77,13 +76,16 @@ pub async fn seal_new_block( block_import, env, inherent_data_provider, + consensus_data_provider: digest_provider, mut sender, .. - }: SealBlockParams<'_, B, SC, HB, E, T, P> + }: SealBlockParams<'_, B, BI, SC, C, E, P> ) where B: BlockT, - HB: HeaderBackend, + BI: BlockImport> + + Send + Sync + 'static, + C: HeaderBackend + ProvideRuntimeApi, E: Environment, >::Error: std::fmt::Display, >::Error: std::fmt::Display, @@ -98,7 +100,7 @@ pub async fn seal_new_block( // get the header to build this new block on. // use the parent_hash supplied via `EngineCommand` // or fetch the best_block. - let header = match parent_hash { + let parent = match parent_hash { Some(hash) => { match client.header(BlockId::Hash(hash))? { Some(header) => header, @@ -108,11 +110,18 @@ pub async fn seal_new_block( None => select_chain.best_chain()? }; - let proposer = env.init(&header) + let proposer = env.init(&parent) .map_err(|err| Error::StringError(format!("{}", err))).await?; let id = inherent_data_provider.create_inherent_data()?; let inherents_len = id.len(); - let proposal = proposer.propose(id, Default::default(), Duration::from_secs(MAX_PROPOSAL_DURATION), false.into()) + + let digest = if let Some(digest_provider) = digest_provider { + digest_provider.create_digest(&parent, &id)? + } else { + Default::default() + }; + + let proposal = proposer.propose(id.clone(), digest, Duration::from_secs(MAX_PROPOSAL_DURATION), false.into()) .map_err(|err| Error::StringError(format!("{}", err))).await?; if proposal.block.extrinsics().len() == inherents_len && !create_empty { @@ -125,6 +134,10 @@ pub async fn seal_new_block( params.finalized = finalize; params.fork_choice = Some(ForkChoiceStrategy::LongestChain); + if let Some(digest_provider) = digest_provider { + digest_provider.append_block_import(&parent, &mut params, &id)?; + } + match block_import.import_block(params, HashMap::new())? { ImportResult::Imported(aux) => { Ok(CreatedBlock { hash: ::Header::hash(&header), aux })