core: Added EVM configuration options
The EVM is now initialised with an additional configured object that allows you to turn on debugging options.
This commit is contained in:
committed by
Jeffrey Wilcke
parent
342ae7ce7d
commit
14013372ae
113
core/vm/vm.go
113
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 {
|
||||
env Environment
|
||||
jumpTable vmJumpTable
|
||||
// Config are the configuration options for the EVM
|
||||
type Config struct {
|
||||
Debug bool
|
||||
EnableJit bool
|
||||
ForceJit bool
|
||||
Logger LogConfig
|
||||
}
|
||||
|
||||
func EVM(env Environment) *Vm {
|
||||
return &Vm{env: env, jumpTable: newJumpTable(env.BlockNumber())}
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
Reference in New Issue
Block a user