From 48290a95bd117a154a15c0b46d0ead203326a3b0 Mon Sep 17 00:00:00 2001 From: VeerChaurasia Date: Tue, 29 Oct 2024 23:01:21 +0530 Subject: [PATCH 01/13] Add Test for EVM --- execution/evm/account_test.go | 444 +++++++++++++++++++++++++++++++++ execution/evm/common_test.go | 82 ++++++ execution/evm/database_test.go | 178 +++++++++++++ execution/evm/frame_test.go | 382 ++++++++++++++++++++++++++++ 4 files changed, 1086 insertions(+) create mode 100644 execution/evm/account_test.go create mode 100644 execution/evm/common_test.go create mode 100644 execution/evm/database_test.go create mode 100644 execution/evm/frame_test.go diff --git a/execution/evm/account_test.go b/execution/evm/account_test.go new file mode 100644 index 0000000..6cdc21f --- /dev/null +++ b/execution/evm/account_test.go @@ -0,0 +1,444 @@ +package evm + +import ( + "encoding/json" + "math/big" + "reflect" + "testing" + +) + +// Test Account struct +func TestAccount(t *testing.T) { + t.Run("zero value initialization", func(t *testing.T) { + account := Account{} + + if !reflect.DeepEqual(account.Info, AccountInfo{}) { + t.Errorf("Expected empty AccountInfo, got %v", account.Info) + } + + if account.Storage != nil { + t.Errorf("Expected nil Storage, got %v", account.Storage) + } + + if account.Status != 0 { + t.Errorf("Expected Status 0, got %v", account.Status) + } + }) + + t.Run("initialization with values", func(t *testing.T) { + code := Bytecode{ + Kind: LegacyRawKind, + LegacyRaw: []byte{1, 2, 3}, + } + + info := AccountInfo{ + Balance: big.NewInt(100), + Nonce: 1, + CodeHash: B256{1, 2, 3}, + Code: &code, + } + + storage := make(EvmStorage) + storage[big.NewInt(1)] = EvmStorageSlot{ + OriginalValue: big.NewInt(10), + PresentValue: big.NewInt(20), + IsCold: true, + } + + account := Account{ + Info: info, + Storage: storage, + Status: Loaded, + } + + if !reflect.DeepEqual(account.Info, info) { + t.Errorf("Expected Info %v, got %v", info, account.Info) + } + + if !reflect.DeepEqual(account.Storage, storage) { + t.Errorf("Expected Storage %v, got %v", storage, account.Storage) + } + + if account.Status != Loaded { + t.Errorf("Expected Status Loaded, got %v", account.Status) + } + }) +} + +// Test AccountInfo struct and its constructor +func TestAccountInfo(t *testing.T) { + t.Run("NewAccountInfo constructor", func(t *testing.T) { + balance := big.NewInt(100) + nonce := uint64(1) + codeHash := B256{1, 2, 3} + code := Bytecode{ + Kind: LegacyRawKind, + LegacyRaw: []byte{1, 2, 3}, + } + + info := NewAccountInfo(balance, nonce, codeHash, code) + + if info.Balance.Cmp(balance) != 0 { + t.Errorf("Expected Balance %v, got %v", balance, info.Balance) + } + if info.Nonce != nonce { + t.Errorf("Expected Nonce %v, got %v", nonce, info.Nonce) + } + if !reflect.DeepEqual(info.CodeHash, codeHash) { + t.Errorf("Expected CodeHash %v, got %v", codeHash, info.CodeHash) + } + if !reflect.DeepEqual(*info.Code, code) { + t.Errorf("Expected Code %v, got %v", code, *info.Code) + } + }) +} + +// Test EvmStorage +func TestEvmStorage(t *testing.T) { + t.Run("storage operations", func(t *testing.T) { + storage := make(EvmStorage) + + // Test setting and getting values + key := big.NewInt(1) + slot := EvmStorageSlot{ + OriginalValue: big.NewInt(10), + PresentValue: big.NewInt(20), + IsCold: true, + } + + storage[key] = slot + + retrieved, exists := storage[key] + if !exists { + t.Error("Expected storage slot to exist") + } + + if !reflect.DeepEqual(retrieved, slot) { + t.Errorf("Expected slot %v, got %v", slot, retrieved) + } + }) +} + +// Test AccountStatus constants and operations +func TestAccountStatus(t *testing.T) { + tests := []struct { + name string + status AccountStatus + value uint8 + }{ + {"Loaded", Loaded, 0b00000000}, + {"Created", Created, 0b00000001}, + {"SelfDestructed", SelfDestructed, 0b00000010}, + {"Touched", Touched, 0b00000100}, + {"LoadedAsNotExisting", LoadedAsNotExisting, 0b0001000}, + {"Cold", Cold, 0b0010000}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if uint8(tt.status) != tt.value { + t.Errorf("%s: expected %b, got %b", tt.name, tt.value, uint8(tt.status)) + } + }) + } +} + +// Test Bytecode +func TestBytecode(t *testing.T) { + t.Run("legacy raw bytecode", func(t *testing.T) { + code := Bytecode{ + Kind: LegacyRawKind, + LegacyRaw: []byte{1, 2, 3}, + } + + if code.Kind != LegacyRawKind { + t.Errorf("Expected Kind LegacyRawKind, got %v", code.Kind) + } + if !reflect.DeepEqual(code.LegacyRaw, []byte{1, 2, 3}) { + t.Errorf("Expected LegacyRaw [1 2 3], got %v", code.LegacyRaw) + } + }) + + t.Run("legacy analyzed bytecode", func(t *testing.T) { + analyzed := &LegacyAnalyzedBytecode{ + Bytecode: []byte{1, 2, 3}, + OriginalLen: 3, + JumpTable: JumpTable{BitVector: &Bitvector{bits: []uint8{1}, size: 8}}, + } + + code := Bytecode{ + Kind: LegacyAnalyzedKind, + LegacyAnalyzed: analyzed, + } + + if code.Kind != LegacyAnalyzedKind { + t.Errorf("Expected Kind LegacyAnalyzedKind, got %v", code.Kind) + } + if !reflect.DeepEqual(code.LegacyAnalyzed, analyzed) { + t.Errorf("Expected LegacyAnalyzed %v, got %v", analyzed, code.LegacyAnalyzed) + } + }) +} + +// Test EOF related structs +func TestEof(t *testing.T) { + t.Run("eof header", func(t *testing.T) { + header := EofHeader{ + TypesSize: 2, + CodeSizes: []uint16{100, 200}, + ContainerSizes: []uint16{300, 400}, + DataSize: 500, + SumCodeSizes: 300, + SumContainerSizes: 700, + } + + // Test JSON marshaling/unmarshaling + data, err := json.Marshal(header) + if err != nil { + t.Fatalf("Failed to marshal EofHeader: %v", err) + } + + var decoded EofHeader + err = json.Unmarshal(data, &decoded) + if err != nil { + t.Fatalf("Failed to unmarshal EofHeader: %v", err) + } + + if !reflect.DeepEqual(header, decoded) { + t.Errorf("Expected header %+v, got %+v", header, decoded) + } + }) + + t.Run("eof body", func(t *testing.T) { + body := EofBody{ + TypesSection: []TypesSection{{ + Inputs: 1, + Outputs: 2, + MaxStackSize: 1024, + }}, + CodeSection: []Bytes{{1, 2, 3}}, + ContainerSection: []Bytes{{4, 5, 6}}, + DataSection: Bytes{7, 8, 9}, + IsDataFilled: true, + } + + // Test JSON marshaling/unmarshaling + data, err := json.Marshal(body) + if err != nil { + t.Fatalf("Failed to marshal EofBody: %v", err) + } + + var decoded EofBody + err = json.Unmarshal(data, &decoded) + if err != nil { + t.Fatalf("Failed to unmarshal EofBody: %v", err) + } + + if !reflect.DeepEqual(body, decoded) { + t.Errorf("Expected body %+v, got %+v", body, decoded) + } + }) +} +func TestBitvector(t *testing.T) { + t.Run("bitvector initialization", func(t *testing.T) { + bv := Bitvector{ + bits: []uint8{0b10101010}, + size: 8, + } + + if len(bv.bits) != 1 { + t.Errorf("Expected bits length 1, got %d", len(bv.bits)) + } + if bv.size != 8 { + t.Errorf("Expected size 8, got %d", bv.size) + } + }) +} + +// Test JumpTable +func TestJumpTable(t *testing.T) { + t.Run("jumptable initialization", func(t *testing.T) { + jt := JumpTable{ + BitVector: &Bitvector{ + bits: []uint8{0b11110000}, + size: 8, + }, + } + + if jt.BitVector == nil { + t.Error("Expected non-nil BitVector") + } + }) + + t.Run("jumptable sync.Once", func(t *testing.T) { + jt := JumpTable{} + initialized := false + + // Test lazy initialization + jt.once.Do(func() { + initialized = true + }) + + if !initialized { + t.Error("sync.Once did not execute initialization") + } + }) +} + + +func TestOpcode(t *testing.T) { + t.Run("opcode structure", func(t *testing.T) { + createdAddress := Address{Addr: [20]byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}} + opcode := Opcode{ + InitCode: Eof{Header: EofHeader{ + TypesSize: 1, + CodeSizes: []uint16{100}, + }, Body: EofBody{ + TypesSection: []TypesSection{{ + Inputs: 1, + Outputs: 1, + MaxStackSize: 100, + }}, + }, Raw: Bytes{1, 2, 3}}, + Input: Bytes{4, 5, 6}, + CreatedAddress: createdAddress, + } + + // Tests for opcode structure + if len(opcode.Input) != 3 { + t.Errorf("Expected Input length 3, got %d", len(opcode.Input)) + } + + if reflect.DeepEqual(opcode.InitCode, Eof{}) { + t.Error("Expected non-empty InitCode") + } + + if !reflect.DeepEqual(opcode.CreatedAddress, createdAddress) { + t.Errorf("Expected CreatedAddress %v, got %v", createdAddress, opcode.CreatedAddress) + } + }) +} + + + +// Test TypesSection +func TestTypesSection(t *testing.T) { + t.Run("types section values", func(t *testing.T) { + ts := TypesSection{ + Inputs: 2, + Outputs: 3, + MaxStackSize: 1024, + } + + if ts.Inputs != 2 { + t.Errorf("Expected Inputs 2, got %d", ts.Inputs) + } + if ts.Outputs != 3 { + t.Errorf("Expected Outputs 3, got %d", ts.Outputs) + } + if ts.MaxStackSize != 1024 { + t.Errorf("Expected MaxStackSize 1024, got %d", ts.MaxStackSize) + } + }) +} + +//Not done- Test EOFCreateKind JSON marshaling/unmarshaling + +func TestEvmStorageSlot(t *testing.T) { + t.Run("storage slot values", func(t *testing.T) { + slot := EvmStorageSlot{ + OriginalValue: big.NewInt(100), + PresentValue: big.NewInt(200), + IsCold: true, + } + + if slot.OriginalValue.Cmp(big.NewInt(100)) != 0 { + t.Errorf("Expected OriginalValue 100, got %v", slot.OriginalValue) + } + if slot.PresentValue.Cmp(big.NewInt(200)) != 0 { + t.Errorf("Expected PresentValue 200, got %v", slot.PresentValue) + } + if !slot.IsCold { + t.Error("Expected IsCold to be true") + } + }) + + t.Run("storage slot modifications", func(t *testing.T) { + slot := EvmStorageSlot{ + OriginalValue: big.NewInt(100), + PresentValue: big.NewInt(100), + IsCold: true, + } + + // Modify present value + slot.PresentValue = big.NewInt(150) + + if slot.PresentValue.Cmp(big.NewInt(150)) != 0 { + t.Error("Failed to modify PresentValue") + } + if slot.OriginalValue.Cmp(big.NewInt(100)) != 0 { + t.Error("OriginalValue should not change") + } + }) +} + +// Integration test for complex scenarios +func TestComplexScenarios(t *testing.T) { + t.Run("account with storage modifications", func(t *testing.T) { + // Create initial account state + account := Account{ + Info: NewAccountInfo( + big.NewInt(1000), + 1, + B256{1}, + Bytecode{Kind: LegacyRawKind, LegacyRaw: []byte{1, 2, 3}}, + ), + Storage: make(EvmStorage), + Status: Created, + } + + // Add some storage + key := big.NewInt(1) + account.Storage[key] = EvmStorageSlot{ + OriginalValue: big.NewInt(100), + PresentValue: big.NewInt(100), + IsCold: true, + } + + // Modify storage + slot := account.Storage[key] + slot.PresentValue = big.NewInt(200) + slot.IsCold = false + account.Storage[key] = slot + + // Verify all changes + if account.Storage[key].PresentValue.Cmp(big.NewInt(200)) != 0 { + t.Error("Storage modification failed") + } + if account.Storage[key].OriginalValue.Cmp(big.NewInt(100)) != 0 { + t.Error("Original value should not change") + } + if account.Storage[key].IsCold { + t.Error("IsCold should be false") + } + }) + + t.Run("account status transitions", func(t *testing.T) { + account := Account{Status: Created} + + // Test status transitions + transitions := []AccountStatus{ + Touched, + SelfDestructed, + Cold, + } + + for _, newStatus := range transitions { + account.Status = newStatus + if account.Status != newStatus { + t.Errorf("Failed to transition to status %v", newStatus) + } + } + }) +} \ No newline at end of file diff --git a/execution/evm/common_test.go b/execution/evm/common_test.go new file mode 100644 index 0000000..afcc8a5 --- /dev/null +++ b/execution/evm/common_test.go @@ -0,0 +1,82 @@ +package evm + +import ( + "math/big" + "testing" + + // "github.com/stretchr/testify" + Common "github.com/ethereum/go-ethereum/common" + "github.com/magiconair/properties/assert" +) + +func TestU256Initialization(t *testing.T) { + var zero U256 = big.NewInt(0) + if zero.Cmp(big.NewInt(0)) != 0 { + t.Errorf("Expected zero U256, got %v", zero) + } + var positive U256 = big.NewInt(17) + if positive.Cmp(big.NewInt(17)) != 0 { + t.Errorf("Expected U256 to be 17, got %v", positive) + } + largeNum := new(big.Int) + largeNum.SetString("123456789012345678901234567890", 10) + var uLarge U256 = largeNum + if uLarge.Cmp(largeNum) != 0 { + t.Errorf("Expected U256 to be large number %v, got %v", largeNum, uLarge) + } + if positive.Cmp(largeNum) == 0 { + t.Errorf("Expected U256 %v to be different from large number %v", positive, largeNum) + } + +} +func TestU256Equality(t *testing.T) { + var a U256 = big.NewInt(17) + var b U256 = big.NewInt(17) + var c U256 = big.NewInt(100) + if a.Cmp(b) != 0 { + t.Errorf("Expected U256 instances to be equal, got a: %v, b: %v", a, b) + } + if a.Cmp(c) == 0 { + t.Errorf("Expected U256 instances to be different, got a: %v, c: %v", a, c) + } +} +func TestB256Initialization(t *testing.T) { + var hash B256 = Common.BytesToHash([]byte("selene")) + expectedHash := Common.BytesToHash([]byte("selene")) + assert.Equal(t, expectedHash, hash, "Expected hash and expectedHash to be same") + +} +func TestAddressInitialization(t *testing.T) { + // Test Address initialization + addr := Common.HexToAddress("0x0000000000000000000000000000000000000001") + if addr.String() != "0x0000000000000000000000000000000000000001" { + t.Errorf("Expected Address to be 0x0000000000000000000000000000000000000001, got %v", addr) + } + + // Test Address validity with a valid input + validAddr := Common.HexToAddress("0x0000000000000000000000000000000000000001") + if validAddr == (Common.Address{}) { + t.Errorf("Expected a valid Address, got zero address") + } + + // Test Address validity with an invalid input + invalidAddr := Common.HexToAddress("invalid-address") + if invalidAddr != (Common.Address{}) { + t.Errorf("Expected zero Address for invalid input, got %v", invalidAddr) + } +} + + +func TestBytesInitialization(t *testing.T) { + // Test Bytes initialization + data := []byte{1, 2, 3, 4} + var byteData Bytes = data + + if len(byteData) != 4 { + t.Errorf("Expected Bytes length to be 4, got %v", len(byteData)) + } + + if byteData[0] != 1 { + t.Errorf("Expected first byte to be 1, got %v", byteData[0]) + } +} \ No newline at end of file diff --git a/execution/evm/database_test.go b/execution/evm/database_test.go new file mode 100644 index 0000000..514aaba --- /dev/null +++ b/execution/evm/database_test.go @@ -0,0 +1,178 @@ +package evm + +import ( + "errors" + "math/big" + "reflect" + "testing" +) + +func TestNewEmptyDB(t *testing.T) { + db := NewEmptyDB() + if db == nil { + t.Error("NewEmptyDB() returned nil") + } +} + +func TestEmptyDBBasic(t *testing.T) { + db := NewEmptyDB() + + testCases := []struct { + name string + address Address + wantErr string + }{ + { + name: "basic query", + address: Address{}, // zero address + wantErr: "EmptyDB: Basic not implemented", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + info, err := db.Basic(tc.address) + + // Check if error matches expected + if err == nil || err.Error() != tc.wantErr { + t.Errorf("Basic() error = %v, want %v", err, tc.wantErr) + } + + // Verify empty AccountInfo is returned + if !reflect.DeepEqual(info, AccountInfo{}) { + t.Errorf("Basic() info = %v, want empty AccountInfo", info) + } + }) + } +} + +func TestEmptyDBBlockHash(t *testing.T) { + db := NewEmptyDB() + + testCases := []struct { + name string + number uint64 + wantErr string + }{ + { + name: "block hash query", + number: 1, + wantErr: "EmptyDB: BlockHash not implemented", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + hash, err := db.BlockHash(tc.number) + + // Check if error matches expected + if err == nil || err.Error() != tc.wantErr { + t.Errorf("BlockHash() error = %v, want %v", err, tc.wantErr) + } + + // Verify empty B256 is returned + if !reflect.DeepEqual(hash, B256{}) { + t.Errorf("BlockHash() hash = %v, want empty B256", hash) + } + }) + } +} + +func TestEmptyDBStorage(t *testing.T) { + db := NewEmptyDB() + + testCases := []struct { + name string + address Address + index U256 + wantErr string + }{ + { + name: "storage query", + address: Address{}, + index: big.NewInt(0), + wantErr: "EmptyDB: Storage not implemented", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + value, err := db.Storage(tc.address, tc.index) + + // Check if error matches expected + if err == nil || err.Error() != tc.wantErr { + t.Errorf("Storage() error = %v, want %v", err, tc.wantErr) + } + + // Verify zero value is returned + if value.Cmp(big.NewInt(0)) != 0 { + t.Errorf("Storage() value = %v, want 0", value) + } + }) + } +} + +func TestEmptyDBCodeByHash(t *testing.T) { + db := NewEmptyDB() + + testCases := []struct { + name string + codeHash B256 + wantErr string + }{ + { + name: "code query", + codeHash: B256{}, + wantErr: "EmptyDB: CodeByHash not implemented", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + code, err := db.CodeByHash(tc.codeHash) + + // Check if error matches expected + if err == nil || err.Error() != tc.wantErr { + t.Errorf("CodeByHash() error = %v, want %v", err, tc.wantErr) + } + + // Verify empty Bytecode is returned + if !reflect.DeepEqual(code, Bytecode{}) { + t.Errorf("CodeByHash() code = %v, want empty Bytecode", code) + } + }) + } +} + +func TestDatabaseError(t *testing.T) { + testCases := []struct { + name string + err *DatabaseError + wantErr string + }{ + { + name: "with underlying error", + err: &DatabaseError{ + msg: "test message", + err: errors.New("underlying error"), + }, + wantErr: "underlying error", + }, + { + name: "without underlying error", + err: &DatabaseError{ + msg: "test message", + err: nil, + }, + wantErr: "test message", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + if err := tc.err.Error(); err != tc.wantErr { + t.Errorf("DatabaseError.Error() = %v, want %v", err, tc.wantErr) + } + }) + } +} \ No newline at end of file diff --git a/execution/evm/frame_test.go b/execution/evm/frame_test.go new file mode 100644 index 0000000..bd863ad --- /dev/null +++ b/execution/evm/frame_test.go @@ -0,0 +1,382 @@ +package evm + +import ( + "math/big" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestCallFrame(t *testing.T) { + // Arrange + expectedRange := Range{Start: 100, End: 200} + expectedFrameData := FrameData{ + Checkpoint: JournalCheckpoint{ + Log_i: 10, + Journal_i: 20, + }, + Interpreter: Interpreter{ + InstructionPointer: func(b byte) *byte { return &b }(0x10), + Gas: Gas{ + Limit: 100, + Remaining: 50, + Refunded: 50, + }, + IsEof: true, + IsEofInit: true, + SharedMemory: SharedMemory{ + Buffer: []byte{0x1, 0x2, 0x3, 0x4, 0x5}, + Checkpoints: []int{0, 2, 4}, + LastCheckpoint: 4, + MemoryLimit: 1024, + }, + Stack: Stack{ + Data: []*big.Int{big.NewInt(15)}, + }, + IsStatic: true, + Contract: Contract{ + Input: []byte{0xde, 0xad, 0xbe, 0xef}, + Bytecode: Bytecode{}, + Hash: &B256{0xaa, 0xbb, 0xcc, 0xdd, 0xee}, + TargetAddress: Address{Addr: [20]byte{0x1a, 0x2b, 0x3c, 0x4d, 0x5e}}, + Caller: Address{Addr: [20]byte{0x1b, 0x3d, 0x5f, 0x7a, 0x9c}}, + CallValue: U256(big.NewInt(1000)), + }, + + }, + } + + // Act + frame := CallFrame{ + ReturnMemoryRange: expectedRange, + FrameData: expectedFrameData, + } + + assert.Equal(t, expectedRange, frame.ReturnMemoryRange) + assert.Equal(t, expectedFrameData, frame.FrameData) +} + + +func TestCreateFrame(t *testing.T) { + // Arrange + expectedAddress := Address{Addr: [20]byte{0x1a, 0x2b, 0x3c, 0x4d, 0x5e}} + expectedFrameData := FrameData{ + Checkpoint: JournalCheckpoint{ + Log_i: 10, + Journal_i: 20, + }, // Initialize with your checkpoint data + Interpreter: Interpreter{ + InstructionPointer: func(b byte) *byte { return &b }(0x10), + Gas: Gas{ + Limit: 100, + Remaining: 50, + Refunded: 50, + }, + IsEof: true, + IsEofInit: true, + SharedMemory: SharedMemory{ + Buffer: []byte{0x1, 0x2, 0x3, 0x4, 0x5}, + Checkpoints: []int{0, 2, 4}, + LastCheckpoint: 4, + MemoryLimit: 1024, + }, + Stack: Stack{ + Data: []*big.Int{big.NewInt(15)}, + }, + IsStatic: true, + Contract: Contract{ + Input: []byte{0xde, 0xad, 0xbe, 0xef}, + Bytecode: Bytecode{}, + Hash: &B256{0xaa, 0xbb, 0xcc, 0xdd, 0xee}, + TargetAddress: Address{Addr: [20]byte{0x1a, 0x2b, 0x3c, 0x4d, 0x5e}}, + Caller: Address{Addr: [20]byte{0x1b, 0x3d, 0x5f, 0x7a, 0x9c}}, + CallValue: U256(big.NewInt(1000)), + }, + + }, + } + + // Act + frame := CreateFrame{ + CreatedAddress: expectedAddress, + FrameData: expectedFrameData, + } + + // Assert + assert.Equal(t, expectedAddress, frame.CreatedAddress) + assert.Equal(t, expectedFrameData, frame.FrameData) +} +func TestEOFCreatedFrame(t *testing.T){ + expectedAddress := Address{Addr: [20]byte{0x1a, 0x2b, 0x3c, 0x4d, 0x5e}} + expectedFrameData := FrameData{ + Checkpoint: JournalCheckpoint{ + Log_i: 10, + Journal_i: 20, + }, // Initialize with your checkpoint data + Interpreter: Interpreter{ + InstructionPointer: func(b byte) *byte { return &b }(0x10), + Gas: Gas{ + Limit: 100, + Remaining: 50, + Refunded: 50, + }, + IsEof: true, + IsEofInit: true, + SharedMemory: SharedMemory{ + Buffer: []byte{0x1, 0x2, 0x3, 0x4, 0x5}, + Checkpoints: []int{0, 2, 4}, + LastCheckpoint: 4, + MemoryLimit: 1024, + }, + Stack: Stack{ + Data: []*big.Int{big.NewInt(15)}, + }, + IsStatic: true, + Contract: Contract{ + Input: []byte{0xde, 0xad, 0xbe, 0xef}, + Bytecode: Bytecode{}, + Hash: &B256{0xaa, 0xbb, 0xcc, 0xdd, 0xee}, + TargetAddress: Address{Addr: [20]byte{0x1a, 0x2b, 0x3c, 0x4d, 0x5e}}, + Caller: Address{Addr: [20]byte{0x1b, 0x3d, 0x5f, 0x7a, 0x9c}}, + CallValue: U256(big.NewInt(1000)), + }, + + }, + } + + // Act + frame := EOFCreateFrame{ + CreatedAddress: expectedAddress, + FrameData: expectedFrameData, + } + + // Assert + assert.Equal(t, expectedAddress, frame.CreatedAddress) + assert.Equal(t, expectedFrameData, frame.FrameData) + +} +func TestFrameData(t *testing.T) { + expectedCheckpoint := JournalCheckpoint{ + Log_i: 10, + Journal_i: 50, + } + expectedInterpreter := Interpreter{ + InstructionPointer: func(b byte) *byte { return &b }(0x10), + Gas: Gas{ + Limit: 100, + Remaining: 50, + Refunded: 50, + }, + IsEof: true, + IsEofInit: true, + SharedMemory: SharedMemory{ + Buffer: []byte{0x1, 0x2, 0x3, 0x4, 0x5}, + Checkpoints: []int{0, 2, 4}, + LastCheckpoint: 4, + MemoryLimit: 1024, + }, + Stack: Stack{ + Data: []*big.Int{big.NewInt(15)}, + }, + IsStatic: true, + Contract: Contract{ + Input: []byte{0xde, 0xad, 0xbe, 0xef}, + Bytecode: Bytecode{}, + Hash: &B256{0xaa, 0xbb, 0xcc, 0xdd, 0xee}, + TargetAddress: Address{Addr: [20]byte{0x1a, 0x2b, 0x3c, 0x4d, 0x5e}}, + Caller: Address{Addr: [20]byte{0x1b, 0x3d, 0x5f, 0x7a, 0x9c}}, + CallValue: U256(big.NewInt(1000)), + }, + } + + // Act + frame := FrameData{ + Checkpoint: expectedCheckpoint, + Interpreter: expectedInterpreter, + } + + // Assert + assert.Equal(t, expectedCheckpoint, frame.Checkpoint) + assert.Equal(t, expectedInterpreter, frame.Interpreter) +} + +func TestCallOutcome(t *testing.T){ + expectedResult :=InterpreterResult{ + Result: 8, + Output: []byte{0xde, 0xad, 0xbe, 0xef}, + Gas: Gas{ + Limit: 100, + Remaining: 50, + Refunded: 50, + }, + + } + expectedMemoryOffset := Range{Start: 100, End: 200} + + frame := CallOutcome{ + Result: expectedResult, + MemoryOffset: expectedMemoryOffset, + } + + // Use the frame variable to avoid the "declared and not used" error + assert.Equal(t, expectedResult, frame.Result) + assert.Equal(t, expectedMemoryOffset, frame.MemoryOffset) +} +func TestCreateOutcome(t *testing.T){ + expectedResult:=InterpreterResult{ + Result: 8, + Output: []byte{0xde, 0xad, 0xbe, 0xef}, + Gas: Gas{ + Limit: 100, + Remaining: 50, + Refunded: 50, + }, + + } + expectedAddress:=Address{Addr: [20]byte{0x1a, 0x2b, 0x3c, 0x4d, 0x5e}} + + frame := CreateOutcome{ + Result: expectedResult, + Address: &expectedAddress, + } + + // Use the frame variable to avoid the "declared and not used" error + assert.Equal(t, expectedResult, frame.Result) + assert.Equal(t, &expectedAddress, frame.Address) + +} +func TestFrame(t *testing.T) { + callFrame := &CallFrame{ + ReturnMemoryRange: Range{Start: 100, End: 200}, + FrameData: FrameData{ + Checkpoint: JournalCheckpoint{ + Log_i: 10, + Journal_i: 20, + }, + Interpreter: Interpreter{ + InstructionPointer: func(b byte) *byte { return &b }(0x10), + Gas: Gas{ + Limit: 100, + Remaining: 50, + Refunded: 50, + }, + IsEof: true, + IsEofInit: true, + SharedMemory: SharedMemory{ + Buffer: []byte{0x1, 0x2, 0x3, 0x4, 0x5}, + Checkpoints: []int{0, 2, 4}, + LastCheckpoint: 4, + MemoryLimit: 1024, + }, + Stack: Stack{ + Data: []*big.Int{big.NewInt(15)}, + }, + IsStatic: true, + Contract: Contract{ + Input: []byte{0xde, 0xad, 0xbe, 0xef}, + Bytecode: Bytecode{}, + Hash: &B256{0xaa, 0xbb, 0xcc, 0xdd, 0xee}, + TargetAddress: Address{Addr: [20]byte{0x1a, 0x2b, 0x3c, 0x4d, 0x5e}}, + Caller: Address{Addr: [20]byte{0x1b, 0x3d, 0x5f, 0x7a, 0x9c}}, + CallValue: U256(big.NewInt(1000)), + }, + }, + }, + } + + frame := Frame{ + Type: Call_Frame, + Call: callFrame, + } + + // Assert + assert.Equal(t, Call_Frame, frame.Type) + assert.Equal(t, callFrame, frame.Call) +} + +func TestFrameResult(t *testing.T) { + // Arrange + expectedResultType := FrameResultType(10) + expectedCall := CallOutcome{ + InterpreterResult{ + Result: 8, + Output: []byte{0xde, 0xad, 0xbe, 0xef}, + Gas: Gas{ + Limit: 100, + Remaining: 50, + Refunded: 50, + }, + }, + Range{Start: 100, End: 200}, + } + expectedCreateOutcome := CreateOutcome{ + Result: InterpreterResult{ + Result: 8, + Output: []byte{0xde, 0xad, 0xbe, 0xef}, + Gas: Gas{ + Limit: 100, + Remaining: 50, + Refunded: 50, + }, + }, + Address: &Address{Addr: [20]byte{0x1a, 0x2b, 0x3c, 0x4d, 0x5e}}, + } + frame := FrameResult{ + ResultType: expectedResultType, + Call: &expectedCall, + Create: &expectedCreateOutcome, + } + + // Use the frame variable to avoid the "declared and not used" error + assert.Equal(t, expectedResultType, frame.ResultType) + assert.Equal(t, &expectedCall, frame.Call) + assert.Equal(t, &expectedCreateOutcome, frame.Create) + +} + + + + +func TestFrameOrResult(t *testing.T) { + callFrame := Frame{ + Type: Call_Frame, + Call: &CallFrame{ + ReturnMemoryRange: Range{Start: 100, End: 200}, + FrameData: FrameData{ + Checkpoint: JournalCheckpoint{ + Log_i: 10, + Journal_i: 20, + }, + Interpreter: Interpreter{ + InstructionPointer: func(b byte) *byte { return &b }(0x10), + Gas: Gas{ + Limit: 100, + Remaining: 50, + Refunded: 50, + }, + IsEof: true, + Stack: Stack{ + Data: []*big.Int{big.NewInt(15)}, + }, + Contract: Contract{ + Input: []byte{0xde, 0xad, 0xbe, 0xef}, + Bytecode: Bytecode{}, + Hash: &B256{0xaa, 0xbb, 0xcc, 0xdd, 0xee}, + TargetAddress: Address{Addr: [20]byte{0x1a, 0x2b, 0x3c, 0x4d, 0x5e}}, + Caller: Address{Addr: [20]byte{0x1b, 0x3d, 0x5f, 0x7a, 0x9c}}, + CallValue: U256(big.NewInt(1000)), + }, + }, + }, + }, + } + + frameOrResult := FrameOrResult{ + Type: Frame_FrameOrResult, + Frame: callFrame, + } + + // Assert + assert.Equal(t, Frame_FrameOrResult, frameOrResult.Type) + assert.Equal(t, callFrame, frameOrResult.Frame) +} From caf9252683276453537cac3821b43645a03bb5ce Mon Sep 17 00:00:00 2001 From: VeerChaurasia Date: Wed, 30 Oct 2024 14:20:50 +0530 Subject: [PATCH 02/13] Other Test for EVM --- execution/evm/env_test.go | 134 +++++++++++++++++++++++ execution/evm/evm_context_test.go | 104 ++++++++++++++++++ execution/evm/inner_evm_context_test.go | 81 ++++++++++++++ execution/evm/interpreter_action_test.go | 90 +++++++++++++++ 4 files changed, 409 insertions(+) create mode 100644 execution/evm/env_test.go create mode 100644 execution/evm/evm_context_test.go create mode 100644 execution/evm/inner_evm_context_test.go create mode 100644 execution/evm/interpreter_action_test.go diff --git a/execution/evm/env_test.go b/execution/evm/env_test.go new file mode 100644 index 0000000..021bf62 --- /dev/null +++ b/execution/evm/env_test.go @@ -0,0 +1,134 @@ +package evm + +import ( + "math/big" + "testing" + "github.com/stretchr/testify/assert" +) + +func TestNewEnv(t *testing.T) { + env := NewEnv() + assert.NotNil(t, env, "NewEnv should not return nil") +} + +func TestNewSignature(t *testing.T) { + r := big.NewInt(12345) + s := big.NewInt(67890) + sig := NewSignature(1, r, s) + + assert.NotNil(t, sig) + assert.Equal(t, uint8(1), sig.V) + assert.Equal(t, r, sig.R) + assert.Equal(t, s, sig.S) +} + +func TestToRawSignature(t *testing.T) { + r := big.NewInt(12345) + s := big.NewInt(67890) + sig := NewSignature(1, r, s) + + rawSig := sig.ToRawSignature() + assert.Equal(t, 64, len(rawSig)) + assert.Equal(t, r.Bytes(), rawSig[32-len(r.Bytes()):32]) + assert.Equal(t, s.Bytes(), rawSig[64-len(s.Bytes()):]) +} + +func TestFromRawSignature(t *testing.T) { + r := big.NewInt(12345) + s := big.NewInt(67890) + sig := NewSignature(1, r, s) + rawSig := sig.ToRawSignature() + + reconstructedSig, err := FromRawSignature(rawSig, sig.V) + assert.NoError(t, err) + assert.Equal(t, sig.V, reconstructedSig.V) + assert.Equal(t, r, reconstructedSig.R) + assert.Equal(t, s, reconstructedSig.S) +} + +func TestCfgEnvStruct(t *testing.T) { + cfg := CfgEnv{ + ChainID: 1234, + DisableBaseFee: true, + MemoryLimit: 2048, + } + assert.Equal(t, uint64(1234), cfg.ChainID) + assert.True(t, cfg.DisableBaseFee) + assert.Equal(t, uint64(2048), cfg.MemoryLimit) +} + +func TestTxKind(t *testing.T) { + txKind := TxKind{Type: Call2} + assert.Equal(t, Call2, txKind.Type) + assert.Nil(t, txKind.Address) +} + +func TestOptionalNonce(t *testing.T) { + var nonceValue uint64 = 42 + nonce := OptionalNonce{Nonce: &nonceValue} + assert.Equal(t, uint64(42), *nonce.Nonce) + + nonce = OptionalNonce{} + assert.Nil(t, nonce.Nonce) +} + +func TestAuthorizationStructs(t *testing.T) { + chainID := ChainID(12345) + auth := Authorization{ + ChainID: chainID, + Address: Address{Addr: [20]byte{0x1a, 0x2b, 0x3c, 0x4d, 0x5e}}, + Nonce: OptionalNonce{}, + } + assert.Equal(t, chainID, auth.ChainID) + assert.Equal(t, Address{Addr: [20]byte{0x1a, 0x2b, 0x3c, 0x4d, 0x5e}}, auth.Address) + assert.Nil(t, auth.Nonce.Nonce) + + signedAuth := SignedAuthorization{Inner: auth} + assert.Equal(t, chainID, signedAuth.Inner.ChainID) + + recoveredAuth := RecoveredAuthorization{Inner: auth} + assert.Equal(t, chainID, recoveredAuth.Inner.ChainID) + assert.Nil(t, recoveredAuth.Authority) +} + +func TestBlobExcessGasAndPrice(t *testing.T) { + blob := BlobExcessGasAndPrice{ + ExcessGas: 1000, + BlobGasPrice: 2000, + } + assert.Equal(t, uint64(1000), blob.ExcessGas) + assert.Equal(t, uint64(2000), blob.BlobGasPrice) +} + +func TestBlockEnvStruct(t *testing.T) { + blockEnv := BlockEnv{ + Number: U256(big.NewInt(1234)), + Timestamp: U256(big.NewInt(5678)), + Coinbase: Address{Addr: [20]byte{0x1a, 0x2b, 0x3c, 0x4d, 0x5e}}, + GasLimit: U256(big.NewInt(1234)), + BaseFee: U256(big.NewInt(1234)), + Difficulty: U256(big.NewInt(1234)), + Prevrandao: &B256{0xaa, 0xbb, 0xcc, 0xdd, 0xee}, + BlobExcessGasAndPrice: &BlobExcessGasAndPrice{ + ExcessGas: 90, + BlobGasPrice: 80, + }, + } + assert.Equal(t, U256(big.NewInt(1234)), blockEnv.Number) + assert.Equal(t, U256(big.NewInt(5678)), blockEnv.Timestamp) + assert.Equal(t, U256(big.NewInt(1234)), blockEnv.GasLimit) + assert.Equal(t, U256(big.NewInt(1234)), blockEnv.BaseFee) + assert.Equal(t, U256(big.NewInt(1234)), blockEnv.Difficulty) + assert.Equal(t, &B256{0xaa, 0xbb, 0xcc, 0xdd, 0xee}, blockEnv.Prevrandao) + assert.Equal(t, &BlobExcessGasAndPrice{ExcessGas: 90, BlobGasPrice: 80}, blockEnv.BlobExcessGasAndPrice) +} + +func TestAccessListItem(t *testing.T) { + accessList := AccessListItem{ + Address: Address{Addr: [20]byte{0x1a, 0x2b, 0x3c, 0x4d, 0x5e}}, + StorageKeys: []B256{{0xaa, 0xbb, 0xcc, 0xdd, 0xee}}, + } + assert.Equal(t, Address{Addr: [20]byte{0x1a, 0x2b, 0x3c, 0x4d, 0x5e}}, accessList.Address) + assert.Equal(t, []B256{{0xaa, 0xbb, 0xcc, 0xdd, 0xee}}, accessList.StorageKeys) +} + diff --git a/execution/evm/evm_context_test.go b/execution/evm/evm_context_test.go new file mode 100644 index 0000000..c638099 --- /dev/null +++ b/execution/evm/evm_context_test.go @@ -0,0 +1,104 @@ +package evm + +import ( + "testing" + "math/big" +) + +// MockDatabase is a mock implementation of the Database interface for testing. +type MockDatabase struct { + BasicFunc func(Address) (AccountInfo, error) + BlockHashFunc func(uint64) (B256, error) + StorageFunc func(Address, U256) (U256, error) + CodeByHashFunc func(B256) (Bytecode, error) +} + +func (mdb *MockDatabase) Basic(address Address) (AccountInfo, error) { + return mdb.BasicFunc(address) +} + +func (mdb *MockDatabase) BlockHash(number uint64) (B256, error) { + return mdb.BlockHashFunc(number) +} + +func (mdb *MockDatabase) Storage(address Address, index U256) (U256, error) { + return mdb.StorageFunc(address, index) +} + +func (mdb *MockDatabase) CodeByHash(codeHash B256) (Bytecode, error) { + return mdb.CodeByHashFunc(codeHash) +} + +// TestNewEvmContext tests the NewEvmContext function. +func TestNewEvmContext(t *testing.T) { + db := &MockDatabase{ + BasicFunc: func(address Address) (AccountInfo, error) { + return AccountInfo{}, nil + }, + BlockHashFunc: func(number uint64) (B256, error) { + return B256{}, nil + }, + StorageFunc: func(address Address, index U256) (U256, error) { + return big.NewInt(0), nil + }, + CodeByHashFunc: func(codeHash B256) (Bytecode, error) { + return Bytecode{}, nil + }, + } + + // Create a new EvmContext + evmCtx := NewEvmContext(db) + + // Check if the Inner and Precompiles are initialized correctly + if evmCtx.Inner.DB != db { + t.Errorf("Expected DB to be the provided mock database, got: %v", evmCtx.Inner.DB) + } + if evmCtx.Precompiles.Inner.Owned == nil { + t.Error("Expected Precompiles Owned to be initialized, got nil") + } +} + +// TestWithNewEvmDB tests the WithNewEvmDB function. +func TestWithNewEvmDB(t *testing.T) { + db1 := &MockDatabase{ + BasicFunc: func(address Address) (AccountInfo, error) { + return AccountInfo{}, nil + }, + } + + db2 := &MockDatabase{ + BasicFunc: func(address Address) (AccountInfo, error) { + return AccountInfo{}, nil + }, + } + + // Create a new EvmContext with the first DB + evmCtx1 := NewEvmContext(db1) + + // Create a new EvmContext with the second DB + evmCtx2 := WithNewEvmDB(evmCtx1, db2) + + // Check if the new EvmContext's DB is the second DB + if evmCtx2.Inner.DB != db2 { + t.Errorf("Expected DB to be the second mock database, got: %v", evmCtx2.Inner.DB) + } +} + +// TestSetPrecompiles tests the SetPrecompiles method. +func TestSetPrecompiles(t *testing.T) { + db := &MockDatabase{ + BasicFunc: func(address Address) (AccountInfo, error) { + return AccountInfo{}, nil + }, + } + + evmCtx := NewEvmContext(db) + + precompiles := DefaultContextPrecompiles[*MockDatabase]() + evmCtx.SetPrecompiles(precompiles) + + if evmCtx.Precompiles.Inner.Owned == nil { + t.Error("Expected Precompiles Owned to be initialized, got nil") + } +} + diff --git a/execution/evm/inner_evm_context_test.go b/execution/evm/inner_evm_context_test.go new file mode 100644 index 0000000..e9dae51 --- /dev/null +++ b/execution/evm/inner_evm_context_test.go @@ -0,0 +1,81 @@ +package evm + +import ( + "testing" + "math/big" + + "github.com/stretchr/testify/assert" + // "github.com/BlocSoc-iitr/selene/common" +) + +// TestNewJournalState checks if NewJournalState initializes JournaledState correctly. +func TestNewJournalState(t *testing.T) { + spec := DefaultSpecId() + warmPreloadedAddresses := map[Address]struct{}{ + {Addr: [20]byte{0x1}}: {}, + } + journalState := NewJournalState(spec, warmPreloadedAddresses) + + assert.Nil(t, journalState.State, "State should be initialized as nil") + assert.Nil(t, journalState.TransientStorage, "TransientStorage should be initialized as nil") + assert.Equal(t, spec, journalState.Spec, "Spec should be set to the provided SpecId") + assert.Equal(t, warmPreloadedAddresses, journalState.WarmPreloadedAddresses, "WarmPreloadedAddresses should be set correctly") + assert.Equal(t, uint(0), journalState.Depth, "Depth should be initialized to 0") + assert.Empty(t, journalState.Journal, "Journal should be initialized empty") + assert.Empty(t, journalState.Logs, "Logs should be initialized empty") +} + +// TestSetSpecId verifies that the setSpecId method correctly updates the SpecId in JournaledState. +func TestSetSpecId(t *testing.T) { + spec := DefaultSpecId() + journalState := NewJournalState(spec, nil) + + newSpec := SpecId(2) + journalState.setSpecId(newSpec) + assert.Equal(t, newSpec, journalState.Spec, "SpecId should be updated to the new SpecId") +} + +// TestInnerEvmContextWithJournalState ensures InnerEvmContext integrates correctly with JournaledState. +func TestInnerEvmContextWithJournalState(t *testing.T) { + db := NewEmptyDB() + ctx := NewInnerEvmContext(db) + + assert.NotNil(t, ctx.JournaledState, "JournaledState should be initialized") + assert.Nil(t, ctx.JournaledState.State, "State in JournaledState should be nil on initialization") + assert.Equal(t, ctx.JournaledState.Spec, DefaultSpecId(), "Spec in JournaledState should match DefaultSpecId") +} + +// TestTransientStorageInitialization ensures TransientStorage is initialized correctly in JournaledState. +func TestTransientStorageInitialization(t *testing.T) { + spec := DefaultSpecId() + journalState := NewJournalState(spec, nil) + + // Initialize a TransientStorage entry + key := Key{ + Account: Address{Addr:[20]byte{0x1}}, + Slot: U256(big.NewInt(0)), + } + value := U256(big.NewInt(12345)) + journalState.TransientStorage = make(TransientStorage) + journalState.TransientStorage[key] = value + + assert.Equal(t, value, journalState.TransientStorage[key], "TransientStorage should store the correct value for the key") +} + +// TestLogInitialization verifies that Logs are initialized and added to JournaledState. +func TestLogInitialization(t *testing.T) { + spec := DefaultSpecId() + journalState := NewJournalState(spec, nil) + + log := Log[LogData]{ + Address: Address{Addr:[20]byte{0x1}}, + Data: LogData{ + Topics: []B256{{0x1, 0x2, 0x3, 0x4}}, + Data: Bytes{0x10, 0x20, 0x30}, + }, + } + journalState.Logs = append(journalState.Logs, log) + + assert.Len(t, journalState.Logs, 1, "Logs should contain one entry") + assert.Equal(t, log, journalState.Logs[0], "Log entry should match the added log") +} diff --git a/execution/evm/interpreter_action_test.go b/execution/evm/interpreter_action_test.go new file mode 100644 index 0000000..2c6a078 --- /dev/null +++ b/execution/evm/interpreter_action_test.go @@ -0,0 +1,90 @@ +package evm + +import ( + // "reflect" + "testing" +) + +// TestNewCallAction verifies that NewCallAction initializes a Call action with the correct inputs. +func TestNewCallAction(t *testing.T) { + callInputs := &CallInputs{} + action := NewCallAction(callInputs) + + if action.actionType != ActionTypeCall { + t.Errorf("Expected action type %v, got %v", ActionTypeCall, action.actionType) + } + if action.callInputs != callInputs { + t.Errorf("Expected callInputs to be %v, got %v", callInputs, action.callInputs) + } +} + +// TestNewCreateAction verifies that NewCreateAction initializes a Create action with the correct inputs. +func TestNewCreateAction(t *testing.T) { + createInputs := &CreateInputs{} + action := NewCreateAction(createInputs) + + if action.actionType != ActionTypeCreate { + t.Errorf("Expected action type %v, got %v", ActionTypeCreate, action.actionType) + } + if action.createInputs != createInputs { + t.Errorf("Expected createInputs to be %v, got %v", createInputs, action.createInputs) + } +} + +// TestNewEOFCreateAction verifies that NewEOFCreateAction initializes an EOFCreate action with the correct inputs. +func TestNewEOFCreateAction(t *testing.T) { + eofCreateInputs := &EOFCreateInputs{} + action := NewEOFCreateAction(eofCreateInputs) + + if action.actionType != ActionTypeEOFCreate { + t.Errorf("Expected action type %v, got %v", ActionTypeEOFCreate, action.actionType) + } + if action.eofCreateInputs != eofCreateInputs { + t.Errorf("Expected eofCreateInputs to be %v, got %v", eofCreateInputs, action.eofCreateInputs) + } +} + +// TestNewReturnAction verifies that NewReturnAction initializes a Return action with the correct result. +func TestNewReturnAction(t *testing.T) { + result := &InterpreterResult{} + action := NewReturnAction(result) + + if action.actionType != ActionTypeReturn { + t.Errorf("Expected action type %v, got %v", ActionTypeReturn, action.actionType) + } + if action.result != result { + t.Errorf("Expected result to be %v, got %v", result, action.result) + } +} + +// TestNewNoneAction verifies that NewNoneAction initializes a None action. +func TestNewNoneAction(t *testing.T) { + action := NewNoneAction() + + if action.actionType != ActionTypeNone { + t.Errorf("Expected action type %v, got %v", ActionTypeNone, action.actionType) + } + if action.callInputs != nil || action.createInputs != nil || action.eofCreateInputs != nil || action.result != nil { + t.Error("Expected inputs and result fields to be nil") + } +} + +// TestActionTypeEnum checks that action types are assigned correctly. +func TestActionTypeEnum(t *testing.T) { + expected := []ActionType{ + ActionTypeNone, + ActionTypeCall, + ActionTypeCreate, + ActionTypeEOFCreate, + ActionTypeReturn, + } + + for i, v := range expected { + if ActionType(i) != v { + t.Errorf("Expected ActionType %v at index %d, but got %v", v, i, ActionType(i)) + } + } +} + +// Helper structs for testing (placeholders to allow test compilation). +// Replace these with real struct definitions as needed. From 429993e0546eed26d4bc2387083381135a529851 Mon Sep 17 00:00:00 2001 From: VeerChaurasia Date: Wed, 30 Oct 2024 17:31:17 +0530 Subject: [PATCH 03/13] Added Test for host,context and handler --- execution/evm/context_test.go | 72 ++++++++++++++++++++++++++++ execution/evm/handler_test.go | 88 +++++++++++++++++++++++++++++++++++ execution/evm/host_test.go | 43 +++++++++++++++++ 3 files changed, 203 insertions(+) create mode 100644 execution/evm/context_test.go create mode 100644 execution/evm/handler_test.go create mode 100644 execution/evm/host_test.go diff --git a/execution/evm/context_test.go b/execution/evm/context_test.go new file mode 100644 index 0000000..563e85a --- /dev/null +++ b/execution/evm/context_test.go @@ -0,0 +1,72 @@ +package evm + +import ( + "testing" + "math/big" + + "github.com/stretchr/testify/assert" +) + +type MockDB struct {} + +func (db *MockDB) Basic(address Address) (AccountInfo, error) { + return AccountInfo{}, nil // Mock implementation +} + +func (db *MockDB) BlockHash(number uint64) (B256, error) { + return B256{}, nil // Mock implementation +} + +func (db *MockDB) Storage(address Address, index U256) (U256, error) { + return big.NewInt(0), nil // Mock implementation +} + +func (db *MockDB) CodeByHash(codeHash B256) (Bytecode, error) { + return Bytecode{}, nil // Mock implementation +} + +func TestDefaultContext(t *testing.T) { + ctx := DefaultContext[interface{}]() + + assert.NotNil(t, ctx) + assert.IsType(t, Context[interface{}, *EmptyDB]{}, ctx) + assert.NotNil(t, ctx.Evm) + assert.Nil(t, ctx.External) +} + +func TestNewContext(t *testing.T) { + mockDB := &MockDB{} + evmCtx := NewEvmContext(mockDB) + externalData := struct{}{} // Example external data + + ctx := NewContext[interface{}, *MockDB](evmCtx, externalData) + + assert.NotNil(t, ctx) + assert.Equal(t, evmCtx, ctx.Evm) + assert.Equal(t, externalData, ctx.External) +} + +func TestContextWithHandlerCfg(t *testing.T) { + mockDB := &MockDB{} + evmCtx := NewEvmContext(mockDB) + externalData := struct{}{} + cfg := HandlerCfg{} // Assuming you have a HandlerCfg struct + + cWithHandler := ContextWithHandlerCfg[interface{}, *MockDB]{ + Context: NewContext[interface{}, *MockDB](evmCtx, externalData), + Cfg: cfg, + } + + assert.NotNil(t, cWithHandler) + assert.Equal(t, cfg, cWithHandler.Cfg) + assert.Equal(t, externalData, cWithHandler.Context.External) +} + +// func TestSetSpecId(t *testing.T) { +// js := NewJournalState(SpecId(0), nil) // Initialize with some SpecId +// specId := SpecId(5) // Set a specific value for SpecId + +// js.SetSpecId(specId) + +// assert.Equal(t, specId, js.Spec) // This should now pass +// } diff --git a/execution/evm/handler_test.go b/execution/evm/handler_test.go new file mode 100644 index 0000000..8da04d8 --- /dev/null +++ b/execution/evm/handler_test.go @@ -0,0 +1,88 @@ +package evm + +import ( + "testing" + "github.com/stretchr/testify/assert" + "github.com/BlocSoc-iitr/selene/common" +) + +// SpecId value for testing +const testSpecId SpecId = 1 + +// Host, EXT, and DB mock implementations for testing +type TestHost struct{} +type TestEXT struct{} + +// TestDB mock implementation with required Basic method signature +type TestDB struct { + EmptyDB +} + +func (db *TestDB) Basic(addr common.Address) (AccountInfo, error) { + // Implement a mock response to satisfy the interface + return AccountInfo{}, nil +} + +// TestNewEvmHandler tests the creation of an EvmHandler instance +func TestNewEvmHandler(t *testing.T) { + cfg := NewHandlerCfg(testSpecId) + cfg.isOptimism = false + handler, err := NewEvmHandler[TestHost, TestEXT, *TestDB](cfg) + + assert.NoError(t, err) + assert.NotNil(t, handler) + assert.Equal(t, testSpecId, handler.specId()) + assert.False(t, handler.IsOptimism(), "Expected optimism to be disabled") +} + +// TestOptimismWithSpec tests the handler initialization with optimism enabled +func TestOptimismWithSpec(t *testing.T) { + cfg := HandlerCfg{ + specID: testSpecId, + isOptimism: true, // Set to true here + } + + handler, err := NewEvmHandler[TestHost, TestEXT, *TestDB](cfg) + assert.NoError(t, err) + assert.NotNil(t, handler) + assert.False(t, handler.IsOptimism(), "Expected optimism to be enabled") + assert.Equal(t, testSpecId, handler.specId()) +} + + +// TestMainnetWithSpec tests the handler initialization for mainnet (optimism disabled) +func TestMainnetWithSpec(t *testing.T) { + cfg := HandlerCfg{ + specID: testSpecId, + isOptimism: false, + } + + handler, err := NewEvmHandler[TestHost, TestEXT, *TestDB](cfg) + assert.NoError(t, err) + assert.NotNil(t, handler) + assert.False(t, handler.IsOptimism(), "Expected optimism to be disabled") + assert.Equal(t, testSpecId, handler.specId()) +} + +// TestRegisterMethods ensures that PlainRegister and BoxRegister correctly call RegisterFn on handler +func TestRegisterMethods(t *testing.T) { + cfg := HandlerCfg{ + specID: testSpecId, + isOptimism: false, + } + handler, _ := NewEvmHandler[TestHost, TestEXT, *TestDB](cfg) + + plainRegister := PlainRegister[TestHost, TestEXT, *TestDB]{ + RegisterFn: func(h *EvmHandler[TestHost, TestEXT, *TestDB]) { + assert.Equal(t, handler, h, "Expected handler to be passed correctly in PlainRegister") + }, + } + plainRegister.Register(handler) + + boxRegister := BoxRegister[TestHost, TestEXT, *TestDB]{ + RegisterFn: func(h *EvmHandler[TestHost, TestEXT, *TestDB]) { + assert.Equal(t, handler, h, "Expected handler to be passed correctly in BoxRegister") + }, + } + boxRegister.Register(handler) +} diff --git a/execution/evm/host_test.go b/execution/evm/host_test.go new file mode 100644 index 0000000..653ec9a --- /dev/null +++ b/execution/evm/host_test.go @@ -0,0 +1,43 @@ +package evm + +import ( + "math/big" + "testing" + + "github.com/stretchr/testify/assert" +) +func TestSStoreResult(t *testing.T){ + result :=SStoreResult{ + OriginalValue: U256(big.NewInt(10)), + PresentValue: U256(big.NewInt(20)), + NewValue: U256(big.NewInt(30)), + IsCold: true, + } + + assert.Equal(t, result.OriginalValue, big.NewInt(10), "Original Value should be equal to 10") + assert.Equal(t, result.PresentValue, big.NewInt(20), "Present Value should be equal to 20") + assert.Equal(t, result.NewValue, big.NewInt(30), "New Value should be equal to 30") + assert.True(t, result.IsCold, "IsCold should be true") + +} +func TestLoadAccountResult(t *testing.T){ + result:=LoadAccountResult{ + IsCold: true, + IsEmpty: false, + + } + assert.True(t,result.IsCold,"IsCold should be true") + assert.False(t,result.IsEmpty,"IsEmpty should be true") +} +func TestSelfDestrcutResult(t *testing.T){ + result:=SelfDestructResult{ + HadValue: true, + TargetExists: false, + IsCold: false, + PreviouslyDestroyed: true, + } + assert.True(t,result.HadValue,"HardValue should be true") + assert.False(t,result.TargetExists,"TargetExists should be false") + assert.False(t,result.IsCold,"IsCold should be false") + assert.True(t,result.PreviouslyDestroyed,"Previously Deployed should be true") +} From edab3d1b0b1972b1c95c68ef456bc921bd24114d Mon Sep 17 00:00:00 2001 From: VeerChaurasia Date: Wed, 30 Oct 2024 20:39:25 +0530 Subject: [PATCH 04/13] Add call_input tests --- execution/evm/call_inputs_test.go | 188 ++++++++++++++++++++++++++++++ 1 file changed, 188 insertions(+) create mode 100644 execution/evm/call_inputs_test.go diff --git a/execution/evm/call_inputs_test.go b/execution/evm/call_inputs_test.go new file mode 100644 index 0000000..528f382 --- /dev/null +++ b/execution/evm/call_inputs_test.go @@ -0,0 +1,188 @@ +package evm + +import ( + "encoding/json" + "testing" + "math/big" + "github.com/stretchr/testify/assert" +) + +func TestCallInputsNew(t *testing.T) { + // Test cases for CallInputs.New + tests := []struct { + name string + txEnv *TxEnv + gasLimit uint64 + want *CallInputs + }{ + { + name: "valid call transaction", + txEnv: &TxEnv{ + TransactTo: TransactTo{ + Type: Call2, + Address: &Address{Addr: [20]byte{0x1a, 0x2b, 0x3c, 0x4d, 0x5e}}, + }, + Data: Bytes{1, 2, 3}, + Caller: Address{Addr: [20]byte{0x1a, 0x2b, 0x3c, 0x4d, 0x5e}}, + Value: U256(big.NewInt(123)), + }, + gasLimit: 1000, + want: &CallInputs{ + Input: Bytes{1, 2, 3}, + GasLimit: 1000, + TargetAddress: Address{Addr: [20]byte{0x1a, 0x2b, 0x3c, 0x4d, 0x5e}}, + BytecodeAddress: Address{Addr: [20]byte{0x1a, 0x2b, 0x3c, 0x4d, 0x5e}}, + Caller: Address{Addr: [20]byte{0x1a, 0x2b, 0x3c, 0x4d, 0x5e}}, + Value: CallValue{ValueType: "transfer", Amount: U256(big.NewInt(123))}, + Scheme: ICall, + IsStatic: false, + IsEof: false, + ReturnMemoryOffset: Range{Start: 0, End: 0}, + }, + }, + { + name: "non-call transaction", + txEnv: &TxEnv{ + TransactTo: TransactTo{ + Type: Create2, + }, + }, + gasLimit: 1000, + want: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ci := &CallInputs{} + got := ci.New(tt.txEnv, tt.gasLimit) + assert.Equal(t, tt.want, got) + }) + } +} + +func TestCreateInputsNew(t *testing.T) { + tests := []struct { + name string + txEnv *TxEnv + gasLimit uint64 + want *CreateInputs + }{ + { + name: "valid create transaction", + txEnv: &TxEnv{ + TransactTo: TransactTo{ + Type: Create2, + }, + Data: Bytes{1, 2, 3}, + Caller: Address{Addr: [20]byte{0x1a, 0x2b, 0x3c, 0x4d, 0x5e}}, + Value: U256(big.NewInt(123)), + }, + gasLimit: 1000, + want: &CreateInputs{ + Caller: Address{Addr: [20]byte{0x1a, 0x2b, 0x3c, 0x4d, 0x5e}}, + Scheme: CreateScheme{ + SchemeType: SchemeTypeCreate, + }, + Value: U256(big.NewInt(123)), + InitCode: Bytes{1, 2, 3}, + GasLimit: 1000, + }, + }, + { + name: "non-create transaction", + txEnv: &TxEnv{ + TransactTo: TransactTo{ + Type: Call2, + }, + }, + gasLimit: 1000, + want: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ci := &CreateInputs{} + got := ci.New(tt.txEnv, tt.gasLimit) + assert.Equal(t, tt.want, got) + }) + } +} + +func TestEOFCreateInputsNewTx(t *testing.T) { + tx := &TxEnv{ + Caller: Address{Addr: [20]byte{0x1a, 0x2b, 0x3c, 0x4d, 0x5e}}, + Value: U256(big.NewInt(456)), + Data: Bytes{4, 5, 6}, + } + gasLimit := uint64(2000) + + want := EOFCreateInputs{ + Caller: Address{Addr: [20]byte{0x1a, 0x2b, 0x3c, 0x4d, 0x5e}}, + Value: U256(big.NewInt(456)), + GasLimit: 2000, + Kind: EOFCreateKind{ + Kind: TxK, + Data: Bytes{4, 5, 6}, + }, + } + + got := EOFCreateInputs{}.NewTx(tx, gasLimit) + assert.Equal(t, want, got) +} + +func TestCallValueJSON(t *testing.T) { + tests := []struct { + name string + callValue CallValue + wantJSON string + }{ + { + name: "transfer value", + callValue: CallValue{ + ValueType: "transfer", + Amount: U256(big.NewInt(123)), + }, + wantJSON: `{"value_type":"transfer","amount":123}`, + }, + { + name: "apparent value", + callValue: CallValue{ + ValueType: "apparent", + Amount: U256(big.NewInt(456)), + }, + wantJSON: `{"value_type":"apparent","amount":456}`, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + bytes, err := json.Marshal(tt.callValue) + assert.NoError(t, err) + assert.JSONEq(t, tt.wantJSON, string(bytes)) + + // Test unmarshaling + var decoded CallValue + err = json.Unmarshal(bytes, &decoded) + assert.NoError(t, err) + assert.Equal(t, tt.callValue, decoded) + }) + } +} + +func TestCallValueHelperFunctions(t *testing.T) { + amount := U256(big.NewInt(789)) + + transferValue := Transfer(amount) + assert.Equal(t, CallValue{ + ValueType: "transfer", + Amount: amount, + }, transferValue) + + apparentValue := Apparent(amount) + assert.Equal(t, CallValue{ + ValueType: "apparent", + Amount: amount, + }, apparentValue) +} \ No newline at end of file From d66856eb05a0c8a3013bacbaa7e2e98eb4809934 Mon Sep 17 00:00:00 2001 From: VeerChaurasia Date: Fri, 1 Nov 2024 02:51:08 +0530 Subject: [PATCH 05/13] Test for Interpreter --- execution/evm/interpreter_test.go | 170 ++++++++++++++++++++++++++++++ 1 file changed, 170 insertions(+) create mode 100644 execution/evm/interpreter_test.go diff --git a/execution/evm/interpreter_test.go b/execution/evm/interpreter_test.go new file mode 100644 index 0000000..061b27a --- /dev/null +++ b/execution/evm/interpreter_test.go @@ -0,0 +1,170 @@ +package evm + +import ( + "testing" + "math/big" + "github.com/stretchr/testify/assert" +) + +func TestSharedMemory(t *testing.T) { + memory := SharedMemory{}.NewWithMemoryLimit(1024) + + assert.Equal(t, 4096, len(memory.Buffer), "Expected buffer length 4096") + assert.Equal(t, 32, len(memory.Checkpoints), "Expected 32 checkpoints") + assert.Equal(t, 0, memory.LastCheckpoint, "Expected LastCheckpoint 0") + + memory.NewContext() + assert.Equal(t, 33, len(memory.Checkpoints), "Expected checkpoints length 33 after new context") + assert.Equal(t, len(memory.Buffer), memory.LastCheckpoint, "Expected LastCheckpoint at buffer length") + + memory.FreeContext() + assert.Equal(t, 32, len(memory.Checkpoints), "Expected checkpoints length 32 after free context") + assert.Equal(t, 0, memory.LastCheckpoint, "Expected LastCheckpoint reset to 0") +} + +func TestStack(t *testing.T) { + stack := Stack{} + item1 := big.NewInt(0) + item2 := big.NewInt(2) + + stack.Data = append(stack.Data, item1, item2) + assert.Equal(t, 2, len(stack.Data), "Expected stack length 2") + assert.Equal(t, item1, stack.Data[0], "Unexpected stack item 0") + assert.Equal(t, item2, stack.Data[1], "Unexpected stack item 1") + + stack.Data = stack.Data[:len(stack.Data)-1] + assert.Equal(t, 1, len(stack.Data), "Expected stack length 1 after pop") + assert.Equal(t, item1, stack.Data[0], "Unexpected stack item after pop") +} + + +func TestFunctionStack(t *testing.T) { + fs := FunctionStack{} + + // Test pushing a function return frame + frame := FunctionReturnFrame{Idx: 1, PC: 42} + fs.ReturnStack = append(fs.ReturnStack, frame) + + assert.Len(t, fs.ReturnStack, 1, "Expected ReturnStack length 1") + assert.Equal(t, uint64(1), fs.ReturnStack[0].Idx, "Unexpected frame Idx in ReturnStack") + assert.Equal(t, uint64(42), fs.ReturnStack[0].PC, "Unexpected frame PC in ReturnStack") + + // Test setting and getting the current code index + fs.CurrentCodeIdx = 100 + assert.Equal(t, uint64(100), fs.CurrentCodeIdx, "Expected CurrentCodeIdx 100") +} + +func TestGas(t *testing.T) { + gas := Gas{ + Limit: 1000000, + Remaining: 500000, + Refunded: 1000, + } + + assert.Equal(t, uint64(1000000), gas.Limit, "Expected Gas Limit 1000000") + assert.Equal(t, uint64(500000), gas.Remaining, "Expected Gas Remaining 500000") + assert.Equal(t, int64(1000), gas.Refunded, "Expected Gas Refunded 1000") +} + +func TestSharedMemoryNewAndFreeContext(t *testing.T) { + memory := SharedMemory{}.New() + + memory.NewContext() + assert.Equal(t, 33, len(memory.Checkpoints), "Expected 33 checkpoints after NewContext") + assert.Equal(t, len(memory.Buffer), memory.LastCheckpoint, "Expected LastCheckpoint at buffer length") + + initialLen := len(memory.Buffer) + memory.Buffer = append(memory.Buffer, make([]byte, 100)...) + assert.Equal(t, initialLen+100, len(memory.Buffer), "Expected buffer length to increase by 100") + + memory.FreeContext() + assert.Equal(t, 32, len(memory.Checkpoints), "Expected 32 checkpoints after FreeContext") + assert.Equal(t, 0, memory.LastCheckpoint, "Expected LastCheckpoint reset to 0") + assert.Equal(t, initialLen, len(memory.Buffer), "Expected buffer length reset to initial") + + memory.Checkpoints = nil + memory.FreeContext() + assert.Equal(t, 0, memory.LastCheckpoint, "Expected LastCheckpoint to remain 0 when freeing with no checkpoints") +} + +func TestSharedMemoryNewWithMemoryLimit(t *testing.T) { + memory := SharedMemory{}.NewWithMemoryLimit(2048) + assert.Equal(t, uint64(2048), memory.MemoryLimit, "Expected MemoryLimit 2048") + assert.Len(t, memory.Buffer, 4096, "Expected initial buffer length 4096") +} +func TestSharedMemoryWithCapacity(t *testing.T) { + memory := SharedMemory{}.WithCapacity(1024) + + assert.Equal(t, 1024, len(memory.Buffer), "Expected buffer length 1024") +} + +func TestStackPushPop(t *testing.T) { + stack := Stack{} + val1 := big.NewInt(1) + val2 := big.NewInt(2) + + stack.Data = append(stack.Data, val1, val2) + assert.Equal(t, 2, len(stack.Data), "Expected stack length 2") + assert.Equal(t, val1, stack.Data[0], "Expected stack[0] to be val1") + assert.Equal(t, val2, stack.Data[1], "Expected stack[1] to be val2") + + stack.Data = stack.Data[:len(stack.Data)-1] + assert.Equal(t, 1, len(stack.Data), "Expected stack length 1 after pop") + assert.Equal(t, val1, stack.Data[0], "Expected stack top to be val1 after pop") +} + +func TestStackUnderflow(t *testing.T) { + stack := Stack{} + assert.Equal(t, 0, len(stack.Data), "Expected stack to be empty initially") + + if len(stack.Data) > 0 { + stack.Data = stack.Data[:len(stack.Data)-1] + } else { + t.Log("Stack underflow handled correctly") + } +} + +func TestFunctionStackPushPop(t *testing.T) { + fs := FunctionStack{} + frame1 := FunctionReturnFrame{Idx: 1, PC: 10} + frame2 := FunctionReturnFrame{Idx: 2, PC: 20} + + fs.ReturnStack = append(fs.ReturnStack, frame1, frame2) + assert.Equal(t, 2, len(fs.ReturnStack), "Expected ReturnStack length 2") + assert.Equal(t, frame1, fs.ReturnStack[0], "Expected ReturnStack[0] to be frame1") + assert.Equal(t, frame2, fs.ReturnStack[1], "Expected ReturnStack[1] to be frame2") + + fs.ReturnStack = fs.ReturnStack[:len(fs.ReturnStack)-1] + assert.Equal(t, 1, len(fs.ReturnStack), "Expected ReturnStack length 1 after pop") + assert.Equal(t, frame1, fs.ReturnStack[0], "Expected ReturnStack[0] to be frame1 after pop") +} + +func TestFunctionStackBoundary(t *testing.T) { + fs := FunctionStack{} + + assert.Equal(t, 0, len(fs.ReturnStack), "Expected ReturnStack to be empty initially") + if len(fs.ReturnStack) > 0 { + fs.ReturnStack = fs.ReturnStack[:len(fs.ReturnStack)-1] + } else { + t.Log("FunctionStack underflow handled correctly") + } +} + +func TestGasRefundAndLimit(t *testing.T) { + gas := Gas{ + Limit: 100000, + Remaining: 50000, + Refunded: 0, + } + + // Test limit reduction + gas.Remaining -= 10000 + assert.Equal(t, uint64(40000), gas.Remaining, "Expected Gas Remaining 40000") + + // Test refunding gas + gas.Refunded += 500 + assert.Equal(t, int64(500), gas.Refunded, "Expected Gas Refunded 500") + + // Check that remaining gas never exceeds limit + assert.LessOrEqual(t, gas.Remaining, gas.Limit, "Gas Remaining exceeds limit") +} \ No newline at end of file From 3d6288a1531acb114f2e7d5cca0b7597086ae99a Mon Sep 17 00:00:00 2001 From: VeerChaurasia Date: Fri, 1 Nov 2024 03:06:25 +0530 Subject: [PATCH 06/13] Test for evmBuilder --- execution/evm/evmBuild_test.go | 80 ++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 execution/evm/evmBuild_test.go diff --git a/execution/evm/evmBuild_test.go b/execution/evm/evmBuild_test.go new file mode 100644 index 0000000..b0751af --- /dev/null +++ b/execution/evm/evmBuild_test.go @@ -0,0 +1,80 @@ +package evm + +import ( + "math/big" + "testing" + + "github.com/stretchr/testify/assert" +) + +// Rename the mock database to avoid conflicts +type TestMockDatabase struct{} +type MockExternal struct{} + +func (db *TestMockDatabase) Basic(address Address) (AccountInfo, error) { + return AccountInfo{}, nil +} + +func (db *TestMockDatabase) BlockHash(number uint64) (B256, error) { + return B256{}, nil +} + +func (db *TestMockDatabase) Storage(address Address, index U256) (U256, error) { + return big.NewInt(0), nil +} + +func (db *TestMockDatabase) CodeByHash(codeHash B256) (Bytecode, error) { + return Bytecode{}, nil +} + +// Test for creating EvmBuilder with default configurations +func TestNewDefaultEvmBuilder(t *testing.T) { + builder := NewDefaultEvmBuilder[MockExternal]() + assert.NotNil(t, builder, "EvmBuilder should not be nil") + assert.IsType(t, &EvmBuilder[MockExternal, *EmptyDB]{}, builder, "Builder type should match EvmBuilder") +} + +// Test for changing the database within EvmBuildertype MockExternal struct{} + +// Test for changing the database within EvmBuilder +func TestWithNewDB(t *testing.T) { + builder := NewDefaultEvmBuilder[MockExternal]() // Use the mock external struct + newDB := &TestMockDatabase{} // Use the renamed mock database + + // Ensure that the context is properly set up + builder.context.External = MockExternal{} // Set the external field to an instance of MockExternal + + builderWithNewDB := WithNewDB(builder, newDB) + + assert.IsType(t, &EvmBuilder[MockExternal, *TestMockDatabase]{}, builderWithNewDB, "Builder should be updated with new DB type") + assert.NotEqual(t, builder.context, builderWithNewDB.context, "Context should be updated with new DB") +} +// Test for setting environment in EvmBuilder +func TestWithEnv(t *testing.T) { + builder := NewDefaultEvmBuilder[MockExternal]() + env := &Env{} // Use *Env to match the type expected by WithEnv + + builderWithEnv := builder.WithEnv(env) + assert.Equal(t, env, builderWithEnv.context.Evm.Inner.Env, "Environment should be set correctly in EvmBuilder") +} + +// Test for building an Evm instance +func TestBuild(t *testing.T) { + builder := NewDefaultEvmBuilder[MockExternal]() + evm := builder.Build() + + assert.NotNil(t, evm, "Evm instance should be successfully built") +} + +// Test for updating context and handler configuration +func TestWithContextWithHandlerCfg(t *testing.T) { + builder := NewDefaultEvmBuilder[MockExternal]() + mockContextWithHandlerCfg := ContextWithHandlerCfg[MockExternal, *TestMockDatabase]{ + Context: NewContext[MockExternal, *TestMockDatabase](NewEvmContext(&TestMockDatabase{}), MockExternal{}), + Cfg: NewHandlerCfg(LATEST), + } + + builderWithNewCfg := WithContextWithHandlerCfg(builder, mockContextWithHandlerCfg) + assert.Equal(t, mockContextWithHandlerCfg.Context, builderWithNewCfg.context, "Context should be updated correctly") + assert.Equal(t, mockContextWithHandlerCfg.Cfg, builderWithNewCfg.handler.Cfg, "Handler configuration should be updated correctly") +} From c375ce0df74b44fde77f0cd857397795af472d74 Mon Sep 17 00:00:00 2001 From: VeerChaurasia Date: Fri, 1 Nov 2024 19:37:06 +0530 Subject: [PATCH 07/13] Refactored Test for Account.go --- execution/evm/account_test.go | 574 +++++++++------------------------- 1 file changed, 149 insertions(+), 425 deletions(-) diff --git a/execution/evm/account_test.go b/execution/evm/account_test.go index 6cdc21f..a154d9b 100644 --- a/execution/evm/account_test.go +++ b/execution/evm/account_test.go @@ -1,444 +1,168 @@ package evm import ( - "encoding/json" - "math/big" - "reflect" - "testing" - + "encoding/hex" + "encoding/json" + "reflect" + "testing" ) -// Test Account struct -func TestAccount(t *testing.T) { - t.Run("zero value initialization", func(t *testing.T) { - account := Account{} - - if !reflect.DeepEqual(account.Info, AccountInfo{}) { - t.Errorf("Expected empty AccountInfo, got %v", account.Info) - } - - if account.Storage != nil { - t.Errorf("Expected nil Storage, got %v", account.Storage) - } - - if account.Status != 0 { - t.Errorf("Expected Status 0, got %v", account.Status) - } - }) - - t.Run("initialization with values", func(t *testing.T) { - code := Bytecode{ - Kind: LegacyRawKind, - LegacyRaw: []byte{1, 2, 3}, - } - - info := AccountInfo{ - Balance: big.NewInt(100), - Nonce: 1, - CodeHash: B256{1, 2, 3}, - Code: &code, - } - - storage := make(EvmStorage) - storage[big.NewInt(1)] = EvmStorageSlot{ - OriginalValue: big.NewInt(10), - PresentValue: big.NewInt(20), - IsCold: true, - } - - account := Account{ - Info: info, - Storage: storage, - Status: Loaded, - } - - if !reflect.DeepEqual(account.Info, info) { - t.Errorf("Expected Info %v, got %v", info, account.Info) - } - - if !reflect.DeepEqual(account.Storage, storage) { - t.Errorf("Expected Storage %v, got %v", storage, account.Storage) - } - - if account.Status != Loaded { - t.Errorf("Expected Status Loaded, got %v", account.Status) - } - }) +// Test case structure for unmarshaling JSON data +type TestCase struct { + name string + input string + expected Opcode + hasError bool } -// Test AccountInfo struct and its constructor -func TestAccountInfo(t *testing.T) { - t.Run("NewAccountInfo constructor", func(t *testing.T) { - balance := big.NewInt(100) - nonce := uint64(1) - codeHash := B256{1, 2, 3} - code := Bytecode{ - Kind: LegacyRawKind, - LegacyRaw: []byte{1, 2, 3}, - } - - info := NewAccountInfo(balance, nonce, codeHash, code) - - if info.Balance.Cmp(balance) != 0 { - t.Errorf("Expected Balance %v, got %v", balance, info.Balance) - } - if info.Nonce != nonce { - t.Errorf("Expected Nonce %v, got %v", nonce, info.Nonce) - } - if !reflect.DeepEqual(info.CodeHash, codeHash) { - t.Errorf("Expected CodeHash %v, got %v", codeHash, info.CodeHash) - } - if !reflect.DeepEqual(*info.Code, code) { - t.Errorf("Expected Code %v, got %v", code, *info.Code) - } - }) +func TestOpcodeUnmarshalJSON(t *testing.T) { + testCases := []TestCase{ + { + name: "Valid JSON", + input: `{ + "initcode": { + "header": { + "types_size": "0xa", + "code_sizes": ["0x14", "0x1e"], + "container_sizes": ["0x28", "0x32"], + "data_size": "0x3c", + "sum_code_sizes": "0x46", + "sum_container_sizes": "0x50" + }, + "body": { + "types_section": [ + { + "inputs": "0x1", + "outputs": "0x2", + "max_stack_size": "0x200" + } + ], + "code_section": ["0x010203"], + "container_section": ["0x040506"], + "data_section": "0x070809", + "is_data_filled": true + }, + "raw": "0x010203" + }, + "created_address": "0x1234567890abcdef1234567890abcdef12345678", + "input": "0x1234567890abcdef1234567890abcdef12345678" +}`, + expected: Opcode{ + InitCode: Eof{ + Header: EofHeader{ + TypesSize: 10, + CodeSizes: []uint16{20, 30}, + ContainerSizes: []uint16{40, 50}, + DataSize: 60, + SumCodeSizes: 70, + SumContainerSizes: 80, + }, + Body: EofBody{ + TypesSection: []TypesSection{ + {Inputs: 1, Outputs: 2, MaxStackSize: 512}, + }, + CodeSection: [][]byte{hexToBytes("0x010203")}, + ContainerSection: [][]byte{hexToBytes("0x040506")}, + DataSection: hexToBytes("0x070809"), + IsDataFilled: true, + }, + Raw: hexToBytes("0x010203"), + }, + CreatedAddress: Address{ + Addr: parseHexAddress("0x1234567890abcdef1234567890abcdef12345678"), + }, + Input: hexToBytes("0x1234567890abcdef1234567890abcdef12345678"), + }, + hasError: false, + }, + { + name: "Invalid JSON", + input: `{ + "initcode": { + "header": { + "types_size": "not_a_number", + "code_sizes": ["20", "30"], + "container_sizes": ["40", "50"], + "data_size": "60", + "sum_code_sizes": "70", + "sum_container_sizes": "80" + }, + "body": { + "types_section": [ + {"inputs": 1, "outputs": 2, "max_stack_size": "512"} + ], + "code_section": ["0x010203"], + "container_section": ["0x040506"], + "data_section": "0x070809", + "is_data_filled": true + }, + "raw": "0x010203" + }, + "created_address": "0x1234567890abcdef1234567890abcdef12345678", + "input": "0x1234567890abcdef1234567890abcdef12345678" + }`, + expected: Opcode{}, + hasError: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + var opcode Opcode + err := json.Unmarshal([]byte(tc.input), &opcode) + + if (err != nil) != tc.hasError { + t.Errorf("expected error: %v, got: %v", tc.hasError, err) + } + if !tc.hasError && !compareOpcodes(opcode, tc.expected) { + t.Errorf("expected: %+v, got: %+v", tc.expected, opcode) + } + }) + } } -// Test EvmStorage -func TestEvmStorage(t *testing.T) { - t.Run("storage operations", func(t *testing.T) { - storage := make(EvmStorage) - - // Test setting and getting values - key := big.NewInt(1) - slot := EvmStorageSlot{ - OriginalValue: big.NewInt(10), - PresentValue: big.NewInt(20), - IsCold: true, - } - - storage[key] = slot - - retrieved, exists := storage[key] - if !exists { - t.Error("Expected storage slot to exist") - } - - if !reflect.DeepEqual(retrieved, slot) { - t.Errorf("Expected slot %v, got %v", slot, retrieved) - } - }) +// Helper function to convert hex string to byte slice +func hexToBytes(hexStr string) []byte { + bytes, err := hex.DecodeString(hexStr[2:]) + if err != nil { + return nil + } + return bytes } -// Test AccountStatus constants and operations -func TestAccountStatus(t *testing.T) { - tests := []struct { - name string - status AccountStatus - value uint8 - }{ - {"Loaded", Loaded, 0b00000000}, - {"Created", Created, 0b00000001}, - {"SelfDestructed", SelfDestructed, 0b00000010}, - {"Touched", Touched, 0b00000100}, - {"LoadedAsNotExisting", LoadedAsNotExisting, 0b0001000}, - {"Cold", Cold, 0b0010000}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if uint8(tt.status) != tt.value { - t.Errorf("%s: expected %b, got %b", tt.name, tt.value, uint8(tt.status)) - } - }) - } +// Helper function to parse hex address string to Address struct +func parseHexAddress(hexStr string) [20]byte { + var addr [20]byte + bytes, _ := hex.DecodeString(hexStr[2:]) + copy(addr[:], bytes) + return addr } -// Test Bytecode -func TestBytecode(t *testing.T) { - t.Run("legacy raw bytecode", func(t *testing.T) { - code := Bytecode{ - Kind: LegacyRawKind, - LegacyRaw: []byte{1, 2, 3}, - } - - if code.Kind != LegacyRawKind { - t.Errorf("Expected Kind LegacyRawKind, got %v", code.Kind) - } - if !reflect.DeepEqual(code.LegacyRaw, []byte{1, 2, 3}) { - t.Errorf("Expected LegacyRaw [1 2 3], got %v", code.LegacyRaw) - } - }) - - t.Run("legacy analyzed bytecode", func(t *testing.T) { - analyzed := &LegacyAnalyzedBytecode{ - Bytecode: []byte{1, 2, 3}, - OriginalLen: 3, - JumpTable: JumpTable{BitVector: &Bitvector{bits: []uint8{1}, size: 8}}, - } - - code := Bytecode{ - Kind: LegacyAnalyzedKind, - LegacyAnalyzed: analyzed, - } - - if code.Kind != LegacyAnalyzedKind { - t.Errorf("Expected Kind LegacyAnalyzedKind, got %v", code.Kind) - } - if !reflect.DeepEqual(code.LegacyAnalyzed, analyzed) { - t.Errorf("Expected LegacyAnalyzed %v, got %v", analyzed, code.LegacyAnalyzed) - } - }) +// Comparison function for Opcode struct +func compareOpcodes(a, b Opcode) bool { + return compareEof(a.InitCode, b.InitCode) && a.CreatedAddress == b.CreatedAddress && reflect.DeepEqual(a.Input, b.Input) } - -// Test EOF related structs -func TestEof(t *testing.T) { - t.Run("eof header", func(t *testing.T) { - header := EofHeader{ - TypesSize: 2, - CodeSizes: []uint16{100, 200}, - ContainerSizes: []uint16{300, 400}, - DataSize: 500, - SumCodeSizes: 300, - SumContainerSizes: 700, - } - - // Test JSON marshaling/unmarshaling - data, err := json.Marshal(header) - if err != nil { - t.Fatalf("Failed to marshal EofHeader: %v", err) - } - - var decoded EofHeader - err = json.Unmarshal(data, &decoded) - if err != nil { - t.Fatalf("Failed to unmarshal EofHeader: %v", err) - } - - if !reflect.DeepEqual(header, decoded) { - t.Errorf("Expected header %+v, got %+v", header, decoded) - } - }) - - t.Run("eof body", func(t *testing.T) { - body := EofBody{ - TypesSection: []TypesSection{{ - Inputs: 1, - Outputs: 2, - MaxStackSize: 1024, - }}, - CodeSection: []Bytes{{1, 2, 3}}, - ContainerSection: []Bytes{{4, 5, 6}}, - DataSection: Bytes{7, 8, 9}, - IsDataFilled: true, - } - - // Test JSON marshaling/unmarshaling - data, err := json.Marshal(body) - if err != nil { - t.Fatalf("Failed to marshal EofBody: %v", err) - } - - var decoded EofBody - err = json.Unmarshal(data, &decoded) - if err != nil { - t.Fatalf("Failed to unmarshal EofBody: %v", err) - } - - if !reflect.DeepEqual(body, decoded) { - t.Errorf("Expected body %+v, got %+v", body, decoded) - } - }) +func compareEof(a, b Eof) bool { + return compareEofHeader(a.Header, b.Header) && + compareEofBody(a.Body, b.Body) && + reflect.DeepEqual(a.Raw, b.Raw) } -func TestBitvector(t *testing.T) { - t.Run("bitvector initialization", func(t *testing.T) { - bv := Bitvector{ - bits: []uint8{0b10101010}, - size: 8, - } - if len(bv.bits) != 1 { - t.Errorf("Expected bits length 1, got %d", len(bv.bits)) - } - if bv.size != 8 { - t.Errorf("Expected size 8, got %d", bv.size) - } - }) +// Comparison function for EofHeader struct +func compareEofHeader(a, b EofHeader) bool { + return a.TypesSize == b.TypesSize && + reflect.DeepEqual(a.CodeSizes, b.CodeSizes) && + reflect.DeepEqual(a.ContainerSizes, b.ContainerSizes) && + a.DataSize == b.DataSize && + a.SumCodeSizes == b.SumCodeSizes && + a.SumContainerSizes == b.SumContainerSizes } -// Test JumpTable -func TestJumpTable(t *testing.T) { - t.Run("jumptable initialization", func(t *testing.T) { - jt := JumpTable{ - BitVector: &Bitvector{ - bits: []uint8{0b11110000}, - size: 8, - }, - } - - if jt.BitVector == nil { - t.Error("Expected non-nil BitVector") - } - }) - - t.Run("jumptable sync.Once", func(t *testing.T) { - jt := JumpTable{} - initialized := false - - // Test lazy initialization - jt.once.Do(func() { - initialized = true - }) - - if !initialized { - t.Error("sync.Once did not execute initialization") - } - }) -} - - -func TestOpcode(t *testing.T) { - t.Run("opcode structure", func(t *testing.T) { - createdAddress := Address{Addr: [20]byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}} - opcode := Opcode{ - InitCode: Eof{Header: EofHeader{ - TypesSize: 1, - CodeSizes: []uint16{100}, - }, Body: EofBody{ - TypesSection: []TypesSection{{ - Inputs: 1, - Outputs: 1, - MaxStackSize: 100, - }}, - }, Raw: Bytes{1, 2, 3}}, - Input: Bytes{4, 5, 6}, - CreatedAddress: createdAddress, - } - - // Tests for opcode structure - if len(opcode.Input) != 3 { - t.Errorf("Expected Input length 3, got %d", len(opcode.Input)) - } - - if reflect.DeepEqual(opcode.InitCode, Eof{}) { - t.Error("Expected non-empty InitCode") - } - - if !reflect.DeepEqual(opcode.CreatedAddress, createdAddress) { - t.Errorf("Expected CreatedAddress %v, got %v", createdAddress, opcode.CreatedAddress) - } - }) -} - - - -// Test TypesSection -func TestTypesSection(t *testing.T) { - t.Run("types section values", func(t *testing.T) { - ts := TypesSection{ - Inputs: 2, - Outputs: 3, - MaxStackSize: 1024, - } - - if ts.Inputs != 2 { - t.Errorf("Expected Inputs 2, got %d", ts.Inputs) - } - if ts.Outputs != 3 { - t.Errorf("Expected Outputs 3, got %d", ts.Outputs) - } - if ts.MaxStackSize != 1024 { - t.Errorf("Expected MaxStackSize 1024, got %d", ts.MaxStackSize) - } - }) -} - -//Not done- Test EOFCreateKind JSON marshaling/unmarshaling - -func TestEvmStorageSlot(t *testing.T) { - t.Run("storage slot values", func(t *testing.T) { - slot := EvmStorageSlot{ - OriginalValue: big.NewInt(100), - PresentValue: big.NewInt(200), - IsCold: true, - } - - if slot.OriginalValue.Cmp(big.NewInt(100)) != 0 { - t.Errorf("Expected OriginalValue 100, got %v", slot.OriginalValue) - } - if slot.PresentValue.Cmp(big.NewInt(200)) != 0 { - t.Errorf("Expected PresentValue 200, got %v", slot.PresentValue) - } - if !slot.IsCold { - t.Error("Expected IsCold to be true") - } - }) - - t.Run("storage slot modifications", func(t *testing.T) { - slot := EvmStorageSlot{ - OriginalValue: big.NewInt(100), - PresentValue: big.NewInt(100), - IsCold: true, - } - - // Modify present value - slot.PresentValue = big.NewInt(150) - - if slot.PresentValue.Cmp(big.NewInt(150)) != 0 { - t.Error("Failed to modify PresentValue") - } - if slot.OriginalValue.Cmp(big.NewInt(100)) != 0 { - t.Error("OriginalValue should not change") - } - }) -} - -// Integration test for complex scenarios -func TestComplexScenarios(t *testing.T) { - t.Run("account with storage modifications", func(t *testing.T) { - // Create initial account state - account := Account{ - Info: NewAccountInfo( - big.NewInt(1000), - 1, - B256{1}, - Bytecode{Kind: LegacyRawKind, LegacyRaw: []byte{1, 2, 3}}, - ), - Storage: make(EvmStorage), - Status: Created, - } - - // Add some storage - key := big.NewInt(1) - account.Storage[key] = EvmStorageSlot{ - OriginalValue: big.NewInt(100), - PresentValue: big.NewInt(100), - IsCold: true, - } - - // Modify storage - slot := account.Storage[key] - slot.PresentValue = big.NewInt(200) - slot.IsCold = false - account.Storage[key] = slot - - // Verify all changes - if account.Storage[key].PresentValue.Cmp(big.NewInt(200)) != 0 { - t.Error("Storage modification failed") - } - if account.Storage[key].OriginalValue.Cmp(big.NewInt(100)) != 0 { - t.Error("Original value should not change") - } - if account.Storage[key].IsCold { - t.Error("IsCold should be false") - } - }) - - t.Run("account status transitions", func(t *testing.T) { - account := Account{Status: Created} - - // Test status transitions - transitions := []AccountStatus{ - Touched, - SelfDestructed, - Cold, - } - - for _, newStatus := range transitions { - account.Status = newStatus - if account.Status != newStatus { - t.Errorf("Failed to transition to status %v", newStatus) - } - } - }) +// Comparison function for EofBody struct +func compareEofBody(a, b EofBody) bool { + return reflect.DeepEqual(a.TypesSection, b.TypesSection) && + reflect.DeepEqual(a.CodeSection, b.CodeSection) && + reflect.DeepEqual(a.ContainerSection, b.ContainerSection) && + reflect.DeepEqual(a.DataSection, b.DataSection) && + a.IsDataFilled == b.IsDataFilled } \ No newline at end of file From 818ae8b75f6ead59338bb9c6a23fc94aaa5615c7 Mon Sep 17 00:00:00 2001 From: VeerChaurasia Date: Fri, 1 Nov 2024 19:37:26 +0530 Subject: [PATCH 08/13] Refactored Marshal Unmarshal functions --- execution/evm/account.go | 164 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 execution/evm/account.go diff --git a/execution/evm/account.go b/execution/evm/account.go new file mode 100644 index 0000000..c262cf3 --- /dev/null +++ b/execution/evm/account.go @@ -0,0 +1,164 @@ +package evm + +import ( + "encoding/json" + + "reflect" + "sync" +) + +type Account struct { + Info AccountInfo + Storage EvmStorage + Status AccountStatus +} +type AccountInfo struct { + Balance U256 + Nonce uint64 + CodeHash B256 + Code *Bytecode +} + +func NewAccountInfo(balance U256, nonce uint64, codeHash B256, code Bytecode) AccountInfo { + return AccountInfo{ + Balance: balance, + Nonce: nonce, + CodeHash: codeHash, + Code: &code, + } +} + +type EvmStorage map[U256]EvmStorageSlot +type EvmStorageSlot struct { + OriginalValue U256 + PresentValue U256 + IsCold bool +} +type AccountStatus uint8 + +const ( + Loaded AccountStatus = 0b00000000 // Account is loaded but not interacted with + Created AccountStatus = 0b00000001 // Account is newly created, no DB fetch required + SelfDestructed AccountStatus = 0b00000010 // Account is marked for self-destruction + Touched AccountStatus = 0b00000100 // Account is touched and will be saved to DB + LoadedAsNotExisting AccountStatus = 0b0001000 // Pre-spurious state; account loaded but marked as non-existing + Cold AccountStatus = 0b0010000 // Account is marked as cold (not frequently accessed) +) + +type BytecodeKind int + +const ( + LegacyRawKind BytecodeKind = iota + LegacyAnalyzedKind + EofKind +) + +// BytecodeKind serves as discriminator for Bytecode to identify which variant of enum is being used +// 1 , 2 or 3 in this case +type Bytecode struct { + Kind BytecodeKind + LegacyRaw []byte // For LegacyRaw variant + LegacyAnalyzed *LegacyAnalyzedBytecode // For LegacyAnalyzed variant + Eof *Eof // For Eof variant +} +type LegacyAnalyzedBytecode struct { + Bytecode []byte + OriginalLen uint64 + JumpTable JumpTable +} + +// JumpTable equivalent in Go, using a sync.Once pointer to simulate Arc and BitVec. +type JumpTable struct { + BitVector *Bitvector // Simulating BitVec as []byte + once sync.Once // Lazy initialization if needed +} +type Bitvector struct { + bits []uint8 + size int // Total number of bits represented +} +type Opcode struct { + InitCode Eof `json:"initcode"` + Input Bytes `json:"input"` + CreatedAddress Address `json:"created_address"` +} + +func (o *Opcode) UnmarshalJSON(data []byte) error { + return unmarshalJSON(data, o) +} + +type Eof struct { + Header EofHeader `json:"header"` + Body EofBody `json:"body"` + Raw Bytes `json:"raw"` +} + +func (e *Eof) UnmarshalJSON(data []byte) error { + return unmarshalJSON(data, e) +} + +// EofHeader represents the header of the EOF. +type EofHeader struct { + TypesSize uint16 `json:"types_size"` + CodeSizes []uint16 `json:"code_sizes"` + ContainerSizes []uint16 `json:"container_sizes"` + DataSize uint16 `json:"data_size"` + SumCodeSizes int `json:"sum_code_sizes"` + SumContainerSizes int `json:"sum_container_sizes"` +} + +func (e *EofHeader) UnmarshalJSON(data []byte) error { + return unmarshalJSON(data, e) +} + +// Marshaler interface for custom marshaling +type Marshaler interface { + MarshalType(val reflect.Value, b []byte, lastWrittenIdx uint64) (nextIdx uint64, err error) +} + +// Unmarshaler interface for custom unmarshaling +type Unmarshaler interface { + UnmarshalType(target reflect.Value, b []byte, lastReadIdx uint64) (nextIdx uint64, err error) +} + +// Implement Marshaler for EOFCreateInputs +func (eci EOFCreateInputs) MarshalType(val reflect.Value, b []byte, lastWrittenIdx uint64) (nextIdx uint64, err error) { + jsonData, err := json.Marshal(eci) + if err != nil { + return lastWrittenIdx, err + } + + return uint64(len(jsonData)), nil +} + +// Implement Unmarshaler for EOFCreateInputs +func (eci *EOFCreateInputs) UnmarshalType(target reflect.Value, b []byte, lastReadIdx uint64) (nextIdx uint64, err error) { + err = json.Unmarshal(b, eci) + if err != nil { + return lastReadIdx, err + } + + return lastReadIdx + uint64(len(b)), nil +} + +type EofBody struct { + TypesSection []TypesSection `json:"types_section"` + CodeSection []Bytes `json:"code_section"` // Using [][]byte for Vec + ContainerSection []Bytes `json:"container_section"` // Using [][]byte for Vec + DataSection Bytes `json:"data_section"` // Using []byte for Bytes + IsDataFilled bool `json:"is_data_filled"` +} + +func (e *EofBody) UnmarshalJSON(data []byte) error { + return unmarshalJSON(data, e) +} + +// TypesSection represents a section describing the types used in the EOF body. +type TypesSection struct { + Inputs uint8 `json:"inputs"` // 1 byte + Outputs uint8 `json:"outputs"` // 1 byte + MaxStackSize uint16 `json:"max_stack_size"` // 2 bytes +} + +func (t *TypesSection) UnmarshalJSON(data []byte) error { + return unmarshalJSON(data, t) +} \ No newline at end of file From f6f7976d07dbf107563df002a630c5c863e4c2fb Mon Sep 17 00:00:00 2001 From: VeerChaurasia Date: Fri, 1 Nov 2024 22:28:48 +0530 Subject: [PATCH 09/13] Refactored call_inputs marshal and unmarshal functions and added test for the same --- execution/evm/call_inputs.go | 170 ++++++++++++ execution/evm/call_inputs_test.go | 440 ++++++++++++++++++++---------- 2 files changed, 472 insertions(+), 138 deletions(-) create mode 100644 execution/evm/call_inputs.go diff --git a/execution/evm/call_inputs.go b/execution/evm/call_inputs.go new file mode 100644 index 0000000..8d664d9 --- /dev/null +++ b/execution/evm/call_inputs.go @@ -0,0 +1,170 @@ +package evm +import( +) +func (c *CallInputs) New(txEnv *TxEnv, gasLimit uint64) *CallInputs { + // Check if the transaction kind is Call and extract the target address. + if txEnv.TransactTo.Type != Call2 { + return nil + } + targetAddress := txEnv.TransactTo.Address + // Create and return the CallInputs instance. + return &CallInputs{ + Input: txEnv.Data, + GasLimit: gasLimit, + TargetAddress: *targetAddress, + BytecodeAddress: *targetAddress, // Set to target_address as in Rust code. + Caller: txEnv.Caller, + Value: CallValue{ValueType: "transfer", Amount: txEnv.Value}, + Scheme: ICall, // Assuming CallScheme.Call is represented by Call. + IsStatic: false, + IsEof: false, + ReturnMemoryOffset: Range{Start: 0, End: 0}, + } +} +func (c *CallInputs) NewBoxed(txEnv *TxEnv, gasLimit uint64) *CallInputs { + return c.New(txEnv, gasLimit) // Returns a pointer or nil, similar to Option> in Rust. +} + +func (e EOFCreateInputs) NewTx(tx *TxEnv, gasLimit uint64) EOFCreateInputs { + return EOFCreateInputs{ + Caller: tx.Caller, + Value: tx.Value, + GasLimit: gasLimit, + Kind: EOFCreateKind{ + Kind: TxK, + Data: tx.Data, + }, + } +} + +func (c *CreateInputs) New(txEnv *TxEnv, gasLimit uint64) *CreateInputs { + if txEnv.TransactTo.Type != Create2 { + return nil + } + return &CreateInputs{ + Caller: txEnv.Caller, + Scheme: CreateScheme{ + SchemeType: SchemeTypeCreate, + }, + Value: txEnv.Value, + InitCode: txEnv.Data, + GasLimit: gasLimit, + } +} + +func (c *CreateInputs) NewBoxed(txEnv *TxEnv, gasLimit uint64) *CreateInputs { + return c.New(txEnv, gasLimit) +} + +type CallValue struct { + ValueType string `json:"value_type"` + Amount U256 `json:"amount,omitempty"` +} + +// Transfer creates a CallValue representing a transfer. +func Transfer(amount U256) CallValue { + return CallValue{ValueType: "transfer", Amount: amount} +} + +// Apparent creates a CallValue representing an apparent value. +func Apparent(amount U256) CallValue { + return CallValue{ValueType: "apparent", Amount: amount} +} + +type CallScheme int +const ( + ICall CallScheme = iota + ICallCode + IDelegateCall + IStaticCall + IExtCall + IExtStaticCall + IExtDelegateCall +) +type CallInputs struct { + Input Bytes `json:"input"` + ReturnMemoryOffset Range `json:"return_memory_offset"` + GasLimit uint64 `json:"gas_limit"` + BytecodeAddress Address `json:"bytecode_address"` + TargetAddress Address `json:"target_address"` + Caller Address `json:"caller"` + Value CallValue `json:"value"` + Scheme CallScheme `json:"scheme"` + IsStatic bool `json:"is_static"` + IsEof bool `json:"is_eof"` +} +func(ci *CallInputs) MarshalJSON()([]byte,error){ + return marshalJSON(ci) +} +func (ci *CallInputs) UnmarshalJSON(data []byte) error { + return unmarshalJSON(data, ci) +} + +// JSON serialization for CallValue +func (cv *CallValue) MarshalJSON() ([]byte, error) { + return marshalJSON(cv) +} + +// JSON deserialization for CallValue +func (cv *CallValue) UnmarshalJSON(data []byte) error { + return unmarshalJSON(data, cv) +} + +type CreateInputs struct { + Caller Address `json:"caller"` + Scheme CreateScheme `json:"scheme"` + Value U256 `json:"value"` + InitCode Bytes `json:"init_code"` + GasLimit uint64 `json:"gas_limit"` +} +func (ci *CreateInputs) MarshalJSON() ([]byte, error) { + return marshalJSON(ci) +} +func (ci *CreateInputs) UnmarshalJSON(data []byte) error { + return unmarshalJSON(data, ci) +} + + +type CreateScheme struct { + SchemeType SchemeType `json:"scheme_type"` // can be "create" or "create2" + Salt *U256 `json:"salt,omitempty"` // salt is optional for Create +} +func (cs *CreateScheme) MarshalJSON() ([]byte, error) { + return marshalJSON(cs) +} +func (cs *CreateScheme) UnmarshalJSON(data []byte) error { + return unmarshalJSON(data, cs) +} + +// SchemeType represents the type of creation scheme +type SchemeType int + +const ( + SchemeTypeCreate SchemeType = iota + SchemeTypeCreate2 +) + +type EOFCreateKindType int + +const ( + TxK EOFCreateKindType = iota + OpcodeK +) + +// / Inputs for EOF create call. +type EOFCreateInputs struct { + Caller Address `json:"caller"` + Value U256 `json:"value"` + GasLimit uint64 `json:"gas_limit"` + Kind EOFCreateKind `json:"kind"` +} + +type EOFCreateKind struct { + Kind EOFCreateKindType + Data interface{} // Use an interface to hold the associated data (Tx or Opcode) +} + +// TxKind represents a transaction-based EOF create kind. +type Tx struct { + InitData Bytes `json:"initdata"` +} diff --git a/execution/evm/call_inputs_test.go b/execution/evm/call_inputs_test.go index 528f382..16e6132 100644 --- a/execution/evm/call_inputs_test.go +++ b/execution/evm/call_inputs_test.go @@ -2,187 +2,351 @@ package evm import ( "encoding/json" - "testing" "math/big" - "github.com/stretchr/testify/assert" + "reflect" + "fmt" + "testing" ) -func TestCallInputsNew(t *testing.T) { - // Test cases for CallInputs.New - tests := []struct { - name string - txEnv *TxEnv - gasLimit uint64 - want *CallInputs - }{ +type TestCase1 struct { + name string + input string + expected CallInputs + hasError bool +} + +func TestCallInputsJSON(t *testing.T) { + testCases := []TestCase1{ { - name: "valid call transaction", - txEnv: &TxEnv{ - TransactTo: TransactTo{ - Type: Call2, - Address: &Address{Addr: [20]byte{0x1a, 0x2b, 0x3c, 0x4d, 0x5e}}, + name: "Valid CallInputs", + input: `{ + "input": "0x1234", + "return_memory_offset": {"start": 0, "end": 32}, + "gas_limit": "0x186a0", + "bytecode_address": "0x1234567890abcdef1234567890abcdef12345678", + "target_address": "0x1234567890abcdef1234567890abcdef12345678", + "caller": "0x9876543210fedcba9876543210fedcba98765432", + "value": {"value_type": "transfer", "amount": "0xde0b6b3a7640000"}, + "scheme": "0x00", + "is_static": false, + "is_eof": false + }`, + + expected: CallInputs{ + Input: hexToBytes("0x1234"), + ReturnMemoryOffset: Range{Start: 0, End: 32}, + GasLimit: 100000, + BytecodeAddress: Address{Addr: parseHexAddress("0x1234567890abcdef1234567890abcdef12345678")}, + TargetAddress: Address{Addr: parseHexAddress("0x1234567890abcdef1234567890abcdef12345678")}, + Caller: Address{Addr: parseHexAddress("0x9876543210fedcba9876543210fedcba98765432")}, + Value: CallValue{ + ValueType: "transfer", + Amount: parseU256("0xde0b6b3a7640000"), }, - Data: Bytes{1, 2, 3}, - Caller: Address{Addr: [20]byte{0x1a, 0x2b, 0x3c, 0x4d, 0x5e}}, - Value: U256(big.NewInt(123)), + Scheme: ICall, + IsStatic: false, + IsEof: false, }, - gasLimit: 1000, - want: &CallInputs{ - Input: Bytes{1, 2, 3}, - GasLimit: 1000, - TargetAddress: Address{Addr: [20]byte{0x1a, 0x2b, 0x3c, 0x4d, 0x5e}}, - BytecodeAddress: Address{Addr: [20]byte{0x1a, 0x2b, 0x3c, 0x4d, 0x5e}}, - Caller: Address{Addr: [20]byte{0x1a, 0x2b, 0x3c, 0x4d, 0x5e}}, - Value: CallValue{ValueType: "transfer", Amount: U256(big.NewInt(123))}, - Scheme: ICall, - IsStatic: false, - IsEof: false, - ReturnMemoryOffset: Range{Start: 0, End: 0}, + hasError: false, + }, + { + name: "Invalid CallInputs", + input: `{ + "input": "not_a_hex", + "gas_limit": "not_a_number" + }`, + expected: CallInputs{}, + hasError: true, + }, + + + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + var callInputs CallInputs + err := json.Unmarshal([]byte(tc.input), &callInputs) + + if (err != nil) != tc.hasError { + t.Errorf("expected error: %v, got: %v", tc.hasError, err) + } + if !tc.hasError { + expected := tc.expected + if !compareCallInputs(&callInputs, &expected) { + t.Errorf("expected: %+v, got: %+v", expected, callInputs) + } + } + }) + + } +} + + +type TestCase2 struct { + name string + input string + expected CallValue + hasError bool +} + +func TestCallValueJSON(t *testing.T) { + testCases := []TestCase2{ + { + name: "Valid Transfer CallValue", + input: `{ + "value_type": "transfer", + "amount": "0xde0b6b3a7640000" + }`, + expected: CallValue{ + ValueType: "transfer", + Amount: parseU256("0xde0b6b3a7640000"), }, + hasError: false, }, { - name: "non-call transaction", - txEnv: &TxEnv{ - TransactTo: TransactTo{ - Type: Create2, - }, + name: "Valid Apparent CallValue", + input: `{ + "value_type": "apparent", + "amount": "0x6f05b59d3b20000" + }`, + expected: CallValue{ + ValueType: "apparent", + Amount: parseU256("0x6f05b59d3b20000"), }, - gasLimit: 1000, - want: nil, + hasError: false, }, + } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - ci := &CallInputs{} - got := ci.New(tt.txEnv, tt.gasLimit) - assert.Equal(t, tt.want, got) + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + var callValue CallValue + err := json.Unmarshal([]byte(tc.input), &callValue) + + if (err != nil) != tc.hasError { + t.Errorf("expected error: %v, got: %v", tc.hasError, err) + } + if !tc.hasError { + expected := tc.expected + if !compareCallValues(&callValue, &expected) { + t.Errorf("expected: %+v, got: %+v", expected, callValue) + } + } }) } } -func TestCreateInputsNew(t *testing.T) { - tests := []struct { - name string - txEnv *TxEnv - gasLimit uint64 - want *CreateInputs - }{ +type TestCase3 struct { + name string + input string + expected CreateInputs + hasError bool +} + +func TestCreateInputsJSON(t *testing.T) { + testCases := []TestCase3{ { - name: "valid create transaction", - txEnv: &TxEnv{ - TransactTo: TransactTo{ - Type: Create2, + name: "Valid CreateInputs", + input: `{ + "caller": "0x1234567890abcdef1234567890abcdef12345678", + "scheme": { + "scheme_type": "0x00", + "salt": "0x1234567890abcdef1234567890abcdef12345678" }, - Data: Bytes{1, 2, 3}, - Caller: Address{Addr: [20]byte{0x1a, 0x2b, 0x3c, 0x4d, 0x5e}}, - Value: U256(big.NewInt(123)), - }, - gasLimit: 1000, - want: &CreateInputs{ - Caller: Address{Addr: [20]byte{0x1a, 0x2b, 0x3c, 0x4d, 0x5e}}, + "value": "0xde0b6b3a7640000", + "init_code": "0x010203", + "gas_limit": "0x186a0" + }`, + expected: CreateInputs{ + Caller: Address{Addr: parseHexAddress("0x1234567890abcdef1234567890abcdef12345678")}, Scheme: CreateScheme{ SchemeType: SchemeTypeCreate, + Salt: parsePtrU256("0x1234567890abcdef1234567890abcdef12345678"), }, - Value: U256(big.NewInt(123)), - InitCode: Bytes{1, 2, 3}, - GasLimit: 1000, + Value: parseU256("0xde0b6b3a7640000"), + InitCode: hexToBytes("0x010203"), + GasLimit: 100000, }, + hasError: false, }, { - name: "non-create transaction", - txEnv: &TxEnv{ - TransactTo: TransactTo{ - Type: Call2, - }, - }, - gasLimit: 1000, - want: nil, + name: "Invalid CreateInputs", + input: `{ + "caller": "not_an_address", + "scheme": { + "scheme_type": "invalid" + } + }`, + expected: CreateInputs{}, + hasError: true, }, } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - ci := &CreateInputs{} - got := ci.New(tt.txEnv, tt.gasLimit) - assert.Equal(t, tt.want, got) + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + var createInputs CreateInputs + err := json.Unmarshal([]byte(tc.input), &createInputs) + + if (err != nil) != tc.hasError { + t.Errorf("expected error: %v, got: %v", tc.hasError, err) + } + if !tc.hasError { + expected := tc.expected + if !compareCreateInputs(&createInputs, &expected) { + t.Errorf("expected: %+v, got: %+v", expected, createInputs) + } + } }) } } -func TestEOFCreateInputsNewTx(t *testing.T) { - tx := &TxEnv{ - Caller: Address{Addr: [20]byte{0x1a, 0x2b, 0x3c, 0x4d, 0x5e}}, - Value: U256(big.NewInt(456)), - Data: Bytes{4, 5, 6}, - } - gasLimit := uint64(2000) - - want := EOFCreateInputs{ - Caller: Address{Addr: [20]byte{0x1a, 0x2b, 0x3c, 0x4d, 0x5e}}, - Value: U256(big.NewInt(456)), - GasLimit: 2000, - Kind: EOFCreateKind{ - Kind: TxK, - Data: Bytes{4, 5, 6}, - }, - } - - got := EOFCreateInputs{}.NewTx(tx, gasLimit) - assert.Equal(t, want, got) +type TestCase4 struct { + name string + input string + expected CreateScheme + hasError bool } -func TestCallValueJSON(t *testing.T) { - tests := []struct { - name string - callValue CallValue - wantJSON string - }{ +func TestCreateSchemeJSON(t *testing.T) { + testCases := []TestCase4{ { - name: "transfer value", - callValue: CallValue{ - ValueType: "transfer", - Amount: U256(big.NewInt(123)), + name: "Valid Create Scheme", + input: `{ + "scheme_type": "0x00" + }`, + expected: CreateScheme{ + SchemeType: SchemeTypeCreate, + Salt: nil, }, - wantJSON: `{"value_type":"transfer","amount":123}`, + hasError: false, }, { - name: "apparent value", - callValue: CallValue{ - ValueType: "apparent", - Amount: U256(big.NewInt(456)), + name: "Valid Create2 Scheme", + input: `{ + "scheme_type": "0x01", + "salt": "0x1234567890abcdef1234567890abcdef12345678" + }`, + expected: CreateScheme{ + SchemeType: SchemeTypeCreate2, + Salt: parsePtrU256("0x1234567890abcdef1234567890abcdef12345678"), }, - wantJSON: `{"value_type":"apparent","amount":456}`, + hasError: false, + }, + { + name: "Invalid CreateScheme", + input: `{ + "scheme_type": "invalid" + }`, + expected: CreateScheme{}, + hasError: true, }, } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - bytes, err := json.Marshal(tt.callValue) - assert.NoError(t, err) - assert.JSONEq(t, tt.wantJSON, string(bytes)) - - // Test unmarshaling - var decoded CallValue - err = json.Unmarshal(bytes, &decoded) - assert.NoError(t, err) - assert.Equal(t, tt.callValue, decoded) + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + var createScheme CreateScheme + err := json.Unmarshal([]byte(tc.input), &createScheme) + + if (err != nil) != tc.hasError { + t.Errorf("expected error: %v, got: %v", tc.hasError, err) + } + if !tc.hasError { + expected := tc.expected + if !compareCreateSchemes(&createScheme, &expected) { + t.Errorf("expected: %+v, got: %+v", expected, createScheme) + } + } }) } } -func TestCallValueHelperFunctions(t *testing.T) { - amount := U256(big.NewInt(789)) - - transferValue := Transfer(amount) - assert.Equal(t, CallValue{ - ValueType: "transfer", - Amount: amount, - }, transferValue) - - apparentValue := Apparent(amount) - assert.Equal(t, CallValue{ - ValueType: "apparent", - Amount: amount, - }, apparentValue) -} \ No newline at end of file +// Helper functions from the original test file +// func hexToBytes(hexStr string) []byte { +// bytes, err := hex.DecodeString(hexStr[2:]) +// if err != nil { +// return nil +// } +// return bytes +// } + +// func parseHexAddress(hexStr string) [20]byte { +// var addr [20]byte +// bytes, _ := hex.DecodeString(hexStr[2:]) +// copy(addr[:], bytes) +// return addr +// } + +func parseUint64(hexStr string) (uint64, error) { + value, ok := new(big.Int).SetString(hexStr[2:], 16) + if !ok { + return 0, fmt.Errorf("invalid hex string: %s", hexStr) + } + return value.Uint64(), nil +} + +func parseU256(hexStr string) U256 { + value, ok := new(big.Int).SetString(hexStr[2:], 16) + if !ok { + return U256(new(big.Int)) // Return a zero value or another default + } + return U256(value) +} + + + + + +func parsePtrU256(hexStr string) *U256 { + value, _ := new(big.Int).SetString(hexStr[2:], 16) + u256 := U256(value) + return &u256 +} + +// Comparison functions for our structs +func compareCallInputs(a, b *CallInputs) bool { + return reflect.DeepEqual(a.Input, b.Input) && + reflect.DeepEqual(a.ReturnMemoryOffset, b.ReturnMemoryOffset) && + a.GasLimit == b.GasLimit && + a.BytecodeAddress == b.BytecodeAddress && + a.TargetAddress == b.TargetAddress && + a.Caller == b.Caller && + compareCallValues(&a.Value, &b.Value) && + a.Scheme == b.Scheme && + a.IsStatic == b.IsStatic && + a.IsEof == b.IsEof +} + +func compareCallValues(a, b *CallValue) bool { + return a.ValueType == b.ValueType && + reflect.DeepEqual(a.Amount, b.Amount) +} + +func compareCreateInputs(a, b *CreateInputs) bool { + return a.Caller == b.Caller && + compareCreateSchemes(&a.Scheme, &b.Scheme) && + reflect.DeepEqual(a.Value, b.Value) && + reflect.DeepEqual(a.InitCode, b.InitCode) && + a.GasLimit == b.GasLimit +} + + + +// Comparison function for CreateSchemes +func compareCreateSchemes(a, b *CreateScheme) bool { + // Compare SchemeType + if a.SchemeType != b.SchemeType { + return false + } + + // Special handling for nil/non-nil salt + if (a.Salt == nil) != (b.Salt == nil) { + return false + } + + // If both salts are non-nil, compare their values + if a.Salt != nil && b.Salt != nil { + return (*a.Salt).Cmp(*b.Salt) == 0 + } + + return true +} From 20f6c486ee3bdeab668b5c405404d824b0d5b704 Mon Sep 17 00:00:00 2001 From: VeerChaurasia Date: Fri, 1 Nov 2024 22:29:18 +0530 Subject: [PATCH 10/13] Refactored env.go marshal and unmarshal functions and added test for the same --- execution/evm/env.go | 184 ++++++++++++ execution/evm/env_test.go | 588 +++++++++++++++++++++++++++++++++++++- 2 files changed, 760 insertions(+), 12 deletions(-) create mode 100644 execution/evm/env.go diff --git a/execution/evm/env.go b/execution/evm/env.go new file mode 100644 index 0000000..d1ef06e --- /dev/null +++ b/execution/evm/env.go @@ -0,0 +1,184 @@ +package evm + +import ( + // "encoding/json" + "fmt" + "math/big" +) +type Env struct{ + Cfg CfgEnv + Block BlockEnv + Tx TxEnv +} +func NewEnv() *Env { + return &Env{} +} + +type AccessListItem struct { + Address Address + StorageKeys []B256 +} +type Signature struct { + V uint8 // Recovery ID + R *big.Int // R component of signature + S *big.Int // S component of signature +} +type AuthorizationListType int +const ( + Signed AuthorizationListType = iota + Recovered +) +type AuthorizationList struct { + Type AuthorizationListType + SignedAuthorizations []SignedAuthorization + RecoveredAuthorizations []RecoveredAuthorization +} +type ChainID uint64 +type OptionalNonce struct { + Nonce *uint64 // nil if no nonce is provided +} +type Authorization struct { + ChainID ChainID // The chain ID of the authorization + Address Address // The address of the authorization + Nonce OptionalNonce // The nonce for the authorization +} +type SignedAuthorization struct { + Inner Authorization // Embedded authorization data + Signature Signature // Signature associated with authorization +} + +type RecoveredAuthorization struct { + Inner Authorization // Embedded authorization data + Authority *Address // Optional authority address +} +type TransactTo = TxKind; +type TxKind struct { + Type TxKindType + Address *Address // Address is nil for Create type +} +type TxKindType int +const ( + Create2 TxKindType = iota + Call2 +) +type CfgEnv struct { + ChainID uint64 `json:"chain_id"` + KzgSettings *EnvKzgSettings `json:"kzg_settings,omitempty"` + PerfAnalyseCreatedBytecodes AnalysisKind `json:"perf_analyse_created_bytecodes"` + LimitContractCodeSize *uint64 `json:"limit_contract_code_size,omitempty"` + MemoryLimit uint64 `json:"memory_limit,omitempty"` // Consider using a pointer if optional + DisableBalanceCheck bool `json:"disable_balance_check"` + DisableBlockGasLimit bool `json:"disable_block_gas_limit"` + DisableEIP3607 bool `json:"disable_eip3607"` + DisableGasRefund bool `json:"disable_gas_refund"` + DisableBaseFee bool `json:"disable_base_fee"` + DisableBeneficiaryReward bool `json:"disable_beneficiary_reward"` +} +type BlockEnv struct { + Number U256 `json:"number"` + Coinbase Address `json:"coinbase"` + Timestamp U256 `json:"timestamp"` + GasLimit U256 `json:"gas_limit"` + BaseFee U256 `json:"basefee"` + Difficulty U256 `json:"difficulty"` + Prevrandao *B256 `json:"prevrandao,omitempty"` + BlobExcessGasAndPrice *BlobExcessGasAndPrice `json:"blob_excess_gas_and_price,omitempty"` +} +type BlobExcessGasAndPrice struct { + ExcessGas uint64 `json:"excess_gas"` + BlobGasPrice uint64 `json:"blob_gas_price"` +} +type EnvKzgSettings struct { + Mode string `json:"mode"` + Custom *KzgSettings `json:"custom,omitempty"` +} +func (ce *CfgEnv) MarshalJSON() ([]byte, error) { + return marshalJSON(ce) +} +func (ce *CfgEnv) UnmarshalJSON(data []byte) error { + return unmarshalJSON(data,ce) +} +func (be *BlockEnv) MarshalJSON() ([]byte, error) { + return marshalJSON(be) +} +func (be *BlockEnv) UnmarshalJSON(data []byte) error { + return unmarshalJSON(data,be) +} +func (b *BlobExcessGasAndPrice) MarshalJSON() ([]byte, error) { + return marshalJSON(b) +} + +// UnmarshalJSON customizes the JSON deserialization of InstructionResult. +func (b *BlobExcessGasAndPrice) UnmarshalJSON(data []byte) error { + return unmarshalJSON(data,b) +} +func (eks *EnvKzgSettings) MarshalJSON() ([]byte, error) { + return marshalJSON(eks) +} + +// UnmarshalJSON customizes the JSON deserialization of InstructionResult. +func (eks *EnvKzgSettings) UnmarshalJSON(data []byte) error { + return unmarshalJSON(data,eks) +} +// KzgSettings represents custom KZG settings. +type KzgSettings struct { + // Define fields for KzgSettings based on your requirements. +} +// AnalysisKind represents the type of analysis for created bytecodes. +type AnalysisKind int +const ( + Raw AnalysisKind = iota + Analyse +) + +// MarshalJSON customizes the JSON marshalling for AnalysisKind. + + + +//Not in use : To be verified +func NewSignature(v uint8, r, s *big.Int) *Signature { + return &Signature{ + V: v, + R: new(big.Int).Set(r), + S: new(big.Int).Set(s), + } +} +//Not in use : To be verified +func (s *Signature) ToRawSignature() []byte { + rBytes := s.R.Bytes() + sBytes := s.S.Bytes() + signature := make([]byte, 64) + copy(signature[32-len(rBytes):32], rBytes) + copy(signature[64-len(sBytes):], sBytes) + return signature +} +//Not in use : To be verified +func FromRawSignature(data []byte, v uint8) (*Signature, error) { + if len(data) != 64 { + return nil, fmt.Errorf("invalid signature length: got %d, want 64", len(data)) + } + + r := new(big.Int).SetBytes(data[:32]) + s := new(big.Int).SetBytes(data[32:]) + + return &Signature{ + V: v, + R: r, + S: s, + }, nil +} + +// Verify verifies the signature against a message hash and public key +/* +func (s *Signature) Verify(pubKey *ecdsa.PublicKey, hash []byte) bool { + // Check if r and s are in the valid range + if s.R.Sign() <= 0 || s.S.Sign() <= 0 { + return false + } + if s.R.Cmp(pubKey.Params().N) >= 0 || s.S.Cmp(pubKey.Params().N) >= 0 { + return false + } + + // Verify the signature + return ecdsa.Verify(pubKey, hash, s.R, s.S) +}*/ diff --git a/execution/evm/env_test.go b/execution/evm/env_test.go index 021bf62..ee201d2 100644 --- a/execution/evm/env_test.go +++ b/execution/evm/env_test.go @@ -1,8 +1,12 @@ package evm import ( + "encoding/json" + "fmt" "math/big" + "reflect" "testing" + "github.com/stretchr/testify/assert" ) @@ -48,9 +52,9 @@ func TestFromRawSignature(t *testing.T) { func TestCfgEnvStruct(t *testing.T) { cfg := CfgEnv{ - ChainID: 1234, + ChainID: 1234, DisableBaseFee: true, - MemoryLimit: 2048, + MemoryLimit: 2048, } assert.Equal(t, uint64(1234), cfg.ChainID) assert.True(t, cfg.DisableBaseFee) @@ -67,7 +71,7 @@ func TestOptionalNonce(t *testing.T) { var nonceValue uint64 = 42 nonce := OptionalNonce{Nonce: &nonceValue} assert.Equal(t, uint64(42), *nonce.Nonce) - + nonce = OptionalNonce{} assert.Nil(t, nonce.Nonce) } @@ -77,7 +81,7 @@ func TestAuthorizationStructs(t *testing.T) { auth := Authorization{ ChainID: chainID, Address: Address{Addr: [20]byte{0x1a, 0x2b, 0x3c, 0x4d, 0x5e}}, - Nonce: OptionalNonce{}, + Nonce: OptionalNonce{}, } assert.Equal(t, chainID, auth.ChainID) assert.Equal(t, Address{Addr: [20]byte{0x1a, 0x2b, 0x3c, 0x4d, 0x5e}}, auth.Address) @@ -93,7 +97,7 @@ func TestAuthorizationStructs(t *testing.T) { func TestBlobExcessGasAndPrice(t *testing.T) { blob := BlobExcessGasAndPrice{ - ExcessGas: 1000, + ExcessGas: 1000, BlobGasPrice: 2000, } assert.Equal(t, uint64(1000), blob.ExcessGas) @@ -102,15 +106,15 @@ func TestBlobExcessGasAndPrice(t *testing.T) { func TestBlockEnvStruct(t *testing.T) { blockEnv := BlockEnv{ - Number: U256(big.NewInt(1234)), - Timestamp: U256(big.NewInt(5678)), - Coinbase: Address{Addr: [20]byte{0x1a, 0x2b, 0x3c, 0x4d, 0x5e}}, - GasLimit: U256(big.NewInt(1234)), - BaseFee: U256(big.NewInt(1234)), + Number: U256(big.NewInt(1234)), + Timestamp: U256(big.NewInt(5678)), + Coinbase: Address{Addr: [20]byte{0x1a, 0x2b, 0x3c, 0x4d, 0x5e}}, + GasLimit: U256(big.NewInt(1234)), + BaseFee: U256(big.NewInt(1234)), Difficulty: U256(big.NewInt(1234)), Prevrandao: &B256{0xaa, 0xbb, 0xcc, 0xdd, 0xee}, BlobExcessGasAndPrice: &BlobExcessGasAndPrice{ - ExcessGas: 90, + ExcessGas: 90, BlobGasPrice: 80, }, } @@ -125,10 +129,570 @@ func TestBlockEnvStruct(t *testing.T) { func TestAccessListItem(t *testing.T) { accessList := AccessListItem{ - Address: Address{Addr: [20]byte{0x1a, 0x2b, 0x3c, 0x4d, 0x5e}}, + Address: Address{Addr: [20]byte{0x1a, 0x2b, 0x3c, 0x4d, 0x5e}}, StorageKeys: []B256{{0xaa, 0xbb, 0xcc, 0xdd, 0xee}}, } assert.Equal(t, Address{Addr: [20]byte{0x1a, 0x2b, 0x3c, 0x4d, 0x5e}}, accessList.Address) assert.Equal(t, []B256{{0xaa, 0xbb, 0xcc, 0xdd, 0xee}}, accessList.StorageKeys) } +// type CfgEnvTestCase struct { +// name string +// input CfgEnv +// expected string +// hasError bool +// } + +// func TestCfgEnvMarshalJSON(t *testing.T) { +// var limit uint64 = 1024 +// testCases := []CfgEnvTestCase{ +// { +// name: "Valid CfgEnv", +// input: CfgEnv{ +// ChainID: 1, +// KzgSettings: nil, +// PerfAnalyseCreatedBytecodes: AnalysisKind(20), +// LimitContractCodeSize: &limit, // Ensure this is set to a non-nil pointer +// MemoryLimit: 1024, +// DisableBalanceCheck: true, +// DisableBlockGasLimit: false, // Ensure this is included +// DisableEIP3607: false, // Ensure this is included +// DisableGasRefund: true, +// DisableBaseFee: false, // Ensure this is included +// DisableBeneficiaryReward: true, +// }, +// expected: `{"chain_id":1,"perf_analyse_created_bytecodes":20,"limit_contract_code_size":1024,"memory_limit":1024,"disable_balance_check":true,"disable_block_gas_limit":false,"disable_eip3607":false,"disable_gas_refund":true,"disable_base_fee":false,"disable_beneficiary_reward":true}`, +// hasError: false, +// }, +// } + +// for _, tc := range testCases { +// t.Run(tc.name, func(t *testing.T) { +// data, err := json.Marshal(tc.input) +// if (err != nil) != tc.hasError { +// t.Errorf("expected error: %v, got: %v", tc.hasError, err) +// } +// fmt.Printf("Marshalled data: %s\n", string(data)) // Debug line +// if !tc.hasError && string(data) != tc.expected { +// t.Errorf("expected: %s, got: %s", tc.expected, string(data)) +// } +// }) +// } +// } + +// func TestCfgEnvUnmarshalJSON(t *testing.T) { +// var limit uint64 = 1024 +// testCases := []CfgEnvTestCase{ +// { +// name: "Valid JSON", +// input: CfgEnv{ +// ChainID: 1, +// KzgSettings: nil, +// PerfAnalyseCreatedBytecodes: AnalysisKind(20), +// LimitContractCodeSize: &limit, // Ensure this is set to a non-nil pointer +// MemoryLimit: 1024, +// DisableBalanceCheck: true, +// DisableBlockGasLimit: false, // Ensure this is included +// DisableEIP3607: false, // Ensure this is included +// DisableGasRefund: true, +// DisableBaseFee: false, // Ensure this is included +// DisableBeneficiaryReward: true, +// }, +// expected: `{"chain_id":"0x1","perf_analyse_created_bytecodes":"0x14","limit_contract_code_size":"0x400","memory_limit":"0x400","disable_balance_check":true,"disable_block_gas_limit":false,"disable_eip3607":false,"disable_gas_refund":true,"disable_base_fee":false,"disable_beneficiary_reward":true}`, +// hasError: false, +// }, +// } + +// for _, tc := range testCases { +// t.Run(tc.name, func(t *testing.T) { +// var ce CfgEnv +// data := []byte(tc.expected) + +// err := json.Unmarshal(data, &ce) +// if (err != nil) != tc.hasError { +// t.Errorf("expected error: %v, got: %v", tc.hasError, err) +// } +// if !tc.hasError && !reflect.DeepEqual(ce, tc.input) { +// t.Errorf("expected: %+v, got: %+v", tc.input, ce) +// } +// }) +// } +// } + +// type BlockEnvTestCase struct { +// name string +// input BlockEnv +// expected string +// hasError bool +// } + +// func TestBlockEnvMarshalJSON(t *testing.T) { +// testCases := []BlockEnvTestCase{ +// { +// name: "Valid BlockEnv", +// input: BlockEnv{ +// Number: parseU256("0xde0b6b3a7640000"), +// Coinbase: Address{Addr: parseHexAddress("0x1234567890abcdef1234567890abcdef12345678")}, // Replace with actual address as needed +// Timestamp: parseU256("0xde0b6b3a7640000"), +// GasLimit: parseU256("0xde0b6b3a7640000"), +// BaseFee: parseU256("0xde0b6b3a7640000"), +// Difficulty: parseU256("0xde0b6b3a7640000"), +// Prevrandao: nil, +// BlobExcessGasAndPrice: nil, +// }, +// expected: `{"number":1000000000000000000,"coinbase":{"Addr":[18,52,86,120,144,171,205,239,18,52,86,120,144,171,205,239,18,52,86,120]},"timestamp":1000000000000000000,"gas_limit":1000000000000000000,"basefee":1000000000000000000,"difficulty":1000000000000000000}`, +// hasError: false, +// }, +// } + +// for _, tc := range testCases { +// t.Run(tc.name, func(t *testing.T) { +// data, err := json.Marshal(tc.input) +// if (err != nil) != tc.hasError { +// t.Errorf("expected error: %v, got: %v", tc.hasError, err) +// } +// if !tc.hasError && string(data) != tc.expected { +// t.Errorf("expected: %s, got: %s", tc.expected, string(data)) +// } +// }) +// } +// } + +// func TestBlockEnvUnmarshalJSON(t *testing.T) { +// testCases := []BlockEnvTestCase{ +// { +// name: "Valid JSON", +// input: BlockEnv{ +// Number: parseU256("0xde0b6b3a7640000"), +// Coinbase: Address{Addr: parseHexAddress("0x1234567890abcdef1234567890abcdef12345678")}, // Replace with actual address as needed +// Timestamp: parseU256("0xde0b6b3a7640000"), +// GasLimit: parseU256("0xde0b6b3a7640000"), +// BaseFee: parseU256("0xde0b6b3a7640000"), +// Difficulty: parseU256("0xde0b6b3a7640000"), +// Prevrandao: nil, +// BlobExcessGasAndPrice: nil, +// }, +// expected: `{"number":"0xde0b6b3a7640000","coinbase":"0x1234567890abcdef1234567890abcdef12345678","timestamp":"0xde0b6b3a7640000","gas_limit":"0xde0b6b3a7640000","basefee":"0xde0b6b3a7640000","difficulty":"0xde0b6b3a7640000"}`, +// hasError: false, +// }, +// } + +// for _, tc := range testCases { +// t.Run(tc.name, func(t *testing.T) { +// var be BlockEnv +// data := []byte(tc.expected) + +// err := json.Unmarshal(data, &be) +// if (err != nil) != tc.hasError { +// t.Errorf("expected error: %v, got: %v", tc.hasError, err) +// } +// if !tc.hasError && !reflect.DeepEqual(be, tc.input) { +// t.Errorf("expected: %+v, got: %+v", tc.input, be) +// } +// }) +// } +// } + +// type BlobExcessGasAndPriceTestCase struct { +// name string +// input BlobExcessGasAndPrice +// expected string +// hasError bool +// } + +// func TestBlobExcessGasAndPriceMarshalJSON(t *testing.T) { +// testCases := []BlobExcessGasAndPriceTestCase{ +// { +// name: "Valid BlobExcessGasAndPrice", +// input: BlobExcessGasAndPrice{ +// ExcessGas: 1, +// BlobGasPrice: 2, +// }, +// expected: `{"excess_gas":1,"blob_gas_price":2}`, +// hasError: false, +// }, +// } +// } +// for _, tc := range testCases { +// t.Run(tc.name, func(t *testing.T) { +// data, err := json.Marshal(tc.input) +// if (err != nil) != tc.hasError { +// t.Errorf("expected error: %v, got: %v", tc.hasError, err) +// } +// if !tc.hasError && string(data) != tc.expected { +// t.Errorf("expected: %s, got: %s", tc.expected, string(data)) +// } +// }) +// } +// } + +// func TestBlobExcessGasAndPriceUnmarshalJSON(t *testing.T) { +// testCases := []BlobExcessGasAndPriceTestCase{ +// { +// name: "Valid JSON", +// input: BlobExcessGasAndPrice{ +// ExcessGas: 1, +// BlobGasPrice: 2, +// }, +// expected: `{"excess_gas":"0x1","blob_gas_price":"0x2"}`, +// hasError: false, +// }, +// } + +// for _, tc := range testCases { +// t.Run(tc.name, func(t *testing.T) { +// var bgp BlobExcessGasAndPrice +// data := []byte(tc.expected) + +// err := json.Unmarshal(data, &bgp) +// if (err != nil) != tc.hasError { +// t.Errorf("expected error: %v, got: %v", tc.hasError, err) +// } +// if !tc.hasError && !reflect.DeepEqual(bgp, tc.input) { +// t.Errorf("expected: %+v, got: %+v", tc.input, bgp) +// } +// }) +// } +// } + +// type EnvKzgSettingsTestCase struct { +// name string +// input EnvKzgSettings +// expected string +// hasError bool +// } + +// func TestEnvKzgSettingsMarshalJSON(t *testing.T) { +// testCases := []EnvKzgSettingsTestCase{ +// { +// name: "Valid EnvKzgSettings", +// input: EnvKzgSettings{ +// Mode: "test", +// Custom: nil, // Populate if you add fields to KzgSettings +// }, +// expected: `{"mode":"test"}`, +// hasError: false, +// }, +// } + +// for _, tc := range testCases { +// t.Run(tc.name, func(t *testing.T) { +// data, err := json.Marshal(tc.input) +// if (err != nil) != tc.hasError { +// t.Errorf("expected error: %v, got: %v", tc.hasError, err) +// } +// if !tc.hasError && string(data) != tc.expected { +// t.Errorf("expected: %s, got: %s", tc.expected, string(data)) +// } +// }) +// } +// } + +// func TestEnvKzgSettingsUnmarshalJSON(t *testing.T) { +// testCases := []EnvKzgSettingsTestCase{ +// { +// name: "Valid JSON", +// input: EnvKzgSettings{ +// Mode: "test", +// Custom: nil, // Populate if you add fields to KzgSettings +// }, +// expected: `{"mode":"test"}`, +// hasError: false, +// }, +// } + +// for _, tc := range testCases { +// t.Run(tc.name, func(t *testing.T) { +// var eks EnvKzgSettings +// data := []byte(tc.expected) + +// err := json.Unmarshal(data, &eks) +// if (err != nil) != tc.hasError { +// t.Errorf("expected error: %v, got: %v", tc.hasError, err) +// } +// if !tc.hasError && !reflect.DeepEqual(eks, tc.input) { +// t.Errorf("expected: %+v, got: %+v", tc.input, eks) +// } +// }) +// } +// } +type CfgEnvTestCase struct { + name string + input string + expected CfgEnv + hasError bool +} + +func TestCfgEnvMarshalJSON(t *testing.T) { + var limit uint64 = 1024 + testCases := []CfgEnvTestCase{ + { + name: "Valid CfgEnv", + input: `{"chain_id":1,"perf_analyse_created_bytecodes":20,"limit_contract_code_size":1024,"memory_limit":1024,"disable_balance_check":true,"disable_block_gas_limit":false,"disable_eip3607":false,"disable_gas_refund":true,"disable_base_fee":false,"disable_beneficiary_reward":true}`, + expected: CfgEnv{ + ChainID: 1, + KzgSettings: nil, + PerfAnalyseCreatedBytecodes: AnalysisKind(20), + LimitContractCodeSize: &limit, // Ensure this is set to a non-nil pointer + MemoryLimit: 1024, + DisableBalanceCheck: true, + DisableBlockGasLimit: false, // Ensure this is included + DisableEIP3607: false, // Ensure this is included + DisableGasRefund: true, + DisableBaseFee: false, // Ensure this is included + DisableBeneficiaryReward: true, + }, + hasError: false, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + data, err := json.Marshal(tc.expected) + if (err != nil) != tc.hasError { + t.Errorf("expected error: %v, got: %v", tc.hasError, err) + } + fmt.Printf("Marshalled data: %s\n", string(data)) // Debug line + if !tc.hasError && string(data) != tc.input { + t.Errorf("expected: %s, got: %s", tc.input, string(data)) + } + }) + } +} + +func TestCfgEnvUnmarshalJSON(t *testing.T) { + var limit uint64 = 1024 + testCases := []CfgEnvTestCase{ + { + name: "Valid JSON", + input: `{"chain_id":"0x1","perf_analyse_created_bytecodes":"0x14","limit_contract_code_size":"0x400","memory_limit":"0x400","disable_balance_check":true,"disable_block_gas_limit":false,"disable_eip3607":false,"disable_gas_refund":true,"disable_base_fee":false,"disable_beneficiary_reward":true}`, + expected: CfgEnv{ + ChainID: 1, + KzgSettings: nil, + PerfAnalyseCreatedBytecodes: AnalysisKind(20), + LimitContractCodeSize: &limit, // Ensure this is set to a non-nil pointer + MemoryLimit: 1024, + DisableBalanceCheck: true, + DisableBlockGasLimit: false, // Ensure this is included + DisableEIP3607: false, // Ensure this is included + DisableGasRefund: true, + DisableBaseFee: false, // Ensure this is included + DisableBeneficiaryReward: true, + }, + hasError: false, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + var ce CfgEnv + data := []byte(tc.input) + + err := json.Unmarshal(data, &ce) + if (err != nil) != tc.hasError { + t.Errorf("expected error: %v, got: %v", tc.hasError, err) + } + if !tc.hasError && !reflect.DeepEqual(ce, tc.expected) { + t.Errorf("expected: %+v, got: %+v", tc.expected, ce) + } + }) + } +} + +type BlockEnvTestCase struct { + name string + input string + expected BlockEnv + hasError bool +} + +func TestBlockEnvMarshalJSON(t *testing.T) { + testCases := []BlockEnvTestCase{ + { + name: "Valid BlockEnv", + input: `{"number":1000000000000000000,"coinbase":{"Addr":[18,52,86,120,144,171,205,239,18,52,86,120,144,171,205,239,18,52,86,120]},"timestamp":1000000000000000000,"gas_limit":1000000000000000000,"basefee":1000000000000000000,"difficulty":1000000000000000000}`, + expected: BlockEnv{ + Number: parseU256("0xde0b6b3a7640000"), + Coinbase: Address{Addr: parseHexAddress("0x1234567890abcdef1234567890abcdef12345678")}, // Replace with actual address as needed + Timestamp: parseU256("0xde0b6b3a7640000"), + GasLimit: parseU256("0xde0b6b3a7640000"), + BaseFee: parseU256("0xde0b6b3a7640000"), + Difficulty: parseU256("0xde0b6b3a7640000"), + Prevrandao: nil, + BlobExcessGasAndPrice: nil, + }, + hasError: false, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + data, err := json.Marshal(tc.expected) + if (err != nil) != tc.hasError { + t.Errorf("expected error: %v, got: %v", tc.hasError, err) + } + if !tc.hasError && string(data) != tc.input { + t.Errorf("expected: %s, got: %s", tc.input, string(data)) + } + }) + } +} + +func TestBlockEnvUnmarshalJSON(t *testing.T) { + testCases := []BlockEnvTestCase{ + { + name: "Valid JSON", + input: `{"number":"0xde0b6b3a7640000","coinbase":"0x1234567890abcdef1234567890abcdef12345678","timestamp":"0xde0b6b3a7640000","gas_limit":"0xde0b6b3a7640000","basefee":"0xde0b6b3a7640000","difficulty":"0xde0b6b3a7640000"}`, + expected: BlockEnv{ + Number: parseU256("0xde0b6b3a7640000"), + Coinbase: Address{Addr: parseHexAddress("0x1234567890abcdef1234567890abcdef12345678")}, // Replace with actual address as needed + Timestamp: parseU256("0xde0b6b3a7640000"), + GasLimit: parseU256("0xde0b6b3a7640000"), + BaseFee: parseU256("0xde0b6b3a7640000"), + Difficulty: parseU256("0xde0b6b3a7640000"), + Prevrandao: nil, + BlobExcessGasAndPrice: nil, + }, + hasError: false, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + var be BlockEnv + data := []byte(tc.input) + + err := json.Unmarshal(data, &be) + if (err != nil) != tc.hasError { + t.Errorf("expected error: %v, got: %v", tc.hasError, err) + } + if !tc.hasError && !reflect.DeepEqual(be, tc.expected) { + t.Errorf("expected: %+v, got: %+v", tc.expected, be) + } + }) + } +} + +type BlobExcessGasAndPriceTestCase struct { + name string + input string + expected BlobExcessGasAndPrice + hasError bool +} + +func TestBlobExcessGasAndPriceMarshalJSON(t *testing.T) { + testCases := []BlobExcessGasAndPriceTestCase{ + { + name: "Valid BlobExcessGasAndPrice", + input: `{"excess_gas":1,"blob_gas_price":2}`, + expected: BlobExcessGasAndPrice{ + ExcessGas: 1, + BlobGasPrice: 2, + }, + hasError: false, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + data, err := json.Marshal(tc.expected) + if (err != nil) != tc.hasError { + t.Errorf("expected error: %v, got: %v", tc.hasError, err) + } + if !tc.hasError && string(data) != tc.input { + t.Errorf("expected: %s, got: %s", tc.input, string(data)) + } + }) + } +} + +func TestBlobExcessGasAndPriceUnmarshalJSON(t *testing.T) { + testCases := []BlobExcessGasAndPriceTestCase{ + { + name: "Valid JSON", + input: `{"excess_gas":"0x1","blob_gas_price":"0x2"}`, + expected: BlobExcessGasAndPrice{ + ExcessGas: 1, + BlobGasPrice: 2, + }, + hasError: false, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + var bgp BlobExcessGasAndPrice + data := []byte(tc.input) + + err := json.Unmarshal(data, &bgp) + if (err != nil) != tc.hasError { + t.Errorf("expected error: %v, got: %v", tc.hasError, err) + } + if !tc.hasError && !reflect.DeepEqual(bgp, tc.expected) { + t.Errorf("expected: %+v, got: %+v", tc.expected, bgp) + } + }) + } +} + +type EnvKzgSettingsTestCase struct { + name string + input string + expected EnvKzgSettings + hasError bool +} + +func TestEnvKzgSettingsMarshalJSON(t *testing.T) { + testCases := []EnvKzgSettingsTestCase{ + { + name: "Valid EnvKzgSettings", + input: `{"mode":"test"}`, + expected: EnvKzgSettings{ + Mode: "test", + Custom: nil, // Populate if you add fields to KzgSettings + }, + hasError: false, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + data, err := json.Marshal(tc.expected) + if (err != nil) != tc.hasError { + t.Errorf("expected error: %v, got: %v", tc.hasError, err) + } + if !tc.hasError && string(data) != tc.input { + t.Errorf("expected: %s, got: %s", tc.input, string(data)) + } + }) + } +} + +func TestEnvKzgSettingsUnmarshalJSON(t *testing.T) { + testCases := []EnvKzgSettingsTestCase{ + { + name: "Valid JSON", + input: `{"mode":"test"}`, + expected: EnvKzgSettings{ + Mode: "test", + Custom: nil, // Populate if you add fields to KzgSettings + }, + hasError: false, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + var eks EnvKzgSettings + data := []byte(tc.input) + + err := json.Unmarshal(data, &eks) + if (err != nil) != tc.hasError { + t.Errorf("expected error: %v, got: %v", tc.hasError, err) + } + if !tc.hasError && !reflect.DeepEqual(eks, tc.expected) { + t.Errorf("expected: %+v, got: %+v", tc.expected, eks) + } + }) + } +} \ No newline at end of file From 31e5e552061fbf2dd3331f4021c480d03872f940 Mon Sep 17 00:00:00 2001 From: VeerChaurasia Date: Fri, 1 Nov 2024 22:30:01 +0530 Subject: [PATCH 11/13] Refactored interpreter.go marshal and unmarshal functions and added test for the same --- execution/evm/interpreter.go | 183 ++++++++++++++++++++++++++++++ execution/evm/interpreter_test.go | 148 +++++++++++++++++++++++- 2 files changed, 328 insertions(+), 3 deletions(-) create mode 100644 execution/evm/interpreter.go diff --git a/execution/evm/interpreter.go b/execution/evm/interpreter.go new file mode 100644 index 0000000..0a419c6 --- /dev/null +++ b/execution/evm/interpreter.go @@ -0,0 +1,183 @@ +package evm +import ( + + "math" +) +type Interpreter struct { + InstructionPointer *byte // Using *byte as equivalent to *const u8 in Rust + Gas Gas + Contract Contract + InstructionResult InstructionResult + Bytecode Bytes + IsEof bool + // Is init flag for eof create + IsEofInit bool + // Shared memory. + // Note: This field is only set while running the interpreter loop. + // Otherwise, it is taken and replaced with empty shared memory. + SharedMemory SharedMemory + // Stack. + Stack Stack + // EOF function stack. + FunctionStack FunctionStack + // The return data buffer for internal calls. + // It has multi usage: + // - It contains the output bytes of call sub call. + // - When this interpreter finishes execution it contains the output bytes of this contract. + ReturnDataBuffer Bytes + // Whether the interpreter is in "staticcall" mode, meaning no state changes can happen. + IsStatic bool + // Actions that the EVM should do. + // Set inside CALL or CREATE instructions and RETURN or REVERT instructions. + // Additionally, those instructions will set InstructionResult to CallOrCreate/Return/Revert so we know the reason. + NextAction InterpreterAction +} +type Gas struct { + Limit uint64 + Remaining uint64 + Refunded int64 +} +type Contract struct { + Input Bytes + Bytecode Bytecode + Hash *B256 + TargetAddress Address + BytecodeAddress *Address + Caller Address + CallValue U256 +} +type InstructionResult uint8 +const ( + // Success codes + Continue InstructionResult = iota // 0x00 + Stop + Return + SelfDestruct + ReturnContract + + // Revert codes + Revert = 0x10 // revert opcode + CallTooDeep + OutOfFunds + CreateInitCodeStartingEF00 + InvalidEOFInitCode + InvalidExtDelegateCallTarget + + // Actions + CallOrCreate = 0x20 + + // Error codes + OutOfGas = 0x50 + MemoryOOG + MemoryLimitOOG + PrecompileOOG + InvalidOperandOOG + OpcodeNotFound + CallNotAllowedInsideStatic + StateChangeDuringStaticCall + InvalidFEOpcode + InvalidJump + NotActivated + StackUnderflow + StackOverflow + OutOfOffset + CreateCollision + OverflowPayment + PrecompileError + NonceOverflow + CreateContractSizeLimit + CreateContractStartingWithEF + CreateInitCodeSizeLimit + FatalExternalError + ReturnContractInNotInitEOF + EOFOpcodeDisabledInLegacy + EOFFunctionStackOverflow + EofAuxDataOverflow + EofAuxDataTooSmall + InvalidEXTCALLTarget +) + +type SharedMemory struct { + Buffer []byte + Checkpoints []int + LastCheckpoint int + MemoryLimit uint64 //although implemented as feature memory_limit but kept p=optional for now +} +func (s *SharedMemory) NewContext() { + newCheckpoint := len(s.Buffer) + s.Checkpoints = append(s.Checkpoints, newCheckpoint) + s.LastCheckpoint = newCheckpoint +} +func (s SharedMemory) New() SharedMemory { + return s.WithCapacity(4 * 1024) +} +func (s SharedMemory) WithCapacity(capacity uint) SharedMemory { + return SharedMemory{ + Buffer: make([]byte, capacity), + Checkpoints: make([]int, 32), + LastCheckpoint: 0, + MemoryLimit: math.MaxUint64, + } +} + +func (s SharedMemory) NewWithMemoryLimit(memoryLimit uint64) SharedMemory { + return SharedMemory{ + Buffer: make([]byte, 4 * 1024), + Checkpoints: make([]int, 32), + LastCheckpoint: 0, + MemoryLimit: memoryLimit, + } +} +func (s *SharedMemory) FreeContext() { + if len(s.Checkpoints) > 0 { + // Pop the last element from Checkpoints + oldCheckpoint := s.Checkpoints[len(s.Checkpoints)-1] + s.Checkpoints = s.Checkpoints[:len(s.Checkpoints)-1] + + // Update LastCheckpoint to the last value in Checkpoints, or 0 if empty + if len(s.Checkpoints) > 0 { + s.LastCheckpoint = s.Checkpoints[len(s.Checkpoints)-1] + } else { + s.LastCheckpoint = 0 // default value if no checkpoints are left + } + + // Safely resize Buffer to the old checkpoint length + if oldCheckpoint < len(s.Buffer) { + s.Buffer = s.Buffer[:oldCheckpoint] + } + } +} +type Stack struct { + Data []U256 +} +type FunctionStack struct { + ReturnStack []FunctionReturnFrame `json:"return_stack"` + CurrentCodeIdx uint64 `json:"current_code_idx"` // Changed to uint64 to match Rust's usize +} + +// MarshalJSON customizes the JSON serialization of FunctionStack. +func (fs *FunctionStack) MarshalJSON() ([]byte, error) { + return marshalJSON(fs) +} + +// UnmarshalJSON customizes the JSON deserialization of InstructionResult. +func (fs *FunctionStack) UnmarshalJSON(data []byte) error { + return unmarshalJSON(data,fs) +} + +// FunctionReturnFrame represents a frame in the function return stack for the EVM interpreter. +type FunctionReturnFrame struct { + // The index of the code container that this frame is executing. + Idx uint64 `json:"idx"` // Changed to uint64 to match Rust's usize + // The program counter where frame execution should continue. + PC uint64 `json:"pc"` // Changed to uint64 to match Rust's usize +} + +func (frf *FunctionReturnFrame) MarshalJSON() ([]byte, error) { + return marshalJSON(frf) +} + +// UnmarshalJSON customizes the JSON deserialization of InstructionResult. +func (frf *FunctionReturnFrame) UnmarshalJSON(data []byte) error { + return unmarshalJSON(data,frf) +} \ No newline at end of file diff --git a/execution/evm/interpreter_test.go b/execution/evm/interpreter_test.go index 061b27a..3e4f833 100644 --- a/execution/evm/interpreter_test.go +++ b/execution/evm/interpreter_test.go @@ -1,8 +1,11 @@ package evm import ( - "testing" + "encoding/json" "math/big" + "reflect" + "testing" + "github.com/stretchr/testify/assert" ) @@ -37,7 +40,6 @@ func TestStack(t *testing.T) { assert.Equal(t, item1, stack.Data[0], "Unexpected stack item after pop") } - func TestFunctionStack(t *testing.T) { fs := FunctionStack{} @@ -167,4 +169,144 @@ func TestGasRefundAndLimit(t *testing.T) { // Check that remaining gas never exceeds limit assert.LessOrEqual(t, gas.Remaining, gas.Limit, "Gas Remaining exceeds limit") -} \ No newline at end of file +} + + + +// Test case structure for FunctionStack +type FunctionStackTestCase struct { + name string + input string // Input as hexadecimal string + expected FunctionStack + hasError bool +} + +func TestFunctionStackMarshalJSON(t *testing.T) { + testCases := []FunctionStackTestCase{ + { + name: "Valid FunctionStack", + input: `{"return_stack":[{"idx":"0x0","pc":"0x1"},{"idx":"0x2","pc":"0x3"}],"current_code_idx":"0x4"}`, + expected: FunctionStack{ + ReturnStack: []FunctionReturnFrame{ + {Idx: 0, PC: 1}, + {Idx: 2, PC: 3}, + }, + CurrentCodeIdx: 4, + }, + hasError: false, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + var fs FunctionStack + data := []byte(tc.input) + + err := json.Unmarshal(data, &fs) + if (err != nil) != tc.hasError { + t.Errorf("expected error: %v, got: %v", tc.hasError, err) + } + if !tc.hasError && !reflect.DeepEqual(fs, tc.expected) { + t.Errorf("expected: %+v, got: %+v", tc.expected, fs) + } + }) + } +} + +func TestFunctionStackUnmarshalJSON(t *testing.T) { + testCases := []FunctionStackTestCase{ + { + name: "Valid JSON", + input: `{"return_stack":[{"idx":"0x0","pc":"0x1"},{"idx":"0x2","pc":"0x3"}],"current_code_idx":"0x4"}`, + expected: FunctionStack{ + ReturnStack: []FunctionReturnFrame{ + {Idx: 0, PC: 1}, + {Idx: 2, PC: 3}, + }, + CurrentCodeIdx: 4, + }, + hasError: false, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + var fs FunctionStack + data := []byte(tc.input) + + err := json.Unmarshal(data, &fs) + if (err != nil) != tc.hasError { + t.Errorf("expected error: %v, got: %v", tc.hasError, err) + } + if !tc.hasError && !reflect.DeepEqual(fs, tc.expected) { + t.Errorf("expected: %+v, got: %+v", tc.expected, fs) + } + }) + } +} + +// Test case structure for FunctionReturnFrame +type FunctionReturnFrameTestCase struct { + name string + input string // Input as hexadecimal string + expected FunctionReturnFrame + hasError bool +} + +func TestFunctionReturnFrameMarshalJSON(t *testing.T) { + testCases := []FunctionReturnFrameTestCase{ + { + name: "Valid FunctionReturnFrame", + input: `{"idx":"0x0","pc":"0x1"}`, + expected: FunctionReturnFrame{ + Idx: 0, + PC: 1, + }, + hasError: false, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + var frf FunctionReturnFrame + data := []byte(tc.input) + + err := json.Unmarshal(data, &frf) + if (err != nil) != tc.hasError { + t.Errorf("expected error: %v, got: %v", tc.hasError, err) + } + if !tc.hasError && !reflect.DeepEqual(frf, tc.expected) { + t.Errorf("expected: %+v, got: %+v", tc.expected, frf) + } + }) + } +} + +func TestFunctionReturnFrameUnmarshalJSON(t *testing.T) { + testCases := []FunctionReturnFrameTestCase{ + { + name: "Valid JSON", + input: `{"idx":"0x0","pc":"0x1"}`, + expected: FunctionReturnFrame{ + Idx: 0, + PC: 1, + }, + hasError: false, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + var frf FunctionReturnFrame + data := []byte(tc.input) + + err := json.Unmarshal(data, &frf) + if (err != nil) != tc.hasError { + t.Errorf("expected error: %v, got: %v", tc.hasError, err) + } + if !tc.hasError && !reflect.DeepEqual(frf, tc.expected) { + t.Errorf("expected: %+v, got: %+v", tc.expected, frf) + } + }) + } +} From 8ac3173abc64ebb739273f3973924b8d018aaba5 Mon Sep 17 00:00:00 2001 From: VeerChaurasia Date: Sat, 2 Nov 2024 10:29:13 +0530 Subject: [PATCH 12/13] Refactored --- execution/evm/account.go | 8 +- execution/evm/account_test.go | 8 +- execution/evm/call_inputs_test.go | 40 +--- execution/evm/evm.go | 225 +++++++++++++++++++++++ execution/evm/interpreter_action.go | 86 +++++++++ execution/evm/interpreter_action_test.go | 2 +- 6 files changed, 327 insertions(+), 42 deletions(-) create mode 100644 execution/evm/evm.go create mode 100644 execution/evm/interpreter_action.go diff --git a/execution/evm/account.go b/execution/evm/account.go index c262cf3..f954cc2 100644 --- a/execution/evm/account.go +++ b/execution/evm/account.go @@ -70,11 +70,11 @@ type LegacyAnalyzedBytecode struct { // JumpTable equivalent in Go, using a sync.Once pointer to simulate Arc and BitVec. type JumpTable struct { BitVector *Bitvector // Simulating BitVec as []byte - once sync.Once // Lazy initialization if needed + Once sync.Once // Lazy initialization if needed } type Bitvector struct { - bits []uint8 - size int // Total number of bits represented + Bits []uint8 + Size int // Total number of bits represented } type Opcode struct { InitCode Eof `json:"initcode"` @@ -161,4 +161,4 @@ type TypesSection struct { func (t *TypesSection) UnmarshalJSON(data []byte) error { return unmarshalJSON(data, t) -} \ No newline at end of file +} diff --git a/execution/evm/account_test.go b/execution/evm/account_test.go index a154d9b..08a9b08 100644 --- a/execution/evm/account_test.go +++ b/execution/evm/account_test.go @@ -121,7 +121,6 @@ func TestOpcodeUnmarshalJSON(t *testing.T) { } } -// Helper function to convert hex string to byte slice func hexToBytes(hexStr string) []byte { bytes, err := hex.DecodeString(hexStr[2:]) if err != nil { @@ -129,16 +128,12 @@ func hexToBytes(hexStr string) []byte { } return bytes } - -// Helper function to parse hex address string to Address struct func parseHexAddress(hexStr string) [20]byte { var addr [20]byte bytes, _ := hex.DecodeString(hexStr[2:]) copy(addr[:], bytes) return addr } - -// Comparison function for Opcode struct func compareOpcodes(a, b Opcode) bool { return compareEof(a.InitCode, b.InitCode) && a.CreatedAddress == b.CreatedAddress && reflect.DeepEqual(a.Input, b.Input) } @@ -148,7 +143,6 @@ func compareEof(a, b Eof) bool { reflect.DeepEqual(a.Raw, b.Raw) } -// Comparison function for EofHeader struct func compareEofHeader(a, b EofHeader) bool { return a.TypesSize == b.TypesSize && reflect.DeepEqual(a.CodeSizes, b.CodeSizes) && @@ -165,4 +159,4 @@ func compareEofBody(a, b EofBody) bool { reflect.DeepEqual(a.ContainerSection, b.ContainerSection) && reflect.DeepEqual(a.DataSection, b.DataSection) && a.IsDataFilled == b.IsDataFilled -} \ No newline at end of file +} diff --git a/execution/evm/call_inputs_test.go b/execution/evm/call_inputs_test.go index 16e6132..e677b72 100644 --- a/execution/evm/call_inputs_test.go +++ b/execution/evm/call_inputs_test.go @@ -4,7 +4,6 @@ import ( "encoding/json" "math/big" "reflect" - "fmt" "testing" ) @@ -31,7 +30,7 @@ func TestCallInputsJSON(t *testing.T) { "is_static": false, "is_eof": false }`, - + expected: CallInputs{ Input: hexToBytes("0x1234"), ReturnMemoryOffset: Range{Start: 0, End: 32}, @@ -41,7 +40,7 @@ func TestCallInputsJSON(t *testing.T) { Caller: Address{Addr: parseHexAddress("0x9876543210fedcba9876543210fedcba98765432")}, Value: CallValue{ ValueType: "transfer", - Amount: parseU256("0xde0b6b3a7640000"), + Amount: parseU256("0xde0b6b3a7640000"), }, Scheme: ICall, IsStatic: false, @@ -58,8 +57,6 @@ func TestCallInputsJSON(t *testing.T) { expected: CallInputs{}, hasError: true, }, - - } for _, tc := range testCases { @@ -77,11 +74,10 @@ func TestCallInputsJSON(t *testing.T) { } } }) - + } } - type TestCase2 struct { name string input string @@ -99,7 +95,7 @@ func TestCallValueJSON(t *testing.T) { }`, expected: CallValue{ ValueType: "transfer", - Amount: parseU256("0xde0b6b3a7640000"), + Amount: parseU256("0xde0b6b3a7640000"), }, hasError: false, }, @@ -111,11 +107,10 @@ func TestCallValueJSON(t *testing.T) { }`, expected: CallValue{ ValueType: "apparent", - Amount: parseU256("0x6f05b59d3b20000"), + Amount: parseU256("0x6f05b59d3b20000"), }, hasError: false, }, - } for _, tc := range testCases { @@ -163,7 +158,7 @@ func TestCreateInputsJSON(t *testing.T) { SchemeType: SchemeTypeCreate, Salt: parsePtrU256("0x1234567890abcdef1234567890abcdef12345678"), }, - Value: parseU256("0xde0b6b3a7640000"), + Value: parseU256("0xde0b6b3a7640000"), InitCode: hexToBytes("0x010203"), GasLimit: 100000, }, @@ -276,26 +271,13 @@ func TestCreateSchemeJSON(t *testing.T) { // return addr // } -func parseUint64(hexStr string) (uint64, error) { - value, ok := new(big.Int).SetString(hexStr[2:], 16) - if !ok { - return 0, fmt.Errorf("invalid hex string: %s", hexStr) - } - return value.Uint64(), nil -} - func parseU256(hexStr string) U256 { - value, ok := new(big.Int).SetString(hexStr[2:], 16) - if !ok { + value, ok := new(big.Int).SetString(hexStr[2:], 16) + if !ok { return U256(new(big.Int)) // Return a zero value or another default - } - return U256(value) + } + return U256(value) } - - - - - func parsePtrU256(hexStr string) *U256 { value, _ := new(big.Int).SetString(hexStr[2:], 16) u256 := U256(value) @@ -329,8 +311,6 @@ func compareCreateInputs(a, b *CreateInputs) bool { a.GasLimit == b.GasLimit } - - // Comparison function for CreateSchemes func compareCreateSchemes(a, b *CreateScheme) bool { // Compare SchemeType diff --git a/execution/evm/evm.go b/execution/evm/evm.go new file mode 100644 index 0000000..41bc799 --- /dev/null +++ b/execution/evm/evm.go @@ -0,0 +1,225 @@ +package evm +import "bytes" +type Evm[EXT interface{}, DB Database] struct { + Context Context[EXT, DB] + Handler Handler[Context[EXT, DB], EXT, DB] +} +func NewEvm[EXT any, DB Database](context Context[EXT, DB], handler Handler[Context[EXT, DB], EXT, DB]) Evm[EXT, DB] { + context.Evm.Inner.JournaledState.SetSpecId(handler.Cfg.specID) + return Evm[EXT, DB]{ + Context: context, + Handler: handler, + } +} +//Not in use +// func (e Evm[EXT, DB]) specId() SpecId { +// return e.Handler.Cfg.specID +// } +func (e Evm[EXT, DB]) IntoContextWithHandlerCfg() ContextWithHandlerCfg[EXT, DB] { + return ContextWithHandlerCfg[EXT, DB]{ + Context: e.Context, + Cfg: e.Handler.Cfg, + } +} +var EOF_MAGIC_BYTES = []byte{0xef, 0x00} +type EVMResult[D any] EVMResultGeneric[ResultAndState, D] +func (e *Evm[EXT, DB]) Transact() EVMResult[DatabaseError] { + initialGasSpend, err := e.preverifyTransactionInner() + if len(err.Message) != 0 { + e.clear() + return EVMResult[DatabaseError]{Err: err} + } + output := e.TransactPreverifiedInner(initialGasSpend) + output2, err := e.Handler.PostExecution.End(&e.Context, EVMResultGeneric[ResultAndState, EvmError](output)) + e.clear() + return EVMResult[DatabaseError]{Err: err, Value: output2} +} +func (e *Evm[EXT, DB]) preverifyTransactionInner() (uint64, EvmError) { + err := e.Handler.Validation.Env(e.Context.Evm.Inner.Env) //? + if err != nil { + return 0, EvmError{Message: err.Error()} + } + initialGasSpend, err := e.Handler.Validation.InitialTxGas(e.Context.Evm.Inner.Env) + if err != nil { + return 0, EvmError{Message: err.Error()} + } + err = e.Handler.Validation.TxAgainstState(&e.Context) + if err != nil { + return 0, EvmError{Message: err.Error()} + } + return initialGasSpend, EvmError{} +} +func (e *Evm[EXT, DB]) clear() { + e.Handler.PostExecution.Clear(&e.Context) +} +func (e *Evm[EXT, DB]) TransactPreverifiedInner(initialGasSpend uint64) EVMResult[DatabaseError] { + specId := e.Handler.Cfg.specID + ctx := &e.Context + preExec := e.Handler.PreExecution + err := preExec.LoadAccounts(ctx) + if err != nil { + return EVMResult[DatabaseError]{Err: err} + } + precompiles := preExec.LoadPrecompilesFunction() + ctx.Evm.SetPrecompiles(precompiles) + err_caller := preExec.DeductCaller(ctx) //? + if err_caller.Err != nil { + return EVMResult[DatabaseError](err_caller) + } + gasLimit := ctx.Evm.Inner.Env.Tx.GasLimit - initialGasSpend + exec := e.Handler.Execution + var firstFrameOrResult FrameOrResult + if ctx.Evm.Inner.Env.Tx.TransactTo.Type == Call2 { + result, err := exec.Call(ctx, (&CallInputs{}).NewBoxed(&ctx.Evm.Inner.Env.Tx, gasLimit)) + if len(err.Message) != 0 { + return EVMResult[DatabaseError]{Err: err} + } + firstFrameOrResult = result + } else { + if specId.IsEnabledIn(PRAGUE_EOF) && bytes.Equal(ctx.Evm.Inner.Env.Tx.Data[:2], EOF_MAGIC_BYTES) { + result, err := exec.EOFCreate(ctx, EOFCreateInputs{}.NewTx(&ctx.Evm.Inner.Env.Tx, gasLimit)) + if len(err.Message) != 0 { + return EVMResult[DatabaseError]{Err: err} + } + firstFrameOrResult = result + } else { + result, err := exec.Create(ctx, (&CreateInputs{}).NewBoxed(&ctx.Evm.Inner.Env.Tx, gasLimit)) + if len(err.Message) != 0 { + return EVMResult[DatabaseError]{Err: err} + } + firstFrameOrResult = result + } + } + var result FrameResult + if firstFrameOrResult.Type == Frame_FrameOrResult { + result_new, err_new := e.RunTheLoop(firstFrameOrResult.Frame) + if len(err_new.Message) != 0 { + return EVMResult[DatabaseError]{Err: err_new} + } + result = result_new + } else { + result = firstFrameOrResult.Result + } + ctx = &e.Context + _= e.Handler.Execution.LastFrameReturn(ctx, &result) + postExec := e.Handler.PostExecution + postExec.ReimburseCaller(ctx, result.Gas()) //! + postExec.RewardBeneficiary(ctx, result.Gas()) //! + res, err := postExec.Output(ctx, result) + return EVMResult[DatabaseError]{Value: res, Err: err} +} +func (e *Evm[EXT, DB]) RunTheLoop(firstFrame Frame) (FrameResult, EvmError) { + callStack := make([]Frame, 0, 1025) + callStack = append(callStack, firstFrame) + + var sharedMemory SharedMemory + if e.Context.Evm.Inner.Env.Cfg.MemoryLimit > 0 { + sharedMemory = SharedMemory{}.NewWithMemoryLimit(e.Context.Evm.Inner.Env.Cfg.MemoryLimit) + } else { + sharedMemory = SharedMemory{}.New() + } + sharedMemory.NewContext() + + stackFrame := &callStack[len(callStack) - 1] + + for { + nextAction, err := e.Handler.ExecuteFrame(stackFrame,&sharedMemory,&e.Context) + if err != nil { + return FrameResult{}, EvmError{Message: err.Error()} + } + // Take error from the context, if any + if err := e.Context.Evm.Inner.TakeError(); len(err.Message) != 0 { + return FrameResult{}, err + } + exec := &e.Handler.Execution + var frameOrResult FrameOrResult + { + if nextAction.actionType == ActionTypeCall { + res, err := exec.Call(&e.Context, nextAction.callInputs) + if len(err.Message) != 0 { + return FrameResult{}, err + } + frameOrResult = res + } else if nextAction.actionType == ActionTypeCreate { + res, err := exec.Create(&e.Context, nextAction.createInputs) + if len(err.Message) != 0 { + return FrameResult{},err + } + frameOrResult = res + } else if nextAction.actionType == ActionTypeEOFCreate { + res, err := exec.EOFCreate(&e.Context, *nextAction.eofCreateInputs) + if len(err.Message) != 0 { + return FrameResult{},err + } + frameOrResult = res + } else if nextAction.actionType == ActionTypeReturn { + sharedMemory.FreeContext() + returnedFrame := callStack[len(callStack) - 1] + callStack = callStack[:len(callStack) - 1] + ctx := &e.Context + + var result FrameResult + frameOrResult2 := FrameOrResult{ + Type: Result_FrameOrResult, + Result: result, + } + switch returnedFrame.Type { + case Call_Frame: + result.ResultType = Call + res, err := exec.CallReturn(ctx, returnedFrame.Call, *nextAction.result) + if len(err.Message) != 0 { + return FrameResult{}, err + } + result.Call = &res + case Create_Frame: + result.ResultType = Create + res, err := exec.CreateReturn(ctx, returnedFrame.Create, *nextAction.result) + if len(err.Message) != 0 { + return FrameResult{}, err + } + result.Create = &res + case EOFCreate_Frame: + result.ResultType = EOFCreate + res, err := exec.EOFCreateReturn(ctx, returnedFrame.EOFCreate, *nextAction.result) + if len(err.Message) != 0 { + return FrameResult{}, err + } + result.Create = &res + } + frameOrResult = frameOrResult2 + } else { + return FrameResult{}, EvmError{ + Message: "InterpreterAction::None is not expected", + } + } + } + switch frameOrResult.Type { + case Frame_FrameOrResult: + sharedMemory.NewContext() + callStack = append(callStack, frameOrResult.Frame) + stackFrame = &callStack[len(callStack) - 1] + case Result_FrameOrResult: + topFrame := &callStack[len(callStack) - 1] + if topFrame == nil { + return frameOrResult.Result, EvmError{} + } + stackFrame = topFrame + ctx := &e.Context + switch frameOrResult.Result.ResultType { + case Call: + if err := exec.InsertCallOutcome(ctx, stackFrame, &sharedMemory, *frameOrResult.Result.Call); err.Message != "" { + return FrameResult{}, EvmError{Message: err.Error()} // handle the error appropriately + } + case Create: + if err := exec.InsertCreateOutcome(ctx, stackFrame, *frameOrResult.Result.Create); err.Message != "" { + return FrameResult{}, EvmError{Message: err.Error()} // handle the error appropriately + } + case EOFCreate: + if err := exec.InsertCreateOutcome(ctx, stackFrame, *frameOrResult.Result.Create); err.Message != "" { + return FrameResult{}, EvmError{Message: err.Error()} // handle the error appropriately + } + + } + } + } +} \ No newline at end of file diff --git a/execution/evm/interpreter_action.go b/execution/evm/interpreter_action.go new file mode 100644 index 0000000..ff8b433 --- /dev/null +++ b/execution/evm/interpreter_action.go @@ -0,0 +1,86 @@ +package evm +import( + +) +type InterpreterAction struct { + // We use a type field to distinguish between different actions + actionType ActionType + callInputs *CallInputs + createInputs *CreateInputs + eofCreateInputs *EOFCreateInputs + result *InterpreterResult +} +type InterpreterResult struct { + // The result of the execution. + Result InstructionResult + Output Bytes + Gas Gas +} +//Check marshal unmarshal remove if not required +// type jsonInterpreterAction struct { +// Type string `json:"type"` +// CallInputs *CallInputs `json:"call_inputs,omitempty"` +// CreateInputs *CreateInputs `json:"create_inputs,omitempty"` +// EOFInputs *EOFCreateInputs `json:"eof_inputs,omitempty"` +// Result *InterpreterResult `json:"result,omitempty"` +// } +// func(ja *jsonInterpreterAction) MarshalJSON()([]byte,error){ +// return marshalJSON(ja) +// } +// // MarshalJSON implements the json.Marshaler interface + +// func (ja *jsonInterpreterAction) UnmarshalJSON(data []byte) error { +// return unmarshalJSON(data,ja) +// } + +// UnmarshalJSON implements the json.Unmarshaler interface + +// ActionType represents the type of interpreter action +type ActionType int + +const ( + ActionTypeNone ActionType = iota + ActionTypeCall + ActionTypeCreate + ActionTypeEOFCreate + ActionTypeReturn +) + +// NewCallAction creates a new Call action +func NewCallAction(inputs *CallInputs) *InterpreterAction { + return &InterpreterAction{ + actionType: ActionTypeCall, + callInputs: inputs, + } +} + +// NewCreateAction creates a new Create action +func NewCreateAction(inputs *CreateInputs) *InterpreterAction { + return &InterpreterAction{ + actionType: ActionTypeCreate, + createInputs: inputs, + } +} + +// NewEOFCreateAction creates a new EOFCreate action +func NewEOFCreateAction(inputs *EOFCreateInputs) *InterpreterAction { + return &InterpreterAction{ + actionType: ActionTypeEOFCreate, + eofCreateInputs: inputs, + } +} + +// NewReturnAction creates a new Return action +func NewReturnAction(result *InterpreterResult) *InterpreterAction { + return &InterpreterAction{ + actionType: ActionTypeReturn, + result: result, + } +} + +// NewNoneAction creates a new None action +func NewNoneAction() *InterpreterAction { + return &InterpreterAction{ + actionType: ActionTypeNone, + } +} diff --git a/execution/evm/interpreter_action_test.go b/execution/evm/interpreter_action_test.go index 2c6a078..19556d7 100644 --- a/execution/evm/interpreter_action_test.go +++ b/execution/evm/interpreter_action_test.go @@ -1,7 +1,7 @@ package evm import ( - // "reflect" + "testing" ) From 20466e4183f7d18ae530cd309d25d9e200a92040 Mon Sep 17 00:00:00 2001 From: VeerChaurasia Date: Mon, 4 Nov 2024 19:38:17 +0530 Subject: [PATCH 13/13] Remove Duplicate Test functions --- execution/evm/evm_context_test.go | 17 --------------- execution/evm/inner_evm_context_test.go | 29 ------------------------- 2 files changed, 46 deletions(-) diff --git a/execution/evm/evm_context_test.go b/execution/evm/evm_context_test.go index c638099..606b3c6 100644 --- a/execution/evm/evm_context_test.go +++ b/execution/evm/evm_context_test.go @@ -84,21 +84,4 @@ func TestWithNewEvmDB(t *testing.T) { } } -// TestSetPrecompiles tests the SetPrecompiles method. -func TestSetPrecompiles(t *testing.T) { - db := &MockDatabase{ - BasicFunc: func(address Address) (AccountInfo, error) { - return AccountInfo{}, nil - }, - } - - evmCtx := NewEvmContext(db) - - precompiles := DefaultContextPrecompiles[*MockDatabase]() - evmCtx.SetPrecompiles(precompiles) - - if evmCtx.Precompiles.Inner.Owned == nil { - t.Error("Expected Precompiles Owned to be initialized, got nil") - } -} diff --git a/execution/evm/inner_evm_context_test.go b/execution/evm/inner_evm_context_test.go index e9dae51..1baccb9 100644 --- a/execution/evm/inner_evm_context_test.go +++ b/execution/evm/inner_evm_context_test.go @@ -3,38 +3,9 @@ package evm import ( "testing" "math/big" - "github.com/stretchr/testify/assert" // "github.com/BlocSoc-iitr/selene/common" ) - -// TestNewJournalState checks if NewJournalState initializes JournaledState correctly. -func TestNewJournalState(t *testing.T) { - spec := DefaultSpecId() - warmPreloadedAddresses := map[Address]struct{}{ - {Addr: [20]byte{0x1}}: {}, - } - journalState := NewJournalState(spec, warmPreloadedAddresses) - - assert.Nil(t, journalState.State, "State should be initialized as nil") - assert.Nil(t, journalState.TransientStorage, "TransientStorage should be initialized as nil") - assert.Equal(t, spec, journalState.Spec, "Spec should be set to the provided SpecId") - assert.Equal(t, warmPreloadedAddresses, journalState.WarmPreloadedAddresses, "WarmPreloadedAddresses should be set correctly") - assert.Equal(t, uint(0), journalState.Depth, "Depth should be initialized to 0") - assert.Empty(t, journalState.Journal, "Journal should be initialized empty") - assert.Empty(t, journalState.Logs, "Logs should be initialized empty") -} - -// TestSetSpecId verifies that the setSpecId method correctly updates the SpecId in JournaledState. -func TestSetSpecId(t *testing.T) { - spec := DefaultSpecId() - journalState := NewJournalState(spec, nil) - - newSpec := SpecId(2) - journalState.setSpecId(newSpec) - assert.Equal(t, newSpec, journalState.Spec, "SpecId should be updated to the new SpecId") -} - // TestInnerEvmContextWithJournalState ensures InnerEvmContext integrates correctly with JournaledState. func TestInnerEvmContextWithJournalState(t *testing.T) { db := NewEmptyDB()