From 63298c12ad3dd0405fecba6b9b339c6ff9b7a735 Mon Sep 17 00:00:00 2001 From: oren-lava Date: Wed, 6 Mar 2024 12:29:18 +0200 Subject: [PATCH 1/8] 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 ef79aefe46c794cc8635ee6e26cb68e7724cbdbd Mon Sep 17 00:00:00 2001 From: oren-lava Date: Wed, 6 Mar 2024 12:33:43 +0200 Subject: [PATCH 2/8] CNS-869: fix min iprpc cost charge bug --- 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 12006885a9..00c790d2b7 100644 --- a/x/rewards/keeper/iprpc.go +++ b/x/rewards/keeper/iprpc.go @@ -41,7 +41,7 @@ func (k Keeper) FundIprpc(ctx sdk.Context, creator string, duration uint64, fund utils.LogAttr("min_iprpc_fund_cost", minIprpcFundCost.String()), ) } - fund = fund.Sub(minIprpcFundCostCoins...) + fund = fund.Sub(minIprpcFundCost) // send the funds to the iprpc pool err = k.bankKeeper.SendCoinsFromAccountToModule(ctx, addr, string(types.IprpcPoolName), fund) From 31124f597c84dd30ad7dd741dc939b332ea02370 Mon Sep 17 00:00:00 2001 From: oren-lava Date: Wed, 6 Mar 2024 12:47:01 +0200 Subject: [PATCH 3/8] 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 b246353ec1cfede2baf29fb3d348c469e56ee936 Mon Sep 17 00:00:00 2001 From: oren-lava Date: Wed, 6 Mar 2024 13:42:57 +0200 Subject: [PATCH 4/8] CNS-869: iprpc pool fund fix --- x/rewards/keeper/iprpc.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/x/rewards/keeper/iprpc.go b/x/rewards/keeper/iprpc.go index 00c790d2b7..5a25ce96ad 100644 --- a/x/rewards/keeper/iprpc.go +++ b/x/rewards/keeper/iprpc.go @@ -4,6 +4,7 @@ import ( "fmt" "strconv" + "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/lavanet/lava/utils" "github.com/lavanet/lava/x/rewards/types" @@ -42,9 +43,10 @@ func (k Keeper) FundIprpc(ctx sdk.Context, creator string, duration uint64, fund ) } fund = fund.Sub(minIprpcFundCost) + allFunds := fund.MulInt(math.NewIntFromUint64(duration)) // send the funds to the iprpc pool - err = k.bankKeeper.SendCoinsFromAccountToModule(ctx, addr, string(types.IprpcPoolName), fund) + err = k.bankKeeper.SendCoinsFromAccountToModule(ctx, addr, string(types.IprpcPoolName), allFunds) if err != nil { return utils.LavaFormatError(types.ErrFundIprpc.Error()+"for funding iprpc pool", err, utils.LogAttr("creator", creator), From 38f4d486fe4d93846a44c5b356fa6345c0734a7c Mon Sep 17 00:00:00 2001 From: oren-lava Date: Wed, 6 Mar 2024 13:43:48 +0200 Subject: [PATCH 5/8] 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 6/8] 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 7/8] 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 8/8] 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) +}