Skip to content

Commit 230cf2e

Browse files
obscurenfjl
authored andcommitted
cmd/evm, core/asm: add EVM assembler (#3686)
The evm compile command implements a simple assembly language that compiles to EVM bytecode.
1 parent 7ff75ac commit 230cf2e

File tree

8 files changed

+852
-133
lines changed

8 files changed

+852
-133
lines changed

cmd/evm/compiler.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Copyright 2017 The go-ethereum Authors
2+
// This file is part of go-ethereum.
3+
//
4+
// go-ethereum is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU 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+
// go-ethereum 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 General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU General Public License
15+
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
16+
17+
package main
18+
19+
import (
20+
"errors"
21+
"fmt"
22+
"io/ioutil"
23+
24+
"github.com/ethereum/go-ethereum/cmd/evm/internal/compiler"
25+
26+
cli "gopkg.in/urfave/cli.v1"
27+
)
28+
29+
var compileCommand = cli.Command{
30+
Action: compileCmd,
31+
Name: "compile",
32+
Usage: "compiles easm source to evm binary",
33+
ArgsUsage: "<file>",
34+
}
35+
36+
func compileCmd(ctx *cli.Context) error {
37+
debug := ctx.GlobalBool(DebugFlag.Name)
38+
39+
if len(ctx.Args().First()) == 0 {
40+
return errors.New("filename required")
41+
}
42+
43+
fn := ctx.Args().First()
44+
src, err := ioutil.ReadFile(fn)
45+
if err != nil {
46+
return err
47+
}
48+
49+
bin, err := compiler.Compile(fn, src, debug)
50+
if err != nil {
51+
return err
52+
}
53+
fmt.Println(bin)
54+
return nil
55+
}

cmd/evm/internal/compiler/compiler.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Copyright 2017 The go-ethereum Authors
2+
// This file is part of go-ethereum.
3+
//
4+
// go-ethereum is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU 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+
// go-ethereum 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 General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU General Public License
15+
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
16+
17+
package compiler
18+
19+
import (
20+
"errors"
21+
"fmt"
22+
23+
"github.com/ethereum/go-ethereum/core/asm"
24+
)
25+
26+
func Compile(fn string, src []byte, debug bool) (string, error) {
27+
compiler := asm.NewCompiler(debug)
28+
compiler.Feed(asm.Lex(fn, src, debug))
29+
30+
bin, compileErrors := compiler.Compile()
31+
if len(compileErrors) > 0 {
32+
// report errors
33+
for _, err := range compileErrors {
34+
fmt.Printf("%s:%v\n", fn, err)
35+
}
36+
return "", errors.New("compiling failed")
37+
}
38+
return bin, nil
39+
}

cmd/evm/main.go

Lines changed: 3 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,10 @@ package main
1919

2020
import (
2121
"fmt"
22-
"io/ioutil"
2322
"math/big"
2423
"os"
25-
goruntime "runtime"
26-
"time"
2724

2825
"github.com/ethereum/go-ethereum/cmd/utils"
29-
"github.com/ethereum/go-ethereum/common"
30-
"github.com/ethereum/go-ethereum/core/state"
31-
"github.com/ethereum/go-ethereum/core/vm"
32-
"github.com/ethereum/go-ethereum/core/vm/runtime"
33-
"github.com/ethereum/go-ethereum/ethdb"
34-
"github.com/ethereum/go-ethereum/log"
3526
"gopkg.in/urfave/cli.v1"
3627
)
3728

@@ -108,113 +99,10 @@ func init() {
10899
InputFlag,
109100
DisableGasMeteringFlag,
110101
}
111-
app.Action = run
112-
}
113-
114-
func run(ctx *cli.Context) error {
115-
glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false)))
116-
glogger.Verbosity(log.Lvl(ctx.GlobalInt(VerbosityFlag.Name)))
117-
log.Root().SetHandler(glogger)
118-
119-
var (
120-
db, _ = ethdb.NewMemDatabase()
121-
statedb, _ = state.New(common.Hash{}, db)
122-
address = common.StringToAddress("sender")
123-
sender = vm.AccountRef(address)
124-
)
125-
statedb.CreateAccount(common.StringToAddress("sender"))
126-
127-
logger := vm.NewStructLogger(nil)
128-
129-
tstart := time.Now()
130-
131-
var (
132-
code []byte
133-
ret []byte
134-
err error
135-
)
136-
137-
if ctx.GlobalString(CodeFlag.Name) != "" {
138-
code = common.Hex2Bytes(ctx.GlobalString(CodeFlag.Name))
139-
} else {
140-
var hexcode []byte
141-
if ctx.GlobalString(CodeFileFlag.Name) != "" {
142-
var err error
143-
hexcode, err = ioutil.ReadFile(ctx.GlobalString(CodeFileFlag.Name))
144-
if err != nil {
145-
fmt.Printf("Could not load code from file: %v\n", err)
146-
os.Exit(1)
147-
}
148-
} else {
149-
var err error
150-
hexcode, err = ioutil.ReadAll(os.Stdin)
151-
if err != nil {
152-
fmt.Printf("Could not load code from stdin: %v\n", err)
153-
os.Exit(1)
154-
}
155-
}
156-
code = common.Hex2Bytes(string(hexcode[:]))
157-
}
158-
159-
if ctx.GlobalBool(CreateFlag.Name) {
160-
input := append(code, common.Hex2Bytes(ctx.GlobalString(InputFlag.Name))...)
161-
ret, _, err = runtime.Create(input, &runtime.Config{
162-
Origin: sender.Address(),
163-
State: statedb,
164-
GasLimit: ctx.GlobalUint64(GasFlag.Name),
165-
GasPrice: utils.GlobalBig(ctx, PriceFlag.Name),
166-
Value: utils.GlobalBig(ctx, ValueFlag.Name),
167-
EVMConfig: vm.Config{
168-
Tracer: logger,
169-
Debug: ctx.GlobalBool(DebugFlag.Name),
170-
DisableGasMetering: ctx.GlobalBool(DisableGasMeteringFlag.Name),
171-
},
172-
})
173-
} else {
174-
receiverAddress := common.StringToAddress("receiver")
175-
statedb.CreateAccount(receiverAddress)
176-
statedb.SetCode(receiverAddress, code)
177-
178-
ret, err = runtime.Call(receiverAddress, common.Hex2Bytes(ctx.GlobalString(InputFlag.Name)), &runtime.Config{
179-
Origin: sender.Address(),
180-
State: statedb,
181-
GasLimit: ctx.GlobalUint64(GasFlag.Name),
182-
GasPrice: utils.GlobalBig(ctx, PriceFlag.Name),
183-
Value: utils.GlobalBig(ctx, ValueFlag.Name),
184-
EVMConfig: vm.Config{
185-
Tracer: logger,
186-
Debug: ctx.GlobalBool(DebugFlag.Name),
187-
DisableGasMetering: ctx.GlobalBool(DisableGasMeteringFlag.Name),
188-
},
189-
})
190-
}
191-
vmdone := time.Since(tstart)
192-
193-
if ctx.GlobalBool(DumpFlag.Name) {
194-
statedb.Commit(true)
195-
fmt.Println(string(statedb.Dump()))
196-
}
197-
vm.StdErrFormat(logger.StructLogs())
198-
199-
if ctx.GlobalBool(SysStatFlag.Name) {
200-
var mem goruntime.MemStats
201-
goruntime.ReadMemStats(&mem)
202-
fmt.Printf("vm took %v\n", vmdone)
203-
fmt.Printf(`alloc: %d
204-
tot alloc: %d
205-
no. malloc: %d
206-
heap alloc: %d
207-
heap objs: %d
208-
num gc: %d
209-
`, mem.Alloc, mem.TotalAlloc, mem.Mallocs, mem.HeapAlloc, mem.HeapObjects, mem.NumGC)
210-
}
211-
212-
fmt.Printf("OUT: 0x%x", ret)
213-
if err != nil {
214-
fmt.Printf(" error: %v", err)
102+
app.Commands = []cli.Command{
103+
compileCommand,
104+
runCommand,
215105
}
216-
fmt.Println()
217-
return nil
218106
}
219107

