From bdf1a70e953aa2ba1889c2cc2aa43199f26d6af8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Silva?= Date: Wed, 18 Mar 2020 13:18:03 +0000 Subject: [PATCH 1/3] grandpa: support for hard forking any pending standard changes --- client/finality-grandpa/src/import.rs | 57 +++++++++++++++++++++++++-- client/finality-grandpa/src/lib.rs | 2 + 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/client/finality-grandpa/src/import.rs b/client/finality-grandpa/src/import.rs index ea1deccdafbf5..2ee7a7e7ff38d 100644 --- a/client/finality-grandpa/src/import.rs +++ b/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,53 @@ impl GrandpaBlockImport>, send_voter_commands: mpsc::UnboundedSender>>, consensus_changes: SharedConsensusChanges>, + authority_set_hard_forks: Vec<( + SetId, + Block::Hash, + 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(|(_, hash, change)| (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/client/finality-grandpa/src/lib.rs b/client/finality-grandpa/src/lib.rs index 91da62848c534..a4582796dab89 100644 --- a/client/finality-grandpa/src/lib.rs +++ b/client/finality-grandpa/src/lib.rs @@ -444,6 +444,7 @@ where let (voter_commands_tx, voter_commands_rx) = mpsc::unbounded(); + // FIXME Ok(( GrandpaBlockImport::new( client.clone(), @@ -451,6 +452,7 @@ where persistent_data.authority_set.clone(), voter_commands_tx, persistent_data.consensus_changes.clone(), + Default::default(), ), LinkHalf { client, From b99fa55f13826e8fc77d598b8189b163cf657ba9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Silva?= Date: Wed, 18 Mar 2020 18:00:26 +0000 Subject: [PATCH 2/3] grandpa: expose authority_set_hard_forks in block import constructor --- client/finality-grandpa/src/import.rs | 13 +++++------- client/finality-grandpa/src/lib.rs | 29 +++++++++++++++++++++++---- 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/client/finality-grandpa/src/import.rs b/client/finality-grandpa/src/import.rs index 2ee7a7e7ff38d..ef5cc739de365 100644 --- a/client/finality-grandpa/src/import.rs +++ b/client/finality-grandpa/src/import.rs @@ -538,17 +538,13 @@ impl GrandpaBlockImport>, send_voter_commands: mpsc::UnboundedSender>>, consensus_changes: SharedConsensusChanges>, - authority_set_hard_forks: Vec<( - SetId, - Block::Hash, - PendingChange>, - )>, + 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 + if let Some((_, change)) = authority_set_hard_forks .iter() - .find(|(set_id, _, _)| *set_id == authority_set.set_id()) + .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(); @@ -559,7 +555,7 @@ impl GrandpaBlockImport>(); // check for and apply any forced authority set hard fork that apply to @@ -567,6 +563,7 @@ impl GrandpaBlockImport( client: Arc, genesis_authorities_provider: &dyn GenesisAuthoritySetProvider, select_chain: SC, -) -> Result<( + authority_set_hard_forks: Vec<(SetId, (Block::Hash, NumberFor), AuthorityList)>, +) -> Result< + ( GrandpaBlockImport, LinkHalf, - ), ClientError> + ), + ClientError, +> where SC: SelectChain, BE: Backend + 'static, @@ -444,7 +448,24 @@ where let (voter_commands_tx, voter_commands_rx) = mpsc::unbounded(); - // FIXME + // 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(), @@ -452,7 +473,7 @@ where persistent_data.authority_set.clone(), voter_commands_tx, persistent_data.consensus_changes.clone(), - Default::default(), + authority_set_hard_forks, ), LinkHalf { client, From f579aaab43822fb4fb81385b3bb72e87443dadfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Silva?= Date: Wed, 18 Mar 2020 18:08:46 +0000 Subject: [PATCH 3/3] grandpa: don't break the public api --- client/finality-grandpa/src/lib.rs | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/client/finality-grandpa/src/lib.rs b/client/finality-grandpa/src/lib.rs index 938d6464917af..967584fa9b2b3 100644 --- a/client/finality-grandpa/src/lib.rs +++ b/client/finality-grandpa/src/lib.rs @@ -417,6 +417,35 @@ pub fn block_import( client: Arc, genesis_authorities_provider: &dyn GenesisAuthoritySetProvider, select_chain: SC, +) -> Result< + ( + GrandpaBlockImport, + LinkHalf, + ), + 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< (