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
GHC:
$ runghc -isrc app/Main.hs
Stack:
$ stack runghc -- -isrc app/Main.hs
or$ stack build; stack exec hassembly-evm
Cabal:
$ cabal run
$ runghc -isrc app/Main.hs
601060200100
main :: IO ()
main = putStrLn $ codegen prog1
prog1 :: EvmAsm
prog1 = do
push1 0x10
push1 0x20
add
prog2 :: EvmAsm
prog2 = do
prog2a
prog2b
prog2a = do
push1 0x40
mload
prog2b = do
push1 0x20
add
_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
prog5 = do
if isBizantinum -- if expression on Haskell
then prog_header1
else prog_header2
push1 0x60
push1 0x40
mstore
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
)
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
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
$ runghc -isrc examples/PprSol.hs
{
0x10
0x20
add
stop
pop
}
$ runghc -isrc examples/StackCheck.hs
Final stack-hight: 0