Skip to content

Commit

Permalink
adding evmconfig and fork config
Browse files Browse the repository at this point in the history
  • Loading branch information
AnieeG committed Feb 9, 2024
1 parent c184438 commit 33c0d8b
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 53 deletions.
34 changes: 17 additions & 17 deletions blockchain/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,43 +42,43 @@ var (
// EVMNetwork configures all the data the test needs to connect and operate on an EVM compatible network
type EVMNetwork struct {
// Human-readable name of the network:
Name string `envconfig:"evm_name" default:"Unnamed EVM Network" toml:"evm_name" json:"evm_name"`
Name string `toml:"evm_name" json:"evm_name"`
// Chain ID for the blockchain
ChainID int64 `envconfig:"evm_chain_id" default:"1337" toml:"evm_chain_id" json:"evm_chain_id"`
ChainID int64 `toml:"evm_chain_id" json:"evm_chain_id"`
// List of websocket URLs you want to connect to
URLs []string `envconfig:"evm_urls" default:"ws://example.url" toml:"evm_urls" json:"evm_urls"`
URLs []string `toml:"evm_urls" json:"evm_urls"`
// List of websocket URLs you want to connect to
HTTPURLs []string `envconfig:"evm_http_urls" default:"http://example.url" toml:"evm_http_urls" json:"evm_http_urls"`
HTTPURLs []string `toml:"evm_http_urls" json:"evm_http_urls"`
// True if the network is simulated like a geth instance in dev mode. False if the network is a real test or mainnet
Simulated bool `envconfig:"evm_simulated" default:"false" toml:"evm_simulated" json:"evm_simulated"`
Simulated bool `toml:"evm_simulated" json:"evm_simulated"`
// Type of chain client node. Values: "none" | "geth" | "besu"
SimulationType string `envconfig:"evm_simulation_type" default:"geth" toml:"evm_simulation_type" json:"evm_simulation_type"`
SimulationType string `toml:"evm_simulation_type" json:"evm_simulation_type"`
// List of private keys to fund the tests
PrivateKeys []string `envconfig:"evm_keys" default:"examplePrivateKey" toml:"evm_keys" json:"evm_keys"`
PrivateKeys []string `toml:"evm_keys" json:"evm_keys"`
// Default gas limit to assume that Chainlink nodes will use. Used to try to estimate the funds that Chainlink
// nodes require to run the tests.
ChainlinkTransactionLimit uint64 `envconfig:"evm_chainlink_transaction_limit" default:"500000" toml:"evm_chainlink_transaction_limit" json:"evm_chainlink_transaction_limit"`
ChainlinkTransactionLimit uint64 `toml:"evm_chainlink_transaction_limit" json:"evm_chainlink_transaction_limit"`
// How long to wait for on-chain operations before timing out an on-chain operation
Timeout StrDuration `envconfig:"evm_transaction_timeout" default:"2m" toml:"evm_transaction_timeout" json:"evm_transaction_timeout"`
Timeout StrDuration `toml:"evm_transaction_timeout" json:"evm_transaction_timeout"`
// How many block confirmations to wait to confirm on-chain events
MinimumConfirmations int `envconfig:"evm_minimum_confirmations" default:"1" toml:"evm_minimum_confirmations" json:"evm_minimum_confirmations"`
MinimumConfirmations int `toml:"evm_minimum_confirmations" json:"evm_minimum_confirmations"`
// How much WEI to add to gas estimations for sending transactions
GasEstimationBuffer uint64 `envconfig:"evm_gas_estimation_buffer" default:"1000" toml:"evm_gas_estimation_buffer" json:"evm_gas_estimation_buffer"`
GasEstimationBuffer uint64 `toml:"evm_gas_estimation_buffer" json:"evm_gas_estimation_buffer"`
// ClientImplementation is the blockchain client to use when interacting with the test chain
ClientImplementation ClientImplementation `envconfig:"client_implementation" default:"Ethereum" toml:"client_implementation" json:"client_implementation"`
ClientImplementation ClientImplementation `toml:"client_implementation" json:"client_implementation"`
// SupportsEIP1559 indicates if the client should try to use EIP1559 style gas and transactions
SupportsEIP1559 bool `envconfig:"evm_supports_eip1559" default:"false" toml:"evm_supports_eip1559" json:"evm_supports_eip1559"`
SupportsEIP1559 bool `toml:"evm_supports_eip1559" json:"evm_supports_eip1559"`

// Default gaslimit to use when sending transactions. If set this will override the transactionOptions gaslimit in case the
// transactionOptions gaslimit is lesser than the defaultGasLimit.
DefaultGasLimit uint64 `envconfig:"evm_default_gas_limit" default:"500000" toml:"evm_default_gas_limit" json:"evm_default_gas_limit"`
DefaultGasLimit uint64 `toml:"evm_default_gas_limit" json:"evm_default_gas_limit"`
// Few chains use finality tags to mark blocks as finalized. This is used to determine if the chain uses finality tags.
FinalityTag bool `envconfig:"evm_finality_tag" default:"false" toml:"evm_finality_tag" json:"evm_finality_tag"`
FinalityTag bool `toml:"evm_finality_tag" json:"evm_finality_tag"`
// If the chain does not use finality tags, this is used to determine how many blocks to wait for before considering a block finalized.
FinalityDepth uint64 `envconfig:"evm_finality_depth" default:"50" toml:"evm_finality_depth" json:"evm_finality_depth"`
FinalityDepth uint64 `toml:"evm_finality_depth" json:"evm_finality_depth"`

// TimeToReachFinality is the time it takes for a block to be considered final. This is used to determine how long to wait for a block to be considered final.
TimeToReachFinality StrDuration `envconfig:"evm_time_to_reach_finality" default:"0s" toml:"evm_time_to_reach_finality" json:"evm_time_to_reach_finality"`
TimeToReachFinality StrDuration `toml:"evm_time_to_reach_finality" json:"evm_time_to_reach_finality"`

// Only used internally, do not set
URL string `ignored:"true"`
Expand Down
82 changes: 75 additions & 7 deletions config/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,48 @@ package config

import (
"encoding/base64"
"errors"
"fmt"
"os"
"strings"

"errors"

"github.com/pelletier/go-toml/v2"

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

const (
Base64NetworkConfigEnvVarName = "BASE64_NETWORK_CONFIG"
)

type ForkConfig struct {
URL string `toml:"url"` // URL is the URL of the node to fork from
BlockNumber int64 `toml:"block_number"` // BlockNumber is the block number to fork from
BlockTime int64 `toml:"block_time"`
Retries int `toml:"retries"`
Timeout int64 `toml:"timeout"`
ComputePerSecond int64 `toml:"compute_per_second"`
RateLimitEnabled bool `toml:"rate_limit_enabled"`
}

// NetworkConfig is the configuration for the networks to be used
type NetworkConfig struct {
SelectedNetworks []string `toml:"selected_networks"`
RpcHttpUrls map[string][]string `toml:"RpcHttpUrls"`
RpcWsUrls map[string][]string `toml:"RpcWsUrls"`
WalletKeys map[string][]string `toml:"WalletKeys"`
SelectedNetworks []string `toml:"selected_networks"`
// EVMNetworks is the configuration for the EVM networks, key is the network name as declared in selected_networks slice.
// if not set, it will try to find the network from defined networks in MappedNetworks under known_networks.go
EVMNetworks map[string]*blockchain.EVMNetwork `toml:"evm_networks,omitempty"`
// ForkConfigs is the configuration for forking from a node,
// key is the network name as declared in selected_networks slice
ForkConfigs map[string]ForkConfig `toml:"fork_config,omitempty"`
// RpcHttpUrls is the RPC HTTP endpoints for each network,
// key is the network name as declared in selected_networks slice
RpcHttpUrls map[string][]string `toml:"RpcHttpUrls"`
// RpcWsUrls is the RPC WS endpoints for each network,
// key is the network name as declared in selected_networks slice
RpcWsUrls map[string][]string `toml:"RpcWsUrls"`
// WalletKeys is the private keys for the funding wallets for each network,
// key is the network name as declared in selected_networks slice
WalletKeys map[string][]string `toml:"WalletKeys"`
}

func (n *NetworkConfig) applySecrets() error {
Expand Down Expand Up @@ -83,7 +107,21 @@ func (n *NetworkConfig) Validate() error {
// we don't need to validate RPC endpoints or private keys for simulated networks
continue
}

if n.ForkConfigs != nil {
if _, ok := n.ForkConfigs[network]; ok {
if evmConfig, exists := n.EVMNetworks[network]; !exists || evmConfig == nil {
return fmt.Errorf("fork config for %s network is set, but no corresponding EVM network is defined", network)
}
if n.ForkConfigs[network].URL == "" {
return fmt.Errorf("fork config for %s network must have a URL", network)
}
if n.ForkConfigs[network].BlockNumber == 0 {
return fmt.Errorf("fork config for %s network must have a block number", network)
}
// we don't need to validate RPC endpoints or private keys for forked networks
continue
}
}
if _, ok := n.RpcHttpUrls[network]; !ok {
return fmt.Errorf("at least one HTTP RPC endpoint for %s network must be set", network)
}
Expand Down Expand Up @@ -120,6 +158,14 @@ func (n *NetworkConfig) UpperCaseNetworkNames() {

for i, network := range n.SelectedNetworks {
n.SelectedNetworks[i] = strings.ToUpper(network)
if _, ok := n.EVMNetworks[network]; ok {
n.EVMNetworks[strings.ToUpper(network)] = n.EVMNetworks[network]
delete(n.EVMNetworks, network)
}
if _, ok := n.ForkConfigs[network]; ok {
n.ForkConfigs[strings.ToUpper(network)] = n.ForkConfigs[network]
delete(n.ForkConfigs, network)
}
}
}

Expand All @@ -131,6 +177,28 @@ func (n *NetworkConfig) applyDefaults(defaults *NetworkConfig) error {
if defaults.SelectedNetworks != nil {
n.SelectedNetworks = defaults.SelectedNetworks
}
if defaults.EVMNetworks != nil {
if n.EVMNetworks == nil || len(n.EVMNetworks) == 0 {
n.EVMNetworks = defaults.EVMNetworks
} else {
for network, cfg := range defaults.EVMNetworks {
if _, ok := n.EVMNetworks[network]; !ok {
n.EVMNetworks[network] = cfg
}
}
}
}
if defaults.ForkConfigs != nil {
if n.ForkConfigs == nil || len(n.ForkConfigs) == 0 {
n.ForkConfigs = defaults.ForkConfigs
} else {
for network, cfg := range defaults.ForkConfigs {
if _, ok := n.ForkConfigs[network]; !ok {
n.ForkConfigs[network] = cfg
}
}
}
}
if defaults.RpcHttpUrls != nil {
if n.RpcHttpUrls == nil || len(n.RpcHttpUrls) == 0 {
n.RpcHttpUrls = defaults.RpcHttpUrls
Expand Down
64 changes: 44 additions & 20 deletions networks/known_networks.go
Original file line number Diff line number Diff line change
Expand Up @@ -754,7 +754,12 @@ func MustSetNetworks(networkCfg config.NetworkConfig) []blockchain.EVMNetwork {
for i := range selectedNetworks {
var walletKeys, httpUrls, wsUrls []string
networkName := strings.ToUpper(selectedNetworks[i])
if !strings.Contains(networkName, "SIMULATED") {
forked := false
if networkCfg.ForkConfigs != nil {
_, forked = networkCfg.ForkConfigs[networkName]
}
// if network is not simulated or forked, use the rpc urls and wallet keys from config
if !strings.Contains(networkName, "SIMULATED") && !forked {
var ok bool
wsUrls, ok = networkCfg.RpcWsUrls[selectedNetworks[i]]
if !ok {
Expand All @@ -771,30 +776,48 @@ func MustSetNetworks(networkCfg config.NetworkConfig) []blockchain.EVMNetwork {
panic(fmt.Errorf("no wallet keys found in config for '%s' network", selectedNetworks[i]))
}
}
network, err := NewEVMNetwork(networkName, walletKeys, httpUrls, wsUrls)
if err != nil {
panic(err)
// if evm_network config is found, use it
if networkCfg.EVMNetworks != nil {
if network, ok := networkCfg.EVMNetworks[networkName]; ok && network != nil {
if err := NewEVMNetwork(network, walletKeys, httpUrls, wsUrls); err != nil {
panic(err)
}
networks = append(networks, *network)
continue
}
}

if knownNetwork, valid := MappedNetworks[networkName]; valid {
err := NewEVMNetwork(&knownNetwork, walletKeys, httpUrls, wsUrls)
if err != nil {
panic(err)
}
networks = append(networks, knownNetwork)
continue
}
networks = append(networks, network)
panic(fmt.Errorf("no evm_network config found in network config. "+
"network '%s' is not a valid network. "+
"Use valid network(s) separated by comma from %v "+
"or add the evm_network details to the network config file",
selectedNetworks[i], getValidNetworkKeys()))
}
return networks
}

func NewEVMNetwork(networkKey string, walletKeys, httpUrls, wsUrls []string) (blockchain.EVMNetwork, error) {
if network, valid := MappedNetworks[networkKey]; valid {
// Overwrite network default values
if len(httpUrls) > 0 {
network.HTTPURLs = httpUrls
}
if len(wsUrls) > 0 {
network.URLs = wsUrls
}
if len(walletKeys) > 0 {
setKeys(&network, walletKeys)
func NewEVMNetwork(network *blockchain.EVMNetwork, walletKeys, httpUrls, wsUrls []string) error {
// Overwrite network default values
if len(httpUrls) > 0 {
network.HTTPURLs = httpUrls
}
if len(wsUrls) > 0 {
network.URLs = wsUrls
}
if len(walletKeys) > 0 {
if err := setKeys(network, walletKeys); err != nil {
return err
}
return network, nil
}
return blockchain.EVMNetwork{}, fmt.Errorf("network key: '%v' is invalid. Use a valid network(s) separated by comma from %v", networkKey, getValidNetworkKeys())
return nil
}

func getValidNetworkKeys() []string {
Expand All @@ -806,7 +829,7 @@ func getValidNetworkKeys() []string {
}

// setKeys sets a network's private key(s) based on env vars
func setKeys(network *blockchain.EVMNetwork, walletKeys []string) {
func setKeys(network *blockchain.EVMNetwork, walletKeys []string) error {
for keyIndex := range walletKeys { // Sanitize keys of possible `0x` prefix
// Trim some common addons
walletKeys[keyIndex] = strings.Trim(walletKeys[keyIndex], "\"'")
Expand All @@ -820,11 +843,12 @@ func setKeys(network *blockchain.EVMNetwork, walletKeys []string) {
for _, key := range network.PrivateKeys {
publicKey, err := privateKeyToAddress(key)
if err != nil {
log.Fatal().Err(err).Msg("Error reading private key")
return fmt.Errorf("error converting private key to public key: %w", err)
}
publicKeys = append(publicKeys, publicKey)
}
log.Info().Interface("Funding Addresses", publicKeys).Msg("Read Network Keys")
return nil
}

func privateKeyToAddress(privateKeyString string) (string, error) {
Expand Down
13 changes: 4 additions & 9 deletions networks/known_networks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,24 +220,19 @@ func TestNewEVMNetwork(t *testing.T) {
}()

t.Run("valid networkKey", func(t *testing.T) {
network, err := NewEVMNetwork("VALID_KEY", nil, nil, nil)
network := MappedNetworks["VALID_KEY"]
err := NewEVMNetwork(&network, nil, nil, nil)
require.NoError(t, err)
require.Equal(t, MappedNetworks["VALID_KEY"].HTTPURLs, network.HTTPURLs)
require.Equal(t, MappedNetworks["VALID_KEY"].URLs, network.URLs)
})

t.Run("invalid networkKey", func(t *testing.T) {
_, err := NewEVMNetwork("INVALID_KEY", nil, nil, nil)
require.Error(t, err)
require.Contains(t, err.Error(), "network key: 'INVALID_KEY' is invalid")
})

t.Run("overwriting default values", func(t *testing.T) {
walletKeys := []string{"1810868fc221b9f50b5b3e0186d8a5f343f892e51ce12a9e818f936ec0b651ed"}
httpUrls := []string{"http://newurl.com"}
wsUrls := []string{"ws://newwsurl.com"}

network, err := NewEVMNetwork("VALID_KEY", walletKeys, httpUrls, wsUrls)
network := MappedNetworks["VALID_KEY"]
err := NewEVMNetwork(&network, walletKeys, httpUrls, wsUrls)
require.NoError(t, err)
require.Equal(t, httpUrls, network.HTTPURLs)
require.Equal(t, wsUrls, network.URLs)
Expand Down

0 comments on commit 33c0d8b

Please sign in to comment.