diff --git a/app/upgrade.go b/app/upgrade.go index 92b78f462..745c8acb3 100644 --- a/app/upgrade.go +++ b/app/upgrade.go @@ -37,6 +37,7 @@ func (app *App) RegisterUpgradeHandlers(chainID string, serverCfg *serverconfig. app.registerVeldUpgradeHandler() app.registerMongolianUpgradeHandler() app.registerAltaiUpgradeHandler() + app.registerSavannaUpgradeHandler() // app.register...() // ... return nil @@ -313,3 +314,19 @@ func (app *App) registerAltaiUpgradeHandler() { return nil }) } + +func (app *App) registerSavannaUpgradeHandler() { + // Register the upgrade handler + app.UpgradeKeeper.SetUpgradeHandler(upgradetypes.Savanna, + func(ctx sdk.Context, plan upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) { + app.Logger().Info("upgrade to ", plan.Name) + return app.mm.RunMigrations(ctx, app.configurator, fromVM) + }) + + // Register the upgrade initializer + app.UpgradeKeeper.SetUpgradeInitializer(upgradetypes.Savanna, + func() error { + app.Logger().Info("Init Savanna upgrade") + return nil + }) +} diff --git a/deployment/localup/localup.sh b/deployment/localup/localup.sh index d3c7221bb..b4ad2b8ca 100644 --- a/deployment/localup/localup.sh +++ b/deployment/localup/localup.sh @@ -182,6 +182,7 @@ function generate_genesis() { echo -e '[[upgrade]]\nname = "Erdos"\nheight = 25\ninfo = ""' >> ${workspace}/.local/validator${i}/config/app.toml echo -e '[[upgrade]]\nname = "Veld"\nheight = 26\ninfo = ""' >> ${workspace}/.local/validator${i}/config/app.toml echo -e '[[upgrade]]\nname = "Mongolian"\nheight = 27\ninfo = ""' >> ${workspace}/.local/validator${i}/config/app.toml + echo -e '[[upgrade]]\nname = "Savanna"\nheight = 28\ninfo = ""' >> ${workspace}/.local/validator${i}/config/app.toml done # enable swagger API for validator0 diff --git a/e2e/tests/payment_test.go b/e2e/tests/payment_test.go index 575f32206..70cbae5dc 100644 --- a/e2e/tests/payment_test.go +++ b/e2e/tests/payment_test.go @@ -2170,6 +2170,134 @@ func (s *PaymentTestSuite) TestDiscontinue_MultiBuckets() { s.Require().True(streamRecordsAfter.User.LockBalance.IsZero()) } +func (s *PaymentTestSuite) TestDeposit_FrozenAccount_NetflowIsZero() { + defer s.revertParams() + + ctx := context.Background() + sp := s.PickStorageProvider() + gvg, found := sp.GetFirstGlobalVirtualGroup() + s.Require().True(found) + queryFamilyResponse, err := s.Client.GlobalVirtualGroupFamily(ctx, &virtualgrouptypes.QueryGlobalVirtualGroupFamilyRequest{ + FamilyId: gvg.FamilyId, + }) + s.Require().NoError(err) + family := queryFamilyResponse.GlobalVirtualGroupFamily + user := s.GenAndChargeAccounts(1, 1000000)[0] + + streamAddresses := []string{ + user.GetAddr().String(), + family.VirtualPaymentAddress, + gvg.VirtualPaymentAddress, + paymenttypes.ValidatorTaxPoolAddress.String(), + } + + // update params + params := s.queryParams() + params.VersionedParams.ReserveTime = 8 + params.ForcedSettleTime = 5 + s.updateParams(params) + + // create bucket + bucketName := s.createBucket(sp, gvg, user, 0) + + // create & seal objects + for i := 0; i < 2; i++ { + _, _, objectName1, objectId1, checksums1, _ := s.createObject(user, bucketName, false) + s.sealObject(sp, gvg, bucketName, objectName1, objectId1, checksums1) + queryHeadObjectRequest := storagetypes.QueryHeadObjectRequest{ + BucketName: bucketName, + ObjectName: objectName1, + } + queryHeadObjectResponse, err := s.Client.HeadObject(ctx, &queryHeadObjectRequest) + s.Require().NoError(err) + s.Require().Equal(queryHeadObjectResponse.ObjectInfo.ObjectStatus, storagetypes.OBJECT_STATUS_SEALED) + time.Sleep(200 * time.Millisecond) + } + + // transfer out all balance + queryBalanceRequest := banktypes.QueryBalanceRequest{Denom: s.Config.Denom, Address: user.GetAddr().String()} + queryBalanceResponse, err := s.Client.BankQueryClient.Balance(ctx, &queryBalanceRequest) + s.Require().NoError(err) + + msgSend := banktypes.NewMsgSend(user.GetAddr(), core.GenRandomAddr(), sdk.NewCoins( + sdk.NewCoin(s.Config.Denom, queryBalanceResponse.Balance.Amount.SubRaw(5*types.DecimalGwei)), + )) + + simulateResponse := s.SimulateTx(msgSend, user) + gasLimit := simulateResponse.GasInfo.GetGasUsed() + gasPrice, err := sdk.ParseCoinNormalized(simulateResponse.GasInfo.GetMinGasPrice()) + s.Require().NoError(err) + + msgSend.Amount = sdk.NewCoins( + sdk.NewCoin(s.Config.Denom, queryBalanceResponse.Balance.Amount.Sub(gasPrice.Amount.Mul(sdk.NewInt(int64(gasLimit))))), + ) + s.SendTxBlock(user, msgSend) + queryBalanceResponse, err = s.Client.BankQueryClient.Balance(ctx, &queryBalanceRequest) + s.Require().NoError(err) + s.Require().Equal(int64(0), queryBalanceResponse.Balance.Amount.Int64()) + + // wait account to be frozen + time.Sleep(8 * time.Second) + streamRecord := s.getStreamRecord(user.GetAddr().String()) + s.Require().True(streamRecord.Status == paymenttypes.STREAM_ACCOUNT_STATUS_FROZEN) + s.Require().True(streamRecord.NetflowRate.IsZero()) + s.Require().True(streamRecord.FrozenNetflowRate.IsNegative()) + + // force delete bucket + msgDiscontinueBucket := storagetypes.NewMsgDiscontinueBucket(sp.GcKey.GetAddr(), bucketName, "test") + txRes := s.SendTxBlock(sp.GcKey, msgDiscontinueBucket) + deleteAt := filterDiscontinueBucketEventFromTx(txRes).DeleteAt + + for { + time.Sleep(200 * time.Millisecond) + statusRes, err := s.TmClient.TmClient.Status(context.Background()) + s.Require().NoError(err) + blockTime := statusRes.SyncInfo.LatestBlockTime.Unix() + + s.T().Logf("current blockTime: %d, delete blockTime: %d", blockTime, deleteAt) + + if blockTime > deleteAt { + break + } + } + + _, err = s.Client.HeadBucket(ctx, &storagetypes.QueryHeadBucketRequest{BucketName: bucketName}) + s.Require().ErrorContains(err, "No such bucket") + streamRecordsAfter := s.getStreamRecords(streamAddresses) + s.Require().True(streamRecordsAfter.User.NetflowRate.IsZero()) + s.Require().True(streamRecordsAfter.User.FrozenNetflowRate.IsZero()) + s.Require().True(streamRecordsAfter.User.LockBalance.IsZero()) + s.Require().True(streamRecordsAfter.User.StaticBalance.IsZero()) + s.Require().True(streamRecordsAfter.User.BufferBalance.IsZero()) + + // deposit payment account + helper := s.GenAndChargeAccounts(1, 1000000)[0] + msgSend = banktypes.NewMsgSend(helper.GetAddr(), user.GetAddr(), sdk.NewCoins( + sdk.NewCoin(s.Config.Denom, sdk.NewInt(2e18)), + )) + s.SendTxBlock(helper, msgSend) + queryBalanceResponse, err = s.Client.BankQueryClient.Balance(ctx, &queryBalanceRequest) + s.Require().NoError(err) + + msgDeposit := &paymenttypes.MsgDeposit{ + Creator: user.GetAddr().String(), + To: user.GetAddr().String(), + Amount: sdk.NewInt(1e18), + } + _ = s.SendTxBlock(user, msgDeposit) + streamRecordsAfter = s.getStreamRecords(streamAddresses) + s.Require().True(streamRecordsAfter.User.Status == paymenttypes.STREAM_ACCOUNT_STATUS_ACTIVE) + s.Require().True(streamRecordsAfter.User.StaticBalance.Equal(sdk.NewInt(1e18))) + s.Require().True(streamRecordsAfter.User.SettleTimestamp == 0) + + // create bucket with quota again + bucketName = s.createBucket(sp, gvg, user, 100) + streamRecordsAfter = s.getStreamRecords(streamAddresses) + s.Require().True(streamRecordsAfter.User.StaticBalance.LT(sdk.NewInt(1e18))) + s.Require().True(streamRecordsAfter.User.BufferBalance.GT(sdk.NewInt(0))) + s.Require().True(streamRecordsAfter.User.SettleTimestamp > 0) +} + func TestPaymentTestSuite(t *testing.T) { suite.Run(t, new(PaymentTestSuite)) } diff --git a/go.mod b/go.mod index f41d2fc61..1371b07c8 100644 --- a/go.mod +++ b/go.mod @@ -176,7 +176,7 @@ replace ( github.com/cometbft/cometbft => github.com/bnb-chain/greenfield-cometbft v1.3.0 github.com/cometbft/cometbft-db => github.com/bnb-chain/greenfield-cometbft-db v0.8.1-alpha.1 github.com/confio/ics23/go => github.com/cosmos/cosmos-sdk/ics23/go v0.8.0 - github.com/cosmos/cosmos-sdk => github.com/bnb-chain/greenfield-cosmos-sdk v1.9.2 + github.com/cosmos/cosmos-sdk => github.com/bnb-chain/greenfield-cosmos-sdk v1.9.3-0.20241106114330-708c5e70e9f3 github.com/cosmos/iavl => github.com/bnb-chain/greenfield-iavl v0.20.1 github.com/syndtr/goleveldb => github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 github.com/wercker/journalhook => github.com/wercker/journalhook v0.0.0-20230927020745-64542ffa4117 diff --git a/go.sum b/go.sum index d34773df7..aa6ef6509 100644 --- a/go.sum +++ b/go.sum @@ -163,8 +163,8 @@ github.com/bnb-chain/greenfield-cometbft v1.3.0 h1:v3nZ16ledTZGF5Csys7fTQGZcEV78 github.com/bnb-chain/greenfield-cometbft v1.3.0/go.mod h1:0D+VPivZTeBldjtGGi9LKbBnKEO/RtMRJikie92LkYI= github.com/bnb-chain/greenfield-cometbft-db v0.8.1-alpha.1 h1:XcWulGacHVRiSCx90Q8Y//ajOrLNBQWR/KDB89dy3cU= github.com/bnb-chain/greenfield-cometbft-db v0.8.1-alpha.1/go.mod h1:ey1CiK4bYo1RBNJLRiVbYr5CMdSxci9S/AZRINLtppI= -github.com/bnb-chain/greenfield-cosmos-sdk v1.9.2 h1:hqZ4v4M3nkBNs1dXbAQkMBrNQuP9maQOSU+rBwzAemQ= -github.com/bnb-chain/greenfield-cosmos-sdk v1.9.2/go.mod h1:2bwmwdXYBISnQoMwgAcZTVGt21lMsHZSeeeMByTvDlQ= +github.com/bnb-chain/greenfield-cosmos-sdk v1.9.3-0.20241106114330-708c5e70e9f3 h1:76nYX3W9tcS4tGhk3kvVAkosQfDoaYiZZu5sKXY3nz0= +github.com/bnb-chain/greenfield-cosmos-sdk v1.9.3-0.20241106114330-708c5e70e9f3/go.mod h1:2bwmwdXYBISnQoMwgAcZTVGt21lMsHZSeeeMByTvDlQ= github.com/bnb-chain/greenfield-cosmos-sdk/api v0.0.0-20230816082903-b48770f5e210 h1:GHPbV2bC+gmuO6/sG0Tm8oGal3KKSRlyE+zPscDjlA8= github.com/bnb-chain/greenfield-cosmos-sdk/api v0.0.0-20230816082903-b48770f5e210/go.mod h1:vhsZxXE9tYJeYB5JR4hPhd6Pc/uPf7j1T8IJ7p9FdeM= github.com/bnb-chain/greenfield-cosmos-sdk/math v0.0.0-20230816082903-b48770f5e210 h1:FLVOn4+OVbsKi2+YJX5kmD27/4dRu4FW7xCXFhzDO5s= diff --git a/x/payment/keeper/stream_record.go b/x/payment/keeper/stream_record.go index 1fb01a63e..eeba9772d 100644 --- a/x/payment/keeper/stream_record.go +++ b/x/payment/keeper/stream_record.go @@ -380,8 +380,18 @@ func (k Keeper) TryResumeStreamRecord(ctx sdk.Context, streamRecord *types.Strea reserveTime := params.VersionedParams.ReserveTime forcedSettleTime := params.ForcedSettleTime + now := ctx.BlockTime().Unix() totalRate := streamRecord.NetflowRate.Add(streamRecord.FrozenNetflowRate) streamRecord.StaticBalance = streamRecord.StaticBalance.Add(depositBalance) + + if totalRate.IsZero() && ctx.IsUpgraded(upgradetypes.Savanna) { + streamRecord.Status = types.STREAM_ACCOUNT_STATUS_ACTIVE + streamRecord.CrudTimestamp = now + streamRecord.SettleTimestamp = 0 + k.SetStreamRecord(ctx, streamRecord) + return nil + } + expectedBalanceToResume := totalRate.Neg().Mul(sdkmath.NewIntFromUint64(reserveTime)) if streamRecord.StaticBalance.LT(expectedBalanceToResume) { // deposit balance is not enough to resume, only add static balance @@ -389,7 +399,6 @@ func (k Keeper) TryResumeStreamRecord(ctx sdk.Context, streamRecord *types.Strea return nil } - now := ctx.BlockTime().Unix() prevSettleTime := streamRecord.SettleTimestamp streamRecord.SettleTimestamp = now + streamRecord.StaticBalance.Quo(totalRate.Abs()).Int64() - int64(forcedSettleTime) streamRecord.BufferBalance = expectedBalanceToResume