220108
func main() {

cmd/evm/runner.go

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
// Copyright 2017 The go-ethereum Authors
2+
// This file is part of go-ethereum.
3+
//
4+
// go-ethereum is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU 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+
// go-ethereum 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 General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU General Public License
15+
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
16+
17+
package main
18+
19+
import (
20+
"bytes"
21+
"fmt"
22+
"io/ioutil"
23+
"os"
24+
"time"
25+
26+
goruntime "runtime"
27+
28+
"github.com/ethereum/go-ethereum/cmd/utils"
29+
"github.com/ethereum/go-ethereum/common"
30+
"github.com/ethereum/go-ethereum/core/state"
31+
"github.com/ethereum/go-ethereum/core/vm"
32+
"github.com/ethereum/go-ethereum/core/vm/runtime"
33+
"github.com/ethereum/go-ethereum/ethdb"
34+
"github.com/ethereum/go-ethereum/log"
35+
cli "gopkg.in/urfave/cli.v1"
36+
)
37+
38+
var runCommand = cli.Command{
39+
Action: runCmd,
40+
Name: "run",
41+
Usage: "run arbitrary evm binary",
42+
ArgsUsage: "<code>",
43+
Description: `The run command runs arbitrary EVM code.`,
44+
}
45+
46+
func runCmd(ctx *cli.Context) error {
47+
glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false)))
48+
glogger.Verbosity(log.Lvl(ctx.GlobalInt(VerbosityFlag.Name)))
49+
log.Root().SetHandler(glogger)
50+
51+
var (
52+
db, _ = ethdb.NewMemDatabase()
53+
statedb, _ = state.New(common.Hash{}, db)
54+
sender = common.StringToAddress("sender")
55+
logger = vm.NewStructLogger(nil)
56+
tstart = time.Now()
57+
)
58+
statedb.CreateAccount(sender)
59+
60+
var (
61+
code []byte
62+
ret []byte
63+
err error
64+
)
65+
if ctx.GlobalString(CodeFlag.Name) != "" {
66+
code = common.Hex2Bytes(ctx.GlobalString(CodeFlag.Name))
67+
} else {
68+
var hexcode []byte
69+
if ctx.GlobalString(CodeFileFlag.Name) != "" {
70+
var err error
71+
hexcode, err = ioutil.ReadFile(ctx.GlobalString(CodeFileFlag.Name))
72+
if err != nil {
73+
fmt.Printf("Could not load code from file: %v\n", err)
74+
os.Exit(1)
75+
}
76+
} else {
77+
var err error
78+
hexcode, err = ioutil.ReadAll(os.Stdin)
79+
if err != nil {
80+
fmt.Printf("Could not load code from stdin: %v\n", err)
81+
os.Exit(1)
82+
}
83+
}
84+
code = common.Hex2Bytes(string(bytes.TrimRight(hexcode, "\n")))
85+
}
86+
87+
if ctx.GlobalBool(CreateFlag.Name) {
88+
input := append(code, common.Hex2Bytes(ctx.GlobalString(InputFlag.Name))...)
89+
ret, _, err = runtime.Create(input, &runtime.Config{
90+
Origin: sender,
91+
State: statedb,
92+
GasLimit: ctx.GlobalUint64(GasFlag.Name),
93+
GasPrice: utils.GlobalBig(ctx, PriceFlag.Name),
94+
Value: utils.GlobalBig(ctx, ValueFlag.Name),
95+
EVMConfig: vm.Config{
96+
Tracer: logger,
97+
Debug: ctx.GlobalBool(DebugFlag.Name),
98+
DisableGasMetering: ctx.GlobalBool(DisableGasMeteringFlag.Name),
99+
},
100+
})
101+
} else {
102+
receiver := common.StringToAddress("receiver")
103+
statedb.SetCode(receiver, code)
104+
105+
ret, err = runtime.Call(receiver, common.Hex2Bytes(ctx.GlobalString(InputFlag.Name)), &runtime.Config{
106+
Origin: sender,
107+
State: statedb,
108+
GasLimit: ctx.GlobalUint64(GasFlag.Name),
109+
GasPrice: utils.GlobalBig(ctx, PriceFlag.Name),
110+
Value: utils.GlobalBig(ctx, ValueFlag.Name),
111+
EVMConfig: vm.Config{
112+
Tracer: logger,
113+
Debug: ctx.GlobalBool(DebugFlag.Name),
114+
DisableGasMetering: ctx.GlobalBool(DisableGasMeteringFlag.Name),
115+
},
116+
})
117+
}
118+
vmdone := time.Since(tstart)
119+
120+
if ctx.GlobalBool(DumpFlag.Name) {
121+
statedb.Commit(true)
122+
fmt.Println(string(statedb.Dump()))
123+
}
124+
vm.StdErrFormat(logger.StructLogs())
125+
126+
if ctx.GlobalBool(SysStatFlag.Name) {
127+
var mem goruntime.MemStats
128+
goruntime.ReadMemStats(&mem)
129+
fmt.Printf("vm took %v\n", vmdone)
130+
fmt.Printf(`alloc: %d
131+
tot alloc: %d
132+
no. malloc: %d
133+
heap alloc: %d
134+
heap objs: %d
135+
num gc: %d
136+
`, mem.Alloc, mem.TotalAlloc, mem.Mallocs, mem.HeapAlloc, mem.HeapObjects, mem.NumGC)
137+
}
138+
139+
fmt.Printf("OUT: 0x%x", ret)
140+
if err != nil {
141+
fmt.Printf(" error: %v", err)
142+
}
143+
fmt.Println()
144+
return nil
145+
}

0 commit comments

Comments
 (0)