-
Notifications
You must be signed in to change notification settings - Fork 46
Expand file tree
/
Copy pathseth.go
More file actions
329 lines (272 loc) · 11.7 KB
/
seth.go
File metadata and controls
329 lines (272 loc) · 11.7 KB
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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
package seth
import (
"errors"
"fmt"
"regexp"
"strings"
"github.com/rs/zerolog"
pkg_seth "github.com/smartcontractkit/chainlink-testing-framework/seth"
"github.com/smartcontractkit/chainlink-testing-framework/lib/blockchain"
"github.com/smartcontractkit/chainlink-testing-framework/lib/config"
"github.com/smartcontractkit/chainlink-testing-framework/lib/k8s/environment"
)
var ErrInsufficientEphemeralKeys = `
Error: Insufficient Ephemeral Addresses for Simulated Network
To operate on a simulated network, you must configure at least one ephemeral address. Currently, %d ephemeral address(es) are set. Please update your TOML configuration file as follows to meet this requirement:
[Seth] ephemeral_addresses_number = 1
This adjustment ensures that your setup is minimally viable. Although it is highly recommended to use at least 20 ephemeral addresses.
`
var ErrInsufficientStaticKeys = `
Error: Insufficient Private Keys for Live Network
To run this test on a live network, you must either:
1. Set at least two private keys in the '[Network.WalletKeys]' section of your TOML configuration file. Example format:
[Network.WalletKeys]
NETWORK_NAME=["PRIVATE_KEY_1", "PRIVATE_KEY_2"]
2. Set at least two private keys in the '[Network.EVMNetworks.NETWORK_NAME] section of your TOML configuration file. Example format:
evm_keys=["PRIVATE_KEY_1", "PRIVATE_KEY_2"]
Currently, only %d private key/s is/are set.
Recommended Action:
Distribute your funds across multiple private keys and update your configuration accordingly. Even though 1 private key is sufficient for testing, it is highly recommended to use at least 10 private keys.
`
var noOpSethConfigFn = func(cfg *pkg_seth.Config) error { return nil }
type ConfigFunction = func(*pkg_seth.Config) error
// OneEphemeralKeysLiveTestnetCheckFn checks whether there's at least one ephemeral key on a simulated network or at least one static key on a live network,
// and that there are no ephemeral keys on a live network. Root key is excluded from the check.
var OneEphemeralKeysLiveTestnetCheckFn = func(sethCfg *pkg_seth.Config) error {
concurrency := sethCfg.GetMaxConcurrency()
if sethCfg.IsSimulatedNetwork() {
if concurrency < 1 {
return fmt.Errorf(ErrInsufficientEphemeralKeys, 0)
}
return nil
}
if sethCfg.EphemeralAddrs != nil && int(*sethCfg.EphemeralAddrs) > 0 {
ephMsg := `
Error: Ephemeral Addresses Detected on Live Network
Ephemeral addresses are currently set for use on a live network, which is not permitted. The number of ephemeral addresses set is %d. Please make the following update to your TOML configuration file to correct this:
'[Seth] ephemeral_addresses_number = 0'
Additionally, ensure the following requirements are met to run this test on a live network:
1. Use more than one private key in your network configuration.
`
return errors.New(ephMsg)
}
if concurrency < 1 {
return fmt.Errorf(ErrInsufficientStaticKeys, len(sethCfg.Network.PrivateKeys))
}
return nil
}
// OneEphemeralKeysLiveTestnetAutoFixFn checks whether there's at least one ephemeral key on a simulated network or at least one static key on a live network,
// and that there are no ephemeral keys on a live network (if ephemeral keys count is different from zero, it will disable them). Root key is excluded from the check.
var OneEphemeralKeysLiveTestnetAutoFixFn = func(sethCfg *pkg_seth.Config) error {
concurrency := sethCfg.GetMaxConcurrency()
if sethCfg.IsSimulatedNetwork() {
if concurrency < 1 {
return fmt.Errorf(ErrInsufficientEphemeralKeys, 0)
}
return nil
}
if sethCfg.EphemeralAddrs != nil && int(*sethCfg.EphemeralAddrs) > 0 {
var zero int64 = 0
sethCfg.EphemeralAddrs = &zero
}
if concurrency < 1 {
return fmt.Errorf(ErrInsufficientStaticKeys, len(sethCfg.Network.PrivateKeys))
}
return nil
}
// GetChainClient returns a seth client for the given network after validating the config
func GetChainClient(c config.SethConfig, network blockchain.EVMNetwork) (*pkg_seth.Client, error) {
return GetChainClientWithConfigFunction(c, network, noOpSethConfigFn)
}
// GetChainClientWithConfigFunction returns a seth client for the given network after validating the config and applying the config function
func GetChainClientWithConfigFunction(c config.SethConfig, network blockchain.EVMNetwork, configFn ConfigFunction) (*pkg_seth.Client, error) {
readSethCfg := c.GetSethConfig()
if readSethCfg == nil {
return nil, fmt.Errorf("Seth config not found in the provided configuration. " +
"Ensure your TOML config file has a [Seth] section with required settings. " +
"See example: https://github.com/smartcontractkit/chainlink-testing-framework/blob/main/seth/seth.toml")
}
sethCfg, err := MergeSethAndEvmNetworkConfigs(network, *readSethCfg)
if err != nil {
return nil, fmt.Errorf("error merging seth and evm network configs: %w", err)
}
err = configFn(&sethCfg)
if err != nil {
return nil, fmt.Errorf("error applying seth config function: %w", err)
}
err = ValidateSethNetworkConfig(sethCfg.Network)
if err != nil {
return nil, fmt.Errorf("error validating seth network config: %w", err)
}
chainClient, err := pkg_seth.NewClientWithConfig(&sethCfg)
if err != nil {
return nil, fmt.Errorf("error creating seth client: %w", err)
}
return chainClient, nil
}
// MergeSethAndEvmNetworkConfigs merges EVMNetwork to Seth config. If Seth config already has Network settings,
// it will return unchanged Seth config that was passed to it. If the network is simulated, it will
// use Geth-specific settings. Otherwise, it will use the chain ID to find the correct network settings.
// If no match is found it will return error.
func MergeSethAndEvmNetworkConfigs(evmNetwork blockchain.EVMNetwork, sethConfig pkg_seth.Config) (pkg_seth.Config, error) {
if sethConfig.Network != nil {
return sethConfig, nil
}
var sethNetwork *pkg_seth.Network
mergeSimulatedNetworks := func(evmNetwork blockchain.EVMNetwork, sethNetwork pkg_seth.Network) *pkg_seth.Network {
sethNetwork.PrivateKeys = evmNetwork.PrivateKeys
if len(sethNetwork.URLs) == 0 {
if len(evmNetwork.URLs) > 0 {
sethNetwork.URLs = evmNetwork.URLs
} else {
sethNetwork.URLs = evmNetwork.HTTPURLs
}
}
// important since Besu doesn't support EIP-1559, but other EVM clients do
sethNetwork.EIP1559DynamicFees = evmNetwork.SupportsEIP1559
// might be needed for cases, when node is incapable of estimating gas limit (e.g. Geth < v1.10.0)
if evmNetwork.DefaultGasLimit != 0 {
sethNetwork.GasLimit = evmNetwork.DefaultGasLimit
}
return &sethNetwork
}
for _, conf := range sethConfig.Networks {
if evmNetwork.Simulated && evmNetwork.Name == pkg_seth.ANVIL && conf.Name == pkg_seth.ANVIL {
// Merge Anvil network
sethNetwork = mergeSimulatedNetworks(evmNetwork, *conf)
break
} else if evmNetwork.Simulated && conf.Name == pkg_seth.GETH {
// Merge all other simulated Geth networks
sethNetwork = mergeSimulatedNetworks(evmNetwork, *conf)
break
} else if isSameNetwork(conf, evmNetwork) {
conf.PrivateKeys = evmNetwork.PrivateKeys
if len(conf.URLs) == 0 {
if len(evmNetwork.URLs) > 0 {
conf.URLs = evmNetwork.URLs
} else {
conf.URLs = evmNetwork.HTTPURLs
}
}
sethNetwork = conf
break
}
}
// If the network is not found, try to find the default network and replace it with the EVM network
if sethNetwork == nil {
for _, conf := range sethConfig.Networks {
if conf.Name == fmt.Sprint(pkg_seth.DefaultNetworkName) {
conf.Name = evmNetwork.Name
conf.PrivateKeys = evmNetwork.PrivateKeys
if len(evmNetwork.URLs) > 0 {
conf.URLs = evmNetwork.URLs
} else {
conf.URLs = evmNetwork.HTTPURLs
}
sethNetwork = conf
break
}
}
}
// If the network is still not found, return an error
if sethNetwork == nil {
msg := `Failed to build network config for chain ID %d. This could be the result of various reasons:
1. You are running tests for a network that hasn't been defined in known_networks.go and you have not defined it under [Network.EVMNetworks.NETWORK_NAME] in TOML
3. You have not defined Seth network settings for the chain ID %d in TOML under [Seth.Networks]
2. You have not defined a Seth Default network in your TOML config file under [Seth.Networks] using name %s`
return pkg_seth.Config{}, fmt.Errorf(msg, evmNetwork.ChainID, evmNetwork.ChainID, pkg_seth.DefaultNetworkName)
}
sethConfig.Network = sethNetwork
return sethConfig, nil
}
// MustReplaceSimulatedNetworkUrlWithK8 replaces the simulated network URL with the K8 URL and returns the network.
// If the network is not simulated, it will return the network unchanged.
func MustReplaceSimulatedNetworkUrlWithK8(l zerolog.Logger, network blockchain.EVMNetwork, testEnvironment environment.Environment) blockchain.EVMNetwork {
if !network.Simulated {
return network
}
networkKeys := []string{"Simulated Geth", "Simulated-Geth"}
var keyToUse string
for _, key := range networkKeys {
_, ok := testEnvironment.URLs[key]
if ok {
keyToUse = key
break
}
}
if keyToUse == "" {
for k := range testEnvironment.URLs {
l.Info().Str("Network", k).Msg("Available networks")
}
panic("no network settings for Simulated Geth")
}
network.URLs = testEnvironment.URLs[keyToUse]
return network
}
// ValidateSethNetworkConfig validates the Seth network config
func ValidateSethNetworkConfig(cfg *pkg_seth.Network) error {
if cfg == nil {
return fmt.Errorf("network configuration cannot be nil. " +
"Ensure your Seth config has properly configured network settings")
}
if len(cfg.URLs) == 0 {
return fmt.Errorf("network URLs are required. " +
"Add RPC endpoint URLs in the 'urls_secret' field of your network config")
}
if len(cfg.PrivateKeys) == 0 {
return fmt.Errorf("private keys are required. " +
"Add at least one private key in 'private_keys_secret' or via environment variables")
}
if cfg.TransferGasFee == 0 {
return fmt.Errorf("transfer_gas_fee must be greater than 0. " +
"This is the gas fee for a simple transfer transaction. " +
"Set 'transfer_gas_fee' in your network config")
}
if cfg.TxnTimeout.Duration() == 0 {
return fmt.Errorf("transaction timeout must be greater than 0. " +
"Set 'txn_timeout' in your network config (e.g., '30s', '1m')")
}
if cfg.EIP1559DynamicFees {
if cfg.GasFeeCap == 0 {
return fmt.Errorf("gas_fee_cap must be greater than 0 for EIP-1559 transactions. " +
"This is the maximum fee per gas (base fee + tip). " +
"Set 'gas_fee_cap' in your network config")
}
if cfg.GasTipCap == 0 {
return fmt.Errorf("gas_tip_cap must be greater than 0 for EIP-1559 transactions. " +
"This is the maximum priority fee per gas. " +
"Set 'gas_tip_cap' in your network config")
}
if cfg.GasFeeCap <= cfg.GasTipCap {
return fmt.Errorf("gas_fee_cap (%d) must be greater than gas_tip_cap (%d). "+
"Fee cap should be base fee + tip cap. "+
"Adjust your network config accordingly",
cfg.GasFeeCap, cfg.GasTipCap)
}
} else {
if cfg.GasPrice == 0 {
return fmt.Errorf("gas_price must be greater than 0 for legacy transactions. " +
"Set 'gas_price' in your network config")
}
}
return nil
}
const RootKeyNum = 0
// AvailableSethKeyNum returns the available Seth address index
// If there are multiple addresses, it will return any synced key
// Otherwise it will return the root key
func AvailableSethKeyNum(client *pkg_seth.Client) int {
if len(client.Addresses) > 1 {
return client.AnySyncedKey()
}
return RootKeyNum
}
func isSameNetwork(conf *pkg_seth.Network, network blockchain.EVMNetwork) bool {
if strings.EqualFold(conf.Name, fmt.Sprint(network.Name)) {
return true
}
re := regexp.MustCompile(`[\s-]+`)
cleanSethName := re.ReplaceAllString(conf.Name, "_")
cleanNetworkName := re.ReplaceAllString(fmt.Sprint(network.Name), "_")
return strings.EqualFold(cleanSethName, cleanNetworkName)
}