From 309c5dcf863a355304b32ec8e2f7008b04db9455 Mon Sep 17 00:00:00 2001 From: Damilola Edwards Date: Tue, 18 Aug 2026 17:26:36 +0100 Subject: [PATCH] Fix nil pointer panic on invalid terminal total difficulty SetString returns a nil result on parse failure, and the return value was being discarded before dereferencing it. A spec response with a non-decimal TERMINAL_TOTAL_DIFFICULTY value would crash the caller. Now it just leaves the field at its zero value when parsing fails. --- pkg/beacon/state/spec.go | 5 ++-- pkg/beacon/state/spec_test.go | 56 +++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 pkg/beacon/state/spec_test.go diff --git a/pkg/beacon/state/spec.go b/pkg/beacon/state/spec.go index ccfae23..fdac158 100644 --- a/pkg/beacon/state/spec.go +++ b/pkg/beacon/state/spec.go @@ -117,8 +117,9 @@ func NewSpec(data map[string]any) Spec { if terminalTotalDifficulty, exists := data["TERMINAL_TOTAL_DIFFICULTY"]; exists { ttd := cast.ToString(fmt.Sprintf("%v", terminalTotalDifficulty)) - casted, _ := (*big.NewInt(0)).SetString(ttd, 10) - spec.TerminalTotalDifficulty = *casted + if casted, ok := new(big.Int).SetString(ttd, 10); ok { + spec.TerminalTotalDifficulty = *casted + } } if maxDeposits, exists := data["MAX_DEPOSITS"]; exists { diff --git a/pkg/beacon/state/spec_test.go b/pkg/beacon/state/spec_test.go new file mode 100644 index 0000000..45f29af --- /dev/null +++ b/pkg/beacon/state/spec_test.go @@ -0,0 +1,56 @@ +package state + +import ( + "math/big" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestNewSpec_TerminalTotalDifficulty(t *testing.T) { + tests := []struct { + name string + value any + expected string + }{ + { + name: "decimal string parses correctly", + value: "58750000000000000000000", + expected: "58750000000000000000000", + }, + { + name: "zero parses correctly", + value: "0", + expected: "0", + }, + { + name: "hex-encoded byte slice does not panic and leaves the zero value", + value: []byte{0xc7, 0x0d, 0x80, 0x8a, 0x12, 0x8d, 0x73, 0x80}, + expected: "0", + }, + { + name: "non-numeric string does not panic and leaves the zero value", + value: "not-a-number", + expected: "0", + }, + { + name: "empty string does not panic and leaves the zero value", + value: "", + expected: "0", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.NotPanics(t, func() { + spec := NewSpec(map[string]any{"TERMINAL_TOTAL_DIFFICULTY": tt.value}) + assert.Equal(t, tt.expected, spec.TerminalTotalDifficulty.String()) + }) + }) + } + + t.Run("field absent leaves the zero value", func(t *testing.T) { + spec := NewSpec(map[string]any{}) + assert.Equal(t, big.NewInt(0).String(), spec.TerminalTotalDifficulty.String()) + }) +}