From 55edb3c2e0624ca1f9ebfab4e74c6e05be4c027f Mon Sep 17 00:00:00 2001 From: dzungdo Date: Thu, 18 Jan 2024 22:04:01 +0700 Subject: [PATCH 01/11] add some initial tests --- testutil/address.go | 5 + x/multi-staking/keeper/keeper_test.go | 3 +- x/multi-staking/keeper/msg_server_test.go | 936 ++++++++++++++++++++++ 3 files changed, 943 insertions(+), 1 deletion(-) create mode 100644 x/multi-staking/keeper/msg_server_test.go diff --git a/testutil/address.go b/testutil/address.go index 51b22a2a..75222fe4 100644 --- a/testutil/address.go +++ b/testutil/address.go @@ -2,9 +2,14 @@ package testutil import ( "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" sdk "github.com/cosmos/cosmos-sdk/types" ) +func GenPubKey() cryptotypes.PubKey { + return secp256k1.GenPrivKey().PubKey() +} + func GenAddress() sdk.AccAddress { priv := secp256k1.GenPrivKey() diff --git a/x/multi-staking/keeper/keeper_test.go b/x/multi-staking/keeper/keeper_test.go index 06a86601..c699e9f7 100644 --- a/x/multi-staking/keeper/keeper_test.go +++ b/x/multi-staking/keeper/keeper_test.go @@ -14,6 +14,7 @@ import ( type KeeperTestSuite struct { suite.Suite + app *simapp.SimApp ctx sdk.Context msKeeper *multistakingkeeper.Keeper } @@ -22,7 +23,7 @@ func (suite *KeeperTestSuite) SetupTest() { app := simapp.Setup(false) ctx := app.BaseApp.NewContext(false, tmproto.Header{}) - suite.ctx, suite.msKeeper = ctx, &app.MultiStakingKeeper + suite.app, suite.ctx, suite.msKeeper = app, ctx, &app.MultiStakingKeeper } func TestKeeperTestSuite(t *testing.T) { diff --git a/x/multi-staking/keeper/msg_server_test.go b/x/multi-staking/keeper/msg_server_test.go new file mode 100644 index 00000000..0306261d --- /dev/null +++ b/x/multi-staking/keeper/msg_server_test.go @@ -0,0 +1,936 @@ +package keeper_test + +import ( + "time" + + "cosmossdk.io/math" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + + "github.com/realio-tech/multi-staking-module/testing/simapp" + "github.com/realio-tech/multi-staking-module/testutil" + + multistakingkeeper "github.com/realio-tech/multi-staking-module/x/multi-staking/keeper" + multistakingtypes "github.com/realio-tech/multi-staking-module/x/multi-staking/types" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" +) + +var ( + MultiStakingDenomA = "ario" + MultiStakingDenomB = "arst" +) + +func (suite *KeeperTestSuite) TestCreateValidator() { + delAddr := testutil.GenAddress() + valPubKey := testutil.GenPubKey() + valAddr := sdk.ValAddress(valPubKey.Address()) + + testCases := []struct { + name string + malleate func(ctx sdk.Context, msKeeper *multistakingkeeper.Keeper, msgServer stakingtypes.MsgServer) (sdk.Coin, error) + expOut sdk.Coin + expErr bool + }{ + { + name: "3001 token, weight 0.3, expect 900", + malleate: func(ctx sdk.Context, msKeeper *multistakingkeeper.Keeper, msgServer stakingtypes.MsgServer) (sdk.Coin, error) { + msKeeper.SetBondWeight(ctx, MultiStakingDenomA, sdk.MustNewDecFromStr("0.3")) + bondAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(3001)) + msg := stakingtypes.MsgCreateValidator{ + Description: stakingtypes.Description{ + Moniker: "test", + Identity: "test", + Website: "test", + SecurityContact: "test", + Details: "test", + }, + Commission: stakingtypes.CommissionRates{ + Rate: sdk.MustNewDecFromStr("0.05"), + MaxRate: sdk.MustNewDecFromStr("0.1"), + MaxChangeRate: sdk.MustNewDecFromStr("0.05"), + }, + MinSelfDelegation: sdk.NewInt(1), + DelegatorAddress: delAddr.String(), + ValidatorAddress: valAddr.String(), + Pubkey: codectypes.UnsafePackAny(valPubKey), + Value: bondAmount, + } + + _, err := msgServer.CreateValidator(ctx, &msg) + return bondAmount, err + }, + expOut: sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(900)), + expErr: false, + }, + { + name: "25 token, weight 0.5, expect 12", + malleate: func(ctx sdk.Context, msKeeper *multistakingkeeper.Keeper, msgServer stakingtypes.MsgServer) (sdk.Coin, error) { + msKeeper.SetBondWeight(ctx, MultiStakingDenomB, sdk.MustNewDecFromStr("0.5")) + bondAmount := sdk.NewCoin(MultiStakingDenomB, sdk.NewInt(25)) + + msg := stakingtypes.MsgCreateValidator{ + Description: stakingtypes.Description{ + Moniker: "test", + Identity: "test", + Website: "test", + SecurityContact: "test", + Details: "test", + }, + Commission: stakingtypes.CommissionRates{ + Rate: sdk.MustNewDecFromStr("0.05"), + MaxRate: sdk.MustNewDecFromStr("0.1"), + MaxChangeRate: sdk.MustNewDecFromStr("0.05"), + }, + MinSelfDelegation: sdk.NewInt(1), + DelegatorAddress: delAddr.String(), + ValidatorAddress: valAddr.String(), + Pubkey: codectypes.UnsafePackAny(valPubKey), + Value: bondAmount, + } + + _, err := msgServer.CreateValidator(ctx, &msg) + return bondAmount, err + }, + expOut: sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(12)), + expErr: false, + }, + { + name: "invalid bond token", + malleate: func(ctx sdk.Context, msKeeper *multistakingkeeper.Keeper, msgServer stakingtypes.MsgServer) (sdk.Coin, error) { + bondAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(25)) + + msg := stakingtypes.MsgCreateValidator{ + Description: stakingtypes.Description{ + Moniker: "test", + Identity: "test", + Website: "test", + SecurityContact: "test", + Details: "test", + }, + Commission: stakingtypes.CommissionRates{ + Rate: sdk.MustNewDecFromStr("0.05"), + MaxRate: sdk.MustNewDecFromStr("0.1"), + MaxChangeRate: sdk.MustNewDecFromStr("0.05"), + }, + MinSelfDelegation: sdk.NewInt(1), + DelegatorAddress: delAddr.String(), + ValidatorAddress: valAddr.String(), + Pubkey: codectypes.UnsafePackAny(valPubKey), + Value: bondAmount, + } + _, err := msgServer.CreateValidator(ctx, &msg) + return bondAmount, err + }, + expOut: sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(12)), + expErr: true, + }, + { + name: "invalid validator address", + malleate: func(ctx sdk.Context, msKeeper *multistakingkeeper.Keeper, msgServer stakingtypes.MsgServer) (sdk.Coin, error) { + msKeeper.SetBondWeight(ctx, MultiStakingDenomA, sdk.MustNewDecFromStr("0.3")) + bondAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(3001)) + + msg := stakingtypes.MsgCreateValidator{ + Description: stakingtypes.Description{ + Moniker: "NewValidator", + }, + Commission: stakingtypes.CommissionRates{ + Rate: sdk.MustNewDecFromStr("0.05"), + MaxRate: sdk.MustNewDecFromStr("0.1"), + MaxChangeRate: sdk.MustNewDecFromStr("0.05"), + }, + MinSelfDelegation: sdk.NewInt(1), + DelegatorAddress: delAddr.String(), + ValidatorAddress: sdk.AccAddress([]byte("invalid")).String(), + Pubkey: codectypes.UnsafePackAny(valPubKey), + Value: bondAmount, + } + + _, err := msgServer.CreateValidator(ctx, &msg) + return bondAmount, err + }, + expOut: sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(12)), + expErr: true, + }, + } + + for _, tc := range testCases { + suite.Run(tc.name, func() { + suite.SetupTest() + valCoins := sdk.NewCoins(sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(10000)), sdk.NewCoin(MultiStakingDenomB, sdk.NewInt(10000))) + simapp.FundAccount(suite.app, suite.ctx, delAddr, valCoins) + + bondAmount, err := tc.malleate(suite.ctx, suite.msKeeper, multistakingkeeper.NewMsgServerImpl(*suite.msKeeper)) + if tc.expErr { + suite.Require().Error(err) + } else { + suite.Require().NoError(err) + lockId := multistakingtypes.MultiStakingLockID(delAddr.String(), valAddr.String()) + lockRecord, found := suite.msKeeper.GetMultiStakingLock(suite.ctx, lockId) + suite.Require().True(found) + actualBond, found := suite.app.StakingKeeper.GetDelegation(suite.ctx, delAddr, valAddr) + suite.Require().True(found) + suite.Require().Equal(bondAmount.Amount, lockRecord.LockedCoin.Amount) + suite.Require().Equal(tc.expOut.Amount, actualBond.Shares.RoundInt()) + } + }) + } +} + +func (suite *KeeperTestSuite) TestEditValidator() { + delAddr := testutil.GenAddress() + valPubKey := testutil.GenPubKey() + valAddr := sdk.ValAddress(valPubKey.Address()) + + testCases := []struct { + name string + malleate func(ctx sdk.Context, msgServer stakingtypes.MsgServer) (stakingtypes.MsgEditValidator, error) + expErr bool + }{ + { + name: "success", + malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer) (stakingtypes.MsgEditValidator, error) { + newRate := sdk.MustNewDecFromStr("0.03") + newMinSelfDelegation := sdk.NewInt(300) + editMsg := stakingtypes.NewMsgEditValidator(valAddr, stakingtypes.Description{ + Moniker: "test 1", + Identity: "test 1", + Website: "test 1", + SecurityContact: "test 1", + Details: "test 1", + }, + &newRate, + &newMinSelfDelegation, + ) + _, err := msgServer.EditValidator(ctx, editMsg) + return *editMsg, err + }, + expErr: false, + }, + { + name: "not found validator", + malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer) (stakingtypes.MsgEditValidator, error) { + newRate := sdk.MustNewDecFromStr("0.03") + newMinSelfDelegation := sdk.NewInt(300) + editMsg := stakingtypes.NewMsgEditValidator(testutil.GenValAddress(), stakingtypes.Description{ + Moniker: "test", + Identity: "test", + Website: "test", + SecurityContact: "test", + Details: "test", + }, + &newRate, + &newMinSelfDelegation, + ) + _, err := msgServer.EditValidator(ctx, editMsg) + return *editMsg, err + }, + expErr: true, + }, + { + name: "negative rate", + malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer) (stakingtypes.MsgEditValidator, error) { + newRate := sdk.MustNewDecFromStr("-0.01") + newMinSelfDelegation := sdk.NewInt(300) + editMsg := stakingtypes.NewMsgEditValidator(valAddr, stakingtypes.Description{ + Moniker: "test 1", + Identity: "test 1", + Website: "test 1", + SecurityContact: "test 1", + Details: "test 1", + }, + &newRate, + &newMinSelfDelegation, + ) + _, err := msgServer.EditValidator(ctx, editMsg) + return *editMsg, err + }, + expErr: true, + }, + { + name: "less than minimum rate", + malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer) (stakingtypes.MsgEditValidator, error) { + newRate := sdk.MustNewDecFromStr("0.01") + newMinSelfDelegation := sdk.NewInt(300) + editMsg := stakingtypes.NewMsgEditValidator(valAddr, stakingtypes.Description{ + Moniker: "test 1", + Identity: "test 1", + Website: "test 1", + SecurityContact: "test 1", + Details: "test 1", + }, + &newRate, + &newMinSelfDelegation, + ) + _, err := msgServer.EditValidator(ctx, editMsg) + return *editMsg, err + }, + expErr: true, + }, + { + name: "more than max rate", + malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer) (stakingtypes.MsgEditValidator, error) { + newRate := sdk.MustNewDecFromStr("0.11") + newMinSelfDelegation := sdk.NewInt(300) + editMsg := stakingtypes.NewMsgEditValidator(valAddr, stakingtypes.Description{ + Moniker: "test 1", + Identity: "test 1", + Website: "test 1", + SecurityContact: "test 1", + Details: "test 1", + }, + &newRate, + &newMinSelfDelegation, + ) + _, err := msgServer.EditValidator(ctx, editMsg) + return *editMsg, err + }, + expErr: true, + }, + { + name: "min self delegation more than validator tokens", + malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer) (stakingtypes.MsgEditValidator, error) { + newRate := sdk.MustNewDecFromStr("0.03") + newMinSelfDelegation := sdk.NewInt(10000) + editMsg := stakingtypes.NewMsgEditValidator(valAddr, stakingtypes.Description{ + Moniker: "test 1", + Identity: "test 1", + Website: "test 1", + SecurityContact: "test 1", + Details: "test 1", + }, + &newRate, + &newMinSelfDelegation, + ) + _, err := msgServer.EditValidator(ctx, editMsg) + return *editMsg, err + }, + expErr: true, + }, + { + name: "min self delegation more than old min delegation value", + malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer) (stakingtypes.MsgEditValidator, error) { + newRate := sdk.MustNewDecFromStr("0.03") + newMinSelfDelegation := sdk.NewInt(100) + editMsg := stakingtypes.NewMsgEditValidator(valAddr, stakingtypes.Description{ + Moniker: "test 1", + Identity: "test 1", + Website: "test 1", + SecurityContact: "test 1", + Details: "test 1", + }, + &newRate, + &newMinSelfDelegation, + ) + _, err := msgServer.EditValidator(ctx, editMsg) + return *editMsg, err + }, + expErr: true, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + suite.SetupTest() + newParam := stakingtypes.DefaultParams() + newParam.MinCommissionRate = sdk.MustNewDecFromStr("0.02") + suite.app.StakingKeeper.SetParams(suite.ctx, newParam) + msgServer := multistakingkeeper.NewMsgServerImpl(*suite.msKeeper) + suite.msKeeper.SetBondWeight(suite.ctx, MultiStakingDenomA, sdk.OneDec()) + bondAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(1000)) + simapp.FundAccount(suite.app, suite.ctx, delAddr, sdk.NewCoins(bondAmount)) + + createMsg := stakingtypes.MsgCreateValidator{ + Description: stakingtypes.Description{ + Moniker: "test", + Identity: "test", + Website: "test", + SecurityContact: "test", + Details: "test", + }, + Commission: stakingtypes.CommissionRates{ + Rate: sdk.MustNewDecFromStr("0.05"), + MaxRate: sdk.MustNewDecFromStr("0.1"), + MaxChangeRate: sdk.MustNewDecFromStr("0.05"), + }, + MinSelfDelegation: sdk.NewInt(200), + DelegatorAddress: delAddr.String(), + ValidatorAddress: valAddr.String(), + Pubkey: codectypes.UnsafePackAny(valPubKey), + Value: bondAmount, + } + msgServer.CreateValidator(suite.ctx, &createMsg) + + suite.ctx = suite.ctx.WithBlockHeader(tmproto.Header{Time: time.Now()}) + originMsg, err := tc.malleate(suite.ctx, msgServer) + + if tc.expErr { + suite.Require().Error(err) + } else { + suite.Require().NoError(err) + validatorInfo, found := suite.app.StakingKeeper.GetValidator(suite.ctx, sdk.ValAddress(originMsg.ValidatorAddress)) + if found { + suite.Require().Equal(validatorInfo.Description, originMsg.Description) + suite.Require().Equal(validatorInfo.MinSelfDelegation, &originMsg.MinSelfDelegation) + suite.Require().Equal(validatorInfo.Commission.CommissionRates.Rate, &originMsg.CommissionRate) + } + } + }) + } +} + +func (suite *KeeperTestSuite) TestDelegate() { + delAddr := testutil.GenAddress() + valPubKey := testutil.GenPubKey() + valAddr := sdk.ValAddress(valPubKey.Address()) + + testCases := []struct { + name string + malleate func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (stakingtypes.MsgDelegate, error) + expRate sdk.Dec + expErr bool + }{ + { + name: "success and not change rate", + malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (stakingtypes.MsgDelegate, error) { + bondAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(1000)) + delMsg := stakingtypes.NewMsgDelegate(delAddr, valAddr, bondAmount) + _, err := msgServer.Delegate(ctx, delMsg) + return *delMsg, err + }, + expRate: sdk.OneDec(), + expErr: false, + }, + { + name: "rate change from 1 to 0.75 (1000 / 1 + 2000 / 0.5 = 3000 / 0.75)", + malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (stakingtypes.MsgDelegate, error) { + bondAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(1000)) + delMsg := stakingtypes.NewMsgDelegate(delAddr, valAddr, bondAmount) + _, err := msgServer.Delegate(ctx, delMsg) + + if err != nil { + return *delMsg, err + } + msKeeper.SetBondWeight(ctx, MultiStakingDenomA, sdk.MustNewDecFromStr("0.5")) + bondAmount1 := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(2000)) + delMsg1 := stakingtypes.NewMsgDelegate(delAddr, valAddr, bondAmount1) + _, err = msgServer.Delegate(ctx, delMsg1) + return *delMsg, err + }, + expRate: sdk.MustNewDecFromStr("0.75"), + expErr: false, + }, + { + name: "not found validator", + malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (stakingtypes.MsgDelegate, error) { + bondAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(1000)) + + delMsg := stakingtypes.NewMsgDelegate(delAddr, testutil.GenValAddress(), bondAmount) + _, err := msgServer.Delegate(ctx, delMsg) + return *delMsg, err + }, + expErr: true, + }, + { + name: "not allow token", + malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (stakingtypes.MsgDelegate, error) { + bondAmount := sdk.NewCoin(MultiStakingDenomB, sdk.NewInt(1000)) + + delMsg := stakingtypes.NewMsgDelegate(delAddr, valAddr, bondAmount) + _, err := msgServer.Delegate(ctx, delMsg) + return *delMsg, err + }, + expErr: true, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + suite.SetupTest() + newParam := stakingtypes.DefaultParams() + newParam.MinCommissionRate = sdk.MustNewDecFromStr("0.02") + suite.app.StakingKeeper.SetParams(suite.ctx, newParam) + msgServer := multistakingkeeper.NewMsgServerImpl(*suite.msKeeper) + suite.msKeeper.SetBondWeight(suite.ctx, MultiStakingDenomA, sdk.OneDec()) + bondAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(1000)) + userBalance := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(10000)) + simapp.FundAccount(suite.app, suite.ctx, delAddr, sdk.NewCoins(userBalance)) + + createMsg := stakingtypes.MsgCreateValidator{ + Description: stakingtypes.Description{ + Moniker: "test", + Identity: "test", + Website: "test", + SecurityContact: "test", + Details: "test", + }, + Commission: stakingtypes.CommissionRates{ + Rate: sdk.MustNewDecFromStr("0.05"), + MaxRate: sdk.MustNewDecFromStr("0.1"), + MaxChangeRate: sdk.MustNewDecFromStr("0.05"), + }, + MinSelfDelegation: sdk.NewInt(200), + DelegatorAddress: delAddr.String(), + ValidatorAddress: valAddr.String(), + Pubkey: codectypes.UnsafePackAny(valPubKey), + Value: bondAmount, + } + msgServer.CreateValidator(suite.ctx, &createMsg) + + _, err := tc.malleate(suite.ctx, msgServer, *suite.msKeeper) + + if tc.expErr { + suite.Require().Error(err) + } else { + suite.Require().NoError(err) + lockId := multistakingtypes.MultiStakingLockID(delAddr.String(), valAddr.String()) + lockRecord, found := suite.msKeeper.GetMultiStakingLock(suite.ctx, lockId) + suite.Require().True(found) + suite.Require().Equal(tc.expRate, lockRecord.GetBondWeight()) + } + }) + } +} + +func (suite *KeeperTestSuite) TestBeginRedelegate() { + delAddr := testutil.GenAddress() + valPubKey1 := testutil.GenPubKey() + valPubKey2 := testutil.GenPubKey() + + valAddr1 := sdk.ValAddress(valPubKey1.Address()) + valAddr2 := sdk.ValAddress(valPubKey2.Address()) + + testCases := []struct { + name string + malleate func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (stakingtypes.MsgBeginRedelegate, error) + expRate []sdk.Dec + expLock []math.Int + expErr bool + }{ + { + name: "redelegate from val1 to val2", + malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (stakingtypes.MsgBeginRedelegate, error) { + bondAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(500)) + multiStakingMsg := stakingtypes.NewMsgBeginRedelegate(delAddr, valAddr1, valAddr2, bondAmount) + _, err := msgServer.BeginRedelegate(ctx, multiStakingMsg) + return *multiStakingMsg, err + }, + expRate: []sdk.Dec{sdk.OneDec(), sdk.OneDec()}, + expLock: []math.Int{sdk.NewInt(500), sdk.NewInt(1500)}, + expErr: false, + }, + { + name: "delegate 500 more to val1 then change rate and redelegate to val2", + malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (stakingtypes.MsgBeginRedelegate, error) { + msKeeper.SetBondWeight(ctx, MultiStakingDenomA, sdk.MustNewDecFromStr("0.25")) + bondAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(500)) + + delMsg := stakingtypes.NewMsgDelegate(delAddr, valAddr1, bondAmount) + _, err := msgServer.Delegate(ctx, delMsg) + + bondAmount1 := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(1000)) + redelMsg := stakingtypes.NewMsgBeginRedelegate(delAddr, valAddr1, valAddr2, bondAmount1) + if err != nil { + return *redelMsg, err + } + + _, err = msgServer.BeginRedelegate(ctx, redelMsg) + return *redelMsg, err + }, + expRate: []sdk.Dec{sdk.MustNewDecFromStr("0.75"), sdk.MustNewDecFromStr("0.875")}, + expLock: []math.Int{sdk.NewInt(500), sdk.NewInt(2000)}, + expErr: false, + }, + { + name: "not found validator", + malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (stakingtypes.MsgBeginRedelegate, error) { + bondAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(500)) + multiStakingMsg := stakingtypes.NewMsgBeginRedelegate(delAddr, valAddr1, testutil.GenValAddress(), bondAmount) + _, err := msgServer.BeginRedelegate(ctx, multiStakingMsg) + return *multiStakingMsg, err + }, + expErr: true, + }, + { + name: "not allow token", + malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (stakingtypes.MsgBeginRedelegate, error) { + bondAmount := sdk.NewCoin(MultiStakingDenomB, sdk.NewInt(1000)) + + multiStakingMsg := stakingtypes.NewMsgBeginRedelegate(delAddr, valAddr1, valAddr2, bondAmount) + _, err := msgServer.BeginRedelegate(ctx, multiStakingMsg) + return *multiStakingMsg, err + }, + expErr: true, + }, + { + name: "setup val3 with bond denom is arst then redelgate from val1 to val3", + malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (stakingtypes.MsgBeginRedelegate, error) { + valPubKey3 := testutil.GenPubKey() + bondAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(500)) + + valAddr3 := sdk.ValAddress(valPubKey3.Address()) + createMsg := stakingtypes.MsgCreateValidator{ + Description: stakingtypes.Description{ + Moniker: "test", + Identity: "test", + Website: "test", + SecurityContact: "test", + Details: "test", + }, + Commission: stakingtypes.CommissionRates{ + Rate: sdk.MustNewDecFromStr("0.05"), + MaxRate: sdk.MustNewDecFromStr("0.1"), + MaxChangeRate: sdk.MustNewDecFromStr("0.1"), + }, + MinSelfDelegation: sdk.NewInt(200), + DelegatorAddress: delAddr.String(), + ValidatorAddress: valAddr3.String(), + Pubkey: codectypes.UnsafePackAny(valPubKey3), + Value: sdk.NewCoin(MultiStakingDenomB, sdk.NewInt(1000)), + } + msgServer.CreateValidator(suite.ctx, &createMsg) + multiStakingMsg := stakingtypes.NewMsgBeginRedelegate(delAddr, valAddr1, valAddr3, bondAmount) + _, err := msgServer.BeginRedelegate(ctx, multiStakingMsg) + return *multiStakingMsg, err + }, + expErr: true, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + suite.SetupTest() + newParam := stakingtypes.DefaultParams() + newParam.MinCommissionRate = sdk.MustNewDecFromStr("0.02") + suite.app.StakingKeeper.SetParams(suite.ctx, newParam) + msgServer := multistakingkeeper.NewMsgServerImpl(*suite.msKeeper) + suite.msKeeper.SetBondWeight(suite.ctx, MultiStakingDenomA, sdk.OneDec()) + bondAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(1000)) + userBalance := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(10000)) + simapp.FundAccount(suite.app, suite.ctx, delAddr, sdk.NewCoins(userBalance, sdk.NewCoin(MultiStakingDenomB, sdk.NewInt(10000)))) + + createMsg := stakingtypes.MsgCreateValidator{ + Description: stakingtypes.Description{ + Moniker: "test", + Identity: "test", + Website: "test", + SecurityContact: "test", + Details: "test", + }, + Commission: stakingtypes.CommissionRates{ + Rate: sdk.MustNewDecFromStr("0.05"), + MaxRate: sdk.MustNewDecFromStr("0.1"), + MaxChangeRate: sdk.MustNewDecFromStr("0.05"), + }, + MinSelfDelegation: sdk.NewInt(200), + DelegatorAddress: delAddr.String(), + ValidatorAddress: valAddr1.String(), + Pubkey: codectypes.UnsafePackAny(valPubKey1), + Value: bondAmount, + } + createMsg2 := stakingtypes.MsgCreateValidator{ + Description: stakingtypes.Description{ + Moniker: "test", + Identity: "test", + Website: "test", + SecurityContact: "test", + Details: "test", + }, + Commission: stakingtypes.CommissionRates{ + Rate: sdk.MustNewDecFromStr("0.05"), + MaxRate: sdk.MustNewDecFromStr("0.1"), + MaxChangeRate: sdk.MustNewDecFromStr("0.1"), + }, + MinSelfDelegation: sdk.NewInt(200), + DelegatorAddress: delAddr.String(), + ValidatorAddress: valAddr2.String(), + Pubkey: codectypes.UnsafePackAny(valPubKey2), + Value: bondAmount, + } + msgServer.CreateValidator(suite.ctx, &createMsg) + msgServer.CreateValidator(suite.ctx, &createMsg2) + + suite.ctx = suite.ctx.WithBlockHeader(tmproto.Header{Time: time.Now()}) + + _, err := tc.malleate(suite.ctx, msgServer, *suite.msKeeper) + + if tc.expErr { + suite.Require().Error(err) + } else { + suite.Require().NoError(err) + lockId1 := multistakingtypes.MultiStakingLockID(delAddr.String(), valAddr1.String()) + lockRecord1, found := suite.msKeeper.GetMultiStakingLock(suite.ctx, lockId1) + suite.Require().True(found) + suite.Require().Equal(tc.expRate[0], lockRecord1.GetBondWeight()) + suite.Require().Equal(tc.expLock[0], lockRecord1.LockedCoin.Amount) + + lockId2 := multistakingtypes.MultiStakingLockID(delAddr.String(), valAddr2.String()) + lockRecord2, found := suite.msKeeper.GetMultiStakingLock(suite.ctx, lockId2) + suite.Require().True(found) + suite.Require().Equal(tc.expRate[1], lockRecord2.GetBondWeight()) + suite.Require().Equal(tc.expLock[1], lockRecord2.LockedCoin.Amount) + } + }) + } +} + +func (suite *KeeperTestSuite) TestUndelegate() { + delAddr := testutil.GenAddress() + valPubKey := testutil.GenPubKey() + valAddr := sdk.ValAddress(valPubKey.Address()) + + testCases := []struct { + name string + malleate func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (*stakingtypes.MsgUndelegate, error) + expUnlock math.Int + expLock math.Int + expErr bool + }{ + { + name: "undelegate success", + malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (*stakingtypes.MsgUndelegate, error) { + bondAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(500)) + multiStakingMsg := stakingtypes.NewMsgUndelegate(delAddr, valAddr, bondAmount) + _, err := msgServer.Undelegate(ctx, multiStakingMsg) + return multiStakingMsg, err + }, + expUnlock: sdk.NewInt(500), + expLock: sdk.NewInt(500), + expErr: false, + }, + { + name: "undelegate 250 then undelegate 500", + malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (*stakingtypes.MsgUndelegate, error) { + bondAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(250)) + multiStakingMsg := stakingtypes.NewMsgUndelegate(delAddr, valAddr, bondAmount) + _, err := msgServer.Undelegate(ctx, multiStakingMsg) + if err != nil { + return multiStakingMsg, err + } + bondAmount1 := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(500)) + multiStakingMsg1 := stakingtypes.NewMsgUndelegate(delAddr, valAddr, bondAmount1) + _, err = msgServer.Undelegate(ctx, multiStakingMsg1) + return multiStakingMsg1, err + }, + expUnlock: sdk.NewInt(750), + expLock: sdk.NewInt(250), + expErr: false, + }, + { + name: "not found validator", + malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (*stakingtypes.MsgUndelegate, error) { + bondAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(500)) + multiStakingMsg := stakingtypes.NewMsgUndelegate(delAddr, testutil.GenValAddress(), bondAmount) + _, err := msgServer.Undelegate(ctx, multiStakingMsg) + return multiStakingMsg, err + }, + expErr: true, + }, + { + name: "not allow token", + malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (*stakingtypes.MsgUndelegate, error) { + bondAmount := sdk.NewCoin(MultiStakingDenomB, sdk.NewInt(1000)) + + multiStakingMsg := stakingtypes.NewMsgUndelegate(delAddr, testutil.GenValAddress(), bondAmount) + _, err := msgServer.Undelegate(ctx, multiStakingMsg) + return multiStakingMsg, err + }, + expErr: true, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + suite.SetupTest() + newParam := stakingtypes.DefaultParams() + newParam.MinCommissionRate = sdk.MustNewDecFromStr("0.02") + suite.app.StakingKeeper.SetParams(suite.ctx, newParam) + msgServer := multistakingkeeper.NewMsgServerImpl(*suite.msKeeper) + suite.msKeeper.SetBondWeight(suite.ctx, MultiStakingDenomA, sdk.OneDec()) + bondAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(1000)) + userBalance := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(10000)) + simapp.FundAccount(suite.app, suite.ctx, delAddr, sdk.NewCoins(userBalance, sdk.NewCoin(MultiStakingDenomB, sdk.NewInt(10000)))) + + createMsg := stakingtypes.MsgCreateValidator{ + Description: stakingtypes.Description{ + Moniker: "test", + Identity: "test", + Website: "test", + SecurityContact: "test", + Details: "test", + }, + Commission: stakingtypes.CommissionRates{ + Rate: sdk.MustNewDecFromStr("0.05"), + MaxRate: sdk.MustNewDecFromStr("0.1"), + MaxChangeRate: sdk.MustNewDecFromStr("0.05"), + }, + MinSelfDelegation: sdk.NewInt(200), + DelegatorAddress: delAddr.String(), + ValidatorAddress: valAddr.String(), + Pubkey: codectypes.UnsafePackAny(valPubKey), + Value: bondAmount, + } + + msgServer.CreateValidator(suite.ctx, &createMsg) + + suite.ctx = suite.ctx.WithBlockHeader(tmproto.Header{Time: time.Now()}) + + _, err := tc.malleate(suite.ctx, msgServer, *suite.msKeeper) + + if tc.expErr { + suite.Require().Error(err) + } else { + suite.Require().NoError(err) + lockId1 := multistakingtypes.MultiStakingLockID(delAddr.String(), valAddr.String()) + lockRecord1, found := suite.msKeeper.GetMultiStakingLock(suite.ctx, lockId1) + suite.Require().True(found) + suite.Require().Equal(tc.expLock, lockRecord1.LockedCoin.Amount) + + unlockID := multistakingtypes.MultiStakingUnlockID(delAddr.String(), valAddr.String()) + unbondRecord, found := suite.msKeeper.GetMultiStakingUnlock(suite.ctx, unlockID) + suite.Require().True(found) + suite.Require().Equal(tc.expUnlock, unbondRecord.Entries[0].UnlockingCoin.Amount) + } + }) + } +} + +func (suite *KeeperTestSuite) TestCancelUnbondingDelegation() { + delAddr := testutil.GenAddress() + valPubKey := testutil.GenPubKey() + valAddr := sdk.ValAddress(valPubKey.Address()) + + testCases := []struct { + name string + malleate func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (stakingtypes.MsgCancelUnbondingDelegation, error) + expUnlock math.Int + expLock math.Int + expErr bool + }{ + { + name: "undelegate success", + malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (stakingtypes.MsgCancelUnbondingDelegation, error) { + bondAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(500)) + multiStakingMsg := stakingtypes.NewMsgCancelUnbondingDelegation(delAddr, valAddr, ctx.BlockHeight(), bondAmount) + _, err := msgServer.CancelUnbondingDelegation(ctx, multiStakingMsg) + return *multiStakingMsg, err + }, + expUnlock: sdk.NewInt(500), + expLock: sdk.NewInt(1000), + expErr: false, + }, + // { + // name: "undelegate 250 then undelegate 500", + // malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (stakingtypes.MsgCancelUnbondingDelegation, error) { + // bondAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(250)) + // multiStakingMsg := stakingtypes.NewMsgCancelUnbondingDelegation(delAddr, valAddr, ctx.BlockHeight(), bondAmount) + // _, err := msgServer.CancelUnbondingDelegation(ctx, multiStakingMsg) + // if err != nil { + // return *multiStakingMsg, err + // } + // bondAmount1 := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(500)) + // multiStakingMsg1 := stakingtypes.NewMsgCancelUnbondingDelegation(delAddr, valAddr, ctx.BlockHeight(), bondAmount1) + // _, err = msgServer.CancelUnbondingDelegation(ctx, multiStakingMsg1) + // return *multiStakingMsg1, err + // }, + // expUnlock: sdk.NewInt(250), + // expLock: sdk.NewInt(1750), + // expErr: false, + // }, + // { + // name: "not found validator", + // malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (stakingtypes.MsgCancelUnbondingDelegation, error) { + // bondAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(500)) + // multiStakingMsg := stakingtypes.NewMsgCancelUnbondingDelegation(delAddr, testutil.GenValAddress(), ctx.BlockHeight(), bondAmount) + // _, err := msgServer.CancelUnbondingDelegation(ctx, multiStakingMsg) + // return *multiStakingMsg, err + // }, + // expErr: true, + // }, + // { + // name: "not allow token", + // malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (stakingtypes.MsgCancelUnbondingDelegation, error) { + // bondAmount := sdk.NewCoin(MultiStakingDenomB, sdk.NewInt(1000)) + + // multiStakingMsg := stakingtypes.NewMsgCancelUnbondingDelegation(delAddr, valAddr, ctx.BlockHeight(), bondAmount) + // _, err := msgServer.CancelUnbondingDelegation(ctx, multiStakingMsg) + // return *multiStakingMsg, err + // }, + // expErr: true, + // }, + // { + // name: "not found entry at height 20", + // malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (stakingtypes.MsgCancelUnbondingDelegation, error) { + // bondAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(500)) + // multiStakingMsg := stakingtypes.NewMsgCancelUnbondingDelegation(delAddr, valAddr, 20, bondAmount) + // _, err := msgServer.CancelUnbondingDelegation(ctx, multiStakingMsg) + // return *multiStakingMsg, err + // }, + // expErr: true, + // }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + suite.SetupTest() + newParam := stakingtypes.DefaultParams() + newParam.MinCommissionRate = sdk.MustNewDecFromStr("0.02") + suite.app.StakingKeeper.SetParams(suite.ctx, newParam) + msgServer := multistakingkeeper.NewMsgServerImpl(*suite.msKeeper) + suite.msKeeper.SetBondWeight(suite.ctx, MultiStakingDenomA, sdk.OneDec()) + bondAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(2000)) + userBalance := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(10000)) + simapp.FundAccount(suite.app, suite.ctx, delAddr, sdk.NewCoins(userBalance, sdk.NewCoin(MultiStakingDenomB, sdk.NewInt(10000)))) + + createMsg := stakingtypes.MsgCreateValidator{ + Description: stakingtypes.Description{ + Moniker: "test", + Identity: "test", + Website: "test", + SecurityContact: "test", + Details: "test", + }, + Commission: stakingtypes.CommissionRates{ + Rate: sdk.MustNewDecFromStr("0.05"), + MaxRate: sdk.MustNewDecFromStr("0.1"), + MaxChangeRate: sdk.MustNewDecFromStr("0.05"), + }, + MinSelfDelegation: sdk.NewInt(200), + DelegatorAddress: delAddr.String(), + ValidatorAddress: valAddr.String(), + Pubkey: codectypes.UnsafePackAny(valPubKey), + Value: bondAmount, + } + + msgServer.CreateValidator(suite.ctx, &createMsg) + + suite.ctx = suite.ctx.WithBlockHeader(tmproto.Header{Time: time.Now()}) + + unbondMsg := stakingtypes.NewMsgUndelegate(delAddr, valAddr, sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(1000))) + msgServer.Undelegate(suite.ctx, unbondMsg) + + _, err := tc.malleate(suite.ctx, msgServer, *suite.msKeeper) + + if tc.expErr { + suite.Require().Error(err) + } else { + suite.Require().NoError(err) + lockId1 := multistakingtypes.MultiStakingLockID(delAddr.String(), valAddr.String()) + lockRecord1, found := suite.msKeeper.GetMultiStakingLock(suite.ctx, lockId1) + suite.Require().True(found) + suite.Require().Equal(tc.expLock, lockRecord1.LockedCoin.Amount) + + unlockID := multistakingtypes.MultiStakingUnlockID(delAddr.String(), valAddr.String()) + unbondRecord, found := suite.msKeeper.GetMultiStakingUnlock(suite.ctx, unlockID) + suite.Require().True(found) + suite.Require().Equal(tc.expUnlock, unbondRecord.Entries[0].UnlockingCoin.Amount) + } + }) + } +} From 62df77c98908b077850618bb69f0910bbe370a5e Mon Sep 17 00:00:00 2001 From: dzungdo Date: Fri, 19 Jan 2024 04:07:37 +0700 Subject: [PATCH 02/11] Recover cancel unbonding logic --- x/multi-staking/keeper/msg_server.go | 18 +++++----- x/multi-staking/keeper/unlock.go | 52 +++++++++++++++++++++++++++- x/multi-staking/types/unlock.go | 12 +++++-- 3 files changed, 68 insertions(+), 14 deletions(-) diff --git a/x/multi-staking/keeper/msg_server.go b/x/multi-staking/keeper/msg_server.go index 32ccfff2..493f4f20 100644 --- a/x/multi-staking/keeper/msg_server.go +++ b/x/multi-staking/keeper/msg_server.go @@ -197,7 +197,7 @@ func (k msgServer) Undelegate(goCtx context.Context, msg *stakingtypes.MsgUndele func (k msgServer) CancelUnbondingDelegation(goCtx context.Context, msg *stakingtypes.MsgCancelUnbondingDelegation) (*stakingtypes.MsgCancelUnbondingDelegationResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) - multiStakerAddr, valAcc, err := types.AccAddrAndValAddrFromStrings(msg.DelegatorAddress, msg.ValidatorAddress) + _, valAcc, err := types.AccAddrAndValAddrFromStrings(msg.DelegatorAddress, msg.ValidatorAddress) if err != nil { return nil, err } @@ -206,11 +206,14 @@ func (k msgServer) CancelUnbondingDelegation(goCtx context.Context, msg *staking return nil, fmt.Errorf("not allow coin") } - unbondEntry, found := k.keeper.GetUnbondingEntryAtCreationHeight(ctx, multiStakerAddr, valAcc, msg.CreationHeight) - if !found { - return nil, fmt.Errorf("unbondEntry not found") + unlockID := types.MultiStakingUnlockID(msg.DelegatorAddress, msg.ValidatorAddress) + cancelMSCoin, err := k.keeper.DecreaseUnlockEntryAmount(ctx, unlockID, msg.Amount.Amount, msg.CreationHeight) + if err != nil { + return nil, err } - cancelUnbondingCoin := sdk.NewCoin(k.keeper.stakingKeeper.BondDenom(ctx), unbondEntry.Balance) + + cancelBondAmount := cancelMSCoin.BondValue() + cancelUnbondingCoin := sdk.NewCoin(k.keeper.stakingKeeper.BondDenom(ctx), cancelBondAmount) sdkMsg := &stakingtypes.MsgCancelUnbondingDelegation{ DelegatorAddress: msg.DelegatorAddress, @@ -218,11 +221,6 @@ func (k msgServer) CancelUnbondingDelegation(goCtx context.Context, msg *staking Amount: cancelUnbondingCoin, // replace with cancelUnbondingCoin } - unlockID := types.MultiStakingUnlockID(msg.DelegatorAddress, msg.ValidatorAddress) - err = k.keeper.DeleteUnlockEntryAtCreationHeight(ctx, unlockID, msg.CreationHeight) - if err != nil { - return nil, err - } return k.stakingMsgServer.CancelUnbondingDelegation(sdk.WrapSDKContext(ctx), sdkMsg) } diff --git a/x/multi-staking/keeper/unlock.go b/x/multi-staking/keeper/unlock.go index 7ff7be26..a665a5eb 100644 --- a/x/multi-staking/keeper/unlock.go +++ b/x/multi-staking/keeper/unlock.go @@ -3,6 +3,7 @@ package keeper import ( "fmt" + "cosmossdk.io/math" "github.com/realio-tech/multi-staking-module/x/multi-staking/types" sdk "github.com/cosmos/cosmos-sdk/types" @@ -43,7 +44,7 @@ func (k Keeper) SetMultiStakingUnlockEntry( if found { unlock.AddEntry(ctx.BlockHeight(), multistakingCoin) } else { - unlock = types.NewMultiStakingUnlock(ctx.BlockHeight(), multistakingCoin) + unlock = types.NewMultiStakingUnlock(unlockID, ctx.BlockHeight(), multistakingCoin) } k.SetMultiStakingUnlock(ctx, unlock) @@ -69,3 +70,52 @@ func (k Keeper) DeleteUnlockEntryAtCreationHeight( k.SetMultiStakingUnlock(ctx, unlock) return nil } + +func (k Keeper) DecreaseUnlockEntryAmount( + ctx sdk.Context, unlockID types.UnlockID, + amount math.Int, creationHeight int64, +) (types.MultiStakingCoin, error) { + unlockRecord, found := k.GetMultiStakingUnlock(ctx, unlockID) + + if !found { + return types.MultiStakingCoin{}, fmt.Errorf("not found unlock recored") + } + var ( + unlockEntry types.UnlockEntry + unlockEntryIndex = -1 + ) + + for i, entry := range unlockRecord.Entries { + if entry.CreationHeight == creationHeight { + unlockEntry = entry + unlockEntryIndex = i + break + } + } + + if unlockEntryIndex == -1 { + return types.MultiStakingCoin{}, fmt.Errorf("unbonding delegation entry is not found at block height %d", creationHeight) + } + + if unlockEntry.UnlockingCoin.Amount.LT(amount) { + return types.MultiStakingCoin{}, fmt.Errorf("amount is greater than the unbonding delegation entry balance") + } + + updatedEntryAmt := unlockEntry.UnlockingCoin.Amount.Sub(amount) + if updatedEntryAmt.IsZero() { + unlockRecord.RemoveEntry(unlockEntryIndex) + } else { + // update the unlocking entry amount for unlocking entry + unlockEntry.UnlockingCoin.Amount = updatedEntryAmt + unlockRecord.Entries[unlockEntryIndex] = unlockEntry + } + + // set the unlocking record or remove it if there are no more entries + if len(unlockRecord.Entries) == 0 { + k.DeleteMultiStakingUnlock(ctx, unlockID) + } else { + k.SetMultiStakingUnlock(ctx, unlockRecord) + } + + return unlockEntry.UnlockingCoin.WithAmount(amount), nil +} diff --git a/x/multi-staking/types/unlock.go b/x/multi-staking/types/unlock.go index 75a4d973..aeb1c3fd 100644 --- a/x/multi-staking/types/unlock.go +++ b/x/multi-staking/types/unlock.go @@ -27,9 +27,10 @@ func (e UnlockEntry) String() string { // //nolint:interfacer func NewMultiStakingUnlock( - creationHeight int64, weightedCoin MultiStakingCoin, + unlockID UnlockID, creationHeight int64, weightedCoin MultiStakingCoin, ) MultiStakingUnlock { return MultiStakingUnlock{ + UnlockID: unlockID, Entries: []UnlockEntry{ NewUnlockEntry(creationHeight, weightedCoin), }, @@ -60,7 +61,12 @@ func (unlock *MultiStakingUnlock) AddEntry(creationHeight int64, weightedCoin Mu } } -// RemoveEntry - remove entry at index i to the unbonding delegation +// RemoveEntry - remove entry at index i to the multi staking unlock +func (unlock *MultiStakingUnlock) RemoveEntry(i int) { + unlock.Entries = append(unlock.Entries[:i], unlock.Entries[i+1:]...) +} + +// RemoveEntryAtCreationHeight - remove entry at creation height to the multi staking unlock func (unlock *MultiStakingUnlock) RemoveEntryAtCreationHeight(creationHeight int64) { // Check the entries exists with creation_height and complete_time entryIndex := -1 @@ -72,7 +78,7 @@ func (unlock *MultiStakingUnlock) RemoveEntryAtCreationHeight(creationHeight int } // entryIndex exists if entryIndex != -1 { - unlock.Entries = append(unlock.Entries[:entryIndex], unlock.Entries[entryIndex+1:]...) + unlock.RemoveEntry(entryIndex) } } From 60db969f1ff6072fa491ed2eda675182263d6443 Mon Sep 17 00:00:00 2001 From: dzungdo Date: Fri, 19 Jan 2024 04:20:02 +0700 Subject: [PATCH 03/11] Fix lint and remove unused func --- x/multi-staking/keeper/msg_server.go | 3 +-- x/multi-staking/keeper/unlock.go | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/x/multi-staking/keeper/msg_server.go b/x/multi-staking/keeper/msg_server.go index 493f4f20..a9d6dc1b 100644 --- a/x/multi-staking/keeper/msg_server.go +++ b/x/multi-staking/keeper/msg_server.go @@ -197,7 +197,7 @@ func (k msgServer) Undelegate(goCtx context.Context, msg *stakingtypes.MsgUndele func (k msgServer) CancelUnbondingDelegation(goCtx context.Context, msg *stakingtypes.MsgCancelUnbondingDelegation) (*stakingtypes.MsgCancelUnbondingDelegationResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) - _, valAcc, err := types.AccAddrAndValAddrFromStrings(msg.DelegatorAddress, msg.ValidatorAddress) + valAcc, err := sdk.ValAddressFromBech32(msg.ValidatorAddress) if err != nil { return nil, err } @@ -221,6 +221,5 @@ func (k msgServer) CancelUnbondingDelegation(goCtx context.Context, msg *staking Amount: cancelUnbondingCoin, // replace with cancelUnbondingCoin } - return k.stakingMsgServer.CancelUnbondingDelegation(sdk.WrapSDKContext(ctx), sdkMsg) } diff --git a/x/multi-staking/keeper/unlock.go b/x/multi-staking/keeper/unlock.go index a665a5eb..3dea0f4f 100644 --- a/x/multi-staking/keeper/unlock.go +++ b/x/multi-staking/keeper/unlock.go @@ -3,9 +3,10 @@ package keeper import ( "fmt" - "cosmossdk.io/math" "github.com/realio-tech/multi-staking-module/x/multi-staking/types" + "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" ) From e49cf9277778cec77b35ddaf3d422f3b96d07a18 Mon Sep 17 00:00:00 2001 From: dzungdo Date: Fri, 19 Jan 2024 10:29:37 +0700 Subject: [PATCH 04/11] Redelegate after cancel unbonding --- x/multi-staking/keeper/msg_server.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/x/multi-staking/keeper/msg_server.go b/x/multi-staking/keeper/msg_server.go index a9d6dc1b..62b4d7d6 100644 --- a/x/multi-staking/keeper/msg_server.go +++ b/x/multi-staking/keeper/msg_server.go @@ -215,6 +215,14 @@ func (k msgServer) CancelUnbondingDelegation(goCtx context.Context, msg *staking cancelBondAmount := cancelMSCoin.BondValue() cancelUnbondingCoin := sdk.NewCoin(k.keeper.stakingKeeper.BondDenom(ctx), cancelBondAmount) + lockID := types.MultiStakingLockID(msg.DelegatorAddress, msg.ValidatorAddress) + lock := k.keeper.GetOrCreateMultiStakingLock(ctx, lockID) + err = lock.AddCoinToMultiStakingLock(cancelMSCoin) + if err != nil { + return nil, err + } + k.keeper.SetMultiStakingLock(ctx, lock) + sdkMsg := &stakingtypes.MsgCancelUnbondingDelegation{ DelegatorAddress: msg.DelegatorAddress, ValidatorAddress: msg.ValidatorAddress, From 369181a807f086ca58c3d1c392b96dabbd404670 Mon Sep 17 00:00:00 2001 From: dzungdo Date: Fri, 19 Jan 2024 10:41:04 +0700 Subject: [PATCH 05/11] Add some msg server tests --- x/multi-staking/keeper/msg_server_test.go | 110 +++++++++++----------- 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/x/multi-staking/keeper/msg_server_test.go b/x/multi-staking/keeper/msg_server_test.go index 0306261d..3c60cc6b 100644 --- a/x/multi-staking/keeper/msg_server_test.go +++ b/x/multi-staking/keeper/msg_server_test.go @@ -815,64 +815,64 @@ func (suite *KeeperTestSuite) TestCancelUnbondingDelegation() { { name: "undelegate success", malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (stakingtypes.MsgCancelUnbondingDelegation, error) { - bondAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(500)) - multiStakingMsg := stakingtypes.NewMsgCancelUnbondingDelegation(delAddr, valAddr, ctx.BlockHeight(), bondAmount) + cancelAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(500)) + multiStakingMsg := stakingtypes.NewMsgCancelUnbondingDelegation(delAddr, valAddr, ctx.BlockHeight(), cancelAmount) _, err := msgServer.CancelUnbondingDelegation(ctx, multiStakingMsg) return *multiStakingMsg, err }, expUnlock: sdk.NewInt(500), - expLock: sdk.NewInt(1000), + expLock: sdk.NewInt(1500), + expErr: false, + }, + { + name: "undelegate 250 then undelegate 500", + malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (stakingtypes.MsgCancelUnbondingDelegation, error) { + cancelAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(250)) + multiStakingMsg := stakingtypes.NewMsgCancelUnbondingDelegation(delAddr, valAddr, ctx.BlockHeight(), cancelAmount) + _, err := msgServer.CancelUnbondingDelegation(ctx, multiStakingMsg) + if err != nil { + return *multiStakingMsg, err + } + cancelAmount1 := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(500)) + multiStakingMsg1 := stakingtypes.NewMsgCancelUnbondingDelegation(delAddr, valAddr, ctx.BlockHeight(), cancelAmount1) + _, err = msgServer.CancelUnbondingDelegation(ctx, multiStakingMsg1) + return *multiStakingMsg1, err + }, + expUnlock: sdk.NewInt(250), + expLock: sdk.NewInt(1750), expErr: false, }, - // { - // name: "undelegate 250 then undelegate 500", - // malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (stakingtypes.MsgCancelUnbondingDelegation, error) { - // bondAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(250)) - // multiStakingMsg := stakingtypes.NewMsgCancelUnbondingDelegation(delAddr, valAddr, ctx.BlockHeight(), bondAmount) - // _, err := msgServer.CancelUnbondingDelegation(ctx, multiStakingMsg) - // if err != nil { - // return *multiStakingMsg, err - // } - // bondAmount1 := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(500)) - // multiStakingMsg1 := stakingtypes.NewMsgCancelUnbondingDelegation(delAddr, valAddr, ctx.BlockHeight(), bondAmount1) - // _, err = msgServer.CancelUnbondingDelegation(ctx, multiStakingMsg1) - // return *multiStakingMsg1, err - // }, - // expUnlock: sdk.NewInt(250), - // expLock: sdk.NewInt(1750), - // expErr: false, - // }, - // { - // name: "not found validator", - // malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (stakingtypes.MsgCancelUnbondingDelegation, error) { - // bondAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(500)) - // multiStakingMsg := stakingtypes.NewMsgCancelUnbondingDelegation(delAddr, testutil.GenValAddress(), ctx.BlockHeight(), bondAmount) - // _, err := msgServer.CancelUnbondingDelegation(ctx, multiStakingMsg) - // return *multiStakingMsg, err - // }, - // expErr: true, - // }, - // { - // name: "not allow token", - // malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (stakingtypes.MsgCancelUnbondingDelegation, error) { - // bondAmount := sdk.NewCoin(MultiStakingDenomB, sdk.NewInt(1000)) - - // multiStakingMsg := stakingtypes.NewMsgCancelUnbondingDelegation(delAddr, valAddr, ctx.BlockHeight(), bondAmount) - // _, err := msgServer.CancelUnbondingDelegation(ctx, multiStakingMsg) - // return *multiStakingMsg, err - // }, - // expErr: true, - // }, - // { - // name: "not found entry at height 20", - // malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (stakingtypes.MsgCancelUnbondingDelegation, error) { - // bondAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(500)) - // multiStakingMsg := stakingtypes.NewMsgCancelUnbondingDelegation(delAddr, valAddr, 20, bondAmount) - // _, err := msgServer.CancelUnbondingDelegation(ctx, multiStakingMsg) - // return *multiStakingMsg, err - // }, - // expErr: true, - // }, + { + name: "not found validator", + malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (stakingtypes.MsgCancelUnbondingDelegation, error) { + cancelAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(500)) + multiStakingMsg := stakingtypes.NewMsgCancelUnbondingDelegation(delAddr, testutil.GenValAddress(), ctx.BlockHeight(), cancelAmount) + _, err := msgServer.CancelUnbondingDelegation(ctx, multiStakingMsg) + return *multiStakingMsg, err + }, + expErr: true, + }, + { + name: "not allow token", + malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (stakingtypes.MsgCancelUnbondingDelegation, error) { + cancelAmount := sdk.NewCoin(MultiStakingDenomB, sdk.NewInt(1000)) + + multiStakingMsg := stakingtypes.NewMsgCancelUnbondingDelegation(delAddr, valAddr, ctx.BlockHeight(), cancelAmount) + _, err := msgServer.CancelUnbondingDelegation(ctx, multiStakingMsg) + return *multiStakingMsg, err + }, + expErr: true, + }, + { + name: "not found entry at height 20", + malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (stakingtypes.MsgCancelUnbondingDelegation, error) { + cancelAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(500)) + multiStakingMsg := stakingtypes.NewMsgCancelUnbondingDelegation(delAddr, valAddr, 20, cancelAmount) + _, err := msgServer.CancelUnbondingDelegation(ctx, multiStakingMsg) + return *multiStakingMsg, err + }, + expErr: true, + }, } for _, tc := range testCases { @@ -921,10 +921,10 @@ func (suite *KeeperTestSuite) TestCancelUnbondingDelegation() { suite.Require().Error(err) } else { suite.Require().NoError(err) - lockId1 := multistakingtypes.MultiStakingLockID(delAddr.String(), valAddr.String()) - lockRecord1, found := suite.msKeeper.GetMultiStakingLock(suite.ctx, lockId1) + lockId := multistakingtypes.MultiStakingLockID(delAddr.String(), valAddr.String()) + lockRecord, found := suite.msKeeper.GetMultiStakingLock(suite.ctx, lockId) suite.Require().True(found) - suite.Require().Equal(tc.expLock, lockRecord1.LockedCoin.Amount) + suite.Require().Equal(tc.expLock, lockRecord.LockedCoin.Amount) unlockID := multistakingtypes.MultiStakingUnlockID(delAddr.String(), valAddr.String()) unbondRecord, found := suite.msKeeper.GetMultiStakingUnlock(suite.ctx, unlockID) From ca4784fc4f158f550c4610b39d8005ca13291670 Mon Sep 17 00:00:00 2001 From: dzungdo Date: Fri, 19 Jan 2024 13:55:06 +0700 Subject: [PATCH 06/11] Fix lint error --- testutil/address.go | 2 +- x/multi-staking/keeper/msg_server_test.go | 92 +++++++++++------------ 2 files changed, 46 insertions(+), 48 deletions(-) diff --git a/testutil/address.go b/testutil/address.go index 75222fe4..1d009b45 100644 --- a/testutil/address.go +++ b/testutil/address.go @@ -6,7 +6,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -func GenPubKey() cryptotypes.PubKey { +func GenPubKey() cryptotypes.PubKey { return secp256k1.GenPrivKey().PubKey() } diff --git a/x/multi-staking/keeper/msg_server_test.go b/x/multi-staking/keeper/msg_server_test.go index 3c60cc6b..ed7f07c0 100644 --- a/x/multi-staking/keeper/msg_server_test.go +++ b/x/multi-staking/keeper/msg_server_test.go @@ -3,17 +3,17 @@ package keeper_test import ( "time" - "cosmossdk.io/math" - codectypes "github.com/cosmos/cosmos-sdk/codec/types" - sdk "github.com/cosmos/cosmos-sdk/types" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - "github.com/realio-tech/multi-staking-module/testing/simapp" "github.com/realio-tech/multi-staking-module/testutil" - multistakingkeeper "github.com/realio-tech/multi-staking-module/x/multi-staking/keeper" multistakingtypes "github.com/realio-tech/multi-staking-module/x/multi-staking/types" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + "cosmossdk.io/math" + + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) var ( @@ -159,7 +159,8 @@ func (suite *KeeperTestSuite) TestCreateValidator() { suite.Run(tc.name, func() { suite.SetupTest() valCoins := sdk.NewCoins(sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(10000)), sdk.NewCoin(MultiStakingDenomB, sdk.NewInt(10000))) - simapp.FundAccount(suite.app, suite.ctx, delAddr, valCoins) + err := simapp.FundAccount(suite.app, suite.ctx, delAddr, valCoins) + suite.Require().NoError(err) bondAmount, err := tc.malleate(suite.ctx, suite.msKeeper, multistakingkeeper.NewMsgServerImpl(*suite.msKeeper)) if tc.expErr { @@ -340,7 +341,8 @@ func (suite *KeeperTestSuite) TestEditValidator() { msgServer := multistakingkeeper.NewMsgServerImpl(*suite.msKeeper) suite.msKeeper.SetBondWeight(suite.ctx, MultiStakingDenomA, sdk.OneDec()) bondAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(1000)) - simapp.FundAccount(suite.app, suite.ctx, delAddr, sdk.NewCoins(bondAmount)) + err := simapp.FundAccount(suite.app, suite.ctx, delAddr, sdk.NewCoins(bondAmount)) + suite.Require().NoError(err) createMsg := stakingtypes.MsgCreateValidator{ Description: stakingtypes.Description{ @@ -361,7 +363,8 @@ func (suite *KeeperTestSuite) TestEditValidator() { Pubkey: codectypes.UnsafePackAny(valPubKey), Value: bondAmount, } - msgServer.CreateValidator(suite.ctx, &createMsg) + _, err = msgServer.CreateValidator(suite.ctx, &createMsg) + suite.Require().NoError(err) suite.ctx = suite.ctx.WithBlockHeader(tmproto.Header{Time: time.Now()}) originMsg, err := tc.malleate(suite.ctx, msgServer) @@ -409,7 +412,6 @@ func (suite *KeeperTestSuite) TestDelegate() { bondAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(1000)) delMsg := stakingtypes.NewMsgDelegate(delAddr, valAddr, bondAmount) _, err := msgServer.Delegate(ctx, delMsg) - if err != nil { return *delMsg, err } @@ -457,7 +459,8 @@ func (suite *KeeperTestSuite) TestDelegate() { suite.msKeeper.SetBondWeight(suite.ctx, MultiStakingDenomA, sdk.OneDec()) bondAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(1000)) userBalance := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(10000)) - simapp.FundAccount(suite.app, suite.ctx, delAddr, sdk.NewCoins(userBalance)) + err := simapp.FundAccount(suite.app, suite.ctx, delAddr, sdk.NewCoins(userBalance)) + suite.Require().NoError(err) createMsg := stakingtypes.MsgCreateValidator{ Description: stakingtypes.Description{ @@ -478,9 +481,10 @@ func (suite *KeeperTestSuite) TestDelegate() { Pubkey: codectypes.UnsafePackAny(valPubKey), Value: bondAmount, } - msgServer.CreateValidator(suite.ctx, &createMsg) + _, err = msgServer.CreateValidator(suite.ctx, &createMsg) + suite.Require().NoError(err) - _, err := tc.malleate(suite.ctx, msgServer, *suite.msKeeper) + _, err = tc.malleate(suite.ctx, msgServer, *suite.msKeeper) if tc.expErr { suite.Require().Error(err) @@ -570,33 +574,18 @@ func (suite *KeeperTestSuite) TestBeginRedelegate() { malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (stakingtypes.MsgBeginRedelegate, error) { valPubKey3 := testutil.GenPubKey() bondAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(500)) - valAddr3 := sdk.ValAddress(valPubKey3.Address()) - createMsg := stakingtypes.MsgCreateValidator{ - Description: stakingtypes.Description{ - Moniker: "test", - Identity: "test", - Website: "test", - SecurityContact: "test", - Details: "test", - }, - Commission: stakingtypes.CommissionRates{ - Rate: sdk.MustNewDecFromStr("0.05"), - MaxRate: sdk.MustNewDecFromStr("0.1"), - MaxChangeRate: sdk.MustNewDecFromStr("0.1"), - }, - MinSelfDelegation: sdk.NewInt(200), - DelegatorAddress: delAddr.String(), - ValidatorAddress: valAddr3.String(), - Pubkey: codectypes.UnsafePackAny(valPubKey3), - Value: sdk.NewCoin(MultiStakingDenomB, sdk.NewInt(1000)), - } - msgServer.CreateValidator(suite.ctx, &createMsg) + createMsg := stakingtypes.MsgCreateValidator{Description: stakingtypes.Description{Moniker: "test", Identity: "test", Website: "test", SecurityContact: "test", Details: "test"}, Commission: stakingtypes.CommissionRates{Rate: sdk.MustNewDecFromStr("0.05"), MaxRate: sdk.MustNewDecFromStr("0.1"), MaxChangeRate: sdk.MustNewDecFromStr("0.1")}, MinSelfDelegation: sdk.NewInt(200), DelegatorAddress: delAddr.String(), ValidatorAddress: valAddr3.String(), Pubkey: codectypes.UnsafePackAny(valPubKey3), Value: sdk.NewCoin(MultiStakingDenomB, sdk.NewInt(1000))} + _, err := msgServer.CreateValidator(suite.ctx, &createMsg) + suite.Require().NoError(err) + multiStakingMsg := stakingtypes.NewMsgBeginRedelegate(delAddr, valAddr1, valAddr3, bondAmount) - _, err := msgServer.BeginRedelegate(ctx, multiStakingMsg) + _, err = msgServer.BeginRedelegate(ctx, multiStakingMsg) return *multiStakingMsg, err }, - expErr: true, + expRate: []sdk.Dec{}, + expLock: []math.Int{}, + expErr: true, }, } @@ -611,7 +600,8 @@ func (suite *KeeperTestSuite) TestBeginRedelegate() { suite.msKeeper.SetBondWeight(suite.ctx, MultiStakingDenomA, sdk.OneDec()) bondAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(1000)) userBalance := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(10000)) - simapp.FundAccount(suite.app, suite.ctx, delAddr, sdk.NewCoins(userBalance, sdk.NewCoin(MultiStakingDenomB, sdk.NewInt(10000)))) + err := simapp.FundAccount(suite.app, suite.ctx, delAddr, sdk.NewCoins(userBalance, sdk.NewCoin(MultiStakingDenomB, sdk.NewInt(10000)))) + suite.Require().NoError(err) createMsg := stakingtypes.MsgCreateValidator{ Description: stakingtypes.Description{ @@ -651,12 +641,15 @@ func (suite *KeeperTestSuite) TestBeginRedelegate() { Pubkey: codectypes.UnsafePackAny(valPubKey2), Value: bondAmount, } - msgServer.CreateValidator(suite.ctx, &createMsg) - msgServer.CreateValidator(suite.ctx, &createMsg2) + _, err = msgServer.CreateValidator(suite.ctx, &createMsg) + suite.Require().NoError(err) + + _, err = msgServer.CreateValidator(suite.ctx, &createMsg2) + suite.Require().NoError(err) suite.ctx = suite.ctx.WithBlockHeader(tmproto.Header{Time: time.Now()}) - _, err := tc.malleate(suite.ctx, msgServer, *suite.msKeeper) + _, err = tc.malleate(suite.ctx, msgServer, *suite.msKeeper) if tc.expErr { suite.Require().Error(err) @@ -754,7 +747,8 @@ func (suite *KeeperTestSuite) TestUndelegate() { suite.msKeeper.SetBondWeight(suite.ctx, MultiStakingDenomA, sdk.OneDec()) bondAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(1000)) userBalance := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(10000)) - simapp.FundAccount(suite.app, suite.ctx, delAddr, sdk.NewCoins(userBalance, sdk.NewCoin(MultiStakingDenomB, sdk.NewInt(10000)))) + err := simapp.FundAccount(suite.app, suite.ctx, delAddr, sdk.NewCoins(userBalance, sdk.NewCoin(MultiStakingDenomB, sdk.NewInt(10000)))) + suite.Require().NoError(err) createMsg := stakingtypes.MsgCreateValidator{ Description: stakingtypes.Description{ @@ -776,11 +770,12 @@ func (suite *KeeperTestSuite) TestUndelegate() { Value: bondAmount, } - msgServer.CreateValidator(suite.ctx, &createMsg) + _, err = msgServer.CreateValidator(suite.ctx, &createMsg) + suite.Require().NoError(err) suite.ctx = suite.ctx.WithBlockHeader(tmproto.Header{Time: time.Now()}) - _, err := tc.malleate(suite.ctx, msgServer, *suite.msKeeper) + _, err = tc.malleate(suite.ctx, msgServer, *suite.msKeeper) if tc.expErr { suite.Require().Error(err) @@ -886,7 +881,8 @@ func (suite *KeeperTestSuite) TestCancelUnbondingDelegation() { suite.msKeeper.SetBondWeight(suite.ctx, MultiStakingDenomA, sdk.OneDec()) bondAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(2000)) userBalance := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(10000)) - simapp.FundAccount(suite.app, suite.ctx, delAddr, sdk.NewCoins(userBalance, sdk.NewCoin(MultiStakingDenomB, sdk.NewInt(10000)))) + err := simapp.FundAccount(suite.app, suite.ctx, delAddr, sdk.NewCoins(userBalance, sdk.NewCoin(MultiStakingDenomB, sdk.NewInt(10000)))) + suite.Require().NoError(err) createMsg := stakingtypes.MsgCreateValidator{ Description: stakingtypes.Description{ @@ -908,14 +904,16 @@ func (suite *KeeperTestSuite) TestCancelUnbondingDelegation() { Value: bondAmount, } - msgServer.CreateValidator(suite.ctx, &createMsg) + _, err = msgServer.CreateValidator(suite.ctx, &createMsg) + suite.Require().NoError(err) suite.ctx = suite.ctx.WithBlockHeader(tmproto.Header{Time: time.Now()}) unbondMsg := stakingtypes.NewMsgUndelegate(delAddr, valAddr, sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(1000))) - msgServer.Undelegate(suite.ctx, unbondMsg) + _, err = msgServer.Undelegate(suite.ctx, unbondMsg) + suite.Require().NoError(err) - _, err := tc.malleate(suite.ctx, msgServer, *suite.msKeeper) + _, err = tc.malleate(suite.ctx, msgServer, *suite.msKeeper) if tc.expErr { suite.Require().Error(err) From b93efe85f8d9dbacdb26c53e12e1c95445d0880f Mon Sep 17 00:00:00 2001 From: dzungdo Date: Fri, 19 Jan 2024 13:58:37 +0700 Subject: [PATCH 07/11] Fix redelegate test error --- x/multi-staking/keeper/msg_server_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/x/multi-staking/keeper/msg_server_test.go b/x/multi-staking/keeper/msg_server_test.go index ed7f07c0..af43cb51 100644 --- a/x/multi-staking/keeper/msg_server_test.go +++ b/x/multi-staking/keeper/msg_server_test.go @@ -598,6 +598,8 @@ func (suite *KeeperTestSuite) TestBeginRedelegate() { suite.app.StakingKeeper.SetParams(suite.ctx, newParam) msgServer := multistakingkeeper.NewMsgServerImpl(*suite.msKeeper) suite.msKeeper.SetBondWeight(suite.ctx, MultiStakingDenomA, sdk.OneDec()) + suite.msKeeper.SetBondWeight(suite.ctx, MultiStakingDenomB, sdk.OneDec()) + bondAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(1000)) userBalance := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(10000)) err := simapp.FundAccount(suite.app, suite.ctx, delAddr, sdk.NewCoins(userBalance, sdk.NewCoin(MultiStakingDenomB, sdk.NewInt(10000)))) From 794786afe8c517e039534b10fd53f0900cba8ee2 Mon Sep 17 00:00:00 2001 From: neitdung Date: Mon, 22 Jan 2024 00:14:41 +0700 Subject: [PATCH 08/11] Refactor cancel unbonding msg function --- x/multi-staking/keeper/unlock.go | 29 ++++---------------- x/multi-staking/types/unlock.go | 46 +++++++++++++++++++++----------- 2 files changed, 36 insertions(+), 39 deletions(-) diff --git a/x/multi-staking/keeper/unlock.go b/x/multi-staking/keeper/unlock.go index 3dea0f4f..97b1c4fa 100644 --- a/x/multi-staking/keeper/unlock.go +++ b/x/multi-staking/keeper/unlock.go @@ -77,38 +77,19 @@ func (k Keeper) DecreaseUnlockEntryAmount( amount math.Int, creationHeight int64, ) (types.MultiStakingCoin, error) { unlockRecord, found := k.GetMultiStakingUnlock(ctx, unlockID) - if !found { return types.MultiStakingCoin{}, fmt.Errorf("not found unlock recored") } - var ( - unlockEntry types.UnlockEntry - unlockEntryIndex = -1 - ) - - for i, entry := range unlockRecord.Entries { - if entry.CreationHeight == creationHeight { - unlockEntry = entry - unlockEntryIndex = i - break - } - } + unlockEntryIndex := unlockRecord.FindEntryIndexByHeight(creationHeight) if unlockEntryIndex == -1 { return types.MultiStakingCoin{}, fmt.Errorf("unbonding delegation entry is not found at block height %d", creationHeight) } - if unlockEntry.UnlockingCoin.Amount.LT(amount) { - return types.MultiStakingCoin{}, fmt.Errorf("amount is greater than the unbonding delegation entry balance") - } - - updatedEntryAmt := unlockEntry.UnlockingCoin.Amount.Sub(amount) - if updatedEntryAmt.IsZero() { - unlockRecord.RemoveEntry(unlockEntryIndex) - } else { - // update the unlocking entry amount for unlocking entry - unlockEntry.UnlockingCoin.Amount = updatedEntryAmt - unlockRecord.Entries[unlockEntryIndex] = unlockEntry + unlockEntry := unlockRecord.Entries[unlockEntryIndex] + err := unlockRecord.RemoveCoinFromEntry(unlockEntryIndex, amount) + if err != nil { + return types.MultiStakingCoin{}, err } // set the unlocking record or remove it if there are no more entries diff --git a/x/multi-staking/types/unlock.go b/x/multi-staking/types/unlock.go index aeb1c3fd..1c5d95e4 100644 --- a/x/multi-staking/types/unlock.go +++ b/x/multi-staking/types/unlock.go @@ -37,23 +37,28 @@ func NewMultiStakingUnlock( } } -// AddEntry - append entry to the unbonding delegation -func (unlock *MultiStakingUnlock) AddEntry(creationHeight int64, weightedCoin MultiStakingCoin) { - // Check the entries exists with creation_height and complete_time +func (unlock *MultiStakingUnlock) FindEntryIndexByHeight(creationHeight int64) int { entryIndex := -1 - for index, ubdEntry := range unlock.Entries { - if ubdEntry.CreationHeight == creationHeight { + for index, unlockEntry := range unlock.Entries { + if unlockEntry.CreationHeight == creationHeight { entryIndex = index break } } + return entryIndex +} + +// AddEntry - append entry to the unbonding delegation +func (unlock *MultiStakingUnlock) AddEntry(creationHeight int64, weightedCoin MultiStakingCoin) { + // Check the entries exists with creation_height and complete_time + entryIndex := unlock.FindEntryIndexByHeight(creationHeight) // entryIndex exists if entryIndex != -1 { - ubdEntry := unlock.Entries[entryIndex] - ubdEntry.UnlockingCoin = ubdEntry.UnlockingCoin.Add(weightedCoin) + unlockEntry := unlock.Entries[entryIndex] + unlockEntry.UnlockingCoin = unlockEntry.UnlockingCoin.Add(weightedCoin) // update the entry - unlock.Entries[entryIndex] = ubdEntry + unlock.Entries[entryIndex] = unlockEntry } else { // append the new unbond delegation entry entry := NewUnlockEntry(creationHeight, weightedCoin) @@ -61,6 +66,23 @@ func (unlock *MultiStakingUnlock) AddEntry(creationHeight int64, weightedCoin Mu } } +// RemoveCoinFromEntry - remove multi staking coin from unlocking entry +func (unlock *MultiStakingUnlock) RemoveCoinFromEntry(entryIndex int, amount math.Int) error { + unlockEntry := unlock.Entries[entryIndex] + if unlockEntry.UnlockingCoin.Amount.LT(amount) { + return fmt.Errorf("cancel amount is greater than the unlocking entry amount") + } + + updatedAmount := unlockEntry.UnlockingCoin.Amount.Sub(amount) + if updatedAmount.IsZero() { + unlock.RemoveEntry(entryIndex) + } else { + unlock.Entries[entryIndex].UnlockingCoin.Amount = updatedAmount + } + + return nil +} + // RemoveEntry - remove entry at index i to the multi staking unlock func (unlock *MultiStakingUnlock) RemoveEntry(i int) { unlock.Entries = append(unlock.Entries[:i], unlock.Entries[i+1:]...) @@ -69,13 +91,7 @@ func (unlock *MultiStakingUnlock) RemoveEntry(i int) { // RemoveEntryAtCreationHeight - remove entry at creation height to the multi staking unlock func (unlock *MultiStakingUnlock) RemoveEntryAtCreationHeight(creationHeight int64) { // Check the entries exists with creation_height and complete_time - entryIndex := -1 - for index, ubdEntry := range unlock.Entries { - if ubdEntry.CreationHeight == creationHeight { - entryIndex = index - break - } - } + entryIndex := unlock.FindEntryIndexByHeight(creationHeight) // entryIndex exists if entryIndex != -1 { unlock.RemoveEntry(entryIndex) From 654fc87400179fd30851d2ef5e4eafd156192ccc Mon Sep 17 00:00:00 2001 From: neitdung Date: Mon, 22 Jan 2024 00:43:52 +0700 Subject: [PATCH 09/11] fix-lint --- x/multi-staking/types/unlock.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/multi-staking/types/unlock.go b/x/multi-staking/types/unlock.go index 1c5d95e4..5d5b79b5 100644 --- a/x/multi-staking/types/unlock.go +++ b/x/multi-staking/types/unlock.go @@ -79,7 +79,7 @@ func (unlock *MultiStakingUnlock) RemoveCoinFromEntry(entryIndex int, amount mat } else { unlock.Entries[entryIndex].UnlockingCoin.Amount = updatedAmount } - + return nil } From ba4d17b7fb9b95915eb66caa8212f82fa3cadd64 Mon Sep 17 00:00:00 2001 From: dzungdo Date: Tue, 23 Jan 2024 17:16:15 +0700 Subject: [PATCH 10/11] Add more checks to staking module --- x/multi-staking/keeper/keeper_test.go | 19 +- x/multi-staking/keeper/msg_server_test.go | 324 ++++++++++++++-------- x/multi-staking/types/unlock.go | 4 +- 3 files changed, 227 insertions(+), 120 deletions(-) diff --git a/x/multi-staking/keeper/keeper_test.go b/x/multi-staking/keeper/keeper_test.go index 4cb83774..9a836d02 100644 --- a/x/multi-staking/keeper/keeper_test.go +++ b/x/multi-staking/keeper/keeper_test.go @@ -3,6 +3,8 @@ package keeper_test import ( "testing" + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/realio-tech/multi-staking-module/testing/simapp" multistakingkeeper "github.com/realio-tech/multi-staking-module/x/multi-staking/keeper" "github.com/stretchr/testify/suite" @@ -14,16 +16,18 @@ import ( type KeeperTestSuite struct { suite.Suite - app *simapp.SimApp - ctx sdk.Context - msKeeper *multistakingkeeper.Keeper + app *simapp.SimApp + ctx sdk.Context + msKeeper *multistakingkeeper.Keeper + msgServer stakingtypes.MsgServer } func (suite *KeeperTestSuite) SetupTest() { app := simapp.Setup(false) ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + multiStakingMsgServer := multistakingkeeper.NewMsgServerImpl(app.MultiStakingKeeper) - suite.app, suite.ctx, suite.msKeeper = app, ctx, &app.MultiStakingKeeper + suite.app, suite.ctx, suite.msKeeper, suite.msgServer = app, ctx, &app.MultiStakingKeeper, multiStakingMsgServer } func TestKeeperTestSuite(t *testing.T) { @@ -31,3 +35,10 @@ func TestKeeperTestSuite(t *testing.T) { } // Todo: add CheckBalance; AddAccountWithCoin; FundAccount +func (suite *KeeperTestSuite) FundAccount(addr sdk.AccAddress, amounts sdk.Coins) error { + err := suite.app.BankKeeper.MintCoins(suite.ctx, minttypes.ModuleName, amounts) + if err != nil { + return err + } + return suite.app.BankKeeper.SendCoinsFromModuleToAccount(suite.ctx, minttypes.ModuleName, addr, amounts) +} diff --git a/x/multi-staking/keeper/msg_server_test.go b/x/multi-staking/keeper/msg_server_test.go index af43cb51..2da5ddbf 100644 --- a/x/multi-staking/keeper/msg_server_test.go +++ b/x/multi-staking/keeper/msg_server_test.go @@ -3,7 +3,6 @@ package keeper_test import ( "time" - "github.com/realio-tech/multi-staking-module/testing/simapp" "github.com/realio-tech/multi-staking-module/testutil" multistakingkeeper "github.com/realio-tech/multi-staking-module/x/multi-staking/keeper" multistakingtypes "github.com/realio-tech/multi-staking-module/x/multi-staking/types" @@ -98,6 +97,7 @@ func (suite *KeeperTestSuite) TestCreateValidator() { { name: "invalid bond token", malleate: func(ctx sdk.Context, msKeeper *multistakingkeeper.Keeper, msgServer stakingtypes.MsgServer) (sdk.Coin, error) { + msKeeper.RemoveBondWeight(ctx, MultiStakingDenomA) bondAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(25)) msg := stakingtypes.MsgCreateValidator{ @@ -159,10 +159,10 @@ func (suite *KeeperTestSuite) TestCreateValidator() { suite.Run(tc.name, func() { suite.SetupTest() valCoins := sdk.NewCoins(sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(10000)), sdk.NewCoin(MultiStakingDenomB, sdk.NewInt(10000))) - err := simapp.FundAccount(suite.app, suite.ctx, delAddr, valCoins) + err := suite.FundAccount(delAddr, valCoins) suite.Require().NoError(err) - bondAmount, err := tc.malleate(suite.ctx, suite.msKeeper, multistakingkeeper.NewMsgServerImpl(*suite.msKeeper)) + bondAmount, err := tc.malleate(suite.ctx, suite.msKeeper, suite.msgServer) if tc.expErr { suite.Require().Error(err) } else { @@ -338,10 +338,9 @@ func (suite *KeeperTestSuite) TestEditValidator() { newParam := stakingtypes.DefaultParams() newParam.MinCommissionRate = sdk.MustNewDecFromStr("0.02") suite.app.StakingKeeper.SetParams(suite.ctx, newParam) - msgServer := multistakingkeeper.NewMsgServerImpl(*suite.msKeeper) suite.msKeeper.SetBondWeight(suite.ctx, MultiStakingDenomA, sdk.OneDec()) bondAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(1000)) - err := simapp.FundAccount(suite.app, suite.ctx, delAddr, sdk.NewCoins(bondAmount)) + err := suite.FundAccount(delAddr, sdk.NewCoins(bondAmount)) suite.Require().NoError(err) createMsg := stakingtypes.MsgCreateValidator{ @@ -363,11 +362,11 @@ func (suite *KeeperTestSuite) TestEditValidator() { Pubkey: codectypes.UnsafePackAny(valPubKey), Value: bondAmount, } - _, err = msgServer.CreateValidator(suite.ctx, &createMsg) + _, err = suite.msgServer.CreateValidator(suite.ctx, &createMsg) suite.Require().NoError(err) suite.ctx = suite.ctx.WithBlockHeader(tmproto.Header{Time: time.Now()}) - originMsg, err := tc.malleate(suite.ctx, msgServer) + originMsg, err := tc.malleate(suite.ctx, suite.msgServer) if tc.expErr { suite.Require().Error(err) @@ -386,63 +385,64 @@ func (suite *KeeperTestSuite) TestEditValidator() { func (suite *KeeperTestSuite) TestDelegate() { delAddr := testutil.GenAddress() + valDelAddr := testutil.GenAddress() valPubKey := testutil.GenPubKey() valAddr := sdk.ValAddress(valPubKey.Address()) testCases := []struct { name string - malleate func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (stakingtypes.MsgDelegate, error) + malleate func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (sdk.Coin, error) expRate sdk.Dec expErr bool }{ { name: "success and not change rate", - malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (stakingtypes.MsgDelegate, error) { - bondAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(1000)) - delMsg := stakingtypes.NewMsgDelegate(delAddr, valAddr, bondAmount) + malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (sdk.Coin, error) { + multiStakingAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(1000)) + delMsg := stakingtypes.NewMsgDelegate(delAddr, valAddr, multiStakingAmount) _, err := msgServer.Delegate(ctx, delMsg) - return *delMsg, err + return multiStakingAmount, err }, expRate: sdk.OneDec(), expErr: false, }, { - name: "rate change from 1 to 0.75 (1000 / 1 + 2000 / 0.5 = 3000 / 0.75)", - malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (stakingtypes.MsgDelegate, error) { - bondAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(1000)) - delMsg := stakingtypes.NewMsgDelegate(delAddr, valAddr, bondAmount) + name: "rate change from 1 to 0.75 (1000 * 1 + 3000 * 0.5 = 4000 * 0.625)", + malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (sdk.Coin, error) { + multiStakingAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(1000)) + delMsg := stakingtypes.NewMsgDelegate(delAddr, valAddr, multiStakingAmount) _, err := msgServer.Delegate(ctx, delMsg) if err != nil { - return *delMsg, err + return multiStakingAmount, err } msKeeper.SetBondWeight(ctx, MultiStakingDenomA, sdk.MustNewDecFromStr("0.5")) - bondAmount1 := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(2000)) - delMsg1 := stakingtypes.NewMsgDelegate(delAddr, valAddr, bondAmount1) + multiStakingAmount1 := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(3000)) + delMsg1 := stakingtypes.NewMsgDelegate(delAddr, valAddr, multiStakingAmount1) _, err = msgServer.Delegate(ctx, delMsg1) - return *delMsg, err + return multiStakingAmount.Add(multiStakingAmount1), err }, - expRate: sdk.MustNewDecFromStr("0.75"), + expRate: sdk.MustNewDecFromStr("0.625"), expErr: false, }, { name: "not found validator", - malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (stakingtypes.MsgDelegate, error) { - bondAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(1000)) + malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (sdk.Coin, error) { + multiStakingAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(1000)) - delMsg := stakingtypes.NewMsgDelegate(delAddr, testutil.GenValAddress(), bondAmount) + delMsg := stakingtypes.NewMsgDelegate(delAddr, testutil.GenValAddress(), multiStakingAmount) _, err := msgServer.Delegate(ctx, delMsg) - return *delMsg, err + return multiStakingAmount, err }, expErr: true, }, { name: "not allow token", - malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (stakingtypes.MsgDelegate, error) { - bondAmount := sdk.NewCoin(MultiStakingDenomB, sdk.NewInt(1000)) + malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (sdk.Coin, error) { + multiStakingAmount := sdk.NewCoin(MultiStakingDenomB, sdk.NewInt(1000)) - delMsg := stakingtypes.NewMsgDelegate(delAddr, valAddr, bondAmount) + delMsg := stakingtypes.NewMsgDelegate(delAddr, valAddr, multiStakingAmount) _, err := msgServer.Delegate(ctx, delMsg) - return *delMsg, err + return multiStakingAmount, err }, expErr: true, }, @@ -455,11 +455,12 @@ func (suite *KeeperTestSuite) TestDelegate() { newParam := stakingtypes.DefaultParams() newParam.MinCommissionRate = sdk.MustNewDecFromStr("0.02") suite.app.StakingKeeper.SetParams(suite.ctx, newParam) - msgServer := multistakingkeeper.NewMsgServerImpl(*suite.msKeeper) suite.msKeeper.SetBondWeight(suite.ctx, MultiStakingDenomA, sdk.OneDec()) bondAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(1000)) userBalance := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(10000)) - err := simapp.FundAccount(suite.app, suite.ctx, delAddr, sdk.NewCoins(userBalance)) + err := suite.FundAccount(delAddr, sdk.NewCoins(userBalance)) + suite.Require().NoError(err) + err = suite.FundAccount(valDelAddr, sdk.NewCoins(userBalance)) suite.Require().NoError(err) createMsg := stakingtypes.MsgCreateValidator{ @@ -476,15 +477,15 @@ func (suite *KeeperTestSuite) TestDelegate() { MaxChangeRate: sdk.MustNewDecFromStr("0.05"), }, MinSelfDelegation: sdk.NewInt(200), - DelegatorAddress: delAddr.String(), + DelegatorAddress: valDelAddr.String(), ValidatorAddress: valAddr.String(), Pubkey: codectypes.UnsafePackAny(valPubKey), Value: bondAmount, } - _, err = msgServer.CreateValidator(suite.ctx, &createMsg) + _, err = suite.msgServer.CreateValidator(suite.ctx, &createMsg) suite.Require().NoError(err) - _, err = tc.malleate(suite.ctx, msgServer, *suite.msKeeper) + multiStakingAmount, err := tc.malleate(suite.ctx, suite.msgServer, *suite.msKeeper) if tc.expErr { suite.Require().Error(err) @@ -494,6 +495,16 @@ func (suite *KeeperTestSuite) TestDelegate() { lockRecord, found := suite.msKeeper.GetMultiStakingLock(suite.ctx, lockId) suite.Require().True(found) suite.Require().Equal(tc.expRate, lockRecord.GetBondWeight()) + + delegation, found := suite.app.StakingKeeper.GetDelegation(suite.ctx, delAddr, valAddr) + suite.Require().True(found) + validator, found := suite.app.StakingKeeper.GetValidator(suite.ctx, valAddr) + suite.Require().True(found) + + multiStakingCoin := multistakingtypes.NewMultiStakingCoin(multiStakingAmount.Denom, multiStakingAmount.Amount, tc.expRate) + expShares, err := validator.SharesFromTokens(multiStakingCoin.BondValue()) + suite.Require().NoError(err) + suite.Require().Equal(expShares, delegation.GetShares()) } }) } @@ -501,6 +512,7 @@ func (suite *KeeperTestSuite) TestDelegate() { func (suite *KeeperTestSuite) TestBeginRedelegate() { delAddr := testutil.GenAddress() + valDelAddr := testutil.GenAddress() valPubKey1 := testutil.GenPubKey() valPubKey2 := testutil.GenPubKey() @@ -509,69 +521,85 @@ func (suite *KeeperTestSuite) TestBeginRedelegate() { testCases := []struct { name string - malleate func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (stakingtypes.MsgBeginRedelegate, error) + malleate func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) ([]sdk.Coin, error) expRate []sdk.Dec expLock []math.Int expErr bool }{ { name: "redelegate from val1 to val2", - malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (stakingtypes.MsgBeginRedelegate, error) { - bondAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(500)) - multiStakingMsg := stakingtypes.NewMsgBeginRedelegate(delAddr, valAddr1, valAddr2, bondAmount) - _, err := msgServer.BeginRedelegate(ctx, multiStakingMsg) - return *multiStakingMsg, err + malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) ([]sdk.Coin, error) { + multiStakingAmount1 := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(1000)) + delMsg := stakingtypes.NewMsgDelegate(delAddr, valAddr1, multiStakingAmount1) + _, err := msgServer.Delegate(ctx, delMsg) + suite.Require().NoError(err) + + multiStakingAmount2 := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(500)) + redelegateMsg := stakingtypes.NewMsgBeginRedelegate(delAddr, valAddr1, valAddr2, multiStakingAmount2) + _, err = msgServer.BeginRedelegate(ctx, redelegateMsg) + return []sdk.Coin{multiStakingAmount1.Sub(multiStakingAmount2), multiStakingAmount2}, err }, expRate: []sdk.Dec{sdk.OneDec(), sdk.OneDec()}, - expLock: []math.Int{sdk.NewInt(500), sdk.NewInt(1500)}, + expLock: []math.Int{sdk.NewInt(500), sdk.NewInt(500)}, expErr: false, }, { - name: "delegate 500 more to val1 then change rate and redelegate to val2", - malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (stakingtypes.MsgBeginRedelegate, error) { + name: "delegate 2000 more to val1 then change rate and redelegate 600 to val2", + malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) ([]sdk.Coin, error) { + multiStakingAmount1 := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(1000)) + delMsg1 := stakingtypes.NewMsgDelegate(delAddr, valAddr1, multiStakingAmount1) + _, err := msgServer.Delegate(ctx, delMsg1) + suite.Require().NoError(err) + + multiStakingAmount2 := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(1000)) + delMsg3 := stakingtypes.NewMsgDelegate(delAddr, valAddr2, multiStakingAmount2) + _, err = msgServer.Delegate(ctx, delMsg3) + suite.Require().NoError(err) + msKeeper.SetBondWeight(ctx, MultiStakingDenomA, sdk.MustNewDecFromStr("0.25")) - bondAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(500)) + multiStakingAmount3 := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(2000)) + delMsg2 := stakingtypes.NewMsgDelegate(delAddr, valAddr1, multiStakingAmount3) + _, err = msgServer.Delegate(ctx, delMsg2) + suite.Require().NoError(err) - delMsg := stakingtypes.NewMsgDelegate(delAddr, valAddr1, bondAmount) - _, err := msgServer.Delegate(ctx, delMsg) - bondAmount1 := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(1000)) - redelMsg := stakingtypes.NewMsgBeginRedelegate(delAddr, valAddr1, valAddr2, bondAmount1) + multiStakingAmount4 := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(600)) + redelMsg := stakingtypes.NewMsgBeginRedelegate(delAddr, valAddr1, valAddr2, multiStakingAmount4) if err != nil { - return *redelMsg, err + return []sdk.Coin{}, err } _, err = msgServer.BeginRedelegate(ctx, redelMsg) - return *redelMsg, err + return []sdk.Coin{multiStakingAmount1.Add(multiStakingAmount3).Sub(multiStakingAmount4), multiStakingAmount2.Add(multiStakingAmount4)}, err }, - expRate: []sdk.Dec{sdk.MustNewDecFromStr("0.75"), sdk.MustNewDecFromStr("0.875")}, - expLock: []math.Int{sdk.NewInt(500), sdk.NewInt(2000)}, + expRate: []sdk.Dec{sdk.MustNewDecFromStr("0.5"), sdk.MustNewDecFromStr("0.8125")}, + expLock: []math.Int{sdk.NewInt(2400), sdk.NewInt(1600)}, expErr: false, }, { name: "not found validator", - malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (stakingtypes.MsgBeginRedelegate, error) { + malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) ([]sdk.Coin, error) { bondAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(500)) multiStakingMsg := stakingtypes.NewMsgBeginRedelegate(delAddr, valAddr1, testutil.GenValAddress(), bondAmount) _, err := msgServer.BeginRedelegate(ctx, multiStakingMsg) - return *multiStakingMsg, err + return []sdk.Coin{}, err }, expErr: true, }, { name: "not allow token", - malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (stakingtypes.MsgBeginRedelegate, error) { + malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) ([]sdk.Coin, error) { bondAmount := sdk.NewCoin(MultiStakingDenomB, sdk.NewInt(1000)) multiStakingMsg := stakingtypes.NewMsgBeginRedelegate(delAddr, valAddr1, valAddr2, bondAmount) _, err := msgServer.BeginRedelegate(ctx, multiStakingMsg) - return *multiStakingMsg, err + return []sdk.Coin{}, err }, expErr: true, }, { name: "setup val3 with bond denom is arst then redelgate from val1 to val3", - malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (stakingtypes.MsgBeginRedelegate, error) { + malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) ([]sdk.Coin, error) { valPubKey3 := testutil.GenPubKey() bondAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(500)) valAddr3 := sdk.ValAddress(valPubKey3.Address()) @@ -581,7 +609,7 @@ func (suite *KeeperTestSuite) TestBeginRedelegate() { multiStakingMsg := stakingtypes.NewMsgBeginRedelegate(delAddr, valAddr1, valAddr3, bondAmount) _, err = msgServer.BeginRedelegate(ctx, multiStakingMsg) - return *multiStakingMsg, err + return []sdk.Coin{}, err }, expRate: []sdk.Dec{}, expLock: []math.Int{}, @@ -596,13 +624,14 @@ func (suite *KeeperTestSuite) TestBeginRedelegate() { newParam := stakingtypes.DefaultParams() newParam.MinCommissionRate = sdk.MustNewDecFromStr("0.02") suite.app.StakingKeeper.SetParams(suite.ctx, newParam) - msgServer := multistakingkeeper.NewMsgServerImpl(*suite.msKeeper) suite.msKeeper.SetBondWeight(suite.ctx, MultiStakingDenomA, sdk.OneDec()) suite.msKeeper.SetBondWeight(suite.ctx, MultiStakingDenomB, sdk.OneDec()) bondAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(1000)) userBalance := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(10000)) - err := simapp.FundAccount(suite.app, suite.ctx, delAddr, sdk.NewCoins(userBalance, sdk.NewCoin(MultiStakingDenomB, sdk.NewInt(10000)))) + err := suite.FundAccount(delAddr, sdk.NewCoins(userBalance, sdk.NewCoin(MultiStakingDenomB, sdk.NewInt(10000)))) + suite.Require().NoError(err) + err = suite.FundAccount(valDelAddr, sdk.NewCoins(userBalance, sdk.NewCoin(MultiStakingDenomB, sdk.NewInt(10000)))) suite.Require().NoError(err) createMsg := stakingtypes.MsgCreateValidator{ @@ -619,7 +648,7 @@ func (suite *KeeperTestSuite) TestBeginRedelegate() { MaxChangeRate: sdk.MustNewDecFromStr("0.05"), }, MinSelfDelegation: sdk.NewInt(200), - DelegatorAddress: delAddr.String(), + DelegatorAddress: valDelAddr.String(), ValidatorAddress: valAddr1.String(), Pubkey: codectypes.UnsafePackAny(valPubKey1), Value: bondAmount, @@ -638,20 +667,20 @@ func (suite *KeeperTestSuite) TestBeginRedelegate() { MaxChangeRate: sdk.MustNewDecFromStr("0.1"), }, MinSelfDelegation: sdk.NewInt(200), - DelegatorAddress: delAddr.String(), + DelegatorAddress: valDelAddr.String(), ValidatorAddress: valAddr2.String(), Pubkey: codectypes.UnsafePackAny(valPubKey2), Value: bondAmount, } - _, err = msgServer.CreateValidator(suite.ctx, &createMsg) + _, err = suite.msgServer.CreateValidator(suite.ctx, &createMsg) suite.Require().NoError(err) - _, err = msgServer.CreateValidator(suite.ctx, &createMsg2) + _, err = suite.msgServer.CreateValidator(suite.ctx, &createMsg2) suite.Require().NoError(err) suite.ctx = suite.ctx.WithBlockHeader(tmproto.Header{Time: time.Now()}) - _, err = tc.malleate(suite.ctx, msgServer, *suite.msKeeper) + multiStakingAmounts, err := tc.malleate(suite.ctx, suite.msgServer, *suite.msKeeper) if tc.expErr { suite.Require().Error(err) @@ -663,11 +692,31 @@ func (suite *KeeperTestSuite) TestBeginRedelegate() { suite.Require().Equal(tc.expRate[0], lockRecord1.GetBondWeight()) suite.Require().Equal(tc.expLock[0], lockRecord1.LockedCoin.Amount) + delegation1, found := suite.app.StakingKeeper.GetDelegation(suite.ctx, delAddr, valAddr1) + suite.Require().True(found) + validator1, found := suite.app.StakingKeeper.GetValidator(suite.ctx, valAddr1) + suite.Require().True(found) + + multiStakingCoin1 := multistakingtypes.NewMultiStakingCoin(multiStakingAmounts[0].Denom, multiStakingAmounts[0].Amount, tc.expRate[0]) + expShares1, err := validator1.SharesFromTokens(multiStakingCoin1.BondValue()) + suite.Require().NoError(err) + suite.Require().Equal(expShares1, delegation1.GetShares()) + lockId2 := multistakingtypes.MultiStakingLockID(delAddr.String(), valAddr2.String()) lockRecord2, found := suite.msKeeper.GetMultiStakingLock(suite.ctx, lockId2) suite.Require().True(found) suite.Require().Equal(tc.expRate[1], lockRecord2.GetBondWeight()) suite.Require().Equal(tc.expLock[1], lockRecord2.LockedCoin.Amount) + + delegation2, found := suite.app.StakingKeeper.GetDelegation(suite.ctx, delAddr, valAddr2) + suite.Require().True(found) + validator2, found := suite.app.StakingKeeper.GetValidator(suite.ctx, valAddr2) + suite.Require().True(found) + + multiStakingCoin2 := multistakingtypes.NewMultiStakingCoin(multiStakingAmounts[1].Denom, multiStakingAmounts[1].Amount, tc.expRate[1]) + expShares2, err := validator2.SharesFromTokens(multiStakingCoin2.BondValue()) + suite.Require().NoError(err) + suite.Require().Equal(expShares2, delegation2.GetShares()) } }) } @@ -680,18 +729,18 @@ func (suite *KeeperTestSuite) TestUndelegate() { testCases := []struct { name string - malleate func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (*stakingtypes.MsgUndelegate, error) + malleate func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) error expUnlock math.Int expLock math.Int expErr bool }{ { name: "undelegate success", - malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (*stakingtypes.MsgUndelegate, error) { - bondAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(500)) - multiStakingMsg := stakingtypes.NewMsgUndelegate(delAddr, valAddr, bondAmount) + malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) error { + undelegateAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(500)) + multiStakingMsg := stakingtypes.NewMsgUndelegate(delAddr, valAddr, undelegateAmount) _, err := msgServer.Undelegate(ctx, multiStakingMsg) - return multiStakingMsg, err + return err }, expUnlock: sdk.NewInt(500), expLock: sdk.NewInt(500), @@ -699,17 +748,17 @@ func (suite *KeeperTestSuite) TestUndelegate() { }, { name: "undelegate 250 then undelegate 500", - malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (*stakingtypes.MsgUndelegate, error) { - bondAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(250)) - multiStakingMsg := stakingtypes.NewMsgUndelegate(delAddr, valAddr, bondAmount) + malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) error { + undelegateAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(250)) + multiStakingMsg := stakingtypes.NewMsgUndelegate(delAddr, valAddr, undelegateAmount) _, err := msgServer.Undelegate(ctx, multiStakingMsg) if err != nil { - return multiStakingMsg, err + return err } - bondAmount1 := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(500)) - multiStakingMsg1 := stakingtypes.NewMsgUndelegate(delAddr, valAddr, bondAmount1) + undelegateAmount1 := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(500)) + multiStakingMsg1 := stakingtypes.NewMsgUndelegate(delAddr, valAddr, undelegateAmount1) _, err = msgServer.Undelegate(ctx, multiStakingMsg1) - return multiStakingMsg1, err + return err }, expUnlock: sdk.NewInt(750), expLock: sdk.NewInt(250), @@ -717,22 +766,22 @@ func (suite *KeeperTestSuite) TestUndelegate() { }, { name: "not found validator", - malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (*stakingtypes.MsgUndelegate, error) { - bondAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(500)) - multiStakingMsg := stakingtypes.NewMsgUndelegate(delAddr, testutil.GenValAddress(), bondAmount) + malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) error { + undelegateAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(500)) + multiStakingMsg := stakingtypes.NewMsgUndelegate(delAddr, testutil.GenValAddress(), undelegateAmount) _, err := msgServer.Undelegate(ctx, multiStakingMsg) - return multiStakingMsg, err + return err }, expErr: true, }, { name: "not allow token", - malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (*stakingtypes.MsgUndelegate, error) { - bondAmount := sdk.NewCoin(MultiStakingDenomB, sdk.NewInt(1000)) + malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) error { + undelegateAmount := sdk.NewCoin(MultiStakingDenomB, sdk.NewInt(1000)) - multiStakingMsg := stakingtypes.NewMsgUndelegate(delAddr, testutil.GenValAddress(), bondAmount) + multiStakingMsg := stakingtypes.NewMsgUndelegate(delAddr, testutil.GenValAddress(), undelegateAmount) _, err := msgServer.Undelegate(ctx, multiStakingMsg) - return multiStakingMsg, err + return err }, expErr: true, }, @@ -745,11 +794,12 @@ func (suite *KeeperTestSuite) TestUndelegate() { newParam := stakingtypes.DefaultParams() newParam.MinCommissionRate = sdk.MustNewDecFromStr("0.02") suite.app.StakingKeeper.SetParams(suite.ctx, newParam) - msgServer := multistakingkeeper.NewMsgServerImpl(*suite.msKeeper) - suite.msKeeper.SetBondWeight(suite.ctx, MultiStakingDenomA, sdk.OneDec()) + + initialWeight := sdk.MustNewDecFromStr("0.5") + suite.msKeeper.SetBondWeight(suite.ctx, MultiStakingDenomA, initialWeight) bondAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(1000)) userBalance := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(10000)) - err := simapp.FundAccount(suite.app, suite.ctx, delAddr, sdk.NewCoins(userBalance, sdk.NewCoin(MultiStakingDenomB, sdk.NewInt(10000)))) + err := suite.FundAccount(delAddr, sdk.NewCoins(userBalance, sdk.NewCoin(MultiStakingDenomB, sdk.NewInt(10000)))) suite.Require().NoError(err) createMsg := stakingtypes.MsgCreateValidator{ @@ -772,26 +822,48 @@ func (suite *KeeperTestSuite) TestUndelegate() { Value: bondAmount, } - _, err = msgServer.CreateValidator(suite.ctx, &createMsg) + _, err = suite.msgServer.CreateValidator(suite.ctx, &createMsg) suite.Require().NoError(err) suite.ctx = suite.ctx.WithBlockHeader(tmproto.Header{Time: time.Now()}) - - _, err = tc.malleate(suite.ctx, msgServer, *suite.msKeeper) + curHeight := suite.ctx.BlockHeight() + err = tc.malleate(suite.ctx, suite.msgServer, *suite.msKeeper) if tc.expErr { suite.Require().Error(err) } else { suite.Require().NoError(err) - lockId1 := multistakingtypes.MultiStakingLockID(delAddr.String(), valAddr.String()) - lockRecord1, found := suite.msKeeper.GetMultiStakingLock(suite.ctx, lockId1) + lockId := multistakingtypes.MultiStakingLockID(delAddr.String(), valAddr.String()) + lockRecord, found := suite.msKeeper.GetMultiStakingLock(suite.ctx, lockId) + suite.Require().True(found) + suite.Require().Equal(tc.expLock, lockRecord.LockedCoin.Amount) + + delegation, found := suite.app.StakingKeeper.GetDelegation(suite.ctx, delAddr, valAddr) suite.Require().True(found) - suite.Require().Equal(tc.expLock, lockRecord1.LockedCoin.Amount) + validator, found := suite.app.StakingKeeper.GetValidator(suite.ctx, valAddr) + suite.Require().True(found) + + multiStakingCoin := multistakingtypes.NewMultiStakingCoin(MultiStakingDenomA, tc.expLock, initialWeight) + expShares, err := validator.SharesFromTokens(multiStakingCoin.BondValue()) + suite.Require().NoError(err) + suite.Require().Equal(expShares, delegation.GetShares()) unlockID := multistakingtypes.MultiStakingUnlockID(delAddr.String(), valAddr.String()) unbondRecord, found := suite.msKeeper.GetMultiStakingUnlock(suite.ctx, unlockID) suite.Require().True(found) suite.Require().Equal(tc.expUnlock, unbondRecord.Entries[0].UnlockingCoin.Amount) + + ubd, found := suite.app.StakingKeeper.GetUnbondingDelegation(suite.ctx, delAddr, valAddr) + suite.Require().True(found) + unlockStakingCoin := multistakingtypes.NewMultiStakingCoin(MultiStakingDenomA, tc.expUnlock, initialWeight) + totalUBDAmount := math.ZeroInt() + + for _, ubdEntry := range ubd.Entries { + if ubdEntry.CreationHeight == curHeight { + totalUBDAmount = totalUBDAmount.Add(ubdEntry.Balance) + } + } + suite.Require().Equal(unlockStakingCoin.BondValue(), totalUBDAmount) } }) } @@ -804,36 +876,36 @@ func (suite *KeeperTestSuite) TestCancelUnbondingDelegation() { testCases := []struct { name string - malleate func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (stakingtypes.MsgCancelUnbondingDelegation, error) + malleate func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (error) expUnlock math.Int expLock math.Int expErr bool }{ { - name: "undelegate success", - malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (stakingtypes.MsgCancelUnbondingDelegation, error) { + name: "cancel unbonding success", + malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (error) { cancelAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(500)) multiStakingMsg := stakingtypes.NewMsgCancelUnbondingDelegation(delAddr, valAddr, ctx.BlockHeight(), cancelAmount) _, err := msgServer.CancelUnbondingDelegation(ctx, multiStakingMsg) - return *multiStakingMsg, err + return err }, expUnlock: sdk.NewInt(500), expLock: sdk.NewInt(1500), expErr: false, }, { - name: "undelegate 250 then undelegate 500", - malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (stakingtypes.MsgCancelUnbondingDelegation, error) { + name: "cancel unbonding 250 then cancel unbonding 500", + malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (error) { cancelAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(250)) multiStakingMsg := stakingtypes.NewMsgCancelUnbondingDelegation(delAddr, valAddr, ctx.BlockHeight(), cancelAmount) _, err := msgServer.CancelUnbondingDelegation(ctx, multiStakingMsg) if err != nil { - return *multiStakingMsg, err + return err } cancelAmount1 := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(500)) multiStakingMsg1 := stakingtypes.NewMsgCancelUnbondingDelegation(delAddr, valAddr, ctx.BlockHeight(), cancelAmount1) _, err = msgServer.CancelUnbondingDelegation(ctx, multiStakingMsg1) - return *multiStakingMsg1, err + return err }, expUnlock: sdk.NewInt(250), expLock: sdk.NewInt(1750), @@ -841,32 +913,32 @@ func (suite *KeeperTestSuite) TestCancelUnbondingDelegation() { }, { name: "not found validator", - malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (stakingtypes.MsgCancelUnbondingDelegation, error) { + malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) ( error) { cancelAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(500)) multiStakingMsg := stakingtypes.NewMsgCancelUnbondingDelegation(delAddr, testutil.GenValAddress(), ctx.BlockHeight(), cancelAmount) _, err := msgServer.CancelUnbondingDelegation(ctx, multiStakingMsg) - return *multiStakingMsg, err + return err }, expErr: true, }, { name: "not allow token", - malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (stakingtypes.MsgCancelUnbondingDelegation, error) { + malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (error) { cancelAmount := sdk.NewCoin(MultiStakingDenomB, sdk.NewInt(1000)) multiStakingMsg := stakingtypes.NewMsgCancelUnbondingDelegation(delAddr, valAddr, ctx.BlockHeight(), cancelAmount) _, err := msgServer.CancelUnbondingDelegation(ctx, multiStakingMsg) - return *multiStakingMsg, err + return err }, expErr: true, }, { name: "not found entry at height 20", - malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (stakingtypes.MsgCancelUnbondingDelegation, error) { + malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (error) { cancelAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(500)) multiStakingMsg := stakingtypes.NewMsgCancelUnbondingDelegation(delAddr, valAddr, 20, cancelAmount) _, err := msgServer.CancelUnbondingDelegation(ctx, multiStakingMsg) - return *multiStakingMsg, err + return err }, expErr: true, }, @@ -879,11 +951,12 @@ func (suite *KeeperTestSuite) TestCancelUnbondingDelegation() { newParam := stakingtypes.DefaultParams() newParam.MinCommissionRate = sdk.MustNewDecFromStr("0.02") suite.app.StakingKeeper.SetParams(suite.ctx, newParam) - msgServer := multistakingkeeper.NewMsgServerImpl(*suite.msKeeper) - suite.msKeeper.SetBondWeight(suite.ctx, MultiStakingDenomA, sdk.OneDec()) + + initialWeight := sdk.MustNewDecFromStr("0.5") + suite.msKeeper.SetBondWeight(suite.ctx, MultiStakingDenomA, initialWeight) bondAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(2000)) userBalance := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(10000)) - err := simapp.FundAccount(suite.app, suite.ctx, delAddr, sdk.NewCoins(userBalance, sdk.NewCoin(MultiStakingDenomB, sdk.NewInt(10000)))) + err := suite.FundAccount(delAddr, sdk.NewCoins(userBalance, sdk.NewCoin(MultiStakingDenomB, sdk.NewInt(10000)))) suite.Require().NoError(err) createMsg := stakingtypes.MsgCreateValidator{ @@ -906,16 +979,17 @@ func (suite *KeeperTestSuite) TestCancelUnbondingDelegation() { Value: bondAmount, } - _, err = msgServer.CreateValidator(suite.ctx, &createMsg) + _, err = suite.msgServer.CreateValidator(suite.ctx, &createMsg) suite.Require().NoError(err) suite.ctx = suite.ctx.WithBlockHeader(tmproto.Header{Time: time.Now()}) + curHeight := suite.ctx.BlockHeight() unbondMsg := stakingtypes.NewMsgUndelegate(delAddr, valAddr, sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(1000))) - _, err = msgServer.Undelegate(suite.ctx, unbondMsg) + _, err = suite.msgServer.Undelegate(suite.ctx, unbondMsg) suite.Require().NoError(err) - _, err = tc.malleate(suite.ctx, msgServer, *suite.msKeeper) + err = tc.malleate(suite.ctx, suite.msgServer, *suite.msKeeper) if tc.expErr { suite.Require().Error(err) @@ -926,10 +1000,32 @@ func (suite *KeeperTestSuite) TestCancelUnbondingDelegation() { suite.Require().True(found) suite.Require().Equal(tc.expLock, lockRecord.LockedCoin.Amount) + delegation, found := suite.app.StakingKeeper.GetDelegation(suite.ctx, delAddr, valAddr) + suite.Require().True(found) + validator, found := suite.app.StakingKeeper.GetValidator(suite.ctx, valAddr) + suite.Require().True(found) + + multiStakingCoin := multistakingtypes.NewMultiStakingCoin(MultiStakingDenomA, tc.expLock, initialWeight) + expShares, err := validator.SharesFromTokens(multiStakingCoin.BondValue()) + suite.Require().NoError(err) + suite.Require().Equal(expShares, delegation.GetShares()) + unlockID := multistakingtypes.MultiStakingUnlockID(delAddr.String(), valAddr.String()) unbondRecord, found := suite.msKeeper.GetMultiStakingUnlock(suite.ctx, unlockID) suite.Require().True(found) suite.Require().Equal(tc.expUnlock, unbondRecord.Entries[0].UnlockingCoin.Amount) + + ubd, found := suite.app.StakingKeeper.GetUnbondingDelegation(suite.ctx, delAddr, valAddr) + suite.Require().True(found) + unlockStakingCoin := multistakingtypes.NewMultiStakingCoin(MultiStakingDenomA, tc.expUnlock, initialWeight) + totalUBDAmount := math.ZeroInt() + + for _, ubdEntry := range ubd.Entries { + if ubdEntry.CreationHeight == curHeight { + totalUBDAmount = totalUBDAmount.Add(ubdEntry.Balance) + } + } + suite.Require().Equal(unlockStakingCoin.BondValue(), totalUBDAmount) } }) } diff --git a/x/multi-staking/types/unlock.go b/x/multi-staking/types/unlock.go index 87cc4826..4e46e1af 100644 --- a/x/multi-staking/types/unlock.go +++ b/x/multi-staking/types/unlock.go @@ -51,7 +51,7 @@ func (unlock *MultiStakingUnlock) AddEntry(creationHeight int64, weightedCoin Mu // Check the entries exists with creation_height and complete_time entryIndex, found := unlock.FindEntryIndexByHeight(creationHeight) // entryIndex exists - if !found { + if found { unlockEntry := unlock.Entries[entryIndex] unlockEntry.UnlockingCoin = unlockEntry.UnlockingCoin.Add(weightedCoin) @@ -91,7 +91,7 @@ func (unlock *MultiStakingUnlock) RemoveEntryAtCreationHeight(creationHeight int // Check the entries exists with creation_height and complete_time entryIndex, found := unlock.FindEntryIndexByHeight(creationHeight) // entryIndex exists - if !found { + if found { unlock.RemoveEntry(entryIndex) } } From 9e03feeb2498762134cdd100d7c4734118d5d110 Mon Sep 17 00:00:00 2001 From: dzungdo Date: Tue, 23 Jan 2024 17:19:36 +0700 Subject: [PATCH 11/11] Fix linter error --- x/multi-staking/keeper/keeper_test.go | 4 ++-- x/multi-staking/keeper/msg_server_test.go | 13 ++++++------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/x/multi-staking/keeper/keeper_test.go b/x/multi-staking/keeper/keeper_test.go index 9a836d02..ed7ae664 100644 --- a/x/multi-staking/keeper/keeper_test.go +++ b/x/multi-staking/keeper/keeper_test.go @@ -3,14 +3,14 @@ package keeper_test import ( "testing" - minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/realio-tech/multi-staking-module/testing/simapp" multistakingkeeper "github.com/realio-tech/multi-staking-module/x/multi-staking/keeper" "github.com/stretchr/testify/suite" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" sdk "github.com/cosmos/cosmos-sdk/types" + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) type KeeperTestSuite struct { diff --git a/x/multi-staking/keeper/msg_server_test.go b/x/multi-staking/keeper/msg_server_test.go index 2da5ddbf..03ac211b 100644 --- a/x/multi-staking/keeper/msg_server_test.go +++ b/x/multi-staking/keeper/msg_server_test.go @@ -562,7 +562,6 @@ func (suite *KeeperTestSuite) TestBeginRedelegate() { _, err = msgServer.Delegate(ctx, delMsg2) suite.Require().NoError(err) - multiStakingAmount4 := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(600)) redelMsg := stakingtypes.NewMsgBeginRedelegate(delAddr, valAddr1, valAddr2, multiStakingAmount4) if err != nil { @@ -876,14 +875,14 @@ func (suite *KeeperTestSuite) TestCancelUnbondingDelegation() { testCases := []struct { name string - malleate func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (error) + malleate func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) error expUnlock math.Int expLock math.Int expErr bool }{ { name: "cancel unbonding success", - malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (error) { + malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) error { cancelAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(500)) multiStakingMsg := stakingtypes.NewMsgCancelUnbondingDelegation(delAddr, valAddr, ctx.BlockHeight(), cancelAmount) _, err := msgServer.CancelUnbondingDelegation(ctx, multiStakingMsg) @@ -895,7 +894,7 @@ func (suite *KeeperTestSuite) TestCancelUnbondingDelegation() { }, { name: "cancel unbonding 250 then cancel unbonding 500", - malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (error) { + malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) error { cancelAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(250)) multiStakingMsg := stakingtypes.NewMsgCancelUnbondingDelegation(delAddr, valAddr, ctx.BlockHeight(), cancelAmount) _, err := msgServer.CancelUnbondingDelegation(ctx, multiStakingMsg) @@ -913,7 +912,7 @@ func (suite *KeeperTestSuite) TestCancelUnbondingDelegation() { }, { name: "not found validator", - malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) ( error) { + malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) error { cancelAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(500)) multiStakingMsg := stakingtypes.NewMsgCancelUnbondingDelegation(delAddr, testutil.GenValAddress(), ctx.BlockHeight(), cancelAmount) _, err := msgServer.CancelUnbondingDelegation(ctx, multiStakingMsg) @@ -923,7 +922,7 @@ func (suite *KeeperTestSuite) TestCancelUnbondingDelegation() { }, { name: "not allow token", - malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (error) { + malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) error { cancelAmount := sdk.NewCoin(MultiStakingDenomB, sdk.NewInt(1000)) multiStakingMsg := stakingtypes.NewMsgCancelUnbondingDelegation(delAddr, valAddr, ctx.BlockHeight(), cancelAmount) @@ -934,7 +933,7 @@ func (suite *KeeperTestSuite) TestCancelUnbondingDelegation() { }, { name: "not found entry at height 20", - malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) (error) { + malleate: func(ctx sdk.Context, msgServer stakingtypes.MsgServer, msKeeper multistakingkeeper.Keeper) error { cancelAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(500)) multiStakingMsg := stakingtypes.NewMsgCancelUnbondingDelegation(delAddr, valAddr, 20, cancelAmount) _, err := msgServer.CancelUnbondingDelegation(ctx, multiStakingMsg)