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.

385 lines
8.8 KiB

  1. // nolint:unused
  2. package v2
  3. import (
  4. "fmt"
  5. "math"
  6. "math/rand"
  7. "time"
  8. "github.com/tendermint/tendermint/p2p"
  9. )
  10. type blockState int
  11. const (
  12. blockStateUnknown blockState = iota
  13. blockStateNew
  14. blockStatePending
  15. blockStateReceived
  16. blockStateProcessed
  17. )
  18. func (e blockState) String() string {
  19. switch e {
  20. case blockStateUnknown:
  21. return "Unknown"
  22. case blockStateNew:
  23. return "New"
  24. case blockStatePending:
  25. return "Pending"
  26. case blockStateReceived:
  27. return "Received"
  28. case blockStateProcessed:
  29. return "Processed"
  30. default:
  31. return fmt.Sprintf("unknown blockState: %d", e)
  32. }
  33. }
  34. type peerState int
  35. const (
  36. peerStateNew = iota
  37. peerStateReady
  38. peerStateRemoved
  39. )
  40. func (e peerState) String() string {
  41. switch e {
  42. case peerStateNew:
  43. return "New"
  44. case peerStateReady:
  45. return "Ready"
  46. case peerStateRemoved:
  47. return "Removed"
  48. default:
  49. return fmt.Sprintf("unknown peerState: %d", e)
  50. }
  51. }
  52. type scPeer struct {
  53. peerID p2p.ID
  54. state peerState
  55. height int64
  56. lastTouched time.Time
  57. lastRate int64
  58. }
  59. func newScPeer(peerID p2p.ID) *scPeer {
  60. return &scPeer{
  61. peerID: peerID,
  62. state: peerStateNew,
  63. height: -1,
  64. lastTouched: time.Time{},
  65. }
  66. }
  67. // The schedule is a composite data structure which allows a scheduler to keep
  68. // track of which blocks have been scheduled into which state.
  69. type schedule struct {
  70. initHeight int64
  71. // a list of blocks in which blockState
  72. blockStates map[int64]blockState
  73. // a map of peerID to schedule specific peer struct `scPeer` used to keep
  74. // track of peer specific state
  75. peers map[p2p.ID]*scPeer
  76. // a map of heights to the peer we are waiting for a response from
  77. pendingBlocks map[int64]p2p.ID
  78. // the time at which a block was put in blockStatePending
  79. pendingTime map[int64]time.Time
  80. // the peerID of the peer which put the block in blockStateReceived
  81. receivedBlocks map[int64]p2p.ID
  82. }
  83. func newSchedule(initHeight int64) *schedule {
  84. sc := schedule{
  85. initHeight: initHeight,
  86. blockStates: make(map[int64]blockState),
  87. peers: make(map[p2p.ID]*scPeer),
  88. pendingBlocks: make(map[int64]p2p.ID),
  89. pendingTime: make(map[int64]time.Time),
  90. receivedBlocks: make(map[int64]p2p.ID),
  91. }
  92. sc.setStateAtHeight(initHeight, blockStateNew)
  93. return &sc
  94. }
  95. func (sc *schedule) addPeer(peerID p2p.ID) error {
  96. if _, ok := sc.peers[peerID]; ok {
  97. return fmt.Errorf("Cannot add duplicate peer %s", peerID)
  98. }
  99. sc.peers[peerID] = newScPeer(peerID)
  100. return nil
  101. }
  102. func (sc *schedule) touchPeer(peerID p2p.ID, time time.Time) error {
  103. peer, ok := sc.peers[peerID]
  104. if !ok {
  105. return fmt.Errorf("Couldn't find peer %s", peerID)
  106. }
  107. if peer.state == peerStateRemoved {
  108. return fmt.Errorf("Tried to touch peer in peerStateRemoved")
  109. }
  110. peer.lastTouched = time
  111. return nil
  112. }
  113. func (sc *schedule) removePeer(peerID p2p.ID) error {
  114. peer, ok := sc.peers[peerID]
  115. if !ok {
  116. return fmt.Errorf("Couldn't find peer %s", peerID)
  117. }
  118. if peer.state == peerStateRemoved {
  119. return fmt.Errorf("Tried to remove peer %s in peerStateRemoved", peerID)
  120. }
  121. for height, pendingPeerID := range sc.pendingBlocks {
  122. if pendingPeerID == peerID {
  123. sc.setStateAtHeight(height, blockStateNew)
  124. delete(sc.pendingTime, height)
  125. delete(sc.pendingBlocks, height)
  126. }
  127. }
  128. for height, rcvPeerID := range sc.receivedBlocks {
  129. if rcvPeerID == peerID {
  130. sc.setStateAtHeight(height, blockStateNew)
  131. delete(sc.receivedBlocks, height)
  132. }
  133. }
  134. peer.state = peerStateRemoved
  135. return nil
  136. }
  137. func (sc *schedule) setPeerHeight(peerID p2p.ID, height int64) error {
  138. peer, ok := sc.peers[peerID]
  139. if !ok {
  140. return fmt.Errorf("Can't find peer %s", peerID)
  141. }
  142. if peer.state == peerStateRemoved {
  143. return fmt.Errorf("Cannot set peer height for a peer in peerStateRemoved")
  144. }
  145. if height < peer.height {
  146. return fmt.Errorf("Cannot move peer height lower. from %d to %d", peer.height, height)
  147. }
  148. peer.height = height
  149. peer.state = peerStateReady
  150. for i := sc.minHeight(); i <= height; i++ {
  151. if sc.getStateAtHeight(i) == blockStateUnknown {
  152. sc.setStateAtHeight(i, blockStateNew)
  153. }
  154. }
  155. return nil
  156. }
  157. func (sc *schedule) getStateAtHeight(height int64) blockState {
  158. if height < sc.initHeight {
  159. return blockStateProcessed
  160. } else if state, ok := sc.blockStates[height]; ok {
  161. return state
  162. } else {
  163. return blockStateUnknown
  164. }
  165. }
  166. func (sc *schedule) getPeersAtHeight(height int64) []*scPeer {
  167. peers := []*scPeer{}
  168. for _, peer := range sc.peers {
  169. if peer.height >= height {
  170. peers = append(peers, peer)
  171. }
  172. }
  173. return peers
  174. }
  175. func (sc *schedule) peersInactiveSince(duration time.Duration, now time.Time) []p2p.ID {
  176. peers := []p2p.ID{}
  177. for _, peer := range sc.peers {
  178. if now.Sub(peer.lastTouched) > duration {
  179. peers = append(peers, peer.peerID)
  180. }
  181. }
  182. return peers
  183. }
  184. func (sc *schedule) peersSlowerThan(minSpeed int64) []p2p.ID {
  185. peers := []p2p.ID{}
  186. for _, peer := range sc.peers {
  187. if peer.lastRate < minSpeed {
  188. peers = append(peers, peer.peerID)
  189. }
  190. }
  191. return peers
  192. }
  193. func (sc *schedule) setStateAtHeight(height int64, state blockState) {
  194. sc.blockStates[height] = state
  195. }
  196. func (sc *schedule) markReceived(peerID p2p.ID, height int64, size int64, now time.Time) error {
  197. peer, ok := sc.peers[peerID]
  198. if !ok {
  199. return fmt.Errorf("Can't find peer %s", peerID)
  200. }
  201. if peer.state == peerStateRemoved {
  202. return fmt.Errorf("Cannot receive blocks from removed peer %s", peerID)
  203. }
  204. if state := sc.getStateAtHeight(height); state != blockStatePending || sc.pendingBlocks[height] != peerID {
  205. return fmt.Errorf("Received block %d from peer %s without being requested", height, peerID)
  206. }
  207. pendingTime, ok := sc.pendingTime[height]
  208. if !ok || now.Sub(pendingTime) <= 0 {
  209. return fmt.Errorf("Clock error. Block %d received at %s but requested at %s",
  210. height, pendingTime, now)
  211. }
  212. peer.lastRate = size / int64(now.Sub(pendingTime).Seconds())
  213. sc.setStateAtHeight(height, blockStateReceived)
  214. delete(sc.pendingBlocks, height)
  215. delete(sc.pendingTime, height)
  216. sc.receivedBlocks[height] = peerID
  217. return nil
  218. }
  219. func (sc *schedule) markPending(peerID p2p.ID, height int64, time time.Time) error {
  220. peer, ok := sc.peers[peerID]
  221. if !ok {
  222. return fmt.Errorf("Can't find peer %s", peerID)
  223. }
  224. state := sc.getStateAtHeight(height)
  225. if state != blockStateNew {
  226. return fmt.Errorf("Block %d should be in blockStateNew but was %s", height, state)
  227. }
  228. if peer.state != peerStateReady {
  229. return fmt.Errorf("Cannot schedule %d from %s in %s", height, peerID, peer.state)
  230. }
  231. if height > peer.height {
  232. return fmt.Errorf("Cannot request height %d from peer %s who is at height %d",
  233. height, peerID, peer.height)
  234. }
  235. sc.setStateAtHeight(height, blockStatePending)
  236. sc.pendingBlocks[height] = peerID
  237. // XXX: to make this more accurate we can introduce a message from
  238. // the IO routine which indicates the time the request was put on the wire
  239. sc.pendingTime[height] = time
  240. return nil
  241. }
  242. func (sc *schedule) markProcessed(height int64) error {
  243. state := sc.getStateAtHeight(height)
  244. if state != blockStateReceived {
  245. return fmt.Errorf("Can't mark height %d received from block state %s", height, state)
  246. }
  247. delete(sc.receivedBlocks, height)
  248. sc.setStateAtHeight(height, blockStateProcessed)
  249. return nil
  250. }
  251. // allBlockProcessed returns true if all blocks are in blockStateProcessed and
  252. // determines if the schedule has been completed
  253. func (sc *schedule) allBlocksProcessed() bool {
  254. for _, state := range sc.blockStates {
  255. if state != blockStateProcessed {
  256. return false
  257. }
  258. }
  259. return true
  260. }
  261. // highest block | state == blockStateNew
  262. func (sc *schedule) maxHeight() int64 {
  263. var max int64 = 0
  264. for height, state := range sc.blockStates {
  265. if state == blockStateNew && height > max {
  266. max = height
  267. }
  268. }
  269. return max
  270. }
  271. // lowest block | state == blockStateNew
  272. func (sc *schedule) minHeight() int64 {
  273. var min int64 = math.MaxInt64
  274. for height, state := range sc.blockStates {
  275. if state == blockStateNew && height < min {
  276. min = height
  277. }
  278. }
  279. return min
  280. }
  281. func (sc *schedule) pendingFrom(peerID p2p.ID) []int64 {
  282. heights := []int64{}
  283. for height, pendingPeerID := range sc.pendingBlocks {
  284. if pendingPeerID == peerID {
  285. heights = append(heights, height)
  286. }
  287. }
  288. return heights
  289. }
  290. func (sc *schedule) selectPeer(peers []*scPeer) *scPeer {
  291. // FIXME: properPeerSelector
  292. s := rand.NewSource(time.Now().Unix())
  293. r := rand.New(s)
  294. return peers[r.Intn(len(peers))]
  295. }
  296. // XXX: this duplicates the logic of peersInactiveSince and peersSlowerThan
  297. func (sc *schedule) prunablePeers(peerTimout time.Duration, minRecvRate int64, now time.Time) []p2p.ID {
  298. prunable := []p2p.ID{}
  299. for peerID, peer := range sc.peers {
  300. if now.Sub(peer.lastTouched) > peerTimout || peer.lastRate < minRecvRate {
  301. prunable = append(prunable, peerID)
  302. }
  303. }
  304. return prunable
  305. }
  306. func (sc *schedule) numBlockInState(targetState blockState) uint32 {
  307. var num uint32 = 0
  308. for _, state := range sc.blockStates {
  309. if state == targetState {
  310. num++
  311. }
  312. }
  313. return num
  314. }