diff --git a/cmd/evm/main.go b/cmd/evm/main.go
index b1479d4f35..8150180908 100644
--- a/cmd/evm/main.go
+++ b/cmd/evm/main.go
@@ -28,8 +28,8 @@ import (
"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/types"
"github.com/ethereum/go-ethereum/core/vm"
+ "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/logger/glog"
"gopkg.in/urfave/cli.v1"
@@ -44,33 +44,55 @@ var (
Name: "debug",
Usage: "output full trace logs",
}
- ForceJitFlag = cli.BoolFlag{
- Name: "forcejit",
- Usage: "forces jit compilation",
- }
- DisableJitFlag = cli.BoolFlag{
- Name: "nojit",
- Usage: "disabled jit compilation",
- }
CodeFlag = cli.StringFlag{
Name: "code",
Usage: "EVM code",
}
GasFlag = cli.StringFlag{
Name: "gas",
- Usage: "gas limit for the evm",
+ Usage: "set the gas limit for the EVM execution",
Value: "10000000000",
}
PriceFlag = cli.StringFlag{
Name: "price",
- Usage: "price set for the evm",
+ Usage: "set the price for the EVM execution",
Value: "0",
}
ValueFlag = cli.StringFlag{
Name: "value",
- Usage: "value set for the evm",
+ Usage: "set the value for the EVM execution",
Value: "0",
}
+ SenderFlag = cli.StringFlag{
+ Name: "origin",
+ Usage: "set the origin for the EVM execution",
+ Value: "0x",
+ }
+ CoinbaseFlag = cli.StringFlag{
+ Name: "coinbase",
+ Usage: "coinbase set for the evm",
+ Value: "0x",
+ }
+ BlockNumberFlag = cli.StringFlag{
+ Name: "blocknumber",
+ Usage: "set the block number for the EVM execution",
+ Value: "1",
+ }
+ BlockTimeFlag = cli.StringFlag{
+ Name: "blocktime",
+ Usage: "set the block time for the EVM execution",
+ Value: "1",
+ }
+ BlockDifficultyFlag = cli.StringFlag{
+ Name: "difficulty",
+ Usage: "set the block difficulty for the EVM execution",
+ Value: "1",
+ }
+ GasLimitFlag = cli.StringFlag{
+ Name: "gaslimit",
+ Usage: "sets the gas limit for the EVM execution",
+ Value: "1",
+ }
DumpFlag = cli.BoolFlag{
Name: "dump",
Usage: "dumps the state after the run",
@@ -98,8 +120,6 @@ func init() {
CreateFlag,
DebugFlag,
VerbosityFlag,
- ForceJitFlag,
- DisableJitFlag,
SysStatFlag,
CodeFlag,
GasFlag,
@@ -107,6 +127,11 @@ func init() {
ValueFlag,
DumpFlag,
InputFlag,
+ GasLimitFlag,
+ CoinbaseFlag,
+ SenderFlag,
+ BlockNumberFlag,
+ BlockTimeFlag,
}
app.Action = run
}
@@ -116,19 +141,46 @@ func run(ctx *cli.Context) error {
glog.SetV(ctx.GlobalInt(VerbosityFlag.Name))
logger := vm.NewStructLogger(nil)
- vmenv := NewEnv(common.StringToAddress("evmuser"), common.Big(ctx.GlobalString(ValueFlag.Name)), common.Big(ctx.GlobalString(PriceFlag.Name)), vm.Config{
- Debug: ctx.GlobalBool(DebugFlag.Name),
- ForceJit: ctx.GlobalBool(ForceJitFlag.Name),
- EnableJit: !ctx.GlobalBool(DisableJitFlag.Name),
- Tracer: logger,
- })
- sender := vmenv.state.CreateAccount(common.StringToAddress("sender"))
- tstart := time.Now()
+ db, err := ethdb.NewMemDatabase()
+ if err != nil {
+ panic(err)
+ }
+ st, err := state.New(common.Hash{}, db)
+ if err != nil {
+ panic(err)
+ }
+ backend := &core.EVMBackend{
+ GetHashFn: func(n uint64) common.Hash {
+ return common.BytesToHash(crypto.Keccak256([]byte(big.NewInt(int64(n)).String())))
+ },
+ State: st,
+ }
+ context := vm.Context{
+ CallContext: core.EVMCallContext{core.CanTransfer, core.Transfer},
+ Origin: common.StringToAddress(ctx.GlobalString(SenderFlag.Name)),
+ Coinbase: common.StringToAddress(ctx.GlobalString(CoinbaseFlag.Name)),
+ BlockNumber: common.String2Big(ctx.GlobalString(BlockNumberFlag.Name)),
+ Time: common.String2Big(ctx.GlobalString(BlockTimeFlag.Name)),
+ Difficulty: common.String2Big(ctx.GlobalString(BlockDifficultyFlag.Name)),
+ GasLimit: common.String2Big(ctx.GlobalString(GasLimitFlag.Name)),
+ GasPrice: common.String2Big(ctx.GlobalString(PriceFlag.Name)),
+ }
+
+ vmenv := vm.NewEnvironment(
+ context,
+ backend,
+ ruleSet{},
+ vm.Config{
+ Debug: ctx.GlobalBool(DebugFlag.Name),
+ Tracer: logger,
+ },
+ )
var (
- ret []byte
- err error
+ ret []byte
+ tstart = time.Now()
+ sender = st.CreateAccount(context.Origin)
)
if ctx.GlobalBool(CreateFlag.Name) {
@@ -140,7 +192,7 @@ func run(ctx *cli.Context) error {
common.Big(ctx.GlobalString(ValueFlag.Name)),
)
} else {
- receiver := vmenv.state.CreateAccount(common.StringToAddress("receiver"))
+ receiver := st.CreateAccount(common.StringToAddress("receiver"))
receiver.SetCode(common.Hex2Bytes(ctx.GlobalString(CodeFlag.Name)))
ret, err = vmenv.Call(
sender,
@@ -153,8 +205,8 @@ func run(ctx *cli.Context) error {
vmdone := time.Since(tstart)
if ctx.GlobalBool(DumpFlag.Name) {
- state.Commit(vmenv.state)
- fmt.Println(string(vmenv.state.Dump()))
+ state.Commit(st)
+ fmt.Println(string(st.Dump()))
}
vm.StdErrFormat(logger.StructLogs())
@@ -186,99 +238,7 @@ func main() {
}
}
-type VMEnv struct {
- state *state.State
- block *types.Block
-
- transactor *common.Address
- value *big.Int
-
- depth int
- Gas, price *big.Int
- time *big.Int
- logs []vm.StructLog
-
- evm *vm.EVM
-}
-
-func NewEnv(transactor common.Address, value, price *big.Int, cfg vm.Config) *VMEnv {
- db, err := ethdb.NewMemDatabase()
- if err != nil {
- panic(err)
- }
- st, err := state.New(common.Hash{}, db)
- if err != nil {
- panic(err)
- }
-
- env := &VMEnv{
- state: st,
- transactor: &transactor,
- value: value,
- price: price,
- time: big.NewInt(time.Now().Unix()),
- }
-
- env.evm = vm.New(env, cfg)
- return env
-}
-
// ruleSet implements vm.RuleSet and will always default to the homestead rule set.
type ruleSet struct{}
func (ruleSet) IsHomestead(*big.Int) bool { return true }
-
-func (self *VMEnv) RuleSet() vm.RuleSet { return ruleSet{} }
-func (self *VMEnv) Vm() vm.Vm { return self.evm }
-func (self *VMEnv) Db() vm.Database { return self.state }
-func (self *VMEnv) Origin() common.Address { return *self.transactor }
-func (self *VMEnv) BlockNumber() *big.Int { return common.Big0 }
-func (self *VMEnv) Coinbase() common.Address { return *self.transactor }
-func (self *VMEnv) Time() *big.Int { return self.time }
-func (self *VMEnv) Difficulty() *big.Int { return common.Big1 }
-func (self *VMEnv) BlockHash() []byte { return make([]byte, 32) }
-func (self *VMEnv) Value() *big.Int { return self.value }
-func (self *VMEnv) GasLimit() *big.Int { return big.NewInt(1000000000) }
-func (self *VMEnv) GasPrice() *big.Int { return big.NewInt(1000000000) }
-func (self *VMEnv) VmType() vm.Type { return vm.StdVmTy }
-func (self *VMEnv) Depth() int { return 0 }
-func (self *VMEnv) SetDepth(i int) { self.depth = i }
-func (self *VMEnv) GetHash(n uint64) common.Hash {
- if self.block.Number().Cmp(big.NewInt(int64(n))) == 0 {
- return self.block.Hash()
- }
- return common.Hash{}
-}
-func (self *VMEnv) ForkState() vm.Database {
- self.state = state.Fork(self.state)
- return self.state
-}
-func (self *VMEnv) SetState(st vm.Database) {
- self.state = st.(*state.State)
-}
-func (self *VMEnv) AddLog(log *vm.Log) {
- self.state.AddLog(log)
-}
-func (self *VMEnv) CanTransfer(from common.Address, balance *big.Int) bool {
- return self.state.GetBalance(from).Cmp(balance) >= 0
-}
-func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) {
- core.Transfer(from, to, amount)
-}
-
-func (self *VMEnv) Call(caller vm.ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) {
- self.Gas = gas
- return core.Call(self, caller, addr, data, gas, value)
-}
-
-func (self *VMEnv) CallCode(caller vm.ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) {
- return core.CallCode(self, caller, addr, data, gas, value)
-}
-
-func (self *VMEnv) DelegateCall(caller vm.ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error) {
- return core.DelegateCall(self, caller, addr, data, gas)
-}
-
-func (self *VMEnv) Create(caller vm.ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error) {
- return core.Create(self, caller, data, gas, value)
-}
diff --git a/common/registrar/ethreg/api.go b/common/registrar/ethreg/api.go
index 6dd0ef46f3..32fef80528 100644
--- a/common/registrar/ethreg/api.go
+++ b/common/registrar/ethreg/api.go
@@ -194,7 +194,16 @@ func (be *registryAPIBackend) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr
}
header := be.bc.CurrentBlock().Header()
- vmenv := core.NewEnv(statedb, be.config, be.bc, msg, header, vm.Config{})
+
+ backend := &core.EVMBackend{
+ GetHashFn: core.GetHashFn(header.ParentHash, be.bc),
+ State: statedb,
+ }
+
+ context := core.ToEVMContext(be.config, msg, header)
+ vmenv := vm.NewEnvironment(context, backend, be.config, vm.Config{})
+
+ //vmenv := vm.NewEnvironment(false, statedb, be.config, be.bc, msg, header, vm.Config{})
gp := new(core.GasPool).AddGas(common.MaxBig)
res, gas, err := core.ApplyMessage(vmenv, msg, gp)
diff --git a/core/evm.go b/core/evm.go
new file mode 100644
index 0000000000..61ef82d190
--- /dev/null
+++ b/core/evm.go
@@ -0,0 +1,97 @@
+// 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 .
+
+package core
+
+import (
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/state"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/core/vm"
+)
+
+// ToEVMContext creates a new context for use in the EVM.
+func ToEVMContext(config *ChainConfig, msg Message, header *types.Header) vm.Context {
+ var from common.Address
+ if config.IsHomestead(header.Number) {
+ from, _ = msg.From()
+ } else {
+ from, _ = msg.FromFrontier()
+ }
+
+ return vm.Context{
+ CallContext: EVMCallContext{CanTransfer, Transfer},
+ Origin: from,
+ Coinbase: header.Coinbase,
+ BlockNumber: new(big.Int).Set(header.Number),
+ Time: new(big.Int).Set(header.Time),
+ Difficulty: new(big.Int).Set(header.Difficulty),
+ GasLimit: new(big.Int).Set(header.GasLimit),
+ GasPrice: new(big.Int).Set(msg.GasPrice()),
+ }
+}
+
+// GetHashFn returns a function for which the VM env can query block hashes through
+// up to the limit defined by the Yellow Paper and uses the given block chain
+// to query for information.
+func GetHashFn(ref common.Hash, chain *BlockChain) func(n uint64) common.Hash {
+ return func(n uint64) common.Hash {
+ for block := chain.GetBlockByHash(ref); block != nil; block = chain.GetBlock(block.ParentHash(), block.NumberU64()-1) {
+ if block.NumberU64() == n {
+ return block.Hash()
+ }
+ }
+
+ return common.Hash{}
+ }
+}
+
+// CanTransfer checks wether there are enough funds in the address' account to make a transfer.
+func CanTransfer(db vm.Database, addr common.Address, amount *big.Int) bool {
+ return db.GetBalance(addr).Cmp(amount) >= 0
+}
+
+// Transfer subtracts amount from sender and adds amount to recipient using the given Db
+func Transfer(db vm.Database, sender, recipient common.Address, amount *big.Int) {
+ db.SubBalance(sender, amount)
+ db.AddBalance(recipient, amount)
+}
+
+// EVMBackend implements vm.Backend and provides an interface to keep track of the current
+// state.
+type EVMBackend struct {
+ GetHashFn func(uint64) common.Hash // getHashFn callback is used to retrieve block hashes
+ State *state.State
+}
+
+// Fork forks the state and returns it.
+func (b *EVMBackend) Fork() vm.Database {
+ b.State = state.Fork(b.State)
+ return b.State
+}
+
+// Get returns the state
+func (b *EVMBackend) Get() vm.Database { return b.State }
+
+// Set sets the current state
+func (b *EVMBackend) Set(db vm.Database) { b.State = db.(*state.State) }
+
+// GetHash returns the canonical hash referenced by the depth
+func (b *EVMBackend) GetHash(n uint64) common.Hash {
+ return b.GetHashFn(n)
+}
diff --git a/core/execution.go b/core/execution.go
index d8ca0a2953..d887e071be 100644
--- a/core/execution.go
+++ b/core/execution.go
@@ -25,31 +25,36 @@ import (
"github.com/ethereum/go-ethereum/params"
)
+type EVMCallContext struct {
+ CanTransfer func(db vm.Database, addr common.Address, amount *big.Int) bool
+ Transfer func(db vm.Database, sender, recipient common.Address, amount *big.Int)
+}
+
// Call executes within the given contract
-func Call(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, value *big.Int) (ret []byte, err error) {
- ret, _, err = exec(env, caller, &addr, &addr, input, env.Db().GetCode(addr), gas, value)
+func (c EVMCallContext) Call(env *vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, value *big.Int) (ret []byte, err error) {
+ ret, _, err = c.exec(env, caller, &addr, &addr, input, env.Db().GetCode(addr), gas, value)
return ret, err
}
// CallCode executes the given address' code as the given contract address
-func CallCode(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, value *big.Int) (ret []byte, err error) {
+func (c EVMCallContext) CallCode(env *vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, value *big.Int) (ret []byte, err error) {
callerAddr := caller.Address()
- ret, _, err = exec(env, caller, &callerAddr, &addr, input, env.Db().GetCode(addr), gas, value)
+ ret, _, err = c.exec(env, caller, &callerAddr, &addr, input, env.Db().GetCode(addr), gas, value)
return ret, err
}
// DelegateCall is equivalent to CallCode except that sender and value propagates from parent scope to child scope
-func DelegateCall(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas *big.Int) (ret []byte, err error) {
+func (c EVMCallContext) DelegateCall(env *vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas *big.Int) (ret []byte, err error) {
callerAddr := caller.Address()
- originAddr := env.Origin()
+ originAddr := env.Origin
callerValue := caller.Value()
- ret, _, err = execDelegateCall(env, caller, &originAddr, &callerAddr, &addr, input, env.Db().GetCode(addr), gas, callerValue)
+ ret, _, err = c.execDelegateCall(env, caller, &originAddr, &callerAddr, &addr, input, env.Db().GetCode(addr), gas, callerValue)
return ret, err
}
// Create creates a new contract with the given code
-func Create(env vm.Environment, caller vm.ContractRef, code []byte, gas, value *big.Int) (ret []byte, address common.Address, err error) {
- ret, address, err = exec(env, caller, nil, nil, nil, code, gas, value)
+func (c EVMCallContext) Create(env *vm.Environment, caller vm.ContractRef, code []byte, gas, value *big.Int) (ret []byte, address common.Address, err error) {
+ ret, address, err = c.exec(env, caller, nil, nil, nil, code, gas, value)
// Here we get an error if we run into maximum stack depth,
// See: https://github.com/ethereum/yellowpaper/pull/131
// and YP definitions for CREATE instruction
@@ -59,17 +64,17 @@ func Create(env vm.Environment, caller vm.ContractRef, code []byte, gas, value *
return ret, address, err
}
-func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.Address, input, code []byte, gas, value *big.Int) (ret []byte, addr common.Address, err error) {
+func (c EVMCallContext) exec(env *vm.Environment, caller vm.ContractRef, address, codeAddr *common.Address, input, code []byte, gas, value *big.Int) (ret []byte, addr common.Address, err error) {
evm := env.Vm()
// Depth check execution. Fail if we're trying to execute above the
// limit.
- if env.Depth() > int(params.CallCreateDepth.Int64()) {
+ if env.Depth > int(params.CallCreateDepth.Int64()) {
caller.ReturnGas(gas.Uint64())
return nil, common.Address{}, vm.DepthError
}
- if !env.CanTransfer(caller.Address(), value) {
+ if !c.CanTransfer(env.Db(), caller.Address(), value) {
caller.ReturnGas(gas.Uint64())
return nil, common.Address{}, ValueTransferErr("insufficient funds to transfer value. Req %v, has %v", value, env.Db().GetBalance(caller.Address()))
@@ -87,7 +92,7 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A
snapshotPreTransfer := env.Db()
// preserve the envs state and move to a new state
- env.ForkState()
+ env.Backend.Fork()
var (
from = env.Db().GetAccount(caller.Address())
to vm.Account
@@ -101,7 +106,7 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A
to = env.Db().GetAccount(*address)
}
}
- env.Transfer(from, to, value)
+ c.Transfer(env.Db(), from.Address(), to.Address(), value)
// initialise a new contract and set the code that is to be used by the
// EVM. The contract is a scoped environment for this execution context
@@ -128,26 +133,26 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A
// When an error was returned by the EVM or when setting the creation code
// above we revert to the snapshot and consume any gas remaining. Additionally
// when we're in homestead this also counts for code storage gas errors.
- if err != nil && (env.RuleSet().IsHomestead(env.BlockNumber()) || err != vm.CodeStoreOutOfGasError) {
+ if err != nil && (env.RuleSet().IsHomestead(env.BlockNumber) || err != vm.CodeStoreOutOfGasError) {
contract.UseGas(contract.Gas())
- env.SetState(snapshotPreTransfer)
+ env.Backend.Set(snapshotPreTransfer)
}
return ret, addr, err
}
-func execDelegateCall(env vm.Environment, caller vm.ContractRef, originAddr, toAddr, codeAddr *common.Address, input, code []byte, gas, value *big.Int) (ret []byte, addr common.Address, err error) {
+func (c EVMCallContext) execDelegateCall(env *vm.Environment, caller vm.ContractRef, originAddr, toAddr, codeAddr *common.Address, input, code []byte, gas, value *big.Int) (ret []byte, addr common.Address, err error) {
evm := env.Vm()
// Depth check execution. Fail if we're trying to execute above the
// limit.
- if env.Depth() > int(params.CallCreateDepth.Int64()) {
+ if env.Depth > int(params.CallCreateDepth.Int64()) {
caller.ReturnGas(gas.Uint64())
return nil, common.Address{}, vm.DepthError
}
snapshot := env.Db()
- env.ForkState()
+ env.Backend.Fork()
var to vm.Account
if !env.Db().Exist(*toAddr) {
@@ -165,14 +170,8 @@ func execDelegateCall(env vm.Environment, caller vm.ContractRef, originAddr, toA
if err != nil {
contract.UseGas(contract.Gas())
- env.SetState(snapshot)
+ env.Backend.Set(snapshot)
}
return ret, addr, err
}
-
-// generic transfer method
-func Transfer(from, to vm.Account, amount *big.Int) {
- from.SubBalance(amount)
- to.AddBalance(amount)
-}
diff --git a/core/state/dump.go b/core/state/dump.go
index b49f248476..4fe0e9461e 100644
--- a/core/state/dump.go
+++ b/core/state/dump.go
@@ -65,6 +65,24 @@ func (self *State) RawDump() World {
}
world.Accounts[common.Bytes2Hex(addr)] = account
}
+ for addr, stateObject := range self.StateObjects {
+ account := Account{
+ Balance: stateObject.balance.String(),
+ Nonce: stateObject.nonce,
+ Root: common.Bytes2Hex(stateObject.Root()),
+ CodeHash: common.Bytes2Hex(stateObject.codeHash),
+ Code: common.Bytes2Hex(stateObject.Code()),
+ Storage: make(map[string]string),
+ }
+ storageIt := stateObject.trie.Iterator()
+ for storageIt.Next() {
+ account.Storage[common.Bytes2Hex(self.Trie.GetKey(storageIt.Key))] = common.Bytes2Hex(storageIt.Value)
+ }
+ for storageAddr, value := range stateObject.storage {
+ account.Storage[storageAddr.Hex()] = value.Hex()
+ }
+ world.Accounts[addr.Hex()] = account
+ }
return world
}
diff --git a/core/state/state.go b/core/state/state.go
index adda2f603e..21d8988f5f 100644
--- a/core/state/state.go
+++ b/core/state/state.go
@@ -133,6 +133,13 @@ func (s *State) CreateStateObject(address common.Address) *StateObject {
return stateObject
}
+func (s *State) SubBalance(address common.Address, amount *big.Int) {
+ stateObject := s.GetOrNewStateObject(address)
+ if stateObject != nil {
+ stateObject.SubBalance(amount)
+ }
+}
+
func (s *State) AddBalance(address common.Address, amount *big.Int) {
stateObject := s.GetOrNewStateObject(address)
if stateObject != nil {
diff --git a/core/state_processor.go b/core/state_processor.go
index 909c296206..e6f569c60e 100644
--- a/core/state_processor.go
+++ b/core/state_processor.go
@@ -99,28 +99,37 @@ func (p *StateProcessor) Process(block *types.Block, st *state.State, cfg vm.Con
// ApplyTransactions returns the generated receipts and vm logs during the
// execution of the state transition phase.
func ApplyTransaction(config *ChainConfig, bc *BlockChain, gp *GasPool, st *state.State, header *types.Header, tx *types.Transaction, usedGas *big.Int, cfg vm.Config) (*state.State, *types.Receipt, vm.Logs, *big.Int, error) {
- env := NewEnv(st, config, bc, tx, header, cfg)
+ //env := NewEnv(st, config, bc, tx, header, cfg)
+
+ backend := &EVMBackend{
+ GetHashFn: GetHashFn(header.ParentHash, bc),
+ State: st,
+ }
+ context := ToEVMContext(config, tx, header)
+
+ env := vm.NewEnvironment(context, backend, config, cfg)
_, gas, err := ApplyMessage(env, tx, gp)
if err != nil {
- return env.state, nil, nil, nil, err
+ return env.Db().(*state.State), nil, nil, nil, err
}
+ st = env.Db().(*state.State)
// Update the state with pending changes
usedGas.Add(usedGas, gas)
- receipt := types.NewReceipt(state.IntermediateRoot(env.state).Bytes(), usedGas)
+ receipt := types.NewReceipt(state.IntermediateRoot(st).Bytes(), usedGas)
receipt.TxHash = tx.Hash()
receipt.GasUsed = new(big.Int).Set(gas)
if MessageCreatesContract(tx) {
from, _ := tx.From()
receipt.ContractAddress = crypto.CreateAddress(from, tx.Nonce())
}
- receipt.Logs = env.state.Logs()
+ receipt.Logs = st.Logs()
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
glog.V(logger.Debug).Infoln(receipt)
- return env.state, receipt, receipt.Logs, gas, err
+ return st, receipt, receipt.Logs, gas, err
}
// AccumulateRewards credits the coinbase of the given block with the
diff --git a/core/state_transition.go b/core/state_transition.go
index bd3dac8846..864adb3b5a 100644
--- a/core/state_transition.go
+++ b/core/state_transition.go
@@ -56,7 +56,7 @@ type StateTransition struct {
value *big.Int
data []byte
- env vm.Environment
+ env *vm.Environment
}
// Message represents a message sent to a contract.
@@ -105,7 +105,7 @@ func IntrinsicGas(data []byte, contractCreation, homestead bool) *big.Int {
}
// NewStateTransition initialises and returns a new state transition object.
-func NewStateTransition(env vm.Environment, msg Message, gp *GasPool) *StateTransition {
+func NewStateTransition(env *vm.Environment, msg Message, gp *GasPool) *StateTransition {
return &StateTransition{
gp: gp,
env: env,
@@ -125,7 +125,7 @@ func NewStateTransition(env vm.Environment, msg Message, gp *GasPool) *StateTran
// the gas used (which includes gas refunds) and an error if it failed. An error always
// indicates a core error meaning that the message would always fail for that particular
// state and would never be accepted within a block.
-func ApplyMessage(env vm.Environment, msg Message, gp *GasPool) ([]byte, *big.Int, error) {
+func ApplyMessage(env *vm.Environment, msg Message, gp *GasPool) ([]byte, *big.Int, error) {
st := NewStateTransition(env, msg, gp)
ret, _, gasUsed, err := st.TransitionDb()
@@ -138,7 +138,7 @@ func (self *StateTransition) from() (vm.Account, error) {
err error
st = self.env.Db()
)
- if self.env.RuleSet().IsHomestead(self.env.BlockNumber()) {
+ if self.env.RuleSet().IsHomestead(self.env.BlockNumber) {
f, err = self.msg.From()
} else {
f, err = self.msg.FromFrontier()
@@ -234,7 +234,7 @@ func (self *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *b
msg := self.msg
sender, _ := self.from() // err checked in preCheck
- homestead := self.env.RuleSet().IsHomestead(self.env.BlockNumber())
+ homestead := self.env.RuleSet().IsHomestead(self.env.BlockNumber)
contractCreation := MessageCreatesContract(msg)
// Pay intrinsic gas
if err = self.useGas(IntrinsicGas(self.data, contractCreation, homestead)); err != nil {
@@ -274,7 +274,7 @@ func (self *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *b
requiredGas = new(big.Int).Set(self.gasUsed())
self.refundGas()
- self.env.Db().AddBalance(self.env.Coinbase(), new(big.Int).Mul(self.gasUsed(), self.gasPrice))
+ self.env.Db().AddBalance(self.env.Coinbase, new(big.Int).Mul(self.gasUsed(), self.gasPrice))
return ret, requiredGas, self.gasUsed(), err
}
diff --git a/core/vm/environment.go b/core/vm/environment.go
deleted file mode 100644
index 2abf690b95..0000000000
--- a/core/vm/environment.go
+++ /dev/null
@@ -1,131 +0,0 @@
-// 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 .
-
-package vm
-
-import (
- "math/big"
-
- "github.com/ethereum/go-ethereum/common"
-)
-
-// Vm is the basic interface for an implementation of the EVM.
-type Vm interface {
- // Run should execute the given contract with the input given in in
- // and return the contract execution return bytes or an error if it
- // failed.
- Run(c *Contract, in []byte) ([]byte, error)
-}
-
-// RuleSet is an interface that defines the current rule set during the
-// execution of the EVM instructions (e.g. whether it's homestead)
-type RuleSet interface {
- IsHomestead(*big.Int) bool
-}
-
-// Environment is an EVM requirement and helper which allows access to outside
-// information such as states.
-type Environment interface {
- // The current ruleset
- RuleSet() RuleSet
- // The state database
- Db() Database
- // PreserveState preserve the current state and returns a new handle
- ForkState() Database
- // SetState sets the new state handle
- SetState(Database)
-
- // Creates a restorable snapshot
- //MakeSnapshot() Database
- // Set database to previous snapshot
- //SetSnapshot(Database)
- // Address of the original invoker (first occurrence of the VM invoker)
-
- Origin() common.Address
- // The block number this VM is invoked on
- BlockNumber() *big.Int
- // The n'th hash ago from this block number
- GetHash(uint64) common.Hash
- // The handler's address
- Coinbase() common.Address
- // The current time (block time)
- Time() *big.Int
- // Difficulty set on the current block
- Difficulty() *big.Int
- // The gas limit of the block
- GasLimit() *big.Int
- // The gas price of the current call
- GasPrice() *big.Int
- // Determines whether it's possible to transact
- CanTransfer(from common.Address, balance *big.Int) bool
- // Transfers amount from one account to the other
- Transfer(from, to Account, amount *big.Int)
- // Type of the VM
- Vm() Vm
- // Get the curret calling depth
- Depth() int
- // Set the current calling depth
- SetDepth(i int)
- // Call another contract
- Call(me ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error)
- // Take another's contract code and execute within our own context
- CallCode(me ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error)
- // Same as CallCode except sender and value is propagated from parent to child scope
- DelegateCall(me ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error)
- // Create a new contract
- Create(me ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error)
-}
-
-// Database is a EVM database for full state querying.
-type Database interface {
- GetAccount(common.Address) Account
- CreateAccount(common.Address) Account
-
- AddBalance(common.Address, *big.Int)
- GetBalance(common.Address) *big.Int
-
- GetNonce(common.Address) uint64
- SetNonce(common.Address, uint64)
-
- GetCode(common.Address) []byte
- SetCode(common.Address, []byte)
-
- AddRefund(*big.Int)
- GetRefund() *big.Int
-
- GetState(common.Address, common.Hash) common.Hash
- SetState(common.Address, common.Hash, common.Hash)
-
- Delete(common.Address) bool
- Exist(common.Address) bool
- IsDeleted(common.Address) bool
-
- AddLog(*Log)
-}
-
-// Account represents a contract or basic ethereum account.
-type Account interface {
- SubBalance(amount *big.Int)
- AddBalance(amount *big.Int)
- SetBalance(*big.Int)
- SetNonce(uint64)
- Balance() *big.Int
- Address() common.Address
- ReturnGas(uint64)
- SetCode([]byte)
- ForEachStorage(cb func(key, value common.Hash) bool)
- Value() *big.Int
-}
diff --git a/core/vm/instructions.go b/core/vm/instructions.go
index 2e3dc02de9..b64a955426 100644
--- a/core/vm/instructions.go
+++ b/core/vm/instructions.go
@@ -27,14 +27,14 @@ import (
type programInstruction interface {
// executes the program instruction and allows the instruction to modify the state of the program
- do(program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error)
+ do(program *Program, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error)
// returns whether the program instruction halts the execution of the JIT
halts() bool
// Returns the current op code (debugging purposes)
Op() OpCode
}
-type instrFn func(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack)
+type instrFn func(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack)
type instruction struct {
op OpCode
@@ -58,7 +58,7 @@ func jump(mapping map[uint64]uint64, destinations map[uint64]struct{}, contract
return mapping[to.Uint64()], nil
}
-func (instr instruction) do(program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func (instr instruction) do(program *Program, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
// calculate the new memory size and gas price for the current executing opcode
newMemSize, cost, err := jitCalculateGasAndSize(env, contract, instr, env.Db(), memory, stack)
if err != nil {
@@ -116,26 +116,26 @@ func (instr instruction) Op() OpCode {
return instr.op
}
-func opStaticJump(instr instruction, pc *uint64, ret *big.Int, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opStaticJump(instr instruction, pc *uint64, ret *big.Int, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
ret.Set(instr.data)
}
-func opAdd(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opAdd(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
x, y := stack.pop(), stack.pop()
stack.push(U256(x.Add(x, y)))
}
-func opSub(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opSub(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
x, y := stack.pop(), stack.pop()
stack.push(U256(x.Sub(x, y)))
}
-func opMul(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opMul(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
x, y := stack.pop(), stack.pop()
stack.push(U256(x.Mul(x, y)))
}
-func opDiv(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opDiv(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
x, y := stack.pop(), stack.pop()
if y.Cmp(common.Big0) != 0 {
stack.push(U256(x.Div(x, y)))
@@ -144,7 +144,7 @@ func opDiv(instr instruction, pc *uint64, env Environment, contract *Contract, m
}
}
-func opSdiv(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opSdiv(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
x, y := S256(stack.pop()), S256(stack.pop())
if y.Cmp(common.Big0) == 0 {
stack.push(new(big.Int))
@@ -164,7 +164,7 @@ func opSdiv(instr instruction, pc *uint64, env Environment, contract *Contract,
}
}
-func opMod(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opMod(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
x, y := stack.pop(), stack.pop()
if y.Cmp(common.Big0) == 0 {
stack.push(new(big.Int))
@@ -173,7 +173,7 @@ func opMod(instr instruction, pc *uint64, env Environment, contract *Contract, m
}
}
-func opSmod(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opSmod(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
x, y := S256(stack.pop()), S256(stack.pop())
if y.Cmp(common.Big0) == 0 {
@@ -193,12 +193,12 @@ func opSmod(instr instruction, pc *uint64, env Environment, contract *Contract,
}
}
-func opExp(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opExp(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
x, y := stack.pop(), stack.pop()
stack.push(U256(x.Exp(x, y, Pow256)))
}
-func opSignExtend(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opSignExtend(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
back := stack.pop()
if back.Cmp(big.NewInt(31)) < 0 {
bit := uint(back.Uint64()*8 + 7)
@@ -215,12 +215,12 @@ func opSignExtend(instr instruction, pc *uint64, env Environment, contract *Cont
}
}
-func opNot(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opNot(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
x := stack.pop()
stack.push(U256(x.Not(x)))
}
-func opLt(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opLt(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
x, y := stack.pop(), stack.pop()
if x.Cmp(y) < 0 {
stack.push(big.NewInt(1))
@@ -229,7 +229,7 @@ func opLt(instr instruction, pc *uint64, env Environment, contract *Contract, me
}
}
-func opGt(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opGt(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
x, y := stack.pop(), stack.pop()
if x.Cmp(y) > 0 {
stack.push(big.NewInt(1))
@@ -238,7 +238,7 @@ func opGt(instr instruction, pc *uint64, env Environment, contract *Contract, me
}
}
-func opSlt(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opSlt(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
x, y := S256(stack.pop()), S256(stack.pop())
if x.Cmp(S256(y)) < 0 {
stack.push(big.NewInt(1))
@@ -247,7 +247,7 @@ func opSlt(instr instruction, pc *uint64, env Environment, contract *Contract, m
}
}
-func opSgt(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opSgt(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
x, y := S256(stack.pop()), S256(stack.pop())
if x.Cmp(y) > 0 {
stack.push(big.NewInt(1))
@@ -256,7 +256,7 @@ func opSgt(instr instruction, pc *uint64, env Environment, contract *Contract, m
}
}
-func opEq(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opEq(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
x, y := stack.pop(), stack.pop()
if x.Cmp(y) == 0 {
stack.push(big.NewInt(1))
@@ -265,7 +265,7 @@ func opEq(instr instruction, pc *uint64, env Environment, contract *Contract, me
}
}
-func opIszero(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opIszero(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
x := stack.pop()
if x.Cmp(common.Big0) > 0 {
stack.push(new(big.Int))
@@ -274,19 +274,19 @@ func opIszero(instr instruction, pc *uint64, env Environment, contract *Contract
}
}
-func opAnd(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opAnd(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
x, y := stack.pop(), stack.pop()
stack.push(x.And(x, y))
}
-func opOr(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opOr(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
x, y := stack.pop(), stack.pop()
stack.push(x.Or(x, y))
}
-func opXor(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opXor(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
x, y := stack.pop(), stack.pop()
stack.push(x.Xor(x, y))
}
-func opByte(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opByte(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
th, val := stack.pop(), stack.pop()
if th.Cmp(big.NewInt(32)) < 0 {
byte := big.NewInt(int64(common.LeftPadBytes(val.Bytes(), 32)[th.Int64()]))
@@ -295,7 +295,7 @@ func opByte(instr instruction, pc *uint64, env Environment, contract *Contract,
stack.push(new(big.Int))
}
}
-func opAddmod(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opAddmod(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
x, y, z := stack.pop(), stack.pop(), stack.pop()
if z.Cmp(Zero) > 0 {
add := x.Add(x, y)
@@ -305,7 +305,7 @@ func opAddmod(instr instruction, pc *uint64, env Environment, contract *Contract
stack.push(new(big.Int))
}
}
-func opMulmod(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opMulmod(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
x, y, z := stack.pop(), stack.pop(), stack.pop()
if z.Cmp(Zero) > 0 {
mul := x.Mul(x, y)
@@ -316,45 +316,45 @@ func opMulmod(instr instruction, pc *uint64, env Environment, contract *Contract
}
}
-func opSha3(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opSha3(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
offset, size := stack.pop(), stack.pop()
hash := crypto.Keccak256(memory.GetPtr(offset.Int64(), size.Int64()))
stack.push(common.BytesToBig(hash))
}
-func opAddress(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opAddress(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
stack.push(common.Bytes2Big(contract.Address().Bytes()))
}
-func opBalance(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opBalance(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
addr := common.BigToAddress(stack.pop())
balance := env.Db().GetBalance(addr)
stack.push(new(big.Int).Set(balance))
}
-func opOrigin(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
- stack.push(env.Origin().Big())
+func opOrigin(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
+ stack.push(env.Origin.Big())
}
-func opCaller(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opCaller(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
stack.push(contract.Caller().Big())
}
-func opCallValue(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opCallValue(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
stack.push(new(big.Int).Set(contract.value))
}
-func opCalldataLoad(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opCalldataLoad(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
stack.push(common.Bytes2Big(getData(contract.Input, stack.pop(), common.Big32)))
}
-func opCalldataSize(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opCalldataSize(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
stack.push(big.NewInt(int64(len(contract.Input))))
}
-func opCalldataCopy(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opCalldataCopy(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
var (
mOff = stack.pop()
cOff = stack.pop()
@@ -363,18 +363,18 @@ func opCalldataCopy(instr instruction, pc *uint64, env Environment, contract *Co
memory.Set(mOff.Uint64(), l.Uint64(), getData(contract.Input, cOff, l))
}
-func opExtCodeSize(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opExtCodeSize(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
addr := common.BigToAddress(stack.pop())
l := big.NewInt(int64(len(env.Db().GetCode(addr))))
stack.push(l)
}
-func opCodeSize(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opCodeSize(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
l := big.NewInt(int64(len(contract.Code)))
stack.push(l)
}
-func opCodeCopy(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opCodeCopy(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
var (
mOff = stack.pop()
cOff = stack.pop()
@@ -385,7 +385,7 @@ func opCodeCopy(instr instruction, pc *uint64, env Environment, contract *Contra
memory.Set(mOff.Uint64(), l.Uint64(), codeCopy)
}
-func opExtCodeCopy(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opExtCodeCopy(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
var (
addr = common.BigToAddress(stack.pop())
mOff = stack.pop()
@@ -397,58 +397,58 @@ func opExtCodeCopy(instr instruction, pc *uint64, env Environment, contract *Con
memory.Set(mOff.Uint64(), l.Uint64(), codeCopy)
}
-func opGasprice(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
- stack.push(new(big.Int).Set(env.GasPrice()))
+func opGasprice(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
+ stack.push(new(big.Int).Set(env.GasPrice))
}
-func opBlockhash(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opBlockhash(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
num := stack.pop()
- n := new(big.Int).Sub(env.BlockNumber(), common.Big257)
- if num.Cmp(n) > 0 && num.Cmp(env.BlockNumber()) < 0 {
+ n := new(big.Int).Sub(env.BlockNumber, common.Big257)
+ if num.Cmp(n) > 0 && num.Cmp(env.BlockNumber) < 0 {
stack.push(env.GetHash(num.Uint64()).Big())
} else {
stack.push(new(big.Int))
}
}
-func opCoinbase(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
- stack.push(env.Coinbase().Big())
+func opCoinbase(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
+ stack.push(env.Coinbase.Big())
}
-func opTimestamp(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
- stack.push(U256(new(big.Int).Set(env.Time())))
+func opTimestamp(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
+ stack.push(U256(new(big.Int).Set(env.Time)))
}
-func opNumber(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
- stack.push(U256(new(big.Int).Set(env.BlockNumber())))
+func opNumber(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
+ stack.push(U256(new(big.Int).Set(env.BlockNumber)))
}
-func opDifficulty(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
- stack.push(U256(new(big.Int).Set(env.Difficulty())))
+func opDifficulty(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
+ stack.push(U256(new(big.Int).Set(env.Difficulty)))
}
-func opGasLimit(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
- stack.push(U256(new(big.Int).Set(env.GasLimit())))
+func opGasLimit(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
+ stack.push(U256(new(big.Int).Set(env.GasLimit)))
}
-func opPop(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opPop(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
stack.pop()
}
-func opPush(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opPush(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
stack.push(new(big.Int).Set(instr.data))
}
-func opDup(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opDup(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
stack.dup(int(instr.data.Int64()))
}
-func opSwap(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opSwap(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
stack.swap(int(instr.data.Int64()))
}
-func opLog(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opLog(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
n := int(instr.data.Int64())
topics := make([]common.Hash, n)
mStart, mSize := stack.pop(), stack.pop()
@@ -457,60 +457,60 @@ func opLog(instr instruction, pc *uint64, env Environment, contract *Contract, m
}
d := memory.Get(mStart.Int64(), mSize.Int64())
- log := NewLog(contract.Address(), topics, d, env.BlockNumber().Uint64())
+ log := NewLog(contract.Address(), topics, d, env.BlockNumber.Uint64())
//env.AddLog(log)
env.Db().AddLog(log)
}
-func opMload(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opMload(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
offset := stack.pop()
val := common.BigD(memory.GetPtr(offset.Int64(), 32))
stack.push(val)
}
-func opMstore(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opMstore(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
// pop value of the stack
mStart, val := stack.pop(), stack.pop()
memory.Set(mStart.Uint64(), 32, common.BigToBytes(val, 256))
}
-func opMstore8(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opMstore8(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
off, val := stack.pop().Int64(), stack.pop().Int64()
memory.store[off] = byte(val & 0xff)
}
-func opSload(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opSload(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
loc := common.BigToHash(stack.pop())
val := env.Db().GetState(contract.Address(), loc).Big()
stack.push(val)
}
-func opSstore(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opSstore(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
loc := common.BigToHash(stack.pop())
val := stack.pop()
env.Db().SetState(contract.Address(), loc, common.BigToHash(val))
}
-func opJump(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opJump(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
}
-func opJumpi(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opJumpi(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
}
-func opJumpdest(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opJumpdest(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
}
-func opPc(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opPc(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
stack.push(new(big.Int).Set(instr.data))
}
-func opMsize(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opMsize(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
stack.push(big.NewInt(int64(memory.Len())))
}
-func opGas(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opGas(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
stack.push(new(big.Int).SetUint64(contract.gas64))
}
-func opCreate(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opCreate(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
var (
value = stack.pop()
offset, size = stack.pop(), stack.pop()
@@ -523,7 +523,7 @@ func opCreate(instr instruction, pc *uint64, env Environment, contract *Contract
// homestead we must check for CodeStoreOutOfGasError (homestead only
// rule) and treat as an error, if the ruleset is frontier we must
// ignore this error and pretend the operation was successful.
- if env.RuleSet().IsHomestead(env.BlockNumber()) && suberr == CodeStoreOutOfGasError {
+ if env.RuleSet().IsHomestead(env.BlockNumber) && suberr == CodeStoreOutOfGasError {
stack.push(new(big.Int))
} else if suberr != nil && suberr != CodeStoreOutOfGasError {
stack.push(new(big.Int))
@@ -532,7 +532,7 @@ func opCreate(instr instruction, pc *uint64, env Environment, contract *Contract
}
}
-func opCall(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opCall(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
gas := stack.pop()
// pop gas and value of the stack.
addr, value := stack.pop(), stack.pop()
@@ -563,7 +563,7 @@ func opCall(instr instruction, pc *uint64, env Environment, contract *Contract,
}
}
-func opCallCode(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opCallCode(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
gas := stack.pop()
// pop gas and value of the stack.
addr, value := stack.pop(), stack.pop()
@@ -594,7 +594,7 @@ func opCallCode(instr instruction, pc *uint64, env Environment, contract *Contra
}
}
-func opDelegateCall(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opDelegateCall(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
gas, to, inOffset, inSize, outOffset, outSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
toAddr := common.BigToAddress(to)
@@ -608,12 +608,12 @@ func opDelegateCall(instr instruction, pc *uint64, env Environment, contract *Co
}
}
-func opReturn(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opReturn(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
}
-func opStop(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opStop(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
}
-func opSuicide(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opSuicide(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
balance := env.Db().GetBalance(contract.Address())
env.Db().AddBalance(common.BigToAddress(stack.pop()), balance)
@@ -624,7 +624,7 @@ func opSuicide(instr instruction, pc *uint64, env Environment, contract *Contrac
// make log instruction function
func makeLog(size int) instrFn {
- return func(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+ return func(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
topics := make([]common.Hash, size)
mStart, mSize := stack.pop(), stack.pop()
for i := 0; i < size; i++ {
@@ -632,14 +632,14 @@ func makeLog(size int) instrFn {
}
d := memory.Get(mStart.Int64(), mSize.Int64())
- log := NewLog(contract.Address(), topics, d, env.BlockNumber().Uint64())
+ log := NewLog(contract.Address(), topics, d, env.BlockNumber.Uint64())
env.Db().AddLog(log)
}
}
// make push instruction function
func makePush(size uint64, bsize *big.Int) instrFn {
- return func(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+ return func(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
byts := getData(contract.Code, new(big.Int).SetUint64(*pc+1), bsize)
stack.push(common.Bytes2Big(byts))
*pc += size
@@ -648,7 +648,7 @@ func makePush(size uint64, bsize *big.Int) instrFn {
// make push instruction function
func makeDup(size int64) instrFn {
- return func(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+ return func(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
stack.dup(int(size))
}
}
@@ -657,7 +657,7 @@ func makeDup(size int64) instrFn {
func makeSwap(size int64) instrFn {
// switch n + 1 otherwise n would be swapped with n
size += 1
- return func(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+ return func(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
stack.swap(int(size))
}
}
diff --git a/core/vm/interface.go b/core/vm/interface.go
new file mode 100644
index 0000000000..70f9717b93
--- /dev/null
+++ b/core/vm/interface.go
@@ -0,0 +1,191 @@
+// 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 .
+
+package vm
+
+import (
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/common"
+)
+
+// Vm is the basic interface for an implementation of the EVM.
+type Vm interface {
+ // Run should execute the given contract with the input given in in
+ // and return the contract execution return bytes or an error if it
+ // failed.
+ Run(c *Contract, in []byte) ([]byte, error)
+}
+
+// RuleSet is an interface that defines the current rule set during the
+// execution of the EVM instructions (e.g. whether it's homestead)
+type RuleSet interface {
+ IsHomestead(*big.Int) bool
+}
+
+// Database is a EVM database for full state querying.
+type Database interface {
+ GetAccount(common.Address) Account
+ CreateAccount(common.Address) Account
+
+ SubBalance(common.Address, *big.Int)
+ AddBalance(common.Address, *big.Int)
+ GetBalance(common.Address) *big.Int
+
+ GetNonce(common.Address) uint64
+ SetNonce(common.Address, uint64)
+
+ GetCode(common.Address) []byte
+ SetCode(common.Address, []byte)
+
+ AddRefund(*big.Int)
+ GetRefund() *big.Int
+
+ GetState(common.Address, common.Hash) common.Hash
+ SetState(common.Address, common.Hash, common.Hash)
+
+ Delete(common.Address) bool
+ Exist(common.Address) bool
+ IsDeleted(common.Address) bool
+
+ AddLog(*Log)
+}
+
+// Account represents a contract or basic ethereum account.
+type Account interface {
+ SubBalance(amount *big.Int)
+ AddBalance(amount *big.Int)
+ SetBalance(*big.Int)
+ SetNonce(uint64)
+ Balance() *big.Int
+ Address() common.Address
+ ReturnGas(uint64)
+ SetCode([]byte)
+ ForEachStorage(cb func(key, value common.Hash) bool)
+ Value() *big.Int
+}
+
+// Context provides the EVM with auxilary information. Once provided it shouldn't be modified.
+type Context struct {
+ CallContext
+
+ // Message information
+ Origin common.Address // Provides information for ORIGIN
+ Coinbase common.Address // Provides information for COINBASE
+ GasPrice *big.Int // Provides information for GASPRICE
+
+ // Block information
+ GasLimit *big.Int // Provides information for GASLIMIT
+ BlockNumber *big.Int // Provides information for NUMBER
+ Time *big.Int // Provides information for TIME
+ Difficulty *big.Int // Provides information for DIFFICULTY
+}
+
+// CallContext provides a basic interface for the EVM calling conventions. The EVM Environment
+// depends on this context being implemented for doing subcalls and initialising new EVM contracts.
+type CallContext interface {
+ // Call another contract
+ Call(env *Environment, me ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error)
+ // Take another's contract code and execute within our own context
+ CallCode(env *Environment, me ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error)
+ // Same as CallCode except sender and value is propagated from parent to child scope
+ DelegateCall(env *Environment, me ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error)
+ // Create a new contract
+ Create(env *Environment, me ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error)
+}
+
+// Backend is the basic interface for keeping track of state and taking care of
+// returning ancestory data.
+type Backend interface {
+ GetHash(n uint64) common.Hash // GetHash returns the hash corresponding to n
+
+ Fork() Database // Fork returns a forked database
+ Get() Database // Get returns the current database
+ Set(Database) // Set sets the new database
+}
+
+type Environment struct {
+ Context
+
+ Backend Backend
+
+ ruleSet RuleSet
+ vmConfig Config
+
+ evm Vm
+
+ Depth int
+}
+
+func NewEnvironment(context Context, backend Backend, ruleSet RuleSet, vmCfg Config) *Environment {
+ env := &Environment{
+ Context: context,
+ Backend: backend,
+ vmConfig: vmCfg,
+ ruleSet: ruleSet,
+ }
+ env.evm = New(env, vmCfg)
+ return env
+}
+
+func (env *Environment) Call(caller ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) {
+ if env.vmConfig.Test && env.Depth > 0 {
+ caller.ReturnGas(gas.Uint64())
+
+ return nil, nil
+ }
+
+ return env.CallContext.Call(env, caller, addr, data, gas, value)
+}
+
+// Take another's contract code and execute within our own context
+func (env *Environment) CallCode(caller ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) {
+ if env.vmConfig.Test && env.Depth > 0 {
+ caller.ReturnGas(gas.Uint64())
+
+ return nil, nil
+ }
+
+ return env.CallContext.CallCode(env, caller, addr, data, gas, value)
+}
+
+// Same as CallCode except sender and value is propagated from parent to child scope
+func (env *Environment) DelegateCall(caller ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error) {
+ if env.vmConfig.Test && env.Depth > 0 {
+ caller.ReturnGas(gas.Uint64())
+
+ return nil, nil
+ }
+
+ return env.CallContext.DelegateCall(env, caller, addr, data, gas)
+}
+
+// Create a new contract
+func (env *Environment) Create(caller ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error) {
+ if env.vmConfig.Test && env.Depth > 0 {
+ caller.ReturnGas(gas.Uint64())
+
+ return nil, common.Address{}, nil
+ }
+
+ return env.CallContext.Create(env, caller, data, gas, value)
+}
+func (env *Environment) RuleSet() RuleSet { return env.ruleSet }
+func (env *Environment) Vm() Vm { return env.evm }
+func (env *Environment) Db() Database { return env.Backend.Get() }
+func (env *Environment) GetHash(n uint64) common.Hash {
+ return env.Backend.GetHash(n)
+}
diff --git a/core/vm/logger.go b/core/vm/logger.go
index ae62b6b573..29225a84cb 100644
--- a/core/vm/logger.go
+++ b/core/vm/logger.go
@@ -149,7 +149,7 @@ func (l *StructLogger) CaptureState(env Environment, pc uint64, op OpCode, gas,
}
}
// create a new snaptshot of the EVM.
- log := StructLog{pc, op, new(big.Int).Set(gas), cost, mem, stck, storage, env.Depth(), err}
+ log := StructLog{pc, op, new(big.Int).Set(gas), cost, mem, stck, storage, env.Depth, err}
l.logs = append(l.logs, log)
}
diff --git a/core/vm/program.go b/core/vm/program.go
index 4190ce82c1..14aba6e274 100644
--- a/core/vm/program.go
+++ b/core/vm/program.go
@@ -288,11 +288,9 @@ func CompileProgram(program *Program) {
program.addInstr(op, pc, nil, nil)
}
}
-
- optimiseProgram(program)
}
-func RunProgram(program *Program, env Environment, contract *Contract, input []byte) ([]byte, error) {
+func RunProgram(program *Program, env *Environment, contract *Contract, input []byte) ([]byte, error) {
return New(env, Config{}).Run(contract, input)
}
@@ -310,7 +308,7 @@ func validDest(dests map[uint64]struct{}, dest *big.Int) bool {
// jitCalculateGasAndSize calculates the required given the opcode and stack items calculates the new memorysize for
// the operation. This does not reduce gas or resizes the memory.
-func jitCalculateGasAndSize(env Environment, contract *Contract, instr instruction, statedb Database, mem *Memory, stack *Stack) (uint64, uint64, error) {
+func jitCalculateGasAndSize(env *Environment, contract *Contract, instr instruction, statedb Database, mem *Memory, stack *Stack) (uint64, uint64, error) {
var (
newMemSize uint64
sizeFault bool
diff --git a/core/vm/program_optimiser.go b/core/vm/program_optimiser.go
index 6d1ac93264..79c91df3e6 100644
--- a/core/vm/program_optimiser.go
+++ b/core/vm/program_optimiser.go
@@ -26,7 +26,7 @@ import (
// optimeProgram optimises a JIT program creating segments out of program
// instructions. Currently covered are multi-pushes and static jumps
-func optimiseProgram(program *Program) {
+func OptimiseProgram(program *Program) {
var load []instruction
var (
diff --git a/core/vm/segments.go b/core/vm/segments.go
index 61d87367c6..ed223d82f2 100644
--- a/core/vm/segments.go
+++ b/core/vm/segments.go
@@ -24,7 +24,7 @@ type jumpSeg struct {
gas uint64
}
-func (j jumpSeg) do(program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func (j jumpSeg) do(program *Program, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
if !contract.UseGas(j.gas) {
return nil, OutOfGasError
}
@@ -42,7 +42,7 @@ type pushSeg struct {
gas uint64
}
-func (s pushSeg) do(program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func (s pushSeg) do(program *Program, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
// Use the calculated gas. When insufficient gas is present, use all gas and return an
// Out Of Gas error
if !contract.UseGas(s.gas) {
diff --git a/core/vm/vm.go b/core/vm/vm.go
index e5807d143a..40c09fff6a 100644
--- a/core/vm/vm.go
+++ b/core/vm/vm.go
@@ -27,10 +27,12 @@ import (
// Config are the configuration options for the EVM
type Config struct {
- Debug bool
- EnableJit bool
- ForceJit bool
- Tracer Tracer
+ Test bool // boolean to indicate this is a VM Test
+
+ ForceCompile bool
+ NoOptimise bool // skip optimising the program if it hasn't been compiled
+ Debug bool
+ Tracer Tracer
}
// EVM is used to run Ethereum based contracts and will utilise the
@@ -38,24 +40,24 @@ type Config struct {
// The EVM will run the byte code VM or JIT VM based on the passed
// configuration.
type EVM struct {
- env Environment
+ env *Environment
jumpTable vmJumpTable
cfg Config
}
// New returns a new instance of the EVM.
-func New(env Environment, cfg Config) *EVM {
+func New(env *Environment, cfg Config) *EVM {
return &EVM{
env: env,
- jumpTable: newJumpTable(env.RuleSet(), env.BlockNumber()),
+ jumpTable: newJumpTable(env.RuleSet(), env.BlockNumber),
cfg: cfg,
}
}
// Run loops and evaluates the contract's code with the given input data
func (evm *EVM) Run(contract *Contract, input []byte) ([]byte, error) {
- evm.env.SetDepth(evm.env.Depth() + 1)
- defer evm.env.SetDepth(evm.env.Depth() - 1)
+ evm.env.Depth++
+ defer func() { evm.env.Depth-- }()
if contract.CodeAddr != nil {
if p, exist := PrecompiledContracts[*contract.CodeAddr]; exist {
@@ -80,6 +82,9 @@ func (evm *EVM) Run(contract *Contract, input []byte) ([]byte, error) {
// Create and compile program
program := NewProgram(contract.Code)
CompileProgram(program)
+ if !evm.cfg.NoOptimise {
+ OptimiseProgram(program)
+ }
return evm.runProgram(program, contract, input)
case progCompile:
@@ -111,7 +116,7 @@ func (evm *EVM) runProgram(program *Program, contract *Contract, input []byte) (
}()
}
- homestead := env.RuleSet().IsHomestead(env.BlockNumber())
+ homestead := env.RuleSet().IsHomestead(env.BlockNumber)
for pc < uint64(len(program.instructions)) {
instrCount++
diff --git a/core/vm/vm_jit_fake.go b/core/vm/vm_jit_fake.go
index 4fa98ccd9c..4d6acf37fc 100644
--- a/core/vm/vm_jit_fake.go
+++ b/core/vm/vm_jit_fake.go
@@ -20,7 +20,7 @@ package vm
import "fmt"
-func NewJitVm(env Environment) VirtualMachine {
+func NewJitVm(env *Environment) VirtualMachine {
fmt.Printf("Warning! EVM JIT not enabled.\n")
return New(env, Config{})
}
diff --git a/core/vm_env.go b/core/vm_env.go
deleted file mode 100644
index cd2a041f50..0000000000
--- a/core/vm_env.go
+++ /dev/null
@@ -1,121 +0,0 @@
-// 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 .
-
-package core
-
-import (
- "math/big"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/core/state"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/core/vm"
-)
-
-// GetHashFn returns a function for which the VM env can query block hashes through
-// up to the limit defined by the Yellow Paper and uses the given block chain
-// to query for information.
-func GetHashFn(ref common.Hash, chain *BlockChain) func(n uint64) common.Hash {
- return func(n uint64) common.Hash {
- for block := chain.GetBlockByHash(ref); block != nil; block = chain.GetBlock(block.ParentHash(), block.NumberU64()-1) {
- if block.NumberU64() == n {
- return block.Hash()
- }
- }
-
- return common.Hash{}
- }
-}
-
-type VMEnv struct {
- chainConfig *ChainConfig // Chain configuration
-
- state *state.State
-
- evm vm.VirtualMachine // The Ethereum Virtual Machine
- depth int // Current execution depth
- msg Message // Message appliod
-
- header *types.Header // Header information
- chain *BlockChain // Blockchain handle
- getHashFn func(uint64) common.Hash // getHashFn callback is used to retrieve block hashes
-}
-
-func NewEnv(state *state.State, chainConfig *ChainConfig, chain *BlockChain, msg Message, header *types.Header, cfg vm.Config) *VMEnv {
- env := &VMEnv{
- chainConfig: chainConfig,
- chain: chain,
- state: state,
- header: header,
- msg: msg,
- getHashFn: GetHashFn(header.ParentHash, chain),
- }
-
- env.evm = vm.New(env, cfg)
- return env
-}
-
-func (self *VMEnv) RuleSet() vm.RuleSet { return self.chainConfig }
-func (self *VMEnv) Vm() vm.Vm { return self.evm }
-func (self *VMEnv) Origin() common.Address { f, _ := self.msg.From(); return f }
-func (self *VMEnv) BlockNumber() *big.Int { return self.header.Number }
-func (self *VMEnv) Coinbase() common.Address { return self.header.Coinbase }
-func (self *VMEnv) Time() *big.Int { return self.header.Time }
-func (self *VMEnv) Difficulty() *big.Int { return self.header.Difficulty }
-func (self *VMEnv) GasLimit() *big.Int { return self.header.GasLimit }
-func (self *VMEnv) GasPrice() *big.Int { return self.msg.GasPrice() }
-func (self *VMEnv) Value() *big.Int { return self.msg.Value() }
-func (self *VMEnv) Db() vm.Database { return self.state }
-func (self *VMEnv) Depth() int { return self.depth }
-func (self *VMEnv) SetDepth(i int) { self.depth = i }
-func (self *VMEnv) GetHash(n uint64) common.Hash {
- return self.getHashFn(n)
-}
-
-func (self *VMEnv) AddLog(log *vm.Log) {
- self.state.AddLog(log)
-}
-func (self *VMEnv) CanTransfer(from common.Address, balance *big.Int) bool {
- return self.state.GetBalance(from).Cmp(balance) >= 0
-}
-
-func (self *VMEnv) ForkState() vm.Database {
- self.state = state.Fork(self.state)
- return self.state
-}
-
-func (self *VMEnv) SetState(st vm.Database) {
- self.state = st.(*state.State)
-}
-
-func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) {
- Transfer(from, to, amount)
-}
-
-func (self *VMEnv) Call(me vm.ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) {
- return Call(self, me, addr, data, gas, value)
-}
-func (self *VMEnv) CallCode(me vm.ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) {
- return CallCode(self, me, addr, data, gas, value)
-}
-
-func (self *VMEnv) DelegateCall(me vm.ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error) {
- return DelegateCall(self, me, addr, data, gas)
-}
-
-func (self *VMEnv) Create(me vm.ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error) {
- return Create(self, me, data, gas, value)
-}
diff --git a/eth/api.go b/eth/api.go
index f4bce47b84..df95f4562c 100644
--- a/eth/api.go
+++ b/eth/api.go
@@ -520,9 +520,17 @@ func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, txHash common.
value: tx.Value(),
data: tx.Data(),
}
+
+ backend := &core.EVMBackend{
+ GetHashFn: core.GetHashFn(block.ParentHash(), api.eth.BlockChain()),
+ State: stateDb,
+ }
+ context := core.ToEVMContext(api.config, msg, block.Header())
+
// Mutate the state if we haven't reached the tracing transaction yet
if uint64(idx) < txIndex {
- vmenv := core.NewEnv(stateDb, api.config, api.eth.BlockChain(), msg, block.Header(), vm.Config{})
+ vmenv := vm.NewEnvironment(context, backend, api.config, vm.Config{})
+ //vmenv := core.NewEnv(stateDb, api.config, api.eth.BlockChain(), msg, block.Header(), vm.Config{})
_, _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()))
if err != nil {
return nil, fmt.Errorf("mutation failed: %v", err)
@@ -531,7 +539,8 @@ func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, txHash common.
continue
}
// Otherwise trace the transaction and return
- vmenv := core.NewEnv(stateDb, api.config, api.eth.BlockChain(), msg, block.Header(), vm.Config{Debug: true, Tracer: tracer})
+ vmenv := vm.NewEnvironment(context, backend, api.config, vm.Config{Debug: true, Tracer: tracer})
+ //vmenv := core.NewEnv(stateDb, api.config, api.eth.BlockChain(), msg, block.Header(), vm.Config{Debug: true, Tracer: tracer})
ret, gas, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()))
if err != nil {
return nil, fmt.Errorf("tracing failed: %v", err)
diff --git a/eth/api_backend.go b/eth/api_backend.go
index fa0365f7d3..481a9ee02f 100644
--- a/eth/api_backend.go
+++ b/eth/api_backend.go
@@ -97,13 +97,21 @@ func (b *EthApiBackend) GetTd(blockHash common.Hash) *big.Int {
return b.eth.blockchain.GetTdByHash(blockHash)
}
-func (b *EthApiBackend) GetVMEnv(ctx context.Context, msg core.Message, st ethapi.State, header *types.Header) (vm.Environment, func() error, error) {
+func (b *EthApiBackend) GetVMEnv(ctx context.Context, msg core.Message, st ethapi.State, header *types.Header) (*vm.Environment, func() error, error) {
stateDb := state.Fork(st.(EthApiState).state)
addr, _ := msg.From()
from := stateDb.GetOrNewStateObject(addr)
from.SetBalance(common.MaxBig)
vmError := func() error { return nil }
- return core.NewEnv(stateDb, b.eth.chainConfig, b.eth.blockchain, msg, header, b.eth.chainConfig.VmConfig), vmError, nil
+
+ backend := &core.EVMBackend{
+ GetHashFn: core.GetHashFn(header.ParentHash, b.eth.blockchain),
+ State: stateDb,
+ }
+ context := core.ToEVMContext(b.eth.chainConfig, msg, header)
+
+ return vm.NewEnvironment(context, backend, b.eth.chainConfig, b.eth.chainConfig.VmConfig), vmError, nil
+ //return core.NewEnv(stateDb, b.eth.chainConfig, b.eth.blockchain, msg, header, b.eth.chainConfig.VmConfig), vmError, nil
}
func (b *EthApiBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error {
diff --git a/eth/backend.go b/eth/backend.go
index c4a883c9e9..a9e1630d33 100644
--- a/eth/backend.go
+++ b/eth/backend.go
@@ -35,7 +35,6 @@ import (
"github.com/ethereum/go-ethereum/common/registrar/ethreg"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/eth/filters"
"github.com/ethereum/go-ethereum/eth/gasprice"
@@ -202,10 +201,6 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
core.WriteChainConfig(chainDb, genesis.Hash(), config.ChainConfig)
eth.chainConfig = config.ChainConfig
- eth.chainConfig.VmConfig = vm.Config{
- EnableJit: config.EnableJit,
- ForceJit: config.ForceJit,
- }
eth.blockchain, err = core.NewBlockChain(chainDb, eth.chainConfig, eth.pow, eth.EventMux())
if err != nil {
diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go
index 0aa3da18d0..3b8ae77e54 100644
--- a/internal/ethapi/backend.go
+++ b/internal/ethapi/backend.go
@@ -50,7 +50,7 @@ type Backend interface {
GetBlock(ctx context.Context, blockHash common.Hash) (*types.Block, error)
GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error)
GetTd(blockHash common.Hash) *big.Int
- GetVMEnv(ctx context.Context, msg core.Message, state State, header *types.Header) (vm.Environment, func() error, error)
+ GetVMEnv(ctx context.Context, msg core.Message, state State, header *types.Header) (*vm.Environment, func() error, error)
// TxPool API
SendTx(ctx context.Context, signedTx *types.Transaction) error
RemoveTx(txHash common.Hash)
diff --git a/miner/worker.go b/miner/worker.go
index 85b9f7f958..19898ca778 100644
--- a/miner/worker.go
+++ b/miner/worker.go
@@ -620,13 +620,7 @@ func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsB
func (env *Work) commitTransaction(tx *types.Transaction, bc *core.BlockChain, gp *core.GasPool) (error, vm.Logs) {
// this is a bit of a hack to force jit for the miners
- config := env.config.VmConfig
- if !(config.EnableJit && config.ForceJit) {
- config.EnableJit = false
- }
- config.ForceJit = false // disable forcing jit
-
- state, receipt, logs, _, err := core.ApplyTransaction(env.config, bc, gp, env.state, env.header, tx, env.header.GasUsed, config)
+ state, receipt, logs, _, err := core.ApplyTransaction(env.config, bc, gp, env.state, env.header, tx, env.header.GasUsed, env.config.VmConfig)
defer func() { env.state = state }()
if err != nil {
diff --git a/tests/state_test_util.go b/tests/state_test_util.go
index 684cec4bf6..ae97437f80 100644
--- a/tests/state_test_util.go
+++ b/tests/state_test_util.go
@@ -236,14 +236,14 @@ func RunState(ruleSet RuleSet, statedb *state.State, env, tx map[string]string)
key, _ := hex.DecodeString(tx["secretKey"])
addr := crypto.PubkeyToAddress(crypto.ToECDSA(key).PublicKey)
message := NewMessage(addr, to, data, value, gas, price, nonce)
- vmenv := NewEnvFromMap(ruleSet, nstatedb, env, tx)
- vmenv.origin = addr
+ vmenv := NewEVMEnvironment(false, ruleSet, nstatedb, env, tx)
+ //vmenv.origin = addr
ret, _, err := core.ApplyMessage(vmenv, message, gaspool)
if core.IsNonceErr(err) || core.IsInvalidTxErr(err) || core.IsGasLimitErr(err) {
- vmenv.SetState(snapshot)
+ vmenv.Backend.Set(snapshot)
}
- statedb.Set(state.Flatten(vmenv.state))
+ statedb.Set(state.Flatten(vmenv.Db().(*state.State)))
state.Commit(statedb)
- return ret, statedb.Logs(), vmenv.Gas, err
+ return ret, statedb.Logs(), gas, err
}
diff --git a/tests/util.go b/tests/util.go
index a1c29baa77..4f4ef84c84 100644
--- a/tests/util.go
+++ b/tests/util.go
@@ -18,6 +18,7 @@ package tests
import (
"bytes"
+ "encoding/hex"
"fmt"
"math/big"
"os"
@@ -149,143 +150,52 @@ func (r RuleSet) IsHomestead(n *big.Int) bool {
return n.Cmp(r.HomesteadBlock) >= 0
}
-type Env struct {
- ruleSet RuleSet
- depth int
- state *state.State
- skipTransfer bool
- initial bool
- Gas, gasPrice *big.Int
+func NewEVMEnvironment(vmTest bool, ruleSet RuleSet, state *state.State, envValues map[string]string, exeValues map[string]string) *vm.Environment {
+ //env := NewEnv(ruleSet, state)
- origin common.Address
- parent common.Hash
- coinbase common.Address
-
- number *big.Int
- time *big.Int
- difficulty *big.Int
- gasLimit *big.Int
-
- vmTest bool
-
- evm vm.VirtualMachine
-}
-
-func NewEnv(ruleSet RuleSet, state *state.State) *Env {
- env := &Env{
- ruleSet: ruleSet,
- state: state,
+ origin := common.HexToAddress(exeValues["caller"])
+ if len(exeValues["secretKey"]) > 0 {
+ key, _ := hex.DecodeString(exeValues["secretKey"])
+ origin = crypto.PubkeyToAddress(crypto.ToECDSA(key).PublicKey)
}
- return env
-}
-func NewEnvFromMap(ruleSet RuleSet, state *state.State, envValues map[string]string, exeValues map[string]string) *Env {
- env := NewEnv(ruleSet, state)
+ context := vm.Context{
+ Origin: origin,
+ Coinbase: common.HexToAddress(envValues["currentCoinbase"]),
+ BlockNumber: common.Big(envValues["currentNumber"]),
+ Time: common.Big(envValues["currentTimestamp"]),
+ Difficulty: common.Big(envValues["currentDifficulty"]),
+ GasLimit: common.Big(envValues["currentGasLimit"]),
+ GasPrice: common.Big(exeValues["gasPrice"]),
+ }
- env.origin = common.HexToAddress(exeValues["caller"])
- env.parent = common.HexToHash(envValues["previousHash"])
- env.coinbase = common.HexToAddress(envValues["currentCoinbase"])
- env.number = common.Big(envValues["currentNumber"])
- env.time = common.Big(envValues["currentTimestamp"])
- env.difficulty = common.Big(envValues["currentDifficulty"])
- env.gasLimit = common.Big(envValues["currentGasLimit"])
- env.Gas = new(big.Int)
- env.gasPrice = common.Big(exeValues["gasPrice"])
-
- env.evm = vm.New(env, vm.Config{
- EnableJit: EnableJit,
- ForceJit: ForceJit,
- })
-
- return env
-}
-
-func (self *Env) RuleSet() vm.RuleSet { return self.ruleSet }
-func (self *Env) Vm() vm.Vm { return self.evm }
-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) GasPrice() *big.Int { return self.gasPrice }
-func (self *Env) VmType() vm.Type { return vm.StdVmTy }
-func (self *Env) GetHash(n uint64) common.Hash {
- return common.BytesToHash(crypto.Keccak256([]byte(big.NewInt(int64(n)).String())))
-}
-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 {
- if self.skipTransfer {
- if self.initial {
- self.initial = false
- return true
+ //var initialCall bool
+ backend := &core.EVMBackend{
+ GetHashFn: func(n uint64) common.Hash {
+ return common.BytesToHash(crypto.Keccak256([]byte(big.NewInt(int64(n)).String())))
+ },
+ State: state,
+ }
+ initialCall := true
+ canTransfer := func(db vm.Database, address common.Address, amount *big.Int) bool {
+ if vmTest {
+ if initialCall {
+ initialCall = false
+ return true
+ }
}
+ return db.GetBalance(address).Cmp(amount) >= 0
}
-
- return self.state.GetBalance(from).Cmp(balance) >= 0
-}
-func (self *Env) ForkState() vm.Database {
- self.state = state.Fork(self.state)
- return self.state
-}
-
-func (self *Env) SetState(st vm.Database) {
- self.state = st.(*state.State)
-}
-
-func (self *Env) Transfer(from, to vm.Account, amount *big.Int) {
- if self.skipTransfer {
- return
+ transfer := func(db vm.Database, sender, recipient common.Address, amount *big.Int) {
+ if vmTest {
+ return
+ }
+ core.Transfer(db, sender, recipient, amount)
}
- core.Transfer(from, to, amount)
-}
+ context.CallContext = core.EVMCallContext{canTransfer, transfer}
-func (self *Env) Call(caller vm.ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) {
- if self.vmTest && self.depth > 0 {
- caller.ReturnGas(gas.Uint64())
-
- return nil, nil
- }
- ret, err := core.Call(self, caller, addr, data, gas, value)
- self.Gas = gas
-
- return ret, err
-
-}
-func (self *Env) CallCode(caller vm.ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) {
- if self.vmTest && self.depth > 0 {
- caller.ReturnGas(gas.Uint64())
-
- return nil, nil
- }
- return core.CallCode(self, caller, addr, data, gas, value)
-}
-
-func (self *Env) DelegateCall(caller vm.ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error) {
- if self.vmTest && self.depth > 0 {
- caller.ReturnGas(gas.Uint64())
-
- return nil, nil
- }
- return core.DelegateCall(self, caller, addr, data, gas)
-}
-
-func (self *Env) Create(caller vm.ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error) {
- if self.vmTest {
- caller.ReturnGas(gas.Uint64())
-
- nonce := self.state.GetNonce(caller.Address())
- obj := self.state.GetOrNewStateObject(crypto.CreateAddress(caller.Address(), nonce))
-
- return nil, obj.Address(), nil
- } else {
- return core.Create(self, caller, data, gas, value)
- }
+ env := vm.NewEnvironment(context, backend, ruleSet, vm.Config{Test: vmTest})
+ return env
}
type Message struct {
diff --git a/tests/vm_test_util.go b/tests/vm_test_util.go
index 08545407d3..58da6feae1 100644
--- a/tests/vm_test_util.go
+++ b/tests/vm_test_util.go
@@ -240,14 +240,11 @@ func RunVm(statedb *state.State, env, exec map[string]string) ([]byte, vm.Logs,
caller := statedb.GetOrNewStateObject(from)
- vmenv := NewEnvFromMap(RuleSet{params.MainNetHomesteadBlock, params.MainNetDAOForkBlock, true}, statedb, env, exec)
- vmenv.vmTest = true
- vmenv.skipTransfer = true
- vmenv.initial = true
+ vmenv := NewEVMEnvironment(true, RuleSet{params.MainNetHomesteadBlock, params.MainNetDAOForkBlock, true}, statedb, env, exec)
ret, err := vmenv.Call(caller, to, data, gas, value)
- statedb.Set(state.Flatten(vmenv.state))
+ statedb.Set(state.Flatten(vmenv.Db().(*state.State)))
state.Commit(statedb)
- return ret, statedb.Logs(), vmenv.Gas, err
+ return ret, statedb.Logs(), gas, err
}