From c7a23d836d271f3d25fc7d30468c8c7921dfb24d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Silva?= Date: Thu, 19 Mar 2020 10:37:29 +0000 Subject: [PATCH] grandpa: support for hard forking any pending standard changes (#5306) * grandpa: support for hard forking any pending standard changes * grandpa: expose authority_set_hard_forks in block import constructor * grandpa: don't break the public api --- .../client/finality-grandpa/src/import.rs | 54 ++++++++++++++++-- substrate/client/finality-grandpa/src/lib.rs | 56 ++++++++++++++++++- 2 files changed, 104 insertions(+), 6 deletions(-) diff --git a/substrate/client/finality-grandpa/src/import.rs b/substrate/client/finality-grandpa/src/import.rs index ea1deccdaf..ef5cc739de 100644 --- a/substrate/client/finality-grandpa/src/import.rs +++ b/substrate/client/finality-grandpa/src/import.rs @@ -30,7 +30,7 @@ use sp_consensus::{ BlockCheckParams, BlockImportParams, ImportResult, JustificationImport, SelectChain, }; -use sp_finality_grandpa::{GRANDPA_ENGINE_ID, ScheduledChange, ConsensusLog}; +use sp_finality_grandpa::{ConsensusLog, ScheduledChange, SetId, GRANDPA_ENGINE_ID}; use sp_runtime::Justification; use sp_runtime::generic::{BlockId, OpaqueDigestItemId}; use sp_runtime::traits::{ @@ -59,6 +59,7 @@ pub struct GrandpaBlockImport { authority_set: SharedAuthoritySet>, send_voter_commands: mpsc::UnboundedSender>>, consensus_changes: SharedConsensusChanges>, + authority_set_hard_forks: HashMap>>, _phantom: PhantomData, } @@ -72,6 +73,7 @@ impl Clone for authority_set: self.authority_set.clone(), send_voter_commands: self.send_voter_commands.clone(), consensus_changes: self.consensus_changes.clone(), + authority_set_hard_forks: self.authority_set_hard_forks.clone(), _phantom: PhantomData, } } @@ -212,9 +214,16 @@ where Client: crate::ClientForGrandpa, { // check for a new authority set change. - fn check_new_change(&self, header: &Block::Header, hash: Block::Hash) - -> Option>> - { + fn check_new_change( + &self, + header: &Block::Header, + hash: Block::Hash, + ) -> Option>> { + // check for forced authority set hard forks + if let Some(change) = self.authority_set_hard_forks.get(&hash) { + return Some(change.clone()); + } + // check for forced change. if let Some((median_last_finalized, change)) = find_forced_change::(header) { return Some(PendingChange { @@ -529,13 +538,50 @@ impl GrandpaBlockImport>, send_voter_commands: mpsc::UnboundedSender>>, consensus_changes: SharedConsensusChanges>, + authority_set_hard_forks: Vec<(SetId, PendingChange>)>, ) -> GrandpaBlockImport { + // check for and apply any forced authority set hard fork that applies + // to the *current* authority set. + if let Some((_, change)) = authority_set_hard_forks + .iter() + .find(|(set_id, _)| *set_id == authority_set.set_id()) + { + let mut authority_set = authority_set.inner().write(); + authority_set.current_authorities = change.next_authorities.clone(); + } + + // index authority set hard forks by block hash so that they can be used + // by any node syncing the chain and importing a block hard fork + // authority set changes. + let authority_set_hard_forks = authority_set_hard_forks + .into_iter() + .map(|(_, change)| (change.canon_hash, change)) + .collect::>(); + + // check for and apply any forced authority set hard fork that apply to + // any *pending* standard changes, checking by the block hash at which + // they were announced. + { + let mut authority_set = authority_set.inner().write(); + + authority_set.pending_standard_changes = authority_set + .pending_standard_changes + .clone() + .map(&mut |hash, _, original| { + authority_set_hard_forks + .get(&hash) + .cloned() + .unwrap_or(original) + }); + } + GrandpaBlockImport { inner, select_chain, authority_set, send_voter_commands, consensus_changes, + authority_set_hard_forks, _phantom: PhantomData, } } diff --git a/substrate/client/finality-grandpa/src/lib.rs b/substrate/client/finality-grandpa/src/lib.rs index 91da62848c..967584fa9b 100644 --- a/substrate/client/finality-grandpa/src/lib.rs +++ b/substrate/client/finality-grandpa/src/lib.rs @@ -417,10 +417,43 @@ pub fn block_import( client: Arc, genesis_authorities_provider: &dyn GenesisAuthoritySetProvider, select_chain: SC, -) -> Result<( +) -> Result< + ( GrandpaBlockImport, LinkHalf, - ), ClientError> + ), + ClientError, +> +where + SC: SelectChain, + BE: Backend + 'static, + Client: ClientForGrandpa + 'static, +{ + block_import_with_authority_set_hard_forks( + client, + genesis_authorities_provider, + select_chain, + Default::default(), + ) +} + +/// Make block importer and link half necessary to tie the background voter to +/// it. A vector of authority set hard forks can be passed, any authority set +/// change signaled at the given block (either already signalled or in a further +/// block when importing it) will be replaced by a standard change with the +/// given static authorities. +pub fn block_import_with_authority_set_hard_forks( + client: Arc, + genesis_authorities_provider: &dyn GenesisAuthoritySetProvider, + select_chain: SC, + authority_set_hard_forks: Vec<(SetId, (Block::Hash, NumberFor), AuthorityList)>, +) -> Result< + ( + GrandpaBlockImport, + LinkHalf, + ), + ClientError, +> where SC: SelectChain, BE: Backend + 'static, @@ -444,6 +477,24 @@ where let (voter_commands_tx, voter_commands_rx) = mpsc::unbounded(); + // create pending change objects with 0 delay and enacted on finality + // (i.e. standard changes) for each authority set hard fork. + let authority_set_hard_forks = authority_set_hard_forks + .into_iter() + .map(|(set_id, (hash, number), authorities)| { + ( + set_id, + authorities::PendingChange { + next_authorities: authorities, + delay: Zero::zero(), + canon_hash: hash, + canon_height: number, + delay_kind: authorities::DelayKind::Finalized, + }, + ) + }) + .collect(); + Ok(( GrandpaBlockImport::new( client.clone(), @@ -451,6 +502,7 @@ where persistent_data.authority_set.clone(), voter_commands_tx, persistent_data.consensus_changes.clone(), + authority_set_hard_forks, ), LinkHalf { client,