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.

443 lines
13 KiB

lite2: light client with weak subjectivity (#3989) Refs #1771 ADR: https://github.com/tendermint/tendermint/blob/master/docs/architecture/adr-044-lite-client-with-weak-subjectivity.md ## Commits: * add Verifier and VerifyCommitTrusting * add two more checks make trustLevel an option * float32 for trustLevel * check newHeader time * started writing lite Client * unify Verify methods * ensure h2.Header.bfttime < h1.Header.bfttime + tp * move trust checks into Verify function * add more comments * more docs * started writing tests * unbonding period failures * tests are green * export ErrNewHeaderTooFarIntoFuture * make golangci happy * test for non-adjusted headers * more precision * providers and stores * VerifyHeader and VerifyHeaderAtHeight funcs * fix compile errors * remove lastVerifiedHeight, persist new trusted header * sequential verification * remove TrustedStore option * started writing tests for light client * cover basic cases for linear verification * bisection tests PASS * rename BisectingVerification to SkippingVerification * refactor the code * add TrustedHeader method * consolidate sequential verification tests * consolidate skipping verification tests * rename trustedVals to trustedNextVals * start writing docs * ValidateTrustLevel func and ErrOldHeaderExpired error * AutoClient and example tests * fix errors * update doc * remove ErrNewHeaderTooFarIntoFuture This check is unnecessary given existing a) ErrOldHeaderExpired b) h2.Time > now checks. * return an error if we're at more recent height * add comments * add LastSignedHeaderHeight method to Store I think it's fine if Store tracks last height * copy over proxy from old lite package * make TrustedHeader return latest if height=0 * modify LastSignedHeaderHeight to return an error if no headers exist * copy over proxy impl * refactor proxy and start http lite client * Tx and BlockchainInfo methods * Block method * commit method * code compiles again * lite client compiles * extract updateLiteClientIfNeededTo func * move final parts * add placeholder for tests * force usage of lite http client in proxy * comment out query tests for now * explicitly mention tp: trusting period * verify nextVals in VerifyHeader * refactor bisection * move the NextValidatorsHash check into updateTrustedHeaderAndVals + update the comment * add ConsensusParams method to RPC client * add ConsensusParams to rpc/mock/client * change trustLevel type to a new cmn.Fraction type + update SkippingVerification comment * stress out trustLevel is only used for non-adjusted headers * fixes after Fede's review Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com> * compare newHeader with a header from an alternative provider * save pivot header Refs https://github.com/tendermint/tendermint/pull/3989#discussion_r349122824 * check header can still be trusted in TrustedHeader Refs https://github.com/tendermint/tendermint/pull/3989#discussion_r349101424 * lite: update Validators and Block endpoints - Block no longer contains BlockMeta - Validators now accept two additional params: page and perPage * make linter happy
5 years ago
  1. package lite
  2. import (
  3. "bytes"
  4. "fmt"
  5. "time"
  6. "github.com/pkg/errors"
  7. cmn "github.com/tendermint/tendermint/libs/common"
  8. "github.com/tendermint/tendermint/libs/log"
  9. "github.com/tendermint/tendermint/lite2/provider"
  10. "github.com/tendermint/tendermint/lite2/store"
  11. "github.com/tendermint/tendermint/types"
  12. )
  13. // TrustOptions are the trust parameters needed when a new light client
  14. // connects to the network or when an existing light client that has been
  15. // offline for longer than the trusting period connects to the network.
  16. //
  17. // The expectation is the user will get this information from a trusted source
  18. // like a validator, a friend, or a secure website. A more user friendly
  19. // solution with trust tradeoffs is that we establish an https based protocol
  20. // with a default end point that populates this information. Also an on-chain
  21. // registry of roots-of-trust (e.g. on the Cosmos Hub) seems likely in the
  22. // future.
  23. type TrustOptions struct {
  24. // tp: trusting period.
  25. //
  26. // Should be significantly less than the unbonding period (e.g. unbonding
  27. // period = 3 weeks, trusting period = 2 weeks).
  28. //
  29. // More specifically, trusting period + time needed to check headers + time
  30. // needed to report and punish misbehavior should be less than the unbonding
  31. // period.
  32. Period time.Duration
  33. // Header's Height and Hash must both be provided to force the trusting of a
  34. // particular header.
  35. Height int64
  36. Hash []byte
  37. }
  38. type mode byte
  39. const (
  40. sequential mode = iota + 1
  41. skipping
  42. )
  43. // Option sets a parameter for the light client.
  44. type Option func(*Client)
  45. // SequentialVerification option configures the light client to sequentially
  46. // check the headers. Note this is much slower than SkippingVerification,
  47. // albeit more secure.
  48. func SequentialVerification() Option {
  49. return func(c *Client) {
  50. c.verificationMode = sequential
  51. }
  52. }
  53. // SkippingVerification option configures the light client to skip headers as
  54. // long as {trustLevel} of the old validator set signed the new header. The
  55. // bisection algorithm from the specification is used for finding the minimal
  56. // "trust path".
  57. //
  58. // trustLevel - fraction of the old validator set (in terms of voting power),
  59. // which must sign the new header in order for us to trust it. NOTE this only
  60. // applies to non-adjusted headers. For adjusted headers, sequential
  61. // verification is used.
  62. func SkippingVerification(trustLevel cmn.Fraction) Option {
  63. if err := ValidateTrustLevel(trustLevel); err != nil {
  64. panic(err)
  65. }
  66. return func(c *Client) {
  67. c.verificationMode = skipping
  68. c.trustLevel = trustLevel
  69. }
  70. }
  71. // AlternativeSources option can be used to supply alternative providers, which
  72. // will be used for cross-checking the primary provider.
  73. func AlternativeSources(providers []provider.Provider) Option {
  74. return func(c *Client) {
  75. c.alternatives = providers
  76. }
  77. }
  78. // Client represents a light client, connected to a single chain, which gets
  79. // headers from a primary provider, verifies them either sequentially or by
  80. // skipping some and stores them in a trusted store (usually, a local FS).
  81. //
  82. // Default verification: SkippingVerification(DefaultTrustLevel)
  83. type Client struct {
  84. chainID string
  85. trustingPeriod time.Duration // see TrustOptions.Period
  86. verificationMode mode
  87. trustLevel cmn.Fraction
  88. // Primary provider of new headers.
  89. primary provider.Provider
  90. // Alternative providers for checking the primary for misbehavior by
  91. // comparing data.
  92. alternatives []provider.Provider
  93. // Where trusted headers are stored.
  94. trustedStore store.Store
  95. // Highest trusted header from the store (height=H).
  96. trustedHeader *types.SignedHeader
  97. // Highest next validator set from the store (height=H+1).
  98. trustedNextVals *types.ValidatorSet
  99. logger log.Logger
  100. }
  101. // NewClient returns a new light client. It returns an error if it fails to
  102. // obtain the header & vals from the primary or they are invalid (e.g. trust
  103. // hash does not match with the one from the header).
  104. //
  105. // See all Option(s) for the additional configuration.
  106. func NewClient(
  107. chainID string,
  108. trustOptions TrustOptions,
  109. primary provider.Provider,
  110. trustedStore store.Store,
  111. options ...Option) (*Client, error) {
  112. c := &Client{
  113. chainID: chainID,
  114. trustingPeriod: trustOptions.Period,
  115. verificationMode: skipping,
  116. trustLevel: DefaultTrustLevel,
  117. primary: primary,
  118. trustedStore: trustedStore,
  119. logger: log.NewNopLogger(),
  120. }
  121. for _, o := range options {
  122. o(c)
  123. }
  124. if err := c.initializeWithTrustOptions(trustOptions); err != nil {
  125. return nil, err
  126. }
  127. return c, nil
  128. }
  129. func (c *Client) initializeWithTrustOptions(options TrustOptions) error {
  130. // 1) Fetch and verify the header.
  131. h, err := c.primary.SignedHeader(options.Height)
  132. if err != nil {
  133. return err
  134. }
  135. // NOTE: Verify func will check if it's expired or not.
  136. if err := h.ValidateBasic(c.chainID); err != nil {
  137. return errors.Wrap(err, "ValidateBasic failed")
  138. }
  139. if !bytes.Equal(h.Hash(), options.Hash) {
  140. return errors.Errorf("expected header's hash %X, but got %X", options.Hash, h.Hash())
  141. }
  142. // 2) Fetch and verify the next vals.
  143. vals, err := c.primary.ValidatorSet(options.Height + 1)
  144. if err != nil {
  145. return err
  146. }
  147. // 3) Persist both of them and continue.
  148. return c.updateTrustedHeaderAndVals(h, vals)
  149. }
  150. // SetLogger sets a logger.
  151. func (c *Client) SetLogger(l log.Logger) {
  152. c.logger = l
  153. }
  154. // TrustedHeader returns a trusted header at the given height (0 - the latest)
  155. // or nil if no such header exist.
  156. // TODO: mention how many headers will be kept by the light client.
  157. // .
  158. // height must be >= 0.
  159. //
  160. // It returns an error if:
  161. // - the header expired (ErrOldHeaderExpired). In that case, update your
  162. // client to more recent height;
  163. // - there are some issues with the trusted store, although that should not
  164. // happen normally.
  165. func (c *Client) TrustedHeader(height int64, now time.Time) (*types.SignedHeader, error) {
  166. if height < 0 {
  167. return nil, errors.New("negative height")
  168. }
  169. if height == 0 {
  170. var err error
  171. height, err = c.LastTrustedHeight()
  172. if err != nil {
  173. return nil, err
  174. }
  175. }
  176. h, err := c.trustedStore.SignedHeader(height)
  177. if err != nil {
  178. return nil, err
  179. }
  180. // Ensure header can still be trusted.
  181. expirationTime := h.Time.Add(c.trustingPeriod)
  182. if !expirationTime.After(now) {
  183. return nil, ErrOldHeaderExpired{expirationTime, now}
  184. }
  185. return h, nil
  186. }
  187. // LastTrustedHeight returns a last trusted height.
  188. func (c *Client) LastTrustedHeight() (int64, error) {
  189. return c.trustedStore.LastSignedHeaderHeight()
  190. }
  191. // ChainID returns the chain ID.
  192. func (c *Client) ChainID() string {
  193. return c.chainID
  194. }
  195. // VerifyHeaderAtHeight fetches the header and validators at the given height
  196. // and calls VerifyHeader.
  197. //
  198. // If the trusted header is more recent than one here, an error is returned.
  199. func (c *Client) VerifyHeaderAtHeight(height int64, now time.Time) error {
  200. if c.trustedHeader.Height >= height {
  201. return errors.Errorf("height #%d is already trusted (last: #%d)", height, c.trustedHeader.Height)
  202. }
  203. // Request the header and the vals.
  204. newHeader, newVals, err := c.fetchHeaderAndValsAtHeight(height)
  205. if err != nil {
  206. return err
  207. }
  208. return c.VerifyHeader(newHeader, newVals, now)
  209. }
  210. // VerifyHeader verifies new header against the trusted state.
  211. //
  212. // SequentialVerification: verifies that 2/3 of the trusted validator set has
  213. // signed the new header. If the headers are not adjacent, **all** intermediate
  214. // headers will be requested.
  215. //
  216. // SkippingVerification(trustLevel): verifies that {trustLevel} of the trusted
  217. // validator set has signed the new header. If it's not the case and the
  218. // headers are not adjacent, bisection is performed and necessary (not all)
  219. // intermediate headers will be requested. See the specification for the
  220. // algorithm.
  221. //
  222. // If the trusted header is more recent than one here, an error is returned.
  223. func (c *Client) VerifyHeader(newHeader *types.SignedHeader, newVals *types.ValidatorSet, now time.Time) error {
  224. if c.trustedHeader.Height >= newHeader.Height {
  225. return errors.Errorf("height #%d is already trusted (last: #%d)", newHeader.Height, c.trustedHeader.Height)
  226. }
  227. if len(c.alternatives) > 0 {
  228. if err := c.compareNewHeaderWithRandomAlternative(newHeader); err != nil {
  229. return err
  230. }
  231. }
  232. var err error
  233. switch c.verificationMode {
  234. case sequential:
  235. err = c.sequence(newHeader, newVals, now)
  236. case skipping:
  237. err = c.bisection(c.trustedHeader, c.trustedNextVals, newHeader, newVals, now)
  238. default:
  239. panic(fmt.Sprintf("Unknown verification mode: %b", c.verificationMode))
  240. }
  241. if err != nil {
  242. return err
  243. }
  244. // Update trusted header and vals.
  245. nextVals, err := c.primary.ValidatorSet(newHeader.Height + 1)
  246. if err != nil {
  247. return err
  248. }
  249. return c.updateTrustedHeaderAndVals(newHeader, nextVals)
  250. }
  251. func (c *Client) sequence(newHeader *types.SignedHeader, newVals *types.ValidatorSet, now time.Time) error {
  252. // 1) Verify any intermediate headers.
  253. var (
  254. interimHeader *types.SignedHeader
  255. nextVals *types.ValidatorSet
  256. err error
  257. )
  258. for height := c.trustedHeader.Height + 1; height < newHeader.Height; height++ {
  259. interimHeader, err = c.primary.SignedHeader(height)
  260. if err != nil {
  261. return errors.Wrapf(err, "failed to obtain the header #%d", height)
  262. }
  263. err = Verify(c.chainID, c.trustedHeader, c.trustedNextVals, interimHeader, c.trustedNextVals,
  264. c.trustingPeriod, now, c.trustLevel)
  265. if err != nil {
  266. return errors.Wrapf(err, "failed to verify the header #%d", height)
  267. }
  268. // Update trusted header and vals.
  269. if height == newHeader.Height-1 {
  270. nextVals = newVals
  271. } else {
  272. nextVals, err = c.primary.ValidatorSet(height + 1)
  273. if err != nil {
  274. return errors.Wrapf(err, "failed to obtain the vals #%d", height+1)
  275. }
  276. }
  277. err = c.updateTrustedHeaderAndVals(interimHeader, nextVals)
  278. if err != nil {
  279. return errors.Wrapf(err, "failed to update trusted state #%d", height)
  280. }
  281. }
  282. // 2) Verify the new header.
  283. return Verify(c.chainID, c.trustedHeader, c.trustedNextVals, newHeader, newVals, c.trustingPeriod, now, c.trustLevel)
  284. }
  285. func (c *Client) bisection(
  286. lastHeader *types.SignedHeader,
  287. lastVals *types.ValidatorSet,
  288. newHeader *types.SignedHeader,
  289. newVals *types.ValidatorSet,
  290. now time.Time) error {
  291. err := Verify(c.chainID, lastHeader, lastVals, newHeader, newVals, c.trustingPeriod, now, c.trustLevel)
  292. switch err.(type) {
  293. case nil:
  294. return nil
  295. case types.ErrTooMuchChange:
  296. // continue bisection
  297. default:
  298. return errors.Wrapf(err, "failed to verify the header #%d ", newHeader.Height)
  299. }
  300. if newHeader.Height == c.trustedHeader.Height+1 {
  301. // TODO: submit evidence here
  302. return errors.Errorf("adjacent headers (#%d and #%d) that are not matching", lastHeader.Height, newHeader.Height)
  303. }
  304. pivot := (c.trustedHeader.Height + newHeader.Header.Height) / 2
  305. pivotHeader, pivotVals, err := c.fetchHeaderAndValsAtHeight(pivot)
  306. if err != nil {
  307. return err
  308. }
  309. // left branch
  310. {
  311. err := c.bisection(lastHeader, lastVals, pivotHeader, pivotVals, now)
  312. if err != nil {
  313. return errors.Wrapf(err, "bisection of #%d and #%d", lastHeader.Height, pivot)
  314. }
  315. }
  316. // right branch
  317. {
  318. nextVals, err := c.primary.ValidatorSet(pivot + 1)
  319. if err != nil {
  320. return errors.Wrapf(err, "failed to obtain the vals #%d", pivot+1)
  321. }
  322. if !bytes.Equal(pivotHeader.NextValidatorsHash, nextVals.Hash()) {
  323. return errors.Errorf("expected next validator's hash %X, but got %X (height #%d)",
  324. pivotHeader.NextValidatorsHash,
  325. nextVals.Hash(),
  326. pivot)
  327. }
  328. err = c.updateTrustedHeaderAndVals(pivotHeader, nextVals)
  329. if err != nil {
  330. return errors.Wrapf(err, "failed to update trusted state #%d", pivot)
  331. }
  332. err = c.bisection(pivotHeader, nextVals, newHeader, newVals, now)
  333. if err != nil {
  334. return errors.Wrapf(err, "bisection of #%d and #%d", pivot, newHeader.Height)
  335. }
  336. }
  337. return nil
  338. }
  339. func (c *Client) updateTrustedHeaderAndVals(h *types.SignedHeader, vals *types.ValidatorSet) error {
  340. if !bytes.Equal(h.NextValidatorsHash, vals.Hash()) {
  341. return errors.Errorf("expected next validator's hash %X, but got %X", h.NextValidatorsHash, vals.Hash())
  342. }
  343. if err := c.trustedStore.SaveSignedHeader(h); err != nil {
  344. return errors.Wrap(err, "failed to save trusted header")
  345. }
  346. if err := c.trustedStore.SaveValidatorSet(vals, h.Height+1); err != nil {
  347. return errors.Wrap(err, "failed to save trusted vals")
  348. }
  349. c.trustedHeader = h
  350. c.trustedNextVals = vals
  351. return nil
  352. }
  353. func (c *Client) fetchHeaderAndValsAtHeight(height int64) (*types.SignedHeader, *types.ValidatorSet, error) {
  354. h, err := c.primary.SignedHeader(height)
  355. if err != nil {
  356. return nil, nil, errors.Wrapf(err, "failed to obtain the header #%d", height)
  357. }
  358. vals, err := c.primary.ValidatorSet(height)
  359. if err != nil {
  360. return nil, nil, errors.Wrapf(err, "failed to obtain the vals #%d", height)
  361. }
  362. return h, vals, nil
  363. }
  364. func (c *Client) compareNewHeaderWithRandomAlternative(h *types.SignedHeader) error {
  365. // 1. Pick an alternative provider.
  366. p := c.alternatives[cmn.RandIntn(len(c.alternatives))]
  367. // 2. Fetch the header.
  368. altHeader, err := p.SignedHeader(h.Height)
  369. if err != nil {
  370. return errors.Wrapf(err,
  371. "failed to obtain header #%d from alternative provider %v", h.Height, p)
  372. }
  373. // 3. Compare hashes.
  374. if !bytes.Equal(h.Hash(), altHeader.Hash()) {
  375. // TODO: One of the providers is lying. Send the evidence to fork
  376. // accountability server.
  377. return errors.Errorf(
  378. "new header hash %X does not match one from alternative provider %X",
  379. h.Hash(), altHeader.Hash())
  380. }
  381. return nil
  382. }