mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 04:41:03 +00:00
manual seal is now consensus agnostic (#7010)
* manual seal is now consensus agnostic * pr grumbles
This commit is contained in:
Generated
+6
@@ -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",
|
||||
|
||||
@@ -51,7 +51,7 @@ fn load_decode<B, T>(backend: &B, key: &[u8]) -> ClientResult<Option<T>>
|
||||
}
|
||||
|
||||
/// Load or initialize persistent epoch change data from backend.
|
||||
pub(crate) fn load_epoch_changes<Block: BlockT, B: AuxStore>(
|
||||
pub fn load_epoch_changes<Block: BlockT, B: AuxStore>(
|
||||
backend: &B,
|
||||
config: &BabeGenesisConfiguration,
|
||||
) -> ClientResult<SharedEpochChanges<Block, Epoch>> {
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! 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<B: BlockT>: Send + Sync {
|
||||
/// Block import transaction type
|
||||
type Transaction;
|
||||
|
||||
/// Attempt to create a consensus digest.
|
||||
fn create_digest(&self, parent: &B::Header, inherents: &InherentData) -> Result<DigestFor<B>, Error>;
|
||||
|
||||
/// set up the neccessary import params.
|
||||
fn append_block_import(
|
||||
&self,
|
||||
parent: &B::Header,
|
||||
params: &mut BlockImportParams<B, Self::Transaction>,
|
||||
inherents: &InherentData
|
||||
) -> Result<(), Error>;
|
||||
}
|
||||
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! 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<B: BlockT, C> {
|
||||
/// shared reference to keystore
|
||||
keystore: KeyStorePtr,
|
||||
|
||||
/// Shared reference to the client.
|
||||
client: Arc<C>,
|
||||
|
||||
/// Shared epoch changes
|
||||
epoch_changes: SharedEpochChanges<B, Epoch>,
|
||||
|
||||
/// BABE config, gotten from the runtime.
|
||||
config: Config,
|
||||
}
|
||||
|
||||
impl<B, C> BabeConsensusDataProvider<B, C>
|
||||
where
|
||||
B: BlockT,
|
||||
C: AuxStore + ProvideRuntimeApi<B>,
|
||||
C::Api: BabeApi<B, Error = sp_blockchain::Error>,
|
||||
{
|
||||
pub fn new(
|
||||
client: Arc<C>,
|
||||
keystore: KeyStorePtr,
|
||||
provider: &InherentDataProviders,
|
||||
epoch_changes: SharedEpochChanges<B, Epoch>,
|
||||
) -> Result<Self, Error> {
|
||||
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<B, C> ConsensusDataProvider<B> for BabeConsensusDataProvider<B, C>
|
||||
where
|
||||
B: BlockT,
|
||||
C: AuxStore + HeaderBackend<B> + HeaderMetadata<B, Error = sp_blockchain::Error> + ProvideRuntimeApi<B>,
|
||||
C::Api: BabeApi<B, Error = sp_blockchain::Error>,
|
||||
{
|
||||
type Transaction = TransactionFor<C, B>;
|
||||
|
||||
fn create_digest(&self, parent: &B::Header, inherents: &InherentData) -> Result<DigestFor<B>, 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![
|
||||
<DigestItemFor<B> as CompatibleDigestItem>::babe_pre_digest(predigest),
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
fn append_block_import(
|
||||
&self,
|
||||
parent: &B::Header,
|
||||
params: &mut BlockImportParams<B, Self::Transaction>,
|
||||
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::<B> { epoch_descriptor }) as Box<dyn Any>,
|
||||
);
|
||||
|
||||
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<Self, Error> {
|
||||
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<String> {
|
||||
InherentError::try_from(&INHERENT_IDENTIFIER, error).map(|e| format!("{:?}", e))
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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<Block, Transaction>(
|
||||
)
|
||||
}
|
||||
|
||||
/// Params required to start the instant sealing authorship task.
|
||||
pub struct ManualSealParams<B: BlockT, BI, E, C: ProvideRuntimeApi<B>, 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<C>,
|
||||
|
||||
/// Shared reference to the transaction pool.
|
||||
pub pool: Arc<txpool::Pool<A>>,
|
||||
|
||||
/// Stream<Item = EngineCommands>, 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<Box<dyn ConsensusDataProvider<B, Transaction = TransactionFor<C, B>>>>,
|
||||
|
||||
/// Provider for inherents to include in blocks.
|
||||
pub inherent_data_providers: InherentDataProviders,
|
||||
}
|
||||
|
||||
/// Params required to start the manual sealing authorship task.
|
||||
pub struct InstantSealParams<B: BlockT, BI, E, C: ProvideRuntimeApi<B>, 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<C>,
|
||||
|
||||
/// Shared reference to the transaction pool.
|
||||
pub pool: Arc<txpool::Pool<A>>,
|
||||
|
||||
/// SelectChain strategy.
|
||||
pub select_chain: SC,
|
||||
|
||||
/// Digest provider for inclusion in blocks.
|
||||
pub consensus_data_provider: Option<Box<dyn ConsensusDataProvider<B, Transaction = TransactionFor<C, B>>>>,
|
||||
|
||||
/// 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<B, CB, E, C, A, SC, S, T>(
|
||||
mut block_import: BoxBlockImport<B, T>,
|
||||
mut env: E,
|
||||
client: Arc<C>,
|
||||
pool: Arc<txpool::Pool<A>>,
|
||||
mut commands_stream: S,
|
||||
select_chain: SC,
|
||||
inherent_data_providers: InherentDataProviders,
|
||||
pub async fn run_manual_seal<B, BI, CB, E, C, A, SC, CS>(
|
||||
ManualSealParams {
|
||||
mut block_import,
|
||||
mut env,
|
||||
client,
|
||||
pool,
|
||||
mut commands_stream,
|
||||
select_chain,
|
||||
inherent_data_providers,
|
||||
consensus_data_provider,
|
||||
..
|
||||
}: ManualSealParams<B, BI, E, C, A, SC, CS>
|
||||
)
|
||||
where
|
||||
A: txpool::ChainApi<Block=B> + 'static,
|
||||
B: BlockT + 'static,
|
||||
C: HeaderBackend<B> + Finalizer<B, CB> + 'static,
|
||||
BI: BlockImport<B, Error = sp_consensus::Error, Transaction = sp_api::TransactionFor<C, B>>
|
||||
+ Send + Sync + 'static,
|
||||
C: HeaderBackend<B> + Finalizer<B, CB> + ProvideRuntimeApi<B> + 'static,
|
||||
CB: ClientBackend<B> + 'static,
|
||||
E: Environment<B> + 'static,
|
||||
E::Error: std::fmt::Display,
|
||||
<E::Proposer as Proposer<B>>::Error: std::fmt::Display,
|
||||
S: Stream<Item=EngineCommand<<B as BlockT>::Hash>> + Unpin + 'static,
|
||||
CS: Stream<Item=EngineCommand<<B as BlockT>::Hash>> + Unpin + 'static,
|
||||
SC: SelectChain<B> + 'static,
|
||||
{
|
||||
while let Some(command) = commands_stream.next().await {
|
||||
@@ -116,7 +177,7 @@ pub async fn run_manual_seal<B, CB, E, C, A, SC, S, T>(
|
||||
parent_hash,
|
||||
sender,
|
||||
} => {
|
||||
seal_new_block(
|
||||
seal_block(
|
||||
SealBlockParams {
|
||||
sender,
|
||||
parent_hash,
|
||||
@@ -126,6 +187,7 @@ pub async fn run_manual_seal<B, CB, E, C, A, SC, S, T>(
|
||||
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<B, CB, E, C, A, SC, S, T>(
|
||||
/// 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<B, CB, E, C, A, SC, T>(
|
||||
block_import: BoxBlockImport<B, T>,
|
||||
env: E,
|
||||
client: Arc<C>,
|
||||
pool: Arc<txpool::Pool<A>>,
|
||||
select_chain: SC,
|
||||
inherent_data_providers: InherentDataProviders,
|
||||
pub async fn run_instant_seal<B, BI, CB, E, C, A, SC>(
|
||||
InstantSealParams {
|
||||
block_import,
|
||||
env,
|
||||
client,
|
||||
pool,
|
||||
select_chain,
|
||||
consensus_data_provider,
|
||||
inherent_data_providers,
|
||||
..
|
||||
}: InstantSealParams<B, BI, E, C, A, SC>
|
||||
)
|
||||
where
|
||||
A: txpool::ChainApi<Block=B> + 'static,
|
||||
B: BlockT + 'static,
|
||||
C: HeaderBackend<B> + Finalizer<B, CB> + 'static,
|
||||
BI: BlockImport<B, Error = sp_consensus::Error, Transaction = sp_api::TransactionFor<C, B>>
|
||||
+ Send + Sync + 'static,
|
||||
C: HeaderBackend<B> + Finalizer<B, CB> + ProvideRuntimeApi<B> + 'static,
|
||||
CB: ClientBackend<B> + 'static,
|
||||
E: Environment<B> + 'static,
|
||||
E::Error: std::fmt::Display,
|
||||
@@ -181,13 +249,16 @@ pub async fn run_instant_seal<B, CB, E, C, A, SC, T>(
|
||||
});
|
||||
|
||||
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();
|
||||
|
||||
@@ -14,7 +14,8 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! RPC interface for the ManualSeal Engine.
|
||||
//! RPC interface for the `ManualSeal` Engine.
|
||||
|
||||
use sp_consensus::ImportedAux;
|
||||
use jsonrpc_core::Error;
|
||||
use jsonrpc_derive::rpc;
|
||||
|
||||
+30
-17
@@ -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<B>, 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<txpool::Pool<P>>,
|
||||
/// header backend
|
||||
pub client: Arc<HB>,
|
||||
pub client: Arc<C>,
|
||||
/// 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<B, Transaction = TransactionFor<C, B>>>,
|
||||
/// block import object
|
||||
pub block_import: &'a mut BoxBlockImport<B, T>,
|
||||
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<B, SC, HB, E, T, P>(
|
||||
pub async fn seal_block<B, BI, SC, C, E, P>(
|
||||
SealBlockParams {
|
||||
create_empty,
|
||||
finalize,
|
||||
@@ -77,13 +76,16 @@ pub async fn seal_new_block<B, SC, HB, E, T, P>(
|
||||
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<B>,
|
||||
BI: BlockImport<B, Error = sp_consensus::Error, Transaction = sp_api::TransactionFor<C, B>>
|
||||
+ Send + Sync + 'static,
|
||||
C: HeaderBackend<B> + ProvideRuntimeApi<B>,
|
||||
E: Environment<B>,
|
||||
<E as Environment<B>>::Error: std::fmt::Display,
|
||||
<E::Proposer as Proposer<B>>::Error: std::fmt::Display,
|
||||
@@ -98,7 +100,7 @@ pub async fn seal_new_block<B, SC, HB, E, T, P>(
|
||||
// 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<B, SC, HB, E, T, P>(
|
||||
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<B, SC, HB, E, T, P>(
|
||||
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: <B as BlockT>::Header::hash(&header), aux })
|
||||
Reference in New Issue
Block a user