#![deny(warnings)]
#![forbid(unsafe_code, missing_docs, unused_variables, unused_imports)]
pub mod digests;
pub mod inherents;
use crate::runtime::{traits::Header, ConsensusEngineId};
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
use codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen};
use scale_info::TypeInfo;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use digests::{NextConfigDescriptor, NextEpochDescriptor};
pub use crate::core::sr25519::vrf::{
VrfInput, VrfPreOutput, VrfProof, VrfSignData, VrfSignature, VrfTranscript,
};
pub const KEY_TYPE: crate::core::crypto::KeyTypeId = crate::application_crypto::key_types::BABE;
mod app {
use crate::application_crypto::{key_types::BABE, sr25519};
crate::app_crypto!(sr25519, BABE);
}
pub const RANDOMNESS_VRF_CONTEXT: &[u8] = b"BabeVRFInOutContext";
pub const RANDOMNESS_LENGTH: usize = 32;
pub type Randomness = [u8; RANDOMNESS_LENGTH];
#[cfg(feature = "std")]
pub type AuthorityPair = app::Pair;
pub type AuthoritySignature = app::Signature;
pub type AuthorityId = app::Public;
pub const BABE_ENGINE_ID: ConsensusEngineId = *b"BABE";
pub const PUBLIC_KEY_LENGTH: usize = 32;
pub const MEDIAN_ALGORITHM_CARDINALITY: usize = 1200;
pub type AuthorityIndex = u32;
pub use crate::consensus::slots::{Slot, SlotDuration};
pub type EquivocationProof<H> = crate::consensus::slots::EquivocationProof<H, AuthorityId>;
pub type BabeAuthorityWeight = u64;
pub type BabeBlockWeight = u32;
pub fn make_vrf_transcript(randomness: &Randomness, slot: Slot, epoch: u64) -> VrfInput {
VrfInput::new(
&BABE_ENGINE_ID,
&[
(b"slot number", &slot.to_le_bytes()),
(b"current epoch", &epoch.to_le_bytes()),
(b"chain randomness", randomness),
],
)
}
pub fn make_vrf_sign_data(randomness: &Randomness, slot: Slot, epoch: u64) -> VrfSignData {
make_vrf_transcript(randomness, slot, epoch).into()
}
#[derive(Decode, Encode, Clone, PartialEq, Eq)]
pub enum ConsensusLog {
#[codec(index = 1)]
NextEpochData(NextEpochDescriptor),
#[codec(index = 2)]
OnDisabled(AuthorityIndex),
#[codec(index = 3)]
NextConfigData(NextConfigDescriptor),
}
#[derive(Clone, PartialEq, Eq, Encode, Decode, Debug, TypeInfo)]
pub struct BabeConfigurationV1 {
pub slot_duration: u64,
pub epoch_length: u64,
pub c: (u64, u64),
pub authorities: Vec<(AuthorityId, BabeAuthorityWeight)>,
pub randomness: Randomness,
pub secondary_slots: bool,
}
impl From<BabeConfigurationV1> for BabeConfiguration {
fn from(v1: BabeConfigurationV1) -> Self {
Self {
slot_duration: v1.slot_duration,
epoch_length: v1.epoch_length,
c: v1.c,
authorities: v1.authorities,
randomness: v1.randomness,
allowed_slots: if v1.secondary_slots {
AllowedSlots::PrimaryAndSecondaryPlainSlots
} else {
AllowedSlots::PrimarySlots
},
}
}
}
#[derive(Clone, PartialEq, Eq, Encode, Decode, Debug, TypeInfo)]
pub struct BabeConfiguration {
pub slot_duration: u64,
pub epoch_length: u64,
pub c: (u64, u64),
pub authorities: Vec<(AuthorityId, BabeAuthorityWeight)>,
pub randomness: Randomness,
pub allowed_slots: AllowedSlots,
}
impl BabeConfiguration {
pub fn slot_duration(&self) -> SlotDuration {
SlotDuration::from_millis(self.slot_duration)
}
}
#[derive(
Clone,
Copy,
PartialEq,
Eq,
Encode,
Decode,
DecodeWithMemTracking,
Debug,
MaxEncodedLen,
TypeInfo,
)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum AllowedSlots {
PrimarySlots,
PrimaryAndSecondaryPlainSlots,
PrimaryAndSecondaryVRFSlots,
}
impl AllowedSlots {
pub fn is_secondary_plain_slots_allowed(&self) -> bool {
*self == Self::PrimaryAndSecondaryPlainSlots
}
pub fn is_secondary_vrf_slots_allowed(&self) -> bool {
*self == Self::PrimaryAndSecondaryVRFSlots
}
}
#[derive(Clone, PartialEq, Eq, Encode, Decode, Debug, MaxEncodedLen, TypeInfo)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct BabeEpochConfiguration {
pub c: (u64, u64),
pub allowed_slots: AllowedSlots,
}
impl Default for BabeEpochConfiguration {
fn default() -> Self {
Self { c: (1, 4), allowed_slots: AllowedSlots::PrimaryAndSecondaryVRFSlots }
}
}
pub fn check_equivocation_proof<H>(proof: EquivocationProof<H>) -> bool
where
H: Header,
{
use crate::application_crypto::RuntimeAppPublic;
use digests::*;
let find_pre_digest =
|header: &H| header.digest().logs().iter().find_map(|log| log.as_babe_pre_digest());
let verify_seal_signature = |mut header: H, offender: &AuthorityId| {
let seal = header.digest_mut().pop()?.as_babe_seal()?;
let pre_hash = header.hash();
if !offender.verify(&pre_hash.as_ref(), &seal) {
return None;
}
Some(())
};
let verify_proof = || {
if proof.first_header.hash() == proof.second_header.hash() {
return None;
}
let first_pre_digest = find_pre_digest(&proof.first_header)?;
let second_pre_digest = find_pre_digest(&proof.second_header)?;
if proof.slot != first_pre_digest.slot()
|| first_pre_digest.slot() != second_pre_digest.slot()
{
return None;
}
if first_pre_digest.authority_index() != second_pre_digest.authority_index() {
return None;
}
verify_seal_signature(proof.first_header, &proof.offender)?;
verify_seal_signature(proof.second_header, &proof.offender)?;
Some(())
};
verify_proof().is_some()
}
#[derive(Decode, Encode, PartialEq, TypeInfo)]
pub struct OpaqueKeyOwnershipProof(Vec<u8>);
impl OpaqueKeyOwnershipProof {
pub fn new(inner: Vec<u8>) -> OpaqueKeyOwnershipProof {
OpaqueKeyOwnershipProof(inner)
}
pub fn decode<T: Decode>(self) -> Option<T> {
Decode::decode(&mut &self.0[..]).ok()
}
}
#[derive(Decode, Encode, PartialEq, Eq, Clone, Debug, TypeInfo)]
pub struct Epoch {
pub epoch_index: u64,
pub start_slot: Slot,
pub duration: u64,
pub authorities: Vec<(AuthorityId, BabeAuthorityWeight)>,
pub randomness: Randomness,
pub config: BabeEpochConfiguration,
}
pub fn epoch_index(slot: Slot, genesis_slot: Slot, epoch_duration: u64) -> u64 {
*slot.saturating_sub(genesis_slot) / epoch_duration
}
pub fn epoch_start_slot(epoch_index: u64, genesis_slot: Slot, epoch_duration: u64) -> Slot {
const PROOF: &str = "slot number is u64; it should relate in some way to wall clock time; \
if u64 is not enough we should crash for safety; qed.";
epoch_index
.checked_mul(epoch_duration)
.and_then(|slot| slot.checked_add(*genesis_slot))
.expect(PROOF)
.into()
}
crate::api::decl_runtime_apis! {
#[api_version(2)]
pub trait BabeApi {
fn configuration() -> BabeConfiguration;
#[changed_in(2)]
fn configuration() -> BabeConfigurationV1;
fn current_epoch_start() -> Slot;
fn current_epoch() -> Epoch;
fn next_epoch() -> Epoch;
fn generate_key_ownership_proof(
slot: Slot,
authority_id: AuthorityId,
) -> Option<OpaqueKeyOwnershipProof>;
fn submit_report_equivocation_unsigned_extrinsic(
equivocation_proof: EquivocationProof<Block::Header>,
key_owner_proof: OpaqueKeyOwnershipProof,
) -> Option<()>;
}
}