Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: fail to resume frozen payment account when netflow is zero #647

Merged
merged 2 commits into from
Nov 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions app/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ func (app *App) RegisterUpgradeHandlers(chainID string, serverCfg *serverconfig.
app.registerVeldUpgradeHandler()
app.registerMongolianUpgradeHandler()
app.registerAltaiUpgradeHandler()
app.registerSavannaUpgradeHandler()
// app.register...()
// ...
return nil
Expand Down Expand Up @@ -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
})
}
1 change: 1 addition & 0 deletions deployment/localup/localup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
134 changes: 134 additions & 0 deletions e2e/tests/payment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,8 @@ func (s *PaymentTestSuite) TestDeposit_ActiveAccount() {
Add(paymentAccountStreamRecordAfter.BufferBalance.Sub(paymentAccountStreamRecord.BufferBalance))
s.Require().Equal(settledBalance.Add(paymentBalanceChange).Int64(), paymentAccountBNBNeeded.Int64())
s.Require().Equal(paymentAccountBNBNeeded.MulRaw(3), settledBalance.Add(paymentAccountStreamRecordAfter.StaticBalance.Add(paymentAccountStreamRecordAfter.BufferBalance)))

_ = s.deleteBucket(user, bucketName)
}

func (s *PaymentTestSuite) TestDeposit_FromBankAccount() {
Expand Down Expand Up @@ -2170,6 +2172,138 @@ 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)
_, 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)

// delete bucket
err = s.deleteBucket(user, bucketName)
s.Require().Error(err)
}

func TestPaymentTestSuite(t *testing.T) {
suite.Run(t, new(PaymentTestSuite))
}
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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=
Expand Down
11 changes: 10 additions & 1 deletion x/payment/keeper/stream_record.go
Original file line number Diff line number Diff line change
Expand Up @@ -380,16 +380,25 @@ 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
k.SetStreamRecord(ctx, streamRecord)
return nil
}

now := ctx.BlockTime().Unix()
prevSettleTime := streamRecord.SettleTimestamp
streamRecord.SettleTimestamp = now + streamRecord.StaticBalance.Quo(totalRate.Abs()).Int64() - int64(forcedSettleTime)
streamRecord.BufferBalance = expectedBalanceToResume
Expand Down
Loading