Skip to content

Commit b9e2eb5

Browse files
authored
beacon/config: fix LoadForks with non-string values (#32609)
Fixes a crash when loading the beacon chain config if new fields like `BLOB_SCHEDULE: []` are present. Previously, the config loader assumed all values were strings, causing errors such as: ``` Fatal: Could not load beacon chain config '/network-configs/config.yaml': failed to parse beacon chain config file: yaml: unmarshal errors: line 242: cannot unmarshal !!seq into string ``` This PR updates the parsing logic to handle non-string values correctly and adds explicit validation for fork fields.
1 parent dce511c commit b9e2eb5

File tree

3 files changed

+69
-16
lines changed

3 files changed

+69
-16
lines changed

beacon/params/config.go

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"crypto/sha256"
2121
"fmt"
2222
"math"
23+
"math/big"
2324
"os"
2425
"slices"
2526
"sort"
@@ -90,12 +91,8 @@ func (c *ChainConfig) AddFork(name string, epoch uint64, version []byte) *ChainC
9091

9192
// LoadForks parses the beacon chain configuration file (config.yaml) and extracts
9293
// the list of forks.
93-
func (c *ChainConfig) LoadForks(path string) error {
94-
file, err := os.ReadFile(path)
95-
if err != nil {
96-
return fmt.Errorf("failed to read beacon chain config file: %v", err)
97-
}
98-
config := make(map[string]string)
94+
func (c *ChainConfig) LoadForks(file []byte) error {
95+
config := make(map[string]any)
9996
if err := yaml.Unmarshal(file, &config); err != nil {
10097
return fmt.Errorf("failed to parse beacon chain config file: %v", err)
10198
}
@@ -108,18 +105,36 @@ func (c *ChainConfig) LoadForks(path string) error {
108105
for key, value := range config {
109106
if strings.HasSuffix(key, "_FORK_VERSION") {
110107
name := key[:len(key)-len("_FORK_VERSION")]
111-
if v, err := hexutil.Decode(value); err == nil {
108+
switch version := value.(type) {
109+
case int:
110+
versions[name] = new(big.Int).SetUint64(uint64(version)).FillBytes(make([]byte, 4))
111+
case uint64:
112+
versions[name] = new(big.Int).SetUint64(version).FillBytes(make([]byte, 4))
113+
case string:
114+
v, err := hexutil.Decode(version)
115+
if err != nil {
116+
return fmt.Errorf("failed to decode hex fork id %q in beacon chain config file: %v", version, err)
117+
}
112118
versions[name] = v
113-
} else {
114-
return fmt.Errorf("failed to decode hex fork id %q in beacon chain config file: %v", value, err)
119+
default:
120+
return fmt.Errorf("invalid fork version %q in beacon chain config file", version)
115121
}
116122
}
117123
if strings.HasSuffix(key, "_FORK_EPOCH") {
118124
name := key[:len(key)-len("_FORK_EPOCH")]
119-
if v, err := strconv.ParseUint(value, 10, 64); err == nil {
125+
switch epoch := value.(type) {
126+
case int:
127+
epochs[name] = uint64(epoch)
128+
case uint64:
129+
epochs[name] = epoch
130+
case string:
131+
v, err := strconv.ParseUint(epoch, 10, 64)
132+
if err != nil {
133+
return fmt.Errorf("failed to parse epoch number %q in beacon chain config file: %v", epoch, err)
134+
}
120135
epochs[name] = v
121-
} else {
122-
return fmt.Errorf("failed to parse epoch number %q in beacon chain config file: %v", value, err)
136+
default:
137+
return fmt.Errorf("invalid fork epoch %q in beacon chain config file", epoch)
123138
}
124139
}
125140
}

beacon/params/config_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package params
2+
3+
import (
4+
"bytes"
5+
"testing"
6+
)
7+
8+
func TestChainConfig_LoadForks(t *testing.T) {
9+
const config = `
10+
GENESIS_FORK_VERSION: 0x00000000
11+
12+
ALTAIR_FORK_VERSION: 0x00000001
13+
ALTAIR_FORK_EPOCH: 1
14+
15+
EIP7928_FORK_VERSION: 0xb0000038
16+
EIP7928_FORK_EPOCH: 18446744073709551615
17+
18+
BLOB_SCHEDULE: []
19+
`
20+
c := &ChainConfig{}
21+
err := c.LoadForks([]byte(config))
22+
if err != nil {
23+
t.Fatal(err)
24+
}
25+
26+
for _, fork := range c.Forks {
27+
if fork.Name == "GENESIS" && (fork.Epoch != 0) {
28+
t.Errorf("unexpected genesis fork epoch %d", fork.Epoch)
29+
}
30+
if fork.Name == "ALTAIR" && (fork.Epoch != 1 || !bytes.Equal(fork.Version, []byte{0, 0, 0, 1})) {
31+
t.Errorf("unexpected altair fork epoch %d version %x", fork.Epoch, fork.Version)
32+
}
33+
}
34+
}

cmd/utils/flags.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1931,11 +1931,15 @@ func MakeBeaconLightConfig(ctx *cli.Context) bparams.ClientConfig {
19311931
} else {
19321932
Fatalf("Could not parse --%s: %v", BeaconGenesisRootFlag.Name, err)
19331933
}
1934-
configFile := ctx.String(BeaconConfigFlag.Name)
1935-
if err := config.ChainConfig.LoadForks(configFile); err != nil {
1936-
Fatalf("Could not load beacon chain config '%s': %v", configFile, err)
1934+
configPath := ctx.String(BeaconConfigFlag.Name)
1935+
file, err := os.ReadFile(configPath)
1936+
if err != nil {
1937+
Fatalf("failed to read beacon chain config file '%s': %v", configPath, err)
1938+
}
1939+
if err := config.ChainConfig.LoadForks(file); err != nil {
1940+
Fatalf("Could not load beacon chain config '%s': %v", configPath, err)
19371941
}
1938-
log.Info("Using custom beacon chain config", "file", configFile)
1942+
log.Info("Using custom beacon chain config", "file", configPath)
19391943
} else {
19401944
if ctx.IsSet(BeaconGenesisRootFlag.Name) {
19411945
Fatalf("Genesis root is specified but custom beacon chain config is missing")

0 commit comments

Comments
 (0)