Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions execution/stagedsync/calc_state_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -603,3 +603,83 @@ func lookupKeyUpdate(t *testing.T, updates *commitment.Updates, plainKey string)
require.NotNil(t, found, "no Update emitted for plainKey %x", plainKey)
return found
}

// TestNormalizeWriteSet_GenesisBypassRetainsEmptyAccount pins that emptyRemoval=false
// retains an empty account as a full UPDATE rather than emitting SelfDestructPath.
func TestNormalizeWriteSet_GenesisBypassRetainsEmptyAccount(t *testing.T) {
zeroAddr := accounts.InternAddress([20]byte{})

ver := state.Version{TxIndex: 0, Incarnation: 0}
rawWrites := state.VersionedWrites{
&state.VersionedWrite{Address: zeroAddr, Path: state.BalancePath, Val: uint256.Int{}, Version: ver},
&state.VersionedWrite{Address: zeroAddr, Path: state.NoncePath, Val: uint64(0), Version: ver},
&state.VersionedWrite{Address: zeroAddr, Path: state.CodeHashPath, Val: accounts.EmptyCodeHash, Version: ver},
}
vm := state.NewVersionMap(nil)
for _, w := range rawWrites {
vm.Write(w.Address, w.Path, accounts.NilKey, ver, w.Val, true)
}

normalized := normalizeWriteSet(rawWrites, vm, 0, 0, nil, nil, false)

for _, w := range normalized {
assert.NotEqual(t, state.SelfDestructPath, w.Path,
"emptyRemoval=false must suppress SelfDestructPath emission for empty accounts")
}

cs := newTestCalcState()
cs.ApplyWrites(normalized)
acc, ok := cs.accounts[zeroAddr]
require.True(t, ok)
assert.False(t, acc.Deleted)

updates := newTestUpdates()
cs.FlushToUpdates(updates)
keyVal := zeroAddr.Value()
got := lookupKeyUpdate(t, updates, string(keyVal[:]))
assert.Equal(t,
commitment.BalanceUpdate|commitment.NonceUpdate|commitment.CodeUpdate,
got.Flags,
"empty account at genesis must emit full UPDATE, matching serial's TrieContext.Account")
}

// TestNormalizeWriteSet_PostGenesisEmptyAccountTriggersEIP161 pins that emptyRemoval=true
// emits SelfDestructPath for an empty account and flushes as DeleteUpdate.
func TestNormalizeWriteSet_PostGenesisEmptyAccountTriggersEIP161(t *testing.T) {
addr := accounts.InternAddress([20]byte{0xab, 0xcd})

ver := state.Version{TxIndex: 0, Incarnation: 0}
rawWrites := state.VersionedWrites{
&state.VersionedWrite{Address: addr, Path: state.BalancePath, Val: uint256.Int{}, Version: ver},
&state.VersionedWrite{Address: addr, Path: state.NoncePath, Val: uint64(0), Version: ver},
&state.VersionedWrite{Address: addr, Path: state.CodeHashPath, Val: accounts.EmptyCodeHash, Version: ver},
}
vm := state.NewVersionMap(nil)
for _, w := range rawWrites {
vm.Write(w.Address, w.Path, accounts.NilKey, ver, w.Val, true)
}

normalized := normalizeWriteSet(rawWrites, vm, 0, 0, nil, nil, true)

sdSeen := false
for _, w := range normalized {
if w.Path == state.SelfDestructPath && w.Address == addr {
v, _ := w.Val.(bool)
sdSeen = sdSeen || v
}
}
require.True(t, sdSeen, "emptyRemoval=true must emit SelfDestructPath=true for empty account")

cs := newTestCalcState()
cs.ApplyWrites(normalized)
acc, ok := cs.accounts[addr]
require.True(t, ok)
assert.True(t, acc.Deleted)

updates := newTestUpdates()
cs.FlushToUpdates(updates)
keyVal := addr.Value()
got := lookupKeyUpdate(t, updates, string(keyVal[:]))
assert.Equal(t, commitment.DeleteUpdate, got.Flags,
"empty account with emptyRemoval=true must emit DeleteUpdate (EIP-161)")
}
4 changes: 3 additions & 1 deletion execution/stagedsync/exec3_parallel.go
Original file line number Diff line number Diff line change
Expand Up @@ -2589,7 +2589,9 @@ func (be *blockExecutor) nextResult(ctx context.Context, pe *parallelExecutor, r
}
return keys
}
txResult.writes = normalizeWriteSet(rawWrites, be.versionMap, txVersion.TxIndex, resultIncarnation, stateReader, domainStorageKeys, pe.cfg.chainConfig.IsSpuriousDragon(be.blockNum))
// Mirror txtask.go's genesis rules-clobber so empty allocs (AuRa ZeroAddress) survive.
emptyRemoval := be.blockNum != 0 && pe.cfg.chainConfig.IsSpuriousDragon(be.blockNum)
txResult.writes = normalizeWriteSet(rawWrites, be.versionMap, txVersion.TxIndex, resultIncarnation, stateReader, domainStorageKeys, emptyRemoval)
}

// Snapshot the finalized result before pushing — prevents
Expand Down
Loading