github.com/aergoio/aergo@v1.3.1/consensus/impl/dpos/status.go (about) 1 package dpos 2 3 import ( 4 "encoding/json" 5 "sync" 6 7 "github.com/aergoio/aergo/consensus" 8 "github.com/aergoio/aergo/consensus/impl/dpos/bp" 9 "github.com/aergoio/aergo/state" 10 "github.com/aergoio/aergo/types" 11 ) 12 13 var bsLoader *bootLoader 14 15 // Status manages DPoS-related infomations like LIB. 16 type Status struct { 17 sync.RWMutex 18 done bool 19 bestBlock *types.Block 20 libState *libStatus 21 bps *bp.Snapshots 22 } 23 24 // NewStatus returns a newly allocated Status. 25 func NewStatus(c bp.ClusterMember, cdb consensus.ChainDB, sdb *state.ChainStateDB, resetHeight types.BlockNo) *Status { 26 s := &Status{ 27 libState: newLibStatus(consensusBlockCount(c.Size())), 28 bps: bp.NewSnapshots(c, cdb, sdb), 29 } 30 s.init(cdb, resetHeight) 31 32 return s 33 } 34 35 // load restores the last LIB status by using the informations loaded from the 36 // DB. 37 func (s *Status) load() { 38 if s.done { 39 return 40 } 41 42 s.bestBlock = bsLoader.bestBlock() 43 44 s.libState = bsLoader.ls 45 46 if bsLoader.ls != nil { 47 s.libState = bsLoader.ls 48 } 49 50 genesisBlock := bsLoader.genesisBlock() 51 s.libState.genesisInfo = &blockInfo{ 52 BlockHash: genesisBlock.ID(), 53 BlockNo: genesisBlock.BlockNo(), 54 } 55 56 s.done = true 57 } 58 59 // Update updates the last irreversible block (LIB). 60 func (s *Status) Update(block *types.Block) { 61 s.Lock() 62 defer s.Unlock() 63 64 // TODO: move the lib status loading to dpos.NewStatus. 65 s.load() 66 67 curBestID := s.bestBlock.ID() 68 if curBestID == block.PrevID() { 69 s.libState.addConfirmInfo(block) 70 71 logger.Debug(). 72 Str("block hash", block.ID()). 73 Uint64("block no", block.BlockNo()). 74 Msg("update LIB status") 75 76 // Block connected 77 if lib := s.libState.update(); lib != nil { 78 s.updateLIB(lib) 79 } 80 81 s.bps.AddSnapshot(block.BlockNo()) 82 } else { 83 // Rollback resulting from a reorganization. 84 logger.Debug(). 85 Str("block hash", block.ID()). 86 Uint64("target block no", block.BlockNo()). 87 Msg("rollback LIB status") 88 89 // Block reorganized. TODO: update consensus status, correctly. 90 if err := s.libState.rollbackStatusTo(block, s.libState.Lib); err != nil { 91 logger.Debug().Err(err).Msg("failed to rollback DPoS status") 92 panic(err) 93 } 94 95 // Rollback BP list. -- BP list is alos affected by a fork. 96 s.bps.UpdateCluster(block.BlockNo()) 97 } 98 99 s.libState.gc() 100 101 s.bestBlock = block 102 } 103 104 func (s *Status) libNo() types.BlockNo { 105 s.RLock() 106 defer s.RUnlock() 107 return s.libState.libNo() 108 } 109 110 func (s *Status) lib() *blockInfo { 111 s.RLock() 112 defer s.RUnlock() 113 return s.libState.lib() 114 } 115 116 func (s *Status) libAsJSON() *json.RawMessage { 117 lib := s.lib() 118 if lib == nil || lib.BlockNo == 0 { 119 return nil 120 } 121 122 l := &struct { 123 LibHash string 124 LibNo types.BlockNo 125 }{ 126 LibHash: lib.BlockHash, 127 LibNo: lib.BlockNo, 128 } 129 130 if b, err := json.Marshal(l); err == nil { 131 m := json.RawMessage(b) 132 return &m 133 } 134 135 return nil 136 } 137 138 func (s *Status) updateLIB(lib *blockInfo) { 139 s.libState.Lib = lib 140 141 logger.Debug(). 142 Str("block hash", s.libState.Lib.BlockHash). 143 Uint64("block no", s.libState.Lib.BlockNo). 144 Int("confirms len", s.libState.confirms.Len()). 145 Int("pm len", len(s.libState.Prpsd)). 146 Msg("last irreversible block (BFT) updated") 147 } 148 149 // Save saves the consensus status information for the later recovery. 150 func (s *Status) Save(tx consensus.TxWriter) error { 151 s.Lock() 152 defer s.Unlock() 153 154 if err := s.libState.save(tx); err != nil { 155 return err 156 } 157 158 return nil 159 } 160 161 // NeedReorganization reports whether reorganization is needed or not. 162 func (s *Status) NeedReorganization(rootNo types.BlockNo) bool { 163 s.RLock() 164 defer s.RUnlock() 165 166 if s.libState.Lib == nil { 167 logger.Debug().Uint64("branch root no", rootNo).Msg("no LIB") 168 return true 169 } 170 171 libNo := s.libState.Lib.BlockNo 172 173 reorganizable := rootNo >= libNo 174 if !reorganizable { 175 logger.Info(). 176 Uint64("LIB", libNo). 177 Uint64("branch root no", rootNo). 178 Msg("reorganization beyond LIB is not allowed") 179 } 180 181 return reorganizable 182 } 183 184 // Info returns the current last irreversible block information as a JSON 185 // string. 186 func (s *Status) Info() string { 187 return s.String() 188 } 189 190 // String returns the current LIB as a JSON string. 191 func (s *Status) String() string { 192 info := consensus.NewInfo(GetName()) 193 info.Status = s.libAsJSON() 194 195 return info.AsJSON() 196 } 197 198 func (s *Status) lpbNo() types.BlockNo { 199 return s.libState.LpbNo 200 } 201 202 // init recovers the last DPoS status including pre-LIB map and confirms 203 // list between LIB and the best block. 204 func (s *Status) init(cdb consensus.ChainDB, resetHeight types.BlockNo) { 205 if cdb == nil { 206 return 207 } 208 209 genesis, err := cdb.GetBlockByNo(0) 210 if err != nil { 211 panic(err) 212 } 213 214 best, err := cdb.GetBestBlock() 215 if err != nil { 216 best = genesis 217 } 218 219 bsLoader = &bootLoader{ 220 ls: newLibStatus(s.libState.confirmsRequired), 221 best: best, 222 genesis: genesis, 223 cdb: cdb, 224 confirmsRequired: s.libState.confirmsRequired, 225 } 226 227 bsLoader.load(resetHeight) 228 } 229 230 type bootLoader struct { 231 ls *libStatus 232 best *types.Block 233 genesis *types.Block 234 cdb consensus.ChainDB 235 confirmsRequired uint16 236 } 237 238 func (bs *bootLoader) load(resetHeight types.BlockNo) { 239 if ls := bs.loadLibStatus(); ls != nil { 240 bs.ls = ls 241 logger.Debug().Int("proposed lib len", len(ls.Prpsd)).Msg("lib status loaded from DB") 242 243 for id, p := range ls.Prpsd { 244 if p == nil { 245 continue 246 } 247 248 // Remove the too high pre-LIBs when the ForceResetHeight parameter is set. 249 if resetHeight > 0 && (p.Plib.BlockNo > resetHeight || p.PlibBy.BlockNo > resetHeight) { 250 delete(ls.Prpsd, id) 251 } 252 253 logger.Debug().Str("BPID", id). 254 Str("confirmed hash", p.Plib.Hash()). 255 Str("confirmedBy hash", p.PlibBy.Hash()). 256 Msg("pre-LIB entry") 257 } 258 259 // Reset the LIB when the ForceResetHeight parameter is set. 260 if resetHeight > 0 && ls.Lib.BlockNo > resetHeight { 261 ls.Lib = &blockInfo{ 262 BlockHash: bs.genesisBlock().ID(), 263 BlockNo: bs.genesisBlock().BlockNo(), 264 } 265 266 tx := bs.cdb.NewTx() 267 reset(tx) 268 tx.Commit() 269 } 270 } 271 }