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.

390 lines
8.7 KiB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
  1. package blockchain
  2. import (
  3. "sync"
  4. "time"
  5. . "github.com/tendermint/tendermint/common"
  6. "github.com/tendermint/tendermint/types"
  7. )
  8. const (
  9. maxTries = 3
  10. inputsChannelCapacity = 200
  11. requestIntervalMS = 500
  12. maxPendingRequests = 200
  13. maxTotalRequests = 300
  14. maxRequestsPerPeer = 300
  15. )
  16. var (
  17. requestTimeoutSeconds = time.Duration(3)
  18. )
  19. /*
  20. Peers self report their heights when we join the block pool.
  21. Starting from our latest pool.height, we request blocks
  22. in sequence from peers that reported higher heights than ours.
  23. Every so often we ask peers what height they're on so we can keep going.
  24. Requests are continuously made for blocks of heigher heights until
  25. the limits. If most of the requests have no available peers, and we
  26. are not at peer limits, we can probably switch to consensus reactor
  27. */
  28. type BlockPool struct {
  29. BaseService
  30. // block requests
  31. requestsMtx sync.Mutex
  32. requests map[int]*bpRequest
  33. height int // the lowest key in requests.
  34. numUnassigned int32 // number of requests not yet assigned to a peer
  35. numPending int32 // number of requests pending assignment or block response
  36. // peers
  37. peersMtx sync.Mutex
  38. peers map[string]*bpPeer
  39. requestsCh chan<- BlockRequest
  40. timeoutsCh chan<- string
  41. repeater *RepeatTimer
  42. }
  43. func NewBlockPool(start int, requestsCh chan<- BlockRequest, timeoutsCh chan<- string) *BlockPool {
  44. bp := &BlockPool{
  45. peers: make(map[string]*bpPeer),
  46. requests: make(map[int]*bpRequest),
  47. height: start,
  48. numUnassigned: 0,
  49. numPending: 0,
  50. requestsCh: requestsCh,
  51. timeoutsCh: timeoutsCh,
  52. repeater: nil,
  53. }
  54. bp.BaseService = *NewBaseService(log, "BlockPool", bp)
  55. return bp
  56. }
  57. func (pool *BlockPool) OnStart() {
  58. pool.BaseService.OnStart()
  59. pool.repeater = NewRepeatTimer("", requestIntervalMS*time.Millisecond)
  60. go pool.run()
  61. }
  62. func (pool *BlockPool) OnStop() {
  63. pool.BaseService.OnStop()
  64. pool.repeater.Stop()
  65. }
  66. // Run spawns requests as needed.
  67. func (pool *BlockPool) run() {
  68. RUN_LOOP:
  69. for {
  70. if !pool.IsRunning() {
  71. break RUN_LOOP
  72. }
  73. _, numPending, _ := pool.GetStatus()
  74. if numPending >= maxPendingRequests {
  75. // sleep for a bit.
  76. time.Sleep(requestIntervalMS * time.Millisecond)
  77. } else if len(pool.requests) >= maxTotalRequests {
  78. // sleep for a bit.
  79. time.Sleep(requestIntervalMS * time.Millisecond)
  80. } else {
  81. // request for more blocks.
  82. pool.makeNextRequest()
  83. }
  84. }
  85. }
  86. func (pool *BlockPool) GetStatus() (height int, numPending int32, numUnssigned int32) {
  87. pool.requestsMtx.Lock() // Lock
  88. defer pool.requestsMtx.Unlock()
  89. return pool.height, pool.numPending, pool.numUnassigned
  90. }
  91. // We need to see the second block's Validation to validate the first block.
  92. // So we peek two blocks at a time.
  93. func (pool *BlockPool) PeekTwoBlocks() (first *types.Block, second *types.Block) {
  94. pool.requestsMtx.Lock() // Lock
  95. defer pool.requestsMtx.Unlock()
  96. if r := pool.requests[pool.height]; r != nil {
  97. first = r.block
  98. }
  99. if r := pool.requests[pool.height+1]; r != nil {
  100. second = r.block
  101. }
  102. return
  103. }
  104. // Pop the first block at pool.height
  105. // It must have been validated by 'second'.Validation from PeekTwoBlocks().
  106. func (pool *BlockPool) PopRequest() {
  107. pool.requestsMtx.Lock() // Lock
  108. defer pool.requestsMtx.Unlock()
  109. if r := pool.requests[pool.height]; r == nil || r.block == nil {
  110. PanicSanity("PopRequest() requires a valid block")
  111. }
  112. delete(pool.requests, pool.height)
  113. pool.height++
  114. }
  115. // Invalidates the block at pool.height.
  116. // Remove the peer and request from others.
  117. func (pool *BlockPool) RedoRequest(height int) {
  118. pool.requestsMtx.Lock() // Lock
  119. defer pool.requestsMtx.Unlock()
  120. request := pool.requests[height]
  121. if request.block == nil {
  122. PanicSanity("Expected block to be non-nil")
  123. }
  124. // TODO: record this malfeasance
  125. // maybe punish peer on switch (an invalid block!)
  126. pool.RemovePeer(request.peerId) // Lock on peersMtx.
  127. request.block = nil
  128. request.peerId = ""
  129. pool.numPending++
  130. pool.numUnassigned++
  131. go requestRoutine(pool, height)
  132. }
  133. func (pool *BlockPool) hasBlock(height int) bool {
  134. pool.requestsMtx.Lock() // Lock
  135. defer pool.requestsMtx.Unlock()
  136. request := pool.requests[height]
  137. return request != nil && request.block != nil
  138. }
  139. func (pool *BlockPool) setPeerForRequest(height int, peerId string) {
  140. pool.requestsMtx.Lock() // Lock
  141. defer pool.requestsMtx.Unlock()
  142. request := pool.requests[height]
  143. if request == nil {
  144. return
  145. }
  146. pool.numUnassigned--
  147. request.peerId = peerId
  148. }
  149. func (pool *BlockPool) removePeerForRequest(height int, peerId string) {
  150. pool.requestsMtx.Lock() // Lock
  151. defer pool.requestsMtx.Unlock()
  152. request := pool.requests[height]
  153. if request == nil {
  154. return
  155. }
  156. pool.numUnassigned++
  157. request.peerId = ""
  158. }
  159. func (pool *BlockPool) AddBlock(block *types.Block, peerId string) {
  160. pool.requestsMtx.Lock() // Lock
  161. defer pool.requestsMtx.Unlock()
  162. request := pool.requests[block.Height]
  163. if request == nil {
  164. return
  165. }
  166. if request.peerId != peerId {
  167. return
  168. }
  169. if request.block != nil {
  170. return
  171. }
  172. request.block = block
  173. pool.numPending--
  174. }
  175. func (pool *BlockPool) getPeer(peerId string) *bpPeer {
  176. pool.peersMtx.Lock() // Lock
  177. defer pool.peersMtx.Unlock()
  178. peer := pool.peers[peerId]
  179. return peer
  180. }
  181. // Sets the peer's alleged blockchain height.
  182. func (pool *BlockPool) SetPeerHeight(peerId string, height int) {
  183. pool.peersMtx.Lock() // Lock
  184. defer pool.peersMtx.Unlock()
  185. peer := pool.peers[peerId]
  186. if peer != nil {
  187. peer.height = height
  188. } else {
  189. peer = &bpPeer{
  190. height: height,
  191. id: peerId,
  192. numRequests: 0,
  193. }
  194. pool.peers[peerId] = peer
  195. }
  196. }
  197. func (pool *BlockPool) RemovePeer(peerId string) {
  198. pool.peersMtx.Lock() // Lock
  199. defer pool.peersMtx.Unlock()
  200. delete(pool.peers, peerId)
  201. }
  202. // Pick an available peer with at least the given minHeight.
  203. // If no peers are available, returns nil.
  204. func (pool *BlockPool) pickIncrAvailablePeer(minHeight int) *bpPeer {
  205. pool.peersMtx.Lock()
  206. defer pool.peersMtx.Unlock()
  207. for _, peer := range pool.peers {
  208. if peer.numRequests >= maxRequestsPerPeer {
  209. continue
  210. }
  211. if peer.height < minHeight {
  212. continue
  213. }
  214. peer.numRequests++
  215. return peer
  216. }
  217. return nil
  218. }
  219. func (pool *BlockPool) decrPeer(peerId string) {
  220. pool.peersMtx.Lock()
  221. defer pool.peersMtx.Unlock()
  222. peer := pool.peers[peerId]
  223. if peer == nil {
  224. return
  225. }
  226. peer.numRequests--
  227. }
  228. func (pool *BlockPool) makeNextRequest() {
  229. pool.requestsMtx.Lock() // Lock
  230. defer pool.requestsMtx.Unlock()
  231. nextHeight := pool.height + len(pool.requests)
  232. request := &bpRequest{
  233. height: nextHeight,
  234. peerId: "",
  235. block: nil,
  236. }
  237. pool.requests[nextHeight] = request
  238. pool.numUnassigned++
  239. pool.numPending++
  240. go requestRoutine(pool, nextHeight)
  241. }
  242. func (pool *BlockPool) sendRequest(height int, peerId string) {
  243. if !pool.IsRunning() {
  244. return
  245. }
  246. pool.requestsCh <- BlockRequest{height, peerId}
  247. }
  248. func (pool *BlockPool) sendTimeout(peerId string) {
  249. if !pool.IsRunning() {
  250. return
  251. }
  252. pool.timeoutsCh <- peerId
  253. }
  254. func (pool *BlockPool) debug() string {
  255. pool.requestsMtx.Lock() // Lock
  256. defer pool.requestsMtx.Unlock()
  257. str := ""
  258. for h := pool.height; h < pool.height+len(pool.requests); h++ {
  259. if pool.requests[h] == nil {
  260. str += Fmt("H(%v):X ", h)
  261. } else {
  262. str += Fmt("H(%v):", h)
  263. str += Fmt("B?(%v) ", pool.requests[h].block != nil)
  264. }
  265. }
  266. return str
  267. }
  268. //-------------------------------------
  269. type bpPeer struct {
  270. id string
  271. height int
  272. numRequests int32
  273. }
  274. type bpRequest struct {
  275. height int
  276. peerId string
  277. block *types.Block
  278. }
  279. //-------------------------------------
  280. // Responsible for making more requests as necessary
  281. // Returns only when a block is found (e.g. AddBlock() is called)
  282. func requestRoutine(pool *BlockPool, height int) {
  283. for {
  284. var peer *bpPeer = nil
  285. PICK_LOOP:
  286. for {
  287. if !pool.IsRunning() {
  288. log.Info("BlockPool not running. Stopping requestRoutine", "height", height)
  289. return
  290. }
  291. peer = pool.pickIncrAvailablePeer(height)
  292. if peer == nil {
  293. //log.Info("No peers available", "height", height)
  294. time.Sleep(requestIntervalMS * time.Millisecond)
  295. continue PICK_LOOP
  296. }
  297. break PICK_LOOP
  298. }
  299. // set the peer, decrement numUnassigned
  300. pool.setPeerForRequest(height, peer.id)
  301. for try := 0; try < maxTries; try++ {
  302. pool.sendRequest(height, peer.id)
  303. time.Sleep(requestTimeoutSeconds * time.Second)
  304. // if successful the block is either in the pool,
  305. if pool.hasBlock(height) {
  306. pool.decrPeer(peer.id)
  307. return
  308. }
  309. // or already processed and we've moved past it
  310. bpHeight, _, _ := pool.GetStatus()
  311. if height < bpHeight {
  312. pool.decrPeer(peer.id)
  313. return
  314. }
  315. }
  316. // unset the peer, increment numUnassigned
  317. pool.removePeerForRequest(height, peer.id)
  318. // this peer failed us, try again
  319. pool.RemovePeer(peer.id)
  320. pool.sendTimeout(peer.id)
  321. }
  322. }
  323. //-------------------------------------
  324. type BlockRequest struct {
  325. Height int
  326. PeerId string
  327. }