erigontech / erigontech/erigon

Parallel exec keeps a self-destructed account's nonce on a same-block value-transfer revival

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

Description

A contract deployed, self-destructed and then revived by a plain value transfer in the same block commits nonce 1 under the parallel executor and nonce 0 under the serial one.

Serial is correct: pre-Cancun, SELFDESTRUCT deletes the account at the end of its tx, so the later transfer recreates it fresh.

Measured with `TestSelfDestructReceiveAccountRecord` (execution/tests/statedb_chain_test.go), which flips `dbg.Exec3Parallel` per subtest — note that clearing `experimentalBAL` alone does not select the serial executor, since the choice is `dbg.Exec3Parallel || cfg.experimentalBAL` and `Exec3Parallel` defaults true:

| arm | committed nonce |
|---|---|
| serial, deploy in the same block | 0 |
| parallel, deploy in the same block | 1 |
| serial, deploy in an earlier block | 0 |
| parallel, deploy in an earlier block | 0 |

The divergence is in writeset normalization. `execution/state/writeset_normalize.go` emits post-destruct default account fields only when the reviving tx also did a CREATE:

```go
if sdEarlier && hasCreateContract {
SetAccountFieldZero(filtered, addr, path, ver)
continue
}
```

Dropping the `hasCreateContract` conjunct makes the parallel arm commit 0, agreeing with serial. Exactly one other test changes with it: `TestNormalizeWriteSet_SelfDestructEarlierThenCreateContractZeroesFields` (execution/stagedsync/exec3_finalize_test.go), whose control case asserts the current behaviour — a value-transfer resurrect inheriting the pre-SD nonce 9. The behaviour is therefore pinned deliberately, and fixing it needs a decision on which side is canonical rather than the one-line change.

Worth noting the two readers already disagree: `versionedReadCore` serves nonce 0 to the EVM for this map shape while the committed record holds 1, so a same-block CREATE would derive its address from 0.

Found while reviewing #23072.

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.