github.com/number571/tendermint@v0.34.11-gost/light/verifier.go (about) 1 package light 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "time" 8 9 tmmath "github.com/number571/tendermint/libs/math" 10 "github.com/number571/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 // 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 // check if the untrusted header is within the trust period 54 if HeaderExpired(untrustedHeader, trustingPeriod, now) { 55 return ErrOldHeaderExpired{untrustedHeader.Time.Add(trustingPeriod), now} 56 } 57 58 if err := verifyNewHeaderAndVals( 59 untrustedHeader, untrustedVals, 60 trustedHeader, 61 now, maxClockDrift); err != nil { 62 return ErrInvalidHeader{err} 63 } 64 65 // Ensure that +`trustLevel` (default 1/3) or more in voting power of the last trusted validator 66 // set signed correctly. 67 err := trustedVals.VerifyCommitLightTrusting(trustedHeader.ChainID, untrustedHeader.Commit, trustLevel) 68 if err != nil { 69 switch e := err.(type) { 70 case types.ErrNotEnoughVotingPowerSigned: 71 return ErrNewValSetCantBeTrusted{e} 72 default: 73 return ErrInvalidHeader{e} 74 } 75 } 76 77 // Ensure that +2/3 of new validators signed correctly. 78 // 79 // NOTE: this should always be the last check because untrustedVals can be 80 // intentionally made very large to DOS the light client. not the case for 81 // VerifyAdjacent, where validator set is known in advance. 82 if err := untrustedVals.VerifyCommitLight(trustedHeader.ChainID, untrustedHeader.Commit.BlockID, 83 untrustedHeader.Height, untrustedHeader.Commit); err != nil { 84 return ErrInvalidHeader{err} 85 } 86 87 return nil 88 } 89 90 // VerifyAdjacent verifies directly adjacent untrustedHeader against 91 // trustedHeader. It ensures that: 92 // 93 // a) trustedHeader can still be trusted (if not, ErrOldHeaderExpired is returned) 94 // b) untrustedHeader is valid (if not, ErrInvalidHeader is returned) 95 // c) untrustedHeader.ValidatorsHash equals trustedHeader.NextValidatorsHash 96 // d) more than 2/3 of new validators (untrustedVals) have signed h2 97 // (otherwise, ErrInvalidHeader is returned) 98 // e) headers are adjacent. 99 // 100 // maxClockDrift defines how much untrustedHeader.Time can drift into the 101 // future. 102 // trustedHeader must have a ChainID, Height, Time and NextValidatorsHash 103 func VerifyAdjacent( 104 trustedHeader *types.SignedHeader, // height=X 105 untrustedHeader *types.SignedHeader, // height=X+1 106 untrustedVals *types.ValidatorSet, // height=X+1 107 trustingPeriod time.Duration, 108 now time.Time, 109 maxClockDrift time.Duration) error { 110 111 checkRequiredHeaderFields(trustedHeader) 112 113 if len(trustedHeader.NextValidatorsHash) == 0 { 114 panic("next validators hash in trusted header is empty") 115 } 116 117 if untrustedHeader.Height != trustedHeader.Height+1 { 118 return errors.New("headers must be adjacent in height") 119 } 120 121 // check if the untrusted header is within the trust period 122 if HeaderExpired(untrustedHeader, trustingPeriod, now) { 123 return ErrOldHeaderExpired{untrustedHeader.Time.Add(trustingPeriod), now} 124 } 125 126 if err := verifyNewHeaderAndVals( 127 untrustedHeader, untrustedVals, 128 trustedHeader, 129 now, maxClockDrift); err != nil { 130 return ErrInvalidHeader{err} 131 } 132 133 // Check the validator hashes are the same 134 if !bytes.Equal(untrustedHeader.ValidatorsHash, trustedHeader.NextValidatorsHash) { 135 err := fmt.Errorf("expected old header's next validators (%X) to match those from new header (%X)", 136 trustedHeader.NextValidatorsHash, 137 untrustedHeader.ValidatorsHash, 138 ) 139 return ErrInvalidHeader{err} 140 } 141 142 // Ensure that +2/3 of new validators signed correctly. 143 if err := untrustedVals.VerifyCommitLight(trustedHeader.ChainID, untrustedHeader.Commit.BlockID, 144 untrustedHeader.Height, untrustedHeader.Commit); err != nil { 145 return ErrInvalidHeader{err} 146 } 147 148 return nil 149 } 150 151 // Verify combines both VerifyAdjacent and VerifyNonAdjacent functions. 152 func Verify( 153 trustedHeader *types.SignedHeader, // height=X 154 trustedVals *types.ValidatorSet, // height=X or height=X+1 155 untrustedHeader *types.SignedHeader, // height=Y 156 untrustedVals *types.ValidatorSet, // height=Y 157 trustingPeriod time.Duration, 158 now time.Time, 159 maxClockDrift time.Duration, 160 trustLevel tmmath.Fraction) error { 161 162 if untrustedHeader.Height != trustedHeader.Height+1 { 163 return VerifyNonAdjacent(trustedHeader, trustedVals, untrustedHeader, untrustedVals, 164 trustingPeriod, now, maxClockDrift, trustLevel) 165 } 166 167 return VerifyAdjacent(trustedHeader, untrustedHeader, untrustedVals, trustingPeriod, now, maxClockDrift) 168 } 169 170 // ValidateTrustLevel checks that trustLevel is within the allowed range [1/3, 171 // 1]. If not, it returns an error. 1/3 is the minimum amount of trust needed 172 // which does not break the security model. Must be strictly less than 1. 173 func ValidateTrustLevel(lvl tmmath.Fraction) error { 174 if lvl.Numerator*3 < lvl.Denominator || // < 1/3 175 lvl.Numerator >= lvl.Denominator || // >= 1 176 lvl.Denominator == 0 { 177 return fmt.Errorf("trustLevel must be within [1/3, 1], given %v", lvl) 178 } 179 return nil 180 } 181 182 // HeaderExpired return true if the given header expired. 183 func HeaderExpired(h *types.SignedHeader, trustingPeriod time.Duration, now time.Time) bool { 184 expirationTime := h.Time.Add(trustingPeriod) 185 return !expirationTime.After(now) 186 } 187 188 // VerifyBackwards verifies an untrusted header with a height one less than 189 // that of an adjacent trusted header. It ensures that: 190 // 191 // a) untrusted header is valid 192 // b) untrusted header has a time before the trusted header 193 // c) that the LastBlockID hash of the trusted header is the same as the hash 194 // of the trusted header 195 // 196 // For any of these cases ErrInvalidHeader is returned. 197 // NOTE: This does not check whether the trusted or untrusted header has expired 198 // or not. These checks are not necessary because the detector never runs during 199 // backwards verification and thus evidence that needs to be within a certain 200 // time bound is never sent. 201 func VerifyBackwards(untrustedHeader, trustedHeader *types.Header) error { 202 if err := untrustedHeader.ValidateBasic(); err != nil { 203 return ErrInvalidHeader{err} 204 } 205 206 if untrustedHeader.ChainID != trustedHeader.ChainID { 207 return ErrInvalidHeader{fmt.Errorf("new header belongs to a different chain (%s != %s)", 208 untrustedHeader.ChainID, trustedHeader.ChainID)} 209 } 210 211 if !untrustedHeader.Time.Before(trustedHeader.Time) { 212 return ErrInvalidHeader{ 213 fmt.Errorf("expected older header time %v to be before new header time %v", 214 untrustedHeader.Time, 215 trustedHeader.Time)} 216 } 217 218 if !bytes.Equal(untrustedHeader.Hash(), trustedHeader.LastBlockID.Hash) { 219 return ErrInvalidHeader{ 220 fmt.Errorf("older header hash %X does not match trusted header's last block %X", 221 untrustedHeader.Hash(), 222 trustedHeader.LastBlockID.Hash)} 223 } 224 225 return nil 226 } 227 228 // NOTE: This function assumes that untrustedHeader is after trustedHeader. 229 // Do not use for backwards verification. 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 }