Skip to content
/ specops Public

SpecOps is a low-level, domain-specific language and compiler for crafting Ethereum VM bytecode. The project also includes a CLI with code execution and terminal-based debugger.

License

Notifications You must be signed in to change notification settings

ARR4N/specops

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

36 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

SpecOps Go Go Reference

specops is a low-level, domain-specific language and compiler for crafting Ethereum VM bytecode. The project also includes a CLI with code execution and terminal-based debugger.

special opcodes

Writing bytecode is hard. Tracking stack items is difficult enough, made worse by refactoring that renders every DUP and SWAP off-by-X. Reverse Polish Notation may be suited to stack-based programming, but it's unintuitive when context-switching from Solidity.

There's always a temptation to give up and use a higher-level language with all of its conveniences, but that defeats the point. What if we could maintain full control of the opcode placement, but with syntactic sugar to help the medicine go down?

Special opcodes provide just that. Some of them are interpreted by the compiler, converting them into regular equivalents, while others are simply compiler hints that leave the resulting bytecode unchanged.

Getting started

See the getting-started/ directory for creating your first SpecOps code. Also check out the examples and the documentation.

Do I have to learn Go?

No.

There's more about this in the getting-started/ README, including the rationale for a Go-based DSL.

Features

New features will be prioritised based on demand. If there's something you'd like included, please file an Issue.

  • JUMPDEST labels (absolute)
  • JUMPDEST labels (relative to PC)
  • PUSH(JUMPDEST) by label with minimal bytes (1 or 2)
  • Label tags; like JUMPDEST but don't add to code
  • Push multiple, concatenated JUMPDEST / Label tags as one word
  • PUSHSize(T,T) pushes Label and/or JUMPDEST distance
  • Function-like syntax (i.e. Reverse Polish Notation is optional)
  • Inverted DUP/SWAP special opcodes from "bottom" of stack (a.k.a. pseudo-variables)
  • PUSH<T> for native Go types
  • PUSH(v) length detection
  • Macros
  • Compiler-state assertions (e.g. expected stack depth)
  • Automated optimal (least-gas) stack transformations
    • Permutations (SWAP-only transforms)
    • General-purpose (combined DUP + SWAP + POP)
    • Caching of search for optimal route
  • Standalone compiler
  • In-process EVM execution (geth)
    • Full control of configuration (e.g. params.ChainConfig and vm.Config)
    • State preloading (e.g. other contracts to call) and inspection (e.g. SSTORE testing)
    • Message overrides (caller and value)
  • Debugger
    • Stepping
    • Breakpoints
    • Programmatic inspection (e.g. native Go tests at opcode resolution)
      • Memory
      • Stack
    • User interface
  • Source mapping
  • Coverage analysis
  • Fork testing with RPC URL

Documentation

The specops Go documentation covers all functionality.

Examples

Hello world

To run this example Code block with the SpecOps CLI, see the getting-started/ directory.

import . github.com/arr4n/specopshello := []byte("Hello world")
code := Code{
    // The compiler determines the shortest-possible PUSH<n> opcode.
    // Fn() simply reverses its arguments (a surprisingly powerful construct)!
    Fn(MSTORE, PUSH0, PUSH(hello)),
    Fn(RETURN, PUSH(32-len(hello)), PUSH(len(hello))),
}

// ----- COMPILE -----
bytecode, err := code.Compile()
// ...

// ----- EXECUTE -----

result, err := code.Run(nil /*callData*/ /*, [runopts.Options]...*/)
// ...

// ----- DEBUG (Programmatic) -----
//
// ***** See below for the debugger's terminal UI *****
//

dbg, results := code.StartDebugging(nil /*callData*/ /*, Options...*/)
defer dbg.FastForward() // best practice to avoid resource leaks

state := dbg.State() // is updated on calls to Step() / FastForward()

for !dbg.Done() {
  dbg.Step()
  fmt.Println("Peek-a-boo", state.ScopeContext.Stack().Back(0))
}

result, err := results()
//...

Other examples

Debugger

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

image

Acknowledgements

Some of SpecOps was, of course, inspired by Huff. I hope to provide something different, of value, and to inspire them too.

About

SpecOps is a low-level, domain-specific language and compiler for crafting Ethereum VM bytecode. The project also includes a CLI with code execution and terminal-based debugger.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published