Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
8f9808a
skeleton for finality tracker
rphmeier Jan 28, 2019
4e28e99
dispatch events when nothing finalized for a long time
rphmeier Jan 28, 2019
37af080
begin integrating finality tracker into grandpa
rphmeier Jan 29, 2019
1185041
add delay field to pending change
rphmeier Jan 29, 2019
1a48669
add has_api_with function to sr_version for querying APIs
rphmeier Jan 29, 2019
f84de0e
partially integrate new force changes into grandpa
rphmeier Jan 29, 2019
5b53bea
implement forced changes
rphmeier Jan 30, 2019
db20c36
get srml-grandpa compiling
rphmeier Jan 30, 2019
9df9464
Update core/finality-grandpa/src/authorities.rs
bkchr Jan 30, 2019
cc5d70b
Update core/finality-grandpa/src/authorities.rs
bkchr Jan 30, 2019
3e78042
Update core/finality-grandpa/src/authorities.rs
bkchr Jan 30, 2019
68c94bf
remove explicit dependence on CoreApi
rphmeier Jan 30, 2019
1fbb26c
increase node runtime version
rphmeier Jan 30, 2019
ecc489b
integrate grandpa forced changes into node runtime
rphmeier Jan 30, 2019
a75d5fd
add some tests to finality-tracker
rphmeier Jan 30, 2019
b9246b0
integrate finality tracking into node-runtime
rphmeier Jan 30, 2019
36dfdf7
Merge branch 'master' into rh-grandpa-offline-fallback
rphmeier Jan 30, 2019
619c7d4
test forced-change logic
rphmeier Jan 31, 2019
5e0a57a
test forced changes in the authority-set handler
rphmeier Jan 31, 2019
81ad7d7
kill some unneeded bounds in client
rphmeier Jan 31, 2019
a631a8d
test forced-changes in finality-grandpa and fix logic
rphmeier Jan 31, 2019
1ce7d40
Merge branch 'master' into rh-grandpa-offline-fallback: bump node ver…
rphmeier Jan 31, 2019
5ccd254
build wasm and finality-tracker is no-std
rphmeier Jan 31, 2019
b3291d4
Merge branch 'master' into rh-grandpa-offline-fallback
rphmeier Feb 4, 2019
386a5f9
restart voter on forced change
rphmeier Feb 5, 2019
7d1981c
Merge branch 'master' into rh-grandpa-offline-fallback
rphmeier Feb 11, 2019
a2add58
allow returning custom error type from lock_import_and_run
rphmeier Feb 14, 2019
35f444a
extract out most DB logic to aux_schema and use atomic client ops
rphmeier Feb 14, 2019
464f616
unify authority set writing
rphmeier Feb 14, 2019
bc2761f
implement set pausing
rphmeier Feb 14, 2019
08d5275
Merge branch 'master' into rh-grandpa-offline-fallback
rphmeier Feb 14, 2019
9518455
bump runtime version
rphmeier Feb 15, 2019
e7d24df
note on DB when we pause.
rphmeier Feb 15, 2019
8b018f1
Merge branch 'master' into rh-grandpa-offline-fallback
rphmeier Feb 15, 2019
4290f7d
Merge branch 'master' into rh-grandpa-offline-fallback
andresilva Feb 21, 2019
1a47254
core: grandpa: integrate forced changes with multiple pending standar…
andresilva Feb 22, 2019
e5b1c56
core: grandpa: fix AuthoritySet tests
andresilva Feb 22, 2019
f7420b7
Merge branch 'master' into rh-grandpa-offline-fallback
andresilva Feb 22, 2019
ad7a092
Merge branch 'master' into rh-grandpa-offline-fallback
andresilva Feb 25, 2019
0f21b3f
runtime: bump impl_version
andresilva Feb 25, 2019
2bcf2c7
core: clear pending justification requests after forced change import
andresilva Feb 25, 2019
33fe51f
srml: finality-tracker: use FinalizedInherentData
andresilva Feb 25, 2019
eb98d97
core: log requests for clearing justification requests
andresilva Feb 25, 2019
ed6b67e
core, node: update runtimes
andresilva Feb 25, 2019
ff1a8a5
core: grandpa: fix tests
andresilva Feb 25, 2019
30890ec
core: grandpa: remove todos and add comments
andresilva Feb 25, 2019
5bff073
core: grandpa: use has_api_with from ApiExt
andresilva Feb 25, 2019
f20746c
core: fix tests
andresilva Feb 25, 2019
c3d05fc
core: grandpa: remove unnecessary mut modifier
andresilva Feb 25, 2019
e537dd5
core: replace PostImportActions bitflags with struct
andresilva Feb 25, 2019
5d80af7
core: grandpa: restrict genesis on forced authority set change
andresilva Feb 26, 2019
abaccb3
core: grandpa: add more docs
andresilva Feb 26, 2019
94729c8
core: grandpa: prevent safety violations in Environment::finalize_block
andresilva Feb 26, 2019
9c43a28
core: grandpa: register finality tracker inherent data provider
andresilva Feb 26, 2019
3c37916
core: grandpa: fix tests
andresilva Feb 26, 2019
0976224
Merge branch 'master' into rh-grandpa-offline-fallback
andresilva Feb 26, 2019
de63d65
node: update runtime blobs
andresilva Feb 26, 2019
b5df296
core: grandpa: remove outdated todo
andresilva Mar 1, 2019
9799356
core: aura: fix typo in log message
andresilva Mar 1, 2019
fbd995b
core: grandpa: check re-finalization is on canonical chain
andresilva Mar 1, 2019
815410d
Merge branch 'master' into rh-grandpa-offline-fallback
andresilva Mar 1, 2019
d034c36
srml: finality-tracker: fix initialization
andresilva Mar 5, 2019
a391918
Merge branch 'master' into rh-grandpa-offline-fallback
andresilva Mar 5, 2019
93b98f7
node: update runtime wasm
andresilva Mar 5, 2019
cbb959f
srml: finality-tracker: don't re-initialize config keys
andresilva Mar 5, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
core: grandpa: restrict genesis on forced authority set change
  • Loading branch information
