diff options
Diffstat (limited to 'ethutil')
-rw-r--r-- | ethutil/bytes.go | 77 | ||||
-rw-r--r-- | ethutil/bytes_test.go | 14 | ||||
-rw-r--r-- | ethutil/common.go | 35 | ||||
-rw-r--r-- | ethutil/config.go | 6 | ||||
-rw-r--r-- | ethutil/path.go | 40 | ||||
-rw-r--r-- | ethutil/reactor.go | 87 | ||||
-rw-r--r-- | ethutil/reactor_test.go | 30 | ||||
-rw-r--r-- | ethutil/rlp.go | 153 | ||||
-rw-r--r-- | ethutil/rlp_test.go | 11 | ||||
-rw-r--r-- | ethutil/value.go | 103 | ||||
-rw-r--r-- | ethutil/value_test.go | 15 |
11 files changed, 352 insertions, 219 deletions
diff --git a/ethutil/bytes.go b/ethutil/bytes.go index 34fff7d42..e38f89454 100644 --- a/ethutil/bytes.go +++ b/ethutil/bytes.go @@ -44,26 +44,28 @@ func BytesToNumber(b []byte) uint64 { // 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 - binary.Read(reader, binary.BigEndian, &num) - ret = uint64(num) - } else if reader.Len() == 4 { +func ReadVarInt(buff []byte) (ret uint64) { + switch l := len(buff); { + case l > 4: + d := LeftPadBytes(buff, 8) + binary.Read(bytes.NewReader(d), binary.BigEndian, &ret) + case l > 2: var num uint32 - binary.Read(reader, binary.BigEndian, &num) + d := LeftPadBytes(buff, 4) + binary.Read(bytes.NewReader(d), binary.BigEndian, &num) ret = uint64(num) - } else if reader.Len() == 2 { + case l > 1: var num uint16 - binary.Read(reader, binary.BigEndian, &num) + d := LeftPadBytes(buff, 2) + binary.Read(bytes.NewReader(d), binary.BigEndian, &num) ret = uint64(num) - } else { + default: var num uint8 - binary.Read(reader, binary.BigEndian, &num) + binary.Read(bytes.NewReader(buff), binary.BigEndian, &num) ret = uint64(num) } - return ret + return } // Binary length @@ -98,6 +100,7 @@ func Bytes2Hex(d []byte) string { func Hex2Bytes(str string) []byte { h, _ := hex.DecodeString(str) + return h } @@ -128,6 +131,26 @@ func FormatData(data string) []byte { return BigToBytes(d, 256) } +func ParseData(data ...interface{}) (ret []byte) { + for _, item := range data { + switch t := item.(type) { + case string: + var str []byte + if IsHex(t) { + str = Hex2Bytes(t[2:]) + } else { + str = []byte(t) + } + + ret = append(ret, RightPadBytes(str, 32)...) + case []byte: + ret = append(ret, LeftPadBytes(t, 32)...) + } + } + + return +} + func RightPadBytes(slice []byte, l int) []byte { if l < len(slice) { return slice @@ -150,6 +173,28 @@ func LeftPadBytes(slice []byte, l int) []byte { return padded } +func LeftPadString(str string, l int) string { + if l < len(str) { + return str + } + + zeros := Bytes2Hex(make([]byte, (l-len(str))/2)) + + return zeros + str + +} + +func RightPadString(str string, l int) string { + if l < len(str) { + return str + } + + zeros := Bytes2Hex(make([]byte, (l-len(str))/2)) + + return str + zeros + +} + func Address(slice []byte) (addr []byte) { if len(slice) < 20 { addr = LeftPadBytes(slice, 20) @@ -163,3 +208,11 @@ func Address(slice []byte) (addr []byte) { return } + +func ByteSliceToInterface(slice [][]byte) (ret []interface{}) { + for _, i := range slice { + ret = append(ret, i) + } + + return +} diff --git a/ethutil/bytes_test.go b/ethutil/bytes_test.go new file mode 100644 index 000000000..381efe7a2 --- /dev/null +++ b/ethutil/bytes_test.go @@ -0,0 +1,14 @@ +package ethutil + +import ( + "bytes" + "testing" +) + +func TestParseData(t *testing.T) { + data := ParseData("hello", "world", "0x0106") + exp := "68656c6c6f000000000000000000000000000000000000000000000000000000776f726c640000000000000000000000000000000000000000000000000000000106000000000000000000000000000000000000000000000000000000000000" + if bytes.Compare(data, Hex2Bytes(exp)) != 0 { + t.Error("Error parsing data") + } +} diff --git a/ethutil/common.go b/ethutil/common.go index cbd94e7ad..8532d80f2 100644 --- a/ethutil/common.go +++ b/ethutil/common.go @@ -34,26 +34,43 @@ var ( // Currency to string // Returns a string representing a human readable format func CurrencyToString(num *big.Int) string { + var ( + fin *big.Int = num + denom string = "Wei" + ) + switch { case num.Cmp(Douglas) >= 0: - return fmt.Sprintf("%v Douglas", new(big.Int).Div(num, Douglas)) + fin = new(big.Int).Div(num, Douglas) + denom = "Douglas" case num.Cmp(Einstein) >= 0: - return fmt.Sprintf("%v Einstein", new(big.Int).Div(num, Einstein)) + fin = new(big.Int).Div(num, Einstein) + denom = "Einstein" case num.Cmp(Ether) >= 0: - return fmt.Sprintf("%v Ether", new(big.Int).Div(num, Ether)) + fin = new(big.Int).Div(num, Ether) + denom = "Ether" case num.Cmp(Finney) >= 0: - return fmt.Sprintf("%v Finney", new(big.Int).Div(num, Finney)) + fin = new(big.Int).Div(num, Finney) + denom = "Finney" case num.Cmp(Szabo) >= 0: - return fmt.Sprintf("%v Szabo", new(big.Int).Div(num, Szabo)) + fin = new(big.Int).Div(num, Szabo) + denom = "Szabo" case num.Cmp(Shannon) >= 0: - return fmt.Sprintf("%v Shannon", new(big.Int).Div(num, Shannon)) + fin = new(big.Int).Div(num, Shannon) + denom = "Shannon" case num.Cmp(Babbage) >= 0: - return fmt.Sprintf("%v Babbage", new(big.Int).Div(num, Babbage)) + fin = new(big.Int).Div(num, Babbage) + denom = "Babbage" case num.Cmp(Ada) >= 0: - return fmt.Sprintf("%v Ada", new(big.Int).Div(num, Ada)) + fin = new(big.Int).Div(num, Ada) + denom = "Ada" + } + + if len(fin.String()) > 5 { + return fmt.Sprintf("%sE%d %s", fin.String()[0:5], len(fin.String())-5, denom) } - return fmt.Sprintf("%v Wei", num) + return fmt.Sprintf("%v %s", fin, denom) } // Common big integers often used diff --git a/ethutil/config.go b/ethutil/config.go index 41bece21d..81052318e 100644 --- a/ethutil/config.go +++ b/ethutil/config.go @@ -3,8 +3,9 @@ package ethutil import ( "flag" "fmt" - "github.com/rakyll/globalconf" "os" + + "github.com/rakyll/globalconf" ) // Config struct @@ -28,8 +29,7 @@ var Config *ConfigManager func ReadConfig(ConfigFile string, Datadir string, EnvPrefix string) *ConfigManager { if Config == nil { // create ConfigFile if does not exist, otherwise globalconf panic when trying to persist flags - _, err := os.Stat(ConfigFile) - if err != nil && os.IsNotExist(err) { + if !FileExist(ConfigFile) { fmt.Printf("config file '%s' doesn't exist, creating it\n", ConfigFile) os.Create(ConfigFile) } diff --git a/ethutil/path.go b/ethutil/path.go index 97f58ab7e..27022bcfa 100644 --- a/ethutil/path.go +++ b/ethutil/path.go @@ -1,6 +1,8 @@ package ethutil import ( + "io/ioutil" + "os" "os/user" "strings" ) @@ -18,3 +20,41 @@ func ExpandHomePath(p string) (path string) { return } + +func FileExist(filePath string) bool { + _, err := os.Stat(filePath) + if err != nil && os.IsNotExist(err) { + return false + } + + return true +} + +func ReadAllFile(filePath string) (string, error) { + file, err := os.Open(filePath) + if err != nil { + return "", err + } + + data, err := ioutil.ReadAll(file) + if err != nil { + return "", err + } + + return string(data), nil +} + +func WriteFile(filePath string, content []byte) error { + fh, err := os.OpenFile(filePath, os.O_RDWR|os.O_CREATE, os.ModePerm) + if err != nil { + return err + } + defer fh.Close() + + _, err = fh.Write(content) + if err != nil { + return err + } + + return nil +} diff --git a/ethutil/reactor.go b/ethutil/reactor.go deleted file mode 100644 index 7cf145245..000000000 --- a/ethutil/reactor.go +++ /dev/null @@ -1,87 +0,0 @@ -package ethutil - -import ( - "sync" -) - -type ReactorEvent struct { - mut sync.Mutex - event string - chans []chan React -} - -// Post the specified reactor resource on the channels -// currently subscribed -func (e *ReactorEvent) Post(react React) { - e.mut.Lock() - defer e.mut.Unlock() - - for _, ch := range e.chans { - go func(ch chan React) { - ch <- react - }(ch) - } -} - -// Add a subscriber to this event -func (e *ReactorEvent) Add(ch chan React) { - e.mut.Lock() - defer e.mut.Unlock() - - e.chans = append(e.chans, ch) -} - -// Remove a subscriber -func (e *ReactorEvent) Remove(ch chan React) { - e.mut.Lock() - defer e.mut.Unlock() - - for i, c := range e.chans { - if c == ch { - e.chans = append(e.chans[:i], e.chans[i+1:]...) - } - } -} - -// Basic reactor resource -type React struct { - Resource interface{} - Event string -} - -// The reactor basic engine. Acts as bridge -// between the events and the subscribers/posters -type ReactorEngine struct { - patterns map[string]*ReactorEvent -} - -func NewReactorEngine() *ReactorEngine { - return &ReactorEngine{patterns: make(map[string]*ReactorEvent)} -} - -// Subscribe a channel to the specified event -func (reactor *ReactorEngine) Subscribe(event string, ch chan React) { - ev := reactor.patterns[event] - // Create a new event if one isn't available - if ev == nil { - ev = &ReactorEvent{event: event} - reactor.patterns[event] = ev - } - - // Add the channel to reactor event handler - ev.Add(ch) -} - -func (reactor *ReactorEngine) Unsubscribe(event string, ch chan React) { - ev := reactor.patterns[event] - if ev != nil { - ev.Remove(ch) - } -} - -func (reactor *ReactorEngine) Post(event string, resource interface{}) { - ev := reactor.patterns[event] - if ev != nil { - ev.Post(React{Resource: resource, Event: event}) - } -} diff --git a/ethutil/reactor_test.go b/ethutil/reactor_test.go deleted file mode 100644 index 48c2f0df3..000000000 --- a/ethutil/reactor_test.go +++ /dev/null @@ -1,30 +0,0 @@ -package ethutil - -import "testing" - -func TestReactorAdd(t *testing.T) { - engine := NewReactorEngine() - ch := make(chan React) - engine.Subscribe("test", ch) - if len(engine.patterns) != 1 { - t.Error("Expected patterns to be 1, got", len(engine.patterns)) - } -} - -func TestReactorEvent(t *testing.T) { - engine := NewReactorEngine() - - // Buffer 1, so it doesn't block for this test - ch := make(chan React, 1) - engine.Subscribe("test", ch) - engine.Post("test", "hello") - - value := <-ch - if val, ok := value.Resource.(string); ok { - if val != "hello" { - t.Error("Expected Resource to be 'hello', got", val) - } - } else { - t.Error("Unable to cast") - } -} diff --git a/ethutil/rlp.go b/ethutil/rlp.go index 195ef0efb..17ff627eb 100644 --- a/ethutil/rlp.go +++ b/ethutil/rlp.go @@ -2,15 +2,16 @@ package ethutil import ( "bytes" - _ "encoding/binary" "fmt" - _ "log" - _ "math" "math/big" ) -type RlpEncodable interface { +type RlpEncode interface { RlpEncode() []byte +} + +type RlpEncodeDecode interface { + RlpEncode RlpValue() []interface{} } @@ -32,12 +33,14 @@ const ( RlpEmptyStr = 0x40 ) +const rlpEof = -1 + func Char(c []byte) int { if len(c) > 0 { return int(c[0]) } - return 0 + return rlpEof } func DecodeWithReader(reader *bytes.Buffer) interface{} { @@ -46,8 +49,6 @@ func DecodeWithReader(reader *bytes.Buffer) interface{} { // Read the next byte char := Char(reader.Next(1)) switch { - case char == 0: - return nil case char <= 0x7f: return char @@ -55,8 +56,7 @@ func DecodeWithReader(reader *bytes.Buffer) interface{} { return reader.Next(int(char - 0x80)) case char <= 0xbf: - buff := bytes.NewReader(reader.Next(int(char - 0xb8))) - length := ReadVarint(buff) + length := ReadVarInt(reader.Next(int(char - 0xb7))) return reader.Next(int(length)) @@ -64,82 +64,23 @@ func DecodeWithReader(reader *bytes.Buffer) interface{} { length := int(char - 0xc0) for i := 0; i < length; i++ { obj := DecodeWithReader(reader) - if obj != nil { - slice = append(slice, obj) - } else { - break - } - } - - return slice - - } - - return slice -} - -// TODO Use a bytes.Buffer instead of a raw byte slice. -// Cleaner code, and use draining instead of seeking the next bytes to read -func Decode(data []byte, pos uint64) (interface{}, uint64) { - var slice []interface{} - char := int(data[pos]) - switch { - case char <= 0x7f: - return data[pos], pos + 1 - - case char <= 0xb7: - b := uint64(data[pos]) - 0x80 - - return data[pos+1 : pos+1+b], pos + 1 + b - - case char <= 0xbf: - b := uint64(data[pos]) - 0xb7 - - b2 := ReadVarint(bytes.NewReader(data[pos+1 : pos+1+b])) - - return data[pos+1+b : pos+1+b+b2], pos + 1 + b + b2 - - case char <= 0xf7: - b := uint64(data[pos]) - 0xc0 - prevPos := pos - pos++ - for i := uint64(0); i < b; { - var obj interface{} - - // Get the next item in the data list and append it - obj, prevPos = Decode(data, pos) slice = append(slice, obj) - - // Increment i by the amount bytes read in the previous - // read - i += (prevPos - pos) - pos = prevPos } - return slice, pos + return slice case char <= 0xff: - l := uint64(data[pos]) - 0xf7 - b := ReadVarint(bytes.NewReader(data[pos+1 : pos+1+l])) - - pos = pos + l + 1 - - prevPos := b - for i := uint64(0); i < uint64(b); { - var obj interface{} - - obj, prevPos = Decode(data, pos) + length := ReadVarInt(reader.Next(int(char - 0xf7))) + for i := uint64(0); i < length; i++ { + obj := DecodeWithReader(reader) slice = append(slice, obj) - - i += (prevPos - pos) - pos = prevPos } - return slice, pos + return slice default: panic(fmt.Sprintf("byte not supported: %q", char)) } - return slice, 0 + return slice } var ( @@ -223,3 +164,67 @@ func Encode(object interface{}) []byte { return buff.Bytes() } + +// TODO Use a bytes.Buffer instead of a raw byte slice. +// Cleaner code, and use draining instead of seeking the next bytes to read +func Decode(data []byte, pos uint64) (interface{}, uint64) { + var slice []interface{} + char := int(data[pos]) + switch { + case char <= 0x7f: + return data[pos], pos + 1 + + case char <= 0xb7: + b := uint64(data[pos]) - 0x80 + + return data[pos+1 : pos+1+b], pos + 1 + b + + case char <= 0xbf: + b := uint64(data[pos]) - 0xb7 + + b2 := ReadVarInt(data[pos+1 : pos+1+b]) + + return data[pos+1+b : pos+1+b+b2], pos + 1 + b + b2 + + case char <= 0xf7: + b := uint64(data[pos]) - 0xc0 + prevPos := pos + pos++ + for i := uint64(0); i < b; { + var obj interface{} + + // Get the next item in the data list and append it + obj, prevPos = Decode(data, pos) + slice = append(slice, obj) + + // Increment i by the amount bytes read in the previous + // read + i += (prevPos - pos) + pos = prevPos + } + return slice, pos + + case char <= 0xff: + l := uint64(data[pos]) - 0xf7 + b := ReadVarInt(data[pos+1 : pos+1+l]) + + pos = pos + l + 1 + + prevPos := b + for i := uint64(0); i < uint64(b); { + var obj interface{} + + obj, prevPos = Decode(data, pos) + slice = append(slice, obj) + + i += (prevPos - pos) + pos = prevPos + } + return slice, pos + + default: + panic(fmt.Sprintf("byte not supported: %q", char)) + } + + return slice, 0 +} diff --git a/ethutil/rlp_test.go b/ethutil/rlp_test.go index 095c01ecc..90057ab42 100644 --- a/ethutil/rlp_test.go +++ b/ethutil/rlp_test.go @@ -44,6 +44,17 @@ func TestValueSlice(t *testing.T) { } } +func TestLargeData(t *testing.T) { + data := make([]byte, 100000) + enc := Encode(data) + value := NewValue(enc) + value.Decode() + + if value.Len() != len(data) { + t.Error("Expected data to be", len(data), "got", value.Len()) + } +} + func TestValue(t *testing.T) { value := NewValueFromBytes([]byte("\xcd\x83dog\x83god\x83cat\x01")) if value.Get(0).Str() != "dog" { diff --git a/ethutil/value.go b/ethutil/value.go index 735a71dbc..608d332ba 100644 --- a/ethutil/value.go +++ b/ethutil/value.go @@ -1,7 +1,6 @@ package ethutil import ( - "bytes" "fmt" "math/big" "reflect" @@ -67,7 +66,7 @@ func (val *Value) Uint() uint64 { } else if Val, ok := val.Val.(uint); ok { return uint64(Val) } else if Val, ok := val.Val.([]byte); ok { - return ReadVarint(bytes.NewReader(Val)) + return new(big.Int).SetBytes(Val).Uint64() } else if Val, ok := val.Val.(*big.Int); ok { return Val.Uint64() } @@ -75,6 +74,30 @@ func (val *Value) Uint() uint64 { return 0 } +func (val *Value) Int() int64 { + if Val, ok := val.Val.(int8); ok { + return int64(Val) + } else if Val, ok := val.Val.(int16); ok { + return int64(Val) + } else if Val, ok := val.Val.(int32); ok { + return int64(Val) + } else if Val, ok := val.Val.(int64); ok { + return Val + } else if Val, ok := val.Val.(int); ok { + return int64(Val) + } else if Val, ok := val.Val.(float32); ok { + return int64(Val) + } else if Val, ok := val.Val.(float64); ok { + return int64(Val) + } else if Val, ok := val.Val.([]byte); ok { + return new(big.Int).SetBytes(Val).Int64() + } else if Val, ok := val.Val.(*big.Int); ok { + return Val.Int64() + } + + return 0 +} + func (val *Value) Byte() byte { if Val, ok := val.Val.(byte); ok { return Val @@ -123,6 +146,14 @@ func (val *Value) Bytes() []byte { return []byte{} } +func (val *Value) Err() error { + if err, ok := val.Val.(error); ok { + return err + } + + return nil +} + func (val *Value) Slice() []interface{} { if d, ok := val.Val.([]interface{}); ok { return d @@ -158,6 +189,11 @@ func (val *Value) IsStr() bool { return val.Type() == reflect.String } +func (self *Value) IsErr() bool { + _, ok := self.Val.(error) + return ok +} + // Special list checking function. Something is considered // a list if it's of type []interface{}. The list is usually // used in conjunction with rlp decoded streams. @@ -207,6 +243,13 @@ func (val *Value) Cmp(o *Value) bool { return reflect.DeepEqual(val.Val, o.Val) } +func (self *Value) DeepCmp(o *Value) bool { + a := NewValue(self.BigInt()) + b := NewValue(o.BigInt()) + + return a.Cmp(b) +} + func (val *Value) Encode() []byte { return Encode(val.Val) } @@ -215,12 +258,15 @@ func (val *Value) Encode() []byte { func (self *Value) Decode() { v, _ := Decode(self.Bytes(), 0) self.Val = v + //self.Val = DecodeWithReader(bytes.NewBuffer(self.Bytes())) } func NewValueFromBytes(data []byte) *Value { if len(data) != 0 { - data, _ := Decode(data, 0) - return NewValue(data) + value := NewValue(data) + value.Decode() + + return value } return NewValue(nil) @@ -262,6 +308,55 @@ func (val *Value) Append(v interface{}) *Value { return val } +const ( + valOpAdd = iota + valOpDiv + valOpMul + valOpPow + valOpSub +) + +// Math stuff +func (self *Value) doOp(op int, other interface{}) *Value { + left := self.BigInt() + right := NewValue(other).BigInt() + + switch op { + case valOpAdd: + self.Val = left.Add(left, right) + case valOpDiv: + self.Val = left.Div(left, right) + case valOpMul: + self.Val = left.Mul(left, right) + case valOpPow: + self.Val = left.Exp(left, right, Big0) + case valOpSub: + self.Val = left.Sub(left, right) + } + + return self +} + +func (self *Value) Add(other interface{}) *Value { + return self.doOp(valOpAdd, other) +} + +func (self *Value) Sub(other interface{}) *Value { + return self.doOp(valOpSub, other) +} + +func (self *Value) Div(other interface{}) *Value { + return self.doOp(valOpDiv, other) +} + +func (self *Value) Mul(other interface{}) *Value { + return self.doOp(valOpMul, other) +} + +func (self *Value) Pow(other interface{}) *Value { + return self.doOp(valOpPow, other) +} + type ValueIterator struct { value *Value currentValue *Value diff --git a/ethutil/value_test.go b/ethutil/value_test.go index a100f44bc..710cbd887 100644 --- a/ethutil/value_test.go +++ b/ethutil/value_test.go @@ -63,3 +63,18 @@ func TestIterator(t *testing.T) { i++ } } + +func TestMath(t *testing.T) { + a := NewValue(1) + a.Add(1).Add(1) + + if !a.DeepCmp(NewValue(3)) { + t.Error("Expected 3, got", a) + } + + a = NewValue(2) + a.Sub(1).Sub(1) + if !a.DeepCmp(NewValue(0)) { + t.Error("Expected 0, got", a) + } +} |