Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TT-849] Move common test config surface to CTF #929

Merged
merged 22 commits into from
May 6, 2024
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
b386fc5
enable debug and trace apis for nethermind
Tofel Apr 25, 2024
85d2305
add an universal concurrent executor that divides tasks equally betwe…
Tofel Apr 25, 2024
4944142
Merge branch 'main' of github.com:smartcontractkit/chainlink-testing-…
Tofel Apr 25, 2024
87140ce
Merge branch 'main' into tt_922_automation_seth
Tofel Apr 25, 2024
5d29248
add missing constants
Tofel Apr 25, 2024
3ae9c36
move common seth methods from core to ctf
Tofel Apr 25, 2024
7d48e60
Merge branch 'main' into tt_922_automation_seth
Tofel Apr 25, 2024
064a32d
Merge branch 'main' into tt_922_automation_seth
Tofel Apr 25, 2024
e93a6c4
restructur ethereum network config
Tofel Apr 26, 2024
b3fb0a7
add tests for DivideSlice, convert examples into tests, support for p…
Tofel Apr 26, 2024
b627421
Merge branch 'tt_922_automation_seth' of github.com:smartcontractkit/…
Tofel Apr 26, 2024
904b8dd
remove duplicated const
Tofel Apr 26, 2024
d9bfeb8
fix lint
Tofel Apr 26, 2024
7d42cf5
Merge branch 'tt_922_automation_seth' into tt_922_automation_seth_com…
Tofel Apr 26, 2024
aa2dd83
Merge branch 'main' into tt_922_automation_seth_common_methods
Tofel Apr 30, 2024
ff0457a
add test, remove comments
Tofel Apr 30, 2024
69318fb
move Describe() to where it belongs
Tofel Apr 30, 2024
ce06269
fix eth2 genesis template generation
Tofel Apr 30, 2024
0fbc0df
Merge branch 'main' into tt_922_automation_seth_common_methods
Tofel Apr 30, 2024
e8d07c5
fix typos
Tofel May 6, 2024
1de34a4
add comments
Tofel May 6, 2024
8af312f
Merge branch 'main' into tt_922_automation_seth_common_methods
Tofel May 6, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
360 changes: 360 additions & 0 deletions config/private_ethereum_network.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,360 @@
package config

import (
_ "embed"
"errors"
"fmt"
"time"

"github.com/pelletier/go-toml/v2"
"github.com/rs/zerolog"
tc "github.com/testcontainers/testcontainers-go"

"github.com/smartcontractkit/chainlink-testing-framework/logging"
"github.com/smartcontractkit/chainlink-testing-framework/utils/slice"
)

var (
ErrMissingEthereumVersion = errors.New("ethereum version is required")
ErrMissingExecutionLayer = errors.New("execution layer is required")
)

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"`
}

