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 }