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
This commit is contained in:
André Silva
2020-03-19 10:37:29 +00:00
committed by GitHub
parent 86410de227
commit c7a23d836d
2 changed files with 104 additions and 6 deletions
@@ -30,7 +30,7 @@ use sp_consensus::{
BlockCheckParams, BlockImportParams, ImportResult, JustificationImport, BlockCheckParams, BlockImportParams, ImportResult, JustificationImport,
SelectChain, 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::Justification;
use sp_runtime::generic::{BlockId, OpaqueDigestItemId}; use sp_runtime::generic::{BlockId, OpaqueDigestItemId};
use sp_runtime::traits::{ use sp_runtime::traits::{
@@ -59,6 +59,7 @@ pub struct GrandpaBlockImport<Backend, Block: BlockT, Client, SC> {
authority_set: SharedAuthoritySet<Block::Hash, NumberFor<Block>>, authority_set: SharedAuthoritySet<Block::Hash, NumberFor<Block>>,
send_voter_commands: mpsc::UnboundedSender<VoterCommand<Block::Hash, NumberFor<Block>>>, send_voter_commands: mpsc::UnboundedSender<VoterCommand<Block::Hash, NumberFor<Block>>>,
consensus_changes: SharedConsensusChanges<Block::Hash, NumberFor<Block>>, consensus_changes: SharedConsensusChanges<Block::Hash, NumberFor<Block>>,
authority_set_hard_forks: HashMap<Block::Hash, PendingChange<Block::Hash, NumberFor<Block>>>,
_phantom: PhantomData<Backend>, _phantom: PhantomData<Backend>,
} }
@@ -72,6 +73,7 @@ impl<Backend, Block: BlockT, Client, SC: Clone> Clone for
authority_set: self.authority_set.clone(), authority_set: self.authority_set.clone(),
send_voter_commands: self.send_voter_commands.clone(), send_voter_commands: self.send_voter_commands.clone(),
consensus_changes: self.consensus_changes.clone(), consensus_changes: self.consensus_changes.clone(),
authority_set_hard_forks: self.authority_set_hard_forks.clone(),
_phantom: PhantomData, _phantom: PhantomData,
} }
} }
@@ -212,9 +214,16 @@ where
Client: crate::ClientForGrandpa<Block, BE>, Client: crate::ClientForGrandpa<Block, BE>,
{ {
// check for a new authority set change. // check for a new authority set change.
fn check_new_change(&self, header: &Block::Header, hash: Block::Hash) fn check_new_change(
-> Option<PendingChange<Block::Hash, NumberFor<Block>>> &self,
{ header: &Block::Header,
hash: Block::Hash,
) -> Option<PendingChange<Block::Hash, NumberFor<Block>>> {
// 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. // check for forced change.
if let Some((median_last_finalized, change)) = find_forced_change::<Block>(header) { if let Some((median_last_finalized, change)) = find_forced_change::<Block>(header) {
return Some(PendingChange { return Some(PendingChange {
@@ -529,13 +538,50 @@ impl<Backend, Block: BlockT, Client, SC> GrandpaBlockImport<Backend, Block, Clie
authority_set: SharedAuthoritySet<Block::Hash, NumberFor<Block>>, authority_set: SharedAuthoritySet<Block::Hash, NumberFor<Block>>,
send_voter_commands: mpsc::UnboundedSender<VoterCommand<Block::Hash, NumberFor<Block>>>, send_voter_commands: mpsc::UnboundedSender<VoterCommand<Block::Hash, NumberFor<Block>>>,
consensus_changes: SharedConsensusChanges<Block::Hash, NumberFor<Block>>, consensus_changes: SharedConsensusChanges<Block::Hash, NumberFor<Block>>,
authority_set_hard_forks: Vec<(SetId, PendingChange<Block::Hash, NumberFor<Block>>)>,
) -> GrandpaBlockImport<Backend, Block, Client, SC> { ) -> GrandpaBlockImport<Backend, Block, Client, SC> {
// 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::<HashMap<_, _>>();
// 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 { GrandpaBlockImport {
inner, inner,
select_chain, select_chain,
authority_set, authority_set,
send_voter_commands, send_voter_commands,
consensus_changes, consensus_changes,
authority_set_hard_forks,
_phantom: PhantomData, _phantom: PhantomData,
} }
} }
+54 -2
View File
@@ -417,10 +417,43 @@ pub fn block_import<BE, Block: BlockT, Client, SC>(
client: Arc<Client>, client: Arc<Client>,
genesis_authorities_provider: &dyn GenesisAuthoritySetProvider<Block>, genesis_authorities_provider: &dyn GenesisAuthoritySetProvider<Block>,
select_chain: SC, select_chain: SC,
) -> Result<( ) -> Result<
(
GrandpaBlockImport<BE, Block, Client, SC>, GrandpaBlockImport<BE, Block, Client, SC>,
LinkHalf<Block, Client, SC>, LinkHalf<Block, Client, SC>,
), ClientError> ),
ClientError,
>
where
SC: SelectChain<Block>,
BE: Backend<Block> + 'static,
Client: ClientForGrandpa<Block, BE> + '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<BE, Block: BlockT, Client, SC>(
client: Arc<Client>,
genesis_authorities_provider: &dyn GenesisAuthoritySetProvider<Block>,
select_chain: SC,
authority_set_hard_forks: Vec<(SetId, (Block::Hash, NumberFor<Block>), AuthorityList)>,
) -> Result<
(
GrandpaBlockImport<BE, Block, Client, SC>,
LinkHalf<Block, Client, SC>,
),
ClientError,
>
where where
SC: SelectChain<Block>, SC: SelectChain<Block>,
BE: Backend<Block> + 'static, BE: Backend<Block> + 'static,
@@ -444,6 +477,24 @@ where
let (voter_commands_tx, voter_commands_rx) = mpsc::unbounded(); 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(( Ok((
GrandpaBlockImport::new( GrandpaBlockImport::new(
client.clone(), client.clone(),
@@ -451,6 +502,7 @@ where
persistent_data.authority_set.clone(), persistent_data.authority_set.clone(),
voter_commands_tx, voter_commands_tx,
persistent_data.consensus_changes.clone(), persistent_data.consensus_changes.clone(),
authority_set_hard_forks,
), ),
LinkHalf { LinkHalf {
client, client,