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.

255 lines
6.8 KiB

  1. package statesync
  2. import (
  3. "crypto/sha256"
  4. "fmt"
  5. "math/rand"
  6. "sort"
  7. tmsync "github.com/tendermint/tendermint/libs/sync"
  8. "github.com/tendermint/tendermint/p2p"
  9. )
  10. // snapshotKey is a snapshot key used for lookups.
  11. type snapshotKey [sha256.Size]byte
  12. // snapshot contains data about a snapshot.
  13. type snapshot struct {
  14. Height uint64
  15. Format uint32
  16. Chunks uint32
  17. Hash []byte
  18. Metadata []byte
  19. trustedAppHash []byte // populated by light client
  20. }
  21. // Key generates a snapshot key, used for lookups. It takes into account not only the height and
  22. // format, but also the chunks, hash, and metadata in case peers have generated snapshots in a
  23. // non-deterministic manner. All fields must be equal for the snapshot to be considered the same.
  24. func (s *snapshot) Key() snapshotKey {
  25. // Hash.Write() never returns an error.
  26. hasher := sha256.New()
  27. hasher.Write([]byte(fmt.Sprintf("%v:%v:%v", s.Height, s.Format, s.Chunks)))
  28. hasher.Write(s.Hash)
  29. hasher.Write(s.Metadata)
  30. var key snapshotKey
  31. copy(key[:], hasher.Sum(nil))
  32. return key
  33. }
  34. // snapshotPool discovers and aggregates snapshots across peers.
  35. type snapshotPool struct {
  36. tmsync.Mutex
  37. snapshots map[snapshotKey]*snapshot
  38. snapshotPeers map[snapshotKey]map[p2p.ID]p2p.Peer
  39. // indexes for fast searches
  40. formatIndex map[uint32]map[snapshotKey]bool
  41. heightIndex map[uint64]map[snapshotKey]bool
  42. peerIndex map[p2p.ID]map[snapshotKey]bool
  43. // blacklists for rejected items
  44. formatBlacklist map[uint32]bool
  45. peerBlacklist map[p2p.ID]bool
  46. snapshotBlacklist map[snapshotKey]bool
  47. }
  48. // newSnapshotPool creates a new snapshot pool. The state source is used for
  49. func newSnapshotPool() *snapshotPool {
  50. return &snapshotPool{
  51. snapshots: make(map[snapshotKey]*snapshot),
  52. snapshotPeers: make(map[snapshotKey]map[p2p.ID]p2p.Peer),
  53. formatIndex: make(map[uint32]map[snapshotKey]bool),
  54. heightIndex: make(map[uint64]map[snapshotKey]bool),
  55. peerIndex: make(map[p2p.ID]map[snapshotKey]bool),
  56. formatBlacklist: make(map[uint32]bool),
  57. peerBlacklist: make(map[p2p.ID]bool),
  58. snapshotBlacklist: make(map[snapshotKey]bool),
  59. }
  60. }
  61. // Add adds a snapshot to the pool, unless the peer has already sent recentSnapshots snapshots. It
  62. // returns true if this was a new, non-blacklisted snapshot. The snapshot height is verified using
  63. // the light client, and the expected app hash is set for the snapshot.
  64. func (p *snapshotPool) Add(peer p2p.Peer, snapshot *snapshot) (bool, error) {
  65. key := snapshot.Key()
  66. p.Lock()
  67. defer p.Unlock()
  68. switch {
  69. case p.formatBlacklist[snapshot.Format]:
  70. return false, nil
  71. case p.peerBlacklist[peer.ID()]:
  72. return false, nil
  73. case p.snapshotBlacklist[key]:
  74. return false, nil
  75. case len(p.peerIndex[peer.ID()]) >= recentSnapshots:
  76. return false, nil
  77. }
  78. if p.snapshotPeers[key] == nil {
  79. p.snapshotPeers[key] = make(map[p2p.ID]p2p.Peer)
  80. }
  81. p.snapshotPeers[key][peer.ID()] = peer
  82. if p.peerIndex[peer.ID()] == nil {
  83. p.peerIndex[peer.ID()] = make(map[snapshotKey]bool)
  84. }
  85. p.peerIndex[peer.ID()][key] = true
  86. if p.snapshots[key] != nil {
  87. return false, nil
  88. }
  89. p.snapshots[key] = snapshot
  90. if p.formatIndex[snapshot.Format] == nil {
  91. p.formatIndex[snapshot.Format] = make(map[snapshotKey]bool)
  92. }
  93. p.formatIndex[snapshot.Format][key] = true
  94. if p.heightIndex[snapshot.Height] == nil {
  95. p.heightIndex[snapshot.Height] = make(map[snapshotKey]bool)
  96. }
  97. p.heightIndex[snapshot.Height][key] = true
  98. return true, nil
  99. }
  100. // Best returns the "best" currently known snapshot, if any.
  101. func (p *snapshotPool) Best() *snapshot {
  102. ranked := p.Ranked()
  103. if len(ranked) == 0 {
  104. return nil
  105. }
  106. return ranked[0]
  107. }
  108. // GetPeer returns a random peer for a snapshot, if any.
  109. func (p *snapshotPool) GetPeer(snapshot *snapshot) p2p.Peer {
  110. peers := p.GetPeers(snapshot)
  111. if len(peers) == 0 {
  112. return nil
  113. }
  114. return peers[rand.Intn(len(peers))] // nolint:gosec // G404: Use of weak random number generator
  115. }
  116. // GetPeers returns the peers for a snapshot.
  117. func (p *snapshotPool) GetPeers(snapshot *snapshot) []p2p.Peer {
  118. key := snapshot.Key()
  119. p.Lock()
  120. defer p.Unlock()
  121. peers := make([]p2p.Peer, 0, len(p.snapshotPeers[key]))
  122. for _, peer := range p.snapshotPeers[key] {
  123. peers = append(peers, peer)
  124. }
  125. // sort results, for testability (otherwise order is random, so tests randomly fail)
  126. sort.Slice(peers, func(a int, b int) bool {
  127. return peers[a].ID() < peers[b].ID()
  128. })
  129. return peers
  130. }
  131. // Ranked returns a list of snapshots ranked by preference. The current heuristic is very naïve,
  132. // preferring the snapshot with the greatest height, then greatest format, then greatest number of
  133. // peers. This can be improved quite a lot.
  134. func (p *snapshotPool) Ranked() []*snapshot {
  135. p.Lock()
  136. defer p.Unlock()
  137. candidates := make([]*snapshot, 0, len(p.snapshots))
  138. for key := range p.snapshots {
  139. candidates = append(candidates, p.snapshots[key])
  140. }
  141. sort.Slice(candidates, func(i, j int) bool {
  142. a := candidates[i]
  143. b := candidates[j]
  144. switch {
  145. case a.Height > b.Height:
  146. return true
  147. case a.Height < b.Height:
  148. return false
  149. case a.Format > b.Format:
  150. return true
  151. case a.Format < b.Format:
  152. return false
  153. case len(p.snapshotPeers[a.Key()]) > len(p.snapshotPeers[b.Key()]):
  154. return true
  155. default:
  156. return false
  157. }
  158. })
  159. return candidates
  160. }
  161. // Reject rejects a snapshot. Rejected snapshots will never be used again.
  162. func (p *snapshotPool) Reject(snapshot *snapshot) {
  163. key := snapshot.Key()
  164. p.Lock()
  165. defer p.Unlock()
  166. p.snapshotBlacklist[key] = true
  167. p.removeSnapshot(key)
  168. }
  169. // RejectFormat rejects a snapshot format. It will never be used again.
  170. func (p *snapshotPool) RejectFormat(format uint32) {
  171. p.Lock()
  172. defer p.Unlock()
  173. p.formatBlacklist[format] = true
  174. for key := range p.formatIndex[format] {
  175. p.removeSnapshot(key)
  176. }
  177. }
  178. // RejectPeer rejects a peer. It will never be used again.
  179. func (p *snapshotPool) RejectPeer(peerID p2p.ID) {
  180. if peerID == "" {
  181. return
  182. }
  183. p.Lock()
  184. defer p.Unlock()
  185. p.removePeer(peerID)
  186. p.peerBlacklist[peerID] = true
  187. }
  188. // RemovePeer removes a peer from the pool, and any snapshots that no longer have peers.
  189. func (p *snapshotPool) RemovePeer(peerID p2p.ID) {
  190. p.Lock()
  191. defer p.Unlock()
  192. p.removePeer(peerID)
  193. }
  194. // removePeer removes a peer. The caller must hold the mutex lock.
  195. func (p *snapshotPool) removePeer(peerID p2p.ID) {
  196. for key := range p.peerIndex[peerID] {
  197. delete(p.snapshotPeers[key], peerID)
  198. if len(p.snapshotPeers[key]) == 0 {
  199. p.removeSnapshot(key)
  200. }
  201. }
  202. delete(p.peerIndex, peerID)
  203. }
  204. // removeSnapshot removes a snapshot. The caller must hold the mutex lock.
  205. func (p *snapshotPool) removeSnapshot(key snapshotKey) {
  206. snapshot := p.snapshots[key]
  207. if snapshot == nil {
  208. return
  209. }
  210. delete(p.snapshots, key)
  211. delete(p.formatIndex[snapshot.Format], key)
  212. delete(p.heightIndex[snapshot.Height], key)
  213. for peerID := range p.snapshotPeers[key] {
  214. delete(p.peerIndex[peerID], key)
  215. }
  216. delete(p.snapshotPeers, key)
  217. }