diff --git a/PENDING.md b/PENDING.md index c9df61865..4fcc0c6a9 100644 --- a/PENDING.md +++ b/PENDING.md @@ -34,6 +34,7 @@ BREAKING CHANGES * `gaiacli gov vote --voter` * [x/gov] Added tags sub-package, changed tags to use dash-case * [x/gov] Governance parameters are now stored in globalparams store +* [core] \#1807 Switch from use of rational to decimal * [lcd] \#1866 Updated lcd /slashing/signing_info endpoint to take cosmosvalpub instead of cosmosvaladdr * [types] sdk.NewCoin now takes sdk.Int, sdk.NewInt64Coin takes int64 * [cli] #1551: Officially removed `--name` from CLI commands diff --git a/client/lcd/test_helpers.go b/client/lcd/test_helpers.go index ee46b267d..818eae1e8 100644 --- a/client/lcd/test_helpers.go +++ b/client/lcd/test_helpers.go @@ -173,7 +173,7 @@ func InitializeTestLCD(t *testing.T, nValidators int, initAddrs []sdk.AccAddress accAuth.Coins = sdk.Coins{sdk.NewInt64Coin("steak", 100)} acc := gapp.NewGenesisAccount(&accAuth) genesisState.Accounts = append(genesisState.Accounts, acc) - genesisState.StakeData.Pool.LooseTokens = genesisState.StakeData.Pool.LooseTokens.Add(sdk.NewRat(100)) + genesisState.StakeData.Pool.LooseTokens = genesisState.StakeData.Pool.LooseTokens.Add(sdk.NewDec(100)) } appState, err := wire.MarshalJSONIndent(cdc, genesisState) diff --git a/cmd/gaia/app/genesis.go b/cmd/gaia/app/genesis.go index 1731fe2dc..e35b21c4b 100644 --- a/cmd/gaia/app/genesis.go +++ b/cmd/gaia/app/genesis.go @@ -185,7 +185,7 @@ func GaiaAppGenState(cdc *wire.Codec, appGenTxs []json.RawMessage) (genesisState } acc := NewGenesisAccount(&accAuth) genaccs[i] = acc - stakeData.Pool.LooseTokens = stakeData.Pool.LooseTokens.Add(sdk.NewRat(freeFermionsAcc)) // increase the supply + stakeData.Pool.LooseTokens = stakeData.Pool.LooseTokens.Add(sdk.NewDec(freeFermionsAcc)) // increase the supply // add the validator if len(genTx.Name) > 0 { @@ -193,10 +193,10 @@ func GaiaAppGenState(cdc *wire.Codec, appGenTxs []json.RawMessage) (genesisState validator := stake.NewValidator(genTx.Address, sdk.MustGetAccPubKeyBech32(genTx.PubKey), desc) - stakeData.Pool.LooseTokens = stakeData.Pool.LooseTokens.Add(sdk.NewRat(freeFermionVal)) // increase the supply + stakeData.Pool.LooseTokens = stakeData.Pool.LooseTokens.Add(sdk.NewDec(freeFermionVal)) // increase the supply // add some new shares to the validator - var issuedDelShares sdk.Rat + var issuedDelShares sdk.Dec validator, stakeData.Pool, issuedDelShares = validator.AddTokensFromDel(stakeData.Pool, freeFermionVal) stakeData.Validators = append(stakeData.Validators, validator) diff --git a/cmd/gaia/app/sim_test.go b/cmd/gaia/app/sim_test.go index f0bea1e17..6369b4ca6 100644 --- a/cmd/gaia/app/sim_test.go +++ b/cmd/gaia/app/sim_test.go @@ -48,7 +48,7 @@ func appStateFn(r *rand.Rand, accs []sdk.AccAddress) json.RawMessage { // Default genesis state stakeGenesis := stake.DefaultGenesisState() - stakeGenesis.Pool.LooseTokens = sdk.NewRat(1000) + stakeGenesis.Pool.LooseTokens = sdk.NewDec(1000) genesis := GenesisState{ Accounts: genesisAccounts, StakeData: stakeGenesis, diff --git a/cmd/gaia/cli_test/cli_test.go b/cmd/gaia/cli_test/cli_test.go index ea4f2e47d..244a9eb83 100644 --- a/cmd/gaia/cli_test/cli_test.go +++ b/cmd/gaia/cli_test/cli_test.go @@ -132,7 +132,7 @@ func TestGaiaCLICreateValidator(t *testing.T) { validator := executeGetValidator(t, fmt.Sprintf("gaiacli stake validator %s --output=json %v", barAddr, flags)) require.Equal(t, validator.Owner, barAddr) - require.True(sdk.RatEq(t, sdk.NewRat(2), validator.Tokens)) + require.True(sdk.DecEq(t, sdk.NewDec(2), validator.Tokens)) // unbond a single share unbondStr := fmt.Sprintf("gaiacli stake unbond begin %v", flags) @@ -149,7 +149,7 @@ func TestGaiaCLICreateValidator(t *testing.T) { require.Equal(t, int64(9), barAcc.GetCoins().AmountOf("steak").Int64(), "%v", barAcc) */ validator = executeGetValidator(t, fmt.Sprintf("gaiacli stake validator %s --output=json %v", barAddr, flags)) - require.Equal(t, "1/1", validator.Tokens.String()) + require.Equal(t, "1.0000000000", validator.Tokens.String()) } func TestGaiaCLISubmitProposal(t *testing.T) { diff --git a/docs/spec/governance/state.md b/docs/spec/governance/state.md index c61fd1e3f..728ded371 100644 --- a/docs/spec/governance/state.md +++ b/docs/spec/governance/state.md @@ -25,9 +25,9 @@ type VotingProcedure struct { ```go type TallyingProcedure struct { - Threshold rational.Rational // Minimum propotion of Yes votes for proposal to pass. Initial value: 0.5 - Veto rational.Rational // Minimum proportion of Veto votes to Total votes ratio for proposal to be vetoed. Initial value: 1/3 - GovernancePenalty sdk.Rat // Penalty if validator does not vote + Threshold sdk.Dec // Minimum propotion of Yes votes for proposal to pass. Initial value: 0.5 + Veto sdk.Dec // Minimum proportion of Veto votes to Total votes ratio for proposal to be vetoed. Initial value: 1/3 + GovernancePenalty sdk.Dec // Penalty if validator does not vote GracePeriod int64 // If validator entered validator set in this period of blocks before vote ended, governance penalty does not apply } ``` @@ -81,7 +81,7 @@ This type is used in a temp map when tallying ```go type ValidatorGovInfo struct { - Minus sdk.Rat + Minus sdk.Dec Vote Vote } ``` @@ -103,17 +103,17 @@ type Proposal struct { VotingStartBlock int64 // Height of the block where MinDeposit was reached. -1 if MinDeposit is not reached CurrentStatus ProposalStatus // Current status of the proposal - YesVotes sdk.Rat - NoVotes sdk.Rat - NoWithVetoVotes sdk.Rat - AbstainVotes sdk.Rat + YesVotes sdk.Dec + NoVotes sdk.Dec + NoWithVetoVotes sdk.Dec + AbstainVotes sdk.Dec } ``` We also mention a method to update the tally for a given proposal: ```go - func (proposal Proposal) updateTally(vote byte, amount sdk.Rat) + func (proposal Proposal) updateTally(vote byte, amount sdk.Dec) ``` ### Stores diff --git a/docs/spec/inflation/state.md b/docs/spec/inflation/state.md index dea10c046..c16286804 100644 --- a/docs/spec/inflation/state.md +++ b/docs/spec/inflation/state.md @@ -7,7 +7,7 @@ The current annual inflation rate. ```golang -type Inflation sdk.Rat +type Inflation sdk.Dec ``` ### InflationLastTime diff --git a/docs/spec/staking/end_block.md b/docs/spec/staking/end_block.md index b40b06b92..0eb557fec 100644 --- a/docs/spec/staking/end_block.md +++ b/docs/spec/staking/end_block.md @@ -16,4 +16,3 @@ EndBlock() ValidatorSetChanges ClearTendermintUpdates() return vsc ``` - diff --git a/docs/spec/staking/state.md b/docs/spec/staking/state.md index 376a38ced..46f18ed78 100644 --- a/docs/spec/staking/state.md +++ b/docs/spec/staking/state.md @@ -13,7 +13,7 @@ type Pool struct { LooseTokens int64 // tokens not associated with any bonded validator BondedTokens int64 // reserve of bonded tokens InflationLastTime int64 // block which the last inflation was processed // TODO make time - Inflation sdk.Rat // current annual inflation rate + Inflation sdk.Dec // current annual inflation rate DateLastCommissionReset int64 // unix timestamp for last commission accounting reset (daily) } @@ -28,10 +28,10 @@ overall functioning of the stake module. ```golang type Params struct { - InflationRateChange sdk.Rat // maximum annual change in inflation rate - InflationMax sdk.Rat // maximum inflation rate - InflationMin sdk.Rat // minimum inflation rate - GoalBonded sdk.Rat // Goal of percent bonded atoms + InflationRateChange sdk.Dec // maximum annual change in inflation rate + InflationMax sdk.Dec // maximum inflation rate + InflationMin sdk.Dec // minimum inflation rate + GoalBonded sdk.Dec // Goal of percent bonded atoms MaxValidators uint16 // maximum number of validators BondDenom string // bondable coin denomination @@ -74,9 +74,9 @@ type Validator struct { Revoked bool // has the validator been revoked? Status sdk.BondStatus // validator status (bonded/unbonding/unbonded) - Tokens sdk.Rat // delegated tokens (incl. self-delegation) - DelegatorShares sdk.Rat // total shares issued to a validator's delegators - SlashRatio sdk.Rat // increases each time the validator is slashed + Tokens sdk.Dec // delegated tokens (incl. self-delegation) + DelegatorShares sdk.Dec // total shares issued to a validator's delegators + SlashRatio sdk.Dec // increases each time the validator is slashed Description Description // description terms for the validator @@ -88,10 +88,10 @@ type Validator struct { } type CommissionInfo struct { - Rate sdk.Rat // the commission rate of fees charged to any delegators - Max sdk.Rat // maximum commission rate which this validator can ever charge - ChangeRate sdk.Rat // maximum daily increase of the validator commission - ChangeToday sdk.Rat // commission rate change today, reset each day (UTC time) + Rate sdk.Dec // the commission rate of fees charged to any delegators + Max sdk.Dec // maximum commission rate which this validator can ever charge + ChangeRate sdk.Dec // maximum daily increase of the validator commission + ChangeToday sdk.Dec // commission rate change today, reset each day (UTC time) LastChange int64 // unix timestamp of last commission change } @@ -117,7 +117,7 @@ the transaction is the owner of the bond. ```golang type Delegation struct { - Shares sdk.Rat // delegation shares recieved + Shares sdk.Dec // delegation shares recieved Height int64 // last height bond updated } ``` @@ -178,8 +178,8 @@ the original redelegation has been completed. ```golang type Redelegation struct { - SourceShares sdk.Rat // amount of source shares redelegating - DestinationShares sdk.Rat // amount of destination shares created at redelegation + SourceShares sdk.Dec // amount of source shares redelegating + DestinationShares sdk.Dec // amount of destination shares created at redelegation CompleteTime int64 // unix time to complete redelegation } ``` diff --git a/docs/spec/staking/transactions.md b/docs/spec/staking/transactions.md index 4f2567958..d1b6a3626 100644 --- a/docs/spec/staking/transactions.md +++ b/docs/spec/staking/transactions.md @@ -18,7 +18,7 @@ Other notes: - `sender` denotes the address of the sender of the transaction - `getXxx`, `setXxx`, and `removeXxx` functions are used to retrieve and modify objects from the store - - `sdk.Rat` refers to a rational numeric type specified by the SDK. + - `sdk.Dec` refers to a decimal type specified by the SDK. ### TxCreateValidator @@ -34,9 +34,9 @@ type TxCreateValidator struct { SelfDelegation coin.Coin Description Description - Commission sdk.Rat - CommissionMax sdk.Rat - CommissionMaxChange sdk.Rat + Commission sdk.Dec + CommissionMax sdk.Dec + CommissionMaxChange sdk.Dec } @@ -65,7 +65,7 @@ If either the `Description` (excluding `DateBonded` which is constant), ```golang type TxEditCandidacy struct { GovernancePubKey crypto.PubKey - Commission sdk.Rat + Commission sdk.Dec Description Description } @@ -199,7 +199,7 @@ type TxRedelegate struct { DelegatorAddr Address ValidatorFrom Validator ValidatorTo Validator - Shares sdk.Rat + Shares sdk.Dec CompletedTime int64 } diff --git a/examples/democoin/mock/validator.go b/examples/democoin/mock/validator.go index 0ef0a2391..789a1cb6a 100644 --- a/examples/democoin/mock/validator.go +++ b/examples/democoin/mock/validator.go @@ -10,7 +10,7 @@ import ( // Validator implements sdk.Validator type Validator struct { Address sdk.AccAddress - Power sdk.Rat + Power sdk.Dec } // Implements sdk.Validator @@ -29,18 +29,18 @@ func (v Validator) GetPubKey() crypto.PubKey { } // Implements sdk.Validator -func (v Validator) GetTokens() sdk.Rat { - return sdk.ZeroRat() +func (v Validator) GetTokens() sdk.Dec { + return sdk.ZeroDec() } // Implements sdk.Validator -func (v Validator) GetPower() sdk.Rat { +func (v Validator) GetPower() sdk.Dec { return v.Power } // Implements sdk.Validator -func (v Validator) GetDelegatorShares() sdk.Rat { - return sdk.ZeroRat() +func (v Validator) GetDelegatorShares() sdk.Dec { + return sdk.ZeroDec() } // Implements sdk.Validator @@ -93,8 +93,8 @@ func (vs *ValidatorSet) ValidatorByPubKey(ctx sdk.Context, pubkey crypto.PubKey) } // TotalPower implements sdk.ValidatorSet -func (vs *ValidatorSet) TotalPower(ctx sdk.Context) sdk.Rat { - res := sdk.ZeroRat() +func (vs *ValidatorSet) TotalPower(ctx sdk.Context) sdk.Dec { + res := sdk.ZeroDec() for _, val := range vs.Validators { res = res.Add(val.Power) } @@ -122,7 +122,7 @@ func (vs *ValidatorSet) RemoveValidator(addr sdk.AccAddress) { } // Implements sdk.ValidatorSet -func (vs *ValidatorSet) Slash(ctx sdk.Context, pubkey crypto.PubKey, height int64, power int64, amt sdk.Rat) { +func (vs *ValidatorSet) Slash(ctx sdk.Context, pubkey crypto.PubKey, height int64, power int64, amt sdk.Dec) { panic("not implemented") } diff --git a/examples/democoin/x/assoc/validator_set_test.go b/examples/democoin/x/assoc/validator_set_test.go index 2fead3ad2..eac23b25e 100644 --- a/examples/democoin/x/assoc/validator_set_test.go +++ b/examples/democoin/x/assoc/validator_set_test.go @@ -32,8 +32,8 @@ func TestValidatorSet(t *testing.T) { addr2 := []byte("addr2") base := &mock.ValidatorSet{[]mock.Validator{ - {addr1, sdk.NewRat(1)}, - {addr2, sdk.NewRat(2)}, + {addr1, sdk.NewDec(1)}, + {addr2, sdk.NewDec(2)}, }} valset := NewValidatorSet(wire.NewCodec(), ctx.KVStore(key).Prefix([]byte("assoc")), base, 1, 5) diff --git a/examples/democoin/x/oracle/README.md b/examples/democoin/x/oracle/README.md index eec02d724..0cfcb820d 100644 --- a/examples/democoin/x/oracle/README.md +++ b/examples/democoin/x/oracle/README.md @@ -38,7 +38,7 @@ func NewHandler(keeper Keeper) sdk.Handler { In the previous example, the keeper has an `oracle.Keeper`. `oracle.Keeper`s are generated by `NewKeeper`. ```go -func NewKeeper(key sdk.StoreKey, cdc *wire.Codec, valset sdk.ValidatorSet, supermaj sdk.Rat, timeout int64) Keeper { +func NewKeeper(key sdk.StoreKey, cdc *wire.Codec, valset sdk.ValidatorSet, supermaj sdk.Dec, timeout int64) Keeper { return Keeper { cdc: cdc, key: key, diff --git a/examples/democoin/x/oracle/handler.go b/examples/democoin/x/oracle/handler.go index 079f7680f..3c78fc566 100644 --- a/examples/democoin/x/oracle/handler.go +++ b/examples/democoin/x/oracle/handler.go @@ -23,7 +23,7 @@ func (keeper Keeper) update(ctx sdk.Context, val sdk.Validator, valset sdk.Valid // and recalculate voted power hash := ctx.BlockHeader().ValidatorsHash if !bytes.Equal(hash, info.Hash) { - info.Power = sdk.ZeroRat() + info.Power = sdk.ZeroDec() info.Hash = hash prefix := GetSignPrefix(p, keeper.cdc) store := ctx.KVStore(keeper.key) diff --git a/examples/democoin/x/oracle/keeper.go b/examples/democoin/x/oracle/keeper.go index 25554faa8..0406f560a 100644 --- a/examples/democoin/x/oracle/keeper.go +++ b/examples/democoin/x/oracle/keeper.go @@ -13,12 +13,12 @@ type Keeper struct { valset sdk.ValidatorSet - supermaj sdk.Rat + supermaj sdk.Dec timeout int64 } // NewKeeper constructs a new keeper -func NewKeeper(key sdk.StoreKey, cdc *wire.Codec, valset sdk.ValidatorSet, supermaj sdk.Rat, timeout int64) Keeper { +func NewKeeper(key sdk.StoreKey, cdc *wire.Codec, valset sdk.ValidatorSet, supermaj sdk.Dec, timeout int64) Keeper { if timeout < 0 { panic("Timeout should not be negative") } @@ -46,7 +46,7 @@ const ( // Info for each payload type Info struct { - Power sdk.Rat + Power sdk.Dec Hash []byte LastSigned int64 Status InfoStatus @@ -55,7 +55,7 @@ type Info struct { // EmptyInfo construct an empty Info func EmptyInfo(ctx sdk.Context) Info { return Info{ - Power: sdk.ZeroRat(), + Power: sdk.ZeroDec(), Hash: ctx.BlockHeader().ValidatorsHash, LastSigned: ctx.BlockHeight(), Status: Pending, diff --git a/examples/democoin/x/oracle/oracle_test.go b/examples/democoin/x/oracle/oracle_test.go index c476290f8..f4971c8b1 100644 --- a/examples/democoin/x/oracle/oracle_test.go +++ b/examples/democoin/x/oracle/oracle_test.go @@ -107,9 +107,9 @@ func TestOracle(t *testing.T) { addr3 := []byte("addr3") addr4 := []byte("addr4") valset := &mock.ValidatorSet{[]mock.Validator{ - {addr1, sdk.NewRat(7)}, - {addr2, sdk.NewRat(7)}, - {addr3, sdk.NewRat(1)}, + {addr1, sdk.NewDec(7)}, + {addr2, sdk.NewDec(7)}, + {addr3, sdk.NewDec(1)}, }} key := sdk.NewKVStoreKey("testkey") @@ -119,7 +119,7 @@ func TestOracle(t *testing.T) { require.Nil(t, err) ctx = ctx.WithBlockHeader(abci.Header{ValidatorsHash: bz}) - ork := NewKeeper(key, cdc, valset, sdk.NewRat(2, 3), 100) + ork := NewKeeper(key, cdc, valset, sdk.NewDecWithPrec(667, 3), 100) // 66.7% h := seqHandler(ork, key, sdk.CodespaceRoot) // Nonmock.Validator signed, transaction failed @@ -171,7 +171,7 @@ func TestOracle(t *testing.T) { require.Equal(t, 1, getSequence(ctx, key)) // Should handle mock.Validator set change - valset.AddValidator(mock.Validator{addr4, sdk.NewRat(12)}) + valset.AddValidator(mock.Validator{addr4, sdk.NewDec(12)}) bz, err = json.Marshal(valset) require.Nil(t, err) ctx = ctx.WithBlockHeader(abci.Header{ValidatorsHash: bz}) diff --git a/types/decimal.go b/types/decimal.go new file mode 100644 index 000000000..cd5c048e3 --- /dev/null +++ b/types/decimal.go @@ -0,0 +1,418 @@ +package types + +import ( + "encoding/json" + "fmt" + "math/big" + "strconv" + "strings" + "testing" +) + +// 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 = 10 + + // bytes required to represent the above precision + // ceil(log2(9999999999)) + DecimalPrecisionBits = 34 +) + +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) +} + +// nolint - common values +func ZeroDec() Dec { return Dec{new(big.Int).Set(zeroInt)} } +func OneDec() Dec { return Dec{precisionInt()} } + +// 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] +} + +//______________________________________________________________________________________________ + +// create a new Dec from integer assuming whole number +func NewDec(i int64) Dec { + return NewDecWithPrec(i, 0) +} + +// create 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)), + } +} + +// create a new Dec from big integer assuming whole numbers +// CONTRACT: prec <= Precision +func NewDecFromBigInt(i *big.Int) Dec { + return NewDecFromBigIntWithPrec(i, 0) +} + +// create 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)), + } +} + +// create a new Dec from big integer assuming whole numbers +// CONTRACT: prec <= Precision +func NewDecFromInt(i Int) Dec { + return NewDecFromIntWithPrec(i, 0) +} + +// create a new Dec from big integer with decimal place at prec +// CONTRACT: prec <= Precision +func NewDecFromIntWithPrec(i Int, prec int64) Dec { + return Dec{ + new(big.Int).Mul(i.BigInt(), precisionMultiplier(prec)), + } +} + +// create 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, ErrUnknownRequest("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, ErrUnknownRequest("decimal string is empty") + } + + strs := strings.Split(str, ".") + lenDecs := 0 + combinedStr := strs[0] + if len(strs) == 2 { + lenDecs = len(strs[1]) + if lenDecs == 0 || len(combinedStr) == 0 { + return d, ErrUnknownRequest("bad decimal length") + } + combinedStr = combinedStr + strs[1] + } else if len(strs) > 2 { + return d, ErrUnknownRequest("too many periods to be a decimal string") + } + + if lenDecs > Precision { + return d, ErrUnknownRequest( + 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 = combinedStr + zeros + + combined, ok := new(big.Int).SetString(combinedStr, 10) + if !ok { + return d, ErrUnknownRequest(fmt.Sprintf("bad string to integer conversion, combinedStr: %v", combinedStr)) + } + if neg { + combined = new(big.Int).Neg(combined) + } + return Dec{combined}, nil +} + +//______________________________________________________________________________________________ +//nolint +func (d Dec) IsZero() bool { return (d.Int).Sign() == 0 } // Is equal to zero +func (d Dec) Equal(d2 Dec) bool { return (d.Int).Cmp(d2.Int) == 0 } +func (d Dec) GT(d2 Dec) bool { return (d.Int).Cmp(d2.Int) > 0 } // greater than +func (d Dec) GTE(d2 Dec) bool { return (d.Int).Cmp(d2.Int) >= 0 } // greater than or equal +func (d Dec) LT(d2 Dec) bool { return (d.Int).Cmp(d2.Int) < 0 } // less than +func (d Dec) LTE(d2 Dec) bool { return (d.Int).Cmp(d2.Int) <= 0 } // less than or equal +func (d Dec) Neg() Dec { return Dec{new(big.Int).Neg(d.Int)} } // reverse the decimal sign + +// 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} +} + +// 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} +} + +// 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} +} + +// 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} +} + +func (d Dec) String() string { + str := d.ToLeftPaddedWithDecimals(Precision) + placement := len(str) - Precision + if placement < 0 { + panic("too few decimal digits") + } + return str[:placement] + "." + str[placement:] +} + +// TODO panic if negative or if totalDigits < len(initStr)??? +// evaluate as an integer and return left padded string +func (d Dec) ToLeftPaddedWithDecimals(totalDigits int8) string { + intStr := d.Int.String() + fcode := `%0` + strconv.Itoa(int(totalDigits)) + `s` + return fmt.Sprintf(fcode, intStr) +} + +// TODO panic if negative or if totalDigits < len(initStr)??? +// evaluate as an integer and return left padded string +func (d Dec) ToLeftPadded(totalDigits int8) string { + chopped := chopPrecisionAndRound(d.Int) + intStr := chopped.String() + fcode := `%0` + strconv.Itoa(int(totalDigits)) + `s` + return fmt.Sprintf(fcode, intStr) +} + +// ____ +// __| |__ "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. +// +// TODO We should make this function mutate the input. The functions here +// don't need to allocate different memory for chopped after computing the +// result +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 trucated quotient and remainder + quo, rem := big.NewInt(0), 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) + } +} + +// RoundInt64 rounds the decimal using bankers rounding +func (d Dec) RoundInt64() int64 { + chopped := chopPrecisionAndRound(d.Int) + if !chopped.IsInt64() { + panic("Int64() out of bound") + } + return chopped.Int64() +} + +// RoundInt round the decimal using bankers rounding +func (d Dec) RoundInt() Int { + return NewIntFromBigInt(chopPrecisionAndRound(d.Int)) +} + +//___________________________________________________________________________________ + +// 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") + } +} + +// 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 +} + +// 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 defines custom encoding scheme +func (d Dec) MarshalJSON() ([]byte, error) { + if d.Int == nil { + return nilJSON, nil + } + + bz, err := d.Int.MarshalText() + if err != nil { + return nil, err + } + return json.Marshal(string(bz)) +} + +// 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 + } + return d.Int.UnmarshalText([]byte(text)) +} + +//___________________________________________________________________________________ +// helpers + +// 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 +} + +// minimum decimal between two +func MinDec(d1, d2 Dec) Dec { + if d1.LT(d2) { + return d1 + } + return d2 +} + +// intended to be used with require/assert: require.True(DecEq(...)) +func DecEq(t *testing.T, exp, got Dec) (*testing.T, bool, string, Dec, Dec) { + return t, exp.Equal(got), "expected:\t%v\ngot:\t\t%v", exp, got +} diff --git a/types/decimal_test.go b/types/decimal_test.go new file mode 100644 index 000000000..115eeacb0 --- /dev/null +++ b/types/decimal_test.go @@ -0,0 +1,301 @@ +package types + +import ( + "math/big" + "testing" + + wire "github.com/cosmos/cosmos-sdk/wire" + "github.com/stretchr/testify/require" +) + +// 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(100000) + 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 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, expDiv, expAdd, expSub Dec + }{ + // d1 d2 MUL DIV ADD SUB + {NewDec(0), NewDec(0), NewDec(0), NewDec(0), NewDec(0), NewDec(0)}, + {NewDec(1), NewDec(0), NewDec(0), NewDec(0), NewDec(1), NewDec(1)}, + {NewDec(0), NewDec(1), NewDec(0), NewDec(0), NewDec(1), NewDec(-1)}, + {NewDec(0), NewDec(-1), NewDec(0), NewDec(0), NewDec(-1), NewDec(1)}, + {NewDec(-1), NewDec(0), NewDec(0), NewDec(0), 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(-2), NewDec(0)}, + {NewDec(1), NewDec(-1), NewDec(-1), NewDec(-1), NewDec(0), NewDec(2)}, + {NewDec(-1), NewDec(1), NewDec(-1), NewDec(-1), NewDec(0), NewDec(-2)}, + + {NewDec(3), NewDec(7), NewDec(21), NewDecWithPrec(4285714286, 10), NewDec(10), NewDec(-4)}, + {NewDec(2), NewDec(4), NewDec(8), NewDecWithPrec(5, 1), NewDec(6), NewDec(-2)}, + {NewDec(100), NewDec(100), NewDec(10000), NewDec(1), NewDec(200), NewDec(0)}, + + {NewDecWithPrec(15, 1), NewDecWithPrec(15, 1), NewDecWithPrec(225, 2), + NewDec(1), NewDec(3), NewDec(0)}, + {NewDecWithPrec(3333, 4), NewDecWithPrec(333, 4), NewDecWithPrec(1109889, 8), + NewDecWithPrec(10009009009, 9), 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) + 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) + + if tc.d2.IsZero() { // panic for divide by zero + require.Panics(t, func() { tc.d1.Quo(tc.d2) }) + } else { + resDiv := tc.d1.Quo(tc.d2) + require.True(t, tc.expDiv.Equal(resDiv), "exp %v, res %v, tc %d", tc.expDiv.String(), resDiv.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 TestToLeftPadded(t *testing.T) { + tests := []struct { + dec Dec + digits int8 + exp string + }{ + {mustNewDecFromStr(t, "33.3"), 8, "00000033"}, + {mustNewDecFromStr(t, "50"), 8, "00000050"}, + {mustNewDecFromStr(t, "333"), 8, "00000333"}, + {mustNewDecFromStr(t, "333"), 12, "000000000333"}, + {mustNewDecFromStr(t, "0.3333"), 8, "00000000"}, + } + for tcIndex, tc := range tests { + res := tc.dec.ToLeftPadded(tc.digits) + require.Equal(t, tc.exp, res, "incorrect left padding, tc %d", tcIndex) + } +} + +var cdc = wire.NewCodec() + +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 TestSerializationGoWireJSON(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 TestSerializationGoWireBinary(t *testing.T) { + d := mustNewDecFromStr(t, "0.333") + + bz, err := cdc.MarshalBinary(d) + require.NoError(t, err) + + var d2 Dec + err = cdc.UnmarshalBinary(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 TestEmbeddedStructSerializationGoWire(t *testing.T) { + obj := testDEmbedStruct{"foo", 10, NewDecWithPrec(1, 3)} + bz, err := cdc.MarshalBinary(obj) + require.Nil(t, err) + + var obj2 testDEmbedStruct + err = cdc.UnmarshalBinary(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.0000000000", + dec3.String(), + ) +} diff --git a/types/rational.go b/types/rational.go deleted file mode 100644 index 89cc76968..000000000 --- a/types/rational.go +++ /dev/null @@ -1,262 +0,0 @@ -package types - -import ( - "fmt" - "math/big" - "strconv" - "strings" - "testing" -) - -// "that's one big rat!" -// ______ -// / / /\ \____oo -// __ /___...._____ _\o -// __| |_ |_ - -// NOTE: never use new(Rat) or else -// we will panic unmarshalling into the -// nil embedded big.Rat -type Rat struct { - *big.Rat `json:"rat"` -} - -// nolint - common values -func ZeroRat() Rat { return Rat{big.NewRat(0, 1)} } -func OneRat() Rat { return Rat{big.NewRat(1, 1)} } - -// New - create a new Rat from integers -func NewRat(Numerator int64, Denominator ...int64) Rat { - switch len(Denominator) { - case 0: - return Rat{big.NewRat(Numerator, 1)} - case 1: - return Rat{big.NewRat(Numerator, Denominator[0])} - default: - panic("improper use of New, can only have one denominator") - } -} - -func getNumeratorDenominator(str []string, prec int) (numerator string, denom int64, err Error) { - switch len(str) { - case 1: - if len(str[0]) == 0 { - return "", 0, ErrUnknownRequest("not a decimal string") - } - numerator = str[0] - return numerator, 1, nil - case 2: - if len(str[0]) == 0 || len(str[1]) == 0 { - return "", 0, ErrUnknownRequest("not a decimal string") - } - if len(str[1]) > prec { - return "", 0, ErrUnknownRequest("string has too many decimals") - } - numerator = str[0] + str[1] - len := int64(len(str[1])) - denom = new(big.Int).Exp(big.NewInt(10), big.NewInt(len), nil).Int64() - return numerator, denom, nil - default: - return "", 0, ErrUnknownRequest("not a decimal string") - } -} - -// create a rational from decimal string or integer string -// precision is the number of values after the decimal point which should be read -func NewRatFromDecimal(decimalStr string, prec int) (f Rat, err Error) { - // first extract any negative symbol - if len(decimalStr) == 0 { - return f, ErrUnknownRequest("decimal string is empty") - } - - neg := false - if string(decimalStr[0]) == "-" { - neg = true - decimalStr = decimalStr[1:] - } - - str := strings.Split(decimalStr, ".") - - numStr, denom, err := getNumeratorDenominator(str, prec) - if err != nil { - return f, err - } - - num, errConv := strconv.Atoi(numStr) - if errConv != nil && strings.HasSuffix(errConv.Error(), "value out of range") { - // resort to big int, don't make this default option for efficiency - numBig, success := new(big.Int).SetString(numStr, 10) - if success != true { - return f, ErrUnknownRequest("not a decimal string") - } - - if neg { - numBig.Neg(numBig) - } - - return NewRatFromBigInt(numBig, big.NewInt(denom)), nil - } else if errConv != nil { - return f, ErrUnknownRequest("not a decimal string") - } - - if neg { - num *= -1 - } - - return NewRat(int64(num), denom), nil -} - -// NewRatFromBigInt constructs Rat from big.Int -func NewRatFromBigInt(num *big.Int, denom ...*big.Int) Rat { - switch len(denom) { - case 0: - return Rat{new(big.Rat).SetInt(num)} - case 1: - return Rat{new(big.Rat).SetFrac(num, denom[0])} - default: - panic("improper use of NewRatFromBigInt, can only have one denominator") - } -} - -// NewRatFromInt constructs Rat from Int -func NewRatFromInt(num Int, denom ...Int) Rat { - switch len(denom) { - case 0: - return Rat{new(big.Rat).SetInt(num.BigInt())} - case 1: - return Rat{new(big.Rat).SetFrac(num.BigInt(), denom[0].BigInt())} - default: - panic("improper use of NewRatFromBigInt, can only have one denominator") - } -} - -//nolint -func (r Rat) Num() Int { return Int{r.Rat.Num()} } // Num - return the numerator -func (r Rat) Denom() Int { return Int{r.Rat.Denom()} } // Denom - return the denominator -func (r Rat) IsZero() bool { return r.Num().IsZero() } // IsZero - Is the Rat equal to zero -func (r Rat) Equal(r2 Rat) bool { return (r.Rat).Cmp(r2.Rat) == 0 } -func (r Rat) GT(r2 Rat) bool { return (r.Rat).Cmp(r2.Rat) == 1 } // greater than -func (r Rat) GTE(r2 Rat) bool { return !r.LT(r2) } // greater than or equal -func (r Rat) LT(r2 Rat) bool { return (r.Rat).Cmp(r2.Rat) == -1 } // less than -func (r Rat) LTE(r2 Rat) bool { return !r.GT(r2) } // less than or equal -func (r Rat) Mul(r2 Rat) Rat { return Rat{new(big.Rat).Mul(r.Rat, r2.Rat)} } // Mul - multiplication -func (r Rat) Quo(r2 Rat) Rat { return Rat{new(big.Rat).Quo(r.Rat, r2.Rat)} } // Quo - quotient -func (r Rat) Add(r2 Rat) Rat { return Rat{new(big.Rat).Add(r.Rat, r2.Rat)} } // Add - addition -func (r Rat) Sub(r2 Rat) Rat { return Rat{new(big.Rat).Sub(r.Rat, r2.Rat)} } // Sub - subtraction -func (r Rat) String() string { return r.Rat.String() } -func (r Rat) FloatString() string { return r.Rat.FloatString(10) } // a human-friendly string format. The last digit is rounded to nearest, with halves rounded away from zero. - -var ( - zero = big.NewInt(0) - one = big.NewInt(1) - two = big.NewInt(2) - five = big.NewInt(5) - nFive = big.NewInt(-5) - ten = big.NewInt(10) -) - -// evaluate the rational using bankers rounding -func (r Rat) EvaluateBig() *big.Int { - - num := r.Rat.Num() - denom := r.Rat.Denom() - - d, rem := new(big.Int), new(big.Int) - d.QuoRem(num, denom, rem) - if rem.Cmp(zero) == 0 { // is the remainder zero - return d - } - - // evaluate the remainder using bankers rounding - tenNum := new(big.Int).Mul(num, ten) - tenD := new(big.Int).Mul(d, ten) - remainderDigit := new(big.Int).Sub(new(big.Int).Quo(tenNum, denom), tenD) // get the first remainder digit - isFinalDigit := (new(big.Int).Rem(tenNum, denom).Cmp(zero) == 0) // is this the final digit in the remainder? - - switch { - case isFinalDigit && (remainderDigit.Cmp(five) == 0 || remainderDigit.Cmp(nFive) == 0): - dRem2 := new(big.Int).Rem(d, two) - return new(big.Int).Add(d, dRem2) // always rounds to the even number - case remainderDigit.Cmp(five) != -1: //remainderDigit >= 5: - d.Add(d, one) - case remainderDigit.Cmp(nFive) != 1: //remainderDigit <= -5: - d.Sub(d, one) - } - return d -} - -// RoundInt64 rounds the rational using bankers rounding -func (r Rat) RoundInt64() int64 { - return r.EvaluateBig().Int64() -} - -// RoundInt round the rational using bankers rounding -func (r Rat) RoundInt() Int { - return NewIntFromBigInt(r.EvaluateBig()) -} - -// round Rat with the provided precisionFactor -func (r Rat) Round(precisionFactor int64) Rat { - rTen := Rat{new(big.Rat).Mul(r.Rat, big.NewRat(precisionFactor, 1))} - return Rat{big.NewRat(rTen.RoundInt64(), precisionFactor)} -} - -// TODO panic if negative or if totalDigits < len(initStr)??? -// evaluate as an integer and return left padded string -func (r Rat) ToLeftPadded(totalDigits int8) string { - intStr := r.EvaluateBig().String() - fcode := `%0` + strconv.Itoa(int(totalDigits)) + `s` - return fmt.Sprintf(fcode, intStr) -} - -//___________________________________________________________________________________ - -//Wraps r.MarshalText(). -func (r Rat) MarshalAmino() (string, error) { - if r.Rat == nil { - r.Rat = new(big.Rat) - } - bz, err := r.Rat.MarshalText() - return string(bz), err -} - -// Requires a valid JSON string - strings quotes and calls UnmarshalText -func (r *Rat) UnmarshalAmino(text string) (err error) { - tempRat := big.NewRat(0, 1) - err = tempRat.UnmarshalText([]byte(text)) - if err != nil { - return err - } - r.Rat = tempRat - return nil -} - -//___________________________________________________________________________________ -// helpers - -// test if two rat arrays are equal -func RatsEqual(r1s, r2s []Rat) bool { - if len(r1s) != len(r2s) { - return false - } - - for i, r1 := range r1s { - if !r1.Equal(r2s[i]) { - return false - } - } - return true -} - -// intended to be used with require/assert: require.True(RatEq(...)) -func RatEq(t *testing.T, exp, got Rat) (*testing.T, bool, string, Rat, Rat) { - return t, exp.Equal(got), "expected:\t%v\ngot:\t\t%v", exp, got -} - -// minimum rational between two -func MinRat(r1, r2 Rat) Rat { - if r1.LT(r2) { - return r1 - } - return r2 -} diff --git a/types/rational_test.go b/types/rational_test.go deleted file mode 100644 index 940de23dc..000000000 --- a/types/rational_test.go +++ /dev/null @@ -1,402 +0,0 @@ -package types - -import ( - "math/big" - "math/rand" - "testing" - - wire "github.com/cosmos/cosmos-sdk/wire" - "github.com/stretchr/testify/require" -) - -func TestNew(t *testing.T) { - require.Equal(t, NewRat(1), NewRat(1, 1)) - require.Equal(t, NewRat(100), NewRat(100, 1)) - require.Equal(t, NewRat(-1), NewRat(-1, 1)) - require.Equal(t, NewRat(-100), NewRat(-100, 1)) - require.Equal(t, NewRat(0), NewRat(0, 1)) - - // do not allow for more than 2 variables - require.Panics(t, func() { NewRat(1, 1, 1) }) -} - -func TestNewFromDecimal(t *testing.T) { - largeBigInt, success := new(big.Int).SetString("3109736052979742687701388262607869", 10) - require.True(t, success) - tests := []struct { - decimalStr string - expErr bool - exp Rat - }{ - {"", true, Rat{}}, - {"0", false, NewRat(0)}, - {"1", false, NewRat(1)}, - {"1.1", false, NewRat(11, 10)}, - {"0.75", false, NewRat(3, 4)}, - {"0.8", false, NewRat(4, 5)}, - {"0.11111", true, NewRat(1111, 10000)}, - {"628240629832763.5738930323617075341", true, NewRat(3141203149163817869, 5000)}, - {"621947210595948537540277652521.5738930323617075341", - true, NewRatFromBigInt(largeBigInt, big.NewInt(5000))}, - {"628240629832763.5738", false, NewRat(3141203149163817869, 5000)}, - {"621947210595948537540277652521.5738", - false, NewRatFromBigInt(largeBigInt, big.NewInt(5000))}, - {".", true, Rat{}}, - {".0", true, Rat{}}, - {"1.", true, Rat{}}, - {"foobar", true, Rat{}}, - {"0.foobar", true, Rat{}}, - {"0.foobar.", true, Rat{}}, - } - - for tcIndex, tc := range tests { - res, err := NewRatFromDecimal(tc.decimalStr, 4) - if tc.expErr { - require.NotNil(t, err, tc.decimalStr, "error expected, tc #%d", tcIndex) - } else { - require.Nil(t, err, tc.decimalStr, "unexpected error, tc #%d", tcIndex) - require.True(t, res.Equal(tc.exp), tc.decimalStr, "equality was incorrect, tc #%d", tcIndex) - } - - // negative tc - res, err = NewRatFromDecimal("-"+tc.decimalStr, 4) - if tc.expErr { - require.NotNil(t, err, tc.decimalStr, "error expected (negative case), tc #%d", tcIndex) - } else { - require.Nil(t, err, tc.decimalStr, "unexpected error (negative case), tc #%d", tcIndex) - require.True(t, res.Equal(tc.exp.Mul(NewRat(-1))), tc.decimalStr, "equality was incorrect (negative case), tc #%d", tcIndex) - } - } -} - -func TestEqualities(t *testing.T) { - tests := []struct { - r1, r2 Rat - gt, lt, eq bool - }{ - {NewRat(0), NewRat(0), false, false, true}, - {NewRat(0, 100), NewRat(0, 10000), false, false, true}, - {NewRat(100), NewRat(100), false, false, true}, - {NewRat(-100), NewRat(-100), false, false, true}, - {NewRat(-100, -1), NewRat(100), false, false, true}, - {NewRat(-1, 1), NewRat(1, -1), false, false, true}, - {NewRat(1, -1), NewRat(-1, 1), false, false, true}, - {NewRat(3, 7), NewRat(3, 7), false, false, true}, - - {NewRat(0), NewRat(3, 7), false, true, false}, - {NewRat(0), NewRat(100), false, true, false}, - {NewRat(-1), NewRat(3, 7), false, true, false}, - {NewRat(-1), NewRat(100), false, true, false}, - {NewRat(1, 7), NewRat(100), false, true, false}, - {NewRat(1, 7), NewRat(3, 7), false, true, false}, - {NewRat(-3, 7), NewRat(-1, 7), false, true, false}, - - {NewRat(3, 7), NewRat(0), true, false, false}, - {NewRat(100), NewRat(0), true, false, false}, - {NewRat(3, 7), NewRat(-1), true, false, false}, - {NewRat(100), NewRat(-1), true, false, false}, - {NewRat(100), NewRat(1, 7), true, false, false}, - {NewRat(3, 7), NewRat(1, 7), true, false, false}, - {NewRat(-1, 7), NewRat(-3, 7), true, false, false}, - } - - for tcIndex, tc := range tests { - require.Equal(t, tc.gt, tc.r1.GT(tc.r2), "GT result is incorrect, tc #%d", tcIndex) - require.Equal(t, tc.lt, tc.r1.LT(tc.r2), "LT result is incorrect, tc #%d", tcIndex) - require.Equal(t, tc.eq, tc.r1.Equal(tc.r2), "equality result is incorrect, tc #%d", tcIndex) - } - -} - -func TestArithmetic(t *testing.T) { - tests := []struct { - r1, r2 Rat - resMul, resDiv, resAdd, resSub Rat - }{ - // r1 r2 MUL DIV ADD SUB - {NewRat(0), NewRat(0), NewRat(0), NewRat(0), NewRat(0), NewRat(0)}, - {NewRat(1), NewRat(0), NewRat(0), NewRat(0), NewRat(1), NewRat(1)}, - {NewRat(0), NewRat(1), NewRat(0), NewRat(0), NewRat(1), NewRat(-1)}, - {NewRat(0), NewRat(-1), NewRat(0), NewRat(0), NewRat(-1), NewRat(1)}, - {NewRat(-1), NewRat(0), NewRat(0), NewRat(0), NewRat(-1), NewRat(-1)}, - - {NewRat(1), NewRat(1), NewRat(1), NewRat(1), NewRat(2), NewRat(0)}, - {NewRat(-1), NewRat(-1), NewRat(1), NewRat(1), NewRat(-2), NewRat(0)}, - {NewRat(1), NewRat(-1), NewRat(-1), NewRat(-1), NewRat(0), NewRat(2)}, - {NewRat(-1), NewRat(1), NewRat(-1), NewRat(-1), NewRat(0), NewRat(-2)}, - - {NewRat(3), NewRat(7), NewRat(21), NewRat(3, 7), NewRat(10), NewRat(-4)}, - {NewRat(2), NewRat(4), NewRat(8), NewRat(1, 2), NewRat(6), NewRat(-2)}, - {NewRat(100), NewRat(100), NewRat(10000), NewRat(1), NewRat(200), NewRat(0)}, - - {NewRat(3, 2), NewRat(3, 2), NewRat(9, 4), NewRat(1), NewRat(3), NewRat(0)}, - {NewRat(3, 7), NewRat(7, 3), NewRat(1), NewRat(9, 49), NewRat(58, 21), NewRat(-40, 21)}, - {NewRat(1, 21), NewRat(11, 5), NewRat(11, 105), NewRat(5, 231), NewRat(236, 105), NewRat(-226, 105)}, - {NewRat(-21), NewRat(3, 7), NewRat(-9), NewRat(-49), NewRat(-144, 7), NewRat(-150, 7)}, - {NewRat(100), NewRat(1, 7), NewRat(100, 7), NewRat(700), NewRat(701, 7), NewRat(699, 7)}, - } - - for tcIndex, tc := range tests { - require.True(t, tc.resMul.Equal(tc.r1.Mul(tc.r2)), "r1 %v, r2 %v. tc #%d", tc.r1.Rat, tc.r2.Rat, tcIndex) - require.True(t, tc.resAdd.Equal(tc.r1.Add(tc.r2)), "r1 %v, r2 %v. tc #%d", tc.r1.Rat, tc.r2.Rat, tcIndex) - require.True(t, tc.resSub.Equal(tc.r1.Sub(tc.r2)), "r1 %v, r2 %v. tc #%d", tc.r1.Rat, tc.r2.Rat, tcIndex) - - if tc.r2.Num().IsZero() { // panic for divide by zero - require.Panics(t, func() { tc.r1.Quo(tc.r2) }) - } else { - require.True(t, tc.resDiv.Equal(tc.r1.Quo(tc.r2)), "r1 %v, r2 %v. tc #%d", tc.r1.Rat, tc.r2.Rat, tcIndex) - } - } -} - -func TestEvaluate(t *testing.T) { - tests := []struct { - r1 Rat - res int64 - }{ - {NewRat(0), 0}, - {NewRat(1), 1}, - {NewRat(1, 4), 0}, - {NewRat(1, 2), 0}, - {NewRat(3, 4), 1}, - {NewRat(5, 6), 1}, - {NewRat(3, 2), 2}, - {NewRat(5, 2), 2}, - {NewRat(6, 11), 1}, // 0.545-> 1 even though 5 is first decimal and 1 not even - {NewRat(17, 11), 2}, // 1.545 - {NewRat(5, 11), 0}, - {NewRat(16, 11), 1}, - {NewRat(113, 12), 9}, - } - - for tcIndex, tc := range tests { - require.Equal(t, tc.res, tc.r1.RoundInt64(), "%v. tc #%d", tc.r1, tcIndex) - require.Equal(t, tc.res*-1, tc.r1.Mul(NewRat(-1)).RoundInt64(), "%v. tc #%d", tc.r1.Mul(NewRat(-1)), tcIndex) - } -} - -func TestRound(t *testing.T) { - many3 := "333333333333333333333333333333333333333333333" - many7 := "777777777777777777777777777777777777777777777" - big3, worked := new(big.Int).SetString(many3, 10) - require.True(t, worked) - big7, worked := new(big.Int).SetString(many7, 10) - require.True(t, worked) - - tests := []struct { - r, res Rat - precFactor int64 - }{ - {NewRat(333, 777), NewRat(429, 1000), 1000}, - {Rat{new(big.Rat).SetFrac(big3, big7)}, NewRat(429, 1000), 1000}, - {Rat{new(big.Rat).SetFrac(big3, big7)}, Rat{big.NewRat(4285714286, 10000000000)}, 10000000000}, - {NewRat(1, 2), NewRat(1, 2), 1000}, - } - - for tcIndex, tc := range tests { - require.Equal(t, tc.res, tc.r.Round(tc.precFactor), "%v", tc.r, "incorrect rounding, tc #%d", tcIndex) - negR1, negRes := tc.r.Mul(NewRat(-1)), tc.res.Mul(NewRat(-1)) - require.Equal(t, negRes, negR1.Round(tc.precFactor), "%v", negR1, "incorrect rounding (negative case), tc #%d", tcIndex) - } -} - -func TestToLeftPadded(t *testing.T) { - tests := []struct { - rat Rat - digits int8 - res string - }{ - {NewRat(100, 3), 8, "00000033"}, - {NewRat(1, 3), 8, "00000000"}, - {NewRat(100, 2), 8, "00000050"}, - {NewRat(1000, 3), 8, "00000333"}, - {NewRat(1000, 3), 12, "000000000333"}, - } - for tcIndex, tc := range tests { - require.Equal(t, tc.res, tc.rat.ToLeftPadded(tc.digits), "incorrect left padding, tc #%d", tcIndex) - } -} - -var cdc = wire.NewCodec() //var jsonCdc JSONCodec // TODO wire.Codec - -func TestZeroSerializationJSON(t *testing.T) { - r := NewRat(0, 1) - err := cdc.UnmarshalJSON([]byte(`"0/1"`), &r) - require.Nil(t, err) - err = cdc.UnmarshalJSON([]byte(`"0/0"`), &r) - require.NotNil(t, err) - err = cdc.UnmarshalJSON([]byte(`"1/0"`), &r) - require.NotNil(t, err) - err = cdc.UnmarshalJSON([]byte(`"{}"`), &r) - require.NotNil(t, err) -} - -func TestSerializationText(t *testing.T) { - r := NewRat(1, 3) - - bz, err := r.MarshalText() - require.NoError(t, err) - - var r2 = Rat{new(big.Rat)} - err = r2.UnmarshalText(bz) - require.NoError(t, err) - require.True(t, r.Equal(r2), "original: %v, unmarshalled: %v", r, r2) -} - -func TestSerializationGoWireJSON(t *testing.T) { - r := NewRat(1, 3) - bz, err := cdc.MarshalJSON(r) - require.NoError(t, err) - - var r2 Rat - err = cdc.UnmarshalJSON(bz, &r2) - require.NoError(t, err) - require.True(t, r.Equal(r2), "original: %v, unmarshalled: %v", r, r2) -} - -func TestSerializationGoWireBinary(t *testing.T) { - r := NewRat(1, 3) - bz, err := cdc.MarshalBinary(r) - require.NoError(t, err) - - var r2 Rat - err = cdc.UnmarshalBinary(bz, &r2) - require.NoError(t, err) - require.True(t, r.Equal(r2), "original: %v, unmarshalled: %v", r, r2) -} - -type testEmbedStruct struct { - Field1 string `json:"f1"` - Field2 int `json:"f2"` - Field3 Rat `json:"f3"` -} - -func TestEmbeddedStructSerializationGoWire(t *testing.T) { - obj := testEmbedStruct{"foo", 10, NewRat(1, 3)} - bz, err := cdc.MarshalJSON(obj) - require.Nil(t, err) - - var obj2 testEmbedStruct - err = cdc.UnmarshalJSON(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 TestRatsEqual(t *testing.T) { - tests := []struct { - r1s, r2s []Rat - eq bool - }{ - {[]Rat{NewRat(0)}, []Rat{NewRat(0)}, true}, - {[]Rat{NewRat(0)}, []Rat{NewRat(1)}, false}, - {[]Rat{NewRat(0)}, []Rat{}, false}, - {[]Rat{NewRat(0), NewRat(1)}, []Rat{NewRat(0), NewRat(1)}, true}, - {[]Rat{NewRat(1), NewRat(0)}, []Rat{NewRat(1), NewRat(0)}, true}, - {[]Rat{NewRat(1), NewRat(0)}, []Rat{NewRat(0), NewRat(1)}, false}, - {[]Rat{NewRat(1), NewRat(0)}, []Rat{NewRat(1)}, false}, - {[]Rat{NewRat(1), NewRat(2)}, []Rat{NewRat(2), NewRat(4)}, false}, - {[]Rat{NewRat(3), NewRat(18)}, []Rat{NewRat(1), NewRat(6)}, false}, - } - - for tcIndex, tc := range tests { - require.Equal(t, tc.eq, RatsEqual(tc.r1s, tc.r2s), "equality of rational arrays is incorrect, tc #%d", tcIndex) - require.Equal(t, tc.eq, RatsEqual(tc.r2s, tc.r1s), "equality of rational arrays is incorrect (converse), tc #%d", tcIndex) - } - -} - -func TestStringOverflow(t *testing.T) { - // two random 64 bit primes - rat1 := NewRat(5164315003622678713, 4389711697696177267) - rat2 := NewRat(-3179849666053572961, 8459429845579852627) - rat3 := rat1.Add(rat2) - require.Equal(t, - "29728537197630860939575850336935951464/37134458148982045574552091851127630409", - rat3.String(), - ) -} - -// Tests below uses randomness -// Since we are using *big.Rat as underlying value -// and (U/)Int is immutable value(see TestImmutability(U/)Int) -// it is safe to use randomness in the tests -func TestArithRat(t *testing.T) { - for i := 0; i < 20; i++ { - n1 := NewInt(int64(rand.Int31())) - d1 := NewInt(int64(rand.Int31())) - rat1 := NewRatFromInt(n1, d1) - - n2 := NewInt(int64(rand.Int31())) - d2 := NewInt(int64(rand.Int31())) - rat2 := NewRatFromInt(n2, d2) - - n1d2 := n1.Mul(d2) - n2d1 := n2.Mul(d1) - - cases := []struct { - nres Int - dres Int - rres Rat - }{ - {n1d2.Add(n2d1), d1.Mul(d2), rat1.Add(rat2)}, - {n1d2.Sub(n2d1), d1.Mul(d2), rat1.Sub(rat2)}, - {n1.Mul(n2), d1.Mul(d2), rat1.Mul(rat2)}, - {n1d2, n2d1, rat1.Quo(rat2)}, - } - - for _, tc := range cases { - require.Equal(t, NewRatFromInt(tc.nres, tc.dres), tc.rres) - } - } -} - -func TestCompRat(t *testing.T) { - for i := 0; i < 20; i++ { - n1 := NewInt(int64(rand.Int31())) - d1 := NewInt(int64(rand.Int31())) - rat1 := NewRatFromInt(n1, d1) - - n2 := NewInt(int64(rand.Int31())) - d2 := NewInt(int64(rand.Int31())) - rat2 := NewRatFromInt(n2, d2) - - n1d2 := n1.Mul(d2) - n2d1 := n2.Mul(d1) - - cases := []struct { - ires bool - rres bool - }{ - {n1d2.Equal(n2d1), rat1.Equal(rat2)}, - {n1d2.GT(n2d1), rat1.GT(rat2)}, - {n1d2.LT(n2d1), rat1.LT(rat2)}, - {n1d2.GT(n2d1) || n1d2.Equal(n2d1), rat1.GTE(rat2)}, - {n1d2.LT(n2d1) || n1d2.Equal(n2d1), rat1.LTE(rat2)}, - } - - for _, tc := range cases { - require.Equal(t, tc.ires, tc.rres) - } - } -} - -func TestImmutabilityRat(t *testing.T) { - for i := 0; i < 20; i++ { - n := int64(rand.Int31()) - r := NewRat(n) - z := ZeroRat() - o := OneRat() - - r.Add(z) - r.Sub(z) - r.Mul(o) - r.Quo(o) - - require.Equal(t, n, r.RoundInt64()) - require.True(t, NewRat(n).Equal(r)) - } - -} diff --git a/types/stake.go b/types/stake.go index 4e3cf38a3..acd24a154 100644 --- a/types/stake.go +++ b/types/stake.go @@ -42,9 +42,9 @@ type Validator interface { GetStatus() BondStatus // status of the validator GetOwner() AccAddress // owner AccAddress to receive/return validators coins GetPubKey() crypto.PubKey // validation pubkey - GetPower() Rat // validation power - GetTokens() Rat // validation tokens - GetDelegatorShares() Rat // Total out standing delegator shares + GetPower() Dec // validation power + GetTokens() Dec // validation tokens + GetDelegatorShares() Dec // Total out standing delegator shares GetBondHeight() int64 // height in which the validator became active } @@ -68,10 +68,10 @@ type ValidatorSet interface { Validator(Context, AccAddress) Validator // get a particular validator by owner AccAddress ValidatorByPubKey(Context, crypto.PubKey) Validator // get a particular validator by signing PubKey - TotalPower(Context) Rat // total power of the validator set + TotalPower(Context) Dec // total power of the validator set // slash the validator and delegators of the validator, specifying offence height, offence power, and slash fraction - Slash(Context, crypto.PubKey, int64, int64, Rat) + Slash(Context, crypto.PubKey, int64, int64, Dec) Revoke(Context, crypto.PubKey) // revoke a validator Unrevoke(Context, crypto.PubKey) // unrevoke a validator } @@ -82,7 +82,7 @@ type ValidatorSet interface { type Delegation interface { GetDelegator() AccAddress // delegator AccAddress for the bond GetValidator() AccAddress // validator owner AccAddress for the bond - GetBondShares() Rat // amount of validator's shares + GetBondShares() Dec // amount of validator's shares } // properties for the set of all delegations for a particular diff --git a/x/distribution/keeper.go b/x/distribution/keeper.go index 145071719..aba07eaca 100644 --- a/x/distribution/keeper.go +++ b/x/distribution/keeper.go @@ -23,13 +23,13 @@ package stake ////_________________________________________________________________________ //// cummulative power of the non-absent prevotes -//func (k Keeper) GetTotalPrecommitVotingPower(ctx sdk.Context) sdk.Rat { +//func (k Keeper) GetTotalPrecommitVotingPower(ctx sdk.Context) sdk.Dec { //store := ctx.KVStore(k.storeKey) //// get absent prevote indexes //absents := ctx.AbsentValidators() -//TotalPower := sdk.ZeroRat() +//TotalPower := sdk.ZeroDec() //i := int32(0) //iterator := store.SubspaceIterator(ValidatorsBondedKey) //for ; iterator.Valid(); iterator.Next() { diff --git a/x/distribution/keeper_test.go b/x/distribution/keeper_test.go index 0d732f8a1..890268060 100644 --- a/x/distribution/keeper_test.go +++ b/x/distribution/keeper_test.go @@ -8,8 +8,8 @@ package stake //var candidatesIn [5]Candidate //for i, amt := range amts { //candidatesIn[i] = NewCandidate(addrVals[i], pks[i], Description{}) -//candidatesIn[i].BondedShares = sdk.NewRat(amt) -//candidatesIn[i].DelegatorShares = sdk.NewRat(amt) +//candidatesIn[i].BondedShares = sdk.NewDec(amt) +//candidatesIn[i].DelegatorShares = sdk.NewDec(amt) //keeper.setCandidate(ctx, candidatesIn[i]) //} @@ -18,7 +18,7 @@ package stake //require.Equal(t, 5, len(gotValidators)) //totPow := keeper.GetTotalPrecommitVotingPower(ctx) -//exp := sdk.NewRat(11111) +//exp := sdk.NewDec(11111) //require.True(t, exp.Equal(totPow), "exp %v, got %v", exp, totPow) //// set absent gotValidators to be the 1st and 3rd record sorted by pubKey address @@ -26,6 +26,6 @@ package stake //totPow = keeper.GetTotalPrecommitVotingPower(ctx) //// XXX verify that this order should infact exclude these two records -//exp = sdk.NewRat(11100) +//exp = sdk.NewDec(11100) //require.True(t, exp.Equal(totPow), "exp %v, got %v", exp, totPow) //} diff --git a/x/distribution/movement.go b/x/distribution/movement.go index 03c4de72c..399a25a68 100644 --- a/x/distribution/movement.go +++ b/x/distribution/movement.go @@ -17,7 +17,7 @@ func BurnFeeHandler(ctx sdk.Context, _ sdk.Tx, collectedFees sdk.Coins) {} //// calculate the proposer reward //precommitPower := k.GetTotalPrecommitVotingPower(ctx) -//toProposer := coinsMulRat(collectedFees, (sdk.NewRat(1, 100).Add(sdk.NewRat(4, 100).Mul(precommitPower).Quo(pool.BondedShares)))) +//toProposer := coinsMulRat(collectedFees, (sdk.NewDec(1, 100).Add(sdk.NewDec(4, 100).Mul(precommitPower).Quo(pool.BondedShares)))) //candidate.ProposerRewardPool = candidate.ProposerRewardPool.Plus(toProposer) //toReservePool := coinsMulRat(collectedFees, params.ReservePoolFee) @@ -34,10 +34,10 @@ func BurnFeeHandler(ctx sdk.Context, _ sdk.Tx, collectedFees sdk.Coins) {} //k.setPool(ctx, pool) //} -//func coinsMulRat(coins sdk.Coins, rat sdk.Rat) sdk.Coins { +//func coinsMulRat(coins sdk.Coins, rat sdk.Dec) sdk.Coins { //var res sdk.Coins //for _, coin := range coins { -//coinMulAmt := rat.Mul(sdk.NewRat(coin.Amount)).Evaluate() +//coinMulAmt := rat.Mul(sdk.NewDec(coin.Amount)).Evaluate() //coinMul := sdk.Coins{{coin.Denom, coinMulAmt}} //res = res.Plus(coinMul) //} @@ -49,14 +49,14 @@ func BurnFeeHandler(ctx sdk.Context, _ sdk.Tx, collectedFees sdk.Coins) {} //// calculate adjustment changes for a candidate at a height //func CalculateAdjustmentChange(candidate Candidate, pool Pool, denoms []string, height int64) (Candidate, Pool) { -//heightRat := sdk.NewRat(height) -//lastHeightRat := sdk.NewRat(height - 1) +//heightRat := sdk.NewDec(height) +//lastHeightRat := sdk.NewDec(height - 1) //candidateFeeCount := candidate.BondedShares.Mul(heightRat) //poolFeeCount := pool.BondedShares.Mul(heightRat) //for i, denom := range denoms { -//poolFeeSumReceived := sdk.NewRat(pool.FeeSumReceived.AmountOf(denom)) -//poolFeeRecent := sdk.NewRat(pool.FeeRecent.AmountOf(denom)) +//poolFeeSumReceived := sdk.NewDec(pool.FeeSumReceived.AmountOf(denom)) +//poolFeeRecent := sdk.NewDec(pool.FeeRecent.AmountOf(denom)) //// calculate simple and projected pools //simplePool := candidateFeeCount.Quo(poolFeeCount).Mul(poolFeeSumReceived) //calc1 := candidate.PrevBondedShares.Mul(lastHeightRat).Quo(pool.PrevBondedShares.Mul(lastHeightRat)).Mul(poolFeeRecent) diff --git a/x/distribution/types.go b/x/distribution/types.go index f9d4f905f..223410471 100644 --- a/x/distribution/types.go +++ b/x/distribution/types.go @@ -23,8 +23,8 @@ package stake //// fee information for a validator //type Validator struct { -//Adjustments []sdk.Rat `json:"fee_adjustments"` // XXX Adjustment factors for lazy fee accounting, couples with Params.BondDenoms -//PrevBondedShares sdk.Rat `json:"prev_bonded_shares"` // total shares of a global hold pools +//Adjustments []sdk.Dec `json:"fee_adjustments"` // XXX Adjustment factors for lazy fee accounting, couples with Params.BondDenoms +//PrevBondedShares sdk.Dec `json:"prev_bonded_shares"` // total shares of a global hold pools //} ////_________________________________________________________________________ @@ -32,7 +32,7 @@ package stake //// Params defines the high level settings for staking //type Params struct { //FeeDenoms []string `json:"fee_denoms"` // accepted fee denoms -//ReservePoolFee sdk.Rat `json:"reserve_pool_fee"` // percent of fees which go to reserve pool +//ReservePoolFee sdk.Dec `json:"reserve_pool_fee"` // percent of fees which go to reserve pool //} //func (p Params) equal(p2 Params) bool { @@ -43,7 +43,7 @@ package stake //func defaultParams() Params { //return Params{ //FeeDenoms: []string{"steak"}, -//ReservePoolFee: sdk.NewRat(5, 100), +//ReservePoolFee: sdk.NewDec(5, 100), //} //} @@ -55,8 +55,8 @@ package stake //FeePool sdk.Coins `json:"fee_pool"` // XXX fee pool for all the fee shares which have already been distributed //FeeSumReceived sdk.Coins `json:"fee_sum_received"` // XXX sum of all fees received, post reserve pool `json:"fee_sum_received"` //FeeRecent sdk.Coins `json:"fee_recent"` // XXX most recent fee collected -//FeeAdjustments []sdk.Rat `json:"fee_adjustments"` // XXX Adjustment factors for lazy fee accounting, couples with Params.BondDenoms -//PrevBondedShares sdk.Rat `json:"prev_bonded_shares"` // XXX last recorded bonded shares +//FeeAdjustments []sdk.Dec `json:"fee_adjustments"` // XXX Adjustment factors for lazy fee accounting, couples with Params.BondDenoms +//PrevBondedShares sdk.Dec `json:"prev_bonded_shares"` // XXX last recorded bonded shares //} //func (p Pool) equal(p2 Pool) bool { @@ -64,7 +64,7 @@ package stake //p.FeePool.IsEqual(p2.FeePool) && //p.FeeSumReceived.IsEqual(p2.FeeSumReceived) && //p.FeeRecent.IsEqual(p2.FeeRecent) && -//sdk.RatsEqual(p.FeeAdjustments, p2.FeeAdjustments) && +//sdk.DecsEqual(p.FeeAdjustments, p2.FeeAdjustments) && //p.PrevBondedShares.Equal(p2.PrevBondedShares) //} @@ -75,8 +75,8 @@ package stake //FeePool: sdk.Coins(nil), //FeeSumReceived: sdk.Coins(nil), //FeeRecent: sdk.Coins(nil), -//FeeAdjustments: []sdk.Rat{sdk.ZeroRat()}, -//PrevBondedShares: sdk.ZeroRat(), +//FeeAdjustments: []sdk.Dec{sdk.ZeroDec()}, +//PrevBondedShares: sdk.ZeroDec(), //} //} @@ -85,8 +85,8 @@ package stake //// Used in calculation of fee shares, added to a queue for each block where a power change occures //type PowerChange struct { //Height int64 `json:"height"` // block height at change -//Power sdk.Rat `json:"power"` // total power at change -//PrevPower sdk.Rat `json:"prev_power"` // total power at previous height-1 +//Power sdk.Dec `json:"power"` // total power at change +//PrevPower sdk.Dec `json:"prev_power"` // total power at previous height-1 //FeesIn sdk.Coins `json:"fees_in"` // fees in at block height //PrevFeePool sdk.Coins `json:"prev_fee_pool"` // total fees in at previous block height //} diff --git a/x/gov/genesis.go b/x/gov/genesis.go index 78b39ff1c..15f952c00 100644 --- a/x/gov/genesis.go +++ b/x/gov/genesis.go @@ -33,9 +33,9 @@ func DefaultGenesisState() GenesisState { VotingPeriod: 200, }, TallyingProcedure: TallyingProcedure{ - Threshold: sdk.NewRat(1, 2), - Veto: sdk.NewRat(1, 3), - GovernancePenalty: sdk.NewRat(1, 100), + Threshold: sdk.NewDecWithPrec(5, 1), + Veto: sdk.NewDecWithPrec(334, 3), + GovernancePenalty: sdk.NewDecWithPrec(1, 2), }, } } diff --git a/x/gov/procedures.go b/x/gov/procedures.go index f46c2149f..f74091c74 100644 --- a/x/gov/procedures.go +++ b/x/gov/procedures.go @@ -12,9 +12,9 @@ type DepositProcedure struct { // Procedure around Tallying votes in governance type TallyingProcedure struct { - Threshold sdk.Rat `json:"threshold"` // Minimum propotion of Yes votes for proposal to pass. Initial value: 0.5 - Veto sdk.Rat `json:"veto"` // Minimum value of Veto votes to Total votes ratio for proposal to be vetoed. Initial value: 1/3 - GovernancePenalty sdk.Rat `json:"governance_penalty"` // Penalty if validator does not vote + Threshold sdk.Dec `json:"threshold"` // Minimum propotion of Yes votes for proposal to pass. Initial value: 0.5 + Veto sdk.Dec `json:"veto"` // Minimum value of Veto votes to Total votes ratio for proposal to be vetoed. Initial value: 1/3 + GovernancePenalty sdk.Dec `json:"governance_penalty"` // Penalty if validator does not vote } // Procedure around Voting in governance diff --git a/x/gov/proposals.go b/x/gov/proposals.go index 0a7f5de70..f05dabd08 100644 --- a/x/gov/proposals.go +++ b/x/gov/proposals.go @@ -297,19 +297,19 @@ func (status ProposalStatus) Format(s fmt.State, verb rune) { //----------------------------------------------------------- // Tally Results type TallyResult struct { - Yes sdk.Rat `json:"yes"` - Abstain sdk.Rat `json:"abstain"` - No sdk.Rat `json:"no"` - NoWithVeto sdk.Rat `json:"no_with_veto"` + Yes sdk.Dec `json:"yes"` + Abstain sdk.Dec `json:"abstain"` + No sdk.Dec `json:"no"` + NoWithVeto sdk.Dec `json:"no_with_veto"` } // checks if two proposals are equal func EmptyTallyResult() TallyResult { return TallyResult{ - Yes: sdk.ZeroRat(), - Abstain: sdk.ZeroRat(), - No: sdk.ZeroRat(), - NoWithVeto: sdk.ZeroRat(), + Yes: sdk.ZeroDec(), + Abstain: sdk.ZeroDec(), + No: sdk.ZeroDec(), + NoWithVeto: sdk.ZeroDec(), } } diff --git a/x/gov/tally.go b/x/gov/tally.go index f8a341e1e..fc5b4317b 100644 --- a/x/gov/tally.go +++ b/x/gov/tally.go @@ -7,20 +7,20 @@ import ( // validatorGovInfo used for tallying type validatorGovInfo struct { Address sdk.AccAddress // sdk.AccAddress of the validator owner - Power sdk.Rat // Power of a Validator - DelegatorShares sdk.Rat // Total outstanding delegator shares - Minus sdk.Rat // Minus of validator, used to compute validator's voting power + Power sdk.Dec // Power of a Validator + DelegatorShares sdk.Dec // Total outstanding delegator shares + Minus sdk.Dec // Minus of validator, used to compute validator's voting power Vote VoteOption // Vote of the validator } func tally(ctx sdk.Context, keeper Keeper, proposal Proposal) (passes bool, tallyResults TallyResult, nonVoting []sdk.AccAddress) { - results := make(map[VoteOption]sdk.Rat) - results[OptionYes] = sdk.ZeroRat() - results[OptionAbstain] = sdk.ZeroRat() - results[OptionNo] = sdk.ZeroRat() - results[OptionNoWithVeto] = sdk.ZeroRat() + results := make(map[VoteOption]sdk.Dec) + results[OptionYes] = sdk.ZeroDec() + results[OptionAbstain] = sdk.ZeroDec() + results[OptionNo] = sdk.ZeroDec() + results[OptionNoWithVeto] = sdk.ZeroDec() - totalVotingPower := sdk.ZeroRat() + totalVotingPower := sdk.ZeroDec() currValidators := make(map[string]validatorGovInfo) keeper.vs.IterateValidatorsBonded(ctx, func(index int64, validator sdk.Validator) (stop bool) { @@ -28,7 +28,7 @@ func tally(ctx sdk.Context, keeper Keeper, proposal Proposal) (passes bool, tall Address: validator.GetOwner(), Power: validator.GetPower(), DelegatorShares: validator.GetDelegatorShares(), - Minus: sdk.ZeroRat(), + Minus: sdk.ZeroDec(), Vote: OptionEmpty, } return false @@ -91,7 +91,7 @@ func tally(ctx sdk.Context, keeper Keeper, proposal Proposal) (passes bool, tall } // If no one votes, proposal fails - if totalVotingPower.Sub(results[OptionAbstain]).Equal(sdk.ZeroRat()) { + if totalVotingPower.Sub(results[OptionAbstain]).Equal(sdk.ZeroDec()) { return false, tallyResults, nonVoting } // If more than 1/3 of voters veto, proposal fails diff --git a/x/gov/test_common.go b/x/gov/test_common.go index df66fa40c..5e7977b50 100644 --- a/x/gov/test_common.go +++ b/x/gov/test_common.go @@ -64,7 +64,7 @@ func getInitChainer(mapp *mock.App, keeper Keeper, stakeKeeper stake.Keeper) sdk mapp.InitChainer(ctx, req) stakeGenesis := stake.DefaultGenesisState() - stakeGenesis.Pool.LooseTokens = sdk.NewRat(100000) + stakeGenesis.Pool.LooseTokens = sdk.NewDec(100000) validators, err := stake.InitGenesis(ctx, stakeKeeper, stakeGenesis) if err != nil { diff --git a/x/params/keeper.go b/x/params/keeper.go index 8817b7c6c..69bcc05ea 100644 --- a/x/params/keeper.go +++ b/x/params/keeper.go @@ -183,8 +183,8 @@ func (k Getter) GetUint(ctx sdk.Context, key string) (res sdk.Uint, err error) { return } -// GetRat is helper function for rat params -func (k Getter) GetRat(ctx sdk.Context, key string) (res sdk.Rat, err error) { +// GetDec is helper function for decimal params +func (k Getter) GetDec(ctx sdk.Context, key string) (res sdk.Dec, err error) { store := ctx.KVStore(k.k.key) bz := store.Get([]byte(key)) err = k.k.cdc.UnmarshalBinary(bz, &res) @@ -301,8 +301,8 @@ func (k Getter) GetUintWithDefault(ctx sdk.Context, key string, def sdk.Uint) (r return } -// GetRatWithDefault is helper function for sdk.Rat params with default value -func (k Getter) GetRatWithDefault(ctx sdk.Context, key string, def sdk.Rat) (res sdk.Rat) { +// GetDecWithDefault is helper function for sdk.Dec params with default value +func (k Getter) GetDecWithDefault(ctx sdk.Context, key string, def sdk.Dec) (res sdk.Dec) { store := ctx.KVStore(k.k.key) bz := store.Get([]byte(key)) if bz == nil { @@ -397,8 +397,8 @@ func (k Setter) SetUint(ctx sdk.Context, key string, param sdk.Uint) { } } -// SetRat is helper function for rat params -func (k Setter) SetRat(ctx sdk.Context, key string, param sdk.Rat) { +// SetDec is helper function for decimal params +func (k Setter) SetDec(ctx sdk.Context, key string, param sdk.Dec) { if err := k.k.set(ctx, key, param); err != nil { panic(err) } diff --git a/x/params/keeper_test.go b/x/params/keeper_test.go index 4bb5744ea..626d68c7d 100644 --- a/x/params/keeper_test.go +++ b/x/params/keeper_test.go @@ -94,7 +94,7 @@ func TestGetter(t *testing.T) { {"uint64", uint64(1)}, {"int", sdk.NewInt(1)}, {"uint", sdk.NewUint(1)}, - {"rat", sdk.NewRat(1)}, + {"rat", sdk.NewDec(1)}, } assert.NotPanics(t, func() { s.SetString(ctx, kvs[0].key, "test") }) @@ -107,7 +107,7 @@ func TestGetter(t *testing.T) { assert.NotPanics(t, func() { s.SetUint64(ctx, kvs[7].key, uint64(1)) }) assert.NotPanics(t, func() { s.SetInt(ctx, kvs[8].key, sdk.NewInt(1)) }) assert.NotPanics(t, func() { s.SetUint(ctx, kvs[9].key, sdk.NewUint(1)) }) - assert.NotPanics(t, func() { s.SetRat(ctx, kvs[10].key, sdk.NewRat(1)) }) + assert.NotPanics(t, func() { s.SetDec(ctx, kvs[10].key, sdk.NewDec(1)) }) var res interface{} var err error @@ -263,18 +263,18 @@ func TestGetter(t *testing.T) { assert.Equal(t, def9, res) // Rat - def10 := sdk.NewRat(0) - res, err = g.GetRat(ctx, kvs[10].key) + def10 := sdk.NewDec(0) + res, err = g.GetDec(ctx, kvs[10].key) assert.Nil(t, err) assert.Equal(t, kvs[10].param, res) - _, err = g.GetRat(ctx, "invalid") + _, err = g.GetDec(ctx, "invalid") assert.NotNil(t, err) - res = g.GetRatWithDefault(ctx, kvs[10].key, def10) + res = g.GetDecWithDefault(ctx, kvs[10].key, def10) assert.Equal(t, kvs[10].param, res) - res = g.GetRatWithDefault(ctx, "invalid", def10) + res = g.GetDecWithDefault(ctx, "invalid", def10) assert.Equal(t, def10, res) } diff --git a/x/slashing/app_test.go b/x/slashing/app_test.go index 523a2e220..1e6a4e89d 100644 --- a/x/slashing/app_test.go +++ b/x/slashing/app_test.go @@ -58,7 +58,7 @@ func getInitChainer(mapp *mock.App, keeper stake.Keeper) sdk.InitChainer { return func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain { mapp.InitChainer(ctx, req) stakeGenesis := stake.DefaultGenesisState() - stakeGenesis.Pool.LooseTokens = sdk.NewRat(100000) + stakeGenesis.Pool.LooseTokens = sdk.NewDec(100000) validators, err := stake.InitGenesis(ctx, keeper, stakeGenesis) if err != nil { panic(err) @@ -109,7 +109,7 @@ func TestSlashingMsgs(t *testing.T) { validator := checkValidator(t, mapp, stakeKeeper, addr1, true) require.Equal(t, addr1, validator.Owner) require.Equal(t, sdk.Bonded, validator.Status) - require.True(sdk.RatEq(t, sdk.NewRat(10), validator.BondedTokens())) + require.True(sdk.DecEq(t, sdk.NewDec(10), validator.BondedTokens())) unrevokeMsg := MsgUnrevoke{ValidatorAddr: sdk.AccAddress(validator.PubKey.Address())} // no signing info yet diff --git a/x/slashing/handler_test.go b/x/slashing/handler_test.go index 89c1e11a6..41cb5d199 100644 --- a/x/slashing/handler_test.go +++ b/x/slashing/handler_test.go @@ -20,7 +20,7 @@ func TestCannotUnrevokeUnlessRevoked(t *testing.T) { require.True(t, got.IsOK()) stake.EndBlocker(ctx, sk) require.Equal(t, ck.GetCoins(ctx, addr), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins.Sub(amt)}}) - require.True(t, sdk.NewRatFromInt(amt).Equal(sk.Validator(ctx, addr).GetPower())) + require.True(t, sdk.NewDecFromInt(amt).Equal(sk.Validator(ctx, addr).GetPower())) // assert non-revoked validator can't be unrevoked got = slh(ctx, NewMsgUnrevoke(addr)) diff --git a/x/slashing/keeper_test.go b/x/slashing/keeper_test.go index d3d6b06ed..68de7a43c 100644 --- a/x/slashing/keeper_test.go +++ b/x/slashing/keeper_test.go @@ -31,7 +31,7 @@ func TestHandleDoubleSign(t *testing.T) { validatorUpdates := stake.EndBlocker(ctx, sk) keeper.AddValidators(ctx, validatorUpdates) require.Equal(t, ck.GetCoins(ctx, addr), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins.Sub(amt)}}) - require.True(t, sdk.NewRatFromInt(amt).Equal(sk.Validator(ctx, addr).GetPower())) + require.True(t, sdk.NewDecFromInt(amt).Equal(sk.Validator(ctx, addr).GetPower())) // handle a signature to set signing info keeper.handleValidatorSignature(ctx, val.Address(), amtInt, true) @@ -44,12 +44,12 @@ func TestHandleDoubleSign(t *testing.T) { // unrevoke to measure power sk.Unrevoke(ctx, val) // power should be reduced - require.Equal(t, sdk.NewRatFromInt(amt).Mul(sdk.NewRat(19).Quo(sdk.NewRat(20))), sk.Validator(ctx, addr).GetPower()) + require.Equal(t, sdk.NewDecFromInt(amt).Mul(sdk.NewDec(19).Quo(sdk.NewDec(20))), sk.Validator(ctx, addr).GetPower()) ctx = ctx.WithBlockHeader(abci.Header{Time: time.Unix(1, 0).Add(keeper.MaxEvidenceAge(ctx))}) // double sign past max age keeper.handleDoubleSign(ctx, val, 0, time.Unix(0, 0), amtInt) - require.Equal(t, sdk.NewRatFromInt(amt).Mul(sdk.NewRat(19).Quo(sdk.NewRat(20))), sk.Validator(ctx, addr).GetPower()) + require.Equal(t, sdk.NewDecFromInt(amt).Mul(sdk.NewDec(19).Quo(sdk.NewDec(20))), sk.Validator(ctx, addr).GetPower()) } // Test a validator through uptime, downtime, revocation, @@ -67,7 +67,7 @@ func TestHandleAbsentValidator(t *testing.T) { validatorUpdates := stake.EndBlocker(ctx, sk) keeper.AddValidators(ctx, validatorUpdates) require.Equal(t, ck.GetCoins(ctx, addr), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins.Sub(amt)}}) - require.True(t, sdk.NewRatFromInt(amt).Equal(sk.Validator(ctx, addr).GetPower())) + require.True(t, sdk.NewDecFromInt(amt).Equal(sk.Validator(ctx, addr).GetPower())) info, found := keeper.getValidatorSigningInfo(ctx, sdk.ValAddress(val.Address())) require.False(t, found) require.Equal(t, int64(0), info.StartHeight) @@ -131,7 +131,7 @@ func TestHandleAbsentValidator(t *testing.T) { // validator should have been slashed pool = sk.GetPool(ctx) - slashAmt := sdk.NewRat(amtInt).Mul(keeper.SlashFractionDowntime(ctx)).RoundInt64() + slashAmt := sdk.NewDec(amtInt).Mul(keeper.SlashFractionDowntime(ctx)).RoundInt64() require.Equal(t, int64(amtInt)-slashAmt, pool.BondedTokens.RoundInt64()) // validator start height should have been changed @@ -177,7 +177,7 @@ func TestHandleNewValidator(t *testing.T) { validatorUpdates := stake.EndBlocker(ctx, sk) keeper.AddValidators(ctx, validatorUpdates) require.Equal(t, ck.GetCoins(ctx, addr), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins.SubRaw(amt)}}) - require.Equal(t, sdk.NewRat(amt), sk.Validator(ctx, addr).GetPower()) + require.Equal(t, sdk.NewDec(amt), sk.Validator(ctx, addr).GetPower()) // 1000 first blocks not a validator ctx = ctx.WithBlockHeight(keeper.SignedBlocksWindow(ctx) + 1) diff --git a/x/slashing/params.go b/x/slashing/params.go index 9d1bc3960..a25d121c9 100644 --- a/x/slashing/params.go +++ b/x/slashing/params.go @@ -30,9 +30,9 @@ func (k Keeper) SignedBlocksWindow(ctx sdk.Context) int64 { // Downtime slashing thershold - default 50% func (k Keeper) MinSignedPerWindow(ctx sdk.Context) int64 { - minSignedPerWindow := k.params.GetRatWithDefault(ctx, MinSignedPerWindowKey, defaultMinSignedPerWindow) + minSignedPerWindow := k.params.GetDecWithDefault(ctx, MinSignedPerWindowKey, defaultMinSignedPerWindow) signedBlocksWindow := k.SignedBlocksWindow(ctx) - return sdk.NewRat(signedBlocksWindow).Mul(minSignedPerWindow).RoundInt64() + return sdk.NewDec(signedBlocksWindow).Mul(minSignedPerWindow).RoundInt64() } // Double-sign unbond duration @@ -46,13 +46,13 @@ func (k Keeper) DowntimeUnbondDuration(ctx sdk.Context) time.Duration { } // SlashFractionDoubleSign - currently default 5% -func (k Keeper) SlashFractionDoubleSign(ctx sdk.Context) sdk.Rat { - return k.params.GetRatWithDefault(ctx, SlashFractionDoubleSignKey, defaultSlashFractionDoubleSign) +func (k Keeper) SlashFractionDoubleSign(ctx sdk.Context) sdk.Dec { + return k.params.GetDecWithDefault(ctx, SlashFractionDoubleSignKey, defaultSlashFractionDoubleSign) } // SlashFractionDowntime - currently default 1% -func (k Keeper) SlashFractionDowntime(ctx sdk.Context) sdk.Rat { - return k.params.GetRatWithDefault(ctx, SlashFractionDowntimeKey, defaultSlashFractionDowntime) +func (k Keeper) SlashFractionDowntime(ctx sdk.Context) sdk.Dec { + return k.params.GetDecWithDefault(ctx, SlashFractionDowntimeKey, defaultSlashFractionDowntime) } // declared as var because of keeper_test.go @@ -72,9 +72,9 @@ var ( // TODO Temporarily set to 10 minutes for testnets defaultDowntimeUnbondDuration int64 = 60 * 10 - defaultMinSignedPerWindow = sdk.NewRat(1, 2) + defaultMinSignedPerWindow = sdk.NewDecWithPrec(5, 1) - defaultSlashFractionDoubleSign = sdk.NewRat(1).Quo(sdk.NewRat(20)) + defaultSlashFractionDoubleSign = sdk.NewDec(1).Quo(sdk.NewDec(20)) - defaultSlashFractionDowntime = sdk.NewRat(1).Quo(sdk.NewRat(100)) + defaultSlashFractionDowntime = sdk.NewDec(1).Quo(sdk.NewDec(100)) ) diff --git a/x/slashing/test_common.go b/x/slashing/test_common.go index 7b3ab0436..50c501d7e 100644 --- a/x/slashing/test_common.go +++ b/x/slashing/test_common.go @@ -69,7 +69,7 @@ func createTestInput(t *testing.T) (sdk.Context, bank.Keeper, stake.Keeper, para sk := stake.NewKeeper(cdc, keyStake, ck, stake.DefaultCodespace) genesis := stake.DefaultGenesisState() - genesis.Pool.LooseTokens = sdk.NewRat(initCoins.MulRaw(int64(len(addrs))).Int64()) + genesis.Pool.LooseTokens = sdk.NewDec(initCoins.MulRaw(int64(len(addrs))).Int64()) _, err = stake.InitGenesis(ctx, sk, genesis) require.Nil(t, err) diff --git a/x/slashing/tick_test.go b/x/slashing/tick_test.go index 8e0d66ed6..b230f9c94 100644 --- a/x/slashing/tick_test.go +++ b/x/slashing/tick_test.go @@ -22,7 +22,7 @@ func TestBeginBlocker(t *testing.T) { validatorUpdates := stake.EndBlocker(ctx, sk) keeper.AddValidators(ctx, validatorUpdates) require.Equal(t, ck.GetCoins(ctx, addr), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins.Sub(amt)}}) - require.True(t, sdk.NewRatFromInt(amt).Equal(sk.Validator(ctx, addr).GetPower())) + require.True(t, sdk.NewDecFromInt(amt).Equal(sk.Validator(ctx, addr).GetPower())) val := abci.Validator{ Address: pk.Address(), diff --git a/x/stake/app_test.go b/x/stake/app_test.go index 4e1b2c2ac..9847ed290 100644 --- a/x/stake/app_test.go +++ b/x/stake/app_test.go @@ -63,7 +63,7 @@ func getInitChainer(mapp *mock.App, keeper Keeper) sdk.InitChainer { mapp.InitChainer(ctx, req) stakeGenesis := DefaultGenesisState() - stakeGenesis.Pool.LooseTokens = sdk.NewRat(100000) + stakeGenesis.Pool.LooseTokens = sdk.NewDec(100000) validators, err := InitGenesis(ctx, keeper, stakeGenesis) if err != nil { @@ -90,14 +90,14 @@ func checkValidator(t *testing.T, mapp *mock.App, keeper Keeper, func checkDelegation( t *testing.T, mapp *mock.App, keeper Keeper, delegatorAddr, - validatorAddr sdk.AccAddress, expFound bool, expShares sdk.Rat, + validatorAddr sdk.AccAddress, expFound bool, expShares sdk.Dec, ) { ctxCheck := mapp.BaseApp.NewContext(true, abci.Header{}) delegation, found := keeper.GetDelegation(ctxCheck, delegatorAddr, validatorAddr) if expFound { require.True(t, found) - require.True(sdk.RatEq(t, expShares, delegation.Shares)) + require.True(sdk.DecEq(t, expShares, delegation.Shares)) return } @@ -138,7 +138,7 @@ func TestStakeMsgs(t *testing.T) { validator := checkValidator(t, mApp, keeper, addr1, true) require.Equal(t, addr1, validator.Owner) require.Equal(t, sdk.Bonded, validator.Status) - require.True(sdk.RatEq(t, sdk.NewRat(10), validator.BondedTokens())) + require.True(sdk.DecEq(t, sdk.NewDec(10), validator.BondedTokens())) // addr1 create validator on behalf of addr2 createValidatorMsgOnBehalfOf := NewMsgCreateValidatorOnBehalfOf(addr1, addr2, priv2.PubKey(), bondCoin, description) @@ -150,10 +150,10 @@ func TestStakeMsgs(t *testing.T) { validator = checkValidator(t, mApp, keeper, addr2, true) require.Equal(t, addr2, validator.Owner) require.Equal(t, sdk.Bonded, validator.Status) - require.True(sdk.RatEq(t, sdk.NewRat(10), validator.Tokens)) + require.True(sdk.DecEq(t, sdk.NewDec(10), validator.Tokens)) // check the bond that should have been created as well - checkDelegation(t, mApp, keeper, addr1, addr1, true, sdk.NewRat(10)) + checkDelegation(t, mApp, keeper, addr1, addr1, true, sdk.NewDec(10)) // edit the validator description = NewDescription("bar_moniker", "", "", "") @@ -169,14 +169,14 @@ func TestStakeMsgs(t *testing.T) { mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{delegateMsg}, []int64{1}, []int64{1}, true, priv2) mock.CheckBalance(t, mApp, addr2, sdk.Coins{genCoin.Minus(bondCoin)}) - checkDelegation(t, mApp, keeper, addr2, addr1, true, sdk.NewRat(10)) + checkDelegation(t, mApp, keeper, addr2, addr1, true, sdk.NewDec(10)) // begin unbonding - beginUnbondingMsg := NewMsgBeginUnbonding(addr2, addr1, sdk.NewRat(10)) + beginUnbondingMsg := NewMsgBeginUnbonding(addr2, addr1, sdk.NewDec(10)) mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{beginUnbondingMsg}, []int64{1}, []int64{2}, true, priv2) // delegation should exist anymore - checkDelegation(t, mApp, keeper, addr2, addr1, false, sdk.Rat{}) + checkDelegation(t, mApp, keeper, addr2, addr1, false, sdk.Dec{}) // balance should be the same because bonding not yet complete mock.CheckBalance(t, mApp, addr2, sdk.Coins{genCoin.Minus(bondCoin)}) diff --git a/x/stake/client/cli/tx.go b/x/stake/client/cli/tx.go index 4c86777f3..bf2df4913 100644 --- a/x/stake/client/cli/tx.go +++ b/x/stake/client/cli/tx.go @@ -239,27 +239,27 @@ func GetCmdBeginRedelegate(storeName string, cdc *wire.Codec) *cobra.Command { func getShares( storeName string, cdc *wire.Codec, sharesAmountStr, sharesPercentStr string, delegatorAddr, validatorAddr sdk.AccAddress, -) (sharesAmount sdk.Rat, err error) { +) (sharesAmount sdk.Dec, err error) { switch { case sharesAmountStr != "" && sharesPercentStr != "": return sharesAmount, errors.Errorf("can either specify the amount OR the percent of the shares, not both") case sharesAmountStr == "" && sharesPercentStr == "": return sharesAmount, errors.Errorf("can either specify the amount OR the percent of the shares, not both") case sharesAmountStr != "": - sharesAmount, err = sdk.NewRatFromDecimal(sharesAmountStr, types.MaxBondDenominatorPrecision) + sharesAmount, err = sdk.NewDecFromStr(sharesAmountStr) if err != nil { return sharesAmount, err } - if !sharesAmount.GT(sdk.ZeroRat()) { + if !sharesAmount.GT(sdk.ZeroDec()) { return sharesAmount, errors.Errorf("shares amount must be positive number (ex. 123, 1.23456789)") } case sharesPercentStr != "": - var sharesPercent sdk.Rat - sharesPercent, err = sdk.NewRatFromDecimal(sharesPercentStr, types.MaxBondDenominatorPrecision) + var sharesPercent sdk.Dec + sharesPercent, err = sdk.NewDecFromStr(sharesPercentStr) if err != nil { return sharesAmount, err } - if !sharesPercent.GT(sdk.ZeroRat()) || !sharesPercent.LTE(sdk.OneRat()) { + if !sharesPercent.GT(sdk.ZeroDec()) || !sharesPercent.LTE(sdk.OneDec()) { return sharesAmount, errors.Errorf("shares percent must be >0 and <=1 (ex. 0.01, 0.75, 1)") } diff --git a/x/stake/client/rest/query.go b/x/stake/client/rest/query.go index 1741f8823..b7d2b10a4 100644 --- a/x/stake/client/rest/query.go +++ b/x/stake/client/rest/query.go @@ -347,7 +347,7 @@ func delegationHandlerFn(cliCtx context.CLIContext, cdc *wire.Codec) http.Handle DelegatorAddr: delegation.DelegatorAddr, ValidatorAddr: delegation.ValidatorAddr, Height: delegation.Height, - Shares: delegation.Shares.FloatString(), + Shares: delegation.Shares.String(), } output, err := cdc.MarshalJSON(outputDelegation) diff --git a/x/stake/client/rest/tx.go b/x/stake/client/rest/tx.go index fbefc7f21..d8b9b6011 100644 --- a/x/stake/client/rest/tx.go +++ b/x/stake/client/rest/tx.go @@ -12,7 +12,6 @@ import ( "github.com/cosmos/cosmos-sdk/wire" authcliCtx "github.com/cosmos/cosmos-sdk/x/auth/client/context" "github.com/cosmos/cosmos-sdk/x/stake" - "github.com/cosmos/cosmos-sdk/x/stake/types" "github.com/gorilla/mux" @@ -160,7 +159,7 @@ func delegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx contex return } - shares, err := sdk.NewRatFromDecimal(msg.SharesAmount, types.MaxBondDenominatorPrecision) + shares, err := sdk.NewDecFromStr(msg.SharesAmount) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(fmt.Sprintf("Couldn't decode shares amount. Error: %s", err.Error()))) @@ -234,7 +233,7 @@ func delegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx contex return } - shares, err := sdk.NewRatFromDecimal(msg.SharesAmount, types.MaxBondDenominatorPrecision) + shares, err := sdk.NewDecFromStr(msg.SharesAmount) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(fmt.Sprintf("Couldn't decode shares amount. Error: %s", err.Error()))) diff --git a/x/stake/client/rest/utils.go b/x/stake/client/rest/utils.go index da00eda0b..1a0809089 100644 --- a/x/stake/client/rest/utils.go +++ b/x/stake/client/rest/utils.go @@ -76,7 +76,7 @@ func getDelegatorDelegations(cliCtx context.CLIContext, cdc *wire.Codec, delegat DelegatorAddr: delegation.DelegatorAddr, ValidatorAddr: delegation.ValidatorAddr, Height: delegation.Height, - Shares: delegation.Shares.FloatString(), + Shares: delegation.Shares.String(), } return outputDelegation, http.StatusOK, "", nil diff --git a/x/stake/genesis_test.go b/x/stake/genesis_test.go index 2febd2c6a..9cdbe1982 100644 --- a/x/stake/genesis_test.go +++ b/x/stake/genesis_test.go @@ -17,7 +17,7 @@ func TestInitGenesis(t *testing.T) { ctx, _, keeper := keep.CreateTestInput(t, false, 1000) pool := keeper.GetPool(ctx) - pool.BondedTokens = sdk.NewRat(2) + pool.BondedTokens = sdk.NewDec(2) params := keeper.GetParams(ctx) var delegations []Delegation @@ -32,11 +32,11 @@ func TestInitGenesis(t *testing.T) { // initialize the validators validators[0].Status = sdk.Bonded - validators[0].Tokens = sdk.OneRat() - validators[0].DelegatorShares = sdk.OneRat() + validators[0].Tokens = sdk.OneDec() + validators[0].DelegatorShares = sdk.OneDec() validators[1].Status = sdk.Bonded - validators[1].Tokens = sdk.OneRat() - validators[1].DelegatorShares = sdk.OneRat() + validators[1].Tokens = sdk.OneDec() + validators[1].DelegatorShares = sdk.OneDec() genesisState = types.NewGenesisState(pool, params, validators, delegations) vals, err := InitGenesis(ctx, keeper, genesisState) @@ -69,7 +69,7 @@ func TestInitGenesisLargeValidatorSet(t *testing.T) { // Assigning 2 to the first 100 vals, 1 to the rest pool := keeper.GetPool(ctx) - pool.BondedTokens = sdk.NewRat(int64(200 + (size - 100))) + pool.BondedTokens = sdk.NewDec(int64(200 + (size - 100))) params := keeper.GetParams(ctx) delegations := []Delegation{} @@ -80,11 +80,11 @@ func TestInitGenesisLargeValidatorSet(t *testing.T) { validators[i].Status = sdk.Bonded if i < 100 { - validators[i].Tokens = sdk.NewRat(2) - validators[i].DelegatorShares = sdk.NewRat(2) + validators[i].Tokens = sdk.NewDec(2) + validators[i].DelegatorShares = sdk.NewDec(2) } else { - validators[i].Tokens = sdk.OneRat() - validators[i].DelegatorShares = sdk.OneRat() + validators[i].Tokens = sdk.OneDec() + validators[i].DelegatorShares = sdk.OneDec() } } diff --git a/x/stake/handler_test.go b/x/stake/handler_test.go index edad64f44..a06be0f88 100644 --- a/x/stake/handler_test.go +++ b/x/stake/handler_test.go @@ -81,7 +81,7 @@ func TestValidatorByPowerIndex(t *testing.T) { require.True(t, got.IsOK(), "expected create-validator to be ok, got %v", got) // slash and revoke the first validator - keeper.Slash(ctx, keep.PKs[0], 0, initBond, sdk.NewRat(1, 2)) + keeper.Slash(ctx, keep.PKs[0], 0, initBond, sdk.NewDecWithPrec(5, 1)) keeper.Revoke(ctx, keep.PKs[0]) validator, found = keeper.GetValidator(ctx, validatorAddr) require.True(t, found) @@ -110,7 +110,7 @@ func TestValidatorByPowerIndex(t *testing.T) { require.Equal(t, power2, power3) // unbond self-delegation - msgBeginUnbonding := NewMsgBeginUnbonding(validatorAddr, validatorAddr, sdk.NewRat(1000000)) + msgBeginUnbonding := NewMsgBeginUnbonding(validatorAddr, validatorAddr, sdk.NewDec(1000000)) msgCompleteUnbonding := NewMsgCompleteUnbonding(validatorAddr, validatorAddr) got = handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper) require.True(t, got.IsOK(), "expected msg to be ok, got %v", got) @@ -138,8 +138,8 @@ func TestDuplicatesMsgCreateValidator(t *testing.T) { assert.Equal(t, sdk.Bonded, validator.Status) assert.Equal(t, addr1, validator.Owner) assert.Equal(t, pk1, validator.PubKey) - assert.Equal(t, sdk.NewRat(10), validator.BondedTokens()) - assert.Equal(t, sdk.NewRat(10), validator.DelegatorShares) + assert.Equal(t, sdk.NewDec(10), validator.BondedTokens()) + assert.Equal(t, sdk.NewDec(10), validator.DelegatorShares) assert.Equal(t, Description{}, validator.Description) // two validators can't have the same owner address @@ -162,8 +162,8 @@ func TestDuplicatesMsgCreateValidator(t *testing.T) { assert.Equal(t, sdk.Bonded, validator.Status) assert.Equal(t, addr2, validator.Owner) assert.Equal(t, pk2, validator.PubKey) - assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.Tokens)) - assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.DelegatorShares)) + assert.True(sdk.DecEq(t, sdk.NewDec(10), validator.Tokens)) + assert.True(sdk.DecEq(t, sdk.NewDec(10), validator.DelegatorShares)) assert.Equal(t, Description{}, validator.Description) } @@ -182,8 +182,8 @@ func TestDuplicatesMsgCreateValidatorOnBehalfOf(t *testing.T) { assert.Equal(t, sdk.Bonded, validator.Status) assert.Equal(t, validatorAddr, validator.Owner) assert.Equal(t, pk, validator.PubKey) - assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.Tokens)) - assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.DelegatorShares)) + assert.True(sdk.DecEq(t, sdk.NewDec(10), validator.Tokens)) + assert.True(sdk.DecEq(t, sdk.NewDec(10), validator.DelegatorShares)) assert.Equal(t, Description{}, validator.Description) // one validator cannot be created twice even from different delegator @@ -221,7 +221,7 @@ func TestIncrementsMsgDelegate(t *testing.T) { pool := keeper.GetPool(ctx) exRate := validator.DelegatorShareExRate() - require.True(t, exRate.Equal(sdk.OneRat()), "expected exRate 1 got %v", exRate) + require.True(t, exRate.Equal(sdk.OneDec()), "expected exRate 1 got %v", exRate) require.Equal(t, bondAmount, pool.BondedTokens.RoundInt64()) // just send the same msgbond multiple times @@ -240,7 +240,7 @@ func TestIncrementsMsgDelegate(t *testing.T) { require.True(t, found) exRate := validator.DelegatorShareExRate() - require.True(t, exRate.Equal(sdk.OneRat()), "expected exRate 1 got %v, i = %v", exRate, i) + require.True(t, exRate.Equal(sdk.OneDec()), "expected exRate 1 got %v, i = %v", exRate, i) expBond := int64(i+1) * bondAmount expDelegatorShares := int64(i+2) * bondAmount // (1 self delegation) @@ -295,7 +295,7 @@ func TestIncrementsMsgUnbond(t *testing.T) { // just send the same msgUnbond multiple times // TODO use decimals here - unbondShares := sdk.NewRat(10) + unbondShares := sdk.NewDec(10) msgBeginUnbonding := NewMsgBeginUnbonding(delegatorAddr, validatorAddr, unbondShares) msgCompleteUnbonding := NewMsgCompleteUnbonding(delegatorAddr, validatorAddr) numUnbonds := 5 @@ -339,7 +339,7 @@ func TestIncrementsMsgUnbond(t *testing.T) { initBond, } for _, c := range errorCases { - unbondShares := sdk.NewRat(int64(c)) + unbondShares := sdk.NewDec(int64(c)) msgBeginUnbonding := NewMsgBeginUnbonding(delegatorAddr, validatorAddr, unbondShares) got = handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper) require.False(t, got.IsOK(), "expected unbond msg to fail") @@ -348,14 +348,14 @@ func TestIncrementsMsgUnbond(t *testing.T) { leftBonded := initBond - int64(numUnbonds)*unbondShares.RoundInt64() // should be unable to unbond one more than we have - unbondShares = sdk.NewRat(leftBonded + 1) + unbondShares = sdk.NewDec(leftBonded + 1) msgBeginUnbonding = NewMsgBeginUnbonding(delegatorAddr, validatorAddr, unbondShares) got = handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper) require.False(t, got.IsOK(), "got: %v\nmsgUnbond: %v\nshares: %v\nleftBonded: %v\n", got, msgBeginUnbonding, unbondShares.String(), leftBonded) // should be able to unbond just what we have - unbondShares = sdk.NewRat(leftBonded) + unbondShares = sdk.NewDec(leftBonded) msgBeginUnbonding = NewMsgBeginUnbonding(delegatorAddr, validatorAddr, unbondShares) got = handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper) require.True(t, got.IsOK(), @@ -391,7 +391,7 @@ func TestMultipleMsgCreateValidator(t *testing.T) { for i, validatorAddr := range validatorAddrs { _, found := keeper.GetValidator(ctx, validatorAddr) require.True(t, found) - msgBeginUnbonding := NewMsgBeginUnbonding(delegatorAddrs[i], validatorAddr, sdk.NewRat(10)) // remove delegation + msgBeginUnbonding := NewMsgBeginUnbonding(delegatorAddrs[i], validatorAddr, sdk.NewDec(10)) // remove delegation msgCompleteUnbonding := NewMsgCompleteUnbonding(delegatorAddrs[i], validatorAddr) got := handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper) require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got) @@ -436,7 +436,7 @@ func TestMultipleMsgDelegate(t *testing.T) { // unbond them all for i, delegatorAddr := range delegatorAddrs { - msgBeginUnbonding := NewMsgBeginUnbonding(delegatorAddr, validatorAddr, sdk.NewRat(10)) + msgBeginUnbonding := NewMsgBeginUnbonding(delegatorAddr, validatorAddr, sdk.NewDec(10)) msgCompleteUnbonding := NewMsgCompleteUnbonding(delegatorAddr, validatorAddr) got := handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper) require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got) @@ -467,7 +467,7 @@ func TestRevokeValidator(t *testing.T) { validator, _ := keeper.GetValidator(ctx, validatorAddr) // unbond the validators bond portion - msgBeginUnbondingValidator := NewMsgBeginUnbonding(validatorAddr, validatorAddr, sdk.NewRat(10)) + msgBeginUnbondingValidator := NewMsgBeginUnbonding(validatorAddr, validatorAddr, sdk.NewDec(10)) msgCompleteUnbondingValidator := NewMsgCompleteUnbonding(validatorAddr, validatorAddr) got = handleMsgBeginUnbonding(ctx, msgBeginUnbondingValidator, keeper) require.True(t, got.IsOK(), "expected no error") @@ -483,7 +483,7 @@ func TestRevokeValidator(t *testing.T) { require.False(t, got.IsOK(), "expected error, got %v", got) // test that the delegator can still withdraw their bonds - msgBeginUnbondingDelegator := NewMsgBeginUnbonding(delegatorAddr, validatorAddr, sdk.NewRat(10)) + msgBeginUnbondingDelegator := NewMsgBeginUnbonding(delegatorAddr, validatorAddr, sdk.NewDec(10)) msgCompleteUnbondingDelegator := NewMsgCompleteUnbonding(delegatorAddr, validatorAddr) got = handleMsgBeginUnbonding(ctx, msgBeginUnbondingDelegator, keeper) require.True(t, got.IsOK(), "expected no error") @@ -510,7 +510,7 @@ func TestUnbondingPeriod(t *testing.T) { require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") // begin unbonding - msgBeginUnbonding := NewMsgBeginUnbonding(validatorAddr, validatorAddr, sdk.NewRat(10)) + msgBeginUnbonding := NewMsgBeginUnbonding(validatorAddr, validatorAddr, sdk.NewDec(10)) got = handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper) require.True(t, got.IsOK(), "expected no error") @@ -565,7 +565,7 @@ func TestRedelegationPeriod(t *testing.T) { bal1 := AccMapper.GetAccount(ctx, validatorAddr).GetCoins() // begin redelegate - msgBeginRedelegate := NewMsgBeginRedelegate(validatorAddr, validatorAddr, validatorAddr2, sdk.NewRat(10)) + msgBeginRedelegate := NewMsgBeginRedelegate(validatorAddr, validatorAddr, validatorAddr2, sdk.NewDec(10)) got = handleMsgBeginRedelegate(ctx, msgBeginRedelegate, keeper) require.True(t, got.IsOK(), "expected no error, %v", got) @@ -617,12 +617,12 @@ func TestTransitiveRedelegation(t *testing.T) { require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") // begin redelegate - msgBeginRedelegate := NewMsgBeginRedelegate(validatorAddr, validatorAddr, validatorAddr2, sdk.NewRat(10)) + msgBeginRedelegate := NewMsgBeginRedelegate(validatorAddr, validatorAddr, validatorAddr2, sdk.NewDec(10)) got = handleMsgBeginRedelegate(ctx, msgBeginRedelegate, keeper) require.True(t, got.IsOK(), "expected no error, %v", got) // cannot redelegation to next validator while first delegation exists - msgBeginRedelegate = NewMsgBeginRedelegate(validatorAddr, validatorAddr2, validatorAddr3, sdk.NewRat(10)) + msgBeginRedelegate = NewMsgBeginRedelegate(validatorAddr, validatorAddr2, validatorAddr3, sdk.NewDec(10)) got = handleMsgBeginRedelegate(ctx, msgBeginRedelegate, keeper) require.True(t, !got.IsOK(), "expected an error, msg: %v", msgBeginRedelegate) @@ -663,7 +663,7 @@ func TestUnbondingWhenExcessValidators(t *testing.T) { require.Equal(t, 2, len(keeper.GetValidatorsBonded(ctx))) // unbond the valdator-2 - msgBeginUnbonding := NewMsgBeginUnbonding(validatorAddr2, validatorAddr2, sdk.NewRat(30)) + msgBeginUnbonding := NewMsgBeginUnbonding(validatorAddr2, validatorAddr2, sdk.NewDec(30)) got = handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper) require.True(t, got.IsOK(), "expected no error on runMsgBeginUnbonding") @@ -785,7 +785,7 @@ func TestCliffValidator(t *testing.T) { require.Equal(t, validatorAddr2.Bytes(), cliffVal) // unbond valdator-2 - msgBeginUnbonding := NewMsgBeginUnbonding(validatorAddr2, validatorAddr2, sdk.NewRat(30)) + msgBeginUnbonding := NewMsgBeginUnbonding(validatorAddr2, validatorAddr2, sdk.NewDec(30)) got = handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper) require.True(t, got.IsOK(), "expected no error on runMsgBeginUnbonding") @@ -798,7 +798,7 @@ func TestCliffValidator(t *testing.T) { require.Equal(t, validatorAddr3.Bytes(), cliffVal) // unbond valdator-1 - msgBeginUnbonding = NewMsgBeginUnbonding(validatorAddr1, validatorAddr1, sdk.NewRat(50)) + msgBeginUnbonding = NewMsgBeginUnbonding(validatorAddr1, validatorAddr1, sdk.NewDec(50)) got = handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper) require.True(t, got.IsOK(), "expected no error on runMsgBeginUnbonding") @@ -832,22 +832,22 @@ func TestBondUnbondRedelegateSlashTwice(t *testing.T) { ctx = ctx.WithBlockHeight(1) // begin unbonding 4 stake - msgBeginUnbonding := NewMsgBeginUnbonding(del, valA, sdk.NewRat(4)) + msgBeginUnbonding := NewMsgBeginUnbonding(del, valA, sdk.NewDec(4)) got = handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper) require.True(t, got.IsOK(), "expected no error on runMsgBeginUnbonding") // begin redelegate 6 stake - msgBeginRedelegate := NewMsgBeginRedelegate(del, valA, valB, sdk.NewRat(6)) + msgBeginRedelegate := NewMsgBeginRedelegate(del, valA, valB, sdk.NewDec(6)) got = handleMsgBeginRedelegate(ctx, msgBeginRedelegate, keeper) require.True(t, got.IsOK(), "expected no error on runMsgBeginRedelegate") // destination delegation should have 6 shares delegation, found := keeper.GetDelegation(ctx, del, valB) require.True(t, found) - require.Equal(t, sdk.NewRat(6), delegation.Shares) + require.Equal(t, sdk.NewDec(6), delegation.Shares) // slash the validator by half - keeper.Slash(ctx, keep.PKs[0], 0, 20, sdk.NewRat(1, 2)) + keeper.Slash(ctx, keep.PKs[0], 0, 20, sdk.NewDecWithPrec(5, 1)) // unbonding delegation should have been slashed by half unbonding, found := keeper.GetUnbondingDelegation(ctx, del, valA) @@ -862,16 +862,16 @@ func TestBondUnbondRedelegateSlashTwice(t *testing.T) { // destination delegation should have been slashed by half delegation, found = keeper.GetDelegation(ctx, del, valB) require.True(t, found) - require.Equal(t, sdk.NewRat(3), delegation.Shares) + require.Equal(t, sdk.NewDec(3), delegation.Shares) // validator power should have been reduced by half validator, found := keeper.GetValidator(ctx, valA) require.True(t, found) - require.Equal(t, sdk.NewRat(5), validator.GetPower()) + require.Equal(t, sdk.NewDec(5), validator.GetPower()) // slash the validator for an infraction committed after the unbonding and redelegation begin ctx = ctx.WithBlockHeight(3) - keeper.Slash(ctx, keep.PKs[0], 2, 10, sdk.NewRat(1, 2)) + keeper.Slash(ctx, keep.PKs[0], 2, 10, sdk.NewDecWithPrec(5, 1)) // unbonding delegation should be unchanged unbonding, found = keeper.GetUnbondingDelegation(ctx, del, valA) @@ -886,7 +886,7 @@ func TestBondUnbondRedelegateSlashTwice(t *testing.T) { // destination delegation should be unchanged delegation, found = keeper.GetDelegation(ctx, del, valB) require.True(t, found) - require.Equal(t, sdk.NewRat(3), delegation.Shares) + require.Equal(t, sdk.NewDec(3), delegation.Shares) // validator power should have been reduced to zero // ergo validator should have been removed from the store diff --git a/x/stake/keeper/delegation.go b/x/stake/keeper/delegation.go index 484e85ad5..4b4d267aa 100644 --- a/x/stake/keeper/delegation.go +++ b/x/stake/keeper/delegation.go @@ -218,7 +218,7 @@ func (k Keeper) RemoveRedelegation(ctx sdk.Context, red types.Redelegation) { // Perform a delegation, set/update everything necessary within the store. func (k Keeper) Delegate(ctx sdk.Context, delegatorAddr sdk.AccAddress, bondAmt sdk.Coin, - validator types.Validator, subtractAccount bool) (newShares sdk.Rat, err sdk.Error) { + validator types.Validator, subtractAccount bool) (newShares sdk.Dec, err sdk.Error) { // Get or create the delegator delegation delegation, found := k.GetDelegation(ctx, delegatorAddr, validator.Owner) @@ -226,7 +226,7 @@ func (k Keeper) Delegate(ctx sdk.Context, delegatorAddr sdk.AccAddress, bondAmt delegation = types.Delegation{ DelegatorAddr: delegatorAddr, ValidatorAddr: validator.Owner, - Shares: sdk.ZeroRat(), + Shares: sdk.ZeroDec(), } } @@ -254,7 +254,7 @@ func (k Keeper) Delegate(ctx sdk.Context, delegatorAddr sdk.AccAddress, bondAmt // unbond the the delegation return func (k Keeper) unbond(ctx sdk.Context, delegatorAddr, validatorAddr sdk.AccAddress, - shares sdk.Rat) (amount sdk.Rat, err sdk.Error) { + shares sdk.Dec) (amount sdk.Dec, err sdk.Error) { // check if delegation has any shares in it unbond delegation, found := k.GetDelegation(ctx, delegatorAddr, validatorAddr) @@ -312,7 +312,7 @@ func (k Keeper) unbond(ctx sdk.Context, delegatorAddr, validatorAddr sdk.AccAddr //______________________________________________________________________________________________________ // complete unbonding an unbonding record -func (k Keeper) BeginUnbonding(ctx sdk.Context, delegatorAddr, validatorAddr sdk.AccAddress, sharesAmount sdk.Rat) sdk.Error { +func (k Keeper) BeginUnbonding(ctx sdk.Context, delegatorAddr, validatorAddr sdk.AccAddress, sharesAmount sdk.Dec) sdk.Error { // TODO quick fix, instead we should use an index, see https://github.com/cosmos/cosmos-sdk/issues/1402 _, found := k.GetUnbondingDelegation(ctx, delegatorAddr, validatorAddr) @@ -365,7 +365,7 @@ func (k Keeper) CompleteUnbonding(ctx sdk.Context, delegatorAddr, validatorAddr // complete unbonding an unbonding record func (k Keeper) BeginRedelegation(ctx sdk.Context, delegatorAddr, validatorSrcAddr, - validatorDstAddr sdk.AccAddress, sharesAmount sdk.Rat) sdk.Error { + validatorDstAddr sdk.AccAddress, sharesAmount sdk.Dec) sdk.Error { // check if this is a transitive redelegation if k.HasReceivingRedelegation(ctx, delegatorAddr, validatorSrcAddr) { diff --git a/x/stake/keeper/delegation_test.go b/x/stake/keeper/delegation_test.go index 5d512f0cf..4333a7494 100644 --- a/x/stake/keeper/delegation_test.go +++ b/x/stake/keeper/delegation_test.go @@ -32,7 +32,7 @@ func TestDelegation(t *testing.T) { bond1to1 := types.Delegation{ DelegatorAddr: addrDels[0], ValidatorAddr: addrVals[0], - Shares: sdk.NewRat(9), + Shares: sdk.NewDec(9), } // check the empty keeper first @@ -46,18 +46,18 @@ func TestDelegation(t *testing.T) { require.True(t, bond1to1.Equal(resBond)) // modify a records, save, and retrieve - bond1to1.Shares = sdk.NewRat(99) + bond1to1.Shares = sdk.NewDec(99) keeper.SetDelegation(ctx, bond1to1) resBond, found = keeper.GetDelegation(ctx, addrDels[0], addrVals[0]) require.True(t, found) require.True(t, bond1to1.Equal(resBond)) // add some more records - bond1to2 := types.Delegation{addrDels[0], addrVals[1], sdk.NewRat(9), 0} - bond1to3 := types.Delegation{addrDels[0], addrVals[2], sdk.NewRat(9), 1} - bond2to1 := types.Delegation{addrDels[1], addrVals[0], sdk.NewRat(9), 2} - bond2to2 := types.Delegation{addrDels[1], addrVals[1], sdk.NewRat(9), 3} - bond2to3 := types.Delegation{addrDels[1], addrVals[2], sdk.NewRat(9), 4} + bond1to2 := types.Delegation{addrDels[0], addrVals[1], sdk.NewDec(9), 0} + bond1to3 := types.Delegation{addrDels[0], addrVals[2], sdk.NewDec(9), 1} + bond2to1 := types.Delegation{addrDels[1], addrVals[0], sdk.NewDec(9), 2} + bond2to2 := types.Delegation{addrDels[1], addrVals[1], sdk.NewDec(9), 3} + bond2to3 := types.Delegation{addrDels[1], addrVals[2], sdk.NewDec(9), 4} keeper.SetDelegation(ctx, bond1to2) keeper.SetDelegation(ctx, bond1to3) keeper.SetDelegation(ctx, bond2to1) @@ -142,7 +142,7 @@ func TestUnbondingDelegation(t *testing.T) { func TestUnbondDelegation(t *testing.T) { ctx, _, keeper := CreateTestInput(t, false, 0) pool := keeper.GetPool(ctx) - pool.LooseTokens = sdk.NewRat(10) + pool.LooseTokens = sdk.NewDec(10) //create a validator and a delegator to that validator validator := types.NewValidator(addrVals[0], PKs[0], types.Description{}) @@ -163,8 +163,8 @@ func TestUnbondDelegation(t *testing.T) { keeper.SetDelegation(ctx, delegation) var err error - var amount sdk.Rat - amount, err = keeper.unbond(ctx, addrDels[0], addrVals[0], sdk.NewRat(6)) + var amount sdk.Dec + amount, err = keeper.unbond(ctx, addrDels[0], addrVals[0], sdk.NewDec(6)) require.NoError(t, err) require.Equal(t, int64(6), amount.RoundInt64()) // shares to be added to an unbonding delegation / redelegation @@ -190,8 +190,8 @@ func TestGetRedelegationsFromValidator(t *testing.T) { ValidatorDstAddr: addrVals[1], CreationHeight: 0, MinTime: time.Unix(0, 0), - SharesSrc: sdk.NewRat(5), - SharesDst: sdk.NewRat(5), + SharesSrc: sdk.NewDec(5), + SharesDst: sdk.NewDec(5), } // set and retrieve a record @@ -220,8 +220,8 @@ func TestRedelegation(t *testing.T) { ValidatorDstAddr: addrVals[1], CreationHeight: 0, MinTime: time.Unix(0, 0), - SharesSrc: sdk.NewRat(5), - SharesDst: sdk.NewRat(5), + SharesSrc: sdk.NewDec(5), + SharesDst: sdk.NewDec(5), } // test shouldn't have and redelegations @@ -242,8 +242,8 @@ func TestRedelegation(t *testing.T) { require.True(t, has) // modify a records, save, and retrieve - rd.SharesSrc = sdk.NewRat(21) - rd.SharesDst = sdk.NewRat(21) + rd.SharesSrc = sdk.NewDec(21) + rd.SharesDst = sdk.NewDec(21) keeper.SetRedelegation(ctx, rd) resBond, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) diff --git a/x/stake/keeper/keeper_test.go b/x/stake/keeper/keeper_test.go index 3f763ea25..9307ddddf 100644 --- a/x/stake/keeper/keeper_test.go +++ b/x/stake/keeper/keeper_test.go @@ -33,7 +33,7 @@ func TestPool(t *testing.T) { require.True(t, expPool.Equal(resPool)) //modify a params, save, and retrieve - expPool.BondedTokens = sdk.NewRat(777) + expPool.BondedTokens = sdk.NewDec(777) keeper.SetPool(ctx, expPool) resPool = keeper.GetPool(ctx) require.True(t, expPool.Equal(resPool)) diff --git a/x/stake/keeper/sdk_types.go b/x/stake/keeper/sdk_types.go index 9c3311474..aeec44fae 100644 --- a/x/stake/keeper/sdk_types.go +++ b/x/stake/keeper/sdk_types.go @@ -69,7 +69,7 @@ func (k Keeper) ValidatorByPubKey(ctx sdk.Context, pubkey crypto.PubKey) sdk.Val } // total power from the bond -func (k Keeper) TotalPower(ctx sdk.Context) sdk.Rat { +func (k Keeper) TotalPower(ctx sdk.Context) sdk.Dec { pool := k.GetPool(ctx) return pool.BondedTokens } diff --git a/x/stake/keeper/slash.go b/x/stake/keeper/slash.go index a741b5a85..c1fabe441 100644 --- a/x/stake/keeper/slash.go +++ b/x/stake/keeper/slash.go @@ -20,15 +20,15 @@ import ( // CONTRACT: // Infraction committed at the current height or at a past height, // not at a height in the future -func (k Keeper) Slash(ctx sdk.Context, pubkey crypto.PubKey, infractionHeight int64, power int64, slashFactor sdk.Rat) { +func (k Keeper) Slash(ctx sdk.Context, pubkey crypto.PubKey, infractionHeight int64, power int64, slashFactor sdk.Dec) { logger := ctx.Logger().With("module", "x/stake") - if slashFactor.LT(sdk.ZeroRat()) { + if slashFactor.LT(sdk.ZeroDec()) { panic(fmt.Errorf("attempted to slash with a negative slashFactor: %v", slashFactor)) } // Amount of slashing = slash slashFactor * power at time of infraction - slashAmount := sdk.NewRat(power).Mul(slashFactor) + slashAmount := sdk.NewDec(power).Mul(slashFactor) // ref https://github.com/cosmos/cosmos-sdk/issues/1348 // ref https://github.com/cosmos/cosmos-sdk/issues/1471 @@ -89,7 +89,7 @@ func (k Keeper) Slash(ctx sdk.Context, pubkey crypto.PubKey, infractionHeight in } // Cannot decrease balance below zero - tokensToBurn := sdk.MinRat(remainingSlashAmount, validator.Tokens) + tokensToBurn := sdk.MinDec(remainingSlashAmount, validator.Tokens) // Get the current pool pool := k.GetPool(ctx) @@ -150,23 +150,23 @@ func (k Keeper) setRevoked(ctx sdk.Context, pubkey crypto.PubKey, revoked bool) // (the amount actually slashed may be less if there's // insufficient stake remaining) func (k Keeper) slashUnbondingDelegation(ctx sdk.Context, unbondingDelegation types.UnbondingDelegation, - infractionHeight int64, slashFactor sdk.Rat) (slashAmount sdk.Rat) { + infractionHeight int64, slashFactor sdk.Dec) (slashAmount sdk.Dec) { now := ctx.BlockHeader().Time // If unbonding started before this height, stake didn't contribute to infraction if unbondingDelegation.CreationHeight < infractionHeight { - return sdk.ZeroRat() + return sdk.ZeroDec() } if unbondingDelegation.MinTime.Before(now) { // Unbonding delegation no longer eligible for slashing, skip it // TODO Settle and delete it automatically? - return sdk.ZeroRat() + return sdk.ZeroDec() } // Calculate slash amount proportional to stake contributing to infraction - slashAmount = sdk.NewRatFromInt(unbondingDelegation.InitialBalance.Amount, sdk.OneInt()).Mul(slashFactor) + slashAmount = sdk.NewDecFromInt(unbondingDelegation.InitialBalance.Amount).Mul(slashFactor) // Don't slash more tokens than held // Possible since the unbonding delegation may already @@ -194,23 +194,23 @@ func (k Keeper) slashUnbondingDelegation(ctx sdk.Context, unbondingDelegation ty // (the amount actually slashed may be less if there's // insufficient stake remaining) func (k Keeper) slashRedelegation(ctx sdk.Context, validator types.Validator, redelegation types.Redelegation, - infractionHeight int64, slashFactor sdk.Rat) (slashAmount sdk.Rat) { + infractionHeight int64, slashFactor sdk.Dec) (slashAmount sdk.Dec) { now := ctx.BlockHeader().Time // If redelegation started before this height, stake didn't contribute to infraction if redelegation.CreationHeight < infractionHeight { - return sdk.ZeroRat() + return sdk.ZeroDec() } if redelegation.MinTime.Before(now) { // Redelegation no longer eligible for slashing, skip it // TODO Delete it automatically? - return sdk.ZeroRat() + return sdk.ZeroDec() } // Calculate slash amount proportional to stake contributing to infraction - slashAmount = sdk.NewRatFromInt(redelegation.InitialBalance.Amount, sdk.OneInt()).Mul(slashFactor) + slashAmount = sdk.NewDecFromInt(redelegation.InitialBalance.Amount).Mul(slashFactor) // Don't slash more tokens than held // Possible since the redelegation may already diff --git a/x/stake/keeper/slash_test.go b/x/stake/keeper/slash_test.go index 878c44d1e..444858f29 100644 --- a/x/stake/keeper/slash_test.go +++ b/x/stake/keeper/slash_test.go @@ -19,7 +19,7 @@ func setupHelper(t *testing.T, amt int64) (sdk.Context, Keeper, types.Params) { params := keeper.GetParams(ctx) pool := keeper.GetPool(ctx) numVals := 3 - pool.LooseTokens = sdk.NewRat(amt * int64(numVals)) + pool.LooseTokens = sdk.NewDec(amt * int64(numVals)) // add numVals validators for i := 0; i < numVals; i++ { @@ -63,7 +63,7 @@ func TestRevocation(t *testing.T) { // tests slashUnbondingDelegation func TestSlashUnbondingDelegation(t *testing.T) { ctx, keeper, params := setupHelper(t, 10) - fraction := sdk.NewRat(1, 2) + fraction := sdk.NewDecWithPrec(5, 1) // set an unbonding delegation ubd := types.UnbondingDelegation{ @@ -106,7 +106,7 @@ func TestSlashUnbondingDelegation(t *testing.T) { // tests slashRedelegation func TestSlashRedelegation(t *testing.T) { ctx, keeper, params := setupHelper(t, 10) - fraction := sdk.NewRat(1, 2) + fraction := sdk.NewDecWithPrec(5, 1) // set a redelegation rd := types.Redelegation{ @@ -116,8 +116,8 @@ func TestSlashRedelegation(t *testing.T) { CreationHeight: 0, // expiration timestamp (beyond which the redelegation shouldn't be slashed) MinTime: time.Unix(0, 0), - SharesSrc: sdk.NewRat(10), - SharesDst: sdk.NewRat(10), + SharesSrc: sdk.NewDec(10), + SharesDst: sdk.NewDec(10), InitialBalance: sdk.NewInt64Coin(params.BondDenom, 10), Balance: sdk.NewInt64Coin(params.BondDenom, 10), } @@ -127,7 +127,7 @@ func TestSlashRedelegation(t *testing.T) { del := types.Delegation{ DelegatorAddr: addrDels[0], ValidatorAddr: addrVals[1], - Shares: sdk.NewRat(10), + Shares: sdk.NewDec(10), } keeper.SetDelegation(ctx, del) @@ -172,7 +172,7 @@ func TestSlashRedelegation(t *testing.T) { func TestSlashAtFutureHeight(t *testing.T) { ctx, keeper, _ := setupHelper(t, 10) pk := PKs[0] - fraction := sdk.NewRat(1, 2) + fraction := sdk.NewDecWithPrec(5, 1) require.Panics(t, func() { keeper.Slash(ctx, pk, 1, 10, fraction) }) } @@ -180,7 +180,7 @@ func TestSlashAtFutureHeight(t *testing.T) { func TestSlashAtCurrentHeight(t *testing.T) { ctx, keeper, _ := setupHelper(t, 10) pk := PKs[0] - fraction := sdk.NewRat(1, 2) + fraction := sdk.NewDecWithPrec(5, 1) oldPool := keeper.GetPool(ctx) validator, found := keeper.GetValidatorByPubKey(ctx, pk) @@ -193,16 +193,16 @@ func TestSlashAtCurrentHeight(t *testing.T) { newPool := keeper.GetPool(ctx) // power decreased - require.Equal(t, sdk.NewRat(5), validator.GetPower()) + require.Equal(t, sdk.NewDec(5), validator.GetPower()) // pool bonded shares decreased - require.Equal(t, sdk.NewRat(5).RoundInt64(), oldPool.BondedTokens.Sub(newPool.BondedTokens).RoundInt64()) + require.Equal(t, sdk.NewDec(5).RoundInt64(), oldPool.BondedTokens.Sub(newPool.BondedTokens).RoundInt64()) } // tests Slash at a previous height with an unbonding delegation func TestSlashWithUnbondingDelegation(t *testing.T) { ctx, keeper, params := setupHelper(t, 10) pk := PKs[0] - fraction := sdk.NewRat(1, 2) + fraction := sdk.NewDecWithPrec(5, 1) // set an unbonding delegation ubd := types.UnbondingDelegation{ @@ -239,7 +239,7 @@ func TestSlashWithUnbondingDelegation(t *testing.T) { // was still bonded at the time of discovery and was slashed by half, 4 stake // bonded at the time of discovery hadn't been bonded at the time of infraction // and wasn't slashed - require.Equal(t, sdk.NewRat(7), validator.GetPower()) + require.Equal(t, sdk.NewDec(7), validator.GetPower()) // slash validator again ctx = ctx.WithBlockHeight(13) @@ -256,7 +256,7 @@ func TestSlashWithUnbondingDelegation(t *testing.T) { validator, found = keeper.GetValidatorByPubKey(ctx, pk) require.True(t, found) // power decreased by 3 again - require.Equal(t, sdk.NewRat(4), validator.GetPower()) + require.Equal(t, sdk.NewDec(4), validator.GetPower()) // slash validator again // all originally bonded stake has been slashed, so this will have no effect @@ -276,7 +276,7 @@ func TestSlashWithUnbondingDelegation(t *testing.T) { validator, found = keeper.GetValidatorByPubKey(ctx, pk) require.True(t, found) // power decreased by 3 again - require.Equal(t, sdk.NewRat(1), validator.GetPower()) + require.Equal(t, sdk.NewDec(1), validator.GetPower()) // slash validator again // all originally bonded stake has been slashed, so this will have no effect @@ -303,7 +303,7 @@ func TestSlashWithUnbondingDelegation(t *testing.T) { func TestSlashWithRedelegation(t *testing.T) { ctx, keeper, params := setupHelper(t, 10) pk := PKs[0] - fraction := sdk.NewRat(1, 2) + fraction := sdk.NewDecWithPrec(5, 1) // set a redelegation rd := types.Redelegation{ @@ -312,8 +312,8 @@ func TestSlashWithRedelegation(t *testing.T) { ValidatorDstAddr: addrVals[1], CreationHeight: 11, MinTime: time.Unix(0, 0), - SharesSrc: sdk.NewRat(6), - SharesDst: sdk.NewRat(6), + SharesSrc: sdk.NewDec(6), + SharesDst: sdk.NewDec(6), InitialBalance: sdk.NewInt64Coin(params.BondDenom, 6), Balance: sdk.NewInt64Coin(params.BondDenom, 6), } @@ -323,13 +323,13 @@ func TestSlashWithRedelegation(t *testing.T) { del := types.Delegation{ DelegatorAddr: addrDels[0], ValidatorAddr: addrVals[1], - Shares: sdk.NewRat(6), + Shares: sdk.NewDec(6), } keeper.SetDelegation(ctx, del) // update bonded tokens pool := keeper.GetPool(ctx) - pool.BondedTokens = pool.BondedTokens.Add(sdk.NewRat(6)) + pool.BondedTokens = pool.BondedTokens.Add(sdk.NewDec(6)) keeper.SetPool(ctx, pool) // slash validator @@ -355,13 +355,13 @@ func TestSlashWithRedelegation(t *testing.T) { // was still bonded at the time of discovery and was slashed by half, 4 stake // bonded at the time of discovery hadn't been bonded at the time of infraction // and wasn't slashed - require.Equal(t, sdk.NewRat(8), validator.GetPower()) + require.Equal(t, sdk.NewDec(8), validator.GetPower()) // slash the validator again ctx = ctx.WithBlockHeight(12) validator, found = keeper.GetValidatorByPubKey(ctx, pk) require.True(t, found) - require.NotPanics(t, func() { keeper.Slash(ctx, pk, 10, 10, sdk.OneRat()) }) + require.NotPanics(t, func() { keeper.Slash(ctx, pk, 10, 10, sdk.OneDec()) }) // read updating redelegation rd, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) @@ -376,13 +376,13 @@ func TestSlashWithRedelegation(t *testing.T) { validator, found = keeper.GetValidatorByPubKey(ctx, pk) require.True(t, found) // power decreased by 4 - require.Equal(t, sdk.NewRat(4), validator.GetPower()) + require.Equal(t, sdk.NewDec(4), validator.GetPower()) // slash the validator again, by 100% ctx = ctx.WithBlockHeight(12) validator, found = keeper.GetValidatorByPubKey(ctx, pk) require.True(t, found) - keeper.Slash(ctx, pk, 10, 10, sdk.OneRat()) + keeper.Slash(ctx, pk, 10, 10, sdk.OneDec()) // read updating redelegation rd, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) @@ -404,7 +404,7 @@ func TestSlashWithRedelegation(t *testing.T) { // validator no longer in the store _, found = keeper.GetValidatorByPubKey(ctx, pk) require.False(t, found) - keeper.Slash(ctx, pk, 10, 10, sdk.OneRat()) + keeper.Slash(ctx, pk, 10, 10, sdk.OneDec()) // read updating redelegation rd, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) @@ -424,7 +424,7 @@ func TestSlashWithRedelegation(t *testing.T) { // tests Slash at a previous height with both an unbonding delegation and a redelegation func TestSlashBoth(t *testing.T) { ctx, keeper, params := setupHelper(t, 10) - fraction := sdk.NewRat(1, 2) + fraction := sdk.NewDecWithPrec(5, 1) // set a redelegation rdA := types.Redelegation{ @@ -434,8 +434,8 @@ func TestSlashBoth(t *testing.T) { CreationHeight: 11, // expiration timestamp (beyond which the redelegation shouldn't be slashed) MinTime: time.Unix(0, 0), - SharesSrc: sdk.NewRat(6), - SharesDst: sdk.NewRat(6), + SharesSrc: sdk.NewDec(6), + SharesDst: sdk.NewDec(6), InitialBalance: sdk.NewInt64Coin(params.BondDenom, 6), Balance: sdk.NewInt64Coin(params.BondDenom, 6), } @@ -445,7 +445,7 @@ func TestSlashBoth(t *testing.T) { delA := types.Delegation{ DelegatorAddr: addrDels[0], ValidatorAddr: addrVals[1], - Shares: sdk.NewRat(6), + Shares: sdk.NewDec(6), } keeper.SetDelegation(ctx, delA) @@ -483,5 +483,5 @@ func TestSlashBoth(t *testing.T) { validator, found = keeper.GetValidatorByPubKey(ctx, PKs[0]) require.True(t, found) // power not decreased, all stake was bonded since - require.Equal(t, sdk.NewRat(10), validator.GetPower()) + require.Equal(t, sdk.NewDec(10), validator.GetPower()) } diff --git a/x/stake/keeper/test_common.go b/x/stake/keeper/test_common.go index 010963034..0470c2898 100644 --- a/x/stake/keeper/test_common.go +++ b/x/stake/keeper/test_common.go @@ -77,10 +77,10 @@ func MakeTestCodec() *wire.Codec { // default params without inflation func ParamsNoInflation() types.Params { return types.Params{ - InflationRateChange: sdk.ZeroRat(), - InflationMax: sdk.ZeroRat(), - InflationMin: sdk.ZeroRat(), - GoalBonded: sdk.NewRat(67, 100), + InflationRateChange: sdk.ZeroDec(), + InflationMax: sdk.ZeroDec(), + InflationMin: sdk.ZeroDec(), + GoalBonded: sdk.NewDecWithPrec(67, 2), MaxValidators: 100, BondDenom: "steak", } @@ -119,7 +119,7 @@ func CreateTestInput(t *testing.T, isCheckTx bool, initCoins int64) (sdk.Context {keeper.GetParams(ctx).BondDenom, sdk.NewInt(initCoins)}, }) require.Nil(t, err) - pool.LooseTokens = pool.LooseTokens.Add(sdk.NewRat(initCoins)) + pool.LooseTokens = pool.LooseTokens.Add(sdk.NewDec(initCoins)) keeper.SetPool(ctx, pool) } diff --git a/x/stake/keeper/validator_test.go b/x/stake/keeper/validator_test.go index b9e61a101..7b15a9c7f 100644 --- a/x/stake/keeper/validator_test.go +++ b/x/stake/keeper/validator_test.go @@ -20,8 +20,8 @@ func TestSetValidator(t *testing.T) { validator := types.NewValidator(addrVals[0], PKs[0], types.Description{}) validator, pool, _ = validator.AddTokensFromDel(pool, 10) require.Equal(t, sdk.Unbonded, validator.Status) - assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.Tokens)) - assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.DelegatorShares)) + assert.True(sdk.DecEq(t, sdk.NewDec(10), validator.Tokens)) + assert.True(sdk.DecEq(t, sdk.NewDec(10), validator.DelegatorShares)) keeper.SetPool(ctx, pool) keeper.UpdateValidator(ctx, validator) @@ -29,8 +29,8 @@ func TestSetValidator(t *testing.T) { validator, found := keeper.GetValidator(ctx, addrVals[0]) require.True(t, found) require.Equal(t, sdk.Bonded, validator.Status) - assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.Tokens)) - assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.DelegatorShares)) + assert.True(sdk.DecEq(t, sdk.NewDec(10), validator.Tokens)) + assert.True(sdk.DecEq(t, sdk.NewDec(10), validator.DelegatorShares)) // Check each store for being saved resVal, found := keeper.GetValidator(ctx, addrVals[0]) @@ -56,8 +56,8 @@ func TestUpdateValidatorByPowerIndex(t *testing.T) { pool := keeper.GetPool(ctx) // create a random pool - pool.LooseTokens = sdk.NewRat(10000) - pool.BondedTokens = sdk.NewRat(1234) + pool.LooseTokens = sdk.NewDec(10000) + pool.BondedTokens = sdk.NewDec(1234) keeper.SetPool(ctx, pool) // add a validator @@ -76,7 +76,7 @@ func TestUpdateValidatorByPowerIndex(t *testing.T) { require.True(t, keeper.validatorByPowerIndexExists(ctx, power)) // burn half the delegator shares - validator, pool, burned := validator.RemoveDelShares(pool, delSharesCreated.Quo(sdk.NewRat(2))) + validator, pool, burned := validator.RemoveDelShares(pool, delSharesCreated.Quo(sdk.NewDec(2))) require.Equal(t, int64(50), burned.RoundInt64()) keeper.SetPool(ctx, pool) // update the pool keeper.UpdateValidator(ctx, validator) // update the validator, possibly kicking it out @@ -103,8 +103,8 @@ func TestCliffValidatorChange(t *testing.T) { keeper.SetParams(ctx, params) // create a random pool - pool.LooseTokens = sdk.NewRat(10000) - pool.BondedTokens = sdk.NewRat(1234) + pool.LooseTokens = sdk.NewDec(10000) + pool.BondedTokens = sdk.NewDec(1234) keeper.SetPool(ctx, pool) validators := make([]types.Validator, numVals) @@ -161,7 +161,7 @@ func TestSlashToZeroPowerRemoved(t *testing.T) { require.Equal(t, int64(100), validator.Tokens.RoundInt64(), "\nvalidator %v\npool %v", validator, pool) // slash the validator by 100% - keeper.Slash(ctx, PKs[0], 0, 100, sdk.OneRat()) + keeper.Slash(ctx, PKs[0], 0, 100, sdk.OneDec()) // validator should have been deleted _, found := keeper.GetValidator(ctx, addrVals[0]) require.False(t, found) @@ -178,13 +178,13 @@ func TestValidatorBasics(t *testing.T) { for i, amt := range amts { validators[i] = types.NewValidator(addrVals[i], PKs[i], types.Description{}) validators[i].Status = sdk.Unbonded - validators[i].Tokens = sdk.ZeroRat() + validators[i].Tokens = sdk.ZeroDec() validators[i], pool, _ = validators[i].AddTokensFromDel(pool, amt) keeper.SetPool(ctx, pool) } - assert.True(sdk.RatEq(t, sdk.NewRat(9), validators[0].Tokens)) - assert.True(sdk.RatEq(t, sdk.NewRat(8), validators[1].Tokens)) - assert.True(sdk.RatEq(t, sdk.NewRat(7), validators[2].Tokens)) + assert.True(sdk.DecEq(t, sdk.NewDec(9), validators[0].Tokens)) + assert.True(sdk.DecEq(t, sdk.NewDec(8), validators[1].Tokens)) + assert.True(sdk.DecEq(t, sdk.NewDec(7), validators[2].Tokens)) // check the empty keeper first _, found := keeper.GetValidator(ctx, addrVals[0]) @@ -193,7 +193,7 @@ func TestValidatorBasics(t *testing.T) { assert.Zero(t, len(resVals)) pool = keeper.GetPool(ctx) - assert.True(sdk.RatEq(t, sdk.ZeroRat(), pool.BondedTokens)) + assert.True(sdk.DecEq(t, sdk.ZeroDec(), pool.BondedTokens)) // set and retrieve a record validators[0] = keeper.UpdateValidator(ctx, validators[0]) @@ -205,15 +205,15 @@ func TestValidatorBasics(t *testing.T) { require.Equal(t, 1, len(resVals)) assert.True(ValEq(t, validators[0], resVals[0])) assert.Equal(t, sdk.Bonded, validators[0].Status) - assert.True(sdk.RatEq(t, sdk.NewRat(9), validators[0].BondedTokens())) + assert.True(sdk.DecEq(t, sdk.NewDec(9), validators[0].BondedTokens())) pool = keeper.GetPool(ctx) - assert.True(sdk.RatEq(t, pool.BondedTokens, validators[0].BondedTokens())) + assert.True(sdk.DecEq(t, pool.BondedTokens, validators[0].BondedTokens())) // modify a records, save, and retrieve validators[0].Status = sdk.Bonded - validators[0].Tokens = sdk.NewRat(10) - validators[0].DelegatorShares = sdk.NewRat(10) + validators[0].Tokens = sdk.NewDec(10) + validators[0].DelegatorShares = sdk.NewDec(10) validators[0] = keeper.UpdateValidator(ctx, validators[0]) resVal, found = keeper.GetValidator(ctx, addrVals[0]) require.True(t, found) @@ -256,19 +256,19 @@ func GetValidatorSortingUnmixed(t *testing.T) { for i, amt := range amts { validators[i] = types.NewValidator(Addrs[i], PKs[i], types.Description{}) validators[i].Status = sdk.Bonded - validators[i].Tokens = sdk.NewRat(amt) - validators[i].DelegatorShares = sdk.NewRat(amt) + validators[i].Tokens = sdk.NewDec(amt) + validators[i].DelegatorShares = sdk.NewDec(amt) keeper.UpdateValidator(ctx, validators[i]) } // first make sure everything made it in to the gotValidator group resValidators := keeper.GetValidatorsByPower(ctx) assert.Equal(t, n, len(resValidators)) - assert.Equal(t, sdk.NewRat(400), resValidators[0].BondedTokens(), "%v", resValidators) - assert.Equal(t, sdk.NewRat(200), resValidators[1].BondedTokens(), "%v", resValidators) - assert.Equal(t, sdk.NewRat(100), resValidators[2].BondedTokens(), "%v", resValidators) - assert.Equal(t, sdk.NewRat(1), resValidators[3].BondedTokens(), "%v", resValidators) - assert.Equal(t, sdk.NewRat(0), resValidators[4].BondedTokens(), "%v", resValidators) + assert.Equal(t, sdk.NewDec(400), resValidators[0].BondedTokens(), "%v", resValidators) + assert.Equal(t, sdk.NewDec(200), resValidators[1].BondedTokens(), "%v", resValidators) + assert.Equal(t, sdk.NewDec(100), resValidators[2].BondedTokens(), "%v", resValidators) + assert.Equal(t, sdk.NewDec(1), resValidators[3].BondedTokens(), "%v", resValidators) + assert.Equal(t, sdk.NewDec(0), resValidators[4].BondedTokens(), "%v", resValidators) assert.Equal(t, validators[3].Owner, resValidators[0].Owner, "%v", resValidators) assert.Equal(t, validators[4].Owner, resValidators[1].Owner, "%v", resValidators) assert.Equal(t, validators[1].Owner, resValidators[2].Owner, "%v", resValidators) @@ -276,14 +276,14 @@ func GetValidatorSortingUnmixed(t *testing.T) { assert.Equal(t, validators[0].Owner, resValidators[4].Owner, "%v", resValidators) // test a basic increase in voting power - validators[3].Tokens = sdk.NewRat(500) + validators[3].Tokens = sdk.NewDec(500) keeper.UpdateValidator(ctx, validators[3]) resValidators = keeper.GetValidatorsByPower(ctx) require.Equal(t, len(resValidators), n) assert.True(ValEq(t, validators[3], resValidators[0])) // test a decrease in voting power - validators[3].Tokens = sdk.NewRat(300) + validators[3].Tokens = sdk.NewDec(300) keeper.UpdateValidator(ctx, validators[3]) resValidators = keeper.GetValidatorsByPower(ctx) require.Equal(t, len(resValidators), n) @@ -291,7 +291,7 @@ func GetValidatorSortingUnmixed(t *testing.T) { assert.True(ValEq(t, validators[4], resValidators[1])) // test equal voting power, different age - validators[3].Tokens = sdk.NewRat(200) + validators[3].Tokens = sdk.NewDec(200) ctx = ctx.WithBlockHeight(10) keeper.UpdateValidator(ctx, validators[3]) resValidators = keeper.GetValidatorsByPower(ctx) @@ -310,8 +310,8 @@ func GetValidatorSortingUnmixed(t *testing.T) { assert.True(ValEq(t, validators[4], resValidators[1])) // change in voting power of both validators, both still in v-set, no age change - validators[3].Tokens = sdk.NewRat(300) - validators[4].Tokens = sdk.NewRat(300) + validators[3].Tokens = sdk.NewDec(300) + validators[4].Tokens = sdk.NewDec(300) keeper.UpdateValidator(ctx, validators[3]) resValidators = keeper.GetValidatorsByPower(ctx) require.Equal(t, len(resValidators), n) @@ -338,20 +338,20 @@ func GetValidatorSortingMixed(t *testing.T) { var validators [5]types.Validator for i, amt := range amts { validators[i] = types.NewValidator(Addrs[i], PKs[i], types.Description{}) - validators[i].DelegatorShares = sdk.NewRat(amt) + validators[i].DelegatorShares = sdk.NewDec(amt) } validators[0].Status = sdk.Bonded validators[1].Status = sdk.Bonded validators[2].Status = sdk.Bonded - validators[0].Tokens = sdk.NewRat(amts[0]) - validators[1].Tokens = sdk.NewRat(amts[1]) - validators[2].Tokens = sdk.NewRat(amts[2]) + validators[0].Tokens = sdk.NewDec(amts[0]) + validators[1].Tokens = sdk.NewDec(amts[1]) + validators[2].Tokens = sdk.NewDec(amts[2]) validators[3].Status = sdk.Bonded validators[4].Status = sdk.Bonded - validators[3].Tokens = sdk.NewRat(amts[3]) - validators[4].Tokens = sdk.NewRat(amts[4]) + validators[3].Tokens = sdk.NewDec(amts[3]) + validators[4].Tokens = sdk.NewDec(amts[4]) for i := range amts { keeper.UpdateValidator(ctx, validators[i]) @@ -375,11 +375,11 @@ func GetValidatorSortingMixed(t *testing.T) { // first make sure everything made it in to the gotValidator group resValidators := keeper.GetValidatorsByPower(ctx) assert.Equal(t, n, len(resValidators)) - assert.Equal(t, sdk.NewRat(400), resValidators[0].BondedTokens(), "%v", resValidators) - assert.Equal(t, sdk.NewRat(200), resValidators[1].BondedTokens(), "%v", resValidators) - assert.Equal(t, sdk.NewRat(100), resValidators[2].BondedTokens(), "%v", resValidators) - assert.Equal(t, sdk.NewRat(1), resValidators[3].BondedTokens(), "%v", resValidators) - assert.Equal(t, sdk.NewRat(0), resValidators[4].BondedTokens(), "%v", resValidators) + assert.Equal(t, sdk.NewDec(400), resValidators[0].BondedTokens(), "%v", resValidators) + assert.Equal(t, sdk.NewDec(200), resValidators[1].BondedTokens(), "%v", resValidators) + assert.Equal(t, sdk.NewDec(100), resValidators[2].BondedTokens(), "%v", resValidators) + assert.Equal(t, sdk.NewDec(1), resValidators[3].BondedTokens(), "%v", resValidators) + assert.Equal(t, sdk.NewDec(0), resValidators[4].BondedTokens(), "%v", resValidators) assert.Equal(t, validators[3].Owner, resValidators[0].Owner, "%v", resValidators) assert.Equal(t, validators[4].Owner, resValidators[1].Owner, "%v", resValidators) assert.Equal(t, validators[1].Owner, resValidators[2].Owner, "%v", resValidators) @@ -444,7 +444,7 @@ func TestGetValidatorsEdgeCases(t *testing.T) { assert.True(ValEq(t, validators[3], resValidators[1])) // validator 3 kicked out temporarily - validators[3], pool, _ = validators[3].RemoveDelShares(pool, sdk.NewRat(201)) + validators[3], pool, _ = validators[3].RemoveDelShares(pool, sdk.NewDec(201)) keeper.SetPool(ctx, pool) validators[3] = keeper.UpdateValidator(ctx, validators[3]) resValidators = keeper.GetValidatorsByPower(ctx) @@ -656,7 +656,7 @@ func TestGetTendermintUpdatesSingleValueChange(t *testing.T) { // test single value change // tendermintUpdate set: {} -> {c1'} validators[0].Status = sdk.Bonded - validators[0].Tokens = sdk.NewRat(600) + validators[0].Tokens = sdk.NewDec(600) validators[0] = keeper.UpdateValidator(ctx, validators[0]) updates := keeper.GetTendermintUpdates(ctx) @@ -794,21 +794,21 @@ func TestGetTendermintUpdatesPowerDecrease(t *testing.T) { require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx))) // check initial power - require.Equal(t, sdk.NewRat(100).RoundInt64(), validators[0].GetPower().RoundInt64()) - require.Equal(t, sdk.NewRat(100).RoundInt64(), validators[1].GetPower().RoundInt64()) + require.Equal(t, sdk.NewDec(100).RoundInt64(), validators[0].GetPower().RoundInt64()) + require.Equal(t, sdk.NewDec(100).RoundInt64(), validators[1].GetPower().RoundInt64()) // test multiple value change // tendermintUpdate set: {c1, c3} -> {c1', c3'} pool := keeper.GetPool(ctx) - validators[0], pool, _ = validators[0].RemoveDelShares(pool, sdk.NewRat(20)) - validators[1], pool, _ = validators[1].RemoveDelShares(pool, sdk.NewRat(30)) + validators[0], pool, _ = validators[0].RemoveDelShares(pool, sdk.NewDec(20)) + validators[1], pool, _ = validators[1].RemoveDelShares(pool, sdk.NewDec(30)) keeper.SetPool(ctx, pool) validators[0] = keeper.UpdateValidator(ctx, validators[0]) validators[1] = keeper.UpdateValidator(ctx, validators[1]) // power has changed - require.Equal(t, sdk.NewRat(80).RoundInt64(), validators[0].GetPower().RoundInt64()) - require.Equal(t, sdk.NewRat(70).RoundInt64(), validators[1].GetPower().RoundInt64()) + require.Equal(t, sdk.NewDec(80).RoundInt64(), validators[0].GetPower().RoundInt64()) + require.Equal(t, sdk.NewDec(70).RoundInt64(), validators[1].GetPower().RoundInt64()) // Tendermint updates should reflect power change updates := keeper.GetTendermintUpdates(ctx) diff --git a/x/stake/simulation/invariants.go b/x/stake/simulation/invariants.go index e4869693c..654d5560b 100644 --- a/x/stake/simulation/invariants.go +++ b/x/stake/simulation/invariants.go @@ -31,7 +31,7 @@ func SupplyInvariants(ck bank.Keeper, k stake.Keeper, am auth.AccountMapper) sim pool := k.GetPool(ctx) loose := sdk.ZeroInt() - bonded := sdk.ZeroRat() + bonded := sdk.ZeroDec() am.IterateAccounts(ctx, func(acc auth.Account) bool { loose = loose.Add(acc.GetCoins().AmountOf("steak")) return false @@ -67,7 +67,7 @@ func PositivePowerInvariant(k stake.Keeper) simulation.Invariant { return func(t *testing.T, app *baseapp.BaseApp, log string) { ctx := app.NewContext(false, abci.Header{}) k.IterateValidatorsBonded(ctx, func(_ int64, validator sdk.Validator) bool { - require.True(t, validator.GetPower().GT(sdk.ZeroRat()), "validator with non-positive power stored") + require.True(t, validator.GetPower().GT(sdk.ZeroDec()), "validator with non-positive power stored") return false }) } diff --git a/x/stake/simulation/msgs.go b/x/stake/simulation/msgs.go index e4077a749..f402d765f 100644 --- a/x/stake/simulation/msgs.go +++ b/x/stake/simulation/msgs.go @@ -132,7 +132,7 @@ func SimulateMsgBeginUnbonding(m auth.AccountMapper, k stake.Keeper) simulation. msg := stake.MsgBeginUnbonding{ DelegatorAddr: delegatorAddress, ValidatorAddr: validatorAddress, - SharesAmount: sdk.NewRatFromInt(amount), + SharesAmount: sdk.NewDecFromInt(amount), } require.Nil(t, msg.ValidateBasic(), "expected msg to pass ValidateBasic: %s", msg.GetSignBytes()) ctx, write := ctx.CacheContext() @@ -191,7 +191,7 @@ func SimulateMsgBeginRedelegate(m auth.AccountMapper, k stake.Keeper) simulation DelegatorAddr: delegatorAddress, ValidatorSrcAddr: sourceValidatorAddress, ValidatorDstAddr: destValidatorAddress, - SharesAmount: sdk.NewRatFromInt(amount), + SharesAmount: sdk.NewDecFromInt(amount), } require.Nil(t, msg.ValidateBasic(), "expected msg to pass ValidateBasic: %s", msg.GetSignBytes()) ctx, write := ctx.CacheContext() @@ -247,7 +247,7 @@ func Setup(mapp *mock.App, k stake.Keeper) simulation.RandSetup { return false }) pool := k.GetPool(ctx) - pool.LooseTokens = pool.LooseTokens.Add(sdk.NewRat(loose.Int64(), 1)) + pool.LooseTokens = pool.LooseTokens.Add(sdk.NewDec(loose.Int64())) k.SetPool(ctx, pool) } } diff --git a/x/stake/types/delegation.go b/x/stake/types/delegation.go index 77fd327a5..78111c030 100644 --- a/x/stake/types/delegation.go +++ b/x/stake/types/delegation.go @@ -16,12 +16,12 @@ import ( type Delegation struct { DelegatorAddr sdk.AccAddress `json:"delegator_addr"` ValidatorAddr sdk.AccAddress `json:"validator_addr"` - Shares sdk.Rat `json:"shares"` + Shares sdk.Dec `json:"shares"` Height int64 `json:"height"` // Last height bond updated } type delegationValue struct { - Shares sdk.Rat + Shares sdk.Dec Height int64 } @@ -81,7 +81,7 @@ var _ sdk.Delegation = Delegation{} // nolint - for sdk.Delegation func (d Delegation) GetDelegator() sdk.AccAddress { return d.DelegatorAddr } func (d Delegation) GetValidator() sdk.AccAddress { return d.ValidatorAddr } -func (d Delegation) GetBondShares() sdk.Rat { return d.Shares } +func (d Delegation) GetBondShares() sdk.Dec { return d.Shares } // HumanReadableString returns a human readable string representation of a // Delegation. An error is returned if the Delegation's delegator or validator @@ -190,8 +190,8 @@ type Redelegation struct { MinTime time.Time `json:"min_time"` // unix time for redelegation completion InitialBalance sdk.Coin `json:"initial_balance"` // initial balance when redelegation started Balance sdk.Coin `json:"balance"` // current balance - SharesSrc sdk.Rat `json:"shares_src"` // amount of source shares redelegating - SharesDst sdk.Rat `json:"shares_dst"` // amount of destination shares redelegating + SharesSrc sdk.Dec `json:"shares_src"` // amount of source shares redelegating + SharesDst sdk.Dec `json:"shares_dst"` // amount of destination shares redelegating } type redValue struct { @@ -199,8 +199,8 @@ type redValue struct { MinTime time.Time InitialBalance sdk.Coin Balance sdk.Coin - SharesSrc sdk.Rat - SharesDst sdk.Rat + SharesSrc sdk.Dec + SharesDst sdk.Dec } // return the redelegation without fields contained within the key for the store diff --git a/x/stake/types/delegation_test.go b/x/stake/types/delegation_test.go index 69823db14..5624b7101 100644 --- a/x/stake/types/delegation_test.go +++ b/x/stake/types/delegation_test.go @@ -12,19 +12,19 @@ func TestDelegationEqual(t *testing.T) { d1 := Delegation{ DelegatorAddr: addr1, ValidatorAddr: addr2, - Shares: sdk.NewRat(100), + Shares: sdk.NewDec(100), } d2 := Delegation{ DelegatorAddr: addr1, ValidatorAddr: addr2, - Shares: sdk.NewRat(100), + Shares: sdk.NewDec(100), } ok := d1.Equal(d2) require.True(t, ok) d2.ValidatorAddr = addr3 - d2.Shares = sdk.NewRat(200) + d2.Shares = sdk.NewDec(200) ok = d1.Equal(d2) require.False(t, ok) @@ -34,7 +34,7 @@ func TestDelegationHumanReadableString(t *testing.T) { d := Delegation{ DelegatorAddr: addr1, ValidatorAddr: addr2, - Shares: sdk.NewRat(100), + Shares: sdk.NewDec(100), } // NOTE: Being that the validator's keypair is random, we cannot test the @@ -58,8 +58,8 @@ func TestUnbondingDelegationEqual(t *testing.T) { require.True(t, ok) ud2.ValidatorAddr = addr3 - ud2.MinTime = time.Unix(20*20*2, 0) + ud2.MinTime = time.Unix(20*20*2, 0) ok = ud1.Equal(ud2) require.False(t, ok) } @@ -92,8 +92,8 @@ func TestRedelegationEqual(t *testing.T) { ok := r1.Equal(r2) require.True(t, ok) - r2.SharesDst = sdk.NewRat(10) - r2.SharesSrc = sdk.NewRat(20) + r2.SharesDst = sdk.NewDec(10) + r2.SharesSrc = sdk.NewDec(20) r2.MinTime = time.Unix(20*20*2, 0) ok = r1.Equal(r2) @@ -105,8 +105,8 @@ func TestRedelegationHumanReadableString(t *testing.T) { DelegatorAddr: addr1, ValidatorSrcAddr: addr2, ValidatorDstAddr: addr3, - SharesDst: sdk.NewRat(10), - SharesSrc: sdk.NewRat(20), + SharesDst: sdk.NewDec(10), + SharesSrc: sdk.NewDec(20), } // NOTE: Being that the validator's keypair is random, we cannot test the diff --git a/x/stake/types/errors.go b/x/stake/types/errors.go index b3e279c8e..1595a70d9 100644 --- a/x/stake/types/errors.go +++ b/x/stake/types/errors.go @@ -100,13 +100,6 @@ func ErrBadSharesAmount(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, CodeInvalidDelegation, "shares must be > 0") } -func ErrBadSharesPrecision(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, CodeInvalidDelegation, - fmt.Sprintf("shares denominator must be < %s, try reducing the number of decimal points", - maximumBondingRationalDenominator.String()), - ) -} - func ErrBadSharesPercent(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, CodeInvalidDelegation, "shares percent must be >0 and <=1") } diff --git a/x/stake/types/inflation_test.go b/x/stake/types/inflation_test.go index 0114b1e05..fd181af3c 100644 --- a/x/stake/types/inflation_test.go +++ b/x/stake/types/inflation_test.go @@ -23,30 +23,30 @@ func TestGetInflation(t *testing.T) { tests := []struct { name string setBondedTokens, setLooseTokens, - setInflation, expectedChange sdk.Rat + setInflation, expectedChange sdk.Dec }{ // with 0% bonded atom supply the inflation should increase by InflationRateChange - {"test 1", sdk.ZeroRat(), sdk.ZeroRat(), sdk.NewRat(7, 100), params.InflationRateChange.Quo(hrsPerYrRat).Round(precision)}, + {"test 1", sdk.ZeroDec(), sdk.ZeroDec(), sdk.NewDecWithPrec(7, 2), params.InflationRateChange.Quo(hrsPerYrDec)}, // 100% bonded, starting at 20% inflation and being reduced // (1 - (1/0.67))*(0.13/8667) - {"test 2", sdk.OneRat(), sdk.ZeroRat(), sdk.NewRat(20, 100), - sdk.OneRat().Sub(sdk.OneRat().Quo(params.GoalBonded)).Mul(params.InflationRateChange).Quo(hrsPerYrRat).Round(precision)}, + {"test 2", sdk.OneDec(), sdk.ZeroDec(), sdk.NewDecWithPrec(20, 2), + sdk.OneDec().Sub(sdk.OneDec().Quo(params.GoalBonded)).Mul(params.InflationRateChange).Quo(hrsPerYrDec)}, // 50% bonded, starting at 10% inflation and being increased - {"test 3", sdk.OneRat(), sdk.OneRat(), sdk.NewRat(10, 100), - sdk.OneRat().Sub(sdk.NewRat(1, 2).Quo(params.GoalBonded)).Mul(params.InflationRateChange).Quo(hrsPerYrRat).Round(precision)}, + {"test 3", sdk.OneDec(), sdk.OneDec(), sdk.NewDecWithPrec(10, 2), + sdk.OneDec().Sub(sdk.NewDecWithPrec(5, 1).Quo(params.GoalBonded)).Mul(params.InflationRateChange).Quo(hrsPerYrDec)}, // test 7% minimum stop (testing with 100% bonded) - {"test 4", sdk.OneRat(), sdk.ZeroRat(), sdk.NewRat(7, 100), sdk.ZeroRat()}, - {"test 5", sdk.OneRat(), sdk.ZeroRat(), sdk.NewRat(70001, 1000000), sdk.NewRat(-1, 1000000)}, + {"test 4", sdk.OneDec(), sdk.ZeroDec(), sdk.NewDecWithPrec(7, 2), sdk.ZeroDec()}, + {"test 5", sdk.OneDec(), sdk.ZeroDec(), sdk.NewDecWithPrec(70001, 6), sdk.NewDecWithPrec(-1, 6)}, // test 20% maximum stop (testing with 0% bonded) - {"test 6", sdk.ZeroRat(), sdk.ZeroRat(), sdk.NewRat(20, 100), sdk.ZeroRat()}, - {"test 7", sdk.ZeroRat(), sdk.ZeroRat(), sdk.NewRat(199999, 1000000), sdk.NewRat(1, 1000000)}, + {"test 6", sdk.ZeroDec(), sdk.ZeroDec(), sdk.NewDecWithPrec(20, 2), sdk.ZeroDec()}, + {"test 7", sdk.ZeroDec(), sdk.ZeroDec(), sdk.NewDecWithPrec(199999, 6), sdk.NewDecWithPrec(1, 6)}, // perfect balance shouldn't change inflation - {"test 8", sdk.NewRat(67), sdk.NewRat(33), sdk.NewRat(15, 100), sdk.ZeroRat()}, + {"test 8", sdk.NewDec(67), sdk.NewDec(33), sdk.NewDecWithPrec(15, 2), sdk.ZeroDec()}, } for _, tc := range tests { pool.BondedTokens, pool.LooseTokens = tc.setBondedTokens, tc.setLooseTokens @@ -67,77 +67,80 @@ func TestProcessProvisions(t *testing.T) { var ( initialTotalTokens int64 = 550000000 - cumulativeExpProvs = sdk.ZeroRat() + cumulativeExpProvs = sdk.ZeroDec() ) - pool.LooseTokens = sdk.NewRat(initialTotalTokens) + pool.LooseTokens = sdk.NewDec(initialTotalTokens) // process the provisions for a year for hr := 0; hr < 100; hr++ { - var expProvisions sdk.Rat + var expProvisions sdk.Dec _, expProvisions, pool = updateProvisions(t, pool, params, hr) cumulativeExpProvs = cumulativeExpProvs.Add(expProvisions) } //get the pool and do the final value checks from checkFinalPoolValues - checkFinalPoolValues(t, pool, sdk.NewRat(initialTotalTokens), cumulativeExpProvs) + checkFinalPoolValues(t, pool, sdk.NewDec(initialTotalTokens), cumulativeExpProvs) } //_________________________________________________________________________________________ ////////////////////////////////HELPER FUNCTIONS BELOW///////////////////////////////////// // Final check on the global pool values for what the total tokens accumulated from each hour of provisions -func checkFinalPoolValues(t *testing.T, pool Pool, initialTotalTokens, cumulativeExpProvs sdk.Rat) { +func checkFinalPoolValues(t *testing.T, pool Pool, initialTotalTokens, cumulativeExpProvs sdk.Dec) { calculatedTotalTokens := initialTotalTokens.Add(cumulativeExpProvs) - require.True(sdk.RatEq(t, calculatedTotalTokens, pool.TokenSupply())) + require.True(sdk.DecEq(t, calculatedTotalTokens, pool.TokenSupply())) } // Processes provisions are added to the pool correctly every hour // Returns expected Provisions, expected Inflation, and pool, to help with cumulative calculations back in main Tests -func updateProvisions(t *testing.T, pool Pool, params Params, hr int) (sdk.Rat, sdk.Rat, Pool) { +func updateProvisions(t *testing.T, pool Pool, params Params, hr int) (sdk.Dec, sdk.Dec, Pool) { expInflation := pool.NextInflation(params) - expProvisions := expInflation.Mul(pool.TokenSupply().Round(precision)).Quo(hrsPerYrRat) + expProvisions := expInflation.Mul(pool.TokenSupply()).Quo(hrsPerYrDec) startTotalSupply := pool.TokenSupply() pool = pool.ProcessProvisions(params) //check provisions were added to pool - require.True(sdk.RatEq(t, startTotalSupply.Add(expProvisions), pool.TokenSupply())) + require.True(sdk.DecEq(t, startTotalSupply.Add(expProvisions), pool.TokenSupply())) return expInflation, expProvisions, pool } // Checks that The inflation will correctly increase or decrease after an update to the pool // nolint: gocyclo -func checkInflation(t *testing.T, pool Pool, previousInflation, updatedInflation sdk.Rat, msg string) { +func checkInflation(t *testing.T, pool Pool, previousInflation, updatedInflation sdk.Dec, msg string) { inflationChange := updatedInflation.Sub(previousInflation) switch { //BELOW 67% - Rate of change positive and increasing, while we are between 7% <= and < 20% inflation - case pool.BondedRatio().LT(sdk.NewRat(67, 100)) && updatedInflation.LT(sdk.NewRat(20, 100)): - require.Equal(t, true, inflationChange.GT(sdk.ZeroRat()), msg) + case pool.BondedRatio().LT(sdk.NewDecWithPrec(67, 2)) && updatedInflation.LT(sdk.NewDecWithPrec(20, 2)): + require.Equal(t, true, inflationChange.GT(sdk.ZeroDec()), msg) //BELOW 67% - Rate of change should be 0 while inflation continually stays at 20% until we reach 67% bonded ratio - case pool.BondedRatio().LT(sdk.NewRat(67, 100)) && updatedInflation.Equal(sdk.NewRat(20, 100)): - if previousInflation.Equal(sdk.NewRat(20, 100)) { + case pool.BondedRatio().LT(sdk.NewDecWithPrec(67, 2)) && updatedInflation.Equal(sdk.NewDecWithPrec(20, 2)): + if previousInflation.Equal(sdk.NewDecWithPrec(20, 2)) { require.Equal(t, true, inflationChange.IsZero(), msg) //This else statement covers the one off case where we first hit 20%, but we still needed a positive ROC to get to 67% bonded ratio (i.e. we went from 19.99999% to 20%) } else { - require.Equal(t, true, inflationChange.GT(sdk.ZeroRat()), msg) + require.Equal(t, true, inflationChange.GT(sdk.ZeroDec()), msg) } //ABOVE 67% - Rate of change should be negative while the bond is above 67, and should stay negative until we reach inflation of 7% - case pool.BondedRatio().GT(sdk.NewRat(67, 100)) && updatedInflation.LT(sdk.NewRat(20, 100)) && updatedInflation.GT(sdk.NewRat(7, 100)): - require.Equal(t, true, inflationChange.LT(sdk.ZeroRat()), msg) + case pool.BondedRatio().GT(sdk.NewDecWithPrec(67, 2)) && + updatedInflation.LT(sdk.NewDecWithPrec(20, 2)) && updatedInflation.GT(sdk.NewDecWithPrec(7, 2)): + require.Equal(t, true, inflationChange.LT(sdk.ZeroDec()), msg) //ABOVE 67% - Rate of change should be 0 while inflation continually stays at 7%. - case pool.BondedRatio().GT(sdk.NewRat(67, 100)) && updatedInflation.Equal(sdk.NewRat(7, 100)): - if previousInflation.Equal(sdk.NewRat(7, 100)) { + case pool.BondedRatio().GT(sdk.NewDecWithPrec(67, 2)) && + updatedInflation.Equal(sdk.NewDecWithPrec(7, 2)): + + if previousInflation.Equal(sdk.NewDecWithPrec(7, 2)) { require.Equal(t, true, inflationChange.IsZero(), msg) //This else statement covers the one off case where we first hit 7%, but we still needed a negative ROC to continue to get down to 67%. (i.e. we went from 7.00001% to 7%) } else { - require.Equal(t, true, inflationChange.LT(sdk.ZeroRat()), msg) + require.Equal(t, true, inflationChange.LT(sdk.ZeroDec()), msg) } } } diff --git a/x/stake/types/msg.go b/x/stake/types/msg.go index 27edad5dd..282000294 100644 --- a/x/stake/types/msg.go +++ b/x/stake/types/msg.go @@ -1,7 +1,6 @@ package types import ( - "math" "reflect" sdk "github.com/cosmos/cosmos-sdk/types" @@ -11,18 +10,11 @@ import ( // name to idetify transaction types const MsgType = "stake" -// Maximum amount of decimal points in the decimal representation of rationals -// used in MsgBeginUnbonding / MsgBeginRedelegate -const MaxBondDenominatorPrecision = 8 - // Verify interface at compile time var _, _, _ sdk.Msg = &MsgCreateValidator{}, &MsgEditValidator{}, &MsgDelegate{} var _, _ sdk.Msg = &MsgBeginUnbonding{}, &MsgCompleteUnbonding{} var _, _ sdk.Msg = &MsgBeginRedelegate{}, &MsgCompleteRedelegate{} -// Initialize Int for the denominator -var maximumBondingRationalDenominator = sdk.NewInt(int64(math.Pow10(MaxBondDenominatorPrecision))) - //______________________________________________________________________ // MsgCreateValidator - struct for unbonding transactions @@ -211,11 +203,11 @@ type MsgBeginRedelegate struct { DelegatorAddr sdk.AccAddress `json:"delegator_addr"` ValidatorSrcAddr sdk.AccAddress `json:"validator_src_addr"` ValidatorDstAddr sdk.AccAddress `json:"validator_dst_addr"` - SharesAmount sdk.Rat `json:"shares_amount"` + SharesAmount sdk.Dec `json:"shares_amount"` } func NewMsgBeginRedelegate(delegatorAddr, validatorSrcAddr, - validatorDstAddr sdk.AccAddress, sharesAmount sdk.Rat) MsgBeginRedelegate { + validatorDstAddr sdk.AccAddress, sharesAmount sdk.Dec) MsgBeginRedelegate { return MsgBeginRedelegate{ DelegatorAddr: delegatorAddr, @@ -261,12 +253,9 @@ func (msg MsgBeginRedelegate) ValidateBasic() sdk.Error { if msg.ValidatorDstAddr == nil { return ErrNilValidatorAddr(DefaultCodespace) } - if msg.SharesAmount.LTE(sdk.ZeroRat()) { + if msg.SharesAmount.LTE(sdk.ZeroDec()) { return ErrBadSharesAmount(DefaultCodespace) } - if msg.SharesAmount.Denom().GT(maximumBondingRationalDenominator) { - return ErrBadSharesPrecision(DefaultCodespace) - } return nil } @@ -322,10 +311,10 @@ func (msg MsgCompleteRedelegate) ValidateBasic() sdk.Error { type MsgBeginUnbonding struct { DelegatorAddr sdk.AccAddress `json:"delegator_addr"` ValidatorAddr sdk.AccAddress `json:"validator_addr"` - SharesAmount sdk.Rat `json:"shares_amount"` + SharesAmount sdk.Dec `json:"shares_amount"` } -func NewMsgBeginUnbonding(delegatorAddr, validatorAddr sdk.AccAddress, sharesAmount sdk.Rat) MsgBeginUnbonding { +func NewMsgBeginUnbonding(delegatorAddr, validatorAddr sdk.AccAddress, sharesAmount sdk.Dec) MsgBeginUnbonding { return MsgBeginUnbonding{ DelegatorAddr: delegatorAddr, ValidatorAddr: validatorAddr, @@ -362,12 +351,9 @@ func (msg MsgBeginUnbonding) ValidateBasic() sdk.Error { if msg.ValidatorAddr == nil { return ErrNilValidatorAddr(DefaultCodespace) } - if msg.SharesAmount.LTE(sdk.ZeroRat()) { + if msg.SharesAmount.LTE(sdk.ZeroDec()) { return ErrBadSharesAmount(DefaultCodespace) } - if msg.SharesAmount.Denom().GT(maximumBondingRationalDenominator) { - return ErrBadSharesPrecision(DefaultCodespace) - } return nil } diff --git a/x/stake/types/msg_test.go b/x/stake/types/msg_test.go index 82f92d9f3..2be34fb20 100644 --- a/x/stake/types/msg_test.go +++ b/x/stake/types/msg_test.go @@ -143,15 +143,15 @@ func TestMsgBeginRedelegate(t *testing.T) { delegatorAddr sdk.AccAddress validatorSrcAddr sdk.AccAddress validatorDstAddr sdk.AccAddress - sharesAmount sdk.Rat + sharesAmount sdk.Dec expectPass bool }{ - {"regular", addr1, addr2, addr3, sdk.NewRat(1, 10), true}, - {"negative decimal", addr1, addr2, addr3, sdk.NewRat(-1, 10), false}, - {"zero amount", addr1, addr2, addr3, sdk.ZeroRat(), false}, - {"empty delegator", emptyAddr, addr1, addr3, sdk.NewRat(1, 10), false}, - {"empty source validator", addr1, emptyAddr, addr3, sdk.NewRat(1, 10), false}, - {"empty destination validator", addr1, addr2, emptyAddr, sdk.NewRat(1, 10), false}, + {"regular", addr1, addr2, addr3, sdk.NewDecWithPrec(1, 1), true}, + {"negative decimal", addr1, addr2, addr3, sdk.NewDecWithPrec(-1, 1), false}, + {"zero amount", addr1, addr2, addr3, sdk.ZeroDec(), false}, + {"empty delegator", emptyAddr, addr1, addr3, sdk.NewDecWithPrec(1, 1), false}, + {"empty source validator", addr1, emptyAddr, addr3, sdk.NewDecWithPrec(1, 1), false}, + {"empty destination validator", addr1, addr2, emptyAddr, sdk.NewDecWithPrec(1, 1), false}, } for _, tc := range tests { @@ -195,14 +195,14 @@ func TestMsgBeginUnbonding(t *testing.T) { name string delegatorAddr sdk.AccAddress validatorAddr sdk.AccAddress - sharesAmount sdk.Rat + sharesAmount sdk.Dec expectPass bool }{ - {"regular", addr1, addr2, sdk.NewRat(1, 10), true}, - {"negative decimal", addr1, addr2, sdk.NewRat(-1, 10), false}, - {"zero amount", addr1, addr2, sdk.ZeroRat(), false}, - {"empty delegator", emptyAddr, addr1, sdk.NewRat(1, 10), false}, - {"empty validator", addr1, emptyAddr, sdk.NewRat(1, 10), false}, + {"regular", addr1, addr2, sdk.NewDecWithPrec(1, 1), true}, + {"negative decimal", addr1, addr2, sdk.NewDecWithPrec(-1, 1), false}, + {"zero amount", addr1, addr2, sdk.ZeroDec(), false}, + {"empty delegator", emptyAddr, addr1, sdk.NewDecWithPrec(1, 1), false}, + {"empty validator", addr1, emptyAddr, sdk.NewDecWithPrec(1, 1), false}, } for _, tc := range tests { diff --git a/x/stake/types/params.go b/x/stake/types/params.go index 0ae1ade09..f297f3105 100644 --- a/x/stake/types/params.go +++ b/x/stake/types/params.go @@ -13,10 +13,10 @@ const defaultUnbondingTime time.Duration = 60 * 60 * 24 * 3 * time.Second // Params defines the high level settings for staking type Params struct { - InflationRateChange sdk.Rat `json:"inflation_rate_change"` // maximum annual change in inflation rate - InflationMax sdk.Rat `json:"inflation_max"` // maximum inflation rate - InflationMin sdk.Rat `json:"inflation_min"` // minimum inflation rate - GoalBonded sdk.Rat `json:"goal_bonded"` // Goal of percent bonded atoms + InflationRateChange sdk.Dec `json:"inflation_rate_change"` // maximum annual change in inflation rate + InflationMax sdk.Dec `json:"inflation_max"` // maximum inflation rate + InflationMin sdk.Dec `json:"inflation_min"` // minimum inflation rate + GoalBonded sdk.Dec `json:"goal_bonded"` // Goal of percent bonded atoms UnbondingTime time.Duration `json:"unbonding_time"` @@ -34,10 +34,10 @@ func (p Params) Equal(p2 Params) bool { // DefaultParams returns a default set of parameters. func DefaultParams() Params { return Params{ - InflationRateChange: sdk.NewRat(13, 100), - InflationMax: sdk.NewRat(20, 100), - InflationMin: sdk.NewRat(7, 100), - GoalBonded: sdk.NewRat(67, 100), + InflationRateChange: sdk.NewDecWithPrec(13, 2), + InflationMax: sdk.NewDecWithPrec(20, 2), + InflationMin: sdk.NewDecWithPrec(7, 2), + GoalBonded: sdk.NewDecWithPrec(67, 2), UnbondingTime: defaultUnbondingTime, MaxValidators: 100, BondDenom: "steak", diff --git a/x/stake/types/pool.go b/x/stake/types/pool.go index 5aab4294b..d59c1ed25 100644 --- a/x/stake/types/pool.go +++ b/x/stake/types/pool.go @@ -10,15 +10,15 @@ import ( // Pool - dynamic parameters of the current state type Pool struct { - LooseTokens sdk.Rat `json:"loose_tokens"` // tokens which are not bonded in a validator - BondedTokens sdk.Rat `json:"bonded_tokens"` // reserve of bonded tokens - InflationLastTime time.Time `json:"inflation_last_time"` // block which the last inflation was processed - Inflation sdk.Rat `json:"inflation"` // current annual inflation rate + LooseTokens sdk.Dec `json:"loose_tokens"` // tokens which are not bonded in a validator + BondedTokens sdk.Dec `json:"bonded_tokens"` // reserve of bonded tokens + InflationLastTime time.Time `json:"inflation_last_time"` // block which the last inflation was processed // TODO make time + Inflation sdk.Dec `json:"inflation"` // current annual inflation rate DateLastCommissionReset int64 `json:"date_last_commission_reset"` // unix timestamp for last commission accounting reset (daily) // Fee Related - PrevBondedShares sdk.Rat `json:"prev_bonded_shares"` // last recorded bonded shares - for fee calculations + PrevBondedShares sdk.Dec `json:"prev_bonded_shares"` // last recorded bonded shares - for fee calculations } // nolint @@ -31,48 +31,48 @@ func (p Pool) Equal(p2 Pool) bool { // initial pool for testing func InitialPool() Pool { return Pool{ - LooseTokens: sdk.ZeroRat(), - BondedTokens: sdk.ZeroRat(), + LooseTokens: sdk.ZeroDec(), + BondedTokens: sdk.ZeroDec(), InflationLastTime: time.Unix(0, 0), - Inflation: sdk.NewRat(7, 100), + Inflation: sdk.NewDecWithPrec(7, 2), DateLastCommissionReset: 0, - PrevBondedShares: sdk.ZeroRat(), + PrevBondedShares: sdk.ZeroDec(), } } //____________________________________________________________________ // Sum total of all staking tokens in the pool -func (p Pool) TokenSupply() sdk.Rat { +func (p Pool) TokenSupply() sdk.Dec { return p.LooseTokens.Add(p.BondedTokens) } //____________________________________________________________________ // get the bond ratio of the global state -func (p Pool) BondedRatio() sdk.Rat { +func (p Pool) BondedRatio() sdk.Dec { supply := p.TokenSupply() - if supply.GT(sdk.ZeroRat()) { + if supply.GT(sdk.ZeroDec()) { return p.BondedTokens.Quo(supply) } - return sdk.ZeroRat() + return sdk.ZeroDec() } //_______________________________________________________________________ -func (p Pool) looseTokensToBonded(bondedTokens sdk.Rat) Pool { +func (p Pool) looseTokensToBonded(bondedTokens sdk.Dec) Pool { p.BondedTokens = p.BondedTokens.Add(bondedTokens) p.LooseTokens = p.LooseTokens.Sub(bondedTokens) - if p.LooseTokens.LT(sdk.ZeroRat()) { + if p.LooseTokens.LT(sdk.ZeroDec()) { panic(fmt.Sprintf("sanity check: loose tokens negative, pool: %v", p)) } return p } -func (p Pool) bondedTokensToLoose(bondedTokens sdk.Rat) Pool { +func (p Pool) bondedTokensToLoose(bondedTokens sdk.Dec) Pool { p.BondedTokens = p.BondedTokens.Sub(bondedTokens) p.LooseTokens = p.LooseTokens.Add(bondedTokens) - if p.BondedTokens.LT(sdk.ZeroRat()) { + if p.BondedTokens.LT(sdk.ZeroDec()) { panic(fmt.Sprintf("sanity check: bonded tokens negative, pool: %v", p)) } return p @@ -82,14 +82,14 @@ func (p Pool) bondedTokensToLoose(bondedTokens sdk.Rat) Pool { // Inflation const precision = 10000 // increased to this precision for accuracy -var hrsPerYrRat = sdk.NewRat(8766) // as defined by a julian year of 365.25 days +var hrsPerYrDec = sdk.NewDec(8766) // as defined by a julian year of 365.25 days // process provisions for an hour period func (p Pool) ProcessProvisions(params Params) Pool { p.Inflation = p.NextInflation(params) provisions := p.Inflation. - Mul(p.TokenSupply().Round(precision)). - Quo(hrsPerYrRat) + Mul(p.TokenSupply()). + Quo(hrsPerYrDec) // TODO add to the fees provisions p.LooseTokens = p.LooseTokens.Add(provisions) @@ -97,7 +97,7 @@ func (p Pool) ProcessProvisions(params Params) Pool { } // get the next inflation rate for the hour -func (p Pool) NextInflation(params Params) (inflation sdk.Rat) { +func (p Pool) NextInflation(params Params) (inflation sdk.Dec) { // The target annual inflation rate is recalculated for each previsions cycle. The // inflation is also subject to a rate change (positive or negative) depending on @@ -106,11 +106,11 @@ func (p Pool) NextInflation(params Params) (inflation sdk.Rat) { // 7% and 20%. // (1 - bondedRatio/GoalBonded) * InflationRateChange - inflationRateChangePerYear := sdk.OneRat(). - Sub(p.BondedRatio().Round(precision). + inflationRateChangePerYear := sdk.OneDec(). + Sub(p.BondedRatio(). Quo(params.GoalBonded)). Mul(params.InflationRateChange) - inflationRateChange := inflationRateChangePerYear.Quo(hrsPerYrRat) + inflationRateChange := inflationRateChangePerYear.Quo(hrsPerYrDec) // increase the new annual inflation for this next cycle inflation = p.Inflation.Add(inflationRateChange) @@ -121,5 +121,5 @@ func (p Pool) NextInflation(params Params) (inflation sdk.Rat) { inflation = params.InflationMin } - return inflation.Round(precision) + return inflation } diff --git a/x/stake/types/pool_test.go b/x/stake/types/pool_test.go index 43a2eac06..4541edd3d 100644 --- a/x/stake/types/pool_test.go +++ b/x/stake/types/pool_test.go @@ -11,28 +11,28 @@ func TestPoolEqual(t *testing.T) { p1 := InitialPool() p2 := InitialPool() require.True(t, p1.Equal(p2)) - p2.BondedTokens = sdk.NewRat(3) + p2.BondedTokens = sdk.NewDec(3) require.False(t, p1.Equal(p2)) } func TestAddBondedTokens(t *testing.T) { pool := InitialPool() - pool.LooseTokens = sdk.NewRat(10) - pool.BondedTokens = sdk.NewRat(10) + pool.LooseTokens = sdk.NewDec(10) + pool.BondedTokens = sdk.NewDec(10) - pool = pool.looseTokensToBonded(sdk.NewRat(10)) + pool = pool.looseTokensToBonded(sdk.NewDec(10)) - require.True(sdk.RatEq(t, sdk.NewRat(20), pool.BondedTokens)) - require.True(sdk.RatEq(t, sdk.NewRat(0), pool.LooseTokens)) + require.True(sdk.DecEq(t, sdk.NewDec(20), pool.BondedTokens)) + require.True(sdk.DecEq(t, sdk.NewDec(0), pool.LooseTokens)) } func TestRemoveBondedTokens(t *testing.T) { pool := InitialPool() - pool.LooseTokens = sdk.NewRat(10) - pool.BondedTokens = sdk.NewRat(10) + pool.LooseTokens = sdk.NewDec(10) + pool.BondedTokens = sdk.NewDec(10) - pool = pool.bondedTokensToLoose(sdk.NewRat(5)) + pool = pool.bondedTokensToLoose(sdk.NewDec(5)) - require.True(sdk.RatEq(t, sdk.NewRat(5), pool.BondedTokens)) - require.True(sdk.RatEq(t, sdk.NewRat(15), pool.LooseTokens)) + require.True(sdk.DecEq(t, sdk.NewDec(5), pool.BondedTokens)) + require.True(sdk.DecEq(t, sdk.NewDec(15), pool.LooseTokens)) } diff --git a/x/stake/types/validator.go b/x/stake/types/validator.go index 837b8f8e8..c9626b5ec 100644 --- a/x/stake/types/validator.go +++ b/x/stake/types/validator.go @@ -26,21 +26,21 @@ type Validator struct { Revoked bool `json:"revoked"` // has the validator been revoked from bonded status? Status sdk.BondStatus `json:"status"` // validator status (bonded/unbonding/unbonded) - Tokens sdk.Rat `json:"tokens"` // delegated tokens (incl. self-delegation) - DelegatorShares sdk.Rat `json:"delegator_shares"` // total shares issued to a validator's delegators + Tokens sdk.Dec `json:"tokens"` // delegated tokens (incl. self-delegation) + DelegatorShares sdk.Dec `json:"delegator_shares"` // total shares issued to a validator's delegators Description Description `json:"description"` // description terms for the validator BondHeight int64 `json:"bond_height"` // earliest height as a bonded validator BondIntraTxCounter int16 `json:"bond_intra_tx_counter"` // block-local tx index of validator change ProposerRewardPool sdk.Coins `json:"proposer_reward_pool"` // XXX reward pool collected from being the proposer - Commission sdk.Rat `json:"commission"` // XXX the commission rate of fees charged to any delegators - CommissionMax sdk.Rat `json:"commission_max"` // XXX maximum commission rate which this validator can ever charge - CommissionChangeRate sdk.Rat `json:"commission_change_rate"` // XXX maximum daily increase of the validator commission - CommissionChangeToday sdk.Rat `json:"commission_change_today"` // XXX commission rate change today, reset each day (UTC time) + Commission sdk.Dec `json:"commission"` // XXX the commission rate of fees charged to any delegators + CommissionMax sdk.Dec `json:"commission_max"` // XXX maximum commission rate which this validator can ever charge + CommissionChangeRate sdk.Dec `json:"commission_change_rate"` // XXX maximum daily increase of the validator commission + CommissionChangeToday sdk.Dec `json:"commission_change_today"` // XXX commission rate change today, reset each day (UTC time) // fee related - LastBondedTokens sdk.Rat `json:"prev_bonded_tokens"` // Previous bonded tokens held + LastBondedTokens sdk.Dec `json:"prev_bonded_tokens"` // Previous bonded tokens held } // NewValidator - initialize a new validator @@ -50,17 +50,17 @@ func NewValidator(owner sdk.AccAddress, pubKey crypto.PubKey, description Descri PubKey: pubKey, Revoked: false, Status: sdk.Unbonded, - Tokens: sdk.ZeroRat(), - DelegatorShares: sdk.ZeroRat(), + Tokens: sdk.ZeroDec(), + DelegatorShares: sdk.ZeroDec(), Description: description, BondHeight: int64(0), BondIntraTxCounter: int16(0), ProposerRewardPool: sdk.Coins{}, - Commission: sdk.ZeroRat(), - CommissionMax: sdk.ZeroRat(), - CommissionChangeRate: sdk.ZeroRat(), - CommissionChangeToday: sdk.ZeroRat(), - LastBondedTokens: sdk.ZeroRat(), + Commission: sdk.ZeroDec(), + CommissionMax: sdk.ZeroDec(), + CommissionChangeRate: sdk.ZeroDec(), + CommissionChangeToday: sdk.ZeroDec(), + LastBondedTokens: sdk.ZeroDec(), } } @@ -69,17 +69,17 @@ type validatorValue struct { PubKey crypto.PubKey Revoked bool Status sdk.BondStatus - Tokens sdk.Rat - DelegatorShares sdk.Rat + Tokens sdk.Dec + DelegatorShares sdk.Dec Description Description BondHeight int64 BondIntraTxCounter int16 ProposerRewardPool sdk.Coins - Commission sdk.Rat - CommissionMax sdk.Rat - CommissionChangeRate sdk.Rat - CommissionChangeToday sdk.Rat - LastBondedTokens sdk.Rat + Commission sdk.Dec + CommissionMax sdk.Dec + CommissionChangeRate sdk.Dec + CommissionChangeToday sdk.Dec + LastBondedTokens sdk.Dec } // return the redelegation without fields contained within the key for the store @@ -159,8 +159,8 @@ func (v Validator) HumanReadableString() (string, error) { resp += fmt.Sprintf("Validator: %s\n", bechVal) resp += fmt.Sprintf("Revoked: %v\n", v.Revoked) resp += fmt.Sprintf("Status: %s\n", sdk.BondStatusToString(v.Status)) - resp += fmt.Sprintf("Tokens: %s\n", v.Tokens.FloatString()) - resp += fmt.Sprintf("Delegator Shares: %s\n", v.DelegatorShares.FloatString()) + resp += fmt.Sprintf("Tokens: %s\n", v.Tokens.String()) + resp += fmt.Sprintf("Delegator Shares: %s\n", v.DelegatorShares.String()) resp += fmt.Sprintf("Description: %s\n", v.Description) resp += fmt.Sprintf("Bond Height: %d\n", v.BondHeight) resp += fmt.Sprintf("Proposer Reward Pool: %s\n", v.ProposerRewardPool.String()) @@ -182,21 +182,21 @@ type BechValidator struct { Revoked bool `json:"revoked"` // has the validator been revoked from bonded status? Status sdk.BondStatus `json:"status"` // validator status (bonded/unbonding/unbonded) - Tokens sdk.Rat `json:"tokens"` // delegated tokens (incl. self-delegation) - DelegatorShares sdk.Rat `json:"delegator_shares"` // total shares issued to a validator's delegators + Tokens sdk.Dec `json:"tokens"` // delegated tokens (incl. self-delegation) + DelegatorShares sdk.Dec `json:"delegator_shares"` // total shares issued to a validator's delegators Description Description `json:"description"` // description terms for the validator BondHeight int64 `json:"bond_height"` // earliest height as a bonded validator BondIntraTxCounter int16 `json:"bond_intra_tx_counter"` // block-local tx index of validator change ProposerRewardPool sdk.Coins `json:"proposer_reward_pool"` // XXX reward pool collected from being the proposer - Commission sdk.Rat `json:"commission"` // XXX the commission rate of fees charged to any delegators - CommissionMax sdk.Rat `json:"commission_max"` // XXX maximum commission rate which this validator can ever charge - CommissionChangeRate sdk.Rat `json:"commission_change_rate"` // XXX maximum daily increase of the validator commission - CommissionChangeToday sdk.Rat `json:"commission_change_today"` // XXX commission rate change today, reset each day (UTC time) + Commission sdk.Dec `json:"commission"` // XXX the commission rate of fees charged to any delegators + CommissionMax sdk.Dec `json:"commission_max"` // XXX maximum commission rate which this validator can ever charge + CommissionChangeRate sdk.Dec `json:"commission_change_rate"` // XXX maximum daily increase of the validator commission + CommissionChangeToday sdk.Dec `json:"commission_change_today"` // XXX commission rate change today, reset each day (UTC time) // fee related - LastBondedTokens sdk.Rat `json:"prev_bonded_shares"` // last bonded token amount + LastBondedTokens sdk.Dec `json:"prev_bonded_shares"` // last bonded token amount } // get the bech validator from the the regular validator @@ -364,7 +364,7 @@ func (v Validator) UpdateStatus(pool Pool, NewStatus sdk.BondStatus) (Validator, } // removes tokens from a validator -func (v Validator) RemoveTokens(pool Pool, tokens sdk.Rat) (Validator, Pool) { +func (v Validator) RemoveTokens(pool Pool, tokens sdk.Dec) (Validator, Pool) { if v.Status == sdk.Bonded { pool = pool.bondedTokensToLoose(tokens) } @@ -376,25 +376,25 @@ func (v Validator) RemoveTokens(pool Pool, tokens sdk.Rat) (Validator, Pool) { //_________________________________________________________________________________________________________ // AddTokensFromDel adds tokens to a validator -func (v Validator) AddTokensFromDel(pool Pool, amount int64) (Validator, Pool, sdk.Rat) { +func (v Validator) AddTokensFromDel(pool Pool, amount int64) (Validator, Pool, sdk.Dec) { // bondedShare/delegatedShare exRate := v.DelegatorShareExRate() - amountRat := sdk.NewRat(amount) + amountDec := sdk.NewDec(amount) if v.Status == sdk.Bonded { - pool = pool.looseTokensToBonded(amountRat) + pool = pool.looseTokensToBonded(amountDec) } - v.Tokens = v.Tokens.Add(amountRat) - issuedShares := amountRat.Quo(exRate) + v.Tokens = v.Tokens.Add(amountDec) + issuedShares := amountDec.Quo(exRate) v.DelegatorShares = v.DelegatorShares.Add(issuedShares) return v, pool, issuedShares } // RemoveDelShares removes delegator shares from a validator. -func (v Validator) RemoveDelShares(pool Pool, delShares sdk.Rat) (Validator, Pool, sdk.Rat) { +func (v Validator) RemoveDelShares(pool Pool, delShares sdk.Dec) (Validator, Pool, sdk.Dec) { issuedTokens := v.DelegatorShareExRate().Mul(delShares) v.Tokens = v.Tokens.Sub(issuedTokens) v.DelegatorShares = v.DelegatorShares.Sub(delShares) @@ -408,19 +408,19 @@ func (v Validator) RemoveDelShares(pool Pool, delShares sdk.Rat) (Validator, Poo // DelegatorShareExRate gets the exchange rate of tokens over delegator shares. // UNITS: tokens/delegator-shares -func (v Validator) DelegatorShareExRate() sdk.Rat { +func (v Validator) DelegatorShareExRate() sdk.Dec { if v.DelegatorShares.IsZero() { - return sdk.OneRat() + return sdk.OneDec() } return v.Tokens.Quo(v.DelegatorShares) } // Get the bonded tokens which the validator holds -func (v Validator) BondedTokens() sdk.Rat { +func (v Validator) BondedTokens() sdk.Dec { if v.Status == sdk.Bonded { return v.Tokens } - return sdk.ZeroRat() + return sdk.ZeroDec() } //______________________________________________________________________ @@ -434,7 +434,7 @@ func (v Validator) GetMoniker() string { return v.Description.Moniker } func (v Validator) GetStatus() sdk.BondStatus { return v.Status } func (v Validator) GetOwner() sdk.AccAddress { return v.Owner } func (v Validator) GetPubKey() crypto.PubKey { return v.PubKey } -func (v Validator) GetPower() sdk.Rat { return v.BondedTokens() } -func (v Validator) GetTokens() sdk.Rat { return v.Tokens } -func (v Validator) GetDelegatorShares() sdk.Rat { return v.DelegatorShares } +func (v Validator) GetPower() sdk.Dec { return v.BondedTokens() } +func (v Validator) GetTokens() sdk.Dec { return v.Tokens } +func (v Validator) GetDelegatorShares() sdk.Dec { return v.DelegatorShares } func (v Validator) GetBondHeight() int64 { return v.BondHeight } diff --git a/x/stake/types/validator_test.go b/x/stake/types/validator_test.go index f0dff4732..3981f5754 100644 --- a/x/stake/types/validator_test.go +++ b/x/stake/types/validator_test.go @@ -75,19 +75,19 @@ func TestRemoveTokens(t *testing.T) { Owner: addr1, PubKey: pk1, Status: sdk.Bonded, - Tokens: sdk.NewRat(100), - DelegatorShares: sdk.NewRat(100), + Tokens: sdk.NewDec(100), + DelegatorShares: sdk.NewDec(100), } pool := InitialPool() - pool.LooseTokens = sdk.NewRat(10) + pool.LooseTokens = sdk.NewDec(10) pool.BondedTokens = validator.BondedTokens() validator, pool = validator.UpdateStatus(pool, sdk.Bonded) require.Equal(t, sdk.Bonded, validator.Status) // remove tokens and test check everything - validator, pool = validator.RemoveTokens(pool, sdk.NewRat(10)) + validator, pool = validator.RemoveTokens(pool, sdk.NewDec(10)) require.Equal(t, int64(90), validator.Tokens.RoundInt64()) require.Equal(t, int64(90), pool.BondedTokens.RoundInt64()) require.Equal(t, int64(20), pool.LooseTokens.RoundInt64()) @@ -98,7 +98,7 @@ func TestRemoveTokens(t *testing.T) { require.Equal(t, int64(0), pool.BondedTokens.RoundInt64()) require.Equal(t, int64(110), pool.LooseTokens.RoundInt64()) - validator, pool = validator.RemoveTokens(pool, sdk.NewRat(10)) + validator, pool = validator.RemoveTokens(pool, sdk.NewDec(10)) require.Equal(t, int64(80), validator.Tokens.RoundInt64()) require.Equal(t, int64(0), pool.BondedTokens.RoundInt64()) require.Equal(t, int64(110), pool.LooseTokens.RoundInt64()) @@ -106,43 +106,43 @@ func TestRemoveTokens(t *testing.T) { func TestAddTokensValidatorBonded(t *testing.T) { pool := InitialPool() - pool.LooseTokens = sdk.NewRat(10) + pool.LooseTokens = sdk.NewDec(10) validator := NewValidator(addr1, pk1, Description{}) validator, pool = validator.UpdateStatus(pool, sdk.Bonded) validator, pool, delShares := validator.AddTokensFromDel(pool, 10) - require.Equal(t, sdk.OneRat(), validator.DelegatorShareExRate()) + require.Equal(t, sdk.OneDec(), validator.DelegatorShareExRate()) - assert.True(sdk.RatEq(t, sdk.NewRat(10), delShares)) - assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.BondedTokens())) + assert.True(sdk.DecEq(t, sdk.NewDec(10), delShares)) + assert.True(sdk.DecEq(t, sdk.NewDec(10), validator.BondedTokens())) } func TestAddTokensValidatorUnbonding(t *testing.T) { pool := InitialPool() - pool.LooseTokens = sdk.NewRat(10) + pool.LooseTokens = sdk.NewDec(10) validator := NewValidator(addr1, pk1, Description{}) validator, pool = validator.UpdateStatus(pool, sdk.Unbonding) validator, pool, delShares := validator.AddTokensFromDel(pool, 10) - require.Equal(t, sdk.OneRat(), validator.DelegatorShareExRate()) + require.Equal(t, sdk.OneDec(), validator.DelegatorShareExRate()) - assert.True(sdk.RatEq(t, sdk.NewRat(10), delShares)) + assert.True(sdk.DecEq(t, sdk.NewDec(10), delShares)) assert.Equal(t, sdk.Unbonding, validator.Status) - assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.Tokens)) + assert.True(sdk.DecEq(t, sdk.NewDec(10), validator.Tokens)) } func TestAddTokensValidatorUnbonded(t *testing.T) { pool := InitialPool() - pool.LooseTokens = sdk.NewRat(10) + pool.LooseTokens = sdk.NewDec(10) validator := NewValidator(addr1, pk1, Description{}) validator, pool = validator.UpdateStatus(pool, sdk.Unbonded) validator, pool, delShares := validator.AddTokensFromDel(pool, 10) - require.Equal(t, sdk.OneRat(), validator.DelegatorShareExRate()) + require.Equal(t, sdk.OneDec(), validator.DelegatorShareExRate()) - assert.True(sdk.RatEq(t, sdk.NewRat(10), delShares)) + assert.True(sdk.DecEq(t, sdk.NewDec(10), delShares)) assert.Equal(t, sdk.Unbonded, validator.Status) - assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.Tokens)) + assert.True(sdk.DecEq(t, sdk.NewDec(10), validator.Tokens)) } // TODO refactor to make simpler like the AddToken tests above @@ -151,16 +151,16 @@ func TestRemoveDelShares(t *testing.T) { Owner: addr1, PubKey: pk1, Status: sdk.Bonded, - Tokens: sdk.NewRat(100), - DelegatorShares: sdk.NewRat(100), + Tokens: sdk.NewDec(100), + DelegatorShares: sdk.NewDec(100), } poolA := InitialPool() - poolA.LooseTokens = sdk.NewRat(10) + poolA.LooseTokens = sdk.NewDec(10) poolA.BondedTokens = valA.BondedTokens() - require.Equal(t, valA.DelegatorShareExRate(), sdk.OneRat()) + require.Equal(t, valA.DelegatorShareExRate(), sdk.OneDec()) // Remove delegator shares - valB, poolB, coinsB := valA.RemoveDelShares(poolA, sdk.NewRat(10)) + valB, poolB, coinsB := valA.RemoveDelShares(poolA, sdk.NewDec(10)) assert.Equal(t, int64(10), coinsB.RoundInt64()) assert.Equal(t, int64(90), valB.DelegatorShares.RoundInt64()) assert.Equal(t, int64(90), valB.BondedTokens().RoundInt64()) @@ -168,13 +168,13 @@ func TestRemoveDelShares(t *testing.T) { assert.Equal(t, int64(20), poolB.LooseTokens.RoundInt64()) // conservation of tokens - require.True(sdk.RatEq(t, + require.True(sdk.DecEq(t, poolB.LooseTokens.Add(poolB.BondedTokens), poolA.LooseTokens.Add(poolA.BondedTokens))) // specific case from random tests - poolTokens := sdk.NewRat(5102) - delShares := sdk.NewRat(115) + poolTokens := sdk.NewDec(5102) + delShares := sdk.NewDec(115) validator := Validator{ Owner: addr1, PubKey: pk1, @@ -183,22 +183,27 @@ func TestRemoveDelShares(t *testing.T) { DelegatorShares: delShares, } pool := Pool{ - BondedTokens: sdk.NewRat(248305), - LooseTokens: sdk.NewRat(232147), + BondedTokens: sdk.NewDec(248305), + LooseTokens: sdk.NewDec(232147), InflationLastTime: time.Unix(0, 0), - Inflation: sdk.NewRat(7, 100), + Inflation: sdk.NewDecWithPrec(7, 2), } - shares := sdk.NewRat(29) + shares := sdk.NewDec(29) _, newPool, tokens := validator.RemoveDelShares(pool, shares) - require.True(sdk.RatEq(t, sdk.NewRat(147958, 115), tokens)) - require.True(sdk.RatEq(t, + + exp, err := sdk.NewDecFromStr("1286.5913043477") + require.NoError(t, err) + + require.True(sdk.DecEq(t, exp, tokens)) + + require.True(sdk.DecEq(t, newPool.LooseTokens.Add(newPool.BondedTokens), pool.LooseTokens.Add(pool.BondedTokens))) } func TestUpdateStatus(t *testing.T) { pool := InitialPool() - pool.LooseTokens = sdk.NewRat(100) + pool.LooseTokens = sdk.NewDec(100) validator := NewValidator(addr1, pk1, Description{}) validator, pool, _ = validator.AddTokensFromDel(pool, 100) @@ -221,8 +226,8 @@ func TestUpdateStatus(t *testing.T) { } func TestPossibleOverflow(t *testing.T) { - poolTokens := sdk.NewRat(2159) - delShares := sdk.NewRat(391432570689183511).Quo(sdk.NewRat(40113011844664)) + poolTokens := sdk.NewDec(2159) + delShares := sdk.NewDec(391432570689183511).Quo(sdk.NewDec(40113011844664)) validator := Validator{ Owner: addr1, PubKey: pk1, @@ -231,17 +236,17 @@ func TestPossibleOverflow(t *testing.T) { DelegatorShares: delShares, } pool := Pool{ - LooseTokens: sdk.NewRat(100), + LooseTokens: sdk.NewDec(100), BondedTokens: poolTokens, InflationLastTime: time.Unix(0, 0), - Inflation: sdk.NewRat(7, 100), + Inflation: sdk.NewDecWithPrec(7, 2), } tokens := int64(71) msg := fmt.Sprintf("validator %#v", validator) newValidator, _, _ := validator.AddTokensFromDel(pool, tokens) msg = fmt.Sprintf("Added %d tokens to %s", tokens, msg) - require.False(t, newValidator.DelegatorShareExRate().LT(sdk.ZeroRat()), + require.False(t, newValidator.DelegatorShareExRate().LT(sdk.ZeroDec()), "Applying operation \"%s\" resulted in negative DelegatorShareExRate(): %v", msg, newValidator.DelegatorShareExRate()) }