diff --git a/libevm/hookstest/stub.go b/libevm/hookstest/stub.go index 80ebeeca07..83d207e967 100644 --- a/libevm/hookstest/stub.go +++ b/libevm/hookstest/stub.go @@ -19,6 +19,7 @@ package hookstest import ( + "encoding/json" "math/big" "testing" @@ -62,6 +63,14 @@ func (s *Stub) Register(tb testing.TB) params.ExtraPayloads[*Stub, *Stub] { }) } +// MarshalJSON implements [json.Marshaler] and always returns the JSON null +// object. This avoids errors due to incompatible field types. +func (Stub) MarshalJSON() ([]byte, error) { return []byte(`null`), nil } + +// UnmarshalJSON implements [json.Unmarshaler] and always returns nil, ignoring +// its input. +func (Stub) UnmarshalJSON([]byte) error { return nil } + // PrecompileOverride uses the s.PrecompileOverrides map, if non-empty, as the // canonical source of all overrides. If the map is empty then no precompiles // are overridden. @@ -135,4 +144,6 @@ func (s Stub) MinimumGasConsumption(limit uint64) uint64 { var _ interface { params.ChainConfigHooks params.RulesHooks + json.Marshaler + json.Unmarshaler } = Stub{} diff --git a/libevm/hookstest/stub_test.go b/libevm/hookstest/stub_test.go new file mode 100644 index 0000000000..b4d1b6d956 --- /dev/null +++ b/libevm/hookstest/stub_test.go @@ -0,0 +1,59 @@ +// 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 +// . + +package hookstest + +import ( + "os" + "testing" + + "github.com/stretchr/testify/require" + "golang.org/x/exp/slog" + + "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/core" + "github.com/ava-labs/libevm/core/rawdb" + "github.com/ava-labs/libevm/libevm/ethtest" + "github.com/ava-labs/libevm/log" + "github.com/ava-labs/libevm/params" +) + +func TestSetupGenesisBlockWithStub(t *testing.T) { + // The original bug was due to [Stub] resulting in an error when being + // marshalled to JSON for [rawdb.WriteChainConfig], which resulted in + // [log.Crit] being called. + l := log.Root() + t.Cleanup(func() { log.SetDefault(l) }) + log.SetDefault(log.NewLogger(slog.NewTextHandler(os.Stderr, nil))) + + stub := &Stub{} + extras := stub.Register(t) + + config := ¶ms.ChainConfig{} + extras.ChainConfig.Set(config, stub) + gen := &core.Genesis{ + Config: config, + } + + db, cache, _ := ethtest.NewEmptyStateDB(t) + + // An eventual call to this function was the root cause. + rawdb.WriteChainConfig(db, common.Hash{1, 2, 3, 4, 5, 6}, config) + + // Also check calls to this function, which was the desired behaviour. + _, _, err := core.SetupGenesisBlock(db, cache.TrieDB(), gen) + require.NoError(t, err, "core.SetupGenesisBlock([%T with %T as registered %T extra])", gen, stub, config) +}