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: fix the precision issue of storage bill #370

Merged
merged 3 commits into from
Jul 25, 2023
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
7 changes: 1 addition & 6 deletions e2e/tests/payment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2085,12 +2085,7 @@ func (s *PaymentTestSuite) TestStorageBill_SealObject_WithPriceChangeValidatorTa
// assertions
streamRecordsAfter = s.getStreamRecords(streamAddresses)
gvgFamilyRateReadAfter, taxRateReadAfter, userTotalRateReadAfter = s.calculateReadRatesCurrentTimestamp(sp, bucketName)
gvgFamilyRateStore, gvgRateStore, taxRateStore, userTotalRateStore = s.calculateStorageRatesCurrentTimestamp(sp, bucketName, objectName, payloadSize)

gvgFamilyRateStore = gvgFamilyRateStore.MulRaw(2)
gvgRateStore = gvgRateStore.MulRaw(2)
taxRateStore = taxRateStore.MulRaw(2)
userTotalRateStore = userTotalRateStore.MulRaw(2)
gvgFamilyRateStore, gvgRateStore, taxRateStore, userTotalRateStore = s.calculateStorageRatesCurrentTimestamp(sp, bucketName, objectName, payloadSize*2)

s.Require().Equal(streamRecordsAfter.User.StaticBalance, sdkmath.ZeroInt())
s.Require().Equal(streamRecordsAfter.User.LockBalance, sdkmath.ZeroInt())
Expand Down
48 changes: 24 additions & 24 deletions e2e/tests/storage_bill_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@ func (s *PaymentTestSuite) TestStorageBill_CopyObject_WithoutPriceChange() {
})
s.Require().NoError(err)
family := queryFamilyResponse.GlobalVirtualGroupFamily
user0 := s.GenAndChargeAccounts(1, 1000000)[0]
user := s.GenAndChargeAccounts(1, 1000000)[0]

