Skip to content
Snippets Groups Projects
vm.go 28.9 KiB
Newer Older
// Copyright 2017 Monax Industries Limited
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

Silas Davis's avatar
Silas Davis committed
package evm
Silas Davis's avatar
Silas Davis committed
	acm "github.com/hyperledger/burrow/account"
	. "github.com/hyperledger/burrow/binary"
	"github.com/hyperledger/burrow/event"
	. "github.com/hyperledger/burrow/execution/evm/asm"
	"github.com/hyperledger/burrow/execution/evm/events"
	"github.com/hyperledger/burrow/execution/evm/sha3"
	"github.com/hyperledger/burrow/logging"
	logging_types "github.com/hyperledger/burrow/logging/types"
	"github.com/hyperledger/burrow/permission"
	ptypes "github.com/hyperledger/burrow/permission/types"
RJ Catalano's avatar
RJ Catalano committed
	ErrUnknownAddress         = errors.New("Unknown address")
	ErrInsufficientBalance    = errors.New("Insufficient balance")
	ErrInvalidJumpDest        = errors.New("Invalid jump dest")
	ErrInsufficientGas        = errors.New("Insufficient gas")
	ErrMemoryOutOfBounds      = errors.New("Memory out of bounds")
	ErrCodeOutOfBounds        = errors.New("Code out of bounds")
	ErrInputOutOfBounds       = errors.New("Input out of bounds")
	ErrCallStackOverflow      = errors.New("Call stack overflow")
	ErrCallStackUnderflow     = errors.New("Call stack underflow")
	ErrDataStackOverflow      = errors.New("Data stack overflow")
	ErrDataStackUnderflow     = errors.New("Data stack underflow")
	ErrInvalidContract        = errors.New("Invalid contract")
	ErrNativeContractCodeCopy = errors.New("Tried to copy native contract code")
)

type ErrPermission struct {
	typ string
}

func (err ErrPermission) Error() string {
	return fmt.Sprintf("Contract does not have permission to %s", err.typ)
}