andresilva committed Feb 26, 2019
commit 5d80af776e02b0987c6e17f392fc87ca0244f136
2 changes: 1 addition & 1 deletion core/finality-grandpa/primitives/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ decl_runtime_apis! {
/// the digest type it should return the same result regardless of the current
/// state.
fn grandpa_forced_change(digest: &DigestFor<Block>)
-> Option<ScheduledChange<NumberFor<Block>>>;
-> Option<(NumberFor<Block>, ScheduledChange<NumberFor<Block>>)>;

/// Get the current GRANDPA authorities and weights. This should not change except
/// for when changes are scheduled and the corresponding delay has passed.
Expand Down
19 changes: 12 additions & 7 deletions core/finality-grandpa/src/authorities.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ where
E: std::error::Error,
{
match pending.delay_kind {
DelayKind::Best => {
DelayKind::Best { .. } => {
self.add_forced_change(pending, is_descendent_of)
},
DelayKind::Finalized => {
Expand Down Expand Up @@ -244,7 +244,7 @@ where
best_hash: H,
best_number: N,
is_descendent_of: &F,
) -> Result<Option<Self>, E>
) -> Result<Option<(N, Self)>, E>
where F: Fn(&H, &H) -> Result<bool, E>,
{
let mut new_set = None;
Expand All @@ -259,12 +259,17 @@ where
info!(target: "finality", "Applying authority set change forced at block #{:?}",
change.canon_height);

new_set = Some(AuthoritySet {
let median_last_finalized = match change.delay_kind {
DelayKind::Best { ref median_last_finalized } => median_last_finalized.clone(),
_ => unreachable!("pending_forced_changes only contains forced changes; forced changes have delay kind Best; qed."),
};

new_set = Some((median_last_finalized, AuthoritySet {
current_authorities: change.next_authorities.clone(),
set_id: self.set_id + 1,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm yet in-progress of trying to understand this PR in details - so probably I'll discover the answer for my question later. But here is the question: is this possible that there will be two adjacent forced-change periods where set_id has increased by 2 without any justification (i.e. set A#1 has changed to set B#2 AND set B#2 was also unable to finalize something AND it has been changed to set C#3 AND only C#3 has generated new justification with set_id=3).

I'm currently relying on the fact that set_id is always accompanied by justification in the #1669 (in this check to be specific). It isn't critical for that code - just wanted to clarify if this is still a rule.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I believe the situation you described can happen.

Copy link
Contributor Author

@rphmeier rphmeier Mar 1, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@svyatonik worth noting that the offline change boots out light clients, so you can still rely on that assumption as long as you handle offline changes by shutting down the light client.

A checkpointing system is the only way to get light clients back onto the network.

pending_standard_changes: ForkTree::new(), // new set, new changes.
pending_forced_changes: Vec::new(),
});
}));

break;
}
Expand Down Expand Up @@ -358,11 +363,11 @@ where

/// Kinds of delays for pending changes.
#[derive(Debug, Clone, Encode, Decode, PartialEq)]
pub(crate) enum DelayKind {
pub(crate) enum DelayKind<N> {
/// Depth in finalized chain.
Finalized,
/// Depth in best chain.
Best,
Best { median_last_finalized: N },
}

/// A pending change to the authority set.
Expand All @@ -381,7 +386,7 @@ pub(crate) struct PendingChange<H, N> {
/// The announcing block's hash.
pub(crate) canon_hash: H,
/// The delay kind.
pub(crate) delay_kind: DelayKind,
pub(crate) delay_kind: DelayKind<N>,
}

impl<H: Decode, N: Decode> Decode for PendingChange<H, N> {
Expand Down
28 changes: 20 additions & 8 deletions core/finality-grandpa/src/import.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use futures::sync::mpsc;
use parking_lot::RwLockWriteGuard;

use client::{blockchain, CallExecutor, Client};
use client::blockchain::HeaderBackend;
use client::backend::Backend;
use client::runtime_api::ApiExt;
use consensus_common::{
Expand Down Expand Up @@ -194,12 +195,12 @@ impl<B, E, Block: BlockT<Hash=H256>, RA, PRA> GrandpaBlockImport<B, E, Block, RA
},
},
Ok(None) => {},
Ok(Some(change)) => return Ok(Some(PendingChange {
Ok(Some((median_last_finalized, change))) => return Ok(Some(PendingChange {
next_authorities: change.next_authorities,
delay: change.delay,
canon_height: *header.number(),
canon_hash: hash,
delay_kind: DelayKind::Best,
delay_kind: DelayKind::Best { median_last_finalized },
})),
}
}
Expand Down Expand Up @@ -290,7 +291,7 @@ impl<B, E, Block: BlockT<Hash=H256>, RA, PRA> GrandpaBlockImport<B, E, Block, RA
let old = guard.as_mut().clone();
guard.set_old(old);

if let DelayKind::Best = change.delay_kind {
if let DelayKind::Best { .. } = change.delay_kind {
do_pause = true;
}

Expand All @@ -305,19 +306,32 @@ impl<B, E, Block: BlockT<Hash=H256>, RA, PRA> GrandpaBlockImport<B, E, Block, RA
.map_err(|e| ConsensusErrorKind::ClientImport(e.to_string()))
.map_err(ConsensusError::from)?;

if let Some(new_set) = forced_change_set {
if let Some((median_last_finalized_number, new_set)) = forced_change_set {
let new_authorities = {
let (set_id, new_authorities) = new_set.current();

let best_finalized_number = self.inner.backend().blockchain().info()
.map_err(|e| ConsensusErrorKind::ClientImport(e.to_string()))?
.finalized_number;

let canon_number = best_finalized_number.min(median_last_finalized_number);

let canon_hash =
self.inner.backend().blockchain().header(BlockId::Number(canon_number))
.map_err(|e| ConsensusErrorKind::ClientImport(e.to_string()))?
.expect("the given block number is less or equal than the current best finalized number; \
current best finalized number must exist in chain; qed.")
.hash();

// we start the new authority assuming the block that enacted the
// change is the canonical one. we could use the block that signalled
// the change instead, but this would make verification of
// justifications more complicated, since we'd need to check if
// there's any pending forced change (since forced changes would
// finalize "backwards")
NewAuthoritySet {
canon_number: number,
canon_hash: hash,
canon_number,
canon_hash,
set_id,
authorities: new_authorities.to_vec(),
}
Expand Down Expand Up @@ -377,8 +391,6 @@ impl<B, E, Block: BlockT<Hash=H256>, RA, PRA> BlockImport<Block>
fn import_block(&self, mut block: ImportBlock<Block>, new_authorities: Option<Vec<Ed25519AuthorityId>>)
-> Result<ImportResult, Self::Error>
{
use client::blockchain::HeaderBackend;

let hash = block.post_header().hash();
let number = block.header.number().clone();

Expand Down
6 changes: 3 additions & 3 deletions node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -302,21 +302,21 @@ impl_runtime_apis! {
Log(InternalLog::grandpa(grandpa_signal)) => Some(grandpa_signal),
_=> None
}) {
if let Some(change) = Grandpa::scrape_digest_change(log, false) {
if let Some(change) = Grandpa::scrape_digest_change(log) {
return Some(change);
}
}
None
}

fn grandpa_forced_change(digest: &DigestFor<Block>)
-> Option<ScheduledChange<NumberFor<Block>>>
-> Option<(NumberFor<Block>, ScheduledChange<NumberFor<Block>>)>
{
for log in digest.logs.iter().filter_map(|l| match l {
Log(InternalLog::grandpa(grandpa_signal)) => Some(grandpa_signal),
_=> None
}) {
if let Some(change) = Grandpa::scrape_digest_change(log, true) {
if let Some(change) = Grandpa::scrape_digest_forced_change(log) {
return Some(change);
}
}
Expand Down
69 changes: 39 additions & 30 deletions srml/grandpa/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ pub trait GrandpaChangeSignal<N> {
/// Try to cast the log entry as a contained signal.
fn as_signal(&self) -> Option<ScheduledChange<N>>;
/// Try to cast the log entry as a contained forced signal.
fn as_forced_signal(&self) -> Option<ScheduledChange<N>>;
fn as_forced_signal(&self) -> Option<(N, ScheduledChange<N>)>;
}

/// A logs in this module.
Expand All @@ -76,24 +76,25 @@ pub enum RawLog<N, SessionKey> {
/// Authorities set change has been signalled. Contains the new set of authorities
/// and the delay in blocks _to finalize_ before applying.
AuthoritiesChangeSignal(N, Vec<(SessionKey, u64)>),
/// A forced authorities set change. Contains the new set of authorities and the
/// delay in blocks _to import_ before applying.
ForcedAuthoritiesChangeSignal(N, Vec<(SessionKey, u64)>),
/// A forced authorities set change. Contains in this order: the median last
/// finalized block when the change was signaled, the delay in blocks _to import_
/// before applying and the new set of authorities.
ForcedAuthoritiesChangeSignal(N, N, Vec<(SessionKey, u64)>),
}

impl<N: Clone, SessionKey> RawLog<N, SessionKey> {
/// Try to cast the log entry as a contained signal.
pub fn as_signal(&self) -> Option<(N, &[(SessionKey, u64)])> {
match *self {
RawLog::AuthoritiesChangeSignal(ref n, ref signal) => Some((n.clone(), signal)),
RawLog::ForcedAuthoritiesChangeSignal(_, _) => None,
RawLog::AuthoritiesChangeSignal(ref delay, ref signal) => Some((delay.clone(), signal)),
RawLog::ForcedAuthoritiesChangeSignal(_, _, _) => None,
}
}

/// Try to cast the log entry as a contained forced signal.
pub fn as_forced_signal(&self) -> Option<(N, &[(SessionKey, u64)])> {
pub fn as_forced_signal(&self) -> Option<(N, N, &[(SessionKey, u64)])> {
match *self {
RawLog::ForcedAuthoritiesChangeSignal(ref n, ref signal) => Some((n.clone(), signal)),
RawLog::ForcedAuthoritiesChangeSignal(ref median, ref delay, ref signal) => Some((median.clone(), delay.clone(), signal)),
RawLog::AuthoritiesChangeSignal(_, _) => None,
}
}
Expand All @@ -112,14 +113,14 @@ impl<N, SessionKey> GrandpaChangeSignal<N> for RawLog<N, SessionKey>
})
}

fn as_forced_signal(&self) -> Option<ScheduledChange<N>> {
RawLog::as_forced_signal(self).map(|(delay, next_authorities)| ScheduledChange {
fn as_forced_signal(&self) -> Option<(N, ScheduledChange<N>)> {
RawLog::as_forced_signal(self).map(|(median, delay, next_authorities)| (median, ScheduledChange {
delay,
next_authorities: next_authorities.iter()
.cloned()
.map(|(k, w)| (k.into(), w))
.collect(),
})
}))
}
}

Expand Down Expand Up @@ -156,14 +157,15 @@ pub struct StoredPendingChange<N, SessionKey> {
pub delay: N,
/// The next authority set.
pub next_authorities: Vec<(SessionKey, u64)>,
/// Whether the change was forced.
pub forced: bool,
/// 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<N>,
}

impl<N: Decode, SessionKey: Decode> Decode for StoredPendingChange<N, SessionKey> {
fn decode<I: codec::Input>(value: &mut I) -> Option<Self> {
let old = OldStoredPendingChange::decode(value)?;
let forced = bool::decode(value).unwrap_or(false);
let forced = <Option<N>>::decode(value).unwrap_or(None);

Some(StoredPendingChange {
scheduled_at: old.scheduled_at,
Expand Down Expand Up @@ -223,13 +225,14 @@ decl_module! {
fn on_finalise(block_number: T::BlockNumber) {
if let Some(pending_change) = <PendingChange<T>>::get() {
if block_number == pending_change.scheduled_at {
if !pending_change.forced {
Self::deposit_log(RawLog::AuthoritiesChangeSignal(
if let Some(median) = pending_change.forced {
Self::deposit_log(RawLog::ForcedAuthoritiesChangeSignal(
median,
pending_change.delay,
pending_change.next_authorities.clone(),
));
} else {
Self::deposit_log(RawLog::ForcedAuthoritiesChangeSignal(
Self::deposit_log(RawLog::AuthoritiesChangeSignal(
pending_change.delay,
pending_change.next_authorities.clone(),
));
Expand Down Expand Up @@ -260,23 +263,24 @@ impl<T: Trait> Module<T> {
/// `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` flag is set to true, this indicates that the current
/// 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.
/// `in_blocks` the given change should be applied. The given block number
/// indicates the median last finalized block number.
///
/// No change should be signalled while any change is pending. Returns
/// an error if a change is already pending.
pub fn schedule_change(
next_authorities: Vec<(T::SessionKey, u64)>,
in_blocks: T::BlockNumber,
forced: bool,
forced: Option<T::BlockNumber>,
) -> Result {
use primitives::traits::As;

if Self::pending_change().is_none() {
let scheduled_at = system::ChainContext::<T>::default().current_height();

if forced {
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.");
}
Expand Down Expand Up @@ -306,15 +310,18 @@ impl<T: Trait> Module<T> {
}

impl<T: Trait> Module<T> where Ed25519AuthorityId: core::convert::From<<T as Trait>::SessionKey> {
/// See if the digest contains any scheduled change.
pub fn scrape_digest_change(log: &Log<T>, forced: bool)
/// See if the digest contains any standard scheduled change.
pub fn scrape_digest_change(log: &Log<T>)
-> Option<ScheduledChange<T::BlockNumber>>
{
if forced {
<Log<T> as GrandpaChangeSignal<T::BlockNumber>>::as_forced_signal(log)
} else {
<Log<T> as GrandpaChangeSignal<T::BlockNumber>>::as_signal(log)
}
<Log<T> as GrandpaChangeSignal<T::BlockNumber>>::as_signal(log)
}

/// See if the digest contains any forced scheduled change.
pub fn scrape_digest_forced_change(log: &Log<T>)
-> Option<(T::BlockNumber, ScheduledChange<T::BlockNumber>)>
{
<Log<T> as GrandpaChangeSignal<T::BlockNumber>>::as_forced_signal(log)
}
}

Expand Down Expand Up @@ -352,7 +359,7 @@ impl<X, T> session::OnSessionChange<X> for SyncedAuthorities<T> where
// instant changes
let last_authorities = <Module<T>>::grandpa_authorities();
if next_authorities != last_authorities {
let _ = <Module<T>>::schedule_change(next_authorities, Zero::zero(), false);
let _ = <Module<T>>::schedule_change(next_authorities, Zero::zero(), None);
}
}
}
Expand All @@ -377,7 +384,9 @@ impl<T> finality_tracker::OnFinalizationStalled<T::BlockNumber> for SyncedAuthor
.map(|key| (key, 1)) // evenly-weighted.
.collect::<Vec<(<T as Trait>::SessionKey, u64)>>();

let median = <finality_tracker::Module<T>>::median();

// schedule a change for `further_wait` blocks.
let _ = <Module<T>>::schedule_change(next_authorities, further_wait, true);
let _ = <Module<T>>::schedule_change(next_authorities, further_wait, Some(median));
}
}