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.

141 lines
3.8 KiB

  1. /*
  2. Package client defines a provider that uses a rpcclient
  3. to get information, which is used to get new headers
  4. and validators directly from a node.
  5. */
  6. package client
  7. import (
  8. "bytes"
  9. rpcclient "github.com/tendermint/tendermint/rpc/client"
  10. ctypes "github.com/tendermint/tendermint/rpc/core/types"
  11. "github.com/tendermint/tendermint/types"
  12. "github.com/tendermint/tendermint/lite"
  13. liteErr "github.com/tendermint/tendermint/lite/errors"
  14. )
  15. // SignStatusClient combines a SignClient and StatusClient.
  16. type SignStatusClient interface {
  17. rpcclient.SignClient
  18. rpcclient.StatusClient
  19. }
  20. type provider struct {
  21. node SignStatusClient
  22. lastHeight int64
  23. }
  24. // NewProvider can wrap any rpcclient to expose it as
  25. // a read-only provider.
  26. func NewProvider(node SignStatusClient) lite.Provider {
  27. return &provider{node: node}
  28. }
  29. // NewHTTPProvider can connect to a tendermint json-rpc endpoint
  30. // at the given url, and uses that as a read-only provider.
  31. func NewHTTPProvider(remote string) lite.Provider {
  32. return &provider{
  33. node: rpcclient.NewHTTP(remote, "/websocket"),
  34. }
  35. }
  36. // StatusClient returns the internal node as a StatusClient
  37. func (p *provider) StatusClient() rpcclient.StatusClient {
  38. return p.node
  39. }
  40. // StoreCommit is a noop, as clients can only read from the chain...
  41. func (p *provider) StoreCommit(_ lite.FullCommit) error { return nil }
  42. // GetHash gets the most recent validator and sees if it matches
  43. //
  44. // TODO: improve when the rpc interface supports more functionality
  45. func (p *provider) GetByHash(hash []byte) (lite.FullCommit, error) {
  46. var fc lite.FullCommit
  47. vals, err := p.node.Validators(nil)
  48. // if we get no validators, or a different height, return an error
  49. if err != nil {
  50. return fc, err
  51. }
  52. p.updateHeight(vals.BlockHeight)
  53. vhash := types.NewValidatorSet(vals.Validators).Hash()
  54. if !bytes.Equal(hash, vhash) {
  55. return fc, liteErr.ErrCommitNotFound()
  56. }
  57. return p.seedFromVals(vals)
  58. }
  59. // GetByHeight gets the validator set by height
  60. func (p *provider) GetByHeight(h int64) (fc lite.FullCommit, err error) {
  61. commit, err := p.node.Commit(&h)
  62. if err != nil {
  63. return fc, err
  64. }
  65. return p.seedFromCommit(commit)
  66. }
  67. // LatestCommit returns the newest commit stored.
  68. func (p *provider) LatestCommit() (fc lite.FullCommit, err error) {
  69. commit, err := p.GetLatestCommit()
  70. if err != nil {
  71. return fc, err
  72. }
  73. return p.seedFromCommit(commit)
  74. }
  75. // GetLatestCommit should return the most recent commit there is,
  76. // which handles queries for future heights as per the semantics
  77. // of GetByHeight.
  78. func (p *provider) GetLatestCommit() (*ctypes.ResultCommit, error) {
  79. status, err := p.node.Status()
  80. if err != nil {
  81. return nil, err
  82. }
  83. return p.node.Commit(&status.SyncInfo.LatestBlockHeight)
  84. }
  85. // CommitFromResult ...
  86. func CommitFromResult(result *ctypes.ResultCommit) lite.Commit {
  87. return (lite.Commit)(result.SignedHeader)
  88. }
  89. func (p *provider) seedFromVals(vals *ctypes.ResultValidators) (lite.FullCommit, error) {
  90. // now get the commits and build a full commit
  91. commit, err := p.node.Commit(&vals.BlockHeight)
  92. if err != nil {
  93. return lite.FullCommit{}, err
  94. }
  95. fc := lite.NewFullCommit(
  96. CommitFromResult(commit),
  97. types.NewValidatorSet(vals.Validators),
  98. )
  99. return fc, nil
  100. }
  101. func (p *provider) seedFromCommit(commit *ctypes.ResultCommit) (fc lite.FullCommit, err error) {
  102. fc.Commit = CommitFromResult(commit)
  103. // now get the proper validators
  104. vals, err := p.node.Validators(&commit.Header.Height)
  105. if err != nil {
  106. return fc, err
  107. }
  108. // make sure they match the commit (as we cannot enforce height)
  109. vset := types.NewValidatorSet(vals.Validators)
  110. if !bytes.Equal(vset.Hash(), commit.Header.ValidatorsHash) {
  111. return fc, liteErr.ErrValidatorsChanged()
  112. }
  113. p.updateHeight(commit.Header.Height)
  114. fc.Validators = vset
  115. return fc, nil
  116. }
  117. func (p *provider) updateHeight(h int64) {
  118. if h > p.lastHeight {
  119. p.lastHeight = h
  120. }
  121. }