Skip to content

Commit

Permalink
Allow ListBeaconCommittees API to return previous epoch (#4647)
Browse files Browse the repository at this point in the history
* Allow committees API to request previous epoch

* Fix error log

* Fix previous epoch test
  • Loading branch information
0xKiwi authored and rauljordan committed Jan 25, 2020
1 parent 10341cb commit 1562d32
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 14 deletions.
28 changes: 16 additions & 12 deletions beacon-chain/rpc/beacon/committees.go
Expand Up @@ -28,16 +28,20 @@ func (bs *Server) ListBeaconCommittees(
startSlot = helpers.StartSlot(q.Epoch)
case *ethpb.ListCommitteesRequest_Genesis:
requestingGenesis = q.Genesis
if !requestingGenesis {
startSlot = headSlot
}
default:
startSlot = headSlot
}

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(headSlot) {
// This is the archival condition, if the requested epoch is < previous epoch.
headEpoch := helpers.SlotToEpoch(headSlot)
// Adding 1 here to prevent underflow on headEpoch.
if helpers.SlotToEpoch(startSlot)+1 < headEpoch {
activeIndices, err = bs.HeadFetcher.HeadValidatorsIndices(helpers.SlotToEpoch(startSlot))
if err != nil {
return nil, status.Errorf(
Expand All @@ -64,24 +68,24 @@ func (bs *Server) ListBeaconCommittees(
)
}
attesterSeed = bytesutil.ToBytes32(archivedCommitteeInfo.AttesterSeed)
} else if !requestingGenesis && helpers.SlotToEpoch(startSlot) == helpers.SlotToEpoch(headSlot) {
// Otherwise, we use data from the current epoch.
currentEpoch := helpers.SlotToEpoch(headSlot)
activeIndices, err = bs.HeadFetcher.HeadValidatorsIndices(currentEpoch)
} else if helpers.SlotToEpoch(startSlot)+1 == headEpoch || helpers.SlotToEpoch(startSlot) == headEpoch {
// Otherwise, we use current beacon state to calculate the committees.
requestedEpoch := helpers.SlotToEpoch(startSlot)
activeIndices, err = bs.HeadFetcher.HeadValidatorsIndices(requestedEpoch)
if err != nil {
return nil, status.Errorf(
codes.Internal,
"Could not retrieve active indices for current epoch %d: %v",
currentEpoch,
"Could not retrieve active indices for requested epoch %d: %v",
requestedEpoch,
err,
)
}
attesterSeed, err = bs.HeadFetcher.HeadSeed(currentEpoch)
attesterSeed, err = bs.HeadFetcher.HeadSeed(requestedEpoch)
if err != nil {
return nil, status.Errorf(
codes.Internal,
"Could not retrieve attester seed for current epoch %d: %v",
currentEpoch,
"Could not retrieve attester seed for requested epoch %d: %v",
requestedEpoch,
err,
)
}
Expand Down
86 changes: 84 additions & 2 deletions beacon-chain/rpc/beacon/committees_test.go
Expand Up @@ -16,7 +16,7 @@ import (
"github.com/prysmaticlabs/prysm/shared/params"
)

func TestServer_ListBeaconCommittees(t *testing.T) {
func TestServer_ListBeaconCommittees_CurrentEpoch(t *testing.T) {
db := dbTest.SetupDB(t)
defer dbTest.TeardownDB(t, db)

Expand Down Expand Up @@ -73,7 +73,9 @@ func TestServer_ListBeaconCommittees(t *testing.T) {
res *ethpb.BeaconCommittees
}{
{
req: &ethpb.ListCommitteesRequest{},
req: &ethpb.ListCommitteesRequest{
QueryFilter: &ethpb.ListCommitteesRequest_Genesis{Genesis: true},
},
res: &ethpb.BeaconCommittees{
Epoch: 0,
Committees: wanted,
Expand All @@ -92,6 +94,86 @@ func TestServer_ListBeaconCommittees(t *testing.T) {
}
}

func TestServer_ListBeaconCommittees_PreviousEpoch(t *testing.T) {
db := dbTest.SetupDB(t)
defer dbTest.TeardownDB(t, db)

numValidators := 128
headState := setupActiveValidators(t, db, numValidators)

headState.RandaoMixes = make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector)
for i := 0; i < len(headState.RandaoMixes); i++ {
headState.RandaoMixes[i] = make([]byte, 32)
}
headState.Slot = params.BeaconConfig().SlotsPerEpoch * 2

bs := &Server{
HeadFetcher: &mock.ChainService{
State: headState,
},
}

activeIndices, err := helpers.ActiveValidatorIndices(headState, 1)
if err != nil {
t.Fatal(err)
}
attesterSeed, err := helpers.Seed(headState, 1, params.BeaconConfig().DomainBeaconAttester)
if err != nil {
t.Fatal(err)
}
wanted := make(map[uint64]*ethpb.BeaconCommittees_CommitteesList)
startSlot := helpers.StartSlot(1)
for slot := startSlot; slot < startSlot + params.BeaconConfig().SlotsPerEpoch; slot++ {
var countAtSlot = uint64(numValidators) / params.BeaconConfig().SlotsPerEpoch / params.BeaconConfig().TargetCommitteeSize
if countAtSlot > params.BeaconConfig().MaxCommitteesPerSlot {
countAtSlot = params.BeaconConfig().MaxCommitteesPerSlot
}
if countAtSlot == 0 {
countAtSlot = 1
}
committeeItems := make([]*ethpb.BeaconCommittees_CommitteeItem, countAtSlot)
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 {
t.Fatal(err)
}
committeeItems[i] = &ethpb.BeaconCommittees_CommitteeItem{
ValidatorIndices: committee,
}
}
wanted[slot] = &ethpb.BeaconCommittees_CommitteesList{
Committees: committeeItems,
}
}

tests := []struct {
req *ethpb.ListCommitteesRequest
res *ethpb.BeaconCommittees
}{
{
req: &ethpb.ListCommitteesRequest{
QueryFilter: &ethpb.ListCommitteesRequest_Epoch{Epoch: 1},
},
res: &ethpb.BeaconCommittees{
Epoch: 1,
Committees: wanted,
ActiveValidatorCount: uint64(numValidators),
},
},
}
for _, test := range tests {
res, err := bs.ListBeaconCommittees(context.Background(), test.req)
if err != nil {
t.Fatal(err)
}
if !proto.Equal(res, test.res) {
t.Errorf("Expected %v, received %v", test.res, res)
}
}
}

func TestServer_ListBeaconCommittees_FromArchive(t *testing.T) {
db := dbTest.SetupDB(t)
defer dbTest.TeardownDB(t, db)
Expand Down

0 comments on commit 1562d32

Please sign in to comment.