Skip to content

feat(rollup): seed snapshots#13577

Merged
LHerskind merged 11 commits intomasterfrom
md/seed-snapshots
Apr 29, 2025
Merged

feat(rollup): seed snapshots#13577
LHerskind merged 11 commits intomasterfrom
md/seed-snapshots

Conversation

@Maddiaa0
Copy link
Member

@Maddiaa0 Maddiaa0 commented Apr 15, 2025

Overview

Snapshot the seeds across epochs.

How it currently works:

  • If we are in the first epoch, we use a genesis seed = uint256.max
  • If the seed for the current epoch has already been calculated, we use the current seed
  • Each time we enter a new epoch, we use the latest seed
  • If nothing exists, we use the latest sample seed

In cases where we are looking at the committee for a given block from the future, we may end up calculated a different committee, as we use the latest sample seed, which may have changed in the time since.

This PR builds ontop of validator snapshots ( where the validator set cannot change in size during an epoch ) to make sure that even when we are querying from the future, and the latest seed has changed, we do not end up with a different calculated committee.

How it should work:
Seeds are no longer stored for each epoch in EpochData, instead they are snapshotted.

  • If an epoch has not been set up before, it's is either genesis, where it is given the seed of uint224 max, or it will use the last seen seed for a given epoch.
  • Whenever an epoch is setup, it will calculate if it needs to create a new seed snapshot for the next epoch to use.
  • If the epoch is not setup, it will use the most recent seed snapshot. ( same behaviour, but not we can query into the past and have a consistent latest snapshot )

fixes: #13559

Copy link
Member Author

Maddiaa0 commented Apr 15, 2025

This stack of pull requests is managed by Graphite. Learn more about stacking.

@Maddiaa0 Maddiaa0 force-pushed the md/seed-snapshots branch from 0ad7f97 to e9bc7f3 Compare April 15, 2025 15:51
@Maddiaa0 Maddiaa0 marked this pull request as ready for review April 15, 2025 18:19
@Maddiaa0 Maddiaa0 linked an issue Apr 15, 2025 that may be closed by this pull request
Base automatically changed from md/val-snapshot to master April 18, 2025 22:13
@Maddiaa0 Maddiaa0 requested a review from charlielye as a code owner April 18, 2025 22:13
@Maddiaa0 Maddiaa0 force-pushed the md/seed-snapshots branch from 7f613a9 to 49ab911 Compare April 21, 2025 20:57
@charlielye charlielye removed their request for review April 22, 2025 14:42
@Maddiaa0 Maddiaa0 force-pushed the md/seed-snapshots branch from 49ab911 to 4321429 Compare April 28, 2025 09:52
@Maddiaa0 Maddiaa0 force-pushed the md/seed-snapshots branch from 93afb33 to 1ecec4f Compare April 28, 2025 10:21
@Maddiaa0 Maddiaa0 requested a review from LHerskind April 28, 2025 10:23
Copy link
Contributor

@LHerskind LHerskind left a comment

Choose a reason for hiding this comment

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

Think we can get rid of a bunch of the branching which should simplify a bunch of the tests. Also think we can get rid of the cheatcode for deposits (but would save that for a separate pr).

onlyOwner
{
CheatLib.cheat__InitialiseValidatorSet(_args);
setupEpoch();
Copy link
Contributor

Choose a reason for hiding this comment

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

This one was leftover from last? 👀


// Set the sample seed for the next epoch if required
// function handles the case where it is already set
setSampleSeedForEpoch(_epochNumber + Epoch.wrap(1));
Copy link
Contributor

Choose a reason for hiding this comment

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

It might be slightly nicer separation if this is moved upwards, e.g., then you have things altering seeds and then things using seeds. Since it should alter in the future don't seems like it should cause an issue.

*/
function setupEpoch(StakingStorage storage _stakingStore) internal {
Epoch epochNumber = Timestamp.wrap(block.timestamp).epochFromTimestamp();
function setupEpoch(StakingStorage storage _stakingStore, Epoch _epochNumber) internal {
Copy link
Contributor

Choose a reason for hiding this comment

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

Just a note for my sanity, we use the case where _epochNumber != currentEpoch when we mught wanna look at a historical and not setup epoch as the sampleValidators are using it.

uint224 sampleSeed = getSampleSeed(_epochNumber);

// If no sample seed is set, we are in a genesis state, we set the sample seed to max and push it into the store
if (sampleSeed == 0) {
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 just set it up at genesis instead? As part of the construction? I might be forgetting something, but would that not at least get rid of this special case in the function that we will continously call? 🤷

Copy link
Member Author

Choose a reason for hiding this comment

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

Yep, lets


// Emulate a sampling of the validators
uint256 sampleSeed = getSampleSeed(_epochNumber);
// Check if the latest checkpoint is for the next epoch
Copy link
Contributor

Choose a reason for hiding this comment

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

If we set the initial using the constructor as well it might simplify us slightly here as well as we won't have the case where the last checkpoint does not exists so one less branch to deal with.

mapping(address => bool) internal _seenCommittee;

/**
* @notice Set up the contracts needed for the tests with time aligned to the provided block name
Copy link
Contributor

Choose a reason for hiding this comment

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

The comment seems copied over, not taking a name.

testERC20.approve(address(rollup), TestConstants.AZTEC_MINIMUM_STAKE * _validatorCount);

if (_validatorCount > 0) {
rollup.cheat__InitialiseValidatorSet(initialValidators);
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we could get rid of the cheat after this pr, but should be done fully separately, no need to grow this one as there is a bunch of node and test stuff that would change

@@ -0,0 +1,19 @@
SetupEpochTest
├── when the rollup is in genesis state
Copy link
Contributor

Choose a reason for hiding this comment

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

Think we could get rid of a whole bunch of these by using the constructor to set some of these values 👀

}

function testInitialCommitteeMatch() public setup(4) {
function testInitialCommitteeMatch() public setup(4) progressEpoch {
Copy link
Contributor

Choose a reason for hiding this comment

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

If we apply the progressEpoch to each one, whould it not make sense to apply it to the setup so there is one less thing to think about?

Copy link
Member Author

Choose a reason for hiding this comment

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

Its also used in the setupEpoch test without progressEpoch applied. I know its a bit annoying in this file.

function setupEpoch(StakingStorage storage _stakingStore, Epoch _epochNumber) internal {
ValidatorSelectionStorage storage store = getStorage();

//################ Seeds ################
Copy link
Contributor

Choose a reason for hiding this comment

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

🙏

@LHerskind LHerskind added this pull request to the merge queue Apr 29, 2025
Merged via the queue into master with commit a6a33e1 Apr 29, 2025
8 checks passed
@LHerskind LHerskind deleted the md/seed-snapshots branch April 29, 2025 11:46
github-merge-queue bot pushed a commit that referenced this pull request May 7, 2025
Following the seed snapshots pr #13577 there was a change that mean that
`getCurrentProposer` can end up running the `setupEpoch` (reducing
number of different flows etc). However, that meant that when we were
running our benchmark test to get some gas numbers, we never end up
including the gas spent to setup the epoch. To address, we are now
explicitly calling `setupEpoch` such that we get some neat measurements
for it, also making it clear when changes are made that impact the
sampling.

A nice side effect, is that it more simply allow us to do the proper
amortized cost for sampling as the propose is for 360 tx, but sampling
is for 11520 (32 * 360). This new setup makes it more simple to see the
direct impact from the sampling on tx costs etc.
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.

feat(sol): snapshotting sample seeds

2 participants