Skip to content

Commit

Permalink
Merge pull request #1242 from lavanet/CNS-872-iprpc-unit-tests
Browse files Browse the repository at this point in the history
CNS-872: IPRPC part 5 - Unit tests
  • Loading branch information
Yaroms authored Mar 6, 2024
2 parents e3ba213 + 1a0e230 commit 4154781
Show file tree
Hide file tree
Showing 8 changed files with 858 additions and 86 deletions.
14 changes: 12 additions & 2 deletions testutil/common/tester.go
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,10 @@ func (ts *Tester) GetBalance(accAddr sdk.AccAddress) int64 {
return ts.Keepers.BankKeeper.GetBalance(ts.Ctx, accAddr, denom).Amount.Int64()
}

func (ts *Tester) GetBalances(accAddr sdk.AccAddress) sdk.Coins {
return ts.Keepers.BankKeeper.GetAllBalances(ts.Ctx, accAddr)
}

func (ts *Tester) FindPlan(index string, block uint64) (planstypes.Plan, bool) {
return ts.Keepers.Plans.FindPlan(ts.Ctx, index, block)
}
Expand Down Expand Up @@ -610,9 +614,14 @@ func (ts *Tester) TxPairingUnfreezeProvider(addr, chainID string) (*pairingtypes
return ts.Servers.PairingServer.UnfreezeProvider(ts.GoCtx, msg)
}

