mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-20 13:44:31 +00:00
## Why this should be merged `goheader` linter apparently hasn't been running but now decided to and is blocking another PR. It's not worth the time to investigatie why (probably a version update). ## How this works Update header comments as required. ## How this was tested N/A
270 lines
7.1 KiB
Go
270 lines
7.1 KiB
Go
// Copyright 2024-2025 the libevm authors.
|
|
//
|
|
// The libevm additions to go-ethereum are free software: you can redistribute
|
|
// them and/or modify them under the terms of the GNU Lesser General Public License
|
|
// as published by the Free Software Foundation, either version 3 of the License,
|
|
// or (at your option) any later version.
|
|
//
|
|
// The libevm additions are distributed in the hope that they will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
|
|
// General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Lesser General Public License
|
|
// along with the go-ethereum library. If not, see
|
|
// <http://www.gnu.org/licenses/>.
|
|
package params
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"math/big"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/ava-labs/libevm/libevm/pseudo"
|
|
)
|
|
|
|
type nestedChainConfigExtra struct {
|
|
NestedFoo string `json:"foo"`
|
|
|
|
NOOPHooks
|
|
}
|
|
|
|
type rootJSONChainConfigExtra struct {
|
|
TopLevelFoo string `json:"foo"`
|
|
|
|
NOOPHooks
|
|
}
|
|
|
|
func TestChainConfigJSONRoundTrip(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
register func()
|
|
jsonInput string
|
|
want *ChainConfig
|
|
}{
|
|
{
|
|
name: "no registered extras",
|
|
register: func() {},
|
|
jsonInput: `{
|
|
"chainId": 1234
|
|
}`,
|
|
want: &ChainConfig{
|
|
ChainID: big.NewInt(1234),
|
|
},
|
|
},
|
|
{
|
|
name: "reuse top-level JSON with non-pointer",
|
|
register: func() {
|
|
RegisterExtras(Extras[rootJSONChainConfigExtra, NOOPHooks]{
|
|
ReuseJSONRoot: true,
|
|
})
|
|
},
|
|
jsonInput: `{
|
|
"chainId": 5678,
|
|
"foo": "hello"
|
|
}`,
|
|
want: &ChainConfig{
|
|
ChainID: big.NewInt(5678),
|
|
extra: pseudo.From(rootJSONChainConfigExtra{TopLevelFoo: "hello"}).Type,
|
|
},
|
|
},
|
|
{
|
|
name: "reuse top-level JSON with pointer",
|
|
register: func() {
|
|
RegisterExtras(Extras[*rootJSONChainConfigExtra, NOOPHooks]{
|
|
ReuseJSONRoot: true,
|
|
})
|
|
},
|
|
jsonInput: `{
|
|
"chainId": 5678,
|
|
"foo": "hello"
|
|
}`,
|
|
want: &ChainConfig{
|
|
ChainID: big.NewInt(5678),
|
|
extra: pseudo.From(&rootJSONChainConfigExtra{TopLevelFoo: "hello"}).Type,
|
|
},
|
|
},
|
|
{
|
|
name: "nested JSON with non-pointer",
|
|
register: func() {
|
|
RegisterExtras(Extras[nestedChainConfigExtra, NOOPHooks]{
|
|
ReuseJSONRoot: false, // explicit zero value only for tests
|
|
})
|
|
},
|
|
jsonInput: `{
|
|
"chainId": 42,
|
|
"extra": {"foo": "world"}
|
|
}`,
|
|
want: &ChainConfig{
|
|
ChainID: big.NewInt(42),
|
|
extra: pseudo.From(nestedChainConfigExtra{NestedFoo: "world"}).Type,
|
|
},
|
|
},
|
|
{
|
|
name: "nested JSON with pointer",
|
|
register: func() {
|
|
RegisterExtras(Extras[*nestedChainConfigExtra, NOOPHooks]{
|
|
ReuseJSONRoot: false, // explicit zero value only for tests
|
|
})
|
|
},
|
|
jsonInput: `{
|
|
"chainId": 42,
|
|
"extra": {"foo": "world"}
|
|
}`,
|
|
want: &ChainConfig{
|
|
ChainID: big.NewInt(42),
|
|
extra: pseudo.From(&nestedChainConfigExtra{NestedFoo: "world"}).Type,
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
TestOnlyClearRegisteredExtras()
|
|
t.Cleanup(TestOnlyClearRegisteredExtras)
|
|
tt.register()
|
|
|
|
t.Run("json.Unmarshal()", func(t *testing.T) {
|
|
got := new(ChainConfig)
|
|
require.NoError(t, json.Unmarshal([]byte(tt.jsonInput), got))
|
|
require.Equal(t, tt.want, got)
|
|
})
|
|
|
|
t.Run("json.Marshal()", func(t *testing.T) {
|
|
var want bytes.Buffer
|
|
require.NoError(t, json.Compact(&want, []byte(tt.jsonInput)), "json.Compact()")
|
|
|
|
got, err := json.Marshal(tt.want)
|
|
require.NoError(t, err, "json.Marshal()")
|
|
require.Equal(t, want.String(), string(got))
|
|
})
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestUnmarshalChainConfigJSON_Errors(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
type testExtra struct {
|
|
Field string `json:"field"`
|
|
}
|
|
|
|
testCases := map[string]struct {
|
|
jsonData string // string for convenience
|
|
extra *testExtra
|
|
reuseJSONRoot bool
|
|
wantConfig ChainConfig
|
|
wantExtra any
|
|
wantErrRegex string
|
|
}{
|
|
"invalid_json": {
|
|
extra: &testExtra{},
|
|
wantExtra: &testExtra{},
|
|
wantErrRegex: `^decoding JSON into combination of \*.+\.ChainConfig and \*.+\.testExtra \(as "extra" key\): .+$`,
|
|
},
|
|
"nil_extra_at_root_depth": {
|
|
jsonData: `{"chainId": 1}`,
|
|
extra: nil,
|
|
reuseJSONRoot: true,
|
|
wantExtra: (*testExtra)(nil),
|
|
wantErrRegex: `^\*.+.testExtra argument is nil; use \*.+\.ChainConfig\.UnmarshalJSON\(\) directly$`,
|
|
},
|
|
"nil_extra_at_extra_key": {
|
|
jsonData: `{"chainId": 1}`,
|
|
extra: nil,
|
|
wantExtra: (*testExtra)(nil),
|
|
wantErrRegex: `^\*.+\.testExtra argument is nil; use \*.+\.ChainConfig.UnmarshalJSON\(\) directly$`,
|
|
},
|
|
"wrong_extra_type_at_extra_key": {
|
|
jsonData: `{"chainId": 1, "extra": 1}`,
|
|
extra: &testExtra{},
|
|
wantConfig: ChainConfig{ChainID: big.NewInt(1)},
|
|
wantExtra: &testExtra{},
|
|
wantErrRegex: `^decoding JSON into combination of \*.+\.ChainConfig and \*.+\.testExtra \(as "extra" key\): .+$`,
|
|
},
|
|
"wrong_extra_type_at_root_depth": {
|
|
jsonData: `{"chainId": 1, "field": 1}`,
|
|
extra: &testExtra{},
|
|
reuseJSONRoot: true,
|
|
wantConfig: ChainConfig{ChainID: big.NewInt(1)},
|
|
wantExtra: &testExtra{},
|
|
wantErrRegex: `^decoding JSON into \*.+\.testExtra: .+`,
|
|
},
|
|
}
|
|
|
|
for name, testCase := range testCases {
|
|
testCase := testCase
|
|
t.Run(name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
data := []byte(testCase.jsonData)
|
|
config := ChainConfig{}
|
|
err := UnmarshalChainConfigJSON(data, &config, testCase.extra, testCase.reuseJSONRoot)
|
|
if testCase.wantErrRegex == "" {
|
|
require.NoError(t, err)
|
|
} else {
|
|
require.Error(t, err)
|
|
require.Regexp(t, testCase.wantErrRegex, err.Error())
|
|
}
|
|
assert.Equal(t, testCase.wantConfig, config)
|
|
assert.Equal(t, testCase.wantExtra, testCase.extra)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMarshalChainConfigJSON_Errors(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testCases := map[string]struct {
|
|
config ChainConfig
|
|
extra any
|
|
reuseJSONRoot bool
|
|
wantJSONData string // string for convenience
|
|
wantErrRegex string
|
|
}{
|
|
"invalid_extra_at_extra_key": {
|
|
extra: struct {
|
|
Field chan struct{} `json:"field"`
|
|
}{},
|
|
wantErrRegex: `^encoding combination of .+\.ChainConfig and .+ to JSON: .+$`,
|
|
},
|
|
"nil_extra_at_extra_key": {
|
|
wantJSONData: `{"chainId":null}`,
|
|
},
|
|
"invalid_extra_at_root_depth": {
|
|
extra: struct {
|
|
Field chan struct{} `json:"field"`
|
|
}{},
|
|
reuseJSONRoot: true,
|
|
wantErrRegex: "^converting extra config to JSON raw messages: .+$",
|
|
},
|
|
"duplicate_key": {
|
|
extra: struct {
|
|
Field string `json:"chainId"`
|
|
}{},
|
|
reuseJSONRoot: true,
|
|
wantErrRegex: `^duplicate JSON key "chainId" in ChainConfig and extra struct .+$`,
|
|
},
|
|
}
|
|
|
|
for name, testCase := range testCases {
|
|
testCase := testCase
|
|
t.Run(name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
config := ChainConfig{}
|
|
data, err := MarshalChainConfigJSON(config, testCase.extra, testCase.reuseJSONRoot)
|
|
if testCase.wantErrRegex == "" {
|
|
require.NoError(t, err)
|
|
} else {
|
|
require.Error(t, err)
|
|
assert.Regexp(t, testCase.wantErrRegex, err.Error())
|
|
}
|
|
assert.Equal(t, testCase.wantJSONData, string(data))
|
|
})
|
|
}
|
|
}
|