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

     1  package kv
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"testing"
     7  
     8  	types "github.com/prysmaticlabs/eth2-types"
     9  	"github.com/prysmaticlabs/prysm/shared/bytesutil"
    10  	"github.com/prysmaticlabs/prysm/shared/params"
    11  	"github.com/prysmaticlabs/prysm/shared/testutil/require"
    12  	bolt "go.etcd.io/bbolt"
    13  )
    14  
    15  func TestPruneAttestations_NoPruning(t *testing.T) {
    16  	pubKey := [48]byte{1}
    17  	validatorDB := setupDB(t, [][48]byte{pubKey})
    18  
    19  	// Write attesting history for every single epoch
    20  	// since genesis to a specified number of epochs.
    21  	numEpochs := params.BeaconConfig().SlashingProtectionPruningEpochs - 1
    22  	err := setupAttestationsForEveryEpoch(t, validatorDB, pubKey, numEpochs)
    23  	require.NoError(t, err)
    24  
    25  	// Next, attempt to prune and realize that we still have all epochs intact
    26  	err = validatorDB.PruneAttestations(context.Background())
    27  	require.NoError(t, err)
    28  
    29  	startEpoch := types.Epoch(0)
    30  	err = checkAttestingHistoryAfterPruning(
    31  		t,
    32  		validatorDB,
    33  		pubKey,
    34  		startEpoch,
    35  		numEpochs,
    36  		false, /* should be pruned */
    37  	)
    38  	require.NoError(t, err)
    39  }
    40  
    41  func TestPruneAttestations_OK(t *testing.T) {
    42  	numKeys := uint64(2048)
    43  	pks := make([][48]byte, 0, numKeys)
    44  	for i := uint64(0); i < numKeys; i++ {
    45  		pks = append(pks, bytesutil.ToBytes48(bytesutil.ToBytes(i, 48)))
    46  	}
    47  	validatorDB := setupDB(t, pks)
    48  
    49  	// Write attesting history for every single epoch
    50  	// since genesis to SLASHING_PROTECTION_PRUNING_EPOCHS * 2.
    51  	numEpochs := params.BeaconConfig().SlashingProtectionPruningEpochs * 2
    52  	for _, pk := range pks {
    53  		require.NoError(t, setupAttestationsForEveryEpoch(t, validatorDB, pk, numEpochs))
    54  	}
    55  
    56  	require.NoError(t, validatorDB.PruneAttestations(context.Background()))
    57  
    58  	// Next, verify that we pruned every epoch
    59  	// from genesis to SLASHING_PROTECTION_PRUNING_EPOCHS - 1.
    60  	startEpoch := types.Epoch(0)
    61  	for _, pk := range pks {
    62  		err := checkAttestingHistoryAfterPruning(
    63  			t,
    64  			validatorDB,
    65  			pk,
    66  			startEpoch,
    67  			params.BeaconConfig().SlashingProtectionPruningEpochs-1,
    68  			true, /* should be pruned */
    69  		)
    70  		require.NoError(t, err)
    71  	}
    72  
    73  	// Next, verify that we pruned every epoch
    74  	// from N = SLASHING_PROTECTION_PRUNING_EPOCHS to N * 2.
    75  	startEpoch = params.BeaconConfig().SlashingProtectionPruningEpochs
    76  	endEpoch := startEpoch * 2
    77  	for _, pk := range pks {
    78  		err := checkAttestingHistoryAfterPruning(
    79  			t,
    80  			validatorDB,
    81  			pk,
    82  			startEpoch,
    83  			endEpoch,
    84  			false, /* should not be pruned */
    85  		)
    86  		require.NoError(t, err)
    87  	}
    88  }
    89  
    90  func BenchmarkPruneAttestations(b *testing.B) {
    91  	numKeys := uint64(8)
    92  	pks := make([][48]byte, 0, numKeys)
    93  	for i := uint64(0); i < numKeys; i++ {
    94  		pks = append(pks, bytesutil.ToBytes48(bytesutil.ToBytes(i, 48)))
    95  	}
    96  	validatorDB := setupDB(b, pks)
    97  
    98  	// Write attesting history for every single epoch
    99  	// since genesis to SLASHING_PROTECTION_PRUNING_EPOCHS * 20.
   100  	numEpochs := params.BeaconConfig().SlashingProtectionPruningEpochs * 20
   101  
   102  	b.ResetTimer()
   103  	for i := 0; i < b.N; i++ {
   104  		b.StopTimer()
   105  		for _, pk := range pks {
   106  			require.NoError(b, setupAttestationsForEveryEpoch(b, validatorDB, pk, numEpochs))
   107  		}
   108  		b.StartTimer()
   109  
   110  		require.NoError(b, validatorDB.PruneAttestations(context.Background()))
   111  	}
   112  }
   113  
   114  // Saves attesting history for every (source, target = source + 1) pairs since genesis
   115  // up to a given number of epochs for a validator public key.
   116  func setupAttestationsForEveryEpoch(t testing.TB, validatorDB *Store, pubKey [48]byte, numEpochs types.Epoch) error {
   117  	return validatorDB.update(func(tx *bolt.Tx) error {
   118  		bucket := tx.Bucket(pubKeysBucket)
   119  		pkBucket, err := bucket.CreateBucketIfNotExists(pubKey[:])
   120  		if err != nil {
   121  			return err
   122  		}
   123  		signingRootsBucket, err := pkBucket.CreateBucketIfNotExists(attestationSigningRootsBucket)
   124  		if err != nil {
   125  			return err
   126  		}
   127  		sourceEpochsBucket, err := pkBucket.CreateBucketIfNotExists(attestationSourceEpochsBucket)
   128  		if err != nil {
   129  			return err
   130  		}
   131  		for sourceEpoch := types.Epoch(0); sourceEpoch < numEpochs; sourceEpoch++ {
   132  			targetEpoch := sourceEpoch + 1
   133  			targetEpochBytes := bytesutil.EpochToBytesBigEndian(targetEpoch)
   134  			sourceEpochBytes := bytesutil.EpochToBytesBigEndian(sourceEpoch)
   135  			// Save (source epoch, target epoch) pairs.
   136  			if err := sourceEpochsBucket.Put(sourceEpochBytes, targetEpochBytes); err != nil {
   137  				return err
   138  			}
   139  			// Save signing root for target epoch.
   140  			var signingRoot [32]byte
   141  			copy(signingRoot[:], fmt.Sprintf("%d", targetEpochBytes))
   142  			if err := signingRootsBucket.Put(targetEpochBytes, signingRoot[:]); err != nil {
   143  				return err
   144  			}
   145  		}
   146  		return nil
   147  	})
   148  }
   149  
   150  // Verifies, based on a boolean input argument, whether or not we should have
   151  // pruned all attesting history since genesis up to a specified number of epochs.
   152  func checkAttestingHistoryAfterPruning(
   153  	t testing.TB,
   154  	validatorDB *Store,
   155  	pubKey [48]byte,
   156  	startEpoch,
   157  	numEpochs types.Epoch,
   158  	shouldBePruned bool,
   159  ) error {
   160  	return validatorDB.view(func(tx *bolt.Tx) error {
   161  		bucket := tx.Bucket(pubKeysBucket)
   162  		pkBkt := bucket.Bucket(pubKey[:])
   163  		signingRootsBkt := pkBkt.Bucket(attestationSigningRootsBucket)
   164  		sourceEpochsBkt := pkBkt.Bucket(attestationSourceEpochsBucket)
   165  		for sourceEpoch := startEpoch; sourceEpoch < numEpochs; sourceEpoch++ {
   166  			targetEpoch := sourceEpoch + 1
   167  			targetEpochBytes := bytesutil.EpochToBytesBigEndian(targetEpoch)
   168  			sourceEpochBytes := bytesutil.EpochToBytesBigEndian(sourceEpoch)
   169  
   170  			storedTargetEpoch := sourceEpochsBkt.Get(sourceEpochBytes)
   171  			signingRoot := signingRootsBkt.Get(targetEpochBytes)
   172  			if shouldBePruned {
   173  				// Expect to have no data if we have pruned.
   174  				require.Equal(t, true, signingRoot == nil)
   175  				require.Equal(t, true, storedTargetEpoch == nil)
   176  			} else {
   177  				// Expect the correct signing root.
   178  				var expectedSigningRoot [32]byte
   179  				copy(expectedSigningRoot[:], fmt.Sprintf("%d", targetEpochBytes))
   180  				require.DeepEqual(t, expectedSigningRoot[:], signingRoot)
   181  				// Expect the correct target epoch.
   182  				require.DeepEqual(t, targetEpochBytes, storedTargetEpoch)
   183  			}
   184  		}
   185  		return nil
   186  	})
   187  }