From b5234413611ce5984292f85a01de1f56c045b490 Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 16 Mar 2015 11:27:38 +0100 Subject: Moved ethutil => common --- common/.gitignore | 12 ++ common/.travis.yml | 3 + common/README.md | 139 ++++++++++++++ common/big.go | 123 +++++++++++++ common/big_test.go | 73 ++++++++ common/bytes.go | 234 ++++++++++++++++++++++++ common/bytes_test.go | 193 ++++++++++++++++++++ common/common.go | 165 +++++++++++++++++ common/common_test.go | 89 +++++++++ common/config.go | 67 +++++++ common/db.go | 12 ++ common/list.go | 81 +++++++++ common/main_test.go | 9 + common/math/dist.go | 80 ++++++++ common/math/dist_test.go | 66 +++++++ common/natspec/natspec.go | 63 +++++++ common/natspec/natspec_js.go | 4 + common/natspec/natspec_test.go | 97 ++++++++++ common/number/int.go | 181 +++++++++++++++++++ common/number/uint_test.go | 92 ++++++++++ common/package.go | 123 +++++++++++++ common/path.go | 68 +++++++ common/path_test.go | 51 ++++++ common/rlp.go | 276 ++++++++++++++++++++++++++++ common/rlp_test.go | 156 ++++++++++++++++ common/size.go | 15 ++ common/size_test.go | 23 +++ common/types.go | 6 + common/value.go | 401 +++++++++++++++++++++++++++++++++++++++++ common/value_test.go | 70 +++++++ 30 files changed, 2972 insertions(+) create mode 100644 common/.gitignore create mode 100644 common/.travis.yml create mode 100644 common/README.md create mode 100644 common/big.go create mode 100644 common/big_test.go create mode 100644 common/bytes.go create mode 100644 common/bytes_test.go create mode 100644 common/common.go create mode 100644 common/common_test.go create mode 100644 common/config.go create mode 100644 common/db.go create mode 100644 common/list.go create mode 100644 common/main_test.go create mode 100644 common/math/dist.go create mode 100644 common/math/dist_test.go create mode 100644 common/natspec/natspec.go create mode 100644 common/natspec/natspec_js.go create mode 100644 common/natspec/natspec_test.go create mode 100644 common/number/int.go create mode 100644 common/number/uint_test.go create mode 100644 common/package.go create mode 100644 common/path.go create mode 100644 common/path_test.go create mode 100644 common/rlp.go create mode 100644 common/rlp_test.go create mode 100644 common/size.go create mode 100644 common/size_test.go create mode 100644 common/types.go create mode 100644 common/value.go create mode 100644 common/value_test.go (limited to 'common') diff --git a/common/.gitignore b/common/.gitignore new file mode 100644 index 000000000..f725d58d1 --- /dev/null +++ b/common/.gitignore @@ -0,0 +1,12 @@ +# See http://help.github.com/ignore-files/ for more about ignoring files. +# +# If you find yourself ignoring temporary files generated by your text editor +# or operating system, you probably want to add a global ignore instead: +# git config --global core.excludesfile ~/.gitignore_global + +/tmp +*/**/*un~ +*un~ +.DS_Store +*/**/.DS_Store + diff --git a/common/.travis.yml b/common/.travis.yml new file mode 100644 index 000000000..69359072d --- /dev/null +++ b/common/.travis.yml @@ -0,0 +1,3 @@ +language: go +go: + - 1.2 diff --git a/common/README.md b/common/README.md new file mode 100644 index 000000000..1ed56b71b --- /dev/null +++ b/common/README.md @@ -0,0 +1,139 @@ +# ethutil + +[![Build +Status](https://travis-ci.org/ethereum/go-ethereum.png?branch=master)](https://travis-ci.org/ethereum/go-ethereum) + +The ethutil package contains the ethereum utility library. + +# Installation + +`go get github.com/ethereum/ethutil-go` + +# Usage + +## RLP (Recursive Linear Prefix) Encoding + +RLP Encoding is an encoding scheme utilized by the Ethereum project. It +encodes any native value or list to string. + +More in depth information about the Encoding scheme see the [Wiki](http://wiki.ethereum.org/index.php/RLP) +article. + +```go +rlp := ethutil.Encode("doge") +fmt.Printf("%q\n", rlp) // => "\0x83dog" + +rlp = ethutil.Encode([]interface{}{"dog", "cat"}) +fmt.Printf("%q\n", rlp) // => "\0xc8\0x83dog\0x83cat" +decoded := ethutil.Decode(rlp) +fmt.Println(decoded) // => ["dog" "cat"] +``` + +## Patricia Trie + +Patricie Tree is a merkle trie utilized by the Ethereum project. + +More in depth information about the (modified) Patricia Trie can be +found on the [Wiki](http://wiki.ethereum.org/index.php/Patricia_Tree). + +The patricia trie uses a db as backend and could be anything as long as +it satisfies the Database interface found in `ethutil/db.go`. + +```go +db := NewDatabase() + +// db, root +trie := ethutil.NewTrie(db, "") + +trie.Put("puppy", "dog") +trie.Put("horse", "stallion") +trie.Put("do", "verb") +trie.Put("doge", "coin") + +// Look up the key "do" in the trie +out := trie.Get("do") +fmt.Println(out) // => verb + +trie.Delete("puppy") +``` + +The patricia trie, in combination with RLP, provides a robust, +cryptographically authenticated data structure that can be used to store +all (key, value) bindings. + +```go +// ... Create db/trie + +// Note that RLP uses interface slices as list +value := ethutil.Encode([]interface{}{"one", 2, "three", []interface{}{42}}) +// Store the RLP encoded value of the list +trie.Put("mykey", value) +``` + +## Value + +Value is a Generic Value which is used in combination with RLP data or +`([])interface{}` structures. It may serve as a bridge between RLP data +and actual real values and takes care of all the type checking and +casting. Unlike Go's `reflect.Value` it does not panic if it's unable to +cast to the requested value. It simple returns the base value of that +type (e.g. `Slice()` returns []interface{}, `Uint()` return 0, etc). + +### Creating a new Value + +`NewEmptyValue()` returns a new \*Value with it's initial value set to a +`[]interface{}` + +`AppendList()` appends a list to the current value. + +`Append(v)` appends the value (v) to the current value/list. + +```go +val := ethutil.NewEmptyValue().Append(1).Append("2") +val.AppendList().Append(3) +``` + +### Retrieving values + +`Get(i)` returns the `i` item in the list. + +`Uint()` returns the value as an unsigned int64. + +`Slice()` returns the value as a interface slice. + +`Str()` returns the value as a string. + +`Bytes()` returns the value as a byte slice. + +`Len()` assumes current to be a slice and returns its length. + +`Byte()` returns the value as a single byte. + +```go +val := ethutil.NewValue([]interface{}{1,"2",[]interface{}{3}}) +val.Get(0).Uint() // => 1 +val.Get(1).Str() // => "2" +s := val.Get(2) // => Value([]interface{}{3}) +s.Get(0).Uint() // => 3 +``` + +## Decoding + +Decoding streams of RLP data is simplified + +```go +val := ethutil.NewValueFromBytes(rlpData) +val.Get(0).Uint() +``` + +## Encoding + +Encoding from Value to RLP is done with the `Encode` method. The +underlying value can be anything RLP can encode (int, str, lists, bytes) + +```go +val := ethutil.NewValue([]interface{}{1,"2",[]interface{}{3}}) +rlp := val.Encode() +// Store the rlp data +Store(rlp) +``` diff --git a/common/big.go b/common/big.go new file mode 100644 index 000000000..750b28f85 --- /dev/null +++ b/common/big.go @@ -0,0 +1,123 @@ +package common + +import "math/big" + +// 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)) + + return c +} + +// Big +// +// Shortcut for new(big.Int).SetString(..., 0) +func Big(num string) *big.Int { + n := new(big.Int) + n.SetString(num, 0) + + return n +} + +// BigD +// +// Shortcut for new(big.Int).SetBytes(...) +func Bytes2Big(data []byte) *big.Int { + n := new(big.Int) + n.SetBytes(data) + + return n +} +func BigD(data []byte) *big.Int { return Bytes2Big(data) } + +func String2Big(num string) *big.Int { + n := new(big.Int) + n.SetString(num, 0) + return n +} + +func BitTest(num *big.Int, i int) bool { + return num.Bit(i) > 0 +} + +// To256 +// +// "cast" the big int to a 256 big int (i.e., limit to) +var tt256 = new(big.Int).Lsh(big.NewInt(1), 256) +var tt256m1 = new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 256), big.NewInt(1)) +var tt255 = new(big.Int).Lsh(big.NewInt(1), 255) + +func U256(x *big.Int) *big.Int { + //if x.Cmp(Big0) < 0 { + // return new(big.Int).Add(tt256, x) + // } + + x.And(x, tt256m1) + + return x +} + +func S256(x *big.Int) *big.Int { + if x.Cmp(tt255) < 0 { + return x + } else { + // We don't want to modify x, ever + return new(big.Int).Sub(x, tt256) + } +} + +func FirstBitSet(v *big.Int) int { + for i := 0; i < v.BitLen(); i++ { + if v.Bit(i) > 0 { + return i + } + } + + return v.BitLen() +} + +// 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) + + if len(num.Bytes()) > base/8 { + return num.Bytes() + } + + return append(ret[:len(ret)-len(num.Bytes())], num.Bytes()...) +} + +// 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 y + } + + return x +} + +// Big min +// +// Returns the minimum size big integer +func BigMin(x, y *big.Int) *big.Int { + if x.Cmp(y) >= 0 { + return y + } + + return x +} diff --git a/common/big_test.go b/common/big_test.go new file mode 100644 index 000000000..cedbaf144 --- /dev/null +++ b/common/big_test.go @@ -0,0 +1,73 @@ +package common + +import ( + "bytes" + "testing" +) + +func TestMisc(t *testing.T) { + a := Big("10") + b := Big("57896044618658097711785492504343953926634992332820282019728792003956564819968") + c := []byte{1, 2, 3, 4} + z := BitTest(a, 1) + + if z != true { + t.Error("Expected true got", z) + } + + U256(a) + S256(a) + + U256(b) + S256(b) + + BigD(c) +} + +func TestBigMax(t *testing.T) { + a := Big("10") + b := Big("5") + + max1 := BigMax(a, b) + if max1 != a { + t.Errorf("Expected %d got %d", a, max1) + } + + max2 := BigMax(b, a) + if max2 != a { + t.Errorf("Expected %d got %d", a, max2) + } +} + +func TestBigMin(t *testing.T) { + a := Big("10") + b := Big("5") + + min1 := BigMin(a, b) + if min1 != b { + t.Errorf("Expected %d got %d", b, min1) + } + + min2 := BigMin(b, a) + if min2 != b { + t.Errorf("Expected %d got %d", b, min2) + } +} + +func TestBigCopy(t *testing.T) { + a := Big("10") + b := BigCopy(a) + c := Big("1000000000000") + y := BigToBytes(b, 16) + ybytes := []byte{0, 10} + z := BigToBytes(c, 16) + zbytes := []byte{232, 212, 165, 16, 0} + + if bytes.Compare(y, ybytes) != 0 { + t.Error("Got", ybytes) + } + + if bytes.Compare(z, zbytes) != 0 { + t.Error("Got", zbytes) + } +} diff --git a/common/bytes.go b/common/bytes.go new file mode 100644 index 000000000..4aef9a223 --- /dev/null +++ b/common/bytes.go @@ -0,0 +1,234 @@ +package common + +import ( + "bytes" + "encoding/binary" + "encoding/hex" + "fmt" + "math/big" + "strings" +) + +type Bytes []byte + +func (self Bytes) String() string { + return string(self) +} + +func DeleteFromByteSlice(s [][]byte, hash []byte) [][]byte { + for i, h := range s { + if bytes.Compare(h, hash) == 0 { + return append(s[:i:i], s[i+1:]...) + } + } + + return s +} + +// 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) + if err != nil { + fmt.Println("NumberToBytes failed:", err) + } + + 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 + + // Make sure the buffer is 64bits + data := make([]byte, 8) + data = append(data[:len(b)], b...) + + buf := bytes.NewReader(data) + err := binary.Read(buf, binary.BigEndian, &number) + if err != nil { + fmt.Println("BytesToNumber failed:", err) + } + + return number +} + +// Read variable int +// +// Read a variable length number in big endian byte order +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 + d := LeftPadBytes(buff, 4) + binary.Read(bytes.NewReader(d), binary.BigEndian, &num) + ret = uint64(num) + case l > 1: + var num uint16 + d := LeftPadBytes(buff, 2) + binary.Read(bytes.NewReader(d), binary.BigEndian, &num) + ret = uint64(num) + default: + var num uint8 + binary.Read(bytes.NewReader(buff), binary.BigEndian, &num) + ret = uint64(num) + } + + return +} + +// Binary length +// +// Returns the true binary length of the given number +func BinaryLength(num int) int { + if num == 0 { + return 0 + } + + return 1 + BinaryLength(num>>8) +} + +// Copy bytes +// +// Returns an exact copy of the provided bytes +func CopyBytes(b []byte) (copiedBytes []byte) { + copiedBytes = make([]byte, len(b)) + copy(copiedBytes, b) + + return +} + +func IsHex(str string) bool { + l := len(str) + return l >= 4 && l%2 == 0 && str[0:2] == "0x" +} + +func Bytes2Hex(d []byte) string { + return hex.EncodeToString(d) +} + +func Hex2Bytes(str string) []byte { + h, _ := hex.DecodeString(str) + + return h +} + +func StringToByteFunc(str string, cb func(str string) []byte) (ret []byte) { + if len(str) > 1 && str[0:2] == "0x" && !strings.Contains(str, "\n") { + ret = Hex2Bytes(str[2:]) + } else { + ret = cb(str) + } + + return +} + +func FormatData(data string) []byte { + if len(data) == 0 { + return nil + } + // Simple stupid + d := new(big.Int) + if data[0:1] == "\"" && data[len(data)-1:] == "\"" { + return RightPadBytes([]byte(data[1:len(data)-1]), 32) + } else if len(data) > 1 && data[:2] == "0x" { + d.SetBytes(Hex2Bytes(data[2:])) + } else { + d.SetString(data, 0) + } + + 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 + } + + padded := make([]byte, l) + copy(padded[0:len(slice)], slice) + + return padded +} + +func LeftPadBytes(slice []byte, l int) []byte { + if l < len(slice) { + return slice + } + + padded := make([]byte, l) + copy(padded[l-len(slice):], slice) + + 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) + } else if len(slice) > 20 { + addr = slice[len(slice)-20:] + } else { + addr = slice + } + + addr = CopyBytes(addr) + + return +} + +func ByteSliceToInterface(slice [][]byte) (ret []interface{}) { + for _, i := range slice { + ret = append(ret, i) + } + + return +} diff --git a/common/bytes_test.go b/common/bytes_test.go new file mode 100644 index 000000000..ec106bf4b --- /dev/null +++ b/common/bytes_test.go @@ -0,0 +1,193 @@ +package common + +import ( + checker "gopkg.in/check.v1" +) + +type BytesSuite struct{} + +var _ = checker.Suite(&BytesSuite{}) + +func (s *BytesSuite) TestByteString(c *checker.C) { + var data Bytes + data = []byte{102, 111, 111} + exp := "foo" + res := data.String() + + c.Assert(res, checker.Equals, exp) +} + +/* +func (s *BytesSuite) TestDeleteFromByteSlice(c *checker.C) { + data := []byte{1, 2, 3, 4} + slice := []byte{1, 2, 3, 4} + exp := []byte{1, 4} + res := DeleteFromByteSlice(data, slice) + + c.Assert(res, checker.DeepEquals, exp) +} + +*/ +func (s *BytesSuite) TestNumberToBytes(c *checker.C) { + // data1 := int(1) + // res1 := NumberToBytes(data1, 16) + // c.Check(res1, checker.Panics) + + var data2 float64 = 3.141592653 + exp2 := []byte{0xe9, 0x38} + res2 := NumberToBytes(data2, 16) + c.Assert(res2, checker.DeepEquals, exp2) +} + +func (s *BytesSuite) TestBytesToNumber(c *checker.C) { + datasmall := []byte{0xe9, 0x38, 0xe9, 0x38} + datalarge := []byte{0xe9, 0x38, 0xe9, 0x38, 0xe9, 0x38, 0xe9, 0x38} + + var expsmall uint64 = 0xe938e938 + var explarge uint64 = 0x0 + + ressmall := BytesToNumber(datasmall) + reslarge := BytesToNumber(datalarge) + + c.Assert(ressmall, checker.Equals, expsmall) + c.Assert(reslarge, checker.Equals, explarge) + +} + +func (s *BytesSuite) TestReadVarInt(c *checker.C) { + data8 := []byte{1, 2, 3, 4, 5, 6, 7, 8} + data4 := []byte{1, 2, 3, 4} + data2 := []byte{1, 2} + data1 := []byte{1} + + exp8 := uint64(72623859790382856) + exp4 := uint64(16909060) + exp2 := uint64(258) + exp1 := uint64(1) + + res8 := ReadVarInt(data8) + res4 := ReadVarInt(data4) + res2 := ReadVarInt(data2) + res1 := ReadVarInt(data1) + + c.Assert(res8, checker.Equals, exp8) + c.Assert(res4, checker.Equals, exp4) + c.Assert(res2, checker.Equals, exp2) + c.Assert(res1, checker.Equals, exp1) +} + +func (s *BytesSuite) TestBinaryLength(c *checker.C) { + data1 := 0 + data2 := 920987656789 + + exp1 := 0 + exp2 := 5 + + res1 := BinaryLength(data1) + res2 := BinaryLength(data2) + + c.Assert(res1, checker.Equals, exp1) + c.Assert(res2, checker.Equals, exp2) +} + +func (s *BytesSuite) TestCopyBytes(c *checker.C) { + data1 := []byte{1, 2, 3, 4} + exp1 := []byte{1, 2, 3, 4} + res1 := CopyBytes(data1) + c.Assert(res1, checker.DeepEquals, exp1) +} + +func (s *BytesSuite) TestIsHex(c *checker.C) { + data1 := "a9e67e" + exp1 := false + res1 := IsHex(data1) + c.Assert(res1, checker.DeepEquals, exp1) + + data2 := "0xa9e67e00" + exp2 := true + res2 := IsHex(data2) + c.Assert(res2, checker.DeepEquals, exp2) + +} + +func (s *BytesSuite) TestParseDataString(c *checker.C) { + res1 := ParseData("hello", "world", "0x0106") + data := "68656c6c6f000000000000000000000000000000000000000000000000000000776f726c640000000000000000000000000000000000000000000000000000000106000000000000000000000000000000000000000000000000000000000000" + exp1 := Hex2Bytes(data) + c.Assert(res1, checker.DeepEquals, exp1) +} + +func (s *BytesSuite) TestParseDataBytes(c *checker.C) { + data1 := []byte{232, 212, 165, 16, 0} + exp1 := []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 212, 165, 16, 0} + + res1 := ParseData(data1) + c.Assert(res1, checker.DeepEquals, exp1) + +} + +func (s *BytesSuite) TestLeftPadBytes(c *checker.C) { + val1 := []byte{1, 2, 3, 4} + exp1 := []byte{0, 0, 0, 0, 1, 2, 3, 4} + + res1 := LeftPadBytes(val1, 8) + res2 := LeftPadBytes(val1, 2) + + c.Assert(res1, checker.DeepEquals, exp1) + c.Assert(res2, checker.DeepEquals, val1) +} + +func (s *BytesSuite) TestFormatData(c *checker.C) { + data1 := "" + data2 := "0xa9e67e00" + data3 := "a9e67e" + data4 := "\"a9e67e00\"" + + // exp1 := []byte{} + exp2 := []byte{00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 0xa9, 0xe6, 0x7e, 00} + exp3 := []byte{00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00} + exp4 := []byte{0x61, 0x39, 0x65, 0x36, 0x37, 0x65, 0x30, 0x30, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00} + + res1 := FormatData(data1) + res2 := FormatData(data2) + res3 := FormatData(data3) + res4 := FormatData(data4) + + c.Assert(res1, checker.IsNil) + c.Assert(res2, checker.DeepEquals, exp2) + c.Assert(res3, checker.DeepEquals, exp3) + c.Assert(res4, checker.DeepEquals, exp4) +} + +func (s *BytesSuite) TestRightPadBytes(c *checker.C) { + val := []byte{1, 2, 3, 4} + exp := []byte{1, 2, 3, 4, 0, 0, 0, 0} + + resstd := RightPadBytes(val, 8) + resshrt := RightPadBytes(val, 2) + + c.Assert(resstd, checker.DeepEquals, exp) + c.Assert(resshrt, checker.DeepEquals, val) +} + +func (s *BytesSuite) TestLeftPadString(c *checker.C) { + val := "test" + exp := "\x30\x30\x30\x30" + val + + resstd := LeftPadString(val, 8) + resshrt := LeftPadString(val, 2) + + c.Assert(resstd, checker.Equals, exp) + c.Assert(resshrt, checker.Equals, val) +} + +func (s *BytesSuite) TestRightPadString(c *checker.C) { + val := "test" + exp := val + "\x30\x30\x30\x30" + + resstd := RightPadString(val, 8) + resshrt := RightPadString(val, 2) + + c.Assert(resstd, checker.Equals, exp) + c.Assert(resshrt, checker.Equals, val) +} diff --git a/common/common.go b/common/common.go new file mode 100644 index 000000000..1371d18d4 --- /dev/null +++ b/common/common.go @@ -0,0 +1,165 @@ +package common + +import ( + "fmt" + "math/big" + "os" + "os/user" + "path" + "path/filepath" + "runtime" + "time" + + "github.com/kardianos/osext" +) + +// MakeName creates a node name that follows the ethereum convention +// for such names. It adds the operation system name and Go runtime version +// the name. +func MakeName(name, version string) string { + return fmt.Sprintf("%s/v%s/%s/%s", name, version, runtime.GOOS, runtime.Version()) +} + +func DefaultAssetPath() string { + var assetPath string + pwd, _ := os.Getwd() + srcdir := path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "cmd", "mist") + + // If the current working directory is the go-ethereum dir + // assume a debug build and use the source directory as + // asset directory. + if pwd == srcdir { + assetPath = path.Join(pwd, "assets") + } else { + switch runtime.GOOS { + case "darwin": + // Get Binary Directory + exedir, _ := osext.ExecutableFolder() + assetPath = filepath.Join(exedir, "../Resources") + case "linux": + assetPath = "/usr/share/mist" + case "windows": + assetPath = "./assets" + default: + assetPath = "." + } + } + + // Check if the assetPath exists. If not, try the source directory + // This happens when binary is run from outside cmd/mist directory + if _, err := os.Stat(assetPath); os.IsNotExist(err) { + assetPath = path.Join(srcdir, "assets") + } + + return assetPath +} + +func DefaultDataDir() string { + usr, _ := user.Current() + if runtime.GOOS == "darwin" { + return path.Join(usr.HomeDir, "Library/Ethereum") + } else if runtime.GOOS == "windows" { + return path.Join(usr.HomeDir, "AppData/Roaming/Ethereum") + } else { + return path.Join(usr.HomeDir, ".ethereum") + } +} + +func FromHex(s string) []byte { + if len(s) > 1 { + if s[0:2] == "0x" { + s = s[2:] + } + if len(s)%2 == 1 { + s = "0" + s + } + return Hex2Bytes(s) + } + return nil +} +func IsWindows() bool { + return runtime.GOOS == "windows" +} + +func WindonizePath(path string) string { + if string(path[0]) == "/" && IsWindows() { + path = path[1:] + } + return path +} + +// The different number of units +var ( + Douglas = BigPow(10, 42) + Einstein = BigPow(10, 21) + Ether = BigPow(10, 18) + Finney = BigPow(10, 15) + Szabo = BigPow(10, 12) + Shannon = BigPow(10, 9) + Babbage = BigPow(10, 6) + Ada = BigPow(10, 3) + Wei = big.NewInt(1) +) + +// +// 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: + fin = new(big.Int).Div(num, Douglas) + denom = "Douglas" + case num.Cmp(Einstein) >= 0: + fin = new(big.Int).Div(num, Einstein) + denom = "Einstein" + case num.Cmp(Ether) >= 0: + fin = new(big.Int).Div(num, Ether) + denom = "Ether" + case num.Cmp(Finney) >= 0: + fin = new(big.Int).Div(num, Finney) + denom = "Finney" + case num.Cmp(Szabo) >= 0: + fin = new(big.Int).Div(num, Szabo) + denom = "Szabo" + case num.Cmp(Shannon) >= 0: + fin = new(big.Int).Div(num, Shannon) + denom = "Shannon" + case num.Cmp(Babbage) >= 0: + fin = new(big.Int).Div(num, Babbage) + denom = "Babbage" + case num.Cmp(Ada) >= 0: + fin = new(big.Int).Div(num, Ada) + denom = "Ada" + } + + // TODO add comment clarifying expected behavior + if len(fin.String()) > 5 { + return fmt.Sprintf("%sE%d %s", fin.String()[0:5], len(fin.String())-5, denom) + } + + return fmt.Sprintf("%v %s", fin, denom) +} + +// Common big integers often used +var ( + Big1 = big.NewInt(1) + Big2 = big.NewInt(2) + Big3 = big.NewInt(3) + Big0 = big.NewInt(0) + BigTrue = Big1 + BigFalse = Big0 + Big32 = big.NewInt(32) + Big256 = big.NewInt(0xff) + Big257 = big.NewInt(257) +) + +func Bench(pre string, cb func()) { + start := time.Now() + cb() + fmt.Println(pre, ": took:", time.Since(start)) +} diff --git a/common/common_test.go b/common/common_test.go new file mode 100644 index 000000000..c7ba87f90 --- /dev/null +++ b/common/common_test.go @@ -0,0 +1,89 @@ +package common + +import ( + "bytes" + "math/big" + "os" + "testing" + + checker "gopkg.in/check.v1" +) + +type CommonSuite struct{} + +var _ = checker.Suite(&CommonSuite{}) + +func (s *CommonSuite) TestOS(c *checker.C) { + expwin := (os.PathSeparator == '\\' && os.PathListSeparator == ';') + res := IsWindows() + + if !expwin { + c.Assert(res, checker.Equals, expwin, checker.Commentf("IsWindows is", res, "but path is", os.PathSeparator)) + } else { + c.Assert(res, checker.Not(checker.Equals), expwin, checker.Commentf("IsWindows is", res, "but path is", os.PathSeparator)) + } +} + +func (s *CommonSuite) TestWindonziePath(c *checker.C) { + iswindowspath := os.PathSeparator == '\\' + path := "/opt/eth/test/file.ext" + res := WindonizePath(path) + ressep := string(res[0]) + + if !iswindowspath { + c.Assert(ressep, checker.Equals, "/") + } else { + c.Assert(ressep, checker.Not(checker.Equals), "/") + } +} + +func (s *CommonSuite) TestCommon(c *checker.C) { + douglas := CurrencyToString(BigPow(10, 43)) + einstein := CurrencyToString(BigPow(10, 22)) + ether := CurrencyToString(BigPow(10, 19)) + finney := CurrencyToString(BigPow(10, 16)) + szabo := CurrencyToString(BigPow(10, 13)) + shannon := CurrencyToString(BigPow(10, 10)) + babbage := CurrencyToString(BigPow(10, 7)) + ada := CurrencyToString(BigPow(10, 4)) + wei := CurrencyToString(big.NewInt(10)) + + c.Assert(douglas, checker.Equals, "10 Douglas") + c.Assert(einstein, checker.Equals, "10 Einstein") + c.Assert(ether, checker.Equals, "10 Ether") + c.Assert(finney, checker.Equals, "10 Finney") + c.Assert(szabo, checker.Equals, "10 Szabo") + c.Assert(shannon, checker.Equals, "10 Shannon") + c.Assert(babbage, checker.Equals, "10 Babbage") + c.Assert(ada, checker.Equals, "10 Ada") + c.Assert(wei, checker.Equals, "10 Wei") +} + +func (s *CommonSuite) TestLarge(c *checker.C) { + douglaslarge := CurrencyToString(BigPow(100000000, 43)) + adalarge := CurrencyToString(BigPow(100000000, 4)) + weilarge := CurrencyToString(big.NewInt(100000000)) + + c.Assert(douglaslarge, checker.Equals, "10000E298 Douglas") + c.Assert(adalarge, checker.Equals, "10000E7 Einstein") + c.Assert(weilarge, checker.Equals, "100 Babbage") +} + +//fromHex +func TestFromHex(t *testing.T) { + input := "0x01" + expected := []byte{1} + result := FromHex(input) + if bytes.Compare(expected, result) != 0 { + t.Errorf("Expected % x got % x", expected, result) + } +} + +func TestFromHexOddLength(t *testing.T) { + input := "0x1" + expected := []byte{1} + result := FromHex(input) + if bytes.Compare(expected, result) != 0 { + t.Errorf("Expected % x got % x", expected, result) + } +} diff --git a/common/config.go b/common/config.go new file mode 100644 index 000000000..23a902bc1 --- /dev/null +++ b/common/config.go @@ -0,0 +1,67 @@ +package common + +import ( + "flag" + "fmt" + "os" + + "github.com/rakyll/globalconf" +) + +// Config struct +type ConfigManager struct { + ExecPath string + Debug bool + Diff bool + DiffType string + Paranoia bool + VmType int + + conf *globalconf.GlobalConf +} + +// Read config +// +// Initialize Config from Config File +func ReadConfig(ConfigFile string, Datadir string, EnvPrefix string) *ConfigManager { + if !FileExist(ConfigFile) { + // create ConfigFile if it does not exist, otherwise + // globalconf will panic when trying to persist flags. + fmt.Printf("config file '%s' doesn't exist, creating it\n", ConfigFile) + os.Create(ConfigFile) + } + g, err := globalconf.NewWithOptions(&globalconf.Options{ + Filename: ConfigFile, + EnvPrefix: EnvPrefix, + }) + if err != nil { + fmt.Println(err) + } else { + g.ParseAll() + } + cfg := &ConfigManager{ExecPath: Datadir, Debug: true, conf: g, Paranoia: true} + return cfg +} + +// provides persistence for flags +func (c *ConfigManager) Save(key string, value interface{}) { + f := &flag.Flag{Name: key, Value: newConfValue(value)} + c.conf.Set("", f) +} + +func (c *ConfigManager) Delete(key string) { + c.conf.Delete("", key) +} + +// private type implementing flag.Value +type confValue struct { + value string +} + +// generic constructor to allow persising non-string values directly +func newConfValue(value interface{}) *confValue { + return &confValue{fmt.Sprintf("%v", value)} +} + +func (self confValue) String() string { return self.value } +func (self confValue) Set(s string) error { self.value = s; return nil } diff --git a/common/db.go b/common/db.go new file mode 100644 index 000000000..6505e61c6 --- /dev/null +++ b/common/db.go @@ -0,0 +1,12 @@ +package common + +// Database interface +type Database interface { + Put(key []byte, value []byte) + Get(key []byte) ([]byte, error) + //GetKeys() []*Key + Delete(key []byte) error + LastKnownTD() []byte + Close() + Print() +} diff --git a/common/list.go b/common/list.go new file mode 100644 index 000000000..594a8a24b --- /dev/null +++ b/common/list.go @@ -0,0 +1,81 @@ +package common + +import ( + "encoding/json" + "reflect" + "sync" +) + +// The list type is an anonymous slice handler which can be used +// for containing any slice type to use in an environment which +// does not support slice types (e.g., JavaScript, QML) +type List struct { + mut sync.Mutex + val interface{} + list reflect.Value + Length int +} + +// Initialise a new list. Panics if non-slice type is given. +func NewList(t interface{}) *List { + list := reflect.ValueOf(t) + if list.Kind() != reflect.Slice { + panic("list container initialized with a non-slice type") + } + + return &List{sync.Mutex{}, t, list, list.Len()} +} + +func EmptyList() *List { + return NewList([]interface{}{}) +} + +// Get N element from the embedded slice. Returns nil if OOB. +func (self *List) Get(i int) interface{} { + if self.list.Len() > i { + self.mut.Lock() + defer self.mut.Unlock() + + i := self.list.Index(i).Interface() + + return i + } + + return nil +} + +func (self *List) GetAsJson(i int) interface{} { + e := self.Get(i) + + r, _ := json.Marshal(e) + + return string(r) +} + +// Appends value at the end of the slice. Panics when incompatible value +// is given. +func (self *List) Append(v interface{}) { + self.mut.Lock() + defer self.mut.Unlock() + + self.list = reflect.Append(self.list, reflect.ValueOf(v)) + self.Length = self.list.Len() +} + +// Returns the underlying slice as interface. +func (self *List) Interface() interface{} { + return self.list.Interface() +} + +// For JavaScript <3 +func (self *List) ToJSON() string { + // make(T, 0) != nil + list := make([]interface{}, 0) + for i := 0; i < self.Length; i++ { + list = append(list, self.Get(i)) + } + + data, _ := json.Marshal(list) + + return string(data) +} diff --git a/common/main_test.go b/common/main_test.go new file mode 100644 index 000000000..2bed278e6 --- /dev/null +++ b/common/main_test.go @@ -0,0 +1,9 @@ +package common + +import ( + "testing" + + checker "gopkg.in/check.v1" +) + +func Test(t *testing.T) { checker.TestingT(t) } diff --git a/common/math/dist.go b/common/math/dist.go new file mode 100644 index 000000000..0a0731971 --- /dev/null +++ b/common/math/dist.go @@ -0,0 +1,80 @@ +package math + +import ( + "math/big" + "sort" + + "github.com/ethereum/go-ethereum/common" +) + +type Summer interface { + Sum(i int) *big.Int + Len() int +} + +func Sum(slice Summer) (sum *big.Int) { + sum = new(big.Int) + + for i := 0; i < slice.Len(); i++ { + sum.Add(sum, slice.Sum(i)) + } + return +} + +type Vector struct { + Gas, Price *big.Int +} + +type VectorsBy func(v1, v2 Vector) bool + +func (self VectorsBy) Sort(vectors []Vector) { + bs := vectorSorter{ + vectors: vectors, + by: self, + } + sort.Sort(bs) +} + +type vectorSorter struct { + vectors []Vector + by func(v1, v2 Vector) bool +} + +func (v vectorSorter) Len() int { return len(v.vectors) } +func (v vectorSorter) Less(i, j int) bool { return v.by(v.vectors[i], v.vectors[j]) } +func (v vectorSorter) Swap(i, j int) { v.vectors[i], v.vectors[j] = v.vectors[j], v.vectors[i] } + +func PriceSort(v1, v2 Vector) bool { return v1.Price.Cmp(v2.Price) < 0 } +func GasSort(v1, v2 Vector) bool { return v1.Gas.Cmp(v2.Gas) < 0 } + +type vectorSummer struct { + vectors []Vector + by func(v Vector) *big.Int +} + +type VectorSum func(v Vector) *big.Int + +func (v VectorSum) Sum(vectors []Vector) *big.Int { + vs := vectorSummer{ + vectors: vectors, + by: v, + } + return Sum(vs) +} + +func (v vectorSummer) Len() int { return len(v.vectors) } +func (v vectorSummer) Sum(i int) *big.Int { return v.by(v.vectors[i]) } + +func GasSum(v Vector) *big.Int { return v.Gas } + +var etherInWei = new(big.Rat).SetInt(common.String2Big("1000000000000000000")) + +func GasPrice(bp, gl, ep *big.Int) *big.Int { + BP := new(big.Rat).SetInt(bp) + GL := new(big.Rat).SetInt(gl) + EP := new(big.Rat).SetInt(ep) + GP := new(big.Rat).Quo(BP, GL) + GP = GP.Quo(GP, EP) + + return GP.Mul(GP, etherInWei).Num() +} diff --git a/common/math/dist_test.go b/common/math/dist_test.go new file mode 100644 index 000000000..90e302f44 --- /dev/null +++ b/common/math/dist_test.go @@ -0,0 +1,66 @@ +package math + +import ( + "fmt" + "math/big" + "testing" +) + +type summer struct { + numbers []*big.Int +} + +func (s summer) Len() int { return len(s.numbers) } +func (s summer) Sum(i int) *big.Int { + return s.numbers[i] +} + +func TestSum(t *testing.T) { + summer := summer{numbers: []*big.Int{big.NewInt(1), big.NewInt(2), big.NewInt(3)}} + sum := Sum(summer) + if sum.Cmp(big.NewInt(6)) != 0 { + t.Errorf("not 6", sum) + } +} + +func TestDist(t *testing.T) { + var vectors = []Vector{ + Vector{big.NewInt(1000), big.NewInt(1234)}, + Vector{big.NewInt(500), big.NewInt(10023)}, + Vector{big.NewInt(1034), big.NewInt(1987)}, + Vector{big.NewInt(1034), big.NewInt(1987)}, + Vector{big.NewInt(8983), big.NewInt(1977)}, + Vector{big.NewInt(98382), big.NewInt(1887)}, + Vector{big.NewInt(12398), big.NewInt(1287)}, + Vector{big.NewInt(12398), big.NewInt(1487)}, + Vector{big.NewInt(12398), big.NewInt(1987)}, + Vector{big.NewInt(12398), big.NewInt(128)}, + Vector{big.NewInt(12398), big.NewInt(1987)}, + Vector{big.NewInt(1398), big.NewInt(187)}, + Vector{big.NewInt(12328), big.NewInt(1927)}, + Vector{big.NewInt(12398), big.NewInt(1987)}, + Vector{big.NewInt(22398), big.NewInt(1287)}, + Vector{big.NewInt(1370), big.NewInt(1981)}, + Vector{big.NewInt(12398), big.NewInt(1957)}, + Vector{big.NewInt(42198), big.NewInt(1987)}, + } + + VectorsBy(GasSort).Sort(vectors) + fmt.Println(vectors) + + BP := big.NewInt(15) + GL := big.NewInt(1000000) + EP := big.NewInt(100) + fmt.Println("BP", BP, "GL", GL, "EP", EP) + GP := GasPrice(BP, GL, EP) + fmt.Println("GP =", GP, "Wei per GU") + + S := len(vectors) / 4 + fmt.Println("L", len(vectors), "S", S) + for i := 1; i <= S*4; i += S { + fmt.Printf("T%d = %v\n", i, vectors[i]) + } + + g := VectorSum(GasSum).Sum(vectors) + fmt.Printf("G = ∑g* (%v)\n", g) +} diff --git a/common/natspec/natspec.go b/common/natspec/natspec.go new file mode 100644 index 000000000..793bf59ae --- /dev/null +++ b/common/natspec/natspec.go @@ -0,0 +1,63 @@ +package natspec + +import ( + "fmt" + "github.com/obscuren/otto" +) + +type NatSpec struct { + jsvm *otto.Otto +} + +// TODO: should initialise with abi and userdoc jsons +func New() (self *NatSpec, err error) { + + self = new(NatSpec) + self.jsvm = otto.New() + + _, err = self.jsvm.Run(natspecJS) + if err != nil { + return + } + _, err = self.jsvm.Run("var natspec = require('natspec');") + if err != nil { + return + } + + return +} + +func (self *NatSpec) Notice(transaction, abi, method, expression string) (string, error) { + var err error + if _, err = self.jsvm.Run("var transaction = " + transaction + ";"); err != nil { + return "", fmt.Errorf("natspec.js error setting transaction: %v", err) + } + + if _, err = self.jsvm.Run("var abi = " + abi + ";"); err != nil { + return "", fmt.Errorf("natspec.js error setting abi: %v", err) + } + + if _, err = self.jsvm.Run("var method = '" + method + "';"); err != nil { + return "", fmt.Errorf("natspec.js error setting method: %v", err) + } + + if _, err = self.jsvm.Run("var expression = \"" + expression + "\";"); err != nil { + return "", fmt.Errorf("natspec.js error setting expression: %v", err) + } + + self.jsvm.Run("var call = {method: method,abi: abi,transaction: transaction};") + value, err := self.jsvm.Run("natspec.evaluateExpression(expression, call);") + if err != nil { + return "", fmt.Errorf("natspec.js error evaluating expression: %v", err) + } + evalError := "Natspec evaluation failed, wrong input params" + if value.String() == evalError { + return "", fmt.Errorf("natspec.js error evaluating expression: wrong input params in expression '%s'", expression) + } + if len(value.String()) == 0 { + return "", fmt.Errorf("natspec.js error evaluating expression") + } + + return value.String(), nil + +} diff --git a/common/natspec/natspec_js.go b/common/natspec/natspec_js.go new file mode 100644 index 000000000..96d8204fb --- /dev/null +++ b/common/natspec/natspec_js.go @@ -0,0 +1,4 @@ +package natspec + +const natspecJS = `require=function t(e,n,r){function i(f,u){if(!n[f]){if(!e[f]){var s="function"==typeof require&&require;if(!u&&s)return s(f,!0);if(o)return o(f,!0);var c=new Error("Cannot find module '"+f+"'");throw c.code="MODULE_NOT_FOUND",c}var a=n[f]={exports:{}};e[f][0].call(a.exports,function(t){var n=e[f][1][t];return i(n?n:t)},a,a.exports,t,e,n,r)}return n[f].exports}for(var o="function"==typeof require&&require,f=0;fv;v++)d.push(g(e.slice(0,s))),e=e.slice(s);n.push(d)}else r.prefixedType("string")(t[c].type)?(a=a.slice(s),n.push(g(e.slice(0,s))),e=e.slice(s)):(n.push(g(e.slice(0,s))),e=e.slice(s))}),n},g=function(t){var e={};return t.forEach(function(t){var r=n.extractDisplayName(t.name),i=n.extractTypeName(t.name),o=function(){var e=Array.prototype.slice.call(arguments);return a(t.inputs,e)};void 0===e[r]&&(e[r]=o),e[r][i]=o}),e},m=function(t){var e={};return t.forEach(function(t){var r=n.extractDisplayName(t.name),i=n.extractTypeName(t.name),o=function(e){return h(t.outputs,e)};void 0===e[r]&&(e[r]=o),e[r][i]=o}),e};e.exports={inputParser:g,outputParser:m,formatInput:a,formatOutput:h}},{"./const":4,"./formatters":5,"./types":6,"./utils":7}],4:[function(t,e){(function(n){if("build"!==n.env.NODE_ENV)var r=t("bignumber.js");var i=["wei","Kwei","Mwei","Gwei","szabo","finney","ether","grand","Mether","Gether","Tether","Pether","Eether","Zether","Yether","Nether","Dether","Vether","Uether"];e.exports={ETH_PADDING:32,ETH_SIGNATURE_LENGTH:4,ETH_UNITS:i,ETH_BIGNUMBER_ROUNDING_MODE:{ROUNDING_MODE:r.ROUND_DOWN},ETH_POLLING_TIMEOUT:1e3}}).call(this,t("_process"))},{_process:2,"bignumber.js":8}],5:[function(t,e){(function(n){if("build"!==n.env.NODE_ENV)var r=t("bignumber.js");var i=t("./utils"),o=t("./const"),f=function(t,e,n){return new Array(e-t.length+1).join(n?n:"0")+t},u=function(t){var e=2*o.ETH_PADDING;return t instanceof r||"number"==typeof t?("number"==typeof t&&(t=new r(t)),r.config(o.ETH_BIGNUMBER_ROUNDING_MODE),t=t.round(),t.lessThan(0)&&(t=new r("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",16).plus(t).plus(1)),t=t.toString(16)):t=0===t.indexOf("0x")?t.substr(2):"string"==typeof t?u(new r(t)):(+t).toString(16),f(t,e)},s=function(t){return i.fromAscii(t,o.ETH_PADDING).substr(2)},c=function(t){return"000000000000000000000000000000000000000000000000000000000000000"+(t?"1":"0")},a=function(t){return u(new r(t).times(new r(2).pow(128)))},l=function(t){return"1"===new r(t.substr(0,1),16).toString(2).substr(0,1)},p=function(t){return t=t||"0",l(t)?new r(t,16).minus(new r("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",16)).minus(1):new r(t,16)},h=function(t){return t=t||"0",new r(t,16)},g=function(t){return p(t).dividedBy(new r(2).pow(128))},m=function(t){return h(t).dividedBy(new r(2).pow(128))},d=function(t){return"0x"+t},v=function(t){return"0000000000000000000000000000000000000000000000000000000000000001"===t?!0:!1},w=function(t){return i.toAscii(t)},y=function(t){return"0x"+t.slice(t.length-40,t.length)};e.exports={formatInputInt:u,formatInputString:s,formatInputBool:c,formatInputReal:a,formatOutputInt:p,formatOutputUInt:h,formatOutputReal:g,formatOutputUReal:m,formatOutputHash:d,formatOutputBool:v,formatOutputString:w,formatOutputAddress:y}}).call(this,t("_process"))},{"./const":4,"./utils":7,_process:2,"bignumber.js":8}],6:[function(t,e){var n=t("./formatters"),r=function(t){return function(e){return 0===e.indexOf(t)}},i=function(t){return function(e){return t===e}},o=function(){return[{type:r("uint"),format:n.formatInputInt},{type:r("int"),format:n.formatInputInt},{type:r("hash"),format:n.formatInputInt},{type:r("string"),format:n.formatInputString},{type:r("real"),format:n.formatInputReal},{type:r("ureal"),format:n.formatInputReal},{type:i("address"),format:n.formatInputInt},{type:i("bool"),format:n.formatInputBool}]},f=function(){return[{type:r("uint"),format:n.formatOutputUInt},{type:r("int"),format:n.formatOutputInt},{type:r("hash"),format:n.formatOutputHash},{type:r("string"),format:n.formatOutputString},{type:r("real"),format:n.formatOutputReal},{type:r("ureal"),format:n.formatOutputUReal},{type:i("address"),format:n.formatOutputAddress},{type:i("bool"),format:n.formatOutputBool}]};e.exports={prefixedType:r,namedType:i,inputTypes:o,outputTypes:f}},{"./formatters":5}],7:[function(t,e){var n=t("./const"),r=function(t,e){for(var n=!1,r=0;rn;n+=2){var i=parseInt(t.substr(n,2),16);if(0===i)break;e+=String.fromCharCode(i)}return e},o=function(t){for(var e="",n=0;n3e3&&rr?"i":"").test(c))return m(a,c,u,r);u?(a.s=0>1/t?(c=c.slice(1),-1):1,$&&c.replace(/^0\.0*|\./,"").length>15&&U(L,O,t),u=!1):a.s=45===c.charCodeAt(0)?(c=c.slice(1),-1):1,c=n(c,10,r,a.s)}else{if(t instanceof e)return a.s=t.s,a.e=t.e,a.c=(t=t.c)?t.slice():t,void(L=0);if((u="number"==typeof t)&&0*t==0){if(a.s=0>1/t?(t=-t,-1):1,t===~~t){for(o=0,f=t;f>=10;f/=10,o++);return a.e=o,a.c=[t],void(L=0)}c=t+""}else{if(!d.test(c=t+""))return m(a,c,u);a.s=45===c.charCodeAt(0)?(c=c.slice(1),-1):1}}for((o=c.indexOf("."))>-1&&(c=c.replace(".","")),(f=c.search(/e/i))>0?(0>o&&(o=f),o+=+c.slice(f+1),c=c.substring(0,f)):0>o&&(o=c.length),f=0;48===c.charCodeAt(f);f++);for(s=c.length;48===c.charCodeAt(--s););if(c=c.slice(f,s+1))if(s=c.length,u&&$&&s>15&&U(L,O,a.s*t),o=o-f-1,o>q)a.c=a.e=null;else if(k>o)a.c=[a.e=0];else{if(a.e=o,a.c=[],f=(o+1)%I,0>o&&(f+=I),s>f){for(f&&a.c.push(+c.slice(0,f)),s-=I;s>f;)a.c.push(+c.slice(f,f+=I));c=c.slice(f),f=I-c.length}else f-=s;for(;f--;c+="0");a.c.push(+c)}else a.c=[a.e=0];L=0}function n(t,n,r,i){var f,u,s,a,p,h,g,m=t.indexOf("."),d=B,v=H;for(37>r&&(t=t.toLowerCase()),m>=0&&(s=Y,Y=0,t=t.replace(".",""),g=new e(r),p=g.pow(t.length-m),Y=s,g.c=c(l(o(p.c),p.e),10,n),g.e=g.c.length),h=c(t,r,n),u=s=h.length;0==h[--s];h.pop());if(!h[0])return"0";if(0>m?--u:(p.c=h,p.e=u,p.s=i,p=G(p,g,d,v,n),h=p.c,a=p.r,u=p.e),f=u+d+1,m=h[f],s=n/2,a=a||0>f||null!=h[f+1],a=4>v?(null!=m||a)&&(0==v||v==(p.s<0?3:2)):m>s||m==s&&(4==v||a||6==v&&1&h[f-1]||v==(p.s<0?8:7)),1>f||!h[0])t=a?l("1",-d):"0";else{if(h.length=f,a)for(--n;++h[--f]>n;)h[f]=0,f||(++u,h.unshift(1));for(s=h.length;!h[--s];);for(m=0,t="";s>=m;t+=N.charAt(h[m++]));t=l(t,u)}return t}function h(t,n,r,i){var f,u,s,c,p;if(r=null!=r&&z(r,0,8,i,b)?0|r:H,!t.c)return t.toString();if(f=t.c[0],s=t.e,null==n)p=o(t.c),p=19==i||24==i&&C>=s?a(p,s):l(p,s);else if(t=F(new e(t),n,r),u=t.e,p=o(t.c),c=p.length,19==i||24==i&&(u>=n||C>=u)){for(;n>c;p+="0",c++);p=a(p,u)}else if(n-=s,p=l(p,u),u+1>c){if(--n>0)for(p+=".";n--;p+="0");}else if(n+=u-c,n>0)for(u+1==c&&(p+=".");n--;p+="0");return t.s<0&&f?"-"+p:p}function S(t,n){var r,i,o=0;for(s(t[0])&&(t=t[0]),r=new e(t[0]);++ot||t>n||t!=p(t))&&U(r,(i||"decimal places")+(e>t||t>n?" out of range":" not an integer"),t),!0}function R(t,e,n){for(var r=1,i=e.length;!e[--i];e.pop());for(i=e[0];i>=10;i/=10,r++);return(n=r+n*I-1)>q?t.c=t.e=null:k>n?t.c=[t.e=0]:(t.e=n,t.c=e),t}function U(t,e,n){var r=new Error(["new BigNumber","cmp","config","div","divToInt","eq","gt","gte","lt","lte","minus","mod","plus","precision","random","round","shift","times","toDigits","toExponential","toFixed","toFormat","toFraction","pow","toPrecision","toString","BigNumber"][t]+"() "+e+": "+n);throw r.name="BigNumber Error",L=0,r}function F(t,e,n,r){var i,o,f,u,s,c,a,l=t.c,p=_;if(l){t:{for(i=1,u=l[0];u>=10;u/=10,i++);if(o=e-i,0>o)o+=I,f=e,s=l[c=0],a=s/p[i-f-1]%10|0;else if(c=v((o+1)/I),c>=l.length){if(!r)break t;for(;l.length<=c;l.push(0));s=a=0,i=1,o%=I,f=o-I+1}else{for(s=u=l[c],i=1;u>=10;u/=10,i++);o%=I,f=o-I+i,a=0>f?0:s/p[i-f-1]%10|0}if(r=r||0>e||null!=l[c+1]||(0>f?s:s%p[i-f-1]),r=4>n?(a||r)&&(0==n||n==(t.s<0?3:2)):a>5||5==a&&(4==n||r||6==n&&(o>0?f>0?s/p[i-f]:0:l[c-1])%10&1||n==(t.s<0?8:7)),1>e||!l[0])return l.length=0,r?(e-=t.e+1,l[0]=p[e%I],t.e=-e||0):l[0]=t.e=0,t;if(0==o?(l.length=c,u=1,c--):(l.length=c+1,u=p[I-o],l[c]=f>0?w(s/p[i-f]%p[f])*u:0),r)for(;;){if(0==c){for(o=1,f=l[0];f>=10;f/=10,o++);for(f=l[0]+=u,u=1;f>=10;f/=10,u++);o!=u&&(t.e++,l[0]==E&&(l[0]=1));break}if(l[c]+=u,l[c]!=E)break;l[c--]=0,u=1}for(o=l.length;0===l[--o];l.pop());}t.e>q?t.c=t.e=null:t.en?null!=(t=i[n++]):void 0};return f(e="DECIMAL_PLACES")&&z(t,0,D,2,e)&&(B=0|t),r[e]=B,f(e="ROUNDING_MODE")&&z(t,0,8,2,e)&&(H=0|t),r[e]=H,f(e="EXPONENTIAL_AT")&&(s(t)?z(t[0],-D,0,2,e)&&z(t[1],0,D,2,e)&&(C=0|t[0],j=0|t[1]):z(t,-D,D,2,e)&&(C=-(j=0|(0>t?-t:t)))),r[e]=[C,j],f(e="RANGE")&&(s(t)?z(t[0],-D,-1,2,e)&&z(t[1],1,D,2,e)&&(k=0|t[0],q=0|t[1]):z(t,-D,D,2,e)&&(0|t?k=-(q=0|(0>t?-t:t)):$&&U(2,e+" cannot be zero",t))),r[e]=[k,q],f(e="ERRORS")&&(t===!!t||1===t||0===t?(L=0,z=($=!!t)?A:u):$&&U(2,e+y,t)),r[e]=$,f(e="CRYPTO")&&(t===!!t||1===t||0===t?(V=!(!t||!g||"object"!=typeof g),t&&!V&&$&&U(2,"crypto unavailable",g)):$&&U(2,e+y,t)),r[e]=V,f(e="MODULO_MODE")&&z(t,0,9,2,e)&&(W=0|t),r[e]=W,f(e="POW_PRECISION")&&z(t,0,D,2,e)&&(Y=0|t),r[e]=Y,f(e="FORMAT")&&("object"==typeof t?Z=t:$&&U(2,e+" not an object",t)),r[e]=Z,r},e.max=function(){return S(arguments,M.lt)},e.min=function(){return S(arguments,M.gt)},e.random=function(){var t=9007199254740992,n=Math.random()*t&2097151?function(){return w(Math.random()*t)}:function(){return 8388608*(1073741824*Math.random()|0)+(8388608*Math.random()|0)};return function(t){var r,i,o,f,u,s=0,c=[],a=new e(P);if(t=null!=t&&z(t,0,D,14)?0|t:B,f=v(t/I),V)if(g&&g.getRandomValues){for(r=g.getRandomValues(new Uint32Array(f*=2));f>s;)u=131072*r[s]+(r[s+1]>>>11),u>=9e15?(i=g.getRandomValues(new Uint32Array(2)),r[s]=i[0],r[s+1]=i[1]):(c.push(u%1e14),s+=2);s=f/2}else if(g&&g.randomBytes){for(r=g.randomBytes(f*=7);f>s;)u=281474976710656*(31&r[s])+1099511627776*r[s+1]+4294967296*r[s+2]+16777216*r[s+3]+(r[s+4]<<16)+(r[s+5]<<8)+r[s+6],u>=9e15?g.randomBytes(7).copy(r,s):(c.push(u%1e14),s+=7);s=f/7}else $&&U(14,"crypto unavailable",g);if(!s)for(;f>s;)u=n(),9e15>u&&(c[s++]=u%1e14);for(f=c[--s],t%=I,f&&t&&(u=_[I-t],c[s]=w(f/u)*u);0===c[s];c.pop(),s--);if(0>s)c=[o=0];else{for(o=-1;0===c[0];c.shift(),o-=I);for(s=1,u=c[0];u>=10;u/=10,s++);I>s&&(o-=I-s)}return a.e=o,a.c=c,a}}(),G=function(){function t(t,e,n){var r,i,o,f,u=0,s=t.length,c=e%T,a=e/T|0;for(t=t.slice();s--;)o=t[s]%T,f=t[s]/T|0,r=a*o+f*c,i=c*o+r%T*T+u,u=(i/n|0)+(r/T|0)+a*f,t[s]=i%n;return u&&t.unshift(u),t}function n(t,e,n,r){var i,o;if(n!=r)o=n>r?1:-1;else for(i=o=0;n>i;i++)if(t[i]!=e[i]){o=t[i]>e[i]?1:-1;break}return o}function r(t,e,n,r){for(var i=0;n--;)t[n]-=i,i=t[n]1;t.shift());}return function(o,f,u,s,c){var a,l,p,h,g,m,d,v,y,b,O,N,x,_,T,D,S,A=o.s==f.s?1:-1,R=o.c,U=f.c;if(!(R&&R[0]&&U&&U[0]))return new e(o.s&&f.s&&(R?!U||R[0]!=U[0]:U)?R&&0==R[0]||!U?0*A:A/0:0/0);for(v=new e(A),y=v.c=[],l=o.e-f.e,A=u+l+1,c||(c=E,l=i(o.e/I)-i(f.e/I),A=A/I|0),p=0;U[p]==(R[p]||0);p++);if(U[p]>(R[p]||0)&&l--,0>A)y.push(1),h=!0;else{for(_=R.length,D=U.length,p=0,A+=2,g=w(c/(U[0]+1)),g>1&&(U=t(U,g,c),R=t(R,g,c),D=U.length,_=R.length),x=D,b=R.slice(0,D),O=b.length;D>O;b[O++]=0);S=U.slice(),S.unshift(0),T=U[0],U[1]>=c/2&&T++;do g=0,a=n(U,b,D,O),0>a?(N=b[0],D!=O&&(N=N*c+(b[1]||0)),g=w(N/T),g>1?(g>=c&&(g=c-1),m=t(U,g,c),d=m.length,O=b.length,a=n(m,b,d,O),1==a&&(g--,r(m,d>D?S:U,d,c))):(0==g&&(a=g=1),m=U.slice()),d=m.length,O>d&&m.unshift(0),r(b,m,O,c),-1==a&&(O=b.length,a=n(U,b,D,O),1>a&&(g++,r(b,O>D?S:U,O,c))),O=b.length):0===a&&(g++,b=[0]),y[p++]=g,a&&b[0]?b[O++]=R[x]||0:(b=[R[x]],O=1);while((x++<_||null!=b[0])&&A--);h=null!=b[0],y[0]||y.shift()}if(c==E){for(p=1,A=y[0];A>=10;A/=10,p++);F(v,u+(v.e=p+l*I-1)+1,s,h)}else v.e=l,v.r=+h;return v}}(),m==function(){var t=/^(-?)0([xbo])(\w[\w.]*$)/i,n=/^([^.]+)\.$/,r=/^\.([^.]+)$/,i=/^-?(Infinity|NaN)$/,o=/^\s*\+([\w.])|^\s+|\s+$/g;return function(f,u,s,c){var a,l=s?u:u.replace(o,"$1");if(i.test(l))f.s=isNaN(l)?null:0>l?-1:1;else{if(!s&&(l=l.replace(t,function(t,e,n){return a="x"==(n=n.toLowerCase())?16:"b"==n?2:8,c&&c!=a?t:e}),c&&(a=c,l=l.replace(n,"$1").replace(r,"0.$1")),u!=l))return new e(l,a);$&&U(L,"not a"+(c?" base "+c:"")+" number",u),f.s=null}f.c=f.e=null,L=0}}(),M.absoluteValue=M.abs=function(){var t=new e(this);return t.s<0&&(t.s=1),t},M.ceil=function(){return F(new e(this),this.e+1,2)},M.comparedTo=M.cmp=function(t,n){return L=1,f(this,new e(t,n))},M.decimalPlaces=M.dp=function(){var t,e,n=this.c;if(!n)return null;if(t=((e=n.length-1)-i(this.e/I))*I,e=n[e])for(;e%10==0;e/=10,t--);return 0>t&&(t=0),t},M.dividedBy=M.div=function(t,n){return L=3,G(this,new e(t,n),B,H)},M.dividedToIntegerBy=M.divToInt=function(t,n){return L=4,G(this,new e(t,n),0,1)},M.equals=M.eq=function(t,n){return L=5,0===f(this,new e(t,n))},M.floor=function(){return F(new e(this),this.e+1,3)},M.greaterThan=M.gt=function(t,n){return L=6,f(this,new e(t,n))>0},M.greaterThanOrEqualTo=M.gte=function(t,n){return L=7,1===(n=f(this,new e(t,n)))||0===n},M.isFinite=function(){return!!this.c},M.isInteger=M.isInt=function(){return!!this.c&&i(this.e/I)>this.c.length-2},M.isNaN=function(){return!this.s},M.isNegative=M.isNeg=function(){return this.s<0},M.isZero=function(){return!!this.c&&0==this.c[0]},M.lessThan=M.lt=function(t,n){return L=8,f(this,new e(t,n))<0},M.lessThanOrEqualTo=M.lte=function(t,n){return L=9,-1===(n=f(this,new e(t,n)))||0===n},M.minus=M.sub=function(t,n){var r,o,f,u,s=this,c=s.s;if(L=10,t=new e(t,n),n=t.s,!c||!n)return new e(0/0);if(c!=n)return t.s=-n,s.plus(t);var a=s.e/I,l=t.e/I,p=s.c,h=t.c;if(!a||!l){if(!p||!h)return p?(t.s=-n,t):new e(h?s:0/0);if(!p[0]||!h[0])return h[0]?(t.s=-n,t):new e(p[0]?s:3==H?-0:0)}if(a=i(a),l=i(l),p=p.slice(),c=a-l){for((u=0>c)?(c=-c,f=p):(l=a,f=h),f.reverse(),n=c;n--;f.push(0));f.reverse()}else for(o=(u=(c=p.length)<(n=h.length))?c:n,c=n=0;o>n;n++)if(p[n]!=h[n]){u=p[n]0)for(;n--;p[r++]=0);for(n=E-1;o>c;){if(p[--o]0?(s=u,r=a):(f=-f,r=c),r.reverse();f--;r.push(0));r.reverse()}for(f=c.length,n=a.length,0>f-n&&(r=a,a=c,c=r,n=f),f=0;n;)f=(c[--n]=c[n]+a[n]+f)/E|0,c[n]%=E;return f&&(c.unshift(f),++s),R(t,c,s)},M.precision=M.sd=function(t){var e,n,r=this,i=r.c;if(null!=t&&t!==!!t&&1!==t&&0!==t&&($&&U(13,"argument"+y,t),t!=!!t&&(t=null)),!i)return null;if(n=i.length-1,e=n*I+1,n=i[n]){for(;n%10==0;n/=10,e--);for(n=i[0];n>=10;n/=10,e++);}return t&&r.e+1>e&&(e=r.e+1),e},M.round=function(t,n){var r=new e(this);return(null==t||z(t,0,D,15))&&F(r,~~t+this.e+1,null!=n&&z(n,0,8,15,b)?0|n:H),r},M.shift=function(t){var n=this;return z(t,-x,x,16,"argument")?n.times("1e"+p(t)):new e(n.c&&n.c[0]&&(-x>t||t>x)?n.s*(0>t?0:1/0):n)},M.squareRoot=M.sqrt=function(){var t,n,r,f,u,s=this,c=s.c,a=s.s,l=s.e,p=B+4,h=new e("0.5");if(1!==a||!c||!c[0])return new e(!a||0>a&&(!c||c[0])?0/0:c?s:1/0);if(a=Math.sqrt(+s),0==a||a==1/0?(n=o(c),(n.length+l)%2==0&&(n+="0"),a=Math.sqrt(n),l=i((l+1)/2)-(0>l||l%2),a==1/0?n="1e"+l:(n=a.toExponential(),n=n.slice(0,n.indexOf("e")+1)+l),r=new e(n)):r=new e(a+""),r.c[0])for(l=r.e,a=l+p,3>a&&(a=0);;)if(u=r,r=h.times(u.plus(G(s,u,p,1))),o(u.c).slice(0,a)===(n=o(r.c)).slice(0,a)){if(r.ea&&(d=b,b=O,O=d,f=a,a=h,h=f),f=a+h,d=[];f--;d.push(0));for(v=E,w=T,f=h;--f>=0;){for(r=0,g=O[f]%w,m=O[f]/w|0,s=a,u=f+s;u>f;)l=b[--s]%w,p=b[s]/w|0,c=m*l+p*g,l=g*l+c%w*w+d[u]+r,r=(l/v|0)+(c/w|0)+m*p,d[u--]=l%v;d[u]=r}return r?++o:d.shift(),R(t,d,o)},M.toDigits=function(t,n){var r=new e(this);return t=null!=t&&z(t,1,D,18,"precision")?0|t:null,n=null!=n&&z(n,0,8,18,b)?0|n:H,t?F(r,t,n):r},M.toExponential=function(t,e){return h(this,null!=t&&z(t,0,D,19)?~~t+1:null,e,19)},M.toFixed=function(t,e){return h(this,null!=t&&z(t,0,D,20)?~~t+this.e+1:null,e,20)},M.toFormat=function(t,e){var n=h(this,null!=t&&z(t,0,D,21)?~~t+this.e+1:null,e,21);if(this.c){var r,i=n.split("."),o=+Z.groupSize,f=+Z.secondaryGroupSize,u=Z.groupSeparator,s=i[0],c=i[1],a=this.s<0,l=a?s.slice(1):s,p=l.length;if(f&&(r=o,o=f,f=r,p-=r),o>0&&p>0){for(r=p%o||o,s=l.substr(0,r);p>r;r+=o)s+=u+l.substr(r,o);f>0&&(s+=u+l.slice(r)),a&&(s="-"+s)}n=c?s+Z.decimalSeparator+((f=+Z.fractionGroupSize)?c.replace(new RegExp("\\d{"+f+"}\\B","g"),"$&"+Z.fractionGroupSeparator):c):s}return n},M.toFraction=function(t){var n,r,i,f,u,s,c,a,l,p=$,h=this,g=h.c,m=new e(P),d=r=new e(P),v=c=new e(P);if(null!=t&&($=!1,s=new e(t),$=p,(!(p=s.isInt())||s.lt(P))&&($&&U(22,"max denominator "+(p?"out of range":"not an integer"),t),t=!p&&s.c&&F(s,s.e+1,1).gte(P)?s:null)),!g)return h.toString();for(l=o(g),f=m.e=l.length-h.e-1,m.c[0]=_[(u=f%I)<0?I+u:u],t=!t||s.cmp(m)>0?f>0?m:d:s,u=q,q=1/0,s=new e(l),c.c[0]=0;a=G(s,m,0,1),i=r.plus(a.times(v)),1!=i.cmp(t);)r=v,v=i,d=c.plus(a.times(i=d)),c=i,m=s.minus(a.times(i=m)),s=i;return i=G(t.minus(r),v,0,1),c=c.plus(i.times(d)),r=r.plus(i.times(v)),c.s=d.s=h.s,f*=2,n=G(d,v,f,H).minus(h).abs().cmp(G(c,r,f,H).minus(h).abs())<1?[d.toString(),v.toString()]:[c.toString(),r.toString()],q=u,n},M.toNumber=function(){var t=this;return+t||(t.s?0*t.s:0/0)},M.toPower=M.pow=function(t){var n,r,i=w(0>t?-t:+t),o=this;if(!z(t,-x,x,23,"exponent")&&(!isFinite(t)||i>x&&(t/=0)||parseFloat(t)!=t&&!(t=0/0)))return new e(Math.pow(+o,t));for(n=Y?v(Y/I+2):0,r=new e(P);;){if(i%2){if(r=r.times(o),!r.c)break;n&&r.c.length>n&&(r.c.length=n)}if(i=w(i/2),!i)break;o=o.times(o),n&&o.c&&o.c.length>n&&(o.c.length=n)}return 0>t&&(r=P.div(r)),n?F(r,Y,H):r},M.toPrecision=function(t,e){return h(this,null!=t&&z(t,1,D,24,"precision")?0|t:null,e,24)},M.toString=function(t){var e,r=this,i=r.s,f=r.e;return null===f?i?(e="Infinity",0>i&&(e="-"+e)):e="NaN":(e=o(r.c),e=null!=t&&z(t,2,64,25,"base")?n(l(e,f),0|t,10,i):C>=f||f>=j?a(e,f):l(e,f),0>i&&r.c[0]&&(e="-"+e)),e},M.truncated=M.trunc=function(){return F(new e(this),this.e+1,1)},M.valueOf=M.toJSON=function(){return this.toString()},null!=t&&e.config(t),e}function i(t){var e=0|t;return t>0||t===e?e:e-1}function o(t){for(var e,n,r=1,i=t.length,o=t[0]+"";i>r;){for(e=t[r++]+"",n=I-e.length;n--;e="0"+e);o+=e}for(i=o.length;48===o.charCodeAt(--i););return o.slice(0,i+1||1)}function f(t,e){var n,r,i=t.c,o=e.c,f=t.s,u=e.s,s=t.e,c=e.e;if(!f||!u)return null;if(n=i&&!i[0],r=o&&!o[0],n||r)return n?r?0:-u:f;if(f!=u)return f;if(n=0>f,r=s==c,!i||!o)return r?0:!i^n?1:-1;if(!r)return s>c^n?1:-1;for(u=(s=i.length)<(c=o.length)?s:c,f=0;u>f;f++)if(i[f]!=o[f])return i[f]>o[f]^n?1:-1;return s==c?0:s>c^n?1:-1}function u(t,e,n){return(t=p(t))>=e&&n>=t}function s(t){return"[object Array]"==Object.prototype.toString.call(t)}function c(t,e,n){for(var r,i,o=[0],f=0,u=t.length;u>f;){for(i=o.length;i--;o[i]*=e);for(o[r=0]+=N.indexOf(t.charAt(f++));rn-1&&(null==o[r+1]&&(o[r+1]=0),o[r+1]+=o[r]/n|0,o[r]%=n)}return o.reverse()}function a(t,e){return(t.length>1?t.charAt(0)+"."+t.slice(1):t)+(0>e?"e":"e+")+e}function l(t,e){var n,r;if(0>e){for(r="0.";++e;r+="0");t=r+t}else if(n=t.length,++e>n){for(r="0",e-=n;--e;r+="0");t+=r}else n>e&&(t=t.slice(0,e)+"."+t.slice(e));return t}function p(t){return t=parseFloat(t),0>t?v(t):w(t)}var h,g,m,d=/^-?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?$/i,v=Math.ceil,w=Math.floor,y=" not a boolean or binary digit",b="rounding mode",O="number type has more than 15 significant digits",N="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_",E=1e14,I=14,x=9007199254740991,_=[1,10,100,1e3,1e4,1e5,1e6,1e7,1e8,1e9,1e10,1e11,1e12,1e13],T=1e7,D=1e9;if(h=r(),"function"==typeof define&&define.amd)define(function(){return h});else if("undefined"!=typeof e&&e.exports){if(e.exports=h,!g)try{g=t("crypto")}catch(S){}}else n.BigNumber=h}(this)},{crypto:1}],natspec:[function(t,e){var n=t("./node_modules/ethereum.js/lib/abi.js"),r=function(){var t=function(t,e){Object.keys(t).forEach(function(n){e[n]=t[n]})},e=function(t){return Object.keys(t).reduce(function(t,e){return t+"var "+e+" = context['"+e+"'];\n"},"")},r=function(t,e){return t.filter(function(t){return t.name===e})[0]},i=function(t,e){var r=n.formatOutput(t.inputs,"0x"+e.params[0].data.slice(10));return t.inputs.reduce(function(t,e,n){return t[e.name]=r[n],t},{})},o=function(t,e){var n,r="",i=/\` + "`" + `(?:\\.|[^` + "`" + `\\])*\` + "`" + `/gim,o=0;try{for(;null!==(n=i.exec(t));){var f=i.lastIndex-n[0].length,u=n[0].slice(1,n[0].length-1);r+=t.slice(o,f);var s=e(u);r+=s,o=i.lastIndex}r+=t.slice(o)}catch(c){throw new Error("Natspec evaluation failed, wrong input params")}return r},f=function(n,f){var u={};if(f)try{var s=r(f.abi,f.method),c=i(s,f.transaction);t(c,u)}catch(a){throw new Error("Natspec evaluation failed, method does not exist")}var l=e(u),p=o(n,function(t){var e=new Function("context",l+"return "+t+";");return e(u).toString()});return p},u=function(t,e){try{return f(t,e)}catch(n){return n.message}};return{evaluateExpression:f,evaluateExpressionSafe:u}}();e.exports=r},{"./node_modules/ethereum.js/lib/abi.js":3}]},{},[]); +//# sourceMappingURL=natspec.js.map` diff --git a/common/natspec/natspec_test.go b/common/natspec/natspec_test.go new file mode 100644 index 000000000..330dc831d --- /dev/null +++ b/common/natspec/natspec_test.go @@ -0,0 +1,97 @@ +package natspec + +import ( + "testing" +) + +func TestNotice(t *testing.T) { + + tx := ` + { + "jsonrpc": "2.0", + "method": "eth_call", + "params": [{ + "to": "0x8521742d3f456bd237e312d6e30724960f72517a", + "data": "0xc6888fa1000000000000000000000000000000000000000000000000000000000000007a" + }], + "id": 6 + } + ` + + abi := ` + [{ + "name": "multiply", + "constant": false, + "type": "function", + "inputs": [{ + "name": "a", + "type": "uint256" + }], + "outputs": [{ + "name": "d", + "type": "uint256" + }] + }] + ` + + desc := "Will multiply `a` by 7 and return `a * 7`." + + method := "multiply" + + ns, err := New() + if err != nil { + t.Errorf("NewNATSpec error %v", err) + } + + notice, err := ns.Notice(tx, abi, method, desc) + + if err != nil { + t.Errorf("expected no error got %v", err) + } + + expected := "Will multiply 122 by 7 and return 854." + if notice != expected { + t.Errorf("incorrect notice. expected %v, got %v", expected, notice) + } else { + t.Logf("returned notice \"%v\"", notice) + } + + notice, err = ns.Notice(tx, abi, method, "Will multiply 122 by \"7\" and return 854.") + + expected = "natspec.js error setting expression: (anonymous): Line 1:41 Unexpected number" + + if err == nil { + t.Errorf("expected error, got nothing (notice: '%v')", err, notice) + } else { + if err.Error() != expected { + t.Errorf("expected error '%s' got '%v' (notice: '%v')", expected, err, notice) + } + } + + // https://github.com/ethereum/natspec.js/issues/1 + badDesc := "Will multiply `e` by 7 and return `a * 7`." + notice, err = ns.Notice(tx, abi, method, badDesc) + + expected = "natspec.js error evaluating expression: Error: Natspec evaluation failed, wrong input params" + + if err == nil { + t.Errorf("expected error, got nothing (notice: '%v')", notice) + } else { + if err.Error() != expected { + t.Errorf("expected error '%s' got '%v' (notice: '%v')", expected, err, notice) + } + } + + notice, err = ns.Notice(tx, abi, "missing_method", desc) + + expected = "natspec.js error evaluating expression: Error: Natspec evaluation failed, method does not exist" + + if err == nil { + t.Errorf("expected error, got nothing (notice: '%v')", notice) + } else { + if err.Error() != expected { + t.Errorf("expected error '%s' got '%v' (notice: '%v')", expected, err, notice) + } + } + +} diff --git a/common/number/int.go b/common/number/int.go new file mode 100644 index 000000000..6cc5e68b4 --- /dev/null +++ b/common/number/int.go @@ -0,0 +1,181 @@ +package number + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" +) + +var tt256 = new(big.Int).Lsh(big.NewInt(1), 256) +var tt256m1 = new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 256), big.NewInt(1)) +var tt255 = new(big.Int).Lsh(big.NewInt(1), 255) + +func limitUnsigned256(x *Number) *Number { + x.num.And(x.num, tt256m1) + return x +} + +func limitSigned256(x *Number) *Number { + if x.num.Cmp(tt255) < 0 { + return x + } else { + x.num.Sub(x.num, tt256) + return x + } +} + +// Number function +type Initialiser func(n int64) *Number + +// A Number represents a generic integer with a bounding function limiter. Limit is called after each operations +// to give "fake" bounded integers. New types of Number can be created through NewInitialiser returning a lambda +// with the new Initialiser. +type Number struct { + num *big.Int + limit func(n *Number) *Number +} + +// Returns a new initialiser for a new *Number without having to expose certain fields +func NewInitialiser(limiter func(*Number) *Number) Initialiser { + return func(n int64) *Number { + return &Number{big.NewInt(n), limiter} + } +} + +// Return a Number with a UNSIGNED limiter up to 256 bits +func Uint256(n int64) *Number { + return &Number{big.NewInt(n), limitUnsigned256} +} + +// Return a Number with a SIGNED limiter up to 256 bits +func Int256(n int64) *Number { + return &Number{big.NewInt(n), limitSigned256} +} + +// Returns a Number with a SIGNED unlimited size +func Big(n int64) *Number { + return &Number{big.NewInt(n), func(x *Number) *Number { return x }} +} + +// Sets i to sum of x+y +func (i *Number) Add(x, y *Number) *Number { + i.num.Add(x.num, y.num) + return i.limit(i) +} + +// Sets i to difference of x-y +func (i *Number) Sub(x, y *Number) *Number { + i.num.Sub(x.num, y.num) + return i.limit(i) +} + +// Sets i to product of x*y +func (i *Number) Mul(x, y *Number) *Number { + i.num.Mul(x.num, y.num) + return i.limit(i) +} + +// Sets i to the quotient prodject of x/y +func (i *Number) Div(x, y *Number) *Number { + i.num.Div(x.num, y.num) + return i.limit(i) +} + +// Sets i to x % y +func (i *Number) Mod(x, y *Number) *Number { + i.num.Mod(x.num, y.num) + return i.limit(i) +} + +// Sets i to x << s +func (i *Number) Lsh(x *Number, s uint) *Number { + i.num.Lsh(x.num, s) + return i.limit(i) +} + +// Sets i to x^y +func (i *Number) Pow(x, y *Number) *Number { + i.num.Exp(x.num, y.num, big.NewInt(0)) + return i.limit(i) +} + +// Setters + +// Set x to i +func (i *Number) Set(x *Number) *Number { + i.num.Set(x.num) + return i.limit(i) +} + +// Set x bytes to i +func (i *Number) SetBytes(x []byte) *Number { + i.num.SetBytes(x) + return i.limit(i) +} + +// Cmp compares x and y and returns: +// +// -1 if x < y +// 0 if x == y +// +1 if x > y +func (i *Number) Cmp(x *Number) int { + return i.num.Cmp(x.num) +} + +// Getters + +// Returns the string representation of i +func (i *Number) String() string { + return i.num.String() +} + +// Returns the byte representation of i +func (i *Number) Bytes() []byte { + return i.num.Bytes() +} + +// Uint64 returns the Uint64 representation of x. If x cannot be represented in an int64, the result is undefined. +func (i *Number) Uint64() uint64 { + return i.num.Uint64() +} + +// Int64 returns the int64 representation of x. If x cannot be represented in an int64, the result is undefined. +func (i *Number) Int64() int64 { + return i.num.Int64() +} + +// Returns the signed version of i +func (i *Number) Int256() *Number { + return Int(0).Set(i) +} + +// Returns the unsigned version of i +func (i *Number) Uint256() *Number { + return Uint(0).Set(i) +} + +// Returns the index of the first bit that's set to 1 +func (i *Number) FirstBitSet() int { + for j := 0; j < i.num.BitLen(); j++ { + if i.num.Bit(j) > 0 { + return j + } + } + + return i.num.BitLen() +} + +// Variables + +var ( + Zero = Uint(0) + One = Uint(1) + Two = Uint(2) + MaxUint256 = Uint(0).SetBytes(common.Hex2Bytes("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) + + MinOne = Int(-1) + + // "typedefs" + Uint = Uint256 + Int = Int256 +) diff --git a/common/number/uint_test.go b/common/number/uint_test.go new file mode 100644 index 000000000..262d584ed --- /dev/null +++ b/common/number/uint_test.go @@ -0,0 +1,92 @@ +package number + +import ( + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" +) + +func TestSet(t *testing.T) { + a := Uint(0) + b := Uint(10) + a.Set(b) + if a.num.Cmp(b.num) != 0 { + t.Error("didn't compare", a, b) + } + + c := Uint(0).SetBytes(common.Hex2Bytes("0a")) + if c.num.Cmp(big.NewInt(10)) != 0 { + t.Error("c set bytes failed.") + } +} + +func TestInitialiser(t *testing.T) { + check := false + init := NewInitialiser(func(x *Number) *Number { + check = true + return x + }) + a := init(0).Add(init(1), init(2)) + if a.Cmp(init(3)) != 0 { + t.Error("expected 3. got", a) + } + if !check { + t.Error("expected limiter to be called") + } +} + +func TestGet(t *testing.T) { + a := Uint(10) + if a.Uint64() != 10 { + t.Error("expected to get 10. got", a.Uint64()) + } + + a = Uint(10) + if a.Int64() != 10 { + t.Error("expected to get 10. got", a.Int64()) + } +} + +func TestCmp(t *testing.T) { + a := Uint(10) + b := Uint(10) + c := Uint(11) + + if a.Cmp(b) != 0 { + t.Error("a b == 0 failed", a, b) + } + + if a.Cmp(c) >= 0 { + t.Error("a c < 0 failed", a, c) + } + + if c.Cmp(b) <= 0 { + t.Error("c b > 0 failed", c, b) + } +} + +func TestMaxArith(t *testing.T) { + a := Uint(0).Add(MaxUint256, One) + if a.Cmp(Zero) != 0 { + t.Error("expected max256 + 1 = 0 got", a) + } + + a = Uint(0).Sub(Uint(0), One) + if a.Cmp(MaxUint256) != 0 { + t.Error("expected 0 - 1 = max256 got", a) + } + + a = Int(0).Sub(Int(0), One) + if a.Cmp(MinOne) != 0 { + t.Error("expected 0 - 1 = -1 got", a) + } +} + +func TestConversion(t *testing.T) { + a := Int(-1) + b := a.Uint256() + if b.Cmp(MaxUint256) != 0 { + t.Error("expected -1 => unsigned to return max. got", b) + } +} diff --git a/common/package.go b/common/package.go new file mode 100644 index 000000000..7f38d8e4d --- /dev/null +++ b/common/package.go @@ -0,0 +1,123 @@ +package common + +import ( + "archive/zip" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "strings" +) + +// Manifest object +// +// The manifest object holds all the relevant information supplied with the +// the manifest specified in the package +type Manifest struct { + Entry string + Height, Width int +} + +// External package +// +// External package contains the main html file and manifest +type ExtPackage struct { + EntryHtml string + Manifest *Manifest +} + +// Read file +// +// Read a given compressed file and returns the read bytes. +// Returns an error otherwise +func ReadFile(f *zip.File) ([]byte, error) { + rc, err := f.Open() + if err != nil { + return nil, err + } + defer rc.Close() + + content, err := ioutil.ReadAll(rc) + if err != nil { + return nil, err + } + + return content, nil +} + +// Reads manifest +// +// Reads and returns a manifest object. Returns error otherwise +func ReadManifest(m []byte) (*Manifest, error) { + var manifest Manifest + + dec := json.NewDecoder(strings.NewReader(string(m))) + if err := dec.Decode(&manifest); err == io.EOF { + } else if err != nil { + return nil, err + } + + return &manifest, nil +} + +// Find file in archive +// +// Returns the index of the given file name if it exists. -1 if file not found +func FindFileInArchive(fn string, files []*zip.File) (index int) { + index = -1 + // Find the manifest first + for i, f := range files { + if f.Name == fn { + index = i + } + } + + return +} + +// Open package +// +// Opens a prepared ethereum package +// Reads the manifest file and determines file contents and returns and +// the external package. +func OpenPackage(fn string) (*ExtPackage, error) { + r, err := zip.OpenReader(fn) + if err != nil { + return nil, err + } + defer r.Close() + + manifestIndex := FindFileInArchive("manifest.json", r.File) + + if manifestIndex < 0 { + return nil, fmt.Errorf("No manifest file found in archive") + } + + f, err := ReadFile(r.File[manifestIndex]) + if err != nil { + return nil, err + } + + manifest, err := ReadManifest(f) + if err != nil { + return nil, err + } + + if manifest.Entry == "" { + return nil, fmt.Errorf("Entry file specified but appears to be empty: %s", manifest.Entry) + } + + entryIndex := FindFileInArchive(manifest.Entry, r.File) + if entryIndex < 0 { + return nil, fmt.Errorf("Entry file not found: '%s'", manifest.Entry) + } + + f, err = ReadFile(r.File[entryIndex]) + if err != nil { + return nil, err + } + + extPackage := &ExtPackage{string(f), manifest} + + return extPackage, nil +} diff --git a/common/path.go b/common/path.go new file mode 100644 index 000000000..680c8366c --- /dev/null +++ b/common/path.go @@ -0,0 +1,68 @@ +package common + +import ( + "io/ioutil" + "os" + "os/user" + "path" + "strings" +) + +func ExpandHomePath(p string) (path string) { + path = p + + // Check in case of paths like "/something/~/something/" + if len(path) > 1 && path[:2] == "~/" { + usr, _ := user.Current() + dir := usr.HomeDir + + path = strings.Replace(p, "~", dir, 1) + } + + 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_TRUNC|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 +} + +func AbsolutePath(Datadir string, filename string) string { + if path.IsAbs(filename) { + return filename + } + return path.Join(Datadir, filename) +} diff --git a/common/path_test.go b/common/path_test.go new file mode 100644 index 000000000..4af1bd7af --- /dev/null +++ b/common/path_test.go @@ -0,0 +1,51 @@ +package common + +import ( + // "os" + "testing" +) + +func TestGoodFile(t *testing.T) { + goodpath := "~/goethereumtest.pass" + path := ExpandHomePath(goodpath) + contentstring := "3.14159265358979323846" + + err := WriteFile(path, []byte(contentstring)) + if err != nil { + t.Error("Could not write file") + } + + if !FileExist(path) { + t.Error("File not found at", path) + } + + v, err := ReadAllFile(path) + if err != nil { + t.Error("Could not read file", path) + } + if v != contentstring { + t.Error("Expected", contentstring, "Got", v) + } + +} + +func TestBadFile(t *testing.T) { + badpath := "/this/path/should/not/exist/goethereumtest.fail" + path := ExpandHomePath(badpath) + contentstring := "3.14159265358979323846" + + err := WriteFile(path, []byte(contentstring)) + if err == nil { + t.Error("Wrote file, but should not be able to", path) + } + + if FileExist(path) { + t.Error("Found file, but should not be able to", path) + } + + v, err := ReadAllFile(path) + if err == nil { + t.Error("Read file, but should not be able to", v) + } + +} diff --git a/common/rlp.go b/common/rlp.go new file mode 100644 index 000000000..602f13202 --- /dev/null +++ b/common/rlp.go @@ -0,0 +1,276 @@ +package common + +import ( + "bytes" + "fmt" + "math/big" + "reflect" +) + +type RlpEncode interface { + RlpEncode() []byte +} + +type RlpEncodeDecode interface { + RlpEncode + RlpValue() []interface{} +} + +type RlpEncodable interface { + RlpData() interface{} +} + +func Rlp(encoder RlpEncode) []byte { + return encoder.RlpEncode() +} + +type RlpEncoder struct { + rlpData []byte +} + +func NewRlpEncoder() *RlpEncoder { + encoder := &RlpEncoder{} + + return encoder +} +func (coder *RlpEncoder) EncodeData(rlpData interface{}) []byte { + return Encode(rlpData) +} + +const ( + RlpEmptyList = 0x80 + RlpEmptyStr = 0x40 +) + +const rlpEof = -1 + +func Char(c []byte) int { + if len(c) > 0 { + return int(c[0]) + } + + return rlpEof +} + +func DecodeWithReader(reader *bytes.Buffer) interface{} { + var slice []interface{} + + // Read the next byte + char := Char(reader.Next(1)) + switch { + case char <= 0x7f: + return char + + case char <= 0xb7: + return reader.Next(int(char - 0x80)) + + case char <= 0xbf: + length := ReadVarInt(reader.Next(int(char - 0xb7))) + + return reader.Next(int(length)) + + case char <= 0xf7: + length := int(char - 0xc0) + for i := 0; i < length; i++ { + obj := DecodeWithReader(reader) + slice = append(slice, obj) + } + + return slice + case char <= 0xff: + length := ReadVarInt(reader.Next(int(char - 0xf7))) + for i := uint64(0); i < length; i++ { + obj := DecodeWithReader(reader) + slice = append(slice, obj) + } + + return slice + default: + panic(fmt.Sprintf("byte not supported: %q", char)) + } + + return slice +} + +var ( + directRlp = big.NewInt(0x7f) + numberRlp = big.NewInt(0xb7) + zeroRlp = big.NewInt(0x0) +) + +func intlen(i int64) (length int) { + for i > 0 { + i = i >> 8 + length++ + } + return +} + +func Encode(object interface{}) []byte { + var buff bytes.Buffer + + if object != nil { + switch t := object.(type) { + case *Value: + buff.Write(Encode(t.Raw())) + case RlpEncodable: + buff.Write(Encode(t.RlpData())) + // Code dup :-/ + case int: + buff.Write(Encode(big.NewInt(int64(t)))) + case uint: + buff.Write(Encode(big.NewInt(int64(t)))) + case int8: + buff.Write(Encode(big.NewInt(int64(t)))) + case int16: + buff.Write(Encode(big.NewInt(int64(t)))) + case int32: + buff.Write(Encode(big.NewInt(int64(t)))) + case int64: + buff.Write(Encode(big.NewInt(t))) + case uint16: + buff.Write(Encode(big.NewInt(int64(t)))) + case uint32: + buff.Write(Encode(big.NewInt(int64(t)))) + case uint64: + buff.Write(Encode(big.NewInt(int64(t)))) + case byte: + buff.Write(Encode(big.NewInt(int64(t)))) + case *big.Int: + // Not sure how this is possible while we check for nil + if t == nil { + buff.WriteByte(0xc0) + } else { + buff.Write(Encode(t.Bytes())) + } + case Bytes: + buff.Write(Encode([]byte(t))) + case []byte: + if len(t) == 1 && t[0] <= 0x7f { + buff.Write(t) + } else if len(t) < 56 { + buff.WriteByte(byte(len(t) + 0x80)) + buff.Write(t) + } else { + b := big.NewInt(int64(len(t))) + buff.WriteByte(byte(len(b.Bytes()) + 0xb7)) + buff.Write(b.Bytes()) + buff.Write(t) + } + case string: + buff.Write(Encode([]byte(t))) + case []interface{}: + // Inline function for writing the slice header + WriteSliceHeader := func(length int) { + if length < 56 { + buff.WriteByte(byte(length + 0xc0)) + } else { + b := big.NewInt(int64(length)) + buff.WriteByte(byte(len(b.Bytes()) + 0xf7)) + buff.Write(b.Bytes()) + } + } + + var b bytes.Buffer + for _, val := range t { + b.Write(Encode(val)) + } + WriteSliceHeader(len(b.Bytes())) + buff.Write(b.Bytes()) + default: + // This is how it should have been from the start + // needs refactoring (@fjl) + v := reflect.ValueOf(t) + switch v.Kind() { + case reflect.Slice: + var b bytes.Buffer + for i := 0; i < v.Len(); i++ { + b.Write(Encode(v.Index(i).Interface())) + } + + blen := b.Len() + if blen < 56 { + buff.WriteByte(byte(blen) + 0xc0) + } else { + ilen := byte(intlen(int64(blen))) + buff.WriteByte(ilen + 0xf7) + t := make([]byte, ilen) + for i := byte(0); i < ilen; i++ { + t[ilen-i-1] = byte(blen >> (i * 8)) + } + buff.Write(t) + } + buff.ReadFrom(&b) + } + } + } else { + // Empty list for nil + buff.WriteByte(0xc0) + } + + 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/common/rlp_test.go b/common/rlp_test.go new file mode 100644 index 000000000..16a3553d7 --- /dev/null +++ b/common/rlp_test.go @@ -0,0 +1,156 @@ +package common + +import ( + "bytes" + "math/big" + "reflect" + "testing" +) + +func TestNonInterfaceSlice(t *testing.T) { + vala := []string{"value1", "value2", "value3"} + valb := []interface{}{"value1", "value2", "value3"} + resa := Encode(vala) + resb := Encode(valb) + if !bytes.Equal(resa, resb) { + t.Errorf("expected []string & []interface{} to be equal") + } +} + +func TestRlpValueEncoding(t *testing.T) { + val := EmptyValue() + val.AppendList().Append(1).Append(2).Append(3) + val.Append("4").AppendList().Append(5) + + res := val.Encode() + exp := Encode([]interface{}{[]interface{}{1, 2, 3}, "4", []interface{}{5}}) + if bytes.Compare(res, exp) != 0 { + t.Errorf("expected %q, got %q", res, exp) + } +} + +func TestValueSlice(t *testing.T) { + val := []interface{}{ + "value1", + "valeu2", + "value3", + } + + value := NewValue(val) + splitVal := value.SliceFrom(1) + + if splitVal.Len() != 2 { + t.Error("SliceFrom: Expected len", 2, "got", splitVal.Len()) + } + + splitVal = value.SliceTo(2) + if splitVal.Len() != 2 { + t.Error("SliceTo: Expected len", 2, "got", splitVal.Len()) + } + + splitVal = value.SliceFromTo(1, 3) + if splitVal.Len() != 2 { + t.Error("SliceFromTo: Expected len", 2, "got", splitVal.Len()) + } +} + +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" { + t.Errorf("expected '%v', got '%v'", value.Get(0).Str(), "dog") + } + + if value.Get(3).Uint() != 1 { + t.Errorf("expected '%v', got '%v'", value.Get(3).Uint(), 1) + } +} + +func TestEncode(t *testing.T) { + strRes := "\x83dog" + bytes := Encode("dog") + + str := string(bytes) + if str != strRes { + t.Errorf("Expected %q, got %q", strRes, str) + } + + sliceRes := "\xcc\x83dog\x83god\x83cat" + strs := []interface{}{"dog", "god", "cat"} + bytes = Encode(strs) + slice := string(bytes) + if slice != sliceRes { + t.Error("Expected %q, got %q", sliceRes, slice) + } + + intRes := "\x82\x04\x00" + bytes = Encode(1024) + if string(bytes) != intRes { + t.Errorf("Expected %q, got %q", intRes, bytes) + } +} + +func TestDecode(t *testing.T) { + single := []byte("\x01") + b, _ := Decode(single, 0) + + if b.(uint8) != 1 { + t.Errorf("Expected 1, got %q", b) + } + + str := []byte("\x83dog") + b, _ = Decode(str, 0) + if bytes.Compare(b.([]byte), []byte("dog")) != 0 { + t.Errorf("Expected dog, got %q", b) + } + + slice := []byte("\xcc\x83dog\x83god\x83cat") + res := []interface{}{"dog", "god", "cat"} + b, _ = Decode(slice, 0) + if reflect.DeepEqual(b, res) { + t.Errorf("Expected %q, got %q", res, b) + } +} + +func TestEncodeDecodeBigInt(t *testing.T) { + bigInt := big.NewInt(1391787038) + encoded := Encode(bigInt) + + value := NewValueFromBytes(encoded) + if value.BigInt().Cmp(bigInt) != 0 { + t.Errorf("Expected %v, got %v", bigInt, value.BigInt()) + } +} + +func TestEncodeDecodeBytes(t *testing.T) { + b := NewValue([]interface{}{[]byte{1, 2, 3, 4, 5}, byte(6)}) + val := NewValueFromBytes(b.Encode()) + if !b.Cmp(val) { + t.Errorf("Expected %v, got %v", val, b) + } +} + +func TestEncodeZero(t *testing.T) { + b := NewValue(0).Encode() + exp := []byte{0xc0} + if bytes.Compare(b, exp) == 0 { + t.Error("Expected", exp, "got", b) + } +} + +func BenchmarkEncodeDecode(b *testing.B) { + for i := 0; i < b.N; i++ { + bytes := Encode([]interface{}{"dog", "god", "cat"}) + Decode(bytes, 0) + } +} diff --git a/common/size.go b/common/size.go new file mode 100644 index 000000000..80a17279b --- /dev/null +++ b/common/size.go @@ -0,0 +1,15 @@ +package common + +import "fmt" + +type StorageSize float64 + +func (self StorageSize) String() string { + if self > 1000000 { + return fmt.Sprintf("%.2f mB", self/1000000) + } else if self > 1000 { + return fmt.Sprintf("%.2f kB", self/1000) + } else { + return fmt.Sprintf("%.2f B", self) + } +} diff --git a/common/size_test.go b/common/size_test.go new file mode 100644 index 000000000..c90eabc26 --- /dev/null +++ b/common/size_test.go @@ -0,0 +1,23 @@ +package common + +import ( + checker "gopkg.in/check.v1" +) + +type SizeSuite struct{} + +var _ = checker.Suite(&SizeSuite{}) + +func (s *SizeSuite) TestStorageSizeString(c *checker.C) { + data1 := 2381273 + data2 := 2192 + data3 := 12 + + exp1 := "2.38 mB" + exp2 := "2.19 kB" + exp3 := "12.00 B" + + c.Assert(StorageSize(data1).String(), checker.Equals, exp1) + c.Assert(StorageSize(data2).String(), checker.Equals, exp2) + c.Assert(StorageSize(data3).String(), checker.Equals, exp3) +} diff --git a/common/types.go b/common/types.go new file mode 100644 index 000000000..7646b2c34 --- /dev/null +++ b/common/types.go @@ -0,0 +1,6 @@ +package common + +type ( + Hash [32]byte + Address [20]byte +) diff --git a/common/value.go b/common/value.go new file mode 100644 index 000000000..72a123772 --- /dev/null +++ b/common/value.go @@ -0,0 +1,401 @@ +package common + +import ( + "bytes" + "fmt" + "math/big" + "reflect" + "strconv" +) + +// Data values are returned by the rlp decoder. The data values represents +// one item within the rlp data structure. It's responsible for all the casting +// It always returns something valid +type Value struct { + Val interface{} + kind reflect.Value +} + +func (val *Value) String() string { + return fmt.Sprintf("%x", val.Val) +} + +func NewValue(val interface{}) *Value { + t := val + if v, ok := val.(*Value); ok { + t = v.Val + } + + return &Value{Val: t} +} + +func (val *Value) Type() reflect.Kind { + return reflect.TypeOf(val.Val).Kind() +} + +func (val *Value) IsNil() bool { + return val.Val == nil +} + +func (val *Value) Len() int { + //return val.kind.Len() + if data, ok := val.Val.([]interface{}); ok { + return len(data) + } + + return len(val.Bytes()) +} + +func (val *Value) Raw() interface{} { + return val.Val +} + +func (val *Value) Interface() interface{} { + return val.Val +} + +func (val *Value) Uint() uint64 { + if Val, ok := val.Val.(uint8); ok { + return uint64(Val) + } else if Val, ok := val.Val.(uint16); ok { + return uint64(Val) + } else if Val, ok := val.Val.(uint32); ok { + return uint64(Val) + } else if Val, ok := val.Val.(uint64); ok { + return Val + } else if Val, ok := val.Val.(float32); ok { + return uint64(Val) + } else if Val, ok := val.Val.(float64); ok { + return uint64(Val) + } else if Val, ok := val.Val.(int); ok { + return uint64(Val) + } else if Val, ok := val.Val.(uint); ok { + return uint64(Val) + } else if Val, ok := val.Val.([]byte); ok { + return new(big.Int).SetBytes(Val).Uint64() + } else if Val, ok := val.Val.(*big.Int); ok { + return Val.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() + } else if Val, ok := val.Val.(string); ok { + n, _ := strconv.Atoi(Val) + return int64(n) + } + + return 0 +} + +func (val *Value) Byte() byte { + if Val, ok := val.Val.(byte); ok { + return Val + } + + return 0x0 +} + +func (val *Value) BigInt() *big.Int { + if a, ok := val.Val.([]byte); ok { + b := new(big.Int).SetBytes(a) + + return b + } else if a, ok := val.Val.(*big.Int); ok { + return a + } else if a, ok := val.Val.(string); ok { + return Big(a) + } else { + return big.NewInt(int64(val.Uint())) + } + + return big.NewInt(0) +} + +func (val *Value) Str() string { + if a, ok := val.Val.([]byte); ok { + return string(a) + } else if a, ok := val.Val.(string); ok { + return a + } else if a, ok := val.Val.(byte); ok { + return string(a) + } + + return "" +} + +func (val *Value) Bytes() []byte { + if a, ok := val.Val.([]byte); ok { + return a + } else if s, ok := val.Val.(byte); ok { + return []byte{s} + } else if s, ok := val.Val.(string); ok { + return []byte(s) + } else if s, ok := val.Val.(*big.Int); ok { + return s.Bytes() + } else { + return big.NewInt(val.Int()).Bytes() + } + + 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 + } + + return []interface{}{} +} + +func (val *Value) SliceFrom(from int) *Value { + slice := val.Slice() + + return NewValue(slice[from:]) +} + +func (val *Value) SliceTo(to int) *Value { + slice := val.Slice() + + return NewValue(slice[:to]) +} + +func (val *Value) SliceFromTo(from, to int) *Value { + slice := val.Slice() + + return NewValue(slice[from:to]) +} + +// TODO More type checking methods +func (val *Value) IsSlice() bool { + return val.Type() == reflect.Slice +} + +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. +func (val *Value) IsList() bool { + _, ok := val.Val.([]interface{}) + + return ok +} + +func (val *Value) IsEmpty() bool { + return val.Val == nil || ((val.IsSlice() || val.IsStr()) && val.Len() == 0) +} + +// Threat the value as a slice +func (val *Value) Get(idx int) *Value { + if d, ok := val.Val.([]interface{}); ok { + // Guard for oob + if len(d) <= idx { + return NewValue(nil) + } + + if idx < 0 { + return NewValue(nil) + } + + return NewValue(d[idx]) + } + + // If this wasn't a slice you probably shouldn't be using this function + return NewValue(nil) +} + +func (self *Value) Copy() *Value { + switch val := self.Val.(type) { + case *big.Int: + return NewValue(new(big.Int).Set(val)) + case []byte: + return NewValue(CopyBytes(val)) + default: + return NewValue(self.Val) + } + + return nil +} + +func (val *Value) Cmp(o *Value) bool { + return reflect.DeepEqual(val.Val, o.Val) +} + +func (self *Value) DeepCmp(o *Value) bool { + return bytes.Compare(self.Bytes(), o.Bytes()) == 0 +} + +func (val *Value) Encode() []byte { + return Encode(val.Val) +} + +// Assume that the data we have is encoded +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 { + value := NewValue(data) + value.Decode() + + return value + } + + return NewValue(nil) +} + +// Value setters +func NewSliceValue(s interface{}) *Value { + list := EmptyValue() + + if s != nil { + if slice, ok := s.([]interface{}); ok { + for _, val := range slice { + list.Append(val) + } + } else if slice, ok := s.([]string); ok { + for _, val := range slice { + list.Append(val) + } + } + } + + return list +} + +func EmptyValue() *Value { + return NewValue([]interface{}{}) +} + +func (val *Value) AppendList() *Value { + list := EmptyValue() + val.Val = append(val.Slice(), list) + + return list +} + +func (val *Value) Append(v interface{}) *Value { + val.Val = append(val.Slice(), v) + + 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 + idx int +} + +func (val *Value) NewIterator() *ValueIterator { + return &ValueIterator{value: val} +} + +func (it *ValueIterator) Len() int { + return it.value.Len() +} + +func (it *ValueIterator) Next() bool { + if it.idx >= it.value.Len() { + return false + } + + it.currentValue = it.value.Get(it.idx) + it.idx++ + + return true +} + +func (it *ValueIterator) Value() *Value { + return it.currentValue +} + +func (it *ValueIterator) Idx() int { + return it.idx - 1 +} diff --git a/common/value_test.go b/common/value_test.go new file mode 100644 index 000000000..09d37802d --- /dev/null +++ b/common/value_test.go @@ -0,0 +1,70 @@ +package common + +import ( + "math/big" + + checker "gopkg.in/check.v1" +) + +type ValueSuite struct{} + +var _ = checker.Suite(&ValueSuite{}) + +func (s *ValueSuite) TestValueCmp(c *checker.C) { + val1 := NewValue("hello") + val2 := NewValue("world") + c.Assert(val1.Cmp(val2), checker.Equals, false) + + val3 := NewValue("hello") + val4 := NewValue("hello") + c.Assert(val3.Cmp(val4), checker.Equals, true) +} + +func (s *ValueSuite) TestValueTypes(c *checker.C) { + str := NewValue("str") + num := NewValue(1) + inter := NewValue([]interface{}{1}) + byt := NewValue([]byte{1, 2, 3, 4}) + bigInt := NewValue(big.NewInt(10)) + + strExp := "str" + numExp := uint64(1) + interExp := []interface{}{1} + bytExp := []byte{1, 2, 3, 4} + bigExp := big.NewInt(10) + + c.Assert(str.Str(), checker.Equals, strExp) + c.Assert(num.Uint(), checker.Equals, numExp) + c.Assert(NewValue(inter.Interface()).Cmp(NewValue(interExp)), checker.Equals, true) + c.Assert(byt.Bytes(), checker.DeepEquals, bytExp) + c.Assert(bigInt.BigInt(), checker.DeepEquals, bigExp) +} + +func (s *ValueSuite) TestIterator(c *checker.C) { + value := NewValue([]interface{}{1, 2, 3}) + iter := value.NewIterator() + values := []uint64{1, 2, 3} + i := 0 + for iter.Next() { + c.Assert(values[i], checker.Equals, iter.Value().Uint()) + i++ + } +} + +func (s *ValueSuite) TestMath(c *checker.C) { + data1 := NewValue(1) + data1.Add(1).Add(1) + exp1 := NewValue(3) + data2 := NewValue(2) + data2.Sub(1).Sub(1) + exp2 := NewValue(0) + + c.Assert(data1.DeepCmp(exp1), checker.Equals, true) + c.Assert(data2.DeepCmp(exp2), checker.Equals, true) +} + +func (s *ValueSuite) TestString(c *checker.C) { + data := "10" + exp := int64(10) + c.Assert(NewValue(data).Int(), checker.DeepEquals, exp) +} -- cgit From 3cf51d5479ab91cee100c4ca04d9db3a848ff8d2 Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 16 Mar 2015 12:03:52 +0100 Subject: tmp --- common/types.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'common') diff --git a/common/types.go b/common/types.go index 7646b2c34..6a9abdf18 100644 --- a/common/types.go +++ b/common/types.go @@ -1,6 +1,6 @@ package common type ( - Hash [32]byte - Address [20]byte + uHash [32]byte + uAddress [20]byte ) -- cgit