mirror of
https://github.com/tendermint/tendermint.git
synced 2026-01-05 13:05:09 +00:00
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:
12
light/provider/errors.go
Normal file
12
light/provider/errors.go
Normal 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
141
light/provider/http/http.go
Normal 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
|
||||
}
|
||||
98
light/provider/http/http_test.go
Normal file
98
light/provider/http/http_test.go
Normal 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)
|
||||
}
|
||||
34
light/provider/mock/deadmock.go
Normal file
34
light/provider/mock/deadmock.go
Normal 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
|
||||
}
|
||||
78
light/provider/mock/mock.go
Normal file
78
light/provider/mock/mock.go
Normal 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
|
||||
}
|
||||
38
light/provider/provider.go
Normal file
38
light/provider/provider.go
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user