You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

800 lines
22 KiB

  1. package statesync
  2. import (
  3. "bytes"
  4. "context"
  5. "errors"
  6. "fmt"
  7. "reflect"
  8. "sort"
  9. "time"
  10. abci "github.com/tendermint/tendermint/abci/types"
  11. tmsync "github.com/tendermint/tendermint/internal/libs/sync"
  12. "github.com/tendermint/tendermint/internal/p2p"
  13. "github.com/tendermint/tendermint/libs/log"
  14. "github.com/tendermint/tendermint/libs/service"
  15. ssproto "github.com/tendermint/tendermint/proto/tendermint/statesync"
  16. "github.com/tendermint/tendermint/proxy"
  17. sm "github.com/tendermint/tendermint/state"
  18. "github.com/tendermint/tendermint/store"
  19. "github.com/tendermint/tendermint/types"
  20. )
  21. var (
  22. _ service.Service = (*Reactor)(nil)
  23. _ p2p.Wrapper = (*ssproto.Message)(nil)
  24. // ChannelShims contains a map of ChannelDescriptorShim objects, where each
  25. // object wraps a reference to a legacy p2p ChannelDescriptor and the corresponding
  26. // p2p proto.Message the new p2p Channel is responsible for handling.
  27. //
  28. //
  29. // TODO: Remove once p2p refactor is complete.
  30. // ref: https://github.com/tendermint/tendermint/issues/5670
  31. ChannelShims = map[p2p.ChannelID]*p2p.ChannelDescriptorShim{
  32. SnapshotChannel: {
  33. MsgType: new(ssproto.Message),
  34. Descriptor: &p2p.ChannelDescriptor{
  35. ID: byte(SnapshotChannel),
  36. Priority: 5,
  37. SendQueueCapacity: 10,
  38. RecvMessageCapacity: snapshotMsgSize,
  39. MaxSendBytes: 400,
  40. },
  41. },
  42. ChunkChannel: {
  43. MsgType: new(ssproto.Message),
  44. Descriptor: &p2p.ChannelDescriptor{
  45. ID: byte(ChunkChannel),
  46. Priority: 1,
  47. SendQueueCapacity: 4,
  48. RecvMessageCapacity: chunkMsgSize,
  49. MaxSendBytes: 400,
  50. },
  51. },
  52. LightBlockChannel: {
  53. MsgType: new(ssproto.Message),
  54. Descriptor: &p2p.ChannelDescriptor{
  55. ID: byte(LightBlockChannel),
  56. Priority: 1,
  57. SendQueueCapacity: 10,
  58. RecvMessageCapacity: lightBlockMsgSize,
  59. MaxSendBytes: 400,
  60. },
  61. },
  62. }
  63. )
  64. const (
  65. // SnapshotChannel exchanges snapshot metadata
  66. SnapshotChannel = p2p.ChannelID(0x60)
  67. // ChunkChannel exchanges chunk contents
  68. ChunkChannel = p2p.ChannelID(0x61)
  69. // LightBlockChannel exchanges light blocks
  70. LightBlockChannel = p2p.ChannelID(0x62)
  71. // recentSnapshots is the number of recent snapshots to send and receive per peer.
  72. recentSnapshots = 10
  73. // snapshotMsgSize is the maximum size of a snapshotResponseMessage
  74. snapshotMsgSize = int(4e6)
  75. // chunkMsgSize is the maximum size of a chunkResponseMessage
  76. chunkMsgSize = int(16e6)
  77. // lightBlockMsgSize is the maximum size of a lightBlockResponseMessage
  78. lightBlockMsgSize = int(1e7)
  79. // lightBlockResponseTimeout is how long the dispatcher waits for a peer to
  80. // return a light block
  81. lightBlockResponseTimeout = 10 * time.Second
  82. // maxLightBlockRequestRetries is the amount of retries acceptable before
  83. // the backfill process aborts
  84. maxLightBlockRequestRetries = 20
  85. )
  86. // Reactor handles state sync, both restoring snapshots for the local node and
  87. // serving snapshots for other nodes.
  88. type Reactor struct {
  89. service.BaseService
  90. stateStore sm.Store
  91. blockStore *store.BlockStore
  92. conn proxy.AppConnSnapshot
  93. connQuery proxy.AppConnQuery
  94. tempDir string
  95. snapshotCh *p2p.Channel
  96. chunkCh *p2p.Channel
  97. blockCh *p2p.Channel
  98. peerUpdates *p2p.PeerUpdates
  99. closeCh chan struct{}
  100. dispatcher *dispatcher
  101. // This will only be set when a state sync is in progress. It is used to feed
  102. // received snapshots and chunks into the sync.
  103. mtx tmsync.RWMutex
  104. syncer *syncer
  105. }
  106. // NewReactor returns a reference to a new state sync reactor, which implements
  107. // the service.Service interface. It accepts a logger, connections for snapshots
  108. // and querying, references to p2p Channels and a channel to listen for peer
  109. // updates on. Note, the reactor will close all p2p Channels when stopping.
  110. func NewReactor(
  111. logger log.Logger,
  112. conn proxy.AppConnSnapshot,
  113. connQuery proxy.AppConnQuery,
  114. snapshotCh, chunkCh, blockCh *p2p.Channel,
  115. peerUpdates *p2p.PeerUpdates,
  116. stateStore sm.Store,
  117. blockStore *store.BlockStore,
  118. tempDir string,
  119. ) *Reactor {
  120. r := &Reactor{
  121. conn: conn,
  122. connQuery: connQuery,
  123. snapshotCh: snapshotCh,
  124. chunkCh: chunkCh,
  125. blockCh: blockCh,
  126. peerUpdates: peerUpdates,
  127. closeCh: make(chan struct{}),
  128. tempDir: tempDir,
  129. dispatcher: newDispatcher(blockCh.Out, lightBlockResponseTimeout),
  130. stateStore: stateStore,
  131. blockStore: blockStore,
  132. }
  133. r.BaseService = *service.NewBaseService(logger, "StateSync", r)
  134. return r
  135. }
  136. // OnStart starts separate go routines for each p2p Channel and listens for
  137. // envelopes on each. In addition, it also listens for peer updates and handles
  138. // messages on that p2p channel accordingly. The caller must be sure to execute
  139. // OnStop to ensure the outbound p2p Channels are closed. No error is returned.
  140. func (r *Reactor) OnStart() error {
  141. // Listen for envelopes on the snapshot p2p Channel in a separate go-routine
  142. // as to not block or cause IO contention with the chunk p2p Channel. Note,
  143. // we do not launch a go-routine to handle individual envelopes as to not
  144. // have to deal with bounding workers or pools.
  145. go r.processSnapshotCh()
  146. // Listen for envelopes on the chunk p2p Channel in a separate go-routine
  147. // as to not block or cause IO contention with the snapshot p2p Channel. Note,
  148. // we do not launch a go-routine to handle individual envelopes as to not
  149. // have to deal with bounding workers or pools.
  150. go r.processChunkCh()
  151. go r.processBlockCh()
  152. go r.processPeerUpdates()
  153. r.dispatcher.start()
  154. return nil
  155. }
  156. // OnStop stops the reactor by signaling to all spawned goroutines to exit and
  157. // blocking until they all exit.
  158. func (r *Reactor) OnStop() {
  159. // tell the dispatcher to stop sending any more requests
  160. r.dispatcher.stop()
  161. // Close closeCh to signal to all spawned goroutines to gracefully exit. All
  162. // p2p Channels should execute Close().
  163. close(r.closeCh)
  164. // Wait for all p2p Channels to be closed before returning. This ensures we
  165. // can easily reason about synchronization of all p2p Channels and ensure no
  166. // panics will occur.
  167. <-r.snapshotCh.Done()
  168. <-r.chunkCh.Done()
  169. <-r.blockCh.Done()
  170. <-r.peerUpdates.Done()
  171. }
  172. // Sync runs a state sync, fetching snapshots and providing chunks to the
  173. // application. It also saves tendermint state and runs a backfill process to
  174. // retrieve the necessary amount of headers, commits and validators sets to be
  175. // able to process evidence and participate in consensus.
  176. func (r *Reactor) Sync(stateProvider StateProvider, discoveryTime time.Duration) error {
  177. r.mtx.Lock()
  178. if r.syncer != nil {
  179. r.mtx.Unlock()
  180. return errors.New("a state sync is already in progress")
  181. }
  182. r.syncer = newSyncer(r.Logger, r.conn, r.connQuery, stateProvider, r.snapshotCh.Out, r.chunkCh.Out, r.tempDir)
  183. r.mtx.Unlock()
  184. hook := func() {
  185. // request snapshots from all currently connected peers
  186. r.Logger.Debug("requesting snapshots from known peers")
  187. r.snapshotCh.Out <- p2p.Envelope{
  188. Broadcast: true,
  189. Message: &ssproto.SnapshotsRequest{},
  190. }
  191. }
  192. hook()
  193. state, commit, err := r.syncer.SyncAny(discoveryTime, hook)
  194. if err != nil {
  195. return err
  196. }
  197. r.mtx.Lock()
  198. r.syncer = nil
  199. r.mtx.Unlock()
  200. err = r.stateStore.Bootstrap(state)
  201. if err != nil {
  202. return fmt.Errorf("failed to bootstrap node with new state: %w", err)
  203. }
  204. err = r.blockStore.SaveSeenCommit(state.LastBlockHeight, commit)
  205. if err != nil {
  206. return fmt.Errorf("failed to store last seen commit: %w", err)
  207. }
  208. // start backfill process to retrieve the necessary headers, commits and
  209. // validator sets
  210. return r.backfill(state)
  211. }
  212. // Backfill sequentially fetches, verifies and stores light blocks in reverse
  213. // order. It does not stop verifying blocks until reaching a block with a height
  214. // and time that is less or equal to the stopHeight and stopTime. The
  215. // trustedBlockID should be of the header at startHeight.
  216. func (r *Reactor) Backfill(
  217. ctx context.Context,
  218. chainID string,
  219. startHeight, stopHeight int64,
  220. trustedBlockID types.BlockID,
  221. stopTime time.Time,
  222. ) error {
  223. r.Logger.Info("starting backfill process...", "startHeight", startHeight,
  224. "stopHeight", stopHeight, "trustedBlockID", trustedBlockID)
  225. var (
  226. lastValidatorSet *types.ValidatorSet
  227. lastChangeHeight int64 = startHeight
  228. )
  229. queue := newBlockQueue(startHeight, stopHeight, stopTime, maxLightBlockRequestRetries)
  230. // fetch light blocks across four workers. The aim with deploying concurrent
  231. // workers is to equate the network messaging time with the verification
  232. // time. Ideally we want the verification process to never have to be
  233. // waiting on blocks. If it takes 4s to retrieve a block and 1s to verify
  234. // it, then steady state involves four workers.
  235. for i := 0; i < 4; i++ {
  236. go func() {
  237. for {
  238. select {
  239. case height := <-queue.nextHeight():
  240. r.Logger.Debug("fetching next block", "height", height)
  241. lb, peer, err := r.dispatcher.LightBlock(ctx, height)
  242. if err != nil {
  243. // we don't punish the peer as it might just not have the block
  244. // at that height
  245. r.Logger.Info("error with fetching light block",
  246. "height", height, "err", err)
  247. queue.retry(height)
  248. continue
  249. }
  250. if lb == nil {
  251. r.Logger.Info("peer didn't have block, fetching from another peer", "height", height)
  252. queue.retry(height)
  253. continue
  254. }
  255. if lb.Height != height {
  256. r.Logger.Info("peer provided wrong height, retrying...", "height", height)
  257. queue.retry(height)
  258. continue
  259. }
  260. // run a validate basic. This checks the validator set and commit
  261. // hashes line up
  262. err = lb.ValidateBasic(chainID)
  263. if err != nil {
  264. r.Logger.Info("fetched light block failed validate basic, removing peer...", "err", err)
  265. queue.retry(height)
  266. r.blockCh.Error <- p2p.PeerError{
  267. NodeID: peer,
  268. Err: fmt.Errorf("received invalid light block: %w", err),
  269. }
  270. continue
  271. }
  272. // add block to queue to be verified
  273. queue.add(lightBlockResponse{
  274. block: lb,
  275. peer: peer,
  276. })
  277. r.Logger.Debug("added light block to processing queue", "height", height)
  278. case <-queue.done():
  279. return
  280. }
  281. }
  282. }()
  283. }
  284. // verify all light blocks
  285. for {
  286. select {
  287. case <-r.closeCh:
  288. queue.close()
  289. return nil
  290. case <-ctx.Done():
  291. queue.close()
  292. return nil
  293. case resp := <-queue.verifyNext():
  294. // validate the header hash. We take the last block id of the
  295. // previous header (i.e. one height above) as the trusted hash which
  296. // we equate to. ValidatorsHash and CommitHash have already been
  297. // checked in the `ValidateBasic`
  298. if w, g := trustedBlockID.Hash, resp.block.Hash(); !bytes.Equal(w, g) {
  299. r.Logger.Info("received invalid light block. header hash doesn't match trusted LastBlockID",
  300. "trustedHash", w, "receivedHash", g, "height", resp.block.Height)
  301. r.blockCh.Error <- p2p.PeerError{
  302. NodeID: resp.peer,
  303. Err: fmt.Errorf("received invalid light block. Expected hash %v, got: %v", w, g),
  304. }
  305. queue.retry(resp.block.Height)
  306. continue
  307. }
  308. // save the signed headers
  309. err := r.blockStore.SaveSignedHeader(resp.block.SignedHeader, trustedBlockID)
  310. if err != nil {
  311. return err
  312. }
  313. // check if there has been a change in the validator set
  314. if lastValidatorSet != nil && !bytes.Equal(resp.block.Header.ValidatorsHash, resp.block.Header.NextValidatorsHash) {
  315. // save all the heights that the last validator set was the same
  316. err = r.stateStore.SaveValidatorSets(resp.block.Height+1, lastChangeHeight, lastValidatorSet)
  317. if err != nil {
  318. return err
  319. }
  320. // update the lastChangeHeight
  321. lastChangeHeight = resp.block.Height
  322. }
  323. trustedBlockID = resp.block.LastBlockID
  324. queue.success(resp.block.Height)
  325. r.Logger.Info("verified and stored light block", "height", resp.block.Height)
  326. lastValidatorSet = resp.block.ValidatorSet
  327. case <-queue.done():
  328. if err := queue.error(); err != nil {
  329. return err
  330. }
  331. // save the final batch of validators
  332. return r.stateStore.SaveValidatorSets(queue.terminal.Height, lastChangeHeight, lastValidatorSet)
  333. }
  334. }
  335. }
  336. // Dispatcher exposes the dispatcher so that a state provider can use it for
  337. // light client verification
  338. func (r *Reactor) Dispatcher() *dispatcher { //nolint:golint
  339. return r.dispatcher
  340. }
  341. // handleSnapshotMessage handles envelopes sent from peers on the
  342. // SnapshotChannel. It returns an error only if the Envelope.Message is unknown
  343. // for this channel. This should never be called outside of handleMessage.
  344. func (r *Reactor) handleSnapshotMessage(envelope p2p.Envelope) error {
  345. logger := r.Logger.With("peer", envelope.From)
  346. switch msg := envelope.Message.(type) {
  347. case *ssproto.SnapshotsRequest:
  348. snapshots, err := r.recentSnapshots(recentSnapshots)
  349. if err != nil {
  350. logger.Error("failed to fetch snapshots", "err", err)
  351. return nil
  352. }
  353. for _, snapshot := range snapshots {
  354. logger.Debug(
  355. "advertising snapshot",
  356. "height", snapshot.Height,
  357. "format", snapshot.Format,
  358. )
  359. r.snapshotCh.Out <- p2p.Envelope{
  360. To: envelope.From,
  361. Message: &ssproto.SnapshotsResponse{
  362. Height: snapshot.Height,
  363. Format: snapshot.Format,
  364. Chunks: snapshot.Chunks,
  365. Hash: snapshot.Hash,
  366. Metadata: snapshot.Metadata,
  367. },
  368. }
  369. }
  370. case *ssproto.SnapshotsResponse:
  371. r.mtx.RLock()
  372. defer r.mtx.RUnlock()
  373. if r.syncer == nil {
  374. logger.Debug("received unexpected snapshot; no state sync in progress")
  375. return nil
  376. }
  377. logger.Debug("received snapshot", "height", msg.Height, "format", msg.Format)
  378. _, err := r.syncer.AddSnapshot(envelope.From, &snapshot{
  379. Height: msg.Height,
  380. Format: msg.Format,
  381. Chunks: msg.Chunks,
  382. Hash: msg.Hash,
  383. Metadata: msg.Metadata,
  384. })
  385. if err != nil {
  386. logger.Error(
  387. "failed to add snapshot",
  388. "height", msg.Height,
  389. "format", msg.Format,
  390. "err", err,
  391. "channel", r.snapshotCh.ID,
  392. )
  393. return nil
  394. }
  395. default:
  396. return fmt.Errorf("received unknown message: %T", msg)
  397. }
  398. return nil
  399. }
  400. // handleChunkMessage handles envelopes sent from peers on the ChunkChannel.
  401. // It returns an error only if the Envelope.Message is unknown for this channel.
  402. // This should never be called outside of handleMessage.
  403. func (r *Reactor) handleChunkMessage(envelope p2p.Envelope) error {
  404. switch msg := envelope.Message.(type) {
  405. case *ssproto.ChunkRequest:
  406. r.Logger.Debug(
  407. "received chunk request",
  408. "height", msg.Height,
  409. "format", msg.Format,
  410. "chunk", msg.Index,
  411. "peer", envelope.From,
  412. )
  413. resp, err := r.conn.LoadSnapshotChunkSync(context.Background(), abci.RequestLoadSnapshotChunk{
  414. Height: msg.Height,
  415. Format: msg.Format,
  416. Chunk: msg.Index,
  417. })
  418. if err != nil {
  419. r.Logger.Error(
  420. "failed to load chunk",
  421. "height", msg.Height,
  422. "format", msg.Format,
  423. "chunk", msg.Index,
  424. "err", err,
  425. "peer", envelope.From,
  426. )
  427. return nil
  428. }
  429. r.Logger.Debug(
  430. "sending chunk",
  431. "height", msg.Height,
  432. "format", msg.Format,
  433. "chunk", msg.Index,
  434. "peer", envelope.From,
  435. )
  436. r.chunkCh.Out <- p2p.Envelope{
  437. To: envelope.From,
  438. Message: &ssproto.ChunkResponse{
  439. Height: msg.Height,
  440. Format: msg.Format,
  441. Index: msg.Index,
  442. Chunk: resp.Chunk,
  443. Missing: resp.Chunk == nil,
  444. },
  445. }
  446. case *ssproto.ChunkResponse:
  447. r.mtx.RLock()
  448. defer r.mtx.RUnlock()
  449. if r.syncer == nil {
  450. r.Logger.Debug("received unexpected chunk; no state sync in progress", "peer", envelope.From)
  451. return nil
  452. }
  453. r.Logger.Debug(
  454. "received chunk; adding to sync",
  455. "height", msg.Height,
  456. "format", msg.Format,
  457. "chunk", msg.Index,
  458. "peer", envelope.From,
  459. )
  460. _, err := r.syncer.AddChunk(&chunk{
  461. Height: msg.Height,
  462. Format: msg.Format,
  463. Index: msg.Index,
  464. Chunk: msg.Chunk,
  465. Sender: envelope.From,
  466. })
  467. if err != nil {
  468. r.Logger.Error(
  469. "failed to add chunk",
  470. "height", msg.Height,
  471. "format", msg.Format,
  472. "chunk", msg.Index,
  473. "err", err,
  474. "peer", envelope.From,
  475. )
  476. return nil
  477. }
  478. default:
  479. return fmt.Errorf("received unknown message: %T", msg)
  480. }
  481. return nil
  482. }
  483. func (r *Reactor) handleLightBlockMessage(envelope p2p.Envelope) error {
  484. switch msg := envelope.Message.(type) {
  485. case *ssproto.LightBlockRequest:
  486. r.Logger.Info("received light block request", "height", msg.Height)
  487. lb, err := r.fetchLightBlock(msg.Height)
  488. if err != nil {
  489. r.Logger.Error("failed to retrieve light block", "err", err, "height", msg.Height)
  490. return err
  491. }
  492. lbproto, err := lb.ToProto()
  493. if err != nil {
  494. r.Logger.Error("marshaling light block to proto", "err", err)
  495. return nil
  496. }
  497. // NOTE: If we don't have the light block we will send a nil light block
  498. // back to the requested node, indicating that we don't have it.
  499. r.blockCh.Out <- p2p.Envelope{
  500. To: envelope.From,
  501. Message: &ssproto.LightBlockResponse{
  502. LightBlock: lbproto,
  503. },
  504. }
  505. case *ssproto.LightBlockResponse:
  506. if err := r.dispatcher.respond(msg.LightBlock, envelope.From); err != nil {
  507. r.Logger.Error("error processing light block response", "err", err)
  508. return err
  509. }
  510. default:
  511. return fmt.Errorf("received unknown message: %T", msg)
  512. }
  513. return nil
  514. }
  515. // handleMessage handles an Envelope sent from a peer on a specific p2p Channel.
  516. // It will handle errors and any possible panics gracefully. A caller can handle
  517. // any error returned by sending a PeerError on the respective channel.
  518. func (r *Reactor) handleMessage(chID p2p.ChannelID, envelope p2p.Envelope) (err error) {
  519. defer func() {
  520. if e := recover(); e != nil {
  521. err = fmt.Errorf("panic in processing message: %v", e)
  522. }
  523. }()
  524. r.Logger.Debug("received message", "message", reflect.TypeOf(envelope.Message), "peer", envelope.From)
  525. switch chID {
  526. case SnapshotChannel:
  527. err = r.handleSnapshotMessage(envelope)
  528. case ChunkChannel:
  529. err = r.handleChunkMessage(envelope)
  530. case LightBlockChannel:
  531. err = r.handleLightBlockMessage(envelope)
  532. default:
  533. err = fmt.Errorf("unknown channel ID (%d) for envelope (%v)", chID, envelope)
  534. }
  535. return err
  536. }
  537. // processSnapshotCh initiates a blocking process where we listen for and handle
  538. // envelopes on the SnapshotChannel.
  539. func (r *Reactor) processSnapshotCh() {
  540. r.processCh(r.snapshotCh, "snapshot")
  541. }
  542. // processChunkCh initiates a blocking process where we listen for and handle
  543. // envelopes on the ChunkChannel.
  544. func (r *Reactor) processChunkCh() {
  545. r.processCh(r.chunkCh, "chunk")
  546. }
  547. // processBlockCh initiates a blocking process where we listen for and handle
  548. // envelopes on the LightBlockChannel.
  549. func (r *Reactor) processBlockCh() {
  550. r.processCh(r.blockCh, "light block")
  551. }
  552. // processCh routes state sync messages to their respective handlers. Any error
  553. // encountered during message execution will result in a PeerError being sent on
  554. // the respective channel. When the reactor is stopped, we will catch the signal
  555. // and close the p2p Channel gracefully.
  556. func (r *Reactor) processCh(ch *p2p.Channel, chName string) {
  557. defer ch.Close()
  558. for {
  559. select {
  560. case envelope := <-ch.In:
  561. if err := r.handleMessage(ch.ID, envelope); err != nil {
  562. r.Logger.Error(fmt.Sprintf("failed to process %s message", chName),
  563. "ch_id", ch.ID, "envelope", envelope, "err", err)
  564. ch.Error <- p2p.PeerError{
  565. NodeID: envelope.From,
  566. Err: err,
  567. }
  568. }
  569. case <-r.closeCh:
  570. r.Logger.Debug(fmt.Sprintf("stopped listening on %s channel; closing...", chName))
  571. return
  572. }
  573. }
  574. }
  575. // processPeerUpdate processes a PeerUpdate, returning an error upon failing to
  576. // handle the PeerUpdate or if a panic is recovered.
  577. func (r *Reactor) processPeerUpdate(peerUpdate p2p.PeerUpdate) {
  578. r.Logger.Debug("received peer update", "peer", peerUpdate.NodeID, "status", peerUpdate.Status)
  579. r.mtx.RLock()
  580. defer r.mtx.RUnlock()
  581. switch peerUpdate.Status {
  582. case p2p.PeerStatusUp:
  583. if r.syncer != nil {
  584. r.syncer.AddPeer(peerUpdate.NodeID)
  585. }
  586. r.dispatcher.addPeer(peerUpdate.NodeID)
  587. case p2p.PeerStatusDown:
  588. if r.syncer != nil {
  589. r.syncer.RemovePeer(peerUpdate.NodeID)
  590. }
  591. r.dispatcher.removePeer(peerUpdate.NodeID)
  592. }
  593. }
  594. // processPeerUpdates initiates a blocking process where we listen for and handle
  595. // PeerUpdate messages. When the reactor is stopped, we will catch the signal and
  596. // close the p2p PeerUpdatesCh gracefully.
  597. func (r *Reactor) processPeerUpdates() {
  598. defer r.peerUpdates.Close()
  599. for {
  600. select {
  601. case peerUpdate := <-r.peerUpdates.Updates():
  602. r.processPeerUpdate(peerUpdate)
  603. case <-r.closeCh:
  604. r.Logger.Debug("stopped listening on peer updates channel; closing...")
  605. return
  606. }
  607. }
  608. }
  609. // recentSnapshots fetches the n most recent snapshots from the app
  610. func (r *Reactor) recentSnapshots(n uint32) ([]*snapshot, error) {
  611. resp, err := r.conn.ListSnapshotsSync(context.Background(), abci.RequestListSnapshots{})
  612. if err != nil {
  613. return nil, err
  614. }
  615. sort.Slice(resp.Snapshots, func(i, j int) bool {
  616. a := resp.Snapshots[i]
  617. b := resp.Snapshots[j]
  618. switch {
  619. case a.Height > b.Height:
  620. return true
  621. case a.Height == b.Height && a.Format > b.Format:
  622. return true
  623. default:
  624. return false
  625. }
  626. })
  627. snapshots := make([]*snapshot, 0, n)
  628. for i, s := range resp.Snapshots {
  629. if i >= recentSnapshots {
  630. break
  631. }
  632. snapshots = append(snapshots, &snapshot{
  633. Height: s.Height,
  634. Format: s.Format,
  635. Chunks: s.Chunks,
  636. Hash: s.Hash,
  637. Metadata: s.Metadata,
  638. })
  639. }
  640. return snapshots, nil
  641. }
  642. // fetchLightBlock works out whether the node has a light block at a particular
  643. // height and if so returns it so it can be gossiped to peers
  644. func (r *Reactor) fetchLightBlock(height uint64) (*types.LightBlock, error) {
  645. h := int64(height)
  646. blockMeta := r.blockStore.LoadBlockMeta(h)
  647. if blockMeta == nil {
  648. return nil, nil
  649. }
  650. commit := r.blockStore.LoadBlockCommit(h)
  651. if commit == nil {
  652. return nil, nil
  653. }
  654. vals, err := r.stateStore.LoadValidators(h)
  655. if err != nil {
  656. return nil, err
  657. }
  658. if vals == nil {
  659. return nil, nil
  660. }
  661. return &types.LightBlock{
  662. SignedHeader: &types.SignedHeader{
  663. Header: &blockMeta.Header,
  664. Commit: commit,
  665. },
  666. ValidatorSet: vals,
  667. }, nil
  668. }
  669. // backfill is a convenience wrapper around the backfill function. It takes
  670. // state to work out how many prior blocks need to be verified
  671. func (r *Reactor) backfill(state sm.State) error {
  672. params := state.ConsensusParams.Evidence
  673. stopHeight := state.LastBlockHeight - params.MaxAgeNumBlocks
  674. stopTime := state.LastBlockTime.Add(-params.MaxAgeDuration)
  675. // ensure that stop height doesn't go below the initial height
  676. if stopHeight < state.InitialHeight {
  677. stopHeight = state.InitialHeight
  678. // this essentially makes stop time a void criteria for termination
  679. stopTime = state.LastBlockTime
  680. }
  681. return r.Backfill(
  682. context.Background(),
  683. state.ChainID,
  684. state.LastBlockHeight, stopHeight,
  685. state.LastBlockID,
  686. stopTime,
  687. )
  688. }