From d32493d43bf499eac895a972b4bf030c2f2eb840 Mon Sep 17 00:00:00 2001 From: Preston Van Loon Date: Fri, 31 Jan 2020 17:18:36 -0800 Subject: [PATCH] Ensure exits are not for an already exited validator (#4701) * ensure exits are not for an already exited validator --- beacon-chain/operations/voluntaryexits/BUILD.bazel | 3 +++ beacon-chain/operations/voluntaryexits/service.go | 11 +++++++---- .../operations/voluntaryexits/service_test.go | 14 ++++++++++++-- beacon-chain/rpc/validator/exit.go | 2 +- beacon-chain/rpc/validator/proposer.go | 7 ++++++- beacon-chain/sync/subscriber_handlers.go | 2 +- 6 files changed, 30 insertions(+), 9 deletions(-) diff --git a/beacon-chain/operations/voluntaryexits/BUILD.bazel b/beacon-chain/operations/voluntaryexits/BUILD.bazel index f1989caef7..1c91e7f190 100644 --- a/beacon-chain/operations/voluntaryexits/BUILD.bazel +++ b/beacon-chain/operations/voluntaryexits/BUILD.bazel @@ -10,6 +10,7 @@ go_library( visibility = ["//beacon-chain:__subpackages__"], deps = [ "//beacon-chain/core/helpers:go_default_library", + "//beacon-chain/state:go_default_library", "//shared/params:go_default_library", "@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library", ], @@ -21,6 +22,8 @@ go_test( srcs = ["service_test.go"], embed = [":go_default_library"], deps = [ + "//beacon-chain/state:go_default_library", + "//proto/beacon/p2p/v1:go_default_library", "//shared/params:go_default_library", "@com_github_gogo_protobuf//proto:go_default_library", "@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library", diff --git a/beacon-chain/operations/voluntaryexits/service.go b/beacon-chain/operations/voluntaryexits/service.go index 6d168af0ec..adf988bccc 100644 --- a/beacon-chain/operations/voluntaryexits/service.go +++ b/beacon-chain/operations/voluntaryexits/service.go @@ -7,6 +7,7 @@ import ( ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" + beaconstate "github.com/prysmaticlabs/prysm/beacon-chain/state" "github.com/prysmaticlabs/prysm/shared/params" ) @@ -29,7 +30,7 @@ func NewPool() *Pool { // PendingExits returns exits that are ready for inclusion at the given slot. This method will not // return more than the block enforced MaxVoluntaryExits. -func (p *Pool) PendingExits(slot uint64) []*ethpb.SignedVoluntaryExit { +func (p *Pool) PendingExits(state *beaconstate.BeaconState, slot uint64) []*ethpb.SignedVoluntaryExit { p.lock.RLock() defer p.lock.RUnlock() pending := make([]*ethpb.SignedVoluntaryExit, 0) @@ -37,7 +38,9 @@ func (p *Pool) PendingExits(slot uint64) []*ethpb.SignedVoluntaryExit { if e.Exit.Epoch > helpers.SlotToEpoch(slot) { continue } - pending = append(pending, e) + if v, err := state.ValidatorAtIndexReadOnly(e.Exit.ValidatorIndex); err == nil && v.ExitEpoch() == params.BeaconConfig().FarFutureEpoch { + pending = append(pending, e) + } } if len(pending) > int(params.BeaconConfig().MaxVoluntaryExits) { pending = pending[:params.BeaconConfig().MaxVoluntaryExits] @@ -47,7 +50,7 @@ func (p *Pool) PendingExits(slot uint64) []*ethpb.SignedVoluntaryExit { // InsertVoluntaryExit into the pool. This method is a no-op if the pending exit already exists, // has been included recently, or the validator is already exited. -func (p *Pool) InsertVoluntaryExit(ctx context.Context, validators []*ethpb.Validator, exit *ethpb.SignedVoluntaryExit) { +func (p *Pool) InsertVoluntaryExit(ctx context.Context, state *beaconstate.BeaconState, exit *ethpb.SignedVoluntaryExit) { p.lock.Lock() defer p.lock.Unlock() @@ -57,7 +60,7 @@ func (p *Pool) InsertVoluntaryExit(ctx context.Context, validators []*ethpb.Vali } // Has the validator been exited already? - if len(validators) <= int(exit.Exit.ValidatorIndex) || validators[exit.Exit.ValidatorIndex].ExitEpoch != params.BeaconConfig().FarFutureEpoch { + if v, err := state.ValidatorAtIndexReadOnly(exit.Exit.ValidatorIndex); err != nil || v.ExitEpoch() != params.BeaconConfig().FarFutureEpoch { return } diff --git a/beacon-chain/operations/voluntaryexits/service_test.go b/beacon-chain/operations/voluntaryexits/service_test.go index 29837f9e97..f0a2f4ef21 100644 --- a/beacon-chain/operations/voluntaryexits/service_test.go +++ b/beacon-chain/operations/voluntaryexits/service_test.go @@ -7,6 +7,8 @@ import ( "github.com/gogo/protobuf/proto" ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" + beaconstate "github.com/prysmaticlabs/prysm/beacon-chain/state" + p2ppb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" "github.com/prysmaticlabs/prysm/shared/params" ) @@ -211,7 +213,11 @@ func TestPool_InsertVoluntaryExit(t *testing.T) { pending: tt.fields.pending, included: tt.fields.included, } - p.InsertVoluntaryExit(ctx, validators, tt.args.exit) + s, err := beaconstate.InitializeFromProtoUnsafe(&p2ppb.BeaconState{Validators: validators}) + if err != nil { + t.Fatal(err) + } + p.InsertVoluntaryExit(ctx, s, tt.args.exit) if len(p.pending) != len(tt.want) { t.Fatalf("Mismatched lengths of pending list. Got %d, wanted %d.", len(p.pending), len(tt.want)) } @@ -444,7 +450,11 @@ func TestPool_PendingExits(t *testing.T) { p := &Pool{ pending: tt.fields.pending, } - if got := p.PendingExits(tt.args.slot); !reflect.DeepEqual(got, tt.want) { + s, err := beaconstate.InitializeFromProtoUnsafe(&p2ppb.BeaconState{Validators: []*ethpb.Validator{{ExitEpoch: params.BeaconConfig().FarFutureEpoch}}}) + if err != nil { + t.Fatal(err) + } + if got := p.PendingExits(s, tt.args.slot); !reflect.DeepEqual(got, tt.want) { t.Errorf("PendingExits() = %v, want %v", got, tt.want) } }) diff --git a/beacon-chain/rpc/validator/exit.go b/beacon-chain/rpc/validator/exit.go index 974d476d6b..71da619cdd 100644 --- a/beacon-chain/rpc/validator/exit.go +++ b/beacon-chain/rpc/validator/exit.go @@ -40,7 +40,7 @@ func (vs *Server) ProposeExit(ctx context.Context, req *ethpb.SignedVoluntaryExi }, }) - vs.ExitPool.InsertVoluntaryExit(ctx, s.Validators(), req) + vs.ExitPool.InsertVoluntaryExit(ctx, s, req) return &ptypes.Empty{}, vs.P2P.Broadcast(ctx, req) } diff --git a/beacon-chain/rpc/validator/proposer.go b/beacon-chain/rpc/validator/proposer.go index 2208a9f042..4857b19f7f 100644 --- a/beacon-chain/rpc/validator/proposer.go +++ b/beacon-chain/rpc/validator/proposer.go @@ -63,6 +63,11 @@ func (vs *Server) GetBlock(ctx context.Context, req *ethpb.BlockRequest) (*ethpb graffiti := bytesutil.ToBytes32(req.Graffiti) + head, err := vs.HeadFetcher.HeadState(ctx) + if err != nil { + return nil, status.Errorf(codes.Internal, "Could not get head state %v", err) + } + blk := ðpb.BeaconBlock{ Slot: req.Slot, ParentRoot: parentRoot[:], @@ -75,7 +80,7 @@ func (vs *Server) GetBlock(ctx context.Context, req *ethpb.BlockRequest) (*ethpb // TODO(2766): Implement rest of the retrievals for beacon block operations ProposerSlashings: []*ethpb.ProposerSlashing{}, AttesterSlashings: []*ethpb.AttesterSlashing{}, - VoluntaryExits: vs.ExitPool.PendingExits(req.Slot), + VoluntaryExits: vs.ExitPool.PendingExits(head, req.Slot), Graffiti: graffiti[:], }, } diff --git a/beacon-chain/sync/subscriber_handlers.go b/beacon-chain/sync/subscriber_handlers.go index 899a49ab40..3e156359d8 100644 --- a/beacon-chain/sync/subscriber_handlers.go +++ b/beacon-chain/sync/subscriber_handlers.go @@ -12,7 +12,7 @@ func (r *Service) voluntaryExitSubscriber(ctx context.Context, msg proto.Message if err != nil { return err } - r.exitPool.InsertVoluntaryExit(ctx, s.Validators(), msg.(*ethpb.SignedVoluntaryExit)) + r.exitPool.InsertVoluntaryExit(ctx, s, msg.(*ethpb.SignedVoluntaryExit)) return nil }