diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go
index 74c95f4659..573d7f95c2 100644
--- a/accounts/abi/bind/backends/simulated.go
+++ b/accounts/abi/bind/backends/simulated.go
@@ -225,7 +225,14 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallM
from.SetBalance(common.MaxBig)
// Execute the call.
msg := callmsg{call}
- vmenv := core.NewEnv(statedb, chainConfig, b.blockchain, msg, block.Header(), vm.Config{})
+
+ backend := &core.EVMBackend{
+ GetHashFn: core.GetHashFn(block.ParentHash(), b.blockchain),
+ State: statedb,
+ }
+ context := core.ToEVMContext(chainConfig, msg, block.Header())
+ vmenv := vm.NewEnvironment(context, backend, chainConfig, vm.Config{})
+
gaspool := new(core.GasPool).AddGas(common.MaxBig)
ret, gasUsed, _, err := core.NewStateTransition(vmenv, msg, gaspool).TransitionDb()
return ret, gasUsed, err
diff --git a/cmd/evm/main.go b/cmd/evm/main.go
index b1a3cd9e20..84b316e387 100644
--- a/cmd/evm/main.go
+++ b/cmd/evm/main.go
@@ -29,7 +29,6 @@ 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"
@@ -47,14 +46,6 @@ 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",
@@ -65,19 +56,49 @@ var (
}
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",
@@ -105,8 +126,6 @@ func init() {
CreateFlag,
DebugFlag,
VerbosityFlag,
- ForceJitFlag,
- DisableJitFlag,
SysStatFlag,
CodeFlag,
CodeFileFlag,
@@ -115,6 +134,11 @@ func init() {
ValueFlag,
DumpFlag,
InputFlag,
+ GasLimitFlag,
+ CoinbaseFlag,
+ SenderFlag,
+ BlockNumberFlag,
+ BlockTimeFlag,
}
app.Action = run
}
@@ -124,23 +148,49 @@ func run(ctx *cli.Context) error {
glog.SetV(ctx.GlobalInt(VerbosityFlag.Name))
db, _ := ethdb.NewMemDatabase()
- statedb, _ := state.New(common.Hash{}, db)
- sender := statedb.CreateAccount(common.StringToAddress("sender"))
logger := vm.NewStructLogger(nil)
- vmenv := NewEnv(statedb, 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,
- })
- 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,
+ params.TestChainConfig,
+ vm.Config{
+ Debug: ctx.GlobalBool(DebugFlag.Name),
+ Tracer: logger,
+ },
+ )
var (
- code []byte
- ret []byte
- err error
+ code []byte
+ ret []byte
+ tstart = time.Now()
+ sender = st.CreateAccount(context.Origin)
)
if ctx.GlobalString(CodeFlag.Name) != "" {
@@ -174,8 +224,7 @@ func run(ctx *cli.Context) error {
common.Big(ctx.GlobalString(ValueFlag.Name)),
)
} else {
- receiver := statedb.CreateAccount(common.StringToAddress("receiver"))
-
+ receiver := st.CreateAccount(common.StringToAddress("receiver"))
receiver.SetCode(crypto.Keccak256Hash(code), code)
ret, err = vmenv.Call(
sender,
@@ -188,8 +237,7 @@ func run(ctx *cli.Context) error {
vmdone := time.Since(tstart)
if ctx.GlobalBool(DumpFlag.Name) {
- statedb.Commit(true)
- fmt.Println(string(statedb.Dump()))
+ fmt.Println(string(st.Dump()))
}
vm.StdErrFormat(logger.StructLogs())
@@ -220,89 +268,3 @@ func main() {
os.Exit(1)
}
}
-
-type VMEnv struct {
- state *state.StateDB
- 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(state *state.StateDB, transactor common.Address, value, price *big.Int, cfg vm.Config) *VMEnv {
- env := &VMEnv{
- state: state,
- transactor: &transactor,
- value: value,
- price: price,
- time: big.NewInt(time.Now().Unix()),
- }
-
- env.evm = vm.New(env, cfg)
- return env
-}
-
-// ruleSet implements vm.ChainConfig and will always default to the homestead rule set.
-type ruleSet struct{}
-
-func (ruleSet) IsHomestead(*big.Int) bool { return true }
-func (ruleSet) GasTable(*big.Int) params.GasTable {
- return params.GasTableHomesteadGasRepriceFork
-}
-
-func (self *VMEnv) ChainConfig() *params.ChainConfig { return params.TestChainConfig }
-func (self *VMEnv) Vm() vm.Vm { return self.evm }
-func (self *VMEnv) Db() vm.Database { return self.state }
-func (self *VMEnv) SnapshotDatabase() int { return self.state.Snapshot() }
-func (self *VMEnv) RevertToSnapshot(snap int) { self.state.RevertToSnapshot(snap) }
-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) 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 bbc34f6ac8..abcbbafeb6 100644
--- a/common/registrar/ethreg/api.go
+++ b/common/registrar/ethreg/api.go
@@ -195,7 +195,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..b19e7545bb
--- /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"
+ "github.com/ethereum/go-ethereum/params"
+)
+
+// ToEVMContext creates a new context for use in the EVM.
+func ToEVMContext(config *params.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.StateDB
+}
+
+// MakeSnapshot returns a copy of the state.
+func (b *EVMBackend) SnapshotDatabase() int {
+ return b.State.Snapshot()
+}
+
+// Get returns the state
+func (b *EVMBackend) Get() vm.Database { return b.State }
+
+// Set sets the current state
+func (b *EVMBackend) RevertToSnapshot(revision int) { b.State.RevertToSnapshot(revision) }
+
+// 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 00a856d22c..05eea24044 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(true, env, caller, &addr, &addr, env.Db().GetCodeHash(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(true, env, caller, &addr, &addr, env.Db().GetCodeHash(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(false, env, caller, &callerAddr, &addr, env.Db().GetCodeHash(addr), input, env.Db().GetCode(addr), gas, value)
+ ret, _, err = c.exec(false, env, caller, &callerAddr, &addr, env.Db().GetCodeHash(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, env.Db().GetCodeHash(addr), input, env.Db().GetCode(addr), gas, callerValue)
+ ret, _, err = c.execDelegateCall(env, caller, &originAddr, &callerAddr, &addr, env.Db().GetCodeHash(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(true, env, caller, nil, nil, crypto.Keccak256Hash(code), 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(true, env, caller, nil, nil, crypto.Keccak256Hash(code), 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(transfers bool, env vm.Environment, caller vm.ContractRef, address, codeAddr *common.Address, codeHash common.Hash, input, code []byte, gas, value *big.Int) (ret []byte, addr common.Address, err error) {
+func (c EVMCallContext) exec(transfers bool, env *vm.Environment, caller vm.ContractRef, address, codeAddr *common.Address, codeHash common.Hash, 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()))
@@ -85,14 +90,14 @@ func exec(transfers bool, env vm.Environment, caller vm.ContractRef, address, co
createAccount = true
}
- snapshotPreTransfer := env.SnapshotDatabase()
+ snapshotPreTransfer := env.Backend.SnapshotDatabase()
var (
from = env.Db().GetAccount(caller.Address())
to vm.Account
)
if createAccount {
to = env.Db().CreateAccount(*address)
- if env.ChainConfig().IsEIP158(env.BlockNumber()) {
+ if env.ChainConfig().IsEIP158(env.BlockNumber) {
env.Db().SetNonce(*address, 1)
}
} else {
@@ -103,7 +108,7 @@ func exec(transfers bool, env vm.Environment, caller vm.ContractRef, address, co
}
}
if transfers {
- 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
@@ -131,25 +136,25 @@ func exec(transfers bool, env vm.Environment, caller vm.ContractRef, address, co
// 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.ChainConfig().IsHomestead(env.BlockNumber()) || err != vm.CodeStoreOutOfGasError) {
+ if err != nil && (env.ChainConfig().IsHomestead(env.BlockNumber) || err != vm.CodeStoreOutOfGasError) {
contract.UseGas(contract.Gas())
- env.RevertToSnapshot(snapshotPreTransfer)
+ env.Backend.RevertToSnapshot(snapshotPreTransfer)
}
return ret, addr, err
}
-func execDelegateCall(env vm.Environment, caller vm.ContractRef, originAddr, toAddr, codeAddr *common.Address, codeHash common.Hash, 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, codeHash common.Hash, 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.SnapshotDatabase()
+ snapshot := env.Backend.SnapshotDatabase()
var to vm.Account
if !env.Db().Exist(*toAddr) {
@@ -167,14 +172,8 @@ func execDelegateCall(env vm.Environment, caller vm.ContractRef, originAddr, toA
if err != nil {
contract.UseGas(contract.Gas())
- env.RevertToSnapshot(snapshot)
+ env.Backend.RevertToSnapshot(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 8294d61b9f..aa79f96726 100644
--- a/core/state/dump.go
+++ b/core/state/dump.go
@@ -67,6 +67,24 @@ func (self *StateDB) RawDump() Dump {
}
dump.Accounts[common.Bytes2Hex(addr)] = account
}
+ for addr, stateObject := range self.stateObjects {
+ account := DumpAccount{
+ Balance: stateObject.Balance().String(),
+ Nonce: stateObject.Nonce(),
+ Root: common.Bytes2Hex(stateObject.data.Root[:]),
+ CodeHash: common.Bytes2Hex(stateObject.CodeHash()),
+ Code: common.Bytes2Hex(stateObject.Code(self.db)),
+ Storage: make(map[string]string),
+ }
+ storageIt := stateObject.getTrie(self.db).Iterator()
+ for storageIt.Next() {
+ account.Storage[common.Bytes2Hex(self.trie.GetKey(storageIt.Key))] = common.Bytes2Hex(storageIt.Value)
+ }
+ for storageAddr, value := range stateObject.cachedStorage {
+ account.Storage[common.Bytes2Hex(storageAddr[:])] = value.Hex()
+ }
+ dump.Accounts[common.Bytes2Hex(addr[:])] = account
+ }
return dump
}
diff --git a/core/state/statedb.go b/core/state/statedb.go
index c6abfdc801..2fd5518237 100644
--- a/core/state/statedb.go
+++ b/core/state/statedb.go
@@ -302,6 +302,13 @@ func (self *StateDB) AddBalance(addr common.Address, amount *big.Int) {
}
}
+func (self *StateDB) SubBalance(addr common.Address, amount *big.Int) {
+ stateObject := self.GetOrNewStateObject(addr)
+ if stateObject != nil {
+ stateObject.SubBalance(amount)
+ }
+}
+
func (self *StateDB) SetBalance(addr common.Address, amount *big.Int) {
stateObject := self.GetOrNewStateObject(addr)
if stateObject != nil {
diff --git a/core/state_processor.go b/core/state_processor.go
index 9d78400627..ba7590f6e5 100644
--- a/core/state_processor.go
+++ b/core/state_processor.go
@@ -91,7 +91,15 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
// ApplyTransactions returns the generated receipts and vm logs during the
// execution of the state transition phase.
func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, cfg vm.Config) (*types.Receipt, vm.Logs, *big.Int, error) {
- _, gas, err := ApplyMessage(NewEnv(statedb, config, bc, tx, header, cfg), tx, gp)
+ backend := &EVMBackend{
+ GetHashFn: GetHashFn(header.ParentHash, bc),
+ State: statedb,
+ }
+ context := ToEVMContext(config, tx, header)
+
+ env := vm.NewEnvironment(context, backend, config, cfg)
+
+ _, gas, err := ApplyMessage(env, tx, gp)
if err != nil {
return nil, nil, nil, err
}
@@ -106,13 +114,12 @@ func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, gp *GasPool, s
receipt.ContractAddress = crypto.CreateAddress(from, tx.Nonce())
}
- logs := statedb.GetLogs(tx.Hash())
- receipt.Logs = logs
+ receipt.Logs = statedb.GetLogs(tx.Hash())
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
glog.V(logger.Debug).Infoln(receipt)
- return receipt, logs, gas, err
+ return 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 0acc290475..5c0765e982 100644
--- a/core/state_transition.go
+++ b/core/state_transition.go
@@ -57,7 +57,7 @@ type StateTransition struct {
data []byte
state vm.Database
- env vm.Environment
+ env *vm.Environment
}
// Message represents a message sent to a contract.
@@ -106,7 +106,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,
@@ -127,7 +127,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()
@@ -139,7 +139,7 @@ func (self *StateTransition) from() (vm.Account, error) {
f common.Address
err error
)
- if self.env.ChainConfig().IsHomestead(self.env.BlockNumber()) {
+ if self.env.ChainConfig().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.ChainConfig().IsHomestead(self.env.BlockNumber())
+ homestead := self.env.ChainConfig().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.state.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 bed3caa29c..0000000000
--- a/core/vm/environment.go
+++ /dev/null
@@ -1,126 +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"
- "github.com/ethereum/go-ethereum/params"
-)
-
-// Environment is an EVM requirement and helper which allows access to outside
-// information such as states.
-type Environment interface {
- // The current ruleset
- ChainConfig() *params.ChainConfig
- // The state database
- Db() Database
- // Creates a restorable snapshot
- SnapshotDatabase() int
- // Set database to previous snapshot
- RevertToSnapshot(int)
- // 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)
- // Adds a LOG to the state
- AddLog(*Log)
- // 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)
-}
-
-// 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)
-}
-
-// 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)
-
- GetCodeHash(common.Address) common.Hash
- GetCodeSize(common.Address) int
- 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)
-
- Suicide(common.Address) bool
- HasSuicided(common.Address) bool
-
- // Exist reports whether the given account exists in state.
- // Notably this should also return true for suicided accounts.
- Exist(common.Address) bool
- Empty(common.Address) bool
-}
-
-// 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(common.Hash, []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 70b32e8e52..59fc4e02e3 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(vm *EVM, program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error)
+ do(vm *EVM, 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,12 +58,13 @@ func jump(mapping map[uint64]uint64, destinations map[uint64]struct{}, contract
return mapping[to.Uint64()], nil
}
-func (instr instruction) do(vm *EVM, program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func (instr instruction) do(vm *EVM, 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 := calculateGasAndSize(vm.gasTable, env, contract, instr, env.Db(), memory, stack)
if err != nil {
return nil, err
}
+ //fmt.Printf("%04d: %-8v %v\n", *pc, instr.op, cost)
// Use the calculated gas. When insufficient gas is present, use all gas and return an
// Out Of Gas error
@@ -114,26 +115,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)))
@@ -142,7 +143,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))
@@ -162,7 +163,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))
@@ -171,7 +172,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 {
@@ -191,12 +192,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)
@@ -213,12 +214,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))
@@ -227,7 +228,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))
@@ -236,7 +237,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))
@@ -245,7 +246,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))
@@ -254,7 +255,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))
@@ -263,7 +264,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))
@@ -272,19 +273,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()]))
@@ -293,7 +294,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)
@@ -303,7 +304,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)
@@ -314,45 +315,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()
@@ -361,18 +362,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(env.Db().GetCodeSize(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()
@@ -383,7 +384,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()
@@ -395,58 +396,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()
@@ -455,59 +456,59 @@ 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())
- env.AddLog(log)
+ log := NewLog(contract.Address(), topics, d, env.BlockNumber.Uint64())
+ 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()
@@ -515,7 +516,7 @@ func opCreate(instr instruction, pc *uint64, env Environment, contract *Contract
//gas = new(big.Int).SetUint64(contract.gas64)
gas = contract.gas64
)
- if env.ChainConfig().IsEIP150(env.BlockNumber()) {
+ if env.ChainConfig().IsEIP150(env.BlockNumber) {
gas = gas - gas/64
}
@@ -525,7 +526,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.ChainConfig().IsHomestead(env.BlockNumber()) && suberr == CodeStoreOutOfGasError {
+ if env.ChainConfig().IsHomestead(env.BlockNumber) && suberr == CodeStoreOutOfGasError {
stack.push(new(big.Int))
} else if suberr != nil && suberr != CodeStoreOutOfGasError {
stack.push(new(big.Int))
@@ -534,7 +535,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()
@@ -565,7 +566,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()
@@ -596,7 +597,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)
@@ -610,12 +611,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)
@@ -626,7 +627,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++ {
@@ -634,14 +635,14 @@ func makeLog(size int) instrFn {
}
d := memory.Get(mStart.Int64(), mSize.Int64())
- log := NewLog(contract.Address(), topics, d, env.BlockNumber().Uint64())
- env.AddLog(log)
+ 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
@@ -650,7 +651,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))
}
}
@@ -659,7 +660,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..e2fda32e4c
--- /dev/null
+++ b/core/vm/interface.go
@@ -0,0 +1,203 @@
+// 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"
+ "github.com/ethereum/go-ethereum/params"
+)
+
+// 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)
+
+ GetCodeHash(common.Address) common.Hash
+ GetCode(common.Address) []byte
+ SetCode(common.Address, []byte)
+ GetCodeSize(common.Address) int
+
+ AddRefund(*big.Int)
+ GetRefund() *big.Int
+
+ GetState(common.Address, common.Hash) common.Hash
+ SetState(common.Address, common.Hash, common.Hash)
+
+ Suicide(common.Address) bool
+ HasSuicided(common.Address) bool
+
+ // Exist reports whether the given account exists in state.
+ // Notably this should also return true for suicided accounts.
+ Exist(common.Address) bool
+ Empty(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(common.Hash, []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 returns the hash corresponding to n
+ GetHash(n uint64) common.Hash
+ // Creates a restorable snapshot
+ SnapshotDatabase() int
+ // Set database to previous snapshot
+ RevertToSnapshot(int)
+ // Get returns the database
+ Get() Database
+}
+
+type Environment struct {
+ Context
+
+ Backend Backend
+
+ chainConfig *params.ChainConfig
+ vmConfig Config
+
+ evm Vm
+
+ Depth int
+}
+
+func NewEnvironment(context Context, backend Backend, chainConfig *params.ChainConfig, vmCfg Config) *Environment {
+ env := &Environment{
+ Context: context,
+ Backend: backend,
+ vmConfig: vmCfg,
+ chainConfig: chainConfig,
+ }
+ 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)
+}
+
+// The current ruleset
+func (env *Environment) ChainConfig() *params.ChainConfig { return env.chainConfig }
+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 9e13d703be..744372c9e7 100644
--- a/core/vm/logger.go
+++ b/core/vm/logger.go
@@ -65,7 +65,7 @@ type StructLog struct {
// Note that reference types are actual VM data structures; make copies
// if you need to retain them beyond the current call.
type Tracer interface {
- CaptureState(env Environment, pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error
+ CaptureState(env *Environment, pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error
}
// StructLogger is an EVM state logger and implements Tracer.
@@ -94,7 +94,7 @@ func NewStructLogger(cfg *LogConfig) *StructLogger {
// captureState logs a new structured log message and pushes it out to the environment
//
// captureState also tracks SSTORE ops to track dirty values.
-func (l *StructLogger) CaptureState(env Environment, pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error {
+func (l *StructLogger) CaptureState(env *Environment, pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error {
// check if already accumulated the specified number of logs
if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) {
return TraceLimitReachedError
@@ -155,7 +155,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)
return nil
diff --git a/core/vm/logger_test.go b/core/vm/logger_test.go
index a7aa0cfa0e..1009799f92 100644
--- a/core/vm/logger_test.go
+++ b/core/vm/logger_test.go
@@ -21,8 +21,13 @@ import (
"testing"
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/params"
)
+type ruleset struct{}
+
+func (ruleset) IsHomestead(*big.Int) bool { return true }
+
type dummyContractRef struct {
calledForEach bool
}
@@ -41,14 +46,14 @@ func (d *dummyContractRef) SetNonce(uint64) {}
func (d *dummyContractRef) Balance() *big.Int { return new(big.Int) }
type dummyEnv struct {
- *Env
+ *Environment
ref *dummyContractRef
}
func newDummyEnv(ref *dummyContractRef) *dummyEnv {
return &dummyEnv{
- Env: NewEnv(&Config{EnableJit: false, ForceJit: false}),
- ref: ref,
+ Environment: NewEnvironment(Context{}, nil, params.TestChainConfig, Config{}),
+ ref: ref,
}
}
func (d dummyEnv) GetAccount(common.Address) Account {
@@ -57,7 +62,7 @@ func (d dummyEnv) GetAccount(common.Address) Account {
func TestStoreCapture(t *testing.T) {
var (
- env = NewEnv(&Config{EnableJit: false, ForceJit: false})
+ env = NewEnvironment(Context{}, nil, params.TestChainConfig, Config{})
logger = NewStructLogger(nil)
mem = NewMemory()
stack = newstack()
@@ -90,13 +95,13 @@ func TestStorageCapture(t *testing.T) {
stack = newstack()
)
- logger.CaptureState(env, 0, STOP, new(big.Int), new(big.Int), mem, stack, contract, 0, nil)
+ logger.CaptureState(env.Environment, 0, STOP, new(big.Int), new(big.Int), mem, stack, contract, 0, nil)
if ref.calledForEach {
t.Error("didn't expect for each to be called")
}
logger = NewStructLogger(&LogConfig{FullStorage: true})
- logger.CaptureState(env, 0, STOP, new(big.Int), new(big.Int), mem, stack, contract, 0, nil)
+ logger.CaptureState(env.Environment, 0, STOP, new(big.Int), new(big.Int), mem, stack, contract, 0, nil)
if !ref.calledForEach {
t.Error("expected for each to be called")
}
diff --git a/core/vm/program.go b/core/vm/program.go
index ebfe314db6..66e2dcfbb3 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 {
// calculateGasAndSize 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 calculateGasAndSize(gasTable params.GasTable, env Environment, contract *Contract, instr instruction, statedb Database, mem *Memory, stack *Stack) (uint64, uint64, error) {
+func calculateGasAndSize(gasTable params.GasTable, env *Environment, contract *Contract, instr instruction, statedb Database, mem *Memory, stack *Stack) (uint64, uint64, error) {
var (
newMemSize, memGas uint64
sizeFault bool
@@ -329,7 +327,7 @@ func calculateGasAndSize(gasTable params.GasTable, env Environment, contract *Co
gas += gasTable.Suicide
var (
address = common.BigToAddress(stack.data[len(stack.data)-1])
- eip158 = env.ChainConfig().IsEIP158(env.BlockNumber())
+ eip158 = env.ChainConfig().IsEIP158(env.BlockNumber)
)
switch {
@@ -558,7 +556,7 @@ func calculateGasAndSize(gasTable params.GasTable, env Environment, contract *Co
if op == CALL {
var (
address = common.BigToAddress(stack.data[len(stack.data)-2])
- eip158 = env.ChainConfig().IsEIP158(env.BlockNumber())
+ eip158 = env.ChainConfig().IsEIP158(env.BlockNumber)
)
switch {
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/program_test.go b/core/vm/program_test.go
index 337cd363f4..e4694d8ae3 100644
--- a/core/vm/program_test.go
+++ b/core/vm/program_test.go
@@ -16,21 +16,14 @@
package vm
-import (
- "math/big"
- "testing"
- "time"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/params"
-)
+import "testing"
const maxRun = 1000
func TestSegmenting(t *testing.T) {
prog := NewProgram([]byte{byte(PUSH1), 0x1, byte(PUSH1), 0x1, 0x0})
CompileProgram(prog)
+ OptimiseProgram(prog)
if instr, ok := prog.instructions[0].(pushSeg); ok {
if len(instr.data) != 2 {
@@ -42,6 +35,7 @@ func TestSegmenting(t *testing.T) {
prog = NewProgram([]byte{byte(PUSH1), 0x1, byte(PUSH1), 0x1, byte(JUMP)})
CompileProgram(prog)
+ OptimiseProgram(prog)
if _, ok := prog.instructions[1].(jumpSeg); ok {
} else {
@@ -50,6 +44,7 @@ func TestSegmenting(t *testing.T) {
prog = NewProgram([]byte{byte(PUSH1), 0x1, byte(PUSH1), 0x1, byte(PUSH1), 0x1, byte(JUMP)})
CompileProgram(prog)
+ OptimiseProgram(prog)
if instr, ok := prog.instructions[0].(pushSeg); ok {
if len(instr.data) != 2 {
@@ -73,8 +68,9 @@ func TestCompiling(t *testing.T) {
}
}
+/*
func TestResetInput(t *testing.T) {
- var sender account
+ var sender Account
env := NewEnv(&Config{})
contract := NewContract(sender, sender, big.NewInt(100), big.NewInt(10000))
@@ -86,6 +82,7 @@ func TestResetInput(t *testing.T) {
t.Errorf("expected input to be nil, got %x", contract.Input)
}
}
+*/
func TestPcMappingToInstruction(t *testing.T) {
program := NewProgram([]byte{byte(PUSH2), 0xbe, 0xef, byte(ADD)})
@@ -94,112 +91,3 @@ func TestPcMappingToInstruction(t *testing.T) {
t.Error("expected mapping PC 4 to me instr no. 2, got", program.mapping[4])
}
}
-
-var benchmarks = map[string]vmBench{
- "pushes": vmBench{
- false, false, false,
- common.Hex2Bytes("600a600a01600a600a01600a600a01600a600a01600a600a01600a600a01600a600a01600a600a01600a600a01600a600a01"), nil,
- },
-}
-
-func BenchmarkPushes(b *testing.B) {
- runVmBench(benchmarks["pushes"], b)
-}
-
-type vmBench struct {
- precompile bool // compile prior to executing
- nojit bool // ignore jit (sets DisbaleJit = true
- forcejit bool // forces the jit, precompile is ignored
-
- code []byte
- input []byte
-}
-
-type account struct{}
-
-func (account) SubBalance(amount *big.Int) {}
-func (account) AddBalance(amount *big.Int) {}
-func (account) SetAddress(common.Address) {}
-func (account) Value() *big.Int { return nil }
-func (account) SetBalance(*big.Int) {}
-func (account) SetNonce(uint64) {}
-func (account) Balance() *big.Int { return nil }
-func (account) Address() common.Address { return common.Address{} }
-func (account) ReturnGas(uint64) {}
-func (account) SetCode(common.Hash, []byte) {}
-func (account) ForEachStorage(cb func(key, value common.Hash) bool) {}
-
-func runVmBench(test vmBench, b *testing.B) {
- var sender account
-
- if test.precompile && !test.forcejit {
- NewProgram(test.code)
- }
- env := NewEnv(&Config{EnableJit: !test.nojit, ForceJit: test.forcejit})
-
- b.ResetTimer()
-
- for i := 0; i < b.N; i++ {
- context := NewContract(sender, sender, big.NewInt(100), big.NewInt(10000))
- context.Code = test.code
- context.CodeAddr = &common.Address{}
- _, err := env.Vm().Run(context, test.input)
- if err != nil {
- b.Error(err)
- b.FailNow()
- }
- }
-}
-
-type Env struct {
- gasLimit *big.Int
- depth int
- evm *EVM
-}
-
-func NewEnv(config *Config) *Env {
- env := &Env{gasLimit: big.NewInt(10000), depth: 0}
- env.evm = New(env, *config)
- return env
-}
-
-func (self *Env) ChainConfig() *params.ChainConfig {
- return ¶ms.ChainConfig{new(big.Int), new(big.Int), true, new(big.Int), common.Hash{}, new(big.Int)}
-}
-func (self *Env) Vm() Vm { return self.evm }
-func (self *Env) GasPrice() *big.Int { return new(big.Int) }
-func (self *Env) Origin() common.Address { return common.Address{} }
-func (self *Env) BlockNumber() *big.Int { return big.NewInt(0) }
-
-//func (self *Env) PrevHash() []byte { return self.parent }
-func (self *Env) Coinbase() common.Address { return common.Address{} }
-func (self *Env) SnapshotDatabase() int { return 0 }
-func (self *Env) RevertToSnapshot(int) {}
-func (self *Env) Time() *big.Int { return big.NewInt(time.Now().Unix()) }
-func (self *Env) Difficulty() *big.Int { return big.NewInt(0) }
-func (self *Env) Db() Database { return nil }
-func (self *Env) GasLimit() *big.Int { return self.gasLimit }
-func (self *Env) VmType() Type { return 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 *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 true
-}
-func (self *Env) Transfer(from, to Account, amount *big.Int) {}
-func (self *Env) Call(caller ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) {
- return nil, nil
-}
-func (self *Env) CallCode(caller ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) {
- return nil, nil
-}
-func (self *Env) Create(caller ContractRef, data []byte, gas, price *big.Int) ([]byte, common.Address, error) {
- return nil, common.Address{}, nil
-}
-func (self *Env) DelegateCall(me ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error) {
- return nil, nil
-}
diff --git a/core/vm/runtime/env.go b/core/vm/runtime/env.go
deleted file mode 100644
index 7a0531edbd..0000000000
--- a/core/vm/runtime/env.go
+++ /dev/null
@@ -1,118 +0,0 @@
-// 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 .
-
-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"
- "github.com/ethereum/go-ethereum/params"
-)
-
-// Env is a basic runtime environment required for running the EVM.
-type Env struct {
- chainConfig *params.ChainConfig
- depth int
- state *state.StateDB
-
- origin common.Address
- coinbase common.Address
-
- number *big.Int
- time *big.Int
- difficulty *big.Int
- gasLimit *big.Int
- gasPrice *big.Int
-
- getHashFn func(uint64) common.Hash
-
- evm *vm.EVM
-}
-
-// NewEnv returns a new vm.Environment
-func NewEnv(cfg *Config, state *state.StateDB) vm.Environment {
- env := &Env{
- chainConfig: cfg.ChainConfig,
- state: state,
- origin: cfg.Origin,
- coinbase: cfg.Coinbase,
- number: cfg.BlockNumber,
- time: cfg.Time,
- difficulty: cfg.Difficulty,
- gasLimit: cfg.GasLimit,
- gasPrice: cfg.GasPrice,
- }
- env.evm = vm.New(env, vm.Config{
- Debug: cfg.Debug,
- EnableJit: !cfg.DisableJit,
- ForceJit: !cfg.DisableJit,
- })
-
- return env
-}
-
-func (self *Env) ChainConfig() *params.ChainConfig { return self.chainConfig }
-func (self *Env) RuleSet() vm.RuleSet { return self.ruleSet }
-func (self *Env) GasPrice() *big.Int { return self.gasPrice }
-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) 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) SnapshotDatabase() int {
- return self.state.Snapshot()
-}
-func (self *Env) RevertToSnapshot(snapshot int) {
- self.state.RevertToSnapshot(snapshot)
-}
-
-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, value *big.Int) ([]byte, error) {
- return core.Call(self, caller, addr, data, gas, value)
-}
-func (self *Env) 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 *Env) DelegateCall(me vm.ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error) {
- return core.DelegateCall(self, me, addr, data, gas)
-}
-
-func (self *Env) 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/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go
index 6674d12088..0a816ea377 100644
--- a/core/vm/runtime/runtime.go
+++ b/core/vm/runtime/runtime.go
@@ -17,11 +17,14 @@
package runtime
import (
+ "math"
"math/big"
"time"
"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"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/params"
@@ -44,7 +47,7 @@ type Config struct {
Coinbase common.Address
BlockNumber *big.Int
Time *big.Int
- GasLimit *big.Int
+ GasLimit uint64
GasPrice *big.Int
Value *big.Int
DisableJit bool // "disable" so it's enabled by default
@@ -66,8 +69,8 @@ func setDefaults(cfg *Config) {
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.GasLimit == 0 {
+ cfg.GasLimit = math.MaxUint64
}
if cfg.GasPrice == nil {
cfg.GasPrice = new(big.Int)
@@ -101,8 +104,25 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
db, _ := ethdb.NewMemDatabase()
cfg.State, _ = state.New(common.Hash{}, db)
}
+
+ backend := &core.EVMBackend{
+ GetHashFn: cfg.GetHashFn,
+ State: cfg.State,
+ }
+
+ context := vm.Context{
+ CallContext: core.EVMCallContext{core.CanTransfer, core.Transfer},
+ Origin: cfg.Origin,
+ Coinbase: cfg.Coinbase,
+ BlockNumber: cfg.BlockNumber,
+ Time: cfg.Time,
+ Difficulty: cfg.Difficulty,
+ GasLimit: new(big.Int).SetUint64(cfg.GasLimit),
+ GasPrice: cfg.GasPrice,
+ }
+ vmenv := vm.NewEnvironment(context, backend, params.TestChainConfig, vm.Config{})
+
var (
- vmenv = NewEnv(cfg, cfg.State)
sender = cfg.State.CreateAccount(cfg.Origin)
receiver = cfg.State.CreateAccount(common.StringToAddress("contract"))
)
@@ -114,7 +134,7 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
sender,
receiver.Address(),
input,
- cfg.GasLimit,
+ new(big.Int).SetUint64(cfg.GasLimit),
cfg.Value,
)
@@ -129,7 +149,22 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
func Call(address common.Address, input []byte, cfg *Config) ([]byte, error) {
setDefaults(cfg)
- vmenv := NewEnv(cfg, cfg.State)
+ backend := &core.EVMBackend{
+ GetHashFn: cfg.GetHashFn,
+ State: cfg.State,
+ }
+
+ context := vm.Context{
+ CallContext: core.EVMCallContext{core.CanTransfer, core.Transfer},
+ Origin: cfg.Origin,
+ Coinbase: cfg.Coinbase,
+ BlockNumber: cfg.BlockNumber,
+ Time: cfg.Time,
+ Difficulty: cfg.Difficulty,
+ GasLimit: new(big.Int).SetUint64(cfg.GasLimit),
+ GasPrice: cfg.GasPrice,
+ }
+ vmenv := vm.NewEnvironment(context, backend, params.TestChainConfig, vm.Config{})
sender := cfg.State.GetOrNewStateObject(cfg.Origin)
// Call the code with the given configuration.
@@ -137,7 +172,7 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, error) {
sender,
address,
input,
- cfg.GasLimit,
+ new(big.Int).SetUint64(cfg.GasLimit),
cfg.Value,
)
diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go
index 88c76c7319..725dda9a0d 100644
--- a/core/vm/runtime/runtime_test.go
+++ b/core/vm/runtime/runtime_test.go
@@ -39,8 +39,8 @@ func TestDefaults(t *testing.T) {
if cfg.Time == nil {
t.Error("expected time to be non nil")
}
- if cfg.GasLimit == nil {
- t.Error("expected time to be non nil")
+ if cfg.GasLimit == 0 {
+ t.Error("expected time to be non 0")
}
if cfg.GasPrice == nil {
t.Error("expected time to be non nil")
diff --git a/core/vm/segments.go b/core/vm/segments.go
index cac245d243..76e662ea07 100644
--- a/core/vm/segments.go
+++ b/core/vm/segments.go
@@ -24,7 +24,7 @@ type jumpSeg struct {
gas uint64
}
-func (j jumpSeg) do(vm *EVM, program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func (j jumpSeg) do(vm *EVM, 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(vm *EVM, program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func (s pushSeg) do(vm *EVM, 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 8d5c021348..dbd10c0d5e 100644
--- a/core/vm/vm.go
+++ b/core/vm/vm.go
@@ -29,10 +29,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
@@ -40,26 +42,26 @@ 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
gasTable params.GasTable
}
// 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.ChainConfig(), env.BlockNumber()),
+ jumpTable: newJumpTable(env.ChainConfig(), env.BlockNumber),
cfg: cfg,
- gasTable: env.ChainConfig().GasTable(env.BlockNumber()),
+ gasTable: env.ChainConfig().GasTable(env.BlockNumber),
}
}
// 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 {
@@ -87,6 +89,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:
@@ -118,7 +123,7 @@ func (evm *EVM) runProgram(program *Program, contract *Contract, input []byte) (
}()
}
- homestead := env.ChainConfig().IsHomestead(env.BlockNumber())
+ homestead := env.ChainConfig().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 4bd42c0a20..0000000000
--- a/core/vm_env.go
+++ /dev/null
@@ -1,119 +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"
- "github.com/ethereum/go-ethereum/params"
-)
-
-// 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 *params.ChainConfig // Chain configuration
- state *state.StateDB // State to use for executing
- evm *vm.EVM // 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.StateDB, chainConfig *params.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) ChainConfig() *params.ChainConfig { 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) SnapshotDatabase() int {
- return self.state.Snapshot()
-}
-
-func (self *VMEnv) RevertToSnapshot(snapshot int) {
- self.state.RevertToSnapshot(snapshot)
-}
-
-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 7932bbcb6d..be6d460a9b 100644
--- a/eth/api.go
+++ b/eth/api.go
@@ -521,9 +521,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)
@@ -532,7 +540,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 9d8ed1c517..bcea7d7209 100644
--- a/eth/api_backend.go
+++ b/eth/api_backend.go
@@ -97,13 +97,20 @@ 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, state ethapi.State, header *types.Header) (vm.Environment, func() error, error) {
+func (b *EthApiBackend) GetVMEnv(ctx context.Context, msg core.Message, state ethapi.State, header *types.Header) (*vm.Environment, func() error, error) {
statedb := state.(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, vm.Config{}), 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, vm.Config{}), vmError, nil
}
func (b *EthApiBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error {
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/internal/ethapi/tracer.go b/internal/ethapi/tracer.go
index 5f69826a3b..f4dbe404d8 100644
--- a/internal/ethapi/tracer.go
+++ b/internal/ethapi/tracer.go
@@ -278,7 +278,7 @@ func wrapError(context string, err error) error {
}
// CaptureState implements the Tracer interface to trace a single step of VM execution
-func (jst *JavascriptTracer) CaptureState(env vm.Environment, pc uint64, op vm.OpCode, gas, cost *big.Int, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error {
+func (jst *JavascriptTracer) CaptureState(env *vm.Environment, pc uint64, op vm.OpCode, gas, cost *big.Int, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error {
if jst.err == nil {
jst.memory.memory = memory
jst.stack.stack = stack
diff --git a/internal/ethapi/tracer_test.go b/internal/ethapi/tracer_test.go
index b88178a6d7..8ee4ae8f74 100644
--- a/internal/ethapi/tracer_test.go
+++ b/internal/ethapi/tracer_test.go
@@ -24,8 +24,11 @@ import (
"time"
"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"
"github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/params"
)
@@ -37,7 +40,7 @@ type Env struct {
func NewEnv(config *vm.Config) *Env {
env := &Env{gasLimit: big.NewInt(10000), depth: 0}
- env.evm = vm.New(env, *config)
+ //env.evm = vm.New(env, *config)
return env
}
@@ -91,14 +94,20 @@ func (account) SetBalance(*big.Int) {}
func (account) SetNonce(uint64) {}
func (account) Balance() *big.Int { return nil }
func (account) Address() common.Address { return common.Address{} }
-func (account) ReturnGas(*big.Int, *big.Int) {}
+func (account) ReturnGas(uint64) {}
func (account) SetCode(common.Hash, []byte) {}
func (account) ForEachStorage(cb func(key, value common.Hash) bool) {}
func runTrace(tracer *JavascriptTracer) (interface{}, error) {
- env := NewEnv(&vm.Config{Debug: true, Tracer: tracer})
+ db, _ := ethdb.NewMemDatabase()
+ st, _ := state.New(common.Hash{}, db)
+ backend := &core.EVMBackend{
+ GetHashFn: nil,
+ State: st,
+ }
+ env := vm.NewEnvironment(vm.Context{GasLimit: big.NewInt(1000000)}, backend, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
- contract := vm.NewContract(account{}, account{}, big.NewInt(0), env.GasLimit(), big.NewInt(1))
+ contract := vm.NewContract(account{}, account{}, big.NewInt(0), env.GasLimit)
contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0}
_, err := env.Vm().Run(contract, []byte{})
@@ -175,7 +184,7 @@ func TestHalt(t *testing.T) {
tracer.Stop(timeout)
}()
- if _, err = runTrace(tracer); err.Error() != "stahp in server-side tracer function 'step'" {
+ if _, err = runTrace(tracer); err == nil || (err != nil && err.Error() != "stahp in server-side tracer function 'step'") {
t.Errorf("Expected timeout error, got %v", err)
}
}
@@ -186,8 +195,14 @@ func TestHaltBetweenSteps(t *testing.T) {
t.Fatal(err)
}
- env := NewEnv(&vm.Config{Debug: true, Tracer: tracer})
- contract := vm.NewContract(&account{}, &account{}, big.NewInt(0), big.NewInt(0), big.NewInt(0))
+ db, _ := ethdb.NewMemDatabase()
+ st, _ := state.New(common.Hash{}, db)
+ backend := &core.EVMBackend{
+ GetHashFn: nil,
+ State: st,
+ }
+ env := vm.NewEnvironment(vm.Context{GasLimit: big.NewInt(1000000)}, backend, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
+ contract := vm.NewContract(&account{}, &account{}, big.NewInt(0), big.NewInt(0))
tracer.CaptureState(env, 0, 0, big.NewInt(0), big.NewInt(0), nil, nil, contract, 0, nil)
timeout := errors.New("stahp")
diff --git a/tests/state_test_util.go b/tests/state_test_util.go
index 6efa7024ee..4ec7cf158f 100644
--- a/tests/state_test_util.go
+++ b/tests/state_test_util.go
@@ -105,6 +105,8 @@ func benchStateTest(chainConfig *params.ChainConfig, test VmTest, env map[string
}
func runStateTests(chainConfig *params.ChainConfig, tests map[string]VmTest, skipTests []string) error {
+ //glog.SetToStderr(true)
+ //glog.SetV(6)
skipTest := make(map[string]bool, len(skipTests))
for _, name := range skipTests {
skipTest[name] = true
@@ -225,25 +227,23 @@ func RunState(chainConfig *params.ChainConfig, statedb *state.StateDB, env, tx m
to = &t
}
// Set pre compiled contracts
- snapshot := statedb.Snapshot()
gaspool := new(core.GasPool).AddGas(common.Big(env["currentGasLimit"]))
key, _ := hex.DecodeString(tx["secretKey"])
addr := crypto.PubkeyToAddress(crypto.ToECDSA(key).PublicKey)
message := NewMessage(addr, to, data, value, gas, price, nonce)
- vmenv := NewEnvFromMap(chainConfig, statedb, env, tx)
- vmenv.origin = addr
-
root, _ := statedb.Commit(false)
statedb.Reset(root)
snapshot := statedb.Snapshot()
+ vmenv := NewEVMEnvironment(false, chainConfig, statedb, env, tx)
+
ret, _, err := core.ApplyMessage(vmenv, message, gaspool)
if core.IsNonceErr(err) || core.IsInvalidTxErr(err) || core.IsGasLimitErr(err) {
statedb.RevertToSnapshot(snapshot)
}
- statedb.Commit(chainConfig.IsEIP158(vmenv.BlockNumber()))
+ statedb.Commit(chainConfig.IsEIP158(vmenv.BlockNumber))
- return ret, vmenv.state.Logs(), vmenv.Gas, err
+ return ret, statedb.Logs(), gas, err
}
diff --git a/tests/util.go b/tests/util.go
index f4b229c017..c9d67abdd4 100644
--- a/tests/util.go
+++ b/tests/util.go
@@ -18,6 +18,7 @@ package tests
import (
"bytes"
+ "encoding/hex"
"fmt"
"math/big"
"os"
@@ -148,141 +149,60 @@ type VmTest struct {
PostStateRoot string
}
-type Env struct {
- chainConfig *params.ChainConfig
- depth int
- state *state.StateDB
- skipTransfer bool
- initial bool
- Gas, gasPrice *big.Int
-
- 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
+type RuleSet struct {
+ HomesteadBlock *big.Int
+ DAOForkBlock *big.Int
+ DAOForkSupport bool
}
-func NewEnv(chainConfig *params.ChainConfig, state *state.StateDB) *Env {
- env := &Env{
- chainConfig: chainConfig,
- state: state,
+func (r RuleSet) IsHomestead(n *big.Int) bool {
+ return n.Cmp(r.HomesteadBlock) >= 0
+}
+
+func NewEVMEnvironment(vmTest bool, chainConfig *params.ChainConfig, state *state.StateDB, envValues map[string]string, exeValues map[string]string) *vm.Environment {
+ 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(chainConfig *params.ChainConfig, state *state.StateDB, envValues map[string]string, exeValues map[string]string) *Env {
- env := NewEnv(chainConfig, 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) ChainConfig() *params.ChainConfig { return self.chainConfig }
-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) SnapshotDatabase() int {
- return self.state.Snapshot()
-}
-func (self *Env) RevertToSnapshot(snapshot int) {
- self.state.RevertToSnapshot(snapshot)
-}
-
-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, chainConfig, vm.Config{Test: vmTest})
+ return env
}
type Message struct {
diff --git a/tests/vm_test_util.go b/tests/vm_test_util.go
index 53fc7a494f..07a92c64f8 100644
--- a/tests/vm_test_util.go
+++ b/tests/vm_test_util.go
@@ -211,7 +211,7 @@ func runVmTest(test VmTest) error {
return nil
}
-func RunVm(state *state.StateDB, env, exec map[string]string) ([]byte, vm.Logs, *big.Int, error) {
+func RunVm(statedb *state.StateDB, env, exec map[string]string) ([]byte, vm.Logs, *big.Int, error) {
var (
to = common.HexToAddress(exec["address"])
from = common.HexToAddress(exec["caller"])
@@ -222,13 +222,11 @@ func RunVm(state *state.StateDB, env, exec map[string]string) ([]byte, vm.Logs,
// Reset the pre-compiled contracts for VM tests.
vm.PrecompiledContracts = make(map[common.Address]vm.PrecompiledContract)
- caller := state.GetOrNewStateObject(from)
+ caller := statedb.GetOrNewStateObject(from)
- vmenv := NewEnvFromMap(¶ms.ChainConfig{params.MainNetHomesteadBlock, params.MainNetDAOForkBlock, true, nil, common.Hash{}, nil}, state, env, exec)
- vmenv.vmTest = true
- vmenv.skipTransfer = true
- vmenv.initial = true
+ config := ¶ms.ChainConfig{params.MainNetHomesteadBlock, params.MainNetDAOForkBlock, true, nil, common.Hash{}, nil}
+ vmenv := NewEVMEnvironment(true, config, statedb, env, exec)
ret, err := vmenv.Call(caller, to, data, gas, value)
- return ret, vmenv.state.Logs(), vmenv.Gas, err
+ return ret, statedb.Logs(), gas, err
}