Some late short-term fixes for dispute slashing (#6249)

* disputes/slashing: slash only backers for ForInvalid

* add an assertion in mock impl

* fix tests

* do not slash backers on onconcluded disputes

* slash an intersection of backers and losers

* zombienet/disputes: check for offence only for invalid disputes

* add backing votes to disputes bench builder

* Update runtime/parachains/src/builder.rs

* Brad implementers guide revisions 2 (#6239)

* Add disputes subsystems fix

* Updated dispute approval vote import reasoning

* Improved wording of my changes

* Resolving issues brought up in comments

* Update disputes prioritisation in `dispute-coordinator` (#6130)

* Scraper processes CandidateBacked events

* Change definition of best-effort

* Fix `dispute-coordinator` tests

* Unit test for dispute filtering

* Clarification comment

* Add tests

* Fix logic

If a dispute is not backed, not included and not confirmed we
don't participate but we do import votes.

* Add metrics for refrained participations

* Revert "Add tests"

This reverts commit 7b8391a087922ced942cde9cd2b50ff3f633efc0.

* Revert "Unit test for dispute filtering"

This reverts commit 92ba5fe678214ab360306313a33c781338e600a0.

* fix dispute-coordinator tests

* Fix scraping

* new tests

* Small fixes in guide

* Apply suggestions from code review

Co-authored-by: Andrei Sandu <54316454+sandreim@users.noreply.github.com>

* Fix some comments and remove a pointless test

* Code review feedback

* Clarification comment in tests

* Some tests

* Reference counted `CandidateHash` in scraper

* Proper handling for Backed and Included candidates in scraper

Backed candidates which are not included should be kept for a
predetermined window of finalized blocks. E.g. if a candidate is backed
but not included in block 2, and the window size is 2, the same
candidate should be cleaned after block 4 is finalized.

Add reference counting for candidates in scraper. A candidate can be
added on multiple block heights so we have to make sure we don't clean
it prematurely from the scraper.

Add tests.

* Update comments in tests

* Guide update

* Fix cleanup logic for `backed_candidates_by_block_number`

* Simplify cleanup

* Make spellcheck happy

* Update tests

* Extract candidate backing logic in separate struct

* Code review feedback

* Treat  backed and included candidates in the same fashion

* Update some comments

* Small improvements in test

* spell check

* Fix some more comments

* clean -> prune

* Code review feedback

* Reword comment

* spelling

Co-authored-by: Andrei Sandu <54316454+sandreim@users.noreply.github.com>

* approval-voting: remove redundant validation check (#6266)

* approval-voting: remove a redundant check

* candidate-validation: remove unreachable check

* remove fill_block (#6200)

Co-authored-by: parity-processbot <>

* fix a compilation warning (#6279)

Fixes #6277.

* Only report concluded if there is an actual dispute. (#6270)

* Only report concluded if there is an actual dispute.

Hence no "non"-disputes will be added to disputes anymore.

* Fix redundant check.

* Test for no onesided disputes.

Co-authored-by: eskimor <eskimor@no-such-url.com>

* [ci] fix buildah image (#6281)

* Revert special casing of Kusama for grandpa rounds. (#6217)

Co-authored-by: eskimor <eskimor@no-such-url.com>

* Fixes "for loop over an `Option`" warnings (#6291)

Was seeing these warnings when running `cargo check --all`:

```
warning: for loop over an `Option`. This is more readably written as an `if let` statement
    --> node/core/approval-voting/src/lib.rs:1147:21
     |
1147 |             for activated in update.activated {
     |                              ^^^^^^^^^^^^^^^^
     |
     = note: `#[warn(for_loops_over_fallibles)]` on by default
help: to check pattern in a loop use `while let`
     |
1147 |             while let Some(activated) = update.activated {
     |             ~~~~~~~~~~~~~~~         ~~~
help: consider using `if let` to clear intent
     |
1147 |             if let Some(activated) = update.activated {
     |             ~~~~~~~~~~~~         ~~~
```

My guess is that `activated` used to be a SmallVec or similar, as is
`deactivated`. It was changed to an `Option`, the `for` still compiled (it's
technically correct, just weird), and the compiler didn't catch it until now.

* companion for #12599 (#6290)

* companion for #12599

* update Cargo.lock

* use cargo path instead of diener

* update lockfile for {"substrate"}

Co-authored-by: parity-processbot <>

* remove the runtime check and test

* append keys on past-session slashing

* runtime/disputes: allow importing backing votes after explicit for

* explicit MaliciousBacker error and a test

* update an outdated comment

* Revert "update an outdated comment"

This reverts commit 7c4c3f5a848f16e2b61435e981d814f00333ed41.

* Revert "remove the runtime check and test"

This reverts commit a5bff0c75e77effb5b7d3a1691de1b14bcdbd648.

* incremental punishment post conclusion + test

* punish backers post FOR vote

* remove unnecessary lifetime annotation

* add a comment to zombinet test

* typo

* fmt

* post merge test fixes

* fix test after changes in master

* address review nits

---------

Co-authored-by: Bradley Olson <34992650+BradleyOlson64@users.noreply.github.com>
Co-authored-by: Tsvetomir Dimitrov <tsvetomir@parity.io>
Co-authored-by: Andrei Sandu <54316454+sandreim@users.noreply.github.com>
Co-authored-by: Sergej Sakac <73715684+Szegoo@users.noreply.github.com>
Co-authored-by: eskimor <eskimor@users.noreply.github.com>
Co-authored-by: eskimor <eskimor@no-such-url.com>
Co-authored-by: Alexander Samusev <41779041+alvicsam@users.noreply.github.com>
Co-authored-by: Marcin S <marcin@bytedude.com>
Co-authored-by: Niklas Adolfsson <niklasadolfsson1@gmail.com>
This commit is contained in:
ordian
2023-02-01 11:36:05 -03:00
committed by GitHub
parent e8d9af4d3d
commit 2fa8565f9d
8 changed files with 670 additions and 152 deletions
+395 -81
View File
@@ -20,7 +20,8 @@ use crate::{
disputes::DisputesHandler,
mock::{
new_test_ext, AccountId, AllPalletsWithSystem, Initializer, MockGenesisConfig, System,
Test, PUNISH_VALIDATORS_AGAINST, PUNISH_VALIDATORS_FOR, REWARD_VALIDATORS,
Test, PUNISH_BACKERS_FOR, PUNISH_VALIDATORS_AGAINST, PUNISH_VALIDATORS_FOR,
REWARD_VALIDATORS,
},
};
use frame_support::{
@@ -30,6 +31,10 @@ use frame_support::{
use primitives::BlockNumber;
use sp_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::Pallet<Test>>::config();
let post_conclusion_acceptance_period = config.dispute_post_conclusion_acceptance_period;
@@ -138,22 +143,26 @@ fn test_import_new_participant() {
start: 0,
concluded_at: None,
},
BTreeSet::new(),
0,
);
assert_err!(
importer.import(ValidatorIndex(9), true),
importer.import(ValidatorIndex(9), VOTE_FOR),
VoteImportError::ValidatorIndexOutOfBounds,
);
assert_err!(importer.import(ValidatorIndex(0), true), VoteImportError::DuplicateStatement);
assert_ok!(importer.import(ValidatorIndex(0), false));
assert_err!(importer.import(ValidatorIndex(0), VOTE_FOR), VoteImportError::DuplicateStatement);
assert_ok!(importer.import(ValidatorIndex(0), VOTE_AGAINST));
assert_ok!(importer.import(ValidatorIndex(2), true));
assert_err!(importer.import(ValidatorIndex(2), true), VoteImportError::DuplicateStatement);
assert_ok!(importer.import(ValidatorIndex(2), VOTE_FOR));
assert_err!(importer.import(ValidatorIndex(2), VOTE_FOR), VoteImportError::DuplicateStatement);
assert_ok!(importer.import(ValidatorIndex(2), false));
assert_err!(importer.import(ValidatorIndex(2), false), 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());
@@ -180,10 +189,11 @@ fn test_import_prev_participant_confirmed() {
start: 0,
concluded_at: None,
},
BTreeSet::new(),
0,
);
assert_ok!(importer.import(ValidatorIndex(2), true));
assert_ok!(importer.import(ValidatorIndex(2), VOTE_FOR));
let summary = importer.finish();
assert_eq!(
@@ -211,15 +221,16 @@ fn test_import_prev_participant_confirmed_slash_for() {
start: 0,
concluded_at: None,
},
BTreeSet::new(),
0,
);
assert_ok!(importer.import(ValidatorIndex(2), true));
assert_ok!(importer.import(ValidatorIndex(2), false));
assert_ok!(importer.import(ValidatorIndex(3), false));
assert_ok!(importer.import(ValidatorIndex(4), false));
assert_ok!(importer.import(ValidatorIndex(5), false));
assert_ok!(importer.import(ValidatorIndex(6), false));
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!(
@@ -250,14 +261,15 @@ fn test_import_slash_against() {
start: 0,
concluded_at: None,
},
BTreeSet::new(),
0,
);
assert_ok!(importer.import(ValidatorIndex(3), true));
assert_ok!(importer.import(ValidatorIndex(4), true));
assert_ok!(importer.import(ValidatorIndex(5), false));
assert_ok!(importer.import(ValidatorIndex(6), true));
assert_ok!(importer.import(ValidatorIndex(7), true));
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!(
@@ -275,6 +287,48 @@ fn test_import_slash_against() {
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));
// Don't import backing vote twice
assert_err!(
importer.import(ValidatorIndex(0), VOTE_BACKING),
VoteImportError::DuplicateStatement,
);
// Don't import explicit votes after backing
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 that dispute timeout is handled correctly.
#[test]
fn test_dispute_timeout() {
@@ -329,6 +383,7 @@ fn test_dispute_timeout() {
});
let candidate_hash = CandidateHash(sp_core::H256::repeat_byte(1));
let inclusion_parent = sp_core::H256::repeat_byte(0xff);
// v0 and v1 vote for 3, v2 against. We need f+1 votes (3) so that the dispute is
// confirmed. Otherwise It will be filtered out.
@@ -338,16 +393,13 @@ fn test_dispute_timeout() {
session,
statements: vec![
(
DisputeStatement::Valid(ValidDisputeStatementKind::Explicit),
DisputeStatement::Valid(ValidDisputeStatementKind::BackingValid(
inclusion_parent,
)),
ValidatorIndex(0),
v0.sign(
&ExplicitDisputeStatement {
valid: true,
candidate_hash: candidate_hash.clone(),
session: start - 1,
}
.signing_payload(),
),
v0.sign(&CompactStatement::Valid(candidate_hash).signing_payload(
&SigningContext { session_index: start - 1, parent_hash: inclusion_parent },
)),
),
(
DisputeStatement::Valid(ValidDisputeStatementKind::Explicit),
@@ -495,21 +547,20 @@ fn test_provide_multi_dispute_is_providing() {
});
let candidate_hash = CandidateHash(sp_core::H256::repeat_byte(1));
let inclusion_parent = sp_core::H256::repeat_byte(0xff);
let session = 1;
let stmts = vec![DisputeStatementSet {
candidate_hash: candidate_hash.clone(),
session: 1,
session,
statements: vec![
(
DisputeStatement::Valid(ValidDisputeStatementKind::Explicit),
DisputeStatement::Valid(ValidDisputeStatementKind::BackingValid(
inclusion_parent,
)),
ValidatorIndex(0),
v0.sign(
&ExplicitDisputeStatement {
valid: true,
candidate_hash: candidate_hash.clone(),
session: 1,
}
.signing_payload(),
),
v0.sign(&CompactStatement::Valid(candidate_hash).signing_payload(
&SigningContext { session_index: session, parent_hash: inclusion_parent },
)),
),
(
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
@@ -518,7 +569,7 @@ fn test_provide_multi_dispute_is_providing() {
&ExplicitDisputeStatement {
valid: false,
candidate_hash: candidate_hash.clone(),
session: 1,
session,
}
.signing_payload(),
),
@@ -538,6 +589,70 @@ fn test_provide_multi_dispute_is_providing() {
})
}
#[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| {
// a new session at each block
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(sp_core::H256::repeat_byte(1));
let session = 1;
let stmts = vec![DisputeStatementSet {
candidate_hash: candidate_hash.clone(),
session,
statements: vec![
(
DisputeStatement::Valid(ValidDisputeStatementKind::Explicit),
ValidatorIndex(0),
v0.sign(
&ExplicitDisputeStatement {
valid: true,
candidate_hash: candidate_hash.clone(),
session,
}
.signing_payload(),
),
),
(
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
ValidatorIndex(1),
v1.sign(
&ExplicitDisputeStatement {
valid: false,
candidate_hash: candidate_hash.clone(),
session,
}
.signing_payload(),
),
),
],
}];
assert!(Pallet::<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(|| {
@@ -555,6 +670,8 @@ fn test_freeze_on_note_included() {
});
let candidate_hash = CandidateHash(sp_core::H256::repeat_byte(1));
let inclusion_parent = sp_core::H256::repeat_byte(0xff);
let session = 3;
// v0 votes for 3
let stmts = vec![DisputeStatementSet {
@@ -586,16 +703,13 @@ fn test_freeze_on_note_included() {
),
),
(
DisputeStatement::Valid(ValidDisputeStatementKind::Explicit),
DisputeStatement::Valid(ValidDisputeStatementKind::BackingValid(
inclusion_parent,
)),
ValidatorIndex(1),
v1.sign(
&ExplicitDisputeStatement {
valid: true,
candidate_hash: candidate_hash.clone(),
session: 3,
}
.signing_payload(),
),
v0.sign(&CompactStatement::Valid(candidate_hash).signing_payload(
&SigningContext { session_index: session, parent_hash: inclusion_parent },
)),
),
],
}];
@@ -629,11 +743,13 @@ fn test_freeze_provided_against_supermajority_for_included() {
});
let candidate_hash = CandidateHash(sp_core::H256::repeat_byte(1));
let inclusion_parent = sp_core::H256::repeat_byte(0xff);
let session = 3;
// v0 votes for 3
let stmts = vec![DisputeStatementSet {
candidate_hash: candidate_hash.clone(),
session: 3,
session,
statements: vec![
(
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
@@ -642,7 +758,7 @@ fn test_freeze_provided_against_supermajority_for_included() {
&ExplicitDisputeStatement {
valid: false,
candidate_hash: candidate_hash.clone(),
session: 3,
session,
}
.signing_payload(),
),
@@ -654,22 +770,19 @@ fn test_freeze_provided_against_supermajority_for_included() {
&ExplicitDisputeStatement {
valid: false,
candidate_hash: candidate_hash.clone(),
session: 3,
session,
}
.signing_payload(),
),
),
(
DisputeStatement::Valid(ValidDisputeStatementKind::Explicit),
DisputeStatement::Valid(ValidDisputeStatementKind::BackingValid(
inclusion_parent,
)),
ValidatorIndex(1),
v1.sign(
&ExplicitDisputeStatement {
valid: true,
candidate_hash: candidate_hash.clone(),
session: 3,
}
.signing_payload(),
),
v0.sign(&CompactStatement::Valid(candidate_hash).signing_payload(
&SigningContext { session_index: session, parent_hash: inclusion_parent },
)),
),
],
}];
@@ -851,23 +964,22 @@ fn test_provide_multi_dispute_success_and_other() {
});
let candidate_hash = CandidateHash(sp_core::H256::repeat_byte(1));
let inclusion_parent = sp_core::H256::repeat_byte(0xff);
let session = 3;
// v0 and v1 vote for 3, v6 votes against
let stmts = vec![DisputeStatementSet {
candidate_hash: candidate_hash.clone(),
session: 3,
session,
statements: vec![
(
DisputeStatement::Valid(ValidDisputeStatementKind::Explicit),
DisputeStatement::Valid(ValidDisputeStatementKind::BackingValid(
inclusion_parent,
)),
ValidatorIndex(0),
v0.sign(
&ExplicitDisputeStatement {
valid: true,
candidate_hash: candidate_hash.clone(),
session: 3,
}
.signing_payload(),
),
v0.sign(&CompactStatement::Valid(candidate_hash).signing_payload(
&SigningContext { session_index: session, parent_hash: inclusion_parent },
)),
),
(
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
@@ -876,7 +988,7 @@ fn test_provide_multi_dispute_success_and_other() {
&ExplicitDisputeStatement {
valid: false,
candidate_hash: candidate_hash.clone(),
session: 3,
session,
}
.signing_payload(),
),
@@ -926,16 +1038,13 @@ fn test_provide_multi_dispute_success_and_other() {
session: 5,
statements: vec![
(
DisputeStatement::Valid(ValidDisputeStatementKind::Explicit),
DisputeStatement::Valid(ValidDisputeStatementKind::BackingValid(
inclusion_parent,
)),
ValidatorIndex(5),
v3.sign(
&ExplicitDisputeStatement {
valid: true,
candidate_hash: candidate_hash.clone(),
session: 5,
}
.signing_payload(),
),
v3.sign(&CompactStatement::Valid(candidate_hash).signing_payload(
&SigningContext { session_index: 5, parent_hash: inclusion_parent },
)),
),
(
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
@@ -1141,6 +1250,211 @@ fn test_provide_multi_dispute_success_and_other() {
})
}
/// In this setup we have only one dispute concluding AGAINST.
/// There are some votes imported post dispute conclusion.
/// We make sure these votes are accounted for in punishment.
#[test]
fn test_punish_post_conclusion() {
new_test_ext(Default::default()).execute_with(|| {
// supermajority threshold is 5
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;
// Mapping between key pair and `ValidatorIndex`
// v0 -> 0
// v1 -> 3
// v2 -> 6
// v3 -> 5
// v4 -> 1
// v5 -> 4
// v6 -> 2
run_to_block(6, |b| {
// a new session at each block
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(sp_core::H256::repeat_byte(1));
let inclusion_parent = sp_core::H256::repeat_byte(0xff);
let session = 3;
let stmts = vec![DisputeStatementSet {
candidate_hash: candidate_hash.clone(),
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: candidate_hash.clone(),
session,
}
.signing_payload(),
),
),
(
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
ValidatorIndex(2),
v6.sign(
&ExplicitDisputeStatement {
valid: false,
candidate_hash: candidate_hash.clone(),
session,
}
.signing_payload(),
),
),
(
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
ValidatorIndex(6),
v2.sign(
&ExplicitDisputeStatement {
valid: false,
candidate_hash: candidate_hash.clone(),
session,
}
.signing_payload(),
),
),
(
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
ValidatorIndex(4),
v5.sign(
&ExplicitDisputeStatement {
valid: false,
candidate_hash: candidate_hash.clone(),
session,
}
.signing_payload(),
),
),
(
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
ValidatorIndex(5),
v3.sign(
&ExplicitDisputeStatement {
valid: false,
candidate_hash: candidate_hash.clone(),
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!(
Pallet::<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)]),],
);
// someone reveals 3 backing vote, 6 votes against
let stmts = vec![DisputeStatementSet {
candidate_hash: candidate_hash.clone(),
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: candidate_hash.clone(),
session,
}
.signing_payload(),
),
),
],
}];
let stmts = filter_dispute_set(stmts);
assert_ok!(Pallet::<Test>::process_checked_multi_dispute_data(stmts), vec![],);
// Ensure punishment for is called
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(|| {