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

     1  package kv
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"reflect"
     8  	"testing"
     9  
    10  	"github.com/prysmaticlabs/prysm/shared/bytesutil"
    11  	"github.com/prysmaticlabs/prysm/shared/testutil/require"
    12  	bolt "go.etcd.io/bbolt"
    13  )
    14  
    15  func TestStore_migrateSourceTargetEpochsBucketUp(t *testing.T) {
    16  	numEpochs := uint64(100)
    17  	// numKeys should be more than batch size for testing.
    18  	// See: https://github.com/prysmaticlabs/prysm/issues/8509
    19  	numKeys := 2*publicKeyMigrationBatchSize + 1
    20  	pubKeys := make([][48]byte, numKeys)
    21  	for i := 0; i < numKeys; i++ {
    22  		var pk [48]byte
    23  		copy(pk[:], fmt.Sprintf("%d", i))
    24  		pubKeys[i] = pk
    25  	}
    26  	tests := []struct {
    27  		name  string
    28  		setup func(t *testing.T, validatorDB *Store)
    29  		eval  func(t *testing.T, validatorDB *Store)
    30  	}{
    31  		{
    32  			name: "only runs once",
    33  			setup: func(t *testing.T, validatorDB *Store) {
    34  				err := validatorDB.update(func(tx *bolt.Tx) error {
    35  					return tx.Bucket(migrationsBucket).Put(migrationSourceTargetEpochsBucketKey, migrationCompleted)
    36  				})
    37  				require.NoError(t, err)
    38  			},
    39  			eval: func(t *testing.T, validatorDB *Store) {
    40  				err := validatorDB.view(func(tx *bolt.Tx) error {
    41  					data := tx.Bucket(migrationsBucket).Get(migrationSourceTargetEpochsBucketKey)
    42  					require.DeepEqual(t, data, migrationCompleted)
    43  					return nil
    44  				})
    45  				require.NoError(t, err)
    46  			},
    47  		},
    48  		{
    49  			name: "populates new target epochs bucket",
    50  			setup: func(t *testing.T, validatorDB *Store) {
    51  				err := validatorDB.update(func(tx *bolt.Tx) error {
    52  					bucket := tx.Bucket(pubKeysBucket)
    53  					for _, pubKey := range pubKeys {
    54  						pkBucket, err := bucket.CreateBucketIfNotExists(pubKey[:])
    55  						if err != nil {
    56  							return err
    57  						}
    58  						sourceEpochsBucket, err := pkBucket.CreateBucketIfNotExists(attestationSourceEpochsBucket)
    59  						if err != nil {
    60  							return err
    61  						}
    62  						for epoch := uint64(1); epoch < numEpochs; epoch++ {
    63  							source := epoch - 1
    64  							target := epoch
    65  							sourceEpoch := bytesutil.Uint64ToBytesBigEndian(source)
    66  							targetEpoch := bytesutil.Uint64ToBytesBigEndian(target)
    67  							if err := sourceEpochsBucket.Put(sourceEpoch, targetEpoch); err != nil {
    68  								return err
    69  							}
    70  						}
    71  					}
    72  					return nil
    73  				})
    74  				require.NoError(t, err)
    75  			},
    76  			eval: func(t *testing.T, validatorDB *Store) {
    77  				// Verify we indeed have the data for all epochs
    78  				// since genesis to epoch 50 under the new schema.
    79  				err := validatorDB.view(func(tx *bolt.Tx) error {
    80  					bucket := tx.Bucket(pubKeysBucket)
    81  					for _, pubKey := range pubKeys {
    82  						pkBucket := bucket.Bucket(pubKey[:])
    83  						sourceEpochsBucket := pkBucket.Bucket(attestationSourceEpochsBucket)
    84  						targetEpochsBucket := pkBucket.Bucket(attestationTargetEpochsBucket)
    85  
    86  						// Verify we have (source epoch, target epoch) pairs.
    87  						for sourceEpoch := uint64(0); sourceEpoch < numEpochs-1; sourceEpoch++ {
    88  							sourceEpochBytes := bytesutil.Uint64ToBytesBigEndian(sourceEpoch)
    89  							targetEpochBytes := sourceEpochsBucket.Get(sourceEpochBytes)
    90  							targetEpoch := bytesutil.BytesToUint64BigEndian(targetEpochBytes)
    91  							require.Equal(t, sourceEpoch+1, targetEpoch)
    92  						}
    93  						// Verify we have (target epoch, source epoch) pairs.
    94  						for targetEpoch := uint64(1); targetEpoch < numEpochs; targetEpoch++ {
    95  							targetEpochBytes := bytesutil.Uint64ToBytesBigEndian(targetEpoch)
    96  							sourceEpochBytes := targetEpochsBucket.Get(targetEpochBytes)
    97  							sourceEpoch := bytesutil.BytesToUint64BigEndian(sourceEpochBytes)
    98  							require.Equal(t, targetEpoch-1, sourceEpoch)
    99  						}
   100  					}
   101  					return nil
   102  				})
   103  				require.NoError(t, err)
   104  			},
   105  		},
   106  	}
   107  	for _, tt := range tests {
   108  		t.Run(tt.name, func(t *testing.T) {
   109  			validatorDB := setupDB(t, pubKeys)
   110  			tt.setup(t, validatorDB)
   111  			require.NoError(t, validatorDB.migrateSourceTargetEpochsBucketUp(context.Background()))
   112  			tt.eval(t, validatorDB)
   113  		})
   114  	}
   115  }
   116  
   117  func TestStore_migrateSourceTargetEpochsBucketDown(t *testing.T) {
   118  	// numKeys should be more than batch size for testing.
   119  	// See: https://github.com/prysmaticlabs/prysm/issues/8509
   120  	numKeys := 2*publicKeyMigrationBatchSize + 1
   121  	pubKeys := make([][48]byte, numKeys)
   122  	for i := 0; i < numKeys; i++ {
   123  		var pk [48]byte
   124  		copy(pk[:], fmt.Sprintf("%d", i))
   125  		pubKeys[i] = pk
   126  	}
   127  	tests := []struct {
   128  		name  string
   129  		setup func(t *testing.T, validatorDB *Store)
   130  		eval  func(t *testing.T, validatorDB *Store)
   131  	}{
   132  		{
   133  			name: "unsets the migration completed key upon completion",
   134  			setup: func(t *testing.T, validatorDB *Store) {
   135  				err := validatorDB.update(func(tx *bolt.Tx) error {
   136  					return tx.Bucket(migrationsBucket).Put(migrationSourceTargetEpochsBucketKey, migrationCompleted)
   137  				})
   138  				require.NoError(t, err)
   139  			},
   140  			eval: func(t *testing.T, validatorDB *Store) {
   141  				err := validatorDB.view(func(tx *bolt.Tx) error {
   142  					data := tx.Bucket(migrationsBucket).Get(migrationSourceTargetEpochsBucketKey)
   143  					require.DeepEqual(t, true, data == nil)
   144  					return nil
   145  				})
   146  				require.NoError(t, err)
   147  			},
   148  		},
   149  		{
   150  			name:  "unsets the migration, even if unset already (no panic)",
   151  			setup: func(t *testing.T, validatorDB *Store) {},
   152  			eval: func(t *testing.T, validatorDB *Store) {
   153  				// Ensure the migration is not marked as complete.
   154  				err := validatorDB.view(func(tx *bolt.Tx) error {
   155  					data := tx.Bucket(migrationsBucket).Get(migrationSourceTargetEpochsBucketKey)
   156  					require.DeepNotEqual(t, data, migrationCompleted)
   157  					return nil
   158  				})
   159  				require.NoError(t, err)
   160  			},
   161  		},
   162  		{
   163  			name: "deletes the new bucket that was created in the up migration",
   164  			setup: func(t *testing.T, validatorDB *Store) {
   165  				err := validatorDB.update(func(tx *bolt.Tx) error {
   166  					bucket := tx.Bucket(pubKeysBucket)
   167  					for _, pubKey := range pubKeys {
   168  						pkBucket, err := bucket.CreateBucketIfNotExists(pubKey[:])
   169  						if err != nil {
   170  							return err
   171  						}
   172  						if _, err := pkBucket.CreateBucketIfNotExists(attestationSourceEpochsBucket); err != nil {
   173  							return err
   174  						}
   175  						if _, err := pkBucket.CreateBucketIfNotExists(attestationTargetEpochsBucket); err != nil {
   176  							return err
   177  						}
   178  					}
   179  					return nil
   180  				})
   181  				require.NoError(t, err)
   182  			},
   183  			eval: func(t *testing.T, validatorDB *Store) {
   184  				err := validatorDB.view(func(tx *bolt.Tx) error {
   185  					bucket := tx.Bucket(pubKeysBucket)
   186  					for _, pubKey := range pubKeys {
   187  						pkBucket := bucket.Bucket(pubKey[:])
   188  						if pkBucket == nil {
   189  							return errors.New("expected pubkey bucket to exist")
   190  						}
   191  						targetEpochsBucket := pkBucket.Bucket(attestationTargetEpochsBucket)
   192  						if targetEpochsBucket != nil {
   193  							return errors.New("expected target epochs bucket to have been deleted")
   194  						}
   195  					}
   196  					return nil
   197  				})
   198  				require.NoError(t, err)
   199  			},
   200  		},
   201  	}
   202  	for _, tt := range tests {
   203  		t.Run(tt.name, func(t *testing.T) {
   204  			validatorDB := setupDB(t, nil)
   205  			tt.setup(t, validatorDB)
   206  			require.NoError(t, validatorDB.migrateSourceTargetEpochsBucketDown(context.Background()))
   207  			tt.eval(t, validatorDB)
   208  		})
   209  	}
   210  }
   211  
   212  func Test_batchPublicKeys(t *testing.T) {
   213  	tests := []struct {
   214  		name       string
   215  		batchSize  int
   216  		publicKeys [][]byte
   217  		want       [][][]byte
   218  	}{
   219  		{
   220  			name:       "less than batch size returns all keys",
   221  			batchSize:  100,
   222  			publicKeys: [][]byte{{1}, {2}, {3}},
   223  			want:       [][][]byte{{{1}, {2}, {3}}},
   224  		},
   225  		{
   226  			name:       "equals batch size returns all keys",
   227  			batchSize:  3,
   228  			publicKeys: [][]byte{{1}, {2}, {3}},
   229  			want:       [][][]byte{{{1}, {2}, {3}}},
   230  		},
   231  		{
   232  			name:       "> batch size returns proper batches",
   233  			batchSize:  5,
   234  			publicKeys: [][]byte{{1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}},
   235  			want:       [][][]byte{{{1}, {2}, {3}, {4}, {5}}, {{6}, {7}, {8}}},
   236  		},
   237  		{
   238  			name:       "equal size batches returns proper batches",
   239  			batchSize:  2,
   240  			publicKeys: [][]byte{{1}, {2}, {3}, {4}},
   241  			want:       [][][]byte{{{1}, {2}}, {{3}, {4}}},
   242  		},
   243  	}
   244  	for _, tt := range tests {
   245  		t.Run(tt.name, func(t *testing.T) {
   246  			if got := batchPublicKeys(tt.publicKeys, tt.batchSize); !reflect.DeepEqual(got, tt.want) {
   247  				t.Errorf("batchPublicKeys() = %v, want %v", got, tt.want)
   248  			}
   249  		})
   250  	}
   251  }