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.

327 lines
9.2 KiB

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