forked from cerc-io/plugeth
VmDebug => StdVm
This commit is contained in:
parent
06bfe19f05
commit
89c69a1d25
@ -9,11 +9,10 @@ import (
|
|||||||
|
|
||||||
var vmlogger = logger.NewLogger("VM")
|
var vmlogger = logger.NewLogger("VM")
|
||||||
|
|
||||||
type Type int
|
type Type byte
|
||||||
|
|
||||||
const (
|
const (
|
||||||
StandardVmTy Type = iota
|
StdVmTy Type = iota
|
||||||
DebugVmTy
|
|
||||||
JitVmTy
|
JitVmTy
|
||||||
|
|
||||||
MaxVmTy
|
MaxVmTy
|
||||||
|
969
vm/vm.go
969
vm/vm.go
@ -1,38 +1,961 @@
|
|||||||
package vm
|
package vm
|
||||||
|
|
||||||
import "math/big"
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
// BIG FAT WARNING. THIS VM IS NOT YET IS USE!
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
// I want to get all VM tests pass first before updating this VM
|
"github.com/ethereum/go-ethereum/ethutil"
|
||||||
|
"github.com/ethereum/go-ethereum/state"
|
||||||
|
)
|
||||||
|
|
||||||
type Vm struct {
|
type Vm struct {
|
||||||
env Environment
|
env Environment
|
||||||
err error
|
|
||||||
depth int
|
logTy byte
|
||||||
|
logStr string
|
||||||
|
|
||||||
|
err error
|
||||||
|
|
||||||
|
// Debugging
|
||||||
|
Dbg Debugger
|
||||||
|
|
||||||
|
BreakPoints []int64
|
||||||
|
Stepping bool
|
||||||
|
Fn string
|
||||||
|
|
||||||
|
Recoverable bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(env Environment, typ Type) VirtualMachine {
|
func NewVm(env Environment) *Vm {
|
||||||
switch typ {
|
lt := LogTyPretty
|
||||||
case DebugVmTy:
|
if ethutil.Config.Diff {
|
||||||
return NewDebugVm(env)
|
lt = LogTyDiff
|
||||||
case JitVmTy:
|
}
|
||||||
return NewJitVm(env)
|
|
||||||
default:
|
return &Vm{env: env, logTy: lt, Recoverable: true}
|
||||||
return &Vm{env: env}
|
}
|
||||||
|
|
||||||
|
func (self *Vm) Run(me, caller ContextRef, code []byte, value, gas, price *big.Int, callData []byte) (ret []byte, err error) {
|
||||||
|
self.env.SetDepth(self.env.Depth() + 1)
|
||||||
|
|
||||||
|
msg := self.env.State().Manifest().AddMessage(&state.Message{
|
||||||
|
To: me.Address(), From: caller.Address(),
|
||||||
|
Input: callData,
|
||||||
|
Origin: self.env.Origin(),
|
||||||
|
Timestamp: self.env.Time(), Coinbase: self.env.Coinbase(), Number: self.env.BlockNumber(),
|
||||||
|
Value: value,
|
||||||
|
})
|
||||||
|
context := NewContext(caller, me, code, gas, price)
|
||||||
|
|
||||||
|
vmlogger.Debugf("(%d) (%x) %x (code=%d) gas: %v (d) %x\n", self.env.Depth(), caller.Address()[:4], context.Address(), len(code), context.Gas, callData)
|
||||||
|
|
||||||
|
if self.Recoverable {
|
||||||
|
// Recover from any require exception
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
self.Printf(" %v", r).Endl()
|
||||||
|
|
||||||
|
context.UseGas(context.Gas)
|
||||||
|
|
||||||
|
ret = context.Return(nil)
|
||||||
|
|
||||||
|
err = fmt.Errorf("%v", r)
|
||||||
|
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
if p := Precompiled[string(me.Address())]; p != nil {
|
||||||
|
return self.RunPrecompiled(p, callData, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
op OpCode
|
||||||
|
|
||||||
|
destinations = analyseJumpDests(context.Code)
|
||||||
|
mem = NewMemory()
|
||||||
|
stack = NewStack()
|
||||||
|
pc uint64 = 0
|
||||||
|
step = 0
|
||||||
|
prevStep = 0
|
||||||
|
statedb = self.env.State()
|
||||||
|
|
||||||
|
jump = func(from uint64, to *big.Int) {
|
||||||
|
p := to.Uint64()
|
||||||
|
|
||||||
|
nop := context.GetOp(p)
|
||||||
|
if !destinations.Has(p) {
|
||||||
|
panic(fmt.Sprintf("invalid jump destination (%v) %v", nop, p))
|
||||||
|
}
|
||||||
|
|
||||||
|
self.Printf(" ~> %v", to)
|
||||||
|
pc = to.Uint64()
|
||||||
|
|
||||||
|
self.Endl()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Don't bother with the execution if there's no code.
|
||||||
|
if len(code) == 0 {
|
||||||
|
return context.Return(nil), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
prevStep = step
|
||||||
|
// The base for all big integer arithmetic
|
||||||
|
base := new(big.Int)
|
||||||
|
|
||||||
|
step++
|
||||||
|
// Get the memory location of pc
|
||||||
|
op = context.GetOp(pc)
|
||||||
|
|
||||||
|
self.Printf("(pc) %-3d -o- %-14s (m) %-4d (s) %-4d ", pc, op.String(), mem.Len(), stack.Len())
|
||||||
|
|
||||||
|
newMemSize, gas := self.calculateGasAndSize(context, caller, op, statedb, mem, stack)
|
||||||
|
|
||||||
|
self.Printf("(g) %-3v (%v)", gas, context.Gas)
|
||||||
|
|
||||||
|
if !context.UseGas(gas) {
|
||||||
|
self.Endl()
|
||||||
|
|
||||||
|
tmp := new(big.Int).Set(context.Gas)
|
||||||
|
|
||||||
|
context.UseGas(context.Gas)
|
||||||
|
|
||||||
|
return context.Return(nil), OOG(gas, tmp)
|
||||||
|
}
|
||||||
|
|
||||||
|
mem.Resize(newMemSize.Uint64())
|
||||||
|
|
||||||
|
switch op {
|
||||||
|
// 0x20 range
|
||||||
|
case ADD:
|
||||||
|
x, y := stack.Popn()
|
||||||
|
self.Printf(" %v + %v", y, x)
|
||||||
|
|
||||||
|
base.Add(y, x)
|
||||||
|
|
||||||
|
U256(base)
|
||||||
|
|
||||||
|
self.Printf(" = %v", base)
|
||||||
|
// Pop result back on the stack
|
||||||
|
stack.Push(base)
|
||||||
|
case SUB:
|
||||||
|
x, y := stack.Popn()
|
||||||
|
self.Printf(" %v - %v", y, x)
|
||||||
|
|
||||||
|
base.Sub(y, x)
|
||||||
|
|
||||||
|
U256(base)
|
||||||
|
|
||||||
|
self.Printf(" = %v", base)
|
||||||
|
// Pop result back on the stack
|
||||||
|
stack.Push(base)
|
||||||
|
case MUL:
|
||||||
|
x, y := stack.Popn()
|
||||||
|
self.Printf(" %v * %v", y, x)
|
||||||
|
|
||||||
|
base.Mul(y, x)
|
||||||
|
|
||||||
|
U256(base)
|
||||||
|
|
||||||
|
self.Printf(" = %v", base)
|
||||||
|
// Pop result back on the stack
|
||||||
|
stack.Push(base)
|
||||||
|
case DIV:
|
||||||
|
x, y := stack.Pop(), stack.Pop()
|
||||||
|
self.Printf(" %v / %v", x, y)
|
||||||
|
|
||||||
|
if y.Cmp(ethutil.Big0) != 0 {
|
||||||
|
base.Div(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
U256(base)
|
||||||
|
|
||||||
|
self.Printf(" = %v", base)
|
||||||
|
// Pop result back on the stack
|
||||||
|
stack.Push(base)
|
||||||
|
case SDIV:
|
||||||
|
x, y := S256(stack.Pop()), S256(stack.Pop())
|
||||||
|
|
||||||
|
self.Printf(" %v / %v", x, y)
|
||||||
|
|
||||||
|
if y.Cmp(ethutil.Big0) == 0 {
|
||||||
|
base.Set(ethutil.Big0)
|
||||||
|
} else {
|
||||||
|
n := new(big.Int)
|
||||||
|
if new(big.Int).Mul(x, y).Cmp(ethutil.Big0) < 0 {
|
||||||
|
n.SetInt64(-1)
|
||||||
|
} else {
|
||||||
|
n.SetInt64(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
base.Div(x.Abs(x), y.Abs(y)).Mul(base, n)
|
||||||
|
|
||||||
|
U256(base)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.Printf(" = %v", base)
|
||||||
|
stack.Push(base)
|
||||||
|
case MOD:
|
||||||
|
x, y := stack.Pop(), stack.Pop()
|
||||||
|
|
||||||
|
self.Printf(" %v %% %v", x, y)
|
||||||
|
|
||||||
|
if y.Cmp(ethutil.Big0) == 0 {
|
||||||
|
base.Set(ethutil.Big0)
|
||||||
|
} else {
|
||||||
|
base.Mod(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
U256(base)
|
||||||
|
|
||||||
|
self.Printf(" = %v", base)
|
||||||
|
stack.Push(base)
|
||||||
|
case SMOD:
|
||||||
|
x, y := S256(stack.Pop()), S256(stack.Pop())
|
||||||
|
|
||||||
|
self.Printf(" %v %% %v", x, y)
|
||||||
|
|
||||||
|
if y.Cmp(ethutil.Big0) == 0 {
|
||||||
|
base.Set(ethutil.Big0)
|
||||||
|
} else {
|
||||||
|
n := new(big.Int)
|
||||||
|
if x.Cmp(ethutil.Big0) < 0 {
|
||||||
|
n.SetInt64(-1)
|
||||||
|
} else {
|
||||||
|
n.SetInt64(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
base.Mod(x.Abs(x), y.Abs(y)).Mul(base, n)
|
||||||
|
|
||||||
|
U256(base)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.Printf(" = %v", base)
|
||||||
|
stack.Push(base)
|
||||||
|
|
||||||
|
case EXP:
|
||||||
|
x, y := stack.Popn()
|
||||||
|
|
||||||
|
self.Printf(" %v ** %v", y, x)
|
||||||
|
|
||||||
|
base.Exp(y, x, Pow256)
|
||||||
|
|
||||||
|
U256(base)
|
||||||
|
|
||||||
|
self.Printf(" = %v", base)
|
||||||
|
|
||||||
|
stack.Push(base)
|
||||||
|
case SIGNEXTEND:
|
||||||
|
back := stack.Pop().Uint64()
|
||||||
|
if back < 31 {
|
||||||
|
bit := uint(back*8 + 7)
|
||||||
|
num := stack.Pop()
|
||||||
|
mask := new(big.Int).Lsh(ethutil.Big1, bit)
|
||||||
|
mask.Sub(mask, ethutil.Big1)
|
||||||
|
if ethutil.BitTest(num, int(bit)) {
|
||||||
|
num.Or(num, mask.Not(mask))
|
||||||
|
} else {
|
||||||
|
num.And(num, mask)
|
||||||
|
}
|
||||||
|
|
||||||
|
num = U256(num)
|
||||||
|
|
||||||
|
self.Printf(" = %v", num)
|
||||||
|
|
||||||
|
stack.Push(num)
|
||||||
|
}
|
||||||
|
case NOT:
|
||||||
|
base.Sub(Pow256, stack.Pop()).Sub(base, ethutil.Big1)
|
||||||
|
|
||||||
|
// Not needed
|
||||||
|
//base = U256(base)
|
||||||
|
|
||||||
|
stack.Push(base)
|
||||||
|
case LT:
|
||||||
|
x, y := stack.Popn()
|
||||||
|
self.Printf(" %v < %v", y, x)
|
||||||
|
// x < y
|
||||||
|
if y.Cmp(x) < 0 {
|
||||||
|
stack.Push(ethutil.BigTrue)
|
||||||
|
} else {
|
||||||
|
stack.Push(ethutil.BigFalse)
|
||||||
|
}
|
||||||
|
case GT:
|
||||||
|
x, y := stack.Popn()
|
||||||
|
self.Printf(" %v > %v", y, x)
|
||||||
|
|
||||||
|
// x > y
|
||||||
|
if y.Cmp(x) > 0 {
|
||||||
|
stack.Push(ethutil.BigTrue)
|
||||||
|
} else {
|
||||||
|
stack.Push(ethutil.BigFalse)
|
||||||
|
}
|
||||||
|
|
||||||
|
case SLT:
|
||||||
|
y, x := S256(stack.Pop()), S256(stack.Pop())
|
||||||
|
self.Printf(" %v < %v", y, x)
|
||||||
|
// x < y
|
||||||
|
if y.Cmp(S256(x)) < 0 {
|
||||||
|
stack.Push(ethutil.BigTrue)
|
||||||
|
} else {
|
||||||
|
stack.Push(ethutil.BigFalse)
|
||||||
|
}
|
||||||
|
case SGT:
|
||||||
|
y, x := S256(stack.Pop()), S256(stack.Pop())
|
||||||
|
self.Printf(" %v > %v", y, x)
|
||||||
|
|
||||||
|
// x > y
|
||||||
|
if y.Cmp(x) > 0 {
|
||||||
|
stack.Push(ethutil.BigTrue)
|
||||||
|
} else {
|
||||||
|
stack.Push(ethutil.BigFalse)
|
||||||
|
}
|
||||||
|
|
||||||
|
case EQ:
|
||||||
|
x, y := stack.Popn()
|
||||||
|
self.Printf(" %v == %v", y, x)
|
||||||
|
|
||||||
|
// x == y
|
||||||
|
if x.Cmp(y) == 0 {
|
||||||
|
stack.Push(ethutil.BigTrue)
|
||||||
|
} else {
|
||||||
|
stack.Push(ethutil.BigFalse)
|
||||||
|
}
|
||||||
|
case ISZERO:
|
||||||
|
x := stack.Pop()
|
||||||
|
if x.Cmp(ethutil.BigFalse) > 0 {
|
||||||
|
stack.Push(ethutil.BigFalse)
|
||||||
|
} else {
|
||||||
|
stack.Push(ethutil.BigTrue)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 0x10 range
|
||||||
|
case AND:
|
||||||
|
x, y := stack.Popn()
|
||||||
|
self.Printf(" %v & %v", y, x)
|
||||||
|
|
||||||
|
stack.Push(base.And(y, x))
|
||||||
|
case OR:
|
||||||
|
x, y := stack.Popn()
|
||||||
|
self.Printf(" %v | %v", y, x)
|
||||||
|
|
||||||
|
stack.Push(base.Or(y, x))
|
||||||
|
case XOR:
|
||||||
|
x, y := stack.Popn()
|
||||||
|
self.Printf(" %v ^ %v", y, x)
|
||||||
|
|
||||||
|
stack.Push(base.Xor(y, x))
|
||||||
|
case BYTE:
|
||||||
|
val, th := stack.Popn()
|
||||||
|
|
||||||
|
if th.Cmp(big.NewInt(32)) < 0 {
|
||||||
|
byt := big.NewInt(int64(ethutil.LeftPadBytes(val.Bytes(), 32)[th.Int64()]))
|
||||||
|
|
||||||
|
base.Set(byt)
|
||||||
|
} else {
|
||||||
|
base.Set(ethutil.BigFalse)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.Printf(" => 0x%x", base.Bytes())
|
||||||
|
|
||||||
|
stack.Push(base)
|
||||||
|
case ADDMOD:
|
||||||
|
|
||||||
|
x := stack.Pop()
|
||||||
|
y := stack.Pop()
|
||||||
|
z := stack.Pop()
|
||||||
|
|
||||||
|
add := new(big.Int).Add(x, y)
|
||||||
|
if len(z.Bytes()) > 0 { // NOT 0x0
|
||||||
|
base.Mod(add, z)
|
||||||
|
|
||||||
|
U256(base)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.Printf(" %v + %v %% %v = %v", x, y, z, base)
|
||||||
|
|
||||||
|
stack.Push(base)
|
||||||
|
case MULMOD:
|
||||||
|
|
||||||
|
x := stack.Pop()
|
||||||
|
y := stack.Pop()
|
||||||
|
z := stack.Pop()
|
||||||
|
|
||||||
|
mul := new(big.Int).Mul(x, y)
|
||||||
|
if len(z.Bytes()) > 0 { // NOT 0x0
|
||||||
|
base.Mod(mul, z)
|
||||||
|
|
||||||
|
U256(base)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.Printf(" %v + %v %% %v = %v", x, y, z, base)
|
||||||
|
|
||||||
|
stack.Push(base)
|
||||||
|
|
||||||
|
// 0x20 range
|
||||||
|
case SHA3:
|
||||||
|
size, offset := stack.Popn()
|
||||||
|
data := crypto.Sha3(mem.Get(offset.Int64(), size.Int64()))
|
||||||
|
|
||||||
|
stack.Push(ethutil.BigD(data))
|
||||||
|
|
||||||
|
self.Printf(" => %x", data)
|
||||||
|
// 0x30 range
|
||||||
|
case ADDRESS:
|
||||||
|
stack.Push(ethutil.BigD(context.Address()))
|
||||||
|
|
||||||
|
self.Printf(" => %x", context.Address())
|
||||||
|
case BALANCE:
|
||||||
|
|
||||||
|
addr := stack.Pop().Bytes()
|
||||||
|
balance := statedb.GetBalance(addr)
|
||||||
|
|
||||||
|
stack.Push(balance)
|
||||||
|
|
||||||
|
self.Printf(" => %v (%x)", balance, addr)
|
||||||
|
case ORIGIN:
|
||||||
|
origin := self.env.Origin()
|
||||||
|
|
||||||
|
stack.Push(ethutil.BigD(origin))
|
||||||
|
|
||||||
|
self.Printf(" => %x", origin)
|
||||||
|
case CALLER:
|
||||||
|
caller := context.caller.Address()
|
||||||
|
stack.Push(ethutil.BigD(caller))
|
||||||
|
|
||||||
|
self.Printf(" => %x", caller)
|
||||||
|
case CALLVALUE:
|
||||||
|
stack.Push(value)
|
||||||
|
|
||||||
|
self.Printf(" => %v", value)
|
||||||
|
case CALLDATALOAD:
|
||||||
|
var (
|
||||||
|
offset = stack.Pop()
|
||||||
|
data = make([]byte, 32)
|
||||||
|
lenData = big.NewInt(int64(len(callData)))
|
||||||
|
)
|
||||||
|
|
||||||
|
if lenData.Cmp(offset) >= 0 {
|
||||||
|
length := new(big.Int).Add(offset, ethutil.Big32)
|
||||||
|
length = ethutil.BigMin(length, lenData)
|
||||||
|
|
||||||
|
copy(data, callData[offset.Int64():length.Int64()])
|
||||||
|
}
|
||||||
|
|
||||||
|
self.Printf(" => 0x%x", data)
|
||||||
|
|
||||||
|
stack.Push(ethutil.BigD(data))
|
||||||
|
case CALLDATASIZE:
|
||||||
|
l := int64(len(callData))
|
||||||
|
stack.Push(big.NewInt(l))
|
||||||
|
|
||||||
|
self.Printf(" => %d", l)
|
||||||
|
case CALLDATACOPY:
|
||||||
|
var (
|
||||||
|
size = uint64(len(callData))
|
||||||
|
mOff = stack.Pop().Uint64()
|
||||||
|
cOff = stack.Pop().Uint64()
|
||||||
|
l = stack.Pop().Uint64()
|
||||||
|
)
|
||||||
|
|
||||||
|
if cOff > size {
|
||||||
|
cOff = 0
|
||||||
|
l = 0
|
||||||
|
} else if cOff+l > size {
|
||||||
|
l = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
code := callData[cOff : cOff+l]
|
||||||
|
|
||||||
|
mem.Set(mOff, l, code)
|
||||||
|
|
||||||
|
self.Printf(" => [%v, %v, %v] %x", mOff, cOff, l, callData[cOff:cOff+l])
|
||||||
|
case CODESIZE, EXTCODESIZE:
|
||||||
|
var code []byte
|
||||||
|
if op == EXTCODESIZE {
|
||||||
|
addr := stack.Pop().Bytes()
|
||||||
|
|
||||||
|
code = statedb.GetCode(addr)
|
||||||
|
} else {
|
||||||
|
code = context.Code
|
||||||
|
}
|
||||||
|
|
||||||
|
l := big.NewInt(int64(len(code)))
|
||||||
|
stack.Push(l)
|
||||||
|
|
||||||
|
self.Printf(" => %d", l)
|
||||||
|
case CODECOPY, EXTCODECOPY:
|
||||||
|
var code []byte
|
||||||
|
if op == EXTCODECOPY {
|
||||||
|
code = statedb.GetCode(stack.Pop().Bytes())
|
||||||
|
} else {
|
||||||
|
code = context.Code
|
||||||
|
}
|
||||||
|
context := NewContext(nil, nil, code, ethutil.Big0, ethutil.Big0)
|
||||||
|
var (
|
||||||
|
mOff = stack.Pop().Uint64()
|
||||||
|
cOff = stack.Pop().Uint64()
|
||||||
|
l = stack.Pop().Uint64()
|
||||||
|
)
|
||||||
|
codeCopy := context.GetCode(cOff, l)
|
||||||
|
|
||||||
|
mem.Set(mOff, l, codeCopy)
|
||||||
|
|
||||||
|
self.Printf(" => [%v, %v, %v] %x", mOff, cOff, l, codeCopy)
|
||||||
|
case GASPRICE:
|
||||||
|
stack.Push(context.Price)
|
||||||
|
|
||||||
|
self.Printf(" => %v", context.Price)
|
||||||
|
|
||||||
|
// 0x40 range
|
||||||
|
case BLOCKHASH:
|
||||||
|
num := stack.Pop()
|
||||||
|
|
||||||
|
n := U256(new(big.Int).Sub(self.env.BlockNumber(), ethutil.Big257))
|
||||||
|
if num.Cmp(n) > 0 && num.Cmp(self.env.BlockNumber()) < 0 {
|
||||||
|
stack.Push(ethutil.BigD(self.env.GetHash(num.Uint64())))
|
||||||
|
} else {
|
||||||
|
stack.Push(ethutil.Big0)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.Printf(" => 0x%x", stack.Peek().Bytes())
|
||||||
|
case COINBASE:
|
||||||
|
coinbase := self.env.Coinbase()
|
||||||
|
|
||||||
|
stack.Push(ethutil.BigD(coinbase))
|
||||||
|
|
||||||
|
self.Printf(" => 0x%x", coinbase)
|
||||||
|
case TIMESTAMP:
|
||||||
|
time := self.env.Time()
|
||||||
|
|
||||||
|
stack.Push(big.NewInt(time))
|
||||||
|
|
||||||
|
self.Printf(" => 0x%x", time)
|
||||||
|
case NUMBER:
|
||||||
|
number := self.env.BlockNumber()
|
||||||
|
|
||||||
|
stack.Push(number)
|
||||||
|
|
||||||
|
self.Printf(" => 0x%x", number.Bytes())
|
||||||
|
case DIFFICULTY:
|
||||||
|
difficulty := self.env.Difficulty()
|
||||||
|
|
||||||
|
stack.Push(difficulty)
|
||||||
|
|
||||||
|
self.Printf(" => 0x%x", difficulty.Bytes())
|
||||||
|
case GASLIMIT:
|
||||||
|
self.Printf(" => %v", self.env.GasLimit())
|
||||||
|
|
||||||
|
stack.Push(self.env.GasLimit())
|
||||||
|
|
||||||
|
// 0x50 range
|
||||||
|
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(ethutil.BigD(byts))
|
||||||
|
pc += a
|
||||||
|
|
||||||
|
step += int(op) - int(PUSH1) + 1
|
||||||
|
|
||||||
|
self.Printf(" => 0x%x", byts)
|
||||||
|
case POP:
|
||||||
|
stack.Pop()
|
||||||
|
case DUP1, DUP2, DUP3, DUP4, DUP5, DUP6, DUP7, DUP8, DUP9, DUP10, DUP11, DUP12, DUP13, DUP14, DUP15, DUP16:
|
||||||
|
n := int(op - DUP1 + 1)
|
||||||
|
stack.Dupn(n)
|
||||||
|
|
||||||
|
self.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)
|
||||||
|
x, y := stack.Swapn(n)
|
||||||
|
|
||||||
|
self.Printf(" => [%d] %x [0] %x", n, x.Bytes(), y.Bytes())
|
||||||
|
case LOG0, LOG1, LOG2, LOG3, LOG4:
|
||||||
|
n := int(op - LOG0)
|
||||||
|
topics := make([][]byte, n)
|
||||||
|
mSize, mStart := stack.Popn()
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
topics[i] = ethutil.LeftPadBytes(stack.Pop().Bytes(), 32)
|
||||||
|
}
|
||||||
|
|
||||||
|
data := mem.Get(mStart.Int64(), mSize.Int64())
|
||||||
|
log := &Log{context.Address(), topics, data}
|
||||||
|
self.env.AddLog(log)
|
||||||
|
|
||||||
|
self.Printf(" => %v", log)
|
||||||
|
case MLOAD:
|
||||||
|
offset := stack.Pop()
|
||||||
|
val := ethutil.BigD(mem.Get(offset.Int64(), 32))
|
||||||
|
stack.Push(val)
|
||||||
|
|
||||||
|
self.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
|
||||||
|
val, mStart := stack.Popn()
|
||||||
|
mem.Set(mStart.Uint64(), 32, ethutil.BigToBytes(val, 256))
|
||||||
|
|
||||||
|
self.Printf(" => 0x%x", val)
|
||||||
|
case MSTORE8:
|
||||||
|
off := stack.Pop()
|
||||||
|
val := stack.Pop()
|
||||||
|
|
||||||
|
mem.store[off.Int64()] = byte(val.Int64() & 0xff)
|
||||||
|
|
||||||
|
self.Printf(" => [%v] 0x%x", off, val)
|
||||||
|
case SLOAD:
|
||||||
|
loc := stack.Pop()
|
||||||
|
val := ethutil.BigD(statedb.GetState(context.Address(), loc.Bytes()))
|
||||||
|
stack.Push(val)
|
||||||
|
|
||||||
|
self.Printf(" {0x%x : 0x%x}", loc.Bytes(), val.Bytes())
|
||||||
|
case SSTORE:
|
||||||
|
val, loc := stack.Popn()
|
||||||
|
statedb.SetState(context.Address(), loc.Bytes(), val)
|
||||||
|
|
||||||
|
msg.AddStorageChange(loc.Bytes())
|
||||||
|
|
||||||
|
self.Printf(" {0x%x : 0x%x}", loc.Bytes(), val.Bytes())
|
||||||
|
case JUMP:
|
||||||
|
jump(pc, stack.Pop())
|
||||||
|
|
||||||
|
continue
|
||||||
|
case JUMPI:
|
||||||
|
cond, pos := stack.Popn()
|
||||||
|
|
||||||
|
if cond.Cmp(ethutil.BigTrue) >= 0 {
|
||||||
|
jump(pc, pos)
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
case JUMPDEST:
|
||||||
|
case PC:
|
||||||
|
stack.Push(big.NewInt(int64(pc)))
|
||||||
|
case MSIZE:
|
||||||
|
stack.Push(big.NewInt(int64(mem.Len())))
|
||||||
|
case GAS:
|
||||||
|
stack.Push(context.Gas)
|
||||||
|
// 0x60 range
|
||||||
|
case CREATE:
|
||||||
|
|
||||||
|
var (
|
||||||
|
value = stack.Pop()
|
||||||
|
size, offset = stack.Popn()
|
||||||
|
input = mem.Get(offset.Int64(), size.Int64())
|
||||||
|
gas = new(big.Int).Set(context.Gas)
|
||||||
|
addr []byte
|
||||||
|
)
|
||||||
|
|
||||||
|
context.UseGas(context.Gas)
|
||||||
|
ret, suberr, ref := self.env.Create(context, nil, input, gas, price, value)
|
||||||
|
if suberr != nil {
|
||||||
|
stack.Push(ethutil.BigFalse)
|
||||||
|
|
||||||
|
self.Printf(" (*) 0x0 %v", suberr)
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// gas < len(ret) * CreateDataGas == NO_CODE
|
||||||
|
dataGas := big.NewInt(int64(len(ret)))
|
||||||
|
dataGas.Mul(dataGas, GasCreateByte)
|
||||||
|
if context.UseGas(dataGas) {
|
||||||
|
ref.SetCode(ret)
|
||||||
|
msg.Output = ret
|
||||||
|
}
|
||||||
|
addr = ref.Address()
|
||||||
|
|
||||||
|
stack.Push(ethutil.BigD(addr))
|
||||||
|
|
||||||
|
self.Printf(" (*) %x", addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug hook
|
||||||
|
if self.Dbg != nil {
|
||||||
|
self.Dbg.SetCode(context.Code)
|
||||||
|
}
|
||||||
|
case CALL, CALLCODE:
|
||||||
|
self.Endl()
|
||||||
|
|
||||||
|
gas := stack.Pop()
|
||||||
|
// Pop gas and value of the stack.
|
||||||
|
value, addr := stack.Popn()
|
||||||
|
// Pop input size and offset
|
||||||
|
inSize, inOffset := stack.Popn()
|
||||||
|
// Pop return size and offset
|
||||||
|
retSize, retOffset := stack.Popn()
|
||||||
|
|
||||||
|
// Get the arguments from the memory
|
||||||
|
args := mem.Get(inOffset.Int64(), inSize.Int64())
|
||||||
|
|
||||||
|
var (
|
||||||
|
ret []byte
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
if op == CALLCODE {
|
||||||
|
ret, err = self.env.CallCode(context, addr.Bytes(), args, gas, price, value)
|
||||||
|
} else {
|
||||||
|
ret, err = self.env.Call(context, addr.Bytes(), args, gas, price, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
stack.Push(ethutil.BigFalse)
|
||||||
|
|
||||||
|
vmlogger.Debugln(err)
|
||||||
|
} else {
|
||||||
|
stack.Push(ethutil.BigTrue)
|
||||||
|
msg.Output = ret
|
||||||
|
|
||||||
|
mem.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||||
|
}
|
||||||
|
self.Printf("resume %x (%v)", context.Address(), context.Gas)
|
||||||
|
|
||||||
|
// Debug hook
|
||||||
|
if self.Dbg != nil {
|
||||||
|
self.Dbg.SetCode(context.Code)
|
||||||
|
}
|
||||||
|
|
||||||
|
case RETURN:
|
||||||
|
size, offset := stack.Popn()
|
||||||
|
ret := mem.Get(offset.Int64(), size.Int64())
|
||||||
|
|
||||||
|
self.Printf(" => [%v, %v] (%d) 0x%x", offset, size, len(ret), ret).Endl()
|
||||||
|
|
||||||
|
return context.Return(ret), nil
|
||||||
|
case SUICIDE:
|
||||||
|
receiver := statedb.GetOrNewStateObject(stack.Pop().Bytes())
|
||||||
|
balance := statedb.GetBalance(context.Address())
|
||||||
|
|
||||||
|
self.Printf(" => (%x) %v", receiver.Address()[:4], balance)
|
||||||
|
|
||||||
|
receiver.AddAmount(balance)
|
||||||
|
statedb.Delete(context.Address())
|
||||||
|
|
||||||
|
fallthrough
|
||||||
|
case STOP: // Stop the context
|
||||||
|
self.Endl()
|
||||||
|
|
||||||
|
return context.Return(nil), nil
|
||||||
|
default:
|
||||||
|
vmlogger.Debugf("(pc) %-3v Invalid opcode %x\n", pc, op)
|
||||||
|
|
||||||
|
panic(fmt.Errorf("Invalid opcode %x", op))
|
||||||
|
}
|
||||||
|
|
||||||
|
pc++
|
||||||
|
|
||||||
|
self.Endl()
|
||||||
|
|
||||||
|
if self.Dbg != nil {
|
||||||
|
for _, instrNo := range self.Dbg.BreakPoints() {
|
||||||
|
if pc == uint64(instrNo) {
|
||||||
|
self.Stepping = true
|
||||||
|
|
||||||
|
if !self.Dbg.BreakHook(prevStep, op, mem, stack, statedb.GetStateObject(context.Address())) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
} else if self.Stepping {
|
||||||
|
if !self.Dbg.StepHook(prevStep, op, mem, stack, statedb.GetStateObject(context.Address())) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Vm) Run(me, caller ContextRef, code []byte, value, gas, price *big.Int, data []byte) (ret []byte, err error) {
|
func (self *Vm) calculateGasAndSize(context *Context, caller ContextRef, op OpCode, statedb *state.StateDB, mem *Memory, stack *Stack) (*big.Int, *big.Int) {
|
||||||
panic("not implemented")
|
gas := new(big.Int)
|
||||||
|
addStepGasUsage := func(amount *big.Int) {
|
||||||
|
if amount.Cmp(ethutil.Big0) >= 0 {
|
||||||
|
gas.Add(gas, amount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addStepGasUsage(GasStep)
|
||||||
|
|
||||||
|
var newMemSize *big.Int = ethutil.Big0
|
||||||
|
var additionalGas *big.Int = new(big.Int)
|
||||||
|
// Stack Check, memory resize & gas phase
|
||||||
|
switch op {
|
||||||
|
// Stack checks only
|
||||||
|
case ISZERO, CALLDATALOAD, POP, JUMP, NOT: // 1
|
||||||
|
stack.require(1)
|
||||||
|
case JUMPI, ADD, SUB, DIV, SDIV, MOD, SMOD, LT, GT, SLT, SGT, EQ, AND, OR, XOR, BYTE, SIGNEXTEND: // 2
|
||||||
|
stack.require(2)
|
||||||
|
case ADDMOD, MULMOD: // 3
|
||||||
|
stack.require(3)
|
||||||
|
case SWAP1, SWAP2, SWAP3, SWAP4, SWAP5, SWAP6, SWAP7, SWAP8, SWAP9, SWAP10, SWAP11, SWAP12, SWAP13, SWAP14, SWAP15, SWAP16:
|
||||||
|
n := int(op - SWAP1 + 2)
|
||||||
|
stack.require(n)
|
||||||
|
case DUP1, DUP2, DUP3, DUP4, DUP5, DUP6, DUP7, DUP8, DUP9, DUP10, DUP11, DUP12, DUP13, DUP14, DUP15, DUP16:
|
||||||
|
n := int(op - DUP1 + 1)
|
||||||
|
stack.require(n)
|
||||||
|
case LOG0, LOG1, LOG2, LOG3, LOG4:
|
||||||
|
n := int(op - LOG0)
|
||||||
|
stack.require(n + 2)
|
||||||
|
|
||||||
|
gas.Set(GasLog)
|
||||||
|
addStepGasUsage(new(big.Int).Mul(big.NewInt(int64(n)), GasLog))
|
||||||
|
|
||||||
|
mSize, mStart := stack.Peekn()
|
||||||
|
addStepGasUsage(mSize)
|
||||||
|
|
||||||
|
newMemSize = calcMemSize(mStart, mSize)
|
||||||
|
case EXP:
|
||||||
|
stack.require(2)
|
||||||
|
|
||||||
|
gas.Set(big.NewInt(int64(len(stack.data[stack.Len()-2].Bytes()) + 1)))
|
||||||
|
// Gas only
|
||||||
|
case STOP:
|
||||||
|
gas.Set(ethutil.Big0)
|
||||||
|
case SUICIDE:
|
||||||
|
stack.require(1)
|
||||||
|
|
||||||
|
gas.Set(ethutil.Big0)
|
||||||
|
case SLOAD:
|
||||||
|
stack.require(1)
|
||||||
|
|
||||||
|
gas.Set(GasSLoad)
|
||||||
|
// Memory resize & Gas
|
||||||
|
case SSTORE:
|
||||||
|
stack.require(2)
|
||||||
|
|
||||||
|
var mult *big.Int
|
||||||
|
y, x := stack.Peekn()
|
||||||
|
val := statedb.GetState(context.Address(), x.Bytes())
|
||||||
|
if len(val) == 0 && len(y.Bytes()) > 0 {
|
||||||
|
// 0 => non 0
|
||||||
|
mult = ethutil.Big3
|
||||||
|
} else if len(val) > 0 && len(y.Bytes()) == 0 {
|
||||||
|
statedb.Refund(caller.Address(), GasSStoreRefund)
|
||||||
|
|
||||||
|
mult = ethutil.Big0
|
||||||
|
} else {
|
||||||
|
// non 0 => non 0 (or 0 => 0)
|
||||||
|
mult = ethutil.Big1
|
||||||
|
}
|
||||||
|
gas.Set(new(big.Int).Mul(mult, GasSStore))
|
||||||
|
case BALANCE:
|
||||||
|
stack.require(1)
|
||||||
|
gas.Set(GasBalance)
|
||||||
|
case MSTORE:
|
||||||
|
stack.require(2)
|
||||||
|
newMemSize = calcMemSize(stack.Peek(), u256(32))
|
||||||
|
case MLOAD:
|
||||||
|
stack.require(1)
|
||||||
|
|
||||||
|
newMemSize = calcMemSize(stack.Peek(), u256(32))
|
||||||
|
case MSTORE8:
|
||||||
|
stack.require(2)
|
||||||
|
newMemSize = calcMemSize(stack.Peek(), u256(1))
|
||||||
|
case RETURN:
|
||||||
|
stack.require(2)
|
||||||
|
|
||||||
|
newMemSize = calcMemSize(stack.Peek(), stack.data[stack.Len()-2])
|
||||||
|
case SHA3:
|
||||||
|
stack.require(2)
|
||||||
|
gas.Set(GasSha)
|
||||||
|
newMemSize = calcMemSize(stack.Peek(), stack.data[stack.Len()-2])
|
||||||
|
additionalGas.Set(stack.data[stack.Len()-2])
|
||||||
|
case CALLDATACOPY:
|
||||||
|
stack.require(2)
|
||||||
|
|
||||||
|
newMemSize = calcMemSize(stack.Peek(), stack.data[stack.Len()-3])
|
||||||
|
additionalGas.Set(stack.data[stack.Len()-3])
|
||||||
|
case CODECOPY:
|
||||||
|
stack.require(3)
|
||||||
|
|
||||||
|
newMemSize = calcMemSize(stack.Peek(), stack.data[stack.Len()-3])
|
||||||
|
additionalGas.Set(stack.data[stack.Len()-3])
|
||||||
|
case EXTCODECOPY:
|
||||||
|
stack.require(4)
|
||||||
|
|
||||||
|
newMemSize = calcMemSize(stack.data[stack.Len()-2], stack.data[stack.Len()-4])
|
||||||
|
additionalGas.Set(stack.data[stack.Len()-4])
|
||||||
|
case CALL, CALLCODE:
|
||||||
|
stack.require(7)
|
||||||
|
gas.Set(GasCall)
|
||||||
|
addStepGasUsage(stack.data[stack.Len()-1])
|
||||||
|
|
||||||
|
x := calcMemSize(stack.data[stack.Len()-6], stack.data[stack.Len()-7])
|
||||||
|
y := calcMemSize(stack.data[stack.Len()-4], stack.data[stack.Len()-5])
|
||||||
|
|
||||||
|
newMemSize = ethutil.BigMax(x, y)
|
||||||
|
case CREATE:
|
||||||
|
stack.require(3)
|
||||||
|
gas.Set(GasCreate)
|
||||||
|
|
||||||
|
newMemSize = calcMemSize(stack.data[stack.Len()-2], stack.data[stack.Len()-3])
|
||||||
|
}
|
||||||
|
|
||||||
|
switch op {
|
||||||
|
case CALLDATACOPY, CODECOPY, EXTCODECOPY:
|
||||||
|
additionalGas.Add(additionalGas, u256(31))
|
||||||
|
additionalGas.Div(additionalGas, u256(32))
|
||||||
|
addStepGasUsage(additionalGas)
|
||||||
|
case SHA3:
|
||||||
|
additionalGas.Add(additionalGas, u256(31))
|
||||||
|
additionalGas.Div(additionalGas, u256(32))
|
||||||
|
additionalGas.Mul(additionalGas, GasSha3Byte)
|
||||||
|
addStepGasUsage(additionalGas)
|
||||||
|
}
|
||||||
|
|
||||||
|
if newMemSize.Cmp(ethutil.Big0) > 0 {
|
||||||
|
newMemSize.Add(newMemSize, u256(31))
|
||||||
|
newMemSize.Div(newMemSize, u256(32))
|
||||||
|
newMemSize.Mul(newMemSize, u256(32))
|
||||||
|
|
||||||
|
if newMemSize.Cmp(u256(int64(mem.Len()))) > 0 {
|
||||||
|
memGasUsage := new(big.Int).Sub(newMemSize, u256(int64(mem.Len())))
|
||||||
|
memGasUsage.Mul(GasMemory, memGasUsage)
|
||||||
|
memGasUsage.Div(memGasUsage, u256(32))
|
||||||
|
|
||||||
|
addStepGasUsage(memGasUsage)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return newMemSize, gas
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Vm) RunPrecompiled(p *PrecompiledAccount, callData []byte, context *Context) (ret []byte, err error) {
|
||||||
|
gas := p.Gas(len(callData))
|
||||||
|
if context.UseGas(gas) {
|
||||||
|
ret = p.Call(callData)
|
||||||
|
self.Printf("NATIVE_FUNC => %x", ret)
|
||||||
|
self.Endl()
|
||||||
|
|
||||||
|
return context.Return(ret), nil
|
||||||
|
} else {
|
||||||
|
self.Printf("NATIVE_FUNC => failed").Endl()
|
||||||
|
|
||||||
|
tmp := new(big.Int).Set(context.Gas)
|
||||||
|
|
||||||
|
panic(OOG(gas, tmp).Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Vm) Printf(format string, v ...interface{}) VirtualMachine {
|
||||||
|
if self.logTy == LogTyPretty {
|
||||||
|
self.logStr += fmt.Sprintf(format, v...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Vm) Endl() VirtualMachine {
|
||||||
|
if self.logTy == LogTyPretty {
|
||||||
|
vmlogger.Debugln(self.logStr)
|
||||||
|
self.logStr = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Vm) Env() Environment {
|
func (self *Vm) Env() Environment {
|
||||||
return self.env
|
return self.env
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Vm) Depth() int {
|
|
||||||
return self.depth
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *Vm) Printf(format string, v ...interface{}) VirtualMachine { return self }
|
|
||||||
func (self *Vm) Endl() VirtualMachine { return self }
|
|
||||||
|
961
vm/vm_debug.go
961
vm/vm_debug.go
@ -1,961 +0,0 @@
|
|||||||
package vm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"math/big"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
|
||||||
"github.com/ethereum/go-ethereum/ethutil"
|
|
||||||
"github.com/ethereum/go-ethereum/state"
|
|
||||||
)
|
|
||||||
|
|
||||||
type DebugVm struct {
|
|
||||||
env Environment
|
|
||||||
|
|
||||||
logTy byte
|
|
||||||
logStr string
|
|
||||||
|
|
||||||
err error
|
|
||||||
|
|
||||||
// Debugging
|
|
||||||
Dbg Debugger
|
|
||||||
|
|
||||||
BreakPoints []int64
|
|
||||||
Stepping bool
|
|
||||||
Fn string
|
|
||||||
|
|
||||||
Recoverable bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewDebugVm(env Environment) *DebugVm {
|
|
||||||
lt := LogTyPretty
|
|
||||||
if ethutil.Config.Diff {
|
|
||||||
lt = LogTyDiff
|
|
||||||
}
|
|
||||||
|
|
||||||
return &DebugVm{env: env, logTy: lt, Recoverable: true}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *DebugVm) Run(me, caller ContextRef, code []byte, value, gas, price *big.Int, callData []byte) (ret []byte, err error) {
|
|
||||||
self.env.SetDepth(self.env.Depth() + 1)
|
|
||||||
|
|
||||||
msg := self.env.State().Manifest().AddMessage(&state.Message{
|
|
||||||
To: me.Address(), From: caller.Address(),
|
|
||||||
Input: callData,
|
|
||||||
Origin: self.env.Origin(),
|
|
||||||
Timestamp: self.env.Time(), Coinbase: self.env.Coinbase(), Number: self.env.BlockNumber(),
|
|
||||||
Value: value,
|
|
||||||
})
|
|
||||||
context := NewContext(caller, me, code, gas, price)
|
|
||||||
|
|
||||||
vmlogger.Debugf("(%d) (%x) %x (code=%d) gas: %v (d) %x\n", self.env.Depth(), caller.Address()[:4], context.Address(), len(code), context.Gas, callData)
|
|
||||||
|
|
||||||
if self.Recoverable {
|
|
||||||
// Recover from any require exception
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
self.Printf(" %v", r).Endl()
|
|
||||||
|
|
||||||
context.UseGas(context.Gas)
|
|
||||||
|
|
||||||
ret = context.Return(nil)
|
|
||||||
|
|
||||||
err = fmt.Errorf("%v", r)
|
|
||||||
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
if p := Precompiled[string(me.Address())]; p != nil {
|
|
||||||
return self.RunPrecompiled(p, callData, context)
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
op OpCode
|
|
||||||
|
|
||||||
destinations = analyseJumpDests(context.Code)
|
|
||||||
mem = NewMemory()
|
|
||||||
stack = NewStack()
|
|
||||||
pc uint64 = 0
|
|
||||||
step = 0
|
|
||||||
prevStep = 0
|
|
||||||
statedb = self.env.State()
|
|
||||||
|
|
||||||
jump = func(from uint64, to *big.Int) {
|
|
||||||
p := to.Uint64()
|
|
||||||
|
|
||||||
nop := context.GetOp(p)
|
|
||||||
if !destinations.Has(p) {
|
|
||||||
panic(fmt.Sprintf("invalid jump destination (%v) %v", nop, p))
|
|
||||||
}
|
|
||||||
|
|
||||||
self.Printf(" ~> %v", to)
|
|
||||||
pc = to.Uint64()
|
|
||||||
|
|
||||||
self.Endl()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// Don't bother with the execution if there's no code.
|
|
||||||
if len(code) == 0 {
|
|
||||||
return context.Return(nil), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
for {
|
|
||||||
prevStep = step
|
|
||||||
// The base for all big integer arithmetic
|
|
||||||
base := new(big.Int)
|
|
||||||
|
|
||||||
step++
|
|
||||||
// Get the memory location of pc
|
|
||||||
op = context.GetOp(pc)
|
|
||||||
|
|
||||||
self.Printf("(pc) %-3d -o- %-14s (m) %-4d (s) %-4d ", pc, op.String(), mem.Len(), stack.Len())
|
|
||||||
|
|
||||||
newMemSize, gas := self.calculateGasAndSize(context, caller, op, statedb, mem, stack)
|
|
||||||
|
|
||||||
self.Printf("(g) %-3v (%v)", gas, context.Gas)
|
|
||||||
|
|
||||||
if !context.UseGas(gas) {
|
|
||||||
self.Endl()
|
|
||||||
|
|
||||||
tmp := new(big.Int).Set(context.Gas)
|
|
||||||
|
|
||||||
context.UseGas(context.Gas)
|
|
||||||
|
|
||||||
return context.Return(nil), OOG(gas, tmp)
|
|
||||||
}
|
|
||||||
|
|
||||||
mem.Resize(newMemSize.Uint64())
|
|
||||||
|
|
||||||
switch op {
|
|
||||||
// 0x20 range
|
|
||||||
case ADD:
|
|
||||||
x, y := stack.Popn()
|
|
||||||
self.Printf(" %v + %v", y, x)
|
|
||||||
|
|
||||||
base.Add(y, x)
|
|
||||||
|
|
||||||
U256(base)
|
|
||||||
|
|
||||||
self.Printf(" = %v", base)
|
|
||||||
// Pop result back on the stack
|
|
||||||
stack.Push(base)
|
|
||||||
case SUB:
|
|
||||||
x, y := stack.Popn()
|
|
||||||
self.Printf(" %v - %v", y, x)
|
|
||||||
|
|
||||||
base.Sub(y, x)
|
|
||||||
|
|
||||||
U256(base)
|
|
||||||
|
|
||||||
self.Printf(" = %v", base)
|
|
||||||
// Pop result back on the stack
|
|
||||||
stack.Push(base)
|
|
||||||
case MUL:
|
|
||||||
x, y := stack.Popn()
|
|
||||||
self.Printf(" %v * %v", y, x)
|
|
||||||
|
|
||||||
base.Mul(y, x)
|
|
||||||
|
|
||||||
U256(base)
|
|
||||||
|
|
||||||
self.Printf(" = %v", base)
|
|
||||||
// Pop result back on the stack
|
|
||||||
stack.Push(base)
|
|
||||||
case DIV:
|
|
||||||
x, y := stack.Pop(), stack.Pop()
|
|
||||||
self.Printf(" %v / %v", x, y)
|
|
||||||
|
|
||||||
if y.Cmp(ethutil.Big0) != 0 {
|
|
||||||
base.Div(x, y)
|
|
||||||
}
|
|
||||||
|
|
||||||
U256(base)
|
|
||||||
|
|
||||||
self.Printf(" = %v", base)
|
|
||||||
// Pop result back on the stack
|
|
||||||
stack.Push(base)
|
|
||||||
case SDIV:
|
|
||||||
x, y := S256(stack.Pop()), S256(stack.Pop())
|
|
||||||
|
|
||||||
self.Printf(" %v / %v", x, y)
|
|
||||||
|
|
||||||
if y.Cmp(ethutil.Big0) == 0 {
|
|
||||||
base.Set(ethutil.Big0)
|
|
||||||
} else {
|
|
||||||
n := new(big.Int)
|
|
||||||
if new(big.Int).Mul(x, y).Cmp(ethutil.Big0) < 0 {
|
|
||||||
n.SetInt64(-1)
|
|
||||||
} else {
|
|
||||||
n.SetInt64(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
base.Div(x.Abs(x), y.Abs(y)).Mul(base, n)
|
|
||||||
|
|
||||||
U256(base)
|
|
||||||
}
|
|
||||||
|
|
||||||
self.Printf(" = %v", base)
|
|
||||||
stack.Push(base)
|
|
||||||
case MOD:
|
|
||||||
x, y := stack.Pop(), stack.Pop()
|
|
||||||
|
|
||||||
self.Printf(" %v %% %v", x, y)
|
|
||||||
|
|
||||||
if y.Cmp(ethutil.Big0) == 0 {
|
|
||||||
base.Set(ethutil.Big0)
|
|
||||||
} else {
|
|
||||||
base.Mod(x, y)
|
|
||||||
}
|
|
||||||
|
|
||||||
U256(base)
|
|
||||||
|
|
||||||
self.Printf(" = %v", base)
|
|
||||||
stack.Push(base)
|
|
||||||
case SMOD:
|
|
||||||
x, y := S256(stack.Pop()), S256(stack.Pop())
|
|
||||||
|
|
||||||
self.Printf(" %v %% %v", x, y)
|
|
||||||
|
|
||||||
if y.Cmp(ethutil.Big0) == 0 {
|
|
||||||
base.Set(ethutil.Big0)
|
|
||||||
} else {
|
|
||||||
n := new(big.Int)
|
|
||||||
if x.Cmp(ethutil.Big0) < 0 {
|
|
||||||
n.SetInt64(-1)
|
|
||||||
} else {
|
|
||||||
n.SetInt64(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
base.Mod(x.Abs(x), y.Abs(y)).Mul(base, n)
|
|
||||||
|
|
||||||
U256(base)
|
|
||||||
}
|
|
||||||
|
|
||||||
self.Printf(" = %v", base)
|
|
||||||
stack.Push(base)
|
|
||||||
|
|
||||||
case EXP:
|
|
||||||
x, y := stack.Popn()
|
|
||||||
|
|
||||||
self.Printf(" %v ** %v", y, x)
|
|
||||||
|
|
||||||
base.Exp(y, x, Pow256)
|
|
||||||
|
|
||||||
U256(base)
|
|
||||||
|
|
||||||
self.Printf(" = %v", base)
|
|
||||||
|
|
||||||
stack.Push(base)
|
|
||||||
case SIGNEXTEND:
|
|
||||||
back := stack.Pop().Uint64()
|
|
||||||
if back < 31 {
|
|
||||||
bit := uint(back*8 + 7)
|
|
||||||
num := stack.Pop()
|
|
||||||
mask := new(big.Int).Lsh(ethutil.Big1, bit)
|
|
||||||
mask.Sub(mask, ethutil.Big1)
|
|
||||||
if ethutil.BitTest(num, int(bit)) {
|
|
||||||
num.Or(num, mask.Not(mask))
|
|
||||||
} else {
|
|
||||||
num.And(num, mask)
|
|
||||||
}
|
|
||||||
|
|
||||||
num = U256(num)
|
|
||||||
|
|
||||||
self.Printf(" = %v", num)
|
|
||||||
|
|
||||||
stack.Push(num)
|
|
||||||
}
|
|
||||||
case NOT:
|
|
||||||
base.Sub(Pow256, stack.Pop()).Sub(base, ethutil.Big1)
|
|
||||||
|
|
||||||
// Not needed
|
|
||||||
//base = U256(base)
|
|
||||||
|
|
||||||
stack.Push(base)
|
|
||||||
case LT:
|
|
||||||
x, y := stack.Popn()
|
|
||||||
self.Printf(" %v < %v", y, x)
|
|
||||||
// x < y
|
|
||||||
if y.Cmp(x) < 0 {
|
|
||||||
stack.Push(ethutil.BigTrue)
|
|
||||||
} else {
|
|
||||||
stack.Push(ethutil.BigFalse)
|
|
||||||
}
|
|
||||||
case GT:
|
|
||||||
x, y := stack.Popn()
|
|
||||||
self.Printf(" %v > %v", y, x)
|
|
||||||
|
|
||||||
// x > y
|
|
||||||
if y.Cmp(x) > 0 {
|
|
||||||
stack.Push(ethutil.BigTrue)
|
|
||||||
} else {
|
|
||||||
stack.Push(ethutil.BigFalse)
|
|
||||||
}
|
|
||||||
|
|
||||||
case SLT:
|
|
||||||
y, x := S256(stack.Pop()), S256(stack.Pop())
|
|
||||||
self.Printf(" %v < %v", y, x)
|
|
||||||
// x < y
|
|
||||||
if y.Cmp(S256(x)) < 0 {
|
|
||||||
stack.Push(ethutil.BigTrue)
|
|
||||||
} else {
|
|
||||||
stack.Push(ethutil.BigFalse)
|
|
||||||
}
|
|
||||||
case SGT:
|
|
||||||
y, x := S256(stack.Pop()), S256(stack.Pop())
|
|
||||||
self.Printf(" %v > %v", y, x)
|
|
||||||
|
|
||||||
// x > y
|
|
||||||
if y.Cmp(x) > 0 {
|
|
||||||
stack.Push(ethutil.BigTrue)
|
|
||||||
} else {
|
|
||||||
stack.Push(ethutil.BigFalse)
|
|
||||||
}
|
|
||||||
|
|
||||||
case EQ:
|
|
||||||
x, y := stack.Popn()
|
|
||||||
self.Printf(" %v == %v", y, x)
|
|
||||||
|
|
||||||
// x == y
|
|
||||||
if x.Cmp(y) == 0 {
|
|
||||||
stack.Push(ethutil.BigTrue)
|
|
||||||
} else {
|
|
||||||
stack.Push(ethutil.BigFalse)
|
|
||||||
}
|
|
||||||
case ISZERO:
|
|
||||||
x := stack.Pop()
|
|
||||||
if x.Cmp(ethutil.BigFalse) > 0 {
|
|
||||||
stack.Push(ethutil.BigFalse)
|
|
||||||
} else {
|
|
||||||
stack.Push(ethutil.BigTrue)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 0x10 range
|
|
||||||
case AND:
|
|
||||||
x, y := stack.Popn()
|
|
||||||
self.Printf(" %v & %v", y, x)
|
|
||||||
|
|
||||||
stack.Push(base.And(y, x))
|
|
||||||
case OR:
|
|
||||||
x, y := stack.Popn()
|
|
||||||
self.Printf(" %v | %v", y, x)
|
|
||||||
|
|
||||||
stack.Push(base.Or(y, x))
|
|
||||||
case XOR:
|
|
||||||
x, y := stack.Popn()
|
|
||||||
self.Printf(" %v ^ %v", y, x)
|
|
||||||
|
|
||||||
stack.Push(base.Xor(y, x))
|
|
||||||
case BYTE:
|
|
||||||
val, th := stack.Popn()
|
|
||||||
|
|
||||||
if th.Cmp(big.NewInt(32)) < 0 {
|
|
||||||
byt := big.NewInt(int64(ethutil.LeftPadBytes(val.Bytes(), 32)[th.Int64()]))
|
|
||||||
|
|
||||||
base.Set(byt)
|
|
||||||
} else {
|
|
||||||
base.Set(ethutil.BigFalse)
|
|
||||||
}
|
|
||||||
|
|
||||||
self.Printf(" => 0x%x", base.Bytes())
|
|
||||||
|
|
||||||
stack.Push(base)
|
|
||||||
case ADDMOD:
|
|
||||||
|
|
||||||
x := stack.Pop()
|
|
||||||
y := stack.Pop()
|
|
||||||
z := stack.Pop()
|
|
||||||
|
|
||||||
add := new(big.Int).Add(x, y)
|
|
||||||
if len(z.Bytes()) > 0 { // NOT 0x0
|
|
||||||
base.Mod(add, z)
|
|
||||||
|
|
||||||
U256(base)
|
|
||||||
}
|
|
||||||
|
|
||||||
self.Printf(" %v + %v %% %v = %v", x, y, z, base)
|
|
||||||
|
|
||||||
stack.Push(base)
|
|
||||||
case MULMOD:
|
|
||||||
|
|
||||||
x := stack.Pop()
|
|
||||||
y := stack.Pop()
|
|
||||||
z := stack.Pop()
|
|
||||||
|
|
||||||
mul := new(big.Int).Mul(x, y)
|
|
||||||
if len(z.Bytes()) > 0 { // NOT 0x0
|
|
||||||
base.Mod(mul, z)
|
|
||||||
|
|
||||||
U256(base)
|
|
||||||
}
|
|
||||||
|
|
||||||
self.Printf(" %v + %v %% %v = %v", x, y, z, base)
|
|
||||||
|
|
||||||
stack.Push(base)
|
|
||||||
|
|
||||||
// 0x20 range
|
|
||||||
case SHA3:
|
|
||||||
size, offset := stack.Popn()
|
|
||||||
data := crypto.Sha3(mem.Get(offset.Int64(), size.Int64()))
|
|
||||||
|
|
||||||
stack.Push(ethutil.BigD(data))
|
|
||||||
|
|
||||||
self.Printf(" => %x", data)
|
|
||||||
// 0x30 range
|
|
||||||
case ADDRESS:
|
|
||||||
stack.Push(ethutil.BigD(context.Address()))
|
|
||||||
|
|
||||||
self.Printf(" => %x", context.Address())
|
|
||||||
case BALANCE:
|
|
||||||
|
|
||||||
addr := stack.Pop().Bytes()
|
|
||||||
balance := statedb.GetBalance(addr)
|
|
||||||
|
|
||||||
stack.Push(balance)
|
|
||||||
|
|
||||||
self.Printf(" => %v (%x)", balance, addr)
|
|
||||||
case ORIGIN:
|
|
||||||
origin := self.env.Origin()
|
|
||||||
|
|
||||||
stack.Push(ethutil.BigD(origin))
|
|
||||||
|
|
||||||
self.Printf(" => %x", origin)
|
|
||||||
case CALLER:
|
|
||||||
caller := context.caller.Address()
|
|
||||||
stack.Push(ethutil.BigD(caller))
|
|
||||||
|
|
||||||
self.Printf(" => %x", caller)
|
|
||||||
case CALLVALUE:
|
|
||||||
stack.Push(value)
|
|
||||||
|
|
||||||
self.Printf(" => %v", value)
|
|
||||||
case CALLDATALOAD:
|
|
||||||
var (
|
|
||||||
offset = stack.Pop()
|
|
||||||
data = make([]byte, 32)
|
|
||||||
lenData = big.NewInt(int64(len(callData)))
|
|
||||||
)
|
|
||||||
|
|
||||||
if lenData.Cmp(offset) >= 0 {
|
|
||||||
length := new(big.Int).Add(offset, ethutil.Big32)
|
|
||||||
length = ethutil.BigMin(length, lenData)
|
|
||||||
|
|
||||||
copy(data, callData[offset.Int64():length.Int64()])
|
|
||||||
}
|
|
||||||
|
|
||||||
self.Printf(" => 0x%x", data)
|
|
||||||
|
|
||||||
stack.Push(ethutil.BigD(data))
|
|
||||||
case CALLDATASIZE:
|
|
||||||
l := int64(len(callData))
|
|
||||||
stack.Push(big.NewInt(l))
|
|
||||||
|
|
||||||
self.Printf(" => %d", l)
|
|
||||||
case CALLDATACOPY:
|
|
||||||
var (
|
|
||||||
size = uint64(len(callData))
|
|
||||||
mOff = stack.Pop().Uint64()
|
|
||||||
cOff = stack.Pop().Uint64()
|
|
||||||
l = stack.Pop().Uint64()
|
|
||||||
)
|
|
||||||
|
|
||||||
if cOff > size {
|
|
||||||
cOff = 0
|
|
||||||
l = 0
|
|
||||||
} else if cOff+l > size {
|
|
||||||
l = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
code := callData[cOff : cOff+l]
|
|
||||||
|
|
||||||
mem.Set(mOff, l, code)
|
|
||||||
|
|
||||||
self.Printf(" => [%v, %v, %v] %x", mOff, cOff, l, callData[cOff:cOff+l])
|
|
||||||
case CODESIZE, EXTCODESIZE:
|
|
||||||
var code []byte
|
|
||||||
if op == EXTCODESIZE {
|
|
||||||
addr := stack.Pop().Bytes()
|
|
||||||
|
|
||||||
code = statedb.GetCode(addr)
|
|
||||||
} else {
|
|
||||||
code = context.Code
|
|
||||||
}
|
|
||||||
|
|
||||||
l := big.NewInt(int64(len(code)))
|
|
||||||
stack.Push(l)
|
|
||||||
|
|
||||||
self.Printf(" => %d", l)
|
|
||||||
case CODECOPY, EXTCODECOPY:
|
|
||||||
var code []byte
|
|
||||||
if op == EXTCODECOPY {
|
|
||||||
code = statedb.GetCode(stack.Pop().Bytes())
|
|
||||||
} else {
|
|
||||||
code = context.Code
|
|
||||||
}
|
|
||||||
context := NewContext(nil, nil, code, ethutil.Big0, ethutil.Big0)
|
|
||||||
var (
|
|
||||||
mOff = stack.Pop().Uint64()
|
|
||||||
cOff = stack.Pop().Uint64()
|
|
||||||
l = stack.Pop().Uint64()
|
|
||||||
)
|
|
||||||
codeCopy := context.GetCode(cOff, l)
|
|
||||||
|
|
||||||
mem.Set(mOff, l, codeCopy)
|
|
||||||
|
|
||||||
self.Printf(" => [%v, %v, %v] %x", mOff, cOff, l, codeCopy)
|
|
||||||
case GASPRICE:
|
|
||||||
stack.Push(context.Price)
|
|
||||||
|
|
||||||
self.Printf(" => %v", context.Price)
|
|
||||||
|
|
||||||
// 0x40 range
|
|
||||||
case BLOCKHASH:
|
|
||||||
num := stack.Pop()
|
|
||||||
|
|
||||||
n := U256(new(big.Int).Sub(self.env.BlockNumber(), ethutil.Big257))
|
|
||||||
if num.Cmp(n) > 0 && num.Cmp(self.env.BlockNumber()) < 0 {
|
|
||||||
stack.Push(ethutil.BigD(self.env.GetHash(num.Uint64())))
|
|
||||||
} else {
|
|
||||||
stack.Push(ethutil.Big0)
|
|
||||||
}
|
|
||||||
|
|
||||||
self.Printf(" => 0x%x", stack.Peek().Bytes())
|
|
||||||
case COINBASE:
|
|
||||||
coinbase := self.env.Coinbase()
|
|
||||||
|
|
||||||
stack.Push(ethutil.BigD(coinbase))
|
|
||||||
|
|
||||||
self.Printf(" => 0x%x", coinbase)
|
|
||||||
case TIMESTAMP:
|
|
||||||
time := self.env.Time()
|
|
||||||
|
|
||||||
stack.Push(big.NewInt(time))
|
|
||||||
|
|
||||||
self.Printf(" => 0x%x", time)
|
|
||||||
case NUMBER:
|
|
||||||
number := self.env.BlockNumber()
|
|
||||||
|
|
||||||
stack.Push(number)
|
|
||||||
|
|
||||||
self.Printf(" => 0x%x", number.Bytes())
|
|
||||||
case DIFFICULTY:
|
|
||||||
difficulty := self.env.Difficulty()
|
|
||||||
|
|
||||||
stack.Push(difficulty)
|
|
||||||
|
|
||||||
self.Printf(" => 0x%x", difficulty.Bytes())
|
|
||||||
case GASLIMIT:
|
|
||||||
self.Printf(" => %v", self.env.GasLimit())
|
|
||||||
|
|
||||||
stack.Push(self.env.GasLimit())
|
|
||||||
|
|
||||||
// 0x50 range
|
|
||||||
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(ethutil.BigD(byts))
|
|
||||||
pc += a
|
|
||||||
|
|
||||||
step += int(op) - int(PUSH1) + 1
|
|
||||||
|
|
||||||
self.Printf(" => 0x%x", byts)
|
|
||||||
case POP:
|
|
||||||
stack.Pop()
|
|
||||||
case DUP1, DUP2, DUP3, DUP4, DUP5, DUP6, DUP7, DUP8, DUP9, DUP10, DUP11, DUP12, DUP13, DUP14, DUP15, DUP16:
|
|
||||||
n := int(op - DUP1 + 1)
|
|
||||||
stack.Dupn(n)
|
|
||||||
|
|
||||||
self.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)
|
|
||||||
x, y := stack.Swapn(n)
|
|
||||||
|
|
||||||
self.Printf(" => [%d] %x [0] %x", n, x.Bytes(), y.Bytes())
|
|
||||||
case LOG0, LOG1, LOG2, LOG3, LOG4:
|
|
||||||
n := int(op - LOG0)
|
|
||||||
topics := make([][]byte, n)
|
|
||||||
mSize, mStart := stack.Popn()
|
|
||||||
for i := 0; i < n; i++ {
|
|
||||||
topics[i] = ethutil.LeftPadBytes(stack.Pop().Bytes(), 32)
|
|
||||||
}
|
|
||||||
|
|
||||||
data := mem.Get(mStart.Int64(), mSize.Int64())
|
|
||||||
log := &Log{context.Address(), topics, data}
|
|
||||||
self.env.AddLog(log)
|
|
||||||
|
|
||||||
self.Printf(" => %v", log)
|
|
||||||
case MLOAD:
|
|
||||||
offset := stack.Pop()
|
|
||||||
val := ethutil.BigD(mem.Get(offset.Int64(), 32))
|
|
||||||
stack.Push(val)
|
|
||||||
|
|
||||||
self.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
|
|
||||||
val, mStart := stack.Popn()
|
|
||||||
mem.Set(mStart.Uint64(), 32, ethutil.BigToBytes(val, 256))
|
|
||||||
|
|
||||||
self.Printf(" => 0x%x", val)
|
|
||||||
case MSTORE8:
|
|
||||||
off := stack.Pop()
|
|
||||||
val := stack.Pop()
|
|
||||||
|
|
||||||
mem.store[off.Int64()] = byte(val.Int64() & 0xff)
|
|
||||||
|
|
||||||
self.Printf(" => [%v] 0x%x", off, val)
|
|
||||||
case SLOAD:
|
|
||||||
loc := stack.Pop()
|
|
||||||
val := ethutil.BigD(statedb.GetState(context.Address(), loc.Bytes()))
|
|
||||||
stack.Push(val)
|
|
||||||
|
|
||||||
self.Printf(" {0x%x : 0x%x}", loc.Bytes(), val.Bytes())
|
|
||||||
case SSTORE:
|
|
||||||
val, loc := stack.Popn()
|
|
||||||
statedb.SetState(context.Address(), loc.Bytes(), val)
|
|
||||||
|
|
||||||
msg.AddStorageChange(loc.Bytes())
|
|
||||||
|
|
||||||
self.Printf(" {0x%x : 0x%x}", loc.Bytes(), val.Bytes())
|
|
||||||
case JUMP:
|
|
||||||
jump(pc, stack.Pop())
|
|
||||||
|
|
||||||
continue
|
|
||||||
case JUMPI:
|
|
||||||
cond, pos := stack.Popn()
|
|
||||||
|
|
||||||
if cond.Cmp(ethutil.BigTrue) >= 0 {
|
|
||||||
jump(pc, pos)
|
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
case JUMPDEST:
|
|
||||||
case PC:
|
|
||||||
stack.Push(big.NewInt(int64(pc)))
|
|
||||||
case MSIZE:
|
|
||||||
stack.Push(big.NewInt(int64(mem.Len())))
|
|
||||||
case GAS:
|
|
||||||
stack.Push(context.Gas)
|
|
||||||
// 0x60 range
|
|
||||||
case CREATE:
|
|
||||||
|
|
||||||
var (
|
|
||||||
value = stack.Pop()
|
|
||||||
size, offset = stack.Popn()
|
|
||||||
input = mem.Get(offset.Int64(), size.Int64())
|
|
||||||
gas = new(big.Int).Set(context.Gas)
|
|
||||||
addr []byte
|
|
||||||
)
|
|
||||||
|
|
||||||
context.UseGas(context.Gas)
|
|
||||||
ret, suberr, ref := self.env.Create(context, nil, input, gas, price, value)
|
|
||||||
if suberr != nil {
|
|
||||||
stack.Push(ethutil.BigFalse)
|
|
||||||
|
|
||||||
self.Printf(" (*) 0x0 %v", suberr)
|
|
||||||
} else {
|
|
||||||
|
|
||||||
// gas < len(ret) * CreateDataGas == NO_CODE
|
|
||||||
dataGas := big.NewInt(int64(len(ret)))
|
|
||||||
dataGas.Mul(dataGas, GasCreateByte)
|
|
||||||
if context.UseGas(dataGas) {
|
|
||||||
ref.SetCode(ret)
|
|
||||||
msg.Output = ret
|
|
||||||
}
|
|
||||||
addr = ref.Address()
|
|
||||||
|
|
||||||
stack.Push(ethutil.BigD(addr))
|
|
||||||
|
|
||||||
self.Printf(" (*) %x", addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Debug hook
|
|
||||||
if self.Dbg != nil {
|
|
||||||
self.Dbg.SetCode(context.Code)
|
|
||||||
}
|
|
||||||
case CALL, CALLCODE:
|
|
||||||
self.Endl()
|
|
||||||
|
|
||||||
gas := stack.Pop()
|
|
||||||
// Pop gas and value of the stack.
|
|
||||||
value, addr := stack.Popn()
|
|
||||||
// Pop input size and offset
|
|
||||||
inSize, inOffset := stack.Popn()
|
|
||||||
// Pop return size and offset
|
|
||||||
retSize, retOffset := stack.Popn()
|
|
||||||
|
|
||||||
// Get the arguments from the memory
|
|
||||||
args := mem.Get(inOffset.Int64(), inSize.Int64())
|
|
||||||
|
|
||||||
var (
|
|
||||||
ret []byte
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
if op == CALLCODE {
|
|
||||||
ret, err = self.env.CallCode(context, addr.Bytes(), args, gas, price, value)
|
|
||||||
} else {
|
|
||||||
ret, err = self.env.Call(context, addr.Bytes(), args, gas, price, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
stack.Push(ethutil.BigFalse)
|
|
||||||
|
|
||||||
vmlogger.Debugln(err)
|
|
||||||
} else {
|
|
||||||
stack.Push(ethutil.BigTrue)
|
|
||||||
msg.Output = ret
|
|
||||||
|
|
||||||
mem.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
|
||||||
}
|
|
||||||
self.Printf("resume %x (%v)", context.Address(), context.Gas)
|
|
||||||
|
|
||||||
// Debug hook
|
|
||||||
if self.Dbg != nil {
|
|
||||||
self.Dbg.SetCode(context.Code)
|
|
||||||
}
|
|
||||||
|
|
||||||
case RETURN:
|
|
||||||
size, offset := stack.Popn()
|
|
||||||
ret := mem.Get(offset.Int64(), size.Int64())
|
|
||||||
|
|
||||||
self.Printf(" => [%v, %v] (%d) 0x%x", offset, size, len(ret), ret).Endl()
|
|
||||||
|
|
||||||
return context.Return(ret), nil
|
|
||||||
case SUICIDE:
|
|
||||||
receiver := statedb.GetOrNewStateObject(stack.Pop().Bytes())
|
|
||||||
balance := statedb.GetBalance(context.Address())
|
|
||||||
|
|
||||||
self.Printf(" => (%x) %v", receiver.Address()[:4], balance)
|
|
||||||
|
|
||||||
receiver.AddAmount(balance)
|
|
||||||
statedb.Delete(context.Address())
|
|
||||||
|
|
||||||
fallthrough
|
|
||||||
case STOP: // Stop the context
|
|
||||||
self.Endl()
|
|
||||||
|
|
||||||
return context.Return(nil), nil
|
|
||||||
default:
|
|
||||||
vmlogger.Debugf("(pc) %-3v Invalid opcode %x\n", pc, op)
|
|
||||||
|
|
||||||
panic(fmt.Errorf("Invalid opcode %x", op))
|
|
||||||
}
|
|
||||||
|
|
||||||
pc++
|
|
||||||
|
|
||||||
self.Endl()
|
|
||||||
|
|
||||||
if self.Dbg != nil {
|
|
||||||
for _, instrNo := range self.Dbg.BreakPoints() {
|
|
||||||
if pc == uint64(instrNo) {
|
|
||||||
self.Stepping = true
|
|
||||||
|
|
||||||
if !self.Dbg.BreakHook(prevStep, op, mem, stack, statedb.GetStateObject(context.Address())) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
} else if self.Stepping {
|
|
||||||
if !self.Dbg.StepHook(prevStep, op, mem, stack, statedb.GetStateObject(context.Address())) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *DebugVm) calculateGasAndSize(context *Context, caller ContextRef, op OpCode, statedb *state.StateDB, mem *Memory, stack *Stack) (*big.Int, *big.Int) {
|
|
||||||
gas := new(big.Int)
|
|
||||||
addStepGasUsage := func(amount *big.Int) {
|
|
||||||
if amount.Cmp(ethutil.Big0) >= 0 {
|
|
||||||
gas.Add(gas, amount)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
addStepGasUsage(GasStep)
|
|
||||||
|
|
||||||
var newMemSize *big.Int = ethutil.Big0
|
|
||||||
var additionalGas *big.Int = new(big.Int)
|
|
||||||
// Stack Check, memory resize & gas phase
|
|
||||||
switch op {
|
|
||||||
// Stack checks only
|
|
||||||
case ISZERO, CALLDATALOAD, POP, JUMP, NOT: // 1
|
|
||||||
stack.require(1)
|
|
||||||
case JUMPI, ADD, SUB, DIV, SDIV, MOD, SMOD, LT, GT, SLT, SGT, EQ, AND, OR, XOR, BYTE, SIGNEXTEND: // 2
|
|
||||||
stack.require(2)
|
|
||||||
case ADDMOD, MULMOD: // 3
|
|
||||||
stack.require(3)
|
|
||||||
case SWAP1, SWAP2, SWAP3, SWAP4, SWAP5, SWAP6, SWAP7, SWAP8, SWAP9, SWAP10, SWAP11, SWAP12, SWAP13, SWAP14, SWAP15, SWAP16:
|
|
||||||
n := int(op - SWAP1 + 2)
|
|
||||||
stack.require(n)
|
|
||||||
case DUP1, DUP2, DUP3, DUP4, DUP5, DUP6, DUP7, DUP8, DUP9, DUP10, DUP11, DUP12, DUP13, DUP14, DUP15, DUP16:
|
|
||||||
n := int(op - DUP1 + 1)
|
|
||||||
stack.require(n)
|
|
||||||
case LOG0, LOG1, LOG2, LOG3, LOG4:
|
|
||||||
n := int(op - LOG0)
|
|
||||||
stack.require(n + 2)
|
|
||||||
|
|
||||||
gas.Set(GasLog)
|
|
||||||
addStepGasUsage(new(big.Int).Mul(big.NewInt(int64(n)), GasLog))
|
|
||||||
|
|
||||||
mSize, mStart := stack.Peekn()
|
|
||||||
addStepGasUsage(mSize)
|
|
||||||
|
|
||||||
newMemSize = calcMemSize(mStart, mSize)
|
|
||||||
case EXP:
|
|
||||||
stack.require(2)
|
|
||||||
|
|
||||||
gas.Set(big.NewInt(int64(len(stack.data[stack.Len()-2].Bytes()) + 1)))
|
|
||||||
// Gas only
|
|
||||||
case STOP:
|
|
||||||
gas.Set(ethutil.Big0)
|
|
||||||
case SUICIDE:
|
|
||||||
stack.require(1)
|
|
||||||
|
|
||||||
gas.Set(ethutil.Big0)
|
|
||||||
case SLOAD:
|
|
||||||
stack.require(1)
|
|
||||||
|
|
||||||
gas.Set(GasSLoad)
|
|
||||||
// Memory resize & Gas
|
|
||||||
case SSTORE:
|
|
||||||
stack.require(2)
|
|
||||||
|
|
||||||
var mult *big.Int
|
|
||||||
y, x := stack.Peekn()
|
|
||||||
val := statedb.GetState(context.Address(), x.Bytes())
|
|
||||||
if len(val) == 0 && len(y.Bytes()) > 0 {
|
|
||||||
// 0 => non 0
|
|
||||||
mult = ethutil.Big3
|
|
||||||
} else if len(val) > 0 && len(y.Bytes()) == 0 {
|
|
||||||
statedb.Refund(caller.Address(), GasSStoreRefund)
|
|
||||||
|
|
||||||
mult = ethutil.Big0
|
|
||||||
} else {
|
|
||||||
// non 0 => non 0 (or 0 => 0)
|
|
||||||
mult = ethutil.Big1
|
|
||||||
}
|
|
||||||
gas.Set(new(big.Int).Mul(mult, GasSStore))
|
|
||||||
case BALANCE:
|
|
||||||
stack.require(1)
|
|
||||||
gas.Set(GasBalance)
|
|
||||||
case MSTORE:
|
|
||||||
stack.require(2)
|
|
||||||
newMemSize = calcMemSize(stack.Peek(), u256(32))
|
|
||||||
case MLOAD:
|
|
||||||
stack.require(1)
|
|
||||||
|
|
||||||
newMemSize = calcMemSize(stack.Peek(), u256(32))
|
|
||||||
case MSTORE8:
|
|
||||||
stack.require(2)
|
|
||||||
newMemSize = calcMemSize(stack.Peek(), u256(1))
|
|
||||||
case RETURN:
|
|
||||||
stack.require(2)
|
|
||||||
|
|
||||||
newMemSize = calcMemSize(stack.Peek(), stack.data[stack.Len()-2])
|
|
||||||
case SHA3:
|
|
||||||
stack.require(2)
|
|
||||||
gas.Set(GasSha)
|
|
||||||
newMemSize = calcMemSize(stack.Peek(), stack.data[stack.Len()-2])
|
|
||||||
additionalGas.Set(stack.data[stack.Len()-2])
|
|
||||||
case CALLDATACOPY:
|
|
||||||
stack.require(2)
|
|
||||||
|
|
||||||
newMemSize = calcMemSize(stack.Peek(), stack.data[stack.Len()-3])
|
|
||||||
additionalGas.Set(stack.data[stack.Len()-3])
|
|
||||||
case CODECOPY:
|
|
||||||
stack.require(3)
|
|
||||||
|
|
||||||
newMemSize = calcMemSize(stack.Peek(), stack.data[stack.Len()-3])
|
|
||||||
additionalGas.Set(stack.data[stack.Len()-3])
|
|
||||||
case EXTCODECOPY:
|
|
||||||
stack.require(4)
|
|
||||||
|
|
||||||
newMemSize = calcMemSize(stack.data[stack.Len()-2], stack.data[stack.Len()-4])
|
|
||||||
additionalGas.Set(stack.data[stack.Len()-4])
|
|
||||||
case CALL, CALLCODE:
|
|
||||||
stack.require(7)
|
|
||||||
gas.Set(GasCall)
|
|
||||||
addStepGasUsage(stack.data[stack.Len()-1])
|
|
||||||
|
|
||||||
x := calcMemSize(stack.data[stack.Len()-6], stack.data[stack.Len()-7])
|
|
||||||
y := calcMemSize(stack.data[stack.Len()-4], stack.data[stack.Len()-5])
|
|
||||||
|
|
||||||
newMemSize = ethutil.BigMax(x, y)
|
|
||||||
case CREATE:
|
|
||||||
stack.require(3)
|
|
||||||
gas.Set(GasCreate)
|
|
||||||
|
|
||||||
newMemSize = calcMemSize(stack.data[stack.Len()-2], stack.data[stack.Len()-3])
|
|
||||||
}
|
|
||||||
|
|
||||||
switch op {
|
|
||||||
case CALLDATACOPY, CODECOPY, EXTCODECOPY:
|
|
||||||
additionalGas.Add(additionalGas, u256(31))
|
|
||||||
additionalGas.Div(additionalGas, u256(32))
|
|
||||||
addStepGasUsage(additionalGas)
|
|
||||||
case SHA3:
|
|
||||||
additionalGas.Add(additionalGas, u256(31))
|
|
||||||
additionalGas.Div(additionalGas, u256(32))
|
|
||||||
additionalGas.Mul(additionalGas, GasSha3Byte)
|
|
||||||
addStepGasUsage(additionalGas)
|
|
||||||
}
|
|
||||||
|
|
||||||
if newMemSize.Cmp(ethutil.Big0) > 0 {
|
|
||||||
newMemSize.Add(newMemSize, u256(31))
|
|
||||||
newMemSize.Div(newMemSize, u256(32))
|
|
||||||
newMemSize.Mul(newMemSize, u256(32))
|
|
||||||
|
|
||||||
if newMemSize.Cmp(u256(int64(mem.Len()))) > 0 {
|
|
||||||
memGasUsage := new(big.Int).Sub(newMemSize, u256(int64(mem.Len())))
|
|
||||||
memGasUsage.Mul(GasMemory, memGasUsage)
|
|
||||||
memGasUsage.Div(memGasUsage, u256(32))
|
|
||||||
|
|
||||||
addStepGasUsage(memGasUsage)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return newMemSize, gas
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *DebugVm) RunPrecompiled(p *PrecompiledAccount, callData []byte, context *Context) (ret []byte, err error) {
|
|
||||||
gas := p.Gas(len(callData))
|
|
||||||
if context.UseGas(gas) {
|
|
||||||
ret = p.Call(callData)
|
|
||||||
self.Printf("NATIVE_FUNC => %x", ret)
|
|
||||||
self.Endl()
|
|
||||||
|
|
||||||
return context.Return(ret), nil
|
|
||||||
} else {
|
|
||||||
self.Printf("NATIVE_FUNC => failed").Endl()
|
|
||||||
|
|
||||||
tmp := new(big.Int).Set(context.Gas)
|
|
||||||
|
|
||||||
panic(OOG(gas, tmp).Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *DebugVm) Printf(format string, v ...interface{}) VirtualMachine {
|
|
||||||
if self.logTy == LogTyPretty {
|
|
||||||
self.logStr += fmt.Sprintf(format, v...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *DebugVm) Endl() VirtualMachine {
|
|
||||||
if self.logTy == LogTyPretty {
|
|
||||||
vmlogger.Debugln(self.logStr)
|
|
||||||
self.logStr = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *DebugVm) Env() Environment {
|
|
||||||
return self.env
|
|
||||||
}
|
|
@ -3,12 +3,12 @@ package vm
|
|||||||
import "math/big"
|
import "math/big"
|
||||||
|
|
||||||
type JitVm struct {
|
type JitVm struct {
|
||||||
env Environment
|
env Environment
|
||||||
backup *DebugVm
|
backup *Vm
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewJitVm(env Environment) *JitVm {
|
func NewJitVm(env Environment) *JitVm {
|
||||||
backupVm := NewDebugVm(env)
|
backupVm := NewVm(env)
|
||||||
return &JitVm{env: env, backup: backupVm}
|
return &JitVm{env: env, backup: backupVm}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,4 +28,4 @@ func (self *JitVm) Env() Environment {
|
|||||||
return self.env
|
return self.env
|
||||||
}
|
}
|
||||||
|
|
||||||
//go is nice
|
//go is nice
|
||||||
|
Loading…
Reference in New Issue
Block a user