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  }