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 }