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