grandpa: fix creation of justification with equivocating precommits in commit (#11302)

* grandpa: fix creation of justification ancestry

we would reject commits that have precommits targeting blocks lower than the
commit target. when there is an equivocation (or if it the commit is not
minimal) it is possible to have such precommits and we should assume that they
are the round base.

* grandpa: bump to 0.16.0

* grandpa: add test for justification with equivocation

* grandpa: fix failing test
This commit is contained in:
André Silva
2022-06-14 11:41:11 +01:00
committed by GitHub
parent 525fc8ebc3
commit 06cf8ad076
9 changed files with 125 additions and 20 deletions
@@ -1569,6 +1569,73 @@ fn grandpa_environment_never_overwrites_round_voter_state() {
assert_matches!(get_current_round(2).unwrap(), HasVoted::Yes(_, _));
}
#[test]
fn justification_with_equivocation() {
use sp_application_crypto::Pair;
// we have 100 authorities
let pairs = (0..100).map(|n| AuthorityPair::from_seed(&[n; 32])).collect::<Vec<_>>();
let voters = pairs.iter().map(AuthorityPair::public).map(|id| (id, 1)).collect::<Vec<_>>();
let api = TestApi::new(voters.clone());
let mut net = GrandpaTestNet::new(api.clone(), 1, 0);
// we create a basic chain with 3 blocks (no forks)
net.peer(0).push_blocks(3, false);
let client = net.peer(0).client().clone();
let block1 = client.header(&BlockId::Number(1)).ok().flatten().unwrap();
let block2 = client.header(&BlockId::Number(2)).ok().flatten().unwrap();
let block3 = client.header(&BlockId::Number(3)).ok().flatten().unwrap();
let set_id = 0;
let justification = {
let round = 1;
let make_precommit = |target_hash, target_number, pair: &AuthorityPair| {
let precommit = finality_grandpa::Precommit { target_hash, target_number };
let msg = finality_grandpa::Message::Precommit(precommit.clone());
let encoded = sp_finality_grandpa::localized_payload(round, set_id, &msg);
let precommit = finality_grandpa::SignedPrecommit {
precommit: precommit.clone(),
signature: pair.sign(&encoded[..]),
id: pair.public(),
};
precommit
};
let mut precommits = Vec::new();
// we have 66/100 votes for block #3 and therefore do not have threshold to finalize
for pair in pairs.iter().take(66) {
let precommit = make_precommit(block3.hash(), *block3.number(), pair);
precommits.push(precommit);
}
// we create an equivocation for the 67th validator targetting blocks #1 and #2.
// this should be accounted as "voting for all blocks" and therefore block #3 will
// have 67/100 votes, reaching finality threshold.
{
precommits.push(make_precommit(block1.hash(), *block1.number(), &pairs[66]));
precommits.push(make_precommit(block2.hash(), *block2.number(), &pairs[66]));
}
let commit = finality_grandpa::Commit {
target_hash: block3.hash(),
target_number: *block3.number(),
precommits,
};
GrandpaJustification::from_commit(&client.as_client(), round, commit).unwrap()
};
// the justification should include the minimal necessary vote ancestry and
// the commit should be valid
assert!(justification.verify(set_id, &voters).is_ok());
}
#[test]
fn imports_justification_for_regular_blocks_on_import() {
// NOTE: this is a regression test since initially we would only import