EVM (Ethereum virtual machine) Assembly on Haskell DSL
Clone or download

README.md

HAssembly-evm

EVM (Ethereum virtual machine) Assembly on Haskell DSL

This is a bytecode generator from EVM assembly on Haskell DSL.

Warning:

  • A big WIP
  • No guarantee and under testing
  • Experimental and conceptual project

Feature:

  • Composable assembly
  • User definable functions
  • Purely functional implementation
  • Simple and slow implementation for readability
  • Only a few dependent libraries and GHC extensions

Limitation:

  • Weak error detection and cheap error implementation
  • Jump size is limited to 2 bytes

Run

Command:

GHC:

  • $ runghc -isrc app/Main.hs

Stack:

  • $ stack runghc -- -isrc app/Main.hs or
  • $ stack build; stack exec hassembly-evm

Cabal:

  • $ cabal run

Example:

$ runghc -isrc app/Main.hs
601060200100

Code example

Basic:

main :: IO ()
main = putStrLn $ codegen prog1

prog1 :: EvmAsm
prog1 = do
    push1 0x10
    push1 0x20
    add

Composable:

prog2 :: EvmAsm
prog2 = do
    prog2a
    prog2b

prog2a = do
    push1 0x40
    mload

prog2b = do
    push1 0x20
    add

Pseudo instruction and built-in function:

  • _dest : pseudo instruction for jump destination (_jump and _jumpi)
  • _label : pseudo instruction for push with symbol (_pushlabel)
  • _jump and _jumpi : jump instruction with symbol
  • _pushlabel : push instruction with symbol
  • _push : push instruction with automatic length adjustment
  • _raw : pseudo instruction for raw byte
  • _progSize : built-in function for program size
  • _genUniqLabel : built-in function to generate unique label
prog3 :: EvmAsm
prog3 = do
    push1 0x60
    push1 0x40
    mstore
    _jump "target2"            -- symbol jump

    push1 (_progSize prog4)    -- program size
    _pushlabel "top1"          -- push label
    _dest "target2"            -- jump target


prog4 :: EvmAsm
prog4 = do
    _label "top1"              -- label
    _push 0x11234456788        -- multi length push
    _raw 0x7                   -- raw byte
    _raw 0x8

Using host language (Haskell):

prog5 = do
    if isBizantinum          -- if expression on Haskell
        then prog_header1
        else prog_header2
    push1 0x60
    push1 0x40
    mstore

User defined functions and syntax:

Example1:

shiftL nbit = do    -- user defined function
    push1 nbit
    push1 2
    exp
    mul

prog6 = do
    mload
    shiftL 16    -- using
    push1 0x1
    add

    string "OK" >> log1

Example2: (examples/userSyntax.hs)

prog7 = do
    let num1 = const 0x10      -- let syntax
    let num2 = const 0x20
    let factor1 = ptr 0x05     -- memory type
    let user1   = key 0x100    -- storage type

    add2(num1, num2)           -- functional syntax
    add2(ref factor1, num1)
    add
    add2(value user1, const 3)
    memory(factor1) <== mul    -- assignment syntax

    storage(user1) <== mul2(num2, (add2(const 4, ref factor1)))

Example3: (examples/userFlow.hs)

prog8 = do
    _if (iszero) (do -- then
          push1 0x01
          push1 0x02
          add
      ) (do -- else
          push1 0x01
          push1 0x02
          sub
      )

Execute bytecode

If you need to execute a generated bytecode, please use the evm command of go-ethereum project or some tools.

Execute bytecode by evm command:

$ evm --code "601060200100" --debug run

Disasemble bytecode by evm command:

$ evm disasm sample.bytecode

Listing and pretty printing

Simple listing:

Use pprList function:

main :: IO ()
main = putStrLn $ pprList prog1   -- using `pprList`

prog1 :: EvmAsm
prog1 = do ...

Output example:

$ runghc -isrc examples/pprList.hs
000000: push1 0x10
000002: push1 0x20
000004: add
000005: stop

Under implementation ...

Pretty-print for Solidity:

$ runghc -isrc examples/PprSol.hs
{
    0x10
    0x20
    add
    stop
    pop
}

Stack hight checking:

$ runghc -isrc examples/StackCheck.hs
Final stack-hight: 0

See also