mirror of
https://github.com/tendermint/tendermint.git
synced 2026-01-03 11:45:18 +00:00
light: rename lite2 to light & remove lite (#4946)
This PR removes lite & renames lite2 to light throughout the repo Signed-off-by: Marko Baricevic <marbar3778@yahoo.com> Closes: #4944
This commit is contained in:
247
light/verifier.go
Normal file
247
light/verifier.go
Normal file
@@ -0,0 +1,247 @@
|
||||
package light
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
tmmath "github.com/tendermint/tendermint/libs/math"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultTrustLevel - new header can be trusted if at least one correct
|
||||
// validator signed it.
|
||||
DefaultTrustLevel = tmmath.Fraction{Numerator: 1, Denominator: 3}
|
||||
)
|
||||
|
||||
// VerifyNonAdjacent verifies non-adjacent untrustedHeader against
|
||||
// trustedHeader. It ensures that:
|
||||
//
|
||||
// a) trustedHeader can still be trusted (if not, ErrOldHeaderExpired is returned)
|
||||
// b) untrustedHeader is valid (if not, ErrInvalidHeader is returned)
|
||||
// c) trustLevel ([1/3, 1]) of trustedHeaderVals (or trustedHeaderNextVals)
|
||||
// signed correctly (if not, ErrNewValSetCantBeTrusted is returned)
|
||||
// d) more than 2/3 of untrustedVals have signed h2
|
||||
// (otherwise, ErrInvalidHeader is returned)
|
||||
// e) headers are non-adjacent.
|
||||
//
|
||||
// maxClockDrift defines how much untrustedHeader.Time can drift into the
|
||||
// future.
|
||||
func VerifyNonAdjacent(
|
||||
chainID string,
|
||||
trustedHeader *types.SignedHeader, // height=X
|
||||
trustedVals *types.ValidatorSet, // height=X or height=X+1
|
||||
untrustedHeader *types.SignedHeader, // height=Y
|
||||
untrustedVals *types.ValidatorSet, // height=Y
|
||||
trustingPeriod time.Duration,
|
||||
now time.Time,
|
||||
maxClockDrift time.Duration,
|
||||
trustLevel tmmath.Fraction) error {
|
||||
|
||||
if untrustedHeader.Height == trustedHeader.Height+1 {
|
||||
return errors.New("headers must be non adjacent in height")
|
||||
}
|
||||
|
||||
if HeaderExpired(trustedHeader, trustingPeriod, now) {
|
||||
return ErrOldHeaderExpired{trustedHeader.Time.Add(trustingPeriod), now}
|
||||
}
|
||||
|
||||
if err := verifyNewHeaderAndVals(
|
||||
chainID,
|
||||
untrustedHeader, untrustedVals,
|
||||
trustedHeader,
|
||||
now, maxClockDrift); err != nil {
|
||||
return ErrInvalidHeader{err}
|
||||
}
|
||||
|
||||
// Ensure that +`trustLevel` (default 1/3) or more of last trusted validators signed correctly.
|
||||
err := trustedVals.VerifyCommitTrusting(chainID, untrustedHeader.Commit, trustLevel)
|
||||
if err != nil {
|
||||
switch e := err.(type) {
|
||||
case types.ErrNotEnoughVotingPowerSigned:
|
||||
return ErrNewValSetCantBeTrusted{e}
|
||||
default:
|
||||
return e
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that +2/3 of new validators signed correctly.
|
||||
//
|
||||
// NOTE: this should always be the last check because untrustedVals can be
|
||||
// intentionally made very large to DOS the light client. not the case for
|
||||
// VerifyAdjacent, where validator set is known in advance.
|
||||
if err := untrustedVals.VerifyCommit(chainID, untrustedHeader.Commit.BlockID, untrustedHeader.Height,
|
||||
untrustedHeader.Commit); err != nil {
|
||||
return ErrInvalidHeader{err}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// VerifyAdjacent verifies directly adjacent untrustedHeader against
|
||||
// trustedHeader. It ensures that:
|
||||
//
|
||||
// a) trustedHeader can still be trusted (if not, ErrOldHeaderExpired is returned)
|
||||
// b) untrustedHeader is valid (if not, ErrInvalidHeader is returned)
|
||||
// c) untrustedHeader.ValidatorsHash equals trustedHeader.NextValidatorsHash
|
||||
// d) more than 2/3 of new validators (untrustedVals) have signed h2
|
||||
// (otherwise, ErrInvalidHeader is returned)
|
||||
// e) headers are adjacent.
|
||||
//
|
||||
// maxClockDrift defines how much untrustedHeader.Time can drift into the
|
||||
// future.
|
||||
func VerifyAdjacent(
|
||||
chainID string,
|
||||
trustedHeader *types.SignedHeader, // height=X
|
||||
untrustedHeader *types.SignedHeader, // height=X+1
|
||||
untrustedVals *types.ValidatorSet, // height=X+1
|
||||
trustingPeriod time.Duration,
|
||||
now time.Time,
|
||||
maxClockDrift time.Duration) error {
|
||||
|
||||
if untrustedHeader.Height != trustedHeader.Height+1 {
|
||||
return errors.New("headers must be adjacent in height")
|
||||
}
|
||||
|
||||
if HeaderExpired(trustedHeader, trustingPeriod, now) {
|
||||
return ErrOldHeaderExpired{trustedHeader.Time.Add(trustingPeriod), now}
|
||||
}
|
||||
|
||||
if err := verifyNewHeaderAndVals(
|
||||
chainID,
|
||||
untrustedHeader, untrustedVals,
|
||||
trustedHeader,
|
||||
now, maxClockDrift); err != nil {
|
||||
return ErrInvalidHeader{err}
|
||||
}
|
||||
|
||||
// Check the validator hashes are the same
|
||||
if !bytes.Equal(untrustedHeader.ValidatorsHash, trustedHeader.NextValidatorsHash) {
|
||||
err := fmt.Errorf("expected old header next validators (%X) to match those from new header (%X)",
|
||||
trustedHeader.NextValidatorsHash,
|
||||
untrustedHeader.ValidatorsHash,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
// Ensure that +2/3 of new validators signed correctly.
|
||||
if err := untrustedVals.VerifyCommit(chainID, untrustedHeader.Commit.BlockID, untrustedHeader.Height,
|
||||
untrustedHeader.Commit); err != nil {
|
||||
return ErrInvalidHeader{err}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Verify combines both VerifyAdjacent and VerifyNonAdjacent functions.
|
||||
func Verify(
|
||||
chainID string,
|
||||
trustedHeader *types.SignedHeader, // height=X
|
||||
trustedVals *types.ValidatorSet, // height=X or height=X+1
|
||||
untrustedHeader *types.SignedHeader, // height=Y
|
||||
untrustedVals *types.ValidatorSet, // height=Y
|
||||
trustingPeriod time.Duration,
|
||||
now time.Time,
|
||||
maxClockDrift time.Duration,
|
||||
trustLevel tmmath.Fraction) error {
|
||||
|
||||
if untrustedHeader.Height != trustedHeader.Height+1 {
|
||||
return VerifyNonAdjacent(chainID, trustedHeader, trustedVals, untrustedHeader, untrustedVals,
|
||||
trustingPeriod, now, maxClockDrift, trustLevel)
|
||||
}
|
||||
|
||||
return VerifyAdjacent(chainID, trustedHeader, untrustedHeader, untrustedVals, trustingPeriod, now, maxClockDrift)
|
||||
}
|
||||
|
||||
func verifyNewHeaderAndVals(
|
||||
chainID string,
|
||||
untrustedHeader *types.SignedHeader,
|
||||
untrustedVals *types.ValidatorSet,
|
||||
trustedHeader *types.SignedHeader,
|
||||
now time.Time,
|
||||
maxClockDrift time.Duration) error {
|
||||
|
||||
if err := untrustedHeader.ValidateBasic(chainID); err != nil {
|
||||
return fmt.Errorf("untrustedHeader.ValidateBasic failed: %w", err)
|
||||
}
|
||||
|
||||
if untrustedHeader.Height <= trustedHeader.Height {
|
||||
return fmt.Errorf("expected new header height %d to be greater than one of old header %d",
|
||||
untrustedHeader.Height,
|
||||
trustedHeader.Height)
|
||||
}
|
||||
|
||||
if !untrustedHeader.Time.After(trustedHeader.Time) {
|
||||
return fmt.Errorf("expected new header time %v to be after old header time %v",
|
||||
untrustedHeader.Time,
|
||||
trustedHeader.Time)
|
||||
}
|
||||
|
||||
if !untrustedHeader.Time.Before(now.Add(maxClockDrift)) {
|
||||
return fmt.Errorf("new header has a time from the future %v (now: %v; max clock drift: %v)",
|
||||
untrustedHeader.Time,
|
||||
now,
|
||||
maxClockDrift)
|
||||
}
|
||||
|
||||
if !bytes.Equal(untrustedHeader.ValidatorsHash, untrustedVals.Hash()) {
|
||||
return fmt.Errorf("expected new header validators (%X) to match those that were supplied (%X) at height %d",
|
||||
untrustedHeader.ValidatorsHash,
|
||||
untrustedVals.Hash(),
|
||||
untrustedHeader.Height,
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateTrustLevel checks that trustLevel is within the allowed range [1/3,
|
||||
// 1]. If not, it returns an error. 1/3 is the minimum amount of trust needed
|
||||
// which does not break the security model.
|
||||
func ValidateTrustLevel(lvl tmmath.Fraction) error {
|
||||
if lvl.Numerator*3 < lvl.Denominator || // < 1/3
|
||||
lvl.Numerator > lvl.Denominator || // > 1
|
||||
lvl.Denominator == 0 {
|
||||
return fmt.Errorf("trustLevel must be within [1/3, 1], given %v", lvl)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// HeaderExpired return true if the given header expired.
|
||||
func HeaderExpired(h *types.SignedHeader, trustingPeriod time.Duration, now time.Time) bool {
|
||||
expirationTime := h.Time.Add(trustingPeriod)
|
||||
return !expirationTime.After(now)
|
||||
}
|
||||
|
||||
// VerifyBackwards verifies an untrusted header with a height one less than
|
||||
// that of an adjacent trusted header. It ensures that:
|
||||
//
|
||||
// a) untrusted header is valid
|
||||
// b) untrusted header has a time before the trusted header
|
||||
// c) that the LastBlockID hash of the trusted header is the same as the hash
|
||||
// of the trusted header
|
||||
//
|
||||
// For any of these cases ErrInvalidHeader is returned.
|
||||
func VerifyBackwards(chainID string, untrustedHeader, trustedHeader *types.SignedHeader) error {
|
||||
if err := untrustedHeader.ValidateBasic(chainID); err != nil {
|
||||
return ErrInvalidHeader{err}
|
||||
}
|
||||
|
||||
if !untrustedHeader.Time.Before(trustedHeader.Time) {
|
||||
return ErrInvalidHeader{
|
||||
fmt.Errorf("expected older header time %v to be before new header time %v",
|
||||
untrustedHeader.Time,
|
||||
trustedHeader.Time)}
|
||||
}
|
||||
|
||||
if !bytes.Equal(untrustedHeader.Hash(), trustedHeader.LastBlockID.Hash) {
|
||||
return ErrInvalidHeader{
|
||||
fmt.Errorf("older header hash %X does not match trusted header's last block %X",
|
||||
untrustedHeader.Hash(),
|
||||
trustedHeader.LastBlockID.Hash)}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user