Implement ListIndexedAttestations Endpoint in Prysm (#4892)

* update patch and workspace

* stub methods

* implementation of indexed attestations list

* include latest ethereumapis

* update request type

* compute committee pure function

* use compute committee helper

* add test into list indexed attestations

* regenerate mock

* imports and out of range check

* test passing for archived epoch

* add comment

* comment

* better comment on func

* throw in continue instead
This commit is contained in:
Raul Jordan 2020-02-17 15:57:13 -06:00 committed by GitHub
parent d7db8b1f5d
commit 5db8c5ad0c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 546 additions and 122 deletions

View File

@ -1272,7 +1272,7 @@ go_repository(
go_repository( go_repository(
name = "com_github_prysmaticlabs_ethereumapis", name = "com_github_prysmaticlabs_ethereumapis",
commit = "b7452dde4ca361809def4ed5924ab3cb7ad1299a", commit = "53ccc146f7f488c5c7634530057f4aedf510a9ac",
importpath = "github.com/prysmaticlabs/ethereumapis", importpath = "github.com/prysmaticlabs/ethereumapis",
patch_args = ["-p1"], patch_args = ["-p1"],
patches = [ patches = [

View File

@ -30,6 +30,7 @@ go_library(
"//beacon-chain/operations/slashings:go_default_library", "//beacon-chain/operations/slashings:go_default_library",
"//beacon-chain/powchain:go_default_library", "//beacon-chain/powchain:go_default_library",
"//proto/beacon/p2p/v1:go_default_library", "//proto/beacon/p2p/v1:go_default_library",
"//shared/attestationutil:go_default_library",
"//shared/bytesutil:go_default_library", "//shared/bytesutil:go_default_library",
"//shared/hashutil:go_default_library", "//shared/hashutil:go_default_library",
"//shared/pagination:go_default_library", "//shared/pagination:go_default_library",
@ -73,6 +74,7 @@ go_test(
"//beacon-chain/rpc/testing:go_default_library", "//beacon-chain/rpc/testing:go_default_library",
"//beacon-chain/state:go_default_library", "//beacon-chain/state:go_default_library",
"//proto/beacon/p2p/v1:go_default_library", "//proto/beacon/p2p/v1:go_default_library",
"//shared/attestationutil:go_default_library",
"//shared/params:go_default_library", "//shared/params:go_default_library",
"//shared/slotutil/testing:go_default_library", "//shared/slotutil/testing:go_default_library",
"@com_github_gogo_protobuf//proto:go_default_library", "@com_github_gogo_protobuf//proto:go_default_library",

View File

@ -8,9 +8,12 @@ import (
ptypes "github.com/gogo/protobuf/types" ptypes "github.com/gogo/protobuf/types"
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
"github.com/prysmaticlabs/go-ssz" "github.com/prysmaticlabs/go-ssz"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/beacon-chain/db/filters" "github.com/prysmaticlabs/prysm/beacon-chain/db/filters"
"github.com/prysmaticlabs/prysm/beacon-chain/flags" "github.com/prysmaticlabs/prysm/beacon-chain/flags"
"github.com/prysmaticlabs/prysm/shared/attestationutil"
"github.com/prysmaticlabs/prysm/shared/pagination" "github.com/prysmaticlabs/prysm/shared/pagination"
"github.com/prysmaticlabs/prysm/shared/params"
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
"google.golang.org/grpc/status" "google.golang.org/grpc/status"
) )
@ -110,6 +113,97 @@ func (bs *Server) ListAttestations(
}, nil }, nil
} }
// ListIndexedAttestations retrieves indexed attestations by target epoch.
// IndexedAttestations are sorted by data slot by default. Either a target epoch filter
// or a boolean filter specifying a request for genesis epoch attestations may be used.
//
// The server may return an empty list when no attestations match the given
// filter criteria. This RPC should not return NOT_FOUND. Only one filter
// criteria should be used.
func (bs *Server) ListIndexedAttestations(
ctx context.Context, req *ethpb.ListIndexedAttestationsRequest,
) (*ethpb.ListIndexedAttestationsResponse, error) {
atts := make([]*ethpb.Attestation, 0)
var err error
epoch := helpers.SlotToEpoch(bs.GenesisTimeFetcher.CurrentSlot())
switch q := req.QueryFilter.(type) {
case *ethpb.ListIndexedAttestationsRequest_TargetEpoch:
atts, err = bs.BeaconDB.Attestations(ctx, filters.NewFilter().SetTargetEpoch(q.TargetEpoch))
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not fetch attestations: %v", err)
}
epoch = q.TargetEpoch
case *ethpb.ListIndexedAttestationsRequest_GenesisEpoch:
atts, err = bs.BeaconDB.Attestations(ctx, filters.NewFilter().SetTargetEpoch(0))
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not fetch attestations: %v", err)
}
epoch = 0
default:
return nil, status.Error(codes.InvalidArgument, "Must specify a filter criteria for fetching attestations")
}
// We sort attestations according to the Sortable interface.
sort.Sort(sortableAttestations(atts))
numAttestations := len(atts)
// If there are no attestations, we simply return a response specifying this.
// Otherwise, attempting to paginate 0 attestations below would result in an error.
if numAttestations == 0 {
return &ethpb.ListIndexedAttestationsResponse{
IndexedAttestations: make([]*ethpb.IndexedAttestation, 0),
TotalSize: int32(0),
NextPageToken: strconv.Itoa(0),
}, nil
}
committeesBySlot, _, err := bs.retrieveCommitteesForEpoch(ctx, epoch)
if err != nil {
return nil, status.Errorf(
codes.Internal,
"Could not retrieve committees for epoch %d: %v",
epoch,
err,
)
}
// We use the retrieved committees for the epoch to convert all attestations
// into indexed form effectively.
indexedAtts := make([]*ethpb.IndexedAttestation, numAttestations, numAttestations)
startSlot := helpers.StartSlot(epoch)
endSlot := startSlot + params.BeaconConfig().SlotsPerEpoch
for i := 0; i < len(indexedAtts); i++ {
att := atts[i]
// Out of range check, the attestation slot cannot be greater
// the last slot of the requested epoch or smaller than its start slot
// given committees are accessed as a map of slot -> commitees list, where there are
// SLOTS_PER_EPOCH keys in the map.
if att.Data.Slot < startSlot || att.Data.Slot > endSlot {
continue
}
committee := committeesBySlot[att.Data.Slot].Committees[att.Data.CommitteeIndex]
idxAtt, err := attestationutil.ConvertToIndexed(ctx, atts[i], committee.ValidatorIndices)
if err != nil {
return nil, status.Errorf(
codes.Internal,
"Could not convert attestation with slot %d to indexed form: %v",
att.Data.Slot,
err,
)
}
indexedAtts[i] = idxAtt
}
start, end, nextPageToken, err := pagination.StartAndEndPage(req.PageToken, int(req.PageSize), len(indexedAtts))
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not paginate attestations: %v", err)
}
return &ethpb.ListIndexedAttestationsResponse{
IndexedAttestations: indexedAtts[start:end],
TotalSize: int32(len(indexedAtts)),
NextPageToken: nextPageToken,
}, nil
}
// StreamAttestations to clients at the end of every slot. This method retrieves the // StreamAttestations to clients at the end of every slot. This method retrieves the
// aggregated attestations currently in the pool at the start of a slot and sends // aggregated attestations currently in the pool at the start of a slot and sends
// them over a gRPC stream. // them over a gRPC stream.
@ -133,6 +227,15 @@ func (bs *Server) StreamAttestations(
} }
} }
// StreamIndexedAttestations to clients at the end of every slot. This method retrieves the
// aggregated attestations currently in the pool, converts them into indexed form, and
// sends them over a gRPC stream.
func (bs *Server) StreamIndexedAttestations(
_ *ptypes.Empty, stream ethpb.BeaconChain_StreamIndexedAttestationsServer,
) error {
return status.Error(codes.Unimplemented, "Unimplemented")
}
// AttestationPool retrieves pending attestations. // AttestationPool retrieves pending attestations.
// //
// The server returns a list of attestations that have been seen but not // The server returns a list of attestations that have been seen but not

