Skip to content

Commit

Permalink
[TT-970] Seth utils (#932)
Browse files Browse the repository at this point in the history
  • Loading branch information
Tofel committed Apr 29, 2024
1 parent 89b5c93 commit eaa4982
Show file tree
Hide file tree
Showing 4 changed files with 276 additions and 4 deletions.
7 changes: 7 additions & 0 deletions config/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package config

import "github.com/smartcontractkit/seth"

type SethConfig interface {
GetSethConfig() *seth.Config
}
5 changes: 3 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,13 @@ require (
github.com/lib/pq v1.10.9
github.com/onsi/gomega v1.27.8
github.com/otiai10/copy v1.14.0
github.com/pelletier/go-toml/v2 v2.1.0
github.com/pelletier/go-toml/v2 v2.1.1
github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v1.17.0
github.com/prometheus/common v0.44.0
github.com/rs/zerolog v1.30.0
github.com/slack-go/slack v0.12.2
github.com/smartcontractkit/seth v0.1.6-0.20240429143720-cacb8160ecec
github.com/smartcontractkit/wasp v0.4.1
github.com/spf13/cobra v1.6.1
github.com/spf13/pflag v1.0.5
Expand Down Expand Up @@ -214,6 +215,7 @@ require (
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
github.com/montanaflynn/stats v0.7.1 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect
Expand Down Expand Up @@ -260,7 +262,6 @@ require (
github.com/uber/jaeger-client-go v2.30.0+incompatible // indirect
github.com/uber/jaeger-lib v2.4.1+incompatible // indirect
github.com/ugorji/go/codec v1.2.11 // indirect
github.com/urfave/cli/v2 v2.25.7 // indirect
github.com/xlab/treeprint v1.1.0 // indirect
github.com/yusufpapurcu/wmi v1.2.3 // indirect
go.etcd.io/etcd/api/v3 v3.5.7 // indirect
Expand Down
9 changes: 7 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -852,6 +852,8 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0=
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4=
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE=
github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
Expand Down Expand Up @@ -911,8 +913,8 @@ github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2D
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE=
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI=
github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/pierrec/lz4/v4 v4.1.18 h1:xaKrnTkyoqfh1YItXl56+6KJNVYWlEEPuAQW9xsplYQ=
Expand Down Expand Up @@ -1030,6 +1032,8 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/slack-go/slack v0.12.2 h1:x3OppyMyGIbbiyFhsBmpf9pwkUzMhthJMRNmNlA4LaQ=
github.com/slack-go/slack v0.12.2/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw=
github.com/smartcontractkit/seth v0.1.6-0.20240429143720-cacb8160ecec h1:BT1loU6TT2YqMenD7XE+aw7IeeTiC25+r1TLKAySVIg=
github.com/smartcontractkit/seth v0.1.6-0.20240429143720-cacb8160ecec/go.mod h1:2TMOZQ8WTAw7rR1YBbXpnad6VmT/+xDd/nXLmB7Eero=
github.com/smartcontractkit/wasp v0.4.1 h1:qgIx2s+eCwH0OaBKaHEAHUQ1Z47bAgDu+ICS9IOqvGQ=
github.com/smartcontractkit/wasp v0.4.1/go.mod h1:3qiofyI3pkbrc48a3CVshbMfgl74SiuPL/tm30d9Wb4=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
Expand Down Expand Up @@ -1101,6 +1105,7 @@ github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljT
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/urfave/cli v1.22.12 h1:igJgVw1JdKH+trcLWLeLwZjU9fEfPesQ+9/e4MQ44S8=
github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs=
github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4=
Expand Down
259 changes: 259 additions & 0 deletions utils/seth/seth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
package seth

import (
"fmt"
"strconv"

"github.com/pkg/errors"
"github.com/rs/zerolog"
pkg_seth "github.com/smartcontractkit/seth"

"github.com/smartcontractkit/chainlink-testing-framework/blockchain"
"github.com/smartcontractkit/chainlink-testing-framework/config"
"github.com/smartcontractkit/chainlink-testing-framework/k8s/environment"
)

var INSUFFICIENT_EPHEMERAL_KEYS = `
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 minimaly viable. Although it is highly recommended to use at least 20 ephemeral addresses.
`

var INSUFFICIENT_STATIC_KEYS = `
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 epehemeral 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(INSUFFICIENT_EPHEMERAL_KEYS, 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(INSUFFICIENT_STATIC_KEYS, 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 epehemeral 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(INSUFFICIENT_EPHEMERAL_KEYS, 0)
}

return nil
}

if sethCfg.EphemeralAddrs != nil && int(*sethCfg.EphemeralAddrs) > 0 {
var zero int64 = 0
sethCfg.EphemeralAddrs = &zero
}

if concurrency < 1 {
return fmt.Errorf(INSUFFICIENT_STATIC_KEYS, 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")
}

sethCfg, err := MergeSethAndEvmNetworkConfigs(network, *readSethCfg)
if err != nil {
return nil, errors.Wrapf(err, "Error merging seth and evm network configs")
}

err = configFn(&sethCfg)
if err != nil {
return nil, errors.Wrapf(err, "Error applying seth config function")
}

err = ValidateSethNetworkConfig(sethCfg.Network)
if err != nil {
return nil, errors.Wrapf(err, "Error validating seth network config")
}

chainClient, err := pkg_seth.NewClientWithConfig(&sethCfg)
if err != nil {
return nil, errors.Wrapf(err, "Error creating seth client")
}

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

for _, conf := range sethConfig.Networks {
if evmNetwork.Simulated {
if conf.Name == pkg_seth.GETH {
conf.PrivateKeys = evmNetwork.PrivateKeys
if len(conf.URLs) == 0 {
conf.URLs = evmNetwork.URLs
}
// important since Besu doesn't support EIP-1559, but other EVM clients do
conf.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 {
conf.GasLimit = evmNetwork.DefaultGasLimit
}

sethNetwork = conf
break
}
} else if conf.ChainID == fmt.Sprint(evmNetwork.ChainID) {
conf.PrivateKeys = evmNetwork.PrivateKeys
if len(conf.URLs) == 0 {
conf.URLs = evmNetwork.URLs
}

sethNetwork = conf
break
}
}

if sethNetwork == nil {
return pkg_seth.Config{}, fmt.Errorf("No matching EVM network found for chain ID %d. If it's a new network please define it as [Network.EVMNetworks.NETWORK_NAME] in TOML", evmNetwork.ChainID)
}

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 cannot be nil")
}
if cfg.ChainID == "" {
return fmt.Errorf("ChainID is required")
}
_, err := strconv.Atoi(cfg.ChainID)
if err != nil {
return fmt.Errorf("ChainID needs to be a number")
}
if cfg.URLs == nil || len(cfg.URLs) == 0 {
return fmt.Errorf("URLs are required")
}
if cfg.PrivateKeys == nil || len(cfg.PrivateKeys) == 0 {
return fmt.Errorf("PrivateKeys are required")
}
if cfg.TransferGasFee == 0 {
return fmt.Errorf("TransferGasFee needs to be above 0. It's the gas fee for a simple transfer transaction")
}
if cfg.TxnTimeout.Duration() == 0 {
return fmt.Errorf("TxnTimeout needs to be above 0. It's the timeout for a transaction")
}
if cfg.EIP1559DynamicFees {
if cfg.GasFeeCap == 0 {
return fmt.Errorf("GasFeeCap needs to be above 0. It's the maximum fee per gas for a transaction (including tip)")
}
if cfg.GasTipCap == 0 {
return fmt.Errorf("GasTipCap needs to be above 0. It's the maximum tip per gas for a transaction")
}
if cfg.GasFeeCap <= cfg.GasTipCap {
return fmt.Errorf("GasFeeCap needs to be above GasTipCap (as it is base fee + tip cap)")
}
} else {
if cfg.GasPrice == 0 {
return fmt.Errorf("GasPrice needs to be above 0. It's the price of gas for a transaction")
}
}

return nil
}

0 comments on commit eaa4982

Please sign in to comment.