light: rename lite2 to light & remove lite (#4946)

This PR removes lite & renames lite2 to light throughout the repo

Signed-off-by: Marko Baricevic <marbar3778@yahoo.com>

Closes: #4944
This commit is contained in:
Marko
2020-06-03 12:13:42 +02:00
committed by GitHub
parent 395d58c27c
commit c2578e2262
63 changed files with 204 additions and 3275 deletions

12
light/provider/errors.go Normal file
View File

@@ -0,0 +1,12 @@
package provider
import "errors"
var (
// ErrSignedHeaderNotFound is returned when a provider can't find the
// requested header.
ErrSignedHeaderNotFound = errors.New("signed header not found")
// ErrValidatorSetNotFound is returned when a provider can't find the
// requested validator set.
ErrValidatorSetNotFound = errors.New("validator set not found")
)

141
light/provider/http/http.go Normal file
View File

@@ -0,0 +1,141 @@
package http
import (
"errors"
"fmt"
"regexp"
"strings"
"github.com/tendermint/tendermint/light/provider"
rpcclient "github.com/tendermint/tendermint/rpc/client"
rpchttp "github.com/tendermint/tendermint/rpc/client/http"
"github.com/tendermint/tendermint/types"
)
// This is very brittle, see: https://github.com/tendermint/tendermint/issues/4740
var regexpMissingHeight = regexp.MustCompile(`height \d+ (must be less than or equal to|is not available)`)
// http provider uses an RPC client to obtain the necessary information.
type http struct {
chainID string
client rpcclient.RemoteClient
}
// New creates a HTTP provider, which is using the rpchttp.HTTP client under
// the hood. If no scheme is provided in the remote URL, http will be used by
// default.
func New(chainID, remote string) (provider.Provider, error) {
// Ensure URL scheme is set (default HTTP) when not provided.
if !strings.Contains(remote, "://") {
remote = "http://" + remote
}
httpClient, err := rpchttp.New(remote, "/websocket")
if err != nil {
return nil, err
}
return NewWithClient(chainID, httpClient), nil
}
// NewWithClient allows you to provide a custom client.
func NewWithClient(chainID string, client rpcclient.RemoteClient) provider.Provider {
return &http{
client: client,
chainID: chainID,
}
}
// ChainID returns a chainID this provider was configured with.
func (p *http) ChainID() string {
return p.chainID
}
func (p *http) String() string {
return fmt.Sprintf("http{%s}", p.client.Remote())
}
// SignedHeader fetches a SignedHeader at the given height and checks the
// chainID matches.
func (p *http) SignedHeader(height int64) (*types.SignedHeader, error) {
h, err := validateHeight(height)
if err != nil {
return nil, err
}
commit, err := p.client.Commit(h)
if err != nil {
// TODO: standartise errors on the RPC side
if regexpMissingHeight.MatchString(err.Error()) {
return nil, provider.ErrSignedHeaderNotFound
}
return nil, err
}
if commit.Header == nil {
return nil, errors.New("header is nil")
}
// Verify we're still on the same chain.
if p.chainID != commit.Header.ChainID {
return nil, fmt.Errorf("expected chainID %s, got %s", p.chainID, commit.Header.ChainID)
}
return &commit.SignedHeader, nil
}
// ValidatorSet fetches a ValidatorSet at the given height. Multiple HTTP
// requests might be required if the validator set size is over 100.
func (p *http) ValidatorSet(height int64) (*types.ValidatorSet, error) {
h, err := validateHeight(height)
if err != nil {
return nil, err
}
const maxPerPage = 100
res, err := p.client.Validators(h, 0, maxPerPage)
if err != nil {
// TODO: standartise errors on the RPC side
if regexpMissingHeight.MatchString(err.Error()) {
return nil, provider.ErrValidatorSetNotFound
}
return nil, err
}
var (
vals = res.Validators
page = 1
)
// Check if there are more validators.
for len(res.Validators) == maxPerPage {
res, err = p.client.Validators(h, page, maxPerPage)
if err != nil {
return nil, err
}
if len(res.Validators) > 0 {
vals = append(vals, res.Validators...)
}
page++
}
return types.NewValidatorSet(vals), nil
}
// ReportEvidence calls `/broadcast_evidence` endpoint.
func (p *http) ReportEvidence(ev types.Evidence) error {
_, err := p.client.BroadcastEvidence(ev)
return err
}
func validateHeight(height int64) (*int64, error) {
if height < 0 {
return nil, fmt.Errorf("expected height >= 0, got height %d", height)
}
h := &height
if height == 0 {
h = nil
}
return h, nil
}

View File

@@ -0,0 +1,98 @@
package http_test
import (
"fmt"
"os"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/abci/example/kvstore"
"github.com/tendermint/tendermint/light/provider"
lighthttp "github.com/tendermint/tendermint/light/provider/http"
rpcclient "github.com/tendermint/tendermint/rpc/client"
rpchttp "github.com/tendermint/tendermint/rpc/client/http"
rpctest "github.com/tendermint/tendermint/rpc/test"
"github.com/tendermint/tendermint/types"
)
func TestNewProvider(t *testing.T) {
c, err := lighthttp.New("chain-test", "192.168.0.1:26657")
require.NoError(t, err)
require.Equal(t, fmt.Sprintf("%s", c), "http{http://192.168.0.1:26657}")
c, err = lighthttp.New("chain-test", "http://153.200.0.1:26657")
require.NoError(t, err)
require.Equal(t, fmt.Sprintf("%s", c), "http{http://153.200.0.1:26657}")
c, err = lighthttp.New("chain-test", "153.200.0.1")
require.NoError(t, err)
require.Equal(t, fmt.Sprintf("%s", c), "http{http://153.200.0.1}")
}
func TestMain(m *testing.M) {
app := kvstore.NewApplication()
app.RetainBlocks = 9
node := rpctest.StartTendermint(app)
code := m.Run()
rpctest.StopTendermint(node)
os.Exit(code)
}
func TestProvider(t *testing.T) {
cfg := rpctest.GetConfig()
defer os.RemoveAll(cfg.RootDir)
rpcAddr := cfg.RPC.ListenAddress
genDoc, err := types.GenesisDocFromFile(cfg.GenesisFile())
if err != nil {
panic(err)
}
chainID := genDoc.ChainID
t.Log("chainID:", chainID)
c, err := rpchttp.New(rpcAddr, "/websocket")
require.Nil(t, err)
p := lighthttp.NewWithClient(chainID, c)
require.NoError(t, err)
require.NotNil(t, p)
// let it produce some blocks
err = rpcclient.WaitForHeight(c, 10, nil)
require.NoError(t, err)
// let's get the highest block
sh, err := p.SignedHeader(0)
require.NoError(t, err)
assert.True(t, sh.Height < 1000)
// let's check this is valid somehow
assert.Nil(t, sh.ValidateBasic(chainID))
// historical queries now work :)
lower := sh.Height - 3
sh, err = p.SignedHeader(lower)
require.NoError(t, err)
assert.Equal(t, lower, sh.Height)
// fetching missing heights (both future and pruned) should return appropriate errors
_, err = p.SignedHeader(1000)
require.Error(t, err)
assert.Equal(t, provider.ErrSignedHeaderNotFound, err)
_, err = p.ValidatorSet(1000)
require.Error(t, err)
assert.Equal(t, provider.ErrValidatorSetNotFound, err)
_, err = p.SignedHeader(1)
require.Error(t, err)
assert.Equal(t, provider.ErrSignedHeaderNotFound, err)
_, err = p.ValidatorSet(1)
require.Error(t, err)
assert.Equal(t, provider.ErrValidatorSetNotFound, err)
}

View File

@@ -0,0 +1,34 @@
package mock
import (
"errors"
"github.com/tendermint/tendermint/light/provider"
"github.com/tendermint/tendermint/types"
)
var errNoResp = errors.New("no response from provider")
type deadMock struct {
chainID string
}
// NewDeadMock creates a mock provider that always errors.
func NewDeadMock(chainID string) provider.Provider {
return &deadMock{chainID: chainID}
}
func (p *deadMock) ChainID() string { return p.chainID }
func (p *deadMock) String() string { return "deadMock" }
func (p *deadMock) SignedHeader(height int64) (*types.SignedHeader, error) {
return nil, errNoResp
}
func (p *deadMock) ValidatorSet(height int64) (*types.ValidatorSet, error) {
return nil, errNoResp
}
func (p *deadMock) ReportEvidence(ev types.Evidence) error {
return errNoResp
}

View File

@@ -0,0 +1,78 @@
package mock
import (
"fmt"
"strings"
"github.com/tendermint/tendermint/light/provider"
"github.com/tendermint/tendermint/types"
)
type Mock struct {
chainID string
headers map[int64]*types.SignedHeader
vals map[int64]*types.ValidatorSet
evidenceToReport map[string]types.Evidence // hash => evidence
}
var _ provider.Provider = (*Mock)(nil)
// New creates a mock provider with the given set of headers and validator
// sets.
func New(chainID string, headers map[int64]*types.SignedHeader, vals map[int64]*types.ValidatorSet) *Mock {
return &Mock{
chainID: chainID,
headers: headers,
vals: vals,
evidenceToReport: make(map[string]types.Evidence),
}
}
// ChainID returns the blockchain ID.
func (p *Mock) ChainID() string {
return p.chainID
}
func (p *Mock) String() string {
var headers strings.Builder
for _, h := range p.headers {
fmt.Fprintf(&headers, " %d:%X", h.Height, h.Hash())
}
var vals strings.Builder
for _, v := range p.vals {
fmt.Fprintf(&vals, " %X", v.Hash())
}
return fmt.Sprintf("Mock{headers: %s, vals: %v}", headers.String(), vals.String())
}
func (p *Mock) SignedHeader(height int64) (*types.SignedHeader, error) {
if height == 0 && len(p.headers) > 0 {
return p.headers[int64(len(p.headers))], nil
}
if _, ok := p.headers[height]; ok {
return p.headers[height], nil
}
return nil, provider.ErrSignedHeaderNotFound
}
func (p *Mock) ValidatorSet(height int64) (*types.ValidatorSet, error) {
if height == 0 && len(p.vals) > 0 {
return p.vals[int64(len(p.vals))], nil
}
if _, ok := p.vals[height]; ok {
return p.vals[height], nil
}
return nil, provider.ErrValidatorSetNotFound
}
func (p *Mock) ReportEvidence(ev types.Evidence) error {
p.evidenceToReport[string(ev.Hash())] = ev
return nil
}
func (p *Mock) HasEvidence(ev types.Evidence) bool {
_, ok := p.evidenceToReport[string(ev.Hash())]
return ok
}

View File

@@ -0,0 +1,38 @@
package provider
import (
"github.com/tendermint/tendermint/types"
)
// Provider provides information for the light client to sync (verification
// happens in the client).
type Provider interface {
// ChainID returns the blockchain ID.
ChainID() string
// SignedHeader returns the SignedHeader that corresponds to the given
// height.
//
// 0 - the latest.
// height must be >= 0.
//
// If the provider fails to fetch the SignedHeader due to the IO or other
// issues, an error will be returned.
// If there's no SignedHeader for the given height, ErrSignedHeaderNotFound
// error is returned.
SignedHeader(height int64) (*types.SignedHeader, error)
// ValidatorSet returns the ValidatorSet that corresponds to height.
//
// 0 - the latest.
// height must be >= 0.
//
// If the provider fails to fetch the ValidatorSet due to the IO or other
// issues, an error will be returned.
// If there's no ValidatorSet for the given height, ErrValidatorSetNotFound
// error is returned.
ValidatorSet(height int64) (*types.ValidatorSet, error)
// ReportEvidence reports an evidence of misbehavior.
ReportEvidence(ev types.Evidence) error
}