github.com/vipernet-xyz/tm@v0.34.24/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/vipernet-xyz/tm/p2p"
    10  	p2pmocks "github.com/vipernet-xyz/tm/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  }