mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-30 07:17:56 +00:00
Freeze chain if there are byzantine threshold + 1 invalid votes against a local candidate (#7225)
This commit is contained in:
committed by
GitHub
parent
0759495cec
commit
9e4bca6895
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -123,6 +123,16 @@ 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],
|
||||
@@ -130,7 +140,9 @@ fn test_dispute_state_flag_from_state() {
|
||||
start: 0,
|
||||
concluded_at: None,
|
||||
}),
|
||||
DisputeStateFlags::AGAINST_SUPERMAJORITY | DisputeStateFlags::CONFIRMED,
|
||||
DisputeStateFlags::AGAINST_SUPERMAJORITY |
|
||||
DisputeStateFlags::CONFIRMED |
|
||||
DisputeStateFlags::AGAINST_BYZANTINE,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user