diff options
Diffstat (limited to 'ethutil/asm.go')
-rw-r--r-- | ethutil/asm.go | 193 |
1 files changed, 193 insertions, 0 deletions
diff --git a/ethutil/asm.go b/ethutil/asm.go new file mode 100644 index 000000000..a547d3ac1 --- /dev/null +++ b/ethutil/asm.go @@ -0,0 +1,193 @@ +package ethutil + +import ( + _ "fmt" + "math/big" + _ "regexp" +) + +// Op codes +var OpCodes = map[string]byte{ + // 0x0 range - arithmetic ops + "STOP": 0x00, + "ADD": 0x01, + "MUL": 0x02, + "SUB": 0x03, + "DIV": 0x04, + "SDIV": 0x05, + "MOD": 0x06, + "SMOD": 0x07, + "EXP": 0x08, + "NEG": 0x09, + "LT": 0x0a, + "GT": 0x0b, + "EQ": 0x0c, + "NOT": 0x0d, + + // 0x10 range - bit ops + "AND": 0x10, + "OR": 0x11, + "XOR": 0x12, + "BYTE": 0x13, + + // 0x20 range - crypto + "SHA3": 0x20, + + // 0x30 range - closure state + "ADDRESS": 0x30, + "BALANCE": 0x31, + "ORIGIN": 0x32, + "CALLER": 0x33, + "CALLVALUE": 0x34, + "CALLDATALOAD": 0x35, + "CALLDATASIZE": 0x36, + "GASPRICE": 0x38, + + // 0x40 range - block operations + "PREVHASH": 0x40, + "COINBASE": 0x41, + "TIMESTAMP": 0x42, + "NUMBER": 0x43, + "DIFFICULTY": 0x44, + "GASLIMIT": 0x45, + + // 0x50 range - 'storage' and execution + "PUSH": 0x50, + + "PUSH20": 0x80, + + "POP": 0x51, + "DUP": 0x52, + "SWAP": 0x53, + "MLOAD": 0x54, + "MSTORE": 0x55, + "MSTORE8": 0x56, + "SLOAD": 0x57, + "SSTORE": 0x58, + "JUMP": 0x59, + "JUMPI": 0x5a, + "PC": 0x5b, + "MSIZE": 0x5c, + + // 0x60 range - closures + "CREATE": 0x60, + "CALL": 0x61, + "RETURN": 0x62, + + // 0x70 range - other + "LOG": 0x70, + "SUICIDE": 0x7f, +} + +// Is op code +// +// Check whether the given string matches anything in +// the OpCode list +func IsOpCode(s string) bool { + for key, _ := range OpCodes { + if key == s { + return true + } + } + return false +} + +// Compile instruction +// +// Attempts to compile and parse the given instruction in "s" +// and returns the byte sequence +func CompileInstr(s interface{}) ([]byte, error) { + switch s.(type) { + case string: + str := s.(string) + isOp := IsOpCode(str) + if isOp { + return []byte{OpCodes[str]}, nil + } + + num := new(big.Int) + _, success := num.SetString(str, 0) + // Assume regular bytes during compilation + if !success { + num.SetBytes([]byte(str)) + } else { + // tmp fix for 32 bytes + n := BigToBytes(num, 256) + return n, nil + } + + return num.Bytes(), nil + case int: + num := BigToBytes(big.NewInt(int64(s.(int))), 256) + return num, nil + case []byte: + return BigD(s.([]byte)).Bytes(), nil + } + + return nil, nil +} + +// Assemble +// +// Assembles the given instructions and returns EVM byte code +func Assemble(instructions ...interface{}) (script []byte) { + //script = make([]string, len(instructions)) + + for _, val := range instructions { + instr, _ := CompileInstr(val) + + //script[i] = string(instr) + script = append(script, instr...) + } + + return +} + +// Pre process script +// +// Take data apart and attempt to find the "init" section and +// "main" section. `main { } init { }` +func PreProcess(data string) (mainInput, initInput string) { + mainInput = getCodeSectionFor("main", data) + if mainInput == "" { + mainInput = data + } + initInput = getCodeSectionFor("init", data) + + return +} + +// Very, very dumb parser. Heed no attention :-) +func getCodeSectionFor(blockMatcher, input string) string { + curCount := -1 + length := len(blockMatcher) + matchfst := rune(blockMatcher[0]) + var currStr string + + for i, run := range input { + // Find init + if curCount == -1 && run == matchfst && input[i:i+length] == blockMatcher { + curCount = 0 + } else if curCount > -1 { + if run == '{' { + curCount++ + if curCount == 1 { + continue + } + } else if run == '}' { + curCount-- + if curCount == 0 { + // we are done + curCount = -1 + break + } + } + + if curCount > 0 { + currStr += string(run) + } + } + } + + return currStr +} |