aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--accounts/abi/bind/backends/simulated.go6
-rw-r--r--cmd/evm/main.go139
-rw-r--r--core/evm.go73
-rw-r--r--core/execution.go217
-rw-r--r--core/state/state_object.go2
-rw-r--r--core/state/statedb.go9
-rw-r--r--core/state_processor.go22
-rw-r--r--core/state_transition.go51
-rw-r--r--core/vm/contract.go13
-rw-r--r--core/vm/environment.go363
-rw-r--r--core/vm/errors.go11
-rw-r--r--core/vm/instructions.go202
-rw-r--r--core/vm/interface.go97
-rw-r--r--core/vm/jit.go18
-rw-r--r--core/vm/jit_test.go66
-rw-r--r--core/vm/logger.go8
-rw-r--r--core/vm/logger_test.go29
-rw-r--r--core/vm/noop.go68
-rw-r--r--core/vm/runtime/env.go98
-rw-r--r--core/vm/runtime/runtime.go29
-rw-r--r--core/vm/segments.go4
-rw-r--r--core/vm/util_test.go32
-rw-r--r--core/vm/vm.go60
-rw-r--r--core/vm/vm_jit_fake.go7
-rw-r--r--core/vm_env.go101
-rw-r--r--eth/api.go8
-rw-r--r--eth/api_backend.go6
-rw-r--r--internal/ethapi/backend.go2
-rw-r--r--internal/ethapi/tracer.go6
-rw-r--r--internal/ethapi/tracer_test.go65
-rw-r--r--les/api_backend.go8
-rw-r--r--les/odr_test.go15
-rw-r--r--light/odr_test.go13
-rw-r--r--light/state.go9
-rw-r--r--light/state_object.go2
-rw-r--r--light/vm_env.go119
-rw-r--r--tests/state_test_util.go29
-rw-r--r--tests/util.go171
-rw-r--r--tests/vm_test_util.go27
39 files changed, 952 insertions, 1253 deletions
diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go
index 00a8cd3e9..06bd13cae 100644
--- a/accounts/abi/bind/backends/simulated.go
+++ b/accounts/abi/bind/backends/simulated.go
@@ -225,7 +225,11 @@ 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{})
+
+ evmContext := core.NewEVMContext(msg, block.Header(), b.blockchain)
+ // Create a new environment which holds all relevant information
+ // about the transaction and calling mechanisms.
+ vmenv := vm.NewEnvironment(evmContext, statedb, 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 2c4329fa5..993dd7659 100644
--- a/cmd/evm/main.go
+++ b/cmd/evm/main.go
@@ -20,21 +20,18 @@ package main
import (
"fmt"
"io/ioutil"
- "math/big"
"os"
- "runtime"
+ goruntime "runtime"
"time"
"github.com/ethereum/go-ethereum/cmd/utils"
"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/core/vm/runtime"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/logger/glog"
- "github.com/ethereum/go-ethereum/params"
"gopkg.in/urfave/cli.v1"
)
@@ -129,13 +126,6 @@ func run(ctx *cli.Context) error {
logger := vm.NewStructLogger(nil)
- vmenv := NewEnv(statedb, common.StringToAddress("evmuser"), common.Big(ctx.GlobalString(ValueFlag.Name)), vm.Config{
- Debug: ctx.GlobalBool(DebugFlag.Name),
- ForceJit: ctx.GlobalBool(ForceJitFlag.Name),
- EnableJit: !ctx.GlobalBool(DisableJitFlag.Name),
- Tracer: logger,
- })
-
tstart := time.Now()
var (
@@ -168,25 +158,30 @@ func run(ctx *cli.Context) error {
if ctx.GlobalBool(CreateFlag.Name) {
input := append(code, common.Hex2Bytes(ctx.GlobalString(InputFlag.Name))...)
- ret, _, err = vmenv.Create(
- sender,
- input,
- common.Big(ctx.GlobalString(GasFlag.Name)),
- common.Big(ctx.GlobalString(PriceFlag.Name)),
- common.Big(ctx.GlobalString(ValueFlag.Name)),
- )
+ ret, _, err = runtime.Create(input, &runtime.Config{
+ Origin: sender.Address(),
+ State: statedb,
+ GasLimit: common.Big(ctx.GlobalString(GasFlag.Name)),
+ GasPrice: common.Big(ctx.GlobalString(PriceFlag.Name)),
+ Value: common.Big(ctx.GlobalString(ValueFlag.Name)),
+ EVMConfig: vm.Config{
+ Tracer: logger,
+ },
+ })
} else {
receiver := statedb.CreateAccount(common.StringToAddress("receiver"))
-
receiver.SetCode(crypto.Keccak256Hash(code), code)
- ret, err = vmenv.Call(
- sender,
- receiver.Address(),
- common.Hex2Bytes(ctx.GlobalString(InputFlag.Name)),
- common.Big(ctx.GlobalString(GasFlag.Name)),
- common.Big(ctx.GlobalString(PriceFlag.Name)),
- common.Big(ctx.GlobalString(ValueFlag.Name)),
- )
+
+ ret, err = runtime.Call(receiver.Address(), common.Hex2Bytes(ctx.GlobalString(InputFlag.Name)), &runtime.Config{
+ Origin: sender.Address(),
+ State: statedb,
+ GasLimit: common.Big(ctx.GlobalString(GasFlag.Name)),
+ GasPrice: common.Big(ctx.GlobalString(PriceFlag.Name)),
+ Value: common.Big(ctx.GlobalString(ValueFlag.Name)),
+ EVMConfig: vm.Config{
+ Tracer: logger,
+ },
+ })
}
vmdone := time.Since(tstart)
@@ -197,8 +192,8 @@ func run(ctx *cli.Context) error {
vm.StdErrFormat(logger.StructLogs())
if ctx.GlobalBool(SysStatFlag.Name) {
- var mem runtime.MemStats
- runtime.ReadMemStats(&mem)
+ var mem goruntime.MemStats
+ goruntime.ReadMemStats(&mem)
fmt.Printf("vm took %v\n", vmdone)
fmt.Printf(`alloc: %d
tot alloc: %d
@@ -223,87 +218,3 @@ func main() {
os.Exit(1)
}
}
-
-type VMEnv struct {
- state *state.StateDB
- block *types.Block
-
- transactor *common.Address
- value *big.Int
-
- depth int
- Gas *big.Int
- time *big.Int
- logs []vm.StructLog
-
- evm *vm.EVM
-}
-
-func NewEnv(state *state.StateDB, transactor common.Address, value *big.Int, cfg vm.Config) *VMEnv {
- env := &VMEnv{
- state: state,
- transactor: &transactor,
- value: value,
- 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) 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, price, value *big.Int) ([]byte, error) {
- self.Gas = gas
- return core.Call(self, caller, addr, data, gas, price, value)
-}
-
-func (self *VMEnv) CallCode(caller vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
- return core.CallCode(self, caller, addr, data, gas, price, value)
-}
-
-func (self *VMEnv) DelegateCall(caller vm.ContractRef, addr common.Address, data []byte, gas, price *big.Int) ([]byte, error) {
- return core.DelegateCall(self, caller, addr, data, gas, price)
-}
-
-func (self *VMEnv) Create(caller vm.ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) {
- return core.Create(self, caller, data, gas, price, value)
-}
diff --git a/core/evm.go b/core/evm.go
new file mode 100644
index 000000000..6a5713075
--- /dev/null
+++ b/core/evm.go
@@ -0,0 +1,73 @@
+// Copyright 2014 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package core
+
+import (
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/core/vm"
+)
+
+// BlockFetcher retrieves headers by their hash
+type HeaderFetcher interface {
+ // GetHeader returns the hash corresponding to their hash
+ GetHeader(common.Hash, uint64) *types.Header
+}
+
+// NewEVMContext creates a new context for use in the EVM.
+func NewEVMContext(msg Message, header *types.Header, chain HeaderFetcher) vm.Context {
+ return vm.Context{
+ CanTransfer: CanTransfer,
+ Transfer: Transfer,
+ GetHash: GetHashFn(header, chain),
+
+ Origin: msg.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 GetHashFunc which retrieves header hashes by number
+func GetHashFn(ref *types.Header, chain HeaderFetcher) func(n uint64) common.Hash {
+ return func(n uint64) common.Hash {
+ for header := chain.GetHeader(ref.ParentHash, ref.Number.Uint64()-1); header != nil; header = chain.GetHeader(header.ParentHash, header.Number.Uint64()-1) {
+ if header.Number.Uint64() == n {
+ return header.Hash()
+ }
+ }
+
+ return common.Hash{}
+ }
+}
+
+// CanTransfer checks wether there are enough funds in the address' account to make a transfer.
+// This does not take the necessary gas in to account to make the transfer valid.
+func CanTransfer(db vm.StateDB, 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.StateDB, sender, recipient common.Address, amount *big.Int) {
+ db.SubBalance(sender, amount)
+ db.AddBalance(recipient, amount)
+}
diff --git a/core/execution.go b/core/execution.go
deleted file mode 100644
index e3ea1006c..000000000
--- a/core/execution.go
+++ /dev/null
@@ -1,217 +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 <http://www.gnu.org/licenses/>.
-
-package core
-
-import (
- "math/big"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/core/vm"
- "github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/params"
-)
-
-// Call executes within the given contract
-func Call(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, gasPrice, value *big.Int) (ret []byte, err error) {
- // Depth check execution. Fail if we're trying to execute above the
- // limit.
- if env.Depth() > int(params.CallCreateDepth.Int64()) {
- caller.ReturnGas(gas, gasPrice)
-
- return nil, vm.DepthError
- }
- if !env.CanTransfer(caller.Address(), value) {
- caller.ReturnGas(gas, gasPrice)
-
- return nil, ValueTransferErr("insufficient funds to transfer value. Req %v, has %v", value, env.Db().GetBalance(caller.Address()))
- }
-
- snapshotPreTransfer := env.SnapshotDatabase()
- var (
- from = env.Db().GetAccount(caller.Address())
- to vm.Account
- )
- if !env.Db().Exist(addr) {
- if vm.Precompiled[addr.Str()] == nil && env.ChainConfig().IsEIP158(env.BlockNumber()) && value.BitLen() == 0 {
- caller.ReturnGas(gas, gasPrice)
- return nil, nil
- }
-
- to = env.Db().CreateAccount(addr)
- } else {
- to = env.Db().GetAccount(addr)
- }
- env.Transfer(from, to, value)
-
- // initialise a new contract and set the code that is to be used by the
- // EVM. The contract is a scoped environment for this execution context
- // only.
- contract := vm.NewContract(caller, to, value, gas, gasPrice)
- contract.SetCallCode(&addr, env.Db().GetCodeHash(addr), env.Db().GetCode(addr))
- defer contract.Finalise()
-
- ret, err = env.Vm().Run(contract, input)
- // 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 {
- contract.UseGas(contract.Gas)
-
- env.RevertToSnapshot(snapshotPreTransfer)
- }
- 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, gasPrice, value *big.Int) (ret []byte, err error) {
- // Depth check execution. Fail if we're trying to execute above the
- // limit.
- if env.Depth() > int(params.CallCreateDepth.Int64()) {
- caller.ReturnGas(gas, gasPrice)
-
- return nil, vm.DepthError
- }
- if !env.CanTransfer(caller.Address(), value) {
- caller.ReturnGas(gas, gasPrice)
-
- return nil, ValueTransferErr("insufficient funds to transfer value. Req %v, has %v", value, env.Db().GetBalance(caller.Address()))
- }
-
- var (
- snapshotPreTransfer = env.SnapshotDatabase()
- to = env.Db().GetAccount(caller.Address())
- )
- // initialise a new contract and set the code that is to be used by the
- // EVM. The contract is a scoped environment for this execution context
- // only.
- contract := vm.NewContract(caller, to, value, gas, gasPrice)
- contract.SetCallCode(&addr, env.Db().GetCodeHash(addr), env.Db().GetCode(addr))
- defer contract.Finalise()
-
- ret, err = env.Vm().Run(contract, input)
- if err != nil {
- contract.UseGas(contract.Gas)
-
- env.RevertToSnapshot(snapshotPreTransfer)
- }
-
- return ret, err
-}
-
-// Create creates a new contract with the given code
-func Create(env vm.Environment, caller vm.ContractRef, code []byte, gas, gasPrice, value *big.Int) (ret []byte, address common.Address, err error) {
- // Depth check execution. Fail if we're trying to execute above the
- // limit.
- if env.Depth() > int(params.CallCreateDepth.Int64()) {
- caller.ReturnGas(gas, gasPrice)
-
- return nil, common.Address{}, vm.DepthError
- }
- if !env.CanTransfer(caller.Address(), value) {
- caller.ReturnGas(gas, gasPrice)
-
- return nil, common.Address{}, ValueTransferErr("insufficient funds to transfer value. Req %v, has %v", value, env.Db().GetBalance(caller.Address()))
- }
-
- // Create a new account on the state
- nonce := env.Db().GetNonce(caller.Address())
- env.Db().SetNonce(caller.Address(), nonce+1)
-
- snapshotPreTransfer := env.SnapshotDatabase()
- var (
- addr = crypto.CreateAddress(caller.Address(), nonce)
- from = env.Db().GetAccount(caller.Address())
- to = env.Db().CreateAccount(addr)
- )
- if env.ChainConfig().IsEIP158(env.BlockNumber()) {
- env.Db().SetNonce(addr, 1)
- }
- env.Transfer(from, to, value)
-
- // initialise a new contract and set the code that is to be used by the
- // EVM. The contract is a scoped environment for this execution context
- // only.
- contract := vm.NewContract(caller, to, value, gas, gasPrice)
- contract.SetCallCode(&addr, crypto.Keccak256Hash(code), code)
- defer contract.Finalise()
-
- ret, err = env.Vm().Run(contract, nil)
- // check whether the max code size has been exceeded
- maxCodeSizeExceeded := len(ret) > params.MaxCodeSize
- // if the contract creation ran successfully and no errors were returned
- // calculate the gas required to store the code. If the code could not
- // be stored due to not enough gas set an error and let it be handled
- // by the error checking condition below.
- if err == nil && !maxCodeSizeExceeded {
- dataGas := big.NewInt(int64(len(ret)))
- dataGas.Mul(dataGas, params.CreateDataGas)
- if contract.UseGas(dataGas) {
- env.Db().SetCode(addr, ret)
- } else {
- err = vm.CodeStoreOutOfGasError
- }
- }
-
- // 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 maxCodeSizeExceeded ||
- (err != nil && (env.ChainConfig().IsHomestead(env.BlockNumber()) || err != vm.CodeStoreOutOfGasError)) {
- contract.UseGas(contract.Gas)
- env.RevertToSnapshot(snapshotPreTransfer)
-
- // Nothing should be returned when an error is thrown.
- return nil, addr, err
- }
-
- return ret, addr, 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, gasPrice *big.Int) (ret []byte, err error) {
- // Depth check execution. Fail if we're trying to execute above the
- // limit.
- if env.Depth() > int(params.CallCreateDepth.Int64()) {
- caller.ReturnGas(gas, gasPrice)
- return nil, vm.DepthError
- }
-
- var (
- snapshot = env.SnapshotDatabase()
- to = env.Db().GetAccount(caller.Address())
- )
-
- // Iinitialise a new contract and make initialise the delegate values
- contract := vm.NewContract(caller, to, caller.Value(), gas, gasPrice).AsDelegate()
- contract.SetCallCode(&addr, env.Db().GetCodeHash(addr), env.Db().GetCode(addr))
- defer contract.Finalise()
-
- ret, err = env.Vm().Run(contract, input)
- if err != nil {
- contract.UseGas(contract.Gas)
-
- env.RevertToSnapshot(snapshot)
- }
-
- return ret, err
-}
-
-// generic transfer method
-func Transfer(from, to vm.Account, amount *big.Int) {
- from.SubBalance(amount)
- to.AddBalance(amount)
-}
diff --git a/core/state/state_object.go b/core/state/state_object.go
index d40b42d83..87aa8ccd6 100644
--- a/core/state/state_object.go
+++ b/core/state/state_object.go
@@ -288,7 +288,7 @@ func (self *StateObject) setBalance(amount *big.Int) {
}
// Return the gas back to the origin. Used by the Virtual machine or Closures
-func (c *StateObject) ReturnGas(gas, price *big.Int) {}
+func (c *StateObject) ReturnGas(gas *big.Int) {}
func (self *StateObject) deepCopy(db *StateDB, onDirty func(addr common.Address)) *StateObject {
stateObject := newObject(db, self.address, self.data, onDirty)
diff --git a/core/state/statedb.go b/core/state/statedb.go
index 3742c178b..82e2ec7c1 100644
--- a/core/state/statedb.go
+++ b/core/state/statedb.go
@@ -293,6 +293,7 @@ func (self *StateDB) HasSuicided(addr common.Address) bool {
* SETTERS
*/
+// AddBalance adds amount to the account associated with addr
func (self *StateDB) AddBalance(addr common.Address, amount *big.Int) {
stateObject := self.GetOrNewStateObject(addr)
if stateObject != nil {
@@ -300,6 +301,14 @@ func (self *StateDB) AddBalance(addr common.Address, amount *big.Int) {
}
}
+// SubBalance subtracts amount from the account associated with addr
+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 e346917c3..67a7ad5a1 100644
--- a/core/state_processor.go
+++ b/core/state_processor.go
@@ -96,28 +96,36 @@ func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, gp *GasPool, s
if err != nil {
return nil, nil, nil, err
}
-
- _, gas, err := ApplyMessage(NewEnv(statedb, config, bc, msg, header, cfg), msg, gp)
+ // Create a new context to be used in the EVM environment
+ context := NewEVMContext(msg, header, bc)
+ // Create a new environment which holds all relevant information
+ // about the transaction and calling mechanisms.
+ vmenv := vm.NewEnvironment(context, statedb, config, vm.Config{})
+ // Apply the transaction to the current state (included in the env)
+ _, gas, err := ApplyMessage(vmenv, msg, gp)
if err != nil {
return nil, nil, nil, err
}
// Update the state with pending changes
usedGas.Add(usedGas, gas)
+ // Create a new receipt for the transaction, storing the intermediate root and gas used by the tx
+ // based on the eip phase, we're passing wether the root touch-delete accounts.
receipt := types.NewReceipt(statedb.IntermediateRoot(config.IsEIP158(header.Number)).Bytes(), usedGas)
receipt.TxHash = tx.Hash()
receipt.GasUsed = new(big.Int).Set(gas)
- if MessageCreatesContract(msg) {
- receipt.ContractAddress = crypto.CreateAddress(msg.From(), tx.Nonce())
+ // if the transaction created a contract, store the creation address in the receipt.
+ if msg.To() == nil {
+ receipt.ContractAddress = crypto.CreateAddress(vmenv.Context.Origin, tx.Nonce())
}
- logs := statedb.GetLogs(tx.Hash())
- receipt.Logs = logs
+ // Set the receipt logs and create a bloom for filtering
+ 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 8abe17b0a..48540be14 100644
--- a/core/state_transition.go
+++ b/core/state_transition.go
@@ -55,9 +55,9 @@ type StateTransition struct {
initialGas *big.Int
value *big.Int
data []byte
- state vm.Database
+ state vm.StateDB
- 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,
@@ -116,7 +116,7 @@ func NewStateTransition(env vm.Environment, msg Message, gp *GasPool) *StateTran
initialGas: new(big.Int),
value: msg.Value(),
data: msg.Data(),
- state: env.Db(),
+ state: env.StateDB,
}
}
@@ -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()
@@ -217,47 +217,44 @@ 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 {
return nil, nil, nil, InvalidTxError(err)
}
- vmenv := self.env
- //var addr common.Address
+ var (
+ vmenv = self.env
+ // vm errors do not effect consensus and are therefor
+ // not assigned to err, except for insufficient balance
+ // error.
+ vmerr error
+ )
if contractCreation {
- ret, _, err = vmenv.Create(sender, self.data, self.gas, self.gasPrice, self.value)
+ ret, _, vmerr = vmenv.Create(sender, self.data, self.gas, self.value)
if homestead && err == vm.CodeStoreOutOfGasError {
self.gas = Big0
}
-
- if err != nil {
- ret = nil
- glog.V(logger.Core).Infoln("VM create err:", err)
- }
} else {
// Increment the nonce for the next transaction
self.state.SetNonce(sender.Address(), self.state.GetNonce(sender.Address())+1)
- ret, err = vmenv.Call(sender, self.to().Address(), self.data, self.gas, self.gasPrice, self.value)
- if err != nil {
- glog.V(logger.Core).Infoln("VM call err:", err)
- }
- }
-
- if err != nil && IsValueTransferErr(err) {
- return nil, nil, nil, InvalidTxError(err)
+ ret, vmerr = vmenv.Call(sender, self.to().Address(), self.data, self.gas, self.value)
}
-
- // We aren't interested in errors here. Errors returned by the VM are non-consensus errors and therefor shouldn't bubble up
- if err != nil {
- err = nil
+ if vmerr != nil {
+ glog.V(logger.Core).Infoln("vm returned with error:", err)
+ // The only possible consensus-error would be if there wasn't
+ // sufficient balance to make the transfer happen. The first
+ // balance transfer may never fail.
+ if vmerr == vm.ErrInsufficientBalance {
+ return nil, nil, nil, InvalidTxError(vmerr)
+ }
}
requiredGas = new(big.Int).Set(self.gasUsed())
self.refundGas()
- self.state.AddBalance(self.env.Coinbase(), new(big.Int).Mul(self.gasUsed(), self.gasPrice))
+ self.state.AddBalance(self.env.Coinbase, new(big.Int).Mul(self.gasUsed(), self.gasPrice))
return ret, requiredGas, self.gasUsed(), err
}
diff --git a/core/vm/contract.go b/core/vm/contract.go
index 70455a4c2..dfa93ab18 100644
--- a/core/vm/contract.go
+++ b/core/vm/contract.go
@@ -24,7 +24,7 @@ import (
// ContractRef is a reference to the contract's backing object
type ContractRef interface {
- ReturnGas(*big.Int, *big.Int)
+ ReturnGas(*big.Int)
Address() common.Address
Value() *big.Int
SetCode(common.Hash, []byte)
@@ -48,7 +48,7 @@ type Contract struct {
CodeAddr *common.Address
Input []byte
- value, Gas, UsedGas, Price *big.Int
+ value, Gas, UsedGas *big.Int
Args []byte
@@ -56,7 +56,7 @@ type Contract struct {
}
// NewContract returns a new contract environment for the execution of EVM.
-func NewContract(caller ContractRef, object ContractRef, value, gas, price *big.Int) *Contract {
+func NewContract(caller ContractRef, object ContractRef, value, gas *big.Int) *Contract {
c := &Contract{CallerAddress: caller.Address(), caller: caller, self: object, Args: nil}
if parent, ok := caller.(*Contract); ok {
@@ -70,9 +70,6 @@ func NewContract(caller ContractRef, object ContractRef, value, gas, price *big.
// This pointer will be off the state transition
c.Gas = gas //new(big.Int).Set(gas)
c.value = new(big.Int).Set(value)
- // In most cases price and value are pointers to transaction objects
- // and we don't want the transaction's values to change.
- c.Price = new(big.Int).Set(price)
c.UsedGas = new(big.Int)
return c
@@ -114,7 +111,7 @@ func (c *Contract) Caller() common.Address {
// caller.
func (c *Contract) Finalise() {
// Return the remaining gas to the caller
- c.caller.ReturnGas(c.Gas, c.Price)
+ c.caller.ReturnGas(c.Gas)
}
// UseGas attempts the use gas and subtracts it and returns true on success
@@ -127,7 +124,7 @@ func (c *Contract) UseGas(gas *big.Int) (ok bool) {
}
// ReturnGas adds the given gas back to itself.
-func (c *Contract) ReturnGas(gas, price *big.Int) {
+func (c *Contract) ReturnGas(gas *big.Int) {
// Return the gas to the context
c.Gas.Add(c.Gas, gas)
c.UsedGas.Sub(c.UsedGas, gas)
diff --git a/core/vm/environment.go b/core/vm/environment.go
index e97c1e58c..50a09d444 100644
--- a/core/vm/environment.go
+++ b/core/vm/environment.go
@@ -17,110 +17,299 @@
package vm
import (
+ "fmt"
"math/big"
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/crypto"
"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
- // 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, price, 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, price, 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, price *big.Int) ([]byte, error)
- // Create a new contract
- Create(me ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error)
+type (
+ CanTransferFunc func(StateDB, common.Address, *big.Int) bool
+ TransferFunc func(StateDB, common.Address, common.Address, *big.Int)
+ // GetHashFunc returns the nth block hash in the blockchain
+ // and is used by the BLOCKHASH EVM op code.
+ GetHashFunc func(uint64) common.Hash
+)
+
+// Context provides the EVM with auxilary information. Once provided it shouldn't be modified.
+type Context struct {
+ // CanTransfer returns whether the account contains
+ // sufficient ether to transfer the value
+ CanTransfer CanTransferFunc
+ // Transfer transfers ether from one account to the other
+ Transfer TransferFunc
+ // GetHash returns the hash corresponding to n
+ GetHash GetHashFunc
+
+ // Message information
+ Origin common.Address // Provides information for ORIGIN
+ GasPrice *big.Int // Provides information for GASPRICE
+
+ // Block information
+ Coinbase common.Address // Provides information for COINBASE
+ 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
+}
+
+// Environment provides information about external sources for the EVM
+//
+// The Environment should never be reused and is not thread safe.
+type Environment struct {
+ // Context provides auxiliary blockchain related information
+ Context
+ // StateDB gives access to the underlying state
+ StateDB StateDB
+ // Depth is the current call stack
+ Depth int
+
+ // evm is the ethereum virtual machine
+ evm Vm
+ // chainConfig contains information about the current chain
+ chainConfig *params.ChainConfig
+ vmConfig Config
+}
+
+// NewEnvironment retutrns a new EVM environment.
+func NewEnvironment(context Context, statedb StateDB, chainConfig *params.ChainConfig, vmConfig Config) *Environment {
+ env := &Environment{
+ Context: context,
+ StateDB: statedb,
+ vmConfig: vmConfig,
+ chainConfig: chainConfig,
+ }
+ env.evm = New(env, vmConfig)
+ return env
+}
+
+// Call executes the contract associated with the addr with the given input as paramaters. It also handles any
+// necessary value transfer required and takes the necessary steps to create accounts and reverses the state in
+// case of an execution error or failed value transfer.
+func (env *Environment) Call(caller ContractRef, addr common.Address, input []byte, gas, value *big.Int) ([]byte, error) {
+ if env.vmConfig.NoRecursion && env.Depth > 0 {
+ caller.ReturnGas(gas)
+
+ return nil, nil
+ }
+
+ // Depth check execution. Fail if we're trying to execute above the
+ // limit.
+ if env.Depth > int(params.CallCreateDepth.Int64()) {
+ caller.ReturnGas(gas)
+
+ return nil, DepthError
+ }
+ if !env.Context.CanTransfer(env.StateDB, caller.Address(), value) {
+ caller.ReturnGas(gas)
+
+ return nil, ErrInsufficientBalance
+ }
+
+ var (
+ to Account
+ snapshotPreTransfer = env.StateDB.Snapshot()
+ )
+ if !env.StateDB.Exist(addr) {
+ if Precompiled[addr.Str()] == nil && env.ChainConfig().IsEIP158(env.BlockNumber) && value.BitLen() == 0 {
+ caller.ReturnGas(gas)
+ return nil, nil
+ }
+
+ to = env.StateDB.CreateAccount(addr)
+ } else {
+ to = env.StateDB.GetAccount(addr)
+ }
+ env.Transfer(env.StateDB, caller.Address(), to.Address(), value)
+
+ // initialise a new contract and set the code that is to be used by the
+ // E The contract is a scoped environment for this execution context
+ // only.
+ contract := NewContract(caller, to, value, gas)
+ contract.SetCallCode(&addr, env.StateDB.GetCodeHash(addr), env.StateDB.GetCode(addr))
+ defer contract.Finalise()
+
+ ret, err := env.EVM().Run(contract, input)
+ // 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 {
+ contract.UseGas(contract.Gas)
+
+ env.StateDB.RevertToSnapshot(snapshotPreTransfer)
+ }
+ return ret, err
}
-// 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)
+// CallCode executes the contract associated with the addr with the given input as paramaters. It also handles any
+// necessary value transfer required and takes the necessary steps to create accounts and reverses the state in
+// case of an execution error or failed value transfer.
+//
+// CallCode differs from Call in the sense that it executes the given address' code with the caller as context.
+func (env *Environment) CallCode(caller ContractRef, addr common.Address, input []byte, gas, value *big.Int) ([]byte, error) {
+ if env.vmConfig.NoRecursion && env.Depth > 0 {
+ caller.ReturnGas(gas)
+
+ return nil, nil
+ }
+
+ // Depth check execution. Fail if we're trying to execute above the
+ // limit.
+ if env.Depth > int(params.CallCreateDepth.Int64()) {
+ caller.ReturnGas(gas)
+
+ return nil, DepthError
+ }
+ if !env.CanTransfer(env.StateDB, caller.Address(), value) {
+ caller.ReturnGas(gas)
+
+ return nil, fmt.Errorf("insufficient funds to transfer value. Req %v, has %v", value, env.StateDB.GetBalance(caller.Address()))
+ }
+
+ var (
+ snapshotPreTransfer = env.StateDB.Snapshot()
+ to = env.StateDB.GetAccount(caller.Address())
+ )
+ // initialise a new contract and set the code that is to be used by the
+ // E The contract is a scoped environment for this execution context
+ // only.
+ contract := NewContract(caller, to, value, gas)
+ contract.SetCallCode(&addr, env.StateDB.GetCodeHash(addr), env.StateDB.GetCode(addr))
+ defer contract.Finalise()
+
+ ret, err := env.EVM().Run(contract, input)
+ if err != nil {
+ contract.UseGas(contract.Gas)
+
+ env.StateDB.RevertToSnapshot(snapshotPreTransfer)
+ }
+
+ return ret, err
}
-// Database is a EVM database for full state querying.
-type Database interface {
- GetAccount(common.Address) Account
- CreateAccount(common.Address) Account
+// DelegateCall executes the contract associated with the addr with the given input as paramaters.
+// It reverses the state in case of an execution error.
+//
+// DelegateCall differs from CallCode in the sense that it executes the given address' code with the caller as context
+// and the caller is set to the caller of the caller.
+func (env *Environment) DelegateCall(caller ContractRef, addr common.Address, input []byte, gas *big.Int) ([]byte, error) {
+ if env.vmConfig.NoRecursion && env.Depth > 0 {
+ caller.ReturnGas(gas)
- AddBalance(common.Address, *big.Int)
- GetBalance(common.Address) *big.Int
+ return nil, nil
+ }
- GetNonce(common.Address) uint64
- SetNonce(common.Address, uint64)
+ // Depth check execution. Fail if we're trying to execute above the
+ // limit.
+ if env.Depth > int(params.CallCreateDepth.Int64()) {
+ caller.ReturnGas(gas)
+ return nil, DepthError
+ }
- GetCodeHash(common.Address) common.Hash
- GetCodeSize(common.Address) int
- GetCode(common.Address) []byte
- SetCode(common.Address, []byte)
+ var (
+ snapshot = env.StateDB.Snapshot()
+ to = env.StateDB.GetAccount(caller.Address())
+ )
- AddRefund(*big.Int)
- GetRefund() *big.Int
+ // Iinitialise a new contract and make initialise the delegate values
+ contract := NewContract(caller, to, caller.Value(), gas).AsDelegate()
+ contract.SetCallCode(&addr, env.StateDB.GetCodeHash(addr), env.StateDB.GetCode(addr))
+ defer contract.Finalise()
- GetState(common.Address, common.Hash) common.Hash
- SetState(common.Address, common.Hash, common.Hash)
+ ret, err := env.EVM().Run(contract, input)
+ if err != nil {
+ contract.UseGas(contract.Gas)
- Suicide(common.Address) bool
- HasSuicided(common.Address) bool
+ env.StateDB.RevertToSnapshot(snapshot)
+ }
- // Exist reports whether the given account exists in state.
- // Notably this should also return true for suicided accounts.
- Exist(common.Address) bool
- // Empty returns whether the given account is empty. Empty
- // is defined according to EIP161 (balance = nonce = code = 0).
- Empty(common.Address) bool
+ return ret, err
}
-// 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(*big.Int, *big.Int)
- SetCode(common.Hash, []byte)
- ForEachStorage(cb func(key, value common.Hash) bool)
- Value() *big.Int
+// Create creates a new contract using code as deployment code.
+func (env *Environment) Create(caller ContractRef, code []byte, gas, value *big.Int) ([]byte, common.Address, error) {
+ if env.vmConfig.NoRecursion && env.Depth > 0 {
+ caller.ReturnGas(gas)
+
+ return nil, common.Address{}, nil
+ }
+
+ // Depth check execution. Fail if we're trying to execute above the
+ // limit.
+ if env.Depth > int(params.CallCreateDepth.Int64()) {
+ caller.ReturnGas(gas)
+
+ return nil, common.Address{}, DepthError
+ }
+ if !env.CanTransfer(env.StateDB, caller.Address(), value) {
+ caller.ReturnGas(gas)
+
+ return nil, common.Address{}, ErrInsufficientBalance
+ }
+
+ // Create a new account on the state
+ nonce := env.StateDB.GetNonce(caller.Address())
+ env.StateDB.SetNonce(caller.Address(), nonce+1)
+
+ snapshotPreTransfer := env.StateDB.Snapshot()
+ var (
+ addr = crypto.CreateAddress(caller.Address(), nonce)
+ to = env.StateDB.CreateAccount(addr)
+ )
+ if env.ChainConfig().IsEIP158(env.BlockNumber) {
+ env.StateDB.SetNonce(addr, 1)
+ }
+ env.Transfer(env.StateDB, caller.Address(), to.Address(), value)
+
+ // initialise a new contract and set the code that is to be used by the
+ // E The contract is a scoped environment for this execution context
+ // only.
+ contract := NewContract(caller, to, value, gas)
+ contract.SetCallCode(&addr, crypto.Keccak256Hash(code), code)
+ defer contract.Finalise()
+
+ ret, err := env.EVM().Run(contract, nil)
+ // check whether the max code size has been exceeded
+ maxCodeSizeExceeded := len(ret) > params.MaxCodeSize
+ // if the contract creation ran successfully and no errors were returned
+ // calculate the gas required to store the code. If the code could not
+ // be stored due to not enough gas set an error and let it be handled
+ // by the error checking condition below.
+ if err == nil && !maxCodeSizeExceeded {
+ dataGas := big.NewInt(int64(len(ret)))
+ dataGas.Mul(dataGas, params.CreateDataGas)
+ if contract.UseGas(dataGas) {
+ env.StateDB.SetCode(addr, ret)
+ } else {
+ err = CodeStoreOutOfGasError
+ }
+ }
+
+ // 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 maxCodeSizeExceeded ||
+ (err != nil && (env.ChainConfig().IsHomestead(env.BlockNumber) || err != CodeStoreOutOfGasError)) {
+ contract.UseGas(contract.Gas)
+ env.StateDB.RevertToSnapshot(snapshotPreTransfer)
+
+ // Nothing should be returned when an error is thrown.
+ return nil, addr, err
+ }
+ // If the vm returned with an error the return value should be set to nil.
+ // This isn't consensus critical but merely to for behaviour reasons such as
+ // tests, RPC calls, etc.
+ if err != nil {
+ ret = nil
+ }
+
+ return ret, addr, err
}
+
+// ChainConfig returns the environment's chain configuration
+func (env *Environment) ChainConfig() *params.ChainConfig { return env.chainConfig }
+
+// EVM returns the environments EVM
+func (env *Environment) EVM() Vm { return env.evm }
diff --git a/core/vm/errors.go b/core/vm/errors.go
index 1766bf9fb..f8d26b1f0 100644
--- a/core/vm/errors.go
+++ b/core/vm/errors.go
@@ -23,7 +23,10 @@ import (
"github.com/ethereum/go-ethereum/params"
)
-var OutOfGasError = errors.New("Out of gas")
-var CodeStoreOutOfGasError = errors.New("Contract creation code storage out of gas")
-var DepthError = fmt.Errorf("Max call depth exceeded (%d)", params.CallCreateDepth)
-var TraceLimitReachedError = errors.New("The number of logs reached the specified limit")
+var (
+ OutOfGasError = errors.New("Out of gas")
+ CodeStoreOutOfGasError = errors.New("Contract creation code storage out of gas")
+ DepthError = fmt.Errorf("Max call depth exceeded (%d)", params.CallCreateDepth)
+ TraceLimitReachedError = errors.New("The number of logs reached the specified limit")
+ ErrInsufficientBalance = errors.New("insufficient balance for transfer")
+)
diff --git a/core/vm/instructions.go b/core/vm/instructions.go
index 4f98953b5..871c09e83 100644
--- a/core/vm/instructions.go
+++ b/core/vm/instructions.go
@@ -28,14 +28,14 @@ import (
type programInstruction interface {
// executes the program instruction and allows the instruction to modify the state of the program
- do(program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error)
+ do(program *Program, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error)
// returns whether the program instruction halts the execution of the JIT
halts() bool
// Returns the current op code (debugging purposes)
Op() OpCode
}
-type instrFn func(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack)
+type instrFn func(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack)
type instruction struct {
op OpCode
@@ -59,9 +59,9 @@ func jump(mapping map[uint64]uint64, destinations map[uint64]struct{}, contract
return mapping[to.Uint64()], nil
}
-func (instr instruction) do(program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func (instr instruction) do(program *Program, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
// calculate the new memory size and gas price for the current executing opcode
- newMemSize, cost, err := jitCalculateGasAndSize(env, contract, instr, env.Db(), memory, stack)
+ newMemSize, cost, err := jitCalculateGasAndSize(env, contract, instr, memory, stack)
if err != nil {
return nil, err
}
@@ -115,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)))
@@ -143,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))
@@ -163,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))
@@ -172,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 {
@@ -192,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) {
base, exponent := stack.pop(), stack.pop()
stack.push(math.Exp(base, exponent))
}
-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)
@@ -214,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))
@@ -228,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))
@@ -237,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))
@@ -246,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))
@@ -255,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))
@@ -264,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))
@@ -273,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()]))
@@ -294,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)
@@ -304,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)
@@ -315,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.Get(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)
+ balance := env.StateDB.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()
@@ -362,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)))
+ l := big.NewInt(int64(env.StateDB.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()
@@ -384,70 +384,70 @@ 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()
cOff = stack.pop()
l = stack.pop()
)
- codeCopy := getData(env.Db().GetCode(addr), cOff, l)
+ codeCopy := getData(env.StateDB.GetCode(addr), cOff, l)
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(contract.Price))
+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()
@@ -456,77 +456,77 @@ 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.StateDB.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.Get(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()
+ val := env.StateDB.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))
+ env.StateDB.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).Set(contract.Gas))
}
-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()
input = memory.Get(offset.Int64(), size.Int64())
gas = new(big.Int).Set(contract.Gas)
)
- if env.ChainConfig().IsEIP150(env.BlockNumber()) {
+ if env.ChainConfig().IsEIP150(env.BlockNumber) {
gas.Div(gas, n64)
gas = gas.Sub(contract.Gas, gas)
}
contract.UseGas(gas)
- _, addr, suberr := env.Create(contract, input, gas, contract.Price, value)
+ _, addr, suberr := env.Create(contract, input, gas, value)
// Push item on the stack based on the returned error. If the ruleset is
// 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))
@@ -535,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()
@@ -554,7 +554,7 @@ func opCall(instr instruction, pc *uint64, env Environment, contract *Contract,
gas.Add(gas, params.CallStipend)
}
- ret, err := env.Call(contract, address, args, gas, contract.Price, value)
+ ret, err := env.Call(contract, address, args, gas, value)
if err != nil {
stack.push(new(big.Int))
@@ -566,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()
@@ -585,7 +585,7 @@ func opCallCode(instr instruction, pc *uint64, env Environment, contract *Contra
gas.Add(gas, params.CallStipend)
}
- ret, err := env.CallCode(contract, address, args, gas, contract.Price, value)
+ ret, err := env.CallCode(contract, address, args, gas, value)
if err != nil {
stack.push(new(big.Int))
@@ -597,12 +597,12 @@ 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)
args := memory.Get(inOffset.Int64(), inSize.Int64())
- ret, err := env.DelegateCall(contract, toAddr, args, gas, contract.Price)
+ ret, err := env.DelegateCall(contract, toAddr, args, gas)
if err != nil {
stack.push(new(big.Int))
} else {
@@ -611,23 +611,23 @@ 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) {
- balance := env.Db().GetBalance(contract.Address())
- env.Db().AddBalance(common.BigToAddress(stack.pop()), balance)
+func opSuicide(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
+ balance := env.StateDB.GetBalance(contract.Address())
+ env.StateDB.AddBalance(common.BigToAddress(stack.pop()), balance)
- env.Db().Suicide(contract.Address())
+ env.StateDB.Suicide(contract.Address())
}
// following functions are used by the instruction jump table
// 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++ {
@@ -635,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.StateDB.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
@@ -651,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))
}
}
@@ -660,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 000000000..918fde85f
--- /dev/null
+++ b/core/vm/interface.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 <http://www.gnu.org/licenses/>.
+
+package vm
+
+import (
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/common"
+)
+
+// Vm is the basic interface for an implementation of the EVM.
+type Vm interface {
+ // Run should execute the given contract with the input given in in
+ // and return the contract execution return bytes or an error if it
+ // failed.
+ Run(c *Contract, in []byte) ([]byte, error)
+}
+
+// StateDB is an EVM database for full state querying.
+type StateDB 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 returns whether the given account is empty. Empty
+ // is defined according to EIP161 (balance = nonce = code = 0).
+ Empty(common.Address) bool
+
+ RevertToSnapshot(int)
+ Snapshot() int
+
+ 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(*big.Int)
+ SetCode(common.Hash, []byte)
+ ForEachStorage(cb func(key, value common.Hash) bool)
+ Value() *big.Int
+}
+
+// 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)
+}
diff --git a/core/vm/jit.go b/core/vm/jit.go
index b75558d39..aabe4488b 100644
--- a/core/vm/jit.go
+++ b/core/vm/jit.go
@@ -299,11 +299,11 @@ func CompileProgram(program *Program) (err error) {
// RunProgram runs the program given the environment and contract and returns an
// error if the execution failed (non-consensus)
-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 runProgram(program, 0, NewMemory(), newstack(), env, contract, input)
}
-func runProgram(program *Program, pcstart uint64, mem *Memory, stack *Stack, env Environment, contract *Contract, input []byte) ([]byte, error) {
+func runProgram(program *Program, pcstart uint64, mem *Memory, stack *Stack, env *Environment, contract *Contract, input []byte) ([]byte, error) {
contract.Input = input
var (
@@ -319,7 +319,7 @@ func runProgram(program *Program, pcstart uint64, mem *Memory, stack *Stack, env
}()
}
- homestead := env.ChainConfig().IsHomestead(env.BlockNumber())
+ homestead := env.ChainConfig().IsHomestead(env.BlockNumber)
for pc < uint64(len(program.instructions)) {
instrCount++
@@ -357,7 +357,7 @@ func validDest(dests map[uint64]struct{}, dest *big.Int) bool {
// jitCalculateGasAndSize calculates the required given the opcode and stack items calculates the new memorysize for
// the operation. This does not reduce gas or resizes the memory.
-func jitCalculateGasAndSize(env Environment, contract *Contract, instr instruction, statedb Database, mem *Memory, stack *Stack) (*big.Int, *big.Int, error) {
+func jitCalculateGasAndSize(env *Environment, contract *Contract, instr instruction, mem *Memory, stack *Stack) (*big.Int, *big.Int, error) {
var (
gas = new(big.Int)
newMemSize *big.Int = new(big.Int)
@@ -408,7 +408,7 @@ func jitCalculateGasAndSize(env Environment, contract *Contract, instr instructi
var g *big.Int
y, x := stack.data[stack.len()-2], stack.data[stack.len()-1]
- val := statedb.GetState(contract.Address(), common.BigToHash(x))
+ val := env.StateDB.GetState(contract.Address(), common.BigToHash(x))
// This checks for 3 scenario's and calculates gas accordingly
// 1. From a zero-value address to a non-zero value (NEW VALUE)
@@ -417,7 +417,7 @@ func jitCalculateGasAndSize(env Environment, contract *Contract, instr instructi
if common.EmptyHash(val) && !common.EmptyHash(common.BigToHash(y)) {
g = params.SstoreSetGas
} else if !common.EmptyHash(val) && common.EmptyHash(common.BigToHash(y)) {
- statedb.AddRefund(params.SstoreRefundGas)
+ env.StateDB.AddRefund(params.SstoreRefundGas)
g = params.SstoreClearGas
} else {
@@ -425,8 +425,8 @@ func jitCalculateGasAndSize(env Environment, contract *Contract, instr instructi
}
gas.Set(g)
case SUICIDE:
- if !statedb.HasSuicided(contract.Address()) {
- statedb.AddRefund(params.SuicideRefundGas)
+ if !env.StateDB.HasSuicided(contract.Address()) {
+ env.StateDB.AddRefund(params.SuicideRefundGas)
}
case MLOAD:
newMemSize = calcMemSize(stack.peek(), u256(32))
@@ -463,7 +463,7 @@ func jitCalculateGasAndSize(env Environment, contract *Contract, instr instructi
gas.Add(gas, stack.data[stack.len()-1])
if op == CALL {
- if !env.Db().Exist(common.BigToAddress(stack.data[stack.len()-2])) {
+ if !env.StateDB.Exist(common.BigToAddress(stack.data[stack.len()-2])) {
gas.Add(gas, params.CallNewAccountGas)
}
}
diff --git a/core/vm/jit_test.go b/core/vm/jit_test.go
index 6f7ba9250..79c389c05 100644
--- a/core/vm/jit_test.go
+++ b/core/vm/jit_test.go
@@ -19,10 +19,8 @@ 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"
)
@@ -86,8 +84,8 @@ func TestCompiling(t *testing.T) {
func TestResetInput(t *testing.T) {
var sender account
- env := NewEnv(&Config{EnableJit: true, ForceJit: true})
- contract := NewContract(sender, sender, big.NewInt(100), big.NewInt(10000), big.NewInt(0))
+ env := NewEnvironment(Context{}, nil, params.TestChainConfig, Config{})
+ contract := NewContract(sender, sender, big.NewInt(100), big.NewInt(10000))
contract.CodeAddr = &common.Address{}
program := NewProgram([]byte{})
@@ -135,7 +133,7 @@ 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(*big.Int) {}
func (account) SetCode(common.Hash, []byte) {}
func (account) ForEachStorage(cb func(key, value common.Hash) bool) {}
@@ -145,70 +143,18 @@ func runVmBench(test vmBench, b *testing.B) {
if test.precompile && !test.forcejit {
NewProgram(test.code)
}
- env := NewEnv(&Config{EnableJit: !test.nojit, ForceJit: test.forcejit})
+ env := NewEnvironment(Context{}, nil, params.TestChainConfig, 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), big.NewInt(0))
+ 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)
+ _, err := env.EVM().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 params.TestChainConfig
-}
-func (self *Env) Vm() Vm { return self.evm }
-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, price, value *big.Int) ([]byte, error) {
- return nil, nil
-}
-func (self *Env) CallCode(caller ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
- return nil, nil
-}
-func (self *Env) Create(caller ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) {
- return nil, common.Address{}, nil
-}
-func (self *Env) DelegateCall(me ContractRef, addr common.Address, data []byte, gas, price *big.Int) ([]byte, error) {
- return nil, nil
-}
diff --git a/core/vm/logger.go b/core/vm/logger.go
index 9e13d703b..6a605a59c 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
@@ -144,7 +144,7 @@ func (l *StructLogger) CaptureState(env Environment, pc uint64, op OpCode, gas,
storage = make(Storage)
// Get the contract account and loop over each storage entry. This may involve looping over
// the trie and is a very expensive process.
- env.Db().GetAccount(contract.Address()).ForEachStorage(func(key, value common.Hash) bool {
+ env.StateDB.GetAccount(contract.Address()).ForEachStorage(func(key, value common.Hash) bool {
storage[key] = value
// Return true, indicating we'd like to continue.
return true
@@ -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 d4d164eb6..05ad32fd8 100644
--- a/core/vm/logger_test.go
+++ b/core/vm/logger_test.go
@@ -21,16 +21,17 @@ import (
"testing"
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/params"
)
type dummyContractRef struct {
calledForEach bool
}
-func (dummyContractRef) ReturnGas(*big.Int, *big.Int) {}
-func (dummyContractRef) Address() common.Address { return common.Address{} }
-func (dummyContractRef) Value() *big.Int { return new(big.Int) }
-func (dummyContractRef) SetCode(common.Hash, []byte) {}
+func (dummyContractRef) ReturnGas(*big.Int) {}
+func (dummyContractRef) Address() common.Address { return common.Address{} }
+func (dummyContractRef) Value() *big.Int { return new(big.Int) }
+func (dummyContractRef) SetCode(common.Hash, []byte) {}
func (d *dummyContractRef) ForEachStorage(callback func(key, value common.Hash) bool) {
d.calledForEach = true
}
@@ -40,28 +41,22 @@ func (d *dummyContractRef) SetBalance(*big.Int) {}
func (d *dummyContractRef) SetNonce(uint64) {}
func (d *dummyContractRef) Balance() *big.Int { return new(big.Int) }
-type dummyEnv struct {
- *Env
+type dummyStateDB struct {
+ NoopStateDB
ref *dummyContractRef
}
-func newDummyEnv(ref *dummyContractRef) *dummyEnv {
- return &dummyEnv{
- Env: NewEnv(&Config{EnableJit: false, ForceJit: false}),
- ref: ref,
- }
-}
-func (d dummyEnv) GetAccount(common.Address) Account {
+func (d dummyStateDB) GetAccount(common.Address) Account {
return d.ref
}
func TestStoreCapture(t *testing.T) {
var (
- env = NewEnv(&Config{EnableJit: false, ForceJit: false})
+ env = NewEnvironment(Context{}, nil, params.TestChainConfig, Config{EnableJit: false, ForceJit: false})
logger = NewStructLogger(nil)
mem = NewMemory()
stack = newstack()
- contract = NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), new(big.Int), new(big.Int))
+ contract = NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), new(big.Int))
)
stack.push(big.NewInt(1))
stack.push(big.NewInt(0))
@@ -83,8 +78,8 @@ func TestStorageCapture(t *testing.T) {
t.Skip("implementing this function is difficult. it requires all sort of interfaces to be implemented which isn't trivial. The value (the actual test) isn't worth it")
var (
ref = &dummyContractRef{}
- contract = NewContract(ref, ref, new(big.Int), new(big.Int), new(big.Int))
- env = newDummyEnv(ref)
+ contract = NewContract(ref, ref, new(big.Int), new(big.Int))
+ env = NewEnvironment(Context{}, dummyStateDB{ref: ref}, params.TestChainConfig, Config{EnableJit: false, ForceJit: false})
logger = NewStructLogger(nil)
mem = NewMemory()
stack = newstack()
diff --git a/core/vm/noop.go b/core/vm/noop.go
new file mode 100644
index 000000000..ca7d1055a
--- /dev/null
+++ b/core/vm/noop.go
@@ -0,0 +1,68 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package vm
+
+import (
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/common"
+)
+
+func NoopCanTransfer(db StateDB, from common.Address, balance *big.Int) bool {
+ return true
+}
+func NoopTransfer(db StateDB, from, to common.Address, amount *big.Int) {}
+
+type NoopEVMCallContext struct{}
+
+func (NoopEVMCallContext) Call(caller ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) {
+ return nil, nil
+}
+func (NoopEVMCallContext) CallCode(caller ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) {
+ return nil, nil
+}
+func (NoopEVMCallContext) Create(caller ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error) {
+ return nil, common.Address{}, nil
+}
+func (NoopEVMCallContext) DelegateCall(me ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error) {
+ return nil, nil
+}
+
+type NoopStateDB struct{}
+
+func (NoopStateDB) GetAccount(common.Address) Account { return nil }
+func (NoopStateDB) CreateAccount(common.Address) Account { return nil }
+func (NoopStateDB) SubBalance(common.Address, *big.Int) {}
+func (NoopStateDB) AddBalance(common.Address, *big.Int) {}
+func (NoopStateDB) GetBalance(common.Address) *big.Int { return nil }
+func (NoopStateDB) GetNonce(common.Address) uint64 { return 0 }
+func (NoopStateDB) SetNonce(common.Address, uint64) {}
+func (NoopStateDB) GetCodeHash(common.Address) common.Hash { return common.Hash{} }
+func (NoopStateDB) GetCode(common.Address) []byte { return nil }
+func (NoopStateDB) SetCode(common.Address, []byte) {}
+func (NoopStateDB) GetCodeSize(common.Address) int { return 0 }
+func (NoopStateDB) AddRefund(*big.Int) {}
+func (NoopStateDB) GetRefund() *big.Int { return nil }
+func (NoopStateDB) GetState(common.Address, common.Hash) common.Hash { return common.Hash{} }
+func (NoopStateDB) SetState(common.Address, common.Hash, common.Hash) {}
+func (NoopStateDB) Suicide(common.Address) bool { return false }
+func (NoopStateDB) HasSuicided(common.Address) bool { return false }
+func (NoopStateDB) Exist(common.Address) bool { return false }
+func (NoopStateDB) Empty(common.Address) bool { return false }
+func (NoopStateDB) RevertToSnapshot(int) {}
+func (NoopStateDB) Snapshot() int { return 0 }
+func (NoopStateDB) AddLog(*Log) {}
diff --git a/core/vm/runtime/env.go b/core/vm/runtime/env.go
index f1a2b60d3..3cf0dd024 100644
--- a/core/vm/runtime/env.go
+++ b/core/vm/runtime/env.go
@@ -23,92 +23,22 @@ import (
"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
-
- 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,
+func NewEnv(cfg *Config, state *state.StateDB) *vm.Environment {
+ context := vm.Context{
+ CanTransfer: core.CanTransfer,
+ Transfer: core.Transfer,
+ GetHash: func(uint64) common.Hash { return common.Hash{} },
+
+ Origin: cfg.Origin,
+ Coinbase: cfg.Coinbase,
+ BlockNumber: cfg.BlockNumber,
+ Time: cfg.Time,
+ Difficulty: cfg.Difficulty,
+ GasLimit: cfg.GasLimit,
+ GasPrice: new(big.Int),
}
- 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) 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, price, value *big.Int) ([]byte, error) {
- return core.Call(self, caller, addr, data, gas, price, value)
-}
-func (self *Env) CallCode(caller vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
- return core.CallCode(self, caller, addr, data, gas, price, value)
-}
-
-func (self *Env) DelegateCall(me vm.ContractRef, addr common.Address, data []byte, gas, price *big.Int) ([]byte, error) {
- return core.DelegateCall(self, me, addr, data, gas, price)
-}
-func (self *Env) Create(caller vm.ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) {
- return core.Create(self, caller, data, gas, price, value)
+ return vm.NewEnvironment(context, cfg.State, cfg.ChainConfig, cfg.EVMConfig)
}
diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go
index d51b435f8..3e99ed689 100644
--- a/core/vm/runtime/runtime.go
+++ b/core/vm/runtime/runtime.go
@@ -22,6 +22,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
+ "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/params"
@@ -49,6 +50,7 @@ type Config struct {
Value *big.Int
DisableJit bool // "disable" so it's enabled by default
Debug bool
+ EVMConfig vm.Config
State *state.StateDB
GetHashFn func(n uint64) common.Hash
@@ -123,13 +125,37 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
receiver.Address(),
input,
cfg.GasLimit,
- cfg.GasPrice,
cfg.Value,
)
return ret, cfg.State, err
}
+// Create executes the code using the EVM create method
+func Create(input []byte, cfg *Config) ([]byte, common.Address, error) {
+ if cfg == nil {
+ cfg = new(Config)
+ }
+ setDefaults(cfg)
+
+ if cfg.State == nil {
+ db, _ := ethdb.NewMemDatabase()
+ cfg.State, _ = state.New(common.Hash{}, db)
+ }
+ var (
+ vmenv = NewEnv(cfg, cfg.State)
+ sender = cfg.State.CreateAccount(cfg.Origin)
+ )
+
+ // Call the code with the given configuration.
+ return vmenv.Create(
+ sender,
+ input,
+ cfg.GasLimit,
+ cfg.Value,
+ )
+}
+
// Call executes the code given by the contract's address. It will return the
// EVM's return value or an error if it failed.
//
@@ -147,7 +173,6 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, error) {
address,
input,
cfg.GasLimit,
- cfg.GasPrice,
cfg.Value,
)
diff --git a/core/vm/segments.go b/core/vm/segments.go
index 648d8a04a..47f535ab5 100644
--- a/core/vm/segments.go
+++ b/core/vm/segments.go
@@ -24,7 +24,7 @@ type jumpSeg struct {
gas *big.Int
}
-func (j jumpSeg) do(program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func (j jumpSeg) do(program *Program, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
if !contract.UseGas(j.gas) {
return nil, OutOfGasError
}
@@ -42,7 +42,7 @@ type pushSeg struct {
gas *big.Int
}
-func (s pushSeg) do(program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func (s pushSeg) do(program *Program, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
// Use the calculated gas. When insufficient gas is present, use all gas and return an
// Out Of Gas error
if !contract.UseGas(s.gas) {
diff --git a/core/vm/util_test.go b/core/vm/util_test.go
deleted file mode 100644
index 5783ee015..000000000
--- a/core/vm/util_test.go
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
-
-package vm
-
-import (
- "math/big"
-
- "github.com/ethereum/go-ethereum/params"
-)
-
-type ruleSet struct {
- hs *big.Int
-}
-
-func (r ruleSet) IsHomestead(n *big.Int) bool { return n.Cmp(r.hs) >= 0 }
-func (r ruleSet) GasTable(*big.Int) params.GasTable {
- return params.GasTableHomestead
-}
diff --git a/core/vm/vm.go b/core/vm/vm.go
index 56aca6912..3521839df 100644
--- a/core/vm/vm.go
+++ b/core/vm/vm.go
@@ -30,10 +30,17 @@ import (
// Config are the configuration options for the EVM
type Config struct {
- Debug bool
+ // Debug enabled debugging EVM options
+ Debug bool
+ // EnableJit enabled the JIT VM
EnableJit bool
- ForceJit bool
- Tracer Tracer
+ // ForceJit forces the JIT VM
+ ForceJit bool
+ // Tracer is the op code logger
+ Tracer Tracer
+ // NoRecursion disabled EVM call, callcode,
+ // delegate call and create.
+ NoRecursion bool
}
// EVM is used to run Ethereum based contracts and will utilise the
@@ -41,26 +48,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) (ret []byte, err 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 := Precompiled[contract.CodeAddr.Str()]; p != nil {
@@ -117,10 +124,9 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) {
code = contract.Code
instrCount = 0
- op OpCode // current opcode
- mem = NewMemory() // bound memory
- stack = newstack() // local stack
- statedb = evm.env.Db() // current state
+ op OpCode // current opcode
+ mem = NewMemory() // bound memory
+ stack = newstack() // local stack
// For optimisation reason we're using uint64 as the program counter.
// It's theoretically possible to go above 2^64. The YP defines the PC to be uint256. Practically much less so feasible.
pc = uint64(0) // program counter
@@ -146,7 +152,7 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) {
// User defer pattern to check for an error and, based on the error being nil or not, use all gas and return.
defer func() {
if err != nil && evm.cfg.Debug {
- evm.cfg.Tracer.CaptureState(evm.env, pc, op, contract.Gas, cost, mem, stack, contract, evm.env.Depth(), err)
+ evm.cfg.Tracer.CaptureState(evm.env, pc, op, contract.Gas, cost, mem, stack, contract, evm.env.Depth, err)
}
}()
@@ -174,7 +180,7 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) {
op = contract.GetOp(pc)
//fmt.Printf("OP %d %v\n", op, op)
// calculate the new memory size and gas price for the current executing opcode
- newMemSize, cost, err = calculateGasAndSize(evm.gasTable, evm.env, contract, caller, op, statedb, mem, stack)
+ newMemSize, cost, err = calculateGasAndSize(evm.gasTable, evm.env, contract, caller, op, mem, stack)
if err != nil {
return nil, err
}
@@ -189,7 +195,7 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) {
mem.Resize(newMemSize.Uint64())
// Add a log message
if evm.cfg.Debug {
- err = evm.cfg.Tracer.CaptureState(evm.env, pc, op, contract.Gas, cost, mem, stack, contract, evm.env.Depth(), nil)
+ err = evm.cfg.Tracer.CaptureState(evm.env, pc, op, contract.Gas, cost, mem, stack, contract, evm.env.Depth, nil)
if err != nil {
return nil, err
}
@@ -242,7 +248,7 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) {
// 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, caller ContractRef, op OpCode, statedb Database, mem *Memory, stack *Stack) (*big.Int, *big.Int, error) {
+func calculateGasAndSize(gasTable params.GasTable, env *Environment, contract *Contract, caller ContractRef, op OpCode, mem *Memory, stack *Stack) (*big.Int, *big.Int, error) {
var (
gas = new(big.Int)
newMemSize *big.Int = new(big.Int)
@@ -260,21 +266,21 @@ func calculateGasAndSize(gasTable params.GasTable, env Environment, contract *Co
gas.Set(gasTable.Suicide)
var (
address = common.BigToAddress(stack.data[len(stack.data)-1])
- eip158 = env.ChainConfig().IsEIP158(env.BlockNumber())
+ eip158 = env.ChainConfig().IsEIP158(env.BlockNumber)
)
if eip158 {
// if empty and transfers value
- if env.Db().Empty(address) && statedb.GetBalance(contract.Address()).BitLen() > 0 {
+ if env.StateDB.Empty(address) && env.StateDB.GetBalance(contract.Address()).BitLen() > 0 {
gas.Add(gas, gasTable.CreateBySuicide)
}
- } else if !env.Db().Exist(address) {
+ } else if !env.StateDB.Exist(address) {
gas.Add(gas, gasTable.CreateBySuicide)
}
}
- if !statedb.HasSuicided(contract.Address()) {
- statedb.AddRefund(params.SuicideRefundGas)
+ if !env.StateDB.HasSuicided(contract.Address()) {
+ env.StateDB.AddRefund(params.SuicideRefundGas)
}
case EXTCODESIZE:
gas.Set(gasTable.ExtcodeSize)
@@ -323,7 +329,7 @@ func calculateGasAndSize(gasTable params.GasTable, env Environment, contract *Co
var g *big.Int
y, x := stack.data[stack.len()-2], stack.data[stack.len()-1]
- val := statedb.GetState(contract.Address(), common.BigToHash(x))
+ val := env.StateDB.GetState(contract.Address(), common.BigToHash(x))
// This checks for 3 scenario's and calculates gas accordingly
// 1. From a zero-value address to a non-zero value (NEW VALUE)
@@ -333,7 +339,7 @@ func calculateGasAndSize(gasTable params.GasTable, env Environment, contract *Co
// 0 => non 0
g = params.SstoreSetGas
} else if !common.EmptyHash(val) && common.EmptyHash(common.BigToHash(y)) {
- statedb.AddRefund(params.SstoreRefundGas)
+ env.StateDB.AddRefund(params.SstoreRefundGas)
g = params.SstoreClearGas
} else {
@@ -394,13 +400,13 @@ 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)
)
if eip158 {
- if env.Db().Empty(address) && transfersValue {
+ if env.StateDB.Empty(address) && transfersValue {
gas.Add(gas, params.CallNewAccountGas)
}
- } else if !env.Db().Exist(address) {
+ } else if !env.StateDB.Exist(address) {
gas.Add(gas, params.CallNewAccountGas)
}
}
diff --git a/core/vm/vm_jit_fake.go b/core/vm/vm_jit_fake.go
index 4fa98ccd9..44b60abf6 100644
--- a/core/vm/vm_jit_fake.go
+++ b/core/vm/vm_jit_fake.go
@@ -17,10 +17,3 @@
// +build !evmjit
package vm
-
-import "fmt"
-
-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
index 43637bd13..58e71e305 100644
--- a/core/vm_env.go
+++ b/core/vm_env.go
@@ -15,104 +15,3 @@
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
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 { return self.msg.From() }
-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) 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, price, value *big.Int) ([]byte, error) {
- return Call(self, me, addr, data, gas, price, value)
-}
-func (self *VMEnv) CallCode(me vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
- return CallCode(self, me, addr, data, gas, price, value)
-}
-
-func (self *VMEnv) DelegateCall(me vm.ContractRef, addr common.Address, data []byte, gas, price *big.Int) ([]byte, error) {
- return DelegateCall(self, me, addr, data, gas, price)
-}
-
-func (self *VMEnv) Create(me vm.ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) {
- return Create(self, me, data, gas, price, value)
-}
diff --git a/eth/api.go b/eth/api.go
index b3185c392..a86ed95cf 100644
--- a/eth/api.go
+++ b/eth/api.go
@@ -515,9 +515,11 @@ func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, txHash common.
if err != nil {
return nil, fmt.Errorf("sender retrieval failed: %v", err)
}
+ context := core.NewEVMContext(msg, block.Header(), api.eth.BlockChain())
+
// 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, stateDb, api.config, vm.Config{})
_, _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()))
if err != nil {
return nil, fmt.Errorf("mutation failed: %v", err)
@@ -525,8 +527,8 @@ func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, txHash common.
stateDb.DeleteSuicides()
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, stateDb, api.config, 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 7858dee2e..b95ef79c5 100644
--- a/eth/api_backend.go
+++ b/eth/api_backend.go
@@ -106,12 +106,14 @@ 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
from := statedb.GetOrNewStateObject(msg.From())
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
+
+ context := core.NewEVMContext(msg, header, b.eth.BlockChain())
+ return vm.NewEnvironment(context, statedb, 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 fdc4a39dc..77df7eb8d 100644
--- a/internal/ethapi/backend.go
+++ b/internal/ethapi/backend.go
@@ -51,7 +51,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 5f69826a3..6d632376b 100644
--- a/internal/ethapi/tracer.go
+++ b/internal/ethapi/tracer.go
@@ -124,7 +124,7 @@ func (sw *stackWrapper) toValue(vm *otto.Otto) otto.Value {
// dbWrapper provides a JS wrapper around vm.Database
type dbWrapper struct {
- db vm.Database
+ db vm.StateDB
}
// getBalance retrieves an account's balance
@@ -278,11 +278,11 @@ 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
- jst.db.db = env.Db()
+ jst.db.db = env.StateDB
ocw := &opCodeWrapper{op}
diff --git a/internal/ethapi/tracer_test.go b/internal/ethapi/tracer_test.go
index b88178a6d..29814d783 100644
--- a/internal/ethapi/tracer_test.go
+++ b/internal/ethapi/tracer_test.go
@@ -25,62 +25,9 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/vm"
- "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
)
-type Env struct {
- gasLimit *big.Int
- depth int
- evm *vm.EVM
-}
-
-func NewEnv(config *vm.Config) *Env {
- env := &Env{gasLimit: big.NewInt(10000), depth: 0}
- env.evm = vm.New(env, *config)
- return env
-}
-
-func (self *Env) ChainConfig() *params.ChainConfig {
- return params.TestChainConfig
-}
-func (self *Env) Vm() vm.Vm { return self.evm }
-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() vm.Database { return nil }
-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 common.BytesToHash(crypto.Keccak256([]byte(big.NewInt(int64(n)).String())))
-}
-func (self *Env) AddLog(log *vm.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 vm.Account, amount *big.Int) {}
-func (self *Env) Call(caller vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
- return nil, nil
-}
-func (self *Env) CallCode(caller vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
- return nil, nil
-}
-func (self *Env) Create(caller vm.ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) {
- return nil, common.Address{}, nil
-}
-func (self *Env) DelegateCall(me vm.ContractRef, addr common.Address, data []byte, gas, price *big.Int) ([]byte, error) {
- return nil, nil
-}
-
type account struct{}
func (account) SubBalance(amount *big.Int) {}
@@ -91,17 +38,17 @@ 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(*big.Int) {}
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})
+ env := vm.NewEnvironment(vm.Context{}, nil, 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), big.NewInt(10000))
contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0}
- _, err := env.Vm().Run(contract, []byte{})
+ _, err := env.EVM().Run(contract, []byte{})
if err != nil {
return nil, err
}
@@ -186,8 +133,8 @@ 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))
+ env := vm.NewEnvironment(vm.Context{}, nil, 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/les/api_backend.go b/les/api_backend.go
index b77767ed7..8df963f6e 100644
--- a/les/api_backend.go
+++ b/les/api_backend.go
@@ -88,7 +88,7 @@ func (b *LesApiBackend) GetTd(blockHash common.Hash) *big.Int {
return b.eth.blockchain.GetTdByHash(blockHash)
}
-func (b *LesApiBackend) GetVMEnv(ctx context.Context, msg core.Message, state ethapi.State, header *types.Header) (vm.Environment, func() error, error) {
+func (b *LesApiBackend) GetVMEnv(ctx context.Context, msg core.Message, state ethapi.State, header *types.Header) (*vm.Environment, func() error, error) {
stateDb := state.(*light.LightState).Copy()
addr := msg.From()
from, err := stateDb.GetOrNewStateObject(ctx, addr)
@@ -96,8 +96,10 @@ func (b *LesApiBackend) GetVMEnv(ctx context.Context, msg core.Message, state et
return nil, nil, err
}
from.SetBalance(common.MaxBig)
- env := light.NewEnv(ctx, stateDb, b.eth.chainConfig, b.eth.blockchain, msg, header, vm.Config{})
- return env, env.Error, nil
+
+ vmstate := light.NewVMState(ctx, stateDb)
+ context := core.NewEVMContext(msg, header, b.eth.blockchain)
+ return vm.NewEnvironment(context, vmstate, b.eth.chainConfig, vm.Config{}), vmstate.Error, nil
}
func (b *LesApiBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error {
diff --git a/les/odr_test.go b/les/odr_test.go
index 80f7b8208..27e3b283d 100644
--- a/les/odr_test.go
+++ b/les/odr_test.go
@@ -115,12 +115,17 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai
if bc != nil {
header := bc.GetHeaderByHash(bhash)
statedb, err := state.New(header.Root, db)
+
if err == nil {
from := statedb.GetOrNewStateObject(testBankAddress)
from.SetBalance(common.MaxBig)
msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), big.NewInt(100000), new(big.Int), data, false)}
- vmenv := core.NewEnv(statedb, config, bc, msg, header, vm.Config{})
+
+ context := core.NewEVMContext(msg, header, bc)
+ vmenv := vm.NewEnvironment(context, statedb, config, vm.Config{})
+
+ //vmenv := core.NewEnv(statedb, config, bc, msg, header, vm.Config{})
gp := new(core.GasPool).AddGas(common.MaxBig)
ret, _, _ := core.ApplyMessage(vmenv, msg, gp)
res = append(res, ret...)
@@ -128,16 +133,20 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai
} else {
header := lc.GetHeaderByHash(bhash)
state := light.NewLightState(light.StateTrieID(header), lc.Odr())
+ vmstate := light.NewVMState(ctx, state)
from, err := state.GetOrNewStateObject(ctx, testBankAddress)
if err == nil {
from.SetBalance(common.MaxBig)
msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), big.NewInt(100000), new(big.Int), data, false)}
- vmenv := light.NewEnv(ctx, state, config, lc, msg, header, vm.Config{})
+ context := core.NewEVMContext(msg, header, lc)
+ vmenv := vm.NewEnvironment(context, vmstate, config, vm.Config{})
+
+ //vmenv := light.NewEnv(ctx, state, config, lc, msg, header, vm.Config{})
gp := new(core.GasPool).AddGas(common.MaxBig)
ret, _, _ := core.ApplyMessage(vmenv, msg, gp)
- if vmenv.Error() == nil {
+ if vmstate.Error() == nil {
res = append(res, ret...)
}
}
diff --git a/light/odr_test.go b/light/odr_test.go
index 50255a7f3..2f60f32fd 100644
--- a/light/odr_test.go
+++ b/light/odr_test.go
@@ -157,6 +157,8 @@ func (callmsg) CheckNonce() bool { return false }
func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain, lc *LightChain, bhash common.Hash) []byte {
data := common.Hex2Bytes("60CD26850000000000000000000000000000000000000000000000000000000000000000")
+ config := params.TestChainConfig
+
var res []byte
for i := 0; i < 3; i++ {
data[35] = byte(i)
@@ -168,7 +170,10 @@ func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain
from.SetBalance(common.MaxBig)
msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), big.NewInt(1000000), new(big.Int), data, false)}
- vmenv := core.NewEnv(statedb, testChainConfig(), bc, msg, header, vm.Config{})
+
+ context := core.NewEVMContext(msg, header, bc)
+ vmenv := vm.NewEnvironment(context, statedb, config, vm.Config{})
+
gp := new(core.GasPool).AddGas(common.MaxBig)
ret, _, _ := core.ApplyMessage(vmenv, msg, gp)
res = append(res, ret...)
@@ -176,15 +181,17 @@ func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain
} else {
header := lc.GetHeaderByHash(bhash)
state := NewLightState(StateTrieID(header), lc.Odr())
+ vmstate := NewVMState(ctx, state)
from, err := state.GetOrNewStateObject(ctx, testBankAddress)
if err == nil {
from.SetBalance(common.MaxBig)
msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), big.NewInt(1000000), new(big.Int), data, false)}
- vmenv := NewEnv(ctx, state, testChainConfig(), lc, msg, header, vm.Config{})
+ context := core.NewEVMContext(msg, header, lc)
+ vmenv := vm.NewEnvironment(context, vmstate, config, vm.Config{})
gp := new(core.GasPool).AddGas(common.MaxBig)
ret, _, _ := core.ApplyMessage(vmenv, msg, gp)
- if vmenv.Error() == nil {
+ if vmstate.Error() == nil {
res = append(res, ret...)
}
}
diff --git a/light/state.go b/light/state.go
index 9f2376809..f8b75c588 100644
--- a/light/state.go
+++ b/light/state.go
@@ -141,6 +141,15 @@ func (self *LightState) AddBalance(ctx context.Context, addr common.Address, amo
return err
}
+// SubBalance adds the given amount to the balance of the specified account
+func (self *LightState) SubBalance(ctx context.Context, addr common.Address, amount *big.Int) error {
+ stateObject, err := self.GetOrNewStateObject(ctx, addr)
+ if err == nil && stateObject != nil {
+ stateObject.SubBalance(amount)
+ }
+ return err
+}
+
// SetNonce sets the nonce of the specified account
func (self *LightState) SetNonce(ctx context.Context, addr common.Address, nonce uint64) error {
stateObject, err := self.GetOrNewStateObject(ctx, addr)
diff --git a/light/state_object.go b/light/state_object.go
index 6161d2dfb..56f607bff 100644
--- a/light/state_object.go
+++ b/light/state_object.go
@@ -179,7 +179,7 @@ func (c *StateObject) SetBalance(amount *big.Int) {
}
// ReturnGas returns the gas back to the origin. Used by the Virtual machine or Closures
-func (c *StateObject) ReturnGas(gas, price *big.Int) {}
+func (c *StateObject) ReturnGas(gas *big.Int) {}
// Copy creates a copy of the state object
func (self *StateObject) Copy() *StateObject {
diff --git a/light/vm_env.go b/light/vm_env.go
index d4d7bcce7..cc0c568c9 100644
--- a/light/vm_env.go
+++ b/light/vm_env.go
@@ -20,123 +20,38 @@ import (
"math/big"
"github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/core"
- "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/params"
"golang.org/x/net/context"
)
-// VMEnv is the light client version of the vm execution environment.
-// Unlike other structures, VMEnv holds a context that is applied by state
-// retrieval requests through the entire execution. If any state operation
-// returns an error, the execution fails.
-type VMEnv struct {
- vm.Environment
- ctx context.Context
- chainConfig *params.ChainConfig
- evm *vm.EVM
- state *VMState
- header *types.Header
- msg core.Message
- depth int
- chain *LightChain
- err error
-}
-
-// NewEnv creates a new execution environment based on an ODR capable light state
-func NewEnv(ctx context.Context, state *LightState, chainConfig *params.ChainConfig, chain *LightChain, msg core.Message, header *types.Header, cfg vm.Config) *VMEnv {
- env := &VMEnv{
- chainConfig: chainConfig,
- chain: chain,
- header: header,
- msg: msg,
- }
- env.state = &VMState{ctx: ctx, state: state, env: env}
-
- 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 { return self.msg.From() }
-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) 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 {
- for header := self.chain.GetHeader(self.header.ParentHash, self.header.Number.Uint64()-1); header != nil; header = self.chain.GetHeader(header.ParentHash, header.Number.Uint64()-1) {
- if header.Number.Uint64() == n {
- return header.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) SnapshotDatabase() int {
- return self.state.SnapshotDatabase()
-}
-
-func (self *VMEnv) RevertToSnapshot(idx int) {
- self.state.RevertToSnapshot(idx)
-}
-
-func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) {
- core.Transfer(from, to, amount)
-}
-
-func (self *VMEnv) Call(me vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
- return core.Call(self, me, addr, data, gas, price, value)
-}
-func (self *VMEnv) CallCode(me vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
- return core.CallCode(self, me, addr, data, gas, price, value)
-}
-
-func (self *VMEnv) DelegateCall(me vm.ContractRef, addr common.Address, data []byte, gas, price *big.Int) ([]byte, error) {
- return core.DelegateCall(self, me, addr, data, gas, price)
-}
-
-func (self *VMEnv) Create(me vm.ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) {
- return core.Create(self, me, data, gas, price, value)
-}
-
-// Error returns the error (if any) that happened during execution.
-func (self *VMEnv) Error() error {
- return self.err
-}
-
// VMState is a wrapper for the light state that holds the actual context and
// passes it to any state operation that requires it.
type VMState struct {
- vm.Database
ctx context.Context
state *LightState
snapshots []*LightState
- env *VMEnv
+ err error
+}
+
+func NewVMState(ctx context.Context, state *LightState) *VMState {
+ return &VMState{ctx: ctx, state: state}
+}
+
+func (s *VMState) Error() error {
+ return s.err
}
+func (s *VMState) AddLog(log *vm.Log) {}
+
// errHandler handles and stores any state error that happens during execution.
func (s *VMState) errHandler(err error) {
- if err != nil && s.env.err == nil {
- s.env.err = err
+ if err != nil && s.err == nil {
+ s.err = err
}
}
-func (self *VMState) SnapshotDatabase() int {
+func (self *VMState) Snapshot() int {
self.snapshots = append(self.snapshots, self.state.Copy())
return len(self.snapshots) - 1
}
@@ -175,6 +90,12 @@ func (s *VMState) AddBalance(addr common.Address, amount *big.Int) {
s.errHandler(err)
}
+// SubBalance adds the given amount to the balance of the specified account
+func (s *VMState) SubBalance(addr common.Address, amount *big.Int) {
+ err := s.state.SubBalance(s.ctx, addr, amount)
+ s.errHandler(err)
+}
+
// GetBalance retrieves the balance from the given address or 0 if the account does
// not exist
func (s *VMState) GetBalance(addr common.Address) *big.Int {
diff --git a/tests/state_test_util.go b/tests/state_test_util.go
index 117bb4b28..dc5872d98 100644
--- a/tests/state_test_util.go
+++ b/tests/state_test_util.go
@@ -18,7 +18,6 @@ package tests
import (
"bytes"
- "encoding/hex"
"fmt"
"io"
"math/big"
@@ -29,9 +28,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state"
- "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
- "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/params"
@@ -207,39 +204,21 @@ func runStateTest(chainConfig *params.ChainConfig, test VmTest) error {
}
func RunState(chainConfig *params.ChainConfig, statedb *state.StateDB, env, tx map[string]string) ([]byte, vm.Logs, *big.Int, error) {
- var (
- data = common.FromHex(tx["data"])
- gas = common.Big(tx["gasLimit"])
- price = common.Big(tx["gasPrice"])
- value = common.Big(tx["value"])
- nonce = common.Big(tx["nonce"]).Uint64()
- )
-
- var to *common.Address
- if len(tx["to"]) > 2 {
- t := common.HexToAddress(tx["to"])
- to = &t
- }
+ environment, msg := NewEVMEnvironment(false, chainConfig, statedb, env, tx)
// Set pre compiled contracts
vm.Precompiled = vm.PrecompiledContracts()
gaspool := new(core.GasPool).AddGas(common.Big(env["currentGasLimit"]))
- key, _ := hex.DecodeString(tx["secretKey"])
- addr := crypto.PubkeyToAddress(crypto.ToECDSA(key).PublicKey)
- message := types.NewMessage(addr, to, nonce, value, gas, price, data, true)
- vmenv := NewEnvFromMap(chainConfig, statedb, env, tx)
- vmenv.origin = addr
-
root, _ := statedb.Commit(false)
statedb.Reset(root)
snapshot := statedb.Snapshot()
- ret, _, err := core.ApplyMessage(vmenv, message, gaspool)
+ ret, gasUsed, err := core.ApplyMessage(environment, msg, gaspool)
if core.IsNonceErr(err) || core.IsInvalidTxErr(err) || core.IsGasLimitErr(err) {
statedb.RevertToSnapshot(snapshot)
}
- statedb.Commit(chainConfig.IsEIP158(vmenv.BlockNumber()))
+ statedb.Commit(chainConfig.IsEIP158(environment.Context.BlockNumber))
- return ret, vmenv.state.Logs(), vmenv.Gas, err
+ return ret, statedb.Logs(), gasUsed, err
}
diff --git a/tests/util.go b/tests/util.go
index 53955a47f..b545a0cc8 100644
--- a/tests/util.go
+++ b/tests/util.go
@@ -18,6 +18,7 @@ package tests
import (
"bytes"
+ "encoding/hex"
"fmt"
"math/big"
"os"
@@ -148,137 +149,63 @@ type VmTest struct {
PostStateRoot string
}
-type Env struct {
- chainConfig *params.ChainConfig
- depth int
- state *state.StateDB
- skipTransfer bool
- initial bool
- Gas *big.Int
+func NewEVMEnvironment(vmTest bool, chainConfig *params.ChainConfig, statedb *state.StateDB, envValues map[string]string, tx map[string]string) (*vm.Environment, core.Message) {
+ var (
+ data = common.FromHex(tx["data"])
+ gas = common.Big(tx["gasLimit"])
+ price = common.Big(tx["gasPrice"])
+ value = common.Big(tx["value"])
+ nonce = common.Big(tx["nonce"]).Uint64()
+ )
- 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.EVM
-}
-
-func NewEnv(chainConfig *params.ChainConfig, state *state.StateDB) *Env {
- env := &Env{
- chainConfig: chainConfig,
- state: state,
+ origin := common.HexToAddress(tx["caller"])
+ if len(tx["secretKey"]) > 0 {
+ key, _ := hex.DecodeString(tx["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)
-
- 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.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) 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 to *common.Address
+ if len(tx["to"]) > 2 {
+ t := common.HexToAddress(tx["to"])
+ to = &t
}
- 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)
-}
+ msg := types.NewMessage(origin, to, nonce, value, gas, price, data, true)
-func (self *Env) Transfer(from, to vm.Account, amount *big.Int) {
- if self.skipTransfer {
- return
- }
- core.Transfer(from, to, amount)
-}
-
-func (self *Env) Call(caller vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
- if self.vmTest && self.depth > 0 {
- caller.ReturnGas(gas, price)
-
- return nil, nil
+ initialCall := true
+ canTransfer := func(db vm.StateDB, address common.Address, amount *big.Int) bool {
+ if vmTest {
+ if initialCall {
+ initialCall = false
+ return true
+ }
+ }
+ return core.CanTransfer(db, address, amount)
}
- ret, err := core.Call(self, caller, addr, data, gas, price, value)
- self.Gas = gas
-
- return ret, err
-
-}
-func (self *Env) CallCode(caller vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
- if self.vmTest && self.depth > 0 {
- caller.ReturnGas(gas, price)
-
- return nil, nil
+ transfer := func(db vm.StateDB, sender, recipient common.Address, amount *big.Int) {
+ if vmTest {
+ return
+ }
+ core.Transfer(db, sender, recipient, amount)
}
- return core.CallCode(self, caller, addr, data, gas, price, value)
-}
-func (self *Env) DelegateCall(caller vm.ContractRef, addr common.Address, data []byte, gas, price *big.Int) ([]byte, error) {
- if self.vmTest && self.depth > 0 {
- caller.ReturnGas(gas, price)
-
- return nil, nil
+ context := vm.Context{
+ CanTransfer: canTransfer,
+ Transfer: transfer,
+ GetHash: func(n uint64) common.Hash {
+ return common.BytesToHash(crypto.Keccak256([]byte(big.NewInt(int64(n)).String())))
+ },
+
+ Origin: origin,
+ Coinbase: common.HexToAddress(envValues["currentCoinbase"]),
+ BlockNumber: common.Big(envValues["currentNumber"]),
+ Time: common.Big(envValues["currentTimestamp"]),
+ GasLimit: common.Big(envValues["currentGasLimit"]),
+ Difficulty: common.Big(envValues["currentDifficulty"]),
+ GasPrice: price,
}
- return core.DelegateCall(self, caller, addr, data, gas, price)
-}
-
-func (self *Env) Create(caller vm.ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) {
- if self.vmTest {
- caller.ReturnGas(gas, price)
-
- 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, price, value)
+ if context.GasPrice == nil {
+ context.GasPrice = new(big.Int)
}
+ return vm.NewEnvironment(context, statedb, chainConfig, vm.Config{NoRecursion: vmTest}), msg
}
diff --git a/tests/vm_test_util.go b/tests/vm_test_util.go
index 50660134b..e23fda5ad 100644
--- a/tests/vm_test_util.go
+++ b/tests/vm_test_util.go
@@ -211,30 +211,23 @@ 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) {
+ chainConfig := &params.ChainConfig{
+ HomesteadBlock: params.MainNetHomesteadBlock,
+ DAOForkBlock: params.MainNetDAOForkBlock,
+ DAOForkSupport: true,
+ }
var (
to = common.HexToAddress(exec["address"])
from = common.HexToAddress(exec["caller"])
data = common.FromHex(exec["data"])
gas = common.Big(exec["gas"])
- price = common.Big(exec["gasPrice"])
value = common.Big(exec["value"])
)
- // Reset the pre-compiled contracts for VM tests.
+ caller := statedb.GetOrNewStateObject(from)
vm.Precompiled = make(map[string]*vm.PrecompiledAccount)
- caller := state.GetOrNewStateObject(from)
-
- chainConfig := &params.ChainConfig{
- HomesteadBlock: params.MainNetHomesteadBlock,
- DAOForkBlock: params.MainNetDAOForkBlock,
- DAOForkSupport: true,
- }
- vmenv := NewEnvFromMap(chainConfig, state, env, exec)
- vmenv.vmTest = true
- vmenv.skipTransfer = true
- vmenv.initial = true
- ret, err := vmenv.Call(caller, to, data, gas, price, value)
-
- return ret, vmenv.state.Logs(), vmenv.Gas, err
+ environment, _ := NewEVMEnvironment(true, chainConfig, statedb, env, exec)
+ ret, err := environment.Call(caller, to, data, gas, value)
+ return ret, statedb.Logs(), gas, err
}