github.com/lazyledger/lazyledger-core@v0.35.0-dev.0.20210613111200-4c651f053571/light/client.go (about)

     1  package light
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"errors"
     7  	"fmt"
     8  	"time"
     9  
    10  	format "github.com/ipfs/go-ipld-format"
    11  	"github.com/ipfs/go-merkledag"
    12  	coreiface "github.com/ipfs/interface-go-ipfs-core"
    13  	"github.com/lazyledger/nmt/namespace"
    14  
    15  	"github.com/lazyledger/lazyledger-core/libs/log"
    16  	tmmath "github.com/lazyledger/lazyledger-core/libs/math"
    17  	tmsync "github.com/lazyledger/lazyledger-core/libs/sync"
    18  	"github.com/lazyledger/lazyledger-core/light/provider"
    19  	"github.com/lazyledger/lazyledger-core/light/store"
    20  	"github.com/lazyledger/lazyledger-core/p2p/ipld"
    21  	"github.com/lazyledger/lazyledger-core/types"
    22  )
    23  
    24  type mode byte
    25  
    26  const (
    27  	sequential mode = iota + 1
    28  	skipping
    29  	dataAvailabilitySampling
    30  
    31  	defaultPruningSize      = 1000
    32  	defaultMaxRetryAttempts = 10
    33  	// For verifySkipping, when using the cache of headers from the previous batch,
    34  	// they will always be at a height greater than 1/2 (normal verifySkipping) so to
    35  	// find something in between the range, 9/16 is used.
    36  	verifySkippingNumerator   = 9
    37  	verifySkippingDenominator = 16
    38  
    39  	// 10s should cover most of the clients.
    40  	// References:
    41  	// - http://vancouver-webpages.com/time/web.html
    42  	// - https://blog.codinghorror.com/keeping-time-on-the-pc/
    43  	defaultMaxClockDrift = 10 * time.Second
    44  )
    45  
    46  // Option sets a parameter for the light client.
    47  type Option func(*Client)
    48  
    49  // SequentialVerification option configures the light client to sequentially
    50  // check the blocks (every block, in ascending height order). Note this is
    51  // much slower than SkippingVerification, albeit more secure.
    52  func SequentialVerification() Option {
    53  	return func(c *Client) {
    54  		c.verificationMode = sequential
    55  	}
    56  }
    57  
    58  // SkippingVerification option configures the light client to skip blocks as
    59  // long as {trustLevel} of the old validator set signed the new header. The
    60  // verifySkipping algorithm from the specification is used for finding the minimal
    61  // "trust path".
    62  //
    63  // trustLevel - fraction of the old validator set (in terms of voting power),
    64  // which must sign the new header in order for us to trust it. NOTE this only
    65  // applies to non-adjacent headers. For adjacent headers, sequential
    66  // verification is used.
    67  func SkippingVerification(trustLevel tmmath.Fraction) Option {
    68  	return func(c *Client) {
    69  		c.verificationMode = skipping
    70  		c.trustLevel = trustLevel
    71  	}
    72  }
    73  
    74  func DataAvailabilitySampling(numSamples uint32, ipfsAPI coreiface.APIDagService) Option {
    75  	return func(c *Client) {
    76  		c.verificationMode = dataAvailabilitySampling
    77  		c.numSamples = numSamples
    78  		c.dag = ipfsAPI
    79  		c.sessionDAG = merkledag.NewSession(context.TODO(), ipfsAPI)
    80  	}
    81  }
    82  
    83  // PruningSize option sets the maximum amount of light blocks that the light
    84  // client stores. When Prune() is run, all light blocks that are earlier than
    85  // the h amount of light blocks will be removed from the store.
    86  // Default: 1000. A pruning size of 0 will not prune the light client at all.
    87  func PruningSize(h uint16) Option {
    88  	return func(c *Client) {
    89  		c.pruningSize = h
    90  	}
    91  }
    92  
    93  // ConfirmationFunction option can be used to prompt to confirm an action. For
    94  // example, remove newer headers if the light client is being reset with an
    95  // older header. No confirmation is required by default!
    96  func ConfirmationFunction(fn func(action string) bool) Option {
    97  	return func(c *Client) {
    98  		c.confirmationFn = fn
    99  	}
   100  }
   101  
   102  // Logger option can be used to set a logger for the client.
   103  func Logger(l log.Logger) Option {
   104  	return func(c *Client) {
   105  		c.logger = l
   106  	}
   107  }
   108  
   109  // MaxRetryAttempts option can be used to set max attempts before replacing
   110  // primary with a witness.
   111  func MaxRetryAttempts(max uint16) Option {
   112  	return func(c *Client) {
   113  		c.maxRetryAttempts = max
   114  	}
   115  }
   116  
   117  // MaxClockDrift defines how much new header's time can drift into
   118  // the future. Default: 10s.
   119  func MaxClockDrift(d time.Duration) Option {
   120  	return func(c *Client) {
   121  		c.maxClockDrift = d
   122  	}
   123  }
   124  
   125  // Client represents a light client, connected to a single chain, which gets
   126  // light blocks from a primary provider, verifies them either sequentially or by
   127  // skipping some and stores them in a trusted store (usually, a local FS).
   128  //
   129  // Default verification: SkippingVerification(DefaultTrustLevel)
   130  type Client struct {
   131  	chainID          string
   132  	trustingPeriod   time.Duration // see TrustOptions.Period
   133  	verificationMode mode
   134  	trustLevel       tmmath.Fraction
   135  	numSamples       uint32
   136  	maxRetryAttempts uint16 // see MaxRetryAttempts option
   137  	maxClockDrift    time.Duration
   138  
   139  	// Mutex for locking during changes of the light clients providers
   140  	providerMutex tmsync.Mutex
   141  	// Primary provider of new headers.
   142  	primary provider.Provider
   143  	// Providers used to "witness" new headers.
   144  	witnesses []provider.Provider
   145  
   146  	// Where trusted light blocks are stored.
   147  	trustedStore store.Store
   148  	// Highest trusted light block from the store (height=H).
   149  	latestTrustedBlock *types.LightBlock
   150  
   151  	// See RemoveNoLongerTrustedHeadersPeriod option
   152  	pruningSize uint16
   153  	// See ConfirmationFunction option
   154  	confirmationFn func(action string) bool
   155  
   156  	quit chan struct{}
   157  
   158  	logger log.Logger
   159  
   160  	dag        coreiface.APIDagService
   161  	sessionDAG format.NodeGetter
   162  }
   163  
   164  // NewClient returns a new light client. It returns an error if it fails to
   165  // obtain the light block from the primary or they are invalid (e.g. trust
   166  // hash does not match with the one from the headers).
   167  //
   168  // Witnesses are providers, which will be used for cross-checking the primary
   169  // provider. At least one witness must be given when skipping verification is
   170  // used (default). A witness can become a primary iff the current primary is
   171  // unavailable.
   172  //
   173  // See all Option(s) for the additional configuration.
   174  func NewClient(
   175  	ctx context.Context,
   176  	chainID string,
   177  	trustOptions TrustOptions,
   178  	primary provider.Provider,
   179  	witnesses []provider.Provider,
   180  	trustedStore store.Store,
   181  	options ...Option) (*Client, error) {
   182  
   183  	if err := trustOptions.ValidateBasic(); err != nil {
   184  		return nil, fmt.Errorf("invalid TrustOptions: %w", err)
   185  	}
   186  
   187  	c, err := NewClientFromTrustedStore(chainID, trustOptions.Period, primary, witnesses, trustedStore, options...)
   188  	if err != nil {
   189  		return nil, err
   190  	}
   191  
   192  	if c.latestTrustedBlock != nil {
   193  		c.logger.Info("Checking trusted light block using options")
   194  		if err := c.checkTrustedHeaderUsingOptions(ctx, trustOptions); err != nil {
   195  			return nil, err
   196  		}
   197  	}
   198  
   199  	if c.latestTrustedBlock == nil || c.latestTrustedBlock.Height < trustOptions.Height {
   200  		c.logger.Info("Downloading trusted light block using options")
   201  		if err := c.initializeWithTrustOptions(ctx, trustOptions); err != nil {
   202  			return nil, err
   203  		}
   204  	}
   205  
   206  	return c, err
   207  }
   208  
   209  // NewClientFromTrustedStore initializes existing client from the trusted store.
   210  //
   211  // See NewClient
   212  func NewClientFromTrustedStore(
   213  	chainID string,
   214  	trustingPeriod time.Duration,
   215  	primary provider.Provider,
   216  	witnesses []provider.Provider,
   217  	trustedStore store.Store,
   218  	options ...Option) (*Client, error) {
   219  
   220  	c := &Client{
   221  		chainID:          chainID,
   222  		trustingPeriod:   trustingPeriod,
   223  		verificationMode: skipping,
   224  		trustLevel:       DefaultTrustLevel,
   225  		maxRetryAttempts: defaultMaxRetryAttempts,
   226  		maxClockDrift:    defaultMaxClockDrift,
   227  		primary:          primary,
   228  		witnesses:        witnesses,
   229  		trustedStore:     trustedStore,
   230  		pruningSize:      defaultPruningSize,
   231  		confirmationFn:   func(action string) bool { return true },
   232  		quit:             make(chan struct{}),
   233  		logger:           log.NewNopLogger(),
   234  	}
   235  
   236  	for _, o := range options {
   237  		o(c)
   238  	}
   239  
   240  	// Validate the number of witnesses.
   241  	if len(c.witnesses) < 1 {
   242  		return nil, ErrNoWitnesses
   243  	}
   244  
   245  	// Validate trust level.
   246  	if err := ValidateTrustLevel(c.trustLevel); err != nil {
   247  		return nil, err
   248  	}
   249  
   250  	if c.verificationMode == dataAvailabilitySampling {
   251  		if err := ValidateNumSamples(c.numSamples); err != nil {
   252  			return nil, err
   253  		}
   254  	}
   255  
   256  	if err := c.restoreTrustedLightBlock(); err != nil {
   257  		return nil, err
   258  	}
   259  
   260  	return c, nil
   261  }
   262  
   263  // restoreTrustedLightBlock loads the latest trusted light block from the store
   264  func (c *Client) restoreTrustedLightBlock() error {
   265  	lastHeight, err := c.trustedStore.LastLightBlockHeight()
   266  	if err != nil {
   267  		return fmt.Errorf("can't get last trusted light block height: %w", err)
   268  	}
   269  
   270  	if lastHeight > 0 {
   271  		trustedBlock, err := c.trustedStore.LightBlock(lastHeight)
   272  		if err != nil {
   273  			return fmt.Errorf("can't get last trusted light block: %w", err)
   274  		}
   275  		c.latestTrustedBlock = trustedBlock
   276  		c.logger.Info("Restored trusted light block", "height", lastHeight)
   277  	}
   278  
   279  	return nil
   280  }
   281  
   282  // if options.Height:
   283  //
   284  //     1) ahead of trustedLightBlock.Height => fetch light blocks (same height as
   285  //     trustedLightBlock) from primary provider and check it's hash matches the
   286  //     trustedLightBlock's hash (if not, remove trustedLightBlock and all the light blocks
   287  //     before)
   288  //
   289  //     2) equals trustedLightBlock.Height => check options.Hash matches the
   290  //     trustedLightBlock's hash (if not, remove trustedLightBlock and all the light blocks
   291  //     before)
   292  //
   293  //     3) behind trustedLightBlock.Height => remove all the light blocks between
   294  //     options.Height and trustedLightBlock.Height, update trustedLightBlock, then
   295  //     check options.Hash matches the trustedLightBlock's hash (if not, remove
   296  //     trustedLightBlock and all the light blocks before)
   297  //
   298  // The intuition here is the user is always right. I.e. if she decides to reset
   299  // the light client with an older header, there must be a reason for it.
   300  func (c *Client) checkTrustedHeaderUsingOptions(ctx context.Context, options TrustOptions) error {
   301  	var primaryHash []byte
   302  	switch {
   303  	case options.Height > c.latestTrustedBlock.Height:
   304  		h, err := c.lightBlockFromPrimary(ctx, c.latestTrustedBlock.Height)
   305  		if err != nil {
   306  			return err
   307  		}
   308  		primaryHash = h.Hash()
   309  	case options.Height == c.latestTrustedBlock.Height:
   310  		primaryHash = options.Hash
   311  	case options.Height < c.latestTrustedBlock.Height:
   312  		c.logger.Info("Client initialized with old header (trusted is more recent)",
   313  			"old", options.Height,
   314  			"trustedHeight", c.latestTrustedBlock.Height,
   315  			"trustedHash", hash2str(c.latestTrustedBlock.Hash()))
   316  
   317  		action := fmt.Sprintf(
   318  			"Rollback to %d (%X)? Note this will remove newer light blocks up to %d (%X)",
   319  			options.Height, options.Hash,
   320  			c.latestTrustedBlock.Height, c.latestTrustedBlock.Hash())
   321  		if c.confirmationFn(action) {
   322  			// remove all the headers (options.Height, trustedHeader.Height]
   323  			err := c.cleanupAfter(options.Height)
   324  			if err != nil {
   325  				return fmt.Errorf("cleanupAfter(%d): %w", options.Height, err)
   326  			}
   327  
   328  			c.logger.Info("Rolled back to older header (newer headers were removed)",
   329  				"old", options.Height)
   330  		} else {
   331  			return nil
   332  		}
   333  
   334  		primaryHash = options.Hash
   335  	}
   336  
   337  	if !bytes.Equal(primaryHash, c.latestTrustedBlock.Hash()) {
   338  		c.logger.Info("Prev. trusted header's hash (h1) doesn't match hash from primary provider (h2)",
   339  			"h1", hash2str(c.latestTrustedBlock.Hash()), "h2", hash2str(primaryHash))
   340  
   341  		action := fmt.Sprintf(
   342  			"Prev. trusted header's hash %X doesn't match hash %X from primary provider. Remove all the stored light blocks?",
   343  			c.latestTrustedBlock.Hash(), primaryHash)
   344  		if c.confirmationFn(action) {
   345  			err := c.Cleanup()
   346  			if err != nil {
   347  				return fmt.Errorf("failed to cleanup: %w", err)
   348  			}
   349  		} else {
   350  			return errors.New("refused to remove the stored light blocks despite hashes mismatch")
   351  		}
   352  	}
   353  
   354  	return nil
   355  }
   356  
   357  // initializeWithTrustOptions fetches the weakly-trusted light block from
   358  // primary provider.
   359  func (c *Client) initializeWithTrustOptions(ctx context.Context, options TrustOptions) error {
   360  	// 1) Fetch and verify the light block.
   361  	l, err := c.lightBlockFromPrimary(ctx, options.Height)
   362  	if err != nil {
   363  		return err
   364  	}
   365  
   366  	// NOTE: - Verify func will check if it's expired or not.
   367  	//       - h.Time is not being checked against time.Now() because we don't
   368  	//         want to add yet another argument to NewClient* functions.
   369  	if err := l.ValidateBasic(c.chainID); err != nil {
   370  		return err
   371  	}
   372  
   373  	if !bytes.Equal(l.Hash(), options.Hash) {
   374  		return fmt.Errorf("expected header's hash %X, but got %X", options.Hash, l.Hash())
   375  	}
   376  
   377  	// 2) Ensure that +2/3 of validators signed correctly.
   378  	err = l.ValidatorSet.VerifyCommitLight(c.chainID, l.Commit.BlockID, l.Height, l.Commit)
   379  	if err != nil {
   380  		return fmt.Errorf("invalid commit: %w", err)
   381  	}
   382  
   383  	// 3) Cross-verify with witnesses to ensure everybody has the same state.
   384  	if err := c.compareFirstHeaderWithWitnesses(ctx, l.SignedHeader); err != nil {
   385  		return err
   386  	}
   387  
   388  	// 4) Persist both of them and continue.
   389  	return c.updateTrustedLightBlock(l)
   390  }
   391  
   392  // TrustedLightBlock returns a trusted light block at the given height (0 - the latest).
   393  //
   394  // It returns an error if:
   395  //  - there are some issues with the trusted store, although that should not
   396  //  happen normally;
   397  //  - negative height is passed;
   398  //  - header has not been verified yet and is therefore not in the store
   399  //
   400  // Safe for concurrent use by multiple goroutines.
   401  func (c *Client) TrustedLightBlock(height int64) (*types.LightBlock, error) {
   402  	height, err := c.compareWithLatestHeight(height)
   403  	if err != nil {
   404  		return nil, err
   405  	}
   406  	return c.trustedStore.LightBlock(height)
   407  }
   408  
   409  func (c *Client) compareWithLatestHeight(height int64) (int64, error) {
   410  	latestHeight, err := c.LastTrustedHeight()
   411  	if err != nil {
   412  		return 0, fmt.Errorf("can't get last trusted height: %w", err)
   413  	}
   414  	if latestHeight == -1 {
   415  		return 0, errors.New("no headers exist")
   416  	}
   417  
   418  	switch {
   419  	case height > latestHeight:
   420  		return 0, fmt.Errorf("unverified header/valset requested (latest: %d)", latestHeight)
   421  	case height == 0:
   422  		return latestHeight, nil
   423  	case height < 0:
   424  		return 0, errors.New("negative height")
   425  	}
   426  
   427  	return height, nil
   428  }
   429  
   430  // Update attempts to advance the state by downloading the latest light
   431  // block and verifying it. It returns a new light block on a successful
   432  // update. Otherwise, it returns nil (plus an error, if any).
   433  func (c *Client) Update(ctx context.Context, now time.Time) (*types.LightBlock, error) {
   434  	lastTrustedHeight, err := c.LastTrustedHeight()
   435  	if err != nil {
   436  		return nil, fmt.Errorf("can't get last trusted height: %w", err)
   437  	}
   438  
   439  	if lastTrustedHeight == -1 {
   440  		// no light blocks yet => wait
   441  		return nil, nil
   442  	}
   443  
   444  	latestBlock, err := c.lightBlockFromPrimary(ctx, 0)
   445  	if err != nil {
   446  		return nil, err
   447  	}
   448  
   449  	if latestBlock.Height > lastTrustedHeight {
   450  		err = c.verifyLightBlock(ctx, latestBlock, now)
   451  		if err != nil {
   452  			return nil, err
   453  		}
   454  		c.logger.Info("Advanced to new state", "height", latestBlock.Height, "hash", hash2str(latestBlock.Hash()))
   455  		return latestBlock, nil
   456  	}
   457  
   458  	return nil, nil
   459  }
   460  
   461  // VerifyLightBlockAtHeight fetches the light block at the given height
   462  // and verifies it. It returns the block immediately if it exists in
   463  // the trustedStore (no verification is needed).
   464  //
   465  // height must be > 0.
   466  //
   467  // It returns provider.ErrlightBlockNotFound if light block is not found by
   468  // primary.
   469  //
   470  // It will replace the primary provider if an error from a request to the provider occurs
   471  func (c *Client) VerifyLightBlockAtHeight(ctx context.Context, height int64, now time.Time) (*types.LightBlock, error) {
   472  	if height <= 0 {
   473  		return nil, errors.New("negative or zero height")
   474  	}
   475  
   476  	// Check if the light block is already verified.
   477  	h, err := c.TrustedLightBlock(height)
   478  	if err == nil {
   479  		c.logger.Info("Header has already been verified", "height", height, "hash", hash2str(h.Hash()))
   480  		// Return already trusted light block
   481  		return h, nil
   482  	}
   483  
   484  	// Request the light block from primary
   485  	l, err := c.lightBlockFromPrimary(ctx, height)
   486  	if err != nil {
   487  		return nil, err
   488  	}
   489  
   490  	return l, c.verifyLightBlock(ctx, l, now)
   491  }
   492  
   493  // VerifyHeader verifies a new header against the trusted state. It returns
   494  // immediately if newHeader exists in trustedStore (no verification is
   495  // needed). Else it performs one of the two types of verification:
   496  //
   497  // SequentialVerification: verifies that 2/3 of the trusted validator set has
   498  // signed the new header. If the headers are not adjacent, **all** intermediate
   499  // headers will be requested. Intermediate headers are not saved to database.
   500  //
   501  // SkippingVerification(trustLevel): verifies that {trustLevel} of the trusted
   502  // validator set has signed the new header. If it's not the case and the
   503  // headers are not adjacent, verifySkipping is performed and necessary (not all)
   504  // intermediate headers will be requested. See the specification for details.
   505  // Intermediate headers are not saved to database.
   506  // https://github.com/tendermint/spec/blob/master/spec/consensus/light-client.md
   507  //
   508  // If the header, which is older than the currently trusted header, is
   509  // requested and the light client does not have it, VerifyHeader will perform:
   510  //		a) verifySkipping verification if nearest trusted header is found & not expired
   511  //		b) backwards verification in all other cases
   512  //
   513  // It returns ErrOldHeaderExpired if the latest trusted header expired.
   514  //
   515  // If the primary provides an invalid header (ErrInvalidHeader), it is rejected
   516  // and replaced by another provider until all are exhausted.
   517  //
   518  // If, at any moment, a LightBlock is not found by the primary provider as part of
   519  // verification then the provider will be replaced by another and the process will
   520  // restart.
   521  func (c *Client) VerifyHeader(ctx context.Context, newHeader *types.Header, now time.Time) error {
   522  	if newHeader == nil {
   523  		return errors.New("nil header")
   524  	}
   525  	if newHeader.Height <= 0 {
   526  		return errors.New("negative or zero height")
   527  	}
   528  
   529  	// Check if newHeader already verified.
   530  	l, err := c.TrustedLightBlock(newHeader.Height)
   531  	if err == nil {
   532  		// Make sure it's the same header.
   533  		if !bytes.Equal(l.Hash(), newHeader.Hash()) {
   534  			return fmt.Errorf("existing trusted header %X does not match newHeader %X", l.Hash(), newHeader.Hash())
   535  		}
   536  		c.logger.Info("Header has already been verified",
   537  			"height", newHeader.Height, "hash", hash2str(newHeader.Hash()))
   538  		return nil
   539  	}
   540  
   541  	// Request the header and the vals.
   542  	l, err = c.lightBlockFromPrimary(ctx, newHeader.Height)
   543  	if err != nil {
   544  		return fmt.Errorf("failed to retrieve light block from primary to verify against: %w", err)
   545  	}
   546  
   547  	if !bytes.Equal(l.Hash(), newHeader.Hash()) {
   548  		return fmt.Errorf("light block header %X does not match newHeader %X", l.Hash(), newHeader.Hash())
   549  	}
   550  
   551  	return c.verifyLightBlock(ctx, l, now)
   552  }
   553  
   554  func (c *Client) verifyLightBlock(ctx context.Context, newLightBlock *types.LightBlock, now time.Time) error {
   555  	c.logger.Info("VerifyHeader", "height", newLightBlock.Height, "hash", hash2str(newLightBlock.Hash()))
   556  
   557  	var (
   558  		verifyFunc func(ctx context.Context, trusted *types.LightBlock, new *types.LightBlock, now time.Time) error
   559  		err        error
   560  	)
   561  
   562  	switch c.verificationMode {
   563  	case sequential, dataAvailabilitySampling:
   564  		verifyFunc = c.verifySequential
   565  	case skipping:
   566  		verifyFunc = c.verifySkippingAgainstPrimary
   567  	default:
   568  		panic(fmt.Sprintf("Unknown verification mode: %b", c.verificationMode))
   569  	}
   570  
   571  	firstBlockHeight, err := c.FirstTrustedHeight()
   572  	if err != nil {
   573  		return fmt.Errorf("can't get first light block height: %w", err)
   574  	}
   575  
   576  	switch {
   577  	// Verifying forwards
   578  	case newLightBlock.Height >= c.latestTrustedBlock.Height:
   579  		err = verifyFunc(ctx, c.latestTrustedBlock, newLightBlock, now)
   580  
   581  	// Verifying backwards
   582  	case newLightBlock.Height < firstBlockHeight:
   583  		var firstBlock *types.LightBlock
   584  		firstBlock, err = c.trustedStore.LightBlock(firstBlockHeight)
   585  		if err != nil {
   586  			return fmt.Errorf("can't get first light block: %w", err)
   587  		}
   588  		err = c.backwards(ctx, firstBlock.Header, newLightBlock.Header)
   589  
   590  	// Verifying between first and last trusted light block
   591  	default:
   592  		var closestBlock *types.LightBlock
   593  		closestBlock, err = c.trustedStore.LightBlockBefore(newLightBlock.Height)
   594  		if err != nil {
   595  			return fmt.Errorf("can't get signed header before height %d: %w", newLightBlock.Height, err)
   596  		}
   597  		err = verifyFunc(ctx, closestBlock, newLightBlock, now)
   598  	}
   599  	if err != nil {
   600  		c.logger.Error("Can't verify", "err", err)
   601  		return err
   602  	}
   603  
   604  	// Once verified, save and return
   605  	return c.updateTrustedLightBlock(newLightBlock)
   606  }
   607  
   608  // see VerifyHeader
   609  func (c *Client) verifySequential(
   610  	ctx context.Context,
   611  	trustedBlock *types.LightBlock,
   612  	newLightBlock *types.LightBlock,
   613  	now time.Time) error {
   614  
   615  	var (
   616  		verifiedBlock = trustedBlock
   617  		interimBlock  *types.LightBlock
   618  		err           error
   619  		trace         = []*types.LightBlock{trustedBlock}
   620  	)
   621  
   622  	for height := trustedBlock.Height + 1; height <= newLightBlock.Height; height++ {
   623  		// 1) Fetch interim light block if needed.
   624  		if height == newLightBlock.Height { // last light block
   625  			interimBlock = newLightBlock
   626  		} else { // intermediate light blocks
   627  			interimBlock, err = c.lightBlockFromPrimary(ctx, height)
   628  			if err != nil {
   629  				return ErrVerificationFailed{From: verifiedBlock.Height, To: height, Reason: err}
   630  			}
   631  		}
   632  
   633  		// 2) Verify them
   634  		c.logger.Debug("Verify adjacent newLightBlock against verifiedBlock",
   635  			"trustedHeight", verifiedBlock.Height,
   636  			"trustedHash", hash2str(verifiedBlock.Hash()),
   637  			"newHeight", interimBlock.Height,
   638  			"newHash", hash2str(interimBlock.Hash()))
   639  
   640  		err = VerifyAdjacent(verifiedBlock.SignedHeader, interimBlock.SignedHeader, interimBlock.ValidatorSet,
   641  			c.trustingPeriod, now, c.maxClockDrift)
   642  		if err != nil {
   643  			err := ErrVerificationFailed{From: verifiedBlock.Height, To: interimBlock.Height, Reason: err}
   644  
   645  			switch errors.Unwrap(err).(type) {
   646  			case ErrInvalidHeader:
   647  				// If the target header is invalid, return immediately.
   648  				if err.To == newLightBlock.Height {
   649  					c.logger.Debug("Target header is invalid", "err", err)
   650  					return err
   651  				}
   652  
   653  				// If some intermediate header is invalid, replace the primary and try
   654  				// again.
   655  				c.logger.Error("primary sent invalid header -> replacing", "err", err, "primary", c.primary)
   656  				replaceErr := c.replacePrimaryProvider()
   657  				if replaceErr != nil {
   658  					c.logger.Error("Can't replace primary", "err", replaceErr)
   659  					// return original error
   660  					return err
   661  				}
   662  
   663  				replacementBlock, fErr := c.lightBlockFromPrimary(ctx, newLightBlock.Height)
   664  				if fErr != nil {
   665  					c.logger.Error("Can't fetch light block from primary", "err", fErr)
   666  					// return original error
   667  					return err
   668  				}
   669  
   670  				if !bytes.Equal(replacementBlock.Hash(), newLightBlock.Hash()) {
   671  					c.logger.Error("Replacement provider has a different light block",
   672  						"newHash", newLightBlock.Hash(),
   673  						"replHash", replacementBlock.Hash())
   674  					// return original error
   675  					return err
   676  				}
   677  
   678  				// attempt to verify header again
   679  				height--
   680  
   681  				continue
   682  			default:
   683  				return err
   684  			}
   685  		}
   686  
   687  		// 2.1) Verify that the data behind the block data is actually available.
   688  		if c.verificationMode == dataAvailabilitySampling {
   689  			start := time.Now()
   690  			// TODO: decide how to handle this case:
   691  			// https://github.com/lazyledger/lazyledger-core/issues/319
   692  			numRows := len(interimBlock.DataAvailabilityHeader.RowsRoots)
   693  			numSamples := min(c.numSamples, uint32(numRows*numRows))
   694  			c.logger.Info("Starting Data Availability sampling",
   695  				"height", height,
   696  				"numSamples", numSamples,
   697  				"squareWidth", numRows)
   698  
   699  			err = ipld.ValidateAvailability(
   700  				ctx,
   701  				c.dag,
   702  				interimBlock.DataAvailabilityHeader,
   703  				numSamples,
   704  				func(data namespace.PrefixedData8) {}, // noop
   705  			)
   706  			if err != nil {
   707  				return fmt.Errorf("data availability sampling failed; ipld.ValidateAvailability: %w", err)
   708  			}
   709  			elapsed := time.Since(start)
   710  			c.logger.Info("Successfully finished DAS sampling",
   711  				"height", height,
   712  				"numSamples", numSamples,
   713  				"elapsed time", elapsed)
   714  		}
   715  
   716  		// 3) Update verifiedBlock
   717  		verifiedBlock = interimBlock
   718  
   719  		// 4) Add verifiedBlock to trace
   720  		trace = append(trace, verifiedBlock)
   721  	}
   722  
   723  	// Compare header with the witnesses to ensure it's not a fork.
   724  	// More witnesses we have, more chance to notice one.
   725  	//
   726  	// CORRECTNESS ASSUMPTION: there's at least 1 correct full node
   727  	// (primary or one of the witnesses).
   728  	return c.detectDivergence(ctx, trace, now)
   729  }
   730  
   731  func min(a, b uint32) int {
   732  	if a < b {
   733  		return int(a)
   734  	}
   735  	return int(b)
   736  }
   737  
   738  // see VerifyHeader
   739  //
   740  // verifySkipping finds the middle light block between a trusted and new light block,
   741  // reiterating the action until it verifies a light block. A cache of light blocks
   742  // requested from source is kept such that when a verification is made, and the
   743  // light client tries again to verify the new light block in the middle, the light
   744  // client does not need to ask for all the same light blocks again.
   745  func (c *Client) verifySkipping(
   746  	ctx context.Context,
   747  	source provider.Provider,
   748  	trustedBlock *types.LightBlock,
   749  	newLightBlock *types.LightBlock,
   750  	now time.Time) ([]*types.LightBlock, error) {
   751  
   752  	var (
   753  		blockCache = []*types.LightBlock{newLightBlock}
   754  		depth      = 0
   755  
   756  		verifiedBlock = trustedBlock
   757  		trace         = []*types.LightBlock{trustedBlock}
   758  	)
   759  
   760  	for {
   761  		c.logger.Debug("Verify non-adjacent newHeader against verifiedBlock",
   762  			"trustedHeight", verifiedBlock.Height,
   763  			"trustedHash", hash2str(verifiedBlock.Hash()),
   764  			"newHeight", blockCache[depth].Height,
   765  			"newHash", hash2str(blockCache[depth].Hash()))
   766  
   767  		err := Verify(verifiedBlock.SignedHeader, verifiedBlock.ValidatorSet, blockCache[depth].SignedHeader,
   768  			blockCache[depth].ValidatorSet, c.trustingPeriod, now, c.maxClockDrift, c.trustLevel)
   769  		switch err.(type) {
   770  		case nil:
   771  			// Have we verified the last header
   772  			if depth == 0 {
   773  				trace = append(trace, newLightBlock)
   774  				return trace, nil
   775  			}
   776  			// If not, update the lower bound to the previous upper bound
   777  			verifiedBlock = blockCache[depth]
   778  			// Remove the light block at the lower bound in the header cache - it will no longer be needed
   779  			blockCache = blockCache[:depth]
   780  			// Reset the cache depth so that we start from the upper bound again
   781  			depth = 0
   782  			// add verifiedBlock to the trace
   783  			trace = append(trace, verifiedBlock)
   784  
   785  		case ErrNewValSetCantBeTrusted:
   786  			// do add another header to the end of the cache
   787  			if depth == len(blockCache)-1 {
   788  				pivotHeight := verifiedBlock.Height + (blockCache[depth].Height-verifiedBlock.
   789  					Height)*verifySkippingNumerator/verifySkippingDenominator
   790  				interimBlock, providerErr := source.LightBlock(ctx, pivotHeight)
   791  				if providerErr != nil {
   792  					return nil, ErrVerificationFailed{From: verifiedBlock.Height, To: pivotHeight, Reason: providerErr}
   793  				}
   794  				blockCache = append(blockCache, interimBlock)
   795  			}
   796  			depth++
   797  
   798  		default:
   799  			return nil, ErrVerificationFailed{From: verifiedBlock.Height, To: blockCache[depth].Height, Reason: err}
   800  		}
   801  	}
   802  }
   803  
   804  // verifySkippingAgainstPrimary does verifySkipping plus it compares new header with
   805  // witnesses and replaces primary if it sends the light client an invalid header
   806  func (c *Client) verifySkippingAgainstPrimary(
   807  	ctx context.Context,
   808  	trustedBlock *types.LightBlock,
   809  	newLightBlock *types.LightBlock,
   810  	now time.Time) error {
   811  
   812  	trace, err := c.verifySkipping(ctx, c.primary, trustedBlock, newLightBlock, now)
   813  
   814  	switch errors.Unwrap(err).(type) {
   815  	case ErrInvalidHeader:
   816  		// If the target header is invalid, return immediately.
   817  		invalidHeaderHeight := err.(ErrVerificationFailed).To
   818  		if invalidHeaderHeight == newLightBlock.Height {
   819  			c.logger.Debug("Target header is invalid", "err", err)
   820  			return err
   821  		}
   822  
   823  		// If some intermediate header is invalid, replace the primary and try
   824  		// again.
   825  		c.logger.Error("primary sent invalid header -> replacing", "err", err, "primary", c.primary)
   826  		replaceErr := c.replacePrimaryProvider()
   827  		if replaceErr != nil {
   828  			c.logger.Error("Can't replace primary", "err", replaceErr)
   829  			// return original error
   830  			return err
   831  		}
   832  
   833  		replacementBlock, fErr := c.lightBlockFromPrimary(ctx, newLightBlock.Height)
   834  		if fErr != nil {
   835  			c.logger.Error("Can't fetch light block from primary", "err", fErr)
   836  			// return original error
   837  			return err
   838  		}
   839  
   840  		if !bytes.Equal(replacementBlock.Hash(), newLightBlock.Hash()) {
   841  			c.logger.Error("Replacement provider has a different light block",
   842  				"newHash", newLightBlock.Hash(),
   843  				"replHash", replacementBlock.Hash())
   844  			// return original error
   845  			return err
   846  		}
   847  
   848  		// attempt to verify the header again
   849  		return c.verifySkippingAgainstPrimary(ctx, trustedBlock, replacementBlock, now)
   850  	case nil:
   851  		// Compare header with the witnesses to ensure it's not a fork.
   852  		// More witnesses we have, more chance to notice one.
   853  		//
   854  		// CORRECTNESS ASSUMPTION: there's at least 1 correct full node
   855  		// (primary or one of the witnesses).
   856  		if cmpErr := c.detectDivergence(ctx, trace, now); cmpErr != nil {
   857  			return cmpErr
   858  		}
   859  	default:
   860  		return err
   861  	}
   862  
   863  	return nil
   864  }
   865  
   866  // LastTrustedHeight returns a last trusted height. -1 and nil are returned if
   867  // there are no trusted headers.
   868  //
   869  // Safe for concurrent use by multiple goroutines.
   870  func (c *Client) LastTrustedHeight() (int64, error) {
   871  	return c.trustedStore.LastLightBlockHeight()
   872  }
   873  
   874  // FirstTrustedHeight returns a first trusted height. -1 and nil are returned if
   875  // there are no trusted headers.
   876  //
   877  // Safe for concurrent use by multiple goroutines.
   878  func (c *Client) FirstTrustedHeight() (int64, error) {
   879  	return c.trustedStore.FirstLightBlockHeight()
   880  }
   881  
   882  // ChainID returns the chain ID the light client was configured with.
   883  //
   884  // Safe for concurrent use by multiple goroutines.
   885  func (c *Client) ChainID() string {
   886  	return c.chainID
   887  }
   888  
   889  // Primary returns the primary provider.
   890  //
   891  // NOTE: provider may be not safe for concurrent access.
   892  func (c *Client) Primary() provider.Provider {
   893  	c.providerMutex.Lock()
   894  	defer c.providerMutex.Unlock()
   895  	return c.primary
   896  }
   897  
   898  // Witnesses returns the witness providers.
   899  //
   900  // NOTE: providers may be not safe for concurrent access.
   901  func (c *Client) Witnesses() []provider.Provider {
   902  	c.providerMutex.Lock()
   903  	defer c.providerMutex.Unlock()
   904  	return c.witnesses
   905  }
   906  
   907  // Cleanup removes all the data (headers and validator sets) stored. Note: the
   908  // client must be stopped at this point.
   909  func (c *Client) Cleanup() error {
   910  	c.logger.Info("Removing all light blocks")
   911  	c.latestTrustedBlock = nil
   912  	return c.trustedStore.Prune(0)
   913  }
   914  
   915  // cleanupAfter deletes all headers & validator sets after +height+. It also
   916  // resets latestTrustedBlock to the latest header.
   917  func (c *Client) cleanupAfter(height int64) error {
   918  	prevHeight := c.latestTrustedBlock.Height
   919  
   920  	for {
   921  		h, err := c.trustedStore.LightBlockBefore(prevHeight)
   922  		if err == store.ErrLightBlockNotFound || (h != nil && h.Height <= height) {
   923  			break
   924  		} else if err != nil {
   925  			return fmt.Errorf("failed to get header before %d: %w", prevHeight, err)
   926  		}
   927  
   928  		err = c.trustedStore.DeleteLightBlock(h.Height)
   929  		if err != nil {
   930  			c.logger.Error("can't remove a trusted header & validator set", "err", err,
   931  				"height", h.Height)
   932  		}
   933  
   934  		prevHeight = h.Height
   935  	}
   936  
   937  	c.latestTrustedBlock = nil
   938  	err := c.restoreTrustedLightBlock()
   939  	if err != nil {
   940  		return err
   941  	}
   942  
   943  	return nil
   944  }
   945  
   946  func (c *Client) updateTrustedLightBlock(l *types.LightBlock) error {
   947  	if err := c.trustedStore.SaveLightBlock(l); err != nil {
   948  		return fmt.Errorf("failed to save trusted header: %w", err)
   949  	}
   950  
   951  	if c.pruningSize > 0 {
   952  		if err := c.trustedStore.Prune(c.pruningSize); err != nil {
   953  			return fmt.Errorf("prune: %w", err)
   954  		}
   955  	}
   956  
   957  	if c.latestTrustedBlock == nil || l.Height > c.latestTrustedBlock.Height {
   958  		c.latestTrustedBlock = l
   959  	}
   960  
   961  	return nil
   962  }
   963  
   964  // backwards verification (see VerifyHeaderBackwards func in the spec) verifies
   965  // headers before a trusted header. If a sent header is invalid the primary is
   966  // replaced with another provider and the operation is repeated.
   967  func (c *Client) backwards(
   968  	ctx context.Context,
   969  	trustedHeader *types.Header,
   970  	newHeader *types.Header) error {
   971  
   972  	var (
   973  		verifiedHeader = trustedHeader
   974  		interimHeader  *types.Header
   975  	)
   976  
   977  	for verifiedHeader.Height > newHeader.Height {
   978  		interimBlock, err := c.lightBlockFromPrimary(ctx, verifiedHeader.Height-1)
   979  		if err != nil {
   980  			return fmt.Errorf("failed to obtain the header at height #%d: %w", verifiedHeader.Height-1, err)
   981  		}
   982  		interimHeader = interimBlock.Header
   983  		c.logger.Debug("Verify newHeader against verifiedHeader",
   984  			"trustedHeight", verifiedHeader.Height,
   985  			"trustedHash", hash2str(verifiedHeader.Hash()),
   986  			"newHeight", interimHeader.Height,
   987  			"newHash", hash2str(interimHeader.Hash()))
   988  		if err := VerifyBackwards(interimHeader, verifiedHeader); err != nil {
   989  			c.logger.Error("primary sent invalid header -> replacing", "err", err, "primary", c.primary)
   990  			if replaceErr := c.replacePrimaryProvider(); replaceErr != nil {
   991  				c.logger.Error("Can't replace primary", "err", replaceErr)
   992  				// return original error
   993  				return fmt.Errorf("verify backwards from %d to %d failed: %w",
   994  					verifiedHeader.Height, interimHeader.Height, err)
   995  			}
   996  			// we need to verify the header at the same height again
   997  			continue
   998  		}
   999  		verifiedHeader = interimHeader
  1000  	}
  1001  
  1002  	return nil
  1003  }
  1004  
  1005  // NOTE: requires a providerMutex locked.
  1006  func (c *Client) removeWitness(idx int) {
  1007  	switch len(c.witnesses) {
  1008  	case 0:
  1009  		panic(fmt.Sprintf("wanted to remove %d element from empty witnesses slice", idx))
  1010  	case 1:
  1011  		c.witnesses = make([]provider.Provider, 0)
  1012  	default:
  1013  		c.witnesses[idx] = c.witnesses[len(c.witnesses)-1]
  1014  		c.witnesses = c.witnesses[:len(c.witnesses)-1]
  1015  	}
  1016  }
  1017  
  1018  // replaceProvider takes the first alternative provider and promotes it as the
  1019  // primary provider.
  1020  func (c *Client) replacePrimaryProvider() error {
  1021  	c.providerMutex.Lock()
  1022  	defer c.providerMutex.Unlock()
  1023  
  1024  	if len(c.witnesses) <= 1 {
  1025  		return ErrNoWitnesses
  1026  	}
  1027  	c.primary = c.witnesses[0]
  1028  	c.witnesses = c.witnesses[1:]
  1029  	c.logger.Info("Replacing primary with the first witness", "new_primary", c.primary)
  1030  
  1031  	return nil
  1032  }
  1033  
  1034  // lightBlockFromPrimary retrieves the lightBlock from the primary provider
  1035  // at the specified height. Handles dropout by the primary provider by swapping
  1036  // with an alternative provider.
  1037  func (c *Client) lightBlockFromPrimary(ctx context.Context, height int64) (*types.LightBlock, error) {
  1038  	c.providerMutex.Lock()
  1039  	var (
  1040  		l   *types.LightBlock
  1041  		err error
  1042  	)
  1043  	switch c.verificationMode {
  1044  	case dataAvailabilitySampling:
  1045  		l, err = c.primary.DASLightBlock(ctx, height)
  1046  	default:
  1047  		l, err = c.primary.LightBlock(ctx, height)
  1048  	}
  1049  	c.providerMutex.Unlock()
  1050  	if err != nil {
  1051  		c.logger.Debug("Error on light block request from primary", "error", err, "primary", c.primary)
  1052  		replaceErr := c.replacePrimaryProvider()
  1053  		if replaceErr != nil {
  1054  			return nil, fmt.Errorf("%v. Tried to replace primary but: %w", err.Error(), replaceErr)
  1055  		}
  1056  		// replace primary and request a light block again
  1057  		return c.lightBlockFromPrimary(ctx, height)
  1058  	}
  1059  	return l, err
  1060  }
  1061  
  1062  // compareFirstHeaderWithWitnesses compares h with all witnesses. If any
  1063  // witness reports a different header than h, the function returns an error.
  1064  func (c *Client) compareFirstHeaderWithWitnesses(ctx context.Context, h *types.SignedHeader) error {
  1065  	compareCtx, cancel := context.WithCancel(ctx)
  1066  	defer cancel()
  1067  
  1068  	if len(c.witnesses) < 1 {
  1069  		return ErrNoWitnesses
  1070  	}
  1071  
  1072  	errc := make(chan error, len(c.witnesses))
  1073  	for i, witness := range c.witnesses {
  1074  		go c.compareNewHeaderWithWitness(compareCtx, errc, h, witness, i)
  1075  	}
  1076  
  1077  	witnessesToRemove := make([]int, 0, len(c.witnesses))
  1078  
  1079  	// handle errors from the header comparisons as they come in
  1080  	for i := 0; i < cap(errc); i++ {
  1081  		err := <-errc
  1082  
  1083  		switch e := err.(type) {
  1084  		case nil:
  1085  			continue
  1086  		case errConflictingHeaders:
  1087  			c.logger.Error(fmt.Sprintf(`Witness #%d has a different header. Please check primary is correct
  1088  and remove witness. Otherwise, use the different primary`, e.WitnessIndex), "witness", c.witnesses[e.WitnessIndex])
  1089  			return err
  1090  		case errBadWitness:
  1091  			// If witness sent us an invalid header, then remove it. If it didn't
  1092  			// respond or couldn't find the block, then we ignore it and move on to
  1093  			// the next witness.
  1094  			if _, ok := e.Reason.(provider.ErrBadLightBlock); ok {
  1095  				c.logger.Info("Witness sent us invalid header / vals -> removing it", "witness", c.witnesses[e.WitnessIndex])
  1096  				witnessesToRemove = append(witnessesToRemove, e.WitnessIndex)
  1097  			}
  1098  		}
  1099  	}
  1100  
  1101  	for _, idx := range witnessesToRemove {
  1102  		c.removeWitness(idx)
  1103  	}
  1104  
  1105  	return nil
  1106  }
  1107  
  1108  func hash2str(hash []byte) string {
  1109  	return fmt.Sprintf("%X", hash)
  1110  }