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  }