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  }