Skip to content

Commit

Permalink
update chunks and detection
Browse files Browse the repository at this point in the history
  • Loading branch information
rkapka committed Jan 10, 2025
1 parent 7325d9f commit 672f6c8
Show file tree
Hide file tree
Showing 8 changed files with 414 additions and 211 deletions.
1 change: 1 addition & 0 deletions beacon-chain/slasher/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -84,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",
Expand Down
72 changes: 42 additions & 30 deletions beacon-chain/slasher/chunks.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,18 +233,8 @@ 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: &ethpb.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 {
// Both attestations should have the same type. If not, we convert both to Electra attestations.
if existingAttWrapper.IndexedAttestation.Version() != incomingAttWrapper.IndexedAttestation.Version() {
existingAttWrapper = &slashertypes.IndexedAttestationWrapper{
IndexedAttestation: &ethpb.IndexedAttestationElectra{
AttestingIndices: existingAttWrapper.IndexedAttestation.GetAttestingIndices(),
Expand All @@ -253,17 +243,33 @@ func (m *MinSpanChunksSlice) CheckSlashable(
},
DataRoot: existingAttWrapper.DataRoot,
}
incomingAttWrapper = &slashertypes.IndexedAttestationWrapper{
IndexedAttestation: &ethpb.IndexedAttestationElectra{
AttestingIndices: incomingAttWrapper.IndexedAttestation.GetAttestingIndices(),
Data: incomingAttWrapper.IndexedAttestation.GetData(),
Signature: incomingAttWrapper.IndexedAttestation.GetSignature(),
},
DataRoot: incomingAttWrapper.DataRoot,
}
}

postElectra := incomingAttWrapper.IndexedAttestation.Version() >= version.Electra
postElectra := existingAttWrapper.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)", &ethpb.IndexedAttestationElectra{}, existing)
return nil, fmt.Errorf(
"existing attestation has wrong type (expected %T, got %T)",
&ethpb.IndexedAttestationElectra{},
existingAttWrapper.IndexedAttestation,
)
}
incoming, ok := incomingAttWrapper.IndexedAttestation.(*ethpb.IndexedAttestationElectra)
if !ok {
return nil, fmt.Errorf("wrong incoming attestation type (expected %T, got %T)", &ethpb.IndexedAttestationElectra{}, incoming)
return nil, fmt.Errorf(
"incoming attestation has wrong type (expected %T, got %T)",
&ethpb.IndexedAttestationElectra{},
incomingAttWrapper.IndexedAttestation,
)
}
slashing := &ethpb.AttesterSlashingElectra{
Attestation_1: existing,
Expand Down Expand Up @@ -377,18 +383,8 @@ 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: &ethpb.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 {
// Both attestations should have the same type. If not, we convert both to Electra attestations.
if existingAttWrapper.IndexedAttestation.Version() != incomingAttWrapper.IndexedAttestation.Version() {
existingAttWrapper = &slashertypes.IndexedAttestationWrapper{
IndexedAttestation: &ethpb.IndexedAttestationElectra{
AttestingIndices: existingAttWrapper.IndexedAttestation.GetAttestingIndices(),
Expand All @@ -397,17 +393,33 @@ func (m *MaxSpanChunksSlice) CheckSlashable(
},
DataRoot: existingAttWrapper.DataRoot,
}
incomingAttWrapper = &slashertypes.IndexedAttestationWrapper{
IndexedAttestation: &ethpb.IndexedAttestationElectra{
AttestingIndices: incomingAttWrapper.IndexedAttestation.GetAttestingIndices(),
Data: incomingAttWrapper.IndexedAttestation.GetData(),
Signature: incomingAttWrapper.IndexedAttestation.GetSignature(),
},
DataRoot: incomingAttWrapper.DataRoot,
}
}

postElectra := incomingAttWrapper.IndexedAttestation.Version() >= version.Electra
postElectra := existingAttWrapper.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)", &ethpb.IndexedAttestationElectra{}, existing)
return nil, fmt.Errorf(
"existing attestation has wrong type (expected %T, got %T)",
&ethpb.IndexedAttestationElectra{},
existingAttWrapper.IndexedAttestation,
)
}
incoming, ok := incomingAttWrapper.IndexedAttestation.(*ethpb.IndexedAttestationElectra)
if !ok {
return nil, fmt.Errorf("wrong incoming attestation type (expected %T, got %T)", &ethpb.IndexedAttestationElectra{}, incoming)
return nil, fmt.Errorf(
"incoming attestation has wrong type (expected %T, got %T)",
&ethpb.IndexedAttestationElectra{},
incomingAttWrapper.IndexedAttestation,
)
}
slashing := &ethpb.AttesterSlashingElectra{
Attestation_1: existing,
Expand Down
103 changes: 95 additions & 8 deletions beacon-chain/slasher/chunks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,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/prysmaticlabs/prysm/v5/testing/assert"
"github.com/prysmaticlabs/prysm/v5/testing/require"
)
Expand Down Expand Up @@ -91,7 +92,7 @@ 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)
att := createAttestationWrapperEmptySig(t, version.Phase0, source, target, nil, nil)

