From 654e153cda3b861ea5d9cab09282d86ce4e37825 Mon Sep 17 00:00:00 2001 From: Francisco de Borja Aranda Castillejo Date: Fri, 8 Nov 2024 13:20:18 +0100 Subject: [PATCH] add unit tests --- .../staking/method_get_rewards_test.go | 136 +++++++----------- precompiles/staking/method_get_validators.go | 4 +- .../staking/method_get_validators_test.go | 93 ++++++++++++ precompiles/staking/staking_test.go | 62 ++++++++ 4 files changed, 205 insertions(+), 90 deletions(-) create mode 100644 precompiles/staking/method_get_validators_test.go diff --git a/precompiles/staking/method_get_rewards_test.go b/precompiles/staking/method_get_rewards_test.go index 5f751980eb..3a8d241662 100644 --- a/precompiles/staking/method_get_rewards_test.go +++ b/precompiles/staking/method_get_rewards_test.go @@ -7,19 +7,15 @@ import ( "testing" "cosmossdk.io/math" - sdk "github.com/cosmos/cosmos-sdk/types" - bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" - stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - "github.com/ethereum/go-ethereum/common" + distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" "github.com/stretchr/testify/require" + precompiletypes "github.com/zeta-chain/node/precompiles/types" "github.com/zeta-chain/node/testutil/sample" "github.com/zeta-chain/node/x/emissions" - fungibletypes "github.com/zeta-chain/node/x/fungible/types" ) func Test_GetRewards(t *testing.T) { - t.Run("become azeta staker, distribute ZRC20, get rewards", func(t *testing.T) { + t.Run("should return empty rewards list to a non staker", func(t *testing.T) { /* ARRANGE */ s := newTestSuite(t) @@ -28,7 +24,38 @@ func Test_GetRewards(t *testing.T) { s.sdkKeepers.StakingKeeper.SetValidator(s.ctx, validator) // Create staker. - staker := sample.Bech32AccAddress() + stakerEVMAddr := sample.EthAddress() + + /* ACT */ + // Call getRewards. + getRewardsMethod := s.stkContractABI.Methods[GetRewardsMethodName] + + s.mockVMContract.Input = packInputArgs( + t, + getRewardsMethod, + []interface{}{stakerEVMAddr, validator.GetOperator().String()}..., + ) + + /* ASSERT */ + bytes, err := s.stkContract.Run(s.mockEVM, s.mockVMContract, false) + require.Error(t, err) + require.Contains(t, err.Error(), "delegation does not exist") + require.Empty(t, bytes) + }) + + t.Run("should return the zrc20 rewards list for a staker", func(t *testing.T) { + /* ARRANGE */ + s := newTestSuite(t) + s.sdkKeepers.DistributionKeeper.SetFeePool(s.ctx, distrtypes.InitialFeePool()) + + // Create validator. + validator := sample.Validator(t, rand.New(rand.NewSource(42))) + s.sdkKeepers.StakingKeeper.SetValidator(s.ctx, validator) + + // Create staker. + stakerEVMAddr := sample.EthAddress() + stakerCosmosAddr, err := precompiletypes.GetCosmosAddress(s.sdkKeepers.BankKeeper, stakerEVMAddr) + require.NoError(t, err) // Become a staker. stakeThroughCosmosAPI( @@ -37,106 +64,39 @@ func Test_GetRewards(t *testing.T) { s.sdkKeepers.BankKeeper, s.sdkKeepers.StakingKeeper, validator, - staker, + stakerCosmosAddr, math.NewInt(100), ) + err = s.sdkKeepers.DistributionKeeper.Hooks().AfterDelegationModified(s.ctx, stakerCosmosAddr, validator.GetOperator()) + require.NoError(t, err) + + // DEBUG CALL + del := s.sdkKeepers.StakingKeeper.GetAllDelegations(s.ctx) + fmt.Println(del) + /* Distribute 1000 ZRC20 tokens to the staking contract */ distributeZRC20(t, s, big.NewInt(1000)) - // Produce blocks. - for i := 0; i < 10; i++ { - // produce a block - emissions.BeginBlocker(s.ctx, *s.sdkKeepers.EmissionsKeeper) - s.ctx = s.ctx.WithBlockHeight(s.ctx.BlockHeight() + 1) - } + emissions.BeginBlocker(s.ctx, *s.sdkKeepers.EmissionsKeeper) + s.ctx = s.ctx.WithBlockHeight(s.ctx.BlockHeight() + 1) /* ACT */ // Call getRewards. getRewardsMethod := s.stkContractABI.Methods[GetRewardsMethodName] - - fmt.Println(common.HexToAddress(staker.String())) - fmt.Println(validator.GetOperator().String()) - // Setup method input. s.mockVMContract.Input = packInputArgs( t, getRewardsMethod, - []interface{}{common.HexToAddress(staker.String()), validator.GetOperator().String()}..., + []interface{}{stakerEVMAddr, validator.GetOperator().String()}..., ) bytes, err := s.stkContract.Run(s.mockEVM, s.mockVMContract, false) require.NoError(t, err) - res, err := s.stkContractABI.Methods[DistributeMethodName].Outputs.Unpack(bytes) + /* ASSERT */ + res, err := getRewardsMethod.Outputs.Unpack(bytes) require.NoError(t, err) fmt.Println(res) - - /* ASSERT */ }) } - -func stakeThroughCosmosAPI( - t *testing.T, - ctx sdk.Context, - bankKeeper bankkeeper.Keeper, - stakingKeeper stakingkeeper.Keeper, - validator stakingtypes.Validator, - staker sdk.AccAddress, - amount math.Int, -) { - // Coins to stake with default cosmos denom. - coins := sdk.NewCoins(sdk.NewCoin("stake", amount)) - - err := bankKeeper.MintCoins(ctx, fungibletypes.ModuleName, coins) - require.NoError(t, err) - - err = bankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) - require.NoError(t, err) - - b := bankKeeper.GetAllBalances(ctx, staker) - fmt.Println(b) - - shares, err := stakingKeeper.Delegate( - ctx, - staker, - coins.AmountOf(coins.Denoms()[0]), - validator.Status, - validator, - true, - ) - require.NoError(t, err) - require.Equal(t, amount.Uint64(), shares.TruncateInt().Uint64()) - b = bankKeeper.GetAllBalances(ctx, staker) - - del, found := stakingKeeper.GetDelegation(ctx, staker, validator.GetOperator()) - fmt.Println(found) - fmt.Println(del) -} - -func distributeZRC20( - t *testing.T, - s testSuite, - amount *big.Int, -) { - distributeMethod := s.stkContractABI.Methods[DistributeMethodName] - - _, err := s.fungibleKeeper.DepositZRC20(s.ctx, s.zrc20Address, s.defaultCaller, amount) - require.NoError(t, err) - allowStaking(t, s, amount) - - // Setup method input. - s.mockVMContract.Input = packInputArgs( - t, - distributeMethod, - []interface{}{s.zrc20Address, amount}..., - ) - - // Call distribute method. - success, err := s.stkContract.Run(s.mockEVM, s.mockVMContract, false) - require.NoError(t, err) - res, err := distributeMethod.Outputs.Unpack(success) - require.NoError(t, err) - ok := res[0].(bool) - require.True(t, ok) -} diff --git a/precompiles/staking/method_get_validators.go b/precompiles/staking/method_get_validators.go index 6c41bdc8ff..fbf133ffb9 100644 --- a/precompiles/staking/method_get_validators.go +++ b/precompiles/staking/method_get_validators.go @@ -16,10 +16,10 @@ func (c *Contract) getDelegatorValidators( method *abi.Method, args []interface{}, ) ([]byte, error) { - if len(args) != 0 { + if len(args) != 1 { return nil, &precompiletypes.ErrInvalidNumberOfArgs{ Got: len(args), - Expect: 0, + Expect: 1, } } diff --git a/precompiles/staking/method_get_validators_test.go b/precompiles/staking/method_get_validators_test.go new file mode 100644 index 0000000000..6fd3957c96 --- /dev/null +++ b/precompiles/staking/method_get_validators_test.go @@ -0,0 +1,93 @@ +package staking + +import ( + "math/rand" + "testing" + + "cosmossdk.io/math" + "github.com/stretchr/testify/require" + precompiletypes "github.com/zeta-chain/node/precompiles/types" + "github.com/zeta-chain/node/testutil/sample" +) + +func Test_GetValidators(t *testing.T) { + t.Run("should return an empty list for a non staker address", func(t *testing.T) { + /* ARRANGE */ + s := newTestSuite(t) + + // Create validator. + validator := sample.Validator(t, rand.New(rand.NewSource(42))) + s.sdkKeepers.StakingKeeper.SetValidator(s.ctx, validator) + + // Create staker. + stakerEVMAddr := sample.EthAddress() + + /* ACT */ + // Call getRewards. + getValidatorsMethod := s.stkContractABI.Methods[GetValidatorsMethodName] + + s.mockVMContract.Input = packInputArgs( + t, + getValidatorsMethod, + []interface{}{stakerEVMAddr}..., + ) + + bytes, err := s.stkContract.Run(s.mockEVM, s.mockVMContract, false) + require.NoError(t, err) + + res, err := getValidatorsMethod.Outputs.Unpack(bytes) + require.NoError(t, err) + require.NotEmpty(t, res) + + list, ok := res[0].([]string) + require.True(t, ok) + require.Len(t, list, 0) + }) + + t.Run("should return staker's validator list", func(t *testing.T) { + /* ARRANGE */ + s := newTestSuite(t) + + // Create validator. + validator := sample.Validator(t, rand.New(rand.NewSource(42))) + s.sdkKeepers.StakingKeeper.SetValidator(s.ctx, validator) + + // Create staker. + stakerEVMAddr := sample.EthAddress() + stakerCosmosAddr, err := precompiletypes.GetCosmosAddress(s.sdkKeepers.BankKeeper, stakerEVMAddr) + require.NoError(t, err) + + // Become a staker. + stakeThroughCosmosAPI( + t, + s.ctx, + s.sdkKeepers.BankKeeper, + s.sdkKeepers.StakingKeeper, + validator, + stakerCosmosAddr, + math.NewInt(100), + ) + + /* ACT */ + // Call getRewards. + getValidatorsMethod := s.stkContractABI.Methods[GetValidatorsMethodName] + + s.mockVMContract.Input = packInputArgs( + t, + getValidatorsMethod, + []interface{}{stakerEVMAddr}..., + ) + + bytes, err := s.stkContract.Run(s.mockEVM, s.mockVMContract, false) + require.NoError(t, err) + + res, err := getValidatorsMethod.Outputs.Unpack(bytes) + require.NoError(t, err) + require.NotEmpty(t, res) + + list, ok := res[0].([]string) + require.True(t, ok) + require.Len(t, list, 1) + require.Equal(t, validator.GetOperator().String(), list[0]) + }) +} diff --git a/precompiles/staking/staking_test.go b/precompiles/staking/staking_test.go index 8fdfd74264..efc70c9305 100644 --- a/precompiles/staking/staking_test.go +++ b/precompiles/staking/staking_test.go @@ -6,12 +6,15 @@ import ( "math/big" + "cosmossdk.io/math" tmdb "github.com/cometbft/cometbft-db" "github.com/cosmos/cosmos-sdk/store" storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" + stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" @@ -397,6 +400,65 @@ func allowStaking(t *testing.T, ts testSuite, amount *big.Int) { require.True(t, allowed) } +func stakeThroughCosmosAPI( + t *testing.T, + ctx sdk.Context, + bankKeeper bankkeeper.Keeper, + stakingKeeper stakingkeeper.Keeper, + validator stakingtypes.Validator, + staker sdk.AccAddress, + amount math.Int, +) { + // Coins to stake with default cosmos denom. + coins := sdk.NewCoins(sdk.NewCoin("stake", amount)) + + err := bankKeeper.MintCoins(ctx, fungibletypes.ModuleName, coins) + require.NoError(t, err) + + err = bankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) + require.NoError(t, err) + + shares, err := stakingKeeper.Delegate( + ctx, + staker, + coins.AmountOf(coins.Denoms()[0]), + validator.Status, + validator, + true, + ) + require.NoError(t, err) + require.Equal(t, amount.Uint64(), shares.TruncateInt().Uint64()) +} + +func distributeZRC20( + t *testing.T, + s testSuite, + amount *big.Int, +) { + distributeMethod := s.stkContractABI.Methods[DistributeMethodName] + + _, err := s.fungibleKeeper.DepositZRC20(s.ctx, s.zrc20Address, s.defaultCaller, amount) + require.NoError(t, err) + allowStaking(t, s, amount) + + // Setup method input. + s.mockVMContract.Input = packInputArgs( + t, + distributeMethod, + []interface{}{s.zrc20Address, amount}..., + ) + + // Call distribute method. + success, err := s.stkContract.Run(s.mockEVM, s.mockVMContract, false) + require.NoError(t, err) + + res, err := distributeMethod.Outputs.Unpack(success) + require.NoError(t, err) + + ok := res[0].(bool) + require.True(t, ok) +} + func callEVM( t *testing.T, ctx sdk.Context,