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() }