@ -149,7 +149,7 @@ func NewClient(
options ... Option ) ( * Client , error ) {
if err := trustOptions . ValidateBasic ( ) ; err != nil {
return nil , errors . Wrap ( err , "invalid TrustOptions" )
return nil , fmt . Errorf ( "invalid TrustOptions: %w" , err )
}
c , err := NewClientFromTrustedStore ( chainID , trustOptions . Period , primary , witnesses , trustedStore , options ... )
@ -206,13 +206,13 @@ func NewClientFromTrustedStore(
// Validate the number of witnesses.
if len ( c . witnesses ) < 1 {
return nil , errors . New ( "expected at least one witness" )
return nil , errNoWitnesses { }
}
// Verify witnesses are all on the same chain.
for i , w := range witnesses {
if w . ChainID ( ) != chainID {
return nil , errors . Errorf ( "witness #%d: %v is on another chain %s, expected %s" ,
return nil , fmt . Errorf ( "witness #%d: %v is on another chain %s, expected %s" ,
i , w , w . ChainID ( ) , chainID )
}
}
@ -234,18 +234,18 @@ func NewClientFromTrustedStore(
func ( c * Client ) restoreTrustedHeaderAndVals ( ) error {
lastHeight , err := c . trustedStore . LastSignedHeaderHeight ( )
if err != nil {
return errors . Wrap ( err , "can't get last trusted header height" )
return fmt . Errorf ( "can't get last trusted header height: %w" , err )
}
if lastHeight > 0 {
trustedHeader , err := c . trustedStore . SignedHeader ( lastHeight )
if err != nil {
return errors . Wrap ( err , "can't get last trusted header" )
return fmt . Errorf ( "can't get last trusted header: %w" , err )
}
trustedVals , err := c . trustedStore . ValidatorSet ( lastHeight )
if err != nil {
return errors . Wrap ( err , "can't get last trusted validators" )
return fmt . Errorf ( "can't get last trusted validators: %w" , err )
}
c . latestTrustedHeader = trustedHeader
@ -300,7 +300,7 @@ func (c *Client) checkTrustedHeaderUsingOptions(options TrustOptions) error {
// remove all the headers (options.Height, trustedHeader.Height]
err := c . cleanupAfter ( options . Height )
if err != nil {
return errors . Wrapf ( err , "cleanupAfter(%d) ", options . Height )
return fmt . Errorf ( "cleanupAfter(%d): %w ", options . Height , err )
}
c . logger . Info ( "Rolled back to older header (newer headers were removed)" ,
@ -322,7 +322,7 @@ func (c *Client) checkTrustedHeaderUsingOptions(options TrustOptions) error {
if c . confirmationFn ( action ) {
err := c . Cleanup ( )
if err != nil {
return errors . Wrap ( err , "failed to cleanup" )
return fmt . Errorf ( "failed to cleanup: %w" , err )
}
} else {
return errors . New ( "refused to remove the stored headers despite hashes mismatch" )
@ -350,7 +350,7 @@ func (c *Client) initializeWithTrustOptions(options TrustOptions) error {
}
if ! bytes . Equal ( h . Hash ( ) , options . Hash ) {
return errors . Errorf ( "expected header's hash %X, but got %X" , options . Hash , h . Hash ( ) )
return fmt . Errorf ( "expected header's hash %X, but got %X" , options . Hash , h . Hash ( ) )
}
err = c . compareNewHeaderWithWitnesses ( h )
@ -365,7 +365,7 @@ func (c *Client) initializeWithTrustOptions(options TrustOptions) error {
}
if ! bytes . Equal ( h . ValidatorsHash , vals . Hash ( ) ) {
return errors . Errorf ( "expected header's validators (%X) to match those that were supplied (%X)" ,
return fmt . Errorf ( "expected header's validators (%X) to match those that were supplied (%X)" ,
h . ValidatorsHash ,
vals . Hash ( ) ,
)
@ -374,7 +374,7 @@ func (c *Client) initializeWithTrustOptions(options TrustOptions) error {
// Ensure that +2/3 of validators signed correctly.
err = vals . VerifyCommit ( c . chainID , h . Commit . BlockID , h . Height , h . Commit )
if err != nil {
return errors . Wrap ( err , "invalid commit" )
return fmt . Errorf ( "invalid commit: %w" , err )
}
// 3) Persist both of them and continue.
@ -436,7 +436,7 @@ func (c *Client) TrustedValidatorSet(height int64) (valSet *types.ValidatorSet,
func ( c * Client ) compareWithLatestHeight ( height int64 ) ( int64 , error ) {
latestHeight , err := c . LastTrustedHeight ( )
if err != nil {
return 0 , errors . Wrap ( err , "can't get last trusted height" )
return 0 , fmt . Errorf ( "can't get last trusted height: %w" , err )
}
if latestHeight == - 1 {
return 0 , errors . New ( "no headers exist" )
@ -444,7 +444,7 @@ func (c *Client) compareWithLatestHeight(height int64) (int64, error) {
switch {
case height > latestHeight :
return 0 , errors . Errorf ( "unverified header/valset requested (latest: %d)" , latestHeight )
return 0 , fmt . Errorf ( "unverified header/valset requested (latest: %d)" , latestHeight )
case height == 0 :
return latestHeight , nil
case height < 0 :
@ -499,6 +499,11 @@ func (c *Client) VerifyHeaderAtHeight(height int64, now time.Time) (*types.Signe
// Intermediate headers are not saved to database.
// https://github.com/tendermint/spec/blob/master/spec/consensus/light-client.md
//
// If the header, which is older than the currently trusted header, is
// requested and the light client does not have it, VerifyHeader will perform:
// a) bisection verification if nearest trusted header is found & not expired
// b) backwards verification in all other cases
//
// It returns ErrOldHeaderExpired if the latest trusted header expired.
//
// If the primary provides an invalid header (ErrInvalidHeader), it is rejected
@ -517,7 +522,7 @@ func (c *Client) VerifyHeader(newHeader *types.SignedHeader, newVals *types.Vali
if err == nil {
// Make sure it's the same header.
if ! bytes . Equal ( h . Hash ( ) , newHeader . Hash ( ) ) {
return errors . Errorf ( "existing trusted header %X does not match newHeader %X" , h . Hash ( ) , newHeader . Hash ( ) )
return fmt . Errorf ( "existing trusted header %X does not match newHeader %X" , h . Hash ( ) , newHeader . Hash ( ) )
}
c . logger . Info ( "Header has already been verified" ,
"height" , newHeader . Height , "hash" , hash2str ( newHeader . Hash ( ) ) )
@ -533,7 +538,7 @@ func (c *Client) verifyHeader(newHeader *types.SignedHeader, newVals *types.Vali
var err error
// 1) If going forward, perform either bisection or sequential verification
// 1) If going forward, perform either bisection or sequential verification.
if newHeader . Height >= c . latestTrustedHeader . Height {
switch c . verificationMode {
case sequential :
@ -544,26 +549,55 @@ func (c *Client) verifyHeader(newHeader *types.SignedHeader, newVals *types.Vali
panic ( fmt . Sprintf ( "Unknown verification mode: %b" , c . verificationMode ) )
}
} else {
// 2) Otherwise, perform backwards verification
// Find the closest trusted header after newHeader.Height
var closestHeader * types . SignedHeader
closestHeader , err = c . trustedStore . SignedHeaderAfter ( newHeader . Height )
// 2) If verifying before the first trusted header, perform backwards
// verification.
var (
closestHeader * types . SignedHeader
firstHeaderHeight int64
)
firstHeaderHeight , err = c . FirstTrustedHeight ( )
if err != nil {
return errors . Wrapf ( err , "can't get signed header after height %d" , newHeader . Height )
return fmt . Errorf ( "can't get first header height: %w" , err )
}
if newHeader . Height < firstHeaderHeight {
closestHeader , err = c . TrustedHeader ( firstHeaderHeight )
if err != nil {
return fmt . Errorf ( "can't get first signed header: %w" , err )
}
if HeaderExpired ( closestHeader , c . trustingPeriod , now ) {
closestHeader = c . latestTrustedHeader
}
err = c . backwards ( closestHeader , newHeader , now )
} else {
// 3) OR if between trusted headers where the nearest has not expired,
// perform bisection verification, else backwards.
closestHeader , err = c . trustedStore . SignedHeaderBefore ( newHeader . Height )
if err != nil {
return fmt . Errorf ( "can't get signed header before height %d: %w" , newHeader . Height , err )
}
var closestValidatorSet * types . ValidatorSet
if c . verificationMode == sequential || HeaderExpired ( closestHeader , c . trustingPeriod , now ) {
err = c . backwards ( c . latestTrustedHeader , newHeader , now )
} else {
closestValidatorSet , _ , err = c . TrustedValidatorSet ( closestHeader . Height )
if err != nil {
return fmt . Errorf ( "can't get validator set at height %d: %w" , closestHeader . Height , err )
}
err = c . bisection ( closestHeader , closestValidatorSet , newHeader , newVals , now )
}
}
err = c . backwards ( closestHeader , newHeader , now )
}
if err != nil {
c . logger . Error ( "Can't verify" , "err" , err )
return err
}
// 4) Compare header with other witnesses
if err := c . compareNewHeaderWithWitnesses ( newHeader ) ; err != nil {
c . logger . Error ( "Error when comparing new header with witnesses" , "err" , err )
return err
}
// 5) Once verified, save and return
return c . updateTrustedHeaderAndVals ( newHeader , newVals )
}
@ -590,7 +624,7 @@ func (c *Client) sequence(
} else { // intermediate headers
interimHeader , interimVals , err = c . fetchHeaderAndValsAtHeight ( height )
if err != nil {
return errors . Wrapf ( err , "failed to obtain the header #%d" , height )
return err
}
}
@ -604,10 +638,10 @@ func (c *Client) sequence(
err = VerifyAdjacent ( c . chainID , trustedHeader , interimHeader , interimVals ,
c . trustingPeriod , now )
if err != nil {
err = errors . Wrapf ( err , "verify adjacent from #%d to #%d failed" ,
trustedHeader . Height , interimHeader . Height )
err = fmt . Errorf ( "verify adjacent from #%d to #%d failed: %w " ,
trustedHeader . Height , interimHeader . Height , err )
switch errors . Cause ( err ) . ( type ) {
switch errors . Unwrap ( err ) . ( type ) {
case ErrInvalidHeader :
c . logger . Error ( "primary sent invalid header -> replacing" , "err" , err )
replaceErr := c . replacePrimaryProvider ( )
@ -696,15 +730,15 @@ func (c *Client) bisection(
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 , headerCache [ depth ] . sh . Height )
return fmt . Errorf ( "verify non adjacent from #%d to #%d failed: %w ",
trustedHeader . Height , headerCache [ depth ] . sh . Height , err )
}
// attempt to verify the header again
continue
default :
return errors . Wrapf ( err , "verify from #%d to #%d failed ",
trustedHeader . Height , headerCache [ depth ] . sh . Height )
return fmt . Errorf ( "verify non adjacent from #%d to #%d failed: %w ",
trustedHeader . Height , headerCache [ depth ] . sh . Height , err )
}
}
}
@ -762,14 +796,14 @@ func (c *Client) Cleanup() error {
// cleanupAfter deletes all headers & validator sets after +height+. It also
// resets latestTrustedHeader to the latest header.
func ( c * Client ) cleanupAfter ( height int64 ) error {
nextHeight := h eight
prevHeight := c . latestTrustedHeader . H eight
for {
h , err := c . trustedStore . SignedHeaderAfter ( next Height )
if err == store . ErrSignedHeaderNotFound {
h , err := c . trustedStore . SignedHeaderBefore ( prev Height )
if err == store . ErrSignedHeaderNotFound || ( h != nil && h . Height <= height ) {
break
} else if err != nil {
return errors . Wrapf ( err , "failed to get header after %d" , nextHeight )
return fmt . Errorf ( "failed to get header before %d: %w" , prevHeight , err )
}
err = c . trustedStore . DeleteSignedHeaderAndValidatorSet ( h . Height )
@ -778,7 +812,7 @@ func (c *Client) cleanupAfter(height int64) error {
"height" , h . Height )
}
next Height = h . Height
prev Height = h . Height
}
c . latestTrustedHeader = nil
@ -793,16 +827,16 @@ func (c *Client) cleanupAfter(height int64) error {
func ( c * Client ) updateTrustedHeaderAndVals ( h * types . SignedHeader , vals * types . ValidatorSet ) error {
if ! bytes . Equal ( h . ValidatorsHash , vals . Hash ( ) ) {
return errors . Errorf ( "expected validator's hash %X, but got %X" , h . ValidatorsHash , vals . Hash ( ) )
return fmt . Errorf ( "expected validator's hash %X, but got %X" , h . ValidatorsHash , vals . Hash ( ) )
}
if err := c . trustedStore . SaveSignedHeaderAndValidatorSet ( h , vals ) ; err != nil {
return errors . Wrap ( err , "failed to save trusted header" )
return fmt . Errorf ( "failed to save trusted header: %w" , err )
}
if c . pruningSize > 0 {
if err := c . trustedStore . Prune ( c . pruningSize ) ; err != nil {
return errors . Wrap ( err , "prune" )
return fmt . Errorf ( "prune: %w" , err )
}
}
@ -819,11 +853,11 @@ func (c *Client) updateTrustedHeaderAndVals(h *types.SignedHeader, vals *types.V
func ( c * Client ) fetchHeaderAndValsAtHeight ( height int64 ) ( * types . SignedHeader , * types . ValidatorSet , error ) {
h , err := c . signedHeaderFromPrimary ( height )
if err != nil {
return nil , nil , errors . Wrapf ( err , "failed to obtain the header #%d" , height )
return nil , nil , fmt . Errorf ( "failed to obtain the header #%d: %w" , height , err )
}
vals , err := c . validatorSetFromPrimary ( height )
if err != nil {
return nil , nil , errors . Wrapf ( err , "failed to obtain the vals #%d" , height )
return nil , nil , fmt . Errorf ( "failed to obtain the vals #%d: %w" , height , err )
}
return h , vals , nil
}
@ -837,6 +871,7 @@ func (c *Client) backwards(
now time . Time ) error {
if HeaderExpired ( initiallyTrustedHeader , c . trustingPeriod , now ) {
c . logger . Error ( "Header Expired" )
return ErrOldHeaderExpired { initiallyTrustedHeader . Time . Add ( c . trustingPeriod ) , now }
}
@ -849,16 +884,20 @@ func (c *Client) backwards(
for trustedHeader . Height > newHeader . Height {
interimHeader , err = c . signedHeaderFromPrimary ( trustedHeader . Height - 1 )
if err != nil {
return errors . Wrapf ( err , "failed to obtain the header at height #%d" , trustedHeader . Height - 1 )
return fmt . Errorf ( "failed to obtain the header at height #%d: %w " , trustedHeader . Height - 1 , err )
}
c . logger . Debug ( "Verify newHeader against trustedHeader" ,
"trustedHeight" , trustedHeader . Height ,
"trustedHash" , hash2str ( trustedHeader . Hash ( ) ) ,
"newHeight" , interimHeader . Height ,
"newHash" , hash2str ( interimHeader . 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 )
return fmt . Errorf ( "verify backwards from %d to %d failed: %w " ,
trustedHeader . Height , interimHeader . Height , err )
}
}
@ -883,7 +922,7 @@ func (c *Client) compareNewHeaderWithWitnesses(h *types.SignedHeader) error {
witnessesToRemove := make ( [ ] int , 0 )
for attempt := uint16 ( 1 ) ; attempt <= c . maxRetryAttempts ; attempt ++ {
if len ( c . witnesses ) == 0 {
return errors . New ( "could not find any witnesses. please reset the light client" )
return errNoWitnesses { }
}
for i , witness := range c . witnesses {
@ -909,7 +948,7 @@ func (c *Client) compareNewHeaderWithWitnesses(h *types.SignedHeader) error {
// TODO: send the diverged headers to primary && all witnesses
return errors . Errorf (
return fmt . Errorf (
"header hash %X does not match one %X from the witness %v" ,
h . Hash ( ) , altH . Hash ( ) , witness )
}
@ -952,7 +991,7 @@ func (c *Client) removeWitness(idx int) {
func ( c * Client ) Update ( now time . Time ) ( * types . SignedHeader , error ) {
lastTrustedHeight , err := c . LastTrustedHeight ( )
if err != nil {
return nil , errors . Wrap ( err , "can't get last trusted height" )
return nil , fmt . Errorf ( "can't get last trusted height: %w" , err )
}
if lastTrustedHeight == - 1 {
@ -962,7 +1001,7 @@ func (c *Client) Update(now time.Time) (*types.SignedHeader, error) {
latestHeader , latestVals , err := c . fetchHeaderAndValsAtHeight ( 0 )
if err != nil {
return nil , errors . Wrapf ( err , "can't get latest header and vals" )
return nil , err
}
if latestHeader . Height > lastTrustedHeight {
@ -984,7 +1023,7 @@ func (c *Client) replacePrimaryProvider() error {
defer c . providerMutex . Unlock ( )
if len ( c . witnesses ) <= 1 {
return errors . Errorf ( "only one witness left. please reset the light client" )
return errNoWitnesses { }
}
c . primary = c . witnesses [ 0 ]
c . witnesses = c . witnesses [ 1 : ]
@ -1004,7 +1043,7 @@ func (c *Client) signedHeaderFromPrimary(height int64) (*types.SignedHeader, err
if err == nil {
// sanity check
if height > 0 && h . Height != height {
return nil , errors . Errorf ( "expected %d height, got %d" , height , h . Height )
return nil , fmt . Errorf ( "expected %d height, got %d" , height , h . Height )
}
return h , nil
}