package lite // Provider is used to get more validators by other means. // // Examples: MemProvider, files.Provider, client.Provider, CacheProvider.... 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 } // NewCacheProvider returns a new provider which wraps multiple other providers. 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 } // GetByHash returns the FullCommit for the hash or an error if the commit is not found. 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 } // LatestCommit returns the latest FullCommit or an error if no commit exists. 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 }