diff --git a/proto/lavanet/lava/subscription/subscription.proto b/proto/lavanet/lava/subscription/subscription.proto index 8f6c09e554..e60780e6c2 100644 --- a/proto/lavanet/lava/subscription/subscription.proto +++ b/proto/lavanet/lava/subscription/subscription.proto @@ -19,4 +19,12 @@ message Subscription { string cluster = 13; // cluster key uint64 duration_total = 14; // continous subscription usage bool auto_renewal = 15; // automatic renewal when the subscription expires + FutureSubscription future_subscription = 16; // future subscription made with buy --advance-purchase } + +message FutureSubscription { + string creator = 1; // creator pays for the future subscription. Will replace the original one once activated + string plan_index = 2; // index (name) of plan + uint64 plan_block = 3; // when the plan was created + uint64 duration_bought = 4; // total requested duration in months +} \ No newline at end of file diff --git a/proto/lavanet/lava/subscription/tx.proto b/proto/lavanet/lava/subscription/tx.proto index aa41513f76..ac10f8fd93 100644 --- a/proto/lavanet/lava/subscription/tx.proto +++ b/proto/lavanet/lava/subscription/tx.proto @@ -22,6 +22,7 @@ message MsgBuy { string index = 3; uint64 duration = 4; // in months bool auto_renewal = 6; + bool advance_purchase = 7; } message MsgBuyResponse { diff --git a/scripts/init_chain_commands.sh b/scripts/init_chain_commands.sh index 5479c7d42d..a8b910b634 100755 --- a/scripts/init_chain_commands.sh +++ b/scripts/init_chain_commands.sh @@ -22,8 +22,20 @@ echo; echo "#### Waiting 4 blocks ####" wait_count_blocks 4 sleep 4 +echo; echo "#### Sending proposal for test plans add ####" +lavad tx gov submit-legacy-proposal plans-add ./cookbook/plans/test_plans/default.json,./cookbook/plans/test_plans/temporary-add.json -y --from alice --gas-adjustment "1.5" --gas "auto" --gas-prices $GASPRICE + +echo; echo "#### Waiting 2 blocks ####" +wait_count_blocks 2 + +echo; echo "#### Voting on plans test add proposal ####" +lavad tx gov vote $(latest_vote) yes -y --from alice --gas-adjustment "1.5" --gas "auto" --gas-prices $GASPRICE + +echo; echo "#### Waiting 4 blocks ####" +wait_count_blocks 2 + echo; echo "#### Sending proposal for plans add ####" -lavad tx gov submit-legacy-proposal plans-add ./cookbook/plans/whale.json,./cookbook/plans/explorer.json,./cookbook/plans/test_plans/default.json,./cookbook/plans/test_plans/temporary-add.json -y --from alice --gas-adjustment "1.5" --gas "auto" --gas-prices $GASPRICE +lavad tx gov submit-legacy-proposal plans-add ./cookbook/plans/explorer.json,./cookbook/plans/adventurer.json,./cookbook/plans/whale.json -y --from alice --gas-adjustment "1.5" --gas "auto" --gas-prices $GASPRICE echo; echo "#### Waiting 2 blocks ####" wait_count_blocks 2 diff --git a/testutil/common/tester.go b/testutil/common/tester.go index 2c2205e5cb..e21e1372e0 100644 --- a/testutil/common/tester.go +++ b/testutil/common/tester.go @@ -450,13 +450,14 @@ func (ts *Tester) TxDualstakingClaimRewards( } // TxSubscriptionBuy: implement 'tx subscription buy' -func (ts *Tester) TxSubscriptionBuy(creator, consumer, plan string, months int, autoRenewal bool) (*subscriptiontypes.MsgBuyResponse, error) { +func (ts *Tester) TxSubscriptionBuy(creator, consumer, plan string, months int, autoRenewal, advancePurchase bool) (*subscriptiontypes.MsgBuyResponse, error) { msg := &subscriptiontypes.MsgBuy{ - Creator: creator, - Consumer: consumer, - Index: plan, - Duration: uint64(months), - AutoRenewal: autoRenewal, + Creator: creator, + Consumer: consumer, + Index: plan, + Duration: uint64(months), + AutoRenewal: autoRenewal, + AdvancePurchase: advancePurchase, } return ts.Servers.SubscriptionServer.Buy(ts.GoCtx, msg) } diff --git a/x/conflict/keeper/msg_server_detection_test.go b/x/conflict/keeper/msg_server_detection_test.go index 10a1e8ef12..d9a4886886 100644 --- a/x/conflict/keeper/msg_server_detection_test.go +++ b/x/conflict/keeper/msg_server_detection_test.go @@ -50,7 +50,7 @@ func (ts *tester) setupForConflict(providersCount int) *tester { ts.spec = ts.Spec("mock") consumer, consumerAddr := ts.AddAccount("consumer", 0, balance) - _, err := ts.TxSubscriptionBuy(consumerAddr, consumerAddr, ts.plan.Index, 1, false) + _, err := ts.TxSubscriptionBuy(consumerAddr, consumerAddr, ts.plan.Index, 1, false, false) require.Nil(ts.T, err) ts.consumer = consumer diff --git a/x/pairing/keeper/cu_tracker_test.go b/x/pairing/keeper/cu_tracker_test.go index c6f71a12b9..04b18e1176 100644 --- a/x/pairing/keeper/cu_tracker_test.go +++ b/x/pairing/keeper/cu_tracker_test.go @@ -26,7 +26,7 @@ func TestAddingTrackedCuWithoutPay(t *testing.T) { _, provider1Addr := ts.GetAccount(common.PROVIDER, 0) _, provider2Addr := ts.GetAccount(common.PROVIDER, 1) - ts.TxSubscriptionBuy(client1Addr, client1Addr, "free", 1, false) // extend by a month so the sub won't expire + ts.TxSubscriptionBuy(client1Addr, client1Addr, "free", 1, false, false) // extend by a month so the sub won't expire res, err := ts.QuerySubscriptionCurrent(client1Addr) require.Nil(t, err) @@ -115,7 +115,7 @@ func TestTrackedCuWithExpiredSubscription(t *testing.T) { ts.AddPlan(ts.plan.Index, ts.plan) clientAcct, clientAddr := ts.AddAccount(common.CONSUMER, 0, testBalance) - _, err := ts.TxSubscriptionBuy(clientAddr, clientAddr, ts.plan.Index, 1, false) + _, err := ts.TxSubscriptionBuy(clientAddr, clientAddr, ts.plan.Index, 1, false, false) require.Nil(t, err) err = ts.addProvider(1) @@ -178,7 +178,7 @@ func TestTrackedCuWithQos(t *testing.T) { provider1Acc, provider1 := ts.GetAccount(common.PROVIDER, 0) provider2Acc, provider2 := ts.GetAccount(common.PROVIDER, 1) - ts.TxSubscriptionBuy(client, client, "free", 1, false) // extend by a month so the sub won't expire + ts.TxSubscriptionBuy(client, client, "free", 1, false, false) // extend by a month so the sub won't expire badQoS := &types.QualityOfServiceReport{ Latency: sdk.ZeroDec(), @@ -381,7 +381,7 @@ func TestProviderMonthlyPayoutQuery(t *testing.T) { clientAcc, client := ts.GetAccount(common.CONSUMER, 0) providerAcct, provider := ts.GetAccount(common.PROVIDER, 0) - ts.TxSubscriptionBuy(client, client, "free", 1, false) // extend by a month so the sub won't expire + ts.TxSubscriptionBuy(client, client, "free", 1, false, false) // extend by a month so the sub won't expire // stake the provider on an additional chain and apply pairing (advance epoch) spec1 := ts.spec @@ -657,7 +657,7 @@ func TestTrackedCuDeletion(t *testing.T) { clientAcc, client := ts.GetAccount(common.CONSUMER, 0) _, provider := ts.GetAccount(common.PROVIDER, 0) - ts.TxSubscriptionBuy(client, client, "free", 1, false) // extend by a month so the sub won't expire + ts.TxSubscriptionBuy(client, client, "free", 1, false, false) // extend by a month so the sub won't expire // send relay to track CU relayPayment := sendRelay(ts, provider, clientAcc, []string{ts.spec.Index}) diff --git a/x/pairing/keeper/delegator_rewards_test.go b/x/pairing/keeper/delegator_rewards_test.go index afbbd77eb6..46dce551db 100644 --- a/x/pairing/keeper/delegator_rewards_test.go +++ b/x/pairing/keeper/delegator_rewards_test.go @@ -72,7 +72,7 @@ func TestProviderDelegatorsRewards(t *testing.T) { _, delegator1 := ts.GetAccount(common.CONSUMER, 1) _, delegator2 := ts.GetAccount(common.CONSUMER, 2) - ts.TxSubscriptionBuy(client, client, "free", 1, false) // extend by a month so the sub won't expire + ts.TxSubscriptionBuy(client, client, "free", 1, false, false) // extend by a month so the sub won't expire ts.AdvanceEpoch() // to apply pairing @@ -203,7 +203,7 @@ func TestDelegationLimitAffectingProviderReward(t *testing.T) { ts.AdvanceEpoch() // to apply pairing - ts.TxSubscriptionBuy(client, client, "free", 1, false) // extend by a month so the sub won't expire + ts.TxSubscriptionBuy(client, client, "free", 1, false, false) // extend by a month so the sub won't expire delegationAmount1 := sdk.NewCoin(ts.TokenDenom(), sdk.NewIntFromUint64(uint64(testStake)/2)) delegationAmount2 := sdk.NewCoin(ts.TokenDenom(), sdk.NewIntFromUint64(uint64(testStake))) @@ -251,7 +251,7 @@ func TestProviderRewardWithCommission(t *testing.T) { clientAcc, client := ts.GetAccount(common.CONSUMER, 0) delegator1Acc, delegator1 := ts.GetAccount(common.CONSUMER, 1) - ts.TxSubscriptionBuy(client, client, "free", 1, false) // extend by a month so the sub won't expire + ts.TxSubscriptionBuy(client, client, "free", 1, false, false) // extend by a month so the sub won't expire ts.AdvanceEpoch() // to apply pairing @@ -349,7 +349,7 @@ func TestQueryDelegatorRewards(t *testing.T) { _, delegator1 := ts.GetAccount(common.CONSUMER, 1) _, delegator2 := ts.GetAccount(common.CONSUMER, 2) // delegates to no one - ts.TxSubscriptionBuy(client, client, "free", 1, false) // extend by a month so the sub won't expire + ts.TxSubscriptionBuy(client, client, "free", 1, false, false) // extend by a month so the sub won't expire spec1 := common.CreateMockSpec() spec1.Index = "mock1" diff --git a/x/pairing/keeper/helpers_test.go b/x/pairing/keeper/helpers_test.go index 1c93b2eb96..95963475ba 100644 --- a/x/pairing/keeper/helpers_test.go +++ b/x/pairing/keeper/helpers_test.go @@ -56,7 +56,7 @@ func (ts *tester) addClient(count int) { start := len(ts.Accounts(common.CONSUMER)) for i := 0; i < count; i++ { _, addr := ts.AddAccount(common.CONSUMER, start+i, testBalance) - _, err := ts.TxSubscriptionBuy(addr, addr, ts.plan.Index, 1, false) + _, err := ts.TxSubscriptionBuy(addr, addr, ts.plan.Index, 1, false, false) if err != nil { panic("addClient: failed to buy subscription: " + err.Error()) } diff --git a/x/pairing/keeper/msg_server_relay_payment_gov_test.go b/x/pairing/keeper/msg_server_relay_payment_gov_test.go index e1a0fc5d00..bd453ce570 100644 --- a/x/pairing/keeper/msg_server_relay_payment_gov_test.go +++ b/x/pairing/keeper/msg_server_relay_payment_gov_test.go @@ -259,7 +259,7 @@ func TestRelayPaymentGovEpochToSaveDecrease(t *testing.T) { client1Acct, client := ts.GetAccount(common.CONSUMER, 0) providerAcct, providerAddr := ts.GetAccount(common.PROVIDER, 0) - ts.TxSubscriptionBuy(client, client, "free", 1, false) // extend by a month so the sub won't expire + ts.TxSubscriptionBuy(client, client, "free", 1, false, false) // extend by a month so the sub won't expire epochBlocks := ts.EpochBlocks() epochsToSave := ts.EpochsToSave() diff --git a/x/pairing/keeper/msg_server_relay_payment_test.go b/x/pairing/keeper/msg_server_relay_payment_test.go index cf1c53cee7..9754e6a35a 100644 --- a/x/pairing/keeper/msg_server_relay_payment_test.go +++ b/x/pairing/keeper/msg_server_relay_payment_test.go @@ -746,7 +746,7 @@ func TestBadgeUsedCuMapTimeout(t *testing.T) { client1Acct, client1Addr := ts.GetAccount(common.CONSUMER, 0) providerAcct, providerAddr := ts.GetAccount(common.PROVIDER, 0) - ts.TxSubscriptionBuy(client1Addr, client1Addr, "free", 1, false) // extend by a month so the sub won't expire + ts.TxSubscriptionBuy(client1Addr, client1Addr, "free", 1, false, false) // extend by a month so the sub won't expire badgeAcct, _ := ts.AddAccount("badge", 0, testBalance) diff --git a/x/pairing/keeper/pairing_subscription_test.go b/x/pairing/keeper/pairing_subscription_test.go index aca11b5dec..29394870e2 100644 --- a/x/pairing/keeper/pairing_subscription_test.go +++ b/x/pairing/keeper/pairing_subscription_test.go @@ -444,7 +444,7 @@ func TestPairingNotChangingDueToCuOveruse(t *testing.T) { client1Acct, client1Addr := ts.GetAccount(common.CONSUMER, 0) // add 10 months to the subscription - _, err := ts.TxSubscriptionBuy(client1Addr, client1Addr, ts.plan.Index, 10, false) + _, err := ts.TxSubscriptionBuy(client1Addr, client1Addr, ts.plan.Index, 10, false, false) require.Nil(t, err) totalCuLimit := ts.plan.PlanPolicy.TotalCuLimit diff --git a/x/pairing/keeper/pairing_test.go b/x/pairing/keeper/pairing_test.go index 331661ead7..44bbf64363 100644 --- a/x/pairing/keeper/pairing_test.go +++ b/x/pairing/keeper/pairing_test.go @@ -29,9 +29,9 @@ func TestPairingUniqueness(t *testing.T) { _, sub1Addr := ts.Account("sub1") _, sub2Addr := ts.Account("sub2") - _, err := ts.TxSubscriptionBuy(sub1Addr, sub1Addr, ts.plan.Index, 1, false) + _, err := ts.TxSubscriptionBuy(sub1Addr, sub1Addr, ts.plan.Index, 1, false, false) require.Nil(t, err) - _, err = ts.TxSubscriptionBuy(sub2Addr, sub2Addr, ts.plan.Index, 1, false) + _, err = ts.TxSubscriptionBuy(sub2Addr, sub2Addr, ts.plan.Index, 1, false, false) require.Nil(t, err) for i := 1; i <= 1000; i++ { @@ -97,7 +97,7 @@ func TestValidatePairingDeterminism(t *testing.T) { _, sub1Addr := ts.Account("sub1") - _, err := ts.TxSubscriptionBuy(sub1Addr, sub1Addr, ts.plan.Index, 1, false) + _, err := ts.TxSubscriptionBuy(sub1Addr, sub1Addr, ts.plan.Index, 1, false, false) require.Nil(t, err) for i := 1; i <= 10; i++ { @@ -247,7 +247,7 @@ func TestPairingStatic(t *testing.T) { ts.AdvanceEpoch() - _, err := ts.TxSubscriptionBuy(sub1Addr, sub1Addr, ts.plan.Index, 1, false) + _, err := ts.TxSubscriptionBuy(sub1Addr, sub1Addr, ts.plan.Index, 1, false, false) require.Nil(t, err) for i := 0; i < int(ts.plan.PlanPolicy.MaxProvidersToPair)*2; i++ { @@ -519,7 +519,7 @@ func TestAddonPairing(t *testing.T) { _, sub1Addr := ts.AddAccount("sub", 0, 10000) - _, err = ts.TxSubscriptionBuy(sub1Addr, sub1Addr, plan.Index, 1, false) + _, err = ts.TxSubscriptionBuy(sub1Addr, sub1Addr, plan.Index, 1, false, false) require.Nil(t, err) // get the admin project and set its policies @@ -710,7 +710,7 @@ func TestSelectedProvidersPairing(t *testing.T) { ts.AdvanceEpoch() - _, err = ts.TxSubscriptionBuy(sub1Addr, sub1Addr, plan.Index, 1, false) + _, err = ts.TxSubscriptionBuy(sub1Addr, sub1Addr, plan.Index, 1, false, false) require.Nil(t, err) // get the admin project and set its policies @@ -873,7 +873,7 @@ func TestPairingUniformDistribution(t *testing.T) { // make the subscription auto-renew so it won't expire after many (pairing) epochs err := ts.TxSubscriptionAutoRenewal(clientAddr, true) - require.Nil(t, err) + require.NoError(t, err) weightFunc := func(p epochstoragetypes.StakeEntry) int64 { return p.Stake.Amount.Int64() } ts.verifyPairingDistribution("uniform distribution", clientAddr, providersToPair, weightFunc) @@ -1007,9 +1007,9 @@ func TestGeolocationPairingScores(t *testing.T) { basicAcct, basicAddr := ts.GetAccount(common.CONSUMER, 1) premiumAcct, premiumAddr := ts.GetAccount(common.CONSUMER, 2) - ts.TxSubscriptionBuy(freeAddr, freeAddr, freePlan.Index, 1, false) - ts.TxSubscriptionBuy(basicAddr, basicAddr, basicPlan.Index, 1, false) - ts.TxSubscriptionBuy(premiumAddr, premiumAddr, premiumPlan.Index, 1, false) + ts.TxSubscriptionBuy(freeAddr, freeAddr, freePlan.Index, 1, false, false) + ts.TxSubscriptionBuy(basicAddr, basicAddr, basicPlan.Index, 1, false, false) + ts.TxSubscriptionBuy(premiumAddr, premiumAddr, premiumPlan.Index, 1, false, false) for geoName, geo := range planstypes.Geolocation_value { if geoName != "GL" && geoName != "GLS" { @@ -1199,7 +1199,7 @@ func TestDuplicateProviders(t *testing.T) { require.Nil(t, err) ts.AdvanceEpoch() - ts.TxSubscriptionBuy(basicAddr, basicAddr, basicPlan.Index, 1, false) + ts.TxSubscriptionBuy(basicAddr, basicAddr, basicPlan.Index, 1, false, false) for geoName, geo := range planstypes.Geolocation_value { if geoName != "GL" && geoName != "GLS" { @@ -1247,7 +1247,7 @@ func TestNoRequiredGeo(t *testing.T) { require.Nil(t, err) ts.AdvanceEpoch() - ts.TxSubscriptionBuy(freeAddr, freeAddr, freePlan.Index, 1, false) + ts.TxSubscriptionBuy(freeAddr, freeAddr, freePlan.Index, 1, false, false) // add 5 more providers that are not in US-E (the only allowed providers in the free plan) err = ts.addProviderGeolocation(5, planstypes.Geolocation_value["AS"]) @@ -1929,7 +1929,7 @@ func TestExtensionAndAddonPairing(t *testing.T) { _, sub1Addr := ts.AddAccount("sub", 0, 10000) - _, err = ts.TxSubscriptionBuy(sub1Addr, sub1Addr, plan.Index, 1, false) + _, err = ts.TxSubscriptionBuy(sub1Addr, sub1Addr, plan.Index, 1, false, false) require.Nil(t, err) // get the admin project and set its policies @@ -2094,7 +2094,7 @@ func TestMixSelectedProvidersAndArchivePairing(t *testing.T) { _, sub1Addr := ts.AddAccount("sub", 0, 10000) - _, err = ts.TxSubscriptionBuy(sub1Addr, sub1Addr, plan.Index, 1, false) + _, err = ts.TxSubscriptionBuy(sub1Addr, sub1Addr, plan.Index, 1, false, false) require.Nil(t, err) // get the admin project and set its policies @@ -2222,7 +2222,7 @@ func TestPairingPerformance(t *testing.T) { _, sub1Addr := ts.Account("sub1") - _, err := ts.TxSubscriptionBuy(sub1Addr, sub1Addr, ts.plan.Index, 1, false) + _, err := ts.TxSubscriptionBuy(sub1Addr, sub1Addr, ts.plan.Index, 1, false, false) require.Nil(t, err) for i := 1; i <= 1000; i++ { diff --git a/x/plans/keeper/plan.go b/x/plans/keeper/plan.go index 39aeeb101e..6a141e27af 100644 --- a/x/plans/keeper/plan.go +++ b/x/plans/keeper/plan.go @@ -20,8 +20,8 @@ func (k Keeper) AddPlan(ctx sdk.Context, planToAdd types.Plan, modify bool) erro var planFromStore types.Plan block, _, _, found := k.plansFS.FindEntryDetailed(ctx, planToAdd.GetIndex(), planToAdd.Block, &planFromStore) if found { - if planFromStore.Price.Amount.LT(planToAdd.Price.Amount) { - return utils.LavaFormatError("failed modifying plan in planFS", fmt.Errorf("plan price cannot be increased"), + if !planFromStore.Price.Amount.Equal(planToAdd.Price.Amount) { + return utils.LavaFormatError("failed modifying plan in planFS", fmt.Errorf("plan price cannot be modified"), utils.Attribute{Key: "planToAdd", Value: planToAdd}, utils.Attribute{Key: "originalPlan", Value: planFromStore}, ) diff --git a/x/plans/keeper/plan_test.go b/x/plans/keeper/plan_test.go index 3c69e7dd2e..c098ecc834 100644 --- a/x/plans/keeper/plan_test.go +++ b/x/plans/keeper/plan_test.go @@ -336,7 +336,7 @@ func TestAddAndDelete(t *testing.T) { // TestModifyPlan checks that plan modification acts as expected: // 1. there should be no new version in the planFS (latest plan should have the same block) -// 2. plan price cannot be increased +// 2. plan price cannot be changed func TestModifyPlan(t *testing.T) { ts := newTester(t) ts.AdvanceEpoch() @@ -369,4 +369,9 @@ func TestModifyPlan(t *testing.T) { originalPlan.Price = originalPlan.Price.AddAmount(math.NewIntFromUint64(1)) err = testkeeper.SimulatePlansAddProposal(ts.Ctx, ts.Keepers.Plans, []types.Plan{originalPlan}, true) require.NotNil(t, err) + + // modify the plan by decreasing its price. proposal should fail + originalPlan.Price = originalPlan.Price.SubAmount(math.NewIntFromUint64(2)) + err = testkeeper.SimulatePlansAddProposal(ts.Ctx, ts.Keepers.Plans, []types.Plan{originalPlan}, true) + require.NotNil(t, err) } diff --git a/x/projects/keeper/project_test.go b/x/projects/keeper/project_test.go index 19da21f1d1..2d491b6141 100644 --- a/x/projects/keeper/project_test.go +++ b/x/projects/keeper/project_test.go @@ -200,7 +200,7 @@ func TestProjectsServerAPI(t *testing.T) { err := ts.TxProposalAddPlans(plan) require.Nil(t, err) - _, err = ts.TxSubscriptionBuy(sub1Addr, sub1Addr, plan.Index, 1, false) + _, err = ts.TxSubscriptionBuy(sub1Addr, sub1Addr, plan.Index, 1, false, false) require.Nil(t, err) projectData := types.ProjectData{ @@ -1050,7 +1050,7 @@ func TestSetPolicySelectedProviders(t *testing.T) { require.NotNil(t, err) } - _, err = ts.TxSubscriptionBuy(sub1Addr, sub1Addr, plan.Index, 1, false) + _, err = ts.TxSubscriptionBuy(sub1Addr, sub1Addr, plan.Index, 1, false, false) require.Nil(t, err) res, err := ts.QuerySubscriptionListProjects(sub1Addr) @@ -1224,7 +1224,7 @@ func TestPendingProject(t *testing.T) { _, sub := ts.Account("sub1") - _, err := ts.TxSubscriptionBuy(sub, sub, "free", 1, false) + _, err := ts.TxSubscriptionBuy(sub, sub, "free", 1, false, false) require.Nil(t, err) res, err := ts.QuerySubscriptionListProjects(sub) diff --git a/x/rewards/keeper/providers_test.go b/x/rewards/keeper/providers_test.go index c834a4837e..990d6b1868 100644 --- a/x/rewards/keeper/providers_test.go +++ b/x/rewards/keeper/providers_test.go @@ -24,7 +24,7 @@ func TestZeroProvidersRewards(t *testing.T) { ts.AdvanceEpoch() consumerAcc, _ := ts.AddAccount(common.CONSUMER, 1, ts.plan.Price.Amount.Int64()) - _, err = ts.TxSubscriptionBuy(consumerAcc.Addr.String(), consumerAcc.Addr.String(), ts.plan.Index, 1, false) + _, err = ts.TxSubscriptionBuy(consumerAcc.Addr.String(), consumerAcc.Addr.String(), ts.plan.Index, 1, false, false) require.Nil(t, err) // first months there are no bonus rewards, just payment from the subscription @@ -57,7 +57,7 @@ func TestBasicBoostProvidersRewards(t *testing.T) { ts.AdvanceEpoch() consumerAcc, _ := ts.AddAccount(common.CONSUMER, 1, ts.plan.Price.Amount.Int64()) - _, err = ts.TxSubscriptionBuy(consumerAcc.Addr.String(), consumerAcc.Addr.String(), ts.plan.Index, 1, false) + _, err = ts.TxSubscriptionBuy(consumerAcc.Addr.String(), consumerAcc.Addr.String(), ts.plan.Index, 1, false, false) require.Nil(t, err) baserewards := uint64(100) @@ -101,7 +101,7 @@ func TestSpecAllocationProvidersRewards(t *testing.T) { ts.AdvanceEpoch() consumerAcc, _ := ts.AddAccount(common.CONSUMER, 1, ts.plan.Price.Amount.Int64()) - _, err = ts.TxSubscriptionBuy(consumerAcc.Addr.String(), consumerAcc.Addr.String(), ts.plan.Index, 1, false) + _, err = ts.TxSubscriptionBuy(consumerAcc.Addr.String(), consumerAcc.Addr.String(), ts.plan.Index, 1, false, false) require.Nil(t, err) msg := ts.SendRelay(providerAcc.Addr.String(), consumerAcc, []string{ts.spec.Index}, ts.plan.Price.Amount.Uint64()) @@ -145,7 +145,7 @@ func TestProvidersDiminishingRewards(t *testing.T) { for i := 0; i < 7; i++ { consumerAcc, _ := ts.AddAccount(common.CONSUMER, 1, ts.plan.Price.Amount.Int64()) - _, err = ts.TxSubscriptionBuy(consumerAcc.Addr.String(), consumerAcc.Addr.String(), ts.plan.Index, 1, false) + _, err = ts.TxSubscriptionBuy(consumerAcc.Addr.String(), consumerAcc.Addr.String(), ts.plan.Index, 1, false, false) require.Nil(t, err) msg := ts.SendRelay(providerAcc.Addr.String(), consumerAcc, []string{ts.spec.Index}, ts.plan.Price.Amount.Uint64()) @@ -193,7 +193,7 @@ func TestProvidersEndRewards(t *testing.T) { for i := 0; i < 50; i++ { consumerAcc, _ := ts.AddAccount(common.CONSUMER, 1, ts.plan.Price.Amount.Int64()) - _, err = ts.TxSubscriptionBuy(consumerAcc.Addr.String(), consumerAcc.Addr.String(), ts.plan.Index, 1, false) + _, err = ts.TxSubscriptionBuy(consumerAcc.Addr.String(), consumerAcc.Addr.String(), ts.plan.Index, 1, false, false) require.Nil(t, err) msg := ts.SendRelay(providerAcc.Addr.String(), consumerAcc, []string{ts.spec.Index}, ts.plan.Price.Amount.Uint64()) @@ -245,7 +245,7 @@ func Test2SpecsZeroShares(t *testing.T) { ts.AdvanceEpoch() consumerAcc, _ := ts.AddAccount(common.CONSUMER, 1, ts.plan.Price.Amount.Int64()) - _, err = ts.TxSubscriptionBuy(consumerAcc.Addr.String(), consumerAcc.Addr.String(), ts.plan.Index, 1, false) + _, err = ts.TxSubscriptionBuy(consumerAcc.Addr.String(), consumerAcc.Addr.String(), ts.plan.Index, 1, false, false) require.Nil(t, err) msg := ts.SendRelay(providerAcc.Addr.String(), consumerAcc, []string{ts.spec.Index}, ts.plan.Price.Amount.Uint64()) @@ -253,7 +253,7 @@ func Test2SpecsZeroShares(t *testing.T) { require.Nil(t, err) consumerAcc2, _ := ts.AddAccount(common.CONSUMER, 1, ts.plan.Price.Amount.Int64()) - _, err = ts.TxSubscriptionBuy(consumerAcc2.Addr.String(), consumerAcc2.Addr.String(), ts.plan.Index, 1, false) + _, err = ts.TxSubscriptionBuy(consumerAcc2.Addr.String(), consumerAcc2.Addr.String(), ts.plan.Index, 1, false, false) require.Nil(t, err) msg = ts.SendRelay(providerAcc.Addr.String(), consumerAcc2, []string{spec2.Index}, ts.plan.Price.Amount.Uint64()) @@ -308,7 +308,7 @@ func Test2SpecsDoubleShares(t *testing.T) { ts.AdvanceEpoch() consumerAcc, _ := ts.AddAccount(common.CONSUMER, 1, ts.plan.Price.Amount.Int64()) - _, err = ts.TxSubscriptionBuy(consumerAcc.Addr.String(), consumerAcc.Addr.String(), ts.plan.Index, 1, false) + _, err = ts.TxSubscriptionBuy(consumerAcc.Addr.String(), consumerAcc.Addr.String(), ts.plan.Index, 1, false, false) require.Nil(t, err) msg := ts.SendRelay(providerAcc.Addr.String(), consumerAcc, []string{ts.spec.Index}, ts.plan.Price.Amount.Uint64()) @@ -316,7 +316,7 @@ func Test2SpecsDoubleShares(t *testing.T) { require.Nil(t, err) consumerAcc2, _ := ts.AddAccount(common.CONSUMER, 1, ts.plan.Price.Amount.Int64()) - _, err = ts.TxSubscriptionBuy(consumerAcc2.Addr.String(), consumerAcc2.Addr.String(), ts.plan.Index, 1, false) + _, err = ts.TxSubscriptionBuy(consumerAcc2.Addr.String(), consumerAcc2.Addr.String(), ts.plan.Index, 1, false, false) require.Nil(t, err) msg = ts.SendRelay(providerAcc.Addr.String(), consumerAcc2, []string{spec2.Index}, ts.plan.Price.Amount.Uint64()) @@ -368,7 +368,7 @@ func TestBonusRewards3Providers(t *testing.T) { ts.AdvanceEpoch() consumerAcc, _ := ts.AddAccount(common.CONSUMER, 1, ts.plan.Price.Amount.Int64()) - _, err = ts.TxSubscriptionBuy(consumerAcc.Addr.String(), consumerAcc.Addr.String(), ts.plan.Index, 1, false) + _, err = ts.TxSubscriptionBuy(consumerAcc.Addr.String(), consumerAcc.Addr.String(), ts.plan.Index, 1, false, false) require.Nil(t, err) msg := ts.SendRelay(providerAcc1.Addr.String(), consumerAcc, []string{ts.spec.Index}, ts.plan.Price.Amount.Uint64()/2) @@ -463,7 +463,7 @@ func TestValidatorsAndCommunityParticipation(t *testing.T) { ts.AdvanceEpoch() consumerAcc, _ := ts.AddAccount(common.CONSUMER, 1, ts.plan.Price.Amount.Int64()) - _, err = ts.TxSubscriptionBuy(consumerAcc.Addr.String(), consumerAcc.Addr.String(), ts.plan.Index, 1, false) + _, err = ts.TxSubscriptionBuy(consumerAcc.Addr.String(), consumerAcc.Addr.String(), ts.plan.Index, 1, false, false) require.Nil(t, err) baserewards := uint64(100) @@ -507,7 +507,7 @@ func TestBonusReward49months(t *testing.T) { ts.AdvanceEpoch() consumerAcc, _ := ts.AddAccount(common.CONSUMER, 1, ts.plan.Price.Amount.Int64()*100) - _, err = ts.TxSubscriptionBuy(consumerAcc.Addr.String(), consumerAcc.Addr.String(), ts.plan.Index, 1, true) + _, err = ts.TxSubscriptionBuy(consumerAcc.Addr.String(), consumerAcc.Addr.String(), ts.plan.Index, 1, true, false) require.Nil(t, err) for i := 0; i < 50; i++ { @@ -555,7 +555,7 @@ func TestBonusRewardsEquall5Providers(t *testing.T) { require.Nil(t, err) consumerAcc, _ := ts.AddAccount(common.CONSUMER, 1, ts.plan.Price.Amount.Int64()) - _, err = ts.TxSubscriptionBuy(consumerAcc.Addr.String(), consumerAcc.Addr.String(), ts.plan.Index, 1, false) + _, err = ts.TxSubscriptionBuy(consumerAcc.Addr.String(), consumerAcc.Addr.String(), ts.plan.Index, 1, false, false) consAccs = append(consAccs, consumerAcc) require.Nil(t, err) } @@ -617,7 +617,7 @@ func TestBonusRewards5Providers(t *testing.T) { require.Nil(t, err) consumerAcc, _ := ts.AddAccount(common.CONSUMER, 1, ts.plan.Price.Amount.Int64()) - _, err = ts.TxSubscriptionBuy(consumerAcc.Addr.String(), consumerAcc.Addr.String(), ts.plan.Index, 1, false) + _, err = ts.TxSubscriptionBuy(consumerAcc.Addr.String(), consumerAcc.Addr.String(), ts.plan.Index, 1, false, false) consAccs = append(consAccs, consumerAcc) require.Nil(t, err) } @@ -703,7 +703,7 @@ func TestCommunityTaxOne(t *testing.T) { ts.AdvanceEpoch() consumerAcc, _ := ts.AddAccount(common.CONSUMER, 1, ts.plan.Price.Amount.Int64()) - _, err = ts.TxSubscriptionBuy(consumerAcc.Addr.String(), consumerAcc.Addr.String(), ts.plan.Index, 1, false) + _, err = ts.TxSubscriptionBuy(consumerAcc.Addr.String(), consumerAcc.Addr.String(), ts.plan.Index, 1, false, false) require.Nil(t, err) baserewards := uint64(100) diff --git a/x/subscription/client/cli/tx_buy.go b/x/subscription/client/cli/tx_buy.go index 33e2f30127..563999398d 100644 --- a/x/subscription/client/cli/tx_buy.go +++ b/x/subscription/client/cli/tx_buy.go @@ -12,7 +12,8 @@ import ( ) const ( - EnableAutoRenewal = "enable-auto-renewal" + EnableAutoRenewalFlag = "enable-auto-renewal" + AdvancedPurchaseFlag = "advance-purchase" ) func CmdBuy() *cobra.Command { @@ -25,7 +26,8 @@ The duration is stated in number of months (default: 1). If the plan index is different than the consumer's current plan, it will upgrade to that plan index.`, Example: `required flags: --from , optional flags: --enable-auto-renewal lavad tx subscription buy [plan-index] --from - lavad tx subscription buy [plan-index] --from 12`, + lavad tx subscription buy [plan-index] --from 12 + lavad tx subscription buy [plan-index] --from 12 --advanced-purchase`, Args: cobra.RangeArgs(1, 3), RunE: func(cmd *cobra.Command, args []string) (err error) { clientCtx, err := client.GetClientTxContext(cmd) @@ -47,18 +49,26 @@ If the plan index is different than the consumer's current plan, it will upgrade } // check if the command includes --enable-auto-renewal - enableAutoRenewalFlag := cmd.Flags().Lookup(EnableAutoRenewal) + enableAutoRenewalFlag := cmd.Flags().Lookup(EnableAutoRenewalFlag) if enableAutoRenewalFlag == nil { - return fmt.Errorf("%s flag wasn't found", EnableAutoRenewal) + return fmt.Errorf("%s flag wasn't found", EnableAutoRenewalFlag) } autoRenewal := enableAutoRenewalFlag.Changed + // check if the command includes --enable-auto-renewal + advancedPurchasedFlag := cmd.Flags().Lookup(AdvancedPurchaseFlag) + if advancedPurchasedFlag == nil { + return fmt.Errorf("%s flag wasn't found", AdvancedPurchaseFlag) + } + advancedPurchase := advancedPurchasedFlag.Changed + msg := types.NewMsgBuy( creator, argConsumer, argIndex, argDuration, autoRenewal, + advancedPurchase, ) if err := msg.ValidateBasic(); err != nil { return err @@ -68,7 +78,8 @@ If the plan index is different than the consumer's current plan, it will upgrade } flags.AddTxFlagsToCmd(cmd) - cmd.Flags().Bool(EnableAutoRenewal, false, "enables auto-renewal upon expiration") + cmd.Flags().Bool(EnableAutoRenewalFlag, false, "enables auto-renewal upon expiration") + cmd.Flags().Bool(AdvancedPurchaseFlag, false, "make an advanced purchase that will be activated once the current subscription ends") return cmd } diff --git a/x/subscription/keeper/cluster_test.go b/x/subscription/keeper/cluster_test.go index 1ed607b658..b617ee434e 100644 --- a/x/subscription/keeper/cluster_test.go +++ b/x/subscription/keeper/cluster_test.go @@ -39,7 +39,7 @@ func TestGetCluster(t *testing.T) { for _, tt := range template { t.Run(tt.name, func(t *testing.T) { - _, err := ts.TxSubscriptionBuy(tt.sub, tt.sub, tt.plan, 12, false) + _, err := ts.TxSubscriptionBuy(tt.sub, tt.sub, tt.plan, 12, false, false) require.Nil(t, err) prevCluster := "" diff --git a/x/subscription/keeper/msg_server_buy.go b/x/subscription/keeper/msg_server_buy.go index a429304355..a7e982fd34 100644 --- a/x/subscription/keeper/msg_server_buy.go +++ b/x/subscription/keeper/msg_server_buy.go @@ -11,16 +11,31 @@ import ( func (k msgServer) Buy(goCtx context.Context, msg *types.MsgBuy) (*types.MsgBuyResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) + var err error - err := k.Keeper.CreateSubscription(ctx, msg.Creator, msg.Consumer, msg.Index, msg.Duration, msg.AutoRenewal) - if err == nil { - logger := k.Keeper.Logger(ctx) - details := map[string]string{ - "consumer": msg.Consumer, - "duration": strconv.FormatUint(msg.Duration, 10), - "plan": msg.Index, + if msg.AdvancePurchase { + err = k.Keeper.CreateFutureSubscription(ctx, msg.Creator, msg.Consumer, msg.Index, msg.Duration) + if err == nil { + logger := k.Keeper.Logger(ctx) + details := map[string]string{ + "consumer": msg.Consumer, + "duration": strconv.FormatUint(msg.Duration, 10), + "plan": msg.Index, + } + utils.LogLavaEvent(ctx, logger, types.AdvancedBuySubscriptionEventName, details, "advanced subscription purchased") + } + } else { + err = k.Keeper.CreateSubscription(ctx, msg.Creator, msg.Consumer, msg.Index, msg.Duration, msg.AutoRenewal) + if err == nil { + logger := k.Keeper.Logger(ctx) + details := map[string]string{ + "consumer": msg.Consumer, + "duration": strconv.FormatUint(msg.Duration, 10), + "plan": msg.Index, + } + utils.LogLavaEvent(ctx, logger, types.BuySubscriptionEventName, details, "subscription purchased") } - utils.LogLavaEvent(ctx, logger, types.BuySubscriptionEventName, details, "subscription purchased") } + return &types.MsgBuyResponse{}, err } diff --git a/x/subscription/keeper/subscription.go b/x/subscription/keeper/subscription.go index cbfc616c3a..53a6280022 100644 --- a/x/subscription/keeper/subscription.go +++ b/x/subscription/keeper/subscription.go @@ -34,26 +34,9 @@ func (k Keeper) CreateSubscription( var err error block := uint64(ctx.BlockHeight()) - - if _, err = sdk.AccAddressFromBech32(consumer); err != nil { - return utils.LavaFormatWarning("invalid subscription consumer address", err, - utils.Attribute{Key: "consumer", Value: consumer}, - ) - } - - creatorAcct, err := sdk.AccAddressFromBech32(creator) + creatorAcct, plan, err := k.verifySubscriptionBuyInputAndGetPlan(ctx, block, creator, consumer, planIndex) if err != nil { - return utils.LavaFormatWarning("invalid subscription creator address", err, - utils.Attribute{Key: "creator", Value: creator}, - ) - } - - plan, found := k.plansKeeper.GetPlan(ctx, planIndex) - if !found { - return utils.LavaFormatWarning("cannot create subscription with invalid plan", err, - utils.Attribute{Key: "plan", Value: planIndex}, - utils.Attribute{Key: "block", Value: block}, - ) + return err } var sub types.Subscription @@ -68,7 +51,7 @@ func (k Keeper) CreateSubscription( // When we make a subscription upgrade, we append the new subscription entry on the next epoch, // but, we want to get the most updated subscription here. // Hence, in case of user making double upgrade in the same epoch, we take the next epoch. - found = k.subsFS.FindEntry(ctx, consumer, nextEpoch, &sub) + found := k.subsFS.FindEntry(ctx, consumer, nextEpoch, &sub) // Subscription creation: // When: if not already exists for consumer address) @@ -111,11 +94,16 @@ func (k Keeper) CreateSubscription( utils.LogAttr("block", block), ) } + originalPlanIndex := sub.PlanIndex + originalPlanBlock := sub.PlanBlock err = k.upgradeSubscriptionPlan(ctx, duration, &sub, &plan) if err != nil { return err } + // Upgrade worked, now we can decrease the old plan's refcount + k.plansKeeper.PutPlan(ctx, originalPlanIndex, originalPlanBlock) + details := map[string]string{ "consumer": consumer, "oldPlan": sub.PlanIndex, @@ -166,26 +154,6 @@ func (k Keeper) CreateSubscription( return utils.LavaFormatWarning("create subscription failed", err) } - // subscription looks good; let's charge the creator - price := plan.GetPrice() - price.Amount = price.Amount.MulRaw(int64(duration)) - - if duration >= utils.MONTHS_IN_YEAR { - // adjust cost if discount given - discount := plan.GetAnnualDiscountPercentage() - if discount > 0 { - factor := int64(100 - discount) - price.Amount = price.Amount.MulRaw(factor).QuoRaw(100) - } - } - - if k.bankKeeper.GetBalance(ctx, creatorAcct, k.stakingKeeper.BondDenom(ctx)).IsLT(price) { - return utils.LavaFormatWarning("create subscription failed", legacyerrors.ErrInsufficientFunds, - utils.Attribute{Key: "creator", Value: creator}, - utils.Attribute{Key: "price", Value: price}, - ) - } - if !found || sub.AutoRenewal { expiry := uint64(utils.NextMonth(ctx.BlockTime()).UTC().Unix()) sub.MonthExpiryTime = expiry @@ -199,15 +167,45 @@ func (k Keeper) CreateSubscription( k.subsFS.ModifyEntry(ctx, consumer, sub.Block, &sub) } - err = k.bankKeeper.SendCoinsFromAccountToModule(ctx, creatorAcct, types.ModuleName, []sdk.Coin{price}) + // subscription looks good; let's charge the creator + price := plan.GetPrice() + price.Amount = price.Amount.MulRaw(int64(duration)) + k.applyPlanDiscountIfEligible(duration, &plan, &price) + + err = k.chargeFromCreatorAccountToModule(ctx, creatorAcct, price) if err != nil { - return utils.LavaFormatError("create subscription failed. funds transfer failed", err, + return err + } + + return nil +} + +func (k Keeper) verifySubscriptionBuyInputAndGetPlan(ctx sdk.Context, block uint64, creator, consumer, planIndex string) (creatorAcct sdk.AccAddress, plan planstypes.Plan, err error) { + EMPTY_PLAN := planstypes.Plan{} + + if _, err := sdk.AccAddressFromBech32(consumer); err != nil { + return nil, EMPTY_PLAN, utils.LavaFormatWarning("invalid subscription consumer address", err, + utils.Attribute{Key: "consumer", Value: consumer}, + ) + } + + creatorAcct, err = sdk.AccAddressFromBech32(creator) + if err != nil { + return nil, EMPTY_PLAN, utils.LavaFormatWarning("invalid subscription creator address", err, utils.Attribute{Key: "creator", Value: creator}, - utils.Attribute{Key: "price", Value: price}, ) } - return nil + plan, found := k.plansKeeper.GetPlan(ctx, planIndex) + if !found { + return nil, EMPTY_PLAN, utils.LavaFormatWarning("cannot create subscription with invalid plan", nil, + utils.Attribute{Key: "creator", Value: creator}, + utils.Attribute{Key: "consumer", Value: consumer}, + utils.Attribute{Key: "plan", Value: planIndex}, + utils.Attribute{Key: "block", Value: block}, + ) + } + return } func (k Keeper) createNewSubscription(ctx sdk.Context, plan *planstypes.Plan, creator, consumer string, @@ -262,13 +260,14 @@ func (k Keeper) upgradeSubscriptionPlan(ctx sdk.Context, duration uint64, sub *t utils.LogAttr("consumer", sub.Consumer)) } + // Remove one refcount for previous plan + k.plansKeeper.PutPlan(ctx, sub.PlanIndex, sub.PlanBlock) + sub.PlanIndex = toPlan.Index sub.PlanBlock = toPlan.Block sub.MonthCuTotal = toPlan.PlanPolicy.TotalCuLimit - k.resetSubscriptionDetailsAndAppendEntry(ctx, sub, nextEpoch, true) - - return nil + return k.resetSubscriptionDetailsAndAppendEntry(ctx, sub, nextEpoch, true) } func (k Keeper) advanceMonth(ctx sdk.Context, subkey []byte) { @@ -293,7 +292,31 @@ func (k Keeper) advanceMonth(ctx sdk.Context, subkey []byte) { sub.DurationTotal += 1 k.resetSubscriptionDetailsAndAppendEntry(ctx, &sub, block, false) } else { - if sub.AutoRenewal { + if sub.FutureSubscription != nil { + // Consumer made advance purchase. Now we activate it. + newSubInfo := sub.FutureSubscription + + plan, found := k.plansKeeper.FindPlan(ctx, newSubInfo.PlanIndex, newSubInfo.PlanBlock) + if !found { + utils.LavaFormatWarning("subscription advance purchase failed: could not find plan. removing subscription", nil, + utils.Attribute{Key: "consumer", Value: sub.Consumer}, + utils.Attribute{Key: "planIndex", Value: newSubInfo.PlanIndex}, + ) + k.RemoveExpiredSubscription(ctx, consumer, block, sub.PlanIndex, sub.PlanBlock) + return + } + + sub.Creator = newSubInfo.Creator + sub.PlanIndex = newSubInfo.PlanIndex + sub.PlanBlock = newSubInfo.PlanBlock + sub.DurationBought = newSubInfo.DurationBought + sub.DurationLeft = newSubInfo.DurationBought + sub.DurationTotal = 0 + sub.FutureSubscription = nil + sub.MonthCuTotal = plan.PlanPolicy.TotalCuLimit + + k.resetSubscriptionDetailsAndAppendEntry(ctx, &sub, block, false) + } else if sub.AutoRenewal { // apply the DurationLeft decrease to 0 and buy an extra month k.subsFS.ModifyEntry(ctx, sub.Consumer, sub.Block, &sub) err := k.CreateSubscription(ctx, sub.Creator, sub.Consumer, sub.PlanIndex, 1, sub.AutoRenewal) @@ -351,7 +374,7 @@ func (k Keeper) handleZeroDurationLeftForSubscription(ctx sdk.Context, block uin k.subsTS.AddTimerByBlockTime(ctx, expiry, []byte(sub.Consumer), []byte{}) } -func (k Keeper) resetSubscriptionDetailsAndAppendEntry(ctx sdk.Context, sub *types.Subscription, block uint64, deleteOldTimer bool) { +func (k Keeper) resetSubscriptionDetailsAndAppendEntry(ctx sdk.Context, sub *types.Subscription, block uint64, deleteOldTimer bool) error { // reset projects CU allowance for this coming month k.projectsKeeper.SnapshotSubscriptionProjects(ctx, sub.Consumer, block) @@ -385,14 +408,138 @@ func (k Keeper) resetSubscriptionDetailsAndAppendEntry(ctx sdk.Context, sub *typ err := k.subsFS.AppendEntry(ctx, sub.Consumer, block, sub) if err != nil { + // Remove new timer if failed + k.subsTS.DelTimerByBlockTime(ctx, expiry, []byte(sub.Consumer)) + // normally would panic! but ok to ignore - the subscription remains // as is with same remaining duration (but not renewed CU) - utils.LavaFormatError("critical: failed to recharge subscription", err, + return utils.LavaFormatError("critical: failed to recharge subscription", err, utils.Attribute{Key: "consumer", Value: sub.Consumer}, utils.Attribute{Key: "plan", Value: sub.PlanIndex}, utils.Attribute{Key: "block", Value: block}, ) } + + return nil +} + +func (k Keeper) applyPlanDiscountIfEligible(duration uint64, plan *planstypes.Plan, price *sdk.Coin) { + if duration >= utils.MONTHS_IN_YEAR { + // adjust cost if discount given + discount := plan.GetAnnualDiscountPercentage() + if discount > 0 { + factor := int64(100 - discount) + price.Amount = price.Amount.MulRaw(factor).QuoRaw(100) + } + } +} + +func (k Keeper) chargeFromCreatorAccountToModule(ctx sdk.Context, creator sdk.AccAddress, price sdk.Coin) error { + if k.bankKeeper.GetBalance(ctx, creator, k.stakingKeeper.BondDenom(ctx)).IsLT(price) { + return utils.LavaFormatWarning("create subscription failed", legacyerrors.ErrInsufficientFunds, + utils.LogAttr("creator", creator), + utils.LogAttr("price", price), + ) + } + + err := k.bankKeeper.SendCoinsFromAccountToModule(ctx, creator, types.ModuleName, []sdk.Coin{price}) + if err != nil { + return utils.LavaFormatError("create subscription failed. funds transfer failed", err, + utils.LogAttr("creator", creator), + utils.LogAttr("price", price), + ) + } + + return nil +} + +func (k Keeper) CreateFutureSubscription(ctx sdk.Context, + creator string, + consumer string, + planIndex string, + duration uint64, +) error { + var err error + + block := uint64(ctx.BlockHeight()) + creatorAcct, plan, err := k.verifySubscriptionBuyInputAndGetPlan(ctx, block, creator, consumer, planIndex) + if err != nil { + return err + } + + var sub types.Subscription + nextEpoch, err := k.epochstorageKeeper.GetNextEpoch(ctx, block) + if err != nil { + return utils.LavaFormatError("Got an error while trying to get next epoch on CreateSubscription", err, + utils.LogAttr("consumer", sub.Consumer), + utils.LogAttr("block", block), + ) + } + + // When we make a subscription upgrade, we append the new subscription entry on the next epoch, + // but, we want to get the most updated subscription here. + // Hence, in case of user making double upgrade in the same epoch, we take the next epoch. + found := k.subsFS.FindEntry(ctx, consumer, nextEpoch, &sub) + + if !found { + return utils.LavaFormatWarning("could not find active subscription. advanced purchase is only available for an active subscription", nil, + utils.LogAttr("consumer", consumer), + utils.LogAttr("block", block), + ) + } + + newPlanPrice := plan.GetPrice() + newPlanPrice.Amount = newPlanPrice.Amount.MulRaw(int64(duration)) + k.applyPlanDiscountIfEligible(duration, &plan, &newPlanPrice) + + if sub.FutureSubscription != nil { + // Consumer already has a future subscription + // If the new plan's price > current future subscription's plan - change and charge the diff + currentPlan, found := k.plansKeeper.FindPlan(ctx, sub.FutureSubscription.PlanIndex, sub.FutureSubscription.PlanBlock) + if !found { + return utils.LavaFormatError("panic: could not future subscription's plan. aborting", err, + utils.Attribute{Key: "creator", Value: creator}, + utils.Attribute{Key: "planIndex", Value: sub.FutureSubscription.PlanIndex}, + ) + } + + consumerBoughDuration := sub.FutureSubscription.DurationBought + consumerPaid := currentPlan.GetPrice() + consumerPaid.Amount = consumerPaid.Amount.MulRaw(int64(consumerBoughDuration)) + k.applyPlanDiscountIfEligible(consumerBoughDuration, &plan, &consumerPaid) + + if newPlanPrice.Amount.GT(consumerPaid.Amount) { + newPlanPrice.Amount = newPlanPrice.Amount.Sub(consumerPaid.Amount) + + details := map[string]string{ + "creator": creator, + "consumer": consumer, + "duration": strconv.FormatUint(duration, 10), + "oldPlanIndex": currentPlan.Index, + "oldPlanBlock": strconv.FormatUint(currentPlan.Block, 10), + "newPlanIndex": plan.Index, + "newPlanBlock": strconv.FormatUint(plan.Block, 10), + } + utils.LogLavaEvent(ctx, k.Logger(ctx), types.AdvancedBuyUpgradeSubscriptionEventName, details, "advanced subscription upgraded") + } else { + return utils.LavaFormatWarning("can't purchase another plan in advanced with a lower price", nil) + } + } + + err = k.chargeFromCreatorAccountToModule(ctx, creatorAcct, newPlanPrice) + if err != nil { + return err + } + + sub.FutureSubscription = &types.FutureSubscription{ + Creator: creator, + PlanIndex: plan.Index, + PlanBlock: plan.Block, + DurationBought: duration, + } + + k.subsFS.ModifyEntry(ctx, consumer, sub.Block, &sub) + return nil } func (k Keeper) RemoveExpiredSubscription(ctx sdk.Context, consumer string, block uint64, planIndex string, planBlock uint64) { diff --git a/x/subscription/keeper/subscription_test.go b/x/subscription/keeper/subscription_test.go index 8b17539b96..0eaadc96d6 100644 --- a/x/subscription/keeper/subscription_test.go +++ b/x/subscription/keeper/subscription_test.go @@ -1,6 +1,7 @@ package keeper_test import ( + "fmt" "strconv" "testing" "time" @@ -23,7 +24,19 @@ type tester struct { func newTester(t *testing.T) *tester { ts := &tester{Tester: *common.NewTester(t)} - ts.AddPlan("free", common.CreateMockPlan()) + freePlan := common.CreateMockPlan() + freePlan.Block = ts.BlockHeight() + ts.AddPlan("free", freePlan) + + premiumPlan := common.CreateMockPlan() + premiumPlan.Index = "premium" + premiumPlan.Price = freePlan.Price.AddAmount(math.NewInt(100)) + premiumPlan.Block = ts.BlockHeight() + premiumPlan.AnnualDiscountPercentage += 5 + premiumPlan.PlanPolicy.TotalCuLimit += 100 + premiumPlan.PlanPolicy.EpochCuLimit += 10 + ts.AddPlan(premiumPlan.Index, premiumPlan) + ts.DisableParticipationFees() return ts } @@ -171,7 +184,7 @@ func TestCreateSubscription(t *testing.T) { PlanIndex: tt.index, } - _, err := ts.TxSubscriptionBuy(sub.Creator, sub.Consumer, sub.PlanIndex, tt.duration, false) + _, err := ts.TxSubscriptionBuy(sub.Creator, sub.Consumer, sub.PlanIndex, tt.duration, false, false) if tt.success { require.Nil(t, err, tt.name) _, found := ts.getSubscription(sub.Consumer) @@ -191,7 +204,7 @@ func TestSubscriptionExpiration(t *testing.T) { _, sub1Addr := ts.Account("sub1") plan := ts.Plan("free") - _, err := ts.TxSubscriptionBuy(sub1Addr, sub1Addr, plan.Index, 1, false) + _, err := ts.TxSubscriptionBuy(sub1Addr, sub1Addr, plan.Index, 1, false, false) require.NoError(t, err) _, found := ts.getSubscription(sub1Addr) require.True(t, found) @@ -211,7 +224,7 @@ func TestRenewSubscription(t *testing.T) { _, sub1Addr := ts.Account("sub1") plan := ts.Plan("free") - _, err := ts.TxSubscriptionBuy(sub1Addr, sub1Addr, plan.Index, 6, false) + _, err := ts.TxSubscriptionBuy(sub1Addr, sub1Addr, plan.Index, 6, false, false) require.NoError(t, err) _, found := ts.getSubscription(sub1Addr) require.True(t, found) @@ -223,11 +236,11 @@ func TestRenewSubscription(t *testing.T) { require.Equal(t, uint64(3), sub.DurationLeft) // with 3 months duration left, asking for 12 more should fail - _, err = ts.TxSubscriptionBuy(sub1Addr, sub1Addr, plan.Index, 12, false) + _, err = ts.TxSubscriptionBuy(sub1Addr, sub1Addr, plan.Index, 12, false, false) require.NotNil(t, err) // but 9 additional month (even 10, the extra month extension below) - _, err = ts.TxSubscriptionBuy(sub1Addr, sub1Addr, plan.Index, 9, false) + _, err = ts.TxSubscriptionBuy(sub1Addr, sub1Addr, plan.Index, 9, false, false) require.NoError(t, err) sub, found = ts.getSubscription(sub1Addr) require.True(t, found) @@ -246,7 +259,7 @@ func TestRenewSubscription(t *testing.T) { // try extending the subscription (we could extend with 1 more month, // but since the subscription's plan changed and its new price is increased // by more than 5% , the extension should fail) - _, err = ts.TxSubscriptionBuy(sub1Addr, sub1Addr, plan.Index, 1, false) + _, err = ts.TxSubscriptionBuy(sub1Addr, sub1Addr, plan.Index, 1, false, false) require.NotNil(t, err) require.Equal(t, uint64(12), sub.DurationLeft) require.Equal(t, uint64(9), sub.DurationBought) @@ -266,7 +279,7 @@ func TestRenewSubscription(t *testing.T) { ts.AdvanceMonths(1).AdvanceEpoch() _, found = ts.getSubscription(sub1Addr) require.True(t, found) - _, err = ts.TxSubscriptionBuy(sub1Addr, sub1Addr, plan.Index, 10, false) + _, err = ts.TxSubscriptionBuy(sub1Addr, sub1Addr, plan.Index, 10, false, false) require.NotNil(t, err) } @@ -277,7 +290,7 @@ func TestSubscriptionAdminProject(t *testing.T) { _, sub1Addr := ts.Account("sub1") plan := ts.Plan("free") - _, err := ts.TxSubscriptionBuy(sub1Addr, sub1Addr, plan.Index, 1, false) + _, err := ts.TxSubscriptionBuy(sub1Addr, sub1Addr, plan.Index, 1, false, false) require.NoError(t, err) // a newly created subscription is expected to have one default project, @@ -295,7 +308,7 @@ func TestMonthlyRechargeCU(t *testing.T) { _, dev1Addr := ts.Account("dev1") plan := ts.Plan("free") - _, err := ts.TxSubscriptionBuy(sub1Addr, sub1Addr, plan.Index, 3, false) + _, err := ts.TxSubscriptionBuy(sub1Addr, sub1Addr, plan.Index, 3, false, false) require.NoError(t, err) // add another project under the subscription @@ -427,7 +440,7 @@ func TestExpiryTime(t *testing.T) { delta := now.Sub(ts.BlockTime()) ts.AdvanceBlock(delta) - _, err := ts.TxSubscriptionBuy(sub1Addr, sub1Addr, plan.Index, tt.months, false) + _, err := ts.TxSubscriptionBuy(sub1Addr, sub1Addr, plan.Index, tt.months, false, false) require.NoError(t, err) sub, found := ts.getSubscription(sub1Addr) @@ -451,7 +464,7 @@ func TestSubscriptionExpire(t *testing.T) { coins := common.NewCoins(ts.TokenDenom(), 10000) ts.Keepers.BankKeeper.SetBalance(ts.Ctx, sub1Acct.Addr, coins) - _, err := ts.TxSubscriptionBuy(sub1Addr, sub1Addr, plan.Index, 1, false) + _, err := ts.TxSubscriptionBuy(sub1Addr, sub1Addr, plan.Index, 1, false, false) require.NoError(t, err) block := ts.BlockHeight() @@ -510,7 +523,7 @@ func TestPrice(t *testing.T) { err := ts.TxProposalAddPlans(plan) require.NoError(t, err) - _, err = ts.TxSubscriptionBuy(sub1Addr, sub1Addr, plan.Index, tt.duration, false) + _, err = ts.TxSubscriptionBuy(sub1Addr, sub1Addr, plan.Index, tt.duration, false, false) require.NoError(t, err) _, found := ts.getSubscription(sub1Addr) @@ -534,7 +547,7 @@ func TestAddProjectToSubscription(t *testing.T) { _, dev1Addr := ts.Account("dev1") plan := ts.Plan("free") - _, err := ts.TxSubscriptionBuy(sub1Addr, dev1Addr, plan.Index, 1, false) + _, err := ts.TxSubscriptionBuy(sub1Addr, dev1Addr, plan.Index, 1, false, false) require.NoError(t, err) template := []struct { @@ -583,9 +596,9 @@ func TestGetProjectsForSubscription(t *testing.T) { plan := ts.Plan("free") // buy two subscriptions - _, err := ts.TxSubscriptionBuy(sub1Addr, sub1Addr, plan.Index, 1, false) + _, err := ts.TxSubscriptionBuy(sub1Addr, sub1Addr, plan.Index, 1, false, false) require.NoError(t, err) - _, err = ts.TxSubscriptionBuy(sub2Addr, sub2Addr, plan.Index, 1, false) + _, err = ts.TxSubscriptionBuy(sub2Addr, sub2Addr, plan.Index, 1, false, false) require.NoError(t, err) // add two projects to the first subscription @@ -627,7 +640,7 @@ func TestAddDelProjectForSubscription(t *testing.T) { plan := ts.Plan("free") // buy subscription and add project - _, err := ts.TxSubscriptionBuy(sub1Addr, sub1Addr, plan.Index, 1, false) + _, err := ts.TxSubscriptionBuy(sub1Addr, sub1Addr, plan.Index, 1, false, false) require.NoError(t, err) projData := projectstypes.ProjectData{ @@ -663,7 +676,7 @@ func TestDelProjectEndSubscription(t *testing.T) { plan := ts.Plan("free") // buy subscription - _, err := ts.TxSubscriptionBuy(sub1Addr, sub1Addr, plan.Index, 1, false) + _, err := ts.TxSubscriptionBuy(sub1Addr, sub1Addr, plan.Index, 1, false, false) require.NoError(t, err) // time of buy subscription @@ -710,7 +723,7 @@ func TestDurationTotal(t *testing.T) { plan := ts.Plan("free") _, subAddr := ts.Account("sub1") - _, err := ts.TxSubscriptionBuy(subAddr, subAddr, plan.Index, months, false) + _, err := ts.TxSubscriptionBuy(subAddr, subAddr, plan.Index, months, false, false) require.NoError(t, err) for i := 0; i < months-1; i++ { @@ -728,7 +741,7 @@ func TestDurationTotal(t *testing.T) { durationSoFar := subRes.Sub.DurationTotal extraMonths := 4 - _, err = ts.TxSubscriptionBuy(subAddr, subAddr, plan.Index, extraMonths, false) + _, err = ts.TxSubscriptionBuy(subAddr, subAddr, plan.Index, extraMonths, false, false) require.NoError(t, err) for i := 0; i < extraMonths; i++ { @@ -747,7 +760,7 @@ func TestDurationTotal(t *testing.T) { require.NoError(t, err) require.Nil(t, subRes.Sub) - _, err = ts.TxSubscriptionBuy(subAddr, subAddr, plan.Index, extraMonths, false) + _, err = ts.TxSubscriptionBuy(subAddr, subAddr, plan.Index, extraMonths, false, false) require.NoError(t, err) subRes, err = ts.QuerySubscriptionCurrent(subAddr) require.NoError(t, err) @@ -769,13 +782,13 @@ func TestSubAutoRenewal(t *testing.T) { // buy two subscriptions with enabled auto-renewal in two different ways // and one with disabled auto-renewal. // verify the auto-renewal flag is true in the first two subs - _, err := ts.TxSubscriptionBuy(subAddr1, subAddr1, plan.Index, 1, true) + _, err := ts.TxSubscriptionBuy(subAddr1, subAddr1, plan.Index, 1, true, false) require.NoError(t, err) - _, err = ts.TxSubscriptionBuy(subAddr2, subAddr2, plan.Index, 1, false) + _, err = ts.TxSubscriptionBuy(subAddr2, subAddr2, plan.Index, 1, false, false) require.NoError(t, err) err = ts.TxSubscriptionAutoRenewal(subAddr2, true) require.NoError(t, err) - _, err = ts.TxSubscriptionBuy(subAddr3, subAddr3, plan.Index, 1, false) + _, err = ts.TxSubscriptionBuy(subAddr3, subAddr3, plan.Index, 1, false, false) require.NoError(t, err) sub1, found := ts.getSubscription(subAddr1) @@ -814,7 +827,7 @@ func TestSubRenewalFailHighPlanPrice(t *testing.T) { _, subAddr1 := ts.Account("sub1") plan := ts.Plan("free") - _, err := ts.TxSubscriptionBuy(subAddr1, subAddr1, plan.Index, 1, true) + _, err := ts.TxSubscriptionBuy(subAddr1, subAddr1, plan.Index, 1, true, false) require.NoError(t, err) _, found := ts.getSubscription(subAddr1) require.True(t, found) @@ -850,15 +863,15 @@ func TestNextToMonthExpiryQuery(t *testing.T) { _, sub3 := ts.Account("sub3") // buy 3 subs - 2 at the same time and one a second later - _, err := ts.TxSubscriptionBuy(sub1, sub1, plan.Index, months, false) + _, err := ts.TxSubscriptionBuy(sub1, sub1, plan.Index, months, false, false) require.NoError(t, err) - _, err = ts.TxSubscriptionBuy(sub2, sub2, plan.Index, months, false) + _, err = ts.TxSubscriptionBuy(sub2, sub2, plan.Index, months, false, false) require.NoError(t, err) sub1Obj, found := ts.getSubscription(sub1) require.True(t, found) ts.AdvanceBlock(time.Second) - _, err = ts.TxSubscriptionBuy(sub3, sub3, plan.Index, months, false) + _, err = ts.TxSubscriptionBuy(sub3, sub3, plan.Index, months, false, false) require.NoError(t, err) sub3Obj, found := ts.getSubscription(sub3) require.True(t, found) @@ -908,7 +921,7 @@ func TestPlanRemovedWhenSubscriptionExpires(t *testing.T) { _, sub1 := ts.Account("sub1") // buy sub with plan first version - _, err := ts.TxSubscriptionBuy(sub1, sub1, plan.Index, months, false) + _, err := ts.TxSubscriptionBuy(sub1, sub1, plan.Index, months, false, false) require.NoError(t, err) oldPlanBlock := ts.BlockHeight() @@ -940,17 +953,10 @@ func TestSubscriptionUpgrade(t *testing.T) { _, consumer := ts.Account("sub1") freePlan := ts.Plan("free") - - // Add premium plan - upgradedPlan := common.CreateMockPlan() - upgradedPlan.Index = "premium" - upgradedPlan.Price = freePlan.Price.AddAmount(math.NewInt(100)) - upgradedPlan.PlanPolicy.TotalCuLimit += 10000 - upgradedPlan.PlanPolicy.EpochCuLimit += 1000 - ts.AddPlan(upgradedPlan.Index, upgradedPlan) + premiumPlan := ts.Plan("premium") // Buy free plan - _, err := ts.TxSubscriptionBuy(consumer, consumer, freePlan.Index, 1, false) + _, err := ts.TxSubscriptionBuy(consumer, consumer, freePlan.Index, 1, false, false) require.NoError(t, err) // Verify subscription found inside getSubscription sub := getSubscriptionAndFailTestIfNotFound(t, ts, consumer) @@ -975,7 +981,7 @@ func TestSubscriptionUpgrade(t *testing.T) { currentDurationTotal := sub.DurationTotal // Buy premium plan - _, err = ts.TxSubscriptionBuy(consumer, consumer, upgradedPlan.Index, 1, false) + _, err = ts.TxSubscriptionBuy(consumer, consumer, premiumPlan.Index, 1, false, false) require.NoError(t, err) nextEpoch := ts.GetNextEpoch() @@ -994,12 +1000,12 @@ func TestSubscriptionUpgrade(t *testing.T) { // Test that the subscription is now updated sub = getSubscriptionAndFailTestIfNotFound(t, ts, consumer) - require.Equal(t, upgradedPlan.Index, sub.PlanIndex) - require.Equal(t, upgradedPlan.PlanPolicy.TotalCuLimit, sub.MonthCuTotal) + require.Equal(t, premiumPlan.Index, sub.PlanIndex) + require.Equal(t, premiumPlan.PlanPolicy.TotalCuLimit, sub.MonthCuTotal) pairingEffectivePolicy, err = ts.QueryPairingEffectivePolicy(spec.Index, consumer) require.NoError(t, err) - require.Equal(t, upgradedPlan.PlanPolicy.EpochCuLimit, pairingEffectivePolicy.Policy.EpochCuLimit) + require.Equal(t, premiumPlan.PlanPolicy.EpochCuLimit, pairingEffectivePolicy.Policy.EpochCuLimit) // Test that the project is now updated project = getProjectAndFailTestIfNotFound(t, ts, consumer, ts.BlockHeight()) @@ -1012,15 +1018,10 @@ func TestSubscriptionDowngradeFails(t *testing.T) { _, consumer := ts.Account("sub1") freePlan := ts.Plan("free") - - // Add premium plan - upgradedPlan := common.CreateMockPlan() - upgradedPlan.Index = "premium" - upgradedPlan.Price = freePlan.Price.AddAmount(math.NewInt(100)) - ts.AddPlan(upgradedPlan.Index, upgradedPlan) + premiumPlan := ts.Plan("premium") // Buy premium plan - _, err := ts.TxSubscriptionBuy(consumer, consumer, upgradedPlan.Index, 1, false) + _, err := ts.TxSubscriptionBuy(consumer, consumer, premiumPlan.Index, 1, false, false) require.NoError(t, err) // Verify subscription found inside getSubscription getSubscriptionAndFailTestIfNotFound(t, ts, consumer) @@ -1028,13 +1029,13 @@ func TestSubscriptionDowngradeFails(t *testing.T) { ts.AdvanceEpochs(2) // Buy premium plan - _, err = ts.TxSubscriptionBuy(consumer, consumer, freePlan.Index, 1, false) + _, err = ts.TxSubscriptionBuy(consumer, consumer, freePlan.Index, 1, false, false) require.NotNil(t, err) ts.AdvanceEpoch() sub := getSubscriptionAndFailTestIfNotFound(t, ts, consumer) - require.Equal(t, upgradedPlan.Index, sub.PlanIndex) + require.Equal(t, premiumPlan.Index, sub.PlanIndex) } func TestSubscriptionCuExhaustAndUpgrade(t *testing.T) { @@ -1059,12 +1060,7 @@ func TestSubscriptionCuExhaustAndUpgrade(t *testing.T) { ts.AdvanceEpoch() freePlan := ts.Plan("free") - - // Add premium plan - premiumPlan := common.CreateMockPlan() - premiumPlan.Index = "premium" - premiumPlan.Price = freePlan.Price.AddAmount(math.NewInt(100)) - ts.AddPlan(premiumPlan.Index, premiumPlan) + premiumPlan := ts.Plan("premium") // Add premium-plus plan premiumPlusPlan := common.CreateMockPlan() @@ -1073,7 +1069,7 @@ func TestSubscriptionCuExhaustAndUpgrade(t *testing.T) { ts.AddPlan(premiumPlusPlan.Index, premiumPlusPlan) // Buy free plan - _, err = ts.TxSubscriptionBuy(consumerAddr, consumerAddr, freePlan.Index, 3, false) + _, err = ts.TxSubscriptionBuy(consumerAddr, consumerAddr, freePlan.Index, 3, false, false) require.NoError(t, err) // Verify subscription found inside getSubscription @@ -1108,7 +1104,7 @@ func TestSubscriptionCuExhaustAndUpgrade(t *testing.T) { sendRelayPayment() // Buy premium plan - _, err = ts.TxSubscriptionBuy(consumerAddr, consumerAddr, premiumPlan.Index, 1, false) + _, err = ts.TxSubscriptionBuy(consumerAddr, consumerAddr, premiumPlan.Index, 1, false, false) require.NoError(t, err) // Trigger new subscription @@ -1126,7 +1122,7 @@ func TestSubscriptionCuExhaustAndUpgrade(t *testing.T) { sendRelayPayment() // Buy premium-plus plan - _, err = ts.TxSubscriptionBuy(consumerAddr, consumerAddr, premiumPlusPlan.Index, 1, false) + _, err = ts.TxSubscriptionBuy(consumerAddr, consumerAddr, premiumPlusPlan.Index, 1, false, false) require.NoError(t, err) // Trigger new subscription @@ -1158,6 +1154,676 @@ func TestSubscriptionCuExhaustAndUpgrade(t *testing.T) { require.Equal(t, expectedPrice, reward.Amount) } +func TestSubscriptionAdvancePurchaseStartsOnExpirationOfCurrent(t *testing.T) { + ts := newTester(t) + ts.SetupAccounts(1, 0, 0) // 1 sub, 0 adm, 0 dev + ts.AddSpec("myspec", common.CreateMockSpec()) + + consumerAcc, consumerAddr := ts.Account("sub1") + spec := ts.Spec("myspec") + freePlan := ts.Plan("free") + premiumPlan := ts.Plan("premium") + consumerBalance := ts.GetBalance(consumerAcc.Addr) + + // Buy free plan + freePlanDuration := int64(2) + _, err := ts.TxSubscriptionBuy(consumerAddr, consumerAddr, freePlan.Index, int(freePlanDuration), false, false) + require.Nil(t, err) + // Verify subscription found inside getSubscription + getSubscriptionAndFailTestIfNotFound(t, ts, consumerAddr) + consumerBalance -= freePlan.Price.Amount.MulRaw(freePlanDuration).Int64() + // Make sure the balance checks out + require.Equal(t, consumerBalance, ts.GetBalance(consumerAcc.Addr)) + + newSubDuration := uint64(4) + _, err = ts.TxSubscriptionBuy(consumerAddr, consumerAddr, premiumPlan.Index, int(newSubDuration), false, true) + require.Nil(t, err) + + // Verify that the consumer charged with the correct amount + consumerShouldPay := premiumPlan.Price.Amount.MulRaw(int64(newSubDuration)) + expectedConsumerBalance := consumerBalance - consumerShouldPay.Int64() + require.Equal(t, expectedConsumerBalance, ts.GetBalance(consumerAcc.Addr)) + + // Verify new future subscription + sub := getSubscriptionAndFailTestIfNotFound(t, ts, consumerAddr) + futureSub := sub.FutureSubscription + require.NotNil(t, futureSub) + require.Equal(t, premiumPlan.Index, futureSub.PlanIndex) + require.Equal(t, premiumPlan.Block, futureSub.PlanBlock) + require.Equal(t, newSubDuration, futureSub.DurationBought) + + ts.AdvanceMonths(1).AdvanceEpoch() + + // Should still be the same before the subscription expires + sub = getSubscriptionAndFailTestIfNotFound(t, ts, consumerAddr) + futureSub = sub.FutureSubscription + require.NotNil(t, futureSub) + require.Equal(t, premiumPlan.Index, futureSub.PlanIndex) + require.Equal(t, premiumPlan.Block, futureSub.PlanBlock) + require.Equal(t, newSubDuration, futureSub.DurationBought) + + ts.AdvanceMonths(1).AdvanceEpoch() + + // New subscription should now be active + sub = getSubscriptionAndFailTestIfNotFound(t, ts, consumerAddr) + require.Nil(t, sub.FutureSubscription) + require.Equal(t, premiumPlan.Index, sub.PlanIndex) + require.Equal(t, premiumPlan.Block, sub.PlanBlock) + require.Equal(t, newSubDuration, sub.DurationBought) + require.Equal(t, newSubDuration, sub.DurationLeft) + require.Equal(t, uint64(0), sub.DurationTotal) + require.Equal(t, premiumPlan.PlanPolicy.TotalCuLimit, sub.MonthCuTotal) + + pairingEffectivePolicy, err := ts.QueryPairingEffectivePolicy(spec.Index, consumerAddr) + require.NoError(t, err) + require.Equal(t, premiumPlan.PlanPolicy.EpochCuLimit, pairingEffectivePolicy.Policy.EpochCuLimit) +} + +func TestSubscriptionAdvancePurchaseSuccessOnPricierPlan_SameBlock(t *testing.T) { + ts := newTester(t) + ts.SetupAccounts(1, 0, 0) // 1 sub, 0 adm, 0 dev + + CHEAP := "cheap" + MEDIUM := "medium" + EXPENSIVE := "expensive" + + cheapPlan := common.CreateMockPlan() + cheapPlan.Index = CHEAP + cheapPlan.Price = common.NewCoin(ts.TokenDenom(), 100) + cheapPlan.Block = ts.BlockHeight() + ts.AddPlan(cheapPlan.Index, cheapPlan) + + mediumPlan := common.CreateMockPlan() + mediumPlan.Index = MEDIUM + mediumPlan.Price = common.NewCoin(ts.TokenDenom(), 200) + mediumPlan.Block = ts.BlockHeight() + ts.AddPlan(mediumPlan.Index, mediumPlan) + + expansivePlan := common.CreateMockPlan() + expansivePlan.Index = EXPENSIVE + expansivePlan.Price = common.NewCoin(ts.TokenDenom(), 400) + expansivePlan.Block = ts.BlockHeight() + ts.AddPlan(expansivePlan.Index, expansivePlan) + + // We start with the medium plan. + // All these test cases should be with a new plan that is more expensive than the plan before them: + // 1. Expansive plan && less duration + // 2. Cheaper plan && more duration + // 3. Same plan && more duration + // 4. Expansive plan && same duration + // 5. Expansive plan && more duration + + startingDuration := int64(3) + originalPlanCost := mediumPlan.Price.Amount.MulRaw(startingDuration).Int64() + testCases := []struct { + name string + plan *planstypes.Plan + duration int64 + price int64 + }{ + { + name: "Expansive plan && less duration", + plan: &expansivePlan, // 400 + duration: startingDuration - 1, // * 2 + price: expansivePlan.Price.Amount.MulRaw(startingDuration - 1).Int64(), // = 800, + }, + { + name: "Cheaper plan && more duration", + plan: &cheapPlan, // 100 + duration: startingDuration + 6, // * 9 + price: cheapPlan.Price.Amount.MulRaw(startingDuration + 6).Int64(), // = 900 + }, + { + name: "Same plan && more duration", + plan: &mediumPlan, // 200 + duration: startingDuration + 2, // * 5 + price: mediumPlan.Price.Amount.MulRaw(startingDuration + 2).Int64(), // = 1000 + }, + { + name: "Expansive plan && same duration", + plan: &expansivePlan, // 400 + duration: startingDuration, // * 3 + price: expansivePlan.Price.Amount.MulRaw(startingDuration).Int64(), // = 1200, + }, + { + name: "Expansive plan && more duration", + plan: &expansivePlan, // 400 + duration: startingDuration + 1, // * 4 + price: expansivePlan.Price.Amount.MulRaw(startingDuration + 1).Int64(), // = 1600, + }, + } + + consumerAcc, consumerAddr := ts.Account("sub1") + consumerBalance := ts.GetBalance(consumerAcc.Addr) + + // Buy medium plan + _, err := ts.TxSubscriptionBuy(consumerAddr, consumerAddr, mediumPlan.Index, 1, false, false) + require.Nil(t, err) + // Verify subscription found inside getSubscription + getSubscriptionAndFailTestIfNotFound(t, ts, consumerAddr) + + consumerBalance -= mediumPlan.Price.Amount.Int64() + // Make sure the balance checks out + require.Equal(t, consumerBalance, ts.GetBalance(consumerAcc.Addr)) + + // Buy future medium plan + _, err = ts.TxSubscriptionBuy(consumerAddr, consumerAddr, mediumPlan.Index, int(startingDuration), false, true) + require.Nil(t, err) + // Verify subscription found inside getSubscription + getSubscriptionAndFailTestIfNotFound(t, ts, consumerAddr) + + consumerBalance -= mediumPlan.Price.Amount.MulRaw(startingDuration).Int64() + // Make sure the balance checks out + require.Equal(t, consumerBalance, ts.GetBalance(consumerAcc.Addr)) + + prevPlanPrice := originalPlanCost + for _, testCase := range testCases { + testName := fmt.Sprintf("%s -> Price: %d", testCase.name, testCase.price) + // Buy new plan + _, err := ts.TxSubscriptionBuy(consumerAddr, consumerAddr, testCase.plan.Index, int(testCase.duration), false, true) + require.Nil(t, err, testName) + + priceDiff := testCase.price - prevPlanPrice + consumerBalance -= priceDiff + + prevPlanPrice = testCase.price + + // Make sure the balance is updated + require.Equal(t, consumerBalance, ts.GetBalance(consumerAcc.Addr), testName) + } +} + +func TestSubscriptionAdvancePurchaseSuccessOnPricierPlan_NewBlock(t *testing.T) { + ts := newTester(t) + ts.SetupAccounts(1, 0, 0) // 1 sub, 0 adm, 0 dev + + MEDIUM := "medium" + + mediumPlan := common.CreateMockPlan() + mediumPlan.Index = MEDIUM + mediumPlan.Price = common.NewCoin(ts.TokenDenom(), 200) + mediumPlan.Block = ts.BlockHeight() + ts.AddPlan(mediumPlan.Index, mediumPlan) + + // We start with the medium plan. + // All these test cases should be with a new plan that is more expensive than current: + // 1. Same plan && cheaper && more duration + // 2. Same plan && more expensive && less duration + // 3. Same plan && more expensive && same duration + // 4. Same plan && more expensive && more duration + + startingDuration := int64(2) + + consumerAcc, consumerAddr := ts.Account("sub1") + consumerBalance := ts.GetBalance(consumerAcc.Addr) + + // Buy medium plan + _, err := ts.TxSubscriptionBuy(consumerAddr, consumerAddr, mediumPlan.Index, 1, false, false) + require.NoError(t, err) + // Verify subscription found inside getSubscription + getSubscriptionAndFailTestIfNotFound(t, ts, consumerAddr) + + // Make sure the balance checks out + consumerBalance -= mediumPlan.Price.Amount.Int64() + require.Equal(t, consumerBalance, ts.GetBalance(consumerAcc.Addr)) + + // Buy future medium plan + _, err = ts.TxSubscriptionBuy(consumerAddr, consumerAddr, mediumPlan.Index, int(startingDuration), false, true) + require.NoError(t, err) + // Verify subscription found inside getSubscription + getSubscriptionAndFailTestIfNotFound(t, ts, consumerAddr) + + // Make sure the balance checks out + prevPlanCost := mediumPlan.Price.Amount.MulRaw(startingDuration).Int64() + consumerBalance -= prevPlanCost + require.Equal(t, consumerBalance, ts.GetBalance(consumerAcc.Addr)) + + ts.AdvanceBlock() + + // Create plan with same index - cheaper price + mediumPlanCheaper := common.CreateMockPlan() + mediumPlanCheaper.Index = MEDIUM + mediumPlanCheaper.Price = common.NewCoin(ts.TokenDenom(), 100) + mediumPlanCheaper.Block = ts.BlockHeight() + ts.AddPlan(mediumPlanCheaper.Index, mediumPlanCheaper) + + // 1. Buy new plan + newPlanDuration := startingDuration + 3 // 5 + newPlanCost := mediumPlanCheaper.Price.Amount.MulRaw(newPlanDuration) // 500 + _, err = ts.TxSubscriptionBuy(consumerAddr, consumerAddr, mediumPlanCheaper.Index, int(newPlanDuration), false, true) + require.NoError(t, err, "Same plan && cheaper && more duration -> Price: "+newPlanCost.String()) + + priceDiff := newPlanCost.SubRaw(prevPlanCost).Int64() + consumerBalance -= priceDiff + + prevPlanCost = newPlanCost.Int64() + + // Make sure the balance has changed + require.Equal(t, consumerBalance, ts.GetBalance(consumerAcc.Addr)) + + ts.AdvanceBlock() + + // Create plan with same index - higher price + mediumPlanExpensive := common.CreateMockPlan() + mediumPlanExpensive.Index = MEDIUM + mediumPlanExpensive.Price = common.NewCoin(ts.TokenDenom(), 600) + mediumPlanExpensive.Block = ts.BlockHeight() + ts.AddPlan(mediumPlanExpensive.Index, mediumPlanExpensive) + + // 2. Buy new plan + newPlanDuration = startingDuration - 1 // 1 + newPlanCost = mediumPlanExpensive.Price.Amount.MulRaw(newPlanDuration) // 600 + _, err = ts.TxSubscriptionBuy(consumerAddr, consumerAddr, mediumPlanExpensive.Index, int(newPlanDuration), false, true) + require.NoError(t, err, "Same plan && more expensive && less duration -> Price: "+newPlanCost.String()) + + priceDiff = newPlanCost.SubRaw(prevPlanCost).Int64() + consumerBalance -= priceDiff + + prevPlanCost = newPlanCost.Int64() + + // Make sure the balance has changed + require.Equal(t, consumerBalance, ts.GetBalance(consumerAcc.Addr)) + + // 3. Buy new plan + newPlanDuration = startingDuration // 2 + newPlanCost = mediumPlanExpensive.Price.Amount.MulRaw(newPlanDuration) // 1200 + _, err = ts.TxSubscriptionBuy(consumerAddr, consumerAddr, mediumPlanExpensive.Index, int(newPlanDuration), false, true) + require.NoError(t, err, "Same plan && more expensive && same duration -> Price: "+newPlanCost.String()) + + priceDiff = newPlanCost.SubRaw(prevPlanCost).Int64() + consumerBalance -= priceDiff + + prevPlanCost = newPlanCost.Int64() + + // Make sure the balance has changed + require.Equal(t, consumerBalance, ts.GetBalance(consumerAcc.Addr)) + + // 4. Buy new plan + newPlanDuration = startingDuration + 1 // 3 + newPlanCost = mediumPlanExpensive.Price.Amount.MulRaw(newPlanDuration) // 1800 + _, err = ts.TxSubscriptionBuy(consumerAddr, consumerAddr, mediumPlanExpensive.Index, int(newPlanDuration), false, true) + require.NoError(t, err, "Same plan && more expensive && more duration -> Price: "+newPlanCost.String()) + + priceDiff = newPlanCost.SubRaw(prevPlanCost).Int64() + consumerBalance -= priceDiff + + // Make sure the balance has changed + require.Equal(t, consumerBalance, ts.GetBalance(consumerAcc.Addr)) +} + +func TestSubscriptionAdvancePurchaseFailOnCheaperPlans_SameBlock(t *testing.T) { + ts := newTester(t) + ts.SetupAccounts(1, 0, 0) // 1 sub, 0 adm, 0 dev + + CHEAP := "cheap" + MEDIUM := "medium" + EXPENSIVE := "expensive" + + cheapPlan := common.CreateMockPlan() + cheapPlan.Index = CHEAP + cheapPlan.Price = common.NewCoin(ts.TokenDenom(), 100) + cheapPlan.Block = ts.BlockHeight() + ts.AddPlan(cheapPlan.Index, cheapPlan) + + mediumPlan := common.CreateMockPlan() + mediumPlan.Index = MEDIUM + mediumPlan.Price = common.NewCoin(ts.TokenDenom(), 200) + mediumPlan.Block = ts.BlockHeight() + ts.AddPlan(mediumPlan.Index, mediumPlan) + + expansivePlan := common.CreateMockPlan() + expansivePlan.Index = EXPENSIVE + expansivePlan.Price = common.NewCoin(ts.TokenDenom(), 300) + expansivePlan.Block = ts.BlockHeight() + ts.AddPlan(expansivePlan.Index, expansivePlan) + + // We start with the medium plan. + // All these test cases should be with a new plan that is equal or cheaper than current: + // 1. Same plan && same duration = equal + // 2. Same plan && less duration = cheaper + // 3. Cheaper plan && same duration = cheaper + // 4. Cheaper plan && more duration = equal + // 5. Cheaper plan && more duration = cheaper + // 6. Expansive plan && less duration = equal + // 7. Expansive plan && less duration = cheaper + + startingDuration := int64(3) + // Original cost: 200 * 3 = 600 + testCases := []struct { + name string + plan *planstypes.Plan + duration int64 + price int64 + }{ + { + name: "Same plan && same duration", + plan: &mediumPlan, // 200 + duration: startingDuration, // * 3 + price: mediumPlan.Price.Amount.MulRaw(startingDuration).Int64(), // = 600 + }, + { + name: "Same plan && less duration", + plan: &mediumPlan, // 200 + duration: startingDuration - 1, // * 2 + price: mediumPlan.Price.Amount.MulRaw(startingDuration - 1).Int64(), // = 400 + }, + { + name: "Cheaper plan && same duration", + plan: &cheapPlan, // 100 + duration: startingDuration, // * 3 + price: cheapPlan.Price.Amount.MulRaw(startingDuration).Int64(), // = 300, + }, + { + name: "Cheaper plan && more duration", + plan: &cheapPlan, // 100 + duration: startingDuration + 3, // * 6 + price: cheapPlan.Price.Amount.MulRaw(startingDuration + 3).Int64(), // = 600, + }, + { + name: "Cheaper plan && more duration", + plan: &cheapPlan, // 100 + duration: startingDuration + 1, // * 4 + price: cheapPlan.Price.Amount.MulRaw(startingDuration + 1).Int64(), // = 400, + }, + { + name: "Expansive plan && less duration", + plan: &expansivePlan, // 300 + duration: startingDuration - 1, // * 2 + price: expansivePlan.Price.Amount.MulRaw(startingDuration - 1).Int64(), // = 600, + }, + { + name: "Expansive plan && less duration", + plan: &expansivePlan, // 300 + duration: startingDuration - 2, // * 1 + price: expansivePlan.Price.Amount.MulRaw(startingDuration - 2).Int64(), // = 300, + }, + } + + consumerAcc, consumerAddr := ts.Account("sub1") + consumerBalance := ts.GetBalance(consumerAcc.Addr) + + // Buy medium plan + _, err := ts.TxSubscriptionBuy(consumerAddr, consumerAddr, mediumPlan.Index, 1, false, false) + require.Nil(t, err) + // Verify subscription found inside getSubscription + getSubscriptionAndFailTestIfNotFound(t, ts, consumerAddr) + + consumerBalance -= mediumPlan.Price.Amount.Int64() + // Make sure the balance checks out + require.Equal(t, consumerBalance, ts.GetBalance(consumerAcc.Addr)) + + // Buy future medium plan + _, err = ts.TxSubscriptionBuy(consumerAddr, consumerAddr, mediumPlan.Index, int(startingDuration), false, true) + require.Nil(t, err) + // Verify subscription found inside getSubscription + getSubscriptionAndFailTestIfNotFound(t, ts, consumerAddr) + + consumerBalance -= mediumPlan.Price.Amount.MulRaw(startingDuration).Int64() + // Make sure the balance checks out + require.Equal(t, consumerBalance, ts.GetBalance(consumerAcc.Addr)) + + for _, testCase := range testCases { + testName := fmt.Sprintf("%s -> Price: %d", testCase.name, testCase.price) + + // Buy new plan + _, err := ts.TxSubscriptionBuy(consumerAddr, consumerAddr, testCase.plan.Index, int(testCase.duration), false, true) + require.NotNil(t, err, testName) + + // Make sure the balance is not changed + require.Equal(t, consumerBalance, ts.GetBalance(consumerAcc.Addr), testName) + } +} + +func TestSubscriptionAdvancePurchaseFailOnCheaperPlans_NewBlock(t *testing.T) { + ts := newTester(t) + ts.SetupAccounts(1, 0, 0) // 1 sub, 0 adm, 0 dev + + MEDIUM := "medium" + + mediumPlan := common.CreateMockPlan() + mediumPlan.Index = MEDIUM + mediumPlan.Price = common.NewCoin(ts.TokenDenom(), 200) + mediumPlan.Block = ts.BlockHeight() + ts.AddPlan(mediumPlan.Index, mediumPlan) + + // We start with the medium plan. + // All these test cases should be with a new plan that is equal or cheaper than current: + // 1. Same plan && more expensive && less duration = equal + // 2. Same plan && more expensive && less duration = cheaper + // 3. Same plan && cheaper && more duration = equal + // 4. Same plan && cheaper && more duration = cheaper + + startingDuration := int64(3) + // Original cost: 200 * 3 = 600 + + consumerAcc, consumerAddr := ts.Account("sub1") + consumerBalance := ts.GetBalance(consumerAcc.Addr) + + // Buy medium plan + _, err := ts.TxSubscriptionBuy(consumerAddr, consumerAddr, mediumPlan.Index, 1, false, false) + require.Nil(t, err) + // Verify subscription found inside getSubscription + getSubscriptionAndFailTestIfNotFound(t, ts, consumerAddr) + + // Make sure the balance checks out + consumerBalance -= mediumPlan.Price.Amount.Int64() + require.Equal(t, consumerBalance, ts.GetBalance(consumerAcc.Addr)) + + // Buy future medium plan + _, err = ts.TxSubscriptionBuy(consumerAddr, consumerAddr, mediumPlan.Index, int(startingDuration), false, true) + require.Nil(t, err) + // Verify subscription found inside getSubscription + getSubscriptionAndFailTestIfNotFound(t, ts, consumerAddr) + + // Make sure the balance checks out + consumerBalance -= mediumPlan.Price.Amount.MulRaw(startingDuration).Int64() + require.Equal(t, consumerBalance, ts.GetBalance(consumerAcc.Addr)) + + ts.AdvanceBlock() + + // Create plan with same index - cheaper price + mediumPlanCheaper := common.CreateMockPlan() + mediumPlanCheaper.Index = MEDIUM + mediumPlanCheaper.Price = common.NewCoin(ts.TokenDenom(), 100) + mediumPlanCheaper.Block = ts.BlockHeight() + ts.AddPlan(mediumPlanCheaper.Index, mediumPlanCheaper) + + // Buy new plan + _, err = ts.TxSubscriptionBuy(consumerAddr, consumerAddr, mediumPlanCheaper.Index, int(startingDuration+3), false, true) + require.NotNil(t, err, "Same plan && cheaper && more duration -> Price: "+ + mediumPlanCheaper.Price.Amount.MulRaw(startingDuration+3).String()) // 600 + + // Make sure the balance is not changed + require.Equal(t, consumerBalance, ts.GetBalance(consumerAcc.Addr)) + + // Buy new plan + _, err = ts.TxSubscriptionBuy(consumerAddr, consumerAddr, mediumPlanCheaper.Index, int(startingDuration+2), false, true) + require.NotNil(t, err, "Same plan && cheaper && more duration -> Price: "+ + mediumPlanCheaper.Price.Amount.MulRaw(startingDuration+2).String()) // 500 + + // Make sure the balance is not changed + require.Equal(t, consumerBalance, ts.GetBalance(consumerAcc.Addr)) + + ts.AdvanceBlock() + + // Create plan with same index - higher price + mediumPlanExpensive := common.CreateMockPlan() + mediumPlanExpensive.Index = MEDIUM + mediumPlanExpensive.Price = common.NewCoin(ts.TokenDenom(), 300) + mediumPlanExpensive.Block = ts.BlockHeight() + ts.AddPlan(mediumPlanExpensive.Index, mediumPlanExpensive) + + // Buy new plan + _, err = ts.TxSubscriptionBuy(consumerAddr, consumerAddr, mediumPlanCheaper.Index, int(startingDuration-1), false, true) + require.NotNil(t, err, "Same plan && cheaper && more duration -> Price: "+ + mediumPlanCheaper.Price.Amount.MulRaw(startingDuration-1).String()) // 600 + + // Make sure the balance is not changed + require.Equal(t, consumerBalance, ts.GetBalance(consumerAcc.Addr)) + + // Buy new plan + _, err = ts.TxSubscriptionBuy(consumerAddr, consumerAddr, mediumPlanCheaper.Index, int(startingDuration-2), false, true) + require.NotNil(t, err, "Same plan && cheaper && more duration -> Price: "+ + mediumPlanCheaper.Price.Amount.MulRaw(startingDuration-2).String()) // 500 + + // Make sure the balance is not changed + require.Equal(t, consumerBalance, ts.GetBalance(consumerAcc.Addr)) +} + +func TestSubscriptionAdvancePurchaseFailOnNoSubscription(t *testing.T) { + ts := newTester(t) + ts.SetupAccounts(1, 0, 0) // 1 sub, 0 adm, 0 dev + + consumerAcc, consumerAddr := ts.Account("sub1") + premiumPlan := ts.Plan("premium") + consumerBalance := ts.GetBalance(consumerAcc.Addr) + + // Advance purchase the subscription with no active subscription + newSubDuration := int64(4) + _, err := ts.TxSubscriptionBuy(consumerAddr, consumerAddr, premiumPlan.Index, int(newSubDuration), false, true) + require.NotNil(t, err) + + // Verify that the consumer is not charged + require.Equal(t, consumerBalance, ts.GetBalance(consumerAcc.Addr)) + + // Verify that there is no new subscription + _, found := ts.getSubscription(consumerAddr) + require.False(t, found) +} + +func TestSubscriptionAdvancePurchaseNewCreator(t *testing.T) { + ts := newTester(t) + ts.SetupAccounts(1, 0, 0) // 1 sub, 0 adm, 0 dev + + creator1Acc, creator1Addr := ts.AddAccount("sugar1", 0, 20000) + creator1Balance := ts.GetBalance(creator1Acc.Addr) + + creator2Acc, creator2Addr := ts.AddAccount("sugar2", 1, 20000) + creator2Balance := ts.GetBalance(creator2Acc.Addr) + + consumerAcc, consumerAddr := ts.Account("sub1") + consumerBalance := ts.GetBalance(consumerAcc.Addr) + freePlan := ts.Plan("free") + premiumPlan := ts.Plan("premium") + + // Buy free plan + freePlanDuration := int64(2) + _, err := ts.TxSubscriptionBuy(consumerAddr, consumerAddr, freePlan.Index, int(freePlanDuration), false, false) + require.Nil(t, err) + // Verify subscription found inside getSubscription + getSubscriptionAndFailTestIfNotFound(t, ts, consumerAddr) + consumerBalance -= freePlan.Price.Amount.MulRaw(freePlanDuration).Int64() + // Make sure that creator1 paid for the subscription + require.Equal(t, consumerBalance, ts.GetBalance(consumerAcc.Addr)) + + // Creator1 buys a future subscription + newSubDuration := uint64(1) + _, err = ts.TxSubscriptionBuy(creator1Addr, consumerAddr, premiumPlan.Index, int(newSubDuration), false, true) + require.Nil(t, err) + + // Make sure that creator1 paid for the new subscription + creator1Balance -= premiumPlan.Price.Amount.MulRaw(int64(newSubDuration)).Int64() + require.Equal(t, creator1Balance, ts.GetBalance(creator1Acc.Addr)) + + // Trigger new subscription + ts.AdvanceMonths(int(freePlanDuration)).AdvanceEpoch() + + // Make sure that creator1 is now the creator of the subscription + sub := getSubscriptionAndFailTestIfNotFound(t, ts, consumerAddr) + require.Nil(t, sub.FutureSubscription) + require.Equal(t, creator1Addr, sub.Creator) + + // Creator2 buys a future subscription + _, err = ts.TxSubscriptionBuy(creator2Addr, consumerAddr, premiumPlan.Index, int(newSubDuration), false, true) + require.Nil(t, err) + + // Make sure that creator2 paid for the new subscription + creator2Balance -= premiumPlan.Price.Amount.MulRaw(int64(newSubDuration)).Int64() + require.Equal(t, creator2Balance, ts.GetBalance(creator2Acc.Addr)) + + // Trigger new subscription + ts.AdvanceMonths(int(newSubDuration)).AdvanceEpoch() + + // Make sure that creator2 is now the creator of the subscription + sub = getSubscriptionAndFailTestIfNotFound(t, ts, consumerAddr) + require.Nil(t, sub.FutureSubscription) + require.Equal(t, creator2Addr, sub.Creator) + + // Original consumer buys a future subscription + _, err = ts.TxSubscriptionBuy(consumerAddr, consumerAddr, premiumPlan.Index, int(newSubDuration), false, true) + require.Nil(t, err) + + // Make sure that consumer paid for the new subscription + consumerBalance -= premiumPlan.Price.Amount.MulRaw(int64(newSubDuration)).Int64() + require.Equal(t, consumerBalance, ts.GetBalance(consumerAcc.Addr)) + + // Trigger new subscription + ts.AdvanceMonths(int(newSubDuration)).AdvanceEpoch() + + // Make sure that consumer is now the creator of the subscription + sub = getSubscriptionAndFailTestIfNotFound(t, ts, consumerAddr) + require.Nil(t, sub.FutureSubscription) + require.Equal(t, consumerAddr, sub.Creator) +} + +func TestSubscriptionAdvancePurchaseAnnuallyDiscount(t *testing.T) { + ts := newTester(t) + ts.SetupAccounts(1, 0, 0) // 1 sub, 0 adm, 0 dev + + consumerAcc, consumerAddr := ts.Account("sub1") + consumerBalance := ts.GetBalance(consumerAcc.Addr) + freePlan := ts.Plan("free") + premiumPlan := ts.Plan("premium") + + premiumPlusPlan := common.CreateMockPlan() + premiumPlusPlan.Index = "premiumPlus" + premiumPlusPlan.Price = premiumPlan.Price.AddAmount(math.NewInt(100)) + premiumPlusPlan.Block = ts.BlockHeight() + premiumPlusPlan.AnnualDiscountPercentage += 5 + premiumPlusPlan.PlanPolicy.TotalCuLimit += 100 + premiumPlusPlan.PlanPolicy.EpochCuLimit += 10 + ts.AddPlan(premiumPlusPlan.Index, premiumPlusPlan) + + // Buy free plan + freePlanDuration := int64(12) + _, err := ts.TxSubscriptionBuy(consumerAddr, consumerAddr, freePlan.Index, int(freePlanDuration), false, false) + require.Nil(t, err) + // Verify subscription found inside getSubscription + getSubscriptionAndFailTestIfNotFound(t, ts, consumerAddr) + + discount := freePlan.GetAnnualDiscountPercentage() + factor := int64(100 - discount) + freePlanPriceAfterDiscount := freePlan.Price.Amount.MulRaw(freePlanDuration).MulRaw(factor).QuoRaw(100).Int64() + consumerBalance -= freePlanPriceAfterDiscount + + // Make sure that consumer paid for the subscription + require.Equal(t, consumerBalance, ts.GetBalance(consumerAcc.Addr)) + + // Consumer buys a future premium subscription + newSubDuration := uint64(12) + _, err = ts.TxSubscriptionBuy(consumerAddr, consumerAddr, premiumPlan.Index, int(newSubDuration), false, true) + require.Nil(t, err) + + // Make sure that consumer paid for the new subscription + discount = premiumPlan.GetAnnualDiscountPercentage() + factor = int64(100 - discount) + premiumPlanPriceAfterDiscount := premiumPlan.Price.Amount.MulRaw(int64(newSubDuration)).MulRaw(factor).QuoRaw(100).Int64() + consumerBalance -= premiumPlanPriceAfterDiscount + require.Equal(t, consumerBalance, ts.GetBalance(consumerAcc.Addr)) + + // Consumer buys a future premiumPlus subscription + _, err = ts.TxSubscriptionBuy(consumerAddr, consumerAddr, premiumPlusPlan.Index, int(newSubDuration), false, true) + require.Nil(t, err) + + // Make sure that consumer paid for the new subscription + discount = premiumPlusPlan.GetAnnualDiscountPercentage() + factor = int64(100 - discount) + premiumPlusPlanPriceAfterDiscount := premiumPlusPlan.Price.Amount.MulRaw(int64(newSubDuration)).MulRaw(factor).QuoRaw(100).Int64() + diffPrice := premiumPlusPlanPriceAfterDiscount - premiumPlanPriceAfterDiscount + consumerBalance -= diffPrice + require.Equal(t, consumerBalance, ts.GetBalance(consumerAcc.Addr)) +} + func TestSubscriptionUpgradeAffectsTimer(t *testing.T) { ts := newTester(t) ts.SetupAccounts(1, 0, 0) // 1 sub, 0 adm, 0 dev @@ -1178,7 +1844,7 @@ func TestSubscriptionUpgradeAffectsTimer(t *testing.T) { ts.AddPlan(premiumPlusPlan.Index, premiumPlusPlan) // Buy free plan - _, err := ts.TxSubscriptionBuy(consumerAddr, consumerAddr, freePlan.Index, 3, false) + _, err := ts.TxSubscriptionBuy(consumerAddr, consumerAddr, freePlan.Index, 3, false, false) require.NoError(t, err) // Verify timer for free plan expiration @@ -1196,7 +1862,7 @@ func TestSubscriptionUpgradeAffectsTimer(t *testing.T) { ts.AdvanceBlock() // Buy premium plan - _, err = ts.TxSubscriptionBuy(consumerAddr, consumerAddr, premiumPlan.Index, 1, false) + _, err = ts.TxSubscriptionBuy(consumerAddr, consumerAddr, premiumPlan.Index, 1, false, false) require.NoError(t, err) verifyTimerStore() @@ -1204,7 +1870,7 @@ func TestSubscriptionUpgradeAffectsTimer(t *testing.T) { ts.AdvanceBlock() // Buy premium-plus plan - _, err = ts.TxSubscriptionBuy(consumerAddr, consumerAddr, premiumPlusPlan.Index, 1, false) + _, err = ts.TxSubscriptionBuy(consumerAddr, consumerAddr, premiumPlusPlan.Index, 1, false, false) require.NoError(t, err) verifyTimerStore() diff --git a/x/subscription/types/message_buy.go b/x/subscription/types/message_buy.go index de1c25d7c2..e308f247a9 100644 --- a/x/subscription/types/message_buy.go +++ b/x/subscription/types/message_buy.go @@ -12,13 +12,14 @@ const TypeMsgBuy = "buy" var _ sdk.Msg = &MsgBuy{} -func NewMsgBuy(creator, consumer, index string, duration uint64, autoRenewal bool) *MsgBuy { +func NewMsgBuy(creator, consumer, index string, duration uint64, autoRenewal, advancePurchase bool) *MsgBuy { return &MsgBuy{ - Creator: creator, - Consumer: consumer, - Index: index, - Duration: duration, - AutoRenewal: autoRenewal, + Creator: creator, + Consumer: consumer, + Index: index, + Duration: duration, + AutoRenewal: autoRenewal, + AdvancePurchase: advancePurchase, } } @@ -58,6 +59,9 @@ func (msg *MsgBuy) ValidateBasic() error { if msg.Duration == 0 || msg.Duration > MAX_SUBSCRIPTION_DURATION { return sdkerrors.Wrapf(ErrInvalidParameter, "invalid subscription duration (%s)", msg.Index) } + if msg.AutoRenewal && msg.AdvancePurchase { + return sdkerrors.Wrapf(ErrInvalidParameter, "can't use enable-auto-renewal and advance-purchase flags together") + } return nil } diff --git a/x/subscription/types/subscription.pb.go b/x/subscription/types/subscription.pb.go index b1e9401ad6..f131a31091 100644 --- a/x/subscription/types/subscription.pb.go +++ b/x/subscription/types/subscription.pb.go @@ -23,19 +23,20 @@ var _ = math.Inf const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package type Subscription struct { - Creator string `protobuf:"bytes,1,opt,name=creator,proto3" json:"creator,omitempty"` - Consumer string `protobuf:"bytes,2,opt,name=consumer,proto3" json:"consumer,omitempty"` - Block uint64 `protobuf:"varint,3,opt,name=block,proto3" json:"block,omitempty"` - PlanIndex string `protobuf:"bytes,4,opt,name=plan_index,json=planIndex,proto3" json:"plan_index,omitempty"` - PlanBlock uint64 `protobuf:"varint,5,opt,name=plan_block,json=planBlock,proto3" json:"plan_block,omitempty"` - DurationBought uint64 `protobuf:"varint,6,opt,name=duration_bought,json=durationBought,proto3" json:"duration_bought,omitempty"` - DurationLeft uint64 `protobuf:"varint,7,opt,name=duration_left,json=durationLeft,proto3" json:"duration_left,omitempty"` - MonthExpiryTime uint64 `protobuf:"varint,8,opt,name=month_expiry_time,json=monthExpiryTime,proto3" json:"month_expiry_time,omitempty"` - MonthCuTotal uint64 `protobuf:"varint,10,opt,name=month_cu_total,json=monthCuTotal,proto3" json:"month_cu_total,omitempty"` - MonthCuLeft uint64 `protobuf:"varint,11,opt,name=month_cu_left,json=monthCuLeft,proto3" json:"month_cu_left,omitempty"` - Cluster string `protobuf:"bytes,13,opt,name=cluster,proto3" json:"cluster,omitempty"` - DurationTotal uint64 `protobuf:"varint,14,opt,name=duration_total,json=durationTotal,proto3" json:"duration_total,omitempty"` - AutoRenewal bool `protobuf:"varint,15,opt,name=auto_renewal,json=autoRenewal,proto3" json:"auto_renewal,omitempty"` + Creator string `protobuf:"bytes,1,opt,name=creator,proto3" json:"creator,omitempty"` + Consumer string `protobuf:"bytes,2,opt,name=consumer,proto3" json:"consumer,omitempty"` + Block uint64 `protobuf:"varint,3,opt,name=block,proto3" json:"block,omitempty"` + PlanIndex string `protobuf:"bytes,4,opt,name=plan_index,json=planIndex,proto3" json:"plan_index,omitempty"` + PlanBlock uint64 `protobuf:"varint,5,opt,name=plan_block,json=planBlock,proto3" json:"plan_block,omitempty"` + DurationBought uint64 `protobuf:"varint,6,opt,name=duration_bought,json=durationBought,proto3" json:"duration_bought,omitempty"` + DurationLeft uint64 `protobuf:"varint,7,opt,name=duration_left,json=durationLeft,proto3" json:"duration_left,omitempty"` + MonthExpiryTime uint64 `protobuf:"varint,8,opt,name=month_expiry_time,json=monthExpiryTime,proto3" json:"month_expiry_time,omitempty"` + MonthCuTotal uint64 `protobuf:"varint,10,opt,name=month_cu_total,json=monthCuTotal,proto3" json:"month_cu_total,omitempty"` + MonthCuLeft uint64 `protobuf:"varint,11,opt,name=month_cu_left,json=monthCuLeft,proto3" json:"month_cu_left,omitempty"` + Cluster string `protobuf:"bytes,13,opt,name=cluster,proto3" json:"cluster,omitempty"` + DurationTotal uint64 `protobuf:"varint,14,opt,name=duration_total,json=durationTotal,proto3" json:"duration_total,omitempty"` + AutoRenewal bool `protobuf:"varint,15,opt,name=auto_renewal,json=autoRenewal,proto3" json:"auto_renewal,omitempty"` + FutureSubscription *FutureSubscription `protobuf:"bytes,16,opt,name=future_subscription,json=futureSubscription,proto3" json:"future_subscription,omitempty"` } func (m *Subscription) Reset() { *m = Subscription{} } @@ -162,8 +163,84 @@ func (m *Subscription) GetAutoRenewal() bool { return false } +func (m *Subscription) GetFutureSubscription() *FutureSubscription { + if m != nil { + return m.FutureSubscription + } + return nil +} + +type FutureSubscription struct { + Creator string `protobuf:"bytes,1,opt,name=creator,proto3" json:"creator,omitempty"` + PlanIndex string `protobuf:"bytes,2,opt,name=plan_index,json=planIndex,proto3" json:"plan_index,omitempty"` + PlanBlock uint64 `protobuf:"varint,3,opt,name=plan_block,json=planBlock,proto3" json:"plan_block,omitempty"` + DurationBought uint64 `protobuf:"varint,4,opt,name=duration_bought,json=durationBought,proto3" json:"duration_bought,omitempty"` +} + +func (m *FutureSubscription) Reset() { *m = FutureSubscription{} } +func (m *FutureSubscription) String() string { return proto.CompactTextString(m) } +func (*FutureSubscription) ProtoMessage() {} +func (*FutureSubscription) Descriptor() ([]byte, []int) { + return fileDescriptor_c3bc5507ca237d79, []int{1} +} +func (m *FutureSubscription) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *FutureSubscription) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_FutureSubscription.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *FutureSubscription) XXX_Merge(src proto.Message) { + xxx_messageInfo_FutureSubscription.Merge(m, src) +} +func (m *FutureSubscription) XXX_Size() int { + return m.Size() +} +func (m *FutureSubscription) XXX_DiscardUnknown() { + xxx_messageInfo_FutureSubscription.DiscardUnknown(m) +} + +var xxx_messageInfo_FutureSubscription proto.InternalMessageInfo + +func (m *FutureSubscription) GetCreator() string { + if m != nil { + return m.Creator + } + return "" +} + +func (m *FutureSubscription) GetPlanIndex() string { + if m != nil { + return m.PlanIndex + } + return "" +} + +func (m *FutureSubscription) GetPlanBlock() uint64 { + if m != nil { + return m.PlanBlock + } + return 0 +} + +func (m *FutureSubscription) GetDurationBought() uint64 { + if m != nil { + return m.DurationBought + } + return 0 +} + func init() { proto.RegisterType((*Subscription)(nil), "lavanet.lava.subscription.Subscription") + proto.RegisterType((*FutureSubscription)(nil), "lavanet.lava.subscription.FutureSubscription") } func init() { @@ -171,32 +248,36 @@ func init() { } var fileDescriptor_c3bc5507ca237d79 = []byte{ - // 391 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x54, 0x92, 0xc1, 0xae, 0xd2, 0x40, - 0x14, 0x86, 0xa9, 0xb7, 0xf7, 0x52, 0x0e, 0x2d, 0xe0, 0xc4, 0xc5, 0x68, 0x62, 0x83, 0xa8, 0x91, - 0x18, 0x52, 0x16, 0xbe, 0x01, 0x46, 0x13, 0x89, 0xab, 0xca, 0xca, 0x4d, 0x33, 0x2d, 0x03, 0x34, - 0xb6, 0x9d, 0x66, 0x3a, 0xa3, 0xf0, 0x16, 0x3e, 0x96, 0x0b, 0x17, 0x2c, 0x5d, 0x1a, 0x78, 0x91, - 0x9b, 0x39, 0x2d, 0x0d, 0xac, 0x26, 0xe7, 0x3b, 0xdf, 0xdf, 0xd3, 0x99, 0x1c, 0x98, 0x65, 0xec, - 0x27, 0x2b, 0xb8, 0x9a, 0x9b, 0x73, 0x5e, 0xe9, 0xb8, 0x4a, 0x64, 0x5a, 0xaa, 0x54, 0x14, 0x37, - 0x45, 0x50, 0x4a, 0xa1, 0x04, 0x79, 0xde, 0xd8, 0x81, 0x39, 0x83, 0x6b, 0x61, 0xf2, 0xf7, 0x0e, - 0xdc, 0x6f, 0x57, 0x80, 0x50, 0xe8, 0x26, 0x92, 0x33, 0x25, 0x24, 0xb5, 0xc6, 0xd6, 0xb4, 0x17, - 0x5e, 0x4a, 0xf2, 0x02, 0x9c, 0x44, 0x14, 0x95, 0xce, 0xb9, 0xa4, 0x4f, 0xb0, 0xd5, 0xd6, 0xe4, - 0x19, 0xdc, 0xc7, 0x99, 0x48, 0x7e, 0xd0, 0xbb, 0xb1, 0x35, 0xb5, 0xc3, 0xba, 0x20, 0x2f, 0x01, - 0xca, 0x8c, 0x15, 0x51, 0x5a, 0xac, 0xf9, 0x9e, 0xda, 0x98, 0xe9, 0x19, 0xf2, 0xc5, 0x80, 0xb6, - 0x5d, 0x27, 0xef, 0x31, 0x89, 0xed, 0x05, 0xa6, 0xdf, 0xc1, 0x70, 0xad, 0x25, 0x33, 0x7f, 0x15, - 0xc5, 0x42, 0x6f, 0x77, 0x8a, 0x3e, 0xa0, 0x33, 0xb8, 0xe0, 0x05, 0x52, 0xf2, 0x1a, 0xbc, 0x56, - 0xcc, 0xf8, 0x46, 0xd1, 0x2e, 0x6a, 0xee, 0x05, 0x7e, 0xe5, 0x1b, 0x45, 0xde, 0xc3, 0xd3, 0x5c, - 0x14, 0x6a, 0x17, 0xf1, 0x7d, 0x99, 0xca, 0x43, 0xa4, 0xd2, 0x9c, 0x53, 0x07, 0xc5, 0x21, 0x36, - 0x3e, 0x21, 0x5f, 0xa5, 0x39, 0x27, 0x6f, 0x60, 0x50, 0xbb, 0x89, 0x8e, 0x94, 0x50, 0x2c, 0xa3, - 0x50, 0x7f, 0x11, 0xe9, 0x47, 0xbd, 0x32, 0x8c, 0x4c, 0xc0, 0x6b, 0x2d, 0x1c, 0xdb, 0x47, 0xa9, - 0xdf, 0x48, 0x38, 0xd5, 0xbc, 0x66, 0xa6, 0x2b, 0xc5, 0x25, 0xf5, 0x9a, 0xd7, 0xac, 0x4b, 0xf2, - 0x16, 0xda, 0x6b, 0x34, 0x33, 0x06, 0x18, 0x6f, 0xaf, 0x52, 0x0f, 0x79, 0x05, 0x2e, 0xd3, 0x4a, - 0x44, 0x92, 0x17, 0xfc, 0x17, 0xcb, 0xe8, 0x70, 0x6c, 0x4d, 0x9d, 0xb0, 0x6f, 0x58, 0x58, 0xa3, - 0xa5, 0xed, 0xf4, 0x46, 0xb0, 0xb4, 0x1d, 0x77, 0xe4, 0x2d, 0x3e, 0xff, 0x39, 0xf9, 0xd6, 0xf1, - 0xe4, 0x5b, 0xff, 0x4f, 0xbe, 0xf5, 0xfb, 0xec, 0x77, 0x8e, 0x67, 0xbf, 0xf3, 0xef, 0xec, 0x77, - 0xbe, 0xcf, 0xb6, 0xa9, 0xda, 0xe9, 0x38, 0x48, 0x44, 0x3e, 0xbf, 0x59, 0x9e, 0xfd, 0xed, 0xfa, - 0xa8, 0x43, 0xc9, 0xab, 0xf8, 0x01, 0x17, 0xe7, 0xc3, 0x63, 0x00, 0x00, 0x00, 0xff, 0xff, 0xea, - 0x29, 0x17, 0x34, 0x68, 0x02, 0x00, 0x00, + // 452 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x93, 0xcf, 0x6e, 0xd3, 0x40, + 0x10, 0xc6, 0xb3, 0xad, 0xdb, 0x3a, 0x93, 0xbf, 0x2c, 0x1c, 0x16, 0x24, 0xac, 0x10, 0x40, 0x44, + 0xa8, 0x38, 0x12, 0xbc, 0x41, 0x10, 0x95, 0xa8, 0x38, 0x99, 0x9e, 0x38, 0x60, 0xd9, 0xee, 0xa6, + 0xb1, 0xb0, 0xbd, 0xd6, 0x7a, 0x16, 0xd2, 0xb7, 0xe0, 0xc2, 0x5b, 0xf0, 0x20, 0x1c, 0x7b, 0xe4, + 0x88, 0x92, 0x17, 0x41, 0x1e, 0x27, 0x56, 0xac, 0x42, 0xd4, 0xd3, 0x6a, 0x7e, 0xf3, 0x7d, 0x1e, + 0xef, 0xce, 0x0c, 0x9c, 0x26, 0xc1, 0xd7, 0x20, 0x93, 0x38, 0x2d, 0xcf, 0x69, 0x61, 0xc2, 0x22, + 0xd2, 0x71, 0x8e, 0xb1, 0xca, 0x1a, 0x81, 0x9b, 0x6b, 0x85, 0x8a, 0x3f, 0xdc, 0xa8, 0xdd, 0xf2, + 0x74, 0x77, 0x05, 0xe3, 0x9f, 0x16, 0x74, 0x3f, 0xee, 0x00, 0x2e, 0xe0, 0x24, 0xd2, 0x32, 0x40, + 0xa5, 0x05, 0x1b, 0xb1, 0x49, 0xdb, 0xdb, 0x86, 0xfc, 0x11, 0xd8, 0x91, 0xca, 0x0a, 0x93, 0x4a, + 0x2d, 0x0e, 0x28, 0x55, 0xc7, 0xfc, 0x01, 0x1c, 0x85, 0x89, 0x8a, 0xbe, 0x88, 0xc3, 0x11, 0x9b, + 0x58, 0x5e, 0x15, 0xf0, 0xc7, 0x00, 0x79, 0x12, 0x64, 0x7e, 0x9c, 0x5d, 0xca, 0xa5, 0xb0, 0xc8, + 0xd3, 0x2e, 0xc9, 0xfb, 0x12, 0xd4, 0xe9, 0xca, 0x79, 0x44, 0x4e, 0x4a, 0xcf, 0xc8, 0xfd, 0x02, + 0x06, 0x97, 0x46, 0x07, 0xe5, 0x5f, 0xf9, 0xa1, 0x32, 0x57, 0x0b, 0x14, 0xc7, 0xa4, 0xe9, 0x6f, + 0xf1, 0x8c, 0x28, 0x7f, 0x0a, 0xbd, 0x5a, 0x98, 0xc8, 0x39, 0x8a, 0x13, 0x92, 0x75, 0xb7, 0xf0, + 0x83, 0x9c, 0x23, 0x7f, 0x09, 0xf7, 0x52, 0x95, 0xe1, 0xc2, 0x97, 0xcb, 0x3c, 0xd6, 0xd7, 0x3e, + 0xc6, 0xa9, 0x14, 0x36, 0x09, 0x07, 0x94, 0x78, 0x47, 0xfc, 0x22, 0x4e, 0x25, 0x7f, 0x06, 0xfd, + 0x4a, 0x1b, 0x19, 0x1f, 0x15, 0x06, 0x89, 0x80, 0xea, 0x8b, 0x44, 0xdf, 0x9a, 0x8b, 0x92, 0xf1, + 0x31, 0xf4, 0x6a, 0x15, 0x95, 0xed, 0x90, 0xa8, 0xb3, 0x11, 0x51, 0xd5, 0xf2, 0x35, 0x13, 0x53, + 0xa0, 0xd4, 0xa2, 0xb7, 0x79, 0xcd, 0x2a, 0xe4, 0xcf, 0xa1, 0xbe, 0xc6, 0xa6, 0x46, 0x9f, 0xec, + 0xf5, 0x55, 0xaa, 0x22, 0x4f, 0xa0, 0x1b, 0x18, 0x54, 0xbe, 0x96, 0x99, 0xfc, 0x16, 0x24, 0x62, + 0x30, 0x62, 0x13, 0xdb, 0xeb, 0x94, 0xcc, 0xab, 0x10, 0xff, 0x0c, 0xf7, 0xe7, 0x06, 0x8d, 0x96, + 0xfe, 0x6e, 0x67, 0xc5, 0x70, 0xc4, 0x26, 0x9d, 0xd7, 0xaf, 0xdc, 0xff, 0xf6, 0xde, 0x3d, 0x23, + 0xd7, 0x6e, 0xf7, 0x3d, 0x3e, 0xbf, 0xc5, 0xce, 0x2d, 0xbb, 0x3d, 0x84, 0x73, 0xcb, 0xee, 0x0e, + 0x7b, 0xe3, 0x1f, 0x0c, 0xf8, 0x6d, 0xdb, 0x9e, 0xa1, 0x69, 0x8e, 0xc0, 0xc1, 0xfe, 0x11, 0x38, + 0xbc, 0xc3, 0x08, 0x58, 0xff, 0x1a, 0x81, 0xd9, 0xd9, 0xaf, 0x95, 0xc3, 0x6e, 0x56, 0x0e, 0xfb, + 0xb3, 0x72, 0xd8, 0xf7, 0xb5, 0xd3, 0xba, 0x59, 0x3b, 0xad, 0xdf, 0x6b, 0xa7, 0xf5, 0xe9, 0xf4, + 0x2a, 0xc6, 0x85, 0x09, 0xdd, 0x48, 0xa5, 0xd3, 0xc6, 0xd2, 0x2c, 0x9b, 0x6b, 0x83, 0xd7, 0xb9, + 0x2c, 0xc2, 0x63, 0x5a, 0x98, 0x37, 0x7f, 0x03, 0x00, 0x00, 0xff, 0xff, 0xb1, 0xdc, 0x42, 0x8e, + 0x60, 0x03, 0x00, 0x00, } func (m *Subscription) Marshal() (dAtA []byte, err error) { @@ -219,6 +300,20 @@ func (m *Subscription) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.FutureSubscription != nil { + { + size, err := m.FutureSubscription.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSubscription(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x82 + } if m.AutoRenewal { i-- if m.AutoRenewal { @@ -300,6 +395,53 @@ func (m *Subscription) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *FutureSubscription) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *FutureSubscription) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *FutureSubscription) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.DurationBought != 0 { + i = encodeVarintSubscription(dAtA, i, uint64(m.DurationBought)) + i-- + dAtA[i] = 0x20 + } + if m.PlanBlock != 0 { + i = encodeVarintSubscription(dAtA, i, uint64(m.PlanBlock)) + i-- + dAtA[i] = 0x18 + } + if len(m.PlanIndex) > 0 { + i -= len(m.PlanIndex) + copy(dAtA[i:], m.PlanIndex) + i = encodeVarintSubscription(dAtA, i, uint64(len(m.PlanIndex))) + i-- + dAtA[i] = 0x12 + } + if len(m.Creator) > 0 { + i -= len(m.Creator) + copy(dAtA[i:], m.Creator) + i = encodeVarintSubscription(dAtA, i, uint64(len(m.Creator))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func encodeVarintSubscription(dAtA []byte, offset int, v uint64) int { offset -= sovSubscription(v) base := offset @@ -360,6 +502,33 @@ func (m *Subscription) Size() (n int) { if m.AutoRenewal { n += 2 } + if m.FutureSubscription != nil { + l = m.FutureSubscription.Size() + n += 2 + l + sovSubscription(uint64(l)) + } + return n +} + +func (m *FutureSubscription) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Creator) + if l > 0 { + n += 1 + l + sovSubscription(uint64(l)) + } + l = len(m.PlanIndex) + if l > 0 { + n += 1 + l + sovSubscription(uint64(l)) + } + if m.PlanBlock != 0 { + n += 1 + sovSubscription(uint64(m.PlanBlock)) + } + if m.DurationBought != 0 { + n += 1 + sovSubscription(uint64(m.DurationBought)) + } return n } @@ -698,6 +867,194 @@ func (m *Subscription) Unmarshal(dAtA []byte) error { } } m.AutoRenewal = bool(v != 0) + case 16: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FutureSubscription", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSubscription + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthSubscription + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSubscription + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.FutureSubscription == nil { + m.FutureSubscription = &FutureSubscription{} + } + if err := m.FutureSubscription.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipSubscription(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSubscription + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *FutureSubscription) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSubscription + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: FutureSubscription: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: FutureSubscription: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Creator", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSubscription + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthSubscription + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSubscription + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Creator = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PlanIndex", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSubscription + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthSubscription + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSubscription + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.PlanIndex = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field PlanBlock", wireType) + } + m.PlanBlock = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSubscription + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.PlanBlock |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DurationBought", wireType) + } + m.DurationBought = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSubscription + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.DurationBought |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipSubscription(dAtA[iNdEx:]) diff --git a/x/subscription/types/tx.pb.go b/x/subscription/types/tx.pb.go index b9ee2e772b..d7c5cbbbf7 100644 --- a/x/subscription/types/tx.pb.go +++ b/x/subscription/types/tx.pb.go @@ -30,11 +30,12 @@ var _ = math.Inf const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package type MsgBuy struct { - Creator string `protobuf:"bytes,1,opt,name=creator,proto3" json:"creator,omitempty"` - Consumer string `protobuf:"bytes,2,opt,name=consumer,proto3" json:"consumer,omitempty"` - Index string `protobuf:"bytes,3,opt,name=index,proto3" json:"index,omitempty"` - Duration uint64 `protobuf:"varint,4,opt,name=duration,proto3" json:"duration,omitempty"` - AutoRenewal bool `protobuf:"varint,6,opt,name=auto_renewal,json=autoRenewal,proto3" json:"auto_renewal,omitempty"` + Creator string `protobuf:"bytes,1,opt,name=creator,proto3" json:"creator,omitempty"` + Consumer string `protobuf:"bytes,2,opt,name=consumer,proto3" json:"consumer,omitempty"` + Index string `protobuf:"bytes,3,opt,name=index,proto3" json:"index,omitempty"` + Duration uint64 `protobuf:"varint,4,opt,name=duration,proto3" json:"duration,omitempty"` + AutoRenewal bool `protobuf:"varint,6,opt,name=auto_renewal,json=autoRenewal,proto3" json:"auto_renewal,omitempty"` + AdvancePurchase bool `protobuf:"varint,7,opt,name=advance_purchase,json=advancePurchase,proto3" json:"advance_purchase,omitempty"` } func (m *MsgBuy) Reset() { *m = MsgBuy{} } @@ -105,6 +106,13 @@ func (m *MsgBuy) GetAutoRenewal() bool { return false } +func (m *MsgBuy) GetAdvancePurchase() bool { + if m != nil { + return m.AdvancePurchase + } + return false +} + type MsgBuyResponse struct { } @@ -421,37 +429,38 @@ func init() { } var fileDescriptor_b1bb075a6865b817 = []byte{ - // 466 bytes of a gzipped FileDescriptorProto + // 490 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x54, 0xc1, 0x6e, 0xd3, 0x40, - 0x10, 0x8d, 0x89, 0x6b, 0xc2, 0xa4, 0xa0, 0x6a, 0x55, 0x8a, 0xf1, 0xc1, 0xa4, 0xe6, 0x12, 0x24, - 0xb4, 0x86, 0x72, 0xe6, 0xd0, 0xa8, 0xe2, 0x00, 0x8a, 0x84, 0xcc, 0x8d, 0x4b, 0xb5, 0xb1, 0x57, - 0x6e, 0x20, 0xd9, 0xb5, 0x76, 0xd7, 0x25, 0xfd, 0x0b, 0xae, 0x88, 0x1f, 0xea, 0xb1, 0x47, 0x4e, - 0x08, 0x25, 0x3f, 0x82, 0xbc, 0x6b, 0xc7, 0x36, 0xa8, 0x71, 0x4f, 0x3b, 0x33, 0xfb, 0xe6, 0xcd, - 0x9b, 0x19, 0xaf, 0x21, 0x58, 0x90, 0x4b, 0xc2, 0xa8, 0x0a, 0x8b, 0x33, 0x94, 0xf9, 0x4c, 0xc6, - 0x62, 0x9e, 0xa9, 0x39, 0x67, 0xa1, 0x5a, 0xe1, 0x4c, 0x70, 0xc5, 0xd1, 0xd3, 0x12, 0x83, 0x8b, - 0x13, 0x37, 0x31, 0xde, 0xf3, 0x56, 0x7a, 0x26, 0xf8, 0x17, 0x1a, 0x2b, 0x59, 0x19, 0x26, 0xdf, - 0x3b, 0x4c, 0x79, 0xca, 0xb5, 0x19, 0x16, 0x96, 0x89, 0x06, 0x3f, 0x2c, 0x70, 0xa6, 0x32, 0x9d, - 0xe4, 0x57, 0xc8, 0x85, 0xfb, 0xb1, 0xa0, 0x44, 0x71, 0xe1, 0x5a, 0x23, 0x6b, 0xfc, 0x20, 0xaa, - 0x5c, 0xe4, 0xc1, 0x20, 0xe6, 0x4c, 0xe6, 0x4b, 0x2a, 0xdc, 0x7b, 0xfa, 0x6a, 0xeb, 0xa3, 0x43, - 0xd8, 0x9b, 0xb3, 0x84, 0xae, 0xdc, 0xbe, 0xbe, 0x30, 0x4e, 0x91, 0x91, 0xe4, 0x82, 0x14, 0xea, - 0x5c, 0x7b, 0x64, 0x8d, 0xed, 0x68, 0xeb, 0xa3, 0x63, 0xd8, 0x27, 0xb9, 0xe2, 0xe7, 0x82, 0x32, - 0xfa, 0x8d, 0x2c, 0x5c, 0x67, 0x64, 0x8d, 0x07, 0xd1, 0xb0, 0x88, 0x45, 0x26, 0xf4, 0xde, 0x1e, - 0xec, 0x1d, 0x38, 0xc1, 0x01, 0x3c, 0x32, 0xd2, 0x22, 0x2a, 0x33, 0xce, 0x24, 0x0d, 0x2e, 0xe1, - 0xe1, 0x54, 0xa6, 0xa7, 0x49, 0xf2, 0xd1, 0xb4, 0xb6, 0x43, 0xf3, 0x07, 0xd8, 0x2f, 0xfb, 0x3f, - 0x4f, 0x88, 0x22, 0x5a, 0xf7, 0xf0, 0x24, 0xc0, 0xad, 0x29, 0x56, 0xa3, 0xc2, 0x25, 0xdf, 0x19, - 0x51, 0x64, 0x62, 0x5f, 0xff, 0x7e, 0xd6, 0x8b, 0x86, 0x59, 0x1d, 0x0a, 0x9e, 0xc0, 0xe3, 0x56, - 0xdd, 0xad, 0xa0, 0xb7, 0x5a, 0xd0, 0x19, 0x5d, 0x74, 0x0b, 0x42, 0x60, 0x33, 0xb2, 0xa4, 0xe5, - 0x00, 0xb5, 0x5d, 0xf2, 0xd6, 0xe9, 0x5b, 0xde, 0x89, 0x6e, 0xfd, 0xb4, 0x1e, 0xc9, 0x0e, 0xe2, - 0x23, 0x70, 0x28, 0x23, 0xb3, 0x85, 0xa1, 0x1e, 0x44, 0xa5, 0x17, 0xb8, 0x70, 0xd4, 0xe6, 0xa8, - 0xd8, 0x4f, 0x7e, 0xf6, 0xa1, 0x3f, 0x95, 0x29, 0xfa, 0x04, 0xfd, 0x62, 0xf1, 0xc7, 0xf8, 0xd6, - 0x4f, 0x0b, 0x9b, 0x05, 0x78, 0x2f, 0x3a, 0x21, 0x15, 0x39, 0xba, 0x00, 0x68, 0x2c, 0x68, 0xbc, - 0x3b, 0xb1, 0x46, 0x7a, 0xaf, 0xee, 0x8a, 0x6c, 0x56, 0x6a, 0x4c, 0xbe, 0xa3, 0x52, 0x8d, 0xec, - 0xaa, 0xf4, 0xff, 0x3a, 0xd0, 0x57, 0x18, 0x36, 0x77, 0xd1, 0x31, 0x8d, 0x06, 0xd4, 0x7b, 0x7d, - 0x67, 0x68, 0x55, 0x6c, 0xf2, 0xee, 0x7a, 0xed, 0x5b, 0x37, 0x6b, 0xdf, 0xfa, 0xb3, 0xf6, 0xad, - 0xef, 0x1b, 0xbf, 0x77, 0xb3, 0xf1, 0x7b, 0xbf, 0x36, 0x7e, 0xef, 0xf3, 0xcb, 0x74, 0xae, 0x2e, - 0xf2, 0x19, 0x8e, 0xf9, 0x32, 0x6c, 0x3d, 0xf9, 0xd5, 0x3f, 0xff, 0x8c, 0xab, 0x8c, 0xca, 0x99, - 0xa3, 0x5f, 0xf8, 0x9b, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x28, 0x35, 0xcb, 0x2c, 0x5d, 0x04, - 0x00, 0x00, + 0x10, 0x8d, 0x89, 0xeb, 0x86, 0x49, 0x81, 0x68, 0x55, 0x8a, 0xf1, 0xc1, 0xa4, 0xe6, 0x92, 0x4a, + 0xc8, 0x86, 0x72, 0xe6, 0xd0, 0xa8, 0xe2, 0x00, 0x8a, 0x54, 0x99, 0x1b, 0x97, 0x68, 0x63, 0xaf, + 0x9c, 0x40, 0xb2, 0x6b, 0xed, 0xae, 0x43, 0xfa, 0x17, 0xdc, 0xf9, 0x1d, 0x0e, 0x3d, 0xf6, 0xc8, + 0x09, 0xa1, 0xe4, 0x47, 0x90, 0xd7, 0xeb, 0xc4, 0x06, 0xb5, 0xce, 0x69, 0x67, 0x66, 0xdf, 0xbc, + 0x99, 0x79, 0xb3, 0x5a, 0xf0, 0xe6, 0x78, 0x89, 0x29, 0x91, 0x41, 0x7e, 0x06, 0x22, 0x9b, 0x88, + 0x88, 0xcf, 0x52, 0x39, 0x63, 0x34, 0x90, 0x2b, 0x3f, 0xe5, 0x4c, 0x32, 0xf4, 0x5c, 0x63, 0xfc, + 0xfc, 0xf4, 0xab, 0x18, 0xe7, 0x65, 0x2d, 0x3d, 0xe5, 0xec, 0x0b, 0x89, 0xa4, 0x28, 0x8d, 0x22, + 0xdf, 0x39, 0x4e, 0x58, 0xc2, 0x94, 0x19, 0xe4, 0x56, 0x11, 0xf5, 0x7e, 0x1a, 0x60, 0x8d, 0x44, + 0x32, 0xcc, 0xae, 0x91, 0x0d, 0x87, 0x11, 0x27, 0x58, 0x32, 0x6e, 0x1b, 0x7d, 0x63, 0xf0, 0x30, + 0x2c, 0x5d, 0xe4, 0x40, 0x27, 0x62, 0x54, 0x64, 0x0b, 0xc2, 0xed, 0x07, 0xea, 0x6a, 0xeb, 0xa3, + 0x63, 0x38, 0x98, 0xd1, 0x98, 0xac, 0xec, 0xb6, 0xba, 0x28, 0x9c, 0x3c, 0x23, 0xce, 0x38, 0xce, + 0xbb, 0xb3, 0xcd, 0xbe, 0x31, 0x30, 0xc3, 0xad, 0x8f, 0x4e, 0xe1, 0x08, 0x67, 0x92, 0x8d, 0x39, + 0xa1, 0xe4, 0x1b, 0x9e, 0xdb, 0x56, 0xdf, 0x18, 0x74, 0xc2, 0x6e, 0x1e, 0x0b, 0x8b, 0x10, 0x3a, + 0x83, 0x1e, 0x8e, 0x97, 0x98, 0x46, 0x64, 0x9c, 0x66, 0x3c, 0x9a, 0x62, 0x41, 0xec, 0x43, 0x05, + 0x7b, 0xa2, 0xe3, 0x57, 0x3a, 0xfc, 0xc1, 0xec, 0x1c, 0xf4, 0x2c, 0xaf, 0x07, 0x8f, 0x8b, 0x29, + 0x42, 0x22, 0x52, 0x46, 0x05, 0xf1, 0x96, 0xf0, 0x68, 0x24, 0x92, 0x8b, 0x38, 0xbe, 0x2a, 0x54, + 0xb8, 0x67, 0xbc, 0x8f, 0x70, 0xa4, 0xa5, 0x1a, 0xc7, 0x58, 0x62, 0x35, 0x62, 0xf7, 0xdc, 0xf3, + 0x6b, 0x82, 0x97, 0xaa, 0xfa, 0x9a, 0xef, 0x12, 0x4b, 0x3c, 0x34, 0x6f, 0x7e, 0xbf, 0x68, 0x85, + 0xdd, 0x74, 0x17, 0xf2, 0x9e, 0xc1, 0xd3, 0x5a, 0xdd, 0x6d, 0x43, 0xef, 0x54, 0x43, 0x97, 0x64, + 0xde, 0xdc, 0x10, 0x02, 0x93, 0xe2, 0x05, 0xd1, 0x5a, 0x2b, 0x5b, 0xf3, 0xee, 0xd2, 0xb7, 0xbc, + 0x43, 0x35, 0xfa, 0x45, 0x45, 0xbd, 0xbb, 0x89, 0x4f, 0xc0, 0x22, 0x14, 0x4f, 0xe6, 0x05, 0x75, + 0x27, 0xd4, 0x9e, 0x67, 0xc3, 0x49, 0x9d, 0xa3, 0x64, 0x3f, 0xff, 0xd1, 0x86, 0xf6, 0x48, 0x24, + 0xe8, 0x13, 0xb4, 0xf3, 0x37, 0x72, 0xea, 0xdf, 0xf9, 0x0a, 0xfd, 0x62, 0x01, 0xce, 0x59, 0x23, + 0xa4, 0x24, 0x47, 0x53, 0x80, 0xca, 0x82, 0x06, 0xf7, 0x27, 0xee, 0x90, 0xce, 0xeb, 0x7d, 0x91, + 0xd5, 0x4a, 0x15, 0xe5, 0x1b, 0x2a, 0xed, 0x90, 0x4d, 0x95, 0xfe, 0x5f, 0x07, 0xfa, 0x0a, 0xdd, + 0xea, 0x2e, 0x1a, 0xd4, 0xa8, 0x40, 0x9d, 0x37, 0x7b, 0x43, 0xcb, 0x62, 0xc3, 0xf7, 0x37, 0x6b, + 0xd7, 0xb8, 0x5d, 0xbb, 0xc6, 0x9f, 0xb5, 0x6b, 0x7c, 0xdf, 0xb8, 0xad, 0xdb, 0x8d, 0xdb, 0xfa, + 0xb5, 0x71, 0x5b, 0x9f, 0x5f, 0x25, 0x33, 0x39, 0xcd, 0x26, 0x7e, 0xc4, 0x16, 0x41, 0xed, 0x77, + 0x58, 0xfd, 0xf3, 0xbd, 0x5c, 0xa7, 0x44, 0x4c, 0x2c, 0xf5, 0x19, 0xbc, 0xfd, 0x1b, 0x00, 0x00, + 0xff, 0xff, 0x9e, 0x2a, 0x4b, 0x16, 0x88, 0x04, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -662,6 +671,16 @@ func (m *MsgBuy) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.AdvancePurchase { + i-- + if m.AdvancePurchase { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x38 + } if m.AutoRenewal { i-- if m.AutoRenewal { @@ -945,6 +964,9 @@ func (m *MsgBuy) Size() (n int) { if m.AutoRenewal { n += 2 } + if m.AdvancePurchase { + n += 2 + } return n } @@ -1202,6 +1224,26 @@ func (m *MsgBuy) Unmarshal(dAtA []byte) error { } } m.AutoRenewal = bool(v != 0) + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field AdvancePurchase", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.AdvancePurchase = bool(v != 0) default: iNdEx = preIndex skippy, err := skipTx(dAtA[iNdEx:]) diff --git a/x/subscription/types/types.go b/x/subscription/types/types.go index 4d185e209b..5dce2d01b0 100644 --- a/x/subscription/types/types.go +++ b/x/subscription/types/types.go @@ -2,6 +2,8 @@ package types const ( BuySubscriptionEventName = "buy_subscription_event" + AdvancedBuySubscriptionEventName = "advanced_buy_subscription_event" + AdvancedBuyUpgradeSubscriptionEventName = "advanced_buy_upgrade_subscription_event" UpgradeSubscriptionEventName = "upgrade_subscription_event" ExpireSubscriptionEventName = "expire_subscription_event" AddProjectEventName = "add_project_to_subscription_event"