diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 8570db3e..f3ae6c9a 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -13,7 +13,7 @@ on: env: GreenfieldTag: v1.0.0 - GreenfieldStorageProviderTag: v0.2.6 + GreenfieldStorageProviderTag: fix-bucket-quota-dev GOPRIVATE: github.com/bnb-chain GH_ACCESS_TOKEN: ${{ secrets.GH_TOKEN }} MYSQL_USER: root diff --git a/client/api_bucket.go b/client/api_bucket.go index f33db4f0..786ed6a3 100644 --- a/client/api_bucket.go +++ b/client/api_bucket.go @@ -6,6 +6,7 @@ import ( "encoding/xml" "errors" "fmt" + ctypes "github.com/cometbft/cometbft/rpc/core/types" "io" "math" "net/http" @@ -21,7 +22,6 @@ import ( storageTypes "github.com/bnb-chain/greenfield/x/storage/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/tx" - govTypes "github.com/cosmos/cosmos-sdk/x/gov/types" "github.com/rs/zerolog/log" "github.com/bnb-chain/greenfield-go-sdk/pkg/utils" @@ -51,7 +51,7 @@ type IBucketClient interface { ListBucketsByBucketID(ctx context.Context, bucketIds []uint64, opts types.EndPointOptions) (types.ListBucketsByBucketIDResponse, error) GetMigrateBucketApproval(ctx context.Context, migrateBucketMsg *storageTypes.MsgMigrateBucket) (*storageTypes.MsgMigrateBucket, error) MigrateBucket(ctx context.Context, bucketName string, dstPrimarySPID uint32, opts types.MigrateBucketOptions) (string, error) - CancelMigrateBucket(ctx context.Context, bucketName string, opts types.CancelMigrateBucketOptions) (uint64, string, error) + CancelMigrateBucket(ctx context.Context, bucketName string, opts types.CancelMigrateBucketOptions) (string, error) ListBucketsByPaymentAccount(ctx context.Context, paymentAccount string, opts types.ListBucketsByPaymentAccountOptions) (types.ListBucketsByPaymentAccountResult, error) } @@ -962,26 +962,46 @@ func (c *Client) MigrateBucket(ctx context.Context, bucketName string, dstPrimar // // - opt: The options of the proposal meta and transaction. // -// - ret1: The proposal ID of canceling migration. -// -// - ret2: Transaction hash return from blockchain. +// - ret1: Transaction hash return from blockchain. // -// - ret3: Return error when the request of cancel migration failed, otherwise return nil. -func (c *Client) CancelMigrateBucket(ctx context.Context, bucketName string, opts types.CancelMigrateBucketOptions) (uint64, string, error) { - govModuleAddress, err := c.GetModuleAccountByName(ctx, govTypes.ModuleName) +// - ret2: Return error when the request of cancel migration failed, otherwise return nil. +func (c *Client) CancelMigrateBucket(ctx context.Context, bucketName string, opts types.CancelMigrateBucketOptions) (string, error) { + + cancelMigrateBucketMsg := storageTypes.NewMsgCancelMigrateBucket(c.MustGetDefaultAccount().GetAddress(), bucketName) + + err := cancelMigrateBucketMsg.ValidateBasic() if err != nil { - return 0, "", err + return "", err } - cancelBucketMsg := storageTypes.NewMsgCancelMigrateBucket( - govModuleAddress.GetAddress(), bucketName, - ) - err = cancelBucketMsg.ValidateBasic() + // set the default txn broadcast mode as block mode + if opts.TxOpts == nil { + broadcastMode := tx.BroadcastMode_BROADCAST_MODE_SYNC + opts.TxOpts = &gnfdsdk.TxOption{Mode: &broadcastMode} + } + + resp, err := c.chainClient.BroadcastTx(ctx, []sdk.Msg{cancelMigrateBucketMsg}, opts.TxOpts) if err != nil { - return 0, "", err + return "", err + } + + var txnResponse *ctypes.ResultTx + txnHash := resp.TxResponse.TxHash + if !opts.IsAsyncMode { + ctxTimeout, cancel := context.WithTimeout(ctx, types.ContextTimeout) + defer cancel() + + txnResponse, err = c.WaitForTx(ctxTimeout, txnHash) + if err != nil { + return txnHash, fmt.Errorf("the transaction has been submitted, please check it later:%v", err) + } + + if txnResponse.TxResult.Code != 0 { + return txnHash, fmt.Errorf("the createBucket txn has failed with response code: %d", txnResponse.TxResult.Code) + } } - return c.SubmitProposal(ctx, []sdk.Msg{cancelBucketMsg}, opts.ProposalDepositAmount, opts.ProposalTitle, opts.ProposalSummary, types.SubmitProposalOptions{Metadata: opts.ProposalMetadata, TxOpts: opts.TxOpts}) + return txnHash, nil } // ListBucketsByPaymentAccount - List bucket info by payment account. diff --git a/e2e/e2e_migrate_bucket_test.go b/e2e/e2e_migrate_bucket_test.go index 5995190b..c1df4f83 100644 --- a/e2e/e2e_migrate_bucket_test.go +++ b/e2e/e2e_migrate_bucket_test.go @@ -189,7 +189,7 @@ func (s *BucketMigrateTestSuite) Test_Bucket_Migrate_Simple_Case() { // selete a storage provider to miragte destSP := s.SelectDestSP(objectDetail) - s.T().Logf(":Migrate Bucket DstPrimarySPID %d", destSP.GetId()) + s.T().Logf("Migrate Bucket DstPrimarySPID %d", destSP.GetId()) // normal no conflict send migrate bucket transaction txhash, err := s.Client.MigrateBucket(s.ClientContext, bucketName, destSP.GetId(), types.MigrateBucketOptions{TxOpts: nil, IsAsyncMode: false}) @@ -209,7 +209,7 @@ func (s *BucketMigrateTestSuite) Test_Bucket_Migrate_Simple_Case() { s.CheckChallenge(uint32(objectDetail.ObjectInfo.Id.Uint64())) } -// test only conflict sp's case +// test conflict sp's case & quota consumed func (s *BucketMigrateTestSuite) Test_Bucket_Migrate_Simple_Conflict_Case() { // 1) create bucket and object in srcSP bucketName, _ := s.MustCreateBucket(storageTypes.VISIBILITY_TYPE_PUBLIC_READ) @@ -244,7 +244,10 @@ func (s *BucketMigrateTestSuite) Test_Bucket_Migrate_Simple_Conflict_Case() { // migrate bucket with conflict conflictSPID := objectDetail.GlobalVirtualGroup.SecondarySpIds[0] - s.T().Logf(":Migrate Bucket DstPrimarySPID %d", conflictSPID) + + quotaBefore, err := s.Client.GetBucketReadQuota(s.ClientContext, bucketName) + s.Require().NoError(err) + s.T().Logf("Migrate bucket %s from %d to DstPrimarySPID %d, current quota:%v", bucketName, objectDetail.GlobalVirtualGroup.PrimarySpId, conflictSPID, quotaBefore) txhash, err := s.Client.MigrateBucket(s.ClientContext, bucketName, conflictSPID, types.MigrateBucketOptions{TxOpts: nil, IsAsyncMode: false}) s.Require().NoError(err) @@ -263,6 +266,14 @@ func (s *BucketMigrateTestSuite) Test_Bucket_Migrate_Simple_Conflict_Case() { time.Sleep(3 * time.Second) } + quotaAfter, err := s.Client.GetBucketReadQuota(s.ClientContext, bucketName) + s.Require().NoError(err) + s.T().Logf("succeed to migrate Bucket, current quota:%v", quotaAfter) + expectQuotaUsed := int(objectDetail.ObjectInfo.PayloadSize) + freeQuotaConsumed := quotaAfter.FreeConsumedSize - quotaBefore.FreeConsumedSize + s.Require().Equal(uint64(expectQuotaUsed), freeQuotaConsumed) + s.Require().Equal(quotaAfter.FreeConsumedSize+quotaAfter.SPFreeReadQuotaSize, quotaBefore.FreeConsumedSize+quotaBefore.SPFreeReadQuotaSize) + family, err := s.Client.QueryVirtualGroupFamily(s.ClientContext, bucketInfo.GlobalVirtualGroupFamilyId) s.Require().NoError(err) s.Require().Equal(family.PrimarySpId, conflictSPID) @@ -305,7 +316,7 @@ func (s *BucketMigrateTestSuite) Test_Empty_Bucket_Migrate_Simple_Case() { } s.Require().NotNil(destSP) - s.T().Logf(":Migrate Bucket DstPrimarySPID %s", destSP.String()) + s.T().Logf("Migrate Bucket DstPrimarySPID %s", destSP.String()) // normal no conflict send migrate bucket transaction txhash, err := s.Client.MigrateBucket(s.ClientContext, bucketName, destSP.GetId(), types.MigrateBucketOptions{TxOpts: nil, IsAsyncMode: false}) @@ -350,3 +361,49 @@ func (s *BucketMigrateTestSuite) CheckChallenge(objectId uint32) bool { return true } + +// test cancel migrate bucket +func (s *BucketMigrateTestSuite) Test_Bucket_Migrate_Cancel_Case() { + // 1) create bucket and object in srcSP + bucketName, _ := s.MustCreateBucket(storageTypes.VISIBILITY_TYPE_PUBLIC_READ) + + // test only one object's case + objectDetails, _, err := s.CreateObjects(bucketName, 1) + s.Require().NoError(err) + + objectDetail := objectDetails[0] + //buffer := contentBuffer[0] + + // selete a storage provider to miragte + destSP := s.SelectDestSP(objectDetail) + + s.T().Logf("Migrate Bucket DstPrimarySPID %d", destSP.GetId()) + quotaBefore, err := s.Client.GetBucketReadQuota(s.ClientContext, bucketName) + s.Require().NoError(err) + s.T().Logf("Migrate bucket %s from %d to DstPrimarySPID %d, current quota:%v", bucketName, objectDetail.GlobalVirtualGroup.PrimarySpId, destSP.GetId(), quotaBefore) + + // normal no conflict send migrate bucket transaction + txhash, err := s.Client.MigrateBucket(s.ClientContext, bucketName, destSP.GetId(), types.MigrateBucketOptions{TxOpts: nil, IsAsyncMode: false}) + s.Require().NoError(err) + s.T().Logf("MigrateBucket txhash : %s", txhash) + createTx, err := s.Client.WaitForTx(s.ClientContext, txhash) + s.Require().NoError(err) + s.T().Log(createTx.TxResult.String()) + + txhash, err = s.Client.CancelMigrateBucket(s.ClientContext, bucketName, types.CancelMigrateBucketOptions{}) + s.Require().NoError(err) + s.T().Logf("CancelMigrateBucket txhash : %s", txhash) + + createTx, err = s.Client.WaitForTx(s.ClientContext, txhash) + s.Require().NoError(err) + s.T().Log(createTx.TxResult.String()) + + txhash, err = s.Client.MigrateBucket(s.ClientContext, bucketName, destSP.GetId(), types.MigrateBucketOptions{TxOpts: nil, IsAsyncMode: false}) + s.Require().NoError(err) + s.T().Logf("MigrateBucket txhash : %s", txhash) + createTx, err = s.Client.WaitForTx(s.ClientContext, txhash) + s.Require().NoError(err) + s.T().Log(createTx.TxResult.String()) + + s.waitUntilBucketMigrateFinish(bucketName, destSP) +} diff --git a/types/option.go b/types/option.go index 8c506aed..35d13beb 100644 --- a/types/option.go +++ b/types/option.go @@ -27,12 +27,8 @@ type MigrateBucketOptions struct { // CancelMigrateBucketOptions indicates the metadata to construct `CancelMigrateBucket` msg of storage module. type CancelMigrateBucketOptions struct { - ProposalDepositAmount math.Int // ProposalDepositAmount defines the amount in wei BNB. - ProposalTitle string // ProposalTitle defines the title for proposal. - ProposalSummary string // ProposalSummary defines the summary for proposal. - ProposalMetadata string // ProposalMetadata defines the metadata for proposal. - TxOpts gnfdsdktypes.TxOption // TxOpts defines the options to customize a transaction. - IsAsyncMode bool // IsAsyncMode indicates whether to create the bucket in asynchronous mode. + TxOpts *gnfdsdktypes.TxOption + IsAsyncMode bool // indicate whether to create the bucket in asynchronous mode } // VoteProposalOptions indicates the metadata to construct `VoteProposal` msg.