From 338b6980915c990c6e6287a7249ddd98e6be20eb Mon Sep 17 00:00:00 2001 From: obscuren Date: Sun, 27 Apr 2014 17:15:44 +0200 Subject: Refactoring and added documentation comments --- ethutil/asm.go | 193 ++++++++++++++++++++++++++++++++++++++++++++ ethutil/asm_test.go | 31 +++++++ ethutil/big.go | 31 ++++--- ethutil/bytes.go | 13 ++- ethutil/common.go | 12 ++- ethutil/config.go | 9 ++- ethutil/parsing.go | 210 ------------------------------------------------ ethutil/parsing_test.go | 31 ------- ethutil/rlp.go | 10 --- 9 files changed, 273 insertions(+), 267 deletions(-) create mode 100644 ethutil/asm.go create mode 100644 ethutil/asm_test.go delete mode 100644 ethutil/parsing.go delete mode 100644 ethutil/parsing_test.go 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 +} diff --git a/ethutil/asm_test.go b/ethutil/asm_test.go new file mode 100644 index 000000000..a9ad347dd --- /dev/null +++ b/ethutil/asm_test.go @@ -0,0 +1,31 @@ +package ethutil + +import ( + "fmt" + "testing" +) + +func TestPreProcess(t *testing.T) { + main, init := PreProcess(` + init { + // init + if a > b { + if { + } + } + } + + main { + // main + if a > b { + if c > d { + } + } + } + `) + + fmt.Println("main") + fmt.Println(main) + fmt.Println("init") + fmt.Println(init) +} diff --git a/ethutil/big.go b/ethutil/big.go index c0488a71f..891d476ad 100644 --- a/ethutil/big.go +++ b/ethutil/big.go @@ -12,7 +12,9 @@ var BigTrue *big.Int = big.NewInt(1) // False var BigFalse *big.Int = big.NewInt(0) -// Returns the power of two integers +// Big pow +// +// Returns the power of two big integers func BigPow(a, b int) *big.Int { c := new(big.Int) c.Exp(big.NewInt(int64(a)), big.NewInt(int64(b)), big.NewInt(0)) @@ -20,7 +22,9 @@ func BigPow(a, b int) *big.Int { return c } -// Like big.NewInt(uint64); this takes a string instead. +// Big +// +// Shortcut for new(big.Int).SetString(..., 0) func Big(num string) *big.Int { n := new(big.Int) n.SetString(num, 0) @@ -28,7 +32,9 @@ func Big(num string) *big.Int { return n } -// Like big.NewInt(uint64); this takes a byte buffer instead. +// BigD +// +// Shortcut for new(big.Int).SetBytes(...) func BigD(data []byte) *big.Int { n := new(big.Int) n.SetBytes(data) @@ -36,21 +42,26 @@ func BigD(data []byte) *big.Int { return n } +// Big to bytes +// +// Returns the bytes of a big integer with the size specified by **base** +// Attempts to pad the byte array with zeros. func BigToBytes(num *big.Int, base int) []byte { ret := make([]byte, base/8) return append(ret[:len(ret)-len(num.Bytes())], num.Bytes()...) } -// Functions like the build in "copy" function -// but works on big integers -func BigCopy(src *big.Int) (ret *big.Int) { - ret = new(big.Int) - ret.Add(ret, src) - - return +// Big copy +// +// Creates a copy of the given big integer +func BigCopy(src *big.Int) *big.Int { + return new(big.Int).Set(src) } +// Big max +// +// Returns the maximum size big integer func BigMax(x, y *big.Int) *big.Int { if x.Cmp(y) <= 0 { return x diff --git a/ethutil/bytes.go b/ethutil/bytes.go index 40903a5f1..957fa254a 100644 --- a/ethutil/bytes.go +++ b/ethutil/bytes.go @@ -6,6 +6,9 @@ import ( "fmt" ) +// Number to bytes +// +// Returns the number in bytes with the specified base func NumberToBytes(num interface{}, bits int) []byte { buf := new(bytes.Buffer) err := binary.Write(buf, binary.BigEndian, num) @@ -16,6 +19,9 @@ func NumberToBytes(num interface{}, bits int) []byte { return buf.Bytes()[buf.Len()-(bits/8):] } +// Bytes to number +// +// Attempts to cast a byte slice to a unsigned integer func BytesToNumber(b []byte) uint64 { var number uint64 @@ -32,7 +38,9 @@ func BytesToNumber(b []byte) uint64 { return number } -// Read variable integer in big endian +// Read variable int +// +// Read a variable length number in big endian byte order func ReadVarint(reader *bytes.Reader) (ret uint64) { if reader.Len() == 8 { var num uint64 @@ -55,6 +63,9 @@ func ReadVarint(reader *bytes.Reader) (ret uint64) { return ret } +// Binary length +// +// Returns the true binary length of the given number func BinaryLength(num int) int { if num == 0 { return 0 diff --git a/ethutil/common.go b/ethutil/common.go index d0ee7b538..983ea5d1b 100644 --- a/ethutil/common.go +++ b/ethutil/common.go @@ -5,16 +5,20 @@ import ( "math/big" ) +// The different number of units var ( Ether = BigPow(10, 18) Finney = BigPow(10, 15) Szabo = BigPow(10, 12) - Vito = BigPow(10, 9) + Vita = BigPow(10, 9) Turing = BigPow(10, 6) Eins = BigPow(10, 3) Wei = big.NewInt(1) ) +// Currency to string +// +// Returns a string representing a human readable format func CurrencyToString(num *big.Int) string { switch { case num.Cmp(Ether) >= 0: @@ -23,8 +27,8 @@ func CurrencyToString(num *big.Int) string { return fmt.Sprintf("%v Finney", new(big.Int).Div(num, Finney)) case num.Cmp(Szabo) >= 0: return fmt.Sprintf("%v Szabo", new(big.Int).Div(num, Szabo)) - case num.Cmp(Vito) >= 0: - return fmt.Sprintf("%v Vito", new(big.Int).Div(num, Vito)) + case num.Cmp(Vita) >= 0: + return fmt.Sprintf("%v Vita", new(big.Int).Div(num, Vita)) case num.Cmp(Turing) >= 0: return fmt.Sprintf("%v Turing", new(big.Int).Div(num, Turing)) case num.Cmp(Eins) >= 0: @@ -34,6 +38,7 @@ func CurrencyToString(num *big.Int) string { return fmt.Sprintf("%v Wei", num) } +// Common big integers often used var ( Big1 = big.NewInt(1) Big2 = big.NewInt(1) @@ -42,6 +47,7 @@ var ( Big256 = big.NewInt(0xff) ) +// Creates an ethereum address given the bytes and the nonce func CreateAddress(b []byte, nonce *big.Int) []byte { addrBytes := append(b, nonce.Bytes()...) diff --git a/ethutil/config.go b/ethutil/config.go index 86c0a855d..323773ba7 100644 --- a/ethutil/config.go +++ b/ethutil/config.go @@ -9,6 +9,7 @@ import ( "runtime" ) +// Log types available type LogType byte const ( @@ -16,7 +17,7 @@ const ( LogTypeFile = 2 ) -// Config struct isn't exposed +// Config struct type config struct { Db Database @@ -31,7 +32,9 @@ type config struct { var Config *config -// Read config doesn't read anything yet. +// Read config +// +// Initialize the global Config variable with default settings func ReadConfig(base string) *config { if Config == nil { usr, _ := user.Current() @@ -56,6 +59,8 @@ func ReadConfig(base string) *config { return Config } +// Set client string +// func (c *config) SetClientString(str string) { Config.ClientString = fmt.Sprintf("%s nv%s/%s", str, c.Ver, runtime.GOOS) } diff --git a/ethutil/parsing.go b/ethutil/parsing.go deleted file mode 100644 index 514fd92cd..000000000 --- a/ethutil/parsing.go +++ /dev/null @@ -1,210 +0,0 @@ -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, -} - -func IsOpCode(s string) bool { - for key, _ := range OpCodes { - if key == s { - return true - } - } - return false -} - -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 -} - -// Script compilation functions -// Compiles strings to machine 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 -} - -/* -Prepocessing function that takes init and main apart: -init() { - // something -} - -main() { - // main something -} -func PreProcess(data string) (mainInput, initInput string) { - reg := "\\(\\)\\s*{([\\d\\w\\W\\n\\s]+?)}" - mainReg := regexp.MustCompile("main" + reg) - initReg := regexp.MustCompile("init" + reg) - - main := mainReg.FindStringSubmatch(data) - if len(main) > 0 { - mainInput = main[1] - } else { - mainInput = data - } - - init := initReg.FindStringSubmatch(data) - if len(init) > 0 { - initInput = init[1] - } - - return -} -*/ - -// Very, very dumb parser. Heed no attention :-) -func FindFor(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 -} - -func PreProcess(data string) (mainInput, initInput string) { - mainInput = FindFor("main", data) - if mainInput == "" { - mainInput = data - } - initInput = FindFor("init", data) - - return -} diff --git a/ethutil/parsing_test.go b/ethutil/parsing_test.go deleted file mode 100644 index a9ad347dd..000000000 --- a/ethutil/parsing_test.go +++ /dev/null @@ -1,31 +0,0 @@ -package ethutil - -import ( - "fmt" - "testing" -) - -func TestPreProcess(t *testing.T) { - main, init := PreProcess(` - init { - // init - if a > b { - if { - } - } - } - - main { - // main - if a > b { - if c > d { - } - } - } - `) - - fmt.Println("main") - fmt.Println(main) - fmt.Println("init") - fmt.Println(init) -} diff --git a/ethutil/rlp.go b/ethutil/rlp.go index d95ace425..69f80a0a6 100644 --- a/ethutil/rlp.go +++ b/ethutil/rlp.go @@ -26,16 +26,6 @@ func (coder *RlpEncoder) EncodeData(rlpData interface{}) []byte { return Encode(rlpData) } -/* -func FromBin(data []byte) uint64 { - if len(data) == 0 { - return 0 - } - - return FromBin(data[:len(data)-1])*256 + uint64(data[len(data)-1]) -} -*/ - const ( RlpEmptyList = 0x80 RlpEmptyStr = 0x40 -- cgit