github.com/smartcontractkit/chainlink-testing-framework/libs@v0.0.0-20240227141906-ec710b4eb1a3/docker/test_env/ethereum_env.go (about) 1 package test_env 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "os" 8 "strings" 9 "testing" 10 "time" 11 12 "github.com/ethereum/go-ethereum/common" 13 "github.com/google/uuid" 14 15 "github.com/rs/zerolog" 16 tc "github.com/testcontainers/testcontainers-go" 17 18 "github.com/smartcontractkit/chainlink-testing-framework/libs/blockchain" 19 "github.com/smartcontractkit/chainlink-testing-framework/libs/docker" 20 "github.com/smartcontractkit/chainlink-testing-framework/libs/logging" 21 "github.com/smartcontractkit/chainlink-testing-framework/libs/utils/testcontext" 22 toml_utils "github.com/smartcontractkit/chainlink-testing-framework/libs/utils/toml" 23 ) 24 25 const ( 26 CONFIG_ENV_VAR_NAME = "PRIVATE_ETHEREUM_NETWORK_CONFIG_PATH" 27 EXEC_CLIENT_ENV_VAR_NAME = "ETH2_EL_CLIENT" 28 ) 29 30 var ( 31 ErrMissingConsensusType = errors.New("consensus type is required") 32 ErrMissingExecutionLayer = errors.New("execution layer is required") 33 ErrMissingConsensusLayer = errors.New("consensus layer is required for PoS") 34 ErrConsensusLayerNotAllowed = errors.New("consensus layer is not allowed for PoW") 35 ErrTestConfigNotSaved = errors.New("could not save test env config") 36 ) 37 38 type ConsensusType string 39 40 const ( 41 ConsensusType_PoS ConsensusType = "pos" 42 ConsensusType_PoW ConsensusType = "pow" 43 ) 44 45 type ExecutionLayer string 46 47 const ( 48 ExecutionLayer_Geth ExecutionLayer = "geth" 49 ExecutionLayer_Nethermind ExecutionLayer = "nethermind" 50 ExecutionLayer_Erigon ExecutionLayer = "erigon" 51 ExecutionLayer_Besu ExecutionLayer = "besu" 52 ) 53 54 type ConsensusLayer string 55 56 const ( 57 ConsensusLayer_Prysm ConsensusLayer = "prysm" 58 ) 59 60 type EthereumNetworkBuilder struct { 61 t *testing.T 62 dockerNetworks []string 63 consensusType ConsensusType 64 consensusLayer *ConsensusLayer 65 executionLayer ExecutionLayer 66 ethereumChainConfig *EthereumChainConfig 67 existingConfig *EthereumNetwork 68 customDockerImages map[ContainerType]string 69 addressesToFund []string 70 waitForFinalization bool 71 existingFromEnvVar bool 72 } 73 74 func NewEthereumNetworkBuilder() EthereumNetworkBuilder { 75 return EthereumNetworkBuilder{ 76 dockerNetworks: []string{}, 77 waitForFinalization: false, 78 } 79 } 80 81 func (b *EthereumNetworkBuilder) WithConsensusType(consensusType ConsensusType) *EthereumNetworkBuilder { 82 b.consensusType = consensusType 83 return b 84 } 85 86 func (b *EthereumNetworkBuilder) WithConsensusLayer(consensusLayer ConsensusLayer) *EthereumNetworkBuilder { 87 b.consensusLayer = &consensusLayer 88 return b 89 } 90 91 func (b *EthereumNetworkBuilder) WithExecutionLayer(executionLayer ExecutionLayer) *EthereumNetworkBuilder { 92 b.executionLayer = executionLayer 93 return b 94 } 95 96 func (b *EthereumNetworkBuilder) WithEthereumChainConfig(config EthereumChainConfig) *EthereumNetworkBuilder { 97 b.ethereumChainConfig = &config 98 return b 99 } 100 101 func (b *EthereumNetworkBuilder) WithDockerNetworks(networks []string) *EthereumNetworkBuilder { 102 b.dockerNetworks = networks 103 return b 104 } 105 106 func (b *EthereumNetworkBuilder) WithExistingConfig(config EthereumNetwork) *EthereumNetworkBuilder { 107 b.existingConfig = &config 108 return b 109 } 110 111 func (b *EthereumNetworkBuilder) WihtExistingConfigFromEnvVar() *EthereumNetworkBuilder { 112 b.existingFromEnvVar = true 113 return b 114 } 115 116 func (b *EthereumNetworkBuilder) WithTest(t *testing.T) *EthereumNetworkBuilder { 117 b.t = t 118 return b 119 } 120 121 func (b *EthereumNetworkBuilder) WithCustomDockerImages(newImages map[ContainerType]string) *EthereumNetworkBuilder { 122 b.customDockerImages = newImages 123 return b 124 } 125 126 func (b *EthereumNetworkBuilder) WithWaitingForFinalization() *EthereumNetworkBuilder { 127 b.waitForFinalization = true 128 return b 129 } 130 131 func (b *EthereumNetworkBuilder) buildNetworkConfig() EthereumNetwork { 132 n := EthereumNetwork{ 133 ConsensusType: &b.consensusType, 134 ExecutionLayer: &b.executionLayer, 135 ConsensusLayer: b.consensusLayer, 136 } 137 138 if b.existingConfig != nil && len(b.existingConfig.Containers) > 0 { 139 n.isRecreated = true 140 n.Containers = b.existingConfig.Containers 141 } 142 143 n.DockerNetworkNames = b.dockerNetworks 144 n.WaitForFinalization = &b.waitForFinalization 145 n.EthereumChainConfig = b.ethereumChainConfig 146 n.CustomDockerImages = b.customDockerImages 147 n.t = b.t 148 149 return n 150 } 151 152 func (b *EthereumNetworkBuilder) Build() (EthereumNetwork, error) { 153 if b.existingFromEnvVar { 154 path := os.Getenv(CONFIG_ENV_VAR_NAME) 155 if path == "" { 156 return EthereumNetwork{}, fmt.Errorf("environment variable %s is not set, but build from env var was requested", CONFIG_ENV_VAR_NAME) 157 } 158 159 config, err := NewPrivateChainEnvConfigFromFile(path) 160 if err != nil { 161 return EthereumNetwork{}, err 162 } 163 164 config.isRecreated = true 165 166 return config, nil 167 } 168 169 if !b.importExistingConfig() { 170 if b.ethereumChainConfig == nil { 171 defaultConfig := GetDefaultChainConfig() 172 b.ethereumChainConfig = &defaultConfig 173 } else { 174 b.ethereumChainConfig.fillInMissingValuesWithDefault() 175 } 176 177 b.ethereumChainConfig.GenerateGenesisTimestamp() 178 } 179 180 err := b.validate() 181 if err != nil { 182 return EthereumNetwork{}, err 183 } 184 185 return b.buildNetworkConfig(), nil 186 } 187 188 func (b *EthereumNetworkBuilder) importExistingConfig() bool { 189 if b.existingConfig == nil { 190 return false 191 } 192 193 if b.existingConfig.ConsensusType != nil { 194 b.consensusType = *b.existingConfig.ConsensusType 195 } 196 197 if b.existingConfig.ConsensusLayer != nil { 198 b.consensusLayer = b.existingConfig.ConsensusLayer 199 } 200 201 if b.existingConfig.ExecutionLayer != nil { 202 b.executionLayer = *b.existingConfig.ExecutionLayer 203 } 204 205 if len(b.existingConfig.DockerNetworkNames) > 0 { 206 b.dockerNetworks = b.existingConfig.DockerNetworkNames 207 } 208 b.ethereumChainConfig = b.existingConfig.EthereumChainConfig 209 b.customDockerImages = b.existingConfig.CustomDockerImages 210 211 return true 212 } 213 214 func (b *EthereumNetworkBuilder) validate() error { 215 if b.consensusType == "" { 216 return ErrMissingConsensusType 217 } 218 219 if b.executionLayer == "" { 220 return ErrMissingExecutionLayer 221 } 222 223 if b.consensusType == ConsensusType_PoS && b.consensusLayer == nil { 224 return ErrMissingConsensusLayer 225 } 226 227 if b.consensusType == ConsensusType_PoW && b.consensusLayer != nil { 228 return ErrConsensusLayerNotAllowed 229 } 230 231 for _, addr := range b.addressesToFund { 232 if !common.IsHexAddress(addr) { 233 return fmt.Errorf("address %s is not a valid hex address", addr) 234 } 235 } 236 237 if b.ethereumChainConfig == nil { 238 return errors.New("ethereum chain config is required") 239 } 240 241 return b.ethereumChainConfig.Validate(logging.GetTestLogger(nil), b.consensusType) 242 } 243 244 type EthereumNetwork struct { 245 ConsensusType *ConsensusType `toml:"consensus_type"` 246 ConsensusLayer *ConsensusLayer `toml:"consensus_layer"` 247 ExecutionLayer *ExecutionLayer `toml:"execution_layer"` 248 DockerNetworkNames []string `toml:"docker_network_names"` 249 Containers EthereumNetworkContainers `toml:"containers"` 250 WaitForFinalization *bool `toml:"wait_for_finalization"` 251 GeneratedDataHostDir *string `toml:"generated_data_host_dir"` 252 ValKeysDir *string `toml:"val_keys_dir"` 253 EthereumChainConfig *EthereumChainConfig `toml:"EthereumChainConfig"` 254 CustomDockerImages map[ContainerType]string `toml:"CustomDockerImages"` 255 isRecreated bool 256 t *testing.T 257 } 258 259 func (en *EthereumNetwork) Start() (blockchain.EVMNetwork, RpcProvider, error) { 260 switch *en.ConsensusType { 261 case ConsensusType_PoS: 262 return en.startPos() 263 case ConsensusType_PoW: 264 return en.startPow() 265 default: 266 return blockchain.EVMNetwork{}, RpcProvider{}, fmt.Errorf("unknown consensus type: %s", *en.ConsensusType) 267 } 268 } 269 270 func (en *EthereumNetwork) startPos() (blockchain.EVMNetwork, RpcProvider, error) { 271 rpcProvider := RpcProvider{ 272 privateHttpUrls: []string{}, 273 privatelWsUrls: []string{}, 274 publiclHttpUrls: []string{}, 275 publicsUrls: []string{}, 276 } 277 278 var net blockchain.EVMNetwork 279 280 if *en.ConsensusLayer != ConsensusLayer_Prysm { 281 return blockchain.EVMNetwork{}, RpcProvider{}, fmt.Errorf("unsupported consensus layer: %s. Use 'prysm'", *en.ConsensusLayer) 282 } 283 284 dockerNetworks, err := en.getOrCreateDockerNetworks() 285 if err != nil { 286 return blockchain.EVMNetwork{}, RpcProvider{}, err 287 } 288 var generatedDataHostDir, valKeysDir string 289 290 // create host directories and run genesis containers only if we are NOT recreating existing containers 291 if !en.isRecreated { 292 generatedDataHostDir, valKeysDir, err = createHostDirectories() 293 294 en.GeneratedDataHostDir = &generatedDataHostDir 295 en.ValKeysDir = &valKeysDir 296 297 if err != nil { 298 return blockchain.EVMNetwork{}, RpcProvider{}, err 299 } 300 301 valKeysGeneretor, err := NewValKeysGeneretor(en.EthereumChainConfig, valKeysDir, en.getImageOverride(ContainerType_ValKeysGenerator)...) 302 if err != nil { 303 return blockchain.EVMNetwork{}, RpcProvider{}, err 304 } 305 valKeysGeneretor.WithTestInstance(en.t) 306 307 err = valKeysGeneretor.StartContainer() 308 if err != nil { 309 return blockchain.EVMNetwork{}, RpcProvider{}, err 310 } 311 312 genesis, err := NewEthGenesisGenerator(*en.EthereumChainConfig, generatedDataHostDir, en.getImageOverride(ContainerType_GenesisGenerator)...) 313 if err != nil { 314 return blockchain.EVMNetwork{}, RpcProvider{}, err 315 } 316 317 genesis.WithTestInstance(en.t) 318 319 err = genesis.StartContainer() 320 if err != nil { 321 return blockchain.EVMNetwork{}, RpcProvider{}, err 322 } 323 324 initHelper := NewInitHelper(*en.EthereumChainConfig, generatedDataHostDir).WithTestInstance(en.t) 325 err = initHelper.StartContainer() 326 if err != nil { 327 return blockchain.EVMNetwork{}, RpcProvider{}, err 328 } 329 } else { 330 // we don't set actual values to not increase complexity, as they do not matter for containers that are already running 331 generatedDataHostDir = "" 332 valKeysDir = "" 333 } 334 335 var client ExecutionClient 336 var clientErr error 337 switch *en.ExecutionLayer { 338 case ExecutionLayer_Geth: 339 client, clientErr = NewGeth2(dockerNetworks, en.EthereumChainConfig, generatedDataHostDir, ConsensusLayer_Prysm, append(en.getImageOverride(ContainerType_Geth), en.setExistingContainerName(ContainerType_Geth))...) 340 case ExecutionLayer_Nethermind: 341 client, clientErr = NewNethermind(dockerNetworks, generatedDataHostDir, ConsensusLayer_Prysm, append(en.getImageOverride(ContainerType_Nethermind), en.setExistingContainerName(ContainerType_Nethermind))...) 342 case ExecutionLayer_Erigon: 343 client, clientErr = NewErigon(dockerNetworks, en.EthereumChainConfig, generatedDataHostDir, ConsensusLayer_Prysm, append(en.getImageOverride(ContainerType_Erigon), en.setExistingContainerName(ContainerType_Erigon))...) 344 case ExecutionLayer_Besu: 345 client, clientErr = NewBesu(dockerNetworks, en.EthereumChainConfig, generatedDataHostDir, ConsensusLayer_Prysm, append(en.getImageOverride(ContainerType_Besu), en.setExistingContainerName(ContainerType_Besu))...) 346 default: 347 return blockchain.EVMNetwork{}, RpcProvider{}, fmt.Errorf("unsupported execution layer: %s", *en.ExecutionLayer) 348 } 349 350 if clientErr != nil { 351 return blockchain.EVMNetwork{}, RpcProvider{}, clientErr 352 } 353 354 client.WithTestInstance(en.t) 355 356 net, err = client.StartContainer() 357 if err != nil { 358 return blockchain.EVMNetwork{}, RpcProvider{}, err 359 } 360 361 beacon, err := NewPrysmBeaconChain(dockerNetworks, en.EthereumChainConfig, generatedDataHostDir, client.GetInternalExecutionURL(), append(en.getImageOverride(ContainerType_ValKeysGenerator), en.setExistingContainerName(ContainerType_PrysmBeacon))...) 362 if err != nil { 363 return blockchain.EVMNetwork{}, RpcProvider{}, err 364 } 365 366 beacon.WithTestInstance(en.t) 367 err = beacon.StartContainer() 368 if err != nil { 369 return blockchain.EVMNetwork{}, RpcProvider{}, err 370 } 371 372 validator, err := NewPrysmValidator(dockerNetworks, en.EthereumChainConfig, generatedDataHostDir, valKeysDir, beacon. 373 InternalBeaconRpcProvider, append(en.getImageOverride(ContainerType_ValKeysGenerator), en.setExistingContainerName(ContainerType_PrysmVal))...) 374 if err != nil { 375 return blockchain.EVMNetwork{}, RpcProvider{}, err 376 } 377 378 validator.WithTestInstance(en.t) 379 err = validator.StartContainer() 380 if err != nil { 381 return blockchain.EVMNetwork{}, RpcProvider{}, err 382 } 383 384 err = client.WaitUntilChainIsReady(testcontext.Get(en.t), en.EthereumChainConfig.GetDefaultWaitDuration()) 385 if err != nil { 386 return blockchain.EVMNetwork{}, RpcProvider{}, err 387 } 388 389 en.DockerNetworkNames = dockerNetworks 390 net.ChainID = int64(en.EthereumChainConfig.ChainID) 391 // use a higher value than the default, because eth2 is slower than dev-mode eth1 392 net.Timeout = blockchain.StrDuration{Duration: time.Duration(4 * time.Minute)} 393 net.FinalityTag = true 394 net.FinalityDepth = 0 395 396 if *en.ExecutionLayer == ExecutionLayer_Besu { 397 // Besu doesn't support "eth_maxPriorityFeePerGas" https://github.com/hyperledger/besu/issues/5658 398 // And if gas is too low, then transaction doesn't get to prioritized pool and is not a candidate for inclusion in the next block 399 net.GasEstimationBuffer = 10_000_000_000 400 } else { 401 net.SupportsEIP1559 = true 402 } 403 404 logger := logging.GetTestLogger(en.t) 405 if en.WaitForFinalization != nil && *en.WaitForFinalization { 406 evmClient, err := blockchain.NewEVMClientFromNetwork(net, logger) 407 if err != nil { 408 return blockchain.EVMNetwork{}, RpcProvider{}, err 409 } 410 411 err = waitForChainToFinaliseAnEpoch(logger, evmClient, en.EthereumChainConfig.GetDefaultFinalizationWaitDuration()) 412 if err != nil { 413 return blockchain.EVMNetwork{}, RpcProvider{}, err 414 } 415 } else { 416 logger.Info().Msg("Not waiting for chain to finalize first epoch") 417 } 418 419 containers := EthereumNetworkContainers{ 420 { 421 ContainerName: client.GetContainerName(), 422 ContainerType: client.GetContainerType(), 423 Container: client.GetContainer(), 424 }, 425 { 426 ContainerName: beacon.ContainerName, 427 ContainerType: ContainerType_PrysmBeacon, 428 Container: &beacon.Container, 429 }, 430 { 431 ContainerName: validator.ContainerName, 432 ContainerType: ContainerType_PrysmVal, 433 Container: &validator.Container, 434 }, 435 } 436 437 en.Containers = append(en.Containers, containers...) 438 439 rpcProvider.privateHttpUrls = append(rpcProvider.privateHttpUrls, client.GetInternalHttpUrl()) 440 rpcProvider.privatelWsUrls = append(rpcProvider.privatelWsUrls, client.GetInternalWsUrl()) 441 rpcProvider.publiclHttpUrls = append(rpcProvider.publiclHttpUrls, client.GetExternalHttpUrl()) 442 rpcProvider.publicsUrls = append(rpcProvider.publicsUrls, client.GetExternalWsUrl()) 443 444 return net, rpcProvider, nil 445 } 446 447 func (en *EthereumNetwork) startPow() (blockchain.EVMNetwork, RpcProvider, error) { 448 var net blockchain.EVMNetwork 449 rpcProvider := RpcProvider{ 450 privateHttpUrls: []string{}, 451 privatelWsUrls: []string{}, 452 publiclHttpUrls: []string{}, 453 publicsUrls: []string{}, 454 } 455 456 if *en.ExecutionLayer != ExecutionLayer_Geth { 457 return blockchain.EVMNetwork{}, RpcProvider{}, fmt.Errorf("unsupported execution layer: %s", *en.ExecutionLayer) 458 } 459 dockerNetworks, err := en.getOrCreateDockerNetworks() 460 if err != nil { 461 return blockchain.EVMNetwork{}, RpcProvider{}, err 462 } 463 464 geth := NewGeth(dockerNetworks, en.EthereumChainConfig, append(en.getImageOverride(ContainerType_Geth), en.setExistingContainerName(ContainerType_Geth))...) 465 geth.WithTestInstance(en.t) 466 467 network, docker, err := geth.StartContainer() 468 if err != nil { 469 return blockchain.EVMNetwork{}, RpcProvider{}, err 470 } 471 472 net = network 473 containers := EthereumNetworkContainers{ 474 { 475 ContainerName: geth.ContainerName, 476 ContainerType: ContainerType_Geth, 477 Container: &geth.Container, 478 }, 479 } 480 481 en.Containers = append(en.Containers, containers...) 482 rpcProvider.privateHttpUrls = append(rpcProvider.privateHttpUrls, docker.HttpUrl) 483 rpcProvider.privatelWsUrls = append(rpcProvider.privatelWsUrls, docker.WsUrl) 484 rpcProvider.publiclHttpUrls = append(rpcProvider.publiclHttpUrls, geth.ExternalHttpUrl) 485 rpcProvider.publicsUrls = append(rpcProvider.publicsUrls, geth.ExternalWsUrl) 486 487 en.DockerNetworkNames = dockerNetworks 488 489 return net, rpcProvider, nil 490 } 491 492 func (en *EthereumNetwork) getOrCreateDockerNetworks() ([]string, error) { 493 if len(en.DockerNetworkNames) != 0 { 494 return en.DockerNetworkNames, nil 495 } 496 497 network, err := docker.CreateNetwork(logging.GetTestLogger(en.t)) 498 if err != nil { 499 return []string{}, err 500 } 501 502 return []string{network.Name}, nil 503 } 504 505 func (en *EthereumNetwork) Describe() string { 506 cL := "prysm" 507 if en.ConsensusLayer == nil { 508 cL = "(none)" 509 } 510 return fmt.Sprintf("consensus type: %s, execution layer: %s, consensus layer: %s", *en.ConsensusType, *en.ExecutionLayer, cL) 511 } 512 513 func (en *EthereumNetwork) setExistingContainerName(ct ContainerType) EnvComponentOption { 514 if !en.isRecreated { 515 return func(c *EnvComponent) {} 516 } 517 518 for _, container := range en.Containers { 519 if container.ContainerType == ct { 520 return func(c *EnvComponent) { 521 if container.ContainerName != "" { 522 c.ContainerName = container.ContainerName 523 } 524 } 525 } 526 } 527 528 return func(c *EnvComponent) {} 529 } 530 531 func (en *EthereumNetwork) getImageOverride(ct ContainerType) []EnvComponentOption { 532 options := []EnvComponentOption{} 533 if image, ok := en.CustomDockerImages[ct]; ok { 534 options = append(options, WithContainerImageWithVersion(image)) 535 } 536 return options 537 } 538 539 func (en *EthereumNetwork) Save() error { 540 name := fmt.Sprintf("ethereum_network_%s", uuid.NewString()[0:8]) 541 confPath, err := toml_utils.SaveStructAsToml(en, ".private_chains", name) 542 if err != nil { 543 return ErrTestConfigNotSaved 544 } 545 546 log := logging.GetTestLogger(en.t) 547 log.Info().Msgf("Saved private Ethereum Network config. To reuse in e2e tests, set: %s=%s", CONFIG_ENV_VAR_NAME, confPath) 548 549 return nil 550 } 551 552 func (en *EthereumNetwork) Validate() error { 553 if en.ConsensusType == nil || *en.ConsensusType == "" { 554 return ErrMissingConsensusType 555 } 556 557 if en.ExecutionLayer == nil || *en.ExecutionLayer == "" { 558 return ErrMissingExecutionLayer 559 } 560 561 if *en.ConsensusType == ConsensusType_PoS && (en.ConsensusLayer == nil || *en.ConsensusLayer == "") { 562 return ErrMissingConsensusLayer 563 } 564 565 if *en.ConsensusType == ConsensusType_PoW && (en.ConsensusLayer != nil && *en.ConsensusLayer != "") { 566 return ErrConsensusLayerNotAllowed 567 } 568 569 if en.EthereumChainConfig == nil { 570 return errors.New("ethereum chain config is required") 571 } 572 573 return en.EthereumChainConfig.Validate(logging.GetTestLogger(nil), *en.ConsensusType) 574 } 575 576 func (en *EthereumNetwork) ApplyOverrides(from *EthereumNetwork) error { 577 if from == nil { 578 return nil 579 } 580 if from.ConsensusLayer != nil { 581 en.ConsensusLayer = from.ConsensusLayer 582 } 583 if from.ExecutionLayer != nil { 584 en.ExecutionLayer = from.ExecutionLayer 585 } 586 if from.ConsensusType != nil { 587 en.ConsensusType = from.ConsensusType 588 } 589 if from.WaitForFinalization != nil { 590 en.WaitForFinalization = from.WaitForFinalization 591 } 592 593 if from.EthereumChainConfig != nil { 594 if en.EthereumChainConfig == nil { 595 en.EthereumChainConfig = from.EthereumChainConfig 596 } else { 597 err := en.EthereumChainConfig.ApplyOverrides(from.EthereumChainConfig) 598 if err != nil { 599 return fmt.Errorf("error applying overrides from network config file to config: %w", err) 600 } 601 } 602 } 603 604 return nil 605 } 606 607 // maybe only store ports here and depending on where the test is executed return different URLs? 608 // maybe 3 different constructors for each "perspective"? (docker, k8s with local runner, k8s with remote runner) 609 type RpcProvider struct { 610 privateHttpUrls []string 611 privatelWsUrls []string 612 publiclHttpUrls []string 613 publicsUrls []string 614 } 615 616 func (s *RpcProvider) PrivateHttpUrls() []string { 617 return s.privateHttpUrls 618 } 619 620 func (s *RpcProvider) PrivateWsUrsl() []string { 621 return s.privatelWsUrls 622 } 623 624 func (s *RpcProvider) PublicHttpUrls() []string { 625 return s.publiclHttpUrls 626 } 627 628 func (s *RpcProvider) PublicWsUrls() []string { 629 return s.publicsUrls 630 } 631 632 type ContainerType string 633 634 const ( 635 ContainerType_Geth ContainerType = "geth" 636 ContainerType_Erigon ContainerType = "erigon" 637 ContainerType_Besu ContainerType = "besu" 638 ContainerType_Nethermind ContainerType = "nethermind" 639 ContainerType_PrysmBeacon ContainerType = "prysm-beacon" 640 ContainerType_PrysmVal ContainerType = "prysm-validator" 641 ContainerType_GenesisGenerator ContainerType = "genesis-generator" 642 ContainerType_ValKeysGenerator ContainerType = "val-keys-generator" 643 ) 644 645 type EthereumNetworkContainer struct { 646 ContainerName string `toml:"container_name"` 647 ContainerType ContainerType `toml:"container_type"` 648 Container *tc.Container `toml:"-"` 649 } 650 651 type EthereumNetworkContainers []EthereumNetworkContainer 652 653 func createHostDirectories() (string, string, error) { 654 customConfigDataDir, err := os.MkdirTemp("", "custom_config_data") 655 if err != nil { 656 return "", "", err 657 } 658 659 valKeysDir, err := os.MkdirTemp("", "val_keys") 660 if err != nil { 661 return "", "", err 662 } 663 664 return customConfigDataDir, valKeysDir, nil 665 } 666 667 func waitForChainToFinaliseAnEpoch(lggr zerolog.Logger, evmClient blockchain.EVMClient, timeout time.Duration) error { 668 lggr.Info().Msg("Waiting for chain to finalize an epoch") 669 670 pollInterval := 15 * time.Second 671 endTime := time.Now().Add(timeout) 672 673 chainStarted := false 674 for { 675 finalized, err := evmClient.GetLatestFinalizedBlockHeader(context.Background()) 676 if err != nil { 677 if strings.Contains(err.Error(), "finalized block not found") { 678 lggr.Err(err).Msgf("error getting finalized block number for %s", evmClient.GetNetworkName()) 679 } else { 680 timeLeft := time.Until(endTime).Seconds() 681 lggr.Warn().Msgf("no epoch finalized yet for chain %s. Time left: %d sec", evmClient.GetNetworkName(), int(timeLeft)) 682 } 683 } 684 685 if finalized != nil && finalized.Number.Int64() > 0 || time.Now().After(endTime) { 686 lggr.Info().Msgf("Chain '%s' finalized an epoch", evmClient.GetNetworkName()) 687 chainStarted = true 688 break 689 } 690 691 time.Sleep(pollInterval) 692 } 693 694 if !chainStarted { 695 return fmt.Errorf("chain %s failed to finalize an epoch", evmClient.GetNetworkName()) 696 } 697 698 return nil 699 } 700 701 func NewPrivateChainEnvConfigFromFile(path string) (EthereumNetwork, error) { 702 c := EthereumNetwork{} 703 err := toml_utils.OpenTomlFileAsStruct(path, &c) 704 return c, err 705 }