// Copyright 2017-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 . //! GRANDPA Consensus module for runtime. //! //! This manages the GRANDPA authority set ready for the native code. //! These authorities are only for GRANDPA finality, not for consensus overall. //! //! In the future, it will also handle misbehavior reports, and on-chain //! finality notifications. //! //! For full integration with GRANDPA, the `GrandpaApi` should be implemented. //! The necessary items are re-exported via the `fg_primitives` crate. #![cfg_attr(not(feature = "std"), no_std)] // re-export since this is necessary for `impl_apis` in runtime. pub use substrate_finality_grandpa_primitives as fg_primitives; use rstd::prelude::*; use parity_codec::{self as codec, Encode, Decode}; use srml_support::{ decl_event, decl_storage, decl_module, dispatch::Result, storage::StorageValue }; use sr_primitives::{ generic::{DigestItem, OpaqueDigestItemId}, traits::{CurrentHeight, Zero}, }; use fg_primitives::{ScheduledChange, ConsensusLog, GRANDPA_ENGINE_ID}; pub use fg_primitives::{AuthorityId, AuthorityWeight}; use system::{ensure_signed, DigestOf}; mod mock; mod tests; pub trait Trait: system::Trait { /// The event type of this module. type Event: From + Into<::Event>; } /// A stored pending change, old format. // TODO: remove shim // https://github.com/paritytech/substrate/issues/1614 #[derive(Encode, Decode)] pub struct OldStoredPendingChange { /// The block number this was scheduled at. pub scheduled_at: N, /// The delay in blocks until it will be applied. pub delay: N, /// The next authority set. pub next_authorities: Vec<(AuthorityId, u64)>, } /// A stored pending change. #[derive(Encode)] pub struct StoredPendingChange { /// The block number this was scheduled at. pub scheduled_at: N, /// The delay in blocks until it will be applied. pub delay: N, /// The next authority set. pub next_authorities: Vec<(AuthorityId, u64)>, /// If defined it means the change was forced and the given block number /// indicates the median last finalized block when the change was signaled. pub forced: Option, } impl Decode for StoredPendingChange { fn decode(value: &mut I) -> Option { let old = OldStoredPendingChange::decode(value)?; let forced = >::decode(value).unwrap_or(None); Some(StoredPendingChange { scheduled_at: old.scheduled_at, delay: old.delay, next_authorities: old.next_authorities, forced, }) } } /// Current state of the GRANDPA authority set. State transitions must happen in /// the same order of states defined below, e.g. `Paused` implies a prior /// `PendingPause`. #[derive(Decode, Encode)] #[cfg_attr(test, derive(Debug, PartialEq))] pub enum StoredState { /// The current authority set is live, and GRANDPA is enabled. Live, /// There is a pending pause event which will be enacted at the given block /// height. PendingPause { /// Block at which the intention to pause was scheduled. scheduled_at: N, /// Number of blocks after which the change will be enacted. delay: N }, /// The current GRANDPA authority set is paused. Paused, /// There is a pending resume event which will be enacted at the given block /// height. PendingResume { /// Block at which the intention to resume was scheduled. scheduled_at: N, /// Number of blocks after which the change will be enacted. delay: N, }, } decl_event!( pub enum Event { /// New authority set has been applied. NewAuthorities(Vec<(AuthorityId, u64)>), /// Current authority set has been paused. Paused, /// Current authority set has been resumed. Resumed, } ); decl_storage! { trait Store for Module as GrandpaFinality { /// The current authority set. Authorities get(authorities) config(): Vec<(AuthorityId, AuthorityWeight)>; /// State of the current authority set. State get(state): StoredState = StoredState::Live; /// Pending change: (signaled at, scheduled change). PendingChange: Option>; /// next block number where we can force a change. NextForced get(next_forced): Option; /// `true` if we are currently stalled. Stalled get(stalled): Option<(T::BlockNumber, T::BlockNumber)>; } } decl_module! { pub struct Module for enum Call where origin: T::Origin { fn deposit_event() = default; /// Report some misbehavior. fn report_misbehavior(origin, _report: Vec) { ensure_signed(origin)?; // FIXME: https://github.com/paritytech/substrate/issues/1112 } fn on_finalize(block_number: T::BlockNumber) { // check for scheduled pending authority set changes if let Some(pending_change) = >::get() { // emit signal if we're at the block that scheduled the change if block_number == pending_change.scheduled_at { if let Some(median) = pending_change.forced { Self::deposit_log(ConsensusLog::ForcedChange( median, ScheduledChange { delay: pending_change.delay, next_authorities: pending_change.next_authorities.clone(), } )) } else { Self::deposit_log(ConsensusLog::ScheduledChange( ScheduledChange{ delay: pending_change.delay, next_authorities: pending_change.next_authorities.clone(), } )); } } // enact the change if we've reached the enacting block if block_number == pending_change.scheduled_at + pending_change.delay { Authorities::put(&pending_change.next_authorities); Self::deposit_event( Event::NewAuthorities(pending_change.next_authorities) ); >::kill(); } } // check for scheduled pending state changes match >::get() { StoredState::PendingPause { scheduled_at, delay } => { // signal change to pause if block_number == scheduled_at { Self::deposit_log(ConsensusLog::Pause(delay)); } // enact change to paused state if block_number == scheduled_at + delay { >::put(StoredState::Paused); Self::deposit_event(Event::Paused); } }, StoredState::PendingResume { scheduled_at, delay } => { // signal change to resume if block_number == scheduled_at { Self::deposit_log(ConsensusLog::Resume(delay)); } // enact change to live state if block_number == scheduled_at + delay { >::put(StoredState::Live); Self::deposit_event(Event::Resumed); } }, _ => {}, } } } } impl Module { /// Get the current set of authorities, along with their respective weights. pub fn grandpa_authorities() -> Vec<(AuthorityId, u64)> { Authorities::get() } pub fn schedule_pause(in_blocks: T::BlockNumber) -> Result { if let StoredState::Live = >::get() { let scheduled_at = system::ChainContext::::default().current_height(); >::put(StoredState::PendingPause { delay: in_blocks, scheduled_at, }); Ok(()) } else { Err("Attempt to signal GRANDPA pause when the authority set isn't live \ (either paused or already pending pause).") } } pub fn schedule_resume(in_blocks: T::BlockNumber) -> Result { if let StoredState::Paused = >::get() { let scheduled_at = system::ChainContext::::default().current_height(); >::put(StoredState::PendingResume { delay: in_blocks, scheduled_at, }); Ok(()) } else { Err("Attempt to signal GRANDPA resume when the authority set isn't paused \ (either live or already pending resume).") } } /// Schedule a change in the authorities. /// /// The change will be applied at the end of execution of the block /// `in_blocks` after the current block. This value may be 0, in which /// case the change is applied at the end of the current block. /// /// If the `forced` parameter is defined, this indicates that the current /// set has been synchronously determined to be offline and that after /// `in_blocks` the given change should be applied. The given block number /// indicates the median last finalized block number and it should be used /// as the canon block when starting the new grandpa voter. /// /// No change should be signaled while any change is pending. Returns /// an error if a change is already pending. pub fn schedule_change( next_authorities: Vec<(AuthorityId, u64)>, in_blocks: T::BlockNumber, forced: Option, ) -> Result { if !>::exists() { let scheduled_at = system::ChainContext::::default().current_height(); if let Some(_) = forced { if Self::next_forced().map_or(false, |next| next > scheduled_at) { return Err("Cannot signal forced change so soon after last."); } // only allow the next forced change when twice the window has passed since // this one. >::put(scheduled_at + in_blocks * 2.into()); } >::put(StoredPendingChange { delay: in_blocks, scheduled_at, next_authorities, forced, }); Ok(()) } else { Err("Attempt to signal GRANDPA change with one already pending.") } } /// Deposit one of this module's logs. fn deposit_log(log: ConsensusLog) { let log: DigestItem = DigestItem::Consensus(GRANDPA_ENGINE_ID, log.encode()); >::deposit_log(log.into()); } } impl Module { pub fn grandpa_log(digest: &DigestOf) -> Option> { let id = OpaqueDigestItemId::Consensus(&GRANDPA_ENGINE_ID); digest.convert_first(|l| l.try_to::>(id)) } pub fn pending_change(digest: &DigestOf) -> Option> { Self::grandpa_log(digest).and_then(|signal| signal.try_into_change()) } pub fn forced_change(digest: &DigestOf) -> Option<(T::BlockNumber, ScheduledChange)> { Self::grandpa_log(digest).and_then(|signal| signal.try_into_forced_change()) } pub fn pending_pause(digest: &DigestOf) -> Option { Self::grandpa_log(digest).and_then(|signal| signal.try_into_pause()) } pub fn pending_resume(digest: &DigestOf) -> Option { Self::grandpa_log(digest).and_then(|signal| signal.try_into_resume()) } } impl session::OneSessionHandler for Module { type Key = AuthorityId; fn on_new_session<'a, I: 'a>(changed: bool, validators: I, _queued_validators: I) where I: Iterator { // instant changes if changed { let next_authorities = validators.map(|(_, k)| (k, 1u64)).collect::>(); let last_authorities = >::grandpa_authorities(); if next_authorities != last_authorities { if let Some((further_wait, median)) = >::take() { let _ = Self::schedule_change(next_authorities, further_wait, Some(median)); } else { let _ = Self::schedule_change(next_authorities, Zero::zero(), None); } } } } fn on_disabled(i: usize) { Self::deposit_log(ConsensusLog::OnDisabled(i as u64)) } } impl finality_tracker::OnFinalizationStalled for Module { fn on_stalled(further_wait: T::BlockNumber, median: T::BlockNumber) { // when we record old authority sets, we can use `finality_tracker::median` // to figure out _who_ failed. until then, we can't meaningfully guard // against `next == last` the way that normal session changes do. >::put((further_wait, median)); } }