github.com/okex/exchain@v1.8.0/libs/tendermint/lite2/client_test.go (about)

     1  package lite_test
     2  
     3  import (
     4  	"sync"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/stretchr/testify/assert"
     9  	"github.com/stretchr/testify/require"
    10  
    11  	dbm "github.com/okex/exchain/libs/tm-db"
    12  
    13  	"github.com/okex/exchain/libs/tendermint/libs/log"
    14  	lite "github.com/okex/exchain/libs/tendermint/lite2"
    15  	"github.com/okex/exchain/libs/tendermint/lite2/provider"
    16  	mockp "github.com/okex/exchain/libs/tendermint/lite2/provider/mock"
    17  	dbs "github.com/okex/exchain/libs/tendermint/lite2/store/db"
    18  	"github.com/okex/exchain/libs/tendermint/types"
    19  )
    20  
    21  const (
    22  	chainID = "test"
    23  )
    24  
    25  var (
    26  	keys     = genPrivKeys(4)
    27  	vals     = keys.ToValidators(20, 10)
    28  	bTime, _ = time.Parse(time.RFC3339, "2006-01-02T15:04:05Z")
    29  	h1       = keys.GenSignedHeader(chainID, 1, bTime, nil, vals, vals,
    30  		hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(keys))
    31  	// 3/3 signed
    32  	h2 = keys.GenSignedHeaderLastBlockID(chainID, 2, bTime.Add(30*time.Minute), nil, vals, vals,
    33  		hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(keys), types.BlockID{Hash: h1.Hash()})
    34  	// 3/3 signed
    35  	h3 = keys.GenSignedHeaderLastBlockID(chainID, 3, bTime.Add(1*time.Hour), nil, vals, vals,
    36  		hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(keys), types.BlockID{Hash: h2.Hash()})
    37  	trustPeriod  = 4 * time.Hour
    38  	trustOptions = lite.TrustOptions{
    39  		Period: 4 * time.Hour,
    40  		Height: 1,
    41  		Hash:   h1.Hash(),
    42  	}
    43  	valSet = map[int64]*types.ValidatorSet{
    44  		1: vals,
    45  		2: vals,
    46  		3: vals,
    47  		4: vals,
    48  	}
    49  	headerSet = map[int64]*types.SignedHeader{
    50  		1: h1,
    51  		// interim header (3/3 signed)
    52  		2: h2,
    53  		// last header (3/3 signed)
    54  		3: h3,
    55  	}
    56  	fullNode = mockp.New(
    57  		chainID,
    58  		headerSet,
    59  		valSet,
    60  	)
    61  	deadNode      = mockp.NewDeadMock(chainID)
    62  	largeFullNode = mockp.New(GenMockNode(chainID, 10, 3, 0, bTime))
    63  )
    64  
    65  func TestClient_SequentialVerification(t *testing.T) {
    66  	newKeys := genPrivKeys(4)
    67  	newVals := newKeys.ToValidators(10, 1)
    68  
    69  	testCases := []struct {
    70  		name         string
    71  		otherHeaders map[int64]*types.SignedHeader // all except ^
    72  		vals         map[int64]*types.ValidatorSet
    73  		initErr      bool
    74  		verifyErr    bool
    75  	}{
    76  		{
    77  			"good",
    78  			headerSet,
    79  			valSet,
    80  			false,
    81  			false,
    82  		},
    83  		{
    84  			"bad: different first header",
    85  			map[int64]*types.SignedHeader{
    86  				// different header
    87  				1: keys.GenSignedHeader(chainID, 1, bTime.Add(1*time.Hour), nil, vals, vals,
    88  					hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(keys)),
    89  			},
    90  			map[int64]*types.ValidatorSet{
    91  				1: vals,
    92  			},
    93  			true,
    94  			false,
    95  		},
    96  		{
    97  			"bad: 1/3 signed interim header",
    98  			map[int64]*types.SignedHeader{
    99  				// trusted header
   100  				1: h1,
   101  				// interim header (1/3 signed)
   102  				2: keys.GenSignedHeader(chainID, 2, bTime.Add(1*time.Hour), nil, vals, vals,
   103  					hash("app_hash"), hash("cons_hash"), hash("results_hash"), len(keys)-1, len(keys)),
   104  				// last header (3/3 signed)
   105  				3: keys.GenSignedHeader(chainID, 3, bTime.Add(2*time.Hour), nil, vals, vals,
   106  					hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(keys)),
   107  			},
   108  			valSet,
   109  			false,
   110  			true,
   111  		},
   112  		{
   113  			"bad: 1/3 signed last header",
   114  			map[int64]*types.SignedHeader{
   115  				// trusted header
   116  				1: h1,
   117  				// interim header (3/3 signed)
   118  				2: keys.GenSignedHeader(chainID, 2, bTime.Add(1*time.Hour), nil, vals, vals,
   119  					hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(keys)),
   120  				// last header (1/3 signed)
   121  				3: keys.GenSignedHeader(chainID, 3, bTime.Add(2*time.Hour), nil, vals, vals,
   122  					hash("app_hash"), hash("cons_hash"), hash("results_hash"), len(keys)-1, len(keys)),
   123  			},
   124  			valSet,
   125  			false,
   126  			true,
   127  		},
   128  		{
   129  			"bad: different validator set at height 3",
   130  			headerSet,
   131  			map[int64]*types.ValidatorSet{
   132  				1: vals,
   133  				2: vals,
   134  				3: newVals,
   135  			},
   136  			false,
   137  			true,
   138  		},
   139  	}
   140  
   141  	for _, tc := range testCases {
   142  		tc := tc
   143  		t.Run(tc.name, func(t *testing.T) {
   144  			c, err := lite.NewClient(
   145  				chainID,
   146  				trustOptions,
   147  				mockp.New(
   148  					chainID,
   149  					tc.otherHeaders,
   150  					tc.vals,
   151  				),
   152  				[]provider.Provider{mockp.New(
   153  					chainID,
   154  					tc.otherHeaders,
   155  					tc.vals,
   156  				)},
   157  				dbs.New(dbm.NewMemDB(), chainID),
   158  				lite.SequentialVerification(),
   159  			)
   160  
   161  			if tc.initErr {
   162  				require.Error(t, err)
   163  				return
   164  			}
   165  
   166  			require.NoError(t, err)
   167  
   168  			_, err = c.VerifyHeaderAtHeight(3, bTime.Add(3*time.Hour))
   169  			if tc.verifyErr {
   170  				assert.Error(t, err)
   171  			} else {
   172  				assert.NoError(t, err)
   173  			}
   174  		})
   175  	}
   176  }
   177  
   178  func TestClient_SkippingVerification(t *testing.T) {
   179  	// required for 2nd test case
   180  	newKeys := genPrivKeys(4)
   181  	newVals := newKeys.ToValidators(10, 1)
   182  
   183  	// 1/3+ of vals, 2/3- of newVals
   184  	transitKeys := keys.Extend(3)
   185  	transitVals := transitKeys.ToValidators(10, 1)
   186  
   187  	testCases := []struct {
   188  		name         string
   189  		otherHeaders map[int64]*types.SignedHeader // all except ^
   190  		vals         map[int64]*types.ValidatorSet
   191  		initErr      bool
   192  		verifyErr    bool
   193  	}{
   194  		{
   195  			"good",
   196  			map[int64]*types.SignedHeader{
   197  				// trusted header
   198  				1: h1,
   199  				// last header (3/3 signed)
   200  				3: h3,
   201  			},
   202  			valSet,
   203  			false,
   204  			false,
   205  		},
   206  		{
   207  			"good, but val set changes by 2/3 (1/3 of vals is still present)",
   208  			map[int64]*types.SignedHeader{
   209  				// trusted header
   210  				1: h1,
   211  				3: transitKeys.GenSignedHeader(chainID, 3, bTime.Add(2*time.Hour), nil, transitVals, transitVals,
   212  					hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(transitKeys)),
   213  			},
   214  			map[int64]*types.ValidatorSet{
   215  				1: vals,
   216  				2: vals,
   217  				3: transitVals,
   218  			},
   219  			false,
   220  			false,
   221  		},
   222  		{
   223  			"good, but val set changes 100% at height 2",
   224  			map[int64]*types.SignedHeader{
   225  				// trusted header
   226  				1: h1,
   227  				// interim header (3/3 signed)
   228  				2: keys.GenSignedHeader(chainID, 2, bTime.Add(1*time.Hour), nil, vals, newVals,
   229  					hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(keys)),
   230  				// last header (0/4 of the original val set signed)
   231  				3: newKeys.GenSignedHeader(chainID, 3, bTime.Add(2*time.Hour), nil, newVals, newVals,
   232  					hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(newKeys)),
   233  			},
   234  			map[int64]*types.ValidatorSet{
   235  				1: vals,
   236  				2: vals,
   237  				3: newVals,
   238  			},
   239  			false,
   240  			false,
   241  		},
   242  		{
   243  			"bad: last header signed by newVals, interim header has no signers",
   244  			map[int64]*types.SignedHeader{
   245  				// trusted header
   246  				1: h1,
   247  				// last header (0/4 of the original val set signed)
   248  				2: keys.GenSignedHeader(chainID, 2, bTime.Add(1*time.Hour), nil, vals, newVals,
   249  					hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, 0),
   250  				// last header (0/4 of the original val set signed)
   251  				3: newKeys.GenSignedHeader(chainID, 3, bTime.Add(2*time.Hour), nil, newVals, newVals,
   252  					hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(newKeys)),
   253  			},
   254  			map[int64]*types.ValidatorSet{
   255  				1: vals,
   256  				2: vals,
   257  				3: newVals,
   258  			},
   259  			false,
   260  			true,
   261  		},
   262  	}
   263  
   264  	for _, tc := range testCases {
   265  		tc := tc
   266  		t.Run(tc.name, func(t *testing.T) {
   267  			c, err := lite.NewClient(
   268  				chainID,
   269  				trustOptions,
   270  				mockp.New(
   271  					chainID,
   272  					tc.otherHeaders,
   273  					tc.vals,
   274  				),
   275  				[]provider.Provider{mockp.New(
   276  					chainID,
   277  					tc.otherHeaders,
   278  					tc.vals,
   279  				)},
   280  				dbs.New(dbm.NewMemDB(), chainID),
   281  				lite.SkippingVerification(lite.DefaultTrustLevel),
   282  			)
   283  			if tc.initErr {
   284  				require.Error(t, err)
   285  				return
   286  			}
   287  
   288  			require.NoError(t, err)
   289  
   290  			_, err = c.VerifyHeaderAtHeight(3, bTime.Add(3*time.Hour))
   291  			if tc.verifyErr {
   292  				assert.Error(t, err)
   293  			} else {
   294  				assert.NoError(t, err)
   295  			}
   296  		})
   297  	}
   298  
   299  	// start from a large header to make sure that the pivot height doesn't select a height outside
   300  	// the appropriate range
   301  	veryLargeFullNode := mockp.New(GenMockNode(chainID, 100, 3, 1, bTime))
   302  	h1, err := veryLargeFullNode.SignedHeader(90)
   303  	require.NoError(t, err)
   304  	c, err := lite.NewClient(
   305  		chainID,
   306  		lite.TrustOptions{
   307  			Period: 4 * time.Hour,
   308  			Height: 90,
   309  			Hash:   h1.Hash(),
   310  		},
   311  		veryLargeFullNode,
   312  		[]provider.Provider{veryLargeFullNode},
   313  		dbs.New(dbm.NewMemDB(), chainID),
   314  		lite.SkippingVerification(lite.DefaultTrustLevel),
   315  	)
   316  	require.NoError(t, err)
   317  	h, err := c.Update(bTime.Add(100 * time.Minute))
   318  	assert.NoError(t, err)
   319  	h2, err := veryLargeFullNode.SignedHeader(100)
   320  	require.NoError(t, err)
   321  	assert.Equal(t, h, h2)
   322  }
   323  
   324  func TestClient_Cleanup(t *testing.T) {
   325  	c, err := lite.NewClient(
   326  		chainID,
   327  		trustOptions,
   328  		fullNode,
   329  		[]provider.Provider{fullNode},
   330  		dbs.New(dbm.NewMemDB(), chainID),
   331  		lite.Logger(log.TestingLogger()),
   332  	)
   333  	require.NoError(t, err)
   334  	_, err = c.TrustedHeader(1)
   335  	require.NoError(t, err)
   336  
   337  	err = c.Cleanup()
   338  	require.NoError(t, err)
   339  
   340  	// Check no headers/valsets exist after Cleanup.
   341  	var height int64 = 1
   342  	h, err := c.TrustedHeader(height)
   343  	assert.Error(t, err)
   344  	assert.Nil(t, h)
   345  
   346  	valSet, _, err := c.TrustedValidatorSet(height)
   347  	assert.Error(t, err)
   348  	assert.Nil(t, valSet)
   349  }
   350  
   351  // trustedHeader.Height == options.Height
   352  func TestClientRestoresTrustedHeaderAfterStartup1(t *testing.T) {
   353  	// 1. options.Hash == trustedHeader.Hash
   354  	{
   355  		trustedStore := dbs.New(dbm.NewMemDB(), chainID)
   356  		err := trustedStore.SaveSignedHeaderAndValidatorSet(h1, vals)
   357  		require.NoError(t, err)
   358  
   359  		c, err := lite.NewClient(
   360  			chainID,
   361  			trustOptions,
   362  			fullNode,
   363  			[]provider.Provider{fullNode},
   364  			trustedStore,
   365  			lite.Logger(log.TestingLogger()),
   366  		)
   367  		require.NoError(t, err)
   368  
   369  		var height int64 = 1
   370  		h, err := c.TrustedHeader(height)
   371  		assert.NoError(t, err)
   372  		assert.NotNil(t, h)
   373  		assert.Equal(t, h.Hash(), h1.Hash())
   374  
   375  		valSet, _, err := c.TrustedValidatorSet(height)
   376  		assert.NoError(t, err)
   377  		assert.NotNil(t, valSet)
   378  		if assert.NotNil(t, valSet) {
   379  			assert.Equal(t, h.ValidatorsHash.Bytes(), valSet.Hash(height))
   380  		}
   381  	}
   382  
   383  	// 2. options.Hash != trustedHeader.Hash
   384  	{
   385  		trustedStore := dbs.New(dbm.NewMemDB(), chainID)
   386  		err := trustedStore.SaveSignedHeaderAndValidatorSet(h1, vals)
   387  		require.NoError(t, err)
   388  
   389  		// header1 != header
   390  		header1 := keys.GenSignedHeader(chainID, 1, bTime.Add(1*time.Hour), nil, vals, vals,
   391  			hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(keys))
   392  
   393  		primary := mockp.New(
   394  			chainID,
   395  			map[int64]*types.SignedHeader{
   396  				// trusted header
   397  				1: header1,
   398  			},
   399  			valSet,
   400  		)
   401  
   402  		c, err := lite.NewClient(
   403  			chainID,
   404  			lite.TrustOptions{
   405  				Period: 4 * time.Hour,
   406  				Height: 1,
   407  				Hash:   header1.Hash(),
   408  			},
   409  			primary,
   410  			[]provider.Provider{primary},
   411  			trustedStore,
   412  			lite.Logger(log.TestingLogger()),
   413  		)
   414  		require.NoError(t, err)
   415  
   416  		var height int64 = 1
   417  		h, err := c.TrustedHeader(height)
   418  		assert.NoError(t, err)
   419  		if assert.NotNil(t, h) {
   420  			assert.Equal(t, h.Hash(), header1.Hash())
   421  		}
   422  
   423  		valSet, _, err := c.TrustedValidatorSet(height)
   424  		assert.NoError(t, err)
   425  		assert.NotNil(t, valSet)
   426  		if assert.NotNil(t, valSet) {
   427  			assert.Equal(t, h.ValidatorsHash.Bytes(), valSet.Hash(height))
   428  		}
   429  	}
   430  }
   431  
   432  // trustedHeader.Height < options.Height
   433  func TestClientRestoresTrustedHeaderAfterStartup2(t *testing.T) {
   434  	// 1. options.Hash == trustedHeader.Hash
   435  	{
   436  		trustedStore := dbs.New(dbm.NewMemDB(), chainID)
   437  		err := trustedStore.SaveSignedHeaderAndValidatorSet(h1, vals)
   438  		require.NoError(t, err)
   439  
   440  		c, err := lite.NewClient(
   441  			chainID,
   442  			lite.TrustOptions{
   443  				Period: 4 * time.Hour,
   444  				Height: 2,
   445  				Hash:   h2.Hash(),
   446  			},
   447  			fullNode,
   448  			[]provider.Provider{fullNode},
   449  			trustedStore,
   450  			lite.Logger(log.TestingLogger()),
   451  		)
   452  		require.NoError(t, err)
   453  
   454  		// Check we still have the 1st header (+header+).
   455  		var height int64 = 1
   456  		h, err := c.TrustedHeader(height)
   457  		assert.NoError(t, err)
   458  		assert.NotNil(t, h)
   459  		assert.Equal(t, h.Hash(), h1.Hash())
   460  
   461  		valSet, _, err := c.TrustedValidatorSet(height)
   462  		assert.NoError(t, err)
   463  		assert.NotNil(t, valSet)
   464  		if assert.NotNil(t, valSet) {
   465  			assert.Equal(t, h.ValidatorsHash.Bytes(), valSet.Hash(height))
   466  		}
   467  	}
   468  
   469  	// 2. options.Hash != trustedHeader.Hash
   470  	// This could happen if previous provider was lying to us.
   471  	{
   472  		trustedStore := dbs.New(dbm.NewMemDB(), chainID)
   473  		err := trustedStore.SaveSignedHeaderAndValidatorSet(h1, vals)
   474  		require.NoError(t, err)
   475  
   476  		// header1 != header
   477  		diffHeader1 := keys.GenSignedHeader(chainID, 1, bTime.Add(1*time.Hour), nil, vals, vals,
   478  			hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(keys))
   479  
   480  		diffHeader2 := keys.GenSignedHeader(chainID, 2, bTime.Add(2*time.Hour), nil, vals, vals,
   481  			hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(keys))
   482  
   483  		primary := mockp.New(
   484  			chainID,
   485  			map[int64]*types.SignedHeader{
   486  				1: diffHeader1,
   487  				2: diffHeader2,
   488  			},
   489  			valSet,
   490  		)
   491  
   492  		c, err := lite.NewClient(
   493  			chainID,
   494  			lite.TrustOptions{
   495  				Period: 4 * time.Hour,
   496  				Height: 2,
   497  				Hash:   diffHeader2.Hash(),
   498  			},
   499  			primary,
   500  			[]provider.Provider{primary},
   501  			trustedStore,
   502  			lite.Logger(log.TestingLogger()),
   503  		)
   504  		require.NoError(t, err)
   505  
   506  		// Check we no longer have the invalid 1st header (+header+).
   507  		h, err := c.TrustedHeader(1)
   508  		assert.Error(t, err)
   509  		assert.Nil(t, h)
   510  
   511  		valSet, _, err := c.TrustedValidatorSet(1)
   512  		assert.Error(t, err)
   513  		assert.Nil(t, valSet)
   514  	}
   515  }
   516  
   517  // trustedHeader.Height > options.Height
   518  func TestClientRestoresTrustedHeaderAfterStartup3(t *testing.T) {
   519  	// 1. options.Hash == trustedHeader.Hash
   520  	{
   521  		trustedStore := dbs.New(dbm.NewMemDB(), chainID)
   522  		err := trustedStore.SaveSignedHeaderAndValidatorSet(h1, vals)
   523  		require.NoError(t, err)
   524  
   525  		//header2 := keys.GenSignedHeader(chainID, 2, bTime.Add(2*time.Hour), nil, vals, vals,
   526  		//	[]byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys))
   527  		err = trustedStore.SaveSignedHeaderAndValidatorSet(h2, vals)
   528  		require.NoError(t, err)
   529  
   530  		c, err := lite.NewClient(
   531  			chainID,
   532  			trustOptions,
   533  			fullNode,
   534  			[]provider.Provider{fullNode},
   535  			trustedStore,
   536  			lite.Logger(log.TestingLogger()),
   537  		)
   538  		require.NoError(t, err)
   539  
   540  		// Check we still have the 1st header (+header+).
   541  		var height int64 = 1
   542  		h, err := c.TrustedHeader(height)
   543  		assert.NoError(t, err)
   544  		assert.NotNil(t, h)
   545  		assert.Equal(t, h.Hash(), h1.Hash())
   546  
   547  		valSet, _, err := c.TrustedValidatorSet(height)
   548  		assert.NoError(t, err)
   549  		assert.NotNil(t, valSet)
   550  		if assert.NotNil(t, valSet) {
   551  			assert.Equal(t, h.ValidatorsHash.Bytes(), valSet.Hash(height))
   552  		}
   553  
   554  		// Check we no longer have 2nd header (+header2+).
   555  		height = 2
   556  		h, err = c.TrustedHeader(height)
   557  		assert.Error(t, err)
   558  		assert.Nil(t, h)
   559  
   560  		valSet, _, err = c.TrustedValidatorSet(height)
   561  		assert.Error(t, err)
   562  		assert.Nil(t, valSet)
   563  	}
   564  
   565  	// 2. options.Hash != trustedHeader.Hash
   566  	// This could happen if previous provider was lying to us.
   567  	{
   568  		trustedStore := dbs.New(dbm.NewMemDB(), chainID)
   569  		err := trustedStore.SaveSignedHeaderAndValidatorSet(h1, vals)
   570  		require.NoError(t, err)
   571  
   572  		// header1 != header
   573  		header1 := keys.GenSignedHeader(chainID, 1, bTime.Add(1*time.Hour), nil, vals, vals,
   574  			hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(keys))
   575  
   576  		header2 := keys.GenSignedHeader(chainID, 2, bTime.Add(2*time.Hour), nil, vals, vals,
   577  			hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(keys))
   578  		err = trustedStore.SaveSignedHeaderAndValidatorSet(header2, vals)
   579  		require.NoError(t, err)
   580  
   581  		primary := mockp.New(
   582  			chainID,
   583  			map[int64]*types.SignedHeader{
   584  				1: header1,
   585  			},
   586  			valSet,
   587  		)
   588  
   589  		c, err := lite.NewClient(
   590  			chainID,
   591  			lite.TrustOptions{
   592  				Period: 4 * time.Hour,
   593  				Height: 1,
   594  				Hash:   header1.Hash(),
   595  			},
   596  			primary,
   597  			[]provider.Provider{primary},
   598  			trustedStore,
   599  			lite.Logger(log.TestingLogger()),
   600  		)
   601  		require.NoError(t, err)
   602  
   603  		// Check we have swapped invalid 1st header (+header+) with correct one (+header1+).
   604  		var height int64 = 1
   605  		h, err := c.TrustedHeader(height)
   606  		assert.NoError(t, err)
   607  		assert.NotNil(t, h)
   608  		assert.Equal(t, h.Hash(), header1.Hash())
   609  
   610  		valSet, _, err := c.TrustedValidatorSet(height)
   611  		assert.NoError(t, err)
   612  		assert.NotNil(t, valSet)
   613  		if assert.NotNil(t, valSet) {
   614  			assert.Equal(t, h.ValidatorsHash.Bytes(), valSet.Hash(height))
   615  		}
   616  
   617  		// Check we no longer have invalid 2nd header (+header2+).
   618  		height = 2
   619  		h, err = c.TrustedHeader(height)
   620  		assert.Error(t, err)
   621  		assert.Nil(t, h)
   622  
   623  		valSet, _, err = c.TrustedValidatorSet(height)
   624  		assert.Error(t, err)
   625  		assert.Nil(t, valSet)
   626  	}
   627  }
   628  
   629  func TestClient_Update(t *testing.T) {
   630  	c, err := lite.NewClient(
   631  		chainID,
   632  		trustOptions,
   633  		fullNode,
   634  		[]provider.Provider{fullNode},
   635  		dbs.New(dbm.NewMemDB(), chainID),
   636  		lite.Logger(log.TestingLogger()),
   637  	)
   638  	require.NoError(t, err)
   639  
   640  	// should result in downloading & verifying header #3
   641  	h, err := c.Update(bTime.Add(2 * time.Hour))
   642  	assert.NoError(t, err)
   643  	if assert.NotNil(t, h) {
   644  		assert.EqualValues(t, 3, h.Height)
   645  	}
   646  	var height int64 = 3
   647  	valSet, _, err := c.TrustedValidatorSet(height)
   648  	assert.NoError(t, err)
   649  	if assert.NotNil(t, valSet) {
   650  		assert.Equal(t, h.ValidatorsHash.Bytes(), valSet.Hash(height))
   651  	}
   652  }
   653  
   654  func TestClient_Concurrency(t *testing.T) {
   655  	c, err := lite.NewClient(
   656  		chainID,
   657  		trustOptions,
   658  		fullNode,
   659  		[]provider.Provider{fullNode},
   660  		dbs.New(dbm.NewMemDB(), chainID),
   661  		lite.Logger(log.TestingLogger()),
   662  	)
   663  	require.NoError(t, err)
   664  
   665  	_, err = c.VerifyHeaderAtHeight(2, bTime.Add(2*time.Hour))
   666  	require.NoError(t, err)
   667  
   668  	var wg sync.WaitGroup
   669  	for i := 0; i < 100; i++ {
   670  		wg.Add(1)
   671  		go func() {
   672  			defer wg.Done()
   673  
   674  			// NOTE: Cleanup, Stop, VerifyHeaderAtHeight and Verify are not supposed
   675  			// to be concurrenly safe.
   676  
   677  			assert.Equal(t, chainID, c.ChainID())
   678  
   679  			_, err := c.LastTrustedHeight()
   680  			assert.NoError(t, err)
   681  
   682  			_, err = c.FirstTrustedHeight()
   683  			assert.NoError(t, err)
   684  
   685  			h, err := c.TrustedHeader(1)
   686  			assert.NoError(t, err)
   687  			assert.NotNil(t, h)
   688  
   689  			vals, _, err := c.TrustedValidatorSet(2)
   690  			assert.NoError(t, err)
   691  			assert.NotNil(t, vals)
   692  		}()
   693  	}
   694  
   695  	wg.Wait()
   696  }
   697  
   698  func TestClientReplacesPrimaryWithWitnessIfPrimaryIsUnavailable(t *testing.T) {
   699  	c, err := lite.NewClient(
   700  		chainID,
   701  		trustOptions,
   702  		deadNode,
   703  		[]provider.Provider{fullNode, fullNode},
   704  		dbs.New(dbm.NewMemDB(), chainID),
   705  		lite.Logger(log.TestingLogger()),
   706  		lite.MaxRetryAttempts(1),
   707  	)
   708  
   709  	require.NoError(t, err)
   710  	_, err = c.Update(bTime.Add(2 * time.Hour))
   711  	require.NoError(t, err)
   712  
   713  	assert.NotEqual(t, c.Primary(), deadNode)
   714  	assert.Equal(t, 1, len(c.Witnesses()))
   715  }
   716  
   717  func TestClient_BackwardsVerification(t *testing.T) {
   718  	{
   719  		trustHeader, _ := largeFullNode.SignedHeader(6)
   720  		c, err := lite.NewClient(
   721  			chainID,
   722  			lite.TrustOptions{
   723  				Period: 4 * time.Minute,
   724  				Height: trustHeader.Height,
   725  				Hash:   trustHeader.Hash(),
   726  			},
   727  			largeFullNode,
   728  			[]provider.Provider{largeFullNode},
   729  			dbs.New(dbm.NewMemDB(), chainID),
   730  			lite.Logger(log.TestingLogger()),
   731  		)
   732  		require.NoError(t, err)
   733  
   734  		// 1) verify before the trusted header using backwards => expect no error
   735  		h, err := c.VerifyHeaderAtHeight(5, bTime.Add(6*time.Minute))
   736  		require.NoError(t, err)
   737  		if assert.NotNil(t, h) {
   738  			assert.EqualValues(t, 5, h.Height)
   739  		}
   740  
   741  		// 2) untrusted header is expired but trusted header is not => expect no error
   742  		h, err = c.VerifyHeaderAtHeight(3, bTime.Add(8*time.Minute))
   743  		assert.NoError(t, err)
   744  		assert.NotNil(t, h)
   745  
   746  		// 3) already stored headers should return the header without error
   747  		h, err = c.VerifyHeaderAtHeight(5, bTime.Add(6*time.Minute))
   748  		assert.NoError(t, err)
   749  		assert.NotNil(t, h)
   750  
   751  		// 4a) First verify latest header
   752  		_, err = c.VerifyHeaderAtHeight(9, bTime.Add(9*time.Minute))
   753  		require.NoError(t, err)
   754  
   755  		// 4b) Verify backwards using bisection => expect no error
   756  		_, err = c.VerifyHeaderAtHeight(7, bTime.Add(10*time.Minute))
   757  		assert.NoError(t, err)
   758  		// shouldn't have verified this header in the process
   759  		_, err = c.TrustedHeader(8)
   760  		assert.Error(t, err)
   761  
   762  		// 5) trusted header has expired => expect error
   763  		_, err = c.VerifyHeaderAtHeight(1, bTime.Add(20*time.Minute))
   764  		assert.Error(t, err)
   765  
   766  		// 6) Try bisection method, but closest header (at 7) has expired
   767  		// so change to backwards => expect no error
   768  		_, err = c.VerifyHeaderAtHeight(8, bTime.Add(12*time.Minute))
   769  		assert.NoError(t, err)
   770  
   771  	}
   772  	{
   773  		testCases := []struct {
   774  			provider provider.Provider
   775  		}{
   776  			{
   777  				// 7) provides incorrect height
   778  				mockp.New(
   779  					chainID,
   780  					map[int64]*types.SignedHeader{
   781  						1: h1,
   782  						2: keys.GenSignedHeader(chainID, 1, bTime.Add(1*time.Hour), nil, vals, vals,
   783  							hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(keys)),
   784  						3: h3,
   785  					},
   786  					valSet,
   787  				),
   788  			},
   789  			{
   790  				// 8) provides incorrect hash
   791  				mockp.New(
   792  					chainID,
   793  					map[int64]*types.SignedHeader{
   794  						1: h1,
   795  						2: keys.GenSignedHeader(chainID, 2, bTime.Add(30*time.Minute), nil, vals, vals,
   796  							hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(keys)),
   797  						3: h3,
   798  					},
   799  					valSet,
   800  				),
   801  			},
   802  		}
   803  
   804  		for _, tc := range testCases {
   805  			c, err := lite.NewClient(
   806  				chainID,
   807  				lite.TrustOptions{
   808  					Period: 1 * time.Hour,
   809  					Height: 3,
   810  					Hash:   h3.Hash(),
   811  				},
   812  				tc.provider,
   813  				[]provider.Provider{tc.provider},
   814  				dbs.New(dbm.NewMemDB(), chainID),
   815  				lite.Logger(log.TestingLogger()),
   816  			)
   817  			require.NoError(t, err)
   818  
   819  			_, err = c.VerifyHeaderAtHeight(2, bTime.Add(1*time.Hour).Add(1*time.Second))
   820  			assert.Error(t, err)
   821  		}
   822  	}
   823  }
   824  
   825  func TestClient_NewClientFromTrustedStore(t *testing.T) {
   826  	// 1) Initiate DB and fill with a "trusted" header
   827  	db := dbs.New(dbm.NewMemDB(), chainID)
   828  	err := db.SaveSignedHeaderAndValidatorSet(h1, vals)
   829  	require.NoError(t, err)
   830  
   831  	c, err := lite.NewClientFromTrustedStore(
   832  		chainID,
   833  		trustPeriod,
   834  		deadNode,
   835  		[]provider.Provider{deadNode},
   836  		db,
   837  	)
   838  	require.NoError(t, err)
   839  
   840  	// 2) Check header exists (deadNode is being used to ensure we're not getting
   841  	// it from primary)
   842  	h, err := c.TrustedHeader(1)
   843  	assert.NoError(t, err)
   844  	assert.EqualValues(t, 1, h.Height)
   845  	var height int64 = 1
   846  	valSet, _, err := c.TrustedValidatorSet(height)
   847  	assert.NoError(t, err)
   848  	assert.NotNil(t, valSet)
   849  	if assert.NotNil(t, valSet) {
   850  		assert.Equal(t, h.ValidatorsHash.Bytes(), valSet.Hash(height))
   851  	}
   852  }
   853  
   854  func TestNewClientErrorsIfAllWitnessesUnavailable(t *testing.T) {
   855  	_, err := lite.NewClient(
   856  		chainID,
   857  		trustOptions,
   858  		fullNode,
   859  		[]provider.Provider{deadNode, deadNode},
   860  		dbs.New(dbm.NewMemDB(), chainID),
   861  		lite.Logger(log.TestingLogger()),
   862  		lite.MaxRetryAttempts(1),
   863  	)
   864  	if assert.Error(t, err) {
   865  		assert.Contains(t, err.Error(), "awaiting response from all witnesses exceeded dropout time")
   866  	}
   867  }
   868  
   869  func TestClientRemovesWitnessIfItSendsUsIncorrectHeader(t *testing.T) {
   870  	// different headers hash then primary plus less than 1/3 signed (no fork)
   871  	badProvider1 := mockp.New(
   872  		chainID,
   873  		map[int64]*types.SignedHeader{
   874  			1: h1,
   875  			2: keys.GenSignedHeaderLastBlockID(chainID, 2, bTime.Add(30*time.Minute), nil, vals, vals,
   876  				hash("app_hash2"), hash("cons_hash"), hash("results_hash"),
   877  				len(keys), len(keys), types.BlockID{Hash: h1.Hash()}),
   878  		},
   879  		map[int64]*types.ValidatorSet{
   880  			1: vals,
   881  			2: vals,
   882  		},
   883  	)
   884  	// header is empty
   885  	badProvider2 := mockp.New(
   886  		chainID,
   887  		map[int64]*types.SignedHeader{
   888  			1: h1,
   889  			2: h2,
   890  			3: {Header: nil, Commit: nil},
   891  		},
   892  		map[int64]*types.ValidatorSet{
   893  			1: vals,
   894  			2: vals,
   895  		},
   896  	)
   897  
   898  	c, err := lite.NewClient(
   899  		chainID,
   900  		trustOptions,
   901  		fullNode,
   902  		[]provider.Provider{badProvider1, badProvider2},
   903  		dbs.New(dbm.NewMemDB(), chainID),
   904  		lite.Logger(log.TestingLogger()),
   905  		lite.MaxRetryAttempts(1),
   906  	)
   907  	// witness should have behaved properly -> no error
   908  	require.NoError(t, err)
   909  	assert.EqualValues(t, 2, len(c.Witnesses()))
   910  
   911  	// witness behaves incorrectly -> removed from list, no error
   912  	h, err := c.VerifyHeaderAtHeight(2, bTime.Add(2*time.Hour))
   913  	assert.NoError(t, err)
   914  	assert.EqualValues(t, 1, len(c.Witnesses()))
   915  	// header should still be verified
   916  	assert.EqualValues(t, 2, h.Height)
   917  
   918  	// no witnesses left to verify -> error
   919  	_, err = c.VerifyHeaderAtHeight(3, bTime.Add(2*time.Hour))
   920  	assert.Error(t, err)
   921  	assert.EqualValues(t, 0, len(c.Witnesses()))
   922  }
   923  
   924  func TestClientTrustedValidatorSet(t *testing.T) {
   925  	c, err := lite.NewClient(
   926  		chainID,
   927  		trustOptions,
   928  		fullNode,
   929  		[]provider.Provider{fullNode},
   930  		dbs.New(dbm.NewMemDB(), chainID),
   931  		lite.Logger(log.TestingLogger()),
   932  	)
   933  
   934  	require.NoError(t, err)
   935  
   936  	_, err = c.VerifyHeaderAtHeight(2, bTime.Add(2*time.Hour).Add(1*time.Second))
   937  	require.NoError(t, err)
   938  
   939  	valSet, height, err := c.TrustedValidatorSet(0)
   940  	assert.NoError(t, err)
   941  	assert.NotNil(t, valSet)
   942  	assert.EqualValues(t, 2, height)
   943  }