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.

216 lines
6.1 KiB

  1. package lite
  2. import (
  3. "bytes"
  4. log "github.com/tendermint/tendermint/libs/log"
  5. lerr "github.com/tendermint/tendermint/lite/errors"
  6. "github.com/tendermint/tendermint/types"
  7. )
  8. var _ Verifier = (*DynamicVerifier)(nil)
  9. // DynamicVerifier implements an auto-updating Verifier. It uses a
  10. // "source" provider to obtain the needed FullCommits to securely sync with
  11. // validator set changes. It stores properly validated data on the
  12. // "trusted" local system.
  13. type DynamicVerifier struct {
  14. logger log.Logger
  15. chainID string
  16. // These are only properly validated data, from local system.
  17. trusted PersistentProvider
  18. // This is a source of new info, like a node rpc, or other import method.
  19. source Provider
  20. }
  21. // NewDynamicVerifier returns a new DynamicVerifier. It uses the
  22. // trusted provider to store validated data and the source provider to
  23. // obtain missing data (e.g. FullCommits).
  24. //
  25. // The trusted provider should a CacheProvider, MemProvider or
  26. // files.Provider. The source provider should be a client.HTTPProvider.
  27. func NewDynamicVerifier(chainID string, trusted PersistentProvider, source Provider) *DynamicVerifier {
  28. return &DynamicVerifier{
  29. logger: log.NewNopLogger(),
  30. chainID: chainID,
  31. trusted: trusted,
  32. source: source,
  33. }
  34. }
  35. func (ic *DynamicVerifier) SetLogger(logger log.Logger) {
  36. logger = logger.With("module", "lite")
  37. ic.logger = logger
  38. ic.trusted.SetLogger(logger)
  39. ic.source.SetLogger(logger)
  40. }
  41. // Implements Verifier.
  42. func (ic *DynamicVerifier) ChainID() string {
  43. return ic.chainID
  44. }
  45. // Implements Verifier.
  46. //
  47. // If the validators have changed since the last known time, it looks to
  48. // ic.trusted and ic.source to prove the new validators. On success, it will
  49. // try to store the SignedHeader in ic.trusted if the next
  50. // validator can be sourced.
  51. func (ic *DynamicVerifier) Certify(shdr types.SignedHeader) error {
  52. // Get the latest known full commit <= h-1 from our trusted providers.
  53. // The full commit at h-1 contains the valset to sign for h.
  54. h := shdr.Height - 1
  55. trustedFC, err := ic.trusted.LatestFullCommit(ic.chainID, 1, h)
  56. if err != nil {
  57. return err
  58. }
  59. if trustedFC.Height() == h {
  60. // Return error if valset doesn't match.
  61. if !bytes.Equal(
  62. trustedFC.NextValidators.Hash(),
  63. shdr.Header.ValidatorsHash) {
  64. return lerr.ErrUnexpectedValidators(
  65. trustedFC.NextValidators.Hash(),
  66. shdr.Header.ValidatorsHash)
  67. }
  68. } else {
  69. // If valset doesn't match...
  70. if !bytes.Equal(trustedFC.NextValidators.Hash(),
  71. shdr.Header.ValidatorsHash) {
  72. // ... update.
  73. trustedFC, err = ic.updateToHeight(h)
  74. if err != nil {
  75. return err
  76. }
  77. // Return error if valset _still_ doesn't match.
  78. if !bytes.Equal(trustedFC.NextValidators.Hash(),
  79. shdr.Header.ValidatorsHash) {
  80. return lerr.ErrUnexpectedValidators(
  81. trustedFC.NextValidators.Hash(),
  82. shdr.Header.ValidatorsHash)
  83. }
  84. }
  85. }
  86. // Certify the signed header using the matching valset.
  87. cert := NewBaseVerifier(ic.chainID, trustedFC.Height()+1, trustedFC.NextValidators)
  88. err = cert.Certify(shdr)
  89. if err != nil {
  90. return err
  91. }
  92. // Get the next validator set.
  93. nextValset, err := ic.source.ValidatorSet(ic.chainID, shdr.Height+1)
  94. if lerr.IsErrUnknownValidators(err) {
  95. // Ignore this error.
  96. return nil
  97. } else if err != nil {
  98. return err
  99. }
  100. // Create filled FullCommit.
  101. nfc := FullCommit{
  102. SignedHeader: shdr,
  103. Validators: trustedFC.NextValidators,
  104. NextValidators: nextValset,
  105. }
  106. // Validate the full commit. This checks the cryptographic
  107. // signatures of Commit against Validators.
  108. if err := nfc.ValidateFull(ic.chainID); err != nil {
  109. return err
  110. }
  111. // Trust it.
  112. return ic.trusted.SaveFullCommit(nfc)
  113. }
  114. // verifyAndSave will verify if this is a valid source full commit given the
  115. // best match trusted full commit, and if good, persist to ic.trusted.
  116. // Returns ErrTooMuchChange when >2/3 of trustedFC did not sign sourceFC.
  117. // Panics if trustedFC.Height() >= sourceFC.Height().
  118. func (ic *DynamicVerifier) verifyAndSave(trustedFC, sourceFC FullCommit) error {
  119. if trustedFC.Height() >= sourceFC.Height() {
  120. panic("should not happen")
  121. }
  122. err := trustedFC.NextValidators.VerifyFutureCommit(
  123. sourceFC.Validators,
  124. ic.chainID, sourceFC.SignedHeader.Commit.BlockID,
  125. sourceFC.SignedHeader.Height, sourceFC.SignedHeader.Commit,
  126. )
  127. if err != nil {
  128. return err
  129. }
  130. return ic.trusted.SaveFullCommit(sourceFC)
  131. }
  132. // updateToHeight will use divide-and-conquer to find a path to h.
  133. // Returns nil error iff we successfully verify and persist a full commit
  134. // for height h, using repeated applications of bisection if necessary.
  135. //
  136. // Returns ErrCommitNotFound if source provider doesn't have the commit for h.
  137. func (ic *DynamicVerifier) updateToHeight(h int64) (FullCommit, error) {
  138. // Fetch latest full commit from source.
  139. sourceFC, err := ic.source.LatestFullCommit(ic.chainID, h, h)
  140. if err != nil {
  141. return FullCommit{}, err
  142. }
  143. // Validate the full commit. This checks the cryptographic
  144. // signatures of Commit against Validators.
  145. if err := sourceFC.ValidateFull(ic.chainID); err != nil {
  146. return FullCommit{}, err
  147. }
  148. // If sourceFC.Height() != h, we can't do it.
  149. if sourceFC.Height() != h {
  150. return FullCommit{}, lerr.ErrCommitNotFound()
  151. }
  152. FOR_LOOP:
  153. for {
  154. // Fetch latest full commit from trusted.
  155. trustedFC, err := ic.trusted.LatestFullCommit(ic.chainID, 1, h)
  156. if err != nil {
  157. return FullCommit{}, err
  158. }
  159. // We have nothing to do.
  160. if trustedFC.Height() == h {
  161. return trustedFC, nil
  162. }
  163. // Try to update to full commit with checks.
  164. err = ic.verifyAndSave(trustedFC, sourceFC)
  165. if err == nil {
  166. // All good!
  167. return sourceFC, nil
  168. }
  169. // Handle special case when err is ErrTooMuchChange.
  170. if lerr.IsErrTooMuchChange(err) {
  171. // Divide and conquer.
  172. start, end := trustedFC.Height(), sourceFC.Height()
  173. if !(start < end) {
  174. panic("should not happen")
  175. }
  176. mid := (start + end) / 2
  177. _, err = ic.updateToHeight(mid)
  178. if err != nil {
  179. return FullCommit{}, err
  180. }
  181. // If we made it to mid, we retry.
  182. continue FOR_LOOP
  183. }
  184. return FullCommit{}, err
  185. }
  186. }
  187. func (ic *DynamicVerifier) LastTrustedHeight() int64 {
  188. fc, err := ic.trusted.LatestFullCommit(ic.chainID, 1, 1<<63-1)
  189. if err != nil {
  190. panic("should not happen")
  191. }
  192. return fc.Height()
  193. }