func (ts *Tester) TxRewardsSetIprpcDataProposal(ctx sdk.Context, authority string, cost sdk.Coin, subs []string) (*rewardstypes.MsgSetIprpcDataResponse, error) {
func (ts *Tester) TxRewardsSetIprpcDataProposal(authority string, cost sdk.Coin, subs []string) (*rewardstypes.MsgSetIprpcDataResponse, error) {
msg := rewardstypes.NewMsgSetIprpcData(authority, cost, subs)
return ts.Servers.RewardsServer.SetIprpcData(sdk.WrapSDKContext(ctx), msg)
return ts.Servers.RewardsServer.SetIprpcData(ts.GoCtx, msg)
}

func (ts *Tester) TxRewardsFundIprpc(creator string, spec string, duration uint64, fund sdk.Coins) (*rewardstypes.MsgFundIprpcResponse, error) {
msg := rewardstypes.NewMsgFundIprpc(creator, spec, duration, fund)
return ts.Servers.RewardsServer.FundIprpc(ts.GoCtx, msg)
}

// TxCreateValidator: implement 'tx staking createvalidator' and bond its tokens
Expand Down Expand Up @@ -859,6 +868,7 @@ func (ts *Tester) QueryRewardsBlockReward() (*rewardstypes.QueryBlockRewardRespo
return ts.Keepers.Rewards.BlockReward(ts.GoCtx, msg)
}

// QueryRewardsShowIprpcData implements 'q rewards show-iprpc-data'
func (ts *Tester) QueryRewardsShowIprpcData() (*rewardstypes.QueryShowIprpcDataResponse, error) {
msg := &rewardstypes.QueryShowIprpcDataRequest{}
return ts.Keepers.Rewards.ShowIprpcData(ts.GoCtx, msg)
Expand Down
43 changes: 5 additions & 38 deletions testutil/keeper/mock_keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,15 +77,9 @@ func (k mockBankKeeper) GetAllBalances(ctx sdk.Context, addr sdk.AccAddress) sdk
}

func (k mockBankKeeper) SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error {
// TODO support multiple coins
moduleAcc := GetModuleAddress(recipientModule)
if amt.Len() > 1 {
return fmt.Errorf("mockbankkeeper dont support more than 1 coin")
}
coin := amt[0]

accountCoin := k.GetBalance(ctx, senderAddr, coin.Denom)
if coin.Amount.GT(accountCoin.Amount) {
accountCoins := k.GetAllBalances(ctx, senderAddr)
if !accountCoins.IsAllGTE(amt) {
return fmt.Errorf("not enough coins")
}

Expand All @@ -99,47 +93,20 @@ func (k mockBankKeeper) UndelegateCoinsFromModuleToAccount(ctx sdk.Context, send
}

func (k mockBankKeeper) SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error {
// TODO support multiple coins
moduleAcc := GetModuleAddress(senderModule)

if amt.Len() > 1 {
return fmt.Errorf("mockbankkeeper doesn't support more than 1 coin")
}
coin := amt[0]

accountCoin := k.GetBalance(ctx, moduleAcc, coin.Denom)
if coin.Amount.GT(accountCoin.Amount) {
accountCoins := k.GetAllBalances(ctx, moduleAcc)
if !accountCoins.IsAllGTE(amt) {
return fmt.Errorf("not enough coins")
}

k.SubFromBalance(moduleAcc, amt)

k.AddToBalance(recipientAddr, amt)

return nil
}

func (k mockBankKeeper) SendCoinsFromModuleToModule(ctx sdk.Context, senderModule string, recipientModule string, amt sdk.Coins) error {
// TODO support multiple coins

senderModuleAcc := GetModuleAddress(senderModule)
recipientModuleAcc := GetModuleAddress(recipientModule)

if amt.Len() > 1 {
return fmt.Errorf("mockbankkeeper doesn't support more than 1 coin")
}
coin := amt[0]

senderAccountCoin := k.GetBalance(ctx, senderModuleAcc, coin.Denom)
if coin.Amount.GT(senderAccountCoin.Amount) {
return fmt.Errorf("not enough coins")
}

k.SubFromBalance(senderModuleAcc, amt)

k.AddToBalance(recipientModuleAcc, amt)

return nil
return k.SendCoinsFromAccountToModule(ctx, senderModuleAcc, recipientModule, amt)
}

func (k mockBankKeeper) DelegateCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error {
Expand Down
84 changes: 78 additions & 6 deletions x/rewards/keeper/helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@ package keeper_test

import (
"testing"
"time"

"cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
commontypes "github.com/lavanet/lava/common/types"
"github.com/lavanet/lava/testutil/common"
testkeeper "github.com/lavanet/lava/testutil/keeper"
planstypes "github.com/lavanet/lava/x/plans/types"
rewardsTypes "github.com/lavanet/lava/x/rewards/types"
rewardstypes "github.com/lavanet/lava/x/rewards/types"
spectypes "github.com/lavanet/lava/x/spec/types"
"github.com/stretchr/testify/require"
)
Expand All @@ -22,10 +25,20 @@ const (
feeCollectorName = authtypes.FeeCollectorName
)

var (
ibcDenom string = "uibc"
minIprpcCost sdk.Coin = sdk.NewCoin(commontypes.TokenDenom, sdk.NewInt(100))
iprpcFunds sdk.Coins = sdk.NewCoins(
sdk.NewCoin(commontypes.TokenDenom, sdk.NewInt(1100)),
sdk.NewCoin(ibcDenom, sdk.NewInt(500)),
)
mockSpec2 string = "mock2"
)

type tester struct {
common.Tester
plan planstypes.Plan
spec spectypes.Spec
plan planstypes.Plan
specs []spectypes.Spec
}

func newTester(t *testing.T, addValidator bool) *tester {
Expand All @@ -38,14 +51,14 @@ func newTester(t *testing.T, addValidator bool) *tester {
}

ts.plan = common.CreateMockPlan()
coins := ts.Keepers.Rewards.TotalPoolTokens(ts.Ctx, rewardsTypes.ProviderRewardsDistributionPool)
coins := ts.Keepers.Rewards.TotalPoolTokens(ts.Ctx, rewardstypes.ProviderRewardsDistributionPool)
_, monthlyProvidersPool := coins.Find(ts.BondDenom())
ts.plan.Price.Amount = monthlyProvidersPool.Amount.QuoRaw(5).AddRaw(5)
ts.plan.PlanPolicy.EpochCuLimit = monthlyProvidersPool.Amount.Uint64() * 5
ts.plan.PlanPolicy.TotalCuLimit = monthlyProvidersPool.Amount.Uint64() * 5
ts.plan.PlanPolicy.MaxProvidersToPair = 5
ts.AddPlan(ts.plan.Index, ts.plan)
ts.spec = ts.AddSpec("mock", common.CreateMockSpec()).Spec("mock")
ts.specs = []spectypes.Spec{ts.AddSpec("mock", common.CreateMockSpec()).Spec("mock")}

return ts
}
Expand All @@ -61,11 +74,70 @@ func (ts *tester) feeCollector() sdk.AccAddress {
return testkeeper.GetModuleAddress(feeCollectorName)
}

func (ts *tester) getPoolBalance(pool rewardsTypes.Pool, denom string) math.Int {
func (ts *tester) getPoolBalance(pool rewardstypes.Pool, denom string) math.Int {
coins := ts.Keepers.Rewards.TotalPoolTokens(ts.Ctx, pool)
return coins.AmountOf(denom)
}

func (ts *tester) iprpcAuthority() string {
return authtypes.NewModuleAddress(govtypes.ModuleName).String()
}

// setupForIprpcTests performs the following to set a proper env for iprpc tests:
// 0. it assumes that ts.newTester(t) was already executed
// 1. setting IPRPC data
// 2. fund the iprpc pool (optional, can specify if to fund from the next month)
func (ts *tester) setupForIprpcTests(fundIprpcPool bool) {
// add two consumers and buy subscriptions
consumerAcc, consumer := ts.AddAccount(common.CONSUMER, 0, testBalance*10000)
_, consumer2 := ts.AddAccount(common.CONSUMER, 1, testBalance*10000)
_, err := ts.TxSubscriptionBuy(consumer, consumer, ts.plan.Index, 5, true, false)
require.NoError(ts.T, err)
_, err = ts.TxSubscriptionBuy(consumer2, consumer2, ts.plan.Index, 5, true, false)
require.NoError(ts.T, err)

// set iprpc data (only consumer is IPRPC eligible)
_, err = ts.TxRewardsSetIprpcDataProposal(ts.iprpcAuthority(), minIprpcCost, []string{consumer})
require.NoError(ts.T, err)

// create a new spec
spec2 := common.CreateMockSpec()
spec2.Index = mockSpec2
spec2.Name = mockSpec2
ts.specs = append(ts.specs, ts.AddSpec(mockSpec2, spec2).Spec(mockSpec2))

// add two providers and stake them both on the two specs
_, provider := ts.AddAccount(common.PROVIDER, 0, testBalance)
_, provider2 := ts.AddAccount(common.PROVIDER, 1, testBalance)
err = ts.StakeProvider(provider, ts.specs[0], testStake)
require.NoError(ts.T, err)
err = ts.StakeProvider(provider, ts.specs[1], testStake)
require.NoError(ts.T, err)
err = ts.StakeProvider(provider2, ts.specs[0], testStake)
require.NoError(ts.T, err)
err = ts.StakeProvider(provider2, ts.specs[1], testStake)
require.NoError(ts.T, err)

ts.AdvanceEpoch() // apply pairing

// reset time to the start of the month
startOfMonth := time.Date(ts.Ctx.BlockTime().Year(), ts.Ctx.BlockTime().Month(), 1, 0, 0, 0, 0, ts.Ctx.BlockTime().Location())
ts.Ctx = ts.Ctx.WithBlockTime(startOfMonth)
ts.GoCtx = sdk.WrapSDKContext(ts.Ctx)

if fundIprpcPool {
duration := uint64(1)
err = ts.Keepers.BankKeeper.AddToBalance(consumerAcc.Addr, iprpcFunds.MulInt(sdk.NewIntFromUint64(duration)))
require.NoError(ts.T, err)
balanceBeforeFund := ts.GetBalances(consumerAcc.Addr)
_, err = ts.TxRewardsFundIprpc(consumer, mockSpec2, duration, iprpcFunds)
require.NoError(ts.T, err)
expectedBalanceAfterFund := balanceBeforeFund.Sub(iprpcFunds.MulInt(math.NewIntFromUint64(duration))...)
require.True(ts.T, ts.GetBalances(consumerAcc.Addr).IsEqual(expectedBalanceAfterFund))
ts.AdvanceMonths(1).AdvanceEpoch() // fund only fund for next month, so advance a month
}
}

// deductParticipationFees calculates the validators and community participation
// fees and returns the providers reward after deducting them
func (ts *tester) DeductParticipationFees(reward math.Int) (updatedReward math.Int, valParticipation math.Int, communityParticipation math.Int) {
Expand Down
14 changes: 10 additions & 4 deletions x/rewards/keeper/iprpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ func (k Keeper) FundIprpc(ctx sdk.Context, creator string, duration uint64, fund
}

// add spec funds to next month IPRPC reward object
k.addSpecFunds(ctx, spec, fund, duration)
k.addSpecFunds(ctx, spec, fund, duration, true)

return nil
}
Expand All @@ -64,7 +64,7 @@ func (k Keeper) FundIprpc(ctx sdk.Context, creator string, duration uint64, fund
// so the IPRPC rewards transfer to the next month
func (k Keeper) handleNoIprpcRewardToProviders(ctx sdk.Context, iprpcFunds []types.Specfund) {
for _, fund := range iprpcFunds {
k.addSpecFunds(ctx, fund.Spec, fund.Fund, 1)
k.addSpecFunds(ctx, fund.Spec, fund.Fund, 1, false)
}

details := map[string]string{
Expand Down Expand Up @@ -93,8 +93,14 @@ func (k Keeper) countIprpcCu(specCuMap map[string]types.SpecCuType, iprpcCu uint

// AddSpecFunds adds funds for a specific spec for <duration> of months.
// This function is used by the fund-iprpc TX.
func (k Keeper) addSpecFunds(ctx sdk.Context, spec string, fund sdk.Coins, duration uint64) {
startID := k.GetIprpcRewardsCurrentId(ctx) + 1 // fund IPRPC only from the next month for <duration> months
// use fromNextMonth=true for normal IPRPC fund (should always start from next month)
// use fromNextMonth=false for IPRPC reward transfer for next month (when no providers are eligible for IPRPC rewards)
func (k Keeper) addSpecFunds(ctx sdk.Context, spec string, fund sdk.Coins, duration uint64, fromNextMonth bool) {
startID := k.GetIprpcRewardsCurrentId(ctx)
if fromNextMonth {
startID++ // fund IPRPC only from the next month for <duration> months
}

for i := startID; i < startID+duration; i++ {
iprpcReward, found := k.GetIprpcReward(ctx, i)
if found {
Expand Down
2 changes: 1 addition & 1 deletion x/rewards/keeper/iprpc_data_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ func TestIprpcDataValidation(t *testing.T) {

for _, tt := range template {
t.Run(tt.name, func(t *testing.T) {
_, err := ts.TxRewardsSetIprpcDataProposal(ts.Ctx, tt.authority, tt.cost, tt.subs)
_, err := ts.TxRewardsSetIprpcDataProposal(tt.authority, tt.cost, tt.subs)
if tt.success {
require.NoError(t, err)
res, err := ts.QueryRewardsShowIprpcData()
Expand Down
Loading

0 comments on commit 4154781

Please sign in to comment.