github.com/ari-anchor/sei-tendermint@v0.0.0-20230519144642-dc826b7b56bb/scripts/keymigrate/migrate_test.go (about)

     1  package keymigrate
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"math"
     8  	"strings"
     9  	"testing"
    10  
    11  	"github.com/google/orderedcode"
    12  	"github.com/stretchr/testify/require"
    13  	dbm "github.com/tendermint/tm-db"
    14  )
    15  
    16  func makeKey(t *testing.T, elems ...interface{}) []byte {
    17  	t.Helper()
    18  	out, err := orderedcode.Append([]byte{}, elems...)
    19  	require.NoError(t, err)
    20  	return out
    21  }
    22  
    23  func getLegacyPrefixKeys(val int) map[string][]byte {
    24  	vstr := fmt.Sprintf("%02x", byte(val))
    25  	return map[string][]byte{
    26  		"Height":            []byte(fmt.Sprintf("H:%d", val)),
    27  		"BlockPart":         []byte(fmt.Sprintf("P:%d:%d", val, val)),
    28  		"BlockPartTwo":      []byte(fmt.Sprintf("P:%d:%d", val+2, val+val)),
    29  		"BlockCommit":       []byte(fmt.Sprintf("C:%d", val)),
    30  		"SeenCommit":        []byte(fmt.Sprintf("SC:%d", val)),
    31  		"BlockHeight":       []byte(fmt.Sprintf("BH:%x", val)),
    32  		"Validators":        []byte(fmt.Sprintf("validatorsKey:%d", val)),
    33  		"ConsensusParams":   []byte(fmt.Sprintf("consensusParamsKey:%d", val)),
    34  		"ABCIResponse":      []byte(fmt.Sprintf("abciResponsesKey:%d", val)),
    35  		"State":             []byte("stateKey"),
    36  		"CommittedEvidence": append([]byte{0x00}, []byte(fmt.Sprintf("%0.16X/%X", int64(val), []byte("committed")))...),
    37  		"PendingEvidence":   append([]byte{0x01}, []byte(fmt.Sprintf("%0.16X/%X", int64(val), []byte("pending")))...),
    38  		"LightBLock":        []byte(fmt.Sprintf("lb/foo/%020d", val)),
    39  		"Size":              []byte("size"),
    40  		"UserKey0":          []byte(fmt.Sprintf("foo/bar/%d/%d", val, val)),
    41  		"UserKey1":          []byte(fmt.Sprintf("foo/bar/baz/%d/%d", val, val)),
    42  		"TxHeight":          []byte(fmt.Sprintf("tx.height/%s/%d/%d", fmt.Sprint(val), val, val)),
    43  		"TxHash": append(
    44  			[]byte(strings.Repeat(vstr[:1], 16)),
    45  			[]byte(strings.Repeat(vstr[1:], 16))...,
    46  		),
    47  
    48  		// Transaction hashes that could be mistaken for evidence keys.
    49  		"TxHashMimic0": append([]byte{0}, []byte(strings.Repeat(vstr, 16)[:31])...),
    50  		"TxHashMimic1": append([]byte{1}, []byte(strings.Repeat(vstr, 16)[:31])...),
    51  	}
    52  }
    53  
    54  func getNewPrefixKeys(t *testing.T, val int) map[string][]byte {
    55  	t.Helper()
    56  	vstr := fmt.Sprintf("%02x", byte(val))
    57  	return map[string][]byte{
    58  		"Height":            makeKey(t, int64(0), int64(val)),
    59  		"BlockPart":         makeKey(t, int64(1), int64(val), int64(val)),
    60  		"BlockPartTwo":      makeKey(t, int64(1), int64(val+2), int64(val+val)),
    61  		"BlockCommit":       makeKey(t, int64(2), int64(val)),
    62  		"SeenCommit":        makeKey(t, int64(3), int64(val)),
    63  		"BlockHeight":       makeKey(t, int64(4), int64(val)),
    64  		"Validators":        makeKey(t, int64(5), int64(val)),
    65  		"ConsensusParams":   makeKey(t, int64(6), int64(val)),
    66  		"ABCIResponse":      makeKey(t, int64(7), int64(val)),
    67  		"State":             makeKey(t, int64(8)),
    68  		"CommittedEvidence": makeKey(t, int64(9), int64(val)),
    69  		"PendingEvidence":   makeKey(t, int64(10), int64(val)),
    70  		"LightBLock":        makeKey(t, int64(11), int64(val)),
    71  		"Size":              makeKey(t, int64(12)),
    72  		"UserKey0":          makeKey(t, "foo", "bar", int64(val), int64(val)),
    73  		"UserKey1":          makeKey(t, "foo", "bar/baz", int64(val), int64(val)),
    74  		"TxHeight":          makeKey(t, "tx.height", fmt.Sprint(val), int64(val), int64(val+2), int64(val+val)),
    75  		"TxHash":            makeKey(t, "tx.hash", strings.Repeat(vstr, 16)),
    76  		"TxHashMimic0":      makeKey(t, "tx.hash", "\x00"+strings.Repeat(vstr, 16)[:31]),
    77  		"TxHashMimic1":      makeKey(t, "tx.hash", "\x01"+strings.Repeat(vstr, 16)[:31]),
    78  	}
    79  }
    80  
    81  func getLegacyDatabase(t *testing.T) (int, dbm.DB) {
    82  	db := dbm.NewMemDB()
    83  	batch := db.NewBatch()
    84  	ct := 0
    85  
    86  	generated := []map[string][]byte{
    87  		getLegacyPrefixKeys(8),
    88  		getLegacyPrefixKeys(9001),
    89  		getLegacyPrefixKeys(math.MaxInt32 << 1),
    90  		getLegacyPrefixKeys(math.MaxInt64 - 8),
    91  	}
    92  
    93  	// populate database
    94  	for _, km := range generated {
    95  		for _, key := range km {
    96  			ct++
    97  			require.NoError(t, batch.Set(key, []byte(fmt.Sprintf(`{"value": %d}`, ct))))
    98  		}
    99  	}
   100  	require.NoError(t, batch.WriteSync())
   101  	require.NoError(t, batch.Close())
   102  	return ct - (2 * len(generated)) + 2, db
   103  }
   104  
   105  func TestMigration(t *testing.T) {
   106  	t.Run("Idempotency", func(t *testing.T) {
   107  		// we want to make sure that the key space for new and
   108  		// legacy keys are entirely non-overlapping.
   109  
   110  		legacyPrefixes := getLegacyPrefixKeys(42)
   111  
   112  		newPrefixes := getNewPrefixKeys(t, 42)
   113  
   114  		require.Equal(t, len(legacyPrefixes), len(newPrefixes))
   115  
   116  		t.Run("Legacy", func(t *testing.T) {
   117  			for kind, le := range legacyPrefixes {
   118  				require.True(t, checkKeyType(le).isLegacy(), kind)
   119  			}
   120  		})
   121  		t.Run("New", func(t *testing.T) {
   122  			for kind, ne := range newPrefixes {
   123  				require.False(t, checkKeyType(ne).isLegacy(), kind)
   124  			}
   125  		})
   126  		t.Run("Conversion", func(t *testing.T) {
   127  			for kind, le := range legacyPrefixes {
   128  				nk, err := migrateKey(le)
   129  				require.NoError(t, err, kind)
   130  				require.False(t, checkKeyType(nk).isLegacy(), kind)
   131  			}
   132  		})
   133  		t.Run("Hashes", func(t *testing.T) {
   134  			t.Run("NewKeysAreNotHashes", func(t *testing.T) {
   135  				for _, key := range getNewPrefixKeys(t, 9001) {
   136  					require.True(t, len(key) != 32)
   137  				}
   138  			})
   139  			t.Run("ContrivedLegacyKeyDetection", func(t *testing.T) {
   140  				// length 32: should appear to be a hash
   141  				require.Equal(t, txHashKey, checkKeyType([]byte("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")))
   142  
   143  				// length ≠ 32: should not appear to be a hash
   144  				require.Equal(t, nonLegacyKey, checkKeyType([]byte("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx--")))
   145  				require.Equal(t, nonLegacyKey, checkKeyType([]byte("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")))
   146  			})
   147  		})
   148  	})
   149  	t.Run("Migrations", func(t *testing.T) {
   150  		t.Run("Errors", func(t *testing.T) {
   151  			table := map[string][]byte{
   152  				"Height":          []byte(fmt.Sprintf("H:%f", 4.22222)),
   153  				"BlockPart":       []byte(fmt.Sprintf("P:%f", 4.22222)),
   154  				"BlockPartTwo":    []byte(fmt.Sprintf("P:%d", 42)),
   155  				"BlockPartThree":  []byte(fmt.Sprintf("P:%f:%f", 4.222, 8.444)),
   156  				"BlockPartFour":   []byte(fmt.Sprintf("P:%d:%f", 4222, 8.444)),
   157  				"BlockCommit":     []byte(fmt.Sprintf("C:%f", 4.22222)),
   158  				"SeenCommit":      []byte(fmt.Sprintf("SC:%f", 4.22222)),
   159  				"BlockHeight":     []byte(fmt.Sprintf("BH:%f", 4.22222)),
   160  				"Validators":      []byte(fmt.Sprintf("validatorsKey:%f", 4.22222)),
   161  				"ConsensusParams": []byte(fmt.Sprintf("consensusParamsKey:%f", 4.22222)),
   162  				"ABCIResponse":    []byte(fmt.Sprintf("abciResponsesKey:%f", 4.22222)),
   163  				"LightBlockShort": []byte(fmt.Sprintf("lb/foo/%010d", 42)),
   164  				"LightBlockLong":  []byte("lb/foo/12345678910.1234567890"),
   165  				"Invalid":         {0x03},
   166  				"BadTXHeight0":    []byte(fmt.Sprintf("tx.height/%s/%f/%f", "boop", 4.4, 4.5)),
   167  				"BadTXHeight1":    []byte(fmt.Sprintf("tx.height/%s/%f", "boop", 4.4)),
   168  				"UserKey0":        []byte("foo/bar/1.3/3.4"),
   169  				"UserKey1":        []byte("foo/bar/1/3.4"),
   170  				"UserKey2":        []byte("foo/bar/baz/1/3.4"),
   171  				"UserKey3":        []byte("foo/bar/baz/1.2/4"),
   172  			}
   173  			for kind, key := range table {
   174  				out, err := migrateKey(key)
   175  				require.Error(t, err, kind)
   176  				require.Nil(t, out, kind)
   177  			}
   178  		})
   179  		t.Run("Replacement", func(t *testing.T) {
   180  			t.Run("MissingKey", func(t *testing.T) {
   181  				db := dbm.NewMemDB()
   182  				require.NoError(t, replaceKey(db, keyID("hi"), nil))
   183  			})
   184  			t.Run("ReplacementFails", func(t *testing.T) {
   185  				db := dbm.NewMemDB()
   186  				key := keyID("hi")
   187  				require.NoError(t, db.Set(key, []byte("world")))
   188  				require.Error(t, replaceKey(db, key, func(k keyID) (keyID, error) {
   189  					return nil, errors.New("hi")
   190  				}))
   191  			})
   192  			t.Run("KeyDisappears", func(t *testing.T) {
   193  				db := dbm.NewMemDB()
   194  				key := keyID("hi")
   195  				require.NoError(t, db.Set(key, []byte("world")))
   196  				require.Error(t, replaceKey(db, key, func(k keyID) (keyID, error) {
   197  					require.NoError(t, db.Delete(key))
   198  					return keyID("wat"), nil
   199  				}))
   200  
   201  				exists, err := db.Has(key)
   202  				require.NoError(t, err)
   203  				require.False(t, exists)
   204  
   205  				exists, err = db.Has(keyID("wat"))
   206  				require.NoError(t, err)
   207  				require.False(t, exists)
   208  			})
   209  		})
   210  	})
   211  	t.Run("Integration", func(t *testing.T) {
   212  		t.Run("KeyDiscovery", func(t *testing.T) {
   213  			size, db := getLegacyDatabase(t)
   214  			keys, err := getAllLegacyKeys(db)
   215  			require.NoError(t, err)
   216  			require.Equal(t, size, len(keys))
   217  			legacyKeys := 0
   218  			for _, k := range keys {
   219  				if checkKeyType(k).isLegacy() {
   220  					legacyKeys++
   221  				}
   222  			}
   223  			require.Equal(t, size, legacyKeys)
   224  		})
   225  		t.Run("KeyIdempotency", func(t *testing.T) {
   226  			for _, key := range getNewPrefixKeys(t, 84) {
   227  				require.False(t, checkKeyType(key).isLegacy())
   228  			}
   229  		})
   230  		t.Run("Migrate", func(t *testing.T) {
   231  			_, db := getLegacyDatabase(t)
   232  
   233  			ctx := context.Background()
   234  			err := Migrate(ctx, db)
   235  			require.NoError(t, err)
   236  			keys, err := getAllLegacyKeys(db)
   237  			require.NoError(t, err)
   238  			require.Equal(t, 0, len(keys))
   239  
   240  		})
   241  	})
   242  }