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