Skip to content

Commit

Permalink
SpecOps CLI w/ template code and instructions for getting started (#21)
Browse files Browse the repository at this point in the history
* feat: `specopsdev` package and `getting-started` template

* refactor: rename `specopsdev.RunCLI()` to `specopscli.Run()`

* doc: README for getting started

* doc: short section on getting started in root README

* doc: remove Go reference in high-level description as it's irrelevant

* doc: top-level README re learning Go + include repo cloning in getting-started

* doc: clean up "Hello world" example and reference equivalent CLI code

* doc: information about first run of CLI

* doc: varied
  • Loading branch information
aschlosberg authored Mar 5, 2024
1 parent 6bb70ac commit dccf452
Show file tree
Hide file tree
Showing 11 changed files with 299 additions and 14 deletions.
1 change: 1 addition & 0 deletions MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ use_repo(
"com_github_google_go_cmp",
"com_github_holiman_uint256",
"com_github_rivo_tview",
"com_github_spf13_cobra",
)

go_deps.module_override(
Expand Down
65 changes: 59 additions & 6 deletions MODULE.bazel.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 14 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# SpecOps [![Go](https://github.com/solidifylabs/specops/actions/workflows/go.yml/badge.svg)](https://github.com/solidifylabs/specops/actions/workflows/go.yml) [![Go Reference](https://pkg.go.dev/badge/github.com/solidifylabs/specops.svg)](https://pkg.go.dev/github.com/solidifylabs/specops)

> `specops` is a low-level, domain-specific language (and compiler) for crafting [Ethereum VM](https://ethereum.org/en/developers/docs/evm) bytecode in Go.
> `specops` is a low-level, domain-specific language (and compiler) for crafting [Ethereum VM](https://ethereum.org/en/developers/docs/evm) bytecode.
This is a _very_ early release. In fact, it's just a weekend project gone rogue
so is less than a week old.
Expand All @@ -18,6 +18,16 @@ converting them to
equivalents, while others are simply compiler hints that leave the resulting
bytecode unchanged.

## Getting started

See the [`getting-started/`](https://github.com/solidifylabs/specops/tree/main/getting-started) directory for creating your first SpecOps code. Also check out the [examples](#other-examples) and the [documentation](#documentation).

### Do I have to learn Go?

> TL;DR You don't
There's more about this in the `getting-started/` README, including the rationale for a Go-based DSL.

## Features

- [x] `JUMPDEST` labels (absolute)
Expand Down Expand Up @@ -54,11 +64,7 @@ functionality.

### Hello world

TODO: link to Go playground; for now, here's the [real implementation](https://github.com/solidifylabs/specops/blob/41efe932c9a85e45ce705b231577447e6c944487/examples_test.go#L12).

The `specops` Go package has a minimal footprint to allow for dot-importing,
making all exported symbols available. TODO: expand on the implications,
rationale, and recommendations as this goes against the style guide.
To run this example `Code` block with the SpecOps CLI, see the `getting-started/` directory.

```go
import . github.com/solidifylabs/specops
Expand Down Expand Up @@ -111,6 +117,8 @@ result, err := results()

### Debugger

Key bindings are described in the `getting-started/` README.

![image](https://github.com/solidifylabs/specops/assets/519948/5057ad0f-bb6f-438b-a295-8b1f410d2330)

## Acknowledgements
Expand Down
6 changes: 4 additions & 2 deletions evmdebug/ui.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,8 +187,10 @@ func (t *termDBG) inputCapture(ev *tcell.EventKey) *tcell.EventKey {
}
} // switch ev.Rune()

t.populateStack()
t.populateMemory()
if t.State().ScopeContext != nil {
t.populateStack()
t.populateMemory()
}

if propagate {
return ev
Expand Down
27 changes: 27 additions & 0 deletions getting-started/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# This file can be ignored (and even deleted) when developing in SpecOps. It is
# a configuration file for a build system called Bazel (https://bazel.build/)
# that is used for the development of SpecOps itself.
#
# Should you happen to know what Bazel is, all `go run getting-started.spec.go`
# commands in the README can be replaced with `bazel run . --`.

load("@rules_go//go:def.bzl", "go_binary", "go_library")

go_library(
name = "getting-started_lib",
srcs = ["getting-started.spec.go"],
importpath = "github.com/solidifylabs/specops/getting-started",
visibility = ["//visibility:private"],
deps = [
"//:specops",
"//specopscli",
"//stack",
"@com_github_ethereum_go_ethereum//common",
],
)

go_binary(
name = "getting-started",
embed = [":getting-started_lib"],
visibility = ["//visibility:public"],
)
58 changes: 58 additions & 0 deletions getting-started/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Getting started with SpecOps

1. [Install Go](https://go.dev/doc/install)
2. Clone the SpecOps repo:

```shell
git clone https://github.com/solidifylabs/specops.git
```

3. From the `getting-started` directory:

```shell
go run getting-started.spec.go compile
```

This will print the compiled EVM bytecode to `stdout`. The first time you run it you may see some logs about fetching dependencies, but from then on it will only output the compiled contract.

## Development

The `getting-started.spec.go` file contains everything you need to be productive.
If this is your first time using Go, stick between the `START/STOP EDITING HERE` comments and everything will work.

### Do I have to learn Go?

> TL;DR You don't
SpecOps is a DSL built and run in Go, but designed so that it reads and is written like a standalone language.
The advantage of piggybacking on the Go toolchain is that we get all of the developer tooling out of the box: syntax highlighting, code completion, etc.
For more experienced Go developers, there is also support for native testing, interoperability with geth, etc.

A standalone language inside another?
In Go, all functions, types, etc. from external packages are *usually* referenced by their package name.
There is, however, the ability to "dot-import" a package, promoting these symbols such that the package-qualification is unnecessary.
`specops.Fn` becomes `Fn`, `specops.MSTORE` becomes `MSTORE`, etc. While this goes against the Go style guide, for a DSL it makes sense as it greatly improves developer experience.

## Other CLI usage

### Commands

The CLI has `compile`, `exec`, and `debug` commands. The `-h` or `--help` flag
will provide more information about each (for now, quite limited).

### calldata

Both the `exec` and `debug` commands support the `--calldata` flag, which accepts hex-encoded calldata (*without* the `0x` prefix). For example:

```shell
go run getting-started.spec.go debug --calldata decafc0ffeebad
```

### Debugger

* `<space>` Step to next instruction
* `<end>` Fast-forward to the end of execution
* `<Esc>` or `q` Once execution has ended, quit
* `Ctrl+C` At any time, quit

![image](https://github.com/solidifylabs/specops/assets/519948/5057ad0f-bb6f-438b-a295-8b1f410d2330)
36 changes: 36 additions & 0 deletions getting-started/getting-started.spec.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package main

import (
"github.com/ethereum/go-ethereum/common"

. "github.com/solidifylabs/specops" //lint:ignore ST1001 SpecOps DSL is designed to be dot-imported
"github.com/solidifylabs/specops/specopscli"
"github.com/solidifylabs/specops/stack"
)

func code() Code {
// ----------------------------------------
// ========== START EDITING HERE ==========
// vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv

hello := []byte("Hello, world!")

return Code{
Fn(MSTORE, PUSH0, PUSH(hello)),
Fn(RETURN, PUSH(32-len(hello)), PUSH(len(hello))),
}

// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// ========== STOP EDITING HERE ===========
// ----------------------------------------
}

func main() {
specopscli.Run(code())
}

// Stop unused imports being removed.
var (
_ = stack.ExpectDepth(0)
_ = common.Address{}
)
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ require (
github.com/google/go-cmp v0.5.9
github.com/holiman/uint256 v1.2.4
github.com/rivo/tview v0.0.0-20240225120200-5605142ca62e
github.com/spf13/cobra v1.5.0
)

require (
Expand All @@ -19,10 +20,12 @@ require (
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
github.com/ethereum/c-kzg-4844 v0.4.0 // indirect
github.com/gdamore/encoding v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/mmcloughlin/addchain v0.4.0 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/supranational/blst v0.3.11 // indirect
golang.org/x/crypto v0.17.0 // indirect
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect
Expand Down
10 changes: 10 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/Yj
github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI=
github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M=
github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 h1:d28BXYi+wUpz1KBmiF9bWrjEMacUEREV6MBi2ODnrfQ=
github.com/crate-crypto/go-kzg-4844 v0.7.0 h1:C0vgZRk4q4EZ/JgPfzuSoxdCq3C3mOZMBShovmncxvA=
github.com/crate-crypto/go-kzg-4844 v0.7.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc=
Expand Down Expand Up @@ -46,6 +47,8 @@ github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3
github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao=
github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU=
github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
Expand Down Expand Up @@ -73,7 +76,12 @@ github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUc
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU=
github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU=
github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4=
github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw=
Expand Down Expand Up @@ -123,7 +131,9 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU=
rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA=
Loading

0 comments on commit dccf452

Please sign in to comment.