-
Notifications
You must be signed in to change notification settings - Fork 1.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
Implement ListBeaconCommittees RPC Method #3977
Changes from all commits
b3714e8
8eebd52
b6645d6
cf169c8
6294037
d2f4b2b
c864627
2c88364
acbc400
59f528f
9bc9b91
2b87f39
064e963
5d2f142
842a5ac
4e22ca2
8617344
b20fc61
03f2cb0
6806e33
a35da60
d5c2e0c
4dab4c0
53c4c18
45a88bb
a2814d6
d27bd4e
29b6d18
4b4a761
bb64762
1b1476b
e6a487e
ddd4149
b925684
bbe9908
6bec964
29237a5
166dc3b
ad1daa1
df3f012
ceddcf4
0438c52
e5ff555
4376e01
11cc97e
4f2a781
e2467ef
393e9b7
828046e
fce0fb4
d22319c
15a3da0
f38fb07
b9dce72
ec296e7
3259f79
8a517cf
be31ef3
88893da
780a130
47d0b96
16489aa
42a89b3
70ab2c8
2beddda
386e90b
018f2c6
f9a555f
f028119
1392f11
e9d3b05
629320d
64a256c
7526b5b
c5d1879
adf1dda
a17b4ed
89a7dcd
b103c9c
412dc8c
de55d8a
1891298
81f6166
7f4300e
f06df62
a7dff12
77e4f96
1da9753
df35233
450b50d
632a7a8
a4e5965
8b9b560
ebfeb34
aeb3557
c1db234
4d6462a
24d13e7
2fbc45a
4100411
370db85
0bf6266
fc979b6
37f9b74
3ead960
7ea385a
fc526b0
f09c38b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -69,10 +69,6 @@ func (s *Service) Status() error { | |
// We archive committee information pertaining to the head state's epoch. | ||
func (s *Service) archiveCommitteeInfo(ctx context.Context, headState *pb.BeaconState) error { | ||
currentEpoch := helpers.SlotToEpoch(headState.Slot) | ||
committeeCount, err := helpers.CommitteeCountAtSlot(headState, helpers.StartSlot(currentEpoch)) | ||
if err != nil { | ||
return errors.Wrap(err, "could not get committee count") | ||
} | ||
proposerSeed, err := helpers.Seed(headState, currentEpoch, params.BeaconConfig().DomainBeaconProposer) | ||
if err != nil { | ||
return errors.Wrap(err, "could not generate seed") | ||
|
@@ -83,9 +79,8 @@ func (s *Service) archiveCommitteeInfo(ctx context.Context, headState *pb.Beacon | |
} | ||
|
||
info := ðpb.ArchivedCommitteeInfo{ | ||
ProposerSeed: proposerSeed[:], | ||
AttesterSeed: attesterSeed[:], | ||
CommitteeCount: committeeCount * params.BeaconConfig().SlotsPerEpoch, | ||
ProposerSeed: proposerSeed[:], | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These two seeds are really all we need to determine historical assignments alongside historical balances |
||
AttesterSeed: attesterSeed[:], | ||
} | ||
if err := s.beaconDB.SaveArchivedCommitteeInfo(ctx, currentEpoch, info); err != nil { | ||
return errors.Wrap(err, "could not archive committee info") | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -163,7 +163,6 @@ func archivedValidatorCommittee( | |
activeIndices []uint64, | ||
archivedBalances []uint64, | ||
) ([]uint64, uint64, uint64, uint64, error) { | ||
committeeCount := archivedInfo.CommitteeCount | ||
proposerSeed := bytesutil.ToBytes32(archivedInfo.ProposerSeed) | ||
attesterSeed := bytesutil.ToBytes32(archivedInfo.AttesterSeed) | ||
|
||
|
@@ -188,7 +187,8 @@ func archivedValidatorCommittee( | |
} | ||
for i := uint64(0); i < countAtSlot; i++ { | ||
epochOffset := i + (slot%params.BeaconConfig().SlotsPerEpoch)*countAtSlot | ||
committee, err := helpers.ComputeCommittee(activeIndices, attesterSeed, epochOffset, committeeCount) | ||
totalCount := countAtSlot * params.BeaconConfig().SlotsPerEpoch | ||
committee, err := helpers.ComputeCommittee(activeIndices, attesterSeed, epochOffset, totalCount) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was a bug that was not caught earlier - the argument to ComputeCommittee needs to be the total committees count in that epoch |
||
if err != nil { | ||
return nil, 0, 0, 0, errors.Wrap(err, "could not compute committee") | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
package beacon | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" | ||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1" | ||
"github.com/prysmaticlabs/prysm/shared/bytesutil" | ||
"github.com/prysmaticlabs/prysm/shared/pagination" | ||
"github.com/prysmaticlabs/prysm/shared/params" | ||
"google.golang.org/grpc/codes" | ||
"google.golang.org/grpc/status" | ||
) | ||
|
||
// ListBeaconCommittees for a given epoch. | ||
// | ||
// If no filter criteria is specified, the response returns | ||
// all beacon committees for the current epoch. The results are paginated by default. | ||
func (bs *Server) ListBeaconCommittees( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For this we might need more options for the In the event of a fork in the network, you could have two chains with different committees at the same epoch. To differentiate on the two sets of committees we would need to be able to identify committees from forked chains, so the state root is a good way to identify them. @shayzluf and I discussed this offline, in the event of a fork in the network , we need to be able to get the appropriate committees from each forked chain. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Great point, that's a neat suggestion. So what happens if you provide a state root that doesnt match head state? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe we can do this in a follow-up PR as that will increase the diff quite a lot. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess it would mean we are asking for the committees from a forked chain :) This would be important for the slasher, as it needs to be able to detect malicious attestations in the event of a fork. Sounds good, we can do it in a follow up PR |
||
ctx context.Context, | ||
req *ethpb.ListCommitteesRequest, | ||
) (*ethpb.BeaconCommittees, error) { | ||
if int(req.PageSize) > params.BeaconConfig().MaxPageSize { | ||
return nil, status.Errorf( | ||
codes.InvalidArgument, | ||
"requested page size %d can not be greater than max size %d", | ||
req.PageSize, | ||
params.BeaconConfig().MaxPageSize, | ||
) | ||
} | ||
|
||
headState := bs.HeadFetcher.HeadState() | ||
var requestingGenesis bool | ||
var startSlot uint64 | ||
switch q := req.QueryFilter.(type) { | ||
case *ethpb.ListCommitteesRequest_Epoch: | ||
startSlot = helpers.StartSlot(q.Epoch) | ||
case *ethpb.ListCommitteesRequest_Genesis: | ||
requestingGenesis = q.Genesis | ||
default: | ||
startSlot = headState.Slot | ||
} | ||
|
||
var attesterSeed [32]byte | ||
var activeIndices []uint64 | ||
var err error | ||
// This is the archival condition, if the requested epoch is < current epoch or if we are | ||
// requesting data from the genesis epoch. | ||
if requestingGenesis || helpers.SlotToEpoch(startSlot) < helpers.SlotToEpoch(headState.Slot) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the archival condition |
||
activeIndices, err = helpers.ActiveValidatorIndices(headState, helpers.SlotToEpoch(startSlot)) | ||
if err != nil { | ||
return nil, status.Errorf( | ||
codes.Internal, | ||
"could not retrieve active indices for epoch %d: %v", | ||
helpers.SlotToEpoch(startSlot), | ||
err, | ||
) | ||
} | ||
archivedCommitteeInfo, err := bs.BeaconDB.ArchivedCommitteeInfo(ctx, helpers.SlotToEpoch(startSlot)) | ||
if err != nil { | ||
return nil, status.Errorf( | ||
codes.Internal, | ||
"could not request archival data for epoch %d: %v", | ||
helpers.SlotToEpoch(startSlot), | ||
err, | ||
) | ||
} | ||
if archivedCommitteeInfo == nil { | ||
return nil, status.Errorf( | ||
codes.NotFound, | ||
"could not request data for epoch %d, perhaps --archive in the running beacon node is disabled", | ||
helpers.SlotToEpoch(startSlot), | ||
) | ||
} | ||
attesterSeed = bytesutil.ToBytes32(archivedCommitteeInfo.AttesterSeed) | ||
} else if !requestingGenesis && helpers.SlotToEpoch(startSlot) == helpers.SlotToEpoch(headState.Slot) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Otherwise if requesting current epoch, return current results There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you add these as comments? it seems helpful |
||
// Otherwise, we use data from the current epoch. | ||
currentEpoch := helpers.SlotToEpoch(headState.Slot) | ||
activeIndices, err = helpers.ActiveValidatorIndices(headState, currentEpoch) | ||
if err != nil { | ||
return nil, status.Errorf( | ||
codes.Internal, | ||
"could not retrieve active indices for current epoch %d: %v", | ||
currentEpoch, | ||
err, | ||
) | ||
} | ||
attesterSeed, err = helpers.Seed(headState, currentEpoch, params.BeaconConfig().DomainBeaconAttester) | ||
if err != nil { | ||
return nil, status.Errorf( | ||
codes.Internal, | ||
"could not retrieve attester seed for current epoch %d: %v", | ||
currentEpoch, | ||
err, | ||
) | ||
} | ||
} else { | ||
// Otherwise, we are requesting data from the future and we return an error. | ||
return nil, status.Errorf( | ||
codes.FailedPrecondition, | ||
"cannot retrieve information about an epoch in the future, current epoch %d, requesting %d", | ||
helpers.SlotToEpoch(headState.Slot), | ||
helpers.StartSlot(startSlot), | ||
) | ||
} | ||
|
||
committees := make([]*ethpb.BeaconCommittees_CommitteeItem, 0) | ||
for slot := startSlot; slot < startSlot+params.BeaconConfig().SlotsPerEpoch; slot++ { | ||
var countAtSlot = uint64(len(activeIndices)) / params.BeaconConfig().SlotsPerEpoch / params.BeaconConfig().TargetCommitteeSize | ||
if countAtSlot > params.BeaconConfig().MaxCommitteesPerSlot { | ||
countAtSlot = params.BeaconConfig().MaxCommitteesPerSlot | ||
} | ||
if countAtSlot == 0 { | ||
countAtSlot = 1 | ||
} | ||
for i := uint64(0); i < countAtSlot; i++ { | ||
epochOffset := i + (slot%params.BeaconConfig().SlotsPerEpoch)*countAtSlot | ||
totalCount := countAtSlot * params.BeaconConfig().SlotsPerEpoch | ||
committee, err := helpers.ComputeCommittee(activeIndices, attesterSeed, epochOffset, totalCount) | ||
if err != nil { | ||
return nil, status.Errorf( | ||
codes.Internal, | ||
"could not compute committee for slot %d: %v", | ||
slot, | ||
err, | ||
) | ||
} | ||
committees = append(committees, ðpb.BeaconCommittees_CommitteeItem{ | ||
Committee: committee, | ||
Slot: slot, | ||
}) | ||
} | ||
} | ||
|
||
numCommittees := len(committees) | ||
start, end, nextPageToken, err := pagination.StartAndEndPage(req.PageToken, int(req.PageSize), numCommittees) | ||
if err != nil { | ||
return nil, status.Errorf( | ||
codes.FailedPrecondition, | ||
"could not paginate results: %v", | ||
err, | ||
) | ||
} | ||
return ðpb.BeaconCommittees{ | ||
Epoch: helpers.SlotToEpoch(startSlot), | ||
ActiveValidatorCount: uint64(len(activeIndices)), | ||
Committees: committees[start:end], | ||
TotalSize: int32(numCommittees), | ||
NextPageToken: nextPageToken, | ||
}, 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.
This is wrong, we shouldn't store this because it can change on a slot by slot basis