diff --git a/config/private_ethereum_network.go b/config/private_ethereum_network.go index da6163b0d..b82d4a65e 100644 --- a/config/private_ethereum_network.go +++ b/config/private_ethereum_network.go @@ -11,6 +11,8 @@ import ( "github.com/rs/zerolog" tc "github.com/testcontainers/testcontainers-go" + config_types "github.com/smartcontractkit/chainlink-testing-framework/config/types" + "github.com/smartcontractkit/chainlink-testing-framework/docker/ethereum" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/utils/slice" ) @@ -23,18 +25,18 @@ var ( ) type EthereumNetworkConfig struct { - ConsensusType *EthereumVersion `toml:"consensus_type"` - EthereumVersion *EthereumVersion `toml:"ethereum_version"` - ConsensusLayer *ConsensusLayer `toml:"consensus_layer"` - ExecutionLayer *ExecutionLayer `toml:"execution_layer"` - DockerNetworkNames []string `toml:"docker_network_names"` - Containers EthereumNetworkContainers `toml:"containers"` - WaitForFinalization *bool `toml:"wait_for_finalization"` - GeneratedDataHostDir *string `toml:"generated_data_host_dir"` - ValKeysDir *string `toml:"val_keys_dir"` - EthereumChainConfig *EthereumChainConfig `toml:"EthereumChainConfig"` - CustomDockerImages map[ContainerType]string `toml:"CustomDockerImages"` - NodeLogLevel *string `toml:"node_log_level,omitempty"` + ConsensusType *config_types.EthereumVersion `toml:"consensus_type"` + EthereumVersion *config_types.EthereumVersion `toml:"ethereum_version"` + ConsensusLayer *ConsensusLayer `toml:"consensus_layer"` + ExecutionLayer *config_types.ExecutionLayer `toml:"execution_layer"` + DockerNetworkNames []string `toml:"docker_network_names"` + Containers EthereumNetworkContainers `toml:"containers"` + WaitForFinalization *bool `toml:"wait_for_finalization"` + GeneratedDataHostDir *string `toml:"generated_data_host_dir"` + ValKeysDir *string `toml:"val_keys_dir"` + EthereumChainConfig *EthereumChainConfig `toml:"EthereumChainConfig"` + CustomDockerImages map[ContainerType]string `toml:"CustomDockerImages"` + NodeLogLevel *string `toml:"node_log_level,omitempty"` } func (en *EthereumNetworkConfig) Validate() error { @@ -49,10 +51,12 @@ func (en *EthereumNetworkConfig) Validate() error { l.Debug().Msg("Using _deprecated_ ConsensusType as EthereumVersion") tempEthVersion := en.ConsensusType switch *tempEthVersion { - case EthereumVersion_Eth1, EthereumVersion_Eth1_Legacy: - *tempEthVersion = EthereumVersion_Eth1 - case EthereumVersion_Eth2, EthereumVersion_Eth2_Legacy: - *tempEthVersion = EthereumVersion_Eth2 + //nolint:staticcheck //ignore SA1019 + case config_types.EthereumVersion_Eth1, config_types.EthereumVersion_Eth1_Legacy: + *tempEthVersion = config_types.EthereumVersion_Eth1 + //nolint:staticcheck //ignore SA1019 + case config_types.EthereumVersion_Eth2, config_types.EthereumVersion_Eth2_Legacy: + *tempEthVersion = config_types.EthereumVersion_Eth2 default: return fmt.Errorf("unknown ethereum version (consensus type): %s", *en.ConsensusType) } @@ -68,12 +72,14 @@ func (en *EthereumNetworkConfig) Validate() error { return ErrMissingExecutionLayer } - if (en.EthereumVersion != nil && (*en.EthereumVersion == EthereumVersion_Eth2_Legacy || *en.EthereumVersion == EthereumVersion_Eth2)) && (en.ConsensusLayer == nil || *en.ConsensusLayer == "") { + //nolint:staticcheck //ignore SA1019 + if (en.EthereumVersion != nil && (*en.EthereumVersion == config_types.EthereumVersion_Eth2_Legacy || *en.EthereumVersion == config_types.EthereumVersion_Eth2)) && (en.ConsensusLayer == nil || *en.ConsensusLayer == "") { l.Warn().Msg("Consensus layer is not set, but is required for PoS. Defaulting to Prysm") en.ConsensusLayer = &ConsensusLayer_Prysm } - if (en.EthereumVersion != nil && (*en.EthereumVersion == EthereumVersion_Eth1_Legacy || *en.EthereumVersion == EthereumVersion_Eth1)) && (en.ConsensusLayer != nil && *en.ConsensusLayer != "") { + //nolint:staticcheck //ignore SA1019 + if (en.EthereumVersion != nil && (*en.EthereumVersion == config_types.EthereumVersion_Eth1_Legacy || *en.EthereumVersion == config_types.EthereumVersion_Eth1)) && (en.ConsensusLayer != nil && *en.ConsensusLayer != "") { l.Warn().Msg("Consensus layer is set, but is not allowed for PoW. Ignoring") en.ConsensusLayer = nil } @@ -82,7 +88,7 @@ func (en *EthereumNetworkConfig) Validate() error { en.NodeLogLevel = &DefaultNodeLogLevel } - if *en.EthereumVersion == EthereumVersion_Eth1 && *en.ExecutionLayer == ExecutionLayer_Reth { + if *en.EthereumVersion == config_types.EthereumVersion_Eth1 && *en.ExecutionLayer == config_types.ExecutionLayer_Reth { msg := `%s If you are using builder to create the network, please change the EthereumVersion to EthereumVersion_Eth2 by calling this method: @@ -105,7 +111,7 @@ ethereum_version="eth2" return errors.New("ethereum chain config is required") } - return en.EthereumChainConfig.Validate(l, en.EthereumVersion) + return en.EthereumChainConfig.Validate(l, en.EthereumVersion, en.ExecutionLayer, en.CustomDockerImages) } func (en *EthereumNetworkConfig) ApplyOverrides(from *EthereumNetworkConfig) error { @@ -163,27 +169,6 @@ const ( ConsensusType_PoW ConsensusType = "pow" ) -type EthereumVersion string - -const ( - EthereumVersion_Eth2 EthereumVersion = "eth2" - // Deprecated: use EthereumVersion_Eth2 instead - EthereumVersion_Eth2_Legacy EthereumVersion = "pos" - EthereumVersion_Eth1 EthereumVersion = "eth1" - // Deprecated: use EthereumVersion_Eth1 instead - EthereumVersion_Eth1_Legacy EthereumVersion = "pow" -) - -type ExecutionLayer string - -const ( - ExecutionLayer_Geth ExecutionLayer = "geth" - ExecutionLayer_Nethermind ExecutionLayer = "nethermind" - ExecutionLayer_Erigon ExecutionLayer = "erigon" - ExecutionLayer_Besu ExecutionLayer = "besu" - ExecutionLayer_Reth ExecutionLayer = "reth" -) - type ConsensusLayer string var ConsensusLayer_Prysm ConsensusLayer = "prysm" @@ -200,10 +185,6 @@ const ( ContainerType_ValKeysGenerator ContainerType = "val_keys_generator" ) -const ( - UnsopportedForkErr = "only 'Electra' and 'EOF' hard forks are supported" -) - type EthereumChainConfig struct { SecondsPerSlot int `json:"seconds_per_slot" toml:"seconds_per_slot"` SlotsPerEpoch int `json:"slots_per_epoch" toml:"slots_per_epoch"` @@ -218,6 +199,7 @@ type EthereumChainConfig struct { //go:embed tomls/default_ethereum_env.toml var defaultEthereumChainConfig []byte +// Default sets the EthereumChainConfig to the default values func (c *EthereumChainConfig) Default() error { wrapper := struct { EthereumNetwork *EthereumNetworkConfig `toml:"PrivateEthereumNetwork"` @@ -239,7 +221,8 @@ func (c *EthereumChainConfig) Default() error { return nil } -func GetDefaultChainConfig() EthereumChainConfig { +// MustGetDefaultChainConfig returns the default EthereumChainConfig or panics if it can't be loaded +func MustGetDefaultChainConfig() EthereumChainConfig { config := EthereumChainConfig{} if err := config.Default(); err != nil { panic(err) @@ -247,14 +230,16 @@ func GetDefaultChainConfig() EthereumChainConfig { return config } -func (c *EthereumChainConfig) Validate(logger zerolog.Logger, ethereumVersion *EthereumVersion) error { +// Validate validates the EthereumChainConfig +func (c *EthereumChainConfig) Validate(l zerolog.Logger, ethereumVersion *config_types.EthereumVersion, executionLayer *config_types.ExecutionLayer, customDockerImages map[ContainerType]string) error { if c.ChainID < 1 { return fmt.Errorf("chain id must be >= 0") } // don't like it 100% but in cases where we load private ethereum network config from TOML it might be incomplete // until we pass it to ethereum network builder that will fill in defaults - if ethereumVersion == nil || (*ethereumVersion == EthereumVersion_Eth1_Legacy || *ethereumVersion == EthereumVersion_Eth1) { + //nolint:staticcheck //ignore SA1019 + if ethereumVersion == nil || (*ethereumVersion == config_types.EthereumVersion_Eth1_Legacy || *ethereumVersion == config_types.EthereumVersion_Eth1) { return nil } @@ -274,7 +259,7 @@ func (c *EthereumChainConfig) Validate(logger zerolog.Logger, ethereumVersion *E return fmt.Errorf("genesis timestamp must be generated by calling GenerateGenesisTimestamp()") } - if err := c.ValidateHardForks(); err != nil { + if err := c.ValidateHardForks(l, ethereumVersion, executionLayer, customDockerImages); err != nil { return err } @@ -286,23 +271,84 @@ func (c *EthereumChainConfig) Validate(logger zerolog.Logger, ethereumVersion *E return err } if hadDuplicates { - logger.Warn().Msg("Duplicate addresses found in addresses_to_fund. Removed them. You might want to review your configuration.") + l.Warn().Msg("Duplicate addresses found in addresses_to_fund. Removed them. You might want to review your configuration.") } return nil } -func (c *EthereumChainConfig) ValidateHardForks() error { - if len(c.HardForkEpochs) == 0 { +// ValidateHardForks validates hard forks based either on custom or default docker images for eth2 execution layer +func (c *EthereumChainConfig) ValidateHardForks(l zerolog.Logger, ethereumVersion *config_types.EthereumVersion, executionLayer *config_types.ExecutionLayer, customDockerImages map[ContainerType]string) error { + //nolint:staticcheck //ignore SA1019 + if ethereumVersion == nil || (*ethereumVersion == config_types.EthereumVersion_Eth1_Legacy || *ethereumVersion == config_types.EthereumVersion_Eth1) { return nil } - // currently Prysm Beacon Chain doesn't support any fork (Electra is coming in 2025) - c.HardForkEpochs = map[string]int{} + customImage := customDockerImages[ContainerType_ExecutionLayer] + var baseEthereumFork ethereum.Fork + var err error + if customImage == "" { + if executionLayer == nil { + return ErrMissingExecutionLayer + } + var dockerImage string + switch *executionLayer { + case config_types.ExecutionLayer_Geth: + dockerImage = ethereum.DefaultGethEth2Image + case config_types.ExecutionLayer_Nethermind: + dockerImage = ethereum.DefaultNethermindEth2Image + case config_types.ExecutionLayer_Erigon: + dockerImage = ethereum.DefaultErigonEth2Image + case config_types.ExecutionLayer_Besu: + dockerImage = ethereum.DefaultBesuEth2Image + case config_types.ExecutionLayer_Reth: + dockerImage = ethereum.DefaultRethEth2Image + } + baseEthereumFork, err = ethereum.LastSupportedForkForEthereumClient(dockerImage) + } else { + baseEthereumFork, err = ethereum.LastSupportedForkForEthereumClient(customImage) + } + + if err != nil { + return err + } + + validFutureForks, err := baseEthereumFork.ValidFutureForks() + if err != nil { + return err + } + + validForks := make(map[string]int) + + // latest Prysm Beacon Chain doesn't support any fork (Electra is coming in 2025) + // but older versions do support Deneb + for fork, epoch := range c.HardForkEpochs { + isValid := false + for _, validFork := range validFutureForks { + if strings.EqualFold(fork, string(validFork)) { + isValid = true + validForks[fork] = epoch + break + } + } + + if !isValid { + l.Debug().Msgf("Fork %s is not supported. Removed it from configuration", fork) + } + } + + // at the same time for Shanghai-based forks we need to add Deneb to the list if it's not there, so that genesis is valid + if _, ok := c.HardForkEpochs[string(ethereum.EthereumFork_Deneb)]; !ok && baseEthereumFork == ethereum.EthereumFork_Shanghai { + l.Debug().Msg("Adding Deneb to fork setup, because it's required, but was missing from the configuration. It's scheduled for epoch 1000") + validForks[string(ethereum.EthereumFork_Deneb)] = 1000 + } + + c.HardForkEpochs = validForks return nil } +// ApplyOverrides applies overrides from another EthereumChainConfig func (c *EthereumChainConfig) ApplyOverrides(from *EthereumChainConfig) error { if from == nil { return nil @@ -328,8 +374,9 @@ func (c *EthereumChainConfig) ApplyOverrides(from *EthereumChainConfig) error { return nil } +// FillInMissingValuesWithDefault fills in missing/zero values with default values func (c *EthereumChainConfig) FillInMissingValuesWithDefault() { - defaultConfig := GetDefaultChainConfig() + defaultConfig := MustGetDefaultChainConfig() if c.ValidatorCount == 0 { c.ValidatorCount = defaultConfig.ValidatorCount } @@ -356,18 +403,21 @@ func (c *EthereumChainConfig) FillInMissingValuesWithDefault() { } } -func (c *EthereumChainConfig) GetValidatorBasedGenesisDelay() int { +// ValidatorBasedGenesisDelay returns the delay in seconds based on the number of validators +func (c *EthereumChainConfig) ValidatorBasedGenesisDelay() int { return c.ValidatorCount * 5 } func (c *EthereumChainConfig) GenerateGenesisTimestamp() { - c.GenesisTimestamp = int(time.Now().Unix()) + c.GetValidatorBasedGenesisDelay() + c.GenesisTimestamp = int(time.Now().Unix()) + c.ValidatorBasedGenesisDelay() } -func (c *EthereumChainConfig) GetDefaultWaitDuration() time.Duration { - return time.Duration((c.GenesisDelay+c.GetValidatorBasedGenesisDelay())*2) * time.Second +// DefaultWaitDuration returns the default wait duration for the network based on the genesis delay and the number of validators +func (c *EthereumChainConfig) DefaultWaitDuration() time.Duration { + return time.Duration((c.GenesisDelay+c.ValidatorBasedGenesisDelay())*2) * time.Second } -func (c *EthereumChainConfig) GetDefaultFinalizationWaitDuration() time.Duration { +// DefaultFinalizationWaitDuration returns the default wait duration for finalization +func (c *EthereumChainConfig) DefaultFinalizationWaitDuration() time.Duration { return 5 * time.Minute } diff --git a/docker/test_env/messages.go b/config/types/messages.go similarity index 92% rename from docker/test_env/messages.go rename to config/types/messages.go index afb88a74a..ae593d304 100644 --- a/docker/test_env/messages.go +++ b/config/types/messages.go @@ -1,4 +1,4 @@ -package test_env +package types const ( MsgInvalidDockerImageFormat = "invalid docker image format: %s" diff --git a/config/types/types.go b/config/types/types.go new file mode 100644 index 000000000..bdfab9657 --- /dev/null +++ b/config/types/types.go @@ -0,0 +1,22 @@ +package types + +type ExecutionLayer string + +const ( + ExecutionLayer_Geth ExecutionLayer = "geth" + ExecutionLayer_Nethermind ExecutionLayer = "nethermind" + ExecutionLayer_Erigon ExecutionLayer = "erigon" + ExecutionLayer_Besu ExecutionLayer = "besu" + ExecutionLayer_Reth ExecutionLayer = "reth" +) + +type EthereumVersion string + +const ( + EthereumVersion_Eth2 EthereumVersion = "eth2" + // Deprecated: use EthereumVersion_Eth2 instead + EthereumVersion_Eth2_Legacy EthereumVersion = "pos" + EthereumVersion_Eth1 EthereumVersion = "eth1" + // Deprecated: use EthereumVersion_Eth1 instead + EthereumVersion_Eth1_Legacy EthereumVersion = "pow" +) diff --git a/docker/ethereum/ethereum.go b/docker/ethereum/ethereum.go new file mode 100644 index 000000000..a3f6be5f6 --- /dev/null +++ b/docker/ethereum/ethereum.go @@ -0,0 +1,165 @@ +package ethereum + +import ( + "errors" + "fmt" + "strings" + + "github.com/Masterminds/semver/v3" + + config_types "github.com/smartcontractkit/chainlink-testing-framework/config/types" + docker_utils "github.com/smartcontractkit/chainlink-testing-framework/utils/docker" +) + +type Fork string + +const ( + EthereumFork_Shanghai Fork = "Shanghai" + EthereumFork_Deneb Fork = "Deneb" +) + +// ValidFutureForks returns the list of valid future forks for the given Ethereum fork +func (e Fork) ValidFutureForks() ([]Fork, error) { + switch e { + case EthereumFork_Shanghai: + return []Fork{EthereumFork_Deneb}, nil + case EthereumFork_Deneb: + return []Fork{}, nil + default: + return []Fork{}, fmt.Errorf("unknown fork: %s", e) + } +} + +// LastSupportedForkForEthereumClient returns the last supported fork for the given Ethereum client. It supports only eth2 clients. +func LastSupportedForkForEthereumClient(imageWithVersion string) (Fork, error) { + ethereumVersion, err := VersionFromImage(imageWithVersion) + if err != nil { + return "", err + } + + //nolint:staticcheck //ignore SA1019 + if ethereumVersion == config_types.EthereumVersion_Eth1 || ethereumVersion == config_types.EthereumVersion_Eth1_Legacy { + return "", fmt.Errorf("ethereum version '%s' is not supported", ethereumVersion) + } + + executionLayer, err := ExecutionLayerFromDockerImage(imageWithVersion) + if err != nil { + return "", err + } + + version, err := docker_utils.GetSemverFromImage(imageWithVersion) + if err != nil { + return "", err + } + + var constraint *semver.Constraints + + switch executionLayer { + case config_types.ExecutionLayer_Besu: + constraint, err = semver.NewConstraint("<24.1") + case config_types.ExecutionLayer_Geth: + constraint, err = semver.NewConstraint("<1.13.12") + case config_types.ExecutionLayer_Erigon: + constraint, err = semver.NewConstraint("= 246 { + kgzConstraint, err := semver.NewConstraint(">=23.1 <24.0") + if err != nil { + return nil, fmt.Errorf("failed to parse constraint: %s", ">=23.1 && <23.7") + } + + if kgzConstraint.Check(version) { + cmd = append(cmd, "--kzg-trusted-setup", fmt.Sprintf("%s/trusted_setup.txt", g.generatedDataContainerDir)) + } + + bonsaiConstraint, err := semver.NewConstraint(">=24.6") + if err != nil { + return nil, fmt.Errorf("failed to parse constraint: %s", ">=24.6") + } + + if bonsaiConstraint.Check(version) { // it crashes with sync-mode=FULL, and when we use a different sync mode then consensus client fails to propose correct blocks cmd = append(cmd, "--bonsai-limit-trie-logs-enabled=false") } @@ -102,7 +121,7 @@ func (g *Besu) getEth2ContainerRequest() (*tc.ContainerRequest, error) { hostConfig.Mounts = append(hostConfig.Mounts, mount.Mount{ Type: mount.TypeBind, Source: g.generatedDataHostDir, - Target: GENERATED_DATA_DIR_INSIDE_CONTAINER, + Target: g.generatedDataContainerDir, ReadOnly: false, }) }, diff --git a/docker/test_env/besu_test.go b/docker/test_env/besu_test.go index 1d4fcf1c8..e2c145fa5 100644 --- a/docker/test_env/besu_test.go +++ b/docker/test_env/besu_test.go @@ -9,6 +9,8 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/blockchain" "github.com/smartcontractkit/chainlink-testing-framework/config" + config_types "github.com/smartcontractkit/chainlink-testing-framework/config/types" + "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" ) @@ -19,8 +21,8 @@ func TestBesuEth1(t *testing.T) { builder := NewEthereumNetworkBuilder() cfg, err := builder. //nolint:staticcheck //ignore SA1019 - WithEthereumVersion(config.EthereumVersion_Eth1_Legacy). - WithExecutionLayer(config.ExecutionLayer_Besu). + WithEthereumVersion(config_types.EthereumVersion_Eth1_Legacy). + WithExecutionLayer(config_types.ExecutionLayer_Besu). Build() require.NoError(t, err, "Builder validation failed") @@ -38,15 +40,14 @@ func TestBesuEth1(t *testing.T) { require.NoError(t, err, "Couldn't close the client") } -func TestBesuEth2(t *testing.T) { +func TestBesuEth2_Deneb(t *testing.T) { l := logging.GetTestLogger(t) builder := NewEthereumNetworkBuilder() cfg, err := builder. - //nolint:staticcheck //ignore SA1019 - WithEthereumVersion(config.EthereumVersion_Eth2_Legacy). + WithCustomDockerImages(map[config.ContainerType]string{config.ContainerType_ExecutionLayer: "hyperledger/besu:24.1.0"}). WithConsensusLayer(config.ConsensusLayer_Prysm). - WithExecutionLayer(config.ExecutionLayer_Besu). + WithExecutionLayer(config_types.ExecutionLayer_Besu). Build() require.NoError(t, err, "Builder validation failed") @@ -76,5 +77,38 @@ func TestBesuEth2(t *testing.T) { _, err = blockchain.ConnectEVMClient(eip1559Network, l) require.Error(t, err, "Could not connect to Besu") require.Contains(t, err.Error(), "Method not found", "Besu should not work EIP-1559 yet") +} + +func TestBesuEth2_Shanghai(t *testing.T) { + l := logging.GetTestLogger(t) + + chainConfig := config.MustGetDefaultChainConfig() + chainConfig.HardForkEpochs = map[string]int{"Deneb": 500} + builder := NewEthereumNetworkBuilder() + cfg, err := builder. + WithCustomDockerImages(map[config.ContainerType]string{config.ContainerType_ExecutionLayer: "hyperledger/besu:23.10"}). + WithExecutionLayer(config_types.ExecutionLayer_Besu). + WithEthereumChainConfig(chainConfig). + Build() + require.NoError(t, err, "Builder validation failed") + + _, eth2, err := cfg.Start() + require.NoError(t, err, "Couldn't start PoS network") + + nonEip1559Network := blockchain.SimulatedEVMNetwork + nonEip1559Network.Name = "Simulated Besu + Prysm (non-EIP 1559)" + nonEip1559Network.GasEstimationBuffer = 10_000_000_000 + nonEip1559Network.URLs = eth2.PublicWsUrls() + clientOne, err := blockchain.ConnectEVMClient(nonEip1559Network, l) + require.NoError(t, err, "Couldn't connect to the evm client") + + t.Cleanup(func() { + err = clientOne.Close() + require.NoError(t, err, "Couldn't close the client") + }) + + address := common.HexToAddress("0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1") + err = sendAndCompareBalances(testcontext.Get(t), clientOne, address) + require.NoError(t, err, fmt.Sprintf("balance wasn't correctly updated for %s network", nonEip1559Network.Name)) } diff --git a/docker/test_env/cmd/internal/start_test_env_commands.go b/docker/test_env/cmd/internal/start_test_env_commands.go index 9d8d59b7d..affe2bed7 100644 --- a/docker/test_env/cmd/internal/start_test_env_commands.go +++ b/docker/test_env/cmd/internal/start_test_env_commands.go @@ -15,6 +15,7 @@ import ( "github.com/testcontainers/testcontainers-go" "github.com/smartcontractkit/chainlink-testing-framework/config" + config_types "github.com/smartcontractkit/chainlink-testing-framework/config/types" "github.com/smartcontractkit/chainlink-testing-framework/docker/test_env" "github.com/smartcontractkit/chainlink-testing-framework/logging" ) @@ -167,9 +168,9 @@ func startPrivateEthChainE(cmd *cobra.Command, args []string) error { } builder := test_env.NewEthereumNetworkBuilder() - builder = *builder.WithEthereumVersion(config.EthereumVersion(ethereumVersion)). + builder = *builder.WithEthereumVersion(config_types.EthereumVersion(ethereumVersion)). WithConsensusLayer(consensusLayerToUse). - WithExecutionLayer(config.ExecutionLayer(executionLayer)). + WithExecutionLayer(config_types.ExecutionLayer(executionLayer)). WithEthereumChainConfig(config.EthereumChainConfig{ ValidatorCount: 8, SlotsPerEpoch: 2, diff --git a/docker/test_env/env_component_test.go b/docker/test_env/env_component_test.go index d09f5f3a2..241bbd4dc 100644 --- a/docker/test_env/env_component_test.go +++ b/docker/test_env/env_component_test.go @@ -39,7 +39,7 @@ func TestEthEnvComponentPauseChaos(t *testing.T) { network, err := docker.CreateNetwork(l) require.NoError(t, err) - defaultChainCfg := config.GetDefaultChainConfig() + defaultChainCfg := config.MustGetDefaultChainConfig() g := NewGethEth1([]string{network.Name}, &defaultChainCfg). WithTestInstance(t) _, err = g.StartContainer() diff --git a/docker/test_env/erigon_base.go b/docker/test_env/erigon_base.go index 176263eba..c55f0da40 100644 --- a/docker/test_env/erigon_base.go +++ b/docker/test_env/erigon_base.go @@ -6,25 +6,20 @@ import ( "testing" "time" + "github.com/Masterminds/semver/v3" "github.com/rs/zerolog" - tc "github.com/testcontainers/testcontainers-go" tcwait "github.com/testcontainers/testcontainers-go/wait" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" "github.com/smartcontractkit/chainlink-testing-framework/config" + config_types "github.com/smartcontractkit/chainlink-testing-framework/config/types" "github.com/smartcontractkit/chainlink-testing-framework/docker" "github.com/smartcontractkit/chainlink-testing-framework/logging" + docker_utils "github.com/smartcontractkit/chainlink-testing-framework/utils/docker" "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" ) -const ( - defaultErigonEth1Image = "thorax/erigon:v2.40.0" - defaultErigonEth2Image = "thorax/erigon:v2.59.3" // v.2.60.0 is the latest, but gas estimations using zero address are broken - erigonBaseImageName = "thorax/erigon" - erigonGitRepo = "ledgerwatch/erigon" -) - type Erigon struct { EnvComponent ExternalHttpUrl string @@ -33,12 +28,12 @@ type Erigon struct { InternalWsUrl string InternalExecutionURL string ExternalExecutionURL string - generatedDataHostDir string chainConfig *config.EthereumChainConfig consensusLayer config.ConsensusLayer - ethereumVersion config.EthereumVersion + ethereumVersion config_types.EthereumVersion l zerolog.Logger t *testing.T + posContainerSettings } func (g *Erigon) WithTestInstance(t *testing.T) ExecutionClient { @@ -50,7 +45,7 @@ func (g *Erigon) WithTestInstance(t *testing.T) ExecutionClient { func (g *Erigon) StartContainer() (blockchain.EVMNetwork, error) { var r *tc.ContainerRequest var err error - if g.GetEthereumVersion() == config.EthereumVersion_Eth1 { + if g.GetEthereumVersion() == config_types.EthereumVersion_Eth1 { r, err = g.getEth1ContainerRequest() } else { r, err = g.getEth2ContainerRequest() @@ -79,7 +74,7 @@ func (g *Erigon) StartContainer() (blockchain.EVMNetwork, error) { return blockchain.EVMNetwork{}, err } - if g.GetEthereumVersion() == config.EthereumVersion_Eth2 { + if g.GetEthereumVersion() == config_types.EthereumVersion_Eth2 { executionPort, err := ct.MappedPort(testcontext.Get(g.t), NatPort(ETH2_EXECUTION_PORT)) if err != nil { return blockchain.EVMNetwork{}, err @@ -95,7 +90,7 @@ func (g *Erigon) StartContainer() (blockchain.EVMNetwork, error) { g.InternalWsUrl = FormatWsUrl(g.ContainerName, DEFAULT_EVM_NODE_HTTP_PORT) networkConfig := blockchain.SimulatedEVMNetwork - if g.GetEthereumVersion() == config.EthereumVersion_Eth1 { + if g.GetEthereumVersion() == config_types.EthereumVersion_Eth1 { networkConfig.Name = fmt.Sprintf("Private Eth-1-PoW [erigon %s]", g.ContainerVersion) } else { networkConfig.Name = fmt.Sprintf("Private Eth-2-PoS [erigon %s] + %s", g.ContainerVersion, g.consensusLayer) @@ -111,14 +106,14 @@ func (g *Erigon) StartContainer() (blockchain.EVMNetwork, error) { } func (g *Erigon) GetInternalExecutionURL() string { - if g.GetEthereumVersion() == config.EthereumVersion_Eth1 { + if g.GetEthereumVersion() == config_types.EthereumVersion_Eth1 { panic("eth1 node doesn't have an execution URL") } return g.InternalExecutionURL } func (g *Erigon) GetExternalExecutionURL() string { - if g.GetEthereumVersion() == config.EthereumVersion_Eth1 { + if g.GetEthereumVersion() == config_types.EthereumVersion_Eth1 { panic("eth1 node doesn't have an execution URL") } return g.ExternalExecutionURL @@ -148,12 +143,12 @@ func (g *Erigon) GetContainer() *tc.Container { return &g.Container } -func (g *Erigon) GetEthereumVersion() config.EthereumVersion { +func (g *Erigon) GetEthereumVersion() config_types.EthereumVersion { return g.ethereumVersion } func (g *Erigon) WaitUntilChainIsReady(ctx context.Context, waitTime time.Duration) error { - if g.GetEthereumVersion() == config.EthereumVersion_Eth1 { + if g.GetEthereumVersion() == config_types.EthereumVersion_Eth1 { return nil } waitForFirstBlock := tcwait.NewLogStrategy("Built block").WithPollInterval(1 * time.Second).WithStartupTimeout(waitTime) @@ -161,28 +156,47 @@ func (g *Erigon) WaitUntilChainIsReady(ctx context.Context, waitTime time.Durati } func (g *Erigon) GethConsensusMechanism() ConsensusMechanism { - if g.GetEthereumVersion() == config.EthereumVersion_Eth1 { + if g.GetEthereumVersion() == config_types.EthereumVersion_Eth1 { return ConsensusMechanism_PoW } return ConsensusMechanism_PoS } func (g *Erigon) getExtraExecutionFlags() (string, error) { - version, err := GetComparableVersionFromDockerImage(g.GetImageWithVersion()) + version, err := docker_utils.GetSemverFromImage(g.GetImageWithVersion()) if err != nil { return "", err } extraExecutionFlags := "" - if version > 247 { + + // Erigon v2.47.0 and above have a new flag for disabling tx fee cap + txFeeCapConstraint, err := semver.NewConstraint(">= 2.47.0") + if err != nil { + return "", err + } + + if txFeeCapConstraint.Check(version) { extraExecutionFlags = " --rpc.txfeecap=0" } - if version > 254 { + // Erigon v2.54.0 and above have a new flag for allowing unprotected txs + allowUnprotectedTxsConstraint, err := semver.NewConstraint(">= 2.54.0") + if err != nil { + return "", err + } + + if allowUnprotectedTxsConstraint.Check(version) { extraExecutionFlags += " --rpc.allow-unprotected-txs" } - if version > 242 { + // Erigon v2.42.0 and above have a new flag for setting the db size limit + dbSizeLimitConstraint, err := semver.NewConstraint(">= 2.42.0") + if err != nil { + return "", err + } + + if dbSizeLimitConstraint.Check(version) { extraExecutionFlags += " --db.size.limit=8GB" } diff --git a/docker/test_env/erigon_eth1.go b/docker/test_env/erigon_eth1.go index 35cede17b..7ef8b40ee 100644 --- a/docker/test_env/erigon_eth1.go +++ b/docker/test_env/erigon_eth1.go @@ -15,6 +15,8 @@ import ( tcwait "github.com/testcontainers/testcontainers-go/wait" "github.com/smartcontractkit/chainlink-testing-framework/config" + config_types "github.com/smartcontractkit/chainlink-testing-framework/config/types" + "github.com/smartcontractkit/chainlink-testing-framework/docker/ethereum" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/mirror" "github.com/smartcontractkit/chainlink-testing-framework/utils/templates" @@ -22,7 +24,7 @@ import ( // NewErigonEth1 starts a new Erigon Eth1 node running in Docker func NewErigonEth1(networks []string, chainConfig *config.EthereumChainConfig, opts ...EnvComponentOption) (*Erigon, error) { - parts := strings.Split(defaultErigonEth1Image, ":") + parts := strings.Split(ethereum.DefaultErigonEth1Image, ":") g := &Erigon{ EnvComponent: EnvComponent{ ContainerName: fmt.Sprintf("%s-%s", "erigon-eth1", uuid.NewString()[0:8]), @@ -33,7 +35,7 @@ func NewErigonEth1(networks []string, chainConfig *config.EthereumChainConfig, o }, chainConfig: chainConfig, l: logging.GetTestLogger(nil), - ethereumVersion: config.EthereumVersion_Eth1, + ethereumVersion: config_types.EthereumVersion_Eth1, } g.SetDefaultHooks() for _, opt := range opts { diff --git a/docker/test_env/erigon_eth2.go b/docker/test_env/erigon_eth2.go index 1ff5bd1fa..c71d2f5ad 100644 --- a/docker/test_env/erigon_eth2.go +++ b/docker/test_env/erigon_eth2.go @@ -15,13 +15,15 @@ import ( tcwait "github.com/testcontainers/testcontainers-go/wait" "github.com/smartcontractkit/chainlink-testing-framework/config" + config_types "github.com/smartcontractkit/chainlink-testing-framework/config/types" + "github.com/smartcontractkit/chainlink-testing-framework/docker/ethereum" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/mirror" ) // NewErigonEth2 starts a new Erigon Eth2 node running in Docker -func NewErigonEth2(networks []string, chainConfig *config.EthereumChainConfig, generatedDataHostDir string, consensusLayer config.ConsensusLayer, opts ...EnvComponentOption) (*Erigon, error) { - parts := strings.Split(defaultErigonEth2Image, ":") +func NewErigonEth2(networks []string, chainConfig *config.EthereumChainConfig, generatedDataHostDir, generatedDataContainerDir string, consensusLayer config.ConsensusLayer, opts ...EnvComponentOption) (*Erigon, error) { + parts := strings.Split(ethereum.DefaultErigonEth2Image, ":") g := &Erigon{ EnvComponent: EnvComponent{ ContainerName: fmt.Sprintf("%s-%s", "erigon-eth2", uuid.NewString()[0:8]), @@ -31,10 +33,10 @@ func NewErigonEth2(networks []string, chainConfig *config.EthereumChainConfig, g StartupTimeout: 2 * time.Minute, }, chainConfig: chainConfig, - generatedDataHostDir: generatedDataHostDir, + posContainerSettings: posContainerSettings{generatedDataHostDir: generatedDataHostDir, generatedDataContainerDir: generatedDataContainerDir}, consensusLayer: consensusLayer, l: logging.GetTestLogger(nil), - ethereumVersion: config.EthereumVersion_Eth2, + ethereumVersion: config_types.EthereumVersion_Eth2, } g.SetDefaultHooks() for _, opt := range opts { @@ -92,7 +94,7 @@ func (g *Erigon) getEth2ContainerRequest() (*tc.ContainerRequest, error) { hostConfig.Mounts = append(hostConfig.Mounts, mount.Mount{ Type: mount.TypeBind, Source: g.generatedDataHostDir, - Target: GENERATED_DATA_DIR_INSIDE_CONTAINER, + Target: g.generatedDataContainerDir, ReadOnly: false, }) }, @@ -145,8 +147,8 @@ func (g *Erigon) buildPosInitScript() (string, error) { }{ HttpPort: DEFAULT_EVM_NODE_HTTP_PORT, ChainID: g.chainConfig.ChainID, - GeneratedDataDir: GENERATED_DATA_DIR_INSIDE_CONTAINER, - JwtFileLocation: JWT_SECRET_FILE_LOCATION_INSIDE_CONTAINER, + GeneratedDataDir: g.generatedDataContainerDir, + JwtFileLocation: getJWTSecretFileLocationInsideContainer(g.generatedDataContainerDir), ExecutionDir: "/home/erigon/execution-data", ExtraExecutionFlags: extraExecutionFlags, SendersToTrace: strings.Join(g.chainConfig.AddressesToFund, ","), diff --git a/docker/test_env/erigon_test.go b/docker/test_env/erigon_test.go index afe2ee41f..8c43b4167 100644 --- a/docker/test_env/erigon_test.go +++ b/docker/test_env/erigon_test.go @@ -4,6 +4,8 @@ import ( "fmt" "testing" + config_types "github.com/smartcontractkit/chainlink-testing-framework/config/types" + "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" @@ -19,8 +21,8 @@ func TestErigonEth1(t *testing.T) { builder := NewEthereumNetworkBuilder() cfg, err := builder. //nolint:staticcheck //ignore SA1019 - WithEthereumVersion(config.EthereumVersion_Eth1_Legacy). - WithExecutionLayer(config.ExecutionLayer_Erigon). + WithEthereumVersion(config_types.EthereumVersion_Eth1_Legacy). + WithExecutionLayer(config_types.ExecutionLayer_Erigon). Build() require.NoError(t, err, "Builder validation failed") @@ -38,15 +40,15 @@ func TestErigonEth1(t *testing.T) { require.NoError(t, err, "Couldn't close the client") } -func TestErigonEth2(t *testing.T) { +func TestErigonEth2_Deneb(t *testing.T) { l := logging.GetTestLogger(t) builder := NewEthereumNetworkBuilder() cfg, err := builder. //nolint:staticcheck //ignore SA1019 - WithEthereumVersion(config.EthereumVersion_Eth2_Legacy). + WithCustomDockerImages(map[config.ContainerType]string{config.ContainerType_ExecutionLayer: "thorax/erigon:v2.59.0"}). WithConsensusLayer(config.ConsensusLayer_Prysm). - WithExecutionLayer(config.ExecutionLayer_Erigon). + WithExecutionLayer(config_types.ExecutionLayer_Erigon). Build() require.NoError(t, err, "Builder validation failed") @@ -84,3 +86,37 @@ func TestErigonEth2(t *testing.T) { err = sendAndCompareBalances(ctx, clientTwo, address) require.NoError(t, err, fmt.Sprintf("balance wasn't correctly updated for %s network", eip1559Network.Name)) } + +func TestErigonEth2_Shanghai(t *testing.T) { + l := logging.GetTestLogger(t) + + chainConfig := config.MustGetDefaultChainConfig() + chainConfig.HardForkEpochs = map[string]int{"Deneb": 500} + + builder := NewEthereumNetworkBuilder() + cfg, err := builder. + WithCustomDockerImages(map[config.ContainerType]string{config.ContainerType_ExecutionLayer: "thorax/erigon:v2.58.0"}). + WithExecutionLayer(config_types.ExecutionLayer_Erigon). + WithEthereumChainConfig(chainConfig). + Build() + require.NoError(t, err, "Builder validation failed") + + _, eth2, err := cfg.Start() + require.NoError(t, err, "Couldn't start PoS network") + + nonEip1559Network := blockchain.SimulatedEVMNetwork + nonEip1559Network.Name = "Simulated Erigon + Prysm (non-EIP 1559)" + nonEip1559Network.URLs = eth2.PublicWsUrls() + clientOne, err := blockchain.ConnectEVMClient(nonEip1559Network, l) + require.NoError(t, err, "Couldn't connect to the evm client") + + t.Cleanup(func() { + err = clientOne.Close() + require.NoError(t, err, "Couldn't close the client") + }) + + ctx := testcontext.Get(t) + address := common.HexToAddress("0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1") + err = sendAndCompareBalances(ctx, clientOne, address) + require.NoError(t, err, fmt.Sprintf("balance wasn't correctly updated for %s network", nonEip1559Network.Name)) +} diff --git a/docker/test_env/eth2_common.go b/docker/test_env/eth2_common.go index 140f86712..71f7a3d95 100644 --- a/docker/test_env/eth2_common.go +++ b/docker/test_env/eth2_common.go @@ -13,19 +13,38 @@ import ( ) var ( - ETH2_EXECUTION_PORT = "8551" - WALLET_PASSWORD = "password" - VALIDATOR_WALLET_PASSWORD_FILE_INSIDE_CONTAINER = fmt.Sprintf("%s/wallet_password.txt", GENERATED_DATA_DIR_INSIDE_CONTAINER) - ACCOUNT_PASSWORD_FILE_INSIDE_CONTAINER = fmt.Sprintf("%s/account_password.txt", GENERATED_DATA_DIR_INSIDE_CONTAINER) - ACCOUNT_KEYSTORE_FILE_INSIDE_CONTAINER = fmt.Sprintf("%s/account_key", KEYSTORE_DIR_LOCATION_INSIDE_CONTAINER) - KEYSTORE_DIR_LOCATION_INSIDE_CONTAINER = fmt.Sprintf("%s/keystore", GENERATED_DATA_DIR_INSIDE_CONTAINER) - GENERATED_VALIDATOR_KEYS_DIR_INSIDE_CONTAINER = "/keys" - NODE_0_DIR_INSIDE_CONTAINER = fmt.Sprintf("%s/node-0", GENERATED_VALIDATOR_KEYS_DIR_INSIDE_CONTAINER) - GENERATED_DATA_DIR_INSIDE_CONTAINER = "/data/metadata" - JWT_SECRET_FILE_LOCATION_INSIDE_CONTAINER = fmt.Sprintf("%s/jwtsecret", GENERATED_DATA_DIR_INSIDE_CONTAINER) // #nosec G101 - VALIDATOR_BIP39_MNEMONIC = "giant issue aisle success illegal bike spike question tent bar rely arctic volcano long crawl hungry vocal artwork sniff fantasy very lucky have athlete" + ETH2_EXECUTION_PORT = "8551" + WALLET_PASSWORD = "password" + GENERATED_VALIDATOR_KEYS_DIR_INSIDE_CONTAINER = "/keys" + NODE_0_DIR_INSIDE_CONTAINER = fmt.Sprintf("%s/node-0", GENERATED_VALIDATOR_KEYS_DIR_INSIDE_CONTAINER) + VALIDATOR_BIP39_MNEMONIC = "giant issue aisle success illegal bike spike question tent bar rely arctic volcano long crawl hungry vocal artwork sniff fantasy very lucky have athlete" ) +type posContainerSettings struct { + generatedDataHostDir string + generatedDataContainerDir string +} + +func getValidatorWalletPasswordFileInsideContainer(generatedDataContainerDir string) string { + return fmt.Sprintf("%s/wallet_password.txt", generatedDataContainerDir) +} + +func getAccountPasswordFileInsideContainer(generatedDataContainerDir string) string { + return fmt.Sprintf("%s/account_password.txt", generatedDataContainerDir) +} + +func getAccountKeystoreFileInsideContainer(generatedDataContainerDir string) string { + return fmt.Sprintf("%s/account_key", getKeystoreDirLocationInsideContainer(generatedDataContainerDir)) +} + +func getKeystoreDirLocationInsideContainer(generatedDataContainerDir string) string { + return fmt.Sprintf("%s/keystore", generatedDataContainerDir) +} + +func getJWTSecretFileLocationInsideContainer(generatedDataContainerDir string) string { + return fmt.Sprintf("%s/jwtsecret", generatedDataContainerDir) +} + func waitForChainToFinaliseAnEpoch(lggr zerolog.Logger, evmClient blockchain.EVMClient, timeout time.Duration) error { lggr.Info().Msg("Waiting for chain to finalize an epoch") diff --git a/docker/test_env/eth2_init_helpers.go b/docker/test_env/eth2_init_helpers.go index 9ec9f0f62..87d889b0c 100644 --- a/docker/test_env/eth2_init_helpers.go +++ b/docker/test_env/eth2_init_helpers.go @@ -22,23 +22,23 @@ import ( type AfterGenesisHelper struct { EnvComponent - chainConfig config.EthereumChainConfig - l zerolog.Logger - customConfigDataDir string - addressesToFund []string - t *testing.T + chainConfig config.EthereumChainConfig + addressesToFund []string + l zerolog.Logger + t *testing.T + posContainerSettings } -func NewInitHelper(chainConfig config.EthereumChainConfig, customConfigDataDir string, opts ...EnvComponentOption) *AfterGenesisHelper { +func NewInitHelper(chainConfig config.EthereumChainConfig, generatedDataHostDir, generatedDataContainerDir string, opts ...EnvComponentOption) *AfterGenesisHelper { g := &AfterGenesisHelper{ EnvComponent: EnvComponent{ ContainerName: fmt.Sprintf("%s-%s", "after-genesis-helper", uuid.NewString()[0:8]), StartupTimeout: 20 * time.Second, }, - chainConfig: chainConfig, - customConfigDataDir: customConfigDataDir, - l: log.Logger, - addressesToFund: []string{}, + chainConfig: chainConfig, + posContainerSettings: posContainerSettings{generatedDataHostDir: generatedDataHostDir, generatedDataContainerDir: generatedDataContainerDir}, + l: log.Logger, + addressesToFund: []string{}, } g.SetDefaultHooks() for _, opt := range opts { @@ -110,8 +110,8 @@ func (g *AfterGenesisHelper) getContainerRequest(networks []string) (*tc.Contain HostConfigModifier: func(hostConfig *container.HostConfig) { hostConfig.Mounts = append(hostConfig.Mounts, mount.Mount{ Type: mount.TypeBind, - Source: g.customConfigDataDir, - Target: GENERATED_DATA_DIR_INSIDE_CONTAINER, + Source: g.generatedDataHostDir, + Target: g.generatedDataContainerDir, ReadOnly: false, }) }, @@ -157,11 +157,11 @@ echo "------------------------------------------------------------------" GenesisTimestamp int }{ WalletPassword: WALLET_PASSWORD, - WalletPasswordFileLocation: VALIDATOR_WALLET_PASSWORD_FILE_INSIDE_CONTAINER, - AccountPasswordFileLocation: ACCOUNT_PASSWORD_FILE_INSIDE_CONTAINER, - JwtFileLocation: JWT_SECRET_FILE_LOCATION_INSIDE_CONTAINER, - AccountKeystoreFileLocation: ACCOUNT_KEYSTORE_FILE_INSIDE_CONTAINER, - KeystoreDirLocation: KEYSTORE_DIR_LOCATION_INSIDE_CONTAINER, + WalletPasswordFileLocation: getValidatorWalletPasswordFileInsideContainer(g.generatedDataContainerDir), + AccountPasswordFileLocation: getAccountPasswordFileInsideContainer(g.generatedDataContainerDir), + JwtFileLocation: getJWTSecretFileLocationInsideContainer(g.generatedDataContainerDir), + AccountKeystoreFileLocation: getAccountKeystoreFileInsideContainer(g.generatedDataContainerDir), + KeystoreDirLocation: getKeystoreDirLocationInsideContainer(g.generatedDataContainerDir), GenesisTimestamp: g.chainConfig.GenesisTimestamp, } diff --git a/docker/test_env/eth_common.go b/docker/test_env/eth_common.go index c71b098a5..e09e9d9c5 100644 --- a/docker/test_env/eth_common.go +++ b/docker/test_env/eth_common.go @@ -7,10 +7,12 @@ import ( "testing" "time" + config_types "github.com/smartcontractkit/chainlink-testing-framework/config/types" + tc "github.com/testcontainers/testcontainers-go" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" - "github.com/smartcontractkit/chainlink-testing-framework/config" + "github.com/smartcontractkit/chainlink-testing-framework/docker/ethereum" ) const ( @@ -39,7 +41,7 @@ type ExecutionClient interface { GetInternalWsUrl() string GetExternalHttpUrl() string GetExternalWsUrl() string - GetEthereumVersion() config.EthereumVersion + GetEthereumVersion() config_types.EthereumVersion GethConsensusMechanism() ConsensusMechanism WaitUntilChainIsReady(ctx context.Context, waitTime time.Duration) error WithTestInstance(t *testing.T) ExecutionClient @@ -51,10 +53,10 @@ type UnsupportedVersion struct { } var UNSUPPORTED_VERSIONS = []UnsupportedVersion{ - {DockerImage: fmt.Sprintf("%s:1.20.0", nethermindBaseImageName), + {DockerImage: fmt.Sprintf("%s:1.20.0", ethereum.NethermindBaseImageName), Reason: "1.20.0 was replaced with 1.20.1, for more info check https://github.com/NethermindEth/nethermind/releases/tag/1.20.0", }, - {DockerImage: fmt.Sprintf("%s:v1.9.0", gethBaseImageName), + {DockerImage: fmt.Sprintf("%s:v1.9.0", ethereum.GethBaseImageName), Reason: "v1.9.0 randomly drops websocket connections, for more info check https://github.com/ethereum/go-ethereum/issues/19001", }, } diff --git a/docker/test_env/ethereum_env.go b/docker/test_env/ethereum_env.go index 13e775455..45acc3096 100644 --- a/docker/test_env/ethereum_env.go +++ b/docker/test_env/ethereum_env.go @@ -13,7 +13,9 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/blockchain" "github.com/smartcontractkit/chainlink-testing-framework/config" + config_types "github.com/smartcontractkit/chainlink-testing-framework/config/types" "github.com/smartcontractkit/chainlink-testing-framework/docker" + "github.com/smartcontractkit/chainlink-testing-framework/docker/ethereum" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/logstream" "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" @@ -35,9 +37,9 @@ type EthereumNetworkBuilder struct { t *testing.T ls *logstream.LogStream dockerNetworks []string - ethereumVersion config.EthereumVersion + ethereumVersion config_types.EthereumVersion consensusLayer *config.ConsensusLayer - executionLayer config.ExecutionLayer + executionLayer config_types.ExecutionLayer ethereumChainConfig *config.EthereumChainConfig existingConfig *config.EthereumNetworkConfig customDockerImages map[config.ContainerType]string @@ -61,16 +63,16 @@ func NewEthereumNetworkBuilder() EthereumNetworkBuilder { func (b *EthereumNetworkBuilder) WithConsensusType(consensusType config.ConsensusType) *EthereumNetworkBuilder { switch consensusType { case config.ConsensusType_PoS: - b.ethereumVersion = config.EthereumVersion_Eth2 + b.ethereumVersion = config_types.EthereumVersion_Eth2 case config.ConsensusType_PoW: - b.ethereumVersion = config.EthereumVersion_Eth1 + b.ethereumVersion = config_types.EthereumVersion_Eth1 default: panic(fmt.Sprintf("unknown consensus type: %s", consensusType)) } return b } -func (b *EthereumNetworkBuilder) WithEthereumVersion(ethereumVersion config.EthereumVersion) *EthereumNetworkBuilder { +func (b *EthereumNetworkBuilder) WithEthereumVersion(ethereumVersion config_types.EthereumVersion) *EthereumNetworkBuilder { b.ethereumVersion = ethereumVersion return b } @@ -80,7 +82,7 @@ func (b *EthereumNetworkBuilder) WithConsensusLayer(consensusLayer config.Consen return b } -func (b *EthereumNetworkBuilder) WithExecutionLayer(executionLayer config.ExecutionLayer) *EthereumNetworkBuilder { +func (b *EthereumNetworkBuilder) WithExecutionLayer(executionLayer config_types.ExecutionLayer) *EthereumNetworkBuilder { b.executionLayer = executionLayer return b } @@ -176,7 +178,7 @@ func (b *EthereumNetworkBuilder) Build() (EthereumNetwork, error) { if !b.importExistingConfig() { if b.ethereumChainConfig == nil { - defaultConfig := config.GetDefaultChainConfig() + defaultConfig := config.MustGetDefaultChainConfig() b.ethereumChainConfig = &defaultConfig } else { b.ethereumChainConfig.FillInMissingValuesWithDefault() @@ -246,7 +248,7 @@ func (b *EthereumNetworkBuilder) validate() error { } //nolint:staticcheck //ignore SA1019 - if (b.ethereumVersion == config.EthereumVersion_Eth2 || b.ethereumVersion == config.EthereumVersion_Eth2_Legacy) && b.consensusLayer == nil { + if (b.ethereumVersion == config_types.EthereumVersion_Eth2 || b.ethereumVersion == config_types.EthereumVersion_Eth2_Legacy) && b.consensusLayer == nil { return ErrMissingConsensusLayer } @@ -265,7 +267,7 @@ func (b *EthereumNetworkBuilder) validate() error { return errors.New("ethereum chain config is required") } - return b.ethereumChainConfig.Validate(logging.GetTestLogger(nil), &b.ethereumVersion) + return b.ethereumChainConfig.Validate(logging.GetTestLogger(nil), &b.ethereumVersion, &b.executionLayer, b.customDockerImages) } func (b *EthereumNetworkBuilder) validateCustomDockerImages() error { @@ -281,7 +283,7 @@ func (b *EthereumNetworkBuilder) validateCustomDockerImages() error { return fmt.Errorf("docker image %s is not supported, due to: %s", image, reason) } - executionLayer, err := GetExecutionLayerFromDockerImage(image) + executionLayer, err := ethereum.ExecutionLayerFromDockerImage(image) if err != nil { return err } @@ -313,18 +315,18 @@ func (b *EthereumNetworkBuilder) autoFill() error { } //nolint:staticcheck //ignore SA1019 - if (b.ethereumVersion == config.EthereumVersion_Eth2_Legacy || b.ethereumVersion == config.EthereumVersion_Eth2) && b.consensusLayer == nil { + if (b.ethereumVersion == config_types.EthereumVersion_Eth2_Legacy || b.ethereumVersion == config_types.EthereumVersion_Eth2) && b.consensusLayer == nil { b.consensusLayer = &config.ConsensusLayer_Prysm } //nolint:staticcheck //ignore SA1019 - if b.ethereumVersion == config.EthereumVersion_Eth1_Legacy { - b.ethereumVersion = config.EthereumVersion_Eth1 + if b.ethereumVersion == config_types.EthereumVersion_Eth1_Legacy { + b.ethereumVersion = config_types.EthereumVersion_Eth1 } //nolint:staticcheck //ignore SA1019 - if b.ethereumVersion == config.EthereumVersion_Eth2_Legacy { - b.ethereumVersion = config.EthereumVersion_Eth2 + if b.ethereumVersion == config_types.EthereumVersion_Eth2_Legacy { + b.ethereumVersion = config_types.EthereumVersion_Eth2 } if b.nodeLogLevel == "" { @@ -338,7 +340,7 @@ func (b *EthereumNetworkBuilder) setExecutionLayerBasedOnCustomDocker() error { if b.executionLayer == "" && len(b.customDockerImages) > 0 { if image, ok := b.customDockerImages[config.ContainerType_ExecutionLayer]; ok { var err error - b.executionLayer, err = GetExecutionLayerFromDockerImage(image) + b.executionLayer, err = ethereum.ExecutionLayerFromDockerImage(image) if err != nil { return err } @@ -382,7 +384,7 @@ func (b *EthereumNetworkBuilder) trySettingEthereumVersionBasedOnCustomImage() e return errors.New("couldn't determine ethereum version as no custom docker image for execution layer was provided") } - ethereumVersion, err := GetEthereumVersionFromImage(b.executionLayer, dockerImageToUse) + ethereumVersion, err := ethereum.VersionFromImage(dockerImageToUse) if err != nil { return err } @@ -402,10 +404,10 @@ type EthereumNetwork struct { func (en *EthereumNetwork) Start() (blockchain.EVMNetwork, RpcProvider, error) { switch *en.EthereumVersion { //nolint:staticcheck //ignore SA1019 - case config.EthereumVersion_Eth1, config.EthereumVersion_Eth1_Legacy: + case config_types.EthereumVersion_Eth1, config_types.EthereumVersion_Eth1_Legacy: return en.startEth1() //nolint:staticcheck //ignore SA1019 - case config.EthereumVersion_Eth2_Legacy, config.EthereumVersion_Eth2: + case config_types.EthereumVersion_Eth2_Legacy, config_types.EthereumVersion_Eth2: return en.startEth2() default: return blockchain.EVMNetwork{}, RpcProvider{}, fmt.Errorf("unknown ethereum version: %s", *en.EthereumVersion) @@ -425,32 +427,56 @@ func (en *EthereumNetwork) startEth2() (blockchain.EVMNetwork, RpcProvider, erro if err != nil { return blockchain.EVMNetwork{}, RpcProvider{}, errors.Wrapf(err, "failed to create docker networks") } - generatedDataHostDir, valKeysDir, err := en.generateGenesisAndFoldersIfNeeded() + + executionLayerImage := en.getImageOverride(config.ContainerType_ExecutionLayer) + if executionLayerImage == "" { + switch *en.ExecutionLayer { + case config_types.ExecutionLayer_Besu: + executionLayerImage = ethereum.DefaultBesuEth2Image + case config_types.ExecutionLayer_Geth: + executionLayerImage = ethereum.DefaultGethEth2Image + case config_types.ExecutionLayer_Nethermind: + executionLayerImage = ethereum.DefaultNethermindEth2Image + case config_types.ExecutionLayer_Erigon: + executionLayerImage = ethereum.DefaultErigonEth2Image + case config_types.ExecutionLayer_Reth: + executionLayerImage = ethereum.DefaultRethEth2Image + default: + return blockchain.EVMNetwork{}, RpcProvider{}, fmt.Errorf(config_types.MsgUnsupportedExecutionLayer, *en.ExecutionLayer) + } + } + + baseEthereumFork, err := ethereum.LastSupportedForkForEthereumClient(executionLayerImage) + if err != nil { + return blockchain.EVMNetwork{}, RpcProvider{}, errors.Wrapf(err, "failed to get last supported fork for Ethereum client") + } + + generatedDataHostDir, generatedDataContainerDir, valKeysDir, err := en.generateGenesisAndFoldersIfNeeded(baseEthereumFork) if err != nil { return blockchain.EVMNetwork{}, RpcProvider{}, errors.Wrapf(err, "failed to prepare genesis") } opts := en.getExecutionLayerEnvComponentOpts() - chainReadyWaitTime := en.EthereumChainConfig.GetDefaultWaitDuration() + chainReadyWaitTime := en.EthereumChainConfig.DefaultWaitDuration() var client ExecutionClient var clientErr error switch *en.ExecutionLayer { - case config.ExecutionLayer_Geth: - client, clientErr = NewGethEth2(dockerNetworks, en.EthereumChainConfig, generatedDataHostDir, config.ConsensusLayer_Prysm, opts...) - case config.ExecutionLayer_Nethermind: - client, clientErr = NewNethermindEth2(dockerNetworks, en.EthereumChainConfig, generatedDataHostDir, config.ConsensusLayer_Prysm, opts...) + case config_types.ExecutionLayer_Geth: + client, clientErr = NewGethEth2(dockerNetworks, en.EthereumChainConfig, generatedDataHostDir, generatedDataContainerDir, config.ConsensusLayer_Prysm, opts...) + case config_types.ExecutionLayer_Nethermind: + client, clientErr = NewNethermindEth2(dockerNetworks, en.EthereumChainConfig, generatedDataHostDir, generatedDataContainerDir, config.ConsensusLayer_Prysm, opts...) chainReadyWaitTime = chainReadyWaitTime * 2 - case config.ExecutionLayer_Erigon: - client, clientErr = NewErigonEth2(dockerNetworks, en.EthereumChainConfig, generatedDataHostDir, config.ConsensusLayer_Prysm, opts...) + case config_types.ExecutionLayer_Erigon: + client, clientErr = NewErigonEth2(dockerNetworks, en.EthereumChainConfig, generatedDataHostDir, generatedDataContainerDir, config.ConsensusLayer_Prysm, opts...) chainReadyWaitTime = chainReadyWaitTime * 2 - case config.ExecutionLayer_Besu: - client, clientErr = NewBesuEth2(dockerNetworks, en.EthereumChainConfig, generatedDataHostDir, config.ConsensusLayer_Prysm, opts...) + case config_types.ExecutionLayer_Besu: + client, clientErr = NewBesuEth2(dockerNetworks, en.EthereumChainConfig, generatedDataHostDir, generatedDataContainerDir, config.ConsensusLayer_Prysm, opts...) chainReadyWaitTime = chainReadyWaitTime * 2 - case config.ExecutionLayer_Reth: - client, clientErr = NewRethEth2(dockerNetworks, en.EthereumChainConfig, generatedDataHostDir, config.ConsensusLayer_Prysm, opts...) + case config_types.ExecutionLayer_Reth: + client, clientErr = NewRethEth2(dockerNetworks, en.EthereumChainConfig, generatedDataHostDir, generatedDataContainerDir, config.ConsensusLayer_Prysm, opts...) default: - return blockchain.EVMNetwork{}, RpcProvider{}, fmt.Errorf(MsgUnsupportedExecutionLayer, *en.ExecutionLayer) + return blockchain.EVMNetwork{}, RpcProvider{}, fmt.Errorf(config_types.MsgUnsupportedExecutionLayer, *en.ExecutionLayer) } if clientErr != nil { @@ -464,7 +490,7 @@ func (en *EthereumNetwork) startEth2() (blockchain.EVMNetwork, RpcProvider, erro return blockchain.EVMNetwork{}, RpcProvider{}, errors.Wrapf(err, "failed to start %s execution client", *en.ExecutionLayer) } - beacon, err := NewPrysmBeaconChain(dockerNetworks, en.EthereumChainConfig, generatedDataHostDir, client.GetInternalExecutionURL(), append(en.getImageOverride(config.ContainerType_ValKeysGenerator), en.setExistingContainerName(config.ContainerType_ConsensusLayer))...) + beacon, err := NewPrysmBeaconChain(dockerNetworks, en.EthereumChainConfig, generatedDataHostDir, generatedDataContainerDir, client.GetInternalExecutionURL(), baseEthereumFork, append(en.getImageOverrideOpts(config.ContainerType_ValKeysGenerator), en.setExistingContainerName(config.ContainerType_ConsensusLayer))...) if err != nil { return blockchain.EVMNetwork{}, RpcProvider{}, errors.Wrapf(err, "failed to create beacon chain instance") } @@ -475,8 +501,8 @@ func (en *EthereumNetwork) startEth2() (blockchain.EVMNetwork, RpcProvider, erro return blockchain.EVMNetwork{}, RpcProvider{}, errors.Wrapf(err, "failed to start beacon chain") } - validator, err := NewPrysmValidator(dockerNetworks, en.EthereumChainConfig, generatedDataHostDir, valKeysDir, beacon. - InternalBeaconRpcProvider, append(en.getImageOverride(config.ContainerType_ValKeysGenerator), en.setExistingContainerName(config.ContainerType_ConsensusValidator))...) + validator, err := NewPrysmValidator(dockerNetworks, en.EthereumChainConfig, generatedDataHostDir, generatedDataContainerDir, valKeysDir, beacon. + InternalBeaconRpcProvider, baseEthereumFork, append(en.getImageOverrideOpts(config.ContainerType_ValKeysGenerator), en.setExistingContainerName(config.ContainerType_ConsensusValidator))...) if err != nil { return blockchain.EVMNetwork{}, RpcProvider{}, errors.Wrapf(err, "failed to create validator instance") } @@ -502,7 +528,7 @@ func (en *EthereumNetwork) startEth2() (blockchain.EVMNetwork, RpcProvider, erro return blockchain.EVMNetwork{}, RpcProvider{}, errors.Wrapf(err, "failed to create evm client") } - err = waitForChainToFinaliseAnEpoch(logger, evmClient, en.EthereumChainConfig.GetDefaultFinalizationWaitDuration()) + err = waitForChainToFinaliseAnEpoch(logger, evmClient, en.EthereumChainConfig.DefaultFinalizationWaitDuration()) if err != nil { return blockchain.EVMNetwork{}, RpcProvider{}, errors.Wrapf(err, "failed to wait for chain to finalize first epoch") } @@ -557,18 +583,18 @@ func (en *EthereumNetwork) startEth1() (blockchain.EVMNetwork, RpcProvider, erro var client ExecutionClient var clientErr error switch *en.ExecutionLayer { - case config.ExecutionLayer_Geth: + case config_types.ExecutionLayer_Geth: client = NewGethEth1(dockerNetworks, en.EthereumChainConfig, opts...) - case config.ExecutionLayer_Besu: + case config_types.ExecutionLayer_Besu: client, clientErr = NewBesuEth1(dockerNetworks, en.EthereumChainConfig, opts...) - case config.ExecutionLayer_Erigon: + case config_types.ExecutionLayer_Erigon: client, clientErr = NewErigonEth1(dockerNetworks, en.EthereumChainConfig, opts...) - case config.ExecutionLayer_Nethermind: + case config_types.ExecutionLayer_Nethermind: client, clientErr = NewNethermindEth1(dockerNetworks, en.EthereumChainConfig, opts...) - case config.ExecutionLayer_Reth: + case config_types.ExecutionLayer_Reth: clientErr = errors.New(config.Eth1NotSupportedByRethMsg) default: - return blockchain.EVMNetwork{}, RpcProvider{}, fmt.Errorf(MsgUnsupportedExecutionLayer, *en.ExecutionLayer) + return blockchain.EVMNetwork{}, RpcProvider{}, fmt.Errorf(config_types.MsgUnsupportedExecutionLayer, *en.ExecutionLayer) } if clientErr != nil { @@ -615,7 +641,7 @@ func (en *EthereumNetwork) getOrCreateDockerNetworks() ([]string, error) { return []string{network.Name}, nil } -func (en *EthereumNetwork) generateGenesisAndFoldersIfNeeded() (generatedDataHostDir string, valKeysDir string, err error) { +func (en *EthereumNetwork) generateGenesisAndFoldersIfNeeded(baseEthereumFork ethereum.Fork) (generatedDataHostDir, generatedDataContainerDir, valKeysDir string, err error) { // create host directories and run genesis containers only if we are NOT recreating existing containers if !en.isRecreated { generatedDataHostDir, valKeysDir, err = createHostDirectories() @@ -628,7 +654,7 @@ func (en *EthereumNetwork) generateGenesisAndFoldersIfNeeded() (generatedDataHos } var valKeysGenerator *ValKeysGenerator - valKeysGenerator, err = NewValKeysGeneretor(en.EthereumChainConfig, valKeysDir, en.getImageOverride(config.ContainerType_ValKeysGenerator)...) + valKeysGenerator, err = NewValKeysGeneretor(en.EthereumChainConfig, valKeysDir, en.getImageOverrideOpts(config.ContainerType_ValKeysGenerator)...) if err != nil { err = errors.Wrap(err, "failed to start val keys generator") return @@ -641,8 +667,8 @@ func (en *EthereumNetwork) generateGenesisAndFoldersIfNeeded() (generatedDataHos return } - var genesis *EthGenesisGeneretor - genesis, err = NewEthGenesisGenerator(*en.EthereumChainConfig, generatedDataHostDir, en.getImageOverride(config.ContainerType_GenesisGenerator)...) + var genesis *EthGenesisGenerator + genesis, err = NewEthGenesisGenerator(*en.EthereumChainConfig, generatedDataHostDir, baseEthereumFork, en.getImageOverrideOpts(config.ContainerType_GenesisGenerator)...) if err != nil { err = errors.Wrap(err, "failed to start genesis generator") return @@ -655,7 +681,9 @@ func (en *EthereumNetwork) generateGenesisAndFoldersIfNeeded() (generatedDataHos return } - initHelper := NewInitHelper(*en.EthereumChainConfig, generatedDataHostDir).WithTestInstance(en.t) + generatedDataContainerDir = genesis.GetGeneratedDataContainerDir() + + initHelper := NewInitHelper(*en.EthereumChainConfig, generatedDataHostDir, generatedDataContainerDir).WithTestInstance(en.t) err = initHelper.StartContainer() if err != nil { err = errors.Wrap(err, "failed to start init helper") @@ -685,7 +713,7 @@ func (en *EthereumNetwork) getFinalEvmNetworkConfig(net blockchain.EVMNetwork) b net.FinalityTag = true net.FinalityDepth = 0 - if *en.ExecutionLayer == config.ExecutionLayer_Besu { + if *en.ExecutionLayer == config_types.ExecutionLayer_Besu { // Besu doesn't support "eth_maxPriorityFeePerGas" https://github.com/hyperledger/besu/issues/5658 // 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 net.GasEstimationBuffer = 10_000_000_000 @@ -715,14 +743,21 @@ func (en *EthereumNetwork) setExistingContainerName(ct config.ContainerType) Env return func(c *EnvComponent) {} } -func (en *EthereumNetwork) getImageOverride(ct config.ContainerType) []EnvComponentOption { - options := []EnvComponentOption{} - if image, ok := en.CustomDockerImages[ct]; ok { +func (en *EthereumNetwork) getImageOverrideOpts(ct config.ContainerType) []EnvComponentOption { + var options []EnvComponentOption + if image := en.getImageOverride(ct); image != "" { options = append(options, WithContainerImageWithVersion(image)) } return options } +func (en *EthereumNetwork) getImageOverride(ct config.ContainerType) string { + if image, ok := en.CustomDockerImages[ct]; ok { + return image + } + return "" +} + func (en *EthereumNetwork) Save() error { name := fmt.Sprintf("ethereum_network_%s", uuid.NewString()[0:8]) confPath, err := toml_utils.SaveStructAsToml(en, ".private_chains", name) @@ -738,7 +773,7 @@ func (en *EthereumNetwork) Save() error { func (en *EthereumNetwork) getExecutionLayerEnvComponentOpts() []EnvComponentOption { opts := []EnvComponentOption{} - opts = append(opts, en.getImageOverride(config.ContainerType_ExecutionLayer)...) + opts = append(opts, en.getImageOverrideOpts(config.ContainerType_ExecutionLayer)...) opts = append(opts, en.setExistingContainerName(config.ContainerType_ExecutionLayer)) opts = append(opts, WithLogStream(en.ls)) diff --git a/docker/test_env/ethereum_env_test.go b/docker/test_env/ethereum_env_test.go index 9d496f269..f3aebbce8 100644 --- a/docker/test_env/ethereum_env_test.go +++ b/docker/test_env/ethereum_env_test.go @@ -6,6 +6,8 @@ import ( "os" "testing" + config_types "github.com/smartcontractkit/chainlink-testing-framework/config/types" + "github.com/ethereum/go-ethereum/common" "github.com/pelletier/go-toml/v2" @@ -21,9 +23,9 @@ func TestEthEnvCustomConfig(t *testing.T) { builder := NewEthereumNetworkBuilder() cfg, err := builder. - WithEthereumVersion(config.EthereumVersion_Eth2). + WithEthereumVersion(config_types.EthereumVersion_Eth2). WithConsensusLayer(config.ConsensusLayer_Prysm). - WithExecutionLayer(config.ExecutionLayer_Geth). + WithExecutionLayer(config_types.ExecutionLayer_Geth). WithEthereumChainConfig(config.EthereumChainConfig{ SecondsPerSlot: 6, SlotsPerEpoch: 2, @@ -48,9 +50,9 @@ func TestEthEnvExtraFunding(t *testing.T) { builder := NewEthereumNetworkBuilder() cfg, err := builder. //nolint:staticcheck //ignore SA1019 - WithEthereumVersion(config.EthereumVersion_Eth2_Legacy). + WithEthereumVersion(config_types.EthereumVersion_Eth2_Legacy). WithConsensusLayer(config.ConsensusLayer_Prysm). - WithExecutionLayer(config.ExecutionLayer_Geth). + WithExecutionLayer(config_types.ExecutionLayer_Geth). WithEthereumChainConfig(config.EthereumChainConfig{ AddressesToFund: []string{addressToFund}, }). @@ -76,9 +78,9 @@ func TestEthEnvWithPrysmAndGethReuseConfig(t *testing.T) { builder := NewEthereumNetworkBuilder() cfg, err := builder. - WithEthereumVersion(config.EthereumVersion_Eth2). + WithEthereumVersion(config_types.EthereumVersion_Eth2). WithConsensusLayer(config.ConsensusLayer_Prysm). - WithExecutionLayer(config.ExecutionLayer_Geth). + WithExecutionLayer(config_types.ExecutionLayer_Geth). Build() require.NoError(t, err, "Builder validation failed") @@ -151,10 +153,10 @@ func TestEthEnvExecClientFromToml(t *testing.T) { WithExistingConfig(tomlCfg). Build() require.NoError(t, err, "Builder validation failed") - require.Equal(t, config.ExecutionLayer_Besu, *cfg.ExecutionLayer, "Execution layer should be Besu") + require.Equal(t, config_types.ExecutionLayer_Besu, *cfg.ExecutionLayer, "Execution layer should be Besu") require.NotNil(t, cfg.ConsensusLayer, "Consensus layer should not be nil") require.Equal(t, config.ConsensusLayer_Prysm, *cfg.ConsensusLayer, "Consensus layer should be Prysm") - require.Equal(t, config.EthereumVersion_Eth2, *cfg.EthereumVersion, "Ethereum Version should be Eth2") + require.Equal(t, config_types.EthereumVersion_Eth2, *cfg.EthereumVersion, "Ethereum Version should be Eth2") require.NotNil(t, cfg.WaitForFinalization, "Wait for finalization should not be nil") require.False(t, *cfg.WaitForFinalization, "Wait for finalization should be false") require.Equal(t, 2, len(cfg.EthereumChainConfig.AddressesToFund), "Should have 2 addresses to fund") @@ -192,14 +194,9 @@ func TestEthEnvCustomDockerImagesFromToml(t *testing.T) { tomlCfg.EthereumChainConfig.GenerateGenesisTimestamp() err = tomlCfg.Validate() - require.NoError(t, err, "Couldn't validate TOML config") - - builder := NewEthereumNetworkBuilder() - _, err = builder. - WithExistingConfig(tomlCfg). - Build() - require.Error(t, err, "Builder validation failed") - require.Contains(t, fmt.Sprintf(MsgUnsupportedDockerImage, "i-dont-exist"), err.Error(), "Error message is not correct") + require.NotNil(t, err, "Should have failed to validate TOML config") + require.Error(t, err, "Couldn't validate TOML config") + require.Equal(t, fmt.Sprintf("failed to parse docker image and extract version: %s", "i-dont-exist:tag-me"), err.Error(), "Error message is not correct") } func TestEthNoLogLevelDefaultsToInfo(t *testing.T) { @@ -307,23 +304,36 @@ func TestEthEnvCustomDockerNetworks(t *testing.T) { builder := NewEthereumNetworkBuilder() cfg, err := builder. - WithEthereumVersion(config.EthereumVersion_Eth2). + WithEthereumVersion(config_types.EthereumVersion_Eth2). WithConsensusLayer(config.ConsensusLayer_Prysm). - WithExecutionLayer(config.ExecutionLayer_Geth). + WithExecutionLayer(config_types.ExecutionLayer_Geth). WithDockerNetworks(networks). Build() require.NoError(t, err, "Builder validation failed") require.Equal(t, networks, cfg.DockerNetworkNames, "Incorrect docker networks in config") } +func TestEthEnvValidHardForks(t *testing.T) { + t.Parallel() + chainConfig := config.MustGetDefaultChainConfig() + chainConfig.HardForkEpochs = map[string]int{"Deneb": 500} + + builder := NewEthereumNetworkBuilder() + _, err := builder. + WithCustomDockerImages(map[config.ContainerType]string{config.ContainerType_ExecutionLayer: "ethereum/client-go:v1.13.11"}). + WithExecutionLayer(config_types.ExecutionLayer_Geth). + WithEthereumChainConfig(chainConfig). + Build() + require.NoError(t, err, "Builder validation failed") +} + func TestEthEnvInvalidHardForks(t *testing.T) { t.Parallel() builder := NewEthereumNetworkBuilder() _, err := builder. - //nolint:staticcheck //ignore SA1019 - WithConsensusType(config.ConsensusType_PoS). + WithEthereumVersion(config_types.EthereumVersion_Eth2). WithConsensusLayer(config.ConsensusLayer_Prysm). - WithExecutionLayer(config.ExecutionLayer_Geth). + WithExecutionLayer(config_types.ExecutionLayer_Geth). WithEthereumChainConfig(config.EthereumChainConfig{ HardForkEpochs: map[string]int{"Electra": 0}, }). @@ -335,24 +345,24 @@ func TestEthEnvAutoEthereumVersionEth1Minor(t *testing.T) { t.Parallel() builder := NewEthereumNetworkBuilder() cfg, err := builder. - WithExecutionLayer(config.ExecutionLayer_Besu). + WithExecutionLayer(config_types.ExecutionLayer_Besu). WithCustomDockerImages(map[config.ContainerType]string{ config.ContainerType_ExecutionLayer: "hyperledger/besu:20.1"}). Build() require.NoError(t, err, "Builder validation failed") - require.Equal(t, config.EthereumVersion_Eth1, *cfg.EthereumVersion, "Ethereum Version should be Eth1") + require.Equal(t, config_types.EthereumVersion_Eth1, *cfg.EthereumVersion, "Ethereum Version should be Eth1") require.Nil(t, cfg.ConsensusLayer, "Consensus layer should be nil") } func TestEthEnvAutoEthereumVersionEth2Minor(t *testing.T) { builder := NewEthereumNetworkBuilder() cfg, err := builder. - WithExecutionLayer(config.ExecutionLayer_Nethermind). + WithExecutionLayer(config_types.ExecutionLayer_Nethermind). WithCustomDockerImages(map[config.ContainerType]string{ config.ContainerType_ExecutionLayer: "nethermind/nethermind:1.17.2"}). Build() require.NoError(t, err, "Builder validation failed") - require.Equal(t, config.EthereumVersion_Eth2, *cfg.EthereumVersion, "Ethereum Version should be Eth2") + require.Equal(t, config_types.EthereumVersion_Eth2, *cfg.EthereumVersion, "Ethereum Version should be Eth2") require.Equal(t, config.ConsensusLayer_Prysm, *cfg.ConsensusLayer, "Consensus layer should be Prysm") } @@ -360,12 +370,12 @@ func TestEthEnvAutoEthereumVersionReleaseCandidate(t *testing.T) { t.Parallel() builder := NewEthereumNetworkBuilder() cfg, err := builder. - WithExecutionLayer(config.ExecutionLayer_Nethermind). + WithExecutionLayer(config_types.ExecutionLayer_Nethermind). WithCustomDockerImages(map[config.ContainerType]string{ config.ContainerType_ExecutionLayer: "nethermind/nethermind:1.17.0-RC2"}). Build() require.NoError(t, err, "Builder validation failed") - require.Equal(t, config.EthereumVersion_Eth2, *cfg.EthereumVersion, "Ethereum Version should be Eth2") + require.Equal(t, config_types.EthereumVersion_Eth2, *cfg.EthereumVersion, "Ethereum Version should be Eth2") require.Equal(t, config.ConsensusLayer_Prysm, *cfg.ConsensusLayer, "Consensus layer should be Prysm") } @@ -374,13 +384,13 @@ func TestEthEnvAutoEthereumVersionWithLettersInVersion(t *testing.T) { builder := NewEthereumNetworkBuilder() cfg, err := builder. WithConsensusLayer(config.ConsensusLayer_Prysm). - WithExecutionLayer(config.ExecutionLayer_Geth). + WithExecutionLayer(config_types.ExecutionLayer_Geth). WithCustomDockerImages(map[config.ContainerType]string{ config.ContainerType_ExecutionLayer: "ethereum/client-go:v1.13.10"}). Build() require.NoError(t, err, "Builder validation failed") - require.Equal(t, config.EthereumVersion_Eth2, *cfg.EthereumVersion, "Ethereum Version should be Eth2") - require.Equal(t, config.ExecutionLayer_Geth, *cfg.ExecutionLayer, "Execution layer should be Geth") + require.Equal(t, config_types.EthereumVersion_Eth2, *cfg.EthereumVersion, "Ethereum Version should be Eth2") + require.Equal(t, config_types.ExecutionLayer_Geth, *cfg.ExecutionLayer, "Execution layer should be Geth") require.Equal(t, config.ConsensusLayer_Prysm, *cfg.ConsensusLayer, "Consensus layer should be Prysm") } @@ -392,8 +402,8 @@ func TestEthEnvAutoEthereumVersionOnlyMajor(t *testing.T) { config.ContainerType_ExecutionLayer: "hyperledger/besu:v24.1"}). Build() require.NoError(t, err, "Builder validation failed") - require.Equal(t, config.EthereumVersion_Eth2, *cfg.EthereumVersion, "Ethereum Version should be Eth2") - require.Equal(t, config.ExecutionLayer_Besu, *cfg.ExecutionLayer, "Execution layer should be Besu") + require.Equal(t, config_types.EthereumVersion_Eth2, *cfg.EthereumVersion, "Ethereum Version should be Eth2") + require.Equal(t, config_types.ExecutionLayer_Besu, *cfg.ExecutionLayer, "Execution layer should be Besu") require.Equal(t, config.ConsensusLayer_Prysm, *cfg.ConsensusLayer, "Consensus layer should be Prysm") } @@ -405,8 +415,8 @@ func TestEthEnvLatestVersionFromGithub(t *testing.T) { config.ContainerType_ExecutionLayer: fmt.Sprintf("hyperledger/besu:%s", AUTOMATIC_STABLE_LATEST_TAG)}). Build() require.NoError(t, err, "Builder validation failed") - require.Equal(t, config.EthereumVersion_Eth2, *cfg.EthereumVersion, "Ethereum Version should be Eth2") - require.Equal(t, config.ExecutionLayer_Besu, *cfg.ExecutionLayer, "Execution layer should be Besu") + require.Equal(t, config_types.EthereumVersion_Eth2, *cfg.EthereumVersion, "Ethereum Version should be Eth2") + require.Equal(t, config_types.ExecutionLayer_Besu, *cfg.ExecutionLayer, "Execution layer should be Besu") require.Equal(t, config.ConsensusLayer_Prysm, *cfg.ConsensusLayer, "Consensus layer should be Prysm") require.NotContains(t, cfg.CustomDockerImages[config.ContainerType_ExecutionLayer], AUTOMATIC_STABLE_LATEST_TAG, "Automatic tag should be replaced") } @@ -415,10 +425,10 @@ func TestEthEnvMischmachedExecutionClient(t *testing.T) { t.Parallel() builder := NewEthereumNetworkBuilder() _, err := builder. - WithExecutionLayer(config.ExecutionLayer_Nethermind). + WithExecutionLayer(config_types.ExecutionLayer_Nethermind). WithCustomDockerImages(map[config.ContainerType]string{ config.ContainerType_ExecutionLayer: "hyperledger/besu:v1.22.0"}). Build() require.Error(t, err, "Builder validation succeeded") - require.Equal(t, fmt.Sprintf(MsgMismatchedExecutionClient, config.ExecutionLayer_Besu, config.ExecutionLayer_Nethermind), err.Error(), "Error message is not correct") + require.Equal(t, fmt.Sprintf(MsgMismatchedExecutionClient, config_types.ExecutionLayer_Besu, config_types.ExecutionLayer_Nethermind), err.Error(), "Error message is not correct") } diff --git a/docker/test_env/ethereum_env_versions_test.go b/docker/test_env/ethereum_env_versions_test.go index 49ca8433f..241b22f02 100644 --- a/docker/test_env/ethereum_env_versions_test.go +++ b/docker/test_env/ethereum_env_versions_test.go @@ -6,6 +6,9 @@ import ( "testing" "time" + "github.com/smartcontractkit/chainlink-testing-framework/config/types" + "github.com/smartcontractkit/chainlink-testing-framework/docker/ethereum" + "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" @@ -36,7 +39,7 @@ func TestEthEnvNethermindCompatibility(t *testing.T) { nethermindTcs = append(nethermindTcs, fmt.Sprintf("nethermind/nethermind:1.%d.0", i)) } - nethermindTcs = append(nethermindTcs, defaultNethermindEth2Image) + nethermindTcs = append(nethermindTcs, ethereum.DefaultNethermindEth2Image) latest, err := FetchLatestEthereumClientDockerImageVersionIfNeed(fmt.Sprintf("nethermind/nethermind:%s", AUTOMATIC_STABLE_LATEST_TAG)) require.NoError(t, err, "Couldn't fetch the latest Nethermind version") @@ -47,7 +50,7 @@ func TestEthEnvNethermindCompatibility(t *testing.T) { t.Run(fmt.Sprintf("nethermind-%s", tc), func(t *testing.T) { builder := NewEthereumNetworkBuilder() cfg, err := builder. - WithExecutionLayer(config.ExecutionLayer_Nethermind). + WithExecutionLayer(types.ExecutionLayer_Nethermind). WithCustomDockerImages(map[config.ContainerType]string{ config.ContainerType_ExecutionLayer: tc, }). @@ -84,7 +87,7 @@ func TestEthEnvGethCompatibility(t *testing.T) { gethTcs = append(gethTcs, fmt.Sprintf("ethereum/client-go:v1.%d.0", i)) } - gethTcs = append(gethTcs, defaultGethEth2Image) + gethTcs = append(gethTcs, ethereum.DefaultGethEth2Image) latest, err := FetchLatestEthereumClientDockerImageVersionIfNeed(fmt.Sprintf("ethereum/client-go:%s", AUTOMATIC_STABLE_LATEST_TAG)) require.NoError(t, err, "Couldn't fetch the latest Go Ethereum version") @@ -131,7 +134,7 @@ func TestEthEnvErigonCompatibility(t *testing.T) { erigonTcs = append(erigonTcs, fmt.Sprintf("thorax/erigon:v2.%d.0", i)) } - erigonTcs = append(erigonTcs, defaultErigonEth2Image) + erigonTcs = append(erigonTcs, ethereum.DefaultErigonEth2Image) latest, err := FetchLatestEthereumClientDockerImageVersionIfNeed(fmt.Sprintf("thorax/erigon:%s", AUTOMATIC_STABLE_LATEST_TAG)) require.NoError(t, err, "Couldn't fetch the latest Erigon version") @@ -178,7 +181,7 @@ func TestEthEnvBesuCompatibility(t *testing.T) { besuTcs = append(besuTcs, fmt.Sprintf("hyperledger/besu:%d.1.0", i)) } - besuTcs = append(besuTcs, defaultBesuEth2Image) + besuTcs = append(besuTcs, ethereum.DefaultBesuEth2Image) latest, err := FetchLatestEthereumClientDockerImageVersionIfNeed(fmt.Sprintf("hyperledger/besu:%s", AUTOMATIC_STABLE_LATEST_TAG)) require.NoError(t, err, "Couldn't fetch the latest Erigon version") diff --git a/docker/test_env/genesis_generator.go b/docker/test_env/genesis_generator.go index 77dc00242..03a46c3bb 100644 --- a/docker/test_env/genesis_generator.go +++ b/docker/test_env/genesis_generator.go @@ -17,32 +17,53 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/config" "github.com/smartcontractkit/chainlink-testing-framework/docker" + "github.com/smartcontractkit/chainlink-testing-framework/docker/ethereum" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/mirror" ) -const defaultGenisisGeneratorImage = "tofelb/ethereum-genesis-generator:3.3.4-slots-per-epoch" +var generatorForkToImageMap = map[ethereum.Fork]string{ + ethereum.EthereumFork_Shanghai: "tofelb/ethereum-genesis-generator:2.0.5", + ethereum.EthereumFork_Deneb: "tofelb/ethereum-genesis-generator:3.3.5-no-future-forks", +} + +var generatorForkToDataDirMap = map[ethereum.Fork]string{ + ethereum.EthereumFork_Shanghai: "/data/custom_config_data", + ethereum.EthereumFork_Deneb: "/data/metadata", +} -type EthGenesisGeneretor struct { +type EthGenesisGenerator struct { EnvComponent - chainConfig config.EthereumChainConfig - l zerolog.Logger - generatedDataHostDir string - t *testing.T + chainConfig config.EthereumChainConfig + l zerolog.Logger + generatedDataHostDir string + generatedDataContainerDir string + t *testing.T } -func NewEthGenesisGenerator(chainConfig config.EthereumChainConfig, generatedDataHostDir string, opts ...EnvComponentOption) (*EthGenesisGeneretor, error) { - parts := strings.Split(defaultGenisisGeneratorImage, ":") - g := &EthGenesisGeneretor{ +func NewEthGenesisGenerator(chainConfig config.EthereumChainConfig, generatedDataHostDir string, lastFork ethereum.Fork, opts ...EnvComponentOption) (*EthGenesisGenerator, error) { + genesisGeneratorImage, ok := generatorForkToImageMap[lastFork] + if !ok { + return nil, fmt.Errorf("unknown fork: %s", lastFork) + } + + generatedDataContainerDir, ok := generatorForkToDataDirMap[lastFork] + if !ok { + return nil, fmt.Errorf("unknown fork: %s", lastFork) + } + + parts := strings.Split(genesisGeneratorImage, ":") + g := &EthGenesisGenerator{ EnvComponent: EnvComponent{ ContainerName: fmt.Sprintf("%s-%s", "eth-genesis-generator", uuid.NewString()[0:8]), ContainerImage: parts[0], ContainerVersion: parts[1], StartupTimeout: 30 * time.Second, }, - chainConfig: chainConfig, - generatedDataHostDir: generatedDataHostDir, - l: log.Logger, + chainConfig: chainConfig, + generatedDataHostDir: generatedDataHostDir, + generatedDataContainerDir: generatedDataContainerDir, + l: log.Logger, } g.SetDefaultHooks() for _, opt := range opts { @@ -53,13 +74,13 @@ func NewEthGenesisGenerator(chainConfig config.EthereumChainConfig, generatedDat return g, nil } -func (g *EthGenesisGeneretor) WithTestInstance(t *testing.T) *EthGenesisGeneretor { +func (g *EthGenesisGenerator) WithTestInstance(t *testing.T) *EthGenesisGenerator { g.l = logging.GetTestLogger(t) g.t = t return g } -func (g *EthGenesisGeneretor) StartContainer() error { +func (g *EthGenesisGenerator) StartContainer() error { r, err := g.getContainerRequest(g.Networks) if err != nil { return err @@ -82,7 +103,7 @@ func (g *EthGenesisGeneretor) StartContainer() error { return nil } -func (g *EthGenesisGeneretor) getContainerRequest(networks []string) (*tc.ContainerRequest, error) { +func (g *EthGenesisGenerator) getContainerRequest(networks []string) (*tc.ContainerRequest, error) { valuesEnv, err := os.CreateTemp("", "values.env") if err != nil { return nil, err @@ -131,7 +152,7 @@ func (g *EthGenesisGeneretor) getContainerRequest(networks []string) (*tc.Contai Networks: networks, WaitingFor: tcwait.ForAll( tcwait.ForLog("+ terminalTotalDifficulty=0"), - tcwait.ForLog("+ sed -i 's/TERMINAL_TOTAL_DIFFICULTY:.*/TERMINAL_TOTAL_DIFFICULTY: 0/' /data/metadata/config.yaml"). + tcwait.ForLog(fmt.Sprintf("+ sed -i 's/TERMINAL_TOTAL_DIFFICULTY:.*/TERMINAL_TOTAL_DIFFICULTY: 0/' %s/config.yaml", g.generatedDataContainerDir)). WithPollInterval(1*time.Second), ).WithStartupTimeoutDefault(g.StartupTimeout), Cmd: []string{"all"}, @@ -161,7 +182,7 @@ func (g *EthGenesisGeneretor) getContainerRequest(networks []string) (*tc.Contai hostConfig.Mounts = append(hostConfig.Mounts, mount.Mount{ Type: mount.TypeBind, Source: g.generatedDataHostDir, - Target: GENERATED_DATA_DIR_INSIDE_CONTAINER, + Target: g.generatedDataContainerDir, ReadOnly: false, }) }, @@ -173,3 +194,7 @@ func (g *EthGenesisGeneretor) getContainerRequest(networks []string) (*tc.Contai }, }, nil } + +func (g *EthGenesisGenerator) GetGeneratedDataContainerDir() string { + return g.generatedDataContainerDir +} diff --git a/docker/test_env/genesis_generator_helpers.go b/docker/test_env/genesis_generator_helpers.go index 98af0e757..fe6f64eb0 100644 --- a/docker/test_env/genesis_generator_helpers.go +++ b/docker/test_env/genesis_generator_helpers.go @@ -54,11 +54,11 @@ export BELLATRIX_FORK_VERSION="0x30000038" export CAPELLA_FORK_VERSION="0x40000038" export CAPELLA_FORK_EPOCH="0" export DENEB_FORK_VERSION="0x50000038" -export DENEB_FORK_EPOCH="0" +export DENEB_FORK_EPOCH="{{.HardForkEpochs.Deneb}}" export ELECTRA_FORK_VERSION="0x60000038" -export ELECTRA_FORK_EPOCH="{{.HardForkEpochs.Electra}}" +#export ELECTRA_FORK_EPOCH="{{.HardForkEpochs.Electra}}" export EIP7594_FORK_VERSION="0x70000000" -export EIP7594_FORK_EPOCH="{{.HardForkEpochs.EOF}}" +#export EIP7594_FORK_EPOCH="{{.HardForkEpochs.EOF}}" export WITHDRAWAL_TYPE="0x00" export WITHDRAWAL_ADDRESS=0xf97e180c050e5Ab072211Ad2C213Eb5AEE4DF134 export BEACON_STATIC_ENR="enr:-Iq4QJk4WqRkjsX5c2CXtOra6HnxN-BMXnWhmhEQO9Bn9iABTJGdjUOurM7Btj1ouKaFkvTRoju5vz2GPmVON2dffQKGAX53x8JigmlkgnY0gmlwhLKAlv6Jc2VjcDI1NmsxoQK6S-Cii_KmfFdUJL2TANL3ksaKUnNXvTCv1tLwXs0QgIN1ZHCCIyk" @@ -118,6 +118,7 @@ el_premine_addrs: ${PREMINE_ADDRS} genesis_timestamp: ${GENESIS_TIMESTAMP} genesis_delay: ${GENESIS_DELAY} genesis_gaslimit: ${GENESIS_GASLIMIT} +deneb_fork_epoch: ${DENEB_FORK_EPOCH} additional_preloaded_contracts: ${ADDITIONAL_PRELOADED_CONTRACTS} slot_duration_in_seconds: ${SLOT_DURATION_IN_SECONDS} electra_fork_epoch: ${ELECTRA_FORK_EPOCH} @@ -163,7 +164,7 @@ CAPELLA_FORK_EPOCH: 0 # DENEB DENEB_FORK_VERSION: $DENEB_FORK_VERSION -DENEB_FORK_EPOCH: 0 +DENEB_FORK_EPOCH: $DENEB_FORK_EPOCH # Electra ELECTRA_FORK_VERSION: $ELECTRA_FORK_VERSION diff --git a/docker/test_env/geth_base.go b/docker/test_env/geth_base.go index e0d40e04e..1ab454b8d 100644 --- a/docker/test_env/geth_base.go +++ b/docker/test_env/geth_base.go @@ -6,8 +6,10 @@ import ( "testing" "time" - "github.com/rs/zerolog" + config_types "github.com/smartcontractkit/chainlink-testing-framework/config/types" + "github.com/Masterminds/semver/v3" + "github.com/rs/zerolog" tc "github.com/testcontainers/testcontainers-go" tcwait "github.com/testcontainers/testcontainers-go/wait" @@ -15,16 +17,10 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/config" "github.com/smartcontractkit/chainlink-testing-framework/docker" "github.com/smartcontractkit/chainlink-testing-framework/logging" + docker_utils "github.com/smartcontractkit/chainlink-testing-framework/utils/docker" "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" ) -const ( - defaultGethEth1Image = "ethereum/client-go:v1.13.8" - defaultGethEth2Image = "ethereum/client-go:v1.14.3" - gethBaseImageName = "ethereum/client-go" - gethGitRepo = "ethereum/go-ethereum" -) - type Geth struct { EnvComponent ExternalHttpUrl string @@ -33,12 +29,12 @@ type Geth struct { InternalWsUrl string InternalExecutionURL string ExternalExecutionURL string - generatedDataHostDir string chainConfig *config.EthereumChainConfig consensusLayer config.ConsensusLayer - ethereumVersion config.EthereumVersion + ethereumVersion config_types.EthereumVersion l zerolog.Logger t *testing.T + posContainerSettings } func (g *Geth) WithTestInstance(t *testing.T) ExecutionClient { @@ -50,7 +46,7 @@ func (g *Geth) WithTestInstance(t *testing.T) ExecutionClient { func (g *Geth) StartContainer() (blockchain.EVMNetwork, error) { var r *tc.ContainerRequest var err error - if g.GetEthereumVersion() == config.EthereumVersion_Eth1 { + if g.GetEthereumVersion() == config_types.EthereumVersion_Eth1 { r, err = g.getEth1ContainerRequest() } else { r, err = g.getEth2ContainerRequest() @@ -82,7 +78,7 @@ func (g *Geth) StartContainer() (blockchain.EVMNetwork, error) { if err != nil { return blockchain.EVMNetwork{}, err } - if g.GetEthereumVersion() == config.EthereumVersion_Eth2 { + if g.GetEthereumVersion() == config_types.EthereumVersion_Eth2 { executionPort, err := ct.MappedPort(testcontext.Get(g.t), NatPort(ETH2_EXECUTION_PORT)) if err != nil { return blockchain.EVMNetwork{}, err @@ -98,7 +94,7 @@ func (g *Geth) StartContainer() (blockchain.EVMNetwork, error) { g.InternalWsUrl = FormatWsUrl(g.ContainerName, DEFAULT_EVM_NODE_WS_PORT) networkConfig := blockchain.SimulatedEVMNetwork - if g.GetEthereumVersion() == config.EthereumVersion_Eth1 { + if g.GetEthereumVersion() == config_types.EthereumVersion_Eth1 { networkConfig.Name = fmt.Sprintf("Private Eth-1-PoA [geth %s]", g.ContainerVersion) } else { networkConfig.Name = fmt.Sprintf("Private Eth-2-PoS [geth %s] + %s", g.ContainerVersion, g.consensusLayer) @@ -107,12 +103,17 @@ func (g *Geth) StartContainer() (blockchain.EVMNetwork, error) { networkConfig.HTTPURLs = []string{g.ExternalHttpUrl} networkConfig.SimulationType = "Geth" - comparableVersion, err := GetComparableVersionFromDockerImage(g.GetImageWithVersion()) + version, err := docker_utils.GetSemverFromImage(g.GetImageWithVersion()) if err != nil { return blockchain.EVMNetwork{}, err } - if comparableVersion >= 110 && comparableVersion < 111 { + constraint, err := semver.NewConstraint(">=1.10 <1.11") + if err != nil { + return blockchain.EVMNetwork{}, fmt.Errorf("failed to parse constraint: %s", ">=1.10 <1.11") + } + + if constraint.Check(version) { // Geth v1.10.x will not set it itself if it's set 0, like later versions do networkConfig.DefaultGasLimit = 9_000_000 } @@ -124,14 +125,14 @@ func (g *Geth) StartContainer() (blockchain.EVMNetwork, error) { } func (g *Geth) GetInternalExecutionURL() string { - if g.GetEthereumVersion() == config.EthereumVersion_Eth1 { + if g.GetEthereumVersion() == config_types.EthereumVersion_Eth1 { panic("eth1 node doesn't have an execution URL") } return g.InternalExecutionURL } func (g *Geth) GetExternalExecutionURL() string { - if g.GetEthereumVersion() == config.EthereumVersion_Eth1 { + if g.GetEthereumVersion() == config_types.EthereumVersion_Eth1 { panic("eth1 node doesn't have an execution URL") } return g.ExternalExecutionURL @@ -161,19 +162,19 @@ func (g *Geth) GetContainer() *tc.Container { return &g.Container } -func (g *Geth) GetEthereumVersion() config.EthereumVersion { +func (g *Geth) GetEthereumVersion() config_types.EthereumVersion { return g.ethereumVersion } func (g *Geth) GethConsensusMechanism() ConsensusMechanism { - if g.GetEthereumVersion() == config.EthereumVersion_Eth1 { + if g.GetEthereumVersion() == config_types.EthereumVersion_Eth1 { return ConsensusMechanism_PoA } return ConsensusMechanism_PoS } func (g *Geth) WaitUntilChainIsReady(ctx context.Context, waitTime time.Duration) error { - if g.GetEthereumVersion() == config.EthereumVersion_Eth1 { + if g.GetEthereumVersion() == config_types.EthereumVersion_Eth1 { return nil } waitForFirstBlock := tcwait.NewLogStrategy("Chain head was updated").WithPollInterval(1 * time.Second).WithStartupTimeout(waitTime) @@ -181,7 +182,7 @@ func (g *Geth) WaitUntilChainIsReady(ctx context.Context, waitTime time.Duration } func (g *Geth) getEntryPointAndKeystoreLocation(minerAddress string) ([]string, error) { - version, err := GetComparableVersionFromDockerImage(g.GetImageWithVersion()) + version, err := docker_utils.GetSemverFromImage(g.GetImageWithVersion()) if err != nil { return nil, err } @@ -204,7 +205,12 @@ func (g *Geth) getEntryPointAndKeystoreLocation(minerAddress string) ([]string, "--unlock", minerAddress, } - if version < 110 { + constraint, err := semver.NewConstraint("<1.10") + if err != nil { + return nil, fmt.Errorf("failed to parse constraint: %s", "<1.10") + } + + if constraint.Check(version) { entrypoint = append(entrypoint, "--rpc", "--rpcapi", enabledApis, @@ -218,9 +224,7 @@ func (g *Geth) getEntryPointAndKeystoreLocation(minerAddress string) ([]string, "--wsapi", enabledApis, fmt.Sprintf("--wsport=%s", DEFAULT_EVM_NODE_WS_PORT), ) - } - - if version >= 110 { + } else { entrypoint = append(entrypoint, "--http", "--http.vhosts", "*", @@ -242,12 +246,17 @@ func (g *Geth) getEntryPointAndKeystoreLocation(minerAddress string) ([]string, } func (g *Geth) getWebsocketEnabledMessage() (string, error) { - version, err := GetComparableVersionFromDockerImage(g.GetImageWithVersion()) + version, err := docker_utils.GetSemverFromImage(g.GetImageWithVersion()) if err != nil { return "", err } - if version < 110 { + constraint, err := semver.NewConstraint("<1.10") + if err != nil { + return "", fmt.Errorf("failed to parse constraint: %s", "<1.10") + } + + if constraint.Check(version) { return "WebSocket endpoint opened", nil } diff --git a/docker/test_env/geth_eth1.go b/docker/test_env/geth_eth1.go index d2763e49f..5581b3982 100644 --- a/docker/test_env/geth_eth1.go +++ b/docker/test_env/geth_eth1.go @@ -7,6 +7,8 @@ import ( "strings" "time" + config_types "github.com/smartcontractkit/chainlink-testing-framework/config/types" + "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/mount" "github.com/google/uuid" @@ -14,6 +16,7 @@ import ( tcwait "github.com/testcontainers/testcontainers-go/wait" "github.com/smartcontractkit/chainlink-testing-framework/config" + "github.com/smartcontractkit/chainlink-testing-framework/docker/ethereum" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/mirror" "github.com/smartcontractkit/chainlink-testing-framework/utils/templates" @@ -21,7 +24,7 @@ import ( // NewGethEth1 starts a new Geth Eth1 node running in Docker func NewGethEth1(networks []string, chainConfig *config.EthereumChainConfig, opts ...EnvComponentOption) *Geth { - parts := strings.Split(defaultGethEth1Image, ":") + parts := strings.Split(ethereum.DefaultGethEth1Image, ":") g := &Geth{ EnvComponent: EnvComponent{ ContainerName: fmt.Sprintf("%s-%s", "geth-eth1", uuid.NewString()[0:8]), @@ -32,7 +35,7 @@ func NewGethEth1(networks []string, chainConfig *config.EthereumChainConfig, opt }, chainConfig: chainConfig, l: logging.GetTestLogger(nil), - ethereumVersion: config.EthereumVersion_Eth1, + ethereumVersion: config_types.EthereumVersion_Eth1, } g.SetDefaultHooks() for _, opt := range opts { diff --git a/docker/test_env/geth_eth2.go b/docker/test_env/geth_eth2.go index 8a0590a23..dc8138639 100644 --- a/docker/test_env/geth_eth2.go +++ b/docker/test_env/geth_eth2.go @@ -8,6 +8,8 @@ import ( "strings" "time" + config_types "github.com/smartcontractkit/chainlink-testing-framework/config/types" + "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/mount" "github.com/google/uuid" @@ -15,13 +17,14 @@ import ( tcwait "github.com/testcontainers/testcontainers-go/wait" "github.com/smartcontractkit/chainlink-testing-framework/config" + "github.com/smartcontractkit/chainlink-testing-framework/docker/ethereum" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/mirror" ) // NewGethEth2 starts a new Geth Eth2 node running in Docker -func NewGethEth2(networks []string, chainConfig *config.EthereumChainConfig, generatedDataHostDir string, consensusLayer config.ConsensusLayer, opts ...EnvComponentOption) (*Geth, error) { - parts := strings.Split(defaultGethEth2Image, ":") +func NewGethEth2(networks []string, chainConfig *config.EthereumChainConfig, generatedDataHostDir, generatedDataContainerDir string, consensusLayer config.ConsensusLayer, opts ...EnvComponentOption) (*Geth, error) { + parts := strings.Split(ethereum.DefaultGethEth2Image, ":") g := &Geth{ EnvComponent: EnvComponent{ ContainerName: fmt.Sprintf("%s-%s", "geth-eth2", uuid.NewString()[0:8]), @@ -31,10 +34,10 @@ func NewGethEth2(networks []string, chainConfig *config.EthereumChainConfig, gen StartupTimeout: 2 * time.Minute, }, chainConfig: chainConfig, - generatedDataHostDir: generatedDataHostDir, + posContainerSettings: posContainerSettings{generatedDataHostDir: generatedDataHostDir, generatedDataContainerDir: generatedDataContainerDir}, consensusLayer: consensusLayer, l: logging.GetTestLogger(nil), - ethereumVersion: config.EthereumVersion_Eth2, + ethereumVersion: config_types.EthereumVersion_Eth2, } g.SetDefaultHooks() for _, opt := range opts { @@ -91,7 +94,7 @@ func (g *Geth) getEth2ContainerRequest() (*tc.ContainerRequest, error) { hostConfig.Mounts = append(hostConfig.Mounts, mount.Mount{ Type: mount.TypeBind, Source: g.generatedDataHostDir, - Target: GENERATED_DATA_DIR_INSIDE_CONTAINER, + Target: g.generatedDataContainerDir, ReadOnly: false, }) }, @@ -152,10 +155,10 @@ func (g *Geth) buildEth2dInitScript() (string, error) { HttpPort: DEFAULT_EVM_NODE_HTTP_PORT, WsPort: DEFAULT_EVM_NODE_WS_PORT, ChainID: g.chainConfig.ChainID, - GeneratedDataDir: GENERATED_DATA_DIR_INSIDE_CONTAINER, - JwtFileLocation: JWT_SECRET_FILE_LOCATION_INSIDE_CONTAINER, - PasswordFileLocation: ACCOUNT_PASSWORD_FILE_INSIDE_CONTAINER, - KeystoreDirLocation: KEYSTORE_DIR_LOCATION_INSIDE_CONTAINER, + GeneratedDataDir: g.generatedDataContainerDir, + JwtFileLocation: getJWTSecretFileLocationInsideContainer(g.generatedDataContainerDir), + PasswordFileLocation: getAccountPasswordFileInsideContainer(g.generatedDataContainerDir), + KeystoreDirLocation: getKeystoreDirLocationInsideContainer(g.generatedDataContainerDir), ExecutionDir: "/execution-data", Verbosity: verbosity, } diff --git a/docker/test_env/geth_test.go b/docker/test_env/geth_test.go index 00dfadc66..5a71ea871 100644 --- a/docker/test_env/geth_test.go +++ b/docker/test_env/geth_test.go @@ -4,6 +4,8 @@ import ( "fmt" "testing" + config_types "github.com/smartcontractkit/chainlink-testing-framework/config/types" + "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" @@ -18,7 +20,7 @@ func TestGethLegacy(t *testing.T) { l := logging.GetTestLogger(t) network, err := docker.CreateNetwork(l) require.NoError(t, err) - defaultChainCfg := config.GetDefaultChainConfig() + defaultChainCfg := config.MustGetDefaultChainConfig() g := NewGethEth1([]string{network.Name}, &defaultChainCfg). WithTestInstance(t) _, err = g.StartContainer() @@ -37,11 +39,11 @@ func TestGethEth1(t *testing.T) { builder := NewEthereumNetworkBuilder() cfg, err := builder. //nolint:staticcheck //ignore SA1019 - WithEthereumVersion(config.EthereumVersion_Eth1_Legacy). + WithEthereumVersion(config_types.EthereumVersion_Eth1_Legacy). WithEthereumChainConfig(config.EthereumChainConfig{ ChainID: 2337, }). - WithExecutionLayer(config.ExecutionLayer_Geth). + WithExecutionLayer(config_types.ExecutionLayer_Geth). Build() require.NoError(t, err, "Builder validation failed") @@ -54,15 +56,14 @@ func TestGethEth1(t *testing.T) { require.NoError(t, err, "Couldn't close the client") } -func TestGethEth2(t *testing.T) { +func TestGethEth2_Deneb(t *testing.T) { l := logging.GetTestLogger(t) builder := NewEthereumNetworkBuilder() cfg, err := builder. - //nolint:staticcheck //ignore SA1019 - WithEthereumVersion(config.EthereumVersion_Eth2_Legacy). + WithCustomDockerImages(map[config.ContainerType]string{config.ContainerType_ExecutionLayer: "ethereum/client-go:v1.13.12"}). WithConsensusLayer(config.ConsensusLayer_Prysm). - WithExecutionLayer(config.ExecutionLayer_Geth). + WithExecutionLayer(config_types.ExecutionLayer_Geth). Build() require.NoError(t, err, "Builder validation failed") @@ -100,3 +101,67 @@ func TestGethEth2(t *testing.T) { err = sendAndCompareBalances(ctx, clientTwo, address) require.NoError(t, err, fmt.Sprintf("balance wasn't correctly updated for %s network", eip1559Network.Name)) } + +func TestGethEth2_Shanghai_No_Fork_Setup(t *testing.T) { + l := logging.GetTestLogger(t) + + builder := NewEthereumNetworkBuilder() + cfg, err := builder. + WithCustomDockerImages(map[config.ContainerType]string{config.ContainerType_ExecutionLayer: "ethereum/client-go:v1.13.11"}). + WithExecutionLayer(config_types.ExecutionLayer_Geth). + Build() + require.NoError(t, err, "Builder validation failed") + + _, eth2, err := cfg.Start() + require.NoError(t, err, "Couldn't start PoS network") + + nonEip1559Network := blockchain.SimulatedEVMNetwork + nonEip1559Network.Name = "Simulated Geth + Prysm (non-EIP 1559)" + nonEip1559Network.URLs = eth2.PublicWsUrls() + clientOne, err := blockchain.ConnectEVMClient(nonEip1559Network, l) + require.NoError(t, err, "Couldn't connect to the evm client") + + t.Cleanup(func() { + err = clientOne.Close() + require.NoError(t, err, "Couldn't close the client") + }) + + ctx := testcontext.Get(t) + address := common.HexToAddress("0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1") + err = sendAndCompareBalances(ctx, clientOne, address) + require.NoError(t, err, fmt.Sprintf("balance wasn't correctly updated for %s network", nonEip1559Network.Name)) +} + +func TestGethEth2_Shanghai(t *testing.T) { + l := logging.GetTestLogger(t) + + chainConfig := config.MustGetDefaultChainConfig() + chainConfig.HardForkEpochs = map[string]int{"Deneb": 500} + + builder := NewEthereumNetworkBuilder() + cfg, err := builder. + WithCustomDockerImages(map[config.ContainerType]string{config.ContainerType_ExecutionLayer: "ethereum/client-go:v1.13.11"}). + WithExecutionLayer(config_types.ExecutionLayer_Geth). + WithEthereumChainConfig(chainConfig). + Build() + require.NoError(t, err, "Builder validation failed") + + _, eth2, err := cfg.Start() + require.NoError(t, err, "Couldn't start PoS network") + + nonEip1559Network := blockchain.SimulatedEVMNetwork + nonEip1559Network.Name = "Simulated Geth + Prysm (non-EIP 1559)" + nonEip1559Network.URLs = eth2.PublicWsUrls() + clientOne, err := blockchain.ConnectEVMClient(nonEip1559Network, l) + require.NoError(t, err, "Couldn't connect to the evm client") + + t.Cleanup(func() { + err = clientOne.Close() + require.NoError(t, err, "Couldn't close the client") + }) + + ctx := testcontext.Get(t) + address := common.HexToAddress("0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1") + err = sendAndCompareBalances(ctx, clientOne, address) + require.NoError(t, err, fmt.Sprintf("balance wasn't correctly updated for %s network", nonEip1559Network.Name)) +} diff --git a/docker/test_env/github_utils.go b/docker/test_env/github_utils.go index be4267af9..941c94dab 100644 --- a/docker/test_env/github_utils.go +++ b/docker/test_env/github_utils.go @@ -5,6 +5,7 @@ import ( "strings" "github.com/smartcontractkit/chainlink-testing-framework/client" + "github.com/smartcontractkit/chainlink-testing-framework/docker/ethereum" ) const AUTOMATIC_LATEST_TAG = "latest_available" @@ -18,7 +19,7 @@ func FetchLatestEthereumClientDockerImageVersionIfNeed(dockerImageWithVersion st return "", fmt.Errorf("expected correctly formatted docker image, but got '%s'", dockerImageWithVersion) } - ghRepo, err := GetGithubRepositoryFromEthereumClientDockerImage(dockerImageWithVersion) + ghRepo, err := ethereum.GithubRepositoryFromEthereumClientDockerImage(dockerImageWithVersion) if err != nil { return "", err } diff --git a/docker/test_env/nethermind_base.go b/docker/test_env/nethermind_base.go index 33e6949ca..1e7937d8f 100644 --- a/docker/test_env/nethermind_base.go +++ b/docker/test_env/nethermind_base.go @@ -6,6 +6,8 @@ import ( "testing" "time" + config_types "github.com/smartcontractkit/chainlink-testing-framework/config/types" + "github.com/rs/zerolog" tc "github.com/testcontainers/testcontainers-go" @@ -17,13 +19,6 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/logging" ) -const ( - defaultNethermindEth1Image = "nethermind/nethermind:1.16.0" - defaultNethermindEth2Image = "nethermind/nethermind:1.26.0" - nethermindBaseImageName = "nethermind/nethermind" - nethermindGitRepo = "NethermindEth/nethermind" -) - type Nethermind struct { EnvComponent ExternalHttpUrl string @@ -32,12 +27,12 @@ type Nethermind struct { InternalWsUrl string InternalExecutionURL string ExternalExecutionURL string - generatedDataHostDir string chainConfig *config.EthereumChainConfig consensusLayer config.ConsensusLayer - ethereumVersion config.EthereumVersion + ethereumVersion config_types.EthereumVersion l zerolog.Logger t *testing.T + posContainerSettings } func (g *Nethermind) WithTestInstance(t *testing.T) ExecutionClient { @@ -49,7 +44,7 @@ func (g *Nethermind) WithTestInstance(t *testing.T) ExecutionClient { func (g *Nethermind) StartContainer() (blockchain.EVMNetwork, error) { var r *tc.ContainerRequest var err error - if g.GetEthereumVersion() == config.EthereumVersion_Eth1 { + if g.GetEthereumVersion() == config_types.EthereumVersion_Eth1 { r, err = g.getEth1ContainerRequest() } else { @@ -83,7 +78,7 @@ func (g *Nethermind) StartContainer() (blockchain.EVMNetwork, error) { return blockchain.EVMNetwork{}, err } - if g.GetEthereumVersion() == config.EthereumVersion_Eth2 { + if g.GetEthereumVersion() == config_types.EthereumVersion_Eth2 { executionPort, err := ct.MappedPort(context.Background(), NatPort(ETH2_EXECUTION_PORT)) if err != nil { return blockchain.EVMNetwork{}, err @@ -99,7 +94,7 @@ func (g *Nethermind) StartContainer() (blockchain.EVMNetwork, error) { g.InternalWsUrl = FormatWsUrl(g.ContainerName, DEFAULT_EVM_NODE_WS_PORT) networkConfig := blockchain.SimulatedEVMNetwork - if g.GetEthereumVersion() == config.EthereumVersion_Eth1 { + if g.GetEthereumVersion() == config_types.EthereumVersion_Eth1 { networkConfig.Name = fmt.Sprintf("Private Eth-1-PoA [nethermind %s", g.ContainerVersion) networkConfig.GasEstimationBuffer = 100_000_000_000 } else { @@ -116,14 +111,14 @@ func (g *Nethermind) StartContainer() (blockchain.EVMNetwork, error) { } func (g *Nethermind) GetInternalExecutionURL() string { - if g.GetEthereumVersion() == config.EthereumVersion_Eth1 { + if g.GetEthereumVersion() == config_types.EthereumVersion_Eth1 { panic("eth1 node doesn't have an execution URL") } return g.InternalExecutionURL } func (g *Nethermind) GetExternalExecutionURL() string { - if g.GetEthereumVersion() == config.EthereumVersion_Eth1 { + if g.GetEthereumVersion() == config_types.EthereumVersion_Eth1 { panic("eth1 node doesn't have an execution URL") } return g.ExternalExecutionURL @@ -153,12 +148,12 @@ func (g *Nethermind) GetContainer() *tc.Container { return &g.Container } -func (g *Nethermind) GetEthereumVersion() config.EthereumVersion { +func (g *Nethermind) GetEthereumVersion() config_types.EthereumVersion { return g.ethereumVersion } func (g *Nethermind) WaitUntilChainIsReady(ctx context.Context, waitTime time.Duration) error { - if g.GetEthereumVersion() == config.EthereumVersion_Eth1 { + if g.GetEthereumVersion() == config_types.EthereumVersion_Eth1 { return nil } waitForFirstBlock := tcwait.NewLogStrategy("Improved post-merge block").WithPollInterval(1 * time.Second).WithStartupTimeout(waitTime) @@ -166,7 +161,7 @@ func (g *Nethermind) WaitUntilChainIsReady(ctx context.Context, waitTime time.Du } func (g *Nethermind) GethConsensusMechanism() ConsensusMechanism { - if g.GetEthereumVersion() == config.EthereumVersion_Eth1 { + if g.GetEthereumVersion() == config_types.EthereumVersion_Eth1 { return ConsensusMechanism_PoA } return ConsensusMechanism_PoS diff --git a/docker/test_env/nethermind_eth1.go b/docker/test_env/nethermind_eth1.go index d64639a6a..4abefd4dc 100644 --- a/docker/test_env/nethermind_eth1.go +++ b/docker/test_env/nethermind_eth1.go @@ -7,6 +7,8 @@ import ( "strings" "time" + config_types "github.com/smartcontractkit/chainlink-testing-framework/config/types" + "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/mount" "github.com/google/uuid" @@ -14,14 +16,15 @@ import ( tcwait "github.com/testcontainers/testcontainers-go/wait" "github.com/smartcontractkit/chainlink-testing-framework/config" + "github.com/smartcontractkit/chainlink-testing-framework/docker/ethereum" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/mirror" "github.com/smartcontractkit/chainlink-testing-framework/utils/templates" ) -// NewNethermindEth1 starts a new Nethermin Eth1 node running in Docker +// NewNethermindEth1 starts a new Nethermind Eth1 node running in Docker func NewNethermindEth1(networks []string, chainConfig *config.EthereumChainConfig, opts ...EnvComponentOption) (*Nethermind, error) { - parts := strings.Split(defaultNethermindEth1Image, ":") + parts := strings.Split(ethereum.DefaultNethermindEth1Image, ":") g := &Nethermind{ EnvComponent: EnvComponent{ ContainerName: fmt.Sprintf("%s-%s", "nethermind-eth1", uuid.NewString()[0:8]), @@ -32,7 +35,7 @@ func NewNethermindEth1(networks []string, chainConfig *config.EthereumChainConfi }, chainConfig: chainConfig, l: logging.GetTestLogger(nil), - ethereumVersion: config.EthereumVersion_Eth1, + ethereumVersion: config_types.EthereumVersion_Eth1, } g.SetDefaultHooks() for _, opt := range opts { diff --git a/docker/test_env/nethermind_eth2.go b/docker/test_env/nethermind_eth2.go index b860a2883..9cb12b9ea 100644 --- a/docker/test_env/nethermind_eth2.go +++ b/docker/test_env/nethermind_eth2.go @@ -6,6 +6,8 @@ import ( "strings" "time" + config_types "github.com/smartcontractkit/chainlink-testing-framework/config/types" + "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/mount" "github.com/google/uuid" @@ -13,13 +15,14 @@ import ( tcwait "github.com/testcontainers/testcontainers-go/wait" "github.com/smartcontractkit/chainlink-testing-framework/config" + "github.com/smartcontractkit/chainlink-testing-framework/docker/ethereum" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/mirror" ) // NewNethermindEth2 starts a new Nethermin Eth2 node running in Docker -func NewNethermindEth2(networks []string, chainConfig *config.EthereumChainConfig, generatedDataHostDir string, consensusLayer config.ConsensusLayer, opts ...EnvComponentOption) (*Nethermind, error) { - parts := strings.Split(defaultNethermindEth2Image, ":") +func NewNethermindEth2(networks []string, chainConfig *config.EthereumChainConfig, generatedDataHostDir, generatedDataContainerDir string, consensusLayer config.ConsensusLayer, opts ...EnvComponentOption) (*Nethermind, error) { + parts := strings.Split(ethereum.DefaultNethermindEth2Image, ":") g := &Nethermind{ EnvComponent: EnvComponent{ ContainerName: fmt.Sprintf("%s-%s", "nethermind-eth2", uuid.NewString()[0:8]), @@ -28,11 +31,11 @@ func NewNethermindEth2(networks []string, chainConfig *config.EthereumChainConfi ContainerVersion: parts[1], StartupTimeout: 2 * time.Minute, }, - generatedDataHostDir: generatedDataHostDir, + posContainerSettings: posContainerSettings{generatedDataHostDir: generatedDataHostDir, generatedDataContainerDir: generatedDataContainerDir}, chainConfig: chainConfig, consensusLayer: consensusLayer, l: logging.GetTestLogger(nil), - ethereumVersion: config.EthereumVersion_Eth2, + ethereumVersion: config_types.EthereumVersion_Eth2, } g.SetDefaultHooks() for _, opt := range opts { @@ -64,7 +67,7 @@ func (g *Nethermind) getEth2ContainerRequest() (*tc.ContainerRequest, error) { command := []string{ "--datadir=/nethermind", "--config=/none.cfg", - fmt.Sprintf("--Init.ChainSpecPath=%s/chainspec.json", GENERATED_DATA_DIR_INSIDE_CONTAINER), + fmt.Sprintf("--Init.ChainSpecPath=%s/chainspec.json", g.generatedDataContainerDir), "--Init.DiscoveryEnabled=false", "--Init.WebSocketsEnabled=true", fmt.Sprintf("--JsonRpc.WebSocketsPort=%s", DEFAULT_EVM_NODE_WS_PORT), @@ -75,11 +78,11 @@ func (g *Nethermind) getEth2ContainerRequest() (*tc.ContainerRequest, error) { fmt.Sprintf("--JsonRpc.Port=%s", DEFAULT_EVM_NODE_HTTP_PORT), "--JsonRpc.EngineHost=0.0.0.0", "--JsonRpc.EnginePort=" + ETH2_EXECUTION_PORT, - fmt.Sprintf("--JsonRpc.JwtSecretFile=%s", JWT_SECRET_FILE_LOCATION_INSIDE_CONTAINER), - fmt.Sprintf("--KeyStore.KeyStoreDirectory=%s", KEYSTORE_DIR_LOCATION_INSIDE_CONTAINER), + fmt.Sprintf("--JsonRpc.JwtSecretFile=%s", getJWTSecretFileLocationInsideContainer(g.generatedDataContainerDir)), + fmt.Sprintf("--KeyStore.KeyStoreDirectory=%s", getKeystoreDirLocationInsideContainer(g.generatedDataContainerDir)), "--KeyStore.BlockAuthorAccount=0x123463a4b065722e99115d6c222f267d9cabb524", "--KeyStore.UnlockAccounts=0x123463a4b065722e99115d6c222f267d9cabb524", - fmt.Sprintf("--KeyStore.PasswordFiles=%s", ACCOUNT_PASSWORD_FILE_INSIDE_CONTAINER), + fmt.Sprintf("--KeyStore.PasswordFiles=%s", getAccountPasswordFileInsideContainer(g.generatedDataContainerDir)), "--Network.MaxActivePeers=0", "--Network.OnlyStaticPeers=true", "--HealthChecks.Enabled=true", // default slug /health @@ -115,7 +118,7 @@ func (g *Nethermind) getEth2ContainerRequest() (*tc.ContainerRequest, error) { hostConfig.Mounts = append(hostConfig.Mounts, mount.Mount{ Type: mount.TypeBind, Source: g.generatedDataHostDir, - Target: GENERATED_DATA_DIR_INSIDE_CONTAINER, + Target: g.generatedDataContainerDir, ReadOnly: false, }) }, diff --git a/docker/test_env/nethermind_test.go b/docker/test_env/nethermind_test.go index a5a61f4bf..184cb72e9 100644 --- a/docker/test_env/nethermind_test.go +++ b/docker/test_env/nethermind_test.go @@ -4,6 +4,8 @@ import ( "fmt" "testing" + config_types "github.com/smartcontractkit/chainlink-testing-framework/config/types" + "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" @@ -19,8 +21,8 @@ func TestNethermindEth1(t *testing.T) { builder := NewEthereumNetworkBuilder() cfg, err := builder. //nolint:staticcheck //ignore SA1019 - WithEthereumVersion(config.EthereumVersion_Eth1_Legacy). - WithExecutionLayer(config.ExecutionLayer_Nethermind). + WithEthereumVersion(config_types.EthereumVersion_Eth1_Legacy). + WithExecutionLayer(config_types.ExecutionLayer_Nethermind). Build() require.NoError(t, err, "Builder validation failed") @@ -38,15 +40,14 @@ func TestNethermindEth1(t *testing.T) { require.NoError(t, err, "Couldn't close the client") } -func TestNethermindEth2(t *testing.T) { +func TestNethermindEth2_Dencun(t *testing.T) { l := logging.GetTestLogger(t) builder := NewEthereumNetworkBuilder() cfg, err := builder. - //nolint:staticcheck //ignore SA1019 - WithEthereumVersion(config.EthereumVersion_Eth2_Legacy). + WithCustomDockerImages(map[config.ContainerType]string{config.ContainerType_ExecutionLayer: "nethermind/nethermind:1.26.0"}). WithConsensusLayer(config.ConsensusLayer_Prysm). - WithExecutionLayer(config.ExecutionLayer_Nethermind). + WithExecutionLayer(config_types.ExecutionLayer_Nethermind). Build() require.NoError(t, err, "Builder validation failed") @@ -84,3 +85,38 @@ func TestNethermindEth2(t *testing.T) { err = sendAndCompareBalances(ctx, clientTwo, address) require.NoError(t, err, fmt.Sprintf("balance wasn't correctly updated for %s network", eip1559Network.Name)) } + +func TestNethermindEth2_Shenghai(t *testing.T) { + l := logging.GetTestLogger(t) + + chainConfig := config.MustGetDefaultChainConfig() + chainConfig.HardForkEpochs = map[string]int{"Deneb": 500} + + builder := NewEthereumNetworkBuilder() + cfg, err := builder. + WithCustomDockerImages(map[config.ContainerType]string{config.ContainerType_ExecutionLayer: "nethermind/nethermind:1.25.4"}). + WithConsensusLayer(config.ConsensusLayer_Prysm). + WithExecutionLayer(config_types.ExecutionLayer_Nethermind). + WithEthereumChainConfig(chainConfig). + Build() + require.NoError(t, err, "Builder validation failed") + + _, eth2, err := cfg.Start() + require.NoError(t, err, "Couldn't start PoS network") + + nonEip1559Network := blockchain.SimulatedEVMNetwork + nonEip1559Network.Name = "Simulated Nethermind + Prysm (non-EIP 1559)" + nonEip1559Network.URLs = eth2.PublicWsUrls() + clientOne, err := blockchain.ConnectEVMClient(nonEip1559Network, l) + require.NoError(t, err, "Couldn't connect to the evm client") + + t.Cleanup(func() { + err = clientOne.Close() + require.NoError(t, err, "Couldn't close the client") + }) + + ctx := testcontext.Get(t) + address := common.HexToAddress("0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1") + err = sendAndCompareBalances(ctx, clientOne, address) + require.NoError(t, err, fmt.Sprintf("balance wasn't correctly updated for %s network", nonEip1559Network.Name)) +} diff --git a/docker/test_env/prysm.go b/docker/test_env/prysm.go index 4d6f38e77..acacb1083 100644 --- a/docker/test_env/prysm.go +++ b/docker/test_env/prysm.go @@ -15,6 +15,7 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/config" "github.com/smartcontractkit/chainlink-testing-framework/docker" + "github.com/smartcontractkit/chainlink-testing-framework/docker/ethereum" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/mirror" "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" @@ -23,26 +24,37 @@ import ( const ( PRYSM_QUERY_RPC_PORT = "3500" PRYSM_NODE_RPC_PORT = "4000" - - defaultPrysmBeaconChainImage = "gcr.io/prysmaticlabs/prysm/beacon-chain:v5.0.4" - defaultPyrsmValidatorImage = "gcr.io/prysmaticlabs/prysm/validator:v5.0.4" ) +var beaconForkToImageMap = map[ethereum.Fork]string{ + ethereum.EthereumFork_Shanghai: "gcr.io/prysmaticlabs/prysm/beacon-chain:v4.1.1", + ethereum.EthereumFork_Deneb: "gcr.io/prysmaticlabs/prysm/beacon-chain:v5.0.4", +} + +var validatorForkToImageMap = map[ethereum.Fork]string{ + ethereum.EthereumFork_Shanghai: "gcr.io/prysmaticlabs/prysm/validator:v4.1.1", + ethereum.EthereumFork_Deneb: "gcr.io/prysmaticlabs/prysm/validator:v5.0.4", +} + type PrysmBeaconChain struct { EnvComponent InternalBeaconRpcProvider string InternalQueryRpcUrl string ExternalBeaconRpcProvider string ExternalQueryRpcUrl string - generatedDataHostDir string gethInternalExecutionURL string chainConfig *config.EthereumChainConfig l zerolog.Logger t *testing.T + posContainerSettings } -func NewPrysmBeaconChain(networks []string, chainConfig *config.EthereumChainConfig, customConfigDataDir, gethExecutionURL string, opts ...EnvComponentOption) (*PrysmBeaconChain, error) { - parts := strings.Split(defaultPrysmBeaconChainImage, ":") +func NewPrysmBeaconChain(networks []string, chainConfig *config.EthereumChainConfig, generatedDataHostDir, generatedDataContainerDir, gethExecutionURL string, baseEthereumFork ethereum.Fork, opts ...EnvComponentOption) (*PrysmBeaconChain, error) { + prysmBeaconChainImage, ok := beaconForkToImageMap[baseEthereumFork] + if !ok { + return nil, fmt.Errorf("unknown fork: %s", baseEthereumFork) + } + parts := strings.Split(prysmBeaconChainImage, ":") g := &PrysmBeaconChain{ EnvComponent: EnvComponent{ ContainerName: fmt.Sprintf("%s-%s", "prysm-beacon-chain", uuid.NewString()[0:8]), @@ -52,7 +64,7 @@ func NewPrysmBeaconChain(networks []string, chainConfig *config.EthereumChainCon StartupTimeout: 2 * time.Minute, }, chainConfig: chainConfig, - generatedDataHostDir: customConfigDataDir, + posContainerSettings: posContainerSettings{generatedDataHostDir: generatedDataHostDir, generatedDataContainerDir: generatedDataContainerDir}, gethInternalExecutionURL: gethExecutionURL, l: logging.GetTestLogger(nil), } @@ -115,7 +127,7 @@ func (g *PrysmBeaconChain) StartContainer() error { } func (g *PrysmBeaconChain) getContainerRequest(networks []string) (*tc.ContainerRequest, error) { - timeout := g.chainConfig.GetDefaultWaitDuration() + timeout := g.chainConfig.DefaultWaitDuration() if g.StartupTimeout < timeout { timeout = g.StartupTimeout } @@ -133,15 +145,15 @@ func (g *PrysmBeaconChain) getContainerRequest(networks []string) (*tc.Container Cmd: []string{ "--accept-terms-of-use", "--datadir=/consensus-data", - fmt.Sprintf("--chain-config-file=%s/config.yaml", GENERATED_DATA_DIR_INSIDE_CONTAINER), - fmt.Sprintf("--genesis-state=%s/genesis.ssz", GENERATED_DATA_DIR_INSIDE_CONTAINER), + fmt.Sprintf("--chain-config-file=%s/config.yaml", g.generatedDataContainerDir), + fmt.Sprintf("--genesis-state=%s/genesis.ssz", g.generatedDataContainerDir), fmt.Sprintf("--execution-endpoint=%s", g.gethInternalExecutionURL), "--rpc-host=0.0.0.0", "--grpc-gateway-host=0.0.0.0", "--grpc-gateway-corsdomain=*", "--suggested-fee-recipient=0x8943545177806ED17B9F23F0a21ee5948eCaa776", "--subscribe-all-subnets=true", - fmt.Sprintf("--jwt-secret=%s", JWT_SECRET_FILE_LOCATION_INSIDE_CONTAINER), + fmt.Sprintf("--jwt-secret=%s", getJWTSecretFileLocationInsideContainer(g.generatedDataContainerDir)), // mine, modify when running multi-node "--minimum-peers-per-subnet=0", "--min-sync-peers=0", @@ -152,7 +164,7 @@ func (g *PrysmBeaconChain) getContainerRequest(networks []string) (*tc.Container hostConfig.Mounts = append(hostConfig.Mounts, mount.Mount{ Type: mount.TypeBind, Source: g.generatedDataHostDir, - Target: GENERATED_DATA_DIR_INSIDE_CONTAINER, + Target: g.generatedDataContainerDir, ReadOnly: false, }) }, @@ -170,13 +182,17 @@ type PrysmValidator struct { chainConfig *config.EthereumChainConfig internalBeaconRpcProvider string valKeysDir string - generatedDataHostDir string l zerolog.Logger t *testing.T + posContainerSettings } -func NewPrysmValidator(networks []string, chainConfig *config.EthereumChainConfig, generatedDataHostDir, valKeysDir, internalBeaconRpcProvider string, opts ...EnvComponentOption) (*PrysmValidator, error) { - parts := strings.Split(defaultPyrsmValidatorImage, ":") +func NewPrysmValidator(networks []string, chainConfig *config.EthereumChainConfig, generatedDataHostDir, generatedDataContainerDir, valKeysDir, internalBeaconRpcProvider string, baseEthereumFork ethereum.Fork, opts ...EnvComponentOption) (*PrysmValidator, error) { + pyrsmValidatorImage, ok := validatorForkToImageMap[baseEthereumFork] + if !ok { + return nil, fmt.Errorf("unknown fork: %s", baseEthereumFork) + } + parts := strings.Split(pyrsmValidatorImage, ":") g := &PrysmValidator{ EnvComponent: EnvComponent{ ContainerName: fmt.Sprintf("%s-%s", "prysm-validator", uuid.NewString()[0:8]), @@ -185,7 +201,7 @@ func NewPrysmValidator(networks []string, chainConfig *config.EthereumChainConfi ContainerVersion: parts[1], }, chainConfig: chainConfig, - generatedDataHostDir: generatedDataHostDir, + posContainerSettings: posContainerSettings{generatedDataHostDir: generatedDataHostDir, generatedDataContainerDir: generatedDataContainerDir}, valKeysDir: valKeysDir, internalBeaconRpcProvider: internalBeaconRpcProvider, l: logging.GetTestLogger(nil), @@ -238,17 +254,17 @@ func (g *PrysmValidator) getContainerRequest(networks []string) (*tc.ContainerRe ImagePlatform: "linux/x86_64", WaitingFor: tcwait.ForAll( tcwait.ForLog("Beacon chain started"). - WithStartupTimeout(g.chainConfig.GetDefaultWaitDuration()). + WithStartupTimeout(g.chainConfig.DefaultWaitDuration()). WithPollInterval(2 * time.Second), ), Cmd: []string{ "--accept-terms-of-use", - fmt.Sprintf("--chain-config-file=%s/config.yaml", GENERATED_DATA_DIR_INSIDE_CONTAINER), + fmt.Sprintf("--chain-config-file=%s/config.yaml", g.generatedDataContainerDir), fmt.Sprintf("--beacon-rpc-provider=%s", g.internalBeaconRpcProvider), "--datadir=/consensus-data", "--suggested-fee-recipient=0x8943545177806ED17B9F23F0a21ee5948eCaa776", fmt.Sprintf("--wallet-dir=%s/prysm", NODE_0_DIR_INSIDE_CONTAINER), - fmt.Sprintf("--wallet-password-file=%s", VALIDATOR_WALLET_PASSWORD_FILE_INSIDE_CONTAINER), + fmt.Sprintf("--wallet-password-file=%s", getValidatorWalletPasswordFileInsideContainer(g.generatedDataContainerDir)), }, HostConfigModifier: func(hostConfig *container.HostConfig) { hostConfig.Mounts = append(hostConfig.Mounts, mount.Mount{ @@ -259,7 +275,7 @@ func (g *PrysmValidator) getContainerRequest(networks []string) (*tc.ContainerRe }, mount.Mount{ Type: mount.TypeBind, Source: g.generatedDataHostDir, - Target: GENERATED_DATA_DIR_INSIDE_CONTAINER, + Target: g.generatedDataContainerDir, ReadOnly: false, }) }, diff --git a/docker/test_env/reth_base.go b/docker/test_env/reth_base.go index 4570316a5..15ab87feb 100644 --- a/docker/test_env/reth_base.go +++ b/docker/test_env/reth_base.go @@ -7,6 +7,8 @@ import ( "testing" "time" + config_types "github.com/smartcontractkit/chainlink-testing-framework/config/types" + "github.com/rs/zerolog" tc "github.com/testcontainers/testcontainers-go" @@ -19,12 +21,6 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" ) -const ( - defaultRethEth2Image = "ghcr.io/paradigmxyz/reth:v1.0.0" - rethBaseImageName = "ghcr.io/paradigmxyz/reth" - rethGitRepo = "paradigmxyz/reth" -) - type Reth struct { EnvComponent ExternalHttpUrl string @@ -33,12 +29,12 @@ type Reth struct { InternalWsUrl string InternalExecutionURL string ExternalExecutionURL string - generatedDataHostDir string chainConfig *config.EthereumChainConfig consensusLayer config.ConsensusLayer - ethereumVersion config.EthereumVersion + ethereumVersion config_types.EthereumVersion l zerolog.Logger t *testing.T + posContainerSettings } func (g *Reth) WithTestInstance(t *testing.T) ExecutionClient { @@ -48,7 +44,7 @@ func (g *Reth) WithTestInstance(t *testing.T) ExecutionClient { } func (g *Reth) StartContainer() (blockchain.EVMNetwork, error) { - if g.GetEthereumVersion() == config.EthereumVersion_Eth1 { + if g.GetEthereumVersion() == config_types.EthereumVersion_Eth1 { return blockchain.EVMNetwork{}, errors.New(config.Eth1NotSupportedByRethMsg) } @@ -83,7 +79,7 @@ func (g *Reth) StartContainer() (blockchain.EVMNetwork, error) { return blockchain.EVMNetwork{}, err } - if g.GetEthereumVersion() == config.EthereumVersion_Eth2 { + if g.GetEthereumVersion() == config_types.EthereumVersion_Eth2 { executionPort, err := ct.MappedPort(testcontext.Get(g.t), NatPort(ETH2_EXECUTION_PORT)) if err != nil { return blockchain.EVMNetwork{}, err @@ -99,7 +95,7 @@ func (g *Reth) StartContainer() (blockchain.EVMNetwork, error) { g.InternalWsUrl = FormatWsUrl(g.ContainerName, DEFAULT_EVM_NODE_WS_PORT) networkConfig := blockchain.SimulatedEVMNetwork - if g.GetEthereumVersion() == config.EthereumVersion_Eth1 { + if g.GetEthereumVersion() == config_types.EthereumVersion_Eth1 { networkConfig.Name = fmt.Sprintf("Private Eth-1-PoW [reth %s]", g.ContainerVersion) } else { networkConfig.Name = fmt.Sprintf("Private Eth-2-PoS [reth %s] + %s", g.ContainerVersion, g.consensusLayer) @@ -115,14 +111,14 @@ func (g *Reth) StartContainer() (blockchain.EVMNetwork, error) { } func (g *Reth) GetInternalExecutionURL() string { - if g.GetEthereumVersion() == config.EthereumVersion_Eth1 { + if g.GetEthereumVersion() == config_types.EthereumVersion_Eth1 { panic("eth1 node doesn't have an execution URL") } return g.InternalExecutionURL } func (g *Reth) GetExternalExecutionURL() string { - if g.GetEthereumVersion() == config.EthereumVersion_Eth1 { + if g.GetEthereumVersion() == config_types.EthereumVersion_Eth1 { panic("eth1 node doesn't have an execution URL") } return g.ExternalExecutionURL @@ -152,12 +148,12 @@ func (g *Reth) GetContainer() *tc.Container { return &g.Container } -func (g *Reth) GetEthereumVersion() config.EthereumVersion { +func (g *Reth) GetEthereumVersion() config_types.EthereumVersion { return g.ethereumVersion } func (g *Reth) WaitUntilChainIsReady(ctx context.Context, waitTime time.Duration) error { - if g.GetEthereumVersion() == config.EthereumVersion_Eth1 { + if g.GetEthereumVersion() == config_types.EthereumVersion_Eth1 { return nil } waitForFirstBlock := tcwait.NewLogStrategy("Block added to canonical chain").WithPollInterval(1 * time.Second).WithStartupTimeout(waitTime) @@ -165,7 +161,7 @@ func (g *Reth) WaitUntilChainIsReady(ctx context.Context, waitTime time.Duration } func (g *Reth) GethConsensusMechanism() ConsensusMechanism { - if g.GetEthereumVersion() == config.EthereumVersion_Eth1 { + if g.GetEthereumVersion() == config_types.EthereumVersion_Eth1 { return ConsensusMechanism_PoW } return ConsensusMechanism_PoS diff --git a/docker/test_env/reth_eth2.go b/docker/test_env/reth_eth2.go index 8e9f29a66..7ed048802 100644 --- a/docker/test_env/reth_eth2.go +++ b/docker/test_env/reth_eth2.go @@ -8,6 +8,8 @@ import ( "strings" "time" + config_types "github.com/smartcontractkit/chainlink-testing-framework/config/types" + "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/mount" "github.com/google/uuid" @@ -15,13 +17,14 @@ import ( tcwait "github.com/testcontainers/testcontainers-go/wait" "github.com/smartcontractkit/chainlink-testing-framework/config" + "github.com/smartcontractkit/chainlink-testing-framework/docker/ethereum" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/mirror" ) // NewRethEth2 starts a new Reth Eth2 node running in Docker -func NewRethEth2(networks []string, chainConfig *config.EthereumChainConfig, generatedDataHostDir string, consensusLayer config.ConsensusLayer, opts ...EnvComponentOption) (*Reth, error) { - parts := strings.Split(defaultRethEth2Image, ":") +func NewRethEth2(networks []string, chainConfig *config.EthereumChainConfig, generatedDataHostDir, generatedDataContainerDir string, consensusLayer config.ConsensusLayer, opts ...EnvComponentOption) (*Reth, error) { + parts := strings.Split(ethereum.DefaultRethEth2Image, ":") g := &Reth{ EnvComponent: EnvComponent{ ContainerName: fmt.Sprintf("%s-%s", "reth-eth2", uuid.NewString()[0:8]), @@ -32,10 +35,10 @@ func NewRethEth2(networks []string, chainConfig *config.EthereumChainConfig, gen StartupTimeout: 120 * time.Second, }, chainConfig: chainConfig, - generatedDataHostDir: generatedDataHostDir, + posContainerSettings: posContainerSettings{generatedDataHostDir: generatedDataHostDir, generatedDataContainerDir: generatedDataContainerDir}, consensusLayer: consensusLayer, l: logging.GetTestLogger(nil), - ethereumVersion: config.EthereumVersion_Eth2, + ethereumVersion: config_types.EthereumVersion_Eth2, } g.SetDefaultHooks() for _, opt := range opts { @@ -94,7 +97,7 @@ func (g *Reth) getEth2ContainerRequest() (*tc.ContainerRequest, error) { hostConfig.Mounts = append(hostConfig.Mounts, mount.Mount{ Type: mount.TypeBind, Source: g.generatedDataHostDir, - Target: GENERATED_DATA_DIR_INSIDE_CONTAINER, + Target: g.generatedDataContainerDir, ReadOnly: false, }) }, @@ -152,8 +155,8 @@ func (g *Reth) buildPosInitScript() (string, error) { HttpPort: DEFAULT_EVM_NODE_HTTP_PORT, WsPort: DEFAULT_EVM_NODE_WS_PORT, ChainID: g.chainConfig.ChainID, - GeneratedDataDir: GENERATED_DATA_DIR_INSIDE_CONTAINER, - JwtFileLocation: JWT_SECRET_FILE_LOCATION_INSIDE_CONTAINER, + GeneratedDataDir: g.generatedDataContainerDir, + JwtFileLocation: getJWTSecretFileLocationInsideContainer(g.generatedDataContainerDir), ExecutionDir: "/root/.local", LogLevel: g.LogLevel, } diff --git a/docker/test_env/reth_test.go b/docker/test_env/reth_test.go index fbccd0ccc..e96ebcbaf 100644 --- a/docker/test_env/reth_test.go +++ b/docker/test_env/reth_test.go @@ -4,6 +4,8 @@ import ( "fmt" "testing" + config_types "github.com/smartcontractkit/chainlink-testing-framework/config/types" + "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" @@ -16,8 +18,8 @@ import ( func TestRethEth1(t *testing.T) { builder := NewEthereumNetworkBuilder() _, err := builder. - WithEthereumVersion(config.EthereumVersion_Eth1). - WithExecutionLayer(config.ExecutionLayer_Reth). + WithEthereumVersion(config_types.EthereumVersion_Eth1). + WithExecutionLayer(config_types.ExecutionLayer_Reth). Build() require.Error(t, err, "Builder validation failed") require.Contains(t, err.Error(), config.Eth1NotSupportedByRethMsg) @@ -28,8 +30,8 @@ func TestRethEth2(t *testing.T) { builder := NewEthereumNetworkBuilder() cfg, err := builder. - WithEthereumVersion(config.EthereumVersion_Eth2). - WithExecutionLayer(config.ExecutionLayer_Reth). + WithEthereumVersion(config_types.EthereumVersion_Eth2). + WithExecutionLayer(config_types.ExecutionLayer_Reth). Build() require.NoError(t, err, "Builder validation failed") diff --git a/docker/test_env/utils.go b/docker/test_env/utils.go index 713aad76f..c646c9ba4 100644 --- a/docker/test_env/utils.go +++ b/docker/test_env/utils.go @@ -3,14 +3,10 @@ package test_env import ( "context" "fmt" - "regexp" - "strconv" "strings" "github.com/docker/go-connections/nat" tc "github.com/testcontainers/testcontainers-go" - - "github.com/smartcontractkit/chainlink-testing-framework/config" ) func NatPortFormat(port string) string { @@ -53,115 +49,6 @@ func FormatWsUrl(host string, port string) string { return fmt.Sprintf("ws://%s:%s", host, port) } -// GetEthereumVersionFromImage returns the consensus type based on the Docker image version -func GetEthereumVersionFromImage(executionLayer config.ExecutionLayer, imageWithVersion string) (config.EthereumVersion, error) { - version, err := GetComparableVersionFromDockerImage(imageWithVersion) - if err != nil { - return "", fmt.Errorf("failed to parse docker image and extract version: %s", imageWithVersion) - } - switch executionLayer { - case config.ExecutionLayer_Geth: - if version < 113 { - return config.EthereumVersion_Eth1, nil - } else { - return config.EthereumVersion_Eth2, nil - } - case config.ExecutionLayer_Besu: - if version < 231 { - return config.EthereumVersion_Eth1, nil - } else { - return config.EthereumVersion_Eth2, nil - } - case config.ExecutionLayer_Erigon: - if version < 241 { - return config.EthereumVersion_Eth1, nil - } else { - return config.EthereumVersion_Eth2, nil - } - case config.ExecutionLayer_Nethermind: - if version < 117 { - return config.EthereumVersion_Eth1, nil - } else { - return config.EthereumVersion_Eth2, nil - } - case config.ExecutionLayer_Reth: - return config.EthereumVersion_Eth2, nil - } - - return "", fmt.Errorf(MsgUnsupportedExecutionLayer, executionLayer) -} - -// GetComparableVersionFromDockerImage returns version in xy format removing all non-numeric characters -// and patch version if present. So x.y.z becomes xy. -func GetComparableVersionFromDockerImage(imageWithVersion string) (int, error) { - parts := strings.Split(imageWithVersion, ":") - if len(parts) != 2 { - return -1, fmt.Errorf(MsgInvalidDockerImageFormat, imageWithVersion) - } - - re := regexp.MustCompile("[a-zA-Z]") - cleanedVersion := re.ReplaceAllString(parts[1], "") - if idx := strings.Index(cleanedVersion, "-"); idx != -1 { - cleanedVersion = string(cleanedVersion[:idx]) - } - // remove patch version if present - if count := strings.Count(cleanedVersion, "."); count > 1 { - cleanedVersion = string(cleanedVersion[:strings.LastIndex(cleanedVersion, ".")]) - } - version, err := strconv.Atoi(strings.Replace(cleanedVersion, ".", "", -1)) - if err != nil { - return -1, fmt.Errorf("failed to parse docker version to an integer: %s", cleanedVersion) - } - - return version, nil -} - -// GetGithubRepositoryFromEthereumClientDockerImage returns the GitHub repository name based on the Docker image -func GetGithubRepositoryFromEthereumClientDockerImage(imageWithVersion string) (string, error) { - parts := strings.Split(imageWithVersion, ":") - if len(parts) != 2 { - return "", fmt.Errorf(MsgInvalidDockerImageFormat, imageWithVersion) - } - - switch { - case strings.Contains(parts[0], gethBaseImageName): - return gethGitRepo, nil - case strings.Contains(parts[0], besuBaseImageName): - return besuGitRepo, nil - case strings.Contains(parts[0], nethermindBaseImageName): - return nethermindGitRepo, nil - case strings.Contains(parts[0], erigonBaseImageName): - return erigonGitRepo, nil - case strings.Contains(parts[0], rethBaseImageName): - return rethGitRepo, nil - default: - return "", fmt.Errorf(MsgUnsupportedDockerImage, parts[0]) - } -} - -// GetExecutionLayerFromDockerImage returns the execution layer based on the Docker image -func GetExecutionLayerFromDockerImage(imageWithVersion string) (config.ExecutionLayer, error) { - parts := strings.Split(imageWithVersion, ":") - if len(parts) != 2 { - return "", fmt.Errorf(MsgInvalidDockerImageFormat, imageWithVersion) - } - - switch { - case strings.Contains(parts[0], gethBaseImageName): - return config.ExecutionLayer_Geth, nil - case strings.Contains(parts[0], besuBaseImageName): - return config.ExecutionLayer_Besu, nil - case strings.Contains(parts[0], nethermindBaseImageName): - return config.ExecutionLayer_Nethermind, nil - case strings.Contains(parts[0], erigonBaseImageName): - return config.ExecutionLayer_Erigon, nil - case strings.Contains(parts[0], rethBaseImageName): - return config.ExecutionLayer_Reth, nil - default: - return "", fmt.Errorf(MsgUnsupportedDockerImage, parts[0]) - } -} - // UniqueStringSlice returns a deduplicated slice of strings func UniqueStringSlice(slice []string) []string { stringSet := make(map[string]struct{}) diff --git a/go.mod b/go.mod index 645c1dbe1..39693844c 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.22.5 require ( dario.cat/mergo v1.0.0 + github.com/Masterminds/semver/v3 v3.2.1 github.com/avast/retry-go v3.0.0+incompatible github.com/aws/constructs-go/constructs/v10 v10.1.255 github.com/aws/jsii-runtime-go v1.75.0 @@ -59,7 +60,6 @@ require ( github.com/K-Phoen/sdk v0.12.2 // indirect github.com/MakeNowJust/heredoc v1.0.0 // indirect github.com/Masterminds/goutils v1.1.1 // indirect - github.com/Masterminds/semver/v3 v3.2.1 // indirect github.com/Masterminds/sprig/v3 v3.2.3 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect github.com/Microsoft/hcsshim v0.11.4 // indirect @@ -286,7 +286,7 @@ require ( golang.org/x/arch v0.4.0 // indirect golang.org/x/crypto v0.17.0 // indirect golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect - golang.org/x/mod v0.14.0 // indirect + golang.org/x/mod v0.19.0 // indirect golang.org/x/sys v0.16.0 // indirect golang.org/x/term v0.15.0 // indirect golang.org/x/time v0.3.0 // indirect diff --git a/go.sum b/go.sum index bc225bbf1..6f326b2fe 100644 --- a/go.sum +++ b/go.sum @@ -1241,8 +1241,8 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= -golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= +golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= diff --git a/utils/docker/docker.go b/utils/docker/docker.go new file mode 100644 index 000000000..971f54744 --- /dev/null +++ b/utils/docker/docker.go @@ -0,0 +1,25 @@ +package docker + +import ( + "fmt" + "strings" + + "github.com/Masterminds/semver/v3" +) + +const MsgInvalidDockerImageFormat = "invalid docker image format: %s" + +// GetSemverFromImage returns a semver version from a docker image string +func GetSemverFromImage(imageWithVersion string) (*semver.Version, error) { + parts := strings.Split(imageWithVersion, ":") + if len(parts) != 2 { + return nil, fmt.Errorf(MsgInvalidDockerImageFormat, imageWithVersion) + } + + parsedVersion, err := semver.NewVersion(parts[1]) + if err != nil { + return nil, fmt.Errorf("failed to parse docker version to a semver: %s", parts[1]) + } + + return parsedVersion, nil +}