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.

258 lines
7.7 KiB

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