This repository has been archived by the owner on Nov 15, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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 7b8391a. * Revert "Unit test for dispute filtering" This reverts commit 92ba5fe. * 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>
- Loading branch information
Showing
8 changed files
with
873 additions
and
99 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
148 changes: 148 additions & 0 deletions
148
node/core/dispute-coordinator/src/scraping/candidates.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
use polkadot_primitives::v2::{BlockNumber, CandidateHash}; | ||
use std::collections::{BTreeMap, HashMap, HashSet}; | ||
|
||
/// Keeps `CandidateHash` in reference counted way. | ||
/// Each `insert` saves a value with `reference count == 1` or increases the reference | ||
/// count if the value already exists. | ||
/// Each `remove` decreases the reference count for the corresponding `CandidateHash`. | ||
/// If the reference count reaches 0 - the value is removed. | ||
struct RefCountedCandidates { | ||
candidates: HashMap<CandidateHash, usize>, | ||
} | ||
|
||
impl RefCountedCandidates { | ||
pub fn new() -> Self { | ||
Self { candidates: HashMap::new() } | ||
} | ||
// If `CandidateHash` doesn't exist in the `HashMap` it is created and its reference | ||
// count is set to 1. | ||
// If `CandidateHash` already exists in the `HashMap` its reference count is increased. | ||
pub fn insert(&mut self, candidate: CandidateHash) { | ||
*self.candidates.entry(candidate).or_default() += 1; | ||
} | ||
|
||
// If a `CandidateHash` with reference count equals to 1 is about to be removed - the | ||
// candidate is dropped from the container too. | ||
// If a `CandidateHash` with reference count biger than 1 is about to be removed - the | ||
// reference count is decreased and the candidate remains in the container. | ||
pub fn remove(&mut self, candidate: &CandidateHash) { | ||
match self.candidates.get_mut(candidate) { | ||
Some(v) if *v > 1 => *v -= 1, | ||
Some(v) => { | ||
assert!(*v == 1); | ||
self.candidates.remove(candidate); | ||
}, | ||
None => {}, | ||
} | ||
} | ||
|
||
pub fn contains(&self, candidate: &CandidateHash) -> bool { | ||
self.candidates.contains_key(&candidate) | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod ref_counted_candidates_tests { | ||
use super::*; | ||
use polkadot_primitives::v2::{BlakeTwo256, HashT}; | ||
|
||
#[test] | ||
fn element_is_removed_when_refcount_reaches_zero() { | ||
let mut container = RefCountedCandidates::new(); | ||
|
||
let zero = CandidateHash(BlakeTwo256::hash(&vec![0])); | ||
let one = CandidateHash(BlakeTwo256::hash(&vec![1])); | ||
// add two separate candidates | ||
container.insert(zero); // refcount == 1 | ||
container.insert(one); | ||
|
||
// and increase the reference count for the first | ||
container.insert(zero); // refcount == 2 | ||
|
||
assert!(container.contains(&zero)); | ||
assert!(container.contains(&one)); | ||
|
||
// remove once -> refcount == 1 | ||
container.remove(&zero); | ||
assert!(container.contains(&zero)); | ||
assert!(container.contains(&one)); | ||
|
||
// remove once -> refcount == 0 | ||
container.remove(&zero); | ||
assert!(!container.contains(&zero)); | ||
assert!(container.contains(&one)); | ||
|
||
// remove the other element | ||
container.remove(&one); | ||
assert!(!container.contains(&zero)); | ||
assert!(!container.contains(&one)); | ||
} | ||
} | ||
|
||
/// Keeps track of scraped candidates. Supports `insert`, `remove_up_to_height` and `contains` | ||
/// operations. | ||
pub struct ScrapedCandidates { | ||
/// Main data structure which keeps the candidates we know about. `contains` does lookups only here. | ||
candidates: RefCountedCandidates, | ||
/// Keeps track at which block number a candidate was inserted. Used in `remove_up_to_height`. | ||
/// Without this tracking we won't be able to remove all candidates before block X. | ||
candidates_by_block_number: BTreeMap<BlockNumber, HashSet<CandidateHash>>, | ||
} | ||
|
||
impl ScrapedCandidates { | ||
pub fn new() -> Self { | ||
Self { | ||
candidates: RefCountedCandidates::new(), | ||
candidates_by_block_number: BTreeMap::new(), | ||
} | ||
} | ||
|
||
pub fn contains(&self, candidate_hash: &CandidateHash) -> bool { | ||
self.candidates.contains(candidate_hash) | ||
} | ||
|
||
// Removes all candidates up to a given height. The candidates at the block height are NOT removed. | ||
pub fn remove_up_to_height(&mut self, height: &BlockNumber) { | ||
let not_stale = self.candidates_by_block_number.split_off(&height); | ||
let stale = std::mem::take(&mut self.candidates_by_block_number); | ||
self.candidates_by_block_number = not_stale; | ||
for candidates in stale.values() { | ||
for c in candidates { | ||
self.candidates.remove(c); | ||
} | ||
} | ||
} | ||
|
||
pub fn insert(&mut self, block_number: BlockNumber, candidate_hash: CandidateHash) { | ||
self.candidates.insert(candidate_hash); | ||
self.candidates_by_block_number | ||
.entry(block_number) | ||
.or_default() | ||
.insert(candidate_hash); | ||
} | ||
|
||
// Used only for tests to verify the pruning doesn't leak data. | ||
#[cfg(test)] | ||
pub fn candidates_by_block_number_is_empty(&self) -> bool { | ||
self.candidates_by_block_number.is_empty() | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod scraped_candidates_tests { | ||
use super::*; | ||
use polkadot_primitives::v2::{BlakeTwo256, HashT}; | ||
|
||
#[test] | ||
fn stale_candidates_are_removed() { | ||
let mut candidates = ScrapedCandidates::new(); | ||
let target = CandidateHash(BlakeTwo256::hash(&vec![1, 2, 3])); | ||
candidates.insert(1, target); | ||
|
||
assert!(candidates.contains(&target)); | ||
|
||
candidates.remove_up_to_height(&2); | ||
assert!(!candidates.contains(&target)); | ||
assert!(candidates.candidates_by_block_number_is_empty()); | ||
} | ||
} |
Oops, something went wrong.