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

Commit 2b728eb

Browse files
authored
Revert chain if at least f+1 validators voted against a candidate (#7151)
* Make `issue_explicit_statement_with_index` regular function * Make `issue_backing_statement_with_index` regular function * Issue `RevertBlocks` as soon as a dispute has `byzantine threshold + 1` invalid votes. * Remove a comment * Fix `has_fresh_byzantine_threshold_against()` * Extend `informs_chain_selection_when_dispute_concluded_against` test
1 parent f13e389 commit 2b728eb

3 files changed

Lines changed: 168 additions & 135 deletions

File tree

node/core/dispute-coordinator/src/import.rs

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,9 @@ pub struct CandidateVoteState<Votes> {
179179

180180
/// Current dispute status, if there is any.
181181
dispute_status: Option<DisputeStatus>,
182+
183+
/// Are there `byzantine threshold + 1` invalid votes
184+
byzantine_threshold_against: bool,
182185
}
183186

184187
impl CandidateVoteState<CandidateVotes> {
@@ -191,7 +194,12 @@ impl CandidateVoteState<CandidateVotes> {
191194
valid: ValidCandidateVotes::new(),
192195
invalid: BTreeMap::new(),
193196
};
194-
Self { votes, own_vote: OwnVoteState::CannotVote, dispute_status: None }
197+
Self {
198+
votes,
199+
own_vote: OwnVoteState::CannotVote,
200+
dispute_status: None,
201+
byzantine_threshold_against: false,
202+
}
195203
}
196204

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

208-
let dispute_status = if is_disputed {
216+
let (dispute_status, byzantine_threshold_against) = if is_disputed {
209217
let mut status = DisputeStatus::active();
210218
let byzantine_threshold = polkadot_primitives::byzantine_threshold(n_validators);
211219
let is_confirmed = votes.voted_indices().len() > byzantine_threshold;
@@ -221,12 +229,12 @@ impl CandidateVoteState<CandidateVotes> {
221229
if concluded_against {
222230
status = status.conclude_against(now);
223231
};
224-
Some(status)
232+
(Some(status), votes.invalid.len() > byzantine_threshold)
225233
} else {
226-
None
234+
(None, false)
227235
};
228236

229-
Self { votes, own_vote, dispute_status }
237+
Self { votes, own_vote, dispute_status, byzantine_threshold_against }
230238
}
231239

232240
/// Import fresh statements.
@@ -328,8 +336,12 @@ impl CandidateVoteState<CandidateVotes> {
328336

329337
/// Extract `CandidateVotes` for handling import of new statements.
330338
fn into_old_state(self) -> (CandidateVotes, CandidateVoteState<()>) {
331-
let CandidateVoteState { votes, own_vote, dispute_status } = self;
332-
(votes, CandidateVoteState { votes: (), own_vote, dispute_status })
339+
let CandidateVoteState { votes, own_vote, dispute_status, byzantine_threshold_against } =
340+
self;
341+
(
342+
votes,
343+
CandidateVoteState { votes: (), own_vote, dispute_status, byzantine_threshold_against },
344+
)
333345
}
334346
}
335347

@@ -477,6 +489,13 @@ impl ImportResult {
477489
self.is_freshly_concluded_against() || self.is_freshly_concluded_for()
478490
}
479491

492+
/// Whether or not the invalid vote count for the dispute went beyond the byzantine threshold
493+
/// after the last import
494+
pub fn has_fresh_byzantine_threshold_against(&self) -> bool {
495+
!self.old_state().byzantine_threshold_against &&
496+
self.new_state().byzantine_threshold_against
497+
}
498+
480499
/// Modify this `ImportResult`s, by importing additional approval votes.
481500
///
482501
/// Both results and `new_state` will be changed as if those approval votes had been in the

node/core/dispute-coordinator/src/initialized.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1094,7 +1094,7 @@ impl Initialized {
10941094

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

0 commit comments

Comments
 (0)