Skip to content

[SharovBot] fix Amsterdam signer support and BAL non-determinism#19434

Merged
yperbasis merged 12 commits intomainfrom
fix/amsterdam-signer-and-bal
Mar 2, 2026
Merged

[SharovBot] fix Amsterdam signer support and BAL non-determinism#19434
yperbasis merged 12 commits intomainfrom
fix/amsterdam-signer-and-bal

Conversation

@Giulio2002
Copy link
Collaborator

@Giulio2002 Giulio2002 commented Feb 23, 2026

[SharovBot] Fix Amsterdam signer support and BAL non-determinism in parallel execution

Summary

  • Amsterdam signer: Add Amsterdam fork handling in MakeSigner and LatestSigner so that setCode and blob tx types are supported when only AmsterdamTime is configured (without PragueTime).
  • BAL non-determinism (parallel execution): Fix multiple sources of non-deterministic BAL (EIP-7928) hashes during parallel tx execution:
    • Sort ApplyVersionedWrites output by (Address, Path, Key) for deterministic application order
    • Sync WaitGroup before block finalization to ensure all prior tx state is applied to pe.rs
    • Flush merged writes (execution + finalize) to the version map so later tx finalizations see the full post-tx state
    • Read coinbase balance from the version map (via versionedStateReader) rather than a stale pe.rs snapshot
    • Sync CodeHash in getStateObject when code is loaded from the version map, preventing the "revert to original" optimization from incorrectly deleting code writes
    • Atomic version map flush: FlushVersionedWrites now holds a single lock for all writes, preventing concurrent workers from observing a partially-flushed state (e.g. seeing AddressPath but not the corresponding CodePath from the same transaction)

Test plan

  • execution/tests package compiles without errors
  • TestExecutionSpecBlockchainDevnet/amsterdam passes in a single run
  • TestExecutionSpecBlockchainDevnet/amsterdam passes 30 consecutive times with -count=1
  • TestExecutionSpecBlockchainDevnet/prague/eip7702 passes (no regression)

Fixes CI job: https://github.com/erigontech/erigon/actions/runs/22296091141/job/64492938121

🤖 Generated with Claude Code

Giulio2002 and others added 3 commits February 23, 2026 14:41
Two issues caused the Amsterdam execution-spec tests to fail:

1. MakeSigner/LatestSigner did not handle AmsterdamTime independently of
   PragueTime. Test fixtures for the Amsterdam fork set AmsterdamTime but
   leave PragueTime nil, so the signer returned for Amsterdam blocks was
   missing setCode and (in LatestSigner) blob capabilities. Add an
   explicit Amsterdam case to MakeSigner and extend the LatestSigner
   checks to include AmsterdamTime.

2. During parallel block execution the finalization merge path in
   nextResult appended the complete post-finalization write set to the
   pre-existing writes, creating duplicate (Address, Path, Key) entries.
   Because IntraBlockState.VersionedWrites iterates a Go map whose
   order varies across processes, the duplicates were processed in
   non-deterministic order by CreateBAL, producing different balance-
   change lists and therefore different BAL hashes ~10% of the time.
   Replace instead of append since addWrites already contains all
   original writes (re-applied via ApplyVersionedWrites) plus the
   finalization changes.

Fixes: https://github.com/erigontech/erigon/actions/runs/22296091141/job/64492938121

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
During parallel block execution, stateObject.Code() only reads from
the state reader (e.g. BufferedReader), which does not see code written
by prior transactions in the same block (only in the version map).
This caused EIP-7702 delegation code set by tx0 to be invisible to
tx1, producing incorrect BAL (Block Access List) entries.

Three complementary fixes:
- stateObject.Code(): fall back to version map when code is nil
- versionedStateReader.ReadAccountCode/Size: check version map for
  CodePath entries (consistent with ReadAccountData)
- exec3_parallel.go finalize: merge finalization writes with execution
  writes instead of replacing, preserving entries the finalization
  replay may drop due to stale state
- bal_create.go: deterministic sort of writes for stable BAL output

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fix EIP-7928 block access list hash non-determinism during parallel
transaction execution by addressing several race conditions:

1. Synchronise CodeHash in getStateObject when code is loaded from the
   version map (written by a prior tx).  refreshVersionedAccount may
   skip updating CodeHash because sdb.Version() makes the version check
   fail for entries from earlier transactions.  The stale CodeHash caused
   SetCode's "revert to original" optimisation to incorrectly delete
   code writes when clearing an EIP-7702 delegation set by a prior tx.

2. Sort ApplyVersionedWrites entries by (Address, Path, Key) so that
   state object creation order is deterministic regardless of Go map
   iteration order.

3. Add WaitGroup synchronisation between the apply-loop goroutine and
   block finalization to ensure all per-tx state updates are visible
   before finalize reads state.

4. Flush merged writes (including fee-calc changes) to the version map
   so subsequent per-tx finalizations see full post-tx state.

5. Use versionedStateReader for coinbase balance reads during finalize
   to get deterministic values from the version map.

6. Enhance versionedStateReader to check the version map for accounts,
   code, and storage written by prior transactions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@Giulio2002
Copy link
Collaborator Author

[SharovBot] 🔧 Fixed lint failure — execution/stagedsync/exec3_parallel.go:1060 had incorrect indentation (gofmt). Corrected comment/code block indentation inside finalize() coinbase reader section.

@Giulio2002 Giulio2002 closed this Feb 23, 2026
…rallel execution

FlushVersionedWrites previously wrote each entry under a separate lock,
allowing concurrent workers to observe a partially-flushed state (e.g.
seeing an AddressPath write but not the corresponding CodePath write
from the same transaction). This caused intermittent BAL (EIP-7928)
hash mismatches during parallel execution of EIP-7702 SET_CODE tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@Giulio2002 Giulio2002 reopened this Feb 23, 2026
@Giulio2002
Copy link
Collaborator Author

Giulio2002 commented Feb 23, 2026

@mh0lt flaky test fix for amsterdam. some might be stupid and useless. especially the whole Amsterdam case part?

… deadlocks test apply loop

The applyWg mechanism called Add(1) per txResult and expected Done() to be
called by the apply-loop consumer. The production apply loop does call Done(),
but the test's simplified loop (runParallel) does not, causing TestAlternatingTx
to hang until the 10-minute timeout.

The versionedStateReader approach (cbReader) already provides deterministic
coinbase reads from the version map without needing pe.rs to be fully applied
first. Remove the applyWg.Wait() gate and the Add/Done machinery entirely.
@Giulio2002
Copy link
Collaborator Author

[SharovBot] 🔧 Fixed TestAlternatingTx deadlock (10-min timeout in Unit tests).

Root cause: applyWg.Add(1) was called per txResult, expecting the apply-loop consumer to call Done(). The production apply loop does this, but the test's simplified loop (runParallel) does not — goroutine 55 blocked on applyWg.Wait() forever.

Fix: Removed the applyWg/wg WaitGroup mechanism entirely. The versionedStateReader (cbReader) approach already provides deterministic coinbase reads from the version map without needing pe.rs to be fully applied first.

@yperbasis yperbasis added the Glamsterdam https://eips.ethereum.org/EIPS/eip-7773 label Feb 24, 2026
@Giulio2002
Copy link
Collaborator Author

Giulio2002 commented Feb 25, 2026

@mh0lt can you please either merge/review or incorporate in your PR so we do not have hive failures.

Copy link
Member

@yperbasis yperbasis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

merge latest main and double check if the changes still make sense

mh0lt and others added 4 commits February 27, 2026 08:09
Amsterdam implies Prague, so:
- MakeSigner: remove duplicate Amsterdam switch case (identical to Prague)
- LatestSigner: remove redundant '|| config.AmsterdamTime != nil' checks

Addresses review feedback from yperbasis.
@yperbasis yperbasis dismissed their stale review March 2, 2026 08:28

review comments addressed

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses two execution-layer correctness issues: (1) enabling Amsterdam signer selection when only AmsterdamTime is configured, and (2) eliminating non-deterministic EIP-7928 Block Access List (BAL) hashes during parallel transaction execution by making write application/flush and certain reads deterministic.

Changes:

  • Make version-map flushing atomic and reuse lock acquisition to prevent partially-observed state during parallel execution.
  • Ensure deterministic state application and BAL construction by sorting versioned writes consistently and improving intra-block visibility of prior-tx updates.
  • Fix several parallel-finalization ordering/state-visibility gaps (coinbase reads, merging finalize writes, flushing merged writes) to stabilize BAL hashing.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
