From b7ba787b62955511a064579c9bc16f019bcd5c28 Mon Sep 17 00:00:00 2001 From: oren-lava Date: Thu, 15 Feb 2024 13:55:44 +0200 Subject: [PATCH 01/17] CNS-872: fix for iprpc reward object update --- x/rewards/keeper/iprpc_reward.go | 13 +++---------- x/rewards/keeper/providers.go | 3 ++- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/x/rewards/keeper/iprpc_reward.go b/x/rewards/keeper/iprpc_reward.go index b124b55e72..083e674908 100644 --- a/x/rewards/keeper/iprpc_reward.go +++ b/x/rewards/keeper/iprpc_reward.go @@ -86,16 +86,9 @@ func GetIprpcRewardIDFromBytes(bz []byte) uint64 { // PopIprpcReward gets the lowest id IprpcReward object and removes it func (k Keeper) PopIprpcReward(ctx sdk.Context) (types.IprpcReward, bool) { - // Get current IprpcReward - iprpcReward, found := k.GetIprpcReward(ctx, k.GetIprpcRewardsCurrent(ctx)) - if !found { - return types.IprpcReward{}, false - } - - // Remove the reward - k.RemoveIprpcReward(ctx, iprpcReward.Id) - - return iprpcReward, true + current := k.GetIprpcRewardsCurrent(ctx) + k.SetIprpcRewardsCurrent(ctx, current+1) + return k.GetIprpcReward(ctx, current) } // AddSpecFunds adds funds for a specific spec for of months. diff --git a/x/rewards/keeper/providers.go b/x/rewards/keeper/providers.go index 94eb41c3d3..48f09a2694 100644 --- a/x/rewards/keeper/providers.go +++ b/x/rewards/keeper/providers.go @@ -107,7 +107,8 @@ func (k Keeper) distributeMonthlyBonusRewards(ctx sdk.Context) { utils.LavaFormatError("current month iprpc reward not found", fmt.Errorf("did not reward providers IPRPC bonus")) return } - k.SetIprpcRewardsCurrent(ctx, iprpcReward.Id+1) + k.RemoveIprpcReward(ctx, iprpcReward.Id) + for _, specFund := range iprpcReward.SpecFunds { // collect details details := map[string]string{"spec": specFund.Spec, "rewards": specFund.Fund.String()} From 6b9cb8f069ff5a684e9bbfb78732dab10f2df56a Mon Sep 17 00:00:00 2001 From: oren-lava Date: Thu, 15 Feb 2024 14:41:56 +0200 Subject: [PATCH 02/17] CNS-872: update pool test with iprpc pool --- x/rewards/keeper/pool_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x/rewards/keeper/pool_test.go b/x/rewards/keeper/pool_test.go index f1fccf4c2c..ab18224d7a 100644 --- a/x/rewards/keeper/pool_test.go +++ b/x/rewards/keeper/pool_test.go @@ -37,8 +37,6 @@ import ( // 1. The allocation pool has the expected allocated funds minus one block reward // 2. The distribution pool has the expected monthly quota minus one block reward // 3. The fee collector has one block reward -// -// the validator got rewards func TestRewardsModuleSetup(t *testing.T) { ts := newTester(t, false) lifetime := int64(types.RewardsAllocationPoolsLifetime) @@ -64,6 +62,8 @@ func TestRewardsModuleSetup(t *testing.T) { require.Equal(t, allocationPoolBalance*(lifetime-1)/lifetime, pool.Balance.AmountOf(ts.BondDenom()).Int64()) case string(types.ValidatorsRewardsDistributionPoolName): require.Equal(t, (allocationPoolBalance/lifetime)-blockReward, pool.Balance.AmountOf(ts.BondDenom()).Int64()) + case string(types.IprpcPoolName): + require.True(t, pool.Balance.Empty()) } } From 8ae57c53215ff2bf60914a502c4281b43d9d1435 Mon Sep 17 00:00:00 2001 From: oren-lava Date: Thu, 15 Feb 2024 14:47:58 +0200 Subject: [PATCH 03/17] CNS-872: create small helper --- testutil/common/tester.go | 5 +++-- x/rewards/keeper/helpers_test.go | 5 +++++ x/rewards/keeper/iprpc_data_test.go | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/testutil/common/tester.go b/testutil/common/tester.go index 8b0f5807a3..708f4c1a14 100644 --- a/testutil/common/tester.go +++ b/testutil/common/tester.go @@ -610,9 +610,9 @@ 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) } // TxCreateValidator: implement 'tx staking createvalidator' and bond its tokens @@ -859,6 +859,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) diff --git a/x/rewards/keeper/helpers_test.go b/x/rewards/keeper/helpers_test.go index e2d009ab50..ddff8d8474 100644 --- a/x/rewards/keeper/helpers_test.go +++ b/x/rewards/keeper/helpers_test.go @@ -6,6 +6,7 @@ import ( "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" "github.com/lavanet/lava/testutil/common" testkeeper "github.com/lavanet/lava/testutil/keeper" @@ -66,6 +67,10 @@ func (ts *tester) getPoolBalance(pool rewardsTypes.Pool, denom string) math.Int return coins.AmountOf(denom) } +func (ts *tester) iprpcAuthority() string { + return authtypes.NewModuleAddress(govtypes.ModuleName).String() +} + // 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) { diff --git a/x/rewards/keeper/iprpc_data_test.go b/x/rewards/keeper/iprpc_data_test.go index f88cb59fbd..8d949565b1 100644 --- a/x/rewards/keeper/iprpc_data_test.go +++ b/x/rewards/keeper/iprpc_data_test.go @@ -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() From e287193bf6570b71e6696df67429056e700218bb Mon Sep 17 00:00:00 2001 From: oren-lava Date: Thu, 15 Feb 2024 18:12:17 +0200 Subject: [PATCH 04/17] CNS-872: make mock bank keeper support multiple coins --- testutil/keeper/mock_keepers.go | 47 +++------------------------------ 1 file changed, 4 insertions(+), 43 deletions(-) diff --git a/testutil/keeper/mock_keepers.go b/testutil/keeper/mock_keepers.go index fbe2c741ce..e0cb927683 100644 --- a/testutil/keeper/mock_keepers.go +++ b/testutil/keeper/mock_keepers.go @@ -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") } @@ -99,47 +93,14 @@ 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) { - return fmt.Errorf("not enough coins") - } - - k.SubFromBalance(moduleAcc, amt) - - k.AddToBalance(recipientAddr, amt) - - return nil + return k.SendCoinsFromAccountToModule(ctx, moduleAcc, recipientAddr.String(), amt) } 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, recipientModuleAcc.String(), amt) } func (k mockBankKeeper) DelegateCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error { From bbf3f1106e6d34373f686eda6192ebe63383f4a2 Mon Sep 17 00:00:00 2001 From: oren-lava Date: Thu, 15 Feb 2024 18:45:03 +0200 Subject: [PATCH 05/17] CNS-872: fix mock bank keeper --- testutil/keeper/mock_keepers.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/testutil/keeper/mock_keepers.go b/testutil/keeper/mock_keepers.go index e0cb927683..ab465c0e22 100644 --- a/testutil/keeper/mock_keepers.go +++ b/testutil/keeper/mock_keepers.go @@ -99,8 +99,7 @@ func (k mockBankKeeper) SendCoinsFromModuleToAccount(ctx sdk.Context, senderModu func (k mockBankKeeper) SendCoinsFromModuleToModule(ctx sdk.Context, senderModule string, recipientModule string, amt sdk.Coins) error { senderModuleAcc := GetModuleAddress(senderModule) - recipientModuleAcc := GetModuleAddress(recipientModule) - return k.SendCoinsFromAccountToModule(ctx, senderModuleAcc, recipientModuleAcc.String(), amt) + return k.SendCoinsFromAccountToModule(ctx, senderModuleAcc, recipientModule, amt) } func (k mockBankKeeper) DelegateCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error { From aaf6d4baa392fc1e00537201b84abafbe4418a33 Mon Sep 17 00:00:00 2001 From: oren-lava Date: Sun, 18 Feb 2024 12:29:57 +0200 Subject: [PATCH 06/17] CNS-872: fix iprpc reward bug --- x/rewards/keeper/iprpc_reward.go | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/x/rewards/keeper/iprpc_reward.go b/x/rewards/keeper/iprpc_reward.go index 083e674908..067a009003 100644 --- a/x/rewards/keeper/iprpc_reward.go +++ b/x/rewards/keeper/iprpc_reward.go @@ -102,22 +102,24 @@ func (k Keeper) addSpecFunds(ctx sdk.Context, spec string, fund sdk.Coins, durat for i := startID; i < duration; i++ { iprpcReward, found := k.GetIprpcReward(ctx, i) if found { - // found IPRPC reward + // found IPRPC reward, find if spec exists + specIndex := -1 for i := 0; i < len(iprpcReward.SpecFunds); i++ { if iprpcReward.SpecFunds[i].Spec == spec { - iprpcReward.SpecFunds[i].Fund = iprpcReward.SpecFunds[i].Fund.Add(fund...) - k.SetIprpcReward(ctx, iprpcReward) - return + specIndex = i } } - // did not find spec in IPRPC reward -> create a new one - iprpcReward.SpecFunds = append(iprpcReward.SpecFunds, types.Specfund{Spec: spec, Fund: fund}) - k.SetIprpcReward(ctx, iprpcReward) + // update spec funds + if specIndex >= 0 { + iprpcReward.SpecFunds[specIndex].Fund = iprpcReward.SpecFunds[specIndex].Fund.Add(fund...) + } else { + iprpcReward.SpecFunds = append(iprpcReward.SpecFunds, types.Specfund{Spec: spec, Fund: fund}) + } } else { // did not find IPRPC reward -> create a new one iprpcReward.Id = i iprpcReward.SpecFunds = []types.Specfund{{Spec: spec, Fund: fund}} - k.SetIprpcReward(ctx, iprpcReward) } + k.SetIprpcReward(ctx, iprpcReward) } } From 04b7f19a69305ec59855a892af01d340597c5d2c Mon Sep 17 00:00:00 2001 From: oren-lava Date: Wed, 21 Feb 2024 16:20:16 +0200 Subject: [PATCH 07/17] CNS-872: unit tests --- testutil/common/tester.go | 5 + x/rewards/keeper/helpers_test.go | 76 +++- x/rewards/keeper/iprpc_test.go | 642 +++++++++++++++++++++++++++++ x/rewards/keeper/providers_test.go | 66 +-- 4 files changed, 750 insertions(+), 39 deletions(-) create mode 100644 x/rewards/keeper/iprpc_test.go diff --git a/testutil/common/tester.go b/testutil/common/tester.go index 708f4c1a14..5ffdee65bf 100644 --- a/testutil/common/tester.go +++ b/testutil/common/tester.go @@ -615,6 +615,11 @@ func (ts *Tester) TxRewardsSetIprpcDataProposal(authority string, cost sdk.Coin, 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 func (ts *Tester) TxCreateValidator(validator sigs.Account, amount math.Int) { consensusPowerTokens := ts.Keepers.StakingKeeper.TokensFromConsensusPower(ts.Ctx, 1) diff --git a/x/rewards/keeper/helpers_test.go b/x/rewards/keeper/helpers_test.go index ddff8d8474..05aaea3234 100644 --- a/x/rewards/keeper/helpers_test.go +++ b/x/rewards/keeper/helpers_test.go @@ -2,16 +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" ) @@ -23,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 { @@ -39,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 } @@ -62,7 +74,7 @@ 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) } @@ -71,6 +83,58 @@ 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) + _, err = ts.TxRewardsFundIprpc(consumer, mockSpec2, duration, iprpcFunds) + require.NoError(ts.T, err) + 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) { diff --git a/x/rewards/keeper/iprpc_test.go b/x/rewards/keeper/iprpc_test.go new file mode 100644 index 0000000000..5d85a1abf6 --- /dev/null +++ b/x/rewards/keeper/iprpc_test.go @@ -0,0 +1,642 @@ +package keeper_test + +import ( + "testing" + + "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + distributiontypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + "github.com/lavanet/lava/testutil/common" + "github.com/lavanet/lava/utils/sigs" + rewardstypes "github.com/lavanet/lava/x/rewards/types" + "github.com/stretchr/testify/require" +) + +// TestFundIprpcTX tests the FundIprpc TX functionality in funding the IPRPC pool +// Scenarios: +// 1. fund IPRPC with different periods (1m,3m,12m) and different denominations (also combinations) +// -> pool balance and iprpc reward should be as expected +func TestFundIprpcTX(t *testing.T) { + ts := newTester(t, true) + ts.setupForIprpcTests(false) + + consumerAcc, consumer := ts.GetAccount(common.CONSUMER, 0) + err := ts.Keepers.BankKeeper.AddToBalance(consumerAcc.Addr, iprpcFunds) + require.NoError(ts.T, err) + + type fundIprpcData struct { + spec string + duration uint64 + fund sdk.Coins + } + + // we fund as follows (to all we add the min IPRPC price. the description below is the funds that go to the pool): + // - 10ulava, 1 month, mockspec + // - 50uibc, 1 month, mockspec + // - 90ulava + 30uibc, 3 months, mockspec2 + // - 130uibc, 3 months, mockspec + // - 10ulava + 120uibc, 12 months, mockspec2 + fundIprpcTXsData := []fundIprpcData{ + {spec: ts.specs[0].Index, duration: 1, fund: sdk.NewCoins( + sdk.NewCoin(ts.BondDenom(), math.NewInt(10+minIprpcCost.Amount.Int64())), + )}, + {spec: ts.specs[0].Index, duration: 1, fund: sdk.NewCoins( + sdk.NewCoin(ts.BondDenom(), math.NewInt(minIprpcCost.Amount.Int64())), + sdk.NewCoin(ibcDenom, math.NewInt(50)), + )}, + {spec: ts.specs[1].Index, duration: 3, fund: sdk.NewCoins( + sdk.NewCoin(ts.BondDenom(), math.NewInt(90+minIprpcCost.Amount.Int64()*3)), + sdk.NewCoin(ibcDenom, math.NewInt(30)), + )}, + {spec: ts.specs[0].Index, duration: 3, fund: sdk.NewCoins( + sdk.NewCoin(ts.BondDenom(), math.NewInt(minIprpcCost.Amount.Int64()*3)), + sdk.NewCoin(ibcDenom, math.NewInt(130)), + )}, + {spec: ts.specs[1].Index, duration: 12, fund: sdk.NewCoins( + sdk.NewCoin(ts.BondDenom(), math.NewInt(10+minIprpcCost.Amount.Int64()*12)), + sdk.NewCoin(ibcDenom, math.NewInt(120)), + )}, + } + + for _, txData := range fundIprpcTXsData { + _, err = ts.TxRewardsFundIprpc(consumer, txData.spec, txData.duration, txData.fund) + require.NoError(t, err) + } + + // Expected total IPRPC pool balance: 110ulava (=10+90+10) and 330uibc + iprpcTotalBalance := ts.Keepers.Rewards.TotalPoolTokens(ts.Ctx, rewardstypes.IprpcPoolName) + expectedIprpcTotalBalance := sdk.NewCoins( + sdk.NewCoin(ts.BondDenom(), math.NewInt(110)), + sdk.NewCoin(ibcDenom, math.NewInt(330)), + ) + require.True(t, expectedIprpcTotalBalance.IsEqual(iprpcTotalBalance)) + + // Expected IPRPC rewards (by months, first month is skipped): + // 1. mockspec: 10ulava + 180uibc(=50+130), mockspec2: 100ulava(=10+90) + 150uibc(=30+120) + // 2. mockspec: 130uibc, mockspec2: 100ulava(=10+90) + 150uibc(=30+120) + // 3. mockspec: 130uibc, mockspec2: 100ulava(=10+90) + 150uibc(=30+120) + // 4-12. mockspec: nothing, mockspec2: 10ulava + 120uibc + iprpcRewards := ts.Keepers.Rewards.GetAllIprpcReward(ts.Ctx) + require.Len(t, iprpcRewards, 12) + for i := range iprpcRewards { + var expectedSpecFunds []rewardstypes.Specfund + switch i { + case 0: + // first month + expectedSpecFunds = []rewardstypes.Specfund{ + { + Spec: ts.specs[0].Index, Fund: sdk.NewCoins( + sdk.NewCoin(ts.BondDenom(), math.NewInt(10)), + sdk.NewCoin(ibcDenom, math.NewInt(180)), + ), + }, + { + Spec: ts.specs[1].Index, Fund: sdk.NewCoins( + sdk.NewCoin(ts.BondDenom(), math.NewInt(100)), + sdk.NewCoin(ibcDenom, math.NewInt(150)), + ), + }, + } + case 1: + // second month + expectedSpecFunds = []rewardstypes.Specfund{ + { + Spec: ts.specs[0].Index, Fund: sdk.NewCoins( + sdk.NewCoin(ibcDenom, math.NewInt(130)), + ), + }, + { + Spec: ts.specs[1].Index, Fund: sdk.NewCoins( + sdk.NewCoin(ts.BondDenom(), math.NewInt(100)), + sdk.NewCoin(ibcDenom, math.NewInt(150)), + ), + }, + } + case 2: + // 3rd month + expectedSpecFunds = []rewardstypes.Specfund{ + { + Spec: ts.specs[0].Index, Fund: sdk.NewCoins( + sdk.NewCoin(ibcDenom, math.NewInt(130)), + ), + }, + { + Spec: ts.specs[1].Index, Fund: sdk.NewCoins( + sdk.NewCoin(ts.BondDenom(), math.NewInt(100)), + sdk.NewCoin(ibcDenom, math.NewInt(150)), + ), + }, + } + default: + // rest of months (until 12) + expectedSpecFunds = []rewardstypes.Specfund{ + { + Spec: ts.specs[1].Index, Fund: sdk.NewCoins( + sdk.NewCoin(ts.BondDenom(), math.NewInt(10)), + sdk.NewCoin(ibcDenom, math.NewInt(120)), + ), + }, + } + } + require.Equal(t, i+1, int(iprpcRewards[i].Id)) + require.ElementsMatch(t, expectedSpecFunds, iprpcRewards[i].SpecFunds) + } +} + +// TestIprpcProviderRewardQuery tests the IprpcProviderReward query functionality +// Scenarios: +// 1. two providers provide different CU for two consumers, which only one is IPRPC eligible -> query should return expected reward +// 2. advance a month, fund the pool and check the query's output again (without sending relays -> provider rewards should be empty) +func TestIprpcProviderRewardQuery(t *testing.T) { + ts := newTester(t, true) + ts.setupForIprpcTests(true) // setup funds IPRPC for mock2 spec + + // get consumers and providers (note, only c1 is IPRPC eligible) + c1Acc, _ := ts.GetAccount(common.CONSUMER, 0) + c2Acc, _ := ts.GetAccount(common.CONSUMER, 1) + _, p1 := ts.GetAccount(common.PROVIDER, 0) + _, p2 := ts.GetAccount(common.PROVIDER, 1) + + // send relays from both consumers to both providers + type relayInfo struct { + consumer sigs.Account + provider string + cu uint64 + } + relaysInfo := []relayInfo{ + {consumer: c1Acc, provider: p1, cu: 100}, + {consumer: c2Acc, provider: p1, cu: 150}, + {consumer: c1Acc, provider: p2, cu: 400}, + {consumer: c2Acc, provider: p2, cu: 450}, + } + for _, info := range relaysInfo { + msg := ts.SendRelay(info.provider, info.consumer, []string{ts.specs[1].Index}, info.cu) + _, err := ts.Servers.PairingServer.RelayPayment(ts.GoCtx, &msg) + require.NoError(t, err) + } + + // check the IprpcProviderReward query + // p1 should get 1/5 of the reward and p2 4/5 of the reward (p1 relative serviced CU is 100/500) + // note: setupForIprpcTests() funds the IPRPC pool with 1000ulava and 500uibc + type providerRewards struct { + provider string + fund sdk.Coins + } + expectedProviderRewards := []providerRewards{ + {provider: p1, fund: iprpcFunds.Sub(minIprpcCost).QuoInt(sdk.NewInt(5))}, + {provider: p2, fund: iprpcFunds.Sub(minIprpcCost).MulInt(sdk.NewInt(4)).QuoInt(sdk.NewInt(5))}, + } + for _, expectedProviderReward := range expectedProviderRewards { + res, err := ts.QueryRewardsIprpcProviderReward(expectedProviderReward.provider) + require.NoError(t, err) + require.ElementsMatch(t, expectedProviderReward.fund, res.SpecFunds[0].Fund) // taking 0 index because there's a single spec + } + + // advance month to distribute monthly rewards + ts.AdvanceMonths(1) + ts.AdvanceEpoch() + + // check that rewards were distributed as expected + for _, expectedProviderReward := range expectedProviderRewards { + res2, err := ts.QueryDualstakingDelegatorRewards(expectedProviderReward.provider, expectedProviderReward.provider, ts.specs[1].Index) + require.NoError(t, err) + require.True(t, res2.Rewards[0].Amount.IsEqual(expectedProviderReward.fund)) // taking 0 index because there are no delegators + } +} + +// TestIprpcSpecRewardQuery tests the IprpcSpecReward query functionality +// Scenarios: +// 0. assume IPRPC pool is funded with two denoms over different periods of vesting with two specs +// 1. query with no args should return all +// 2. query with arg should return the IPRPC rewards for the specific spec +// 3. advance a month, this month reward should transfer to next month -> query should return updated iprpc pool balance +// 4. make a provider provide service, advance a month to get his reward -> query should return updated iprpc pool balance +func TestIprpcSpecRewardQuery(t *testing.T) { + ts := newTester(t, true) + ts.setupForIprpcTests(true) // setup funds IPRPC for mock2 spec for 1 month and advances a month + + _, consumer := ts.GetAccount(common.CONSUMER, 0) + + // do another funding for mockspec and mock2 for 3 months + // Expected funds: + // first month: mock2 - 500uibc + 3000ulava, mockspec - 100000ulava + // second + third month: mock2 - 2000ulava, mockspec - 100000ulava + duration := int64(3) + minIprpcCostForFund := minIprpcCost.Amount.MulRaw(duration) + _, err := ts.TxRewardsFundIprpc(consumer, ts.specs[0].Index, uint64(duration), + sdk.NewCoins(sdk.NewCoin(ts.BondDenom(), sdk.NewInt(100000).Add(minIprpcCostForFund)))) + require.NoError(ts.T, err) + + _, err = ts.TxRewardsFundIprpc(consumer, ts.specs[1].Index, uint64(duration), + sdk.NewCoins(sdk.NewCoin(ts.BondDenom(), sdk.NewInt(2000).Add(minIprpcCostForFund)))) + require.NoError(ts.T, err) + + expectedResults := []rewardstypes.IprpcReward{ + { + Id: 1, SpecFunds: []rewardstypes.Specfund{ + {Spec: ts.specs[1].Index, Fund: sdk.NewCoins(sdk.NewCoin(ibcDenom, sdk.NewInt(500)), + sdk.NewCoin(ts.BondDenom(), sdk.NewInt(1000)))}, + }, + }, + { + Id: 2, SpecFunds: []rewardstypes.Specfund{ + {Spec: ts.specs[0].Index, Fund: sdk.NewCoins(sdk.NewCoin(ts.BondDenom(), sdk.NewInt(100000)))}, + {Spec: ts.specs[1].Index, Fund: sdk.NewCoins(sdk.NewCoin(ts.BondDenom(), sdk.NewInt(2000)))}, + }, + }, + { + Id: 3, SpecFunds: []rewardstypes.Specfund{ + {Spec: ts.specs[0].Index, Fund: sdk.NewCoins(sdk.NewCoin(ts.BondDenom(), sdk.NewInt(100000)))}, + {Spec: ts.specs[1].Index, Fund: sdk.NewCoins(sdk.NewCoin(ts.BondDenom(), sdk.NewInt(2000)))}, + }, + }, + { + Id: 4, SpecFunds: []rewardstypes.Specfund{ + {Spec: ts.specs[0].Index, Fund: sdk.NewCoins(sdk.NewCoin(ts.BondDenom(), sdk.NewInt(100000)))}, + {Spec: ts.specs[1].Index, Fund: sdk.NewCoins(sdk.NewCoin(ts.BondDenom(), sdk.NewInt(2000)))}, + }, + }, + } + + // query with no args + res, err := ts.QueryRewardsIprpcSpecReward("") + require.NoError(t, err) + require.ElementsMatch(t, expectedResults, res.IprpcRewards) + + // query with arg = mockspec + mockspecExpectedResults := []rewardstypes.IprpcReward{ + { + Id: 2, SpecFunds: []rewardstypes.Specfund{ + {Spec: ts.specs[0].Index, Fund: sdk.NewCoins(sdk.NewCoin(ts.BondDenom(), sdk.NewInt(100000)))}, + }, + }, + { + Id: 3, SpecFunds: []rewardstypes.Specfund{ + {Spec: ts.specs[0].Index, Fund: sdk.NewCoins(sdk.NewCoin(ts.BondDenom(), sdk.NewInt(100000)))}, + }, + }, + { + Id: 4, SpecFunds: []rewardstypes.Specfund{ + {Spec: ts.specs[0].Index, Fund: sdk.NewCoins(sdk.NewCoin(ts.BondDenom(), sdk.NewInt(100000)))}, + }, + }, + } + res, err = ts.QueryRewardsIprpcSpecReward(ts.specs[0].Index) + require.NoError(t, err) + require.ElementsMatch(t, mockspecExpectedResults, res.IprpcRewards) + + // advance a month with no providers getting rewarded this month's reward should transfer to the next month + // 2nd month expected funds: mockspec - 100000ulava, mock2 - 3000ulava(=2000+1000) and 500uibc + ts.AdvanceMonths(1) + ts.AdvanceEpoch() + + afterMonthExpectedResults := expectedResults[1:] + afterMonthExpectedResults[0].SpecFunds = []rewardstypes.Specfund{ + {Spec: ts.specs[0].Index, Fund: sdk.NewCoins(sdk.NewCoin(ts.BondDenom(), sdk.NewInt(100000)))}, + {Spec: ts.specs[1].Index, Fund: sdk.NewCoins( + sdk.NewCoin(ts.BondDenom(), sdk.NewInt(3000)), + sdk.NewCoin(ibcDenom, sdk.NewInt(500)), + )}, + } + res, err = ts.QueryRewardsIprpcSpecReward("") + require.NoError(t, err) + require.Len(t, res.IprpcRewards, len(afterMonthExpectedResults)) + for i := range res.IprpcRewards { + require.Equal(t, afterMonthExpectedResults[i].Id, res.IprpcRewards[i].Id) + require.ElementsMatch(t, afterMonthExpectedResults[i].SpecFunds, res.IprpcRewards[i].SpecFunds) + } + + // make a provider provide some service to an IPRPC eligible subscription + c1Acc, _ := ts.GetAccount(common.CONSUMER, 0) + _, p1 := ts.GetAccount(common.PROVIDER, 0) + relay := ts.SendRelay(p1, c1Acc, []string{ts.specs[1].Index}, 100) + _, err = ts.Servers.PairingServer.RelayPayment(ts.GoCtx, &relay) + require.NoError(t, err) + + // advance month to distribute monthly rewards + ts.AdvanceMonths(1) + ts.AdvanceEpoch() + + // check that the latest iprpc object has been deleted + afterProviderServiceExpectedResults := afterMonthExpectedResults[1:] + res, err = ts.QueryRewardsIprpcSpecReward("") + require.NoError(t, err) + require.ElementsMatch(t, afterProviderServiceExpectedResults, res.IprpcRewards) +} + +// TestIprpcRewardObjectsUpdate tests that the IPRPC reward objects' management works as expected: +// Scenarios: +// 0. fund iprpc pool for 2 months, current should be 0 and first iprpc reward should be with id=1 (fund is always for the next month) +// 1. there is no service to eligible subscriptions, month passes -> current shouldn't increment and there should be no IPRPC object +// 2. provider provides service for eligible subscription, month passes -> current should increment by 1 and a new IPRPC reward should be created with id=current +func TestIprpcRewardObjectsUpdate(t *testing.T) { + ts := newTester(t, true) + ts.setupForIprpcTests(false) + consumerAcc, consumer := ts.GetAccount(common.CONSUMER, 0) + + // fund iprpc pool + duration := uint64(2) + iprpcCost := sdk.NewCoin(ts.BondDenom(), minIprpcCost.Amount.MulRaw(int64(duration))) + fundForIprpc := iprpcFunds + err := ts.Keepers.BankKeeper.AddToBalance(consumerAcc.Addr, fundForIprpc) + require.NoError(ts.T, err) + _, err = ts.TxRewardsFundIprpc(consumer, mockSpec2, duration, iprpcFunds) + require.NoError(ts.T, err) + + // check there are 2 iprpc reward object, and the first one is with id=1 + currentIprpcRewardId := ts.Keepers.Rewards.GetIprpcRewardsCurrent(ts.Ctx) + require.Equal(t, uint64(0), currentIprpcRewardId) + res, err := ts.QueryRewardsIprpcSpecReward(mockSpec2) + require.NoError(t, err) + require.Len(t, res.IprpcRewards, 2) + require.Equal(t, uint64(0), res.CurrentMonthId) + for i := range res.IprpcRewards { + require.Equal(t, uint64(i+1), res.IprpcRewards[i].Id) + require.True(t, fundForIprpc.Sub(iprpcCost).IsEqual(res.IprpcRewards[i].SpecFunds[0].Fund)) + } + + // advance month to reach the first iprpc reward (first object is with id=1) + // there should still be the exact two objects as before + ts.AdvanceMonths(1) + ts.AdvanceEpoch() + currentIprpcRewardId = ts.Keepers.Rewards.GetIprpcRewardsCurrent(ts.Ctx) + require.Equal(t, uint64(1), currentIprpcRewardId) + res, err = ts.QueryRewardsIprpcSpecReward(mockSpec2) + require.NoError(t, err) + require.Len(t, res.IprpcRewards, 2) + require.Equal(t, uint64(1), res.CurrentMonthId) + for i := range res.IprpcRewards { + require.Equal(t, uint64(i+1), res.IprpcRewards[i].Id) + require.True(t, fundForIprpc.Sub(iprpcCost).IsEqual(res.IprpcRewards[i].SpecFunds[0].Fund)) + } + + // advance month without any provider service, there should be one IPRPC object with combined reward + ts.AdvanceMonths(1) + ts.AdvanceEpoch() + currentIprpcRewardId = ts.Keepers.Rewards.GetIprpcRewardsCurrent(ts.Ctx) + require.Equal(t, uint64(2), currentIprpcRewardId) + res, err = ts.QueryRewardsIprpcSpecReward(mockSpec2) + require.NoError(t, err) + require.Len(t, res.IprpcRewards, 1) + require.Equal(t, uint64(2), res.CurrentMonthId) + require.True(t, fundForIprpc.Sub(iprpcCost).MulInt(sdk.NewInt(2)).IsEqual(res.IprpcRewards[0].SpecFunds[0].Fund)) + + // make a provider service an IPRPC eligible consumer and advance a month + // there should be no iprpc rewards objects + c1Acc, _ := ts.GetAccount(common.CONSUMER, 0) + _, p1 := ts.GetAccount(common.PROVIDER, 0) + relay := ts.SendRelay(p1, c1Acc, []string{ts.specs[1].Index}, 100) + _, err = ts.Servers.PairingServer.RelayPayment(ts.GoCtx, &relay) + require.NoError(t, err) + ts.AdvanceMonths(1) + ts.AdvanceEpoch() + res, err = ts.QueryRewardsIprpcSpecReward(mockSpec2) + require.NoError(t, err) + require.Len(t, res.IprpcRewards, 0) + require.Equal(t, uint64(3), res.CurrentMonthId) +} + +// TestIprpcMinCost tests that a fund TX fails if it doesn't have enough tokens to cover for the minimum IPRPC costs +// Scenarios: +// 1. fund TX with the minimum cost available -> TX success +// 2. assume min cost = 100ulava, fund TX with 50ulava and 200ibc -> TX fails (ibc "has enough funds") +// 3. fund TX without the minimum cost available -> TX fails +// 4. fund TX with the minimum cost but creator doesn't have enough balance for the funding -> TX fails +func TestIprpcMinCost(t *testing.T) { + ts := newTester(t, true) + ts.setupForIprpcTests(false) + consumerAcc, consumer := ts.GetAccount(common.CONSUMER, 0) + err := ts.Keepers.BankKeeper.AddToBalance(consumerAcc.Addr, sdk.NewCoins(sdk.NewCoin(ibcDenom, sdk.NewInt(500)))) + + _, poorConsumer := ts.AddAccount(common.CONSUMER, 1, minIprpcCost.Amount.Int64()-10) + + testCases := []struct { + name string + creator string + fund sdk.Coins + success bool + }{ + { + name: "Happy flow - creator with enough funds and above min iprpc cost", + creator: consumer, + fund: sdk.NewCoins(minIprpcCost.AddAmount(sdk.NewInt(10))), + success: true, + }, + { + name: "fund without min iprpc cost", + creator: consumer, + fund: sdk.NewCoins(minIprpcCost.SubAmount(sdk.NewInt(10))), + success: false, + }, + { + name: "fund with other denom above min iprpc cost", + creator: consumer, + fund: sdk.NewCoins(sdk.NewCoin(ibcDenom, minIprpcCost.Amount.AddRaw(10))), + success: false, + }, + { + name: "insufficient balance for fund", + creator: poorConsumer, + fund: sdk.NewCoins(minIprpcCost.AddAmount(sdk.NewInt(10))), + success: false, + }, + } + + for _, tt := range testCases { + t.Run(tt.name, func(t *testing.T) { + _, err = ts.TxRewardsFundIprpc(tt.creator, mockSpec2, 1, tt.fund) + if tt.success { + require.NoError(t, err) + } else { + require.Error(t, err) + } + }) + } +} + +// TestIprpcEligibleSubscriptions tests that IPRPC CU is counted only if serviced an eligible subscription +// Scenarios: +// 0. assume two providers: p1, p2 and two consumers: c1, c2. Only c1 is IPRPC eligible +// 1. p1 provides service for both consumers, p2 provides service for c1 -> IPRPC reward should divide equally between p1 and p2 +// 2. both providers provide service for c2 -> No IPRPC rewards should be given +func TestIprpcEligibleSubscriptions(t *testing.T) { + ts := newTester(t, true) + ts.setupForIprpcTests(true) // setup creates consumers and providers and funds IPRPC pool for mock2 spec + + c1Acc, c1 := ts.GetAccount(common.CONSUMER, 0) + c2Acc, _ := ts.GetAccount(common.CONSUMER, 1) + _, p1 := ts.GetAccount(common.PROVIDER, 0) + _, p2 := ts.GetAccount(common.PROVIDER, 1) + + // p1 provides service for both consumers, p2 provides service for c1 + msg := ts.SendRelay(p1, c1Acc, []string{mockSpec2}, 100) + _, err := ts.TxPairingRelayPayment(msg.Creator, msg.Relays...) + require.NoError(t, err) + + msg = ts.SendRelay(p1, c2Acc, []string{mockSpec2}, 100) + _, err = ts.TxPairingRelayPayment(msg.Creator, msg.Relays...) + require.NoError(t, err) + + msg = ts.SendRelay(p2, c1Acc, []string{mockSpec2}, 100) + _, err = ts.TxPairingRelayPayment(msg.Creator, msg.Relays...) + require.NoError(t, err) + + // check expected reward for each provider, it should be equal (the service for c1 was equal) + res1, err := ts.QueryRewardsIprpcProviderReward(p1) + require.NoError(t, err) + res2, err := ts.QueryRewardsIprpcProviderReward(p2) + require.NoError(t, err) + require.True(t, res1.SpecFunds[0].Fund.IsEqual(res2.SpecFunds[0].Fund)) + require.True(t, iprpcFunds.Sub(minIprpcCost).QuoInt(sdk.NewInt(2)).IsEqual(res1.SpecFunds[0].Fund)) + + // fund the pool again (advance month to apply) + _, err = ts.TxRewardsFundIprpc(c1, mockSpec2, 1, sdk.NewCoins(minIprpcCost.AddAmount(sdk.NewInt(10)))) + require.NoError(ts.T, err) + ts.AdvanceMonths(1).AdvanceEpoch() + + // provide service only for c2 + msg = ts.SendRelay(p1, c2Acc, []string{mockSpec2}, 100) + _, err = ts.TxPairingRelayPayment(msg.Creator, msg.Relays...) + require.NoError(t, err) + + msg = ts.SendRelay(p2, c2Acc, []string{mockSpec2}, 100) + _, err = ts.TxPairingRelayPayment(msg.Creator, msg.Relays...) + require.NoError(t, err) + + // check none of the providers should get rewards + res1, err = ts.QueryRewardsIprpcProviderReward(p1) + require.NoError(t, err) + res2, err = ts.QueryRewardsIprpcProviderReward(p2) + require.NoError(t, err) + require.Len(t, res1.SpecFunds, 0) + require.Len(t, res2.SpecFunds, 0) +} + +// TestMultipleIprpcSpec checks that rewards are distributed correctly when multiple specs are configured in the IPRPC pool +// Scenarios: +// 0. IPRPC pool is funded for two specs for different periods and different denom (some are the same) +// 1. two providers provide service for consumer on 3 specs, two of them are the IPRPC ones -> they get rewarded relative to their serviced CU on each spec +func TestMultipleIprpcSpec(t *testing.T) { + ts := newTester(t, true) + ts.setupForIprpcTests(false) // creates consumers and providers staked on two stakes + + c1Acc, c1 := ts.GetAccount(common.CONSUMER, 0) + _, p1 := ts.GetAccount(common.PROVIDER, 0) + _, p2 := ts.GetAccount(common.PROVIDER, 1) + + // add another spec and stake the providers + mockSpec3 := "mock3" + spec3 := common.CreateMockSpec() + spec3.Index = mockSpec3 + spec3.Name = mockSpec3 + ts.specs = append(ts.specs, ts.AddSpec(mockSpec3, spec3).Spec(mockSpec3)) + err := ts.StakeProvider(p1, ts.specs[2], testStake) + require.NoError(ts.T, err) + err = ts.StakeProvider(p2, ts.specs[2], testStake) + require.NoError(ts.T, err) + + // fund iprpc pool for mock2 spec for 1 months + duration := uint64(1) + iprpcCost := sdk.NewCoin(ts.BondDenom(), minIprpcCost.Amount.MulRaw(int64(duration))) + mock2Fund := sdk.NewCoin(ts.BondDenom(), sdk.NewInt(1700)) + _, err = ts.TxRewardsFundIprpc(c1, mockSpec2, duration, sdk.NewCoins(mock2Fund.Add(iprpcCost))) + require.NoError(t, err) + + // fund iprpc pool for mock3 spec for 3 months + duration = uint64(3) + iprpcCost = sdk.NewCoin(ts.BondDenom(), minIprpcCost.Amount.MulRaw(int64(duration))) + mock3Fund := sdk.NewCoin(ts.BondDenom(), sdk.NewInt(400)) + _, err = ts.TxRewardsFundIprpc(c1, mockSpec3, duration, sdk.NewCoins(mock3Fund.Add(iprpcCost))) + require.NoError(t, err) + + // advance month and epoch to apply pairing and iprpc fund + ts.AdvanceMonths(1) + ts.AdvanceEpoch() + + // make both providers service the consumer on 3 specs, only 2 are funded by IPRPC + nonIprpcSpec := ts.specs[0].Index + type relayData struct { + provider string + spec string + cu uint64 + } + relaysData := []relayData{ + {provider: p1, spec: nonIprpcSpec, cu: 100}, + {provider: p1, spec: mockSpec2, cu: 200}, + {provider: p1, spec: mockSpec3, cu: 300}, + {provider: p2, spec: nonIprpcSpec, cu: 700}, + {provider: p2, spec: mockSpec2, cu: 200}, + {provider: p2, spec: mockSpec3, cu: 300}, + } + for _, rd := range relaysData { + msg := ts.SendRelay(rd.provider, c1Acc, []string{rd.spec}, rd.cu) + _, err = ts.TxPairingRelayPayment(msg.Creator, msg.Relays...) + require.NoError(t, err) + } + + // p1 total CU: 600, p2 total CU: 1200 -> if the rewards were divided by total CU (wrong) the rewards ratio should've been 1:2 + // p1 total iprpc CU: 500, p2 total iprpc CU: 500 -> if the rewards were divided by total iprpc CU the rewards should be equal + res1, err := ts.QueryRewardsIprpcProviderReward(p1) + require.NoError(t, err) + res2, err := ts.QueryRewardsIprpcProviderReward(p2) + require.NoError(t, err) + require.Equal(t, len(res1.SpecFunds), len(res2.SpecFunds)) + responses := []*rewardstypes.QueryIprpcProviderRewardResponse{res1, res2} + for _, res := range responses { + for _, sf := range res.SpecFunds { + switch sf.Spec { + case mockSpec2: + expectedReward := sdk.NewCoins(mock2Fund).QuoInt(sdk.NewInt(2)) + require.True(t, expectedReward.IsEqual(sf.Fund)) + case mockSpec3: + expectedReward := sdk.NewCoins(mock3Fund).QuoInt(sdk.NewInt(2)) + require.True(t, expectedReward.IsEqual(sf.Fund)) + } + } + } +} + +// TestIprpcRewardWithZeroSubRewards checks that even if a subscription is free (providers won't get paid for their service) +// if the providers service an IPRPC eligible subscription, they get IPRPC rewards +// Scenarios: +// 0. consumer is IPRPC eligible and community tax = 100% -> provider won't get paid for its service +// 1. two providers provide service -> they get IPRPC reward relative to their serviced CU +func TestIprpcRewardWithZeroSubRewards(t *testing.T) { + ts := newTester(t, true) + ts.setupForIprpcTests(true) // create a consumer and buys subscription + funds iprpc + + c1Acc, _ := ts.GetAccount(common.CONSUMER, 0) + _, p1 := ts.GetAccount(common.PROVIDER, 0) + _, p2 := ts.GetAccount(common.PROVIDER, 1) + + // make community participation percentage to be 100% to make the provider not get rewarded for its service later + distParams := distributiontypes.DefaultParams() + distParams.CommunityTax = sdk.OneDec() + err := ts.Keepers.Distribution.SetParams(ts.Ctx, distParams) + require.NoError(t, err) + + // make providers service the IPRPC eligible consumer + msg := ts.SendRelay(p1, c1Acc, []string{mockSpec2}, 100) + _, err = ts.TxPairingRelayPayment(msg.Creator, msg.Relays...) + require.NoError(t, err) + + msg = ts.SendRelay(p2, c1Acc, []string{mockSpec2}, 400) + _, err = ts.TxPairingRelayPayment(msg.Creator, msg.Relays...) + require.NoError(t, err) + + // advance month to trigger monthly iprpc reward + blocksToSave to trigger sub rewards + ts.AdvanceMonths(1) + ts.AdvanceEpoch() + ts.AdvanceBlocks(ts.BlocksToSave() + 1) + + // check provider rewards (should be only expected IPRPC rewards) + p1ExpectedReward := iprpcFunds.Sub(minIprpcCost).QuoInt(sdk.NewInt(5)) + res1, err := ts.QueryDualstakingDelegatorRewards(p1, p1, mockSpec2) + require.NoError(t, err) + require.True(t, p1ExpectedReward.IsEqual(res1.Rewards[0].Amount)) + + p2ExpectedReward := p1ExpectedReward.MulInt(sdk.NewInt(4)) + res2, err := ts.QueryDualstakingDelegatorRewards(p2, p2, mockSpec2) + require.NoError(t, err) + require.True(t, p2ExpectedReward.IsEqual(res2.Rewards[0].Amount)) +} diff --git a/x/rewards/keeper/providers_test.go b/x/rewards/keeper/providers_test.go index c2ed44c47d..abbeba4e41 100644 --- a/x/rewards/keeper/providers_test.go +++ b/x/rewards/keeper/providers_test.go @@ -18,7 +18,7 @@ func TestZeroProvidersRewards(t *testing.T) { ts := newTester(t, true) providerAcc, _ := ts.AddAccount(common.PROVIDER, 1, testBalance) - err := ts.StakeProvider(providerAcc.Addr.String(), ts.spec, testBalance) + err := ts.StakeProvider(providerAcc.Addr.String(), ts.specs[0], testBalance) require.NoError(t, err) ts.AdvanceEpoch() @@ -51,7 +51,7 @@ func TestBasicBoostProvidersRewards(t *testing.T) { ts := newTester(t, true) providerAcc, _ := ts.AddAccount(common.PROVIDER, 1, testBalance) - err := ts.StakeProvider(providerAcc.Addr.String(), ts.spec, testBalance) + err := ts.StakeProvider(providerAcc.Addr.String(), ts.specs[0], testBalance) require.NoError(t, err) ts.AdvanceEpoch() @@ -62,7 +62,7 @@ func TestBasicBoostProvidersRewards(t *testing.T) { baserewards := uint64(100) // the rewards by the subscription will be limited by LIMIT_TOKEN_PER_CU - msg := ts.SendRelay(providerAcc.Addr.String(), consumerAcc, []string{ts.spec.Index}, baserewards) + msg := ts.SendRelay(providerAcc.Addr.String(), consumerAcc, []string{ts.specs[0].Index}, baserewards) _, err = ts.TxPairingRelayPayment(msg.Creator, msg.Relays...) require.NoError(t, err) @@ -96,7 +96,7 @@ func TestSpecAllocationProvidersRewards(t *testing.T) { ts := newTester(t, true) providerAcc, _ := ts.AddAccount(common.PROVIDER, 1, testBalance) - err := ts.StakeProvider(providerAcc.Addr.String(), ts.spec, testBalance) + err := ts.StakeProvider(providerAcc.Addr.String(), ts.specs[0], testBalance) require.NoError(t, err) ts.AdvanceEpoch() @@ -105,7 +105,7 @@ func TestSpecAllocationProvidersRewards(t *testing.T) { _, err = ts.TxSubscriptionBuy(consumerAcc.Addr.String(), consumerAcc.Addr.String(), ts.plan.Index, 1, false, false) require.NoError(t, err) - msg := ts.SendRelay(providerAcc.Addr.String(), consumerAcc, []string{ts.spec.Index}, ts.plan.Price.Amount.Uint64()) + msg := ts.SendRelay(providerAcc.Addr.String(), consumerAcc, []string{ts.specs[0].Index}, ts.plan.Price.Amount.Uint64()) _, err = ts.TxPairingRelayPayment(msg.Creator, msg.Relays...) require.NoError(t, err) @@ -140,7 +140,7 @@ func TestProvidersDiminishingRewards(t *testing.T) { ts := newTester(t, true) providerAcc, _ := ts.AddAccount(common.PROVIDER, 1, testBalance) - err := ts.StakeProvider(providerAcc.Addr.String(), ts.spec, testBalance) + err := ts.StakeProvider(providerAcc.Addr.String(), ts.specs[0], testBalance) require.NoError(t, err) ts.AdvanceEpoch() @@ -150,7 +150,7 @@ func TestProvidersDiminishingRewards(t *testing.T) { _, err = ts.TxSubscriptionBuy(consumerAcc.Addr.String(), consumerAcc.Addr.String(), ts.plan.Index, 1, false, false) require.NoError(t, err) - msg := ts.SendRelay(providerAcc.Addr.String(), consumerAcc, []string{ts.spec.Index}, ts.plan.Price.Amount.Uint64()) + msg := ts.SendRelay(providerAcc.Addr.String(), consumerAcc, []string{ts.specs[0].Index}, ts.plan.Price.Amount.Uint64()) _, err = ts.TxPairingRelayPayment(msg.Creator, msg.Relays...) require.NoError(t, err) } @@ -189,7 +189,7 @@ func TestProvidersEndRewards(t *testing.T) { ts := newTester(t, true) providerAcc, _ := ts.AddAccount(common.PROVIDER, 1, testBalance) - err := ts.StakeProvider(providerAcc.Addr.String(), ts.spec, testBalance) + err := ts.StakeProvider(providerAcc.Addr.String(), ts.specs[0], testBalance) require.NoError(t, err) ts.AdvanceEpoch() @@ -199,7 +199,7 @@ func TestProvidersEndRewards(t *testing.T) { _, err = ts.TxSubscriptionBuy(consumerAcc.Addr.String(), consumerAcc.Addr.String(), ts.plan.Index, 1, false, false) require.NoError(t, err) - msg := ts.SendRelay(providerAcc.Addr.String(), consumerAcc, []string{ts.spec.Index}, ts.plan.Price.Amount.Uint64()) + msg := ts.SendRelay(providerAcc.Addr.String(), consumerAcc, []string{ts.specs[0].Index}, ts.plan.Price.Amount.Uint64()) _, err = ts.TxPairingRelayPayment(msg.Creator, msg.Relays...) require.NoError(t, err) } @@ -233,14 +233,14 @@ func TestProvidersEndRewards(t *testing.T) { // this means that no matter how much rewards the providers in this spec will get, they will get 0 bonus rewards func Test2SpecsZeroShares(t *testing.T) { ts := newTester(t, true) - spec2 := ts.spec + spec2 := ts.specs[0] spec2.Index = "mock2" spec2.Name = spec2.Index spec2.Shares = 0 ts.AddSpec(spec2.Index, spec2) providerAcc, _ := ts.AddAccount(common.PROVIDER, 1, 2*testBalance) - err := ts.StakeProvider(providerAcc.Addr.String(), ts.spec, testBalance) + err := ts.StakeProvider(providerAcc.Addr.String(), ts.specs[0], testBalance) require.NoError(t, err) err = ts.StakeProvider(providerAcc.Addr.String(), spec2, testBalance) @@ -252,7 +252,7 @@ func Test2SpecsZeroShares(t *testing.T) { _, err = ts.TxSubscriptionBuy(consumerAcc.Addr.String(), consumerAcc.Addr.String(), ts.plan.Index, 1, false, false) require.NoError(t, err) - msg := ts.SendRelay(providerAcc.Addr.String(), consumerAcc, []string{ts.spec.Index}, ts.plan.Price.Amount.Uint64()) + msg := ts.SendRelay(providerAcc.Addr.String(), consumerAcc, []string{ts.specs[0].Index}, ts.plan.Price.Amount.Uint64()) _, err = ts.TxPairingRelayPayment(msg.Creator, msg.Relays...) require.NoError(t, err) @@ -287,7 +287,7 @@ func Test2SpecsZeroShares(t *testing.T) { require.NoError(t, err) require.Len(t, res.Rewards, 1) require.Equal(t, distBalance.QuoRaw(int64(ts.Keepers.Rewards.MaxRewardBoost(ts.Ctx))), res.Rewards[0].Amount.AmountOf(ts.BondDenom())) - require.Equal(t, res.Rewards[0].ChainId, ts.spec.Index) + require.Equal(t, res.Rewards[0].ChainId, ts.specs[0].Index) _, err = ts.TxDualstakingClaimRewards(providerAcc.Addr.String(), providerAcc.Addr.String()) require.NoError(t, err) } @@ -297,14 +297,14 @@ func Test2SpecsZeroShares(t *testing.T) { // the bonus for the provider with double the shares should be double than the other provider func Test2SpecsDoubleShares(t *testing.T) { ts := newTester(t, true) - spec2 := ts.spec + spec2 := ts.specs[0] spec2.Index = "mock2" spec2.Name = spec2.Index spec2.Shares *= 2 ts.AddSpec(spec2.Index, spec2) providerAcc, _ := ts.AddAccount(common.PROVIDER, 1, 2*testBalance) - err := ts.StakeProvider(providerAcc.Addr.String(), ts.spec, testBalance) + err := ts.StakeProvider(providerAcc.Addr.String(), ts.specs[0], testBalance) require.NoError(t, err) err = ts.StakeProvider(providerAcc.Addr.String(), spec2, testBalance) @@ -316,7 +316,7 @@ func Test2SpecsDoubleShares(t *testing.T) { _, err = ts.TxSubscriptionBuy(consumerAcc.Addr.String(), consumerAcc.Addr.String(), ts.plan.Index, 1, false, false) require.NoError(t, err) - msg := ts.SendRelay(providerAcc.Addr.String(), consumerAcc, []string{ts.spec.Index}, ts.plan.Price.Amount.Uint64()) + msg := ts.SendRelay(providerAcc.Addr.String(), consumerAcc, []string{ts.specs[0].Index}, ts.plan.Price.Amount.Uint64()) _, err = ts.TxPairingRelayPayment(msg.Creator, msg.Relays...) require.NoError(t, err) @@ -360,15 +360,15 @@ func TestBonusRewards3Providers(t *testing.T) { ts := newTester(t, true) providerAcc1, _ := ts.AddAccount(common.PROVIDER, 1, 2*testBalance) - err := ts.StakeProvider(providerAcc1.Addr.String(), ts.spec, testBalance) + err := ts.StakeProvider(providerAcc1.Addr.String(), ts.specs[0], testBalance) require.NoError(t, err) providerAcc2, _ := ts.AddAccount(common.PROVIDER, 2, 2*testBalance) - err = ts.StakeProvider(providerAcc2.Addr.String(), ts.spec, 2*testBalance) + err = ts.StakeProvider(providerAcc2.Addr.String(), ts.specs[0], 2*testBalance) require.NoError(t, err) providerAcc3, _ := ts.AddAccount(common.PROVIDER, 3, 3*testBalance) - err = ts.StakeProvider(providerAcc3.Addr.String(), ts.spec, 3*testBalance) + err = ts.StakeProvider(providerAcc3.Addr.String(), ts.specs[0], 3*testBalance) require.NoError(t, err) ts.AdvanceEpoch() @@ -377,15 +377,15 @@ func TestBonusRewards3Providers(t *testing.T) { _, err = ts.TxSubscriptionBuy(consumerAcc.Addr.String(), consumerAcc.Addr.String(), ts.plan.Index, 1, false, false) require.NoError(t, err) - msg := ts.SendRelay(providerAcc1.Addr.String(), consumerAcc, []string{ts.spec.Index}, ts.plan.Price.Amount.Uint64()/2) + msg := ts.SendRelay(providerAcc1.Addr.String(), consumerAcc, []string{ts.specs[0].Index}, ts.plan.Price.Amount.Uint64()/2) _, err = ts.TxPairingRelayPayment(msg.Creator, msg.Relays...) require.NoError(t, err) - msg = ts.SendRelay(providerAcc2.Addr.String(), consumerAcc, []string{ts.spec.Index}, ts.plan.Price.Amount.Uint64()) + msg = ts.SendRelay(providerAcc2.Addr.String(), consumerAcc, []string{ts.specs[0].Index}, ts.plan.Price.Amount.Uint64()) _, err = ts.TxPairingRelayPayment(msg.Creator, msg.Relays...) require.NoError(t, err) - msg = ts.SendRelay(providerAcc3.Addr.String(), consumerAcc, []string{ts.spec.Index}, ts.plan.Price.Amount.Uint64()*2) + msg = ts.SendRelay(providerAcc3.Addr.String(), consumerAcc, []string{ts.specs[0].Index}, ts.plan.Price.Amount.Uint64()*2) _, err = ts.TxPairingRelayPayment(msg.Creator, msg.Relays...) require.NoError(t, err) @@ -464,7 +464,7 @@ func TestValidatorsAndCommunityParticipation(t *testing.T) { // create provider+comsumer, send relay and send relay payment TX providerAcc, _ := ts.AddAccount(common.PROVIDER, 1, testBalance) - err = ts.StakeProvider(providerAcc.Addr.String(), ts.spec, testBalance) + err = ts.StakeProvider(providerAcc.Addr.String(), ts.specs[0], testBalance) require.NoError(t, err) ts.AdvanceEpoch() @@ -475,7 +475,7 @@ func TestValidatorsAndCommunityParticipation(t *testing.T) { baserewards := uint64(100) // the rewards by the subscription will be limited by LIMIT_TOKEN_PER_CU - msg := ts.SendRelay(providerAcc.Addr.String(), consumerAcc, []string{ts.spec.Index}, baserewards) + msg := ts.SendRelay(providerAcc.Addr.String(), consumerAcc, []string{ts.specs[0].Index}, baserewards) _, err = ts.TxPairingRelayPayment(msg.Creator, msg.Relays...) require.NoError(t, err) @@ -509,7 +509,7 @@ func TestValidatorsAndCommunityParticipation(t *testing.T) { func TestBonusReward49months(t *testing.T) { ts := newTester(t, true) providerAcc, _ := ts.AddAccount(common.PROVIDER, 1, testBalance) - err := ts.StakeProvider(providerAcc.Addr.String(), ts.spec, testBalance) + err := ts.StakeProvider(providerAcc.Addr.String(), ts.specs[0], testBalance) require.NoError(t, err) ts.AdvanceEpoch() @@ -525,7 +525,7 @@ func TestBonusReward49months(t *testing.T) { baserewards := uint64(100) // the rewards by the subscription will be limited by LIMIT_TOKEN_PER_CU - msg := ts.SendRelay(providerAcc.Addr.String(), consumerAcc, []string{ts.spec.Index}, baserewards) + msg := ts.SendRelay(providerAcc.Addr.String(), consumerAcc, []string{ts.specs[0].Index}, baserewards) _, err = ts.TxPairingRelayPayment(msg.Creator, msg.Relays...) require.NoError(t, err) @@ -559,7 +559,7 @@ func TestBonusRewardsEquall5Providers(t *testing.T) { for i := 0; i < count; i++ { providerAcc, _ := ts.AddAccount(common.PROVIDER, 1, testBalance) - err := ts.StakeProvider(providerAcc.Addr.String(), ts.spec, testBalance) + err := ts.StakeProvider(providerAcc.Addr.String(), ts.specs[0], testBalance) providerAccs = append(providerAccs, providerAcc) require.NoError(t, err) @@ -574,7 +574,7 @@ func TestBonusRewardsEquall5Providers(t *testing.T) { for _, providerAcc := range providerAccs { for _, consAcc := range consAccs { - msg := ts.SendRelay(providerAcc.Addr.String(), consAcc, []string{ts.spec.Index}, ts.plan.Price.Amount.Uint64()/uint64(count)/1000) + msg := ts.SendRelay(providerAcc.Addr.String(), consAcc, []string{ts.specs[0].Index}, ts.plan.Price.Amount.Uint64()/uint64(count)/1000) _, err := ts.TxPairingRelayPayment(msg.Creator, msg.Relays...) require.NoError(t, err) } @@ -622,7 +622,7 @@ func TestBonusRewards5Providers(t *testing.T) { for i := 0; i < count; i++ { providerAcc, _ := ts.AddAccount(common.PROVIDER, 1, testBalance) - err := ts.StakeProvider(providerAcc.Addr.String(), ts.spec, testBalance) + err := ts.StakeProvider(providerAcc.Addr.String(), ts.specs[0], testBalance) providerAccs = append(providerAccs, providerAcc) require.NoError(t, err) @@ -635,13 +635,13 @@ func TestBonusRewards5Providers(t *testing.T) { for i := 1; i < 10; i++ { ts.AdvanceEpoch() - msg := ts.SendRelay(providerAccs[0].Addr.String(), consAccs[0], []string{ts.spec.Index}, ts.plan.Price.Amount.Uint64()/100) + msg := ts.SendRelay(providerAccs[0].Addr.String(), consAccs[0], []string{ts.specs[0].Index}, ts.plan.Price.Amount.Uint64()/100) _, err := ts.TxPairingRelayPayment(msg.Creator, msg.Relays...) require.NoError(t, err) for _, providerAcc := range providerAccs[1:] { for _, consAcc := range consAccs[1:] { - msg := ts.SendRelay(providerAcc.Addr.String(), consAcc, []string{ts.spec.Index}, ts.plan.Price.Amount.Uint64()/uint64(count)/100) + msg := ts.SendRelay(providerAcc.Addr.String(), consAcc, []string{ts.specs[0].Index}, ts.plan.Price.Amount.Uint64()/uint64(count)/100) _, err := ts.TxPairingRelayPayment(msg.Creator, msg.Relays...) require.NoError(t, err) } @@ -708,7 +708,7 @@ func TestCommunityTaxOne(t *testing.T) { // create provider+comsumer, send relay and send relay payment TX providerAcc, _ := ts.AddAccount(common.PROVIDER, 1, testBalance) - err = ts.StakeProvider(providerAcc.Addr.String(), ts.spec, testBalance) + err = ts.StakeProvider(providerAcc.Addr.String(), ts.specs[0], testBalance) require.NoError(t, err) ts.AdvanceEpoch() @@ -719,7 +719,7 @@ func TestCommunityTaxOne(t *testing.T) { baserewards := uint64(100) // the rewards by the subscription will be limited by LIMIT_TOKEN_PER_CU - msg := ts.SendRelay(providerAcc.Addr.String(), consumerAcc, []string{ts.spec.Index}, baserewards) + msg := ts.SendRelay(providerAcc.Addr.String(), consumerAcc, []string{ts.specs[0].Index}, baserewards) _, err = ts.TxPairingRelayPayment(msg.Creator, msg.Relays...) require.NoError(t, err) From 481457792caa2bf6d4ee574d247b63344ff29307 Mon Sep 17 00:00:00 2001 From: oren-lava Date: Wed, 21 Feb 2024 18:34:47 +0200 Subject: [PATCH 08/17] CNS-872: fix mock bank keeper --- testutil/keeper/mock_keepers.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/testutil/keeper/mock_keepers.go b/testutil/keeper/mock_keepers.go index ab465c0e22..d8512793d1 100644 --- a/testutil/keeper/mock_keepers.go +++ b/testutil/keeper/mock_keepers.go @@ -94,7 +94,14 @@ func (k mockBankKeeper) UndelegateCoinsFromModuleToAccount(ctx sdk.Context, send func (k mockBankKeeper) SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error { moduleAcc := GetModuleAddress(senderModule) - return k.SendCoinsFromAccountToModule(ctx, moduleAcc, recipientAddr.String(), amt) + 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 { From ce6e04119c4e5af6461872e00b727eaa23a5d43b Mon Sep 17 00:00:00 2001 From: oren-lava Date: Mon, 26 Feb 2024 15:59:44 +0200 Subject: [PATCH 09/17] CNS-872: fix lint --- x/rewards/keeper/iprpc_test.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/x/rewards/keeper/iprpc_test.go b/x/rewards/keeper/iprpc_test.go index 5d85a1abf6..1150b29581 100644 --- a/x/rewards/keeper/iprpc_test.go +++ b/x/rewards/keeper/iprpc_test.go @@ -187,7 +187,7 @@ func TestIprpcProviderRewardQuery(t *testing.T) { {provider: p2, fund: iprpcFunds.Sub(minIprpcCost).MulInt(sdk.NewInt(4)).QuoInt(sdk.NewInt(5))}, } for _, expectedProviderReward := range expectedProviderRewards { - res, err := ts.QueryRewardsIprpcProviderReward(expectedProviderReward.provider) + res, err := ts.QueryRewardsIprpcProviderRewardEstimation(expectedProviderReward.provider) require.NoError(t, err) require.ElementsMatch(t, expectedProviderReward.fund, res.SpecFunds[0].Fund) // taking 0 index because there's a single spec } @@ -482,9 +482,9 @@ func TestIprpcEligibleSubscriptions(t *testing.T) { require.NoError(t, err) // check expected reward for each provider, it should be equal (the service for c1 was equal) - res1, err := ts.QueryRewardsIprpcProviderReward(p1) + res1, err := ts.QueryRewardsIprpcProviderRewardEstimation(p1) require.NoError(t, err) - res2, err := ts.QueryRewardsIprpcProviderReward(p2) + res2, err := ts.QueryRewardsIprpcProviderRewardEstimation(p2) require.NoError(t, err) require.True(t, res1.SpecFunds[0].Fund.IsEqual(res2.SpecFunds[0].Fund)) require.True(t, iprpcFunds.Sub(minIprpcCost).QuoInt(sdk.NewInt(2)).IsEqual(res1.SpecFunds[0].Fund)) @@ -504,9 +504,9 @@ func TestIprpcEligibleSubscriptions(t *testing.T) { require.NoError(t, err) // check none of the providers should get rewards - res1, err = ts.QueryRewardsIprpcProviderReward(p1) + res1, err = ts.QueryRewardsIprpcProviderRewardEstimation(p1) require.NoError(t, err) - res2, err = ts.QueryRewardsIprpcProviderReward(p2) + res2, err = ts.QueryRewardsIprpcProviderRewardEstimation(p2) require.NoError(t, err) require.Len(t, res1.SpecFunds, 0) require.Len(t, res2.SpecFunds, 0) @@ -576,12 +576,12 @@ func TestMultipleIprpcSpec(t *testing.T) { // p1 total CU: 600, p2 total CU: 1200 -> if the rewards were divided by total CU (wrong) the rewards ratio should've been 1:2 // p1 total iprpc CU: 500, p2 total iprpc CU: 500 -> if the rewards were divided by total iprpc CU the rewards should be equal - res1, err := ts.QueryRewardsIprpcProviderReward(p1) + res1, err := ts.QueryRewardsIprpcProviderRewardEstimation(p1) require.NoError(t, err) - res2, err := ts.QueryRewardsIprpcProviderReward(p2) + res2, err := ts.QueryRewardsIprpcProviderRewardEstimation(p2) require.NoError(t, err) require.Equal(t, len(res1.SpecFunds), len(res2.SpecFunds)) - responses := []*rewardstypes.QueryIprpcProviderRewardResponse{res1, res2} + responses := []*rewardstypes.QueryIprpcProviderRewardEstimationResponse{res1, res2} for _, res := range responses { for _, sf := range res.SpecFunds { switch sf.Spec { From 617a02df4b277921ff212c233d04022dff646e6b Mon Sep 17 00:00:00 2001 From: oren-lava Date: Wed, 6 Mar 2024 11:55:21 +0200 Subject: [PATCH 10/17] CNS-872: small fix --- x/rewards/keeper/iprpc_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/x/rewards/keeper/iprpc_test.go b/x/rewards/keeper/iprpc_test.go index 1150b29581..f41c26bcb0 100644 --- a/x/rewards/keeper/iprpc_test.go +++ b/x/rewards/keeper/iprpc_test.go @@ -344,7 +344,7 @@ func TestIprpcRewardObjectsUpdate(t *testing.T) { require.NoError(ts.T, err) // check there are 2 iprpc reward object, and the first one is with id=1 - currentIprpcRewardId := ts.Keepers.Rewards.GetIprpcRewardsCurrent(ts.Ctx) + currentIprpcRewardId := ts.Keepers.Rewards.GetIprpcRewardsCurrentId(ts.Ctx) require.Equal(t, uint64(0), currentIprpcRewardId) res, err := ts.QueryRewardsIprpcSpecReward(mockSpec2) require.NoError(t, err) @@ -359,7 +359,7 @@ func TestIprpcRewardObjectsUpdate(t *testing.T) { // there should still be the exact two objects as before ts.AdvanceMonths(1) ts.AdvanceEpoch() - currentIprpcRewardId = ts.Keepers.Rewards.GetIprpcRewardsCurrent(ts.Ctx) + currentIprpcRewardId = ts.Keepers.Rewards.GetIprpcRewardsCurrentId(ts.Ctx) require.Equal(t, uint64(1), currentIprpcRewardId) res, err = ts.QueryRewardsIprpcSpecReward(mockSpec2) require.NoError(t, err) @@ -373,7 +373,7 @@ func TestIprpcRewardObjectsUpdate(t *testing.T) { // advance month without any provider service, there should be one IPRPC object with combined reward ts.AdvanceMonths(1) ts.AdvanceEpoch() - currentIprpcRewardId = ts.Keepers.Rewards.GetIprpcRewardsCurrent(ts.Ctx) + currentIprpcRewardId = ts.Keepers.Rewards.GetIprpcRewardsCurrentId(ts.Ctx) require.Equal(t, uint64(2), currentIprpcRewardId) res, err = ts.QueryRewardsIprpcSpecReward(mockSpec2) require.NoError(t, err) From 7f71d5db718bbd3f1e2fd8d662737223f3734ea0 Mon Sep 17 00:00:00 2001 From: oren-lava Date: Wed, 6 Mar 2024 12:24:11 +0200 Subject: [PATCH 11/17] CNS-872: unit test fix --- x/rewards/keeper/iprpc.go | 14 ++++++++++---- x/rewards/keeper/iprpc_test.go | 3 +++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/x/rewards/keeper/iprpc.go b/x/rewards/keeper/iprpc.go index 12006885a9..35c2a2fcb5 100644 --- a/x/rewards/keeper/iprpc.go +++ b/x/rewards/keeper/iprpc.go @@ -53,7 +53,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 } @@ -62,7 +62,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{ @@ -91,8 +91,14 @@ func (k Keeper) countIprpcCu(specCuMap map[string]types.SpecCuType, iprpcCu uint // AddSpecFunds adds funds for a specific spec for 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 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 = startID + 1 // fund IPRPC only from the next month for months + } + for i := startID; i < startID+duration; i++ { iprpcReward, found := k.GetIprpcReward(ctx, i) if found { diff --git a/x/rewards/keeper/iprpc_test.go b/x/rewards/keeper/iprpc_test.go index f41c26bcb0..510d88d7fa 100644 --- a/x/rewards/keeper/iprpc_test.go +++ b/x/rewards/keeper/iprpc_test.go @@ -312,6 +312,9 @@ func TestIprpcSpecRewardQuery(t *testing.T) { relay := ts.SendRelay(p1, c1Acc, []string{ts.specs[1].Index}, 100) _, err = ts.Servers.PairingServer.RelayPayment(ts.GoCtx, &relay) require.NoError(t, err) + relay = ts.SendRelay(p1, c1Acc, []string{ts.specs[0].Index}, 100) + _, err = ts.Servers.PairingServer.RelayPayment(ts.GoCtx, &relay) + require.NoError(t, err) // advance month to distribute monthly rewards ts.AdvanceMonths(1) From 63298c12ad3dd0405fecba6b9b339c6ff9b7a735 Mon Sep 17 00:00:00 2001 From: oren-lava Date: Wed, 6 Mar 2024 12:29:18 +0200 Subject: [PATCH 12/17] CNS-872L lint fix --- x/rewards/keeper/iprpc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/rewards/keeper/iprpc.go b/x/rewards/keeper/iprpc.go index 35c2a2fcb5..812b00da10 100644 --- a/x/rewards/keeper/iprpc.go +++ b/x/rewards/keeper/iprpc.go @@ -96,7 +96,7 @@ func (k Keeper) countIprpcCu(specCuMap map[string]types.SpecCuType, iprpcCu uint func (k Keeper) addSpecFunds(ctx sdk.Context, spec string, fund sdk.Coins, duration uint64, fromNextMonth bool) { startID := k.GetIprpcRewardsCurrentId(ctx) if fromNextMonth { - startID = startID + 1 // fund IPRPC only from the next month for months + startID++ // fund IPRPC only from the next month for months } for i := startID; i < startID+duration; i++ { From 31124f597c84dd30ad7dd741dc939b332ea02370 Mon Sep 17 00:00:00 2001 From: oren-lava Date: Wed, 6 Mar 2024 12:47:01 +0200 Subject: [PATCH 13/17] CNS-872: fix unit tests --- x/rewards/keeper/iprpc_test.go | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/x/rewards/keeper/iprpc_test.go b/x/rewards/keeper/iprpc_test.go index 510d88d7fa..044249f0b2 100644 --- a/x/rewards/keeper/iprpc_test.go +++ b/x/rewards/keeper/iprpc_test.go @@ -1,6 +1,7 @@ package keeper_test import ( + "fmt" "testing" "cosmossdk.io/math" @@ -45,15 +46,15 @@ func TestFundIprpcTX(t *testing.T) { sdk.NewCoin(ibcDenom, math.NewInt(50)), )}, {spec: ts.specs[1].Index, duration: 3, fund: sdk.NewCoins( - sdk.NewCoin(ts.BondDenom(), math.NewInt(90+minIprpcCost.Amount.Int64()*3)), + sdk.NewCoin(ts.BondDenom(), math.NewInt(90+minIprpcCost.Amount.Int64())), sdk.NewCoin(ibcDenom, math.NewInt(30)), )}, {spec: ts.specs[0].Index, duration: 3, fund: sdk.NewCoins( - sdk.NewCoin(ts.BondDenom(), math.NewInt(minIprpcCost.Amount.Int64()*3)), + sdk.NewCoin(ts.BondDenom(), math.NewInt(minIprpcCost.Amount.Int64())), sdk.NewCoin(ibcDenom, math.NewInt(130)), )}, {spec: ts.specs[1].Index, duration: 12, fund: sdk.NewCoins( - sdk.NewCoin(ts.BondDenom(), math.NewInt(10+minIprpcCost.Amount.Int64()*12)), + sdk.NewCoin(ts.BondDenom(), math.NewInt(10+minIprpcCost.Amount.Int64())), sdk.NewCoin(ibcDenom, math.NewInt(120)), )}, } @@ -222,13 +223,12 @@ func TestIprpcSpecRewardQuery(t *testing.T) { // first month: mock2 - 500uibc + 3000ulava, mockspec - 100000ulava // second + third month: mock2 - 2000ulava, mockspec - 100000ulava duration := int64(3) - minIprpcCostForFund := minIprpcCost.Amount.MulRaw(duration) _, err := ts.TxRewardsFundIprpc(consumer, ts.specs[0].Index, uint64(duration), - sdk.NewCoins(sdk.NewCoin(ts.BondDenom(), sdk.NewInt(100000).Add(minIprpcCostForFund)))) + sdk.NewCoins(sdk.NewCoin(ts.BondDenom(), sdk.NewInt(100000).Add(minIprpcCost.Amount)))) require.NoError(ts.T, err) _, err = ts.TxRewardsFundIprpc(consumer, ts.specs[1].Index, uint64(duration), - sdk.NewCoins(sdk.NewCoin(ts.BondDenom(), sdk.NewInt(2000).Add(minIprpcCostForFund)))) + sdk.NewCoins(sdk.NewCoin(ts.BondDenom(), sdk.NewInt(2000).Add(minIprpcCost.Amount)))) require.NoError(ts.T, err) expectedResults := []rewardstypes.IprpcReward{ @@ -261,6 +261,8 @@ func TestIprpcSpecRewardQuery(t *testing.T) { // query with no args res, err := ts.QueryRewardsIprpcSpecReward("") require.NoError(t, err) + fmt.Printf("expectedResults: %v\n", expectedResults) + fmt.Printf("res.IprpcRewards: %v\n", res.IprpcRewards) require.ElementsMatch(t, expectedResults, res.IprpcRewards) // query with arg = mockspec @@ -339,9 +341,7 @@ func TestIprpcRewardObjectsUpdate(t *testing.T) { // fund iprpc pool duration := uint64(2) - iprpcCost := sdk.NewCoin(ts.BondDenom(), minIprpcCost.Amount.MulRaw(int64(duration))) - fundForIprpc := iprpcFunds - err := ts.Keepers.BankKeeper.AddToBalance(consumerAcc.Addr, fundForIprpc) + err := ts.Keepers.BankKeeper.AddToBalance(consumerAcc.Addr, iprpcFunds) require.NoError(ts.T, err) _, err = ts.TxRewardsFundIprpc(consumer, mockSpec2, duration, iprpcFunds) require.NoError(ts.T, err) @@ -355,7 +355,7 @@ func TestIprpcRewardObjectsUpdate(t *testing.T) { require.Equal(t, uint64(0), res.CurrentMonthId) for i := range res.IprpcRewards { require.Equal(t, uint64(i+1), res.IprpcRewards[i].Id) - require.True(t, fundForIprpc.Sub(iprpcCost).IsEqual(res.IprpcRewards[i].SpecFunds[0].Fund)) + require.True(t, iprpcFunds.Sub(minIprpcCost).IsEqual(res.IprpcRewards[i].SpecFunds[0].Fund)) } // advance month to reach the first iprpc reward (first object is with id=1) @@ -370,7 +370,7 @@ func TestIprpcRewardObjectsUpdate(t *testing.T) { require.Equal(t, uint64(1), res.CurrentMonthId) for i := range res.IprpcRewards { require.Equal(t, uint64(i+1), res.IprpcRewards[i].Id) - require.True(t, fundForIprpc.Sub(iprpcCost).IsEqual(res.IprpcRewards[i].SpecFunds[0].Fund)) + require.True(t, iprpcFunds.Sub(minIprpcCost).IsEqual(res.IprpcRewards[i].SpecFunds[0].Fund)) } // advance month without any provider service, there should be one IPRPC object with combined reward @@ -382,7 +382,7 @@ func TestIprpcRewardObjectsUpdate(t *testing.T) { require.NoError(t, err) require.Len(t, res.IprpcRewards, 1) require.Equal(t, uint64(2), res.CurrentMonthId) - require.True(t, fundForIprpc.Sub(iprpcCost).MulInt(sdk.NewInt(2)).IsEqual(res.IprpcRewards[0].SpecFunds[0].Fund)) + require.True(t, iprpcFunds.Sub(minIprpcCost).MulInt(sdk.NewInt(2)).IsEqual(res.IprpcRewards[0].SpecFunds[0].Fund)) // make a provider service an IPRPC eligible consumer and advance a month // there should be no iprpc rewards objects @@ -540,16 +540,14 @@ func TestMultipleIprpcSpec(t *testing.T) { // fund iprpc pool for mock2 spec for 1 months duration := uint64(1) - iprpcCost := sdk.NewCoin(ts.BondDenom(), minIprpcCost.Amount.MulRaw(int64(duration))) mock2Fund := sdk.NewCoin(ts.BondDenom(), sdk.NewInt(1700)) - _, err = ts.TxRewardsFundIprpc(c1, mockSpec2, duration, sdk.NewCoins(mock2Fund.Add(iprpcCost))) + _, err = ts.TxRewardsFundIprpc(c1, mockSpec2, duration, sdk.NewCoins(mock2Fund.Add(minIprpcCost))) require.NoError(t, err) // fund iprpc pool for mock3 spec for 3 months duration = uint64(3) - iprpcCost = sdk.NewCoin(ts.BondDenom(), minIprpcCost.Amount.MulRaw(int64(duration))) mock3Fund := sdk.NewCoin(ts.BondDenom(), sdk.NewInt(400)) - _, err = ts.TxRewardsFundIprpc(c1, mockSpec3, duration, sdk.NewCoins(mock3Fund.Add(iprpcCost))) + _, err = ts.TxRewardsFundIprpc(c1, mockSpec3, duration, sdk.NewCoins(mock3Fund.Add(minIprpcCost))) require.NoError(t, err) // advance month and epoch to apply pairing and iprpc fund From 38f4d486fe4d93846a44c5b356fa6345c0734a7c Mon Sep 17 00:00:00 2001 From: oren-lava Date: Wed, 6 Mar 2024 13:43:48 +0200 Subject: [PATCH 14/17] CNS-872: iprpc unit tests fix --- x/rewards/keeper/iprpc_test.go | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/x/rewards/keeper/iprpc_test.go b/x/rewards/keeper/iprpc_test.go index 044249f0b2..4a357954b2 100644 --- a/x/rewards/keeper/iprpc_test.go +++ b/x/rewards/keeper/iprpc_test.go @@ -22,7 +22,7 @@ func TestFundIprpcTX(t *testing.T) { ts.setupForIprpcTests(false) consumerAcc, consumer := ts.GetAccount(common.CONSUMER, 0) - err := ts.Keepers.BankKeeper.AddToBalance(consumerAcc.Addr, iprpcFunds) + err := ts.Keepers.BankKeeper.AddToBalance(consumerAcc.Addr, iprpcFunds.MulInt(math.NewInt(12))) require.NoError(ts.T, err) type fundIprpcData struct { @@ -64,11 +64,17 @@ func TestFundIprpcTX(t *testing.T) { require.NoError(t, err) } - // Expected total IPRPC pool balance: 110ulava (=10+90+10) and 330uibc + // Expected total IPRPC pool balance (by TXs): + // 1. 10ulava + // 2. 50uibc + // 3. (90ulava + 30uibc) * 3 = 270ulava + 90uibc + // 4. 130uibc * 3 = 390uibc + // 5. (10ulava + 120uibc) * 12 = 120ulava + 1440uibc + // Total: 400ulava + 1970uibc iprpcTotalBalance := ts.Keepers.Rewards.TotalPoolTokens(ts.Ctx, rewardstypes.IprpcPoolName) expectedIprpcTotalBalance := sdk.NewCoins( - sdk.NewCoin(ts.BondDenom(), math.NewInt(110)), - sdk.NewCoin(ibcDenom, math.NewInt(330)), + sdk.NewCoin(ts.BondDenom(), math.NewInt(400)), + sdk.NewCoin(ibcDenom, math.NewInt(1970)), ) require.True(t, expectedIprpcTotalBalance.IsEqual(iprpcTotalBalance)) @@ -341,7 +347,7 @@ func TestIprpcRewardObjectsUpdate(t *testing.T) { // fund iprpc pool duration := uint64(2) - err := ts.Keepers.BankKeeper.AddToBalance(consumerAcc.Addr, iprpcFunds) + err := ts.Keepers.BankKeeper.AddToBalance(consumerAcc.Addr, iprpcFunds.MulInt(math.NewInt(2))) require.NoError(ts.T, err) _, err = ts.TxRewardsFundIprpc(consumer, mockSpec2, duration, iprpcFunds) require.NoError(ts.T, err) From 85ba6891d7cb69e895c6500e8115b27858e3a3cb Mon Sep 17 00:00:00 2001 From: oren-lava Date: Wed, 6 Mar 2024 14:51:44 +0200 Subject: [PATCH 15/17] CNS-872: PR fix --- testutil/common/tester.go | 4 ++++ x/rewards/keeper/helpers_test.go | 3 +++ x/rewards/keeper/iprpc_test.go | 9 +++++++++ 3 files changed, 16 insertions(+) diff --git a/testutil/common/tester.go b/testutil/common/tester.go index 02fea69170..d3e775421e 100644 --- a/testutil/common/tester.go +++ b/testutil/common/tester.go @@ -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) } diff --git a/x/rewards/keeper/helpers_test.go b/x/rewards/keeper/helpers_test.go index 05aaea3234..6c9be49dfb 100644 --- a/x/rewards/keeper/helpers_test.go +++ b/x/rewards/keeper/helpers_test.go @@ -129,8 +129,11 @@ func (ts *tester) setupForIprpcTests(fundIprpcPool bool) { 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 } } diff --git a/x/rewards/keeper/iprpc_test.go b/x/rewards/keeper/iprpc_test.go index 4a357954b2..0d7c9e78b8 100644 --- a/x/rewards/keeper/iprpc_test.go +++ b/x/rewards/keeper/iprpc_test.go @@ -519,6 +519,15 @@ func TestIprpcEligibleSubscriptions(t *testing.T) { require.NoError(t, err) require.Len(t, res1.SpecFunds, 0) require.Len(t, res2.SpecFunds, 0) + + // advance another month and see there are still no rewards + ts.AdvanceMonths(1).AdvanceEpoch() + res1, err = ts.QueryRewardsIprpcProviderRewardEstimation(p1) + require.NoError(t, err) + res2, err = ts.QueryRewardsIprpcProviderRewardEstimation(p2) + require.NoError(t, err) + require.Len(t, res1.SpecFunds, 0) + require.Len(t, res2.SpecFunds, 0) } // TestMultipleIprpcSpec checks that rewards are distributed correctly when multiple specs are configured in the IPRPC pool From d24e2b9b06ce7e85d048887903e734174a6a55b2 Mon Sep 17 00:00:00 2001 From: oren-lava Date: Wed, 6 Mar 2024 16:58:46 +0200 Subject: [PATCH 16/17] CNS-872: added unit test --- x/rewards/keeper/iprpc_test.go | 51 ++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/x/rewards/keeper/iprpc_test.go b/x/rewards/keeper/iprpc_test.go index 0d7c9e78b8..c4b2f7b3bd 100644 --- a/x/rewards/keeper/iprpc_test.go +++ b/x/rewards/keeper/iprpc_test.go @@ -1,7 +1,6 @@ package keeper_test import ( - "fmt" "testing" "cosmossdk.io/math" @@ -267,8 +266,6 @@ func TestIprpcSpecRewardQuery(t *testing.T) { // query with no args res, err := ts.QueryRewardsIprpcSpecReward("") require.NoError(t, err) - fmt.Printf("expectedResults: %v\n", expectedResults) - fmt.Printf("res.IprpcRewards: %v\n", res.IprpcRewards) require.ElementsMatch(t, expectedResults, res.IprpcRewards) // query with arg = mockspec @@ -405,6 +402,54 @@ func TestIprpcRewardObjectsUpdate(t *testing.T) { require.Equal(t, uint64(3), res.CurrentMonthId) } +// TestFundIprpcTwice tests the following scenario: +// IPRPC is funded for two months, advance month and fund again +// during this month, let provider serve and advance month again -> reward should be as the original funding +// advance again and serve -> reward should be from both funds (funding only starts from the next month) +func TestFundIprpcTwice(t *testing.T) { + ts := newTester(t, true) + ts.setupForIprpcTests(false) + consumerAcc, consumer := ts.GetAccount(common.CONSUMER, 0) + _, p1 := ts.GetAccount(common.PROVIDER, 0) + + // fund iprpc pool + err := ts.Keepers.BankKeeper.AddToBalance(consumerAcc.Addr, iprpcFunds.MulInt(math.NewInt(2))) + require.NoError(ts.T, err) + _, err = ts.TxRewardsFundIprpc(consumer, mockSpec2, 2, iprpcFunds) + require.NoError(ts.T, err) + + // advance month and fund again + ts.AdvanceMonths(1).AdvanceEpoch() + err = ts.Keepers.BankKeeper.AddToBalance(consumerAcc.Addr, iprpcFunds.MulInt(math.NewInt(2))) + require.NoError(ts.T, err) + _, err = ts.TxRewardsFundIprpc(consumer, mockSpec2, 2, iprpcFunds) + require.NoError(ts.T, err) + + // make a provider service an IPRPC eligible consumer and advance month + relay := ts.SendRelay(p1, consumerAcc, []string{ts.specs[1].Index}, 100) + _, err = ts.Servers.PairingServer.RelayPayment(ts.GoCtx, &relay) + ts.AdvanceEpoch() + require.NoError(t, err) + ts.AdvanceMonths(1).AdvanceEpoch() + + // check rewards - should be only from first funding (=iprpcFunds) + res, err := ts.QueryDualstakingDelegatorRewards(p1, p1, mockSpec2) + require.NoError(t, err) + require.True(t, iprpcFunds.Sub(minIprpcCost).IsEqual(res.Rewards[0].Amount)) + + // make a provider service an IPRPC eligible consumer and advance month again + relay = ts.SendRelay(p1, consumerAcc, []string{ts.specs[1].Index}, 100) + _, err = ts.Servers.PairingServer.RelayPayment(ts.GoCtx, &relay) + ts.AdvanceEpoch() + require.NoError(t, err) + ts.AdvanceMonths(1).AdvanceEpoch() + + // check rewards - should be only from first + second funding (=iprpcFunds*3) + res, err = ts.QueryDualstakingDelegatorRewards(p1, p1, mockSpec2) + require.NoError(t, err) + require.True(t, iprpcFunds.Sub(minIprpcCost).MulInt(math.NewInt(3)).IsEqual(res.Rewards[0].Amount)) +} + // TestIprpcMinCost tests that a fund TX fails if it doesn't have enough tokens to cover for the minimum IPRPC costs // Scenarios: // 1. fund TX with the minimum cost available -> TX success From 1a0e230004c9aa514fa8de5ebdd7b2bf5399c5d2 Mon Sep 17 00:00:00 2001 From: oren-lava Date: Wed, 6 Mar 2024 17:06:28 +0200 Subject: [PATCH 17/17] CNS-872: added min cost unit test --- x/rewards/keeper/iprpc_test.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/x/rewards/keeper/iprpc_test.go b/x/rewards/keeper/iprpc_test.go index c4b2f7b3bd..07486ddaf6 100644 --- a/x/rewards/keeper/iprpc_test.go +++ b/x/rewards/keeper/iprpc_test.go @@ -701,3 +701,17 @@ func TestIprpcRewardWithZeroSubRewards(t *testing.T) { require.NoError(t, err) require.True(t, p2ExpectedReward.IsEqual(res2.Rewards[0].Amount)) } + +// TestMinIprpcCostForSeveralMonths checks that if a user sends fund=1.1*minIprpcCost with duration=2 +// the TX succeeds (checks that the min iprpc cost check that per month there is enough funds) +func TestMinIprpcCostForSeveralMonths(t *testing.T) { + ts := newTester(t, true) + ts.setupForIprpcTests(false) + consumerAcc, consumer := ts.GetAccount(common.CONSUMER, 0) + + // fund iprpc pool + err := ts.Keepers.BankKeeper.AddToBalance(consumerAcc.Addr, iprpcFunds.MulInt(math.NewInt(3))) + require.NoError(ts.T, err) + _, err = ts.TxRewardsFundIprpc(consumer, mockSpec2, 2, iprpcFunds.MulInt(math.NewInt(110)).QuoInt(math.NewInt(100))) + require.NoError(ts.T, err) +}