You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

169 lines
5.7 KiB

lite2: remove auto update (#4535) We first introduced auto-update as a separate struct AutoClient, which was wrapping Client and calling Update periodically. // AutoClient can auto update itself by fetching headers every N seconds. type AutoClient struct { base *Client updatePeriod time.Duration quit chan struct{} trustedHeaders chan *types.SignedHeader errs chan error } // NewAutoClient creates a new client and starts a polling goroutine. func NewAutoClient(base *Client, updatePeriod time.Duration) *AutoClient { c := &AutoClient{ base: base, updatePeriod: updatePeriod, quit: make(chan struct{}), trustedHeaders: make(chan *types.SignedHeader), errs: make(chan error), } go c.autoUpdate() return c } // TrustedHeaders returns a channel onto which new trusted headers are posted. func (c *AutoClient) TrustedHeaders() <-chan *types.SignedHeader { return c.trustedHeaders } // Err returns a channel onto which errors are posted. func (c *AutoClient) Errs() <-chan error { return c.errs } // Stop stops the client. func (c *AutoClient) Stop() { close(c.quit) } func (c *AutoClient) autoUpdate() { ticker := time.NewTicker(c.updatePeriod) defer ticker.Stop() for { select { case <-ticker.C: lastTrustedHeight, err := c.base.LastTrustedHeight() if err != nil { c.errs <- err continue } if lastTrustedHeight == -1 { // no headers yet => wait continue } newTrustedHeader, err := c.base.Update(time.Now()) if err != nil { c.errs <- err continue } if newTrustedHeader != nil { c.trustedHeaders <- newTrustedHeader } case <-c.quit: return } } } Later we merged it into the Client itself with the assumption that most clients will want it. But now I am not sure. Neither IBC nor cosmos/relayer are using it. It increases complexity (Start/Stop methods). That said, I think it makes sense to remove it until we see a need for it (until we better understand usage behavior). We can always introduce it later 😅. Maybe in the form of AutoClient.
4 years ago
  1. # ADR 046: Lite Client Implementation
  2. ## Changelog
  3. * 13-02-2020: Initial draft
  4. * 26-02-2020: Cross-checking the first header
  5. * 28-02-2020: Bisection algorithm details
  6. * 31-03-2020: Verify signature got changed
  7. ## Context
  8. A `Client` struct represents a light client, connected to a single blockchain.
  9. The user has an option to verify headers using `VerifyHeader` or
  10. `VerifyHeaderAtHeight` or `Update` methods. The latter method downloads the
  11. latest header from primary and compares it with the currently trusted one.
  12. ```go
  13. type Client interface {
  14. // verify new headers
  15. VerifyHeaderAtHeight(height int64, now time.Time) (*types.SignedHeader, error)
  16. VerifyHeader(newHeader *types.SignedHeader, newVals *types.ValidatorSet, now time.Time) error
  17. Update(now time.Time) (*types.SignedHeader, error)
  18. // get trusted headers & validators
  19. TrustedHeader(height int64) (*types.SignedHeader, error)
  20. TrustedValidatorSet(height int64) (valSet *types.ValidatorSet, heightUsed int64, err error)
  21. LastTrustedHeight() (int64, error)
  22. FirstTrustedHeight() (int64, error)
  23. // query configuration options
  24. ChainID() string
  25. Primary() provider.Provider
  26. Witnesses() []provider.Provider
  27. Cleanup() error
  28. }
  29. ```
  30. A new light client can either be created from scratch (via `NewClient`) or
  31. using the trusted store (via `NewClientFromTrustedStore`). When there's some
  32. data in the trusted store and `NewClient` is called, the light client will a)
  33. check if stored header is more recent b) optionally ask the user whenever it
  34. should rollback (no confirmation required by default).
  35. ```go
  36. func NewClient(
  37. chainID string,
  38. trustOptions TrustOptions,
  39. primary provider.Provider,
  40. witnesses []provider.Provider,
  41. trustedStore store.Store,
  42. options ...Option) (*Client, error) {
  43. ```
  44. `witnesses` as argument (as opposite to `Option`) is an intentional choice,
  45. made to increase security by default. At least one witness is required,
  46. although, right now, the light client does not check that primary != witness.
  47. When cross-checking a new header with witnesses, minimum number of witnesses
  48. required to respond: 1. Note the very first header (`TrustOptions.Hash`) is
  49. also cross-checked with witnesses for additional security.
  50. Due to bisection algorithm nature, some headers might be skipped. If the light
  51. client does not have a header for height `X` and `VerifyHeaderAtHeight(X)` or
  52. `VerifyHeader(H#X)` methods are called, these will perform either a) backwards
  53. verification from the latest header back to the header at height `X` or b)
  54. bisection verification from the first stored header to the header at height `X`.
  55. `TrustedHeader`, `TrustedValidatorSet` only communicate with the trusted store.
  56. If some header is not there, an error will be returned indicating that
  57. verification is required.
  58. ```go
  59. type Provider interface {
  60. ChainID() string
  61. SignedHeader(height int64) (*types.SignedHeader, error)
  62. ValidatorSet(height int64) (*types.ValidatorSet, error)
  63. }
  64. ```
  65. Provider is a full node usually, but can be another light client. The above
  66. interface is thin and can accommodate many implementations.
  67. If provider (primary or witness) becomes unavailable for a prolonged period of
  68. time, it will be removed to ensure smooth operation.
  69. Both `Client` and providers expose chain ID to track if there are on the same
  70. chain. Note, when chain upgrades or intentionally forks, chain ID changes.
  71. The light client stores headers & validators in the trusted store:
  72. ```go
  73. type Store interface {
  74. SaveSignedHeaderAndValidatorSet(sh *types.SignedHeader, valSet *types.ValidatorSet) error
  75. DeleteSignedHeaderAndValidatorSet(height int64) error
  76. SignedHeader(height int64) (*types.SignedHeader, error)
  77. ValidatorSet(height int64) (*types.ValidatorSet, error)
  78. LastSignedHeaderHeight() (int64, error)
  79. FirstSignedHeaderHeight() (int64, error)
  80. SignedHeaderAfter(height int64) (*types.SignedHeader, error)
  81. Prune(size uint16) error
  82. Size() uint16
  83. }
  84. ```
  85. At the moment, the only implementation is the `db` store (wrapper around the KV
  86. database, used in Tendermint). In the future, remote adapters are possible
  87. (e.g. `Postgresql`).
  88. ```go
  89. func Verify(
  90. chainID string,
  91. trustedHeader *types.SignedHeader, // height=X
  92. trustedVals *types.ValidatorSet, // height=X or height=X+1
  93. untrustedHeader *types.SignedHeader, // height=Y
  94. untrustedVals *types.ValidatorSet, // height=Y
  95. trustingPeriod time.Duration,
  96. now time.Time,
  97. maxClockDrift time.Duration,
  98. trustLevel tmmath.Fraction) error {
  99. ```
  100. `Verify` pure function is exposed for a header verification. It handles both
  101. cases of adjacent and non-adjacent headers. In the former case, it compares the
  102. hashes directly (2/3+ signed transition). Otherwise, it verifies 1/3+
  103. (`trustLevel`) of trusted validators are still present in new validators.
  104. While `Verify` function is certainly handy, `VerifyAdjacent` and
  105. `VerifyNonAdjacent` should be used most often to avoid logic errors.
  106. ### Bisection algorithm details
  107. Non-recursive bisection algorithm was implemented despite the spec containing
  108. the recursive version. There are two major reasons:
  109. 1) Constant memory consumption => no risk of getting OOM (Out-Of-Memory) exceptions;
  110. 2) Faster finality (see Fig. 1).
  111. _Fig. 1: Differences between recursive and non-recursive bisections_
  112. ![Fig. 1](./img/adr-046-fig1.png)
  113. Specification of the non-recursive bisection can be found
  114. [here](https://github.com/tendermint/spec/blob/zm_non-recursive-verification/spec/consensus/light-client/non-recursive-verification.md).
  115. ## Status
  116. Implemented
  117. ## Consequences
  118. ### Positive
  119. * single `Client` struct, which is easy to use
  120. * flexible interfaces for header providers and trusted storage
  121. ### Negative
  122. * `Verify` needs to be aligned with the current spec
  123. ### Neutral
  124. * `Verify` function might be misused (called with non-adjacent headers in
  125. incorrectly implemented sequential verification)