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

Revert chain if at least f+1 validators voted against a candidate #7151

Merged
merged 7 commits into from
May 17, 2023
33 changes: 26 additions & 7 deletions node/core/dispute-coordinator/src/import.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,9 @@ pub struct CandidateVoteState<Votes> {

/// Current dispute status, if there is any.
dispute_status: Option<DisputeStatus>,

/// Are there `byzantine threshold + 1` invalid votes
byzantine_threshold_against: bool,
}

impl CandidateVoteState<CandidateVotes> {
Expand All @@ -191,7 +194,12 @@ impl CandidateVoteState<CandidateVotes> {
valid: ValidCandidateVotes::new(),
invalid: BTreeMap::new(),
};
Self { votes, own_vote: OwnVoteState::CannotVote, dispute_status: None }
Self {
votes,
own_vote: OwnVoteState::CannotVote,
dispute_status: None,
byzantine_threshold_against: false,
}
}

/// Create a new `CandidateVoteState` from already existing votes.
Expand All @@ -205,7 +213,7 @@ impl CandidateVoteState<CandidateVotes> {
// We have a dispute, if we have votes on both sides:
let is_disputed = !votes.invalid.is_empty() && !votes.valid.raw().is_empty();

let dispute_status = if is_disputed {
let (dispute_status, byzantine_threshold_against) = if is_disputed {
let mut status = DisputeStatus::active();
let byzantine_threshold = polkadot_primitives::byzantine_threshold(n_validators);
let is_confirmed = votes.voted_indices().len() > byzantine_threshold;
Expand All @@ -221,12 +229,12 @@ impl CandidateVoteState<CandidateVotes> {
if concluded_against {
status = status.conclude_against(now);
};
Some(status)
(Some(status), votes.invalid.len() > byzantine_threshold)
} else {
None
(None, false)
};

Self { votes, own_vote, dispute_status }
Self { votes, own_vote, dispute_status, byzantine_threshold_against }
}

/// Import fresh statements.
Expand Down Expand Up @@ -328,8 +336,12 @@ impl CandidateVoteState<CandidateVotes> {

/// Extract `CandidateVotes` for handling import of new statements.
fn into_old_state(self) -> (CandidateVotes, CandidateVoteState<()>) {
let CandidateVoteState { votes, own_vote, dispute_status } = self;
(votes, CandidateVoteState { votes: (), own_vote, dispute_status })
let CandidateVoteState { votes, own_vote, dispute_status, byzantine_threshold_against } =
self;
(
votes,
CandidateVoteState { votes: (), own_vote, dispute_status, byzantine_threshold_against },
)
}
}

Expand Down Expand Up @@ -477,6 +489,13 @@ impl ImportResult {
self.is_freshly_concluded_against() || self.is_freshly_concluded_for()
}

/// Whether or not the invalid vote count for the dispute went beyond the byzantine threshold
/// after the last import
pub fn has_fresh_byzantine_threshold_against(&self) -> bool {
!self.old_state().byzantine_threshold_against &&
self.new_state().byzantine_threshold_against
}

/// Modify this `ImportResult`s, by importing additional approval votes.
///
/// Both results and `new_state` will be changed as if those approval votes had been in the
Expand Down
2 changes: 1 addition & 1 deletion node/core/dispute-coordinator/src/initialized.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1094,7 +1094,7 @@ impl Initialized {

// Notify ChainSelection if a dispute has concluded against a candidate. ChainSelection
// will need to mark the candidate's relay parent as reverted.
if import_result.is_freshly_concluded_against() {
if import_result.has_fresh_byzantine_threshold_against() {
let blocks_including = self.scraper.get_blocks_including_candidate(&candidate_hash);
for (parent_block_number, parent_block_hash) in &blocks_including {
gum::trace!(
Expand Down
Loading