@ -476,9 +476,10 @@ func (c *Client) ChainID() string {
// and calls VerifyHeader. It returns header immediately if such exists in
// trustedStore (no verification is needed).
//
// height must be > 0.
//
// It returns provider.ErrSignedHeaderNotFound if header is not found by
// primary.
// It returns ErrOldHeaderExpired if header expired.
func ( c * Client ) VerifyHeaderAtHeight ( height int64 , now time . Time ) ( * types . SignedHeader , error ) {
if height <= 0 {
return nil , errors . New ( "negative or zero height" )
@ -515,7 +516,10 @@ func (c *Client) VerifyHeaderAtHeight(height int64, now time.Time) (*types.Signe
// intermediate headers will be requested. See the specification for details.
// https://github.com/tendermint/spec/blob/master/spec/consensus/light-client.md
//
// It returns ErrOldHeaderExpired if newHeader expired.
// It returns ErrOldHeaderExpired if the latest trusted header expired.
//
// If the primary provides an invalid header (ErrInvalidHeader), it is rejected
// and replaced by another provider until all are exhausted.
//
// If, at any moment, SignedHeader or ValidatorSet are not found by the primary
// provider, provider.ErrSignedHeaderNotFound /
@ -676,7 +680,23 @@ func (c *Client) sequence(
err = VerifyAdjacent ( c . chainID , trustedHeader , interimHeader , interimVals ,
c . trustingPeriod , now )
if err != nil {
return errors . Wrapf ( err , "failed to verify the header #%d" , height )
err = errors . Wrapf ( err , "verify adjacent from #%d to #%d failed" ,
trustedHeader . Height , interimHeader . Height )
switch errors . Cause ( err ) . ( type ) {
case ErrInvalidHeader :
c . logger . Error ( "primary sent invalid header -> replacing" , "err" , err )
replaceErr := c . replacePrimaryProvider ( )
if replaceErr != nil {
c . logger . Error ( "Can't replace primary" , "err" , replaceErr )
return err // return original error
}
// attempt to verify header again
height --
continue
default :
return err
}
}
// 3) Update trustedHeader
@ -729,8 +749,21 @@ func (c *Client) bisection(
return err
}
case ErrInvalidHeader :
c . logger . Error ( "primary sent invalid header -> replacing" , "err" , err )
replaceErr := c . replacePrimaryProvider ( )
if replaceErr != nil {
c . logger . Error ( "Can't replace primary" , "err" , replaceErr )
// return original error
return errors . Wrapf ( err , "verify from #%d to #%d failed" ,
trustedHeader . Height , interimHeader . Height )
}
// attempt to verify the header again
continue
default :
return errors . Wrapf ( err , "failed to verify the header #%d" , newHeader . Height )
return errors . Wrapf ( err , "verify from #%d to #%d failed" ,
trustedHeader . Height , interimHeader . Height )
}
}
}
@ -772,7 +805,9 @@ func (c *Client) fetchHeaderAndValsAtHeight(height int64) (*types.SignedHeader,
return h , vals , nil
}
// Backwards verification (see VerifyHeaderBackwards func in the spec)
// backwards verification (see VerifyHeaderBackwards func in the spec) verifies
// headers before a trusted header. If a sent header is invalid the primary is
// replaced with another provider and the operation is repeated.
func ( c * Client ) backwards (
initiallyTrustedHeader * types . SignedHeader ,
newHeader * types . SignedHeader ,
@ -794,20 +829,14 @@ func (c *Client) backwards(
return errors . Wrapf ( err , "failed to obtain the header at height #%d" , trustedHeader . Height - 1 )
}
if err := interimHeader . ValidateBasic ( c . chainID ) ; err != nil {
return errors . Wrap ( err , "untrustedHeader.ValidateBasic failed" )
}
if ! interimHeader . Time . Before ( trustedHeader . Time ) {
return errors . Errorf ( "expected older header time %v to be before newer header time %v" ,
interimHeader . Time ,
trustedHeader . Time )
}
if ! bytes . Equal ( interimHeader . Hash ( ) , trustedHeader . LastBlockID . Hash ) {
return errors . Errorf ( "older header hash %X does not match trusted header's last block %X" ,
interimHeader . Hash ( ) ,
trustedHeader . LastBlockID . Hash )
if err := VerifyBackwards ( c . chainID , interimHeader , trustedHeader ) ; err != nil {
c . logger . Error ( "primary sent invalid header -> replacing" , "err" , err )
if replaceErr := c . replacePrimaryProvider ( ) ; replaceErr != nil {
c . logger . Error ( "Can't replace primary" , "err" , replaceErr )
// return original error
return errors . Wrapf ( err , "verify backwards from %d to %d failed" ,
trustedHeader . Height , interimHeader . Height )
}
}
trustedHeader = interimHeader