2015-03-16 02:24:08 -07:00
|
|
|
package vm
|
|
|
|
|
|
|
|
import (
|
2015-04-17 16:05:36 -07:00
|
|
|
"bytes"
|
2015-03-17 21:46:26 -07:00
|
|
|
"errors"
|
2015-03-16 02:24:08 -07:00
|
|
|
"fmt"
|
2015-03-29 00:07:10 -07:00
|
|
|
"math/big"
|
2015-03-16 02:24:08 -07:00
|
|
|
|
2015-04-01 17:30:16 -07:00
|
|
|
. "github.com/tendermint/tendermint/common"
|
2015-04-13 21:43:13 -07:00
|
|
|
"github.com/tendermint/tendermint/events"
|
2015-05-20 00:40:02 -04:00
|
|
|
ptypes "github.com/tendermint/tendermint/permission/types"
|
2015-04-14 19:25:49 -07:00
|
|
|
"github.com/tendermint/tendermint/types"
|
2015-04-01 17:30:16 -07:00
|
|
|
"github.com/tendermint/tendermint/vm/sha3"
|
2015-03-16 02:24:08 -07:00
|
|
|
)
|
|
|
|
|
2015-03-17 21:46:26 -07:00
|
|
|
var (
|
2015-03-28 23:44:07 -07:00
|
|
|
ErrUnknownAddress = errors.New("Unknown address")
|
2015-03-17 21:46:26 -07:00
|
|
|
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")
|
|
|
|
)
|
2015-03-16 02:24:08 -07:00
|
|
|
|
2015-05-12 20:40:19 -04:00
|
|
|
type ErrPermission struct {
|
|
|
|
typ string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (err ErrPermission) Error() string {
|
|
|
|
return fmt.Sprintf("Contract does not have permission to %s", err.typ)
|
|
|
|
}
|
|
|
|
|
2015-03-29 00:07:10 -07:00
|
|
|
type Debug bool
|
|
|
|
|
2015-03-17 21:46:26 -07:00
|
|
|
const (
|
2015-03-29 00:07:10 -07:00
|
|
|
dataStackCapacity = 1024
|
|
|
|
callStackCapacity = 100 // TODO ensure usage.
|
|
|
|
memoryCapacity = 1024 * 1024 // 1 MB
|
|
|
|
dbg Debug = true
|
2015-03-17 21:46:26 -07:00
|
|
|
)
|
|
|
|
|
2015-03-29 00:07:10 -07:00
|
|
|
func (d Debug) Printf(s string, a ...interface{}) {
|
|
|
|
if d {
|
|
|
|
fmt.Printf(s, a...)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-17 21:46:26 -07:00
|
|
|
type VM struct {
|
|
|
|
appState AppState
|
2015-03-20 05:47:52 -07:00
|
|
|
params Params
|
2015-03-28 23:44:07 -07:00
|
|
|
origin Word256
|
2015-04-14 19:25:49 -07:00
|
|
|
txid []byte
|
2015-03-17 21:46:26 -07:00
|
|
|
|
|
|
|
callDepth int
|
2015-04-13 21:43:13 -07:00
|
|
|
|
2015-04-15 23:40:27 -07:00
|
|
|
evc events.Fireable
|
2015-03-16 02:24:08 -07:00
|
|
|
}
|
|
|
|
|
2015-04-14 19:25:49 -07:00
|
|
|
func NewVM(appState AppState, params Params, origin Word256, txid []byte) *VM {
|
2015-03-17 21:46:26 -07:00
|
|
|
return &VM{
|
2015-05-16 00:48:04 -04:00
|
|
|
appState: appState,
|
|
|
|
params: params,
|
|
|
|
origin: origin,
|
|
|
|
callDepth: 0,
|
|
|
|
txid: txid,
|
2015-03-16 02:24:08 -07:00
|
|
|
}
|
2015-03-17 21:46:26 -07:00
|
|
|
}
|
2015-03-16 02:24:08 -07:00
|
|
|
|
2015-04-13 21:43:13 -07:00
|
|
|
// satisfies events.Eventable
|
2015-04-15 23:40:27 -07:00
|
|
|
func (vm *VM) SetFireable(evc events.Fireable) {
|
|
|
|
vm.evc = evc
|
2015-04-13 21:43:13 -07:00
|
|
|
}
|
|
|
|
|
2015-07-07 14:26:05 -07:00
|
|
|
// CONTRACT: it is the duty of the contract writer to call known permissions
|
2015-06-26 01:36:31 +00:00
|
|
|
// 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)
|
2015-07-07 14:26:05 -07:00
|
|
|
// If the perm is not defined in the acc nor set by default in GlobalPermissions,
|
|
|
|
// prints a log warning and returns false.
|
2015-05-21 23:42:45 -04:00
|
|
|
func HasPermission(appState AppState, acc *Account, perm ptypes.PermFlag) bool {
|
2015-05-20 00:40:02 -04:00
|
|
|
v, err := acc.Permissions.Base.Get(perm)
|
|
|
|
if _, ok := err.(ptypes.ErrValueNotSet); ok {
|
2015-05-21 23:42:45 -04:00
|
|
|
if appState == nil {
|
2015-07-07 14:26:05 -07:00
|
|
|
log.Warn(Fmt("\n\n***** Unknown permission %b! ********\n\n", perm))
|
2015-06-26 01:36:31 +00:00
|
|
|
return false
|
2015-05-21 23:42:45 -04:00
|
|
|
}
|
|
|
|
return HasPermission(nil, appState.GetAccount(ptypes.GlobalPermissionsAddress256), perm)
|
2015-05-20 00:40:02 -04:00
|
|
|
}
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
|
2015-07-23 23:57:28 -07:00
|
|
|
func (vm *VM) fireCallEvent(exception *string, output *[]byte, caller, callee *Account, input []byte, value int64, gas *int64) {
|
2015-07-20 14:24:57 -04:00
|
|
|
// fire the post call event (including exception if applicable)
|
|
|
|
if vm.evc != nil {
|
2015-07-23 23:57:28 -07:00
|
|
|
vm.evc.FireEvent(types.EventStringAccCall(callee.Address.Postfix(20)), types.EventMsgCall{
|
2015-07-20 14:24:57 -04:00
|
|
|
&types.CallData{caller.Address.Postfix(20), callee.Address.Postfix(20), input, value, *gas},
|
|
|
|
vm.origin.Postfix(20),
|
|
|
|
vm.txid,
|
|
|
|
*output,
|
|
|
|
*exception,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-20 19:59:42 -07:00
|
|
|
// CONTRACT appState is aware of caller and callee, so we can just mutate them.
|
2015-03-20 18:48:47 -07:00
|
|
|
// value: To be transferred from caller to callee. Refunded upon error.
|
|
|
|
// gas: Available gas. No refunds for gas.
|
2015-05-14 20:23:36 -04:00
|
|
|
// code: May be nil, since the CALL opcode may be used to send value from contracts to accounts
|
2015-06-25 20:28:34 -07:00
|
|
|
func (vm *VM) Call(caller, callee *Account, code, input []byte, value int64, gas *int64) (output []byte, err error) {
|
2015-03-16 02:24:08 -07:00
|
|
|
|
2015-05-15 15:30:24 -04:00
|
|
|
exception := new(string)
|
2015-07-20 14:24:57 -04:00
|
|
|
// fire the post call event (including exception if applicable)
|
2015-07-23 23:57:28 -07:00
|
|
|
defer vm.fireCallEvent(exception, &output, caller, callee, input, value, gas)
|
2015-05-21 17:58:28 -04:00
|
|
|
|
2015-03-20 18:48:47 -07:00
|
|
|
if err = transfer(caller, callee, value); err != nil {
|
2015-05-15 15:30:24 -04:00
|
|
|
*exception = err.Error()
|
2015-03-20 18:48:47 -07:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2015-05-14 20:23:36 -04:00
|
|
|
if len(code) > 0 {
|
|
|
|
vm.callDepth += 1
|
|
|
|
output, err = vm.call(caller, callee, code, input, value, gas)
|
|
|
|
vm.callDepth -= 1
|
2015-03-20 18:48:47 -07:00
|
|
|
if err != nil {
|
2015-05-15 15:30:24 -04:00
|
|
|
*exception = err.Error()
|
2015-05-14 20:23:36 -04:00
|
|
|
err := transfer(callee, caller, value)
|
|
|
|
if err != nil {
|
2015-06-26 01:50:31 +00:00
|
|
|
// data has been corrupted in ram
|
2015-07-19 23:42:52 +00:00
|
|
|
PanicCrisis("Could not return value to caller")
|
2015-05-14 20:23:36 -04:00
|
|
|
}
|
2015-03-20 18:48:47 -07:00
|
|
|
}
|
|
|
|
}
|
2015-05-14 20:23:36 -04:00
|
|
|
|
2015-03-20 18:48:47 -07:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2015-07-24 12:32:22 -07:00
|
|
|
// Try to deduct gasToUse from gasLeft. If ok return false, otherwise
|
|
|
|
// set err and return true.
|
|
|
|
func useGasNegative(gasLeft *int64, gasToUse int64, err *error) bool {
|
|
|
|
if *gasLeft >= gasToUse {
|
|
|
|
*gasLeft -= gasToUse
|
|
|
|
return false
|
|
|
|
} else if *err == nil {
|
|
|
|
*err = ErrInsufficientGas
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2015-03-20 18:48:47 -07:00
|
|
|
// Just like Call() but does not transfer 'value' or modify the callDepth.
|
2015-06-25 20:28:34 -07:00
|
|
|
func (vm *VM) call(caller, callee *Account, code, input []byte, value int64, gas *int64) (output []byte, err error) {
|
2015-03-29 00:07:10 -07:00
|
|
|
dbg.Printf("(%d) (%X) %X (code=%d) gas: %v (d) %X\n", vm.callDepth, caller.Address[:4], callee.Address, len(callee.Code), *gas, input)
|
2015-03-16 02:24:08 -07:00
|
|
|
|
|
|
|
var (
|
2015-06-25 20:28:34 -07:00
|
|
|
pc int64 = 0
|
|
|
|
stack = NewStack(dataStackCapacity, gas, &err)
|
|
|
|
memory = make([]byte, memoryCapacity)
|
2015-03-16 02:24:08 -07:00
|
|
|
)
|
|
|
|
|
|
|
|
for {
|
2015-07-24 12:32:22 -07:00
|
|
|
|
|
|
|
// Use BaseOp gas.
|
|
|
|
if useGasNegative(gas, GasBaseOp, &err) {
|
2015-03-21 01:44:47 -07:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2015-03-17 21:46:26 -07:00
|
|
|
var op = codeGetOp(code, pc)
|
2015-03-29 00:07:10 -07:00
|
|
|
dbg.Printf("(pc) %-3d (op) %-14s (st) %-4d ", pc, op.String(), stack.Len())
|
2015-03-16 02:24:08 -07:00
|
|
|
|
|
|
|
switch op {
|
|
|
|
|
2015-03-17 21:46:26 -07:00
|
|
|
case STOP: // 0x00
|
|
|
|
return nil, nil
|
|
|
|
|
2015-03-16 02:24:08 -07:00
|
|
|
case ADD: // 0x01
|
2015-03-29 00:07:10 -07:00
|
|
|
x, y := stack.Pop(), stack.Pop()
|
2015-04-17 16:05:36 -07:00
|
|
|
xb := new(big.Int).SetBytes(x[:])
|
|
|
|
yb := new(big.Int).SetBytes(y[:])
|
2015-03-29 00:07:10 -07:00
|
|
|
sum := new(big.Int).Add(xb, yb)
|
2015-04-17 16:05:36 -07:00
|
|
|
res := LeftPadWord256(U256(sum).Bytes())
|
|
|
|
stack.Push(res)
|
|
|
|
dbg.Printf(" %v + %v = %v (%X)\n", xb, yb, sum, res)
|
2015-03-16 02:24:08 -07:00
|
|
|
|
|
|
|
case MUL: // 0x02
|
2015-03-29 00:07:10 -07:00
|
|
|
x, y := stack.Pop(), stack.Pop()
|
2015-04-17 16:05:36 -07:00
|
|
|
xb := new(big.Int).SetBytes(x[:])
|
|
|
|
yb := new(big.Int).SetBytes(y[:])
|
2015-03-29 00:07:10 -07:00
|
|
|
prod := new(big.Int).Mul(xb, yb)
|
2015-04-17 16:05:36 -07:00
|
|
|
res := LeftPadWord256(U256(prod).Bytes())
|
|
|
|
stack.Push(res)
|
|
|
|
dbg.Printf(" %v * %v = %v (%X)\n", xb, yb, prod, res)
|
2015-03-16 02:24:08 -07:00
|
|
|
|
|
|
|
case SUB: // 0x03
|
2015-03-29 00:07:10 -07:00
|
|
|
x, y := stack.Pop(), stack.Pop()
|
2015-04-17 16:05:36 -07:00
|
|
|
xb := new(big.Int).SetBytes(x[:])
|
|
|
|
yb := new(big.Int).SetBytes(y[:])
|
2015-03-29 00:07:10 -07:00
|
|
|
diff := new(big.Int).Sub(xb, yb)
|
2015-04-17 16:05:36 -07:00
|
|
|
res := LeftPadWord256(U256(diff).Bytes())
|
|
|
|
stack.Push(res)
|
|
|
|
dbg.Printf(" %v - %v = %v (%X)\n", xb, yb, diff, res)
|
2015-03-16 02:24:08 -07:00
|
|
|
|
|
|
|
case DIV: // 0x04
|
2015-03-29 00:07:10 -07:00
|
|
|
x, y := stack.Pop(), stack.Pop()
|
2015-04-17 16:05:36 -07:00
|
|
|
if y.IsZero() {
|
2015-03-28 23:44:07 -07:00
|
|
|
stack.Push(Zero256)
|
2015-04-17 16:05:36 -07:00
|
|
|
dbg.Printf(" %x / %x = %v\n", x, y, 0)
|
2015-03-16 02:24:08 -07:00
|
|
|
} else {
|
2015-04-17 16:05:36 -07:00
|
|
|
xb := new(big.Int).SetBytes(x[:])
|
|
|
|
yb := new(big.Int).SetBytes(y[:])
|
2015-03-29 00:07:10 -07:00
|
|
|
div := new(big.Int).Div(xb, yb)
|
2015-04-17 16:05:36 -07:00
|
|
|
res := LeftPadWord256(U256(div).Bytes())
|
|
|
|
stack.Push(res)
|
|
|
|
dbg.Printf(" %v / %v = %v (%X)\n", xb, yb, div, res)
|
2015-03-16 02:24:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
case SDIV: // 0x05
|
2015-04-17 16:05:36 -07:00
|
|
|
x, y := stack.Pop(), stack.Pop()
|
|
|
|
if y.IsZero() {
|
2015-03-28 23:44:07 -07:00
|
|
|
stack.Push(Zero256)
|
2015-04-17 16:05:36 -07:00
|
|
|
dbg.Printf(" %x / %x = %v\n", x, y, 0)
|
2015-03-16 02:24:08 -07:00
|
|
|
} else {
|
2015-04-17 16:05:36 -07:00
|
|
|
xb := S256(new(big.Int).SetBytes(x[:]))
|
|
|
|
yb := S256(new(big.Int).SetBytes(y[:]))
|
|
|
|
div := new(big.Int).Div(xb, yb)
|
|
|
|
res := LeftPadWord256(U256(div).Bytes())
|
|
|
|
stack.Push(res)
|
|
|
|
dbg.Printf(" %v / %v = %v (%X)\n", xb, yb, div, res)
|
2015-03-16 02:24:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
case MOD: // 0x06
|
2015-03-29 00:07:10 -07:00
|
|
|
x, y := stack.Pop(), stack.Pop()
|
2015-04-17 16:05:36 -07:00
|
|
|
if y.IsZero() {
|
2015-03-28 23:44:07 -07:00
|
|
|
stack.Push(Zero256)
|
2015-04-17 16:05:36 -07:00
|
|
|
dbg.Printf(" %v %% %v = %v\n", x, y, 0)
|
2015-03-16 02:24:08 -07:00
|
|
|
} else {
|
2015-04-17 16:05:36 -07:00
|
|
|
xb := new(big.Int).SetBytes(x[:])
|
|
|
|
yb := new(big.Int).SetBytes(y[:])
|
2015-03-29 00:07:10 -07:00
|
|
|
mod := new(big.Int).Mod(xb, yb)
|
2015-04-17 16:05:36 -07:00
|
|
|
res := LeftPadWord256(U256(mod).Bytes())
|
|
|
|
stack.Push(res)
|
|
|
|
dbg.Printf(" %v %% %v = %v (%X)\n", xb, yb, mod, res)
|
2015-03-16 02:24:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
case SMOD: // 0x07
|
2015-04-17 16:05:36 -07:00
|
|
|
x, y := stack.Pop(), stack.Pop()
|
|
|
|
if y.IsZero() {
|
2015-03-28 23:44:07 -07:00
|
|
|
stack.Push(Zero256)
|
2015-04-17 16:05:36 -07:00
|
|
|
dbg.Printf(" %v %% %v = %v\n", x, y, 0)
|
2015-03-16 02:24:08 -07:00
|
|
|
} else {
|
2015-04-17 16:05:36 -07:00
|
|
|
xb := S256(new(big.Int).SetBytes(x[:]))
|
|
|
|
yb := S256(new(big.Int).SetBytes(y[:]))
|
|
|
|
mod := new(big.Int).Mod(xb, yb)
|
|
|
|
res := LeftPadWord256(U256(mod).Bytes())
|
|
|
|
stack.Push(res)
|
|
|
|
dbg.Printf(" %v %% %v = %v (%X)\n", xb, yb, mod, res)
|
2015-03-16 02:24:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
case ADDMOD: // 0x08
|
2015-04-17 16:05:36 -07:00
|
|
|
x, y, z := stack.Pop(), stack.Pop(), stack.Pop()
|
|
|
|
if z.IsZero() {
|
2015-03-28 23:44:07 -07:00
|
|
|
stack.Push(Zero256)
|
2015-04-17 16:05:36 -07:00
|
|
|
dbg.Printf(" %v %% %v = %v\n", x, y, 0)
|
2015-03-16 02:24:08 -07:00
|
|
|
} else {
|
2015-04-17 16:05:36 -07:00
|
|
|
xb := new(big.Int).SetBytes(x[:])
|
|
|
|
yb := new(big.Int).SetBytes(y[:])
|
|
|
|
zb := new(big.Int).SetBytes(z[:])
|
|
|
|
add := new(big.Int).Add(xb, yb)
|
|
|
|
mod := new(big.Int).Mod(add, zb)
|
|
|
|
res := LeftPadWord256(U256(mod).Bytes())
|
|
|
|
stack.Push(res)
|
|
|
|
dbg.Printf(" %v + %v %% %v = %v (%X)\n",
|
|
|
|
xb, yb, zb, mod, res)
|
2015-03-16 02:24:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
case MULMOD: // 0x09
|
2015-04-17 16:05:36 -07:00
|
|
|
x, y, z := stack.Pop(), stack.Pop(), stack.Pop()
|
|
|
|
if z.IsZero() {
|
2015-03-28 23:44:07 -07:00
|
|
|
stack.Push(Zero256)
|
2015-04-17 16:05:36 -07:00
|
|
|
dbg.Printf(" %v %% %v = %v\n", x, y, 0)
|
2015-03-16 02:24:08 -07:00
|
|
|
} else {
|
2015-04-17 16:05:36 -07:00
|
|
|
xb := new(big.Int).SetBytes(x[:])
|
|
|
|
yb := new(big.Int).SetBytes(y[:])
|
|
|
|
zb := new(big.Int).SetBytes(z[:])
|
|
|
|
mul := new(big.Int).Mul(xb, yb)
|
|
|
|
mod := new(big.Int).Mod(mul, zb)
|
|
|
|
res := LeftPadWord256(U256(mod).Bytes())
|
|
|
|
stack.Push(res)
|
|
|
|
dbg.Printf(" %v * %v %% %v = %v (%X)\n",
|
|
|
|
xb, yb, zb, mod, res)
|
2015-03-16 02:24:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
case EXP: // 0x0A
|
2015-03-29 00:07:10 -07:00
|
|
|
x, y := stack.Pop(), stack.Pop()
|
2015-04-17 16:05:36 -07:00
|
|
|
xb := new(big.Int).SetBytes(x[:])
|
|
|
|
yb := new(big.Int).SetBytes(y[:])
|
2015-03-29 00:07:10 -07:00
|
|
|
pow := new(big.Int).Exp(xb, yb, big.NewInt(0))
|
2015-04-17 16:05:36 -07:00
|
|
|
res := LeftPadWord256(U256(pow).Bytes())
|
2015-04-17 20:51:01 -07:00
|
|
|
stack.Push(res)
|
2015-04-17 16:05:36 -07:00
|
|
|
dbg.Printf(" %v ** %v = %v (%X)\n", xb, yb, pow, res)
|
2015-03-16 02:24:08 -07:00
|
|
|
|
|
|
|
case SIGNEXTEND: // 0x0B
|
2015-04-17 16:05:36 -07:00
|
|
|
back := stack.Pop()
|
|
|
|
backb := new(big.Int).SetBytes(back[:])
|
|
|
|
if backb.Cmp(big.NewInt(31)) < 0 {
|
|
|
|
bit := uint(backb.Uint64()*8 + 7)
|
|
|
|
num := stack.Pop()
|
|
|
|
numb := new(big.Int).SetBytes(num[:])
|
|
|
|
mask := new(big.Int).Lsh(big.NewInt(1), bit)
|
|
|
|
mask.Sub(mask, big.NewInt(1))
|
|
|
|
if numb.Bit(int(bit)) == 1 {
|
|
|
|
numb.Or(numb, mask.Not(mask))
|
|
|
|
} else {
|
|
|
|
numb.Add(numb, mask)
|
|
|
|
}
|
|
|
|
res := LeftPadWord256(U256(numb).Bytes())
|
|
|
|
dbg.Printf(" = %v (%X)", numb, res)
|
|
|
|
stack.Push(res)
|
|
|
|
}
|
2015-03-16 02:24:08 -07:00
|
|
|
|
|
|
|
case LT: // 0x10
|
2015-04-17 16:05:36 -07:00
|
|
|
x, y := stack.Pop(), stack.Pop()
|
|
|
|
xb := new(big.Int).SetBytes(x[:])
|
|
|
|
yb := new(big.Int).SetBytes(y[:])
|
|
|
|
if xb.Cmp(yb) < 0 {
|
2015-03-16 02:24:08 -07:00
|
|
|
stack.Push64(1)
|
2015-04-17 16:05:36 -07:00
|
|
|
dbg.Printf(" %v < %v = %v\n", xb, yb, 1)
|
2015-03-16 02:24:08 -07:00
|
|
|
} else {
|
2015-03-28 23:44:07 -07:00
|
|
|
stack.Push(Zero256)
|
2015-04-17 16:05:36 -07:00
|
|
|
dbg.Printf(" %v < %v = %v\n", xb, yb, 0)
|
2015-03-16 02:24:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
case GT: // 0x11
|
2015-04-17 16:05:36 -07:00
|
|
|
x, y := stack.Pop(), stack.Pop()
|
|
|
|
xb := new(big.Int).SetBytes(x[:])
|
|
|
|
yb := new(big.Int).SetBytes(y[:])
|
|
|
|
if xb.Cmp(yb) > 0 {
|
2015-03-16 02:24:08 -07:00
|
|
|
stack.Push64(1)
|
2015-04-17 16:05:36 -07:00
|
|
|
dbg.Printf(" %v > %v = %v\n", xb, yb, 1)
|
2015-03-16 02:24:08 -07:00
|
|
|
} else {
|
2015-03-28 23:44:07 -07:00
|
|
|
stack.Push(Zero256)
|
2015-04-17 16:05:36 -07:00
|
|
|
dbg.Printf(" %v > %v = %v\n", xb, yb, 0)
|
2015-03-16 02:24:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
case SLT: // 0x12
|
2015-04-17 16:05:36 -07:00
|
|
|
x, y := stack.Pop(), stack.Pop()
|
|
|
|
xb := S256(new(big.Int).SetBytes(x[:]))
|
|
|
|
yb := S256(new(big.Int).SetBytes(y[:]))
|
|
|
|
if xb.Cmp(yb) < 0 {
|
2015-03-16 02:24:08 -07:00
|
|
|
stack.Push64(1)
|
2015-04-17 16:05:36 -07:00
|
|
|
dbg.Printf(" %v < %v = %v\n", xb, yb, 1)
|
2015-03-16 02:24:08 -07:00
|
|
|
} else {
|
2015-03-28 23:44:07 -07:00
|
|
|
stack.Push(Zero256)
|
2015-04-17 16:05:36 -07:00
|
|
|
dbg.Printf(" %v < %v = %v\n", xb, yb, 0)
|
2015-03-16 02:24:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
case SGT: // 0x13
|
2015-04-17 16:05:36 -07:00
|
|
|
x, y := stack.Pop(), stack.Pop()
|
|
|
|
xb := S256(new(big.Int).SetBytes(x[:]))
|
|
|
|
yb := S256(new(big.Int).SetBytes(y[:]))
|
|
|
|
if xb.Cmp(yb) > 0 {
|
2015-03-16 02:24:08 -07:00
|
|
|
stack.Push64(1)
|
2015-04-17 16:05:36 -07:00
|
|
|
dbg.Printf(" %v > %v = %v\n", xb, yb, 1)
|
2015-03-16 02:24:08 -07:00
|
|
|
} else {
|
2015-03-28 23:44:07 -07:00
|
|
|
stack.Push(Zero256)
|
2015-04-17 16:05:36 -07:00
|
|
|
dbg.Printf(" %v > %v = %v\n", xb, yb, 0)
|
2015-03-16 02:24:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
case EQ: // 0x14
|
2015-04-17 16:05:36 -07:00
|
|
|
x, y := stack.Pop(), stack.Pop()
|
|
|
|
if bytes.Equal(x[:], y[:]) {
|
2015-03-16 02:24:08 -07:00
|
|
|
stack.Push64(1)
|
2015-04-17 16:05:36 -07:00
|
|
|
dbg.Printf(" %X == %X = %v\n", x, y, 1)
|
2015-03-16 02:24:08 -07:00
|
|
|
} else {
|
2015-03-28 23:44:07 -07:00
|
|
|
stack.Push(Zero256)
|
2015-04-17 16:05:36 -07:00
|
|
|
dbg.Printf(" %X == %X = %v\n", x, y, 0)
|
2015-03-16 02:24:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
case ISZERO: // 0x15
|
2015-04-17 16:05:36 -07:00
|
|
|
x := stack.Pop()
|
|
|
|
if x.IsZero() {
|
2015-03-16 02:24:08 -07:00
|
|
|
stack.Push64(1)
|
2015-04-17 16:05:36 -07:00
|
|
|
dbg.Printf(" %v == 0 = %v\n", x, 1)
|
2015-03-16 02:24:08 -07:00
|
|
|
} else {
|
2015-03-28 23:44:07 -07:00
|
|
|
stack.Push(Zero256)
|
2015-04-17 16:05:36 -07:00
|
|
|
dbg.Printf(" %v == 0 = %v\n", x, 0)
|
2015-03-16 02:24:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
case AND: // 0x16
|
2015-04-17 17:43:45 -07:00
|
|
|
x, y := stack.Pop(), stack.Pop()
|
2015-04-17 16:05:36 -07:00
|
|
|
z := [32]byte{}
|
|
|
|
for i := 0; i < 32; i++ {
|
|
|
|
z[i] = x[i] & y[i]
|
|
|
|
}
|
|
|
|
stack.Push(z)
|
|
|
|
dbg.Printf(" %X & %X = %X\n", x, y, z)
|
|
|
|
|
2015-03-16 02:24:08 -07:00
|
|
|
case OR: // 0x17
|
2015-04-17 17:43:45 -07:00
|
|
|
x, y := stack.Pop(), stack.Pop()
|
2015-04-17 16:05:36 -07:00
|
|
|
z := [32]byte{}
|
|
|
|
for i := 0; i < 32; i++ {
|
|
|
|
z[i] = x[i] | y[i]
|
|
|
|
}
|
|
|
|
stack.Push(z)
|
|
|
|
dbg.Printf(" %X | %X = %X\n", x, y, z)
|
2015-03-16 02:24:08 -07:00
|
|
|
|
|
|
|
case XOR: // 0x18
|
2015-04-17 16:05:36 -07:00
|
|
|
x, y := stack.Pop(), stack.Pop()
|
|
|
|
z := [32]byte{}
|
|
|
|
for i := 0; i < 32; i++ {
|
|
|
|
z[i] = x[i] ^ y[i]
|
|
|
|
}
|
|
|
|
stack.Push(z)
|
|
|
|
dbg.Printf(" %X ^ %X = %X\n", x, y, z)
|
2015-03-16 02:24:08 -07:00
|
|
|
|
|
|
|
case NOT: // 0x19
|
2015-04-17 16:05:36 -07:00
|
|
|
x := stack.Pop()
|
|
|
|
z := [32]byte{}
|
|
|
|
for i := 0; i < 32; i++ {
|
|
|
|
z[i] = ^x[i]
|
|
|
|
}
|
|
|
|
stack.Push(z)
|
|
|
|
dbg.Printf(" !%X = %X\n", x, z)
|
2015-03-16 02:24:08 -07:00
|
|
|
|
|
|
|
case BYTE: // 0x1A
|
|
|
|
idx, val := stack.Pop64(), stack.Pop()
|
2015-03-17 21:46:26 -07:00
|
|
|
res := byte(0)
|
2015-03-16 02:24:08 -07:00
|
|
|
if idx < 32 {
|
2015-03-17 21:46:26 -07:00
|
|
|
res = val[idx]
|
2015-03-16 02:24:08 -07:00
|
|
|
}
|
2015-06-25 20:28:34 -07:00
|
|
|
stack.Push64(int64(res))
|
2015-03-29 00:07:10 -07:00
|
|
|
dbg.Printf(" => 0x%X\n", res)
|
2015-03-16 02:24:08 -07:00
|
|
|
|
|
|
|
case SHA3: // 0x20
|
2015-07-24 12:32:22 -07:00
|
|
|
if useGasNegative(gas, GasSha3, &err) {
|
|
|
|
return nil, err
|
2015-03-16 02:24:08 -07:00
|
|
|
}
|
|
|
|
offset, size := stack.Pop64(), stack.Pop64()
|
2015-04-17 16:05:36 -07:00
|
|
|
data, ok := subslice(memory, offset, size)
|
2015-03-17 21:46:26 -07:00
|
|
|
if !ok {
|
|
|
|
return nil, firstErr(err, ErrMemoryOutOfBounds)
|
|
|
|
}
|
|
|
|
data = sha3.Sha3(data)
|
2015-04-17 16:05:36 -07:00
|
|
|
stack.PushBytes(data)
|
2015-03-29 00:07:10 -07:00
|
|
|
dbg.Printf(" => (%v) %X\n", size, data)
|
2015-03-16 02:24:08 -07:00
|
|
|
|
|
|
|
case ADDRESS: // 0x30
|
2015-04-17 20:51:01 -07:00
|
|
|
stack.Push(callee.Address)
|
|
|
|
dbg.Printf(" => %X\n", callee.Address)
|
2015-03-16 02:24:08 -07:00
|
|
|
|
|
|
|
case BALANCE: // 0x31
|
2015-03-17 21:46:26 -07:00
|
|
|
addr := stack.Pop()
|
2015-07-24 12:32:22 -07:00
|
|
|
if useGasNegative(gas, GasGetAccount, &err) {
|
|
|
|
return nil, err
|
2015-03-17 21:46:26 -07:00
|
|
|
}
|
2015-04-17 16:05:36 -07:00
|
|
|
acc := vm.appState.GetAccount(addr)
|
2015-03-28 23:44:07 -07:00
|
|
|
if acc == nil {
|
|
|
|
return nil, firstErr(err, ErrUnknownAddress)
|
2015-03-16 02:24:08 -07:00
|
|
|
}
|
2015-03-28 23:44:07 -07:00
|
|
|
balance := acc.Balance
|
2015-03-16 02:24:08 -07:00
|
|
|
stack.Push64(balance)
|
2015-03-29 00:07:10 -07:00
|
|
|
dbg.Printf(" => %v (%X)\n", balance, addr)
|
2015-03-16 02:24:08 -07:00
|
|
|
|
|
|
|
case ORIGIN: // 0x32
|
2015-04-17 20:51:01 -07:00
|
|
|
stack.Push(vm.origin)
|
|
|
|
dbg.Printf(" => %X\n", vm.origin)
|
2015-03-16 02:24:08 -07:00
|
|
|
|
|
|
|
case CALLER: // 0x33
|
2015-04-17 20:51:01 -07:00
|
|
|
stack.Push(caller.Address)
|
|
|
|
dbg.Printf(" => %X\n", caller.Address)
|
2015-03-16 02:24:08 -07:00
|
|
|
|
|
|
|
case CALLVALUE: // 0x34
|
|
|
|
stack.Push64(value)
|
2015-03-29 00:07:10 -07:00
|
|
|
dbg.Printf(" => %v\n", value)
|
2015-03-16 02:24:08 -07:00
|
|
|
|
|
|
|
case CALLDATALOAD: // 0x35
|
|
|
|
offset := stack.Pop64()
|
2015-04-17 16:05:36 -07:00
|
|
|
data, ok := subslice(input, offset, 32)
|
2015-03-17 21:46:26 -07:00
|
|
|
if !ok {
|
|
|
|
return nil, firstErr(err, ErrInputOutOfBounds)
|
|
|
|
}
|
2015-04-17 16:05:36 -07:00
|
|
|
res := LeftPadWord256(data)
|
|
|
|
stack.Push(res)
|
|
|
|
dbg.Printf(" => 0x%X\n", res)
|
2015-03-16 02:24:08 -07:00
|
|
|
|
|
|
|
case CALLDATASIZE: // 0x36
|
2015-06-25 20:28:34 -07:00
|
|
|
stack.Push64(int64(len(input)))
|
2015-03-29 00:07:10 -07:00
|
|
|
dbg.Printf(" => %d\n", len(input))
|
2015-03-16 02:24:08 -07:00
|
|
|
|
|
|
|
case CALLDATACOPY: // 0x37
|
|
|
|
memOff := stack.Pop64()
|
|
|
|
inputOff := stack.Pop64()
|
|
|
|
length := stack.Pop64()
|
2015-04-17 16:05:36 -07:00
|
|
|
data, ok := subslice(input, inputOff, length)
|
2015-03-17 21:46:26 -07:00
|
|
|
if !ok {
|
|
|
|
return nil, firstErr(err, ErrInputOutOfBounds)
|
2015-03-16 02:24:08 -07:00
|
|
|
}
|
2015-04-17 16:05:36 -07:00
|
|
|
dest, ok := subslice(memory, memOff, length)
|
2015-03-17 21:46:26 -07:00
|
|
|
if !ok {
|
|
|
|
return nil, firstErr(err, ErrMemoryOutOfBounds)
|
|
|
|
}
|
|
|
|
copy(dest, data)
|
2015-03-29 00:07:10 -07:00
|
|
|
dbg.Printf(" => [%v, %v, %v] %X\n", memOff, inputOff, length, data)
|
2015-03-16 02:24:08 -07:00
|
|
|
|
|
|
|
case CODESIZE: // 0x38
|
2015-06-25 20:28:34 -07:00
|
|
|
l := int64(len(code))
|
2015-03-16 02:24:08 -07:00
|
|
|
stack.Push64(l)
|
2015-03-29 00:07:10 -07:00
|
|
|
dbg.Printf(" => %d\n", l)
|
2015-03-16 02:24:08 -07:00
|
|
|
|
|
|
|
case CODECOPY: // 0x39
|
|
|
|
memOff := stack.Pop64()
|
|
|
|
codeOff := stack.Pop64()
|
|
|
|
length := stack.Pop64()
|
2015-04-17 16:05:36 -07:00
|
|
|
data, ok := subslice(code, codeOff, length)
|
2015-03-17 21:46:26 -07:00
|
|
|
if !ok {
|
|
|
|
return nil, firstErr(err, ErrCodeOutOfBounds)
|
|
|
|
}
|
2015-04-17 16:05:36 -07:00
|
|
|
dest, ok := subslice(memory, memOff, length)
|
2015-03-17 21:46:26 -07:00
|
|
|
if !ok {
|
|
|
|
return nil, firstErr(err, ErrMemoryOutOfBounds)
|
2015-03-16 02:24:08 -07:00
|
|
|
}
|
2015-03-17 21:46:26 -07:00
|
|
|
copy(dest, data)
|
2015-03-29 00:07:10 -07:00
|
|
|
dbg.Printf(" => [%v, %v, %v] %X\n", memOff, codeOff, length, data)
|
2015-03-16 02:24:08 -07:00
|
|
|
|
2015-03-18 23:12:53 -07:00
|
|
|
case GASPRICE_DEPRECATED: // 0x3A
|
2015-03-28 23:44:07 -07:00
|
|
|
stack.Push(Zero256)
|
2015-03-29 00:07:10 -07:00
|
|
|
dbg.Printf(" => %X (GASPRICE IS DEPRECATED)\n")
|
2015-03-16 02:24:08 -07:00
|
|
|
|
|
|
|
case EXTCODESIZE: // 0x3B
|
2015-03-17 21:46:26 -07:00
|
|
|
addr := stack.Pop()
|
2015-07-24 12:32:22 -07:00
|
|
|
if useGasNegative(gas, GasGetAccount, &err) {
|
|
|
|
return nil, err
|
2015-03-17 21:46:26 -07:00
|
|
|
}
|
2015-04-17 16:05:36 -07:00
|
|
|
acc := vm.appState.GetAccount(addr)
|
2015-03-28 23:44:07 -07:00
|
|
|
if acc == nil {
|
|
|
|
return nil, firstErr(err, ErrUnknownAddress)
|
2015-03-17 21:46:26 -07:00
|
|
|
}
|
2015-03-28 23:44:07 -07:00
|
|
|
code := acc.Code
|
2015-06-25 20:28:34 -07:00
|
|
|
l := int64(len(code))
|
2015-03-16 02:24:08 -07:00
|
|
|
stack.Push64(l)
|
2015-03-29 00:07:10 -07:00
|
|
|
dbg.Printf(" => %d\n", l)
|
2015-03-16 02:24:08 -07:00
|
|
|
|
|
|
|
case EXTCODECOPY: // 0x3C
|
2015-03-17 21:46:26 -07:00
|
|
|
addr := stack.Pop()
|
2015-07-24 12:32:22 -07:00
|
|
|
if useGasNegative(gas, GasGetAccount, &err) {
|
|
|
|
return nil, err
|
2015-03-17 21:46:26 -07:00
|
|
|
}
|
2015-04-17 16:05:36 -07:00
|
|
|
acc := vm.appState.GetAccount(addr)
|
2015-03-28 23:44:07 -07:00
|
|
|
if acc == nil {
|
|
|
|
return nil, firstErr(err, ErrUnknownAddress)
|
2015-03-17 21:46:26 -07:00
|
|
|
}
|
2015-03-28 23:44:07 -07:00
|
|
|
code := acc.Code
|
2015-03-16 02:24:08 -07:00
|
|
|
memOff := stack.Pop64()
|
|
|
|
codeOff := stack.Pop64()
|
|
|
|
length := stack.Pop64()
|
2015-04-17 16:05:36 -07:00
|
|
|
data, ok := subslice(code, codeOff, length)
|
2015-03-17 21:46:26 -07:00
|
|
|
if !ok {
|
2015-03-20 05:47:52 -07:00
|
|
|
return nil, firstErr(err, ErrCodeOutOfBounds)
|
2015-03-16 02:24:08 -07:00
|
|
|
}
|
2015-04-17 16:05:36 -07:00
|
|
|
dest, ok := subslice(memory, memOff, length)
|
2015-03-17 21:46:26 -07:00
|
|
|
if !ok {
|
2015-03-20 05:47:52 -07:00
|
|
|
return nil, firstErr(err, ErrMemoryOutOfBounds)
|
2015-03-17 21:46:26 -07:00
|
|
|
}
|
|
|
|
copy(dest, data)
|
2015-03-29 00:07:10 -07:00
|
|
|
dbg.Printf(" => [%v, %v, %v] %X\n", memOff, codeOff, length, data)
|
2015-03-16 02:24:08 -07:00
|
|
|
|
|
|
|
case BLOCKHASH: // 0x40
|
2015-03-28 23:44:07 -07:00
|
|
|
stack.Push(Zero256)
|
2015-03-29 00:07:10 -07:00
|
|
|
dbg.Printf(" => 0x%X (NOT SUPPORTED)\n", stack.Peek().Bytes())
|
2015-03-16 02:24:08 -07:00
|
|
|
|
|
|
|
case COINBASE: // 0x41
|
2015-03-28 23:44:07 -07:00
|
|
|
stack.Push(Zero256)
|
2015-03-29 00:07:10 -07:00
|
|
|
dbg.Printf(" => 0x%X (NOT SUPPORTED)\n", stack.Peek().Bytes())
|
2015-03-16 02:24:08 -07:00
|
|
|
|
|
|
|
case TIMESTAMP: // 0x42
|
|
|
|
time := vm.params.BlockTime
|
2015-06-25 20:28:34 -07:00
|
|
|
stack.Push64(int64(time))
|
2015-03-29 00:07:10 -07:00
|
|
|
dbg.Printf(" => 0x%X\n", time)
|
2015-03-16 02:24:08 -07:00
|
|
|
|
|
|
|
case BLOCKHEIGHT: // 0x43
|
2015-06-25 20:28:34 -07:00
|
|
|
number := int64(vm.params.BlockHeight)
|
2015-03-16 02:24:08 -07:00
|
|
|
stack.Push64(number)
|
2015-03-29 00:07:10 -07:00
|
|
|
dbg.Printf(" => 0x%X\n", number)
|
2015-03-16 02:24:08 -07:00
|
|
|
|
|
|
|
case GASLIMIT: // 0x45
|
|
|
|
stack.Push64(vm.params.GasLimit)
|
2015-03-29 00:07:10 -07:00
|
|
|
dbg.Printf(" => %v\n", vm.params.GasLimit)
|
2015-03-16 02:24:08 -07:00
|
|
|
|
|
|
|
case POP: // 0x50
|
2015-07-24 12:32:22 -07:00
|
|
|
popped := stack.Pop()
|
|
|
|
dbg.Printf(" => 0x%X\n", popped)
|
2015-03-16 02:24:08 -07:00
|
|
|
|
|
|
|
case MLOAD: // 0x51
|
|
|
|
offset := stack.Pop64()
|
2015-04-17 16:05:36 -07:00
|
|
|
data, ok := subslice(memory, offset, 32)
|
2015-03-17 21:46:26 -07:00
|
|
|
if !ok {
|
2015-03-20 05:47:52 -07:00
|
|
|
return nil, firstErr(err, ErrMemoryOutOfBounds)
|
2015-03-17 21:46:26 -07:00
|
|
|
}
|
2015-04-17 16:05:36 -07:00
|
|
|
stack.Push(LeftPadWord256(data))
|
2015-03-29 00:07:10 -07:00
|
|
|
dbg.Printf(" => 0x%X\n", data)
|
2015-03-17 21:46:26 -07:00
|
|
|
|
|
|
|
case MSTORE: // 0x52
|
|
|
|
offset, data := stack.Pop64(), stack.Pop()
|
2015-04-17 16:05:36 -07:00
|
|
|
dest, ok := subslice(memory, offset, 32)
|
2015-03-17 21:46:26 -07:00
|
|
|
if !ok {
|
2015-03-20 05:47:52 -07:00
|
|
|
return nil, firstErr(err, ErrMemoryOutOfBounds)
|
2015-03-17 21:46:26 -07:00
|
|
|
}
|
2015-04-17 16:05:36 -07:00
|
|
|
copy(dest, data[:])
|
2015-03-29 00:07:10 -07:00
|
|
|
dbg.Printf(" => 0x%X\n", data)
|
2015-03-16 02:24:08 -07:00
|
|
|
|
2015-03-17 21:46:26 -07:00
|
|
|
case MSTORE8: // 0x53
|
|
|
|
offset, val := stack.Pop64(), byte(stack.Pop64()&0xFF)
|
|
|
|
if len(memory) <= int(offset) {
|
2015-03-20 05:47:52 -07:00
|
|
|
return nil, firstErr(err, ErrMemoryOutOfBounds)
|
2015-03-17 21:46:26 -07:00
|
|
|
}
|
|
|
|
memory[offset] = val
|
2015-03-29 00:07:10 -07:00
|
|
|
dbg.Printf(" => [%v] 0x%X\n", offset, val)
|
2015-03-17 21:46:26 -07:00
|
|
|
|
|
|
|
case SLOAD: // 0x54
|
|
|
|
loc := stack.Pop()
|
2015-03-28 23:44:07 -07:00
|
|
|
data := vm.appState.GetStorage(callee.Address, loc)
|
2015-03-29 19:05:29 -07:00
|
|
|
stack.Push(data)
|
2015-03-29 00:07:10 -07:00
|
|
|
dbg.Printf(" {0x%X : 0x%X}\n", loc, data)
|
2015-03-17 21:46:26 -07:00
|
|
|
|
|
|
|
case SSTORE: // 0x55
|
|
|
|
loc, data := stack.Pop(), stack.Pop()
|
2015-07-24 12:32:22 -07:00
|
|
|
if useGasNegative(gas, GasStorageUpdate, &err) {
|
|
|
|
return nil, err
|
|
|
|
}
|
2015-03-28 23:44:07 -07:00
|
|
|
vm.appState.SetStorage(callee.Address, loc, data)
|
2015-03-29 00:07:10 -07:00
|
|
|
dbg.Printf(" {0x%X : 0x%X}\n", loc, data)
|
2015-03-16 02:24:08 -07:00
|
|
|
|
2015-03-17 21:46:26 -07:00
|
|
|
case JUMP: // 0x56
|
2015-03-21 15:51:36 -07:00
|
|
|
err = jump(code, stack.Pop64(), &pc)
|
2015-03-16 02:24:08 -07:00
|
|
|
continue
|
|
|
|
|
2015-03-17 21:46:26 -07:00
|
|
|
case JUMPI: // 0x57
|
2015-04-17 16:05:36 -07:00
|
|
|
pos, cond := stack.Pop64(), stack.Pop()
|
|
|
|
if !cond.IsZero() {
|
2015-03-21 15:51:36 -07:00
|
|
|
err = jump(code, pos, &pc)
|
2015-03-16 02:24:08 -07:00
|
|
|
continue
|
|
|
|
}
|
2015-03-29 00:07:10 -07:00
|
|
|
dbg.Printf(" ~> false\n")
|
2015-03-17 21:46:26 -07:00
|
|
|
|
|
|
|
case PC: // 0x58
|
|
|
|
stack.Push64(pc)
|
2015-03-16 02:24:08 -07:00
|
|
|
|
2015-03-17 21:46:26 -07:00
|
|
|
case MSIZE: // 0x59
|
2015-06-25 20:28:34 -07:00
|
|
|
stack.Push64(int64(len(memory)))
|
2015-03-16 02:24:08 -07:00
|
|
|
|
2015-03-17 21:46:26 -07:00
|
|
|
case GAS: // 0x5A
|
|
|
|
stack.Push64(*gas)
|
2015-03-29 00:07:10 -07:00
|
|
|
dbg.Printf(" => %X\n", *gas)
|
2015-03-16 02:24:08 -07:00
|
|
|
|
2015-03-17 21:46:26 -07:00
|
|
|
case JUMPDEST: // 0x5B
|
2015-03-29 00:07:10 -07:00
|
|
|
dbg.Printf("\n")
|
2015-03-17 21:46:26 -07:00
|
|
|
// Do nothing
|
2015-03-16 02:24:08 -07:00
|
|
|
|
|
|
|
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:
|
2015-06-25 20:28:34 -07:00
|
|
|
a := int64(op - PUSH1 + 1)
|
2015-04-17 16:05:36 -07:00
|
|
|
codeSegment, ok := subslice(code, pc+1, a)
|
2015-03-17 21:46:26 -07:00
|
|
|
if !ok {
|
|
|
|
return nil, firstErr(err, ErrCodeOutOfBounds)
|
|
|
|
}
|
2015-04-17 16:05:36 -07:00
|
|
|
res := LeftPadWord256(codeSegment)
|
2015-03-17 21:46:26 -07:00
|
|
|
stack.Push(res)
|
2015-03-16 02:24:08 -07:00
|
|
|
pc += a
|
2015-03-29 00:07:10 -07:00
|
|
|
dbg.Printf(" => 0x%X\n", res)
|
2015-05-14 20:58:24 -04:00
|
|
|
//stack.Print(10)
|
2015-03-16 02:24:08 -07:00
|
|
|
|
|
|
|
case DUP1, DUP2, DUP3, DUP4, DUP5, DUP6, DUP7, DUP8, DUP9, DUP10, DUP11, DUP12, DUP13, DUP14, DUP15, DUP16:
|
|
|
|
n := int(op - DUP1 + 1)
|
2015-03-17 21:46:26 -07:00
|
|
|
stack.Dup(n)
|
2015-03-29 00:07:10 -07:00
|
|
|
dbg.Printf(" => [%d] 0x%X\n", n, stack.Peek().Bytes())
|
2015-03-16 02:24:08 -07:00
|
|
|
|
|
|
|
case SWAP1, SWAP2, SWAP3, SWAP4, SWAP5, SWAP6, SWAP7, SWAP8, SWAP9, SWAP10, SWAP11, SWAP12, SWAP13, SWAP14, SWAP15, SWAP16:
|
|
|
|
n := int(op - SWAP1 + 2)
|
2015-03-17 21:46:26 -07:00
|
|
|
stack.Swap(n)
|
2015-04-17 20:51:01 -07:00
|
|
|
dbg.Printf(" => [%d] %X\n", n, stack.Peek())
|
2015-05-14 20:58:24 -04:00
|
|
|
//stack.Print(10)
|
2015-03-16 02:24:08 -07:00
|
|
|
|
|
|
|
case LOG0, LOG1, LOG2, LOG3, LOG4:
|
|
|
|
n := int(op - LOG0)
|
2015-03-28 23:44:07 -07:00
|
|
|
topics := make([]Word256, n)
|
2015-03-17 21:46:26 -07:00
|
|
|
offset, size := stack.Pop64(), stack.Pop64()
|
2015-03-16 02:24:08 -07:00
|
|
|
for i := 0; i < n; i++ {
|
2015-03-17 21:46:26 -07:00
|
|
|
topics[i] = stack.Pop()
|
2015-03-16 02:24:08 -07:00
|
|
|
}
|
2015-04-17 16:05:36 -07:00
|
|
|
data, ok := subslice(memory, offset, size)
|
2015-03-17 21:46:26 -07:00
|
|
|
if !ok {
|
|
|
|
return nil, firstErr(err, ErrMemoryOutOfBounds)
|
|
|
|
}
|
|
|
|
log := &Log{
|
|
|
|
callee.Address,
|
|
|
|
topics,
|
|
|
|
data,
|
|
|
|
vm.params.BlockHeight,
|
|
|
|
}
|
|
|
|
vm.appState.AddLog(log)
|
2015-07-11 17:11:14 +02:00
|
|
|
if vm.evc != nil {
|
|
|
|
eventId := types.EventStringLogEvent(callee.Address.Postfix(20))
|
|
|
|
fmt.Printf("eventId: %s\n", eventId)
|
|
|
|
vm.evc.FireEvent(eventId, log)
|
|
|
|
}
|
|
|
|
// Using sol-log for this as well since 'log' will print garbage.
|
|
|
|
dbg.Printf(" => T:%X D:%X\n", log.Topics, log.Data)
|
2015-03-16 02:24:08 -07:00
|
|
|
|
2015-03-17 21:46:26 -07:00
|
|
|
case CREATE: // 0xF0
|
2015-07-07 14:26:05 -07:00
|
|
|
if !HasPermission(vm.appState, callee, ptypes.CreateContract) {
|
2015-05-21 01:13:55 -04:00
|
|
|
return nil, ErrPermission{"create_contract"}
|
2015-05-12 20:40:19 -04:00
|
|
|
}
|
2015-03-20 20:50:38 -07:00
|
|
|
contractValue := stack.Pop64()
|
2015-03-17 21:46:26 -07:00
|
|
|
offset, size := stack.Pop64(), stack.Pop64()
|
2015-04-17 16:05:36 -07:00
|
|
|
input, ok := subslice(memory, offset, size)
|
2015-03-17 21:46:26 -07:00
|
|
|
if !ok {
|
|
|
|
return nil, firstErr(err, ErrMemoryOutOfBounds)
|
|
|
|
}
|
2015-03-16 02:24:08 -07:00
|
|
|
|
2015-03-17 21:46:26 -07:00
|
|
|
// Check balance
|
2015-03-20 20:50:38 -07:00
|
|
|
if callee.Balance < contractValue {
|
2015-03-17 21:46:26 -07:00
|
|
|
return nil, firstErr(err, ErrInsufficientBalance)
|
|
|
|
}
|
2015-03-16 02:24:08 -07:00
|
|
|
|
2015-03-17 21:46:26 -07:00
|
|
|
// TODO charge for gas to create account _ the code length * GasCreateByte
|
2015-03-16 02:24:08 -07:00
|
|
|
|
2015-03-28 23:44:07 -07:00
|
|
|
newAccount := vm.appState.CreateAccount(callee)
|
|
|
|
// Run the input to get the contract code.
|
|
|
|
ret, err_ := vm.Call(callee, newAccount, input, input, contractValue, gas)
|
|
|
|
if err_ != nil {
|
|
|
|
stack.Push(Zero256)
|
2015-03-16 02:24:08 -07:00
|
|
|
} else {
|
2015-03-28 23:44:07 -07:00
|
|
|
newAccount.Code = ret // Set the code
|
2015-04-17 20:51:01 -07:00
|
|
|
stack.Push(newAccount.Address)
|
2015-03-16 02:24:08 -07:00
|
|
|
}
|
|
|
|
|
2015-03-17 21:46:26 -07:00
|
|
|
case CALL, CALLCODE: // 0xF1, 0xF2
|
2015-07-07 14:26:05 -07:00
|
|
|
if !HasPermission(vm.appState, callee, ptypes.Call) {
|
2015-05-12 20:40:19 -04:00
|
|
|
return nil, ErrPermission{"call"}
|
|
|
|
}
|
2015-03-17 21:46:26 -07:00
|
|
|
gasLimit := stack.Pop64()
|
|
|
|
addr, value := stack.Pop(), stack.Pop64()
|
|
|
|
inOffset, inSize := stack.Pop64(), stack.Pop64() // inputs
|
|
|
|
retOffset, retSize := stack.Pop64(), stack.Pop64() // outputs
|
2015-03-29 00:07:10 -07:00
|
|
|
dbg.Printf(" => %X\n", addr)
|
2015-03-16 02:24:08 -07:00
|
|
|
|
|
|
|
// Get the arguments from the memory
|
2015-04-17 16:05:36 -07:00
|
|
|
args, ok := subslice(memory, inOffset, inSize)
|
2015-03-17 21:46:26 -07:00
|
|
|
if !ok {
|
|
|
|
return nil, firstErr(err, ErrMemoryOutOfBounds)
|
2015-03-16 02:24:08 -07:00
|
|
|
}
|
|
|
|
|
2015-03-17 21:46:26 -07:00
|
|
|
// Ensure that gasLimit is reasonable
|
|
|
|
if *gas < gasLimit {
|
|
|
|
return nil, firstErr(err, ErrInsufficientGas)
|
2015-03-16 02:24:08 -07:00
|
|
|
} else {
|
2015-03-17 21:46:26 -07:00
|
|
|
*gas -= gasLimit
|
|
|
|
// NOTE: we will return any used gas later.
|
2015-03-16 02:24:08 -07:00
|
|
|
}
|
|
|
|
|
2015-03-17 21:46:26 -07:00
|
|
|
// Begin execution
|
|
|
|
var ret []byte
|
|
|
|
var err error
|
2015-07-21 17:27:47 -04:00
|
|
|
if nativeContract := registeredNativeContracts[addr]; nativeContract != nil {
|
2015-03-20 05:47:52 -07:00
|
|
|
// Native contract
|
2015-07-21 17:27:47 -04:00
|
|
|
ret, err = nativeContract(vm.appState, callee, args, &gasLimit)
|
|
|
|
|
2015-07-23 23:57:28 -07:00
|
|
|
// for now we fire the Call event. maybe later we'll fire more particulars
|
2015-07-21 17:27:47 -04:00
|
|
|
var exception string
|
|
|
|
if err != nil {
|
|
|
|
exception = err.Error()
|
|
|
|
}
|
2015-07-23 23:57:28 -07:00
|
|
|
vm.fireCallEvent(&exception, &ret, callee, &Account{Address: addr}, args, value, gas)
|
2015-03-16 02:24:08 -07:00
|
|
|
} else {
|
2015-03-20 05:47:52 -07:00
|
|
|
// EVM contract
|
2015-07-24 12:32:22 -07:00
|
|
|
if useGasNegative(gas, GasGetAccount, &err) {
|
|
|
|
return nil, err
|
2015-03-17 21:46:26 -07:00
|
|
|
}
|
2015-04-17 16:05:36 -07:00
|
|
|
acc := vm.appState.GetAccount(addr)
|
2015-05-14 20:23:36 -04:00
|
|
|
// 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
|
2015-03-17 21:46:26 -07:00
|
|
|
if op == CALLCODE {
|
2015-05-14 20:23:36 -04:00
|
|
|
if acc == nil {
|
|
|
|
return nil, firstErr(err, ErrUnknownAddress)
|
|
|
|
}
|
2015-03-28 23:44:07 -07:00
|
|
|
ret, err = vm.Call(callee, callee, acc.Code, args, value, gas)
|
2015-03-17 21:46:26 -07:00
|
|
|
} else {
|
2015-05-14 20:23:36 -04:00
|
|
|
if acc == nil {
|
2015-07-21 13:30:24 -04:00
|
|
|
// nil account means we're sending funds to a new account
|
|
|
|
if !HasPermission(vm.appState, caller, ptypes.CreateAccount) {
|
|
|
|
return nil, ErrPermission{"create_account"}
|
2015-05-14 20:23:36 -04:00
|
|
|
}
|
2015-07-21 13:30:24 -04:00
|
|
|
acc = &Account{Address: addr}
|
|
|
|
vm.appState.UpdateAccount(acc)
|
|
|
|
// send funds to new account
|
|
|
|
ret, err = vm.Call(callee, acc, acc.Code, args, value, gas)
|
2015-07-20 14:24:57 -04:00
|
|
|
} else {
|
|
|
|
// call standard contract
|
|
|
|
ret, err = vm.Call(callee, acc, acc.Code, args, value, gas)
|
2015-05-14 20:23:36 -04:00
|
|
|
}
|
2015-03-17 21:46:26 -07:00
|
|
|
}
|
2015-03-16 02:24:08 -07:00
|
|
|
}
|
|
|
|
|
2015-03-17 21:46:26 -07:00
|
|
|
// Push result
|
|
|
|
if err != nil {
|
2015-05-21 15:51:57 -04:00
|
|
|
dbg.Printf("error on call: %s\n", err.Error())
|
2015-03-28 23:44:07 -07:00
|
|
|
stack.Push(Zero256)
|
2015-03-17 21:46:26 -07:00
|
|
|
} else {
|
2015-03-28 23:44:07 -07:00
|
|
|
stack.Push(One256)
|
2015-04-17 16:05:36 -07:00
|
|
|
dest, ok := subslice(memory, retOffset, retSize)
|
2015-03-17 21:46:26 -07:00
|
|
|
if !ok {
|
|
|
|
return nil, firstErr(err, ErrMemoryOutOfBounds)
|
|
|
|
}
|
|
|
|
copy(dest, ret)
|
|
|
|
}
|
2015-03-16 02:24:08 -07:00
|
|
|
|
2015-03-17 21:46:26 -07:00
|
|
|
// Handle remaining gas.
|
|
|
|
*gas += gasLimit
|
2015-03-16 02:24:08 -07:00
|
|
|
|
2015-03-29 00:07:10 -07:00
|
|
|
dbg.Printf("resume %X (%v)\n", callee.Address, gas)
|
2015-03-16 02:24:08 -07:00
|
|
|
|
2015-03-17 21:46:26 -07:00
|
|
|
case RETURN: // 0xF3
|
|
|
|
offset, size := stack.Pop64(), stack.Pop64()
|
2015-04-17 16:05:36 -07:00
|
|
|
ret, ok := subslice(memory, offset, size)
|
2015-03-17 21:46:26 -07:00
|
|
|
if !ok {
|
|
|
|
return nil, firstErr(err, ErrMemoryOutOfBounds)
|
|
|
|
}
|
2015-03-29 00:07:10 -07:00
|
|
|
dbg.Printf(" => [%v, %v] (%d) 0x%X\n", offset, size, len(ret), ret)
|
2015-03-17 21:46:26 -07:00
|
|
|
return ret, nil
|
2015-03-16 02:24:08 -07:00
|
|
|
|
2015-03-17 21:46:26 -07:00
|
|
|
case SUICIDE: // 0xFF
|
|
|
|
addr := stack.Pop()
|
2015-07-24 12:32:22 -07:00
|
|
|
if useGasNegative(gas, GasGetAccount, &err) {
|
|
|
|
return nil, err
|
2015-03-17 21:46:26 -07:00
|
|
|
}
|
2015-03-29 00:07:10 -07:00
|
|
|
// TODO if the receiver is , then make it the fee.
|
2015-04-17 16:05:36 -07:00
|
|
|
receiver := vm.appState.GetAccount(addr)
|
2015-03-28 23:44:07 -07:00
|
|
|
if receiver == nil {
|
|
|
|
return nil, firstErr(err, ErrUnknownAddress)
|
2015-03-17 21:46:26 -07:00
|
|
|
}
|
|
|
|
balance := callee.Balance
|
|
|
|
receiver.Balance += balance
|
|
|
|
vm.appState.UpdateAccount(receiver)
|
2015-03-28 23:44:07 -07:00
|
|
|
vm.appState.RemoveAccount(callee)
|
2015-03-29 00:07:10 -07:00
|
|
|
dbg.Printf(" => (%X) %v\n", addr[:4], balance)
|
2015-03-16 02:24:08 -07:00
|
|
|
fallthrough
|
|
|
|
|
|
|
|
default:
|
2015-03-29 00:07:10 -07:00
|
|
|
dbg.Printf("(pc) %-3v Invalid opcode %X\n", pc, op)
|
2015-06-26 01:50:31 +00:00
|
|
|
return nil, fmt.Errorf("Invalid opcode %X", op)
|
2015-03-16 02:24:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
pc++
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-25 20:28:34 -07:00
|
|
|
func subslice(data []byte, offset, length int64) (ret []byte, ok bool) {
|
|
|
|
size := int64(len(data))
|
2015-03-16 02:24:08 -07:00
|
|
|
if size < offset {
|
|
|
|
return nil, false
|
|
|
|
} else if size < offset+length {
|
2015-04-17 13:18:50 -07:00
|
|
|
ret, ok = data[offset:], true
|
|
|
|
ret = RightPadBytes(ret, 32)
|
2015-03-16 02:24:08 -07:00
|
|
|
} else {
|
2015-03-21 15:51:36 -07:00
|
|
|
ret, ok = data[offset:offset+length], true
|
2015-03-16 02:24:08 -07:00
|
|
|
}
|
2015-04-17 13:18:50 -07:00
|
|
|
|
2015-03-21 15:51:36 -07:00
|
|
|
return
|
2015-03-16 02:24:08 -07:00
|
|
|
}
|
|
|
|
|
2015-04-17 17:43:45 -07:00
|
|
|
func rightMostBytes(data []byte, n int) []byte {
|
|
|
|
size := MinInt(len(data), n)
|
|
|
|
offset := len(data) - size
|
|
|
|
return data[offset:]
|
|
|
|
}
|
|
|
|
|
2015-06-25 20:28:34 -07:00
|
|
|
func codeGetOp(code []byte, n int64) OpCode {
|
|
|
|
if int64(len(code)) <= n {
|
2015-03-17 21:46:26 -07:00
|
|
|
return OpCode(0) // stop
|
2015-03-16 02:24:08 -07:00
|
|
|
} else {
|
2015-03-17 21:46:26 -07:00
|
|
|
return OpCode(code[n])
|
2015-03-16 02:24:08 -07:00
|
|
|
}
|
|
|
|
}
|
2015-03-17 21:46:26 -07:00
|
|
|
|
2015-06-25 20:28:34 -07:00
|
|
|
func jump(code []byte, to int64, pc *int64) (err error) {
|
2015-03-17 21:46:26 -07:00
|
|
|
dest := codeGetOp(code, to)
|
|
|
|
if dest != JUMPDEST {
|
2015-03-29 00:07:10 -07:00
|
|
|
dbg.Printf(" ~> %v invalid jump dest %v\n", to, dest)
|
2015-03-17 21:46:26 -07:00
|
|
|
return ErrInvalidJumpDest
|
|
|
|
}
|
2015-03-29 00:07:10 -07:00
|
|
|
dbg.Printf(" ~> %v\n", to)
|
2015-03-21 15:51:36 -07:00
|
|
|
*pc = to
|
2015-03-17 21:46:26 -07:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func firstErr(errA, errB error) error {
|
|
|
|
if errA != nil {
|
|
|
|
return errA
|
|
|
|
} else {
|
|
|
|
return errB
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-25 20:28:34 -07:00
|
|
|
func transfer(from, to *Account, amount int64) error {
|
2015-03-20 05:47:52 -07:00
|
|
|
if from.Balance < amount {
|
|
|
|
return ErrInsufficientBalance
|
|
|
|
} else {
|
|
|
|
from.Balance -= amount
|
|
|
|
to.Balance += amount
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|