grandpa: generic voting rule for backing off from best block (#4635)

* grandpa: generic voting rule for backing off from best block

* grandpa: fix tests
This commit is contained in:
André Silva
2020-01-16 13:53:45 +00:00
committed by Robert Habermeier
parent afc3318f21
commit d18fbd7940
5 changed files with 69 additions and 42 deletions
+1
View File
@@ -5482,6 +5482,7 @@ dependencies = [
"sc-telemetry 2.0.0",
"serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)",
"sp-api 2.0.0",
"sp-arithmetic 2.0.0",
"sp-blockchain 2.0.0",
"sp-consensus 0.8.0",
"sp-consensus-babe 0.8.0",
@@ -13,6 +13,7 @@ log = "0.4.8"
parking_lot = "0.9.0"
rand = "0.7.2"
parity-scale-codec = { version = "1.0.0", features = ["derive"] }
sp-arithmetic = { version = "2.0.0", path = "../../primitives/arithmetic" }
sp-runtime = { version = "2.0.0", path = "../../primitives/runtime" }
sp-consensus = { version = "0.8", path = "../../primitives/consensus/common" }
sp-core = { version = "2.0.0", path = "../../primitives/core" }
+1 -1
View File
@@ -96,7 +96,7 @@ pub use justification::GrandpaJustification;
pub use light_import::light_block_import;
pub use observer::run_grandpa_observer;
pub use voting_rule::{
BeforeBestBlock, ThreeQuartersOfTheUnfinalizedChain, VotingRule, VotingRulesBuilder
BeforeBestBlockBy, ThreeQuartersOfTheUnfinalizedChain, VotingRule, VotingRulesBuilder
};
use aux_schema::PersistentData;
+14 -14
View File
@@ -1694,8 +1694,8 @@ fn grandpa_environment_respects_voting_rules() {
}
};
// add 20 blocks
peer.push_blocks(20, false);
// add 21 blocks
peer.push_blocks(21, false);
// create an environment with no voting rule restrictions
let unrestricted_env = environment(Box::new(()));
@@ -1716,38 +1716,38 @@ fn grandpa_environment_respects_voting_rules() {
unrestricted_env.best_chain_containing(
peer.client().info().finalized_hash
).unwrap().1,
20,
21,
);
// both the other environments should return block 15, which is 3/4 of the
// both the other environments should return block 16, which is 3/4 of the
// way in the unfinalized chain
assert_eq!(
three_quarters_env.best_chain_containing(
peer.client().info().finalized_hash
).unwrap().1,
15,
16,
);
assert_eq!(
default_env.best_chain_containing(
peer.client().info().finalized_hash
).unwrap().1,
15,
16,
);
// we finalize block 19 with block 20 being the best block
// we finalize block 19 with block 21 being the best block
peer.client().finalize_block(BlockId::Number(19), None, false).unwrap();
// the 3/4 environment should propose block 20 for voting
// the 3/4 environment should propose block 21 for voting
assert_eq!(
three_quarters_env.best_chain_containing(
peer.client().info().finalized_hash
).unwrap().1,
20,
21,
);
// while the default environment will always still make sure we don't vote
// on the best block
// on the best block (2 behind)
assert_eq!(
default_env.best_chain_containing(
peer.client().info().finalized_hash
@@ -1755,17 +1755,17 @@ fn grandpa_environment_respects_voting_rules() {
19,
);
// we finalize block 20 with block 20 being the best block
peer.client().finalize_block(BlockId::Number(20), None, false).unwrap();
// we finalize block 21 with block 21 being the best block
peer.client().finalize_block(BlockId::Number(21), None, false).unwrap();
// even though the default environment will always try to not vote on the
// best block, there's a hard rule that we can't cast any votes lower than
// the given base (#20).
// the given base (#21).
assert_eq!(
default_env.best_chain_containing(
peer.client().info().finalized_hash
).unwrap().1,
20,
21,
);
}
@@ -70,30 +70,38 @@ impl<Block, B> VotingRule<Block, B> for () where
/// A custom voting rule that guarantees that our vote is always behind the best
/// block, in the best case exactly one block behind it.
#[derive(Clone)]
pub struct BeforeBestBlock;
impl<Block, B> VotingRule<Block, B> for BeforeBestBlock where
pub struct BeforeBestBlockBy<N>(N);
impl<Block, B> VotingRule<Block, B> for BeforeBestBlockBy<NumberFor<Block>> where
Block: BlockT,
B: HeaderBackend<Block>,
{
fn restrict_vote(
&self,
_backend: &B,
backend: &B,
_base: &Block::Header,
best_target: &Block::Header,
current_target: &Block::Header,
) -> Option<(Block::Hash, NumberFor<Block>)> {
use sp_arithmetic::traits::Saturating;
if current_target.number().is_zero() {
return None;
}
if current_target.number() == best_target.number() {
return Some((
current_target.parent_hash().clone(),
*current_target.number() - One::one(),
));
// find the target number restricted by this rule
let target_number = best_target.number().saturating_sub(self.0);
// our current target is already lower than this rule would restrict
if target_number >= *current_target.number() {
return None;
}
None
// find the block at the given target height
find_target(
backend,
target_number,
current_target,
)
}
}
@@ -130,10 +138,27 @@ impl<Block, B> VotingRule<Block, B> for ThreeQuartersOfTheUnfinalizedChain where
return None;
}
let mut target_header = current_target.clone();
let mut target_hash = current_target.hash();
// find the block at the given target height
find_target(
backend,
target_number,
current_target,
)
}
}
// walk backwards until we find the target block
fn find_target<Block, B>(
backend: &B,
target_number: NumberFor<Block>,
current_header: &Block::Header,
) -> Option<(Block::Hash, NumberFor<Block>)> where
Block: BlockT,
B: HeaderBackend<Block>,
{
let mut target_hash = current_header.hash();
let mut target_header = current_header.clone();
// walk backwards until we find the target block
loop {
if *target_header.number() < target_number {
unreachable!(
@@ -142,6 +167,7 @@ impl<Block, B> VotingRule<Block, B> for ThreeQuartersOfTheUnfinalizedChain where
qed"
);
}
if *target_header.number() == target_number {
return Some((target_hash, target_number));
}
@@ -150,7 +176,6 @@ impl<Block, B> VotingRule<Block, B> for ThreeQuartersOfTheUnfinalizedChain where
target_header = backend.header(BlockId::Hash(target_hash)).ok()?
.expect("Header known to exist due to the existence of one of its descendents; qed");
}
}
}
struct VotingRules<Block, B> {
@@ -213,7 +238,7 @@ impl<Block, B> Default for VotingRulesBuilder<Block, B> where
{
fn default() -> Self {
VotingRulesBuilder::new()
.add(BeforeBestBlock)
.add(BeforeBestBlockBy(2.into()))
.add(ThreeQuartersOfTheUnfinalizedChain)
}
}