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 }