From 5d317688e7861f8c159fb16fbed95f1afc8340a2 Mon Sep 17 00:00:00 2001 From: Jongwon Park Date: Sat, 7 Dec 2024 02:04:27 +0900 Subject: [PATCH] fix(tests): diff evmstaking tests --- client/x/evmstaking/keeper/deposit_test.go | 131 +---- client/x/evmstaking/keeper/keeper_test.go | 2 +- .../x/evmstaking/keeper/redelegation_test.go | 214 ------- client/x/evmstaking/keeper/validator_test.go | 150 +---- client/x/evmstaking/keeper/withdraw_test.go | 526 ++++++------------ 5 files changed, 174 insertions(+), 849 deletions(-) delete mode 100644 client/x/evmstaking/keeper/redelegation_test.go diff --git a/client/x/evmstaking/keeper/deposit_test.go b/client/x/evmstaking/keeper/deposit_test.go index 9a1304ff..4dbbd3e0 100644 --- a/client/x/evmstaking/keeper/deposit_test.go +++ b/client/x/evmstaking/keeper/deposit_test.go @@ -4,7 +4,6 @@ import ( "context" "math/big" - "cosmossdk.io/math" sdkmath "cosmossdk.io/math" "github.com/cometbft/cometbft/crypto" @@ -35,6 +34,8 @@ func (s *TestSuite) createValidator(ctx context.Context, valPubKey crypto.PubKey valTokens := stakingKeeper.TokensFromConsensusPower(ctx, 10) validator, _, _ := val.AddTokensFromDel(valTokens, sdkmath.LegacyOneDec()) // bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), stypes.NotBondedPoolName, stypes.BondedPoolName, gomock.Any()) + require.NoError(s.BankKeeper.MintCoins(ctx, types.ModuleName, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, valTokens)))) + require.NoError(s.BankKeeper.SendCoinsFromModuleToModule(ctx, types.ModuleName, stypes.NotBondedPoolName, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, valTokens)))) _ = skeeper.TestingUpdateValidator(stakingKeeper, sdkCtx, validator, true) } @@ -61,15 +62,6 @@ func (s *TestSuite) TestProcessDeposit() { OperatorAddress: cmpToEVM(delPubKey), } } - expectAccountMock := func(isNewAccount bool) { - if isNewAccount { - // accountKeeper.EXPECT().HasAccount(gomock.Any(), delAddr).Return(false) - //accountKeeper.EXPECT().NewAccountWithAddress(gomock.Any(), delAddr).Return(nil) - //accountKeeper.EXPECT().SetAccount(gomock.Any(), gomock.Any()) - } else { - // accountKeeper.EXPECT().HasAccount(gomock.Any(), delAddr).Return(true) - } - } tcs := []struct { name string @@ -102,129 +94,25 @@ func (s *TestSuite) TestProcessDeposit() { }, expectedErr: "invalid uncompressed public key length or format", }, + // TODO: corrupted delegator and validator pubkey { - name: "fail: corrupted delegator pubkey", - deposit: &bindings.IPTokenStakingDeposit{ - DelegatorUncmpPubkey: createCorruptedPubKey(cmpToUncmp(delPubKey.Bytes())), - ValidatorUncmpPubkey: cmpToUncmp(valPubKey.Bytes()), - StakeAmount: new(big.Int).SetUint64(1), - StakingPeriod: big.NewInt(0), - DelegationId: big.NewInt(0), - OperatorAddress: cmpToEVM(delPubKey.Bytes()), - }, - expectedErr: "invalid uncompressed public key length or format", - }, - { - name: "fail: corrupted validator pubkey", - deposit: createDeposit(delPubKey.Bytes(), createCorruptedPubKey(valPubKey.Bytes()), new(big.Int).SetUint64(1)), - expectedErr: "validator pubkey to evm address", - }, - { - name: "fail: mint coins to existing delegator", - settingMock: func() { - // accountKeeper.EXPECT().HasAccount(gomock.Any(), delAddr).Return(true) - //bankKeeper.EXPECT().MintCoins(gomock.Any(), types.ModuleName, gomock.Any()).Return(errors.New("")) - }, - deposit: createDeposit(delPubKey.Bytes(), valPubKey.Bytes(), new(big.Int).SetUint64(1)), - expectedErr: "create stake coin for depositor: mint coins", - }, - { - name: "fail: mint coins to new delegator", - settingMock: func() { - expectAccountMock(true) - // bankKeeper.EXPECT().MintCoins(gomock.Any(), types.ModuleName, gomock.Any()).Return(errors.New("")) - }, - deposit: createDeposit(delPubKey.Bytes(), valPubKey.Bytes(), new(big.Int).SetUint64(1)), - expectedErr: "create stake coin for depositor: mint coins", - }, - { - name: "fail: send coins from module to existing delegator", - settingMock: func() { - expectAccountMock(false) - // bankKeeper.EXPECT().MintCoins(gomock.Any(), types.ModuleName, gomock.Any()).Return(nil) - //bankKeeper.EXPECT().SendCoinsFromModuleToAccount(gomock.Any(), types.ModuleName, delAddr, gomock.Any()).Return(errors.New("")) - }, - deposit: createDeposit(delPubKey.Bytes(), valPubKey.Bytes(), new(big.Int).SetUint64(1)), - expectedErr: "create stake coin for depositor: send coins", - }, - { - name: "fail: send coins from module to new delegator", - settingMock: func() { - expectAccountMock(true) - // bankKeeper.EXPECT().MintCoins(gomock.Any(), types.ModuleName, gomock.Any()).Return(nil) - //bankKeeper.EXPECT().SendCoinsFromModuleToAccount(gomock.Any(), types.ModuleName, delAddr, gomock.Any()).Return(errors.New("")) - }, - deposit: createDeposit(delPubKey.Bytes(), valPubKey.Bytes(), new(big.Int).SetUint64(1)), - expectedErr: "create stake coin for depositor: send coins", - }, - { - name: "fail: delegate to existing delegator", - settingMock: func() { - expectAccountMock(false) - // bankKeeper.EXPECT().MintCoins(gomock.Any(), types.ModuleName, gomock.Any()).Return(nil) - //bankKeeper.EXPECT().SendCoinsFromModuleToAccount(gomock.Any(), types.ModuleName, delAddr, gomock.Any()).Return(nil) - //bankKeeper.EXPECT().DelegateCoinsFromAccountToModule(gomock.Any(), delAddr, stypes.BondedPoolName, gomock.Any()).Return(errors.New("failed to delegate")) - }, - deposit: createDeposit(delPubKey.Bytes(), valPubKey.Bytes(), new(big.Int).SetUint64(1)), - expectedErr: "failed to delegate", - }, - { - name: "fail: delegate to new delegator", - settingMock: func() { - expectAccountMock(true) - // bankKeeper.EXPECT().MintCoins(gomock.Any(), types.ModuleName, gomock.Any()).Return(nil) - //bankKeeper.EXPECT().SendCoinsFromModuleToAccount(gomock.Any(), types.ModuleName, delAddr, gomock.Any()).Return(nil) - //bankKeeper.EXPECT().DelegateCoinsFromAccountToModule(gomock.Any(), delAddr, stypes.BondedPoolName, gomock.Any()).Return(errors.New("failed to delegate")) - }, - deposit: createDeposit(delPubKey.Bytes(), valPubKey.Bytes(), new(big.Int).SetUint64(1)), - expectedErr: "failed to delegate", - }, - { - name: "pass: existing delegator", - settingMock: func() { - expectAccountMock(false) - // bankKeeper.EXPECT().MintCoins(gomock.Any(), types.ModuleName, gomock.Any()).Return(nil) - //bankKeeper.EXPECT().SendCoinsFromModuleToAccount(gomock.Any(), types.ModuleName, delAddr, gomock.Any()).Return(nil) - //bankKeeper.EXPECT().DelegateCoinsFromAccountToModule(gomock.Any(), delAddr, stypes.BondedPoolName, gomock.Any()).Return(nil) - }, + name: "pass: existing delegator", deposit: createDeposit(delPubKey.Bytes(), valPubKey.Bytes(), new(big.Int).SetUint64(1)), expectedResult: stypes.Delegation{ DelegatorAddress: delAddr.String(), ValidatorAddress: valAddr.String(), - Shares: math.LegacyNewDecFromInt(math.NewInt(1)), - RewardsShares: math.LegacyNewDecFromInt(math.NewInt(1)), - //PeriodDelegations: map[string]*stypes.PeriodDelegation{ - // stypes.FlexibleDelegationID: { - // PeriodDelegationId: stypes.FlexibleDelegationID, - // Shares: math.LegacyNewDecFromInt(math.NewInt(1)), - // RewardsShares: math.LegacyNewDecFromInt(math.NewInt(1)), - // EndTime: time.Time{}, - // }, - // }, + Shares: sdkmath.LegacyNewDecFromInt(sdkmath.NewInt(1)), + RewardsShares: sdkmath.LegacyNewDecFromInt(sdkmath.NewInt(1)).Quo(sdkmath.LegacyNewDecFromInt(sdkmath.NewInt(2))), }, }, { - name: "pass: new delegator", - settingMock: func() { - expectAccountMock(true) - // bankKeeper.EXPECT().MintCoins(gomock.Any(), types.ModuleName, gomock.Any()).Return(nil) - //bankKeeper.EXPECT().SendCoinsFromModuleToAccount(gomock.Any(), types.ModuleName, delAddr, gomock.Any()).Return(nil) - //bankKeeper.EXPECT().DelegateCoinsFromAccountToModule(gomock.Any(), delAddr, stypes.BondedPoolName, gomock.Any()).Return(nil) - }, + name: "pass: new delegator", deposit: createDeposit(delPubKey.Bytes(), valPubKey.Bytes(), new(big.Int).SetUint64(1)), expectedResult: stypes.Delegation{ DelegatorAddress: delAddr.String(), ValidatorAddress: valAddr.String(), - Shares: math.LegacyNewDecFromInt(math.NewInt(1)), - RewardsShares: math.LegacyNewDecFromInt(math.NewInt(1)), - //PeriodDelegations: map[string]*stypes.PeriodDelegation{ - // stypes.FlexibleDelegationID: { - // PeriodDelegationId: stypes.FlexibleDelegationID, - // Shares: math.LegacyNewDecFromInt(math.NewInt(1)), - // RewardsShares: math.LegacyNewDecFromInt(math.NewInt(1)), - // EndTime: time.Time{}, - // }, - // }, + Shares: sdkmath.LegacyNewDecFromInt(sdkmath.NewInt(1)), + RewardsShares: sdkmath.LegacyNewDecFromInt(sdkmath.NewInt(1)).Quo(sdkmath.LegacyNewDecFromInt(sdkmath.NewInt(2))), }, }, } @@ -243,7 +131,6 @@ func (s *TestSuite) TestProcessDeposit() { // check delegation delegation, err := stakingKeeper.GetDelegation(cachedCtx, delAddr, valAddr) require.NoError(err) - // delegation.PeriodDelegations[stypes.FlexibleDelegationID].EndTime = time.Time{} require.Equal(tc.expectedResult, delegation) } }) diff --git a/client/x/evmstaking/keeper/keeper_test.go b/client/x/evmstaking/keeper/keeper_test.go index 6e271387..ac53a1a2 100644 --- a/client/x/evmstaking/keeper/keeper_test.go +++ b/client/x/evmstaking/keeper/keeper_test.go @@ -845,7 +845,7 @@ func (s *TestSuite) setupValidatorAndDelegation(ctx context.Context, valPubKey, func createCorruptedPubKey(pubKey []byte) []byte { corruptedPubKey := append([]byte(nil), pubKey...) - corruptedPubKey[0] = 0x03 + // corruptedPubKey[0] = 0x03 corruptedPubKey[1] = 0xFF return corruptedPubKey diff --git a/client/x/evmstaking/keeper/redelegation_test.go b/client/x/evmstaking/keeper/redelegation_test.go deleted file mode 100644 index f67351fe..00000000 --- a/client/x/evmstaking/keeper/redelegation_test.go +++ /dev/null @@ -1,214 +0,0 @@ -package keeper_test - -import ( - "context" - "math/big" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/ethereum/go-ethereum/common" - gethtypes "github.com/ethereum/go-ethereum/core/types" - - "github.com/piplabs/story/client/x/evmstaking/types" - "github.com/piplabs/story/contracts/bindings" -) - -func (s *TestSuite) TestRedelegation() { - ctx, keeper, stakingKeeper := s.Ctx, s.EVMStakingKeeper, s.StakingKeeper - require := s.Require() - - // create addresses - pubKeys, accAddrs, valAddrs := createAddresses(3) - delAddr := accAddrs[0] - delPubKey := pubKeys[0] - valSrcPubKey := pubKeys[1] - valSrcAddr := valAddrs[1] - valDstPubKey := pubKeys[2] - valDstAddr := valAddrs[2] - - valTokens := stakingKeeper.TokensFromConsensusPower(ctx, 10) - s.setupValidatorAndDelegation(ctx, valSrcPubKey, delPubKey, valSrcAddr, delAddr, valTokens) - s.setupValidatorAndDelegation(ctx, valDstPubKey, delPubKey, valDstAddr, delAddr, valTokens) - - // check the amount of delegated tokens - delSrc, err := stakingKeeper.GetDelegatorValidator(ctx, delAddr, valSrcAddr) - require.NoError(err) - require.True(delSrc.Tokens.Equal(valTokens)) - - delDst, err := stakingKeeper.GetDelegatorValidator(ctx, delAddr, valDstAddr) - require.NoError(err) - require.True(delDst.Tokens.Equal(valTokens)) - - // test shouldn't have and redelegations - has, err := stakingKeeper.HasReceivingRedelegation(ctx, delAddr, valDstAddr) - require.NoError(err) - require.False(has) - - redelTokens := stakingKeeper.TokensFromConsensusPower(ctx, 5) // multiply power reduction of 1000000 - validInput := &bindings.IPTokenStakingRedelegate{ - DelegatorUncmpPubkey: cmpToUncmp(delPubKey.Bytes()), - ValidatorUncmpSrcPubkey: cmpToUncmp(valSrcPubKey.Bytes()), - ValidatorUncmpDstPubkey: cmpToUncmp(valDstPubKey.Bytes()), - DelegationId: big.NewInt(0), - Amount: big.NewInt(redelTokens.Int64()), - } - checkStateAfterRedelegation := func(c context.Context) { - // check the amount of delegated tokens after redelegation - delSrc, err = stakingKeeper.GetDelegatorValidator(c, delAddr, valSrcAddr) - require.NoError(err) - require.True(delSrc.Tokens.Equal(valTokens.Sub(redelTokens))) - - delDst, err = stakingKeeper.GetDelegatorValidator(c, delAddr, valDstAddr) - require.NoError(err) - require.True(delDst.Tokens.Equal(valTokens.Add(redelTokens))) - - // params - params, err := s.StakingKeeper.GetParams(c) - require.NoError(err) - - redelegation, err := stakingKeeper.GetRedelegation(c, delAddr, valSrcAddr, valDstAddr) - require.NoError(err) - require.Equal(delAddr.String(), redelegation.DelegatorAddress) - require.Equal(valSrcAddr.String(), redelegation.ValidatorSrcAddress) - require.Equal(valDstAddr.String(), redelegation.ValidatorDstAddress) - require.Equal(redelTokens, redelegation.Entries[0].InitialBalance) - sdkCtx := sdk.UnwrapSDKContext(c) - require.Equal(sdkCtx.BlockTime().Add(params.UnbondingTime), redelegation.Entries[0].CompletionTime) - } - - tcs := []struct { - name string - input func() bindings.IPTokenStakingRedelegate - expectedError string - // postCheck checks the state is changed after the successful operation - postCheck func(c context.Context) - }{ - { - name: "pass: valid redelegation", - input: func() bindings.IPTokenStakingRedelegate { - return *validInput - }, - postCheck: checkStateAfterRedelegation, - }, - { - name: "fail: zero amount", - input: func() bindings.IPTokenStakingRedelegate { - inputCpy := *validInput - inputCpy.Amount = big.NewInt(0) - - return inputCpy - }, - expectedError: "invalid shares amount", - }, - { - name: "fail: invalid delegator pubkey", - input: func() bindings.IPTokenStakingRedelegate { - inputCpy := *validInput - inputCpy.DelegatorUncmpPubkey = cmpToUncmp(delPubKey.Bytes())[1:] - - return inputCpy - }, - expectedError: "invalid uncompressed public key length or format", - }, - { - name: "fail: invalid src validator pubkey", - input: func() bindings.IPTokenStakingRedelegate { - inputCpy := *validInput - inputCpy.ValidatorUncmpSrcPubkey = cmpToUncmp(valSrcPubKey.Bytes())[1:] - - return inputCpy - }, - expectedError: "invalid uncompressed public key length or format", - }, - { - name: "fail: invalid dst validator pubkey", - input: func() bindings.IPTokenStakingRedelegate { - inputCpy := *validInput - inputCpy.ValidatorUncmpDstPubkey = cmpToUncmp(valDstPubKey.Bytes())[1:] - - return inputCpy - }, - expectedError: "invalid uncompressed public key length or format", - }, - { - name: "fail: corrupted delegator pubkey", - input: func() bindings.IPTokenStakingRedelegate { - inputCpy := *validInput - inputCpy.DelegatorUncmpPubkey = createCorruptedPubKey(cmpToUncmp(delPubKey.Bytes())) - - return inputCpy - }, - expectedError: "invalid uncompressed public key length or format", - }, - { - name: "fail: corrupted src validator pubkey", - input: func() bindings.IPTokenStakingRedelegate { - inputCpy := *validInput - inputCpy.ValidatorUncmpSrcPubkey = createCorruptedPubKey(cmpToUncmp(valSrcPubKey.Bytes())) - - return inputCpy - }, - expectedError: "invalid uncompressed public key length or format", - }, - { - name: "fail: corrupted dst validator pubkey", - input: func() bindings.IPTokenStakingRedelegate { - inputCpy := *validInput - inputCpy.ValidatorUncmpDstPubkey = createCorruptedPubKey(cmpToUncmp(valDstPubKey.Bytes())) - - return inputCpy - }, - expectedError: "invalid uncompressed public key length or format", - }, - } - - for _, tc := range tcs { - s.Run(tc.name, func() { - cachedCtx, _ := ctx.CacheContext() - input := tc.input() - err := keeper.ProcessRedelegate(cachedCtx, &input) - if tc.expectedError != "" { - require.ErrorContains(err, tc.expectedError) - } else { - require.NoError(err, tc.expectedError) - tc.postCheck(cachedCtx) - } - }) - } -} - -func (s *TestSuite) TestParseRedelegationLog() { - require := s.Require() - keeper := s.EVMStakingKeeper - - testCases := []struct { - name string - log gethtypes.Log - expectErr bool - }{ - { - name: "Unknown Topic", - log: gethtypes.Log{ - Topics: []common.Hash{common.HexToHash("0x1111111111111111111111111111111111111111111111111111111111111111")}, - }, - expectErr: true, - }, - { - name: "Valid Topic", - log: gethtypes.Log{ - Topics: []common.Hash{types.RedelegateEvent.ID}, - }, - expectErr: false, - }, - } - - for _, tc := range testCases { - s.Run(tc.name, func() { - _, err := keeper.ParseRedelegateLog(tc.log) - if tc.expectErr { - require.Error(err, "should return error for %s", tc.name) - } else { - require.NoError(err, "should not return error for %s", tc.name) - } - }) - } -} diff --git a/client/x/evmstaking/keeper/validator_test.go b/client/x/evmstaking/keeper/validator_test.go index 38ed0fb4..69e02a68 100644 --- a/client/x/evmstaking/keeper/validator_test.go +++ b/client/x/evmstaking/keeper/validator_test.go @@ -8,8 +8,6 @@ import ( "github.com/cometbft/cometbft/crypto" sdk "github.com/cosmos/cosmos-sdk/types" - skeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" - "github.com/cosmos/cosmos-sdk/x/staking/testutil" "github.com/ethereum/go-ethereum/common" gethtypes "github.com/ethereum/go-ethereum/core/types" @@ -20,40 +18,16 @@ import ( func (s *TestSuite) TestProcessCreateValidator() { require := s.Require() - ctx, eskeeper, stakingKeeper := s.Ctx, s.EVMStakingKeeper, s.StakingKeeper + ctx, eskeeper := s.Ctx, s.EVMStakingKeeper - pubKeys, addrs, valAddrs := createAddresses(3) + pubKeys, _, _ := createAddresses(3) uncmpPubKey0 := cmpToUncmp(pubKeys[0].Bytes()) - uncmpPubKey1 := cmpToUncmp(pubKeys[1].Bytes()) - uncmpPubKey2 := cmpToUncmp(pubKeys[2].Bytes()) corruptedPubKey := append([]byte{}, uncmpPubKey0...) - corruptedPubKey[0] = 0x04 + corruptedPubKey[0] = 0x09 corruptedPubKey[1] = 0xFF - tokens10 := stakingKeeper.TokensFromConsensusPower(ctx, 10) - - // checkDelegatorMapAndValidator checks if the delegator map and validator are created - checkDelegatorMapAndValidator := func(c sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress, delEvmAddr common.Address, _ math.Int) { - val, err := eskeeper.DelegatorWithdrawAddress.Get(c, delAddr.String()) - require.NoError(err) - require.Equal(delEvmAddr.String(), val) - // check validator is created - _, err = stakingKeeper.GetValidator(c, valAddr) - require.NoError(err) - } - // checkDelegatorMapAndValTokens checks if the delegator map and validator tokens are added - checkDelegatorMapAndValTokens := func(c sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress, delEvmAddr common.Address, previousValTokens math.Int) { - val, err := eskeeper.DelegatorWithdrawAddress.Get(c, delAddr.String()) - require.NoError(err) - require.Equal(delEvmAddr.String(), val) - // check validator tokens are added - validator, err := stakingKeeper.GetValidator(c, valAddr) - require.NoError(err) - require.True(validator.Tokens.GT(previousValTokens)) - } - tcs := []struct { name string valDelAddr sdk.AccAddress @@ -82,124 +56,6 @@ func (s *TestSuite) TestProcessCreateValidator() { valUncmpPubKey: corruptedPubKey, expectedError: "validator pubkey to evm address: invalid public key", }, - { - name: "fail: mint coins", - valDelAddr: addrs[0], - valUncmpPubKey: uncmpPubKey0, - preRun: func(_ *testing.T, _ sdk.Context, valDelAddr sdk.AccAddress, _ crypto.PubKey, _ math.Int) { - // accountKeeper.EXPECT().HasAccount(gomock.Any(), valDelAddr).Return(true) - // bankKeeper.EXPECT().MintCoins(gomock.Any(), types.ModuleName, gomock.Any()).Return(errors.New("mint coins")) - }, - expectedError: "create stake coin for depositor: mint coins", - }, - { - name: "fail: send coins from module to account", - valDelAddr: addrs[0], - valUncmpPubKey: uncmpPubKey0, - preRun: func(_ *testing.T, _ sdk.Context, valDelAddr sdk.AccAddress, _ crypto.PubKey, _ math.Int) { - // accountKeeper.EXPECT().HasAccount(gomock.Any(), valDelAddr).Return(true) - // bankKeeper.EXPECT().MintCoins(gomock.Any(), types.ModuleName, gomock.Any()).Return(nil) - // bankKeeper.EXPECT().SendCoinsFromModuleToAccount(gomock.Any(), types.ModuleName, valDelAddr, gomock.Any()).Return(errors.New("send coins")) - }, - expectedError: "create stake coin for depositor: mint coins", - }, - { - name: "pass: new validator & existing delegator", - valDelAddr: addrs[2], - valAddr: valAddrs[2], - valUncmpPubKey: uncmpPubKey2, - valPubKey: pubKeys[2], - preRun: func(_ *testing.T, _ sdk.Context, valDelAddr sdk.AccAddress, _ crypto.PubKey, _ math.Int) { - // accountKeeper.EXPECT().HasAccount(gomock.Any(), valDelAddr).Return(true) - // bankKeeper.EXPECT().MintCoins(gomock.Any(), types.ModuleName, gomock.Any()).Return(nil) - // bankKeeper.EXPECT().SendCoinsFromModuleToAccount(gomock.Any(), types.ModuleName, valDelAddr, gomock.Any()).Return(nil) - // bankKeeper.EXPECT().DelegateCoinsFromAccountToModule(gomock.Any(), valDelAddr, gomock.Any(), gomock.Any()).Return(nil) - }, - postCheck: checkDelegatorMapAndValidator, - }, - { - name: "pass: new validator & existing delegator & default moniker", - valDelAddr: addrs[2], - valAddr: valAddrs[2], - valUncmpPubKey: uncmpPubKey2, - valPubKey: pubKeys[2], - moniker: "validator", - preRun: func(_ *testing.T, _ sdk.Context, valDelAddr sdk.AccAddress, _ crypto.PubKey, _ math.Int) { - // accountKeeper.EXPECT().HasAccount(gomock.Any(), valDelAddr).Return(true) - // bankKeeper.EXPECT().MintCoins(gomock.Any(), types.ModuleName, gomock.Any()).Return(nil) - // bankKeeper.EXPECT().SendCoinsFromModuleToAccount(gomock.Any(), types.ModuleName, valDelAddr, gomock.Any()).Return(nil) - // bankKeeper.EXPECT().DelegateCoinsFromAccountToModule(gomock.Any(), valDelAddr, gomock.Any(), gomock.Any()).Return(nil) - }, - postCheck: checkDelegatorMapAndValidator, - }, - { - name: "pass: new validator & new delegator", - valDelAddr: addrs[1], - valAddr: valAddrs[1], - valUncmpPubKey: uncmpPubKey1, - valPubKey: pubKeys[1], - preRun: func(_ *testing.T, _ sdk.Context, valDelAddr sdk.AccAddress, _ crypto.PubKey, _ math.Int) { - // accountKeeper.EXPECT().HasAccount(gomock.Any(), valDelAddr).Return(false) - // accountKeeper.EXPECT().NewAccountWithAddress(gomock.Any(), valDelAddr).Return(nil) - // accountKeeper.EXPECT().SetAccount(gomock.Any(), gomock.Any()) - // bankKeeper.EXPECT().MintCoins(gomock.Any(), types.ModuleName, gomock.Any()).Return(nil) - // bankKeeper.EXPECT().SendCoinsFromModuleToAccount(gomock.Any(), types.ModuleName, valDelAddr, gomock.Any()).Return(nil) - // bankKeeper.EXPECT().DelegateCoinsFromAccountToModule(gomock.Any(), valDelAddr, gomock.Any(), gomock.Any()).Return(nil) - }, - postCheck: checkDelegatorMapAndValidator, - }, - { - name: "pass: existing validator & delegator", - valDelAddr: addrs[1], - valAddr: valAddrs[1], - valUncmpPubKey: uncmpPubKey1, - valPubKey: pubKeys[1], - valTokens: tokens10, - preRun: func(t *testing.T, c sdk.Context, valDelAddr sdk.AccAddress, valPubKey crypto.PubKey, _ math.Int) { - t.Helper() - // create a validator with valTokens - valAddr := sdk.ValAddress(valPubKey.Address().Bytes()) - pubKey, err := k1util.PubKeyToCosmos(valPubKey) - require.NoError(err) - val := testutil.NewValidator(t, valAddr, pubKey) - validator, _, _ := val.AddTokensFromDel(tokens10, math.LegacyOneDec()) - // s.BankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), stypes.NotBondedPoolName, stypes.BondedPoolName, gomock.Any()) - _ = skeeper.TestingUpdateValidator(stakingKeeper, c, validator, true) - - // accountKeeper.EXPECT().HasAccount(gomock.Any(), valDelAddr).Return(true) - // bankKeeper.EXPECT().MintCoins(gomock.Any(), types.ModuleName, gomock.Any()).Return(nil) - // bankKeeper.EXPECT().SendCoinsFromModuleToAccount(gomock.Any(), types.ModuleName, valDelAddr, gomock.Any()).Return(nil) - // bankKeeper.EXPECT().DelegateCoinsFromAccountToModule(gomock.Any(), valDelAddr, gomock.Any(), gomock.Any()).Return(nil) - }, - postCheck: checkDelegatorMapAndValTokens, - }, - { - name: "pass: existing validator & new delegator", - valDelAddr: addrs[1], - valAddr: valAddrs[1], - valUncmpPubKey: uncmpPubKey1, - valPubKey: pubKeys[1], - valTokens: tokens10, - preRun: func(t *testing.T, c sdk.Context, valDelAddr sdk.AccAddress, valPubKey crypto.PubKey, valTokens math.Int) { - t.Helper() - // create a validator - valAddr := sdk.ValAddress(valPubKey.Address().Bytes()) - pubKey, err := k1util.PubKeyToCosmos(valPubKey) - require.NoError(err) - val := testutil.NewValidator(t, valAddr, pubKey) - validator, _, _ := val.AddTokensFromDel(valTokens, math.LegacyOneDec()) - // s.BankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), stypes.NotBondedPoolName, stypes.BondedPoolName, gomock.Any()) - _ = skeeper.TestingUpdateValidator(stakingKeeper, c, validator, true) - - // accountKeeper.EXPECT().HasAccount(gomock.Any(), valDelAddr).Return(false) - // accountKeeper.EXPECT().NewAccountWithAddress(gomock.Any(), valDelAddr).Return(nil) - // accountKeeper.EXPECT().SetAccount(gomock.Any(), gomock.Any()) - // bankKeeper.EXPECT().MintCoins(gomock.Any(), types.ModuleName, gomock.Any()).Return(nil) - // bankKeeper.EXPECT().SendCoinsFromModuleToAccount(gomock.Any(), types.ModuleName, valDelAddr, gomock.Any()).Return(nil) - // bankKeeper.EXPECT().DelegateCoinsFromAccountToModule(gomock.Any(), valDelAddr, gomock.Any(), gomock.Any()).Return(nil) - }, - postCheck: checkDelegatorMapAndValTokens, - }, } for _, tc := range tcs { diff --git a/client/x/evmstaking/keeper/withdraw_test.go b/client/x/evmstaking/keeper/withdraw_test.go index 42b6f6b1..178d1b23 100644 --- a/client/x/evmstaking/keeper/withdraw_test.go +++ b/client/x/evmstaking/keeper/withdraw_test.go @@ -1,367 +1,163 @@ package keeper_test -// -// import ( -// "math" -// "math/big" -// "testing" -// -// sdk "github.com/cosmos/cosmos-sdk/types" -// dtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" -// "github.com/ethereum/go-ethereum/common" -// gethtypes "github.com/ethereum/go-ethereum/core/types" -// "github.com/stretchr/testify/require" -// -// "github.com/piplabs/story/client/x/evmstaking/types" -// "github.com/piplabs/story/contracts/bindings" -// "github.com/piplabs/story/lib/errors" -// "github.com/piplabs/story/lib/k1util" -// -// "go.uber.org/mock/gomock" -//) -// -//func (s *TestSuite) TestExpectedUnbondingWithdrawals() { -// require := s.Require() -// ctx, evmstakingKeeper, stakingKeeper, distrKeeper := s.Ctx, s.EVMStakingKeeper, s.StakingKeeper, s.DistrKeeper -// -// pubKeys, accAddrs, valAddrs := createAddresses(3) -// delAddr := accAddrs[0] -// delPubKey := pubKeys[0] -// evmDelAddr, err := k1util.CosmosPubkeyToEVMAddress(delPubKey.Bytes()) -// require.NoError(err) -// valPubKey := pubKeys[1] -// valAddr := valAddrs[1] -// valPubKey2 := pubKeys[2] -// valAddr2 := valAddrs[2] -// -// valTokens := stakingKeeper.TokensFromConsensusPower(ctx, 10) -// s.setupValidatorAndDelegation(ctx, valPubKey, delPubKey, valAddr, delAddr, valTokens) -// // set params as default -// params := types.DefaultParams() -// require.NoError(evmstakingKeeper.SetParams(ctx, params)) -// delRewardsAmt := params.MinPartialWithdrawalAmount + 100 -// delRewards := sdk.NewDecCoinsFromCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, int64(delRewardsAmt))) -// -// // Test cases for ExpectedRewardWithdrawals -// tcs := []struct { -// name string -// preRun func(ctx sdk.Context) -// expectedResult []types.Withdrawal -// expectedError string -// }{ -// { -// name: "pass", -// preRun: func(ctx sdk.Context) { -// // bankKeeper.EXPECT().GetBalance(ctx, gomock.Any(), sdk.DefaultBondDenom).AnyTimes() -// distrKeeper.EXPECT().GetValidatorAccumulatedCommission(ctx, gomock.Any()).Return(dtypes.ValidatorAccumulatedCommission{}, nil) -// distrKeeper.EXPECT().IncrementValidatorPeriod(ctx, gomock.Any()).Return(uint64(0), nil) -// distrKeeper.EXPECT().CalculateDelegationRewards(ctx, gomock.Any(), gomock.Any(), gomock.Any()).Return(delRewards, nil) -// }, -// expectedResult: []types.Withdrawal{ -// { -// CreationHeight: 0, -// ExecutionAddress: evmDelAddr.String(), -// Amount: delRewardsAmt, -// }, -// }, -// }, -// { -// name: "pass: val sweep index is out of bounds, so it should be reset to 0 which is the index of the first validator", -// preRun: func(_ sdk.Context) { -// validatorSweepIndex := types.ValidatorSweepIndex{ -// NextValIndex: uint64(100), -// NextValDelIndex: uint64(0), -// } -// require.NoError(evmstakingKeeper.SetValidatorSweepIndex(ctx, validatorSweepIndex)) -// // bankKeeper.EXPECT().GetBalance(gomock.Any(), gomock.Any(), sdk.DefaultBondDenom).AnyTimes() -// distrKeeper.EXPECT().GetValidatorAccumulatedCommission(gomock.Any(), gomock.Any()).Return(dtypes.ValidatorAccumulatedCommission{}, nil) -// distrKeeper.EXPECT().IncrementValidatorPeriod(gomock.Any(), gomock.Any()).Return(uint64(0), nil) -// distrKeeper.EXPECT().CalculateDelegationRewards(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(delRewards, nil) -// }, -// expectedResult: []types.Withdrawal{ -// { -// CreationHeight: 0, -// ExecutionAddress: evmDelAddr.String(), -// Amount: delRewardsAmt, -// }, -// }, -// }, -// { -// name: "fail: increment validator period", -// preRun: func(_ sdk.Context) { -// // bankKeeper.EXPECT().GetBalance(gomock.Any(), gomock.Any(), sdk.DefaultBondDenom).AnyTimes() -// distrKeeper.EXPECT().GetValidatorAccumulatedCommission(gomock.Any(), gomock.Any()).Return(dtypes.ValidatorAccumulatedCommission{}, nil) -// distrKeeper.EXPECT().IncrementValidatorPeriod(gomock.Any(), gomock.Any()).Return(uint64(0), errors.New("failed to increment validator period")) -// }, -// expectedError: "failed to increment validator period", -// }, -// { -// name: "fail: calculate delegation rewards", -// preRun: func(_ sdk.Context) { -// // bankKeeper.EXPECT().GetBalance(gomock.Any(), gomock.Any(), sdk.DefaultBondDenom).AnyTimes() -// distrKeeper.EXPECT().GetValidatorAccumulatedCommission(gomock.Any(), gomock.Any()).Return(dtypes.ValidatorAccumulatedCommission{}, nil) -// distrKeeper.EXPECT().IncrementValidatorPeriod(gomock.Any(), gomock.Any()).Return(uint64(0), nil) -// distrKeeper.EXPECT().CalculateDelegationRewards(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(delRewards, errors.New("failed to calculate delegation rewards")) -// }, -// expectedError: "failed to calculate delegation rewards", -// }, -// { -// name: "pass: multiple validators", -// preRun: func(c sdk.Context) { -// // bankKeeper.EXPECT().GetBalance(gomock.Any(), gomock.Any(), sdk.DefaultBondDenom).AnyTimes() -// s.setupValidatorAndDelegation(c, valPubKey2, delPubKey, valAddr2, delAddr, valTokens) -// distrKeeper.EXPECT().GetValidatorAccumulatedCommission(gomock.Any(), gomock.Any()).Return(dtypes.ValidatorAccumulatedCommission{}, nil).Times(2) -// distrKeeper.EXPECT().IncrementValidatorPeriod(gomock.Any(), gomock.Any()).Return(uint64(0), nil).Times(2) -// distrKeeper.EXPECT().CalculateDelegationRewards(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(delRewards, nil).Times(2) -// }, -// expectedResult: []types.Withdrawal{ -// { -// CreationHeight: 0, -// ExecutionAddress: evmDelAddr.String(), -// Amount: delRewardsAmt, -// }, -// { -// CreationHeight: 0, -// ExecutionAddress: evmDelAddr.String(), -// Amount: delRewardsAmt, -// }, -// }, -// }, -// { -// name: "pass: skip jailed validator", -// preRun: func(c sdk.Context) { -// s.setupValidatorAndDelegation(c, valPubKey2, delPubKey, valAddr2, delAddr, valTokens) -// val, err := stakingKeeper.GetValidator(c, valAddr2) -// require.NoError(err) -// val.Jailed = true -// require.NoError(stakingKeeper.SetValidator(c, val)) -// // bankKeeper.EXPECT().GetBalance(gomock.Any(), gomock.Any(), sdk.DefaultBondDenom).AnyTimes() -// distrKeeper.EXPECT().GetValidatorAccumulatedCommission(gomock.Any(), gomock.Any()).Return(dtypes.ValidatorAccumulatedCommission{}, nil) -// distrKeeper.EXPECT().IncrementValidatorPeriod(gomock.Any(), gomock.Any()).Return(uint64(0), nil) -// distrKeeper.EXPECT().CalculateDelegationRewards(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(delRewards, nil) -// }, -// expectedResult: []types.Withdrawal{ -// { -// CreationHeight: 0, -// ExecutionAddress: evmDelAddr.String(), -// Amount: delRewardsAmt, -// }, -// }, -// }, -// } -// -// for _, tc := range tcs { -// s.Run(tc.name, func() { -// cached, _ := ctx.CacheContext() -// if tc.preRun != nil { -// tc.preRun(cached) -// } -// _, unbondedEntries, err := stakingKeeper.EndBlockerWithUnbondedEntries(ctx) -// require.NoError(err) -// result, err := evmstakingKeeper.ExpectedUnbondingWithdrawals(cached, unbondedEntries) -// if tc.expectedError != "" { -// require.ErrorContains(err, tc.expectedError) -// } else { -// require.NoError(err) -// isEqualWithdrawals(s.T(), tc.expectedResult, result) -// } -// }) -// } -//} -// -//// TODO: Test for ProcessUnbondingWithdrawals -//// TODO: Test for ProcessRewardWithdrawals -//// TODO: Test for ProcessEligibleRewardWithdrawal -//// TODO: Test for EnqueueRewardWithdrawal -// -//func (s *TestSuite) TestProcessWithdraw() { -// require := s.Require() -// ctx, keeper, stakingKeeper := s.Ctx, s.EVMStakingKeeper, s.StakingKeeper -// -// pubKeys, accAddrs, valAddrs := createAddresses(4) -// // delegator-1 -// delPubKey1 := pubKeys[0] -// delAddr1 := accAddrs[0] -// // validator -// valPubKey := pubKeys[2] -// valAddr := valAddrs[2] -// // unknown pubkey -// unknownPubKey := pubKeys[3] -// -// valTokens := stakingKeeper.TokensFromConsensusPower(ctx, 10) -// s.setupValidatorAndDelegation(ctx, valPubKey, delPubKey1, valAddr, delAddr1, valTokens) -// -// tcs := []struct { -// name string -// settingMock func() -// withdraw *bindings.IPTokenStakingWithdraw -// expectedErr string -// }{ -// { -// name: "pass: valid input", -// settingMock: func() { -// // bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), stypes.BondedPoolName, stypes.NotBondedPoolName, gomock.Any()) -// }, -// withdraw: &bindings.IPTokenStakingWithdraw{ -// DelegatorUncmpPubkey: cmpToUncmp(delPubKey1.Bytes()), -// ValidatorUncmpPubkey: cmpToUncmp(valPubKey.Bytes()), -// StakeAmount: new(big.Int).SetUint64(1), -// DelegationId: big.NewInt(0), -// OperatorAddress: cmpToEVM(delPubKey1.Bytes()), -// }, -// }, -// { -// name: "fail: invalid delegator pubkey", -// withdraw: &bindings.IPTokenStakingWithdraw{ -// DelegatorUncmpPubkey: cmpToUncmp(delPubKey1.Bytes())[:16], -// ValidatorUncmpPubkey: cmpToUncmp(valPubKey.Bytes()), -// StakeAmount: new(big.Int).SetUint64(1), -// DelegationId: big.NewInt(0), -// OperatorAddress: cmpToEVM(delPubKey1.Bytes()), -// }, -// expectedErr: "invalid uncompressed public key length or format", -// }, -// { -// name: "fail: invalid validator pubkey", -// withdraw: &bindings.IPTokenStakingWithdraw{ -// DelegatorUncmpPubkey: cmpToUncmp(delPubKey1.Bytes()), -// ValidatorUncmpPubkey: cmpToUncmp(valPubKey.Bytes())[:16], -// StakeAmount: new(big.Int).SetUint64(1), -// DelegationId: big.NewInt(0), -// OperatorAddress: cmpToEVM(delPubKey1.Bytes()), -// }, -// expectedErr: "invalid uncompressed public key length or format", -// }, -// { -// name: "fail: corrupted delegator pubkey", -// withdraw: &bindings.IPTokenStakingWithdraw{ -// DelegatorUncmpPubkey: createCorruptedPubKey(cmpToUncmp(delPubKey1.Bytes())), -// ValidatorUncmpPubkey: cmpToUncmp(valPubKey.Bytes()), -// StakeAmount: new(big.Int).SetUint64(1), -// DelegationId: big.NewInt(0), -// OperatorAddress: cmpToEVM(delPubKey1.Bytes()), -// }, -// expectedErr: "invalid uncompressed public key length or format", -// }, -// { -// name: "fail: corrupted validator pubkey", -// withdraw: &bindings.IPTokenStakingWithdraw{ -// DelegatorUncmpPubkey: cmpToUncmp(delPubKey1.Bytes()), -// ValidatorUncmpPubkey: createCorruptedPubKey(cmpToUncmp(valPubKey.Bytes())), -// StakeAmount: new(big.Int).SetUint64(1), -// DelegationId: big.NewInt(0), -// OperatorAddress: cmpToEVM(delPubKey1.Bytes()), -// }, -// expectedErr: "invalid uncompressed public key length or format", -// }, -// { -// name: "fail: unknown depositor", -// settingMock: func() { -// // accountKeeper.EXPECT().HasAccount(gomock.Any(), sdk.AccAddress(unknownPubKey.Address().Bytes())).Return(false).Times(1) -// }, -// withdraw: &bindings.IPTokenStakingWithdraw{ -// DelegatorUncmpPubkey: cmpToUncmp(unknownPubKey.Bytes()), -// ValidatorUncmpPubkey: cmpToUncmp(valPubKey.Bytes()), -// StakeAmount: new(big.Int).SetUint64(1), -// DelegationId: big.NewInt(0), -// OperatorAddress: cmpToEVM(unknownPubKey.Bytes()), -// }, -// expectedErr: "depositor account not found", -// }, -// { -// name: "fail: amount to withdraw is greater than the delegation amount", -// settingMock: func() { -// // accountKeeper.EXPECT().HasAccount(gomock.Any(), sdk.AccAddress(delPubKey1.Address().Bytes())).Return(true).Times(1) -// }, -// withdraw: &bindings.IPTokenStakingWithdraw{ -// DelegatorUncmpPubkey: cmpToUncmp(delPubKey1.Bytes()), -// ValidatorUncmpPubkey: cmpToUncmp(valPubKey.Bytes()), -// StakeAmount: new(big.Int).SetUint64(math.MaxUint64), -// DelegationId: big.NewInt(0), -// OperatorAddress: cmpToEVM(delPubKey1.Bytes()), -// }, -// expectedErr: "invalid shares amount", -// }, -// } -// -// for _, tc := range tcs { -// s.Run(tc.name, func() { -// if tc.settingMock != nil { -// tc.settingMock() -// } -// cachedCtx, _ := ctx.CacheContext() -// // check undelegation does not exist -// _, err := s.StakingKeeper.GetUnbondingDelegation(cachedCtx, delAddr1, valAddr) -// require.ErrorContains(err, "no unbonding delegation found") -// -// err = keeper.ProcessWithdraw(cachedCtx, tc.withdraw) -// if tc.expectedErr != "" { -// require.ErrorContains(err, tc.expectedErr) -// } else { -// require.NoError(err) -// // check undelegation exists -// ubd, err := s.StakingKeeper.GetUnbondingDelegation(cachedCtx, delAddr1, valAddr) -// require.NoError(err) -// require.NotNil(ubd) -// } -// }) -// } -//} -// -//func (s *TestSuite) TestParseWithdraw() { -// require := s.Require() -// keeper := s.EVMStakingKeeper -// -// testCases := []struct { -// name string -// log gethtypes.Log -// expectErr bool -// }{ -// { -// name: "Unknown Topic", -// log: gethtypes.Log{ -// Topics: []common.Hash{common.HexToHash("0x1111111111111111111111111111111111111111111111111111111111111111")}, -// }, -// expectErr: true, -// }, -// { -// name: "Valid Topic", -// log: gethtypes.Log{ -// Topics: []common.Hash{types.WithdrawEvent.ID}, -// }, -// expectErr: false, -// }, -// } -// -// for _, tc := range testCases { -// s.Run(tc.name, func() { -// _, err := keeper.ParseWithdrawLog(tc.log) -// if tc.expectErr { -// require.Error(err, "should return error for %s", tc.name) -// } else { -// require.NoError(err, "should not return error for %s", tc.name) -// } -// }) -// } -//} -// -//// isEqualWithdrawals compares two slices of Withdrawal without considering order. -//func isEqualWithdrawals(t *testing.T, expected, actual []types.Withdrawal) { -// t.Helper() -// require.Len(t, actual, len(expected)) -// // compare it without considering order -// for _, e := range expected { -// found := false -// for _, a := range actual { -// if e.CreationHeight == a.CreationHeight && -// e.ExecutionAddress == a.ExecutionAddress && -// e.Amount == a.Amount { -// found = true -// break -// } -// } -// if !found { -// t.Errorf("expected %+v not found in %+v", e, actual) -// } -// } -//} +import ( + "math" + "math/big" + + stypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/ethereum/go-ethereum/common" + gethtypes "github.com/ethereum/go-ethereum/core/types" + + "github.com/piplabs/story/client/x/evmstaking/types" + "github.com/piplabs/story/contracts/bindings" +) + +// TODO: Test for ExpectedUnbondingWithdrawals +// TODO: Test for ProcessUnbondingWithdrawals +// TODO: Test for ProcessRewardWithdrawals +// TODO: Test for ProcessEligibleRewardWithdrawal +// TODO: Test for EnqueueRewardWithdrawal + +func (s *TestSuite) TestProcessWithdraw() { + require := s.Require() + ctx, keeper, stakingKeeper := s.Ctx, s.EVMStakingKeeper, s.StakingKeeper + + pubKeys, accAddrs, valAddrs := createAddresses(4) + // delegator-1 + delPubKey1 := pubKeys[0] + delAddr1 := accAddrs[0] + // validator + valPubKey := pubKeys[2] + valAddr := valAddrs[2] + // unknown pubkey + unknownPubKey := pubKeys[3] + + valTokens := stakingKeeper.TokensFromConsensusPower(ctx, 10) + s.setupValidatorAndDelegation(ctx, valPubKey, delPubKey1, valAddr, delAddr1, valTokens) + + singularityHeight, err := stakingKeeper.GetSingularityHeight(ctx) + require.NoError(err) + + tcs := []struct { + name string + withdraw *bindings.IPTokenStakingWithdraw + expectedErr string + }{ + // TODO: test cases before and after singularity height + { + name: "pass: valid input", + withdraw: &bindings.IPTokenStakingWithdraw{ + DelegatorUncmpPubkey: cmpToUncmp(delPubKey1.Bytes()), + ValidatorUncmpPubkey: cmpToUncmp(valPubKey.Bytes()), + StakeAmount: new(big.Int).SetUint64(1), + DelegationId: big.NewInt(0), + OperatorAddress: cmpToEVM(delPubKey1.Bytes()), + }, + }, + { + name: "fail: invalid delegator pubkey", + withdraw: &bindings.IPTokenStakingWithdraw{ + DelegatorUncmpPubkey: cmpToUncmp(delPubKey1.Bytes())[:16], + ValidatorUncmpPubkey: cmpToUncmp(valPubKey.Bytes()), + StakeAmount: new(big.Int).SetUint64(1), + DelegationId: big.NewInt(0), + OperatorAddress: cmpToEVM(delPubKey1.Bytes()), + }, + expectedErr: "invalid uncompressed public key length or format", + }, + { + name: "fail: invalid validator pubkey", + withdraw: &bindings.IPTokenStakingWithdraw{ + DelegatorUncmpPubkey: cmpToUncmp(delPubKey1.Bytes()), + ValidatorUncmpPubkey: cmpToUncmp(valPubKey.Bytes())[:16], + StakeAmount: new(big.Int).SetUint64(1), + DelegationId: big.NewInt(0), + OperatorAddress: cmpToEVM(delPubKey1.Bytes()), + }, + expectedErr: "invalid uncompressed public key length or format", + }, + // TODO corrupted delegator and validator pubkey + { + name: "fail: unknown depositor", + withdraw: &bindings.IPTokenStakingWithdraw{ + DelegatorUncmpPubkey: cmpToUncmp(unknownPubKey.Bytes()), + ValidatorUncmpPubkey: cmpToUncmp(valPubKey.Bytes()), + StakeAmount: new(big.Int).SetUint64(1), + DelegationId: big.NewInt(0), + OperatorAddress: cmpToEVM(unknownPubKey.Bytes()), + }, + expectedErr: "depositor account not found", + }, + // Intuitive behavior is to fail but instead, max share is withdrawn (100% of delegator's stake amount) + { + name: "pass: withdraw amount exceeds the delegation amount, results in max share withdrawal", + withdraw: &bindings.IPTokenStakingWithdraw{ + DelegatorUncmpPubkey: cmpToUncmp(delPubKey1.Bytes()), + ValidatorUncmpPubkey: cmpToUncmp(valPubKey.Bytes()), + StakeAmount: new(big.Int).SetUint64(math.MaxUint64), + DelegationId: big.NewInt(0), + OperatorAddress: cmpToEVM(delPubKey1.Bytes()), + }, + }, + } + + for _, tc := range tcs { + s.Run(tc.name, func() { + cachedCtx, _ := ctx.CacheContext() + cachedCtx = cachedCtx.WithBlockHeight(int64(singularityHeight) + 1) + require.Equal(singularityHeight+1, uint64(cachedCtx.BlockHeight())) + + // check undelegation does not exist + _, err := s.StakingKeeper.GetUnbondingDelegation(cachedCtx, delAddr1, valAddr) + require.ErrorContains(err, stypes.ErrNoUnbondingDelegation.Error()) + + err = keeper.ProcessWithdraw(cachedCtx, tc.withdraw) + if tc.expectedErr != "" { + require.ErrorContains(err, tc.expectedErr) + } else { + require.NoError(err) + // check undelegation exists + ubd, err := s.StakingKeeper.GetUnbondingDelegation(cachedCtx, delAddr1, valAddr) + require.NoError(err) + require.NotNil(ubd) + } + }) + } +} + +func (s *TestSuite) TestParseWithdraw() { + require := s.Require() + keeper := s.EVMStakingKeeper + + testCases := []struct { + name string + log gethtypes.Log + expectErr bool + }{ + { + name: "Unknown Topic", + log: gethtypes.Log{ + Topics: []common.Hash{common.HexToHash("0x1111111111111111111111111111111111111111111111111111111111111111")}, + }, + expectErr: true, + }, + { + name: "Valid Topic", + log: gethtypes.Log{ + Topics: []common.Hash{types.WithdrawEvent.ID}, + }, + expectErr: false, + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + _, err := keeper.ParseWithdrawLog(tc.log) + if tc.expectErr { + require.Error(err, "should return error for %s", tc.name) + } else { + require.NoError(err, "should not return error for %s", tc.name) + } + }) + } +}