github.com/prysmaticlabs/prysm@v1.4.4/validator/db/kv/attester_protection_test.go (about)

     1  package kv
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  	"path/filepath"
     8  	"sync"
     9  	"testing"
    10  
    11  	types "github.com/prysmaticlabs/eth2-types"
    12  	ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
    13  	"github.com/prysmaticlabs/prysm/shared/bytesutil"
    14  	"github.com/prysmaticlabs/prysm/shared/testutil/assert"
    15  	"github.com/prysmaticlabs/prysm/shared/testutil/require"
    16  	logTest "github.com/sirupsen/logrus/hooks/test"
    17  	bolt "go.etcd.io/bbolt"
    18  )
    19  
    20  func TestPendingAttestationRecords_Flush(t *testing.T) {
    21  	queue := NewQueuedAttestationRecords()
    22  
    23  	// Add 5 atts
    24  	num := 5
    25  	for i := 0; i < num; i++ {
    26  		queue.Append(&AttestationRecord{
    27  			Target: types.Epoch(i),
    28  		})
    29  	}
    30  
    31  	res := queue.Flush()
    32  	assert.Equal(t, len(res), num, "Wrong number of flushed attestations")
    33  	assert.Equal(t, len(queue.records), 0, "Records were not cleared/flushed")
    34  }
    35  
    36  func TestPendingAttestationRecords_Len(t *testing.T) {
    37  	queue := NewQueuedAttestationRecords()
    38  	assert.Equal(t, queue.Len(), 0)
    39  	queue.Append(&AttestationRecord{})
    40  	assert.Equal(t, queue.Len(), 1)
    41  	queue.Flush()
    42  	assert.Equal(t, queue.Len(), 0)
    43  }
    44  
    45  func TestStore_CheckSlashableAttestation_DoubleVote(t *testing.T) {
    46  	ctx := context.Background()
    47  	numValidators := 1
    48  	pubKeys := make([][48]byte, numValidators)
    49  	validatorDB := setupDB(t, pubKeys)
    50  	tests := []struct {
    51  		name                string
    52  		existingAttestation *ethpb.IndexedAttestation
    53  		existingSigningRoot [32]byte
    54  		incomingAttestation *ethpb.IndexedAttestation
    55  		incomingSigningRoot [32]byte
    56  		want                bool
    57  	}{
    58  		{
    59  			name:                "different signing root at same target equals a double vote",
    60  			existingAttestation: createAttestation(0, 1 /* Target */),
    61  			existingSigningRoot: [32]byte{1},
    62  			incomingAttestation: createAttestation(0, 1 /* Target */),
    63  			incomingSigningRoot: [32]byte{2},
    64  			want:                true,
    65  		},
    66  		{
    67  			name:                "same signing root at same target is safe",
    68  			existingAttestation: createAttestation(0, 1 /* Target */),
    69  			existingSigningRoot: [32]byte{1},
    70  			incomingAttestation: createAttestation(0, 1 /* Target */),
    71  			incomingSigningRoot: [32]byte{1},
    72  			want:                false,
    73  		},
    74  		{
    75  			name:                "different signing root at different target is safe",
    76  			existingAttestation: createAttestation(0, 1 /* Target */),
    77  			existingSigningRoot: [32]byte{1},
    78  			incomingAttestation: createAttestation(0, 2 /* Target */),
    79  			incomingSigningRoot: [32]byte{2},
    80  			want:                false,
    81  		},
    82  		{
    83  			name:                "no data stored at target should not be considered a double vote",
    84  			existingAttestation: createAttestation(0, 1 /* Target */),
    85  			existingSigningRoot: [32]byte{1},
    86  			incomingAttestation: createAttestation(0, 2 /* Target */),
    87  			incomingSigningRoot: [32]byte{1},
    88  			want:                false,
    89  		},
    90  	}
    91  	for _, tt := range tests {
    92  		t.Run(tt.name, func(t *testing.T) {
    93  			err := validatorDB.SaveAttestationForPubKey(
    94  				ctx,
    95  				pubKeys[0],
    96  				tt.existingSigningRoot,
    97  				tt.existingAttestation,
    98  			)
    99  			require.NoError(t, err)
   100  			slashingKind, err := validatorDB.CheckSlashableAttestation(
   101  				ctx,
   102  				pubKeys[0],
   103  				tt.incomingSigningRoot,
   104  				tt.incomingAttestation,
   105  			)
   106  			if tt.want {
   107  				require.NotNil(t, err)
   108  				assert.Equal(t, DoubleVote, slashingKind)
   109  			} else {
   110  				require.NoError(t, err)
   111  			}
   112  		})
   113  	}
   114  }
   115  
   116  func TestStore_CheckSlashableAttestation_SurroundVote_MultipleTargetsPerSource(t *testing.T) {
   117  	ctx := context.Background()
   118  	numValidators := 1
   119  	pubKeys := make([][48]byte, numValidators)
   120  	validatorDB := setupDB(t, pubKeys)
   121  
   122  	// Create an attestation with source 1 and target 50, save it.
   123  	firstAtt := createAttestation(1, 50)
   124  	err := validatorDB.SaveAttestationForPubKey(ctx, pubKeys[0], [32]byte{0}, firstAtt)
   125  	require.NoError(t, err)
   126  
   127  	// Create an attestation with source 1 and target 100, save it.
   128  	secondAtt := createAttestation(1, 100)
   129  	err = validatorDB.SaveAttestationForPubKey(ctx, pubKeys[0], [32]byte{1}, secondAtt)
   130  	require.NoError(t, err)
   131  
   132  	// Create an attestation with source 0 and target 51, which should surround
   133  	// our first attestation. Given there can be multiple attested target epochs per
   134  	// source epoch, we expect our logic to be able to catch this slashable offense.
   135  	evilAtt := createAttestation(firstAtt.Data.Source.Epoch-1, firstAtt.Data.Target.Epoch+1)
   136  	slashable, err := validatorDB.CheckSlashableAttestation(ctx, pubKeys[0], [32]byte{2}, evilAtt)
   137  	require.NotNil(t, err)
   138  	assert.Equal(t, SurroundingVote, slashable)
   139  }
   140  
   141  func TestStore_CheckSlashableAttestation_SurroundVote_54kEpochs(t *testing.T) {
   142  	ctx := context.Background()
   143  	numValidators := 1
   144  	numEpochs := types.Epoch(54000)
   145  	pubKeys := make([][48]byte, numValidators)
   146  	validatorDB := setupDB(t, pubKeys)
   147  
   148  	// Attest to every (source = epoch, target = epoch + 1) sequential pair
   149  	// since genesis up to and including the weak subjectivity period epoch (54,000).
   150  	err := validatorDB.update(func(tx *bolt.Tx) error {
   151  		bucket := tx.Bucket(pubKeysBucket)
   152  		pkBucket, err := bucket.CreateBucketIfNotExists(pubKeys[0][:])
   153  		if err != nil {
   154  			return err
   155  		}
   156  		sourceEpochsBucket, err := pkBucket.CreateBucketIfNotExists(attestationSourceEpochsBucket)
   157  		if err != nil {
   158  			return err
   159  		}
   160  		for epoch := types.Epoch(1); epoch < numEpochs; epoch++ {
   161  			att := createAttestation(epoch-1, epoch)
   162  			sourceEpoch := bytesutil.EpochToBytesBigEndian(att.Data.Source.Epoch)
   163  			targetEpoch := bytesutil.EpochToBytesBigEndian(att.Data.Target.Epoch)
   164  			if err := sourceEpochsBucket.Put(sourceEpoch, targetEpoch); err != nil {
   165  				return err
   166  			}
   167  		}
   168  		return nil
   169  	})
   170  	require.NoError(t, err)
   171  
   172  	tests := []struct {
   173  		name        string
   174  		signingRoot [32]byte
   175  		attestation *ethpb.IndexedAttestation
   176  		want        SlashingKind
   177  	}{
   178  		{
   179  			name:        "surround vote at half of the weak subjectivity period",
   180  			signingRoot: [32]byte{},
   181  			attestation: createAttestation(numEpochs/2, numEpochs),
   182  			want:        SurroundingVote,
   183  		},
   184  		{
   185  			name:        "spanning genesis to weak subjectivity period surround vote",
   186  			signingRoot: [32]byte{},
   187  			attestation: createAttestation(0, numEpochs),
   188  			want:        SurroundingVote,
   189  		},
   190  		{
   191  			name:        "simple surround vote at end of weak subjectivity period",
   192  			signingRoot: [32]byte{},
   193  			attestation: createAttestation(numEpochs-3, numEpochs),
   194  			want:        SurroundingVote,
   195  		},
   196  		{
   197  			name:        "non-slashable vote",
   198  			signingRoot: [32]byte{},
   199  			attestation: createAttestation(numEpochs, numEpochs+1),
   200  			want:        NotSlashable,
   201  		},
   202  	}
   203  	for _, tt := range tests {
   204  		t.Run(tt.name, func(t *testing.T) {
   205  			slashingKind, err := validatorDB.CheckSlashableAttestation(ctx, pubKeys[0], tt.signingRoot, tt.attestation)
   206  			if tt.want != NotSlashable {
   207  				require.NotNil(t, err)
   208  			}
   209  			assert.Equal(t, tt.want, slashingKind)
   210  		})
   211  	}
   212  }
   213  
   214  func TestLowestSignedSourceEpoch_SaveRetrieve(t *testing.T) {
   215  	ctx := context.Background()
   216  	validatorDB, err := NewKVStore(ctx, t.TempDir(), &Config{})
   217  	require.NoError(t, err, "Failed to instantiate DB")
   218  	t.Cleanup(func() {
   219  		require.NoError(t, validatorDB.Close(), "Failed to close database")
   220  		require.NoError(t, validatorDB.ClearDB(), "Failed to clear database")
   221  	})
   222  	p0 := [48]byte{0}
   223  	p1 := [48]byte{1}
   224  	// Can save.
   225  	require.NoError(
   226  		t,
   227  		validatorDB.SaveAttestationForPubKey(ctx, p0, [32]byte{}, createAttestation(100, 101)),
   228  	)
   229  	require.NoError(
   230  		t,
   231  		validatorDB.SaveAttestationForPubKey(ctx, p1, [32]byte{}, createAttestation(200, 201)),
   232  	)
   233  	got, _, err := validatorDB.LowestSignedSourceEpoch(ctx, p0)
   234  	require.NoError(t, err)
   235  	require.Equal(t, types.Epoch(100), got)
   236  	got, _, err = validatorDB.LowestSignedSourceEpoch(ctx, p1)
   237  	require.NoError(t, err)
   238  	require.Equal(t, types.Epoch(200), got)
   239  
   240  	// Can replace.
   241  	require.NoError(
   242  		t,
   243  		validatorDB.SaveAttestationForPubKey(ctx, p0, [32]byte{}, createAttestation(99, 100)),
   244  	)
   245  	require.NoError(
   246  		t,
   247  		validatorDB.SaveAttestationForPubKey(ctx, p1, [32]byte{}, createAttestation(199, 200)),
   248  	)
   249  	got, _, err = validatorDB.LowestSignedSourceEpoch(ctx, p0)
   250  	require.NoError(t, err)
   251  	require.Equal(t, types.Epoch(99), got)
   252  	got, _, err = validatorDB.LowestSignedSourceEpoch(ctx, p1)
   253  	require.NoError(t, err)
   254  	require.Equal(t, types.Epoch(199), got)
   255  
   256  	// Can not replace.
   257  	require.NoError(
   258  		t,
   259  		validatorDB.SaveAttestationForPubKey(ctx, p0, [32]byte{}, createAttestation(100, 101)),
   260  	)
   261  	require.NoError(
   262  		t,
   263  		validatorDB.SaveAttestationForPubKey(ctx, p1, [32]byte{}, createAttestation(200, 201)),
   264  	)
   265  	got, _, err = validatorDB.LowestSignedSourceEpoch(ctx, p0)
   266  	require.NoError(t, err)
   267  	require.Equal(t, types.Epoch(99), got)
   268  	got, _, err = validatorDB.LowestSignedSourceEpoch(ctx, p1)
   269  	require.NoError(t, err)
   270  	require.Equal(t, types.Epoch(199), got)
   271  }
   272  
   273  func TestLowestSignedTargetEpoch_SaveRetrieveReplace(t *testing.T) {
   274  	ctx := context.Background()
   275  	validatorDB, err := NewKVStore(ctx, t.TempDir(), &Config{})
   276  	require.NoError(t, err, "Failed to instantiate DB")
   277  	t.Cleanup(func() {
   278  		require.NoError(t, validatorDB.Close(), "Failed to close database")
   279  		require.NoError(t, validatorDB.ClearDB(), "Failed to clear database")
   280  	})
   281  	p0 := [48]byte{0}
   282  	p1 := [48]byte{1}
   283  	// Can save.
   284  	require.NoError(
   285  		t,
   286  		validatorDB.SaveAttestationForPubKey(ctx, p0, [32]byte{}, createAttestation(99, 100)),
   287  	)
   288  	require.NoError(
   289  		t,
   290  		validatorDB.SaveAttestationForPubKey(ctx, p1, [32]byte{}, createAttestation(199, 200)),
   291  	)
   292  	got, _, err := validatorDB.LowestSignedTargetEpoch(ctx, p0)
   293  	require.NoError(t, err)
   294  	require.Equal(t, types.Epoch(100), got)
   295  	got, _, err = validatorDB.LowestSignedTargetEpoch(ctx, p1)
   296  	require.NoError(t, err)
   297  	require.Equal(t, types.Epoch(200), got)
   298  
   299  	// Can replace.
   300  	require.NoError(
   301  		t,
   302  		validatorDB.SaveAttestationForPubKey(ctx, p0, [32]byte{}, createAttestation(98, 99)),
   303  	)
   304  	require.NoError(
   305  		t,
   306  		validatorDB.SaveAttestationForPubKey(ctx, p1, [32]byte{}, createAttestation(198, 199)),
   307  	)
   308  	got, _, err = validatorDB.LowestSignedTargetEpoch(ctx, p0)
   309  	require.NoError(t, err)
   310  	require.Equal(t, types.Epoch(99), got)
   311  	got, _, err = validatorDB.LowestSignedTargetEpoch(ctx, p1)
   312  	require.NoError(t, err)
   313  	require.Equal(t, types.Epoch(199), got)
   314  
   315  	// Can not replace.
   316  	require.NoError(
   317  		t,
   318  		validatorDB.SaveAttestationForPubKey(ctx, p0, [32]byte{}, createAttestation(99, 100)),
   319  	)
   320  	require.NoError(
   321  		t,
   322  		validatorDB.SaveAttestationForPubKey(ctx, p1, [32]byte{}, createAttestation(199, 200)),
   323  	)
   324  	got, _, err = validatorDB.LowestSignedTargetEpoch(ctx, p0)
   325  	require.NoError(t, err)
   326  	require.Equal(t, types.Epoch(99), got)
   327  	got, _, err = validatorDB.LowestSignedTargetEpoch(ctx, p1)
   328  	require.NoError(t, err)
   329  	require.Equal(t, types.Epoch(199), got)
   330  }
   331  
   332  func TestStore_SaveAttestationsForPubKey(t *testing.T) {
   333  	ctx := context.Background()
   334  	numValidators := 1
   335  	pubKeys := make([][48]byte, numValidators)
   336  	validatorDB := setupDB(t, pubKeys)
   337  	atts := make([]*ethpb.IndexedAttestation, 0)
   338  	signingRoots := make([][32]byte, 0)
   339  	for i := types.Epoch(1); i < 10; i++ {
   340  		atts = append(atts, createAttestation(i-1, i))
   341  		var sr [32]byte
   342  		copy(sr[:], fmt.Sprintf("%d", i))
   343  		signingRoots = append(signingRoots, sr)
   344  	}
   345  	err := validatorDB.SaveAttestationsForPubKey(
   346  		ctx,
   347  		pubKeys[0],
   348  		signingRoots[:1],
   349  		atts,
   350  	)
   351  	require.ErrorContains(t, "does not match number of attestations", err)
   352  	err = validatorDB.SaveAttestationsForPubKey(
   353  		ctx,
   354  		pubKeys[0],
   355  		signingRoots,
   356  		atts,
   357  	)
   358  	require.NoError(t, err)
   359  	for _, att := range atts {
   360  		// Ensure the same attestations but different signing root lead to double votes.
   361  		slashingKind, err := validatorDB.CheckSlashableAttestation(
   362  			ctx,
   363  			pubKeys[0],
   364  			[32]byte{},
   365  			att,
   366  		)
   367  		require.NotNil(t, err)
   368  		require.Equal(t, DoubleVote, slashingKind)
   369  	}
   370  }
   371  
   372  func TestSaveAttestationForPubKey_BatchWrites_FullCapacity(t *testing.T) {
   373  	hook := logTest.NewGlobal()
   374  	ctx, cancel := context.WithCancel(context.Background())
   375  	defer cancel()
   376  	numValidators := attestationBatchCapacity
   377  	pubKeys := make([][48]byte, numValidators)
   378  	validatorDB := setupDB(t, pubKeys)
   379  
   380  	// For each public key, we attempt to save an attestation with signing root.
   381  	var wg sync.WaitGroup
   382  	for i, pubKey := range pubKeys {
   383  		wg.Add(1)
   384  		go func(j types.Epoch, pk [48]byte, w *sync.WaitGroup) {
   385  			defer w.Done()
   386  			var signingRoot [32]byte
   387  			copy(signingRoot[:], fmt.Sprintf("%d", j))
   388  			att := createAttestation(j, j+1)
   389  			err := validatorDB.SaveAttestationForPubKey(ctx, pk, signingRoot, att)
   390  			require.NoError(t, err)
   391  		}(types.Epoch(i), pubKey, &wg)
   392  	}
   393  	wg.Wait()
   394  
   395  	// We verify that we reached the max capacity of batched attestations
   396  	// before we are required to force flush them to the DB.
   397  	require.LogsContain(t, hook, "Reached max capacity of batched attestation records")
   398  	require.LogsDoNotContain(t, hook, "Batched attestation records write interval reached")
   399  	require.LogsContain(t, hook, "Successfully flushed batched attestations to DB")
   400  	require.Equal(t, 0, validatorDB.batchedAttestations.Len())
   401  
   402  	// We then verify all the data we wanted to save is indeed saved to disk.
   403  	err := validatorDB.view(func(tx *bolt.Tx) error {
   404  		bucket := tx.Bucket(pubKeysBucket)
   405  		for i, pubKey := range pubKeys {
   406  			var signingRoot [32]byte
   407  			copy(signingRoot[:], fmt.Sprintf("%d", i))
   408  			pkBucket := bucket.Bucket(pubKey[:])
   409  			signingRootsBucket := pkBucket.Bucket(attestationSigningRootsBucket)
   410  			sourceEpochsBucket := pkBucket.Bucket(attestationSourceEpochsBucket)
   411  
   412  			source := bytesutil.Uint64ToBytesBigEndian(uint64(i))
   413  			target := bytesutil.Uint64ToBytesBigEndian(uint64(i) + 1)
   414  			savedSigningRoot := signingRootsBucket.Get(target)
   415  			require.DeepEqual(t, signingRoot[:], savedSigningRoot)
   416  			savedTarget := sourceEpochsBucket.Get(source)
   417  			require.DeepEqual(t, signingRoot[:], savedSigningRoot)
   418  			require.DeepEqual(t, target, savedTarget)
   419  		}
   420  		return nil
   421  	})
   422  	require.NoError(t, err)
   423  }
   424  
   425  func TestSaveAttestationForPubKey_BatchWrites_LowCapacity_TimerReached(t *testing.T) {
   426  	hook := logTest.NewGlobal()
   427  	ctx, cancel := context.WithCancel(context.Background())
   428  	defer cancel()
   429  	// Number of validators equal to half the total capacity
   430  	// of batch attestation processing. This will allow us to
   431  	// test force flushing to the DB based on a timer instead
   432  	// of the max capacity being reached.
   433  	numValidators := attestationBatchCapacity / 2
   434  	pubKeys := make([][48]byte, numValidators)
   435  	validatorDB := setupDB(t, pubKeys)
   436  
   437  	// For each public key, we attempt to save an attestation with signing root.
   438  	var wg sync.WaitGroup
   439  	for i, pubKey := range pubKeys {
   440  		wg.Add(1)
   441  		go func(j types.Epoch, pk [48]byte, w *sync.WaitGroup) {
   442  			defer w.Done()
   443  			var signingRoot [32]byte
   444  			copy(signingRoot[:], fmt.Sprintf("%d", j))
   445  			att := createAttestation(j, j+1)
   446  			err := validatorDB.SaveAttestationForPubKey(ctx, pk, signingRoot, att)
   447  			require.NoError(t, err)
   448  		}(types.Epoch(i), pubKey, &wg)
   449  	}
   450  	wg.Wait()
   451  
   452  	// We verify that we reached a timer interval for force flushing records
   453  	// before we are required to force flush them to the DB.
   454  	require.LogsDoNotContain(t, hook, "Reached max capacity of batched attestation records")
   455  	require.LogsContain(t, hook, "Batched attestation records write interval reached")
   456  	require.LogsContain(t, hook, "Successfully flushed batched attestations to DB")
   457  	require.Equal(t, 0, validatorDB.batchedAttestations.Len())
   458  
   459  	// We then verify all the data we wanted to save is indeed saved to disk.
   460  	err := validatorDB.view(func(tx *bolt.Tx) error {
   461  		bucket := tx.Bucket(pubKeysBucket)
   462  		for i, pubKey := range pubKeys {
   463  			var signingRoot [32]byte
   464  			copy(signingRoot[:], fmt.Sprintf("%d", i))
   465  			pkBucket := bucket.Bucket(pubKey[:])
   466  			signingRootsBucket := pkBucket.Bucket(attestationSigningRootsBucket)
   467  			sourceEpochsBucket := pkBucket.Bucket(attestationSourceEpochsBucket)
   468  
   469  			source := bytesutil.Uint64ToBytesBigEndian(uint64(i))
   470  			target := bytesutil.Uint64ToBytesBigEndian(uint64(i) + 1)
   471  			savedSigningRoot := signingRootsBucket.Get(target)
   472  			require.DeepEqual(t, signingRoot[:], savedSigningRoot)
   473  			savedTarget := sourceEpochsBucket.Get(source)
   474  			require.DeepEqual(t, signingRoot[:], savedSigningRoot)
   475  			require.DeepEqual(t, target, savedTarget)
   476  		}
   477  		return nil
   478  	})
   479  	require.NoError(t, err)
   480  }
   481  
   482  func BenchmarkStore_CheckSlashableAttestation_Surround_SafeAttestation_54kEpochs(b *testing.B) {
   483  	numValidators := 1
   484  	numEpochs := types.Epoch(54000)
   485  	pubKeys := make([][48]byte, numValidators)
   486  	benchCheckSurroundVote(b, pubKeys, numEpochs, false /* surround */)
   487  }
   488  
   489  func BenchmarkStore_CheckSurroundVote_Surround_Slashable_54kEpochs(b *testing.B) {
   490  	numValidators := 1
   491  	numEpochs := types.Epoch(54000)
   492  	pubKeys := make([][48]byte, numValidators)
   493  	benchCheckSurroundVote(b, pubKeys, numEpochs, true /* surround */)
   494  }
   495  
   496  func benchCheckSurroundVote(
   497  	b *testing.B,
   498  	pubKeys [][48]byte,
   499  	numEpochs types.Epoch,
   500  	shouldSurround bool,
   501  ) {
   502  	ctx := context.Background()
   503  	validatorDB, err := NewKVStore(ctx, filepath.Join(os.TempDir(), "benchsurroundvote"), &Config{
   504  		PubKeys: pubKeys,
   505  	})
   506  	require.NoError(b, err, "Failed to instantiate DB")
   507  	defer func() {
   508  		require.NoError(b, validatorDB.Close(), "Failed to close database")
   509  		require.NoError(b, validatorDB.ClearDB(), "Failed to clear database")
   510  	}()
   511  	// Every validator will have attested every (source, target) sequential pair
   512  	// since genesis up to and including the weak subjectivity period epoch (54,000).
   513  	err = validatorDB.update(func(tx *bolt.Tx) error {
   514  		for _, pubKey := range pubKeys {
   515  			bucket := tx.Bucket(pubKeysBucket)
   516  			pkBucket, err := bucket.CreateBucketIfNotExists(pubKey[:])
   517  			if err != nil {
   518  				return err
   519  			}
   520  			sourceEpochsBucket, err := pkBucket.CreateBucketIfNotExists(attestationSourceEpochsBucket)
   521  			if err != nil {
   522  				return err
   523  			}
   524  			for epoch := types.Epoch(1); epoch < numEpochs; epoch++ {
   525  				att := createAttestation(epoch-1, epoch)
   526  				sourceEpoch := bytesutil.EpochToBytesBigEndian(att.Data.Source.Epoch)
   527  				targetEpoch := bytesutil.EpochToBytesBigEndian(att.Data.Target.Epoch)
   528  				if err := sourceEpochsBucket.Put(sourceEpoch, targetEpoch); err != nil {
   529  					return err
   530  				}
   531  			}
   532  		}
   533  		return nil
   534  	})
   535  	require.NoError(b, err)
   536  
   537  	// Will surround many attestations.
   538  	var surroundingVote *ethpb.IndexedAttestation
   539  	if shouldSurround {
   540  		surroundingVote = createAttestation(numEpochs/2, numEpochs)
   541  	} else {
   542  		surroundingVote = createAttestation(numEpochs+1, numEpochs+2)
   543  	}
   544  	b.ResetTimer()
   545  	for i := 0; i < b.N; i++ {
   546  		for _, pubKey := range pubKeys {
   547  			slashingKind, err := validatorDB.CheckSlashableAttestation(ctx, pubKey, [32]byte{}, surroundingVote)
   548  			if shouldSurround {
   549  				require.NotNil(b, err)
   550  				assert.Equal(b, SurroundingVote, slashingKind)
   551  			} else {
   552  				require.NoError(b, err)
   553  			}
   554  		}
   555  	}
   556  }
   557  
   558  func createAttestation(source, target types.Epoch) *ethpb.IndexedAttestation {
   559  	return &ethpb.IndexedAttestation{
   560  		Data: &ethpb.AttestationData{
   561  			Source: &ethpb.Checkpoint{
   562  				Epoch: source,
   563  			},
   564  			Target: &ethpb.Checkpoint{
   565  				Epoch: target,
   566  			},
   567  		},
   568  	}
   569  }
   570  
   571  func TestStore_flushAttestationRecords_InProgress(t *testing.T) {
   572  	s := &Store{}
   573  	s.batchedAttestationsFlushInProgress.Set()
   574  
   575  	hook := logTest.NewGlobal()
   576  	s.flushAttestationRecords(context.Background(), nil)
   577  	assert.LogsContain(t, hook, "Attempted to flush attestation records when already in progress")
   578  }