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.

376 lines
7.9 KiB

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