github.com/okex/exchain@v1.8.0/libs/tendermint/blockchain/v2/reactor.go (about) 1 package v2 2 3 import ( 4 "errors" 5 "fmt" 6 "sync" 7 "time" 8 9 "github.com/tendermint/go-amino" 10 11 "github.com/okex/exchain/libs/tendermint/behaviour" 12 "github.com/okex/exchain/libs/tendermint/libs/log" 13 "github.com/okex/exchain/libs/tendermint/p2p" 14 "github.com/okex/exchain/libs/tendermint/state" 15 "github.com/okex/exchain/libs/tendermint/types" 16 ) 17 18 //------------------------------------- 19 20 type bcBlockRequestMessage struct { 21 Height int64 22 } 23 24 // ValidateBasic performs basic validation. 25 func (m *bcBlockRequestMessage) ValidateBasic() error { 26 if m.Height < 0 { 27 return errors.New("negative Height") 28 } 29 return nil 30 } 31 32 func (m *bcBlockRequestMessage) String() string { 33 return fmt.Sprintf("[bcBlockRequestMessage %v]", m.Height) 34 } 35 36 type bcNoBlockResponseMessage struct { 37 Height int64 38 } 39 40 // ValidateBasic performs basic validation. 41 func (m *bcNoBlockResponseMessage) ValidateBasic() error { 42 if m.Height < 0 { 43 return errors.New("negative Height") 44 } 45 return nil 46 } 47 48 func (m *bcNoBlockResponseMessage) String() string { 49 return fmt.Sprintf("[bcNoBlockResponseMessage %d]", m.Height) 50 } 51 52 //------------------------------------- 53 54 type bcBlockResponseMessage struct { 55 Block *types.Block 56 } 57 58 // ValidateBasic performs basic validation. 59 func (m *bcBlockResponseMessage) ValidateBasic() error { 60 if m.Block == nil { 61 return errors.New("block response message has nil block") 62 } 63 64 return m.Block.ValidateBasic() 65 } 66 67 func (m *bcBlockResponseMessage) String() string { 68 return fmt.Sprintf("[bcBlockResponseMessage %v]", m.Block.Height) 69 } 70 71 //------------------------------------- 72 73 type bcStatusRequestMessage struct { 74 Height int64 75 Base int64 76 } 77 78 // ValidateBasic performs basic validation. 79 func (m *bcStatusRequestMessage) ValidateBasic() error { 80 if m.Base < 0 { 81 return errors.New("negative Base") 82 } 83 if m.Height < 0 { 84 return errors.New("negative Height") 85 } 86 if m.Base > m.Height { 87 return fmt.Errorf("base %v cannot be greater than height %v", m.Base, m.Height) 88 } 89 return nil 90 } 91 92 func (m *bcStatusRequestMessage) String() string { 93 return fmt.Sprintf("[bcStatusRequestMessage %v:%v]", m.Base, m.Height) 94 } 95 96 //------------------------------------- 97 98 type bcStatusResponseMessage struct { 99 Height int64 100 Base int64 101 } 102 103 // ValidateBasic performs basic validation. 104 func (m *bcStatusResponseMessage) ValidateBasic() error { 105 if m.Base < 0 { 106 return errors.New("negative Base") 107 } 108 if m.Height < 0 { 109 return errors.New("negative Height") 110 } 111 if m.Base > m.Height { 112 return fmt.Errorf("base %v cannot be greater than height %v", m.Base, m.Height) 113 } 114 return nil 115 } 116 117 func (m *bcStatusResponseMessage) String() string { 118 return fmt.Sprintf("[bcStatusResponseMessage %v:%v]", m.Base, m.Height) 119 } 120 121 type blockStore interface { 122 LoadBlock(height int64) *types.Block 123 SaveBlock(*types.Block, *types.PartSet, *types.Commit) 124 Base() int64 125 Height() int64 126 } 127 128 // BlockchainReactor handles fast sync protocol. 129 type BlockchainReactor struct { 130 p2p.BaseReactor 131 132 fastSync bool // if true, enable fast sync on start 133 events chan Event // XXX: Rename eventsFromPeers 134 scheduler *Routine 135 processor *Routine 136 logger log.Logger 137 138 mtx sync.RWMutex 139 maxPeerHeight int64 140 syncHeight int64 141 142 reporter behaviour.Reporter 143 io iIO 144 store blockStore 145 } 146 147 //nolint:unused,deadcode 148 type blockVerifier interface { 149 VerifyCommit(chainID string, blockID types.BlockID, height int64, commit *types.Commit) error 150 } 151 152 //nolint:deadcode 153 type blockApplier interface { 154 ApplyBlock(state state.State, blockID types.BlockID, block *types.Block) (state.State, int64, error) 155 } 156 157 // XXX: unify naming in this package around tmState 158 // XXX: V1 stores a copy of state as initialState, which is never mutated. Is that nessesary? 159 func newReactor(state state.State, store blockStore, reporter behaviour.Reporter, 160 blockApplier blockApplier, bufferSize int, fastSync bool) *BlockchainReactor { 161 scheduler := newScheduler(state.LastBlockHeight, time.Now()) 162 pContext := newProcessorContext(store, blockApplier, state) 163 // TODO: Fix naming to just newProcesssor 164 // newPcState requires a processorContext 165 processor := newPcState(pContext) 166 167 return &BlockchainReactor{ 168 events: make(chan Event, bufferSize), 169 scheduler: newRoutine("scheduler", scheduler.handle, bufferSize), 170 processor: newRoutine("processor", processor.handle, bufferSize), 171 store: store, 172 reporter: reporter, 173 logger: log.NewNopLogger(), 174 fastSync: fastSync, 175 } 176 } 177 178 // NewBlockchainReactor creates a new reactor instance. 179 func NewBlockchainReactor( 180 state state.State, 181 blockApplier blockApplier, 182 store blockStore, 183 fastSync bool) *BlockchainReactor { 184 reporter := behaviour.NewMockReporter() 185 return newReactor(state, store, reporter, blockApplier, 1000, fastSync) 186 } 187 188 // SetSwitch implements Reactor interface. 189 func (r *BlockchainReactor) SetSwitch(sw *p2p.Switch) { 190 r.Switch = sw 191 if sw != nil { 192 r.io = newSwitchIo(sw) 193 } else { 194 r.io = nil 195 } 196 } 197 198 func (r *BlockchainReactor) setMaxPeerHeight(height int64) { 199 r.mtx.Lock() 200 defer r.mtx.Unlock() 201 if height > r.maxPeerHeight { 202 r.maxPeerHeight = height 203 } 204 } 205 206 func (r *BlockchainReactor) setSyncHeight(height int64) { 207 r.mtx.Lock() 208 defer r.mtx.Unlock() 209 r.syncHeight = height 210 } 211 212 // SyncHeight returns the height to which the BlockchainReactor has synced. 213 func (r *BlockchainReactor) SyncHeight() int64 { 214 r.mtx.RLock() 215 defer r.mtx.RUnlock() 216 return r.syncHeight 217 } 218 219 // SetLogger sets the logger of the reactor. 220 func (r *BlockchainReactor) SetLogger(logger log.Logger) { 221 r.logger = logger 222 r.scheduler.setLogger(logger) 223 r.processor.setLogger(logger) 224 } 225 226 // Start implements cmn.Service interface 227 func (r *BlockchainReactor) Start() error { 228 r.reporter = behaviour.NewSwitchReporter(r.BaseReactor.Switch) 229 if r.fastSync { 230 go r.scheduler.start() 231 go r.processor.start() 232 go r.demux() 233 } 234 return nil 235 } 236 237 // reactor generated ticker events: 238 // ticker for cleaning peers 239 type rTryPrunePeer struct { 240 priorityHigh 241 time time.Time 242 } 243 244 func (e rTryPrunePeer) String() string { 245 return fmt.Sprintf(": %v", e.time) 246 } 247 248 // ticker event for scheduling block requests 249 type rTrySchedule struct { 250 priorityHigh 251 time time.Time 252 } 253 254 func (e rTrySchedule) String() string { 255 return fmt.Sprintf(": %v", e.time) 256 } 257 258 // ticker for block processing 259 type rProcessBlock struct { 260 priorityNormal 261 } 262 263 // reactor generated events based on blockchain related messages from peers: 264 // blockResponse message received from a peer 265 type bcBlockResponse struct { 266 priorityNormal 267 time time.Time 268 peerID p2p.ID 269 size int64 270 block *types.Block 271 } 272 273 // blockNoResponse message received from a peer 274 type bcNoBlockResponse struct { 275 priorityNormal 276 time time.Time 277 peerID p2p.ID 278 height int64 279 } 280 281 // statusResponse message received from a peer 282 type bcStatusResponse struct { 283 priorityNormal 284 time time.Time 285 peerID p2p.ID 286 base int64 287 height int64 288 } 289 290 // new peer is connected 291 type bcAddNewPeer struct { 292 priorityNormal 293 peerID p2p.ID 294 } 295 296 // existing peer is removed 297 type bcRemovePeer struct { 298 priorityHigh 299 peerID p2p.ID 300 reason interface{} 301 } 302 303 func (r *BlockchainReactor) demux() { 304 var lastRate = 0.0 305 var lastHundred = time.Now() 306 307 var ( 308 processBlockFreq = 20 * time.Millisecond 309 doProcessBlockCh = make(chan struct{}, 1) 310 doProcessBlockTk = time.NewTicker(processBlockFreq) 311 ) 312 defer doProcessBlockTk.Stop() 313 314 var ( 315 prunePeerFreq = 1 * time.Second 316 doPrunePeerCh = make(chan struct{}, 1) 317 doPrunePeerTk = time.NewTicker(prunePeerFreq) 318 ) 319 defer doPrunePeerTk.Stop() 320 321 var ( 322 scheduleFreq = 20 * time.Millisecond 323 doScheduleCh = make(chan struct{}, 1) 324 doScheduleTk = time.NewTicker(scheduleFreq) 325 ) 326 defer doScheduleTk.Stop() 327 328 var ( 329 statusFreq = 10 * time.Second 330 doStatusCh = make(chan struct{}, 1) 331 doStatusTk = time.NewTicker(statusFreq) 332 ) 333 defer doStatusTk.Stop() 334 335 // XXX: Extract timers to make testing atemporal 336 for { 337 select { 338 // Pacers: send at most per frequency but don't saturate 339 case <-doProcessBlockTk.C: 340 select { 341 case doProcessBlockCh <- struct{}{}: 342 default: 343 } 344 case <-doPrunePeerTk.C: 345 select { 346 case doPrunePeerCh <- struct{}{}: 347 default: 348 } 349 case <-doScheduleTk.C: 350 select { 351 case doScheduleCh <- struct{}{}: 352 default: 353 } 354 case <-doStatusTk.C: 355 select { 356 case doStatusCh <- struct{}{}: 357 default: 358 } 359 360 // Tickers: perform tasks periodically 361 case <-doScheduleCh: 362 r.scheduler.send(rTrySchedule{time: time.Now()}) 363 case <-doPrunePeerCh: 364 r.scheduler.send(rTryPrunePeer{time: time.Now()}) 365 case <-doProcessBlockCh: 366 r.processor.send(rProcessBlock{}) 367 case <-doStatusCh: 368 r.io.broadcastStatusRequest(r.store.Base(), r.SyncHeight()) 369 370 // Events from peers. Closing the channel signals event loop termination. 371 case event, ok := <-r.events: 372 if !ok { 373 r.logger.Info("Stopping event processing") 374 return 375 } 376 switch event := event.(type) { 377 case bcStatusResponse: 378 r.setMaxPeerHeight(event.height) 379 r.scheduler.send(event) 380 case bcAddNewPeer, bcRemovePeer, bcBlockResponse, bcNoBlockResponse: 381 r.scheduler.send(event) 382 default: 383 r.logger.Error("Received unknown event", "event", fmt.Sprintf("%T", event)) 384 } 385 386 // Incremental events form scheduler 387 case event := <-r.scheduler.next(): 388 switch event := event.(type) { 389 case scBlockReceived: 390 r.processor.send(event) 391 case scPeerError: 392 r.processor.send(event) 393 r.reporter.Report(behaviour.BadMessage(event.peerID, "scPeerError")) 394 case scBlockRequest: 395 r.io.sendBlockRequest(event.peerID, event.height) 396 case scFinishedEv: 397 r.processor.send(event) 398 r.scheduler.stop() 399 case noOpEvent: 400 default: 401 r.logger.Error("Received unknown scheduler event", "event", fmt.Sprintf("%T", event)) 402 } 403 404 // Incremental events from processor 405 case event := <-r.processor.next(): 406 switch event := event.(type) { 407 case pcBlockProcessed: 408 r.setSyncHeight(event.height) 409 if r.syncHeight%100 == 0 { 410 lastRate = 0.9*lastRate + 0.1*(100/time.Since(lastHundred).Seconds()) 411 r.logger.Info("Fast Syncc Rate", "height", r.syncHeight, 412 "max_peer_height", r.maxPeerHeight, "blocks/s", lastRate) 413 lastHundred = time.Now() 414 } 415 r.scheduler.send(event) 416 case pcBlockVerificationFailure: 417 r.scheduler.send(event) 418 case pcFinished: 419 r.io.trySwitchToConsensus(event.tmState, event.blocksSynced) 420 r.processor.stop() 421 case noOpEvent: 422 default: 423 r.logger.Error("Received unknown processor event", "event", fmt.Sprintf("%T", event)) 424 } 425 426 // Terminal event from scheduler 427 case err := <-r.scheduler.final(): 428 switch err { 429 case nil: 430 r.logger.Info("Scheduler stopped") 431 default: 432 r.logger.Error("Scheduler aborted with error", "err", err) 433 } 434 435 // Terminal event from processor 436 case err := <-r.processor.final(): 437 switch err { 438 case nil: 439 r.logger.Info("Processor stopped") 440 default: 441 r.logger.Error("Processor aborted with error", "err", err) 442 } 443 } 444 } 445 } 446 447 // Stop implements cmn.Service interface. 448 func (r *BlockchainReactor) Stop() error { 449 r.logger.Info("reactor stopping") 450 451 r.scheduler.stop() 452 r.processor.stop() 453 close(r.events) 454 455 r.logger.Info("reactor stopped") 456 return nil 457 } 458 459 const ( 460 // NOTE: keep up to date with bcBlockResponseMessage 461 bcBlockResponseMessagePrefixSize = 4 462 bcBlockResponseMessageFieldKeySize = 1 463 maxMsgSize = types.MaxBlockSizeBytes + 464 bcBlockResponseMessagePrefixSize + 465 bcBlockResponseMessageFieldKeySize 466 ) 467 468 // BlockchainMessage is a generic message for this reactor. 469 type BlockchainMessage interface { 470 ValidateBasic() error 471 } 472 473 // RegisterBlockchainMessages registers the fast sync messages for amino encoding. 474 func RegisterBlockchainMessages(cdc *amino.Codec) { 475 cdc.RegisterInterface((*BlockchainMessage)(nil), nil) 476 cdc.RegisterConcrete(&bcBlockRequestMessage{}, "tendermint/blockchain/BlockRequest", nil) 477 cdc.RegisterConcrete(&bcBlockResponseMessage{}, "tendermint/blockchain/BlockResponse", nil) 478 cdc.RegisterConcrete(&bcNoBlockResponseMessage{}, "tendermint/blockchain/NoBlockResponse", nil) 479 cdc.RegisterConcrete(&bcStatusResponseMessage{}, "tendermint/blockchain/StatusResponse", nil) 480 cdc.RegisterConcrete(&bcStatusRequestMessage{}, "tendermint/blockchain/StatusRequest", nil) 481 } 482 483 func decodeMsg(bz []byte) (msg BlockchainMessage, err error) { 484 if len(bz) > maxMsgSize { 485 return msg, fmt.Errorf("msg exceeds max size (%d > %d)", len(bz), maxMsgSize) 486 } 487 err = cdc.UnmarshalBinaryBare(bz, &msg) 488 return 489 } 490 491 // Receive implements Reactor by handling different message types. 492 func (r *BlockchainReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte) { 493 msg, err := decodeMsg(msgBytes) 494 if err != nil { 495 r.logger.Error("error decoding message", 496 "src", src.ID(), "chId", chID, "msg", msg, "err", err, "bytes", msgBytes) 497 _ = r.reporter.Report(behaviour.BadMessage(src.ID(), err.Error())) 498 return 499 } 500 501 if err = msg.ValidateBasic(); err != nil { 502 r.logger.Error("peer sent us invalid msg", "peer", src, "msg", msg, "err", err) 503 _ = r.reporter.Report(behaviour.BadMessage(src.ID(), err.Error())) 504 return 505 } 506 507 r.logger.Debug("Receive", "src", src.ID(), "chID", chID, "msg", msg) 508 509 switch msg := msg.(type) { 510 case *bcStatusRequestMessage: 511 if err := r.io.sendStatusResponse(r.store.Height(), src.ID()); err != nil { 512 r.logger.Error("Could not send status message to peer", "src", src) 513 } 514 515 case *bcBlockRequestMessage: 516 block := r.store.LoadBlock(msg.Height) 517 518 if block != nil { 519 if err = r.io.sendBlockToPeer(block, src.ID()); err != nil { 520 r.logger.Error("Could not send block message to peer: ", err) 521 } 522 } else { 523 r.logger.Info("peer asking for a block we don't have", "src", src, "height", msg.Height) 524 peerID := src.ID() 525 if err = r.io.sendBlockNotFound(msg.Height, peerID); err != nil { 526 r.logger.Error("Couldn't send block not found: ", err) 527 } 528 } 529 530 case *bcStatusResponseMessage: 531 r.events <- bcStatusResponse{peerID: src.ID(), base: msg.Base, height: msg.Height} 532 533 case *bcBlockResponseMessage: 534 r.events <- bcBlockResponse{ 535 peerID: src.ID(), 536 block: msg.Block, 537 size: int64(len(msgBytes)), 538 time: time.Now(), 539 } 540 541 case *bcNoBlockResponseMessage: 542 r.events <- bcNoBlockResponse{peerID: src.ID(), height: msg.Height, time: time.Now()} 543 } 544 } 545 546 // AddPeer implements Reactor interface 547 func (r *BlockchainReactor) AddPeer(peer p2p.Peer) { 548 err := r.io.sendStatusResponse(r.store.Height(), peer.ID()) 549 if err != nil { 550 r.logger.Error("Could not send status message to peer new", "src", peer.ID, "height", r.SyncHeight()) 551 } 552 r.events <- bcAddNewPeer{peerID: peer.ID()} 553 } 554 555 // RemovePeer implements Reactor interface. 556 func (r *BlockchainReactor) RemovePeer(peer p2p.Peer, reason interface{}) { 557 event := bcRemovePeer{ 558 peerID: peer.ID(), 559 reason: reason, 560 } 561 r.events <- event 562 } 563 564 // GetChannels implements Reactor 565 func (r *BlockchainReactor) GetChannels() []*p2p.ChannelDescriptor { 566 return []*p2p.ChannelDescriptor{ 567 { 568 ID: BlockchainChannel, 569 Priority: 10, 570 SendQueueCapacity: 2000, 571 RecvBufferCapacity: 50 * 4096, 572 RecvMessageCapacity: maxMsgSize, 573 }, 574 } 575 }