github.com/okex/exchain@v1.8.0/libs/tendermint/lite2/verifier.go (about) 1 package lite 2 3 import ( 4 "bytes" 5 "time" 6 7 "github.com/pkg/errors" 8 9 tmmath "github.com/okex/exchain/libs/tendermint/libs/math" 10 "github.com/okex/exchain/libs/tendermint/types" 11 ) 12 13 var ( 14 // DefaultTrustLevel - new header can be trusted if at least one correct 15 // validator signed it. 16 DefaultTrustLevel = tmmath.Fraction{Numerator: 1, Denominator: 3} 17 ) 18 19 // VerifyNonAdjacent verifies non-adjacent untrustedHeader against 20 // trustedHeader. It ensures that: 21 // 22 // a) trustedHeader can still be trusted (if not, ErrOldHeaderExpired is returned) 23 // b) untrustedHeader is valid (if not, ErrInvalidHeader is returned) 24 // c) trustLevel ([1/3, 1]) of trustedHeaderVals (or trustedHeaderNextVals) 25 // signed correctly (if not, ErrNewValSetCantBeTrusted is returned) 26 // d) more than 2/3 of untrustedVals have signed h2 27 // (otherwise, ErrInvalidHeader is returned) 28 // e) headers are non-adjacent. 29 // 30 // maxClockDrift defines how much untrustedHeader.Time can drift into the 31 // future. 32 func VerifyNonAdjacent( 33 chainID string, 34 trustedHeader *types.SignedHeader, // height=X 35 trustedVals *types.ValidatorSet, // height=X or height=X+1 36 untrustedHeader *types.SignedHeader, // height=Y 37 untrustedVals *types.ValidatorSet, // height=Y 38 trustingPeriod time.Duration, 39 now time.Time, 40 maxClockDrift time.Duration, 41 trustLevel tmmath.Fraction) error { 42 43 return commonVerifyNonAdjacent( 44 chainID, trustedHeader, trustedVals, untrustedHeader, 45 untrustedVals, trustingPeriod, now, maxClockDrift, trustLevel, false) 46 } 47 48 func commonVerifyNonAdjacent( 49 chainID string, 50 trustedHeader *types.SignedHeader, // height=X 51 trustedVals *types.ValidatorSet, // height=X or height=X+1 52 untrustedHeader *types.SignedHeader, // height=Y 53 untrustedVals *types.ValidatorSet, // height=Y 54 trustingPeriod time.Duration, 55 now time.Time, 56 maxClockDrift time.Duration, 57 trustLevel tmmath.Fraction, isIbc bool) error { 58 59 if untrustedHeader.Height == trustedHeader.Height+1 { 60 return errors.New("headers must be non adjacent in height") 61 } 62 63 if HeaderExpired(trustedHeader, trustingPeriod, now) { 64 return ErrOldHeaderExpired{trustedHeader.Time.Add(trustingPeriod), now} 65 } 66 67 var err error 68 if err = verifyNewHeaderAndVals( 69 chainID, 70 untrustedHeader, untrustedVals, 71 trustedHeader, 72 now, maxClockDrift, isIbc); err != nil { 73 return ErrInvalidHeader{err} 74 } 75 76 // Ensure that +`trustLevel` (default 1/3) or more of last trusted validators signed correctly. 77 if isIbc { 78 err = trustedVals.IBCVerifyCommitLightTrusting(chainID, untrustedHeader.Commit.BlockID, untrustedHeader.Height, 79 untrustedHeader.Commit, trustLevel) 80 } else { 81 err = trustedVals.VerifyCommitLightTrusting(chainID, untrustedHeader.Commit.BlockID, untrustedHeader.Height, 82 untrustedHeader.Commit, trustLevel) 83 } 84 if err != nil { 85 switch e := err.(type) { 86 case types.ErrNotEnoughVotingPowerSigned: 87 return ErrNewValSetCantBeTrusted{e} 88 default: 89 return e 90 } 91 } 92 93 // Ensure that +2/3 of new validators signed correctly. 94 // 95 // NOTE: this should always be the last check because untrustedVals can be 96 // intentionally made very large to DOS the light client. not the case for 97 // VerifyAdjacent, where validator set is known in advance. 98 if isIbc { 99 err = untrustedVals.IBCVerifyCommitLight(chainID, untrustedHeader.Commit.BlockID, untrustedHeader.Height, 100 untrustedHeader.Commit) 101 } else { 102 err = untrustedVals.VerifyCommitLight(chainID, untrustedHeader.Commit.BlockID, untrustedHeader.Height, 103 untrustedHeader.Commit) 104 } 105 if err != nil { 106 return ErrInvalidHeader{err} 107 } 108 109 return nil 110 } 111 112 // VerifyAdjacent verifies directly adjacent untrustedHeader against 113 // trustedHeader. It ensures that: 114 // 115 // a) trustedHeader can still be trusted (if not, ErrOldHeaderExpired is returned) 116 // b) untrustedHeader is valid (if not, ErrInvalidHeader is returned) 117 // c) untrustedHeader.ValidatorsHash equals trustedHeader.NextValidatorsHash 118 // d) more than 2/3 of new validators (untrustedVals) have signed h2 119 // (otherwise, ErrInvalidHeader is returned) 120 // e) headers are adjacent. 121 // 122 // maxClockDrift defines how much untrustedHeader.Time can drift into the 123 // future. 124 func VerifyAdjacent( 125 chainID string, 126 trustedHeader *types.SignedHeader, // height=X 127 untrustedHeader *types.SignedHeader, // height=X+1 128 untrustedVals *types.ValidatorSet, // height=X+1 129 trustingPeriod time.Duration, 130 now time.Time, 131 maxClockDrift time.Duration) error { 132 133 return commonVerifyAdjacent( 134 chainID, 135 trustedHeader, // height=X 136 untrustedHeader, // height=X+1 137 untrustedVals, // height=X+1 138 trustingPeriod, 139 now, 140 maxClockDrift, false) 141 } 142 143 func commonVerifyAdjacent( 144 chainID string, 145 trustedHeader *types.SignedHeader, // height=X 146 untrustedHeader *types.SignedHeader, // height=X+1 147 untrustedVals *types.ValidatorSet, // height=X+1 148 trustingPeriod time.Duration, 149 now time.Time, 150 maxClockDrift time.Duration, isIbc bool) error { 151 152 if untrustedHeader.Height != trustedHeader.Height+1 { 153 return errors.New("headers must be adjacent in height") 154 } 155 156 if HeaderExpired(trustedHeader, trustingPeriod, now) { 157 return ErrOldHeaderExpired{trustedHeader.Time.Add(trustingPeriod), now} 158 } 159 160 if err := verifyNewHeaderAndVals( 161 chainID, 162 untrustedHeader, untrustedVals, 163 trustedHeader, 164 now, maxClockDrift, isIbc); err != nil { 165 return ErrInvalidHeader{err} 166 } 167 168 // Check the validator hashes are the same 169 if !bytes.Equal(untrustedHeader.ValidatorsHash, trustedHeader.NextValidatorsHash) { 170 err := errors.Errorf("expected old header next validators (%X) to match those from new header (%X)", 171 trustedHeader.NextValidatorsHash, 172 untrustedHeader.ValidatorsHash, 173 ) 174 return err 175 } 176 177 // Ensure that +2/3 of new validators signed correctly. 178 var err error 179 if isIbc { 180 err = untrustedVals.IBCVerifyCommitLight(chainID, untrustedHeader.Commit.BlockID, untrustedHeader.Height, 181 untrustedHeader.Commit) 182 } else { 183 err = untrustedVals.VerifyCommitLight(chainID, untrustedHeader.Commit.BlockID, untrustedHeader.Height, 184 untrustedHeader.Commit) 185 } 186 if err != nil { 187 return ErrInvalidHeader{err} 188 } 189 190 return nil 191 } 192 193 // Verify combines both VerifyAdjacent and VerifyNonAdjacent functions. 194 func Verify( 195 chainID string, 196 trustedHeader *types.SignedHeader, // height=X 197 trustedVals *types.ValidatorSet, // height=X or height=X+1 198 untrustedHeader *types.SignedHeader, // height=Y 199 untrustedVals *types.ValidatorSet, // height=Y 200 trustingPeriod time.Duration, 201 now time.Time, 202 maxClockDrift time.Duration, 203 trustLevel tmmath.Fraction) error { 204 205 if untrustedHeader.Height != trustedHeader.Height+1 { 206 return VerifyNonAdjacent(chainID, trustedHeader, trustedVals, untrustedHeader, untrustedVals, 207 trustingPeriod, now, maxClockDrift, trustLevel) 208 } 209 210 return VerifyAdjacent(chainID, trustedHeader, untrustedHeader, untrustedVals, trustingPeriod, now, maxClockDrift) 211 } 212 213 func verifyNewHeaderAndVals( 214 chainID string, 215 untrustedHeader *types.SignedHeader, 216 untrustedVals *types.ValidatorSet, 217 trustedHeader *types.SignedHeader, 218 now time.Time, 219 maxClockDrift time.Duration, isIbc bool) error { 220 221 var err error 222 if isIbc { 223 err = untrustedHeader.ValidateBasicForIBC(chainID) 224 } else { 225 err = untrustedHeader.ValidateBasic(chainID) 226 } 227 if err != nil { 228 return errors.Wrap(err, "untrustedHeader.ValidateBasic failed") 229 } 230 231 if untrustedHeader.Height <= trustedHeader.Height { 232 return errors.Errorf("expected new header height %d to be greater than one of old header %d", 233 untrustedHeader.Height, 234 trustedHeader.Height) 235 } 236 237 if !untrustedHeader.Time.After(trustedHeader.Time) { 238 return errors.Errorf("expected new header time %v to be after old header time %v", 239 untrustedHeader.Time, 240 trustedHeader.Time) 241 } 242 243 if !untrustedHeader.Time.Before(now.Add(maxClockDrift)) { 244 return errors.Errorf("new header has a time from the future %v (now: %v; max clock drift: %v)", 245 untrustedHeader.Time, 246 now, 247 maxClockDrift) 248 } 249 250 var hash []byte 251 if isIbc { 252 hash = untrustedVals.IBCHash() 253 } else { 254 hash = untrustedVals.Hash(untrustedHeader.Height) 255 } 256 if !bytes.Equal(untrustedHeader.ValidatorsHash, hash) { 257 return errors.Errorf("expected new header validators (%X) to match those that were supplied (%X) at height %d", 258 untrustedHeader.ValidatorsHash, 259 untrustedVals.Hash(untrustedHeader.Height), 260 untrustedHeader.Height, 261 ) 262 } 263 264 return nil 265 } 266 267 // ValidateTrustLevel checks that trustLevel is within the allowed range [1/3, 268 // 1]. If not, it returns an error. 1/3 is the minimum amount of trust needed 269 // which does not break the security model. 270 func ValidateTrustLevel(lvl tmmath.Fraction) error { 271 if lvl.Numerator*3 < lvl.Denominator || // < 1/3 272 lvl.Numerator > lvl.Denominator || // > 1 273 lvl.Denominator == 0 { 274 return errors.Errorf("trustLevel must be within [1/3, 1], given %v", lvl) 275 } 276 return nil 277 } 278 279 // HeaderExpired return true if the given header expired. 280 func HeaderExpired(h *types.SignedHeader, trustingPeriod time.Duration, now time.Time) bool { 281 expirationTime := h.Time.Add(trustingPeriod) 282 return !expirationTime.After(now) 283 } 284 285 // VerifyBackwards verifies an untrusted header with a height one less than 286 // that of an adjacent trusted header. It ensures that: 287 // 288 // a) untrusted header is valid 289 // b) untrusted header has a time before the trusted header 290 // c) that the LastBlockID hash of the trusted header is the same as the hash 291 // of the trusted header 292 // 293 // For any of these cases ErrInvalidHeader is returned. 294 func VerifyBackwards(chainID string, untrustedHeader, trustedHeader *types.SignedHeader) error { 295 if err := untrustedHeader.ValidateBasic(chainID); err != nil { 296 return ErrInvalidHeader{err} 297 } 298 299 if !untrustedHeader.Time.Before(trustedHeader.Time) { 300 return ErrInvalidHeader{ 301 errors.Errorf("expected older header time %v to be before new header time %v", 302 untrustedHeader.Time, 303 trustedHeader.Time)} 304 } 305 306 if !bytes.Equal(untrustedHeader.Hash(), trustedHeader.LastBlockID.Hash) { 307 return ErrInvalidHeader{ 308 errors.Errorf("older header hash %X does not match trusted header's last block %X", 309 untrustedHeader.Hash(), 310 trustedHeader.LastBlockID.Hash)} 311 } 312 313 return nil 314 }