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