const (
	dataStackCapacity = 1024
	callStackCapacity = 100 // TODO ensure usage.
Silas Davis's avatar
Silas Davis committed
type Params struct {
	BlockHeight uint64
	BlockHash   Word256
	BlockTime   int64
	GasLimit    uint64
Silas Davis's avatar
Silas Davis committed
	state          acm.StateWriter
	memoryProvider func() Memory
	params         Params
Silas Davis's avatar
Silas Davis committed
	origin         acm.Address
Silas Davis's avatar
Silas Davis committed
	callDepth      int
	publisher      event.Publisher
Silas Davis's avatar
Silas Davis committed
	logger         logging_types.InfoTraceLogger
Silas Davis's avatar
Silas Davis committed
func NewVM(state acm.StateWriter, memoryProvider func() Memory, params Params, origin acm.Address, txid []byte,
	logger logging_types.InfoTraceLogger) *VM {
Silas Davis's avatar
Silas Davis committed
		state:          state,
		memoryProvider: memoryProvider,
		params:         params,
		origin:         origin,
		callDepth:      0,
		txid:           txid,
		logger:         logging.WithScope(logger, "NewVM"),
Silas Davis's avatar
Silas Davis committed
func (vm *VM) Debugf(format string, a ...interface{}) {
	logging.TraceMsg(vm.logger, fmt.Sprintf(format, a...), "tag", "vm_debug")
Silas Davis's avatar
Silas Davis committed
}

// satisfies go_events.Eventable
func (vm *VM) SetPublisher(publisher event.Publisher) {
	vm.publisher = publisher
}

// CONTRACT: it is the duty of the contract writer to call known permissions
// we do not convey if a permission is not set
// (unlike in state/execution, where we guarantee HasPermission is called
// on known permissions and panics else)
// If the perm is not defined in the acc nor set by default in GlobalPermissions,
Silas Davis's avatar
Silas Davis committed
func HasPermission(state acm.StateWriter, acc acm.Account, perm ptypes.PermFlag) bool {
	value, _ := acc.Permissions().Base.Compose(permission.GlobalAccountPermissions(state).Base).Get(perm)
	return value
Silas Davis's avatar
Silas Davis committed
func (vm *VM) fireCallEvent(exception *string, output *[]byte, callerAddress, calleeAddress acm.Address, input []byte, value uint64, gas *uint64) {
	// fire the post call event (including exception if applicable)
	if vm.publisher != nil {
		events.PublishAccountCall(vm.publisher, calleeAddress, &events.EventDataCall{
Silas Davis's avatar
Silas Davis committed
			&events.CallData{Caller: callerAddress, Callee: calleeAddress, Data: input, Value: value, Gas: *gas},
			vm.origin,
Silas Davis's avatar
Silas Davis committed
// CONTRACT state is aware of caller and callee, so we can just mutate them.
// CONTRACT code and input are not mutated.
// CONTRACT returned 'ret' is a new compact slice.
// value: To be transferred from caller to callee. Refunded upon error.
// gas:   Available gas. No refunds for gas.
// code: May be nil, since the CALL opcode may be used to send value from contracts to accounts
Silas Davis's avatar
Silas Davis committed
func (vm *VM) Call(caller, callee acm.MutableAccount, code, input []byte, value uint64, gas *uint64) (output []byte, err error) {

	exception := new(string)
	// fire the post call event (including exception if applicable)
Silas Davis's avatar
Silas Davis committed
	defer vm.fireCallEvent(exception, &output, caller.Address(), callee.Address(), input, value, gas)

	if err = transfer(caller, callee, value); err != nil {
		*exception = err.Error()
		return
	}

	if len(code) > 0 {
		vm.callDepth += 1
		output, err = vm.call(caller, callee, code, input, value, gas)
		vm.callDepth -= 1
		if err != nil {
			*exception = err.Error()
			err := transfer(callee, caller, value)
			if err != nil {
				// data has been corrupted in ram
Silas Davis's avatar
Silas Davis committed
				panic("Could not return value to caller")
// DelegateCall is executed by the DELEGATECALL opcode, introduced as off Ethereum Homestead.
// The intent of delegate call is to run the code of the callee in the storage context of the caller;
// while preserving the original caller to the previous callee.
// Different to the normal CALL or CALLCODE, the value does not need to be transferred to the callee.
Silas Davis's avatar
Silas Davis committed
func (vm *VM) DelegateCall(caller, callee acm.MutableAccount, code, input []byte, value uint64, gas *uint64) (output []byte, err error) {
	exception := new(string)
	// fire the post call event (including exception if applicable)
Silas Davis's avatar
Silas Davis committed
	// introduce event EventStringAccDelegateCall Acc/%s/DelegateCall
	// defer vm.fireCallEvent(exception, &output, caller, callee, input, value, gas)
	// DelegateCall does not transfer the value to the callee.

	if len(code) > 0 {
		vm.callDepth += 1
		output, err = vm.call(caller, callee, code, input, value, gas)
		vm.callDepth -= 1
		if err != nil {
			*exception = err.Error()
		}
	}

	return
}

// Try to deduct gasToUse from gasLeft.  If ok return false, otherwise
// set err and return true.
Silas Davis's avatar
Silas Davis committed
func useGasNegative(gasLeft *uint64, gasToUse uint64, err *error) bool {
	if *gasLeft >= gasToUse {
		*gasLeft -= gasToUse
		return false
	} else if *err == nil {
		*err = ErrInsufficientGas
	}
	return true
}

// Just like Call() but does not transfer 'value' or modify the callDepth.
Silas Davis's avatar
Silas Davis committed
func (vm *VM) call(caller, callee acm.MutableAccount, code, input []byte, value uint64, gas *uint64) (output []byte, err error) {
	vm.Debugf("(%d) (%X) %X (code=%d) gas: %v (d) %X\n", vm.callDepth, caller.Address().Bytes()[:4], callee.Address(),
		len(callee.Code()), *gas, input)

	var (
		pc     int64 = 0
		stack        = NewStack(dataStackCapacity, gas, &err)
		memory       = vm.memoryProvider()
	)

	for {
		// Use BaseOp gas.
		if useGasNegative(gas, GasBaseOp, &err) {
			return nil, err
		}

		var op = codeGetOp(code, pc)
Silas Davis's avatar
Silas Davis committed
		vm.Debugf("(pc) %-3d (op) %-14s (st) %-4d ", pc, op.String(), stack.Len())
			x, y := stack.PopBigInt(), stack.PopBigInt()
			sum := x.Add(x, y)
			res := stack.PushBigInt(sum)
			vm.Debugf(" %v + %v = %v (%X)\n", x, y, sum, res)
			x, y := stack.PopBigInt(), stack.PopBigInt()
			prod := x.Mul(x, y)
			res := stack.PushBigInt(prod)
			vm.Debugf(" %v * %v = %v (%X)\n", x, y, prod, res)
			x, y := stack.PopBigInt(), stack.PopBigInt()
			diff := x.Sub(x, y)
			res := stack.PushBigInt(diff)
			vm.Debugf(" %v - %v = %v (%X)\n", x, y, diff, res)
			x, y := stack.PopBigInt(), stack.PopBigInt()
			if y.Sign() == 0 {
Silas Davis's avatar
Silas Davis committed
				vm.Debugf(" %x / %x = %v\n", x, y, 0)
				div := x.Div(x, y)
				res := stack.PushBigInt(div)
				vm.Debugf(" %v / %v = %v (%X)\n", x, y, div, res)
			x, y := stack.PopBigIntSigned(), stack.PopBigIntSigned()
			if y.Sign() == 0 {
Silas Davis's avatar
Silas Davis committed
				vm.Debugf(" %x / %x = %v\n", x, y, 0)
				div := x.Div(x, y)
				res := stack.PushBigInt(div)
				vm.Debugf(" %v / %v = %v (%X)\n", x, y, div, res)
			x, y := stack.PopBigInt(), stack.PopBigInt()
			if y.Sign() == 0 {
Silas Davis's avatar
Silas Davis committed
				vm.Debugf(" %v %% %v = %v\n", x, y, 0)
				mod := x.Mod(x, y)
				res := stack.PushBigInt(mod)
				vm.Debugf(" %v %% %v = %v (%X)\n", x, y, mod, res)
			x, y := stack.PopBigIntSigned(), stack.PopBigIntSigned()
			if y.Sign() == 0 {
Silas Davis's avatar
Silas Davis committed
				vm.Debugf(" %v %% %v = %v\n", x, y, 0)
				mod := x.Mod(x, y)
				res := stack.PushBigInt(mod)
				vm.Debugf(" %v %% %v = %v (%X)\n", x, y, mod, res)
			x, y, z := stack.PopBigInt(), stack.PopBigInt(), stack.PopBigInt()
			if z.Sign() == 0 {
Silas Davis's avatar
Silas Davis committed
				vm.Debugf(" %v %% %v = %v\n", x, y, 0)
				add := x.Add(x, y)
				mod := add.Mod(add, z)
				res := stack.PushBigInt(mod)
				vm.Debugf(" %v + %v %% %v = %v (%X)\n", x, y, z, mod, res)
			x, y, z := stack.PopBigInt(), stack.PopBigInt(), stack.PopBigInt()
			if z.Sign() == 0 {
Silas Davis's avatar
Silas Davis committed
				vm.Debugf(" %v %% %v = %v\n", x, y, 0)
				mul := x.Mul(x, y)
				mod := mul.Mod(mul, z)
				res := stack.PushBigInt(mod)
				vm.Debugf(" %v * %v %% %v = %v (%X)\n", x, y, z, mod, res)
			x, y := stack.PopBigInt(), stack.PopBigInt()
			pow := x.Exp(x, y, nil)
			res := stack.PushBigInt(pow)
			vm.Debugf(" %v ** %v = %v (%X)\n", x, y, pow, res)
			back := stack.PopU64()
			if back < Word256Length-1 {
				stack.PushBigInt(SignExtend(back, stack.PopBigInt()))
			x, y := stack.PopBigInt(), stack.PopBigInt()
			if x.Cmp(y) < 0 {
				stack.Push(One256)
				vm.Debugf(" %v < %v = %v\n", x, y, 1)
				vm.Debugf(" %v < %v = %v\n", x, y, 0)
			x, y := stack.PopBigInt(), stack.PopBigInt()
			if x.Cmp(y) > 0 {
				stack.Push(One256)
				vm.Debugf(" %v > %v = %v\n", x, y, 1)
				vm.Debugf(" %v > %v = %v\n", x, y, 0)
			x, y := stack.PopBigIntSigned(), stack.PopBigIntSigned()
			if x.Cmp(y) < 0 {
				stack.Push(One256)
				vm.Debugf(" %v < %v = %v\n", x, y, 1)
				vm.Debugf(" %v < %v = %v\n", x, y, 0)
			x, y := stack.PopBigIntSigned(), stack.PopBigIntSigned()
			if x.Cmp(y) > 0 {
				stack.Push(One256)
				vm.Debugf(" %v > %v = %v\n", x, y, 1)
				vm.Debugf(" %v > %v = %v\n", x, y, 0)
			}

		case EQ: // 0x14
			x, y := stack.Pop(), stack.Pop()
			if bytes.Equal(x[:], y[:]) {
Silas Davis's avatar
Silas Davis committed
				vm.Debugf(" %X == %X = %v\n", x, y, 1)
Silas Davis's avatar
Silas Davis committed
				vm.Debugf(" %X == %X = %v\n", x, y, 0)
			}

		case ISZERO: // 0x15
			x := stack.Pop()
			if x.IsZero() {
				vm.Debugf(" %X == 0 = %v\n", x, 1)
				vm.Debugf(" %X == 0 = %v\n", x, 0)
			}

		case AND: // 0x16
			x, y := stack.Pop(), stack.Pop()
			z := [32]byte{}
			for i := 0; i < 32; i++ {
				z[i] = x[i] & y[i]
			}
			stack.Push(z)
Silas Davis's avatar
Silas Davis committed
			vm.Debugf(" %X & %X = %X\n", x, y, z)

		case OR: // 0x17
			x, y := stack.Pop(), stack.Pop()
			z := [32]byte{}
			for i := 0; i < 32; i++ {
				z[i] = x[i] | y[i]
			}
			stack.Push(z)
Silas Davis's avatar
Silas Davis committed
			vm.Debugf(" %X | %X = %X\n", x, y, z)

		case XOR: // 0x18
			x, y := stack.Pop(), stack.Pop()
			z := [32]byte{}
			for i := 0; i < 32; i++ {
				z[i] = x[i] ^ y[i]
			}
			stack.Push(z)
Silas Davis's avatar
Silas Davis committed
			vm.Debugf(" %X ^ %X = %X\n", x, y, z)

		case NOT: // 0x19
			x := stack.Pop()
			z := [32]byte{}
			for i := 0; i < 32; i++ {
				z[i] = ^x[i]
			}
			stack.Push(z)
Silas Davis's avatar
Silas Davis committed
			vm.Debugf(" !%X = %X\n", x, z)

		case BYTE: // 0x1A
			idx, val := stack.Pop64(), stack.Pop()
			res := byte(0)
			if idx < 32 {
				res = val[idx]
			}
			stack.Push64(int64(res))
Silas Davis's avatar
Silas Davis committed
			vm.Debugf(" => 0x%X\n", res)

		case SHA3: // 0x20
			if useGasNegative(gas, GasSha3, &err) {
				return nil, err
			}
			offset, size := stack.PopBigInt(), stack.PopBigInt()
			data, memErr := memory.Read(offset, size)
			if memErr != nil {
Silas Davis's avatar
Silas Davis committed
				vm.Debugf(" => Memory err: %s", memErr)
				return nil, firstErr(err, ErrMemoryOutOfBounds)
			}
			data = sha3.Sha3(data)
			stack.PushBytes(data)
Silas Davis's avatar
Silas Davis committed
			vm.Debugf(" => (%v) %X\n", size, data)
Silas Davis's avatar
Silas Davis committed
			stack.Push(callee.Address().Word256())
			vm.Debugf(" => %X\n", callee.Address())

		case BALANCE: // 0x31
			addr := stack.Pop()
			if useGasNegative(gas, GasGetAccount, &err) {
				return nil, err
			}
Silas Davis's avatar
Silas Davis committed
			acc, errAcc := vm.state.GetAccount(acm.AddressFromWord256(addr))
			if errAcc != nil {
				return nil, firstErr(err, errAcc)
			}
			if acc == nil {
				return nil, firstErr(err, ErrUnknownAddress)
			}
Silas Davis's avatar
Silas Davis committed
			balance := acc.Balance()
			stack.PushU64(balance)
			vm.Debugf(" => %v (%X)\n", balance, addr)
Silas Davis's avatar
Silas Davis committed
			stack.Push(vm.origin.Word256())
			vm.Debugf(" => %X\n", vm.origin)
Silas Davis's avatar
Silas Davis committed
			stack.Push(caller.Address().Word256())
			vm.Debugf(" => %X\n", caller.Address())
Silas Davis's avatar
Silas Davis committed
			stack.PushU64(value)
			vm.Debugf(" => %v\n", value)

		case CALLDATALOAD: // 0x35
			offset := stack.Pop64()
			data, ok := subslice(input, offset, 32)
			if !ok {
				return nil, firstErr(err, ErrInputOutOfBounds)
			}
			res := LeftPadWord256(data)
			stack.Push(res)
Silas Davis's avatar
Silas Davis committed
			vm.Debugf(" => 0x%X\n", res)

		case CALLDATASIZE: // 0x36
			stack.Push64(int64(len(input)))
Silas Davis's avatar
Silas Davis committed
			vm.Debugf(" => %d\n", len(input))
			memOff := stack.PopBigInt()
			inputOff := stack.Pop64()
			length := stack.Pop64()
			data, ok := subslice(input, inputOff, length)
			if !ok {
				return nil, firstErr(err, ErrInputOutOfBounds)
			}
			memErr := memory.Write(memOff, data)
			if memErr != nil {
Silas Davis's avatar
Silas Davis committed
				vm.Debugf(" => Memory err: %s", memErr)
				return nil, firstErr(err, ErrMemoryOutOfBounds)
			}
Silas Davis's avatar
Silas Davis committed
			vm.Debugf(" => [%v, %v, %v] %X\n", memOff, inputOff, length, data)

		case CODESIZE: // 0x38
			l := int64(len(code))
			stack.Push64(l)
Silas Davis's avatar
Silas Davis committed
			vm.Debugf(" => %d\n", l)
			memOff := stack.PopBigInt()
			codeOff := stack.Pop64()
			length := stack.Pop64()
			data, ok := subslice(code, codeOff, length)
			if !ok {
				return nil, firstErr(err, ErrCodeOutOfBounds)
			}
			memErr := memory.Write(memOff, data)
			if memErr != nil {
Silas Davis's avatar
Silas Davis committed
				vm.Debugf(" => Memory err: %s", memErr)
				return nil, firstErr(err, ErrMemoryOutOfBounds)
			}
Silas Davis's avatar
Silas Davis committed
			vm.Debugf(" => [%v, %v, %v] %X\n", memOff, codeOff, length, data)

		case GASPRICE_DEPRECATED: // 0x3A
			stack.Push(Zero256)
Silas Davis's avatar
Silas Davis committed
			vm.Debugf(" => %X (GASPRICE IS DEPRECATED)\n")

		case EXTCODESIZE: // 0x3B
			addr := stack.Pop()
			if useGasNegative(gas, GasGetAccount, &err) {
				return nil, err
			}
Silas Davis's avatar
Silas Davis committed
			acc, errAcc := vm.state.GetAccount(acm.AddressFromWord256(addr))
			if errAcc != nil {
				return nil, firstErr(err, errAcc)
			}
				if _, ok := registeredNativeContracts[addr]; !ok {
					return nil, firstErr(err, ErrUnknownAddress)
				}
Silas Davis's avatar
Silas Davis committed
				vm.Debugf(" => returning code size of 1 to indicated existence of native contract at %X\n", addr)
			} else {
Silas Davis's avatar
Silas Davis committed
				code := acc.Code()
				l := int64(len(code))
				stack.Push64(l)
Silas Davis's avatar
Silas Davis committed
				vm.Debugf(" => %d\n", l)
			}
		case EXTCODECOPY: // 0x3C
			addr := stack.Pop()
			if useGasNegative(gas, GasGetAccount, &err) {
				return nil, err
			}
Silas Davis's avatar
Silas Davis committed
			acc, errAcc := vm.state.GetAccount(acm.AddressFromWord256(addr))
			if errAcc != nil {
				return nil, firstErr(err, errAcc)
			}
				if _, ok := registeredNativeContracts[addr]; ok {
Silas Davis's avatar
Silas Davis committed
					vm.Debugf(" => attempted to copy native contract at %X but this is not supported\n", addr)
					return nil, firstErr(err, ErrNativeContractCodeCopy)
RJ Catalano's avatar
RJ Catalano committed
				}
				return nil, firstErr(err, ErrUnknownAddress)
Silas Davis's avatar
Silas Davis committed
			code := acc.Code()
			memOff := stack.PopBigInt()
			codeOff := stack.Pop64()
			length := stack.Pop64()
			data, ok := subslice(code, codeOff, length)
			if !ok {
				return nil, firstErr(err, ErrCodeOutOfBounds)
			}
			memErr := memory.Write(memOff, data)
			if memErr != nil {
Silas Davis's avatar
Silas Davis committed
				vm.Debugf(" => Memory err: %s", memErr)
				return nil, firstErr(err, ErrMemoryOutOfBounds)
			}
Silas Davis's avatar
Silas Davis committed
			vm.Debugf(" => [%v, %v, %v] %X\n", memOff, codeOff, length, data)

		case BLOCKHASH: // 0x40
			stack.Push(Zero256)
Silas Davis's avatar
Silas Davis committed
			vm.Debugf(" => 0x%X (NOT SUPPORTED)\n", stack.Peek().Bytes())

		case COINBASE: // 0x41
			stack.Push(Zero256)
Silas Davis's avatar
Silas Davis committed
			vm.Debugf(" => 0x%X (NOT SUPPORTED)\n", stack.Peek().Bytes())

		case TIMESTAMP: // 0x42
			time := vm.params.BlockTime
			stack.Push64(int64(time))
Silas Davis's avatar
Silas Davis committed
			vm.Debugf(" => 0x%X\n", time)
Silas Davis's avatar
Silas Davis committed
			number := vm.params.BlockHeight
			stack.PushU64(number)
			vm.Debugf(" => 0x%X\n", number)
Silas Davis's avatar
Silas Davis committed
			stack.PushU64(vm.params.GasLimit)
			vm.Debugf(" => %v\n", vm.params.GasLimit)

		case POP: // 0x50
			popped := stack.Pop()
Silas Davis's avatar
Silas Davis committed
			vm.Debugf(" => 0x%X\n", popped)
			offset := stack.PopBigInt()
			data, memErr := memory.Read(offset, BigWord256Length)
Silas Davis's avatar
Silas Davis committed
				vm.Debugf(" => Memory err: %s", memErr)
				return nil, firstErr(err, ErrMemoryOutOfBounds)
			}
			stack.Push(LeftPadWord256(data))
Silas Davis's avatar
Silas Davis committed
			vm.Debugf(" => 0x%X @ 0x%X\n", data, offset)
			offset, data := stack.PopBigInt(), stack.Pop()
			memErr := memory.Write(offset, data.Bytes())
			if memErr != nil {
Silas Davis's avatar
Silas Davis committed
				vm.Debugf(" => Memory err: %s", memErr)
				return nil, firstErr(err, ErrMemoryOutOfBounds)
			}
Silas Davis's avatar
Silas Davis committed
			vm.Debugf(" => 0x%X @ 0x%X\n", data, offset)
			offset, val := stack.PopBigInt(), byte(stack.Pop64()&0xFF)
			memErr := memory.Write(offset, []byte{val})
			if memErr != nil {
Silas Davis's avatar
Silas Davis committed
				vm.Debugf(" => Memory err: %s", memErr)
				return nil, firstErr(err, ErrMemoryOutOfBounds)
			}
Silas Davis's avatar
Silas Davis committed
			vm.Debugf(" => [%v] 0x%X\n", offset, val)
Silas Davis's avatar
Silas Davis committed
			data, errSto := vm.state.GetStorage(callee.Address(), loc)
			if errSto != nil {
				return nil, firstErr(err, errSto)
			}
Silas Davis's avatar
Silas Davis committed
			vm.Debugf(" {0x%X : 0x%X}\n", loc, data)

		case SSTORE: // 0x55
			loc, data := stack.Pop(), stack.Pop()
			if useGasNegative(gas, GasStorageUpdate, &err) {
				return nil, err
			}
Silas Davis's avatar
Silas Davis committed
			vm.state.SetStorage(callee.Address(), loc, data)
			vm.Debugf(" {0x%X : 0x%X}\n", loc, data)
			jumpErr := vm.jump(code, stack.Pop64(), &pc)
			if jumpErr != nil {
				vm.Debugf(" => JUMP err: %s", jumpErr)
				return nil, firstErr(err, jumpErr)
			}
			continue

		case JUMPI: // 0x57
			pos, cond := stack.Pop64(), stack.Pop()
			if !cond.IsZero() {
				jumpErr := vm.jump(code, pos, &pc)
				if jumpErr != nil {
					return nil, firstErr(err, jumpErr)
Silas Davis's avatar
Silas Davis committed
			vm.Debugf(" ~> false\n")

		case PC: // 0x58
			stack.Push64(pc)

		case MSIZE: // 0x59
			// Note: Solidity will write to this offset expecting to find guaranteed
			// free memory to be allocated for it if a subsequent MSTORE is made to
			// this offset.
			capacity := memory.Capacity()
			stack.PushBigInt(capacity)
Silas Davis's avatar
Silas Davis committed
			vm.Debugf(" => 0x%X\n", capacity)
Silas Davis's avatar
Silas Davis committed
			stack.PushU64(*gas)
			vm.Debugf(" => %X\n", *gas)
Silas Davis's avatar
Silas Davis committed
			vm.Debugf("\n")
			// Do nothing

		case PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16, PUSH17, PUSH18, PUSH19, PUSH20, PUSH21, PUSH22, PUSH23, PUSH24, PUSH25, PUSH26, PUSH27, PUSH28, PUSH29, PUSH30, PUSH31, PUSH32:
			a := int64(op - PUSH1 + 1)
			codeSegment, ok := subslice(code, pc+1, a)
			if !ok {
				return nil, firstErr(err, ErrCodeOutOfBounds)
			}
			res := LeftPadWord256(codeSegment)
			stack.Push(res)
			pc += a
Silas Davis's avatar
Silas Davis committed
			vm.Debugf(" => 0x%X\n", res)
			//stack.Print(10)

		case DUP1, DUP2, DUP3, DUP4, DUP5, DUP6, DUP7, DUP8, DUP9, DUP10, DUP11, DUP12, DUP13, DUP14, DUP15, DUP16:
			n := int(op - DUP1 + 1)
			stack.Dup(n)
Silas Davis's avatar
Silas Davis committed
			vm.Debugf(" => [%d] 0x%X\n", n, stack.Peek().Bytes())

		case SWAP1, SWAP2, SWAP3, SWAP4, SWAP5, SWAP6, SWAP7, SWAP8, SWAP9, SWAP10, SWAP11, SWAP12, SWAP13, SWAP14, SWAP15, SWAP16:
			n := int(op - SWAP1 + 2)
			stack.Swap(n)
Silas Davis's avatar
Silas Davis committed
			vm.Debugf(" => [%d] %X\n", n, stack.Peek())
			//stack.Print(10)

		case LOG0, LOG1, LOG2, LOG3, LOG4:
			n := int(op - LOG0)
			topics := make([]Word256, n)
			offset, size := stack.PopBigInt(), stack.PopBigInt()
			for i := 0; i < n; i++ {
				topics[i] = stack.Pop()
			}
			data, memErr := memory.Read(offset, size)
			if memErr != nil {
Silas Davis's avatar
Silas Davis committed
				vm.Debugf(" => Memory err: %s", memErr)
				return nil, firstErr(err, ErrMemoryOutOfBounds)
			}
			if vm.publisher != nil {
Silas Davis's avatar
Silas Davis committed
				eventID := events.EventStringLogEvent(callee.Address())
				fmt.Printf("eventID: %s\n", eventID)
				events.PublishLogEvent(vm.publisher, callee.Address(), &events.EventDataLog{
Silas Davis's avatar
Silas Davis committed
					Address: callee.Address(),
					Topics:  topics,
					Data:    data,
					Height:  vm.params.BlockHeight,
Silas Davis's avatar
Silas Davis committed
			vm.Debugf(" => T:%X D:%X\n", topics, data)
Silas Davis's avatar
Silas Davis committed
			if !HasPermission(vm.state, callee, permission.CreateContract) {
				return nil, ErrPermission{"create_contract"}
			}
Silas Davis's avatar
Silas Davis committed
			contractValue := stack.PopU64()
			offset, size := stack.PopBigInt(), stack.PopBigInt()
			input, memErr := memory.Read(offset, size)
			if memErr != nil {
Silas Davis's avatar
Silas Davis committed
				vm.Debugf(" => Memory err: %s", memErr)
				return nil, firstErr(err, ErrMemoryOutOfBounds)
			}

			// Check balance
Silas Davis's avatar
Silas Davis committed
			if callee.Balance() < uint64(contractValue) {
				return nil, firstErr(err, ErrInsufficientBalance)
			}

			// TODO charge for gas to create account _ the code length * GasCreateByte
Silas Davis's avatar
Silas Davis committed
			newAccount := DeriveNewAccount(callee, permission.GlobalAccountPermissions(vm.state))
			vm.state.UpdateAccount(newAccount)
Ethan Buchman's avatar
Ethan Buchman committed

			// Run the input to get the contract code.
			// NOTE: no need to copy 'input' as per Call contract.
			ret, err_ := vm.Call(callee, newAccount, input, input, contractValue, gas)
			if err_ != nil {
				stack.Push(Zero256)
			} else {
Silas Davis's avatar
Silas Davis committed
				newAccount.SetCode(ret) // Set the code (ret need not be copied as per Call contract)
				stack.Push(newAccount.Address().Word256())
		case CALL, CALLCODE, DELEGATECALL: // 0xF1, 0xF2, 0xF4
Silas Davis's avatar
Silas Davis committed
			if !HasPermission(vm.state, callee, permission.Call) {
				return nil, ErrPermission{"call"}
			}
Silas Davis's avatar
Silas Davis committed
			gasLimit := stack.PopU64()
			addr := stack.Pop()
			// NOTE: for DELEGATECALL value is preserved from the original
			// caller, as such it is not stored on stack as an argument
			// for DELEGATECALL and should not be popped.  Instead previous
			// caller value is used.  for CALL and CALLCODE value is stored
			// on stack and needs to be overwritten from the given value.
			if op != DELEGATECALL {
Silas Davis's avatar
Silas Davis committed
				value = stack.PopU64()
			inOffset, inSize := stack.PopBigInt(), stack.PopBigInt() // inputs
			retOffset, retSize := stack.PopBigInt(), stack.Pop64()   // outputs
Silas Davis's avatar
Silas Davis committed
			vm.Debugf(" => %X\n", addr)

			// Get the arguments from the memory
			args, memErr := memory.Read(inOffset, inSize)
			if memErr != nil {
Silas Davis's avatar
Silas Davis committed
				vm.Debugf(" => Memory err: %s", memErr)
				return nil, firstErr(err, ErrMemoryOutOfBounds)
			}

			// Ensure that gasLimit is reasonable
			if *gas < gasLimit {
				return nil, firstErr(err, ErrInsufficientGas)
			} else {
				*gas -= gasLimit
				// NOTE: we will return any used gas later.
			}

			// Begin execution
			var ret []byte
			var callErr error
			if nativeContract := registeredNativeContracts[addr]; nativeContract != nil {
				// Native contract
				ret, callErr = nativeContract(vm.state, callee, args, &gasLimit, vm.logger)

				// for now we fire the Call event. maybe later we'll fire more particulars
				var exception string
				if callErr != nil {
					exception = callErr.Error()
Silas Davis's avatar
Silas Davis committed
				// NOTE: these fire call go_events and not particular go_events for eg name reg or permissions
				vm.fireCallEvent(&exception, &ret, callee.Address(), acm.AddressFromWord256(addr), args, value, &gasLimit)
				if useGasNegative(gas, GasGetAccount, &callErr) {
					return nil, callErr
Silas Davis's avatar
Silas Davis committed
				acc, errAcc := acm.GetMutableAccount(vm.state, acm.AddressFromWord256(addr))
				if errAcc != nil {
					return nil, firstErr(callErr, errAcc)
Silas Davis's avatar
Silas Davis committed
				}
				// since CALL is used also for sending funds,
				// acc may not exist yet. This is an error for
				// CALLCODE, but not for CALL, though I don't think
				// ethereum actually cares
				if op == CALLCODE {
					if acc == nil {
						return nil, firstErr(callErr, ErrUnknownAddress)
					ret, callErr = vm.Call(callee, callee, acc.Code(), args, value, &gasLimit)
				} else if op == DELEGATECALL {
					if acc == nil {
						return nil, firstErr(callErr, ErrUnknownAddress)
					ret, callErr = vm.DelegateCall(caller, callee, acc.Code(), args, value, &gasLimit)
Ethan Buchman's avatar
Ethan Buchman committed
					// nil account means we're sending funds to a new account
Silas Davis's avatar
Silas Davis committed
						if !HasPermission(vm.state, caller, permission.CreateAccount) {
							return nil, ErrPermission{"create_account"}
						}
Silas Davis's avatar
Silas Davis committed
						acc = (&acm.ConcreteAccount{Address: acm.AddressFromWord256(addr)}).MutableAccount()
Ethan Buchman's avatar
Ethan Buchman committed
					// add account to the tx cache
Silas Davis's avatar
Silas Davis committed
					vm.state.UpdateAccount(acc)
					ret, callErr = vm.Call(callee, acc, acc.Code(), args, value, &gasLimit)
			if callErr != nil {
				vm.Debugf("error on call: %s\n", callErr.Error())
				// TODO: we probably don't want to return the error - decide
				//err = firstErr(err, callErr)
				stack.Push(Zero256)
			} else {
				stack.Push(One256)

				// Should probably only be necessary when there is no return value and
				// ret is empty, but since EVM expects retSize to be respected this will
				// defensively pad or truncate the portion of ret to be returned.
				memErr := memory.Write(retOffset, RightPadBytes(ret, int(retSize)))
				if memErr != nil {
Silas Davis's avatar
Silas Davis committed
					vm.Debugf(" => Memory err: %s", memErr)
					return nil, firstErr(callErr, ErrMemoryOutOfBounds)
Silas Davis's avatar
Silas Davis committed
			vm.Debugf("resume %s (%v)\n", callee.Address(), gas)
			offset, size := stack.PopBigInt(), stack.PopBigInt()
			output, memErr := memory.Read(offset, size)
			if memErr != nil {
Silas Davis's avatar
Silas Davis committed
				vm.Debugf(" => Memory err: %s", memErr)
				return nil, firstErr(err, ErrMemoryOutOfBounds)
			}
Silas Davis's avatar
Silas Davis committed
			vm.Debugf(" => [%v, %v] (%d) 0x%X\n", offset, size, len(output), output)
		case SELFDESTRUCT: // 0xFF
			addr := stack.Pop()
			if useGasNegative(gas, GasGetAccount, &err) {
				return nil, err
			}
			// TODO if the receiver is , then make it the fee. (?)
			// TODO: create account if doesn't exist (no reason not to)
Silas Davis's avatar
Silas Davis committed
			receiver, errAcc := acm.GetMutableAccount(vm.state, acm.AddressFromWord256(addr))
			if errAcc != nil {
				return nil, firstErr(err, errAcc)
			}
			if receiver == nil {
				return nil, firstErr(err, ErrUnknownAddress)
			}
Silas Davis's avatar
Silas Davis committed

			receiver, errAdd := receiver.AddToBalance(callee.Balance())
			if errAdd != nil {
				return nil, firstErr(err, errAdd)
			}
Silas Davis's avatar
Silas Davis committed
			vm.state.UpdateAccount(receiver)
			vm.state.RemoveAccount(callee.Address())
			vm.Debugf(" => (%X) %v\n", addr[:4], callee.Balance())
			fallthrough

		case STOP: // 0x00
			return nil, nil

		default:
Silas Davis's avatar
Silas Davis committed
			vm.Debugf("(pc) %-3v Invalid opcode %X\n", pc, op)
			return nil, fmt.Errorf("Invalid opcode %X", op)
		}

		pc++
	}
}

// TODO: [Silas] this function seems extremely dubious to me. It was being used
// in circumstances where its behaviour did not match the intention. It's bounds
// check is strange (treats a read at data length as a zero read of arbitrary length)
// I have left it in for now to be conservative about where its behaviour is being used
//
// Returns a subslice from offset of length length and a bool
// (true iff slice was possible). If the subslice
// extends past the end of data it returns A COPY of the segment at the end of
// data padded with zeroes on the right. If offset == len(data) it returns all
// zeroes. if offset > len(data) it returns a false
func subslice(data []byte, offset, length int64) (ret []byte, ok bool) {
	size := int64(len(data))
	if size < offset || offset < 0 || length < 0 {
		return nil, false
	} else if size < offset+length {
		ret, ok = data[offset:], true
		ret = RightPadBytes(ret, 32)
	} else {
		ret, ok = data[offset:offset+length], true
	}
	return
}

func codeGetOp(code []byte, n int64) OpCode {
	if int64(len(code)) <= n {
		return OpCode(0) // stop
	} else {
		return OpCode(code[n])
	}
}

Silas Davis's avatar
Silas Davis committed
func (vm *VM) jump(code []byte, to int64, pc *int64) (err error) {
	dest := codeGetOp(code, to)
	if dest != JUMPDEST {
Silas Davis's avatar
Silas Davis committed
		vm.Debugf(" ~> %v invalid jump dest %v\n", to, dest)
Silas Davis's avatar
Silas Davis committed
	vm.Debugf(" ~> %v\n", to)
	*pc = to
	return nil
}

func firstErr(errA, errB error) error {
	if errA != nil {
		return errA
	} else {
		return errB
	}
}

Silas Davis's avatar
Silas Davis committed
func transfer(from, to acm.MutableAccount, amount uint64) error {
	if from.Balance() < amount {
		return ErrInsufficientBalance
	} else {
Silas Davis's avatar
Silas Davis committed
		from.SubtractFromBalance(amount)
		_, err := to.AddToBalance(amount)
		if err != nil {
			return err
		}