Skip to content
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

Custom Fallback TOML Config #15617

Open
wants to merge 5 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/tall-falcons-yawn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"chainlink": patch
---

#added the ability to define a fallback.toml override config using CL_CHAIN_DEFAULTS env var
146 changes: 83 additions & 63 deletions core/chains/evm/config/toml/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"bytes"
"embed"
"fmt"
"io"
"io/fs"
"log"
"os"
"path/filepath"
Expand All @@ -19,7 +19,6 @@ import (
)

var (

//go:embed defaults/*.toml
defaultsFS embed.FS
fallback Chain
Expand All @@ -33,48 +32,24 @@ var (
)

func init() {
// read the defaults first
var (
fb *Chain
err error
)

fes, err := defaultsFS.ReadDir("defaults")
// read all default configs
DefaultIDs, defaultNames, defaults, fb, err = initDefaults(defaultsFS.ReadDir, defaultsFS.ReadFile, "defaults")
if err != nil {
log.Fatalf("failed to read defaults/: %v", err)
log.Fatalf("failed to read defaults: %s", 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)
}
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)
}
fallback = config.Chain
continue
}
if config.ChainID == nil {
log.Fatalf("missing ChainID: %s", path)
}
DefaultIDs = append(DefaultIDs, config.ChainID)
id := config.ChainID.String()
if _, ok := defaults[id]; ok {
log.Fatalf("%q contains duplicate ChainID: %s", path, id)
}
defaults[id] = config.Chain
defaultNames[id] = strings.ReplaceAll(strings.TrimSuffix(fe.Name(), ".toml"), "_", " ")
if fb == nil {
log.Fatal("failed to set fallback chain config")
}
slices.SortFunc(DefaultIDs, func(a, b *big.Big) int {
return a.Cmp(b)
})

fallback = *fb

// check for and apply any overrides
// read the custom defaults overrides
dir := env.CustomDefaults.Get()
if dir == "" {
Expand All @@ -83,54 +58,99 @@ func init() {
}

// use evm overrides specifically
evmDir := fmt.Sprintf("%s/evm", dir)
_, _, customDefaults, fb, err = initDefaults(os.ReadDir, os.ReadFile, dir+"/evm")
if err != nil {
log.Fatalf("failed to read custom overrides: %s", err)
}

// Read directory contents for evm only
entries, err := os.ReadDir(evmDir)
if fb != nil {
fallback = *fb
}
}

func initDefaults(
dirReader func(name string) ([]fs.DirEntry, error),
fileReader func(name string) ([]byte, error),
root string,
) ([]*big.Big, map[string]string, map[string]Chain, *Chain, error) {
entries, err := dirReader(root)
if err != nil {
log.Fatalf("error reading evm custom defaults override directory: %v", err)
return
return nil, nil, nil, nil, fmt.Errorf("failed to read directory: %w", err)
}

var fb *Chain

ids := make([]*big.Big, 0)
configs := make(map[string]Chain)
names := make(map[string]string)

for _, entry := range entries {
if entry.IsDir() {
// Skip directories
continue
}

path := evmDir + "/" + entry.Name()
file, err := os.Open(path)
if err != nil {
log.Fatalf("error opening file (name: %v) in custom defaults override directory: %v", entry.Name(), err)
}
// read the file to bytes
path := filepath.Join(root, entry.Name())

// Read file contents
b, err := io.ReadAll(file)
file.Close()
chainID, chain, err := readConfig(path, fileReader)
if err != nil {
log.Fatalf("error reading file (name: %v) contents in custom defaults override directory: %v", entry.Name(), err)
return nil, nil, nil, nil, err
}

var config = struct {
ChainID *big.Big
Chain
}{}
if entry.Name() == "fallback.toml" {
if chainID != nil {
return nil, nil, nil, nil, fmt.Errorf("fallback ChainID must be nil: found: %s", chainID)
}

fb = &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)
continue
}

if config.ChainID == nil {
log.Fatalf("missing ChainID in: %s in custom defaults override directory. exiting", path)
// ensure ChainID is set
if chainID == nil {
return nil, nil, nil, nil, fmt.Errorf("missing ChainID: %s", path)
}

id := config.ChainID.String()
ids = append(ids, chainID)

if _, ok := customDefaults[id]; ok {
// ChainID as a default should not be duplicated
id := chainID.String()
if _, ok := configs[id]; ok {
log.Fatalf("%q contains duplicate ChainID: %s", path, id)
}
customDefaults[id] = config.Chain

// set lookups
configs[id] = chain
names[id] = strings.ReplaceAll(strings.TrimSuffix(entry.Name(), ".toml"), "_", " ")
}

// sort IDs in numeric order
slices.SortFunc(ids, func(a, b *big.Big) int {
return a.Cmp(b)
})

return ids, names, configs, fb, nil
}

func readConfig(path string, reader func(name string) ([]byte, error)) (*big.Big, Chain, error) {
bts, err := reader(path)
if err != nil {
return nil, Chain{}, fmt.Errorf("error reading file: %w", err)
}

var config = struct {
ChainID *big.Big
Chain
}{}

// decode from toml to a chain config
if err := cconfig.DecodeTOML(bytes.NewReader(bts), &config); err != nil {
return nil, Chain{}, fmt.Errorf("error in TOML decoding %s: %w", path, err)
}

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.
Expand Down
Loading
Loading