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.

331 lines
9.3 KiB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
  1. package blockchain
  2. import (
  3. "bytes"
  4. "errors"
  5. "fmt"
  6. "reflect"
  7. "time"
  8. . "github.com/tendermint/go-common"
  9. "github.com/tendermint/go-p2p"
  10. "github.com/tendermint/go-wire"
  11. "github.com/tendermint/tendermint/events"
  12. sm "github.com/tendermint/tendermint/state"
  13. "github.com/tendermint/tendermint/types"
  14. )
  15. const (
  16. BlockchainChannel = byte(0x40)
  17. defaultChannelCapacity = 100
  18. defaultSleepIntervalMS = 500
  19. trySyncIntervalMS = 100
  20. // stop syncing when last block's time is
  21. // within this much of the system time.
  22. // stopSyncingDurationMinutes = 10
  23. // ask for best height every 10s
  24. statusUpdateIntervalSeconds = 10
  25. // check if we should switch to consensus reactor
  26. switchToConsensusIntervalSeconds = 1
  27. maxBlockchainResponseSize = types.MaxBlockSize + 2
  28. )
  29. type consensusReactor interface {
  30. // for when we switch from blockchain reactor and fast sync to
  31. // the consensus machine
  32. SwitchToConsensus(*sm.State)
  33. }
  34. // BlockchainReactor handles long-term catchup syncing.
  35. type BlockchainReactor struct {
  36. p2p.BaseReactor
  37. sw *p2p.Switch
  38. state *sm.State
  39. store *BlockStore
  40. pool *BlockPool
  41. sync bool
  42. requestsCh chan BlockRequest
  43. timeoutsCh chan string
  44. lastBlock *types.Block
  45. evsw events.Fireable
  46. }
  47. func NewBlockchainReactor(state *sm.State, store *BlockStore, sync bool) *BlockchainReactor {
  48. if state.LastBlockHeight != store.Height() &&
  49. state.LastBlockHeight != store.Height()-1 { // XXX double check this logic.
  50. PanicSanity(Fmt("state (%v) and store (%v) height mismatch", state.LastBlockHeight, store.Height()))
  51. }
  52. requestsCh := make(chan BlockRequest, defaultChannelCapacity)
  53. timeoutsCh := make(chan string, defaultChannelCapacity)
  54. pool := NewBlockPool(
  55. store.Height()+1,
  56. requestsCh,
  57. timeoutsCh,
  58. )
  59. bcR := &BlockchainReactor{
  60. state: state,
  61. store: store,
  62. pool: pool,
  63. sync: sync,
  64. requestsCh: requestsCh,
  65. timeoutsCh: timeoutsCh,
  66. }
  67. bcR.BaseReactor = *p2p.NewBaseReactor(log, "BlockchainReactor", bcR)
  68. return bcR
  69. }
  70. func (bcR *BlockchainReactor) OnStart() error {
  71. bcR.BaseReactor.OnStart()
  72. if bcR.sync {
  73. _, err := bcR.pool.Start()
  74. if err != nil {
  75. return err
  76. }
  77. go bcR.poolRoutine()
  78. }
  79. return nil
  80. }
  81. func (bcR *BlockchainReactor) OnStop() {
  82. bcR.BaseReactor.OnStop()
  83. bcR.pool.Stop()
  84. }
  85. // Implements Reactor
  86. func (bcR *BlockchainReactor) GetChannels() []*p2p.ChannelDescriptor {
  87. return []*p2p.ChannelDescriptor{
  88. &p2p.ChannelDescriptor{
  89. ID: BlockchainChannel,
  90. Priority: 5,
  91. SendQueueCapacity: 100,
  92. },
  93. }
  94. }
  95. // Implements Reactor
  96. func (bcR *BlockchainReactor) AddPeer(peer *p2p.Peer) {
  97. // Send peer our state.
  98. peer.Send(BlockchainChannel, &bcStatusResponseMessage{bcR.store.Height()})
  99. }
  100. // Implements Reactor
  101. func (bcR *BlockchainReactor) RemovePeer(peer *p2p.Peer, reason interface{}) {
  102. // Remove peer from the pool.
  103. bcR.pool.RemovePeer(peer.Key)
  104. }
  105. // Implements Reactor
  106. func (bcR *BlockchainReactor) Receive(chID byte, src *p2p.Peer, msgBytes []byte) {
  107. _, msg, err := DecodeMessage(msgBytes)
  108. if err != nil {
  109. log.Warn("Error decoding message", "error", err)
  110. return
  111. }
  112. log.Notice("Received message", "src", src, "chID", chID, "msg", msg)
  113. switch msg := msg.(type) {
  114. case *bcBlockRequestMessage:
  115. // Got a request for a block. Respond with block if we have it.
  116. block := bcR.store.LoadBlock(msg.Height)
  117. if block != nil {
  118. msg := &bcBlockResponseMessage{Block: block}
  119. queued := src.TrySend(BlockchainChannel, msg)
  120. if !queued {
  121. // queue is full, just ignore.
  122. }
  123. } else {
  124. // TODO peer is asking for things we don't have.
  125. }
  126. case *bcBlockResponseMessage:
  127. // Got a block.
  128. bcR.pool.AddBlock(src.Key, msg.Block, len(msgBytes))
  129. case *bcStatusRequestMessage:
  130. // Send peer our state.
  131. queued := src.TrySend(BlockchainChannel, &bcStatusResponseMessage{bcR.store.Height()})
  132. if !queued {
  133. // sorry
  134. }
  135. case *bcStatusResponseMessage:
  136. // Got a peer status. Unverified.
  137. bcR.pool.SetPeerHeight(src.Key, msg.Height)
  138. default:
  139. log.Warn(Fmt("Unknown message type %v", reflect.TypeOf(msg)))
  140. }
  141. }
  142. // Handle messages from the poolReactor telling the reactor what to do.
  143. // NOTE: Don't sleep in the FOR_LOOP or otherwise slow it down!
  144. // (Except for the SYNC_LOOP, which is the primary purpose and must be synchronous.)
  145. func (bcR *BlockchainReactor) poolRoutine() {
  146. trySyncTicker := time.NewTicker(trySyncIntervalMS * time.Millisecond)
  147. statusUpdateTicker := time.NewTicker(statusUpdateIntervalSeconds * time.Second)
  148. switchToConsensusTicker := time.NewTicker(switchToConsensusIntervalSeconds * time.Second)
  149. FOR_LOOP:
  150. for {
  151. select {
  152. case request := <-bcR.requestsCh: // chan BlockRequest
  153. peer := bcR.Switch.Peers().Get(request.PeerID)
  154. if peer == nil {
  155. continue FOR_LOOP // Peer has since been disconnected.
  156. }
  157. msg := &bcBlockRequestMessage{request.Height}
  158. queued := peer.TrySend(BlockchainChannel, msg)
  159. if !queued {
  160. // We couldn't make the request, send-queue full.
  161. // The pool handles timeouts, just let it go.
  162. continue FOR_LOOP
  163. }
  164. case peerID := <-bcR.timeoutsCh: // chan string
  165. // Peer timed out.
  166. peer := bcR.Switch.Peers().Get(peerID)
  167. if peer != nil {
  168. bcR.Switch.StopPeerForError(peer, errors.New("BlockchainReactor Timeout"))
  169. }
  170. case _ = <-statusUpdateTicker.C:
  171. // ask for status updates
  172. go bcR.BroadcastStatusRequest()
  173. case _ = <-switchToConsensusTicker.C:
  174. height, numPending := bcR.pool.GetStatus()
  175. outbound, inbound, _ := bcR.Switch.NumPeers()
  176. log.Info("Consensus ticker", "numPending", numPending, "total", len(bcR.pool.requesters),
  177. "outbound", outbound, "inbound", inbound)
  178. if bcR.pool.IsCaughtUp() {
  179. log.Notice("Time to switch to consensus reactor!", "height", height)
  180. bcR.pool.Stop()
  181. conR := bcR.Switch.Reactor("CONSENSUS").(consensusReactor)
  182. conR.SwitchToConsensus(bcR.state)
  183. break FOR_LOOP
  184. }
  185. case _ = <-trySyncTicker.C: // chan time
  186. // This loop can be slow as long as it's doing syncing work.
  187. SYNC_LOOP:
  188. for i := 0; i < 10; i++ {
  189. // See if there are any blocks to sync.
  190. first, second := bcR.pool.PeekTwoBlocks()
  191. //log.Info("TrySync peeked", "first", first, "second", second)
  192. if first == nil || second == nil {
  193. // We need both to sync the first block.
  194. break SYNC_LOOP
  195. }
  196. firstParts := first.MakePartSet()
  197. firstPartsHeader := firstParts.Header()
  198. // Finally, verify the first block using the second's validation.
  199. err := bcR.state.Validators.VerifyValidation(
  200. bcR.state.ChainID, first.Hash(), firstPartsHeader, first.Height, second.LastValidation)
  201. if err != nil {
  202. log.Info("error in validation", "error", err)
  203. bcR.pool.RedoRequest(first.Height)
  204. break SYNC_LOOP
  205. } else {
  206. bcR.pool.PopRequest()
  207. err := sm.ExecBlock(bcR.state, first, firstPartsHeader)
  208. if err != nil {
  209. // TODO This is bad, are we zombie?
  210. PanicQ(Fmt("Failed to process committed block: %v", err))
  211. }
  212. bcR.store.SaveBlock(first, firstParts, second.LastValidation)
  213. bcR.state.Save()
  214. }
  215. }
  216. continue FOR_LOOP
  217. case <-bcR.Quit:
  218. break FOR_LOOP
  219. }
  220. }
  221. }
  222. func (bcR *BlockchainReactor) BroadcastStatusResponse() error {
  223. bcR.Switch.Broadcast(BlockchainChannel, &bcStatusResponseMessage{bcR.store.Height()})
  224. return nil
  225. }
  226. func (bcR *BlockchainReactor) BroadcastStatusRequest() error {
  227. bcR.Switch.Broadcast(BlockchainChannel, &bcStatusRequestMessage{bcR.store.Height()})
  228. return nil
  229. }
  230. // implements events.Eventable
  231. func (bcR *BlockchainReactor) SetFireable(evsw events.Fireable) {
  232. bcR.evsw = evsw
  233. }
  234. //-----------------------------------------------------------------------------
  235. // Messages
  236. const (
  237. msgTypeBlockRequest = byte(0x10)
  238. msgTypeBlockResponse = byte(0x11)
  239. msgTypeStatusResponse = byte(0x20)
  240. msgTypeStatusRequest = byte(0x21)
  241. )
  242. type BlockchainMessage interface{}
  243. var _ = wire.RegisterInterface(
  244. struct{ BlockchainMessage }{},
  245. wire.ConcreteType{&bcBlockRequestMessage{}, msgTypeBlockRequest},
  246. wire.ConcreteType{&bcBlockResponseMessage{}, msgTypeBlockResponse},
  247. wire.ConcreteType{&bcStatusResponseMessage{}, msgTypeStatusResponse},
  248. wire.ConcreteType{&bcStatusRequestMessage{}, msgTypeStatusRequest},
  249. )
  250. // TODO: ensure that bz is completely read.
  251. func DecodeMessage(bz []byte) (msgType byte, msg BlockchainMessage, err error) {
  252. msgType = bz[0]
  253. n := int(0)
  254. r := bytes.NewReader(bz)
  255. msg = wire.ReadBinary(struct{ BlockchainMessage }{}, r, maxBlockchainResponseSize, &n, &err).(struct{ BlockchainMessage }).BlockchainMessage
  256. if err != nil && n != len(bz) {
  257. err = errors.New("DecodeMessage() had bytes left over.")
  258. }
  259. return
  260. }
  261. //-------------------------------------
  262. type bcBlockRequestMessage struct {
  263. Height int
  264. }
  265. func (m *bcBlockRequestMessage) String() string {
  266. return fmt.Sprintf("[bcBlockRequestMessage %v]", m.Height)
  267. }
  268. //-------------------------------------
  269. // NOTE: keep up-to-date with maxBlockchainResponseSize
  270. type bcBlockResponseMessage struct {
  271. Block *types.Block
  272. }
  273. func (m *bcBlockResponseMessage) String() string {
  274. return fmt.Sprintf("[bcBlockResponseMessage %v]", m.Block.Height)
  275. }
  276. //-------------------------------------
  277. type bcStatusRequestMessage struct {
  278. Height int
  279. }
  280. func (m *bcStatusRequestMessage) String() string {
  281. return fmt.Sprintf("[bcStatusRequestMessage %v]", m.Height)
  282. }
  283. //-------------------------------------
  284. type bcStatusResponseMessage struct {
  285. Height int
  286. }
  287. func (m *bcStatusResponseMessage) String() string {
  288. return fmt.Sprintf("[bcStatusResponseMessage %v]", m.Height)
  289. }