-
Notifications
You must be signed in to change notification settings - Fork 658
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[stateless_validation] Split chunk_validation module into multiple files #10522
Conversation
Codecov ReportAttention:
Additional details and impacted files@@ Coverage Diff @@
## master #10522 +/- ##
==========================================
- Coverage 71.92% 71.90% -0.03%
==========================================
Files 720 723 +3
Lines 146747 146751 +4
Branches 146747 146751 +4
==========================================
- Hits 105553 105522 -31
- Misses 36331 36363 +32
- Partials 4863 4866 +3
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ugh, I'm getting too many of these nowdays :(
Thanks
chain/client/src/client.rs
Outdated
@@ -187,6 +188,8 @@ pub struct Client { | |||
/// Tracks current chunks that are ready to be included in block | |||
/// Also tracks banned chunk producers and filters out chunks produced by them | |||
pub chunk_inclusion_tracker: ChunkInclusionTracker, | |||
/// Tracks chunk endorsements received from chunk validators. Used to filter out chunks ready for inclusion | |||
pub chunk_endorsement_tracker: EndorsementTracker, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved LRU cache tracking chunk endorsements to a separate struct. No functional changes
endorsement: ChunkEndorsement, | ||
) -> Result<(), Error> { | ||
let chunk_header = self.chain.get_chunk(endorsement.chunk_hash())?.cloned_header(); | ||
self.chunk_endorsement_tracker.process_chunk_endorsement(chunk_header, endorsement) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tiny tiny amount of redirection here. Logic for process_chunk_endorsement
pushed to EndorsementTracker
from Client
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
just please add comments to the public methods
// Ideally, we should not be processing more than num_shards chunks at a time. | ||
const NUM_CHUNK_ENDORSEMENTS_CACHE_COUNT: usize = 100; | ||
|
||
pub struct EndorsementTracker { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a comment please?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we call this ChunkEndorsementTracker, because Endorsement is an overloaded term with "block endorsement"
// TODO(stateless_validation): It's possible for a malicious validator to send endorsements | ||
// for 100 unique chunks thus pushing out current valid endorsements from our cache. | ||
// Maybe add check to ensure we don't accept endorsements from chunks already included in some block? | ||
// Maybe add check to ensure we don't accept endorsements from chunks that have too old height_created? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another option: deduplicate based on the (height, account_id of the chunk validator)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah that, plus a bypass for chunks that are currently being considered for inclusion in a block.
It's kind of a similar issue compared to the ShardsManager forwarding cache, we can't validate much until we wait for the header to arrive. So, one possible way to do this is to have a separate cache based on (height, account id of chunk validator) and only when we have the chunk header do we actually process those. Yeah... a bit messy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@staffik is working on fixing this issue. We can communicate this idea! Meanwhile for purposes of refactoring, I'm leaving this as is.
let epoch_id = | ||
self.epoch_manager.get_epoch_id_from_prev_block(chunk_header.prev_block_hash())?; | ||
let protocol_version = self.epoch_manager.get_epoch_protocol_version(&epoch_id)?; | ||
if !checked_feature!("stable", ChunkValidation, protocol_version) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can also consider renaming ChunkValidation to StatelessValidation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
StatelessValidationStage0 maybe? StatelessValidation is a very big name :) Also I still feel like this feature flag is currently only used for stateless chunk validation, not anything else we're planning to launch with stateless validation like validator role change, so maybe StatelessChunkValidation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
StatelessValidation is a big name because we call the whole project that, not of itself ;) For the sake of naming things in code I wouldn't consider role chages etc. as stateless validation. Anyway ChunkValidation is confusing because we most certainly do already validate chunks today so any change would be good.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, agree with "StatelessValidation" but not sure if we have consensus here? I'm leaving ChunkValidation
as is for this PR and we can get back.
let Some(chunk_endorsements) = self.chunk_endorsements.peek(&chunk_header.chunk_hash()) | ||
else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
funny formatting but I guess this is what the autoformatter does
let epoch_id = | ||
self.epoch_manager.get_epoch_id_from_prev_block(chunk_header.prev_block_hash())?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you already have epoch_id variable
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch! Might have been introduced while merging from master in some previous PR
|
||
// Check whether the current set of chunk_validators have enough stake to include chunk in block. | ||
if !chunk_validator_assignments | ||
.does_chunk_have_enough_stake(&chunk_endorsements.keys().cloned().collect()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is cloned necessary? Can you pass by reference?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed signature to take HashSet<&AccountId>
instead of &HashSet<AccountId>
.
use crate::{metrics, Client}; | ||
|
||
impl Client { | ||
pub(crate) fn shadow_validate_block_chunks(&mut self, block: &Block) -> Result<(), Error> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: add a small comment please
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very nice refactoring, thank you! Future developers will thank you for keeping the files small and the structs modular. LGTM.
// Ideally, we should not be processing more than num_shards chunks at a time. | ||
const NUM_CHUNK_ENDORSEMENTS_CACHE_COUNT: usize = 100; | ||
|
||
pub struct EndorsementTracker { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we call this ChunkEndorsementTracker, because Endorsement is an overloaded term with "block endorsement"
|
||
// This is the number of unique chunks for which we would track the chunk endorsements. | ||
// Ideally, we should not be processing more than num_shards chunks at a time. | ||
const NUM_CHUNK_ENDORSEMENTS_CACHE_COUNT: usize = 100; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: maybe call it NUM_CHUNKS_IN_CHUNK_ENDORSEMENTS_CACHE? It's more descriptive.
// TODO(stateless_validation): It's possible for a malicious validator to send endorsements | ||
// for 100 unique chunks thus pushing out current valid endorsements from our cache. | ||
// Maybe add check to ensure we don't accept endorsements from chunks already included in some block? | ||
// Maybe add check to ensure we don't accept endorsements from chunks that have too old height_created? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah that, plus a bypass for chunks that are currently being considered for inclusion in a block.
It's kind of a similar issue compared to the ShardsManager forwarding cache, we can't validate much until we wait for the header to arrive. So, one possible way to do this is to have a separate cache based on (height, account id of chunk validator) and only when we have the chunk header do we actually process those. Yeah... a bit messy.
let epoch_id = | ||
self.epoch_manager.get_epoch_id_from_prev_block(chunk_header.prev_block_hash())?; | ||
let protocol_version = self.epoch_manager.get_epoch_protocol_version(&epoch_id)?; | ||
if !checked_feature!("stable", ChunkValidation, protocol_version) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
StatelessValidationStage0 maybe? StatelessValidation is a very big name :) Also I still feel like this feature flag is currently only used for stateless chunk validation, not anything else we're planning to launch with stateless validation like validator role change, so maybe StatelessChunkValidation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
super nit: maybe change the title to be a bit more specific: "split chunk_validation
module into multiple files"
and please mention #10275
…ValidationV0` (#10540) Originally discussed in [this comment](#10522 (comment)), I feel like renaming feature `ChunkValidation` to `StatelessValidationV0` makes sense. With subsequent releases, we can have `StatelessValidationV1` etc. @wacban @robin-near, thoughts?
Personally found it extremely hard to locate code in the chunk validator module.
Refactored into several files.