diff --git a/cmd/createcluster.go b/cmd/createcluster.go index 6f31ed96a..68c18b27d 100644 --- a/cmd/createcluster.go +++ b/cmd/createcluster.go @@ -111,6 +111,7 @@ func newCreateClusterCmd(runFunc func(context.Context, io.Writer, clusterConfig) bindClusterFlags(cmd.Flags(), &conf) bindInsecureFlags(cmd.Flags(), &conf.InsecureKeys) + bindNetworkFlags(cmd.Flags(), &conf.Network, &conf.testnetConfig) wrapPreRunE(cmd, func(cmd *cobra.Command, _ []string) error { thresholdPresent := cmd.Flags().Lookup("threshold").Changed @@ -142,16 +143,11 @@ func bindClusterFlags(flags *pflag.FlagSet, config *clusterConfig) { flags.IntVar(&config.Threshold, "threshold", 0, "Optional override of threshold required for signature reconstruction. Defaults to ceil(n*2/3) if zero. Warning, non-default values decrease security.") flags.StringSliceVar(&config.FeeRecipientAddrs, "fee-recipient-addresses", nil, "Comma separated list of Ethereum addresses of the fee recipient for each validator. Either provide a single fee recipient address or fee recipient addresses for each validator.") flags.StringSliceVar(&config.WithdrawalAddrs, "withdrawal-addresses", nil, "Comma separated list of Ethereum addresses to receive the returned stake and accrued rewards for each validator. Either provide a single withdrawal address or withdrawal addresses for each validator.") - flags.StringVar(&config.Network, "network", "", "Ethereum network to create validators for. Options: mainnet, goerli, sepolia, hoodi, holesky, gnosis, chiado.") flags.IntVar(&config.NumDVs, "num-validators", 0, "The number of distributed validators needed in the cluster.") flags.BoolVar(&config.SplitKeys, "split-existing-keys", false, "Split an existing validator's private key into a set of distributed validator private key shares. Does not re-create deposit data for this key.") flags.StringVar(&config.SplitKeysDir, "split-keys-dir", "", "Directory containing keys to split. Expects keys in keystore-*.json and passwords in keystore-*.txt. Requires --split-existing-keys.") flags.StringVar(&config.PublishAddr, "publish-address", "https://api.obol.tech/v1", "The URL to publish the lock file to.") flags.BoolVar(&config.Publish, "publish", false, "Publish lock file to obol-api.") - flags.StringVar(&config.testnetConfig.Name, "testnet-name", "", "Name of the custom test network.") - flags.StringVar(&config.testnetConfig.GenesisForkVersionHex, "testnet-fork-version", "", "Genesis fork version of the custom test network (in hex).") - flags.Uint64Var(&config.testnetConfig.ChainID, "testnet-chain-id", 0, "Chain ID of the custom test network.") - flags.Int64Var(&config.testnetConfig.GenesisTimestamp, "testnet-genesis-timestamp", 0, "Genesis timestamp of the custom test network.") flags.IntSliceVar(&config.DepositAmounts, "deposit-amounts", nil, "List of partial deposit amounts (integers) in ETH. Values must sum up to at least 32ETH.") flags.StringVar(&config.ConsensusProtocol, "consensus-protocol", "", "Preferred consensus protocol name for the cluster. Selected automatically when not specified.") flags.UintVar(&config.TargetGasLimit, "target-gas-limit", 60000000, "Preferred target gas limit for transactions.") @@ -160,6 +156,14 @@ func bindClusterFlags(flags *pflag.FlagSet, config *clusterConfig) { flags.BoolVar(&config.Zipped, "zipped", false, "Create a tar archive compressed with gzip of the cluster directory after creation.") } +func bindNetworkFlags(flags *pflag.FlagSet, network *string, testnetConfig *eth2util.Network) { + flags.StringVar(network, "network", "mainnet", "Ethereum network to create validators for. Options: mainnet, goerli, sepolia, hoodi, holesky, gnosis, chiado.") + flags.StringVar(&testnetConfig.Name, "testnet-name", "", "Name of the custom test network.") + flags.StringVar(&testnetConfig.GenesisForkVersionHex, "testnet-fork-version", "", "Genesis fork version of the custom test network (in hex).") + flags.Uint64Var(&testnetConfig.ChainID, "testnet-chain-id", 0, "Chain ID of the custom test network.") + flags.Int64Var(&testnetConfig.GenesisTimestamp, "testnet-genesis-timestamp", 0, "Genesis timestamp of the custom test network.") +} + func bindInsecureFlags(flags *pflag.FlagSet, insecureKeys *bool) { flags.BoolVar(insecureKeys, "insecure-keys", false, "Generates insecure keystore files. This should never be used. It is not supported on mainnet.") } @@ -377,7 +381,7 @@ func validateCreateConfig(ctx context.Context, conf clusterConfig) error { } // Check for valid network configuration. - if err := validateNetworkConfig(conf); err != nil { + if err := validateNetworkConfig(conf.Network, conf.testnetConfig); err != nil { return errors.Wrap(err, "get network config") } @@ -1219,19 +1223,19 @@ func builderRegistrationFromETH2(reg core.VersionedSignedValidatorRegistration) } // validateNetworkConfig returns an error if the network configuration is invalid in the given cluster configuration. -func validateNetworkConfig(conf clusterConfig) error { - if conf.Network != "" { - if eth2util.ValidNetwork(conf.Network) { +func validateNetworkConfig(network string, testnet eth2util.Network) error { + if network != "" { + if eth2util.ValidNetwork(network) { return nil } - return errors.New("invalid network specified", z.Str("network", conf.Network)) + return errors.New("invalid network specified", z.Str("network", network)) } // Check if custom testnet configuration is provided. - if conf.testnetConfig.IsNonZero() { + if testnet.IsNonZero() { // Add testnet config to supported networks. - eth2util.AddTestNetwork(conf.testnetConfig) + eth2util.AddTestNetwork(testnet) return nil } diff --git a/cmd/createdkg.go b/cmd/createdkg.go index 56d9b6105..e03e1b255 100644 --- a/cmd/createdkg.go +++ b/cmd/createdkg.go @@ -45,6 +45,8 @@ type createDKGConfig struct { Publish bool PublishAddress string OperatorsAddresses []string + + testnetConfig eth2util.Network } func newCreateDKGCmd(runFunc func(context.Context, createDKGConfig) error) *cobra.Command { @@ -61,6 +63,7 @@ func newCreateDKGCmd(runFunc func(context.Context, createDKGConfig) error) *cobr } bindCreateDKGFlags(cmd, &config) + bindNetworkFlags(cmd.Flags(), &config.Network, &config.testnetConfig) wrapPreRunE(cmd, func(cmd *cobra.Command, _ []string) error { thresholdPresent := cmd.Flags().Lookup("threshold").Changed @@ -99,7 +102,6 @@ func bindCreateDKGFlags(cmd *cobra.Command, config *createDKGConfig) { cmd.Flags().IntVarP(&config.Threshold, "threshold", "t", 0, "Optional override of threshold required for signature reconstruction. Defaults to ceil(n*2/3) if zero. Warning, non-default values decrease security.") cmd.Flags().StringSliceVar(&config.FeeRecipientAddrs, "fee-recipient-addresses", nil, "Comma separated list of Ethereum addresses of the fee recipient for each validator. Either provide a single fee recipient address or fee recipient addresses for each validator.") cmd.Flags().StringSliceVar(&config.WithdrawalAddrs, "withdrawal-addresses", nil, "Comma separated list of Ethereum addresses to receive the returned stake and accrued rewards for each validator. Either provide a single withdrawal address or withdrawal addresses for each validator.") - cmd.Flags().StringVar(&config.Network, "network", defaultNetwork, "Ethereum network to create validators for. Options: mainnet, goerli, sepolia, hoodi, holesky, gnosis, chiado.") cmd.Flags().StringVar(&config.DKGAlgo, "dkg-algorithm", "default", "DKG algorithm to use; default, frost or pedersen.") cmd.Flags().IntSliceVar(&config.DepositAmounts, "deposit-amounts", nil, "List of partial deposit amounts (integers) in ETH. Values must sum up to at least 32ETH.") cmd.Flags().StringSliceVar(&config.OperatorENRs, "operator-enrs", nil, "Comma-separated list of each operator's Charon ENR address.") @@ -131,7 +133,7 @@ func runCreateDKG(ctx context.Context, conf createDKGConfig) (err error) { operatorsLen = len(conf.OperatorsAddresses) } - if err = validateDKGConfig(operatorsLen, conf.Network, conf.DepositAmounts, conf.ConsensusProtocol, conf.Compounding); err != nil { + if err = validateDKGConfig(operatorsLen, conf.Network, conf.testnetConfig, conf.DepositAmounts, conf.ConsensusProtocol, conf.Compounding); err != nil { return err } @@ -187,9 +189,16 @@ func runCreateDKG(ctx context.Context, conf createDKGConfig) (err error) { log.Warn(ctx, "Non standard `--threshold` flag provided, this will affect cluster safety", nil, z.Int("threshold", conf.Threshold), z.Int("safe_threshold", safeThreshold)) } - forkVersion, err := eth2util.NetworkToForkVersion(conf.Network) - if err != nil { - return err + var forkVersion string + if conf.Network != "" { + forkVersion, err = eth2util.NetworkToForkVersion(conf.Network) + if err != nil { + return err + } + } else if conf.testnetConfig.GenesisForkVersionHex != "" { + forkVersion = conf.testnetConfig.GenesisForkVersionHex + } else { + return errors.New("network not specified, missing --network or --testnet-fork-version") } var privKey *k1.PrivateKey @@ -284,14 +293,14 @@ func validateWithdrawalAddrs(addrs []string, network string) error { } // validateDKGConfig returns an error if any of the provided config parameter is invalid. -func validateDKGConfig(numOperators int, network string, depositAmounts []int, consensusProtocol string, compounding bool) error { +func validateDKGConfig(numOperators int, network string, testnet eth2util.Network, depositAmounts []int, consensusProtocol string, compounding bool) error { // Don't allow cluster size to be less than 3. if numOperators < minNodes { return errors.New("number of operators is below minimum", z.Int("operators", numOperators), z.Int("min", minNodes)) } - if !eth2util.ValidNetwork(network) { - return errors.New("unsupported network", z.Str("network", network)) + if err := validateNetworkConfig(network, testnet); err != nil { + return errors.Wrap(err, "unsupported network", z.Str("network", network)) } if len(depositAmounts) > 0 { diff --git a/cmd/createdkg_internal_test.go b/cmd/createdkg_internal_test.go index 5e1c87331..17a5ed8bb 100644 --- a/cmd/createdkg_internal_test.go +++ b/cmd/createdkg_internal_test.go @@ -198,23 +198,23 @@ func TestValidateWithdrawalAddr(t *testing.T) { func TestValidateDKGConfig(t *testing.T) { t.Run("insufficient ENRs", func(t *testing.T) { numOperators := 2 - err := validateDKGConfig(numOperators, "", nil, "", false) + err := validateDKGConfig(numOperators, "", eth2util.Network{}, nil, "", false) require.ErrorContains(t, err, "number of operators is below minimum") }) t.Run("invalid network", func(t *testing.T) { numOperators := 4 - err := validateDKGConfig(numOperators, "cosmos", nil, "", false) + err := validateDKGConfig(numOperators, "cosmos", eth2util.Network{}, nil, "", false) require.ErrorContains(t, err, "unsupported network") }) t.Run("wrong deposit amounts sum", func(t *testing.T) { - err := validateDKGConfig(4, "goerli", []int{8, 16}, "", false) + err := validateDKGConfig(4, "goerli", eth2util.Network{}, []int{8, 16}, "", false) require.ErrorContains(t, err, "sum of partial deposit amounts must be at least 32ETH, repetition is allowed") }) t.Run("unsupported consensus protocol", func(t *testing.T) { - err := validateDKGConfig(4, "goerli", nil, "unreal", false) + err := validateDKGConfig(4, "goerli", eth2util.Network{}, nil, "unreal", false) require.ErrorContains(t, err, "unsupported consensus protocol") }) }