mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-14 13:21:10 +00:00
Introduce inherent digests (#2466)
* Introduce inherent digests * Implement inherent digests * fix silly error * Implementation of inherent digests in BABE All tests pass. There are still limitations: 1. The runtime strips out inherent digests, so BABE must re-add them. 2. The test runtime checks that it can re-compute all digests. It can’t, so I had to comment out that test. * Fix compilation and seal import Seals were not imported correctly: the pre-digest was imported twice, instead of both it and the seal being imported. Also, other parts of the code did not compile due to incomplete refactoring. * Remove bogus assertion * Fix testsuite compilation * Remove unused import * Fix compiler diagnostics * Add inherent digest parameters to block constructors This enforces that inherent digests are added first. * Fixup Cargo.lock * Fix build errors * Re-add an incorrectly removed import * Bump primitive-types version * Update Cargo.lock * Refactoring * Use inherent digests for AuRa They do reach the runtime, but get stripped. I have not figured out where. * Fix compilation errors * Fix compilation errors due to incorrect types * Fix whitespace Suggested-by: Tomasz Drwiega <tomasz@parity.io> * Add preamble Suggested-by: Tomasz Drwiega <tomasz@parity.io> * Fix silly compile error * Refactor pre-digest finding code into a separate function * Remove unwanted assertion It is too likely to bring down the entire blockchain. Suggested-by: Tomasz Drwiega <tomasz@parity.io> * Use `find_pre_digest` after runtime, too Also, use `Member` trait rather than rolling our own requirements. Suggested-by: Tomasz Drwiega <tomasz@parity.io> * Fix various warnings mostly due to upgrading the dependency on `error_chain`. * Pre-digests nearly complete This nearly completes the implementation of pre-runtime digests. * `Seal2` → `Seal` and fix test suite * Try to fix the storage error * Try to fix storage (again) * Fix tests * Hopefully finish pre-runtime digests The key is to pass *only* the pre-runtime digests to the runtime. The others must be stripped out by `initialize_block`. * Fix silly typo * Fix another silly mistake * Remove unnecessary filtering of BABE pre-digests We no longer get duplicate BABE pre-digests, so if they appear, the header should be rejected outright. * Update Cargo.lock files * Reformatting * Fix silly typo in inherent digest code Also, revert `error.rs` files that contained calls to the `error_chain!` macro. * Try to keep the runtime from stripping pre-digests Currently runs into the “Storage root must match that calculated” assertion. * Don’t compute storage root until storage changes are done. Also, fix a compilation error. * Fix compile-time error * Fix compilation errors * Fix more compile errors * Hopefully it compiles this time… * Fix compilation and add docs * Prevent BABE from adding duplicate pre-runtime digests Found by comparing with the AuRa code. I also did some refactoring. * Respond to review and fix some warnings * Delete some dead code introduced earlier * More dead code goes away * `ref mut` → `&mut` * Respond to review and fix some warnings * Fix compilation error * Remove unneeded `HashT` type parameter Suggested-by: Robert Habermeier <robert@parity.io> * Remove spurious #[allow(deprecated)] * Document inherent digest parameter to `build_block` * Delete `Simple` trait It wasn’t needed * delete wrongly added files * Fix trait bounds * Digest serialization tests I also did some reformatting and cleanup. * Apply suggestions from code review Reformatting Co-Authored-By: André Silva <andre.beat@gmail.com> * Swap two arguments to `propose` and `propose_with` Also, remove some needless unsafe code. * Remove bogus `#![allow(deprecated)]` annotations With the removal of the deprecated `Seal` variant, these are not needed. * Add a missing `#[allow(deprecated)]` in the AuRa tests * Fix silly compile error * Fix silly compiler error RLS did not tell me that I hadn’t fixed `babe/lib.rs`, so I missed it. * Fixes made automatically by Cargo
This commit is contained in:
committed by
Gavin Wood
parent
e9a4c80c40
commit
c7d1204ce5
@@ -0,0 +1,67 @@
|
||||
// Copyright 2018-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate 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.
|
||||
|
||||
// Substrate 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 Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Aura (Authority-Round) digests
|
||||
//!
|
||||
//! This implements the digests for AuRa, to allow the private
|
||||
//! `CompatibleDigestItem` trait to appear in public interfaces.
|
||||
use primitives::Pair;
|
||||
use aura_primitives::AURA_ENGINE_ID;
|
||||
use runtime_primitives::generic::DigestItem;
|
||||
use parity_codec::{Encode, Decode};
|
||||
|
||||
type Signature<P> = <P as Pair>::Signature;
|
||||
|
||||
/// A digest item which is usable with aura consensus.
|
||||
pub trait CompatibleDigestItem<T: Pair>: Sized {
|
||||
/// Construct a digest item which contains a signature on the hash.
|
||||
fn aura_seal(signature: Signature<T>) -> Self;
|
||||
|
||||
/// If this item is an Aura seal, return the signature.
|
||||
fn as_aura_seal(&self) -> Option<&Signature<T>>;
|
||||
|
||||
/// Construct a digest item which contains the slot number
|
||||
fn aura_pre_digest(slot_num: u64) -> Self;
|
||||
|
||||
/// If this item is an AuRa pre-digest, return the slot number
|
||||
fn as_aura_pre_digest(&self) -> Option<u64>;
|
||||
}
|
||||
|
||||
impl<P, Hash> CompatibleDigestItem<P> for DigestItem<Hash, P::Public, P::Signature>
|
||||
where P: Pair, P::Signature: Clone + Encode + Decode,
|
||||
{
|
||||
fn aura_seal(signature: Signature<P>) -> Self {
|
||||
DigestItem::Seal(AURA_ENGINE_ID, signature)
|
||||
}
|
||||
|
||||
fn as_aura_seal(&self) -> Option<&Signature<P>> {
|
||||
match self {
|
||||
DigestItem::Seal(AURA_ENGINE_ID, ref sig) => Some(sig),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn aura_pre_digest(slot_num: u64) -> Self {
|
||||
DigestItem::PreRuntime(AURA_ENGINE_ID, slot_num.encode())
|
||||
}
|
||||
|
||||
fn as_aura_pre_digest(&self) -> Option<u64> {
|
||||
match self {
|
||||
DigestItem::PreRuntime(AURA_ENGINE_ID, ref buffer) => Decode::decode(&mut &buffer[..]),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,18 +44,20 @@ use client::{
|
||||
error::Result as CResult,
|
||||
backend::AuxStore,
|
||||
};
|
||||
use aura_primitives::AURA_ENGINE_ID;
|
||||
use runtime_primitives::{generic, generic::BlockId, Justification};
|
||||
|
||||
use runtime_primitives::{generic::{self, BlockId}, Justification};
|
||||
use runtime_primitives::traits::{
|
||||
Block, Header, Digest, DigestItemFor, DigestItem, ProvideRuntimeApi, AuthorityIdFor, Zero,
|
||||
Block, Header, Digest, DigestItemFor, DigestItem, ProvideRuntimeApi, AuthorityIdFor,
|
||||
Zero, Member,
|
||||
};
|
||||
|
||||
use primitives::Pair;
|
||||
use inherents::{InherentDataProviders, InherentData};
|
||||
use authorities::AuthoritiesApi;
|
||||
|
||||
use futures::{Future, IntoFuture, future, stream::Stream};
|
||||
use futures::{Future, IntoFuture, future};
|
||||
use tokio::timer::Timeout;
|
||||
use log::{warn, debug, info, trace};
|
||||
use log::{error, warn, debug, info, trace};
|
||||
|
||||
use srml_aura::{
|
||||
InherentType as AuraInherent, AuraInherentData,
|
||||
@@ -68,24 +70,10 @@ use slots::{CheckedHeader, SlotWorker, SlotInfo, SlotCompatible, slot_now, check
|
||||
pub use aura_primitives::*;
|
||||
pub use consensus_common::{SyncOracle, ExtraVerification};
|
||||
|
||||
mod digest;
|
||||
use digest::CompatibleDigestItem;
|
||||
|
||||
type AuthorityId<P> = <P as Pair>::Public;
|
||||
type Signature<P> = <P as Pair>::Signature;
|
||||
|
||||
/// A handle to the network. This is generally implemented by providing some
|
||||
/// handle to a gossip service or similar.
|
||||
///
|
||||
/// Intended to be a lightweight handle such as an `Arc`.
|
||||
#[deprecated(
|
||||
since = "1.0.1",
|
||||
note = "This is dead code and will be removed in a future release",
|
||||
)]
|
||||
pub trait Network: Clone {
|
||||
/// A stream of input messages for a topic.
|
||||
type In: Stream<Item=Vec<u8>,Error=()>;
|
||||
|
||||
/// Send a message at a specific round out.
|
||||
fn send_message(&self, slot: u64, message: Vec<u8>);
|
||||
}
|
||||
|
||||
/// A slot duration. Create with `get_or_compute`.
|
||||
#[derive(Clone, Copy, Debug, Encode, Decode, Hash, PartialOrd, Ord, PartialEq, Eq)]
|
||||
@@ -122,48 +110,6 @@ fn slot_author<P: Pair>(slot_num: u64, authorities: &[AuthorityId<P>]) -> Option
|
||||
Some(current_author)
|
||||
}
|
||||
|
||||
/// A digest item which is usable with aura consensus.
|
||||
pub trait CompatibleDigestItem<T: Pair>: Sized {
|
||||
/// Construct a digest item which contains a slot number and a signature on the
|
||||
/// hash.
|
||||
fn aura_seal(slot_num: u64, signature: Signature<T>) -> Self;
|
||||
|
||||
/// If this item is an Aura seal, return the slot number and signature.
|
||||
fn as_aura_seal(&self) -> Option<(u64, Signature<T>)>;
|
||||
|
||||
/// Return `true` if this seal type is deprecated. Otherwise, return
|
||||
/// `false`.
|
||||
fn is_deprecated(&self) -> bool;
|
||||
}
|
||||
|
||||
impl<P, Hash> CompatibleDigestItem<P> for generic::DigestItem<Hash, P::Public, P::Signature>
|
||||
where P: Pair, P::Signature: Clone + Encode + Decode,
|
||||
{
|
||||
/// Construct a digest item which is a slot number and a signature on the
|
||||
/// hash.
|
||||
fn aura_seal(slot_number: u64, signature: Signature<P>) -> Self {
|
||||
generic::DigestItem::Consensus(AURA_ENGINE_ID, (slot_number, signature).encode())
|
||||
}
|
||||
|
||||
/// If this item is an Aura seal, return the slot number and signature.
|
||||
#[allow(deprecated)]
|
||||
fn as_aura_seal(&self) -> Option<(u64, Signature<P>)> {
|
||||
match self {
|
||||
generic::DigestItem::Seal(slot, ref sig) => Some((*slot, (*sig).clone())),
|
||||
generic::DigestItem::Consensus(AURA_ENGINE_ID, seal) => Decode::decode(&mut &seal[..]),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
fn is_deprecated(&self) -> bool {
|
||||
match self {
|
||||
generic::DigestItem::Seal(_, _) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
struct AuraSlotCompatible;
|
||||
|
||||
@@ -178,60 +124,8 @@ impl SlotCompatible for AuraSlotCompatible {
|
||||
}
|
||||
}
|
||||
|
||||
/// Start the aura worker in a separate thread.
|
||||
#[deprecated(since = "1.1", note = "Please spawn a thread manually")]
|
||||
pub fn start_aura_thread<B, C, SC, E, I, P, SO, Error, OnExit>(
|
||||
slot_duration: SlotDuration,
|
||||
local_key: Arc<P>,
|
||||
client: Arc<C>,
|
||||
select_chain: SC,
|
||||
block_import: Arc<I>,
|
||||
env: Arc<E>,
|
||||
sync_oracle: SO,
|
||||
on_exit: OnExit,
|
||||
inherent_data_providers: InherentDataProviders,
|
||||
force_authoring: bool,
|
||||
) -> Result<(), consensus_common::Error> where
|
||||
B: Block + 'static,
|
||||
C: ProvideRuntimeApi + ProvideCache<B> + AuxStore + Send + Sync + 'static,
|
||||
C::Api: AuthoritiesApi<B>,
|
||||
SC: SelectChain<B> + Clone + 'static,
|
||||
E: Environment<B, Error=Error> + Send + Sync + 'static,
|
||||
E::Proposer: Proposer<B, Error=Error> + Send + 'static,
|
||||
<<E::Proposer as Proposer<B>>::Create as IntoFuture>::Future: Send + 'static,
|
||||
I: BlockImport<B> + Send + Sync + 'static,
|
||||
Error: From<I::Error> + 'static,
|
||||
P: Pair + Send + Sync + 'static,
|
||||
P::Public: Encode + Decode + Eq + Clone + Debug + Hash + Send + Sync + 'static,
|
||||
P::Signature: Encode,
|
||||
SO: SyncOracle + Send + Sync + Clone + 'static,
|
||||
OnExit: Future<Item=(), Error=()> + Send + 'static,
|
||||
DigestItemFor<B>: CompatibleDigestItem<P> + DigestItem<AuthorityId=AuthorityId<P>> + 'static,
|
||||
Error: ::std::error::Error + Send + From<::consensus_common::Error> + 'static,
|
||||
{
|
||||
let worker = AuraWorker {
|
||||
client: client.clone(),
|
||||
block_import,
|
||||
env,
|
||||
local_key,
|
||||
inherent_data_providers: inherent_data_providers.clone(),
|
||||
sync_oracle: sync_oracle.clone(),
|
||||
force_authoring,
|
||||
};
|
||||
|
||||
#[allow(deprecated)] // The function we are in is also deprecated.
|
||||
slots::start_slot_worker_thread::<_, _, _, _, AuraSlotCompatible, u64, _>(
|
||||
slot_duration.0,
|
||||
select_chain,
|
||||
Arc::new(worker),
|
||||
sync_oracle,
|
||||
on_exit,
|
||||
inherent_data_providers
|
||||
)
|
||||
}
|
||||
|
||||
/// Start the aura worker. The returned future should be run in a tokio runtime.
|
||||
pub fn start_aura<B, C, SC, E, I, P, SO, Error, OnExit>(
|
||||
pub fn start_aura<B, C, SC, E, I, P, SO, Error, OnExit, H>(
|
||||
slot_duration: SlotDuration,
|
||||
local_key: Arc<P>,
|
||||
client: Arc<C>,
|
||||
@@ -243,20 +137,25 @@ pub fn start_aura<B, C, SC, E, I, P, SO, Error, OnExit>(
|
||||
inherent_data_providers: InherentDataProviders,
|
||||
force_authoring: bool,
|
||||
) -> Result<impl Future<Item=(), Error=()>, consensus_common::Error> where
|
||||
B: Block,
|
||||
C: ProvideRuntimeApi + ProvideCache<B> + AuxStore,
|
||||
B: Block<Header=H>,
|
||||
C: ProvideRuntimeApi + ProvideCache<B> + AuxStore + Send + Sync,
|
||||
C::Api: AuthoritiesApi<B>,
|
||||
SC: SelectChain<B> + Clone,
|
||||
E: Environment<B, Error=Error>,
|
||||
SC: SelectChain<B>,
|
||||
generic::DigestItem<B::Hash, P::Public, P::Signature>: DigestItem<Hash=B::Hash>,
|
||||
E::Proposer: Proposer<B, Error=Error>,
|
||||
<<E::Proposer as Proposer<B>>::Create as IntoFuture>::Future: Send + 'static,
|
||||
I: BlockImport<B> + Send + Sync + 'static,
|
||||
P: Pair + Send + Sync + 'static,
|
||||
P::Public: Hash + Eq + Send + Sync + Clone + Debug + Encode + Decode + 'static,
|
||||
P::Signature: Encode,
|
||||
SO: SyncOracle + Send + Sync + Clone,
|
||||
P::Public: Hash + Member + Encode + Decode,
|
||||
P::Signature: Hash + Member + Encode + Decode,
|
||||
DigestItemFor<B>: CompatibleDigestItem<P> + DigestItem<AuthorityId=AuthorityId<P>>,
|
||||
H: Header<
|
||||
Digest=generic::Digest<generic::DigestItem<B::Hash, P::Public, P::Signature>>,
|
||||
Hash=B::Hash,
|
||||
>,
|
||||
E: Environment<B, Error=Error>,
|
||||
I: BlockImport<B> + Send + Sync + 'static,
|
||||
Error: ::std::error::Error + Send + From<::consensus_common::Error> + From<I::Error> + 'static,
|
||||
SO: SyncOracle + Send + Sync + Clone,
|
||||
OnExit: Future<Item=(), Error=()>,
|
||||
{
|
||||
let worker = AuraWorker {
|
||||
@@ -288,18 +187,23 @@ struct AuraWorker<C, E, I, P, SO> {
|
||||
force_authoring: bool,
|
||||
}
|
||||
|
||||
impl<B: Block, C, E, I, P, Error, SO> SlotWorker<B> for AuraWorker<C, E, I, P, SO> where
|
||||
C: ProvideRuntimeApi + ProvideCache<B> + AuxStore,
|
||||
impl<H, B, C, E, I, P, Error, SO> SlotWorker<B> for AuraWorker<C, E, I, P, SO> where
|
||||
B: Block<Header=H>,
|
||||
C: ProvideRuntimeApi + ProvideCache<B> + Sync,
|
||||
C::Api: AuthoritiesApi<B>,
|
||||
E: Environment<B, Error=Error>,
|
||||
E::Proposer: Proposer<B, Error=Error>,
|
||||
<<E::Proposer as Proposer<B>>::Create as IntoFuture>::Future: Send + 'static,
|
||||
H: Header<
|
||||
Digest=generic::Digest<generic::DigestItem<B::Hash, P::Public, P::Signature>>,
|
||||
Hash=B::Hash,
|
||||
>,
|
||||
I: BlockImport<B> + Send + Sync + 'static,
|
||||
P: Pair + Send + Sync + 'static,
|
||||
P::Public: Hash + Eq + Send + Sync + Clone + Debug + Encode + Decode + 'static,
|
||||
P::Signature: Encode,
|
||||
P::Public: Member + Encode + Decode + Hash,
|
||||
P::Signature: Member + Encode + Decode + Hash + Debug,
|
||||
SO: SyncOracle + Send + Clone,
|
||||
DigestItemFor<B>: CompatibleDigestItem<P> + DigestItem<AuthorityId=AuthorityId<P>>,
|
||||
DigestItemFor<B>: CompatibleDigestItem<P> + DigestItem<AuthorityId=AuthorityId<P>, Hash=B::Hash>,
|
||||
Error: ::std::error::Error + Send + From<::consensus_common::Error> + From<I::Error> + 'static,
|
||||
{
|
||||
type OnSlot = Box<Future<Item=(), Error=consensus_common::Error> + Send>;
|
||||
@@ -376,7 +280,15 @@ impl<B: Block, C, E, I, P, Error, SO> SlotWorker<B> for AuraWorker<C, E, I, P, S
|
||||
// deadline our production to approx. the end of the
|
||||
// slot
|
||||
Timeout::new(
|
||||
proposer.propose(slot_info.inherent_data, remaining_duration).into_future(),
|
||||
proposer.propose(
|
||||
slot_info.inherent_data,
|
||||
generic::Digest {
|
||||
logs: vec![
|
||||
<DigestItemFor<B> as CompatibleDigestItem<P>>::aura_pre_digest(slot_num),
|
||||
],
|
||||
},
|
||||
remaining_duration,
|
||||
).into_future(),
|
||||
remaining_duration,
|
||||
)
|
||||
} else {
|
||||
@@ -384,72 +296,98 @@ impl<B: Block, C, E, I, P, Error, SO> SlotWorker<B> for AuraWorker<C, E, I, P, S
|
||||
}
|
||||
};
|
||||
|
||||
Box::new(
|
||||
proposal_work
|
||||
.map(move |b| {
|
||||
// minor hack since we don't have access to the timestamp
|
||||
// that is actually set by the proposer.
|
||||
let slot_after_building = slot_now(slot_duration);
|
||||
if slot_after_building != Some(slot_num) {
|
||||
info!(
|
||||
"Discarding proposal for slot {}; block production took too long",
|
||||
slot_num
|
||||
);
|
||||
telemetry!(CONSENSUS_INFO; "aura.discarding_proposal_took_too_long";
|
||||
"slot" => slot_num
|
||||
);
|
||||
return
|
||||
}
|
||||
Box::new(proposal_work.map(move |b| {
|
||||
// minor hack since we don't have access to the timestamp
|
||||
// that is actually set by the proposer.
|
||||
let slot_after_building = slot_now(slot_duration);
|
||||
if slot_after_building != Some(slot_num) {
|
||||
info!(
|
||||
"Discarding proposal for slot {}; block production took too long",
|
||||
slot_num
|
||||
);
|
||||
telemetry!(CONSENSUS_INFO; "aura.discarding_proposal_took_too_long";
|
||||
"slot" => slot_num
|
||||
);
|
||||
return
|
||||
}
|
||||
|
||||
let (header, body) = b.deconstruct();
|
||||
let header_num = header.number().clone();
|
||||
let pre_hash = header.hash();
|
||||
let parent_hash = header.parent_hash().clone();
|
||||
let (header, body) = b.deconstruct();
|
||||
let pre_digest: Result<u64, String> = find_pre_digest::<B, P>(&header);
|
||||
if let Err(e) = pre_digest {
|
||||
error!(target: "aura", "FATAL ERROR: Invalid pre-digest: {}!", e);
|
||||
return
|
||||
} else {
|
||||
trace!(target: "aura", "Got correct number of seals. Good!")
|
||||
};
|
||||
|
||||
// sign the pre-sealed hash of the block and then
|
||||
// add it to a digest item.
|
||||
let to_sign = (slot_num, pre_hash).encode();
|
||||
let signature = pair.sign(&to_sign[..]);
|
||||
let item = <DigestItemFor<B> as CompatibleDigestItem<P>>::aura_seal(
|
||||
slot_num,
|
||||
signature,
|
||||
);
|
||||
let header_num = header.number().clone();
|
||||
let parent_hash = header.parent_hash().clone();
|
||||
|
||||
let import_block: ImportBlock<B> = ImportBlock {
|
||||
origin: BlockOrigin::Own,
|
||||
header,
|
||||
justification: None,
|
||||
post_digests: vec![item],
|
||||
body: Some(body),
|
||||
finalized: false,
|
||||
auxiliary: Vec::new(),
|
||||
fork_choice: ForkChoiceStrategy::LongestChain,
|
||||
};
|
||||
// sign the pre-sealed hash of the block and then
|
||||
// add it to a digest item.
|
||||
let header_hash = header.hash();
|
||||
let signature = pair.sign(header_hash.as_ref());
|
||||
let signature_digest_item = <DigestItemFor<B> as CompatibleDigestItem<P>>::aura_seal(signature);
|
||||
|
||||
info!("Pre-sealed block for proposal at {}. Hash now {:?}, previously {:?}.",
|
||||
header_num,
|
||||
import_block.post_header().hash(),
|
||||
pre_hash
|
||||
);
|
||||
telemetry!(CONSENSUS_INFO; "aura.pre_sealed_block";
|
||||
"header_num" => ?header_num,
|
||||
"hash_now" => ?import_block.post_header().hash(),
|
||||
"hash_previously" => ?pre_hash
|
||||
);
|
||||
let import_block: ImportBlock<B> = ImportBlock {
|
||||
origin: BlockOrigin::Own,
|
||||
header,
|
||||
justification: None,
|
||||
post_digests: vec![signature_digest_item],
|
||||
body: Some(body),
|
||||
finalized: false,
|
||||
auxiliary: Vec::new(),
|
||||
fork_choice: ForkChoiceStrategy::LongestChain,
|
||||
};
|
||||
|
||||
if let Err(e) = block_import.import_block(import_block, Default::default()) {
|
||||
warn!(target: "aura", "Error with block built on {:?}: {:?}",
|
||||
parent_hash, e);
|
||||
telemetry!(CONSENSUS_WARN; "aura.err_with_block_built_on";
|
||||
"hash" => ?parent_hash, "err" => ?e
|
||||
);
|
||||
}
|
||||
})
|
||||
.map_err(|e| consensus_common::Error::ClientImport(format!("{:?}", e)).into())
|
||||
)
|
||||
info!("Pre-sealed block for proposal at {}. Hash now {:?}, previously {:?}.",
|
||||
header_num,
|
||||
import_block.post_header().hash(),
|
||||
header_hash
|
||||
);
|
||||
telemetry!(CONSENSUS_INFO; "aura.pre_sealed_block";
|
||||
"header_num" => ?header_num,
|
||||
"hash_now" => ?import_block.post_header().hash(),
|
||||
"hash_previously" => ?header_hash
|
||||
);
|
||||
|
||||
if let Err(e) = block_import.import_block(import_block, Default::default()) {
|
||||
warn!(target: "aura", "Error with block built on {:?}: {:?}",
|
||||
parent_hash, e);
|
||||
telemetry!(CONSENSUS_WARN; "aura.err_with_block_built_on";
|
||||
"hash" => ?parent_hash, "err" => ?e
|
||||
);
|
||||
}
|
||||
}).map_err(|e| consensus_common::Error::ClientImport(format!("{:?}", e)).into()))
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! aura_err {
|
||||
($($i: expr),+) => {
|
||||
{ debug!(target: "aura", $($i),+)
|
||||
; format!($($i),+)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn find_pre_digest<B: Block, P: Pair>(header: &B::Header) -> Result<u64, String>
|
||||
where DigestItemFor<B>: CompatibleDigestItem<P>,
|
||||
P::Signature: Decode,
|
||||
P::Public: Encode + Decode + PartialEq + Clone,
|
||||
{
|
||||
let mut pre_digest: Option<u64> = None;
|
||||
for log in header.digest().logs() {
|
||||
trace!(target: "aura", "Checking log {:?}", log);
|
||||
match (log.as_aura_pre_digest(), pre_digest.is_some()) {
|
||||
(Some(_), true) => Err(aura_err!("Multiple AuRa pre-runtime headers, rejecting!"))?,
|
||||
(None, _) => trace!(target: "aura", "Ignoring digest not meant for us"),
|
||||
(s, false) => pre_digest = s,
|
||||
}
|
||||
}
|
||||
pre_digest.ok_or_else(|| aura_err!("No AuRa pre-runtime digest found"))
|
||||
}
|
||||
|
||||
|
||||
/// check a header has been signed by the right key. If the slot is too far in the future, an error will be returned.
|
||||
/// if it's successful, returns the pre-header and the digest item containing the seal.
|
||||
///
|
||||
@@ -462,30 +400,25 @@ fn check_header<C, B: Block, P: Pair>(
|
||||
mut header: B::Header,
|
||||
hash: B::Hash,
|
||||
authorities: &[AuthorityId<P>],
|
||||
allow_old_seals: bool,
|
||||
) -> Result<CheckedHeader<B::Header, DigestItemFor<B>>, String>
|
||||
where DigestItemFor<B>: CompatibleDigestItem<P>,
|
||||
P::Signature: Decode,
|
||||
C: client::backend::AuxStore,
|
||||
P::Public: AsRef<P::Public> + Encode + Decode + PartialEq + Clone,
|
||||
) -> Result<CheckedHeader<B::Header, (u64, DigestItemFor<B>)>, String> where
|
||||
DigestItemFor<B>: CompatibleDigestItem<P>,
|
||||
P::Signature: Decode,
|
||||
C: client::backend::AuxStore,
|
||||
P::Public: AsRef<P::Public> + Encode + Decode + PartialEq + Clone,
|
||||
{
|
||||
let digest_item = match header.digest_mut().pop() {
|
||||
let seal = match header.digest_mut().pop() {
|
||||
Some(x) => x,
|
||||
None => return Err(format!("Header {:?} is unsealed", hash)),
|
||||
};
|
||||
|
||||
if !allow_old_seals && digest_item.is_deprecated() {
|
||||
debug!(target: "aura", "Header {:?} uses old seal format, rejecting", hash);
|
||||
return Err(format!("Header {:?} uses old seal format, rejecting", hash))
|
||||
}
|
||||
|
||||
let (slot_num, sig) = digest_item.as_aura_seal().ok_or_else(|| {
|
||||
debug!(target: "aura", "Header {:?} is unsealed", hash);
|
||||
format!("Header {:?} is unsealed", hash)
|
||||
let sig = seal.as_aura_seal().ok_or_else(|| {
|
||||
aura_err!("Header {:?} has a bad seal", hash)
|
||||
})?;
|
||||
|
||||
let slot_num = find_pre_digest::<B, _>(&header)?;
|
||||
|
||||
if slot_num > slot_now {
|
||||
header.digest_mut().push(digest_item);
|
||||
header.digest_mut().push(seal);
|
||||
Ok(CheckedHeader::Deferred(header, slot_num))
|
||||
} else {
|
||||
// check the signature is valid under the expected authority and
|
||||
@@ -496,16 +429,14 @@ fn check_header<C, B: Block, P: Pair>(
|
||||
};
|
||||
|
||||
let pre_hash = header.hash();
|
||||
let to_sign = (slot_num, pre_hash).encode();
|
||||
let public = expected_author;
|
||||
|
||||
if P::verify(&sig, &to_sign[..], public) {
|
||||
if P::verify(&sig, pre_hash.as_ref(), expected_author) {
|
||||
match check_equivocation::<_, _, <P as Pair>::Public>(
|
||||
client,
|
||||
slot_now,
|
||||
slot_num,
|
||||
header.clone(),
|
||||
public.clone(),
|
||||
expected_author.clone(),
|
||||
) {
|
||||
Ok(Some(equivocation_proof)) => {
|
||||
let log_str = format!(
|
||||
@@ -517,7 +448,7 @@ fn check_header<C, B: Block, P: Pair>(
|
||||
info!("{}", log_str);
|
||||
Err(log_str)
|
||||
},
|
||||
Ok(None) => Ok(CheckedHeader::Checked(header, digest_item)),
|
||||
Ok(None) => Ok(CheckedHeader::Checked(header, (slot_num, seal))),
|
||||
Err(e) => Err(e.to_string()),
|
||||
}
|
||||
} else {
|
||||
@@ -532,7 +463,6 @@ pub struct AuraVerifier<C, E, P> {
|
||||
extra: E,
|
||||
phantom: PhantomData<P>,
|
||||
inherent_data_providers: inherents::InherentDataProviders,
|
||||
allow_old_seals: bool,
|
||||
}
|
||||
|
||||
impl<C, E, P> AuraVerifier<C, E, P>
|
||||
@@ -631,20 +561,17 @@ impl<B: Block, C, E, P> Verifier<B> for AuraVerifier<C, E, P> where
|
||||
);
|
||||
|
||||
// we add one to allow for some small drift.
|
||||
// FIXME #1019 in the future, alter this queue to allow deferring of headers
|
||||
// FIXME #1019 in the future, alter this queue to allow deferring of
|
||||
// headers
|
||||
let checked_header = check_header::<C, B, P>(
|
||||
&self.client,
|
||||
slot_now + 1,
|
||||
header,
|
||||
hash,
|
||||
&authorities[..],
|
||||
self.allow_old_seals,
|
||||
)?;
|
||||
match checked_header {
|
||||
CheckedHeader::Checked(pre_header, seal) => {
|
||||
let (slot_num, _) = seal.as_aura_seal()
|
||||
.expect("check_header always returns a seal digest item; qed");
|
||||
|
||||
CheckedHeader::Checked(pre_header, (slot_num, seal)) => {
|
||||
// if the body is passed through, we need to use the runtime
|
||||
// to check that the internally-set timestamp in the inherents
|
||||
// actually matches the slot set in the seal.
|
||||
@@ -813,52 +740,6 @@ pub fn import_queue<B, C, E, P>(
|
||||
extra,
|
||||
inherent_data_providers,
|
||||
phantom: PhantomData,
|
||||
allow_old_seals: false,
|
||||
}
|
||||
);
|
||||
Ok(BasicQueue::new(
|
||||
verifier,
|
||||
block_import,
|
||||
justification_import,
|
||||
finality_proof_import,
|
||||
finality_proof_request_builder,
|
||||
))
|
||||
}
|
||||
|
||||
/// Start an import queue for the Aura consensus algorithm with backwards compatibility.
|
||||
#[deprecated(
|
||||
since = "1.0.1",
|
||||
note = "should not be used unless backwards compatibility with an older chain is needed.",
|
||||
)]
|
||||
pub fn import_queue_accept_old_seals<B, C, E, P>(
|
||||
slot_duration: SlotDuration,
|
||||
block_import: SharedBlockImport<B>,
|
||||
justification_import: Option<SharedJustificationImport<B>>,
|
||||
finality_proof_import: Option<SharedFinalityProofImport<B>>,
|
||||
finality_proof_request_builder: Option<SharedFinalityProofRequestBuilder<B>>,
|
||||
client: Arc<C>,
|
||||
extra: E,
|
||||
inherent_data_providers: InherentDataProviders,
|
||||
) -> Result<AuraImportQueue<B>, consensus_common::Error> where
|
||||
B: Block,
|
||||
C: 'static + ProvideRuntimeApi + ProvideCache<B> + Send + Sync + AuxStore,
|
||||
C::Api: BlockBuilderApi<B> + AuthoritiesApi<B>,
|
||||
DigestItemFor<B>: CompatibleDigestItem<P> + DigestItem<AuthorityId=AuthorityId<P>>,
|
||||
E: 'static + ExtraVerification<B>,
|
||||
P: Pair + Send + Sync + 'static,
|
||||
P::Public: Clone + Eq + Send + Sync + Hash + Debug + Encode + Decode + AsRef<P::Public>,
|
||||
P::Signature: Encode + Decode,
|
||||
{
|
||||
register_aura_inherent_data_provider(&inherent_data_providers, slot_duration.get())?;
|
||||
initialize_authorities_cache(&*client)?;
|
||||
|
||||
let verifier = Arc::new(
|
||||
AuraVerifier {
|
||||
client: client.clone(),
|
||||
extra,
|
||||
inherent_data_providers,
|
||||
phantom: PhantomData,
|
||||
allow_old_seals: true,
|
||||
}
|
||||
);
|
||||
Ok(BasicQueue::new(
|
||||
@@ -873,10 +754,11 @@ pub fn import_queue_accept_old_seals<B, C, E, P>(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use futures::stream::Stream as _;
|
||||
use consensus_common::NoNetwork as DummyOracle;
|
||||
use network::test::*;
|
||||
use network::test::{Block as TestBlock, PeersClient, PeersFullClient};
|
||||
use runtime_primitives::traits::Block as BlockT;
|
||||
use runtime_primitives::traits::{Block as BlockT, DigestFor};
|
||||
use network::config::ProtocolConfig;
|
||||
use parking_lot::Mutex;
|
||||
use tokio::runtime::current_thread;
|
||||
@@ -910,8 +792,13 @@ mod tests {
|
||||
type Error = Error;
|
||||
type Create = Result<TestBlock, Error>;
|
||||
|
||||
fn propose(&self, _: InherentData, _: Duration) -> Result<TestBlock, Error> {
|
||||
self.1.new_block().unwrap().bake().map_err(|e| e.into())
|
||||
fn propose(
|
||||
&self,
|
||||
_: InherentData,
|
||||
digests: DigestFor<TestBlock>,
|
||||
_: Duration,
|
||||
) -> Result<TestBlock, Error> {
|
||||
self.1.new_block(digests).unwrap().bake().map_err(|e| e.into())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -955,7 +842,6 @@ mod tests {
|
||||
extra: NothingExtra,
|
||||
inherent_data_providers,
|
||||
phantom: Default::default(),
|
||||
allow_old_seals: false,
|
||||
})
|
||||
},
|
||||
PeersClient::Light(_) => unreachable!("No (yet) tests for light client + Aura"),
|
||||
@@ -995,19 +881,18 @@ mod tests {
|
||||
extrinsics_root: Default::default(),
|
||||
digest: DigestTest { logs: vec![], },
|
||||
};
|
||||
let item = <DigestItemFor<TestBlock> as CompatibleDigestItem<sr25519::Pair>>::aura_pre_digest(slot_num);
|
||||
header.digest_mut().push(item);
|
||||
let header_hash: H256 = header.hash();
|
||||
let to_sign = (slot_num, header_hash).encode();
|
||||
let signature = pair.sign(&to_sign[..]);
|
||||
let signature = pair.sign(&header_hash[..]);
|
||||
|
||||
let item = <generic::DigestItem<_, _, _> as CompatibleDigestItem<sr25519::Pair>>::aura_seal(
|
||||
slot_num,
|
||||
signature,
|
||||
);
|
||||
let item = CompatibleDigestItem::<sr25519::Pair>::aura_seal(signature);
|
||||
header.digest_mut().push(item);
|
||||
(header, header_hash)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(deprecated)]
|
||||
fn authoring_blocks() {
|
||||
let _ = ::env_logger::try_init();
|
||||
let mut net = AuraTestNet::new(3);
|
||||
@@ -1046,7 +931,7 @@ mod tests {
|
||||
&inherent_data_providers, slot_duration.get()
|
||||
).expect("Registers aura inherent data provider");
|
||||
|
||||
let aura = start_aura::<_, _, _, _, _, sr25519::Pair, _, _, _>(
|
||||
let aura = start_aura::<_, _, _, _, _, sr25519::Pair, _, _, _, _>(
|
||||
slot_duration,
|
||||
Arc::new(key.clone().into()),
|
||||
client.clone(),
|
||||
@@ -1111,22 +996,22 @@ mod tests {
|
||||
let c = Arc::new(client);
|
||||
|
||||
// It's ok to sign same headers.
|
||||
assert!(check_header::<_, B, P>(&c, 2, header1.clone(), header1_hash, &authorities, false).is_ok());
|
||||
assert!(check_header::<_, B, P>(&c, 3, header1, header1_hash, &authorities, false).is_ok());
|
||||
check_header::<_, B, P>(&c, 2, header1.clone(), header1_hash, &authorities).unwrap();
|
||||
assert!(check_header::<_, B, P>(&c, 3, header1, header1_hash, &authorities).is_ok());
|
||||
|
||||
// But not two different headers at the same slot.
|
||||
assert!(check_header::<_, B, P>(&c, 4, header2, header2_hash, &authorities, false).is_err());
|
||||
assert!(check_header::<_, B, P>(&c, 4, header2, header2_hash, &authorities).is_err());
|
||||
|
||||
// Different slot is ok.
|
||||
assert!(check_header::<_, B, P>(&c, 5, header3, header3_hash, &authorities, false).is_ok());
|
||||
assert!(check_header::<_, B, P>(&c, 5, header3, header3_hash, &authorities).is_ok());
|
||||
|
||||
// Here we trigger pruning and save header 4.
|
||||
assert!(check_header::<_, B, P>(&c, PRUNING_BOUND + 2, header4, header4_hash, &authorities, false).is_ok());
|
||||
assert!(check_header::<_, B, P>(&c, PRUNING_BOUND + 2, header4, header4_hash, &authorities).is_ok());
|
||||
|
||||
// This fails because header 5 is an equivocation of header 4.
|
||||
assert!(check_header::<_, B, P>(&c, PRUNING_BOUND + 3, header5, header5_hash, &authorities, false).is_err());
|
||||
assert!(check_header::<_, B, P>(&c, PRUNING_BOUND + 3, header5, header5_hash, &authorities).is_err());
|
||||
|
||||
// This is ok because we pruned the corresponding header. Shows that we are pruning.
|
||||
assert!(check_header::<_, B, P>(&c, PRUNING_BOUND + 4, header6, header6_hash, &authorities, false).is_ok());
|
||||
assert!(check_header::<_, B, P>(&c, PRUNING_BOUND + 4, header6, header6_hash, &authorities).is_ok());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user