From 287f99089181c1eaa6f25a6b531e476b631a201a Mon Sep 17 00:00:00 2001
From: obscuren <geffobscura@gmail.com>
Date: Fri, 12 Jun 2015 13:35:14 +0200
Subject: core/vm: Improved error reporting for trace logging

---
 core/vm/environment.go |  1 +
 core/vm/errors.go      | 12 +++------
 core/vm/logger.go      |  9 +++++--
 core/vm/vm.go          | 69 +++++++++++++++++++++++++-------------------------
 4 files changed, 45 insertions(+), 46 deletions(-)

(limited to 'core/vm')

diff --git a/core/vm/environment.go b/core/vm/environment.go
index 5c04e7022..c103049a2 100644
--- a/core/vm/environment.go
+++ b/core/vm/environment.go
@@ -45,6 +45,7 @@ type StructLog struct {
 	Memory  []byte
 	Stack   []*big.Int
 	Storage map[common.Hash][]byte
+	Err     error
 }
 
 type Account interface {
diff --git a/core/vm/errors.go b/core/vm/errors.go
index fc3459de0..799eb6797 100644
--- a/core/vm/errors.go
+++ b/core/vm/errors.go
@@ -2,20 +2,14 @@ package vm
 
 import (
 	"fmt"
+
 	"github.com/ethereum/go-ethereum/params"
-	"math/big"
 )
 
-type OutOfGasError struct {
-	req, has *big.Int
-}
-
-func OOG(req, has *big.Int) OutOfGasError {
-	return OutOfGasError{req, has}
-}
+type OutOfGasError struct{}
 
 func (self OutOfGasError) Error() string {
-	return fmt.Sprintf("out of gas! require %v, have %v", self.req, self.has)
+	return "Out Of Gas"
 }
 
 func IsOOGErr(err error) bool {
diff --git a/core/vm/logger.go b/core/vm/logger.go
index 061e3745b..0e2a417ae 100644
--- a/core/vm/logger.go
+++ b/core/vm/logger.go
@@ -9,9 +9,14 @@ import (
 )
 
 func StdErrFormat(logs []StructLog) {
-	fmt.Fprintf(os.Stderr, "VM Stats %d ops\n", len(logs))
+	fmt.Fprintf(os.Stderr, "VM STAT %d OPs\n", len(logs))
 	for _, log := range logs {
-		fmt.Fprintf(os.Stderr, "PC %08d: %s GAS: %v COST: %v\n", log.Pc, log.Op, log.Gas, log.GasCost)
+		fmt.Fprintf(os.Stderr, "PC %08d: %s GAS: %v COST: %v", log.Pc, log.Op, log.Gas, log.GasCost)
+		if log.Err != nil {
+			fmt.Fprintf(os.Stderr, " ERROR: %v", log.Err)
+		}
+		fmt.Fprintf(os.Stderr, "\n")
+
 		fmt.Fprintln(os.Stderr, "STACK =", len(log.Stack))
 
 		for i := len(log.Stack) - 1; i >= 0; i-- {
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