erigontech / erigontech/erigon
EIP-8246: versionedReadCore wipes a preserved balance that applySubFieldWrites keeps
- 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
Assessment
This issue has not been assessed yet.