diff --git a/plugin/evm/config.go b/plugin/evm/config.go new file mode 100644 index 0000000000..5ad18aa2f2 --- /dev/null +++ b/plugin/evm/config.go @@ -0,0 +1,21 @@ +// (c) 2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package evm + +import ( + "github.com/ava-labs/coreth/core/txpool/legacypool" + "github.com/ava-labs/coreth/plugin/evm/config" +) + +// defaultTxPoolConfig uses [legacypool.DefaultConfig] to make a [config.TxPoolConfig] +// that can be passed to [config.Config.SetDefaults]. +var defaultTxPoolConfig = config.TxPoolConfig{ + PriceLimit: legacypool.DefaultConfig.PriceLimit, + PriceBump: legacypool.DefaultConfig.PriceBump, + AccountSlots: legacypool.DefaultConfig.AccountSlots, + GlobalSlots: legacypool.DefaultConfig.GlobalSlots, + AccountQueue: legacypool.DefaultConfig.AccountQueue, + GlobalQueue: legacypool.DefaultConfig.GlobalQueue, + Lifetime: legacypool.DefaultConfig.Lifetime, +} diff --git a/plugin/evm/config/config.go b/plugin/evm/config/config.go index a92405bcb5..f5bf644657 100644 --- a/plugin/evm/config/config.go +++ b/plugin/evm/config/config.go @@ -9,8 +9,6 @@ import ( "time" "github.com/ava-labs/avalanchego/utils/constants" - "github.com/ava-labs/coreth/core/txpool/legacypool" - "github.com/ava-labs/coreth/eth" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/spf13/cast" @@ -224,28 +222,37 @@ type Config struct { HttpBodyLimit uint64 `json:"http-body-limit"` } +// TxPoolConfig contains the transaction pool config to be passed +// to [Config.SetDefaults]. +type TxPoolConfig struct { + PriceLimit uint64 + PriceBump uint64 + AccountSlots uint64 + GlobalSlots uint64 + AccountQueue uint64 + GlobalQueue uint64 + Lifetime time.Duration +} + // EthAPIs returns an array of strings representing the Eth APIs that should be enabled func (c Config) EthAPIs() []string { return c.EnabledEthAPIs } -func (c Config) EthBackendSettings() eth.Settings { - return eth.Settings{MaxBlocksPerRequest: c.MaxBlocksPerRequest} -} - -func (c *Config) SetDefaults() { +func (c *Config) SetDefaults(txPoolConfig TxPoolConfig) { c.EnabledEthAPIs = defaultEnabledAPIs c.RPCGasCap = defaultRpcGasCap c.RPCTxFeeCap = defaultRpcTxFeeCap c.MetricsExpensiveEnabled = defaultMetricsExpensiveEnabled - c.TxPoolPriceLimit = legacypool.DefaultConfig.PriceLimit - c.TxPoolPriceBump = legacypool.DefaultConfig.PriceBump - c.TxPoolAccountSlots = legacypool.DefaultConfig.AccountSlots - c.TxPoolGlobalSlots = legacypool.DefaultConfig.GlobalSlots - c.TxPoolAccountQueue = legacypool.DefaultConfig.AccountQueue - c.TxPoolGlobalQueue = legacypool.DefaultConfig.GlobalQueue - c.TxPoolLifetime.Duration = legacypool.DefaultConfig.Lifetime + // TxPool settings + c.TxPoolPriceLimit = txPoolConfig.PriceLimit + c.TxPoolPriceBump = txPoolConfig.PriceBump + c.TxPoolAccountSlots = txPoolConfig.AccountSlots + c.TxPoolGlobalSlots = txPoolConfig.GlobalSlots + c.TxPoolAccountQueue = txPoolConfig.AccountQueue + c.TxPoolGlobalQueue = txPoolConfig.GlobalQueue + c.TxPoolLifetime.Duration = txPoolConfig.Lifetime c.APIMaxDuration.Duration = defaultApiMaxDuration c.WSCPURefillRate.Duration = defaultWsCpuRefillRate diff --git a/plugin/evm/imports_test.go b/plugin/evm/imports_test.go new file mode 100644 index 0000000000..8b7ac4c4a9 --- /dev/null +++ b/plugin/evm/imports_test.go @@ -0,0 +1,72 @@ +// (c) 2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package evm + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" + "golang.org/x/tools/go/packages" +) + +// getDependencies takes a fully qualified package name and returns a map of all +// its recursive package imports (including itself) in the same format. +func getDependencies(packageName string) (map[string]struct{}, error) { + // Configure the load mode to include dependencies + cfg := &packages.Config{Mode: packages.NeedDeps | packages.NeedImports | packages.NeedName | packages.NeedModule} + pkgs, err := packages.Load(cfg, packageName) + if err != nil { + return nil, fmt.Errorf("failed to load package: %v", err) + } + + if len(pkgs) == 0 || pkgs[0].Errors != nil { + return nil, fmt.Errorf("failed to load package %s", packageName) + } + + deps := make(map[string]struct{}) + var collectDeps func(pkg *packages.Package) + collectDeps = func(pkg *packages.Package) { + if _, ok := deps[pkg.PkgPath]; ok { + return // Avoid re-processing the same dependency + } + deps[pkg.PkgPath] = struct{}{} + for _, dep := range pkg.Imports { + collectDeps(dep) + } + } + + // Start collecting dependencies + collectDeps(pkgs[0]) + return deps, nil +} + +func TestMustNotImport(t *testing.T) { + withRepo := func(pkg string) string { + const repo = "github.com/ava-labs/coreth" + return fmt.Sprintf("%s/%s", repo, pkg) + } + mustNotImport := map[string][]string{ + // The following sub-packages of plugin/evm must not import core, core/vm + // so clients (e.g., wallets, e2e tests) can import them without pulling in + // the entire VM logic. + // Importing these packages configures libevm globally and it is not + // possible to do so for both coreth and subnet-evm, where the client may + // wish to connect to multiple chains. + "plugin/evm/atomic": {"core", "core/vm"}, + "plugin/evm/client": {"core", "core/vm"}, + "plugin/evm/config": {"core", "core/vm"}, + } + + for packageName, forbiddenImports := range mustNotImport { + imports, err := getDependencies(withRepo(packageName)) + require.NoError(t, err) + + for _, forbiddenImport := range forbiddenImports { + fullForbiddenImport := withRepo(forbiddenImport) + _, found := imports[fullForbiddenImport] + require.False(t, found, "package %s must not import %s, check output of go list -f '{{ .Deps }}' \"%s\" ", packageName, fullForbiddenImport, withRepo(packageName)) + } + } +} diff --git a/plugin/evm/vm.go b/plugin/evm/vm.go index 89c8ea4629..eebbd4a7ce 100644 --- a/plugin/evm/vm.go +++ b/plugin/evm/vm.go @@ -339,7 +339,7 @@ func (vm *VM) Initialize( fxs []*commonEng.Fx, appSender commonEng.AppSender, ) error { - vm.config.SetDefaults() + vm.config.SetDefaults(defaultTxPoolConfig) if len(configBytes) > 0 { if err := json.Unmarshal(configBytes, &vm.config); err != nil { return fmt.Errorf("failed to unmarshal config %s: %w", string(configBytes), err) @@ -660,7 +660,7 @@ func (vm *VM) initializeChain(lastAcceptedHash common.Hash) error { &vm.ethConfig, &EthPushGossiper{vm: vm}, vm.chaindb, - vm.config.EthBackendSettings(), + eth.Settings{MaxBlocksPerRequest: vm.config.MaxBlocksPerRequest}, lastAcceptedHash, dummy.NewFakerWithClock(callbacks, &vm.clock), &vm.clock, diff --git a/plugin/evm/vm_test.go b/plugin/evm/vm_test.go index 2a79f3fb6f..507c8d3a94 100644 --- a/plugin/evm/vm_test.go +++ b/plugin/evm/vm_test.go @@ -402,7 +402,7 @@ func TestVMConfigDefaults(t *testing.T) { _, vm, _, _, _ := GenesisVM(t, false, "", configJSON, "") var vmConfig config.Config - vmConfig.SetDefaults() + vmConfig.SetDefaults(defaultTxPoolConfig) vmConfig.RPCTxFeeCap = txFeeCap vmConfig.EnabledEthAPIs = enabledEthAPIs require.Equal(t, vmConfig, vm.config, "VM Config should match default with overrides") @@ -414,7 +414,7 @@ func TestVMNilConfig(t *testing.T) { // VM Config should match defaults if no config is passed in var vmConfig config.Config - vmConfig.SetDefaults() + vmConfig.SetDefaults(defaultTxPoolConfig) require.Equal(t, vmConfig, vm.config, "VM Config should match default config") require.NoError(t, vm.Shutdown(context.Background())) }