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.

303 lines
8.3 KiB

  1. package statesync
  2. import (
  3. "testing"
  4. "github.com/stretchr/testify/assert"
  5. "github.com/stretchr/testify/require"
  6. "github.com/tendermint/tendermint/p2p"
  7. p2pmocks "github.com/tendermint/tendermint/p2p/mocks"
  8. )
  9. func TestSnapshot_Key(t *testing.T) {
  10. testcases := map[string]struct {
  11. modify func(*snapshot)
  12. }{
  13. "new height": {func(s *snapshot) { s.Height = 9 }},
  14. "new format": {func(s *snapshot) { s.Format = 9 }},
  15. "new chunk count": {func(s *snapshot) { s.Chunks = 9 }},
  16. "new hash": {func(s *snapshot) { s.Hash = []byte{9} }},
  17. "no metadata": {func(s *snapshot) { s.Metadata = nil }},
  18. }
  19. for name, tc := range testcases {
  20. tc := tc
  21. t.Run(name, func(t *testing.T) {
  22. s := snapshot{
  23. Height: 3,
  24. Format: 1,
  25. Chunks: 7,
  26. Hash: []byte{1, 2, 3},
  27. Metadata: []byte{255},
  28. }
  29. before := s.Key()
  30. tc.modify(&s)
  31. after := s.Key()
  32. assert.NotEqual(t, before, after)
  33. })
  34. }
  35. }
  36. func TestSnapshotPool_Add(t *testing.T) {
  37. peer := &p2pmocks.Peer{}
  38. peer.On("ID").Return(p2p.ID("id"))
  39. // Adding to the pool should work
  40. pool := newSnapshotPool()
  41. added, err := pool.Add(peer, &snapshot{
  42. Height: 1,
  43. Format: 1,
  44. Chunks: 1,
  45. Hash: []byte{1},
  46. })
  47. require.NoError(t, err)
  48. assert.True(t, added)
  49. // Adding again from a different peer should return false
  50. otherPeer := &p2pmocks.Peer{}
  51. otherPeer.On("ID").Return(p2p.ID("other"))
  52. added, err = pool.Add(peer, &snapshot{
  53. Height: 1,
  54. Format: 1,
  55. Chunks: 1,
  56. Hash: []byte{1},
  57. })
  58. require.NoError(t, err)
  59. assert.False(t, added)
  60. // The pool should have populated the snapshot with the trusted app hash
  61. snapshot := pool.Best()
  62. require.NotNil(t, snapshot)
  63. }
  64. func TestSnapshotPool_GetPeer(t *testing.T) {
  65. pool := newSnapshotPool()
  66. s := &snapshot{Height: 1, Format: 1, Chunks: 1, Hash: []byte{1}}
  67. peerA := &p2pmocks.Peer{}
  68. peerA.On("ID").Return(p2p.ID("a"))
  69. peerB := &p2pmocks.Peer{}
  70. peerB.On("ID").Return(p2p.ID("b"))
  71. _, err := pool.Add(peerA, s)
  72. require.NoError(t, err)
  73. _, err = pool.Add(peerB, s)
  74. require.NoError(t, err)
  75. _, err = pool.Add(peerA, &snapshot{Height: 2, Format: 1, Chunks: 1, Hash: []byte{1}})
  76. require.NoError(t, err)
  77. // GetPeer currently picks a random peer, so lets run it until we've seen both.
  78. seenA := false
  79. seenB := false
  80. for !seenA || !seenB {
  81. peer := pool.GetPeer(s)
  82. switch peer.ID() {
  83. case p2p.ID("a"):
  84. seenA = true
  85. case p2p.ID("b"):
  86. seenB = true
  87. }
  88. }
  89. // GetPeer should return nil for an unknown snapshot
  90. peer := pool.GetPeer(&snapshot{Height: 9, Format: 9})
  91. assert.Nil(t, peer)
  92. }
  93. func TestSnapshotPool_GetPeers(t *testing.T) {
  94. pool := newSnapshotPool()
  95. s := &snapshot{Height: 1, Format: 1, Chunks: 1, Hash: []byte{1}}
  96. peerA := &p2pmocks.Peer{}
  97. peerA.On("ID").Return(p2p.ID("a"))
  98. peerB := &p2pmocks.Peer{}
  99. peerB.On("ID").Return(p2p.ID("b"))
  100. _, err := pool.Add(peerA, s)
  101. require.NoError(t, err)
  102. _, err = pool.Add(peerB, s)
  103. require.NoError(t, err)
  104. _, err = pool.Add(peerA, &snapshot{Height: 2, Format: 1, Chunks: 1, Hash: []byte{2}})
  105. require.NoError(t, err)
  106. peers := pool.GetPeers(s)
  107. assert.Len(t, peers, 2)
  108. assert.EqualValues(t, "a", peers[0].ID())
  109. assert.EqualValues(t, "b", peers[1].ID())
  110. }
  111. func TestSnapshotPool_Ranked_Best(t *testing.T) {
  112. pool := newSnapshotPool()
  113. // snapshots in expected order (best to worst). Highest height wins, then highest format.
  114. // Snapshots with different chunk hashes are considered different, and the most peers is
  115. // tie-breaker.
  116. expectSnapshots := []struct {
  117. snapshot *snapshot
  118. peers []string
  119. }{
  120. {&snapshot{Height: 2, Format: 2, Chunks: 4, Hash: []byte{1, 3}}, []string{"a", "b", "c"}},
  121. {&snapshot{Height: 2, Format: 2, Chunks: 5, Hash: []byte{1, 2}}, []string{"a"}},
  122. {&snapshot{Height: 2, Format: 1, Chunks: 3, Hash: []byte{1, 2}}, []string{"a", "b"}},
  123. {&snapshot{Height: 1, Format: 2, Chunks: 5, Hash: []byte{1, 2}}, []string{"a", "b"}},
  124. {&snapshot{Height: 1, Format: 1, Chunks: 4, Hash: []byte{1, 2}}, []string{"a", "b", "c"}},
  125. }
  126. // Add snapshots in reverse order, to make sure the pool enforces some order.
  127. for i := len(expectSnapshots) - 1; i >= 0; i-- {
  128. for _, peerID := range expectSnapshots[i].peers {
  129. peer := &p2pmocks.Peer{}
  130. peer.On("ID").Return(p2p.ID(peerID))
  131. _, err := pool.Add(peer, expectSnapshots[i].snapshot)
  132. require.NoError(t, err)
  133. }
  134. }
  135. // Ranked should return the snapshots in the same order
  136. ranked := pool.Ranked()
  137. assert.Len(t, ranked, len(expectSnapshots))
  138. for i := range ranked {
  139. assert.Equal(t, expectSnapshots[i].snapshot, ranked[i])
  140. }
  141. // Check that best snapshots are returned in expected order
  142. for i := range expectSnapshots {
  143. snapshot := expectSnapshots[i].snapshot
  144. require.Equal(t, snapshot, pool.Best())
  145. pool.Reject(snapshot)
  146. }
  147. assert.Nil(t, pool.Best())
  148. }
  149. func TestSnapshotPool_Reject(t *testing.T) {
  150. pool := newSnapshotPool()
  151. peer := &p2pmocks.Peer{}
  152. peer.On("ID").Return(p2p.ID("id"))
  153. snapshots := []*snapshot{
  154. {Height: 2, Format: 2, Chunks: 1, Hash: []byte{1, 2}},
  155. {Height: 2, Format: 1, Chunks: 1, Hash: []byte{1, 2}},
  156. {Height: 1, Format: 2, Chunks: 1, Hash: []byte{1, 2}},
  157. {Height: 1, Format: 1, Chunks: 1, Hash: []byte{1, 2}},
  158. }
  159. for _, s := range snapshots {
  160. _, err := pool.Add(peer, s)
  161. require.NoError(t, err)
  162. }
  163. pool.Reject(snapshots[0])
  164. assert.Equal(t, snapshots[1:], pool.Ranked())
  165. added, err := pool.Add(peer, snapshots[0])
  166. require.NoError(t, err)
  167. assert.False(t, added)
  168. added, err = pool.Add(peer, &snapshot{Height: 3, Format: 3, Chunks: 1, Hash: []byte{1}})
  169. require.NoError(t, err)
  170. assert.True(t, added)
  171. }
  172. func TestSnapshotPool_RejectFormat(t *testing.T) {
  173. pool := newSnapshotPool()
  174. peer := &p2pmocks.Peer{}
  175. peer.On("ID").Return(p2p.ID("id"))
  176. snapshots := []*snapshot{
  177. {Height: 2, Format: 2, Chunks: 1, Hash: []byte{1, 2}},
  178. {Height: 2, Format: 1, Chunks: 1, Hash: []byte{1, 2}},
  179. {Height: 1, Format: 2, Chunks: 1, Hash: []byte{1, 2}},
  180. {Height: 1, Format: 1, Chunks: 1, Hash: []byte{1, 2}},
  181. }
  182. for _, s := range snapshots {
  183. _, err := pool.Add(peer, s)
  184. require.NoError(t, err)
  185. }
  186. pool.RejectFormat(1)
  187. assert.Equal(t, []*snapshot{snapshots[0], snapshots[2]}, pool.Ranked())
  188. added, err := pool.Add(peer, &snapshot{Height: 3, Format: 1, Chunks: 1, Hash: []byte{1}})
  189. require.NoError(t, err)
  190. assert.False(t, added)
  191. assert.Equal(t, []*snapshot{snapshots[0], snapshots[2]}, pool.Ranked())
  192. added, err = pool.Add(peer, &snapshot{Height: 3, Format: 3, Chunks: 1, Hash: []byte{1}})
  193. require.NoError(t, err)
  194. assert.True(t, added)
  195. }
  196. func TestSnapshotPool_RejectPeer(t *testing.T) {
  197. pool := newSnapshotPool()
  198. peerA := &p2pmocks.Peer{}
  199. peerA.On("ID").Return(p2p.ID("a"))
  200. peerB := &p2pmocks.Peer{}
  201. peerB.On("ID").Return(p2p.ID("b"))
  202. s1 := &snapshot{Height: 1, Format: 1, Chunks: 1, Hash: []byte{1}}
  203. s2 := &snapshot{Height: 2, Format: 1, Chunks: 1, Hash: []byte{2}}
  204. s3 := &snapshot{Height: 3, Format: 1, Chunks: 1, Hash: []byte{2}}
  205. _, err := pool.Add(peerA, s1)
  206. require.NoError(t, err)
  207. _, err = pool.Add(peerA, s2)
  208. require.NoError(t, err)
  209. _, err = pool.Add(peerB, s2)
  210. require.NoError(t, err)
  211. _, err = pool.Add(peerB, s3)
  212. require.NoError(t, err)
  213. pool.RejectPeer(peerA.ID())
  214. assert.Empty(t, pool.GetPeers(s1))
  215. peers2 := pool.GetPeers(s2)
  216. assert.Len(t, peers2, 1)
  217. assert.EqualValues(t, "b", peers2[0].ID())
  218. peers3 := pool.GetPeers(s2)
  219. assert.Len(t, peers3, 1)
  220. assert.EqualValues(t, "b", peers3[0].ID())
  221. // it should no longer be possible to add the peer back
  222. _, err = pool.Add(peerA, s1)
  223. require.NoError(t, err)
  224. assert.Empty(t, pool.GetPeers(s1))
  225. }
  226. func TestSnapshotPool_RemovePeer(t *testing.T) {
  227. pool := newSnapshotPool()
  228. peerA := &p2pmocks.Peer{}
  229. peerA.On("ID").Return(p2p.ID("a"))
  230. peerB := &p2pmocks.Peer{}
  231. peerB.On("ID").Return(p2p.ID("b"))
  232. s1 := &snapshot{Height: 1, Format: 1, Chunks: 1, Hash: []byte{1}}
  233. s2 := &snapshot{Height: 2, Format: 1, Chunks: 1, Hash: []byte{2}}
  234. _, err := pool.Add(peerA, s1)
  235. require.NoError(t, err)
  236. _, err = pool.Add(peerA, s2)
  237. require.NoError(t, err)
  238. _, err = pool.Add(peerB, s1)
  239. require.NoError(t, err)
  240. pool.RemovePeer(peerA.ID())
  241. peers1 := pool.GetPeers(s1)
  242. assert.Len(t, peers1, 1)
  243. assert.EqualValues(t, "b", peers1[0].ID())
  244. peers2 := pool.GetPeers(s2)
  245. assert.Empty(t, peers2)
  246. // it should still be possible to add the peer back
  247. _, err = pool.Add(peerA, s1)
  248. require.NoError(t, err)
  249. peers1 = pool.GetPeers(s1)
  250. assert.Len(t, peers1, 2)
  251. assert.EqualValues(t, "a", peers1[0].ID())
  252. assert.EqualValues(t, "b", peers1[1].ID())
  253. }