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

!feat: Add invartiants #101

Merged
merged 41 commits into from
Jan 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
5246527
add invariants escrow address
GNaD13 Jan 13, 2024
7dd5797
add invariants lock denom
GNaD13 Jan 14, 2024
9ad8504
add burn slash coin
GNaD13 Jan 15, 2024
fe20826
lint
GNaD13 Jan 15, 2024
380b684
lint
GNaD13 Jan 15, 2024
6b4b4e6
remove unused code and change var name
GNaD13 Jan 17, 2024
94b6b95
nit
GNaD13 Jan 17, 2024
3f81150
nit
GNaD13 Jan 17, 2024
200a1b0
nit
GNaD13 Jan 18, 2024
6532533
Merge remote-tracking branch 'origin/main' into add-invartiants
GNaD13 Jan 19, 2024
eb9ba22
add setup test for invariants
GNaD13 Jan 19, 2024
e4e9194
nit
GNaD13 Jan 19, 2024
45f6ffd
nit
GNaD13 Jan 19, 2024
7b13a6c
test
GNaD13 Jan 19, 2024
4191994
nit
GNaD13 Jan 19, 2024
0b4798d
nit
GNaD13 Jan 19, 2024
2fadd91
nit
GNaD13 Jan 19, 2024
dd7c48b
add lock to log
GNaD13 Jan 19, 2024
c5dc54b
nit
GNaD13 Jan 19, 2024
6642f43
nit
GNaD13 Jan 19, 2024
7db6a36
nit
GNaD13 Jan 19, 2024
fbf0df5
nit
GNaD13 Jan 19, 2024
c42897e
nit
GNaD13 Jan 19, 2024
6a23550
nit
GNaD13 Jan 19, 2024
7dfaa18
nit
GNaD13 Jan 19, 2024
6a8d3cc
add more test case
GNaD13 Jan 19, 2024
e628b52
add fund
GNaD13 Jan 19, 2024
fd0f2a8
add app
GNaD13 Jan 19, 2024
05b1917
add more test case
GNaD13 Jan 19, 2024
107bad4
nit
GNaD13 Jan 19, 2024
6bdccfe
nit
GNaD13 Jan 19, 2024
6a04ad2
add more case
GNaD13 Jan 19, 2024
84960d9
begin redelegate
GNaD13 Jan 19, 2024
feac933
undelegate
GNaD13 Jan 19, 2024
4dc3a19
lint
GNaD13 Jan 19, 2024
6fd72d9
Merge remote-tracking branch 'origin/main' into add-invartiants
GNaD13 Jan 23, 2024
de7689b
nit
GNaD13 Jan 23, 2024
9d308ae
Merge remote-tracking branch 'origin/main' into add-invartiants
GNaD13 Jan 25, 2024
a9122dc
lint
GNaD13 Jan 25, 2024
430b7f5
nit
GNaD13 Jan 25, 2024
44c4715
change to to coin
GNaD13 Jan 25, 2024
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
1 change: 0 additions & 1 deletion testing/simapp/test_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,6 @@ func genesisStateWithValSet(app *SimApp, genesisState GenesisState, valSet *tmty

// set validators and delegations
stakingGenesis := stakingtypes.NewGenesisState(stakingtypes.DefaultParams(), validators, delegations)
genesisState[stakingtypes.ModuleName] = app.AppCodec().MustMarshalJSON(stakingGenesis)

multistakingGenesis := multistakingtypes.GenesisState{
MultiStakingLocks: locks,
Expand Down
6 changes: 6 additions & 0 deletions testutil/address.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,9 @@ func GenValAddress() sdk.ValAddress {

return sdk.ValAddress(priv.PubKey().Address())
}

func GenValAddressWithPrivKey() (*secp256k1.PrivKey, sdk.ValAddress) {
priv := secp256k1.GenPrivKey()

return priv, sdk.ValAddress(priv.PubKey().Address())
}
108 changes: 108 additions & 0 deletions x/multi-staking/keeper/invariants.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package keeper

import (
"fmt"

"github.com/realio-tech/multi-staking-module/x/multi-staking/types"

sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
)

// RegisterInvariants registers all staking invariants
func RegisterInvariants(ir sdk.InvariantRegistry, k Keeper) {
ir.RegisterRoute(types.ModuleName, "module-accounts",
ModuleAccountInvariants(k))
ir.RegisterRoute(types.ModuleName, "validator-lock-denom",
ValidatorLockDenomInvariants(k))
}

func ModuleAccountInvariants(k Keeper) sdk.Invariant {
return func(ctx sdk.Context) (string, bool) {
totalLockCoinAmount := sdk.NewCoins()

// calculate lock amount
lockCoinAmount := sdk.NewCoins()
k.MultiStakingLockIterator(ctx, func(stakingLock types.MultiStakingLock) bool {
lockCoinAmount = lockCoinAmount.Add(stakingLock.LockedCoin.ToCoin())
return false
})
totalLockCoinAmount = totalLockCoinAmount.Add(lockCoinAmount...)

// calculate unlocking amount
unlockingCoinAmount := sdk.NewCoins()
k.MultiStakingUnlockIterator(ctx, func(unlock types.MultiStakingUnlock) bool {
for _, entry := range unlock.Entries {
unlockingCoinAmount = unlockingCoinAmount.Add(entry.UnlockingCoin.ToCoin())
}
return false
})
totalLockCoinAmount = totalLockCoinAmount.Add(unlockingCoinAmount...)

moduleAccount := authtypes.NewModuleAddress(types.ModuleName)
escrowBalances := k.bankKeeper.GetAllBalances(ctx, moduleAccount)

broken := !escrowBalances.IsAllGTE(totalLockCoinAmount)

return sdk.FormatInvariant(
types.ModuleName,
"ModuleAccountInvariants",
fmt.Sprintf(
"\tescrow coins balances: %v\n"+
"\ttotal lock coin amount: %v\n",
escrowBalances, totalLockCoinAmount),
), broken
}
}

func ValidatorLockDenomInvariants(k Keeper) sdk.Invariant {
return func(ctx sdk.Context) (string, bool) {
var (
msg string
broken bool
)

var multiStakingLocks []types.MultiStakingLock
k.MultiStakingLockIterator(ctx, func(stakingLock types.MultiStakingLock) bool {
multiStakingLocks = append(multiStakingLocks, stakingLock)
return false
})

for _, lock := range multiStakingLocks {
valBench32Addr := lock.LockID.ValAddr
valAddr, _ := sdk.ValAddressFromBech32(valBench32Addr)
if valMsDenom := k.GetValidatorMultiStakingCoin(ctx, valAddr); valMsDenom != lock.LockedCoin.Denom {
broken = true
msg += fmt.Sprintf("validator lock denom invariants:\n\t"+
"\tlock denom: %v allow denom: %v\n"+
"\tlock: %v\n",
lock.LockedCoin.Denom, valMsDenom, lock)
}
}

var multiStakingUnlocks []types.MultiStakingUnlock
k.MultiStakingUnlockIterator(ctx, func(stakingUnlock types.MultiStakingUnlock) bool {
multiStakingUnlocks = append(multiStakingUnlocks, stakingUnlock)
return false
})

for _, unlock := range multiStakingUnlocks {
valBench32Addr := unlock.UnlockID.ValAddr
valAddr, _ := sdk.ValAddressFromBech32(valBench32Addr)
valMsDenom := k.GetValidatorMultiStakingCoin(ctx, valAddr)

for _, entry := range unlock.Entries {
if entry.UnlockingCoin.Denom != valMsDenom {
broken = true
msg += fmt.Sprintf("validator unlock denom invariants:\n\t"+
"\n\tunlock denom: %v allow denom: %v\n"+
"\n\t entry height %v"+
"\n\t validator address %s deladdress %s",
entry.UnlockingCoin.Denom, valMsDenom, entry.CreationHeight, unlock.UnlockID.ValAddr, unlock.UnlockID.MultiStakerAddr)
}
}
}

return sdk.FormatInvariant(types.ModuleName, "validator lock denom", fmt.Sprintf("found invalid validator lock denom\n%s", msg)), broken
}
}
161 changes: 161 additions & 0 deletions x/multi-staking/keeper/invartiants_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
package keeper_test

import (
"time"

"github.com/realio-tech/multi-staking-module/testutil"
"github.com/realio-tech/multi-staking-module/x/multi-staking/keeper"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"

codectypes "github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
)

func (suite *KeeperTestSuite) TestModuleAccountInvariants() {
delAddr := testutil.GenAddress()
priv, valAddr := testutil.GenValAddressWithPrivKey()
valPubKey := priv.PubKey()

testCases := []struct {
name string
malleate func()
expPass bool
}{
{
name: "Success",
malleate: func() {},
expPass: true,
},
{
name: "Success Edit Validator",
malleate: func() {
suite.ctx = suite.ctx.WithBlockHeader(tmproto.Header{Time: time.Now()})
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 := suite.msgServer.EditValidator(suite.ctx, editMsg)
suite.Require().NoError(err)
},
expPass: true,
},
{
name: "Success Delegate",
malleate: func() {
bondAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(1000))
delMsg := stakingtypes.NewMsgDelegate(delAddr, valAddr, bondAmount)
_, err := suite.msgServer.Delegate(suite.ctx, delMsg)
suite.Require().NoError(err)
},
expPass: true,
},
{
name: "Success Delegate",
malleate: func() {
bondAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(1000))
delMsg := stakingtypes.NewMsgDelegate(delAddr, valAddr, bondAmount)
_, err := suite.msgServer.Delegate(suite.ctx, delMsg)
suite.Require().NoError(err)
},
expPass: true,
},
{
name: "Success BeginRedelegate",
malleate: func() {
priv, valAddr2 := testutil.GenValAddressWithPrivKey()
valPubKey2 := priv.PubKey()
bondAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(500))
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,
}

_, err := suite.msgServer.CreateValidator(suite.ctx, &createMsg2)
suite.Require().NoError(err)

multiStakingMsg := stakingtypes.NewMsgBeginRedelegate(delAddr, valAddr, valAddr2, bondAmount)
_, err = suite.msgServer.BeginRedelegate(suite.ctx, multiStakingMsg)
suite.Require().NoError(err)
},
expPass: true,
},
{
name: "Success Undelegate",
malleate: func() {
bondAmount := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(250))
multiStakingMsg := stakingtypes.NewMsgUndelegate(delAddr, valAddr, bondAmount)
_, err := suite.msgServer.Undelegate(suite.ctx, multiStakingMsg)
suite.Require().NoError(err)

bondAmount1 := sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(500))
multiStakingMsg1 := stakingtypes.NewMsgUndelegate(delAddr, valAddr, bondAmount1)
_, err = suite.msgServer.Undelegate(suite.ctx, multiStakingMsg1)
suite.Require().NoError(err)
},
expPass: true,
},
}
for _, tc := range testCases {
suite.SetupTest() // reset

valCoins := sdk.NewCoins(sdk.NewCoin(MultiStakingDenomA, sdk.NewInt(10000)), sdk.NewCoin(MultiStakingDenomB, sdk.NewInt(10000)))
err := suite.FundAccount(delAddr, valCoins)
suite.Require().NoError(err)

suite.msKeeper.SetBondWeight(suite.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 = suite.msgServer.CreateValidator(suite.ctx, &msg)
suite.Require().NoError(err)

tc.malleate()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why there's no fail case in this test

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

invariants should be success in any case

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there should be fail case in test, just set data in the store that causes it to fail

_, broken := keeper.ModuleAccountInvariants(*suite.msKeeper)(suite.ctx)

if tc.expPass {
suite.Require().False(broken)
}
}
}
51 changes: 47 additions & 4 deletions x/multi-staking/keeper/store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,19 @@ package keeper_test
import (
"github.com/realio-tech/multi-staking-module/testutil"
multistakingkeeper "github.com/realio-tech/multi-staking-module/x/multi-staking/keeper"
"github.com/realio-tech/multi-staking-module/x/multi-staking/types"

sdk "github.com/cosmos/cosmos-sdk/types"
)

