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