Skip to content

Commit

Permalink
[TT-849] Move common test config surface to CTF (#929)
Browse files Browse the repository at this point in the history
  • Loading branch information
Tofel committed May 6, 2024
1 parent 880ce17 commit 1e74610
Show file tree
Hide file tree
Showing 37 changed files with 862 additions and 671 deletions.
361 changes: 361 additions & 0 deletions config/private_ethereum_network.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,361 @@
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
// we need to deduplicate addresses to fund, because if present they will crash the genesis
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"`
}
File renamed without changes.

0 comments on commit 1e74610

Please sign in to comment.