Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix: Decouple opcodes from big integer #13

Merged
merged 8 commits into from
Apr 11, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ language: go
go:
- "1.11.x"

env:
- GO111MODULE=on

script:
- go test -v ./... -coverprofile=coverage.out -coverpkg=./...

Expand Down
9 changes: 3 additions & 6 deletions vm/call_stack.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@ package vm

import (
"errors"
"math/big"
)

type Frame struct {
variables map[int]big.Int
variables map[int][]byte
returnAddress int
}

Expand All @@ -31,15 +30,13 @@ func (cs *CallStack) Pop() (frame *Frame, err error) {
element := (*cs).values[cs.GetLength()-1]
cs.values = cs.values[:cs.GetLength()-1]
return element, nil
} else {
return nil, errors.New("pop() on empty callStack")
}
return nil, errors.New("pop() on empty callStack")
}

func (cs *CallStack) Peek() (frame *Frame, err error) {
if (*cs).GetLength() > 0 {
return (*cs).values[cs.GetLength()-1], nil
} else {
return nil, errors.New("peek() on empty callStack")
}
return nil, errors.New("peek() on empty callStack")
}
26 changes: 13 additions & 13 deletions vm/call_stack_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ func TestCallStack_NewCallStack(t *testing.T) {
func TestCallStack_Push(t *testing.T) {
cs := NewCallStack()

variables := map[int]big.Int{
0: *big.NewInt(int64(4)),
1: *big.NewInt(int64(5)),
2: *big.NewInt(int64(6)),
variables := map[int][]byte{
0: SignedByteArrayConversion(*big.NewInt(int64(4))),
1: SignedByteArrayConversion(*big.NewInt(int64(5))),
2: SignedByteArrayConversion(*big.NewInt(int64(6))),
}

cs.Push(&Frame{variables: variables, returnAddress: 3})
Expand All @@ -37,19 +37,19 @@ func TestCallStack_Push(t *testing.T) {
func TestCallStack_MultiplePushPop(t *testing.T) {
cs := NewCallStack()

variables1 := map[int]big.Int{
0: *big.NewInt(int64(4)),
variables1 := map[int][]byte{
0: SignedByteArrayConversion(*big.NewInt(int64(4))),
}

variables2 := map[int]big.Int{
0: *big.NewInt(int64(4)),
1: *big.NewInt(int64(5)),
variables2 := map[int][]byte{
0: SignedByteArrayConversion(*big.NewInt(int64(4))),
1: SignedByteArrayConversion(*big.NewInt(int64(5))),
}

variables3 := map[int]big.Int{
0: *big.NewInt(int64(4)),
1: *big.NewInt(int64(5)),
2: *big.NewInt(int64(6)),
variables3 := map[int][]byte{
0: SignedByteArrayConversion(*big.NewInt(int64(4))),
1: SignedByteArrayConversion(*big.NewInt(int64(5))),
2: SignedByteArrayConversion(*big.NewInt(int64(6))),
}

cs.Push(&Frame{variables: variables1, returnAddress: 0})
Expand Down
18 changes: 9 additions & 9 deletions vm/op_codes.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,12 @@ const (
StoreSt
LoadLoc
LoadSt
Address // Address of account
Issuer // Owner of smart contract account
Balance // Balance of account
Address // Address of account
Issuer // Owner of smart contract account
Balance // Balance of account
Caller
CallVal // Amount of bazo coins transacted in transaction
CallData // Parameters and function signature hash
CallVal // Amount of bazo coins transacted in transaction
CallData // Parameters and function signature hash
NewMap
MapHasKey
MapPush
Expand Down Expand Up @@ -113,10 +113,10 @@ var OpCodes = []OpCode{
{CallExt, "callext", 3, []int{ADDR, BYTE, BYTE, BYTE, BYTE, BYTE}, 1000, 2},
{Ret, "ret", 0, nil, 1, 1},
{Size, "size", 0, nil, 1, 1},
{StoreLoc, "store", 0, nil, 1, 2},
{StoreSt, "sstore", 1, []int{BYTE}, 1000, 2},
{LoadLoc, "load", 1, []int{BYTE}, 1, 2},
{LoadSt, "sload", 1, []int{BYTE}, 10, 2},
{StoreLoc, "storeloc", 1, []int{BYTE}, 1, 2},
{StoreSt, "storest", 1, []int{BYTE}, 1000, 2},
{LoadLoc, "loadloc", 1, []int{BYTE}, 1, 2},
{LoadSt, "loadst", 1, []int{BYTE}, 10, 2},
{Address, "address", 0, nil, 1, 1},
{Issuer, "issuer", 0, nil, 1, 1},
{Balance, "balance", 0, nil, 1, 1},
Expand Down
147 changes: 91 additions & 56 deletions vm/vm.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package vm

import (
"bytes"
"crypto/ecdsa"
"crypto/elliptic"
"encoding/binary"
Expand Down Expand Up @@ -90,7 +91,7 @@ func (vm *VM) trace() {
case BYTE:
if len(vm.code)-vm.pc > 0 {
args = []byte{vm.code[vm.pc+1+counter]}
counter += 1
counter++
formattedArgs += fmt.Sprintf("%v (byte) ", args[:])
}

Expand Down Expand Up @@ -155,9 +156,8 @@ func (vm *VM) Exec(trace bool) bool {
if vm.fee < opCode.gasPrice {
vm.evaluationStack.Push([]byte("vm.exec(): out of gas"))
return false
} else {
vm.fee -= opCode.gasPrice
}
vm.fee -= opCode.gasPrice

// Decode
switch opCode.code {
Expand Down Expand Up @@ -374,8 +374,8 @@ func (vm *VM) Exec(trace bool) bool {
// The Exp OpCode is a special case in terms of gas calculation. The calculation of the gasCost is done
// during execution. An Exp function such as 2 ** n can be split up into n multiplications of the first
// factor -> 2 * 2 * 2 ... (n times). Therefore the gasCosts need to be as high as if the user performed
// n multiplications. As the user already payed the opcode price, we reduce the gasCost by this price.
gasCost := opCode.gasPrice * uint64(right.Int64()) - opCode.gasPrice
// n multiplications. As the user already paid the opcode price, we reduce the gasCost by this price.
gasCost := opCode.gasPrice*uint64(right.Int64()) - opCode.gasPrice

if int64(vm.fee-gasCost) < 0 {
vm.evaluationStack.Push([]byte(opCode.Name + ": Out of gas"))
Expand Down Expand Up @@ -434,77 +434,79 @@ func (vm *VM) Exec(trace bool) bool {
}

case Neg:
tos, err := vm.PopSignedBigInt(opCode)
tos, err := vm.PopBytes(opCode)

if err != nil {
vm.evaluationStack.Push([]byte(opCode.Name + ": " + err.Error()))
return false
}

tos.Neg(&tos)

vm.evaluationStack.Push(SignedByteArrayConversion(tos))
switch tos[0] {
case 1:
tos[0] = 0
case 0:
tos[0] = 1
default:
err = fmt.Errorf("unable to negate %v", tos[0])
_ = vm.evaluationStack.Push([]byte(opCode.Name + ": " + err.Error()))
return false
}

err = vm.evaluationStack.Push(tos)
if err != nil {
_ = vm.evaluationStack.Push([]byte(opCode.Name + ": " + err.Error()))
return false
}
case Eq:
right, rerr := vm.PopUnsignedBigInt(opCode)
left, lerr := vm.PopUnsignedBigInt(opCode)
right, rerr := vm.PopBytes(opCode)
left, lerr := vm.PopBytes(opCode)

if !vm.checkErrors(opCode.Name, rerr, lerr) {
return false
}

result := left.Cmp(&right) == 0
vm.evaluationStack.Push(BoolToByteArray(result))
result := bytes.Compare(left, right)
err := vm.evaluationStack.Push(BoolToByteArray(result == 0))

if err != nil {
_ = vm.evaluationStack.Push([]byte(opCode.Name + ": " + err.Error()))
return false
}
case NotEq:
right, rerr := vm.PopUnsignedBigInt(opCode)
left, lerr := vm.PopUnsignedBigInt(opCode)
right, rerr := vm.PopBytes(opCode)
left, lerr := vm.PopBytes(opCode)

if !vm.checkErrors(opCode.Name, rerr, lerr) {
return false
}

result := left.Cmp(&right) != 0
vm.evaluationStack.Push(BoolToByteArray(result))
result := bytes.Compare(left, right)
err := vm.evaluationStack.Push(BoolToByteArray(result != 0))

if err != nil {
_ = vm.evaluationStack.Push([]byte(opCode.Name + ": " + err.Error()))
return false
}
case Lt:
right, rerr := vm.PopSignedBigInt(opCode)
left, lerr := vm.PopSignedBigInt(opCode)
if !vm.checkErrors(opCode.Name, rerr, lerr) {
isSuccess := vm.evaluateRelationalComp(opCode, -1)
if !isSuccess {
return false
}

result := left.Cmp(&right) == -1
vm.evaluationStack.Push(BoolToByteArray(result))

case Gt:
right, rerr := vm.PopSignedBigInt(opCode)
left, lerr := vm.PopSignedBigInt(opCode)
if !vm.checkErrors(opCode.Name, rerr, lerr) {
isSuccess := vm.evaluateRelationalComp(opCode, 1)
if !isSuccess {
return false
}

result := left.Cmp(&right) == 1
vm.evaluationStack.Push(BoolToByteArray(result))

case LtEq:
right, rerr := vm.PopSignedBigInt(opCode)
left, lerr := vm.PopSignedBigInt(opCode)
if !vm.checkErrors(opCode.Name, rerr, lerr) {
isSuccess := vm.evaluateRelationalComp(opCode, -1, 0)
if !isSuccess {
return false
}

result := left.Cmp(&right) == -1 || left.Cmp(&right) == 0
vm.evaluationStack.Push(BoolToByteArray(result))

case GtEq:
right, rerr := vm.PopSignedBigInt(opCode)
left, lerr := vm.PopSignedBigInt(opCode)
if !vm.checkErrors(opCode.Name, rerr, lerr) {
isSuccess := vm.evaluateRelationalComp(opCode, 0, 1)
if !isSuccess {
return false
}

result := left.Cmp(&right) == 1 || left.Cmp(&right) == 0
vm.evaluationStack.Push(BoolToByteArray(result))

case ShiftL:
nrOfShifts, errArg := vm.fetch(opCode.Name)
tos, errStack := vm.PopSignedBigInt(opCode)
Expand Down Expand Up @@ -595,10 +597,10 @@ func (vm *VM) Exec(trace bool) bool {
return false
}

frame := &Frame{returnAddress: vm.pc, variables: make(map[int]big.Int)}
frame := &Frame{returnAddress: vm.pc, variables: make(map[int][]byte)}

for i := int(argsToLoad) - 1; i >= 0; i-- {
frame.variables[i], err = vm.PopUnsignedBigInt(opCode)
frame.variables[i], err = vm.PopBytes(opCode)
if err != nil {
vm.evaluationStack.Push([]byte(opCode.Name + ": " + err.Error()))
return false
Expand Down Expand Up @@ -626,10 +628,10 @@ func (vm *VM) Exec(trace bool) bool {
return false
}

frame := &Frame{returnAddress: vm.pc, variables: make(map[int]big.Int)}
frame := &Frame{returnAddress: vm.pc, variables: make(map[int][]byte)}

for i := int(argsToLoad) - 1; i >= 0; i-- {
frame.variables[i], err = vm.PopUnsignedBigInt(opCode)
frame.variables[i], err = vm.PopBytes(opCode)
if err != nil {
vm.evaluationStack.Push([]byte(opCode.Name + ": " + err.Error()))
return false
Expand Down Expand Up @@ -691,7 +693,7 @@ func (vm *VM) Exec(trace bool) bool {

case StoreLoc:
address, errArgs := vm.fetch(opCode.Name)
right, errStack := vm.PopSignedBigInt(opCode)
right, errStack := vm.PopBytes(opCode)

if !vm.checkErrors(opCode.Name, errArgs, errStack) {
return false
Expand Down Expand Up @@ -734,8 +736,7 @@ func (vm *VM) Exec(trace bool) bool {

val := callstackTos.variables[int(address)]

err := vm.evaluationStack.Push(SignedByteArrayConversion(val))

err := vm.evaluationStack.Push(val)
if err != nil {
vm.evaluationStack.Push([]byte(opCode.Name + ": " + err.Error()))
return false
Expand Down Expand Up @@ -1215,19 +1216,17 @@ func (vm *VM) fetch(errorLocation string) (element byte, err error) {
if len(vm.code) > tempPc {
vm.pc++
return vm.code[tempPc], nil
} else {
return 0, errors.New("Instruction set out of bounds")
}
return 0, errors.New("Instruction set out of bounds")
}

func (vm *VM) fetchMany(errorLocation string, argument int) (elements []byte, err error) {
tempPc := vm.pc
if len(vm.code)-tempPc > argument {
vm.pc += argument
return vm.code[tempPc : tempPc+argument], nil
} else {
return []byte{}, errors.New("Instruction set out of bounds")
}
return []byte{}, errors.New("Instruction set out of bounds")
}

func (vm *VM) checkErrors(errorLocation string, errors ...error) bool {
Expand Down Expand Up @@ -1307,3 +1306,39 @@ func (vm *VM) GetErrorMsg() string {
}
return string(tos)
}

func (vm *VM) evaluateRelationalComp(opCode OpCode, expectedResult ...int) bool {
right, rerr := vm.PopBytes(opCode)
left, lerr := vm.PopBytes(opCode)
if !vm.checkErrors(opCode.Name, rerr, lerr) {
return false
}

var result int
// char has always one byte
if len(left) == 1 && len(right) == 1 {
result = bytes.Compare(left, right)
} else {
leftInt, lerr := SignedBigIntConversion(left, nil)
rightInt, rerr := SignedBigIntConversion(right, nil)

if !vm.checkErrors(opCode.Name, rerr, lerr) {
return false
}
result = leftInt.Cmp(&rightInt)
}

var compResult bool
for _, r := range expectedResult {
if r == result {
compResult = true
}
}

err := vm.evaluationStack.Push(BoolToByteArray(compResult))
if err != nil {
_ = vm.evaluationStack.Push([]byte(opCode.Name + ": " + err.Error()))
return false
}
return true
}
Loading