-
Notifications
You must be signed in to change notification settings - Fork 104
test: example of configuring config.toml values in systest #667
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
Open
technicallyty
wants to merge
7
commits into
main
Choose a base branch
from
technicallyty/configurable-testnet
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+471
−162
Open
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
51be6a2
allow testnet config to be configured via flags
technicallyty 04b2695
configured
technicallyty f76b968
use tag
technicallyty 22acfd4
updates
technicallyty b58f848
remove comment
technicallyty cd1a933
readme stuff
technicallyty 36b536f
Tidy
technicallyty File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,12 +4,13 @@ import ( | |
"bufio" | ||
"encoding/json" | ||
"fmt" | ||
"github.com/cosmos/evm/config" | ||
"net" | ||
"os" | ||
"path/filepath" | ||
"time" | ||
|
||
"github.com/cosmos/evm/config" | ||
|
||
cosmosevmhd "github.com/cosmos/evm/crypto/hd" | ||
cosmosevmkeyring "github.com/cosmos/evm/crypto/keyring" | ||
"github.com/cosmos/evm/evmd" | ||
|
@@ -62,6 +63,7 @@ var ( | |
flagPrintMnemonic = "print-mnemonic" | ||
flagSingleHost = "single-host" | ||
flagCommitTimeout = "commit-timeout" | ||
configChanges = "config-changes" | ||
unsafeStartValidatorFn UnsafeStartValidatorCmdCreator | ||
) | ||
|
||
|
@@ -88,6 +90,7 @@ type initArgs struct { | |
startingIPAddress string | ||
singleMachine bool | ||
useDocker bool | ||
configChanges []string | ||
} | ||
|
||
type startArgs struct { | ||
|
@@ -188,6 +191,7 @@ Example: | |
return err | ||
} | ||
args.algo, _ = cmd.Flags().GetString(flags.FlagKeyType) | ||
args.configChanges, _ = cmd.Flags().GetStringSlice(configChanges) | ||
|
||
return initTestnetFiles(clientCtx, cmd, config, mbm, genBalIterator, args) | ||
}, | ||
|
@@ -201,6 +205,7 @@ Example: | |
cmd.Flags().String(flagStartingIPAddress, "192.168.0.1", "Starting IP address (192.168.0.1 results in persistent peers list [email protected]:46656, [email protected]:46656, ...)") | ||
cmd.Flags().String(flags.FlagKeyringBackend, flags.DefaultKeyringBackend, "Select keyring's backend (os|file|test)") | ||
cmd.Flags().Bool(flagsUseDocker, false, "test network via docker") | ||
cmd.Flags().StringSlice(configChanges, []string{}, "Config changes to apply to the node: i.e. consensus.timeout_commit=50s") | ||
|
||
return cmd | ||
} | ||
|
@@ -350,6 +355,11 @@ func initTestnetFiles( | |
} | ||
} | ||
|
||
if err := parseAndApplyConfigChanges(nodeConfig, args.configChanges); err != nil { | ||
_ = os.RemoveAll(args.outputDir) | ||
return fmt.Errorf("failed to apply config changes for node %d: %w", i, err) | ||
} | ||
|
||
nodeIDs[i], valPubKeys[i], err = genutil.InitializeNodeValidatorFiles(nodeConfig) | ||
if err != nil { | ||
_ = os.RemoveAll(args.outputDir) | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,187 @@ | ||
package cmd | ||
|
||
import ( | ||
"fmt" | ||
"reflect" | ||
"strconv" | ||
"strings" | ||
"time" | ||
|
||
cmtconfig "github.com/cometbft/cometbft/config" | ||
) | ||
|
||
// parseAndApplyConfigChanges parses the config changes string and applies them to the nodeConfig | ||
func parseAndApplyConfigChanges(nodeConfig *cmtconfig.Config, configChanges []string) error { | ||
if len(configChanges) == 0 { | ||
return nil | ||
} | ||
|
||
if err := applyConfigOverrides(nodeConfig, configChanges); err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// updateConfigField updates a field in the config based on dot notation | ||
// Example: "consensus.timeout_propose=5s" or "log_level=debug" (for BaseConfig fields) | ||
func updateConfigField(config *cmtconfig.Config, fieldPath, value string) error { | ||
parts := strings.Split(fieldPath, ".") | ||
|
||
configValue := reflect.ValueOf(config).Elem() | ||
|
||
// Handle BaseConfig fields (squashed/embedded fields) | ||
if len(parts) == 1 { | ||
// This might be a BaseConfig field, try to find it in the embedded struct | ||
baseConfigField := configValue.FieldByName("BaseConfig") | ||
if baseConfigField.IsValid() { | ||
targetFieldName := getFieldName(baseConfigField.Type(), parts[0]) | ||
if targetFieldName != "" { | ||
targetField := baseConfigField.FieldByName(targetFieldName) | ||
if targetField.IsValid() && targetField.CanSet() { | ||
return setFieldValue(targetField, value) | ||
} | ||
} | ||
} | ||
|
||
// If not found in BaseConfig, try in the main Config struct | ||
targetFieldName := getFieldName(configValue.Type(), parts[0]) | ||
if targetFieldName != "" { | ||
targetField := configValue.FieldByName(targetFieldName) | ||
if targetField.IsValid() && targetField.CanSet() { | ||
return setFieldValue(targetField, value) | ||
} | ||
} | ||
|
||
return fmt.Errorf("field not found: %s", parts[0]) | ||
} | ||
|
||
// Handle nested fields (e.g., consensus.timeout_propose) | ||
current := configValue | ||
for i, part := range parts[:len(parts)-1] { | ||
field := current.FieldByName(getFieldName(current.Type(), part)) | ||
if !field.IsValid() { | ||
return fmt.Errorf("field not found: %s", strings.Join(parts[:i+1], ".")) | ||
} | ||
|
||
// If it's a pointer to a struct, get the element | ||
if field.Kind() == reflect.Ptr { | ||
if field.IsNil() { | ||
// Initialize the pointer if it's nil | ||
field.Set(reflect.New(field.Type().Elem())) | ||
} | ||
field = field.Elem() | ||
} | ||
current = field | ||
} | ||
|
||
// Set the final field | ||
finalFieldName := parts[len(parts)-1] | ||
targetField := current.FieldByName(getFieldName(current.Type(), finalFieldName)) | ||
if !targetField.IsValid() { | ||
return fmt.Errorf("field not found: %s", finalFieldName) | ||
} | ||
|
||
if !targetField.CanSet() { | ||
return fmt.Errorf("field cannot be set: %s", finalFieldName) | ||
} | ||
|
||
return setFieldValue(targetField, value) | ||
} | ||
|
||
// getFieldName finds the struct field name from mapstructure tag or field name | ||
func getFieldName(structType reflect.Type, tagName string) string { | ||
for i := 0; i < structType.NumField(); i++ { | ||
field := structType.Field(i) | ||
|
||
// Check mapstructure tag | ||
if tag := field.Tag.Get("mapstructure"); tag != "" { | ||
if tag == tagName { | ||
return field.Name | ||
} | ||
} | ||
|
||
// Check field name (case insensitive) | ||
if strings.EqualFold(field.Name, tagName) { | ||
return field.Name | ||
} | ||
} | ||
return "" | ||
} | ||
|
||
// setFieldValue sets the field value based on its type | ||
func setFieldValue(field reflect.Value, value string) error { | ||
switch field.Type() { | ||
case reflect.TypeOf(time.Duration(0)): | ||
duration, err := time.ParseDuration(value) | ||
if err != nil { | ||
return fmt.Errorf("invalid duration format: %s", value) | ||
} | ||
field.Set(reflect.ValueOf(duration)) | ||
|
||
case reflect.TypeOf(""): | ||
field.SetString(value) | ||
|
||
case reflect.TypeOf(true): | ||
boolVal, err := strconv.ParseBool(value) | ||
if err != nil { | ||
return fmt.Errorf("invalid boolean format: %s", value) | ||
} | ||
field.SetBool(boolVal) | ||
|
||
case reflect.TypeOf(int64(0)): | ||
intVal, err := strconv.ParseInt(value, 10, 64) | ||
if err != nil { | ||
return fmt.Errorf("invalid int64 format: %s", value) | ||
} | ||
field.SetInt(intVal) | ||
|
||
case reflect.TypeOf(int(0)): | ||
intVal, err := strconv.Atoi(value) | ||
if err != nil { | ||
return fmt.Errorf("invalid int format: %s", value) | ||
} | ||
field.SetInt(int64(intVal)) | ||
|
||
case reflect.TypeOf([]string{}): | ||
// Handle string slices - split by comma | ||
var slice []string | ||
if strings.TrimSpace(value) != "" { | ||
// Split by comma and trim whitespace | ||
parts := strings.Split(value, ",") | ||
for _, part := range parts { | ||
slice = append(slice, strings.TrimSpace(part)) | ||
} | ||
} | ||
field.Set(reflect.ValueOf(slice)) | ||
|
||
default: | ||
return fmt.Errorf("unsupported field type: %v", field.Type()) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// parseConfigOverride parses a string like "consensus.timeout_propose=5s" | ||
func parseConfigOverride(override string) (string, string, error) { | ||
parts := strings.SplitN(override, "=", 2) | ||
if len(parts) != 2 { | ||
return "", "", fmt.Errorf("invalid override format: %s (expected field=value)", override) | ||
} | ||
return parts[0], parts[1], nil | ||
} | ||
|
||
// applyConfigOverrides applies multiple overrides to the config | ||
func applyConfigOverrides(config *cmtconfig.Config, overrides []string) error { | ||
for _, override := range overrides { | ||
fieldPath, value, err := parseConfigOverride(override) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if err := updateConfigField(config, fieldPath, value); err != nil { | ||
return fmt.Errorf("failed to set %s: %w", fieldPath, err) | ||
} | ||
} | ||
return nil | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
package cmd | ||
|
||
import ( | ||
"testing" | ||
"time" | ||
|
||
cmtconfig "github.com/cometbft/cometbft/config" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestParseAndApplyConfigChanges(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
override string | ||
expectedValue interface{} | ||
checkFunc func(*cmtconfig.Config) interface{} | ||
}{ | ||
{ | ||
name: "consensus timeout_propose", | ||
override: "consensus.timeout_propose=10s", | ||
expectedValue: 10 * time.Second, | ||
checkFunc: func(cfg *cmtconfig.Config) interface{} { return cfg.Consensus.TimeoutPropose }, | ||
}, | ||
{ | ||
name: "consensus create_empty_blocks", | ||
override: "consensus.create_empty_blocks=false", | ||
expectedValue: false, | ||
checkFunc: func(cfg *cmtconfig.Config) interface{} { return cfg.Consensus.CreateEmptyBlocks }, | ||
}, | ||
{ | ||
name: "consensus double_sign_check_height", | ||
override: "consensus.double_sign_check_height=500", | ||
expectedValue: int64(500), | ||
checkFunc: func(cfg *cmtconfig.Config) interface{} { return cfg.Consensus.DoubleSignCheckHeight }, | ||
}, | ||
{ | ||
name: "baseconfig home", | ||
override: "home=/custom/path", | ||
expectedValue: "/custom/path", | ||
checkFunc: func(cfg *cmtconfig.Config) interface{} { return cfg.RootDir }, | ||
}, | ||
{ | ||
name: "baseconfig log_level", | ||
override: "log_level=error", | ||
expectedValue: "error", | ||
checkFunc: func(cfg *cmtconfig.Config) interface{} { return cfg.LogLevel }, | ||
}, | ||
{ | ||
name: "baseconfig log_format", | ||
override: "log_format=json", | ||
expectedValue: "json", | ||
checkFunc: func(cfg *cmtconfig.Config) interface{} { return cfg.LogFormat }, | ||
}, | ||
{ | ||
name: "baseconfig db_backend", | ||
override: "db_backend=badgerdb", | ||
expectedValue: "badgerdb", | ||
checkFunc: func(cfg *cmtconfig.Config) interface{} { return cfg.DBBackend }, | ||
}, | ||
{ | ||
name: "string slice single value", | ||
override: "statesync.rpc_servers=production", | ||
expectedValue: []string{"production"}, | ||
checkFunc: func(cfg *cmtconfig.Config) interface{} { | ||
return cfg.StateSync.RPCServers | ||
}, | ||
}, | ||
{ | ||
name: "string slice multiple values", | ||
override: "statesync.rpc_servers=production,monitoring,critical", | ||
expectedValue: []string{"production", "monitoring", "critical"}, | ||
checkFunc: func(cfg *cmtconfig.Config) interface{} { | ||
return cfg.StateSync.RPCServers | ||
}, | ||
}, | ||
{ | ||
name: "string slice empty", | ||
override: "statesync.rpc_servers=", | ||
expectedValue: []string(nil), | ||
checkFunc: func(cfg *cmtconfig.Config) interface{} { | ||
return cfg.StateSync.RPCServers | ||
}, | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
cfg := cmtconfig.DefaultConfig() | ||
|
||
err := parseAndApplyConfigChanges(cfg, []string{tt.override}) | ||
require.NoError(t, err) | ||
|
||
actualValue := tt.checkFunc(cfg) | ||
require.Equal(t, tt.expectedValue, actualValue) | ||
}) | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Check notice
Code scanning / CodeQL
Sensitive package import Note test