mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-08 07:03:58 -05:00
* Ran gopls modernize to fix everything go run golang.org/x/tools/gopls/internal/analysis/modernize/cmd/modernize@latest -fix -test ./... * Override rules_go provided dependency for golang.org/x/tools to v0.38.0. To update this, checked out rules_go, then ran `bazel run //go/tools/releaser -- upgrade-dep -mirror=false org_golang_x_tools` and copied the patches. * Fix buildtag violations and ignore buildtag violations in external * Introduce modernize analyzer package. * Add modernize "any" analyzer. * Fix violations of any analyzer * Add modernize "appendclipped" analyzer. * Fix violations of appendclipped * Add modernize "bloop" analyzer. * Add modernize "fmtappendf" analyzer. * Add modernize "forvar" analyzer. * Add modernize "mapsloop" analyzer. * Add modernize "minmax" analyzer. * Fix violations of minmax analyzer * Add modernize "omitzero" analyzer. * Add modernize "rangeint" analyzer. * Fix violations of rangeint. * Add modernize "reflecttypefor" analyzer. * Fix violations of reflecttypefor analyzer. * Add modernize "slicescontains" analyzer. * Add modernize "slicessort" analyzer. * Add modernize "slicesdelete" analyzer. This is disabled by default for now. See https://go.dev/issue/73686. * Add modernize "stringscutprefix" analyzer. * Add modernize "stringsbuilder" analyzer. * Fix violations of stringsbuilder analyzer. * Add modernize "stringsseq" analyzer. * Add modernize "testingcontext" analyzer. * Add modernize "waitgroup" analyzer. * Changelog fragment * gofmt * gazelle * Add modernize "newexpr" analyzer. * Disable newexpr until go1.26 * Add more details in WORKSPACE on how to update the override * @nalepae feedback on min() * gofmt * Fix violations of forvar
909 lines
35 KiB
Go
909 lines
35 KiB
Go
package client
|
|
|
|
import (
|
|
"context"
|
|
"encoding/hex"
|
|
"errors"
|
|
"fmt"
|
|
"reflect"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/OffchainLabs/go-bitfield"
|
|
"github.com/OffchainLabs/prysm/v7/async/event"
|
|
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/signing"
|
|
"github.com/OffchainLabs/prysm/v7/config/features"
|
|
fieldparams "github.com/OffchainLabs/prysm/v7/config/fieldparams"
|
|
"github.com/OffchainLabs/prysm/v7/config/params"
|
|
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
|
|
"github.com/OffchainLabs/prysm/v7/encoding/bytesutil"
|
|
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
|
|
validatorpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1/validator-client"
|
|
"github.com/OffchainLabs/prysm/v7/testing/assert"
|
|
"github.com/OffchainLabs/prysm/v7/testing/require"
|
|
"github.com/OffchainLabs/prysm/v7/testing/util"
|
|
logTest "github.com/sirupsen/logrus/hooks/test"
|
|
"go.uber.org/mock/gomock"
|
|
"gopkg.in/d4l3k/messagediff.v1"
|
|
)
|
|
|
|
func TestRequestAttestation_ValidatorDutiesRequestFailure(t *testing.T) {
|
|
for _, isSlashingProtectionMinimal := range [...]bool{false, true} {
|
|
t.Run(fmt.Sprintf("SlashingProtectionMinimal:%v", isSlashingProtectionMinimal), func(t *testing.T) {
|
|
hook := logTest.NewGlobal()
|
|
validator, _, validatorKey, finish := setup(t, isSlashingProtectionMinimal)
|
|
validator.duties = ðpb.ValidatorDutiesContainer{CurrentEpochDuties: []*ethpb.ValidatorDuty{}}
|
|
defer finish()
|
|
|
|
var pubKey [fieldparams.BLSPubkeyLength]byte
|
|
copy(pubKey[:], validatorKey.PublicKey().Marshal())
|
|
validator.SubmitAttestation(t.Context(), 30, pubKey)
|
|
require.LogsContain(t, hook, "Could not fetch validator assignment")
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestAttestToBlockHead_SubmitAttestation_EmptyCommittee(t *testing.T) {
|
|
for _, isSlashingProtectionMinimal := range [...]bool{false, true} {
|
|
t.Run(fmt.Sprintf("SlashingProtectionMinimal:%v", isSlashingProtectionMinimal), func(t *testing.T) {
|
|
hook := logTest.NewGlobal()
|
|
|
|
validator, _, validatorKey, finish := setup(t, isSlashingProtectionMinimal)
|
|
defer finish()
|
|
var pubKey [fieldparams.BLSPubkeyLength]byte
|
|
copy(pubKey[:], validatorKey.PublicKey().Marshal())
|
|
validator.duties = ðpb.ValidatorDutiesContainer{CurrentEpochDuties: []*ethpb.ValidatorDuty{
|
|
{
|
|
PublicKey: validatorKey.PublicKey().Marshal(),
|
|
CommitteeIndex: 0,
|
|
ValidatorIndex: 0,
|
|
}}}
|
|
validator.SubmitAttestation(t.Context(), 0, pubKey)
|
|
require.LogsContain(t, hook, "Empty committee")
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestAttestToBlockHead_SubmitAttestation_RequestFailure(t *testing.T) {
|
|
for _, isSlashingProtectionMinimal := range [...]bool{false, true} {
|
|
t.Run(fmt.Sprintf("SlashingProtectionMinimal:%v", isSlashingProtectionMinimal), func(t *testing.T) {
|
|
hook := logTest.NewGlobal()
|
|
|
|
validator, m, validatorKey, finish := setup(t, isSlashingProtectionMinimal)
|
|
defer finish()
|
|
validator.duties = ðpb.ValidatorDutiesContainer{CurrentEpochDuties: []*ethpb.ValidatorDuty{
|
|
{
|
|
PublicKey: validatorKey.PublicKey().Marshal(),
|
|
CommitteeIndex: 5,
|
|
CommitteeLength: 111,
|
|
ValidatorIndex: 0,
|
|
}}}
|
|
m.validatorClient.EXPECT().AttestationData(
|
|
gomock.Any(), // ctx
|
|
gomock.AssignableToTypeOf(ðpb.AttestationDataRequest{}),
|
|
).Return(ðpb.AttestationData{
|
|
BeaconBlockRoot: make([]byte, fieldparams.RootLength),
|
|
Target: ðpb.Checkpoint{Root: make([]byte, fieldparams.RootLength)},
|
|
Source: ðpb.Checkpoint{Root: make([]byte, fieldparams.RootLength)},
|
|
}, nil)
|
|
m.validatorClient.EXPECT().DomainData(
|
|
gomock.Any(), // ctx
|
|
gomock.Any(), // epoch2
|
|
).Times(2).Return(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil /*err*/)
|
|
m.validatorClient.EXPECT().ProposeAttestation(
|
|
gomock.Any(), // ctx
|
|
gomock.AssignableToTypeOf(ðpb.Attestation{}),
|
|
).Return(nil, errors.New("something went wrong"))
|
|
|
|
var pubKey [fieldparams.BLSPubkeyLength]byte
|
|
copy(pubKey[:], validatorKey.PublicKey().Marshal())
|
|
validator.SubmitAttestation(t.Context(), 30, pubKey)
|
|
require.LogsContain(t, hook, "Could not submit attestation to beacon node")
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSubmitAttestation_ElectraCommitteeIndex(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
electraForkEpoch uint64
|
|
attestationSlot primitives.Slot
|
|
assignedCommitteeIndex primitives.CommitteeIndex
|
|
expectedCommitteeIndex primitives.CommitteeIndex
|
|
isPostElectra bool
|
|
}{
|
|
{
|
|
name: "Pre-Electra uses assigned committee index",
|
|
electraForkEpoch: 10,
|
|
attestationSlot: 300,
|
|
assignedCommitteeIndex: 5,
|
|
expectedCommitteeIndex: 5,
|
|
isPostElectra: false,
|
|
},
|
|
{
|
|
name: "Post-Electra uses committee index 0",
|
|
electraForkEpoch: 1,
|
|
attestationSlot: 32,
|
|
assignedCommitteeIndex: 5,
|
|
expectedCommitteeIndex: 0,
|
|
isPostElectra: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
for _, isSlashingProtectionMinimal := range [...]bool{false, true} {
|
|
t.Run(fmt.Sprintf("%s (SlashingProtectionMinimal:%v)", tt.name, isSlashingProtectionMinimal), func(t *testing.T) {
|
|
params.SetupTestConfigCleanup(t)
|
|
cfg := params.BeaconConfig().Copy()
|
|
cfg.ElectraForkEpoch = primitives.Epoch(tt.electraForkEpoch)
|
|
params.OverrideBeaconConfig(cfg)
|
|
|
|
validator, m, validatorKey, finish := setup(t, isSlashingProtectionMinimal)
|
|
defer finish()
|
|
validatorIndex := primitives.ValidatorIndex(7)
|
|
committee := []primitives.ValidatorIndex{0, 3, 4, 2, validatorIndex, 6, 8, 9, 10}
|
|
var pubKey [fieldparams.BLSPubkeyLength]byte
|
|
copy(pubKey[:], validatorKey.PublicKey().Marshal())
|
|
validator.duties = ðpb.ValidatorDutiesContainer{CurrentEpochDuties: []*ethpb.ValidatorDuty{
|
|
{
|
|
PublicKey: validatorKey.PublicKey().Marshal(),
|
|
CommitteeIndex: tt.assignedCommitteeIndex,
|
|
CommitteeLength: uint64(len(committee)),
|
|
ValidatorIndex: validatorIndex,
|
|
},
|
|
}}
|
|
|
|
var capturedRequest *ethpb.AttestationDataRequest
|
|
// Capture the actual request to verify committee index
|
|
m.validatorClient.EXPECT().AttestationData(
|
|
gomock.Any(), // ctx
|
|
gomock.AssignableToTypeOf(ðpb.AttestationDataRequest{}),
|
|
).Do(func(_ context.Context, req *ethpb.AttestationDataRequest) {
|
|
capturedRequest = req
|
|
}).Return(ðpb.AttestationData{
|
|
BeaconBlockRoot: make([]byte, fieldparams.RootLength),
|
|
Target: ðpb.Checkpoint{Root: make([]byte, fieldparams.RootLength)},
|
|
Source: ðpb.Checkpoint{Root: make([]byte, fieldparams.RootLength)},
|
|
}, nil)
|
|
|
|
m.validatorClient.EXPECT().DomainData(
|
|
gomock.Any(), // ctx
|
|
gomock.Any(), // epoch
|
|
).Times(2).Return(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil)
|
|
|
|
if tt.isPostElectra {
|
|
m.validatorClient.EXPECT().ProposeAttestationElectra(
|
|
gomock.Any(), // ctx
|
|
gomock.AssignableToTypeOf(ðpb.SingleAttestation{}),
|
|
).Return(ðpb.AttestResponse{}, nil)
|
|
} else {
|
|
m.validatorClient.EXPECT().ProposeAttestation(
|
|
gomock.Any(), // ctx
|
|
gomock.AssignableToTypeOf(ðpb.Attestation{}),
|
|
).Return(ðpb.AttestResponse{}, nil)
|
|
}
|
|
|
|
validator.SubmitAttestation(t.Context(), tt.attestationSlot, pubKey)
|
|
|
|
// Verify the committee index in the request
|
|
require.NotNil(t, capturedRequest, "AttestationDataRequest should have been called")
|
|
assert.Equal(t, tt.expectedCommitteeIndex, capturedRequest.CommitteeIndex,
|
|
"Committee index mismatch: expected %d, got %d", tt.expectedCommitteeIndex, capturedRequest.CommitteeIndex)
|
|
assert.Equal(t, tt.attestationSlot, capturedRequest.Slot,
|
|
"Slot should match the provided slot")
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestAttestToBlockHead_AttestsCorrectly(t *testing.T) {
|
|
for _, isSlashingProtectionMinimal := range [...]bool{false, true} {
|
|
t.Run(fmt.Sprintf("Phase 0 (SlashingProtectionMinimal:%v)", isSlashingProtectionMinimal), func(t *testing.T) {
|
|
validator, m, validatorKey, finish := setup(t, isSlashingProtectionMinimal)
|
|
defer finish()
|
|
hook := logTest.NewGlobal()
|
|
validatorIndex := primitives.ValidatorIndex(7)
|
|
committee := []primitives.ValidatorIndex{0, 3, 4, 2, validatorIndex, 6, 8, 9, 10}
|
|
var pubKey [fieldparams.BLSPubkeyLength]byte
|
|
copy(pubKey[:], validatorKey.PublicKey().Marshal())
|
|
validator.duties = ðpb.ValidatorDutiesContainer{CurrentEpochDuties: []*ethpb.ValidatorDuty{
|
|
{
|
|
PublicKey: validatorKey.PublicKey().Marshal(),
|
|
CommitteeIndex: 5,
|
|
CommitteeLength: uint64(len(committee)),
|
|
ValidatorCommitteeIndex: 4,
|
|
ValidatorIndex: validatorIndex,
|
|
},
|
|
}}
|
|
|
|
beaconBlockRoot := bytesutil.ToBytes32([]byte("A"))
|
|
targetRoot := bytesutil.ToBytes32([]byte("B"))
|
|
sourceRoot := bytesutil.ToBytes32([]byte("C"))
|
|
m.validatorClient.EXPECT().AttestationData(
|
|
gomock.Any(), // ctx
|
|
gomock.AssignableToTypeOf(ðpb.AttestationDataRequest{}),
|
|
).Return(ðpb.AttestationData{
|
|
BeaconBlockRoot: beaconBlockRoot[:],
|
|
Target: ðpb.Checkpoint{Root: targetRoot[:]},
|
|
Source: ðpb.Checkpoint{Root: sourceRoot[:], Epoch: 3},
|
|
}, nil)
|
|
|
|
m.validatorClient.EXPECT().DomainData(
|
|
gomock.Any(), // ctx
|
|
gomock.Any(), // epoch
|
|
).Times(2).Return(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil /*err*/)
|
|
|
|
var generatedAttestation *ethpb.Attestation
|
|
m.validatorClient.EXPECT().ProposeAttestation(
|
|
gomock.Any(), // ctx
|
|
gomock.AssignableToTypeOf(ðpb.Attestation{}),
|
|
).Do(func(_ context.Context, att *ethpb.Attestation) {
|
|
generatedAttestation = att
|
|
}).Return(ðpb.AttestResponse{}, nil /* error */)
|
|
|
|
validator.SubmitAttestation(t.Context(), 30, pubKey)
|
|
|
|
aggregationBitfield := bitfield.NewBitlist(uint64(len(committee)))
|
|
aggregationBitfield.SetBitAt(4, true)
|
|
expectedAttestation := ðpb.Attestation{
|
|
Data: ðpb.AttestationData{
|
|
BeaconBlockRoot: beaconBlockRoot[:],
|
|
Target: ðpb.Checkpoint{Root: targetRoot[:]},
|
|
Source: ðpb.Checkpoint{Root: sourceRoot[:], Epoch: 3},
|
|
},
|
|
AggregationBits: aggregationBitfield,
|
|
Signature: make([]byte, 96),
|
|
}
|
|
|
|
root, err := signing.ComputeSigningRoot(expectedAttestation.Data, make([]byte, 32))
|
|
require.NoError(t, err)
|
|
|
|
sig, err := validator.km.Sign(t.Context(), &validatorpb.SignRequest{
|
|
PublicKey: validatorKey.PublicKey().Marshal(),
|
|
SigningRoot: root[:],
|
|
})
|
|
require.NoError(t, err)
|
|
expectedAttestation.Signature = sig.Marshal()
|
|
if !reflect.DeepEqual(generatedAttestation, expectedAttestation) {
|
|
t.Errorf("Incorrectly attested head, wanted %v, received %v", expectedAttestation, generatedAttestation)
|
|
diff, _ := messagediff.PrettyDiff(expectedAttestation, generatedAttestation)
|
|
t.Log(diff)
|
|
}
|
|
require.LogsDoNotContain(t, hook, "Could not")
|
|
})
|
|
}
|
|
for _, isSlashingProtectionMinimal := range [...]bool{false, true} {
|
|
t.Run(fmt.Sprintf("Electra (SlashingProtectionMinimal:%v)", isSlashingProtectionMinimal), func(t *testing.T) {
|
|
electraForkEpoch := uint64(1)
|
|
params.SetupTestConfigCleanup(t)
|
|
cfg := params.BeaconConfig().Copy()
|
|
cfg.ElectraForkEpoch = primitives.Epoch(electraForkEpoch)
|
|
params.OverrideBeaconConfig(cfg)
|
|
|
|
validator, m, validatorKey, finish := setup(t, isSlashingProtectionMinimal)
|
|
defer finish()
|
|
hook := logTest.NewGlobal()
|
|
validatorIndex := primitives.ValidatorIndex(7)
|
|
committee := []primitives.ValidatorIndex{0, 3, 4, 2, validatorIndex, 6, 8, 9, 10}
|
|
var pubKey [fieldparams.BLSPubkeyLength]byte
|
|
copy(pubKey[:], validatorKey.PublicKey().Marshal())
|
|
validator.duties = ðpb.ValidatorDutiesContainer{CurrentEpochDuties: []*ethpb.ValidatorDuty{
|
|
{
|
|
PublicKey: validatorKey.PublicKey().Marshal(),
|
|
CommitteeIndex: 5,
|
|
CommitteeLength: uint64(len(committee)),
|
|
ValidatorIndex: validatorIndex,
|
|
},
|
|
}}
|
|
|
|
beaconBlockRoot := bytesutil.ToBytes32([]byte("A"))
|
|
targetRoot := bytesutil.ToBytes32([]byte("B"))
|
|
sourceRoot := bytesutil.ToBytes32([]byte("C"))
|
|
m.validatorClient.EXPECT().AttestationData(
|
|
gomock.Any(), // ctx
|
|
gomock.AssignableToTypeOf(ðpb.AttestationDataRequest{}),
|
|
).Return(ðpb.AttestationData{
|
|
BeaconBlockRoot: beaconBlockRoot[:],
|
|
Target: ðpb.Checkpoint{Root: targetRoot[:]},
|
|
Source: ðpb.Checkpoint{Root: sourceRoot[:], Epoch: 3},
|
|
}, nil)
|
|
|
|
m.validatorClient.EXPECT().DomainData(
|
|
gomock.Any(), // ctx
|
|
gomock.Any(), // epoch
|
|
).Times(2).Return(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil /*err*/)
|
|
|
|
var generatedAttestation *ethpb.SingleAttestation
|
|
m.validatorClient.EXPECT().ProposeAttestationElectra(
|
|
gomock.Any(), // ctx
|
|
gomock.AssignableToTypeOf(ðpb.SingleAttestation{}),
|
|
).Do(func(_ context.Context, att *ethpb.SingleAttestation) {
|
|
generatedAttestation = att
|
|
}).Return(ðpb.AttestResponse{}, nil /* error */)
|
|
|
|
validator.SubmitAttestation(t.Context(), params.BeaconConfig().SlotsPerEpoch.Mul(electraForkEpoch), pubKey)
|
|
|
|
aggregationBitfield := bitfield.NewBitlist(uint64(len(committee)))
|
|
aggregationBitfield.SetBitAt(4, true)
|
|
committeeBits := primitives.NewAttestationCommitteeBits()
|
|
committeeBits.SetBitAt(5, true)
|
|
expectedAttestation := ðpb.SingleAttestation{
|
|
Data: ðpb.AttestationData{
|
|
BeaconBlockRoot: beaconBlockRoot[:],
|
|
Target: ðpb.Checkpoint{Root: targetRoot[:]},
|
|
Source: ðpb.Checkpoint{Root: sourceRoot[:], Epoch: 3},
|
|
},
|
|
AttesterIndex: validatorIndex,
|
|
CommitteeId: 5,
|
|
Signature: make([]byte, 96),
|
|
}
|
|
|
|
root, err := signing.ComputeSigningRoot(expectedAttestation.Data, make([]byte, 32))
|
|
require.NoError(t, err)
|
|
|
|
sig, err := validator.km.Sign(t.Context(), &validatorpb.SignRequest{
|
|
PublicKey: validatorKey.PublicKey().Marshal(),
|
|
SigningRoot: root[:],
|
|
})
|
|
require.NoError(t, err)
|
|
expectedAttestation.Signature = sig.Marshal()
|
|
if !reflect.DeepEqual(generatedAttestation, expectedAttestation) {
|
|
t.Errorf("Incorrectly attested head, wanted %v, received %v", expectedAttestation, generatedAttestation)
|
|
diff, _ := messagediff.PrettyDiff(expectedAttestation, generatedAttestation)
|
|
t.Log(diff)
|
|
}
|
|
require.LogsDoNotContain(t, hook, "Could not")
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestAttestToBlockHead_BlocksDoubleAtt(t *testing.T) {
|
|
for _, isSlashingProtectionMinimal := range [...]bool{false, true} {
|
|
t.Run(fmt.Sprintf("SlashingProtectionMinimal:%v", isSlashingProtectionMinimal), func(t *testing.T) {
|
|
hook := logTest.NewGlobal()
|
|
validator, m, validatorKey, finish := setup(t, isSlashingProtectionMinimal)
|
|
defer finish()
|
|
validatorIndex := primitives.ValidatorIndex(7)
|
|
committee := []primitives.ValidatorIndex{0, 3, 4, 2, validatorIndex, 6, 8, 9, 10}
|
|
var pubKey [fieldparams.BLSPubkeyLength]byte
|
|
copy(pubKey[:], validatorKey.PublicKey().Marshal())
|
|
validator.duties = ðpb.ValidatorDutiesContainer{CurrentEpochDuties: []*ethpb.ValidatorDuty{
|
|
{
|
|
PublicKey: validatorKey.PublicKey().Marshal(),
|
|
CommitteeIndex: 5,
|
|
CommitteeLength: uint64(len(committee)),
|
|
ValidatorIndex: validatorIndex,
|
|
},
|
|
}}
|
|
beaconBlockRoot := bytesutil.ToBytes32([]byte("A"))
|
|
targetRoot := bytesutil.ToBytes32([]byte("B"))
|
|
sourceRoot := bytesutil.ToBytes32([]byte("C"))
|
|
beaconBlockRoot2 := bytesutil.ToBytes32([]byte("D"))
|
|
|
|
m.validatorClient.EXPECT().AttestationData(
|
|
gomock.Any(), // ctx
|
|
gomock.AssignableToTypeOf(ðpb.AttestationDataRequest{}),
|
|
).Return(ðpb.AttestationData{
|
|
BeaconBlockRoot: beaconBlockRoot[:],
|
|
Target: ðpb.Checkpoint{Root: targetRoot[:], Epoch: 4},
|
|
Source: ðpb.Checkpoint{Root: sourceRoot[:], Epoch: 3},
|
|
}, nil)
|
|
m.validatorClient.EXPECT().AttestationData(
|
|
gomock.Any(), // ctx
|
|
gomock.AssignableToTypeOf(ðpb.AttestationDataRequest{}),
|
|
).Return(ðpb.AttestationData{
|
|
BeaconBlockRoot: beaconBlockRoot2[:],
|
|
Target: ðpb.Checkpoint{Root: targetRoot[:], Epoch: 4},
|
|
Source: ðpb.Checkpoint{Root: sourceRoot[:], Epoch: 3},
|
|
}, nil)
|
|
m.validatorClient.EXPECT().DomainData(
|
|
gomock.Any(), // ctx
|
|
gomock.Any(), // epoch
|
|
).Times(4).Return(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil /*err*/)
|
|
|
|
m.validatorClient.EXPECT().ProposeAttestation(
|
|
gomock.Any(), // ctx
|
|
gomock.AssignableToTypeOf(ðpb.Attestation{}),
|
|
).Return(ðpb.AttestResponse{AttestationDataRoot: make([]byte, 32)}, nil /* error */)
|
|
|
|
validator.SubmitAttestation(t.Context(), 30, pubKey)
|
|
validator.SubmitAttestation(t.Context(), 30, pubKey)
|
|
require.LogsContain(t, hook, "Failed attestation slashing protection")
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestAttestToBlockHead_BlocksSurroundAtt(t *testing.T) {
|
|
for _, isSlashingProtectionMinimal := range [...]bool{false, true} {
|
|
t.Run(fmt.Sprintf("SlashingProtectionMinimal:%v", isSlashingProtectionMinimal), func(t *testing.T) {
|
|
hook := logTest.NewGlobal()
|
|
validator, m, validatorKey, finish := setup(t, isSlashingProtectionMinimal)
|
|
defer finish()
|
|
validatorIndex := primitives.ValidatorIndex(7)
|
|
committee := []primitives.ValidatorIndex{0, 3, 4, 2, validatorIndex, 6, 8, 9, 10}
|
|
var pubKey [fieldparams.BLSPubkeyLength]byte
|
|
copy(pubKey[:], validatorKey.PublicKey().Marshal())
|
|
validator.duties = ðpb.ValidatorDutiesContainer{CurrentEpochDuties: []*ethpb.ValidatorDuty{
|
|
{
|
|
PublicKey: validatorKey.PublicKey().Marshal(),
|
|
CommitteeIndex: 5,
|
|
CommitteeLength: uint64(len(committee)),
|
|
ValidatorIndex: validatorIndex,
|
|
},
|
|
}}
|
|
beaconBlockRoot := bytesutil.ToBytes32([]byte("A"))
|
|
targetRoot := bytesutil.ToBytes32([]byte("B"))
|
|
sourceRoot := bytesutil.ToBytes32([]byte("C"))
|
|
|
|
m.validatorClient.EXPECT().AttestationData(
|
|
gomock.Any(), // ctx
|
|
gomock.AssignableToTypeOf(ðpb.AttestationDataRequest{}),
|
|
).Return(ðpb.AttestationData{
|
|
BeaconBlockRoot: beaconBlockRoot[:],
|
|
Target: ðpb.Checkpoint{Root: targetRoot[:], Epoch: 2},
|
|
Source: ðpb.Checkpoint{Root: sourceRoot[:], Epoch: 1},
|
|
}, nil)
|
|
m.validatorClient.EXPECT().AttestationData(
|
|
gomock.Any(), // ctx
|
|
gomock.AssignableToTypeOf(ðpb.AttestationDataRequest{}),
|
|
).Return(ðpb.AttestationData{
|
|
BeaconBlockRoot: beaconBlockRoot[:],
|
|
Target: ðpb.Checkpoint{Root: targetRoot[:], Epoch: 3},
|
|
Source: ðpb.Checkpoint{Root: sourceRoot[:], Epoch: 0},
|
|
}, nil)
|
|
|
|
m.validatorClient.EXPECT().DomainData(
|
|
gomock.Any(), // ctx
|
|
gomock.Any(), // epoch
|
|
).Times(4).Return(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil /*err*/)
|
|
|
|
m.validatorClient.EXPECT().ProposeAttestation(
|
|
gomock.Any(), // ctx
|
|
gomock.AssignableToTypeOf(ðpb.Attestation{}),
|
|
).Return(ðpb.AttestResponse{}, nil /* error */)
|
|
|
|
validator.SubmitAttestation(t.Context(), 30, pubKey)
|
|
validator.SubmitAttestation(t.Context(), 30, pubKey)
|
|
require.LogsContain(t, hook, "Failed attestation slashing protection")
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestAttestToBlockHead_BlocksSurroundedAtt(t *testing.T) {
|
|
for _, isSlashingProtectionMinimal := range [...]bool{false, true} {
|
|
t.Run(fmt.Sprintf("SlashingProtectionMinimal:%v", isSlashingProtectionMinimal), func(t *testing.T) {
|
|
hook := logTest.NewGlobal()
|
|
validator, m, validatorKey, finish := setup(t, isSlashingProtectionMinimal)
|
|
defer finish()
|
|
validatorIndex := primitives.ValidatorIndex(7)
|
|
var pubKey [fieldparams.BLSPubkeyLength]byte
|
|
copy(pubKey[:], validatorKey.PublicKey().Marshal())
|
|
committee := []primitives.ValidatorIndex{0, 3, 4, 2, validatorIndex, 6, 8, 9, 10}
|
|
validator.duties = ðpb.ValidatorDutiesContainer{CurrentEpochDuties: []*ethpb.ValidatorDuty{
|
|
{
|
|
PublicKey: validatorKey.PublicKey().Marshal(),
|
|
CommitteeIndex: 5,
|
|
CommitteeLength: uint64(len(committee)),
|
|
ValidatorIndex: validatorIndex,
|
|
},
|
|
}}
|
|
beaconBlockRoot := bytesutil.ToBytes32([]byte("A"))
|
|
targetRoot := bytesutil.ToBytes32([]byte("B"))
|
|
sourceRoot := bytesutil.ToBytes32([]byte("C"))
|
|
|
|
m.validatorClient.EXPECT().AttestationData(
|
|
gomock.Any(), // ctx
|
|
gomock.AssignableToTypeOf(ðpb.AttestationDataRequest{}),
|
|
).Return(ðpb.AttestationData{
|
|
BeaconBlockRoot: beaconBlockRoot[:],
|
|
Target: ðpb.Checkpoint{Root: targetRoot[:], Epoch: 3},
|
|
Source: ðpb.Checkpoint{Root: sourceRoot[:], Epoch: 0},
|
|
}, nil)
|
|
|
|
m.validatorClient.EXPECT().DomainData(
|
|
gomock.Any(), // ctx
|
|
gomock.Any(), // epoch
|
|
).Times(4).Return(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil /*err*/)
|
|
|
|
m.validatorClient.EXPECT().ProposeAttestation(
|
|
gomock.Any(), // ctx
|
|
gomock.AssignableToTypeOf(ðpb.Attestation{}),
|
|
).Return(ðpb.AttestResponse{}, nil /* error */)
|
|
|
|
validator.SubmitAttestation(t.Context(), 30, pubKey)
|
|
require.LogsDoNotContain(t, hook, failedAttLocalProtectionErr)
|
|
|
|
m.validatorClient.EXPECT().AttestationData(
|
|
gomock.Any(), // ctx
|
|
gomock.AssignableToTypeOf(ðpb.AttestationDataRequest{}),
|
|
).Return(ðpb.AttestationData{
|
|
BeaconBlockRoot: bytesutil.PadTo([]byte("A"), 32),
|
|
Target: ðpb.Checkpoint{Root: bytesutil.PadTo([]byte("B"), 32), Epoch: 2},
|
|
Source: ðpb.Checkpoint{Root: bytesutil.PadTo([]byte("C"), 32), Epoch: 1},
|
|
}, nil)
|
|
|
|
validator.SubmitAttestation(t.Context(), 30, pubKey)
|
|
require.LogsContain(t, hook, "Failed attestation slashing protection")
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestAttestToBlockHead_DoesNotAttestBeforeDelay(t *testing.T) {
|
|
for _, isSlashingProtectionMinimal := range [...]bool{false, true} {
|
|
t.Run(fmt.Sprintf("SlashingProtectionMinimal:%v", isSlashingProtectionMinimal), func(t *testing.T) {
|
|
validator, m, validatorKey, finish := setup(t, isSlashingProtectionMinimal)
|
|
defer finish()
|
|
|
|
var pubKey [fieldparams.BLSPubkeyLength]byte
|
|
copy(pubKey[:], validatorKey.PublicKey().Marshal())
|
|
validator.genesisTime = time.Now()
|
|
m.validatorClient.EXPECT().Duties(
|
|
gomock.Any(), // ctx
|
|
gomock.AssignableToTypeOf(ðpb.DutiesRequest{}),
|
|
).Times(0)
|
|
|
|
m.validatorClient.EXPECT().AttestationData(
|
|
gomock.Any(), // ctx
|
|
gomock.AssignableToTypeOf(ðpb.AttestationDataRequest{}),
|
|
).Times(0)
|
|
|
|
m.validatorClient.EXPECT().ProposeAttestation(
|
|
gomock.Any(), // ctx
|
|
gomock.AssignableToTypeOf(ðpb.Attestation{}),
|
|
).Return(ðpb.AttestResponse{}, nil /* error */).Times(0)
|
|
|
|
timer := time.NewTimer(1 * time.Second)
|
|
go validator.SubmitAttestation(t.Context(), 0, pubKey)
|
|
<-timer.C
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestAttestToBlockHead_DoesAttestAfterDelay(t *testing.T) {
|
|
for _, isSlashingProtectionMinimal := range [...]bool{false, true} {
|
|
t.Run(fmt.Sprintf("SlashingProtectionMinimal:%v", isSlashingProtectionMinimal), func(t *testing.T) {
|
|
validator, m, validatorKey, finish := setup(t, isSlashingProtectionMinimal)
|
|
defer finish()
|
|
|
|
var wg sync.WaitGroup
|
|
wg.Add(1)
|
|
defer wg.Wait()
|
|
|
|
validator.genesisTime = time.Now()
|
|
validatorIndex := primitives.ValidatorIndex(5)
|
|
committee := []primitives.ValidatorIndex{0, 3, 4, 2, validatorIndex, 6, 8, 9, 10}
|
|
var pubKey [fieldparams.BLSPubkeyLength]byte
|
|
copy(pubKey[:], validatorKey.PublicKey().Marshal())
|
|
validator.duties = ðpb.ValidatorDutiesContainer{CurrentEpochDuties: []*ethpb.ValidatorDuty{
|
|
{
|
|
PublicKey: validatorKey.PublicKey().Marshal(),
|
|
CommitteeIndex: 5,
|
|
CommitteeLength: uint64(len(committee)),
|
|
ValidatorIndex: validatorIndex,
|
|
}}}
|
|
|
|
m.validatorClient.EXPECT().AttestationData(
|
|
gomock.Any(), // ctx
|
|
gomock.AssignableToTypeOf(ðpb.AttestationDataRequest{}),
|
|
).Return(ðpb.AttestationData{
|
|
BeaconBlockRoot: bytesutil.PadTo([]byte("A"), 32),
|
|
Target: ðpb.Checkpoint{Root: bytesutil.PadTo([]byte("B"), 32)},
|
|
Source: ðpb.Checkpoint{Root: bytesutil.PadTo([]byte("C"), 32), Epoch: 3},
|
|
}, nil).Do(func(arg0, arg1 any) {
|
|
wg.Done()
|
|
})
|
|
|
|
m.validatorClient.EXPECT().DomainData(
|
|
gomock.Any(), // ctx
|
|
gomock.Any(), // epoch
|
|
).Times(2).Return(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil /*err*/)
|
|
|
|
m.validatorClient.EXPECT().ProposeAttestation(
|
|
gomock.Any(), // ctx
|
|
gomock.Any(),
|
|
).Return(ðpb.AttestResponse{}, nil).Times(1)
|
|
|
|
validator.SubmitAttestation(t.Context(), 0, pubKey)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestAttestToBlockHead_CorrectBitfieldLength(t *testing.T) {
|
|
for _, isSlashingProtectionMinimal := range [...]bool{false, true} {
|
|
t.Run(fmt.Sprintf("SlashingProtectionMinimal:%v", isSlashingProtectionMinimal), func(t *testing.T) {
|
|
validator, m, validatorKey, finish := setup(t, isSlashingProtectionMinimal)
|
|
defer finish()
|
|
validatorIndex := primitives.ValidatorIndex(2)
|
|
committee := []primitives.ValidatorIndex{0, 3, 4, 2, validatorIndex, 6, 8, 9, 10}
|
|
var pubKey [fieldparams.BLSPubkeyLength]byte
|
|
copy(pubKey[:], validatorKey.PublicKey().Marshal())
|
|
validator.duties = ðpb.ValidatorDutiesContainer{CurrentEpochDuties: []*ethpb.ValidatorDuty{
|
|
{
|
|
PublicKey: validatorKey.PublicKey().Marshal(),
|
|
CommitteeIndex: 5,
|
|
CommitteeLength: uint64(len(committee)),
|
|
ValidatorIndex: validatorIndex,
|
|
}}}
|
|
m.validatorClient.EXPECT().AttestationData(
|
|
gomock.Any(), // ctx
|
|
gomock.AssignableToTypeOf(ðpb.AttestationDataRequest{}),
|
|
).Return(ðpb.AttestationData{
|
|
Target: ðpb.Checkpoint{Root: bytesutil.PadTo([]byte("B"), 32)},
|
|
Source: ðpb.Checkpoint{Root: bytesutil.PadTo([]byte("C"), 32), Epoch: 3},
|
|
BeaconBlockRoot: make([]byte, fieldparams.RootLength),
|
|
}, nil)
|
|
|
|
m.validatorClient.EXPECT().DomainData(
|
|
gomock.Any(), // ctx
|
|
gomock.Any(), // epoch
|
|
).Times(2).Return(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil /*err*/)
|
|
|
|
var generatedAttestation *ethpb.Attestation
|
|
m.validatorClient.EXPECT().ProposeAttestation(
|
|
gomock.Any(), // ctx
|
|
gomock.AssignableToTypeOf(ðpb.Attestation{}),
|
|
).Do(func(_ context.Context, att *ethpb.Attestation) {
|
|
generatedAttestation = att
|
|
}).Return(ðpb.AttestResponse{}, nil /* error */)
|
|
|
|
validator.SubmitAttestation(t.Context(), 30, pubKey)
|
|
|
|
assert.Equal(t, 2, len(generatedAttestation.AggregationBits))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSignAttestation(t *testing.T) {
|
|
for _, isSlashingProtectionMinimal := range [...]bool{false, true} {
|
|
t.Run(fmt.Sprintf("SlashingProtectionMinimal:%v", isSlashingProtectionMinimal), func(t *testing.T) {
|
|
validator, m, _, finish := setup(t, isSlashingProtectionMinimal)
|
|
defer finish()
|
|
|
|
wantedFork := ðpb.Fork{
|
|
PreviousVersion: []byte{'a', 'b', 'c', 'd'},
|
|
CurrentVersion: []byte{'d', 'e', 'f', 'f'},
|
|
Epoch: 0,
|
|
}
|
|
genesisValidatorsRoot := [32]byte{0x01, 0x02}
|
|
attesterDomain, err := signing.Domain(wantedFork, 0, params.BeaconConfig().DomainBeaconAttester, genesisValidatorsRoot[:])
|
|
require.NoError(t, err)
|
|
m.validatorClient.EXPECT().
|
|
DomainData(gomock.Any(), gomock.Any()).
|
|
Return(ðpb.DomainResponse{SignatureDomain: attesterDomain}, nil)
|
|
ctx := t.Context()
|
|
att := util.NewAttestation()
|
|
att.Data.Source.Epoch = 100
|
|
att.Data.Target.Epoch = 200
|
|
att.Data.Slot = 999
|
|
att.Data.BeaconBlockRoot = bytesutil.PadTo([]byte("blockRoot"), 32)
|
|
|
|
pk := testKeyFromBytes(t, []byte{1})
|
|
validator.km = newMockKeymanager(t, pk)
|
|
sig, sr, err := validator.signAtt(ctx, pk.pub, att.Data, att.Data.Slot)
|
|
require.NoError(t, err, "%x,%x,%v", sig, sr, err)
|
|
require.Equal(t, "b6a60f8497bd328908be83634d045"+
|
|
"dd7a32f5e246b2c4031fc2f316983f362e36fc27fd3d6d5a2b15"+
|
|
"b4dbff38804ffb10b1719b7ebc54e9cbf3293fd37082bc0fc91f"+
|
|
"79d70ce5b04ff13de3c8e10bb41305bfdbe921a43792c12624f2"+
|
|
"25ee865", hex.EncodeToString(sig))
|
|
// proposer domain
|
|
require.DeepEqual(t, "02bbdb88056d6cbafd6e94575540"+
|
|
"e74b8cf2c0f2c1b79b8e17e7b21ed1694305", hex.EncodeToString(sr[:]))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestServer_WaitToSlotOneThird_CanWait(t *testing.T) {
|
|
currentTime := time.Now()
|
|
currentSlot := primitives.Slot(4)
|
|
genesisTime := currentTime.Add(-1 * time.Duration(currentSlot.Mul(params.BeaconConfig().SecondsPerSlot)) * time.Second)
|
|
|
|
v := &validator{
|
|
genesisTime: genesisTime,
|
|
slotFeed: new(event.Feed),
|
|
}
|
|
|
|
timeToSleep := params.BeaconConfig().SecondsPerSlot / 3
|
|
oneThird := currentTime.Add(time.Duration(timeToSleep) * time.Second)
|
|
v.waitOneThirdOrValidBlock(t.Context(), currentSlot)
|
|
|
|
if oneThird.Sub(time.Now()) > 10*time.Millisecond { // Allow for small diff due to execution time.
|
|
t.Errorf("Wanted %s time for slot one third but got %s", oneThird, currentTime)
|
|
}
|
|
}
|
|
|
|
func TestServer_WaitToSlotOneThird_SameReqSlot(t *testing.T) {
|
|
currentTime := time.Now()
|
|
currentSlot := primitives.Slot(4)
|
|
genesisTime := currentTime.Add(-1 * time.Duration(currentSlot.Mul(params.BeaconConfig().SecondsPerSlot)) * time.Second)
|
|
|
|
v := &validator{
|
|
genesisTime: genesisTime,
|
|
slotFeed: new(event.Feed),
|
|
highestValidSlot: currentSlot,
|
|
}
|
|
|
|
v.waitOneThirdOrValidBlock(t.Context(), currentSlot)
|
|
|
|
if currentTime.Sub(time.Now()) > 10*time.Millisecond { // Allow for small diff due to execution time.
|
|
t.Errorf("Wanted %s time for slot one third but got %s", time.Now(), currentTime)
|
|
}
|
|
}
|
|
|
|
func TestServer_WaitToSlotOneThird_ReceiveBlockSlot(t *testing.T) {
|
|
resetCfg := features.InitWithReset(&features.Flags{AttestTimely: true})
|
|
defer resetCfg()
|
|
|
|
currentTime := time.Now()
|
|
currentSlot := primitives.Slot(4)
|
|
genesisTime := currentTime.Add(-1 * time.Duration(currentSlot.Mul(params.BeaconConfig().SecondsPerSlot)) * time.Second)
|
|
|
|
v := &validator{
|
|
genesisTime: genesisTime,
|
|
slotFeed: new(event.Feed),
|
|
}
|
|
|
|
wg := &sync.WaitGroup{}
|
|
wg.Go(func() {
|
|
time.Sleep(100 * time.Millisecond)
|
|
v.slotFeed.Send(currentSlot)
|
|
})
|
|
|
|
v.waitOneThirdOrValidBlock(t.Context(), currentSlot)
|
|
|
|
if currentTime.Sub(time.Now()) > 10*time.Millisecond { // Allow for small diff due to execution time.
|
|
t.Errorf("Wanted %s time for slot one third but got %s", time.Now(), currentTime)
|
|
}
|
|
}
|
|
|
|
func Test_slashableAttestationCheck(t *testing.T) {
|
|
for _, isSlashingProtectionMinimal := range [...]bool{false, true} {
|
|
t.Run(fmt.Sprintf("SlashingProtectionMinimal:%v", isSlashingProtectionMinimal), func(t *testing.T) {
|
|
validator, _, validatorKey, finish := setup(t, isSlashingProtectionMinimal)
|
|
defer finish()
|
|
var pubKey [fieldparams.BLSPubkeyLength]byte
|
|
copy(pubKey[:], validatorKey.PublicKey().Marshal())
|
|
att := ðpb.IndexedAttestation{
|
|
AttestingIndices: []uint64{1, 2},
|
|
Data: ðpb.AttestationData{
|
|
Slot: 5,
|
|
CommitteeIndex: 2,
|
|
BeaconBlockRoot: bytesutil.PadTo([]byte("great block"), 32),
|
|
Source: ðpb.Checkpoint{
|
|
Epoch: 4,
|
|
Root: bytesutil.PadTo([]byte("good source"), 32),
|
|
},
|
|
Target: ðpb.Checkpoint{
|
|
Epoch: 10,
|
|
Root: bytesutil.PadTo([]byte("good target"), 32),
|
|
},
|
|
},
|
|
}
|
|
|
|
err := validator.db.SlashableAttestationCheck(t.Context(), att, pubKey, [32]byte{1}, false, nil)
|
|
require.NoError(t, err, "Expected allowed attestation not to throw error")
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_slashableAttestationCheck_UpdatesLowestSignedEpochs(t *testing.T) {
|
|
for _, isSlashingProtectionMinimal := range [...]bool{false, true} {
|
|
t.Run(fmt.Sprintf("SlashingProtectionMinimal:%v", isSlashingProtectionMinimal), func(t *testing.T) {
|
|
validator, m, validatorKey, finish := setup(t, isSlashingProtectionMinimal)
|
|
defer finish()
|
|
ctx := t.Context()
|
|
var pubKey [fieldparams.BLSPubkeyLength]byte
|
|
copy(pubKey[:], validatorKey.PublicKey().Marshal())
|
|
att := ðpb.IndexedAttestation{
|
|
AttestingIndices: []uint64{1, 2},
|
|
Data: ðpb.AttestationData{
|
|
Slot: 5,
|
|
CommitteeIndex: 2,
|
|
BeaconBlockRoot: bytesutil.PadTo([]byte("great block"), 32),
|
|
Source: ðpb.Checkpoint{
|
|
Epoch: 4,
|
|
Root: bytesutil.PadTo([]byte("good source"), 32),
|
|
},
|
|
Target: ðpb.Checkpoint{
|
|
Epoch: 10,
|
|
Root: bytesutil.PadTo([]byte("good target"), 32),
|
|
},
|
|
},
|
|
}
|
|
|
|
m.validatorClient.EXPECT().DomainData(
|
|
gomock.Any(), // ctx
|
|
ðpb.DomainRequest{Epoch: 10, Domain: []byte{1, 0, 0, 0}},
|
|
).Return(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil /*err*/)
|
|
_, sr, err := validator.domainAndSigningRoot(ctx, att.Data)
|
|
require.NoError(t, err)
|
|
|
|
err = validator.db.SlashableAttestationCheck(t.Context(), att, pubKey, sr, false, nil)
|
|
require.NoError(t, err)
|
|
differentSigningRoot := [32]byte{2}
|
|
|
|
err = validator.db.SlashableAttestationCheck(t.Context(), att, pubKey, differentSigningRoot, false, nil)
|
|
require.ErrorContains(t, "could not sign attestation", err)
|
|
|
|
e, exists, err := validator.db.LowestSignedSourceEpoch(t.Context(), pubKey)
|
|
require.NoError(t, err)
|
|
require.Equal(t, true, exists)
|
|
require.Equal(t, primitives.Epoch(4), e)
|
|
e, exists, err = validator.db.LowestSignedTargetEpoch(t.Context(), pubKey)
|
|
require.NoError(t, err)
|
|
require.Equal(t, true, exists)
|
|
require.Equal(t, primitives.Epoch(10), e)
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_slashableAttestationCheck_OK(t *testing.T) {
|
|
for _, isSlashingProtectionMinimal := range [...]bool{false, true} {
|
|
t.Run(fmt.Sprintf("SlashingProtectionMinimal:%v", isSlashingProtectionMinimal), func(t *testing.T) {
|
|
ctx := t.Context()
|
|
validator, _, _, finish := setup(t, isSlashingProtectionMinimal)
|
|
defer finish()
|
|
att := ðpb.IndexedAttestation{
|
|
AttestingIndices: []uint64{1, 2},
|
|
Data: ðpb.AttestationData{
|
|
Slot: 5,
|
|
CommitteeIndex: 2,
|
|
BeaconBlockRoot: []byte("great block"),
|
|
Source: ðpb.Checkpoint{
|
|
Epoch: 4,
|
|
Root: []byte("good source"),
|
|
},
|
|
Target: ðpb.Checkpoint{
|
|
Epoch: 10,
|
|
Root: []byte("good target"),
|
|
},
|
|
},
|
|
}
|
|
sr := [32]byte{1}
|
|
fakePubkey := bytesutil.ToBytes48([]byte("test"))
|
|
|
|
err := validator.db.SlashableAttestationCheck(ctx, att, fakePubkey, sr, false, nil)
|
|
require.NoError(t, err, "Expected allowed attestation not to throw error")
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_slashableAttestationCheck_GenesisEpoch(t *testing.T) {
|
|
for _, isSlashingProtectionMinimal := range [...]bool{false, true} {
|
|
t.Run(fmt.Sprintf("SlashingProtectionMinimal:%v", isSlashingProtectionMinimal), func(t *testing.T) {
|
|
ctx := t.Context()
|
|
validator, _, _, finish := setup(t, isSlashingProtectionMinimal)
|
|
defer finish()
|
|
att := ðpb.IndexedAttestation{
|
|
AttestingIndices: []uint64{1, 2},
|
|
Data: ðpb.AttestationData{
|
|
Slot: 5,
|
|
CommitteeIndex: 2,
|
|
BeaconBlockRoot: bytesutil.PadTo([]byte("great block root"), 32),
|
|
Source: ðpb.Checkpoint{
|
|
Epoch: 0,
|
|
Root: bytesutil.PadTo([]byte("great root"), 32),
|
|
},
|
|
Target: ðpb.Checkpoint{
|
|
Epoch: 0,
|
|
Root: bytesutil.PadTo([]byte("great root"), 32),
|
|
},
|
|
},
|
|
}
|
|
|
|
fakePubkey := bytesutil.ToBytes48([]byte("test"))
|
|
err := validator.db.SlashableAttestationCheck(ctx, att, fakePubkey, [32]byte{}, false, nil)
|
|
require.NoError(t, err, "Expected allowed attestation not to throw error")
|
|
e, exists, err := validator.db.LowestSignedSourceEpoch(t.Context(), fakePubkey)
|
|
require.NoError(t, err)
|
|
require.Equal(t, true, exists)
|
|
require.Equal(t, primitives.Epoch(0), e)
|
|
e, exists, err = validator.db.LowestSignedTargetEpoch(t.Context(), fakePubkey)
|
|
require.NoError(t, err)
|
|
require.Equal(t, true, exists)
|
|
require.Equal(t, primitives.Epoch(0), e)
|
|
})
|
|
}
|
|
}
|