package rpc import ( "context" "github.com/gogo/protobuf/proto" "github.com/gogo/protobuf/types" "github.com/pkg/errors" "github.com/prysmaticlabs/go-ssz" "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1" "github.com/prysmaticlabs/prysm/slasher/db" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) // Server defines a server implementation of the gRPC Slasher service, // providing RPC endpoints for retrieving slashing proofs for malicious validators. type Server struct { SlasherDB *db.Store ctx context.Context } // IsSlashableAttestation returns an attester slashing if the attestation submitted // is a slashable vote. func (ss *Server) IsSlashableAttestation(ctx context.Context, req *ethpb.IndexedAttestation) (*ethpb.AttesterSlashingResponse, error) { //TODO(#3133): add signature validation if err := ss.SlasherDB.SaveIndexedAttestation(req); err != nil { return nil, err } tEpoch := req.Data.Target.Epoch indices := append(req.CustodyBit_0Indices, req.CustodyBit_1Indices...) root, err := ssz.HashTreeRoot(req.Data) if err != nil { return nil, err } atsSlashinngRes := ðpb.AttesterSlashingResponse{} for _, idx := range indices { atts, err := ss.SlasherDB.DoubleVotes(tEpoch, idx, root[:], req) if err != nil { return nil, err } if atts != nil && len(atts) > 0 { atsSlashinngRes.AttesterSlashing = append(atsSlashinngRes.AttesterSlashing, atts...) } } //TODO(#3133): add surround detection return atsSlashinngRes, nil } // IsSlashableBlock returns a proposer slashing if the block header submitted is // a slashable proposal. func (ss *Server) IsSlashableBlock(ctx context.Context, psr *ethpb.ProposerSlashingRequest) (*ethpb.ProposerSlashingResponse, error) { //TODO(#3133): add signature validation epoch := helpers.SlotToEpoch(psr.BlockHeader.Slot) blockHeaders, err := ss.SlasherDB.BlockHeader(epoch, psr.ValidatorIndex) if err != nil { return nil, errors.Wrap(err, "slasher service error while trying to retrieve blocks") } pSlashingsResponse := ðpb.ProposerSlashingResponse{} presentInDb := false for _, bh := range blockHeaders { if proto.Equal(bh, psr.BlockHeader) { presentInDb = true continue } pSlashingsResponse.ProposerSlashing = append(pSlashingsResponse.ProposerSlashing, ðpb.ProposerSlashing{ProposerIndex: psr.ValidatorIndex, Header_1: psr.BlockHeader, Header_2: bh}) } if len(pSlashingsResponse.ProposerSlashing) == 0 && !presentInDb { err = ss.SlasherDB.SaveBlockHeader(epoch, psr.ValidatorIndex, psr.BlockHeader) if err != nil { return nil, err } } return pSlashingsResponse, nil } // SlashableProposals is a subscription to receive all slashable proposer slashing events found by the watchtower. func (ss *Server) SlashableProposals(req *types.Empty, server ethpb.Slasher_SlashableProposalsServer) error { //TODO(3133): implement stream provider for newly discovered listening to slashable proposals. return status.Error(codes.Unimplemented, "not implemented") } // SlashableAttestations is a subscription to receive all slashable attester slashing events found by the watchtower. func (ss *Server) SlashableAttestations(req *types.Empty, server ethpb.Slasher_SlashableAttestationsServer) error { //TODO(3133): implement stream provider for newly discovered listening to slashable attestation. return status.Error(codes.Unimplemented, "not implemented") }