// This file is part of Substrate.
// Copyright (C) 2018-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
=
::Public;
/// Slot duration type for Aura.
pub type SlotDuration = sc_consensus_slots::SlotDuration ]) -> Option<&AuthorityId > {
if authorities.is_empty() { return None }
let idx = slot_num % (authorities.len() as u64);
assert!(
idx <= usize::max_value() as u64,
"It is impossible to have a vector with length beyond the address space; qed",
);
let current_author = authorities.get(idx as usize)
.expect("authorities not empty; index constrained to list length;\
this is a valid index; qed");
Some(current_author)
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
struct AuraSlotCompatible;
impl SlotCompatible for AuraSlotCompatible {
fn extract_timestamp_and_slot(
&self,
data: &InherentData
) -> Result<(TimestampInherent, AuraInherent, std::time::Duration), sp_consensus::Error> {
data.timestamp_inherent_data()
.and_then(|t| data.aura_inherent_data().map(|a| (t, a)))
.map_err(Into::into)
.map_err(sp_consensus::Error::InherentData)
.map(|(x, y)| (x, y, Default::default()))
}
}
/// Start the aura worker. The returned future should be run in a futures executor.
pub fn start_aura(
slot_duration: SlotDuration,
client: Arc ,
};
register_aura_inherent_data_provider(
&inherent_data_providers,
slot_duration.slot_duration()
)?;
Ok(sc_consensus_slots::start_slot_worker::<_, _, _, _, _, AuraSlotCompatible, _>(
slot_duration,
select_chain,
worker,
sync_oracle,
inherent_data_providers,
AuraSlotCompatible,
can_author_with,
))
}
struct AuraWorker ,
}
impl sc_consensus_slots::SimpleSlotWorker for AuraWorker (slot_number, epoch_data);
expected_author.and_then(|p| {
self.keystore.read()
.key_pair_by_type:: (&p, sp_application_crypto::key_types::AURA).ok()
}).and_then(|p| {
Some(p.public())
})
}
fn pre_digest_data(
&self,
slot_number: u64,
_claim: &Self::Claim,
) -> Vec >::aura_pre_digest(slot_number),
]
}
fn block_import_params(&self) -> Box >::aura_seal(signature);
let mut import_block = BlockImportParams::new(BlockOrigin::Own, header);
import_block.post_digests.push(signature_digest_item);
import_block.body = Some(body);
import_block.storage_changes = Some(storage_changes);
import_block.fork_choice = Some(ForkChoiceStrategy::LongestChain);
Ok(import_block)
})
}
fn force_authoring(&self) -> bool {
self.force_authoring
}
fn sync_oracle(&mut self) -> &mut Self::SyncOracle {
&mut self.sync_oracle
}
fn proposer(&mut self, block: &B::Header) -> Self::CreateProposer {
Box::pin(self.env.init(block).map_err(|e| {
sp_consensus::Error::ClientImport(format!("{:?}", e)).into()
}))
}
fn proposing_remaining_duration(
&self,
head: &B::Header,
slot_info: &SlotInfo,
) -> Option ,
P::Signature: Decode,
P::Public: Encode + Decode + PartialEq + Clone,
{
if header.number().is_zero() {
return Ok(0);
}
let mut pre_digest: Option ],
) -> Result ,
P::Signature: Decode,
C: sc_client_api::backend::AuxStore,
P::Public: Encode + Decode + PartialEq + Clone,
{
let seal = match header.digest_mut().pop() {
Some(x) => x,
None => return Err(Error::HeaderUnsealed(hash)),
};
let sig = seal.as_aura_seal().ok_or_else(|| {
aura_err(Error::HeaderBadSeal(hash))
})?;
let slot_num = find_pre_digest::(&header)?;
if slot_num > slot_now {
header.digest_mut().push(seal);
Ok(CheckedHeader::Deferred(header, slot_num))
} else {
// check the signature is valid under the expected authority and
// chain state.
let expected_author = match slot_author:: (slot_num, &authorities) {
None => return Err(Error::SlotAuthorNotFound),
Some(author) => author,
};
let pre_hash = header.hash();
if P::verify(&sig, pre_hash.as_ref(), expected_author) {
if let Some(equivocation_proof) = check_equivocation(
client,
slot_now,
slot_num,
&header,
expected_author,
).map_err(Error::Client)? {
info!(
"Slot author is equivocating at slot {} with headers {:?} and {:?}",
slot_num,
equivocation_proof.first_header.hash(),
equivocation_proof.second_header.hash(),
);
}
Ok(CheckedHeader::Checked(header, (slot_num, seal)))
} else {
Err(Error::BadSignature(hash))
}
}
}
/// A verifier for Aura blocks.
pub struct AuraVerifier ,
inherent_data_providers: sp_inherents::InherentDataProviders,
can_author_with: CAW,
}
impl ,
P: Pair + Send + Sync + 'static,
P::Public: Send + Sync + Hash + Eq + Clone + Decode + Encode + Debug + 'static,
P::Signature: Encode + Decode,
CAW: CanAuthorWith + Send + Sync + 'static,
{
fn verify(
&mut self,
origin: BlockOrigin,
header: B::Header,
justification: Option ,
P: Pair + Send + Sync + 'static,
P::Public: Clone + Eq + Send + Sync + Hash + Debug + Encode + Decode,
P::Signature: Encode + Decode,
S: sp_core::traits::SpawnNamed,
CAW: CanAuthorWith + Send + Sync + 'static,
{
register_aura_inherent_data_provider(&inherent_data_providers, slot_duration.get())?;
initialize_authorities_cache(&*client)?;
let verifier = AuraVerifier {
client,
inherent_data_providers,
phantom: PhantomData,
can_author_with,
};
Ok(BasicQueue::new(
verifier,
Box::new(block_import),
justification_import,
finality_proof_import,
spawner,
registry,
))
}
#[cfg(test)]
mod tests {
use super::*;
use sp_consensus::{NoNetwork as DummyOracle, Proposal, RecordProof, AlwaysCanAuthor};
use sc_network_test::{Block as TestBlock, *};
use sp_runtime::traits::{Block as BlockT, DigestFor};
use sc_network::config::ProtocolConfig;
use parking_lot::Mutex;
use sp_keyring::sr25519::Keyring;
use sc_client_api::BlockchainEvents;
use sp_consensus_aura::sr25519::AuthorityPair;
use sc_consensus_slots::SimpleSlotWorker;
use std::task::Poll;
use sc_block_builder::BlockBuilderProvider;
use sp_runtime::traits::Header as _;
use substrate_test_runtime_client::runtime::{Header, H256};
type Error = sp_blockchain::Error;
type TestClient = substrate_test_runtime_client::client::Client<
substrate_test_runtime_client::Backend,
substrate_test_runtime_client::Executor,
TestBlock,
substrate_test_runtime_client::runtime::RuntimeApi
>;
struct DummyFactory(Arc