Skip to content
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

Merged
merged 7 commits into from
Jan 30, 2024

Conversation

shreyan-gupta
Copy link
Contributor

Personally found it extremely hard to locate code in the chunk validator module.

Refactored into several files.

Copy link

codecov bot commented Jan 29, 2024

Codecov Report

Attention: 137 lines in your changes are missing coverage. Please review.

Comparison is base (95c80bf) 71.92% compared to head (6404632) 71.90%.
Report is 1 commits behind head on master.

Files Patch % Lines
...client/src/stateless_validation/shadow_validate.rs 3.77% 102 Missing ⚠️
...src/stateless_validation/state_witness_producer.rs 86.00% 6 Missing and 22 partials ⚠️
.../stateless_validation/chunk_endorsement_tracker.rs 92.85% 2 Missing and 4 partials ⚠️
chain/client/src/chunk_inclusion_tracker.rs 50.00% 0 Missing and 1 partial ⚠️
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     
Flag Coverage Δ
backward-compatibility 0.08% <0.00%> (ø)
db-migration 0.08% <0.00%> (ø)
genesis-check 1.25% <0.00%> (-0.01%) ⬇️
integration-tests 36.81% <66.17%> (-0.07%) ⬇️
linux 71.11% <8.64%> (-0.03%) ⬇️
linux-nightly 71.25% <66.17%> (-0.04%) ⬇️
macos 53.30% <8.64%> (-1.70%) ⬇️
pytests 1.47% <0.00%> (-0.01%) ⬇️
sanity-checks 1.27% <0.00%> (-0.01%) ⬇️
unittests 67.88% <64.19%> (-0.02%) ⬇️
upgradability 0.13% <0.00%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo

Copy link
Contributor Author

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

@@ -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,
Copy link
Contributor Author

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)
Copy link
Contributor Author

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

Copy link
Contributor

@wacban wacban left a 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 {
Copy link
Contributor

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?

Copy link
Contributor

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?
Copy link
Contributor

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)

Copy link
Contributor

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.

Copy link
Contributor Author

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) {
Copy link
Contributor

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.

Copy link
Contributor

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?

Copy link
Contributor

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.

Copy link
Contributor Author

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.

Comment on lines 106 to 107
let Some(chunk_endorsements) = self.chunk_endorsements.peek(&chunk_header.chunk_hash())
else {
Copy link
Contributor

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

Comment on lines 112 to 113
let epoch_id =
self.epoch_manager.get_epoch_id_from_prev_block(chunk_header.prev_block_hash())?;
Copy link
Contributor

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

Copy link
Contributor Author

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())
Copy link
Contributor

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?

Copy link
Contributor Author

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> {
Copy link
Contributor

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

@wacban wacban changed the title [stateless_validaiton] Refactor chunk_validator code [stateless_validation] Refactor chunk_validator code Jan 29, 2024
Copy link
Contributor

@robin-near robin-near left a 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 {
Copy link
Contributor

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;
Copy link
Contributor

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?
Copy link
Contributor

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) {
Copy link
Contributor

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?

Copy link
Contributor

@pugachAG pugachAG left a 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

@shreyan-gupta shreyan-gupta changed the title [stateless_validation] Refactor chunk_validator code [stateless_validation] Split chunk_validation module into multiple files Jan 29, 2024
@shreyan-gupta shreyan-gupta added this pull request to the merge queue Jan 30, 2024
Merged via the queue into master with commit 699a5c7 Jan 30, 2024
24 of 26 checks passed
@shreyan-gupta shreyan-gupta deleted the shreyan/stateless_validation/refactor branch January 30, 2024 13:35
github-merge-queue bot pushed a commit that referenced this pull request Jan 31, 2024
…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?
@shreyan-gupta shreyan-gupta added the A-stateless-validation Area: stateless validation label Feb 8, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-stateless-validation Area: stateless validation
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants