Skip to content

Commit 3fc7c97

Browse files
obscurenfjl
authored andcommitted
core, core/vm: implemented a generic environment (#3348)
Environment is now a struct (not an interface). This reduces a lot of tech-debt throughout the codebase where a virtual machine environment had to be implemented in order to test or run it. The new environment is suitable to be used en the json tests, core consensus and light client.
1 parent 7f79d24 commit 3fc7c97

39 files changed

+952
-1253
lines changed

accounts/abi/bind/backends/simulated.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,11 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallM
225225
from.SetBalance(common.MaxBig)
226226
// Execute the call.
227227
msg := callmsg{call}
228-
vmenv := core.NewEnv(statedb, chainConfig, b.blockchain, msg, block.Header(), vm.Config{})
228+
229+
evmContext := core.NewEVMContext(msg, block.Header(), b.blockchain)
230+
// Create a new environment which holds all relevant information
231+
// about the transaction and calling mechanisms.
232+
vmenv := vm.NewEnvironment(evmContext, statedb, chainConfig, vm.Config{})
229233
gaspool := new(core.GasPool).AddGas(common.MaxBig)
230234
ret, gasUsed, _, err := core.NewStateTransition(vmenv, msg, gaspool).TransitionDb()
231235
return ret, gasUsed, err

cmd/evm/main.go

Lines changed: 25 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -20,21 +20,18 @@ package main
2020
import (
2121
"fmt"
2222
"io/ioutil"
23-
"math/big"
2423
"os"
25-
"runtime"
24+
goruntime "runtime"
2625
"time"
2726

2827
"github.com/ethereum/go-ethereum/cmd/utils"
2928
"github.com/ethereum/go-ethereum/common"
30-
"github.com/ethereum/go-ethereum/core"
3129
"github.com/ethereum/go-ethereum/core/state"
32-
"github.com/ethereum/go-ethereum/core/types"
3330
"github.com/ethereum/go-ethereum/core/vm"
31+
"github.com/ethereum/go-ethereum/core/vm/runtime"
3432
"github.com/ethereum/go-ethereum/crypto"
3533
"github.com/ethereum/go-ethereum/ethdb"
3634
"github.com/ethereum/go-ethereum/logger/glog"
37-
"github.com/ethereum/go-ethereum/params"
3835
"gopkg.in/urfave/cli.v1"
3936
)
4037

@@ -129,13 +126,6 @@ func run(ctx *cli.Context) error {
129126

130127
logger := vm.NewStructLogger(nil)
131128

132-
vmenv := NewEnv(statedb, common.StringToAddress("evmuser"), common.Big(ctx.GlobalString(ValueFlag.Name)), vm.Config{
133-
Debug: ctx.GlobalBool(DebugFlag.Name),
134-
ForceJit: ctx.GlobalBool(ForceJitFlag.Name),
135-
EnableJit: !ctx.GlobalBool(DisableJitFlag.Name),
136-
Tracer: logger,
137-
})
138-
139129
tstart := time.Now()
140130

141131
var (
@@ -168,25 +158,30 @@ func run(ctx *cli.Context) error {
168158

169159
if ctx.GlobalBool(CreateFlag.Name) {
170160
input := append(code, common.Hex2Bytes(ctx.GlobalString(InputFlag.Name))...)
171-
ret, _, err = vmenv.Create(
172-
sender,
173-
input,
174-
common.Big(ctx.GlobalString(GasFlag.Name)),
175-
common.Big(ctx.GlobalString(PriceFlag.Name)),
176-
common.Big(ctx.GlobalString(ValueFlag.Name)),
177-
)
161+
ret, _, err = runtime.Create(input, &runtime.Config{
162+
Origin: sender.Address(),
163+
State: statedb,
164+
GasLimit: common.Big(ctx.GlobalString(GasFlag.Name)),
165+
GasPrice: common.Big(ctx.GlobalString(PriceFlag.Name)),
166+
Value: common.Big(ctx.GlobalString(ValueFlag.Name)),
167+
EVMConfig: vm.Config{
168+
Tracer: logger,
169+
},
170+
})
178171
} else {
179172
receiver := statedb.CreateAccount(common.StringToAddress("receiver"))
180-
181173
receiver.SetCode(crypto.Keccak256Hash(code), code)
182-
ret, err = vmenv.Call(
183-
sender,
184-
receiver.Address(),
185-
common.Hex2Bytes(ctx.GlobalString(InputFlag.Name)),
186-
common.Big(ctx.GlobalString(GasFlag.Name)),
187-
common.Big(ctx.GlobalString(PriceFlag.Name)),
188-
common.Big(ctx.GlobalString(ValueFlag.Name)),
189-
)
174+
175+
ret, err = runtime.Call(receiver.Address(), common.Hex2Bytes(ctx.GlobalString(InputFlag.Name)), &runtime.Config{
176+
Origin: sender.Address(),
177+
State: statedb,
178+
GasLimit: common.Big(ctx.GlobalString(GasFlag.Name)),
179+
GasPrice: common.Big(ctx.GlobalString(PriceFlag.Name)),
180+
Value: common.Big(ctx.GlobalString(ValueFlag.Name)),
181+
EVMConfig: vm.Config{
182+
Tracer: logger,
183+
},
184+
})
190185
}
191186
vmdone := time.Since(tstart)
192187

@@ -197,8 +192,8 @@ func run(ctx *cli.Context) error {
197192
vm.StdErrFormat(logger.StructLogs())
198193

199194
if ctx.GlobalBool(SysStatFlag.Name) {
200-
var mem runtime.MemStats
201-
runtime.ReadMemStats(&mem)
195+
var mem goruntime.MemStats
196+
goruntime.ReadMemStats(&mem)
202197
fmt.Printf("vm took %v\n", vmdone)
203198
fmt.Printf(`alloc: %d
204199
tot alloc: %d
@@ -223,87 +218,3 @@ func main() {
223218
os.Exit(1)
224219
}
225220
}
226-
227-
type VMEnv struct {
228-
state *state.StateDB
229-
block *types.Block
230-
231-
transactor *common.Address
232-
value *big.Int
233-
234-
depth int
235-
Gas *big.Int
236-
time *big.Int
237-
logs []vm.StructLog
238-
239-
evm *vm.EVM
240-
}
241-
242-
func NewEnv(state *state.StateDB, transactor common.Address, value *big.Int, cfg vm.Config) *VMEnv {
243-
env := &VMEnv{
244-
state: state,
245-
transactor: &transactor,
246-
value: value,
247-
time: big.NewInt(time.Now().Unix()),
248-
}
249-
250-
env.evm = vm.New(env, cfg)
251-
return env
252-
}
253-
254-
// ruleSet implements vm.ChainConfig and will always default to the homestead rule set.
255-
type ruleSet struct{}
256-
257-
func (ruleSet) IsHomestead(*big.Int) bool { return true }
258-
func (ruleSet) GasTable(*big.Int) params.GasTable {
259-
return params.GasTableHomesteadGasRepriceFork
260-
}
261-
262-
func (self *VMEnv) ChainConfig() *params.ChainConfig { return params.TestChainConfig }
263-
func (self *VMEnv) Vm() vm.Vm { return self.evm }
264-
func (self *VMEnv) Db() vm.Database { return self.state }
265-
func (self *VMEnv) SnapshotDatabase() int { return self.state.Snapshot() }
266-
func (self *VMEnv) RevertToSnapshot(snap int) { self.state.RevertToSnapshot(snap) }
267-
func (self *VMEnv) Origin() common.Address { return *self.transactor }
268-
func (self *VMEnv) BlockNumber() *big.Int { return common.Big0 }
269-
func (self *VMEnv) Coinbase() common.Address { return *self.transactor }
270-
func (self *VMEnv) Time() *big.Int { return self.time }
271-
func (self *VMEnv) Difficulty() *big.Int { return common.Big1 }
272-
func (self *VMEnv) BlockHash() []byte { return make([]byte, 32) }
273-
func (self *VMEnv) Value() *big.Int { return self.value }
274-
func (self *VMEnv) GasLimit() *big.Int { return big.NewInt(1000000000) }
275-
func (self *VMEnv) VmType() vm.Type { return vm.StdVmTy }
276-
func (self *VMEnv) Depth() int { return 0 }
277-
func (self *VMEnv) SetDepth(i int) { self.depth = i }
278-
func (self *VMEnv) GetHash(n uint64) common.Hash {
279-
if self.block.Number().Cmp(big.NewInt(int64(n))) == 0 {
280-
return self.block.Hash()
281-
}
282-
return common.Hash{}
283-
}
284-
func (self *VMEnv) AddLog(log *vm.Log) {
285-
self.state.AddLog(log)
286-
}
287-
func (self *VMEnv) CanTransfer(from common.Address, balance *big.Int) bool {
288-
return self.state.GetBalance(from).Cmp(balance) >= 0
289-
}
290-
func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) {
291-
core.Transfer(from, to, amount)
292-
}
293-
294-
func (self *VMEnv) Call(caller vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
295-
self.Gas = gas
296-
return core.Call(self, caller, addr, data, gas, price, value)
297-
}
298-
299-
func (self *VMEnv) CallCode(caller vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
300-
return core.CallCode(self, caller, addr, data, gas, price, value)
301-
}
302-
303-
func (self *VMEnv) DelegateCall(caller vm.ContractRef, addr common.Address, data []byte, gas, price *big.Int) ([]byte, error) {
304-
return core.DelegateCall(self, caller, addr, data, gas, price)
305-
}
306-
307-
func (self *VMEnv) Create(caller vm.ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) {
308-
return core.Create(self, caller, data, gas, price, value)
309-
}

core/evm.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// Copyright 2014 The go-ethereum Authors
2+
// This file is part of the go-ethereum library.
3+
//
4+
// The go-ethereum library is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Lesser General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// The go-ethereum library is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Lesser General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Lesser General Public License
15+
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
16+
17+
package core
18+
19+
import (
20+
"math/big"
21+
22+
"github.com/ethereum/go-ethereum/common"
23+
"github.com/ethereum/go-ethereum/core/types"
24+
"github.com/ethereum/go-ethereum/core/vm"
25+
)
26+
27+
// BlockFetcher retrieves headers by their hash
28+
type HeaderFetcher interface {
29+
// GetHeader returns the hash corresponding to their hash
30+
GetHeader(common.Hash, uint64) *types.Header
31+
}
32+
33+
// NewEVMContext creates a new context for use in the EVM.
34+
func NewEVMContext(msg Message, header *types.Header, chain HeaderFetcher) vm.Context {
35+
return vm.Context{
36+
CanTransfer: CanTransfer,
37+
Transfer: Transfer,
38+
GetHash: GetHashFn(header, chain),
39+
40+
Origin: msg.From(),
41+
Coinbase: header.Coinbase,
42+
BlockNumber: new(big.Int).Set(header.Number),
43+
Time: new(big.Int).Set(header.Time),
44+
Difficulty: new(big.Int).Set(header.Difficulty),
45+
GasLimit: new(big.Int).Set(header.GasLimit),
46+
GasPrice: new(big.Int).Set(msg.GasPrice()),
47+
}
48+
}
49+
50+
// GetHashFn returns a GetHashFunc which retrieves header hashes by number
51+
func GetHashFn(ref *types.Header, chain HeaderFetcher) func(n uint64) common.Hash {
52+
return func(n uint64) common.Hash {
53+
for header := chain.GetHeader(ref.ParentHash, ref.Number.Uint64()-1); header != nil; header = chain.GetHeader(header.ParentHash, header.Number.Uint64()-1) {
54+
if header.Number.Uint64() == n {
55+
return header.Hash()
56+
}
57+
}
58+
59+
return common.Hash{}
60+
}
61+
}
62+
63+
// CanTransfer checks wether there are enough funds in the address' account to make a transfer.
64+
// This does not take the necessary gas in to account to make the transfer valid.
65+
func CanTransfer(db vm.StateDB, addr common.Address, amount *big.Int) bool {
66+
return db.GetBalance(addr).Cmp(amount) >= 0
67+
}
68+
69+
// Transfer subtracts amount from sender and adds amount to recipient using the given Db
70+
func Transfer(db vm.StateDB, sender, recipient common.Address, amount *big.Int) {
71+
db.SubBalance(sender, amount)
72+
db.AddBalance(recipient, amount)
73+
}

0 commit comments

Comments
 (0)