prysm-pulse/slasher/rpc/server.go
shayzluf 4330839bc1
Add surround check to endpoint (#4065)
* first version of the watchtower api

* service files

* Begin work on grpc server

* More changes to server

* REnames and mock setup

* working test

* merge

* double propose detection test

* nishant review

* todo change

* gaz

* fix service

* gaz

* remove unused import

* gaz

* resolve circular dependency

* resolve circular dependency 2nd try

* remove package

* fix package

* fix test

* added tests

* gaz

* remove status check

* gaz

* remove context

* remove context

* change var name

* moved to rpc dir

* gaz

* remove server code

* gaz

* slasher server

* visibility change

* pb

* service update

* gaz

* slasher grpc server

* making it work

* setup db and start

* gaz

* service flags fixes

* grpc service running

* go imports

* remove new initializer

* gaz

* remove feature flags

* change back SetupSlasherDB

* fix SetupSlasherDB calls

* define err

* fix bad merge

* fix test

* fix imports

* fix imports

* fix imports

* add cancel

* comment stop

* fix cancel issue

* remove unneeded code

* bring back bad merge that removed TODO

* remove use of epoch as am input

* fixed slasher to be runable again

* wait for channel close

* gaz

* small test

* flags fix

* fix flag order

* double vote detection

* remove source epoch from indexed attestation indices

* change server method to receive indexed attestation

* start implementation

* double vote detection

* proto

* pb

* fir comment

* add surround detection and retrieval to endpoint

* nishant review

* import fix

* fix miss order

* fix detection 0 case
added tests

* terence review
2019-11-21 12:41:23 +05:30

138 lines
4.9 KiB
Go

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 := &ethpb.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...)
}
}
for _, idx := range indices {
atts, err := ss.DetectSurroundVotes(ctx, req.Data.Source.Epoch, req.Data.Target.Epoch, idx)
if err != nil {
return nil, err
}
for _, ia := range atts {
atsSlashinngRes.AttesterSlashing = append(atsSlashinngRes.AttesterSlashing, &ethpb.AttesterSlashing{
Attestation_1: req,
Attestation_2: ia,
})
}
}
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 := &ethpb.ProposerSlashingResponse{}
presentInDb := false
for _, bh := range blockHeaders {
if proto.Equal(bh, psr.BlockHeader) {
presentInDb = true
continue
}
pSlashingsResponse.ProposerSlashing = append(pSlashingsResponse.ProposerSlashing, &ethpb.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")
}
// DetectSurroundVotes is a method used to return the attestation that were detected
// by min max surround detection method.
func (ss *Server) DetectSurroundVotes(ctx context.Context, source uint64, target uint64, validatorIdx uint64) ([]*ethpb.IndexedAttestation, error) {
minTargetEpoch, err := ss.DetectAndUpdateMinEpochSpan(ctx, source, target, validatorIdx)
if err != nil {
return nil, err
}
maxTargetEpoch, err := ss.DetectAndUpdateMaxEpochSpan(ctx, source, target, validatorIdx)
if err != nil {
return nil, err
}
var idxAtts []*ethpb.IndexedAttestation
if minTargetEpoch > 0 {
attestations, err := ss.SlasherDB.IndexedAttestation(minTargetEpoch, validatorIdx)
if err != nil {
return nil, err
}
for _, ia := range attestations {
if ia.Data.Source.Epoch > source && ia.Data.Target.Epoch < target {
idxAtts = append(idxAtts, ia)
}
}
}
if maxTargetEpoch > 0 {
attestations, err := ss.SlasherDB.IndexedAttestation(maxTargetEpoch, validatorIdx)
if err != nil {
return nil, err
}
for _, ia := range attestations {
if ia.Data.Source.Epoch < source && ia.Data.Target.Epoch > target {
idxAtts = append(idxAtts, ia)
}
}
}
return idxAtts, nil
}