github.com/prysmaticlabs/prysm@v1.4.4/slasher/rpc/server.go (about)

     1  package rpc
     2  
     3  import (
     4  	"context"
     5  	"sync"
     6  
     7  	"github.com/pkg/errors"
     8  	types "github.com/prysmaticlabs/eth2-types"
     9  	"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
    10  	ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
    11  	slashpb "github.com/prysmaticlabs/prysm/proto/slashing"
    12  	"github.com/prysmaticlabs/prysm/shared/attestationutil"
    13  	"github.com/prysmaticlabs/prysm/shared/bls"
    14  	"github.com/prysmaticlabs/prysm/shared/p2putils"
    15  	"github.com/prysmaticlabs/prysm/shared/params"
    16  	"github.com/prysmaticlabs/prysm/slasher/beaconclient"
    17  	"github.com/prysmaticlabs/prysm/slasher/db"
    18  	"github.com/prysmaticlabs/prysm/slasher/detection"
    19  	"github.com/sirupsen/logrus"
    20  	"go.opencensus.io/trace"
    21  	"google.golang.org/grpc/codes"
    22  	"google.golang.org/grpc/status"
    23  )
    24  
    25  // Server defines a server implementation of the gRPC Slasher service,
    26  // providing RPC endpoints for retrieving slashing proofs for malicious validators.
    27  type Server struct {
    28  	ctx             context.Context
    29  	detector        *detection.Service
    30  	slasherDB       db.Database
    31  	beaconClient    *beaconclient.Service
    32  	attestationLock sync.Mutex
    33  	proposeLock     sync.Mutex
    34  }
    35  
    36  // HighestAttestations returns the highest observed attestation source and epoch for a given validator id.
    37  func (s *Server) HighestAttestations(ctx context.Context, req *slashpb.HighestAttestationRequest) (*slashpb.HighestAttestationResponse, error) {
    38  	ctx, span := trace.StartSpan(ctx, "history.HighestAttestations")
    39  	defer span.End()
    40  
    41  	ret := make([]*slashpb.HighestAttestation, 0)
    42  	for _, id := range req.ValidatorIds {
    43  		if ctx.Err() != nil {
    44  			return nil, ctx.Err()
    45  		}
    46  
    47  		res, err := s.slasherDB.HighestAttestation(ctx, id)
    48  		if err != nil {
    49  			return nil, err
    50  		}
    51  		if res != nil {
    52  			ret = append(ret, &slashpb.HighestAttestation{
    53  				ValidatorId:        res.ValidatorId,
    54  				HighestTargetEpoch: res.HighestTargetEpoch,
    55  				HighestSourceEpoch: res.HighestSourceEpoch,
    56  			})
    57  		}
    58  	}
    59  
    60  	return &slashpb.HighestAttestationResponse{
    61  		Attestations: ret,
    62  	}, nil
    63  }
    64  
    65  // IsSlashableAttestation returns an attester slashing if the attestation submitted
    66  // is a slashable vote.
    67  func (s *Server) IsSlashableAttestation(ctx context.Context, req *ethpb.IndexedAttestation) (*slashpb.AttesterSlashingResponse, error) {
    68  	ctx, span := trace.StartSpan(ctx, "detection.IsSlashableAttestation")
    69  	defer span.End()
    70  
    71  	log.WithFields(logrus.Fields{
    72  		"slot":    req.Data.Slot,
    73  		"indices": req.AttestingIndices,
    74  	}).Debug("Received attestation via RPC")
    75  	if req == nil {
    76  		return nil, status.Error(codes.InvalidArgument, "nil request provided")
    77  	}
    78  	if req.Data == nil {
    79  		return nil, status.Error(codes.InvalidArgument, "nil request data provided")
    80  	}
    81  	if req.Data.Target == nil {
    82  		return nil, status.Error(codes.InvalidArgument, "nil request data target provided")
    83  	}
    84  	if req.Data.Source == nil {
    85  		return nil, status.Error(codes.InvalidArgument, "nil request data source provided")
    86  	}
    87  	if req.Signature == nil {
    88  		return nil, status.Error(codes.InvalidArgument, "nil signature provided")
    89  	}
    90  
    91  	err := attestationutil.IsValidAttestationIndices(ctx, req)
    92  	if err != nil {
    93  		return nil, err
    94  	}
    95  	gvr, err := s.beaconClient.GenesisValidatorsRoot(ctx)
    96  	if err != nil {
    97  		return nil, err
    98  	}
    99  	fork, err := p2putils.Fork(req.Data.Target.Epoch)
   100  	if err != nil {
   101  		return nil, err
   102  	}
   103  	domain, err := helpers.Domain(fork, req.Data.Target.Epoch, params.BeaconConfig().DomainBeaconAttester, gvr)
   104  	if err != nil {
   105  		return nil, err
   106  	}
   107  	indices := make([]types.ValidatorIndex, len(req.AttestingIndices))
   108  	for i, index := range req.AttestingIndices {
   109  		indices[i] = types.ValidatorIndex(index)
   110  	}
   111  	pkMap, err := s.beaconClient.FindOrGetPublicKeys(ctx, indices)
   112  	if err != nil {
   113  		return nil, err
   114  	}
   115  	var pubkeys []bls.PublicKey
   116  	for _, pkBytes := range pkMap {
   117  		pk, err := bls.PublicKeyFromBytes(pkBytes)
   118  		if err != nil {
   119  			return nil, errors.Wrap(err, "could not deserialize validator public key")
   120  		}
   121  		pubkeys = append(pubkeys, pk)
   122  	}
   123  
   124  	err = attestationutil.VerifyIndexedAttestationSig(ctx, req, pubkeys, domain)
   125  	if err != nil {
   126  		log.WithError(err).Error("failed to verify indexed attestation signature")
   127  		return nil, status.Errorf(codes.Internal, "could not verify indexed attestation signature: %v: %v", req, err)
   128  	}
   129  
   130  	s.attestationLock.Lock()
   131  	defer s.attestationLock.Unlock()
   132  
   133  	slashings, err := s.detector.DetectAttesterSlashings(ctx, req)
   134  	if err != nil {
   135  		return nil, status.Errorf(codes.Internal, "could not detect attester slashings for attestation: %v: %v", req, err)
   136  	}
   137  	if len(slashings) < 1 {
   138  		if err := s.slasherDB.SaveIndexedAttestation(ctx, req); err != nil {
   139  			log.WithError(err).Error("Could not save indexed attestation")
   140  			return nil, status.Errorf(codes.Internal, "could not save indexed attestation: %v: %v", req, err)
   141  		}
   142  		if err := s.detector.UpdateSpans(ctx, req); err != nil {
   143  			log.WithError(err).Error("could not update spans")
   144  			return nil, status.Errorf(codes.Internal, "failed to update spans: %v: %v", req, err)
   145  		}
   146  	}
   147  	return &slashpb.AttesterSlashingResponse{
   148  		AttesterSlashing: slashings,
   149  	}, nil
   150  }
   151  
   152  // IsSlashableBlock returns an proposer slashing if the block submitted
   153  // is a double proposal.
   154  func (s *Server) IsSlashableBlock(ctx context.Context, req *ethpb.SignedBeaconBlockHeader) (*slashpb.ProposerSlashingResponse, error) {
   155  	ctx, span := trace.StartSpan(ctx, "detection.IsSlashableBlock")
   156  	defer span.End()
   157  
   158  	log.WithFields(logrus.Fields{
   159  		"slot":           req.Header.Slot,
   160  		"proposer_index": req.Header.ProposerIndex,
   161  	}).Info("Received block via RPC")
   162  	if req == nil {
   163  		return nil, status.Error(codes.InvalidArgument, "nil request provided")
   164  	}
   165  	if req.Header == nil {
   166  		return nil, status.Error(codes.InvalidArgument, "nil header provided")
   167  
   168  	}
   169  	if req.Signature == nil {
   170  		return nil, status.Error(codes.InvalidArgument, "nil signature provided")
   171  	}
   172  	gvr, err := s.beaconClient.GenesisValidatorsRoot(ctx)
   173  	if err != nil {
   174  		return nil, err
   175  	}
   176  	blockEpoch := helpers.SlotToEpoch(req.Header.Slot)
   177  	fork, err := p2putils.Fork(blockEpoch)
   178  	if err != nil {
   179  		return nil, err
   180  	}
   181  	domain, err := helpers.Domain(fork, blockEpoch, params.BeaconConfig().DomainBeaconProposer, gvr)
   182  	if err != nil {
   183  		return nil, err
   184  	}
   185  	pkMap, err := s.beaconClient.FindOrGetPublicKeys(ctx, []types.ValidatorIndex{req.Header.ProposerIndex})
   186  	if err != nil {
   187  		return nil, err
   188  	}
   189  	if err := helpers.VerifyBlockHeaderSigningRoot(
   190  		req.Header,
   191  		pkMap[req.Header.ProposerIndex],
   192  		req.Signature, domain); err != nil {
   193  		return nil, err
   194  	}
   195  
   196  	s.proposeLock.Lock()
   197  	defer s.proposeLock.Unlock()
   198  
   199  	slashing, err := s.detector.DetectDoubleProposals(ctx, req)
   200  	if err != nil {
   201  		return nil, status.Errorf(codes.Internal, "could not detect proposer slashing for block: %v: %v", req, err)
   202  	}
   203  	psr := &slashpb.ProposerSlashingResponse{}
   204  	if slashing != nil {
   205  		psr = &slashpb.ProposerSlashingResponse{
   206  			ProposerSlashing: []*ethpb.ProposerSlashing{slashing},
   207  		}
   208  	}
   209  	return psr, nil
   210  
   211  }
   212  
   213  // IsSlashableAttestationNoUpdate returns true if the attestation submitted
   214  // is a slashable vote (no db update is being done).
   215  func (s *Server) IsSlashableAttestationNoUpdate(ctx context.Context, req *ethpb.IndexedAttestation) (*slashpb.Slashable, error) {
   216  	sl := &slashpb.Slashable{}
   217  	slashings, err := s.detector.DetectAttesterSlashings(ctx, req)
   218  	if err != nil {
   219  		return sl, status.Errorf(codes.Internal, "could not detect attester slashings for attestation: %v: %v", req, err)
   220  	}
   221  	if len(slashings) < 1 {
   222  		return sl, nil
   223  	}
   224  	sl.Slashable = true
   225  	return sl, nil
   226  }
   227  
   228  // IsSlashableBlockNoUpdate returns true if the block submitted
   229  // is slashable (no db update is being done).
   230  func (s *Server) IsSlashableBlockNoUpdate(ctx context.Context, req *ethpb.BeaconBlockHeader) (*slashpb.Slashable, error) {
   231  	sl := &slashpb.Slashable{}
   232  	slash, err := s.detector.DetectDoubleProposeNoUpdate(ctx, req)
   233  	if err != nil {
   234  		return sl, status.Errorf(codes.Internal, "could not detect proposer slashing for block: %v: %v", req, err)
   235  	}
   236  	sl.Slashable = slash
   237  	return sl, nil
   238  }