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