github.com/smartcontractkit/chainlink-testing-framework/libs@v0.0.0-20240227141906-ec710b4eb1a3/config/network.go (about)

     1  package config
     2  
     3  import (
     4  	"encoding/base64"
     5  	"errors"
     6  	"fmt"
     7  	"os"
     8  	"strings"
     9  
    10  	"github.com/pelletier/go-toml/v2"
    11  
    12  	"github.com/smartcontractkit/chainlink-testing-framework/libs/blockchain"
    13  	"github.com/smartcontractkit/chainlink-testing-framework/libs/logging"
    14  )
    15  
    16  const (
    17  	Base64NetworkConfigEnvVarName = "BASE64_NETWORK_CONFIG"
    18  )
    19  
    20  type ForkConfig struct {
    21  	URL              string `toml:"url"`                          // URL is the URL of the node to fork from. Refer to https://book.getfoundry.sh/reference/anvil/#options
    22  	BlockNumber      int64  `toml:"block_number"`                 // BlockNumber is the block number to fork from. Refer to https://book.getfoundry.sh/reference/anvil/#options
    23  	BlockTime        int64  `toml:"block_time"`                   // how frequent blocks are mined. By default, it automatically generates a new block as soon as a transaction is submitted. Refer to https://book.getfoundry.sh/reference/anvil/#options
    24  	Retries          int    `toml:"retries,omitempty"`            //  Number of retry requests for spurious networks (timed out requests). Refer to https://book.getfoundry.sh/reference/anvil/#options
    25  	Timeout          int64  `toml:"timeout,omitempty"`            //  Timeout in ms for requests sent to remote JSON-RPC server in forking mode. Refer to https://book.getfoundry.sh/reference/anvil/#options
    26  	ComputePerSecond int64  `toml:"compute_per_second,omitempty"` // Sets the number of assumed available compute units per second for this provider. Refer to https://book.getfoundry.sh/reference/anvil/#options
    27  	RateLimitEnabled bool   `toml:"rate_limit_enabled,omitempty"` //  rate limiting for this node’s provider. Refer to https://book.getfoundry.sh/reference/anvil/#options
    28  }
    29  
    30  // NetworkConfig is the configuration for the networks to be used
    31  type NetworkConfig struct {
    32  	SelectedNetworks []string `toml:"selected_networks"`
    33  	// EVMNetworks is the configuration for the EVM networks, key is the network name as declared in selected_networks slice.
    34  	// if not set, it will try to find the network from defined networks in MappedNetworks under known_networks.go
    35  	EVMNetworks map[string]*blockchain.EVMNetwork `toml:"EVMNetworks,omitempty"`
    36  	// ForkConfigs is the configuration for forking from a node,
    37  	// key is the network name as declared in selected_networks slice
    38  	ForkConfigs map[string]*ForkConfig `toml:"ForkConfigs,omitempty"`
    39  	// RpcHttpUrls is the RPC HTTP endpoints for each network,
    40  	// key is the network name as declared in selected_networks slice
    41  	RpcHttpUrls map[string][]string `toml:"RpcHttpUrls"`
    42  	// RpcWsUrls is the RPC WS endpoints for each network,
    43  	// key is the network name as declared in selected_networks slice
    44  	RpcWsUrls map[string][]string `toml:"RpcWsUrls"`
    45  	// WalletKeys is the private keys for the funding wallets for each network,
    46  	// key is the network name as declared in selected_networks slice
    47  	WalletKeys map[string][]string `toml:"WalletKeys"`
    48  }
    49  
    50  func (n *NetworkConfig) applySecrets() error {
    51  	encodedEndpoints, isSet := os.LookupEnv(Base64NetworkConfigEnvVarName)
    52  	if !isSet {
    53  		return nil
    54  	}
    55  
    56  	err := n.applyBase64Enconded(encodedEndpoints)
    57  	if err != nil {
    58  		return fmt.Errorf("error reading network encoded endpoints: %w", err)
    59  	}
    60  
    61  	return nil
    62  }
    63  
    64  func (n *NetworkConfig) applyDecoded(configDecoded string) error {
    65  	if configDecoded == "" {
    66  		return nil
    67  	}
    68  
    69  	var cfg NetworkConfig
    70  	err := toml.Unmarshal([]byte(configDecoded), &cfg)
    71  	if err != nil {
    72  		return fmt.Errorf("error unmarshaling network config: %w", err)
    73  	}
    74  
    75  	cfg.UpperCaseNetworkNames()
    76  
    77  	err = n.applyDefaults(&cfg)
    78  	if err != nil {
    79  		return fmt.Errorf("error applying overrides from decoded network config file to config: %w", err)
    80  	}
    81  	n.OverrideURLsAndKeysFromEVMNetwork()
    82  
    83  	return nil
    84  }
    85  
    86  // OverrideURLsAndKeysFromEVMNetwork applies the URLs and keys from the EVMNetworks to the NetworkConfig
    87  // it overrides the URLs and Keys present in RpcHttpUrls, RpcWsUrls and WalletKeys in the NetworkConfig
    88  // with the URLs and Keys provided in the EVMNetworks
    89  func (n *NetworkConfig) OverrideURLsAndKeysFromEVMNetwork() {
    90  	if n.EVMNetworks == nil {
    91  		return
    92  	}
    93  	for name, evmNetwork := range n.EVMNetworks {
    94  		if evmNetwork.URLs != nil && len(evmNetwork.URLs) > 0 {
    95  			logging.L.Warn().Str("network", name).Msg("found URLs in EVMNetwork. overriding RPC URLs in RpcWsUrls with EVMNetwork URLs")
    96  			if n.RpcWsUrls == nil {
    97  				n.RpcWsUrls = make(map[string][]string)
    98  			}
    99  			n.RpcWsUrls[name] = evmNetwork.URLs
   100  		}
   101  		if evmNetwork.HTTPURLs != nil && len(evmNetwork.HTTPURLs) > 0 {
   102  			logging.L.Warn().Str("network", name).Msg("found HTTPURLs in EVMNetwork. overriding RPC URLs in RpcHttpUrls with EVMNetwork HTTP URLs")
   103  			if n.RpcHttpUrls == nil {
   104  				n.RpcHttpUrls = make(map[string][]string)
   105  			}
   106  			n.RpcHttpUrls[name] = evmNetwork.HTTPURLs
   107  		}
   108  		if evmNetwork.PrivateKeys != nil && len(evmNetwork.PrivateKeys) > 0 {
   109  			logging.L.Warn().Str("network", name).Msg("found PrivateKeys in EVMNetwork. overriding wallet keys in WalletKeys with EVMNetwork private keys")
   110  			if n.WalletKeys == nil {
   111  				n.WalletKeys = make(map[string][]string)
   112  			}
   113  			n.WalletKeys[name] = evmNetwork.PrivateKeys
   114  		}
   115  	}
   116  }
   117  
   118  func (n *NetworkConfig) applyBase64Enconded(configEncoded string) error {
   119  	if configEncoded == "" {
   120  		return nil
   121  	}
   122  
   123  	decoded, err := base64.StdEncoding.DecodeString(configEncoded)
   124  	if err != nil {
   125  		return err
   126  	}
   127  
   128  	return n.applyDecoded(string(decoded))
   129  }
   130  
   131  // Validate checks if all required fields are set, meaning that there must be at least
   132  // 1 selected network and unless it's a simulated network, there must be at least 1
   133  // rpc endpoint for HTTP and WS and 1 private key for funding wallet
   134  func (n *NetworkConfig) Validate() error {
   135  	if len(n.SelectedNetworks) == 0 {
   136  		return errors.New("selected_networks must be set")
   137  	}
   138  
   139  	for _, network := range n.SelectedNetworks {
   140  		if strings.Contains(network, "SIMULATED") {
   141  			// we don't need to validate RPC endpoints or private keys for simulated networks
   142  			continue
   143  		}
   144  		for name, evmNetwork := range n.EVMNetworks {
   145  			if evmNetwork.ClientImplementation == "" {
   146  				return fmt.Errorf("client implementation for %s network must be set", name)
   147  			}
   148  			if evmNetwork.ChainID == 0 {
   149  				return fmt.Errorf("chain ID for %s network must be set", name)
   150  			}
   151  		}
   152  		if n.ForkConfigs != nil {
   153  			if _, ok := n.ForkConfigs[network]; ok {
   154  				if n.ForkConfigs[network].URL == "" {
   155  					return fmt.Errorf("fork config for %s network must have a URL", network)
   156  				}
   157  				if n.ForkConfigs[network].BlockNumber == 0 {
   158  					return fmt.Errorf("fork config for %s network must have a block number", network)
   159  				}
   160  				// we don't need to validate RPC endpoints or private keys for forked networks
   161  				continue
   162  			}
   163  		}
   164  		// if the network is not forked, we need to validate RPC endpoints and private keys
   165  		if _, ok := n.RpcHttpUrls[network]; !ok {
   166  			return fmt.Errorf("at least one HTTP RPC endpoint for %s network must be set", network)
   167  		}
   168  
   169  		if _, ok := n.RpcWsUrls[network]; !ok {
   170  			return fmt.Errorf("at least one WS RPC endpoint for %s network must be set", network)
   171  		}
   172  
   173  		if _, ok := n.WalletKeys[network]; !ok {
   174  			return fmt.Errorf("at least one private key of funding wallet for %s network must be set", network)
   175  		}
   176  	}
   177  
   178  	return nil
   179  }
   180  
   181  // UpperCaseNetworkNames converts all network name keys for wallet keys, rpc endpoints maps and
   182  // selected network slice to upper case
   183  func (n *NetworkConfig) UpperCaseNetworkNames() {
   184  	var upperCaseMapKeys = func(m map[string][]string) {
   185  		newMap := make(map[string][]string)
   186  		for key := range m {
   187  			newMap[strings.ToUpper(key)] = m[key]
   188  			delete(m, key)
   189  		}
   190  		for key := range newMap {
   191  			m[key] = newMap[key]
   192  		}
   193  	}
   194  
   195  	upperCaseMapKeys(n.RpcHttpUrls)
   196  	upperCaseMapKeys(n.RpcWsUrls)
   197  	upperCaseMapKeys(n.WalletKeys)
   198  
   199  	for network := range n.EVMNetworks {
   200  		if network != strings.ToUpper(network) {
   201  			n.EVMNetworks[strings.ToUpper(network)] = n.EVMNetworks[network]
   202  			delete(n.EVMNetworks, network)
   203  		}
   204  	}
   205  
   206  	for network := range n.ForkConfigs {
   207  		if network != strings.ToUpper(network) {
   208  			n.ForkConfigs[strings.ToUpper(network)] = n.ForkConfigs[network]
   209  			delete(n.ForkConfigs, network)
   210  		}
   211  	}
   212  
   213  	for i, network := range n.SelectedNetworks {
   214  		n.SelectedNetworks[i] = strings.ToUpper(network)
   215  	}
   216  }
   217  
   218  func (n *NetworkConfig) applyDefaults(defaults *NetworkConfig) error {
   219  	if defaults == nil {
   220  		return nil
   221  	}
   222  
   223  	if defaults.SelectedNetworks != nil {
   224  		n.SelectedNetworks = defaults.SelectedNetworks
   225  	}
   226  	if defaults.EVMNetworks != nil {
   227  		if n.EVMNetworks == nil || len(n.EVMNetworks) == 0 {
   228  			n.EVMNetworks = defaults.EVMNetworks
   229  		} else {
   230  			for network, cfg := range defaults.EVMNetworks {
   231  				if _, ok := n.EVMNetworks[network]; !ok {
   232  					n.EVMNetworks[network] = cfg
   233  				}
   234  			}
   235  		}
   236  	}
   237  	if defaults.ForkConfigs != nil {
   238  		if n.ForkConfigs == nil || len(n.ForkConfigs) == 0 {
   239  			n.ForkConfigs = defaults.ForkConfigs
   240  		} else {
   241  			for network, cfg := range defaults.ForkConfigs {
   242  				if _, ok := n.ForkConfigs[network]; !ok {
   243  					n.ForkConfigs[network] = cfg
   244  				}
   245  			}
   246  		}
   247  	}
   248  	if defaults.RpcHttpUrls != nil {
   249  		if n.RpcHttpUrls == nil || len(n.RpcHttpUrls) == 0 {
   250  			n.RpcHttpUrls = defaults.RpcHttpUrls
   251  		} else {
   252  			for network, urls := range defaults.RpcHttpUrls {
   253  				if _, ok := n.RpcHttpUrls[network]; !ok {
   254  					n.RpcHttpUrls[network] = urls
   255  				}
   256  			}
   257  		}
   258  	}
   259  	if defaults.RpcWsUrls != nil {
   260  		if n.RpcWsUrls == nil || len(n.RpcWsUrls) == 0 {
   261  			n.RpcWsUrls = defaults.RpcWsUrls
   262  		} else {
   263  			for network, urls := range defaults.RpcWsUrls {
   264  				if _, ok := n.RpcWsUrls[network]; !ok {
   265  					n.RpcWsUrls[network] = urls
   266  				}
   267  			}
   268  		}
   269  	}
   270  	if defaults.WalletKeys != nil {
   271  		if n.WalletKeys == nil || len(n.WalletKeys) == 0 {
   272  			n.WalletKeys = defaults.WalletKeys
   273  		} else {
   274  
   275  			for network, keys := range defaults.WalletKeys {
   276  				if _, ok := n.WalletKeys[network]; !ok {
   277  					n.WalletKeys[network] = keys
   278  				}
   279  			}
   280  		}
   281  	}
   282  
   283  	return nil
   284  }
   285  
   286  // Default applies default values to the network config after reading it
   287  // from BASE64_NETWORK_CONFIG env var. It will only fill in the gaps, not override
   288  // meaning that if you provided WS RPC endpoint in your network config, but not the
   289  // HTTP one, then only HTTP will be taken from default config (provided it's there)
   290  func (n *NetworkConfig) Default() error {
   291  	return n.applySecrets()
   292  }