github.com/DFWallet/tendermint-cosmos@v0.0.2/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/DFWallet/tendermint-cosmos/libs/math" 11 "github.com/DFWallet/tendermint-cosmos/light" 12 "github.com/DFWallet/tendermint-cosmos/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 }