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.

96 lines
2.7 KiB

  1. package lite
  2. import (
  3. "github.com/tendermint/tendermint/types"
  4. liteErr "github.com/tendermint/tendermint/lite/errors"
  5. )
  6. var _ Certifier = (*DynamicCertifier)(nil)
  7. // DynamicCertifier uses a StaticCertifier for Certify, but adds an
  8. // Update method to allow for a change of validators.
  9. //
  10. // You can pass in a FullCommit with another validator set,
  11. // and if this is a provably secure transition (< 1/3 change,
  12. // sufficient signatures), then it will update the
  13. // validator set for the next Certify call.
  14. // For security, it will only follow validator set changes
  15. // going forward.
  16. type DynamicCertifier struct {
  17. cert *StaticCertifier
  18. lastHeight int64
  19. }
  20. // NewDynamic returns a new dynamic certifier.
  21. func NewDynamicCertifier(chainID string, vals *types.ValidatorSet, height int64) *DynamicCertifier {
  22. return &DynamicCertifier{
  23. cert: NewStaticCertifier(chainID, vals),
  24. lastHeight: height,
  25. }
  26. }
  27. // ChainID returns the chain id of this certifier.
  28. // Implements Certifier.
  29. func (dc *DynamicCertifier) ChainID() string {
  30. return dc.cert.ChainID()
  31. }
  32. // Validators returns the validators of this certifier.
  33. func (dc *DynamicCertifier) Validators() *types.ValidatorSet {
  34. return dc.cert.vSet
  35. }
  36. // Hash returns the hash of this certifier.
  37. func (dc *DynamicCertifier) Hash() []byte {
  38. return dc.cert.Hash()
  39. }
  40. // LastHeight returns the last height of this certifier.
  41. func (dc *DynamicCertifier) LastHeight() int64 {
  42. return dc.lastHeight
  43. }
  44. // Certify will verify whether the commit is valid and will update the height if it is or return an
  45. // error if it is not.
  46. // Implements Certifier.
  47. func (dc *DynamicCertifier) Certify(check Commit) error {
  48. err := dc.cert.Certify(check)
  49. if err == nil {
  50. // update last seen height if input is valid
  51. dc.lastHeight = check.Height()
  52. }
  53. return err
  54. }
  55. // Update will verify if this is a valid change and update
  56. // the certifying validator set if safe to do so.
  57. //
  58. // Returns an error if update is impossible (invalid proof or IsTooMuchChangeErr)
  59. func (dc *DynamicCertifier) Update(fc FullCommit) error {
  60. // ignore all checkpoints in the past -> only to the future
  61. h := fc.Height()
  62. if h <= dc.lastHeight {
  63. return liteErr.ErrPastTime()
  64. }
  65. // first, verify if the input is self-consistent....
  66. err := fc.ValidateBasic(dc.ChainID())
  67. if err != nil {
  68. return err
  69. }
  70. // now, make sure not too much change... meaning this commit
  71. // would be approved by the currently known validator set
  72. // as well as the new set
  73. commit := fc.Commit.Commit
  74. err = dc.Validators().VerifyCommitAny(fc.Validators, dc.ChainID(), commit.BlockID, h, commit)
  75. if err != nil {
  76. return liteErr.ErrTooMuchChange()
  77. }
  78. // looks good, we can update
  79. dc.cert = NewStaticCertifier(dc.ChainID(), fc.Validators)
  80. dc.lastHeight = h
  81. return nil
  82. }