Skip to content

Commit

Permalink
Merge pull request ethereum#1964 from obscuren/evm-runtime
Browse files Browse the repository at this point in the history
core/vm/runtime: added simple execution runtime
  • Loading branch information
obscuren committed Nov 18, 2015
2 parents 4a93840 + 1372b99 commit 23f42d9
Show file tree
Hide file tree
Showing 5 changed files with 399 additions and 0 deletions.
18 changes: 18 additions & 0 deletions core/vm/runtime/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright 2014 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.

// Package runtime provides a basic execution model for executing EVM code.
package runtime
106 changes: 106 additions & 0 deletions core/vm/runtime/env.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// Copyright 2014 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.

package runtime

import (
"math/big"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/vm"
)

// Env is a basic runtime environment required for running the EVM.
type Env struct {
depth int
state *state.StateDB

origin common.Address
coinbase common.Address

number *big.Int
time *big.Int
difficulty *big.Int
gasLimit *big.Int

logs []vm.StructLog

getHashFn func(uint64) common.Hash
}

// NewEnv returns a new vm.Environment
func NewEnv(cfg *Config, state *state.StateDB) vm.Environment {
return &Env{
state: state,
origin: cfg.Origin,
coinbase: cfg.Coinbase,
number: cfg.BlockNumber,
time: cfg.Time,
difficulty: cfg.Difficulty,
gasLimit: cfg.GasLimit,
}
}

func (self *Env) StructLogs() []vm.StructLog {
return self.logs
}

func (self *Env) AddStructLog(log vm.StructLog) {
self.logs = append(self.logs, log)
}

func (self *Env) Origin() common.Address { return self.origin }
func (self *Env) BlockNumber() *big.Int { return self.number }
func (self *Env) Coinbase() common.Address { return self.coinbase }
func (self *Env) Time() *big.Int { return self.time }
func (self *Env) Difficulty() *big.Int { return self.difficulty }
func (self *Env) Db() vm.Database { return self.state }
func (self *Env) GasLimit() *big.Int { return self.gasLimit }
func (self *Env) VmType() vm.Type { return vm.StdVmTy }
func (self *Env) GetHash(n uint64) common.Hash {
return self.getHashFn(n)
}
func (self *Env) AddLog(log *vm.Log) {
self.state.AddLog(log)
}
func (self *Env) Depth() int { return self.depth }
func (self *Env) SetDepth(i int) { self.depth = i }
func (self *Env) CanTransfer(from common.Address, balance *big.Int) bool {
return self.state.GetBalance(from).Cmp(balance) >= 0
}
func (self *Env) MakeSnapshot() vm.Database {
return self.state.Copy()
}
func (self *Env) SetSnapshot(copy vm.Database) {
self.state.Set(copy.(*state.StateDB))
}

func (self *Env) Transfer(from, to vm.Account, amount *big.Int) {
core.Transfer(from, to, amount)
}

func (self *Env) Call(caller vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
return core.Call(self, caller, addr, data, gas, price, value)
}
func (self *Env) CallCode(caller vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
return core.CallCode(self, caller, addr, data, gas, price, value)
}

func (self *Env) Create(caller vm.ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) {
return core.Create(self, caller, data, gas, price, value)
}
121 changes: 121 additions & 0 deletions core/vm/runtime/runtime.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
// Copyright 2014 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.

package runtime

import (
"math/big"
"time"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
)

// Config is a basic type specifing certain configuration flags for running
// the EVM.
type Config struct {
Difficulty *big.Int
Origin common.Address
Coinbase common.Address
BlockNumber *big.Int
Time *big.Int
GasLimit *big.Int
GasPrice *big.Int
Value *big.Int
DisableJit bool // "disable" so it's enabled by default
Debug bool

GetHashFn func(n uint64) common.Hash
}

// sets defaults on the config
func setDefaults(cfg *Config) {
if cfg.Difficulty == nil {
cfg.Difficulty = new(big.Int)
}
if cfg.Time == nil {
cfg.Time = big.NewInt(time.Now().Unix())
}
if cfg.GasLimit == nil {
cfg.GasLimit = new(big.Int).Set(common.MaxBig)
}
if cfg.GasPrice == nil {
cfg.GasPrice = new(big.Int)
}
if cfg.Value == nil {
cfg.Value = new(big.Int)
}
if cfg.BlockNumber == nil {
cfg.BlockNumber = new(big.Int)
}
if cfg.GetHashFn == nil {
cfg.GetHashFn = func(n uint64) common.Hash {
return common.BytesToHash(crypto.Sha3([]byte(new(big.Int).SetUint64(n).String())))
}
}
}

// Execute executes the code using the input as call data during the execution.
// It returns the EVM's return value, the new state and an error if it failed.
//
// Executes sets up a in memory, temporarily, environment for the execution of
// the given code. It enabled the JIT by default and make sure that it's restored
// to it's original state afterwards.
func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
if cfg == nil {
cfg = new(Config)
}
setDefaults(cfg)

// defer the call to setting back the original values
defer func(debug, forceJit, enableJit bool) {
vm.Debug = debug
vm.ForceJit = forceJit
vm.EnableJit = enableJit
}(vm.Debug, vm.ForceJit, vm.EnableJit)

vm.ForceJit = !cfg.DisableJit
vm.EnableJit = !cfg.DisableJit
vm.Debug = cfg.Debug

var (
db, _ = ethdb.NewMemDatabase()
statedb, _ = state.New(common.Hash{}, db)
vmenv = NewEnv(cfg, statedb)
sender = statedb.CreateAccount(cfg.Origin)
receiver = statedb.CreateAccount(common.StringToAddress("contract"))
)
// set the receiver's (the executing contract) code for execution.
receiver.SetCode(code)

// Call the code with the given configuration.
ret, err := vmenv.Call(
sender,
receiver.Address(),
input,
cfg.GasLimit,
cfg.GasPrice,
cfg.Value,
)

if cfg.Debug {
vm.StdErrFormat(vmenv.StructLogs())
}
return ret, statedb, err
}
34 changes: 34 additions & 0 deletions core/vm/runtime/runtime_example_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.

package runtime_test

import (
"fmt"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/vm/runtime"
)

func ExampleExecute() {
ret, _, err := runtime.Execute(common.Hex2Bytes("6060604052600a8060106000396000f360606040526008565b00"), nil, nil)
if err != nil {
fmt.Println(err)
}
fmt.Println(ret)
// Output:
// [96 96 96 64 82 96 8 86 91 0]
}
Loading

0 comments on commit 23f42d9

Please sign in to comment.