erigontech / erigontech/erigon

EIP-8246: versionedReadCore wipes a preserved balance that applySubFieldWrites keeps

Open
#23,238 1 comment 0 reactions 2 assignees Claimed by @taratorio View on GitHub
go
Dominant language
Go
Stars
3.6k
Forks
1.5k
Avg merge
1d 16h
Merged PRs (30d)
455

Description

`versionedReadCore` and `applySubFieldWrites` disagree about the balance of an EIP-8246 self-destructed account.

Under EIP-8246 SELFDESTRUCT no longer burns: the balance moves to the beneficiary, and a same-tx-created contract is cleared at finalization but keeps any residual balance. `selfdestructVersioned` therefore writes no `BalancePath` cell on that path (intra_block_state.go:1894-1907) — the pre-destruct cell is the live value by construction.

That leaves the balance floor *below* the destruct index, and read_paths.go:626 bumps the scan floor for `BalancePath`:

```go
lo := hdr.Version.TxIndex
if path == CodeHashPath || path == BalancePath {
lo++
}
if sdVer, ok := s.versionMap.FindDoneSelfDestructInRange(addr, lo, s.txIndex, true); ok {
```

With the floor at F and the destruct at M > F, the destruct falls inside `[F+1, txIndex)`, so `versionedReadCore` serves the EVM zero. `applySubFieldWrites` takes no destruct scan for Balance at all and serves the preserved balance. The reconstruction readers and the EVM then disagree about the same account — the bug class #23072 exists to close, on the one path that PR does not scan.

`applySubFieldWrites` looks like the correct side: the residual balance is what EIP-8246 says survives.

The `+1` bump was written for the opposite case, where the destruct writes its own balance cell *at* the destruct index and that cell is what remains after destruction. It is only wrong when there is no such cell.

## Gating

Not reachable today. `opSelfdestruct` passes `preserveBalance=true` only under `rules.IsAmsterdam` and only for a same-tx-created contract (execution/vm/instructions.go:1364-1382). The other `Selfdestruct(addr, true)` call site (intra_block_state.go:3489) is the write-set replay in `ApplyVersionedWrites`, whose write set always carries the destruct's `BalancePath` cell, so it does not produce this shape.

## Why not fixed in #23072

That PR removed `destructScanFloor`'s `BalancePath` arm as dead code — no wipe scan passes it — and its helper doc now states that Balance is deliberately not scanned. Reconciling the other direction means changing what `versionedReadCore` serves the EVM and what it records as a read, which is a change to the validated read path rather than to the reconstruction readers.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.