View File

@ -7,6 +7,7 @@ import (
"strconv" "strconv"
"strings" "strings"
"testing" "testing"
"time"
"github.com/gogo/protobuf/proto" "github.com/gogo/protobuf/proto"
ptypes "github.com/gogo/protobuf/types" ptypes "github.com/gogo/protobuf/types"
@ -15,12 +16,14 @@ import (
"github.com/prysmaticlabs/go-bitfield" "github.com/prysmaticlabs/go-bitfield"
"github.com/prysmaticlabs/go-ssz" "github.com/prysmaticlabs/go-ssz"
mock "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing" mock "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
dbTest "github.com/prysmaticlabs/prysm/beacon-chain/db/testing" dbTest "github.com/prysmaticlabs/prysm/beacon-chain/db/testing"
"github.com/prysmaticlabs/prysm/beacon-chain/flags" "github.com/prysmaticlabs/prysm/beacon-chain/flags"
"github.com/prysmaticlabs/prysm/beacon-chain/operations/attestations" "github.com/prysmaticlabs/prysm/beacon-chain/operations/attestations"
mockRPC "github.com/prysmaticlabs/prysm/beacon-chain/rpc/testing" mockRPC "github.com/prysmaticlabs/prysm/beacon-chain/rpc/testing"
stateTrie "github.com/prysmaticlabs/prysm/beacon-chain/state" stateTrie "github.com/prysmaticlabs/prysm/beacon-chain/state"
pbp2p "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" pbp2p "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"github.com/prysmaticlabs/prysm/shared/attestationutil"
"github.com/prysmaticlabs/prysm/shared/params" "github.com/prysmaticlabs/prysm/shared/params"
mocktick "github.com/prysmaticlabs/prysm/shared/slotutil/testing" mocktick "github.com/prysmaticlabs/prysm/shared/slotutil/testing"
) )
@ -536,6 +539,196 @@ func TestServer_ListAttestations_Pagination_DefaultPageSize(t *testing.T) {
} }
} }
func TestServer_ListIndexedAttestations_GenesisEpoch(t *testing.T) {
db := dbTest.SetupDB(t)
defer dbTest.TeardownDB(t, db)
helpers.ClearCache()
ctx := context.Background()
count := params.BeaconConfig().SlotsPerEpoch
atts := make([]*ethpb.Attestation, 0, count)
for i := uint64(0); i < count; i++ {
attExample := &ethpb.Attestation{
Data: &ethpb.AttestationData{
BeaconBlockRoot: []byte("root"),
Slot: i,
CommitteeIndex: 0,
Target: &ethpb.Checkpoint{
Epoch: 0,
Root: make([]byte, 32),
},
},
AggregationBits: bitfield.Bitlist{0b11},
}
atts = append(atts, attExample)
}
if err := db.SaveAttestations(ctx, atts); err != nil {
t.Fatal(err)
}
// We setup 128 validators.
numValidators := 128
headState := setupActiveValidators(t, db, numValidators)
randaoMixes := make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector)
for i := 0; i < len(randaoMixes); i++ {
randaoMixes[i] = make([]byte, 32)
}
if err := headState.SetRandaoMixes(randaoMixes); err != nil {
t.Fatal(err)
}
activeIndices, err := helpers.ActiveValidatorIndices(headState, 0)
if err != nil {
t.Fatal(err)
}
epoch := uint64(0)
attesterSeed, err := helpers.Seed(headState, epoch, params.BeaconConfig().DomainBeaconAttester)
if err != nil {
t.Fatal(err)
}
committees, err := computeCommittees(helpers.StartSlot(epoch), activeIndices, attesterSeed)
if err != nil {
t.Fatal(err)
}
// Next up we convert the test attestations to indexed form:
indexedAtts := make([]*ethpb.IndexedAttestation, len(atts), len(atts))
for i := 0; i < len(indexedAtts); i++ {
att := atts[i]
committee := committees[att.Data.Slot].Committees[att.Data.CommitteeIndex]
idxAtt, err := attestationutil.ConvertToIndexed(ctx, atts[i], committee.ValidatorIndices)
if err != nil {
t.Fatalf("Could not convert attestation to indexed: %v", err)
}
indexedAtts[i] = idxAtt
}
bs := &Server{
BeaconDB: db,
HeadFetcher: &mock.ChainService{
State: headState,
},
GenesisTimeFetcher: &mock.ChainService{
Genesis: time.Now(),
},
}
res, err := bs.ListIndexedAttestations(ctx, &ethpb.ListIndexedAttestationsRequest{
QueryFilter: &ethpb.ListIndexedAttestationsRequest_GenesisEpoch{
GenesisEpoch: true,
},
})
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(indexedAtts, res.IndexedAttestations) {
t.Fatalf(
"Incorrect list indexed attestations response: wanted %v, received %v",
indexedAtts,
res.IndexedAttestations,
)
}
}
func TestServer_ListIndexedAttestations_ArchivedEpoch(t *testing.T) {
db := dbTest.SetupDB(t)
defer dbTest.TeardownDB(t, db)
helpers.ClearCache()
ctx := context.Background()
count := params.BeaconConfig().SlotsPerEpoch
atts := make([]*ethpb.Attestation, 0, count)
startSlot := helpers.StartSlot(50)
epoch := uint64(50)
for i := startSlot; i < count; i++ {
attExample := &ethpb.Attestation{
Data: &ethpb.AttestationData{
BeaconBlockRoot: []byte("root"),
Slot: i,
CommitteeIndex: 0,
Target: &ethpb.Checkpoint{
Epoch: epoch,
Root: make([]byte, 32),
},
},
AggregationBits: bitfield.Bitlist{0b11},
}
atts = append(atts, attExample)
}
if err := db.SaveAttestations(ctx, atts); err != nil {
t.Fatal(err)
}
// We setup 128 validators.
numValidators := 128
headState := setupActiveValidators(t, db, numValidators)
randaoMixes := make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector)
for i := 0; i < len(randaoMixes); i++ {
randaoMixes[i] = make([]byte, 32)
}
if err := headState.SetRandaoMixes(randaoMixes); err != nil {
t.Fatal(err)
}
if err := headState.SetSlot(startSlot); err != nil {
t.Fatal(err)
}
activeIndices, err := helpers.ActiveValidatorIndices(headState, epoch)
if err != nil {
t.Fatal(err)
}
attesterSeed, err := helpers.Seed(headState, epoch, params.BeaconConfig().DomainBeaconAttester)
if err != nil {
t.Fatal(err)
}
committees, err := computeCommittees(epoch, activeIndices, attesterSeed)
if err != nil {
t.Fatal(err)
}
// Next up we convert the test attestations to indexed form:
indexedAtts := make([]*ethpb.IndexedAttestation, len(atts), len(atts))
for i := 0; i < len(indexedAtts); i++ {
att := atts[i]
committee := committees[att.Data.Slot].Committees[att.Data.CommitteeIndex]
idxAtt, err := attestationutil.ConvertToIndexed(ctx, atts[i], committee.ValidatorIndices)
if err != nil {
t.Fatalf("Could not convert attestation to indexed: %v", err)
}
indexedAtts[i] = idxAtt
}
bs := &Server{
BeaconDB: db,
HeadFetcher: &mock.ChainService{
State: headState,
},
GenesisTimeFetcher: &mock.ChainService{
Genesis: time.Now(),
},
}
res, err := bs.ListIndexedAttestations(ctx, &ethpb.ListIndexedAttestationsRequest{
QueryFilter: &ethpb.ListIndexedAttestationsRequest_TargetEpoch{
TargetEpoch: epoch,
},
})
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(indexedAtts, res.IndexedAttestations) {
t.Fatalf(
"Incorrect list indexed attestations response: wanted %v, received %v",
indexedAtts,
res.IndexedAttestations,
)
}
}
func TestServer_AttestationPool_Pagination_ExceedsMaxPageSize(t *testing.T) { func TestServer_AttestationPool_Pagination_ExceedsMaxPageSize(t *testing.T) {
ctx := context.Background() ctx := context.Background()
bs := &Server{} bs := &Server{}

View File

@ -34,17 +34,35 @@ func (bs *Server) ListBeaconCommittees(
default: default:
startSlot = headSlot startSlot = headSlot
} }
committees, activeIndices, err := bs.retrieveCommitteesForEpoch(ctx, helpers.SlotToEpoch(startSlot))
if err != nil {
return nil, status.Errorf(
codes.Internal,
"Could not retrieve committees for epoch %d: %v",
helpers.SlotToEpoch(startSlot),
err,
)
}
return &ethpb.BeaconCommittees{
Epoch: helpers.SlotToEpoch(startSlot),
Committees: committees,
ActiveValidatorCount: uint64(len(activeIndices)),
}, nil
}
func (bs *Server) retrieveCommitteesForEpoch(
ctx context.Context,
epoch uint64,
) (map[uint64]*ethpb.BeaconCommittees_CommitteesList, []uint64, error) {
var attesterSeed [32]byte var attesterSeed [32]byte
var activeIndices []uint64 var activeIndices []uint64
var err error var err error
// This is the archival condition, if the requested epoch is < previous epoch. startSlot := helpers.StartSlot(epoch)
headEpoch := helpers.SlotToEpoch(headSlot) headEpoch := helpers.SlotToEpoch(bs.HeadFetcher.HeadSlot())
// Adding 1 here to prevent underflow on headEpoch.
if helpers.SlotToEpoch(startSlot)+1 < headEpoch { if helpers.SlotToEpoch(startSlot)+1 < headEpoch {
activeIndices, err = bs.HeadFetcher.HeadValidatorsIndices(helpers.SlotToEpoch(startSlot)) activeIndices, err = bs.HeadFetcher.HeadValidatorsIndices(helpers.SlotToEpoch(startSlot))
if err != nil { if err != nil {
return nil, status.Errorf( return nil, nil, status.Errorf(
codes.Internal, codes.Internal,
"Could not retrieve active indices for epoch %d: %v", "Could not retrieve active indices for epoch %d: %v",
helpers.SlotToEpoch(startSlot), helpers.SlotToEpoch(startSlot),
@ -53,7 +71,7 @@ func (bs *Server) ListBeaconCommittees(
} }
archivedCommitteeInfo, err := bs.BeaconDB.ArchivedCommitteeInfo(ctx, helpers.SlotToEpoch(startSlot)) archivedCommitteeInfo, err := bs.BeaconDB.ArchivedCommitteeInfo(ctx, helpers.SlotToEpoch(startSlot))
if err != nil { if err != nil {
return nil, status.Errorf( return nil, nil, status.Errorf(
codes.Internal, codes.Internal,
"Could not request archival data for epoch %d: %v", "Could not request archival data for epoch %d: %v",
helpers.SlotToEpoch(startSlot), helpers.SlotToEpoch(startSlot),
@ -61,7 +79,7 @@ func (bs *Server) ListBeaconCommittees(
) )
} }
if archivedCommitteeInfo == nil { if archivedCommitteeInfo == nil {
return nil, status.Errorf( return nil, nil, status.Errorf(
codes.NotFound, codes.NotFound,
"Could not retrieve data for epoch %d, perhaps --archive in the running beacon node is disabled", "Could not retrieve data for epoch %d, perhaps --archive in the running beacon node is disabled",
helpers.SlotToEpoch(startSlot), helpers.SlotToEpoch(startSlot),
@ -73,7 +91,7 @@ func (bs *Server) ListBeaconCommittees(
requestedEpoch := helpers.SlotToEpoch(startSlot) requestedEpoch := helpers.SlotToEpoch(startSlot)
activeIndices, err = bs.HeadFetcher.HeadValidatorsIndices(requestedEpoch) activeIndices, err = bs.HeadFetcher.HeadValidatorsIndices(requestedEpoch)
if err != nil { if err != nil {
return nil, status.Errorf( return nil, nil, status.Errorf(
codes.Internal, codes.Internal,
"Could not retrieve active indices for requested epoch %d: %v", "Could not retrieve active indices for requested epoch %d: %v",
requestedEpoch, requestedEpoch,
@ -82,7 +100,7 @@ func (bs *Server) ListBeaconCommittees(
} }
attesterSeed, err = bs.HeadFetcher.HeadSeed(requestedEpoch) attesterSeed, err = bs.HeadFetcher.HeadSeed(requestedEpoch)
if err != nil { if err != nil {
return nil, status.Errorf( return nil, nil, status.Errorf(
codes.Internal, codes.Internal,
"Could not retrieve attester seed for requested epoch %d: %v", "Could not retrieve attester seed for requested epoch %d: %v",
requestedEpoch, requestedEpoch,
@ -91,15 +109,34 @@ func (bs *Server) ListBeaconCommittees(
} }
} else { } else {
// Otherwise, we are requesting data from the future and we return an error. // Otherwise, we are requesting data from the future and we return an error.
return nil, status.Errorf( return nil, nil, status.Errorf(
codes.InvalidArgument, codes.InvalidArgument,
"Cannot retrieve information about an epoch in the future, current epoch %d, requesting %d", "Cannot retrieve information about an epoch in the future, current epoch %d, requesting %d",
helpers.SlotToEpoch(headSlot), headEpoch,
helpers.SlotToEpoch(startSlot), helpers.SlotToEpoch(startSlot),
) )
} }
committeesList := make(map[uint64]*ethpb.BeaconCommittees_CommitteesList) committeesListsBySlot, err := computeCommittees(startSlot, activeIndices, attesterSeed)
if err != nil {
return nil, nil, status.Errorf(
codes.InvalidArgument,
"Could not compute committees for epoch %d: %v",
helpers.SlotToEpoch(startSlot),
err,
)
}
return committeesListsBySlot, activeIndices, nil
}
// Compute committees given a start slot, active validator indices, and
// the attester seeds value.
func computeCommittees(
startSlot uint64,
activeIndices []uint64,
attesterSeed [32]byte,
) (map[uint64]*ethpb.BeaconCommittees_CommitteesList, error) {
committeesListsBySlot := make(map[uint64]*ethpb.BeaconCommittees_CommitteesList)
for slot := startSlot; slot < startSlot+params.BeaconConfig().SlotsPerEpoch; slot++ { for slot := startSlot; slot < startSlot+params.BeaconConfig().SlotsPerEpoch; slot++ {
var countAtSlot = uint64(len(activeIndices)) / params.BeaconConfig().SlotsPerEpoch / params.BeaconConfig().TargetCommitteeSize var countAtSlot = uint64(len(activeIndices)) / params.BeaconConfig().SlotsPerEpoch / params.BeaconConfig().TargetCommitteeSize
if countAtSlot > params.BeaconConfig().MaxCommitteesPerSlot { if countAtSlot > params.BeaconConfig().MaxCommitteesPerSlot {
@ -123,14 +160,9 @@ func (bs *Server) ListBeaconCommittees(
ValidatorIndices: committee, ValidatorIndices: committee,
} }
} }
committeesList[slot] = &ethpb.BeaconCommittees_CommitteesList{ committeesListsBySlot[slot] = &ethpb.BeaconCommittees_CommitteesList{
Committees: committeeItems, Committees: committeeItems,
} }
} }
return committeesListsBySlot, nil
return &ethpb.BeaconCommittees{
Epoch: helpers.SlotToEpoch(startSlot),
Committees: committeesList,
ActiveValidatorCount: uint64(len(activeIndices)),
}, nil
} }

View File

@ -48,53 +48,24 @@ func TestServer_ListBeaconCommittees_CurrentEpoch(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
wanted := make(map[uint64]*ethpb.BeaconCommittees_CommitteesList) committees, err := computeCommittees(0, activeIndices, attesterSeed)
for slot := uint64(0); slot < params.BeaconConfig().SlotsPerEpoch; slot++ { if err != nil {
var countAtSlot = uint64(numValidators) / params.BeaconConfig().SlotsPerEpoch / params.BeaconConfig().TargetCommitteeSize t.Fatal(err)
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++ {
committee, err := helpers.BeaconCommittee(activeIndices, attesterSeed, slot, i)
if err != nil {
t.Fatal(err)
}
committeeItems[i] = &ethpb.BeaconCommittees_CommitteeItem{
ValidatorIndices: committee,
}
}
wanted[slot] = &ethpb.BeaconCommittees_CommitteesList{
Committees: committeeItems,
}
} }
tests := []struct { wanted := &ethpb.BeaconCommittees{
req *ethpb.ListCommitteesRequest Epoch: 0,
res *ethpb.BeaconCommittees Committees: committees,
}{ ActiveValidatorCount: uint64(numValidators),
{
req: &ethpb.ListCommitteesRequest{
QueryFilter: &ethpb.ListCommitteesRequest_Genesis{Genesis: true},
},
res: &ethpb.BeaconCommittees{
Epoch: 0,
Committees: wanted,
ActiveValidatorCount: uint64(numValidators),
},
},
} }
for _, test := range tests { res, err := bs.ListBeaconCommittees(context.Background(), &ethpb.ListCommitteesRequest{
res, err := bs.ListBeaconCommittees(context.Background(), test.req) QueryFilter: &ethpb.ListCommitteesRequest_Genesis{Genesis: true},
if err != nil { })
t.Fatal(err) if err != nil {
} t.Fatal(err)
if !proto.Equal(res, test.res) { }
t.Errorf("Expected %v, received %v", test.res, res) if !proto.Equal(res, wanted) {
} t.Errorf("Expected %v, received %v", wanted, res)
} }
} }
@ -127,31 +98,10 @@ func TestServer_ListBeaconCommittees_PreviousEpoch(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
wanted := make(map[uint64]*ethpb.BeaconCommittees_CommitteesList)
startSlot := helpers.StartSlot(1) startSlot := helpers.StartSlot(1)
for slot := startSlot; slot < startSlot+params.BeaconConfig().SlotsPerEpoch; slot++ { wanted, err := computeCommittees(startSlot, activeIndices, attesterSeed)
var countAtSlot = uint64(numValidators) / params.BeaconConfig().SlotsPerEpoch / params.BeaconConfig().TargetCommitteeSize if err != nil {
if countAtSlot > params.BeaconConfig().MaxCommitteesPerSlot { t.Fatal(err)
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 { tests := []struct {
@ -245,28 +195,9 @@ func TestServer_ListBeaconCommittees_FromArchive(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
wanted := make(map[uint64]*ethpb.BeaconCommittees_CommitteesList) wanted, err := computeCommittees(0, activeIndices, seed)
for slot := uint64(0); slot < params.BeaconConfig().SlotsPerEpoch; slot++ { if err != nil {
var countAtSlot = uint64(numValidators) / params.BeaconConfig().SlotsPerEpoch / params.BeaconConfig().TargetCommitteeSize t.Fatal(err)
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++ {
committee, err := helpers.BeaconCommittee(activeIndices, seed, slot, i)
if err != nil {
t.Fatal(err)
}
committeeItems[i] = &ethpb.BeaconCommittees_CommitteeItem{
ValidatorIndices: committee,
}
}
wanted[slot] = &ethpb.BeaconCommittees_CommitteesList{
Committees: committeeItems,
}
} }
res1, err := bs.ListBeaconCommittees(context.Background(), &ethpb.ListCommitteesRequest{ res1, err := bs.ListBeaconCommittees(context.Background(), &ethpb.ListCommitteesRequest{
QueryFilter: &ethpb.ListCommitteesRequest_Genesis{ QueryFilter: &ethpb.ListCommitteesRequest_Genesis{

View File

@ -1,5 +1,5 @@
// Code generated by MockGen. DO NOT EDIT. // Code generated by MockGen. DO NOT EDIT.
// Source: github.com/prysmaticlabs/ethereumapis/eth/v1alpha1 (interfaces: BeaconChainClient,BeaconChain_StreamChainHeadClient,BeaconChain_StreamBlocksClient,BeaconChain_StreamAttestationsClient,BeaconChain_StreamValidatorsInfoClient) // Source: github.com/prysmaticlabs/ethereumapis/eth/v1alpha1 (interfaces: BeaconChainClient,BeaconChain_StreamChainHeadClient,BeaconChain_StreamBlocksClient,BeaconChain_StreamAttestationsClient,BeaconChain_StreamValidatorsInfoClient,BeaconChain_StreamIndexedAttestationsClient)
// Package mock is a generated GoMock package. // Package mock is a generated GoMock package.
package mock package mock
@ -257,6 +257,26 @@ func (mr *MockBeaconChainClientMockRecorder) ListBlocks(arg0, arg1 interface{},
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListBlocks", reflect.TypeOf((*MockBeaconChainClient)(nil).ListBlocks), varargs...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListBlocks", reflect.TypeOf((*MockBeaconChainClient)(nil).ListBlocks), varargs...)
} }
// ListIndexedAttestations mocks base method
func (m *MockBeaconChainClient) ListIndexedAttestations(arg0 context.Context, arg1 *v1alpha1.ListIndexedAttestationsRequest, arg2 ...grpc.CallOption) (*v1alpha1.ListIndexedAttestationsResponse, error) {
m.ctrl.T.Helper()
varargs := []interface{}{arg0, arg1}
for _, a := range arg2 {
varargs = append(varargs, a)
}
ret := m.ctrl.Call(m, "ListIndexedAttestations", varargs...)
ret0, _ := ret[0].(*v1alpha1.ListIndexedAttestationsResponse)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// ListIndexedAttestations indicates an expected call of ListIndexedAttestations
func (mr *MockBeaconChainClientMockRecorder) ListIndexedAttestations(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
varargs := append([]interface{}{arg0, arg1}, arg2...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListIndexedAttestations", reflect.TypeOf((*MockBeaconChainClient)(nil).ListIndexedAttestations), varargs...)
}
// ListValidatorAssignments mocks base method // ListValidatorAssignments mocks base method
func (m *MockBeaconChainClient) ListValidatorAssignments(arg0 context.Context, arg1 *v1alpha1.ListValidatorAssignmentsRequest, arg2 ...grpc.CallOption) (*v1alpha1.ValidatorAssignments, error) { func (m *MockBeaconChainClient) ListValidatorAssignments(arg0 context.Context, arg1 *v1alpha1.ListValidatorAssignmentsRequest, arg2 ...grpc.CallOption) (*v1alpha1.ValidatorAssignments, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
@ -377,6 +397,26 @@ func (mr *MockBeaconChainClientMockRecorder) StreamChainHead(arg0, arg1 interfac
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StreamChainHead", reflect.TypeOf((*MockBeaconChainClient)(nil).StreamChainHead), varargs...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StreamChainHead", reflect.TypeOf((*MockBeaconChainClient)(nil).StreamChainHead), varargs...)
} }
// StreamIndexedAttestations mocks base method
func (m *MockBeaconChainClient) StreamIndexedAttestations(arg0 context.Context, arg1 *empty.Empty, arg2 ...grpc.CallOption) (v1alpha1.BeaconChain_StreamIndexedAttestationsClient, error) {
m.ctrl.T.Helper()
varargs := []interface{}{arg0, arg1}
for _, a := range arg2 {
varargs = append(varargs, a)
}
ret := m.ctrl.Call(m, "StreamIndexedAttestations", varargs...)
ret0, _ := ret[0].(v1alpha1.BeaconChain_StreamIndexedAttestationsClient)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// StreamIndexedAttestations indicates an expected call of StreamIndexedAttestations
func (mr *MockBeaconChainClientMockRecorder) StreamIndexedAttestations(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
varargs := append([]interface{}{arg0, arg1}, arg2...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StreamIndexedAttestations", reflect.TypeOf((*MockBeaconChainClient)(nil).StreamIndexedAttestations), varargs...)
}
// StreamValidatorsInfo mocks base method // StreamValidatorsInfo mocks base method
func (m *MockBeaconChainClient) StreamValidatorsInfo(arg0 context.Context, arg1 ...grpc.CallOption) (v1alpha1.BeaconChain_StreamValidatorsInfoClient, error) { func (m *MockBeaconChainClient) StreamValidatorsInfo(arg0 context.Context, arg1 ...grpc.CallOption) (v1alpha1.BeaconChain_StreamValidatorsInfoClient, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
@ -942,3 +982,126 @@ func (mr *MockBeaconChain_StreamValidatorsInfoClientMockRecorder) Trailer() *gom
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Trailer", reflect.TypeOf((*MockBeaconChain_StreamValidatorsInfoClient)(nil).Trailer)) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Trailer", reflect.TypeOf((*MockBeaconChain_StreamValidatorsInfoClient)(nil).Trailer))
} }
// MockBeaconChain_StreamIndexedAttestationsClient is a mock of BeaconChain_StreamIndexedAttestationsClient interface
type MockBeaconChain_StreamIndexedAttestationsClient struct {
ctrl *gomock.Controller
recorder *MockBeaconChain_StreamIndexedAttestationsClientMockRecorder
}
// MockBeaconChain_StreamIndexedAttestationsClientMockRecorder is the mock recorder for MockBeaconChain_StreamIndexedAttestationsClient
type MockBeaconChain_StreamIndexedAttestationsClientMockRecorder struct {
mock *MockBeaconChain_StreamIndexedAttestationsClient
}
// NewMockBeaconChain_StreamIndexedAttestationsClient creates a new mock instance
func NewMockBeaconChain_StreamIndexedAttestationsClient(ctrl *gomock.Controller) *MockBeaconChain_StreamIndexedAttestationsClient {
mock := &MockBeaconChain_StreamIndexedAttestationsClient{ctrl: ctrl}
mock.recorder = &MockBeaconChain_StreamIndexedAttestationsClientMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use
func (m *MockBeaconChain_StreamIndexedAttestationsClient) EXPECT() *MockBeaconChain_StreamIndexedAttestationsClientMockRecorder {
return m.recorder
}
// CloseSend mocks base method
func (m *MockBeaconChain_StreamIndexedAttestationsClient) CloseSend() error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CloseSend")
ret0, _ := ret[0].(error)
return ret0
}
// CloseSend indicates an expected call of CloseSend
func (mr *MockBeaconChain_StreamIndexedAttestationsClientMockRecorder) CloseSend() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CloseSend", reflect.TypeOf((*MockBeaconChain_StreamIndexedAttestationsClient)(nil).CloseSend))
}
// Context mocks base method
func (m *MockBeaconChain_StreamIndexedAttestationsClient) Context() context.Context {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Context")
ret0, _ := ret[0].(context.Context)
return ret0
}
// Context indicates an expected call of Context
func (mr *MockBeaconChain_StreamIndexedAttestationsClientMockRecorder) Context() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Context", reflect.TypeOf((*MockBeaconChain_StreamIndexedAttestationsClient)(nil).Context))
}
// Header mocks base method
func (m *MockBeaconChain_StreamIndexedAttestationsClient) Header() (metadata.MD, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Header")
ret0, _ := ret[0].(metadata.MD)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Header indicates an expected call of Header
func (mr *MockBeaconChain_StreamIndexedAttestationsClientMockRecorder) Header() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Header", reflect.TypeOf((*MockBeaconChain_StreamIndexedAttestationsClient)(nil).Header))
}
// Recv mocks base method
func (m *MockBeaconChain_StreamIndexedAttestationsClient) Recv() (*v1alpha1.IndexedAttestation, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Recv")
ret0, _ := ret[0].(*v1alpha1.IndexedAttestation)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Recv indicates an expected call of Recv
func (mr *MockBeaconChain_StreamIndexedAttestationsClientMockRecorder) Recv() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Recv", reflect.TypeOf((*MockBeaconChain_StreamIndexedAttestationsClient)(nil).Recv))
}
// RecvMsg mocks base method
func (m *MockBeaconChain_StreamIndexedAttestationsClient) RecvMsg(arg0 interface{}) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "RecvMsg", arg0)
ret0, _ := ret[0].(error)
return ret0
}
// RecvMsg indicates an expected call of RecvMsg
func (mr *MockBeaconChain_StreamIndexedAttestationsClientMockRecorder) RecvMsg(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecvMsg", reflect.TypeOf((*MockBeaconChain_StreamIndexedAttestationsClient)(nil).RecvMsg), arg0)
}
// SendMsg mocks base method
func (m *MockBeaconChain_StreamIndexedAttestationsClient) SendMsg(arg0 interface{}) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "SendMsg", arg0)
ret0, _ := ret[0].(error)
return ret0
}
// SendMsg indicates an expected call of SendMsg
func (mr *MockBeaconChain_StreamIndexedAttestationsClientMockRecorder) SendMsg(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendMsg", reflect.TypeOf((*MockBeaconChain_StreamIndexedAttestationsClient)(nil).SendMsg), arg0)
}
// Trailer mocks base method
func (m *MockBeaconChain_StreamIndexedAttestationsClient) Trailer() metadata.MD {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Trailer")
ret0, _ := ret[0].(metadata.MD)
return ret0
}
// Trailer indicates an expected call of Trailer
func (mr *MockBeaconChain_StreamIndexedAttestationsClientMockRecorder) Trailer() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Trailer", reflect.TypeOf((*MockBeaconChain_StreamIndexedAttestationsClient)(nil).Trailer))
}

View File

@ -262,7 +262,7 @@ index 2ce5c34..4cbb276 100644
+ bytes signature = 3 [(gogoproto.moretags) = "ssz-size:\"96\""]; + bytes signature = 3 [(gogoproto.moretags) = "ssz-size:\"96\""];
} }
diff --git a/eth/v1alpha1/beacon_chain.proto b/eth/v1alpha1/beacon_chain.proto diff --git a/eth/v1alpha1/beacon_chain.proto b/eth/v1alpha1/beacon_chain.proto
index cdac301..945f8b5 100644 index 0099328..8b8c6eb 100644
--- a/eth/v1alpha1/beacon_chain.proto --- a/eth/v1alpha1/beacon_chain.proto
+++ b/eth/v1alpha1/beacon_chain.proto +++ b/eth/v1alpha1/beacon_chain.proto
@@ -15,6 +15,7 @@ syntax = "proto3"; @@ -15,6 +15,7 @@ syntax = "proto3";
@ -273,7 +273,7 @@ index cdac301..945f8b5 100644
import "google/api/annotations.proto"; import "google/api/annotations.proto";
import "google/protobuf/empty.proto"; import "google/protobuf/empty.proto";
import "google/protobuf/any.proto"; import "google/protobuf/any.proto";
@@ -358,7 +359,7 @@ message ChainHead { @@ -410,7 +411,7 @@ message ChainHead {
uint64 head_epoch = 2; uint64 head_epoch = 2;
// 32 byte merkle tree root of the canonical head block in the beacon node. // 32 byte merkle tree root of the canonical head block in the beacon node.
@ -282,7 +282,7 @@ index cdac301..945f8b5 100644
// Most recent slot that contains the finalized block. // Most recent slot that contains the finalized block.
uint64 finalized_slot = 4; uint64 finalized_slot = 4;
@@ -367,7 +368,7 @@ message ChainHead { @@ -419,7 +420,7 @@ message ChainHead {
uint64 finalized_epoch = 5; uint64 finalized_epoch = 5;
// Most recent 32 byte finalized block root. // Most recent 32 byte finalized block root.
@ -291,7 +291,7 @@ index cdac301..945f8b5 100644
// Most recent slot that contains the justified block. // Most recent slot that contains the justified block.
uint64 justified_slot = 7; uint64 justified_slot = 7;
@@ -376,7 +377,7 @@ message ChainHead { @@ -428,7 +429,7 @@ message ChainHead {
uint64 justified_epoch = 8; uint64 justified_epoch = 8;
// Most recent 32 byte justified block root. // Most recent 32 byte justified block root.
@ -300,7 +300,7 @@ index cdac301..945f8b5 100644
// Most recent slot that contains the previous justified block. // Most recent slot that contains the previous justified block.
uint64 previous_justified_slot = 10; uint64 previous_justified_slot = 10;
@@ -385,7 +386,7 @@ message ChainHead { @@ -437,7 +438,7 @@ message ChainHead {
uint64 previous_justified_epoch = 11; uint64 previous_justified_epoch = 11;
// Previous 32 byte justified block root. // Previous 32 byte justified block root.
@ -309,7 +309,7 @@ index cdac301..945f8b5 100644
} }
message ListCommitteesRequest { message ListCommitteesRequest {
@@ -430,7 +431,7 @@ message ListValidatorBalancesRequest { @@ -482,7 +483,7 @@ message ListValidatorBalancesRequest {
// Validator 48 byte BLS public keys to filter validators for the given // Validator 48 byte BLS public keys to filter validators for the given
// epoch. // epoch.
@ -318,7 +318,7 @@ index cdac301..945f8b5 100644
// Validator indices to filter validators for the given epoch. // Validator indices to filter validators for the given epoch.
repeated uint64 indices = 4; repeated uint64 indices = 4;
@@ -451,7 +452,7 @@ message ValidatorBalances { @@ -503,7 +504,7 @@ message ValidatorBalances {
message Balance { message Balance {
// Validator's 48 byte BLS public key. // Validator's 48 byte BLS public key.
@ -327,7 +327,7 @@ index cdac301..945f8b5 100644
// Validator's index in the validator set. // Validator's index in the validator set.
uint64 index = 2; uint64 index = 2;
@@ -500,7 +501,7 @@ message GetValidatorRequest { @@ -552,7 +553,7 @@ message GetValidatorRequest {
uint64 index = 1; uint64 index = 1;
// 48 byte validator public key. // 48 byte validator public key.
@ -336,7 +336,7 @@ index cdac301..945f8b5 100644
} }
} }
@@ -542,26 +543,25 @@ message ActiveSetChanges { @@ -594,26 +595,25 @@ message ActiveSetChanges {
uint64 epoch = 1; uint64 epoch = 1;
// 48 byte validator public keys that have been activated in the given epoch. // 48 byte validator public keys that have been activated in the given epoch.
@ -369,7 +369,7 @@ index cdac301..945f8b5 100644
// Indices of validators ejected in the given epoch. // Indices of validators ejected in the given epoch.
repeated uint64 ejected_indices = 9; repeated uint64 ejected_indices = 9;
@@ -611,11 +611,11 @@ message ValidatorQueue { @@ -663,11 +663,11 @@ message ValidatorQueue {
// Ordered list of 48 byte public keys awaiting activation. 0th index is the // Ordered list of 48 byte public keys awaiting activation. 0th index is the
// next key to be processed. // next key to be processed.
@ -383,7 +383,7 @@ index cdac301..945f8b5 100644
} }
message ListValidatorAssignmentsRequest { message ListValidatorAssignmentsRequest {
@@ -627,7 +627,7 @@ message ListValidatorAssignmentsRequest { @@ -679,7 +679,7 @@ message ListValidatorAssignmentsRequest {
bool genesis = 2; bool genesis = 2;
} }
// 48 byte validator public keys to filter assignments for the given epoch. // 48 byte validator public keys to filter assignments for the given epoch.
@ -392,7 +392,7 @@ index cdac301..945f8b5 100644
// Validator indicies to filter assignments for the given epoch. // Validator indicies to filter assignments for the given epoch.
repeated uint64 indices = 4; repeated uint64 indices = 4;
@@ -662,7 +662,7 @@ message ValidatorAssignments { @@ -714,7 +714,7 @@ message ValidatorAssignments {
uint64 proposer_slot = 4; uint64 proposer_slot = 4;
// 48 byte BLS public key. // 48 byte BLS public key.