func (en *EthereumNetworkConfig) Validate() error {
l := logging.GetTestLogger(nil)

// logically it doesn't belong here, but placing it here guarantees it will always run without changing API
if en.EthereumVersion != nil && en.ConsensusType != nil {
l.Warn().Msg("Both EthereumVersion and ConsensusType are set. ConsensusType as a _deprecated_ field will be ignored")
}

if en.EthereumVersion == nil && en.ConsensusType != nil {
l.Debug().Msg("Using _deprecated_ ConsensusType as EthereumVersion")
tempEthVersion := (*EthereumVersion)(en.ConsensusType)
switch *tempEthVersion {
case EthereumVersion_Eth1, EthereumVersion_Eth1_Legacy:
*tempEthVersion = EthereumVersion_Eth1
case EthereumVersion_Eth2, EthereumVersion_Eth2_Legacy:
*tempEthVersion = EthereumVersion_Eth2
default:
return fmt.Errorf("unknown ethereum version (consensus type): %s", *en.ConsensusType)
}

en.EthereumVersion = tempEthVersion
}

if (en.EthereumVersion == nil || *en.EthereumVersion == "") && len(en.CustomDockerImages) == 0 {
return ErrMissingEthereumVersion
}

if (en.ExecutionLayer == nil || *en.ExecutionLayer == "") && len(en.CustomDockerImages) == 0 {
return ErrMissingExecutionLayer
}

if (en.EthereumVersion != nil && (*en.EthereumVersion == EthereumVersion_Eth2_Legacy || *en.EthereumVersion == 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 != "") {
l.Warn().Msg("Consensus layer is set, but is not allowed for PoW. Ignoring")
en.ConsensusLayer = nil
}

if en.EthereumChainConfig == nil {
return errors.New("ethereum chain config is required")
}

return en.EthereumChainConfig.Validate(l, en.EthereumVersion)
}

func (en *EthereumNetworkConfig) ApplyOverrides(from *EthereumNetworkConfig) error {
if from == nil {
return nil
}
if from.ConsensusLayer != nil {
en.ConsensusLayer = from.ConsensusLayer
}
if from.ExecutionLayer != nil {
en.ExecutionLayer = from.ExecutionLayer
}
if from.EthereumVersion != nil {
en.EthereumVersion = from.EthereumVersion
}
if from.WaitForFinalization != nil {
en.WaitForFinalization = from.WaitForFinalization
}

if from.EthereumChainConfig != nil {
if en.EthereumChainConfig == nil {
en.EthereumChainConfig = from.EthereumChainConfig
} else {
err := en.EthereumChainConfig.ApplyOverrides(from.EthereumChainConfig)
if err != nil {
return fmt.Errorf("error applying overrides from network config file to config: %w", err)
}
}
}

return nil
}

func (en *EthereumNetworkConfig) Describe() string {
cL := "prysm"
if en.ConsensusLayer == nil {
cL = "(none)"
}
return fmt.Sprintf("ethereum version: %s, execution layer: %s, consensus layer: %s", *en.EthereumVersion, *en.ExecutionLayer, cL)
}

type EthereumNetworkContainer struct {
ContainerName string `toml:"container_name"`
ContainerType ContainerType `toml:"container_type"`
Container *tc.Container `toml:"-"`
}

// Deprecated: use EthereumVersion instead
type ConsensusType string

const (
// Deprecated: use EthereumVersion_Eth2 instead
ConsensusType_PoS ConsensusType = "pos"
// Deprecated: use EthereumVersion_Eth1 instead
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"
)

type ConsensusLayer string

var ConsensusLayer_Prysm ConsensusLayer = "prysm"

type EthereumNetworkContainers []EthereumNetworkContainer

type ContainerType string

const (
ContainerType_ExecutionLayer ContainerType = "execution_layer"
ContainerType_ConsensusLayer ContainerType = "consensus_layer"
ContainerType_ConsensusValidator ContainerType = "consensus_validator"
ContainerType_GenesisGenerator ContainerType = "genesis_generator"
ContainerType_ValKeysGenerator ContainerType = "val_keys_generator"
)

const (
UnsopportedForkErr = "only Deneb hard fork is supported"
)

type EthereumChainConfig struct {
SecondsPerSlot int `json:"seconds_per_slot" toml:"seconds_per_slot"`
SlotsPerEpoch int `json:"slots_per_epoch" toml:"slots_per_epoch"`
GenesisDelay int `json:"genesis_delay" toml:"genesis_delay"`
ValidatorCount int `json:"validator_count" toml:"validator_count"`
ChainID int `json:"chain_id" toml:"chain_id"`
GenesisTimestamp int // this is not serialized
AddressesToFund []string `json:"addresses_to_fund" toml:"addresses_to_fund"`
HardForkEpochs map[string]int `json:"HardForkEpochs" toml:"HardForkEpochs"`
}

//go:embed tomls/default_ethereum_env.toml
var defaultEthereumChainConfig []byte

func (c *EthereumChainConfig) Default() error {
wrapper := struct {
EthereumNetwork *EthereumNetworkConfig `toml:"PrivateEthereumNetwork"`
}{}
if err := toml.Unmarshal(defaultEthereumChainConfig, &wrapper); err != nil {
return fmt.Errorf("error unmarshalling ethereum network config: %w", err)
}

if wrapper.EthereumNetwork == nil {
return errors.New("[EthereumNetwork] was not present in default TOML file")
}

*c = *wrapper.EthereumNetwork.EthereumChainConfig

if c.GenesisTimestamp == 0 {
c.GenerateGenesisTimestamp()
}

return nil
}

func GetDefaultChainConfig() EthereumChainConfig {
config := EthereumChainConfig{}
if err := config.Default(); err != nil {
panic(err)
}
return config
}

func (c *EthereumChainConfig) Validate(logger zerolog.Logger, ethereumVersion *EthereumVersion) 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) {
return nil
}

if c.ValidatorCount < 4 {
return fmt.Errorf("validator count must be >= 4")
}
if c.SecondsPerSlot < 3 {
return fmt.Errorf("seconds per slot must be >= 3")
}
if c.SlotsPerEpoch < 2 {
return fmt.Errorf("slots per epoch must be >= 1")
}
if c.GenesisDelay < 10 {
return fmt.Errorf("genesis delay must be >= 10")
}
if c.GenesisTimestamp == 0 {
return fmt.Errorf("genesis timestamp must be generated by calling GenerateGenesisTimestamp()")
}

