github.com/smartcontractkit/chainlink-testing-framework/libs@v0.0.0-20240227141906-ec710b4eb1a3/docker/test_env/eth2_common.go (about)

     1  package test_env
     2  
     3  import (
     4  	"context"
     5  	_ "embed"
     6  	"errors"
     7  	"fmt"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/ethereum/go-ethereum/common"
    12  	"github.com/pelletier/go-toml/v2"
    13  	"github.com/rs/zerolog"
    14  	tc "github.com/testcontainers/testcontainers-go"
    15  
    16  	"github.com/smartcontractkit/chainlink-testing-framework/libs/blockchain"
    17  )
    18  
    19  var (
    20  	ETH2_EXECUTION_PORT                             = "8551"
    21  	WALLET_PASSWORD                                 = "password"
    22  	VALIDATOR_WALLET_PASSWORD_FILE_INSIDE_CONTAINER = fmt.Sprintf("%s/wallet_password.txt", GENERATED_DATA_DIR_INSIDE_CONTAINER)
    23  	ACCOUNT_PASSWORD_FILE_INSIDE_CONTAINER          = fmt.Sprintf("%s/account_password.txt", GENERATED_DATA_DIR_INSIDE_CONTAINER)
    24  	ACCOUNT_KEYSTORE_FILE_INSIDE_CONTAINER          = fmt.Sprintf("%s/account_key", KEYSTORE_DIR_LOCATION_INSIDE_CONTAINER)
    25  	KEYSTORE_DIR_LOCATION_INSIDE_CONTAINER          = fmt.Sprintf("%s/keystore", GENERATED_DATA_DIR_INSIDE_CONTAINER)
    26  	GENERATED_VALIDATOR_KEYS_DIR_INSIDE_CONTAINER   = "/keys"
    27  	NODE_0_DIR_INSIDE_CONTAINER                     = fmt.Sprintf("%s/node-0", GENERATED_VALIDATOR_KEYS_DIR_INSIDE_CONTAINER)
    28  	GENERATED_DATA_DIR_INSIDE_CONTAINER             = "/data/custom_config_data"
    29  	JWT_SECRET_FILE_LOCATION_INSIDE_CONTAINER       = fmt.Sprintf("%s/jwtsecret", GENERATED_DATA_DIR_INSIDE_CONTAINER) // #nosec G101
    30  	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"
    31  )
    32  
    33  type EthereumChainConfig struct {
    34  	SecondsPerSlot   int      `json:"seconds_per_slot" toml:"seconds_per_slot"`
    35  	SlotsPerEpoch    int      `json:"slots_per_epoch" toml:"slots_per_epoch"`
    36  	GenesisDelay     int      `json:"genesis_delay" toml:"genesis_delay"`
    37  	ValidatorCount   int      `json:"validator_count" toml:"validator_count"`
    38  	ChainID          int      `json:"chain_id" toml:"chain_id"`
    39  	genesisTimestamp int      // this is not serialized
    40  	AddressesToFund  []string `json:"addresses_to_fund" toml:"addresses_to_fund"`
    41  }
    42  
    43  //go:embed example/default_ethereum_env.toml
    44  var defaultEthereumChainConfig []byte
    45  
    46  func (c *EthereumChainConfig) Default() error {
    47  	wrapper := struct {
    48  		EthereumNetwork *EthereumNetwork `toml:"PrivateEthereumNetwork"`
    49  	}{}
    50  	if err := toml.Unmarshal(defaultEthereumChainConfig, &wrapper); err != nil {
    51  		return fmt.Errorf("error unmarshaling ethereum network config: %w", err)
    52  	}
    53  
    54  	if wrapper.EthereumNetwork == nil {
    55  		return errors.New("[EthereumNetwork] was not present in default TOML file")
    56  	}
    57  
    58  	*c = *wrapper.EthereumNetwork.EthereumChainConfig
    59  
    60  	if c.genesisTimestamp == 0 {
    61  		c.GenerateGenesisTimestamp()
    62  	}
    63  
    64  	return nil
    65  }
    66  
    67  func GetDefaultChainConfig() EthereumChainConfig {
    68  	config := EthereumChainConfig{}
    69  	if err := config.Default(); err != nil {
    70  		panic(err)
    71  	}
    72  	return config
    73  }
    74  
    75  func (c *EthereumChainConfig) Validate(logger zerolog.Logger, consensusType ConsensusType) error {
    76  	if c.ChainID < 1 {
    77  		return fmt.Errorf("chain id must be >= 0")
    78  	}
    79  
    80  	if consensusType == ConsensusType_PoW {
    81  		return nil
    82  	}
    83  
    84  	if c.ValidatorCount < 4 {
    85  		return fmt.Errorf("validator count must be >= 4")
    86  	}
    87  	if c.SecondsPerSlot < 3 {
    88  		return fmt.Errorf("seconds per slot must be >= 3")
    89  	}
    90  	if c.SlotsPerEpoch < 2 {
    91  		return fmt.Errorf("slots per epoch must be >= 1")
    92  	}
    93  	if c.GenesisDelay < 10 {
    94  		return fmt.Errorf("genesis delay must be >= 10")
    95  	}
    96  	if c.genesisTimestamp == 0 {
    97  		return fmt.Errorf("genesis timestamp must be generated by calling GenerateGenesisTimestamp()")
    98  	}
    99  
   100  	addressSet := make(map[string]struct{})
   101  	deduplicated := make([]string, 0)
   102  
   103  	for _, addr := range c.AddressesToFund {
   104  		if !common.IsHexAddress(addr) {
   105  			return fmt.Errorf("address %s is not a valid hex address", addr)
   106  		}
   107  
   108  		if _, exists := addressSet[addr]; exists {
   109  			logger.Warn().Str("address", addr).Msg("duplicate address in addresses to fund, this should not happen, removing it so that genesis generation doesn't crash")
   110  			continue
   111  		}
   112  
   113  		addressSet[addr] = struct{}{}
   114  		deduplicated = append(deduplicated, addr)
   115  	}
   116  
   117  	c.AddressesToFund = deduplicated
   118  
   119  	return nil
   120  }
   121  
   122  func (c *EthereumChainConfig) ApplyOverrides(from *EthereumChainConfig) error {
   123  	if from == nil {
   124  		return nil
   125  	}
   126  	if from.ValidatorCount != 0 {
   127  		c.ValidatorCount = from.ValidatorCount
   128  	}
   129  	if from.SecondsPerSlot != 0 {
   130  		c.SecondsPerSlot = from.SecondsPerSlot
   131  	}
   132  	if from.SlotsPerEpoch != 0 {
   133  		c.SlotsPerEpoch = from.SlotsPerEpoch
   134  	}
   135  	if from.GenesisDelay != 0 {
   136  		c.GenesisDelay = from.GenesisDelay
   137  	}
   138  	if from.ChainID != 0 {
   139  		c.ChainID = from.ChainID
   140  	}
   141  	if len(from.AddressesToFund) != 0 {
   142  		c.AddressesToFund = append([]string{}, from.AddressesToFund...)
   143  	}
   144  	return nil
   145  }
   146  
   147  func (c *EthereumChainConfig) fillInMissingValuesWithDefault() {
   148  	defaultConfig := GetDefaultChainConfig()
   149  	if c.ValidatorCount == 0 {
   150  		c.ValidatorCount = defaultConfig.ValidatorCount
   151  	}
   152  	if c.SecondsPerSlot == 0 {
   153  		c.SecondsPerSlot = defaultConfig.SecondsPerSlot
   154  	}
   155  	if c.SlotsPerEpoch == 0 {
   156  		c.SlotsPerEpoch = defaultConfig.SlotsPerEpoch
   157  	}
   158  	if c.GenesisDelay == 0 {
   159  		c.GenesisDelay = defaultConfig.GenesisDelay
   160  	}
   161  	if c.ChainID == 0 {
   162  		c.ChainID = defaultConfig.ChainID
   163  	}
   164  	if len(c.AddressesToFund) == 0 {
   165  		c.AddressesToFund = append([]string{}, defaultConfig.AddressesToFund...)
   166  	} else {
   167  		c.AddressesToFund = append(append([]string{}, c.AddressesToFund...), defaultConfig.AddressesToFund...)
   168  	}
   169  }
   170  
   171  func (c *EthereumChainConfig) GetValidatorBasedGenesisDelay() int {
   172  	return c.ValidatorCount * 5
   173  }
   174  
   175  func (c *EthereumChainConfig) GenerateGenesisTimestamp() {
   176  	c.genesisTimestamp = int(time.Now().Unix()) + c.GetValidatorBasedGenesisDelay()
   177  }
   178  
   179  func (c *EthereumChainConfig) GetDefaultWaitDuration() time.Duration {
   180  	return time.Duration((c.GenesisDelay+c.GetValidatorBasedGenesisDelay())*2) * time.Second
   181  }
   182  
   183  func (c *EthereumChainConfig) GetDefaultFinalizationWaitDuration() time.Duration {
   184  	return time.Duration(5 * time.Minute)
   185  }
   186  
   187  type ExecutionClient interface {
   188  	GetContainerName() string
   189  	StartContainer() (blockchain.EVMNetwork, error)
   190  	GetContainer() *tc.Container
   191  	GetContainerType() ContainerType
   192  	GetInternalExecutionURL() string
   193  	GetExternalExecutionURL() string
   194  	GetInternalHttpUrl() string
   195  	GetInternalWsUrl() string
   196  	GetExternalHttpUrl() string
   197  	GetExternalWsUrl() string
   198  	WaitUntilChainIsReady(ctx context.Context, waitTime time.Duration) error
   199  	WithTestInstance(t *testing.T) ExecutionClient
   200  }