// Copyright 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 . //! Consensus extension module for BABE consensus. #![cfg_attr(not(feature = "std"), no_std)] #![forbid(unused_must_use, unsafe_code, unused_variables, dead_code)] pub use timestamp; use rstd::{result, prelude::*}; use srml_support::{decl_storage, decl_module, StorageValue, traits::FindAuthor, traits::Get}; use timestamp::{OnTimestampSet, Trait}; use primitives::{generic::DigestItem, traits::{SaturatedConversion, Saturating, RandomnessBeacon}}; use primitives::ConsensusEngineId; #[cfg(feature = "std")] use timestamp::TimestampInherentData; use parity_codec::{Encode, Decode}; use inherents::{RuntimeString, InherentIdentifier, InherentData, ProvideInherent, MakeFatalError}; #[cfg(feature = "std")] use inherents::{InherentDataProviders, ProvideInherentData}; use babe_primitives::{BABE_ENGINE_ID, ConsensusLog}; pub use babe_primitives::{AuthorityId, VRF_OUTPUT_LENGTH, VRF_PROOF_LENGTH, PUBLIC_KEY_LENGTH}; /// The BABE inherent identifier. pub const INHERENT_IDENTIFIER: InherentIdentifier = *b"babeslot"; /// The type of the BABE inherent. pub type InherentType = u64; /// Auxiliary trait to extract BABE inherent data. pub trait BabeInherentData { /// Get BABE inherent data. fn babe_inherent_data(&self) -> result::Result; /// Replace BABE inherent data. fn babe_replace_inherent_data(&mut self, new: InherentType); } impl BabeInherentData for InherentData { fn babe_inherent_data(&self) -> result::Result { self.get_data(&INHERENT_IDENTIFIER) .and_then(|r| r.ok_or_else(|| "BABE inherent data not found".into())) } fn babe_replace_inherent_data(&mut self, new: InherentType) { self.replace_data(INHERENT_IDENTIFIER, &new); } } /// Provides the slot duration inherent data for BABE. #[cfg(feature = "std")] pub struct InherentDataProvider { slot_duration: u64, } #[cfg(feature = "std")] impl InherentDataProvider { /// Constructs `Self` pub fn new(slot_duration: u64) -> Self { Self { slot_duration } } } #[cfg(feature = "std")] impl ProvideInherentData for InherentDataProvider { fn on_register( &self, providers: &InherentDataProviders, ) -> result::Result<(), RuntimeString> { if !providers.has_provider(×tamp::INHERENT_IDENTIFIER) { // Add the timestamp inherent data provider, as we require it. providers.register_provider(timestamp::InherentDataProvider) } else { Ok(()) } } fn inherent_identifier(&self) -> &'static inherents::InherentIdentifier { &INHERENT_IDENTIFIER } fn provide_inherent_data( &self, inherent_data: &mut InherentData, ) -> result::Result<(), RuntimeString> { let timestamp = inherent_data.timestamp_inherent_data()?; let slot_num = timestamp / self.slot_duration; inherent_data.put_data(INHERENT_IDENTIFIER, &slot_num) } fn error_to_string(&self, error: &[u8]) -> Option { RuntimeString::decode(&mut &error[..]).map(Into::into) } } /// The length of the BABE randomness pub const RANDOMNESS_LENGTH: usize = 32; decl_storage! { trait Store for Module as Babe { /// The last timestamp. LastTimestamp get(last): T::Moment; /// The current authorities set. Authorities get(authorities): Vec; /// The epoch randomness. /// /// # Security /// /// This MUST NOT be used for gambling, as it can be influenced by a /// malicious validator in the short term. It MAY be used in many /// cryptographic protocols, however, so long as one remembers that this /// (like everything else on-chain) it is public. For example, it can be /// used where a number is needed that cannot have been chosen by an /// adversary, for purposes such as public-coin zero-knowledge proofs. EpochRandomness get(epoch_randomness): [u8; VRF_OUTPUT_LENGTH]; /// The randomness under construction UnderConstruction: [u8; VRF_OUTPUT_LENGTH]; /// The randomness for the next epoch NextEpochRandomness: [u8; VRF_OUTPUT_LENGTH]; /// The current epoch EpochIndex get(epoch_index): u64; } } decl_module! { /// The BABE SRML module pub struct Module for enum Call where origin: T::Origin { /// Initialization fn on_initialize() { for i in Self::get_inherent_digests() .logs .iter() .filter_map(|s| s.as_pre_runtime()) .filter_map(|(id, mut data)| if id == BABE_ENGINE_ID { <[u8; VRF_OUTPUT_LENGTH]>::decode(&mut data) } else { None }) { Self::deposit_vrf_output(&i); } } } } impl RandomnessBeacon for Module { fn random() -> [u8; VRF_OUTPUT_LENGTH] { Self::epoch_randomness() } } /// A BABE public key pub type BabeKey = [u8; PUBLIC_KEY_LENGTH]; impl FindAuthor for Module { fn find_author<'a, I>(digests: I) -> Option where I: 'a + IntoIterator { for (id, mut data) in digests.into_iter() { if id == BABE_ENGINE_ID { let (_, _, i): ( [u8; VRF_OUTPUT_LENGTH], [u8; VRF_PROOF_LENGTH], u64, ) = Decode::decode(&mut data)?; return Some(i) } } return None } } impl Module { /// Determine the BABE slot duration based on the Timestamp module configuration. pub fn slot_duration() -> T::Moment { // we double the minimum block-period so each author can always propose within // the majority of their slot. ::MinimumPeriod::get().saturating_mul(2.into()) } fn change_authorities(new: Vec) { Authorities::put(&new); let log: DigestItem = DigestItem::Consensus(BABE_ENGINE_ID, new.encode()); >::deposit_log(log.into()); } fn deposit_vrf_output(vrf_output: &[u8; VRF_OUTPUT_LENGTH]) { UnderConstruction::mutate(|z| z.iter_mut().zip(vrf_output).for_each(|(x, y)| *x^=y)) } fn get_inherent_digests() -> system::DigestOf { >::digest() } } impl OnTimestampSet for Module { fn on_timestamp_set(_moment: T::Moment) { } } impl session::OneSessionHandler for Module { type Key = AuthorityId; fn on_new_session<'a, I: 'a>(changed: bool, validators: I) where I: Iterator { // instant changes if changed { let next_authorities = validators.map(|(_, k)| k).collect::>(); let last_authorities = >::authorities(); if next_authorities != last_authorities { Self::change_authorities(next_authorities); } } let rho = UnderConstruction::get(); UnderConstruction::put([0; 32]); let last_epoch_randomness = EpochRandomness::get(); let epoch_index = EpochIndex::get() .checked_add(1) .expect("epoch indices will never reach 2^64 before the death of the universe; qed"); EpochIndex::put(epoch_index); EpochRandomness::put(NextEpochRandomness::get()); let mut s = [0; 72]; s[..32].copy_from_slice(&last_epoch_randomness); s[32..40].copy_from_slice(&epoch_index.to_le_bytes()); s[40..].copy_from_slice(&rho); NextEpochRandomness::put(runtime_io::blake2_256(&s)) } fn on_disabled(i: usize) { let log: DigestItem = DigestItem::Consensus( BABE_ENGINE_ID, ConsensusLog::OnDisabled(i as u64).encode(), ); >::deposit_log(log.into()); } } impl ProvideInherent for Module { type Call = timestamp::Call; type Error = MakeFatalError; const INHERENT_IDENTIFIER: InherentIdentifier = INHERENT_IDENTIFIER; fn create_inherent(_: &InherentData) -> Option { None } fn check_inherent(call: &Self::Call, data: &InherentData) -> result::Result<(), Self::Error> { let timestamp = match call { timestamp::Call::set(ref timestamp) => timestamp.clone(), _ => return Ok(()), }; let timestamp_based_slot = (timestamp / Self::slot_duration()).saturated_into::(); let seal_slot = data.babe_inherent_data()?; if timestamp_based_slot == seal_slot { Ok(()) } else { Err(RuntimeString::from("timestamp set in block doesn’t match slot in seal").into()) } } }