github.com/number571/tendermint@v0.34.11-gost/light/verifier.go (about)

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