Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.

Commit 4f25b47

Browse files
andresilvarphmeier
authored andcommitted
grandpa: pluggable voting rules (#3673)
* grandpa: support pluggable custom voting rules * grandpa: add docs to grandpa voting rule * grandpa: make voting rule mandatory * grandpa: add test for voting rule * node: add GRANDPA voting rule * grandpa: pass backend to VotingRule * core: fix docs in SelectChain::finality_target * grandpa: implement 3/4 of unfinalized chain restriction as voting rule * grandpa: rename AlwaysBehindBestBlock voting rule * grandpa: fix tests * grandpa: remove useless test * grandpa: extend environemnt voting rule test * grandpa: add proofs to unreachable statements * grandpa: fix typo * grandpa: fix docs
1 parent c96f11a commit 4f25b47

File tree

7 files changed

+471
-68
lines changed

7 files changed

+471
-68
lines changed

core/consensus/common/src/select_chain.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,9 @@ pub trait SelectChain<Block: BlockT>: Sync + Send + Clone {
4242
/// best chain to author new blocks upon and probably finalize.
4343
fn best_chain(&self) -> Result<<Block as BlockT>::Header, Error>;
4444

45-
/// Get the best ancestor of `target_hash` that we should attempt
46-
/// to finalize next.
45+
/// Get the best descendent of `target_hash` that we should attempt to
46+
/// finalize next, if any. It is valid to return the given `target_hash`
47+
/// itself if no better descendent exists.
4748
fn finality_target(
4849
&self,
4950
target_hash: <Block as BlockT>::Hash,

core/finality-grandpa/src/environment.rs

Lines changed: 53 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ use crate::authorities::{AuthoritySet, SharedAuthoritySet};
5252
use crate::consensus_changes::SharedConsensusChanges;
5353
use crate::justification::GrandpaJustification;
5454
use crate::until_imported::UntilVoteTargetImported;
55+
use crate::voting_rule::VotingRule;
5556
use fg_primitives::{AuthorityId, AuthoritySignature, SetId, RoundNumber};
5657

5758
type HistoricalVotes<Block> = grandpa::HistoricalVotes<
@@ -368,7 +369,7 @@ impl<Block: BlockT> SharedVoterSetState<Block> {
368369
}
369370

370371
/// The environment we run GRANDPA in.
371-
pub(crate) struct Environment<B, E, Block: BlockT, N: Network<Block>, RA, SC> {
372+
pub(crate) struct Environment<B, E, Block: BlockT, N: Network<Block>, RA, SC, VR> {
372373
pub(crate) inner: Arc<Client<B, E, Block, RA>>,
373374
pub(crate) select_chain: SC,
374375
pub(crate) voters: Arc<VoterSet<AuthorityId>>,
@@ -378,9 +379,10 @@ pub(crate) struct Environment<B, E, Block: BlockT, N: Network<Block>, RA, SC> {
378379
pub(crate) network: crate::communication::NetworkBridge<Block, N>,
379380
pub(crate) set_id: SetId,
380381
pub(crate) voter_set_state: SharedVoterSetState<Block>,
382+
pub(crate) voting_rule: VR,
381383
}
382384

383-
impl<B, E, Block: BlockT, N: Network<Block>, RA, SC> Environment<B, E, Block, N, RA, SC> {
385+
impl<B, E, Block: BlockT, N: Network<Block>, RA, SC, VR> Environment<B, E, Block, N, RA, SC, VR> {
384386
/// Updates the voter set state using the given closure. The write lock is
385387
/// held during evaluation of the closure and the environment's voter set
386388
/// state is set to its result if successful.
@@ -396,16 +398,18 @@ impl<B, E, Block: BlockT, N: Network<Block>, RA, SC> Environment<B, E, Block, N,
396398
}
397399
}
398400

399-
impl<Block: BlockT<Hash=H256>, B, E, N, RA, SC>
401+
impl<Block: BlockT<Hash=H256>, B, E, N, RA, SC, VR>
400402
grandpa::Chain<Block::Hash, NumberFor<Block>>
401-
for Environment<B, E, Block, N, RA, SC>
403+
for Environment<B, E, Block, N, RA, SC, VR>
402404
where
403405
Block: 'static,
404406
B: Backend<Block, Blake2Hasher> + 'static,
405-
E: CallExecutor<Block, Blake2Hasher> + 'static,
407+
E: CallExecutor<Block, Blake2Hasher> + Send + Sync + 'static,
406408
N: Network<Block> + 'static,
407409
N::In: 'static,
408410
SC: SelectChain<Block> + 'static,
411+
VR: VotingRule<Block, Client<B, E, Block, RA>>,
412+
RA: Send + Sync,
409413
NumberFor<Block>: BlockNumberOps,
410414
{
411415
fn ancestry(&self, base: Block::Hash, block: Block::Hash) -> Result<Vec<Block::Hash>, GrandpaError> {
@@ -429,7 +433,7 @@ where
429433
debug!(target: "afg", "Finding best chain containing block {:?} with number limit {:?}", block, limit);
430434

431435
match self.select_chain.finality_target(block, None) {
432-
Ok(Some(mut best_hash)) => {
436+
Ok(Some(best_hash)) => {
433437
let base_header = self.inner.header(&BlockId::Hash(block)).ok()?
434438
.expect("Header known to exist after `best_containing` call; qed");
435439

@@ -445,35 +449,51 @@ where
445449
}
446450
}
447451

448-
let mut best_header = self.inner.header(&BlockId::Hash(best_hash)).ok()?
452+
let best_header = self.inner.header(&BlockId::Hash(best_hash)).ok()?
449453
.expect("Header known to exist after `best_containing` call; qed");
450454

451-
// we target a vote towards 3/4 of the unfinalized chain (rounding up)
452-
let target = {
453-
let two = NumberFor::<Block>::one() + One::one();
454-
let three = two + One::one();
455-
let four = three + One::one();
456-
457-
let diff = *best_header.number() - *base_header.number();
458-
let diff = ((diff * three) + two) / four;
455+
// check if our vote is currently being limited due to a pending change
456+
let limit = limit.filter(|limit| limit < best_header.number());
457+
let target;
458+
459+
let target_header = if let Some(target_number) = limit {
460+
let mut target_header = best_header.clone();
461+
462+
// walk backwards until we find the target block
463+
loop {
464+
if *target_header.number() < target_number {
465+
unreachable!(
466+
"we are traversing backwards from a known block; \
467+
blocks are stored contiguously; \
468+
qed"
469+
);
470+
}
471+
472+
if *target_header.number() == target_number {
473+
break;
474+
}
475+
476+
target_header = self.inner.header(&BlockId::Hash(*target_header.parent_hash())).ok()?
477+
.expect("Header known to exist after `best_containing` call; qed");
478+
}
459479

460-
*base_header.number() + diff
480+
target = target_header;
481+
&target
482+
} else {
483+
// otherwise just use the given best as the target
484+
&best_header
461485
};
462486

463-
// unless our vote is currently being limited due to a pending change
464-
let target = limit.map(|limit| limit.min(target)).unwrap_or(target);
465-
466-
// walk backwards until we find the target block
467-
loop {
468-
if *best_header.number() < target { unreachable!(); }
469-
if *best_header.number() == target {
470-
return Some((best_hash, *best_header.number()));
471-
}
472-
473-
best_hash = *best_header.parent_hash();
474-
best_header = self.inner.header(&BlockId::Hash(best_hash)).ok()?
475-
.expect("Header known to exist after `best_containing` call; qed");
476-
}
487+
// restrict vote according to the given voting rule, if the
488+
// voting rule doesn't restrict the vote then we keep the
489+
// previous target.
490+
//
491+
// note that we pass the original `best_header`, i.e. before the
492+
// authority set limit filter, which can be considered a
493+
// mandatory/implicit voting rule.
494+
self.voting_rule
495+
.restrict_vote(&*self.inner, &base_header, &best_header, target_header)
496+
.or(Some((target_header.hash(), *target_header.number())))
477497
},
478498
Ok(None) => {
479499
debug!(target: "afg", "Encountered error finding best chain containing {:?}: couldn't find target block", block);
@@ -519,9 +539,9 @@ pub(crate) fn ancestry<B, Block: BlockT<Hash=H256>, E, RA>(
519539
Ok(tree_route.retracted().iter().skip(1).map(|e| e.hash).collect())
520540
}
521541

522-
impl<B, E, Block: BlockT<Hash=H256>, N, RA, SC>
542+
impl<B, E, Block: BlockT<Hash=H256>, N, RA, SC, VR>
523543
voter::Environment<Block::Hash, NumberFor<Block>>
524-
for Environment<B, E, Block, N, RA, SC>
544+
for Environment<B, E, Block, N, RA, SC, VR>
525545
where
526546
Block: 'static,
527547
B: Backend<Block, Blake2Hasher> + 'static,
@@ -530,6 +550,7 @@ where
530550
N::In: 'static + Send,
531551
RA: 'static + Send + Sync,
532552
SC: SelectChain<Block> + 'static,
553+
VR: VotingRule<Block, Client<B, E, Block, RA>>,
533554
NumberFor<Block>: BlockNumberOps,
534555
{
535556
type Timer = Box<dyn Future<Item = (), Error = Self::Error> + Send>;

core/finality-grandpa/src/lib.rs

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,15 @@ mod justification;
9292
mod light_import;
9393
mod observer;
9494
mod until_imported;
95+
mod voting_rule;
9596

9697
pub use communication::Network;
9798
pub use finality_proof::FinalityProofProvider;
9899
pub use light_import::light_block_import;
99100
pub use observer::run_grandpa_observer;
101+
pub use voting_rule::{
102+
BeforeBestBlock, ThreeQuartersOfTheUnfinalizedChain, VotingRule, VotingRulesBuilder
103+
};
100104

101105
use aux_schema::PersistentData;
102106
use environment::{Environment, VoterSetState};
@@ -466,7 +470,7 @@ fn register_finality_tracker_inherent_data_provider<B, E, Block: BlockT<Hash=H25
466470
}
467471

468472
/// Parameters used to run Grandpa.
469-
pub struct GrandpaParams<B, E, Block: BlockT<Hash=H256>, N, RA, SC, X> {
473+
pub struct GrandpaParams<B, E, Block: BlockT<Hash=H256>, N, RA, SC, VR, X> {
470474
/// Configuration for the GRANDPA service.
471475
pub config: Config,
472476
/// A link to the block import worker.
@@ -479,19 +483,22 @@ pub struct GrandpaParams<B, E, Block: BlockT<Hash=H256>, N, RA, SC, X> {
479483
pub on_exit: X,
480484
/// If supplied, can be used to hook on telemetry connection established events.
481485
pub telemetry_on_connect: Option<mpsc::UnboundedReceiver<()>>,
486+
/// A voting rule used to potentially restrict target votes.
487+
pub voting_rule: VR,
482488
}
483489

484490
/// Run a GRANDPA voter as a task. Provide configuration and a link to a
485491
/// block import worker that has already been instantiated with `block_import`.
486-
pub fn run_grandpa_voter<B, E, Block: BlockT<Hash=H256>, N, RA, SC, X>(
487-
grandpa_params: GrandpaParams<B, E, Block, N, RA, SC, X>,
492+
pub fn run_grandpa_voter<B, E, Block: BlockT<Hash=H256>, N, RA, SC, VR, X>(
493+
grandpa_params: GrandpaParams<B, E, Block, N, RA, SC, VR, X>,
488494
) -> client::error::Result<impl Future<Item=(),Error=()> + Send + 'static> where
489495
Block::Hash: Ord,
490496
B: Backend<Block, Blake2Hasher> + 'static,
491497
E: CallExecutor<Block, Blake2Hasher> + Send + Sync + 'static,
492498
N: Network<Block> + Send + Sync + 'static,
493499
N::In: Send + 'static,
494500
SC: SelectChain<Block> + 'static,
501+
VR: VotingRule<Block, Client<B, E, Block, RA>> + Clone + 'static,
495502
NumberFor<Block>: BlockNumberOps,
496503
DigestFor<Block>: Encode,
497504
RA: Send + Sync + 'static,
@@ -504,6 +511,7 @@ pub fn run_grandpa_voter<B, E, Block: BlockT<Hash=H256>, N, RA, SC, X>(
504511
inherent_data_providers,
505512
on_exit,
506513
telemetry_on_connect,
514+
voting_rule,
507515
} = grandpa_params;
508516

509517
let LinkHalf {
@@ -556,8 +564,9 @@ pub fn run_grandpa_voter<B, E, Block: BlockT<Hash=H256>, N, RA, SC, X>(
556564
config,
557565
network,
558566
select_chain,
567+
voting_rule,
559568
persistent_data,
560-
voter_commands_rx
569+
voter_commands_rx,
561570
);
562571

563572
let voter_work = voter_work
@@ -578,13 +587,13 @@ pub fn run_grandpa_voter<B, E, Block: BlockT<Hash=H256>, N, RA, SC, X>(
578587

579588
/// Future that powers the voter.
580589
#[must_use]
581-
struct VoterWork<B, E, Block: BlockT, N: Network<Block>, RA, SC> {
590+
struct VoterWork<B, E, Block: BlockT, N: Network<Block>, RA, SC, VR> {
582591
voter: Box<dyn Future<Item = (), Error = CommandOrError<Block::Hash, NumberFor<Block>>> + Send>,
583-
env: Arc<Environment<B, E, Block, N, RA, SC>>,
592+
env: Arc<Environment<B, E, Block, N, RA, SC, VR>>,
584593
voter_commands_rx: mpsc::UnboundedReceiver<VoterCommand<Block::Hash, NumberFor<Block>>>,
585594
}
586595

587-
impl<B, E, Block, N, RA, SC> VoterWork<B, E, Block, N, RA, SC>
596+
impl<B, E, Block, N, RA, SC, VR> VoterWork<B, E, Block, N, RA, SC, VR>
588597
where
589598
Block: BlockT<Hash=H256>,
590599
N: Network<Block> + Sync,
@@ -594,12 +603,14 @@ where
594603
E: CallExecutor<Block, Blake2Hasher> + Send + Sync + 'static,
595604
B: Backend<Block, Blake2Hasher> + 'static,
596605
SC: SelectChain<Block> + 'static,
606+
VR: VotingRule<Block, Client<B, E, Block, RA>> + Clone + 'static,
597607
{
598608
fn new(
599609
client: Arc<Client<B, E, Block, RA>>,
600610
config: Config,
601611
network: NetworkBridge<Block, N>,
602612
select_chain: SC,
613+
voting_rule: VR,
603614
persistent_data: PersistentData<Block>,
604615
voter_commands_rx: mpsc::UnboundedReceiver<VoterCommand<Block::Hash, NumberFor<Block>>>,
605616
) -> Self {
@@ -608,6 +619,7 @@ where
608619
let env = Arc::new(Environment {
609620
inner: client,
610621
select_chain,
622+
voting_rule,
611623
voters: Arc::new(voters),
612624
config,
613625
network,
@@ -731,6 +743,7 @@ where
731743
authority_set: self.env.authority_set.clone(),
732744
consensus_changes: self.env.consensus_changes.clone(),
733745
network: self.env.network.clone(),
746+
voting_rule: self.env.voting_rule.clone(),
734747
});
735748

736749
self.rebuild_voter();
@@ -755,7 +768,7 @@ where
755768
}
756769
}
757770

758-
impl<B, E, Block, N, RA, SC> Future for VoterWork<B, E, Block, N, RA, SC>
771+
impl<B, E, Block, N, RA, SC, VR> Future for VoterWork<B, E, Block, N, RA, SC, VR>
759772
where
760773
Block: BlockT<Hash=H256>,
761774
N: Network<Block> + Sync,
@@ -765,6 +778,7 @@ where
765778
E: CallExecutor<Block, Blake2Hasher> + Send + Sync + 'static,
766779
B: Backend<Block, Blake2Hasher> + 'static,
767780
SC: SelectChain<Block> + 'static,
781+
VR: VotingRule<Block, Client<B, E, Block, RA>> + Clone + 'static,
768782
{
769783
type Item = ();
770784
type Error = Error;
@@ -809,8 +823,8 @@ where
809823
}
810824

811825
#[deprecated(since = "1.1", note = "Please switch to run_grandpa_voter.")]
812-
pub fn run_grandpa<B, E, Block: BlockT<Hash=H256>, N, RA, SC, X>(
813-
grandpa_params: GrandpaParams<B, E, Block, N, RA, SC, X>,
826+
pub fn run_grandpa<B, E, Block: BlockT<Hash=H256>, N, RA, SC, VR, X>(
827+
grandpa_params: GrandpaParams<B, E, Block, N, RA, SC, VR, X>,
814828
) -> ::client::error::Result<impl Future<Item=(),Error=()> + Send + 'static> where
815829
Block::Hash: Ord,
816830
B: Backend<Block, Blake2Hasher> + 'static,
@@ -821,6 +835,7 @@ pub fn run_grandpa<B, E, Block: BlockT<Hash=H256>, N, RA, SC, X>(
821835
NumberFor<Block>: BlockNumberOps,
822836
DigestFor<Block>: Encode,
823837
RA: Send + Sync + 'static,
838+
VR: VotingRule<Block, Client<B, E, Block, RA>> + Clone + 'static,
824839
X: Future<Item=(),Error=()> + Clone + Send + 'static,
825840
{
826841
run_grandpa_voter(grandpa_params)

0 commit comments

Comments
 (0)