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.

146 lines
4.7 KiB

  1. # ADR 046: Lite Client Implementation
  2. ## Changelog
  3. * 13-02-2020: Initial draft
  4. ## Context
  5. A `Client` struct represents a light client, connected to a single blockchain.
  6. As soon as it's started (via `Start`), it tries to update to the latest header
  7. (using bisection algorithm by default).
  8. Cleaning routine is also started to remove headers outside of trusting period.
  9. NOTE: since it's periodic, we still need to check header is not expired in
  10. `TrustedHeader`, `TrustedValidatorSet` methods (and others which are using the
  11. latest trusted header).
  12. The user has an option to manually verify headers using `VerifyHeader` and
  13. `VerifyHeaderAtHeight` methods. To avoid races, `UpdatePeriod(0)` needs to be
  14. passed when initializing the light client (it turns off the auto update).
  15. ```go
  16. type Client interface {
  17. // start and stop updating & cleaning goroutines
  18. Start() error
  19. Stop()
  20. Cleanup() error
  21. // get trusted headers & validators
  22. TrustedHeader(height int64, now time.Time) (*types.SignedHeader, error)
  23. TrustedValidatorSet(height int64, now time.Time) (*types.ValidatorSet, error)
  24. LastTrustedHeight() (int64, error)
  25. FirstTrustedHeight() (int64, error)
  26. // query configuration options
  27. ChainID() string
  28. Primary() provider.Provider
  29. Witnesses() []provider.Provider
  30. // verify new headers
  31. VerifyHeaderAtHeight(height int64, now time.Time) (*types.SignedHeader, error)
  32. VerifyHeader(newHeader *types.SignedHeader, newVals *types.ValidatorSet, now time.Time) error
  33. }
  34. ```
  35. A new light client can either be created from scratch (via `NewClient`) or
  36. using the trusted store (via `NewClientFromTrustedStore`). When there's some
  37. data in the trusted store and `NewClient` is called, the light client will a)
  38. check if stored header is more recent b) optionally ask the user whenever it
  39. should rollback (no confirmation required by default).
  40. ```go
  41. func NewClient(
  42. chainID string,
  43. trustOptions TrustOptions,
  44. primary provider.Provider,
  45. witnesses []provider.Provider,
  46. trustedStore store.Store,
  47. options ...Option) (*Client, error) {
  48. ```
  49. `witnesses` as argument (as opposite to `Option`) is an intentional choice,
  50. made to increase security by default. At least one witness is required,
  51. although, right now, the light client does not check that primary != witness.
  52. When cross-checking a new header with witnesses, minimum number of witnesses
  53. required to respond: 1.
  54. Due to bisection algorithm nature, some headers might be skipped. If the light
  55. client does not have a header for height `X` and `TrustedHeader(X)` or
  56. `TrustedValidatorSet(X)` methods are called, it will download the header from
  57. primary provider and perform a backwards verification.
  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. SaveSignedHeaderAndNextValidatorSet(sh *types.SignedHeader, valSet *types.ValidatorSet) error
  75. DeleteSignedHeaderAndNextValidatorSet(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. }
  82. ```
  83. At the moment, the only implementation is the `db` store (wrapper around the KV
  84. database, used in Tendermint). In the future, remote adapters are possible
  85. (e.g. `Postgresql`).
  86. ```go
  87. func Verify(
  88. chainID string,
  89. h1 *types.SignedHeader,
  90. h1NextVals *types.ValidatorSet,
  91. h2 *types.SignedHeader,
  92. h2Vals *types.ValidatorSet,
  93. trustingPeriod time.Duration,
  94. now time.Time,
  95. trustLevel tmmath.Fraction) error {
  96. ```
  97. `Verify` pure function is exposed for a header verification. It handles both
  98. cases of adjacent and non-adjacent headers. In the former case, it compares the
  99. hashes directly (2/3+ signed transition). Otherwise, it verifies 1/3+
  100. (`trustLevel`) of trusted validators are still present in new validators.
  101. ## Status
  102. Accepted.
  103. ## Consequences
  104. ### Positive
  105. * single `Client` struct, which is easy to use
  106. * flexible interfaces for header providers and trusted storage
  107. ### Negative
  108. * `Verify` needs to be aligned with the current spec
  109. ### Neutral
  110. * `Verify` function might be misused (called with non-adjacent headers in
  111. incorrectly implemented sequential verification)