Adds AuraConsensusDataProvider (#10503)

* adds support for parachains to test-runner

* adds file header

* Apply suggestions from code review

Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>

* proper docs, remove unused _client

* fixes

* Update client/consensus/manual-seal/src/consensus/timestamp.rs

Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>

* Update client/consensus/manual-seal/src/consensus/timestamp.rs

Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>

* pr fixes

Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>
This commit is contained in:
Seun Lanlege
2022-01-10 15:39:04 +01:00
committed by GitHub
parent 2178cb1939
commit a4057bb9e2
17 changed files with 278 additions and 1368 deletions
+13 -2
View File
@@ -490,24 +490,34 @@ fn aura_err<B: BlockT>(error: Error<B>) -> Error<B> {
error
}
/// Aura Errors
#[derive(derive_more::Display, Debug)]
enum Error<B: BlockT> {
pub enum Error<B: BlockT> {
/// Multiple Aura pre-runtime headers
#[display(fmt = "Multiple Aura pre-runtime headers")]
MultipleHeaders,
/// No Aura pre-runtime digest found
#[display(fmt = "No Aura pre-runtime digest found")]
NoDigestFound,
/// Header is unsealed
#[display(fmt = "Header {:?} is unsealed", _0)]
HeaderUnsealed(B::Hash),
/// Header has a bad seal
#[display(fmt = "Header {:?} has a bad seal", _0)]
HeaderBadSeal(B::Hash),
/// Slot Author not found
#[display(fmt = "Slot Author not found")]
SlotAuthorNotFound,
/// Bad signature
#[display(fmt = "Bad signature on {:?}", _0)]
BadSignature(B::Hash),
/// Client Error
Client(sp_blockchain::Error),
/// Unknown inherent error for identifier
#[display(fmt = "Unknown inherent error for identifier: {}", "String::from_utf8_lossy(_0)")]
UnknownInherentError(sp_inherents::InherentIdentifier),
#[display(fmt = "Inherent error: {}", _0)]
/// Inherents Error
Inherent(sp_inherents::Error),
}
@@ -517,7 +527,8 @@ impl<B: BlockT> std::convert::From<Error<B>> for String {
}
}
fn find_pre_digest<B: BlockT, Signature: Codec>(header: &B::Header) -> Result<Slot, Error<B>> {
/// Get pre-digests from the header
pub fn find_pre_digest<B: BlockT, Signature: Codec>(header: &B::Header) -> Result<Slot, Error<B>> {
if header.number().is_zero() {
return Ok(0.into())
}
@@ -27,8 +27,10 @@ async-trait = "0.1.50"
sc-client-api = { path = "../../api", version = "4.0.0-dev" }
sc-consensus = { version = "0.10.0-dev", path = "../../consensus/common" }
sc-consensus-babe = { path = "../../consensus/babe", version = "0.10.0-dev" }
sc-consensus-aura = { path = "../../consensus/aura", version = "0.10.0-dev" }
sc-consensus-epochs = { path = "../../consensus/epochs", version = "0.10.0-dev" }
sp-consensus-babe = { path = "../../../primitives/consensus/babe", version = "0.10.0-dev" }
sp-consensus-aura = { path = "../../../primitives/consensus/aura", version = "0.10.0-dev" }
sc-transaction-pool = { path = "../../transaction-pool", version = "4.0.0-dev" }
sp-blockchain = { path = "../../../primitives/blockchain", version = "4.0.0-dev" }
@@ -23,7 +23,9 @@ use sc_consensus::BlockImportParams;
use sp_inherents::InherentData;
use sp_runtime::{traits::Block as BlockT, Digest};
pub mod aura;
pub mod babe;
pub mod timestamp;
/// Consensus data provider, manual seal uses this trait object for authoring blocks valid
/// for any runtime.
@@ -0,0 +1,97 @@
// This file is part of Substrate.
// Copyright (C) 2022 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/>.
//! Aura consensus data provider, This allows manual seal author blocks that are valid for
//! runtimes that expect the aura-specific digests.
use crate::{ConsensusDataProvider, Error};
use sc_client_api::{AuxStore, UsageProvider};
use sc_consensus::BlockImportParams;
use sc_consensus_aura::slot_duration;
use sp_api::{ProvideRuntimeApi, TransactionFor};
use sp_blockchain::{HeaderBackend, HeaderMetadata};
use sp_consensus_aura::{
digests::CompatibleDigestItem,
sr25519::{AuthorityId, AuthoritySignature},
AuraApi,
};
use sp_inherents::InherentData;
use sp_runtime::{traits::Block as BlockT, Digest, DigestItem};
use sp_timestamp::TimestampInherentData;
use std::{marker::PhantomData, sync::Arc};
/// Consensus data provider for Aura.
pub struct AuraConsensusDataProvider<B, C> {
// slot duration in milliseconds
slot_duration: u64,
// phantom data for required generics
_phantom: PhantomData<(B, C)>,
}
impl<B, C> AuraConsensusDataProvider<B, C>
where
B: BlockT,
C: AuxStore + ProvideRuntimeApi<B> + UsageProvider<B>,
C::Api: AuraApi<B, AuthorityId>,
{
/// Creates a new instance of the [`AuraConsensusDataProvider`], requires that `client`
/// implements [`sp_consensus_aura::AuraApi`]
pub fn new(client: Arc<C>) -> Self {
let slot_duration =
(*slot_duration(&*client).expect("slot_duration is always present; qed.")).get();
Self { slot_duration, _phantom: PhantomData }
}
}
impl<B, C> ConsensusDataProvider<B> for AuraConsensusDataProvider<B, C>
where
B: BlockT,
C: AuxStore
+ HeaderBackend<B>
+ HeaderMetadata<B, Error = sp_blockchain::Error>
+ UsageProvider<B>
+ ProvideRuntimeApi<B>,
C::Api: AuraApi<B, AuthorityId>,
{
type Transaction = TransactionFor<C, B>;
fn create_digest(
&self,
_parent: &B::Header,
inherents: &InherentData,
) -> Result<Digest, Error> {
let time_stamp =
*inherents.timestamp_inherent_data()?.expect("Timestamp is always present; qed");
// we always calculate the new slot number based on the current time-stamp and the slot
// duration.
let digest_item = <DigestItem as CompatibleDigestItem<AuthoritySignature>>::aura_pre_digest(
(time_stamp / self.slot_duration).into(),
);
Ok(Digest { logs: vec![digest_item] })
}
fn append_block_import(
&self,
_parent: &B::Header,
_params: &mut BlockImportParams<B, Self::Transaction>,
_inherents: &InherentData,
) -> Result<(), Error> {
Ok(())
}
}
@@ -16,7 +16,8 @@
// 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
//! BABE consensus data provider, This allows manual seal author blocks that are valid for runtimes
//! that expect babe-specific digests.
use super::ConsensusDataProvider;
use crate::Error;
@@ -30,11 +31,7 @@ use sc_consensus_epochs::{
descendent_query, EpochHeader, SharedEpochChanges, ViableEpochDescriptor,
};
use sp_keystore::SyncCryptoStorePtr;
use std::{
borrow::Cow,
sync::{atomic, Arc},
time::SystemTime,
};
use std::{borrow::Cow, sync::Arc};
use sc_consensus::{BlockImportParams, ForkChoiceStrategy, Verifier};
use sp_api::{ProvideRuntimeApi, TransactionFor};
@@ -46,13 +43,13 @@ use sp_consensus_babe::{
AuthorityId, BabeApi, BabeAuthorityWeight, ConsensusLog, BABE_ENGINE_ID,
};
use sp_consensus_slots::Slot;
use sp_inherents::{InherentData, InherentDataProvider, InherentIdentifier};
use sp_inherents::InherentData;
use sp_runtime::{
generic::{BlockId, Digest},
traits::{Block as BlockT, Header, Zero},
traits::{Block as BlockT, Header},
DigestItem,
};
use sp_timestamp::{InherentType, TimestampInherentData, INHERENT_IDENTIFIER};
use sp_timestamp::TimestampInherentData;
/// Provides BABE-compatible predigests and BlockImportParams.
/// Intended for use with BABE runtimes.
@@ -311,67 +308,3 @@ where
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.
pub struct SlotTimestampProvider {
time: atomic::AtomicU64,
slot_duration: u64,
}
impl SlotTimestampProvider {
/// Create a new mocked time stamp provider.
pub fn new<B, C>(client: Arc<C>) -> Result<Self, Error>
where
B: BlockT,
C: AuxStore + HeaderBackend<B> + ProvideRuntimeApi<B> + UsageProvider<B>,
C::Api: BabeApi<B>,
{
let slot_duration = Config::get(&*client)?.slot_duration;
let info = client.info();
// looks like this isn't the first block, rehydrate the fake time.
// otherwise we'd be producing blocks for older slots.
let time = if info.best_number != Zero::zero() {
let header = client.header(BlockId::Hash(info.best_hash))?.unwrap();
let slot = find_pre_digest::<B>(&header).unwrap().slot();
// add the slot duration so there's no collision of slots
(*slot * slot_duration) + slot_duration
} else {
// this is the first block, use the correct time.
let now = SystemTime::now();
now.duration_since(SystemTime::UNIX_EPOCH)
.map_err(|err| Error::StringError(format!("{}", err)))?
.as_millis() as u64
};
Ok(Self { time: atomic::AtomicU64::new(time), slot_duration })
}
/// Get the current slot number
pub fn slot(&self) -> u64 {
self.time.load(atomic::Ordering::SeqCst) / self.slot_duration
}
}
#[async_trait::async_trait]
impl InherentDataProvider for SlotTimestampProvider {
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).into();
inherent_data.put_data(INHERENT_IDENTIFIER, &duration)?;
Ok(())
}
async fn try_handle_error(
&self,
_: &InherentIdentifier,
_: &[u8],
) -> Option<Result<(), sp_inherents::Error>> {
None
}
}
@@ -0,0 +1,156 @@
// This file is part of Substrate.
// Copyright (C) 2020-2021 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/>.
//! Mocked timestamp inherent, allows for manual seal to create blocks for runtimes
//! that expect this inherent.
use crate::Error;
use sc_client_api::{AuxStore, UsageProvider};
use sc_consensus_aura::slot_duration;
use sc_consensus_babe::Config;
use sp_api::ProvideRuntimeApi;
use sp_blockchain::HeaderBackend;
use sp_consensus_aura::{
sr25519::{AuthorityId, AuthoritySignature},
AuraApi,
};
use sp_consensus_babe::BabeApi;
use sp_inherents::{InherentData, InherentDataProvider, InherentIdentifier};
use sp_runtime::{
generic::BlockId,
traits::{Block as BlockT, Zero},
};
use sp_timestamp::{InherentType, INHERENT_IDENTIFIER};
use std::{
sync::{atomic, Arc},
time::SystemTime,
};
/// Provide duration since unix epoch in millisecond for timestamp inherent.
/// Mocks the timestamp inherent to always produce a valid timestamp for the next slot.
///
/// This works by either fetching the `slot_number` from the most recent header and dividing
/// that value by `slot_duration` in order to fork chains that expect this inherent.
///
/// It produces timestamp inherents that are increaed by `slot_duraation` whenever
/// `provide_inherent_data` is called.
pub struct SlotTimestampProvider {
// holds the unix millisecnd timestamp for the most recent block
unix_millis: atomic::AtomicU64,
// configured slot_duration in the runtime
slot_duration: u64,
}
impl SlotTimestampProvider {
/// Create a new mocked time stamp provider, for babe.
pub fn new_babe<B, C>(client: Arc<C>) -> Result<Self, Error>
where
B: BlockT,
C: AuxStore + HeaderBackend<B> + ProvideRuntimeApi<B> + UsageProvider<B>,
C::Api: BabeApi<B>,
{
let slot_duration = Config::get(&*client)?.slot_duration;
let time = Self::with_header(&client, slot_duration, |header| {
let slot_number = *sc_consensus_babe::find_pre_digest::<B>(&header)
.map_err(|err| format!("{}", err))?
.slot();
Ok(slot_number)
})?;
Ok(Self { unix_millis: atomic::AtomicU64::new(time), slot_duration })
}
/// Create a new mocked time stamp provider, for aura
pub fn new_aura<B, C>(client: Arc<C>) -> Result<Self, Error>
where
B: BlockT,
C: AuxStore + HeaderBackend<B> + ProvideRuntimeApi<B> + UsageProvider<B>,
C::Api: AuraApi<B, AuthorityId>,
{
let slot_duration = (*slot_duration(&*client)?).get();
let time = Self::with_header(&client, slot_duration, |header| {
let slot_number = *sc_consensus_aura::find_pre_digest::<B, AuthoritySignature>(&header)
.map_err(|err| format!("{}", err))?;
Ok(slot_number)
})?;
Ok(Self { unix_millis: atomic::AtomicU64::new(time), slot_duration })
}
fn with_header<F, C, B>(client: &Arc<C>, slot_duration: u64, func: F) -> Result<u64, Error>
where
B: BlockT,
C: AuxStore + HeaderBackend<B> + UsageProvider<B>,
F: Fn(B::Header) -> Result<u64, Error>,
{
let info = client.info();
// looks like this isn't the first block, rehydrate the fake time.
// otherwise we'd be producing blocks for older slots.
let time = if info.best_number != Zero::zero() {
let header = client
.header(BlockId::Hash(info.best_hash))?
.ok_or_else(|| "best header not found in the db!".to_string())?;
let slot = func(header)?;
// add the slot duration so there's no collision of slots
(slot * slot_duration) + slot_duration
} else {
// this is the first block, use the correct time.
let now = SystemTime::now();
now.duration_since(SystemTime::UNIX_EPOCH)
.map_err(|err| Error::StringError(format!("{}", err)))?
.as_millis() as u64
};
Ok(time)
}
/// Get the current slot number
pub fn slot(&self) -> u64 {
self.unix_millis.load(atomic::Ordering::SeqCst) / self.slot_duration
}
/// Gets the current time stamp.
pub fn timestamp(&self) -> sp_timestamp::Timestamp {
sp_timestamp::Timestamp::new(self.unix_millis.load(atomic::Ordering::SeqCst))
}
}
#[async_trait::async_trait]
impl InherentDataProvider for SlotTimestampProvider {
fn provide_inherent_data(
&self,
inherent_data: &mut InherentData,
) -> Result<(), sp_inherents::Error> {
// we update the time here.
let new_time: InherentType =
self.unix_millis.fetch_add(self.slot_duration, atomic::Ordering::SeqCst).into();
inherent_data.put_data(INHERENT_IDENTIFIER, &new_time)?;
Ok(())
}
async fn try_handle_error(
&self,
_: &InherentIdentifier,
_: &[u8],
) -> Option<Result<(), sp_inherents::Error>> {
None
}
}
@@ -62,7 +62,6 @@ pub enum Error {
BlockNotFound(String),
/// Some string error
#[display(fmt = "{}", _0)]
#[from(ignore)]
StringError(String),
/// send error
#[display(fmt = "Consensus process is terminating")]