Skip to content

Commit

Permalink
manual seal is now consensus agnostic (paritytech#7010)
Browse files Browse the repository at this point in the history
* manual seal is now consensus agnostic

* pr grumbles
  • Loading branch information
seunlanlege authored and thadouk committed Sep 10, 2020
1 parent eb52e43 commit 43e9af8
Show file tree
Hide file tree
Showing 10 changed files with 439 additions and 88 deletions.
6 changes: 6 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion client/consensus/babe/src/aux_schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>> {
Expand Down
5 changes: 3 additions & 2 deletions client/consensus/babe/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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> {
Expand Down
28 changes: 18 additions & 10 deletions client/consensus/manual-seal/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
44 changes: 44 additions & 0 deletions client/consensus/manual-seal/src/consensus.rs
Original file line number Diff line number Diff line change
@@ -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>;
}
197 changes: 197 additions & 0 deletions client/consensus/manual-seal/src/consensus/babe.rs
Original file line number Diff line number Diff line change
@@ -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))
}
}
1 change: 1 addition & 0 deletions client/consensus/manual-seal/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Loading

0 comments on commit 43e9af8

Please sign in to comment.