mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-26 14:46:42 +00:00
core, tests, miner, cmd: improved EVM environment
Reduced the overhead caused by all the diffirent implementations of the vm.Environment interface. Where previously the Environment was an interface it has now become an implementation it self using several new easy to implement interfaces such as the Backend that takes care of the state and CallContext which takes care of the EVM calling conventions. Most of these interfaces have been implemented in the core package with the exception of the test suit which uses a combination of the core package implementations and a mix of its own. This will reduce the amount of extra overhead when changing an interface, such as adding a new method or changing a method signature.
This commit is contained in:
parent
af2eb0de97
commit
2d6a950bbc
32 changed files with 743 additions and 916 deletions
|
|
@ -225,7 +225,14 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallM
|
||||||
from.SetBalance(common.MaxBig)
|
from.SetBalance(common.MaxBig)
|
||||||
// Execute the call.
|
// Execute the call.
|
||||||
msg := callmsg{call}
|
msg := callmsg{call}
|
||||||
vmenv := core.NewEnv(statedb, chainConfig, b.blockchain, msg, block.Header(), vm.Config{})
|
|
||||||
|
backend := &core.EVMBackend{
|
||||||
|
GetHashFn: core.GetHashFn(block.ParentHash(), b.blockchain),
|
||||||
|
State: statedb,
|
||||||
|
}
|
||||||
|
context := core.ToEVMContext(chainConfig, msg, block.Header())
|
||||||
|
vmenv := vm.NewEnvironment(context, backend, chainConfig, vm.Config{})
|
||||||
|
|
||||||
gaspool := new(core.GasPool).AddGas(common.MaxBig)
|
gaspool := new(core.GasPool).AddGas(common.MaxBig)
|
||||||
ret, gasUsed, _, err := core.NewStateTransition(vmenv, msg, gaspool).TransitionDb()
|
ret, gasUsed, _, err := core.NewStateTransition(vmenv, msg, gaspool).TransitionDb()
|
||||||
return ret, gasUsed, err
|
return ret, gasUsed, err
|
||||||
|
|
|
||||||
190
cmd/evm/main.go
190
cmd/evm/main.go
|
|
@ -29,7 +29,6 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"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"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
|
|
@ -47,14 +46,6 @@ var (
|
||||||
Name: "debug",
|
Name: "debug",
|
||||||
Usage: "output full trace logs",
|
Usage: "output full trace logs",
|
||||||
}
|
}
|
||||||
ForceJitFlag = cli.BoolFlag{
|
|
||||||
Name: "forcejit",
|
|
||||||
Usage: "forces jit compilation",
|
|
||||||
}
|
|
||||||
DisableJitFlag = cli.BoolFlag{
|
|
||||||
Name: "nojit",
|
|
||||||
Usage: "disabled jit compilation",
|
|
||||||
}
|
|
||||||
CodeFlag = cli.StringFlag{
|
CodeFlag = cli.StringFlag{
|
||||||
Name: "code",
|
Name: "code",
|
||||||
Usage: "EVM code",
|
Usage: "EVM code",
|
||||||
|
|
@ -65,19 +56,49 @@ var (
|
||||||
}
|
}
|
||||||
GasFlag = cli.StringFlag{
|
GasFlag = cli.StringFlag{
|
||||||
Name: "gas",
|
Name: "gas",
|
||||||
Usage: "gas limit for the evm",
|
Usage: "set the gas limit for the EVM execution",
|
||||||
Value: "10000000000",
|
Value: "10000000000",
|
||||||
}
|
}
|
||||||
PriceFlag = cli.StringFlag{
|
PriceFlag = cli.StringFlag{
|
||||||
Name: "price",
|
Name: "price",
|
||||||
Usage: "price set for the evm",
|
Usage: "set the price for the EVM execution",
|
||||||
Value: "0",
|
Value: "0",
|
||||||
}
|
}
|
||||||
ValueFlag = cli.StringFlag{
|
ValueFlag = cli.StringFlag{
|
||||||
Name: "value",
|
Name: "value",
|
||||||
Usage: "value set for the evm",
|
Usage: "set the value for the EVM execution",
|
||||||
Value: "0",
|
Value: "0",
|
||||||
}
|
}
|
||||||
|
SenderFlag = cli.StringFlag{
|
||||||
|
Name: "origin",
|
||||||
|
Usage: "set the origin for the EVM execution",
|
||||||
|
Value: "0x",
|
||||||
|
}
|
||||||
|
CoinbaseFlag = cli.StringFlag{
|
||||||
|
Name: "coinbase",
|
||||||
|
Usage: "coinbase set for the evm",
|
||||||
|
Value: "0x",
|
||||||
|
}
|
||||||
|
BlockNumberFlag = cli.StringFlag{
|
||||||
|
Name: "blocknumber",
|
||||||
|
Usage: "set the block number for the EVM execution",
|
||||||
|
Value: "1",
|
||||||
|
}
|
||||||
|
BlockTimeFlag = cli.StringFlag{
|
||||||
|
Name: "blocktime",
|
||||||
|
Usage: "set the block time for the EVM execution",
|
||||||
|
Value: "1",
|
||||||
|
}
|
||||||
|
BlockDifficultyFlag = cli.StringFlag{
|
||||||
|
Name: "difficulty",
|
||||||
|
Usage: "set the block difficulty for the EVM execution",
|
||||||
|
Value: "1",
|
||||||
|
}
|
||||||
|
GasLimitFlag = cli.StringFlag{
|
||||||
|
Name: "gaslimit",
|
||||||
|
Usage: "sets the gas limit for the EVM execution",
|
||||||
|
Value: "1",
|
||||||
|
}
|
||||||
DumpFlag = cli.BoolFlag{
|
DumpFlag = cli.BoolFlag{
|
||||||
Name: "dump",
|
Name: "dump",
|
||||||
Usage: "dumps the state after the run",
|
Usage: "dumps the state after the run",
|
||||||
|
|
@ -105,8 +126,6 @@ func init() {
|
||||||
CreateFlag,
|
CreateFlag,
|
||||||
DebugFlag,
|
DebugFlag,
|
||||||
VerbosityFlag,
|
VerbosityFlag,
|
||||||
ForceJitFlag,
|
|
||||||
DisableJitFlag,
|
|
||||||
SysStatFlag,
|
SysStatFlag,
|
||||||
CodeFlag,
|
CodeFlag,
|
||||||
CodeFileFlag,
|
CodeFileFlag,
|
||||||
|
|
@ -115,6 +134,11 @@ func init() {
|
||||||
ValueFlag,
|
ValueFlag,
|
||||||
DumpFlag,
|
DumpFlag,
|
||||||
InputFlag,
|
InputFlag,
|
||||||
|
GasLimitFlag,
|
||||||
|
CoinbaseFlag,
|
||||||
|
SenderFlag,
|
||||||
|
BlockNumberFlag,
|
||||||
|
BlockTimeFlag,
|
||||||
}
|
}
|
||||||
app.Action = run
|
app.Action = run
|
||||||
}
|
}
|
||||||
|
|
@ -124,23 +148,49 @@ func run(ctx *cli.Context) error {
|
||||||
glog.SetV(ctx.GlobalInt(VerbosityFlag.Name))
|
glog.SetV(ctx.GlobalInt(VerbosityFlag.Name))
|
||||||
|
|
||||||
db, _ := ethdb.NewMemDatabase()
|
db, _ := ethdb.NewMemDatabase()
|
||||||
statedb, _ := state.New(common.Hash{}, db)
|
|
||||||
sender := statedb.CreateAccount(common.StringToAddress("sender"))
|
|
||||||
|
|
||||||
logger := vm.NewStructLogger(nil)
|
logger := vm.NewStructLogger(nil)
|
||||||
vmenv := NewEnv(statedb, common.StringToAddress("evmuser"), common.Big(ctx.GlobalString(ValueFlag.Name)), common.Big(ctx.GlobalString(PriceFlag.Name)), vm.Config{
|
|
||||||
Debug: ctx.GlobalBool(DebugFlag.Name),
|
|
||||||
ForceJit: ctx.GlobalBool(ForceJitFlag.Name),
|
|
||||||
EnableJit: !ctx.GlobalBool(DisableJitFlag.Name),
|
|
||||||
Tracer: logger,
|
|
||||||
})
|
|
||||||
|
|
||||||
tstart := time.Now()
|
db, err := ethdb.NewMemDatabase()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
st, err := state.New(common.Hash{}, db)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
backend := &core.EVMBackend{
|
||||||
|
GetHashFn: func(n uint64) common.Hash {
|
||||||
|
return common.BytesToHash(crypto.Keccak256([]byte(big.NewInt(int64(n)).String())))
|
||||||
|
},
|
||||||
|
State: st,
|
||||||
|
}
|
||||||
|
context := vm.Context{
|
||||||
|
CallContext: core.EVMCallContext{core.CanTransfer, core.Transfer},
|
||||||
|
Origin: common.StringToAddress(ctx.GlobalString(SenderFlag.Name)),
|
||||||
|
Coinbase: common.StringToAddress(ctx.GlobalString(CoinbaseFlag.Name)),
|
||||||
|
BlockNumber: common.String2Big(ctx.GlobalString(BlockNumberFlag.Name)),
|
||||||
|
Time: common.String2Big(ctx.GlobalString(BlockTimeFlag.Name)),
|
||||||
|
Difficulty: common.String2Big(ctx.GlobalString(BlockDifficultyFlag.Name)),
|
||||||
|
GasLimit: common.String2Big(ctx.GlobalString(GasLimitFlag.Name)),
|
||||||
|
GasPrice: common.String2Big(ctx.GlobalString(PriceFlag.Name)),
|
||||||
|
}
|
||||||
|
|
||||||
|
vmenv := vm.NewEnvironment(
|
||||||
|
context,
|
||||||
|
backend,
|
||||||
|
params.TestChainConfig,
|
||||||
|
vm.Config{
|
||||||
|
Debug: ctx.GlobalBool(DebugFlag.Name),
|
||||||
|
Tracer: logger,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
code []byte
|
code []byte
|
||||||
ret []byte
|
ret []byte
|
||||||
err error
|
tstart = time.Now()
|
||||||
|
sender = st.CreateAccount(context.Origin)
|
||||||
)
|
)
|
||||||
|
|
||||||
if ctx.GlobalString(CodeFlag.Name) != "" {
|
if ctx.GlobalString(CodeFlag.Name) != "" {
|
||||||
|
|
@ -174,8 +224,7 @@ func run(ctx *cli.Context) error {
|
||||||
common.Big(ctx.GlobalString(ValueFlag.Name)),
|
common.Big(ctx.GlobalString(ValueFlag.Name)),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
receiver := statedb.CreateAccount(common.StringToAddress("receiver"))
|
receiver := st.CreateAccount(common.StringToAddress("receiver"))
|
||||||
|
|
||||||
receiver.SetCode(crypto.Keccak256Hash(code), code)
|
receiver.SetCode(crypto.Keccak256Hash(code), code)
|
||||||
ret, err = vmenv.Call(
|
ret, err = vmenv.Call(
|
||||||
sender,
|
sender,
|
||||||
|
|
@ -188,8 +237,7 @@ func run(ctx *cli.Context) error {
|
||||||
vmdone := time.Since(tstart)
|
vmdone := time.Since(tstart)
|
||||||
|
|
||||||
if ctx.GlobalBool(DumpFlag.Name) {
|
if ctx.GlobalBool(DumpFlag.Name) {
|
||||||
statedb.Commit(true)
|
fmt.Println(string(st.Dump()))
|
||||||
fmt.Println(string(statedb.Dump()))
|
|
||||||
}
|
}
|
||||||
vm.StdErrFormat(logger.StructLogs())
|
vm.StdErrFormat(logger.StructLogs())
|
||||||
|
|
||||||
|
|
@ -220,89 +268,3 @@ func main() {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type VMEnv struct {
|
|
||||||
state *state.StateDB
|
|
||||||
block *types.Block
|
|
||||||
|
|
||||||
transactor *common.Address
|
|
||||||
value *big.Int
|
|
||||||
|
|
||||||
depth int
|
|
||||||
Gas, price *big.Int
|
|
||||||
time *big.Int
|
|
||||||
logs []vm.StructLog
|
|
||||||
|
|
||||||
evm *vm.EVM
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewEnv(state *state.StateDB, transactor common.Address, value, price *big.Int, cfg vm.Config) *VMEnv {
|
|
||||||
env := &VMEnv{
|
|
||||||
state: state,
|
|
||||||
transactor: &transactor,
|
|
||||||
value: value,
|
|
||||||
price: price,
|
|
||||||
time: big.NewInt(time.Now().Unix()),
|
|
||||||
}
|
|
||||||
|
|
||||||
env.evm = vm.New(env, cfg)
|
|
||||||
return env
|
|
||||||
}
|
|
||||||
|
|
||||||
// ruleSet implements vm.ChainConfig and will always default to the homestead rule set.
|
|
||||||
type ruleSet struct{}
|
|
||||||
|
|
||||||
func (ruleSet) IsHomestead(*big.Int) bool { return true }
|
|
||||||
func (ruleSet) GasTable(*big.Int) params.GasTable {
|
|
||||||
return params.GasTableHomesteadGasRepriceFork
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *VMEnv) ChainConfig() *params.ChainConfig { return params.TestChainConfig }
|
|
||||||
func (self *VMEnv) Vm() vm.Vm { return self.evm }
|
|
||||||
func (self *VMEnv) Db() vm.Database { return self.state }
|
|
||||||
func (self *VMEnv) SnapshotDatabase() int { return self.state.Snapshot() }
|
|
||||||
func (self *VMEnv) RevertToSnapshot(snap int) { self.state.RevertToSnapshot(snap) }
|
|
||||||
func (self *VMEnv) Origin() common.Address { return *self.transactor }
|
|
||||||
func (self *VMEnv) BlockNumber() *big.Int { return common.Big0 }
|
|
||||||
func (self *VMEnv) Coinbase() common.Address { return *self.transactor }
|
|
||||||
func (self *VMEnv) Time() *big.Int { return self.time }
|
|
||||||
func (self *VMEnv) Difficulty() *big.Int { return common.Big1 }
|
|
||||||
func (self *VMEnv) BlockHash() []byte { return make([]byte, 32) }
|
|
||||||
func (self *VMEnv) Value() *big.Int { return self.value }
|
|
||||||
func (self *VMEnv) GasLimit() *big.Int { return big.NewInt(1000000000) }
|
|
||||||
func (self *VMEnv) GasPrice() *big.Int { return big.NewInt(1000000000) }
|
|
||||||
func (self *VMEnv) VmType() vm.Type { return vm.StdVmTy }
|
|
||||||
func (self *VMEnv) Depth() int { return 0 }
|
|
||||||
func (self *VMEnv) SetDepth(i int) { self.depth = i }
|
|
||||||
func (self *VMEnv) GetHash(n uint64) common.Hash {
|
|
||||||
if self.block.Number().Cmp(big.NewInt(int64(n))) == 0 {
|
|
||||||
return self.block.Hash()
|
|
||||||
}
|
|
||||||
return common.Hash{}
|
|
||||||
}
|
|
||||||
func (self *VMEnv) AddLog(log *vm.Log) {
|
|
||||||
self.state.AddLog(log)
|
|
||||||
}
|
|
||||||
func (self *VMEnv) CanTransfer(from common.Address, balance *big.Int) bool {
|
|
||||||
return self.state.GetBalance(from).Cmp(balance) >= 0
|
|
||||||
}
|
|
||||||
func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) {
|
|
||||||
core.Transfer(from, to, amount)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *VMEnv) Call(caller vm.ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) {
|
|
||||||
self.Gas = gas
|
|
||||||
return core.Call(self, caller, addr, data, gas, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *VMEnv) CallCode(caller vm.ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) {
|
|
||||||
return core.CallCode(self, caller, addr, data, gas, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *VMEnv) DelegateCall(caller vm.ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error) {
|
|
||||||
return core.DelegateCall(self, caller, addr, data, gas)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *VMEnv) Create(caller vm.ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error) {
|
|
||||||
return core.Create(self, caller, data, gas, value)
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -195,7 +195,16 @@ func (be *registryAPIBackend) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr
|
||||||
}
|
}
|
||||||
|
|
||||||
header := be.bc.CurrentBlock().Header()
|
header := be.bc.CurrentBlock().Header()
|
||||||
vmenv := core.NewEnv(statedb, be.config, be.bc, msg, header, vm.Config{})
|
|
||||||
|
backend := &core.EVMBackend{
|
||||||
|
GetHashFn: core.GetHashFn(header.ParentHash, be.bc),
|
||||||
|
State: statedb,
|
||||||
|
}
|
||||||
|
|
||||||
|
context := core.ToEVMContext(be.config, msg, header)
|
||||||
|
vmenv := vm.NewEnvironment(context, backend, be.config, vm.Config{})
|
||||||
|
|
||||||
|
//vmenv := vm.NewEnvironment(false, statedb, be.config, be.bc, msg, header, vm.Config{})
|
||||||
gp := new(core.GasPool).AddGas(common.MaxBig)
|
gp := new(core.GasPool).AddGas(common.MaxBig)
|
||||||
res, gas, err := core.ApplyMessage(vmenv, msg, gp)
|
res, gas, err := core.ApplyMessage(vmenv, msg, gp)
|
||||||
|
|
||||||
|
|
|
||||||
97
core/evm.go
Normal file
97
core/evm.go
Normal file
|
|
@ -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 core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ToEVMContext creates a new context for use in the EVM.
|
||||||
|
func ToEVMContext(config *params.ChainConfig, msg Message, header *types.Header) vm.Context {
|
||||||
|
var from common.Address
|
||||||
|
if config.IsHomestead(header.Number) {
|
||||||
|
from, _ = msg.From()
|
||||||
|
} else {
|
||||||
|
from, _ = msg.FromFrontier()
|
||||||
|
}
|
||||||
|
|
||||||
|
return vm.Context{
|
||||||
|
CallContext: EVMCallContext{CanTransfer, Transfer},
|
||||||
|
Origin: from,
|
||||||
|
Coinbase: header.Coinbase,
|
||||||
|
BlockNumber: new(big.Int).Set(header.Number),
|
||||||
|
Time: new(big.Int).Set(header.Time),
|
||||||
|
Difficulty: new(big.Int).Set(header.Difficulty),
|
||||||
|
GasLimit: new(big.Int).Set(header.GasLimit),
|
||||||
|
GasPrice: new(big.Int).Set(msg.GasPrice()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHashFn returns a function for which the VM env can query block hashes through
|
||||||
|
// up to the limit defined by the Yellow Paper and uses the given block chain
|
||||||
|
// to query for information.
|
||||||
|
func GetHashFn(ref common.Hash, chain *BlockChain) func(n uint64) common.Hash {
|
||||||
|
return func(n uint64) common.Hash {
|
||||||
|
for block := chain.GetBlockByHash(ref); block != nil; block = chain.GetBlock(block.ParentHash(), block.NumberU64()-1) {
|
||||||
|
if block.NumberU64() == n {
|
||||||
|
return block.Hash()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return common.Hash{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CanTransfer checks wether there are enough funds in the address' account to make a transfer.
|
||||||
|
func CanTransfer(db vm.Database, addr common.Address, amount *big.Int) bool {
|
||||||
|
return db.GetBalance(addr).Cmp(amount) >= 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transfer subtracts amount from sender and adds amount to recipient using the given Db
|
||||||
|
func Transfer(db vm.Database, sender, recipient common.Address, amount *big.Int) {
|
||||||
|
db.SubBalance(sender, amount)
|
||||||
|
db.AddBalance(recipient, amount)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EVMBackend implements vm.Backend and provides an interface to keep track of the current
|
||||||
|
// state.
|
||||||
|
type EVMBackend struct {
|
||||||
|
GetHashFn func(uint64) common.Hash // getHashFn callback is used to retrieve block hashes
|
||||||
|
State *state.StateDB
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeSnapshot returns a copy of the state.
|
||||||
|
func (b *EVMBackend) SnapshotDatabase() int {
|
||||||
|
return b.State.Snapshot()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns the state
|
||||||
|
func (b *EVMBackend) Get() vm.Database { return b.State }
|
||||||
|
|
||||||
|
// Set sets the current state
|
||||||
|
func (b *EVMBackend) RevertToSnapshot(revision int) { b.State.RevertToSnapshot(revision) }
|
||||||
|
|
||||||
|
// GetHash returns the canonical hash referenced by the depth
|
||||||
|
func (b *EVMBackend) GetHash(n uint64) common.Hash {
|
||||||
|
return b.GetHashFn(n)
|
||||||
|
}
|
||||||
|
|
@ -25,31 +25,36 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type EVMCallContext struct {
|
||||||
|
CanTransfer func(db vm.Database, addr common.Address, amount *big.Int) bool
|
||||||
|
Transfer func(db vm.Database, sender, recipient common.Address, amount *big.Int)
|
||||||
|
}
|
||||||
|
|
||||||
// Call executes within the given contract
|
// Call executes within the given contract
|
||||||
func Call(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, value *big.Int) (ret []byte, err error) {
|
func (c EVMCallContext) Call(env *vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, value *big.Int) (ret []byte, err error) {
|
||||||
ret, _, err = exec(true, env, caller, &addr, &addr, env.Db().GetCodeHash(addr), input, env.Db().GetCode(addr), gas, value)
|
ret, _, err = c.exec(true, env, caller, &addr, &addr, env.Db().GetCodeHash(addr), input, env.Db().GetCode(addr), gas, value)
|
||||||
return ret, err
|
return ret, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// CallCode executes the given address' code as the given contract address
|
// CallCode executes the given address' code as the given contract address
|
||||||
func CallCode(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, value *big.Int) (ret []byte, err error) {
|
func (c EVMCallContext) CallCode(env *vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, value *big.Int) (ret []byte, err error) {
|
||||||
callerAddr := caller.Address()
|
callerAddr := caller.Address()
|
||||||
ret, _, err = exec(false, env, caller, &callerAddr, &addr, env.Db().GetCodeHash(addr), input, env.Db().GetCode(addr), gas, value)
|
ret, _, err = c.exec(false, env, caller, &callerAddr, &addr, env.Db().GetCodeHash(addr), input, env.Db().GetCode(addr), gas, value)
|
||||||
return ret, err
|
return ret, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// DelegateCall is equivalent to CallCode except that sender and value propagates from parent scope to child scope
|
// DelegateCall is equivalent to CallCode except that sender and value propagates from parent scope to child scope
|
||||||
func DelegateCall(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas *big.Int) (ret []byte, err error) {
|
func (c EVMCallContext) DelegateCall(env *vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas *big.Int) (ret []byte, err error) {
|
||||||
callerAddr := caller.Address()
|
callerAddr := caller.Address()
|
||||||
originAddr := env.Origin()
|
originAddr := env.Origin
|
||||||
callerValue := caller.Value()
|
callerValue := caller.Value()
|
||||||
ret, _, err = execDelegateCall(env, caller, &originAddr, &callerAddr, &addr, env.Db().GetCodeHash(addr), input, env.Db().GetCode(addr), gas, callerValue)
|
ret, _, err = c.execDelegateCall(env, caller, &originAddr, &callerAddr, &addr, env.Db().GetCodeHash(addr), input, env.Db().GetCode(addr), gas, callerValue)
|
||||||
return ret, err
|
return ret, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create creates a new contract with the given code
|
// Create creates a new contract with the given code
|
||||||
func Create(env vm.Environment, caller vm.ContractRef, code []byte, gas, value *big.Int) (ret []byte, address common.Address, err error) {
|
func (c EVMCallContext) Create(env *vm.Environment, caller vm.ContractRef, code []byte, gas, value *big.Int) (ret []byte, address common.Address, err error) {
|
||||||
ret, address, err = exec(true, env, caller, nil, nil, crypto.Keccak256Hash(code), nil, code, gas, value)
|
ret, address, err = c.exec(true, env, caller, nil, nil, crypto.Keccak256Hash(code), nil, code, gas, value)
|
||||||
// Here we get an error if we run into maximum stack depth,
|
// Here we get an error if we run into maximum stack depth,
|
||||||
// See: https://github.com/ethereum/yellowpaper/pull/131
|
// See: https://github.com/ethereum/yellowpaper/pull/131
|
||||||
// and YP definitions for CREATE instruction
|
// and YP definitions for CREATE instruction
|
||||||
|
|
@ -59,17 +64,17 @@ func Create(env vm.Environment, caller vm.ContractRef, code []byte, gas, value *
|
||||||
return ret, address, err
|
return ret, address, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func exec(transfers bool, env vm.Environment, caller vm.ContractRef, address, codeAddr *common.Address, codeHash common.Hash, input, code []byte, gas, value *big.Int) (ret []byte, addr common.Address, err error) {
|
func (c EVMCallContext) exec(transfers bool, env *vm.Environment, caller vm.ContractRef, address, codeAddr *common.Address, codeHash common.Hash, input, code []byte, gas, value *big.Int) (ret []byte, addr common.Address, err error) {
|
||||||
evm := env.Vm()
|
evm := env.Vm()
|
||||||
// Depth check execution. Fail if we're trying to execute above the
|
// Depth check execution. Fail if we're trying to execute above the
|
||||||
// limit.
|
// limit.
|
||||||
if env.Depth() > int(params.CallCreateDepth.Int64()) {
|
if env.Depth > int(params.CallCreateDepth.Int64()) {
|
||||||
caller.ReturnGas(gas.Uint64())
|
caller.ReturnGas(gas.Uint64())
|
||||||
|
|
||||||
return nil, common.Address{}, vm.DepthError
|
return nil, common.Address{}, vm.DepthError
|
||||||
}
|
}
|
||||||
|
|
||||||
if !env.CanTransfer(caller.Address(), value) {
|
if !c.CanTransfer(env.Db(), caller.Address(), value) {
|
||||||
caller.ReturnGas(gas.Uint64())
|
caller.ReturnGas(gas.Uint64())
|
||||||
|
|
||||||
return nil, common.Address{}, ValueTransferErr("insufficient funds to transfer value. Req %v, has %v", value, env.Db().GetBalance(caller.Address()))
|
return nil, common.Address{}, ValueTransferErr("insufficient funds to transfer value. Req %v, has %v", value, env.Db().GetBalance(caller.Address()))
|
||||||
|
|
@ -85,14 +90,14 @@ func exec(transfers bool, env vm.Environment, caller vm.ContractRef, address, co
|
||||||
createAccount = true
|
createAccount = true
|
||||||
}
|
}
|
||||||
|
|
||||||
snapshotPreTransfer := env.SnapshotDatabase()
|
snapshotPreTransfer := env.Backend.SnapshotDatabase()
|
||||||
var (
|
var (
|
||||||
from = env.Db().GetAccount(caller.Address())
|
from = env.Db().GetAccount(caller.Address())
|
||||||
to vm.Account
|
to vm.Account
|
||||||
)
|
)
|
||||||
if createAccount {
|
if createAccount {
|
||||||
to = env.Db().CreateAccount(*address)
|
to = env.Db().CreateAccount(*address)
|
||||||
if env.ChainConfig().IsEIP158(env.BlockNumber()) {
|
if env.ChainConfig().IsEIP158(env.BlockNumber) {
|
||||||
env.Db().SetNonce(*address, 1)
|
env.Db().SetNonce(*address, 1)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -103,7 +108,7 @@ func exec(transfers bool, env vm.Environment, caller vm.ContractRef, address, co
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if transfers {
|
if transfers {
|
||||||
env.Transfer(from, to, value)
|
c.Transfer(env.Db(), from.Address(), to.Address(), value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// initialise a new contract and set the code that is to be used by the
|
// initialise a new contract and set the code that is to be used by the
|
||||||
|
|
@ -131,25 +136,25 @@ func exec(transfers bool, env vm.Environment, caller vm.ContractRef, address, co
|
||||||
// When an error was returned by the EVM or when setting the creation code
|
// 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
|
// 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.
|
// when we're in homestead this also counts for code storage gas errors.
|
||||||
if err != nil && (env.ChainConfig().IsHomestead(env.BlockNumber()) || err != vm.CodeStoreOutOfGasError) {
|
if err != nil && (env.ChainConfig().IsHomestead(env.BlockNumber) || err != vm.CodeStoreOutOfGasError) {
|
||||||
contract.UseGas(contract.Gas())
|
contract.UseGas(contract.Gas())
|
||||||
|
|
||||||
env.RevertToSnapshot(snapshotPreTransfer)
|
env.Backend.RevertToSnapshot(snapshotPreTransfer)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret, addr, err
|
return ret, addr, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func execDelegateCall(env vm.Environment, caller vm.ContractRef, originAddr, toAddr, codeAddr *common.Address, codeHash common.Hash, input, code []byte, gas, value *big.Int) (ret []byte, addr common.Address, err error) {
|
func (c EVMCallContext) execDelegateCall(env *vm.Environment, caller vm.ContractRef, originAddr, toAddr, codeAddr *common.Address, codeHash common.Hash, input, code []byte, gas, value *big.Int) (ret []byte, addr common.Address, err error) {
|
||||||
evm := env.Vm()
|
evm := env.Vm()
|
||||||
// Depth check execution. Fail if we're trying to execute above the
|
// Depth check execution. Fail if we're trying to execute above the
|
||||||
// limit.
|
// limit.
|
||||||
if env.Depth() > int(params.CallCreateDepth.Int64()) {
|
if env.Depth > int(params.CallCreateDepth.Int64()) {
|
||||||
caller.ReturnGas(gas.Uint64())
|
caller.ReturnGas(gas.Uint64())
|
||||||
return nil, common.Address{}, vm.DepthError
|
return nil, common.Address{}, vm.DepthError
|
||||||
}
|
}
|
||||||
|
|
||||||
snapshot := env.SnapshotDatabase()
|
snapshot := env.Backend.SnapshotDatabase()
|
||||||
|
|
||||||
var to vm.Account
|
var to vm.Account
|
||||||
if !env.Db().Exist(*toAddr) {
|
if !env.Db().Exist(*toAddr) {
|
||||||
|
|
@ -167,14 +172,8 @@ func execDelegateCall(env vm.Environment, caller vm.ContractRef, originAddr, toA
|
||||||
if err != nil {
|
if err != nil {
|
||||||
contract.UseGas(contract.Gas())
|
contract.UseGas(contract.Gas())
|
||||||
|
|
||||||
env.RevertToSnapshot(snapshot)
|
env.Backend.RevertToSnapshot(snapshot)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret, addr, err
|
return ret, addr, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// generic transfer method
|
|
||||||
func Transfer(from, to vm.Account, amount *big.Int) {
|
|
||||||
from.SubBalance(amount)
|
|
||||||
to.AddBalance(amount)
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,24 @@ func (self *StateDB) RawDump() Dump {
|
||||||
}
|
}
|
||||||
dump.Accounts[common.Bytes2Hex(addr)] = account
|
dump.Accounts[common.Bytes2Hex(addr)] = account
|
||||||
}
|
}
|
||||||
|
for addr, stateObject := range self.stateObjects {
|
||||||
|
account := DumpAccount{
|
||||||
|
Balance: stateObject.Balance().String(),
|
||||||
|
Nonce: stateObject.Nonce(),
|
||||||
|
Root: common.Bytes2Hex(stateObject.data.Root[:]),
|
||||||
|
CodeHash: common.Bytes2Hex(stateObject.CodeHash()),
|
||||||
|
Code: common.Bytes2Hex(stateObject.Code(self.db)),
|
||||||
|
Storage: make(map[string]string),
|
||||||
|
}
|
||||||
|
storageIt := stateObject.getTrie(self.db).Iterator()
|
||||||
|
for storageIt.Next() {
|
||||||
|
account.Storage[common.Bytes2Hex(self.trie.GetKey(storageIt.Key))] = common.Bytes2Hex(storageIt.Value)
|
||||||
|
}
|
||||||
|
for storageAddr, value := range stateObject.cachedStorage {
|
||||||
|
account.Storage[common.Bytes2Hex(storageAddr[:])] = value.Hex()
|
||||||
|
}
|
||||||
|
dump.Accounts[common.Bytes2Hex(addr[:])] = account
|
||||||
|
}
|
||||||
return dump
|
return dump
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -302,6 +302,13 @@ func (self *StateDB) AddBalance(addr common.Address, amount *big.Int) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *StateDB) SubBalance(addr common.Address, amount *big.Int) {
|
||||||
|
stateObject := self.GetOrNewStateObject(addr)
|
||||||
|
if stateObject != nil {
|
||||||
|
stateObject.SubBalance(amount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (self *StateDB) SetBalance(addr common.Address, amount *big.Int) {
|
func (self *StateDB) SetBalance(addr common.Address, amount *big.Int) {
|
||||||
stateObject := self.GetOrNewStateObject(addr)
|
stateObject := self.GetOrNewStateObject(addr)
|
||||||
if stateObject != nil {
|
if stateObject != nil {
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,15 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
|
||||||
// ApplyTransactions returns the generated receipts and vm logs during the
|
// ApplyTransactions returns the generated receipts and vm logs during the
|
||||||
// execution of the state transition phase.
|
// execution of the state transition phase.
|
||||||
func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, cfg vm.Config) (*types.Receipt, vm.Logs, *big.Int, error) {
|
func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, cfg vm.Config) (*types.Receipt, vm.Logs, *big.Int, error) {
|
||||||
_, gas, err := ApplyMessage(NewEnv(statedb, config, bc, tx, header, cfg), tx, gp)
|
backend := &EVMBackend{
|
||||||
|
GetHashFn: GetHashFn(header.ParentHash, bc),
|
||||||
|
State: statedb,
|
||||||
|
}
|
||||||
|
context := ToEVMContext(config, tx, header)
|
||||||
|
|
||||||
|
env := vm.NewEnvironment(context, backend, config, cfg)
|
||||||
|
|
||||||
|
_, gas, err := ApplyMessage(env, tx, gp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -106,13 +114,12 @@ func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, gp *GasPool, s
|
||||||
receipt.ContractAddress = crypto.CreateAddress(from, tx.Nonce())
|
receipt.ContractAddress = crypto.CreateAddress(from, tx.Nonce())
|
||||||
}
|
}
|
||||||
|
|
||||||
logs := statedb.GetLogs(tx.Hash())
|
receipt.Logs = statedb.GetLogs(tx.Hash())
|
||||||
receipt.Logs = logs
|
|
||||||
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
|
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
|
||||||
|
|
||||||
glog.V(logger.Debug).Infoln(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
|
// AccumulateRewards credits the coinbase of the given block with the
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ type StateTransition struct {
|
||||||
data []byte
|
data []byte
|
||||||
state vm.Database
|
state vm.Database
|
||||||
|
|
||||||
env vm.Environment
|
env *vm.Environment
|
||||||
}
|
}
|
||||||
|
|
||||||
// Message represents a message sent to a contract.
|
// 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.
|
// 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{
|
return &StateTransition{
|
||||||
gp: gp,
|
gp: gp,
|
||||||
env: env,
|
env: env,
|
||||||
|
|
@ -127,7 +127,7 @@ func NewStateTransition(env vm.Environment, msg Message, gp *GasPool) *StateTran
|
||||||
// the gas used (which includes gas refunds) and an error if it failed. An error always
|
// 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
|
// indicates a core error meaning that the message would always fail for that particular
|
||||||
// state and would never be accepted within a block.
|
// 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)
|
st := NewStateTransition(env, msg, gp)
|
||||||
|
|
||||||
ret, _, gasUsed, err := st.TransitionDb()
|
ret, _, gasUsed, err := st.TransitionDb()
|
||||||
|
|
@ -139,7 +139,7 @@ func (self *StateTransition) from() (vm.Account, error) {
|
||||||
f common.Address
|
f common.Address
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
if self.env.ChainConfig().IsHomestead(self.env.BlockNumber()) {
|
if self.env.ChainConfig().IsHomestead(self.env.BlockNumber) {
|
||||||
f, err = self.msg.From()
|
f, err = self.msg.From()
|
||||||
} else {
|
} else {
|
||||||
f, err = self.msg.FromFrontier()
|
f, err = self.msg.FromFrontier()
|
||||||
|
|
@ -234,7 +234,7 @@ func (self *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *b
|
||||||
msg := self.msg
|
msg := self.msg
|
||||||
sender, _ := self.from() // err checked in preCheck
|
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)
|
contractCreation := MessageCreatesContract(msg)
|
||||||
// Pay intrinsic gas
|
// Pay intrinsic gas
|
||||||
if err = self.useGas(IntrinsicGas(self.data, contractCreation, homestead)); err != nil {
|
if err = self.useGas(IntrinsicGas(self.data, contractCreation, homestead)); err != nil {
|
||||||
|
|
@ -274,7 +274,7 @@ func (self *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *b
|
||||||
requiredGas = new(big.Int).Set(self.gasUsed())
|
requiredGas = new(big.Int).Set(self.gasUsed())
|
||||||
|
|
||||||
self.refundGas()
|
self.refundGas()
|
||||||
self.state.AddBalance(self.env.Coinbase(), new(big.Int).Mul(self.gasUsed(), self.gasPrice))
|
self.env.Db().AddBalance(self.env.Coinbase, new(big.Int).Mul(self.gasUsed(), self.gasPrice))
|
||||||
|
|
||||||
return ret, requiredGas, self.gasUsed(), err
|
return ret, requiredGas, self.gasUsed(), err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,126 +0,0 @@
|
||||||
// Copyright 2014 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package vm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"math/big"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/params"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Environment is an EVM requirement and helper which allows access to outside
|
|
||||||
// information such as states.
|
|
||||||
type Environment interface {
|
|
||||||
// The current ruleset
|
|
||||||
ChainConfig() *params.ChainConfig
|
|
||||||
// The state database
|
|
||||||
Db() Database
|
|
||||||
// Creates a restorable snapshot
|
|
||||||
SnapshotDatabase() int
|
|
||||||
// Set database to previous snapshot
|
|
||||||
RevertToSnapshot(int)
|
|
||||||
// Address of the original invoker (first occurrence of the VM invoker)
|
|
||||||
Origin() common.Address
|
|
||||||
// The block number this VM is invoked on
|
|
||||||
BlockNumber() *big.Int
|
|
||||||
// The n'th hash ago from this block number
|
|
||||||
GetHash(uint64) common.Hash
|
|
||||||
// The handler's address
|
|
||||||
Coinbase() common.Address
|
|
||||||
// The current time (block time)
|
|
||||||
Time() *big.Int
|
|
||||||
// Difficulty set on the current block
|
|
||||||
Difficulty() *big.Int
|
|
||||||
// The gas limit of the block
|
|
||||||
GasLimit() *big.Int
|
|
||||||
// The gas price of the current call
|
|
||||||
GasPrice() *big.Int
|
|
||||||
// Determines whether it's possible to transact
|
|
||||||
CanTransfer(from common.Address, balance *big.Int) bool
|
|
||||||
// Transfers amount from one account to the other
|
|
||||||
Transfer(from, to Account, amount *big.Int)
|
|
||||||
// Adds a LOG to the state
|
|
||||||
AddLog(*Log)
|
|
||||||
// Type of the VM
|
|
||||||
Vm() Vm
|
|
||||||
// Get the curret calling depth
|
|
||||||
Depth() int
|
|
||||||
// Set the current calling depth
|
|
||||||
SetDepth(i int)
|
|
||||||
// Call another contract
|
|
||||||
Call(me ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error)
|
|
||||||
// Take another's contract code and execute within our own context
|
|
||||||
CallCode(me ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error)
|
|
||||||
// Same as CallCode except sender and value is propagated from parent to child scope
|
|
||||||
DelegateCall(me ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error)
|
|
||||||
// Create a new contract
|
|
||||||
Create(me ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Vm is the basic interface for an implementation of the EVM.
|
|
||||||
type Vm interface {
|
|
||||||
// Run should execute the given contract with the input given in in
|
|
||||||
// and return the contract execution return bytes or an error if it
|
|
||||||
// failed.
|
|
||||||
Run(c *Contract, in []byte) ([]byte, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Database is a EVM database for full state querying.
|
|
||||||
type Database interface {
|
|
||||||
GetAccount(common.Address) Account
|
|
||||||
CreateAccount(common.Address) Account
|
|
||||||
|
|
||||||
AddBalance(common.Address, *big.Int)
|
|
||||||
GetBalance(common.Address) *big.Int
|
|
||||||
|
|
||||||
GetNonce(common.Address) uint64
|
|
||||||
SetNonce(common.Address, uint64)
|
|
||||||
|
|
||||||
GetCodeHash(common.Address) common.Hash
|
|
||||||
GetCodeSize(common.Address) int
|
|
||||||
GetCode(common.Address) []byte
|
|
||||||
SetCode(common.Address, []byte)
|
|
||||||
|
|
||||||
AddRefund(*big.Int)
|
|
||||||
GetRefund() *big.Int
|
|
||||||
|
|
||||||
GetState(common.Address, common.Hash) common.Hash
|
|
||||||
SetState(common.Address, common.Hash, common.Hash)
|
|
||||||
|
|
||||||
Suicide(common.Address) bool
|
|
||||||
HasSuicided(common.Address) bool
|
|
||||||
|
|
||||||
// Exist reports whether the given account exists in state.
|
|
||||||
// Notably this should also return true for suicided accounts.
|
|
||||||
Exist(common.Address) bool
|
|
||||||
Empty(common.Address) bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// Account represents a contract or basic ethereum account.
|
|
||||||
type Account interface {
|
|
||||||
SubBalance(amount *big.Int)
|
|
||||||
AddBalance(amount *big.Int)
|
|
||||||
SetBalance(*big.Int)
|
|
||||||
SetNonce(uint64)
|
|
||||||
Balance() *big.Int
|
|
||||||
Address() common.Address
|
|
||||||
ReturnGas(uint64)
|
|
||||||
SetCode(common.Hash, []byte)
|
|
||||||
ForEachStorage(cb func(key, value common.Hash) bool)
|
|
||||||
Value() *big.Int
|
|
||||||
}
|
|
||||||
|
|
@ -27,14 +27,14 @@ import (
|
||||||
|
|
||||||
type programInstruction interface {
|
type programInstruction interface {
|
||||||
// executes the program instruction and allows the instruction to modify the state of the program
|
// executes the program instruction and allows the instruction to modify the state of the program
|
||||||
do(vm *EVM, program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error)
|
do(vm *EVM, program *Program, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error)
|
||||||
// returns whether the program instruction halts the execution of the JIT
|
// returns whether the program instruction halts the execution of the JIT
|
||||||
halts() bool
|
halts() bool
|
||||||
// Returns the current op code (debugging purposes)
|
// Returns the current op code (debugging purposes)
|
||||||
Op() OpCode
|
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 {
|
type instruction struct {
|
||||||
op OpCode
|
op OpCode
|
||||||
|
|
@ -58,12 +58,13 @@ func jump(mapping map[uint64]uint64, destinations map[uint64]struct{}, contract
|
||||||
return mapping[to.Uint64()], nil
|
return mapping[to.Uint64()], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (instr instruction) do(vm *EVM, program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
func (instr instruction) do(vm *EVM, program *Program, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||||
// calculate the new memory size and gas price for the current executing opcode
|
// calculate the new memory size and gas price for the current executing opcode
|
||||||
newMemSize, cost, err := calculateGasAndSize(vm.gasTable, env, contract, instr, env.Db(), memory, stack)
|
newMemSize, cost, err := calculateGasAndSize(vm.gasTable, env, contract, instr, env.Db(), memory, stack)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
//fmt.Printf("%04d: %-8v %v\n", *pc, instr.op, cost)
|
||||||
|
|
||||||
// Use the calculated gas. When insufficient gas is present, use all gas and return an
|
// Use the calculated gas. When insufficient gas is present, use all gas and return an
|
||||||
// Out Of Gas error
|
// Out Of Gas error
|
||||||
|
|
@ -114,26 +115,26 @@ func (instr instruction) Op() OpCode {
|
||||||
return instr.op
|
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)
|
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()
|
x, y := stack.pop(), stack.pop()
|
||||||
stack.push(U256(x.Add(x, y)))
|
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()
|
x, y := stack.pop(), stack.pop()
|
||||||
stack.push(U256(x.Sub(x, y)))
|
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()
|
x, y := stack.pop(), stack.pop()
|
||||||
stack.push(U256(x.Mul(x, y)))
|
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()
|
x, y := stack.pop(), stack.pop()
|
||||||
if y.Cmp(common.Big0) != 0 {
|
if y.Cmp(common.Big0) != 0 {
|
||||||
stack.push(U256(x.Div(x, y)))
|
stack.push(U256(x.Div(x, y)))
|
||||||
|
|
@ -142,7 +143,7 @@ func opDiv(instr instruction, pc *uint64, env Environment, contract *Contract, m
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func opSdiv(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
func opSdiv(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||||
x, y := S256(stack.pop()), S256(stack.pop())
|
x, y := S256(stack.pop()), S256(stack.pop())
|
||||||
if y.Cmp(common.Big0) == 0 {
|
if y.Cmp(common.Big0) == 0 {
|
||||||
stack.push(new(big.Int))
|
stack.push(new(big.Int))
|
||||||
|
|
@ -162,7 +163,7 @@ func opSdiv(instr instruction, pc *uint64, env Environment, contract *Contract,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func opMod(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
func opMod(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||||
x, y := stack.pop(), stack.pop()
|
x, y := stack.pop(), stack.pop()
|
||||||
if y.Cmp(common.Big0) == 0 {
|
if y.Cmp(common.Big0) == 0 {
|
||||||
stack.push(new(big.Int))
|
stack.push(new(big.Int))
|
||||||
|
|
@ -171,7 +172,7 @@ func opMod(instr instruction, pc *uint64, env Environment, contract *Contract, m
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func opSmod(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
func opSmod(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||||
x, y := S256(stack.pop()), S256(stack.pop())
|
x, y := S256(stack.pop()), S256(stack.pop())
|
||||||
|
|
||||||
if y.Cmp(common.Big0) == 0 {
|
if y.Cmp(common.Big0) == 0 {
|
||||||
|
|
@ -191,12 +192,12 @@ func opSmod(instr instruction, pc *uint64, env Environment, contract *Contract,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func opExp(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
func opExp(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||||
x, y := stack.pop(), stack.pop()
|
x, y := stack.pop(), stack.pop()
|
||||||
stack.push(U256(x.Exp(x, y, Pow256)))
|
stack.push(U256(x.Exp(x, y, Pow256)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func opSignExtend(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
func opSignExtend(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||||
back := stack.pop()
|
back := stack.pop()
|
||||||
if back.Cmp(big.NewInt(31)) < 0 {
|
if back.Cmp(big.NewInt(31)) < 0 {
|
||||||
bit := uint(back.Uint64()*8 + 7)
|
bit := uint(back.Uint64()*8 + 7)
|
||||||
|
|
@ -213,12 +214,12 @@ func opSignExtend(instr instruction, pc *uint64, env Environment, contract *Cont
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func opNot(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
func opNot(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||||
x := stack.pop()
|
x := stack.pop()
|
||||||
stack.push(U256(x.Not(x)))
|
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()
|
x, y := stack.pop(), stack.pop()
|
||||||
if x.Cmp(y) < 0 {
|
if x.Cmp(y) < 0 {
|
||||||
stack.push(big.NewInt(1))
|
stack.push(big.NewInt(1))
|
||||||
|
|
@ -227,7 +228,7 @@ func opLt(instr instruction, pc *uint64, env Environment, contract *Contract, me
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func opGt(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
func opGt(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||||
x, y := stack.pop(), stack.pop()
|
x, y := stack.pop(), stack.pop()
|
||||||
if x.Cmp(y) > 0 {
|
if x.Cmp(y) > 0 {
|
||||||
stack.push(big.NewInt(1))
|
stack.push(big.NewInt(1))
|
||||||
|
|
@ -236,7 +237,7 @@ func opGt(instr instruction, pc *uint64, env Environment, contract *Contract, me
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func opSlt(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
func opSlt(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||||
x, y := S256(stack.pop()), S256(stack.pop())
|
x, y := S256(stack.pop()), S256(stack.pop())
|
||||||
if x.Cmp(S256(y)) < 0 {
|
if x.Cmp(S256(y)) < 0 {
|
||||||
stack.push(big.NewInt(1))
|
stack.push(big.NewInt(1))
|
||||||
|
|
@ -245,7 +246,7 @@ func opSlt(instr instruction, pc *uint64, env Environment, contract *Contract, m
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func opSgt(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
func opSgt(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||||
x, y := S256(stack.pop()), S256(stack.pop())
|
x, y := S256(stack.pop()), S256(stack.pop())
|
||||||
if x.Cmp(y) > 0 {
|
if x.Cmp(y) > 0 {
|
||||||
stack.push(big.NewInt(1))
|
stack.push(big.NewInt(1))
|
||||||
|
|
@ -254,7 +255,7 @@ func opSgt(instr instruction, pc *uint64, env Environment, contract *Contract, m
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func opEq(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
func opEq(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||||
x, y := stack.pop(), stack.pop()
|
x, y := stack.pop(), stack.pop()
|
||||||
if x.Cmp(y) == 0 {
|
if x.Cmp(y) == 0 {
|
||||||
stack.push(big.NewInt(1))
|
stack.push(big.NewInt(1))
|
||||||
|
|
@ -263,7 +264,7 @@ func opEq(instr instruction, pc *uint64, env Environment, contract *Contract, me
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func opIszero(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
func opIszero(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||||
x := stack.pop()
|
x := stack.pop()
|
||||||
if x.Cmp(common.Big0) > 0 {
|
if x.Cmp(common.Big0) > 0 {
|
||||||
stack.push(new(big.Int))
|
stack.push(new(big.Int))
|
||||||
|
|
@ -272,19 +273,19 @@ func opIszero(instr instruction, pc *uint64, env Environment, contract *Contract
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func opAnd(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
func opAnd(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||||
x, y := stack.pop(), stack.pop()
|
x, y := stack.pop(), stack.pop()
|
||||||
stack.push(x.And(x, y))
|
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()
|
x, y := stack.pop(), stack.pop()
|
||||||
stack.push(x.Or(x, y))
|
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()
|
x, y := stack.pop(), stack.pop()
|
||||||
stack.push(x.Xor(x, y))
|
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()
|
th, val := stack.pop(), stack.pop()
|
||||||
if th.Cmp(big.NewInt(32)) < 0 {
|
if th.Cmp(big.NewInt(32)) < 0 {
|
||||||
byte := big.NewInt(int64(common.LeftPadBytes(val.Bytes(), 32)[th.Int64()]))
|
byte := big.NewInt(int64(common.LeftPadBytes(val.Bytes(), 32)[th.Int64()]))
|
||||||
|
|
@ -293,7 +294,7 @@ func opByte(instr instruction, pc *uint64, env Environment, contract *Contract,
|
||||||
stack.push(new(big.Int))
|
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()
|
x, y, z := stack.pop(), stack.pop(), stack.pop()
|
||||||
if z.Cmp(Zero) > 0 {
|
if z.Cmp(Zero) > 0 {
|
||||||
add := x.Add(x, y)
|
add := x.Add(x, y)
|
||||||
|
|
@ -303,7 +304,7 @@ func opAddmod(instr instruction, pc *uint64, env Environment, contract *Contract
|
||||||
stack.push(new(big.Int))
|
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()
|
x, y, z := stack.pop(), stack.pop(), stack.pop()
|
||||||
if z.Cmp(Zero) > 0 {
|
if z.Cmp(Zero) > 0 {
|
||||||
mul := x.Mul(x, y)
|
mul := x.Mul(x, y)
|
||||||
|
|
@ -314,45 +315,45 @@ func opMulmod(instr instruction, pc *uint64, env Environment, contract *Contract
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func opSha3(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
func opSha3(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||||
offset, size := stack.pop(), stack.pop()
|
offset, size := stack.pop(), stack.pop()
|
||||||
hash := crypto.Keccak256(memory.GetPtr(offset.Int64(), size.Int64()))
|
hash := crypto.Keccak256(memory.GetPtr(offset.Int64(), size.Int64()))
|
||||||
|
|
||||||
stack.push(common.BytesToBig(hash))
|
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()))
|
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())
|
addr := common.BigToAddress(stack.pop())
|
||||||
balance := env.Db().GetBalance(addr)
|
balance := env.Db().GetBalance(addr)
|
||||||
|
|
||||||
stack.push(new(big.Int).Set(balance))
|
stack.push(new(big.Int).Set(balance))
|
||||||
}
|
}
|
||||||
|
|
||||||
func opOrigin(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
func opOrigin(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||||
stack.push(env.Origin().Big())
|
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())
|
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))
|
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)))
|
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))))
|
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 (
|
var (
|
||||||
mOff = stack.pop()
|
mOff = stack.pop()
|
||||||
cOff = stack.pop()
|
cOff = stack.pop()
|
||||||
|
|
@ -361,18 +362,18 @@ func opCalldataCopy(instr instruction, pc *uint64, env Environment, contract *Co
|
||||||
memory.Set(mOff.Uint64(), l.Uint64(), getData(contract.Input, cOff, l))
|
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())
|
addr := common.BigToAddress(stack.pop())
|
||||||
l := big.NewInt(int64(env.Db().GetCodeSize(addr)))
|
l := big.NewInt(int64(env.Db().GetCodeSize(addr)))
|
||||||
stack.push(l)
|
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)))
|
l := big.NewInt(int64(len(contract.Code)))
|
||||||
stack.push(l)
|
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 (
|
var (
|
||||||
mOff = stack.pop()
|
mOff = stack.pop()
|
||||||
cOff = stack.pop()
|
cOff = stack.pop()
|
||||||
|
|
@ -383,7 +384,7 @@ func opCodeCopy(instr instruction, pc *uint64, env Environment, contract *Contra
|
||||||
memory.Set(mOff.Uint64(), l.Uint64(), codeCopy)
|
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 (
|
var (
|
||||||
addr = common.BigToAddress(stack.pop())
|
addr = common.BigToAddress(stack.pop())
|
||||||
mOff = stack.pop()
|
mOff = stack.pop()
|
||||||
|
|
@ -395,58 +396,58 @@ func opExtCodeCopy(instr instruction, pc *uint64, env Environment, contract *Con
|
||||||
memory.Set(mOff.Uint64(), l.Uint64(), codeCopy)
|
memory.Set(mOff.Uint64(), l.Uint64(), codeCopy)
|
||||||
}
|
}
|
||||||
|
|
||||||
func opGasprice(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
func opGasprice(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||||
stack.push(new(big.Int).Set(env.GasPrice()))
|
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()
|
num := stack.pop()
|
||||||
|
|
||||||
n := new(big.Int).Sub(env.BlockNumber(), common.Big257)
|
n := new(big.Int).Sub(env.BlockNumber, common.Big257)
|
||||||
if num.Cmp(n) > 0 && num.Cmp(env.BlockNumber()) < 0 {
|
if num.Cmp(n) > 0 && num.Cmp(env.BlockNumber) < 0 {
|
||||||
stack.push(env.GetHash(num.Uint64()).Big())
|
stack.push(env.GetHash(num.Uint64()).Big())
|
||||||
} else {
|
} else {
|
||||||
stack.push(new(big.Int))
|
stack.push(new(big.Int))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func opCoinbase(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
func opCoinbase(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||||
stack.push(env.Coinbase().Big())
|
stack.push(env.Coinbase.Big())
|
||||||
}
|
}
|
||||||
|
|
||||||
func opTimestamp(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
func opTimestamp(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||||
stack.push(U256(new(big.Int).Set(env.Time())))
|
stack.push(U256(new(big.Int).Set(env.Time)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func opNumber(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
func opNumber(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||||
stack.push(U256(new(big.Int).Set(env.BlockNumber())))
|
stack.push(U256(new(big.Int).Set(env.BlockNumber)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func opDifficulty(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
func opDifficulty(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||||
stack.push(U256(new(big.Int).Set(env.Difficulty())))
|
stack.push(U256(new(big.Int).Set(env.Difficulty)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func opGasLimit(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
func opGasLimit(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||||
stack.push(U256(new(big.Int).Set(env.GasLimit())))
|
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()
|
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))
|
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()))
|
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()))
|
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())
|
n := int(instr.data.Int64())
|
||||||
topics := make([]common.Hash, n)
|
topics := make([]common.Hash, n)
|
||||||
mStart, mSize := stack.pop(), stack.pop()
|
mStart, mSize := stack.pop(), stack.pop()
|
||||||
|
|
@ -455,59 +456,59 @@ func opLog(instr instruction, pc *uint64, env Environment, contract *Contract, m
|
||||||
}
|
}
|
||||||
|
|
||||||
d := memory.Get(mStart.Int64(), mSize.Int64())
|
d := memory.Get(mStart.Int64(), mSize.Int64())
|
||||||
log := NewLog(contract.Address(), topics, d, env.BlockNumber().Uint64())
|
log := NewLog(contract.Address(), topics, d, env.BlockNumber.Uint64())
|
||||||
env.AddLog(log)
|
env.Db().AddLog(log)
|
||||||
}
|
}
|
||||||
|
|
||||||
func opMload(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
func opMload(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||||
offset := stack.pop()
|
offset := stack.pop()
|
||||||
val := common.BigD(memory.GetPtr(offset.Int64(), 32))
|
val := common.BigD(memory.GetPtr(offset.Int64(), 32))
|
||||||
stack.push(val)
|
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
|
// pop value of the stack
|
||||||
mStart, val := stack.pop(), stack.pop()
|
mStart, val := stack.pop(), stack.pop()
|
||||||
memory.Set(mStart.Uint64(), 32, common.BigToBytes(val, 256))
|
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()
|
off, val := stack.pop().Int64(), stack.pop().Int64()
|
||||||
memory.store[off] = byte(val & 0xff)
|
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())
|
loc := common.BigToHash(stack.pop())
|
||||||
val := env.Db().GetState(contract.Address(), loc).Big()
|
val := env.Db().GetState(contract.Address(), loc).Big()
|
||||||
stack.push(val)
|
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())
|
loc := common.BigToHash(stack.pop())
|
||||||
val := stack.pop()
|
val := stack.pop()
|
||||||
env.Db().SetState(contract.Address(), loc, common.BigToHash(val))
|
env.Db().SetState(contract.Address(), loc, common.BigToHash(val))
|
||||||
}
|
}
|
||||||
|
|
||||||
func opJump(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
func opJump(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||||
}
|
}
|
||||||
func opJumpi(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
func opJumpi(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||||
}
|
}
|
||||||
func opJumpdest(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
func opJumpdest(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func opPc(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
func opPc(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||||
stack.push(new(big.Int).Set(instr.data))
|
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())))
|
stack.push(big.NewInt(int64(memory.Len())))
|
||||||
}
|
}
|
||||||
|
|
||||||
func opGas(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
func opGas(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||||
stack.push(new(big.Int).SetUint64(contract.gas64))
|
stack.push(new(big.Int).SetUint64(contract.gas64))
|
||||||
}
|
}
|
||||||
|
|
||||||
func opCreate(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
func opCreate(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||||
var (
|
var (
|
||||||
value = stack.pop()
|
value = stack.pop()
|
||||||
offset, size = stack.pop(), stack.pop()
|
offset, size = stack.pop(), stack.pop()
|
||||||
|
|
@ -515,7 +516,7 @@ func opCreate(instr instruction, pc *uint64, env Environment, contract *Contract
|
||||||
//gas = new(big.Int).SetUint64(contract.gas64)
|
//gas = new(big.Int).SetUint64(contract.gas64)
|
||||||
gas = contract.gas64
|
gas = contract.gas64
|
||||||
)
|
)
|
||||||
if env.ChainConfig().IsEIP150(env.BlockNumber()) {
|
if env.ChainConfig().IsEIP150(env.BlockNumber) {
|
||||||
gas = gas - gas/64
|
gas = gas - gas/64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -525,7 +526,7 @@ func opCreate(instr instruction, pc *uint64, env Environment, contract *Contract
|
||||||
// homestead we must check for CodeStoreOutOfGasError (homestead only
|
// homestead we must check for CodeStoreOutOfGasError (homestead only
|
||||||
// rule) and treat as an error, if the ruleset is frontier we must
|
// rule) and treat as an error, if the ruleset is frontier we must
|
||||||
// ignore this error and pretend the operation was successful.
|
// 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))
|
stack.push(new(big.Int))
|
||||||
} else if suberr != nil && suberr != CodeStoreOutOfGasError {
|
} else if suberr != nil && suberr != CodeStoreOutOfGasError {
|
||||||
stack.push(new(big.Int))
|
stack.push(new(big.Int))
|
||||||
|
|
@ -534,7 +535,7 @@ func opCreate(instr instruction, pc *uint64, env Environment, contract *Contract
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func opCall(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
func opCall(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||||
gas := stack.pop()
|
gas := stack.pop()
|
||||||
// pop gas and value of the stack.
|
// pop gas and value of the stack.
|
||||||
addr, value := stack.pop(), stack.pop()
|
addr, value := stack.pop(), stack.pop()
|
||||||
|
|
@ -565,7 +566,7 @@ func opCall(instr instruction, pc *uint64, env Environment, contract *Contract,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func opCallCode(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
func opCallCode(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||||
gas := stack.pop()
|
gas := stack.pop()
|
||||||
// pop gas and value of the stack.
|
// pop gas and value of the stack.
|
||||||
addr, value := stack.pop(), stack.pop()
|
addr, value := stack.pop(), stack.pop()
|
||||||
|
|
@ -596,7 +597,7 @@ func opCallCode(instr instruction, pc *uint64, env Environment, contract *Contra
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func opDelegateCall(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
func opDelegateCall(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||||
gas, to, inOffset, inSize, outOffset, outSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
|
gas, to, inOffset, inSize, outOffset, outSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
|
||||||
|
|
||||||
toAddr := common.BigToAddress(to)
|
toAddr := common.BigToAddress(to)
|
||||||
|
|
@ -610,12 +611,12 @@ func opDelegateCall(instr instruction, pc *uint64, env Environment, contract *Co
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func opReturn(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
func opReturn(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||||
}
|
}
|
||||||
func opStop(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
func opStop(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func opSuicide(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
func opSuicide(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||||
balance := env.Db().GetBalance(contract.Address())
|
balance := env.Db().GetBalance(contract.Address())
|
||||||
env.Db().AddBalance(common.BigToAddress(stack.pop()), balance)
|
env.Db().AddBalance(common.BigToAddress(stack.pop()), balance)
|
||||||
|
|
||||||
|
|
@ -626,7 +627,7 @@ func opSuicide(instr instruction, pc *uint64, env Environment, contract *Contrac
|
||||||
|
|
||||||
// make log instruction function
|
// make log instruction function
|
||||||
func makeLog(size int) instrFn {
|
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)
|
topics := make([]common.Hash, size)
|
||||||
mStart, mSize := stack.pop(), stack.pop()
|
mStart, mSize := stack.pop(), stack.pop()
|
||||||
for i := 0; i < size; i++ {
|
for i := 0; i < size; i++ {
|
||||||
|
|
@ -634,14 +635,14 @@ func makeLog(size int) instrFn {
|
||||||
}
|
}
|
||||||
|
|
||||||
d := memory.Get(mStart.Int64(), mSize.Int64())
|
d := memory.Get(mStart.Int64(), mSize.Int64())
|
||||||
log := NewLog(contract.Address(), topics, d, env.BlockNumber().Uint64())
|
log := NewLog(contract.Address(), topics, d, env.BlockNumber.Uint64())
|
||||||
env.AddLog(log)
|
env.Db().AddLog(log)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// make push instruction function
|
// make push instruction function
|
||||||
func makePush(size uint64, bsize *big.Int) instrFn {
|
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)
|
byts := getData(contract.Code, new(big.Int).SetUint64(*pc+1), bsize)
|
||||||
stack.push(common.Bytes2Big(byts))
|
stack.push(common.Bytes2Big(byts))
|
||||||
*pc += size
|
*pc += size
|
||||||
|
|
@ -650,7 +651,7 @@ func makePush(size uint64, bsize *big.Int) instrFn {
|
||||||
|
|
||||||
// make push instruction function
|
// make push instruction function
|
||||||
func makeDup(size int64) instrFn {
|
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))
|
stack.dup(int(size))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -659,7 +660,7 @@ func makeDup(size int64) instrFn {
|
||||||
func makeSwap(size int64) instrFn {
|
func makeSwap(size int64) instrFn {
|
||||||
// switch n + 1 otherwise n would be swapped with n
|
// switch n + 1 otherwise n would be swapped with n
|
||||||
size += 1
|
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))
|
stack.swap(int(size))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
203
core/vm/interface.go
Normal file
203
core/vm/interface.go
Normal file
|
|
@ -0,0 +1,203 @@
|
||||||
|
// Copyright 2014 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package vm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Vm is the basic interface for an implementation of the EVM.
|
||||||
|
type Vm interface {
|
||||||
|
// Run should execute the given contract with the input given in in
|
||||||
|
// and return the contract execution return bytes or an error if it
|
||||||
|
// failed.
|
||||||
|
Run(c *Contract, in []byte) ([]byte, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RuleSet is an interface that defines the current rule set during the
|
||||||
|
// execution of the EVM instructions (e.g. whether it's homestead)
|
||||||
|
type RuleSet interface {
|
||||||
|
IsHomestead(*big.Int) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Database is a EVM database for full state querying.
|
||||||
|
type Database interface {
|
||||||
|
GetAccount(common.Address) Account
|
||||||
|
CreateAccount(common.Address) Account
|
||||||
|
|
||||||
|
SubBalance(common.Address, *big.Int)
|
||||||
|
AddBalance(common.Address, *big.Int)
|
||||||
|
GetBalance(common.Address) *big.Int
|
||||||
|
|
||||||
|
GetNonce(common.Address) uint64
|
||||||
|
SetNonce(common.Address, uint64)
|
||||||
|
|
||||||
|
GetCodeHash(common.Address) common.Hash
|
||||||
|
GetCode(common.Address) []byte
|
||||||
|
SetCode(common.Address, []byte)
|
||||||
|
GetCodeSize(common.Address) int
|
||||||
|
|
||||||
|
AddRefund(*big.Int)
|
||||||
|
GetRefund() *big.Int
|
||||||
|
|
||||||
|
GetState(common.Address, common.Hash) common.Hash
|
||||||
|
SetState(common.Address, common.Hash, common.Hash)
|
||||||
|
|
||||||
|
Suicide(common.Address) bool
|
||||||
|
HasSuicided(common.Address) bool
|
||||||
|
|
||||||
|
// Exist reports whether the given account exists in state.
|
||||||
|
// Notably this should also return true for suicided accounts.
|
||||||
|
Exist(common.Address) bool
|
||||||
|
Empty(common.Address) bool
|
||||||
|
|
||||||
|
AddLog(*Log)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Account represents a contract or basic ethereum account.
|
||||||
|
type Account interface {
|
||||||
|
SubBalance(amount *big.Int)
|
||||||
|
AddBalance(amount *big.Int)
|
||||||
|
SetBalance(*big.Int)
|
||||||
|
SetNonce(uint64)
|
||||||
|
Balance() *big.Int
|
||||||
|
Address() common.Address
|
||||||
|
ReturnGas(uint64)
|
||||||
|
SetCode(common.Hash, []byte)
|
||||||
|
ForEachStorage(cb func(key, value common.Hash) bool)
|
||||||
|
Value() *big.Int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Context provides the EVM with auxilary information. Once provided it shouldn't be modified.
|
||||||
|
type Context struct {
|
||||||
|
CallContext
|
||||||
|
|
||||||
|
// Message information
|
||||||
|
Origin common.Address // Provides information for ORIGIN
|
||||||
|
Coinbase common.Address // Provides information for COINBASE
|
||||||
|
GasPrice *big.Int // Provides information for GASPRICE
|
||||||
|
|
||||||
|
// Block information
|
||||||
|
GasLimit *big.Int // Provides information for GASLIMIT
|
||||||
|
BlockNumber *big.Int // Provides information for NUMBER
|
||||||
|
Time *big.Int // Provides information for TIME
|
||||||
|
Difficulty *big.Int // Provides information for DIFFICULTY
|
||||||
|
}
|
||||||
|
|
||||||
|
// CallContext provides a basic interface for the EVM calling conventions. The EVM Environment
|
||||||
|
// depends on this context being implemented for doing subcalls and initialising new EVM contracts.
|
||||||
|
type CallContext interface {
|
||||||
|
// Call another contract
|
||||||
|
Call(env *Environment, me ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error)
|
||||||
|
// Take another's contract code and execute within our own context
|
||||||
|
CallCode(env *Environment, me ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error)
|
||||||
|
// Same as CallCode except sender and value is propagated from parent to child scope
|
||||||
|
DelegateCall(env *Environment, me ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error)
|
||||||
|
// Create a new contract
|
||||||
|
Create(env *Environment, me ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Backend is the basic interface for keeping track of state and taking care of
|
||||||
|
// returning ancestory data.
|
||||||
|
type Backend interface {
|
||||||
|
// GetHash returns the hash corresponding to n
|
||||||
|
GetHash(n uint64) common.Hash
|
||||||
|
// Creates a restorable snapshot
|
||||||
|
SnapshotDatabase() int
|
||||||
|
// Set database to previous snapshot
|
||||||
|
RevertToSnapshot(int)
|
||||||
|
// Get returns the database
|
||||||
|
Get() Database
|
||||||
|
}
|
||||||
|
|
||||||
|
type Environment struct {
|
||||||
|
Context
|
||||||
|
|
||||||
|
Backend Backend
|
||||||
|
|
||||||
|
chainConfig *params.ChainConfig
|
||||||
|
vmConfig Config
|
||||||
|
|
||||||
|
evm Vm
|
||||||
|
|
||||||
|
Depth int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewEnvironment(context Context, backend Backend, chainConfig *params.ChainConfig, vmCfg Config) *Environment {
|
||||||
|
env := &Environment{
|
||||||
|
Context: context,
|
||||||
|
Backend: backend,
|
||||||
|
vmConfig: vmCfg,
|
||||||
|
chainConfig: chainConfig,
|
||||||
|
}
|
||||||
|
env.evm = New(env, vmCfg)
|
||||||
|
return env
|
||||||
|
}
|
||||||
|
|
||||||
|
func (env *Environment) Call(caller ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) {
|
||||||
|
if env.vmConfig.Test && env.Depth > 0 {
|
||||||
|
caller.ReturnGas(gas.Uint64())
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return env.CallContext.Call(env, caller, addr, data, gas, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Take another's contract code and execute within our own context
|
||||||
|
func (env *Environment) CallCode(caller ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) {
|
||||||
|
if env.vmConfig.Test && env.Depth > 0 {
|
||||||
|
caller.ReturnGas(gas.Uint64())
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return env.CallContext.CallCode(env, caller, addr, data, gas, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Same as CallCode except sender and value is propagated from parent to child scope
|
||||||
|
func (env *Environment) DelegateCall(caller ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error) {
|
||||||
|
if env.vmConfig.Test && env.Depth > 0 {
|
||||||
|
caller.ReturnGas(gas.Uint64())
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return env.CallContext.DelegateCall(env, caller, addr, data, gas)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new contract
|
||||||
|
func (env *Environment) Create(caller ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error) {
|
||||||
|
if env.vmConfig.Test && env.Depth > 0 {
|
||||||
|
caller.ReturnGas(gas.Uint64())
|
||||||
|
|
||||||
|
return nil, common.Address{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return env.CallContext.Create(env, caller, data, gas, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The current ruleset
|
||||||
|
func (env *Environment) ChainConfig() *params.ChainConfig { return env.chainConfig }
|
||||||
|
func (env *Environment) Vm() Vm { return env.evm }
|
||||||
|
func (env *Environment) Db() Database { return env.Backend.Get() }
|
||||||
|
func (env *Environment) GetHash(n uint64) common.Hash {
|
||||||
|
return env.Backend.GetHash(n)
|
||||||
|
}
|
||||||
|
|
@ -65,7 +65,7 @@ type StructLog struct {
|
||||||
// Note that reference types are actual VM data structures; make copies
|
// Note that reference types are actual VM data structures; make copies
|
||||||
// if you need to retain them beyond the current call.
|
// if you need to retain them beyond the current call.
|
||||||
type Tracer interface {
|
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.
|
// 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 logs a new structured log message and pushes it out to the environment
|
||||||
//
|
//
|
||||||
// captureState also tracks SSTORE ops to track dirty values.
|
// 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
|
// check if already accumulated the specified number of logs
|
||||||
if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) {
|
if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) {
|
||||||
return TraceLimitReachedError
|
return TraceLimitReachedError
|
||||||
|
|
@ -155,7 +155,7 @@ func (l *StructLogger) CaptureState(env Environment, pc uint64, op OpCode, gas,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// create a new snaptshot of the EVM.
|
// 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)
|
l.logs = append(l.logs, log)
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,13 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type ruleset struct{}
|
||||||
|
|
||||||
|
func (ruleset) IsHomestead(*big.Int) bool { return true }
|
||||||
|
|
||||||
type dummyContractRef struct {
|
type dummyContractRef struct {
|
||||||
calledForEach bool
|
calledForEach bool
|
||||||
}
|
}
|
||||||
|
|
@ -41,13 +46,13 @@ func (d *dummyContractRef) SetNonce(uint64) {}
|
||||||
func (d *dummyContractRef) Balance() *big.Int { return new(big.Int) }
|
func (d *dummyContractRef) Balance() *big.Int { return new(big.Int) }
|
||||||
|
|
||||||
type dummyEnv struct {
|
type dummyEnv struct {
|
||||||
*Env
|
*Environment
|
||||||
ref *dummyContractRef
|
ref *dummyContractRef
|
||||||
}
|
}
|
||||||
|
|
||||||
func newDummyEnv(ref *dummyContractRef) *dummyEnv {
|
func newDummyEnv(ref *dummyContractRef) *dummyEnv {
|
||||||
return &dummyEnv{
|
return &dummyEnv{
|
||||||
Env: NewEnv(&Config{EnableJit: false, ForceJit: false}),
|
Environment: NewEnvironment(Context{}, nil, params.TestChainConfig, Config{}),
|
||||||
ref: ref,
|
ref: ref,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -57,7 +62,7 @@ func (d dummyEnv) GetAccount(common.Address) Account {
|
||||||
|
|
||||||
func TestStoreCapture(t *testing.T) {
|
func TestStoreCapture(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
env = NewEnv(&Config{EnableJit: false, ForceJit: false})
|
env = NewEnvironment(Context{}, nil, params.TestChainConfig, Config{})
|
||||||
logger = NewStructLogger(nil)
|
logger = NewStructLogger(nil)
|
||||||
mem = NewMemory()
|
mem = NewMemory()
|
||||||
stack = newstack()
|
stack = newstack()
|
||||||
|
|
@ -90,13 +95,13 @@ func TestStorageCapture(t *testing.T) {
|
||||||
stack = newstack()
|
stack = newstack()
|
||||||
)
|
)
|
||||||
|
|
||||||
logger.CaptureState(env, 0, STOP, new(big.Int), new(big.Int), mem, stack, contract, 0, nil)
|
logger.CaptureState(env.Environment, 0, STOP, new(big.Int), new(big.Int), mem, stack, contract, 0, nil)
|
||||||
if ref.calledForEach {
|
if ref.calledForEach {
|
||||||
t.Error("didn't expect for each to be called")
|
t.Error("didn't expect for each to be called")
|
||||||
}
|
}
|
||||||
|
|
||||||
logger = NewStructLogger(&LogConfig{FullStorage: true})
|
logger = NewStructLogger(&LogConfig{FullStorage: true})
|
||||||
logger.CaptureState(env, 0, STOP, new(big.Int), new(big.Int), mem, stack, contract, 0, nil)
|
logger.CaptureState(env.Environment, 0, STOP, new(big.Int), new(big.Int), mem, stack, contract, 0, nil)
|
||||||
if !ref.calledForEach {
|
if !ref.calledForEach {
|
||||||
t.Error("expected for each to be called")
|
t.Error("expected for each to be called")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -288,11 +288,9 @@ func CompileProgram(program *Program) {
|
||||||
program.addInstr(op, pc, nil, nil)
|
program.addInstr(op, pc, nil, nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
optimiseProgram(program)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func RunProgram(program *Program, env Environment, contract *Contract, input []byte) ([]byte, error) {
|
func RunProgram(program *Program, env *Environment, contract *Contract, input []byte) ([]byte, error) {
|
||||||
return New(env, Config{}).Run(contract, input)
|
return New(env, Config{}).Run(contract, input)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -310,7 +308,7 @@ func validDest(dests map[uint64]struct{}, dest *big.Int) bool {
|
||||||
|
|
||||||
// calculateGasAndSize calculates the required given the opcode and stack items calculates the new memorysize for
|
// 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.
|
// the operation. This does not reduce gas or resizes the memory.
|
||||||
func calculateGasAndSize(gasTable params.GasTable, env Environment, contract *Contract, instr instruction, statedb Database, mem *Memory, stack *Stack) (uint64, uint64, error) {
|
func calculateGasAndSize(gasTable params.GasTable, env *Environment, contract *Contract, instr instruction, statedb Database, mem *Memory, stack *Stack) (uint64, uint64, error) {
|
||||||
var (
|
var (
|
||||||
newMemSize, memGas uint64
|
newMemSize, memGas uint64
|
||||||
sizeFault bool
|
sizeFault bool
|
||||||
|
|
@ -329,7 +327,7 @@ func calculateGasAndSize(gasTable params.GasTable, env Environment, contract *Co
|
||||||
gas += gasTable.Suicide
|
gas += gasTable.Suicide
|
||||||
var (
|
var (
|
||||||
address = common.BigToAddress(stack.data[len(stack.data)-1])
|
address = common.BigToAddress(stack.data[len(stack.data)-1])
|
||||||
eip158 = env.ChainConfig().IsEIP158(env.BlockNumber())
|
eip158 = env.ChainConfig().IsEIP158(env.BlockNumber)
|
||||||
)
|
)
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
|
|
@ -558,7 +556,7 @@ func calculateGasAndSize(gasTable params.GasTable, env Environment, contract *Co
|
||||||
if op == CALL {
|
if op == CALL {
|
||||||
var (
|
var (
|
||||||
address = common.BigToAddress(stack.data[len(stack.data)-2])
|
address = common.BigToAddress(stack.data[len(stack.data)-2])
|
||||||
eip158 = env.ChainConfig().IsEIP158(env.BlockNumber())
|
eip158 = env.ChainConfig().IsEIP158(env.BlockNumber)
|
||||||
)
|
)
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ import (
|
||||||
|
|
||||||
// optimeProgram optimises a JIT program creating segments out of program
|
// optimeProgram optimises a JIT program creating segments out of program
|
||||||
// instructions. Currently covered are multi-pushes and static jumps
|
// instructions. Currently covered are multi-pushes and static jumps
|
||||||
func optimiseProgram(program *Program) {
|
func OptimiseProgram(program *Program) {
|
||||||
var load []instruction
|
var load []instruction
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
|
||||||
|
|
@ -16,21 +16,14 @@
|
||||||
|
|
||||||
package vm
|
package vm
|
||||||
|
|
||||||
import (
|
import "testing"
|
||||||
"math/big"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
|
||||||
"github.com/ethereum/go-ethereum/params"
|
|
||||||
)
|
|
||||||
|
|
||||||
const maxRun = 1000
|
const maxRun = 1000
|
||||||
|
|
||||||
func TestSegmenting(t *testing.T) {
|
func TestSegmenting(t *testing.T) {
|
||||||
prog := NewProgram([]byte{byte(PUSH1), 0x1, byte(PUSH1), 0x1, 0x0})
|
prog := NewProgram([]byte{byte(PUSH1), 0x1, byte(PUSH1), 0x1, 0x0})
|
||||||
CompileProgram(prog)
|
CompileProgram(prog)
|
||||||
|
OptimiseProgram(prog)
|
||||||
|
|
||||||
if instr, ok := prog.instructions[0].(pushSeg); ok {
|
if instr, ok := prog.instructions[0].(pushSeg); ok {
|
||||||
if len(instr.data) != 2 {
|
if len(instr.data) != 2 {
|
||||||
|
|
@ -42,6 +35,7 @@ func TestSegmenting(t *testing.T) {
|
||||||
|
|
||||||
prog = NewProgram([]byte{byte(PUSH1), 0x1, byte(PUSH1), 0x1, byte(JUMP)})
|
prog = NewProgram([]byte{byte(PUSH1), 0x1, byte(PUSH1), 0x1, byte(JUMP)})
|
||||||
CompileProgram(prog)
|
CompileProgram(prog)
|
||||||
|
OptimiseProgram(prog)
|
||||||
|
|
||||||
if _, ok := prog.instructions[1].(jumpSeg); ok {
|
if _, ok := prog.instructions[1].(jumpSeg); ok {
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -50,6 +44,7 @@ func TestSegmenting(t *testing.T) {
|
||||||
|
|
||||||
prog = NewProgram([]byte{byte(PUSH1), 0x1, byte(PUSH1), 0x1, byte(PUSH1), 0x1, byte(JUMP)})
|
prog = NewProgram([]byte{byte(PUSH1), 0x1, byte(PUSH1), 0x1, byte(PUSH1), 0x1, byte(JUMP)})
|
||||||
CompileProgram(prog)
|
CompileProgram(prog)
|
||||||
|
OptimiseProgram(prog)
|
||||||
|
|
||||||
if instr, ok := prog.instructions[0].(pushSeg); ok {
|
if instr, ok := prog.instructions[0].(pushSeg); ok {
|
||||||
if len(instr.data) != 2 {
|
if len(instr.data) != 2 {
|
||||||
|
|
@ -73,8 +68,9 @@ func TestCompiling(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
func TestResetInput(t *testing.T) {
|
func TestResetInput(t *testing.T) {
|
||||||
var sender account
|
var sender Account
|
||||||
|
|
||||||
env := NewEnv(&Config{})
|
env := NewEnv(&Config{})
|
||||||
contract := NewContract(sender, sender, big.NewInt(100), big.NewInt(10000))
|
contract := NewContract(sender, sender, big.NewInt(100), big.NewInt(10000))
|
||||||
|
|
@ -86,6 +82,7 @@ func TestResetInput(t *testing.T) {
|
||||||
t.Errorf("expected input to be nil, got %x", contract.Input)
|
t.Errorf("expected input to be nil, got %x", contract.Input)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
func TestPcMappingToInstruction(t *testing.T) {
|
func TestPcMappingToInstruction(t *testing.T) {
|
||||||
program := NewProgram([]byte{byte(PUSH2), 0xbe, 0xef, byte(ADD)})
|
program := NewProgram([]byte{byte(PUSH2), 0xbe, 0xef, byte(ADD)})
|
||||||
|
|
@ -94,112 +91,3 @@ func TestPcMappingToInstruction(t *testing.T) {
|
||||||
t.Error("expected mapping PC 4 to me instr no. 2, got", program.mapping[4])
|
t.Error("expected mapping PC 4 to me instr no. 2, got", program.mapping[4])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var benchmarks = map[string]vmBench{
|
|
||||||
"pushes": vmBench{
|
|
||||||
false, false, false,
|
|
||||||
common.Hex2Bytes("600a600a01600a600a01600a600a01600a600a01600a600a01600a600a01600a600a01600a600a01600a600a01600a600a01"), nil,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkPushes(b *testing.B) {
|
|
||||||
runVmBench(benchmarks["pushes"], b)
|
|
||||||
}
|
|
||||||
|
|
||||||
type vmBench struct {
|
|
||||||
precompile bool // compile prior to executing
|
|
||||||
nojit bool // ignore jit (sets DisbaleJit = true
|
|
||||||
forcejit bool // forces the jit, precompile is ignored
|
|
||||||
|
|
||||||
code []byte
|
|
||||||
input []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
type account struct{}
|
|
||||||
|
|
||||||
func (account) SubBalance(amount *big.Int) {}
|
|
||||||
func (account) AddBalance(amount *big.Int) {}
|
|
||||||
func (account) SetAddress(common.Address) {}
|
|
||||||
func (account) Value() *big.Int { return nil }
|
|
||||||
func (account) SetBalance(*big.Int) {}
|
|
||||||
func (account) SetNonce(uint64) {}
|
|
||||||
func (account) Balance() *big.Int { return nil }
|
|
||||||
func (account) Address() common.Address { return common.Address{} }
|
|
||||||
func (account) ReturnGas(uint64) {}
|
|
||||||
func (account) SetCode(common.Hash, []byte) {}
|
|
||||||
func (account) ForEachStorage(cb func(key, value common.Hash) bool) {}
|
|
||||||
|
|
||||||
func runVmBench(test vmBench, b *testing.B) {
|
|
||||||
var sender account
|
|
||||||
|
|
||||||
if test.precompile && !test.forcejit {
|
|
||||||
NewProgram(test.code)
|
|
||||||
}
|
|
||||||
env := NewEnv(&Config{EnableJit: !test.nojit, ForceJit: test.forcejit})
|
|
||||||
|
|
||||||
b.ResetTimer()
|
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
context := NewContract(sender, sender, big.NewInt(100), big.NewInt(10000))
|
|
||||||
context.Code = test.code
|
|
||||||
context.CodeAddr = &common.Address{}
|
|
||||||
_, err := env.Vm().Run(context, test.input)
|
|
||||||
if err != nil {
|
|
||||||
b.Error(err)
|
|
||||||
b.FailNow()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Env struct {
|
|
||||||
gasLimit *big.Int
|
|
||||||
depth int
|
|
||||||
evm *EVM
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewEnv(config *Config) *Env {
|
|
||||||
env := &Env{gasLimit: big.NewInt(10000), depth: 0}
|
|
||||||
env.evm = New(env, *config)
|
|
||||||
return env
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *Env) ChainConfig() *params.ChainConfig {
|
|
||||||
return ¶ms.ChainConfig{new(big.Int), new(big.Int), true, new(big.Int), common.Hash{}, new(big.Int)}
|
|
||||||
}
|
|
||||||
func (self *Env) Vm() Vm { return self.evm }
|
|
||||||
func (self *Env) GasPrice() *big.Int { return new(big.Int) }
|
|
||||||
func (self *Env) Origin() common.Address { return common.Address{} }
|
|
||||||
func (self *Env) BlockNumber() *big.Int { return big.NewInt(0) }
|
|
||||||
|
|
||||||
//func (self *Env) PrevHash() []byte { return self.parent }
|
|
||||||
func (self *Env) Coinbase() common.Address { return common.Address{} }
|
|
||||||
func (self *Env) SnapshotDatabase() int { return 0 }
|
|
||||||
func (self *Env) RevertToSnapshot(int) {}
|
|
||||||
func (self *Env) Time() *big.Int { return big.NewInt(time.Now().Unix()) }
|
|
||||||
func (self *Env) Difficulty() *big.Int { return big.NewInt(0) }
|
|
||||||
func (self *Env) Db() Database { return nil }
|
|
||||||
func (self *Env) GasLimit() *big.Int { return self.gasLimit }
|
|
||||||
func (self *Env) VmType() Type { return StdVmTy }
|
|
||||||
func (self *Env) GetHash(n uint64) common.Hash {
|
|
||||||
return common.BytesToHash(crypto.Keccak256([]byte(big.NewInt(int64(n)).String())))
|
|
||||||
}
|
|
||||||
func (self *Env) AddLog(log *Log) {
|
|
||||||
}
|
|
||||||
func (self *Env) Depth() int { return self.depth }
|
|
||||||
func (self *Env) SetDepth(i int) { self.depth = i }
|
|
||||||
func (self *Env) CanTransfer(from common.Address, balance *big.Int) bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
func (self *Env) Transfer(from, to Account, amount *big.Int) {}
|
|
||||||
func (self *Env) Call(caller ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
func (self *Env) CallCode(caller ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
func (self *Env) Create(caller ContractRef, data []byte, gas, price *big.Int) ([]byte, common.Address, error) {
|
|
||||||
return nil, common.Address{}, nil
|
|
||||||
}
|
|
||||||
func (self *Env) DelegateCall(me ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,118 +0,0 @@
|
||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package runtime
|
|
||||||
|
|
||||||
import (
|
|
||||||
"math/big"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/core"
|
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
|
||||||
"github.com/ethereum/go-ethereum/params"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Env is a basic runtime environment required for running the EVM.
|
|
||||||
type Env struct {
|
|
||||||
chainConfig *params.ChainConfig
|
|
||||||
depth int
|
|
||||||
state *state.StateDB
|
|
||||||
|
|
||||||
origin common.Address
|
|
||||||
coinbase common.Address
|
|
||||||
|
|
||||||
number *big.Int
|
|
||||||
time *big.Int
|
|
||||||
difficulty *big.Int
|
|
||||||
gasLimit *big.Int
|
|
||||||
gasPrice *big.Int
|
|
||||||
|
|
||||||
getHashFn func(uint64) common.Hash
|
|
||||||
|
|
||||||
evm *vm.EVM
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewEnv returns a new vm.Environment
|
|
||||||
func NewEnv(cfg *Config, state *state.StateDB) vm.Environment {
|
|
||||||
env := &Env{
|
|
||||||
chainConfig: cfg.ChainConfig,
|
|
||||||
state: state,
|
|
||||||
origin: cfg.Origin,
|
|
||||||
coinbase: cfg.Coinbase,
|
|
||||||
number: cfg.BlockNumber,
|
|
||||||
time: cfg.Time,
|
|
||||||
difficulty: cfg.Difficulty,
|
|
||||||
gasLimit: cfg.GasLimit,
|
|
||||||
gasPrice: cfg.GasPrice,
|
|
||||||
}
|
|
||||||
env.evm = vm.New(env, vm.Config{
|
|
||||||
Debug: cfg.Debug,
|
|
||||||
EnableJit: !cfg.DisableJit,
|
|
||||||
ForceJit: !cfg.DisableJit,
|
|
||||||
})
|
|
||||||
|
|
||||||
return env
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *Env) ChainConfig() *params.ChainConfig { return self.chainConfig }
|
|
||||||
func (self *Env) RuleSet() vm.RuleSet { return self.ruleSet }
|
|
||||||
func (self *Env) GasPrice() *big.Int { return self.gasPrice }
|
|
||||||
func (self *Env) Vm() vm.Vm { return self.evm }
|
|
||||||
func (self *Env) Origin() common.Address { return self.origin }
|
|
||||||
func (self *Env) BlockNumber() *big.Int { return self.number }
|
|
||||||
func (self *Env) Coinbase() common.Address { return self.coinbase }
|
|
||||||
func (self *Env) Time() *big.Int { return self.time }
|
|
||||||
func (self *Env) Difficulty() *big.Int { return self.difficulty }
|
|
||||||
func (self *Env) Db() vm.Database { return self.state }
|
|
||||||
func (self *Env) GasLimit() *big.Int { return self.gasLimit }
|
|
||||||
func (self *Env) VmType() vm.Type { return vm.StdVmTy }
|
|
||||||
func (self *Env) GetHash(n uint64) common.Hash {
|
|
||||||
return self.getHashFn(n)
|
|
||||||
}
|
|
||||||
func (self *Env) AddLog(log *vm.Log) {
|
|
||||||
self.state.AddLog(log)
|
|
||||||
}
|
|
||||||
func (self *Env) Depth() int { return self.depth }
|
|
||||||
func (self *Env) SetDepth(i int) { self.depth = i }
|
|
||||||
func (self *Env) CanTransfer(from common.Address, balance *big.Int) bool {
|
|
||||||
return self.state.GetBalance(from).Cmp(balance) >= 0
|
|
||||||
}
|
|
||||||
func (self *Env) SnapshotDatabase() int {
|
|
||||||
return self.state.Snapshot()
|
|
||||||
}
|
|
||||||
func (self *Env) RevertToSnapshot(snapshot int) {
|
|
||||||
self.state.RevertToSnapshot(snapshot)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *Env) Transfer(from, to vm.Account, amount *big.Int) {
|
|
||||||
core.Transfer(from, to, amount)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *Env) Call(caller vm.ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) {
|
|
||||||
return core.Call(self, caller, addr, data, gas, value)
|
|
||||||
}
|
|
||||||
func (self *Env) CallCode(caller vm.ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) {
|
|
||||||
return core.CallCode(self, caller, addr, data, gas, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *Env) DelegateCall(me vm.ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error) {
|
|
||||||
return core.DelegateCall(self, me, addr, data, gas)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *Env) Create(caller vm.ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error) {
|
|
||||||
return core.Create(self, caller, data, gas, value)
|
|
||||||
}
|
|
||||||
|
|
@ -17,11 +17,14 @@
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"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/state"
|
||||||
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
|
@ -44,7 +47,7 @@ type Config struct {
|
||||||
Coinbase common.Address
|
Coinbase common.Address
|
||||||
BlockNumber *big.Int
|
BlockNumber *big.Int
|
||||||
Time *big.Int
|
Time *big.Int
|
||||||
GasLimit *big.Int
|
GasLimit uint64
|
||||||
GasPrice *big.Int
|
GasPrice *big.Int
|
||||||
Value *big.Int
|
Value *big.Int
|
||||||
DisableJit bool // "disable" so it's enabled by default
|
DisableJit bool // "disable" so it's enabled by default
|
||||||
|
|
@ -66,8 +69,8 @@ func setDefaults(cfg *Config) {
|
||||||
if cfg.Time == nil {
|
if cfg.Time == nil {
|
||||||
cfg.Time = big.NewInt(time.Now().Unix())
|
cfg.Time = big.NewInt(time.Now().Unix())
|
||||||
}
|
}
|
||||||
if cfg.GasLimit == nil {
|
if cfg.GasLimit == 0 {
|
||||||
cfg.GasLimit = new(big.Int).Set(common.MaxBig)
|
cfg.GasLimit = math.MaxUint64
|
||||||
}
|
}
|
||||||
if cfg.GasPrice == nil {
|
if cfg.GasPrice == nil {
|
||||||
cfg.GasPrice = new(big.Int)
|
cfg.GasPrice = new(big.Int)
|
||||||
|
|
@ -101,8 +104,25 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
|
||||||
db, _ := ethdb.NewMemDatabase()
|
db, _ := ethdb.NewMemDatabase()
|
||||||
cfg.State, _ = state.New(common.Hash{}, db)
|
cfg.State, _ = state.New(common.Hash{}, db)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
backend := &core.EVMBackend{
|
||||||
|
GetHashFn: cfg.GetHashFn,
|
||||||
|
State: cfg.State,
|
||||||
|
}
|
||||||
|
|
||||||
|
context := vm.Context{
|
||||||
|
CallContext: core.EVMCallContext{core.CanTransfer, core.Transfer},
|
||||||
|
Origin: cfg.Origin,
|
||||||
|
Coinbase: cfg.Coinbase,
|
||||||
|
BlockNumber: cfg.BlockNumber,
|
||||||
|
Time: cfg.Time,
|
||||||
|
Difficulty: cfg.Difficulty,
|
||||||
|
GasLimit: new(big.Int).SetUint64(cfg.GasLimit),
|
||||||
|
GasPrice: cfg.GasPrice,
|
||||||
|
}
|
||||||
|
vmenv := vm.NewEnvironment(context, backend, params.TestChainConfig, vm.Config{})
|
||||||
|
|
||||||
var (
|
var (
|
||||||
vmenv = NewEnv(cfg, cfg.State)
|
|
||||||
sender = cfg.State.CreateAccount(cfg.Origin)
|
sender = cfg.State.CreateAccount(cfg.Origin)
|
||||||
receiver = cfg.State.CreateAccount(common.StringToAddress("contract"))
|
receiver = cfg.State.CreateAccount(common.StringToAddress("contract"))
|
||||||
)
|
)
|
||||||
|
|
@ -114,7 +134,7 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
|
||||||
sender,
|
sender,
|
||||||
receiver.Address(),
|
receiver.Address(),
|
||||||
input,
|
input,
|
||||||
cfg.GasLimit,
|
new(big.Int).SetUint64(cfg.GasLimit),
|
||||||
cfg.Value,
|
cfg.Value,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -129,7 +149,22 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
|
||||||
func Call(address common.Address, input []byte, cfg *Config) ([]byte, error) {
|
func Call(address common.Address, input []byte, cfg *Config) ([]byte, error) {
|
||||||
setDefaults(cfg)
|
setDefaults(cfg)
|
||||||
|
|
||||||
vmenv := NewEnv(cfg, cfg.State)
|
backend := &core.EVMBackend{
|
||||||
|
GetHashFn: cfg.GetHashFn,
|
||||||
|
State: cfg.State,
|
||||||
|
}
|
||||||
|
|
||||||
|
context := vm.Context{
|
||||||
|
CallContext: core.EVMCallContext{core.CanTransfer, core.Transfer},
|
||||||
|
Origin: cfg.Origin,
|
||||||
|
Coinbase: cfg.Coinbase,
|
||||||
|
BlockNumber: cfg.BlockNumber,
|
||||||
|
Time: cfg.Time,
|
||||||
|
Difficulty: cfg.Difficulty,
|
||||||
|
GasLimit: new(big.Int).SetUint64(cfg.GasLimit),
|
||||||
|
GasPrice: cfg.GasPrice,
|
||||||
|
}
|
||||||
|
vmenv := vm.NewEnvironment(context, backend, params.TestChainConfig, vm.Config{})
|
||||||
|
|
||||||
sender := cfg.State.GetOrNewStateObject(cfg.Origin)
|
sender := cfg.State.GetOrNewStateObject(cfg.Origin)
|
||||||
// Call the code with the given configuration.
|
// Call the code with the given configuration.
|
||||||
|
|
@ -137,7 +172,7 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, error) {
|
||||||
sender,
|
sender,
|
||||||
address,
|
address,
|
||||||
input,
|
input,
|
||||||
cfg.GasLimit,
|
new(big.Int).SetUint64(cfg.GasLimit),
|
||||||
cfg.Value,
|
cfg.Value,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,8 +39,8 @@ func TestDefaults(t *testing.T) {
|
||||||
if cfg.Time == nil {
|
if cfg.Time == nil {
|
||||||
t.Error("expected time to be non nil")
|
t.Error("expected time to be non nil")
|
||||||
}
|
}
|
||||||
if cfg.GasLimit == nil {
|
if cfg.GasLimit == 0 {
|
||||||
t.Error("expected time to be non nil")
|
t.Error("expected time to be non 0")
|
||||||
}
|
}
|
||||||
if cfg.GasPrice == nil {
|
if cfg.GasPrice == nil {
|
||||||
t.Error("expected time to be non nil")
|
t.Error("expected time to be non nil")
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ type jumpSeg struct {
|
||||||
gas uint64
|
gas uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j jumpSeg) do(vm *EVM, program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
func (j jumpSeg) do(vm *EVM, program *Program, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||||
if !contract.UseGas(j.gas) {
|
if !contract.UseGas(j.gas) {
|
||||||
return nil, OutOfGasError
|
return nil, OutOfGasError
|
||||||
}
|
}
|
||||||
|
|
@ -42,7 +42,7 @@ type pushSeg struct {
|
||||||
gas uint64
|
gas uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s pushSeg) do(vm *EVM, program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
func (s pushSeg) do(vm *EVM, program *Program, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||||
// Use the calculated gas. When insufficient gas is present, use all gas and return an
|
// Use the calculated gas. When insufficient gas is present, use all gas and return an
|
||||||
// Out Of Gas error
|
// Out Of Gas error
|
||||||
if !contract.UseGas(s.gas) {
|
if !contract.UseGas(s.gas) {
|
||||||
|
|
|
||||||
|
|
@ -29,9 +29,11 @@ import (
|
||||||
|
|
||||||
// Config are the configuration options for the EVM
|
// Config are the configuration options for the EVM
|
||||||
type Config struct {
|
type Config struct {
|
||||||
|
Test bool // boolean to indicate this is a VM Test
|
||||||
|
|
||||||
|
ForceCompile bool
|
||||||
|
NoOptimise bool // skip optimising the program if it hasn't been compiled
|
||||||
Debug bool
|
Debug bool
|
||||||
EnableJit bool
|
|
||||||
ForceJit bool
|
|
||||||
Tracer Tracer
|
Tracer Tracer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -40,26 +42,26 @@ type Config struct {
|
||||||
// The EVM will run the byte code VM or JIT VM based on the passed
|
// The EVM will run the byte code VM or JIT VM based on the passed
|
||||||
// configuration.
|
// configuration.
|
||||||
type EVM struct {
|
type EVM struct {
|
||||||
env Environment
|
env *Environment
|
||||||
jumpTable vmJumpTable
|
jumpTable vmJumpTable
|
||||||
cfg Config
|
cfg Config
|
||||||
gasTable params.GasTable
|
gasTable params.GasTable
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns a new instance of the EVM.
|
// New returns a new instance of the EVM.
|
||||||
func New(env Environment, cfg Config) *EVM {
|
func New(env *Environment, cfg Config) *EVM {
|
||||||
return &EVM{
|
return &EVM{
|
||||||
env: env,
|
env: env,
|
||||||
jumpTable: newJumpTable(env.ChainConfig(), env.BlockNumber()),
|
jumpTable: newJumpTable(env.ChainConfig(), env.BlockNumber),
|
||||||
cfg: cfg,
|
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
|
// Run loops and evaluates the contract's code with the given input data
|
||||||
func (evm *EVM) Run(contract *Contract, input []byte) ([]byte, error) {
|
func (evm *EVM) Run(contract *Contract, input []byte) ([]byte, error) {
|
||||||
evm.env.SetDepth(evm.env.Depth() + 1)
|
evm.env.Depth++
|
||||||
defer evm.env.SetDepth(evm.env.Depth() - 1)
|
defer func() { evm.env.Depth-- }()
|
||||||
|
|
||||||
if contract.CodeAddr != nil {
|
if contract.CodeAddr != nil {
|
||||||
if p, exist := PrecompiledContracts[*contract.CodeAddr]; exist {
|
if p, exist := PrecompiledContracts[*contract.CodeAddr]; exist {
|
||||||
|
|
@ -87,6 +89,9 @@ func (evm *EVM) Run(contract *Contract, input []byte) ([]byte, error) {
|
||||||
// Create and compile program
|
// Create and compile program
|
||||||
program := NewProgram(contract.Code)
|
program := NewProgram(contract.Code)
|
||||||
CompileProgram(program)
|
CompileProgram(program)
|
||||||
|
if !evm.cfg.NoOptimise {
|
||||||
|
OptimiseProgram(program)
|
||||||
|
}
|
||||||
|
|
||||||
return evm.runProgram(program, contract, input)
|
return evm.runProgram(program, contract, input)
|
||||||
case progCompile:
|
case progCompile:
|
||||||
|
|
@ -118,7 +123,7 @@ func (evm *EVM) runProgram(program *Program, contract *Contract, input []byte) (
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
homestead := env.ChainConfig().IsHomestead(env.BlockNumber())
|
homestead := env.ChainConfig().IsHomestead(env.BlockNumber)
|
||||||
for pc < uint64(len(program.instructions)) {
|
for pc < uint64(len(program.instructions)) {
|
||||||
instrCount++
|
instrCount++
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ package vm
|
||||||
|
|
||||||
import "fmt"
|
import "fmt"
|
||||||
|
|
||||||
func NewJitVm(env Environment) VirtualMachine {
|
func NewJitVm(env *Environment) VirtualMachine {
|
||||||
fmt.Printf("Warning! EVM JIT not enabled.\n")
|
fmt.Printf("Warning! EVM JIT not enabled.\n")
|
||||||
return New(env, Config{})
|
return New(env, Config{})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
119
core/vm_env.go
119
core/vm_env.go
|
|
@ -1,119 +0,0 @@
|
||||||
// Copyright 2014 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <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 { f, _ := self.msg.From(); return f }
|
|
||||||
func (self *VMEnv) BlockNumber() *big.Int { return self.header.Number }
|
|
||||||
func (self *VMEnv) Coinbase() common.Address { return self.header.Coinbase }
|
|
||||||
func (self *VMEnv) Time() *big.Int { return self.header.Time }
|
|
||||||
func (self *VMEnv) Difficulty() *big.Int { return self.header.Difficulty }
|
|
||||||
func (self *VMEnv) GasLimit() *big.Int { return self.header.GasLimit }
|
|
||||||
func (self *VMEnv) GasPrice() *big.Int { return self.msg.GasPrice() }
|
|
||||||
func (self *VMEnv) Value() *big.Int { return self.msg.Value() }
|
|
||||||
func (self *VMEnv) Db() vm.Database { return self.state }
|
|
||||||
func (self *VMEnv) Depth() int { return self.depth }
|
|
||||||
func (self *VMEnv) SetDepth(i int) { self.depth = i }
|
|
||||||
func (self *VMEnv) GetHash(n uint64) common.Hash {
|
|
||||||
return self.getHashFn(n)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *VMEnv) AddLog(log *vm.Log) {
|
|
||||||
self.state.AddLog(log)
|
|
||||||
}
|
|
||||||
func (self *VMEnv) CanTransfer(from common.Address, balance *big.Int) bool {
|
|
||||||
return self.state.GetBalance(from).Cmp(balance) >= 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *VMEnv) SnapshotDatabase() int {
|
|
||||||
return self.state.Snapshot()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *VMEnv) RevertToSnapshot(snapshot int) {
|
|
||||||
self.state.RevertToSnapshot(snapshot)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) {
|
|
||||||
Transfer(from, to, amount)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *VMEnv) Call(me vm.ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) {
|
|
||||||
return Call(self, me, addr, data, gas, value)
|
|
||||||
}
|
|
||||||
func (self *VMEnv) CallCode(me vm.ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) {
|
|
||||||
return CallCode(self, me, addr, data, gas, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *VMEnv) DelegateCall(me vm.ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error) {
|
|
||||||
return DelegateCall(self, me, addr, data, gas)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *VMEnv) Create(me vm.ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error) {
|
|
||||||
return Create(self, me, data, gas, value)
|
|
||||||
}
|
|
||||||
13
eth/api.go
13
eth/api.go
|
|
@ -521,9 +521,17 @@ func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, txHash common.
|
||||||
value: tx.Value(),
|
value: tx.Value(),
|
||||||
data: tx.Data(),
|
data: tx.Data(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
backend := &core.EVMBackend{
|
||||||
|
GetHashFn: core.GetHashFn(block.ParentHash(), api.eth.BlockChain()),
|
||||||
|
State: stateDb,
|
||||||
|
}
|
||||||
|
context := core.ToEVMContext(api.config, msg, block.Header())
|
||||||
|
|
||||||
// Mutate the state if we haven't reached the tracing transaction yet
|
// Mutate the state if we haven't reached the tracing transaction yet
|
||||||
if uint64(idx) < txIndex {
|
if uint64(idx) < txIndex {
|
||||||
vmenv := core.NewEnv(stateDb, api.config, api.eth.BlockChain(), msg, block.Header(), vm.Config{})
|
vmenv := vm.NewEnvironment(context, backend, api.config, vm.Config{})
|
||||||
|
//vmenv := core.NewEnv(stateDb, api.config, api.eth.BlockChain(), msg, block.Header(), vm.Config{})
|
||||||
_, _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()))
|
_, _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("mutation failed: %v", err)
|
return nil, fmt.Errorf("mutation failed: %v", err)
|
||||||
|
|
@ -532,7 +540,8 @@ func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, txHash common.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Otherwise trace the transaction and return
|
// Otherwise trace the transaction and return
|
||||||
vmenv := core.NewEnv(stateDb, api.config, api.eth.BlockChain(), msg, block.Header(), vm.Config{Debug: true, Tracer: tracer})
|
vmenv := vm.NewEnvironment(context, backend, api.config, vm.Config{Debug: true, Tracer: tracer})
|
||||||
|
//vmenv := core.NewEnv(stateDb, api.config, api.eth.BlockChain(), msg, block.Header(), vm.Config{Debug: true, Tracer: tracer})
|
||||||
ret, gas, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()))
|
ret, gas, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("tracing failed: %v", err)
|
return nil, fmt.Errorf("tracing failed: %v", err)
|
||||||
|
|
|
||||||
|
|
@ -97,13 +97,20 @@ func (b *EthApiBackend) GetTd(blockHash common.Hash) *big.Int {
|
||||||
return b.eth.blockchain.GetTdByHash(blockHash)
|
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
|
statedb := state.(EthApiState).state
|
||||||
addr, _ := msg.From()
|
addr, _ := msg.From()
|
||||||
from := statedb.GetOrNewStateObject(addr)
|
from := statedb.GetOrNewStateObject(addr)
|
||||||
from.SetBalance(common.MaxBig)
|
from.SetBalance(common.MaxBig)
|
||||||
vmError := func() error { return nil }
|
vmError := func() error { return nil }
|
||||||
return core.NewEnv(statedb, b.eth.chainConfig, b.eth.blockchain, msg, header, vm.Config{}), vmError, nil
|
|
||||||
|
backend := &core.EVMBackend{
|
||||||
|
GetHashFn: core.GetHashFn(header.ParentHash, b.eth.blockchain),
|
||||||
|
State: statedb,
|
||||||
|
}
|
||||||
|
context := core.ToEVMContext(b.eth.chainConfig, msg, header)
|
||||||
|
|
||||||
|
return vm.NewEnvironment(context, backend, b.eth.chainConfig, vm.Config{}), vmError, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *EthApiBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error {
|
func (b *EthApiBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error {
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ type Backend interface {
|
||||||
GetBlock(ctx context.Context, blockHash common.Hash) (*types.Block, error)
|
GetBlock(ctx context.Context, blockHash common.Hash) (*types.Block, error)
|
||||||
GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error)
|
GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error)
|
||||||
GetTd(blockHash common.Hash) *big.Int
|
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
|
// TxPool API
|
||||||
SendTx(ctx context.Context, signedTx *types.Transaction) error
|
SendTx(ctx context.Context, signedTx *types.Transaction) error
|
||||||
RemoveTx(txHash common.Hash)
|
RemoveTx(txHash common.Hash)
|
||||||
|
|
|
||||||
|
|
@ -278,7 +278,7 @@ func wrapError(context string, err error) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CaptureState implements the Tracer interface to trace a single step of VM execution
|
// 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 {
|
if jst.err == nil {
|
||||||
jst.memory.memory = memory
|
jst.memory.memory = memory
|
||||||
jst.stack.stack = stack
|
jst.stack.stack = stack
|
||||||
|
|
|
||||||
|
|
@ -24,8 +24,11 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core"
|
||||||
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -37,7 +40,7 @@ type Env struct {
|
||||||
|
|
||||||
func NewEnv(config *vm.Config) *Env {
|
func NewEnv(config *vm.Config) *Env {
|
||||||
env := &Env{gasLimit: big.NewInt(10000), depth: 0}
|
env := &Env{gasLimit: big.NewInt(10000), depth: 0}
|
||||||
env.evm = vm.New(env, *config)
|
//env.evm = vm.New(env, *config)
|
||||||
return env
|
return env
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -91,14 +94,20 @@ func (account) SetBalance(*big.Int) {}
|
||||||
func (account) SetNonce(uint64) {}
|
func (account) SetNonce(uint64) {}
|
||||||
func (account) Balance() *big.Int { return nil }
|
func (account) Balance() *big.Int { return nil }
|
||||||
func (account) Address() common.Address { return common.Address{} }
|
func (account) Address() common.Address { return common.Address{} }
|
||||||
func (account) ReturnGas(*big.Int, *big.Int) {}
|
func (account) ReturnGas(uint64) {}
|
||||||
func (account) SetCode(common.Hash, []byte) {}
|
func (account) SetCode(common.Hash, []byte) {}
|
||||||
func (account) ForEachStorage(cb func(key, value common.Hash) bool) {}
|
func (account) ForEachStorage(cb func(key, value common.Hash) bool) {}
|
||||||
|
|
||||||
func runTrace(tracer *JavascriptTracer) (interface{}, error) {
|
func runTrace(tracer *JavascriptTracer) (interface{}, error) {
|
||||||
env := NewEnv(&vm.Config{Debug: true, Tracer: tracer})
|
db, _ := ethdb.NewMemDatabase()
|
||||||
|
st, _ := state.New(common.Hash{}, db)
|
||||||
|
backend := &core.EVMBackend{
|
||||||
|
GetHashFn: nil,
|
||||||
|
State: st,
|
||||||
|
}
|
||||||
|
env := vm.NewEnvironment(vm.Context{GasLimit: big.NewInt(1000000)}, backend, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
|
||||||
|
|
||||||
contract := vm.NewContract(account{}, account{}, big.NewInt(0), env.GasLimit(), big.NewInt(1))
|
contract := vm.NewContract(account{}, account{}, big.NewInt(0), env.GasLimit)
|
||||||
contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0}
|
contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0}
|
||||||
|
|
||||||
_, err := env.Vm().Run(contract, []byte{})
|
_, err := env.Vm().Run(contract, []byte{})
|
||||||
|
|
@ -175,7 +184,7 @@ func TestHalt(t *testing.T) {
|
||||||
tracer.Stop(timeout)
|
tracer.Stop(timeout)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if _, err = runTrace(tracer); err.Error() != "stahp in server-side tracer function 'step'" {
|
if _, err = runTrace(tracer); err == nil || (err != nil && err.Error() != "stahp in server-side tracer function 'step'") {
|
||||||
t.Errorf("Expected timeout error, got %v", err)
|
t.Errorf("Expected timeout error, got %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -186,8 +195,14 @@ func TestHaltBetweenSteps(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
env := NewEnv(&vm.Config{Debug: true, Tracer: tracer})
|
db, _ := ethdb.NewMemDatabase()
|
||||||
contract := vm.NewContract(&account{}, &account{}, big.NewInt(0), big.NewInt(0), big.NewInt(0))
|
st, _ := state.New(common.Hash{}, db)
|
||||||
|
backend := &core.EVMBackend{
|
||||||
|
GetHashFn: nil,
|
||||||
|
State: st,
|
||||||
|
}
|
||||||
|
env := vm.NewEnvironment(vm.Context{GasLimit: big.NewInt(1000000)}, backend, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
|
||||||
|
contract := vm.NewContract(&account{}, &account{}, big.NewInt(0), big.NewInt(0))
|
||||||
|
|
||||||
tracer.CaptureState(env, 0, 0, big.NewInt(0), big.NewInt(0), nil, nil, contract, 0, nil)
|
tracer.CaptureState(env, 0, 0, big.NewInt(0), big.NewInt(0), nil, nil, contract, 0, nil)
|
||||||
timeout := errors.New("stahp")
|
timeout := errors.New("stahp")
|
||||||
|
|
|
||||||
|
|
@ -105,6 +105,8 @@ func benchStateTest(chainConfig *params.ChainConfig, test VmTest, env map[string
|
||||||
}
|
}
|
||||||
|
|
||||||
func runStateTests(chainConfig *params.ChainConfig, tests map[string]VmTest, skipTests []string) error {
|
func runStateTests(chainConfig *params.ChainConfig, tests map[string]VmTest, skipTests []string) error {
|
||||||
|
//glog.SetToStderr(true)
|
||||||
|
//glog.SetV(6)
|
||||||
skipTest := make(map[string]bool, len(skipTests))
|
skipTest := make(map[string]bool, len(skipTests))
|
||||||
for _, name := range skipTests {
|
for _, name := range skipTests {
|
||||||
skipTest[name] = true
|
skipTest[name] = true
|
||||||
|
|
@ -225,25 +227,23 @@ func RunState(chainConfig *params.ChainConfig, statedb *state.StateDB, env, tx m
|
||||||
to = &t
|
to = &t
|
||||||
}
|
}
|
||||||
// Set pre compiled contracts
|
// Set pre compiled contracts
|
||||||
snapshot := statedb.Snapshot()
|
|
||||||
gaspool := new(core.GasPool).AddGas(common.Big(env["currentGasLimit"]))
|
gaspool := new(core.GasPool).AddGas(common.Big(env["currentGasLimit"]))
|
||||||
|
|
||||||
key, _ := hex.DecodeString(tx["secretKey"])
|
key, _ := hex.DecodeString(tx["secretKey"])
|
||||||
addr := crypto.PubkeyToAddress(crypto.ToECDSA(key).PublicKey)
|
addr := crypto.PubkeyToAddress(crypto.ToECDSA(key).PublicKey)
|
||||||
message := NewMessage(addr, to, data, value, gas, price, nonce)
|
message := NewMessage(addr, to, data, value, gas, price, nonce)
|
||||||
vmenv := NewEnvFromMap(chainConfig, statedb, env, tx)
|
|
||||||
vmenv.origin = addr
|
|
||||||
|
|
||||||
root, _ := statedb.Commit(false)
|
root, _ := statedb.Commit(false)
|
||||||
statedb.Reset(root)
|
statedb.Reset(root)
|
||||||
|
|
||||||
snapshot := statedb.Snapshot()
|
snapshot := statedb.Snapshot()
|
||||||
|
|
||||||
|
vmenv := NewEVMEnvironment(false, chainConfig, statedb, env, tx)
|
||||||
|
|
||||||
ret, _, err := core.ApplyMessage(vmenv, message, gaspool)
|
ret, _, err := core.ApplyMessage(vmenv, message, gaspool)
|
||||||
if core.IsNonceErr(err) || core.IsInvalidTxErr(err) || core.IsGasLimitErr(err) {
|
if core.IsNonceErr(err) || core.IsInvalidTxErr(err) || core.IsGasLimitErr(err) {
|
||||||
statedb.RevertToSnapshot(snapshot)
|
statedb.RevertToSnapshot(snapshot)
|
||||||
}
|
}
|
||||||
statedb.Commit(chainConfig.IsEIP158(vmenv.BlockNumber()))
|
statedb.Commit(chainConfig.IsEIP158(vmenv.BlockNumber))
|
||||||
|
|
||||||
return ret, vmenv.state.Logs(), vmenv.Gas, err
|
return ret, statedb.Logs(), gas, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
158
tests/util.go
158
tests/util.go
|
|
@ -18,6 +18,7 @@ package tests
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
|
|
@ -148,141 +149,60 @@ type VmTest struct {
|
||||||
PostStateRoot string
|
PostStateRoot string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Env struct {
|
type RuleSet struct {
|
||||||
chainConfig *params.ChainConfig
|
HomesteadBlock *big.Int
|
||||||
depth int
|
DAOForkBlock *big.Int
|
||||||
state *state.StateDB
|
DAOForkSupport bool
|
||||||
skipTransfer bool
|
|
||||||
initial bool
|
|
||||||
Gas, gasPrice *big.Int
|
|
||||||
|
|
||||||
origin common.Address
|
|
||||||
parent common.Hash
|
|
||||||
coinbase common.Address
|
|
||||||
|
|
||||||
number *big.Int
|
|
||||||
time *big.Int
|
|
||||||
difficulty *big.Int
|
|
||||||
gasLimit *big.Int
|
|
||||||
|
|
||||||
vmTest bool
|
|
||||||
|
|
||||||
evm vm.VirtualMachine
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewEnv(chainConfig *params.ChainConfig, state *state.StateDB) *Env {
|
func (r RuleSet) IsHomestead(n *big.Int) bool {
|
||||||
env := &Env{
|
return n.Cmp(r.HomesteadBlock) >= 0
|
||||||
chainConfig: chainConfig,
|
|
||||||
state: state,
|
|
||||||
}
|
|
||||||
return env
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewEnvFromMap(chainConfig *params.ChainConfig, state *state.StateDB, envValues map[string]string, exeValues map[string]string) *Env {
|
func NewEVMEnvironment(vmTest bool, chainConfig *params.ChainConfig, state *state.StateDB, envValues map[string]string, exeValues map[string]string) *vm.Environment {
|
||||||
env := NewEnv(chainConfig, state)
|
origin := common.HexToAddress(exeValues["caller"])
|
||||||
|
if len(exeValues["secretKey"]) > 0 {
|
||||||
env.origin = common.HexToAddress(exeValues["caller"])
|
key, _ := hex.DecodeString(exeValues["secretKey"])
|
||||||
env.parent = common.HexToHash(envValues["previousHash"])
|
origin = crypto.PubkeyToAddress(crypto.ToECDSA(key).PublicKey)
|
||||||
env.coinbase = common.HexToAddress(envValues["currentCoinbase"])
|
|
||||||
env.number = common.Big(envValues["currentNumber"])
|
|
||||||
env.time = common.Big(envValues["currentTimestamp"])
|
|
||||||
env.difficulty = common.Big(envValues["currentDifficulty"])
|
|
||||||
env.gasLimit = common.Big(envValues["currentGasLimit"])
|
|
||||||
env.Gas = new(big.Int)
|
|
||||||
env.gasPrice = common.Big(exeValues["gasPrice"])
|
|
||||||
|
|
||||||
env.evm = vm.New(env, vm.Config{
|
|
||||||
EnableJit: EnableJit,
|
|
||||||
ForceJit: ForceJit,
|
|
||||||
})
|
|
||||||
|
|
||||||
return env
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Env) ChainConfig() *params.ChainConfig { return self.chainConfig }
|
context := vm.Context{
|
||||||
func (self *Env) Vm() vm.Vm { return self.evm }
|
Origin: origin,
|
||||||
func (self *Env) Origin() common.Address { return self.origin }
|
Coinbase: common.HexToAddress(envValues["currentCoinbase"]),
|
||||||
func (self *Env) BlockNumber() *big.Int { return self.number }
|
BlockNumber: common.Big(envValues["currentNumber"]),
|
||||||
func (self *Env) Coinbase() common.Address { return self.coinbase }
|
Time: common.Big(envValues["currentTimestamp"]),
|
||||||
func (self *Env) Time() *big.Int { return self.time }
|
Difficulty: common.Big(envValues["currentDifficulty"]),
|
||||||
func (self *Env) Difficulty() *big.Int { return self.difficulty }
|
GasLimit: common.Big(envValues["currentGasLimit"]),
|
||||||
func (self *Env) Db() vm.Database { return self.state }
|
GasPrice: common.Big(exeValues["gasPrice"]),
|
||||||
func (self *Env) GasLimit() *big.Int { return self.gasLimit }
|
}
|
||||||
func (self *Env) GasPrice() *big.Int { return self.gasPrice }
|
|
||||||
func (self *Env) VmType() vm.Type { return vm.StdVmTy }
|
//var initialCall bool
|
||||||
func (self *Env) GetHash(n uint64) common.Hash {
|
backend := &core.EVMBackend{
|
||||||
|
GetHashFn: func(n uint64) common.Hash {
|
||||||
return common.BytesToHash(crypto.Keccak256([]byte(big.NewInt(int64(n)).String())))
|
return common.BytesToHash(crypto.Keccak256([]byte(big.NewInt(int64(n)).String())))
|
||||||
|
},
|
||||||
|
State: state,
|
||||||
}
|
}
|
||||||
func (self *Env) AddLog(log *vm.Log) {
|
initialCall := true
|
||||||
self.state.AddLog(log)
|
canTransfer := func(db vm.Database, address common.Address, amount *big.Int) bool {
|
||||||
}
|
if vmTest {
|
||||||
func (self *Env) Depth() int { return self.depth }
|
if initialCall {
|
||||||
func (self *Env) SetDepth(i int) { self.depth = i }
|
initialCall = false
|
||||||
func (self *Env) CanTransfer(from common.Address, balance *big.Int) bool {
|
|
||||||
if self.skipTransfer {
|
|
||||||
if self.initial {
|
|
||||||
self.initial = false
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return db.GetBalance(address).Cmp(amount) >= 0
|
||||||
return self.state.GetBalance(from).Cmp(balance) >= 0
|
|
||||||
}
|
}
|
||||||
func (self *Env) SnapshotDatabase() int {
|
transfer := func(db vm.Database, sender, recipient common.Address, amount *big.Int) {
|
||||||
return self.state.Snapshot()
|
if vmTest {
|
||||||
}
|
|
||||||
func (self *Env) RevertToSnapshot(snapshot int) {
|
|
||||||
self.state.RevertToSnapshot(snapshot)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *Env) Transfer(from, to vm.Account, amount *big.Int) {
|
|
||||||
if self.skipTransfer {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
core.Transfer(from, to, amount)
|
core.Transfer(db, sender, recipient, amount)
|
||||||
}
|
}
|
||||||
|
context.CallContext = core.EVMCallContext{canTransfer, transfer}
|
||||||
|
|
||||||
func (self *Env) Call(caller vm.ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) {
|
env := vm.NewEnvironment(context, backend, chainConfig, vm.Config{Test: vmTest})
|
||||||
if self.vmTest && self.depth > 0 {
|
return env
|
||||||
caller.ReturnGas(gas.Uint64())
|
|
||||||
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
ret, err := core.Call(self, caller, addr, data, gas, value)
|
|
||||||
self.Gas = gas
|
|
||||||
|
|
||||||
return ret, err
|
|
||||||
|
|
||||||
}
|
|
||||||
func (self *Env) CallCode(caller vm.ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) {
|
|
||||||
if self.vmTest && self.depth > 0 {
|
|
||||||
caller.ReturnGas(gas.Uint64())
|
|
||||||
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
return core.CallCode(self, caller, addr, data, gas, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *Env) DelegateCall(caller vm.ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error) {
|
|
||||||
if self.vmTest && self.depth > 0 {
|
|
||||||
caller.ReturnGas(gas.Uint64())
|
|
||||||
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
return core.DelegateCall(self, caller, addr, data, gas)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *Env) Create(caller vm.ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error) {
|
|
||||||
if self.vmTest {
|
|
||||||
caller.ReturnGas(gas.Uint64())
|
|
||||||
|
|
||||||
nonce := self.state.GetNonce(caller.Address())
|
|
||||||
obj := self.state.GetOrNewStateObject(crypto.CreateAddress(caller.Address(), nonce))
|
|
||||||
|
|
||||||
return nil, obj.Address(), nil
|
|
||||||
} else {
|
|
||||||
return core.Create(self, caller, data, gas, value)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Message struct {
|
type Message struct {
|
||||||
|
|
|
||||||
|
|
@ -211,7 +211,7 @@ func runVmTest(test VmTest) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func RunVm(state *state.StateDB, env, exec map[string]string) ([]byte, vm.Logs, *big.Int, error) {
|
func RunVm(statedb *state.StateDB, env, exec map[string]string) ([]byte, vm.Logs, *big.Int, error) {
|
||||||
var (
|
var (
|
||||||
to = common.HexToAddress(exec["address"])
|
to = common.HexToAddress(exec["address"])
|
||||||
from = common.HexToAddress(exec["caller"])
|
from = common.HexToAddress(exec["caller"])
|
||||||
|
|
@ -222,13 +222,11 @@ func RunVm(state *state.StateDB, env, exec map[string]string) ([]byte, vm.Logs,
|
||||||
// Reset the pre-compiled contracts for VM tests.
|
// Reset the pre-compiled contracts for VM tests.
|
||||||
vm.PrecompiledContracts = make(map[common.Address]vm.PrecompiledContract)
|
vm.PrecompiledContracts = make(map[common.Address]vm.PrecompiledContract)
|
||||||
|
|
||||||
caller := state.GetOrNewStateObject(from)
|
caller := statedb.GetOrNewStateObject(from)
|
||||||
|
|
||||||
vmenv := NewEnvFromMap(¶ms.ChainConfig{params.MainNetHomesteadBlock, params.MainNetDAOForkBlock, true, nil, common.Hash{}, nil}, state, env, exec)
|
config := ¶ms.ChainConfig{params.MainNetHomesteadBlock, params.MainNetDAOForkBlock, true, nil, common.Hash{}, nil}
|
||||||
vmenv.vmTest = true
|
vmenv := NewEVMEnvironment(true, config, statedb, env, exec)
|
||||||
vmenv.skipTransfer = true
|
|
||||||
vmenv.initial = true
|
|
||||||
ret, err := vmenv.Call(caller, to, data, gas, value)
|
ret, err := vmenv.Call(caller, to, data, gas, value)
|
||||||
|
|
||||||
return ret, vmenv.state.Logs(), vmenv.Gas, err
|
return ret, statedb.Logs(), gas, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue