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 }