diff --git a/core/evm.go b/core/evm.go index b7d48afd76..3c6a6cf21b 100644 --- a/core/evm.go +++ b/core/evm.go @@ -41,6 +41,7 @@ import ( "github.com/ava-labs/libevm/core/state" "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/core/vm" + "github.com/ava-labs/libevm/libevm" "github.com/ava-labs/libevm/libevm/stateconf" "github.com/holiman/uint256" ) @@ -56,6 +57,16 @@ func RegisterExtras() { vm.RegisterHooks(hooks{}) } +// WithTempRegisteredExtras runs `fn` with temporary registration otherwise +// equivalent to a call to [RegisterExtras], but limited to the life of `fn`. +// +// This function is not intended for direct use. Use +// `evm.WithTempRegisteredLibEVMExtras()` instead as it calls this along with +// all other temporary-registration functions. +func WithTempRegisteredExtras(lock libevm.ExtrasLock, fn func() error) error { + return vm.WithTempRegisteredHooks(lock, hooks{}, fn) +} + type hooks struct{} // OverrideNewEVMArgs is a hook that is called in [vm.NewEVM]. diff --git a/core/extstate/statedb.go b/core/extstate/statedb.go index 30f499c9e0..c19f4b0414 100644 --- a/core/extstate/statedb.go +++ b/core/extstate/statedb.go @@ -10,6 +10,7 @@ import ( "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/state" "github.com/ava-labs/libevm/core/types" + "github.com/ava-labs/libevm/libevm" "github.com/ava-labs/libevm/libevm/stateconf" "github.com/holiman/uint256" @@ -28,6 +29,16 @@ func RegisterExtras() { state.RegisterExtras(normalizeStateKeysHook{}) } +// WithTempRegisteredExtras runs `fn` with temporary registration otherwise +// equivalent to a call to [RegisterExtras], but limited to the life of `fn`. +// +// This function is not intended for direct use. Use +// `evm.WithTempRegisteredLibEVMExtras()` instead as it calls this along with +// all other temporary-registration functions. +func WithTempRegisteredExtras(lock libevm.ExtrasLock, fn func() error) error { + return state.WithTempRegisteredExtras(lock, normalizeStateKeysHook{}, fn) +} + type normalizeStateKeysHook struct{} // TransformStateKey transforms all keys with [normalizeStateKey]. diff --git a/go.mod b/go.mod index 2760703e4d..608f6a6c9c 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/VictoriaMetrics/fastcache v1.12.1 github.com/ava-labs/avalanchego v1.13.6-0.20251003124629-84e9aebcfbc0 github.com/ava-labs/firewood-go-ethhash/ffi v0.0.12 - github.com/ava-labs/libevm v1.13.15-0.20251002164226-35926db4d661 + github.com/ava-labs/libevm v1.13.15-0.20251007153555-3ee649c91723 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc github.com/deckarep/golang-set/v2 v2.1.0 github.com/fjl/gencodec v0.1.1 diff --git a/go.sum b/go.sum index 8880cb2ebf..e55ef2a442 100644 --- a/go.sum +++ b/go.sum @@ -32,6 +32,8 @@ github.com/ava-labs/firewood-go-ethhash/ffi v0.0.12 h1:aMcrLbpJ/dyu2kZDf/Di/4JIW github.com/ava-labs/firewood-go-ethhash/ffi v0.0.12/go.mod h1:cq89ua3iiZ5wPBALTEQS5eG8DIZcs7ov6OiL4YR1BVY= github.com/ava-labs/libevm v1.13.15-0.20251002164226-35926db4d661 h1:lt4yQE1HMvxWrdD5RFj+h9kWUsZK2rmNohvkeQsbG9M= github.com/ava-labs/libevm v1.13.15-0.20251002164226-35926db4d661/go.mod h1:ivRC/KojP8sai7j8WnpXIReQpcRklL2bIzoysnjpARQ= +github.com/ava-labs/libevm v1.13.15-0.20251007153555-3ee649c91723 h1:VCPjG+9LOu1M74Eeuhhe536TzIXJEueHT83rgkkDfAY= +github.com/ava-labs/libevm v1.13.15-0.20251007153555-3ee649c91723/go.mod h1:ivRC/KojP8sai7j8WnpXIReQpcRklL2bIzoysnjpARQ= github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= diff --git a/params/config.go b/params/config.go index 23e1770834..ed85231172 100644 --- a/params/config.go +++ b/params/config.go @@ -32,6 +32,7 @@ import ( "github.com/ava-labs/coreth/params/extras" "github.com/ava-labs/coreth/utils" + "github.com/ava-labs/libevm/libevm" ethparams "github.com/ava-labs/libevm/params" ) @@ -46,7 +47,12 @@ var ( ) func init() { - WithTempRegisteredExtras(initialiseChainConfigs) + libevm.WithTemporaryExtrasLock(func(l libevm.ExtrasLock) error { + return WithTempRegisteredExtras(l, func() error { + initialiseChainConfigs() + return nil + }) + }) } var ( diff --git a/params/config_libevm.go b/params/config_libevm.go index 104b3b6f35..4bb4d112d4 100644 --- a/params/config_libevm.go +++ b/params/config_libevm.go @@ -7,6 +7,7 @@ import ( "math/big" "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/libevm" "github.com/ava-labs/coreth/params/extras" "github.com/ava-labs/coreth/precompile/modules" @@ -34,16 +35,22 @@ func RegisterExtras() { } // WithTempRegisteredExtras runs `fn` with temporary registration otherwise -// equivalent to a call to [RegisterExtras], but limited to the life of `fn`. It -// is not threadsafe. -func WithTempRegisteredExtras(fn func()) { +// equivalent to a call to [RegisterExtras], but limited to the life of `fn`. +// +// This function is not intended for direct use. Use +// `evm.WithTempRegisteredLibEVMExtras()` instead as it calls this along with +// all other temporary-registration functions. +func WithTempRegisteredExtras(lock libevm.ExtrasLock, fn func() error) error { old := payloads defer func() { payloads = old }() - ethparams.WithTempRegisteredExtras(extrasToRegister(), func(extras ethparams.ExtraPayloads[*extras.ChainConfig, RulesExtra]) { - payloads = extras - fn() - }) + return ethparams.WithTempRegisteredExtras( + lock, extrasToRegister(), + func(extras ethparams.ExtraPayloads[*extras.ChainConfig, RulesExtra]) error { + payloads = extras + return fn() + }, + ) } var payloads ethparams.ExtraPayloads[*extras.ChainConfig, RulesExtra] diff --git a/plugin/evm/customtypes/aliases_test.go b/plugin/evm/customtypes/aliases_test.go new file mode 100644 index 0000000000..ff2e1ab2d8 --- /dev/null +++ b/plugin/evm/customtypes/aliases_test.go @@ -0,0 +1,43 @@ +// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package customtypes + +import ( + "github.com/ava-labs/libevm/core/types" +) + +// TODO(arr4n) These tests were originally part of the `coreth/core/types` +// package so assume the presence of identifiers. Aliases reduce PR noise during +// the refactoring. + +type ( + AccessListTx = types.AccessListTx + Block = types.Block + BlockNonce = types.BlockNonce + Bloom = types.Bloom + Body = types.Body + DynamicFeeTx = types.DynamicFeeTx + Header = types.Header + LegacyTx = types.LegacyTx + Log = types.Log + Receipt = types.Receipt + ReceiptForStorage = types.ReceiptForStorage + Receipts = types.Receipts + Transaction = types.Transaction + Transactions = types.Transactions + Withdrawal = types.Withdrawal + Withdrawals = types.Withdrawals +) + +var ( + // Function aliases in production code MUST be `func` declarations, not + // variables; this is only acceptable in tests. + CopyHeader = types.CopyHeader + MustSignNewTx = types.MustSignNewTx + NewBlock = types.NewBlock + NewLondonSigner = types.NewLondonSigner + NewTransaction = types.NewTransaction +) + +const ReceiptStatusSuccessful = types.ReceiptStatusSuccessful diff --git a/plugin/evm/customtypes/block_ext_test.go b/plugin/evm/customtypes/block_ext_test.go index 762411c9f8..51d15b610c 100644 --- a/plugin/evm/customtypes/block_ext_test.go +++ b/plugin/evm/customtypes/block_ext_test.go @@ -20,11 +20,6 @@ import ( "github.com/ava-labs/coreth/utils" "github.com/ava-labs/coreth/utils/utilstest" - - // TODO(arr4n) These tests were originally part of the `coreth/core/types` - // package so assume the presence of identifiers. A dot-import reduces PR - // noise during the refactoring. - . "github.com/ava-labs/libevm/core/types" ) func TestCopyHeader(t *testing.T) { diff --git a/plugin/evm/customtypes/header_ext_test.go b/plugin/evm/customtypes/header_ext_test.go index 8791ec4a4a..1cd09849b1 100644 --- a/plugin/evm/customtypes/header_ext_test.go +++ b/plugin/evm/customtypes/header_ext_test.go @@ -19,11 +19,6 @@ import ( "github.com/stretchr/testify/require" "github.com/ava-labs/coreth/utils/utilstest" - - // TODO(arr4n) These tests were originally part of the `coreth/core/types` - // package so assume the presence of identifiers. A dot-import reduces PR - // noise during the refactoring. - . "github.com/ava-labs/libevm/core/types" ) func TestHeaderRLP(t *testing.T) { diff --git a/plugin/evm/customtypes/libevm.go b/plugin/evm/customtypes/libevm.go index 90886ccdfc..5476865c14 100644 --- a/plugin/evm/customtypes/libevm.go +++ b/plugin/evm/customtypes/libevm.go @@ -4,11 +4,18 @@ package customtypes import ( + "github.com/ava-labs/libevm/libevm" + ethtypes "github.com/ava-labs/libevm/core/types" ) var extras ethtypes.ExtraPayloads[*HeaderExtra, *BlockBodyExtra, isMultiCoin] +func setExtras(e ethtypes.ExtraPayloads[*HeaderExtra, *BlockBodyExtra, isMultiCoin]) { + extras = e + IsMultiCoinPayloads = e.StateAccount +} + // Register registers the types with libevm. It MUST NOT be called more than // once and therefore is only allowed to be used in tests and `package main`, to // avoid polluting other packages that transitively depend on this one but don't @@ -17,10 +24,28 @@ var extras ethtypes.ExtraPayloads[*HeaderExtra, *BlockBodyExtra, isMultiCoin] // Without a call to Register, none of the functionality of this package will // work, and most will simply panic. func Register() { - extras = ethtypes.RegisterExtras[ + setExtras(ethtypes.RegisterExtras[ HeaderExtra, *HeaderExtra, BlockBodyExtra, *BlockBodyExtra, isMultiCoin, - ]() - IsMultiCoinPayloads = extras.StateAccount + ]()) +} + +// WithTempRegisteredExtras runs `fn` with temporary registration otherwise +// equivalent to a call to [RegisterExtras], but limited to the life of `fn`. +// +// This function is not intended for direct use. Use +// `evm.WithTempRegisteredLibEVMExtras()` instead as it calls this along with +// all other temporary-registration functions. +func WithTempRegisteredExtras(lock libevm.ExtrasLock, fn func() error) error { + old := extras + defer setExtras(old) + + return ethtypes.WithTempRegisteredExtras[HeaderExtra, BlockBodyExtra, isMultiCoin]( + lock, + func(e ethtypes.ExtraPayloads[*HeaderExtra, *BlockBodyExtra, isMultiCoin]) error { + setExtras(e) + return fn() + }, + ) } diff --git a/plugin/evm/customtypes/rlp_fuzzer_test.go b/plugin/evm/customtypes/rlp_fuzzer_test.go index 18f7871c20..46d7e507b6 100644 --- a/plugin/evm/customtypes/rlp_fuzzer_test.go +++ b/plugin/evm/customtypes/rlp_fuzzer_test.go @@ -35,11 +35,6 @@ import ( "github.com/ava-labs/libevm/rlp" "github.com/holiman/uint256" - - // TODO(arr4n) These tests were originally part of the `coreth/core/types` - // package so assume the presence of identifiers. A dot-import reduces PR - // noise during the refactoring. - . "github.com/ava-labs/libevm/core/types" ) func decodeEncode(input []byte, val interface{}) error { diff --git a/plugin/evm/customtypes/types_test.go b/plugin/evm/customtypes/types_test.go index f14ca6d260..8563f7b112 100644 --- a/plugin/evm/customtypes/types_test.go +++ b/plugin/evm/customtypes/types_test.go @@ -35,11 +35,6 @@ import ( "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/crypto" "github.com/ava-labs/libevm/rlp" - - // TODO(arr4n) These tests were originally part of the `coreth/core/types` - // package so assume the presence of identifiers. A dot-import reduces PR - // noise during the refactoring. - . "github.com/ava-labs/libevm/core/types" ) func TestMain(m *testing.M) { diff --git a/plugin/evm/libevm.go b/plugin/evm/libevm.go index b1f7dff8e5..ae45a6d5f1 100644 --- a/plugin/evm/libevm.go +++ b/plugin/evm/libevm.go @@ -4,6 +4,8 @@ package evm import ( + "github.com/ava-labs/libevm/libevm" + "github.com/ava-labs/coreth/core" "github.com/ava-labs/coreth/core/extstate" "github.com/ava-labs/coreth/params" @@ -24,3 +26,16 @@ func RegisterAllLibEVMExtras() { extstate.RegisterExtras() params.RegisterExtras() } + +// WithTempRegisteredLibEVMExtras runs `fn` with temporary registration +// otherwise equivalent to a call to [RegisterAllLibEVMExtras], but limited to +// the life of `fn`. +func WithTempRegisteredLibEVMExtras(lock libevm.ExtrasLock, fn func() error) error { + return core.WithTempRegisteredExtras(lock, func() error { + return customtypes.WithTempRegisteredExtras(lock, func() error { + return extstate.WithTempRegisteredExtras(lock, func() error { + return params.WithTempRegisteredExtras(lock, fn) + }) + }) + }) +} diff --git a/plugin/evm/libevm_test.go b/plugin/evm/libevm_test.go new file mode 100644 index 0000000000..c1ca5f0ad8 --- /dev/null +++ b/plugin/evm/libevm_test.go @@ -0,0 +1,87 @@ +// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +// These tests are run in a different package because the primary `evm` tests +// leak goroutines that result in race conditions with the temporary +// registration of extras, which is intended to be done separately. +package evm_test + +import ( + "testing" + + "github.com/ava-labs/libevm/core/state" + "github.com/ava-labs/libevm/core/types" + "github.com/ava-labs/libevm/core/vm" + "github.com/ava-labs/libevm/libevm" + "github.com/ava-labs/libevm/params" + "github.com/stretchr/testify/require" + + "github.com/ava-labs/coreth/plugin/evm" + "github.com/ava-labs/coreth/plugin/evm/customtypes" + + cparams "github.com/ava-labs/coreth/params" +) + +func TestWithTempRegisteredLibEVMExtras(t *testing.T) { + params.TestOnlyClearRegisteredExtras() + state.TestOnlyClearRegisteredExtras() + types.TestOnlyClearRegisteredExtras() + vm.TestOnlyClearRegisteredHooks() + + var reRegistered bool + t.Cleanup(func() { + if !reRegistered { + evm.RegisterAllLibEVMExtras() + } + }) + + payloadTests := map[string]func(t *testing.T){ + "customtypes": func(t *testing.T) { + t.Helper() + require.False(t, customtypes.IsMultiCoin(&types.StateAccount{})) + }, + "params": func(t *testing.T) { + t.Helper() + require.False(t, cparams.GetRulesExtra(params.Rules{}).IsEtna) + }, + } + + t.Run("with_temp_registration", func(t *testing.T) { + err := libevm.WithTemporaryExtrasLock(func(lock libevm.ExtrasLock) error { + return evm.WithTempRegisteredLibEVMExtras(lock, func() error { //nolint:whitespace // Avoid visual crowding due to nested calls + + t.Run("payloads", func(t *testing.T) { + for pkg, fn := range payloadTests { + t.Run(pkg, fn) + } + }) + return nil + }) + }) + require.NoError(t, err) + }) + + // These are deliberately placed after the tests of temporary registration, + // to demonstrate that (a) they are indeed temporary, and (b) they would + // otherwise panic. + t.Run("without_registration", func(t *testing.T) { + t.Run("payloads", func(t *testing.T) { + for pkg, fn := range payloadTests { + t.Run(pkg, func(t *testing.T) { + require.Panics(t, func() { fn(t) }) + }) + } + }) + }) + + evm.RegisterAllLibEVMExtras() + reRegistered = true + + t.Run("with_permanent_registration", func(t *testing.T) { + t.Run("payloads", func(t *testing.T) { + for pkg, fn := range payloadTests { + t.Run(pkg, fn) + } + }) + }) +} diff --git a/tests/init.go b/tests/init.go index 2f0149318c..b588e66584 100644 --- a/tests/init.go +++ b/tests/init.go @@ -37,13 +37,19 @@ import ( "github.com/ava-labs/coreth/params" "github.com/ava-labs/coreth/params/extras" "github.com/ava-labs/coreth/utils" + "github.com/ava-labs/libevm/libevm" ) // Forks table defines supported forks and their chain config. var Forks map[string]*params.ChainConfig func init() { - params.WithTempRegisteredExtras(initializeForks) + libevm.WithTemporaryExtrasLock(func(l libevm.ExtrasLock) error { + return params.WithTempRegisteredExtras(l, func() error { + initializeForks() + return nil + }) + }) } // initializeForks MUST be called inside [params.WithTempRegisteredExtras] to