@@ -546,3 +546,83 @@ func lookupKeyUpdate(t *testing.T, updates *commitment.Updates, plainKey string)
546546 require .NotNil (t , found , "no Update emitted for plainKey %x" , plainKey )
547547 return found
548548}
549+
550+ // TestNormalizeWriteSet_GenesisBypassRetainsEmptyAccount pins that emptyRemoval=false
551+ // retains an empty account as a full UPDATE rather than emitting SelfDestructPath.
552+ func TestNormalizeWriteSet_GenesisBypassRetainsEmptyAccount (t * testing.T ) {
553+ zeroAddr := accounts .InternAddress ([20 ]byte {})
554+
555+ ver := state.Version {TxIndex : 0 , Incarnation : 0 }
556+ rawWrites := state.VersionedWrites {
557+ & state.VersionedWrite {Address : zeroAddr , Path : state .BalancePath , Val : uint256.Int {}, Version : ver },
558+ & state.VersionedWrite {Address : zeroAddr , Path : state .NoncePath , Val : uint64 (0 ), Version : ver },
559+ & state.VersionedWrite {Address : zeroAddr , Path : state .CodeHashPath , Val : accounts .EmptyCodeHash , Version : ver },
560+ }
561+ vm := state .NewVersionMap (nil )
562+ for _ , w := range rawWrites {
563+ vm .Write (w .Address , w .Path , accounts .NilKey , ver , w .Val , true )
564+ }
565+
566+ normalized := normalizeWriteSet (rawWrites , vm , 0 , 0 , nil , nil , false )
567+
568+ for _ , w := range normalized {
569+ assert .NotEqual (t , state .SelfDestructPath , w .Path ,
570+ "emptyRemoval=false must suppress SelfDestructPath emission for empty accounts" )
571+ }
572+
573+ cs := newTestCalcState ()
574+ cs .ApplyWrites (normalized )
575+ acc , ok := cs .accounts [zeroAddr ]
576+ require .True (t , ok )
577+ assert .False (t , acc .Deleted )
578+
579+ updates := newTestUpdates ()
580+ cs .FlushToUpdates (updates )
581+ keyVal := zeroAddr .Value ()
582+ got := lookupKeyUpdate (t , updates , string (keyVal [:]))
583+ assert .Equal (t ,
584+ commitment .BalanceUpdate | commitment .NonceUpdate | commitment .CodeUpdate ,
585+ got .Flags ,
586+ "empty account at genesis must emit full UPDATE, matching serial's TrieContext.Account" )
587+ }
588+
589+ // TestNormalizeWriteSet_PostGenesisEmptyAccountTriggersEIP161 pins that emptyRemoval=true
590+ // emits SelfDestructPath for an empty account and flushes as DeleteUpdate.
591+ func TestNormalizeWriteSet_PostGenesisEmptyAccountTriggersEIP161 (t * testing.T ) {
592+ addr := accounts .InternAddress ([20 ]byte {0xab , 0xcd })
593+
594+ ver := state.Version {TxIndex : 0 , Incarnation : 0 }
595+ rawWrites := state.VersionedWrites {
596+ & state.VersionedWrite {Address : addr , Path : state .BalancePath , Val : uint256.Int {}, Version : ver },
597+ & state.VersionedWrite {Address : addr , Path : state .NoncePath , Val : uint64 (0 ), Version : ver },
598+ & state.VersionedWrite {Address : addr , Path : state .CodeHashPath , Val : accounts .EmptyCodeHash , Version : ver },
599+ }
600+ vm := state .NewVersionMap (nil )
601+ for _ , w := range rawWrites {
602+ vm .Write (w .Address , w .Path , accounts .NilKey , ver , w .Val , true )
603+ }
604+
605+ normalized := normalizeWriteSet (rawWrites , vm , 0 , 0 , nil , nil , true )
606+
607+ sdSeen := false
608+ for _ , w := range normalized {
609+ if w .Path == state .SelfDestructPath && w .Address == addr {
610+ v , _ := w .Val .(bool )
611+ sdSeen = sdSeen || v
612+ }
613+ }
614+ require .True (t , sdSeen , "emptyRemoval=true must emit SelfDestructPath=true for empty account" )
615+
616+ cs := newTestCalcState ()
617+ cs .ApplyWrites (normalized )
618+ acc , ok := cs .accounts [addr ]
619+ require .True (t , ok )
620+ assert .True (t , acc .Deleted )
621+
622+ updates := newTestUpdates ()
623+ cs .FlushToUpdates (updates )
624+ keyVal := addr .Value ()
625+ got := lookupKeyUpdate (t , updates , string (keyVal [:]))
626+ assert .Equal (t , commitment .DeleteUpdate , got .Flags ,
627+ "empty account with emptyRemoval=true must emit DeleteUpdate (EIP-161)" )
628+ }
0 commit comments