streamAddresses := []string{
user0.GetAddr().String(),
user.GetAddr().String(),
family.VirtualPaymentAddress,
gvg.VirtualPaymentAddress,
paymenttypes.ValidatorTaxPoolAddress.String(),
Expand All @@ -46,11 +46,11 @@ func (s *PaymentTestSuite) TestStorageBill_CopyObject_WithoutPriceChange() {
s.T().Logf("paymentParams %s, err: %v", paymentParams, err)
s.Require().NoError(err)

bucketName := s.createBucket(sp, user0, 0)
bucketName := s.createBucket(sp, user, 0)

// create object with none zero payload size
streamRecordsBefore := s.getStreamRecords(streamAddresses)
_, _, objectName, objectId, checksums, payloadSize := s.createObject(user0, bucketName, false)
_, _, objectName, objectId, checksums, payloadSize := s.createObject(user, bucketName, false)

// assertions
streamRecordsAfter := s.getStreamRecords(streamAddresses)
Expand Down Expand Up @@ -78,7 +78,7 @@ func (s *PaymentTestSuite) TestStorageBill_CopyObject_WithoutPriceChange() {
distBucketName := bucketName
distObjectName := storagetestutils.GenRandomObjectName()

objectIfo, err := s.copyObject(user0, sp, bucketName, objectName, distBucketName, distObjectName)
objectIfo, err := s.copyObject(user, sp, bucketName, objectName, distBucketName, distObjectName)
s.Require().NoError(err)
s.sealObject(sp, gvg, distBucketName, distObjectName, objectIfo.Id, objectIfo.Checksums)
// assertions
Expand All @@ -104,10 +104,10 @@ func (s *PaymentTestSuite) TestStorageBill_CopyObject_WithPriceChange() {
})
s.Require().NoError(err)
family := queryFamilyResponse.GlobalVirtualGroupFamily
user0 := s.GenAndChargeAccounts(1, 1000000)[0]
user := s.GenAndChargeAccounts(1, 1000000)[0]

streamAddresses := []string{
user0.GetAddr().String(),
user.GetAddr().String(),
family.VirtualPaymentAddress,
gvg.VirtualPaymentAddress,
paymenttypes.ValidatorTaxPoolAddress.String(),
Expand All @@ -117,11 +117,11 @@ func (s *PaymentTestSuite) TestStorageBill_CopyObject_WithPriceChange() {
s.T().Logf("paymentParams %s, err: %v", paymentParams, err)
s.Require().NoError(err)

bucketName := s.createBucket(sp, user0, 0)
bucketName := s.createBucket(sp, user, 0)

// create object with none zero payload size
streamRecordsBefore := s.getStreamRecords(streamAddresses)
_, _, objectName, objectId, checksums, payloadSize := s.createObject(user0, bucketName, false)
_, _, objectName, objectId, checksums, payloadSize := s.createObject(user, bucketName, false)

// assertions
streamRecordsAfter := s.getStreamRecords(streamAddresses)
Expand Down Expand Up @@ -160,9 +160,9 @@ func (s *PaymentTestSuite) TestStorageBill_CopyObject_WithPriceChange() {
}
s.SendTxBlock(sp.OperatorKey, msgUpdatePrice)

distBucketName := s.createBucket(sp, user0, 0)
distBucketName := s.createBucket(sp, user, 0)
distObjectName := storagetestutils.GenRandomObjectName()
objectIfo, err := s.copyObject(user0, sp, bucketName, objectName, distBucketName, distObjectName)
objectIfo, err := s.copyObject(user, sp, bucketName, objectName, distBucketName, distObjectName)
s.Require().NoError(err)
s.sealObject(sp, gvg, distBucketName, distObjectName, objectIfo.Id, objectIfo.Checksums)
// assertions
Expand All @@ -182,6 +182,7 @@ func (s *PaymentTestSuite) TestStorageBill_UpdateBucketQuota() {
var err error
ctx := context.Background()
sp := s.PickStorageProvider()
user := s.GenAndChargeAccounts(1, 10)[0]
// recover price
defer s.SetSPPrice(sp, "12.34", "0")
gvg, found := sp.GetFirstGlobalVirtualGroup()
Expand All @@ -191,7 +192,6 @@ func (s *PaymentTestSuite) TestStorageBill_UpdateBucketQuota() {
})
s.Require().NoError(err)
family := queryFamilyResponse.GlobalVirtualGroupFamily
user := s.GenAndChargeAccounts(1, 10)[0]

streamAddresses := []string{
user.GetAddr().String(),
Expand Down Expand Up @@ -320,7 +320,7 @@ func (s *PaymentTestSuite) TestStorageBill_UpdateBucketQuota() {
chargedReadQuota := readQuota * 1024 * 1024
msgUpdateBucketInfo := storagetypes.NewMsgUpdateBucketInfo(
user.GetAddr(), bucketName, &chargedReadQuota, user.GetAddr(), storagetypes.VISIBILITY_TYPE_PRIVATE)

s.reduceBNBBalance(user, s.Validator, sdkmath.NewIntWithDecimal(1, 16))
s.SendTxBlockWithExpectErrorString(msgUpdateBucketInfo, user, "apply user flows list failed")

}
Expand Down Expand Up @@ -495,10 +495,10 @@ func (s *PaymentTestSuite) TestStorageBill_MigrationBucket() {
})
s.Require().NoError(err)
family := queryFamilyResponse.GlobalVirtualGroupFamily
user0 := s.GenAndChargeAccounts(1, 10)[0]
user := s.GenAndChargeAccounts(1, 10)[0]

streamAddresses := []string{
user0.GetAddr().String(),
user.GetAddr().String(),
family.VirtualPaymentAddress,
gvg.VirtualPaymentAddress,
paymenttypes.ValidatorTaxPoolAddress.String(),
Expand All @@ -509,15 +509,15 @@ func (s *PaymentTestSuite) TestStorageBill_MigrationBucket() {
s.T().Logf("paymentParams %s, err: %v", paymentParams, err)
s.Require().NoError(err)

bucketName := s.createBucket(primarySP, user0, 0)
bucketName := s.createBucket(primarySP, user, 0)
bucketInfo, err := s.Client.HeadBucket(context.Background(), &storagetypes.QueryHeadBucketRequest{
BucketName: bucketName,
})
s.Require().NoError(err)

// create object with none zero payload size
streamRecordsBefore := s.getStreamRecords(streamAddresses)
_, _, objectName, objectId, checksums, payloadSize := s.createObject(user0, bucketName, false)
_, _, objectName, objectId, checksums, payloadSize := s.createObject(user, bucketName, false)

// assertions
streamRecordsAfter := s.getStreamRecords(streamAddresses)
Expand Down Expand Up @@ -561,7 +561,7 @@ func (s *PaymentTestSuite) TestStorageBill_MigrationBucket() {
s.Require().NoError(err)
family = queryFamilyResponse.GlobalVirtualGroupFamily
streamAddresses = []string{
user0.GetAddr().String(),
user.GetAddr().String(),
family.VirtualPaymentAddress,
dstGVG.VirtualPaymentAddress,
paymenttypes.ValidatorTaxPoolAddress.String(),
Expand All @@ -574,8 +574,8 @@ func (s *PaymentTestSuite) TestStorageBill_MigrationBucket() {
s.Require().NoError(err)

// MigrationBucket
msgMigrationBucket, msgCompleteMigrationBucket := s.NewMigrateBucket(primarySP, dstPrimarySP, user0, bucketName, gvg.FamilyId, dstGVG.FamilyId, bucketInfo.BucketInfo.Id)
s.SendTxBlock(user0, msgMigrationBucket)
msgMigrationBucket, msgCompleteMigrationBucket := s.NewMigrateBucket(primarySP, dstPrimarySP, user, bucketName, gvg.FamilyId, dstGVG.FamilyId, bucketInfo.BucketInfo.Id)
s.SendTxBlock(user, msgMigrationBucket)
s.Require().NoError(err)

// complete MigrationBucket
Expand Down Expand Up @@ -604,19 +604,19 @@ func (s *PaymentTestSuite) TestStorageBill_MigrationBucket() {
s.Require().NoError(err)
streamRecordsBefore = s.getStreamRecords(streamAddresses0)
// send msgMigrationBucket
msgMigrationBucket, msgCompleteMigrationBucket = s.NewMigrateBucket(dstPrimarySP, primarySP, user0, bucketName, dstGVG.FamilyId, gvg.FamilyId, bucketInfo.BucketInfo.Id)
msgMigrationBucket, msgCompleteMigrationBucket = s.NewMigrateBucket(dstPrimarySP, primarySP, user, bucketName, dstGVG.FamilyId, gvg.FamilyId, bucketInfo.BucketInfo.Id)

s.SendTxBlock(user0, msgMigrationBucket)
s.SendTxBlock(user, msgMigrationBucket)
s.Require().NoError(err)
s.reduceBNBBalance(user0, s.Validator, sdkmath.NewIntWithDecimal(1, 1))
s.reduceBNBBalance(user, s.Validator, sdkmath.NewIntWithDecimal(1, 1))

s.SendTxBlockWithExpectErrorString(msgCompleteMigrationBucket, primarySP.OperatorKey, "apply stream record changes for user failed")

s.SetSPPrice(primarySP, "12.3", "13")
readPrice, primaryPrice, secondaryPrice := s.getPrices(primarySP, time.Now().Unix())
s.T().Logf("readPrice: %v, primaryPrice: %v,secondaryPrice: %v", readPrice, primaryPrice, secondaryPrice)

s.transferBNB(s.Validator, user0, sdkmath.NewIntWithDecimal(10000, 18))
s.transferBNB(s.Validator, user, sdkmath.NewIntWithDecimal(10000, 18))

s.SendTxBlock(primarySP.OperatorKey, msgCompleteMigrationBucket)
streamRecordsAfter = s.getStreamRecords(streamAddresses0)
Expand Down
136 changes: 68 additions & 68 deletions x/storage/keeper/payment.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"github.com/bnb-chain/greenfield/x/payment/types"
storagetypes "github.com/bnb-chain/greenfield/x/storage/types"
vgtypes "github.com/bnb-chain/greenfield/x/virtualgroup/types"
)

func (k Keeper) ChargeBucketReadFee(ctx sdk.Context, bucketInfo *storagetypes.BucketInfo,
Expand Down Expand Up @@ -390,51 +391,30 @@ func (k Keeper) ChargeViaObjectChange(ctx sdk.Context, bucketInfo *storagetypes.
}
}

// primary sp
primaryRate := price.PrimaryStorePrice.MulInt(sdkmath.NewIntFromUint64(chargeSize)).TruncateInt()
if primaryRate.IsPositive() {
userFlows.Flows = append(userFlows.Flows, types.OutFlow{
ToAddress: gvgFamily.VirtualPaymentAddress,
Rate: primaryRate,
})
}

//secondary sp
gvg, found := k.virtualGroupKeeper.GetGVG(ctx, lvg.GlobalVirtualGroupId)
if !found {
return fmt.Errorf("get GVG failed: %d, %s", lvg.GlobalVirtualGroupId, lvg.String())
}

secondaryRate := price.SecondaryStorePrice.MulInt(sdkmath.NewIntFromUint64(chargeSize)).TruncateInt()
secondaryRate = secondaryRate.MulRaw(int64(len(gvg.SecondarySpIds)))
if secondaryRate.IsPositive() {
userFlows.Flows = append(userFlows.Flows, types.OutFlow{
ToAddress: gvg.VirtualPaymentAddress,
Rate: secondaryRate,
})
}

versionedParams, err := k.paymentKeeper.GetVersionedParamsWithTs(ctx, internalBucketInfo.PriceTime)
if err != nil {
return fmt.Errorf("failed to get validator tax rate: %w, time: %d", err, internalBucketInfo.PriceTime)
}
validatorTaxRate := versionedParams.ValidatorTaxRate.MulInt(primaryRate.Add(secondaryRate)).TruncateInt()
if validatorTaxRate.IsPositive() {
userFlows.Flows = append(userFlows.Flows, types.OutFlow{
ToAddress: types.ValidatorTaxPoolAddress.String(),
Rate: validatorTaxRate,
})
}

if !delete {
preOutFlows := k.calculateLVGStoreBill(ctx, price, versionedParams, gvgFamily, gvg, lvg)
var newOutFlows []types.OutFlow
if !delete { // seal object
internalBucketInfo.TotalChargeSize = internalBucketInfo.TotalChargeSize + chargeSize
lvg.TotalChargeSize = lvg.TotalChargeSize + chargeSize
} else {
newOutFlows = k.calculateLVGStoreBill(ctx, price, versionedParams, gvgFamily, gvg, lvg)
} else { // delete object
internalBucketInfo.TotalChargeSize = internalBucketInfo.TotalChargeSize - chargeSize
lvg.TotalChargeSize = lvg.TotalChargeSize - chargeSize

userFlows.Flows = getNegFlows(userFlows.Flows)
newOutFlows = k.calculateLVGStoreBill(ctx, price, versionedParams, gvgFamily, gvg, lvg)
}

userFlows.Flows = append(userFlows.Flows, getNegFlows(preOutFlows)...)
userFlows.Flows = append(userFlows.Flows, newOutFlows...)
err = k.paymentKeeper.ApplyUserFlowsList(ctx, []types.UserFlows{userFlows})
if err != nil {
ctx.Logger().Error("charge object store fee failed", "err", err.Error())
Expand All @@ -444,18 +424,55 @@ func (k Keeper) ChargeViaObjectChange(ctx sdk.Context, bucketInfo *storagetypes.
return nil
}

func (k Keeper) calculateLVGStoreBill(ctx sdk.Context, price types.StoragePrice, params types.VersionedParams,
gvgFamily *vgtypes.GlobalVirtualGroupFamily, gvg *vgtypes.GlobalVirtualGroup, lvg *storagetypes.LocalVirtualGroup) []types.OutFlow {
outFlows := make([]types.OutFlow, 0)

// primary sp
primaryStoreFlowRate := price.PrimaryStorePrice.MulInt(sdkmath.NewIntFromUint64(lvg.TotalChargeSize)).TruncateInt()
if primaryStoreFlowRate.IsPositive() {
outFlows = append(outFlows, types.OutFlow{
ToAddress: gvgFamily.VirtualPaymentAddress,
Rate: primaryStoreFlowRate,
})
}

//secondary sp
secondaryStoreFlowRate := price.SecondaryStorePrice.MulInt(sdkmath.NewIntFromUint64(lvg.TotalChargeSize)).TruncateInt()
secondaryStoreFlowRate = secondaryStoreFlowRate.MulRaw(int64(len(gvg.SecondarySpIds)))
if secondaryStoreFlowRate.IsPositive() {
outFlows = append(outFlows, types.OutFlow{
ToAddress: gvg.VirtualPaymentAddress,
Rate: secondaryStoreFlowRate,
})
}

validatorTaxStoreFlowRate := params.ValidatorTaxRate.MulInt(primaryStoreFlowRate.Add(secondaryStoreFlowRate)).TruncateInt()
if validatorTaxStoreFlowRate.IsPositive() {
outFlows = append(outFlows, types.OutFlow{
ToAddress: types.ValidatorTaxPoolAddress.String(),
Rate: validatorTaxStoreFlowRate,
})
}

return outFlows
}

func (k Keeper) GetBucketReadStoreBill(ctx sdk.Context, bucketInfo *storagetypes.BucketInfo,
internalBucketInfo *storagetypes.InternalBucketInfo) (userFlows types.UserFlows, err error) {
userFlows.From = sdk.MustAccAddressFromHex(bucketInfo.PaymentAddress)

if internalBucketInfo.TotalChargeSize == 0 && bucketInfo.ChargedReadQuota == 0 {
return userFlows, nil
}

// calculate read fee & store fee separately, for precision
// calculate read fee
gvgFamily, found := k.virtualGroupKeeper.GetGVGFamily(ctx, bucketInfo.GlobalVirtualGroupFamilyId)
if !found {
return userFlows, fmt.Errorf("get GVG family failed: %d", bucketInfo.GlobalVirtualGroupFamilyId)
}

if internalBucketInfo.TotalChargeSize == 0 && bucketInfo.ChargedReadQuota == 0 {
return userFlows, nil
}
price, err := k.paymentKeeper.GetStoragePrice(ctx, types.StoragePriceParams{
PrimarySp: gvgFamily.PrimarySpId,
PriceTime: internalBucketInfo.PriceTime,
Expand All @@ -464,55 +481,38 @@ func (k Keeper) GetBucketReadStoreBill(ctx sdk.Context, bucketInfo *storagetypes
return userFlows, fmt.Errorf("get storage price failed: %w", err)
}

// primary sp total rate
primaryTotalFlowRate := price.ReadPrice.MulInt(sdkmath.NewIntFromUint64(bucketInfo.ChargedReadQuota)).TruncateInt()

// secondary sp total rate
secondaryTotalFlowRate := sdk.ZeroInt()

for _, lvg := range internalBucketInfo.LocalVirtualGroups {
// primary sp
primaryRate := price.PrimaryStorePrice.MulInt(sdkmath.NewIntFromUint64(lvg.TotalChargeSize)).TruncateInt()
if primaryRate.IsPositive() {
primaryTotalFlowRate = primaryTotalFlowRate.Add(primaryRate)
}

//secondary sp
gvg, found := k.virtualGroupKeeper.GetGVG(ctx, lvg.GlobalVirtualGroupId)
if !found {
return userFlows, fmt.Errorf("get GVG failed: %d, %s", lvg.GlobalVirtualGroupId, lvg.String())
}

secondaryRate := price.SecondaryStorePrice.MulInt(sdkmath.NewIntFromUint64(lvg.TotalChargeSize)).TruncateInt()
secondaryRate = secondaryRate.MulRaw(int64(len(gvg.SecondarySpIds)))
if secondaryRate.IsPositive() {
userFlows.Flows = append(userFlows.Flows, types.OutFlow{
ToAddress: gvg.VirtualPaymentAddress,
Rate: secondaryRate,
})
secondaryTotalFlowRate = secondaryTotalFlowRate.Add(secondaryRate)
}
}

if primaryTotalFlowRate.IsPositive() {
primaryReadFlowRate := price.ReadPrice.MulInt(sdkmath.NewIntFromUint64(bucketInfo.ChargedReadQuota)).TruncateInt()
if primaryReadFlowRate.IsPositive() {
userFlows.Flows = append(userFlows.Flows, types.OutFlow{
ToAddress: gvgFamily.VirtualPaymentAddress,
Rate: primaryTotalFlowRate,
Rate: primaryReadFlowRate,
})
}

versionedParams, err := k.paymentKeeper.GetVersionedParamsWithTs(ctx, internalBucketInfo.PriceTime)
if err != nil {
return userFlows, fmt.Errorf("failed to get validator tax rate: %w, time: %d", err, internalBucketInfo.PriceTime)
}
validatorTaxRate := versionedParams.ValidatorTaxRate.MulInt(primaryTotalFlowRate.Add(secondaryTotalFlowRate)).TruncateInt()
if validatorTaxRate.IsPositive() {
validatorTaxReadFlowRate := versionedParams.ValidatorTaxRate.MulInt(primaryReadFlowRate).TruncateInt()
if validatorTaxReadFlowRate.IsPositive() {
userFlows.Flows = append(userFlows.Flows, types.OutFlow{
ToAddress: types.ValidatorTaxPoolAddress.String(),
Rate: validatorTaxRate,
Rate: validatorTaxReadFlowRate,
})
}

// calculate store fee
// be noted, here we split the fee calculation for each lvg, to make sure each lvg's calculation is precise
for _, lvg := range internalBucketInfo.LocalVirtualGroups {
//secondary sp
gvg, found := k.virtualGroupKeeper.GetGVG(ctx, lvg.GlobalVirtualGroupId)
if !found {
return userFlows, fmt.Errorf("get GVG failed: %d, %s", lvg.GlobalVirtualGroupId, lvg.String())
}
outFlows := k.calculateLVGStoreBill(ctx, price, versionedParams, gvgFamily, gvg, lvg)
userFlows.Flows = append(userFlows.Flows, outFlows...)
}

return userFlows, nil
}

Expand Down
Loading
Loading