Skip to content

core/vm, params, eth: merge geth v1.17.5 (v1.17.5 sync) - #2354

Draft
pratikspatil024 wants to merge 118 commits into
ppatil-upstream-v1.17.4from
ppatil-upstream-v1.17.5
Draft

core/vm, params, eth: merge geth v1.17.5 (v1.17.5 sync)#2354
pratikspatil024 wants to merge 118 commits into
ppatil-upstream-v1.17.4from
ppatil-upstream-v1.17.5

Conversation

@pratikspatil024

@pratikspatil024 pratikspatil024 commented Aug 11, 2026

Copy link
Copy Markdown
Member

Important

Reviewer guide — stacked PR 12 of 12. Part of the combined go-ethereum v1.17.4 + v1.17.5 upstream sync, which ships as one stable release. Every PR in the stack merges into the base branch upstream-merge-v1.17.4; that base merges into develop once, at the very end — not per-PR.

Merge-commit only — never squash. Squashing rewrites a branch's SHAs and breaks every PR stacked above it.

Review bottom-up: #2308#2319#2325#2328#2337#2340#2341#2342#2343#2345#2346#2354. Start at #2308 / #2319 — every PR above inherits them, so reviewing top-down means re-reviewing.

Expected-red / flaky checks (not code blockers): Quality metrics (diffguard — skipped by team decision; it also mis-scopes across a stacked diff, comparing against the bottom of the stack), and codecov/project (repo-wide coverage threshold; per-PR patch coverage is green). Kurtosis e2e occasionally flakes (~1-in-5, devtools-owned) and is re-run by hand. Full per-batch conflict-resolution reasoning is in docs/upstream-merges/.


