prysm-pulse/beacon-chain/rpc/prysm/v1alpha1/beacon/assignments_test.go
kasey 17a43c1158
Retrieving state by slot to always apply canonical block at slot, when available (#10255)
* new stategen.StateReplayer/ReplayerBuilder to give more fine-grained
  control of replaying state+block history
* all rpc/api methods updated to use the new interface, return post-state

Co-authored-by: Kasey Kirkham <kasey@users.noreply.github.com>
Co-authored-by: Radosław Kapka <rkapka@wp.pl>
Co-authored-by: terence tsao <terence@prysmaticlabs.com>
2022-03-09 13:33:18 -06:00

407 lines
14 KiB
Go

package beacon
import (
"context"
"encoding/binary"
"fmt"
"strconv"
"testing"
types "github.com/prysmaticlabs/eth2-types"
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"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stategen"
mockstategen "github.com/prysmaticlabs/prysm/beacon-chain/state/stategen/mock"
"github.com/prysmaticlabs/prysm/cmd"
"github.com/prysmaticlabs/prysm/config/params"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/wrapper"
"github.com/prysmaticlabs/prysm/testing/assert"
"github.com/prysmaticlabs/prysm/testing/require"
"github.com/prysmaticlabs/prysm/testing/util"
"github.com/prysmaticlabs/prysm/time/slots"
"google.golang.org/protobuf/proto"
)
func TestServer_ListAssignments_CannotRequestFutureEpoch(t *testing.T) {
db := dbTest.SetupDB(t)
ctx := context.Background()
bs := &Server{
BeaconDB: db,
GenesisTimeFetcher: &mock.ChainService{},
}
addDefaultReplayerBuilder(bs, db)
wanted := errNoEpochInfoError
_, err := bs.ListValidatorAssignments(
ctx,
&ethpb.ListValidatorAssignmentsRequest{
QueryFilter: &ethpb.ListValidatorAssignmentsRequest_Epoch{
Epoch: slots.ToEpoch(bs.GenesisTimeFetcher.CurrentSlot()) + 1,
},
},
)
assert.ErrorContains(t, wanted, err)
}
func TestServer_ListAssignments_NoResults(t *testing.T) {
db := dbTest.SetupDB(t)
ctx := context.Background()
st, err := util.NewBeaconState()
require.NoError(t, err)
b := util.NewBeaconBlock()
require.NoError(t, db.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(b)))
gRoot, err := b.Block.HashTreeRoot()
require.NoError(t, err)
require.NoError(t, db.SaveGenesisBlockRoot(ctx, gRoot))
require.NoError(t, db.SaveState(ctx, st, gRoot))
bs := &Server{
BeaconDB: db,
GenesisTimeFetcher: &mock.ChainService{},
StateGen: stategen.New(db),
ReplayerBuilder: mockstategen.NewMockReplayerBuilder(mockstategen.WithMockState(st)),
}
wanted := &ethpb.ValidatorAssignments{
Assignments: make([]*ethpb.ValidatorAssignments_CommitteeAssignment, 0),
TotalSize: int32(0),
NextPageToken: strconv.Itoa(0),
}
res, err := bs.ListValidatorAssignments(
ctx,
&ethpb.ListValidatorAssignmentsRequest{
QueryFilter: &ethpb.ListValidatorAssignmentsRequest_Genesis{
Genesis: true,
},
},
)
require.NoError(t, err)
if !proto.Equal(wanted, res) {
t.Errorf("Wanted %v, received %v", wanted, res)
}
}
func TestServer_ListAssignments_Pagination_InputOutOfRange(t *testing.T) {
helpers.ClearCache()
db := dbTest.SetupDB(t)
ctx := context.Background()
count := 100
validators := make([]*ethpb.Validator, 0, count)
for i := 0; i < count; i++ {
pubKey := make([]byte, params.BeaconConfig().BLSPubkeyLength)
withdrawalCred := make([]byte, 32)
binary.LittleEndian.PutUint64(pubKey, uint64(i))
validators = append(validators, &ethpb.Validator{
PublicKey: pubKey,
WithdrawalCredentials: withdrawalCred,
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
ActivationEpoch: 0,
})
}
blk := util.NewBeaconBlock()
blockRoot, err := blk.Block.HashTreeRoot()
require.NoError(t, err)
s, err := util.NewBeaconState()
require.NoError(t, err)
require.NoError(t, s.SetValidators(validators))
require.NoError(t, db.SaveState(ctx, s, blockRoot))
require.NoError(t, db.SaveGenesisBlockRoot(ctx, blockRoot))
bs := &Server{
BeaconDB: db,
HeadFetcher: &mock.ChainService{
State: s,
},
FinalizationFetcher: &mock.ChainService{
FinalizedCheckPoint: &ethpb.Checkpoint{
Epoch: 0,
},
},
GenesisTimeFetcher: &mock.ChainService{},
StateGen: stategen.New(db),
ReplayerBuilder: mockstategen.NewMockReplayerBuilder(mockstategen.WithMockState(s)),
}
wanted := fmt.Sprintf("page start %d >= list %d", 500, count)
_, err = bs.ListValidatorAssignments(context.Background(), &ethpb.ListValidatorAssignmentsRequest{
PageToken: strconv.Itoa(2),
QueryFilter: &ethpb.ListValidatorAssignmentsRequest_Genesis{Genesis: true},
})
assert.ErrorContains(t, wanted, err)
}
func TestServer_ListAssignments_Pagination_ExceedsMaxPageSize(t *testing.T) {
bs := &Server{}
exceedsMax := int32(cmd.Get().MaxRPCPageSize + 1)
wanted := fmt.Sprintf("Requested page size %d can not be greater than max size %d", exceedsMax, cmd.Get().MaxRPCPageSize)
req := &ethpb.ListValidatorAssignmentsRequest{
PageToken: strconv.Itoa(0),
PageSize: exceedsMax,
}
_, err := bs.ListValidatorAssignments(context.Background(), req)
assert.ErrorContains(t, wanted, err)
}
func TestServer_ListAssignments_Pagination_DefaultPageSize_NoArchive(t *testing.T) {
helpers.ClearCache()
db := dbTest.SetupDB(t)
ctx := context.Background()
count := 500
validators := make([]*ethpb.Validator, 0, count)
for i := 0; i < count; i++ {
pubKey := make([]byte, params.BeaconConfig().BLSPubkeyLength)
withdrawalCred := make([]byte, 32)
binary.LittleEndian.PutUint64(pubKey, uint64(i))
// Mark the validators with index divisible by 3 inactive.
if i%3 == 0 {
validators = append(validators, &ethpb.Validator{
PublicKey: pubKey,
WithdrawalCredentials: withdrawalCred,
ExitEpoch: 0,
ActivationEpoch: 0,
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
})
} else {
validators = append(validators, &ethpb.Validator{
PublicKey: pubKey,
WithdrawalCredentials: withdrawalCred,
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
ActivationEpoch: 0,
})
}
}
b := util.NewBeaconBlock()
blockRoot, err := b.Block.HashTreeRoot()
require.NoError(t, err)
s, err := util.NewBeaconState()
require.NoError(t, err)
require.NoError(t, s.SetValidators(validators))
require.NoError(t, db.SaveState(ctx, s, blockRoot))
require.NoError(t, db.SaveGenesisBlockRoot(ctx, blockRoot))
bs := &Server{
BeaconDB: db,
HeadFetcher: &mock.ChainService{
State: s,
},
FinalizationFetcher: &mock.ChainService{
FinalizedCheckPoint: &ethpb.Checkpoint{
Epoch: 0,
},
},
GenesisTimeFetcher: &mock.ChainService{},
StateGen: stategen.New(db),
ReplayerBuilder: mockstategen.NewMockReplayerBuilder(mockstategen.WithMockState(s)),
}
res, err := bs.ListValidatorAssignments(context.Background(), &ethpb.ListValidatorAssignmentsRequest{
QueryFilter: &ethpb.ListValidatorAssignmentsRequest_Genesis{Genesis: true},
})
require.NoError(t, err)
// Construct the wanted assignments.
var wanted []*ethpb.ValidatorAssignments_CommitteeAssignment
activeIndices, err := helpers.ActiveValidatorIndices(ctx, s, 0)
require.NoError(t, err)
committeeAssignments, proposerIndexToSlots, err := helpers.CommitteeAssignments(context.Background(), s, 0)
require.NoError(t, err)
for _, index := range activeIndices[0:params.BeaconConfig().DefaultPageSize] {
val, err := s.ValidatorAtIndex(index)
require.NoError(t, err)
wanted = append(wanted, &ethpb.ValidatorAssignments_CommitteeAssignment{
BeaconCommittees: committeeAssignments[index].Committee,
CommitteeIndex: committeeAssignments[index].CommitteeIndex,
AttesterSlot: committeeAssignments[index].AttesterSlot,
ProposerSlots: proposerIndexToSlots[index],
PublicKey: val.PublicKey,
ValidatorIndex: index,
})
}
assert.DeepSSZEqual(t, wanted, res.Assignments, "Did not receive wanted assignments")
}
func TestServer_ListAssignments_FilterPubkeysIndices_NoPagination(t *testing.T) {
helpers.ClearCache()
db := dbTest.SetupDB(t)
ctx := context.Background()
count := 100
validators := make([]*ethpb.Validator, 0, count)
withdrawCreds := make([]byte, 32)
for i := 0; i < count; i++ {
pubKey := make([]byte, params.BeaconConfig().BLSPubkeyLength)
binary.LittleEndian.PutUint64(pubKey, uint64(i))
val := &ethpb.Validator{
PublicKey: pubKey,
WithdrawalCredentials: withdrawCreds,
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
}
validators = append(validators, val)
}
b := util.NewBeaconBlock()
blockRoot, err := b.Block.HashTreeRoot()
require.NoError(t, err)
s, err := util.NewBeaconState()
require.NoError(t, err)
require.NoError(t, s.SetValidators(validators))
require.NoError(t, db.SaveState(ctx, s, blockRoot))
require.NoError(t, db.SaveGenesisBlockRoot(ctx, blockRoot))
bs := &Server{
BeaconDB: db,
FinalizationFetcher: &mock.ChainService{
FinalizedCheckPoint: &ethpb.Checkpoint{
Epoch: 0,
},
},
GenesisTimeFetcher: &mock.ChainService{},
StateGen: stategen.New(db),
ReplayerBuilder: mockstategen.NewMockReplayerBuilder(mockstategen.WithMockState(s)),
}
pubKey1 := make([]byte, params.BeaconConfig().BLSPubkeyLength)
binary.LittleEndian.PutUint64(pubKey1, 1)
pubKey2 := make([]byte, params.BeaconConfig().BLSPubkeyLength)
binary.LittleEndian.PutUint64(pubKey2, 2)
req := &ethpb.ListValidatorAssignmentsRequest{PublicKeys: [][]byte{pubKey1, pubKey2}, Indices: []types.ValidatorIndex{2, 3}}
res, err := bs.ListValidatorAssignments(context.Background(), req)
require.NoError(t, err)
// Construct the wanted assignments.
var wanted []*ethpb.ValidatorAssignments_CommitteeAssignment
activeIndices, err := helpers.ActiveValidatorIndices(ctx, s, 0)
require.NoError(t, err)
committeeAssignments, proposerIndexToSlots, err := helpers.CommitteeAssignments(context.Background(), s, 0)
require.NoError(t, err)
for _, index := range activeIndices[1:4] {
val, err := s.ValidatorAtIndex(index)
require.NoError(t, err)
wanted = append(wanted, &ethpb.ValidatorAssignments_CommitteeAssignment{
BeaconCommittees: committeeAssignments[index].Committee,
CommitteeIndex: committeeAssignments[index].CommitteeIndex,
AttesterSlot: committeeAssignments[index].AttesterSlot,
ProposerSlots: proposerIndexToSlots[index],
PublicKey: val.PublicKey,
ValidatorIndex: index,
})
}
assert.DeepEqual(t, wanted, res.Assignments, "Did not receive wanted assignments")
}
func TestServer_ListAssignments_CanFilterPubkeysIndices_WithPagination(t *testing.T) {
helpers.ClearCache()
db := dbTest.SetupDB(t)
ctx := context.Background()
count := 100
validators := make([]*ethpb.Validator, 0, count)
withdrawCred := make([]byte, 32)
for i := 0; i < count; i++ {
pubKey := make([]byte, params.BeaconConfig().BLSPubkeyLength)
binary.LittleEndian.PutUint64(pubKey, uint64(i))
val := &ethpb.Validator{
PublicKey: pubKey,
WithdrawalCredentials: withdrawCred,
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
}
validators = append(validators, val)
}
b := util.NewBeaconBlock()
blockRoot, err := b.Block.HashTreeRoot()
require.NoError(t, err)
s, err := util.NewBeaconState()
require.NoError(t, err)
w, err := wrapper.WrappedSignedBeaconBlock(b)
require.NoError(t, err)
require.NoError(t, s.SetValidators(validators))
require.NoError(t, db.SaveState(ctx, s, blockRoot))
require.NoError(t, db.SaveBlock(ctx, w))
require.NoError(t, db.SaveGenesisBlockRoot(ctx, blockRoot))
bs := &Server{
BeaconDB: db,
FinalizationFetcher: &mock.ChainService{
FinalizedCheckPoint: &ethpb.Checkpoint{
Epoch: 0,
},
},
GenesisTimeFetcher: &mock.ChainService{},
StateGen: stategen.New(db),
}
addDefaultReplayerBuilder(bs, db)
req := &ethpb.ListValidatorAssignmentsRequest{Indices: []types.ValidatorIndex{1, 2, 3, 4, 5, 6}, PageSize: 2, PageToken: "1"}
res, err := bs.ListValidatorAssignments(context.Background(), req)
require.NoError(t, err)
// Construct the wanted assignments.
var assignments []*ethpb.ValidatorAssignments_CommitteeAssignment
activeIndices, err := helpers.ActiveValidatorIndices(ctx, s, 0)
require.NoError(t, err)
committeeAssignments, proposerIndexToSlots, err := helpers.CommitteeAssignments(context.Background(), s, 0)
require.NoError(t, err)
for _, index := range activeIndices[3:5] {
val, err := s.ValidatorAtIndex(index)
require.NoError(t, err)
assignments = append(assignments, &ethpb.ValidatorAssignments_CommitteeAssignment{
BeaconCommittees: committeeAssignments[index].Committee,
CommitteeIndex: committeeAssignments[index].CommitteeIndex,
AttesterSlot: committeeAssignments[index].AttesterSlot,
ProposerSlots: proposerIndexToSlots[index],
PublicKey: val.PublicKey,
ValidatorIndex: index,
})
}
wantedRes := &ethpb.ValidatorAssignments{
Assignments: assignments,
TotalSize: int32(len(req.Indices)),
NextPageToken: "2",
}
assert.DeepEqual(t, wantedRes, res, "Did not get wanted assignments")
// Test the wrap around scenario.
assignments = nil
req = &ethpb.ListValidatorAssignmentsRequest{Indices: []types.ValidatorIndex{1, 2, 3, 4, 5, 6}, PageSize: 5, PageToken: "1"}
res, err = bs.ListValidatorAssignments(context.Background(), req)
require.NoError(t, err)
cAssignments, proposerIndexToSlots, err := helpers.CommitteeAssignments(context.Background(), s, 0)
require.NoError(t, err)
for _, index := range activeIndices[6:7] {
val, err := s.ValidatorAtIndex(index)
require.NoError(t, err)
assignments = append(assignments, &ethpb.ValidatorAssignments_CommitteeAssignment{
BeaconCommittees: cAssignments[index].Committee,
CommitteeIndex: cAssignments[index].CommitteeIndex,
AttesterSlot: cAssignments[index].AttesterSlot,
ProposerSlots: proposerIndexToSlots[index],
PublicKey: val.PublicKey,
ValidatorIndex: index,
})
}
wantedRes = &ethpb.ValidatorAssignments{
Assignments: assignments,
TotalSize: int32(len(req.Indices)),
NextPageToken: "",
}
assert.DeepEqual(t, wantedRes, res, "Did not receive wanted assignments")
}