github.com/aergoio/aergo@v1.3.1/chain/recover.go (about) 1 package chain 2 3 import ( 4 "bytes" 5 "encoding/gob" 6 "errors" 7 "fmt" 8 "github.com/aergoio/aergo/internal/enc" 9 "github.com/aergoio/aergo/types" 10 "os" 11 "runtime" 12 "runtime/debug" 13 ) 14 15 var ( 16 ErrInvalidPrevHash = errors.New("no of previous hash block is invalid") 17 ErrRecoInvalidBest = errors.New("best block is not equal to old chain") 18 ) 19 20 func RecoverExit() { 21 if r := recover(); r != nil { 22 logger.Error().Str("callstack", string(debug.Stack())).Msg("panic occurred in chain manager") 23 os.Exit(10) 24 } 25 } 26 27 // Recover has 2 situation 28 // 1. normal recovery 29 // normal recovery recovers error that has occures while adding single block 30 // 2. reorg recovery 31 // reorg recovery recovers error that has occures while executing reorg 32 func (cs *ChainService) Recover() error { 33 defer RecoverExit() 34 35 logger.Debug().Msg("recover start") 36 37 // check if reorg marker exists 38 marker, err := cs.cdb.getReorgMarker() 39 if err != nil { 40 return err 41 } 42 43 if marker == nil { 44 // normal recover 45 // TODO check state root maker of bestblock 46 if err := cs.recoverNormal(); err != nil { 47 return err 48 } 49 return nil 50 } 51 52 logger.Info().Str("reorg marker", marker.toString()).Msg("chain recovery started") 53 54 runtime.LockOSThread() 55 defer runtime.UnlockOSThread() 56 57 best, err := cs.GetBestBlock() 58 if err != nil { 59 return err 60 } 61 62 // check status of chain 63 if !bytes.Equal(best.BlockHash(), marker.BrBestHash) { 64 logger.Error().Str("best", best.ID()).Str("markerbest", enc.ToString(marker.BrBestHash)).Msg("best block is not equal to old chain") 65 return ErrRecoInvalidBest 66 } 67 68 if err = cs.recoverReorg(marker); err != nil { 69 return err 70 } 71 72 return nil 73 } 74 75 // recover from normal 76 // set stateroot for bestblock 77 // when panic occured, memory state of server may not be consistent. 78 // so restart server when panic in chainservice 79 func (cs *ChainService) recoverNormal() error { 80 best, err := cs.GetBestBlock() 81 if err != nil { 82 return err 83 } 84 85 logger.Info().Msg("start normal recovery") 86 87 stateDB := cs.sdb.GetStateDB() 88 if !stateDB.HasMarker(best.GetHeader().GetBlocksRootHash()) { 89 logger.Warn().Str("besthash", best.ID()).Uint64("no", best.GetHeader().GetBlockNo()).Msg("marker of state root does not exist") 90 } 91 92 if !bytes.Equal(cs.sdb.GetStateDB().GetRoot(), best.GetHeader().GetBlocksRootHash()) { 93 return ErrRecoInvalidSdbRoot 94 } 95 96 logger.Info().Msg("recover normal end") 97 98 return nil 99 } 100 101 // recoverReorg redo task that need to be performed after swapping chain meta 102 // 1. delete receipts of rollbacked blocks 103 // 2. swap tx mapping 104 func (cs *ChainService) recoverReorg(marker *ReorgMarker) error { 105 // build reorgnizer from reorg marker 106 topBlock, err := cs.GetBlock(marker.BrTopHash) 107 if err != nil { 108 return err 109 } 110 111 if err = cs.reorg(topBlock, marker); err != nil { 112 logger.Error().Err(err).Msg("failed to retry reorg") 113 return err 114 } 115 116 logger.Info().Msg("recover reorg end") 117 return nil 118 } 119 120 type ReorgMarker struct { 121 cdb *ChainDB 122 BrStartHash []byte 123 BrStartNo types.BlockNo 124 BrBestHash []byte 125 BrBestNo types.BlockNo 126 BrTopHash []byte 127 BrTopNo types.BlockNo 128 } 129 130 func NewReorgMarker(reorg *reorganizer) *ReorgMarker { 131 return &ReorgMarker{ 132 cdb: reorg.cs.cdb, 133 BrStartHash: reorg.brStartBlock.BlockHash(), 134 BrStartNo: reorg.brStartBlock.GetHeader().GetBlockNo(), 135 BrBestHash: reorg.bestBlock.BlockHash(), 136 BrBestNo: reorg.bestBlock.GetHeader().GetBlockNo(), 137 BrTopHash: reorg.brTopBlock.BlockHash(), 138 BrTopNo: reorg.brTopBlock.GetHeader().GetBlockNo(), 139 } 140 } 141 142 // RecoverChainMapping rollback chain (no/hash) mapping to old chain of reorg. 143 // it is required for LIB loading 144 func (rm *ReorgMarker) RecoverChainMapping(cdb *ChainDB) error { 145 best, err := cdb.GetBestBlock() 146 if err != nil { 147 return err 148 } 149 150 if bytes.Equal(best.BlockHash(), rm.BrBestHash) { 151 return nil 152 } 153 154 logger.Info().Str("marker", rm.toString()).Str("curbest", best.ID()).Uint64("curbestno", best.GetHeader().GetBlockNo()).Msg("start to recover chain mapping") 155 156 bestBlock, err := cdb.getBlock(rm.BrBestHash) 157 if err != nil { 158 return err 159 } 160 161 bulk := cdb.store.NewBulk() 162 defer bulk.DiscardLast() 163 164 var tmpBlkNo types.BlockNo 165 var tmpBlk *types.Block 166 167 // remove unnecessary chain mapping of new chain 168 for tmpBlkNo = rm.BrTopNo; tmpBlkNo > rm.BrBestNo; tmpBlkNo-- { 169 logger.Debug().Uint64("no", tmpBlkNo).Msg("delete chain mapping of new chain") 170 bulk.Delete(types.BlockNoToBytes(tmpBlkNo)) 171 } 172 173 tmpBlk = bestBlock 174 tmpBlkNo = tmpBlk.GetHeader().GetBlockNo() 175 176 for tmpBlkNo > rm.BrStartNo { 177 logger.Debug().Str("hash", tmpBlk.ID()).Uint64("no", tmpBlkNo).Msg("update chain mapping to old chain") 178 179 bulk.Set(types.BlockNoToBytes(tmpBlkNo), tmpBlk.BlockHash()) 180 181 if tmpBlk, err = cdb.getBlock(tmpBlk.GetHeader().GetPrevBlockHash()); err != nil { 182 return err 183 } 184 185 if tmpBlkNo != tmpBlk.GetHeader().GetBlockNo()+1 { 186 return ErrInvalidPrevHash 187 } 188 tmpBlkNo = tmpBlk.GetHeader().GetBlockNo() 189 } 190 191 logger.Info().Uint64("bestno", rm.BrBestNo).Msg("update best block") 192 193 bulk.Set(latestKey, types.BlockNoToBytes(rm.BrBestNo)) 194 bulk.Flush() 195 196 cdb.setLatest(bestBlock) 197 198 logger.Info().Msg("succeed to recover chain mapping") 199 return nil 200 } 201 202 func (rm *ReorgMarker) setCDB(cdb *ChainDB) { 203 rm.cdb = cdb 204 } 205 206 func (rm *ReorgMarker) write() error { 207 if err := rm.cdb.writeReorgMarker(rm); err != nil { 208 return err 209 } 210 211 return nil 212 } 213 214 func (rm *ReorgMarker) delete() { 215 rm.cdb.deleteReorgMarker() 216 } 217 218 func (rm *ReorgMarker) toBytes() ([]byte, error) { 219 var val bytes.Buffer 220 encoder := gob.NewEncoder(&val) 221 if err := encoder.Encode(rm); err != nil { 222 return nil, err 223 } 224 225 return val.Bytes(), nil 226 } 227 228 func (rm *ReorgMarker) toString() string { 229 buf := "" 230 231 if len(rm.BrStartHash) != 0 { 232 buf = buf + fmt.Sprintf("branch root=(%d, %s).", rm.BrStartNo, enc.ToString(rm.BrStartHash)) 233 } 234 if len(rm.BrTopHash) != 0 { 235 buf = buf + fmt.Sprintf("branch top=(%d, %s).", rm.BrTopNo, enc.ToString(rm.BrTopHash)) 236 } 237 if len(rm.BrBestHash) != 0 { 238 buf = buf + fmt.Sprintf("org best=(%d, %s).", rm.BrBestNo, enc.ToString(rm.BrBestHash)) 239 } 240 241 return buf 242 }