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  }