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