From 1d753ac4d1f8f00860a3d2c6720b8eced60d37f8 Mon Sep 17 00:00:00 2001 From: Awbrey Hughlett Date: Tue, 10 Dec 2024 14:35:53 -0600 Subject: [PATCH] Custom Fallback TOML Config This commit provides setting a new env var `CL_CHAIN_FALLBACK` as a path to a custom `fallback.toml`. This allows plugins to define their own set of fallback options apart from the core node which override the default fallback options. --- core/chains/evm/config/toml/defaults.go | 140 ++++++++++++++++-------- core/config/env/env.go | 1 + 2 files changed, 96 insertions(+), 45 deletions(-) diff --git a/core/chains/evm/config/toml/defaults.go b/core/chains/evm/config/toml/defaults.go index 0885d94e6df..fbd65cb2467 100644 --- a/core/chains/evm/config/toml/defaults.go +++ b/core/chains/evm/config/toml/defaults.go @@ -3,8 +3,8 @@ package toml import ( "bytes" "embed" + "errors" "fmt" - "io" "log" "os" "path/filepath" @@ -33,48 +33,83 @@ var ( ) func init() { - // read the defaults first + // read all default configs + initReadDefaults() + + // overrides should be applied as: + // fallback.toml(defaults dir) <- fallback.toml(env CL_CHAIN_FALLBACK) <- ChainSpecific.toml(env CL_CHAIN_DEFAULTS) + // + // the custom fallback gets processed and overrides the default fallback + if path := env.CustomFallback.Get(); path != "" { + _, chain, err := readConfig(path, os.ReadFile, true) + if err != nil { + if !errors.Is(err, errFallbackConfig) { + log.Fatalf("custom fallback config error: %s", err.Error()) + } + + fallback = chain + } + } + + // check for and apply any overrides + initApplyEVMOverrides() +} + +var ( + errRead = errors.New("error reading file") + errDecode = errors.New("error in TOML decoding") + errMissingChainID = errors.New("missing ChainID") + errNonNilChainID = errors.New("fallback ChainID must be nil") + errFallbackConfig = errors.New("fallback config") +) +func initReadDefaults() { + // read the defaults first fes, err := defaultsFS.ReadDir("defaults") if err != nil { log.Fatalf("failed to read defaults/: %v", err) } + for _, fe := range fes { - path := filepath.Join("defaults", fe.Name()) - b, err2 := defaultsFS.ReadFile(path) - if err2 != nil { - log.Fatalf("failed to read %q: %v", path, err2) + if fe.IsDir() { + // Skip directories + continue } - var config = struct { - ChainID *big.Big - Chain - }{} - if err3 := cconfig.DecodeTOML(bytes.NewReader(b), &config); err3 != nil { - log.Fatalf("failed to decode %q: %v", path, err3) - } - if fe.Name() == "fallback.toml" { - if config.ChainID != nil { - log.Fatalf("fallback ChainID must be nil, not: %s", config.ChainID) + // read the file to bytes + path := filepath.Join("defaults", fe.Name()) + chainID, chain, err := readConfig(path, defaultsFS.ReadFile, fe.Name() == "fallback.toml") + if err != nil { + if errors.Is(err, errFallbackConfig) { + fallback = chain + + continue } - fallback = config.Chain - continue - } - if config.ChainID == nil { - log.Fatalf("missing ChainID: %s", path) + + log.Fatal(err.Error()) } - DefaultIDs = append(DefaultIDs, config.ChainID) - id := config.ChainID.String() + + // add ChainID to set of default IDs + DefaultIDs = append(DefaultIDs, chainID) + + // ChainID as a default should not be duplicated + id := chainID.String() if _, ok := defaults[id]; ok { log.Fatalf("%q contains duplicate ChainID: %s", path, id) } - defaults[id] = config.Chain + + // set default lookups + defaults[id] = chain defaultNames[id] = strings.ReplaceAll(strings.TrimSuffix(fe.Name(), ".toml"), "_", " ") } + + // sort default IDs in numeric order slices.SortFunc(DefaultIDs, func(a, b *big.Big) int { return a.Cmp(b) }) +} +func initApplyEVMOverrides() { // read the custom defaults overrides dir := env.CustomDefaults.Get() if dir == "" { @@ -98,39 +133,54 @@ func init() { continue } + // read the file to bytes path := evmDir + "/" + entry.Name() - file, err := os.Open(path) + chainID, chain, err := readConfig(path, defaultsFS.ReadFile, false) if err != nil { - log.Fatalf("error opening file (name: %v) in custom defaults override directory: %v", entry.Name(), err) + log.Fatalf("custom defaults override failure (%s): %s", entry.Name(), err.Error()) } - // Read file contents - b, err := io.ReadAll(file) - file.Close() - if err != nil { - log.Fatalf("error reading file (name: %v) contents in custom defaults override directory: %v", entry.Name(), err) + // ChainID as a default should not be duplicated + id := chainID.String() + if _, ok := customDefaults[id]; ok { + log.Fatalf("%q contains duplicate ChainID: %s", path, id) } - var config = struct { - ChainID *big.Big - Chain - }{} + // set default lookups + customDefaults[id] = chain + } +} + +func readConfig(path string, reader func(name string) ([]byte, error), fallback bool) (*big.Big, Chain, error) { + bts, err := reader(path) + if err != nil { + return nil, Chain{}, fmt.Errorf("%w: %s", errRead, err.Error()) + } + + var config = struct { + ChainID *big.Big + Chain + }{} - if err := cconfig.DecodeTOML(bytes.NewReader(b), &config); err != nil { - log.Fatalf("failed to decode %q in custom defaults override directory: %v", path, err) - } + // decode from toml to a chain config + if err := cconfig.DecodeTOML(bytes.NewReader(bts), &config); err != nil { + return nil, Chain{}, fmt.Errorf("%w %q: %v", errDecode, path, err) + } - if config.ChainID == nil { - log.Fatalf("missing ChainID in: %s in custom defaults override directory. exiting", path) + if fallback { + if config.ChainID != nil { + return nil, Chain{}, fmt.Errorf("%w: found: %s", errNonNilChainID, config.ChainID) } - id := config.ChainID.String() + return nil, config.Chain, errFallbackConfig + } - if _, ok := customDefaults[id]; ok { - log.Fatalf("%q contains duplicate ChainID: %s", path, id) - } - customDefaults[id] = config.Chain + // ensure ChainID is set + if config.ChainID == nil { + return nil, Chain{}, fmt.Errorf("%w: %s", errMissingChainID, path) } + + return config.ChainID, config.Chain, nil } // DefaultsNamed returns the default Chain values, optionally for the given chainID, as well as a name if the chainID is known. diff --git a/core/config/env/env.go b/core/config/env/env.go index c34cd7f4f5e..8a24ff21476 100644 --- a/core/config/env/env.go +++ b/core/config/env/env.go @@ -21,6 +21,7 @@ var ( // Migrations env vars EVMChainIDNotNullMigration0195 = "CL_EVM_CHAINID_NOT_NULL_MIGRATION_0195" CustomDefaults = Var("CL_CHAIN_DEFAULTS") + CustomFallback = Var("CL_CHAIN_FALLBACK") ) // LOOPP commands and vars