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