Stacked PR — base is ppatil-upstream-v1.17.4 (#2346), not develop.
Merge with a merge commit. Never squash. Squashing rewrites this branch's
SHAs, which makes every PR stacked above it re-show these changes and conflict
against its base. This is destructive to the stack, not merely undesirable.

Summary

Merges go-ethereum v1.17.5 into bor, continuing the upstream sync. 110
upstream first-parent commits (v1.17.4..v1.17.5), taken in 6 batches, each
a single merge commit whose resolution reasoning is recorded in the ledger. The
upstream v1.17.5 tag (9621c6ad1) is an ancestor of this branch, so the sync
target is reached rather than approximated.

v1.17.4 and v1.17.5 ship as one stable release: this PR merges into the
v1.17.4 branch, and the base branch goes to develop once, at the end of the
whole effort.

Upstream range v1.17.4..v1.17.5, 110 first-parent commits
Batches 6, all GPG-signed
Net diff vs base 194 files, +6234 / −1220
Conflicts resolved 187 across the milestone (b1 30, b2 24, b3 22, b4 32, b5 41, b6 18)
New hardforks enabled none — see below

Documentation lives in docs/upstream-merges/v1.17.5/:

  • plan.md — batch boundaries, deviations, ordered follow-ups
  • ledger.md — every resolution, per batch, with reasoning
  • fork-register.md — per-fork/EIP dormancy verification
  • needs-wiring.md — everything intentionally not adopted, and what adopting it would take

Consensus-relevant items, for the reviewer's attention first

Bogota lands dormant, in bor's block-based form. Upstream schedules it by
timestamp; bor schedules by block, so the config field, activation helper, rules
flag, fork-ordering and compatibility entries, startup banner and the four VM
sites were all converted. One compatibility entry had auto-merged in the
BogotaTime form and would not have compiled. The gate is nil on all six
surfaces
— both params presets, both internal/cli/server/chains/*.go, both
builder/files/genesis-*.json.

Its precompile delta against the preceding fork is empty, and that was read
off the code twice rather than assumed once: batch 5's #35383 rebased
newBogotaInstructionSet() from Osaka onto Amsterdam, moving the basis the
first check was made against, so the check was repeated. Bor's
activePrecompiledContracts has no IsAmsterdam case, so Amsterdam falls
through to PrecompiledContractsOsaka — exactly what the explicit IsBogota
case returns. Only the opcode basis moved.

Both bor guards fired on the new rules field and were worked through rather
than silenced
: TestReinforceMultiClientPreCompilesTest (the 5-step
multi-client checklist) and TestV2ForkParity (classified {inV1: false, inV2: false}).

A stateless-node regression was caught outside any conflict. #35252 moved
the chain-rewind state check, and its replacement auto-merged without bor's
!bc.cfg.Stateless exemption. A stateless node would have hit
log.Crit("Chain is stateless at a non-genesis block") and exited on any
rewind. The guard was re-applied at the relocated site. git reported that file
as a clean merge.

Every upstream fork and EIP in this range is merged dormant (invariant 9),
verified per fork in fork-register.md. Nothing here changes behaviour on
mainnet or Amoy.

Executed tests

Per-batch tier, run on all six batches:

  • go build ./... — exit 0
  • go vet ./... — exactly the two known pre-existing lock-copy findings
    (core/parallel_state_processor.go:341, trie/secure_trie.go:88) and nothing
    else
  • gofmt clean; go mod tidy clean in the main module
  • go test on every touched package, including the full core, eth and
    miner suites

Additionally on the final batch, because it introduces a goroutine into
consensus-critical ValidateState and changes lock ordering in the downloader:

  • go test -race ./core -run 'Validate|Insert|Chain|State' — clean
  • go test -race ./eth/downloader/... — clean

Pre-existing failures, re-run and signature-matched at each batch (compared
by failure text, not package name, against the previous batch's commit):
cmd/devp2p/internal/ethtest (nil-BorConfig panic via
params.(*BorConfig).CalculatePeriod), cmd/geth (TestAttachWelcome,
TestConsoleWelcome, TestExport, TestCustomBackend, TestCustomGenesis),
cmd/evm (TestT8n, TestEvmRun, TestEVMTracing, TestEvmRunRegEx). All
are invisible to CI, which excludes cmd/ via
TESTALL = $(go list ./... | grep -v go-ethereum/cmd/).

Accepted-red CI checks

  • Quality metrics (diffguard) — skipped by operator decision. It is also
    structurally broken for stacked PRs: --base origin/${{ github.base_ref }}
    resolves to the bottom of the stack, so it diffs the entire sync (~400 files)
    and is SIGTERM'd at the runner limit.
  • codecov — expected red on a merge of this shape.
  • Kurtosis e2e has a known ~1-in-5 flake rate (devtools-owned); re-runs are
    by hand.

Rollout notes

  • Not consensus-affecting as merged. Every new fork and EIP is dormant; no
    gate is flipped, no activation height is set, no precompile set changes on any
    live network.
  • No coordinated upgrade required for this PR on its own — it is one link in
    a stack that ships as a single stable release.
  • Operator-facing: none. The one dependency change is a golang/snappy bump,
    flagged in the ledger because upstream pinned an unreleased pseudo-version
    (v1.0.1-0.20260716114414-9ae09f520e93) rather than a tagged release. It is
    trivially revertible if the team prefers to hold at v1.0.0.
  • version/version.go now reads 1.17.5 / stable, matching upstream's
    release commit.

Follow-ups owed on top of this milestone

Ordered, with the dependency spelled out — the naive order is the wrong one.
Full detail in plan.md.

  1. Adopt #35318 (EIP-2780 / EIP-8037 spec changes). Deferred by operator
    decision: it is a structural rework of the intrinsic-gas and gas-budget flow,
    not a spec tweak, and it silently drops the isMadhugiri arm of bor's live
    EIP-7825 transaction gas cap. Dormant either way, so deferring changes no
    behaviour today.
  2. Restore core/eip8037_test.go + core/vm/eip8037_test.go (and the five
    further orphaned EIP test files). Blocked on (1) — #35318 rewrote those
    tests, so upstream's current versions target the post-#35318 shape. Doing (1)
    first makes these tests its verification.
  3. Align makeGasSStoreFuncPIP88 with upstream's post-#35261 ordering.
    Independent of (1) and (2), and on a live EVM gas path. It needs its own
    review, boundary tests, and explicit sign-off because it changes a
    transaction's state read set.
  4. go mod tidy in cmd/keeper. Pre-existing; wants its own small commit.

Deferred, and worth one deliberate team decision

The eth-protocol surface is now four deferrals deep: eth/68-drop (#33511),
delayed decoding (#33835), eth/71 BAL, and now eth/72 sparse blobpool
(#34047)
— 47 files, EIP-8070, blob cells, custody bitmap via
forkchoiceUpdatedV4, engine_getBlobsV4. eth/70 was adopted separately in
#2341. This divergence compounds with each release and deserves a single
deliberate decision rather than a fifth deferral.

Also recorded: an Amsterdam-enable-time backlog in needs-wiring.md that
must all land before Amsterdam is ever enabled — most notably that bor's miner
never calls core.PreExecution, so EIP-7997 would not be applied during block
building and a producer would disagree on state root with validators.

fjl and others added 30 commits June 22, 2026 09:47
This PR drops support for v0 blob sidecar in blobpool. Since the
osaka fork activation time has passed, these code paths are
now unused. It is assumed that only v1 transactions exist in
the blobpool.
This PR inlines the gas deduction by getting rid of the tracer and use
`chargeRegularOnly` for the non-state opcode.

It fixes a performance regression introduced by EIP-8037 PR.

```

throughput MGas/s | 184.4 (±0.3%) | 193.1 (±1.0%) | +4.7% ▲
-- | -- | -- | --
mean newPayload | 164.2 ms (±0.3%) | 156.9 ms (±1.0%) | -4.5% ▲
p50 newPayload | 154.6 ms (±0.1%) | 147.6 ms (±0.7%) | -4.5% ▲
p95 newPayload | 273.3 ms (±2.3%) | 261.6 ms (±2.4%) | -4.3% ≈ noise
p99 newPayload | 403.6 ms (±4.4%) | 380.9 ms (±4.0%) | -5.6% ≈ noise

```
Mirror the guard applied to (*UDPv4).Dial in #34916: when the target
node has no usable UDP endpoint, return errNoUDPEndpoint instead of
silently sending the ENRRequest to an invalid AddrPort and waiting for a
timeout.

The other UDPEndpoint-using request paths in this file already do this:

  ping        v4_udp.go:215   errNoUDPEndpoint
  Ping        v4_udp.go:228   errNoUDPEndpoint
  newLookup   v4_udp.go:309   errNoUDPEndpoint
  RequestENR  v4_udp.go:358   addr, _ := n.UDPEndpoint()  <-- outlier

RequestENR is reachable from external callers like cmd/devp2p/crawl.go,
which feeds in arbitrary nodes that may not have a UDP port set. Before
this change, such nodes burn one full RPC timeout; after it, the caller
gets a clean error immediately.

The added test fails on master with "RPC timeout" and the trace logs
"PING/v4 addr=invalid AddrPort", confirming packets are being written to
an unspecified address; with the fix it returns errNoUDPEndpoint without
doing any I/O.
This PR adds the support of Pebble v2, details as below:

- Pebble V2 will be used if database is empty
- Pebble V1 will be used if database is not empty and the format is old
- Upgrade command (geth db pebble-upgrade) is provided to upgrade the
format to v2 offline
When ancient history is pruned, geth serves old block bodies and receipts
back from era files on disk. Until now that fallback only worked for .era1
files (pre-merge), so requests for post-merge blocks backed by .ere files
failed even though the data was present. This PR generalizes the era store
to open both formats.

---------

Co-authored-by: Sina Mahmoodi <itz.s1na@gmail.com>
…es (#34772)

Replace 1-byte-per-bit path encoding with bit-packed `BitArray`,
reducing DB key size by 8x

Benchmark (sparse single-leaf write, M3 Pro):
```
                           │  Before (1B/bit)  │  After (BitArray)  │
                           │      sec/op       │  sec/op   vs base  │
CollectNodesSparseWrite-11        10.50µ ± 1%   9.78µ ± 1%  -6.86%
                           │       B/op        │   B/op    vs base  │
CollectNodesSparseWrite-11       5.50Ki ± 0%   5.09Ki ± 0%  -7.38%
                           │    allocs/op      │ allocs   vs base   │
CollectNodesSparseWrite-11          67 ± 0%       58 ± 0%  -13.43%
```

---------

Co-authored-by: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
Implements spec change ethereum/EIPs#11807

This PR resolves the conflict between the EIP-7928 and EIP-8037.
Specifically in contract deployment, EIP-7928 requires to not resolve 
the deployed account until it's accessed, while in EIP-8037, the early 
access is required to determine if the account-creation should
be charged or not.

This PR addresses this conflict by changing the EIP-8037 a bit,
unconditionally charge the account creation in CREATE Family 
(CreateTx, Create/Create2 opcode) and refunds the associated 
gas cost if the account creation doesn't happen ultimately.

Checkout https://hackmd.io/@bFEBbZiVSAO0IURh9qzEFg/BJmFYqCeGl for more
details

What's more, now the LIFO mechanism is used for refilling the state cost
in frame revert, frame halt, state opcode refunds.
This PR improves the block download used by snap sync. Specifically,
blocks and their associated data (receipts and canonical hash mappings)
are now written directly to the database without checking existence.

The current implementation could fail in cases where the block header
and body were already present (has.Block returns true), but the
corresponding canonical hash mapping was missing. One possible scenario
is when a newPayload event is processed without a subsequent
forkChoiceUpdate.

It is still unclear why Geth may re-enter snap sync after Engine API
events have been processed after the sync. Anyway, bypassing the
existence is a reasonable change.

What's more, in the downloader, the presence of canonical hash is also
considered for deciding the range of blocks to be downloaded.
Specifically:

- in the full sync, the block with header and body available but
canonical hash missing will be re-inserted;
- in the snap sync, the block with header, body and receipt available
but canonical hash missing will be re-inserted;
This PR addresses the panic in tests. As the eventLoop is spun up when
the downloader was closed, the sub will be nil and make the panic
happens.

```
  goroutine 421 [running]:
  github.com/ethereum/go-ethereum/eth/downloader.(*DownloaderAPI).eventLoop(0xcb0e4d0)
      /opt/actions-runner/_work/go-ethereum/go-ethereum/eth/downloader/api.go:91 +0x127
  created by github.com/ethereum/go-ethereum/eth/downloader.NewDownloaderAPI in goroutine 352
      /opt/actions-runner/_work/go-ethereum/go-ethereum/eth/downloader/api.go:50 +0xf2
```
## Summary

- Release the storage iterator after iterating slots in `geth snapshot
dump`, matching the existing account iterator cleanup.

## Test plan

- [x] `go build ./cmd/geth/...`
- [ ] Manual: run `geth snapshot dump` on a node with storage data and
verify output is unchanged
Implements https://eips.ethereum.org/EIPS/eip-2780

---------

Co-authored-by: Marius van der Wijden <m.vanderwijden@live.de>
This feature is an optimization used in the BAL, mostly for experimental
purpose.

---------

Co-authored-by: jwasinger <j-wasinger@hotmail.com>
Co-authored-by: MariusVanDerWijden <m.vanderwijden@live.de>
…5250)

listEIP7610EligibleAccounts opens an account iterator and never releases it.
`devp2p discv4 listen` / `discv5 listen` is the supported replacement
for the removed bootnode tool, but it bound IPv4-only and `-extaddr`
took a single address, so it couldn't run a dual-stack bootnode.

This binds the listener dual-stack (falling back to IPv4-only where IPv6
is unavailable) and lets `-extaddr` take a comma-separated IPv4/IPv6
pair. A single node can then advertise both `ip` and `ip6` in its ENR
over one UDP port:

```
devp2p discv4 listen --nodekey <key> --addr [::]:30301 \
  --extaddr 203.0.113.10:30301,[2001:db8::1]:30301
```

The fallback IP is only derived from the listener when no `-extaddr` is
given, so a v4- or v6-only `-extaddr` no longer leaks a loopback entry.
All addresses must share one UDP port (single socket).
Clone the existing terminal handler attrs before appending new attrs.
This avoids a potential attr memory overwrite when append reuses the
backing array shared with the parent handler.
…bles (#35258)

This PR is related to the recent bug reported in #35210.

While trying to reproduce the error, I found that when the head state is
missing (e.g. unclean shutdown), we attempt to truncate the head to the
most recent block with state across all chain freezer tables.

However, for newly added tables such as the bal table, both the head and
tail are initialized to the minimum head of the existing chain freezer
tables. As a result, the `truncateHead` fails with the “truncate below
tail” error.

This PR fixes the issue by resetting newly added empty tables with
`items` as the tail when `truncateHead(items)` is called on them.
This PR adds blobTxForPool migration support in limbo. Previously, there
was no conversion path from limbo entries containing types.Transaction.
Now that we have the new blobTxForPool type, this PR adds migration
logic between both types. New test code (limbo_test.go) to test this
conversion is added.

---------

Co-authored-by: Felix Lange <fjl@twurst.com>
rjl493456442 and others added 16 commits July 23, 2026 22:49
This PR parallelizes the block validation alongside the IntermediateRoot, 
saving the time spent on the receiptRoot hashing, BAL hashing and so on.
The deep-reorg check used depth >= maxReorgDepth, rejecting reorgs at
exactly the configured limit. Use > so a depth equal to maxReorgDepth is
still accepted.
This PR coordinates the prefetcher with the main tx executor. Block
processing publishes the index of the transaction it is executing,
prefetch workers skip anything already reached and transactions above 1M
gas are promoted to the front of the prefetch queue while the rest keeps
block order.
commitPivotBlock updated committed without pivotLock, while other pivot
transitions serialize through that lock. Take the lock around the store
to keep pivot commitment consistent with pivotHeader updates.
This PR fixes the incorrect size calculation for blob sidecar. The
original formula is for legacy sidecar without the version tag. As the
legacy version has been deprecated and no longer supported by the Geth's
blobPool, the size calculation should also be flipped to sidecar v1.
This PR updates the go-snappy dependency.
Twenty upstream first-parent commits, `v1.17.4..04bf045`. Thirty conflicted
paths, and eleven further breaks that git reported as clean merges — the
conflicts were the cheap part of this batch.

Hardfork and consensus surfaces.

EIP-7954 moves MaxCodeSizeAmsterdam from 32768 to 65536. Dormant: bor's live cap
is MaxCodeSizePostAhmedabad, reached through the Ahmedabad gate, and Amsterdam is
nil on every preset. It does change the enable-time delta, though — activating
Amsterdam would now double the contract size limit rather than leave it
unchanged, which is a product decision and is recorded as one.

EIP-8282 arrives entangled with the block-access-list construction cluster
declined during the v1.17.4 sync: upstream's PostExecution returns three values
and its helpers take rules and a construction list, where bor's take neither.
Neither side-pick was right, since keeping ours drops a new EIP and taking
theirs re-adopts the declined cluster sideways. The EIP is wired dormant in
bor's own shape instead, mirroring the existing withdrawal and consolidation
queue wrappers, behind bor's block-based Amsterdam gate.

EIP-8246 removes the SELFDESTRUCT self-burn under Amsterdam, so the residual
burn-log pass and its log type are deleted upstream. All of those removals
landed cleanly, but bor also carried a parallel-executor twin of the burn-log
function that upstream cannot see, which survived as dead code still calling the
deleted log constructor. Removing it keeps the two executors in lockstep: the
sole call site dispatched through the vm.StateDB interface and served both.

The state-prefetch fix for absent accounts turned out to be upstream converging
on bor's existing ordering, so bor's side was kept and witness behaviour is
untouched.

Two upstream commits were adopted as units after partial adoption proved unsafe.

The snap-sync canonical-mapping fix has a detection half and a repair half that
only work together; taking detection alone makes the downloader re-request a
block forever while the writer keeps skipping it. Adopted in full across all
four landing sites, including bor's two inlined linkage checks and its diverged
writeLive, which keeps its own signature, header collection and receipt
handling.

The v0 blob sidecar drop was initially declined at the validation layer, which
was wrong: the commit's production half had already auto-merged, leaving
validation demanding v0 while the pool stored v1 only. The tests found it. Now
adopted as a unit.

Pebble v2 is a port rather than an adoption. Upstream keeps the old
implementation alongside a version detector and makes the main file the v2 path;
bor's write-path tuning and its bespoke amplification metrics live in that file
and were carried across onto v2's renamed fields, verified against the module
source. Half those renames were outside the conflicts and only the compiler
found them. The instrumentation was deliberately not duplicated into the
retained v1 implementation, so a node on a pre-v2 database format reports only
upstream's basic metrics until it migrates — recorded rather than discovered
later.

The era store's ere-file support is declined wholesale: it imports a package bor
deleted entirely, so there is no landing site.

Four new upstream EIP test files are removed as coverage gaps. Each depends on
either the timestamp-based Amsterdam field bor does not have or on a fixture
from the declined block-access-list tests. Bor takes the EIP code without
upstream's tests, and each file has a backlog row saying what re-adding it would
need.

Verified: build clean; full-tree vet clean apart from the two pre-existing
lock-copy findings; gofmt clean; go mod tidy clean in both modules; and the
touched packages all pass — pebble, params, rawdb and eradb, the txpool and its
subpools, downloader, state, vm, tracers and devp2p. One failure is pre-existing
and signature-matched to the baseline: an ethtest panic on a nil Bor config
reached from the miner's work loop, which CI does not see because its test list
filters the command packages.
Twenty upstream first-parent commits, `04bf04530..f9417bb`. Twenty-four
conflicted paths and five further breaks that git reported as clean merges.

Two commits dominate, and both were adopted as units because declining the
conflicted files would have stranded a half that had already auto-merged.

The blob schedule changes shape upstream: from Prague onward it is updated only
at BPO forks, so named forks no longer declare their own configuration. Bor has
no BPO forks, so adoption reduces to dropping the Osaka entry and taking the
rewritten lookup. It is behaviour-neutral here, because bor's Osaka blob values
were byte-identical to Prague's and the fall-through returns the same numbers;
neither production network configures a blob schedule at all. The rewrite also
brings a nil guard the previous switch lacked, which matters precisely because
a nil schedule is bor's production case. Bor's own Verkle entry is untouched by
the upstream change and is kept.

The Amsterdam override flag is the quieter of the two. Its flag definition and
command registration had already merged cleanly, so declining the remaining
files would have shipped an option that parses, is accepted, and does nothing —
no build failure and no failing test to catch it. It is wired through instead,
converted to bor's block-based gate rather than upstream's timestamp.

Three EIPs arrive and all merge dormant. The deterministic deployment factory
auto-merged against an Amsterdam predicate bor does not have and was rewired to
the block form, where "first block on which the fork is active" is the same
condition. The cold storage access cost arrived as an add beside bor's own
PIP-88 SLOAD helper at the same offset; both are kept, and declining was not
available because its call site had already merged. The block-access-list spec
change is a re-decline touching only an already-deleted test file.

Elsewhere: the transaction-handler preallocation has no landing site, since the
function it optimises does not exist in bor after two earlier protocol
declines; the authorization-scheme change is taken verbatim despite an upstream
defect, so that upstream's own fix will merge cleanly later; and a test
diagnostic missing its format verbs is completed by hand where it did not
auto-merge.

Three more upstream EIP test files are removed as coverage gaps. Unlike the
earlier ones these depend on nothing of their own — they are orphaned by helper
definitions removed in the previous batch. Both of those blocking files turn
out to be gated on a single line each, so one small follow-up would restore
coverage for all five EIPs bor has now adopted dormant. That is recorded as one
backlog entry rather than seven, and deliberately left out of this merge.

Verified: build clean; full-tree vet clean apart from the two pre-existing
lock-copy findings; gofmt clean; go mod tidy clean in the main module; and the
touched packages all pass. Two packages fail and both were re-run at the
previous batch's commit and reproduce there with identical assertions, so
neither belongs to this batch; CI does not see either, because its test list
filters the command packages. The previous batch's claim that the keeper module
was tidy is corrected: it is not, it does not build standalone either before or
after this batch, and since nothing upstream touches it here that churn is left
out rather than folded into a merge commit.
Twenty upstream first-parent commits, `f9417bb27..76e3dc6`. Twenty-two
conflicted paths, and three further breaks that git reported as clean merges.
The one that mattered most was not in a conflict at all.

Upstream decouples chain rewinding from state recovery in the path scheme, so a
deep rollback is performed once at the end rather than block by block. Bor
carried a stateless-node exemption around the old state check, upstream deleted
that check and reintroduced its replacement further down the same function, and
the replacement merged cleanly without the exemption. A stateless node holds no
state by design, so it would have reached the non-genesis crash branch on any
rewind and exited. The exemption is re-applied at the new site, leaving
stateless behaviour exactly as it was and giving every other node the
single-shot rollback. Bor's other local difference here vanished with the block
that carried it.

The deterministic deployment factory now runs during block building, simulation
and tracing rather than during block processing alone, which is the difference
between a producer and its validators agreeing on the state root at the
activation block. It is adopted in bor's block-gated form, and the transition
helper is retired in favour of the pre-execution path that upstream moved it to.
One gap is recorded rather than closed: bor's miner does not call the shared
pre-execution routine at all, inlining its own steps instead, so the fix has no
landing site there. The conflicted region on bor's side is empty, and adding the
step would be authoring consensus-path code rather than resolving a conflict, so
it belongs with the enablement work.

The block access list ordering check becomes strict, so a duplicated address is
rejected as the spec requires. The helper it calls exists upstream but had been
removed here once its last caller went away, so taking the one-line change alone
would not have compiled; the helper is restored along with it.

The access list exchange fix applies to a protocol surface this fork does not
carry, and is declined accordingly; the test that arrived with it outside any
conflict is removed for the same reason. A flag-parsing correction that treats
an explicit false as presence is applied to this fork's own network cases too,
since the neighbouring case had already merged with the new behaviour and one
switch should not mix both.

Verified: build clean after correcting one clean-merge break in the transition
tool; full-tree vet clean apart from the two pre-existing lock-copy findings;
gofmt clean; go mod tidy clean in the main module; and the touched packages all
pass. Three packages fail and each was re-run at the previous batch's commit and
reproduces there with an identical failure set, so none belongs to this batch.
The transition tool was baselined explicitly because this batch edits it. CI
sees none of the three, because its test list filters the command packages.

A separate finding on a live gas-metering path was raised during this batch's
twin scan and deferred to the follow-up list by decision; it is not addressed
here.
Twenty upstream first-parent commits, `76e3dc6b5..6e6fcef`. Thirty-two
conflicted paths, the most of this sync, resolving to its smallest diff: four
upstream commits are declined whole and taken as follow-up work instead.

The batch exists for the new fork stub, which lands gated off. Upstream schedules
it by timestamp; this fork schedules by block, so the field, the activation
helper, the rules flag, the fork-order and compatibility entries, the banner and
the four virtual-machine sites are all converted to the block form used by every
neighbouring fork. One of those, the compatibility entry, had merged cleanly in
the timestamp form and would not have compiled. The gate is nil on all six
surfaces: both sets of chain parameters, both runtime presets and both packaged
genesis files. Upstream enables it from genesis on one development preset; this
fork's development chain stops several forks earlier, and the new fork's
instruction set derives from one it never enables, so it is left nil there too.

The precompile delta against the previous fork is empty, and that was read off
the stub rather than assumed: the new instruction set is the previous one with
nothing enabled on top, and both precompile lookups return the previous fork's
tables, so the P256 verifier is carried forward as required. This fork's own
later forks are matched ahead of it in all three switches, so their precompile
sets cannot be displaced. Two local guards fired on the new rules field and were
worked through rather than silenced: the multi-client precompile checklist, and
the classification of every fork against the serial and parallel state
processors, where a stub with no state-processor branch is recorded as absent
from both.

Of the four declined commits, three had already merged part of themselves
cleanly, so declining them file by file left the other half stranded: a whole
new peer-scoring package with references bleeding into merged code, an
access-list field this fork removed long ago leaving a dangling reference and
unused imports, and a test-harness accessor for a field this fork's environment
type does not have. Each was reverted in full instead. A fourth commit was
declined only because it shares a file with two of those, and keeping its
configuration half alone would have shipped a command-line flag wired to
nothing.

The largest decline is the intrinsic-gas and gas-budget rework, deferred by
decision to its own change. It is structural rather than a specification tweak,
and it quietly drops one arm of a transaction gas-limit cap that is live on both
production networks. Everything in it is gated behind a fork that is disabled
here, so deferring it changes no behaviour today. Two neighbouring commits that
share files with it were checked against its shape, found independent, and
re-applied afterwards.

Also taken: a cryptography library bump, aligned across both modules and trimmed
to exactly that change; refreshed builder contract addresses and bytecode, copied
verbatim rather than retyped; a gas-budget cap in the execution runtime; charging
the calldata floor when it exceeds regular gas; and draining oversized protocol
messages before rejecting them rather than after.

Verified: build clean after fixing two clean-merge breaks; full-tree vet clean
apart from the two pre-existing lock-copy findings; formatting and module
tidiness clean; and the touched packages all pass, including the full core suite.
One package fails with the standing signature-matched panic that continuous
integration does not see.
Twenty upstream first-parent commits, `6e6fcef0b..81ab8b5`. Forty-one
conflicted paths, the widest set of this sync, resolving to its smallest result:
eleven files. One forty-seven-file commit and five dependents are declined
together.

The large one implements the sparse blob pool, relaying blob cells rather than
whole blobs over a new protocol version. It is declined on two independent
grounds, either sufficient on its own. This fork still speaks the two older
protocol versions, having declined the two intervening ones with recorded
reasoning about state-sync receipt exclusion, so a third cannot precede them;
and the cache file this commit extends by two hundred lines does not exist here,
having been declined earlier. That makes four deferrals on the same protocol
surface, and the divergence is compounding enough to deserve one deliberate
decision rather than another batch-by-batch deferral. Two further commits fall
with it because the only files they touch are files it creates.

The new fork stub gains a correction: its instruction set now derives from the
preceding fork rather than skipping back one further, which is right, since it
follows that fork and should inherit its opcodes. The precompile finding
recorded when the stub landed is unaffected, and was re-checked rather than
assumed: the preceding fork has no precompile branch of its own and falls
through to the same table the stub returns explicitly, so the two share a set
and the delta between them remains empty.

Six other commits are declined as consequences of earlier decisions rather than
new judgements: two belong to the engine-API group already set aside, one adds
an access list to the chain export format from the declined cluster, one edits a
function introduced by the deferred gas-metering rework, one has no landing site
because this fork's chain object holds no such cache, and one is entirely test
files this fork keeps deleted. That last one also adds a new test which
references a helper from a removed file, so it joins the same orphan family.

A further pair is declined as twins already present: this fork implements both
the garbage-collection knob and the cache-derived tuning independently, in its
own command-line surface. Adopting upstream's would leave two competing knobs
across two surfaces.

Adopted, in this fork's shape: the snap-sync pivot is no longer advanced once it
has been committed. The upstream condition also consults a helper that lives in
a file renamed away here, so only the commitment guard is taken — the field
already exists and the identical guard is already used elsewhere in the forked
downloader, so this aligns two paths that had drifted apart.

Verified: build clean after three revert rounds driven by the compiler, each one
a commit whose other half had merged cleanly; full-tree vet clean apart from the
two pre-existing lock-copy findings; formatting and module tidiness clean; and
the touched packages all pass, including the full core, eth and miner suites.
One package fails with the standing signature-matched panic that continuous
integration does not see.
Ten upstream first-parent commits, `81ab8b594..9621c6a`, closing this sync.
Eighteen conflicted paths resolving to ten files — the smallest batch, the only
one to build on the first attempt, and the only one where no declined commit
left an auto-merged half behind.

The version file moves to the release value. It had merged in as an unstable
snapshot at the start of this sync and was deliberately left alone until the
commit that stabilises it, which is the last one in the range.

The one commit needing real analysis coordinates the prefetcher with the main
executor: the processor publishes the index it is executing, prefetch workers
skip anything already reached, and heavy transactions are promoted to the front
of the queue. An earlier batch had a case where upstream turned out to be
converging on this fork's own ordering, so that was checked first. This is not
that case. Both interfaces here are diverged in both directions rather than by
the added parameter alone; the published index has no sound meaning against
this fork's parallel executor, which runs transactions out of order and
speculatively, so supplying one from the serial path only would be a silently
wrong signal on the path that matters; and this fork's prefetcher is
stream-based, its workers carrying arrival order rather than block position,
with a second caller that has no block to sort at all. Declined whole, with the
port shape recorded.

Adopted, in this fork's shape: block validation now computes the derivable
header fields concurrently with the state root, with this fork's existing timer
moved to follow the root computation to its new site, and the extracted helper
losing the parameter only the declined access-list branch used. Error
precedence is unchanged.

A downloader fix was applied to this fork's twin rather than to the file
upstream changed, which was renamed away here long ago. The twin carried the
same defect — a commitment flag stored outside the lock every neighbouring
pivot transition uses — on a path that is live, and whose reader was aligned
with upstream only one batch ago. The sole caller holds no lock there.

Also taken: the sidecar size fix, which is version-conditional and therefore
correct for both sidecar shapes even though the pool half of the same commit is
declined against a shape this fork does not have; a bearer-token comparison
that was case-insensitive when tested but case-sensitive when stripped; and a
compression dependency bump, noting that upstream pinned an unreleased revision
rather than a tagged one.

Declined besides: a fetcher fix whose only file belongs to a declined feature,
a reorg-depth fix from the engine-API group, the pool half of the sidecar
commit, and a memory-limit guard that has nowhere to land because this fork
replaced that whole configuration path with its own.

The twin scan found three and resolved them three different ways — one fixed,
one with nowhere to land, and one where this fork's own constant carries the
same off-by-one upstream corrected. That last is left alone and written down:
it changes accept and reject behaviour on a constant this fork authored, which
is not something to settle on an upstream merge.

Verified: build clean on the first attempt; full-tree vet clean apart from the
two pre-existing lock-copy findings; formatting and module tidiness clean; the
touched packages all pass, including the full core and eth suites; and the race
detector was run over both the newly concurrent validation path and the changed
downloader lock ordering. Three packages fail with the standing
signature-matched failures that continuous integration does not see.
Documentation only; no code changes. Covers all six batches of this milestone,
and with them the merge work is complete: the branch carries upstream history
through the release tag, in six batches, each a single merge commit with its
reasoning recorded.

These four files were kept out of the tree until now on purpose, so that no
merge commit carried documentation churn alongside its conflict resolutions.

The milestone's weight sits in four decisions.

The new stub fork was the reason the middle batch existed. Upstream schedules it
by timestamp; this fork schedules by block, so the field, the activation helper,
the rules flag, the ordering and compatibility entries, the banner and the four
virtual-machine sites were all converted — including one compatibility entry that
had merged cleanly in the timestamp form and would not have compiled. The gate is
nil on all six surfaces. Its precompile delta against the preceding fork is
empty, and that was read off the code twice rather than assumed once: a later
batch rebased the stub's instruction set onto the preceding fork, which moved the
basis the first check had been made against, so the check was repeated against
the new basis. Two local guards fired on the new rules field and were worked
through rather than silenced.

The sparse blob pool was declined, and with it the two commits whose only files
it creates. It is the fourth deferral on the same protocol surface; that
compounding divergence is now written down as deserving one deliberate decision
rather than a fifth deferral.

The intrinsic-gas and gas-budget rework was deferred to its own change by
operator decision. It is structural rather than a specification tweak, and it
quietly drops one arm of a transaction gas-limit cap that is live on both
production networks. Everything in it is gated behind a fork that is disabled
here, so deferring it changes no behaviour today — but the ordering matters and
is recorded: the same upstream change rewrote the tests that a separate deferred
item wants to restore, so restoring those first would mean restoring tests for a
shape this fork does not have.

The prefetcher coordination was declined last, after being checked against an
earlier case in this same sync where upstream turned out to be converging on this
fork's own ordering. It is not that case, for three independent reasons recorded
in full, and the port shape is written down rather than the refusal alone.

One live-path finding was surfaced during the sync and deferred by operator
decision to work on top of this milestone. It concerns the ordering of a state
read against a gas guard in a storage-write cost function, is described here in
neutral post-fix terms, and is tracked with the other follow-ups.

Everything intentionally not adopted is in the backlog file, with what a future
adoption would require. Nothing was dropped silently.
@socket-security

socket-security Bot commented Aug 11, 2026

Copy link
Copy Markdown

@codecov

codecov Bot commented Aug 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 35.09272% with 1365 lines in your changes missing coverage. Please review.
✅ Project coverage is 54.80%. Comparing base (f399939) to head (84c5db1).

Files with missing lines Patch % Lines
ethdb/pebble/pebble_v1.go 22.39% 382 Missing and 13 partials ⚠️
core/vm/gas_table.go 0.00% 137 Missing ⚠️
core/vm/operations_acl.go 5.51% 118 Missing and 2 partials ⚠️
core/state/statedb.go 9.87% 71 Missing and 2 partials ⚠️
ethdb/pebble/version.go 23.61% 54 Missing and 1 partial ⚠️
trie/trie.go 14.06% 52 Missing and 3 partials ⚠️
core/state_transition.go 66.07% 29 Missing and 9 partials ⚠️
trie/secure_trie.go 0.00% 36 Missing ⚠️
params/config.go 8.82% 30 Missing and 1 partial ⚠️
trie/bintrie/store_commit.go 84.61% 14 Missing and 10 partials ⚠️
... and 55 more

❌ Your patch check has failed because the patch coverage (35.09%) is below the target coverage (90.00%). You can increase the patch coverage or adjust the target coverage.

Additional details and impacted files

Impacted file tree graph

@@                     Coverage Diff                     @@
##           ppatil-upstream-v1.17.4    #2354      +/-   ##
===========================================================
- Coverage                    54.96%   54.80%   -0.16%     
===========================================================
  Files                          930      934       +4     
  Lines                       168109   169551    +1442     
===========================================================
+ Hits                         92393    92924     +531     
- Misses                       69808    70667     +859     
- Partials                      5908     5960      +52     
Files with missing lines Coverage Δ
accounts/abi/bind/v2/base.go 67.74% <100.00%> (ø)
accounts/manager.go 6.00% <100.00%> (+6.00%) ⬆️
consensus/misc/eip4844/eip4844.go 47.27% <ø> (+0.84%) ⬆️
core/blockchain_reader.go 42.85% <100.00%> (ø)
core/rawdb/accessors_chain.go 59.55% <ø> (ø)
core/rawdb/freezer.go 51.57% <100.00%> (+0.35%) ⬆️
core/rawdb/schema.go 37.56% <ø> (ø)
core/state/database.go 68.42% <ø> (ø)
core/state/parallel_statedb.go 95.51% <ø> (+1.91%) ⬆️
core/state/statedb_hooked.go 48.21% <100.00%> (+0.87%) ⬆️
... and 98 more

... and 21 files with indirect coverage changes

Files with missing lines Coverage Δ
accounts/abi/bind/v2/base.go 67.74% <100.00%> (ø)
accounts/manager.go 6.00% <100.00%> (+6.00%) ⬆️
consensus/misc/eip4844/eip4844.go 47.27% <ø> (+0.84%) ⬆️
core/blockchain_reader.go 42.85% <100.00%> (ø)
core/rawdb/accessors_chain.go 59.55% <ø> (ø)
core/rawdb/freezer.go 51.57% <100.00%> (+0.35%) ⬆️
core/rawdb/schema.go 37.56% <ø> (ø)
core/state/database.go 68.42% <ø> (ø)
core/state/parallel_statedb.go 95.51% <ø> (+1.91%) ⬆️
core/state/statedb_hooked.go 48.21% <100.00%> (+0.87%) ⬆️
... and 98 more

... and 21 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

pebble v2's LevelMetrics.TablesSize is already int64, so the int64()
conversion in the actual-data-size gauge is a no-op that golangci-lint's
unconvert linter rejects.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.