github.com/devwanda/aphelion-staking@v0.33.9/lite2/verifier_test.go (about)

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