@@ -267,6 +267,225 @@ func TestStorageRoundTrip(t *testing.T) {
267267 }
268268}
269269
270+ // newTestTrie creates a fresh BinaryTrie with an empty root and a default
271+ // prevalue tracer. Used by tests that need a realistic trie instance.
272+ func newTestTrie (t * testing.T ) * BinaryTrie {
273+ t .Helper ()
274+ return & BinaryTrie {
275+ root : NewBinaryNode (),
276+ tracer : trie .NewPrevalueTracer (),
277+ }
278+ }
279+
280+ // makeAccount constructs a StateAccount with the given fields. The Root is
281+ // zeroed out because the bintrie has no per-account storage root.
282+ func makeAccount (nonce uint64 , balance uint64 , codeHash common.Hash ) * types.StateAccount {
283+ return & types.StateAccount {
284+ Nonce : nonce ,
285+ Balance : uint256 .NewInt (balance ),
286+ CodeHash : codeHash .Bytes (),
287+ }
288+ }
289+
290+ // TestDeleteAccountRoundTrip verifies the basic delete path: create an
291+ // account, read it back, delete it, confirm subsequent reads return nil.
292+ // Regression test for the no-op DeleteAccount bug where the deletion was
293+ // silently ignored and the old values remained in the trie.
294+ func TestDeleteAccountRoundTrip (t * testing.T ) {
295+ tr := newTestTrie (t )
296+ addr := common .HexToAddress ("0x1234567890abcdef1234567890abcdef12345678" )
297+ codeHash := common .HexToHash ("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470" )
298+
299+ // Create: write account, verify round-trip.
300+ acc := makeAccount (42 , 1000 , codeHash )
301+ if err := tr .UpdateAccount (addr , acc , 0 ); err != nil {
302+ t .Fatalf ("UpdateAccount: %v" , err )
303+ }
304+ got , err := tr .GetAccount (addr )
305+ if err != nil {
306+ t .Fatalf ("GetAccount: %v" , err )
307+ }
308+ if got == nil {
309+ t .Fatal ("GetAccount returned nil after UpdateAccount" )
310+ }
311+ if got .Nonce != 42 {
312+ t .Fatalf ("Nonce: got %d, want 42" , got .Nonce )
313+ }
314+ if got .Balance .Uint64 () != 1000 {
315+ t .Fatalf ("Balance: got %s, want 1000" , got .Balance )
316+ }
317+ if ! bytes .Equal (got .CodeHash , codeHash [:]) {
318+ t .Fatalf ("CodeHash: got %x, want %x" , got .CodeHash , codeHash )
319+ }
320+
321+ // Delete: verify GetAccount returns nil afterwards.
322+ if err := tr .DeleteAccount (addr ); err != nil {
323+ t .Fatalf ("DeleteAccount: %v" , err )
324+ }
325+ got , err = tr .GetAccount (addr )
326+ if err != nil {
327+ t .Fatalf ("GetAccount after delete: %v" , err )
328+ }
329+ if got != nil {
330+ t .Fatalf ("GetAccount after delete: got %+v, want nil" , got )
331+ }
332+ }
333+
334+ // TestDeleteAccountOnMissingAccount verifies that deleting an account that
335+ // was never created does not error and subsequent reads still return nil.
336+ func TestDeleteAccountOnMissingAccount (t * testing.T ) {
337+ tr := newTestTrie (t )
338+ addr := common .HexToAddress ("0x1234567890abcdef1234567890abcdef12345678" )
339+
340+ // Delete without any prior create. Should not panic or error on an
341+ // empty root, and GetAccount should still return nil.
342+ if err := tr .DeleteAccount (addr ); err != nil {
343+ t .Fatalf ("DeleteAccount on empty trie: %v" , err )
344+ }
345+ got , err := tr .GetAccount (addr )
346+ if err != nil {
347+ t .Fatalf ("GetAccount after delete on empty trie: %v" , err )
348+ }
349+ if got != nil {
350+ t .Fatalf ("GetAccount on deleted missing account: got %+v, want nil" , got )
351+ }
352+ }
353+
354+ // TestDeleteAccountPreservesOtherAccounts verifies that deleting one account
355+ // does not affect accounts at different stems.
356+ func TestDeleteAccountPreservesOtherAccounts (t * testing.T ) {
357+ tr := newTestTrie (t )
358+ addrA := common .HexToAddress ("0x1234567890abcdef1234567890abcdef12345678" )
359+ addrB := common .HexToAddress ("0xabcdef1234567890abcdef1234567890abcdef12" )
360+ codeHashA := common .HexToHash ("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470" )
361+ codeHashB := common .HexToHash ("f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff0102030405060708090a0b0c0d0e0f10" )
362+
363+ // Create two distinct accounts.
364+ if err := tr .UpdateAccount (addrA , makeAccount (1 , 100 , codeHashA ), 0 ); err != nil {
365+ t .Fatalf ("UpdateAccount(A): %v" , err )
366+ }
367+ if err := tr .UpdateAccount (addrB , makeAccount (2 , 200 , codeHashB ), 0 ); err != nil {
368+ t .Fatalf ("UpdateAccount(B): %v" , err )
369+ }
370+
371+ // Delete A.
372+ if err := tr .DeleteAccount (addrA ); err != nil {
373+ t .Fatalf ("DeleteAccount(A): %v" , err )
374+ }
375+
376+ // A should be gone.
377+ if got , err := tr .GetAccount (addrA ); err != nil {
378+ t .Fatalf ("GetAccount(A): %v" , err )
379+ } else if got != nil {
380+ t .Fatalf ("GetAccount(A) after delete: got %+v, want nil" , got )
381+ }
382+
383+ // B should still be readable with its original values.
384+ got , err := tr .GetAccount (addrB )
385+ if err != nil {
386+ t .Fatalf ("GetAccount(B): %v" , err )
387+ }
388+ if got == nil {
389+ t .Fatal ("GetAccount(B) returned nil after unrelated delete" )
390+ }
391+ if got .Nonce != 2 {
392+ t .Fatalf ("Account B Nonce: got %d, want 2" , got .Nonce )
393+ }
394+ if got .Balance .Uint64 () != 200 {
395+ t .Fatalf ("Account B Balance: got %s, want 200" , got .Balance )
396+ }
397+ if ! bytes .Equal (got .CodeHash , codeHashB [:]) {
398+ t .Fatalf ("Account B CodeHash: got %x, want %x" , got .CodeHash , codeHashB )
399+ }
400+ }
401+
402+ // TestDeleteAccountThenRecreate verifies that an account can be deleted and
403+ // then recreated with different values; the second read must return the new
404+ // values, not the stale ones from before deletion.
405+ func TestDeleteAccountThenRecreate (t * testing.T ) {
406+ tr := newTestTrie (t )
407+ addr := common .HexToAddress ("0x1234567890abcdef1234567890abcdef12345678" )
408+ codeHash1 := common .HexToHash ("1111111111111111111111111111111111111111111111111111111111111111" )
409+ codeHash2 := common .HexToHash ("2222222222222222222222222222222222222222222222222222222222222222" )
410+
411+ // Create.
412+ if err := tr .UpdateAccount (addr , makeAccount (1 , 100 , codeHash1 ), 0 ); err != nil {
413+ t .Fatalf ("UpdateAccount #1: %v" , err )
414+ }
415+ // Delete.
416+ if err := tr .DeleteAccount (addr ); err != nil {
417+ t .Fatalf ("DeleteAccount: %v" , err )
418+ }
419+ // Recreate with new values.
420+ if err := tr .UpdateAccount (addr , makeAccount (7 , 9999 , codeHash2 ), 0 ); err != nil {
421+ t .Fatalf ("UpdateAccount #2: %v" , err )
422+ }
423+ // Read: must observe the new values, not the originals.
424+ got , err := tr .GetAccount (addr )
425+ if err != nil {
426+ t .Fatalf ("GetAccount: %v" , err )
427+ }
428+ if got == nil {
429+ t .Fatal ("GetAccount returned nil after recreate" )
430+ }
431+ if got .Nonce != 7 {
432+ t .Fatalf ("Nonce: got %d, want 7" , got .Nonce )
433+ }
434+ if got .Balance .Uint64 () != 9999 {
435+ t .Fatalf ("Balance: got %s, want 9999" , got .Balance )
436+ }
437+ if ! bytes .Equal (got .CodeHash , codeHash2 [:]) {
438+ t .Fatalf ("CodeHash: got %x, want %x" , got .CodeHash , codeHash2 )
439+ }
440+ }
441+
442+ // TestDeleteAccountDoesNotAffectMainStorage verifies that DeleteAccount only
443+ // clears the account's BasicData and CodeHash, leaving main storage slots
444+ // (which live at different stems) untouched. Wiping storage on self-destruct
445+ // is a separate concern handled at the StateDB level.
446+ func TestDeleteAccountDoesNotAffectMainStorage (t * testing.T ) {
447+ tr := newTestTrie (t )
448+ addr := common .HexToAddress ("0x1234567890abcdef1234567890abcdef12345678" )
449+ codeHash := common .HexToHash ("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470" )
450+
451+ // Create account.
452+ if err := tr .UpdateAccount (addr , makeAccount (1 , 100 , codeHash ), 0 ); err != nil {
453+ t .Fatalf ("UpdateAccount: %v" , err )
454+ }
455+ // Write a main storage slot — i.e. key[31] >= 64 or key[:31] != 0 — so
456+ // it lives at a different stem from the account header.
457+ slot := common .HexToHash ("0000000000000000000000000000000000000000000000000000000000000080" )
458+ value := common .TrimLeftZeroes (common .HexToHash ("00000000000000000000000000000000000000000000000000000000deadbeef" ).Bytes ())
459+ if err := tr .UpdateStorage (addr , slot [:], value ); err != nil {
460+ t .Fatalf ("UpdateStorage: %v" , err )
461+ }
462+
463+ // Delete the account.
464+ if err := tr .DeleteAccount (addr ); err != nil {
465+ t .Fatalf ("DeleteAccount: %v" , err )
466+ }
467+
468+ // Account should be absent.
469+ if got , _ := tr .GetAccount (addr ); got != nil {
470+ t .Fatalf ("GetAccount after delete: got %+v, want nil" , got )
471+ }
472+
473+ // Main storage slot should still be readable — DeleteAccount must not
474+ // have touched it.
475+ got , err := tr .GetStorage (addr , slot [:])
476+ if err != nil {
477+ t .Fatalf ("GetStorage after DeleteAccount: %v" , err )
478+ }
479+ if len (got ) == 0 {
480+ t .Fatal ("main storage slot was wiped by DeleteAccount, expected it to survive" )
481+ }
482+ var expected [HashSize ]byte
483+ copy (expected [HashSize - len (value ):], value )
484+ if ! bytes .Equal (got , expected [:]) {
485+ t .Fatalf ("main storage slot: got %x, want %x" , got , expected )
486+ }
487+ }
488+
270489func TestBinaryTrieWitness (t * testing.T ) {
271490 tracer := trie .NewPrevalueTracer ()
272491
0 commit comments