github.com/ari-anchor/sei-tendermint@v0.0.0-20230519144642-dc826b7b56bb/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  
    19  	"github.com/creachadair/taskgroup"
    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  	var 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 !checkKeyType(k).isLegacy() {
    43  			continue
    44  		}
    45  
    46  		// Make an explicit copy, since not all tm-db backends do.
    47  		out = append(out, []byte(string(k)))
    48  	}
    49  
    50  	if err = iter.Error(); err != nil {
    51  		return nil, err
    52  	}
    53  
    54  	if err = iter.Close(); err != nil {
    55  		return nil, err
    56  	}
    57  
    58  	return out, nil
    59  }
    60  
    61  // keyType is an enumeration for the structural type of a key.
    62  type keyType int
    63  
    64  func (t keyType) isLegacy() bool { return t != nonLegacyKey }
    65  
    66  const (
    67  	nonLegacyKey keyType = iota // non-legacy key (presumed already converted)
    68  	consensusParamsKey
    69  	abciResponsesKey
    70  	validatorsKey
    71  	stateStoreKey        // state storage record
    72  	blockMetaKey         // H:
    73  	blockPartKey         // P:
    74  	commitKey            // C:
    75  	seenCommitKey        // SC:
    76  	blockHashKey         // BH:
    77  	lightSizeKey         // size
    78  	lightBlockKey        // lb/
    79  	evidenceCommittedKey // \x00
    80  	evidencePendingKey   // \x01
    81  	txHeightKey          // tx.height/... (special case)
    82  	abciEventKey         // name/value/height/index
    83  	txHashKey            // 32-byte transaction hash (unprefixed)
    84  )
    85  
    86  var prefixes = []struct {
    87  	prefix []byte
    88  	ktype  keyType
    89  	check  func(keyID) bool
    90  }{
    91  	{[]byte("consensusParamsKey:"), consensusParamsKey, nil},
    92  	{[]byte("abciResponsesKey:"), abciResponsesKey, nil},
    93  	{[]byte("validatorsKey:"), validatorsKey, nil},
    94  	{[]byte("stateKey"), stateStoreKey, nil},
    95  	{[]byte("H:"), blockMetaKey, nil},
    96  	{[]byte("P:"), blockPartKey, nil},
    97  	{[]byte("C:"), commitKey, nil},
    98  	{[]byte("SC:"), seenCommitKey, nil},
    99  	{[]byte("BH:"), blockHashKey, nil},
   100  	{[]byte("size"), lightSizeKey, nil},
   101  	{[]byte("lb/"), lightBlockKey, nil},
   102  	{[]byte("\x00"), evidenceCommittedKey, checkEvidenceKey},
   103  	{[]byte("\x01"), evidencePendingKey, checkEvidenceKey},
   104  }
   105  
   106  // checkKeyType classifies a candidate key based on its structure.
   107  func checkKeyType(key keyID) keyType {
   108  	for _, p := range prefixes {
   109  		if bytes.HasPrefix(key, p.prefix) {
   110  			if p.check == nil || p.check(key) {
   111  				return p.ktype
   112  			}
   113  		}
   114  	}
   115  
   116  	// A legacy event key has the form:
   117  	//
   118  	//    <name> / <value> / <height> / <index>
   119  	//
   120  	// Transaction hashes are stored as a raw binary hash with no prefix.
   121  	//
   122  	// Because a hash can contain any byte, it is possible (though unlikely)
   123  	// that a hash could have the correct form for an event key, in which case
   124  	// we would translate it incorrectly.  To reduce the likelihood of an
   125  	// incorrect interpretation, we parse candidate event keys and check for
   126  	// some structural properties before making a decision.
   127  	//
   128  	// Note, though, that nothing prevents event names or values from containing
   129  	// additional "/" separators, so the parse has to be forgiving.
   130  	parts := bytes.Split(key, []byte("/"))
   131  	if len(parts) >= 4 {
   132  		// Special case for tx.height.
   133  		if len(parts) == 4 && bytes.Equal(parts[0], []byte("tx.height")) {
   134  			return txHeightKey
   135  		}
   136  
   137  		// The name cannot be empty, but we don't know where the name ends and
   138  		// the value begins, so insist that there be something.
   139  		var n int
   140  		for _, part := range parts[:len(parts)-2] {
   141  			n += len(part)
   142  		}
   143  		// Check whether the last two fields could be .../height/index.
   144  		if n > 0 && isDecimal(parts[len(parts)-1]) && isDecimal(parts[len(parts)-2]) {
   145  			return abciEventKey
   146  		}
   147  	}
   148  
   149  	// If we get here, it's not an event key. Treat it as a hash if it is the
   150  	// right length. Note that it IS possible this could collide with the
   151  	// translation of some other key (though not a hash, since encoded hashes
   152  	// will be longer). The chance of that is small, but there is nothing we can
   153  	// do to detect it.
   154  	if len(key) == 32 {
   155  		return txHashKey
   156  	}
   157  	return nonLegacyKey
   158  }
   159  
   160  // isDecimal reports whether buf is a non-empty sequence of Unicode decimal
   161  // digits.
   162  func isDecimal(buf []byte) bool {
   163  	for _, c := range buf {
   164  		if c < '0' || c > '9' {
   165  			return false
   166  		}
   167  	}
   168  	return len(buf) != 0
   169  }
   170  
   171  func migrateKey(key keyID) (keyID, error) {
   172  	switch checkKeyType(key) {
   173  	case blockMetaKey:
   174  		val, err := strconv.Atoi(string(key[2:]))
   175  		if err != nil {
   176  			return nil, err
   177  		}
   178  
   179  		return orderedcode.Append(nil, int64(0), int64(val))
   180  	case blockPartKey:
   181  		parts := bytes.Split(key[2:], []byte(":"))
   182  		if len(parts) != 2 {
   183  			return nil, fmt.Errorf("block parts key has %d rather than 2 components",
   184  				len(parts))
   185  		}
   186  		valOne, err := strconv.Atoi(string(parts[0]))
   187  		if err != nil {
   188  			return nil, err
   189  		}
   190  
   191  		valTwo, err := strconv.Atoi(string(parts[1]))
   192  		if err != nil {
   193  			return nil, err
   194  		}
   195  
   196  		return orderedcode.Append(nil, int64(1), int64(valOne), int64(valTwo))
   197  	case commitKey:
   198  		val, err := strconv.Atoi(string(key[2:]))
   199  		if err != nil {
   200  			return nil, err
   201  		}
   202  
   203  		return orderedcode.Append(nil, int64(2), int64(val))
   204  	case seenCommitKey:
   205  		val, err := strconv.Atoi(string(key[3:]))
   206  		if err != nil {
   207  			return nil, err
   208  		}
   209  
   210  		return orderedcode.Append(nil, int64(3), int64(val))
   211  	case blockHashKey:
   212  		hash := string(key[3:])
   213  		if len(hash)%2 == 1 {
   214  			hash = "0" + hash
   215  		}
   216  		val, err := hex.DecodeString(hash)
   217  		if err != nil {
   218  			return nil, err
   219  		}
   220  
   221  		return orderedcode.Append(nil, int64(4), string(val))
   222  	case validatorsKey:
   223  		val, err := strconv.Atoi(string(key[14:]))
   224  		if err != nil {
   225  			return nil, err
   226  		}
   227  
   228  		return orderedcode.Append(nil, int64(5), int64(val))
   229  	case consensusParamsKey:
   230  		val, err := strconv.Atoi(string(key[19:]))
   231  		if err != nil {
   232  			return nil, err
   233  		}
   234  
   235  		return orderedcode.Append(nil, int64(6), int64(val))
   236  	case abciResponsesKey:
   237  		val, err := strconv.Atoi(string(key[17:]))
   238  		if err != nil {
   239  			return nil, err
   240  		}
   241  
   242  		return orderedcode.Append(nil, int64(7), int64(val))
   243  	case stateStoreKey:
   244  		return orderedcode.Append(nil, int64(8))
   245  	case evidenceCommittedKey:
   246  		return convertEvidence(key, 9)
   247  	case evidencePendingKey:
   248  		return convertEvidence(key, 10)
   249  	case lightBlockKey:
   250  		if len(key) < 24 {
   251  			return nil, fmt.Errorf("light block evidence %q in invalid format", string(key))
   252  		}
   253  
   254  		val, err := strconv.Atoi(string(key[len(key)-20:]))
   255  		if err != nil {
   256  			return nil, err
   257  		}
   258  
   259  		return orderedcode.Append(nil, int64(11), int64(val))
   260  	case lightSizeKey:
   261  		return orderedcode.Append(nil, int64(12))
   262  	case txHeightKey:
   263  		parts := bytes.Split(key, []byte("/"))
   264  		if len(parts) != 4 {
   265  			return nil, fmt.Errorf("key has %d parts rather than 4", len(parts))
   266  		}
   267  		parts = parts[1:] // drop prefix
   268  
   269  		elems := make([]interface{}, 0, len(parts)+1)
   270  		elems = append(elems, "tx.height")
   271  
   272  		for idx, pt := range parts {
   273  			val, err := strconv.Atoi(string(pt))
   274  			if err != nil {
   275  				return nil, err
   276  			}
   277  			if idx == 0 {
   278  				elems = append(elems, fmt.Sprintf("%d", val))
   279  			} else {
   280  				elems = append(elems, int64(val))
   281  			}
   282  		}
   283  
   284  		return orderedcode.Append(nil, elems...)
   285  	case abciEventKey:
   286  		parts := bytes.Split(key, []byte("/"))
   287  
   288  		elems := make([]interface{}, 0, 4)
   289  		if len(parts) == 4 {
   290  			elems = append(elems, string(parts[0]), string(parts[1]))
   291  
   292  			val, err := strconv.Atoi(string(parts[2]))
   293  			if err != nil {
   294  				return nil, err
   295  			}
   296  			elems = append(elems, int64(val))
   297  
   298  			val2, err := strconv.Atoi(string(parts[3]))
   299  			if err != nil {
   300  				return nil, err
   301  			}
   302  			elems = append(elems, int64(val2))
   303  		} else {
   304  			elems = append(elems, string(parts[0]))
   305  			parts = parts[1:]
   306  
   307  			val, err := strconv.Atoi(string(parts[len(parts)-1]))
   308  			if err != nil {
   309  				return nil, err
   310  			}
   311  
   312  			val2, err := strconv.Atoi(string(parts[len(parts)-2]))
   313  			if err != nil {
   314  				return nil, err
   315  			}
   316  
   317  			appKey := bytes.Join(parts[:len(parts)-3], []byte("/"))
   318  			elems = append(elems, string(appKey), int64(val), int64(val2))
   319  		}
   320  		return orderedcode.Append(nil, elems...)
   321  	case txHashKey:
   322  		return orderedcode.Append(nil, "tx.hash", string(key))
   323  	default:
   324  		return nil, fmt.Errorf("key %q is in the wrong format", string(key))
   325  	}
   326  }
   327  
   328  func convertEvidence(key keyID, newPrefix int64) ([]byte, error) {
   329  	parts := bytes.Split(key[1:], []byte("/"))
   330  	if len(parts) != 2 {
   331  		return nil, fmt.Errorf("evidence key is malformed with %d parts not 2",
   332  			len(parts))
   333  	}
   334  
   335  	hb, err := hex.DecodeString(string(parts[0]))
   336  	if err != nil {
   337  		return nil, err
   338  	}
   339  
   340  	evidenceHash, err := hex.DecodeString(string(parts[1]))
   341  	if err != nil {
   342  		return nil, err
   343  	}
   344  
   345  	return orderedcode.Append(nil, newPrefix, binary.BigEndian.Uint64(hb), string(evidenceHash))
   346  }
   347  
   348  // checkEvidenceKey reports whether a candidate key with one of the legacy
   349  // evidence prefixes has the correct structure for a legacy evidence key.
   350  //
   351  // This check is needed because transaction hashes are stored without a prefix,
   352  // so checking the one-byte prefix alone is not enough to distinguish them.
   353  // Legacy evidence keys are suffixed with a string of the format:
   354  //
   355  //     "%0.16X/%X"
   356  //
   357  // where the first element is the height and the second is the hash.  Thus, we
   358  // check
   359  func checkEvidenceKey(key keyID) bool {
   360  	parts := bytes.SplitN(key[1:], []byte("/"), 2)
   361  	if len(parts) != 2 || len(parts[0]) != 16 || !isHex(parts[0]) || !isHex(parts[1]) {
   362  		return false
   363  	}
   364  	return true
   365  }
   366  
   367  func isHex(data []byte) bool {
   368  	for _, b := range data {
   369  		if ('0' <= b && b <= '9') || ('a' <= b && b <= 'f') || ('A' <= b && b <= 'F') {
   370  			continue
   371  		}
   372  		return false
   373  	}
   374  	return len(data) != 0
   375  }
   376  
   377  func replaceKey(db dbm.DB, key keyID, gooseFn migrateFunc) error {
   378  	exists, err := db.Has(key)
   379  	if err != nil {
   380  		return err
   381  	}
   382  	if !exists {
   383  		return nil
   384  	}
   385  
   386  	newKey, err := gooseFn(key)
   387  	if err != nil {
   388  		return err
   389  	}
   390  
   391  	val, err := db.Get(key)
   392  	if err != nil {
   393  		return err
   394  	}
   395  
   396  	batch := db.NewBatch()
   397  
   398  	if err = batch.Set(newKey, val); err != nil {
   399  		return err
   400  	}
   401  	if err = batch.Delete(key); err != nil {
   402  		return err
   403  	}
   404  
   405  	// 10% of the time, force a write to disk, but mostly don't,
   406  	// because it's faster.
   407  	if rand.Intn(100)%10 == 0 { // nolint:gosec
   408  		if err = batch.WriteSync(); err != nil {
   409  			return err
   410  		}
   411  	} else {
   412  		if err = batch.Write(); err != nil {
   413  			return err
   414  		}
   415  	}
   416  
   417  	if err = batch.Close(); err != nil {
   418  		return err
   419  	}
   420  
   421  	return nil
   422  }
   423  
   424  // Migrate converts all legacy key formats to new key formats. The
   425  // operation is idempotent, so it's safe to resume a failed
   426  // operation. The operation is somewhat parallelized, relying on the
   427  // concurrency safety of the underlying databases.
   428  //
   429  // Migrate has "continue on error" semantics and will iterate through
   430  // all legacy keys attempt to migrate them, and will collect all
   431  // errors and will return only at the end of the operation.
   432  //
   433  // The context allows for a safe termination of the operation
   434  // (e.g connected to a singal handler,) to abort the operation
   435  // in-between migration operations.
   436  func Migrate(ctx context.Context, db dbm.DB) error {
   437  	keys, err := getAllLegacyKeys(db)
   438  	if err != nil {
   439  		return err
   440  	}
   441  
   442  	var errs []string
   443  	g, start := taskgroup.New(func(err error) error {
   444  		errs = append(errs, err.Error())
   445  		return err
   446  	}).Limit(runtime.NumCPU())
   447  
   448  	for _, key := range keys {
   449  		key := key
   450  		start(func() error {
   451  			if err := ctx.Err(); err != nil {
   452  				return err
   453  			}
   454  			return replaceKey(db, key, migrateKey)
   455  		})
   456  	}
   457  	if g.Wait() != nil {
   458  		return fmt.Errorf("encountered errors during migration: %q", errs)
   459  	}
   460  	return nil
   461  }