vm makeover

This commit is contained in:
Jae Kwon
2015-03-17 21:46:26 -07:00
parent f03547007a
commit c21369cebd
11 changed files with 607 additions and 684 deletions

627
vm/vm.go
View File

@ -1,84 +1,96 @@
package vm
import (
"errors"
"fmt"
"math"
sm "github.com/tendermint/tendermint/state"
. "github.com/tendermint/tendermint/common"
"github.com/tendermint/tendermint/vm/sha3"
)
type Vm struct {
VMEnvironment
var (
ErrInsufficientBalance = errors.New("Insufficient balance")
ErrInvalidJumpDest = errors.New("Invalid jump dest")
ErrInsufficientGas = errors.New("Insuffient 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")
)
const (
dataStackCapacity = 1024
callStackCapacity = 100 // TODO ensure usage.
memoryCapacity = 1024 * 1024 // 1 MB
)
type VM struct {
appState AppState
params VMParams
callDepth int
}
func NewVM(appState AppState, params VMParams) *Vm {
vmEnv := NewVMEnvironment(appState, params)
type VMParams struct {
BlockHeight uint64
BlockHash Word
BlockTime int64
GasLimit uint64
GasPrice uint64
CallStackLimit uint64
Origin Word
}
func NewVM(appState AppState, params VMParams) *VM {
return &VM{
VMEnvironment: vmEnv,
appState: appState,
params: params,
callDepth: 0,
}
}
// feeLimit: the maximum the caller is willing to pay for fees.
// gasLimit: the maximum gas that will be run.
func (vm *Vm) RunTransaction(caller, target *Account, feeLimit, gasLimit, value uint64, input []byte) (output []byte, err error) {
if len(target.Code) == 0 {
panic("RunTransaction() requires target with code")
}
// Check the gasLimit vs feeLimit
// TODO
/*
// When running a transaction, the caller the pays for the fees.
// Check caller's account balance vs feeLimit and value
if caller.Balance < (feeLimit + value) {
return nil, ErrInsufficientAccountBalance
return nil, ErrInsufficientBalance
}
// Deduct balance from caller.
caller.Balance -= (feeLimit + value)
vm.SetupTransaction(caller, target, gasLimit, value, input)
*/
fmt.Printf("(%d) (%X) %X (code=%d) gas: %v (d) %X\n", vm.CallStackDepth(), caller.Address[:4], target.Address, len(target.Code), gasLimit, input)
// gas: the maximum gas that will be run.
// When the function returns, *gas will be the amount of remaining gas.
func (vm *VM) Call(caller, callee *Account, code, input []byte, value uint64, gas *uint64) (output []byte, err error) {
/*
if p := Precompiled[string(me.Address())]; p != nil {
return vm.RunPrecompiled(p, callData, context)
}
*/
if len(callee.Code) == 0 {
panic("Call() requires callee with code")
}
//-----------------------------------
fmt.Printf("(%d) (%X) %X (code=%d) gas: %v (d) %X\n", vm.callDepth, caller.Address[:4], callee.Address, len(callee.Code), *gas, input)
// By the time we're here, the related VMCall context is already appended onto VMEnvironment.callStack
var (
code = target.Code
pc uint64 = 0
gas uint64 = call.gasLimit
err error = nil
stack = NewStack(defaultDataStackCapacity, &gas, &err)
memory = NewMemory(&gas, &err)
// volatile, convenience
ok = false
// TODO review this code.
jump = func(from, to uint64) error {
dest := CodeGetOp(code, to)
if dest != JUMPDEST {
return ErrInvalidJumpDest
}
pc = to
fmt.Printf(" ~> %v\n", to)
return nil
}
stack = NewStack(dataStackCapacity, gas, &err)
memory = make([]byte, memoryCapacity)
ok = false // convenience
)
for {
var op = CodeGetOp(code, pc)
fmt.Printf("(pc) %-3d -o- %-14s (m) %-4d (s) %-4d ", pc, op.String(), mem.Len(), stack.Len())
var op = codeGetOp(code, pc)
fmt.Printf("(pc) %-3d (op) %-14s (st) %-4d ", pc, op.String(), stack.Len())
switch op {
case STOP: // 0x00
return nil, nil
case ADD: // 0x01
x, y := stack.Pop64(), stack.Pop64()
stack.Push64(x + y)
@ -241,345 +253,418 @@ func (vm *Vm) RunTransaction(caller, target *Account, feeLimit, gasLimit, value
case BYTE: // 0x1A
idx, val := stack.Pop64(), stack.Pop()
res := 0
res := byte(0)
if idx < 32 {
res = Uint642Bytes(val[idx/8])[idx%8]
res = val[idx]
}
stack.Push64(res)
fmt.Printf(" => 0x%X", res)
stack.Push64(uint64(res))
fmt.Printf(" => 0x%X\n", res)
case SHA3: // 0x20
if gas, ok = useGas(gas, GasSha3); !ok {
return ErrInsufficientGas
if ok = useGas(gas, GasSha3); !ok {
return nil, firstErr(err, ErrInsufficientGas)
}
offset, size := stack.Pop64(), stack.Pop64()
data := sha3.Sha3(memory.Get(offset, size))
data, ok := subslice(memory, offset, size)
if !ok {
return nil, firstErr(err, ErrMemoryOutOfBounds)
}
data = sha3.Sha3(data)
stack.PushBytes(data)
fmt.Printf(" => (%v) %X", size, data)
fmt.Printf(" => (%v) %X\n", size, data)
case ADDRESS: // 0x30
stack.PushBytes(RightPadBytes(context.Address(), 32))
fmt.Printf(" => %X", RightPadBytes(context.Address(), 32))
stack.Push(callee.Address)
fmt.Printf(" => %X\n", callee.Address)
case BALANCE: // 0x31
addr := stack.PopBytes()
if gas, ok = useGas(gas, GasGetAccount); !ok {
return ErrInsufficientGas
addr := stack.Pop()
if ok = useGas(gas, GasGetAccount); !ok {
return nil, firstErr(err, ErrInsufficientGas)
}
account, err_ := vm.appState.GetAccount(addr) // TODO ensure that 20byte lengths are supported.
if err = firstErr(err, err_); err != nil {
return nil, err
}
account := vm.GetAccount(addr) // TODO ensure that 20byte lengths are supported.
balance := account.Balance
stack.Push64(balance)
fmt.Printf(" => %v (%X)", balance, addr)
fmt.Printf(" => %v (%X)\n", balance, addr)
case ORIGIN: // 0x32
origin := vm.Origin()
stack.PushBytes(origin)
fmt.Printf(" => %X", origin)
origin := vm.params.Origin
stack.Push(origin)
fmt.Printf(" => %X\n", origin)
case CALLER: // 0x33
caller := vm.lastCall.caller
stack.PushBytes(caller.Address)
fmt.Printf(" => %X", caller.Address)
stack.Push(caller.Address)
fmt.Printf(" => %X\n", caller.Address)
case CALLVALUE: // 0x34
stack.Push64(value)
fmt.Printf(" => %v", value)
fmt.Printf(" => %v\n", value)
case CALLDATALOAD: // 0x35
offset := stack.Pop64()
data, _ := subslice(input, offset, 32)
stack.PushBytes(RightPadBytes(data), 32)
fmt.Printf(" => 0x%X", data)
data, ok := subslice(input, offset, 32)
if !ok {
return nil, firstErr(err, ErrInputOutOfBounds)
}
stack.Push(RightPadWord(data))
fmt.Printf(" => 0x%X\n", data)
case CALLDATASIZE: // 0x36
stack.Push64(uint64(len(callData)))
fmt.Printf(" => %d", len(callData))
stack.Push64(uint64(len(input)))
fmt.Printf(" => %d\n", len(input))
case CALLDATACOPY: // 0x37
memOff := stack.Pop64()
inputOff := stack.Pop64()
length := stack.Pop64()
data, ok := subslice(input, inputOff, length)
if ok {
memory.Set(memOff, length, data)
if !ok {
return nil, firstErr(err, ErrInputOutOfBounds)
}
fmt.Printf(" => [%v, %v, %v] %X", memOff, inputOff, length, data)
dest, ok := subslice(memory, memOff, length)
if !ok {
return nil, firstErr(err, ErrMemoryOutOfBounds)
}
copy(dest, data)
fmt.Printf(" => [%v, %v, %v] %X\n", memOff, inputOff, length, data)
case CODESIZE: // 0x38
l := uint64(len(code))
stack.Push64(l)
fmt.Printf(" => %d", l)
fmt.Printf(" => %d\n", l)
case CODECOPY: // 0x39
memOff := stack.Pop64()
codeOff := stack.Pop64()
length := stack.Pop64()
data, ok := subslice(code, codeOff, length)
if ok {
memory.Set(memOff, length, data)
if !ok {
return nil, firstErr(err, ErrCodeOutOfBounds)
}
fmt.Printf(" => [%v, %v, %v] %X", memOff, codeOff, length, data)
dest, ok := subslice(memory, memOff, length)
if !ok {
return nil, firstErr(err, ErrMemoryOutOfBounds)
}
copy(dest, data)
fmt.Printf(" => [%v, %v, %v] %X\n", memOff, codeOff, length, data)
case GASPRICE: // 0x3A
stack.Push64(vm.params.GasPrice)
fmt.Printf(" => %X", vm.params.GasPrice)
fmt.Printf(" => %X\n", vm.params.GasPrice)
case EXTCODESIZE: // 0x3B
addr := stack.PopBytes()[:20]
account := vm.GetAccount(addr)
addr := stack.Pop()
if ok = useGas(gas, GasGetAccount); !ok {
return nil, firstErr(err, ErrInsufficientGas)
}
account, err_ := vm.appState.GetAccount(addr)
if err = firstErr(err, err_); err != nil {
return nil, err
}
code := account.Code
l := uint64(len(code))
stack.Push64(l)
fmt.Printf(" => %d", l)
fmt.Printf(" => %d\n", l)
case EXTCODECOPY: // 0x3C
addr := stack.PopBytes()[:20]
account := vm.GetAccount(addr)
addr := stack.Pop()
if ok = useGas(gas, GasGetAccount); !ok {
return nil, firstErr(err, ErrInsufficientGas)
}
account, err_ := vm.appState.GetAccount(addr)
if err = firstErr(err, err_); err != nil {
return nil, err
}
code := account.Code
memOff := stack.Pop64()
codeOff := stack.Pop64()
length := stack.Pop64()
data, ok := subslice(code, codeOff, length)
if ok {
memory.Set(memOff, length, data)
if !ok {
return nil, ErrCodeOutOfBounds
}
fmt.Printf(" => [%v, %v, %v] %X", memOff, codeOff, length, data)
dest, ok := subslice(memory, memOff, length)
if !ok {
return nil, ErrMemoryOutOfBounds
}
copy(dest, data)
fmt.Printf(" => [%v, %v, %v] %X\n", memOff, codeOff, length, data)
case BLOCKHASH: // 0x40
/*
num := stack.pop()
n := new(big.Int).Sub(vm.env.BlockHeight(), Big257)
if num.Cmp(n) > 0 && num.Cmp(vm.env.BlockHeight()) < 0 {
stack.push(Bytes2Big(vm.env.GetBlockHash(num.Uint64())))
} else {
stack.push(Big0)
}
*/
stack.Push([4]Word{0, 0, 0, 0})
fmt.Printf(" => 0x%X (NOT SUPPORTED)", stack.Peek().Bytes())
stack.Push(Zero)
fmt.Printf(" => 0x%X (NOT SUPPORTED)\n", stack.Peek().Bytes())
case COINBASE: // 0x41
stack.Push([4]Word{0, 0, 0, 0})
fmt.Printf(" => 0x%X (NOT SUPPORTED)", stack.Peek().Bytes())
stack.Push(Zero)
fmt.Printf(" => 0x%X (NOT SUPPORTED)\n", stack.Peek().Bytes())
case TIMESTAMP: // 0x42
time := vm.params.BlockTime
stack.Push64(uint64(time))
fmt.Printf(" => 0x%X", time)
fmt.Printf(" => 0x%X\n", time)
case BLOCKHEIGHT: // 0x43
number := uint64(vm.params.BlockHeight)
stack.Push64(number)
fmt.Printf(" => 0x%X", number)
fmt.Printf(" => 0x%X\n", number)
case GASLIMIT: // 0x45
stack.Push64(vm.params.GasLimit)
fmt.Printf(" => %v", vm.params.GasLimit)
fmt.Printf(" => %v\n", vm.params.GasLimit)
case POP: // 0x50
stack.Pop()
fmt.Printf(" => %v", vm.params.GasLimit)
fmt.Printf(" => %v\n", vm.params.GasLimit)
case MLOAD: // 0x51
offset := stack.Pop64()
data, _ := subslice(input, offset, 32)
stack.PushBytes(RightPadBytes(data), 32)
fmt.Printf(" => 0x%X", data)
data, ok := subslice(memory, offset, 32)
if !ok {
return nil, ErrMemoryOutOfBounds
}
stack.Push(RightPadWord(data))
fmt.Printf(" => 0x%X\n", data)
offset := stack.pop()
val := Bytes2Big(mem.Get(offset.Int64(), 32))
stack.push(val)
case MSTORE: // 0x52
offset, data := stack.Pop64(), stack.Pop()
dest, ok := subslice(memory, offset, 32)
if !ok {
return nil, ErrMemoryOutOfBounds
}
copy(dest, data[:])
fmt.Printf(" => 0x%X\n", data)
fmt.Printf(" => 0x%X", val.Bytes())
case MSTORE: // Store the value at stack top-1 in to memory at location stack top
// pop value of the stack
mStart, val := stack.pop(), stack.pop()
mem.Set(mStart.Uint64(), 32, Big2Bytes(val, 256))
case MSTORE8: // 0x53
offset, val := stack.Pop64(), byte(stack.Pop64()&0xFF)
if len(memory) <= int(offset) {
return nil, ErrMemoryOutOfBounds
}
memory[offset] = val
fmt.Printf(" => [%v] 0x%X\n", offset, val)
fmt.Printf(" => 0x%X", val)
case MSTORE8:
off, val := stack.pop(), stack.pop()
case SLOAD: // 0x54
loc := stack.Pop()
data, _ := vm.appState.GetStorage(callee.Address, loc)
stack.Push(data)
fmt.Printf(" {0x%X : 0x%X}\n", loc, data)
mem.store[off.Int64()] = byte(val.Int64() & 0xff)
fmt.Printf(" => [%v] 0x%X", off, val)
case SLOAD:
loc := stack.pop()
val := Bytes2Big(state.GetState(context.Address(), loc.Bytes()))
stack.push(val)
fmt.Printf(" {0x%X : 0x%X}", loc.Bytes(), val.Bytes())
case SSTORE:
loc, val := stack.pop(), stack.pop()
state.SetState(context.Address(), loc.Bytes(), val)
fmt.Printf(" {0x%X : 0x%X}", loc.Bytes(), val.Bytes())
case JUMP:
jump(pc, stack.pop())
case SSTORE: // 0x55
loc, data := stack.Pop(), stack.Pop()
updated, err_ := vm.appState.SetStorage(callee.Address, loc, data)
if err = firstErr(err, err_); err != nil {
return nil, err
}
if updated {
useGas(gas, GasStorageUpdate)
} else {
useGas(gas, GasStorageCreate)
}
fmt.Printf(" {0x%X : 0x%X}\n", loc, data)
case JUMP: // 0x56
err = jump(code, stack.Pop64())
continue
case JUMPI:
pos, cond := stack.pop(), stack.pop()
if cond.Cmp(BigTrue) >= 0 {
jump(pc, pos)
case JUMPI: // 0x57
pos, cond := stack.Pop64(), stack.Pop64()
if cond >= 1 {
err = jump(code, pos)
continue
}
fmt.Printf(" ~> false\n")
fmt.Printf(" ~> false")
case PC: // 0x58
stack.Push64(pc)
case JUMPDEST:
case PC:
stack.push(Big(int64(pc)))
case MSIZE:
stack.push(Big(int64(mem.Len())))
case GAS:
stack.push(context.Gas)
case MSIZE: // 0x59
stack.Push64(uint64(len(memory)))
fmt.Printf(" => %X", context.Gas)
case GAS: // 0x5A
stack.Push64(*gas)
fmt.Printf(" => %X\n", *gas)
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 := uint64(op - PUSH1 + 1)
byts := context.GetRangeValue(pc+1, a)
// push value to stack
stack.push(Bytes2Big(byts))
codeSegment, ok := subslice(code, pc+1, a)
if !ok {
return nil, firstErr(err, ErrCodeOutOfBounds)
}
res := RightPadWord(codeSegment)
stack.Push(res)
pc += a
fmt.Printf(" => 0x%X", byts)
fmt.Printf(" => 0x%X\n", res)
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)
stack.Dup(n)
fmt.Printf(" => [%d] 0x%X\n", n, stack.Peek().Bytes())
fmt.Printf(" => [%d] 0x%X", 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)
stack.Swap(n)
fmt.Printf(" => [%d]\n", n)
fmt.Printf(" => [%d]", n)
case LOG0, LOG1, LOG2, LOG3, LOG4:
n := int(op - LOG0)
topics := make([][]byte, n)
mStart, mSize := stack.pop(), stack.pop()
topics := make([]Word, n)
offset, size := stack.Pop64(), stack.Pop64()
for i := 0; i < n; i++ {
topics[i] = LeftPadBytes(stack.pop().Bytes(), 32)
topics[i] = stack.Pop()
}
data, ok := subslice(memory, offset, size)
if !ok {
return nil, firstErr(err, ErrMemoryOutOfBounds)
}
log := &Log{
callee.Address,
topics,
data,
vm.params.BlockHeight,
}
vm.appState.AddLog(log)
fmt.Printf(" => %v\n", log)
case CREATE: // 0xF0
value := stack.Pop64()
offset, size := stack.Pop64(), stack.Pop64()
input, ok := subslice(memory, offset, size)
if !ok {
return nil, firstErr(err, ErrMemoryOutOfBounds)
}
data := mem.Get(mStart.Int64(), mSize.Int64())
log := &Log{context.Address(), topics, data, vm.env.BlockHeight().Uint64()}
vm.env.AddLog(log)
fmt.Printf(" => %v", log)
// 0x60 range
case CREATE:
var (
value = stack.pop()
offset, size = stack.pop(), stack.pop()
input = mem.Get(offset.Int64(), size.Int64())
gas = new(big.Int).Set(context.Gas)
addr []byte
)
vm.Endl()
context.UseGas(context.Gas)
ret, suberr, ref := Create(vm, context, nil, input, gas, price, value)
if suberr != nil {
stack.push(BigFalse)
fmt.Printf(" (*) 0x0 %v", suberr)
// Check balance
if caller.Balance < value {
return nil, firstErr(err, ErrInsufficientBalance)
} else {
// gas < len(ret) * CreateDataGas == NO_CODE
dataGas := Big(int64(len(ret)))
dataGas.Mul(dataGas, GasCreateByte)
if context.UseGas(dataGas) {
ref.SetCode(ret)
}
addr = ref.Address()
stack.push(Bytes2Big(addr))
caller.Balance -= value
}
case CALL, CALLCODE:
gas := stack.pop()
// pop gas and value of the stack.
addr, value := stack.pop(), stack.pop()
value = U256(value)
// pop input size and offset
inOffset, inSize := stack.pop(), stack.pop()
// pop return size and offset
retOffset, retSize := stack.pop(), stack.pop()
// Create a new address
nonce := caller.Nonce
addr := createAddress(caller.Address, nonce)
caller.Nonce += 1
address := addr.Bytes()
fmt.Printf(" => %X", address).Endl()
// TODO charge for gas to create account _ the code length * GasCreateByte
newAccount, err := vm.appState.CreateAccount(addr, value)
if err != nil {
stack.Push64(0)
fmt.Printf(" (*) 0x0 %v\n", err)
} else {
// Run the input to get the contract code.
// The code as well as the input to the code are the same.
ret, err_ := vm.Call(callee, newAccount, input, input, value, gas)
if err_ != nil {
caller.Balance += value // Return the balance
stack.Push64(0)
} else {
newAccount.Code = ret // Set the code
stack.Push(newAccount.Address)
}
}
case CALL, CALLCODE: // 0xF1, 0xF2
gasLimit := stack.Pop64()
addr, value := stack.Pop(), stack.Pop64()
inOffset, inSize := stack.Pop64(), stack.Pop64() // inputs
retOffset, retSize := stack.Pop64(), stack.Pop64() // outputs
fmt.Printf(" => %X\n", addr)
// Get the arguments from the memory
args := mem.Get(inOffset.Int64(), inSize.Int64())
if len(value.Bytes()) > 0 {
gas.Add(gas, GasStipend)
args, ok := subslice(memory, inOffset, inSize)
if !ok {
return nil, firstErr(err, ErrMemoryOutOfBounds)
}
var (
ret []byte
err error
)
if op == CALLCODE {
ret, err = CallCode(env, context, address, args, gas, price, value)
// Ensure that gasLimit is reasonable
if *gas < gasLimit {
return nil, firstErr(err, ErrInsufficientGas)
} else {
ret, err = Call(env, context, address, args, gas, price, value)
*gas -= gasLimit
// NOTE: we will return any used gas later.
}
// Begin execution
var ret []byte
var err error
// If addr is in nativeContracts
if nativeContract := nativeContracts[addr]; nativeContract != nil {
ret, err = nativeContract(args, &gasLimit)
} else {
if ok = useGas(gas, GasGetAccount); !ok {
return nil, firstErr(err, ErrInsufficientGas)
}
account, err_ := vm.appState.GetAccount(addr)
if err = firstErr(err, err_); err != nil {
return nil, err
}
if op == CALLCODE {
ret, err = vm.Call(callee, callee, account.Code, args, value, gas)
} else {
ret, err = vm.Call(callee, account, account.Code, args, value, gas)
}
}
// Push result
if err != nil {
stack.push(BigFalse)
fmt.Printf("%v").Endl()
stack.Push(Zero)
} else {
stack.push(BigTrue)
mem.Set(retOffset.Uint64(), retSize.Uint64(), ret)
stack.Push(One)
dest, ok := subslice(memory, retOffset, retSize)
if !ok {
return nil, firstErr(err, ErrMemoryOutOfBounds)
}
copy(dest, ret)
}
fmt.Printf("resume %X (%v)", context.Address(), context.Gas)
case RETURN:
offset, size := stack.pop(), stack.pop()
ret := mem.Get(offset.Int64(), size.Int64())
fmt.Printf(" => [%v, %v] (%d) 0x%X", offset, size, len(ret), ret).Endl()
return context.Return(ret), nil
// Handle remaining gas.
*gas += gasLimit
case SUICIDE:
receiver := state.GetOrNewStateObject(stack.pop().Bytes())
balance := state.GetBalance(context.Address())
fmt.Printf("resume %X (%v)\n", callee.Address, gas)
fmt.Printf(" => (%X) %v", receiver.Address()[:4], balance)
receiver.AddBalance(balance)
state.Delete(context.Address())
case RETURN: // 0xF3
offset, size := stack.Pop64(), stack.Pop64()
ret, ok := subslice(memory, offset, size)
if !ok {
return nil, firstErr(err, ErrMemoryOutOfBounds)
}
fmt.Printf(" => [%v, %v] (%d) 0x%X\n", offset, size, len(ret), ret)
return ret, nil
case SUICIDE: // 0xFF
addr := stack.Pop()
if ok = useGas(gas, GasGetAccount); !ok {
return nil, firstErr(err, ErrInsufficientGas)
}
receiver, err_ := vm.appState.GetAccount(addr)
if err = firstErr(err, err_); err != nil {
return nil, err
}
balance := callee.Balance
receiver.Balance += balance
vm.appState.UpdateAccount(receiver)
vm.appState.DeleteAccount(callee)
fmt.Printf(" => (%X) %v\n", addr[:4], balance)
fallthrough
case STOP: // Stop the context
vm.Endl()
return context.Return(nil), nil
default:
fmt.Printf("(pc) %-3v Invalid opcode %X\n", pc, op).Endl()
fmt.Printf("(pc) %-3v Invalid opcode %X\n", pc, op)
panic(fmt.Errorf("Invalid opcode %X", op))
}
pc++
vm.Endl()
}
}
/*
func (vm *Vm) RunPrecompiled(p *PrecompiledAccount, callData []byte, context *Context) (ret []byte, err error) {
func (vm *VM) CallPrecompiled(p *PrecompiledAccount, callData []byte, context *Context) (ret []byte, err error) {
gas := p.Gas(len(callData))
if context.UseGas(gas) {
ret = p.Call(callData)
@ -608,10 +693,44 @@ func subslice(data []byte, offset, length uint64) ([]byte, bool) {
}
}
func useGas(gasLeft, gasToUse uint64) (uint64, bool) {
if gasLeft > gasToUse {
return gasLeft - gasToUse, true
func codeGetOp(code []byte, n uint64) OpCode {
if uint64(len(code)) <= n {
return OpCode(0) // stop
} else {
return gasLeft, false
return OpCode(code[n])
}
}
func jump(code []byte, to uint64) (err error) {
dest := codeGetOp(code, to)
if dest != JUMPDEST {
return ErrInvalidJumpDest
}
fmt.Printf(" ~> %v\n", to)
return nil
}
func firstErr(errA, errB error) error {
if errA != nil {
return errA
} else {
return errB
}
}
func useGas(gas *uint64, gasToUse uint64) bool {
if *gas > gasToUse {
*gas -= gasToUse
return true
} else {
return false
}
}
// Creates a 20 byte address from the creatorAddr and nonce.
func createAddress(creatorAddr Word, nonce uint64) Word {
temp := make([]byte, 32+8)
copy(temp, creatorAddr[:])
PutUint64(temp[32:], nonce)
return RightPadWord(sha3.Sha3(temp)[:20])
}