@@ -438,18 +438,175 @@ func (s *StateSuite) TestTouchDelete(c *check.C) {
438
438
// TestCopyOfCopy tests that modified objects are carried over to the copy, and the copy of the copy.
439
439
// See https://github.com/ethereum/go-ethereum/pull/15225#issuecomment-380191512
440
440
func TestCopyOfCopy (t * testing.T ) {
441
- sdb , _ := New (common.Hash {}, NewDatabase (rawdb .NewMemoryDatabase ()))
441
+ state , _ := New (common.Hash {}, NewDatabase (rawdb .NewMemoryDatabase ()))
442
442
addr := common .HexToAddress ("aaaa" )
443
- sdb .SetBalance (addr , big .NewInt (42 ))
443
+ state .SetBalance (addr , big .NewInt (42 ))
444
444
445
- if got := sdb .Copy ().GetBalance (addr ).Uint64 (); got != 42 {
445
+ if got := state .Copy ().GetBalance (addr ).Uint64 (); got != 42 {
446
446
t .Fatalf ("1st copy fail, expected 42, got %v" , got )
447
447
}
448
- if got := sdb .Copy ().Copy ().GetBalance (addr ).Uint64 (); got != 42 {
448
+ if got := state .Copy ().Copy ().GetBalance (addr ).Uint64 (); got != 42 {
449
449
t .Fatalf ("2nd copy fail, expected 42, got %v" , got )
450
450
}
451
451
}
452
452
453
+ // Tests a regression where committing a copy lost some internal meta information,
454
+ // leading to corrupted subsequent copies.
455
+ //
456
+ // See https://github.com/ethereum/go-ethereum/issues/20106.
457
+ func TestCopyCommitCopy (t * testing.T ) {
458
+ state , _ := New (common.Hash {}, NewDatabase (rawdb .NewMemoryDatabase ()))
459
+
460
+ // Create an account and check if the retrieved balance is correct
461
+ addr := common .HexToAddress ("0xaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe" )
462
+ skey := common .HexToHash ("aaa" )
463
+ sval := common .HexToHash ("bbb" )
464
+
465
+ state .SetBalance (addr , big .NewInt (42 )) // Change the account trie
466
+ state .SetCode (addr , []byte ("hello" )) // Change an external metadata
467
+ state .SetState (addr , skey , sval ) // Change the storage trie
468
+
469
+ if balance := state .GetBalance (addr ); balance .Cmp (big .NewInt (42 )) != 0 {
470
+ t .Fatalf ("initial balance mismatch: have %v, want %v" , balance , 42 )
471
+ }
472
+ if code := state .GetCode (addr ); ! bytes .Equal (code , []byte ("hello" )) {
473
+ t .Fatalf ("initial code mismatch: have %x, want %x" , code , []byte ("hello" ))
474
+ }
475
+ if val := state .GetState (addr , skey ); val != sval {
476
+ t .Fatalf ("initial non-committed storage slot mismatch: have %x, want %x" , val , sval )
477
+ }
478
+ if val := state .GetCommittedState (addr , skey ); val != (common.Hash {}) {
479
+ t .Fatalf ("initial committed storage slot mismatch: have %x, want %x" , val , common.Hash {})
480
+ }
481
+ // Copy the non-committed state database and check pre/post commit balance
482
+ copyOne := state .Copy ()
483
+ if balance := copyOne .GetBalance (addr ); balance .Cmp (big .NewInt (42 )) != 0 {
484
+ t .Fatalf ("first copy pre-commit balance mismatch: have %v, want %v" , balance , 42 )
485
+ }
486
+ if code := copyOne .GetCode (addr ); ! bytes .Equal (code , []byte ("hello" )) {
487
+ t .Fatalf ("first copy pre-commit code mismatch: have %x, want %x" , code , []byte ("hello" ))
488
+ }
489
+ if val := copyOne .GetState (addr , skey ); val != sval {
490
+ t .Fatalf ("first copy pre-commit non-committed storage slot mismatch: have %x, want %x" , val , sval )
491
+ }
492
+ if val := copyOne .GetCommittedState (addr , skey ); val != (common.Hash {}) {
493
+ t .Fatalf ("first copy pre-commit committed storage slot mismatch: have %x, want %x" , val , common.Hash {})
494
+ }
495
+
496
+ copyOne .Commit (false )
497
+ if balance := copyOne .GetBalance (addr ); balance .Cmp (big .NewInt (42 )) != 0 {
498
+ t .Fatalf ("first copy post-commit balance mismatch: have %v, want %v" , balance , 42 )
499
+ }
500
+ if code := copyOne .GetCode (addr ); ! bytes .Equal (code , []byte ("hello" )) {
501
+ t .Fatalf ("first copy post-commit code mismatch: have %x, want %x" , code , []byte ("hello" ))
502
+ }
503
+ if val := copyOne .GetState (addr , skey ); val != sval {
504
+ t .Fatalf ("first copy post-commit non-committed storage slot mismatch: have %x, want %x" , val , sval )
505
+ }
506
+ if val := copyOne .GetCommittedState (addr , skey ); val != sval {
507
+ t .Fatalf ("first copy post-commit committed storage slot mismatch: have %x, want %x" , val , sval )
508
+ }
509
+ // Copy the copy and check the balance once more
510
+ copyTwo := copyOne .Copy ()
511
+ if balance := copyTwo .GetBalance (addr ); balance .Cmp (big .NewInt (42 )) != 0 {
512
+ t .Fatalf ("second copy balance mismatch: have %v, want %v" , balance , 42 )
513
+ }
514
+ if code := copyTwo .GetCode (addr ); ! bytes .Equal (code , []byte ("hello" )) {
515
+ t .Fatalf ("second copy code mismatch: have %x, want %x" , code , []byte ("hello" ))
516
+ }
517
+ if val := copyTwo .GetState (addr , skey ); val != sval {
518
+ t .Fatalf ("second copy non-committed storage slot mismatch: have %x, want %x" , val , sval )
519
+ }
520
+ if val := copyTwo .GetCommittedState (addr , skey ); val != sval {
521
+ t .Fatalf ("second copy post-commit committed storage slot mismatch: have %x, want %x" , val , sval )
522
+ }
523
+ }
524
+
525
+ // Tests a regression where committing a copy lost some internal meta information,
526
+ // leading to corrupted subsequent copies.
527
+ //
528
+ // See https://github.com/ethereum/go-ethereum/issues/20106.
529
+ func TestCopyCopyCommitCopy (t * testing.T ) {
530
+ state , _ := New (common.Hash {}, NewDatabase (rawdb .NewMemoryDatabase ()))
531
+
532
+ // Create an account and check if the retrieved balance is correct
533
+ addr := common .HexToAddress ("0xaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe" )
534
+ skey := common .HexToHash ("aaa" )
535
+ sval := common .HexToHash ("bbb" )
536
+
537
+ state .SetBalance (addr , big .NewInt (42 )) // Change the account trie
538
+ state .SetCode (addr , []byte ("hello" )) // Change an external metadata
539
+ state .SetState (addr , skey , sval ) // Change the storage trie
540
+
541
+ if balance := state .GetBalance (addr ); balance .Cmp (big .NewInt (42 )) != 0 {
542
+ t .Fatalf ("initial balance mismatch: have %v, want %v" , balance , 42 )
543
+ }
544
+ if code := state .GetCode (addr ); ! bytes .Equal (code , []byte ("hello" )) {
545
+ t .Fatalf ("initial code mismatch: have %x, want %x" , code , []byte ("hello" ))
546
+ }
547
+ if val := state .GetState (addr , skey ); val != sval {
548
+ t .Fatalf ("initial non-committed storage slot mismatch: have %x, want %x" , val , sval )
549
+ }
550
+ if val := state .GetCommittedState (addr , skey ); val != (common.Hash {}) {
551
+ t .Fatalf ("initial committed storage slot mismatch: have %x, want %x" , val , common.Hash {})
552
+ }
553
+ // Copy the non-committed state database and check pre/post commit balance
554
+ copyOne := state .Copy ()
555
+ if balance := copyOne .GetBalance (addr ); balance .Cmp (big .NewInt (42 )) != 0 {
556
+ t .Fatalf ("first copy balance mismatch: have %v, want %v" , balance , 42 )
557
+ }
558
+ if code := copyOne .GetCode (addr ); ! bytes .Equal (code , []byte ("hello" )) {
559
+ t .Fatalf ("first copy code mismatch: have %x, want %x" , code , []byte ("hello" ))
560
+ }
561
+ if val := copyOne .GetState (addr , skey ); val != sval {
562
+ t .Fatalf ("first copy non-committed storage slot mismatch: have %x, want %x" , val , sval )
563
+ }
564
+ if val := copyOne .GetCommittedState (addr , skey ); val != (common.Hash {}) {
565
+ t .Fatalf ("first copy committed storage slot mismatch: have %x, want %x" , val , common.Hash {})
566
+ }
567
+ // Copy the copy and check the balance once more
568
+ copyTwo := copyOne .Copy ()
569
+ if balance := copyTwo .GetBalance (addr ); balance .Cmp (big .NewInt (42 )) != 0 {
570
+ t .Fatalf ("second copy pre-commit balance mismatch: have %v, want %v" , balance , 42 )
571
+ }
572
+ if code := copyTwo .GetCode (addr ); ! bytes .Equal (code , []byte ("hello" )) {
573
+ t .Fatalf ("second copy pre-commit code mismatch: have %x, want %x" , code , []byte ("hello" ))
574
+ }
575
+ if val := copyTwo .GetState (addr , skey ); val != sval {
576
+ t .Fatalf ("second copy pre-commit non-committed storage slot mismatch: have %x, want %x" , val , sval )
577
+ }
578
+ if val := copyTwo .GetCommittedState (addr , skey ); val != (common.Hash {}) {
579
+ t .Fatalf ("second copy pre-commit committed storage slot mismatch: have %x, want %x" , val , common.Hash {})
580
+ }
581
+ copyTwo .Commit (false )
582
+ if balance := copyTwo .GetBalance (addr ); balance .Cmp (big .NewInt (42 )) != 0 {
583
+ t .Fatalf ("second copy post-commit balance mismatch: have %v, want %v" , balance , 42 )
584
+ }
585
+ if code := copyTwo .GetCode (addr ); ! bytes .Equal (code , []byte ("hello" )) {
586
+ t .Fatalf ("second copy post-commit code mismatch: have %x, want %x" , code , []byte ("hello" ))
587
+ }
588
+ if val := copyTwo .GetState (addr , skey ); val != sval {
589
+ t .Fatalf ("second copy post-commit non-committed storage slot mismatch: have %x, want %x" , val , sval )
590
+ }
591
+ if val := copyTwo .GetCommittedState (addr , skey ); val != sval {
592
+ t .Fatalf ("second copy post-commit committed storage slot mismatch: have %x, want %x" , val , sval )
593
+ }
594
+ // Copy the copy-copy and check the balance once more
595
+ copyThree := copyTwo .Copy ()
596
+ if balance := copyThree .GetBalance (addr ); balance .Cmp (big .NewInt (42 )) != 0 {
597
+ t .Fatalf ("third copy balance mismatch: have %v, want %v" , balance , 42 )
598
+ }
599
+ if code := copyThree .GetCode (addr ); ! bytes .Equal (code , []byte ("hello" )) {
600
+ t .Fatalf ("third copy code mismatch: have %x, want %x" , code , []byte ("hello" ))
601
+ }
602
+ if val := copyThree .GetState (addr , skey ); val != sval {
603
+ t .Fatalf ("third copy non-committed storage slot mismatch: have %x, want %x" , val , sval )
604
+ }
605
+ if val := copyThree .GetCommittedState (addr , skey ); val != sval {
606
+ t .Fatalf ("third copy committed storage slot mismatch: have %x, want %x" , val , sval )
607
+ }
608
+ }
609
+
453
610
// TestDeleteCreateRevert tests a weird state transition corner case that we hit
454
611
// while changing the internals of statedb. The workflow is that a contract is
455
612
// self destructed, then in a followup transaction (but same block) it's created
0 commit comments