prysm-pulse/validator/db/kv/attester_protection_test.go
Manu NALEPA 1112e01c06
Make Prysm VC compatible with the version v5.3.0 of the slashing protections interchange tests. (#13232)
* `TestStore_GenesisValidatorsRoot_ReadAndWrite`: Make all test cases independents.

In a test with multiple test cases, each test case should be independents.
(aka: Removing test case `A` should not impact test case `B`)

* `SaveGenesisValidatorsRoot`: Allow to overwrite the genesis validator root if the root is the same.

* `ProposalHistoryForSlot`: Add `signingRootExists`

Currently, it is not possible with `ProposalHistoryForSlot` to know if a
proposal is stored with and `0x00000....` signing root or with an empty
signing root. Both cases result to `proposalExists == true` and
`signingRoot == 0x00000`.

This commit adds a new return boolean: `signingRootExists`.

If a proposal has been saved with a `0x00000...` signing root, then:
- `proposalExists` is set to `true`, and
- `signingRootExists` is set to `true`, and
- `signingRoot` is set to `0x00000...`

If a proposal has been saved with an empty signing root, then:
- `proposalExists` is set to `true`, and
- `signingRootExists` is set to `false`, and
- (`signingRoot` is set to `0x00000...`)

* `ImportStandardProtectionJSON`: When importing EIP-3076 Slashing Protection Interchange Format, do not filter any more slashable keys.
Note: Those keys are still saved into the black-listed public keys list.

There is two reason not to do so:
- The EIP-3076 test cases do not know about Prysm's internal black-listed public keys list.
  Tests will expect, without looking into this internal black-listed public keys list,
  to deny a further signature. If we filter these keys from the DB (even if we keep them
  into the black-listed keys list), then some tests will fail.
- If we import a interchange file containing slashable keys and we filter them, then,
  if we re-export the DB, those slashing offences won't appear in the exported interchange
  file.

* `transformSignedBlocks`: Store an 0-len byte slice

When importing an EIP-3076 interchange format, and when no
signing root is specified into the file, we currently store a
`0x00000.....` signing root.

In such a case, instead storing `0x00000...`, this commit stores
a 0-len byte array, so we can differentiate real `0x000.....` signing
root and no signing-root at all.

* `slashableProposalCheck`: Manage lack of sign root

Currently, `slashableProposalCheck` does not really make a difference
between a `0x0000.....` signing root and a missing signing root.

(Signing roots can be missing when importing an EIP-3076 interchange
file.)

This commit differentiate, for  `slashableProposalCheck`, `0x0000....`
signing root and a missing signing root.

* `AttestationRecord.SigningRoot`: ==> `[]byte`

When importing attestations from EIP-3076 interchange format,
the signing root of an attestation may be missing.

Currently, Prysm consider any missing attestation signing root as
`0x000...`.
However, it may conflict with signing root which really are equal to
`0x000...`.

This commit transforms `AttestationRecord.SigningRoot` from `[32]byte` to
`[]byte`, and change the minimal set of functions (sic) to support this
new type.

* `CheckSlashableAttestation`: Empty signing root

Regarding slashing roots, 2 attestations are slashable, if:
- both signing roots are defined and differs, or
- one attestation exists, but without a signing root

* `filterSlashablePubKeysFromAttestations`: Err sort

Rergarding `CheckSlashableAttestation`, we consider that:
- If slashable == NotSlashable and err != nil, then CheckSlashableAttestation
failed.
- If slashable != NotSlashable, then err contains the reason why the attestation
is slashable.

* `setupEIP3076SpecTests`: Update to `v5.3.0`

This commit:
- Updates the version of EIP-3076 tests to `v.5.2.1`.
- Setups on anti-slashing DB per test case, instead per step.

* `ImportStandardProtectionJSON`: Reduce cycl cmplxt

* `AttestationHistoryForPubKey`: copy signing root

BoltDB documentation specifies:
| Byte slices returned from Bolt are only valid during a transaction.
| Once the transaction has been committed or rolled back then the memory
| they point to can be reused by a new page or can be unmapped
| from virtual memory and you'll see an unexpected fault address panic
| when accessing it.
2023-12-04 17:10:32 +00:00

579 lines
19 KiB
Go

package kv
import (
"context"
"fmt"
"path/filepath"
"sync"
"testing"
fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams"
"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"
logTest "github.com/sirupsen/logrus/hooks/test"
bolt "go.etcd.io/bbolt"
)
func TestPendingAttestationRecords_Flush(t *testing.T) {
queue := NewQueuedAttestationRecords()
// Add 5 atts
num := 5
for i := 0; i < num; i++ {
queue.Append(&AttestationRecord{
Target: primitives.Epoch(i),
})
}
res := queue.Flush()
assert.Equal(t, len(res), num, "Wrong number of flushed attestations")
assert.Equal(t, len(queue.records), 0, "Records were not cleared/flushed")
}
func TestPendingAttestationRecords_Len(t *testing.T) {
queue := NewQueuedAttestationRecords()
assert.Equal(t, queue.Len(), 0)
queue.Append(&AttestationRecord{})
assert.Equal(t, queue.Len(), 1)
queue.Flush()
assert.Equal(t, queue.Len(), 0)
}
func TestStore_CheckSlashableAttestation_DoubleVote(t *testing.T) {
ctx := context.Background()
numValidators := 1
pubKeys := make([][fieldparams.BLSPubkeyLength]byte, numValidators)
validatorDB := setupDB(t, pubKeys)
tests := []struct {
name string
existingAttestation *ethpb.IndexedAttestation
existingSigningRoot [32]byte
incomingAttestation *ethpb.IndexedAttestation
incomingSigningRoot [32]byte
want bool
}{
{
name: "different signing root at same target equals a double vote",
existingAttestation: createAttestation(0, 1 /* Target */),
existingSigningRoot: [32]byte{1},
incomingAttestation: createAttestation(0, 1 /* Target */),
incomingSigningRoot: [32]byte{2},
want: true,
},
{
name: "same signing root at same target is safe",
existingAttestation: createAttestation(0, 1 /* Target */),
existingSigningRoot: [32]byte{1},
incomingAttestation: createAttestation(0, 1 /* Target */),
incomingSigningRoot: [32]byte{1},
want: false,
},
{
name: "different signing root at different target is safe",
existingAttestation: createAttestation(0, 1 /* Target */),
existingSigningRoot: [32]byte{1},
incomingAttestation: createAttestation(0, 2 /* Target */),
incomingSigningRoot: [32]byte{2},
want: false,
},
{
name: "no data stored at target should not be considered a double vote",
existingAttestation: createAttestation(0, 1 /* Target */),
existingSigningRoot: [32]byte{1},
incomingAttestation: createAttestation(0, 2 /* Target */),
incomingSigningRoot: [32]byte{1},
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := validatorDB.SaveAttestationForPubKey(
ctx,
pubKeys[0],
tt.existingSigningRoot,
tt.existingAttestation,
)
require.NoError(t, err)
slashingKind, err := validatorDB.CheckSlashableAttestation(
ctx,
pubKeys[0],
tt.incomingSigningRoot[:],
tt.incomingAttestation,
)
if tt.want {
require.NotNil(t, err)
assert.Equal(t, DoubleVote, slashingKind)
} else {
require.NoError(t, err)
}
})
}
}
func TestStore_CheckSlashableAttestation_SurroundVote_MultipleTargetsPerSource(t *testing.T) {
ctx := context.Background()
numValidators := 1
pubKeys := make([][fieldparams.BLSPubkeyLength]byte, numValidators)
validatorDB := setupDB(t, pubKeys)
// Create an attestation with source 1 and target 50, save it.
firstAtt := createAttestation(1, 50)
err := validatorDB.SaveAttestationForPubKey(ctx, pubKeys[0], [32]byte{0}, firstAtt)
require.NoError(t, err)
// Create an attestation with source 1 and target 100, save it.
secondAtt := createAttestation(1, 100)
err = validatorDB.SaveAttestationForPubKey(ctx, pubKeys[0], [32]byte{1}, secondAtt)
require.NoError(t, err)
// Create an attestation with source 0 and target 51, which should surround
// our first attestation. Given there can be multiple attested target epochs per
// source epoch, we expect our logic to be able to catch this slashable offense.
evilAtt := createAttestation(firstAtt.Data.Source.Epoch-1, firstAtt.Data.Target.Epoch+1)
slashable, err := validatorDB.CheckSlashableAttestation(ctx, pubKeys[0], []byte{2}, evilAtt)
require.NotNil(t, err)
assert.Equal(t, SurroundingVote, slashable)
}
func TestStore_CheckSlashableAttestation_SurroundVote_54kEpochs(t *testing.T) {
ctx := context.Background()
numValidators := 1
numEpochs := primitives.Epoch(54000)
pubKeys := make([][fieldparams.BLSPubkeyLength]byte, numValidators)
validatorDB := setupDB(t, pubKeys)
// Attest to every (source = epoch, target = epoch + 1) sequential pair
// since genesis up to and including the weak subjectivity period epoch (54,000).
err := validatorDB.update(func(tx *bolt.Tx) error {
bucket := tx.Bucket(pubKeysBucket)
pkBucket, err := bucket.CreateBucketIfNotExists(pubKeys[0][:])
if err != nil {
return err
}
sourceEpochsBucket, err := pkBucket.CreateBucketIfNotExists(attestationSourceEpochsBucket)
if err != nil {
return err
}
for epoch := primitives.Epoch(1); epoch < numEpochs; epoch++ {
att := createAttestation(epoch-1, epoch)
sourceEpoch := bytesutil.EpochToBytesBigEndian(att.Data.Source.Epoch)
targetEpoch := bytesutil.EpochToBytesBigEndian(att.Data.Target.Epoch)
if err := sourceEpochsBucket.Put(sourceEpoch, targetEpoch); err != nil {
return err
}
}
return nil
})
require.NoError(t, err)
tests := []struct {
name string
signingRoot []byte
attestation *ethpb.IndexedAttestation
want SlashingKind
}{
{
name: "surround vote at half of the weak subjectivity period",
signingRoot: []byte{},
attestation: createAttestation(numEpochs/2, numEpochs),
want: SurroundingVote,
},
{
name: "spanning genesis to weak subjectivity period surround vote",
signingRoot: []byte{},
attestation: createAttestation(0, numEpochs),
want: SurroundingVote,
},
{
name: "simple surround vote at end of weak subjectivity period",
signingRoot: []byte{},
attestation: createAttestation(numEpochs-3, numEpochs),
want: SurroundingVote,
},
{
name: "non-slashable vote",
signingRoot: []byte{},
attestation: createAttestation(numEpochs, numEpochs+1),
want: NotSlashable,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
slashingKind, err := validatorDB.CheckSlashableAttestation(ctx, pubKeys[0], tt.signingRoot, tt.attestation)
if tt.want != NotSlashable {
require.NotNil(t, err)
}
assert.Equal(t, tt.want, slashingKind)
})
}
}
func TestLowestSignedSourceEpoch_SaveRetrieve(t *testing.T) {
ctx := context.Background()
validatorDB, err := NewKVStore(ctx, t.TempDir(), &Config{})
require.NoError(t, err, "Failed to instantiate DB")
t.Cleanup(func() {
require.NoError(t, validatorDB.Close(), "Failed to close database")
require.NoError(t, validatorDB.ClearDB(), "Failed to clear database")
})
p0 := [fieldparams.BLSPubkeyLength]byte{0}
p1 := [fieldparams.BLSPubkeyLength]byte{1}
// Can save.
require.NoError(
t,
validatorDB.SaveAttestationForPubKey(ctx, p0, [32]byte{}, createAttestation(100, 101)),
)
require.NoError(
t,
validatorDB.SaveAttestationForPubKey(ctx, p1, [32]byte{}, createAttestation(200, 201)),
)
got, _, err := validatorDB.LowestSignedSourceEpoch(ctx, p0)
require.NoError(t, err)
require.Equal(t, primitives.Epoch(100), got)
got, _, err = validatorDB.LowestSignedSourceEpoch(ctx, p1)
require.NoError(t, err)
require.Equal(t, primitives.Epoch(200), got)
// Can replace.
require.NoError(
t,
validatorDB.SaveAttestationForPubKey(ctx, p0, [32]byte{}, createAttestation(99, 100)),
)
require.NoError(
t,
validatorDB.SaveAttestationForPubKey(ctx, p1, [32]byte{}, createAttestation(199, 200)),
)
got, _, err = validatorDB.LowestSignedSourceEpoch(ctx, p0)
require.NoError(t, err)
require.Equal(t, primitives.Epoch(99), got)
got, _, err = validatorDB.LowestSignedSourceEpoch(ctx, p1)
require.NoError(t, err)
require.Equal(t, primitives.Epoch(199), got)
// Can not replace.
require.NoError(
t,
validatorDB.SaveAttestationForPubKey(ctx, p0, [32]byte{}, createAttestation(100, 101)),
)
require.NoError(
t,
validatorDB.SaveAttestationForPubKey(ctx, p1, [32]byte{}, createAttestation(200, 201)),
)
got, _, err = validatorDB.LowestSignedSourceEpoch(ctx, p0)
require.NoError(t, err)
require.Equal(t, primitives.Epoch(99), got)
got, _, err = validatorDB.LowestSignedSourceEpoch(ctx, p1)
require.NoError(t, err)
require.Equal(t, primitives.Epoch(199), got)
}
func TestLowestSignedTargetEpoch_SaveRetrieveReplace(t *testing.T) {
ctx := context.Background()
validatorDB, err := NewKVStore(ctx, t.TempDir(), &Config{})
require.NoError(t, err, "Failed to instantiate DB")
t.Cleanup(func() {
require.NoError(t, validatorDB.Close(), "Failed to close database")
require.NoError(t, validatorDB.ClearDB(), "Failed to clear database")
})
p0 := [fieldparams.BLSPubkeyLength]byte{0}
p1 := [fieldparams.BLSPubkeyLength]byte{1}
// Can save.
require.NoError(
t,
validatorDB.SaveAttestationForPubKey(ctx, p0, [32]byte{}, createAttestation(99, 100)),
)
require.NoError(
t,
validatorDB.SaveAttestationForPubKey(ctx, p1, [32]byte{}, createAttestation(199, 200)),
)
got, _, err := validatorDB.LowestSignedTargetEpoch(ctx, p0)
require.NoError(t, err)
require.Equal(t, primitives.Epoch(100), got)
got, _, err = validatorDB.LowestSignedTargetEpoch(ctx, p1)
require.NoError(t, err)
require.Equal(t, primitives.Epoch(200), got)
// Can replace.
require.NoError(
t,
validatorDB.SaveAttestationForPubKey(ctx, p0, [32]byte{}, createAttestation(98, 99)),
)
require.NoError(
t,
validatorDB.SaveAttestationForPubKey(ctx, p1, [32]byte{}, createAttestation(198, 199)),
)
got, _, err = validatorDB.LowestSignedTargetEpoch(ctx, p0)
require.NoError(t, err)
require.Equal(t, primitives.Epoch(99), got)
got, _, err = validatorDB.LowestSignedTargetEpoch(ctx, p1)
require.NoError(t, err)
require.Equal(t, primitives.Epoch(199), got)
// Can not replace.
require.NoError(
t,
validatorDB.SaveAttestationForPubKey(ctx, p0, [32]byte{}, createAttestation(99, 100)),
)
require.NoError(
t,
validatorDB.SaveAttestationForPubKey(ctx, p1, [32]byte{}, createAttestation(199, 200)),
)
got, _, err = validatorDB.LowestSignedTargetEpoch(ctx, p0)
require.NoError(t, err)
require.Equal(t, primitives.Epoch(99), got)
got, _, err = validatorDB.LowestSignedTargetEpoch(ctx, p1)
require.NoError(t, err)
require.Equal(t, primitives.Epoch(199), got)
}
func TestStore_SaveAttestationsForPubKey(t *testing.T) {
ctx := context.Background()
numValidators := 1
pubKeys := make([][fieldparams.BLSPubkeyLength]byte, numValidators)
validatorDB := setupDB(t, pubKeys)
atts := make([]*ethpb.IndexedAttestation, 0)
signingRoots := make([][]byte, 0)
for i := primitives.Epoch(1); i < 10; i++ {
atts = append(atts, createAttestation(i-1, i))
var sr []byte
copy(sr, fmt.Sprintf("%d", i))
signingRoots = append(signingRoots, sr)
}
err := validatorDB.SaveAttestationsForPubKey(
ctx,
pubKeys[0],
signingRoots[:1],
atts,
)
require.ErrorContains(t, "does not match number of attestations", err)
err = validatorDB.SaveAttestationsForPubKey(
ctx,
pubKeys[0],
signingRoots,
atts,
)
require.NoError(t, err)
for _, att := range atts {
// Ensure the same attestations but different signing root lead to double votes.
slashingKind, err := validatorDB.CheckSlashableAttestation(
ctx,
pubKeys[0],
[]byte{},
att,
)
require.NotNil(t, err)
require.Equal(t, DoubleVote, slashingKind)
}
}
func TestSaveAttestationForPubKey_BatchWrites_FullCapacity(t *testing.T) {
hook := logTest.NewGlobal()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
numValidators := attestationBatchCapacity
pubKeys := make([][fieldparams.BLSPubkeyLength]byte, numValidators)
validatorDB := setupDB(t, pubKeys)
// For each public key, we attempt to save an attestation with signing root.
var wg sync.WaitGroup
for i, pubKey := range pubKeys {
wg.Add(1)
go func(j primitives.Epoch, pk [fieldparams.BLSPubkeyLength]byte, w *sync.WaitGroup) {
defer w.Done()
var signingRoot [32]byte
copy(signingRoot[:], fmt.Sprintf("%d", j))
att := createAttestation(j, j+1)
err := validatorDB.SaveAttestationForPubKey(ctx, pk, signingRoot, att)
require.NoError(t, err)
}(primitives.Epoch(i), pubKey, &wg)
}
wg.Wait()
// We verify that we reached the max capacity of batched attestations
// before we are required to force flush them to the DB.
require.LogsContain(t, hook, "Reached max capacity of batched attestation records")
require.LogsDoNotContain(t, hook, "Batched attestation records write interval reached")
require.LogsContain(t, hook, "Successfully flushed batched attestations to DB")
require.Equal(t, 0, validatorDB.batchedAttestations.Len())
// We then verify all the data we wanted to save is indeed saved to disk.
err := validatorDB.view(func(tx *bolt.Tx) error {
bucket := tx.Bucket(pubKeysBucket)
for i, pubKey := range pubKeys {
var signingRoot [32]byte
copy(signingRoot[:], fmt.Sprintf("%d", i))
pkBucket := bucket.Bucket(pubKey[:])
signingRootsBucket := pkBucket.Bucket(attestationSigningRootsBucket)
sourceEpochsBucket := pkBucket.Bucket(attestationSourceEpochsBucket)
source := bytesutil.Uint64ToBytesBigEndian(uint64(i))
target := bytesutil.Uint64ToBytesBigEndian(uint64(i) + 1)
savedSigningRoot := signingRootsBucket.Get(target)
require.DeepEqual(t, signingRoot[:], savedSigningRoot)
savedTarget := sourceEpochsBucket.Get(source)
require.DeepEqual(t, signingRoot[:], savedSigningRoot)
require.DeepEqual(t, target, savedTarget)
}
return nil
})
require.NoError(t, err)
}
func TestSaveAttestationForPubKey_BatchWrites_LowCapacity_TimerReached(t *testing.T) {
hook := logTest.NewGlobal()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// Number of validators equal to half the total capacity
// of batch attestation processing. This will allow us to
// test force flushing to the DB based on a timer instead
// of the max capacity being reached.
numValidators := attestationBatchCapacity / 2
pubKeys := make([][fieldparams.BLSPubkeyLength]byte, numValidators)
validatorDB := setupDB(t, pubKeys)
// For each public key, we attempt to save an attestation with signing root.
var wg sync.WaitGroup
for i, pubKey := range pubKeys {
wg.Add(1)
go func(j primitives.Epoch, pk [fieldparams.BLSPubkeyLength]byte, w *sync.WaitGroup) {
defer w.Done()
var signingRoot [32]byte
copy(signingRoot[:], fmt.Sprintf("%d", j))
att := createAttestation(j, j+1)
err := validatorDB.SaveAttestationForPubKey(ctx, pk, signingRoot, att)
require.NoError(t, err)
}(primitives.Epoch(i), pubKey, &wg)
}
wg.Wait()
// We verify that we reached a timer interval for force flushing records
// before we are required to force flush them to the DB.
require.LogsDoNotContain(t, hook, "Reached max capacity of batched attestation records")
require.LogsContain(t, hook, "Batched attestation records write interval reached")
require.LogsContain(t, hook, "Successfully flushed batched attestations to DB")
require.Equal(t, 0, validatorDB.batchedAttestations.Len())
// We then verify all the data we wanted to save is indeed saved to disk.
err := validatorDB.view(func(tx *bolt.Tx) error {
bucket := tx.Bucket(pubKeysBucket)
for i, pubKey := range pubKeys {
var signingRoot [32]byte
copy(signingRoot[:], fmt.Sprintf("%d", i))
pkBucket := bucket.Bucket(pubKey[:])
signingRootsBucket := pkBucket.Bucket(attestationSigningRootsBucket)
sourceEpochsBucket := pkBucket.Bucket(attestationSourceEpochsBucket)
source := bytesutil.Uint64ToBytesBigEndian(uint64(i))
target := bytesutil.Uint64ToBytesBigEndian(uint64(i) + 1)
savedSigningRoot := signingRootsBucket.Get(target)
require.DeepEqual(t, signingRoot[:], savedSigningRoot)
savedTarget := sourceEpochsBucket.Get(source)
require.DeepEqual(t, signingRoot[:], savedSigningRoot)
require.DeepEqual(t, target, savedTarget)
}
return nil
})
require.NoError(t, err)
}
func BenchmarkStore_CheckSlashableAttestation_Surround_SafeAttestation_54kEpochs(b *testing.B) {
numValidators := 1
numEpochs := primitives.Epoch(54000)
pubKeys := make([][fieldparams.BLSPubkeyLength]byte, numValidators)
benchCheckSurroundVote(b, pubKeys, numEpochs, false /* surround */)
}
func BenchmarkStore_CheckSurroundVote_Surround_Slashable_54kEpochs(b *testing.B) {
numValidators := 1
numEpochs := primitives.Epoch(54000)
pubKeys := make([][fieldparams.BLSPubkeyLength]byte, numValidators)
benchCheckSurroundVote(b, pubKeys, numEpochs, true /* surround */)
}
func benchCheckSurroundVote(
b *testing.B,
pubKeys [][fieldparams.BLSPubkeyLength]byte,
numEpochs primitives.Epoch,
shouldSurround bool,
) {
ctx := context.Background()
validatorDB, err := NewKVStore(ctx, filepath.Join(b.TempDir(), "benchsurroundvote"), &Config{
PubKeys: pubKeys,
})
require.NoError(b, err, "Failed to instantiate DB")
defer func() {
require.NoError(b, validatorDB.Close(), "Failed to close database")
require.NoError(b, validatorDB.ClearDB(), "Failed to clear database")
}()
// Every validator will have attested every (source, target) sequential pair
// since genesis up to and including the weak subjectivity period epoch (54,000).
err = validatorDB.update(func(tx *bolt.Tx) error {
for _, pubKey := range pubKeys {
bucket := tx.Bucket(pubKeysBucket)
pkBucket, err := bucket.CreateBucketIfNotExists(pubKey[:])
if err != nil {
return err
}
sourceEpochsBucket, err := pkBucket.CreateBucketIfNotExists(attestationSourceEpochsBucket)
if err != nil {
return err
}
for epoch := primitives.Epoch(1); epoch < numEpochs; epoch++ {
att := createAttestation(epoch-1, epoch)
sourceEpoch := bytesutil.EpochToBytesBigEndian(att.Data.Source.Epoch)
targetEpoch := bytesutil.EpochToBytesBigEndian(att.Data.Target.Epoch)
if err := sourceEpochsBucket.Put(sourceEpoch, targetEpoch); err != nil {
return err
}
}
}
return nil
})
require.NoError(b, err)
// Will surround many attestations.
var surroundingVote *ethpb.IndexedAttestation
if shouldSurround {
surroundingVote = createAttestation(numEpochs/2, numEpochs)
} else {
surroundingVote = createAttestation(numEpochs+1, numEpochs+2)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
for _, pubKey := range pubKeys {
slashingKind, err := validatorDB.CheckSlashableAttestation(ctx, pubKey, []byte{}, surroundingVote)
if shouldSurround {
require.NotNil(b, err)
assert.Equal(b, SurroundingVote, slashingKind)
} else {
require.NoError(b, err)
}
}
}
}
func createAttestation(source, target primitives.Epoch) *ethpb.IndexedAttestation {
return &ethpb.IndexedAttestation{
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{
Epoch: source,
},
Target: &ethpb.Checkpoint{
Epoch: target,
},
},
}
}
func TestStore_flushAttestationRecords_InProgress(t *testing.T) {
s := &Store{}
s.batchedAttestationsFlushInProgress.Set()
hook := logTest.NewGlobal()
s.flushAttestationRecords(context.Background(), nil)
assert.LogsContain(t, hook, "Attempted to flush attestation records when already in progress")
}