2021-01-07 21:40:37 +00:00
|
|
|
package kv
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"testing"
|
|
|
|
|
2021-02-22 23:20:57 +00:00
|
|
|
types "github.com/prysmaticlabs/eth2-types"
|
2022-01-06 17:33:08 +00:00
|
|
|
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
|
2021-09-21 19:59:25 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/config/params"
|
2021-09-23 15:23:37 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
|
2021-09-23 18:53:46 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/testing/require"
|
2021-01-07 21:40:37 +00:00
|
|
|
bolt "go.etcd.io/bbolt"
|
|
|
|
)
|
|
|
|
|
2021-03-11 15:51:16 +00:00
|
|
|
func TestPruneAttestations_NoPruning(t *testing.T) {
|
2022-01-06 17:33:08 +00:00
|
|
|
pubKey := [fieldparams.BLSPubkeyLength]byte{1}
|
|
|
|
validatorDB := setupDB(t, [][fieldparams.BLSPubkeyLength]byte{pubKey})
|
2021-01-07 21:40:37 +00:00
|
|
|
|
|
|
|
// Write attesting history for every single epoch
|
2021-03-11 15:51:16 +00:00
|
|
|
// since genesis to a specified number of epochs.
|
|
|
|
numEpochs := params.BeaconConfig().SlashingProtectionPruningEpochs - 1
|
2021-01-07 21:40:37 +00:00
|
|
|
err := setupAttestationsForEveryEpoch(t, validatorDB, pubKey, numEpochs)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
// Next, attempt to prune and realize that we still have all epochs intact
|
2021-03-11 15:51:16 +00:00
|
|
|
err = validatorDB.PruneAttestations(context.Background())
|
2021-01-07 21:40:37 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
2021-03-11 15:51:16 +00:00
|
|
|
startEpoch := types.Epoch(0)
|
2021-01-07 21:40:37 +00:00
|
|
|
err = checkAttestingHistoryAfterPruning(
|
2021-03-11 15:51:16 +00:00
|
|
|
t,
|
|
|
|
validatorDB,
|
|
|
|
pubKey,
|
|
|
|
startEpoch,
|
|
|
|
numEpochs,
|
|
|
|
false, /* should be pruned */
|
2021-01-07 21:40:37 +00:00
|
|
|
)
|
|
|
|
require.NoError(t, err)
|
|
|
|
}
|
|
|
|
|
2021-03-11 15:51:16 +00:00
|
|
|
func TestPruneAttestations_OK(t *testing.T) {
|
2021-09-29 18:17:37 +00:00
|
|
|
numKeys := uint64(64)
|
2022-01-06 17:33:08 +00:00
|
|
|
pks := make([][fieldparams.BLSPubkeyLength]byte, 0, numKeys)
|
2021-03-18 22:39:28 +00:00
|
|
|
for i := uint64(0); i < numKeys; i++ {
|
|
|
|
pks = append(pks, bytesutil.ToBytes48(bytesutil.ToBytes(i, 48)))
|
|
|
|
}
|
|
|
|
validatorDB := setupDB(t, pks)
|
2021-01-07 21:40:37 +00:00
|
|
|
|
|
|
|
// Write attesting history for every single epoch
|
2021-03-11 15:51:16 +00:00
|
|
|
// since genesis to SLASHING_PROTECTION_PRUNING_EPOCHS * 2.
|
|
|
|
numEpochs := params.BeaconConfig().SlashingProtectionPruningEpochs * 2
|
2021-03-18 22:39:28 +00:00
|
|
|
for _, pk := range pks {
|
|
|
|
require.NoError(t, setupAttestationsForEveryEpoch(t, validatorDB, pk, numEpochs))
|
|
|
|
}
|
2021-01-07 21:40:37 +00:00
|
|
|
|
2021-03-18 22:39:28 +00:00
|
|
|
require.NoError(t, validatorDB.PruneAttestations(context.Background()))
|
2021-01-07 21:40:37 +00:00
|
|
|
|
2021-03-11 15:51:16 +00:00
|
|
|
// Next, verify that we pruned every epoch
|
|
|
|
// from genesis to SLASHING_PROTECTION_PRUNING_EPOCHS - 1.
|
|
|
|
startEpoch := types.Epoch(0)
|
2021-03-18 22:39:28 +00:00
|
|
|
for _, pk := range pks {
|
|
|
|
err := checkAttestingHistoryAfterPruning(
|
|
|
|
t,
|
|
|
|
validatorDB,
|
|
|
|
pk,
|
|
|
|
startEpoch,
|
|
|
|
params.BeaconConfig().SlashingProtectionPruningEpochs-1,
|
|
|
|
true, /* should be pruned */
|
|
|
|
)
|
|
|
|
require.NoError(t, err)
|
|
|
|
}
|
2021-01-07 21:40:37 +00:00
|
|
|
|
2021-03-11 15:51:16 +00:00
|
|
|
// Next, verify that we pruned every epoch
|
|
|
|
// from N = SLASHING_PROTECTION_PRUNING_EPOCHS to N * 2.
|
|
|
|
startEpoch = params.BeaconConfig().SlashingProtectionPruningEpochs
|
|
|
|
endEpoch := startEpoch * 2
|
2021-03-18 22:39:28 +00:00
|
|
|
for _, pk := range pks {
|
|
|
|
err := checkAttestingHistoryAfterPruning(
|
|
|
|
t,
|
|
|
|
validatorDB,
|
|
|
|
pk,
|
|
|
|
startEpoch,
|
|
|
|
endEpoch,
|
|
|
|
false, /* should not be pruned */
|
|
|
|
)
|
|
|
|
require.NoError(t, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func BenchmarkPruneAttestations(b *testing.B) {
|
|
|
|
numKeys := uint64(8)
|
2022-01-06 17:33:08 +00:00
|
|
|
pks := make([][fieldparams.BLSPubkeyLength]byte, 0, numKeys)
|
2021-03-18 22:39:28 +00:00
|
|
|
for i := uint64(0); i < numKeys; i++ {
|
|
|
|
pks = append(pks, bytesutil.ToBytes48(bytesutil.ToBytes(i, 48)))
|
|
|
|
}
|
|
|
|
validatorDB := setupDB(b, pks)
|
|
|
|
|
|
|
|
// Write attesting history for every single epoch
|
|
|
|
// since genesis to SLASHING_PROTECTION_PRUNING_EPOCHS * 20.
|
|
|
|
numEpochs := params.BeaconConfig().SlashingProtectionPruningEpochs * 20
|
|
|
|
|
|
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
|
|
b.StopTimer()
|
|
|
|
for _, pk := range pks {
|
|
|
|
require.NoError(b, setupAttestationsForEveryEpoch(b, validatorDB, pk, numEpochs))
|
|
|
|
}
|
|
|
|
b.StartTimer()
|
|
|
|
|
|
|
|
require.NoError(b, validatorDB.PruneAttestations(context.Background()))
|
|
|
|
}
|
2021-01-07 21:40:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Saves attesting history for every (source, target = source + 1) pairs since genesis
|
|
|
|
// up to a given number of epochs for a validator public key.
|
2022-01-06 17:33:08 +00:00
|
|
|
func setupAttestationsForEveryEpoch(t testing.TB, validatorDB *Store, pubKey [fieldparams.BLSPubkeyLength]byte, numEpochs types.Epoch) error {
|
2021-01-07 21:40:37 +00:00
|
|
|
return validatorDB.update(func(tx *bolt.Tx) error {
|
|
|
|
bucket := tx.Bucket(pubKeysBucket)
|
|
|
|
pkBucket, err := bucket.CreateBucketIfNotExists(pubKey[:])
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
signingRootsBucket, err := pkBucket.CreateBucketIfNotExists(attestationSigningRootsBucket)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
sourceEpochsBucket, err := pkBucket.CreateBucketIfNotExists(attestationSourceEpochsBucket)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-03-11 15:51:16 +00:00
|
|
|
for sourceEpoch := types.Epoch(0); sourceEpoch < numEpochs; sourceEpoch++ {
|
|
|
|
targetEpoch := sourceEpoch + 1
|
2021-02-09 10:05:22 +00:00
|
|
|
targetEpochBytes := bytesutil.EpochToBytesBigEndian(targetEpoch)
|
2021-03-11 15:51:16 +00:00
|
|
|
sourceEpochBytes := bytesutil.EpochToBytesBigEndian(sourceEpoch)
|
2021-01-07 21:40:37 +00:00
|
|
|
// Save (source epoch, target epoch) pairs.
|
|
|
|
if err := sourceEpochsBucket.Put(sourceEpochBytes, targetEpochBytes); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
// Save signing root for target epoch.
|
|
|
|
var signingRoot [32]byte
|
|
|
|
copy(signingRoot[:], fmt.Sprintf("%d", targetEpochBytes))
|
|
|
|
if err := signingRootsBucket.Put(targetEpochBytes, signingRoot[:]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verifies, based on a boolean input argument, whether or not we should have
|
|
|
|
// pruned all attesting history since genesis up to a specified number of epochs.
|
|
|
|
func checkAttestingHistoryAfterPruning(
|
2021-03-11 15:51:16 +00:00
|
|
|
t testing.TB,
|
|
|
|
validatorDB *Store,
|
2022-01-06 17:33:08 +00:00
|
|
|
pubKey [fieldparams.BLSPubkeyLength]byte,
|
2021-03-24 19:57:27 +00:00
|
|
|
startEpoch,
|
2021-03-11 15:51:16 +00:00
|
|
|
numEpochs types.Epoch,
|
|
|
|
shouldBePruned bool,
|
2021-01-07 21:40:37 +00:00
|
|
|
) error {
|
|
|
|
return validatorDB.view(func(tx *bolt.Tx) error {
|
|
|
|
bucket := tx.Bucket(pubKeysBucket)
|
|
|
|
pkBkt := bucket.Bucket(pubKey[:])
|
|
|
|
signingRootsBkt := pkBkt.Bucket(attestationSigningRootsBucket)
|
|
|
|
sourceEpochsBkt := pkBkt.Bucket(attestationSourceEpochsBucket)
|
2021-03-11 15:51:16 +00:00
|
|
|
for sourceEpoch := startEpoch; sourceEpoch < numEpochs; sourceEpoch++ {
|
|
|
|
targetEpoch := sourceEpoch + 1
|
2021-02-09 10:05:22 +00:00
|
|
|
targetEpochBytes := bytesutil.EpochToBytesBigEndian(targetEpoch)
|
2021-03-11 15:51:16 +00:00
|
|
|
sourceEpochBytes := bytesutil.EpochToBytesBigEndian(sourceEpoch)
|
2021-01-07 21:40:37 +00:00
|
|
|
|
|
|
|
storedTargetEpoch := sourceEpochsBkt.Get(sourceEpochBytes)
|
|
|
|
signingRoot := signingRootsBkt.Get(targetEpochBytes)
|
|
|
|
if shouldBePruned {
|
|
|
|
// Expect to have no data if we have pruned.
|
|
|
|
require.Equal(t, true, signingRoot == nil)
|
|
|
|
require.Equal(t, true, storedTargetEpoch == nil)
|
|
|
|
} else {
|
|
|
|
// Expect the correct signing root.
|
|
|
|
var expectedSigningRoot [32]byte
|
|
|
|
copy(expectedSigningRoot[:], fmt.Sprintf("%d", targetEpochBytes))
|
|
|
|
require.DeepEqual(t, expectedSigningRoot[:], signingRoot)
|
|
|
|
// Expect the correct target epoch.
|
|
|
|
require.DeepEqual(t, targetEpochBytes, storedTargetEpoch)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
}
|