From 7325d9fc966514963d81337d8974d72c98c1fe6d Mon Sep 17 00:00:00 2001 From: rkapka Date: Fri, 3 Jan 2025 15:06:14 +0100 Subject: [PATCH] EIP-7549: slasher --- beacon-chain/slasher/BUILD.bazel | 1 + beacon-chain/slasher/chunks.go | 97 +++++++++++ beacon-chain/slasher/detect_attestations.go | 183 +++++++++++++++----- validator/client/attest.go | 20 +-- 4 files changed, 245 insertions(+), 56 deletions(-) diff --git a/beacon-chain/slasher/BUILD.bazel b/beacon-chain/slasher/BUILD.bazel index b0f0391ac116..dbb6fddb1b6c 100644 --- a/beacon-chain/slasher/BUILD.bazel +++ b/beacon-chain/slasher/BUILD.bazel @@ -41,6 +41,7 @@ go_library( "//encoding/bytesutil:go_default_library", "//monitoring/tracing/trace:go_default_library", "//proto/prysm/v1alpha1:go_default_library", + "//runtime/version:go_default_library", "//time/slots:go_default_library", "@com_github_pkg_errors//:go_default_library", "@com_github_prometheus_client_golang//prometheus:go_default_library", diff --git a/beacon-chain/slasher/chunks.go b/beacon-chain/slasher/chunks.go index 01b5943c66b5..2f89d2044fae 100644 --- a/beacon-chain/slasher/chunks.go +++ b/beacon-chain/slasher/chunks.go @@ -11,6 +11,7 @@ import ( slashertypes "github.com/prysmaticlabs/prysm/v5/beacon-chain/slasher/types" "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" + "github.com/prysmaticlabs/prysm/v5/runtime/version" "github.com/sirupsen/logrus" ) @@ -232,6 +233,54 @@ func (m *MinSpanChunksSlice) CheckSlashable( surroundingVotesTotal.Inc() + // Both attestations should have the same type + if existingAttWrapper.IndexedAttestation.Version() >= version.Electra && incomingAttWrapper.IndexedAttestation.Version() < version.Electra { + incomingAttWrapper = &slashertypes.IndexedAttestationWrapper{ + IndexedAttestation: ðpb.IndexedAttestationElectra{ + AttestingIndices: incomingAttWrapper.IndexedAttestation.GetAttestingIndices(), + Data: incomingAttWrapper.IndexedAttestation.GetData(), + Signature: incomingAttWrapper.IndexedAttestation.GetSignature(), + }, + DataRoot: incomingAttWrapper.DataRoot, + } + } + if incomingAttWrapper.IndexedAttestation.Version() >= version.Electra && existingAttWrapper.IndexedAttestation.Version() < version.Electra { + existingAttWrapper = &slashertypes.IndexedAttestationWrapper{ + IndexedAttestation: ðpb.IndexedAttestationElectra{ + AttestingIndices: existingAttWrapper.IndexedAttestation.GetAttestingIndices(), + Data: existingAttWrapper.IndexedAttestation.GetData(), + Signature: existingAttWrapper.IndexedAttestation.GetSignature(), + }, + DataRoot: existingAttWrapper.DataRoot, + } + } + + postElectra := incomingAttWrapper.IndexedAttestation.Version() >= version.Electra + if postElectra { + existing, ok := existingAttWrapper.IndexedAttestation.(*ethpb.IndexedAttestationElectra) + if !ok { + return nil, fmt.Errorf("wrong existing attestation type (expected %T, got %T)", ðpb.IndexedAttestationElectra{}, existing) + } + incoming, ok := incomingAttWrapper.IndexedAttestation.(*ethpb.IndexedAttestationElectra) + if !ok { + return nil, fmt.Errorf("wrong incoming attestation type (expected %T, got %T)", ðpb.IndexedAttestationElectra{}, incoming) + } + slashing := ðpb.AttesterSlashingElectra{ + Attestation_1: existing, + Attestation_2: incoming, + } + + // Ensure the attestation with the lower data root is the first attestation. + if bytes.Compare(existingAttWrapper.DataRoot[:], incomingAttWrapper.DataRoot[:]) > 0 { + slashing = ðpb.AttesterSlashingElectra{ + Attestation_1: incoming, + Attestation_2: existing, + } + } + + return slashing, nil + } + existing, ok := existingAttWrapper.IndexedAttestation.(*ethpb.IndexedAttestation) if !ok { return nil, fmt.Errorf( @@ -328,6 +377,54 @@ func (m *MaxSpanChunksSlice) CheckSlashable( surroundedVotesTotal.Inc() + // Both attestations should have the same type + if existingAttWrapper.IndexedAttestation.Version() >= version.Electra && incomingAttWrapper.IndexedAttestation.Version() < version.Electra { + incomingAttWrapper = &slashertypes.IndexedAttestationWrapper{ + IndexedAttestation: ðpb.IndexedAttestationElectra{ + AttestingIndices: incomingAttWrapper.IndexedAttestation.GetAttestingIndices(), + Data: incomingAttWrapper.IndexedAttestation.GetData(), + Signature: incomingAttWrapper.IndexedAttestation.GetSignature(), + }, + DataRoot: incomingAttWrapper.DataRoot, + } + } + if incomingAttWrapper.IndexedAttestation.Version() >= version.Electra && existingAttWrapper.IndexedAttestation.Version() < version.Electra { + existingAttWrapper = &slashertypes.IndexedAttestationWrapper{ + IndexedAttestation: ðpb.IndexedAttestationElectra{ + AttestingIndices: existingAttWrapper.IndexedAttestation.GetAttestingIndices(), + Data: existingAttWrapper.IndexedAttestation.GetData(), + Signature: existingAttWrapper.IndexedAttestation.GetSignature(), + }, + DataRoot: existingAttWrapper.DataRoot, + } + } + + postElectra := incomingAttWrapper.IndexedAttestation.Version() >= version.Electra + if postElectra { + existing, ok := existingAttWrapper.IndexedAttestation.(*ethpb.IndexedAttestationElectra) + if !ok { + return nil, fmt.Errorf("wrong existing attestation type (expected %T, got %T)", ðpb.IndexedAttestationElectra{}, existing) + } + incoming, ok := incomingAttWrapper.IndexedAttestation.(*ethpb.IndexedAttestationElectra) + if !ok { + return nil, fmt.Errorf("wrong incoming attestation type (expected %T, got %T)", ðpb.IndexedAttestationElectra{}, incoming) + } + slashing := ðpb.AttesterSlashingElectra{ + Attestation_1: existing, + Attestation_2: incoming, + } + + // Ensure the attestation with the lower data root is the first attestation. + if bytes.Compare(existingAttWrapper.DataRoot[:], incomingAttWrapper.DataRoot[:]) > 0 { + slashing = ðpb.AttesterSlashingElectra{ + Attestation_1: incoming, + Attestation_2: existing, + } + } + + return slashing, nil + } + existing, ok := existingAttWrapper.IndexedAttestation.(*ethpb.IndexedAttestation) if !ok { return nil, fmt.Errorf( diff --git a/beacon-chain/slasher/detect_attestations.go b/beacon-chain/slasher/detect_attestations.go index bbfc1238b20d..00290e0897f4 100644 --- a/beacon-chain/slasher/detect_attestations.go +++ b/beacon-chain/slasher/detect_attestations.go @@ -11,6 +11,7 @@ import ( "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" "github.com/prysmaticlabs/prysm/v5/monitoring/tracing/trace" ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" + "github.com/prysmaticlabs/prysm/v5/runtime/version" "golang.org/x/exp/maps" ) @@ -193,33 +194,80 @@ func (s *Service) checkDoubleVotes( // This is a double vote. doubleVotesTotal.Inc() - existing, ok := existingAttWrapper.IndexedAttestation.(*ethpb.IndexedAttestation) - if !ok { - return nil, fmt.Errorf( - "existing attestation has wrong type (expected %T, got %T)", - ðpb.IndexedAttestation{}, - existingAttWrapper.IndexedAttestation, - ) + var slashing ethpb.AttSlashing + + // Both attestations should have the same type + if existingAttWrapper.IndexedAttestation.Version() >= version.Electra && incomingAttWrapper.IndexedAttestation.Version() < version.Electra { + incomingAttWrapper = &slashertypes.IndexedAttestationWrapper{ + IndexedAttestation: ðpb.IndexedAttestationElectra{ + AttestingIndices: incomingAttWrapper.IndexedAttestation.GetAttestingIndices(), + Data: incomingAttWrapper.IndexedAttestation.GetData(), + Signature: incomingAttWrapper.IndexedAttestation.GetSignature(), + }, + DataRoot: incomingAttWrapper.DataRoot, + } } - incoming, ok := incomingAttWrapper.IndexedAttestation.(*ethpb.IndexedAttestation) - if !ok { - return nil, fmt.Errorf( - "incoming attestation has wrong type (expected %T, got %T)", - ðpb.IndexedAttestation{}, - incomingAttWrapper.IndexedAttestation, - ) + if incomingAttWrapper.IndexedAttestation.Version() >= version.Electra && existingAttWrapper.IndexedAttestation.Version() < version.Electra { + existingAttWrapper = &slashertypes.IndexedAttestationWrapper{ + IndexedAttestation: ðpb.IndexedAttestationElectra{ + AttestingIndices: existingAttWrapper.IndexedAttestation.GetAttestingIndices(), + Data: existingAttWrapper.IndexedAttestation.GetData(), + Signature: existingAttWrapper.IndexedAttestation.GetSignature(), + }, + DataRoot: existingAttWrapper.DataRoot, + } } - slashing := ðpb.AttesterSlashing{ - Attestation_1: existing, - Attestation_2: incoming, - } + postElectra := incomingAttWrapper.IndexedAttestation.Version() >= version.Electra + if postElectra { + existing, ok := existingAttWrapper.IndexedAttestation.(*ethpb.IndexedAttestationElectra) + if !ok { + return nil, fmt.Errorf("wrong existing attestation type (expected %T, got %T)", ðpb.IndexedAttestationElectra{}, existing) + } + incoming, ok := incomingAttWrapper.IndexedAttestation.(*ethpb.IndexedAttestationElectra) + if !ok { + return nil, fmt.Errorf("wrong incoming attestation type (expected %T, got %T)", ðpb.IndexedAttestationElectra{}, incoming) + } + slashing = ðpb.AttesterSlashingElectra{ + Attestation_1: existing, + Attestation_2: incoming, + } - // Ensure the attestation with the lower data root is the first attestation. - if bytes.Compare(existingAttWrapper.DataRoot[:], incomingAttWrapper.DataRoot[:]) > 0 { + // Ensure the attestation with the lower data root is the first attestation. + if bytes.Compare(existingAttWrapper.DataRoot[:], incomingAttWrapper.DataRoot[:]) > 0 { + slashing = ðpb.AttesterSlashingElectra{ + Attestation_1: incoming, + Attestation_2: existing, + } + } + } else { + existing, ok := existingAttWrapper.IndexedAttestation.(*ethpb.IndexedAttestation) + if !ok { + return nil, fmt.Errorf( + "existing attestation has wrong type (expected %T, got %T)", + ðpb.IndexedAttestation{}, + existingAttWrapper.IndexedAttestation, + ) + } + incoming, ok := incomingAttWrapper.IndexedAttestation.(*ethpb.IndexedAttestation) + if !ok { + return nil, fmt.Errorf( + "incoming attestation has wrong type (expected %T, got %T)", + ðpb.IndexedAttestation{}, + incomingAttWrapper.IndexedAttestation, + ) + } slashing = ðpb.AttesterSlashing{ - Attestation_1: incoming, - Attestation_2: existing, + Attestation_1: existing, + Attestation_2: incoming, + } + + // Ensure the attestation with the lower data root is the first attestation. + if bytes.Compare(existingAttWrapper.DataRoot[:], incomingAttWrapper.DataRoot[:]) > 0 { + slashing = ðpb.AttesterSlashing{ + Attestation_1: incoming, + Attestation_2: existing, + } } } @@ -245,33 +293,80 @@ func (s *Service) checkDoubleVotes( wrapper_1 := doubleVote.Wrapper_1 wrapper_2 := doubleVote.Wrapper_2 - att_1, ok := wrapper_1.IndexedAttestation.(*ethpb.IndexedAttestation) - if !ok { - return nil, fmt.Errorf( - "first attestation has wrong type (expected %T, got %T)", - ðpb.IndexedAttestation{}, - wrapper_1.IndexedAttestation, - ) + var slashing ethpb.AttSlashing + + // Both attestations should have the same type + if wrapper_1.IndexedAttestation.Version() >= version.Electra && wrapper_2.IndexedAttestation.Version() < version.Electra { + wrapper_2 = &slashertypes.IndexedAttestationWrapper{ + IndexedAttestation: ðpb.IndexedAttestationElectra{ + AttestingIndices: wrapper_2.IndexedAttestation.GetAttestingIndices(), + Data: wrapper_2.IndexedAttestation.GetData(), + Signature: wrapper_2.IndexedAttestation.GetSignature(), + }, + DataRoot: wrapper_2.DataRoot, + } } - att_2, ok := wrapper_2.IndexedAttestation.(*ethpb.IndexedAttestation) - if !ok { - return nil, fmt.Errorf( - "second attestation has wrong type (expected %T, got %T)", - ðpb.IndexedAttestation{}, - wrapper_2.IndexedAttestation, - ) + if wrapper_2.IndexedAttestation.Version() >= version.Electra && wrapper_1.IndexedAttestation.Version() < version.Electra { + wrapper_1 = &slashertypes.IndexedAttestationWrapper{ + IndexedAttestation: ðpb.IndexedAttestationElectra{ + AttestingIndices: wrapper_1.IndexedAttestation.GetAttestingIndices(), + Data: wrapper_1.IndexedAttestation.GetData(), + Signature: wrapper_1.IndexedAttestation.GetSignature(), + }, + DataRoot: wrapper_1.DataRoot, + } } - slashing := ðpb.AttesterSlashing{ - Attestation_1: att_1, - Attestation_2: att_2, - } + postElectra := wrapper_2.IndexedAttestation.Version() >= version.Electra + if postElectra { + existing, ok := wrapper_1.IndexedAttestation.(*ethpb.IndexedAttestationElectra) + if !ok { + return nil, fmt.Errorf("wrong existing attestation type (expected %T, got %T)", ðpb.IndexedAttestationElectra{}, existing) + } + incoming, ok := wrapper_2.IndexedAttestation.(*ethpb.IndexedAttestationElectra) + if !ok { + return nil, fmt.Errorf("wrong incoming attestation type (expected %T, got %T)", ðpb.IndexedAttestationElectra{}, incoming) + } + slashing = ðpb.AttesterSlashingElectra{ + Attestation_1: existing, + Attestation_2: incoming, + } - // Ensure the attestation with the lower data root is the first attestation. - if bytes.Compare(wrapper_1.DataRoot[:], wrapper_2.DataRoot[:]) > 0 { + // Ensure the attestation with the lower data root is the first attestation. + if bytes.Compare(wrapper_1.DataRoot[:], wrapper_2.DataRoot[:]) > 0 { + slashing = ðpb.AttesterSlashingElectra{ + Attestation_1: incoming, + Attestation_2: existing, + } + } + } else { + att_1, ok := wrapper_1.IndexedAttestation.(*ethpb.IndexedAttestation) + if !ok { + return nil, fmt.Errorf( + "first attestation has wrong type (expected %T, got %T)", + ðpb.IndexedAttestation{}, + wrapper_1.IndexedAttestation, + ) + } + att_2, ok := wrapper_2.IndexedAttestation.(*ethpb.IndexedAttestation) + if !ok { + return nil, fmt.Errorf( + "second attestation has wrong type (expected %T, got %T)", + ðpb.IndexedAttestation{}, + wrapper_2.IndexedAttestation, + ) + } slashing = ðpb.AttesterSlashing{ - Attestation_1: att_2, - Attestation_2: att_1, + Attestation_1: att_1, + Attestation_2: att_2, + } + + // Ensure the attestation with the lower data root is the first attestation. + if bytes.Compare(wrapper_1.DataRoot[:], wrapper_2.DataRoot[:]) > 0 { + slashing = ðpb.AttesterSlashing{ + Attestation_1: att_2, + Attestation_2: att_1, + } } } diff --git a/validator/client/attest.go b/validator/client/attest.go index 0c2f92a83cc6..a7c11411037e 100644 --- a/validator/client/attest.go +++ b/validator/client/attest.go @@ -139,18 +139,14 @@ func (v *validator) SubmitAttestation(ctx context.Context, slot primitives.Slot, return } - // TODO: Extend to Electra - phase0Att, ok := indexedAtt.(*ethpb.IndexedAttestation) - if ok { - // Send the attestation to the beacon node. - if err := v.db.SlashableAttestationCheck(ctx, phase0Att, pubKey, signingRoot, v.emitAccountMetrics, ValidatorAttestFailVec); err != nil { - log.WithError(err).Error("Failed attestation slashing protection check") - log.WithFields( - attestationLogFields(pubKey, indexedAtt), - ).Debug("Attempted slashable attestation details") - tracing.AnnotateError(span, err) - return - } + // Send the attestation to the beacon node. + if err := v.db.SlashableAttestationCheck(ctx, indexedAtt, pubKey, signingRoot, v.emitAccountMetrics, ValidatorAttestFailVec); err != nil { + log.WithError(err).Error("Failed attestation slashing protection check") + log.WithFields( + attestationLogFields(pubKey, indexedAtt), + ).Debug("Attempted slashable attestation details") + tracing.AnnotateError(span, err) + return } aggregationBitfield := bitfield.NewBitlist(uint64(len(duty.Committee)))