package privval
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/tendermint/tendermint/crypto"
|
|
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
|
"github.com/tendermint/tendermint/types"
|
|
)
|
|
|
|
// RetrySignerClient wraps SignerClient adding retry for each operation (except
|
|
// Ping) w/ a timeout.
|
|
type RetrySignerClient struct {
|
|
next *SignerClient
|
|
retries int
|
|
timeout time.Duration
|
|
}
|
|
|
|
// NewRetrySignerClient returns RetrySignerClient. If +retries+ is 0, the
|
|
// client will be retrying each operation indefinitely.
|
|
func NewRetrySignerClient(sc *SignerClient, retries int, timeout time.Duration) *RetrySignerClient {
|
|
return &RetrySignerClient{sc, retries, timeout}
|
|
}
|
|
|
|
var _ types.PrivValidator = (*RetrySignerClient)(nil)
|
|
|
|
func (sc *RetrySignerClient) Close() error {
|
|
return sc.next.Close()
|
|
}
|
|
|
|
func (sc *RetrySignerClient) IsConnected() bool {
|
|
return sc.next.IsConnected()
|
|
}
|
|
|
|
func (sc *RetrySignerClient) WaitForConnection(maxWait time.Duration) error {
|
|
return sc.next.WaitForConnection(maxWait)
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// Implement PrivValidator
|
|
|
|
func (sc *RetrySignerClient) Ping() error {
|
|
return sc.next.Ping()
|
|
}
|
|
|
|
func (sc *RetrySignerClient) GetPubKey(ctx context.Context) (crypto.PubKey, error) {
|
|
var (
|
|
pk crypto.PubKey
|
|
err error
|
|
)
|
|
|
|
t := time.NewTimer(sc.timeout)
|
|
for i := 0; i < sc.retries || sc.retries == 0; i++ {
|
|
pk, err = sc.next.GetPubKey(ctx)
|
|
if err == nil {
|
|
return pk, nil
|
|
}
|
|
// If remote signer errors, we don't retry.
|
|
if _, ok := err.(*RemoteSignerError); ok {
|
|
return nil, err
|
|
}
|
|
select {
|
|
case <-ctx.Done():
|
|
return nil, ctx.Err()
|
|
case <-t.C:
|
|
t.Reset(sc.timeout)
|
|
}
|
|
}
|
|
return nil, fmt.Errorf("exhausted all attempts to get pubkey: %w", err)
|
|
}
|
|
|
|
func (sc *RetrySignerClient) SignVote(ctx context.Context, chainID string, vote *tmproto.Vote) error {
|
|
var err error
|
|
for i := 0; i < sc.retries || sc.retries == 0; i++ {
|
|
err = sc.next.SignVote(ctx, chainID, vote)
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
// If remote signer errors, we don't retry.
|
|
if _, ok := err.(*RemoteSignerError); ok {
|
|
return err
|
|
}
|
|
time.Sleep(sc.timeout)
|
|
}
|
|
return fmt.Errorf("exhausted all attempts to sign vote: %w", err)
|
|
}
|
|
|
|
func (sc *RetrySignerClient) SignProposal(ctx context.Context, chainID string, proposal *tmproto.Proposal) error {
|
|
var err error
|
|
for i := 0; i < sc.retries || sc.retries == 0; i++ {
|
|
err = sc.next.SignProposal(ctx, chainID, proposal)
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
// If remote signer errors, we don't retry.
|
|
if _, ok := err.(*RemoteSignerError); ok {
|
|
return err
|
|
}
|
|
time.Sleep(sc.timeout)
|
|
}
|
|
return fmt.Errorf("exhausted all attempts to sign proposal: %w", err)
|
|
}
|