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.

139 lines
4.0 KiB

  1. /*
  2. Package client defines a provider that uses a rpchttp
  3. to get information, which is used to get new headers
  4. and validators directly from a Tendermint client.
  5. */
  6. package client
  7. import (
  8. "fmt"
  9. log "github.com/tendermint/tendermint/libs/log"
  10. "github.com/tendermint/tendermint/lite"
  11. lerr "github.com/tendermint/tendermint/lite/errors"
  12. rpcclient "github.com/tendermint/tendermint/rpc/client"
  13. rpchttp "github.com/tendermint/tendermint/rpc/client/http"
  14. ctypes "github.com/tendermint/tendermint/rpc/core/types"
  15. "github.com/tendermint/tendermint/types"
  16. )
  17. // SignStatusClient combines a SignClient and StatusClient.
  18. type SignStatusClient interface {
  19. rpcclient.SignClient
  20. rpcclient.StatusClient
  21. }
  22. type provider struct {
  23. logger log.Logger
  24. chainID string
  25. client SignStatusClient
  26. }
  27. // NewProvider implements Provider (but not PersistentProvider).
  28. func NewProvider(chainID string, client SignStatusClient) lite.Provider {
  29. return &provider{
  30. logger: log.NewNopLogger(),
  31. chainID: chainID,
  32. client: client,
  33. }
  34. }
  35. // NewHTTPProvider can connect to a tendermint json-rpc endpoint
  36. // at the given url, and uses that as a read-only provider.
  37. func NewHTTPProvider(chainID, remote string) (lite.Provider, error) {
  38. httpClient, err := rpchttp.New(remote, "/websocket")
  39. if err != nil {
  40. return nil, err
  41. }
  42. return NewProvider(chainID, httpClient), nil
  43. }
  44. // Implements Provider.
  45. func (p *provider) SetLogger(logger log.Logger) {
  46. logger = logger.With("module", "lite/client")
  47. p.logger = logger
  48. }
  49. // StatusClient returns the internal client as a StatusClient
  50. func (p *provider) StatusClient() rpcclient.StatusClient {
  51. return p.client
  52. }
  53. // LatestFullCommit implements Provider.
  54. func (p *provider) LatestFullCommit(chainID string, minHeight, maxHeight int64) (fc lite.FullCommit, err error) {
  55. if chainID != p.chainID {
  56. err = fmt.Errorf("expected chainID %s, got %s", p.chainID, chainID)
  57. return
  58. }
  59. if maxHeight != 0 && maxHeight < minHeight {
  60. err = fmt.Errorf("need maxHeight == 0 or minHeight <= maxHeight, got min %v and max %v",
  61. minHeight, maxHeight)
  62. return
  63. }
  64. commit, err := p.fetchLatestCommit(minHeight, maxHeight)
  65. if err != nil {
  66. return
  67. }
  68. fc, err = p.fillFullCommit(commit.SignedHeader)
  69. return
  70. }
  71. // fetchLatestCommit fetches the latest commit from the client.
  72. func (p *provider) fetchLatestCommit(minHeight int64, maxHeight int64) (*ctypes.ResultCommit, error) {
  73. status, err := p.client.Status()
  74. if err != nil {
  75. return nil, err
  76. }
  77. if status.SyncInfo.LatestBlockHeight < minHeight {
  78. err = fmt.Errorf("provider is at %v but require minHeight=%v",
  79. status.SyncInfo.LatestBlockHeight, minHeight)
  80. return nil, err
  81. }
  82. if maxHeight == 0 {
  83. maxHeight = status.SyncInfo.LatestBlockHeight
  84. } else if status.SyncInfo.LatestBlockHeight < maxHeight {
  85. maxHeight = status.SyncInfo.LatestBlockHeight
  86. }
  87. return p.client.Commit(&maxHeight)
  88. }
  89. // Implements Provider.
  90. func (p *provider) ValidatorSet(chainID string, height int64) (valset *types.ValidatorSet, err error) {
  91. return p.getValidatorSet(chainID, height)
  92. }
  93. func (p *provider) getValidatorSet(chainID string, height int64) (valset *types.ValidatorSet, err error) {
  94. if chainID != p.chainID {
  95. err = fmt.Errorf("expected chainID %s, got %s", p.chainID, chainID)
  96. return
  97. }
  98. if height < 1 {
  99. err = fmt.Errorf("expected height >= 1, got height %v", height)
  100. return
  101. }
  102. res, err := p.client.Validators(&height, 0, 0)
  103. if err != nil {
  104. // TODO pass through other types of errors.
  105. return nil, lerr.ErrUnknownValidators(chainID, height)
  106. }
  107. valset = types.NewValidatorSet(res.Validators)
  108. return
  109. }
  110. // This does no validation.
  111. func (p *provider) fillFullCommit(signedHeader types.SignedHeader) (fc lite.FullCommit, err error) {
  112. // Get the validators.
  113. valset, err := p.getValidatorSet(signedHeader.ChainID, signedHeader.Height)
  114. if err != nil {
  115. return lite.FullCommit{}, err
  116. }
  117. // Get the next validators.
  118. nextValset, err := p.getValidatorSet(signedHeader.ChainID, signedHeader.Height+1)
  119. if err != nil {
  120. return lite.FullCommit{}, err
  121. }
  122. return lite.NewFullCommit(signedHeader, valset, nextValset), nil
  123. }