var (
gasDenom = "ario"
govDenom = "arst"
)

func (suite *KeeperTestSuite) TestSetBondWeight() {
suite.SetupTest()

gasDenom := "ario"
govDenom := "arst"
gasWeight := sdk.OneDec()
govWeight := sdk.NewDecWithPrec(2, 4)

Expand All @@ -28,8 +32,7 @@ func (suite *KeeperTestSuite) TestSetBondWeight() {
func (suite *KeeperTestSuite) TestSetValidatorMultiStakingCoin() {
valA := testutil.GenValAddress()
valB := testutil.GenValAddress()
gasDenom := "ario"
govDenom := "arst"

testCases := []struct {
name string
malleate func(ctx sdk.Context, msKeeper *multistakingkeeper.Keeper) []string
Expand Down Expand Up @@ -86,3 +89,43 @@ func (suite *KeeperTestSuite) TestSetValidatorMultiStakingCoin() {
})
}
}

func (suite *KeeperTestSuite) TestSetMultiStakingLock() {
suite.SetupTest()
delAddr := testutil.GenAddress()
valAddr := testutil.GenValAddress()

lock := types.MultiStakingLock{
LockID: types.LockID{
MultiStakerAddr: delAddr.String(),
ValAddr: valAddr.String(),
},
LockedCoin: types.MultiStakingCoin{
Denom: gasDenom,
Amount: sdk.NewIntFromUint64(1000000),
BondWeight: sdk.NewDec(1),
},
}

testCases := []struct {
name string
malleate func()
expError bool
}{
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there's no fail case for this test

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is different test for check the store, not related in invariants. We should continue this test in different PR

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there should be fail case anyway because there is tc.expError.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If so I think we should move it to a separate PR

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's okay to put the test in this pr

{
"Success",
func() {
suite.msKeeper.SetMultiStakingLock(suite.ctx, lock)
},
false,
},
}
for _, tc := range testCases {
if !tc.expError {
tc.malleate()
msLock, found := suite.msKeeper.GetMultiStakingLock(suite.ctx, lock.LockID)
suite.Require().True(found)
suite.Require().Equal(lock, msLock)
}
}
}
3 changes: 2 additions & 1 deletion x/multi-staking/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,9 @@ func (AppModule) Name() string {
}

// RegisterInvariants registers the staking module invariants.
// TODO: Need to implement invariants
func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) {
am.skAppModule.RegisterInvariants(ir)
multistakingkeeper.RegisterInvariants(ir, am.keeper)
}

// Deprecated: Route returns the message routing key for the staking module.
Expand Down
1 change: 1 addition & 0 deletions x/multi-staking/types/expected_keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ type StakingKeeper interface {

type BankKeeper interface {
GetBalance(ctx sdk.Context, addr sdk.AccAddress, denom string) sdk.Coin
GetAllBalances(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins
MintCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error
BurnCoins(ctx sdk.Context, moduleName string, amounts sdk.Coins) error
SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error
Expand Down
Loading