|
|
- package certifiers
-
- import (
- certerr "github.com/tendermint/tendermint/certifiers/errors"
- )
-
- // Provider is used to get more validators by other means
- //
- // Examples: MemProvider, files.Provider, client.Provider....
- type Provider interface {
- // StoreCommit saves a FullCommit after we have verified it,
- // so we can query for it later. Important for updating our
- // store of trusted commits
- StoreCommit(fc FullCommit) error
- // GetByHeight returns the closest commit with height <= h
- GetByHeight(h int) (FullCommit, error)
- // GetByHash returns a commit exactly matching this validator hash
- GetByHash(hash []byte) (FullCommit, error)
- // LatestCommit returns the newest commit stored
- LatestCommit() (FullCommit, error)
- }
-
- // cacheProvider allows you to place one or more caches in front of a source
- // Provider. It runs through them in order until a match is found.
- // So you can keep a local cache, and check with the network if
- // no data is there.
- type cacheProvider struct {
- Providers []Provider
- }
-
- func NewCacheProvider(providers ...Provider) Provider {
- return cacheProvider{
- Providers: providers,
- }
- }
-
- // StoreCommit tries to add the seed to all providers.
- //
- // Aborts on first error it encounters (closest provider)
- func (c cacheProvider) StoreCommit(fc FullCommit) (err error) {
- for _, p := range c.Providers {
- err = p.StoreCommit(fc)
- if err != nil {
- break
- }
- }
- return err
- }
-
- /*
- GetByHeight should return the closest possible match from all providers.
-
- The Cache is usually organized in order from cheapest call (memory)
- to most expensive calls (disk/network). However, since GetByHeight returns
- a FullCommit at h' <= h, if the memory has a seed at h-10, but the network would
- give us the exact match, a naive "stop at first non-error" would hide
- the actual desired results.
-
- Thus, we query each provider in order until we find an exact match
- or we finished querying them all. If at least one returned a non-error,
- then this returns the best match (minimum h-h').
- */
- func (c cacheProvider) GetByHeight(h int) (fc FullCommit, err error) {
- for _, p := range c.Providers {
- var tfc FullCommit
- tfc, err = p.GetByHeight(h)
- if err == nil {
- if tfc.Height() > fc.Height() {
- fc = tfc
- }
- if tfc.Height() == h {
- break
- }
- }
- }
- // even if the last one had an error, if any was a match, this is good
- if fc.Height() > 0 {
- err = nil
- }
- return fc, err
- }
-
- func (c cacheProvider) GetByHash(hash []byte) (fc FullCommit, err error) {
- for _, p := range c.Providers {
- fc, err = p.GetByHash(hash)
- if err == nil {
- break
- }
- }
- return fc, err
- }
-
- func (c cacheProvider) LatestCommit() (fc FullCommit, err error) {
- for _, p := range c.Providers {
- var tfc FullCommit
- tfc, err = p.LatestCommit()
- if err == nil && tfc.Height() > fc.Height() {
- fc = tfc
- }
- }
- // even if the last one had an error, if any was a match, this is good
- if fc.Height() > 0 {
- err = nil
- }
- return fc, err
- }
-
- // missingProvider doens't store anything, always a miss
- // Designed as a mock for testing
- type missingProvider struct{}
-
- func NewMissingProvider() Provider {
- return missingProvider{}
- }
-
- func (missingProvider) StoreCommit(_ FullCommit) error { return nil }
- func (missingProvider) GetByHeight(_ int) (FullCommit, error) {
- return FullCommit{}, certerr.ErrCommitNotFound()
- }
- func (missingProvider) GetByHash(_ []byte) (FullCommit, error) {
- return FullCommit{}, certerr.ErrCommitNotFound()
- }
- func (missingProvider) LatestCommit() (FullCommit, error) {
- return FullCommit{}, certerr.ErrCommitNotFound()
- }
|