Pull from go-ethereum up to 2f24e25 (6 Mar 2019)#2
Closed
AlexeyAkhunov wants to merge 145 commits into
Closed
Conversation
* node: close AccountsManager in new Close method * p2p/simulations, p2p/simulations/adapters: handle node close on shutdown * node: move node ephemeralKeystore cleanup to stop method * node: call Stop in Node.Close method * cmd/geth: close node.Node created with makeFullNode in cli commands * node: close Node instances in tests * cmd/geth, node: minor code style fixes * cmd, console, miner, mobile: proper node Close() termination
contracts/*: golint updates for this or self warning
cmd/utils, eth: relinquish GC cache to read cache in archive mode
ethapi: default to use eip-155 protected transactions
core: repro #18977
* build: use sftp for launchpad uploads * .travis.yml: configure sftp export * build: update CI docs
* common/fdlimit: cap on MacOS file limits, fixes #18994 * common/fdlimit: fix Maximum-check to respect OPEN_MAX * common/fdlimit: return error if OPEN_MAX is exceeded in Raise() * common/fdlimit: goimports * common/fdlimit: check value after setting fdlimit * common/fdlimit: make comment a bit more descriptive * cmd/utils: make fdlimit happy path a bit cleaner
* signer/clef: make use of json-rpc notification * signer: tidy up output of OnApprovedTx * accounts/external, signer: implement remote signing of text, make accounts_sign take hexdata * clef: added basic testscript * signer, external, api: add clique signing test to debug rpc, fix clique signing in clef * signer: fix clique interoperability between geth and clef * clef: rename networkid switch to chainid * clef: enable chainid flag * clef, signer: minor changes from review * clef: more tests for signer
* clef: initial implementation of bidirectional RPC communication for the UI * signer: fix tests to pass + formatting * clef: fix unused import + formatting * signer: gosimple nitpicks
This was referenced Apr 7, 2026
Merged
4 tasks
4 tasks
5 tasks
3 tasks
5 tasks
This was referenced May 7, 2026
5 tasks
5 tasks
Sahil-4555
pushed a commit
to Sahil-4555/erigon
that referenced
this pull request
May 9, 2026
…tyRemoval (erigontech#21032) ## Summary Fixes a wrong trie-root in the parallel commitment calculator when post-tx writesets are indistinguishable between two cases: 1. **SELFDESTRUCT of a pre-existing contract** — serial keeps the leaf with `incarnation>0`, zero balance/nonce/empty codeHash. Parallel was emitting `DeleteUpdate` and removing the leaf. 2. **EIP-161 emptyRemoval** of a touched-empty account (e.g. `0xff…fe` after the EIP-4788 system call) — serial emits `DeleteUpdate`. Parallel was emitting a zero-account UPDATE. The discriminator is the **pre-block incarnation**, which the writeset alone didn't carry. Fix wires it through `LightCollector.DeleteAccount` → `IncarnationPath` write → `calcAccountState.Incarnation` → 3-way branch in `FlushToUpdates`. ## Dependency direction This PR is a **precursor for erigontech#21017** (the CI matrix that runs every test under both `serial` and `parallel` exec modes). Without this fix, parallel-mode tests on hive `rpc-compat`, `engine api/cancun/withdrawals`, and the eest selfdestruct sub-suites all fail with wrong-trie-root errors at SD/empty-removal blocks. **erigontech#21017 cannot land until this PR lands.** The matrix in erigontech#21017 will validate this PR end-to-end via the hive parallel sub-suites — meaning this PR's parallel-exec changes don't run in *this* PR's own CI, only in erigontech#21017's. The rebased erigontech#21017 is the meaningful go/no-go signal. ## Changes * `LightCollector.DeleteAccount` now emits `IncarnationPath` alongside `SelfDestructPath=true` when `original.Incarnation > 0`. Calc receives the pre-deletion incarnation through the same channel as every other write — no exec-side state leakage. * `calc_state.ApplyWrites` consumes `IncarnationPath` into `calcAccountState.Incarnation` via direct type-assertion (panic on mismatch — see *Concern 3* below). * `calc_state.FlushToUpdates` switches on a 3-way `Deleted` branch with `isAllZero` defense-in-depth: * `Deleted && Incarnation>0 && all-zero` → zero-account UPDATE (matches serial's `DomainDel`-leaves-post-CREATE-encoding for SD'd contracts) * `Deleted && all-zero && Incarnation==0` → `DeleteUpdate` (matches serial's `DomainDel`-truly-empties for EIP-161 emptyRemoval) * `Deleted` with retained non-zero values → regular UPDATE — defensive coverage for OOG-CREATE2-with-retained-balance and any future write-ordering race * `SelfDestructPath` also marks all tracked storage slots dirty so `FlushToUpdates` emits per-slot updates alongside the account reset. Load-bearing invariant: `normalizeWriteSet`'s `vm.StorageKeys(addr)` loop emits `StoragePath=0` entries that arrive in `ApplyWrites` after `SelfDestructPath`, overwriting the marked slots' values to zero so they emit `DeleteUpdate` not `StorageUpdate(pre-SD value)`. Inline comment in `calc_state.go` spells this out — see *Concern 2* below. ## Earlier draft snags (resolved) The first draft also added an `IncarnationPath > 0` exclusion to `normalizeWriteSet`'s empty-account check. This was **redundant** (the empty-check already requires `Nonce == 0`, which excludes successful CREATE/CREATE2) and **broke OOG-during-CREATE2 cases** (which leave `Nonce=0/Balance=0/Code=empty/Incarnation=1` and *must* still be deleted). Removed in `9539998f14`. The `exec3_parallel.go` diff in this PR is now comments-only. ## Reviewer concerns addressed ### erigontech#1 (yperbasis): PR description was stale This body. ✓ ### erigontech#2 (yperbasis + Copilot): SD storage-cascade load-bearing invariant Inline comment in `calc_state.go`'s `SelfDestructPath` case now spells out the dependency on `normalizeWriteSet`'s `vm.StorageKeys(addr)` loop. ✓ ### erigontech#3 (yperbasis + Copilot): IncarnationPath guarded type-assertion Changed to direct `w.Val.(uint64)` to match the other paths. Silent zero would route a real SD into the EIP-161 branch and reproduce the very wrong-root bug — better to panic at the source. ✓ ### erigontech#4 (yperbasis): `TestFlushToUpdates_DeletedWithRetainedBalance_EmitsRegularUpdate` docstring Updated to clarify: this is **defensive coverage** for the third `FlushToUpdates` branch in isolation, NOT a direct repro of the eest_devnet OOG path. The actual OOG fix is the removal of the redundant `IncarnationPath > 0` clause from `normalizeWriteSet` (the OOG writeset has `Nonce=0` → empty-account → `DeleteUpdate`, not `Deleted+RetainedBalance`). End-to-end coverage of that path lives in the eest_devnet suite, surfaced via erigontech#21017's matrix. ✓ ### erigontech#5 (yperbasis): `versionedWriteCollector.DeleteAccount` asymmetry — *intentional non-fix* Decision: **keep the asymmetry, document why.** Inline comment added on `versionedWriteCollector.DeleteAccount` explaining: * It's wired only into `txResult.finalize` (fee calc + post-Cancun system calls). * Neither path SDs a pre-existing contract today, so the SD-with-incarnation differentiator is unreachable from here. * If a future caller ever does emit `DeleteAccount` on a pre-existing contract through this collector, the comment flags that this code should mirror `LightCollector.DeleteAccount`'s `IncarnationPath` emit. Adding the emit speculatively was rejected because: (a) it changes the writeset shape for paths that today don't need it, (b) any test exercising the new emit would be vacuous since no production caller hits the `original.Incarnation > 0` branch, and (c) the comment is enough to attribute the bug at first sight if someone *does* reach that code path in the future. ## Intentional non-fixes * **Concern erigontech#5 above** — `versionedWriteCollector.DeleteAccount` left without the `IncarnationPath` emit (rationale above). * **Defensive `TestFlushToUpdates_DeletedWithRetainedBalance` test kept** despite the state being unreachable from real LightCollector writesets today — protects the FlushToUpdates branch in isolation against future ApplyWrites refactors that might drop the `BalancePath`-clears-`Deleted` invariant. ## Test plan - [x] All 6 unit tests in `calc_state_test.go` pass (`TestFlushToUpdates_DeletedWithIncarnation_EmitsZeroAccountUpdate`, `TestFlushToUpdates_DeletedWithoutIncarnation_EmitsDelete`, `TestFlushToUpdates_DeletedWithRetainedBalance_EmitsRegularUpdate`, `TestFlushToUpdates_LiveAccount_EmitsFullUpdate`, `TestApplyWrites_IncarnationPath`, `TestApplyWrites_BalancePathClearsDeleted`) - [x] eest_devnet `for_amsterdam/constantinople/eip1052_extcodehash/extcodehash/extcodehash_subcall_create2_oog` all 6 variants pass locally - [x] Full `for_amsterdam/constantinople` eest_devnet suite passes - [x] `make lint` clean - [x] CI on `9539998f14` was green End-to-end validation comes via erigontech#21017's CI matrix once it rebases on top of this PR.
This was referenced May 13, 2026
This was referenced May 15, 2026
3 tasks
sudeepdino008
added a commit
that referenced
this pull request
May 21, 2026
…21310) ## Summary Two spec-deviations in `getFilterBlockTree` compound to reject valid current-epoch leaves at every epoch boundary, causing Caplin's `GetHead` to fall back to the justified checkpoint root (a ~30-50 slot regression) and triggering execution-side unwinds. ## Observed symptom (from #21301) On bloatnet, once execution catches up to chain tip, the node enters a steady-state cycle of **22-36 block execution unwinds every ~6 minutes** — one per epoch boundary. Histogram from one ~3h run (110 unwind events): ``` 1 size=11 1 size=14 4 size=15 1 size=16 4 size=18 2 size=19 3 size=20 5 size=21 9 size=22 14 size=23 ← most common 3 size=24 5 size=25 4 size=26 4 size=27 8 size=28 1 size=30 2 size=31 4 size=32 3 size=33 4 size=34 ...many size=35-36 ``` The unwinds are on the **same chain** (no real reorg) — execution is forced to roll back to an older head because Caplin regresses its `GetHead()` return value: ``` 08:38:45 Caplin FCU: headSlot=652928 currentSlot=652988 lagSlots=60 eth1Head=0xe0f3... 08:39:36 Caplin FCU: headSlot=652993 currentSlot=652993 lagSlots=0 eth1Head=0x263b... ← at tip 08:44:00 Caplin FCU: headSlot=652959 currentSlot=653015 lagSlots=56 eth1Head=0x7674... ← regressed 34 slots ``` The 08:44:00 FCU triggers a 23-block exec unwind: ``` [forkchoice] entering unwind path fcuNum=24701328 canonHash=0x7674... fcuHash=0x7674... hashesDiffer=false finishProgress=24701350 executionAhead=true Unwind Execution from=24701350 to=24701327 ``` `hashesDiffer=false` confirms same-chain. `executionAhead=true` because exec raced ahead of Caplin's regressing head. ## Root cause: filter rejects mid-epoch leaves Tracing inside `GetHead`: ``` [GetHead] cache miss, rebuilding from justifiedCheckpoint justifiedEpoch=20410 [filterTree] reject leaf: checkpoint mismatch blockRoot=0xe644... slot=653150 justifiedOk=false finalizedOk=false storeJustEpoch=20410 blockJustEpoch=20411 storeFinalEpoch=20409 blockFinalEpoch=20410 [GetHead] filtered tree size viableBlocks=0 totalHeads=1 [GetHead] rebuilt head headHash=0x... headSlot=653119 ← fallback to justified root Caplin is sending forkchoice headSlot=653119 currentSlot=653174 lagSlots=55 ``` Every leaf in the tree gets filtered because its `unrealizedJustifications[blockRoot].epoch = N+1` but the store's `justifiedCheckpoint.epoch = N` (store's realized lags by 1 epoch mid-epoch). The walk falls back to the justified root → 50+ slot regression. ## Spec deviations (the fix) ### 1. Voting-source selection used unrealized unconditionally Spec [`get_voting_source`](https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/fork-choice.md#get_voting_source) picks unrealized only for **prior-epoch** blocks (pull-up justification view); current-epoch blocks should use the block's realized state checkpoint: ```python def get_voting_source(store: Store, block_root: Root) -> Checkpoint: block = store.blocks[block_root] current_epoch = get_current_store_epoch(store) block_epoch = compute_epoch_at_slot(block.slot) if current_epoch > block_epoch: return store.unrealized_justifications[block_root] else: return store.block_states[block_root].current_justified_checkpoint ``` Erigon's code: ```go // Use per-block unrealized justifications (spec: store.unrealized_justifications[block_root]) // Fall back to realized checkpoints if unrealized not available currentJustifiedCheckpoint, has := f.getUnrealizedJustification(blockRoot) if !has { currentJustifiedCheckpoint, has = f.forkGraph.GetCurrentJustifiedCheckpoint(blockRoot) ... } ``` → Always picks unrealized when available. The "fall back" comment misreads the spec — spec is a conditional branch, not a fallback. ### 2. Justified/finalized check was strict equality Spec [`filter_block_tree`](https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/fork-choice.md#filter_block_tree) has a lenient `+2 >= current_epoch` lookback: ```python correct_justified = ( store.justified_checkpoint.epoch == GENESIS_EPOCH or voting_source.epoch == store.justified_checkpoint.epoch or voting_source.epoch + 2 >= current_epoch # ← lenient lookback ) ``` Erigon's code only allowed the first two clauses (strict `Equal()`). ## Regression provenance The deviations were introduced in #20035 (`caplin: unified Engine API client for standalone mode`, Mar 25 2026) by Mark Holt: ``` 082697d cl/phase1/forkchoice/get_head.go +Use per-block unrealized justifications ``` Before that PR, `getFilterBlockTree` used `f.forkGraph.GetCurrentJustifiedCheckpoint(blockRoot)` directly — which returns the block's **realized** justified checkpoint. Block.realized matched store.realized (both on the same epoch-boundary timeline), so the equality check passed. PR #20035 introduced the per-block unrealized lookup (`store.unrealized_justifications` per the spec), but missed the conditional branching (spec deviation #1) and didn't add the +2 lookback (spec deviation #2). The result: block.unrealized goes ahead by 1 epoch mid-epoch, and store.realized doesn't catch up until `OnTick`'s epoch-boundary path runs. ## Fix Restore spec-faithful behavior in `getFilterBlockTree`: 1. Branch the voting source selection on `currentEpoch > blockEpoch`: - Prior-epoch → `store.unrealized_justifications[block_root]` - Current/future-epoch → block's realized `current_justified_checkpoint` In both branches, return `false` (reject the leaf) if the lookup is absent. `OnBlock` populates `unrealizedJustifications` for every imported block above the finalized slot, so a missing entry indicates either an invariant violation or a leaf below finalized — neither should produce a viable head. 2. Replace the strict `Equal()` check on the justified side with the spec's three flat disjuncts: `store.justified_checkpoint.epoch == GENESIS_EPOCH || voting_source.epoch == store.justified_checkpoint.epoch || voting_source.epoch + 2 >= current_epoch`. 3. Replace the finalized-side checkpoint-equality check with the spec's ancestor-descent rule: `store.finalized_checkpoint.root == get_checkpoint_block(block_root, finalized.epoch)`, implemented via `f.Ancestor(blockRoot, finalizedSlot)`. 4. Snapshot `currentEpoch` once at the start of the walk (in `getFilteredBlockTree`) and thread it through the recursion so the whole rebuild uses a single store-epoch value — `OnTick` updates `f.time` without holding `f.mu`, so per-leaf `f.Slot()` reads can mix epochs across leaves around a slot boundary. ## Validation Tested on bloatnet at chain tip, on `performance` HEAD + this fix: - **Before fix**: 22-36 block unwinds every ~6 min, one per epoch boundary, persisting indefinitely. 110 events in 3h. - **After fix**: zero unwinds at epoch boundaries observed across 30+ minutes (~5 epoch boundaries crossed). `[filterTree] reject` no longer fires. Effects: - **CPU waste eliminated**: ~30 blocks of execution work were being discarded and re-done every cycle. - **db_size**: unchanged (was already not affected since rolled-back state was in MemoryMutation overlay, not MDBX). - **Logs**: clean — no more spurious `Unwind Execution` lines at tip. ## Test plan - [x] Run on bloatnet to chain tip, observe no large unwinds at epoch boundaries - [x] Confirm no head regression in `Caplin is sending forkchoice` logs (lagSlots stays ~0 at tip) - [ ] Spectest still passes (`make spectest`) ## Refs Full diagnosis trail: #21301
lystopad
added a commit
that referenced
this pull request
May 21, 2026
Findings from Copilot (3) and yperbasis (8). The Copilot findings on Peers routing, empty NodeInfo, and close(statusReady) are real; yperbasis identified the matching doc/code mismatches and a few polish items. 1. Peers() / message routing (Copilot #1). The multi-sentry client uses Peers() to decide which sentry owns each peer and routes SendMessageById via that sentry's gRPC. With the previous reporter-only gating, every peer mapped to Servers[0] (which is the *lowest* configured protocol, ETH69 in the default — yperbasis #1 noted the comment said "highest"). Servers[0]'s goodPeers doesn't have eth/70 or eth/71 peers, so SendMessageById silently no-op'd. Fix: each GrpcServer.Peers() now reports its own goodPeers, filtered to skip entries where both protocol and witProtocol are zero. Each peer ends up in exactly one eth-sentry's goodPeers (the negotiated version) plus, at most, the sentry hosting the wit sideprotocol, so admin_peers aggregation is naturally non-duplicating and routing is correct. 2. NodeInfo() (Copilot #3). Non-reporters returning empty replies polluted admin_nodeInfo with blank entries that sorted first. With the shared p2p.Server every sentry has the same Node ID and the same enode, so they now return identical NodeInfo. node/eth.NodesInfo deduplicates by Enode before sorting. 3. SetStatus's close(ss.statusReady) (Copilot #2) panicked for callers that construct GrpcServer outside NewGrpcServer (existing TestSentryServerImpl_* does this). Guarded the close with a nil check; awaitStatus tolerates a nil channel via the select's other cases. 4. SetP2PServer returns an error instead of panicking on double-call (yperbasis #3). The "ownership decided up front" invariant is still enforced, just propagated up the Provider.Initialize path cleanly. 5. SimplePeerCount filters protocol=0 ghosts (yperbasis #4). With wit/0 deduped to one sentry, peers that negotiate eth/N on a different sentry end up as protocol=0/witProtocol=0 ghosts on the wit-hosting sentry. Counting them would emit a bogus eth.ProtocolToString[0] bucket in the GoodPeers log. The Peers() filter already drops them from admin_peers; this aligns SimplePeerCount. 6. awaitStatus logs a Debug line when its timeout fires (yperbasis #5) so operators can tell "core didn't send status in time" from "core never tried" when the caller disconnects the peer with PeerErrorLocalStatusNeeded. Doc comment clarifies the ctx.Done case too (yperbasis #7). Drops the reportsPeers flag and IsPeerReporter accessor — no longer needed once per-sentry goodPeers replaces the reporter gating. SetP2PServer signature loses its second argument. Tests updated for the new shape; new TestGrpcServer_PeersReturnsPerSentryGoodPeers (per-sentry view + ghost-entry filter) and TestGrpcServer_SetStatus_NilStatusReadyIsSafe (close-nil guard). Deferred to follow-up: - BootstrapNodes/DNS resolution helper to dedupe logic between makeP2PServer and startSharedP2PServer (yperbasis #6). - End-to-end Provider.Initialize test in local mode (yperbasis #8). - [r3.4] backport PR (yperbasis #9). Co-Authored-By: Claude
AskAlexSharov
pushed a commit
to HoustonOla35/erigon
that referenced
this pull request
May 22, 2026
…rgs) (erigontech#21211) ## Summary First incremental cut toward [erigontech#21138](erigontech#21138 structural goal: **one finalize function per parallel-exec result, with `IntraBlockState` used nowhere outside workers**. This PR removes two finalize variants that are already unreachable from production: | Function | LOC | Production callers on main | |---|---|---| | `finalizeWithIBS` (full IBS reconstruction, BAL-compat path) | ~120 | 0 | | `finalizeTx` (delta-args variant, direct fee-balance path) | ~250 | 0 (only `TestFinalizeTx_AllScenarios`) | Plus the test suite that exclusively exercised the delta-args path (`TestFinalizeTx_*`, fixture builders `coinbaseIsRecipientScenario` / `selfTransferScenario`, helpers `hasCoinbaseDelta` / `adjustForTransferDelta` / `buildWriteMap` / `fmtWriteVal` / `extractBalanceReads`) and one stale comment in `engine_api_bal_test.go`. Net: **-690 lines**, **+1 line**, no semantic change. ## Why now The parallel-exec correctness stack landed in erigontech#21153 (merged 2026-05-15). The combined effect of that PR plus erigontech#21177 routed all production finalize flows through `finalizeTxSimple` — these two functions became unreachable. Removing them shrinks `exec3_parallel.go` from 3640 → 3268 lines, making subsequent IBS-dependency drains easier to review. The next steps in the erigontech#21138 sequence: - **PR 2** — drain IBS dependency erigontech#1 (SD address lookup): `LogSelfDestructedAccounts` consumes `result.SelfDestructedWithBalance` only, no `ibs.GetRemovedAccountsWithBalance()` call. - **PR 3** — drain IBS deps erigontech#2 (`AddLog` → return logs) and erigontech#3 (`AddBalance` bookkeeping → already on `CollectorWrites`); `finalizeTxSimple` becomes IBS-free. - Later — `normalizeWriteSet` → `filterWritesByVersionMap`; `calcState.ApplyWrites` → `VersionedWrites.TouchUpdates`; move EIP-7002/7251 syscall execution into the worker pool. End state: one `finalizeTx`, no IBS outside workers. ## Test plan - [x] `make lint` clean - [x] `make test-short` (full `execution/stagedsync`, `execution/state`, `execution/tests`, `rpc/jsonrpc` packages) green under `EXEC3_PARALLEL=true` - [x] BAL family (`TestEngineApiBAL*`) 8/8 parallel - [x] `TestEIP7708BurnLogWhenCoinbaseSelfDestructs` green - [x] Surviving `TestFinalizeTxSimple_*` family green - [ ] CI: race-tests, kurtosis, hive matrix legs green on both serial and parallel ## Related - erigontech#21017 — serial/parallel CI matrix that surfaces parallel-leg failures (now rebuilt on post-erigontech#21153 main; CI fresh-running) - erigontech#21153 — parallel-exec correctness stack (merged) - erigontech#21138 — heuristic-removal / IBS-dependency-removal tracker (the parent)
mh0lt
pushed a commit
that referenced
this pull request
May 24, 2026
…ash LRU Two surgical commits bundled (both touch the code-read hot path): 1. IntraBlockState.GetCodeSize now loads the full bytes via stateReader.ReadAccountCode on first touch and populates stateObject.code, so subsequent same-addr EXTCODESIZE / EXTCODEHASH / CALL within the tx are in-struct slice-len calls (~50 ns), not full reader round-trips. Mirrors geth's pattern at core/state/state_object.go ~Code() — pay one read per addr per tx, free for the rest. 2. CodeCache.addrToHash switched from a no-op-when-full maphash.Map[versionedAddressID] to an LRU lru.Cache[[20]byte, versionedAddressID] (hashicorp/golang-lru/v2, already imported elsewhere). Cap derived from the existing byte budget at ~28 bytes/entry (~580 k entries for the 16 MB default). Fresh-address workloads (mainnet thousands of new addrs per block) now warm up the addr layer over time instead of silently dropping new entries forever; matches geth's lru.Cache at core/state/database_code.go. The hashToCode layer is unchanged (content-addressed bytes, immutable, byte-capped with new-entry no-op when full — the same semantic as before since code bytes by codeHash never change). Bench on the EXTCODESIZE-EXISTING_CONTRACT-30M family: 62.34 mgas/s (was 61.50). The marginal gain is small on this bench because BAL prefetch already populates the cache layers; neither lever fires heavily. The expected wins are on non-BAL workloads where EXTCODESIZE-loop patterns repeat within a tx (#1) and fresh-address-churn mainnet blocks fill the addr layer (#2). Updated TestCodeCache_AddrCapacityLimit to assert LRU eviction (was asserting no-op-when-full); the prior behaviour was the bug. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
mh0lt
pushed a commit
that referenced
this pull request
May 25, 2026
…ash LRU Two surgical commits bundled (both touch the code-read hot path): 1. IntraBlockState.GetCodeSize now loads the full bytes via stateReader.ReadAccountCode on first touch and populates stateObject.code, so subsequent same-addr EXTCODESIZE / EXTCODEHASH / CALL within the tx are in-struct slice-len calls (~50 ns), not full reader round-trips. Mirrors geth's pattern at core/state/state_object.go ~Code() — pay one read per addr per tx, free for the rest. 2. CodeCache.addrToHash switched from a no-op-when-full maphash.Map[versionedAddressID] to an LRU lru.Cache[[20]byte, versionedAddressID] (hashicorp/golang-lru/v2, already imported elsewhere). Cap derived from the existing byte budget at ~28 bytes/entry (~580 k entries for the 16 MB default). Fresh-address workloads (mainnet thousands of new addrs per block) now warm up the addr layer over time instead of silently dropping new entries forever; matches geth's lru.Cache at core/state/database_code.go. The hashToCode layer is unchanged (content-addressed bytes, immutable, byte-capped with new-entry no-op when full — the same semantic as before since code bytes by codeHash never change). Bench on the EXTCODESIZE-EXISTING_CONTRACT-30M family: 62.34 mgas/s (was 61.50). The marginal gain is small on this bench because BAL prefetch already populates the cache layers; neither lever fires heavily. The expected wins are on non-BAL workloads where EXTCODESIZE-loop patterns repeat within a tx (#1) and fresh-address-churn mainnet blocks fill the addr layer (#2). Updated TestCodeCache_AddrCapacityLimit to assert LRU eviction (was asserting no-op-when-full); the prior behaviour was the bug. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.