Merge branch 'master' into marko/int64-

This commit is contained in:
Marko Baricevic
2021-02-10 09:53:43 +01:00
31 changed files with 535 additions and 1244 deletions
+10 -14
View File
@@ -5,6 +5,7 @@ import (
"context"
"errors"
"fmt"
"sort"
"time"
"github.com/tendermint/tendermint/libs/log"
@@ -21,8 +22,7 @@ const (
sequential mode = iota + 1
skipping
defaultPruningSize = 1000
defaultMaxRetryAttempts = 10
defaultPruningSize = 1000
// For verifySkipping, when using the cache of headers from the previous batch,
// they will always be at a height greater than 1/2 (normal verifySkipping) so to
// find something in between the range, 9/16 is used.
@@ -90,14 +90,6 @@ func Logger(l log.Logger) Option {
}
}
// MaxRetryAttempts option can be used to set max attempts before replacing
// primary with a witness.
func MaxRetryAttempts(max uint16) Option {
return func(c *Client) {
c.maxRetryAttempts = max
}
}
// MaxClockDrift defines how much new header's time can drift into
// the future. Default: 10s.
func MaxClockDrift(d time.Duration) Option {
@@ -116,7 +108,6 @@ type Client struct {
trustingPeriod time.Duration // see TrustOptions.Period
verificationMode mode
trustLevel tmmath.Fraction
maxRetryAttempts uint16 // see MaxRetryAttempts option
maxClockDrift time.Duration
// Mutex for locking during changes of the light clients providers
@@ -202,7 +193,6 @@ func NewClientFromTrustedStore(
trustingPeriod: trustingPeriod,
verificationMode: skipping,
trustLevel: DefaultTrustLevel,
maxRetryAttempts: defaultMaxRetryAttempts,
maxClockDrift: defaultMaxClockDrift,
primary: primary,
witnesses: witnesses,
@@ -995,6 +985,9 @@ func (c *Client) compareFirstHeaderWithWitnesses(ctx context.Context, h *types.S
compareCtx, cancel := context.WithCancel(ctx)
defer cancel()
c.providerMutex.Lock()
defer c.providerMutex.Unlock()
if len(c.witnesses) < 1 {
return ErrNoWitnesses
}
@@ -1028,8 +1021,11 @@ and remove witness. Otherwise, use the different primary`, e.WitnessIndex), "wit
}
}
for _, idx := range witnessesToRemove {
c.removeWitness(idx)
// we need to make sure that we remove witnesses by index in the reverse
// order so as to not affect the indexes themselves
sort.Ints(witnessesToRemove)
for i := len(witnessesToRemove) - 1; i >= 0; i-- {
c.removeWitness(witnessesToRemove[i])
}
return nil
-3
View File
@@ -768,7 +768,6 @@ func TestClientReplacesPrimaryWithWitnessIfPrimaryIsUnavailable(t *testing.T) {
[]provider.Provider{fullNode, fullNode},
dbs.New(dbm.NewMemDB()),
light.Logger(log.TestingLogger()),
light.MaxRetryAttempts(1),
)
require.NoError(t, err)
@@ -946,7 +945,6 @@ func TestClientRemovesWitnessIfItSendsUsIncorrectHeader(t *testing.T) {
[]provider.Provider{badProvider1, badProvider2},
dbs.New(dbm.NewMemDB()),
light.Logger(log.TestingLogger()),
light.MaxRetryAttempts(1),
)
// witness should have behaved properly -> no error
require.NoError(t, err)
@@ -1086,7 +1084,6 @@ func TestClientEnsureValidHeadersAndValSets(t *testing.T) {
badNode,
[]provider.Provider{badNode, badNode},
dbs.New(dbm.NewMemDB()),
light.MaxRetryAttempts(1),
)
require.NoError(t, err)
+6 -2
View File
@@ -5,6 +5,7 @@ import (
"context"
"errors"
"fmt"
"sort"
"time"
"github.com/tendermint/tendermint/light/provider"
@@ -127,8 +128,11 @@ func (c *Client) detectDivergence(ctx context.Context, primaryTrace []*types.Lig
}
}
for _, idx := range witnessesToRemove {
c.removeWitness(idx)
// we need to make sure that we remove witnesses by index in the reverse
// order so as to not affect the indexes themselves
sort.Ints(witnessesToRemove)
for i := len(witnessesToRemove) - 1; i >= 0; i-- {
c.removeWitness(witnessesToRemove[i])
}
// 1. If we had at least one witness that returned the same header then we
-5
View File
@@ -59,7 +59,6 @@ func TestLightClientAttackEvidence_Lunatic(t *testing.T) {
[]provider.Provider{witness},
dbs.New(dbm.NewMemDB()),
light.Logger(log.TestingLogger()),
light.MaxRetryAttempts(1),
)
require.NoError(t, err)
@@ -141,7 +140,6 @@ func TestLightClientAttackEvidence_Equivocation(t *testing.T) {
[]provider.Provider{witness},
dbs.New(dbm.NewMemDB()),
light.Logger(log.TestingLogger()),
light.MaxRetryAttempts(1),
verificationOption,
)
require.NoError(t, err)
@@ -196,7 +194,6 @@ func TestClientDivergentTraces1(t *testing.T) {
[]provider.Provider{witness},
dbs.New(dbm.NewMemDB()),
light.Logger(log.TestingLogger()),
light.MaxRetryAttempts(1),
)
require.Error(t, err)
assert.Contains(t, err.Error(), "does not match primary")
@@ -220,7 +217,6 @@ func TestClientDivergentTraces2(t *testing.T) {
[]provider.Provider{deadNode, deadNode, primary},
dbs.New(dbm.NewMemDB()),
light.Logger(log.TestingLogger()),
light.MaxRetryAttempts(1),
)
require.NoError(t, err)
@@ -255,7 +251,6 @@ func TestClientDivergentTraces3(t *testing.T) {
[]provider.Provider{witness},
dbs.New(dbm.NewMemDB()),
light.Logger(log.TestingLogger()),
light.MaxRetryAttempts(1),
)
require.NoError(t, err)
+36 -10
View File
@@ -18,38 +18,60 @@ var (
// This is very brittle, see: https://github.com/tendermint/tendermint/issues/4740
regexpMissingHeight = regexp.MustCompile(`height \d+ (must be less than or equal to|is not available)`)
maxRetryAttempts = 10
timeout = 5 * time.Second
defaultOptions = Options{
MaxRetryAttempts: 10,
Timeout: 5 * time.Second,
}
)
// http provider uses an RPC client to obtain the necessary information.
type http struct {
chainID string
client rpcclient.RemoteClient
maxRetryAttempts int
}
type Options struct {
// -1 means no limit
MaxRetryAttempts int
// 0 means no timeout.
Timeout time.Duration
}
// 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. The 5s timeout is used for all requests.
func New(chainID, remote string) (provider.Provider, error) {
return NewWithOptions(chainID, remote, defaultOptions)
}
// NewWithOptions is an extension to creating a new http provider that allows the addition
// of a specified timeout and maxRetryAttempts
func NewWithOptions(chainID, remote string, options Options) (provider.Provider, error) {
// Ensure URL scheme is set (default HTTP) when not provided.
if !strings.Contains(remote, "://") {
remote = "http://" + remote
}
httpClient, err := rpchttp.NewWithTimeout(remote, "/websocket", timeout)
httpClient, err := rpchttp.NewWithTimeout(remote, "/websocket", options.Timeout)
if err != nil {
return nil, err
}
return NewWithClient(chainID, httpClient), nil
return NewWithClientAndOptions(chainID, httpClient, options), nil
}
func NewWithClient(chainID string, client rpcclient.RemoteClient) provider.Provider {
return NewWithClientAndOptions(chainID, client, defaultOptions)
}
// NewWithClient allows you to provide a custom client.
func NewWithClient(chainID string, client rpcclient.RemoteClient) provider.Provider {
func NewWithClientAndOptions(chainID string, client rpcclient.RemoteClient, options Options) provider.Provider {
return &http{
client: client,
chainID: chainID,
client: client,
chainID: chainID,
maxRetryAttempts: options.MaxRetryAttempts,
}
}
@@ -108,7 +130,9 @@ func (p *http) validatorSet(ctx context.Context, height *uint64) (*types.Validat
)
for len(vals) != total && page <= maxPages {
for attempt := 1; attempt <= maxRetryAttempts; attempt++ {
// create another for loop to control retries. If p.maxRetryAttempts
// is negative we will keep repeating.
for attempt := 0; attempt != p.maxRetryAttempts+1; attempt++ {
res, err := p.client.Validators(ctx, height, &page, &perPage)
if err != nil {
// TODO: standardize errors on the RPC side
@@ -116,7 +140,7 @@ func (p *http) validatorSet(ctx context.Context, height *uint64) (*types.Validat
return nil, provider.ErrLightBlockNotFound
}
// if we have exceeded retry attempts then return no response error
if attempt == maxRetryAttempts {
if attempt == p.maxRetryAttempts {
return nil, provider.ErrNoResponse
}
// else we wait and try again with exponential backoff
@@ -153,7 +177,9 @@ func (p *http) validatorSet(ctx context.Context, height *uint64) (*types.Validat
}
func (p *http) signedHeader(ctx context.Context, height *uint64) (*types.SignedHeader, error) {
for attempt := 1; attempt <= maxRetryAttempts; attempt++ {
// create a for loop to control retries. If p.maxRetryAttempts
// is negative we will keep repeating.
for attempt := 0; attempt != p.maxRetryAttempts+1; attempt++ {
commit, err := p.client.Commit(ctx, height)
if err != nil {
// TODO: standardize errors on the RPC side