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