EVM (Ethereum virtual machine) Assembly on Haskell DSL
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
* Ethereum Yellow Paper * Go Ethereum * The Solidity Contract-Oriented Programming Language * Ethereum EVM illustrated