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.

133 lines
3.6 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. // StoreCommit is a noop, as clients can only read from the chain...
  36. func (p *provider) StoreCommit(_ certifiers.FullCommit) error { return nil }
  37. // GetHash gets the most recent validator and sees if it matches
  38. //
  39. // TODO: improve when the rpc interface supports more functionality
  40. func (p *provider) GetByHash(hash []byte) (certifiers.FullCommit, error) {
  41. var fc certifiers.FullCommit
  42. vals, err := p.node.Validators(nil)
  43. // if we get no validators, or a different height, return an error
  44. if err != nil {
  45. return fc, err
  46. }
  47. p.updateHeight(vals.BlockHeight)
  48. vhash := types.NewValidatorSet(vals.Validators).Hash()
  49. if !bytes.Equal(hash, vhash) {
  50. return fc, certerr.ErrCommitNotFound()
  51. }
  52. return p.seedFromVals(vals)
  53. }
  54. // GetByHeight gets the validator set by height
  55. func (p *provider) GetByHeight(h int) (fc certifiers.FullCommit, err error) {
  56. commit, err := p.node.Commit(&h)
  57. if err != nil {
  58. return fc, err
  59. }
  60. return p.seedFromCommit(commit)
  61. }
  62. func (p *provider) LatestCommit() (fc certifiers.FullCommit, err error) {
  63. commit, err := p.GetLatestCommit()
  64. if err != nil {
  65. return fc, err
  66. }
  67. return p.seedFromCommit(commit)
  68. }
  69. // GetLatestCommit should return the most recent commit there is,
  70. // which handles queries for future heights as per the semantics
  71. // of GetByHeight.
  72. func (p *provider) GetLatestCommit() (*ctypes.ResultCommit, error) {
  73. status, err := p.node.Status()
  74. if err != nil {
  75. return nil, err
  76. }
  77. return p.node.Commit(&status.LatestBlockHeight)
  78. }
  79. func CommitFromResult(result *ctypes.ResultCommit) certifiers.Commit {
  80. return (certifiers.Commit)(result.SignedHeader)
  81. }
  82. func (p *provider) seedFromVals(vals *ctypes.ResultValidators) (certifiers.FullCommit, error) {
  83. // now get the commits and build a full commit
  84. commit, err := p.node.Commit(&vals.BlockHeight)
  85. if err != nil {
  86. return certifiers.FullCommit{}, err
  87. }
  88. fc := certifiers.NewFullCommit(
  89. CommitFromResult(commit),
  90. types.NewValidatorSet(vals.Validators),
  91. )
  92. return fc, nil
  93. }
  94. func (p *provider) seedFromCommit(commit *ctypes.ResultCommit) (fc certifiers.FullCommit, err error) {
  95. fc.Commit = CommitFromResult(commit)
  96. // now get the proper validators
  97. vals, err := p.node.Validators(&commit.Header.Height)
  98. if err != nil {
  99. return fc, err
  100. }
  101. // make sure they match the commit (as we cannot enforce height)
  102. vset := types.NewValidatorSet(vals.Validators)
  103. if !bytes.Equal(vset.Hash(), commit.Header.ValidatorsHash) {
  104. return fc, certerr.ErrValidatorsChanged()
  105. }
  106. p.updateHeight(commit.Header.Height)
  107. fc.Validators = vset
  108. return fc, nil
  109. }
  110. func (p *provider) updateHeight(h int) {
  111. if h > p.lastHeight {
  112. p.lastHeight = h
  113. }
  114. }