@ -121,9 +121,9 @@ type Client struct {
// Where trusted headers are stored.
trustedStore store . Store
// Highest trusted header from the store (height=H).
trustedHeader * types . SignedHeader
la testT rustedHeader * types . SignedHeader
// Highest next validator set from the store (height=H+1).
trustedNextVals * types . ValidatorSet
la testT rustedNextVals * types . ValidatorSet
// See UpdatePeriod option
updatePeriod time . Duration
@ -164,13 +164,13 @@ func NewClient(
return nil , err
}
if c . trustedHeader != nil {
if c . la testT rustedHeader != nil {
if err := c . checkTrustedHeaderUsingOptions ( trustOptions ) ; err != nil {
return nil , err
}
}
if c . trustedHeader == nil || c . trustedHeader . Height < trustOptions . Height {
if c . la testT rustedHeader == nil || c . la testT rustedHeader. Height < trustOptions . Height {
if err := c . initializeWithTrustOptions ( trustOptions ) ; err != nil {
return nil , err
}
@ -253,8 +253,8 @@ func (c *Client) restoreTrustedHeaderAndNextVals() error {
return errors . Wrap ( err , "can't get last trusted next validators" )
}
c . trustedHeader = trustedHeader
c . trustedNextVals = trustedNextVals
c . la testT rustedHeader = trustedHeader
c . la testT rustedNextVals = trustedNextVals
c . logger . Debug ( "Restored trusted header and next vals" , lastHeight )
}
@ -283,24 +283,24 @@ func (c *Client) restoreTrustedHeaderAndNextVals() error {
func ( c * Client ) checkTrustedHeaderUsingOptions ( options TrustOptions ) error {
var primaryHash [ ] byte
switch {
case options . Height > c . trustedHeader . Height :
h , err := c . signedHeaderFromPrimary ( c . trustedHeader . Height )
case options . Height > c . la testT rustedHeader. Height :
h , err := c . signedHeaderFromPrimary ( c . la testT rustedHeader. Height )
if err != nil {
return err
}
primaryHash = h . Hash ( )
case options . Height == c . trustedHeader . Height :
case options . Height == c . la testT rustedHeader. Height :
primaryHash = options . Hash
case options . Height < c . trustedHeader . Height :
case options . Height < c . la testT rustedHeader. Height :
c . logger . Info ( "Client initialized with old header (trusted is more recent)" ,
"old" , options . Height ,
"trustedHeight" , c . trustedHeader . Height ,
"trustedHash" , hash2str ( c . trustedHeader . Hash ( ) ) )
"trustedHeight" , c . la testT rustedHeader. Height ,
"trustedHash" , hash2str ( c . la testT rustedHeader. Hash ( ) ) )
action := fmt . Sprintf (
"Rollback to %d (%X)? Note this will remove newer headers up to %d (%X)" ,
options . Height , options . Hash ,
c . trustedHeader . Height , c . trustedHeader . Hash ( ) )
c . la testT rustedHeader. Height , c . la testT rustedHeader. Hash ( ) )
if c . confirmationFn ( action ) {
// remove all the headers (options.Height, trustedHeader.Height]
c . cleanup ( options . Height + 1 )
@ -314,13 +314,13 @@ func (c *Client) checkTrustedHeaderUsingOptions(options TrustOptions) error {
primaryHash = options . Hash
}
if ! bytes . Equal ( primaryHash , c . trustedHeader . Hash ( ) ) {
if ! bytes . Equal ( primaryHash , c . la testT rustedHeader. Hash ( ) ) {
c . logger . Info ( "Prev. trusted header's hash (h1) doesn't match hash from primary provider (h2)" ,
"h1" , c . trustedHeader . Hash ( ) , "h1 ", primaryHash )
"h1" , hash2str ( c . la testT rustedHeader. Hash ( ) ) , "h2 ", hash2str ( primaryHash ) )
action := fmt . Sprintf (
"Prev. trusted header's hash %X doesn't match hash %X from primary provider. Remove all the stored headers?" ,
c . trustedHeader . Hash ( ) , primaryHash )
c . la testT rustedHeader. Hash ( ) , primaryHash )
if c . confirmationFn ( action ) {
err := c . Cleanup ( )
if err != nil {
@ -449,14 +449,7 @@ func (c *Client) TrustedHeader(height int64, now time.Time) (*types.SignedHeader
// 2) Get header from store.
h , err := c . trustedStore . SignedHeader ( height )
switch {
case errors . Is ( err , store . ErrSignedHeaderNotFound ) :
// 2.1) If not found, try to fetch header from primary.
h , err = c . fetchMissingTrustedHeader ( height , now )
if err != nil {
return nil , err
}
case err != nil :
if err != nil {
return nil , err
}
@ -523,15 +516,25 @@ func (c *Client) ChainID() string {
return c . chainID
}
// VerifyHeaderAtHeight fetches the header and validators at the given height
// and calls VerifyHeader.
// VerifyHeaderAtHeight fetches header and validators at the given height
// and calls VerifyHeader. It returns header immediately if such exists in
// trustedStore (no verification is needed).
//
// If the trusted header is more recent than one here, an error is returned.
// If the header is not found by the primary provider,
// provider.ErrSignedHeaderNotFound error is return ed.
// It returns provider.ErrSignedHeaderNotFound if header is not found by
// primary.
// It returns ErrOldHeaderExpired if header expir ed.
func ( c * Client ) VerifyHeaderAtHeight ( height int64 , now time . Time ) ( * types . SignedHeader , error ) {
if c . trustedHeader . Height >= height {
return nil , errors . Errorf ( "header at more recent height #%d exists" , c . trustedHeader . Height )
if height <= 0 {
return nil , errors . New ( "negative or zero height" )
}
h , err := c . TrustedHeader ( height , now )
switch err . ( type ) {
case nil : // Return already trusted header
c . logger . Info ( "Header has already been verified" , "height" , height , "hash" , hash2str ( h . Hash ( ) ) )
return h , nil
case ErrOldHeaderExpired :
return nil , err
}
// Request the header and the vals.
@ -540,10 +543,12 @@ func (c *Client) VerifyHeaderAtHeight(height int64, now time.Time) (*types.Signe
return nil , err
}
return newHeader , c . V erifyHeader( newHeader , newVals , now )
return newHeader , c . v erifyHeader( newHeader , newVals , now )
}
// VerifyHeader verifies new header against the trusted state.
// VerifyHeader verifies new header against the trusted state. It returns
// immediately if newHeader exists in trustedStore (no verification is
// needed).
//
// SequentialVerification: verifies that 2/3 of the trusted validator set has
// signed the new header. If the headers are not adjacent, **all** intermediate
@ -555,7 +560,7 @@ 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
//
// If the trusted header is more recent than one here, an error is return ed.
// It returns ErrOldHeaderExpired if newHeader expir ed.
//
// If, at any moment, SignedHeader or ValidatorSet are not found by the primary
// provider, provider.ErrSignedHeaderNotFound /
@ -565,21 +570,49 @@ func (c *Client) VerifyHeaderAtHeight(height int64, now time.Time) (*types.Signe
// validator set at height newHeader.Height+1 (i.e.
// newHeader.NextValidatorsHash).
func ( c * Client ) VerifyHeader ( newHeader * types . SignedHeader , newVals * types . ValidatorSet , now time . Time ) error {
h , err := c . TrustedHeader ( newHeader . Height , now )
switch err . ( type ) {
case nil : // Return already trusted header
// 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 ( ) )
}
c . logger . Info ( "Header has already been verified" ,
"height" , newHeader . Height , "hash" , hash2str ( newHeader . Hash ( ) ) )
return nil
case ErrOldHeaderExpired :
return err
}
return c . verifyHeader ( newHeader , newVals , now )
}
func ( c * Client ) verifyHeader ( newHeader * types . SignedHeader , newVals * types . ValidatorSet , now time . Time ) error {
c . logger . Info ( "VerifyHeader" , "height" , newHeader . Height , "hash" , hash2str ( newHeader . Hash ( ) ) ,
"vals" , hash2str ( newVals . Hash ( ) ) )
if c . trustedHeader . Height >= newHeader . Height {
return errors . Errorf ( "header at more recent height #%d exists" , c . trustedHeader . Height )
}
var err error
switch c . verificationMode {
case sequential :
err = c . sequence ( c . trustedHeader , c . trustedNextVals , newHeader , newVals , now )
case skipping :
err = c . bisection ( c . trustedHeader , c . trustedNextVals , newHeader , newVals , now )
default :
panic ( fmt . Sprintf ( "Unknown verification mode: %b" , c . verificationMode ) )
// 1) If going forward, perform either bisection or sequential verification
if newHeader . Height >= c . latestTrustedHeader . Height {
switch c . verificationMode {
case sequential :
err = c . sequence ( c . latestTrustedHeader , c . latestTrustedNextVals , newHeader , newVals , now )
case skipping :
err = c . bisection ( c . latestTrustedHeader , c . latestTrustedNextVals , newHeader , newVals , now )
default :
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 )
if err != nil {
return errors . Wrapf ( err , "can't get signed header after height %d" , newHeader . Height )
}
err = c . backwards ( closestHeader , newHeader , now )
}
if err != nil {
c . logger . Error ( "Can't verify" , "err" , err )
@ -596,6 +629,7 @@ func (c *Client) VerifyHeader(newHeader *types.SignedHeader, newVals *types.Vali
if err != nil {
return err
}
return c . updateTrustedHeaderAndNextVals ( newHeader , nextVals )
}
@ -652,8 +686,8 @@ func (c *Client) cleanup(stopHeight int64) error {
}
}
c . trustedHeader = nil
c . trustedNextVals = nil
c . la testT rustedHeader = nil
c . la testT rustedNextVals = nil
err = c . restoreTrustedHeaderAndNextVals ( )
if err != nil {
return err
@ -682,8 +716,8 @@ func (c *Client) sequence(
}
c . logger . Debug ( "Verify newHeader against trustedHeader" ,
"trustedHeight" , c . trustedHeader . Height ,
"trustedHash" , hash2str ( c . trustedHeader . Hash ( ) ) ,
"trustedHeight" , c . la testT rustedHeader. Height ,
"trustedHash" , hash2str ( c . la testT rustedHeader. Hash ( ) ) ,
"newHeight" , interimHeader . Height ,
"newHash" , hash2str ( interimHeader . Hash ( ) ) )
err = VerifyAdjacent ( c . chainID , trustedHeader , interimHeader , trustedNextVals ,
@ -709,7 +743,7 @@ func (c *Client) sequence(
}
// 2) Verify the new header.
return VerifyAdjacent ( c . chainID , c . trustedHeader , newHeader , newVals , c . trustingPeriod , now )
return VerifyAdjacent ( c . chainID , c . la testT rustedHeader, newHeader , newVals , c . trustingPeriod , now )
}
// see VerifyHeader
@ -779,8 +813,8 @@ func (c *Client) updateTrustedHeaderAndNextVals(h *types.SignedHeader, nextVals
return errors . Wrap ( err , "failed to save trusted header" )
}
c . trustedHeader = h
c . trustedNextVals = nextVals
c . la testT rustedHeader = h
c . la testT rustedNextVals = nextVals
return nil
}
@ -799,77 +833,44 @@ func (c *Client) fetchHeaderAndValsAtHeight(height int64) (*types.SignedHeader,
return h , vals , nil
}
// fetchMissingTrustedHeader finds the closest height after the
// requested height and does backwards verification.
func ( c * Client ) fetchMissingTrustedHeader ( height int64 , now time . Time ) ( * types . SignedHeader , error ) {
c . logger . Info ( "Fetching missing header" , "height" , height )
closestHeader , err := c . trustedStore . SignedHeaderAfter ( height )
if err != nil {
return nil , errors . Wrapf ( err , "can't get signed header after %d" , height )
}
// Perform backwards verification from closestHeader to header at the given
// height.
h , err := c . backwards ( height , closestHeader , now )
if err != nil {
return nil , err
}
// Fetch next validator set from primary and persist it.
nextVals , err := c . validatorSetFromPrimary ( height + 1 )
if err != nil {
return nil , errors . Wrapf ( err , "failed to obtain the vals #%d" , height )
}
if ! bytes . Equal ( h . NextValidatorsHash , nextVals . Hash ( ) ) {
return nil , errors . Errorf ( "expected next validator's hash %X, but got %X" ,
h . NextValidatorsHash , nextVals . Hash ( ) )
}
if err := c . trustedStore . SaveSignedHeaderAndNextValidatorSet ( h , nextVals ) ; err != nil {
return nil , errors . Wrap ( err , "failed to save trusted header" )
}
return h , nil
}
// Backwards verification (see VerifyHeaderBackwards func in the spec)
func ( c * Client ) backwards ( toHeight int64 , fromHeader * types . SignedHeader , now time . Time ) ( * types . SignedHeader , error ) {
func ( c * Client ) backwards ( trustedHeader * types . SignedHeader , newHeader * types . SignedHeader ,
now time . Time ) error {
var (
trustedHeader = fromHeader
untrustedHeader * types . SignedHeader
err error
interimHeader * types . SignedHeader
err error
)
for i := trustedHeader . Height - 1 ; i >= toHeight ; i -- {
untrusted Header, err = c . signedHeaderFromPrimary ( i )
for trustedHeader . Height > newHeader . Height {
interimHeader , err = c . signedHeaderFromPrimary ( trustedHeader . Height - 1 )
if err != nil {
return nil , errors . Wrapf ( err , "failed to obtain the header #%d" , i )
return errors . Wrapf ( err , "failed to obtain the header at height #%d" , trustedHeader . Height - 1 )
}
if err := untrusted Header. ValidateBasic ( c . chainID ) ; err != nil {
return nil , errors . Wrap ( err , "untrustedHeader.ValidateBasic failed" )
if err := interimHeader . ValidateBasic ( c . chainID ) ; err != nil {
return errors . Wrap ( err , "untrustedHeader.ValidateBasic failed" )
}
if ! untrusted Header. Time . Before ( trustedHeader . Time ) {
return nil , errors . Errorf ( "expected older header time %v to be before newer header time %v" ,
untrusted Header. Time ,
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 HeaderExpired ( untrusted Header, c . trustingPeriod , now ) {
return nil , ErrOldHeaderExpired { untrusted Header. Time . Add ( c . trustingPeriod ) , now }
if HeaderExpired ( interimHeader , c . trustingPeriod , now ) {
return ErrOldHeaderExpired { interimHeader . Time . Add ( c . trustingPeriod ) , now }
}
if ! bytes . Equal ( untrusted Header. Hash ( ) , trustedHeader . LastBlockID . Hash ) {
return nil , errors . Errorf ( "older header hash %X does not match trusted header's last block %X" ,
untrusted Header. Hash ( ) ,
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 )
}
trustedHeader = untrusted Header
trustedHeader = interim Header
}
return trustedHeader , nil
return nil
}
// compare header with all witnesses provided.
@ -899,7 +900,7 @@ func (c *Client) compareNewHeaderWithWitnesses(h *types.SignedHeader) error {
}
if ! bytes . Equal ( h . Hash ( ) , altH . Hash ( ) ) {
if err = c . trustedNextVals . VerifyCommitTrusting ( c . chainID , altH . Commit . BlockID ,
if err = c . la testT rustedNextVals. VerifyCommitTrusting ( c . chainID , altH . Commit . BlockID ,
altH . Height , altH . Commit , c . trustLevel ) ; err != nil {
c . logger . Error ( "Witness sent us incorrect header" , "err" , err , "witness" , witness )
witnessesToRemove = append ( witnessesToRemove , i )
@ -989,7 +990,7 @@ func (c *Client) RemoveNoLongerTrustedHeaders(now time.Time) {
// 3) Remove all headers that are outside of the trusting period.
//
// NOTE: even the latest header can be removed. it's okay because
// c.trustedHeader will retain it in memory so other funcs like VerifyHeader
// c.la testT rustedHeader will retain it in memory so other funcs like VerifyHeader
// don't crash.
for height := oldestHeight ; height <= latestHeight ; height ++ {
h , err := c . trustedStore . SignedHeader ( height )