github.com/sixexorg/magnetic-ring@v0.0.0-20191119090307-31705a21e419/consense/dpoa/statemgr.go (about)

     1  package dpoa
     2  
     3  import (
     4  	"time"
     5  
     6  	"github.com/sixexorg/magnetic-ring/log"
     7  	//"github.com/sixexorg/magnetic-ring/consense/dpoa/comm"
     8  	//"github.com/sixexorg/magnetic-ring/core/types"
     9  	"fmt"
    10  )
    11  
    12  type StateEventType int
    13  
    14  type PeerState struct {
    15  	peerIdx           string
    16  	committedBlockNum uint64
    17  	connected         bool
    18  }
    19  
    20  type stateChange struct {
    21  	currentState     ServerState
    22  }
    23  
    24  const (
    25  	ConfigLoaded     StateEventType = iota
    26  	UpdatePeerState                 // notify statemgmt on peer heartbeat
    27  )
    28  
    29  type ServerState int
    30  
    31  const (
    32  	Init ServerState = iota
    33  	LocalConfigured
    34  	Configured
    35  	Syncing
    36  	WaitNetworkReady
    37  	SyncReady
    38  )
    39  
    40  type StateEvent struct {
    41  	Type      StateEventType
    42  	peerState *PeerState
    43  	blockNum  uint32
    44  }
    45  
    46  func isReady(state ServerState) bool {
    47  	return state >= SyncReady
    48  }
    49  
    50  type StateMgr struct {
    51  	syncReadyTimeout time.Duration
    52  	currentState     ServerState
    53  	StateEventC      chan *StateEvent
    54  	peers            map[string]*PeerState
    55  	liveTicker             *time.Timer
    56  	lastTickChainHeight    uint32
    57  	lastBlockSyncReqHeight uint64
    58  	store            *BlockStore
    59  	peerpool         *PeerPool
    60  	notifyObj        *Feed
    61  	cfg              *Config
    62  	quitC            chan struct{}
    63  }
    64  
    65  func NewStateMgr(store *BlockStore, notifyObj *Feed, cfg *Config) *StateMgr {
    66  	return &StateMgr{
    67  		store:             store,
    68  		syncReadyTimeout: time.Second * 10,
    69  		currentState:     Init,
    70  		StateEventC:      make(chan *StateEvent, 16),
    71  		peers:            make(map[string]*PeerState),
    72  		notifyObj:        notifyObj,
    73  		cfg:              cfg,
    74  		quitC:            make(chan struct{}),
    75  	}
    76  }
    77  
    78  func (self *StateMgr) run() {
    79  	go func() {
    80  	for {
    81  		select {
    82  		case evt := <-self.StateEventC:
    83  			switch evt.Type {
    84  			case ConfigLoaded:
    85  				if self.currentState == Init {
    86  					self.currentState = LocalConfigured
    87  				}
    88  			case UpdatePeerState:
    89  				if evt.peerState.connected {
    90  					if err := self.onPeerUpdate(evt.peerState); err != nil {
    91  						log.Error("statemgr process peer (%d) err: %s", evt.peerState.peerIdx, err)
    92  					}
    93  				} else {
    94  					if err := self.onPeerDisconnected(evt.peerState.peerIdx); err != nil {
    95  						log.Error("statmgr process peer (%d) disconn err: %s", evt.peerState.peerIdx, err)
    96  					}
    97  				}
    98  			}
    99  
   100  		case <-self.quitC:
   101  			log.Info("server %d, state mgr quit", self.cfg.accountStr)
   102  			return
   103  		}
   104  	}
   105  	}()
   106  }
   107  
   108  func (self *StateMgr) onPeerDisconnected(peerIdx string) error {
   109  
   110  	if _, present := self.peers[peerIdx]; !present {
   111  		return nil
   112  	}
   113  	delete(self.peers, peerIdx)
   114  
   115  	// start another connection if necessary
   116  	if self.currentState == SyncReady {
   117  		if self.peerpool.getActivePeerCount() < self.getMinActivePeerCount() {
   118  			self.currentState = WaitNetworkReady
   119  			st := stateChange{
   120  				currentState: self.currentState,
   121  			}
   122  			self.notifyObj.Send(st)
   123  		}
   124  	}
   125  
   126  	return nil
   127  }
   128  
   129  func (self *StateMgr) getConsensusedCommittedBlockNum() (uint64, bool) {
   130  	C := len(self.store.GetCurStars()) / 2
   131  	consensused := false
   132  	var maxCommitted uint64
   133  	myCommitted := self.store.getLatestBlockNumber()
   134  	peers := make(map[uint64][]string)
   135  	for _, p := range self.peers {
   136  		n := p.committedBlockNum
   137  		if n >= myCommitted {
   138  			if _, present := peers[n]; !present {
   139  				peers[n] = make([]string, 0)
   140  			}
   141  			for k := range peers {
   142  				if n >= k {
   143  					peers[k] = append(peers[k], p.peerIdx)
   144  				}
   145  			}
   146  			if len(peers[n]) >= C {
   147  				maxCommitted = n
   148  				consensused = true
   149  			}
   150  		}
   151  	}
   152  
   153  	return maxCommitted, consensused
   154  }
   155  
   156  func (self *StateMgr) isSyncedReady() bool {
   157  	// check action peer connections
   158  	if len(self.peers) < self.getMinActivePeerCount() {
   159  		return false
   160  	}
   161  	// check chain consensus
   162  	committedBlkNum, ok := self.getConsensusedCommittedBlockNum()
   163  	if !ok {
   164  		fmt.Println("StateMgr getConsensusedCommittedBlockNum", committedBlkNum, ok)
   165  		return false
   166  	}
   167  	fmt.Println("StateMgr getLatestBlockNumber",self.store.getLatestBlockNumber(), committedBlkNum)
   168  	if self.store.getLatestBlockNumber() >= committedBlkNum {
   169  		return true
   170  	}
   171  	return false
   172  }
   173  
   174  func (self *StateMgr) setSyncedReady() error {
   175  	prevState := self.currentState
   176  	self.currentState = SyncReady
   177  	if prevState <= SyncReady {
   178  		log.Debug("server %v start sync ready", "self.cfg.accountStr", self.cfg.accountStr)
   179  		//fmt.Println("1@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@setSyncedReady", prevState, self.store.getLatestBlockNumber()+1)
   180  		//<-time.After(time.Second*3)
   181  		//fmt.Println("2@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@setSyncedReady", prevState, self.store.getLatestBlockNumber()+1)
   182  
   183  		st := stateChange{
   184  			currentState:self.currentState,
   185  		}
   186  		self.notifyObj.Send(st)
   187  	}
   188  
   189  	return nil
   190  }
   191  
   192  func (self *StateMgr) onPeerUpdate(peerState *PeerState) error {
   193  	peerIdx := peerState.peerIdx
   194  	newPeer := false
   195  	if _, present := self.peers[peerIdx]; !present {
   196  		newPeer = true
   197  	}
   198  
   199  	log.Info("server peer update ", "accountStr", self.cfg.accountStr, "currentblk", self.store.getLatestBlockNumber()+1, "state", self.currentState,
   200  		"frompeer", peerState.peerIdx, "committed", peerState.committedBlockNum, "newPeer", newPeer)
   201  
   202  	self.peers[peerIdx] = peerState
   203  
   204  	if !newPeer {
   205  		if !self.store.isEarth() && peerState.peerIdx == self.store.earthNode(){
   206  			//if isReady(self.currentState) && peerState.committedBlockNum >= self.store.getLatestBlockNumber()+1 {
   207  			//	log.Warn("server seems lost sync with earth:", "accountStr", self.cfg.accountStr, "committedBlockNum",
   208  			//		peerState.committedBlockNum, "peerIdx", peerState.peerIdx, "getLatestBlockNumber",self.store.getLatestBlockNumber()+1)
   209  			//	if err := self.checkStartSyncing(self.store.getLatestBlockNumber(), true); err != nil {
   210  			//		log.Error("server start syncing check failed", "accountStr", self.cfg.accountStr)
   211  			//		return nil
   212  			//	}
   213  			//}
   214  			return nil
   215  		}else {
   216  			if isReady(self.currentState) && peerState.committedBlockNum > self.store.getLatestBlockNumber()+1+10 {
   217  				log.Warn("server seems lost sync", "accountStr", self.cfg.accountStr, "committedBlockNum",
   218  					peerState.committedBlockNum, "peerIdx", peerState.peerIdx, "getLatestBlockNumber", self.store.getLatestBlockNumber()+1)
   219  					return nil
   220  			}
   221  		}
   222  	}
   223  
   224  	switch self.currentState {
   225  	case LocalConfigured:
   226  		log.Info("server statemgr update","accountStr", self.cfg.accountStr, "currentState",self.currentState, "peerIdx", peerIdx, "peercnt",len(self.peers))
   227  		if self.getSyncedPeers() > 0{
   228  			self.currentState = Syncing
   229  		}
   230  	case Configured:
   231  	case Syncing:
   232  		if peerState.committedBlockNum > self.store.getLatestBlockNumber() {
   233  			committedBlkNum, ok := self.getConsensusedCommittedBlockNum()
   234  			if ok && committedBlkNum > self.store.getLatestBlockNumber() {
   235  				log.Info("server syncing","accountStr", self.cfg.accountStr, "syncing",self.store.getLatestBlockNumber(), "target",committedBlkNum)
   236  				self.checkStartSyncing(self.store.getLatestBlockNumber(), false)
   237  			}
   238  		}
   239  		if self.isSyncedReady() {
   240  			log.Debug("server synced from syncing", "accountStr", self.cfg.accountStr)
   241  			fmt.Println("==>>>setSyncedReady")
   242  			if err := self.setSyncedReady(); err != nil {
   243  				log.Warn("server state set syncready", self.cfg.account.PublicKey, self.currentState, err)
   244  			}
   245  		}
   246  	case WaitNetworkReady:
   247  		if self.isSyncedReady() {
   248  			fmt.Println("==>>>WaitNetworkReady")
   249  			log.Debug("server synced from sync-ready", "accountStr", self.cfg.accountStr)
   250  			if err := self.setSyncedReady(); err != nil {
   251  				log.Warn("server state set syncready", "accountStr", self.cfg.accountStr, "currentState", self.currentState, "err", err)
   252  			}
   253  		}
   254  	case SyncReady:
   255  		committedBlkNum, ok := self.getConsensusedCommittedBlockNum()
   256  		if ok && committedBlkNum > self.store.getLatestBlockNumber()+1 {
   257  			log.Info("server synced try fastforward from", "accountStr", self.cfg.accountStr, "getLatestBlockNumber", self.store.getLatestBlockNumber())
   258  			self.checkStartSyncing(self.store.getLatestBlockNumber(), false)
   259  		}
   260  	}
   261  
   262  	return nil
   263  }
   264  
   265  func (self *StateMgr) checkStartSyncing(startBlkNum uint64, forceSync bool) error {
   266  	if self.store.nonConsensusNode() {
   267  		return nil
   268  	}
   269  
   270  	var maxCommitted uint64
   271  	peers := make(map[uint64][]string)
   272  	for _, p := range self.peers {
   273  		n := p.committedBlockNum
   274  		if n > startBlkNum {
   275  			if _, present := peers[n]; !present {
   276  				peers[n] = make([]string, 0)
   277  			}
   278  			for k := range peers {
   279  				if n >= k {
   280  					peers[k] = append(peers[k], p.peerIdx)
   281  				}
   282  			}
   283  			if len(peers[n]) >= len(self.store.GetCurStars())/2 {
   284  				maxCommitted = n
   285  			}
   286  		}
   287  	}
   288  
   289  	if forceSync && maxCommitted == 0{
   290  		maxCommitted = startBlkNum
   291  	}
   292  
   293  	if maxCommitted > startBlkNum || forceSync {
   294  		log.Debug("server startBlkNum forceSync maxCommitted lastBlockSyncReqHeight", "accountStr",
   295  			self.cfg.accountStr, "startBlkNum", startBlkNum, "forceSync", forceSync, "maxCommitted", maxCommitted, "lastBlockSyncReqHeight", self.lastBlockSyncReqHeight)
   296  		preState := self.currentState
   297  		self.currentState = Syncing
   298  		if preState == SyncReady{
   299  			st := stateChange{
   300  				currentState: self.currentState,
   301  			}
   302  			self.notifyObj.Send(st)
   303  		}
   304  		startBlkNum = self.store.getLatestBlockNumber() + 1
   305  
   306  		if maxCommitted > self.lastBlockSyncReqHeight {
   307  			log.Info("server start syncing", "accountStr", self.cfg.accountStr, "startBlkNum", startBlkNum, "maxCommitted", maxCommitted, "peers", peers)
   308  			self.lastBlockSyncReqHeight = maxCommitted
   309  		}
   310  	}
   311  
   312  	return nil
   313  }
   314  
   315  func (self *StateMgr) getSyncedPeers() uint32 {
   316  	if len(self.peers) < self.getMinActivePeerCount() {
   317  		return 0
   318  	}
   319  
   320  	return uint32(len(self.peers))
   321  }
   322  
   323  func (self *StateMgr) getMinActivePeerCount() int {
   324  	if self.store.isEarth(){
   325  		return len(self.store.GetCurStars())/4
   326  	}
   327  
   328  	return len(self.store.GetCurStars())/2
   329  }
   330  
   331  func (self *StateMgr) getState() ServerState {
   332  	return self.currentState
   333  }