diff --git a/go.mod b/go.mod index e5068ba2f..734199aea 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d github.com/cespare/cp v1.1.1 + github.com/cosmos/cosmos-sdk v0.37.1 github.com/davecgh/go-spew v1.1.1 github.com/deckarep/golang-set v1.7.1 github.com/edsrzf/mmap-go v1.0.0 // indirect @@ -17,7 +18,7 @@ require ( github.com/fjl/memsize v0.0.0-20180929194037-2a09253e352a github.com/garslo/gogen v0.0.0-20170307003452-d6ebae628c7c // indirect github.com/golang/mock v1.3.1 - github.com/golang/protobuf v1.3.1 + github.com/golang/protobuf v1.3.2 github.com/golangci/golangci-lint v1.17.1 github.com/gorilla/handlers v1.4.0 github.com/gorilla/mux v1.7.2 @@ -47,24 +48,22 @@ require ( github.com/pborman/uuid v1.2.0 github.com/pkg/errors v0.8.1 github.com/prometheus/client_golang v0.9.2 - github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 // indirect github.com/prometheus/common v0.4.1 // indirect github.com/prometheus/procfs v0.0.3 // indirect github.com/rjeczalik/notify v0.9.2 - github.com/rs/cors v1.6.0 // indirect github.com/rs/zerolog v1.14.3 github.com/shirou/gopsutil v2.18.12+incompatible github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a // indirect github.com/stretchr/testify v1.3.0 - github.com/syndtr/goleveldb v1.0.0 - github.com/tendermint/tendermint v0.31.7 + github.com/syndtr/goleveldb v1.0.1-0.20190318030020-c3a204f8e965 + github.com/tendermint/tendermint v0.32.3 github.com/whyrusleeping/go-logging v0.0.0-20170515211332-0457bb6b88fc golang.org/x/crypto v0.0.0-20190618222545-ea8f1a30c443 golang.org/x/lint v0.0.0-20190409202823-959b441ac422 golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3 // indirect golang.org/x/tools v0.0.0-20190827205025-b29f5f60c37a google.golang.org/appengine v1.4.0 // indirect - google.golang.org/grpc v1.21.1 + google.golang.org/grpc v1.22.0 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 gopkg.in/ini.v1 v1.42.0 gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect diff --git a/staking/types/commission.go b/staking/types/commission.go new file mode 100644 index 000000000..87bf34b7e --- /dev/null +++ b/staking/types/commission.go @@ -0,0 +1,21 @@ +package types + +import ( + "math/big" +) + +type ( + // Commission defines a commission parameters for a given validator. + Commission struct { + CommissionRates `json:"commission_rates" yaml:"commission_rates"` + UpdateHeight big.Int `json:"update_time" yaml:"update_time"` // the block height the commission rate was last changed + } + + // CommissionRates defines the initial commission rates to be used for creating a + // validator. + CommissionRates struct { + Rate Dec `json:"rate" yaml:"rate"` // the commission rate charged to delegators, as a fraction + MaxRate Dec `json:"max_rate" yaml:"max_rate"` // maximum commission rate which validator can ever charge, as a fraction + MaxChangeRate Dec `json:"max_change_rate" yaml:"max_change_rate"` // maximum increase of the validator commission every epoch, as a fraction + } +) diff --git a/staking/types/decimal.go b/staking/types/decimal.go new file mode 100644 index 000000000..5508eeb3d --- /dev/null +++ b/staking/types/decimal.go @@ -0,0 +1,652 @@ +package types + +import ( + "encoding/json" + "errors" + "fmt" + "math/big" + "strconv" + "strings" + "testing" +) + +// Dec represent a decimal. NOTE: never use new(Dec) or else we will panic unmarshalling into the +// nil embedded big.Int +type Dec struct { + *big.Int `json:"int"` +} + +// number of decimal places +const ( + Precision = 18 + + // bytes required to represent the above precision + // Ceiling[Log2[999 999 999 999 999 999]] + DecimalPrecisionBits = 60 +) + +var ( + precisionReuse = new(big.Int).Exp(big.NewInt(10), big.NewInt(Precision), nil) + fivePrecision = new(big.Int).Quo(precisionReuse, big.NewInt(2)) + precisionMultipliers []*big.Int + zeroInt = big.NewInt(0) + oneInt = big.NewInt(1) + tenInt = big.NewInt(10) +) + +// Set precision multipliers +func init() { + precisionMultipliers = make([]*big.Int, Precision+1) + for i := 0; i <= Precision; i++ { + precisionMultipliers[i] = calcPrecisionMultiplier(int64(i)) + } +} + +func precisionInt() *big.Int { + return new(big.Int).Set(precisionReuse) +} + +// ZeroDec ... +func ZeroDec() Dec { return Dec{new(big.Int).Set(zeroInt)} } + +// OneDec ... +func OneDec() Dec { return Dec{precisionInt()} } + +// SmallestDec ... +func SmallestDec() Dec { return Dec{new(big.Int).Set(oneInt)} } + +// calculate the precision multiplier +func calcPrecisionMultiplier(prec int64) *big.Int { + if prec > Precision { + panic(fmt.Sprintf("too much precision, maximum %v, provided %v", Precision, prec)) + } + zerosToAdd := Precision - prec + multiplier := new(big.Int).Exp(tenInt, big.NewInt(zerosToAdd), nil) + return multiplier +} + +// get the precision multiplier, do not mutate result +func precisionMultiplier(prec int64) *big.Int { + if prec > Precision { + panic(fmt.Sprintf("too much precision, maximum %v, provided %v", Precision, prec)) + } + return precisionMultipliers[prec] +} + +//______________________________________________________________________________________________ + +// NewDec creates a new Dec from integer assuming whole number +func NewDec(i int64) Dec { + return NewDecWithPrec(i, 0) +} + +// NewDecWithPrec creates a new Dec from integer with decimal place at prec +// CONTRACT: prec <= Precision +func NewDecWithPrec(i, prec int64) Dec { + return Dec{ + new(big.Int).Mul(big.NewInt(i), precisionMultiplier(prec)), + } +} + +// NewDecFromBigInt creates a new Dec from big integer assuming whole numbers +// CONTRACT: prec <= Precision +func NewDecFromBigInt(i *big.Int) Dec { + return NewDecFromBigIntWithPrec(i, 0) +} + +// NewDecFromBigIntWithPrec creates a new Dec from big integer assuming whole numbers +// CONTRACT: prec <= Precision +func NewDecFromBigIntWithPrec(i *big.Int, prec int64) Dec { + return Dec{ + new(big.Int).Mul(i, precisionMultiplier(prec)), + } +} + +// NewDecFromInt creates a new Dec from big integer assuming whole numbers +// CONTRACT: prec <= Precision +func NewDecFromInt(i *big.Int) Dec { + return NewDecFromIntWithPrec(i, 0) +} + +// NewDecFromIntWithPrec creates a new Dec from big integer with decimal place at prec +// CONTRACT: prec <= Precision +func NewDecFromIntWithPrec(i *big.Int, prec int64) Dec { + return Dec{ + new(big.Int).Mul(i, precisionMultiplier(prec)), + } +} + +// NewDecFromStr creates a decimal from an input decimal string. +// valid must come in the form: +// (-) whole integers (.) decimal integers +// examples of acceptable input include: +// -123.456 +// 456.7890 +// 345 +// -456789 +// +// NOTE - An error will return if more decimal places +// are provided in the string than the constant Precision. +// +// CONTRACT - This function does not mutate the input str. +func NewDecFromStr(str string) (d Dec, err error) { + if len(str) == 0 { + return d, errors.New("decimal string is empty") + } + + // first extract any negative symbol + neg := false + if str[0] == '-' { + neg = true + str = str[1:] + } + + if len(str) == 0 { + return d, errors.New("decimal string is empty") + } + + strs := strings.Split(str, ".") + lenDecs := 0 + combinedStr := strs[0] + + if len(strs) == 2 { // has a decimal place + lenDecs = len(strs[1]) + if lenDecs == 0 || len(combinedStr) == 0 { + return d, errors.New("bad decimal length") + } + combinedStr += strs[1] + + } else if len(strs) > 2 { + return d, errors.New("too many periods to be a decimal string") + } + + if lenDecs > Precision { + return d, errors.New( + fmt.Sprintf("too much precision, maximum %v, len decimal %v", Precision, lenDecs)) + } + + // add some extra zero's to correct to the Precision factor + zerosToAdd := Precision - lenDecs + zeros := fmt.Sprintf(`%0`+strconv.Itoa(zerosToAdd)+`s`, "") + combinedStr += zeros + + combined, ok := new(big.Int).SetString(combinedStr, 10) // base 10 + if !ok { + return d, errors.New(fmt.Sprintf("bad string to integer conversion, combinedStr: %v", combinedStr)) + } + if neg { + combined = new(big.Int).Neg(combined) + } + return Dec{combined}, nil +} + +// MustNewDecFromStr Decimal from string, panic on error +func MustNewDecFromStr(s string) Dec { + dec, err := NewDecFromStr(s) + if err != nil { + panic(err) + } + return dec +} + +// IsNil ... +func (d Dec) IsNil() bool { return d.Int == nil } // is decimal nil +// IsZero ... +func (d Dec) IsZero() bool { return (d.Int).Sign() == 0 } // is equal to zero +// IsNegative ... +func (d Dec) IsNegative() bool { return (d.Int).Sign() == -1 } // is negative +// IsPositive ... +func (d Dec) IsPositive() bool { return (d.Int).Sign() == 1 } // is positive +// Equal ... +func (d Dec) Equal(d2 Dec) bool { return (d.Int).Cmp(d2.Int) == 0 } // equal decimals +// GT ... +func (d Dec) GT(d2 Dec) bool { return (d.Int).Cmp(d2.Int) > 0 } // greater than +// GTE ... +func (d Dec) GTE(d2 Dec) bool { return (d.Int).Cmp(d2.Int) >= 0 } // greater than or equal +// LT ... +func (d Dec) LT(d2 Dec) bool { return (d.Int).Cmp(d2.Int) < 0 } // less than +// LTE ... +func (d Dec) LTE(d2 Dec) bool { return (d.Int).Cmp(d2.Int) <= 0 } // less than or equal +// Neg ... +func (d Dec) Neg() Dec { return Dec{new(big.Int).Neg(d.Int)} } // reverse the decimal sign +// Abs ... +func (d Dec) Abs() Dec { return Dec{new(big.Int).Abs(d.Int)} } // absolute value + +// Add addition +func (d Dec) Add(d2 Dec) Dec { + res := new(big.Int).Add(d.Int, d2.Int) + + if res.BitLen() > 255+DecimalPrecisionBits { + panic("Int overflow") + } + return Dec{res} +} + +// Sub subtraction +func (d Dec) Sub(d2 Dec) Dec { + res := new(big.Int).Sub(d.Int, d2.Int) + + if res.BitLen() > 255+DecimalPrecisionBits { + panic("Int overflow") + } + return Dec{res} +} + +// Mul multiplication +func (d Dec) Mul(d2 Dec) Dec { + mul := new(big.Int).Mul(d.Int, d2.Int) + chopped := chopPrecisionAndRound(mul) + + if chopped.BitLen() > 255+DecimalPrecisionBits { + panic("Int overflow") + } + return Dec{chopped} +} + +// MulTruncate multiplication truncate +func (d Dec) MulTruncate(d2 Dec) Dec { + mul := new(big.Int).Mul(d.Int, d2.Int) + chopped := chopPrecisionAndTruncate(mul) + + if chopped.BitLen() > 255+DecimalPrecisionBits { + panic("Int overflow") + } + return Dec{chopped} +} + +// MulInt multiplication +func (d Dec) MulInt(i *big.Int) Dec { + mul := new(big.Int).Mul(d.Int, i) + + if mul.BitLen() > 255+DecimalPrecisionBits { + panic("Int overflow") + } + return Dec{mul} +} + +// MulInt64 - multiplication with int64 +func (d Dec) MulInt64(i int64) Dec { + mul := new(big.Int).Mul(d.Int, big.NewInt(i)) + + if mul.BitLen() > 255+DecimalPrecisionBits { + panic("Int overflow") + } + return Dec{mul} +} + +// Quo quotient +func (d Dec) Quo(d2 Dec) Dec { + + // multiply precision twice + mul := new(big.Int).Mul(d.Int, precisionReuse) + mul.Mul(mul, precisionReuse) + + quo := new(big.Int).Quo(mul, d2.Int) + chopped := chopPrecisionAndRound(quo) + + if chopped.BitLen() > 255+DecimalPrecisionBits { + panic("Int overflow") + } + return Dec{chopped} +} + +// QuoTruncate quotient truncate +func (d Dec) QuoTruncate(d2 Dec) Dec { + + // multiply precision twice + mul := new(big.Int).Mul(d.Int, precisionReuse) + mul.Mul(mul, precisionReuse) + + quo := new(big.Int).Quo(mul, d2.Int) + chopped := chopPrecisionAndTruncate(quo) + + if chopped.BitLen() > 255+DecimalPrecisionBits { + panic("Int overflow") + } + return Dec{chopped} +} + +// QuoRoundUp quotient, round up +func (d Dec) QuoRoundUp(d2 Dec) Dec { + // multiply precision twice + mul := new(big.Int).Mul(d.Int, precisionReuse) + mul.Mul(mul, precisionReuse) + + quo := new(big.Int).Quo(mul, d2.Int) + chopped := chopPrecisionAndRoundUp(quo) + + if chopped.BitLen() > 255+DecimalPrecisionBits { + panic("Int overflow") + } + return Dec{chopped} +} + +// QuoInt quotient +func (d Dec) QuoInt(i *big.Int) Dec { + mul := new(big.Int).Quo(d.Int, i) + return Dec{mul} +} + +// QuoInt64 - quotient with int64 +func (d Dec) QuoInt64(i int64) Dec { + mul := new(big.Int).Quo(d.Int, big.NewInt(i)) + return Dec{mul} +} + +// IsInteger is integer, e.g. decimals are zero +func (d Dec) IsInteger() bool { + return new(big.Int).Rem(d.Int, precisionReuse).Sign() == 0 +} + +// Format decimal state +func (d Dec) Format(s fmt.State, verb rune) { + _, err := s.Write([]byte(d.String())) + if err != nil { + panic(err) + } +} + +func (d Dec) String() string { + if d.Int == nil { + return d.Int.String() + } + + isNeg := d.IsNegative() + if d.IsNegative() { + d = d.Neg() + } + + bzInt, err := d.Int.MarshalText() + if err != nil { + return "" + } + inputSize := len(bzInt) + + var bzStr []byte + + // TODO: Remove trailing zeros + // case 1, purely decimal + if inputSize <= Precision { + bzStr = make([]byte, Precision+2) + + // 0. prefix + bzStr[0] = byte('0') + bzStr[1] = byte('.') + + // set relevant digits to 0 + for i := 0; i < Precision-inputSize; i++ { + bzStr[i+2] = byte('0') + } + + // set final digits + copy(bzStr[2+(Precision-inputSize):], bzInt) + + } else { + + // inputSize + 1 to account for the decimal point that is being added + bzStr = make([]byte, inputSize+1) + decPointPlace := inputSize - Precision + + copy(bzStr, bzInt[:decPointPlace]) // pre-decimal digits + bzStr[decPointPlace] = byte('.') // decimal point + copy(bzStr[decPointPlace+1:], bzInt[decPointPlace:]) // post-decimal digits + } + + if isNeg { + return "-" + string(bzStr) + } + + return string(bzStr) +} + +// ____ +// __| |__ "chop 'em +// ` \ round!" +// ___|| ~ _ -bankers +// | | __ +// | | | __|__|__ +// |_____: / | $$$ | +// |________| + +// nolint - go-cyclo +// Remove a Precision amount of rightmost digits and perform bankers rounding +// on the remainder (gaussian rounding) on the digits which have been removed. +// +// Mutates the input. Use the non-mutative version if that is undesired +func chopPrecisionAndRound(d *big.Int) *big.Int { + + // remove the negative and add it back when returning + if d.Sign() == -1 { + // make d positive, compute chopped value, and then un-mutate d + d = d.Neg(d) + d = chopPrecisionAndRound(d) + d = d.Neg(d) + return d + } + + // get the truncated quotient and remainder + quo, rem := d, big.NewInt(0) + quo, rem = quo.QuoRem(d, precisionReuse, rem) + + if rem.Sign() == 0 { // remainder is zero + return quo + } + + switch rem.Cmp(fivePrecision) { + case -1: + return quo + case 1: + return quo.Add(quo, oneInt) + default: // bankers rounding must take place + // always round to an even number + if quo.Bit(0) == 0 { + return quo + } + return quo.Add(quo, oneInt) + } +} + +func chopPrecisionAndRoundUp(d *big.Int) *big.Int { + + // remove the negative and add it back when returning + if d.Sign() == -1 { + // make d positive, compute chopped value, and then un-mutate d + d = d.Neg(d) + // truncate since d is negative... + d = chopPrecisionAndTruncate(d) + d = d.Neg(d) + return d + } + + // get the truncated quotient and remainder + quo, rem := d, big.NewInt(0) + quo, rem = quo.QuoRem(d, precisionReuse, rem) + + if rem.Sign() == 0 { // remainder is zero + return quo + } + + return quo.Add(quo, oneInt) +} + +func chopPrecisionAndRoundNonMutative(d *big.Int) *big.Int { + tmp := new(big.Int).Set(d) + return chopPrecisionAndRound(tmp) +} + +// RoundInt64 rounds the decimal using bankers rounding +func (d Dec) RoundInt64() int64 { + chopped := chopPrecisionAndRoundNonMutative(d.Int) + if !chopped.IsInt64() { + panic("Int64() out of bound") + } + return chopped.Int64() +} + +// RoundInt round the decimal using bankers rounding +//func (d Dec) RoundInt() big.Int { +// return NewIntFromBigInt(chopPrecisionAndRoundNonMutative(d.Int)) +//} + +//___________________________________________________________________________________ + +// similar to chopPrecisionAndRound, but always rounds down +func chopPrecisionAndTruncate(d *big.Int) *big.Int { + return d.Quo(d, precisionReuse) +} + +func chopPrecisionAndTruncateNonMutative(d *big.Int) *big.Int { + tmp := new(big.Int).Set(d) + return chopPrecisionAndTruncate(tmp) +} + +// TruncateInt64 truncates the decimals from the number and returns an int64 +func (d Dec) TruncateInt64() int64 { + chopped := chopPrecisionAndTruncateNonMutative(d.Int) + if !chopped.IsInt64() { + panic("Int64() out of bound") + } + return chopped.Int64() +} + +// TruncateInt truncates the decimals from the number and returns an Int +//func (d Dec) TruncateInt() big.Int { +// return NewIntFromBigInt(chopPrecisionAndTruncateNonMutative(d.Int)) +//} + +// TruncateDec truncates the decimals from the number and returns a Dec +func (d Dec) TruncateDec() Dec { + return NewDecFromBigInt(chopPrecisionAndTruncateNonMutative(d.Int)) +} + +// Ceil returns the smallest interger value (as a decimal) that is greater than +// or equal to the given decimal. +func (d Dec) Ceil() Dec { + tmp := new(big.Int).Set(d.Int) + + quo, rem := tmp, big.NewInt(0) + quo, rem = quo.QuoRem(tmp, precisionReuse, rem) + + // no need to round with a zero remainder regardless of sign + if rem.Cmp(zeroInt) == 0 { + return NewDecFromBigInt(quo) + } + + if rem.Sign() == -1 { + return NewDecFromBigInt(quo) + } + + return NewDecFromBigInt(quo.Add(quo, oneInt)) +} + +//___________________________________________________________________________________ + +// reuse nil values +var ( + nilAmino string + nilJSON []byte +) + +func init() { + empty := new(big.Int) + bz, err := empty.MarshalText() + if err != nil { + panic("bad nil amino init") + } + nilAmino = string(bz) + + nilJSON, err = json.Marshal(string(bz)) + if err != nil { + panic("bad nil json init") + } +} + +// MarshalAmino wraps d.MarshalText() +func (d Dec) MarshalAmino() (string, error) { + if d.Int == nil { + return nilAmino, nil + } + bz, err := d.Int.MarshalText() + return string(bz), err +} + +// UnmarshalAmino requires a valid JSON string - strings quotes and calls UnmarshalText +func (d *Dec) UnmarshalAmino(text string) (err error) { + tempInt := new(big.Int) + err = tempInt.UnmarshalText([]byte(text)) + if err != nil { + return err + } + d.Int = tempInt + return nil +} + +// MarshalJSON marshals the decimal +func (d Dec) MarshalJSON() ([]byte, error) { + if d.Int == nil { + return nilJSON, nil + } + + return json.Marshal(d.String()) +} + +// UnmarshalJSON defines custom decoding scheme +func (d *Dec) UnmarshalJSON(bz []byte) error { + if d.Int == nil { + d.Int = new(big.Int) + } + + var text string + err := json.Unmarshal(bz, &text) + if err != nil { + return err + } + // TODO: Reuse dec allocation + newDec, err := NewDecFromStr(text) + if err != nil { + return err + } + d.Int = newDec.Int + return nil +} + +// MarshalYAML returns Ythe AML representation. +func (d Dec) MarshalYAML() (interface{}, error) { return d.String(), nil } + +//___________________________________________________________________________________ +// helpers + +// DecsEqual test if two decimal arrays are equal +func DecsEqual(d1s, d2s []Dec) bool { + if len(d1s) != len(d2s) { + return false + } + + for i, d1 := range d1s { + if !d1.Equal(d2s[i]) { + return false + } + } + return true +} + +// MinDec minimum decimal between two +func MinDec(d1, d2 Dec) Dec { + if d1.LT(d2) { + return d1 + } + return d2 +} + +// MaxDec maximum decimal between two +func MaxDec(d1, d2 Dec) Dec { + if d1.LT(d2) { + return d2 + } + return d1 +} + +// DecEq intended to be used with require/assert: require.True(DecEq(...)) +func DecEq(t *testing.T, exp, got Dec) (*testing.T, bool, string, string, string) { + return t, exp.Equal(got), "expected:\t%v\ngot:\t\t%v", exp.String(), got.String() +} diff --git a/staking/types/decimal_test.go b/staking/types/decimal_test.go new file mode 100644 index 000000000..940b45d39 --- /dev/null +++ b/staking/types/decimal_test.go @@ -0,0 +1,407 @@ +package types + +import ( + "math/big" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/codec" +) + +// create a decimal from a decimal string (ex. "1234.5678") +func mustNewDecFromStr(t *testing.T, str string) (d Dec) { + d, err := NewDecFromStr(str) + require.NoError(t, err) + return d +} + +//_______________________________________ + +func TestPrecisionMultiplier(t *testing.T) { + res := precisionMultiplier(5) + exp := big.NewInt(10000000000000) + require.Equal(t, 0, res.Cmp(exp), "equality was incorrect, res %v, exp %v", res, exp) +} + +func TestNewDecFromStr(t *testing.T) { + largeBigInt, success := new(big.Int).SetString("3144605511029693144278234343371835", 10) + require.True(t, success) + tests := []struct { + decimalStr string + expErr bool + exp Dec + }{ + {"", true, Dec{}}, + {"0.-75", true, Dec{}}, + {"0", false, NewDec(0)}, + {"1", false, NewDec(1)}, + {"1.1", false, NewDecWithPrec(11, 1)}, + {"0.75", false, NewDecWithPrec(75, 2)}, + {"0.8", false, NewDecWithPrec(8, 1)}, + {"0.11111", false, NewDecWithPrec(11111, 5)}, + {"314460551102969.3144278234343371835", true, NewDec(3141203149163817869)}, + {"314460551102969314427823434337.1835718092488231350", + true, NewDecFromBigIntWithPrec(largeBigInt, 4)}, + {"314460551102969314427823434337.1835", + false, NewDecFromBigIntWithPrec(largeBigInt, 4)}, + {".", true, Dec{}}, + {".0", true, NewDec(0)}, + {"1.", true, NewDec(1)}, + {"foobar", true, Dec{}}, + {"0.foobar", true, Dec{}}, + {"0.foobar.", true, Dec{}}, + } + + for tcIndex, tc := range tests { + res, err := NewDecFromStr(tc.decimalStr) + if tc.expErr { + require.NotNil(t, err, "error expected, decimalStr %v, tc %v", tc.decimalStr, tcIndex) + } else { + require.Nil(t, err, "unexpected error, decimalStr %v, tc %v", tc.decimalStr, tcIndex) + require.True(t, res.Equal(tc.exp), "equality was incorrect, res %v, exp %v, tc %v", res, tc.exp, tcIndex) + } + + // negative tc + res, err = NewDecFromStr("-" + tc.decimalStr) + if tc.expErr { + require.NotNil(t, err, "error expected, decimalStr %v, tc %v", tc.decimalStr, tcIndex) + } else { + require.Nil(t, err, "unexpected error, decimalStr %v, tc %v", tc.decimalStr, tcIndex) + exp := tc.exp.Mul(NewDec(-1)) + require.True(t, res.Equal(exp), "equality was incorrect, res %v, exp %v, tc %v", res, exp, tcIndex) + } + } +} + +func TestDecString(t *testing.T) { + tests := []struct { + d Dec + want string + }{ + {NewDec(0), "0.000000000000000000"}, + {NewDec(1), "1.000000000000000000"}, + {NewDec(10), "10.000000000000000000"}, + {NewDec(12340), "12340.000000000000000000"}, + {NewDecWithPrec(12340, 4), "1.234000000000000000"}, + {NewDecWithPrec(12340, 5), "0.123400000000000000"}, + {NewDecWithPrec(12340, 8), "0.000123400000000000"}, + {NewDecWithPrec(1009009009009009009, 17), "10.090090090090090090"}, + } + for tcIndex, tc := range tests { + assert.Equal(t, tc.want, tc.d.String(), "bad String(), index: %v", tcIndex) + } +} + +func TestEqualities(t *testing.T) { + tests := []struct { + d1, d2 Dec + gt, lt, eq bool + }{ + {NewDec(0), NewDec(0), false, false, true}, + {NewDecWithPrec(0, 2), NewDecWithPrec(0, 4), false, false, true}, + {NewDecWithPrec(100, 0), NewDecWithPrec(100, 0), false, false, true}, + {NewDecWithPrec(-100, 0), NewDecWithPrec(-100, 0), false, false, true}, + {NewDecWithPrec(-1, 1), NewDecWithPrec(-1, 1), false, false, true}, + {NewDecWithPrec(3333, 3), NewDecWithPrec(3333, 3), false, false, true}, + + {NewDecWithPrec(0, 0), NewDecWithPrec(3333, 3), false, true, false}, + {NewDecWithPrec(0, 0), NewDecWithPrec(100, 0), false, true, false}, + {NewDecWithPrec(-1, 0), NewDecWithPrec(3333, 3), false, true, false}, + {NewDecWithPrec(-1, 0), NewDecWithPrec(100, 0), false, true, false}, + {NewDecWithPrec(1111, 3), NewDecWithPrec(100, 0), false, true, false}, + {NewDecWithPrec(1111, 3), NewDecWithPrec(3333, 3), false, true, false}, + {NewDecWithPrec(-3333, 3), NewDecWithPrec(-1111, 3), false, true, false}, + + {NewDecWithPrec(3333, 3), NewDecWithPrec(0, 0), true, false, false}, + {NewDecWithPrec(100, 0), NewDecWithPrec(0, 0), true, false, false}, + {NewDecWithPrec(3333, 3), NewDecWithPrec(-1, 0), true, false, false}, + {NewDecWithPrec(100, 0), NewDecWithPrec(-1, 0), true, false, false}, + {NewDecWithPrec(100, 0), NewDecWithPrec(1111, 3), true, false, false}, + {NewDecWithPrec(3333, 3), NewDecWithPrec(1111, 3), true, false, false}, + {NewDecWithPrec(-1111, 3), NewDecWithPrec(-3333, 3), true, false, false}, + } + + for tcIndex, tc := range tests { + require.Equal(t, tc.gt, tc.d1.GT(tc.d2), "GT result is incorrect, tc %d", tcIndex) + require.Equal(t, tc.lt, tc.d1.LT(tc.d2), "LT result is incorrect, tc %d", tcIndex) + require.Equal(t, tc.eq, tc.d1.Equal(tc.d2), "equality result is incorrect, tc %d", tcIndex) + } + +} + +func TestDecsEqual(t *testing.T) { + tests := []struct { + d1s, d2s []Dec + eq bool + }{ + {[]Dec{NewDec(0)}, []Dec{NewDec(0)}, true}, + {[]Dec{NewDec(0)}, []Dec{NewDec(1)}, false}, + {[]Dec{NewDec(0)}, []Dec{}, false}, + {[]Dec{NewDec(0), NewDec(1)}, []Dec{NewDec(0), NewDec(1)}, true}, + {[]Dec{NewDec(1), NewDec(0)}, []Dec{NewDec(1), NewDec(0)}, true}, + {[]Dec{NewDec(1), NewDec(0)}, []Dec{NewDec(0), NewDec(1)}, false}, + {[]Dec{NewDec(1), NewDec(0)}, []Dec{NewDec(1)}, false}, + {[]Dec{NewDec(1), NewDec(2)}, []Dec{NewDec(2), NewDec(4)}, false}, + {[]Dec{NewDec(3), NewDec(18)}, []Dec{NewDec(1), NewDec(6)}, false}, + } + + for tcIndex, tc := range tests { + require.Equal(t, tc.eq, DecsEqual(tc.d1s, tc.d2s), "equality of decional arrays is incorrect, tc %d", tcIndex) + require.Equal(t, tc.eq, DecsEqual(tc.d2s, tc.d1s), "equality of decional arrays is incorrect (converse), tc %d", tcIndex) + } +} + +func TestArithmetic(t *testing.T) { + tests := []struct { + d1, d2 Dec + expMul, expMulTruncate Dec + expQuo, expQuoRoundUp, expQuoTruncate Dec + expAdd, expSub Dec + }{ + // d1 d2 MUL MulTruncate QUO QUORoundUp QUOTrunctate ADD SUB + {NewDec(0), NewDec(0), NewDec(0), NewDec(0), NewDec(0), NewDec(0), NewDec(0), NewDec(0), NewDec(0)}, + {NewDec(1), NewDec(0), NewDec(0), NewDec(0), NewDec(0), NewDec(0), NewDec(0), NewDec(1), NewDec(1)}, + {NewDec(0), NewDec(1), NewDec(0), NewDec(0), NewDec(0), NewDec(0), NewDec(0), NewDec(1), NewDec(-1)}, + {NewDec(0), NewDec(-1), NewDec(0), NewDec(0), NewDec(0), NewDec(0), NewDec(0), NewDec(-1), NewDec(1)}, + {NewDec(-1), NewDec(0), NewDec(0), NewDec(0), NewDec(0), NewDec(0), NewDec(0), NewDec(-1), NewDec(-1)}, + + {NewDec(1), NewDec(1), NewDec(1), NewDec(1), NewDec(1), NewDec(1), NewDec(1), NewDec(2), NewDec(0)}, + {NewDec(-1), NewDec(-1), NewDec(1), NewDec(1), NewDec(1), NewDec(1), NewDec(1), NewDec(-2), NewDec(0)}, + {NewDec(1), NewDec(-1), NewDec(-1), NewDec(-1), NewDec(-1), NewDec(-1), NewDec(-1), NewDec(0), NewDec(2)}, + {NewDec(-1), NewDec(1), NewDec(-1), NewDec(-1), NewDec(-1), NewDec(-1), NewDec(-1), NewDec(0), NewDec(-2)}, + + {NewDec(3), NewDec(7), NewDec(21), NewDec(21), + NewDecWithPrec(428571428571428571, 18), NewDecWithPrec(428571428571428572, 18), NewDecWithPrec(428571428571428571, 18), + NewDec(10), NewDec(-4)}, + {NewDec(2), NewDec(4), NewDec(8), NewDec(8), NewDecWithPrec(5, 1), NewDecWithPrec(5, 1), NewDecWithPrec(5, 1), + NewDec(6), NewDec(-2)}, + + {NewDec(100), NewDec(100), NewDec(10000), NewDec(10000), NewDec(1), NewDec(1), NewDec(1), NewDec(200), NewDec(0)}, + + {NewDecWithPrec(15, 1), NewDecWithPrec(15, 1), NewDecWithPrec(225, 2), NewDecWithPrec(225, 2), + NewDec(1), NewDec(1), NewDec(1), NewDec(3), NewDec(0)}, + {NewDecWithPrec(3333, 4), NewDecWithPrec(333, 4), NewDecWithPrec(1109889, 8), NewDecWithPrec(1109889, 8), + MustNewDecFromStr("10.009009009009009009"), MustNewDecFromStr("10.009009009009009010"), MustNewDecFromStr("10.009009009009009009"), + NewDecWithPrec(3666, 4), NewDecWithPrec(3, 1)}, + } + + for tcIndex, tc := range tests { + resAdd := tc.d1.Add(tc.d2) + resSub := tc.d1.Sub(tc.d2) + resMul := tc.d1.Mul(tc.d2) + resMulTruncate := tc.d1.MulTruncate(tc.d2) + require.True(t, tc.expAdd.Equal(resAdd), "exp %v, res %v, tc %d", tc.expAdd, resAdd, tcIndex) + require.True(t, tc.expSub.Equal(resSub), "exp %v, res %v, tc %d", tc.expSub, resSub, tcIndex) + require.True(t, tc.expMul.Equal(resMul), "exp %v, res %v, tc %d", tc.expMul, resMul, tcIndex) + require.True(t, tc.expMulTruncate.Equal(resMulTruncate), "exp %v, res %v, tc %d", tc.expMulTruncate, resMulTruncate, tcIndex) + + if tc.d2.IsZero() { // panic for divide by zero + require.Panics(t, func() { tc.d1.Quo(tc.d2) }) + } else { + resQuo := tc.d1.Quo(tc.d2) + require.True(t, tc.expQuo.Equal(resQuo), "exp %v, res %v, tc %d", tc.expQuo.String(), resQuo.String(), tcIndex) + + resQuoRoundUp := tc.d1.QuoRoundUp(tc.d2) + require.True(t, tc.expQuoRoundUp.Equal(resQuoRoundUp), "exp %v, res %v, tc %d", + tc.expQuoRoundUp.String(), resQuoRoundUp.String(), tcIndex) + + resQuoTruncate := tc.d1.QuoTruncate(tc.d2) + require.True(t, tc.expQuoTruncate.Equal(resQuoTruncate), "exp %v, res %v, tc %d", + tc.expQuoTruncate.String(), resQuoTruncate.String(), tcIndex) + } + } +} + +func TestBankerRoundChop(t *testing.T) { + tests := []struct { + d1 Dec + exp int64 + }{ + {mustNewDecFromStr(t, "0.25"), 0}, + {mustNewDecFromStr(t, "0"), 0}, + {mustNewDecFromStr(t, "1"), 1}, + {mustNewDecFromStr(t, "0.75"), 1}, + {mustNewDecFromStr(t, "0.5"), 0}, + {mustNewDecFromStr(t, "7.5"), 8}, + {mustNewDecFromStr(t, "1.5"), 2}, + {mustNewDecFromStr(t, "2.5"), 2}, + {mustNewDecFromStr(t, "0.545"), 1}, // 0.545-> 1 even though 5 is first decimal and 1 not even + {mustNewDecFromStr(t, "1.545"), 2}, + } + + for tcIndex, tc := range tests { + resNeg := tc.d1.Neg().RoundInt64() + require.Equal(t, -1*tc.exp, resNeg, "negative tc %d", tcIndex) + + resPos := tc.d1.RoundInt64() + require.Equal(t, tc.exp, resPos, "positive tc %d", tcIndex) + } +} + +func TestTruncate(t *testing.T) { + tests := []struct { + d1 Dec + exp int64 + }{ + {mustNewDecFromStr(t, "0"), 0}, + {mustNewDecFromStr(t, "0.25"), 0}, + {mustNewDecFromStr(t, "0.75"), 0}, + {mustNewDecFromStr(t, "1"), 1}, + {mustNewDecFromStr(t, "1.5"), 1}, + {mustNewDecFromStr(t, "7.5"), 7}, + {mustNewDecFromStr(t, "7.6"), 7}, + {mustNewDecFromStr(t, "7.4"), 7}, + {mustNewDecFromStr(t, "100.1"), 100}, + {mustNewDecFromStr(t, "1000.1"), 1000}, + } + + for tcIndex, tc := range tests { + resNeg := tc.d1.Neg().TruncateInt64() + require.Equal(t, -1*tc.exp, resNeg, "negative tc %d", tcIndex) + + resPos := tc.d1.TruncateInt64() + require.Equal(t, tc.exp, resPos, "positive tc %d", tcIndex) + } +} + +var cdc = codec.New() + +func TestDecMarshalJSON(t *testing.T) { + decimal := func(i int64) Dec { + d := NewDec(0) + d.Int = new(big.Int).SetInt64(i) + return d + } + tests := []struct { + name string + d Dec + want string + wantErr bool // if wantErr = false, will also attempt unmarshaling + }{ + {"zero", decimal(0), "\"0.000000000000000000\"", false}, + {"one", decimal(1), "\"0.000000000000000001\"", false}, + {"ten", decimal(10), "\"0.000000000000000010\"", false}, + {"12340", decimal(12340), "\"0.000000000000012340\"", false}, + {"zeroInt", NewDec(0), "\"0.000000000000000000\"", false}, + {"oneInt", NewDec(1), "\"1.000000000000000000\"", false}, + {"tenInt", NewDec(10), "\"10.000000000000000000\"", false}, + {"12340Int", NewDec(12340), "\"12340.000000000000000000\"", false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.d.MarshalJSON() + if (err != nil) != tt.wantErr { + t.Errorf("Dec.MarshalJSON() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !tt.wantErr { + assert.Equal(t, tt.want, string(got), "incorrect marshalled value") + unmarshalledDec := NewDec(0) + unmarshalledDec.UnmarshalJSON(got) + assert.Equal(t, tt.d, unmarshalledDec, "incorrect unmarshalled value") + } + }) + } +} + +func TestZeroDeserializationJSON(t *testing.T) { + d := Dec{new(big.Int)} + err := cdc.UnmarshalJSON([]byte(`"0"`), &d) + require.Nil(t, err) + err = cdc.UnmarshalJSON([]byte(`"{}"`), &d) + require.NotNil(t, err) +} + +func TestSerializationText(t *testing.T) { + d := mustNewDecFromStr(t, "0.333") + + bz, err := d.MarshalText() + require.NoError(t, err) + + d2 := Dec{new(big.Int)} + err = d2.UnmarshalText(bz) + require.NoError(t, err) + require.True(t, d.Equal(d2), "original: %v, unmarshalled: %v", d, d2) +} + +func TestSerializationGocodecJSON(t *testing.T) { + d := mustNewDecFromStr(t, "0.333") + + bz, err := cdc.MarshalJSON(d) + require.NoError(t, err) + + d2 := Dec{new(big.Int)} + err = cdc.UnmarshalJSON(bz, &d2) + require.NoError(t, err) + require.True(t, d.Equal(d2), "original: %v, unmarshalled: %v", d, d2) +} + +func TestSerializationGocodecBinary(t *testing.T) { + d := mustNewDecFromStr(t, "0.333") + + bz, err := cdc.MarshalBinaryLengthPrefixed(d) + require.NoError(t, err) + + var d2 Dec + err = cdc.UnmarshalBinaryLengthPrefixed(bz, &d2) + require.NoError(t, err) + require.True(t, d.Equal(d2), "original: %v, unmarshalled: %v", d, d2) +} + +type testDEmbedStruct struct { + Field1 string `json:"f1"` + Field2 int `json:"f2"` + Field3 Dec `json:"f3"` +} + +// TODO make work for UnmarshalJSON +func TestEmbeddedStructSerializationGocodec(t *testing.T) { + obj := testDEmbedStruct{"foo", 10, NewDecWithPrec(1, 3)} + bz, err := cdc.MarshalBinaryLengthPrefixed(obj) + require.Nil(t, err) + + var obj2 testDEmbedStruct + err = cdc.UnmarshalBinaryLengthPrefixed(bz, &obj2) + require.Nil(t, err) + + require.Equal(t, obj.Field1, obj2.Field1) + require.Equal(t, obj.Field2, obj2.Field2) + require.True(t, obj.Field3.Equal(obj2.Field3), "original: %v, unmarshalled: %v", obj, obj2) +} + +func TestStringOverflow(t *testing.T) { + // two random 64 bit primes + dec1, err := NewDecFromStr("51643150036226787134389711697696177267") + require.NoError(t, err) + dec2, err := NewDecFromStr("-31798496660535729618459429845579852627") + require.NoError(t, err) + dec3 := dec1.Add(dec2) + require.Equal(t, + "19844653375691057515930281852116324640.000000000000000000", + dec3.String(), + ) +} + +func TestDecCeil(t *testing.T) { + testCases := []struct { + input Dec + expected Dec + }{ + {NewDecWithPrec(1000000000000000, Precision), NewDec(1)}, // 0.001 => 1.0 + {NewDecWithPrec(-1000000000000000, Precision), ZeroDec()}, // -0.001 => 0.0 + {ZeroDec(), ZeroDec()}, // 0.0 => 0.0 + {NewDecWithPrec(900000000000000000, Precision), NewDec(1)}, // 0.9 => 1.0 + {NewDecWithPrec(4001000000000000000, Precision), NewDec(5)}, // 4.001 => 5.0 + {NewDecWithPrec(-4001000000000000000, Precision), NewDec(-4)}, // -4.001 => -4.0 + {NewDecWithPrec(4700000000000000000, Precision), NewDec(5)}, // 4.7 => 5.0 + {NewDecWithPrec(-4700000000000000000, Precision), NewDec(-4)}, // -4.7 => -4.0 + } + + for i, tc := range testCases { + res := tc.input.Ceil() + require.Equal(t, tc.expected, res, "unexpected result for test case %d, input: %v", i, tc.input) + } +} diff --git a/staking/types/validator.go b/staking/types/validator.go new file mode 100644 index 000000000..3bb70bb74 --- /dev/null +++ b/staking/types/validator.go @@ -0,0 +1,28 @@ +package types + +import ( + "github.com/harmony-one/bls/ffi/go/bls" + "github.com/harmony-one/harmony/internal/common" + "math/big" +) + +// Validator - data fields for a validator +type Validator struct { + StakingAddress common.Address `json:"staking_address" yaml:"staking_address"` // ECDSA address of the validator + ValidatingPubKey bls.PublicKey `json:"validating_pub_key" yaml:"validating_pub_key"` // The BLS public key of the validator for consensus + Description Description `json:"description" yaml:"description"` // description for the validator + Active bool `json:"active" yaml:"active"` // Is the validator active in the validating process or not + Stake big.Int `json:"stake" yaml:"stake"` // The stake put by the validator itself + UnbondingHeight big.Int `json:"unbonding_height" yaml:"unbonding_height"` // if unbonding, height at which this validator has begun unbonding + Commission Commission `json:"commission" yaml:"commission"` // commission parameters + MinSelfDelegation big.Int `json:"min_self_delegation" yaml:"min_self_delegation"` // validator's self declared minimum self delegation +} + +// Description - description fields for a validator +type Description struct { + Name string `json:"name" yaml:"name"` // name + Identity string `json:"identity" yaml:"identity"` // optional identity signature (ex. UPort or Keybase) + Website string `json:"website" yaml:"website"` // optional website link + SecurityContact string `json:"security_contact" yaml:"security_contact"` // optional security contact info + Details string `json:"details" yaml:"details"` // optional details +}