execution/state/versionmap.go Adds a locked internal write helper and makes FlushVersionedWrites flush all writes under a single lock for atomic visibility.
execution/state/versionedio.go Extends versionedStateReader to fall back to the version map for account/storage/code reads when needed for same-block visibility.
execution/state/intra_block_state.go Synchronizes CodeHash when code is loaded from the version map; sorts ApplyVersionedWrites input for deterministic processing order.
execution/stagedsync/exec3_parallel.go Uses version-map-aware reader for deterministic coinbase balance, merges finalize writes with execution writes, and flushes merged writes for subsequent finalizations.
execution/stagedsync/bal_create.go Sorts per-tx write sets before BAL construction to remove nondeterminism from iteration order.
Comments suppressed due to low confidence (1)

execution/stagedsync/bal_create.go:59

  • The comment about write slice order being non-deterministic is now misleading: this function explicitly sorts writes above, so the order is deterministic at this point. Please update the comment to reflect the actual reason for the two-pass handling (ensuring SelfDestructPath is applied before other paths), not Go map iteration order.
		// First pass: apply SelfDestructPath writes so the selfDestructed flag
		// is up-to-date before balance/nonce/code writes are processed.
		// The write slice order is non-deterministic, and a SelfDestructPath=false
		// (un-selfdestruct in a later tx) may appear after BalancePath in the slice.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

// CodeHash causes the "revert to original" optimisation in SetCode
// to incorrectly delete code writes when clearing a delegation that
// was set by a prior transaction in the same block.
codeHash := accounts.InternCodeHash(crypto.Keccak256Hash(code))
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Computing Keccak256Hash(code) here can be expensive for large contract code, and this path may be hit frequently in parallel execution when prior-tx code updates are loaded from the version map. Consider using the version map’s existing CodeHashPath value (when present) to sync CodeHash instead of hashing the full bytecode, falling back to hashing only if the hash entry is unavailable.

Suggested change
codeHash := accounts.InternCodeHash(crypto.Keccak256Hash(code))
// Prefer using the existing CodeHashPath value from the version map
// (when present) to avoid recomputing Keccak256 on large code blobs.
var rawCodeHash common.Hash
hashFromVersionMap := false
if sdb.versionMap != nil {
if res := sdb.versionMap.Read(addr, CodeHashPath, accounts.NilKey, sdb.txIndex); res.Status() == MVReadResultDone {
switch v := res.Value().(type) {
case common.Hash:
rawCodeHash = v
hashFromVersionMap = true
case [32]byte:
rawCodeHash = common.BytesToHash(v[:])
hashFromVersionMap = true
}
}
}
if !hashFromVersionMap {
rawCodeHash = crypto.Keccak256Hash(code)
}
codeHash := accounts.InternCodeHash(rawCodeHash)

Copilot uses AI. Check for mistakes.
Comment on lines +2377 to +2384
// Sort writes by (Address, Path, Key) to ensure deterministic processing
// order. VersionedWrites come from WriteSet map iteration (Go maps have
// non-deterministic order). Processing order matters because some paths
// (CodePath, SelfDestructPath) call GetOrNewStateObject which triggers a
// read from the stateReader. If a BalancePath write for the same address
// has already been processed, the state object is already loaded and no
// read occurs; otherwise an extra read is recorded. Different reads
// produce different EIP-7928 BAL hashes.
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change fixes determinism by sorting versioned writes before applying them, but there’s no regression test covering the nondeterminism scenario described in the comment (different read recording affecting BAL hash). Since this file already has tests for ApplyVersionedWrites, please add a focused test that exercises the problematic ordering (e.g., CodePath/SelfDestructPath vs BalancePath) and asserts deterministic reads/BAL-related output regardless of original write ordering.

Copilot uses AI. Check for mistakes.
@yperbasis yperbasis merged commit 9d1c662 into main Mar 2, 2026
29 checks passed
@yperbasis yperbasis deleted the fix/amsterdam-signer-and-bal branch March 2, 2026 09:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Glamsterdam https://eips.ethereum.org/EIPS/eip-7773

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants