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.

103 lines
3.1 KiB

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