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.

125 lines
3.5 KiB

  1. package certifiers
  2. import (
  3. certerr "github.com/tendermint/tendermint/certifiers/errors"
  4. )
  5. // Provider is used to get more validators by other means
  6. //
  7. // Examples: MemProvider, files.Provider, client.Provider....
  8. type Provider interface {
  9. // StoreCommit saves a FullCommit after we have verified it,
  10. // so we can query for it later. Important for updating our
  11. // store of trusted commits
  12. StoreCommit(fc FullCommit) error
  13. // GetByHeight returns the closest commit with height <= h
  14. GetByHeight(h int) (FullCommit, error)
  15. // GetByHash returns a commit exactly matching this validator hash
  16. GetByHash(hash []byte) (FullCommit, error)
  17. // LatestCommit returns the newest commit stored
  18. LatestCommit() (FullCommit, error)
  19. }
  20. // cacheProvider allows you to place one or more caches in front of a source
  21. // Provider. It runs through them in order until a match is found.
  22. // So you can keep a local cache, and check with the network if
  23. // no data is there.
  24. type cacheProvider struct {
  25. Providers []Provider
  26. }
  27. func NewCacheProvider(providers ...Provider) Provider {
  28. return cacheProvider{
  29. Providers: providers,
  30. }
  31. }
  32. // StoreCommit tries to add the seed to all providers.
  33. //
  34. // Aborts on first error it encounters (closest provider)
  35. func (c cacheProvider) StoreCommit(fc FullCommit) (err error) {
  36. for _, p := range c.Providers {
  37. err = p.StoreCommit(fc)
  38. if err != nil {
  39. break
  40. }
  41. }
  42. return err
  43. }
  44. /*
  45. GetByHeight should return the closest possible match from all providers.
  46. The Cache is usually organized in order from cheapest call (memory)
  47. to most expensive calls (disk/network). However, since GetByHeight returns
  48. a FullCommit at h' <= h, if the memory has a seed at h-10, but the network would
  49. give us the exact match, a naive "stop at first non-error" would hide
  50. the actual desired results.
  51. Thus, we query each provider in order until we find an exact match
  52. or we finished querying them all. If at least one returned a non-error,
  53. then this returns the best match (minimum h-h').
  54. */
  55. func (c cacheProvider) GetByHeight(h int) (fc FullCommit, err error) {
  56. for _, p := range c.Providers {
  57. var tfc FullCommit
  58. tfc, err = p.GetByHeight(h)
  59. if err == nil {
  60. if tfc.Height() > fc.Height() {
  61. fc = tfc
  62. }
  63. if tfc.Height() == h {
  64. break
  65. }
  66. }
  67. }
  68. // even if the last one had an error, if any was a match, this is good
  69. if fc.Height() > 0 {
  70. err = nil
  71. }
  72. return fc, err
  73. }
  74. func (c cacheProvider) GetByHash(hash []byte) (fc FullCommit, err error) {
  75. for _, p := range c.Providers {
  76. fc, err = p.GetByHash(hash)
  77. if err == nil {
  78. break
  79. }
  80. }
  81. return fc, err
  82. }
  83. func (c cacheProvider) LatestCommit() (fc FullCommit, err error) {
  84. for _, p := range c.Providers {
  85. var tfc FullCommit
  86. tfc, err = p.LatestCommit()
  87. if err == nil && tfc.Height() > fc.Height() {
  88. fc = tfc
  89. }
  90. }
  91. // even if the last one had an error, if any was a match, this is good
  92. if fc.Height() > 0 {
  93. err = nil
  94. }
  95. return fc, err
  96. }
  97. // missingProvider doens't store anything, always a miss
  98. // Designed as a mock for testing
  99. type missingProvider struct{}
  100. func NewMissingProvider() Provider {
  101. return missingProvider{}
  102. }
  103. func (missingProvider) StoreCommit(_ FullCommit) error { return nil }
  104. func (missingProvider) GetByHeight(_ int) (FullCommit, error) {
  105. return FullCommit{}, certerr.ErrCommitNotFound()
  106. }
  107. func (missingProvider) GetByHash(_ []byte) (FullCommit, error) {
  108. return FullCommit{}, certerr.ErrCommitNotFound()
  109. }
  110. func (missingProvider) LatestCommit() (FullCommit, error) {
  111. return FullCommit{}, certerr.ErrCommitNotFound()
  112. }