github.com/number571/tendermint@v0.34.11-gost/scripts/keymigrate/migrate.go (about) 1 // Package keymigrate translates all legacy formatted keys to their 2 // new components. 3 // 4 // The key migration operation as implemented provides a potential 5 // model for database migration operations. Crucially, the migration 6 // as implemented does not depend on any tendermint code. 7 package keymigrate 8 9 import ( 10 "bytes" 11 "context" 12 "encoding/binary" 13 "encoding/hex" 14 "fmt" 15 "math/rand" 16 "runtime" 17 "strconv" 18 "sync" 19 20 "github.com/google/orderedcode" 21 dbm "github.com/tendermint/tm-db" 22 ) 23 24 type ( 25 keyID []byte 26 migrateFunc func(keyID) (keyID, error) 27 ) 28 29 func getAllLegacyKeys(db dbm.DB) ([]keyID, error) { 30 out := []keyID{} 31 32 iter, err := db.Iterator(nil, nil) 33 if err != nil { 34 return nil, err 35 } 36 37 for ; iter.Valid(); iter.Next() { 38 k := iter.Key() 39 40 // make sure it's a key with a legacy format, and skip 41 // all other keys, to make it safe to resume the migration. 42 if !keyIsLegacy(k) { 43 continue 44 } 45 46 // there's inconsistency around tm-db's handling of 47 // key copies. 48 nk := make([]byte, len(k)) 49 copy(nk, k) 50 out = append(out, nk) 51 } 52 53 if err = iter.Error(); err != nil { 54 return nil, err 55 } 56 57 if err = iter.Close(); err != nil { 58 return nil, err 59 } 60 61 return out, nil 62 } 63 64 func makeKeyChan(keys []keyID) <-chan keyID { 65 out := make(chan keyID, len(keys)) 66 defer close(out) 67 68 for _, key := range keys { 69 out <- key 70 } 71 72 return out 73 } 74 75 func keyIsLegacy(key keyID) bool { 76 for _, prefix := range []keyID{ 77 // core "store" 78 keyID("consensusParamsKey:"), 79 keyID("abciResponsesKey:"), 80 keyID("validatorsKey:"), 81 keyID("stateKey"), 82 keyID("H:"), 83 keyID("P:"), 84 keyID("C:"), 85 keyID("SC:"), 86 keyID("BH:"), 87 // light 88 keyID("size"), 89 keyID("lb/"), 90 // evidence 91 keyID([]byte{0x00}), 92 keyID([]byte{0x01}), 93 // tx index 94 keyID("tx.height/"), 95 keyID("tx.hash/"), 96 } { 97 if bytes.HasPrefix(key, prefix) { 98 return true 99 } 100 } 101 102 // this means it's a tx index... 103 if bytes.Count(key, []byte("/")) >= 3 { 104 return true 105 } 106 107 return keyIsHash(key) 108 } 109 110 func keyIsHash(key keyID) bool { 111 return len(key) == 32 && !bytes.Contains(key, []byte("/")) 112 } 113 114 func migarateKey(key keyID) (keyID, error) { 115 switch { 116 case bytes.HasPrefix(key, keyID("H:")): 117 val, err := strconv.Atoi(string(key[2:])) 118 if err != nil { 119 return nil, err 120 } 121 122 return orderedcode.Append(nil, int64(0), int64(val)) 123 case bytes.HasPrefix(key, keyID("P:")): 124 parts := bytes.Split(key[2:], []byte(":")) 125 if len(parts) != 2 { 126 return nil, fmt.Errorf("block parts key has %d rather than 2 components", 127 len(parts)) 128 } 129 valOne, err := strconv.Atoi(string(parts[0])) 130 if err != nil { 131 return nil, err 132 } 133 134 valTwo, err := strconv.Atoi(string(parts[1])) 135 if err != nil { 136 return nil, err 137 } 138 139 return orderedcode.Append(nil, int64(1), int64(valOne), int64(valTwo)) 140 case bytes.HasPrefix(key, keyID("C:")): 141 val, err := strconv.Atoi(string(key[2:])) 142 if err != nil { 143 return nil, err 144 } 145 146 return orderedcode.Append(nil, int64(2), int64(val)) 147 case bytes.HasPrefix(key, keyID("SC:")): 148 val, err := strconv.Atoi(string(key[3:])) 149 if err != nil { 150 return nil, err 151 } 152 153 return orderedcode.Append(nil, int64(3), int64(val)) 154 case bytes.HasPrefix(key, keyID("BH:")): 155 val, err := strconv.Atoi(string(key[3:])) 156 if err != nil { 157 return nil, err 158 } 159 160 return orderedcode.Append(nil, int64(4), int64(val)) 161 case bytes.HasPrefix(key, keyID("validatorsKey:")): 162 val, err := strconv.Atoi(string(key[14:])) 163 if err != nil { 164 return nil, err 165 } 166 167 return orderedcode.Append(nil, int64(5), int64(val)) 168 case bytes.HasPrefix(key, keyID("consensusParamsKey:")): 169 val, err := strconv.Atoi(string(key[19:])) 170 if err != nil { 171 return nil, err 172 } 173 174 return orderedcode.Append(nil, int64(6), int64(val)) 175 case bytes.HasPrefix(key, keyID("abciResponsesKey:")): 176 val, err := strconv.Atoi(string(key[17:])) 177 if err != nil { 178 return nil, err 179 } 180 181 return orderedcode.Append(nil, int64(7), int64(val)) 182 case bytes.HasPrefix(key, keyID("stateKey")): 183 return orderedcode.Append(nil, int64(8)) 184 case bytes.HasPrefix(key, []byte{0x00}): // committed evidence 185 return convertEvidence(key, 9) 186 case bytes.HasPrefix(key, []byte{0x01}): // pending evidence 187 return convertEvidence(key, 10) 188 case bytes.HasPrefix(key, keyID("lb/")): 189 if len(key) < 24 { 190 return nil, fmt.Errorf("light block evidence %q in invalid format", string(key)) 191 } 192 193 val, err := strconv.Atoi(string(key[len(key)-20:])) 194 if err != nil { 195 return nil, err 196 } 197 198 return orderedcode.Append(nil, int64(11), int64(val)) 199 case bytes.HasPrefix(key, keyID("size")): 200 return orderedcode.Append(nil, int64(12)) 201 case bytes.HasPrefix(key, keyID("tx.height")): 202 parts := bytes.Split(key, []byte("/")) 203 if len(parts) != 4 { 204 return nil, fmt.Errorf("key has %d parts rather than 4", len(parts)) 205 } 206 parts = parts[1:] // drop prefix 207 208 elems := make([]interface{}, 0, len(parts)+1) 209 elems = append(elems, "tx.height") 210 211 for idx, pt := range parts { 212 val, err := strconv.Atoi(string(pt)) 213 if err != nil { 214 return nil, err 215 } 216 if idx == 0 { 217 elems = append(elems, fmt.Sprintf("%d", val)) 218 } else { 219 elems = append(elems, int64(val)) 220 } 221 } 222 223 return orderedcode.Append(nil, elems...) 224 case bytes.Count(key, []byte("/")) >= 3: // tx indexer 225 parts := bytes.Split(key, []byte("/")) 226 227 elems := make([]interface{}, 0, 4) 228 if len(parts) == 4 { 229 elems = append(elems, string(parts[0]), string(parts[1])) 230 231 val, err := strconv.Atoi(string(parts[2])) 232 if err != nil { 233 return nil, err 234 } 235 elems = append(elems, int64(val)) 236 237 val2, err := strconv.Atoi(string(parts[3])) 238 if err != nil { 239 return nil, err 240 } 241 elems = append(elems, int64(val2)) 242 } else { 243 elems = append(elems, string(parts[0])) 244 parts = parts[1:] 245 246 val, err := strconv.Atoi(string(parts[len(parts)-1])) 247 if err != nil { 248 return nil, err 249 } 250 251 val2, err := strconv.Atoi(string(parts[len(parts)-2])) 252 if err != nil { 253 return nil, err 254 } 255 256 appKey := bytes.Join(parts[:len(parts)-3], []byte("/")) 257 elems = append(elems, string(appKey), int64(val), int64(val2)) 258 } 259 return orderedcode.Append(nil, elems...) 260 case keyIsHash(key): 261 return orderedcode.Append(nil, "tx.hash", string(key)) 262 default: 263 return nil, fmt.Errorf("key %q is in the wrong format", string(key)) 264 } 265 } 266 267 func convertEvidence(key keyID, newPrefix int64) ([]byte, error) { 268 parts := bytes.Split(key[1:], []byte("/")) 269 if len(parts) != 2 { 270 return nil, fmt.Errorf("evidence key is malformed with %d parts not 2", 271 len(parts)) 272 } 273 274 hb, err := hex.DecodeString(string(parts[0])) 275 if err != nil { 276 return nil, err 277 } 278 279 evidenceHash, err := hex.DecodeString(string(parts[1])) 280 if err != nil { 281 return nil, err 282 } 283 284 return orderedcode.Append(nil, newPrefix, binary.BigEndian.Uint64(hb), string(evidenceHash)) 285 } 286 287 func replaceKey(db dbm.DB, key keyID, gooseFn migrateFunc) error { 288 exists, err := db.Has(key) 289 if err != nil { 290 return err 291 } 292 if !exists { 293 return nil 294 } 295 296 newKey, err := gooseFn(key) 297 if err != nil { 298 return err 299 } 300 301 val, err := db.Get(key) 302 if err != nil { 303 return err 304 } 305 306 batch := db.NewBatch() 307 308 if err = batch.Set(newKey, val); err != nil { 309 return err 310 } 311 if err = batch.Delete(key); err != nil { 312 return err 313 } 314 315 // 10% of the time, force a write to disk, but mostly don't, 316 // because it's faster. 317 if rand.Intn(100)%10 == 0 { // nolint:gosec 318 if err = batch.WriteSync(); err != nil { 319 return err 320 } 321 } else { 322 if err = batch.Write(); err != nil { 323 return err 324 } 325 } 326 327 if err = batch.Close(); err != nil { 328 return err 329 } 330 331 return nil 332 } 333 334 // Migrate converts all legacy key formats to new key formats. The 335 // operation is idempotent, so it's safe to resume a failed 336 // operation. The operation is somewhat parallelized, relying on the 337 // concurrency safety of the underlying databases. 338 // 339 // Migrate has "continue on error" semantics and will iterate through 340 // all legacy keys attempt to migrate them, and will collect all 341 // errors and will return only at the end of the operation. 342 // 343 // The context allows for a safe termination of the operation 344 // (e.g connected to a singal handler,) to abort the operation 345 // in-between migration operations. 346 func Migrate(ctx context.Context, db dbm.DB) error { 347 keys, err := getAllLegacyKeys(db) 348 if err != nil { 349 return err 350 } 351 352 numWorkers := runtime.NumCPU() 353 wg := &sync.WaitGroup{} 354 355 errs := make(chan error, numWorkers) 356 357 keyCh := makeKeyChan(keys) 358 359 // run migrations. 360 for i := 0; i < numWorkers; i++ { 361 wg.Add(1) 362 go func() { 363 defer wg.Done() 364 for key := range keyCh { 365 err := replaceKey(db, key, migarateKey) 366 if err != nil { 367 errs <- err 368 } 369 370 if ctx.Err() != nil { 371 return 372 } 373 } 374 }() 375 } 376 377 // collect and process the errors. 378 errStrs := []string{} 379 signal := make(chan struct{}) 380 go func() { 381 defer close(signal) 382 for err := range errs { 383 if err == nil { 384 continue 385 } 386 errStrs = append(errStrs, err.Error()) 387 } 388 }() 389 390 // Wait for everything to be done. 391 wg.Wait() 392 close(errs) 393 <-signal 394 395 // check the error results 396 if len(errs) != 0 { 397 return fmt.Errorf("encountered errors during migration: %v", errStrs) 398 } 399 400 return nil 401 }