Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test(evmstaking/keeper): add test cases for validator #66

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
271 changes: 271 additions & 0 deletions client/x/evmstaking/keeper/validator_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,271 @@
package keeper_test

import (
"math/big"
"testing"

"cosmossdk.io/math"

"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"
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"
"github.com/piplabs/story/lib/errors"
"github.com/piplabs/story/lib/k1util"

"go.uber.org/mock/gomock"
)

func (s *TestSuite) TestProcessCreateValidator() {
require := s.Require()
ctx, keeper, stakingKeeper, accountKeeper, bankKeeper := s.Ctx, s.EVMStakingKeeper, s.StakingKeeper, s.AccountKeeper, s.BankKeeper

pubKeys, addrs, valAddrs := createAddresses(3)
corruptedPubKey := append([]byte{}, pubKeys[0].Bytes()...)
corruptedPubKey[0] = 0x04
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 := keeper.DelegatorMap.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 := keeper.DelegatorMap.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
valAddr sdk.ValAddress
valPubKey crypto.PubKey
valPubKeyBytes []byte
valTokens math.Int
moniker string
preRun func(t *testing.T, c sdk.Context, valDelAddr sdk.AccAddress, valPubKey crypto.PubKey, valTokens math.Int)
postCheck func(c sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress, delEvmAddr common.Address, previousTokens math.Int)
expectedError string
}{
{
name: "fail: nil validator pubkey",
valPubKeyBytes: nil,
expectedError: "validator pubkey to cosmos",
},
{
name: "fail: invalid validator pubkey",
valPubKeyBytes: pubKeys[0].Bytes()[1:],
expectedError: "validator pubkey to cosmos",
},
{
name: "fail: corrupted validator pubkey",
valPubKeyBytes: corruptedPubKey,
expectedError: "validator pubkey to evm address",
},
{
name: "fail: mint coins",
valDelAddr: addrs[0],
valPubKeyBytes: pubKeys[0].Bytes(),
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],
valPubKeyBytes: pubKeys[0].Bytes(),
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],
valPubKeyBytes: pubKeys[2].Bytes(),
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],
valPubKeyBytes: pubKeys[2].Bytes(),
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],
valPubKeyBytes: pubKeys[1].Bytes(),
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],
valPubKeyBytes: pubKeys[1].Bytes(),
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)
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],
valPubKeyBytes: pubKeys[1].Bytes(),
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)
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 {
s.Run(tc.name, func() {
cachedCtx, _ := ctx.CacheContext()
// create valPubKey using tc.valPubKeyBytes
if tc.preRun != nil {
tc.preRun(s.T(), cachedCtx, tc.valDelAddr, tc.valPubKey, tc.valTokens)
}
moniker := tc.moniker
if moniker == "" {
moniker = "testing"
}
err := keeper.ProcessCreateValidator(cachedCtx, &bindings.IPTokenStakingCreateValidator{
ValidatorUncmpPubkey: nil,
ValidatorCmpPubkey: tc.valPubKeyBytes,
Moniker: moniker,
StakeAmount: new(big.Int).SetUint64(100),
CommissionRate: 1000, // 10%
MaxCommissionRate: 5000, // 50%
MaxCommissionChangeRate: 500, // 5%
Raw: gethtypes.Log{},
})
if tc.expectedError != "" {
require.Error(err, tc.expectedError)
} else {
require.NoError(err)
delEvmAddr, err := k1util.CosmosPubkeyToEVMAddress(tc.valPubKeyBytes)
require.NoError(err)
tc.postCheck(cachedCtx, tc.valDelAddr, tc.valAddr, delEvmAddr, tc.valTokens)
}
})
}
}

func (s *TestSuite) TestParseCreateValidatorLog() {
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.CreateValidatorEvent.ID},
},
expectErr: false,
},
}

for _, tc := range testCases {
s.Run(tc.name, func() {
_, err := keeper.ParseCreateValidatorLog(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)
}
})
}
}
Loading