|
|
@@ -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
|
|
|
-}
|