diff options
Diffstat (limited to 'core/vm/vm.go')
-rw-r--r-- | core/vm/vm.go | 109 |
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 -} |