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

Adds finalized header gap check #124

Merged
merged 13 commits into from
Mar 18, 2024
Merged

Conversation

claravanstaden
Copy link
Collaborator

Resolves: SNO-912

This is just on the on-chain part, off-chain coming soon.

@claravanstaden claravanstaden marked this pull request as ready for review March 12, 2024 13:10
Copy link

@vgeddes vgeddes left a comment

Choose a reason for hiding this comment

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

If I understand correctly, a max gap of 8192 slots is equivalent to ensuring that there is at least one imported beacon header per sync committee period?

If that is the case we could rather use the compute_period function to simplify the check (like we do with many other checks).

let header_period = compute_period(update.finalized_header.slot);
let latest_finalized_state_period = compute_period(latest_finalized_state.slot);
ensure!(
  (latest_finalized_state_period..=latest_finalized_state_period + 1).contains(&header_period)
  Error::<T>::InvalidFinalizedHeaderGap
);

Comment on lines 408 to 412
let max_latency = config::EPOCHS_PER_SYNC_COMMITTEE_PERIOD * config::SLOTS_PER_EPOCH;
ensure!(
latest_finalized_state.slot + max_latency as u64 >= update.finalized_header.slot,
Error::<T>::InvalidFinalizedHeaderGap
);
Copy link

Choose a reason for hiding this comment

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

Should preferably define the max gap as a constant in config.rs as its inputs are constants, and use saturating or checked arithmetic to guard against overflow. Luckily saturating_mul can be used at compile time.

pub const HEADER_IMPORT_MAX_GAP: usize = EPOCHS_PER_SYNC_COMMITTEE_PERIOD.saturating_mul(SLOTS_PER_EPOCH);
Suggested change
let max_latency = config::EPOCHS_PER_SYNC_COMMITTEE_PERIOD * config::SLOTS_PER_EPOCH;
ensure!(
latest_finalized_state.slot + max_latency as u64 >= update.finalized_header.slot,
Error::<T>::InvalidFinalizedHeaderGap
);
ensure!(
update.finalized_header.slot <= latest_finalized_state.slot + HEADER_IMPORT_MAX_GAP as u64,
Error::<T>::InvalidFinalizedHeaderGap
);

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Thanks! This is a good suggestion, adding in c263214.

Copy link

Choose a reason for hiding this comment

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

I see there is already a SLOTS_PER_HISTORICAL_ROOT constant, which has a value of 8192. Can we use reuse that here?

Copy link

Choose a reason for hiding this comment

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

Yeah, prefer to reuse SLOTS_PER_HISTORICAL_ROOT

@claravanstaden
Copy link
Collaborator Author

If I understand correctly, a max gap of 8192 slots is equivalent to ensuring that there is at least one imported beacon header per sync committee period?

If that is the case we could rather use the compute_period function to simplify the check (like we do with many other checks).

let header_period = compute_period(update.finalized_header.slot);
let latest_finalized_state_period = compute_period(latest_finalized_state.slot);
ensure!(
  (latest_finalized_state_period..=latest_finalized_state_period + 1).contains(&header_period)
  Error::<T>::InvalidFinalizedHeaderGap
);

The two finalized headers could each be in their different sync committee periods, but more than 8192 headers apart. For example:

  1. Header 100 (sync committee period 1)
  2. Header 9000 (sync committee period 2)

(8900 blocks apart)

The reason we need a header every 8192 slots at least, is because the block_roots array contains 8192 slots, which is used for ancestry proofs. If we import header 9000, and we receive a message to be verified at header 200, the block_roots field of header 9000 won't contain the header, in order to do the ancestry check. We also can't import an older finalized header because of this check: https://github.com/Snowfork/polkadot-sdk/blob/snowbridge/bridges/snowbridge/pallets/ethereum-client/src/lib.rs#L396 So then the light client is essentially bricked because it can never do the ancestry check. This is what happened on Rococo.

@vgeddes
Copy link

vgeddes commented Mar 13, 2024

The two finalized headers could each be in their different sync committee periods, but more than 8192 headers apart. For example:

Thanks, I understand better now. Makes sense 👍

@claravanstaden
Copy link
Collaborator Author

@vgeddes @yrong ready for final review 😄

claravanstaden and others added 3 commits March 15, 2024 13:11
@claravanstaden claravanstaden merged commit 8be52c9 into snowbridge Mar 18, 2024
7 checks passed
@claravanstaden claravanstaden deleted the limit-finalized-header-gap branch March 18, 2024 05:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants