aboutsummaryrefslogtreecommitdiffstats
path: root/core/vm/vm.go
diff options
context:
space:
mode:
Diffstat (limited to 'core/vm/vm.go')
-rw-r--r--core/vm/vm.go109
1 files changed, 59 insertions, 50 deletions
diff --git a/core/vm/vm.go b/core/vm/vm.go
index 26df8aef4..f72c853a2 100644
--- a/core/vm/vm.go
+++ b/core/vm/vm.go
@@ -28,24 +28,54 @@ import (
"github.com/ethereum/go-ethereum/params"
)
-// Vm is an EVM and implements VirtualMachine
-type Vm struct {
+// Config are the configuration options for the EVM
+type Config struct {
+ Debug bool
+ EnableJit bool
+ ForceJit bool
+ Logger LogConfig
+}
+
+// EVM is used to run Ethereum based contracts and will utilise the
+// passed environment to query external sources for state information.
+// The EVM will run the byte code VM or JIT VM based on the passed
+// configuration.
+type EVM struct {
env Environment
jumpTable vmJumpTable
+ cfg *Config
+
+ logger *Logger
}
-func EVM(env Environment) *Vm {
- return &Vm{env: env, jumpTable: newJumpTable(env.BlockNumber())}
+// New returns a new instance of the EVM.
+func New(env Environment, cfg *Config) *EVM {
+ // initialise a default config if none is present
+ if cfg == nil {
+ cfg = new(Config)
+ }
+
+ var logger *Logger
+ if cfg.Debug {
+ logger = newLogger(cfg.Logger, env)
+ }
+
+ return &EVM{
+ env: env,
+ jumpTable: newJumpTable(env.BlockNumber()),
+ cfg: cfg,
+ logger: logger,
+ }
}
// Run loops and evaluates the contract's code with the given input data
-func (self *Vm) Run(contract *Contract, input []byte) (ret []byte, err error) {
- self.env.SetDepth(self.env.Depth() + 1)
- defer self.env.SetDepth(self.env.Depth() - 1)
+func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) {
+ evm.env.SetDepth(evm.env.Depth() + 1)
+ defer evm.env.SetDepth(evm.env.Depth() - 1)
if contract.CodeAddr != nil {
if p := Precompiled[contract.CodeAddr.Str()]; p != nil {
- return self.RunPrecompiled(p, input, contract)
+ return evm.RunPrecompiled(p, input, contract)
}
}
@@ -58,21 +88,21 @@ func (self *Vm) Run(contract *Contract, input []byte) (ret []byte, err error) {
codehash = crypto.Keccak256Hash(contract.Code) // codehash is used when doing jump dest caching
program *Program
)
- if EnableJit {
+ if evm.cfg.EnableJit {
// If the JIT is enabled check the status of the JIT program,
// if it doesn't exist compile a new program in a separate
// goroutine or wait for compilation to finish if the JIT is
// forced.
switch GetProgramStatus(codehash) {
case progReady:
- return RunProgram(GetProgram(codehash), self.env, contract, input)
+ return RunProgram(GetProgram(codehash), evm.env, contract, input)
case progUnknown:
- if ForceJit {
+ if evm.cfg.ForceJit {
// Create and compile program
program = NewProgram(contract.Code)
perr := CompileProgram(program)
if perr == nil {
- return RunProgram(program, self.env, contract, input)
+ return RunProgram(program, evm.env, contract, input)
}
glog.V(logger.Info).Infoln("error compiling program", err)
} else {
@@ -95,10 +125,10 @@ func (self *Vm) Run(contract *Contract, input []byte) (ret []byte, err error) {
code = contract.Code
instrCount = 0
- op OpCode // current opcode
- mem = NewMemory() // bound memory
- stack = newstack() // local stack
- statedb = self.env.Db() // current state
+ op OpCode // current opcode
+ mem = NewMemory() // bound memory
+ stack = newstack() // local stack
+ statedb = evm.env.Db() // 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. Practically much less so feasible.
pc = uint64(0) // program counter
@@ -123,8 +153,8 @@ func (self *Vm) Run(contract *Contract, input []byte) (ret []byte, err error) {
// 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 err != nil {
- self.log(pc, op, contract.Gas, cost, mem, stack, contract, err)
+ if err != nil && evm.cfg.Debug {
+ evm.logger.captureState(pc, op, contract.Gas, cost, mem, stack, contract, err)
}
}()
@@ -143,7 +173,7 @@ func (self *Vm) Run(contract *Contract, input []byte) (ret []byte, err error) {
// move execution
fmt.Println("moved", it)
glog.V(logger.Info).Infoln("Moved execution to JIT")
- return runProgram(program, pc, mem, stack, self.env, contract, input)
+ return runProgram(program, pc, mem, stack, evm.env, contract, input)
}
}
*/
@@ -151,7 +181,7 @@ func (self *Vm) Run(contract *Contract, input []byte) (ret []byte, err error) {
// Get the memory location of pc
op = contract.GetOp(pc)
// calculate the new memory size and gas price for the current executing opcode
- newMemSize, cost, err = calculateGasAndSize(self.env, contract, caller, op, statedb, mem, stack)
+ newMemSize, cost, err = calculateGasAndSize(evm.env, contract, caller, op, statedb, mem, stack)
if err != nil {
return nil, err
}
@@ -165,14 +195,17 @@ func (self *Vm) Run(contract *Contract, input []byte) (ret []byte, err error) {
// Resize the memory calculated previously
mem.Resize(newMemSize.Uint64())
// Add a log message
- self.log(pc, op, contract.Gas, cost, mem, stack, contract, nil)
- if opPtr := self.jumpTable[op]; opPtr.valid {
+ if evm.cfg.Debug {
+ evm.logger.captureState(pc, op, contract.Gas, cost, mem, stack, contract, nil)
+ }
+
+ if opPtr := evm.jumpTable[op]; opPtr.valid {
if opPtr.fn != nil {
- opPtr.fn(instruction{}, &pc, self.env, contract, mem, stack)
+ opPtr.fn(instruction{}, &pc, evm.env, contract, mem, stack)
} else {
switch op {
case PC:
- opPc(instruction{data: new(big.Int).SetUint64(pc)}, &pc, self.env, contract, mem, stack)
+ opPc(instruction{data: new(big.Int).SetUint64(pc)}, &pc, evm.env, contract, mem, stack)
case JUMP:
if err := jump(pc, stack.pop()); err != nil {
return nil, err
@@ -195,7 +228,7 @@ func (self *Vm) Run(contract *Contract, input []byte) (ret []byte, err error) {
return ret, nil
case SUICIDE:
- opSuicide(instruction{}, nil, self.env, contract, mem, stack)
+ opSuicide(instruction{}, nil, evm.env, contract, mem, stack)
fallthrough
case STOP: // Stop the contract
@@ -347,7 +380,7 @@ func calculateGasAndSize(env Environment, contract *Contract, caller ContractRef
}
// RunPrecompile runs and evaluate the output of a precompiled contract defined in contracts.go
-func (self *Vm) RunPrecompiled(p *PrecompiledAccount, input []byte, contract *Contract) (ret []byte, err error) {
+func (evm *EVM) RunPrecompiled(p *PrecompiledAccount, input []byte, contract *Contract) (ret []byte, err error) {
gas := p.Gas(len(input))
if contract.UseGas(gas) {
ret = p.Call(input)
@@ -357,27 +390,3 @@ func (self *Vm) RunPrecompiled(p *PrecompiledAccount, input []byte, contract *Co
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, contract *Contract, err error) {
- if Debug || GenerateStructLogs {
- mem := make([]byte, len(memory.Data()))
- copy(mem, memory.Data())
-
- stck := make([]*big.Int, len(stack.Data()))
- for i, item := range stack.Data() {
- stck[i] = new(big.Int).Set(item)
- }
- storage := make(map[common.Hash][]byte)
- contract.self.EachStorage(func(k, v []byte) {
- storage[common.BytesToHash(k)] = v
- })
- self.env.AddStructLog(StructLog{pc, op, new(big.Int).Set(gas), cost, mem, stck, storage, err})
- }
-}
-
-// Environment returns the current workable state of the VM
-func (self *Vm) Env() Environment {
- return self.env
-}