mirror of
https://github.com/tendermint/tendermint.git
synced 2026-09-28 18:55:42 +00:00
Merge branch 'master' into marko/int64-
This commit is contained in:
+10
-14
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user