Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Commit

Permalink
Freeze chain if there are byzantine threshold + 1 invalid votes again…
Browse files Browse the repository at this point in the history
…st a local candidate (#7225)
  • Loading branch information
tdimitrov authored May 17, 2023
1 parent 2b728eb commit 29a4fb1
Show file tree
Hide file tree
Showing 2 changed files with 176 additions and 4 deletions.
10 changes: 8 additions & 2 deletions runtime/parachains/src/disputes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,8 @@ bitflags::bitflags! {
const FOR_SUPERMAJORITY = 0b0010;
/// Is the supermajority against the validity of the block reached.
const AGAINST_SUPERMAJORITY = 0b0100;
/// Is there f+1 against the validity of the block reached
const AGAINST_BYZANTINE = 0b1000;
}
}

Expand All @@ -582,6 +584,10 @@ impl DisputeStateFlags {
flags |= DisputeStateFlags::FOR_SUPERMAJORITY;
}

if state.validators_against.count_ones() > byzantine_threshold {
flags |= DisputeStateFlags::AGAINST_BYZANTINE;
}

if state.validators_against.count_ones() >= supermajority_threshold {
flags |= DisputeStateFlags::AGAINST_SUPERMAJORITY;
}
Expand Down Expand Up @@ -1243,8 +1249,8 @@ impl<T: Config> Pallet<T> {

<Disputes<T>>::insert(&session, &candidate_hash, &summary.state);

// Freeze if just concluded against some local candidate
if summary.new_flags.contains(DisputeStateFlags::AGAINST_SUPERMAJORITY) {
// Freeze if the INVALID votes against some local candidate are above the byzantine threshold
if summary.new_flags.contains(DisputeStateFlags::AGAINST_BYZANTINE) {
if let Some(revert_to) = <Included<T>>::get(&session, &candidate_hash) {
Self::revert_and_freeze(revert_to);
}
Expand Down
170 changes: 168 additions & 2 deletions runtime/parachains/src/disputes/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,14 +123,26 @@ fn test_dispute_state_flag_from_state() {
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_SUPERMAJORITY |
DisputeStateFlags::CONFIRMED |
DisputeStateFlags::AGAINST_BYZANTINE,
);
}

Expand Down Expand Up @@ -248,7 +260,9 @@ fn test_import_prev_participant_confirmed_slash_for() {
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::CONFIRMED |
DisputeStateFlags::AGAINST_SUPERMAJORITY |
DisputeStateFlags::AGAINST_BYZANTINE,
);
}

Expand Down Expand Up @@ -674,6 +688,158 @@ fn test_freeze_provided_against_supermajority_for_included() {
});
}

#[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()))));

// A candidate which will be disputed
let candidate_hash = CandidateHash(sp_core::H256::repeat_byte(1));
let inclusion_parent = sp_core::H256::repeat_byte(0xff);
let session = 3;

// A byzantine threshold of INVALID
let stmts = vec![DisputeStatementSet {
candidate_hash: candidate_hash.clone(),
session,
statements: vec![
(
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
ValidatorIndex(0),
v0.sign(
&ExplicitDisputeStatement {
valid: false,
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(),
),
),
(
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
ValidatorIndex(2),
v2.sign(
&ExplicitDisputeStatement {
valid: false,
candidate_hash: candidate_hash.clone(),
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 },
)),
),
],
}];

// Include the candidate and import the votes
Pallet::<Test>::note_included(3, candidate_hash.clone(), 3);
assert!(Pallet::<Test>::process_checked_multi_dispute_data(
&stmts
.into_iter()
.map(CheckedDisputeStatementSet::unchecked_from_unchecked)
.collect()
)
.is_ok());
// Successful import should freeze the chain
assert_eq!(Frozen::<Test>::get(), Some(2));

// Now include one more block
run_to_block(7, |b| Some((true, b, active_set.clone(), Some(active_set.clone()))));
Pallet::<Test>::note_included(3, CandidateHash(sp_core::H256::repeat_byte(2)), 3);

// And generate enough votes to reach supermajority of invalid votes
let stmts = vec![DisputeStatementSet {
candidate_hash: candidate_hash.clone(),
session,
statements: vec![
(
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
ValidatorIndex(3),
v3.sign(
&ExplicitDisputeStatement {
valid: false,
candidate_hash: candidate_hash.clone(),
session,
}
.signing_payload(),
),
),
(
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
ValidatorIndex(4),
v4.sign(
&ExplicitDisputeStatement {
valid: false,
candidate_hash: candidate_hash.clone(),
session,
}
.signing_payload(),
),
),
(
DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit),
ValidatorIndex(5),
v5.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_ok());
// Chain should still be frozen
assert_eq!(Frozen::<Test>::get(), Some(2));
});
}

mod unconfirmed_disputes {
use super::*;
use assert_matches::assert_matches;
Expand Down

0 comments on commit 29a4fb1

Please sign in to comment.