-
Notifications
You must be signed in to change notification settings - Fork 1.6k
/
chain.go
172 lines (150 loc) · 5.28 KB
/
chain.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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
package terra
import (
"context"
"fmt"
"math"
"math/rand"
"time"
"github.com/pkg/errors"
"go.uber.org/multierr"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/smartcontractkit/sqlx"
"github.com/smartcontractkit/chainlink-terra/pkg/terra"
terraclient "github.com/smartcontractkit/chainlink-terra/pkg/terra/client"
"github.com/smartcontractkit/chainlink-terra/pkg/terra/db"
v2 "github.com/smartcontractkit/chainlink/core/config/v2"
"github.com/smartcontractkit/chainlink/core/chains/terra/monitor"
"github.com/smartcontractkit/chainlink/core/chains/terra/terratxm"
"github.com/smartcontractkit/chainlink/core/chains/terra/types"
"github.com/smartcontractkit/chainlink/core/logger"
"github.com/smartcontractkit/chainlink/core/services"
"github.com/smartcontractkit/chainlink/core/services/keystore"
"github.com/smartcontractkit/chainlink/core/services/pg"
"github.com/smartcontractkit/chainlink/core/utils"
)
// DefaultRequestTimeout is the default Terra client timeout.
// Note that while the terra node is processing a heavy block,
// requests can be delayed significantly (https://github.com/tendermint/tendermint/issues/6899),
// however there's nothing we can do but wait until the block is processed.
// So we set a fairly high timeout here.
const DefaultRequestTimeout = 30 * time.Second
//go:generate mockery --name TxManager --srcpkg github.com/smartcontractkit/chainlink-terra/pkg/terra --output ./mocks/ --case=underscore
//go:generate mockery --name Reader --srcpkg github.com/smartcontractkit/chainlink-terra/pkg/terra/client --output ./mocks/ --case=underscore
//go:generate mockery --name Chain --srcpkg github.com/smartcontractkit/chainlink-terra/pkg/terra --output ./mocks/ --case=underscore
var _ terra.Chain = (*chain)(nil)
type chain struct {
utils.StartStopOnce
id string
cfg terra.Config
cfgImmutable bool // toml config is immutable
txm *terratxm.Txm
balanceMonitor services.ServiceCtx
orm types.ORM
lggr logger.Logger
}
func newChain(id string, cfg terra.Config, db *sqlx.DB, ks keystore.Terra, logCfg pg.QConfig, eb pg.EventBroadcaster, orm types.ORM, lggr logger.Logger) (*chain, error) {
lggr = lggr.With("terraChainID", id)
var ch = chain{
id: id,
cfg: cfg,
orm: orm,
lggr: lggr.Named("Chain"),
}
tc := func() (terraclient.ReaderWriter, error) {
return ch.getClient("")
}
gpeFCD := terraclient.NewFCDGasPriceEstimator(cfg, DefaultRequestTimeout, lggr)
gpe := terraclient.NewMustGasPriceEstimator([]terraclient.GasPricesEstimator{
terraclient.NewCachingGasPriceEstimator(gpeFCD, lggr),
terraclient.NewClosureGasPriceEstimator(func() (map[string]sdk.DecCoin, error) {
return map[string]sdk.DecCoin{
"uluna": sdk.NewDecCoinFromDec("uluna", cfg.FallbackGasPriceULuna()),
}, nil
}),
}, lggr)
ch.txm = terratxm.NewTxm(db, tc, *gpe, ch.id, cfg, ks, lggr, logCfg, eb)
ch.balanceMonitor = monitor.NewBalanceMonitor(ch.id, cfg, lggr, ks, ch.Reader)
return &ch, nil
}
func (c *chain) ID() string {
return c.id
}
func (c *chain) Config() terra.Config {
return c.cfg
}
func (c *chain) UpdateConfig(cfg *db.ChainCfg) {
if c.cfgImmutable {
c.lggr.Criticalw("TOML configuration cannot be updated", "err", v2.ErrUnsupported)
return
}
c.cfg.Update(*cfg)
}
func (c *chain) TxManager() terra.TxManager {
return c.txm
}
func (c *chain) Reader(name string) (terraclient.Reader, error) {
return c.getClient(name)
}
// getClient returns a client, optionally requiring a specific node by name.
func (c *chain) getClient(name string) (terraclient.ReaderWriter, error) {
//TODO cache clients?
var node db.Node
if name == "" { // Any node
nodes, cnt, err := c.orm.NodesForChain(c.id, 0, math.MaxInt)
if err != nil {
return nil, errors.Wrap(err, "failed to get nodes")
}
if cnt == 0 {
return nil, errors.New("no nodes available")
}
// #nosec
node = nodes[rand.Intn(len(nodes))]
} else { // Named node
var err error
node, err = c.orm.NodeNamed(name)
if err != nil {
return nil, errors.Wrapf(err, "failed to get node named %s", name)
}
if node.TerraChainID != c.id {
return nil, fmt.Errorf("failed to create client for chain %s with node %s: wrong chain id %s", c.id, name, node.TerraChainID)
}
}
client, err := terraclient.NewClient(c.id, node.TendermintURL, DefaultRequestTimeout, c.lggr.Named("Client-"+name))
if err != nil {
return nil, errors.Wrap(err, "failed to create client")
}
c.lggr.Debugw("Created client", "name", node.Name, "tendermint-url", node.TendermintURL)
return client, nil
}
// Start starts terra chain.
func (c *chain) Start(ctx context.Context) error {
return c.StartOnce("Chain", func() error {
c.lggr.Debug("Starting")
//TODO dial client?
c.lggr.Debug("Starting txm")
c.lggr.Debug("Starting balance monitor")
var ms services.MultiStart
return ms.Start(ctx, c.txm, c.balanceMonitor)
})
}
func (c *chain) Close() error {
return c.StopOnce("Chain", func() error {
c.lggr.Debug("Stopping")
c.lggr.Debug("Stopping txm")
c.lggr.Debug("Stopping balance monitor")
return multierr.Combine(c.txm.Close(),
c.balanceMonitor.Close())
})
}
func (c *chain) Ready() error {
return multierr.Combine(
c.StartStopOnce.Ready(),
c.txm.Ready(),
)
}
func (c *chain) Healthy() error {
return multierr.Combine(
c.StartStopOnce.Healthy(),
c.txm.Healthy(),
)
}