|
|
|
@@ -20,7 +20,6 @@
|
|
|
|
|
//! Some of tests in this module may partially duplicate tests from `justification.rs`,
|
|
|
|
|
//! but their purpose is different.
|
|
|
|
|
|
|
|
|
|
use assert_matches::assert_matches;
|
|
|
|
|
use bp_header_chain::justification::{verify_justification, Error, GrandpaJustification};
|
|
|
|
|
use bp_test_utils::{
|
|
|
|
|
header_id, make_justification_for_header, signed_precommit, test_header, Account,
|
|
|
|
@@ -106,7 +105,7 @@ pub fn make_default_justification(header: &TestHeader) -> GrandpaJustification<T
|
|
|
|
|
//
|
|
|
|
|
// 1) to return `Err()` (which only may happen if `finality_grandpa::Chain` implementation
|
|
|
|
|
// returns an error);
|
|
|
|
|
// 2) to return `Ok(validation_result) if validation_result.ghost().is_none()`.
|
|
|
|
|
// 2) to return `Ok(validation_result)` if `validation_result.is_valid()` is false.
|
|
|
|
|
//
|
|
|
|
|
// Our implementation would just return error in both cases.
|
|
|
|
|
|
|
|
|
@@ -126,16 +125,17 @@ fn same_result_when_precommit_target_has_lower_number_than_commit_target() {
|
|
|
|
|
),
|
|
|
|
|
Err(Error::PrecommitIsNotCommitDescendant),
|
|
|
|
|
);
|
|
|
|
|
// original implementation returns empty GHOST
|
|
|
|
|
assert_matches!(
|
|
|
|
|
finality_grandpa::validate_commit(
|
|
|
|
|
&justification.commit,
|
|
|
|
|
&full_voter_set(),
|
|
|
|
|
&AncestryChain::new(&justification.votes_ancestries),
|
|
|
|
|
)
|
|
|
|
|
.map(|result| result.ghost().cloned()),
|
|
|
|
|
Ok(None)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// original implementation returns `Ok(validation_result)`
|
|
|
|
|
// with `validation_result.is_valid() == false`.
|
|
|
|
|
let result = finality_grandpa::validate_commit(
|
|
|
|
|
&justification.commit,
|
|
|
|
|
&full_voter_set(),
|
|
|
|
|
&AncestryChain::new(&justification.votes_ancestries),
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
|
|
assert!(!result.is_valid());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
@@ -158,21 +158,28 @@ fn same_result_when_precommit_target_is_not_descendant_of_commit_target() {
|
|
|
|
|
),
|
|
|
|
|
Err(Error::PrecommitIsNotCommitDescendant),
|
|
|
|
|
);
|
|
|
|
|
// original implementation returns empty GHOST
|
|
|
|
|
assert_matches!(
|
|
|
|
|
finality_grandpa::validate_commit(
|
|
|
|
|
&justification.commit,
|
|
|
|
|
&full_voter_set(),
|
|
|
|
|
&AncestryChain::new(&justification.votes_ancestries),
|
|
|
|
|
)
|
|
|
|
|
.map(|result| result.ghost().cloned()),
|
|
|
|
|
Ok(None)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// original implementation returns `Ok(validation_result)`
|
|
|
|
|
// with `validation_result.is_valid() == false`.
|
|
|
|
|
let result = finality_grandpa::validate_commit(
|
|
|
|
|
&justification.commit,
|
|
|
|
|
&full_voter_set(),
|
|
|
|
|
&AncestryChain::new(&justification.votes_ancestries),
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
|
|
assert!(!result.is_valid());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn same_result_when_justification_contains_duplicate_vote() {
|
|
|
|
|
let mut justification = make_default_justification(&test_header(1));
|
|
|
|
|
let mut justification = make_justification_for_header(JustificationGeneratorParams {
|
|
|
|
|
header: test_header(1),
|
|
|
|
|
authorities: minimal_accounts_set(),
|
|
|
|
|
ancestors: 0,
|
|
|
|
|
..Default::default()
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// the justification may contain exactly the same vote (i.e. same precommit and same signature)
|
|
|
|
|
// multiple times && it isn't treated as an error by original implementation
|
|
|
|
|
justification.commit.precommits.push(justification.commit.precommits[0].clone());
|
|
|
|
@@ -188,21 +195,28 @@ fn same_result_when_justification_contains_duplicate_vote() {
|
|
|
|
|
),
|
|
|
|
|
Ok(()),
|
|
|
|
|
);
|
|
|
|
|
// original implementation returns non-empty GHOST
|
|
|
|
|
assert_matches!(
|
|
|
|
|
finality_grandpa::validate_commit(
|
|
|
|
|
&justification.commit,
|
|
|
|
|
&full_voter_set(),
|
|
|
|
|
&AncestryChain::new(&justification.votes_ancestries),
|
|
|
|
|
)
|
|
|
|
|
.map(|result| result.ghost().cloned()),
|
|
|
|
|
Ok(Some(_))
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// original implementation returns `Ok(validation_result)`
|
|
|
|
|
// with `validation_result.is_valid() == true`.
|
|
|
|
|
let result = finality_grandpa::validate_commit(
|
|
|
|
|
&justification.commit,
|
|
|
|
|
&full_voter_set(),
|
|
|
|
|
&AncestryChain::new(&justification.votes_ancestries),
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
|
|
assert!(result.is_valid());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn same_result_when_authority_equivocates_once_in_a_round() {
|
|
|
|
|
let mut justification = make_default_justification(&test_header(1));
|
|
|
|
|
let mut justification = make_justification_for_header(JustificationGeneratorParams {
|
|
|
|
|
header: test_header(1),
|
|
|
|
|
authorities: minimal_accounts_set(),
|
|
|
|
|
ancestors: 0,
|
|
|
|
|
..Default::default()
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// the justification original implementation allows authority to submit two different
|
|
|
|
|
// votes in a single round, of which only first is 'accepted'
|
|
|
|
|
justification.commit.precommits.push(signed_precommit::<TestHeader>(
|
|
|
|
@@ -222,21 +236,28 @@ fn same_result_when_authority_equivocates_once_in_a_round() {
|
|
|
|
|
),
|
|
|
|
|
Ok(()),
|
|
|
|
|
);
|
|
|
|
|
// original implementation returns non-empty GHOST
|
|
|
|
|
assert_matches!(
|
|
|
|
|
finality_grandpa::validate_commit(
|
|
|
|
|
&justification.commit,
|
|
|
|
|
&full_voter_set(),
|
|
|
|
|
&AncestryChain::new(&justification.votes_ancestries),
|
|
|
|
|
)
|
|
|
|
|
.map(|result| result.ghost().cloned()),
|
|
|
|
|
Ok(Some(_))
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// original implementation returns `Ok(validation_result)`
|
|
|
|
|
// with `validation_result.is_valid() == true`.
|
|
|
|
|
let result = finality_grandpa::validate_commit(
|
|
|
|
|
&justification.commit,
|
|
|
|
|
&full_voter_set(),
|
|
|
|
|
&AncestryChain::new(&justification.votes_ancestries),
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
|
|
assert!(result.is_valid());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn same_result_when_authority_equivocates_twice_in_a_round() {
|
|
|
|
|
let mut justification = make_default_justification(&test_header(1));
|
|
|
|
|
let mut justification = make_justification_for_header(JustificationGeneratorParams {
|
|
|
|
|
header: test_header(1),
|
|
|
|
|
authorities: minimal_accounts_set(),
|
|
|
|
|
ancestors: 0,
|
|
|
|
|
..Default::default()
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// there's some code in the original implementation that should return an error when
|
|
|
|
|
// same authority submits more than two different votes in a single round:
|
|
|
|
|
// https://github.com/paritytech/finality-grandpa/blob/6aeea2d1159d0f418f0b86e70739f2130629ca09/src/lib.rs#L473
|
|
|
|
@@ -266,16 +287,17 @@ fn same_result_when_authority_equivocates_twice_in_a_round() {
|
|
|
|
|
),
|
|
|
|
|
Ok(()),
|
|
|
|
|
);
|
|
|
|
|
// original implementation returns non-empty GHOST
|
|
|
|
|
assert_matches!(
|
|
|
|
|
finality_grandpa::validate_commit(
|
|
|
|
|
&justification.commit,
|
|
|
|
|
&full_voter_set(),
|
|
|
|
|
&AncestryChain::new(&justification.votes_ancestries),
|
|
|
|
|
)
|
|
|
|
|
.map(|result| result.ghost().cloned()),
|
|
|
|
|
Ok(Some(_))
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// original implementation returns `Ok(validation_result)`
|
|
|
|
|
// with `validation_result.is_valid() == true`.
|
|
|
|
|
let result = finality_grandpa::validate_commit(
|
|
|
|
|
&justification.commit,
|
|
|
|
|
&full_voter_set(),
|
|
|
|
|
&AncestryChain::new(&justification.votes_ancestries),
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
|
|
assert!(result.is_valid());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
@@ -299,14 +321,15 @@ fn same_result_when_there_are_not_enough_cumulative_weight_to_finalize_commit_ta
|
|
|
|
|
),
|
|
|
|
|
Err(Error::TooLowCumulativeWeight),
|
|
|
|
|
);
|
|
|
|
|
// original implementation returns empty GHOST
|
|
|
|
|
assert_matches!(
|
|
|
|
|
finality_grandpa::validate_commit(
|
|
|
|
|
&justification.commit,
|
|
|
|
|
&full_voter_set(),
|
|
|
|
|
&AncestryChain::new(&justification.votes_ancestries),
|
|
|
|
|
)
|
|
|
|
|
.map(|result| result.ghost().cloned()),
|
|
|
|
|
Ok(None)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// original implementation returns `Ok(validation_result)`
|
|
|
|
|
// with `validation_result.is_valid() == false`.
|
|
|
|
|
let result = finality_grandpa::validate_commit(
|
|
|
|
|
&justification.commit,
|
|
|
|
|
&full_voter_set(),
|
|
|
|
|
&AncestryChain::new(&justification.votes_ancestries),
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
|
|
assert!(!result.is_valid());
|
|
|
|
|
}
|
|
|
|
|