// A faulty chunk should lead to error.
chunk := &MinSpanChunksSlice{
Expand Down Expand Up @@ -126,7 +127,7 @@ func TestMinSpanChunksSlice_CheckSlashable(t *testing.T) {
chunk = EmptyMinSpanChunksSlice(params)
source = primitives.Epoch(1)
target = primitives.Epoch(2)
att = createAttestationWrapperEmptySig(t, source, target, nil, nil)
att = createAttestationWrapperEmptySig(t, version.Phase0, source, target, nil, nil)
chunkIndex := uint64(0)
startEpoch := target
currentEpoch := target
Expand All @@ -137,7 +138,7 @@ func TestMinSpanChunksSlice_CheckSlashable(t *testing.T) {
// 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, source, target, nil, nil)
surroundingVote := createAttestationWrapperEmptySig(t, version.Phase0, source, target, nil, nil)

slashing, err = chunk.CheckSlashable(ctx, slasherDB, validatorIdx, surroundingVote)
require.NoError(t, err)
Expand All @@ -146,7 +147,7 @@ func TestMinSpanChunksSlice_CheckSlashable(t *testing.T) {
// Next up, 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},
Expand All @@ -158,6 +159,49 @@ func TestMinSpanChunksSlice_CheckSlashable(t *testing.T) {
require.NotEqual(t, (*ethpb.AttesterSlashing)(nil), slashing)
}

func TestMinSpanChunksSlice_CheckSlashable_DifferentVersions(t *testing.T) {
ctx := context.Background()
slasherDB := dbtest.SetupSlasherDB(t)
params := &Parameters{
chunkSize: 3,
validatorChunkSize: 2,
historyLength: 3,
}
validatorIdx := primitives.ValidatorIndex(1)
source := primitives.Epoch(1)
target := primitives.Epoch(2)

// We create a vote with Phase0 version.
att := createAttestationWrapperEmptySig(t, version.Phase0, 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)
require.NoError(t, err)

// We create a surrounding vote with Electra version.
source = primitives.Epoch(0)
target = primitives.Epoch(3)
surroundingVote := createAttestationWrapperEmptySig(t, version.Electra, source, target, nil, nil)

// We save the old attestation record, then check if the surrounding vote is indeed slashable.
attData := att.IndexedAttestation.GetData()
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)
require.NoError(t, err)
// The old record should be converted to Electra and the resulting slashing should be an Electra slashing.
require.NotEqual(t, (*ethpb.AttesterSlashingElectra)(nil), slashing)
}

func TestMaxSpanChunksSlice_CheckSlashable(t *testing.T) {
ctx := context.Background()
slasherDB := dbtest.SetupSlasherDB(t)
Expand All @@ -169,7 +213,7 @@ func TestMaxSpanChunksSlice_CheckSlashable(t *testing.T) {
validatorIdx := primitives.ValidatorIndex(1)
source := primitives.Epoch(1)
target := primitives.Epoch(2)
att := createAttestationWrapperEmptySig(t, source, target, nil, nil)
att := createAttestationWrapperEmptySig(t, version.Phase0, source, target, nil, nil)

// A faulty chunk should lead to error.
chunk := &MaxSpanChunksSlice{
Expand Down Expand Up @@ -204,7 +248,7 @@ func TestMaxSpanChunksSlice_CheckSlashable(t *testing.T) {
chunk = EmptyMaxSpanChunksSlice(params)
source = primitives.Epoch(0)
target = primitives.Epoch(3)
att = createAttestationWrapperEmptySig(t, source, target, nil, nil)
att = createAttestationWrapperEmptySig(t, version.Phase0, source, target, nil, nil)
chunkIndex := uint64(0)
startEpoch := source
currentEpoch := target
Expand All @@ -215,7 +259,7 @@ func TestMaxSpanChunksSlice_CheckSlashable(t *testing.T) {
// 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, source, target, nil, nil)
surroundedVote := createAttestationWrapperEmptySig(t, version.Phase0, source, target, nil, nil)

slashing, err = chunk.CheckSlashable(ctx, slasherDB, validatorIdx, surroundedVote)
require.NoError(t, err)
Expand All @@ -226,7 +270,7 @@ func TestMaxSpanChunksSlice_CheckSlashable(t *testing.T) {
attData := att.IndexedAttestation.GetData()
signingRoot := [32]byte{1}
attRecord := createAttestationWrapperEmptySig(
t, attData.Source.Epoch, attData.Target.Epoch, []uint64{uint64(validatorIdx)}, signingRoot[:],
t, version.Phase0, attData.Source.Epoch, attData.Target.Epoch, []uint64{uint64(validatorIdx)}, signingRoot[:],
)
err = slasherDB.SaveAttestationRecordsForValidators(
ctx,
Expand All @@ -239,6 +283,49 @@ func TestMaxSpanChunksSlice_CheckSlashable(t *testing.T) {
require.NotEqual(t, (*ethpb.AttesterSlashing)(nil), slashing)
}

func TestMaxSpanChunksSlice_CheckSlashable_DifferentVersions(t *testing.T) {
ctx := context.Background()
slasherDB := dbtest.SetupSlasherDB(t)
params := &Parameters{
chunkSize: 4,
validatorChunkSize: 2,
historyLength: 4,
}
validatorIdx := primitives.ValidatorIndex(1)
source := primitives.Epoch(0)
target := primitives.Epoch(3)

// We create a vote with Phase0 version.
att := createAttestationWrapperEmptySig(t, version.Phase0, source, target, nil, nil)

// We initialize an empty chunks slice and mark an attestation with (source 0, target 3) as attested.
chunk := EmptyMinSpanChunksSlice(params)
chunkIndex := uint64(0)
startEpoch := target
currentEpoch := target
_, err := chunk.Update(chunkIndex, currentEpoch, validatorIdx, startEpoch, target)
require.NoError(t, err)

// We create a surrounded vote with Electra version.
source = primitives.Epoch(1)
target = primitives.Epoch(2)
surroundedVote := createAttestationWrapperEmptySig(t, version.Electra, source, target, nil, nil)

// We save the old attestation record, then check if the sorrounded vote is indeed slashable.
attData := att.IndexedAttestation.GetData()
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)
require.NoError(t, err)
// The old record should be converted to Electra and the resulting slashing should be an Electra slashing.
require.NotEqual(t, (*ethpb.AttesterSlashingElectra)(nil), slashing)
}

func TestMinSpanChunksSlice_Update_MultipleChunks(t *testing.T) {
// Let's set H = historyLength = 2, meaning a min span
// will hold 2 epochs worth of attesting history. Then we set C = 2 meaning we will
Expand Down
Loading

0 comments on commit 672f6c8

Please sign in to comment.