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 }