github.com/adoriasoft/tendermint@v0.34.0-dev1.0.20200722151356-96d84601a75a/light/client_test.go (about)

     1  package light_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/tendermint/tm-db"
    12  
    13  	"github.com/tendermint/tendermint/libs/log"
    14  	"github.com/tendermint/tendermint/light"
    15  	"github.com/tendermint/tendermint/light/provider"
    16  	mockp "github.com/tendermint/tendermint/light/provider/mock"
    17  	dbs "github.com/tendermint/tendermint/light/store/db"
    18  	"github.com/tendermint/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 = light.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 TestValidateTrustOptions(t *testing.T) {
    66  	testCases := []struct {
    67  		err bool
    68  		to  light.TrustOptions
    69  	}{
    70  		{
    71  			false,
    72  			trustOptions,
    73  		},
    74  		{
    75  			true,
    76  			light.TrustOptions{
    77  				Period: -1 * time.Hour,
    78  				Height: 1,
    79  				Hash:   h1.Hash(),
    80  			},
    81  		},
    82  		{
    83  			true,
    84  			light.TrustOptions{
    85  				Period: 1 * time.Hour,
    86  				Height: 0,
    87  				Hash:   h1.Hash(),
    88  			},
    89  		},
    90  		{
    91  			true,
    92  			light.TrustOptions{
    93  				Period: 1 * time.Hour,
    94  				Height: 1,
    95  				Hash:   []byte("incorrect hash"),
    96  			},
    97  		},
    98  	}
    99  
   100  	for _, tc := range testCases {
   101  		err := tc.to.ValidateBasic()
   102  		if tc.err {
   103  			assert.Error(t, err)
   104  		} else {
   105  			assert.NoError(t, err)
   106  		}
   107  	}
   108  
   109  }
   110  
   111  func TestClient_SequentialVerification(t *testing.T) {
   112  	newKeys := genPrivKeys(4)
   113  	newVals := newKeys.ToValidators(10, 1)
   114  
   115  	testCases := []struct {
   116  		name         string
   117  		otherHeaders map[int64]*types.SignedHeader // all except ^
   118  		vals         map[int64]*types.ValidatorSet
   119  		initErr      bool
   120  		verifyErr    bool
   121  	}{
   122  		{
   123  			"good",
   124  			headerSet,
   125  			valSet,
   126  			false,
   127  			false,
   128  		},
   129  		{
   130  			"bad: different first header",
   131  			map[int64]*types.SignedHeader{
   132  				// different header
   133  				1: keys.GenSignedHeader(chainID, 1, bTime.Add(1*time.Hour), nil, vals, vals,
   134  					hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(keys)),
   135  			},
   136  			map[int64]*types.ValidatorSet{
   137  				1: vals,
   138  			},
   139  			true,
   140  			false,
   141  		},
   142  		{
   143  			"bad: 1/3 signed interim header",
   144  			map[int64]*types.SignedHeader{
   145  				// trusted header
   146  				1: h1,
   147  				// interim header (1/3 signed)
   148  				2: keys.GenSignedHeader(chainID, 2, bTime.Add(1*time.Hour), nil, vals, vals,
   149  					hash("app_hash"), hash("cons_hash"), hash("results_hash"), len(keys)-1, len(keys)),
   150  				// last header (3/3 signed)
   151  				3: keys.GenSignedHeader(chainID, 3, bTime.Add(2*time.Hour), nil, vals, vals,
   152  					hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(keys)),
   153  			},
   154  			valSet,
   155  			false,
   156  			true,
   157  		},
   158  		{
   159  			"bad: 1/3 signed last header",
   160  			map[int64]*types.SignedHeader{
   161  				// trusted header
   162  				1: h1,
   163  				// interim header (3/3 signed)
   164  				2: keys.GenSignedHeader(chainID, 2, bTime.Add(1*time.Hour), nil, vals, vals,
   165  					hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(keys)),
   166  				// last header (1/3 signed)
   167  				3: keys.GenSignedHeader(chainID, 3, bTime.Add(2*time.Hour), nil, vals, vals,
   168  					hash("app_hash"), hash("cons_hash"), hash("results_hash"), len(keys)-1, len(keys)),
   169  			},
   170  			valSet,
   171  			false,
   172  			true,
   173  		},
   174  		{
   175  			"bad: different validator set at height 3",
   176  			headerSet,
   177  			map[int64]*types.ValidatorSet{
   178  				1: vals,
   179  				2: vals,
   180  				3: newVals,
   181  			},
   182  			false,
   183  			true,
   184  		},
   185  	}
   186  
   187  	for _, tc := range testCases {
   188  		tc := tc
   189  		t.Run(tc.name, func(t *testing.T) {
   190  			c, err := light.NewClient(
   191  				chainID,
   192  				trustOptions,
   193  				mockp.New(
   194  					chainID,
   195  					tc.otherHeaders,
   196  					tc.vals,
   197  				),
   198  				[]provider.Provider{mockp.New(
   199  					chainID,
   200  					tc.otherHeaders,
   201  					tc.vals,
   202  				)},
   203  				dbs.New(dbm.NewMemDB(), chainID),
   204  				light.SequentialVerification(),
   205  				light.Logger(log.TestingLogger()),
   206  			)
   207  
   208  			if tc.initErr {
   209  				require.Error(t, err)
   210  				return
   211  			}
   212  
   213  			require.NoError(t, err)
   214  
   215  			_, err = c.VerifyHeaderAtHeight(3, bTime.Add(3*time.Hour))
   216  			if tc.verifyErr {
   217  				assert.Error(t, err)
   218  			} else {
   219  				assert.NoError(t, err)
   220  			}
   221  		})
   222  	}
   223  }
   224  
   225  func TestClient_SkippingVerification(t *testing.T) {
   226  	// required for 2nd test case
   227  	newKeys := genPrivKeys(4)
   228  	newVals := newKeys.ToValidators(10, 1)
   229  
   230  	// 1/3+ of vals, 2/3- of newVals
   231  	transitKeys := keys.Extend(3)
   232  	transitVals := transitKeys.ToValidators(10, 1)
   233  
   234  	testCases := []struct {
   235  		name         string
   236  		otherHeaders map[int64]*types.SignedHeader // all except ^
   237  		vals         map[int64]*types.ValidatorSet
   238  		initErr      bool
   239  		verifyErr    bool
   240  	}{
   241  		{
   242  			"good",
   243  			map[int64]*types.SignedHeader{
   244  				// trusted header
   245  				1: h1,
   246  				// last header (3/3 signed)
   247  				3: h3,
   248  			},
   249  			valSet,
   250  			false,
   251  			false,
   252  		},
   253  		{
   254  			"good, but val set changes by 2/3 (1/3 of vals is still present)",
   255  			map[int64]*types.SignedHeader{
   256  				// trusted header
   257  				1: h1,
   258  				3: transitKeys.GenSignedHeader(chainID, 3, bTime.Add(2*time.Hour), nil, transitVals, transitVals,
   259  					hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(transitKeys)),
   260  			},
   261  			map[int64]*types.ValidatorSet{
   262  				1: vals,
   263  				2: vals,
   264  				3: transitVals,
   265  			},
   266  			false,
   267  			false,
   268  		},
   269  		{
   270  			"good, but val set changes 100% at height 2",
   271  			map[int64]*types.SignedHeader{
   272  				// trusted header
   273  				1: h1,
   274  				// interim header (3/3 signed)
   275  				2: keys.GenSignedHeader(chainID, 2, bTime.Add(1*time.Hour), nil, vals, newVals,
   276  					hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(keys)),
   277  				// last header (0/4 of the original val set signed)
   278  				3: newKeys.GenSignedHeader(chainID, 3, bTime.Add(2*time.Hour), nil, newVals, newVals,
   279  					hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(newKeys)),
   280  			},
   281  			map[int64]*types.ValidatorSet{
   282  				1: vals,
   283  				2: vals,
   284  				3: newVals,
   285  			},
   286  			false,
   287  			false,
   288  		},
   289  		{
   290  			"bad: last header signed by newVals, interim header has no signers",
   291  			map[int64]*types.SignedHeader{
   292  				// trusted header
   293  				1: h1,
   294  				// last header (0/4 of the original val set signed)
   295  				2: keys.GenSignedHeader(chainID, 2, bTime.Add(1*time.Hour), nil, vals, newVals,
   296  					hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, 0),
   297  				// last header (0/4 of the original val set signed)
   298  				3: newKeys.GenSignedHeader(chainID, 3, bTime.Add(2*time.Hour), nil, newVals, newVals,
   299  					hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(newKeys)),
   300  			},
   301  			map[int64]*types.ValidatorSet{
   302  				1: vals,
   303  				2: vals,
   304  				3: newVals,
   305  			},
   306  			false,
   307  			true,
   308  		},
   309  	}
   310  
   311  	for _, tc := range testCases {
   312  		tc := tc
   313  		t.Run(tc.name, func(t *testing.T) {
   314  			c, err := light.NewClient(
   315  				chainID,
   316  				trustOptions,
   317  				mockp.New(
   318  					chainID,
   319  					tc.otherHeaders,
   320  					tc.vals,
   321  				),
   322  				[]provider.Provider{mockp.New(
   323  					chainID,
   324  					tc.otherHeaders,
   325  					tc.vals,
   326  				)},
   327  				dbs.New(dbm.NewMemDB(), chainID),
   328  				light.SkippingVerification(light.DefaultTrustLevel),
   329  				light.Logger(log.TestingLogger()),
   330  			)
   331  			if tc.initErr {
   332  				require.Error(t, err)
   333  				return
   334  			}
   335  
   336  			require.NoError(t, err)
   337  
   338  			_, err = c.VerifyHeaderAtHeight(3, bTime.Add(3*time.Hour))
   339  			if tc.verifyErr {
   340  				assert.Error(t, err)
   341  			} else {
   342  				assert.NoError(t, err)
   343  			}
   344  		})
   345  	}
   346  
   347  }
   348  
   349  // start from a large header to make sure that the pivot height doesn't select a height outside
   350  // the appropriate range
   351  func TestClientLargeBisectionVerification(t *testing.T) {
   352  	veryLargeFullNode := mockp.New(GenMockNode(chainID, 100, 3, 1, bTime))
   353  	h1, err := veryLargeFullNode.SignedHeader(90)
   354  	require.NoError(t, err)
   355  	c, err := light.NewClient(
   356  		chainID,
   357  		light.TrustOptions{
   358  			Period: 4 * time.Hour,
   359  			Height: 90,
   360  			Hash:   h1.Hash(),
   361  		},
   362  		veryLargeFullNode,
   363  		[]provider.Provider{veryLargeFullNode},
   364  		dbs.New(dbm.NewMemDB(), chainID),
   365  		light.SkippingVerification(light.DefaultTrustLevel),
   366  	)
   367  	require.NoError(t, err)
   368  	h, err := c.Update(bTime.Add(100 * time.Minute))
   369  	assert.NoError(t, err)
   370  	h2, err := veryLargeFullNode.SignedHeader(100)
   371  	require.NoError(t, err)
   372  	assert.Equal(t, h, h2)
   373  }
   374  
   375  func TestClientBisectionBetweenTrustedHeaders(t *testing.T) {
   376  	c, err := light.NewClient(
   377  		chainID,
   378  		light.TrustOptions{
   379  			Period: 4 * time.Hour,
   380  			Height: 1,
   381  			Hash:   h1.Hash(),
   382  		},
   383  		fullNode,
   384  		[]provider.Provider{fullNode},
   385  		dbs.New(dbm.NewMemDB(), chainID),
   386  		light.SkippingVerification(light.DefaultTrustLevel),
   387  	)
   388  	require.NoError(t, err)
   389  
   390  	_, err = c.VerifyHeaderAtHeight(3, bTime.Add(2*time.Hour))
   391  	require.NoError(t, err)
   392  
   393  	// confirm that the client already doesn't have the header
   394  	_, err = c.TrustedHeader(2)
   395  	require.Error(t, err)
   396  
   397  	// verify using bisection the header between the two trusted headers
   398  	_, err = c.VerifyHeaderAtHeight(2, bTime.Add(1*time.Hour))
   399  	assert.NoError(t, err)
   400  }
   401  
   402  func TestClient_Cleanup(t *testing.T) {
   403  	c, err := light.NewClient(
   404  		chainID,
   405  		trustOptions,
   406  		fullNode,
   407  		[]provider.Provider{fullNode},
   408  		dbs.New(dbm.NewMemDB(), chainID),
   409  		light.Logger(log.TestingLogger()),
   410  	)
   411  	require.NoError(t, err)
   412  	_, err = c.TrustedHeader(1)
   413  	require.NoError(t, err)
   414  
   415  	err = c.Cleanup()
   416  	require.NoError(t, err)
   417  
   418  	// Check no headers/valsets exist after Cleanup.
   419  	h, err := c.TrustedHeader(1)
   420  	assert.Error(t, err)
   421  	assert.Nil(t, h)
   422  
   423  	valSet, _, err := c.TrustedValidatorSet(1)
   424  	assert.Error(t, err)
   425  	assert.Nil(t, valSet)
   426  }
   427  
   428  // trustedHeader.Height == options.Height
   429  func TestClientRestoresTrustedHeaderAfterStartup1(t *testing.T) {
   430  	// 1. options.Hash == trustedHeader.Hash
   431  	{
   432  		trustedStore := dbs.New(dbm.NewMemDB(), chainID)
   433  		err := trustedStore.SaveSignedHeaderAndValidatorSet(h1, vals)
   434  		require.NoError(t, err)
   435  
   436  		c, err := light.NewClient(
   437  			chainID,
   438  			trustOptions,
   439  			fullNode,
   440  			[]provider.Provider{fullNode},
   441  			trustedStore,
   442  			light.Logger(log.TestingLogger()),
   443  		)
   444  		require.NoError(t, err)
   445  
   446  		h, err := c.TrustedHeader(1)
   447  		assert.NoError(t, err)
   448  		assert.NotNil(t, h)
   449  		assert.Equal(t, h.Hash(), h1.Hash())
   450  
   451  		valSet, _, err := c.TrustedValidatorSet(1)
   452  		assert.NoError(t, err)
   453  		assert.NotNil(t, valSet)
   454  		if assert.NotNil(t, valSet) {
   455  			assert.Equal(t, h.ValidatorsHash.Bytes(), valSet.Hash())
   456  		}
   457  	}
   458  
   459  	// 2. options.Hash != trustedHeader.Hash
   460  	{
   461  		trustedStore := dbs.New(dbm.NewMemDB(), chainID)
   462  		err := trustedStore.SaveSignedHeaderAndValidatorSet(h1, vals)
   463  		require.NoError(t, err)
   464  
   465  		// header1 != header
   466  		header1 := keys.GenSignedHeader(chainID, 1, bTime.Add(1*time.Hour), nil, vals, vals,
   467  			hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(keys))
   468  
   469  		primary := mockp.New(
   470  			chainID,
   471  			map[int64]*types.SignedHeader{
   472  				// trusted header
   473  				1: header1,
   474  			},
   475  			valSet,
   476  		)
   477  
   478  		c, err := light.NewClient(
   479  			chainID,
   480  			light.TrustOptions{
   481  				Period: 4 * time.Hour,
   482  				Height: 1,
   483  				Hash:   header1.Hash(),
   484  			},
   485  			primary,
   486  			[]provider.Provider{primary},
   487  			trustedStore,
   488  			light.Logger(log.TestingLogger()),
   489  		)
   490  		require.NoError(t, err)
   491  
   492  		h, err := c.TrustedHeader(1)
   493  		assert.NoError(t, err)
   494  		if assert.NotNil(t, h) {
   495  			assert.Equal(t, h.Hash(), header1.Hash())
   496  		}
   497  
   498  		valSet, _, err := c.TrustedValidatorSet(1)
   499  		assert.NoError(t, err)
   500  		assert.NotNil(t, valSet)
   501  		if assert.NotNil(t, valSet) {
   502  			assert.Equal(t, h.ValidatorsHash.Bytes(), valSet.Hash())
   503  		}
   504  	}
   505  }
   506  
   507  // trustedHeader.Height < options.Height
   508  func TestClientRestoresTrustedHeaderAfterStartup2(t *testing.T) {
   509  	// 1. options.Hash == trustedHeader.Hash
   510  	{
   511  		trustedStore := dbs.New(dbm.NewMemDB(), chainID)
   512  		err := trustedStore.SaveSignedHeaderAndValidatorSet(h1, vals)
   513  		require.NoError(t, err)
   514  
   515  		c, err := light.NewClient(
   516  			chainID,
   517  			light.TrustOptions{
   518  				Period: 4 * time.Hour,
   519  				Height: 2,
   520  				Hash:   h2.Hash(),
   521  			},
   522  			fullNode,
   523  			[]provider.Provider{fullNode},
   524  			trustedStore,
   525  			light.Logger(log.TestingLogger()),
   526  		)
   527  		require.NoError(t, err)
   528  
   529  		// Check we still have the 1st header (+header+).
   530  		h, err := c.TrustedHeader(1)
   531  		assert.NoError(t, err)
   532  		assert.NotNil(t, h)
   533  		assert.Equal(t, h.Hash(), h1.Hash())
   534  
   535  		valSet, _, err := c.TrustedValidatorSet(1)
   536  		assert.NoError(t, err)
   537  		assert.NotNil(t, valSet)
   538  		if assert.NotNil(t, valSet) {
   539  			assert.Equal(t, h.ValidatorsHash.Bytes(), valSet.Hash())
   540  		}
   541  	}
   542  
   543  	// 2. options.Hash != trustedHeader.Hash
   544  	// This could happen if previous provider was lying to us.
   545  	{
   546  		trustedStore := dbs.New(dbm.NewMemDB(), chainID)
   547  		err := trustedStore.SaveSignedHeaderAndValidatorSet(h1, vals)
   548  		require.NoError(t, err)
   549  
   550  		// header1 != header
   551  		diffHeader1 := keys.GenSignedHeader(chainID, 1, bTime.Add(1*time.Hour), nil, vals, vals,
   552  			hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(keys))
   553  
   554  		diffHeader2 := keys.GenSignedHeader(chainID, 2, bTime.Add(2*time.Hour), nil, vals, vals,
   555  			hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(keys))
   556  
   557  		primary := mockp.New(
   558  			chainID,
   559  			map[int64]*types.SignedHeader{
   560  				1: diffHeader1,
   561  				2: diffHeader2,
   562  			},
   563  			valSet,
   564  		)
   565  
   566  		c, err := light.NewClient(
   567  			chainID,
   568  			light.TrustOptions{
   569  				Period: 4 * time.Hour,
   570  				Height: 2,
   571  				Hash:   diffHeader2.Hash(),
   572  			},
   573  			primary,
   574  			[]provider.Provider{primary},
   575  			trustedStore,
   576  			light.Logger(log.TestingLogger()),
   577  		)
   578  		require.NoError(t, err)
   579  
   580  		// Check we no longer have the invalid 1st header (+header+).
   581  		h, err := c.TrustedHeader(1)
   582  		assert.Error(t, err)
   583  		assert.Nil(t, h)
   584  
   585  		valSet, _, err := c.TrustedValidatorSet(1)
   586  		assert.Error(t, err)
   587  		assert.Nil(t, valSet)
   588  	}
   589  }
   590  
   591  // trustedHeader.Height > options.Height
   592  func TestClientRestoresTrustedHeaderAfterStartup3(t *testing.T) {
   593  	// 1. options.Hash == trustedHeader.Hash
   594  	{
   595  		// load the first three headers into the trusted store
   596  		trustedStore := dbs.New(dbm.NewMemDB(), chainID)
   597  		err := trustedStore.SaveSignedHeaderAndValidatorSet(h1, vals)
   598  		require.NoError(t, err)
   599  
   600  		err = trustedStore.SaveSignedHeaderAndValidatorSet(h2, vals)
   601  		require.NoError(t, err)
   602  
   603  		c, err := light.NewClient(
   604  			chainID,
   605  			trustOptions,
   606  			fullNode,
   607  			[]provider.Provider{fullNode},
   608  			trustedStore,
   609  			light.Logger(log.TestingLogger()),
   610  		)
   611  		require.NoError(t, err)
   612  
   613  		// Check we still have the 1st header (+header+).
   614  		h, err := c.TrustedHeader(1)
   615  		assert.NoError(t, err)
   616  		assert.NotNil(t, h)
   617  		assert.Equal(t, h.Hash(), h1.Hash())
   618  
   619  		valSet, _, err := c.TrustedValidatorSet(1)
   620  		assert.NoError(t, err)
   621  		assert.NotNil(t, valSet)
   622  		if assert.NotNil(t, valSet) {
   623  			assert.Equal(t, h.ValidatorsHash.Bytes(), valSet.Hash())
   624  		}
   625  
   626  		// Check we no longer have 2nd header (+header2+).
   627  		h, err = c.TrustedHeader(2)
   628  		assert.Error(t, err)
   629  		assert.Nil(t, h)
   630  
   631  		valSet, _, err = c.TrustedValidatorSet(2)
   632  		assert.Error(t, err)
   633  		assert.Nil(t, valSet)
   634  
   635  		h, err = c.TrustedHeader(3)
   636  		assert.Error(t, err)
   637  		assert.Nil(t, h)
   638  	}
   639  
   640  	// 2. options.Hash != trustedHeader.Hash
   641  	// This could happen if previous provider was lying to us.
   642  	{
   643  		trustedStore := dbs.New(dbm.NewMemDB(), chainID)
   644  		err := trustedStore.SaveSignedHeaderAndValidatorSet(h1, vals)
   645  		require.NoError(t, err)
   646  
   647  		// header1 != header
   648  		header1 := keys.GenSignedHeader(chainID, 1, bTime.Add(1*time.Hour), nil, vals, vals,
   649  			hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(keys))
   650  
   651  		header2 := keys.GenSignedHeader(chainID, 2, bTime.Add(2*time.Hour), nil, vals, vals,
   652  			hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(keys))
   653  		err = trustedStore.SaveSignedHeaderAndValidatorSet(header2, vals)
   654  		require.NoError(t, err)
   655  
   656  		primary := mockp.New(
   657  			chainID,
   658  			map[int64]*types.SignedHeader{
   659  				1: header1,
   660  			},
   661  			valSet,
   662  		)
   663  
   664  		c, err := light.NewClient(
   665  			chainID,
   666  			light.TrustOptions{
   667  				Period: 4 * time.Hour,
   668  				Height: 1,
   669  				Hash:   header1.Hash(),
   670  			},
   671  			primary,
   672  			[]provider.Provider{primary},
   673  			trustedStore,
   674  			light.Logger(log.TestingLogger()),
   675  		)
   676  		require.NoError(t, err)
   677  
   678  		// Check we have swapped invalid 1st header (+header+) with correct one (+header1+).
   679  		h, err := c.TrustedHeader(1)
   680  		assert.NoError(t, err)
   681  		assert.NotNil(t, h)
   682  		assert.Equal(t, h.Hash(), header1.Hash())
   683  
   684  		valSet, _, err := c.TrustedValidatorSet(1)
   685  		assert.NoError(t, err)
   686  		assert.NotNil(t, valSet)
   687  		if assert.NotNil(t, valSet) {
   688  			assert.Equal(t, h.ValidatorsHash.Bytes(), valSet.Hash())
   689  		}
   690  
   691  		// Check we no longer have invalid 2nd header (+header2+).
   692  		h, err = c.TrustedHeader(2)
   693  		assert.Error(t, err)
   694  		assert.Nil(t, h)
   695  
   696  		valSet, _, err = c.TrustedValidatorSet(2)
   697  		assert.Error(t, err)
   698  		assert.Nil(t, valSet)
   699  	}
   700  }
   701  
   702  func TestClient_Update(t *testing.T) {
   703  	c, err := light.NewClient(
   704  		chainID,
   705  		trustOptions,
   706  		fullNode,
   707  		[]provider.Provider{fullNode},
   708  		dbs.New(dbm.NewMemDB(), chainID),
   709  		light.Logger(log.TestingLogger()),
   710  	)
   711  	require.NoError(t, err)
   712  
   713  	// should result in downloading & verifying header #3
   714  	h, err := c.Update(bTime.Add(2 * time.Hour))
   715  	assert.NoError(t, err)
   716  	if assert.NotNil(t, h) {
   717  		assert.EqualValues(t, 3, h.Height)
   718  	}
   719  
   720  	valSet, _, err := c.TrustedValidatorSet(3)
   721  	assert.NoError(t, err)
   722  	if assert.NotNil(t, valSet) {
   723  		assert.Equal(t, h.ValidatorsHash.Bytes(), valSet.Hash())
   724  	}
   725  }
   726  
   727  func TestClient_Concurrency(t *testing.T) {
   728  	c, err := light.NewClient(
   729  		chainID,
   730  		trustOptions,
   731  		fullNode,
   732  		[]provider.Provider{fullNode},
   733  		dbs.New(dbm.NewMemDB(), chainID),
   734  		light.Logger(log.TestingLogger()),
   735  	)
   736  	require.NoError(t, err)
   737  
   738  	_, err = c.VerifyHeaderAtHeight(2, bTime.Add(2*time.Hour))
   739  	require.NoError(t, err)
   740  
   741  	var wg sync.WaitGroup
   742  	for i := 0; i < 100; i++ {
   743  		wg.Add(1)
   744  		go func() {
   745  			defer wg.Done()
   746  
   747  			// NOTE: Cleanup, Stop, VerifyHeaderAtHeight and Verify are not supposed
   748  			// to be concurrenly safe.
   749  
   750  			assert.Equal(t, chainID, c.ChainID())
   751  
   752  			_, err := c.LastTrustedHeight()
   753  			assert.NoError(t, err)
   754  
   755  			_, err = c.FirstTrustedHeight()
   756  			assert.NoError(t, err)
   757  
   758  			h, err := c.TrustedHeader(1)
   759  			assert.NoError(t, err)
   760  			assert.NotNil(t, h)
   761  
   762  			vals, _, err := c.TrustedValidatorSet(2)
   763  			assert.NoError(t, err)
   764  			assert.NotNil(t, vals)
   765  		}()
   766  	}
   767  
   768  	wg.Wait()
   769  }
   770  
   771  func TestClientReplacesPrimaryWithWitnessIfPrimaryIsUnavailable(t *testing.T) {
   772  	c, err := light.NewClient(
   773  		chainID,
   774  		trustOptions,
   775  		deadNode,
   776  		[]provider.Provider{fullNode, fullNode},
   777  		dbs.New(dbm.NewMemDB(), chainID),
   778  		light.Logger(log.TestingLogger()),
   779  		light.MaxRetryAttempts(1),
   780  	)
   781  
   782  	require.NoError(t, err)
   783  	_, err = c.Update(bTime.Add(2 * time.Hour))
   784  	require.NoError(t, err)
   785  
   786  	assert.NotEqual(t, c.Primary(), deadNode)
   787  	assert.Equal(t, 1, len(c.Witnesses()))
   788  }
   789  
   790  func TestClient_BackwardsVerification(t *testing.T) {
   791  	{
   792  		trustHeader, _ := largeFullNode.SignedHeader(6)
   793  		c, err := light.NewClient(
   794  			chainID,
   795  			light.TrustOptions{
   796  				Period: 4 * time.Minute,
   797  				Height: trustHeader.Height,
   798  				Hash:   trustHeader.Hash(),
   799  			},
   800  			largeFullNode,
   801  			[]provider.Provider{largeFullNode},
   802  			dbs.New(dbm.NewMemDB(), chainID),
   803  			light.Logger(log.TestingLogger()),
   804  		)
   805  		require.NoError(t, err)
   806  
   807  		// 1) verify before the trusted header using backwards => expect no error
   808  		h, err := c.VerifyHeaderAtHeight(5, bTime.Add(6*time.Minute))
   809  		require.NoError(t, err)
   810  		if assert.NotNil(t, h) {
   811  			assert.EqualValues(t, 5, h.Height)
   812  		}
   813  
   814  		// 2) untrusted header is expired but trusted header is not => expect no error
   815  		h, err = c.VerifyHeaderAtHeight(3, bTime.Add(8*time.Minute))
   816  		assert.NoError(t, err)
   817  		assert.NotNil(t, h)
   818  
   819  		// 3) already stored headers should return the header without error
   820  		h, err = c.VerifyHeaderAtHeight(5, bTime.Add(6*time.Minute))
   821  		assert.NoError(t, err)
   822  		assert.NotNil(t, h)
   823  
   824  		// 4a) First verify latest header
   825  		_, err = c.VerifyHeaderAtHeight(9, bTime.Add(9*time.Minute))
   826  		require.NoError(t, err)
   827  
   828  		// 4b) Verify backwards using bisection => expect no error
   829  		_, err = c.VerifyHeaderAtHeight(7, bTime.Add(10*time.Minute))
   830  		assert.NoError(t, err)
   831  		// shouldn't have verified this header in the process
   832  		_, err = c.TrustedHeader(8)
   833  		assert.Error(t, err)
   834  
   835  		// 5) trusted header has expired => expect error
   836  		_, err = c.VerifyHeaderAtHeight(1, bTime.Add(20*time.Minute))
   837  		assert.Error(t, err)
   838  
   839  		// 6) Try bisection method, but closest header (at 7) has expired
   840  		// so change to backwards => expect no error
   841  		_, err = c.VerifyHeaderAtHeight(8, bTime.Add(12*time.Minute))
   842  		assert.NoError(t, err)
   843  
   844  	}
   845  	{
   846  		testCases := []struct {
   847  			provider provider.Provider
   848  		}{
   849  			{
   850  				// 7) provides incorrect height
   851  				mockp.New(
   852  					chainID,
   853  					map[int64]*types.SignedHeader{
   854  						1: h1,
   855  						2: keys.GenSignedHeader(chainID, 1, bTime.Add(1*time.Hour), nil, vals, vals,
   856  							hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(keys)),
   857  						3: h3,
   858  					},
   859  					valSet,
   860  				),
   861  			},
   862  			{
   863  				// 8) provides incorrect hash
   864  				mockp.New(
   865  					chainID,
   866  					map[int64]*types.SignedHeader{
   867  						1: h1,
   868  						2: keys.GenSignedHeader(chainID, 2, bTime.Add(30*time.Minute), nil, vals, vals,
   869  							hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(keys)),
   870  						3: h3,
   871  					},
   872  					valSet,
   873  				),
   874  			},
   875  		}
   876  
   877  		for _, tc := range testCases {
   878  			c, err := light.NewClient(
   879  				chainID,
   880  				light.TrustOptions{
   881  					Period: 1 * time.Hour,
   882  					Height: 3,
   883  					Hash:   h3.Hash(),
   884  				},
   885  				tc.provider,
   886  				[]provider.Provider{tc.provider},
   887  				dbs.New(dbm.NewMemDB(), chainID),
   888  				light.Logger(log.TestingLogger()),
   889  			)
   890  			require.NoError(t, err)
   891  
   892  			_, err = c.VerifyHeaderAtHeight(2, bTime.Add(1*time.Hour).Add(1*time.Second))
   893  			assert.Error(t, err)
   894  		}
   895  	}
   896  }
   897  
   898  func TestClient_NewClientFromTrustedStore(t *testing.T) {
   899  	// 1) Initiate DB and fill with a "trusted" header
   900  	db := dbs.New(dbm.NewMemDB(), chainID)
   901  	err := db.SaveSignedHeaderAndValidatorSet(h1, vals)
   902  	require.NoError(t, err)
   903  
   904  	c, err := light.NewClientFromTrustedStore(
   905  		chainID,
   906  		trustPeriod,
   907  		deadNode,
   908  		[]provider.Provider{deadNode},
   909  		db,
   910  	)
   911  	require.NoError(t, err)
   912  
   913  	// 2) Check header exists (deadNode is being used to ensure we're not getting
   914  	// it from primary)
   915  	h, err := c.TrustedHeader(1)
   916  	assert.NoError(t, err)
   917  	assert.EqualValues(t, 1, h.Height)
   918  
   919  	valSet, _, err := c.TrustedValidatorSet(1)
   920  	assert.NoError(t, err)
   921  	assert.NotNil(t, valSet)
   922  	if assert.NotNil(t, valSet) {
   923  		assert.Equal(t, h.ValidatorsHash.Bytes(), valSet.Hash())
   924  	}
   925  }
   926  
   927  func TestClientRemovesWitnessIfItSendsUsIncorrectHeader(t *testing.T) {
   928  	// different headers hash then primary plus less than 1/3 signed (no fork)
   929  	badProvider1 := mockp.New(
   930  		chainID,
   931  		map[int64]*types.SignedHeader{
   932  			1: h1,
   933  			2: keys.GenSignedHeaderLastBlockID(chainID, 2, bTime.Add(30*time.Minute), nil, vals, vals,
   934  				hash("app_hash2"), hash("cons_hash"), hash("results_hash"),
   935  				len(keys), len(keys), types.BlockID{Hash: h1.Hash()}),
   936  		},
   937  		map[int64]*types.ValidatorSet{
   938  			1: vals,
   939  			2: vals,
   940  		},
   941  	)
   942  	// header is empty
   943  	badProvider2 := mockp.New(
   944  		chainID,
   945  		map[int64]*types.SignedHeader{
   946  			1: h1,
   947  			2: h2,
   948  			3: {Header: nil, Commit: nil},
   949  		},
   950  		map[int64]*types.ValidatorSet{
   951  			1: vals,
   952  			2: vals,
   953  		},
   954  	)
   955  
   956  	c, err := light.NewClient(
   957  		chainID,
   958  		trustOptions,
   959  		fullNode,
   960  		[]provider.Provider{badProvider1, badProvider2},
   961  		dbs.New(dbm.NewMemDB(), chainID),
   962  		light.Logger(log.TestingLogger()),
   963  		light.MaxRetryAttempts(1),
   964  	)
   965  	// witness should have behaved properly -> no error
   966  	require.NoError(t, err)
   967  	assert.EqualValues(t, 2, len(c.Witnesses()))
   968  
   969  	// witness behaves incorrectly -> removed from list, no error
   970  	h, err := c.VerifyHeaderAtHeight(2, bTime.Add(2*time.Hour))
   971  	assert.NoError(t, err)
   972  	assert.EqualValues(t, 1, len(c.Witnesses()))
   973  	// header should still be verified
   974  	assert.EqualValues(t, 2, h.Height)
   975  
   976  	// remaining witnesses doesn't have header -> error
   977  	_, err = c.VerifyHeaderAtHeight(3, bTime.Add(2*time.Hour))
   978  	if assert.Error(t, err) {
   979  		assert.Equal(t, "awaiting response from all witnesses exceeded dropout time", err.Error())
   980  	}
   981  	// witness does not have a header -> left in the list
   982  	assert.EqualValues(t, 1, len(c.Witnesses()))
   983  }
   984  
   985  func TestClient_TrustedValidatorSet(t *testing.T) {
   986  	noValSetNode := mockp.New(
   987  		chainID,
   988  		headerSet,
   989  		map[int64]*types.ValidatorSet{
   990  			1: nil,
   991  			2: nil,
   992  			3: nil,
   993  		},
   994  	)
   995  
   996  	differentVals, _ := types.RandValidatorSet(10, 100)
   997  
   998  	badValSetNode := mockp.New(
   999  		chainID,
  1000  		map[int64]*types.SignedHeader{
  1001  			1: h1,
  1002  			// 3/3 signed, but validator set at height 2 below is invalid -> witness
  1003  			// should be removed.
  1004  			2: keys.GenSignedHeaderLastBlockID(chainID, 2, bTime.Add(30*time.Minute), nil, vals, vals,
  1005  				hash("app_hash2"), hash("cons_hash"), hash("results_hash"),
  1006  				0, len(keys), types.BlockID{Hash: h1.Hash()}),
  1007  			3: h3,
  1008  		},
  1009  		map[int64]*types.ValidatorSet{
  1010  			1: vals,
  1011  			2: differentVals,
  1012  			3: differentVals,
  1013  		},
  1014  	)
  1015  
  1016  	c, err := light.NewClient(
  1017  		chainID,
  1018  		trustOptions,
  1019  		noValSetNode,
  1020  		[]provider.Provider{fullNode, badValSetNode, fullNode},
  1021  		dbs.New(dbm.NewMemDB(), chainID),
  1022  		light.Logger(log.TestingLogger()),
  1023  	)
  1024  	require.NoError(t, err)
  1025  	assert.Equal(t, 2, len(c.Witnesses()))
  1026  
  1027  	_, err = c.VerifyHeaderAtHeight(2, bTime.Add(2*time.Hour).Add(1*time.Second))
  1028  	assert.NoError(t, err)
  1029  	assert.Equal(t, 1, len(c.Witnesses()))
  1030  
  1031  	valSet, height, err := c.TrustedValidatorSet(0)
  1032  	assert.NoError(t, err)
  1033  	assert.NotNil(t, valSet)
  1034  	assert.EqualValues(t, 2, height)
  1035  }
  1036  
  1037  func TestClientReportsConflictingHeadersEvidence(t *testing.T) {
  1038  	// fullNode2 sends us different header
  1039  	altH2 := keys.GenSignedHeaderLastBlockID(chainID, 2, bTime.Add(30*time.Minute), nil, vals, vals,
  1040  		hash("app_hash2"), hash("cons_hash"), hash("results_hash"),
  1041  		0, len(keys), types.BlockID{Hash: h1.Hash()})
  1042  	fullNode2 := mockp.New(
  1043  		chainID,
  1044  		map[int64]*types.SignedHeader{
  1045  			1: h1,
  1046  			2: altH2,
  1047  		},
  1048  		map[int64]*types.ValidatorSet{
  1049  			1: vals,
  1050  			2: vals,
  1051  		},
  1052  	)
  1053  
  1054  	c, err := light.NewClient(
  1055  		chainID,
  1056  		trustOptions,
  1057  		fullNode,
  1058  		[]provider.Provider{fullNode2},
  1059  		dbs.New(dbm.NewMemDB(), chainID),
  1060  		light.Logger(log.TestingLogger()),
  1061  		light.MaxRetryAttempts(1),
  1062  	)
  1063  	require.NoError(t, err)
  1064  
  1065  	// Check verification returns an error.
  1066  	_, err = c.VerifyHeaderAtHeight(2, bTime.Add(2*time.Hour))
  1067  	if assert.Error(t, err) {
  1068  		assert.Contains(t, err.Error(), "does not match one")
  1069  	}
  1070  
  1071  	// Check evidence was sent to both full nodes.
  1072  	ev := &types.ConflictingHeadersEvidence{H1: h2, H2: altH2}
  1073  	assert.True(t, fullNode2.HasEvidence(ev))
  1074  	assert.True(t, fullNode.HasEvidence(ev))
  1075  }
  1076  
  1077  func TestClientPrunesHeadersAndValidatorSets(t *testing.T) {
  1078  	c, err := light.NewClient(
  1079  		chainID,
  1080  		trustOptions,
  1081  		fullNode,
  1082  		[]provider.Provider{fullNode},
  1083  		dbs.New(dbm.NewMemDB(), chainID),
  1084  		light.Logger(log.TestingLogger()),
  1085  		light.PruningSize(1),
  1086  	)
  1087  	require.NoError(t, err)
  1088  	_, err = c.TrustedHeader(1)
  1089  	require.NoError(t, err)
  1090  
  1091  	h, err := c.Update(bTime.Add(2 * time.Hour))
  1092  	require.NoError(t, err)
  1093  	require.Equal(t, int64(3), h.Height)
  1094  
  1095  	_, err = c.TrustedHeader(1)
  1096  	assert.Error(t, err)
  1097  }
  1098  
  1099  func TestClientEnsureValidHeadersAndValSets(t *testing.T) {
  1100  	emptyValSet := &types.ValidatorSet{
  1101  		Validators: nil,
  1102  		Proposer:   nil,
  1103  	}
  1104  
  1105  	testCases := []struct {
  1106  		headers map[int64]*types.SignedHeader
  1107  		vals    map[int64]*types.ValidatorSet
  1108  		err     bool
  1109  	}{
  1110  		{
  1111  			headerSet,
  1112  			valSet,
  1113  			false,
  1114  		},
  1115  		{
  1116  			headerSet,
  1117  			map[int64]*types.ValidatorSet{
  1118  				1: vals,
  1119  				2: vals,
  1120  				3: nil,
  1121  			},
  1122  			true,
  1123  		},
  1124  		{
  1125  			map[int64]*types.SignedHeader{
  1126  				1: h1,
  1127  				2: h2,
  1128  				3: nil,
  1129  			},
  1130  			valSet,
  1131  			true,
  1132  		},
  1133  		{
  1134  			headerSet,
  1135  			map[int64]*types.ValidatorSet{
  1136  				1: vals,
  1137  				2: vals,
  1138  				3: emptyValSet,
  1139  			},
  1140  			true,
  1141  		},
  1142  	}
  1143  
  1144  	for _, tc := range testCases {
  1145  		badNode := mockp.New(
  1146  			chainID,
  1147  			tc.headers,
  1148  			tc.vals,
  1149  		)
  1150  		c, err := light.NewClient(
  1151  			chainID,
  1152  			trustOptions,
  1153  			badNode,
  1154  			[]provider.Provider{badNode, badNode},
  1155  			dbs.New(dbm.NewMemDB(), chainID),
  1156  			light.MaxRetryAttempts(1),
  1157  		)
  1158  		require.NoError(t, err)
  1159  
  1160  		_, err = c.VerifyHeaderAtHeight(3, bTime.Add(2*time.Hour))
  1161  		if tc.err {
  1162  			assert.Error(t, err)
  1163  		} else {
  1164  			assert.NoError(t, err)
  1165  		}
  1166  	}
  1167  
  1168  }