package blocks_test import ( "context" "testing" ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks" "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" stateTrie "github.com/prysmaticlabs/prysm/beacon-chain/state" pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" "github.com/prysmaticlabs/prysm/shared/bls" "github.com/prysmaticlabs/prysm/shared/params" "github.com/prysmaticlabs/prysm/shared/testutil/assert" "github.com/prysmaticlabs/prysm/shared/testutil/require" ) func TestProcessVoluntaryExits_ValidatorNotActive(t *testing.T) { exits := []*ethpb.SignedVoluntaryExit{ { Exit: ðpb.VoluntaryExit{ ValidatorIndex: 0, }, }, } registry := []*ethpb.Validator{ { ExitEpoch: 0, }, } state, err := stateTrie.InitializeFromProto(&pb.BeaconState{ Validators: registry, }) require.NoError(t, err) block := ðpb.BeaconBlock{ Body: ðpb.BeaconBlockBody{ VoluntaryExits: exits, }, } want := "non-active validator cannot exit" _, err = blocks.ProcessVoluntaryExits(context.Background(), state, block.Body) assert.ErrorContains(t, want, err) } func TestProcessVoluntaryExits_InvalidExitEpoch(t *testing.T) { exits := []*ethpb.SignedVoluntaryExit{ { Exit: ðpb.VoluntaryExit{ Epoch: 10, }, }, } registry := []*ethpb.Validator{ { ExitEpoch: params.BeaconConfig().FarFutureEpoch, }, } state, err := stateTrie.InitializeFromProto(&pb.BeaconState{ Validators: registry, Slot: 0, }) require.NoError(t, err) block := ðpb.BeaconBlock{ Body: ðpb.BeaconBlockBody{ VoluntaryExits: exits, }, } want := "expected current epoch >= exit epoch" _, err = blocks.ProcessVoluntaryExits(context.Background(), state, block.Body) assert.ErrorContains(t, want, err) } func TestProcessVoluntaryExits_NotActiveLongEnoughToExit(t *testing.T) { exits := []*ethpb.SignedVoluntaryExit{ { Exit: ðpb.VoluntaryExit{ ValidatorIndex: 0, Epoch: 0, }, }, } registry := []*ethpb.Validator{ { ExitEpoch: params.BeaconConfig().FarFutureEpoch, }, } state, err := stateTrie.InitializeFromProto(&pb.BeaconState{ Validators: registry, Slot: 10, }) require.NoError(t, err) block := ðpb.BeaconBlock{ Body: ðpb.BeaconBlockBody{ VoluntaryExits: exits, }, } want := "validator has not been active long enough to exit" _, err = blocks.ProcessVoluntaryExits(context.Background(), state, block.Body) assert.ErrorContains(t, want, err) } func TestProcessVoluntaryExits_AppliesCorrectStatus(t *testing.T) { exits := []*ethpb.SignedVoluntaryExit{ { Exit: ðpb.VoluntaryExit{ ValidatorIndex: 0, Epoch: 0, }, }, } registry := []*ethpb.Validator{ { ExitEpoch: params.BeaconConfig().FarFutureEpoch, ActivationEpoch: 0, }, } state, err := stateTrie.InitializeFromProto(&pb.BeaconState{ Validators: registry, Fork: &pb.Fork{ CurrentVersion: params.BeaconConfig().GenesisForkVersion, PreviousVersion: params.BeaconConfig().GenesisForkVersion, }, Slot: params.BeaconConfig().SlotsPerEpoch * 5, }) require.NoError(t, err) err = state.SetSlot(state.Slot() + (params.BeaconConfig().ShardCommitteePeriod * params.BeaconConfig().SlotsPerEpoch)) require.NoError(t, err) priv := bls.RandKey() val, err := state.ValidatorAtIndex(0) require.NoError(t, err) val.PublicKey = priv.PublicKey().Marshal()[:] require.NoError(t, state.UpdateValidatorAtIndex(0, val)) exits[0].Signature, err = helpers.ComputeDomainAndSign(state, helpers.CurrentEpoch(state), exits[0].Exit, params.BeaconConfig().DomainVoluntaryExit, priv) require.NoError(t, err) block := ðpb.BeaconBlock{ Body: ðpb.BeaconBlockBody{ VoluntaryExits: exits, }, } newState, err := blocks.ProcessVoluntaryExits(context.Background(), state, block.Body) require.NoError(t, err, "Could not process exits") newRegistry := newState.Validators() if newRegistry[0].ExitEpoch != helpers.ActivationExitEpoch(state.Slot()/params.BeaconConfig().SlotsPerEpoch) { t.Errorf("Expected validator exit epoch to be %d, got %d", helpers.ActivationExitEpoch(state.Slot()/params.BeaconConfig().SlotsPerEpoch), newRegistry[0].ExitEpoch) } }