diff --git a/CHANGELOG.md b/CHANGELOG.md index 53e2ed3b6a96..fe01959fd581 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -184,6 +184,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (x/mint) [#18283](https://github.com/cosmos/cosmos-sdk/pull/18283) Mint module was moved to its own go.mod `cosmossdk.io/x/mint` * (x/consensus) [#18041](https://github.com/cosmos/cosmos-sdk/pull/18041) `ToProtoConsensusParams()` returns an error * (x/slashing) [#18115](https://github.com/cosmos/cosmos-sdk/pull/18115) `NewValidatorSigningInfo` takes strings instead of `sdk.AccAddress` +* (types) [#18268](https://github.com/cosmos/cosmos-sdk/pull/18268) Remove global setting of basedenom. Use the staking module parameter instead ### CLI Breaking Changes diff --git a/types/coin.go b/types/coin.go index fbaf006d6a33..87847800f755 100644 --- a/types/coin.go +++ b/types/coin.go @@ -881,7 +881,8 @@ func ParseCoinNormalized(coinStr string) (coin Coin, err error) { return Coin{}, err } - coin, _ = NormalizeDecCoin(decCoin).TruncateDecimal() + coin, _ = NewDecCoinFromDec(decCoin.Denom, decCoin.Amount).TruncateDecimal() + return coin, nil } @@ -900,3 +901,20 @@ func ParseCoinsNormalized(coinStr string) (Coins, error) { } return NormalizeCoins(coins), nil } + +// ---------------------------------------------------------------------------- + +// NormalizeCoins normalize and truncate a list of decimal coins +func NormalizeCoins(coins []DecCoin) Coins { + if coins == nil { + return nil + } + result := make([]Coin, 0, len(coins)) + + for _, coin := range coins { + newCoin, _ := NewDecCoinFromDec(coin.Denom, coin.Amount).TruncateDecimal() + result = append(result, newCoin) + } + + return result +} diff --git a/types/denom.go b/types/denom.go deleted file mode 100644 index 5cd0910ad44f..000000000000 --- a/types/denom.go +++ /dev/null @@ -1,160 +0,0 @@ -package types - -import ( - "fmt" - - "cosmossdk.io/math" -) - -// denomUnits contains a mapping of denomination mapped to their respective unit -// multipliers (e.g. 1atom = 10^-6uatom). -var denomUnits = map[string]math.LegacyDec{} - -// baseDenom is the denom of smallest unit registered -var baseDenom string - -// RegisterDenom registers a denomination with a corresponding unit. If the -// denomination is already registered, an error will be returned. -func RegisterDenom(denom string, unit math.LegacyDec) error { - if err := ValidateDenom(denom); err != nil { - return err - } - - if _, ok := denomUnits[denom]; ok { - return fmt.Errorf("denom %s already registered", denom) - } - - denomUnits[denom] = unit - - if baseDenom == "" || unit.LT(denomUnits[baseDenom]) { - baseDenom = denom - } - return nil -} - -// GetDenomUnit returns a unit for a given denomination if it exists. A boolean -// is returned if the denomination is registered. -func GetDenomUnit(denom string) (math.LegacyDec, bool) { - if err := ValidateDenom(denom); err != nil { - return math.LegacyZeroDec(), false - } - - unit, ok := denomUnits[denom] - if !ok { - return math.LegacyZeroDec(), false - } - - return unit, true -} - -// SetBaseDenom allow overwritting the base denom -// if the denom has registered before, otherwise return error -func SetBaseDenom(denom string) error { - _, ok := denomUnits[denom] - if !ok { - return fmt.Errorf("denom %s not registered", denom) - } - baseDenom = denom - return nil -} - -// GetBaseDenom returns the denom of smallest unit registered -func GetBaseDenom() (string, error) { - if baseDenom == "" { - return "", fmt.Errorf("no denom is registered") - } - return baseDenom, nil -} - -// ConvertCoin attempts to convert a coin to a given denomination. If the given -// denomination is invalid or if neither denomination is registered, an error -// is returned. -func ConvertCoin(coin Coin, denom string) (Coin, error) { - if err := ValidateDenom(denom); err != nil { - return Coin{}, err - } - - srcUnit, ok := GetDenomUnit(coin.Denom) - if !ok { - return Coin{}, fmt.Errorf("source denom not registered: %s", coin.Denom) - } - - dstUnit, ok := GetDenomUnit(denom) - if !ok { - return Coin{}, fmt.Errorf("destination denom not registered: %s", denom) - } - - if srcUnit.Equal(dstUnit) { - return NewCoin(denom, coin.Amount), nil - } - - return NewCoin(denom, math.LegacyNewDecFromInt(coin.Amount).Mul(srcUnit).Quo(dstUnit).TruncateInt()), nil -} - -// ConvertDecCoin attempts to convert a decimal coin to a given denomination. If the given -// denomination is invalid or if neither denomination is registered, an error -// is returned. -func ConvertDecCoin(coin DecCoin, denom string) (DecCoin, error) { - if err := ValidateDenom(denom); err != nil { - return DecCoin{}, err - } - - srcUnit, ok := GetDenomUnit(coin.Denom) - if !ok { - return DecCoin{}, fmt.Errorf("source denom not registered: %s", coin.Denom) - } - - dstUnit, ok := GetDenomUnit(denom) - if !ok { - return DecCoin{}, fmt.Errorf("destination denom not registered: %s", denom) - } - - if srcUnit.Equal(dstUnit) { - return NewDecCoinFromDec(denom, coin.Amount), nil - } - - return NewDecCoinFromDec(denom, coin.Amount.Mul(srcUnit).Quo(dstUnit)), nil -} - -// NormalizeCoin try to convert a coin to the smallest unit registered, -// returns original one if failed. -func NormalizeCoin(coin Coin) Coin { - base, err := GetBaseDenom() - if err != nil { - return coin - } - newCoin, err := ConvertCoin(coin, base) - if err != nil { - return coin - } - return newCoin -} - -// NormalizeDecCoin try to convert a decimal coin to the smallest unit registered, -// returns original one if failed. -func NormalizeDecCoin(coin DecCoin) DecCoin { - base, err := GetBaseDenom() - if err != nil { - return coin - } - newCoin, err := ConvertDecCoin(coin, base) - if err != nil { - return coin - } - return newCoin -} - -// NormalizeCoins normalize and truncate a list of decimal coins -func NormalizeCoins(coins []DecCoin) Coins { - if coins == nil { - return nil - } - result := make([]Coin, 0, len(coins)) - - for _, coin := range coins { - newCoin, _ := NormalizeDecCoin(coin).TruncateDecimal() - result = append(result, newCoin) - } - - return result -} diff --git a/types/denom_internal_test.go b/types/denom_internal_test.go deleted file mode 100644 index a22c3a211540..000000000000 --- a/types/denom_internal_test.go +++ /dev/null @@ -1,211 +0,0 @@ -package types - -import ( - "testing" - - "github.com/stretchr/testify/suite" - - "cosmossdk.io/math" -) - -var ( - atom = "atom" // 1 (base denom unit) - matom = "matom" // 10^-3 (milli) - uatom = "uatom" // 10^-6 (micro) - natom = "natom" // 10^-9 (nano) -) - -type internalDenomTestSuite struct { - suite.Suite -} - -func TestInternalDenomTestSuite(t *testing.T) { - suite.Run(t, new(internalDenomTestSuite)) -} - -func (s *internalDenomTestSuite) TestRegisterDenom() { - atomUnit := math.LegacyOneDec() // 1 (base denom unit) - - s.Require().NoError(RegisterDenom(atom, atomUnit)) - s.Require().Error(RegisterDenom(atom, atomUnit)) - - res, ok := GetDenomUnit(atom) - s.Require().True(ok) - s.Require().Equal(atomUnit, res) - - res, ok = GetDenomUnit(matom) - s.Require().False(ok) - s.Require().Equal(math.LegacyZeroDec(), res) - - err := SetBaseDenom(atom) - s.Require().NoError(err) - - res, ok = GetDenomUnit(atom) - s.Require().True(ok) - s.Require().Equal(atomUnit, res) - - // reset registration - baseDenom = "" - denomUnits = map[string]math.LegacyDec{} -} - -func (s *internalDenomTestSuite) TestConvertCoins() { - atomUnit := math.LegacyOneDec() // 1 (base denom unit) - s.Require().NoError(RegisterDenom(atom, atomUnit)) - - matomUnit := math.LegacyNewDecWithPrec(1, 3) // 10^-3 (milli) - s.Require().NoError(RegisterDenom(matom, matomUnit)) - - uatomUnit := math.LegacyNewDecWithPrec(1, 6) // 10^-6 (micro) - s.Require().NoError(RegisterDenom(uatom, uatomUnit)) - - natomUnit := math.LegacyNewDecWithPrec(1, 9) // 10^-9 (nano) - s.Require().NoError(RegisterDenom(natom, natomUnit)) - - res, err := GetBaseDenom() - s.Require().NoError(err) - s.Require().Equal(res, natom) - s.Require().Equal(NormalizeCoin(NewCoin(uatom, math.NewInt(1))), NewCoin(natom, math.NewInt(1000))) - s.Require().Equal(NormalizeCoin(NewCoin(matom, math.NewInt(1))), NewCoin(natom, math.NewInt(1000000))) - s.Require().Equal(NormalizeCoin(NewCoin(atom, math.NewInt(1))), NewCoin(natom, math.NewInt(1000000000))) - - coins, err := ParseCoinsNormalized("1atom,1matom,1uatom") - s.Require().NoError(err) - s.Require().Equal(coins, Coins{ - Coin{natom, math.NewInt(1000000000)}, - Coin{natom, math.NewInt(1000000)}, - Coin{natom, math.NewInt(1000)}, - }) - - testCases := []struct { - input Coin - denom string - result Coin - expErr bool - }{ - {NewCoin("foo", math.ZeroInt()), atom, Coin{}, true}, - {NewCoin(atom, math.ZeroInt()), "foo", Coin{}, true}, - {NewCoin(atom, math.ZeroInt()), "FOO", Coin{}, true}, - - {NewCoin(atom, math.NewInt(5)), matom, NewCoin(matom, math.NewInt(5000)), false}, // atom => matom - {NewCoin(atom, math.NewInt(5)), uatom, NewCoin(uatom, math.NewInt(5000000)), false}, // atom => uatom - {NewCoin(atom, math.NewInt(5)), natom, NewCoin(natom, math.NewInt(5000000000)), false}, // atom => natom - - {NewCoin(uatom, math.NewInt(5000000)), matom, NewCoin(matom, math.NewInt(5000)), false}, // uatom => matom - {NewCoin(uatom, math.NewInt(5000000)), natom, NewCoin(natom, math.NewInt(5000000000)), false}, // uatom => natom - {NewCoin(uatom, math.NewInt(5000000)), atom, NewCoin(atom, math.NewInt(5)), false}, // uatom => atom - - {NewCoin(matom, math.NewInt(5000)), natom, NewCoin(natom, math.NewInt(5000000000)), false}, // matom => natom - {NewCoin(matom, math.NewInt(5000)), uatom, NewCoin(uatom, math.NewInt(5000000)), false}, // matom => uatom - } - - for i, tc := range testCases { - res, err := ConvertCoin(tc.input, tc.denom) - s.Require().Equal( - tc.expErr, err != nil, - "unexpected error; tc: #%d, input: %s, denom: %s", i+1, tc.input, tc.denom, - ) - s.Require().Equal( - tc.result, res, - "invalid result; tc: #%d, input: %s, denom: %s", i+1, tc.input, tc.denom, - ) - } - - // reset registration - baseDenom = "" - denomUnits = map[string]math.LegacyDec{} -} - -func (s *internalDenomTestSuite) TestConvertDecCoins() { - atomUnit := math.LegacyOneDec() // 1 (base denom unit) - s.Require().NoError(RegisterDenom(atom, atomUnit)) - - matomUnit := math.LegacyNewDecWithPrec(1, 3) // 10^-3 (milli) - s.Require().NoError(RegisterDenom(matom, matomUnit)) - - uatomUnit := math.LegacyNewDecWithPrec(1, 6) // 10^-6 (micro) - s.Require().NoError(RegisterDenom(uatom, uatomUnit)) - - natomUnit := math.LegacyNewDecWithPrec(1, 9) // 10^-9 (nano) - s.Require().NoError(RegisterDenom(natom, natomUnit)) - - res, err := GetBaseDenom() - s.Require().NoError(err) - s.Require().Equal(res, natom) - s.Require().Equal(NormalizeDecCoin(NewDecCoin(uatom, math.NewInt(1))), NewDecCoin(natom, math.NewInt(1000))) - s.Require().Equal(NormalizeDecCoin(NewDecCoin(matom, math.NewInt(1))), NewDecCoin(natom, math.NewInt(1000000))) - s.Require().Equal(NormalizeDecCoin(NewDecCoin(atom, math.NewInt(1))), NewDecCoin(natom, math.NewInt(1000000000))) - - coins, err := ParseCoinsNormalized("0.1atom,0.1matom,0.1uatom") - s.Require().NoError(err) - s.Require().Equal(coins, Coins{ - Coin{natom, math.NewInt(100000000)}, - Coin{natom, math.NewInt(100000)}, - Coin{natom, math.NewInt(100)}, - }) - - testCases := []struct { - input DecCoin - denom string - result DecCoin - expErr bool - }{ - {NewDecCoin("foo", math.ZeroInt()), atom, DecCoin{}, true}, - {NewDecCoin(atom, math.ZeroInt()), "foo", DecCoin{}, true}, - {NewDecCoin(atom, math.ZeroInt()), "FOO", DecCoin{}, true}, - - // 0.5atom - {NewDecCoinFromDec(atom, math.LegacyNewDecWithPrec(5, 1)), matom, NewDecCoin(matom, math.NewInt(500)), false}, // atom => matom - {NewDecCoinFromDec(atom, math.LegacyNewDecWithPrec(5, 1)), uatom, NewDecCoin(uatom, math.NewInt(500000)), false}, // atom => uatom - {NewDecCoinFromDec(atom, math.LegacyNewDecWithPrec(5, 1)), natom, NewDecCoin(natom, math.NewInt(500000000)), false}, // atom => natom - - {NewDecCoin(uatom, math.NewInt(5000000)), matom, NewDecCoin(matom, math.NewInt(5000)), false}, // uatom => matom - {NewDecCoin(uatom, math.NewInt(5000000)), natom, NewDecCoin(natom, math.NewInt(5000000000)), false}, // uatom => natom - {NewDecCoin(uatom, math.NewInt(5000000)), atom, NewDecCoin(atom, math.NewInt(5)), false}, // uatom => atom - - {NewDecCoin(matom, math.NewInt(5000)), natom, NewDecCoin(natom, math.NewInt(5000000000)), false}, // matom => natom - {NewDecCoin(matom, math.NewInt(5000)), uatom, NewDecCoin(uatom, math.NewInt(5000000)), false}, // matom => uatom - } - - for i, tc := range testCases { - res, err := ConvertDecCoin(tc.input, tc.denom) - s.Require().Equal( - tc.expErr, err != nil, - "unexpected error; tc: #%d, input: %s, denom: %s", i+1, tc.input, tc.denom, - ) - s.Require().Equal( - tc.result, res, - "invalid result; tc: #%d, input: %s, denom: %s", i+1, tc.input, tc.denom, - ) - } - - // reset registration - baseDenom = "" - denomUnits = map[string]math.LegacyDec{} -} - -func (s *internalDenomTestSuite) TestDecOperationOrder() { - dec, err := math.LegacyNewDecFromStr("11") - s.Require().NoError(err) - s.Require().NoError(RegisterDenom("unit1", dec)) - dec, err = math.LegacyNewDecFromStr("100000011") - s.Require().NoError(err) - s.Require().NoError(RegisterDenom("unit2", dec)) - - coin, err := ConvertCoin(NewCoin("unit1", math.NewInt(100000011)), "unit2") - s.Require().NoError(err) - s.Require().Equal(coin, NewCoin("unit2", math.NewInt(11))) - - // reset registration - baseDenom = "" - denomUnits = map[string]math.LegacyDec{} -} - -func (s *internalDenomTestSuite) TestSetBaseDenomError() { - err := SetBaseDenom(atom) - s.Require().Error(err) - - // reset registration - baseDenom = "" - denomUnits = map[string]math.LegacyDec{} -} diff --git a/x/distribution/keeper/delegation.go b/x/distribution/keeper/delegation.go index a80064a03a46..a107f35d7c93 100644 --- a/x/distribution/keeper/delegation.go +++ b/x/distribution/keeper/delegation.go @@ -301,9 +301,9 @@ func (k Keeper) withdrawDelegationRewards(ctx context.Context, val stakingtypes. } if finalRewards.IsZero() { - baseDenom, _ := sdk.GetBaseDenom() - if baseDenom == "" { - baseDenom = sdk.DefaultBondDenom + baseDenom, err := k.stakingKeeper.BondDenom(ctx) + if err != nil { + return nil, err } // Note, we do not call the NewCoins constructor as we do not want the zero diff --git a/x/distribution/keeper/delegation_test.go b/x/distribution/keeper/delegation_test.go index 8a17df9eaa94..828bc16aff11 100644 --- a/x/distribution/keeper/delegation_test.go +++ b/x/distribution/keeper/delegation_test.go @@ -1006,6 +1006,7 @@ func Test100PercentCommissionReward(t *testing.T) { accountKeeper.EXPECT().GetModuleAddress("distribution").Return(distrAcc.GetAddress()) stakingKeeper.EXPECT().ValidatorAddressCodec().Return(address.NewBech32Codec(sdk.Bech32PrefixValAddr)).AnyTimes() + stakingKeeper.EXPECT().BondDenom(gomock.Any()).Return("stake", nil).AnyTimes() accountKeeper.EXPECT().AddressCodec().Return(address.NewBech32Codec(sdk.Bech32MainPrefix)).AnyTimes() distrKeeper := keeper.NewKeeper( diff --git a/x/distribution/testutil/expected_keepers_mocks.go b/x/distribution/testutil/expected_keepers_mocks.go index 111bc8056aff..f57b9aca571b 100644 --- a/x/distribution/testutil/expected_keepers_mocks.go +++ b/x/distribution/testutil/expected_keepers_mocks.go @@ -315,6 +315,21 @@ func (m *MockStakingKeeper) EXPECT() *MockStakingKeeperMockRecorder { return m.recorder } +// BondDenom mocks base method. +func (m *MockStakingKeeper) BondDenom(ctx context.Context) (string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "BondDenom", ctx) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// BondDenom indicates an expected call of BondDenom. +func (mr *MockStakingKeeperMockRecorder) BondDenom(ctx interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BondDenom", reflect.TypeOf((*MockStakingKeeper)(nil).BondDenom), ctx) +} + // ConsensusAddressCodec mocks base method. func (m *MockStakingKeeper) ConsensusAddressCodec() address.Codec { m.ctrl.T.Helper() diff --git a/x/distribution/types/expected_keepers.go b/x/distribution/types/expected_keepers.go index c0d332000aee..6b0c7ffc391a 100644 --- a/x/distribution/types/expected_keepers.go +++ b/x/distribution/types/expected_keepers.go @@ -44,6 +44,8 @@ type PoolKeeper interface { type StakingKeeper interface { ValidatorAddressCodec() address.Codec ConsensusAddressCodec() address.Codec + BondDenom(ctx context.Context) (string, error) + // iterate through validators by operator address, execute func for each validator IterateValidators(context.Context, func(index int64, validator stakingtypes.ValidatorI) (stop bool)) error