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