mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 13:28:01 -05:00
Update slasher service to Electra (#14812)
* Update slasher service to Electra * Update beacon-chain/slasher/chunks.go Co-authored-by: Manu NALEPA <enalepa@offchainlabs.com> * Update beacon-chain/slasher/chunks_test.go Co-authored-by: Manu NALEPA <enalepa@offchainlabs.com> * Manu's review * Manu's review again --------- Co-authored-by: Manu NALEPA <enalepa@offchainlabs.com>
This commit is contained in:
@@ -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",
|
||||
@@ -83,6 +84,7 @@ go_test(
|
||||
"//crypto/bls/common:go_default_library",
|
||||
"//encoding/bytesutil:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//runtime/version:go_default_library",
|
||||
"//testing/assert:go_default_library",
|
||||
"//testing/require:go_default_library",
|
||||
"//testing/util:go_default_library",
|
||||
|
||||
@@ -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,43 @@ func (m *MinSpanChunksSlice) CheckSlashable(
|
||||
|
||||
surroundingVotesTotal.Inc()
|
||||
|
||||
// Both attestations should have the same type. If not, we convert both to Electra attestations.
|
||||
unifyAttWrapperVersion(existingAttWrapper, incomingAttWrapper)
|
||||
|
||||
postElectra := existingAttWrapper.IndexedAttestation.Version() >= version.Electra
|
||||
if postElectra {
|
||||
existing, ok := existingAttWrapper.IndexedAttestation.(*ethpb.IndexedAttestationElectra)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf(
|
||||
"existing attestation has wrong type (expected %T, got %T)",
|
||||
ðpb.IndexedAttestationElectra{},
|
||||
existingAttWrapper.IndexedAttestation,
|
||||
)
|
||||
}
|
||||
incoming, ok := incomingAttWrapper.IndexedAttestation.(*ethpb.IndexedAttestationElectra)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf(
|
||||
"incoming attestation has wrong type (expected %T, got %T)",
|
||||
ðpb.IndexedAttestationElectra{},
|
||||
incomingAttWrapper.IndexedAttestation,
|
||||
)
|
||||
}
|
||||
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 +366,43 @@ func (m *MaxSpanChunksSlice) CheckSlashable(
|
||||
|
||||
surroundedVotesTotal.Inc()
|
||||
|
||||
// Both attestations should have the same type. If not, we convert the non-Electra attestation into an Electra attestation.
|
||||
unifyAttWrapperVersion(existingAttWrapper, incomingAttWrapper)
|
||||
|
||||
postElectra := existingAttWrapper.IndexedAttestation.Version() >= version.Electra
|
||||
if postElectra {
|
||||
existing, ok := existingAttWrapper.IndexedAttestation.(*ethpb.IndexedAttestationElectra)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf(
|
||||
"existing attestation has wrong type (expected %T, got %T)",
|
||||
ðpb.IndexedAttestationElectra{},
|
||||
existingAttWrapper.IndexedAttestation,
|
||||
)
|
||||
}
|
||||
incoming, ok := incomingAttWrapper.IndexedAttestation.(*ethpb.IndexedAttestationElectra)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf(
|
||||
"incoming attestation has wrong type (expected %T, got %T)",
|
||||
ðpb.IndexedAttestationElectra{},
|
||||
incomingAttWrapper.IndexedAttestation,
|
||||
)
|
||||
}
|
||||
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(
|
||||
|
||||
@@ -3,12 +3,14 @@ package slasher
|
||||
import (
|
||||
"context"
|
||||
"math"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
dbtest "github.com/prysmaticlabs/prysm/v5/beacon-chain/db/testing"
|
||||
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/prysmaticlabs/prysm/v5/testing/assert"
|
||||
"github.com/prysmaticlabs/prysm/v5/testing/require"
|
||||
)
|
||||
@@ -82,6 +84,99 @@ func TestMaxSpanChunksSlice_MaxChunkSpanFrom(t *testing.T) {
|
||||
|
||||
func TestMinSpanChunksSlice_CheckSlashable(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
for _, v := range []int{version.Phase0, version.Electra} {
|
||||
t.Run(version.String(v), func(t *testing.T) {
|
||||
slasherDB := dbtest.SetupSlasherDB(t)
|
||||
params := &Parameters{
|
||||
chunkSize: 3,
|
||||
validatorChunkSize: 2,
|
||||
historyLength: 3,
|
||||
}
|
||||
validatorIdx := primitives.ValidatorIndex(1)
|
||||
source := primitives.Epoch(1)
|
||||
target := primitives.Epoch(2)
|
||||
att := createAttestationWrapperEmptySig(t, v, source, target, nil, nil)
|
||||
|
||||
// A faulty chunk should lead to error.
|
||||
chunk := &MinSpanChunksSlice{
|
||||
params: params,
|
||||
data: []uint16{},
|
||||
}
|
||||
_, err := chunk.CheckSlashable(ctx, nil, validatorIdx, att)
|
||||
require.ErrorContains(t, "could not get min target for validator", err)
|
||||
|
||||
// We initialize a proper slice with 2 chunks with chunk size 3, 2 validators, and
|
||||
// a history length of 3 representing a perfect attesting history.
|
||||
//
|
||||
// val0 val1
|
||||
// { } { }
|
||||
// [2, 2, 2, 2, 2, 2]
|
||||
data := []uint16{2, 2, 2, 2, 2, 2}
|
||||
chunk, err = MinChunkSpansSliceFrom(params, data)
|
||||
require.NoError(t, err)
|
||||
|
||||
// An attestation with source 1 and target 2 should not be slashable
|
||||
// based on our min chunk for either validator.
|
||||
slashing, err := chunk.CheckSlashable(ctx, slasherDB, validatorIdx, att)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, nil, slashing)
|
||||
|
||||
slashing, err = chunk.CheckSlashable(ctx, slasherDB, validatorIdx.Sub(1), att)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, nil, slashing)
|
||||
|
||||
// Next up we initialize an empty chunks slice and mark an attestation
|
||||
// with (source 1, target 2) as attested.
|
||||
chunk = EmptyMinSpanChunksSlice(params)
|
||||
source = primitives.Epoch(1)
|
||||
target = primitives.Epoch(2)
|
||||
att = createAttestationWrapperEmptySig(t, v, source, target, nil, nil)
|
||||
chunkIndex := uint64(0)
|
||||
startEpoch := target
|
||||
currentEpoch := target
|
||||
_, err = chunk.Update(chunkIndex, currentEpoch, validatorIdx, startEpoch, target)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Next up, we create a surrounding vote, but it should NOT be slashable
|
||||
// because we DO NOT have an existing attestation record in our database at the min target epoch.
|
||||
source = primitives.Epoch(0)
|
||||
target = primitives.Epoch(3)
|
||||
surroundingVote := createAttestationWrapperEmptySig(t, v, source, target, nil, nil)
|
||||
|
||||
slashing, err = chunk.CheckSlashable(ctx, slasherDB, validatorIdx, surroundingVote)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, nil, slashing)
|
||||
|
||||
// Next up, we save the old attestation record, then check if the
|
||||
// surrounding vote is indeed slashable.
|
||||
attData := att.IndexedAttestation.GetData()
|
||||
attRecord := createAttestationWrapperEmptySig(t, v, attData.Source.Epoch, attData.Target.Epoch, []uint64{uint64(validatorIdx)}, []byte{1})
|
||||
err = slasherDB.SaveAttestationRecordsForValidators(
|
||||
ctx,
|
||||
[]*slashertypes.IndexedAttestationWrapper{attRecord},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
slashing, err = chunk.CheckSlashable(ctx, slasherDB, validatorIdx, surroundingVote)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, false, reflect.ValueOf(slashing).IsNil())
|
||||
|
||||
// We check the attestation with the lower data root is the first attestation.
|
||||
// Firstly we require the setup to have the surrounding vote as the second attestation.
|
||||
// Then we modify the root of the surrounding vote and expect the vote to be the first attestation.
|
||||
require.DeepEqual(t, surroundingVote.IndexedAttestation, slashing.SecondAttestation())
|
||||
surroundingVote.DataRoot = [32]byte{}
|
||||
slashing, err = chunk.CheckSlashable(ctx, slasherDB, validatorIdx, surroundingVote)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, false, reflect.ValueOf(slashing).IsNil())
|
||||
assert.DeepEqual(t, surroundingVote.IndexedAttestation, slashing.FirstAttestation())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMinSpanChunksSlice_CheckSlashable_DifferentVersions(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
slasherDB := dbtest.SetupSlasherDB(t)
|
||||
params := &Parameters{
|
||||
chunkSize: 3,
|
||||
@@ -91,75 +186,138 @@ func TestMinSpanChunksSlice_CheckSlashable(t *testing.T) {
|
||||
validatorIdx := primitives.ValidatorIndex(1)
|
||||
source := primitives.Epoch(1)
|
||||
target := primitives.Epoch(2)
|
||||
att := createAttestationWrapperEmptySig(t, source, target, nil, nil)
|
||||
|
||||
// A faulty chunk should lead to error.
|
||||
chunk := &MinSpanChunksSlice{
|
||||
params: params,
|
||||
data: []uint16{},
|
||||
}
|
||||
_, err := chunk.CheckSlashable(ctx, nil, validatorIdx, att)
|
||||
require.ErrorContains(t, "could not get min target for validator", err)
|
||||
// We create a vote with Phase0 version.
|
||||
att := createAttestationWrapperEmptySig(t, version.Phase0, source, target, nil, nil)
|
||||
|
||||
// We initialize a proper slice with 2 chunks with chunk size 3, 2 validators, and
|
||||
// a history length of 3 representing a perfect attesting history.
|
||||
//
|
||||
// val0 val1
|
||||
// { } { }
|
||||
// [2, 2, 2, 2, 2, 2]
|
||||
data := []uint16{2, 2, 2, 2, 2, 2}
|
||||
chunk, err = MinChunkSpansSliceFrom(params, data)
|
||||
require.NoError(t, err)
|
||||
|
||||
// An attestation with source 1 and target 2 should not be slashable
|
||||
// based on our min chunk for either validator.
|
||||
slashing, err := chunk.CheckSlashable(ctx, slasherDB, validatorIdx, att)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, nil, slashing)
|
||||
|
||||
slashing, err = chunk.CheckSlashable(ctx, slasherDB, validatorIdx.Sub(1), att)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, nil, slashing)
|
||||
|
||||
// Next up we initialize an empty chunks slice and mark an attestation
|
||||
// with (source 1, target 2) as attested.
|
||||
chunk = EmptyMinSpanChunksSlice(params)
|
||||
source = primitives.Epoch(1)
|
||||
target = primitives.Epoch(2)
|
||||
att = createAttestationWrapperEmptySig(t, source, target, nil, nil)
|
||||
// We initialize an empty chunks slice and mark an attestation with (source 1, target 2) as attested.
|
||||
chunk := EmptyMinSpanChunksSlice(params)
|
||||
chunkIndex := uint64(0)
|
||||
startEpoch := target
|
||||
currentEpoch := target
|
||||
_, err = chunk.Update(chunkIndex, currentEpoch, validatorIdx, startEpoch, target)
|
||||
_, err := chunk.Update(chunkIndex, currentEpoch, validatorIdx, startEpoch, target)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Next up, we create a surrounding vote, but it should NOT be slashable
|
||||
// because we DO NOT have an existing attestation record in our database at the min target epoch.
|
||||
// We create a surrounding vote with Electra version.
|
||||
source = primitives.Epoch(0)
|
||||
target = primitives.Epoch(3)
|
||||
surroundingVote := createAttestationWrapperEmptySig(t, source, target, nil, nil)
|
||||
surroundingVote := createAttestationWrapperEmptySig(t, version.Electra, source, target, nil, nil)
|
||||
|
||||
slashing, err = chunk.CheckSlashable(ctx, slasherDB, validatorIdx, surroundingVote)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, nil, slashing)
|
||||
|
||||
// Next up, we save the old attestation record, then check if the
|
||||
// surrounding vote is indeed slashable.
|
||||
// We save the old attestation record, then check if the surrounding vote is indeed slashable.
|
||||
attData := att.IndexedAttestation.GetData()
|
||||
attRecord := createAttestationWrapperEmptySig(t, attData.Source.Epoch, attData.Target.Epoch, []uint64{uint64(validatorIdx)}, []byte{1})
|
||||
attRecord := createAttestationWrapperEmptySig(t, version.Phase0, attData.Source.Epoch, attData.Target.Epoch, []uint64{uint64(validatorIdx)}, []byte{1})
|
||||
err = slasherDB.SaveAttestationRecordsForValidators(
|
||||
ctx,
|
||||
[]*slashertypes.IndexedAttestationWrapper{attRecord},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
slashing, err = chunk.CheckSlashable(ctx, slasherDB, validatorIdx, surroundingVote)
|
||||
slashing, err := chunk.CheckSlashable(ctx, slasherDB, validatorIdx, surroundingVote)
|
||||
require.NoError(t, err)
|
||||
require.NotEqual(t, (*ethpb.AttesterSlashing)(nil), slashing)
|
||||
// The old record should be converted to Electra and the resulting slashing should be an Electra slashing.
|
||||
electraSlashing, ok := slashing.(*ethpb.AttesterSlashingElectra)
|
||||
require.Equal(t, true, ok, "slashing has the wrong type")
|
||||
assert.NotNil(t, electraSlashing)
|
||||
}
|
||||
|
||||
func TestMaxSpanChunksSlice_CheckSlashable(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
for _, v := range []int{version.Phase0, version.Electra} {
|
||||
t.Run(version.String(v), func(t *testing.T) {
|
||||
slasherDB := dbtest.SetupSlasherDB(t)
|
||||
params := &Parameters{
|
||||
chunkSize: 4,
|
||||
validatorChunkSize: 2,
|
||||
historyLength: 4,
|
||||
}
|
||||
validatorIdx := primitives.ValidatorIndex(1)
|
||||
source := primitives.Epoch(1)
|
||||
target := primitives.Epoch(2)
|
||||
att := createAttestationWrapperEmptySig(t, v, source, target, nil, nil)
|
||||
|
||||
// A faulty chunk should lead to error.
|
||||
chunk := &MaxSpanChunksSlice{
|
||||
params: params,
|
||||
data: []uint16{},
|
||||
}
|
||||
_, err := chunk.CheckSlashable(ctx, nil, validatorIdx, att)
|
||||
require.ErrorContains(t, "could not get max target for validator", err)
|
||||
|
||||
// We initialize a proper slice with 2 chunks with chunk size 4, 2 validators, and
|
||||
// a history length of 4 representing a perfect attesting history.
|
||||
//
|
||||
// val0 val1
|
||||
// { } { }
|
||||
// [0, 0, 0, 0, 0, 0, 0, 0]
|
||||
data := []uint16{0, 0, 0, 0, 0, 0, 0, 0}
|
||||
chunk, err = MaxChunkSpansSliceFrom(params, data)
|
||||
require.NoError(t, err)
|
||||
|
||||
// An attestation with source 1 and target 2 should not be slashable
|
||||
// based on our max chunk for either validator.
|
||||
slashing, err := chunk.CheckSlashable(ctx, slasherDB, validatorIdx, att)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, nil, slashing)
|
||||
|
||||
slashing, err = chunk.CheckSlashable(ctx, slasherDB, validatorIdx.Sub(1), att)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, nil, slashing)
|
||||
|
||||
// Next up we initialize an empty chunks slice and mark an attestation
|
||||
// with (source 0, target 3) as attested.
|
||||
chunk = EmptyMaxSpanChunksSlice(params)
|
||||
source = primitives.Epoch(0)
|
||||
target = primitives.Epoch(3)
|
||||
att = createAttestationWrapperEmptySig(t, v, source, target, nil, nil)
|
||||
chunkIndex := uint64(0)
|
||||
startEpoch := source
|
||||
currentEpoch := target
|
||||
_, err = chunk.Update(chunkIndex, currentEpoch, validatorIdx, startEpoch, target)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Next up, we create a surrounded vote, but it should NOT be slashable
|
||||
// because we DO NOT have an existing attestation record in our database at the max target epoch.
|
||||
source = primitives.Epoch(1)
|
||||
target = primitives.Epoch(2)
|
||||
surroundedVote := createAttestationWrapperEmptySig(t, v, source, target, nil, nil)
|
||||
|
||||
slashing, err = chunk.CheckSlashable(ctx, slasherDB, validatorIdx, surroundedVote)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, nil, slashing)
|
||||
|
||||
// Next up, we save the old attestation record, then check if the
|
||||
// surroundedVote vote is indeed slashable.
|
||||
attData := att.IndexedAttestation.GetData()
|
||||
signingRoot := [32]byte{1}
|
||||
attRecord := createAttestationWrapperEmptySig(
|
||||
t, v, attData.Source.Epoch, attData.Target.Epoch, []uint64{uint64(validatorIdx)}, signingRoot[:],
|
||||
)
|
||||
err = slasherDB.SaveAttestationRecordsForValidators(
|
||||
ctx,
|
||||
[]*slashertypes.IndexedAttestationWrapper{attRecord},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
slashing, err = chunk.CheckSlashable(ctx, slasherDB, validatorIdx, surroundedVote)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, false, reflect.ValueOf(slashing).IsNil())
|
||||
|
||||
// We check the attestation with the lower data root is the first attestation.
|
||||
// Firstly we require the setup to have the surrounded vote as the second attestation.
|
||||
// Then we modify the root of the surrounded vote and expect the vote to be the first attestation.
|
||||
require.DeepEqual(t, surroundedVote.IndexedAttestation, slashing.SecondAttestation())
|
||||
surroundedVote.DataRoot = [32]byte{}
|
||||
slashing, err = chunk.CheckSlashable(ctx, slasherDB, validatorIdx, surroundedVote)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, false, reflect.ValueOf(slashing).IsNil())
|
||||
assert.DeepEqual(t, surroundedVote.IndexedAttestation, slashing.FirstAttestation())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMaxSpanChunksSlice_CheckSlashable_DifferentVersions(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
slasherDB := dbtest.SetupSlasherDB(t)
|
||||
params := &Parameters{
|
||||
chunkSize: 4,
|
||||
@@ -167,76 +325,38 @@ func TestMaxSpanChunksSlice_CheckSlashable(t *testing.T) {
|
||||
historyLength: 4,
|
||||
}
|
||||
validatorIdx := primitives.ValidatorIndex(1)
|
||||
source := primitives.Epoch(1)
|
||||
target := primitives.Epoch(2)
|
||||
att := createAttestationWrapperEmptySig(t, source, target, nil, nil)
|
||||
source := primitives.Epoch(0)
|
||||
target := primitives.Epoch(3)
|
||||
|
||||
// A faulty chunk should lead to error.
|
||||
chunk := &MaxSpanChunksSlice{
|
||||
params: params,
|
||||
data: []uint16{},
|
||||
}
|
||||
_, err := chunk.CheckSlashable(ctx, nil, validatorIdx, att)
|
||||
require.ErrorContains(t, "could not get max target for validator", err)
|
||||
// We create a vote with Phase0 version.
|
||||
att := createAttestationWrapperEmptySig(t, version.Phase0, source, target, nil, nil)
|
||||
|
||||
// We initialize a proper slice with 2 chunks with chunk size 4, 2 validators, and
|
||||
// a history length of 4 representing a perfect attesting history.
|
||||
//
|
||||
// val0 val1
|
||||
// { } { }
|
||||
// [0, 0, 0, 0, 0, 0, 0, 0]
|
||||
data := []uint16{0, 0, 0, 0, 0, 0, 0, 0}
|
||||
chunk, err = MaxChunkSpansSliceFrom(params, data)
|
||||
require.NoError(t, err)
|
||||
|
||||
// An attestation with source 1 and target 2 should not be slashable
|
||||
// based on our max chunk for either validator.
|
||||
slashing, err := chunk.CheckSlashable(ctx, slasherDB, validatorIdx, att)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, nil, slashing)
|
||||
|
||||
slashing, err = chunk.CheckSlashable(ctx, slasherDB, validatorIdx.Sub(1), att)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, nil, slashing)
|
||||
|
||||
// Next up we initialize an empty chunks slice and mark an attestation
|
||||
// with (source 0, target 3) as attested.
|
||||
chunk = EmptyMaxSpanChunksSlice(params)
|
||||
source = primitives.Epoch(0)
|
||||
target = primitives.Epoch(3)
|
||||
att = createAttestationWrapperEmptySig(t, source, target, nil, nil)
|
||||
// We initialize an empty chunks slice and mark an attestation with (source 0, target 3) as attested.
|
||||
chunk := EmptyMaxSpanChunksSlice(params)
|
||||
chunkIndex := uint64(0)
|
||||
startEpoch := source
|
||||
currentEpoch := target
|
||||
_, err = chunk.Update(chunkIndex, currentEpoch, validatorIdx, startEpoch, target)
|
||||
_, err := chunk.Update(chunkIndex, target, validatorIdx, source, target)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Next up, we create a surrounded vote, but it should NOT be slashable
|
||||
// because we DO NOT have an existing attestation record in our database at the max target epoch.
|
||||
// We create a surrounded vote with Electra version.
|
||||
source = primitives.Epoch(1)
|
||||
target = primitives.Epoch(2)
|
||||
surroundedVote := createAttestationWrapperEmptySig(t, source, target, nil, nil)
|
||||
surroundedVote := createAttestationWrapperEmptySig(t, version.Electra, source, target, nil, nil)
|
||||
|
||||
slashing, err = chunk.CheckSlashable(ctx, slasherDB, validatorIdx, surroundedVote)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, nil, slashing)
|
||||
|
||||
// Next up, we save the old attestation record, then check if the
|
||||
// surroundedVote vote is indeed slashable.
|
||||
// We save the old attestation record, then check if the surrounded vote is indeed slashable.
|
||||
attData := att.IndexedAttestation.GetData()
|
||||
signingRoot := [32]byte{1}
|
||||
attRecord := createAttestationWrapperEmptySig(
|
||||
t, attData.Source.Epoch, attData.Target.Epoch, []uint64{uint64(validatorIdx)}, signingRoot[:],
|
||||
)
|
||||
attRecord := createAttestationWrapperEmptySig(t, version.Phase0, attData.Source.Epoch, attData.Target.Epoch, []uint64{uint64(validatorIdx)}, []byte{1})
|
||||
err = slasherDB.SaveAttestationRecordsForValidators(
|
||||
ctx,
|
||||
[]*slashertypes.IndexedAttestationWrapper{attRecord},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
slashing, err = chunk.CheckSlashable(ctx, slasherDB, validatorIdx, surroundedVote)
|
||||
slashing, err := chunk.CheckSlashable(ctx, slasherDB, validatorIdx, surroundedVote)
|
||||
require.NoError(t, err)
|
||||
require.NotEqual(t, (*ethpb.AttesterSlashing)(nil), slashing)
|
||||
// The old record should be converted to Electra and the resulting slashing should be an Electra slashing.
|
||||
electraSlashing, ok := slashing.(*ethpb.AttesterSlashingElectra)
|
||||
require.Equal(t, true, ok, "slashing has wrong type")
|
||||
assert.NotNil(t, electraSlashing)
|
||||
}
|
||||
|
||||
func TestMinSpanChunksSlice_Update_MultipleChunks(t *testing.T) {
|
||||
|
||||
@@ -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,69 @@ 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,
|
||||
)
|
||||
}
|
||||
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,
|
||||
)
|
||||
}
|
||||
var slashing ethpb.AttSlashing
|
||||
|
||||
slashing := ðpb.AttesterSlashing{
|
||||
Attestation_1: existing,
|
||||
Attestation_2: incoming,
|
||||
}
|
||||
// Both attestations should have the same type. If not, we convert both to Electra attestations.
|
||||
unifyAttWrapperVersion(existingAttWrapper, incomingAttWrapper)
|
||||
|
||||
// Ensure the attestation with the lower data root is the first attestation.
|
||||
if bytes.Compare(existingAttWrapper.DataRoot[:], incomingAttWrapper.DataRoot[:]) > 0 {
|
||||
postElectra := existingAttWrapper.IndexedAttestation.Version() >= version.Electra
|
||||
if postElectra {
|
||||
existing, ok := existingAttWrapper.IndexedAttestation.(*ethpb.IndexedAttestationElectra)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf(
|
||||
"existing attestation has wrong type (expected %T, got %T)",
|
||||
ðpb.IndexedAttestationElectra{},
|
||||
existingAttWrapper.IndexedAttestation,
|
||||
)
|
||||
}
|
||||
incoming, ok := incomingAttWrapper.IndexedAttestation.(*ethpb.IndexedAttestationElectra)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf(
|
||||
"incoming attestation has wrong type (expected %T, got %T)",
|
||||
ðpb.IndexedAttestationElectra{},
|
||||
incomingAttWrapper.IndexedAttestation,
|
||||
)
|
||||
}
|
||||
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,
|
||||
}
|
||||
}
|
||||
} 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 +282,69 @@ 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,
|
||||
)
|
||||
}
|
||||
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,
|
||||
)
|
||||
}
|
||||
var slashing ethpb.AttSlashing
|
||||
|
||||
slashing := ðpb.AttesterSlashing{
|
||||
Attestation_1: att_1,
|
||||
Attestation_2: att_2,
|
||||
}
|
||||
// Both attestations should have the same type. If not, we convert both to Electra attestations.
|
||||
unifyAttWrapperVersion(wrapper_1, wrapper_2)
|
||||
|
||||
// Ensure the attestation with the lower data root is the first attestation.
|
||||
if bytes.Compare(wrapper_1.DataRoot[:], wrapper_2.DataRoot[:]) > 0 {
|
||||
postElectra := wrapper_1.IndexedAttestation.Version() >= version.Electra
|
||||
if postElectra {
|
||||
att_1, ok := wrapper_1.IndexedAttestation.(*ethpb.IndexedAttestationElectra)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf(
|
||||
"first attestation has wrong type (expected %T, got %T)",
|
||||
ðpb.IndexedAttestationElectra{},
|
||||
wrapper_1.IndexedAttestation,
|
||||
)
|
||||
}
|
||||
att_2, ok := wrapper_2.IndexedAttestation.(*ethpb.IndexedAttestationElectra)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf(
|
||||
"second attestation has wrong type (expected %T, got %T)",
|
||||
ðpb.IndexedAttestationElectra{},
|
||||
wrapper_2.IndexedAttestation,
|
||||
)
|
||||
}
|
||||
slashing = ðpb.AttesterSlashingElectra{
|
||||
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.AttesterSlashingElectra{
|
||||
Attestation_1: att_2,
|
||||
Attestation_2: att_1,
|
||||
}
|
||||
}
|
||||
} 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/v5/container/slice"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v5/runtime/version"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
@@ -249,3 +250,24 @@ func closeDB(d *slasherkv.Store) {
|
||||
log.WithError(err).Error("could not close database")
|
||||
}
|
||||
}
|
||||
|
||||
// unifyAttWrapperVersion ensures that the two wrappers wrap indexed attestations of the same version.
|
||||
// If versions differ, the wrapped attestation with the lower version will be converted to the higher version.
|
||||
func unifyAttWrapperVersion(w1 *slashertypes.IndexedAttestationWrapper, w2 *slashertypes.IndexedAttestationWrapper) {
|
||||
if w1.IndexedAttestation.Version() == w2.IndexedAttestation.Version() {
|
||||
return
|
||||
}
|
||||
if w1.IndexedAttestation.Version() != version.Electra {
|
||||
w1.IndexedAttestation = ðpb.IndexedAttestationElectra{
|
||||
AttestingIndices: w1.IndexedAttestation.GetAttestingIndices(),
|
||||
Data: w1.IndexedAttestation.GetData(),
|
||||
Signature: w1.IndexedAttestation.GetSignature(),
|
||||
}
|
||||
return
|
||||
}
|
||||
w2.IndexedAttestation = ðpb.IndexedAttestationElectra{
|
||||
AttestingIndices: w2.IndexedAttestation.GetAttestingIndices(),
|
||||
Data: w2.IndexedAttestation.GetData(),
|
||||
Signature: w2.IndexedAttestation.GetSignature(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v5/config/params"
|
||||
"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/prysmaticlabs/prysm/v5/testing/require"
|
||||
logTest "github.com/sirupsen/logrus/hooks/test"
|
||||
)
|
||||
@@ -32,13 +33,13 @@ func TestService_groupByValidatorChunkIndex(t *testing.T) {
|
||||
validatorChunkSize: 2,
|
||||
},
|
||||
atts: []*slashertypes.IndexedAttestationWrapper{
|
||||
createAttestationWrapperEmptySig(t, 0, 0, []uint64{0, 1}, nil),
|
||||
createAttestationWrapperEmptySig(t, 0, 0, []uint64{0, 1}, nil),
|
||||
createAttestationWrapperEmptySig(t, version.Phase0, 0, 0, []uint64{0, 1}, nil),
|
||||
createAttestationWrapperEmptySig(t, version.Phase0, 0, 0, []uint64{0, 1}, nil),
|
||||
},
|
||||
want: map[uint64][]*slashertypes.IndexedAttestationWrapper{
|
||||
0: {
|
||||
createAttestationWrapperEmptySig(t, 0, 0, []uint64{0, 1}, nil),
|
||||
createAttestationWrapperEmptySig(t, 0, 0, []uint64{0, 1}, nil),
|
||||
createAttestationWrapperEmptySig(t, version.Phase0, 0, 0, []uint64{0, 1}, nil),
|
||||
createAttestationWrapperEmptySig(t, version.Phase0, 0, 0, []uint64{0, 1}, nil),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -48,17 +49,17 @@ func TestService_groupByValidatorChunkIndex(t *testing.T) {
|
||||
validatorChunkSize: 2,
|
||||
},
|
||||
atts: []*slashertypes.IndexedAttestationWrapper{
|
||||
createAttestationWrapperEmptySig(t, 0, 0, []uint64{0, 2, 4}, nil),
|
||||
createAttestationWrapperEmptySig(t, version.Phase0, 0, 0, []uint64{0, 2, 4}, nil),
|
||||
},
|
||||
want: map[uint64][]*slashertypes.IndexedAttestationWrapper{
|
||||
0: {
|
||||
createAttestationWrapperEmptySig(t, 0, 0, []uint64{0, 2, 4}, nil),
|
||||
createAttestationWrapperEmptySig(t, version.Phase0, 0, 0, []uint64{0, 2, 4}, nil),
|
||||
},
|
||||
1: {
|
||||
createAttestationWrapperEmptySig(t, 0, 0, []uint64{0, 2, 4}, nil),
|
||||
createAttestationWrapperEmptySig(t, version.Phase0, 0, 0, []uint64{0, 2, 4}, nil),
|
||||
},
|
||||
2: {
|
||||
createAttestationWrapperEmptySig(t, 0, 0, []uint64{0, 2, 4}, nil),
|
||||
createAttestationWrapperEmptySig(t, version.Phase0, 0, 0, []uint64{0, 2, 4}, nil),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -95,13 +96,13 @@ func TestService_groupByChunkIndex(t *testing.T) {
|
||||
historyLength: 3,
|
||||
},
|
||||
atts: []*slashertypes.IndexedAttestationWrapper{
|
||||
createAttestationWrapperEmptySig(t, 0, 0, nil, nil),
|
||||
createAttestationWrapperEmptySig(t, 1, 0, nil, nil),
|
||||
createAttestationWrapperEmptySig(t, version.Phase0, 0, 0, nil, nil),
|
||||
createAttestationWrapperEmptySig(t, version.Phase0, 1, 0, nil, nil),
|
||||
},
|
||||
want: map[uint64][]*slashertypes.IndexedAttestationWrapper{
|
||||
0: {
|
||||
createAttestationWrapperEmptySig(t, 0, 0, nil, nil),
|
||||
createAttestationWrapperEmptySig(t, 1, 0, nil, nil),
|
||||
createAttestationWrapperEmptySig(t, version.Phase0, 0, 0, nil, nil),
|
||||
createAttestationWrapperEmptySig(t, version.Phase0, 1, 0, nil, nil),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -112,17 +113,17 @@ func TestService_groupByChunkIndex(t *testing.T) {
|
||||
historyLength: 3,
|
||||
},
|
||||
atts: []*slashertypes.IndexedAttestationWrapper{
|
||||
createAttestationWrapperEmptySig(t, 0, 0, nil, nil),
|
||||
createAttestationWrapperEmptySig(t, 1, 0, nil, nil),
|
||||
createAttestationWrapperEmptySig(t, 2, 0, nil, nil),
|
||||
createAttestationWrapperEmptySig(t, version.Phase0, 0, 0, nil, nil),
|
||||
createAttestationWrapperEmptySig(t, version.Phase0, 1, 0, nil, nil),
|
||||
createAttestationWrapperEmptySig(t, version.Phase0, 2, 0, nil, nil),
|
||||
},
|
||||
want: map[uint64][]*slashertypes.IndexedAttestationWrapper{
|
||||
0: {
|
||||
createAttestationWrapperEmptySig(t, 0, 0, nil, nil),
|
||||
createAttestationWrapperEmptySig(t, 1, 0, nil, nil),
|
||||
createAttestationWrapperEmptySig(t, version.Phase0, 0, 0, nil, nil),
|
||||
createAttestationWrapperEmptySig(t, version.Phase0, 1, 0, nil, nil),
|
||||
},
|
||||
1: {
|
||||
createAttestationWrapperEmptySig(t, 2, 0, nil, nil),
|
||||
createAttestationWrapperEmptySig(t, version.Phase0, 2, 0, nil, nil),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -207,7 +208,7 @@ func TestService_filterAttestations(t *testing.T) {
|
||||
{
|
||||
name: "Source > target gets dropped",
|
||||
input: []*slashertypes.IndexedAttestationWrapper{
|
||||
createAttestationWrapperEmptySig(t, 1, 0, []uint64{1}, make([]byte, 32)),
|
||||
createAttestationWrapperEmptySig(t, version.Phase0, 1, 0, []uint64{1}, make([]byte, 32)),
|
||||
},
|
||||
inputEpoch: 0,
|
||||
wantedDropped: 1,
|
||||
@@ -215,33 +216,33 @@ func TestService_filterAttestations(t *testing.T) {
|
||||
{
|
||||
name: "Source < target is valid",
|
||||
input: []*slashertypes.IndexedAttestationWrapper{
|
||||
createAttestationWrapperEmptySig(t, 0, 1, []uint64{1}, make([]byte, 32)),
|
||||
createAttestationWrapperEmptySig(t, version.Phase0, 0, 1, []uint64{1}, make([]byte, 32)),
|
||||
},
|
||||
inputEpoch: 1,
|
||||
wantedValid: []*slashertypes.IndexedAttestationWrapper{
|
||||
createAttestationWrapperEmptySig(t, 0, 1, []uint64{1}, make([]byte, 32)),
|
||||
createAttestationWrapperEmptySig(t, version.Phase0, 0, 1, []uint64{1}, make([]byte, 32)),
|
||||
},
|
||||
wantedDropped: 0,
|
||||
},
|
||||
{
|
||||
name: "Source == target is valid",
|
||||
input: []*slashertypes.IndexedAttestationWrapper{
|
||||
createAttestationWrapperEmptySig(t, 0, 0, []uint64{1}, make([]byte, 32)),
|
||||
createAttestationWrapperEmptySig(t, version.Phase0, 0, 0, []uint64{1}, make([]byte, 32)),
|
||||
},
|
||||
inputEpoch: 1,
|
||||
wantedValid: []*slashertypes.IndexedAttestationWrapper{
|
||||
createAttestationWrapperEmptySig(t, 0, 0, []uint64{1}, make([]byte, 32)),
|
||||
createAttestationWrapperEmptySig(t, version.Phase0, 0, 0, []uint64{1}, make([]byte, 32)),
|
||||
},
|
||||
wantedDropped: 0,
|
||||
},
|
||||
{
|
||||
name: "Attestation from the future is deferred",
|
||||
input: []*slashertypes.IndexedAttestationWrapper{
|
||||
createAttestationWrapperEmptySig(t, 0, 2, []uint64{1}, make([]byte, 32)),
|
||||
createAttestationWrapperEmptySig(t, version.Phase0, 0, 2, []uint64{1}, make([]byte, 32)),
|
||||
},
|
||||
inputEpoch: 1,
|
||||
wantedDeferred: []*slashertypes.IndexedAttestationWrapper{
|
||||
createAttestationWrapperEmptySig(t, 0, 2, []uint64{1}, make([]byte, 32)),
|
||||
createAttestationWrapperEmptySig(t, version.Phase0, 0, 2, []uint64{1}, make([]byte, 32)),
|
||||
},
|
||||
wantedDropped: 0,
|
||||
},
|
||||
@@ -271,22 +272,22 @@ func Test_logSlashingEvent(t *testing.T) {
|
||||
{
|
||||
name: "Surrounding vote",
|
||||
slashing: ðpb.AttesterSlashing{
|
||||
Attestation_1: createAttestationWrapperEmptySig(t, 0, 0, nil, nil).IndexedAttestation.(*ethpb.IndexedAttestation),
|
||||
Attestation_2: createAttestationWrapperEmptySig(t, 0, 0, nil, nil).IndexedAttestation.(*ethpb.IndexedAttestation),
|
||||
Attestation_1: createAttestationWrapperEmptySig(t, version.Phase0, 0, 0, nil, nil).IndexedAttestation.(*ethpb.IndexedAttestation),
|
||||
Attestation_2: createAttestationWrapperEmptySig(t, version.Phase0, 0, 0, nil, nil).IndexedAttestation.(*ethpb.IndexedAttestation),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Surrounded vote",
|
||||
slashing: ðpb.AttesterSlashing{
|
||||
Attestation_1: createAttestationWrapperEmptySig(t, 0, 0, nil, nil).IndexedAttestation.(*ethpb.IndexedAttestation),
|
||||
Attestation_2: createAttestationWrapperEmptySig(t, 0, 0, nil, nil).IndexedAttestation.(*ethpb.IndexedAttestation),
|
||||
Attestation_1: createAttestationWrapperEmptySig(t, version.Phase0, 0, 0, nil, nil).IndexedAttestation.(*ethpb.IndexedAttestation),
|
||||
Attestation_2: createAttestationWrapperEmptySig(t, version.Phase0, 0, 0, nil, nil).IndexedAttestation.(*ethpb.IndexedAttestation),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Double vote",
|
||||
slashing: ðpb.AttesterSlashing{
|
||||
Attestation_1: createAttestationWrapperEmptySig(t, 0, 0, nil, nil).IndexedAttestation.(*ethpb.IndexedAttestation),
|
||||
Attestation_2: createAttestationWrapperEmptySig(t, 0, 0, nil, nil).IndexedAttestation.(*ethpb.IndexedAttestation),
|
||||
Attestation_1: createAttestationWrapperEmptySig(t, version.Phase0, 0, 0, nil, nil).IndexedAttestation.(*ethpb.IndexedAttestation),
|
||||
Attestation_2: createAttestationWrapperEmptySig(t, version.Phase0, 0, 0, nil, nil).IndexedAttestation.(*ethpb.IndexedAttestation),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
slashertypes "github.com/prysmaticlabs/prysm/v5/beacon-chain/slasher/types"
|
||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/v5/runtime/version"
|
||||
"github.com/prysmaticlabs/prysm/v5/testing/require"
|
||||
)
|
||||
|
||||
@@ -12,8 +13,8 @@ func Test_attestationsQueue(t *testing.T) {
|
||||
t.Run("push_and_dequeue", func(tt *testing.T) {
|
||||
attQueue := newAttestationsQueue()
|
||||
wantedAtts := []*slashertypes.IndexedAttestationWrapper{
|
||||
createAttestationWrapperEmptySig(t, 0, 1, []uint64{1}, make([]byte, 32)),
|
||||
createAttestationWrapperEmptySig(t, 1, 2, []uint64{1}, make([]byte, 32)),
|
||||
createAttestationWrapperEmptySig(t, version.Phase0, 0, 1, []uint64{1}, make([]byte, 32)),
|
||||
createAttestationWrapperEmptySig(t, version.Phase0, 1, 2, []uint64{1}, make([]byte, 32)),
|
||||
}
|
||||
attQueue.push(wantedAtts[0])
|
||||
attQueue.push(wantedAtts[1])
|
||||
@@ -27,8 +28,8 @@ func Test_attestationsQueue(t *testing.T) {
|
||||
t.Run("extend_and_dequeue", func(tt *testing.T) {
|
||||
attQueue := newAttestationsQueue()
|
||||
wantedAtts := []*slashertypes.IndexedAttestationWrapper{
|
||||
createAttestationWrapperEmptySig(t, 0, 1, []uint64{1}, make([]byte, 32)),
|
||||
createAttestationWrapperEmptySig(t, 1, 2, []uint64{1}, make([]byte, 32)),
|
||||
createAttestationWrapperEmptySig(t, version.Phase0, 0, 1, []uint64{1}, make([]byte, 32)),
|
||||
createAttestationWrapperEmptySig(t, version.Phase0, 1, 2, []uint64{1}, make([]byte, 32)),
|
||||
}
|
||||
attQueue.extend(wantedAtts)
|
||||
require.DeepEqual(t, 2, attQueue.size())
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v5/runtime/version"
|
||||
"github.com/prysmaticlabs/prysm/v5/testing/assert"
|
||||
"github.com/prysmaticlabs/prysm/v5/testing/require"
|
||||
"github.com/prysmaticlabs/prysm/v5/testing/util"
|
||||
@@ -38,8 +39,8 @@ func TestSlasher_receiveAttestations_OK(t *testing.T) {
|
||||
}()
|
||||
firstIndices := []uint64{1, 2, 3}
|
||||
secondIndices := []uint64{4, 5, 6}
|
||||
att1 := createAttestationWrapperEmptySig(t, 1, 2, firstIndices, nil)
|
||||
att2 := createAttestationWrapperEmptySig(t, 1, 2, secondIndices, nil)
|
||||
att1 := createAttestationWrapperEmptySig(t, version.Phase0, 1, 2, firstIndices, nil)
|
||||
att2 := createAttestationWrapperEmptySig(t, version.Phase0, 1, 2, secondIndices, nil)
|
||||
wrappedAtt1 := &slashertypes.WrappedIndexedAtt{IndexedAtt: att1.IndexedAttestation}
|
||||
wrappedAtt2 := &slashertypes.WrappedIndexedAtt{IndexedAtt: att2.IndexedAttestation}
|
||||
indexedAttsChan <- wrappedAtt1
|
||||
@@ -67,14 +68,14 @@ func TestService_pruneSlasherDataWithinSlidingWindow_AttestationsPruned(t *testi
|
||||
|
||||
// Setup attestations for 2 validators at each epoch for epochs 0, 1, 2, 3.
|
||||
err := slasherDB.SaveAttestationRecordsForValidators(ctx, []*slashertypes.IndexedAttestationWrapper{
|
||||
createAttestationWrapperEmptySig(t, 0, 0, []uint64{0}, bytesutil.PadTo([]byte("0a"), 32)),
|
||||
createAttestationWrapperEmptySig(t, 0, 0, []uint64{1}, bytesutil.PadTo([]byte("0b"), 32)),
|
||||
createAttestationWrapperEmptySig(t, 0, 1, []uint64{0}, bytesutil.PadTo([]byte("1a"), 32)),
|
||||
createAttestationWrapperEmptySig(t, 0, 1, []uint64{1}, bytesutil.PadTo([]byte("1b"), 32)),
|
||||
createAttestationWrapperEmptySig(t, 0, 2, []uint64{0}, bytesutil.PadTo([]byte("2a"), 32)),
|
||||
createAttestationWrapperEmptySig(t, 0, 2, []uint64{1}, bytesutil.PadTo([]byte("2b"), 32)),
|
||||
createAttestationWrapperEmptySig(t, 0, 3, []uint64{0}, bytesutil.PadTo([]byte("3a"), 32)),
|
||||
createAttestationWrapperEmptySig(t, 0, 3, []uint64{1}, bytesutil.PadTo([]byte("3b"), 32)),
|
||||
createAttestationWrapperEmptySig(t, version.Phase0, 0, 0, []uint64{0}, bytesutil.PadTo([]byte("0a"), 32)),
|
||||
createAttestationWrapperEmptySig(t, version.Phase0, 0, 0, []uint64{1}, bytesutil.PadTo([]byte("0b"), 32)),
|
||||
createAttestationWrapperEmptySig(t, version.Phase0, 0, 1, []uint64{0}, bytesutil.PadTo([]byte("1a"), 32)),
|
||||
createAttestationWrapperEmptySig(t, version.Phase0, 0, 1, []uint64{1}, bytesutil.PadTo([]byte("1b"), 32)),
|
||||
createAttestationWrapperEmptySig(t, version.Phase0, 0, 2, []uint64{0}, bytesutil.PadTo([]byte("2a"), 32)),
|
||||
createAttestationWrapperEmptySig(t, version.Phase0, 0, 2, []uint64{1}, bytesutil.PadTo([]byte("2b"), 32)),
|
||||
createAttestationWrapperEmptySig(t, version.Phase0, 0, 3, []uint64{0}, bytesutil.PadTo([]byte("3a"), 32)),
|
||||
createAttestationWrapperEmptySig(t, version.Phase0, 0, 3, []uint64{1}, bytesutil.PadTo([]byte("3b"), 32)),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -95,8 +96,8 @@ func TestService_pruneSlasherDataWithinSlidingWindow_AttestationsPruned(t *testi
|
||||
|
||||
// Setup attestations for 2 validators at epoch 4.
|
||||
err = slasherDB.SaveAttestationRecordsForValidators(ctx, []*slashertypes.IndexedAttestationWrapper{
|
||||
createAttestationWrapperEmptySig(t, 0, 4, []uint64{0}, bytesutil.PadTo([]byte("4a"), 32)),
|
||||
createAttestationWrapperEmptySig(t, 0, 4, []uint64{1}, bytesutil.PadTo([]byte("4b"), 32)),
|
||||
createAttestationWrapperEmptySig(t, version.Phase0, 0, 4, []uint64{0}, bytesutil.PadTo([]byte("4a"), 32)),
|
||||
createAttestationWrapperEmptySig(t, version.Phase0, 0, 4, []uint64{1}, bytesutil.PadTo([]byte("4b"), 32)),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -224,7 +225,7 @@ func TestSlasher_receiveAttestations_OnlyValidAttestations(t *testing.T) {
|
||||
firstIndices := []uint64{1, 2, 3}
|
||||
secondIndices := []uint64{4, 5, 6}
|
||||
// Add a valid attestation.
|
||||
validAtt := createAttestationWrapperEmptySig(t, 1, 2, firstIndices, nil)
|
||||
validAtt := createAttestationWrapperEmptySig(t, version.Phase0, 1, 2, firstIndices, nil)
|
||||
wrappedValidAtt := &slashertypes.WrappedIndexedAtt{IndexedAtt: validAtt.IndexedAttestation}
|
||||
indexedAttsChan <- wrappedValidAtt
|
||||
// Send an invalid, bad attestation which will not
|
||||
|
||||
3
changelog/radek_eip-7549-slasher-pt1.md
Normal file
3
changelog/radek_eip-7549-slasher-pt1.md
Normal file
@@ -0,0 +1,3 @@
|
||||
### Added
|
||||
|
||||
- Update slasher service to Electra
|
||||
@@ -32,6 +32,7 @@ go_library(
|
||||
"//crypto/rand:go_default_library",
|
||||
"//encoding/bytesutil: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_sirupsen_logrus//:go_default_library",
|
||||
@@ -55,6 +56,7 @@ go_test(
|
||||
"//crypto/bls:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//proto/prysm/v1alpha1/slashings:go_default_library",
|
||||
"//runtime/version:go_default_library",
|
||||
"//testing/require:go_default_library",
|
||||
"//testing/util:go_default_library",
|
||||
],
|
||||
|
||||
@@ -15,15 +15,14 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v5/crypto/rand"
|
||||
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v5/runtime/version"
|
||||
"github.com/prysmaticlabs/prysm/v5/time/slots"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func (s *Simulator) generateAttestationsForSlot(
|
||||
ctx context.Context, slot primitives.Slot,
|
||||
) ([]*ethpb.IndexedAttestation, []*ethpb.AttesterSlashing, error) {
|
||||
attestations := make([]*ethpb.IndexedAttestation, 0)
|
||||
slashings := make([]*ethpb.AttesterSlashing, 0)
|
||||
func (s *Simulator) generateAttestationsForSlot(ctx context.Context, ver int, slot primitives.Slot) ([]ethpb.IndexedAtt, []ethpb.AttSlashing, error) {
|
||||
attestations := make([]ethpb.IndexedAtt, 0)
|
||||
slashings := make([]ethpb.AttSlashing, 0)
|
||||
currentEpoch := slots.ToEpoch(slot)
|
||||
|
||||
committeesPerSlot := helpers.SlotCommitteeCount(s.srvConfig.Params.NumValidators)
|
||||
@@ -64,12 +63,23 @@ func (s *Simulator) generateAttestationsForSlot(
|
||||
for idx := i; idx < attEndIdx; idx++ {
|
||||
indices = append(indices, idx)
|
||||
}
|
||||
att := ðpb.IndexedAttestation{
|
||||
AttestingIndices: indices,
|
||||
Data: attData,
|
||||
Signature: params.BeaconConfig().EmptySignature[:],
|
||||
|
||||
var att ethpb.IndexedAtt
|
||||
if ver >= version.Electra {
|
||||
att = ðpb.IndexedAttestationElectra{
|
||||
AttestingIndices: indices,
|
||||
Data: attData,
|
||||
Signature: params.BeaconConfig().EmptySignature[:],
|
||||
}
|
||||
} else {
|
||||
att = ðpb.IndexedAttestation{
|
||||
AttestingIndices: indices,
|
||||
Data: attData,
|
||||
Signature: params.BeaconConfig().EmptySignature[:],
|
||||
}
|
||||
}
|
||||
beaconState, err := s.srvConfig.AttestationStateFetcher.AttestationTargetState(ctx, att.Data.Target)
|
||||
|
||||
beaconState, err := s.srvConfig.AttestationStateFetcher.AttestationTargetState(ctx, att.GetData().Target)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -79,7 +89,12 @@ func (s *Simulator) generateAttestationsForSlot(
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
att.Signature = aggSig.Marshal()
|
||||
|
||||
if ver >= version.Electra {
|
||||
att.(*ethpb.IndexedAttestationElectra).Signature = aggSig.Marshal()
|
||||
} else {
|
||||
att.(*ethpb.IndexedAttestation).Signature = aggSig.Marshal()
|
||||
}
|
||||
|
||||
attestations = append(attestations, att)
|
||||
if rand.NewGenerator().Float64() < s.srvConfig.Params.AttesterSlashingProbab {
|
||||
@@ -88,29 +103,50 @@ func (s *Simulator) generateAttestationsForSlot(
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
slashableAtt.Signature = aggSig.Marshal()
|
||||
slashedIndices = append(slashedIndices, slashableAtt.AttestingIndices...)
|
||||
|
||||
attDataRoot, err := att.Data.HashTreeRoot()
|
||||
if ver >= version.Electra {
|
||||
slashableAtt.(*ethpb.IndexedAttestationElectra).Signature = aggSig.Marshal()
|
||||
} else {
|
||||
slashableAtt.(*ethpb.IndexedAttestation).Signature = aggSig.Marshal()
|
||||
}
|
||||
|
||||
slashedIndices = append(slashedIndices, slashableAtt.GetAttestingIndices()...)
|
||||
|
||||
attDataRoot, err := att.GetData().HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "cannot compte `att` hash tree root")
|
||||
}
|
||||
|
||||
slashableAttDataRoot, err := slashableAtt.Data.HashTreeRoot()
|
||||
slashableAttDataRoot, err := slashableAtt.GetData().HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "cannot compte `slashableAtt` hash tree root")
|
||||
}
|
||||
|
||||
slashing := ðpb.AttesterSlashing{
|
||||
Attestation_1: att,
|
||||
Attestation_2: slashableAtt,
|
||||
var slashing ethpb.AttSlashing
|
||||
if ver >= version.Electra {
|
||||
slashing = ðpb.AttesterSlashingElectra{
|
||||
Attestation_1: att.(*ethpb.IndexedAttestationElectra),
|
||||
Attestation_2: slashableAtt.(*ethpb.IndexedAttestationElectra),
|
||||
}
|
||||
} else {
|
||||
slashing = ðpb.AttesterSlashing{
|
||||
Attestation_1: att.(*ethpb.IndexedAttestation),
|
||||
Attestation_2: slashableAtt.(*ethpb.IndexedAttestation),
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure the attestation with the lower data root is the first attestation.
|
||||
if bytes.Compare(attDataRoot[:], slashableAttDataRoot[:]) > 0 {
|
||||
slashing = ðpb.AttesterSlashing{
|
||||
Attestation_1: slashableAtt,
|
||||
Attestation_2: att,
|
||||
if ver >= version.Electra {
|
||||
slashing = ðpb.AttesterSlashingElectra{
|
||||
Attestation_1: slashableAtt.(*ethpb.IndexedAttestationElectra),
|
||||
Attestation_2: att.(*ethpb.IndexedAttestationElectra),
|
||||
}
|
||||
} else {
|
||||
slashing = ðpb.AttesterSlashing{
|
||||
Attestation_1: slashableAtt.(*ethpb.IndexedAttestation),
|
||||
Attestation_2: att.(*ethpb.IndexedAttestation),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,46 +167,55 @@ func (s *Simulator) generateAttestationsForSlot(
|
||||
}
|
||||
|
||||
func (s *Simulator) aggregateSigForAttestation(
|
||||
beaconState state.ReadOnlyBeaconState, att *ethpb.IndexedAttestation,
|
||||
beaconState state.ReadOnlyBeaconState, att ethpb.IndexedAtt,
|
||||
) (bls.Signature, error) {
|
||||
domain, err := signing.Domain(
|
||||
beaconState.Fork(),
|
||||
att.Data.Target.Epoch,
|
||||
att.GetData().Target.Epoch,
|
||||
params.BeaconConfig().DomainBeaconAttester,
|
||||
beaconState.GenesisValidatorsRoot(),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
signingRoot, err := signing.ComputeSigningRoot(att.Data, domain)
|
||||
signingRoot, err := signing.ComputeSigningRoot(att.GetData(), domain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sigs := make([]bls.Signature, len(att.AttestingIndices))
|
||||
for i, validatorIndex := range att.AttestingIndices {
|
||||
sigs := make([]bls.Signature, len(att.GetAttestingIndices()))
|
||||
for i, validatorIndex := range att.GetAttestingIndices() {
|
||||
privKey := s.srvConfig.PrivateKeysByValidatorIndex[primitives.ValidatorIndex(validatorIndex)]
|
||||
sigs[i] = privKey.Sign(signingRoot[:])
|
||||
}
|
||||
return bls.AggregateSignatures(sigs), nil
|
||||
}
|
||||
|
||||
func makeSlashableFromAtt(att *ethpb.IndexedAttestation, indices []uint64) *ethpb.IndexedAttestation {
|
||||
if att.Data.Source.Epoch <= 2 {
|
||||
func makeSlashableFromAtt(att ethpb.IndexedAtt, indices []uint64) ethpb.IndexedAtt {
|
||||
if att.GetData().Source.Epoch <= 2 {
|
||||
return makeDoubleVoteFromAtt(att, indices)
|
||||
}
|
||||
attData := ðpb.AttestationData{
|
||||
Slot: att.Data.Slot,
|
||||
CommitteeIndex: att.Data.CommitteeIndex,
|
||||
BeaconBlockRoot: att.Data.BeaconBlockRoot,
|
||||
Slot: att.GetData().Slot,
|
||||
CommitteeIndex: att.GetData().CommitteeIndex,
|
||||
BeaconBlockRoot: att.GetData().BeaconBlockRoot,
|
||||
Source: ðpb.Checkpoint{
|
||||
Epoch: att.Data.Source.Epoch - 3,
|
||||
Root: att.Data.Source.Root,
|
||||
Epoch: att.GetData().Source.Epoch - 3,
|
||||
Root: att.GetData().Source.Root,
|
||||
},
|
||||
Target: ðpb.Checkpoint{
|
||||
Epoch: att.Data.Target.Epoch,
|
||||
Root: att.Data.Target.Root,
|
||||
Epoch: att.GetData().Target.Epoch,
|
||||
Root: att.GetData().Target.Root,
|
||||
},
|
||||
}
|
||||
|
||||
if att.Version() >= version.Electra {
|
||||
return ðpb.IndexedAttestationElectra{
|
||||
AttestingIndices: indices,
|
||||
Data: attData,
|
||||
Signature: params.BeaconConfig().EmptySignature[:],
|
||||
}
|
||||
}
|
||||
|
||||
return ðpb.IndexedAttestation{
|
||||
AttestingIndices: indices,
|
||||
Data: attData,
|
||||
@@ -178,20 +223,29 @@ func makeSlashableFromAtt(att *ethpb.IndexedAttestation, indices []uint64) *ethp
|
||||
}
|
||||
}
|
||||
|
||||
func makeDoubleVoteFromAtt(att *ethpb.IndexedAttestation, indices []uint64) *ethpb.IndexedAttestation {
|
||||
func makeDoubleVoteFromAtt(att ethpb.IndexedAtt, indices []uint64) ethpb.IndexedAtt {
|
||||
attData := ðpb.AttestationData{
|
||||
Slot: att.Data.Slot,
|
||||
CommitteeIndex: att.Data.CommitteeIndex,
|
||||
Slot: att.GetData().Slot,
|
||||
CommitteeIndex: att.GetData().CommitteeIndex,
|
||||
BeaconBlockRoot: bytesutil.PadTo([]byte("slash me"), 32),
|
||||
Source: ðpb.Checkpoint{
|
||||
Epoch: att.Data.Source.Epoch,
|
||||
Root: att.Data.Source.Root,
|
||||
Epoch: att.GetData().Source.Epoch,
|
||||
Root: att.GetData().Source.Root,
|
||||
},
|
||||
Target: ðpb.Checkpoint{
|
||||
Epoch: att.Data.Target.Epoch,
|
||||
Root: att.Data.Target.Root,
|
||||
Epoch: att.GetData().Target.Epoch,
|
||||
Root: att.GetData().Target.Root,
|
||||
},
|
||||
}
|
||||
|
||||
if att.Version() >= version.Electra {
|
||||
return ðpb.IndexedAttestationElectra{
|
||||
AttestingIndices: indices,
|
||||
Data: attData,
|
||||
Signature: params.BeaconConfig().EmptySignature[:],
|
||||
}
|
||||
}
|
||||
|
||||
return ðpb.IndexedAttestation{
|
||||
AttestingIndices: indices,
|
||||
Data: attData,
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/prysmaticlabs/prysm/v5/config/params"
|
||||
"github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1/slashings"
|
||||
"github.com/prysmaticlabs/prysm/v5/runtime/version"
|
||||
"github.com/prysmaticlabs/prysm/v5/testing/require"
|
||||
)
|
||||
|
||||
@@ -20,14 +21,18 @@ func TestGenerateAttestationsForSlot_Slashing(t *testing.T) {
|
||||
}
|
||||
srv := setupService(t, simParams)
|
||||
|
||||
epoch3Atts, _, err := srv.generateAttestationsForSlot(ctx, params.BeaconConfig().SlotsPerEpoch*3)
|
||||
require.NoError(t, err)
|
||||
epoch4Atts, _, err := srv.generateAttestationsForSlot(ctx, params.BeaconConfig().SlotsPerEpoch*4)
|
||||
require.NoError(t, err)
|
||||
for i := 0; i < len(epoch3Atts); i += 2 {
|
||||
goodAtt := epoch3Atts[i]
|
||||
surroundAtt := epoch4Atts[i+1]
|
||||
require.Equal(t, true, slashings.IsSurround(surroundAtt, goodAtt))
|
||||
for _, v := range []int{version.Phase0, version.Electra} {
|
||||
t.Run(version.String(v), func(t *testing.T) {
|
||||
epoch3Atts, _, err := srv.generateAttestationsForSlot(ctx, v, params.BeaconConfig().SlotsPerEpoch*3)
|
||||
require.NoError(t, err)
|
||||
epoch4Atts, _, err := srv.generateAttestationsForSlot(ctx, v, params.BeaconConfig().SlotsPerEpoch*4)
|
||||
require.NoError(t, err)
|
||||
for i := 0; i < len(epoch3Atts); i += 2 {
|
||||
goodAtt := epoch3Atts[i]
|
||||
surroundAtt := epoch4Atts[i+1]
|
||||
require.Equal(t, true, slashings.IsSurround(surroundAtt, goodAtt))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,24 +46,29 @@ func TestGenerateAttestationsForSlot_CorrectIndices(t *testing.T) {
|
||||
AttesterSlashingProbab: 0,
|
||||
}
|
||||
srv := setupService(t, simParams)
|
||||
slot0Atts, _, err := srv.generateAttestationsForSlot(ctx, 0)
|
||||
require.NoError(t, err)
|
||||
slot1Atts, _, err := srv.generateAttestationsForSlot(ctx, 1)
|
||||
require.NoError(t, err)
|
||||
slot2Atts, _, err := srv.generateAttestationsForSlot(ctx, 2)
|
||||
require.NoError(t, err)
|
||||
var validatorIndices []uint64
|
||||
for _, att := range append(slot0Atts, slot1Atts...) {
|
||||
validatorIndices = append(validatorIndices, att.AttestingIndices...)
|
||||
}
|
||||
for _, att := range slot2Atts {
|
||||
validatorIndices = append(validatorIndices, att.AttestingIndices...)
|
||||
}
|
||||
|
||||
// Making sure indices are one after the other for attestations.
|
||||
var validatorIndex uint64
|
||||
for _, ii := range validatorIndices {
|
||||
require.Equal(t, validatorIndex, ii)
|
||||
validatorIndex++
|
||||
for _, v := range []int{version.Phase0, version.Electra} {
|
||||
t.Run(version.String(v), func(t *testing.T) {
|
||||
slot0Atts, _, err := srv.generateAttestationsForSlot(ctx, v, 0)
|
||||
require.NoError(t, err)
|
||||
slot1Atts, _, err := srv.generateAttestationsForSlot(ctx, v, 1)
|
||||
require.NoError(t, err)
|
||||
slot2Atts, _, err := srv.generateAttestationsForSlot(ctx, v, 2)
|
||||
require.NoError(t, err)
|
||||
var validatorIndices []uint64
|
||||
for _, att := range append(slot0Atts, slot1Atts...) {
|
||||
validatorIndices = append(validatorIndices, att.GetAttestingIndices()...)
|
||||
}
|
||||
for _, att := range slot2Atts {
|
||||
validatorIndices = append(validatorIndices, att.GetAttestingIndices()...)
|
||||
}
|
||||
|
||||
// Making sure indices are one after the other for attestations.
|
||||
var validatorIndex uint64
|
||||
for _, ii := range validatorIndices {
|
||||
require.Equal(t, validatorIndex, ii)
|
||||
validatorIndex++
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/v5/crypto/bls"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v5/runtime/version"
|
||||
"github.com/prysmaticlabs/prysm/v5/time/slots"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
@@ -62,7 +63,7 @@ type Simulator struct {
|
||||
sentAttSlashingFeed *event.Feed
|
||||
sentBlockSlashingFeed *event.Feed
|
||||
sentProposerSlashings map[[32]byte]*ethpb.ProposerSlashing
|
||||
sentAttesterSlashings map[[32]byte]*ethpb.AttesterSlashing
|
||||
sentAttesterSlashings map[[32]byte]ethpb.AttSlashing
|
||||
genesisTime time.Time
|
||||
}
|
||||
|
||||
@@ -111,7 +112,7 @@ func New(ctx context.Context, srvConfig *ServiceConfig) (*Simulator, error) {
|
||||
sentAttSlashingFeed: sentAttSlashingFeed,
|
||||
sentBlockSlashingFeed: sentBlockSlashingFeed,
|
||||
sentProposerSlashings: make(map[[32]byte]*ethpb.ProposerSlashing),
|
||||
sentAttesterSlashings: make(map[[32]byte]*ethpb.AttesterSlashing),
|
||||
sentAttesterSlashings: make(map[[32]byte]ethpb.AttSlashing),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -206,7 +207,7 @@ func (s *Simulator) simulateBlocksAndAttestations(ctx context.Context) {
|
||||
s.beaconBlocksFeed.Send(bb)
|
||||
}
|
||||
|
||||
atts, attSlashings, err := s.generateAttestationsForSlot(ctx, slot)
|
||||
atts, attSlashings, err := s.generateAttestationsForSlot(ctx, version.Phase0, slot)
|
||||
if err != nil {
|
||||
log.WithError(err).Fatal("Could not generate attestations for slot")
|
||||
}
|
||||
@@ -271,20 +272,20 @@ func (s *Simulator) verifySlashingsWereDetected(ctx context.Context) {
|
||||
for slashingRoot, slashing := range s.sentAttesterSlashings {
|
||||
if _, ok := detectedAttesterSlashings[slashingRoot]; !ok {
|
||||
log.WithFields(logrus.Fields{
|
||||
"targetEpoch": slashing.Attestation_1.Data.Target.Epoch,
|
||||
"prevTargetEpoch": slashing.Attestation_2.Data.Target.Epoch,
|
||||
"sourceEpoch": slashing.Attestation_1.Data.Source.Epoch,
|
||||
"prevSourceEpoch": slashing.Attestation_2.Data.Source.Epoch,
|
||||
"prevBeaconBlockRoot": fmt.Sprintf("%#x", slashing.Attestation_1.Data.BeaconBlockRoot),
|
||||
"newBeaconBlockRoot": fmt.Sprintf("%#x", slashing.Attestation_2.Data.BeaconBlockRoot),
|
||||
"targetEpoch": slashing.FirstAttestation().GetData().Target.Epoch,
|
||||
"prevTargetEpoch": slashing.SecondAttestation().GetData().Target.Epoch,
|
||||
"sourceEpoch": slashing.FirstAttestation().GetData().Source.Epoch,
|
||||
"prevSourceEpoch": slashing.SecondAttestation().GetData().Source.Epoch,
|
||||
"prevBeaconBlockRoot": fmt.Sprintf("%#x", slashing.FirstAttestation().GetData().BeaconBlockRoot),
|
||||
"newBeaconBlockRoot": fmt.Sprintf("%#x", slashing.SecondAttestation().GetData().BeaconBlockRoot),
|
||||
}).Errorf("Did not detect simulated attester slashing")
|
||||
continue
|
||||
}
|
||||
log.WithFields(logrus.Fields{
|
||||
"targetEpoch": slashing.Attestation_1.Data.Target.Epoch,
|
||||
"prevTargetEpoch": slashing.Attestation_2.Data.Target.Epoch,
|
||||
"sourceEpoch": slashing.Attestation_1.Data.Source.Epoch,
|
||||
"prevSourceEpoch": slashing.Attestation_2.Data.Source.Epoch,
|
||||
"targetEpoch": slashing.FirstAttestation().GetData().Target.Epoch,
|
||||
"prevTargetEpoch": slashing.SecondAttestation().GetData().Target.Epoch,
|
||||
"sourceEpoch": slashing.FirstAttestation().GetData().Source.Epoch,
|
||||
"prevSourceEpoch": slashing.SecondAttestation().GetData().Source.Epoch,
|
||||
}).Info("Correctly detected simulated attester slashing")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user