This repository was 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.5k
Update disputes prioritisation in dispute-coordinator
#6130
Merged
Merged
Changes from 29 commits
Commits
Show all changes
39 commits
Select commit
Hold shift + click to select a range
56731cf
Scraper processes CandidateBacked events
tdimitrov 056a606
Change definition of best-effort
tdimitrov 9794e99
Fix `dispute-coordinator` tests
tdimitrov d44055e
Unit test for dispute filtering
tdimitrov 032475a
Clarification comment
tdimitrov a70d7d8
Add tests
tdimitrov 8bafd7a
Fix logic
tdimitrov dd6fbc0
Add metrics for refrained participations
tdimitrov baa7e20
Revert "Add tests"
tdimitrov d7af3f7
Revert "Unit test for dispute filtering"
tdimitrov 18f7a14
fix dispute-coordinator tests
tdimitrov b82df59
Fix scraping
tdimitrov 1523bce
new tests
tdimitrov 2c6272f
Small fixes in guide
tdimitrov cd42bcb
Apply suggestions from code review
tdimitrov 0551b7c
Fix some comments and remove a pointless test
tdimitrov 2906d2a
Code review feedback
tdimitrov eaa250f
Clarification comment in tests
tdimitrov 9b40f11
Some tests
tdimitrov 7e3040b
Reference counted `CandidateHash` in scraper
tdimitrov 77cc49e
Proper handling for Backed and Included candidates in scraper
tdimitrov dcd0465
Update comments in tests
tdimitrov f925dd3
Guide update
tdimitrov 9dd091a
Fix cleanup logic for `backed_candidates_by_block_number`
tdimitrov e533e6d
Simplify cleanup
tdimitrov fcb99d0
Merge branch 'master' into disputes-backed
tdimitrov 56f4e2a
Make spellcheck happy
tdimitrov aecc3e0
Update tests
tdimitrov a97e29a
Extract candidate backing logic in separate struct
tdimitrov d29b300
Code review feedback
tdimitrov 0d498dc
Treat backed and included candidates in the same fashion
tdimitrov 40b4115
Update some comments
tdimitrov 4c030a1
Small improvements in test
tdimitrov 76bca96
spell check
tdimitrov a57ffaa
Fix some more comments
tdimitrov f9a4407
clean -> prune
tdimitrov c441720
Code review feedback
tdimitrov f283517
Reword comment
tdimitrov f7393a7
spelling
tdimitrov File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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 hidden or 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 hidden or 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
193 changes: 193 additions & 0 deletions
193
node/core/dispute-coordinator/src/scraping/candidates.rs
This file contains hidden or 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,193 @@ | ||
| 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. | ||
| pub 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 backed candidates. Supports `insert`, `remove`, `remove_up_to_height` | ||
| /// and `contains` operations. | ||
| /// This structure has got an undesired side effect. If a candidate is removed explicitly | ||
| /// with `remove` its entries will remain in `backed_candidates_by_block_number` until | ||
| /// `remove_up_to_height` is called. This means we will keep data in | ||
| /// `backed_candidates_by_block_number` a bit longer than necessary. | ||
| /// Unfortunately fixing this is not trivial. `backed_candidates` uses reference counting | ||
| /// because a candidate can be backed on multiple block heights. So `remove` doesn't necessary | ||
| /// removes the candidate - it just decreases its reference count. For this reason it is not | ||
| /// a good idea to remove any entries in `backed_candidates_by_block_number` when `remove` is | ||
| /// called. | ||
| /// One approach for a fix is to introduce `BTreeMap<CandidateHash, HashSet<BlockNumber>>` to | ||
| /// keep track on which candidates exist at a specific key in `backed_candidates_by_block_number`. | ||
| /// However when removing a candidate with `remove` we don't know (and don't care) at which block | ||
| /// height it was backed. So no precise removal is possible. This means that we should either | ||
| /// a) don't do any cleanup in `backed_candidates_by_block_number` | ||
| /// b) add the 2nd `BTreeMap` mentioned above and delete a random block from | ||
| /// `backed_candidates_by_block_number` on each removal. | ||
| /// Option a) sounds more reasonable to me - if we can't do the right thing we'd better do nothing. | ||
| pub struct BackedCandidates { | ||
| /// Main data structure which keeps the backed candidates we know about. `contains` does | ||
| /// lookups only here. | ||
| backed_candidates: RefCountedCandidates, | ||
| /// Keeps track at which block number a candidate was backed. Used in `remove_up_to_height`. | ||
| /// Without this tracking we won't be able to 'remove all candidates included in block X and before'. | ||
| backed_candidates_by_block_number: BTreeMap<BlockNumber, HashSet<CandidateHash>>, | ||
| } | ||
|
|
||
| impl BackedCandidates { | ||
| pub fn new() -> Self { | ||
| Self { | ||
| backed_candidates: RefCountedCandidates::new(), | ||
| backed_candidates_by_block_number: BTreeMap::new(), | ||
| } | ||
| } | ||
|
|
||
| pub fn contains(&self, candidate_hash: &CandidateHash) -> bool { | ||
| self.backed_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.backed_candidates_by_block_number.split_off(&height); | ||
| let stale = std::mem::take(&mut self.backed_candidates_by_block_number); | ||
| self.backed_candidates_by_block_number = not_stale; | ||
| for candidates in stale.values() { | ||
| for c in candidates { | ||
| self.backed_candidates.remove(c); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // Removes a single candidate | ||
| pub fn remove(&mut self, candidate_hash: &CandidateHash) { | ||
| self.backed_candidates.remove(candidate_hash); | ||
| } | ||
|
|
||
| pub fn insert(&mut self, block_number: BlockNumber, candidate_hash: CandidateHash) { | ||
| self.backed_candidates.insert(candidate_hash); | ||
| self.backed_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 backed_candidates_by_block_number_is_empty(&self) -> bool { | ||
| self.backed_candidates_by_block_number.is_empty() | ||
| } | ||
|
|
||
| #[cfg(test)] | ||
| pub fn backed_candidates_by_block_number_has_key(&self, key: &BlockNumber) -> bool { | ||
| self.backed_candidates_by_block_number.contains_key(key) | ||
| } | ||
| } | ||
|
|
||
| #[cfg(test)] | ||
| mod backed_candidates_tests { | ||
| use super::*; | ||
| use polkadot_primitives::v2::{BlakeTwo256, HashT}; | ||
|
|
||
| #[test] | ||
| fn explicitly_removed_are_cleaned_from_block_mapping() { | ||
| let mut candidates = BackedCandidates::new(); | ||
| let target = CandidateHash(BlakeTwo256::hash(&vec![1, 2, 3])); | ||
| candidates.insert(1, target); | ||
|
|
||
| assert!(candidates.contains(&target)); | ||
|
|
||
| candidates.remove(&target); | ||
| assert!(!candidates.contains(&target)); | ||
| assert!(candidates.backed_candidates_by_block_number_has_key(&1)); // undesired side effect | ||
|
|
||
| candidates.remove_up_to_height(&2); | ||
| assert!(!candidates.backed_candidates_by_block_number_has_key(&1)); | ||
| assert!(candidates.backed_candidates_by_block_number_is_empty()); | ||
| } | ||
|
|
||
| #[test] | ||
| fn stale_candidates_are_removed() { | ||
| let mut candidates = BackedCandidates::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.backed_candidates_by_block_number_is_empty()); | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.