github.com/lazyledger/lazyledger-core@v0.35.0-dev.0.20210613111200-4c651f053571/light/verifier.go (about) 1 package light 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "time" 8 9 tmmath "github.com/lazyledger/lazyledger-core/libs/math" 10 "github.com/lazyledger/lazyledger-core/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 // trustedHeader must have a ChainID, Height and Time 33 func VerifyNonAdjacent( 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 checkRequiredHeaderFields(trustedHeader) 44 45 if untrustedHeader.Height == trustedHeader.Height+1 { 46 return errors.New("headers must be non adjacent in height") 47 } 48 49 if err := ValidateTrustLevel(trustLevel); err != nil { 50 return err 51 } 52 53 if HeaderExpired(trustedHeader, trustingPeriod, now) { 54 return ErrOldHeaderExpired{trustedHeader.Time.Add(trustingPeriod), now} 55 } 56 57 if err := verifyNewHeaderAndVals( 58 untrustedHeader, untrustedVals, 59 trustedHeader, 60 now, maxClockDrift); err != nil { 61 return ErrInvalidHeader{err} 62 } 63 64 // Ensure that +`trustLevel` (default 1/3) or more in voting power of the last trusted validator 65 // set signed correctly. 66 err := trustedVals.VerifyCommitLightTrusting(trustedHeader.ChainID, untrustedHeader.Commit, trustLevel) 67 if err != nil { 68 switch e := err.(type) { 69 case types.ErrNotEnoughVotingPowerSigned: 70 return ErrNewValSetCantBeTrusted{e} 71 default: 72 return ErrInvalidHeader{e} 73 } 74 } 75 76 // Ensure that +2/3 of new validators signed correctly. 77 // 78 // NOTE: this should always be the last check because untrustedVals can be 79 // intentionally made very large to DOS the light client. not the case for 80 // VerifyAdjacent, where validator set is known in advance. 81 if err := untrustedVals.VerifyCommitLight(trustedHeader.ChainID, untrustedHeader.Commit.BlockID, 82 untrustedHeader.Height, untrustedHeader.Commit); err != nil { 83 return ErrInvalidHeader{err} 84 } 85 86 return nil 87 } 88 89 // VerifyAdjacent verifies directly adjacent untrustedHeader against 90 // trustedHeader. It ensures that: 91 // 92 // a) trustedHeader can still be trusted (if not, ErrOldHeaderExpired is returned) 93 // b) untrustedHeader is valid (if not, ErrInvalidHeader is returned) 94 // c) untrustedHeader.ValidatorsHash equals trustedHeader.NextValidatorsHash 95 // d) more than 2/3 of new validators (untrustedVals) have signed h2 96 // (otherwise, ErrInvalidHeader is returned) 97 // e) headers are adjacent. 98 // 99 // maxClockDrift defines how much untrustedHeader.Time can drift into the 100 // future. 101 // trustedHeader must have a ChainID, Height, Time and NextValidatorsHash 102 func VerifyAdjacent( 103 trustedHeader *types.SignedHeader, // height=X 104 untrustedHeader *types.SignedHeader, // height=X+1 105 untrustedVals *types.ValidatorSet, // height=X+1 106 trustingPeriod time.Duration, 107 now time.Time, 108 maxClockDrift time.Duration) error { 109 110 checkRequiredHeaderFields(trustedHeader) 111 112 if len(trustedHeader.NextValidatorsHash) == 0 { 113 panic("next validators hash in trusted header is empty") 114 } 115 116 if untrustedHeader.Height != trustedHeader.Height+1 { 117 return errors.New("headers must be adjacent in height") 118 } 119 120 if HeaderExpired(trustedHeader, trustingPeriod, now) { 121 return ErrOldHeaderExpired{trustedHeader.Time.Add(trustingPeriod), now} 122 } 123 124 if err := verifyNewHeaderAndVals( 125 untrustedHeader, untrustedVals, 126 trustedHeader, 127 now, maxClockDrift); err != nil { 128 return ErrInvalidHeader{err} 129 } 130 131 // Check the validator hashes are the same 132 if !bytes.Equal(untrustedHeader.ValidatorsHash, trustedHeader.NextValidatorsHash) { 133 err := fmt.Errorf("expected old header next validators (%X) to match those from new header (%X)", 134 trustedHeader.NextValidatorsHash, 135 untrustedHeader.ValidatorsHash, 136 ) 137 return ErrInvalidHeader{err} 138 } 139 140 // Ensure that +2/3 of new validators signed correctly. 141 if err := untrustedVals.VerifyCommitLight(trustedHeader.ChainID, untrustedHeader.Commit.BlockID, 142 untrustedHeader.Height, untrustedHeader.Commit); err != nil { 143 return ErrInvalidHeader{err} 144 } 145 146 return nil 147 } 148 149 // Verify combines both VerifyAdjacent and VerifyNonAdjacent functions. 150 func Verify( 151 trustedHeader *types.SignedHeader, // height=X 152 trustedVals *types.ValidatorSet, // height=X or height=X+1 153 untrustedHeader *types.SignedHeader, // height=Y 154 untrustedVals *types.ValidatorSet, // height=Y 155 trustingPeriod time.Duration, 156 now time.Time, 157 maxClockDrift time.Duration, 158 trustLevel tmmath.Fraction) error { 159 160 if untrustedHeader.Height != trustedHeader.Height+1 { 161 return VerifyNonAdjacent(trustedHeader, trustedVals, untrustedHeader, untrustedVals, 162 trustingPeriod, now, maxClockDrift, trustLevel) 163 } 164 165 return VerifyAdjacent(trustedHeader, untrustedHeader, untrustedVals, trustingPeriod, now, maxClockDrift) 166 } 167 168 // ValidateTrustLevel checks that trustLevel is within the allowed range [1/3, 169 // 1]. If not, it returns an error. 1/3 is the minimum amount of trust needed 170 // which does not break the security model. 171 func ValidateTrustLevel(lvl tmmath.Fraction) error { 172 if lvl.Numerator*3 < lvl.Denominator || // < 1/3 173 lvl.Numerator > lvl.Denominator || // > 1 174 lvl.Denominator == 0 { 175 return fmt.Errorf("trustLevel must be within [1/3, 1], given %v", lvl) 176 } 177 return nil 178 } 179 180 func ValidateNumSamples(numSamples uint32) error { 181 if numSamples == 0 { 182 return fmt.Errorf("number of samples must be > 0") 183 } 184 return nil 185 } 186 187 // HeaderExpired return true if the given header expired. 188 func HeaderExpired(h *types.SignedHeader, trustingPeriod time.Duration, now time.Time) bool { 189 expirationTime := h.Time.Add(trustingPeriod) 190 return !expirationTime.After(now) 191 } 192 193 // VerifyBackwards verifies an untrusted header with a height one less than 194 // that of an adjacent trusted header. It ensures that: 195 // 196 // a) untrusted header is valid 197 // b) untrusted header has a time before the trusted header 198 // c) that the LastBlockID hash of the trusted header is the same as the hash 199 // of the trusted header 200 // 201 // For any of these cases ErrInvalidHeader is returned. 202 // NOTE: This does not check whether the trusted header has expired or not. 203 func VerifyBackwards(untrustedHeader, trustedHeader *types.Header) error { 204 if err := untrustedHeader.ValidateBasic(); err != nil { 205 return ErrInvalidHeader{err} 206 } 207 208 if untrustedHeader.ChainID != trustedHeader.ChainID { 209 return ErrInvalidHeader{fmt.Errorf("new header belongs to a different chain (%s != %s)", 210 untrustedHeader.ChainID, trustedHeader.ChainID)} 211 } 212 213 if !untrustedHeader.Time.Before(trustedHeader.Time) { 214 return ErrInvalidHeader{ 215 fmt.Errorf("expected older header time %v to be before new header time %v", 216 untrustedHeader.Time, 217 trustedHeader.Time)} 218 } 219 220 if !bytes.Equal(untrustedHeader.Hash(), trustedHeader.LastBlockID.Hash) { 221 return ErrInvalidHeader{ 222 fmt.Errorf("older header hash %X does not match trusted header's last block %X", 223 untrustedHeader.Hash(), 224 trustedHeader.LastBlockID.Hash)} 225 } 226 227 return nil 228 } 229 230 func verifyNewHeaderAndVals( 231 untrustedHeader *types.SignedHeader, 232 untrustedVals *types.ValidatorSet, 233 trustedHeader *types.SignedHeader, 234 now time.Time, 235 maxClockDrift time.Duration) error { 236 237 if err := untrustedHeader.ValidateBasic(trustedHeader.ChainID); err != nil { 238 return fmt.Errorf("untrustedHeader.ValidateBasic failed: %w", err) 239 } 240 241 if untrustedHeader.Height <= trustedHeader.Height { 242 return fmt.Errorf("expected new header height %d to be greater than one of old header %d", 243 untrustedHeader.Height, 244 trustedHeader.Height) 245 } 246 247 if !untrustedHeader.Time.After(trustedHeader.Time) { 248 return fmt.Errorf("expected new header time %v to be after old header time %v", 249 untrustedHeader.Time, 250 trustedHeader.Time) 251 } 252 253 if !untrustedHeader.Time.Before(now.Add(maxClockDrift)) { 254 return fmt.Errorf("new header has a time from the future %v (now: %v; max clock drift: %v)", 255 untrustedHeader.Time, 256 now, 257 maxClockDrift) 258 } 259 260 if !bytes.Equal(untrustedHeader.ValidatorsHash, untrustedVals.Hash()) { 261 return fmt.Errorf("expected new header validators (%X) to match those that were supplied (%X) at height %d", 262 untrustedHeader.ValidatorsHash, 263 untrustedVals.Hash(), 264 untrustedHeader.Height, 265 ) 266 } 267 268 return nil 269 } 270 271 func checkRequiredHeaderFields(h *types.SignedHeader) { 272 if h.Height == 0 { 273 panic("height in trusted header must be set (non zero") 274 } 275 276 zeroTime := time.Time{} 277 if h.Time == zeroTime { 278 panic("time in trusted header must be set") 279 } 280 281 if h.ChainID == "" { 282 panic("chain ID in trusted header must be set") 283 } 284 }