github.com/vipernet-xyz/tm@v0.34.24/light/verifier_test.go (about)

     1  package light_test
     2  
     3  import (
     4  	"fmt"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/stretchr/testify/assert"
     9  
    10  	tmmath "github.com/vipernet-xyz/tm/libs/math"
    11  	"github.com/vipernet-xyz/tm/light"
    12  	"github.com/vipernet-xyz/tm/types"
    13  )
    14  
    15  const (
    16  	maxClockDrift = 10 * time.Second
    17  )
    18  
    19  func TestVerifyAdjacentHeaders(t *testing.T) {
    20  	const (
    21  		chainID    = "TestVerifyAdjacentHeaders"
    22  		lastHeight = 1
    23  		nextHeight = 2
    24  	)
    25  
    26  	var (
    27  		keys = genPrivKeys(4)
    28  		// 20, 30, 40, 50 - the first 3 don't have 2/3, the last 3 do!
    29  		vals     = keys.ToValidators(20, 10)
    30  		bTime, _ = time.Parse(time.RFC3339, "2006-01-02T15:04:05Z")
    31  		header   = keys.GenSignedHeader(chainID, lastHeight, bTime, nil, vals, vals,
    32  			hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(keys))
    33  	)
    34  
    35  	testCases := []struct {
    36  		newHeader      *types.SignedHeader
    37  		newVals        *types.ValidatorSet
    38  		trustingPeriod time.Duration
    39  		now            time.Time
    40  		expErr         error
    41  		expErrText     string
    42  	}{
    43  		// same header -> no error
    44  		0: {
    45  			header,
    46  			vals,
    47  			3 * time.Hour,
    48  			bTime.Add(2 * time.Hour),
    49  			nil,
    50  			"headers must be adjacent in height",
    51  		},
    52  		// different chainID -> error
    53  		1: {
    54  			keys.GenSignedHeader("different-chainID", nextHeight, bTime.Add(1*time.Hour), nil, vals, vals,
    55  				hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(keys)),
    56  			vals,
    57  			3 * time.Hour,
    58  			bTime.Add(2 * time.Hour),
    59  			nil,
    60  			"header belongs to another chain",
    61  		},
    62  		// new header's time is before old header's time -> error
    63  		2: {
    64  			keys.GenSignedHeader(chainID, nextHeight, bTime.Add(-1*time.Hour), nil, vals, vals,
    65  				hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(keys)),
    66  			vals,
    67  			3 * time.Hour,
    68  			bTime.Add(2 * time.Hour),
    69  			nil,
    70  			"to be after old header time",
    71  		},
    72  		// new header's time is from the future -> error
    73  		3: {
    74  			keys.GenSignedHeader(chainID, nextHeight, bTime.Add(3*time.Hour), nil, vals, vals,
    75  				hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(keys)),
    76  			vals,
    77  			3 * time.Hour,
    78  			bTime.Add(2 * time.Hour),
    79  			nil,
    80  			"new header has a time from the future",
    81  		},
    82  		// new header's time is from the future, but it's acceptable (< maxClockDrift) -> no error
    83  		4: {
    84  			keys.GenSignedHeader(chainID, nextHeight,
    85  				bTime.Add(2*time.Hour).Add(maxClockDrift).Add(-1*time.Millisecond), nil, vals, vals,
    86  				hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(keys)),
    87  			vals,
    88  			3 * time.Hour,
    89  			bTime.Add(2 * time.Hour),
    90  			nil,
    91  			"",
    92  		},
    93  		// 3/3 signed -> no error
    94  		5: {
    95  			keys.GenSignedHeader(chainID, nextHeight, bTime.Add(1*time.Hour), nil, vals, vals,
    96  				hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(keys)),
    97  			vals,
    98  			3 * time.Hour,
    99  			bTime.Add(2 * time.Hour),
   100  			nil,
   101  			"",
   102  		},
   103  		// 2/3 signed -> no error
   104  		6: {
   105  			keys.GenSignedHeader(chainID, nextHeight, bTime.Add(1*time.Hour), nil, vals, vals,
   106  				hash("app_hash"), hash("cons_hash"), hash("results_hash"), 1, len(keys)),
   107  			vals,
   108  			3 * time.Hour,
   109  			bTime.Add(2 * time.Hour),
   110  			nil,
   111  			"",
   112  		},
   113  		// 1/3 signed -> error
   114  		7: {
   115  			keys.GenSignedHeader(chainID, nextHeight, bTime.Add(1*time.Hour), nil, vals, vals,
   116  				hash("app_hash"), hash("cons_hash"), hash("results_hash"), len(keys)-1, len(keys)),
   117  			vals,
   118  			3 * time.Hour,
   119  			bTime.Add(2 * time.Hour),
   120  			light.ErrInvalidHeader{Reason: types.ErrNotEnoughVotingPowerSigned{Got: 50, Needed: 93}},
   121  			"",
   122  		},
   123  		// vals does not match with what we have -> error
   124  		8: {
   125  			keys.GenSignedHeader(chainID, nextHeight, bTime.Add(1*time.Hour), nil, keys.ToValidators(10, 1), vals,
   126  				hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(keys)),
   127  			keys.ToValidators(10, 1),
   128  			3 * time.Hour,
   129  			bTime.Add(2 * time.Hour),
   130  			nil,
   131  			"to match those from new header",
   132  		},
   133  		// vals are inconsistent with newHeader -> error
   134  		9: {
   135  			keys.GenSignedHeader(chainID, nextHeight, bTime.Add(1*time.Hour), nil, vals, vals,
   136  				hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(keys)),
   137  			keys.ToValidators(10, 1),
   138  			3 * time.Hour,
   139  			bTime.Add(2 * time.Hour),
   140  			nil,
   141  			"to match those that were supplied",
   142  		},
   143  		// old header has expired -> error
   144  		10: {
   145  			keys.GenSignedHeader(chainID, nextHeight, bTime.Add(1*time.Hour), nil, vals, vals,
   146  				hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(keys)),
   147  			keys.ToValidators(10, 1),
   148  			1 * time.Hour,
   149  			bTime.Add(1 * time.Hour),
   150  			nil,
   151  			"old header has expired",
   152  		},
   153  	}
   154  
   155  	for i, tc := range testCases {
   156  		tc := tc
   157  		t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) {
   158  			err := light.VerifyAdjacent(header, tc.newHeader, tc.newVals, tc.trustingPeriod, tc.now, maxClockDrift)
   159  			switch {
   160  			case tc.expErr != nil && assert.Error(t, err):
   161  				assert.Equal(t, tc.expErr, err)
   162  			case tc.expErrText != "":
   163  				assert.Contains(t, err.Error(), tc.expErrText)
   164  			default:
   165  				assert.NoError(t, err)
   166  			}
   167  		})
   168  	}
   169  
   170  }
   171  
   172  func TestVerifyNonAdjacentHeaders(t *testing.T) {
   173  	const (
   174  		chainID    = "TestVerifyNonAdjacentHeaders"
   175  		lastHeight = 1
   176  	)
   177  
   178  	var (
   179  		keys = genPrivKeys(4)
   180  		// 20, 30, 40, 50 - the first 3 don't have 2/3, the last 3 do!
   181  		vals     = keys.ToValidators(20, 10)
   182  		bTime, _ = time.Parse(time.RFC3339, "2006-01-02T15:04:05Z")
   183  		header   = keys.GenSignedHeader(chainID, lastHeight, bTime, nil, vals, vals,
   184  			hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(keys))
   185  
   186  		// 30, 40, 50
   187  		twoThirds     = keys[1:]
   188  		twoThirdsVals = twoThirds.ToValidators(30, 10)
   189  
   190  		// 50
   191  		oneThird     = keys[len(keys)-1:]
   192  		oneThirdVals = oneThird.ToValidators(50, 10)
   193  
   194  		// 20
   195  		lessThanOneThird     = keys[0:1]
   196  		lessThanOneThirdVals = lessThanOneThird.ToValidators(20, 10)
   197  	)
   198  
   199  	testCases := []struct {
   200  		newHeader      *types.SignedHeader
   201  		newVals        *types.ValidatorSet
   202  		trustingPeriod time.Duration
   203  		now            time.Time
   204  		expErr         error
   205  		expErrText     string
   206  	}{
   207  		// 3/3 new vals signed, 3/3 old vals present -> no error
   208  		0: {
   209  			keys.GenSignedHeader(chainID, 3, bTime.Add(1*time.Hour), nil, vals, vals,
   210  				hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(keys)),
   211  			vals,
   212  			3 * time.Hour,
   213  			bTime.Add(2 * time.Hour),
   214  			nil,
   215  			"",
   216  		},
   217  		// 2/3 new vals signed, 3/3 old vals present -> no error
   218  		1: {
   219  			keys.GenSignedHeader(chainID, 4, bTime.Add(1*time.Hour), nil, vals, vals,
   220  				hash("app_hash"), hash("cons_hash"), hash("results_hash"), 1, len(keys)),
   221  			vals,
   222  			3 * time.Hour,
   223  			bTime.Add(2 * time.Hour),
   224  			nil,
   225  			"",
   226  		},
   227  		// 1/3 new vals signed, 3/3 old vals present -> error
   228  		2: {
   229  			keys.GenSignedHeader(chainID, 5, bTime.Add(1*time.Hour), nil, vals, vals,
   230  				hash("app_hash"), hash("cons_hash"), hash("results_hash"), len(keys)-1, len(keys)),
   231  			vals,
   232  			3 * time.Hour,
   233  			bTime.Add(2 * time.Hour),
   234  			light.ErrInvalidHeader{types.ErrNotEnoughVotingPowerSigned{Got: 50, Needed: 93}},
   235  			"",
   236  		},
   237  		// 3/3 new vals signed, 2/3 old vals present -> no error
   238  		3: {
   239  			twoThirds.GenSignedHeader(chainID, 5, bTime.Add(1*time.Hour), nil, twoThirdsVals, twoThirdsVals,
   240  				hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(twoThirds)),
   241  			twoThirdsVals,
   242  			3 * time.Hour,
   243  			bTime.Add(2 * time.Hour),
   244  			nil,
   245  			"",
   246  		},
   247  		// 3/3 new vals signed, 1/3 old vals present -> no error
   248  		4: {
   249  			oneThird.GenSignedHeader(chainID, 5, bTime.Add(1*time.Hour), nil, oneThirdVals, oneThirdVals,
   250  				hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(oneThird)),
   251  			oneThirdVals,
   252  			3 * time.Hour,
   253  			bTime.Add(2 * time.Hour),
   254  			nil,
   255  			"",
   256  		},
   257  		// 3/3 new vals signed, less than 1/3 old vals present -> error
   258  		5: {
   259  			lessThanOneThird.GenSignedHeader(chainID, 5, bTime.Add(1*time.Hour), nil, lessThanOneThirdVals, lessThanOneThirdVals,
   260  				hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(lessThanOneThird)),
   261  			lessThanOneThirdVals,
   262  			3 * time.Hour,
   263  			bTime.Add(2 * time.Hour),
   264  			light.ErrNewValSetCantBeTrusted{types.ErrNotEnoughVotingPowerSigned{Got: 20, Needed: 46}},
   265  			"",
   266  		},
   267  	}
   268  
   269  	for i, tc := range testCases {
   270  		tc := tc
   271  		t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) {
   272  			err := light.VerifyNonAdjacent(header, vals, tc.newHeader, tc.newVals, tc.trustingPeriod,
   273  				tc.now, maxClockDrift,
   274  				light.DefaultTrustLevel)
   275  
   276  			switch {
   277  			case tc.expErr != nil && assert.Error(t, err):
   278  				assert.Equal(t, tc.expErr, err)
   279  			case tc.expErrText != "":
   280  				assert.Contains(t, err.Error(), tc.expErrText)
   281  			default:
   282  				assert.NoError(t, err)
   283  			}
   284  		})
   285  	}
   286  }
   287  
   288  func TestVerifyReturnsErrorIfTrustLevelIsInvalid(t *testing.T) {
   289  	const (
   290  		chainID    = "TestVerifyReturnsErrorIfTrustLevelIsInvalid"
   291  		lastHeight = 1
   292  	)
   293  
   294  	var (
   295  		keys = genPrivKeys(4)
   296  		// 20, 30, 40, 50 - the first 3 don't have 2/3, the last 3 do!
   297  		vals     = keys.ToValidators(20, 10)
   298  		bTime, _ = time.Parse(time.RFC3339, "2006-01-02T15:04:05Z")
   299  		header   = keys.GenSignedHeader(chainID, lastHeight, bTime, nil, vals, vals,
   300  			hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(keys))
   301  	)
   302  
   303  	err := light.Verify(header, vals, header, vals, 2*time.Hour, time.Now(), maxClockDrift,
   304  		tmmath.Fraction{Numerator: 2, Denominator: 1})
   305  	assert.Error(t, err)
   306  }
   307  
   308  func TestValidateTrustLevel(t *testing.T) {
   309  	testCases := []struct {
   310  		lvl   tmmath.Fraction
   311  		valid bool
   312  	}{
   313  		// valid
   314  		0: {tmmath.Fraction{Numerator: 1, Denominator: 1}, true},
   315  		1: {tmmath.Fraction{Numerator: 1, Denominator: 3}, true},
   316  		2: {tmmath.Fraction{Numerator: 2, Denominator: 3}, true},
   317  		3: {tmmath.Fraction{Numerator: 3, Denominator: 3}, true},
   318  		4: {tmmath.Fraction{Numerator: 4, Denominator: 5}, true},
   319  
   320  		// invalid
   321  		5: {tmmath.Fraction{Numerator: 6, Denominator: 5}, false},
   322  		6: {tmmath.Fraction{Numerator: 0, Denominator: 1}, false},
   323  		7: {tmmath.Fraction{Numerator: 0, Denominator: 0}, false},
   324  		8: {tmmath.Fraction{Numerator: 1, Denominator: 0}, false},
   325  	}
   326  
   327  	for _, tc := range testCases {
   328  		err := light.ValidateTrustLevel(tc.lvl)
   329  		if !tc.valid {
   330  			assert.Error(t, err)
   331  		} else {
   332  			assert.NoError(t, err)
   333  		}
   334  	}
   335  }