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.

142 lines
3.2 KiB

  1. package certifiers
  2. import (
  3. "github.com/tendermint/tendermint/types"
  4. certerr "github.com/tendermint/tendermint/certifiers/errors"
  5. )
  6. type Inquiring struct {
  7. cert *Dynamic
  8. // These are only properly validated data, from local system
  9. trusted Provider
  10. // This is a source of new info, like a node rpc, or other import method
  11. Source Provider
  12. }
  13. func NewInquiring(chainID string, fc FullCommit, trusted Provider, source Provider) *Inquiring {
  14. // store the data in trusted
  15. trusted.StoreCommit(fc)
  16. return &Inquiring{
  17. cert: NewDynamic(chainID, fc.Validators, fc.Height()),
  18. trusted: trusted,
  19. Source: source,
  20. }
  21. }
  22. func (c *Inquiring) ChainID() string {
  23. return c.cert.ChainID()
  24. }
  25. func (c *Inquiring) Validators() *types.ValidatorSet {
  26. return c.cert.cert.vSet
  27. }
  28. func (c *Inquiring) LastHeight() int {
  29. return c.cert.lastHeight
  30. }
  31. // Certify makes sure this is checkpoint is valid.
  32. //
  33. // If the validators have changed since the last know time, it looks
  34. // for a path to prove the new validators.
  35. //
  36. // On success, it will store the checkpoint in the store for later viewing
  37. func (c *Inquiring) Certify(commit *Commit) error {
  38. err := c.useClosestTrust(commit.Height())
  39. if err != nil {
  40. return err
  41. }
  42. err = c.cert.Certify(commit)
  43. if !certerr.IsValidatorsChangedErr(err) {
  44. return err
  45. }
  46. err = c.updateToHash(commit.Header.ValidatorsHash)
  47. if err != nil {
  48. return err
  49. }
  50. err = c.cert.Certify(commit)
  51. if err != nil {
  52. return err
  53. }
  54. // store the new checkpoint
  55. c.trusted.StoreCommit(
  56. NewFullCommit(commit, c.Validators()))
  57. return nil
  58. }
  59. func (c *Inquiring) Update(fc FullCommit) error {
  60. err := c.useClosestTrust(fc.Height())
  61. if err != nil {
  62. return err
  63. }
  64. err = c.cert.Update(fc)
  65. if err == nil {
  66. c.trusted.StoreCommit(fc)
  67. }
  68. return err
  69. }
  70. func (c *Inquiring) useClosestTrust(h int) error {
  71. closest, err := c.trusted.GetByHeight(h)
  72. if err != nil {
  73. return err
  74. }
  75. // if the best seed is not the one we currently use,
  76. // let's just reset the dynamic validator
  77. if closest.Height() != c.LastHeight() {
  78. c.cert = NewDynamic(c.ChainID(), closest.Validators, closest.Height())
  79. }
  80. return nil
  81. }
  82. // updateToHash gets the validator hash we want to update to
  83. // if IsTooMuchChangeErr, we try to find a path by binary search over height
  84. func (c *Inquiring) updateToHash(vhash []byte) error {
  85. // try to get the match, and update
  86. fc, err := c.Source.GetByHash(vhash)
  87. if err != nil {
  88. return err
  89. }
  90. err = c.cert.Update(fc)
  91. // handle IsTooMuchChangeErr by using divide and conquer
  92. if certerr.IsTooMuchChangeErr(err) {
  93. err = c.updateToHeight(fc.Height())
  94. }
  95. return err
  96. }
  97. // updateToHeight will use divide-and-conquer to find a path to h
  98. func (c *Inquiring) updateToHeight(h int) error {
  99. // try to update to this height (with checks)
  100. fc, err := c.Source.GetByHeight(h)
  101. if err != nil {
  102. return err
  103. }
  104. start, end := c.LastHeight(), fc.Height()
  105. if end <= start {
  106. return certerr.ErrNoPathFound()
  107. }
  108. err = c.Update(fc)
  109. // we can handle IsTooMuchChangeErr specially
  110. if !certerr.IsTooMuchChangeErr(err) {
  111. return err
  112. }
  113. // try to update to mid
  114. mid := (start + end) / 2
  115. err = c.updateToHeight(mid)
  116. if err != nil {
  117. return err
  118. }
  119. // if we made it to mid, we recurse
  120. return c.updateToHeight(h)
  121. }