if err := c.ValidateHardForks(); err != nil {
return err
}

var err error
var hadDuplicates bool
c.AddressesToFund, hadDuplicates, err = slice.ValidateAndDeduplicateAddresses(c.AddressesToFund)
if err != nil {
return err
}
if hadDuplicates {
logger.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 {
return nil
}

switch len(c.HardForkEpochs) {
case 0:
return nil
case 1:
for k := range c.HardForkEpochs {
if k != "Deneb" {
return errors.New(UnsopportedForkErr)
}
}
default:
return errors.New(UnsopportedForkErr)
}

for fork, epoch := range c.HardForkEpochs {
if epoch < 1 {
return fmt.Errorf("hard fork %s epoch must be >= 1", fork)
}
}

return nil
}

func (c *EthereumChainConfig) ApplyOverrides(from *EthereumChainConfig) error {
if from == nil {
return nil
}
if from.ValidatorCount != 0 {
c.ValidatorCount = from.ValidatorCount
}
if from.SecondsPerSlot != 0 {
c.SecondsPerSlot = from.SecondsPerSlot
}
if from.SlotsPerEpoch != 0 {
c.SlotsPerEpoch = from.SlotsPerEpoch
}
if from.GenesisDelay != 0 {
c.GenesisDelay = from.GenesisDelay
}
if from.ChainID != 0 {
c.ChainID = from.ChainID
}
if len(from.AddressesToFund) != 0 {
c.AddressesToFund = append([]string{}, from.AddressesToFund...)
}
return nil
}

func (c *EthereumChainConfig) FillInMissingValuesWithDefault() {
defaultConfig := GetDefaultChainConfig()
if c.ValidatorCount == 0 {
c.ValidatorCount = defaultConfig.ValidatorCount
}
if c.SecondsPerSlot == 0 {
c.SecondsPerSlot = defaultConfig.SecondsPerSlot
}
if c.SlotsPerEpoch == 0 {
c.SlotsPerEpoch = defaultConfig.SlotsPerEpoch
}
if c.GenesisDelay == 0 {
c.GenesisDelay = defaultConfig.GenesisDelay
}
if c.ChainID == 0 {
c.ChainID = defaultConfig.ChainID
}
if len(c.AddressesToFund) == 0 {
c.AddressesToFund = append([]string{}, defaultConfig.AddressesToFund...)
} else {
c.AddressesToFund = append(append([]string{}, c.AddressesToFund...), defaultConfig.AddressesToFund...)
}

if len(c.HardForkEpochs) == 0 {
c.HardForkEpochs = defaultConfig.HardForkEpochs
}
}

func (c *EthereumChainConfig) GetValidatorBasedGenesisDelay() int {
return c.ValidatorCount * 5
}

func (c *EthereumChainConfig) GenerateGenesisTimestamp() {
c.GenesisTimestamp = int(time.Now().Unix()) + c.GetValidatorBasedGenesisDelay()
}

func (c *EthereumChainConfig) GetDefaultWaitDuration() time.Duration {
return time.Duration((c.GenesisDelay+c.GetValidatorBasedGenesisDelay())*2) * time.Second
}

func (c *EthereumChainConfig) GetDefaultFinalizationWaitDuration() time.Duration {
return time.Duration(5 * time.Minute)
}
36 changes: 36 additions & 0 deletions config/testconfig.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package config

import (
"github.com/smartcontractkit/seth"
)

func (c *TestConfig) GetLoggingConfig() *LoggingConfig {
return c.Logging
}

func (c TestConfig) GetNetworkConfig() *NetworkConfig {
return c.Network
}

func (c TestConfig) GetChainlinkImageConfig() *ChainlinkImageConfig {
return c.ChainlinkImage
}

func (c TestConfig) GetPrivateEthereumNetworkConfig() *EthereumNetworkConfig {
return c.PrivateEthereumNetwork
}

func (c TestConfig) GetPyroscopeConfig() *PyroscopeConfig {
return c.Pyroscope
}

type TestConfig struct {
ChainlinkImage *ChainlinkImageConfig `toml:"ChainlinkImage"`
ChainlinkUpgradeImage *ChainlinkImageConfig `toml:"ChainlinkUpgradeImage"`
Logging *LoggingConfig `toml:"Logging"`
Network *NetworkConfig `toml:"Network"`
Pyroscope *PyroscopeConfig `toml:"Pyroscope"`
PrivateEthereumNetwork *EthereumNetworkConfig `toml:"PrivateEthereumNetwork"`
WaspConfig *WaspAutoBuildConfig `toml:"WaspAutoBuild"`
Seth *seth.Config `toml:"Seth"`
}