github.com/aergoio/aergo@v1.3.1/consensus/impl/dpos/lib.go (about) 1 package dpos 2 3 import ( 4 "container/list" 5 "fmt" 6 "github.com/aergoio/aergo/p2p/p2pkey" 7 "sort" 8 9 "github.com/aergoio/aergo-lib/db" 10 "github.com/aergoio/aergo/consensus" 11 "github.com/aergoio/aergo/internal/common" 12 "github.com/aergoio/aergo/types" 13 "github.com/davecgh/go-spew/spew" 14 ) 15 16 // LibStatusKey is the key when a LIB information is put into the chain DB. 17 var LibStatusKey = []byte("dpos.LibStatus") 18 19 type errLibUpdate struct { 20 current string 21 parent string 22 oldBest string 23 } 24 25 func (e errLibUpdate) Error() string { 26 return fmt.Sprintf( 27 "current block %v (parent %v) inconsistent with old best %v", 28 e.current, e.parent, e.oldBest) 29 } 30 31 type proposed map[string]*plInfo 32 33 func (pm proposed) set(bpID string, pl *plInfo) { 34 pm[bpID] = pl 35 logger.Debug().Str("BP", bpID). 36 Str("hash", pl.Plib.BlockHash).Uint64("no", pl.Plib.BlockNo). 37 Str("hash", pl.PlibBy.BlockHash).Uint64("no", pl.PlibBy.BlockNo). 38 Msg("proposed LIB map updated") 39 } 40 41 type libStatus struct { 42 Prpsd proposed // BP-wise proposed LIB map 43 Lib *blockInfo 44 LpbNo types.BlockNo 45 confirms *list.List 46 genesisInfo *blockInfo 47 bpid string 48 confirmsRequired uint16 49 } 50 51 func newLibStatus(confirmsRequired uint16) *libStatus { 52 return &libStatus{ 53 Prpsd: make(proposed), 54 Lib: &blockInfo{}, 55 confirms: list.New(), 56 bpid: p2pkey.NodeSID(), 57 confirmsRequired: confirmsRequired, 58 } 59 } 60 61 func (ls libStatus) lpbNo() types.BlockNo { 62 return ls.LpbNo 63 } 64 65 func (ls libStatus) libNo() types.BlockNo { 66 return ls.Lib.BlockNo 67 } 68 69 func (ls libStatus) lib() *blockInfo { 70 return ls.Lib 71 } 72 73 func (ls *libStatus) addConfirmInfo(block *types.Block) { 74 // Genesis block must not be added. 75 if block.BlockNo() == 0 { 76 return 77 } 78 79 ci := newConfirmInfo(block, ls.confirmsRequired) 80 81 bi := ci.blockInfo 82 83 if e := ls.confirms.PushBack(ci); e.Prev() != nil { 84 prevBi := cInfo(e.Prev()).blockInfo 85 if bi.BlockNo != prevBi.BlockNo+1 { 86 logger.Error(). 87 Uint64("prev no", prevBi.BlockNo).Uint64("current no", bi.BlockNo). 88 Msg("inconsistent confirm info found") 89 } 90 91 } 92 93 // Initialize an empty pre-LIB map entry with genesis block info. 94 if _, exist := ls.Prpsd[ci.bpid]; !exist { 95 ls.updatePreLIB(ci.bpid, 96 &plInfo{ 97 Plib: ls.genesisInfo, 98 PlibBy: ls.genesisInfo, 99 }, 100 ) 101 } 102 103 if ci.bpid == ls.bpid { 104 ls.LpbNo = block.BlockNo() 105 } 106 107 logger.Debug().Str("BP", ci.bpid). 108 Str("hash", bi.BlockHash).Uint64("no", bi.BlockNo). 109 Uint64("range", ci.ConfirmRange).Uint16("confirms left", ci.confirmsLeft). 110 Msg("new confirm info added") 111 } 112 113 func (ls *libStatus) update() *blockInfo { 114 if bpID, pl := ls.getPreLIB(); pl != nil { 115 ls.updatePreLIB(bpID, pl) 116 117 return ls.calcLIB() 118 } 119 return nil 120 } 121 122 func (ls *libStatus) updatePreLIB(bpID string, pl *plInfo) { 123 ls.Prpsd.set(bpID, pl) 124 } 125 126 func (ls *libStatus) getPreLIB() (bpID string, pl *plInfo) { 127 var ( 128 confirmed *blockInfo 129 last = ls.confirms.Back() 130 lastCi = cInfo(last) 131 confirmedBy = lastCi.blockInfo 132 ) 133 134 min := lastCi.BlockNo - lastCi.ConfirmRange + 1 135 max := lastCi.BlockNo 136 for e := last; e != nil; e = e.Prev() { 137 c := cInfo(e) 138 if c.BlockNo >= min && c.BlockNo <= max { 139 c.confirmsLeft-- 140 } 141 142 if c.confirmsLeft == 0 { 143 confirmed = c.bInfo() 144 break 145 } 146 } 147 148 if confirmed != nil { 149 bpID = lastCi.bpid 150 pl = &plInfo{Plib: confirmed, PlibBy: confirmedBy} 151 } 152 153 return 154 } 155 156 func (ls *libStatus) begRecoBlockNo(endBlockNo types.BlockNo) types.BlockNo { 157 offset := 3 * types.BlockNo(ls.confirmsRequired) 158 159 libBlockNo := ls.Lib.BlockNo 160 161 // To reduce IO operation 162 begNo := endBlockNo 163 if begNo < libBlockNo { 164 begNo = libBlockNo 165 } 166 167 if begNo > offset { 168 begNo -= offset 169 } else { 170 begNo = 1 171 } 172 173 return begNo 174 } 175 176 func (ls *libStatus) rollbackStatusTo(block *types.Block, lib *blockInfo) error { 177 targetBlockNo := block.BlockNo() 178 179 logger.Debug(). 180 Uint64("target no", targetBlockNo).Int("confirms len", ls.confirms.Len()). 181 Msg("start LIB status rollback") 182 183 ls.load(targetBlockNo) 184 185 return nil 186 } 187 188 func (ls *libStatus) load(endBlockNo types.BlockNo) { 189 // Remove all the previous confirmation info. 190 if ls.confirms.Len() > 0 { 191 ls.confirms.Init() 192 } 193 194 // Nothing left for the genesis block. 195 if endBlockNo == 0 { 196 return 197 } 198 199 begBlockNo := ls.begRecoBlockNo(endBlockNo) 200 201 // Rebuild confirms info & pre-LIB map from LIB + 1 and block based on 202 // the blocks. 203 if tmp := loadPlibStatus(begBlockNo, endBlockNo, ls.confirmsRequired); tmp != nil { 204 if tmp.confirms.Len() > 0 { 205 ls.confirms = tmp.confirms 206 } 207 for bpID, v := range tmp.Prpsd { 208 if v != nil && v.Plib.BlockNo > 0 { 209 ls.Prpsd[bpID] = v 210 } 211 } 212 } 213 } 214 215 func (ls *libStatus) save(tx consensus.TxWriter) error { 216 b, err := common.GobEncode(ls) 217 if err != nil { 218 return err 219 } 220 221 tx.Set(LibStatusKey, b) 222 223 logger.Debug().Int("proposed lib len", len(ls.Prpsd)).Msg("lib status stored to DB") 224 225 return nil 226 } 227 228 func reset(tx db.Transaction) { 229 tx.Delete(LibStatusKey) 230 } 231 232 func (ls *libStatus) gc() { 233 // GC based on the LIB no 234 if ls.Lib != nil { 235 removeIf(ls.confirms, 236 func(e *list.Element) bool { 237 return cInfo(e).BlockNo <= ls.Lib.BlockNo 238 }, 239 func(e *list.Element) bool { 240 return cInfo(e).BlockNo > ls.Lib.BlockNo 241 }, 242 ) 243 } 244 // GC based on the element no 245 limitConfirms := ls.gcNumLimit() 246 nRemoved := 0 247 for ls.confirms.Len() > limitConfirms { 248 ls.confirms.Remove(ls.confirms.Front()) 249 nRemoved++ 250 } 251 252 if nRemoved > 0 { 253 logger.Debug().Int("len", ls.confirms.Len()). 254 Int("limit", limitConfirms).Int("removed", nRemoved). 255 Msg("number-based GC done for confirms list") 256 } 257 258 } 259 260 func (ls libStatus) gcNumLimit() int { 261 return int(ls.confirmsRequired * 3) 262 } 263 264 func removeIf(l *list.List, p func(e *list.Element) bool, bc func(e *list.Element) bool) { 265 forEachCond(l, 266 func(e *list.Element) { 267 if p(e) { 268 l.Remove(e) 269 } 270 }, 271 bc, 272 ) 273 } 274 275 func forEachCond(l *list.List, f func(e *list.Element), bc func(e *list.Element) bool) { 276 e := l.Front() 277 for e != nil { 278 next := e.Next() 279 if bc(e) { 280 break 281 } 282 f(e) 283 e = next 284 } 285 } 286 287 func (c *confirmInfo) bInfo() *blockInfo { 288 return c.blockInfo 289 } 290 291 func cInfo(e *list.Element) *confirmInfo { 292 return e.Value.(*confirmInfo) 293 } 294 295 func (ls *libStatus) calcLIB() *blockInfo { 296 if len(ls.Prpsd) == 0 { 297 return nil 298 } 299 300 libInfos := make([]*plInfo, 0, len(ls.Prpsd)) 301 for _, l := range ls.Prpsd { 302 if l != nil { 303 libInfos = append(libInfos, l) 304 } 305 } 306 307 if len(libInfos) == 0 { 308 return nil 309 } 310 311 sort.Slice(libInfos, func(i, j int) bool { 312 return libInfos[i].Plib.BlockNo < libInfos[j].Plib.BlockNo 313 }) 314 315 // TODO: check the correctness of the formula. 316 lib := libInfos[(len(libInfos)-1)/3] 317 318 return lib.Plib 319 } 320 321 type blockInfo struct { 322 BlockHash string 323 BlockNo uint64 324 ConfirmRange uint64 325 } 326 327 func newBlockInfo(block *types.Block) *blockInfo { 328 return &blockInfo{ 329 BlockHash: block.ID(), 330 BlockNo: block.BlockNo(), 331 ConfirmRange: block.GetHeader().GetConfirms(), 332 } 333 } 334 335 func (bi *blockInfo) Hash() string { 336 if bi == nil { 337 return "(nil)" 338 } 339 return bi.BlockHash 340 } 341 342 type plInfo struct { 343 PlibBy *blockInfo // the block info by which a block becomes pre-LIB. 344 Plib *blockInfo // pre-LIB 345 } 346 347 type confirmInfo struct { 348 *blockInfo 349 bpid string 350 confirmsLeft uint16 351 } 352 353 func newConfirmInfo(block *types.Block, confirmsRequired uint16) *confirmInfo { 354 return &confirmInfo{ 355 bpid: block.BPID2Str(), 356 blockInfo: newBlockInfo(block), 357 confirmsLeft: confirmsRequired, 358 } 359 } 360 361 func (bs *bootLoader) loadLibStatus() *libStatus { 362 pls := newLibStatus(bs.confirmsRequired) 363 if err := bs.decodeStatus(LibStatusKey, pls); err != nil { 364 return nil 365 } 366 pls.load(bs.best.BlockNo()) 367 368 return pls 369 } 370 371 func (bs *bootLoader) decodeStatus(key []byte, dst interface{}) error { 372 value := bs.cdb.Get(key) 373 if len(value) == 0 { 374 return fmt.Errorf("LIB status not found: key = %v", string(key)) 375 } 376 377 err := common.GobDecode(value, dst) 378 if err != nil { 379 logger.Debug().Err(err).Str("key", string(key)). 380 Msg("failed to decode DPoS status") 381 panic(err) 382 } 383 return nil 384 } 385 386 func loadPlibStatus(begBlockNo, endBlockNo types.BlockNo, confirmsRequired uint16) *libStatus { 387 if begBlockNo == endBlockNo { 388 return nil 389 } else if begBlockNo > endBlockNo { 390 logger.Info().Uint64("beg", begBlockNo).Uint64("end", endBlockNo). 391 Msg("skip pre-LIB status recovery due to the invalid block range") 392 return nil 393 } else if begBlockNo == 0 { 394 begBlockNo = 1 395 } 396 397 pls := newLibStatus(confirmsRequired) 398 pls.genesisInfo = newBlockInfo(bsLoader.genesis) 399 400 logger.Debug().Uint64("beginning", begBlockNo).Uint64("ending", endBlockNo). 401 Msg("restore pre-LIB status from blocks") 402 for i := begBlockNo; i <= endBlockNo; i++ { 403 block, err := bsLoader.cdb.GetBlockByNo(i) 404 if err != nil { 405 // XXX Better error handling?! 406 logger.Error().Err(err).Msg("failed to read block") 407 return nil 408 } 409 pls.addConfirmInfo(block) 410 pls.update() 411 } 412 413 return pls 414 } 415 416 func (bs *bootLoader) bestBlock() *types.Block { 417 return bs.best 418 } 419 420 func (bs *bootLoader) genesisBlock() *types.Block { 421 return bs.genesis 422 } 423 424 func (bs *bootLoader) lpbNo() types.BlockNo { 425 return bs.ls.lpbNo() 426 } 427 428 func dumpConfirmInfo(name string, l *list.List) { 429 forEach(l, 430 func(e *list.Element) { 431 logger.Debug().Str("confirm info", spew.Sdump(cInfo(e))).Msg(name) 432 }, 433 ) 434 } 435 436 func forEach(l *list.List, f func(e *list.Element)) { 437 for e := l.Front(); e != nil; e = e.Next() { 438 f(e) 439 } 440 }