From 468501cb860508af55e1fcd586e1498df0a2d984 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 10 Jun 2015 10:44:46 +0200 Subject: core/vm: changed program counter to uint64 --- core/vm/vm.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'core/vm/vm.go') diff --git a/core/vm/vm.go b/core/vm/vm.go index 2bd950385..ed4157178 100644 --- a/core/vm/vm.go +++ b/core/vm/vm.go @@ -81,17 +81,17 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) { codehash = crypto.Sha3Hash(code) mem = NewMemory() stack = newStack() - pc = new(big.Int) + pc = uint64(0) statedb = self.env.State() - jump = func(from *big.Int, to *big.Int) error { + jump = func(from uint64, to *big.Int) error { if !context.jumpdests.has(codehash, code, to) { - nop := context.GetOp(to) + nop := context.GetOp(to.Uint64()) return fmt.Errorf("invalid jump destination (%v) %v", nop, to) } self.Printf(" ~> %v", to) - pc = to + pc = to.Uint64() self.Endl() @@ -519,11 +519,11 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) { stack.push(self.env.GasLimit()) 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 := big.NewInt(int64(op - PUSH1 + 1)) - byts := getData(code, new(big.Int).Add(pc, big.NewInt(1)), a) + size := uint64(op - PUSH1 + 1) + byts := getData(code, new(big.Int).SetUint64(pc+1), new(big.Int).SetUint64(size)) // push value to stack stack.push(common.Bytes2Big(byts)) - pc.Add(pc, a) + pc += size self.Printf(" => 0x%x", byts) case POP: @@ -603,7 +603,7 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) { case JUMPDEST: case PC: - stack.push(pc) + stack.push(new(big.Int).SetUint64(pc)) case MSIZE: stack.push(big.NewInt(int64(mem.Len()))) case GAS: @@ -708,7 +708,7 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) { return nil, fmt.Errorf("Invalid opcode %x", op) } - pc.Add(pc, One) + pc++ self.Endl() } -- cgit v1.2.3 From ff5b3ef0877978699235d20b3caa9890b35ec6f8 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 10 Jun 2015 10:59:44 +0200 Subject: core/vm: added structured logging --- core/vm/vm.go | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) (limited to 'core/vm/vm.go') diff --git a/core/vm/vm.go b/core/vm/vm.go index ed4157178..e6d4c8df2 100644 --- a/core/vm/vm.go +++ b/core/vm/vm.go @@ -11,10 +11,18 @@ import ( "github.com/ethereum/go-ethereum/params" ) +type log struct { + op OpCode + gas *big.Int + memory []byte + stack []*big.Int +} + type Vm struct { env Environment - logTy byte + // structured logging + Logs []log logStr string err error @@ -32,9 +40,7 @@ type Vm struct { } func New(env Environment) *Vm { - lt := LogTyPretty - - return &Vm{debug: Debug, env: env, logTy: lt, Recoverable: true} + return &Vm{env: env, Recoverable: true} } func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) { @@ -106,6 +112,8 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) { // Get the memory location of pc op = context.GetOp(pc) + self.Log(op, context.Gas, mem, stack) + self.Printf("(pc) %-3d -o- %-14s (m) %-4d (s) %-4d ", pc, op.String(), mem.Len(), stack.len()) newMemSize, gas, err := self.calculateGasAndSize(context, caller, op, statedb, mem, stack) if err != nil { @@ -855,6 +863,16 @@ func (self *Vm) calculateGasAndSize(context *Context, caller ContextRef, op OpCo return newMemSize, gas, nil } +func (vm *Vm) Log(op OpCode, gas *big.Int, memory *Memory, stack *stack) { + if vm.debug { + mem := make([]byte, len(memory.store)) + copy(mem, memory.store) + stck := make([]*big.Int, len(stack.data)) + copy(stck, stack.data) + vm.Logs = append(vm.Logs, log{op, new(big.Int).Set(gas), mem, stck}) + } +} + func (self *Vm) RunPrecompiled(p *PrecompiledAccount, callData []byte, context *Context) (ret []byte, err error) { gas := p.Gas(len(callData)) if context.UseGas(gas) { -- cgit v1.2.3 From 38c61f6f2567e7943c9a16e2be0a2bfedb3a1fb3 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 10 Jun 2015 12:23:49 +0200 Subject: core, core/vm: added structure logging This also reduces the time required spend in the VM --- core/vm/vm.go | 147 ++++++---------------------------------------------------- 1 file changed, 14 insertions(+), 133 deletions(-) (limited to 'core/vm/vm.go') diff --git a/core/vm/vm.go b/core/vm/vm.go index e6d4c8df2..7c4a7ce6d 100644 --- a/core/vm/vm.go +++ b/core/vm/vm.go @@ -7,24 +7,12 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/params" ) -type log struct { - op OpCode - gas *big.Int - memory []byte - stack []*big.Int -} - type Vm struct { env Environment - // structured logging - Logs []log - logStr string - err error // For logging debug bool @@ -40,7 +28,7 @@ type Vm struct { } func New(env Environment) *Vm { - return &Vm{env: env, Recoverable: true} + return &Vm{env: env, debug: Debug, Recoverable: true} } func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) { @@ -54,8 +42,6 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) { price = context.Price ) - self.Printf("(%d) (%x) %x (code=%d) gas: %v (d) %x", self.env.Depth(), caller.Address().Bytes()[:4], context.Address(), len(code), context.Gas, callData).Endl() - // User defer pattern to check for an error and, based on the error being nil or not, use all gas and return. defer func() { if self.After != nil { @@ -63,7 +49,7 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) { } if err != nil { - self.Printf(" %v", err).Endl() + // In case of a VM exception (known exceptions) all gas consumed (panics NOT included). context.UseGas(context.Gas) @@ -96,11 +82,8 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) { return fmt.Errorf("invalid jump destination (%v) %v", nop, to) } - self.Printf(" ~> %v", to) pc = to.Uint64() - self.Endl() - return nil } ) @@ -112,18 +95,14 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) { // Get the memory location of pc op = context.GetOp(pc) - self.Log(op, context.Gas, mem, stack) + self.log(pc, op, context.Gas, mem, stack) - self.Printf("(pc) %-3d -o- %-14s (m) %-4d (s) %-4d ", pc, op.String(), mem.Len(), stack.len()) newMemSize, gas, err := self.calculateGasAndSize(context, caller, op, statedb, mem, stack) if err != nil { return nil, err } - self.Printf("(g) %-3v (%v)", gas, context.Gas) - if !context.UseGas(gas) { - self.Endl() tmp := new(big.Int).Set(context.Gas) @@ -137,40 +116,33 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) { switch op { case ADD: x, y := stack.pop(), stack.pop() - self.Printf(" %v + %v", y, x) base.Add(x, y) U256(base) - self.Printf(" = %v", base) // pop result back on the stack stack.push(base) case SUB: x, y := stack.pop(), stack.pop() - self.Printf(" %v - %v", x, y) base.Sub(x, y) U256(base) - self.Printf(" = %v", base) // pop result back on the stack stack.push(base) case MUL: x, y := stack.pop(), stack.pop() - self.Printf(" %v * %v", y, x) base.Mul(x, y) 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(common.Big0) != 0 { base.Div(x, y) @@ -178,14 +150,11 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) { 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(common.Big0) == 0 { base.Set(common.Big0) } else { @@ -201,13 +170,10 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) { 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(common.Big0) == 0 { base.Set(common.Big0) } else { @@ -216,13 +182,10 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) { 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(common.Big0) == 0 { base.Set(common.Big0) } else { @@ -238,20 +201,15 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) { U256(base) } - self.Printf(" = %v", base) stack.push(base) case EXP: x, y := stack.pop(), stack.pop() - self.Printf(" %v ** %v", x, y) - base.Exp(x, y, Pow256) U256(base) - self.Printf(" = %v", base) - stack.push(base) case SIGNEXTEND: back := stack.pop() @@ -268,15 +226,13 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) { num = U256(num) - self.Printf(" = %v", num) - stack.push(num) } case NOT: stack.push(U256(new(big.Int).Not(stack.pop()))) case LT: x, y := stack.pop(), stack.pop() - self.Printf(" %v < %v", x, y) + // x < y if x.Cmp(y) < 0 { stack.push(common.BigTrue) @@ -285,7 +241,6 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) { } case GT: x, y := stack.pop(), stack.pop() - self.Printf(" %v > %v", x, y) // x > y if x.Cmp(y) > 0 { @@ -296,7 +251,7 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) { case SLT: x, y := S256(stack.pop()), S256(stack.pop()) - self.Printf(" %v < %v", x, y) + // x < y if x.Cmp(S256(y)) < 0 { stack.push(common.BigTrue) @@ -305,7 +260,6 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) { } case SGT: x, y := S256(stack.pop()), S256(stack.pop()) - self.Printf(" %v > %v", x, y) // x > y if x.Cmp(y) > 0 { @@ -316,7 +270,6 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) { case EQ: x, y := stack.pop(), stack.pop() - self.Printf(" %v == %v", y, x) // x == y if x.Cmp(y) == 0 { @@ -334,17 +287,14 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) { case AND: x, y := stack.pop(), stack.pop() - self.Printf(" %v & %v", y, x) stack.push(base.And(x, y)) case OR: x, y := stack.pop(), stack.pop() - self.Printf(" %v | %v", x, y) stack.push(base.Or(x, y)) case XOR: x, y := stack.pop(), stack.pop() - self.Printf(" %v ^ %v", x, y) stack.push(base.Xor(x, y)) case BYTE: @@ -358,8 +308,6 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) { base.Set(common.BigFalse) } - self.Printf(" => 0x%x", base.Bytes()) - stack.push(base) case ADDMOD: x := stack.pop() @@ -373,8 +321,6 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) { base = U256(base) } - self.Printf(" %v + %v %% %v = %v", x, y, z, base) - stack.push(base) case MULMOD: x := stack.pop() @@ -388,8 +334,6 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) { U256(base) } - self.Printf(" %v + %v %% %v = %v", x, y, z, base) - stack.push(base) case SHA3: @@ -398,44 +342,35 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) { stack.push(common.BigD(data)) - self.Printf(" => (%v) %x", size, data) case ADDRESS: stack.push(common.Bytes2Big(context.Address().Bytes())) - self.Printf(" => %x", context.Address()) case BALANCE: addr := common.BigToAddress(stack.pop()) balance := statedb.GetBalance(addr) stack.push(balance) - self.Printf(" => %v (%x)", balance, addr) case ORIGIN: origin := self.env.Origin() stack.push(origin.Big()) - self.Printf(" => %x", origin) case CALLER: caller := context.caller.Address() stack.push(common.Bytes2Big(caller.Bytes())) - self.Printf(" => %x", caller) case CALLVALUE: stack.push(value) - self.Printf(" => %v", value) case CALLDATALOAD: data := getData(callData, stack.pop(), common.Big32) - self.Printf(" => 0x%x", data) - stack.push(common.Bytes2Big(data)) case CALLDATASIZE: l := int64(len(callData)) stack.push(big.NewInt(l)) - self.Printf(" => %d", l) case CALLDATACOPY: var ( mOff = stack.pop() @@ -446,7 +381,6 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) { mem.Set(mOff.Uint64(), l.Uint64(), data) - self.Printf(" => [%v, %v, %v]", mOff, cOff, l) case CODESIZE, EXTCODESIZE: var code []byte if op == EXTCODESIZE { @@ -460,7 +394,6 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) { l := big.NewInt(int64(len(code))) stack.push(l) - self.Printf(" => %d", l) case CODECOPY, EXTCODECOPY: var code []byte if op == EXTCODECOPY { @@ -480,12 +413,9 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) { mem.Set(mOff.Uint64(), l.Uint64(), codeCopy) - self.Printf(" => [%v, %v, %v] %x", mOff, cOff, l, codeCopy) case GASPRICE: stack.push(context.Price) - self.Printf(" => %x", context.Price) - case BLOCKHASH: num := stack.pop() @@ -496,33 +426,27 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) { stack.push(common.Big0) } - self.Printf(" => 0x%x", stack.peek().Bytes()) case COINBASE: coinbase := self.env.Coinbase() stack.push(coinbase.Big()) - 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(U256(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()) @@ -533,19 +457,16 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) { stack.push(common.Bytes2Big(byts)) pc += size - 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.dup(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) stack.swap(n) - self.Printf(" => [%d]", n) case LOG0, LOG1, LOG2, LOG3, LOG4: n := int(op - LOG0) topics := make([]common.Hash, n) @@ -558,38 +479,32 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) { log := state.NewLog(context.Address(), topics, data, self.env.BlockNumber().Uint64()) self.env.AddLog(log) - self.Printf(" => %v", log) case MLOAD: offset := stack.pop() val := common.BigD(mem.Get(offset.Int64(), 32)) stack.push(val) - self.Printf(" => 0x%x", val.Bytes()) case MSTORE: // pop value of the stack mStart, val := stack.pop(), stack.pop() mem.Set(mStart.Uint64(), 32, common.BigToBytes(val, 256)) - self.Printf(" => 0x%x", val) case MSTORE8: off, val := stack.pop().Int64(), stack.pop().Int64() mem.store[off] = byte(val & 0xff) - self.Printf(" => [%v] 0x%x", off, mem.store[off]) case SLOAD: loc := common.BigToHash(stack.pop()) val := common.Bytes2Big(statedb.GetState(context.Address(), loc)) stack.push(val) - self.Printf(" {0x%x : 0x%x}", loc, val.Bytes()) case SSTORE: loc := common.BigToHash(stack.pop()) val := stack.pop() statedb.SetState(context.Address(), loc, val) - self.Printf(" {0x%x : 0x%x}", loc, val.Bytes()) case JUMP: if err := jump(pc, stack.pop()); err != nil { return nil, err @@ -607,8 +522,6 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) { continue } - self.Printf(" ~> false") - case JUMPDEST: case PC: stack.push(new(big.Int).SetUint64(pc)) @@ -617,7 +530,6 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) { case GAS: stack.push(context.Gas) - self.Printf(" => %x", context.Gas) case CREATE: var ( @@ -627,14 +539,12 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) { gas = new(big.Int).Set(context.Gas) addr common.Address ) - self.Endl() context.UseGas(context.Gas) ret, suberr, ref := self.env.Create(context, input, gas, price, value) if suberr != nil { stack.push(common.BigFalse) - self.Printf(" (*) 0x0 %v", suberr) } else { // gas < len(ret) * CreateDataGas == NO_CODE dataGas := big.NewInt(int64(len(ret))) @@ -659,7 +569,6 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) { retOffset, retSize := stack.pop(), stack.pop() address := common.BigToAddress(addr) - self.Printf(" => %x", address).Endl() // Get the arguments from the memory args := mem.Get(inOffset.Int64(), inSize.Int64()) @@ -681,48 +590,40 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) { if err != nil { stack.push(common.BigFalse) - self.Printf("%v").Endl() } else { stack.push(common.BigTrue) mem.Set(retOffset.Uint64(), retSize.Uint64(), ret) } - self.Printf("resume %x (%v)", context.Address(), context.Gas) + case RETURN: offset, size := stack.pop(), stack.pop() ret := mem.GetPtr(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(common.BigToAddress(stack.pop())) balance := statedb.GetBalance(context.Address()) - self.Printf(" => (%x) %v", receiver.Address().Bytes()[:4], balance) - receiver.AddBalance(balance) statedb.Delete(context.Address()) fallthrough case STOP: // Stop the context - self.Endl() return context.Return(nil), nil default: - self.Printf("(pc) %-3v Invalid opcode %x\n", pc, op).Endl() return nil, fmt.Errorf("Invalid opcode %x", op) } pc++ - self.Endl() } } -func (self *Vm) calculateGasAndSize(context *Context, caller ContextRef, op OpCode, statedb *state.StateDB, mem *Memory, stack *stack) (*big.Int, *big.Int, error) { +func (self *Vm) calculateGasAndSize(context *Context, caller ContextRef, op OpCode, statedb *state.StateDB, mem *Memory, stack *Stack) (*big.Int, *big.Int, error) { var ( gas = new(big.Int) newMemSize *big.Int = new(big.Int) @@ -863,26 +764,13 @@ func (self *Vm) calculateGasAndSize(context *Context, caller ContextRef, op OpCo return newMemSize, gas, nil } -func (vm *Vm) Log(op OpCode, gas *big.Int, memory *Memory, stack *stack) { - if vm.debug { - mem := make([]byte, len(memory.store)) - copy(mem, memory.store) - stck := make([]*big.Int, len(stack.data)) - copy(stck, stack.data) - vm.Logs = append(vm.Logs, log{op, new(big.Int).Set(gas), mem, stck}) - } -} - 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) @@ -890,21 +778,14 @@ func (self *Vm) RunPrecompiled(p *PrecompiledAccount, callData []byte, context * } } -func (self *Vm) Printf(format string, v ...interface{}) VirtualMachine { - if self.debug { - self.logStr += fmt.Sprintf(format, v...) - } - - return self -} - -func (self *Vm) Endl() VirtualMachine { - if self.debug { - glog.V(0).Infoln(self.logStr) - self.logStr = "" +func (self *Vm) log(pc uint64, op OpCode, gas *big.Int, memory *Memory, stack *Stack) { + if Debug { + mem := make([]byte, len(memory.Data())) + copy(mem, memory.Data()) + stck := make([]*big.Int, len(stack.Data())) + copy(stck, stack.Data()) + self.env.AddStructLog(StructLog{pc, op, new(big.Int).Set(gas), mem, stck}) } - - return self } func (self *Vm) Env() Environment { -- cgit v1.2.3 From 6fb6e6679eb7c329ac9013d0c879a7c4b17daca5 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 10 Jun 2015 12:57:37 +0200 Subject: core/vm, core/state: added storage to structured vm logging --- core/vm/vm.go | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'core/vm/vm.go') diff --git a/core/vm/vm.go b/core/vm/vm.go index 7c4a7ce6d..e4f6e9268 100644 --- a/core/vm/vm.go +++ b/core/vm/vm.go @@ -95,7 +95,7 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) { // Get the memory location of pc op = context.GetOp(pc) - self.log(pc, op, context.Gas, mem, stack) + self.log(pc, op, context.Gas, mem, stack, context) newMemSize, gas, err := self.calculateGasAndSize(context, caller, op, statedb, mem, stack) if err != nil { @@ -778,13 +778,20 @@ func (self *Vm) RunPrecompiled(p *PrecompiledAccount, callData []byte, context * } } -func (self *Vm) log(pc uint64, op OpCode, gas *big.Int, memory *Memory, stack *Stack) { +func (self *Vm) log(pc uint64, op OpCode, gas *big.Int, memory *Memory, stack *Stack, context *Context) { if Debug { mem := make([]byte, len(memory.Data())) copy(mem, memory.Data()) stck := make([]*big.Int, len(stack.Data())) copy(stck, stack.Data()) - self.env.AddStructLog(StructLog{pc, op, new(big.Int).Set(gas), mem, stck}) + + object := context.self.(*state.StateObject) + storage := make(map[common.Hash][]byte) + object.EachStorage(func(k, v []byte) { + storage[common.BytesToHash(k)] = v + }) + + self.env.AddStructLog(StructLog{pc, op, new(big.Int).Set(gas), mem, stck, storage}) } } -- cgit v1.2.3 From 065aff9ffa2bee1008d1f406328dd12a073cb239 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 10 Jun 2015 17:40:13 +0200 Subject: core/vm: documentation and name changes --- core/vm/vm.go | 48 ++++++++++++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 18 deletions(-) (limited to 'core/vm/vm.go') diff --git a/core/vm/vm.go b/core/vm/vm.go index e4f6e9268..bf8bbcdc2 100644 --- a/core/vm/vm.go +++ b/core/vm/vm.go @@ -10,6 +10,7 @@ import ( "github.com/ethereum/go-ethereum/params" ) +// Vm implements VirtualMachine type Vm struct { env Environment @@ -27,11 +28,13 @@ type Vm struct { After func(*Context, error) } +// New returns a new Virtual Machine func New(env Environment) *Vm { return &Vm{env: env, debug: Debug, Recoverable: true} } -func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) { +// Run loops and evaluates the contract's code with the given input data +func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) { self.env.SetDepth(self.env.Depth() + 1) defer self.env.SetDepth(self.env.Depth() - 1) @@ -59,7 +62,7 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) { if context.CodeAddr != nil { if p := Precompiled[context.CodeAddr.Str()]; p != nil { - return self.RunPrecompiled(p, callData, context) + return self.RunPrecompiled(p, input, context) } } @@ -69,13 +72,15 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) { } var ( - op OpCode - codehash = crypto.Sha3Hash(code) - mem = NewMemory() - stack = newStack() - pc = uint64(0) - statedb = self.env.State() - + op OpCode // current opcode + codehash = crypto.Sha3Hash(code) // codehash is used when doing jump dest caching + mem = NewMemory() // bound memory + stack = newStack() // local stack + pc = uint64(0) // program counter + statedb = self.env.State() // current state + + // jump evaluates and checks whether the given jump destination is a valid one + // if valid move the `pc` otherwise return an error. jump = func(from uint64, to *big.Int) error { if !context.jumpdests.has(codehash, code, to) { nop := context.GetOp(to.Uint64()) @@ -97,20 +102,22 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) { self.log(pc, op, context.Gas, mem, stack, context) + // calculate the new memory size and gas price for the current executing opcode newMemSize, gas, err := self.calculateGasAndSize(context, caller, op, statedb, mem, stack) if err != nil { return nil, err } + // Use the calculated gas. When insufficient gas is present, use all gas and return an + // Out Of Gas error if !context.UseGas(gas) { - tmp := new(big.Int).Set(context.Gas) context.UseGas(context.Gas) return context.Return(nil), OOG(gas, tmp) } - + // Resize the memory calculated previously mem.Resize(newMemSize.Uint64()) switch op { @@ -364,11 +371,11 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) { stack.push(value) case CALLDATALOAD: - data := getData(callData, stack.pop(), common.Big32) + data := getData(input, stack.pop(), common.Big32) stack.push(common.Bytes2Big(data)) case CALLDATASIZE: - l := int64(len(callData)) + l := int64(len(input)) stack.push(big.NewInt(l)) case CALLDATACOPY: @@ -377,7 +384,7 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) { cOff = stack.pop() l = stack.pop() ) - data := getData(callData, cOff, l) + data := getData(input, cOff, l) mem.Set(mOff.Uint64(), l.Uint64(), data) @@ -623,6 +630,8 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) { } } +// calculateGasAndSize calculates the required given the opcode and stack items calculates the new memorysize for +// the operation. This does not reduce gas or resizes the memory. func (self *Vm) calculateGasAndSize(context *Context, caller ContextRef, op OpCode, statedb *state.StateDB, mem *Memory, stack *Stack) (*big.Int, *big.Int, error) { var ( gas = new(big.Int) @@ -764,20 +773,22 @@ func (self *Vm) calculateGasAndSize(context *Context, caller ContextRef, op OpCo return newMemSize, gas, nil } -func (self *Vm) RunPrecompiled(p *PrecompiledAccount, callData []byte, context *Context) (ret []byte, err error) { - gas := p.Gas(len(callData)) +// RunPrecompile runs and evaluate the output of a precompiled contract defined in contracts.go +func (self *Vm) RunPrecompiled(p *PrecompiledAccount, input []byte, context *Context) (ret []byte, err error) { + gas := p.Gas(len(input)) if context.UseGas(gas) { - ret = p.Call(callData) + ret = p.Call(input) return context.Return(ret), nil } else { - tmp := new(big.Int).Set(context.Gas) return nil, OOG(gas, tmp) } } +// log emits a log event to the environment for each opcode encountered. This is not to be confused with the +// LOG* opcode. func (self *Vm) log(pc uint64, op OpCode, gas *big.Int, memory *Memory, stack *Stack, context *Context) { if Debug { mem := make([]byte, len(memory.Data())) @@ -795,6 +806,7 @@ func (self *Vm) log(pc uint64, op OpCode, gas *big.Int, memory *Memory, stack *S } } +// Environment returns the current workable state of the VM func (self *Vm) Env() Environment { return self.env } -- cgit v1.2.3 From fc2a061d510fbe09534ee1ade167d66c40ba7bf1 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 10 Jun 2015 17:45:21 +0200 Subject: core/vm: unexported stack again. No longer required --- core/vm/vm.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'core/vm/vm.go') diff --git a/core/vm/vm.go b/core/vm/vm.go index bf8bbcdc2..fe380d79d 100644 --- a/core/vm/vm.go +++ b/core/vm/vm.go @@ -75,7 +75,7 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) { op OpCode // current opcode codehash = crypto.Sha3Hash(code) // codehash is used when doing jump dest caching mem = NewMemory() // bound memory - stack = newStack() // local stack + stack = newstack() // local stack pc = uint64(0) // program counter statedb = self.env.State() // current state @@ -632,7 +632,7 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) { // calculateGasAndSize calculates the required given the opcode and stack items calculates the new memorysize for // the operation. This does not reduce gas or resizes the memory. -func (self *Vm) calculateGasAndSize(context *Context, caller ContextRef, op OpCode, statedb *state.StateDB, mem *Memory, stack *Stack) (*big.Int, *big.Int, error) { +func (self *Vm) calculateGasAndSize(context *Context, caller ContextRef, op OpCode, statedb *state.StateDB, mem *Memory, stack *stack) (*big.Int, *big.Int, error) { var ( gas = new(big.Int) newMemSize *big.Int = new(big.Int) @@ -789,7 +789,7 @@ func (self *Vm) RunPrecompiled(p *PrecompiledAccount, input []byte, context *Con // log emits a log event to the environment for each opcode encountered. This is not to be confused with the // LOG* opcode. -func (self *Vm) log(pc uint64, op OpCode, gas *big.Int, memory *Memory, stack *Stack, context *Context) { +func (self *Vm) log(pc uint64, op OpCode, gas *big.Int, memory *Memory, stack *stack, context *Context) { if Debug { mem := make([]byte, len(memory.Data())) copy(mem, memory.Data()) -- cgit v1.2.3 From 10af69b57c8022bb400e1f00bb3c6413e640a7e1 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 10 Jun 2015 19:56:40 +0200 Subject: core, core/vm: moved logger and added gas cost to struct logging --- core/vm/vm.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'core/vm/vm.go') diff --git a/core/vm/vm.go b/core/vm/vm.go index fe380d79d..117331389 100644 --- a/core/vm/vm.go +++ b/core/vm/vm.go @@ -100,14 +100,14 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) { // Get the memory location of pc op = context.GetOp(pc) - self.log(pc, op, context.Gas, mem, stack, context) - // calculate the new memory size and gas price for the current executing opcode newMemSize, gas, err := self.calculateGasAndSize(context, caller, op, statedb, mem, stack) if err != nil { return nil, err } + self.log(pc, op, context.Gas, gas, mem, stack, context) + // Use the calculated gas. When insufficient gas is present, use all gas and return an // Out Of Gas error if !context.UseGas(gas) { @@ -789,7 +789,7 @@ func (self *Vm) RunPrecompiled(p *PrecompiledAccount, input []byte, context *Con // log emits a log event to the environment for each opcode encountered. This is not to be confused with the // LOG* opcode. -func (self *Vm) log(pc uint64, op OpCode, gas *big.Int, memory *Memory, stack *stack, context *Context) { +func (self *Vm) log(pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, stack *stack, context *Context) { if Debug { mem := make([]byte, len(memory.Data())) copy(mem, memory.Data()) @@ -802,7 +802,7 @@ func (self *Vm) log(pc uint64, op OpCode, gas *big.Int, memory *Memory, stack *s storage[common.BytesToHash(k)] = v }) - self.env.AddStructLog(StructLog{pc, op, new(big.Int).Set(gas), mem, stck, storage}) + self.env.AddStructLog(StructLog{pc, op, new(big.Int).Set(gas), cost, mem, stck, storage}) } } -- cgit v1.2.3 From f599a1b5f143503817c9fa411854b9a8dac6ba72 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 11 Jun 2015 11:59:30 +0200 Subject: core/vm: added a comment regarding the uint64 vs *big.Int --- core/vm/vm.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'core/vm/vm.go') diff --git a/core/vm/vm.go b/core/vm/vm.go index 117331389..4c0ab0f47 100644 --- a/core/vm/vm.go +++ b/core/vm/vm.go @@ -76,8 +76,10 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) { codehash = crypto.Sha3Hash(code) // codehash is used when doing jump dest caching mem = NewMemory() // bound memory stack = newstack() // local stack - pc = uint64(0) // program counter statedb = self.env.State() // current state + // For optimisation reason we're using uint64 as the program counter. + // It's theoretically possible to go above 2^64. The YP defines the PC to be uint256. Pratically much less so feasible. + pc = uint64(0) // program counter // jump evaluates and checks whether the given jump destination is a valid one // if valid move the `pc` otherwise return an error. -- cgit v1.2.3 From 287f99089181c1eaa6f25a6b531e476b631a201a Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 12 Jun 2015 13:35:14 +0200 Subject: core/vm: Improved error reporting for trace logging --- core/vm/vm.go | 69 +++++++++++++++++++++++++++++------------------------------ 1 file changed, 34 insertions(+), 35 deletions(-) (limited to 'core/vm/vm.go') diff --git a/core/vm/vm.go b/core/vm/vm.go index 4c0ab0f47..c5ad761f6 100644 --- a/core/vm/vm.go +++ b/core/vm/vm.go @@ -43,6 +43,31 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) { code = context.Code value = context.value price = context.Price + + op OpCode // current opcode + codehash = crypto.Sha3Hash(code) // codehash is used when doing jump dest caching + mem = NewMemory() // bound memory + stack = newstack() // local stack + statedb = self.env.State() // current state + // For optimisation reason we're using uint64 as the program counter. + // It's theoretically possible to go above 2^64. The YP defines the PC to be uint256. Pratically much less so feasible. + pc = uint64(0) // program counter + + // jump evaluates and checks whether the given jump destination is a valid one + // if valid move the `pc` otherwise return an error. + jump = func(from uint64, to *big.Int) error { + if !context.jumpdests.has(codehash, code, to) { + nop := context.GetOp(to.Uint64()) + return fmt.Errorf("invalid jump destination (%v) %v", nop, to) + } + + pc = to.Uint64() + + return nil + } + + newMemSize *big.Int + cost *big.Int ) // User defer pattern to check for an error and, based on the error being nil or not, use all gas and return. @@ -52,6 +77,7 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) { } if err != nil { + self.log(pc, op, context.Gas, cost, mem, stack, context, err) // In case of a VM exception (known exceptions) all gas consumed (panics NOT included). context.UseGas(context.Gas) @@ -71,30 +97,6 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) { return context.Return(nil), nil } - var ( - op OpCode // current opcode - codehash = crypto.Sha3Hash(code) // codehash is used when doing jump dest caching - mem = NewMemory() // bound memory - stack = newstack() // local stack - statedb = self.env.State() // current state - // For optimisation reason we're using uint64 as the program counter. - // It's theoretically possible to go above 2^64. The YP defines the PC to be uint256. Pratically much less so feasible. - pc = uint64(0) // program counter - - // jump evaluates and checks whether the given jump destination is a valid one - // if valid move the `pc` otherwise return an error. - jump = func(from uint64, to *big.Int) error { - if !context.jumpdests.has(codehash, code, to) { - nop := context.GetOp(to.Uint64()) - return fmt.Errorf("invalid jump destination (%v) %v", nop, to) - } - - pc = to.Uint64() - - return nil - } - ) - for { // The base for all big integer arithmetic base := new(big.Int) @@ -103,24 +105,23 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) { op = context.GetOp(pc) // calculate the new memory size and gas price for the current executing opcode - newMemSize, gas, err := self.calculateGasAndSize(context, caller, op, statedb, mem, stack) + newMemSize, cost, err = self.calculateGasAndSize(context, caller, op, statedb, mem, stack) if err != nil { return nil, err } - self.log(pc, op, context.Gas, gas, mem, stack, context) - // Use the calculated gas. When insufficient gas is present, use all gas and return an // Out Of Gas error - if !context.UseGas(gas) { - tmp := new(big.Int).Set(context.Gas) + if !context.UseGas(cost) { context.UseGas(context.Gas) - return context.Return(nil), OOG(gas, tmp) + return context.Return(nil), OutOfGasError{} } // Resize the memory calculated previously mem.Resize(newMemSize.Uint64()) + // Add a log message + self.log(pc, op, context.Gas, cost, mem, stack, context, nil) switch op { case ADD: @@ -783,15 +784,13 @@ func (self *Vm) RunPrecompiled(p *PrecompiledAccount, input []byte, context *Con return context.Return(ret), nil } else { - tmp := new(big.Int).Set(context.Gas) - - return nil, OOG(gas, tmp) + return nil, OutOfGasError{} } } // log emits a log event to the environment for each opcode encountered. This is not to be confused with the // LOG* opcode. -func (self *Vm) log(pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, stack *stack, context *Context) { +func (self *Vm) log(pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, stack *stack, context *Context, err error) { if Debug { mem := make([]byte, len(memory.Data())) copy(mem, memory.Data()) @@ -804,7 +803,7 @@ func (self *Vm) log(pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, st storage[common.BytesToHash(k)] = v }) - self.env.AddStructLog(StructLog{pc, op, new(big.Int).Set(gas), cost, mem, stck, storage}) + self.env.AddStructLog(StructLog{pc, op, new(big.Int).Set(gas), cost, mem, stck, storage, err}) } } -- cgit v1.2.3