github.com/iotexproject/iotex-core@v1.14.1-rc1/tools/staterecoverer/staterecoverer.go (about)

     1  // Copyright (c) 2019 IoTeX Foundation
     2  // This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability
     3  // or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed.
     4  // This source code is governed by Apache License 2.0 that can be found in the LICENSE file.
     5  
     6  // This is a recovery tool that recovers a corrupted or missing state database.
     7  // To use, run "make recover"
     8  package main
     9  
    10  import (
    11  	"context"
    12  	"flag"
    13  	"fmt"
    14  	glog "log"
    15  	"os"
    16  	"strings"
    17  
    18  	"go.uber.org/zap"
    19  
    20  	"github.com/iotexproject/iotex-core/blockchain/blockdao"
    21  	"github.com/iotexproject/iotex-core/blockchain/genesis"
    22  	"github.com/iotexproject/iotex-core/config"
    23  	"github.com/iotexproject/iotex-core/pkg/log"
    24  	"github.com/iotexproject/iotex-core/server/itx"
    25  	"github.com/iotexproject/iotex-core/state/factory"
    26  )
    27  
    28  // recoveryHeight is the blockchain height being recovered to
    29  var recoveryHeight int
    30  
    31  /**
    32   * overwritePath is the path to the config file which overwrite default values
    33   * secretPath is the path to the  config file store secret values
    34   */
    35  var (
    36  	genesisPath    string
    37  	_overwritePath string
    38  	_secretPath    string
    39  	_plugins       strs
    40  )
    41  
    42  type strs []string
    43  
    44  func (ss *strs) String() string {
    45  	return strings.Join(*ss, ",")
    46  }
    47  
    48  func (ss *strs) Set(str string) error {
    49  	*ss = append(*ss, str)
    50  	return nil
    51  }
    52  
    53  func init() {
    54  	flag.StringVar(&genesisPath, "genesis-path", "", "Genesis path")
    55  	flag.StringVar(&_overwritePath, "config-path", "", "Config path")
    56  	flag.StringVar(&_secretPath, "secret-path", "", "Secret path")
    57  	flag.Var(&_plugins, "plugin", "Plugin of the node")
    58  	flag.IntVar(&recoveryHeight, "recovery-height", 0, "Recovery height")
    59  	flag.Usage = func() {
    60  		_, _ = fmt.Fprintf(os.Stderr,
    61  			"usage: recover -config-path=[string]\n -recovery-height=[int]\n")
    62  		flag.PrintDefaults()
    63  		os.Exit(2)
    64  	}
    65  	flag.Parse()
    66  }
    67  
    68  func main() {
    69  	genesisCfg, err := genesis.New(genesisPath)
    70  	if err != nil {
    71  		glog.Fatalln("Failed to new genesis config.", zap.Error(err))
    72  	}
    73  
    74  	cfg, err := config.New([]string{_overwritePath, _secretPath}, _plugins)
    75  	if err != nil {
    76  		glog.Fatalln("Failed to new config.", zap.Error(err))
    77  	}
    78  
    79  	cfg.Genesis = genesisCfg
    80  
    81  	log.S().Infof("Config in use: %+v", cfg)
    82  
    83  	// create server
    84  	svr, err := itx.NewServer(cfg)
    85  	if err != nil {
    86  		log.L().Fatal("Failed to create server.", zap.Error(err))
    87  	}
    88  
    89  	// recover chain and state
    90  	bc := svr.ChainService(cfg.Chain.ID).Blockchain()
    91  	sf := svr.ChainService(cfg.Chain.ID).StateFactory()
    92  	dao := svr.ChainService(cfg.Chain.ID).BlockDAO()
    93  	if err := bc.Start(context.Background()); err == nil {
    94  		log.L().Info("State DB status is normal.")
    95  	}
    96  	defer func() {
    97  		if err := bc.Stop(context.Background()); err != nil {
    98  			log.L().Fatal("Failed to stop blockchain")
    99  		}
   100  	}()
   101  	if err := recoverChainAndState(dao, sf, cfg, uint64(recoveryHeight)); err != nil {
   102  		log.L().Fatal("Failed to recover chain and state.", zap.Error(err))
   103  	} else {
   104  		log.S().Infof("Success to recover chain and state to target height %d", recoveryHeight)
   105  	}
   106  }
   107  
   108  // recoverChainAndState recovers the chain to target height and refresh state db if necessary
   109  func recoverChainAndState(dao blockdao.BlockDAO, sf factory.Factory, cfg config.Config, targetHeight uint64) error {
   110  	// TODO: not all indexers could be reverted, e.g., state factory. Thus, dao.DeleteTipBlockToTarget will always
   111  	// return error. Therefore, it is not hard to tell that this tool is not working. The right way is to revert indexer
   112  	// one by one. Before we implement it in that way, comment out the following code.
   113  	/*
   114  		if err := dao.DeleteBlockToTarget(targetHeight); err != nil {
   115  			return errors.Wrapf(err, "failed to recover blockchain to target height %d", targetHeight)
   116  		}
   117  		stateHeight, err := sf.Height()
   118  		if err != nil {
   119  			return err
   120  		}
   121  		if targetHeight < stateHeight {
   122  			// delete existing state DB (build from scratch)
   123  			if fileutil.FileExists(cfg.Chain.TrieDBPath) && os.Remove(cfg.Chain.TrieDBPath) != nil {
   124  				return errors.New("failed to delete existing state DB")
   125  			}
   126  		}
   127  	*/
   128  	panic("not implemented")
   129  }