diff --git a/beacon-chain/rpc/validator/exit.go b/beacon-chain/rpc/validator/exit.go index 58544252e281..c0750beb92f1 100644 --- a/beacon-chain/rpc/validator/exit.go +++ b/beacon-chain/rpc/validator/exit.go @@ -9,6 +9,7 @@ import ( "github.com/prysmaticlabs/prysm/beacon-chain/core/feed" opfeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/operation" "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" + "github.com/prysmaticlabs/prysm/shared/params" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) @@ -22,6 +23,12 @@ func (vs *Server) ProposeExit(ctx context.Context, req *ethpb.SignedVoluntaryExi if err != nil { return nil, status.Errorf(codes.Internal, "Could not get head state: %v", err) } + if req.Exit == nil { + return nil, status.Error(codes.InvalidArgument, "voluntary exit does not exist") + } + if req.Signature == nil || len(req.Signature) != params.BeaconConfig().BLSSignatureLength { + return nil, status.Error(codes.InvalidArgument, "invalid signature provided") + } // Confirm the validator is eligible to exit with the parameters provided. val, err := s.ValidatorAtIndex(req.Exit.ValidatorIndex) diff --git a/beacon-chain/rpc/validator/exit_test.go b/beacon-chain/rpc/validator/exit_test.go index 25cdc45beb9c..2ba9e8c3a289 100644 --- a/beacon-chain/rpc/validator/exit_test.go +++ b/beacon-chain/rpc/validator/exit_test.go @@ -5,6 +5,7 @@ import ( "testing" "time" + "github.com/prysmaticlabs/prysm/shared/bytesutil" ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" "github.com/prysmaticlabs/go-ssz" mockChain "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing" @@ -110,3 +111,87 @@ func TestSub(t *testing.T) { } } } + +func TestProposeExit_NoPanic(t *testing.T) { + db := dbutil.SetupDB(t) + defer dbutil.TeardownDB(t, db) + ctx := context.Background() + testutil.ResetCache() + deposits, keys, err := testutil.DeterministicDepositsAndKeys(params.BeaconConfig().MinGenesisActiveValidatorCount) + if err != nil { + t.Fatal(err) + } + beaconState, err := state.GenesisBeaconState(deposits, 0, ðpb.Eth1Data{BlockHash: make([]byte, 32)}) + if err != nil { + t.Fatal(err) + } + block := blk.NewGenesisBlock([]byte{}) + if err := db.SaveBlock(ctx, block); err != nil { + t.Fatalf("Could not save genesis block: %v", err) + } + genesisRoot, err := ssz.HashTreeRoot(block.Block) + if err != nil { + t.Fatalf("Could not get signing root %v", err) + } + + // Set genesis time to be 100 epochs ago. + genesisTime := time.Now().Add(time.Duration(-100*int64(params.BeaconConfig().SecondsPerSlot*params.BeaconConfig().SlotsPerEpoch)) * time.Second) + mockChainService := &mockChain.ChainService{State: beaconState, Root: genesisRoot[:], Genesis: genesisTime} + server := &Server{ + BeaconDB: db, + HeadFetcher: mockChainService, + SyncChecker: &mockSync.Sync{IsSyncing: false}, + GenesisTimeFetcher: mockChainService, + StateNotifier: mockChainService.StateNotifier(), + OperationNotifier: mockChainService.OperationNotifier(), + ExitPool: voluntaryexits.NewPool(), + P2P: mockp2p.NewTestP2P(t), + } + + // Subscribe to operation notifications. + opChannel := make(chan *feed.Event, 1024) + opSub := server.OperationNotifier.OperationFeed().Subscribe(opChannel) + defer opSub.Unsubscribe() + + req := ðpb.SignedVoluntaryExit{} + _, err = server.ProposeExit(context.Background(), req) + if err == nil { + t.Fatal("Expected error for no exit existing") + } + + // Send the request, expect a result on the state feed. + epoch := uint64(2048) + validatorIndex := uint64(0) + req = ðpb.SignedVoluntaryExit{ + Exit: ðpb.VoluntaryExit{ + Epoch: epoch, + ValidatorIndex: validatorIndex, + }, + } + + _, err = server.ProposeExit(context.Background(), req) + if err == nil { + t.Fatal("Expected error for no signature exists") + } + req.Signature = bytesutil.FromBytes48([48]byte{}) + + _, err = server.ProposeExit(context.Background(), req) + if err == nil { + t.Fatal("Expected error for invalid signature length") + } + + domain, err := helpers.Domain(beaconState.Fork(), epoch, params.BeaconConfig().DomainVoluntaryExit, beaconState.GenesisValidatorRoot()) + if err != nil { + t.Fatal(err) + } + sigRoot, err := helpers.ComputeSigningRoot(req.Exit, domain) + if err != nil { + t.Fatalf("Could not compute signing root: %v", err) + } + req.Signature = keys[0].Sign(sigRoot[:]).Marshal() + + _, err = server.ProposeExit(context.Background(), req) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } +}