2020-07-03 19:54:42 +00:00
|
|
|
|
package client
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
2021-01-22 23:12:22 +00:00
|
|
|
|
"encoding/hex"
|
2020-07-03 19:54:42 +00:00
|
|
|
|
"fmt"
|
|
|
|
|
|
2020-11-10 14:14:11 +00:00
|
|
|
|
"github.com/pkg/errors"
|
2021-07-21 21:34:07 +00:00
|
|
|
|
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
2020-07-03 19:54:42 +00:00
|
|
|
|
"github.com/prysmaticlabs/prysm/shared/featureconfig"
|
2021-01-22 23:12:22 +00:00
|
|
|
|
"github.com/prysmaticlabs/prysm/shared/slashutil"
|
2020-11-10 14:14:11 +00:00
|
|
|
|
"github.com/prysmaticlabs/prysm/validator/db/kv"
|
2020-12-09 05:47:02 +00:00
|
|
|
|
"go.opencensus.io/trace"
|
2020-07-03 19:54:42 +00:00
|
|
|
|
)
|
|
|
|
|
|
2020-11-10 14:14:11 +00:00
|
|
|
|
var failedAttLocalProtectionErr = "attempted to make slashable attestation, rejected by local slashing protection"
|
2021-01-06 17:41:00 +00:00
|
|
|
|
var failedPostAttSignExternalErr = "attempted to make slashable attestation, rejected by external slasher service"
|
2020-07-03 19:54:42 +00:00
|
|
|
|
|
2020-12-11 18:31:35 +00:00
|
|
|
|
// Checks if an attestation is slashable by comparing it with the attesting
|
|
|
|
|
// history for the given public key in our DB. If it is not, we then update the history
|
|
|
|
|
// with new values and save it to the database.
|
|
|
|
|
func (v *validator) slashableAttestationCheck(
|
|
|
|
|
ctx context.Context,
|
|
|
|
|
indexedAtt *ethpb.IndexedAttestation,
|
|
|
|
|
pubKey [48]byte,
|
|
|
|
|
signingRoot [32]byte,
|
|
|
|
|
) error {
|
2020-12-09 05:47:02 +00:00
|
|
|
|
ctx, span := trace.StartSpan(ctx, "validator.postAttSignUpdate")
|
|
|
|
|
defer span.End()
|
|
|
|
|
|
2021-01-22 23:12:22 +00:00
|
|
|
|
// Based on EIP3076, validator should refuse to sign any attestation with source epoch less
|
|
|
|
|
// than the minimum source epoch present in that signer’s attestations.
|
|
|
|
|
lowestSourceEpoch, exists, err := v.db.LowestSignedSourceEpoch(ctx, pubKey)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if exists && indexedAtt.Data.Source.Epoch < lowestSourceEpoch {
|
|
|
|
|
return fmt.Errorf(
|
|
|
|
|
"could not sign attestation lower than lowest source epoch in db, %d < %d",
|
|
|
|
|
indexedAtt.Data.Source.Epoch,
|
|
|
|
|
lowestSourceEpoch,
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
existingSigningRoot, err := v.db.SigningRootAtTargetEpoch(ctx, pubKey, indexedAtt.Data.Target.Epoch)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
signingRootsDiffer := slashutil.SigningRootsDiffer(existingSigningRoot, signingRoot)
|
|
|
|
|
|
|
|
|
|
// Based on EIP3076, validator should refuse to sign any attestation with target epoch less
|
|
|
|
|
// than or equal to the minimum target epoch present in that signer’s attestations.
|
|
|
|
|
lowestTargetEpoch, exists, err := v.db.LowestSignedTargetEpoch(ctx, pubKey)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if signingRootsDiffer && exists && indexedAtt.Data.Target.Epoch <= lowestTargetEpoch {
|
|
|
|
|
return fmt.Errorf(
|
|
|
|
|
"could not sign attestation lower than or equal to lowest target epoch in db, %d <= %d",
|
|
|
|
|
indexedAtt.Data.Target.Epoch,
|
|
|
|
|
lowestTargetEpoch,
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
fmtKey := "0x" + hex.EncodeToString(pubKey[:])
|
2021-01-11 23:59:17 +00:00
|
|
|
|
slashingKind, err := v.db.CheckSlashableAttestation(ctx, pubKey, signingRoot, indexedAtt)
|
2020-12-11 18:31:35 +00:00
|
|
|
|
if err != nil {
|
2020-11-24 17:24:23 +00:00
|
|
|
|
if v.emitAccountMetrics {
|
|
|
|
|
ValidatorAttestFailVec.WithLabelValues(fmtKey).Inc()
|
|
|
|
|
}
|
2021-01-11 23:59:17 +00:00
|
|
|
|
switch slashingKind {
|
|
|
|
|
case kv.DoubleVote:
|
|
|
|
|
log.Warn("Attestation is slashable as it is a double vote")
|
|
|
|
|
case kv.SurroundingVote:
|
|
|
|
|
log.Warn("Attestation is slashable as it is surrounding a previous attestation")
|
|
|
|
|
case kv.SurroundedVote:
|
|
|
|
|
log.Warn("Attestation is slashable as it is surrounded by a previous attestation")
|
|
|
|
|
}
|
|
|
|
|
return errors.Wrap(err, failedAttLocalProtectionErr)
|
2021-01-09 04:00:56 +00:00
|
|
|
|
}
|
2021-01-11 23:59:17 +00:00
|
|
|
|
|
|
|
|
|
if err := v.db.SaveAttestationForPubKey(ctx, pubKey, signingRoot, indexedAtt); err != nil {
|
|
|
|
|
return errors.Wrap(err, "could not save attestation history for validator public key")
|
2020-12-11 18:31:35 +00:00
|
|
|
|
}
|
2020-07-03 19:54:42 +00:00
|
|
|
|
|
|
|
|
|
if featureconfig.Get().SlasherProtection && v.protector != nil {
|
|
|
|
|
if !v.protector.CommitAttestation(ctx, indexedAtt) {
|
|
|
|
|
if v.emitAccountMetrics {
|
|
|
|
|
ValidatorAttestFailVecSlasher.WithLabelValues(fmtKey).Inc()
|
|
|
|
|
}
|
|
|
|
|
return errors.New(failedPostAttSignExternalErr)
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-12-11 18:31:35 +00:00
|
|
|
|
return nil
|
2020-07-03 19:54:42 +00:00
|
|
|
|
}
|