diff --git a/core/types/bal/bal_encoding.go b/core/types/bal/bal_encoding.go index 1b1406ea32..f543b7f71e 100644 --- a/core/types/bal/bal_encoding.go +++ b/core/types/bal/bal_encoding.go @@ -159,6 +159,19 @@ func (e *AccountAccess) validate() error { return errors.New("storage read slots not in lexicographic order") } + // EIP-7928: a slot must not appear in both storage_changes and storage_reads. + if len(e.StorageWrites) > 0 && len(e.StorageReads) > 0 { + changeSlots := make(map[common.Hash]struct{}, len(e.StorageWrites)) + for _, sc := range e.StorageWrites { + changeSlots[sc.Slot] = struct{}{} + } + for _, key := range e.StorageReads { + if _, exists := changeSlots[key]; exists { + return fmt.Errorf("storage key %s in both changes and reads", key) + } + } + } + // Check the balance changes are sorted in order if !slices.IsSortedFunc(e.BalanceChanges, func(a, b encodingBalanceChange) int { return cmp.Compare[uint16](a.TxIdx, b.TxIdx) diff --git a/core/types/bal/bal_test.go b/core/types/bal/bal_test.go index 52c0de825e..153d8ac969 100644 --- a/core/types/bal/bal_test.go +++ b/core/types/bal/bal_test.go @@ -252,3 +252,26 @@ func TestBlockAccessListValidation(t *testing.T) { t.Fatalf("Unexpected validation error: %v", err) } } + +func TestBlockAccessListSlotUniqueness(t *testing.T) { + var addr common.Address + addr[19] = 0x01 + slot := common.HexToHash("0x01") + + ac := AccountAccess{ + Address: addr, + StorageWrites: []encodingSlotWrites{ + { + Slot: slot, + Accesses: []encodingStorageWrite{ + {TxIdx: 0, ValueAfter: [32]byte{1}}, + }, + }, + }, + StorageReads: [][32]byte{slot}, + } + bal := BlockAccessList{[]AccountAccess{ac}} + if err := bal.Validate(); err == nil { + t.Fatal("expected error for slot in both changes and reads") + } +}