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 }