prysm-pulse/slasher/detection/detect.go
Raul Jordan 14b3181e67
Plug-In Attester Slashing Detection Into Slasher Runtime (#4937)
* more spanner additions

* implement iface

* begin implement

* wrapped up spanner functions

* rem interface

* added in necessary comments

* comments on enums

* begin adding tests

* plug in surround vote detection

* saved indexed db implementation

* finally plugin slashing for historical data

* Small fixes

* add in all gazelle

* save incoming new functions

* resolve todo

* fix broken test channel item

* tests passing when fixing certain arguments and setups

* Add comment and change unimplemented

* find surround

* added in gazelle

* gazz

* feedback from shay

* fixed up naming

* Update

* Add tests for detectSurroundVotes

* Remove logs

* Fix slasher test

* formatting

* Remove unneeded condition

* Test indices better

* fixing broken build

* pass tests

* skip tests

* imports

* Update slasher/detection/attestations/attestations_test.go

* Update slasher/beaconclient/historical_data_retrieval_test.go

* Address comments

* Rename function

* Add comment for future optimization

* Fix comment

Co-authored-by: Ivan Martinez <ivanthegreatdev@gmail.com>
2020-02-27 12:22:39 -05:00

103 lines
3.1 KiB
Go

package detection
import (
"context"
"github.com/pkg/errors"
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
"github.com/prysmaticlabs/prysm/shared/sliceutil"
"github.com/prysmaticlabs/prysm/slasher/detection/attestations/types"
)
func (ds *Service) detectAttesterSlashings(
ctx context.Context,
att *ethpb.IndexedAttestation,
) ([]*ethpb.AttesterSlashing, error) {
slashings := make([]*ethpb.AttesterSlashing, 0)
for i := 0; i < len(att.AttestingIndices); i++ {
valIdx := att.AttestingIndices[i]
surroundedAttSlashings, err := ds.detectSurroundVotes(ctx, valIdx, att)
if err != nil {
return nil, errors.Wrap(err, "could not detect surround votes on attestation")
}
doubleAttSlashings, err := ds.detectDoubleVotes(ctx, att)
if err != nil {
return nil, errors.Wrap(err, "could not detect double votes on attestation")
}
if len(surroundedAttSlashings) > 0 {
log.Infof("Found %d slashings for val idx %d", len(surroundedAttSlashings), valIdx)
}
newSlashings := append(surroundedAttSlashings, doubleAttSlashings...)
slashings = append(slashings, newSlashings...)
}
return slashings, nil
}
// detectDoubleVote --
// TODO(#4589): Implement.
func (ds *Service) detectDoubleVotes(
ctx context.Context,
att *ethpb.IndexedAttestation,
) ([]*ethpb.AttesterSlashing, error) {
return nil, nil
}
// detectSurroundVotes cross references the passed in attestation with the requested validator's
// voting history in order to detect any possible surround votes.
func (ds *Service) detectSurroundVotes(
ctx context.Context,
validatorIdx uint64,
incomingAtt *ethpb.IndexedAttestation,
) ([]*ethpb.AttesterSlashing, error) {
res, err := ds.minMaxSpanDetector.DetectSlashingForValidator(
ctx,
validatorIdx,
incomingAtt.Data,
)
if err != nil {
return nil, err
}
if res == nil {
return nil, nil
}
if res.Kind != types.SurroundVote {
return nil, nil
}
var slashings []*ethpb.AttesterSlashing
otherAtts, err := ds.slasherDB.IndexedAttestationsForEpoch(ctx, res.SlashableEpoch)
if err != nil {
return nil, err
}
for _, att := range otherAtts {
if att.Data == nil {
continue
}
// If there are no shared indices, there is no validator to slash.
if len(sliceutil.IntersectionUint64(att.AttestingIndices, incomingAtt.AttestingIndices)) == 0 {
continue
}
if isSurrounding(incomingAtt, att) || isSurrounded(incomingAtt, att) {
slashings = append(slashings, &ethpb.AttesterSlashing{
Attestation_1: att,
Attestation_2: incomingAtt,
})
}
}
if len(slashings) == 0 {
return nil, errors.New("unexpected false positive in surround vote detection")
}
return slashings, nil
}
func isSurrounding(incomingAtt *ethpb.IndexedAttestation, prevAtt *ethpb.IndexedAttestation) bool {
return incomingAtt.Data.Source.Epoch < prevAtt.Data.Source.Epoch &&
incomingAtt.Data.Target.Epoch > prevAtt.Data.Target.Epoch
}
func isSurrounded(incomingAtt *ethpb.IndexedAttestation, prevAtt *ethpb.IndexedAttestation) bool {
return incomingAtt.Data.Source.Epoch > prevAtt.Data.Source.Epoch &&
incomingAtt.Data.Target.Epoch < prevAtt.Data.Target.Epoch
}