From faef2454fcfeaaa0fb63706a5a743b4640c20582 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Wed, 29 Apr 2026 16:44:09 +0200 Subject: [PATCH 1/7] core/vm: global cache for jumpdest bitmaps --- core/vm/evm.go | 2 +- core/vm/jumpdests.go | 29 ++++++++++++++++++++++++++++- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/core/vm/evm.go b/core/vm/evm.go index 59e301c0a7..579c81ed0a 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -140,7 +140,7 @@ func NewEVM(blockCtx BlockContext, statedb StateDB, chainConfig *params.ChainCon Config: config, chainConfig: chainConfig, chainRules: chainConfig.Rules(blockCtx.BlockNumber, blockCtx.Random != nil, blockCtx.Time), - jumpDests: newMapJumpDests(), + jumpDests: newGlobalJumpDests(), } evm.precompiles = activePrecompiledContracts(evm.chainRules) diff --git a/core/vm/jumpdests.go b/core/vm/jumpdests.go index 1a30c1943f..3c962fba77 100644 --- a/core/vm/jumpdests.go +++ b/core/vm/jumpdests.go @@ -16,7 +16,11 @@ package vm -import "github.com/ethereum/go-ethereum/common" +import ( + "sync" + + "github.com/ethereum/go-ethereum/common" +) // JumpDestCache represents the cache of jumpdest analysis results. type JumpDestCache interface { @@ -45,3 +49,26 @@ func (j mapJumpDests) Load(codeHash common.Hash) (BitVec, bool) { func (j mapJumpDests) Store(codeHash common.Hash, vec BitVec) { j[codeHash] = vec } + +// globalJumpDestCache is a global cache of JUMPDEST bitmaps. +var globalJumpDestCache sync.Map + +type globalJumpDests struct{} + +// newGlobalJumpDests returns a JumpDestCache backed by the process-global +// sync.Map. All callers share the same backing map. +func newGlobalJumpDests() JumpDestCache { + return globalJumpDests{} +} + +func (globalJumpDests) Load(codeHash common.Hash) (BitVec, bool) { + v, ok := globalJumpDestCache.Load(codeHash) + if !ok { + return nil, false + } + return v.(BitVec), true +} + +func (globalJumpDests) Store(codeHash common.Hash, vec BitVec) { + globalJumpDestCache.Store(codeHash, vec) +} From 3f49929091bd5271f0d1ae7f84019df78fe46c12 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Thu, 30 Apr 2026 08:47:14 +0000 Subject: [PATCH 2/7] Add cap of 16k contracts --- core/vm/evm.go | 2 +- core/vm/jumpdests.go | 32 ++++++++++++++------------------ 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/core/vm/evm.go b/core/vm/evm.go index c0a649cf1d..81de3f2d7a 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -142,7 +142,7 @@ func NewEVM(blockCtx BlockContext, statedb StateDB, chainConfig *params.ChainCon Config: config, chainConfig: chainConfig, chainRules: chainConfig.Rules(blockCtx.BlockNumber, blockCtx.Random != nil, blockCtx.Time), - jumpDests: newGlobalJumpDests(), + jumpDests: globalJumpDests, arena: newArena(), } evm.precompiles = activePrecompiledContracts(evm.chainRules) diff --git a/core/vm/jumpdests.go b/core/vm/jumpdests.go index 3c962fba77..576be7e9e5 100644 --- a/core/vm/jumpdests.go +++ b/core/vm/jumpdests.go @@ -17,11 +17,14 @@ package vm import ( - "sync" - "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/lru" ) +// globalJumpDestCacheSize caps the global cache. Worst case ~48MB (24KB code +// → ~3KB bitmap × 16384 entries). +const globalJumpDestCacheSize = 16384 + // JumpDestCache represents the cache of jumpdest analysis results. type JumpDestCache interface { // Load retrieves the cached jumpdest analysis for the given code hash. @@ -50,25 +53,18 @@ func (j mapJumpDests) Store(codeHash common.Hash, vec BitVec) { j[codeHash] = vec } -// globalJumpDestCache is a global cache of JUMPDEST bitmaps. -var globalJumpDestCache sync.Map +// globalJumpDests is a process-global LRU of JUMPDEST bitmaps, shared across +// every EVM instance and keyed by the immutable contract code hash. +var globalJumpDests = &lruJumpDests{cache: lru.NewCache[common.Hash, BitVec](globalJumpDestCacheSize)} -type globalJumpDests struct{} - -// newGlobalJumpDests returns a JumpDestCache backed by the process-global -// sync.Map. All callers share the same backing map. -func newGlobalJumpDests() JumpDestCache { - return globalJumpDests{} +type lruJumpDests struct { + cache *lru.Cache[common.Hash, BitVec] } -func (globalJumpDests) Load(codeHash common.Hash) (BitVec, bool) { - v, ok := globalJumpDestCache.Load(codeHash) - if !ok { - return nil, false - } - return v.(BitVec), true +func (j *lruJumpDests) Load(codeHash common.Hash) (BitVec, bool) { + return j.cache.Get(codeHash) } -func (globalJumpDests) Store(codeHash common.Hash, vec BitVec) { - globalJumpDestCache.Store(codeHash, vec) +func (j *lruJumpDests) Store(codeHash common.Hash, vec BitVec) { + j.cache.Add(codeHash, vec) } From 5c4a89562d3d42854c6725b242720f733df226f9 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Thu, 7 May 2026 19:46:51 +0000 Subject: [PATCH 3/7] core, eth: move jumpdest cache to BlockChain Replaces the process-global LRU with a chain-owned, byte-bounded SizeConstrainedCache (128MB) shared between block processor and prefetcher. Threaded through Process/Prefetch as an optional argument; RPC, tracing, and stateless callers pass nil and the EVM falls back to its per-instance map. Adds chain/cache/jumpdest/{hit,miss} meters. --- core/blockchain.go | 6 ++-- core/blockchain_test.go | 2 +- core/jumpdest.go | 63 ++++++++++++++++++++++++++++++++++++++++ core/state_prefetcher.go | 5 +++- core/state_processor.go | 5 +++- core/stateless.go | 2 +- core/types.go | 4 +-- core/vm/evm.go | 2 +- core/vm/jumpdests.go | 25 +--------------- eth/state_accessor.go | 2 +- 10 files changed, 82 insertions(+), 34 deletions(-) create mode 100644 core/jumpdest.go diff --git a/core/blockchain.go b/core/blockchain.go index 296ef6bc16..81700f0e88 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -324,6 +324,7 @@ type BlockChain struct { flushInterval atomic.Int64 // Time interval (processing time) after which to flush a state triedb *triedb.Database // The database handler for maintaining trie nodes. codedb *state.CodeDB // The database handler for maintaining contract codes. + jumpDest *JumpDestCache // Shared JUMPDEST analysis cache for block processing txIndexer *txIndexer // Transaction indexer, might be nil if not enabled hc *HeaderChain @@ -406,6 +407,7 @@ func NewBlockChain(db ethdb.Database, genesis *Genesis, engine consensus.Engine, db: db, triedb: triedb, codedb: state.NewCodeDB(db), + jumpDest: NewJumpDestCache(), triegc: prque.New[int64, common.Hash](nil), chainmu: syncx.NewClosableMutex(), bodyCache: lru.NewCache[common.Hash, *types.Body](bodyCacheLimit), @@ -2176,7 +2178,7 @@ func (bc *BlockChain) ProcessBlock(ctx context.Context, parentRoot common.Hash, // Disable tracing for prefetcher executions. vmCfg := bc.cfg.VmConfig vmCfg.Tracer = nil - bc.prefetcher.Prefetch(block, throwaway, vmCfg, &interrupt) + bc.prefetcher.Prefetch(block, throwaway, bc.jumpDest, vmCfg, &interrupt) blockPrefetchExecuteTimer.Update(time.Since(start)) if interrupt.Load() { @@ -2222,7 +2224,7 @@ func (bc *BlockChain) ProcessBlock(ctx context.Context, parentRoot common.Hash, // Process block using the parent state as reference point pstart := time.Now() pctx, _, spanEnd := telemetry.StartSpan(ctx, "bc.processor.Process") - res, err := bc.processor.Process(pctx, block, statedb, bc.cfg.VmConfig) + res, err := bc.processor.Process(pctx, block, statedb, bc.jumpDest, bc.cfg.VmConfig) spanEnd(&err) if err != nil { bc.reportBadBlock(block, res, err) diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 1a2ee45291..a8ddf5caa8 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -160,7 +160,7 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error { if err != nil { return err } - res, err := blockchain.processor.Process(context.Background(), block, statedb, vm.Config{}) + res, err := blockchain.processor.Process(context.Background(), block, statedb, nil, vm.Config{}) if err != nil { blockchain.reportBadBlock(block, res, err) return err diff --git a/core/jumpdest.go b/core/jumpdest.go new file mode 100644 index 0000000000..cf8512f22e --- /dev/null +++ b/core/jumpdest.go @@ -0,0 +1,63 @@ +// Copyright 2026 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it 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 go-ethereum library is distributed in the hope that it 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 core + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/lru" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/metrics" +) + +var ( + jumpDestHitMeter = metrics.NewRegisteredMeter("chain/cache/jumpdest/hit", nil) + jumpDestMissMeter = metrics.NewRegisteredMeter("chain/cache/jumpdest/miss", nil) +) + +// jumpDestCacheSize is the total memory budget granted to the jumpdest +// analysis cache. +const jumpDestCacheSize = 128 * 1024 * 1024 + +// JumpDestCache is a thread-safe, byte-bounded LRU of JUMPDEST analysis +// bitmaps. It is owned by BlockChain and shared across block processing and +// prefetching, keyed by the immutable contract code hash. +type JumpDestCache struct { + cache *lru.SizeConstrainedCache[common.Hash, vm.BitVec] +} + +// NewJumpDestCache constructs the analysis cache. +func NewJumpDestCache() *JumpDestCache { + return &JumpDestCache{ + cache: lru.NewSizeConstrainedCache[common.Hash, vm.BitVec](jumpDestCacheSize), + } +} + +// Load retrieves the cached jumpdest analysis for the given code hash. +func (c *JumpDestCache) Load(hash common.Hash) (vm.BitVec, bool) { + v, ok := c.cache.Get(hash) + if ok { + jumpDestHitMeter.Mark(1) + } else { + jumpDestMissMeter.Mark(1) + } + return v, ok +} + +// Store saves the jumpdest analysis for the given code hash. +func (c *JumpDestCache) Store(hash common.Hash, b vm.BitVec) { + c.cache.Add(hash, b) +} diff --git a/core/state_prefetcher.go b/core/state_prefetcher.go index ed292d0beb..b36f8b7bba 100644 --- a/core/state_prefetcher.go +++ b/core/state_prefetcher.go @@ -49,7 +49,7 @@ func newStatePrefetcher(config *params.ChainConfig, chain *HeaderChain) *statePr // Prefetch processes the state changes according to the Ethereum rules by running // the transaction messages using the statedb, but any changes are discarded. The // only goal is to warm the state caches. -func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, cfg vm.Config, interrupt *atomic.Bool) { +func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, jumpDests *JumpDestCache, cfg vm.Config, interrupt *atomic.Bool) { var ( fails atomic.Int64 header = block.Header() @@ -94,6 +94,9 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, c // Execute the message to preload the implicit touched states evm := vm.NewEVM(NewEVMBlockContext(header, p.chain, nil), stateCpy, p.config, cfg) defer evm.Release() + if jumpDests != nil { + evm.SetJumpDestCache(jumpDests) + } // Convert the transaction into an executable message and pre-cache its sender msg, err := TransactionToMessage(tx, signer, header.BaseFee) diff --git a/core/state_processor.go b/core/state_processor.go index 54ebbd047b..0994d19226 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -61,7 +61,7 @@ func (p *StateProcessor) chainConfig() *params.ChainConfig { // Process returns the receipts and logs accumulated during the process and // returns the amount of gas that was used in the process. If any of the // transactions failed to execute due to insufficient gas it will return an error. -func (p *StateProcessor) Process(ctx context.Context, block *types.Block, statedb *state.StateDB, cfg vm.Config) (*ProcessResult, error) { +func (p *StateProcessor) Process(ctx context.Context, block *types.Block, statedb *state.StateDB, jumpDests *JumpDestCache, cfg vm.Config) (*ProcessResult, error) { var ( config = p.chainConfig() receipts = make(types.Receipts, 0, len(block.Transactions())) @@ -89,6 +89,9 @@ func (p *StateProcessor) Process(ctx context.Context, block *types.Block, stated context = NewEVMBlockContext(header, p.chain, nil) evm := vm.NewEVM(context, tracingStateDB, config, cfg) defer evm.Release() + if jumpDests != nil { + evm.SetJumpDestCache(jumpDests) + } if beaconRoot := block.BeaconRoot(); beaconRoot != nil { ProcessBeaconBlockRoot(*beaconRoot, evm) diff --git a/core/stateless.go b/core/stateless.go index 86d4dc304b..805ef7ffbe 100644 --- a/core/stateless.go +++ b/core/stateless.go @@ -68,7 +68,7 @@ func ExecuteStateless(ctx context.Context, config *params.ChainConfig, vmconfig validator := NewBlockValidator(config, nil) // No chain, we only validate the state, not the block // Run the stateless blocks processing and self-validate certain fields - res, err := processor.Process(ctx, block, db, vmconfig) + res, err := processor.Process(ctx, block, db, nil, vmconfig) if err != nil { return common.Hash{}, common.Hash{}, err } diff --git a/core/types.go b/core/types.go index 87bbfcff58..935202a441 100644 --- a/core/types.go +++ b/core/types.go @@ -41,7 +41,7 @@ type Prefetcher interface { // Prefetch processes the state changes according to the Ethereum rules by running // the transaction messages using the statedb, but any changes are discarded. The // only goal is to pre-cache transaction signatures and state trie nodes. - Prefetch(block *types.Block, statedb *state.StateDB, cfg vm.Config, interrupt *atomic.Bool) + Prefetch(block *types.Block, statedb *state.StateDB, jumpDests *JumpDestCache, cfg vm.Config, interrupt *atomic.Bool) } // Processor is an interface for processing blocks using a given initial state. @@ -49,7 +49,7 @@ type Processor interface { // Process processes the state changes according to the Ethereum rules by running // the transaction messages using the statedb and applying any rewards to both // the processor (coinbase) and any included uncles. - Process(ctx context.Context, block *types.Block, statedb *state.StateDB, cfg vm.Config) (*ProcessResult, error) + Process(ctx context.Context, block *types.Block, statedb *state.StateDB, jumpDests *JumpDestCache, cfg vm.Config) (*ProcessResult, error) } // ProcessResult contains the values computed by Process. diff --git a/core/vm/evm.go b/core/vm/evm.go index 81de3f2d7a..26b2f73a00 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -142,7 +142,7 @@ func NewEVM(blockCtx BlockContext, statedb StateDB, chainConfig *params.ChainCon Config: config, chainConfig: chainConfig, chainRules: chainConfig.Rules(blockCtx.BlockNumber, blockCtx.Random != nil, blockCtx.Time), - jumpDests: globalJumpDests, + jumpDests: newMapJumpDests(), arena: newArena(), } evm.precompiles = activePrecompiledContracts(evm.chainRules) diff --git a/core/vm/jumpdests.go b/core/vm/jumpdests.go index 576be7e9e5..1a30c1943f 100644 --- a/core/vm/jumpdests.go +++ b/core/vm/jumpdests.go @@ -16,14 +16,7 @@ package vm -import ( - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/lru" -) - -// globalJumpDestCacheSize caps the global cache. Worst case ~48MB (24KB code -// → ~3KB bitmap × 16384 entries). -const globalJumpDestCacheSize = 16384 +import "github.com/ethereum/go-ethereum/common" // JumpDestCache represents the cache of jumpdest analysis results. type JumpDestCache interface { @@ -52,19 +45,3 @@ func (j mapJumpDests) Load(codeHash common.Hash) (BitVec, bool) { func (j mapJumpDests) Store(codeHash common.Hash, vec BitVec) { j[codeHash] = vec } - -// globalJumpDests is a process-global LRU of JUMPDEST bitmaps, shared across -// every EVM instance and keyed by the immutable contract code hash. -var globalJumpDests = &lruJumpDests{cache: lru.NewCache[common.Hash, BitVec](globalJumpDestCacheSize)} - -type lruJumpDests struct { - cache *lru.Cache[common.Hash, BitVec] -} - -func (j *lruJumpDests) Load(codeHash common.Hash) (BitVec, bool) { - return j.cache.Get(codeHash) -} - -func (j *lruJumpDests) Store(codeHash common.Hash, vec BitVec) { - j.cache.Add(codeHash, vec) -} diff --git a/eth/state_accessor.go b/eth/state_accessor.go index a806a4fc56..f7ecff1488 100644 --- a/eth/state_accessor.go +++ b/eth/state_accessor.go @@ -151,7 +151,7 @@ func (eth *Ethereum) hashState(ctx context.Context, block *types.Block, base *st if current = eth.blockchain.GetBlockByNumber(next); current == nil { return nil, nil, fmt.Errorf("block #%d not found", next) } - _, err := eth.blockchain.Processor().Process(ctx, current, statedb, vm.Config{}) + _, err := eth.blockchain.Processor().Process(ctx, current, statedb, nil, vm.Config{}) if err != nil { return nil, nil, fmt.Errorf("processing block %d failed: %v", current.NumberU64(), err) } From 3ca07b6750f06f44ca080a5bb9b77a293d6de525 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Mon, 11 May 2026 09:57:16 +0000 Subject: [PATCH 4/7] core: shard jumpdest cache into 8 buckets of 8MB --- core/jumpdest.go | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/core/jumpdest.go b/core/jumpdest.go index cf8512f22e..50f93cc61b 100644 --- a/core/jumpdest.go +++ b/core/jumpdest.go @@ -28,27 +28,40 @@ var ( jumpDestMissMeter = metrics.NewRegisteredMeter("chain/cache/jumpdest/miss", nil) ) -// jumpDestCacheSize is the total memory budget granted to the jumpdest -// analysis cache. -const jumpDestCacheSize = 128 * 1024 * 1024 +const ( + // jumpDestBuckets is the number of independent LRU shards. Code hashes + // are dispatched by the low bits of the first byte to spread load across + // shards and reduce mutex contention from the parallel prefetcher. + jumpDestBuckets = 8 + + // jumpDestBucketSize is the per-shard byte budget. Total cache size is + // jumpDestBuckets * jumpDestBucketSize. + jumpDestBucketSize = 8 * 1024 * 1024 +) // JumpDestCache is a thread-safe, byte-bounded LRU of JUMPDEST analysis -// bitmaps. It is owned by BlockChain and shared across block processing and -// prefetching, keyed by the immutable contract code hash. +// bitmaps, sharded into independent buckets to reduce lock contention. It is +// owned by BlockChain and shared across block processing and prefetching, +// keyed by the immutable contract code hash. type JumpDestCache struct { - cache *lru.SizeConstrainedCache[common.Hash, vm.BitVec] + buckets [jumpDestBuckets]struct { + dest *lru.SizeConstrainedCache[common.Hash, vm.BitVec] + } } // NewJumpDestCache constructs the analysis cache. func NewJumpDestCache() *JumpDestCache { - return &JumpDestCache{ - cache: lru.NewSizeConstrainedCache[common.Hash, vm.BitVec](jumpDestCacheSize), + c := new(JumpDestCache) + for i := range c.buckets { + c.buckets[i].dest = lru.NewSizeConstrainedCache[common.Hash, vm.BitVec](jumpDestBucketSize) } + return c } // Load retrieves the cached jumpdest analysis for the given code hash. func (c *JumpDestCache) Load(hash common.Hash) (vm.BitVec, bool) { - v, ok := c.cache.Get(hash) + bucket := &c.buckets[hash[0]&(jumpDestBuckets-1)] + v, ok := bucket.dest.Get(hash) if ok { jumpDestHitMeter.Mark(1) } else { @@ -59,5 +72,6 @@ func (c *JumpDestCache) Load(hash common.Hash) (vm.BitVec, bool) { // Store saves the jumpdest analysis for the given code hash. func (c *JumpDestCache) Store(hash common.Hash, b vm.BitVec) { - c.cache.Add(hash, b) + bucket := &c.buckets[hash[0]&(jumpDestBuckets-1)] + bucket.dest.Add(hash, b) } From 35d19278e7be0c4e34811815f1c77117841f179c Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Mon, 11 May 2026 11:04:32 +0000 Subject: [PATCH 5/7] minor --- core/jumpdest.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/jumpdest.go b/core/jumpdest.go index 50f93cc61b..e7f500dbe9 100644 --- a/core/jumpdest.go +++ b/core/jumpdest.go @@ -34,8 +34,7 @@ const ( // shards and reduce mutex contention from the parallel prefetcher. jumpDestBuckets = 8 - // jumpDestBucketSize is the per-shard byte budget. Total cache size is - // jumpDestBuckets * jumpDestBucketSize. + // jumpDestBucketSize is the per-shard byte budget. jumpDestBucketSize = 8 * 1024 * 1024 ) From b9999244e56e123390a7edf1383f88c39ba7fee6 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Mon, 11 May 2026 15:48:01 +0000 Subject: [PATCH 6/7] interface for public api --- core/blockchain.go | 2 +- core/jumpdest.go | 12 ++++++------ core/state_prefetcher.go | 2 +- core/state_processor.go | 2 +- core/types.go | 4 ++-- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index 81700f0e88..897a05e442 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -324,7 +324,7 @@ type BlockChain struct { flushInterval atomic.Int64 // Time interval (processing time) after which to flush a state triedb *triedb.Database // The database handler for maintaining trie nodes. codedb *state.CodeDB // The database handler for maintaining contract codes. - jumpDest *JumpDestCache // Shared JUMPDEST analysis cache for block processing + jumpDest vm.JumpDestCache // Shared JUMPDEST analysis cache for block processing txIndexer *txIndexer // Transaction indexer, might be nil if not enabled hc *HeaderChain diff --git a/core/jumpdest.go b/core/jumpdest.go index e7f500dbe9..d2c861b70f 100644 --- a/core/jumpdest.go +++ b/core/jumpdest.go @@ -38,19 +38,19 @@ const ( jumpDestBucketSize = 8 * 1024 * 1024 ) -// JumpDestCache is a thread-safe, byte-bounded LRU of JUMPDEST analysis +// shardedJumpDestCache is a thread-safe, byte-bounded LRU of JUMPDEST analysis // bitmaps, sharded into independent buckets to reduce lock contention. It is // owned by BlockChain and shared across block processing and prefetching, // keyed by the immutable contract code hash. -type JumpDestCache struct { +type shardedJumpDestCache struct { buckets [jumpDestBuckets]struct { dest *lru.SizeConstrainedCache[common.Hash, vm.BitVec] } } // NewJumpDestCache constructs the analysis cache. -func NewJumpDestCache() *JumpDestCache { - c := new(JumpDestCache) +func NewJumpDestCache() vm.JumpDestCache { + c := new(shardedJumpDestCache) for i := range c.buckets { c.buckets[i].dest = lru.NewSizeConstrainedCache[common.Hash, vm.BitVec](jumpDestBucketSize) } @@ -58,7 +58,7 @@ func NewJumpDestCache() *JumpDestCache { } // Load retrieves the cached jumpdest analysis for the given code hash. -func (c *JumpDestCache) Load(hash common.Hash) (vm.BitVec, bool) { +func (c *shardedJumpDestCache) Load(hash common.Hash) (vm.BitVec, bool) { bucket := &c.buckets[hash[0]&(jumpDestBuckets-1)] v, ok := bucket.dest.Get(hash) if ok { @@ -70,7 +70,7 @@ func (c *JumpDestCache) Load(hash common.Hash) (vm.BitVec, bool) { } // Store saves the jumpdest analysis for the given code hash. -func (c *JumpDestCache) Store(hash common.Hash, b vm.BitVec) { +func (c *shardedJumpDestCache) Store(hash common.Hash, b vm.BitVec) { bucket := &c.buckets[hash[0]&(jumpDestBuckets-1)] bucket.dest.Add(hash, b) } diff --git a/core/state_prefetcher.go b/core/state_prefetcher.go index b36f8b7bba..a71a7929c3 100644 --- a/core/state_prefetcher.go +++ b/core/state_prefetcher.go @@ -49,7 +49,7 @@ func newStatePrefetcher(config *params.ChainConfig, chain *HeaderChain) *statePr // Prefetch processes the state changes according to the Ethereum rules by running // the transaction messages using the statedb, but any changes are discarded. The // only goal is to warm the state caches. -func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, jumpDests *JumpDestCache, cfg vm.Config, interrupt *atomic.Bool) { +func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, jumpDests vm.JumpDestCache, cfg vm.Config, interrupt *atomic.Bool) { var ( fails atomic.Int64 header = block.Header() diff --git a/core/state_processor.go b/core/state_processor.go index 0994d19226..024d36858a 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -61,7 +61,7 @@ func (p *StateProcessor) chainConfig() *params.ChainConfig { // Process returns the receipts and logs accumulated during the process and // returns the amount of gas that was used in the process. If any of the // transactions failed to execute due to insufficient gas it will return an error. -func (p *StateProcessor) Process(ctx context.Context, block *types.Block, statedb *state.StateDB, jumpDests *JumpDestCache, cfg vm.Config) (*ProcessResult, error) { +func (p *StateProcessor) Process(ctx context.Context, block *types.Block, statedb *state.StateDB, jumpDests vm.JumpDestCache, cfg vm.Config) (*ProcessResult, error) { var ( config = p.chainConfig() receipts = make(types.Receipts, 0, len(block.Transactions())) diff --git a/core/types.go b/core/types.go index 935202a441..0b75b269ba 100644 --- a/core/types.go +++ b/core/types.go @@ -41,7 +41,7 @@ type Prefetcher interface { // Prefetch processes the state changes according to the Ethereum rules by running // the transaction messages using the statedb, but any changes are discarded. The // only goal is to pre-cache transaction signatures and state trie nodes. - Prefetch(block *types.Block, statedb *state.StateDB, jumpDests *JumpDestCache, cfg vm.Config, interrupt *atomic.Bool) + Prefetch(block *types.Block, statedb *state.StateDB, jumpDests vm.JumpDestCache, cfg vm.Config, interrupt *atomic.Bool) } // Processor is an interface for processing blocks using a given initial state. @@ -49,7 +49,7 @@ type Processor interface { // Process processes the state changes according to the Ethereum rules by running // the transaction messages using the statedb and applying any rewards to both // the processor (coinbase) and any included uncles. - Process(ctx context.Context, block *types.Block, statedb *state.StateDB, jumpDests *JumpDestCache, cfg vm.Config) (*ProcessResult, error) + Process(ctx context.Context, block *types.Block, statedb *state.StateDB, jumpDests vm.JumpDestCache, cfg vm.Config) (*ProcessResult, error) } // ProcessResult contains the values computed by Process. From 6711508f7e1aa98a8d673d31393fa10e5f6e64f8 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Mon, 11 May 2026 16:33:18 +0000 Subject: [PATCH 7/7] consistent naming --- core/blockchain.go | 8 ++++---- core/state_prefetcher.go | 6 +++--- core/state_processor.go | 6 +++--- core/types.go | 4 ++-- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index 897a05e442..cf2dcf2a1f 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -324,7 +324,7 @@ type BlockChain struct { flushInterval atomic.Int64 // Time interval (processing time) after which to flush a state triedb *triedb.Database // The database handler for maintaining trie nodes. codedb *state.CodeDB // The database handler for maintaining contract codes. - jumpDest vm.JumpDestCache // Shared JUMPDEST analysis cache for block processing + jumpDestCache vm.JumpDestCache // Shared JUMPDEST analysis cache for block processing txIndexer *txIndexer // Transaction indexer, might be nil if not enabled hc *HeaderChain @@ -407,7 +407,7 @@ func NewBlockChain(db ethdb.Database, genesis *Genesis, engine consensus.Engine, db: db, triedb: triedb, codedb: state.NewCodeDB(db), - jumpDest: NewJumpDestCache(), + jumpDestCache: NewJumpDestCache(), triegc: prque.New[int64, common.Hash](nil), chainmu: syncx.NewClosableMutex(), bodyCache: lru.NewCache[common.Hash, *types.Body](bodyCacheLimit), @@ -2178,7 +2178,7 @@ func (bc *BlockChain) ProcessBlock(ctx context.Context, parentRoot common.Hash, // Disable tracing for prefetcher executions. vmCfg := bc.cfg.VmConfig vmCfg.Tracer = nil - bc.prefetcher.Prefetch(block, throwaway, bc.jumpDest, vmCfg, &interrupt) + bc.prefetcher.Prefetch(block, throwaway, bc.jumpDestCache, vmCfg, &interrupt) blockPrefetchExecuteTimer.Update(time.Since(start)) if interrupt.Load() { @@ -2224,7 +2224,7 @@ func (bc *BlockChain) ProcessBlock(ctx context.Context, parentRoot common.Hash, // Process block using the parent state as reference point pstart := time.Now() pctx, _, spanEnd := telemetry.StartSpan(ctx, "bc.processor.Process") - res, err := bc.processor.Process(pctx, block, statedb, bc.jumpDest, bc.cfg.VmConfig) + res, err := bc.processor.Process(pctx, block, statedb, bc.jumpDestCache, bc.cfg.VmConfig) spanEnd(&err) if err != nil { bc.reportBadBlock(block, res, err) diff --git a/core/state_prefetcher.go b/core/state_prefetcher.go index a71a7929c3..0f2a2ce33d 100644 --- a/core/state_prefetcher.go +++ b/core/state_prefetcher.go @@ -49,7 +49,7 @@ func newStatePrefetcher(config *params.ChainConfig, chain *HeaderChain) *statePr // Prefetch processes the state changes according to the Ethereum rules by running // the transaction messages using the statedb, but any changes are discarded. The // only goal is to warm the state caches. -func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, jumpDests vm.JumpDestCache, cfg vm.Config, interrupt *atomic.Bool) { +func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, jumpDestCache vm.JumpDestCache, cfg vm.Config, interrupt *atomic.Bool) { var ( fails atomic.Int64 header = block.Header() @@ -94,8 +94,8 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, j // Execute the message to preload the implicit touched states evm := vm.NewEVM(NewEVMBlockContext(header, p.chain, nil), stateCpy, p.config, cfg) defer evm.Release() - if jumpDests != nil { - evm.SetJumpDestCache(jumpDests) + if jumpDestCache != nil { + evm.SetJumpDestCache(jumpDestCache) } // Convert the transaction into an executable message and pre-cache its sender diff --git a/core/state_processor.go b/core/state_processor.go index 024d36858a..00b43aa149 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -61,7 +61,7 @@ func (p *StateProcessor) chainConfig() *params.ChainConfig { // Process returns the receipts and logs accumulated during the process and // returns the amount of gas that was used in the process. If any of the // transactions failed to execute due to insufficient gas it will return an error. -func (p *StateProcessor) Process(ctx context.Context, block *types.Block, statedb *state.StateDB, jumpDests vm.JumpDestCache, cfg vm.Config) (*ProcessResult, error) { +func (p *StateProcessor) Process(ctx context.Context, block *types.Block, statedb *state.StateDB, jumpDestCache vm.JumpDestCache, cfg vm.Config) (*ProcessResult, error) { var ( config = p.chainConfig() receipts = make(types.Receipts, 0, len(block.Transactions())) @@ -89,8 +89,8 @@ func (p *StateProcessor) Process(ctx context.Context, block *types.Block, stated context = NewEVMBlockContext(header, p.chain, nil) evm := vm.NewEVM(context, tracingStateDB, config, cfg) defer evm.Release() - if jumpDests != nil { - evm.SetJumpDestCache(jumpDests) + if jumpDestCache != nil { + evm.SetJumpDestCache(jumpDestCache) } if beaconRoot := block.BeaconRoot(); beaconRoot != nil { diff --git a/core/types.go b/core/types.go index 0b75b269ba..6608fd6660 100644 --- a/core/types.go +++ b/core/types.go @@ -41,7 +41,7 @@ type Prefetcher interface { // Prefetch processes the state changes according to the Ethereum rules by running // the transaction messages using the statedb, but any changes are discarded. The // only goal is to pre-cache transaction signatures and state trie nodes. - Prefetch(block *types.Block, statedb *state.StateDB, jumpDests vm.JumpDestCache, cfg vm.Config, interrupt *atomic.Bool) + Prefetch(block *types.Block, statedb *state.StateDB, jumpDestCache vm.JumpDestCache, cfg vm.Config, interrupt *atomic.Bool) } // Processor is an interface for processing blocks using a given initial state. @@ -49,7 +49,7 @@ type Processor interface { // Process processes the state changes according to the Ethereum rules by running // the transaction messages using the statedb and applying any rewards to both // the processor (coinbase) and any included uncles. - Process(ctx context.Context, block *types.Block, statedb *state.StateDB, jumpDests vm.JumpDestCache, cfg vm.Config) (*ProcessResult, error) + Process(ctx context.Context, block *types.Block, statedb *state.StateDB, jumpDestCache vm.JumpDestCache, cfg vm.Config) (*ProcessResult, error) } // ProcessResult contains the values computed by Process.