forked from tendermint/tendermint
-
Notifications
You must be signed in to change notification settings - Fork 0
/
retry_signer_client.go
104 lines (90 loc) · 2.69 KB
/
retry_signer_client.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
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(ctx context.Context, maxWait time.Duration) error {
return sc.next.WaitForConnection(ctx, maxWait)
}
//--------------------------------------------------------
// Implement PrivValidator
func (sc *RetrySignerClient) Ping(ctx context.Context) error {
return sc.next.Ping(ctx)
}
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)
}