prysm-pulse/slasher/detection/attestations/spanner.go
Raul Jordan 6158a648cd
Plugging in spanner db (#5009)
* rem slasher proto

* with cache

* delete old code

* moving to bytes.go fix traces

* moving to bytes.go fix traces

* raul feedback

* raul feedback

* begin

* add eviction test

* ivan feedback

* ivan feedback

* test is running

* some comment improvements

* test included for bytes and bool

* import

* cleanup

* tests pass

* fill in all fields in test

* gaz

* fix integration

* gaz + goimports

* fix service.go

* remove sleep

* cleanup

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2020-03-05 12:11:54 -06:00

210 lines
6.3 KiB
Go

package attestations
import (
"context"
"fmt"
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
"github.com/prysmaticlabs/prysm/shared/params"
db "github.com/prysmaticlabs/prysm/slasher/db"
"github.com/prysmaticlabs/prysm/slasher/detection/attestations/iface"
"github.com/prysmaticlabs/prysm/slasher/detection/attestations/types"
"go.opencensus.io/trace"
)
var _ = iface.SpanDetector(&SpanDetector{})
// SpanDetector defines a struct which can detect slashable
// attestation offenses by tracking validator min-max
// spans from validators and attestation data roots.
type SpanDetector struct {
slasherDB db.Database
}
// NewSpanDetector creates a new instance of a struct tracking
// several epochs of min-max spans for each validator in
// the beacon state.
func NewSpanDetector(db db.Database) *SpanDetector {
return &SpanDetector{
slasherDB: db,
}
}
// DetectSlashingForValidator uses a validator index and its corresponding
// min-max spans during an epoch to detect an epoch in which the validator
// committed a slashable attestation.
func (s *SpanDetector) DetectSlashingForValidator(
ctx context.Context,
validatorIdx uint64,
attData *ethpb.AttestationData,
) (*types.DetectionResult, error) {
ctx, traceSpan := trace.StartSpan(ctx, "detection.DetectSlashingForValidator")
defer traceSpan.End()
sourceEpoch := attData.Source.Epoch
targetEpoch := attData.Target.Epoch
if (targetEpoch - sourceEpoch) > params.BeaconConfig().WeakSubjectivityPeriod {
return nil, fmt.Errorf(
"attestation span was greater than weak subjectivity period %d, received: %d",
params.BeaconConfig().WeakSubjectivityPeriod,
targetEpoch-sourceEpoch,
)
}
distance := uint16(targetEpoch - sourceEpoch)
sp, err := s.slasherDB.EpochSpanByValidatorIndex(ctx, validatorIdx, sourceEpoch)
if err != nil {
return nil, err
}
minSpan := sp.MinSpan
if minSpan > 0 && minSpan < distance {
slashableEpoch := sourceEpoch + uint64(minSpan)
span, err := s.slasherDB.EpochSpanByValidatorIndex(ctx, validatorIdx, slashableEpoch)
if err != nil {
return nil, err
}
return &types.DetectionResult{
Kind: types.SurroundVote,
SlashableEpoch: sourceEpoch + uint64(minSpan),
SigBytes: span.SigBytes,
}, nil
}
maxSpan := sp.MaxSpan
if maxSpan > distance {
slashableEpoch := sourceEpoch + uint64(maxSpan)
span, err := s.slasherDB.EpochSpanByValidatorIndex(ctx, validatorIdx, slashableEpoch)
if err != nil {
return nil, err
}
return &types.DetectionResult{
Kind: types.SurroundVote,
SlashableEpoch: slashableEpoch,
SigBytes: span.SigBytes,
}, nil
}
sp, err = s.slasherDB.EpochSpanByValidatorIndex(ctx, validatorIdx, targetEpoch)
if err != nil {
return nil, err
}
// Check if the validator has attested for this epoch or not.
if sp.HasAttested {
return &types.DetectionResult{
Kind: types.DoubleVote,
SlashableEpoch: targetEpoch,
SigBytes: sp.SigBytes,
}, nil
}
return nil, nil
}
// UpdateSpans given an indexed attestation for all of its attesting indices.
func (s *SpanDetector) UpdateSpans(ctx context.Context, att *ethpb.IndexedAttestation) error {
ctx, span := trace.StartSpan(ctx, "detection.UpdateSpans")
defer span.End()
source := att.Data.Source.Epoch
target := att.Data.Target.Epoch
for i := 0; i < len(att.AttestingIndices); i++ {
valIdx := att.AttestingIndices[i]
// Save the signature for the received attestation so we can have more detail to find it in the DB.
err := s.saveSigBytes(ctx, att, valIdx)
if err != nil {
return err
}
// Update min and max spans.
err = s.updateMinSpan(ctx, source, target, valIdx)
if err != nil {
return err
}
err = s.updateMaxSpan(ctx, source, target, valIdx)
if err != nil {
return err
}
}
return nil
}
// saveSigBytes saves the first 2 bytes of the signature for the att we're updating the spans to.
// Later used to help us find the violating attestation in the DB.
func (s *SpanDetector) saveSigBytes(ctx context.Context, att *ethpb.IndexedAttestation, valIdx uint64) error {
target := att.Data.Target.Epoch
sp, err := s.slasherDB.EpochSpanByValidatorIndex(ctx, valIdx, target)
if err != nil {
return err
}
// If the validator has already attested for this target epoch,
// then we do not need to update the values of the span sig bytes.
if sp.HasAttested {
return nil
}
sigBytes := [2]byte{0, 0}
if len(att.Signature) > 1 {
sigBytes = [2]byte{att.Signature[0], att.Signature[1]}
}
// Save the signature bytes into the span for this epoch.
sp.HasAttested = true
sp.SigBytes = sigBytes
return s.slasherDB.SaveValidatorEpochSpans(ctx, valIdx, target, sp)
}
// Updates a min span for a validator index given a source and target epoch
// for an attestation produced by the validator. Used for catching surrounding votes.
func (s *SpanDetector) updateMinSpan(ctx context.Context, source uint64, target uint64, valIdx uint64) error {
if source < 1 {
return nil
}
for epochInt := int64(source - 1); epochInt >= 0; epochInt-- {
epoch := uint64(epochInt)
span, err := s.slasherDB.EpochSpanByValidatorIndex(ctx, valIdx, epoch)
if err != nil {
return err
}
newMinSpan := uint16(target - epoch)
if span.MinSpan == 0 || span.MinSpan > newMinSpan {
span = types.Span{
MinSpan: newMinSpan,
MaxSpan: span.MaxSpan,
SigBytes: span.SigBytes,
HasAttested: span.HasAttested,
}
err := s.slasherDB.SaveValidatorEpochSpans(ctx, valIdx, epoch, span)
if err != nil {
return err
}
} else {
break
}
}
return nil
}
// Updates a max span for a validator index given a source and target epoch
// for an attestation produced by the validator. Used for catching surrounded votes.
func (s *SpanDetector) updateMaxSpan(ctx context.Context, source uint64, target uint64, valIdx uint64) error {
for epoch := source + 1; epoch < target; epoch++ {
span, err := s.slasherDB.EpochSpanByValidatorIndex(ctx, valIdx, epoch)
if err != nil {
return err
}
newMaxSpan := uint16(target - epoch)
if newMaxSpan > span.MaxSpan {
span = types.Span{
MinSpan: span.MinSpan,
MaxSpan: newMaxSpan,
SigBytes: span.SigBytes,
HasAttested: span.HasAttested,
}
err := s.slasherDB.SaveValidatorEpochSpans(ctx, valIdx, epoch, span)
if err != nil {
return err
}
} else {
break
}
}
return nil
}