mirror of
https://github.com/luxfi/evmgpu.git
synced 2026-10-30 16:57:11 +00:00
177 lines
5.8 KiB
Go
177 lines
5.8 KiB
Go
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package parallel
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/luxfi/geth/common"
|
|
)
|
|
|
|
// TestMvMemoryFullKeyPreventsHashCollision verifies that MvMemory uses the
|
|
// full MemoryLocation as key, not just LocationHash. Two locations that
|
|
// produce the same LocationHash must be stored in separate version chains.
|
|
// This is the fix for Red Team F02 — LocationHash collision vulnerability.
|
|
func TestMvMemoryFullKeyPreventsHashCollision(t *testing.T) {
|
|
// Construct two MemoryLocations that have the same LocationHash.
|
|
// Hash() only uses the last 8 bytes of the address XOR'd with slot,
|
|
// so we craft two (address, slot) pairs that collide.
|
|
//
|
|
// Location A: address with last 8 bytes = 0x0102030405060708
|
|
// slot with last 8 bytes = 0x0000000000000000
|
|
// Location B: address with last 8 bytes = 0x0000000000000000
|
|
// slot with last 8 bytes = 0x0102030405060708
|
|
//
|
|
// Hash(A) = 0x0102030405060708 ^ type_bits ^ 0x0000000000000000
|
|
// Hash(B) = 0x0000000000000000 ^ type_bits ^ 0x0102030405060708
|
|
// These differ because of XOR ordering — so use a simpler approach:
|
|
// same slot bytes, different address prefix (first 12 bytes differ,
|
|
// last 8 bytes identical). Hash() ignores the first 12 bytes.
|
|
|
|
var addrA, addrB common.Address
|
|
// Last 8 bytes identical
|
|
for i := 12; i < 20; i++ {
|
|
addrA[i] = byte(i)
|
|
addrB[i] = byte(i)
|
|
}
|
|
// First 12 bytes differ
|
|
addrA[0] = 0xAA
|
|
addrB[0] = 0xBB
|
|
|
|
locA := MemoryLocation{Address: addrA, Type: LocationStorage, Slot: common.Hash{}}
|
|
locB := MemoryLocation{Address: addrB, Type: LocationStorage, Slot: common.Hash{}}
|
|
|
|
// Verify they produce the same LocationHash
|
|
if locA.Hash() != locB.Hash() {
|
|
t.Fatalf("test setup error: locA.Hash()=%x != locB.Hash()=%x", locA.Hash(), locB.Hash())
|
|
}
|
|
|
|
// Verify they are NOT equal as MemoryLocations
|
|
if locA == locB {
|
|
t.Fatal("test setup error: locA == locB, but they should differ")
|
|
}
|
|
|
|
// Create MvMemory and write different values at each location
|
|
mvMem := NewMvMemory(4)
|
|
|
|
valA := MemoryValue{Type: ValueAbsolute, Storage: common.HexToHash("0xAAAA")}
|
|
valB := MemoryValue{Type: ValueAbsolute, Storage: common.HexToHash("0xBBBB")}
|
|
|
|
mvMem.Write(locA, 0, 0, valA)
|
|
mvMem.Write(locB, 1, 0, valB)
|
|
|
|
// Read locA from tx 1 — should find valA (written by tx 0)
|
|
entryA, foundA := mvMem.Read(locA, 1)
|
|
if !foundA {
|
|
t.Fatal("expected to find locA in MvMemory")
|
|
}
|
|
if entryA.Value.Storage != valA.Storage {
|
|
t.Fatalf("locA read wrong value: got %x, want %x", entryA.Value.Storage, valA.Storage)
|
|
}
|
|
|
|
// Read locB from tx 2 — should find valB (written by tx 1)
|
|
entryB, foundB := mvMem.Read(locB, 2)
|
|
if !foundB {
|
|
t.Fatal("expected to find locB in MvMemory")
|
|
}
|
|
if entryB.Value.Storage != valB.Storage {
|
|
t.Fatalf("locB read wrong value: got %x, want %x", entryB.Value.Storage, valB.Storage)
|
|
}
|
|
|
|
// Read locB from tx 1 — should NOT find anything (tx 1 wrote it, tx 0 did not)
|
|
_, foundB1 := mvMem.Read(locB, 1)
|
|
if foundB1 {
|
|
t.Fatal("locB should not be visible to tx 1 (only tx 1 wrote to locB)")
|
|
}
|
|
}
|
|
|
|
// TestMvMemoryDistinctChains verifies that two locations with different hashes
|
|
// still get distinct chains (basic sanity check).
|
|
func TestMvMemoryDistinctChains(t *testing.T) {
|
|
var addr common.Address
|
|
addr[19] = 0x01
|
|
|
|
locBalance := MemoryLocation{Address: addr, Type: LocationBalance}
|
|
locStorage := MemoryLocation{Address: addr, Type: LocationStorage, Slot: common.HexToHash("0x01")}
|
|
|
|
mvMem := NewMvMemory(4)
|
|
|
|
valBal := MemoryValue{Type: ValueAbsolute, Balance: common.HexToHash("0x1000")}
|
|
valSto := MemoryValue{Type: ValueAbsolute, Storage: common.HexToHash("0x2000")}
|
|
|
|
mvMem.Write(locBalance, 0, 0, valBal)
|
|
mvMem.Write(locStorage, 0, 0, valSto)
|
|
|
|
entryBal, found := mvMem.Read(locBalance, 1)
|
|
if !found {
|
|
t.Fatal("expected to find balance location")
|
|
}
|
|
if entryBal.Value.Balance != valBal.Balance {
|
|
t.Fatalf("balance wrong: got %x, want %x", entryBal.Value.Balance, valBal.Balance)
|
|
}
|
|
|
|
entrySto, found := mvMem.Read(locStorage, 1)
|
|
if !found {
|
|
t.Fatal("expected to find storage location")
|
|
}
|
|
if entrySto.Value.Storage != valSto.Storage {
|
|
t.Fatalf("storage wrong: got %x, want %x", entrySto.Value.Storage, valSto.Storage)
|
|
}
|
|
}
|
|
|
|
// TestMvMemoryValidateReadSetWithFullKey verifies that ValidateReadSet uses
|
|
// the full MemoryLocation for re-validation, not just the hash.
|
|
func TestMvMemoryValidateReadSetWithFullKey(t *testing.T) {
|
|
var addrA, addrB common.Address
|
|
// Same last 8 bytes → same LocationHash
|
|
for i := 12; i < 20; i++ {
|
|
addrA[i] = 0x42
|
|
addrB[i] = 0x42
|
|
}
|
|
addrA[0] = 0x11
|
|
addrB[0] = 0x22
|
|
|
|
locA := MemoryLocation{Address: addrA, Type: LocationStorage, Slot: common.Hash{}}
|
|
locB := MemoryLocation{Address: addrB, Type: LocationStorage, Slot: common.Hash{}}
|
|
|
|
if locA.Hash() != locB.Hash() {
|
|
t.Fatalf("test setup: hashes should match")
|
|
}
|
|
|
|
mvMem := NewMvMemory(4)
|
|
|
|
// Tx 0 writes to locA
|
|
valA := MemoryValue{Type: ValueAbsolute, Storage: common.HexToHash("0xAA")}
|
|
mvMem.Write(locA, 0, 0, valA)
|
|
|
|
// Tx 1 reads locA from MvMemory and locB from storage
|
|
mvMem.RecordReadSet(1, []ReadEntry{
|
|
{
|
|
Location: locA,
|
|
Origin: ReadOrigin{
|
|
FromMvMemory: true,
|
|
Version: TxVersion{TxIdx: 0, Incarnation: 0},
|
|
},
|
|
},
|
|
{
|
|
Location: locB,
|
|
Origin: ReadOrigin{FromMvMemory: false},
|
|
},
|
|
})
|
|
|
|
// Validate tx 1 — should pass (locA still written by tx 0, locB still not in MvMemory)
|
|
if !mvMem.ValidateReadSet(1) {
|
|
t.Fatal("expected read set to be valid")
|
|
}
|
|
|
|
// Now tx 0 writes to locB as well
|
|
valB := MemoryValue{Type: ValueAbsolute, Storage: common.HexToHash("0xBB")}
|
|
mvMem.Write(locB, 0, 0, valB)
|
|
|
|
// Validate tx 1 again — should now FAIL because locB appeared in MvMemory
|
|
if mvMem.ValidateReadSet(1) {
|
|
t.Fatal("expected read set to be INVALID after locB was written")
|
|
}
|
|
}
|