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  }