-
Notifications
You must be signed in to change notification settings - Fork 1k
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
Fix CommitteeAssignments
to not return every validator
#14039
Conversation
6e583b1
to
6a117eb
Compare
nextSlotToEpoch := slots.ToEpoch(s.Slot() + 1) | ||
nextEpoch := req.Epoch + 1 |
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.
This was wrong, but luckily it was not surfaced because the old CommitteeAssignments
would mutate the slot. We should use the request epoch instead of the state slot for the query, and only update the next sync committee if the next epoch crosses the boundary as specified below.
@@ -1738,6 +1738,7 @@ func TestGetProposerDuties(t *testing.T) { | |||
t.Run("next epoch", func(t *testing.T) { | |||
bs, err := transition.GenesisBeaconState(context.Background(), deposits, 0, eth1Data) | |||
require.NoError(t, err, "Could not set up genesis state") | |||
require.NoError(t, bs.SetSlot(params.BeaconConfig().SlotsPerEpoch)) |
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.
This was another test bug because the old CommitteeAssignments
would mutate state slot. We should set the state slot here similar to what we did in the first test case
6a117eb
to
f7ec94a
Compare
// we need to reset state slot back to start slot so that we can compute the correct committees. | ||
currentProposalEpoch := epoch < nextEpoch | ||
if !currentProposalEpoch { | ||
if err := state.SetSlot(state.Slot() - params.BeaconConfig().SlotsPerEpoch); err != nil { |
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.
The function verifyAssignmentEpoch
allows to check for a state that has a slot from a past epoch. However, we never perform an epoch transition on the state when checking and simply use SetSlot()
which does not update balances. This could potentially give the wrong indices for both proposers and attesters when the state passed is from an epoch before the requested epoch.
vals[v] = struct{}{} | ||
} | ||
|
||
// Compute committee assignments for each slot in the epoch. | ||
for i := primitives.Slot(0); i < params.BeaconConfig().SlotsPerEpoch; i++ { |
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.
This reads simpler as
for i := primitives.Slot(0); i < params.BeaconConfig().SlotsPerEpoch; i++ { | |
for slot := primitives.Slot(startSlot); slot < startSlot + params.BeaconConfig().SlotsPerEpoch; slot++ { |
return nil, err | ||
} | ||
|
||
// Retrieve active validator indices for the specified epoch. | ||
activeValidatorIndices, err := ActiveValidatorIndices(ctx, state, epoch) |
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.
This allocates a very large slice for no reason other than requesting its length in the next line 240. We only need to get the number of committees for the slot. No allocations should be performed here.
a404d64
to
afb414c
Compare
afb414c
to
d220d4d
Compare
* Rewrite CommitteeAssignments to not return every validator * Potuz's feedback (cherry picked from commit c35889d)
Currently,
CommitteeAssignments
returns every active validator in the beacon state. With 1 million validators, this easily exceeds 32 MB in the heap. This PR addresses the issue by refactoringCommitteeAssignment
into three parts:verifyAssignmentEpoch
: Verifies the requested epoch is correct with respect to the input beacon state.ProposerAssignments
: Takesstate
andepoch
as inputs, usesverifyAssignmentEpoch
, and returnsmap[primitives.ValidatorIndex][]primitives.Slot
. All proposers of the epoch are returned, with only one proposer per slot.CommitteeAssignments
: Takesstate
,epoch
, andrequested_indices
as inputs, usesverifyAssignmentEpoch
, and returnsmap[primitives.ValidatorIndex]*CommitteeAssignment
. The output is bounded byrequested_indices
.Note to the reviewer: In
ProposerAssignments
, at the end of the function, I reset the beacon state to its original slot to prevent the function from mutating the slot.The previous behavior was incorrect and caused many inconsistent test false positives. The previous behavior was:
I'll document the false test behavior and the bug in the code diff.