use super::*;
use crate::{
configuration::HostConfiguration,
disputes::DisputesHandler,
mock::{
new_test_ext, AccountId, AllPalletsWithSystem, Initializer, MockGenesisConfig, System,
Test, PUNISH_BACKERS_FOR, PUNISH_VALIDATORS_AGAINST, PUNISH_VALIDATORS_FOR,
REWARD_VALIDATORS,
},
};
use pezframe_support::{
assert_err, assert_noop, assert_ok,
traits::{OnFinalize, OnInitialize},
};
use pezframe_system::pezpallet_prelude::BlockNumberFor;
use pezkuwi_primitives::BlockNumber;
use pezsp_core::{crypto::CryptoType, Pair};
const VOTE_FOR: VoteKind = VoteKind::ExplicitValid;
const VOTE_AGAINST: VoteKind = VoteKind::Invalid;
const VOTE_BACKING: VoteKind = VoteKind::Backing;
fn filter_dispute_set(stmts: MultiDisputeStatementSet) -> CheckedMultiDisputeStatementSet {
let config = configuration::ActiveConfig::<Test>::get();
let post_conclusion_acceptance_period = config.dispute_post_conclusion_acceptance_period;
stmts
.into_iter()
.filter_map(|set| {
let filter =
Pezpallet::<Test>::filter_dispute_data(&set, post_conclusion_acceptance_period);
filter.filter_statement_set(set)
})
.collect::<Vec<_>>()
}
fn contains_duplicates_in_sorted_iter<
'a,
T: 'a,
I: 'a + IntoIterator<Item = &'a T>,
C: 'static + FnMut(&T, &T) -> bool,
>(
iter: I,
mut check_equal: C,
) -> bool {
let mut iter = iter.into_iter();
if let Some(mut previous) = iter.next() {
while let Some(current) = iter.next() {
if check_equal(previous, current) {
return true;
}
previous = current;
}
}
return false;
}
type NewSession<'a> = (
bool,
SessionIndex,
Vec<(&'a AccountId, ValidatorId)>,
Option<Vec<(&'a AccountId, ValidatorId)>>,
);
pub(crate) fn run_to_block<'a>(
to: BlockNumber,
new_session: impl Fn(BlockNumber) -> Option<NewSession<'a>>,
) {
while System::block_number() < to {
let b = System::block_number();
if b != 0 {
crate::paras_inherent::Included::<Test>::set(Some(()));
AllPalletsWithSystem::on_finalize(b);
System::finalize();
}
System::reset_events();
System::initialize(&(b + 1), &Default::default(), &Default::default());
AllPalletsWithSystem::on_initialize(b + 1);
if let Some(new_session) = new_session(b + 1) {
Initializer::test_trigger_on_new_session(
new_session.0,
new_session.1,
new_session.2.into_iter(),
new_session.3.map(|q| q.into_iter()),
);
}
}
}
#[test]
fn test_contains_duplicates_in_sorted_iter() {
let v = vec![1, 2, 3, 5, 5, 8];
assert_eq!(true, contains_duplicates_in_sorted_iter(&v, |a, b| a == b));
let v = vec![1, 2, 3, 4];
assert_eq!(false, contains_duplicates_in_sorted_iter(&v, |a, b| a == b));
}
#[test]
fn test_dispute_state_flag_from_state() {
assert_eq!(
DisputeStateFlags::from_state(&DisputeState {
validators_for: bitvec![u8, BitOrderLsb0; 0, 0, 0, 0, 0, 0, 0, 0],
validators_against: bitvec![u8, BitOrderLsb0; 0, 0, 0, 0, 0, 0, 0, 0],
start: 0,
concluded_at: None,
}),
DisputeStateFlags::default(),
);
assert_eq!(
DisputeStateFlags::from_state(&DisputeState {
validators_for: bitvec![u8, BitOrderLsb0; 1, 1, 1, 1, 1, 0, 0],
validators_against: bitvec![u8, BitOrderLsb0; 0, 0, 0, 0, 0, 0, 0],
start: 0,
concluded_at: None,
}),
DisputeStateFlags::FOR_SUPERMAJORITY | DisputeStateFlags::CONFIRMED,
);
assert_eq!(
DisputeStateFlags::from_state(&DisputeState {
validators_for: bitvec![u8, BitOrderLsb0; 0, 0, 0, 0, 0, 0, 0],
validators_against: bitvec![u8, BitOrderLsb0; 1, 1, 1, 0, 0, 0, 0],
start: 0,
concluded_at: None,
}),
DisputeStateFlags::CONFIRMED | DisputeStateFlags::AGAINST_BYZANTINE,
);
assert_eq!(
DisputeStateFlags::from_state(&DisputeState {
validators_for: bitvec![u8, BitOrderLsb0; 0, 0, 0, 0, 0, 0, 0],
validators_against: bitvec![u8, BitOrderLsb0; 1, 1, 1, 1, 1, 0, 0],
start: 0,
concluded_at: None,
}),
DisputeStateFlags::AGAINST_SUPERMAJORITY
| DisputeStateFlags::CONFIRMED
| DisputeStateFlags::AGAINST_BYZANTINE,
);
}
#[test]
fn test_import_new_participant() {
let mut importer = DisputeStateImporter::new(
DisputeState {
validators_for: bitvec![u8, BitOrderLsb0; 1, 0, 0, 0, 0, 0, 0, 0],
validators_against: bitvec![u8, BitOrderLsb0; 0, 0, 0, 0, 0, 0, 0, 0],
start: 0,
concluded_at: None,
},
BTreeSet::new(),
0,
);
assert_err!(
importer.import(ValidatorIndex(9), VOTE_FOR),
VoteImportError::ValidatorIndexOutOfBounds,
);
assert_err!(importer.import(ValidatorIndex(0), VOTE_FOR), VoteImportError::DuplicateStatement);
assert_ok!(importer.import(ValidatorIndex(0), VOTE_AGAINST));
assert_ok!(importer.import(ValidatorIndex(2), VOTE_FOR));
assert_err!(importer.import(ValidatorIndex(2), VOTE_FOR), VoteImportError::DuplicateStatement);
assert_ok!(importer.import(ValidatorIndex(2), VOTE_AGAINST));
assert_err!(
importer.import(ValidatorIndex(2), VOTE_AGAINST),
VoteImportError::DuplicateStatement
);
let summary = importer.finish();
assert_eq!(summary.new_flags, DisputeStateFlags::default());
assert_eq!(
summary.state,
DisputeState {
validators_for: bitvec![u8, BitOrderLsb0; 1, 0, 1, 0, 0, 0, 0, 0],
validators_against: bitvec![u8, BitOrderLsb0; 1, 0, 1, 0, 0, 0, 0, 0],
start: 0,
concluded_at: None,
},
);
assert!(summary.slash_for.is_empty());
assert!(summary.slash_against.is_empty());
assert_eq!(summary.new_participants, bitvec![u8, BitOrderLsb0; 0, 0, 1, 0, 0, 0, 0, 0]);
}
#[test]
fn test_import_prev_participant_confirmed() {
let mut importer = DisputeStateImporter::new(
DisputeState {
validators_for: bitvec![u8, BitOrderLsb0; 1, 0, 0, 0, 0, 0, 0, 0],
validators_against: bitvec![u8, BitOrderLsb0; 0, 1, 0, 0, 0, 0, 0, 0],
start: 0,
concluded_at: None,
},
BTreeSet::new(),
0,
);
assert_ok!(importer.import(ValidatorIndex(2), VOTE_FOR));
let summary = importer.finish();
assert_eq!(
summary.state,
DisputeState {
validators_for: bitvec![u8, BitOrderLsb0; 1, 0, 1, 0, 0, 0, 0, 0],
validators_against: bitvec![u8, BitOrderLsb0; 0, 1, 0, 0, 0, 0, 0, 0],
start: 0,
concluded_at: None,
},
);
assert!(summary.slash_for.is_empty());
assert!(summary.slash_against.is_empty());
assert_eq!(summary.new_participants, bitvec![u8, BitOrderLsb0; 0, 0, 1, 0, 0, 0, 0, 0]);
assert_eq!(summary.new_flags, DisputeStateFlags::CONFIRMED);
}
#[test]
fn test_import_prev_participant_confirmed_slash_for() {
let mut importer = DisputeStateImporter::new(
DisputeState {
validators_for: bitvec![u8, BitOrderLsb0; 1, 0, 0, 0, 0, 0, 0, 0],
validators_against: bitvec![u8, BitOrderLsb0; 0, 1, 0, 0, 0, 0, 0, 0],
start: 0,
concluded_at: None,
},
BTreeSet::new(),
0,
);
assert_ok!(importer.import(ValidatorIndex(2), VOTE_FOR));
assert_ok!(importer.import(ValidatorIndex(2), VOTE_AGAINST));
assert_ok!(importer.import(ValidatorIndex(3), VOTE_AGAINST));
assert_ok!(importer.import(ValidatorIndex(4), VOTE_AGAINST));
assert_ok!(importer.import(ValidatorIndex(5), VOTE_AGAINST));
assert_ok!(importer.import(ValidatorIndex(6), VOTE_AGAINST));
let summary = importer.finish();
assert_eq!(
summary.state,
DisputeState {
validators_for: bitvec![u8, BitOrderLsb0; 1, 0, 1, 0, 0, 0, 0, 0],
validators_against: bitvec![u8, BitOrderLsb0; 0, 1, 1, 1, 1, 1, 1, 0],
start: 0,
concluded_at: Some(0),
},
);
assert_eq!(summary.slash_for, vec![ValidatorIndex(0), ValidatorIndex(2)]);
assert!(summary.slash_against.is_empty());
assert_eq!(summary.new_participants, bitvec![u8, BitOrderLsb0; 0, 0, 1, 1, 1, 1, 1, 0]);
assert_eq!(
summary.new_flags,
DisputeStateFlags::CONFIRMED
| DisputeStateFlags::AGAINST_SUPERMAJORITY
| DisputeStateFlags::AGAINST_BYZANTINE,
);
}
#[test]
fn test_import_slash_against() {
let mut importer = DisputeStateImporter::new(
DisputeState {
validators_for: bitvec![u8, BitOrderLsb0; 1, 0, 1, 0, 0, 0, 0, 0],
validators_against: bitvec![u8, BitOrderLsb0; 0, 1, 0, 0, 0, 0, 0, 0],
start: 0,
concluded_at: None,
},
BTreeSet::new(),
0,
);
assert_ok!(importer.import(ValidatorIndex(3), VOTE_FOR));
assert_ok!(importer.import(ValidatorIndex(4), VOTE_FOR));
assert_ok!(importer.import(ValidatorIndex(5), VOTE_AGAINST));
assert_ok!(importer.import(ValidatorIndex(6), VOTE_FOR));
assert_ok!(importer.import(ValidatorIndex(7), VOTE_FOR));
let summary = importer.finish();
assert_eq!(
summary.state,
DisputeState {
validators_for: bitvec![u8, BitOrderLsb0; 1, 0, 1, 1, 1, 0, 1, 1],
validators_against: bitvec![u8, BitOrderLsb0; 0, 1, 0, 0, 0, 1, 0, 0],
start: 0,
concluded_at: Some(0),
},
);
assert!(summary.slash_for.is_empty());
assert_eq!(summary.slash_against, vec![ValidatorIndex(1), ValidatorIndex(5)]);
assert_eq!(summary.new_participants, bitvec![u8, BitOrderLsb0; 0, 0, 0, 1, 1, 1, 1, 1]);
assert_eq!(summary.new_flags, DisputeStateFlags::FOR_SUPERMAJORITY);
}
#[test]
fn test_import_backing_votes() {
let mut importer = DisputeStateImporter::new(
DisputeState {
validators_for: bitvec![u8, BitOrderLsb0; 1, 0, 1, 0, 0, 0, 0, 0],
validators_against: bitvec![u8, BitOrderLsb0; 0, 1, 0, 0, 0, 0, 0, 0],
start: 0,
concluded_at: None,
},
BTreeSet::from_iter([ValidatorIndex(0)]),
0,
);
assert_ok!(importer.import(ValidatorIndex(3), VOTE_FOR));
assert_ok!(importer.import(ValidatorIndex(3), VOTE_BACKING));
assert_ok!(importer.import(ValidatorIndex(3), VOTE_AGAINST));
assert_ok!(importer.import(ValidatorIndex(6), VOTE_FOR));
assert_ok!(importer.import(ValidatorIndex(7), VOTE_BACKING));
assert_err!(
importer.import(ValidatorIndex(0), VOTE_BACKING),
VoteImportError::DuplicateStatement,
);
assert_err!(importer.import(ValidatorIndex(7), VOTE_FOR), VoteImportError::MaliciousBacker,);
let summary = importer.finish();
assert_eq!(
summary.state,
DisputeState {
validators_for: bitvec![u8, BitOrderLsb0; 1, 0, 1, 1, 0, 0, 1, 1],
validators_against: bitvec![u8, BitOrderLsb0; 0, 1, 0, 1, 0, 0, 0, 0],
start: 0,
concluded_at: None,
},
);
assert_eq!(
summary.backers,
BTreeSet::from_iter([ValidatorIndex(0), ValidatorIndex(3), ValidatorIndex(7),]),
);
}
#[test]
fn test_initializer_on_new_session() {
let dispute_period = 3;
let mock_genesis_config = MockGenesisConfig {
configuration: crate::configuration::GenesisConfig {
config: HostConfiguration { dispute_period, ..Default::default() },
},
..Default::default()
};
new_test_ext(mock_genesis_config).execute_with(|| {
let v0 = <ValidatorId as CryptoType>::Pair::generate().0;
let candidate_hash = CandidateHash(pezsp_core::H256::repeat_byte(1));
Pezpallet::<Test>::note_included(0, candidate_hash, 0);
Pezpallet::<Test>::note_included(1, candidate_hash, 1);
Pezpallet::<Test>::note_included(2, candidate_hash, 2);
Pezpallet::<Test>::note_included(3, candidate_hash, 3);
Pezpallet::<Test>::note_included(4, candidate_hash, 4);
Pezpallet::<Test>::note_included(5, candidate_hash, 5);
Pezpallet::<Test>::note_included(6, candidate_hash, 5);
run_to_block(7, |b| {
Some((true, b, vec![(&0, v0.public())], Some(vec![(&0, v0.public())])))
});
assert_eq!(Included::<Test>::iter_prefix(0).count(), 0);
assert_eq!(Included::<Test>::iter_prefix(1).count(), 0);
assert_eq!(Included::<Test>::iter_prefix(2).count(), 0);
assert_eq!(Included::<Test>::iter_prefix(3).count(), 1);
assert_eq!(Included::<Test>::iter_prefix(4).count(), 1);
assert_eq!(Included::<Test>::iter_prefix(5).count(), 1);
assert_eq!(Included::<Test>::iter_prefix(6).count(), 1);
});
}
#[test]
fn test_provide_data_duplicate_error() {
new_test_ext(Default::default()).execute_with(|| {
let candidate_hash_1 = CandidateHash(pezsp_core::H256::repeat_byte(1));
let candidate_hash_2 = CandidateHash(pezsp_core::H256::repeat_byte(2));
let mut stmts = vec![
DisputeStatementSet {
candidate_hash: candidate_hash_2,
session: 2,
statements: vec![],
},
DisputeStatementSet {
candidate_hash: candidate_hash_1,
session: 1,
statements: vec![],
},
DisputeStatementSet {
candidate_hash: candidate_hash_2,
session: 2,
statements: vec![],
},
];
assert!(Pezpallet::<Test>::deduplicate_and_sort_dispute_data(&mut stmts).is_err());
assert_eq!(stmts.len(), 2);
})
}
#[test]
fn test_provide_multi_dispute_is_providing() {
new_test_ext(Default::default()).execute_with(|| {
let v0 = <ValidatorId as CryptoType>::Pair::generate().0;
let v1 = <ValidatorId as CryptoType>::Pair::generate().0;
run_to_block(3, |b| {
if b == 1 {
Some((
true,
b,
vec![(&0, v0.public()), (&1, v1.public())],
Some(vec![(&0, v0.public()), (&1, v1.public())]),
))
} else {
Some((true, b, vec![(&1, v1.public())], Some(vec![(&1, v1.public())])))
}
});
let candidate_hash = CandidateHash(pezsp_core::H256::repeat_byte(1));
let inclusion_parent = pezsp_core::H256::repeat_byte(0xff);
let session = 1;
let stmts = vec![DisputeStatementSet {
candidate_hash,
session,
statements: vec![
(
DisputeStatement::Valid(ValidDisputeStatementKind::BackingValid(
inclusion_parent,
)),
ValidatorIndex(0),
v0.sign(&CompactStatement::Valid(candidate_hash).signing_payload(
&SigningContext { session_index: session, parent_hash: inclusion_parent },
)),
),
(
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
ValidatorIndex(1),
v1.sign(
&ExplicitDisputeStatement { valid: false, candidate_hash, session }
.signing_payload(),
),
),
],
}];
assert_ok!(
Pezpallet::<Test>::process_checked_multi_dispute_data(
&stmts
.into_iter()
.map(CheckedDisputeStatementSet::unchecked_from_unchecked)
.collect()
),
vec![(1, candidate_hash)],
);
})
}
#[test]
fn test_disputes_with_missing_backing_votes_are_rejected() {
new_test_ext(Default::default()).execute_with(|| {
let v0 = <ValidatorId as CryptoType>::Pair::generate().0;
let v1 = <ValidatorId as CryptoType>::Pair::generate().0;
run_to_block(3, |b| {
if b == 1 {
Some((
true,
b,
vec![(&0, v0.public()), (&1, v1.public())],
Some(vec![(&0, v0.public()), (&1, v1.public())]),
))
} else {
Some((true, b, vec![(&1, v1.public())], Some(vec![(&1, v1.public())])))
}
});
let candidate_hash = CandidateHash(pezsp_core::H256::repeat_byte(1));
let session = 1;
let stmts = vec![DisputeStatementSet {
candidate_hash,
session,
statements: vec![
(
DisputeStatement::Valid(ValidDisputeStatementKind::Explicit),
ValidatorIndex(0),
v0.sign(
&ExplicitDisputeStatement { valid: true, candidate_hash, session }
.signing_payload(),
),
),
(
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
ValidatorIndex(1),
v1.sign(
&ExplicitDisputeStatement { valid: false, candidate_hash, session }
.signing_payload(),
),
),
],
}];
assert!(Pezpallet::<Test>::process_checked_multi_dispute_data(
&stmts
.into_iter()
.map(CheckedDisputeStatementSet::unchecked_from_unchecked)
.collect()
)
.is_err(),);
})
}
#[test]
fn test_freeze_on_note_included() {
new_test_ext(Default::default()).execute_with(|| {
let v0 = <ValidatorId as CryptoType>::Pair::generate().0;
let v1 = <ValidatorId as CryptoType>::Pair::generate().0;
run_to_block(6, |b| {
Some((
true,
b,
vec![(&0, v0.public()), (&1, v1.public())],
Some(vec![(&0, v0.public()), (&1, v1.public())]),
))
});
let candidate_hash = CandidateHash(pezsp_core::H256::repeat_byte(1));
let inclusion_parent = pezsp_core::H256::repeat_byte(0xff);
let session = 3;
let stmts = vec![DisputeStatementSet {
candidate_hash,
session: 3,
statements: vec![
(
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
ValidatorIndex(0),
v0.sign(
&ExplicitDisputeStatement { valid: false, candidate_hash, session: 3 }
.signing_payload(),
),
),
(
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
ValidatorIndex(1),
v1.sign(
&ExplicitDisputeStatement { valid: false, candidate_hash, session: 3 }
.signing_payload(),
),
),
(
DisputeStatement::Valid(ValidDisputeStatementKind::BackingValid(
inclusion_parent,
)),
ValidatorIndex(1),
v0.sign(&CompactStatement::Valid(candidate_hash).signing_payload(
&SigningContext { session_index: session, parent_hash: inclusion_parent },
)),
),
],
}];
assert!(Pezpallet::<Test>::process_checked_multi_dispute_data(
&stmts
.into_iter()
.map(CheckedDisputeStatementSet::unchecked_from_unchecked)
.collect()
)
.is_ok());
Pezpallet::<Test>::note_included(3, candidate_hash, 3);
assert_eq!(Frozen::<Test>::get(), Some(2));
});
}
#[test]
fn test_freeze_provided_against_supermajority_for_included() {
new_test_ext(Default::default()).execute_with(|| {
let v0 = <ValidatorId as CryptoType>::Pair::generate().0;
let v1 = <ValidatorId as CryptoType>::Pair::generate().0;
run_to_block(6, |b| {
Some((
true,
b,
vec![(&0, v0.public()), (&1, v1.public())],
Some(vec![(&0, v0.public()), (&1, v1.public())]),
))
});
let candidate_hash = CandidateHash(pezsp_core::H256::repeat_byte(1));
let inclusion_parent = pezsp_core::H256::repeat_byte(0xff);
let session = 3;
let stmts = vec![DisputeStatementSet {
candidate_hash,
session,
statements: vec![
(
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
ValidatorIndex(0),
v0.sign(
&ExplicitDisputeStatement { valid: false, candidate_hash, session }
.signing_payload(),
),
),
(
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
ValidatorIndex(1),
v1.sign(
&ExplicitDisputeStatement { valid: false, candidate_hash, session }
.signing_payload(),
),
),
(
DisputeStatement::Valid(ValidDisputeStatementKind::BackingValid(
inclusion_parent,
)),
ValidatorIndex(1),
v0.sign(&CompactStatement::Valid(candidate_hash).signing_payload(
&SigningContext { session_index: session, parent_hash: inclusion_parent },
)),
),
],
}];
Pezpallet::<Test>::note_included(3, candidate_hash, 3);
assert!(Pezpallet::<Test>::process_checked_multi_dispute_data(
&stmts
.into_iter()
.map(CheckedDisputeStatementSet::unchecked_from_unchecked)
.collect()
)
.is_ok());
assert_eq!(Frozen::<Test>::get(), Some(2));
});
}
#[test]
fn test_freeze_provided_against_byzantine_threshold_for_included() {
new_test_ext(Default::default()).execute_with(|| {
let v0 = <ValidatorId as CryptoType>::Pair::generate().0;
let v1 = <ValidatorId as CryptoType>::Pair::generate().0;
let v2 = <ValidatorId as CryptoType>::Pair::generate().0;
let v3 = <ValidatorId as CryptoType>::Pair::generate().0;
let v4 = <ValidatorId as CryptoType>::Pair::generate().0;
let v5 = <ValidatorId as CryptoType>::Pair::generate().0;
let v6 = <ValidatorId as CryptoType>::Pair::generate().0;
let active_set = vec![
(&0, v0.public()),
(&1, v1.public()),
(&2, v2.public()),
(&3, v3.public()),
(&4, v4.public()),
(&5, v5.public()),
(&6, v6.public()),
];
run_to_block(6, |b| Some((true, b, active_set.clone(), Some(active_set.clone()))));
let candidate_hash = CandidateHash(pezsp_core::H256::repeat_byte(1));
let inclusion_parent = pezsp_core::H256::repeat_byte(0xff);
let session = 3;
let stmts = vec![DisputeStatementSet {
candidate_hash,
session,
statements: vec![
(
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
ValidatorIndex(0),
v0.sign(
&ExplicitDisputeStatement { valid: false, candidate_hash, session }
.signing_payload(),
),
),
(
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
ValidatorIndex(1),
v1.sign(
&ExplicitDisputeStatement { valid: false, candidate_hash, session }
.signing_payload(),
),
),
(
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
ValidatorIndex(2),
v2.sign(
&ExplicitDisputeStatement { valid: false, candidate_hash, session }
.signing_payload(),
),
),
(
DisputeStatement::Valid(ValidDisputeStatementKind::BackingValid(
inclusion_parent,
)),
ValidatorIndex(1),
v0.sign(&CompactStatement::Valid(candidate_hash).signing_payload(
&SigningContext { session_index: session, parent_hash: inclusion_parent },
)),
),
],
}];
Pezpallet::<Test>::note_included(3, candidate_hash, 3);
assert!(Pezpallet::<Test>::process_checked_multi_dispute_data(
&stmts
.into_iter()
.map(CheckedDisputeStatementSet::unchecked_from_unchecked)
.collect()
)
.is_ok());
assert_eq!(Frozen::<Test>::get(), Some(2));
run_to_block(7, |b| Some((true, b, active_set.clone(), Some(active_set.clone()))));
Pezpallet::<Test>::note_included(3, CandidateHash(pezsp_core::H256::repeat_byte(2)), 3);
let stmts = vec![DisputeStatementSet {
candidate_hash,
session,
statements: vec![
(
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
ValidatorIndex(3),
v3.sign(
&ExplicitDisputeStatement { valid: false, candidate_hash, session }
.signing_payload(),
),
),
(
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
ValidatorIndex(4),
v4.sign(
&ExplicitDisputeStatement { valid: false, candidate_hash, session }
.signing_payload(),
),
),
(
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
ValidatorIndex(5),
v5.sign(
&ExplicitDisputeStatement { valid: false, candidate_hash, session }
.signing_payload(),
),
),
],
}];
assert!(Pezpallet::<Test>::process_checked_multi_dispute_data(
&stmts
.into_iter()
.map(CheckedDisputeStatementSet::unchecked_from_unchecked)
.collect()
)
.is_ok());
assert_eq!(Frozen::<Test>::get(), Some(2));
});
}
mod unconfirmed_disputes {
use super::*;
use assert_matches::assert_matches;
use pezsp_runtime::ModuleError;
fn generate_dispute_statement_set_and_run_to_block() -> DisputeStatementSet {
let v0 = <ValidatorId as CryptoType>::Pair::generate().0;
let v1 = <ValidatorId as CryptoType>::Pair::generate().0;
let v2 = <ValidatorId as CryptoType>::Pair::generate().0;
let v3 = <ValidatorId as CryptoType>::Pair::generate().0;
let v4 = <ValidatorId as CryptoType>::Pair::generate().0;
let v5 = <ValidatorId as CryptoType>::Pair::generate().0;
let v6 = <ValidatorId as CryptoType>::Pair::generate().0;
run_to_block(6, |b| {
Some((
true,
b,
vec![
(&0, v0.public()),
(&1, v1.public()),
(&2, v2.public()),
(&3, v3.public()),
(&4, v4.public()),
(&5, v5.public()),
(&6, v6.public()),
],
Some(vec![
(&0, v0.public()),
(&1, v1.public()),
(&2, v2.public()),
(&3, v3.public()),
(&4, v4.public()),
(&5, v5.public()),
(&6, v6.public()),
]),
))
});
let candidate_hash = CandidateHash(pezsp_core::H256::repeat_byte(1));
DisputeStatementSet {
candidate_hash,
session: 4,
statements: vec![
(
DisputeStatement::Valid(ValidDisputeStatementKind::Explicit),
ValidatorIndex(0),
v0.sign(
&ExplicitDisputeStatement { valid: true, candidate_hash, session: 4 }
.signing_payload(),
),
),
(
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
ValidatorIndex(3),
v1.sign(
&ExplicitDisputeStatement { valid: false, candidate_hash, session: 4 }
.signing_payload(),
),
),
],
}
}
#[test]
fn test_unconfirmed_are_ignored() {
new_test_ext(Default::default()).execute_with(|| {
let stmts = vec![generate_dispute_statement_set_and_run_to_block()];
let stmts = filter_dispute_set(stmts);
assert_ok!(Pezpallet::<Test>::process_checked_multi_dispute_data(&stmts), vec![],);
});
}
#[test]
fn test_unconfirmed_disputes_cause_block_import_error() {
new_test_ext(Default::default()).execute_with(|| {
let stmts = generate_dispute_statement_set_and_run_to_block();
let stmts = vec![CheckedDisputeStatementSet::unchecked_from_unchecked(stmts)];
assert_matches!(
Pezpallet::<Test>::process_checked_multi_dispute_data(&stmts),
Err(DispatchError::Module(ModuleError{index: _, error: _, message})) => assert_eq!(message, Some("UnconfirmedDispute"))
);
});
}
}
#[test]
fn test_provide_multi_dispute_success_and_other() {
new_test_ext(Default::default()).execute_with(|| {
let v0 = <ValidatorId as CryptoType>::Pair::generate().0;
let v1 = <ValidatorId as CryptoType>::Pair::generate().0;
let v2 = <ValidatorId as CryptoType>::Pair::generate().0;
let v3 = <ValidatorId as CryptoType>::Pair::generate().0;
let v4 = <ValidatorId as CryptoType>::Pair::generate().0;
let v5 = <ValidatorId as CryptoType>::Pair::generate().0;
let v6 = <ValidatorId as CryptoType>::Pair::generate().0;
run_to_block(6, |b| {
Some((
true,
b,
vec![
(&0, v0.public()),
(&1, v1.public()),
(&2, v2.public()),
(&3, v3.public()),
(&4, v4.public()),
(&5, v5.public()),
(&6, v6.public()),
],
Some(vec![
(&0, v0.public()),
(&1, v1.public()),
(&2, v2.public()),
(&3, v3.public()),
(&4, v4.public()),
(&5, v5.public()),
(&6, v6.public()),
]),
))
});
let candidate_hash = CandidateHash(pezsp_core::H256::repeat_byte(1));
let inclusion_parent = pezsp_core::H256::repeat_byte(0xff);
let session = 3;
let stmts = vec![DisputeStatementSet {
candidate_hash,
session,
statements: vec![
(
DisputeStatement::Valid(ValidDisputeStatementKind::BackingValid(
inclusion_parent,
)),
ValidatorIndex(0),
v0.sign(&CompactStatement::Valid(candidate_hash).signing_payload(
&SigningContext { session_index: session, parent_hash: inclusion_parent },
)),
),
(
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
ValidatorIndex(2),
v6.sign(
&ExplicitDisputeStatement { valid: false, candidate_hash, session }
.signing_payload(),
),
),
(
DisputeStatement::Valid(ValidDisputeStatementKind::Explicit),
ValidatorIndex(3),
v1.sign(
&ExplicitDisputeStatement { valid: true, candidate_hash, session: 3 }
.signing_payload(),
),
),
],
}];
let stmts = filter_dispute_set(stmts);
assert_ok!(
Pezpallet::<Test>::process_checked_multi_dispute_data(&stmts),
vec![(3, candidate_hash)],
);
let stmts = vec![
DisputeStatementSet {
candidate_hash,
session: 3,
statements: vec![(
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
ValidatorIndex(5),
v3.sign(
&ExplicitDisputeStatement { valid: false, candidate_hash, session: 3 }
.signing_payload(),
),
)],
},
DisputeStatementSet {
candidate_hash,
session: 5,
statements: vec![
(
DisputeStatement::Valid(ValidDisputeStatementKind::BackingValid(
inclusion_parent,
)),
ValidatorIndex(5),
v3.sign(&CompactStatement::Valid(candidate_hash).signing_payload(
&SigningContext { session_index: 5, parent_hash: inclusion_parent },
)),
),
(
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
ValidatorIndex(6),
v2.sign(
&ExplicitDisputeStatement { valid: false, candidate_hash, session: 5 }
.signing_payload(),
),
),
(
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
ValidatorIndex(2),
v6.sign(
&ExplicitDisputeStatement { valid: false, candidate_hash, session: 5 }
.signing_payload(),
),
),
],
},
];
let stmts = filter_dispute_set(stmts);
assert_ok!(
Pezpallet::<Test>::process_checked_multi_dispute_data(&stmts),
vec![(5, candidate_hash)],
);
let stmts = vec![DisputeStatementSet {
candidate_hash,
session: 3,
statements: vec![(
DisputeStatement::Valid(ValidDisputeStatementKind::Explicit),
ValidatorIndex(6),
v2.sign(
&ExplicitDisputeStatement { valid: true, candidate_hash, session: 3 }
.signing_payload(),
),
)],
}];
let stmts = filter_dispute_set(stmts);
assert_ok!(Pezpallet::<Test>::process_checked_multi_dispute_data(&stmts), vec![]);
let stmts = vec![
DisputeStatementSet {
candidate_hash,
session: 5,
statements: vec![
(
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
ValidatorIndex(0),
v0.sign(
&ExplicitDisputeStatement { valid: false, candidate_hash, session: 5 }
.signing_payload(),
),
),
(
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
ValidatorIndex(1),
v4.sign(
&ExplicitDisputeStatement { valid: false, candidate_hash, session: 5 }
.signing_payload(),
),
),
(
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
ValidatorIndex(4),
v5.sign(
&ExplicitDisputeStatement { valid: false, candidate_hash, session: 5 }
.signing_payload(),
),
),
],
},
DisputeStatementSet {
candidate_hash,
session: 3,
statements: vec![
(
DisputeStatement::Valid(ValidDisputeStatementKind::Explicit),
ValidatorIndex(1),
v4.sign(
&ExplicitDisputeStatement { valid: true, candidate_hash, session: 3 }
.signing_payload(),
),
),
(
DisputeStatement::Valid(ValidDisputeStatementKind::Explicit),
ValidatorIndex(4),
v5.sign(
&ExplicitDisputeStatement { valid: true, candidate_hash, session: 3 }
.signing_payload(),
),
),
],
},
];
let stmts = filter_dispute_set(stmts);
assert_ok!(Pezpallet::<Test>::process_checked_multi_dispute_data(&stmts), vec![]);
assert_eq!(
Pezpallet::<Test>::disputes(),
vec![
(
5,
candidate_hash,
DisputeState {
validators_for: bitvec![u8, BitOrderLsb0; 0, 0, 0, 0, 0, 1, 0],
validators_against: bitvec![u8, BitOrderLsb0; 1, 1, 1, 0, 1, 0, 1],
start: 6,
concluded_at: Some(6), }
),
(
3,
candidate_hash,
DisputeState {
validators_for: bitvec![u8, BitOrderLsb0; 1, 1, 0, 1, 1, 0, 1],
validators_against: bitvec![u8, BitOrderLsb0; 0, 0, 1, 0, 0, 1, 0],
start: 6,
concluded_at: Some(6), }
),
]
);
assert!(!Pezpallet::<Test>::concluded_invalid(3, candidate_hash));
assert!(!Pezpallet::<Test>::concluded_invalid(4, candidate_hash));
assert!(Pezpallet::<Test>::concluded_invalid(5, candidate_hash));
assert_eq!(
REWARD_VALIDATORS.with(|r| r.borrow().clone()),
vec![
(3, vec![ValidatorIndex(0), ValidatorIndex(2), ValidatorIndex(3)]),
(3, vec![ValidatorIndex(5)]),
(5, vec![ValidatorIndex(2), ValidatorIndex(5), ValidatorIndex(6)]),
(3, vec![ValidatorIndex(6)]),
(5, vec![ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(4)]),
(3, vec![ValidatorIndex(1), ValidatorIndex(4)]),
],
);
assert_eq!(
PUNISH_VALIDATORS_AGAINST.with(|r| r.borrow().clone()),
vec![
(3, vec![]),
(3, vec![]),
(5, vec![]),
(3, vec![]),
(5, vec![]),
(3, vec![ValidatorIndex(2), ValidatorIndex(5)]),
],
);
assert_eq!(
PUNISH_VALIDATORS_FOR.with(|r| r.borrow().clone()),
vec![
(3, vec![]),
(3, vec![]),
(5, vec![]),
(3, vec![]),
(5, vec![ValidatorIndex(5)]),
(3, vec![]),
],
);
})
}
#[test]
fn test_punish_post_conclusion() {
new_test_ext(Default::default()).execute_with(|| {
let v0 = <ValidatorId as CryptoType>::Pair::generate().0;
let v1 = <ValidatorId as CryptoType>::Pair::generate().0;
let v2 = <ValidatorId as CryptoType>::Pair::generate().0;
let v3 = <ValidatorId as CryptoType>::Pair::generate().0;
let v4 = <ValidatorId as CryptoType>::Pair::generate().0;
let v5 = <ValidatorId as CryptoType>::Pair::generate().0;
let v6 = <ValidatorId as CryptoType>::Pair::generate().0;
run_to_block(6, |b| {
Some((
true,
b,
vec![
(&0, v0.public()),
(&1, v1.public()),
(&2, v2.public()),
(&3, v3.public()),
(&4, v4.public()),
(&5, v5.public()),
(&6, v6.public()),
],
Some(vec![
(&0, v0.public()),
(&1, v1.public()),
(&2, v2.public()),
(&3, v3.public()),
(&4, v4.public()),
(&5, v5.public()),
(&6, v6.public()),
]),
))
});
let candidate_hash = CandidateHash(pezsp_core::H256::repeat_byte(1));
let inclusion_parent = pezsp_core::H256::repeat_byte(0xff);
let session = 3;
let stmts = vec![DisputeStatementSet {
candidate_hash,
session,
statements: vec![
(
DisputeStatement::Valid(ValidDisputeStatementKind::BackingValid(
inclusion_parent,
)),
ValidatorIndex(0),
v0.sign(&CompactStatement::Valid(candidate_hash).signing_payload(
&SigningContext { session_index: session, parent_hash: inclusion_parent },
)),
),
(
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
ValidatorIndex(1),
v4.sign(
&ExplicitDisputeStatement { valid: false, candidate_hash, session }
.signing_payload(),
),
),
(
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
ValidatorIndex(2),
v6.sign(
&ExplicitDisputeStatement { valid: false, candidate_hash, session }
.signing_payload(),
),
),
(
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
ValidatorIndex(6),
v2.sign(
&ExplicitDisputeStatement { valid: false, candidate_hash, session }
.signing_payload(),
),
),
(
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
ValidatorIndex(4),
v5.sign(
&ExplicitDisputeStatement { valid: false, candidate_hash, session }
.signing_payload(),
),
),
(
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
ValidatorIndex(5),
v3.sign(
&ExplicitDisputeStatement { valid: false, candidate_hash, session }
.signing_payload(),
),
),
(
DisputeStatement::Valid(ValidDisputeStatementKind::ApprovalChecking),
ValidatorIndex(3),
v1.sign(&ApprovalVote(candidate_hash).signing_payload(session)),
),
],
}];
let stmts = filter_dispute_set(stmts);
assert_ok!(
Pezpallet::<Test>::process_checked_multi_dispute_data(&stmts),
vec![(session, candidate_hash)],
);
assert_eq!(
PUNISH_VALIDATORS_FOR.with(|r| r.borrow().clone()),
vec![(session, vec![ValidatorIndex(0), ValidatorIndex(3)]),],
);
assert_eq!(
PUNISH_BACKERS_FOR.with(|r| r.borrow().clone()),
vec![(session, vec![ValidatorIndex(0)]),],
);
let stmts = vec![DisputeStatementSet {
candidate_hash,
session,
statements: vec![
(
DisputeStatement::Valid(ValidDisputeStatementKind::BackingValid(
inclusion_parent,
)),
ValidatorIndex(3),
v1.sign(&CompactStatement::Valid(candidate_hash).signing_payload(
&SigningContext { session_index: session, parent_hash: inclusion_parent },
)),
),
(
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
ValidatorIndex(6),
v2.sign(
&ExplicitDisputeStatement { valid: false, candidate_hash, session }
.signing_payload(),
),
),
],
}];
let stmts = filter_dispute_set(stmts);
assert_ok!(Pezpallet::<Test>::process_checked_multi_dispute_data(&stmts), vec![],);
assert_eq!(
PUNISH_VALIDATORS_FOR.with(|r| r.borrow().clone()),
vec![
(session, vec![ValidatorIndex(0), ValidatorIndex(3)]),
(session, vec![ValidatorIndex(3)]),
],
);
assert_eq!(
PUNISH_BACKERS_FOR.with(|r| r.borrow().clone()),
vec![
(session, vec![ValidatorIndex(0)]),
(session, vec![ValidatorIndex(0), ValidatorIndex(3)])
],
);
assert_eq!(
PUNISH_VALIDATORS_AGAINST.with(|r| r.borrow().clone()),
vec![(session, vec![]), (session, vec![]),],
);
})
}
#[test]
fn test_revert_and_freeze() {
new_test_ext(Default::default()).execute_with(|| {
System::set_block_number(1);
Frozen::<Test>::put(Some(0));
assert_noop!(
{
Pezpallet::<Test>::revert_and_freeze(0);
Result::<(), ()>::Err(()) },
(),
);
Frozen::<Test>::kill();
Pezpallet::<Test>::revert_and_freeze(0);
assert_eq!(Frozen::<Test>::get(), Some(0));
assert_eq!(System::digest().logs[0], ConsensusLog::Revert(1).into());
System::assert_has_event(Event::Revert(1).into());
})
}
#[test]
fn test_revert_and_freeze_merges() {
new_test_ext(Default::default()).execute_with(|| {
Frozen::<Test>::put(Some(10));
assert_noop!(
{
Pezpallet::<Test>::revert_and_freeze(10);
Result::<(), ()>::Err(()) },
(),
);
Pezpallet::<Test>::revert_and_freeze(8);
assert_eq!(Frozen::<Test>::get(), Some(8));
})
}
#[test]
fn test_has_supermajority_against() {
assert_eq!(
has_supermajority_against(&DisputeState {
validators_for: bitvec![u8, BitOrderLsb0; 1, 1, 0, 0, 0, 0, 0, 0],
validators_against: bitvec![u8, BitOrderLsb0; 1, 1, 1, 1, 1, 0, 0, 0],
start: 0,
concluded_at: None,
}),
false,
);
assert_eq!(
has_supermajority_against(&DisputeState {
validators_for: bitvec![u8, BitOrderLsb0; 1, 1, 0, 0, 0, 0, 0, 0],
validators_against: bitvec![u8, BitOrderLsb0; 1, 1, 1, 1, 1, 1, 0, 0],
start: 0,
concluded_at: None,
}),
true,
);
}
#[test]
fn test_check_signature() {
let validator_id = <ValidatorId as CryptoType>::Pair::generate().0;
let wrong_validator_id = <ValidatorId as CryptoType>::Pair::generate().0;
let session = 0;
let wrong_session = 1;
let candidate_hash = CandidateHash(pezsp_core::H256::repeat_byte(1));
let wrong_candidate_hash = CandidateHash(pezsp_core::H256::repeat_byte(2));
let inclusion_parent = pezsp_core::H256::repeat_byte(3);
let wrong_inclusion_parent = pezsp_core::H256::repeat_byte(4);
let statement_1 = DisputeStatement::Valid(ValidDisputeStatementKind::Explicit);
let statement_2 =
DisputeStatement::Valid(ValidDisputeStatementKind::BackingSeconded(inclusion_parent));
let wrong_statement_2 =
DisputeStatement::Valid(ValidDisputeStatementKind::BackingSeconded(wrong_inclusion_parent));
let statement_3 =
DisputeStatement::Valid(ValidDisputeStatementKind::BackingValid(inclusion_parent));
let wrong_statement_3 =
DisputeStatement::Valid(ValidDisputeStatementKind::BackingValid(wrong_inclusion_parent));
let statement_4 = DisputeStatement::Valid(ValidDisputeStatementKind::ApprovalChecking);
let statement_5 = DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit);
let signed_1 = validator_id
.sign(&ExplicitDisputeStatement { valid: true, candidate_hash, session }.signing_payload());
let signed_2 = validator_id.sign(&CompactStatement::Seconded(candidate_hash).signing_payload(
&SigningContext { session_index: session, parent_hash: inclusion_parent },
));
let signed_3 = validator_id.sign(&CompactStatement::Valid(candidate_hash).signing_payload(
&SigningContext { session_index: session, parent_hash: inclusion_parent },
));
let signed_4 = validator_id.sign(&ApprovalVote(candidate_hash).signing_payload(session));
let signed_5 = validator_id.sign(
&ExplicitDisputeStatement { valid: false, candidate_hash, session }.signing_payload(),
);
assert!(check_signature(
&validator_id.public(),
candidate_hash,
session,
&statement_1,
&signed_1,
true,
)
.is_ok());
assert!(check_signature(
&wrong_validator_id.public(),
candidate_hash,
session,
&statement_1,
&signed_1,
true
)
.is_err());
assert!(check_signature(
&validator_id.public(),
wrong_candidate_hash,
session,
&statement_1,
&signed_1,
true,
)
.is_err());
assert!(check_signature(
&validator_id.public(),
candidate_hash,
wrong_session,
&statement_1,
&signed_1,
true
)
.is_err());
assert!(check_signature(
&validator_id.public(),
candidate_hash,
session,
&statement_2,
&signed_1,
true,
)
.is_err());
assert!(check_signature(
&validator_id.public(),
candidate_hash,
session,
&statement_3,
&signed_1,
true
)
.is_err());
assert!(check_signature(
&validator_id.public(),
candidate_hash,
session,
&statement_4,
&signed_1,
true
)
.is_err());
assert!(check_signature(
&validator_id.public(),
candidate_hash,
session,
&statement_5,
&signed_1,
true,
)
.is_err());
assert!(check_signature(
&validator_id.public(),
candidate_hash,
session,
&statement_2,
&signed_2,
true,
)
.is_ok());
assert!(check_signature(
&wrong_validator_id.public(),
candidate_hash,
session,
&statement_2,
&signed_2,
true,
)
.is_err());
assert!(check_signature(
&validator_id.public(),
wrong_candidate_hash,
session,
&statement_2,
&signed_2,
true,
)
.is_err());
assert!(check_signature(
&validator_id.public(),
candidate_hash,
wrong_session,
&statement_2,
&signed_2,
true
)
.is_err());
assert!(check_signature(
&validator_id.public(),
candidate_hash,
session,
&wrong_statement_2,
&signed_2,
true,
)
.is_err());
assert!(check_signature(
&validator_id.public(),
candidate_hash,
session,
&statement_1,
&signed_2,
true,
)
.is_err());
assert!(check_signature(
&validator_id.public(),
candidate_hash,
session,
&statement_3,
&signed_2,
true,
)
.is_err());
assert!(check_signature(
&validator_id.public(),
candidate_hash,
session,
&statement_4,
&signed_2,
true,
)
.is_err());
assert!(check_signature(
&validator_id.public(),
candidate_hash,
session,
&statement_5,
&signed_2,
true,
)
.is_err());
assert!(check_signature(
&validator_id.public(),
candidate_hash,
session,
&statement_3,
&signed_3,
true,
)
.is_ok());
assert!(check_signature(
&wrong_validator_id.public(),
candidate_hash,
session,
&statement_3,
&signed_3,
true,
)
.is_err());
assert!(check_signature(
&validator_id.public(),
wrong_candidate_hash,
session,
&statement_3,
&signed_3,
true,
)
.is_err());
assert!(check_signature(
&validator_id.public(),
candidate_hash,
wrong_session,
&statement_3,
&signed_3,
true,
)
.is_err());
assert!(check_signature(
&validator_id.public(),
candidate_hash,
session,
&wrong_statement_3,
&signed_3,
true,
)
.is_err());
assert!(check_signature(
&validator_id.public(),
candidate_hash,
session,
&statement_1,
&signed_3,
true,
)
.is_err());
assert!(check_signature(
&validator_id.public(),
candidate_hash,
session,
&statement_2,
&signed_3,
true
)
.is_err());
assert!(check_signature(
&validator_id.public(),
candidate_hash,
session,
&statement_4,
&signed_3,
true,
)
.is_err());
assert!(check_signature(
&validator_id.public(),
candidate_hash,
session,
&statement_5,
&signed_3,
true,
)
.is_err());
assert!(check_signature(
&validator_id.public(),
candidate_hash,
session,
&statement_4,
&signed_4,
true,
)
.is_ok());
assert!(check_signature(
&wrong_validator_id.public(),
candidate_hash,
session,
&statement_4,
&signed_4,
true,
)
.is_err());
assert!(check_signature(
&validator_id.public(),
wrong_candidate_hash,
session,
&statement_4,
&signed_4,
true,
)
.is_err());
assert!(check_signature(
&validator_id.public(),
candidate_hash,
wrong_session,
&statement_4,
&signed_4,
true,
)
.is_err());
assert!(check_signature(
&validator_id.public(),
candidate_hash,
session,
&statement_1,
&signed_4,
true,
)
.is_err());
assert!(check_signature(
&validator_id.public(),
candidate_hash,
session,
&statement_2,
&signed_4,
true,
)
.is_err());
assert!(check_signature(
&validator_id.public(),
candidate_hash,
session,
&statement_3,
&signed_4,
true,
)
.is_err());
assert!(check_signature(
&validator_id.public(),
candidate_hash,
session,
&statement_5,
&signed_4,
true,
)
.is_err());
assert!(check_signature(
&validator_id.public(),
candidate_hash,
session,
&statement_5,
&signed_5,
true,
)
.is_ok());
assert!(check_signature(
&wrong_validator_id.public(),
candidate_hash,
session,
&statement_5,
&signed_5,
true,
)
.is_err());
assert!(check_signature(
&validator_id.public(),
wrong_candidate_hash,
session,
&statement_5,
&signed_5,
true,
)
.is_err());
assert!(check_signature(
&validator_id.public(),
candidate_hash,
wrong_session,
&statement_5,
&signed_5,
true,
)
.is_err());
assert!(check_signature(
&validator_id.public(),
candidate_hash,
session,
&statement_1,
&signed_5,
true,
)
.is_err());
assert!(check_signature(
&validator_id.public(),
candidate_hash,
session,
&statement_2,
&signed_5,
true,
)
.is_err());
assert!(check_signature(
&validator_id.public(),
candidate_hash,
session,
&statement_3,
&signed_5,
true,
)
.is_err());
assert!(check_signature(
&validator_id.public(),
candidate_hash,
session,
&statement_4,
&signed_5,
true,
)
.is_err());
}
#[test]
fn deduplication_and_sorting_works() {
new_test_ext(Default::default()).execute_with(|| {
let v0 = <ValidatorId as CryptoType>::Pair::generate().0;
let v1 = <ValidatorId as CryptoType>::Pair::generate().0;
let v2 = <ValidatorId as CryptoType>::Pair::generate().0;
let v3 = <ValidatorId as CryptoType>::Pair::generate().0;
run_to_block(3, |b| {
Some((
true,
b,
vec![(&0, v0.public()), (&1, v1.public()), (&2, v2.public()), (&3, v3.public())],
Some(vec![
(&0, v0.public()),
(&1, v1.public()),
(&2, v2.public()),
(&3, v3.public()),
]),
))
});
let candidate_hash_a = CandidateHash(pezsp_core::H256::repeat_byte(1));
let candidate_hash_b = CandidateHash(pezsp_core::H256::repeat_byte(2));
let candidate_hash_c = CandidateHash(pezsp_core::H256::repeat_byte(3));
let create_explicit_statement = |vidx: ValidatorIndex,
validator: &<ValidatorId as CryptoType>::Pair,
c_hash: &CandidateHash,
valid,
session| {
let payload = ExplicitDisputeStatement { valid, candidate_hash: *c_hash, session }
.signing_payload();
let sig = validator.sign(&payload);
(DisputeStatement::Valid(ValidDisputeStatementKind::Explicit), vidx, sig.clone())
};
let explicit_triple_a =
create_explicit_statement(ValidatorIndex(0), &v0, &candidate_hash_a, true, 1);
let explicit_triple_a_bad =
create_explicit_statement(ValidatorIndex(1), &v1, &candidate_hash_a, false, 1);
let explicit_triple_b =
create_explicit_statement(ValidatorIndex(0), &v0, &candidate_hash_b, true, 2);
let explicit_triple_b_bad =
create_explicit_statement(ValidatorIndex(1), &v1, &candidate_hash_b, false, 2);
let explicit_triple_c =
create_explicit_statement(ValidatorIndex(0), &v0, &candidate_hash_c, true, 2);
let explicit_triple_c_bad =
create_explicit_statement(ValidatorIndex(1), &v1, &candidate_hash_c, false, 2);
let mut disputes = vec![
DisputeStatementSet {
candidate_hash: candidate_hash_b,
session: 2,
statements: vec![explicit_triple_b.clone(), explicit_triple_b_bad.clone()],
},
DisputeStatementSet {
candidate_hash: candidate_hash_c,
session: 2,
statements: vec![explicit_triple_c, explicit_triple_c_bad],
},
DisputeStatementSet {
candidate_hash: candidate_hash_b,
session: 2,
statements: vec![explicit_triple_b.clone(), explicit_triple_b_bad.clone()],
},
DisputeStatementSet {
candidate_hash: candidate_hash_a,
session: 1,
statements: vec![explicit_triple_a, explicit_triple_a_bad],
},
];
let disputes_orig = disputes.clone();
<Pezpallet<Test> as DisputesHandler<BlockNumberFor<Test>>>::deduplicate_and_sort_dispute_data(
&mut disputes,
)
.unwrap_err();
assert_eq!(disputes_orig.len(), disputes.len() + 1);
let are_these_equal = |a: &DisputeStatementSet, b: &DisputeStatementSet| {
use core::cmp::Ordering;
let cmp =
a.session.cmp(&b.session).then_with(|| a.candidate_hash.cmp(&b.candidate_hash));
assert_ne!(cmp, Ordering::Greater);
cmp == Ordering::Equal
};
assert_eq!(false, contains_duplicates_in_sorted_iter(&disputes, are_these_equal));
})
}
fn apply_filter_all<T: Config, I: IntoIterator<Item = DisputeStatementSet>>(
sets: I,
) -> Vec<CheckedDisputeStatementSet> {
let config = configuration::ActiveConfig::<T>::get();
let post_conclusion_acceptance_period = config.dispute_post_conclusion_acceptance_period;
let mut acc = Vec::<CheckedDisputeStatementSet>::new();
for dispute_statement in sets {
if let Some(checked) =
<Pezpallet<T> as DisputesHandler<BlockNumberFor<T>>>::filter_dispute_data(
dispute_statement,
post_conclusion_acceptance_period,
) {
acc.push(checked);
}
}
acc
}
#[test]
fn filter_removes_duplicates_within_set() {
new_test_ext(Default::default()).execute_with(|| {
let v0 = <ValidatorId as CryptoType>::Pair::generate().0;
let v1 = <ValidatorId as CryptoType>::Pair::generate().0;
run_to_block(3, |b| {
Some((
true,
b,
vec![(&0, v0.public()), (&1, v1.public())],
Some(vec![(&0, v0.public()), (&1, v1.public())]),
))
});
let candidate_hash = CandidateHash(pezsp_core::H256::repeat_byte(1));
let payload =
ExplicitDisputeStatement { valid: true, candidate_hash, session: 1 }.signing_payload();
let payload_against =
ExplicitDisputeStatement { valid: false, candidate_hash, session: 1 }.signing_payload();
let sig_a = v0.sign(&payload);
let sig_b = v0.sign(&payload);
let sig_c = v0.sign(&payload);
let sig_d = v1.sign(&payload_against);
let statements = DisputeStatementSet {
candidate_hash,
session: 1,
statements: vec![
(
DisputeStatement::Valid(ValidDisputeStatementKind::Explicit),
ValidatorIndex(0),
sig_a.clone(),
),
(
DisputeStatement::Valid(ValidDisputeStatementKind::Explicit),
ValidatorIndex(0),
sig_b,
),
(
DisputeStatement::Valid(ValidDisputeStatementKind::Explicit),
ValidatorIndex(0),
sig_c,
),
(
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
ValidatorIndex(1),
sig_d.clone(),
),
],
};
let post_conclusion_acceptance_period = 10;
let statements =
<Pezpallet<Test> as DisputesHandler<BlockNumberFor<Test>>>::filter_dispute_data(
statements,
post_conclusion_acceptance_period,
);
assert_eq!(
statements,
Some(CheckedDisputeStatementSet::unchecked_from_unchecked(DisputeStatementSet {
candidate_hash,
session: 1,
statements: vec![
(
DisputeStatement::Valid(ValidDisputeStatementKind::Explicit),
ValidatorIndex(0),
sig_a,
),
(
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
ValidatorIndex(1),
sig_d,
),
]
}))
);
})
}
#[test]
fn filter_bad_signatures_correctly_detects_single_sided() {
new_test_ext(Default::default()).execute_with(|| {
let v0 = <ValidatorId as CryptoType>::Pair::generate().0;
let v1 = <ValidatorId as CryptoType>::Pair::generate().0;
let v2 = <ValidatorId as CryptoType>::Pair::generate().0;
let v3 = <ValidatorId as CryptoType>::Pair::generate().0;
run_to_block(3, |b| {
Some((
true,
b,
vec![(&0, v0.public()), (&1, v1.public()), (&2, v2.public()), (&3, v3.public())],
Some(vec![
(&0, v0.public()),
(&1, v1.public()),
(&2, v2.public()),
(&3, v3.public()),
]),
))
});
let candidate_hash_a = CandidateHash(pezsp_core::H256::repeat_byte(1));
let payload = |c_hash: &CandidateHash, valid| {
ExplicitDisputeStatement { valid, candidate_hash: *c_hash, session: 1 }
.signing_payload()
};
let payload_a = payload(&candidate_hash_a, true);
let payload_a_bad = payload(&candidate_hash_a, false);
let sig_0 = v0.sign(&payload_a);
let sig_1 = v1.sign(&payload_a_bad);
let statements = vec![DisputeStatementSet {
candidate_hash: candidate_hash_a,
session: 1,
statements: vec![
(
DisputeStatement::Valid(ValidDisputeStatementKind::Explicit),
ValidatorIndex(0),
sig_0.clone(),
),
(
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
ValidatorIndex(2),
sig_1.clone(),
),
],
}];
let statements = apply_filter_all::<Test, _>(statements);
assert!(statements.is_empty());
})
}
#[test]
fn filter_removes_session_out_of_bounds() {
new_test_ext(Default::default()).execute_with(|| {
let v0 = <ValidatorId as CryptoType>::Pair::generate().0;
run_to_block(3, |b| {
Some((true, b, vec![(&0, v0.public())], Some(vec![(&0, v0.public())])))
});
let candidate_hash = CandidateHash(pezsp_core::H256::repeat_byte(1));
let payload =
ExplicitDisputeStatement { valid: true, candidate_hash, session: 1 }.signing_payload();
let sig_a = v0.sign(&payload);
let statements = vec![DisputeStatementSet {
candidate_hash,
session: 100,
statements: vec![(
DisputeStatement::Valid(ValidDisputeStatementKind::Explicit),
ValidatorIndex(0),
sig_a,
)],
}];
let statements = apply_filter_all::<Test, _>(statements);
assert!(statements.is_empty());
})
}
#[test]
fn filter_removes_concluded_ancient() {
let dispute_post_conclusion_acceptance_period = 2;
let mock_genesis_config = MockGenesisConfig {
configuration: crate::configuration::GenesisConfig {
config: HostConfiguration {
dispute_post_conclusion_acceptance_period,
..Default::default()
},
},
..Default::default()
};
new_test_ext(mock_genesis_config).execute_with(|| {
let v0 = <ValidatorId as CryptoType>::Pair::generate().0;
run_to_block(3, |b| {
Some((true, b, vec![(&0, v0.public())], Some(vec![(&0, v0.public())])))
});
let candidate_hash_a = CandidateHash(pezsp_core::H256::repeat_byte(1));
let candidate_hash_b = CandidateHash(pezsp_core::H256::repeat_byte(2));
Disputes::<Test>::insert(
&1,
&candidate_hash_a,
DisputeState {
validators_for: bitvec![u8, BitOrderLsb0; 0; 4],
validators_against: bitvec![u8, BitOrderLsb0; 1; 4],
start: 0,
concluded_at: Some(0),
},
);
Disputes::<Test>::insert(
&1,
&candidate_hash_b,
DisputeState {
validators_for: bitvec![u8, BitOrderLsb0; 0; 4],
validators_against: bitvec![u8, BitOrderLsb0; 1; 4],
start: 0,
concluded_at: Some(1),
},
);
let payload_a =
ExplicitDisputeStatement { valid: true, candidate_hash: candidate_hash_a, session: 1 }
.signing_payload();
let payload_b =
ExplicitDisputeStatement { valid: true, candidate_hash: candidate_hash_b, session: 1 }
.signing_payload();
let sig_a = v0.sign(&payload_a);
let sig_b = v0.sign(&payload_b);
let statements = vec![
DisputeStatementSet {
candidate_hash: candidate_hash_a,
session: 1,
statements: vec![(
DisputeStatement::Valid(ValidDisputeStatementKind::Explicit),
ValidatorIndex(0),
sig_a,
)],
},
DisputeStatementSet {
candidate_hash: candidate_hash_b,
session: 1,
statements: vec![(
DisputeStatement::Valid(ValidDisputeStatementKind::Explicit),
ValidatorIndex(0),
sig_b.clone(),
)],
},
];
let statements = apply_filter_all::<Test, _>(statements);
assert_eq!(
statements,
vec![CheckedDisputeStatementSet::unchecked_from_unchecked(DisputeStatementSet {
candidate_hash: candidate_hash_b,
session: 1,
statements: vec![(
DisputeStatement::Valid(ValidDisputeStatementKind::Explicit),
ValidatorIndex(0),
sig_b,
),]
})]
);
})
}
#[test]
fn filter_removes_duplicate_statements_sets() {
new_test_ext(Default::default()).execute_with(|| {
let v0 = <ValidatorId as CryptoType>::Pair::generate().0;
let v1 = <ValidatorId as CryptoType>::Pair::generate().0;
run_to_block(3, |b| {
Some((
true,
b,
vec![(&0, v0.public()), (&1, v1.public())],
Some(vec![(&0, v0.public()), (&1, v1.public())]),
))
});
let candidate_hash_a = CandidateHash(pezsp_core::H256::repeat_byte(1));
let payload =
ExplicitDisputeStatement { valid: true, candidate_hash: candidate_hash_a, session: 1 }
.signing_payload();
let payload_against =
ExplicitDisputeStatement { valid: false, candidate_hash: candidate_hash_a, session: 1 }
.signing_payload();
let sig_a = v0.sign(&payload);
let sig_a_against = v1.sign(&payload_against);
let statements = vec![
(
DisputeStatement::Valid(ValidDisputeStatementKind::Explicit),
ValidatorIndex(0),
sig_a.clone(),
),
(
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
ValidatorIndex(1),
sig_a_against.clone(),
),
];
let mut sets = vec![
DisputeStatementSet {
candidate_hash: candidate_hash_a,
session: 1,
statements: statements.clone(),
},
DisputeStatementSet {
candidate_hash: candidate_hash_a,
session: 1,
statements: statements.clone(),
},
];
assert!(<Pezpallet::<Test> as DisputesHandler<
BlockNumberFor<Test>,
>>::deduplicate_and_sort_dispute_data(&mut sets)
.is_err());
assert_eq!(
sets,
vec![DisputeStatementSet { candidate_hash: candidate_hash_a, session: 1, statements }]
);
})
}
#[test]
fn filter_ignores_single_sided() {
new_test_ext(Default::default()).execute_with(|| {
let v0 = <ValidatorId as CryptoType>::Pair::generate().0;
run_to_block(3, |b| {
Some((true, b, vec![(&0, v0.public())], Some(vec![(&0, v0.public())])))
});
let candidate_hash_a = CandidateHash(pezsp_core::H256::repeat_byte(1));
let payload =
ExplicitDisputeStatement { valid: true, candidate_hash: candidate_hash_a, session: 1 }
.signing_payload();
let sig_a = v0.sign(&payload);
let statements = vec![DisputeStatementSet {
candidate_hash: candidate_hash_a,
session: 1,
statements: vec![(
DisputeStatement::Valid(ValidDisputeStatementKind::Explicit),
ValidatorIndex(0),
sig_a.clone(),
)],
}];
let statements = apply_filter_all::<Test, _>(statements);
assert!(statements.is_empty());
})
}
#[test]
fn import_ignores_single_sided() {
new_test_ext(Default::default()).execute_with(|| {
let v0 = <ValidatorId as CryptoType>::Pair::generate().0;
run_to_block(3, |b| {
Some((true, b, vec![(&0, v0.public())], Some(vec![(&0, v0.public())])))
});
let candidate_hash_a = CandidateHash(pezsp_core::H256::repeat_byte(1));
let payload =
ExplicitDisputeStatement { valid: true, candidate_hash: candidate_hash_a, session: 1 }
.signing_payload();
let sig_a = v0.sign(&payload);
let statements = vec![DisputeStatementSet {
candidate_hash: candidate_hash_a,
session: 1,
statements: vec![(
DisputeStatement::Valid(ValidDisputeStatementKind::Explicit),
ValidatorIndex(0),
sig_a.clone(),
)],
}];
let statements = apply_filter_all::<Test, _>(statements);
assert!(statements.is_empty());
})
}