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.

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