2021-04-23 17:06:13 +00:00
|
|
|
package slasherkv
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"encoding/binary"
|
2021-08-05 21:27:05 +00:00
|
|
|
"math/rand"
|
2021-04-23 17:06:13 +00:00
|
|
|
"reflect"
|
2021-08-05 21:27:05 +00:00
|
|
|
"sort"
|
2021-04-23 17:06:13 +00:00
|
|
|
"testing"
|
|
|
|
|
2022-06-28 13:03:24 +00:00
|
|
|
ssz "github.com/prysmaticlabs/fastssz"
|
2023-03-17 18:52:56 +00:00
|
|
|
slashertypes "github.com/prysmaticlabs/prysm/v4/beacon-chain/slasher/types"
|
|
|
|
"github.com/prysmaticlabs/prysm/v4/config/params"
|
|
|
|
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
|
|
|
|
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
|
|
|
|
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
|
|
|
|
"github.com/prysmaticlabs/prysm/v4/testing/assert"
|
|
|
|
"github.com/prysmaticlabs/prysm/v4/testing/require"
|
2021-04-23 17:06:13 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func TestStore_AttestationRecordForValidator_SaveRetrieve(t *testing.T) {
|
|
|
|
ctx := context.Background()
|
|
|
|
beaconDB := setupDB(t)
|
2023-01-26 14:40:12 +00:00
|
|
|
valIdx := primitives.ValidatorIndex(1)
|
|
|
|
target := primitives.Epoch(5)
|
|
|
|
source := primitives.Epoch(4)
|
2021-04-23 17:06:13 +00:00
|
|
|
attRecord, err := beaconDB.AttestationRecordForValidator(ctx, valIdx, target)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, true, attRecord == nil)
|
|
|
|
|
|
|
|
sr := [32]byte{1}
|
|
|
|
err = beaconDB.SaveAttestationRecordsForValidators(
|
|
|
|
ctx,
|
|
|
|
[]*slashertypes.IndexedAttestationWrapper{
|
|
|
|
createAttestationWrapper(source, target, []uint64{uint64(valIdx)}, sr[:]),
|
|
|
|
},
|
|
|
|
)
|
|
|
|
require.NoError(t, err)
|
|
|
|
attRecord, err = beaconDB.AttestationRecordForValidator(ctx, valIdx, target)
|
|
|
|
require.NoError(t, err)
|
|
|
|
assert.DeepEqual(t, target, attRecord.IndexedAttestation.Data.Target.Epoch)
|
|
|
|
assert.DeepEqual(t, source, attRecord.IndexedAttestation.Data.Source.Epoch)
|
|
|
|
assert.DeepEqual(t, sr, attRecord.SigningRoot)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestStore_LastEpochWrittenForValidators(t *testing.T) {
|
|
|
|
ctx := context.Background()
|
|
|
|
beaconDB := setupDB(t)
|
2023-01-26 14:40:12 +00:00
|
|
|
indices := []primitives.ValidatorIndex{1, 2, 3}
|
|
|
|
epoch := primitives.Epoch(5)
|
2021-04-23 17:06:13 +00:00
|
|
|
|
|
|
|
attestedEpochs, err := beaconDB.LastEpochWrittenForValidators(ctx, indices)
|
|
|
|
require.NoError(t, err)
|
2021-12-06 21:45:38 +00:00
|
|
|
require.Equal(t, true, len(attestedEpochs) == len(indices))
|
|
|
|
for _, item := range attestedEpochs {
|
2023-01-26 14:40:12 +00:00
|
|
|
require.Equal(t, primitives.Epoch(0), item.Epoch)
|
2021-12-06 21:45:38 +00:00
|
|
|
}
|
2021-04-23 17:06:13 +00:00
|
|
|
|
2023-01-26 14:40:12 +00:00
|
|
|
epochsByValidator := map[primitives.ValidatorIndex]primitives.Epoch{
|
2021-12-06 21:45:38 +00:00
|
|
|
1: epoch,
|
|
|
|
2: epoch,
|
|
|
|
3: epoch,
|
|
|
|
}
|
|
|
|
err = beaconDB.SaveLastEpochsWrittenForValidators(ctx, epochsByValidator)
|
2021-04-23 17:06:13 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
retrievedEpochs, err := beaconDB.LastEpochWrittenForValidators(ctx, indices)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, len(indices), len(retrievedEpochs))
|
|
|
|
|
|
|
|
for i, retrievedEpoch := range retrievedEpochs {
|
|
|
|
want := &slashertypes.AttestedEpochForValidator{
|
|
|
|
Epoch: epoch,
|
|
|
|
ValidatorIndex: indices[i],
|
|
|
|
}
|
|
|
|
require.DeepEqual(t, want, retrievedEpoch)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestStore_CheckAttesterDoubleVotes(t *testing.T) {
|
|
|
|
ctx := context.Background()
|
|
|
|
beaconDB := setupDB(t)
|
|
|
|
err := beaconDB.SaveAttestationRecordsForValidators(ctx, []*slashertypes.IndexedAttestationWrapper{
|
|
|
|
createAttestationWrapper(2, 3, []uint64{0, 1}, []byte{1}),
|
2021-08-05 21:27:05 +00:00
|
|
|
createAttestationWrapper(3, 4, []uint64{2, 3}, []byte{3}),
|
2021-04-23 17:06:13 +00:00
|
|
|
})
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
slashableAtts := []*slashertypes.IndexedAttestationWrapper{
|
|
|
|
createAttestationWrapper(2, 3, []uint64{0, 1}, []byte{2}), // Different signing root.
|
2021-08-05 21:27:05 +00:00
|
|
|
createAttestationWrapper(3, 4, []uint64{2, 3}, []byte{4}), // Different signing root.
|
2021-04-23 17:06:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
wanted := []*slashertypes.AttesterDoubleVote{
|
|
|
|
{
|
|
|
|
ValidatorIndex: 0,
|
|
|
|
Target: 3,
|
|
|
|
PrevAttestationWrapper: createAttestationWrapper(2, 3, []uint64{0, 1}, []byte{1}),
|
|
|
|
AttestationWrapper: createAttestationWrapper(2, 3, []uint64{0, 1}, []byte{2}),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
ValidatorIndex: 1,
|
|
|
|
Target: 3,
|
|
|
|
PrevAttestationWrapper: createAttestationWrapper(2, 3, []uint64{0, 1}, []byte{1}),
|
|
|
|
AttestationWrapper: createAttestationWrapper(2, 3, []uint64{0, 1}, []byte{2}),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
ValidatorIndex: 2,
|
|
|
|
Target: 4,
|
2021-08-05 21:27:05 +00:00
|
|
|
PrevAttestationWrapper: createAttestationWrapper(3, 4, []uint64{2, 3}, []byte{3}),
|
|
|
|
AttestationWrapper: createAttestationWrapper(3, 4, []uint64{2, 3}, []byte{4}),
|
2021-04-23 17:06:13 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
ValidatorIndex: 3,
|
|
|
|
Target: 4,
|
2021-08-05 21:27:05 +00:00
|
|
|
PrevAttestationWrapper: createAttestationWrapper(3, 4, []uint64{2, 3}, []byte{3}),
|
|
|
|
AttestationWrapper: createAttestationWrapper(3, 4, []uint64{2, 3}, []byte{4}),
|
2021-04-23 17:06:13 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
doubleVotes, err := beaconDB.CheckAttesterDoubleVotes(ctx, slashableAtts)
|
|
|
|
require.NoError(t, err)
|
2021-08-05 21:27:05 +00:00
|
|
|
sort.SliceStable(doubleVotes, func(i, j int) bool {
|
|
|
|
return uint64(doubleVotes[i].ValidatorIndex) < uint64(doubleVotes[j].ValidatorIndex)
|
|
|
|
})
|
|
|
|
require.Equal(t, len(wanted), len(doubleVotes))
|
|
|
|
for i, double := range doubleVotes {
|
|
|
|
require.DeepEqual(t, wanted[i].ValidatorIndex, double.ValidatorIndex)
|
|
|
|
require.DeepEqual(t, wanted[i].Target, double.Target)
|
|
|
|
require.DeepEqual(t, wanted[i].PrevAttestationWrapper, double.PrevAttestationWrapper)
|
|
|
|
require.DeepEqual(t, wanted[i].AttestationWrapper, double.AttestationWrapper)
|
|
|
|
}
|
2021-04-23 17:06:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestStore_SlasherChunk_SaveRetrieve(t *testing.T) {
|
|
|
|
ctx := context.Background()
|
|
|
|
beaconDB := setupDB(t)
|
|
|
|
elemsPerChunk := 16
|
|
|
|
totalChunks := 64
|
|
|
|
chunkKeys := make([][]byte, totalChunks)
|
|
|
|
chunks := make([][]uint16, totalChunks)
|
|
|
|
for i := 0; i < totalChunks; i++ {
|
|
|
|
chunk := make([]uint16, elemsPerChunk)
|
|
|
|
for j := 0; j < len(chunk); j++ {
|
|
|
|
chunk[j] = uint16(0)
|
|
|
|
}
|
|
|
|
chunks[i] = chunk
|
|
|
|
chunkKeys[i] = ssz.MarshalUint64(make([]byte, 0), uint64(i))
|
|
|
|
}
|
|
|
|
|
|
|
|
// We save chunks for min spans.
|
|
|
|
err := beaconDB.SaveSlasherChunks(ctx, slashertypes.MinSpan, chunkKeys, chunks)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
// We expect no chunks to be stored for max spans.
|
|
|
|
_, chunksExist, err := beaconDB.LoadSlasherChunks(
|
|
|
|
ctx, slashertypes.MaxSpan, chunkKeys,
|
|
|
|
)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, len(chunks), len(chunksExist))
|
|
|
|
for _, exists := range chunksExist {
|
|
|
|
require.Equal(t, false, exists)
|
|
|
|
}
|
|
|
|
|
|
|
|
// We check we saved the right chunks.
|
|
|
|
retrievedChunks, chunksExist, err := beaconDB.LoadSlasherChunks(
|
|
|
|
ctx, slashertypes.MinSpan, chunkKeys,
|
|
|
|
)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, len(chunks), len(retrievedChunks))
|
|
|
|
require.Equal(t, len(chunks), len(chunksExist))
|
|
|
|
for i, exists := range chunksExist {
|
|
|
|
require.Equal(t, true, exists)
|
|
|
|
require.DeepEqual(t, chunks[i], retrievedChunks[i])
|
|
|
|
}
|
|
|
|
|
|
|
|
// We save chunks for max spans.
|
|
|
|
err = beaconDB.SaveSlasherChunks(ctx, slashertypes.MaxSpan, chunkKeys, chunks)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
// We check we saved the right chunks.
|
|
|
|
retrievedChunks, chunksExist, err = beaconDB.LoadSlasherChunks(
|
|
|
|
ctx, slashertypes.MaxSpan, chunkKeys,
|
|
|
|
)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, len(chunks), len(retrievedChunks))
|
|
|
|
require.Equal(t, len(chunks), len(chunksExist))
|
|
|
|
for i, exists := range chunksExist {
|
|
|
|
require.Equal(t, true, exists)
|
|
|
|
require.DeepEqual(t, chunks[i], retrievedChunks[i])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-30 18:29:40 +00:00
|
|
|
func TestStore_SlasherChunk_PreventsSavingWrongLength(t *testing.T) {
|
|
|
|
ctx := context.Background()
|
|
|
|
beaconDB := setupDB(t)
|
|
|
|
totalChunks := 64
|
|
|
|
chunkKeys := make([][]byte, totalChunks)
|
|
|
|
chunks := make([][]uint16, totalChunks)
|
|
|
|
for i := 0; i < totalChunks; i++ {
|
|
|
|
chunks[i] = []uint16{}
|
|
|
|
chunkKeys[i] = ssz.MarshalUint64(make([]byte, 0), uint64(i))
|
|
|
|
}
|
|
|
|
// We should get an error if saving empty chunks.
|
|
|
|
err := beaconDB.SaveSlasherChunks(ctx, slashertypes.MinSpan, chunkKeys, chunks)
|
|
|
|
require.ErrorContains(t, "cannot encode empty chunk", err)
|
|
|
|
}
|
|
|
|
|
2021-04-23 17:06:13 +00:00
|
|
|
func TestStore_ExistingBlockProposals(t *testing.T) {
|
|
|
|
ctx := context.Background()
|
|
|
|
beaconDB := setupDB(t)
|
|
|
|
proposals := []*slashertypes.SignedBlockHeaderWrapper{
|
|
|
|
createProposalWrapper(t, 1, 1, []byte{1}),
|
|
|
|
createProposalWrapper(t, 2, 1, []byte{1}),
|
|
|
|
createProposalWrapper(t, 3, 1, []byte{1}),
|
|
|
|
}
|
|
|
|
// First time checking should return empty existing proposals.
|
|
|
|
doubleProposals, err := beaconDB.CheckDoubleBlockProposals(ctx, proposals)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, 0, len(doubleProposals))
|
|
|
|
|
|
|
|
// We then save the block proposals to disk.
|
|
|
|
err = beaconDB.SaveBlockProposals(ctx, proposals)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
// Second time checking same proposals but all with different signing root should
|
|
|
|
// return all double proposals.
|
|
|
|
proposals[0].SigningRoot = bytesutil.ToBytes32([]byte{2})
|
|
|
|
proposals[1].SigningRoot = bytesutil.ToBytes32([]byte{2})
|
|
|
|
proposals[2].SigningRoot = bytesutil.ToBytes32([]byte{2})
|
|
|
|
|
|
|
|
doubleProposals, err = beaconDB.CheckDoubleBlockProposals(ctx, proposals)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, len(proposals), len(doubleProposals))
|
|
|
|
for i, existing := range doubleProposals {
|
|
|
|
require.DeepEqual(t, doubleProposals[i].Header_1, existing.Header_1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func Test_encodeDecodeProposalRecord(t *testing.T) {
|
|
|
|
tests := []struct {
|
|
|
|
name string
|
|
|
|
blkHdr *slashertypes.SignedBlockHeaderWrapper
|
|
|
|
wantErr bool
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "empty standard encode/decode",
|
|
|
|
blkHdr: createProposalWrapper(t, 0, 0, nil),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "standard encode/decode",
|
|
|
|
blkHdr: createProposalWrapper(t, 15, 6, []byte("1")),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "failing encode/decode",
|
|
|
|
blkHdr: &slashertypes.SignedBlockHeaderWrapper{
|
|
|
|
SignedBeaconBlockHeader: ðpb.SignedBeaconBlockHeader{
|
|
|
|
Header: ðpb.BeaconBlockHeader{},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
wantErr: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "failing empty encode/decode",
|
|
|
|
blkHdr: &slashertypes.SignedBlockHeaderWrapper{},
|
|
|
|
wantErr: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "failing nil",
|
|
|
|
blkHdr: nil,
|
|
|
|
wantErr: true,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
got, err := encodeProposalRecord(tt.blkHdr)
|
|
|
|
if (err != nil) != tt.wantErr {
|
|
|
|
t.Fatalf("encodeProposalRecord() error = %v, wantErr %v", err, tt.wantErr)
|
|
|
|
}
|
|
|
|
decoded, err := decodeProposalRecord(got)
|
|
|
|
if (err != nil) != tt.wantErr {
|
|
|
|
t.Fatalf("decodeProposalRecord() error = %v, wantErr %v", err, tt.wantErr)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !tt.wantErr && !reflect.DeepEqual(tt.blkHdr, decoded) {
|
|
|
|
t.Errorf("Did not match got = %v, want %v", tt.blkHdr, decoded)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func Test_encodeDecodeAttestationRecord(t *testing.T) {
|
|
|
|
tests := []struct {
|
|
|
|
name string
|
|
|
|
attWrapper *slashertypes.IndexedAttestationWrapper
|
|
|
|
wantErr bool
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "empty standard encode/decode",
|
|
|
|
attWrapper: createAttestationWrapper(0, 0, nil /* indices */, nil /* signingRoot */),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "standard encode/decode",
|
|
|
|
attWrapper: createAttestationWrapper(15, 6, []uint64{2, 4}, []byte("1") /* signingRoot */),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "failing encode/decode",
|
|
|
|
attWrapper: &slashertypes.IndexedAttestationWrapper{
|
|
|
|
IndexedAttestation: ðpb.IndexedAttestation{
|
|
|
|
Data: ðpb.AttestationData{},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
wantErr: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "failing empty encode/decode",
|
|
|
|
attWrapper: &slashertypes.IndexedAttestationWrapper{},
|
|
|
|
wantErr: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "failing nil",
|
|
|
|
attWrapper: nil,
|
|
|
|
wantErr: true,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
got, err := encodeAttestationRecord(tt.attWrapper)
|
|
|
|
if (err != nil) != tt.wantErr {
|
|
|
|
t.Fatalf("encodeAttestationRecord() error = %v, wantErr %v", err, tt.wantErr)
|
|
|
|
}
|
|
|
|
decoded, err := decodeAttestationRecord(got)
|
|
|
|
if (err != nil) != tt.wantErr {
|
|
|
|
t.Fatalf("decodeAttestationRecord() error = %v, wantErr %v", err, tt.wantErr)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !tt.wantErr && !reflect.DeepEqual(tt.attWrapper, decoded) {
|
|
|
|
t.Errorf("Did not match got = %v, want %v", tt.attWrapper, decoded)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestStore_HighestAttestations(t *testing.T) {
|
|
|
|
ctx := context.Background()
|
|
|
|
tests := []struct {
|
|
|
|
name string
|
|
|
|
attestationsInDB []*slashertypes.IndexedAttestationWrapper
|
2021-12-07 17:52:39 +00:00
|
|
|
expected []*ethpb.HighestAttestation
|
2023-01-26 14:40:12 +00:00
|
|
|
indices []primitives.ValidatorIndex
|
2021-04-23 17:06:13 +00:00
|
|
|
wantErr bool
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "should get highest att if single att in db",
|
|
|
|
attestationsInDB: []*slashertypes.IndexedAttestationWrapper{
|
|
|
|
createAttestationWrapper(0, 3, []uint64{1}, []byte{1}),
|
|
|
|
},
|
2023-01-26 14:40:12 +00:00
|
|
|
indices: []primitives.ValidatorIndex{1},
|
2021-12-07 17:52:39 +00:00
|
|
|
expected: []*ethpb.HighestAttestation{
|
2021-04-23 17:06:13 +00:00
|
|
|
{
|
|
|
|
ValidatorIndex: 1,
|
|
|
|
HighestSourceEpoch: 0,
|
|
|
|
HighestTargetEpoch: 3,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "should get highest att for multiple with diff histories",
|
|
|
|
attestationsInDB: []*slashertypes.IndexedAttestationWrapper{
|
|
|
|
createAttestationWrapper(0, 3, []uint64{2}, []byte{1}),
|
2021-08-05 21:27:05 +00:00
|
|
|
createAttestationWrapper(1, 4, []uint64{3}, []byte{2}),
|
|
|
|
createAttestationWrapper(2, 3, []uint64{4}, []byte{3}),
|
|
|
|
createAttestationWrapper(5, 6, []uint64{5}, []byte{4}),
|
2021-04-23 17:06:13 +00:00
|
|
|
},
|
2023-01-26 14:40:12 +00:00
|
|
|
indices: []primitives.ValidatorIndex{2, 3, 4, 5},
|
2021-12-07 17:52:39 +00:00
|
|
|
expected: []*ethpb.HighestAttestation{
|
2021-04-23 17:06:13 +00:00
|
|
|
{
|
|
|
|
ValidatorIndex: 2,
|
|
|
|
HighestSourceEpoch: 0,
|
|
|
|
HighestTargetEpoch: 3,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
ValidatorIndex: 3,
|
|
|
|
HighestSourceEpoch: 1,
|
|
|
|
HighestTargetEpoch: 4,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
ValidatorIndex: 4,
|
|
|
|
HighestSourceEpoch: 2,
|
|
|
|
HighestTargetEpoch: 3,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
ValidatorIndex: 5,
|
|
|
|
HighestSourceEpoch: 5,
|
|
|
|
HighestTargetEpoch: 6,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "should get correct highest att for multiple shared atts with diff histories",
|
|
|
|
attestationsInDB: []*slashertypes.IndexedAttestationWrapper{
|
|
|
|
createAttestationWrapper(1, 4, []uint64{2, 3}, []byte{1}),
|
2021-08-05 21:27:05 +00:00
|
|
|
createAttestationWrapper(2, 5, []uint64{3, 5}, []byte{2}),
|
|
|
|
createAttestationWrapper(4, 5, []uint64{1, 2}, []byte{3}),
|
|
|
|
createAttestationWrapper(6, 7, []uint64{5}, []byte{4}),
|
2021-04-23 17:06:13 +00:00
|
|
|
},
|
2023-01-26 14:40:12 +00:00
|
|
|
indices: []primitives.ValidatorIndex{2, 3, 4, 5},
|
2021-12-07 17:52:39 +00:00
|
|
|
expected: []*ethpb.HighestAttestation{
|
2021-04-23 17:06:13 +00:00
|
|
|
{
|
|
|
|
ValidatorIndex: 2,
|
|
|
|
HighestSourceEpoch: 4,
|
|
|
|
HighestTargetEpoch: 5,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
ValidatorIndex: 3,
|
|
|
|
HighestSourceEpoch: 2,
|
|
|
|
HighestTargetEpoch: 5,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
ValidatorIndex: 5,
|
|
|
|
HighestSourceEpoch: 6,
|
|
|
|
HighestTargetEpoch: 7,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
beaconDB := setupDB(t)
|
|
|
|
require.NoError(t, beaconDB.SaveAttestationRecordsForValidators(ctx, tt.attestationsInDB))
|
|
|
|
|
|
|
|
highestAttestations, err := beaconDB.HighestAttestations(ctx, tt.indices)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, len(tt.expected), len(highestAttestations))
|
|
|
|
for i, existing := range highestAttestations {
|
|
|
|
require.DeepEqual(t, existing, tt.expected[i])
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func BenchmarkHighestAttestations(b *testing.B) {
|
|
|
|
b.StopTimer()
|
|
|
|
count := 10000
|
|
|
|
valsPerAtt := 100
|
|
|
|
indicesPerAtt := make([][]uint64, count)
|
|
|
|
for i := 0; i < count; i++ {
|
|
|
|
indicesForAtt := make([]uint64, valsPerAtt)
|
|
|
|
for r := i * count; r < valsPerAtt*(i+1); r++ {
|
|
|
|
indicesForAtt[i] = uint64(r)
|
|
|
|
}
|
|
|
|
indicesPerAtt[i] = indicesForAtt
|
|
|
|
}
|
|
|
|
atts := make([]*slashertypes.IndexedAttestationWrapper, count)
|
|
|
|
for i := 0; i < count; i++ {
|
2023-01-26 14:40:12 +00:00
|
|
|
atts[i] = createAttestationWrapper(primitives.Epoch(i), primitives.Epoch(i+2), indicesPerAtt[i], []byte{})
|
2021-04-23 17:06:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ctx := context.Background()
|
|
|
|
beaconDB := setupDB(b)
|
|
|
|
require.NoError(b, beaconDB.SaveAttestationRecordsForValidators(ctx, atts))
|
|
|
|
|
2023-01-26 14:40:12 +00:00
|
|
|
allIndices := make([]primitives.ValidatorIndex, valsPerAtt*count)
|
2021-04-23 17:06:13 +00:00
|
|
|
for i := 0; i < count; i++ {
|
2023-01-26 14:40:12 +00:00
|
|
|
indicesForAtt := make([]primitives.ValidatorIndex, valsPerAtt)
|
2021-04-23 17:06:13 +00:00
|
|
|
for r := 0; r < valsPerAtt; r++ {
|
2023-01-26 14:40:12 +00:00
|
|
|
indicesForAtt[r] = primitives.ValidatorIndex(atts[i].IndexedAttestation.AttestingIndices[r])
|
2021-04-23 17:06:13 +00:00
|
|
|
}
|
|
|
|
allIndices = append(allIndices, indicesForAtt...)
|
|
|
|
}
|
|
|
|
b.ReportAllocs()
|
|
|
|
b.StartTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
|
|
_, err := beaconDB.HighestAttestations(ctx, allIndices)
|
|
|
|
require.NoError(b, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-05 21:27:05 +00:00
|
|
|
func BenchmarkStore_CheckDoubleBlockProposals(b *testing.B) {
|
|
|
|
b.StopTimer()
|
|
|
|
count := 10000
|
|
|
|
valsPerAtt := 100
|
|
|
|
indicesPerAtt := make([][]uint64, count)
|
|
|
|
for i := 0; i < count; i++ {
|
|
|
|
indicesForAtt := make([]uint64, valsPerAtt)
|
|
|
|
for r := i * count; r < valsPerAtt*(i+1); r++ {
|
|
|
|
indicesForAtt[i] = uint64(r)
|
|
|
|
}
|
|
|
|
indicesPerAtt[i] = indicesForAtt
|
|
|
|
}
|
|
|
|
atts := make([]*slashertypes.IndexedAttestationWrapper, count)
|
|
|
|
for i := 0; i < count; i++ {
|
2023-01-26 14:40:12 +00:00
|
|
|
atts[i] = createAttestationWrapper(primitives.Epoch(i), primitives.Epoch(i+2), indicesPerAtt[i], []byte{})
|
2021-08-05 21:27:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ctx := context.Background()
|
|
|
|
beaconDB := setupDB(b)
|
|
|
|
require.NoError(b, beaconDB.SaveAttestationRecordsForValidators(ctx, atts))
|
|
|
|
|
|
|
|
// shuffle attestations
|
|
|
|
rand.Shuffle(count, func(i, j int) { atts[i], atts[j] = atts[j], atts[i] })
|
|
|
|
|
|
|
|
b.ReportAllocs()
|
|
|
|
b.StartTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
|
|
_, err := beaconDB.CheckAttesterDoubleVotes(ctx, atts)
|
|
|
|
require.NoError(b, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-26 14:40:12 +00:00
|
|
|
func createProposalWrapper(t *testing.T, slot primitives.Slot, proposerIndex primitives.ValidatorIndex, signingRoot []byte) *slashertypes.SignedBlockHeaderWrapper {
|
2021-04-23 17:06:13 +00:00
|
|
|
header := ðpb.BeaconBlockHeader{
|
|
|
|
Slot: slot,
|
|
|
|
ProposerIndex: proposerIndex,
|
|
|
|
ParentRoot: params.BeaconConfig().ZeroHash[:],
|
|
|
|
StateRoot: bytesutil.PadTo(signingRoot, 32),
|
|
|
|
BodyRoot: params.BeaconConfig().ZeroHash[:],
|
|
|
|
}
|
|
|
|
signRoot, err := header.HashTreeRoot()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
return &slashertypes.SignedBlockHeaderWrapper{
|
|
|
|
SignedBeaconBlockHeader: ðpb.SignedBeaconBlockHeader{
|
|
|
|
Header: header,
|
|
|
|
Signature: params.BeaconConfig().EmptySignature[:],
|
|
|
|
},
|
|
|
|
SigningRoot: signRoot,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-26 14:40:12 +00:00
|
|
|
func createAttestationWrapper(source, target primitives.Epoch, indices []uint64, signingRoot []byte) *slashertypes.IndexedAttestationWrapper {
|
2021-04-23 17:06:13 +00:00
|
|
|
signRoot := bytesutil.ToBytes32(signingRoot)
|
|
|
|
if signingRoot == nil {
|
|
|
|
signRoot = params.BeaconConfig().ZeroHash
|
|
|
|
}
|
|
|
|
data := ðpb.AttestationData{
|
|
|
|
BeaconBlockRoot: params.BeaconConfig().ZeroHash[:],
|
|
|
|
Source: ðpb.Checkpoint{
|
|
|
|
Epoch: source,
|
|
|
|
Root: params.BeaconConfig().ZeroHash[:],
|
|
|
|
},
|
|
|
|
Target: ðpb.Checkpoint{
|
|
|
|
Epoch: target,
|
|
|
|
Root: params.BeaconConfig().ZeroHash[:],
|
|
|
|
},
|
|
|
|
}
|
|
|
|
return &slashertypes.IndexedAttestationWrapper{
|
|
|
|
IndexedAttestation: ðpb.IndexedAttestation{
|
|
|
|
AttestingIndices: indices,
|
|
|
|
Data: data,
|
|
|
|
Signature: params.BeaconConfig().EmptySignature[:],
|
|
|
|
},
|
|
|
|
SigningRoot: signRoot,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func Test_encodeValidatorIndex(t *testing.T) {
|
|
|
|
tests := []struct {
|
|
|
|
name string
|
2023-01-26 14:40:12 +00:00
|
|
|
index primitives.ValidatorIndex
|
2021-04-23 17:06:13 +00:00
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "0",
|
2023-01-26 14:40:12 +00:00
|
|
|
index: primitives.ValidatorIndex(0),
|
2021-04-23 17:06:13 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "genesis_validator_count",
|
2023-01-26 14:40:12 +00:00
|
|
|
index: primitives.ValidatorIndex(params.BeaconConfig().MinGenesisActiveValidatorCount),
|
2021-04-23 17:06:13 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "max_possible_value",
|
2023-01-26 14:40:12 +00:00
|
|
|
index: primitives.ValidatorIndex(params.BeaconConfig().ValidatorRegistryLimit - 1),
|
2021-04-23 17:06:13 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
got := encodeValidatorIndex(tt.index)
|
|
|
|
encodedIndex := append(got[:5], 0, 0, 0)
|
|
|
|
decoded := binary.LittleEndian.Uint64(encodedIndex)
|
2023-01-26 14:40:12 +00:00
|
|
|
require.DeepEqual(t, tt.index, primitives.ValidatorIndex(decoded))
|
2021-04-23 17:06:13 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|