From 30c9f94484ee8d21b4deb6a12530be954bbe2401 Mon Sep 17 00:00:00 2001 From: tk-codes Date: Thu, 25 Apr 2019 18:41:03 +0200 Subject: [PATCH 1/7] Create Struct data type --- vm/struct.go | 14 ++++++++++++++ vm/struct_test.go | 15 +++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 vm/struct.go create mode 100644 vm/struct_test.go diff --git a/vm/struct.go b/vm/struct.go new file mode 100644 index 0000000..495b265 --- /dev/null +++ b/vm/struct.go @@ -0,0 +1,14 @@ +package vm + +// Struct type represents the composite data type declaration that +// defines a group of variables. +type Struct Array + +// NewStruct creates a new struct data structure. +func NewStruct(size int) Struct { + array := NewArray() + for i := 0; i < size; i++ { + array.IncrementSize() + } + return Struct(array) +} diff --git a/vm/struct_test.go b/vm/struct_test.go new file mode 100644 index 0000000..9fe4b84 --- /dev/null +++ b/vm/struct_test.go @@ -0,0 +1,15 @@ +package vm + +import ( + "gotest.tools/assert" + "testing" +) + +func TestStruct_NewStruct(t *testing.T) { + s := NewStruct(2) + a := Array(s) + size, err := a.getSize() + + assert.NilError(t, err) + assert.Equal(t, size, uint16(2)) +} From 4fc4b7d4369dd5c10dc16f388c81d2a262283458 Mon Sep 17 00:00:00 2001 From: tk-codes Date: Sat, 27 Apr 2019 17:29:19 +0200 Subject: [PATCH 2/7] Implement StoreField and LoadField internal functions The internal struct functions operates on the custom array data structure --- go.mod | 1 + vm/struct.go | 40 +++++++++++++++++++++++++++++++++++----- vm/struct_test.go | 46 +++++++++++++++++++++++++++++++++++++++++++--- vm/vm_test.go | 13 ++++++------- 4 files changed, 85 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index 8018eb9..9ca7c14 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.12 require ( github.com/bazo-blockchain/bazo-miner v0.0.0-20190416182631-de8b4c07084f github.com/google/go-cmp v0.2.0 // indirect + github.com/pkg/errors v0.8.1 github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/willf/bitset v1.1.10 // indirect golang.org/x/crypto v0.0.0-20190417174047-f416ebab96af diff --git a/vm/struct.go b/vm/struct.go index 495b265..b7021f5 100644 --- a/vm/struct.go +++ b/vm/struct.go @@ -1,14 +1,44 @@ package vm +import ( + "github.com/pkg/errors" +) + // Struct type represents the composite data type declaration that // defines a group of variables. -type Struct Array +type Struct struct { + fields Array + size uint16 +} // NewStruct creates a new struct data structure. -func NewStruct(size int) Struct { +func newStruct(size uint16) *Struct { array := NewArray() - for i := 0; i < size; i++ { - array.IncrementSize() + for i := uint16(0); i < size; i++ { + _ = array.Append([]byte{0}) + } + return &Struct{array, size} +} + +// LoadField returns the field at the given index +func (s *Struct) LoadField(index uint16) ([]byte, error) { + return s.fields.At(index) +} + +// StoreField sets the element on the given index +func (s *Struct) StoreField(index uint16, element []byte) error { + if index >= s.size { + return errors.New("index out of bounds") + } + + // Array insert does not work for an array with size = 1 + if s.size == index+1 { + err := s.fields.Remove(index) + if err != nil { + return err + } + err = s.fields.Append(element) + return err } - return Struct(array) + return s.fields.Insert(index, element) } diff --git a/vm/struct_test.go b/vm/struct_test.go index 9fe4b84..fad97a1 100644 --- a/vm/struct_test.go +++ b/vm/struct_test.go @@ -6,10 +6,50 @@ import ( ) func TestStruct_NewStruct(t *testing.T) { - s := NewStruct(2) - a := Array(s) - size, err := a.getSize() + s := newStruct(2) + size, err := s.fields.getSize() assert.NilError(t, err) assert.Equal(t, size, uint16(2)) + + // Initial field value at index 0 + elementAt, atErr := s.fields.At(0) + assert.NilError(t, atErr) + assertBytes(t, elementAt, 0) + + // Initial field value at index 1 + elementAt, atErr = s.fields.At(1) + assert.NilError(t, atErr) + assertBytes(t, elementAt, 0) +} + +func TestStruct_StoreField(t *testing.T) { + s := newStruct(1) + element := []byte{2} + + err := s.StoreField(0, element) + assert.NilError(t, err) + + fieldValue, loadErr := s.LoadField(0) + assert.NilError(t, loadErr) + assertBytes(t, fieldValue, element...) +} + +func TestStruct_StoreFields(t *testing.T) { + s := newStruct(2) + element1 := []byte{2} + element2 := []byte{3} + + err := s.StoreField(0, element1) + assert.NilError(t, err) + err = s.StoreField(1, element2) + assert.NilError(t, err) + + fieldValue, loadErr := s.LoadField(0) + assert.NilError(t, loadErr) + assertBytes(t, fieldValue, element1...) + + fieldValue, loadErr = s.LoadField(1) + assert.NilError(t, loadErr) + assertBytes(t, fieldValue, element2...) } diff --git a/vm/vm_test.go b/vm/vm_test.go index 072f4b8..b709ac3 100644 --- a/vm/vm_test.go +++ b/vm/vm_test.go @@ -1830,16 +1830,15 @@ func TestVM_Exec_ArrAppend(t *testing.T) { func TestVM_Exec_ArrInsert(t *testing.T) { code := []byte{ - Push, 2, 0x00, 0x00, - Push, 2, 0x00, 0x00, + Push, 2, 0x00, 0x02, // new value [0,2] + Push, 2, 0x00, 0x00, // index 0 - Push, 2, 0xFF, 0x00, - - Push, 2, 0xFF, 0x00, + Push, 1, 0xFE, // value [254] at index 1 + Push, 1, 0xFF, // value [255] at index 0 NewArr, ArrAppend, ArrAppend, - ArrInsert, + ArrInsert, // Replace [255] with the new value [0,2] Halt, } @@ -1863,7 +1862,7 @@ func TestVM_Exec_ArrInsert(t *testing.T) { t.Errorf("invalid element appended, Expected '[%# x]' but was '[%# x]'", expectedSize, actual[1:2]) } - expectedValue := []byte{0x00, 0x00} + expectedValue := []byte{0x00, 0x02} if !bytes.Equal(expectedValue, actual[5:7]) { t.Errorf("invalid element appended, Expected '[%# x' but was '[%# x]'", expectedValue, actual[5:7]) } From 58b261103029a32520d1a4157d61f7ab91917909 Mon Sep 17 00:00:00 2001 From: tk-codes Date: Sun, 28 Apr 2019 00:00:21 +0200 Subject: [PATCH 3/7] Implement NewStr opcode --- vm/op_codes.go | 6 ++++++ vm/struct.go | 22 ++++++++++++++++++---- vm/struct_test.go | 12 ++++++------ vm/vm.go | 17 +++++++++++++++++ vm/vm_test.go | 18 ++++++++++++++++++ 5 files changed, 65 insertions(+), 10 deletions(-) diff --git a/vm/op_codes.go b/vm/op_codes.go index fc337d8..4f5f046 100644 --- a/vm/op_codes.go +++ b/vm/op_codes.go @@ -55,6 +55,9 @@ const ( ArrInsert ArrRemove ArrAt + NewStr + StoreFld + LoadFld SHA3 CheckSig ErrHalt @@ -134,6 +137,9 @@ var OpCodes = []OpCode{ {ArrInsert, "arrinsert", 0, nil, 1, 2}, {ArrRemove, "arrremove", 0, nil, 1, 2}, {ArrAt, "arrat", 0, nil, 1, 2}, + {NewStr, "newstr", 1, []int{BYTE}, 1, 2}, + {StoreFld, "storefld", 2, []int{BYTE, BYTES}, 1, 2}, + {LoadFld, "loadfld", 1, []int{BYTE}, 1, 2}, {SHA3, "sha3", 0, nil, 1, 2}, {CheckSig, "checksig", 0, nil, 1, 2}, {ErrHalt, "errhalt", 0, nil, 0, 1}, diff --git a/vm/struct.go b/vm/struct.go index b7021f5..453601f 100644 --- a/vm/struct.go +++ b/vm/struct.go @@ -20,13 +20,27 @@ func newStruct(size uint16) *Struct { return &Struct{array, size} } -// LoadField returns the field at the given index -func (s *Struct) LoadField(index uint16) ([]byte, error) { +func structFromByteArray(arr []byte) (*Struct, error) { + a, err := ArrayFromByteArray(arr) + if err != nil { + return nil, err + } + + size, sizeErr := a.getSize() + if sizeErr != nil { + return nil, err + } + + return &Struct{a, size}, nil +} + +// loadField returns the field at the given index +func (s *Struct) loadField(index uint16) ([]byte, error) { return s.fields.At(index) } -// StoreField sets the element on the given index -func (s *Struct) StoreField(index uint16, element []byte) error { +// storeField sets the element on the given index +func (s *Struct) storeField(index uint16, element []byte) error { if index >= s.size { return errors.New("index out of bounds") } diff --git a/vm/struct_test.go b/vm/struct_test.go index fad97a1..5ad76c5 100644 --- a/vm/struct_test.go +++ b/vm/struct_test.go @@ -27,10 +27,10 @@ func TestStruct_StoreField(t *testing.T) { s := newStruct(1) element := []byte{2} - err := s.StoreField(0, element) + err := s.storeField(0, element) assert.NilError(t, err) - fieldValue, loadErr := s.LoadField(0) + fieldValue, loadErr := s.loadField(0) assert.NilError(t, loadErr) assertBytes(t, fieldValue, element...) } @@ -40,16 +40,16 @@ func TestStruct_StoreFields(t *testing.T) { element1 := []byte{2} element2 := []byte{3} - err := s.StoreField(0, element1) + err := s.storeField(0, element1) assert.NilError(t, err) - err = s.StoreField(1, element2) + err = s.storeField(1, element2) assert.NilError(t, err) - fieldValue, loadErr := s.LoadField(0) + fieldValue, loadErr := s.loadField(0) assert.NilError(t, loadErr) assertBytes(t, fieldValue, element1...) - fieldValue, loadErr = s.LoadField(1) + fieldValue, loadErr = s.loadField(1) assert.NilError(t, loadErr) assertBytes(t, fieldValue, element2...) } diff --git a/vm/vm.go b/vm/vm.go index 5a496fc..b6bf7d7 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -1179,6 +1179,19 @@ func (vm *VM) Exec(trace bool) bool { vm.evaluationStack.Push([]byte(opCode.Name + ": " + err.Error())) return false } + case NewStr: + sizeBytes, err := vm.PopBytes(opCode) + if err != nil { + vm.pushError(opCode.Name, err) + } + + size, sizeErr := ByteArrayToUI16(sizeBytes) + if sizeErr != nil { + vm.pushError(opCode.Name, err) + } + + s := newStruct(size) + _ = vm.evaluationStack.Push(s.fields) case SHA3: right, err := vm.PopBytes(opCode) @@ -1267,6 +1280,10 @@ func (vm *VM) checkErrors(errorLocation string, errors ...error) bool { return true } +func (vm *VM) pushError(errorLocation string, err error) { + _ = vm.evaluationStack.Push([]byte(errorLocation + ": " + err.Error())) +} + func (vm *VM) PopBytes(opCode OpCode) (elements []byte, err error) { bytes, err := vm.evaluationStack.Pop() if err != nil { diff --git a/vm/vm_test.go b/vm/vm_test.go index b709ac3..e66d806 100644 --- a/vm/vm_test.go +++ b/vm/vm_test.go @@ -1966,6 +1966,24 @@ func TestVM_Exec_ArrAt(t *testing.T) { } +func TestVM_Exec_NewStr(t *testing.T) { + code := []byte{ + Push, 2, 2, 0, // size=2 + NewStr, + Halt, + } + + vm, isSuccess := execCode(code) + assert.Assert(t, isSuccess) + + arr, err := vm.evaluationStack.Pop() + assert.NilError(t, err) + + s, sErr := structFromByteArray(arr) + assert.NilError(t, sErr) + assert.Equal(t, s.size, uint16(2)) +} + func TestVM_Exec_NonValidOpCode(t *testing.T) { code := []byte{ 89, From e70283aa669c0281914f743b2138576d77bdb8b3 Mon Sep 17 00:00:00 2001 From: tk-codes Date: Sun, 28 Apr 2019 00:28:36 +0200 Subject: [PATCH 4/7] Refactor Struct to an alias type to Array --- vm/struct.go | 41 ++++++++++++++++++++++------------------- vm/struct_test.go | 7 ++++--- vm/vm.go | 2 +- vm/vm_test.go | 13 +++++++++---- 4 files changed, 36 insertions(+), 27 deletions(-) diff --git a/vm/struct.go b/vm/struct.go index 453601f..b95c90f 100644 --- a/vm/struct.go +++ b/vm/struct.go @@ -6,53 +6,56 @@ import ( // Struct type represents the composite data type declaration that // defines a group of variables. -type Struct struct { - fields Array - size uint16 -} +type Struct Array // NewStruct creates a new struct data structure. -func newStruct(size uint16) *Struct { +func newStruct(size uint16) Struct { array := NewArray() for i := uint16(0); i < size; i++ { _ = array.Append([]byte{0}) } - return &Struct{array, size} + return Struct(array) } -func structFromByteArray(arr []byte) (*Struct, error) { - a, err := ArrayFromByteArray(arr) +func structFromByteArray(arr []byte) (Struct, error) { + array, err := ArrayFromByteArray(arr) if err != nil { return nil, err } - size, sizeErr := a.getSize() - if sizeErr != nil { - return nil, err - } + return Struct(array), nil +} - return &Struct{a, size}, nil +func (s *Struct) toArray() *Array { + return (*Array)(s) } // loadField returns the field at the given index func (s *Struct) loadField(index uint16) ([]byte, error) { - return s.fields.At(index) + array := s.toArray() + return array.At(index) } // storeField sets the element on the given index func (s *Struct) storeField(index uint16, element []byte) error { - if index >= s.size { + array := s.toArray() + size, err := array.getSize() + if err != nil { + return err + } + + if index >= size { return errors.New("index out of bounds") } // Array insert does not work for an array with size = 1 - if s.size == index+1 { - err := s.fields.Remove(index) + if size == index+1 { + err := array.Remove(index) if err != nil { return err } - err = s.fields.Append(element) + err = array.Append(element) return err } - return s.fields.Insert(index, element) + return array.Insert(index, element) } diff --git a/vm/struct_test.go b/vm/struct_test.go index 5ad76c5..662f7c9 100644 --- a/vm/struct_test.go +++ b/vm/struct_test.go @@ -7,18 +7,19 @@ import ( func TestStruct_NewStruct(t *testing.T) { s := newStruct(2) - size, err := s.fields.getSize() + array := s.toArray() + size, err := array.getSize() assert.NilError(t, err) assert.Equal(t, size, uint16(2)) // Initial field value at index 0 - elementAt, atErr := s.fields.At(0) + elementAt, atErr := array.At(0) assert.NilError(t, atErr) assertBytes(t, elementAt, 0) // Initial field value at index 1 - elementAt, atErr = s.fields.At(1) + elementAt, atErr = array.At(1) assert.NilError(t, atErr) assertBytes(t, elementAt, 0) } diff --git a/vm/vm.go b/vm/vm.go index b6bf7d7..4c33f05 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -1191,7 +1191,7 @@ func (vm *VM) Exec(trace bool) bool { } s := newStruct(size) - _ = vm.evaluationStack.Push(s.fields) + _ = vm.evaluationStack.Push(s) case SHA3: right, err := vm.PopBytes(opCode) diff --git a/vm/vm_test.go b/vm/vm_test.go index e66d806..8e7fd00 100644 --- a/vm/vm_test.go +++ b/vm/vm_test.go @@ -1976,12 +1976,17 @@ func TestVM_Exec_NewStr(t *testing.T) { vm, isSuccess := execCode(code) assert.Assert(t, isSuccess) - arr, err := vm.evaluationStack.Pop() + arrBytes, err := vm.evaluationStack.Pop() assert.NilError(t, err) - s, sErr := structFromByteArray(arr) - assert.NilError(t, sErr) - assert.Equal(t, s.size, uint16(2)) + s, structErr := structFromByteArray(arrBytes) + assert.NilError(t, structErr) + assert.Assert(t, s != nil) + + arr := s.toArray() + size, sizeErr := arr.getSize() + assert.NilError(t, sizeErr) + assert.Equal(t, size, uint16(2)) } func TestVM_Exec_NonValidOpCode(t *testing.T) { From 9771fc36849df98eb0e108292e55d2e86e900f29 Mon Sep 17 00:00:00 2001 From: tk-codes Date: Sun, 28 Apr 2019 01:20:16 +0200 Subject: [PATCH 5/7] Implement StoreFld opcode StoreFld stores the element at the given struct field index --- vm/op_codes.go | 2 +- vm/vm.go | 47 ++++++++++++++++++++++++++++++++++++++--------- vm/vm_test.go | 37 ++++++++++++++++++++++++++++++------- 3 files changed, 69 insertions(+), 17 deletions(-) diff --git a/vm/op_codes.go b/vm/op_codes.go index 4f5f046..17eb50e 100644 --- a/vm/op_codes.go +++ b/vm/op_codes.go @@ -138,7 +138,7 @@ var OpCodes = []OpCode{ {ArrRemove, "arrremove", 0, nil, 1, 2}, {ArrAt, "arrat", 0, nil, 1, 2}, {NewStr, "newstr", 1, []int{BYTE}, 1, 2}, - {StoreFld, "storefld", 2, []int{BYTE, BYTES}, 1, 2}, + {StoreFld, "storefld", 1, []int{BYTE}, 1, 2}, {LoadFld, "loadfld", 1, []int{BYTE}, 1, 2}, {SHA3, "sha3", 0, nil, 1, 2}, {CheckSig, "checksig", 0, nil, 1, 2}, diff --git a/vm/vm.go b/vm/vm.go index 4c33f05..a66bc37 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -1180,18 +1180,47 @@ func (vm *VM) Exec(trace bool) bool { return false } case NewStr: - sizeBytes, err := vm.PopBytes(opCode) + sizeBytes, err := vm.fetchMany(opCode.Name, 2) if err != nil { - vm.pushError(opCode.Name, err) + vm.pushError(opCode, err) + return false } - size, sizeErr := ByteArrayToUI16(sizeBytes) - if sizeErr != nil { - vm.pushError(opCode.Name, err) + size, err := ByteArrayToUI16(sizeBytes) + if err != nil { + vm.pushError(opCode, err) + return false } - s := newStruct(size) - _ = vm.evaluationStack.Push(s) + str := newStruct(size) + _ = vm.evaluationStack.Push(str) + + case StoreFld: + indexBytes, indexErr := vm.fetchMany(opCode.Name, 2) + element, elementErr := vm.PopBytes(opCode) + structBytes, structErr := vm.PopBytes(opCode) + + if !vm.checkErrors(opCode.Name, structErr, indexErr, elementErr) { + return false + } + + if len(indexBytes) > 2 { + vm.pushError(opCode, errors.New("size should be 2 bytes")) + return false + } + + str, structErr := structFromByteArray(structBytes) + index, indexErr := ByteArrayToUI16(indexBytes) + if !vm.checkErrors(opCode.Name, structErr, indexErr) { + return false + } + + err := str.storeField(index, element) + if err != nil { + vm.pushError(opCode, err) + return false + } + _ = vm.evaluationStack.Push(str) case SHA3: right, err := vm.PopBytes(opCode) @@ -1280,8 +1309,8 @@ func (vm *VM) checkErrors(errorLocation string, errors ...error) bool { return true } -func (vm *VM) pushError(errorLocation string, err error) { - _ = vm.evaluationStack.Push([]byte(errorLocation + ": " + err.Error())) +func (vm *VM) pushError(opCode OpCode, err error) { + _ = vm.evaluationStack.Push([]byte(opCode.Name + ": " + err.Error())) } func (vm *VM) PopBytes(opCode OpCode) (elements []byte, err error) { diff --git a/vm/vm_test.go b/vm/vm_test.go index 8e7fd00..91213b2 100644 --- a/vm/vm_test.go +++ b/vm/vm_test.go @@ -1968,8 +1968,7 @@ func TestVM_Exec_ArrAt(t *testing.T) { func TestVM_Exec_NewStr(t *testing.T) { code := []byte{ - Push, 2, 2, 0, // size=2 - NewStr, + NewStr, 2, 0, // size=2 Halt, } @@ -1979,16 +1978,40 @@ func TestVM_Exec_NewStr(t *testing.T) { arrBytes, err := vm.evaluationStack.Pop() assert.NilError(t, err) - s, structErr := structFromByteArray(arrBytes) + str, structErr := structFromByteArray(arrBytes) assert.NilError(t, structErr) - assert.Assert(t, s != nil) + assert.Assert(t, str != nil) - arr := s.toArray() + arr := str.toArray() size, sizeErr := arr.getSize() assert.NilError(t, sizeErr) assert.Equal(t, size, uint16(2)) } +func TestVM_Exec_StoreFld(t *testing.T) { + code := []byte{ + NewStr, 1, 0, + PushInt, 1, 0, 4, + StoreFld, 0, 0, // Store field on index 0 + Halt, + } + + vm, isSuccess := execCode(code) + assert.Assert(t, isSuccess) + + structBytes, err := vm.evaluationStack.Pop() + assert.NilError(t, err) + + str, err := structFromByteArray(structBytes) + assert.NilError(t, err) + assert.Assert(t, str != nil) + + arr := str.toArray() + element, err := arr.At(0) + assert.NilError(t, err) + assertBytes(t, element, 0, 4) +} + func TestVM_Exec_NonValidOpCode(t *testing.T) { code := []byte{ 89, @@ -2787,13 +2810,13 @@ func TestPeekEvalStack(t *testing.T) { // Helper functions // ---------------- -func execCode(code []byte) (VM, bool) { +func execCode(code []byte) (*VM, bool) { vm := NewTestVM([]byte{}) mc := NewMockContext(code) vm.context = mc isSuccess := vm.Exec(false) - return vm, isSuccess + return &vm, isSuccess } func assertBytes(t *testing.T, actual []byte, expected ...byte) { From d420facf762d340457224c354a16fad5f4b3f36f Mon Sep 17 00:00:00 2001 From: tk-codes Date: Sun, 28 Apr 2019 14:02:48 +0200 Subject: [PATCH 6/7] Implement LoadFld opcode --- vm/vm.go | 39 ++++++++++++++++++++++++++++++++------- vm/vm_test.go | 24 ++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 7 deletions(-) diff --git a/vm/vm.go b/vm/vm.go index a66bc37..7572fb7 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -12,6 +12,9 @@ import ( "golang.org/x/crypto/sha3" ) +// Context is the VM execution context +// which is composed with data coming from the transaction and the account. +// Context interface declares functions required to start the execution of the contract. type Context interface { GetContract() []byte GetContractVariable(index int) ([]byte, error) @@ -1193,8 +1196,10 @@ func (vm *VM) Exec(trace bool) bool { } str := newStruct(size) - _ = vm.evaluationStack.Push(str) - + err = vm.evaluationStack.Push(str) + if err != nil { + return false + } case StoreFld: indexBytes, indexErr := vm.fetchMany(opCode.Name, 2) element, elementErr := vm.PopBytes(opCode) @@ -1204,8 +1209,26 @@ func (vm *VM) Exec(trace bool) bool { return false } - if len(indexBytes) > 2 { - vm.pushError(opCode, errors.New("size should be 2 bytes")) + str, structErr := structFromByteArray(structBytes) + index, indexErr := ByteArrayToUI16(indexBytes) + if !vm.checkErrors(opCode.Name, structErr, indexErr) { + return false + } + + err := str.storeField(index, element) + if err != nil { + vm.pushError(opCode, err) + return false + } + err = vm.evaluationStack.Push(str) + if err != nil { + return false + } + case LoadFld: + indexBytes, indexErr := vm.fetchMany(opCode.Name, 2) + structBytes, structErr := vm.PopBytes(opCode) + + if !vm.checkErrors(opCode.Name, structErr, indexErr) { return false } @@ -1215,13 +1238,15 @@ func (vm *VM) Exec(trace bool) bool { return false } - err := str.storeField(index, element) + element, err := str.loadField(index) if err != nil { vm.pushError(opCode, err) return false } - _ = vm.evaluationStack.Push(str) - + err = vm.evaluationStack.Push(element) + if err != nil { + return false + } case SHA3: right, err := vm.PopBytes(opCode) if err != nil { diff --git a/vm/vm_test.go b/vm/vm_test.go index 91213b2..0b499f5 100644 --- a/vm/vm_test.go +++ b/vm/vm_test.go @@ -2012,6 +2012,30 @@ func TestVM_Exec_StoreFld(t *testing.T) { assertBytes(t, element, 0, 4) } +func TestVM_Exec_LoadFld(t *testing.T) { + code := []byte{ + NewStr, 2, 0, + + PushInt, 1, 0, 4, + StoreFld, 0, 0, // Store field on index 0 + + PushInt, 1, 0, 8, + StoreFld, 1, 0, // Store field on index 1 + + LoadFld, 0, 0, // Load field at index 0 + Halt, + } + + vm, isSuccess := execCode(code) + assert.Assert(t, isSuccess) + + assert.Assert(t, len(vm.evaluationStack.Stack) == 1) + + element, err := vm.evaluationStack.Pop() + assert.NilError(t, err) + assertBytes(t, element, 0, 4) +} + func TestVM_Exec_NonValidOpCode(t *testing.T) { code := []byte{ 89, From 1974349f4a27eb8253c2d1ec07565e2cac7669f6 Mon Sep 17 00:00:00 2001 From: tk-codes Date: Sun, 28 Apr 2019 21:46:53 +0200 Subject: [PATCH 7/7] Use BigEndian byte order Byte util functions used both little endian and big endian. It may lead to inconsistencies. Therefore, the opcodes are refactored to use BigEndian consistently. --- vm/array_test.go | 18 +++++++++--------- vm/map_test.go | 2 +- vm/utils.go | 4 ++-- vm/vm_test.go | 34 +++++++++++++++++----------------- 4 files changed, 29 insertions(+), 29 deletions(-) diff --git a/vm/array_test.go b/vm/array_test.go index 1cea9d7..15880b3 100644 --- a/vm/array_test.go +++ b/vm/array_test.go @@ -30,7 +30,7 @@ func TestArray_IncerementSize(t *testing.T) { } func TestArray_DecrementSize(t *testing.T) { - a := Array([]byte{0x02, 0x02, 0x00}) + a := Array([]byte{0x02, 0x00, 0x02}) s, err := ByteArrayToUI16(a[1:3]) if s != 2 || err != nil { @@ -46,11 +46,11 @@ func TestArray_DecrementSize(t *testing.T) { func TestArray_At(t *testing.T) { a := Array([]byte{0x02, - 0x03, 0x00, + 0x00, 0x03, - 0x08, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x65, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x65, 0x00, + 0x00, 0x08, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x04, 0x65, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x65, 0x00, }) expected0 := []byte{0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} @@ -84,11 +84,11 @@ func TestArray_At(t *testing.T) { func TestArray_Insert(t *testing.T) { a := Array([]byte{0x02, - 0x03, 0x00, + 0x00, 0x03, - 0x08, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x65, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x65, 0x00, + 0x00, 0x08, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x04, 0x65, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x65, 0x00, }) v := []byte{0x01} diff --git a/vm/map_test.go b/vm/map_test.go index 6394d8b..0aa7b12 100644 --- a/vm/map_test.go +++ b/vm/map_test.go @@ -29,7 +29,7 @@ func TestMap_IncerementSize(t *testing.T) { } func TestMap_DecrementSize(t *testing.T) { - a := Array([]byte{0x02, 0x02, 0x00}) + a := Array([]byte{0x02, 0x00, 0x02}) s, err := ByteArrayToUI16(a[1:3]) if s != 2 || err != nil { diff --git a/vm/utils.go b/vm/utils.go index 3870f8b..4d55eee 100644 --- a/vm/utils.go +++ b/vm/utils.go @@ -18,7 +18,7 @@ func UInt64ToByteArray(element uint64) []byte { func UInt16ToByteArray(element uint16) []byte { ba := make([]byte, 2) - binary.LittleEndian.PutUint16(ba, uint16(element)) + binary.BigEndian.PutUint16(ba, uint16(element)) return ba } @@ -30,7 +30,7 @@ func ByteArrayToUI16(element []byte) (uint16, error) { return 0, errors.New("byte array to uint16 invalid parameters provided") } - return binary.LittleEndian.Uint16(element), nil + return binary.BigEndian.Uint16(element), nil } func StrToBigInt(element string) big.Int { diff --git a/vm/vm_test.go b/vm/vm_test.go index 0b499f5..9f679c6 100644 --- a/vm/vm_test.go +++ b/vm/vm_test.go @@ -1703,11 +1703,11 @@ func TestVM_Exec_MapSetVal(t *testing.T) { } expected := []byte{0x01, - 0x02, 0x00, - 0x01, 0x00, 0x02, - 0x02, 0x00, 0x69, 0x69, - 0x01, 0x00, 0x03, - 0x02, 0x00, 0x55, 0x55, + 0x00, 0x02, + 0x00, 0x01, 0x02, + 0x00, 0x02, 0x69, 0x69, + 0x00, 0x01, 0x03, + 0x00, 0x02, 0x55, 0x55, } if !bytes.Equal(actual, expected) { @@ -1760,11 +1760,11 @@ func TestVM_Exec_MapRemove(t *testing.T) { } expected := []byte{0x01, - 0x02, 0x00, - 0x01, 0x00, 0x02, - 0x02, 0x00, 0x69, 0x69, - 0x01, 0x00, 0x01, - 0x02, 0x00, 0x48, 0x48, + 0x00, 0x02, + 0x00, 0x01, 0x02, + 0x00, 0x02, 0x69, 0x69, + 0x00, 0x01, 0x01, + 0x00, 0x02, 0x48, 0x48, } if !bytes.Equal(actual, expected) { @@ -1857,8 +1857,8 @@ func TestVM_Exec_ArrInsert(t *testing.T) { t.Errorf("%v", err) } - expectedSize := []byte{0x02} - if !bytes.Equal(expectedSize, actual[1:2]) { + expectedSize := []byte{0x00, 0x02} + if !bytes.Equal(expectedSize, actual[1:3]) { t.Errorf("invalid element appended, Expected '[%# x]' but was '[%# x]'", expectedSize, actual[1:2]) } @@ -1870,7 +1870,7 @@ func TestVM_Exec_ArrInsert(t *testing.T) { func TestVM_Exec_ArrRemove(t *testing.T) { code := []byte{ - Push, 2, 0x01, 0x00, //Index of element to remove + Push, 2, 0x00, 0x01, //Index of element to remove Push, 2, 0xBB, 0x00, Push, 2, 0xAA, 0x00, Push, 2, 0xFF, 0x00, @@ -1927,7 +1927,7 @@ func TestVM_Exec_ArrRemove(t *testing.T) { func TestVM_Exec_ArrAt(t *testing.T) { code := []byte{ - Push, 2, 0x02, 0x00, // index for ARRAT + Push, 2, 0x00, 0x02, // index for ARRAT Push, 2, 0xBB, 0x00, Push, 2, 0xAA, 0x00, Push, 2, 0xFF, 0x00, @@ -1968,7 +1968,7 @@ func TestVM_Exec_ArrAt(t *testing.T) { func TestVM_Exec_NewStr(t *testing.T) { code := []byte{ - NewStr, 2, 0, // size=2 + NewStr, 0, 2, // size=2 Halt, } @@ -2014,13 +2014,13 @@ func TestVM_Exec_StoreFld(t *testing.T) { func TestVM_Exec_LoadFld(t *testing.T) { code := []byte{ - NewStr, 2, 0, + NewStr, 0, 2, PushInt, 1, 0, 4, StoreFld, 0, 0, // Store field on index 0 PushInt, 1, 0, 8, - StoreFld, 1, 0, // Store field on index 1 + StoreFld, 0, 1, // Store field on index 1 LoadFld, 0, 0, // Load field at index 0 Halt,