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.
import (
"bytes"
"errors"
"fmt"
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"
)
var (
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.
type Params struct {
BlockHeight uint64
BlockHash Word256
BlockTime int64
GasLimit uint64
}
type VM struct {
memoryProvider func() Memory
params Params
publisher event.Publisher
func NewVM(state acm.StateWriter, memoryProvider func() Memory, params Params, origin acm.Address, txid []byte,
logger logging_types.InfoTraceLogger) *VM {
return &VM{
memoryProvider: memoryProvider,
params: params,
origin: origin,
callDepth: 0,
txid: txid,
logger: logging.WithScope(logger, "NewVM"),
}
}
logging.TraceMsg(vm.logger, fmt.Sprintf(format, a...), "tag", "vm_debug")
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
committed
// this function returns false.
func HasPermission(state acm.StateWriter, acc acm.Account, perm ptypes.PermFlag) bool {
Silas Davis
committed
value, _ := acc.Permissions().Base.Compose(permission.GlobalAccountPermissions(state).Base).Get(perm)
return value
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{
&events.CallData{Caller: callerAddress, Callee: calleeAddress, Data: input, Value: value, Gas: *gas},
vm.origin,
vm.txid,
*output,
*exception,
})
}
}
// 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
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)
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
}
}
}
return
}
// 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.
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)
Benjamin Bollen
committed
// NOTE: [ben] hotfix for issue 371;
// introduce event EventStringAccDelegateCall Acc/%s/DelegateCall
Benjamin Bollen
committed
// 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.
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.
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)
vm.Debugf("(pc) %-3d (op) %-14s (st) %-4d ", pc, op.String(), stack.Len())
switch op {
case ADD: // 0x01
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)
case MUL: // 0x02
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)
case SUB: // 0x03
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)
case DIV: // 0x04
x, y := stack.PopBigInt(), stack.PopBigInt()
if y.Sign() == 0 {
stack.Push(Zero256)
} else {
div := x.Div(x, y)
res := stack.PushBigInt(div)
vm.Debugf(" %v / %v = %v (%X)\n", x, y, div, res)
}
case SDIV: // 0x05
x, y := stack.PopBigIntSigned(), stack.PopBigIntSigned()
if y.Sign() == 0 {
stack.Push(Zero256)
} else {
div := x.Div(x, y)
res := stack.PushBigInt(div)
vm.Debugf(" %v / %v = %v (%X)\n", x, y, div, res)
}
case MOD: // 0x06
x, y := stack.PopBigInt(), stack.PopBigInt()
if y.Sign() == 0 {
stack.Push(Zero256)
} else {
mod := x.Mod(x, y)
res := stack.PushBigInt(mod)
vm.Debugf(" %v %% %v = %v (%X)\n", x, y, mod, res)
}
case SMOD: // 0x07
x, y := stack.PopBigIntSigned(), stack.PopBigIntSigned()
if y.Sign() == 0 {
stack.Push(Zero256)
} else {
mod := x.Mod(x, y)
res := stack.PushBigInt(mod)
vm.Debugf(" %v %% %v = %v (%X)\n", x, y, mod, res)
}
case ADDMOD: // 0x08
x, y, z := stack.PopBigInt(), stack.PopBigInt(), stack.PopBigInt()
if z.Sign() == 0 {
stack.Push(Zero256)
} else {
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)
}
case MULMOD: // 0x09
x, y, z := stack.PopBigInt(), stack.PopBigInt(), stack.PopBigInt()
if z.Sign() == 0 {
stack.Push(Zero256)
} else {
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)
}
case EXP: // 0x0A
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)
case SIGNEXTEND: // 0x0B
back := stack.PopU64()
if back < Word256Length-1 {
stack.PushBigInt(SignExtend(back, stack.PopBigInt()))
}
case LT: // 0x10
x, y := stack.PopBigInt(), stack.PopBigInt()
if x.Cmp(y) < 0 {
stack.Push(One256)
vm.Debugf(" %v < %v = %v\n", x, y, 1)
} else {
stack.Push(Zero256)
vm.Debugf(" %v < %v = %v\n", x, y, 0)
}
case GT: // 0x11
x, y := stack.PopBigInt(), stack.PopBigInt()
if x.Cmp(y) > 0 {
stack.Push(One256)
vm.Debugf(" %v > %v = %v\n", x, y, 1)
} else {
stack.Push(Zero256)
vm.Debugf(" %v > %v = %v\n", x, y, 0)
}
case SLT: // 0x12
x, y := stack.PopBigIntSigned(), stack.PopBigIntSigned()
if x.Cmp(y) < 0 {
stack.Push(One256)
vm.Debugf(" %v < %v = %v\n", x, y, 1)
} else {
stack.Push(Zero256)
vm.Debugf(" %v < %v = %v\n", x, y, 0)
}
case SGT: // 0x13
x, y := stack.PopBigIntSigned(), stack.PopBigIntSigned()
if x.Cmp(y) > 0 {
stack.Push(One256)
vm.Debugf(" %v > %v = %v\n", x, y, 1)
} else {
stack.Push(Zero256)
vm.Debugf(" %v > %v = %v\n", x, y, 0)
}
case EQ: // 0x14
x, y := stack.Pop(), stack.Pop()
if bytes.Equal(x[:], y[:]) {
} else {
stack.Push(Zero256)
}
case ISZERO: // 0x15
x := stack.Pop()
if x.IsZero() {
vm.Debugf(" %X == 0 = %v\n", x, 1)
} else {
stack.Push(Zero256)
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)
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)
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)
case NOT: // 0x19
x := stack.Pop()
z := [32]byte{}
for i := 0; i < 32; i++ {
z[i] = ^x[i]
}
stack.Push(z)
case BYTE: // 0x1A
idx, val := stack.Pop64(), stack.Pop()
res := byte(0)
if idx < 32 {
res = val[idx]
}
stack.Push64(int64(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 {
return nil, firstErr(err, ErrMemoryOutOfBounds)
}
data = sha3.Sha3(data)
stack.PushBytes(data)
case ADDRESS: // 0x30
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
}
acc, errAcc := vm.state.GetAccount(acm.AddressFromWord256(addr))
if errAcc != nil {
return nil, firstErr(err, errAcc)
}
if acc == nil {
return nil, firstErr(err, ErrUnknownAddress)
}
balance := acc.Balance()
stack.PushU64(balance)
vm.Debugf(" => %v (%X)\n", balance, addr)
case ORIGIN: // 0x32
stack.Push(vm.origin.Word256())
vm.Debugf(" => %X\n", vm.origin)
case CALLER: // 0x33
stack.Push(caller.Address().Word256())
vm.Debugf(" => %X\n", caller.Address())
case CALLVALUE: // 0x34
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)
case CALLDATASIZE: // 0x36
stack.Push64(int64(len(input)))
case CALLDATACOPY: // 0x37
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 {
return nil, firstErr(err, ErrMemoryOutOfBounds)
}
vm.Debugf(" => [%v, %v, %v] %X\n", memOff, inputOff, length, data)
case CODESIZE: // 0x38
l := int64(len(code))
stack.Push64(l)
case CODECOPY: // 0x39
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 {
return nil, firstErr(err, ErrMemoryOutOfBounds)
}
vm.Debugf(" => [%v, %v, %v] %X\n", memOff, codeOff, length, data)
case GASPRICE_DEPRECATED: // 0x3A
stack.Push(Zero256)
case EXTCODESIZE: // 0x3B
addr := stack.Pop()
if useGasNegative(gas, GasGetAccount, &err) {
return nil, err
}
acc, errAcc := vm.state.GetAccount(acm.AddressFromWord256(addr))
if errAcc != nil {
return nil, firstErr(err, errAcc)
}
if acc == nil {
if _, ok := registeredNativeContracts[addr]; !ok {
return nil, firstErr(err, ErrUnknownAddress)
}
vm.Debugf(" => returning code size of 1 to indicated existence of native contract at %X\n", addr)
stack.Push(One256)
}
case EXTCODECOPY: // 0x3C
addr := stack.Pop()
if useGasNegative(gas, GasGetAccount, &err) {
return nil, err
}
acc, errAcc := vm.state.GetAccount(acm.AddressFromWord256(addr))
if errAcc != nil {
return nil, firstErr(err, errAcc)
}
if acc == nil {
if _, ok := registeredNativeContracts[addr]; ok {
vm.Debugf(" => attempted to copy native contract at %X but this is not supported\n", addr)
return nil, firstErr(err, ErrNativeContractCodeCopy)
return nil, firstErr(err, ErrUnknownAddress)
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 {
return nil, firstErr(err, ErrMemoryOutOfBounds)
}
vm.Debugf(" => [%v, %v, %v] %X\n", memOff, codeOff, length, data)
case BLOCKHASH: // 0x40
stack.Push(Zero256)
vm.Debugf(" => 0x%X (NOT SUPPORTED)\n", stack.Peek().Bytes())
case COINBASE: // 0x41
stack.Push(Zero256)
vm.Debugf(" => 0x%X (NOT SUPPORTED)\n", stack.Peek().Bytes())
case TIMESTAMP: // 0x42
time := vm.params.BlockTime
stack.Push64(int64(time))
case BLOCKHEIGHT: // 0x43
number := vm.params.BlockHeight
stack.PushU64(number)
vm.Debugf(" => 0x%X\n", number)
case GASLIMIT: // 0x45
stack.PushU64(vm.params.GasLimit)
vm.Debugf(" => %v\n", vm.params.GasLimit)
case POP: // 0x50
popped := stack.Pop()
case MLOAD: // 0x51
offset := stack.PopBigInt()
data, memErr := memory.Read(offset, BigWord256Length)
return nil, firstErr(err, ErrMemoryOutOfBounds)
}
stack.Push(LeftPadWord256(data))
case MSTORE: // 0x52
offset, data := stack.PopBigInt(), stack.Pop()
memErr := memory.Write(offset, data.Bytes())
if memErr != nil {
return nil, firstErr(err, ErrMemoryOutOfBounds)
}
case MSTORE8: // 0x53
offset, val := stack.PopBigInt(), byte(stack.Pop64()&0xFF)
memErr := memory.Write(offset, []byte{val})
if memErr != nil {
return nil, firstErr(err, ErrMemoryOutOfBounds)
}
case SLOAD: // 0x54
loc := stack.Pop()
data, errSto := vm.state.GetStorage(callee.Address(), loc)
if errSto != nil {
return nil, firstErr(err, errSto)
}
stack.Push(data)
case SSTORE: // 0x55
loc, data := stack.Pop(), stack.Pop()
if useGasNegative(gas, GasStorageUpdate, &err) {
return nil, err
}
vm.state.SetStorage(callee.Address(), loc, data)
vm.Debugf(" {0x%X : 0x%X}\n", loc, data)
case JUMP: // 0x56
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)
}
continue
}
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()
case GAS: // 0x5A
case JUMPDEST: // 0x5B
// 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
//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)
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)
//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 {
return nil, firstErr(err, ErrMemoryOutOfBounds)
}
if vm.publisher != nil {
fmt.Printf("eventID: %s\n", eventID)
events.PublishLogEvent(vm.publisher, callee.Address(), &events.EventDataLog{
Address: callee.Address(),
Topics: topics,
Data: data,
Height: vm.params.BlockHeight,
case CREATE: // 0xF0
if !HasPermission(vm.state, callee, permission.CreateContract) {
return nil, ErrPermission{"create_contract"}
}
offset, size := stack.PopBigInt(), stack.PopBigInt()
input, memErr := memory.Read(offset, size)
if memErr != nil {
return nil, firstErr(err, ErrMemoryOutOfBounds)
}
// Check balance
return nil, firstErr(err, ErrInsufficientBalance)
}
// TODO charge for gas to create account _ the code length * GasCreateByte
newAccount := DeriveNewAccount(callee, permission.GlobalAccountPermissions(vm.state))
vm.state.UpdateAccount(newAccount)
// 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 {
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
return nil, ErrPermission{"call"}
}
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 {
inOffset, inSize := stack.PopBigInt(), stack.PopBigInt() // inputs
retOffset, retSize := stack.PopBigInt(), stack.Pop64() // outputs
// Get the arguments from the memory
args, memErr := memory.Read(inOffset, inSize)
if memErr != nil {
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
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()
// 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)
} else {
// EVM contract
if useGasNegative(gas, GasGetAccount, &callErr) {
return nil, callErr
acc, errAcc := acm.GetMutableAccount(vm.state, acm.AddressFromWord256(addr))
if errAcc != nil {
return nil, firstErr(callErr, errAcc)
// 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)
} else {
// nil account means we're sending funds to a new account
if acc == nil {
if !HasPermission(vm.state, caller, permission.CreateAccount) {
return nil, ErrPermission{"create_account"}
}
acc = (&acm.ConcreteAccount{Address: acm.AddressFromWord256(addr)}).MutableAccount()
ret, callErr = vm.Call(callee, acc, acc.Code(), args, value, &gasLimit)
}
}
// Push result
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 {
return nil, firstErr(callErr, ErrMemoryOutOfBounds)
}
}
// Handle remaining gas.
*gas += gasLimit
case RETURN: // 0xF3
offset, size := stack.PopBigInt(), stack.PopBigInt()
output, memErr := memory.Read(offset, size)
if memErr != nil {
return nil, firstErr(err, ErrMemoryOutOfBounds)
}
vm.Debugf(" => [%v, %v] (%d) 0x%X\n", offset, size, len(output), output)
return output, nil
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)
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)
}
receiver, errAdd := receiver.AddToBalance(callee.Balance())
if errAdd != nil {
return nil, firstErr(err, errAdd)
}
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:
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])
}
}
func (vm *VM) jump(code []byte, to int64, pc *int64) (err error) {
dest := codeGetOp(code, to)
if dest != JUMPDEST {
return ErrInvalidJumpDest
}
*pc = to
return nil
}
func firstErr(errA, errB error) error {
if errA != nil {
return errA
} else {
return errB
}
}
func transfer(from, to acm.MutableAccount, amount uint64) error {
if from.Balance() < amount {
return ErrInsufficientBalance
} else {
_, err := to.AddToBalance(amount)
if err != nil {
return err
}
return nil