quorum/cmd/evm/main.go

312 lines
8.5 KiB
Go
Raw Normal View History

2015-07-06 17:54:22 -07:00
// Copyright 2014 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum 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
2015-07-06 17:54:22 -07:00
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
2014-11-10 02:47:37 -08:00
2015-07-06 20:08:16 -07:00
// evm executes EVM code snippets.
2014-11-10 02:47:37 -08:00
package main
import (
"fmt"
"io/ioutil"
2014-11-10 02:47:37 -08:00
"math/big"
"os"
"runtime"
"time"
"github.com/ethereum/go-ethereum/cmd/utils"
2015-03-22 04:12:29 -07:00
"github.com/ethereum/go-ethereum/common"
2014-12-04 02:30:41 -08:00
"github.com/ethereum/go-ethereum/core"
2015-03-24 07:27:05 -07:00
"github.com/ethereum/go-ethereum/core/state"
2014-12-04 02:30:41 -08:00
"github.com/ethereum/go-ethereum/core/types"
2015-03-24 07:27:05 -07:00
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
2014-11-10 03:42:16 -08:00
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/params"
"gopkg.in/urfave/cli.v1"
2014-11-10 02:47:37 -08:00
)
var gitCommit = "" // Git SHA1 commit hash of the release (set via linker flags)
2014-11-10 02:47:37 -08:00
var (
app = utils.NewApp(gitCommit, "the evm command line interface")
DebugFlag = cli.BoolFlag{
Name: "debug",
Usage: "output full trace logs",
}
ForceJitFlag = cli.BoolFlag{
Name: "forcejit",
Usage: "forces jit compilation",
}
DisableJitFlag = cli.BoolFlag{
Name: "nojit",
Usage: "disabled jit compilation",
}
CodeFlag = cli.StringFlag{
Name: "code",
Usage: "EVM code",
}
CodeFileFlag = cli.StringFlag{
Name: "codefile",
Usage: "file containing EVM code",
}
GasFlag = cli.StringFlag{
Name: "gas",
Usage: "gas limit for the evm",
Value: "10000000000",
}
PriceFlag = cli.StringFlag{
Name: "price",
Usage: "price set for the evm",
Value: "0",
}
ValueFlag = cli.StringFlag{
Name: "value",
Usage: "value set for the evm",
Value: "0",
}
DumpFlag = cli.BoolFlag{
Name: "dump",
Usage: "dumps the state after the run",
}
InputFlag = cli.StringFlag{
Name: "input",
Usage: "input for the EVM",
}
SysStatFlag = cli.BoolFlag{
Name: "sysstat",
Usage: "display system stats",
}
VerbosityFlag = cli.IntFlag{
Name: "verbosity",
Usage: "sets the verbosity level",
}
CreateFlag = cli.BoolFlag{
Name: "create",
Usage: "indicates the action should be create rather than call",
}
2014-11-10 02:47:37 -08:00
)
func init() {
app.Flags = []cli.Flag{
CreateFlag,
DebugFlag,
VerbosityFlag,
ForceJitFlag,
DisableJitFlag,
SysStatFlag,
CodeFlag,
CodeFileFlag,
GasFlag,
PriceFlag,
ValueFlag,
DumpFlag,
InputFlag,
}
app.Action = run
2014-11-10 02:47:37 -08:00
}
func run(ctx *cli.Context) error {
glog.SetToStderr(true)
glog.SetV(ctx.GlobalInt(VerbosityFlag.Name))
2014-11-10 02:47:37 -08:00
2014-12-04 02:30:41 -08:00
db, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, db)
2015-04-01 02:42:02 -07:00
sender := statedb.CreateAccount(common.StringToAddress("sender"))
2014-12-04 02:30:41 -08:00
logger := vm.NewStructLogger(nil)
vmenv := NewEnv(statedb, common.StringToAddress("evmuser"), common.Big(ctx.GlobalString(ValueFlag.Name)), vm.Config{
Debug: ctx.GlobalBool(DebugFlag.Name),
ForceJit: ctx.GlobalBool(ForceJitFlag.Name),
EnableJit: !ctx.GlobalBool(DisableJitFlag.Name),
Tracer: logger,
})
2014-11-10 02:47:37 -08:00
tstart := time.Now()
var (
code []byte
ret []byte
err error
)
if ctx.GlobalString(CodeFlag.Name) != "" {
code = common.Hex2Bytes(ctx.GlobalString(CodeFlag.Name))
} else {
var hexcode []byte
if ctx.GlobalString(CodeFileFlag.Name) != "" {
var err error
hexcode, err = ioutil.ReadFile(ctx.GlobalString(CodeFileFlag.Name))
if err != nil {
fmt.Printf("Could not load code from file: %v\n", err)
os.Exit(1)
}
} else {
var err error
hexcode, err = ioutil.ReadAll(os.Stdin)
if err != nil {
fmt.Printf("Could not load code from stdin: %v\n", err)
os.Exit(1)
}
}
code = common.Hex2Bytes(string(hexcode[:]))
}
if ctx.GlobalBool(CreateFlag.Name) {
input := append(code, common.Hex2Bytes(ctx.GlobalString(InputFlag.Name))...)
ret, _, err = vmenv.Create(
sender,
input,
common.Big(ctx.GlobalString(GasFlag.Name)),
common.Big(ctx.GlobalString(PriceFlag.Name)),
common.Big(ctx.GlobalString(ValueFlag.Name)),
)
} else {
receiver := statedb.CreateAccount(common.StringToAddress("receiver"))
receiver.SetCode(crypto.Keccak256Hash(code), code)
ret, err = vmenv.Call(
sender,
receiver.Address(),
common.Hex2Bytes(ctx.GlobalString(InputFlag.Name)),
common.Big(ctx.GlobalString(GasFlag.Name)),
common.Big(ctx.GlobalString(PriceFlag.Name)),
common.Big(ctx.GlobalString(ValueFlag.Name)),
)
}
vmdone := time.Since(tstart)
2014-11-10 02:47:37 -08:00
if ctx.GlobalBool(DumpFlag.Name) {
statedb.Commit()
2014-12-04 02:30:41 -08:00
fmt.Println(string(statedb.Dump()))
2014-11-10 03:42:16 -08:00
}
vm.StdErrFormat(logger.StructLogs())
if ctx.GlobalBool(SysStatFlag.Name) {
var mem runtime.MemStats
runtime.ReadMemStats(&mem)
fmt.Printf("vm took %v\n", vmdone)
fmt.Printf(`alloc: %d
2014-11-10 02:47:37 -08:00
tot alloc: %d
no. malloc: %d
heap alloc: %d
heap objs: %d
num gc: %d
`, mem.Alloc, mem.TotalAlloc, mem.Mallocs, mem.HeapAlloc, mem.HeapObjects, mem.NumGC)
}
2014-11-10 02:47:37 -08:00
fmt.Printf("OUT: 0x%x", ret)
if err != nil {
fmt.Printf(" error: %v", err)
}
fmt.Println()
return nil
}
func main() {
if err := app.Run(os.Args); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
2014-11-10 02:47:37 -08:00
}
2014-12-04 02:30:41 -08:00
type VMEnv struct {
2014-12-04 03:09:22 -08:00
state *state.StateDB
2014-12-04 02:30:41 -08:00
block *types.Block
2015-03-22 04:12:29 -07:00
transactor *common.Address
2014-12-04 02:30:41 -08:00
value *big.Int
depth int
Gas *big.Int
time *big.Int
2015-06-10 07:46:43 -07:00
logs []vm.StructLog
evm *vm.EVM
2014-11-10 02:47:37 -08:00
}
func NewEnv(state *state.StateDB, transactor common.Address, value *big.Int, cfg vm.Config) *VMEnv {
env := &VMEnv{
2014-12-04 02:30:41 -08:00
state: state,
2015-03-22 04:12:29 -07:00
transactor: &transactor,
2014-12-04 02:30:41 -08:00
value: value,
time: big.NewInt(time.Now().Unix()),
2014-12-04 02:30:41 -08:00
}
env.evm = vm.New(env, cfg)
return env
2014-12-04 02:30:41 -08:00
}
// ruleSet implements vm.RuleSet and will always default to the homestead rule set.
type ruleSet struct{}
func (ruleSet) IsHomestead(*big.Int) bool { return true }
func (ruleSet) GasTable(*big.Int) params.GasTable {
return params.GasTableHomesteadGasRepriceFork
}
func (self *VMEnv) RuleSet() vm.RuleSet { return ruleSet{} }
func (self *VMEnv) Vm() vm.Vm { return self.evm }
func (self *VMEnv) Db() vm.Database { return self.state }
func (self *VMEnv) SnapshotDatabase() int { return self.state.Snapshot() }
func (self *VMEnv) RevertToSnapshot(snap int) { self.state.RevertToSnapshot(snap) }
func (self *VMEnv) Origin() common.Address { return *self.transactor }
func (self *VMEnv) BlockNumber() *big.Int { return common.Big0 }
func (self *VMEnv) Coinbase() common.Address { return *self.transactor }
func (self *VMEnv) Time() *big.Int { return self.time }
func (self *VMEnv) Difficulty() *big.Int { return common.Big1 }
func (self *VMEnv) BlockHash() []byte { return make([]byte, 32) }
func (self *VMEnv) Value() *big.Int { return self.value }
func (self *VMEnv) GasLimit() *big.Int { return big.NewInt(1000000000) }
func (self *VMEnv) VmType() vm.Type { return vm.StdVmTy }
func (self *VMEnv) Depth() int { return 0 }
func (self *VMEnv) SetDepth(i int) { self.depth = i }
2015-03-22 04:12:29 -07:00
func (self *VMEnv) GetHash(n uint64) common.Hash {
if self.block.Number().Cmp(big.NewInt(int64(n))) == 0 {
return self.block.Hash()
}
2015-03-22 04:12:29 -07:00
return common.Hash{}
}
func (self *VMEnv) AddLog(log *vm.Log) {
2014-12-04 02:30:41 -08:00
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)
2014-12-04 02:30:41 -08:00
}
func (self *VMEnv) Call(caller vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
self.Gas = gas
return core.Call(self, caller, addr, data, gas, price, value)
2014-12-04 02:30:41 -08:00
}
func (self *VMEnv) CallCode(caller vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
return core.CallCode(self, caller, addr, data, gas, price, value)
2014-11-10 02:47:37 -08:00
}
func (self *VMEnv) DelegateCall(caller vm.ContractRef, addr common.Address, data []byte, gas, price *big.Int) ([]byte, error) {
return core.DelegateCall(self, caller, addr, data, gas, price)
}
func (self *VMEnv) Create(caller vm.ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) {
return core.Create(self, caller, data, gas, price, value)
2014-11-10 02:47:37 -08:00
}
core, core/vm: dual state & read only EVM This commit implements a dual state approach. The dual state approach separates public and private state by making the core vm environment context aware. Although not currently implemented it will need to prohibit value transfers and it must initialise all transactions from accounts on the public state. This means that sending transactions increments the account nonce on the public state and contract addresses are derived from the public state when initialised by a transaction. For obvious reasons, contract created by private contracts are still derived from public state. This is required in order to have consensus over the public state at all times as non-private participants would still process the transaction on the public state even though private payload can not be decrypted. This means that participants of a private group must do the same in order to have public consensus. However the creation of the contract and interaction still occurs on the private state. It implements support for the following calling model: S: sender, (X): private, X: public, ->: direction, [ ]: read only mode 1. S -> A -> B 2. S -> (A) -> (B) 3. S -> (A) -> [ B -> C ] It does not support 1. (S) -> A 2. (S) -> (A) 3. S -> (A) -> B Implemented "read only" mode for the EVM. Read only mode is checked during any opcode that could potentially modify the state. If such an opcode is encountered during "read only", it throws an exception. The EVM is flagged "read only" when a private contract calls in to public state.
2016-10-31 04:46:40 -07:00
func (*VMEnv) ReadOnly() bool { return false }