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