From 5c8fe28b725bd9b128edceae3215132ea741641b Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Sun, 26 Feb 2017 22:21:51 +0100 Subject: common: move big integer math to common/math (#3699) * common: remove CurrencyToString Move denomination values to params instead. * common: delete dead code * common: move big integer operations to common/math This commit consolidates all big integer operations into common/math and adds tests and documentation. There should be no change in semantics for BigPow, BigMin, BigMax, S256, U256, Exp and their behaviour is now locked in by tests. The BigD, BytesToBig and Bytes2Big functions don't provide additional value, all uses are replaced by new(big.Int).SetBytes(). BigToBytes is now called PaddedBigBytes, its minimum output size parameter is now specified as the number of bytes instead of bits. The single use of this function is in the EVM's MSTORE instruction. Big and String2Big are replaced by ParseBig, which is slightly stricter. It previously accepted leading zeros for hexadecimal inputs but treated decimal inputs as octal if a leading zero digit was present. ParseUint64 is used in places where String2Big was used to decode a uint64. The new functions MustParseBig and MustParseUint64 are now used in many places where parsing errors were previously ignored. * common: delete unused big integer variables * accounts/abi: replace uses of BytesToBig with use of encoding/binary * common: remove BytesToBig * common: remove Bytes2Big * common: remove BigTrue * cmd/utils: add BigFlag and use it for error-checked integer flags While here, remove environment variable processing for DirectoryFlag because we don't use it. * core: add missing error checks in genesis block parser * common: remove String2Big * cmd/evm: use utils.BigFlag * common/math: check for 256 bit overflow in ParseBig This is supposed to prevent silent overflow/truncation of values in the genesis block JSON. Without this check, a genesis block that set a balance larger than 256 bits would lead to weird behaviour in the VM. * cmd/utils: fixup import --- common/math/big.go | 143 ++++++++++++++++++++++++++++++++ common/math/big_test.go | 196 ++++++++++++++++++++++++++++++++++++++++++++ common/math/dist.go | 96 ---------------------- common/math/dist_test.go | 82 ------------------ common/math/exp.go | 47 ----------- common/math/integer.go | 65 +++++++++++++-- common/math/integer_test.go | 75 +++++++++++++++-- 7 files changed, 468 insertions(+), 236 deletions(-) create mode 100644 common/math/big.go create mode 100644 common/math/big_test.go delete mode 100644 common/math/dist.go delete mode 100644 common/math/dist_test.go delete mode 100644 common/math/exp.go (limited to 'common/math') diff --git a/common/math/big.go b/common/math/big.go new file mode 100644 index 000000000..c0508c102 --- /dev/null +++ b/common/math/big.go @@ -0,0 +1,143 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package math provides integer math utilities. +package math + +import ( + "math/big" +) + +var ( + tt255 = BigPow(2, 255) + tt256 = BigPow(2, 256) + tt256m1 = new(big.Int).Sub(tt256, big.NewInt(1)) + MaxBig256 = new(big.Int).Set(tt256m1) +) + +// ParseBig256 parses s as a 256 bit integer in decimal or hexadecimal syntax. +// Leading zeros are accepted. The empty string parses as zero. +func ParseBig256(s string) (*big.Int, bool) { + if s == "" { + return new(big.Int), true + } + var bigint *big.Int + var ok bool + if len(s) >= 2 && (s[:2] == "0x" || s[:2] == "0X") { + bigint, ok = new(big.Int).SetString(s[2:], 16) + } else { + bigint, ok = new(big.Int).SetString(s, 10) + } + if ok && bigint.BitLen() > 256 { + bigint, ok = nil, false + } + return bigint, ok +} + +// MustParseBig parses s as a 256 bit big integer and panics if the string is invalid. +func MustParseBig256(s string) *big.Int { + v, ok := ParseBig256(s) + if !ok { + panic("invalid 256 bit integer: " + s) + } + return v +} + +// BigPow returns a ** b as a big integer. +func BigPow(a, b int64) *big.Int { + r := big.NewInt(a) + return r.Exp(r, big.NewInt(b), nil) +} + +// BigMax returns the larger of x or y. +func BigMax(x, y *big.Int) *big.Int { + if x.Cmp(y) < 0 { + return y + } + return x +} + +// BigMin returns the smaller of x or y. +func BigMin(x, y *big.Int) *big.Int { + if x.Cmp(y) > 0 { + return y + } + return x +} + +// FirstBitSet returns the index of the first 1 bit in v, counting from LSB. +func FirstBitSet(v *big.Int) int { + for i := 0; i < v.BitLen(); i++ { + if v.Bit(i) > 0 { + return i + } + } + return v.BitLen() +} + +// PaddedBigBytes encodes a big integer as a big-endian byte slice. The length +// of the slice is at least n bytes. +func PaddedBigBytes(bigint *big.Int, n int) []byte { + bytes := bigint.Bytes() + if len(bytes) >= n { + return bytes + } + ret := make([]byte, n) + return append(ret[:len(ret)-len(bytes)], bytes...) +} + +// U256 encodes as a 256 bit two's complement number. This operation is destructive. +func U256(x *big.Int) *big.Int { + return x.And(x, tt256m1) +} + +// S256 interprets x as a two's complement number. +// x must not exceed 256 bits (the result is undefined if it does) and is not modified. +// +// S256(0) = 0 +// S256(1) = 1 +// S256(2**255) = -2**255 +// S256(2**256-1) = -1 +func S256(x *big.Int) *big.Int { + if x.Cmp(tt255) < 0 { + return x + } else { + return new(big.Int).Sub(x, tt256) + } +} + +// wordSize is the size number of bits in a big.Word. +const wordSize = 32 << (uint64(^big.Word(0)) >> 63) + +// Exp implements exponentiation by squaring. +// Exp returns a newly-allocated big integer and does not change +// base or exponent. The result is truncated to 256 bits. +// +// Courtesy @karalabe and @chfast +func Exp(base, exponent *big.Int) *big.Int { + result := big.NewInt(1) + + for _, word := range exponent.Bits() { + for i := 0; i < wordSize; i++ { + if word&1 == 1 { + U256(result.Mul(result, base)) + } + U256(base.Mul(base, base)) + word >>= 1 + } + } + return result +} diff --git a/common/math/big_test.go b/common/math/big_test.go new file mode 100644 index 000000000..a0f48a8eb --- /dev/null +++ b/common/math/big_test.go @@ -0,0 +1,196 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package math + +import ( + "bytes" + "math/big" + "testing" +) + +func TestParseBig256(t *testing.T) { + tests := []struct { + input string + num *big.Int + ok bool + }{ + {"", big.NewInt(0), true}, + {"0", big.NewInt(0), true}, + {"0x0", big.NewInt(0), true}, + {"12345678", big.NewInt(12345678), true}, + {"0x12345678", big.NewInt(0x12345678), true}, + {"0X12345678", big.NewInt(0x12345678), true}, + // Tests for leading zero behaviour: + {"0123456789", big.NewInt(123456789), true}, // note: not octal + {"00", big.NewInt(0), true}, + {"0x00", big.NewInt(0), true}, + {"0x012345678abc", big.NewInt(0x12345678abc), true}, + // Invalid syntax: + {"abcdef", nil, false}, + {"0xgg", nil, false}, + // Larger than 256 bits: + {"115792089237316195423570985008687907853269984665640564039457584007913129639936", nil, false}, + } + for _, test := range tests { + num, ok := ParseBig256(test.input) + if ok != test.ok { + t.Errorf("ParseBig(%q) -> ok = %t, want %t", test.input, ok, test.ok) + continue + } + if num != nil && test.num != nil && num.Cmp(test.num) != 0 { + t.Errorf("ParseBig(%q) -> %d, want %d", test.input, num, test.num) + } + } +} + +func TestMustParseBig256(t *testing.T) { + defer func() { + if recover() == nil { + t.Error("MustParseBig should've panicked") + } + }() + MustParseBig256("ggg") +} + +func TestBigMax(t *testing.T) { + a := big.NewInt(10) + b := big.NewInt(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.NewInt(10) + b := big.NewInt(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 TestFirstBigSet(t *testing.T) { + tests := []struct { + num *big.Int + ix int + }{ + {big.NewInt(0), 0}, + {big.NewInt(1), 0}, + {big.NewInt(2), 1}, + {big.NewInt(0x100), 8}, + } + for _, test := range tests { + if ix := FirstBitSet(test.num); ix != test.ix { + t.Errorf("FirstBitSet(b%b) = %d, want %d", test.num, ix, test.ix) + } + } +} + +func TestPaddedBigBytes(t *testing.T) { + tests := []struct { + num *big.Int + n int + result []byte + }{ + {num: big.NewInt(0), n: 4, result: []byte{0, 0, 0, 0}}, + {num: big.NewInt(1), n: 4, result: []byte{0, 0, 0, 1}}, + {num: big.NewInt(512), n: 4, result: []byte{0, 0, 2, 0}}, + {num: BigPow(2, 32), n: 4, result: []byte{1, 0, 0, 0, 0}}, + } + for _, test := range tests { + if result := PaddedBigBytes(test.num, test.n); !bytes.Equal(result, test.result) { + t.Errorf("PaddedBigBytes(%d, %d) = %v, want %v", test.num, test.n, result, test.result) + } + } +} + +func TestU256(t *testing.T) { + tests := []struct{ x, y *big.Int }{ + {x: big.NewInt(0), y: big.NewInt(0)}, + {x: big.NewInt(1), y: big.NewInt(1)}, + {x: BigPow(2, 255), y: BigPow(2, 255)}, + {x: BigPow(2, 256), y: big.NewInt(0)}, + {x: new(big.Int).Add(BigPow(2, 256), big.NewInt(1)), y: big.NewInt(1)}, + // negative values + {x: big.NewInt(-1), y: new(big.Int).Sub(BigPow(2, 256), big.NewInt(1))}, + {x: big.NewInt(-2), y: new(big.Int).Sub(BigPow(2, 256), big.NewInt(2))}, + {x: BigPow(2, -255), y: big.NewInt(1)}, + } + for _, test := range tests { + if y := U256(new(big.Int).Set(test.x)); y.Cmp(test.y) != 0 { + t.Errorf("U256(%x) = %x, want %x", test.x, y, test.y) + } + } +} + +func TestS256(t *testing.T) { + tests := []struct{ x, y *big.Int }{ + {x: big.NewInt(0), y: big.NewInt(0)}, + {x: big.NewInt(1), y: big.NewInt(1)}, + {x: big.NewInt(2), y: big.NewInt(2)}, + { + x: new(big.Int).Sub(BigPow(2, 255), big.NewInt(1)), + y: new(big.Int).Sub(BigPow(2, 255), big.NewInt(1)), + }, + { + x: BigPow(2, 255), + y: new(big.Int).Neg(BigPow(2, 255)), + }, + { + x: new(big.Int).Sub(BigPow(2, 256), big.NewInt(1)), + y: big.NewInt(-1), + }, + { + x: new(big.Int).Sub(BigPow(2, 256), big.NewInt(2)), + y: big.NewInt(-2), + }, + } + for _, test := range tests { + if y := S256(test.x); y.Cmp(test.y) != 0 { + t.Errorf("S256(%x) = %x, want %x", test.x, y, test.y) + } + } +} + +func TestExp(t *testing.T) { + tests := []struct{ base, exponent, result *big.Int }{ + {base: big.NewInt(0), exponent: big.NewInt(0), result: big.NewInt(1)}, + {base: big.NewInt(1), exponent: big.NewInt(0), result: big.NewInt(1)}, + {base: big.NewInt(1), exponent: big.NewInt(1), result: big.NewInt(1)}, + {base: big.NewInt(1), exponent: big.NewInt(2), result: big.NewInt(1)}, + {base: big.NewInt(3), exponent: big.NewInt(144), result: MustParseBig256("507528786056415600719754159741696356908742250191663887263627442114881")}, + {base: big.NewInt(2), exponent: big.NewInt(255), result: MustParseBig256("57896044618658097711785492504343953926634992332820282019728792003956564819968")}, + } + for _, test := range tests { + if result := Exp(test.base, test.exponent); result.Cmp(test.result) != 0 { + t.Errorf("Exp(%d, %d) = %d, want %d", test.base, test.exponent, result, test.result) + } + } +} diff --git a/common/math/dist.go b/common/math/dist.go deleted file mode 100644 index 913fbfbd4..000000000 --- a/common/math/dist.go +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright 2015 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -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 deleted file mode 100644 index f5857b6f8..000000000 --- a/common/math/dist_test.go +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2015 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -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("got sum = %d, want 6", sum) - } -} - -func TestDist(t *testing.T) { - var vectors = []Vector{ - {big.NewInt(1000), big.NewInt(1234)}, - {big.NewInt(500), big.NewInt(10023)}, - {big.NewInt(1034), big.NewInt(1987)}, - {big.NewInt(1034), big.NewInt(1987)}, - {big.NewInt(8983), big.NewInt(1977)}, - {big.NewInt(98382), big.NewInt(1887)}, - {big.NewInt(12398), big.NewInt(1287)}, - {big.NewInt(12398), big.NewInt(1487)}, - {big.NewInt(12398), big.NewInt(1987)}, - {big.NewInt(12398), big.NewInt(128)}, - {big.NewInt(12398), big.NewInt(1987)}, - {big.NewInt(1398), big.NewInt(187)}, - {big.NewInt(12328), big.NewInt(1927)}, - {big.NewInt(12398), big.NewInt(1987)}, - {big.NewInt(22398), big.NewInt(1287)}, - {big.NewInt(1370), big.NewInt(1981)}, - {big.NewInt(12398), big.NewInt(1957)}, - {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/math/exp.go b/common/math/exp.go deleted file mode 100644 index 113b76b39..000000000 --- a/common/math/exp.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package math - -import ( - "math/big" - - "github.com/ethereum/go-ethereum/common" -) - -// wordSize is the size number of bits in a big.Int Word. -const wordSize = 32 << (uint64(^big.Word(0)) >> 63) - -// Exp implement exponentiation by squaring algorithm. -// -// Exp return a new variable; base and exponent must -// not be changed under any circumstance. -// -// Courtesy @karalabe and @chfast -func Exp(base, exponent *big.Int) *big.Int { - result := big.NewInt(1) - - for _, word := range exponent.Bits() { - for i := 0; i < wordSize; i++ { - if word&1 == 1 { - common.U256(result.Mul(result, base)) - } - common.U256(base.Mul(base, base)) - word >>= 1 - } - } - return result -} diff --git a/common/math/integer.go b/common/math/integer.go index 1689b6586..a3eeee27e 100644 --- a/common/math/integer.go +++ b/common/math/integer.go @@ -1,10 +1,63 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + package math -import gmath "math" +import "strconv" + +const ( + // Integer limit values. + MaxInt8 = 1<<7 - 1 + MinInt8 = -1 << 7 + MaxInt16 = 1<<15 - 1 + MinInt16 = -1 << 15 + MaxInt32 = 1<<31 - 1 + MinInt32 = -1 << 31 + MaxInt64 = 1<<63 - 1 + MinInt64 = -1 << 63 + MaxUint8 = 1<<8 - 1 + MaxUint16 = 1<<16 - 1 + MaxUint32 = 1<<32 - 1 + MaxUint64 = 1<<64 - 1 +) + +// ParseUint64 parses s as an integer in decimal or hexadecimal syntax. +// Leading zeros are accepted. The empty string parses as zero. +func ParseUint64(s string) (uint64, bool) { + if s == "" { + return 0, true + } + if len(s) >= 2 && (s[:2] == "0x" || s[:2] == "0X") { + v, err := strconv.ParseUint(s[2:], 16, 64) + return v, err == nil + } + v, err := strconv.ParseUint(s, 10, 64) + return v, err == nil +} + +// MustParseUint64 parses s as an integer and panics if the string is invalid. +func MustParseUint64(s string) uint64 { + v, ok := ParseUint64(s) + if !ok { + panic("invalid unsigned 64 bit integer: " + s) + } + return v +} -/* - * NOTE: The following methods need to be optimised using either bit checking or asm - */ +// NOTE: The following methods need to be optimised using either bit checking or asm // SafeSub returns subtraction result and whether overflow occurred. func SafeSub(x, y uint64) (uint64, bool) { @@ -13,7 +66,7 @@ func SafeSub(x, y uint64) (uint64, bool) { // SafeAdd returns the result and whether overflow occurred. func SafeAdd(x, y uint64) (uint64, bool) { - return x + y, y > gmath.MaxUint64-x + return x + y, y > MaxUint64-x } // SafeMul returns multiplication result and whether overflow occurred. @@ -21,5 +74,5 @@ func SafeMul(x, y uint64) (uint64, bool) { if x == 0 || y == 0 { return 0, false } - return x * y, y > gmath.MaxUint64/x + return x * y, y > MaxUint64/x } diff --git a/common/math/integer_test.go b/common/math/integer_test.go index 198114e5e..05bba221f 100644 --- a/common/math/integer_test.go +++ b/common/math/integer_test.go @@ -1,7 +1,22 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + package math import ( - gmath "math" "testing" ) @@ -21,17 +36,18 @@ func TestOverflow(t *testing.T) { op operation }{ // add operations - {gmath.MaxUint64, 1, true, add}, - {gmath.MaxUint64 - 1, 1, false, add}, + {MaxUint64, 1, true, add}, + {MaxUint64 - 1, 1, false, add}, // sub operations {0, 1, true, sub}, {0, 0, false, sub}, // mul operations + {0, 0, false, mul}, {10, 10, false, mul}, - {gmath.MaxUint64, 2, true, mul}, - {gmath.MaxUint64, 1, false, mul}, + {MaxUint64, 2, true, mul}, + {MaxUint64, 1, false, mul}, } { var overflows bool switch test.op { @@ -48,3 +64,52 @@ func TestOverflow(t *testing.T) { } } } + +func TestParseUint64(t *testing.T) { + tests := []struct { + input string + num uint64 + ok bool + }{ + {"", 0, true}, + {"0", 0, true}, + {"0x0", 0, true}, + {"12345678", 12345678, true}, + {"0x12345678", 0x12345678, true}, + {"0X12345678", 0x12345678, true}, + // Tests for leading zero behaviour: + {"0123456789", 123456789, true}, // note: not octal + {"0x00", 0, true}, + {"0x012345678abc", 0x12345678abc, true}, + // Invalid syntax: + {"abcdef", 0, false}, + {"0xgg", 0, false}, + // Doesn't fit into 64 bits: + {"18446744073709551617", 0, false}, + } + for _, test := range tests { + num, ok := ParseUint64(test.input) + if ok != test.ok { + t.Errorf("ParseUint64(%q) -> ok = %t, want %t", test.input, ok, test.ok) + continue + } + if ok && num != test.num { + t.Errorf("ParseUint64(%q) -> %d, want %d", test.input, num, test.num) + } + } +} + +func TestMustParseUint64(t *testing.T) { + if v := MustParseUint64("12345"); v != 12345 { + t.Errorf(`MustParseUint64("12345") = %d, want 12345`, v) + } +} + +func TestMustParseUint64Panic(t *testing.T) { + defer func() { + if recover() == nil { + t.Error("MustParseBig should've panicked") + } + }() + MustParseUint64("ggg") +} -- cgit v1.2.3