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.

186 lines
4.3 KiB

  1. package v2
  2. import (
  3. "fmt"
  4. "github.com/tendermint/tendermint/p2p"
  5. tdState "github.com/tendermint/tendermint/state"
  6. "github.com/tendermint/tendermint/types"
  7. )
  8. type peerError struct {
  9. priorityHigh
  10. peerID p2p.ID
  11. }
  12. type pcDuplicateBlock struct {
  13. priorityNormal
  14. }
  15. type pcShortBlock struct {
  16. priorityNormal
  17. }
  18. type bcBlockResponse struct {
  19. priorityNormal
  20. peerID p2p.ID
  21. block *types.Block
  22. height int64
  23. }
  24. type pcBlockVerificationFailure struct {
  25. priorityNormal
  26. peerID p2p.ID
  27. height int64
  28. }
  29. type pcBlockProcessed struct {
  30. priorityNormal
  31. height int64
  32. peerID p2p.ID
  33. }
  34. type pcProcessBlock struct {
  35. priorityNormal
  36. }
  37. type pcStop struct {
  38. priorityNormal
  39. }
  40. type pcFinished struct {
  41. priorityNormal
  42. height int64
  43. blocksSynced int64
  44. }
  45. func (p pcFinished) Error() string {
  46. return "finished"
  47. }
  48. type queueItem struct {
  49. block *types.Block
  50. peerID p2p.ID
  51. }
  52. type blockQueue map[int64]queueItem
  53. type pcState struct {
  54. height int64 // height of the last synced block
  55. queue blockQueue // blocks waiting to be processed
  56. chainID string
  57. blocksSynced int64
  58. draining bool
  59. tdState tdState.State
  60. context processorContext
  61. }
  62. func (state *pcState) String() string {
  63. return fmt.Sprintf("height: %d queue length: %d draining: %v blocks synced: %d",
  64. state.height, len(state.queue), state.draining, state.blocksSynced)
  65. }
  66. // newPcState returns a pcState initialized with the last verified block enqueued
  67. func newPcState(initHeight int64, tdState tdState.State, chainID string, context processorContext) *pcState {
  68. return &pcState{
  69. height: initHeight,
  70. queue: blockQueue{},
  71. chainID: chainID,
  72. draining: false,
  73. blocksSynced: 0,
  74. context: context,
  75. tdState: tdState,
  76. }
  77. }
  78. // nextTwo returns the next two unverified blocks
  79. func (state *pcState) nextTwo() (queueItem, queueItem, error) {
  80. if first, ok := state.queue[state.height+1]; ok {
  81. if second, ok := state.queue[state.height+2]; ok {
  82. return first, second, nil
  83. }
  84. }
  85. return queueItem{}, queueItem{}, fmt.Errorf("not found")
  86. }
  87. // synced returns true when at most the last verified block remains in the queue
  88. func (state *pcState) synced() bool {
  89. return len(state.queue) <= 1
  90. }
  91. func (state *pcState) advance() {
  92. state.height++
  93. delete(state.queue, state.height)
  94. state.blocksSynced++
  95. }
  96. func (state *pcState) enqueue(peerID p2p.ID, block *types.Block, height int64) error {
  97. if _, ok := state.queue[height]; ok {
  98. return fmt.Errorf("duplicate queue item")
  99. }
  100. state.queue[height] = queueItem{block: block, peerID: peerID}
  101. return nil
  102. }
  103. // purgePeer moves all unprocessed blocks from the queue
  104. func (state *pcState) purgePeer(peerID p2p.ID) {
  105. // what if height is less than state.height?
  106. for height, item := range state.queue {
  107. if item.peerID == peerID {
  108. delete(state.queue, height)
  109. }
  110. }
  111. }
  112. // handle processes FSM events
  113. func (state *pcState) handle(event Event) (Event, error) {
  114. switch event := event.(type) {
  115. case *bcBlockResponse:
  116. if event.height <= state.height {
  117. return pcShortBlock{}, nil
  118. }
  119. err := state.enqueue(event.peerID, event.block, event.height)
  120. if err != nil {
  121. return pcDuplicateBlock{}, nil
  122. }
  123. case pcProcessBlock:
  124. firstItem, secondItem, err := state.nextTwo()
  125. if err != nil {
  126. if state.draining {
  127. return noOp, pcFinished{height: state.height}
  128. }
  129. return noOp, nil
  130. }
  131. first, second := firstItem.block, secondItem.block
  132. firstParts := first.MakePartSet(types.BlockPartSizeBytes)
  133. firstPartsHeader := firstParts.Header()
  134. firstID := types.BlockID{Hash: first.Hash(), PartsHeader: firstPartsHeader}
  135. err = state.context.verifyCommit(state.chainID, firstID, first.Height, second.LastCommit)
  136. if err != nil {
  137. return pcBlockVerificationFailure{peerID: firstItem.peerID, height: first.Height}, nil
  138. }
  139. state.context.saveBlock(first, firstParts, second.LastCommit)
  140. state.tdState, err = state.context.applyBlock(state.tdState, firstID, first)
  141. if err != nil {
  142. panic(fmt.Sprintf("failed to process committed block (%d:%X): %v", first.Height, first.Hash(), err))
  143. }
  144. state.advance()
  145. return pcBlockProcessed{height: first.Height, peerID: firstItem.peerID}, nil
  146. case *peerError:
  147. state.purgePeer(event.peerID)
  148. case pcStop:
  149. if state.synced() {
  150. return noOp, pcFinished{height: state.height, blocksSynced: state.blocksSynced}
  151. }
  152. state.draining = true
  153. }
  154. return noOp, nil
  155. }