diff --git a/.testcoverage.yml b/.testcoverage.yml index 052ad1dc8..5a675d980 100644 --- a/.testcoverage.yml +++ b/.testcoverage.yml @@ -33,13 +33,16 @@ exclude: - \.pb\.go$ # excludes all protobuf generated files - \.pb\.gw\.go$ # excludes all protobuf generated files - .*_mocks.go$ # excludes all protobuf generated files + - types/.* + - simulation/.* - testutil/.* - e2e/.* - - types/.* + - .*/cli/.* - sdk/.* + - version/.* + - cmd/gnfd/.* - internal/sequence/.* - - x/types/.* - - .*/simulation/.* + - x/gensp/.* - .*/module.go - .*/module_simulation.go diff --git a/app/ante/ante_test.go b/app/ante/ante_test.go index 1cb1dc1b0..410e6d84b 100644 --- a/app/ante/ante_test.go +++ b/app/ante/ante_test.go @@ -33,7 +33,7 @@ func (suite *AnteTestSuite) TestAnteHandler() { bz, _ := hex.DecodeString(test.TEST_PUBKEY) faucetPubKey := ðsecp256k1.PubKey{Key: bz} err := suite.app.BankKeeper.SendCoins(suite.ctx, faucetPubKey.Address().Bytes(), acc.GetAddress(), sdk.Coins{sdk.Coin{ - Denom: sdk.DefaultBondDenom, + Denom: test.TEST_TOKEN_NAME, Amount: sdk.NewInt(100000000000000), }}) if err != nil { @@ -53,8 +53,8 @@ func (suite *AnteTestSuite) TestAnteHandler() { "success - DeliverTx EIP712 signed Cosmos Tx MsgSend", func() sdk.Tx { gas := uint64(12e3) - fee := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewIntFromUint64(gas))) - txBuilder := suite.CreateTestEIP712TxBuilderMsgSend(addr, privKey, "greenfield_9000-1", gas, fee) + fee := sdk.NewCoins(sdk.NewCoin(test.TEST_TOKEN_NAME, sdk.NewIntFromUint64(gas))) + txBuilder := suite.CreateTestEIP712TxBuilderMsgSend(addr, privKey, test.TEST_CHAIN_ID, gas, fee) return txBuilder.GetTx() }, true, false, true, }, @@ -62,8 +62,8 @@ func (suite *AnteTestSuite) TestAnteHandler() { "success - DeliverTx EIP712 signed Cosmos Tx MsgDelegate", func() sdk.Tx { gas := uint64(12e3) - fee := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewIntFromUint64(gas))) - txBuilder := suite.CreateTestEIP712TxBuilderMsgDelegate(addr, privKey, "greenfield_9000-1", gas, fee) + fee := sdk.NewCoins(sdk.NewCoin(test.TEST_TOKEN_NAME, sdk.NewIntFromUint64(gas))) + txBuilder := suite.CreateTestEIP712TxBuilderMsgDelegate(addr, privKey, test.TEST_CHAIN_ID, gas, fee) return txBuilder.GetTx() }, true, false, true, }, @@ -71,8 +71,8 @@ func (suite *AnteTestSuite) TestAnteHandler() { "success - DeliverTx EIP712 signed Cosmos Tx MsgCreateValidator", func() sdk.Tx { gas := uint64(2e8) - fee := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewIntFromUint64(gas))) - txBuilder := suite.CreateTestEIP712MsgCreateValidator(addr, privKey, "greenfield_9000-1", gas, fee) + fee := sdk.NewCoins(sdk.NewCoin(test.TEST_TOKEN_NAME, sdk.NewIntFromUint64(gas))) + txBuilder := suite.CreateTestEIP712MsgCreateValidator(addr, privKey, test.TEST_CHAIN_ID, gas, fee) return txBuilder.GetTx() }, true, false, true, }, @@ -80,8 +80,8 @@ func (suite *AnteTestSuite) TestAnteHandler() { "success - DeliverTx EIP712 signed Cosmos Tx MsgGrantAllowance", func() sdk.Tx { gas := uint64(16e3) - fee := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewIntFromUint64(gas))) - txBuilder := suite.CreateTestEIP712GrantAllowance(addr, privKey, "greenfield_9000-1", gas, fee) + fee := sdk.NewCoins(sdk.NewCoin(test.TEST_TOKEN_NAME, sdk.NewIntFromUint64(gas))) + txBuilder := suite.CreateTestEIP712GrantAllowance(addr, privKey, test.TEST_CHAIN_ID, gas, fee) return txBuilder.GetTx() }, true, false, true, }, @@ -89,8 +89,8 @@ func (suite *AnteTestSuite) TestAnteHandler() { "success - DeliverTx EIP712 signed Cosmos Tx MsgEditValidator", func() sdk.Tx { gas := uint64(2e7) - fee := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewIntFromUint64(gas))) - txBuilder := suite.CreateTestEIP712MsgEditValidator(addr, privKey, "greenfield_9000-1", gas, fee) + fee := sdk.NewCoins(sdk.NewCoin(test.TEST_TOKEN_NAME, sdk.NewIntFromUint64(gas))) + txBuilder := suite.CreateTestEIP712MsgEditValidator(addr, privKey, test.TEST_CHAIN_ID, gas, fee) return txBuilder.GetTx() }, true, false, true, }, @@ -98,8 +98,8 @@ func (suite *AnteTestSuite) TestAnteHandler() { "success - DeliverTx EIP712 signed Cosmos Tx MsgSubmitProposalV1", func() sdk.Tx { gas := uint64(2e8) - fee := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewIntFromUint64(gas))) - txBuilder := suite.CreateTestEIP712TxBuilderMsgSubmitProposalV1(addr, privKey, "greenfield_9000-1", gas, fee) + fee := sdk.NewCoins(sdk.NewCoin(test.TEST_TOKEN_NAME, sdk.NewIntFromUint64(gas))) + txBuilder := suite.CreateTestEIP712TxBuilderMsgSubmitProposalV1(addr, privKey, test.TEST_CHAIN_ID, gas, fee) return txBuilder.GetTx() }, true, false, true, }, @@ -107,8 +107,8 @@ func (suite *AnteTestSuite) TestAnteHandler() { "success - DeliverTx EIP712 signed Cosmos Tx MsgGrant", func() sdk.Tx { gas := uint64(16e3) - fee := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewIntFromUint64(gas))) - txBuilder := suite.CreateTestEIP712TxBuilderMsgGrant(addr, privKey, "greenfield_9000-1", gas, fee) + fee := sdk.NewCoins(sdk.NewCoin(test.TEST_TOKEN_NAME, sdk.NewIntFromUint64(gas))) + txBuilder := suite.CreateTestEIP712TxBuilderMsgGrant(addr, privKey, test.TEST_CHAIN_ID, gas, fee) return txBuilder.GetTx() }, true, false, true, }, @@ -116,9 +116,9 @@ func (suite *AnteTestSuite) TestAnteHandler() { "fails - DeliverTx legacy msg MsgSubmitProposal v1beta", func() sdk.Tx { gas := uint64(2000000000) - fee := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewIntFromUint64(gas))) - deposit := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewIntFromUint64(20))) - txBuilder := suite.CreateTestEIP712SubmitProposal(addr, privKey, "greenfield_9000-1", gas, fee, deposit) + fee := sdk.NewCoins(sdk.NewCoin(test.TEST_TOKEN_NAME, sdk.NewIntFromUint64(gas))) + deposit := sdk.NewCoins(sdk.NewCoin(test.TEST_TOKEN_NAME, sdk.NewIntFromUint64(20))) + txBuilder := suite.CreateTestEIP712SubmitProposal(addr, privKey, test.TEST_CHAIN_ID, gas, fee, deposit) return txBuilder.GetTx() }, true, false, false, }, @@ -126,8 +126,8 @@ func (suite *AnteTestSuite) TestAnteHandler() { "fails - DeliverTx unregistered msg type MsgSubmitEvidence", func() sdk.Tx { gas := uint64(2000000000) - fee := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewIntFromUint64(gas))) - txBuilder := suite.CreateTestEIP712MsgSubmitEvidence(addr, privKey, "greenfield_9000-1", gas, fee) + fee := sdk.NewCoins(sdk.NewCoin(test.TEST_TOKEN_NAME, sdk.NewIntFromUint64(gas))) + txBuilder := suite.CreateTestEIP712MsgSubmitEvidence(addr, privKey, test.TEST_CHAIN_ID, gas, fee) return txBuilder.GetTx() }, true, false, false, }, @@ -135,7 +135,7 @@ func (suite *AnteTestSuite) TestAnteHandler() { "fails - DeliverTx EIP712 signed Cosmos Tx with wrong Chain ID", func() sdk.Tx { gas := uint64(12e3) - fee := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewIntFromUint64(gas))) + fee := sdk.NewCoins(sdk.NewCoin(test.TEST_TOKEN_NAME, sdk.NewIntFromUint64(gas))) txBuilder := suite.CreateTestEIP712TxBuilderMsgSend(addr, privKey, "ethermint_9002-1", gas, fee) return txBuilder.GetTx() }, true, false, false, @@ -144,10 +144,10 @@ func (suite *AnteTestSuite) TestAnteHandler() { "fails - DeliverTx EIP712 signed Cosmos Tx with different gas fees", func() sdk.Tx { gas := uint64(12e3) - fee := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewIntFromUint64(gas))) - txBuilder := suite.CreateTestEIP712TxBuilderMsgSend(addr, privKey, "greenfield_9000-1", gas, fee) + fee := sdk.NewCoins(sdk.NewCoin(test.TEST_TOKEN_NAME, sdk.NewIntFromUint64(gas))) + txBuilder := suite.CreateTestEIP712TxBuilderMsgSend(addr, privKey, test.TEST_CHAIN_ID, gas, fee) txBuilder.SetGasLimit(uint64(300000)) - txBuilder.SetFeeAmount(sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.NewInt(30)))) + txBuilder.SetFeeAmount(sdk.NewCoins(sdk.NewCoin(test.TEST_TOKEN_NAME, sdkmath.NewInt(30)))) return txBuilder.GetTx() }, true, false, false, }, @@ -155,8 +155,8 @@ func (suite *AnteTestSuite) TestAnteHandler() { "fails - DeliverTx EIP712 signed Cosmos Tx with empty signature", func() sdk.Tx { gas := uint64(12e3) - fee := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewIntFromUint64(gas))) - txBuilder := suite.CreateTestEIP712TxBuilderMsgSend(addr, privKey, "greenfield_9000-1", gas, fee) + fee := sdk.NewCoins(sdk.NewCoin(test.TEST_TOKEN_NAME, sdk.NewIntFromUint64(gas))) + txBuilder := suite.CreateTestEIP712TxBuilderMsgSend(addr, privKey, test.TEST_CHAIN_ID, gas, fee) sigsV2 := signing.SignatureV2{ PubKey: privKey.PubKey(), } @@ -169,8 +169,8 @@ func (suite *AnteTestSuite) TestAnteHandler() { "fails - DeliverTx EIP712 signed Cosmos Tx with invalid sequence", func() sdk.Tx { gas := uint64(12e3) - fee := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewIntFromUint64(gas))) - txBuilder := suite.CreateTestEIP712TxBuilderMsgSend(addr, privKey, "greenfield_9000-1", gas, fee) + fee := sdk.NewCoins(sdk.NewCoin(test.TEST_TOKEN_NAME, sdk.NewIntFromUint64(gas))) + txBuilder := suite.CreateTestEIP712TxBuilderMsgSend(addr, privKey, test.TEST_CHAIN_ID, gas, fee) nonce, err := suite.app.AccountKeeper.GetSequence(suite.ctx, addr) suite.Require().NoError(err) sigsV2 := signing.SignatureV2{ @@ -189,8 +189,8 @@ func (suite *AnteTestSuite) TestAnteHandler() { "fails - DeliverTx EIP712 signed Cosmos Tx with invalid signMode", func() sdk.Tx { gas := uint64(12e3) - fee := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewIntFromUint64(gas))) - txBuilder := suite.CreateTestEIP712TxBuilderMsgSend(addr, privKey, "greenfield_9000-1", gas, fee) + fee := sdk.NewCoins(sdk.NewCoin(test.TEST_TOKEN_NAME, sdk.NewIntFromUint64(gas))) + txBuilder := suite.CreateTestEIP712TxBuilderMsgSend(addr, privKey, test.TEST_CHAIN_ID, gas, fee) nonce, err := suite.app.AccountKeeper.GetSequence(suite.ctx, addr) suite.Require().NoError(err) sigsV2 := signing.SignatureV2{ diff --git a/app/ante/utils_test.go b/app/ante/utils_test.go index 46a8d434d..aa309b1cb 100644 --- a/app/ante/utils_test.go +++ b/app/ante/utils_test.go @@ -1,7 +1,6 @@ package ante_test import ( - "encoding/hex" "encoding/json" "fmt" "math/big" @@ -10,17 +9,11 @@ import ( sdkmath "cosmossdk.io/math" dbm "github.com/cometbft/cometbft-db" - abci "github.com/cometbft/cometbft/abci/types" "github.com/cometbft/cometbft/libs/log" tmproto "github.com/cometbft/cometbft/proto/tendermint/types" - tmtypes "github.com/cometbft/cometbft/types" - "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" - "github.com/cosmos/cosmos-sdk/crypto/keys/eth/ethsecp256k1" storetypes "github.com/cosmos/cosmos-sdk/store/types" - "github.com/cosmos/cosmos-sdk/testutil/mock" - simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" sdk "github.com/cosmos/cosmos-sdk/types" signingtypes "github.com/cosmos/cosmos-sdk/types/tx/signing" authante "github.com/cosmos/cosmos-sdk/x/auth/ante" @@ -43,6 +36,7 @@ import ( "github.com/bnb-chain/greenfield/e2e/core" "github.com/bnb-chain/greenfield/sdk/client/test" "github.com/bnb-chain/greenfield/sdk/keys" + "github.com/bnb-chain/greenfield/testutil" "github.com/bnb-chain/greenfield/testutil/sample" ) @@ -61,10 +55,12 @@ func TestAnteTestSuite(t *testing.T) { func (suite *AnteTestSuite) SetupTest() { var encCfg params.EncodingConfig - suite.app, encCfg, _ = NewApp(baseapp.SetChainID("greenfield_9000-1")) + logger := log.NewNopLogger() + db := dbm.NewMemDB() + suite.app, encCfg, _ = testutil.NewTestApp(logger, db, nil, true, test.TEST_CHAIN_ID) - suite.ctx = suite.app.NewContext(false, tmproto.Header{Height: 2, ChainID: "greenfield_9000-1", Time: time.Now().UTC()}) - suite.ctx = suite.ctx.WithMinGasPrices(sdk.NewDecCoins(sdk.NewDecCoin(sdk.DefaultBondDenom, sdk.OneInt()))) // set to 1 stake + suite.ctx = suite.app.NewUncachedContext(false, tmproto.Header{Height: 2, ChainID: test.TEST_CHAIN_ID, Time: time.Now().UTC()}) + suite.ctx = suite.ctx.WithMinGasPrices(sdk.NewDecCoins(sdk.NewDecCoin(test.TEST_TOKEN_NAME, sdk.OneInt()))) // set to 1 stake infCtx := suite.ctx.WithGasMeter(storetypes.NewInfiniteGasMeter()) err := suite.app.AccountKeeper.SetParams(infCtx, authtypes.DefaultParams()) @@ -84,13 +80,13 @@ func (suite *AnteTestSuite) SetupTest() { func (suite *AnteTestSuite) CreateTestEIP712TxBuilderMsgSend(from sdk.AccAddress, priv keys.KeyManager, chainId string, gas uint64, gasAmount sdk.Coins) client.TxBuilder { recipient := core.GenRandomAddr() - msgSend := banktypes.NewMsgSend(from, recipient, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.NewInt(1)))) + msgSend := banktypes.NewMsgSend(from, recipient, sdk.NewCoins(sdk.NewCoin(test.TEST_TOKEN_NAME, sdkmath.NewInt(1)))) return suite.CreateTestEIP712CosmosTxBuilder(from, priv, chainId, gas, gasAmount, msgSend) } func (suite *AnteTestSuite) CreateTestEIP712TxBuilderMsgDelegate(from sdk.AccAddress, priv keys.KeyManager, chainId string, gas uint64, gasAmount sdk.Coins) client.TxBuilder { validator := core.GenRandomAddr() - msgSend := stakingtypes.NewMsgDelegate(from, validator, sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.NewInt(20))) + msgSend := stakingtypes.NewMsgDelegate(from, validator, sdk.NewCoin(test.TEST_TOKEN_NAME, sdkmath.NewInt(20))) return suite.CreateTestEIP712CosmosTxBuilder(from, priv, chainId, gas, gasAmount, msgSend) } @@ -100,7 +96,7 @@ func (suite *AnteTestSuite) CreateTestEIP712MsgCreateValidator(from sdk.AccAddre msgCreate, err := stakingtypes.NewMsgCreateValidator( from, privEd.PubKey(), - sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(20)), + sdk.NewCoin(test.TEST_TOKEN_NAME, sdk.NewInt(20)), stakingtypes.NewDescription("moniker", "identity", "website", "security_contract", "details"), stakingtypes.NewCommissionRates(sdk.OneDec(), sdk.OneDec(), sdk.OneDec()), sdk.OneInt(), @@ -124,7 +120,7 @@ func (suite *AnteTestSuite) CreateTestEIP712SubmitProposal(from sdk.AccAddress, } func (suite *AnteTestSuite) CreateTestEIP712GrantAllowance(from sdk.AccAddress, priv keys.KeyManager, chainId string, gas uint64, gasAmount sdk.Coins) client.TxBuilder { - spendLimit := sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 10)) + spendLimit := sdk.NewCoins(sdk.NewInt64Coin(test.TEST_TOKEN_NAME, 10)) threeHours := time.Now().Add(3 * time.Hour) basic := &feegrant.BasicAllowance{ SpendLimit: spendLimit, @@ -173,7 +169,7 @@ func (suite *AnteTestSuite) CreateTestEIP712TxBuilderMsgSubmitProposalV1(from sd msgCreate, err := stakingtypes.NewMsgCreateValidator( from, privEd.PubKey(), - sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(20)), + sdk.NewCoin(test.TEST_TOKEN_NAME, sdk.NewInt(20)), stakingtypes.NewDescription("moniker", "indentity", "website", "security_contract", "details"), stakingtypes.NewCommissionRates(sdk.OneDec(), sdk.OneDec(), sdk.OneDec()), sdk.OneInt(), @@ -187,7 +183,7 @@ func (suite *AnteTestSuite) CreateTestEIP712TxBuilderMsgSubmitProposalV1(from sd suite.Require().NoError(err) msgSubmitProposal, err := govtypesv1.NewMsgSubmitProposal( []sdk.Msg{msgCreate}, - sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(20))}, + sdk.Coins{sdk.NewCoin(test.TEST_TOKEN_NAME, sdk.NewInt(20))}, from.String(), "test", "test", "test", ) @@ -265,56 +261,3 @@ func (suite *AnteTestSuite) CreateTestEIP712CosmosTxBuilder( suite.Require().NoError(err) return txBuilder } - -func NewApp(options ...func(baseApp *baseapp.BaseApp)) (*app.App, params.EncodingConfig, error) { - // create public key - privVal := mock.NewPV() - pubKey, _ := privVal.GetPubKey() - - // create validator set with single validator - validator := tmtypes.NewValidator(pubKey, 1) - valSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{validator}) - - // generate genesis account - bz, _ := hex.DecodeString(test.TEST_PUBKEY) - faucetPubKey := ðsecp256k1.PubKey{Key: bz} - - acc := authtypes.NewBaseAccount(faucetPubKey.Address().Bytes(), faucetPubKey, 0, 0) - balance := banktypes.Balance{ - Address: acc.GetAddress().String(), - Coins: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100000000000000))), - } - - logger := log.NewNopLogger() - db := dbm.NewMemDB() - encCfg := app.MakeEncodingConfig() - - nApp := app.New( - logger, - db, - nil, - true, - app.DefaultNodeHome, - 0, - encCfg, - &app.AppConfig{CrossChain: app.NewDefaultAppConfig().CrossChain}, - simtestutil.EmptyAppOptions{}, - options..., - ) - - genesisState := app.NewDefaultGenesisState(encCfg.Marshaler) - genesisState, _ = simtestutil.GenesisStateWithValSet(nApp.AppCodec(), genesisState, valSet, []authtypes.GenesisAccount{acc}, balance) - - stateBytes, _ := json.MarshalIndent(genesisState, "", " ") - - // Initialize the chain - nApp.InitChain( - abci.RequestInitChain{ - ChainId: "greenfield_9000-1", - Validators: []abci.ValidatorUpdate{}, - AppStateBytes: stateBytes, - }, - ) - - return nApp, encCfg, nil -} diff --git a/app/app.go b/app/app.go index 13d7e146d..87c87658e 100644 --- a/app/app.go +++ b/app/app.go @@ -729,6 +729,7 @@ func (app *App) initModules(ctx sdk.Context) { app.initBridge() app.initStorage() + app.initGov() } func (app *App) initCrossChain() { @@ -744,6 +745,13 @@ func (app *App) initStorage() { storagemodulekeeper.RegisterCrossApps(app.StorageKeeper) } +func (app *App) initGov() { + err := app.GovKeeper.RegisterCrossChainSyncParamsApp() + if err != nil { + panic(err) + } +} + // Name returns the name of the App func (app *App) Name() string { return app.BaseApp.Name() } diff --git a/app/export_test.go b/app/export_test.go new file mode 100644 index 000000000..a9c3cd26a --- /dev/null +++ b/app/export_test.go @@ -0,0 +1,22 @@ +package app_test + +import ( + "testing" + + "github.com/bnb-chain/greenfield/sdk/client/test" + "github.com/bnb-chain/greenfield/testutil" + dbm "github.com/cometbft/cometbft-db" + "github.com/cometbft/cometbft/libs/log" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" +) + +func TestExportAppStateAndValidators(t *testing.T) { + logger := log.NewNopLogger() + db := dbm.NewMemDB() + app, _, _ := testutil.NewTestApp(logger, db, nil, true, test.TEST_CHAIN_ID) + + _, err := app.ExportAppStateAndValidators(false, nil, []string{banktypes.ModuleName}) + if err != nil { + t.Fatalf("error exporting state: %s", err) + } +} diff --git a/deployment/localup/localup.sh b/deployment/localup/localup.sh index 72fcbacaf..f4fe16708 100644 --- a/deployment/localup/localup.sh +++ b/deployment/localup/localup.sh @@ -150,7 +150,7 @@ function generate_genesis() { sed -i -e "s/\"heartbeat_interval\": \"1000\"/\"heartbeat_interval\": \"100\"/g" ${workspace}/.local/validator${i}/config/genesis.json sed -i -e "s/\"attestation_inturn_interval\": \"120\"/\"attestation_inturn_interval\": \"10\"/g" ${workspace}/.local/validator${i}/config/genesis.json sed -i -e "s/\"discontinue_confirm_period\": \"604800\"/\"discontinue_confirm_period\": \"5\"/g" ${workspace}/.local/validator${i}/config/genesis.json - sed -i -e "s/\"discontinue_deletion_max\": \"10000\"/\"discontinue_deletion_max\": \"2\"/g" ${workspace}/.local/validator${i}/config/genesis.json + sed -i -e "s/\"discontinue_deletion_max\": \"100\"/\"discontinue_deletion_max\": \"2\"/g" ${workspace}/.local/validator${i}/config/genesis.json sed -i -e "s/\"voting_period\": \"30s\"/\"voting_period\": \"5s\"/g" ${workspace}/.local/validator${i}/config/genesis.json #sed -i -e "s/\"community_tax\": \"0.020000000000000000\"/\"community_tax\": \"0\"/g" ${workspace}/.local/validator${i}/config/genesis.json sed -i -e "s/log_level = \"info\"/\log_level= \"debug\"/g" ${workspace}/.local/validator${i}/config/config.toml @@ -159,6 +159,9 @@ function generate_genesis() { # enable swagger API for validator0 sed -i -e "/Enable defines if the API server should be enabled/{N;s/enable = false/enable = true/;}" ${workspace}/.local/validator0/config/app.toml sed -i -e 's/swagger = false/swagger = true/' ${workspace}/.local/validator0/config/app.toml + + # enable telemetry for validator0 + sed -i -e "/other sinks such as Prometheus/{N;s/enable = false/enable = true/;}" ${workspace}/.local/validator0/config/app.toml } function start() { diff --git a/e2e/tests/bridge_test.go b/e2e/tests/bridge_test.go index 7795a9a44..4d6df2237 100644 --- a/e2e/tests/bridge_test.go +++ b/e2e/tests/bridge_test.go @@ -8,12 +8,9 @@ import ( "time" sdkmath "cosmossdk.io/math" - sdkClient "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/tx" - "github.com/cosmos/cosmos-sdk/types/tx/signing" - authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" crosschaintypes "github.com/cosmos/cosmos-sdk/x/crosschain/types" @@ -26,7 +23,6 @@ import ( "github.com/bnb-chain/greenfield/e2e/core" gnfdtypes "github.com/bnb-chain/greenfield/sdk/types" types2 "github.com/bnb-chain/greenfield/sdk/types" - "github.com/bnb-chain/greenfield/x/bridge/client/cli" bridgetypes "github.com/bnb-chain/greenfield/x/bridge/types" ) @@ -40,44 +36,6 @@ func (s *BridgeTestSuite) SetupSuite() { func (s *BridgeTestSuite) SetupTest() {} -func (s *BridgeTestSuite) TestCliQuery() { - ctx := context.Background() - cliCtx := &sdkClient.Context{Client: s.TmClient.TmClient, Codec: s.Client.GetCodec()} - ctx = context.WithValue(ctx, sdkClient.ClientContextKey, cliCtx) - queryCmd := cli.GetQueryCmd() - - // query params - queryCmd.SetArgs([]string{"params"}) - s.Require().NoError(queryCmd.ExecuteContext(ctx)) -} - -func (s *BridgeTestSuite) TestCliTx() { - ctx := context.Background() - txCfg := authtx.NewTxConfig(s.Client.GetCodec(), []signing.SignMode{signing.SignMode_SIGN_MODE_EIP_712}) - cliCtx := &sdkClient.Context{ - FromAddress: s.Validator.GetAddr(), - Client: s.TmClient.TmClient, - InterfaceRegistry: s.Client.GetCodec().InterfaceRegistry(), - Codec: s.Client.GetCodec(), - From: s.Validator.String(), - AccountRetriever: authtypes.AccountRetriever{}, - ChainID: s.Config.ChainId, - TxConfig: txCfg, - SkipConfirm: true, - Simulate: true, - } - ctx = context.WithValue(ctx, sdkClient.ClientContextKey, cliCtx) - txCmd := cli.GetTxCmd() - - // wrong to address - txCmd.SetArgs([]string{"transfer-out", "test", "1000000000000000000BNB"}) - s.Require().Contains(txCmd.ExecuteContext(ctx).Error(), "invalid address hex length") - - // tx transfer-out - txCmd.SetArgs([]string{"transfer-out", sdk.AccAddress("test").String(), "1000000000000000000BNB"}) - s.Require().NoError(txCmd.ExecuteContext(ctx)) -} - func (s *BridgeTestSuite) TestTransferOut() { users := s.GenAndChargeAccounts(2, 1000000) diff --git a/e2e/tests/payment_test.go b/e2e/tests/payment_test.go index 7ceaa3bda..302d403ed 100644 --- a/e2e/tests/payment_test.go +++ b/e2e/tests/payment_test.go @@ -6,7 +6,6 @@ import ( "fmt" "math" "reflect" - "sort" "strconv" "testing" "time" @@ -926,1304 +925,6 @@ func (s *PaymentTestSuite) TestWithdraw() { s.Require().Equal(paymentAccountStreamRecord.StaticBalance.Sub(paymentAccountStreamRecordAfter.StaticBalance).Int64(), amount.Add(staticBalanceChange).Int64()) } -func (s *PaymentTestSuite) TestStorageBill_DeleteBucket_WithReadQuota() { - var err error - 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(), - } - - // CreateBucket - chargedReadQuota := uint64(100) - bucketName := storagetestutils.GenRandomBucketName() - msgCreateBucket := storagetypes.NewMsgCreateBucket( - user.GetAddr(), bucketName, storagetypes.VISIBILITY_TYPE_PUBLIC_READ, sp.OperatorKey.GetAddr(), - nil, math.MaxUint, nil, chargedReadQuota) - msgCreateBucket.PrimarySpApproval.GlobalVirtualGroupFamilyId = gvg.FamilyId - msgCreateBucket.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateBucket.GetApprovalBytes()) - s.Require().NoError(err) - s.SendTxBlock(user, msgCreateBucket) - - streamRecordsBeforeDelete := s.getStreamRecords(streamAddresses) - s.T().Logf("streamRecordsBeforeDelete: %s", core.YamlString(streamRecordsBeforeDelete)) - s.Require().NotEqual(streamRecordsBeforeDelete.User.NetflowRate.String(), "0") - - // DeleteBucket - msgDeleteBucket := storagetypes.NewMsgDeleteBucket(user.GetAddr(), bucketName) - s.SendTxBlock(user, msgDeleteBucket) - - // check the billing change - streamRecordsAfterDelete := s.getStreamRecords(streamAddresses) - s.T().Logf("streamRecordsBeforeDelete: %s", core.YamlString(streamRecordsAfterDelete)) - s.Require().Equal(streamRecordsAfterDelete.User.NetflowRate.String(), "0") -} - -func (s *PaymentTestSuite) TestStorageBill_Smoke() { - var err error - 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(), - } - streamRecordsBeforeCreateBucket := s.getStreamRecords(streamAddresses) - s.T().Logf("streamRecordsBeforeCreateBucket: %s", core.YamlString(streamRecordsBeforeCreateBucket)) - - params := s.queryParams() - - // create bucket - bucketName := storagetestutils.GenRandomBucketName() - bucketChargedReadQuota := uint64(1000) - msgCreateBucket := storagetypes.NewMsgCreateBucket( - user.GetAddr(), bucketName, storagetypes.VISIBILITY_TYPE_PRIVATE, sp.OperatorKey.GetAddr(), - nil, math.MaxUint, nil, 0) - msgCreateBucket.ChargedReadQuota = bucketChargedReadQuota - msgCreateBucket.PrimarySpApproval.GlobalVirtualGroupFamilyId = gvg.FamilyId - msgCreateBucket.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateBucket.GetApprovalBytes()) - s.Require().NoError(err) - s.SendTxBlock(user, msgCreateBucket) - - // check bill after creating bucket - userBankAccount, err := s.Client.Balance(ctx, &banktypes.QueryBalanceRequest{ - Address: user.GetAddr().String(), - Denom: s.Config.Denom, - }) - s.Require().NoError(err) - s.T().Logf("user bank account %s", userBankAccount) - - streamRecordsAfterCreateBucket := s.getStreamRecords(streamAddresses) - userStreamRecord := streamRecordsAfterCreateBucket.User - s.Require().Equal(userStreamRecord.StaticBalance, sdkmath.ZeroInt()) - - // check price and rate calculation - queryHeadBucketRequest := storagetypes.QueryHeadBucketRequest{ - BucketName: bucketName, - } - queryHeadBucketResponse, err := s.Client.HeadBucket(ctx, &queryHeadBucketRequest) - s.Require().NoError(err) - - queryGetSpStoragePriceByTimeResp, err := s.Client.QueryGetSpStoragePriceByTime(ctx, &sptypes.QueryGetSpStoragePriceByTimeRequest{ - SpAddr: sp.OperatorKey.GetAddr().String(), - Timestamp: queryHeadBucketResponse.BucketInfo.CreateAt, - }) - s.T().Logf("queryGetSpStoragePriceByTimeResp %s, err: %v", queryGetSpStoragePriceByTimeResp, err) - s.Require().NoError(err) - - readPrice := queryGetSpStoragePriceByTimeResp.SpStoragePrice.ReadPrice - readChargeRate := readPrice.MulInt(sdk.NewIntFromUint64(queryHeadBucketResponse.BucketInfo.ChargedReadQuota)).TruncateInt() - s.T().Logf("readPrice: %s, readChargeRate: %s", readPrice, readChargeRate) - userTaxRate := params.VersionedParams.ValidatorTaxRate.MulInt(readChargeRate).TruncateInt() - userTotalRate := readChargeRate.Add(userTaxRate) - s.Require().Equal(userStreamRecord.NetflowRate.Abs(), userTotalRate) - expectedOutFlows := []paymenttypes.OutFlow{ - {ToAddress: family.VirtualPaymentAddress, Rate: readChargeRate}, - {ToAddress: paymenttypes.ValidatorTaxPoolAddress.String(), Rate: userTaxRate}, - } - userOutFlowsResponse, err := s.Client.OutFlows(ctx, &paymenttypes.QueryOutFlowsRequest{Account: user.GetAddr().String()}) - s.Require().NoError(err) - sort.Slice(userOutFlowsResponse.OutFlows, func(i, j int) bool { - return userOutFlowsResponse.OutFlows[i].ToAddress < userOutFlowsResponse.OutFlows[j].ToAddress - }) - sort.Slice(expectedOutFlows, func(i, j int) bool { - return expectedOutFlows[i].ToAddress < expectedOutFlows[j].ToAddress - }) - s.Require().Equal(expectedOutFlows, userOutFlowsResponse.OutFlows) - - // CreateObject - objectName := storagetestutils.GenRandomObjectName() - // create test buffer - var buffer bytes.Buffer - // Create 1MiB content where each line contains 1024 characters. - for i := 0; i < 1024; i++ { - buffer.WriteString(fmt.Sprintf("[%05d] %s\n", i, line)) - } - payloadSize := buffer.Len() - checksum := sdk.Keccak256(buffer.Bytes()) - expectChecksum := [][]byte{checksum, checksum, checksum, checksum, checksum, checksum, checksum} - contextType := "text/event-stream" - msgCreateObject := storagetypes.NewMsgCreateObject(user.GetAddr(), bucketName, objectName, uint64(payloadSize), storagetypes.VISIBILITY_TYPE_PRIVATE, expectChecksum, contextType, storagetypes.REDUNDANCY_EC_TYPE, math.MaxUint, nil) - msgCreateObject.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateObject.GetApprovalBytes()) - s.Require().NoError(err) - // simulate - res := s.SimulateTx(msgCreateObject, user) - s.T().Logf("res %v", res.Result) - // check EventFeePreview in simulation result - var feePreviewEventEmitted bool - events := res.Result.Events - for _, event := range events { - if event.Type == "greenfield.payment.EventFeePreview" { - s.T().Logf("event %v", event) - feePreviewEventEmitted = true - } - } - s.Require().True(feePreviewEventEmitted) - s.SendTxBlock(user, msgCreateObject) - - // check lock balance - queryHeadBucketResponseAfterCreateObj, err := s.Client.HeadBucket(ctx, &queryHeadBucketRequest) - s.T().Logf("queryHeadBucketResponseAfterCreateObj %s, err: %v", queryHeadBucketResponseAfterCreateObj, err) - s.Require().NoError(err) - queryHeadObjectRequest := storagetypes.QueryHeadObjectRequest{ - BucketName: bucketName, - ObjectName: objectName, - } - queryHeadObjectResponse, err := s.Client.HeadObject(ctx, &queryHeadObjectRequest) - s.T().Logf("queryHeadObjectResponse %s, err: %v", queryHeadObjectResponse, err) - s.Require().NoError(err) - - queryGetSecondarySpStorePriceByTime, err := s.Client.QueryGetSecondarySpStorePriceByTime(ctx, &sptypes.QueryGetSecondarySpStorePriceByTimeRequest{ - Timestamp: queryHeadBucketResponse.BucketInfo.CreateAt, - }) - s.T().Logf("queryGetSecondarySpStorePriceByTime %s, err: %v", queryGetSecondarySpStorePriceByTime, err) - s.Require().NoError(err) - primaryStorePrice := queryGetSpStoragePriceByTimeResp.SpStoragePrice.StorePrice - secondaryStorePrice := queryGetSecondarySpStorePriceByTime.SecondarySpStorePrice.StorePrice - chargeSize := s.getChargeSize(queryHeadObjectResponse.ObjectInfo.PayloadSize) - expectedChargeRate := primaryStorePrice.Add(secondaryStorePrice.MulInt64(6)).MulInt(sdk.NewIntFromUint64(chargeSize)).TruncateInt() - expectedChargeRate = params.VersionedParams.ValidatorTaxRate.MulInt(expectedChargeRate).TruncateInt().Add(expectedChargeRate) - expectedLockedBalance := expectedChargeRate.Mul(sdkmath.NewIntFromUint64(params.VersionedParams.ReserveTime)) - - streamRecordsAfterCreateObject := s.getStreamRecords(streamAddresses) - s.T().Logf("streamRecordsAfterCreateObject %s", core.YamlString(streamRecordsAfterCreateObject)) - userStreamAccountAfterCreateObj := streamRecordsAfterCreateObject.User - - s.Require().Equal(expectedLockedBalance.String(), userStreamAccountAfterCreateObj.LockBalance.String()) - - // seal object - gvgId := gvg.Id - msgSealObject := storagetypes.NewMsgSealObject(sp.SealKey.GetAddr(), bucketName, objectName, gvg.Id, nil) - secondarySigs := make([][]byte, 0) - secondarySPBlsPubKeys := make([]bls.PublicKey, 0) - blsSignHash := storagetypes.NewSecondarySpSealObjectSignDoc(s.GetChainID(), gvgId, queryHeadObjectResponse.ObjectInfo.Id, storagetypes.GenerateHash(expectChecksum[:])).GetBlsSignHash() - // every secondary sp signs the checksums - for _, spID := range gvg.SecondarySpIds { - sig, err := core.BlsSignAndVerify(s.StorageProviders[spID], blsSignHash) - s.Require().NoError(err) - secondarySigs = append(secondarySigs, sig) - pk, err := bls.PublicKeyFromBytes(s.StorageProviders[spID].BlsKey.PubKey().Bytes()) - s.Require().NoError(err) - secondarySPBlsPubKeys = append(secondarySPBlsPubKeys, pk) - } - aggBlsSig, err := core.BlsAggregateAndVerify(secondarySPBlsPubKeys, blsSignHash, secondarySigs) - s.Require().NoError(err) - msgSealObject.SecondarySpBlsAggSignatures = aggBlsSig - s.T().Logf("msg %s", msgSealObject.String()) - s.SendTxBlock(sp.SealKey, msgSealObject) - - // check bill after seal - streamRecordsAfterSeal := s.getStreamRecords(streamAddresses) - s.T().Logf("streamRecordsAfterSeal %s", core.YamlString(streamRecordsAfterSeal)) - s.Require().Equal(sdkmath.ZeroInt(), streamRecordsAfterSeal.User.LockBalance) - s.checkStreamRecordsBeforeAndAfter(streamRecordsAfterCreateObject, streamRecordsAfterSeal, readPrice, readChargeRate, primaryStorePrice, secondaryStorePrice, chargeSize, uint64(payloadSize)) - - // query dynamic balance - time.Sleep(3 * time.Second) - queryDynamicBalanceRequest := paymenttypes.QueryDynamicBalanceRequest{ - Account: user.GetAddr().String(), - } - queryDynamicBalanceResponse, err := s.Client.DynamicBalance(ctx, &queryDynamicBalanceRequest) - s.Require().NoError(err) - s.T().Logf("queryDynamicBalanceResponse %s", core.YamlString(queryDynamicBalanceResponse)) - - // create empty object - streamRecordsBeforeCreateEmptyObject := s.getStreamRecords(streamAddresses) - s.T().Logf("streamRecordsBeforeCreateEmptyObject %s", core.YamlString(streamRecordsBeforeCreateEmptyObject)) - - emptyObjectName := "sub_directory/" - // create empty test buffer - var emptyBuffer bytes.Buffer - emptyPayloadSize := emptyBuffer.Len() - emptyChecksum := sdk.Keccak256(emptyBuffer.Bytes()) - emptyExpectChecksum := [][]byte{emptyChecksum, emptyChecksum, emptyChecksum, emptyChecksum, emptyChecksum, emptyChecksum, emptyChecksum} - msgCreateEmptyObject := storagetypes.NewMsgCreateObject(user.GetAddr(), bucketName, emptyObjectName, uint64(emptyPayloadSize), storagetypes.VISIBILITY_TYPE_PRIVATE, emptyExpectChecksum, contextType, storagetypes.REDUNDANCY_EC_TYPE, math.MaxUint, nil) - msgCreateEmptyObject.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateEmptyObject.GetApprovalBytes()) - s.Require().NoError(err) - s.SendTxBlock(user, msgCreateEmptyObject) - - streamRecordsAfterCreateEmptyObject := s.getStreamRecords(streamAddresses) - s.T().Logf("streamRecordsAfterCreateEmptyObject %s", core.YamlString(streamRecordsAfterCreateEmptyObject)) - chargeSize = s.getChargeSize(uint64(emptyPayloadSize)) - s.checkStreamRecordsBeforeAndAfter(streamRecordsBeforeCreateEmptyObject, streamRecordsAfterCreateEmptyObject, readPrice, readChargeRate, primaryStorePrice, secondaryStorePrice, chargeSize, uint64(emptyPayloadSize)) - - // test query auto settle records - queryAllAutoSettleRecordRequest := paymenttypes.QueryAllAutoSettleRecordRequest{} - queryAllAutoSettleRecordResponse, err := s.Client.AutoSettleRecordAll(ctx, &queryAllAutoSettleRecordRequest) - s.Require().NoError(err) - s.T().Logf("queryAllAutoSettleRecordResponse %s", core.YamlString(queryAllAutoSettleRecordResponse)) - s.Require().True(len(queryAllAutoSettleRecordResponse.AutoSettleRecord) >= 1) - - // simulate delete object, check fee preview - deleteObjectMsg := storagetypes.NewMsgDeleteObject(user.GetAddr(), bucketName, objectName) - deleteObjectSimRes := s.SimulateTx(deleteObjectMsg, user) - s.T().Logf("deleteObjectSimRes %v", deleteObjectSimRes.Result) -} - -func (s *PaymentTestSuite) TestStorageBill_DeleteObjectBucket_WithoutPriceChange() { - var err error - 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(), - } - streamRecordsBefore := s.getStreamRecords(streamAddresses) - - // create bucket - bucketName := s.createBucket(sp, user, 256) - - //simulate delete bucket gas - msgDeleteBucket := storagetypes.NewMsgDeleteBucket(user.GetAddr(), bucketName) - simulateResponse := s.SimulateTx(msgDeleteBucket, user) - gasLimit := simulateResponse.GasInfo.GetGasUsed() - gasPrice, err := sdk.ParseCoinNormalized(simulateResponse.GasInfo.GetMinGasPrice()) - s.Require().NoError(err) - - gas := gasPrice.Amount.Mul(sdk.NewInt(int64(gasLimit))) - s.T().Log("total gas", "gas", gas) - - // create & seal objects - _, _, objectName1, objectId1, checksums1, _ := s.createObject(user, bucketName, false) - s.sealObject(sp, gvg, bucketName, objectName1, objectId1, checksums1) - - // for payment - time.Sleep(2 * time.Second) - - //transfer gas - msgSend := banktypes.NewMsgSend(user.GetAddr(), core.GenRandomAddr(), sdk.NewCoins( - sdk.NewCoin(s.Config.Denom, sdkmath.NewInt(5*types.DecimalGwei)), - )) - simulateResponse = s.SimulateTx(msgSend, user) - gasLimit = simulateResponse.GasInfo.GetGasUsed() - gasPrice, err = sdk.ParseCoinNormalized(simulateResponse.GasInfo.GetMinGasPrice()) - s.Require().NoError(err) - - gas = gas.Add(gasPrice.Amount.Mul(sdk.NewInt(int64(gasLimit)))) - s.T().Log("total gas", "gas", gas) - - //delete object gas - msgDeleteObject := storagetypes.NewMsgDeleteObject(user.GetAddr(), bucketName, objectName1) - simulateResponse = s.SimulateTx(msgDeleteObject, user) - gasLimit = simulateResponse.GasInfo.GetGasUsed() - gasPrice, err = sdk.ParseCoinNormalized(simulateResponse.GasInfo.GetMinGasPrice()) - s.Require().NoError(err) - - gas = gas.Add(gasPrice.Amount.Mul(sdk.NewInt(int64(gasLimit)))) - s.T().Log("total gas", "gas", gas) - - // transfer out user's 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.Amount = sdk.NewCoins( - sdk.NewCoin(s.Config.Denom, queryBalanceResponse.Balance.Amount.Sub(gas)), - ) - s.SendTxBlock(user, msgSend) - _, err = s.Client.BankQueryClient.Balance(ctx, &queryBalanceRequest) - s.Require().NoError(err) - - s.SendTxBlock(user, msgDeleteObject) - s.SendTxBlock(user, msgDeleteBucket) - - // assertions - streamRecordsAfter := s.getStreamRecords(streamAddresses) - s.Require().Equal(streamRecordsAfter.User.NetflowRate, sdkmath.ZeroInt()) - s.Require().Equal(streamRecordsAfter.User.NetflowRate.Sub(streamRecordsBefore.User.NetflowRate).Int64(), int64(0)) - s.Require().Equal(streamRecordsAfter.GVGFamily.NetflowRate.Sub(streamRecordsBefore.GVGFamily.NetflowRate).Int64(), int64(0)) - s.Require().Equal(streamRecordsAfter.GVG.NetflowRate.Sub(streamRecordsBefore.GVG.NetflowRate).Int64(), int64(0)) - s.Require().Equal(streamRecordsAfter.Tax.NetflowRate.Sub(streamRecordsBefore.Tax.NetflowRate).Int64(), int64(0)) -} - -func (s *PaymentTestSuite) TestStorageBill_DeleteObjectBucket_WithPriceChange() { - var err error - 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(), - } - streamRecordsBefore := s.getStreamRecords(streamAddresses) - - // create bucket - bucketName := s.createBucket(sp, user, 256) - - //simulate delete bucket gas - msgDeleteBucket := storagetypes.NewMsgDeleteBucket(user.GetAddr(), bucketName) - simulateResponse := s.SimulateTx(msgDeleteBucket, user) - gasLimit := simulateResponse.GasInfo.GetGasUsed() - gasPrice, err := sdk.ParseCoinNormalized(simulateResponse.GasInfo.GetMinGasPrice()) - s.Require().NoError(err) - - gas := gasPrice.Amount.Mul(sdk.NewInt(int64(gasLimit))) - s.T().Log("total gas", "gas", gas) - - // create & seal objects - _, _, objectName1, objectId1, checksums1, _ := s.createObject(user, bucketName, false) - s.sealObject(sp, gvg, bucketName, objectName1, objectId1, checksums1) - - // for payment - time.Sleep(2 * time.Second) - - //transfer gas - msgSend := banktypes.NewMsgSend(user.GetAddr(), core.GenRandomAddr(), sdk.NewCoins( - sdk.NewCoin(s.Config.Denom, sdkmath.NewInt(5*types.DecimalGwei)), - )) - simulateResponse = s.SimulateTx(msgSend, user) - gasLimit = simulateResponse.GasInfo.GetGasUsed() - gasPrice, err = sdk.ParseCoinNormalized(simulateResponse.GasInfo.GetMinGasPrice()) - s.Require().NoError(err) - - gas = gas.Add(gasPrice.Amount.Mul(sdk.NewInt(int64(gasLimit)))) - s.T().Log("total gas", "gas", gas) - - //delete object gas - msgDeleteObject := storagetypes.NewMsgDeleteObject(user.GetAddr(), bucketName, objectName1) - simulateResponse = s.SimulateTx(msgDeleteObject, user) - gasLimit = simulateResponse.GasInfo.GetGasUsed() - gasPrice, err = sdk.ParseCoinNormalized(simulateResponse.GasInfo.GetMinGasPrice()) - s.Require().NoError(err) - - gas = gas.Add(gasPrice.Amount.Mul(sdk.NewInt(int64(gasLimit)))) - s.T().Log("total gas", "gas", gas) - - // transfer out user's 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.Amount = sdk.NewCoins( - sdk.NewCoin(s.Config.Denom, queryBalanceResponse.Balance.Amount.Sub(gas)), - ) - s.SendTxBlock(user, msgSend) - _, err = s.Client.BankQueryClient.Balance(ctx, &queryBalanceRequest) - s.Require().NoError(err) - - // sp price changes - priceRes, err := s.Client.QueryGetSpStoragePriceByTime(ctx, &sptypes.QueryGetSpStoragePriceByTimeRequest{ - SpAddr: sp.OperatorKey.GetAddr().String(), - Timestamp: 0, - }) - s.Require().NoError(err) - s.T().Log("price", priceRes.SpStoragePrice) - - // update new price - msgUpdatePrice := &sptypes.MsgUpdateSpStoragePrice{ - SpAddress: sp.OperatorKey.GetAddr().String(), - ReadPrice: priceRes.SpStoragePrice.ReadPrice.MulInt64(1000), - FreeReadQuota: priceRes.SpStoragePrice.FreeReadQuota, - StorePrice: priceRes.SpStoragePrice.StorePrice.MulInt64(10000), - } - s.SendTxBlock(sp.OperatorKey, msgUpdatePrice) - - s.SendTxBlock(user, msgDeleteObject) - s.SendTxBlock(user, msgDeleteBucket) - - // assertions - streamRecordsAfter := s.getStreamRecords(streamAddresses) - s.Require().Equal(streamRecordsAfter.User.NetflowRate, sdkmath.ZeroInt()) - s.Require().Equal(streamRecordsAfter.User.NetflowRate.Sub(streamRecordsBefore.User.NetflowRate).Int64(), int64(0)) - s.Require().Equal(streamRecordsAfter.GVGFamily.NetflowRate.Sub(streamRecordsBefore.GVGFamily.NetflowRate).Int64(), int64(0)) - s.Require().Equal(streamRecordsAfter.GVG.NetflowRate.Sub(streamRecordsBefore.GVG.NetflowRate).Int64(), int64(0)) - s.Require().Equal(streamRecordsAfter.Tax.NetflowRate.Sub(streamRecordsBefore.Tax.NetflowRate).Int64(), int64(0)) - - // revert price - msgUpdatePrice = &sptypes.MsgUpdateSpStoragePrice{ - SpAddress: sp.OperatorKey.GetAddr().String(), - ReadPrice: priceRes.SpStoragePrice.ReadPrice, - FreeReadQuota: priceRes.SpStoragePrice.FreeReadQuota, - StorePrice: priceRes.SpStoragePrice.StorePrice, - } - s.SendTxBlock(sp.OperatorKey, msgUpdatePrice) -} - -func (s *PaymentTestSuite) TestStorageBill_DeleteObjectBucket_WithPriceChangeReserveTimeChange() { - defer s.revertParams() - - var err error - 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(), - } - streamRecordsBefore := s.getStreamRecords(streamAddresses) - - // create bucket - bucketName := s.createBucket(sp, user, 256) - - //simulate delete bucket gas - msgDeleteBucket := storagetypes.NewMsgDeleteBucket(user.GetAddr(), bucketName) - simulateResponse := s.SimulateTx(msgDeleteBucket, user) - gasLimit := simulateResponse.GasInfo.GetGasUsed() - gasPrice, err := sdk.ParseCoinNormalized(simulateResponse.GasInfo.GetMinGasPrice()) - s.Require().NoError(err) - - gas := gasPrice.Amount.Mul(sdk.NewInt(int64(gasLimit))) - s.T().Log("total gas", "gas", gas) - - // create & seal objects - _, _, objectName1, objectId1, checksums1, _ := s.createObject(user, bucketName, false) - s.sealObject(sp, gvg, bucketName, objectName1, objectId1, checksums1) - - // for payment - time.Sleep(2 * time.Second) - - //transfer gas - msgSend := banktypes.NewMsgSend(user.GetAddr(), core.GenRandomAddr(), sdk.NewCoins( - sdk.NewCoin(s.Config.Denom, sdkmath.NewInt(5*types.DecimalGwei)), - )) - simulateResponse = s.SimulateTx(msgSend, user) - gasLimit = simulateResponse.GasInfo.GetGasUsed() - gasPrice, err = sdk.ParseCoinNormalized(simulateResponse.GasInfo.GetMinGasPrice()) - s.Require().NoError(err) - - gas = gas.Add(gasPrice.Amount.Mul(sdk.NewInt(int64(gasLimit)))) - s.T().Log("total gas", "gas", gas) - - //delete object gas - msgDeleteObject := storagetypes.NewMsgDeleteObject(user.GetAddr(), bucketName, objectName1) - simulateResponse = s.SimulateTx(msgDeleteObject, user) - gasLimit = simulateResponse.GasInfo.GetGasUsed() - gasPrice, err = sdk.ParseCoinNormalized(simulateResponse.GasInfo.GetMinGasPrice()) - s.Require().NoError(err) - - gas = gas.Add(gasPrice.Amount.Mul(sdk.NewInt(int64(gasLimit)))) - s.T().Log("total gas", "gas", gas) - - // transfer out user's 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.Amount = sdk.NewCoins( - sdk.NewCoin(s.Config.Denom, queryBalanceResponse.Balance.Amount.Sub(gas)), - ) - s.SendTxBlock(user, msgSend) - _, err = s.Client.BankQueryClient.Balance(ctx, &queryBalanceRequest) - s.Require().NoError(err) - - // sp price changes - priceRes, err := s.Client.QueryGetSpStoragePriceByTime(ctx, &sptypes.QueryGetSpStoragePriceByTimeRequest{ - SpAddr: sp.OperatorKey.GetAddr().String(), - Timestamp: 0, - }) - s.Require().NoError(err) - s.T().Log("price", priceRes.SpStoragePrice) - - // update new price - msgUpdatePrice := &sptypes.MsgUpdateSpStoragePrice{ - SpAddress: sp.OperatorKey.GetAddr().String(), - ReadPrice: priceRes.SpStoragePrice.ReadPrice.MulInt64(1000), - FreeReadQuota: priceRes.SpStoragePrice.FreeReadQuota, - StorePrice: priceRes.SpStoragePrice.StorePrice.MulInt64(10000), - } - s.SendTxBlock(sp.OperatorKey, msgUpdatePrice) - - // update params - params := s.queryParams() - oldReserveTime := params.VersionedParams.ReserveTime - oldValidatorTaxRate := params.VersionedParams.ValidatorTaxRate - - params.VersionedParams.ReserveTime = oldReserveTime * 2 - params.VersionedParams.ValidatorTaxRate = oldValidatorTaxRate.MulInt64(2) - s.updateParams(params) - - s.SendTxBlock(user, msgDeleteObject) - s.SendTxBlock(user, msgDeleteBucket) - - // assertions - streamRecordsAfter := s.getStreamRecords(streamAddresses) - s.Require().Equal(streamRecordsAfter.User.NetflowRate, sdkmath.ZeroInt()) - s.Require().Equal(streamRecordsAfter.User.NetflowRate.Sub(streamRecordsBefore.User.NetflowRate).Int64(), int64(0)) - s.Require().Equal(streamRecordsAfter.GVGFamily.NetflowRate.Sub(streamRecordsBefore.GVGFamily.NetflowRate).Int64(), int64(0)) - s.Require().Equal(streamRecordsAfter.GVG.NetflowRate.Sub(streamRecordsBefore.GVG.NetflowRate).Int64(), int64(0)) - s.Require().Equal(streamRecordsAfter.Tax.NetflowRate.Sub(streamRecordsBefore.Tax.NetflowRate).Int64(), int64(0)) - - // revert price - msgUpdatePrice = &sptypes.MsgUpdateSpStoragePrice{ - SpAddress: sp.OperatorKey.GetAddr().String(), - ReadPrice: priceRes.SpStoragePrice.ReadPrice, - FreeReadQuota: priceRes.SpStoragePrice.FreeReadQuota, - StorePrice: priceRes.SpStoragePrice.StorePrice, - } - s.SendTxBlock(sp.OperatorKey, msgUpdatePrice) -} - -func (s *PaymentTestSuite) TestStorageBill_DeleteObject_WithStoreLessThanReserveTime() { - var err error - 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] - - params := s.queryParams() - reserveTime := params.VersionedParams.ReserveTime - - // create bucket - bucketName := s.createBucket(sp, user, 256) - - // create & seal objects - _, _, objectName1, objectId1, checksums1, payloadSize := s.createObject(user, bucketName, false) - s.sealObject(sp, gvg, bucketName, objectName1, objectId1, checksums1) - - headObjectRes, err := s.Client.HeadObject(ctx, &storagetypes.QueryHeadObjectRequest{ - BucketName: bucketName, - ObjectName: objectName1, - }) - s.Require().NoError(err) - s.T().Log("headObjectRes", headObjectRes) - - streamAddresses := []string{ - user.GetAddr().String(), - family.VirtualPaymentAddress, - gvg.VirtualPaymentAddress, - paymenttypes.ValidatorTaxPoolAddress.String(), - } - streamRecordsBefore := s.getStreamRecords(streamAddresses) - _, _, userRateRead := s.calculateReadRates(sp, bucketName) - _, _, _, userRateStore := s.calculateStorageRates(sp, bucketName, objectName1, payloadSize, 0) - - msgDeleteObject := storagetypes.NewMsgDeleteObject(user.GetAddr(), bucketName, objectName1) - s.SendTxBlock(user, msgDeleteObject) - - // assertions - streamRecordsAfter := s.getStreamRecords(streamAddresses) - - settledTime := streamRecordsAfter.User.CrudTimestamp - streamRecordsBefore.User.CrudTimestamp - timeToPay := int64(reserveTime) + headObjectRes.ObjectInfo.CreateAt - streamRecordsAfter.User.CrudTimestamp - balanceDelta := userRateRead.Add(userRateStore).MulRaw(settledTime).Add(userRateStore.MulRaw(timeToPay)) - - s.Require().Equal(streamRecordsAfter.User.NetflowRate.Sub(streamRecordsBefore.User.NetflowRate).Int64(), userRateStore.Int64()) - userBalanceChange := streamRecordsAfter.User.BufferBalance.Add(streamRecordsAfter.User.StaticBalance). - Sub(streamRecordsBefore.User.BufferBalance.Add(streamRecordsBefore.User.StaticBalance)) - s.Require().Equal(userBalanceChange.Neg().Int64(), balanceDelta.Int64()) - - familyDelta := streamRecordsAfter.GVGFamily.StaticBalance.Sub(streamRecordsBefore.GVGFamily.StaticBalance) - gvgDelta := streamRecordsAfter.GVG.StaticBalance.Sub(streamRecordsBefore.GVG.StaticBalance) - taxPoolDelta := streamRecordsAfter.Tax.StaticBalance.Sub(streamRecordsBefore.Tax.StaticBalance) - s.T().Log("familyDelta", familyDelta, "gvgDelta", gvgDelta, "taxPoolDelta", taxPoolDelta) - s.Require().True(familyDelta.Add(gvgDelta).Add(taxPoolDelta).Int64() >= balanceDelta.Int64()) // could exist other buckets/objects on the gvg & family -} - -func (s *PaymentTestSuite) TestStorageBill_DeleteObject_WithStoreMoreThanReserveTime() { - defer s.revertParams() - - var err error - 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] - - params := s.queryParams() - params.VersionedParams.ReserveTime = 5 - params.ForcedSettleTime = 2 - s.updateParams(params) - - // create bucket - bucketName := s.createBucket(sp, user, 256) - - // create & seal objects - _, _, objectName1, objectId1, checksums1, payloadSize := s.createObject(user, bucketName, false) - s.sealObject(sp, gvg, bucketName, objectName1, objectId1, checksums1) - - headObjectRes, err := s.Client.HeadObject(ctx, &storagetypes.QueryHeadObjectRequest{ - BucketName: bucketName, - ObjectName: objectName1, - }) - s.Require().NoError(err) - s.T().Log("headObjectRes", headObjectRes) - - time.Sleep(5 * time.Second) - queryBalanceRequest := banktypes.QueryBalanceRequest{Denom: s.Config.Denom, Address: user.GetAddr().String()} - queryBalanceResponse, err := s.Client.BankQueryClient.Balance(ctx, &queryBalanceRequest) - s.Require().NoError(err) - balanceBefore := queryBalanceResponse.Balance.Amount - - streamAddresses := []string{ - user.GetAddr().String(), - family.VirtualPaymentAddress, - gvg.VirtualPaymentAddress, - paymenttypes.ValidatorTaxPoolAddress.String(), - } - streamRecordsBefore := s.getStreamRecords(streamAddresses) - _, _, userRateRead := s.calculateReadRates(sp, bucketName) - _, _, _, userRateStore := s.calculateStorageRates(sp, bucketName, objectName1, payloadSize, 0) - - msgDeleteObject := storagetypes.NewMsgDeleteObject(user.GetAddr(), bucketName, objectName1) - simulateResponse := s.SimulateTx(msgDeleteObject, user) - gasLimit := simulateResponse.GasInfo.GetGasUsed() - gasPrice, err := sdk.ParseCoinNormalized(simulateResponse.GasInfo.GetMinGasPrice()) - s.Require().NoError(err) - gas := gasPrice.Amount.MulRaw(int64(gasLimit)) - - // delete object - s.SendTxBlock(user, msgDeleteObject) - - // assertions - streamRecordsAfter := s.getStreamRecords(streamAddresses) - queryBalanceRequest = banktypes.QueryBalanceRequest{Denom: s.Config.Denom, Address: user.GetAddr().String()} - queryBalanceResponse, err = s.Client.BankQueryClient.Balance(ctx, &queryBalanceRequest) - s.Require().NoError(err) - balanceAfter := queryBalanceResponse.Balance.Amount - - settledTime := streamRecordsAfter.User.CrudTimestamp - streamRecordsBefore.User.CrudTimestamp - balanceDelta := userRateRead.Add(userRateStore).MulRaw(settledTime) - - s.Require().Equal(streamRecordsAfter.User.NetflowRate.Sub(streamRecordsBefore.User.NetflowRate).Int64(), userRateStore.Int64()) - userBalanceChange := streamRecordsBefore.User.BufferBalance.Add(streamRecordsBefore.User.StaticBalance). - Sub(streamRecordsAfter.User.BufferBalance.Add(streamRecordsAfter.User.StaticBalance)) - userBalanceChange = userBalanceChange.Add(balanceBefore.Sub(balanceAfter)).Sub(gas) - s.Require().Equal(userBalanceChange.Int64(), balanceDelta.Int64()) - - familyDelta := streamRecordsAfter.GVGFamily.StaticBalance.Sub(streamRecordsBefore.GVGFamily.StaticBalance) - gvgDelta := streamRecordsAfter.GVG.StaticBalance.Sub(streamRecordsBefore.GVG.StaticBalance) - taxPoolDelta := streamRecordsAfter.Tax.StaticBalance.Sub(streamRecordsBefore.Tax.StaticBalance) - s.T().Log("familyDelta", familyDelta, "gvgDelta", gvgDelta, "taxPoolDelta", taxPoolDelta) - s.Require().True(familyDelta.Add(gvgDelta).Add(taxPoolDelta).Int64() >= balanceDelta.Int64()) // could exist other buckets/objects on the gvg & family -} - -func (s *PaymentTestSuite) TestStorageBill_CreateBucket_WithZeroNoneZeroReadQuota() { - var err error - 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(), - } - streamRecordsBefore := s.getStreamRecords(streamAddresses) - - params := s.queryParams() - - // case: create bucket with zero read quota - bucketName := s.createBucket(sp, user, 0) - - // bucket created - queryHeadBucketRequest := storagetypes.QueryHeadBucketRequest{ - BucketName: bucketName, - } - _, err = s.Client.HeadBucket(ctx, &queryHeadBucketRequest) - s.Require().NoError(err) - - // assertions - streamRecordsAfter := s.getStreamRecords(streamAddresses) - s.Require().Equal(streamRecordsAfter.User.StaticBalance, sdkmath.ZeroInt()) - s.Require().Equal(streamRecordsAfter.User.NetflowRate, streamRecordsBefore.User.NetflowRate) - s.Require().Equal(streamRecordsAfter.GVGFamily.NetflowRate, streamRecordsBefore.GVGFamily.NetflowRate) - s.Require().Equal(streamRecordsAfter.Tax.NetflowRate, streamRecordsBefore.Tax.NetflowRate) - - // case: create bucket with none zero read quota - bucketName = s.createBucket(sp, user, 10240) - - // bucket created - queryHeadBucketRequest = storagetypes.QueryHeadBucketRequest{ - BucketName: bucketName, - } - queryHeadBucketResponse, err := s.Client.HeadBucket(ctx, &queryHeadBucketRequest) - s.Require().NoError(err) - - // check price and rate calculation - queryGetSpStoragePriceByTimeResp, err := s.Client.QueryGetSpStoragePriceByTime(ctx, &sptypes.QueryGetSpStoragePriceByTimeRequest{ - SpAddr: sp.OperatorKey.GetAddr().String(), - Timestamp: queryHeadBucketResponse.BucketInfo.CreateAt, - }) - s.T().Logf("queryGetSpStoragePriceByTimeResp %s, err: %v", queryGetSpStoragePriceByTimeResp, err) - s.Require().NoError(err) - - readPrice := queryGetSpStoragePriceByTimeResp.SpStoragePrice.ReadPrice - readChargeRate := readPrice.MulInt(sdk.NewIntFromUint64(queryHeadBucketResponse.BucketInfo.ChargedReadQuota)).TruncateInt() - s.T().Logf("readPrice: %s, readChargeRate: %s", readPrice, readChargeRate) - taxRate := params.VersionedParams.ValidatorTaxRate.MulInt(readChargeRate).TruncateInt() - userTotalRate := readChargeRate.Add(taxRate) - - // assertions - streamRecordsAfter = s.getStreamRecords(streamAddresses) - s.Require().Equal(streamRecordsAfter.User.StaticBalance, sdkmath.ZeroInt()) - s.Require().Equal(streamRecordsAfter.User.NetflowRate.Sub(streamRecordsBefore.User.NetflowRate), userTotalRate.Neg()) - s.Require().Equal(streamRecordsAfter.GVGFamily.NetflowRate.Sub(streamRecordsBefore.GVGFamily.NetflowRate), readChargeRate) - s.Require().Equal(streamRecordsAfter.Tax.NetflowRate.Sub(streamRecordsBefore.Tax.NetflowRate), taxRate) - - expectedOutFlows := []paymenttypes.OutFlow{ - {ToAddress: family.VirtualPaymentAddress, Rate: readChargeRate}, - {ToAddress: paymenttypes.ValidatorTaxPoolAddress.String(), Rate: taxRate}, - } - userOutFlowsResponse, err := s.Client.OutFlows(ctx, &paymenttypes.QueryOutFlowsRequest{Account: user.GetAddr().String()}) - s.Require().NoError(err) - sort.Slice(userOutFlowsResponse.OutFlows, func(i, j int) bool { - return userOutFlowsResponse.OutFlows[i].ToAddress < userOutFlowsResponse.OutFlows[j].ToAddress - }) - sort.Slice(expectedOutFlows, func(i, j int) bool { - return expectedOutFlows[i].ToAddress < expectedOutFlows[j].ToAddress - }) - s.Require().Equal(expectedOutFlows, userOutFlowsResponse.OutFlows) -} - -func (s *PaymentTestSuite) TestStorageBill_CreateObject_WithZeroNoneZeroPayload() { - var err error - 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(), - } - - bucketName := s.createBucket(sp, user, 0) - - // case: create object with zero payload size - streamRecordsBefore := s.getStreamRecords(streamAddresses) - _, _, objectName, _, _, payloadSize := s.createObject(user, bucketName, true) - - // assertions - streamRecordsAfter := s.getStreamRecords(streamAddresses) - s.Require().Equal(streamRecordsAfter.User.StaticBalance, sdkmath.ZeroInt()) - s.Require().Equal(streamRecordsAfter.User.LockBalance, sdkmath.ZeroInt()) - gvgFamilyRate, gvgRate, taxRate, userTotalRate := s.calculateStorageRates(sp, bucketName, objectName, payloadSize, 0) - s.Require().Equal(streamRecordsAfter.User.NetflowRate.Sub(streamRecordsBefore.User.NetflowRate), userTotalRate.Neg()) - s.Require().Equal(streamRecordsAfter.GVGFamily.NetflowRate.Sub(streamRecordsBefore.GVGFamily.NetflowRate), gvgFamilyRate) - s.Require().Equal(streamRecordsAfter.GVG.NetflowRate.Sub(streamRecordsBefore.GVG.NetflowRate), gvgRate) - s.Require().Equal(streamRecordsAfter.Tax.NetflowRate.Sub(streamRecordsBefore.Tax.NetflowRate), taxRate) - - // case: create object with none zero payload size - streamRecordsBefore = s.getStreamRecords(streamAddresses) - _, _, objectName, _, _, payloadSize = s.createObject(user, bucketName, false) - - // assertions - streamRecordsAfter = s.getStreamRecords(streamAddresses) - s.Require().Equal(streamRecordsAfter.User.StaticBalance, sdkmath.ZeroInt()) - lockFee := s.calculateLockFee(sp, bucketName, objectName, payloadSize) - s.Require().Equal(streamRecordsAfter.User.LockBalance.Sub(streamRecordsBefore.User.LockBalance), lockFee) - s.Require().Equal(streamRecordsAfter.User.NetflowRate.Sub(streamRecordsBefore.User.NetflowRate).Int64(), int64(0)) - s.Require().Equal(streamRecordsAfter.GVGFamily.NetflowRate.Sub(streamRecordsBefore.GVGFamily.NetflowRate).Int64(), int64(0)) - s.Require().Equal(streamRecordsAfter.GVG.NetflowRate.Sub(streamRecordsBefore.GVG.NetflowRate).Int64(), int64(0)) - s.Require().Equal(streamRecordsAfter.Tax.NetflowRate.Sub(streamRecordsBefore.Tax.NetflowRate).Int64(), int64(0)) -} - -func (s *PaymentTestSuite) TestStorageBill_CreateObject_WithReserveTimeValidatorTaxRateChange() { - defer s.revertParams() - - var err error - 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(), - } - - bucketName := s.createBucket(sp, user, 0) - - // create object with none zero payload size - streamRecordsBefore := s.getStreamRecords(streamAddresses) - _, _, objectName, _, _, payloadSize := s.createObject(user, bucketName, false) - - // assertions - streamRecordsAfter := s.getStreamRecords(streamAddresses) - s.Require().Equal(streamRecordsAfter.User.StaticBalance, sdkmath.ZeroInt()) - lockFee := s.calculateLockFee(sp, bucketName, objectName, payloadSize) - s.Require().Equal(streamRecordsAfter.User.LockBalance.Sub(streamRecordsBefore.User.LockBalance), lockFee) - s.Require().Equal(streamRecordsAfter.User.NetflowRate.Sub(streamRecordsBefore.User.NetflowRate).Int64(), int64(0)) - s.Require().Equal(streamRecordsAfter.GVGFamily.NetflowRate.Sub(streamRecordsBefore.GVGFamily.NetflowRate).Int64(), int64(0)) - s.Require().Equal(streamRecordsAfter.GVG.NetflowRate.Sub(streamRecordsBefore.GVG.NetflowRate).Int64(), int64(0)) - s.Require().Equal(streamRecordsAfter.Tax.NetflowRate.Sub(streamRecordsBefore.Tax.NetflowRate).Int64(), int64(0)) - - // update params - - params := s.queryParams() - oldReserveTime := params.VersionedParams.ReserveTime - oldValidatorTaxRate := params.VersionedParams.ValidatorTaxRate - - params.VersionedParams.ReserveTime = oldReserveTime * 2 - params.VersionedParams.ValidatorTaxRate = oldValidatorTaxRate.MulInt64(2) - s.updateParams(params) - - // create another object after parameter changes - streamRecordsBefore = s.getStreamRecords(streamAddresses) - _, _, objectName, _, _, payloadSize = s.createObject(user, bucketName, false) - - // assertions - streamRecordsAfter = s.getStreamRecords(streamAddresses) - s.Require().Equal(streamRecordsAfter.User.StaticBalance, sdkmath.ZeroInt()) - lockFeeAfterParameterChange := s.calculateLockFee(sp, bucketName, objectName, payloadSize) - s.Require().Equal(streamRecordsAfter.User.LockBalance.Sub(streamRecordsBefore.User.LockBalance), lockFeeAfterParameterChange) - s.Require().Equal(streamRecordsAfter.User.NetflowRate.Sub(streamRecordsBefore.User.NetflowRate).Int64(), int64(0)) - s.Require().Equal(streamRecordsAfter.GVGFamily.NetflowRate.Sub(streamRecordsBefore.GVGFamily.NetflowRate).Int64(), int64(0)) - s.Require().Equal(streamRecordsAfter.GVG.NetflowRate.Sub(streamRecordsBefore.GVG.NetflowRate).Int64(), int64(0)) - s.Require().Equal(streamRecordsAfter.Tax.NetflowRate.Sub(streamRecordsBefore.Tax.NetflowRate).Int64(), int64(0)) - s.Require().True(lockFeeAfterParameterChange.GT(lockFee.MulRaw(2))) -} - -func (s *PaymentTestSuite) TestStorageBill_CancelCreateObject() { - var err error - 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(), - } - - bucketName := s.createBucket(sp, user, 0) - - // create object with none zero payload size - streamRecordsBefore := s.getStreamRecords(streamAddresses) - _, _, objectName, _, _, payloadSize := s.createObject(user, bucketName, false) - - // assertions - streamRecordsAfter := s.getStreamRecords(streamAddresses) - s.Require().Equal(streamRecordsAfter.User.StaticBalance, sdkmath.ZeroInt()) - lockFee := s.calculateLockFee(sp, bucketName, objectName, payloadSize) - s.Require().Equal(streamRecordsAfter.User.LockBalance.Sub(streamRecordsBefore.User.LockBalance), lockFee) - s.Require().Equal(streamRecordsAfter.User.NetflowRate.Sub(streamRecordsBefore.User.NetflowRate).Int64(), int64(0)) - s.Require().Equal(streamRecordsAfter.GVGFamily.NetflowRate.Sub(streamRecordsBefore.GVGFamily.NetflowRate).Int64(), int64(0)) - s.Require().Equal(streamRecordsAfter.GVG.NetflowRate.Sub(streamRecordsBefore.GVG.NetflowRate).Int64(), int64(0)) - s.Require().Equal(streamRecordsAfter.Tax.NetflowRate.Sub(streamRecordsBefore.Tax.NetflowRate).Int64(), int64(0)) - - // cancel create object - s.cancelCreateObject(user, bucketName, objectName) - - // assertions - streamRecordsAfter = s.getStreamRecords(streamAddresses) - s.Require().Equal(streamRecordsAfter.User.StaticBalance, lockFee) - s.Require().Equal(streamRecordsAfter.User.LockBalance.Int64(), int64(0)) - s.Require().Equal(streamRecordsAfter.User.NetflowRate.Sub(streamRecordsBefore.User.NetflowRate).Int64(), int64(0)) - s.Require().Equal(streamRecordsAfter.GVGFamily.NetflowRate.Sub(streamRecordsBefore.GVGFamily.NetflowRate).Int64(), int64(0)) - s.Require().Equal(streamRecordsAfter.GVG.NetflowRate.Sub(streamRecordsBefore.GVG.NetflowRate).Int64(), int64(0)) - s.Require().Equal(streamRecordsAfter.Tax.NetflowRate.Sub(streamRecordsBefore.Tax.NetflowRate).Int64(), int64(0)) -} - -func (s *PaymentTestSuite) TestStorageBill_SealObject_WithoutPriceChange() { - var err error - 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(), - } - - bucketName := s.createBucket(sp, user, 0) - - // create object with none zero payload size - streamRecordsBefore := s.getStreamRecords(streamAddresses) - _, _, objectName, objectId, checksums, payloadSize := s.createObject(user, bucketName, false) - - // assertions - streamRecordsAfter := s.getStreamRecords(streamAddresses) - s.Require().Equal(streamRecordsAfter.User.StaticBalance, sdkmath.ZeroInt()) - lockFee := s.calculateLockFee(sp, bucketName, objectName, payloadSize) - s.Require().Equal(streamRecordsAfter.User.LockBalance.Sub(streamRecordsBefore.User.LockBalance), lockFee) - s.Require().Equal(streamRecordsAfter.User.NetflowRate.Sub(streamRecordsBefore.User.NetflowRate).Int64(), int64(0)) - s.Require().Equal(streamRecordsAfter.GVGFamily.NetflowRate.Sub(streamRecordsBefore.GVGFamily.NetflowRate).Int64(), int64(0)) - s.Require().Equal(streamRecordsAfter.GVG.NetflowRate.Sub(streamRecordsBefore.GVG.NetflowRate).Int64(), int64(0)) - s.Require().Equal(streamRecordsAfter.Tax.NetflowRate.Sub(streamRecordsBefore.Tax.NetflowRate).Int64(), int64(0)) - - // case: seal object without price change - s.sealObject(sp, gvg, bucketName, objectName, objectId, checksums) - - // assertions - streamRecordsAfter = s.getStreamRecords(streamAddresses) - s.Require().Equal(streamRecordsAfter.User.StaticBalance, sdkmath.ZeroInt()) - s.Require().Equal(streamRecordsAfter.User.LockBalance, sdkmath.ZeroInt()) - gvgFamilyRate, gvgRate, taxRate, userTotalRate := s.calculateStorageRates(sp, bucketName, objectName, payloadSize, 0) - s.Require().Equal(streamRecordsAfter.User.NetflowRate.Sub(streamRecordsBefore.User.NetflowRate), userTotalRate.Neg()) - s.Require().Equal(streamRecordsAfter.GVGFamily.NetflowRate.Sub(streamRecordsBefore.GVGFamily.NetflowRate), gvgFamilyRate) - s.Require().Equal(streamRecordsAfter.GVG.NetflowRate.Sub(streamRecordsBefore.GVG.NetflowRate), gvgRate) - s.Require().Equal(streamRecordsAfter.Tax.NetflowRate.Sub(streamRecordsBefore.Tax.NetflowRate), taxRate) -} - -func (s *PaymentTestSuite) TestStorageBill_SealObject_WithPriceChange() { - var err error - 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(), - } - - bucketName := s.createBucket(sp, user, 102400) - - // case: seal object with read price change and storage price change - _, _, objectName, objectId, checksums, payloadSize := s.createObject(user, bucketName, false) - - priceRes, err := s.Client.QueryGetSpStoragePriceByTime(ctx, &sptypes.QueryGetSpStoragePriceByTimeRequest{ - SpAddr: sp.OperatorKey.GetAddr().String(), - Timestamp: 0, - }) - s.Require().NoError(err) - // update new price - msgUpdatePrice := &sptypes.MsgUpdateSpStoragePrice{ - SpAddress: sp.OperatorKey.GetAddr().String(), - ReadPrice: priceRes.SpStoragePrice.ReadPrice.MulInt64(2), - FreeReadQuota: priceRes.SpStoragePrice.FreeReadQuota, - StorePrice: priceRes.SpStoragePrice.StorePrice.MulInt64(2), - } - s.SendTxBlock(sp.OperatorKey, msgUpdatePrice) - - streamRecordsBefore := s.getStreamRecords(streamAddresses) - gvgFamilyRateReadBefore, taxRateReadBefore, userTotalRateReadBefore := s.calculateReadRates(sp, bucketName) - - // seal object - s.sealObject(sp, gvg, bucketName, objectName, objectId, checksums) - - // assertions - streamRecordsAfter := s.getStreamRecords(streamAddresses) - gvgFamilyRateReadAfter, taxRateReadAfter, userTotalRateReadAfter := s.calculateReadRatesCurrentTimestamp(sp, bucketName) - gvgFamilyRateStore, gvgRateStore, taxRateStore, userTotalRateStore := s.calculateStorageRatesCurrentTimestamp(sp, bucketName, objectName, payloadSize) - - s.Require().Equal(streamRecordsAfter.User.StaticBalance, sdkmath.ZeroInt()) - s.Require().Equal(streamRecordsAfter.User.LockBalance, sdkmath.ZeroInt()) - - s.Require().Equal(streamRecordsAfter.User.NetflowRate.Sub(streamRecordsBefore.User.NetflowRate), userTotalRateReadAfter.Sub(userTotalRateReadBefore).Add(userTotalRateStore).Neg()) - s.Require().Equal(streamRecordsAfter.GVG.NetflowRate.Sub(streamRecordsBefore.GVG.NetflowRate), gvgRateStore) - s.Require().Equal(streamRecordsAfter.GVGFamily.NetflowRate.Sub(streamRecordsBefore.GVGFamily.NetflowRate), gvgFamilyRateReadAfter.Sub(gvgFamilyRateReadBefore).Add(gvgFamilyRateStore)) - s.Require().Equal(streamRecordsAfter.Tax.NetflowRate.Sub(streamRecordsBefore.Tax.NetflowRate), taxRateReadAfter.Sub(taxRateReadBefore).Add(taxRateStore)) -} - -func (s *PaymentTestSuite) TestStorageBill_SealObject_WithPriceChangeValidatorTaxRateChange() { - defer s.revertParams() - - var err error - 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(), - } - - bucketName := s.createBucket(sp, user, 102400) - - // case: seal object with read price change and storage price change - _, _, objectName, objectId, checksums, payloadSize := s.createObject(user, bucketName, false) - - priceRes, err := s.Client.QueryGetSpStoragePriceByTime(ctx, &sptypes.QueryGetSpStoragePriceByTimeRequest{ - SpAddr: sp.OperatorKey.GetAddr().String(), - Timestamp: 0, - }) - s.Require().NoError(err) - // update new price - msgUpdatePrice := &sptypes.MsgUpdateSpStoragePrice{ - SpAddress: sp.OperatorKey.GetAddr().String(), - ReadPrice: priceRes.SpStoragePrice.ReadPrice.MulInt64(2), - FreeReadQuota: priceRes.SpStoragePrice.FreeReadQuota, - StorePrice: priceRes.SpStoragePrice.StorePrice.MulInt64(2), - } - s.SendTxBlock(sp.OperatorKey, msgUpdatePrice) - - streamRecordsBefore := s.getStreamRecords(streamAddresses) - gvgFamilyRateReadBefore, taxRateReadBefore, userTotalRateReadBefore := s.calculateReadRates(sp, bucketName) - - // seal object - s.sealObject(sp, gvg, bucketName, objectName, objectId, checksums) - - // assertions - streamRecordsAfter := s.getStreamRecords(streamAddresses) - gvgFamilyRateReadAfter, taxRateReadAfter, userTotalRateReadAfter := s.calculateReadRatesCurrentTimestamp(sp, bucketName) - gvgFamilyRateStore, gvgRateStore, taxRateStore, userTotalRateStore := s.calculateStorageRatesCurrentTimestamp(sp, bucketName, objectName, payloadSize) - - s.Require().Equal(streamRecordsAfter.User.StaticBalance, sdkmath.ZeroInt()) - s.Require().Equal(streamRecordsAfter.User.LockBalance, sdkmath.ZeroInt()) - - s.Require().Equal(streamRecordsAfter.User.NetflowRate.Sub(streamRecordsBefore.User.NetflowRate), userTotalRateReadAfter.Sub(userTotalRateReadBefore).Add(userTotalRateStore).Neg()) - s.Require().Equal(streamRecordsAfter.GVG.NetflowRate.Sub(streamRecordsBefore.GVG.NetflowRate), gvgRateStore) - s.Require().Equal(streamRecordsAfter.GVGFamily.NetflowRate.Sub(streamRecordsBefore.GVGFamily.NetflowRate), gvgFamilyRateReadAfter.Sub(gvgFamilyRateReadBefore).Add(gvgFamilyRateStore)) - s.Require().Equal(streamRecordsAfter.Tax.NetflowRate.Sub(streamRecordsBefore.Tax.NetflowRate), taxRateReadAfter.Sub(taxRateReadBefore).Add(taxRateStore)) - - // update params - params := s.queryParams() - oldReserveTime := params.VersionedParams.ReserveTime - oldValidatorTaxRate := params.VersionedParams.ValidatorTaxRate - - params.VersionedParams.ReserveTime = oldReserveTime * 2 - params.VersionedParams.ValidatorTaxRate = oldValidatorTaxRate.MulInt64(2) - s.updateParams(params) - - _, _, objectName, objectId, checksums, payloadSize = s.createObject(user, bucketName, false) - s.sealObject(sp, gvg, bucketName, objectName, objectId, checksums) - - // assertions - streamRecordsAfter = s.getStreamRecords(streamAddresses) - gvgFamilyRateReadAfter, taxRateReadAfter, userTotalRateReadAfter = s.calculateReadRatesCurrentTimestamp(sp, bucketName) - 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()) - - s.Require().Equal(streamRecordsAfter.User.NetflowRate.Sub(streamRecordsBefore.User.NetflowRate), userTotalRateReadAfter.Sub(userTotalRateReadBefore).Add(userTotalRateStore).Neg()) - s.Require().Equal(streamRecordsAfter.GVG.NetflowRate.Sub(streamRecordsBefore.GVG.NetflowRate), gvgRateStore) - s.Require().Equal(streamRecordsAfter.GVGFamily.NetflowRate.Sub(streamRecordsBefore.GVGFamily.NetflowRate), gvgFamilyRateReadAfter.Sub(gvgFamilyRateReadBefore).Add(gvgFamilyRateStore)) - s.Require().Equal(streamRecordsAfter.Tax.NetflowRate.Sub(streamRecordsBefore.Tax.NetflowRate), taxRateReadAfter.Sub(taxRateReadBefore).Add(taxRateStore)) -} - -func (s *PaymentTestSuite) TestStorageBill_FullLifecycle() { - defer s.revertParams() - - var err error - 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] - - // query storage price - priceRes, err := s.Client.QueryGetSpStoragePriceByTime(ctx, &sptypes.QueryGetSpStoragePriceByTimeRequest{ - SpAddr: sp.OperatorKey.GetAddr().String(), - Timestamp: 0, - }) - s.Require().NoError(err) - s.T().Log("price", priceRes.SpStoragePrice) - - streamAddresses := []string{ - user.GetAddr().String(), - family.VirtualPaymentAddress, - gvg.VirtualPaymentAddress, - paymenttypes.ValidatorTaxPoolAddress.String(), - } - streamRecordsBefore := s.getStreamRecords(streamAddresses) - - // full lifecycle - bucketName1 := s.createBucket(sp, user, 0) - _, _, objectName1, _, _, _ := s.createObject(user, bucketName1, true) - _, _, objectName2, objectId2, checksums2, _ := s.createObject(user, bucketName1, false) - s.sealObject(sp, gvg, bucketName1, objectName2, objectId2, checksums2) - - bucketName2 := s.createBucket(sp, user, 1024) - _, _, objectName3, objectId3, checksums3, _ := s.createObject(user, bucketName2, false) - s.sealObject(sp, gvg, bucketName2, objectName3, objectId3, checksums3) - - // update params - params := s.queryParams() - params.VersionedParams.ReserveTime = params.VersionedParams.ReserveTime * 3 - params.ForcedSettleTime = params.ForcedSettleTime * 2 - s.updateParams(params) - - _, _, objectName4, objectId4, checksums4, _ := s.createObject(user, bucketName2, false) - s.sealObject(sp, gvg, bucketName2, objectName4, objectId4, checksums4) - - bucketName3 := s.createBucket(sp, user, 1024) - _, _, objectName5, objectId5, checksums5, _ := s.createObject(user, bucketName3, false) - s.sealObject(sp, gvg, bucketName3, objectName5, objectId5, checksums5) - - // update new price - msgUpdatePrice := &sptypes.MsgUpdateSpStoragePrice{ - SpAddress: sp.OperatorKey.GetAddr().String(), - ReadPrice: priceRes.SpStoragePrice.ReadPrice, - FreeReadQuota: priceRes.SpStoragePrice.FreeReadQuota, - StorePrice: priceRes.SpStoragePrice.StorePrice.MulInt64(10000), - } - s.SendTxBlock(sp.OperatorKey, msgUpdatePrice) - - // update params - params = s.queryParams() - params.VersionedParams.ReserveTime = params.VersionedParams.ReserveTime / 2 - params.ForcedSettleTime = params.ForcedSettleTime / 3 - s.updateParams(params) - - _, _, objectName6, objectId6, checksums6, _ := s.createObject(user, bucketName3, false) - s.sealObject(sp, gvg, bucketName3, objectName6, objectId6, checksums6) - - bucketName4 := s.createBucket(sp, user, 1024) - _, _, objectName7, objectId7, checksums7, _ := s.createObject(user, bucketName4, false) - s.sealObject(sp, gvg, bucketName4, objectName7, objectId7, checksums7) - - // update params - params = s.queryParams() - params.VersionedParams.ValidatorTaxRate = params.VersionedParams.ValidatorTaxRate.MulInt64(2) - s.updateParams(params) - - _, _, objectName8, objectId8, checksums8, _ := s.createObject(user, bucketName4, false) - s.sealObject(sp, gvg, bucketName4, objectName8, objectId8, checksums8) - - time.Sleep(3 * time.Second) - - _ = s.deleteObject(user, bucketName1, objectName1) - _ = s.deleteObject(user, bucketName1, objectName2) - - // update params - params = s.queryParams() - params.VersionedParams.ValidatorTaxRate = params.VersionedParams.ValidatorTaxRate.MulInt64(3) - s.updateParams(params) - - _ = s.deleteObject(user, bucketName2, objectName3) - _ = s.deleteObject(user, bucketName2, objectName4) - err = s.deleteBucket(user, bucketName1) - s.Require().Error(err) - err = s.deleteBucket(user, bucketName2) - s.Require().Error(err) - - _ = s.deleteObject(user, bucketName3, objectName5) - _ = s.deleteObject(user, bucketName3, objectName6) - _ = s.deleteObject(user, bucketName4, objectName7) - _ = s.deleteObject(user, bucketName4, objectName8) - err = s.deleteBucket(user, bucketName3) - s.Require().Error(err) - err = s.deleteBucket(user, bucketName4) - s.Require().Error(err) - - // assertions - streamRecordsAfter := s.getStreamRecords(streamAddresses) - s.Require().True(!streamRecordsAfter.User.StaticBalance.IsZero()) - s.Require().Equal(streamRecordsAfter.User.NetflowRate.Sub(streamRecordsBefore.User.NetflowRate).Int64(), int64(0)) - s.Require().Equal(streamRecordsAfter.GVGFamily.NetflowRate.Sub(streamRecordsBefore.GVGFamily.NetflowRate).Int64(), int64(0)) - s.Require().Equal(streamRecordsAfter.GVG.NetflowRate.Sub(streamRecordsBefore.GVG.NetflowRate).Int64(), int64(0)) - s.Require().Equal(streamRecordsAfter.Tax.NetflowRate.Sub(streamRecordsBefore.Tax.NetflowRate).Int64(), int64(0)) - - // revert price - msgUpdatePrice = &sptypes.MsgUpdateSpStoragePrice{ - SpAddress: sp.OperatorKey.GetAddr().String(), - ReadPrice: priceRes.SpStoragePrice.ReadPrice, - FreeReadQuota: priceRes.SpStoragePrice.FreeReadQuota, - StorePrice: priceRes.SpStoragePrice.StorePrice, - } - s.SendTxBlock(sp.OperatorKey, msgUpdatePrice) -} - func (s *PaymentTestSuite) TestVirtualGroup_Settle() { var err error ctx := context.Background() @@ -3038,6 +1739,8 @@ func (s *PaymentTestSuite) TestDiscontinue_InBlocks_WithPriceChangeReserveTimeCh s.Require().NoError(err) s.Require().Equal(queryHeadObjectResponse.ObjectInfo.ObjectStatus, storagetypes.OBJECT_STATUS_SEALED) time.Sleep(200 * time.Millisecond) + userStream := s.getStreamRecord(user.GetAddr().String()) + s.Require().True(userStream.LockBalance.IsPositive()) } queryBalanceRequest := banktypes.QueryBalanceRequest{Denom: s.Config.Denom, Address: user.GetAddr().String()} @@ -3122,6 +1825,238 @@ func (s *PaymentTestSuite) TestDiscontinue_InBlocks_WithPriceChangeReserveTimeCh s.SendTxBlock(sp.OperatorKey, msgUpdatePrice) } +func (s *PaymentTestSuite) TestDiscontinue_MultiObjects() { + 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(), + } + streamRecordsBefore := s.getStreamRecords(streamAddresses) + + // create bucket + bucketName := s.createBucket(sp, user, 0) + objectIds := []sdkmath.Uint{} + + // create objects + for i := 0; i < 3; i++ { + _, _, objectName, objectId, _, _ := s.createObject(user, bucketName, false) + queryHeadObjectRequest := storagetypes.QueryHeadObjectRequest{ + BucketName: bucketName, + ObjectName: objectName, + } + queryHeadObjectResponse, err := s.Client.HeadObject(ctx, &queryHeadObjectRequest) + s.Require().NoError(err) + s.Require().Equal(queryHeadObjectResponse.ObjectInfo.ObjectStatus, storagetypes.OBJECT_STATUS_CREATED) + time.Sleep(200 * time.Millisecond) + objectIds = append(objectIds, objectId) + } + + // create & seal objects + for i := 0; i < 3; i++ { + _, _, objectName, objectId, checksums, _ := s.createObject(user, bucketName, false) + s.sealObject(sp, gvg, bucketName, objectName, objectId, checksums) + queryHeadObjectRequest := storagetypes.QueryHeadObjectRequest{ + BucketName: bucketName, + ObjectName: objectName, + } + 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) + objectIds = append(objectIds, objectId) + } + + 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()) + + // for payment + time.Sleep(2 * time.Second) + + // force objects + msgs := make([]sdk.Msg, 0) + for _, id := range objectIds { + msgDiscontinueObject := storagetypes.NewMsgDiscontinueObject(sp.GcKey.GetAddr(), bucketName, []sdkmath.Uint{id}, "test") + msgs = append(msgs, msgDiscontinueObject) + } + msgs = append(msgs, storagetypes.NewMsgDiscontinueBucket(sp.GcKey.GetAddr(), bucketName, "test")) + txRes := s.SendTxBlock(sp.GcKey, msgs...) + deleteAt := filterDiscontinueObjectEventFromTx(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+5 { + break + } + } + + _, err = s.Client.HeadBucket(ctx, &storagetypes.QueryHeadBucketRequest{BucketName: bucketName}) + s.Require().ErrorContains(err, "No such bucket") + streamRecordsAfter := s.getStreamRecords(streamAddresses) + s.Require().Equal(streamRecordsAfter.User.NetflowRate.Sub(streamRecordsBefore.User.NetflowRate).Int64(), int64(0)) + s.Require().Equal(streamRecordsAfter.GVGFamily.NetflowRate.Sub(streamRecordsBefore.GVGFamily.NetflowRate).Int64(), int64(0)) + s.Require().Equal(streamRecordsAfter.GVG.NetflowRate.Sub(streamRecordsBefore.GVG.NetflowRate).Int64(), int64(0)) + s.Require().Equal(streamRecordsAfter.Tax.NetflowRate.Sub(streamRecordsBefore.Tax.NetflowRate).Int64(), int64(0)) + s.Require().True(streamRecordsAfter.User.LockBalance.IsZero()) +} + +func (s *PaymentTestSuite) TestDiscontinue_MultiBuckets() { + 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(), + } + streamRecordsBefore := s.getStreamRecords(streamAddresses) + + bucketNames := []string{} + // create bucket + bucketName1 := s.createBucket(sp, user, 1023) + bucketNames = append(bucketNames, bucketName1) + + // create objects + for i := 0; i < 2; i++ { + _, _, objectName, _, _, _ := s.createObject(user, bucketName1, false) + queryHeadObjectRequest := storagetypes.QueryHeadObjectRequest{ + BucketName: bucketName1, + ObjectName: objectName, + } + queryHeadObjectResponse, err := s.Client.HeadObject(ctx, &queryHeadObjectRequest) + s.Require().NoError(err) + s.Require().Equal(queryHeadObjectResponse.ObjectInfo.ObjectStatus, storagetypes.OBJECT_STATUS_CREATED) + time.Sleep(200 * time.Millisecond) + } + + // create & seal objects + for i := 0; i < 2; i++ { + _, _, objectName, objectId, checksums, _ := s.createObject(user, bucketName1, false) + s.sealObject(sp, gvg, bucketName1, objectName, objectId, checksums) + queryHeadObjectRequest := storagetypes.QueryHeadObjectRequest{ + BucketName: bucketName1, + ObjectName: objectName, + } + 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) + } + + // create bucket + bucketName2 := s.createBucket(sp, user, 21023) + bucketNames = append(bucketNames, bucketName2) + + // create bucket + bucketName3 := s.createBucket(sp, user, 0) + bucketNames = append(bucketNames, bucketName3) + + // create bucket + bucketName4 := s.createBucket(sp, user, 55) + bucketNames = append(bucketNames, bucketName4) + + 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()) + + // for payment + time.Sleep(2 * time.Second) + + // force objects + msgs := make([]sdk.Msg, 0) + for _, bucketName := range bucketNames { + msgDiscontinueBucket := storagetypes.NewMsgDiscontinueBucket(sp.GcKey.GetAddr(), bucketName, "test") + msgs = append(msgs, msgDiscontinueBucket) + } + txRes := s.SendTxBlock(sp.GcKey, msgs...) + 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+5 { + break + } + } + + for _, bucketName := range bucketNames { + _, err = s.Client.HeadBucket(ctx, &storagetypes.QueryHeadBucketRequest{BucketName: bucketName}) + s.Require().ErrorContains(err, "No such bucket") + } + streamRecordsAfter := s.getStreamRecords(streamAddresses) + s.Require().Equal(streamRecordsAfter.User.NetflowRate.Sub(streamRecordsBefore.User.NetflowRate).Int64(), int64(0)) + s.Require().Equal(streamRecordsAfter.GVGFamily.NetflowRate.Sub(streamRecordsBefore.GVGFamily.NetflowRate).Int64(), int64(0)) + s.Require().Equal(streamRecordsAfter.GVG.NetflowRate.Sub(streamRecordsBefore.GVG.NetflowRate).Int64(), int64(0)) + s.Require().Equal(streamRecordsAfter.Tax.NetflowRate.Sub(streamRecordsBefore.Tax.NetflowRate).Int64(), int64(0)) + s.Require().True(streamRecordsAfter.User.LockBalance.IsZero()) +} + func TestPaymentTestSuite(t *testing.T) { suite.Run(t, new(PaymentTestSuite)) } @@ -3576,6 +2511,20 @@ func (s *PaymentTestSuite) sealObject(sp *core.StorageProvider, gvg *virtualgrou return gvg } +func (s *PaymentTestSuite) rejectSealObject(sp *core.StorageProvider, gvg *virtualgrouptypes.GlobalVirtualGroup, bucketName, objectName string) { + msgRejectSealObject := storagetypes.NewMsgRejectUnsealedObject(sp.SealKey.GetAddr(), bucketName, objectName) + + s.T().Logf("msg %s", msgRejectSealObject.String()) + s.SendTxBlock(sp.SealKey, msgRejectSealObject) + + queryHeadObjectRequest := storagetypes.QueryHeadObjectRequest{ + BucketName: bucketName, + ObjectName: objectName, + } + _, err := s.Client.HeadObject(context.Background(), &queryHeadObjectRequest) + s.Require().Error(err) +} + func (s *PaymentTestSuite) deleteObject(user keys.KeyManager, bucketName, objectName string) error { msgDeleteObject := storagetypes.NewMsgDeleteObject(user.GetAddr(), bucketName, objectName) s.SendTxBlock(user, msgDeleteObject) diff --git a/e2e/tests/permission_test.go b/e2e/tests/permission_test.go index 38285a11e..8b48b38cb 100644 --- a/e2e/tests/permission_test.go +++ b/e2e/tests/permission_test.go @@ -1110,6 +1110,109 @@ func (s *StorageTestSuite) TestStalePermissionForAccountGC() { s.Require().ErrorContains(err, "No such Policy") } +func (s *StorageTestSuite) TestDeleteObjectPolicy() { + var err error + ctx := context.Background() + user1 := s.GenAndChargeAccounts(1, 1000000)[0] + + _, owner, bucketName, _, objectName, objectId := s.createObjectWithVisibility(storagetypes.VISIBILITY_TYPE_PUBLIC_READ) + + principal := types.NewPrincipalWithAccount(user1.GetAddr()) + + // Put bucket policy + bucketStatement := &types.Statement{ + Actions: []types.ActionType{types.ACTION_DELETE_BUCKET}, + Effect: types.EFFECT_ALLOW, + } + msgPutBucketPolicy := storagetypes.NewMsgPutPolicy(owner.GetAddr(), types2.NewBucketGRN(bucketName).String(), + principal, []*types.Statement{bucketStatement}, nil) + s.SendTxBlock(owner, msgPutBucketPolicy) + + // Put Object policy + objectStatement := &types.Statement{ + Actions: []types.ActionType{types.ACTION_DELETE_OBJECT}, + Effect: types.EFFECT_ALLOW, + } + msgPutObjectPolicy := storagetypes.NewMsgPutPolicy(owner.GetAddr(), types2.NewObjectGRN(bucketName, objectName).String(), + principal, []*types.Statement{objectStatement}, nil) + s.SendTxBlock(owner, msgPutObjectPolicy) + + // Query the policy which is enforced on bucket and object + grn1 := types2.NewObjectGRN(bucketName, objectName) + queryPolicyForAccountResp, err := s.Client.QueryPolicyForAccount(ctx, &storagetypes.QueryPolicyForAccountRequest{Resource: grn1.String(), + PrincipalAddress: user1.GetAddr().String()}) + s.Require().NoError(err) + s.Require().Equal(objectId, queryPolicyForAccountResp.Policy.ResourceId) + + // Delete object policy + msgDeletePolicy := storagetypes.NewMsgDeletePolicy(owner.GetAddr(), grn1.String(), types.NewPrincipalWithAccount(user1.GetAddr())) + s.SendTxBlock(owner, msgDeletePolicy) + + // verify permission + verifyPermReq := storagetypes.QueryVerifyPermissionRequest{ + Operator: user1.GetAddr().String(), + BucketName: bucketName, + ObjectName: objectName, + ActionType: types.ACTION_DELETE_OBJECT, + } + verifyPermResp, err := s.Client.VerifyPermission(ctx, &verifyPermReq) + s.T().Logf("resp: %s, rep %s", verifyPermReq.String(), verifyPermResp.String()) + s.Require().NoError(err) + s.Require().Equal(verifyPermResp.Effect, types.EFFECT_DENY) +} + +func (s *StorageTestSuite) TestDeleteGroupPolicy() { + var err error + ctx := context.Background() + + user := s.GenAndChargeAccounts(4, 1000000) + owner := user[0] + _ = s.BaseSuite.PickStorageProvider() + + // Create Group + testGroupName := "testGroup" + msgCreateGroup := storagetypes.NewMsgCreateGroup(owner.GetAddr(), testGroupName, + []sdk.AccAddress{user[1].GetAddr(), user[2].GetAddr(), user[3].GetAddr()}, + "") + s.SendTxBlock(owner, msgCreateGroup) + + // Head Group + headGroupRequest := storagetypes.QueryHeadGroupRequest{GroupOwner: owner.GetAddr().String(), GroupName: testGroupName} + headGroupResponse, err := s.Client.HeadGroup(ctx, &headGroupRequest) + s.Require().NoError(err) + s.Require().Equal(headGroupResponse.GroupInfo.GroupName, testGroupName) + s.Require().True(owner.GetAddr().Equals(sdk.MustAccAddressFromHex(headGroupResponse.GroupInfo.Owner))) + s.T().Logf("GroupInfo: %s", headGroupResponse.GetGroupInfo().String()) + + // Put policy + groupStatement := &types.Statement{ + Actions: []types.ActionType{types.ACTION_UPDATE_GROUP_MEMBER}, + Effect: types.EFFECT_ALLOW, + } + msgPutGroupPolicy := storagetypes.NewMsgPutPolicy(owner.GetAddr(), types2.NewGroupGRN(owner.GetAddr(), testGroupName).String(), + types.NewPrincipalWithAccount(user[1].GetAddr()), []*types.Statement{groupStatement}, nil) + s.SendTxBlock(owner, msgPutGroupPolicy) + + // Query for policy + grn := types2.NewGroupGRN(owner.GetAddr(), testGroupName) + queryPolicyForAccountReq := storagetypes.QueryPolicyForAccountRequest{Resource: grn.String(), + PrincipalAddress: user[1].GetAddr().String()} + + queryPolicyForAccountResp, err := s.Client.QueryPolicyForAccount(ctx, &queryPolicyForAccountReq) + s.Require().NoError(err) + s.Require().Equal(queryPolicyForAccountResp.Policy.ResourceType, resource.RESOURCE_TYPE_GROUP) + s.T().Logf("policy is %s", queryPolicyForAccountResp.Policy.String()) + + // Delete policy + msgDeletePolicy := storagetypes.NewMsgDeletePolicy(owner.GetAddr(), grn.String(), types.NewPrincipalWithAccount(user[1].GetAddr())) + s.SendTxBlock(owner, msgDeletePolicy) + + // verify permission + _, err = s.Client.QueryPolicyForAccount(ctx, &queryPolicyForAccountReq) + s.Require().Error(err) + s.Require().ErrorContains(err, "No such Policy") +} + // When resources are deleted, policies which associated with group and resources(Bucket and Object) // will also be garbage collected. func (s *StorageTestSuite) TestStalePermissionForGroupGC() { diff --git a/e2e/tests/storage_bill_test.go b/e2e/tests/storage_bill_test.go index 0c73fe4d2..b55bf930b 100644 --- a/e2e/tests/storage_bill_test.go +++ b/e2e/tests/storage_bill_test.go @@ -1,7 +1,9 @@ package tests import ( + "bytes" "context" + "fmt" "math" "sort" "time" @@ -21,6 +23,1378 @@ import ( virtualgrouptypes "github.com/bnb-chain/greenfield/x/virtualgroup/types" ) +func (s *PaymentTestSuite) TestStorageBill_DeleteBucket_WithReadQuota() { + var err error + 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(), + } + + // CreateBucket + chargedReadQuota := uint64(100) + bucketName := storagetestutils.GenRandomBucketName() + msgCreateBucket := storagetypes.NewMsgCreateBucket( + user.GetAddr(), bucketName, storagetypes.VISIBILITY_TYPE_PUBLIC_READ, sp.OperatorKey.GetAddr(), + nil, math.MaxUint, nil, chargedReadQuota) + msgCreateBucket.PrimarySpApproval.GlobalVirtualGroupFamilyId = gvg.FamilyId + msgCreateBucket.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateBucket.GetApprovalBytes()) + s.Require().NoError(err) + s.SendTxBlock(user, msgCreateBucket) + + streamRecordsBeforeDelete := s.getStreamRecords(streamAddresses) + s.T().Logf("streamRecordsBeforeDelete: %s", core.YamlString(streamRecordsBeforeDelete)) + s.Require().NotEqual(streamRecordsBeforeDelete.User.NetflowRate.String(), "0") + + // DeleteBucket + msgDeleteBucket := storagetypes.NewMsgDeleteBucket(user.GetAddr(), bucketName) + s.SendTxBlock(user, msgDeleteBucket) + + // check the billing change + streamRecordsAfterDelete := s.getStreamRecords(streamAddresses) + s.T().Logf("streamRecordsBeforeDelete: %s", core.YamlString(streamRecordsAfterDelete)) + s.Require().Equal(streamRecordsAfterDelete.User.NetflowRate.String(), "0") +} + +func (s *PaymentTestSuite) TestStorageBill_Smoke() { + var err error + 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(), + } + streamRecordsBeforeCreateBucket := s.getStreamRecords(streamAddresses) + s.T().Logf("streamRecordsBeforeCreateBucket: %s", core.YamlString(streamRecordsBeforeCreateBucket)) + + params := s.queryParams() + + // create bucket + bucketName := storagetestutils.GenRandomBucketName() + bucketChargedReadQuota := uint64(1000) + msgCreateBucket := storagetypes.NewMsgCreateBucket( + user.GetAddr(), bucketName, storagetypes.VISIBILITY_TYPE_PRIVATE, sp.OperatorKey.GetAddr(), + nil, math.MaxUint, nil, 0) + msgCreateBucket.ChargedReadQuota = bucketChargedReadQuota + msgCreateBucket.PrimarySpApproval.GlobalVirtualGroupFamilyId = gvg.FamilyId + msgCreateBucket.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateBucket.GetApprovalBytes()) + s.Require().NoError(err) + s.SendTxBlock(user, msgCreateBucket) + + // check bill after creating bucket + userBankAccount, err := s.Client.Balance(ctx, &banktypes.QueryBalanceRequest{ + Address: user.GetAddr().String(), + Denom: s.Config.Denom, + }) + s.Require().NoError(err) + s.T().Logf("user bank account %s", userBankAccount) + + streamRecordsAfterCreateBucket := s.getStreamRecords(streamAddresses) + userStreamRecord := streamRecordsAfterCreateBucket.User + s.Require().Equal(userStreamRecord.StaticBalance, sdkmath.ZeroInt()) + + // check price and rate calculation + queryHeadBucketRequest := storagetypes.QueryHeadBucketRequest{ + BucketName: bucketName, + } + queryHeadBucketResponse, err := s.Client.HeadBucket(ctx, &queryHeadBucketRequest) + s.Require().NoError(err) + + queryGetSpStoragePriceByTimeResp, err := s.Client.QueryGetSpStoragePriceByTime(ctx, &sptypes.QueryGetSpStoragePriceByTimeRequest{ + SpAddr: sp.OperatorKey.GetAddr().String(), + Timestamp: queryHeadBucketResponse.BucketInfo.CreateAt, + }) + s.T().Logf("queryGetSpStoragePriceByTimeResp %s, err: %v", queryGetSpStoragePriceByTimeResp, err) + s.Require().NoError(err) + + readPrice := queryGetSpStoragePriceByTimeResp.SpStoragePrice.ReadPrice + readChargeRate := readPrice.MulInt(sdk.NewIntFromUint64(queryHeadBucketResponse.BucketInfo.ChargedReadQuota)).TruncateInt() + s.T().Logf("readPrice: %s, readChargeRate: %s", readPrice, readChargeRate) + userTaxRate := params.VersionedParams.ValidatorTaxRate.MulInt(readChargeRate).TruncateInt() + userTotalRate := readChargeRate.Add(userTaxRate) + s.Require().Equal(userStreamRecord.NetflowRate.Abs(), userTotalRate) + expectedOutFlows := []paymenttypes.OutFlow{ + {ToAddress: family.VirtualPaymentAddress, Rate: readChargeRate}, + {ToAddress: paymenttypes.ValidatorTaxPoolAddress.String(), Rate: userTaxRate}, + } + userOutFlowsResponse, err := s.Client.OutFlows(ctx, &paymenttypes.QueryOutFlowsRequest{Account: user.GetAddr().String()}) + s.Require().NoError(err) + sort.Slice(userOutFlowsResponse.OutFlows, func(i, j int) bool { + return userOutFlowsResponse.OutFlows[i].ToAddress < userOutFlowsResponse.OutFlows[j].ToAddress + }) + sort.Slice(expectedOutFlows, func(i, j int) bool { + return expectedOutFlows[i].ToAddress < expectedOutFlows[j].ToAddress + }) + s.Require().Equal(expectedOutFlows, userOutFlowsResponse.OutFlows) + + // CreateObject + objectName := storagetestutils.GenRandomObjectName() + // create test buffer + var buffer bytes.Buffer + // Create 1MiB content where each line contains 1024 characters. + for i := 0; i < 1024; i++ { + buffer.WriteString(fmt.Sprintf("[%05d] %s\n", i, line)) + } + payloadSize := buffer.Len() + checksum := sdk.Keccak256(buffer.Bytes()) + expectChecksum := [][]byte{checksum, checksum, checksum, checksum, checksum, checksum, checksum} + contextType := "text/event-stream" + msgCreateObject := storagetypes.NewMsgCreateObject(user.GetAddr(), bucketName, objectName, uint64(payloadSize), storagetypes.VISIBILITY_TYPE_PRIVATE, expectChecksum, contextType, storagetypes.REDUNDANCY_EC_TYPE, math.MaxUint, nil) + msgCreateObject.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateObject.GetApprovalBytes()) + s.Require().NoError(err) + // simulate + res := s.SimulateTx(msgCreateObject, user) + s.T().Logf("res %v", res.Result) + // check EventFeePreview in simulation result + var feePreviewEventEmitted bool + events := res.Result.Events + for _, event := range events { + if event.Type == "greenfield.payment.EventFeePreview" { + s.T().Logf("event %v", event) + feePreviewEventEmitted = true + } + } + s.Require().True(feePreviewEventEmitted) + s.SendTxBlock(user, msgCreateObject) + + // check lock balance + queryHeadBucketResponseAfterCreateObj, err := s.Client.HeadBucket(ctx, &queryHeadBucketRequest) + s.T().Logf("queryHeadBucketResponseAfterCreateObj %s, err: %v", queryHeadBucketResponseAfterCreateObj, err) + s.Require().NoError(err) + queryHeadObjectRequest := storagetypes.QueryHeadObjectRequest{ + BucketName: bucketName, + ObjectName: objectName, + } + queryHeadObjectResponse, err := s.Client.HeadObject(ctx, &queryHeadObjectRequest) + s.T().Logf("queryHeadObjectResponse %s, err: %v", queryHeadObjectResponse, err) + s.Require().NoError(err) + + queryGetSecondarySpStorePriceByTime, err := s.Client.QueryGetSecondarySpStorePriceByTime(ctx, &sptypes.QueryGetSecondarySpStorePriceByTimeRequest{ + Timestamp: queryHeadBucketResponse.BucketInfo.CreateAt, + }) + s.T().Logf("queryGetSecondarySpStorePriceByTime %s, err: %v", queryGetSecondarySpStorePriceByTime, err) + s.Require().NoError(err) + primaryStorePrice := queryGetSpStoragePriceByTimeResp.SpStoragePrice.StorePrice + secondaryStorePrice := queryGetSecondarySpStorePriceByTime.SecondarySpStorePrice.StorePrice + chargeSize := s.getChargeSize(queryHeadObjectResponse.ObjectInfo.PayloadSize) + expectedChargeRate := primaryStorePrice.Add(secondaryStorePrice.MulInt64(6)).MulInt(sdk.NewIntFromUint64(chargeSize)).TruncateInt() + expectedChargeRate = params.VersionedParams.ValidatorTaxRate.MulInt(expectedChargeRate).TruncateInt().Add(expectedChargeRate) + expectedLockedBalance := expectedChargeRate.Mul(sdkmath.NewIntFromUint64(params.VersionedParams.ReserveTime)) + + streamRecordsAfterCreateObject := s.getStreamRecords(streamAddresses) + s.T().Logf("streamRecordsAfterCreateObject %s", core.YamlString(streamRecordsAfterCreateObject)) + userStreamAccountAfterCreateObj := streamRecordsAfterCreateObject.User + + s.Require().Equal(expectedLockedBalance.String(), userStreamAccountAfterCreateObj.LockBalance.String()) + + // seal object + gvgId := gvg.Id + msgSealObject := storagetypes.NewMsgSealObject(sp.SealKey.GetAddr(), bucketName, objectName, gvg.Id, nil) + secondarySigs := make([][]byte, 0) + secondarySPBlsPubKeys := make([]bls.PublicKey, 0) + blsSignHash := storagetypes.NewSecondarySpSealObjectSignDoc(s.GetChainID(), gvgId, queryHeadObjectResponse.ObjectInfo.Id, storagetypes.GenerateHash(expectChecksum[:])).GetBlsSignHash() + // every secondary sp signs the checksums + for _, spID := range gvg.SecondarySpIds { + sig, err := core.BlsSignAndVerify(s.StorageProviders[spID], blsSignHash) + s.Require().NoError(err) + secondarySigs = append(secondarySigs, sig) + pk, err := bls.PublicKeyFromBytes(s.StorageProviders[spID].BlsKey.PubKey().Bytes()) + s.Require().NoError(err) + secondarySPBlsPubKeys = append(secondarySPBlsPubKeys, pk) + } + aggBlsSig, err := core.BlsAggregateAndVerify(secondarySPBlsPubKeys, blsSignHash, secondarySigs) + s.Require().NoError(err) + msgSealObject.SecondarySpBlsAggSignatures = aggBlsSig + s.T().Logf("msg %s", msgSealObject.String()) + s.SendTxBlock(sp.SealKey, msgSealObject) + + // check bill after seal + streamRecordsAfterSeal := s.getStreamRecords(streamAddresses) + s.T().Logf("streamRecordsAfterSeal %s", core.YamlString(streamRecordsAfterSeal)) + s.Require().Equal(sdkmath.ZeroInt(), streamRecordsAfterSeal.User.LockBalance) + s.checkStreamRecordsBeforeAndAfter(streamRecordsAfterCreateObject, streamRecordsAfterSeal, readPrice, readChargeRate, primaryStorePrice, secondaryStorePrice, chargeSize, uint64(payloadSize)) + + // query dynamic balance + time.Sleep(3 * time.Second) + queryDynamicBalanceRequest := paymenttypes.QueryDynamicBalanceRequest{ + Account: user.GetAddr().String(), + } + queryDynamicBalanceResponse, err := s.Client.DynamicBalance(ctx, &queryDynamicBalanceRequest) + s.Require().NoError(err) + s.T().Logf("queryDynamicBalanceResponse %s", core.YamlString(queryDynamicBalanceResponse)) + + // create empty object + streamRecordsBeforeCreateEmptyObject := s.getStreamRecords(streamAddresses) + s.T().Logf("streamRecordsBeforeCreateEmptyObject %s", core.YamlString(streamRecordsBeforeCreateEmptyObject)) + + emptyObjectName := "sub_directory/" + // create empty test buffer + var emptyBuffer bytes.Buffer + emptyPayloadSize := emptyBuffer.Len() + emptyChecksum := sdk.Keccak256(emptyBuffer.Bytes()) + emptyExpectChecksum := [][]byte{emptyChecksum, emptyChecksum, emptyChecksum, emptyChecksum, emptyChecksum, emptyChecksum, emptyChecksum} + msgCreateEmptyObject := storagetypes.NewMsgCreateObject(user.GetAddr(), bucketName, emptyObjectName, uint64(emptyPayloadSize), storagetypes.VISIBILITY_TYPE_PRIVATE, emptyExpectChecksum, contextType, storagetypes.REDUNDANCY_EC_TYPE, math.MaxUint, nil) + msgCreateEmptyObject.PrimarySpApproval.Sig, err = sp.ApprovalKey.Sign(msgCreateEmptyObject.GetApprovalBytes()) + s.Require().NoError(err) + s.SendTxBlock(user, msgCreateEmptyObject) + + streamRecordsAfterCreateEmptyObject := s.getStreamRecords(streamAddresses) + s.T().Logf("streamRecordsAfterCreateEmptyObject %s", core.YamlString(streamRecordsAfterCreateEmptyObject)) + chargeSize = s.getChargeSize(uint64(emptyPayloadSize)) + s.checkStreamRecordsBeforeAndAfter(streamRecordsBeforeCreateEmptyObject, streamRecordsAfterCreateEmptyObject, readPrice, readChargeRate, primaryStorePrice, secondaryStorePrice, chargeSize, uint64(emptyPayloadSize)) + + // test query auto settle records + queryAllAutoSettleRecordRequest := paymenttypes.QueryAllAutoSettleRecordRequest{} + queryAllAutoSettleRecordResponse, err := s.Client.AutoSettleRecordAll(ctx, &queryAllAutoSettleRecordRequest) + s.Require().NoError(err) + s.T().Logf("queryAllAutoSettleRecordResponse %s", core.YamlString(queryAllAutoSettleRecordResponse)) + s.Require().True(len(queryAllAutoSettleRecordResponse.AutoSettleRecord) >= 1) + + // simulate delete object, check fee preview + deleteObjectMsg := storagetypes.NewMsgDeleteObject(user.GetAddr(), bucketName, objectName) + deleteObjectSimRes := s.SimulateTx(deleteObjectMsg, user) + s.T().Logf("deleteObjectSimRes %v", deleteObjectSimRes.Result) +} + +func (s *PaymentTestSuite) TestStorageBill_DeleteObjectBucket_WithoutPriceChange() { + var err error + 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(), + } + streamRecordsBefore := s.getStreamRecords(streamAddresses) + + // create bucket + bucketName := s.createBucket(sp, user, 256) + + //simulate delete bucket gas + msgDeleteBucket := storagetypes.NewMsgDeleteBucket(user.GetAddr(), bucketName) + simulateResponse := s.SimulateTx(msgDeleteBucket, user) + gasLimit := simulateResponse.GasInfo.GetGasUsed() + gasPrice, err := sdk.ParseCoinNormalized(simulateResponse.GasInfo.GetMinGasPrice()) + s.Require().NoError(err) + + gas := gasPrice.Amount.Mul(sdk.NewInt(int64(gasLimit))) + s.T().Log("total gas", "gas", gas) + + // create & seal objects + _, _, objectName1, objectId1, checksums1, _ := s.createObject(user, bucketName, false) + s.sealObject(sp, gvg, bucketName, objectName1, objectId1, checksums1) + + // for payment + time.Sleep(2 * time.Second) + + //transfer gas + msgSend := banktypes.NewMsgSend(user.GetAddr(), core.GenRandomAddr(), sdk.NewCoins( + sdk.NewCoin(s.Config.Denom, sdkmath.NewInt(5*types.DecimalGwei)), + )) + simulateResponse = s.SimulateTx(msgSend, user) + gasLimit = simulateResponse.GasInfo.GetGasUsed() + gasPrice, err = sdk.ParseCoinNormalized(simulateResponse.GasInfo.GetMinGasPrice()) + s.Require().NoError(err) + + gas = gas.Add(gasPrice.Amount.Mul(sdk.NewInt(int64(gasLimit)))) + s.T().Log("total gas", "gas", gas) + + //delete object gas + msgDeleteObject := storagetypes.NewMsgDeleteObject(user.GetAddr(), bucketName, objectName1) + simulateResponse = s.SimulateTx(msgDeleteObject, user) + gasLimit = simulateResponse.GasInfo.GetGasUsed() + gasPrice, err = sdk.ParseCoinNormalized(simulateResponse.GasInfo.GetMinGasPrice()) + s.Require().NoError(err) + + gas = gas.Add(gasPrice.Amount.Mul(sdk.NewInt(int64(gasLimit)))) + s.T().Log("total gas", "gas", gas) + + // transfer out user's 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.Amount = sdk.NewCoins( + sdk.NewCoin(s.Config.Denom, queryBalanceResponse.Balance.Amount.Sub(gas)), + ) + s.SendTxBlock(user, msgSend) + _, err = s.Client.BankQueryClient.Balance(ctx, &queryBalanceRequest) + s.Require().NoError(err) + + s.SendTxBlock(user, msgDeleteObject) + s.SendTxBlock(user, msgDeleteBucket) + + // assertions + streamRecordsAfter := s.getStreamRecords(streamAddresses) + s.Require().Equal(streamRecordsAfter.User.NetflowRate, sdkmath.ZeroInt()) + s.Require().Equal(streamRecordsAfter.User.NetflowRate.Sub(streamRecordsBefore.User.NetflowRate).Int64(), int64(0)) + s.Require().Equal(streamRecordsAfter.GVGFamily.NetflowRate.Sub(streamRecordsBefore.GVGFamily.NetflowRate).Int64(), int64(0)) + s.Require().Equal(streamRecordsAfter.GVG.NetflowRate.Sub(streamRecordsBefore.GVG.NetflowRate).Int64(), int64(0)) + s.Require().Equal(streamRecordsAfter.Tax.NetflowRate.Sub(streamRecordsBefore.Tax.NetflowRate).Int64(), int64(0)) +} + +func (s *PaymentTestSuite) TestStorageBill_DeleteObjectBucket_WithPriceChange() { + var err error + 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(), + } + streamRecordsBefore := s.getStreamRecords(streamAddresses) + + // create bucket + bucketName := s.createBucket(sp, user, 256) + + //simulate delete bucket gas + msgDeleteBucket := storagetypes.NewMsgDeleteBucket(user.GetAddr(), bucketName) + simulateResponse := s.SimulateTx(msgDeleteBucket, user) + gasLimit := simulateResponse.GasInfo.GetGasUsed() + gasPrice, err := sdk.ParseCoinNormalized(simulateResponse.GasInfo.GetMinGasPrice()) + s.Require().NoError(err) + + gas := gasPrice.Amount.Mul(sdk.NewInt(int64(gasLimit))) + s.T().Log("total gas", "gas", gas) + + // create & seal objects + _, _, objectName1, objectId1, checksums1, _ := s.createObject(user, bucketName, false) + s.sealObject(sp, gvg, bucketName, objectName1, objectId1, checksums1) + + // for payment + time.Sleep(2 * time.Second) + + //transfer gas + msgSend := banktypes.NewMsgSend(user.GetAddr(), core.GenRandomAddr(), sdk.NewCoins( + sdk.NewCoin(s.Config.Denom, sdkmath.NewInt(5*types.DecimalGwei)), + )) + simulateResponse = s.SimulateTx(msgSend, user) + gasLimit = simulateResponse.GasInfo.GetGasUsed() + gasPrice, err = sdk.ParseCoinNormalized(simulateResponse.GasInfo.GetMinGasPrice()) + s.Require().NoError(err) + + gas = gas.Add(gasPrice.Amount.Mul(sdk.NewInt(int64(gasLimit)))) + s.T().Log("total gas", "gas", gas) + + //delete object gas + msgDeleteObject := storagetypes.NewMsgDeleteObject(user.GetAddr(), bucketName, objectName1) + simulateResponse = s.SimulateTx(msgDeleteObject, user) + gasLimit = simulateResponse.GasInfo.GetGasUsed() + gasPrice, err = sdk.ParseCoinNormalized(simulateResponse.GasInfo.GetMinGasPrice()) + s.Require().NoError(err) + + gas = gas.Add(gasPrice.Amount.Mul(sdk.NewInt(int64(gasLimit)))) + s.T().Log("total gas", "gas", gas) + + // transfer out user's 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.Amount = sdk.NewCoins( + sdk.NewCoin(s.Config.Denom, queryBalanceResponse.Balance.Amount.Sub(gas)), + ) + s.SendTxBlock(user, msgSend) + _, err = s.Client.BankQueryClient.Balance(ctx, &queryBalanceRequest) + s.Require().NoError(err) + + // sp price changes + priceRes, err := s.Client.QueryGetSpStoragePriceByTime(ctx, &sptypes.QueryGetSpStoragePriceByTimeRequest{ + SpAddr: sp.OperatorKey.GetAddr().String(), + Timestamp: 0, + }) + s.Require().NoError(err) + s.T().Log("price", priceRes.SpStoragePrice) + + // update new price + msgUpdatePrice := &sptypes.MsgUpdateSpStoragePrice{ + SpAddress: sp.OperatorKey.GetAddr().String(), + ReadPrice: priceRes.SpStoragePrice.ReadPrice.MulInt64(1000), + FreeReadQuota: priceRes.SpStoragePrice.FreeReadQuota, + StorePrice: priceRes.SpStoragePrice.StorePrice.MulInt64(10000), + } + s.SendTxBlock(sp.OperatorKey, msgUpdatePrice) + + s.SendTxBlock(user, msgDeleteObject) + s.SendTxBlock(user, msgDeleteBucket) + + // assertions + streamRecordsAfter := s.getStreamRecords(streamAddresses) + s.Require().Equal(streamRecordsAfter.User.NetflowRate, sdkmath.ZeroInt()) + s.Require().Equal(streamRecordsAfter.User.NetflowRate.Sub(streamRecordsBefore.User.NetflowRate).Int64(), int64(0)) + s.Require().Equal(streamRecordsAfter.GVGFamily.NetflowRate.Sub(streamRecordsBefore.GVGFamily.NetflowRate).Int64(), int64(0)) + s.Require().Equal(streamRecordsAfter.GVG.NetflowRate.Sub(streamRecordsBefore.GVG.NetflowRate).Int64(), int64(0)) + s.Require().Equal(streamRecordsAfter.Tax.NetflowRate.Sub(streamRecordsBefore.Tax.NetflowRate).Int64(), int64(0)) + + // revert price + msgUpdatePrice = &sptypes.MsgUpdateSpStoragePrice{ + SpAddress: sp.OperatorKey.GetAddr().String(), + ReadPrice: priceRes.SpStoragePrice.ReadPrice, + FreeReadQuota: priceRes.SpStoragePrice.FreeReadQuota, + StorePrice: priceRes.SpStoragePrice.StorePrice, + } + s.SendTxBlock(sp.OperatorKey, msgUpdatePrice) +} + +func (s *PaymentTestSuite) TestStorageBill_DeleteObjectBucket_WithPriceChangeReserveTimeChange() { + defer s.revertParams() + + var err error + 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(), + } + streamRecordsBefore := s.getStreamRecords(streamAddresses) + + // create bucket + bucketName := s.createBucket(sp, user, 256) + + //simulate delete bucket gas + msgDeleteBucket := storagetypes.NewMsgDeleteBucket(user.GetAddr(), bucketName) + simulateResponse := s.SimulateTx(msgDeleteBucket, user) + gasLimit := simulateResponse.GasInfo.GetGasUsed() + gasPrice, err := sdk.ParseCoinNormalized(simulateResponse.GasInfo.GetMinGasPrice()) + s.Require().NoError(err) + + gas := gasPrice.Amount.Mul(sdk.NewInt(int64(gasLimit))) + s.T().Log("total gas", "gas", gas) + + // create & seal objects + _, _, objectName1, objectId1, checksums1, _ := s.createObject(user, bucketName, false) + s.sealObject(sp, gvg, bucketName, objectName1, objectId1, checksums1) + + // for payment + time.Sleep(2 * time.Second) + + //transfer gas + msgSend := banktypes.NewMsgSend(user.GetAddr(), core.GenRandomAddr(), sdk.NewCoins( + sdk.NewCoin(s.Config.Denom, sdkmath.NewInt(5*types.DecimalGwei)), + )) + simulateResponse = s.SimulateTx(msgSend, user) + gasLimit = simulateResponse.GasInfo.GetGasUsed() + gasPrice, err = sdk.ParseCoinNormalized(simulateResponse.GasInfo.GetMinGasPrice()) + s.Require().NoError(err) + + gas = gas.Add(gasPrice.Amount.Mul(sdk.NewInt(int64(gasLimit)))) + s.T().Log("total gas", "gas", gas) + + //delete object gas + msgDeleteObject := storagetypes.NewMsgDeleteObject(user.GetAddr(), bucketName, objectName1) + simulateResponse = s.SimulateTx(msgDeleteObject, user) + gasLimit = simulateResponse.GasInfo.GetGasUsed() + gasPrice, err = sdk.ParseCoinNormalized(simulateResponse.GasInfo.GetMinGasPrice()) + s.Require().NoError(err) + + gas = gas.Add(gasPrice.Amount.Mul(sdk.NewInt(int64(gasLimit)))) + s.T().Log("total gas", "gas", gas) + + // transfer out user's 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.Amount = sdk.NewCoins( + sdk.NewCoin(s.Config.Denom, queryBalanceResponse.Balance.Amount.Sub(gas)), + ) + s.SendTxBlock(user, msgSend) + _, err = s.Client.BankQueryClient.Balance(ctx, &queryBalanceRequest) + s.Require().NoError(err) + + // sp price changes + priceRes, err := s.Client.QueryGetSpStoragePriceByTime(ctx, &sptypes.QueryGetSpStoragePriceByTimeRequest{ + SpAddr: sp.OperatorKey.GetAddr().String(), + Timestamp: 0, + }) + s.Require().NoError(err) + s.T().Log("price", priceRes.SpStoragePrice) + + // update new price + msgUpdatePrice := &sptypes.MsgUpdateSpStoragePrice{ + SpAddress: sp.OperatorKey.GetAddr().String(), + ReadPrice: priceRes.SpStoragePrice.ReadPrice.MulInt64(1000), + FreeReadQuota: priceRes.SpStoragePrice.FreeReadQuota, + StorePrice: priceRes.SpStoragePrice.StorePrice.MulInt64(10000), + } + s.SendTxBlock(sp.OperatorKey, msgUpdatePrice) + + // update params + params := s.queryParams() + oldReserveTime := params.VersionedParams.ReserveTime + oldValidatorTaxRate := params.VersionedParams.ValidatorTaxRate + + params.VersionedParams.ReserveTime = oldReserveTime * 2 + params.VersionedParams.ValidatorTaxRate = oldValidatorTaxRate.MulInt64(2) + s.updateParams(params) + + s.SendTxBlock(user, msgDeleteObject) + s.SendTxBlock(user, msgDeleteBucket) + + // assertions + streamRecordsAfter := s.getStreamRecords(streamAddresses) + s.Require().Equal(streamRecordsAfter.User.NetflowRate, sdkmath.ZeroInt()) + s.Require().Equal(streamRecordsAfter.User.NetflowRate.Sub(streamRecordsBefore.User.NetflowRate).Int64(), int64(0)) + s.Require().Equal(streamRecordsAfter.GVGFamily.NetflowRate.Sub(streamRecordsBefore.GVGFamily.NetflowRate).Int64(), int64(0)) + s.Require().Equal(streamRecordsAfter.GVG.NetflowRate.Sub(streamRecordsBefore.GVG.NetflowRate).Int64(), int64(0)) + s.Require().Equal(streamRecordsAfter.Tax.NetflowRate.Sub(streamRecordsBefore.Tax.NetflowRate).Int64(), int64(0)) + + // revert price + msgUpdatePrice = &sptypes.MsgUpdateSpStoragePrice{ + SpAddress: sp.OperatorKey.GetAddr().String(), + ReadPrice: priceRes.SpStoragePrice.ReadPrice, + FreeReadQuota: priceRes.SpStoragePrice.FreeReadQuota, + StorePrice: priceRes.SpStoragePrice.StorePrice, + } + s.SendTxBlock(sp.OperatorKey, msgUpdatePrice) +} + +func (s *PaymentTestSuite) TestStorageBill_DeleteObject_WithStoreLessThanReserveTime() { + var err error + 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] + + params := s.queryParams() + reserveTime := params.VersionedParams.ReserveTime + + // create bucket + bucketName := s.createBucket(sp, user, 256) + + // create & seal objects + _, _, objectName1, objectId1, checksums1, payloadSize := s.createObject(user, bucketName, false) + s.sealObject(sp, gvg, bucketName, objectName1, objectId1, checksums1) + + headObjectRes, err := s.Client.HeadObject(ctx, &storagetypes.QueryHeadObjectRequest{ + BucketName: bucketName, + ObjectName: objectName1, + }) + s.Require().NoError(err) + s.T().Log("headObjectRes", headObjectRes) + + streamAddresses := []string{ + user.GetAddr().String(), + family.VirtualPaymentAddress, + gvg.VirtualPaymentAddress, + paymenttypes.ValidatorTaxPoolAddress.String(), + } + streamRecordsBefore := s.getStreamRecords(streamAddresses) + _, _, userRateRead := s.calculateReadRates(sp, bucketName) + _, _, _, userRateStore := s.calculateStorageRates(sp, bucketName, objectName1, payloadSize, 0) + + msgDeleteObject := storagetypes.NewMsgDeleteObject(user.GetAddr(), bucketName, objectName1) + s.SendTxBlock(user, msgDeleteObject) + + // assertions + streamRecordsAfter := s.getStreamRecords(streamAddresses) + + settledTime := streamRecordsAfter.User.CrudTimestamp - streamRecordsBefore.User.CrudTimestamp + timeToPay := int64(reserveTime) + headObjectRes.ObjectInfo.CreateAt - streamRecordsAfter.User.CrudTimestamp + balanceDelta := userRateRead.Add(userRateStore).MulRaw(settledTime).Add(userRateStore.MulRaw(timeToPay)) + + s.Require().Equal(streamRecordsAfter.User.NetflowRate.Sub(streamRecordsBefore.User.NetflowRate).Int64(), userRateStore.Int64()) + userBalanceChange := streamRecordsAfter.User.BufferBalance.Add(streamRecordsAfter.User.StaticBalance). + Sub(streamRecordsBefore.User.BufferBalance.Add(streamRecordsBefore.User.StaticBalance)) + s.Require().Equal(userBalanceChange.Neg().Int64(), balanceDelta.Int64()) + + familyDelta := streamRecordsAfter.GVGFamily.StaticBalance.Sub(streamRecordsBefore.GVGFamily.StaticBalance) + gvgDelta := streamRecordsAfter.GVG.StaticBalance.Sub(streamRecordsBefore.GVG.StaticBalance) + taxPoolDelta := streamRecordsAfter.Tax.StaticBalance.Sub(streamRecordsBefore.Tax.StaticBalance) + s.T().Log("familyDelta", familyDelta, "gvgDelta", gvgDelta, "taxPoolDelta", taxPoolDelta) + s.Require().True(familyDelta.Add(gvgDelta).Add(taxPoolDelta).Int64() >= balanceDelta.Int64()) // could exist other buckets/objects on the gvg & family +} + +func (s *PaymentTestSuite) TestStorageBill_DeleteObject_WithStoreMoreThanReserveTime() { + defer s.revertParams() + + var err error + 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] + + params := s.queryParams() + params.VersionedParams.ReserveTime = 5 + params.ForcedSettleTime = 2 + s.updateParams(params) + + // create bucket + bucketName := s.createBucket(sp, user, 256) + + // create & seal objects + _, _, objectName1, objectId1, checksums1, payloadSize := s.createObject(user, bucketName, false) + s.sealObject(sp, gvg, bucketName, objectName1, objectId1, checksums1) + + headObjectRes, err := s.Client.HeadObject(ctx, &storagetypes.QueryHeadObjectRequest{ + BucketName: bucketName, + ObjectName: objectName1, + }) + s.Require().NoError(err) + s.T().Log("headObjectRes", headObjectRes) + + time.Sleep(5 * time.Second) + queryBalanceRequest := banktypes.QueryBalanceRequest{Denom: s.Config.Denom, Address: user.GetAddr().String()} + queryBalanceResponse, err := s.Client.BankQueryClient.Balance(ctx, &queryBalanceRequest) + s.Require().NoError(err) + balanceBefore := queryBalanceResponse.Balance.Amount + + streamAddresses := []string{ + user.GetAddr().String(), + family.VirtualPaymentAddress, + gvg.VirtualPaymentAddress, + paymenttypes.ValidatorTaxPoolAddress.String(), + } + streamRecordsBefore := s.getStreamRecords(streamAddresses) + _, _, userRateRead := s.calculateReadRates(sp, bucketName) + _, _, _, userRateStore := s.calculateStorageRates(sp, bucketName, objectName1, payloadSize, 0) + + msgDeleteObject := storagetypes.NewMsgDeleteObject(user.GetAddr(), bucketName, objectName1) + simulateResponse := s.SimulateTx(msgDeleteObject, user) + gasLimit := simulateResponse.GasInfo.GetGasUsed() + gasPrice, err := sdk.ParseCoinNormalized(simulateResponse.GasInfo.GetMinGasPrice()) + s.Require().NoError(err) + gas := gasPrice.Amount.MulRaw(int64(gasLimit)) + + // delete object + s.SendTxBlock(user, msgDeleteObject) + + // assertions + streamRecordsAfter := s.getStreamRecords(streamAddresses) + queryBalanceRequest = banktypes.QueryBalanceRequest{Denom: s.Config.Denom, Address: user.GetAddr().String()} + queryBalanceResponse, err = s.Client.BankQueryClient.Balance(ctx, &queryBalanceRequest) + s.Require().NoError(err) + balanceAfter := queryBalanceResponse.Balance.Amount + + settledTime := streamRecordsAfter.User.CrudTimestamp - streamRecordsBefore.User.CrudTimestamp + balanceDelta := userRateRead.Add(userRateStore).MulRaw(settledTime) + + s.Require().Equal(streamRecordsAfter.User.NetflowRate.Sub(streamRecordsBefore.User.NetflowRate).Int64(), userRateStore.Int64()) + userBalanceChange := streamRecordsBefore.User.BufferBalance.Add(streamRecordsBefore.User.StaticBalance). + Sub(streamRecordsAfter.User.BufferBalance.Add(streamRecordsAfter.User.StaticBalance)) + userBalanceChange = userBalanceChange.Add(balanceBefore.Sub(balanceAfter)).Sub(gas) + s.Require().Equal(userBalanceChange.Int64(), balanceDelta.Int64()) + + familyDelta := streamRecordsAfter.GVGFamily.StaticBalance.Sub(streamRecordsBefore.GVGFamily.StaticBalance) + gvgDelta := streamRecordsAfter.GVG.StaticBalance.Sub(streamRecordsBefore.GVG.StaticBalance) + taxPoolDelta := streamRecordsAfter.Tax.StaticBalance.Sub(streamRecordsBefore.Tax.StaticBalance) + s.T().Log("familyDelta", familyDelta, "gvgDelta", gvgDelta, "taxPoolDelta", taxPoolDelta) + s.Require().True(familyDelta.Add(gvgDelta).Add(taxPoolDelta).Int64() >= balanceDelta.Int64()) // could exist other buckets/objects on the gvg & family +} + +func (s *PaymentTestSuite) TestStorageBill_CreateBucket_WithZeroNoneZeroReadQuota() { + var err error + 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(), + } + streamRecordsBefore := s.getStreamRecords(streamAddresses) + + params := s.queryParams() + + // case: create bucket with zero read quota + bucketName := s.createBucket(sp, user, 0) + + // bucket created + queryHeadBucketRequest := storagetypes.QueryHeadBucketRequest{ + BucketName: bucketName, + } + _, err = s.Client.HeadBucket(ctx, &queryHeadBucketRequest) + s.Require().NoError(err) + + // assertions + streamRecordsAfter := s.getStreamRecords(streamAddresses) + s.Require().Equal(streamRecordsAfter.User.StaticBalance, sdkmath.ZeroInt()) + s.Require().Equal(streamRecordsAfter.User.NetflowRate, streamRecordsBefore.User.NetflowRate) + s.Require().Equal(streamRecordsAfter.GVGFamily.NetflowRate, streamRecordsBefore.GVGFamily.NetflowRate) + s.Require().Equal(streamRecordsAfter.Tax.NetflowRate, streamRecordsBefore.Tax.NetflowRate) + + // case: create bucket with none zero read quota + bucketName = s.createBucket(sp, user, 10240) + + // bucket created + queryHeadBucketRequest = storagetypes.QueryHeadBucketRequest{ + BucketName: bucketName, + } + queryHeadBucketResponse, err := s.Client.HeadBucket(ctx, &queryHeadBucketRequest) + s.Require().NoError(err) + + // check price and rate calculation + queryGetSpStoragePriceByTimeResp, err := s.Client.QueryGetSpStoragePriceByTime(ctx, &sptypes.QueryGetSpStoragePriceByTimeRequest{ + SpAddr: sp.OperatorKey.GetAddr().String(), + Timestamp: queryHeadBucketResponse.BucketInfo.CreateAt, + }) + s.T().Logf("queryGetSpStoragePriceByTimeResp %s, err: %v", queryGetSpStoragePriceByTimeResp, err) + s.Require().NoError(err) + + readPrice := queryGetSpStoragePriceByTimeResp.SpStoragePrice.ReadPrice + readChargeRate := readPrice.MulInt(sdk.NewIntFromUint64(queryHeadBucketResponse.BucketInfo.ChargedReadQuota)).TruncateInt() + s.T().Logf("readPrice: %s, readChargeRate: %s", readPrice, readChargeRate) + taxRate := params.VersionedParams.ValidatorTaxRate.MulInt(readChargeRate).TruncateInt() + userTotalRate := readChargeRate.Add(taxRate) + + // assertions + streamRecordsAfter = s.getStreamRecords(streamAddresses) + s.Require().Equal(streamRecordsAfter.User.StaticBalance, sdkmath.ZeroInt()) + s.Require().Equal(streamRecordsAfter.User.NetflowRate.Sub(streamRecordsBefore.User.NetflowRate), userTotalRate.Neg()) + s.Require().Equal(streamRecordsAfter.GVGFamily.NetflowRate.Sub(streamRecordsBefore.GVGFamily.NetflowRate), readChargeRate) + s.Require().Equal(streamRecordsAfter.Tax.NetflowRate.Sub(streamRecordsBefore.Tax.NetflowRate), taxRate) + + expectedOutFlows := []paymenttypes.OutFlow{ + {ToAddress: family.VirtualPaymentAddress, Rate: readChargeRate}, + {ToAddress: paymenttypes.ValidatorTaxPoolAddress.String(), Rate: taxRate}, + } + userOutFlowsResponse, err := s.Client.OutFlows(ctx, &paymenttypes.QueryOutFlowsRequest{Account: user.GetAddr().String()}) + s.Require().NoError(err) + sort.Slice(userOutFlowsResponse.OutFlows, func(i, j int) bool { + return userOutFlowsResponse.OutFlows[i].ToAddress < userOutFlowsResponse.OutFlows[j].ToAddress + }) + sort.Slice(expectedOutFlows, func(i, j int) bool { + return expectedOutFlows[i].ToAddress < expectedOutFlows[j].ToAddress + }) + s.Require().Equal(expectedOutFlows, userOutFlowsResponse.OutFlows) +} + +func (s *PaymentTestSuite) TestStorageBill_CreateObject_WithZeroNoneZeroPayload() { + var err error + 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(), + } + + bucketName := s.createBucket(sp, user, 0) + + // case: create object with zero payload size + streamRecordsBefore := s.getStreamRecords(streamAddresses) + _, _, objectName, _, _, payloadSize := s.createObject(user, bucketName, true) + + // assertions + streamRecordsAfter := s.getStreamRecords(streamAddresses) + s.Require().Equal(streamRecordsAfter.User.StaticBalance, sdkmath.ZeroInt()) + s.Require().Equal(streamRecordsAfter.User.LockBalance, sdkmath.ZeroInt()) + gvgFamilyRate, gvgRate, taxRate, userTotalRate := s.calculateStorageRates(sp, bucketName, objectName, payloadSize, 0) + s.Require().Equal(streamRecordsAfter.User.NetflowRate.Sub(streamRecordsBefore.User.NetflowRate), userTotalRate.Neg()) + s.Require().Equal(streamRecordsAfter.GVGFamily.NetflowRate.Sub(streamRecordsBefore.GVGFamily.NetflowRate), gvgFamilyRate) + s.Require().Equal(streamRecordsAfter.GVG.NetflowRate.Sub(streamRecordsBefore.GVG.NetflowRate), gvgRate) + s.Require().Equal(streamRecordsAfter.Tax.NetflowRate.Sub(streamRecordsBefore.Tax.NetflowRate), taxRate) + + // case: create object with none zero payload size + streamRecordsBefore = s.getStreamRecords(streamAddresses) + _, _, objectName, _, _, payloadSize = s.createObject(user, bucketName, false) + + // assertions + streamRecordsAfter = s.getStreamRecords(streamAddresses) + s.Require().Equal(streamRecordsAfter.User.StaticBalance, sdkmath.ZeroInt()) + lockFee := s.calculateLockFee(sp, bucketName, objectName, payloadSize) + s.Require().Equal(streamRecordsAfter.User.LockBalance.Sub(streamRecordsBefore.User.LockBalance), lockFee) + s.Require().Equal(streamRecordsAfter.User.NetflowRate.Sub(streamRecordsBefore.User.NetflowRate).Int64(), int64(0)) + s.Require().Equal(streamRecordsAfter.GVGFamily.NetflowRate.Sub(streamRecordsBefore.GVGFamily.NetflowRate).Int64(), int64(0)) + s.Require().Equal(streamRecordsAfter.GVG.NetflowRate.Sub(streamRecordsBefore.GVG.NetflowRate).Int64(), int64(0)) + s.Require().Equal(streamRecordsAfter.Tax.NetflowRate.Sub(streamRecordsBefore.Tax.NetflowRate).Int64(), int64(0)) +} + +func (s *PaymentTestSuite) TestStorageBill_CreateObject_WithReserveTimeValidatorTaxRateChange() { + defer s.revertParams() + + var err error + 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(), + } + + bucketName := s.createBucket(sp, user, 0) + + // create object with none zero payload size + streamRecordsBefore := s.getStreamRecords(streamAddresses) + _, _, objectName, _, _, payloadSize := s.createObject(user, bucketName, false) + + // assertions + streamRecordsAfter := s.getStreamRecords(streamAddresses) + s.Require().Equal(streamRecordsAfter.User.StaticBalance, sdkmath.ZeroInt()) + lockFee := s.calculateLockFee(sp, bucketName, objectName, payloadSize) + s.Require().Equal(streamRecordsAfter.User.LockBalance.Sub(streamRecordsBefore.User.LockBalance), lockFee) + s.Require().Equal(streamRecordsAfter.User.NetflowRate.Sub(streamRecordsBefore.User.NetflowRate).Int64(), int64(0)) + s.Require().Equal(streamRecordsAfter.GVGFamily.NetflowRate.Sub(streamRecordsBefore.GVGFamily.NetflowRate).Int64(), int64(0)) + s.Require().Equal(streamRecordsAfter.GVG.NetflowRate.Sub(streamRecordsBefore.GVG.NetflowRate).Int64(), int64(0)) + s.Require().Equal(streamRecordsAfter.Tax.NetflowRate.Sub(streamRecordsBefore.Tax.NetflowRate).Int64(), int64(0)) + + // update params + + params := s.queryParams() + oldReserveTime := params.VersionedParams.ReserveTime + oldValidatorTaxRate := params.VersionedParams.ValidatorTaxRate + + params.VersionedParams.ReserveTime = oldReserveTime * 2 + params.VersionedParams.ValidatorTaxRate = oldValidatorTaxRate.MulInt64(2) + s.updateParams(params) + + // create another object after parameter changes + streamRecordsBefore = s.getStreamRecords(streamAddresses) + _, _, objectName, _, _, payloadSize = s.createObject(user, bucketName, false) + + // assertions + streamRecordsAfter = s.getStreamRecords(streamAddresses) + s.Require().Equal(streamRecordsAfter.User.StaticBalance, sdkmath.ZeroInt()) + lockFeeAfterParameterChange := s.calculateLockFee(sp, bucketName, objectName, payloadSize) + s.Require().Equal(streamRecordsAfter.User.LockBalance.Sub(streamRecordsBefore.User.LockBalance), lockFeeAfterParameterChange) + s.Require().Equal(streamRecordsAfter.User.NetflowRate.Sub(streamRecordsBefore.User.NetflowRate).Int64(), int64(0)) + s.Require().Equal(streamRecordsAfter.GVGFamily.NetflowRate.Sub(streamRecordsBefore.GVGFamily.NetflowRate).Int64(), int64(0)) + s.Require().Equal(streamRecordsAfter.GVG.NetflowRate.Sub(streamRecordsBefore.GVG.NetflowRate).Int64(), int64(0)) + s.Require().Equal(streamRecordsAfter.Tax.NetflowRate.Sub(streamRecordsBefore.Tax.NetflowRate).Int64(), int64(0)) + s.Require().True(lockFeeAfterParameterChange.GT(lockFee.MulRaw(2))) +} + +func (s *PaymentTestSuite) TestStorageBill_CancelCreateObject() { + var err error + 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(), + } + + bucketName := s.createBucket(sp, user, 0) + + // create object with none zero payload size + streamRecordsBefore := s.getStreamRecords(streamAddresses) + _, _, objectName, _, _, payloadSize := s.createObject(user, bucketName, false) + + // assertions + streamRecordsAfter := s.getStreamRecords(streamAddresses) + s.Require().Equal(streamRecordsAfter.User.StaticBalance, sdkmath.ZeroInt()) + lockFee := s.calculateLockFee(sp, bucketName, objectName, payloadSize) + s.Require().Equal(streamRecordsAfter.User.LockBalance.Sub(streamRecordsBefore.User.LockBalance), lockFee) + s.Require().Equal(streamRecordsAfter.User.NetflowRate.Sub(streamRecordsBefore.User.NetflowRate).Int64(), int64(0)) + s.Require().Equal(streamRecordsAfter.GVGFamily.NetflowRate.Sub(streamRecordsBefore.GVGFamily.NetflowRate).Int64(), int64(0)) + s.Require().Equal(streamRecordsAfter.GVG.NetflowRate.Sub(streamRecordsBefore.GVG.NetflowRate).Int64(), int64(0)) + s.Require().Equal(streamRecordsAfter.Tax.NetflowRate.Sub(streamRecordsBefore.Tax.NetflowRate).Int64(), int64(0)) + + // cancel create object + s.cancelCreateObject(user, bucketName, objectName) + + // assertions + streamRecordsAfter = s.getStreamRecords(streamAddresses) + s.Require().Equal(streamRecordsAfter.User.StaticBalance, lockFee) + s.Require().Equal(streamRecordsAfter.User.LockBalance.Int64(), int64(0)) + s.Require().Equal(streamRecordsAfter.User.NetflowRate.Sub(streamRecordsBefore.User.NetflowRate).Int64(), int64(0)) + s.Require().Equal(streamRecordsAfter.GVGFamily.NetflowRate.Sub(streamRecordsBefore.GVGFamily.NetflowRate).Int64(), int64(0)) + s.Require().Equal(streamRecordsAfter.GVG.NetflowRate.Sub(streamRecordsBefore.GVG.NetflowRate).Int64(), int64(0)) + s.Require().Equal(streamRecordsAfter.Tax.NetflowRate.Sub(streamRecordsBefore.Tax.NetflowRate).Int64(), int64(0)) +} + +func (s *PaymentTestSuite) TestStorageBill_SealObject_WithoutPriceChange() { + var err error + 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(), + } + + bucketName := s.createBucket(sp, user, 0) + + // create object with none zero payload size + streamRecordsBefore := s.getStreamRecords(streamAddresses) + _, _, objectName, objectId, checksums, payloadSize := s.createObject(user, bucketName, false) + + // assertions + streamRecordsAfter := s.getStreamRecords(streamAddresses) + s.Require().Equal(streamRecordsAfter.User.StaticBalance, sdkmath.ZeroInt()) + lockFee := s.calculateLockFee(sp, bucketName, objectName, payloadSize) + s.Require().Equal(streamRecordsAfter.User.LockBalance.Sub(streamRecordsBefore.User.LockBalance), lockFee) + s.Require().Equal(streamRecordsAfter.User.NetflowRate.Sub(streamRecordsBefore.User.NetflowRate).Int64(), int64(0)) + s.Require().Equal(streamRecordsAfter.GVGFamily.NetflowRate.Sub(streamRecordsBefore.GVGFamily.NetflowRate).Int64(), int64(0)) + s.Require().Equal(streamRecordsAfter.GVG.NetflowRate.Sub(streamRecordsBefore.GVG.NetflowRate).Int64(), int64(0)) + s.Require().Equal(streamRecordsAfter.Tax.NetflowRate.Sub(streamRecordsBefore.Tax.NetflowRate).Int64(), int64(0)) + + // case: seal object without price change + s.sealObject(sp, gvg, bucketName, objectName, objectId, checksums) + + // assertions + streamRecordsAfter = s.getStreamRecords(streamAddresses) + s.Require().Equal(streamRecordsAfter.User.StaticBalance, sdkmath.ZeroInt()) + s.Require().Equal(streamRecordsAfter.User.LockBalance, sdkmath.ZeroInt()) + gvgFamilyRate, gvgRate, taxRate, userTotalRate := s.calculateStorageRates(sp, bucketName, objectName, payloadSize, 0) + s.Require().Equal(streamRecordsAfter.User.NetflowRate.Sub(streamRecordsBefore.User.NetflowRate), userTotalRate.Neg()) + s.Require().Equal(streamRecordsAfter.GVGFamily.NetflowRate.Sub(streamRecordsBefore.GVGFamily.NetflowRate), gvgFamilyRate) + s.Require().Equal(streamRecordsAfter.GVG.NetflowRate.Sub(streamRecordsBefore.GVG.NetflowRate), gvgRate) + s.Require().Equal(streamRecordsAfter.Tax.NetflowRate.Sub(streamRecordsBefore.Tax.NetflowRate), taxRate) +} + +func (s *PaymentTestSuite) TestStorageBill_SealObject_WithPriceChange() { + var err error + 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(), + } + + bucketName := s.createBucket(sp, user, 102400) + + // case: seal object with read price change and storage price change + _, _, objectName, objectId, checksums, payloadSize := s.createObject(user, bucketName, false) + + priceRes, err := s.Client.QueryGetSpStoragePriceByTime(ctx, &sptypes.QueryGetSpStoragePriceByTimeRequest{ + SpAddr: sp.OperatorKey.GetAddr().String(), + Timestamp: 0, + }) + s.Require().NoError(err) + // update new price + msgUpdatePrice := &sptypes.MsgUpdateSpStoragePrice{ + SpAddress: sp.OperatorKey.GetAddr().String(), + ReadPrice: priceRes.SpStoragePrice.ReadPrice.MulInt64(2), + FreeReadQuota: priceRes.SpStoragePrice.FreeReadQuota, + StorePrice: priceRes.SpStoragePrice.StorePrice.MulInt64(2), + } + s.SendTxBlock(sp.OperatorKey, msgUpdatePrice) + + streamRecordsBefore := s.getStreamRecords(streamAddresses) + gvgFamilyRateReadBefore, taxRateReadBefore, userTotalRateReadBefore := s.calculateReadRates(sp, bucketName) + + // seal object + s.sealObject(sp, gvg, bucketName, objectName, objectId, checksums) + + // assertions + streamRecordsAfter := s.getStreamRecords(streamAddresses) + gvgFamilyRateReadAfter, taxRateReadAfter, userTotalRateReadAfter := s.calculateReadRatesCurrentTimestamp(sp, bucketName) + gvgFamilyRateStore, gvgRateStore, taxRateStore, userTotalRateStore := s.calculateStorageRatesCurrentTimestamp(sp, bucketName, objectName, payloadSize) + + s.Require().Equal(streamRecordsAfter.User.StaticBalance, sdkmath.ZeroInt()) + s.Require().Equal(streamRecordsAfter.User.LockBalance, sdkmath.ZeroInt()) + + s.Require().Equal(streamRecordsAfter.User.NetflowRate.Sub(streamRecordsBefore.User.NetflowRate), userTotalRateReadAfter.Sub(userTotalRateReadBefore).Add(userTotalRateStore).Neg()) + s.Require().Equal(streamRecordsAfter.GVG.NetflowRate.Sub(streamRecordsBefore.GVG.NetflowRate), gvgRateStore) + s.Require().Equal(streamRecordsAfter.GVGFamily.NetflowRate.Sub(streamRecordsBefore.GVGFamily.NetflowRate), gvgFamilyRateReadAfter.Sub(gvgFamilyRateReadBefore).Add(gvgFamilyRateStore)) + s.Require().Equal(streamRecordsAfter.Tax.NetflowRate.Sub(streamRecordsBefore.Tax.NetflowRate), taxRateReadAfter.Sub(taxRateReadBefore).Add(taxRateStore)) +} + +func (s *PaymentTestSuite) TestStorageBill_SealObject_WithPriceChangeValidatorTaxRateChange() { + defer s.revertParams() + + var err error + 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(), + } + + bucketName := s.createBucket(sp, user, 102400) + + // case: seal object with read price change and storage price change + _, _, objectName, objectId, checksums, payloadSize := s.createObject(user, bucketName, false) + + priceRes, err := s.Client.QueryGetSpStoragePriceByTime(ctx, &sptypes.QueryGetSpStoragePriceByTimeRequest{ + SpAddr: sp.OperatorKey.GetAddr().String(), + Timestamp: 0, + }) + s.Require().NoError(err) + // update new price + msgUpdatePrice := &sptypes.MsgUpdateSpStoragePrice{ + SpAddress: sp.OperatorKey.GetAddr().String(), + ReadPrice: priceRes.SpStoragePrice.ReadPrice.MulInt64(2), + FreeReadQuota: priceRes.SpStoragePrice.FreeReadQuota, + StorePrice: priceRes.SpStoragePrice.StorePrice.MulInt64(2), + } + s.SendTxBlock(sp.OperatorKey, msgUpdatePrice) + + streamRecordsBefore := s.getStreamRecords(streamAddresses) + gvgFamilyRateReadBefore, taxRateReadBefore, userTotalRateReadBefore := s.calculateReadRates(sp, bucketName) + + // seal object + s.sealObject(sp, gvg, bucketName, objectName, objectId, checksums) + + // assertions + streamRecordsAfter := s.getStreamRecords(streamAddresses) + gvgFamilyRateReadAfter, taxRateReadAfter, userTotalRateReadAfter := s.calculateReadRatesCurrentTimestamp(sp, bucketName) + gvgFamilyRateStore, gvgRateStore, taxRateStore, userTotalRateStore := s.calculateStorageRatesCurrentTimestamp(sp, bucketName, objectName, payloadSize) + + s.Require().Equal(streamRecordsAfter.User.StaticBalance, sdkmath.ZeroInt()) + s.Require().Equal(streamRecordsAfter.User.LockBalance, sdkmath.ZeroInt()) + + s.Require().Equal(streamRecordsAfter.User.NetflowRate.Sub(streamRecordsBefore.User.NetflowRate), userTotalRateReadAfter.Sub(userTotalRateReadBefore).Add(userTotalRateStore).Neg()) + s.Require().Equal(streamRecordsAfter.GVG.NetflowRate.Sub(streamRecordsBefore.GVG.NetflowRate), gvgRateStore) + s.Require().Equal(streamRecordsAfter.GVGFamily.NetflowRate.Sub(streamRecordsBefore.GVGFamily.NetflowRate), gvgFamilyRateReadAfter.Sub(gvgFamilyRateReadBefore).Add(gvgFamilyRateStore)) + s.Require().Equal(streamRecordsAfter.Tax.NetflowRate.Sub(streamRecordsBefore.Tax.NetflowRate), taxRateReadAfter.Sub(taxRateReadBefore).Add(taxRateStore)) + + // update params + params := s.queryParams() + oldReserveTime := params.VersionedParams.ReserveTime + oldValidatorTaxRate := params.VersionedParams.ValidatorTaxRate + + params.VersionedParams.ReserveTime = oldReserveTime * 2 + params.VersionedParams.ValidatorTaxRate = oldValidatorTaxRate.MulInt64(2) + s.updateParams(params) + + _, _, objectName, objectId, checksums, payloadSize = s.createObject(user, bucketName, false) + s.sealObject(sp, gvg, bucketName, objectName, objectId, checksums) + + // assertions + streamRecordsAfter = s.getStreamRecords(streamAddresses) + gvgFamilyRateReadAfter, taxRateReadAfter, userTotalRateReadAfter = s.calculateReadRatesCurrentTimestamp(sp, bucketName) + 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()) + + s.Require().Equal(streamRecordsAfter.User.NetflowRate.Sub(streamRecordsBefore.User.NetflowRate), userTotalRateReadAfter.Sub(userTotalRateReadBefore).Add(userTotalRateStore).Neg()) + s.Require().Equal(streamRecordsAfter.GVG.NetflowRate.Sub(streamRecordsBefore.GVG.NetflowRate), gvgRateStore) + s.Require().Equal(streamRecordsAfter.GVGFamily.NetflowRate.Sub(streamRecordsBefore.GVGFamily.NetflowRate), gvgFamilyRateReadAfter.Sub(gvgFamilyRateReadBefore).Add(gvgFamilyRateStore)) + s.Require().Equal(streamRecordsAfter.Tax.NetflowRate.Sub(streamRecordsBefore.Tax.NetflowRate), taxRateReadAfter.Sub(taxRateReadBefore).Add(taxRateStore)) + + // revert price + msgUpdatePrice = &sptypes.MsgUpdateSpStoragePrice{ + SpAddress: sp.OperatorKey.GetAddr().String(), + ReadPrice: priceRes.SpStoragePrice.ReadPrice, + FreeReadQuota: priceRes.SpStoragePrice.FreeReadQuota, + StorePrice: priceRes.SpStoragePrice.StorePrice, + } + s.SendTxBlock(sp.OperatorKey, msgUpdatePrice) +} + +func (s *PaymentTestSuite) TestStorageBill_RejectSealObject_WithPriceChange() { + var err error + 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(), + } + + bucketName := s.createBucket(sp, user, 102400) + + // case: seal object with read price change and storage price change + _, _, objectName, _, _, _ := s.createObject(user, bucketName, false) + + priceRes, err := s.Client.QueryGetSpStoragePriceByTime(ctx, &sptypes.QueryGetSpStoragePriceByTimeRequest{ + SpAddr: sp.OperatorKey.GetAddr().String(), + Timestamp: 0, + }) + s.Require().NoError(err) + // update new price + msgUpdatePrice := &sptypes.MsgUpdateSpStoragePrice{ + SpAddress: sp.OperatorKey.GetAddr().String(), + ReadPrice: priceRes.SpStoragePrice.ReadPrice.MulInt64(2), + FreeReadQuota: priceRes.SpStoragePrice.FreeReadQuota, + StorePrice: priceRes.SpStoragePrice.StorePrice.MulInt64(2), + } + s.SendTxBlock(sp.OperatorKey, msgUpdatePrice) + + streamRecordsBefore := s.getStreamRecords(streamAddresses) + s.Require().True(streamRecordsBefore.User.LockBalance.IsPositive()) + + // reject seal object + s.rejectSealObject(sp, gvg, bucketName, objectName) + + // assertions + streamRecordsAfter := s.getStreamRecords(streamAddresses) + s.Require().True(streamRecordsAfter.User.StaticBalance.IsPositive()) + s.Require().Equal(streamRecordsAfter.User.LockBalance, sdkmath.ZeroInt()) + + s.Require().Equal(streamRecordsAfter.User.NetflowRate.Sub(streamRecordsBefore.User.NetflowRate).Int64(), int64(0)) + s.Require().Equal(streamRecordsAfter.GVG.NetflowRate.Sub(streamRecordsBefore.GVG.NetflowRate).Int64(), int64(0)) + s.Require().Equal(streamRecordsAfter.GVGFamily.NetflowRate.Sub(streamRecordsBefore.GVGFamily.NetflowRate).Int64(), int64(0)) + s.Require().Equal(streamRecordsAfter.Tax.NetflowRate.Sub(streamRecordsBefore.Tax.NetflowRate).Int64(), int64(0)) + + // revert price + msgUpdatePrice = &sptypes.MsgUpdateSpStoragePrice{ + SpAddress: sp.OperatorKey.GetAddr().String(), + ReadPrice: priceRes.SpStoragePrice.ReadPrice, + FreeReadQuota: priceRes.SpStoragePrice.FreeReadQuota, + StorePrice: priceRes.SpStoragePrice.StorePrice, + } + s.SendTxBlock(sp.OperatorKey, msgUpdatePrice) +} + +func (s *PaymentTestSuite) TestStorageBill_FullLifecycle() { + defer s.revertParams() + + var err error + 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] + + // query storage price + priceRes, err := s.Client.QueryGetSpStoragePriceByTime(ctx, &sptypes.QueryGetSpStoragePriceByTimeRequest{ + SpAddr: sp.OperatorKey.GetAddr().String(), + Timestamp: 0, + }) + s.Require().NoError(err) + s.T().Log("price", priceRes.SpStoragePrice) + + streamAddresses := []string{ + user.GetAddr().String(), + family.VirtualPaymentAddress, + gvg.VirtualPaymentAddress, + paymenttypes.ValidatorTaxPoolAddress.String(), + } + streamRecordsBefore := s.getStreamRecords(streamAddresses) + + // full lifecycle + bucketName1 := s.createBucket(sp, user, 0) + _, _, objectName1, _, _, _ := s.createObject(user, bucketName1, true) + _, _, objectName2, objectId2, checksums2, _ := s.createObject(user, bucketName1, false) + s.sealObject(sp, gvg, bucketName1, objectName2, objectId2, checksums2) + + bucketName2 := s.createBucket(sp, user, 1024) + _, _, objectName3, objectId3, checksums3, _ := s.createObject(user, bucketName2, false) + s.sealObject(sp, gvg, bucketName2, objectName3, objectId3, checksums3) + + // update params + params := s.queryParams() + params.VersionedParams.ReserveTime = params.VersionedParams.ReserveTime * 3 + params.ForcedSettleTime = params.ForcedSettleTime * 2 + s.updateParams(params) + + _, _, objectName4, objectId4, checksums4, _ := s.createObject(user, bucketName2, false) + s.sealObject(sp, gvg, bucketName2, objectName4, objectId4, checksums4) + + bucketName3 := s.createBucket(sp, user, 1024) + _, _, objectName5, objectId5, checksums5, _ := s.createObject(user, bucketName3, false) + s.sealObject(sp, gvg, bucketName3, objectName5, objectId5, checksums5) + + // update new price + msgUpdatePrice := &sptypes.MsgUpdateSpStoragePrice{ + SpAddress: sp.OperatorKey.GetAddr().String(), + ReadPrice: priceRes.SpStoragePrice.ReadPrice, + FreeReadQuota: priceRes.SpStoragePrice.FreeReadQuota, + StorePrice: priceRes.SpStoragePrice.StorePrice.MulInt64(10000), + } + s.SendTxBlock(sp.OperatorKey, msgUpdatePrice) + + // update params + params = s.queryParams() + params.VersionedParams.ReserveTime = params.VersionedParams.ReserveTime / 2 + params.ForcedSettleTime = params.ForcedSettleTime / 3 + s.updateParams(params) + + _, _, objectName6, objectId6, checksums6, _ := s.createObject(user, bucketName3, false) + s.sealObject(sp, gvg, bucketName3, objectName6, objectId6, checksums6) + + bucketName4 := s.createBucket(sp, user, 1024) + _, _, objectName7, objectId7, checksums7, _ := s.createObject(user, bucketName4, false) + s.sealObject(sp, gvg, bucketName4, objectName7, objectId7, checksums7) + + // update params + params = s.queryParams() + params.VersionedParams.ValidatorTaxRate = params.VersionedParams.ValidatorTaxRate.MulInt64(2) + s.updateParams(params) + + _, _, objectName8, objectId8, checksums8, _ := s.createObject(user, bucketName4, false) + s.sealObject(sp, gvg, bucketName4, objectName8, objectId8, checksums8) + + time.Sleep(3 * time.Second) + + _ = s.deleteObject(user, bucketName1, objectName1) + _ = s.deleteObject(user, bucketName1, objectName2) + + // update params + params = s.queryParams() + params.VersionedParams.ValidatorTaxRate = params.VersionedParams.ValidatorTaxRate.MulInt64(3) + s.updateParams(params) + + _ = s.deleteObject(user, bucketName2, objectName3) + _ = s.deleteObject(user, bucketName2, objectName4) + err = s.deleteBucket(user, bucketName1) + s.Require().Error(err) + err = s.deleteBucket(user, bucketName2) + s.Require().Error(err) + + _ = s.deleteObject(user, bucketName3, objectName5) + _ = s.deleteObject(user, bucketName3, objectName6) + _ = s.deleteObject(user, bucketName4, objectName7) + _ = s.deleteObject(user, bucketName4, objectName8) + err = s.deleteBucket(user, bucketName3) + s.Require().Error(err) + err = s.deleteBucket(user, bucketName4) + s.Require().Error(err) + + // assertions + streamRecordsAfter := s.getStreamRecords(streamAddresses) + s.Require().True(!streamRecordsAfter.User.StaticBalance.IsZero()) + s.Require().Equal(streamRecordsAfter.User.NetflowRate.Sub(streamRecordsBefore.User.NetflowRate).Int64(), int64(0)) + s.Require().Equal(streamRecordsAfter.GVGFamily.NetflowRate.Sub(streamRecordsBefore.GVGFamily.NetflowRate).Int64(), int64(0)) + s.Require().Equal(streamRecordsAfter.GVG.NetflowRate.Sub(streamRecordsBefore.GVG.NetflowRate).Int64(), int64(0)) + s.Require().Equal(streamRecordsAfter.Tax.NetflowRate.Sub(streamRecordsBefore.Tax.NetflowRate).Int64(), int64(0)) + + // revert price + msgUpdatePrice = &sptypes.MsgUpdateSpStoragePrice{ + SpAddress: sp.OperatorKey.GetAddr().String(), + ReadPrice: priceRes.SpStoragePrice.ReadPrice, + FreeReadQuota: priceRes.SpStoragePrice.FreeReadQuota, + StorePrice: priceRes.SpStoragePrice.StorePrice, + } + s.SendTxBlock(sp.OperatorKey, msgUpdatePrice) +} + // TestStorageBill_CopyObject_WithoutPriceChange func (s *PaymentTestSuite) TestStorageBill_CopyObject_WithoutPriceChange() { var err error diff --git a/e2e/tests/storage_test.go b/e2e/tests/storage_test.go index 5b04412ba..4aa84e72e 100644 --- a/e2e/tests/storage_test.go +++ b/e2e/tests/storage_test.go @@ -99,6 +99,26 @@ func (s *StorageTestSuite) TestCreateBucket() { s.Require().NoError(err) s.Require().Equal(queryHeadBucketResponseAfterUpdateBucket.BucketInfo.Visibility, storagetypes.VISIBILITY_TYPE_PRIVATE) + // verify HeadBucketById + queryHeadBucketResponseAfterUpdateBucket, err = s.Client.HeadBucketById(ctx, &storagetypes.QueryHeadBucketByIdRequest{BucketId: queryHeadBucketResponseAfterUpdateBucket.BucketInfo.Id.String()}) + s.Require().NoError(err) + s.Require().Equal(queryHeadBucketResponseAfterUpdateBucket.BucketInfo.Visibility, storagetypes.VISIBILITY_TYPE_PRIVATE) + s.Require().Equal(queryHeadBucketResponseAfterUpdateBucket.BucketInfo.BucketName, bucketName) + + // verify HeadBucketNFT + headBucketNftResponse, err := s.Client.HeadBucketNFT(ctx, &storagetypes.QueryNFTRequest{ + TokenId: queryHeadBucketResponseAfterUpdateBucket.BucketInfo.Id.String(), + }) + s.Require().NoError(err) + s.Require().Equal(headBucketNftResponse.MetaData.BucketName, bucketName) + + // verify QueryIsPriceChanged + isPriceChanged, err := s.Client.QueryIsPriceChanged(ctx, &storagetypes.QueryIsPriceChangedRequest{ + BucketName: bucketName, + }) + s.Require().NoError(err) + s.Require().Equal(isPriceChanged.Changed, false) + // DeleteBucket msgDeleteBucket := storagetypes.NewMsgDeleteBucket(user.GetAddr(), bucketName) s.SendTxBlock(user, msgDeleteBucket) @@ -205,6 +225,21 @@ func (s *StorageTestSuite) TestCreateObject() { s.Require().Equal(len(queryListObjectsResponse.ObjectInfos), 1) s.Require().Equal(queryListObjectsResponse.ObjectInfos[0].ObjectName, objectName) + // verify ListObjectsByBucketId + queryListObjectsResponse, err = s.Client.ListObjectsByBucketId(ctx, &storagetypes.QueryListObjectsByBucketIdRequest{ + BucketId: queryHeadBucketResponse.BucketInfo.Id.String(), + }) + s.Require().NoError(err) + s.Require().Equal(len(queryListObjectsResponse.ObjectInfos), 1) + s.Require().Equal(queryListObjectsResponse.ObjectInfos[0].ObjectName, objectName) + + // verify HeadObjectNFT + headObjectNftResponse, err := s.Client.HeadObjectNFT(ctx, &storagetypes.QueryNFTRequest{ + TokenId: queryListObjectsResponse.ObjectInfos[0].Id.String(), + }) + s.Require().NoError(err) + s.Require().Equal(headObjectNftResponse.MetaData.ObjectName, objectName) + // UpdateObjectInfo updateObjectInfo := storagetypes.NewMsgUpdateObjectInfo( user.GetAddr(), bucketName, objectName, storagetypes.VISIBILITY_TYPE_INHERIT) @@ -218,6 +253,12 @@ func (s *StorageTestSuite) TestCreateObject() { s.Require().NoError(err) s.Require().Equal(queryHeadObjectAfterUpdateObjectResponse.ObjectInfo.Visibility, storagetypes.VISIBILITY_TYPE_INHERIT) + // verify HeadObjectById + queryHeadObjectAfterUpdateObjectResponse, err = s.Client.HeadObjectById(context.Background(), &storagetypes.QueryHeadObjectByIdRequest{ObjectId: queryHeadObjectAfterUpdateObjectResponse.ObjectInfo.Id.String()}) + s.Require().NoError(err) + s.Require().Equal(queryHeadObjectAfterUpdateObjectResponse.ObjectInfo.Visibility, storagetypes.VISIBILITY_TYPE_INHERIT) + s.Require().Equal(queryHeadObjectAfterUpdateObjectResponse.ObjectInfo.ObjectName, objectName) + // DeleteObject msgDeleteObject := storagetypes.NewMsgDeleteObject(user.GetAddr(), bucketName, objectName) s.SendTxBlock(user, msgDeleteObject) @@ -246,6 +287,13 @@ func (s *StorageTestSuite) TestCreateGroup() { s.Require().Equal(queryHeadGroupResp.GroupInfo.GroupName, groupName) s.Require().Equal(queryHeadGroupResp.GroupInfo.Owner, owner.GetAddr().String()) + // 2.1. HeadGroupNFT + headGroupNftResponse, err := s.Client.HeadGroupNFT(ctx, &storagetypes.QueryNFTRequest{ + TokenId: queryHeadGroupResp.GroupInfo.Id.String(), + }) + s.Require().NoError(err) + s.Require().Equal(headGroupNftResponse.MetaData.GroupName, groupName) + // 3. ListGroup queryListGroupReq := storagetypes.QueryListGroupRequest{GroupOwner: owner.GetAddr().String()} queryListGroupResp, err := s.Client.ListGroup(ctx, &queryListGroupReq) @@ -292,6 +340,69 @@ func (s *StorageTestSuite) TestCreateGroup() { s.SendTxBlockWithExpectErrorString(msgCreateGroup, owner, "exists") } +func (s *StorageTestSuite) TestLeaveGroup() { + ctx := context.Background() + + owner := s.GenAndChargeAccounts(1, 1000000)[0] + member := s.GenAndChargeAccounts(1, 1000000)[0] + groupName := storageutils.GenRandomGroupName() + + // 1. CreateGroup + msgCreateGroup := storagetypes.NewMsgCreateGroup(owner.GetAddr(), groupName, []sdk.AccAddress{member.GetAddr()}, "") + s.SendTxBlock(owner, msgCreateGroup) + s.T().Logf("CerateGroup success, owner: %s, group name: %s", owner.GetAddr().String(), groupName) + + // 2. HeadGroup + queryHeadGroupReq := storagetypes.QueryHeadGroupRequest{GroupOwner: owner.GetAddr().String(), GroupName: groupName} + queryHeadGroupResp, err := s.Client.HeadGroup(ctx, &queryHeadGroupReq) + s.Require().NoError(err) + s.Require().Equal(queryHeadGroupResp.GroupInfo.GroupName, groupName) + s.Require().Equal(queryHeadGroupResp.GroupInfo.Owner, owner.GetAddr().String()) + + // 2.1. HeadGroupNFT + headGroupNftResponse, err := s.Client.HeadGroupNFT(ctx, &storagetypes.QueryNFTRequest{ + TokenId: queryHeadGroupResp.GroupInfo.Id.String(), + }) + s.Require().NoError(err) + s.Require().Equal(headGroupNftResponse.MetaData.GroupName, groupName) + + // 3. ListGroup + queryListGroupReq := storagetypes.QueryListGroupRequest{GroupOwner: owner.GetAddr().String()} + queryListGroupResp, err := s.Client.ListGroup(ctx, &queryListGroupReq) + s.Require().NoError(err) + s.Require().GreaterOrEqual(len(queryListGroupResp.GroupInfos), 1) + + // 3. HeadGroupMember + queryHeadGroupMemberReq := storagetypes.QueryHeadGroupMemberRequest{ + Member: member.GetAddr().String(), + GroupName: groupName, + GroupOwner: owner.GetAddr().String(), + } + queryHeadGroupMemberResp, err := s.Client.HeadGroupMember(ctx, &queryHeadGroupMemberReq) + s.Require().NoError(err) + s.Require().Equal(queryHeadGroupMemberResp.GroupMember.GroupId, queryHeadGroupResp.GroupInfo.Id) + + // 4. UpdateGroupMember + member2 := s.GenAndChargeAccounts(1, 1000000)[0] + membersToAdd := []sdk.AccAddress{member2.GetAddr()} + membersToDelete := []sdk.AccAddress{member.GetAddr()} + msgUpdateGroupMember := storagetypes.NewMsgUpdateGroupMember(owner.GetAddr(), owner.GetAddr(), groupName, membersToAdd, membersToDelete) + s.SendTxBlock(owner, msgUpdateGroupMember) + + // 5. leave group + msgLeaveGroup := storagetypes.NewMsgLeaveGroup(member2.GetAddr(), owner.GetAddr(), groupName) + s.SendTxBlock(member2, msgLeaveGroup) + + // 6. HeadGroupMember (leave) + queryHeadGroupMemberReqDelete := storagetypes.QueryHeadGroupMemberRequest{ + Member: member2.GetAddr().String(), + GroupName: groupName, + GroupOwner: owner.GetAddr().String(), + } + _, err = s.Client.HeadGroupMember(ctx, &queryHeadGroupMemberReqDelete) + s.Require().True(strings.Contains(err.Error(), storagetypes.ErrNoSuchGroupMember.Error())) +} + func (s *StorageTestSuite) TestDeleteBucket() { var err error user := s.GenAndChargeAccounts(1, 1000000)[0] diff --git a/testutil/app.go b/testutil/app.go new file mode 100644 index 000000000..4a62c3237 --- /dev/null +++ b/testutil/app.go @@ -0,0 +1,82 @@ +package testutil + +import ( + "encoding/hex" + "encoding/json" + "io" + + dbm "github.com/cometbft/cometbft-db" + abci "github.com/cometbft/cometbft/abci/types" + "github.com/cometbft/cometbft/libs/log" + tmtypes "github.com/cometbft/cometbft/types" + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/crypto/keys/eth/ethsecp256k1" + "github.com/cosmos/cosmos-sdk/testutil/mock" + simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + + "github.com/bnb-chain/greenfield/app" + "github.com/bnb-chain/greenfield/app/params" + "github.com/bnb-chain/greenfield/sdk/client/test" +) + +func NewTestApp( + logger log.Logger, + db dbm.DB, + traceStore io.Writer, + loadLatest bool, + chainID string, + options ...func(baseApp *baseapp.BaseApp), +) (*app.App, params.EncodingConfig, error) { + // create public key + privVal := mock.NewPV() + pubKey, _ := privVal.GetPubKey() + + // create validator set with single validator + validator := tmtypes.NewValidator(pubKey, 1) + valSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{validator}) + + // generate genesis account + bz, _ := hex.DecodeString(test.TEST_PUBKEY) + faucetPubKey := ðsecp256k1.PubKey{Key: bz} + + acc := authtypes.NewBaseAccount(faucetPubKey.Address().Bytes(), faucetPubKey, 0, 0) + balance := banktypes.Balance{ + Address: acc.GetAddress().String(), + Coins: sdk.NewCoins(sdk.NewCoin(test.TEST_TOKEN_NAME, sdk.NewInt(100000000000000))), + } + + encCfg := app.MakeEncodingConfig() + options = append(options, baseapp.SetChainID(chainID)) + nApp := app.New( + logger, + db, + traceStore, + loadLatest, + app.DefaultNodeHome, + 0, + encCfg, + &app.AppConfig{CrossChain: app.NewDefaultAppConfig().CrossChain}, + simtestutil.EmptyAppOptions{}, + options..., + ) + + genesisState := app.NewDefaultGenesisState(encCfg.Marshaler) + genesisState, _ = simtestutil.GenesisStateWithValSet(nApp.AppCodec(), genesisState, valSet, []authtypes.GenesisAccount{acc}, balance) + + stateBytes, _ := json.MarshalIndent(genesisState, "", " ") + + // Initialize the chain + nApp.InitChain( + abci.RequestInitChain{ + ChainId: chainID, + Validators: []abci.ValidatorUpdate{}, + AppStateBytes: stateBytes, + }, + ) + nApp.Commit() + + return nApp, encCfg, nil +} diff --git a/x/bridge/client/cli/query_test.go b/x/bridge/client/cli/query_test.go new file mode 100644 index 000000000..00b7d55be --- /dev/null +++ b/x/bridge/client/cli/query_test.go @@ -0,0 +1,54 @@ +package cli_test + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/client/flags" + clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" + "github.com/cosmos/gogoproto/proto" + + "github.com/bnb-chain/greenfield/x/bridge/client/cli" + "github.com/bnb-chain/greenfield/x/bridge/types" +) + +func (s *CLITestSuite) TestQueryCmd() { + commonFlags := []string{ + fmt.Sprintf("--%s=%s", flags.FlagOutput, "json"), + } + + testCases := []struct { + name string + args []string + expectErr bool + expectErrMsg string + respType proto.Message + }{ + { + "query params", + append( + []string{ + "params", + }, + commonFlags..., + ), + false, "", &types.QueryParamsResponse{}, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.GetQueryCmd() + out, err := clitestutil.ExecTestCLICmd(s.clientCtx, cmd, tc.args) + + if tc.expectErr { + s.Require().Error(err) + s.Require().Contains(err.Error(), tc.expectErrMsg) + } else { + s.Require().NoError(err) + s.Require().NoError(s.clientCtx.Codec.UnmarshalJSON(out.Bytes(), tc.respType), out.String()) + } + }) + } +} diff --git a/x/bridge/client/cli/tx_test.go b/x/bridge/client/cli/tx_test.go new file mode 100644 index 000000000..3f04f8dfb --- /dev/null +++ b/x/bridge/client/cli/tx_test.go @@ -0,0 +1,128 @@ +package cli_test + +import ( + "bytes" + "fmt" + "io" + "testing" + + abci "github.com/cometbft/cometbft/abci/types" + rpcclientmock "github.com/cometbft/cometbft/rpc/client/mock" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/suite" + + "github.com/bnb-chain/greenfield/app" + "github.com/bnb-chain/greenfield/app/params" + "github.com/bnb-chain/greenfield/sdk/client/test" + "github.com/bnb-chain/greenfield/testutil/sample" + "github.com/bnb-chain/greenfield/x/bridge/client/cli" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/testutil" +) + +type CLITestSuite struct { + suite.Suite + + kr keyring.Keyring + baseCtx client.Context + encCfg params.EncodingConfig + clientCtx client.Context +} + +func TestCLITestSuite(t *testing.T) { + suite.Run(t, new(CLITestSuite)) +} + +func (s *CLITestSuite) SetupSuite() { + s.T().Log("setting up integration test suite") + + s.encCfg = app.MakeEncodingConfig() + s.kr = keyring.NewInMemory(s.encCfg.Marshaler) + s.baseCtx = client.Context{}. + WithKeyring(s.kr). + WithTxConfig(s.encCfg.TxConfig). + WithCodec(s.encCfg.Marshaler). + WithClient(clitestutil.MockTendermintRPC{Client: rpcclientmock.Client{}}). + WithAccountRetriever(client.MockAccountRetriever{}). + WithOutput(io.Discard). + WithChainID(test.TEST_CHAIN_ID) + + accounts := testutil.CreateKeyringAccounts(s.T(), s.kr, 1) + s.baseCtx = s.baseCtx.WithFrom(accounts[0].Address.String()) + s.baseCtx = s.baseCtx.WithFromName(accounts[0].Name) + s.baseCtx = s.baseCtx.WithFromAddress(accounts[0].Address) + + var outBuf bytes.Buffer + ctxGen := func() client.Context { + bz, _ := s.encCfg.Marshaler.Marshal(&sdk.TxResponse{}) + c := clitestutil.NewMockTendermintRPC(abci.ResponseQuery{ + Value: bz, + }) + + return s.baseCtx.WithClient(c) + } + s.clientCtx = ctxGen().WithOutput(&outBuf) + + if testing.Short() { + s.T().Skip("skipping test in unit-tests mode.") + } +} + +func (s *CLITestSuite) TestTxCmdTransferOut() { + clientCtx := s.clientCtx + + commonFlags := []string{ + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + } + + testCases := []struct { + name string + args []string + expectErr bool + expErrMsg string + }{ + { + "invalid to address", + append( + []string{ + "invalidAddress", + "1000000000000000000BNB", + }, + commonFlags..., + ), + true, "invalid address hex length", + }, + { + "success case", + append( + []string{ + sample.RandAccAddressHex(), + "1000000000000000000BNB", + }, + commonFlags..., + ), + false, "", + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.CmdTransferOut() + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + + if tc.expectErr { + s.Require().Error(err) + s.Require().Contains(err.Error(), tc.expErrMsg) + } else { + s.Require().NoError(err) + s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), &sdk.TxResponse{}), out.String()) + } + }) + } +} diff --git a/x/challenge/client/cli/query.go b/x/challenge/client/cli/query.go index d4a8ce07a..fc0d8924f 100644 --- a/x/challenge/client/cli/query.go +++ b/x/challenge/client/cli/query.go @@ -11,7 +11,7 @@ import ( ) // GetQueryCmd returns the cli query commands for this module -func GetQueryCmd(queryRoute string) *cobra.Command { +func GetQueryCmd() *cobra.Command { // Group challenge queries under a subcommand cmd := &cobra.Command{ Use: types.ModuleName, diff --git a/x/challenge/client/cli/query_test.go b/x/challenge/client/cli/query_test.go new file mode 100644 index 000000000..cfb109862 --- /dev/null +++ b/x/challenge/client/cli/query_test.go @@ -0,0 +1,74 @@ +package cli_test + +import ( + "fmt" + + clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" + "github.com/cosmos/gogoproto/proto" + + "github.com/bnb-chain/greenfield/x/challenge/client/cli" + "github.com/bnb-chain/greenfield/x/challenge/types" + "github.com/cosmos/cosmos-sdk/client/flags" +) + +func (s *CLITestSuite) TestQueryCmd() { + commonFlags := []string{ + fmt.Sprintf("--%s=%s", flags.FlagOutput, "json"), + } + + testCases := []struct { + name string + args []string + expectErr bool + expectErrMsg string + respType proto.Message + }{ + { + "query params", + append( + []string{ + "params", + }, + commonFlags..., + ), + false, "", &types.QueryParamsResponse{}, + }, + { + "query latest-attested-challenges", + append( + []string{ + "latest-attested-challenges", + }, + commonFlags..., + ), + false, "", &types.QueryLatestAttestedChallengesResponse{}, + }, + { + "query inturn-attestation-submitter", + append( + []string{ + "inturn-attestation-submitter", + }, + commonFlags..., + ), + false, "", &types.QueryInturnAttestationSubmitterResponse{}, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.GetQueryCmd() + out, err := clitestutil.ExecTestCLICmd(s.clientCtx, cmd, tc.args) + + if tc.expectErr { + s.Require().Error(err) + s.Require().Contains(err.Error(), tc.expectErrMsg) + } else { + s.Require().NoError(err) + s.Require().NoError(s.clientCtx.Codec.UnmarshalJSON(out.Bytes(), tc.respType), out.String()) + } + }) + } +} diff --git a/x/challenge/client/cli/tx_test.go b/x/challenge/client/cli/tx_test.go new file mode 100644 index 000000000..0c726d75b --- /dev/null +++ b/x/challenge/client/cli/tx_test.go @@ -0,0 +1,70 @@ +package cli_test + +import ( + "bytes" + "io" + "testing" + + abci "github.com/cometbft/cometbft/abci/types" + rpcclientmock "github.com/cometbft/cometbft/rpc/client/mock" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/cosmos/cosmos-sdk/testutil" + clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/suite" + + "github.com/bnb-chain/greenfield/app" + "github.com/bnb-chain/greenfield/app/params" + "github.com/bnb-chain/greenfield/sdk/client/test" +) + +type CLITestSuite struct { + suite.Suite + + kr keyring.Keyring + baseCtx client.Context + encCfg params.EncodingConfig + clientCtx client.Context +} + +func TestCLITestSuite(t *testing.T) { + suite.Run(t, new(CLITestSuite)) +} + +func (s *CLITestSuite) SetupSuite() { + s.T().Log("setting up integration test suite") + + s.encCfg = app.MakeEncodingConfig() + s.kr = keyring.NewInMemory(s.encCfg.Marshaler) + s.baseCtx = client.Context{}. + WithKeyring(s.kr). + WithTxConfig(s.encCfg.TxConfig). + WithCodec(s.encCfg.Marshaler). + WithClient(clitestutil.MockTendermintRPC{Client: rpcclientmock.Client{}}). + WithAccountRetriever(client.MockAccountRetriever{}). + WithOutput(io.Discard). + WithChainID(test.TEST_CHAIN_ID) + + accounts := testutil.CreateKeyringAccounts(s.T(), s.kr, 1) + s.baseCtx = s.baseCtx.WithFrom(accounts[0].Address.String()) + s.baseCtx = s.baseCtx.WithFromName(accounts[0].Name) + s.baseCtx = s.baseCtx.WithFromAddress(accounts[0].Address) + + var outBuf bytes.Buffer + ctxGen := func() client.Context { + bz, _ := s.encCfg.Marshaler.Marshal(&sdk.TxResponse{}) + c := clitestutil.NewMockTendermintRPC(abci.ResponseQuery{ + Value: bz, + }) + + return s.baseCtx.WithClient(c) + } + s.clientCtx = ctxGen().WithOutput(&outBuf) + + if testing.Short() { + s.T().Skip("skipping test in unit-tests mode.") + } +} + +// TODO: Add more tests diff --git a/x/challenge/module.go b/x/challenge/module.go index 41ae438a2..808a8702f 100644 --- a/x/challenge/module.go +++ b/x/challenge/module.go @@ -78,7 +78,7 @@ func (a AppModuleBasic) GetTxCmd() *cobra.Command { // GetQueryCmd returns the root query command for the module. The subcommands of this root command are used by end-users to generate new queries to the subset of the state defined by the module func (AppModuleBasic) GetQueryCmd() *cobra.Command { - return cli.GetQueryCmd(types.StoreKey) + return cli.GetQueryCmd() } // ---------------------------------------------------------------------------- diff --git a/x/payment/client/cli/query_test.go b/x/payment/client/cli/query_test.go new file mode 100644 index 000000000..3428869ee --- /dev/null +++ b/x/payment/client/cli/query_test.go @@ -0,0 +1,150 @@ +package cli_test + +import ( + "fmt" + + clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" + "github.com/cosmos/gogoproto/proto" + + "github.com/bnb-chain/greenfield/testutil/sample" + "github.com/bnb-chain/greenfield/x/payment/client/cli" + "github.com/bnb-chain/greenfield/x/payment/types" + "github.com/cosmos/cosmos-sdk/client/flags" +) + +func (s *CLITestSuite) TestQueryCmd() { + commonFlags := []string{ + fmt.Sprintf("--%s=%s", flags.FlagOutput, "json"), + } + + testCases := []struct { + name string + args []string + expectErr bool + expectErrMsg string + respType proto.Message + }{ + { + "query params", + append( + []string{ + "params", + }, + commonFlags..., + ), + false, "", &types.QueryParamsResponse{}, + }, + { + "query dynamic-balance", + append( + []string{ + "dynamic-balance", + sample.RandAccAddressHex(), + }, + commonFlags..., + ), + false, "", &types.QueryDynamicBalanceResponse{}, + }, + { + "query get-payment-accounts-by-owner", + append( + []string{ + "get-payment-accounts-by-owner", + sample.RandAccAddressHex(), + }, + commonFlags..., + ), + false, "", &types.QueryGetPaymentAccountsByOwnerResponse{}, + }, + { + "query list-auto-settle-record", + append( + []string{ + "list-auto-settle-record", + }, + commonFlags..., + ), + false, "", &types.QueryAllAutoSettleRecordResponse{}, + }, + { + "query list-payment-account", + append( + []string{ + "list-payment-account", + }, + commonFlags..., + ), + false, "", &types.QueryAllPaymentAccountResponse{}, + }, + { + "query list-payment-account-count", + append( + []string{ + "list-payment-account-count", + }, + commonFlags..., + ), + false, "", &types.QueryAllPaymentAccountCountResponse{}, + }, + { + "query list-stream-record", + append( + []string{ + "list-stream-record", + }, + commonFlags..., + ), + false, "", &types.QueryAllStreamRecordResponse{}, + }, + { + "query show-payment-account", + append( + []string{ + "show-payment-account", + sample.RandAccAddressHex(), + }, + commonFlags..., + ), + false, "", &types.QueryGetPaymentAccountResponse{}, + }, + { + "query show-payment-account-count", + append( + []string{ + "show-payment-account-count", + sample.RandAccAddressHex(), + }, + commonFlags..., + ), + false, "", &types.QueryGetPaymentAccountCountResponse{}, + }, + { + "query show-stream-record", + append( + []string{ + "show-stream-record", + sample.RandAccAddressHex(), + }, + commonFlags..., + ), + false, "", &types.QueryGetStreamRecordResponse{}, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.GetQueryCmd() + out, err := clitestutil.ExecTestCLICmd(s.clientCtx, cmd, tc.args) + + if tc.expectErr { + s.Require().Error(err) + s.Require().Contains(err.Error(), tc.expectErrMsg) + } else { + s.Require().NoError(err) + s.Require().NoError(s.clientCtx.Codec.UnmarshalJSON(out.Bytes(), tc.respType), out.String()) + } + }) + } +} diff --git a/x/payment/client/cli/tx_test.go b/x/payment/client/cli/tx_test.go new file mode 100644 index 000000000..0c726d75b --- /dev/null +++ b/x/payment/client/cli/tx_test.go @@ -0,0 +1,70 @@ +package cli_test + +import ( + "bytes" + "io" + "testing" + + abci "github.com/cometbft/cometbft/abci/types" + rpcclientmock "github.com/cometbft/cometbft/rpc/client/mock" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/cosmos/cosmos-sdk/testutil" + clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/suite" + + "github.com/bnb-chain/greenfield/app" + "github.com/bnb-chain/greenfield/app/params" + "github.com/bnb-chain/greenfield/sdk/client/test" +) + +type CLITestSuite struct { + suite.Suite + + kr keyring.Keyring + baseCtx client.Context + encCfg params.EncodingConfig + clientCtx client.Context +} + +func TestCLITestSuite(t *testing.T) { + suite.Run(t, new(CLITestSuite)) +} + +func (s *CLITestSuite) SetupSuite() { + s.T().Log("setting up integration test suite") + + s.encCfg = app.MakeEncodingConfig() + s.kr = keyring.NewInMemory(s.encCfg.Marshaler) + s.baseCtx = client.Context{}. + WithKeyring(s.kr). + WithTxConfig(s.encCfg.TxConfig). + WithCodec(s.encCfg.Marshaler). + WithClient(clitestutil.MockTendermintRPC{Client: rpcclientmock.Client{}}). + WithAccountRetriever(client.MockAccountRetriever{}). + WithOutput(io.Discard). + WithChainID(test.TEST_CHAIN_ID) + + accounts := testutil.CreateKeyringAccounts(s.T(), s.kr, 1) + s.baseCtx = s.baseCtx.WithFrom(accounts[0].Address.String()) + s.baseCtx = s.baseCtx.WithFromName(accounts[0].Name) + s.baseCtx = s.baseCtx.WithFromAddress(accounts[0].Address) + + var outBuf bytes.Buffer + ctxGen := func() client.Context { + bz, _ := s.encCfg.Marshaler.Marshal(&sdk.TxResponse{}) + c := clitestutil.NewMockTendermintRPC(abci.ResponseQuery{ + Value: bz, + }) + + return s.baseCtx.WithClient(c) + } + s.clientCtx = ctxGen().WithOutput(&outBuf) + + if testing.Short() { + s.T().Skip("skipping test in unit-tests mode.") + } +} + +// TODO: Add more tests diff --git a/x/payment/keeper/msg_server_test.go b/x/payment/keeper/msg_server_test.go index 4c6f7ecd1..768d21d42 100644 --- a/x/payment/keeper/msg_server_test.go +++ b/x/payment/keeper/msg_server_test.go @@ -87,7 +87,7 @@ func (s *TestSuite) TestUpdateParams() { { name: "invalid authority", msg: types.MsgUpdateParams{ - Authority: s.paymentKeeper.GetAuthority(), + Authority: sample.RandAccAddressHex(), }, err: true, }, { diff --git a/x/permission/client/cli/query_test.go b/x/permission/client/cli/query_test.go new file mode 100644 index 000000000..44d63f5c6 --- /dev/null +++ b/x/permission/client/cli/query_test.go @@ -0,0 +1,54 @@ +package cli_test + +import ( + "fmt" + + clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" + "github.com/cosmos/gogoproto/proto" + + "github.com/bnb-chain/greenfield/x/payment/client/cli" + "github.com/bnb-chain/greenfield/x/payment/types" + "github.com/cosmos/cosmos-sdk/client/flags" +) + +func (s *CLITestSuite) TestQueryCmd() { + commonFlags := []string{ + fmt.Sprintf("--%s=%s", flags.FlagOutput, "json"), + } + + testCases := []struct { + name string + args []string + expectErr bool + expectErrMsg string + respType proto.Message + }{ + { + "query params", + append( + []string{ + "params", + }, + commonFlags..., + ), + false, "", &types.QueryParamsResponse{}, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.GetQueryCmd() + out, err := clitestutil.ExecTestCLICmd(s.clientCtx, cmd, tc.args) + + if tc.expectErr { + s.Require().Error(err) + s.Require().Contains(err.Error(), tc.expectErrMsg) + } else { + s.Require().NoError(err) + s.Require().NoError(s.clientCtx.Codec.UnmarshalJSON(out.Bytes(), tc.respType), out.String()) + } + }) + } +} diff --git a/x/permission/client/cli/tx_test.go b/x/permission/client/cli/tx_test.go new file mode 100644 index 000000000..0c726d75b --- /dev/null +++ b/x/permission/client/cli/tx_test.go @@ -0,0 +1,70 @@ +package cli_test + +import ( + "bytes" + "io" + "testing" + + abci "github.com/cometbft/cometbft/abci/types" + rpcclientmock "github.com/cometbft/cometbft/rpc/client/mock" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/cosmos/cosmos-sdk/testutil" + clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/suite" + + "github.com/bnb-chain/greenfield/app" + "github.com/bnb-chain/greenfield/app/params" + "github.com/bnb-chain/greenfield/sdk/client/test" +) + +type CLITestSuite struct { + suite.Suite + + kr keyring.Keyring + baseCtx client.Context + encCfg params.EncodingConfig + clientCtx client.Context +} + +func TestCLITestSuite(t *testing.T) { + suite.Run(t, new(CLITestSuite)) +} + +func (s *CLITestSuite) SetupSuite() { + s.T().Log("setting up integration test suite") + + s.encCfg = app.MakeEncodingConfig() + s.kr = keyring.NewInMemory(s.encCfg.Marshaler) + s.baseCtx = client.Context{}. + WithKeyring(s.kr). + WithTxConfig(s.encCfg.TxConfig). + WithCodec(s.encCfg.Marshaler). + WithClient(clitestutil.MockTendermintRPC{Client: rpcclientmock.Client{}}). + WithAccountRetriever(client.MockAccountRetriever{}). + WithOutput(io.Discard). + WithChainID(test.TEST_CHAIN_ID) + + accounts := testutil.CreateKeyringAccounts(s.T(), s.kr, 1) + s.baseCtx = s.baseCtx.WithFrom(accounts[0].Address.String()) + s.baseCtx = s.baseCtx.WithFromName(accounts[0].Name) + s.baseCtx = s.baseCtx.WithFromAddress(accounts[0].Address) + + var outBuf bytes.Buffer + ctxGen := func() client.Context { + bz, _ := s.encCfg.Marshaler.Marshal(&sdk.TxResponse{}) + c := clitestutil.NewMockTendermintRPC(abci.ResponseQuery{ + Value: bz, + }) + + return s.baseCtx.WithClient(c) + } + s.clientCtx = ctxGen().WithOutput(&outBuf) + + if testing.Short() { + s.T().Skip("skipping test in unit-tests mode.") + } +} + +// TODO: Add more tests diff --git a/x/sp/client/cli/query.go b/x/sp/client/cli/query.go index 8972c6b39..2bc625bf7 100644 --- a/x/sp/client/cli/query.go +++ b/x/sp/client/cli/query.go @@ -13,7 +13,7 @@ import ( ) // GetQueryCmd returns the cli query commands for this module -func GetQueryCmd(queryRoute string) *cobra.Command { +func GetQueryCmd() *cobra.Command { // Group sp queries under a subcommand cmd := &cobra.Command{ Use: types.ModuleName, @@ -67,7 +67,7 @@ func CmdStorageProviders() *cobra.Command { func CmdStorageProvider() *cobra.Command { cmd := &cobra.Command{ Use: "storage-provider [sp-id]", - Short: "Query storage provider with specify operator address", + Short: "Query storage provider with specify sp id", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) (err error) { reqSpID := args[0] @@ -103,8 +103,8 @@ func CmdStorageProvider() *cobra.Command { func CmdStorageProviderByOperatorAddress() *cobra.Command { cmd := &cobra.Command{ Use: "storage-provider-by-operator-address [operator address]", - Short: "Query StorageProviderByOperatorAddress", - Args: cobra.ExactArgs(0), + Short: "Query storage provider with specify operator address", + Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) (err error) { reqSpAddr := args[0] diff --git a/x/sp/client/cli/query_test.go b/x/sp/client/cli/query_test.go new file mode 100644 index 000000000..f7dbf1a80 --- /dev/null +++ b/x/sp/client/cli/query_test.go @@ -0,0 +1,87 @@ +package cli_test + +import ( + "fmt" + + clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" + "github.com/cosmos/gogoproto/proto" + + "github.com/bnb-chain/greenfield/testutil/sample" + "github.com/bnb-chain/greenfield/x/sp/client/cli" + "github.com/bnb-chain/greenfield/x/sp/types" + "github.com/cosmos/cosmos-sdk/client/flags" +) + +func (s *CLITestSuite) TestQueryCmd() { + commonFlags := []string{ + fmt.Sprintf("--%s=%s", flags.FlagOutput, "json"), + } + + testCases := []struct { + name string + args []string + expectErr bool + expectErrMsg string + respType proto.Message + }{ + { + "query params", + append( + []string{ + "params", + }, + commonFlags..., + ), + false, "", &types.QueryParamsResponse{}, + }, + { + "query storage-provider", + append( + []string{ + "storage-provider", + "1", + }, + commonFlags..., + ), + false, "", &types.QueryStorageProviderResponse{}, + }, + { + "query storage-provider-by-operator-address", + append( + []string{ + "storage-provider-by-operator-address", + sample.RandAccAddressHex(), + }, + commonFlags..., + ), + false, "", &types.QueryStorageProviderByOperatorAddressResponse{}, + }, + { + "query storage-providers", + append( + []string{ + "storage-providers", + }, + commonFlags..., + ), + false, "", &types.QueryStorageProvidersResponse{}, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.GetQueryCmd() + out, err := clitestutil.ExecTestCLICmd(s.clientCtx, cmd, tc.args) + + if tc.expectErr { + s.Require().Error(err) + s.Require().Contains(err.Error(), tc.expectErrMsg) + } else { + s.Require().NoError(err) + s.Require().NoError(s.clientCtx.Codec.UnmarshalJSON(out.Bytes(), tc.respType), out.String()) + } + }) + } +} diff --git a/x/sp/client/cli/tx.go b/x/sp/client/cli/tx.go index e02c951ca..7f1ebb6c3 100644 --- a/x/sp/client/cli/tx.go +++ b/x/sp/client/cli/tx.go @@ -212,7 +212,7 @@ func CmdEditStorageProvider() *cobra.Command { if err != nil { return err } - if len(blsPubKey) != 2*sdk.BLSPubKeyLength { + if len(blsPubKey) > 0 && len(blsPubKey) != 2*sdk.BLSPubKeyLength { return fmt.Errorf("invalid bls pubkey") } diff --git a/x/sp/client/cli/tx_test.go b/x/sp/client/cli/tx_test.go new file mode 100644 index 000000000..0c726d75b --- /dev/null +++ b/x/sp/client/cli/tx_test.go @@ -0,0 +1,70 @@ +package cli_test + +import ( + "bytes" + "io" + "testing" + + abci "github.com/cometbft/cometbft/abci/types" + rpcclientmock "github.com/cometbft/cometbft/rpc/client/mock" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/cosmos/cosmos-sdk/testutil" + clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/suite" + + "github.com/bnb-chain/greenfield/app" + "github.com/bnb-chain/greenfield/app/params" + "github.com/bnb-chain/greenfield/sdk/client/test" +) + +type CLITestSuite struct { + suite.Suite + + kr keyring.Keyring + baseCtx client.Context + encCfg params.EncodingConfig + clientCtx client.Context +} + +func TestCLITestSuite(t *testing.T) { + suite.Run(t, new(CLITestSuite)) +} + +func (s *CLITestSuite) SetupSuite() { + s.T().Log("setting up integration test suite") + + s.encCfg = app.MakeEncodingConfig() + s.kr = keyring.NewInMemory(s.encCfg.Marshaler) + s.baseCtx = client.Context{}. + WithKeyring(s.kr). + WithTxConfig(s.encCfg.TxConfig). + WithCodec(s.encCfg.Marshaler). + WithClient(clitestutil.MockTendermintRPC{Client: rpcclientmock.Client{}}). + WithAccountRetriever(client.MockAccountRetriever{}). + WithOutput(io.Discard). + WithChainID(test.TEST_CHAIN_ID) + + accounts := testutil.CreateKeyringAccounts(s.T(), s.kr, 1) + s.baseCtx = s.baseCtx.WithFrom(accounts[0].Address.String()) + s.baseCtx = s.baseCtx.WithFromName(accounts[0].Name) + s.baseCtx = s.baseCtx.WithFromAddress(accounts[0].Address) + + var outBuf bytes.Buffer + ctxGen := func() client.Context { + bz, _ := s.encCfg.Marshaler.Marshal(&sdk.TxResponse{}) + c := clitestutil.NewMockTendermintRPC(abci.ResponseQuery{ + Value: bz, + }) + + return s.baseCtx.WithClient(c) + } + s.clientCtx = ctxGen().WithOutput(&outBuf) + + if testing.Short() { + s.T().Skip("skipping test in unit-tests mode.") + } +} + +// TODO: Add more tests diff --git a/x/sp/module.go b/x/sp/module.go index ddab571e7..1cd21a2b7 100644 --- a/x/sp/module.go +++ b/x/sp/module.go @@ -78,7 +78,7 @@ func (a AppModuleBasic) GetTxCmd() *cobra.Command { // GetQueryCmd returns the root query command for the module. The subcommands of this root command are used by end-users to generate new queries to the subset of the state defined by the module func (AppModuleBasic) GetQueryCmd() *cobra.Command { - return cli.GetQueryCmd(types.StoreKey) + return cli.GetQueryCmd() } // ---------------------------------------------------------------------------- diff --git a/x/storage/client/cli/query.go b/x/storage/client/cli/query.go index b0f3fdf6d..6d8075541 100644 --- a/x/storage/client/cli/query.go +++ b/x/storage/client/cli/query.go @@ -12,7 +12,7 @@ import ( ) // GetQueryCmd returns the cli query commands for this module -func GetQueryCmd(queryRoute string) *cobra.Command { +func GetQueryCmd() *cobra.Command { // Group storage queries under a subcommand storageQueryCmd := &cobra.Command{ Use: types.ModuleName, @@ -109,7 +109,7 @@ func CmdHeadObject() *cobra.Command { func CmdListBuckets() *cobra.Command { cmd := &cobra.Command{ Use: "list-buckets", - Short: "Query list buckets of the user", + Short: "Query all list buckets", Args: cobra.ExactArgs(0), RunE: func(cmd *cobra.Command, args []string) (err error) { clientCtx := client.GetClientContextFromCmd(cmd) diff --git a/x/storage/client/cli/query_test.go b/x/storage/client/cli/query_test.go new file mode 100644 index 000000000..ff984294c --- /dev/null +++ b/x/storage/client/cli/query_test.go @@ -0,0 +1,149 @@ +package cli_test + +import ( + "fmt" + + clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" + "github.com/cosmos/gogoproto/proto" + + "github.com/bnb-chain/greenfield/testutil/sample" + "github.com/bnb-chain/greenfield/x/storage/client/cli" + "github.com/bnb-chain/greenfield/x/storage/types" + "github.com/cosmos/cosmos-sdk/client/flags" +) + +func (s *CLITestSuite) TestQueryCmd() { + commonFlags := []string{ + fmt.Sprintf("--%s=%s", flags.FlagOutput, "json"), + } + + testCases := []struct { + name string + args []string + expectErr bool + expectErrMsg string + respType proto.Message + }{ + { + "query params", + append( + []string{ + "params", + }, + commonFlags..., + ), + false, "", &types.QueryParamsResponse{}, + }, + { + "query head-bucket", + append( + []string{ + "head-bucket", + "bucketName", + }, + commonFlags..., + ), + false, "", &types.QueryHeadBucketResponse{}, + }, + { + "query head-group", + append( + []string{ + "head-group", + sample.RandAccAddressHex(), + "groupName", + }, + commonFlags..., + ), + false, "", &types.QueryHeadGroupResponse{}, + }, + { + "query head-group-member", + append( + []string{ + "head-group-member", + sample.RandAccAddressHex(), + "groupName", + sample.RandAccAddressHex(), + }, + commonFlags..., + ), + false, "", &types.QueryHeadGroupMemberResponse{}, + }, + { + "query head-object", + append( + []string{ + "head-object", + "bucketName", + "objectName", + }, + commonFlags..., + ), + false, "", &types.QueryHeadObjectResponse{}, + }, + { + "query list-buckets", + append( + []string{ + "list-buckets", + }, + commonFlags..., + ), + false, "", &types.QueryListBucketsResponse{}, + }, + { + "query list-group", + append( + []string{ + "list-group", + sample.RandAccAddressHex(), + }, + commonFlags..., + ), + false, "", &types.QueryListGroupResponse{}, + }, + { + "query list-objects", + append( + []string{ + "list-objects", + "bucketName", + }, + commonFlags..., + ), + false, "", &types.QueryListObjectsResponse{}, + }, + { + "query verify-permission", + append( + []string{ + "verify-permission", + sample.RandAccAddressHex(), + "bucketName", + "objectName", + "ACTION_TYPE_ALL", + }, + commonFlags..., + ), + false, "", &types.QueryVerifyPermissionResponse{}, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.GetQueryCmd() + out, err := clitestutil.ExecTestCLICmd(s.clientCtx, cmd, tc.args) + + if tc.expectErr { + s.Require().Error(err) + s.Require().Contains(err.Error(), tc.expectErrMsg) + } else { + s.Require().NoError(err) + s.Require().NoError(s.clientCtx.Codec.UnmarshalJSON(out.Bytes(), tc.respType), out.String()) + } + }) + } +} diff --git a/x/storage/client/cli/tx_test.go b/x/storage/client/cli/tx_test.go new file mode 100644 index 000000000..0c726d75b --- /dev/null +++ b/x/storage/client/cli/tx_test.go @@ -0,0 +1,70 @@ +package cli_test + +import ( + "bytes" + "io" + "testing" + + abci "github.com/cometbft/cometbft/abci/types" + rpcclientmock "github.com/cometbft/cometbft/rpc/client/mock" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/cosmos/cosmos-sdk/testutil" + clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/suite" + + "github.com/bnb-chain/greenfield/app" + "github.com/bnb-chain/greenfield/app/params" + "github.com/bnb-chain/greenfield/sdk/client/test" +) + +type CLITestSuite struct { + suite.Suite + + kr keyring.Keyring + baseCtx client.Context + encCfg params.EncodingConfig + clientCtx client.Context +} + +func TestCLITestSuite(t *testing.T) { + suite.Run(t, new(CLITestSuite)) +} + +func (s *CLITestSuite) SetupSuite() { + s.T().Log("setting up integration test suite") + + s.encCfg = app.MakeEncodingConfig() + s.kr = keyring.NewInMemory(s.encCfg.Marshaler) + s.baseCtx = client.Context{}. + WithKeyring(s.kr). + WithTxConfig(s.encCfg.TxConfig). + WithCodec(s.encCfg.Marshaler). + WithClient(clitestutil.MockTendermintRPC{Client: rpcclientmock.Client{}}). + WithAccountRetriever(client.MockAccountRetriever{}). + WithOutput(io.Discard). + WithChainID(test.TEST_CHAIN_ID) + + accounts := testutil.CreateKeyringAccounts(s.T(), s.kr, 1) + s.baseCtx = s.baseCtx.WithFrom(accounts[0].Address.String()) + s.baseCtx = s.baseCtx.WithFromName(accounts[0].Name) + s.baseCtx = s.baseCtx.WithFromAddress(accounts[0].Address) + + var outBuf bytes.Buffer + ctxGen := func() client.Context { + bz, _ := s.encCfg.Marshaler.Marshal(&sdk.TxResponse{}) + c := clitestutil.NewMockTendermintRPC(abci.ResponseQuery{ + Value: bz, + }) + + return s.baseCtx.WithClient(c) + } + s.clientCtx = ctxGen().WithOutput(&outBuf) + + if testing.Short() { + s.T().Skip("skipping test in unit-tests mode.") + } +} + +// TODO: Add more tests diff --git a/x/storage/abci.go b/x/storage/keeper/abci.go similarity index 88% rename from x/storage/abci.go rename to x/storage/keeper/abci.go index b395f512c..01877b4b6 100644 --- a/x/storage/abci.go +++ b/x/storage/keeper/abci.go @@ -1,13 +1,12 @@ -package storage +package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" paymenttypes "github.com/bnb-chain/greenfield/x/payment/types" - k "github.com/bnb-chain/greenfield/x/storage/keeper" ) -func BeginBlocker(ctx sdk.Context, keeper k.Keeper) { +func BeginBlocker(ctx sdk.Context, keeper Keeper) { blockHeight := uint64(ctx.BlockHeight()) countingWindow := keeper.DiscontinueCountingWindow(ctx) if blockHeight > 0 && countingWindow > 0 && blockHeight%countingWindow == 0 { @@ -16,7 +15,7 @@ func BeginBlocker(ctx sdk.Context, keeper k.Keeper) { } } -func EndBlocker(ctx sdk.Context, keeper k.Keeper) { +func EndBlocker(ctx sdk.Context, keeper Keeper) { deletionMax := keeper.DiscontinueDeletionMax(ctx) if deletionMax == 0 { return diff --git a/x/storage/keeper/cross_app_bucket_test.go b/x/storage/keeper/cross_app_bucket_test.go index c4e9f35df..43bf71c60 100644 --- a/x/storage/keeper/cross_app_bucket_test.go +++ b/x/storage/keeper/cross_app_bucket_test.go @@ -13,12 +13,6 @@ import ( ) func (s *TestSuite) TestSynDeleteBucket() { - pack := types.DeleteBucketAckPackage{ - Status: 1, - Id: big.NewInt(10), - ExtraData: []byte("x"), - } - pack.MustSerialize() ctrl := gomock.NewController(s.T()) storageKeeper := types.NewMockStorageKeeper(ctrl) storageKeeper.EXPECT().Logger(gomock.Any()).Return(s.ctx.Logger()).AnyTimes() @@ -55,3 +49,202 @@ func (s *TestSuite) TestSynDeleteBucket() { res = app.ExecuteSynPackage(s.ctx, nil, serializedSynPackage) s.Require().NoError(res.Err) } + +func (s *TestSuite) TestSynCreateBucket() { + ctrl := gomock.NewController(s.T()) + storageKeeper := types.NewMockStorageKeeper(ctrl) + storageKeeper.EXPECT().Logger(gomock.Any()).Return(s.ctx.Logger()).AnyTimes() + + app := keeper.NewBucketApp(storageKeeper) + createSynPackage := types.CreateBucketSynPackage{ + Creator: sample.RandAccAddress(), + BucketName: "bucketname", + ExtraData: []byte("extra data"), + PaymentAddress: sample.RandAccAddress(), + PrimarySpAddress: sample.RandAccAddress(), + } + serializedSynPackage := createSynPackage.MustSerialize() + serializedSynPackage = append([]byte{types.OperationCreateBucket}, serializedSynPackage...) + + // case 1: invalid package + res := app.ExecuteSynPackage(s.ctx, nil, serializedSynPackage) + s.Require().ErrorContains(res.Err, "Invalid type of visibility") + + // case 2: create bucket error + createSynPackage.Visibility = uint32(types.VISIBILITY_TYPE_PUBLIC_READ) + serializedSynPackage = createSynPackage.MustSerialize() + serializedSynPackage = append([]byte{types.OperationCreateBucket}, serializedSynPackage...) + + storageKeeper.EXPECT().CreateBucket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(sdk.NewUint(1), fmt.Errorf("create error")) + res = app.ExecuteSynPackage(s.ctx, nil, serializedSynPackage) + s.Require().ErrorContains(res.Err, "create error") + + // case 3: create bucket success + createSynPackage.Visibility = uint32(types.VISIBILITY_TYPE_PUBLIC_READ) + serializedSynPackage = createSynPackage.MustSerialize() + serializedSynPackage = append([]byte{types.OperationCreateBucket}, serializedSynPackage...) + + storageKeeper.EXPECT().CreateBucket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(sdk.NewUint(1), nil) + res = app.ExecuteSynPackage(s.ctx, nil, serializedSynPackage) + s.Require().NoError(res.Err) +} + +func (s *TestSuite) TestSynMirrorBucket() { + ctrl := gomock.NewController(s.T()) + storageKeeper := types.NewMockStorageKeeper(ctrl) + storageKeeper.EXPECT().Logger(gomock.Any()).Return(s.ctx.Logger()).AnyTimes() + + app := keeper.NewBucketApp(storageKeeper) + synPackage := types.MirrorBucketSynPackage{ + Owner: sample.RandAccAddress(), + Id: big.NewInt(10), + } + + serializedSynPack, err := synPackage.Serialize() + s.Require().NoError(err) + serializedSynPack = append([]byte{types.OperationMirrorBucket}, serializedSynPack...) + + // case 1: normal case + res := app.ExecuteSynPackage(s.ctx, nil, serializedSynPack) + s.Require().NoError(res.Err) +} + +func (s *TestSuite) TestAckMirrorBucket() { + ctrl := gomock.NewController(s.T()) + storageKeeper := types.NewMockStorageKeeper(ctrl) + storageKeeper.EXPECT().Logger(gomock.Any()).Return(s.ctx.Logger()).AnyTimes() + + app := keeper.NewBucketApp(storageKeeper) + ackPackage := types.MirrorBucketAckPackage{ + Status: types.StatusSuccess, + Id: big.NewInt(10), + } + + serializedAckPack, err := ackPackage.Serialize() + s.Require().NoError(err) + serializedAckPack = append([]byte{types.OperationMirrorBucket}, serializedAckPack...) + + // case 1: bucket not found + storageKeeper.EXPECT().GetBucketInfoById(gomock.Any(), gomock.Any()).Return(nil, false) + + res := app.ExecuteAckPackage(s.ctx, nil, serializedAckPack) + s.Require().ErrorIs(res.Err, types.ErrNoSuchBucket) + + // case 2: success case + storageKeeper.EXPECT().GetBucketInfoById(gomock.Any(), gomock.Any()).Return(&types.BucketInfo{}, true) + storageKeeper.EXPECT().SetBucketInfo(gomock.Any(), gomock.Any()).Return() + + res = app.ExecuteAckPackage(s.ctx, &sdk.CrossChainAppContext{}, serializedAckPack) + s.Require().NoError(res.Err) +} + +func (s *TestSuite) TestAckCreateBucket() { + ctrl := gomock.NewController(s.T()) + storageKeeper := types.NewMockStorageKeeper(ctrl) + storageKeeper.EXPECT().Logger(gomock.Any()).Return(s.ctx.Logger()).AnyTimes() + + app := keeper.NewBucketApp(storageKeeper) + ackPackage := types.CreateBucketAckPackage{ + Status: types.StatusSuccess, + Id: big.NewInt(10), + Creator: sample.RandAccAddress(), + ExtraData: []byte("extra data"), + } + + serializedAckPack := ackPackage.MustSerialize() + serializedAckPack = append([]byte{types.OperationCreateBucket}, serializedAckPack...) + + // case 1: normal case + res := app.ExecuteAckPackage(s.ctx, nil, serializedAckPack) + s.Require().NoError(res.Err) +} + +func (s *TestSuite) TestAckDeleteBucket() { + ctrl := gomock.NewController(s.T()) + storageKeeper := types.NewMockStorageKeeper(ctrl) + storageKeeper.EXPECT().Logger(gomock.Any()).Return(s.ctx.Logger()).AnyTimes() + + app := keeper.NewBucketApp(storageKeeper) + ackPackage := types.DeleteBucketAckPackage{ + Status: types.StatusSuccess, + Id: big.NewInt(10), + ExtraData: []byte("extra data"), + } + + serializedAckPack := ackPackage.MustSerialize() + serializedAckPack = append([]byte{types.OperationDeleteBucket}, serializedAckPack...) + + // case 1: normal case + res := app.ExecuteAckPackage(s.ctx, nil, serializedAckPack) + s.Require().NoError(res.Err) +} + +func (s *TestSuite) TestFailAckMirrorBucket() { + ctrl := gomock.NewController(s.T()) + storageKeeper := types.NewMockStorageKeeper(ctrl) + storageKeeper.EXPECT().Logger(gomock.Any()).Return(s.ctx.Logger()).AnyTimes() + + app := keeper.NewBucketApp(storageKeeper) + ackPackage := types.MirrorBucketSynPackage{ + Id: big.NewInt(10), + Owner: sample.RandAccAddress(), + } + + serializedAckPack, err := ackPackage.Serialize() + s.Require().NoError(err) + serializedAckPack = append([]byte{types.OperationMirrorBucket}, serializedAckPack...) + + // case 1: bucket not found + storageKeeper.EXPECT().GetBucketInfoById(gomock.Any(), gomock.Any()).Return(&types.BucketInfo{}, false) + + res := app.ExecuteFailAckPackage(s.ctx, nil, serializedAckPack) + s.Require().ErrorIs(res.Err, types.ErrNoSuchBucket) + + // case 2: normal case + storageKeeper.EXPECT().GetBucketInfoById(gomock.Any(), gomock.Any()).Return(&types.BucketInfo{}, true) + storageKeeper.EXPECT().SetBucketInfo(gomock.Any(), gomock.Any()).Return() + + res = app.ExecuteFailAckPackage(s.ctx, &sdk.CrossChainAppContext{}, serializedAckPack) + s.Require().NoError(res.Err) +} + +func (s *TestSuite) TestFailAckCreateBucket() { + ctrl := gomock.NewController(s.T()) + storageKeeper := types.NewMockStorageKeeper(ctrl) + storageKeeper.EXPECT().Logger(gomock.Any()).Return(s.ctx.Logger()).AnyTimes() + + app := keeper.NewBucketApp(storageKeeper) + createSynPackage := types.CreateBucketSynPackage{ + Creator: sample.RandAccAddress(), + BucketName: "bucketname", + ExtraData: []byte("extra data"), + PaymentAddress: sample.RandAccAddress(), + PrimarySpAddress: sample.RandAccAddress(), + } + serializedSynPackage := createSynPackage.MustSerialize() + serializedSynPackage = append([]byte{types.OperationCreateBucket}, serializedSynPackage...) + + // case 1: normal case + res := app.ExecuteFailAckPackage(s.ctx, nil, serializedSynPackage) + s.Require().NoError(res.Err) +} + +func (s *TestSuite) TestFailAckDeleteBucket() { + ctrl := gomock.NewController(s.T()) + storageKeeper := types.NewMockStorageKeeper(ctrl) + storageKeeper.EXPECT().Logger(gomock.Any()).Return(s.ctx.Logger()).AnyTimes() + + app := keeper.NewBucketApp(storageKeeper) + deleteSynPackage := types.DeleteBucketSynPackage{ + Operator: sample.RandAccAddress(), + Id: big.NewInt(10), + ExtraData: []byte("extra data"), + } + + serializedSynPackage := deleteSynPackage.MustSerialize() + serializedSynPackage = append([]byte{types.OperationDeleteBucket}, serializedSynPackage...) + + // case 1: normal case + res := app.ExecuteFailAckPackage(s.ctx, nil, serializedSynPackage) + s.Require().NoError(res.Err) +} diff --git a/x/storage/keeper/cross_app_group_test.go b/x/storage/keeper/cross_app_group_test.go new file mode 100644 index 000000000..23a3a6aed --- /dev/null +++ b/x/storage/keeper/cross_app_group_test.go @@ -0,0 +1,307 @@ +package keeper_test + +import ( + "fmt" + "math/big" + + "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/golang/mock/gomock" + + "github.com/bnb-chain/greenfield/testutil/sample" + gnfderrors "github.com/bnb-chain/greenfield/types/errors" + "github.com/bnb-chain/greenfield/x/storage/keeper" + "github.com/bnb-chain/greenfield/x/storage/types" +) + +func (s *TestSuite) TestAckMirrorGroup() { + ctrl := gomock.NewController(s.T()) + storageKeeper := types.NewMockStorageKeeper(ctrl) + storageKeeper.EXPECT().Logger(gomock.Any()).Return(s.ctx.Logger()).AnyTimes() + + app := keeper.NewGroupApp(storageKeeper) + ackPackage := types.MirrorGroupAckPackage{ + Status: types.StatusSuccess, + Id: big.NewInt(10), + } + + serializedAckPackage, err := ackPackage.Serialize() + s.Require().NoError(err) + serializedAckPackage = append([]byte{types.OperationMirrorGroup}, serializedAckPackage...) + + // case 1: mirror group not found + storageKeeper.EXPECT().GetGroupInfoById(gomock.Any(), gomock.Any()).Return(nil, false) + res := app.ExecuteAckPackage(s.ctx, nil, serializedAckPackage) + s.Require().ErrorIs(res.Err, types.ErrNoSuchGroup) + + // case 2: normal case + storageKeeper.EXPECT().GetGroupInfoById(gomock.Any(), gomock.Any()).Return(&types.GroupInfo{}, true) + storageKeeper.EXPECT().SetGroupInfo(gomock.Any(), gomock.Any()).Return() + res = app.ExecuteAckPackage(s.ctx, &sdk.CrossChainAppContext{}, serializedAckPackage) + s.Require().NoError(res.Err) +} + +func (s *TestSuite) TestAckCreateGroup() { + ctrl := gomock.NewController(s.T()) + storageKeeper := types.NewMockStorageKeeper(ctrl) + storageKeeper.EXPECT().Logger(gomock.Any()).Return(s.ctx.Logger()).AnyTimes() + + app := keeper.NewGroupApp(storageKeeper) + ackPackage := types.CreateGroupAckPackage{ + Status: types.StatusSuccess, + Id: big.NewInt(10), + Creator: sample.RandAccAddress(), + ExtraData: []byte("extra data"), + } + + serializedAckPackage := ackPackage.MustSerialize() + serializedAckPackage = append([]byte{types.OperationCreateGroup}, serializedAckPackage...) + + // case 1: normal case + res := app.ExecuteAckPackage(s.ctx, nil, serializedAckPackage) + s.Require().NoError(res.Err) +} + +func (s *TestSuite) TestAckDeleteGroup() { + ctrl := gomock.NewController(s.T()) + storageKeeper := types.NewMockStorageKeeper(ctrl) + storageKeeper.EXPECT().Logger(gomock.Any()).Return(s.ctx.Logger()).AnyTimes() + + app := keeper.NewGroupApp(storageKeeper) + ackPackage := types.DeleteGroupAckPackage{ + Status: types.StatusSuccess, + Id: big.NewInt(10), + ExtraData: []byte("extra data"), + } + + serializedAckPackage := ackPackage.MustSerialize() + serializedAckPackage = append([]byte{types.OperationDeleteGroup}, serializedAckPackage...) + + // case 1: normal case + res := app.ExecuteAckPackage(s.ctx, nil, serializedAckPackage) + s.Require().NoError(res.Err) +} + +func (s *TestSuite) TestFailAckMirrorGroup() { + ctrl := gomock.NewController(s.T()) + storageKeeper := types.NewMockStorageKeeper(ctrl) + storageKeeper.EXPECT().Logger(gomock.Any()).Return(s.ctx.Logger()).AnyTimes() + + app := keeper.NewGroupApp(storageKeeper) + ackPackage := types.MirrorGroupSynPackage{ + Id: big.NewInt(10), + Owner: sample.RandAccAddress(), + } + + serializedAckPackage, err := ackPackage.Serialize() + s.Require().NoError(err) + serializedAckPackage = append([]byte{types.OperationMirrorGroup}, serializedAckPackage...) + + // case 1: group not found + storageKeeper.EXPECT().GetGroupInfoById(gomock.Any(), gomock.Any()).Return(nil, false) + res := app.ExecuteFailAckPackage(s.ctx, nil, serializedAckPackage) + s.Require().ErrorIs(res.Err, types.ErrNoSuchGroup) + + // case 2: normal case + storageKeeper.EXPECT().GetGroupInfoById(gomock.Any(), gomock.Any()).Return(&types.GroupInfo{}, true) + storageKeeper.EXPECT().SetGroupInfo(gomock.Any(), gomock.Any()).Return() + res = app.ExecuteFailAckPackage(s.ctx, &sdk.CrossChainAppContext{}, serializedAckPackage) + s.Require().NoError(res.Err) +} + +func (s *TestSuite) TestFailAckCreateGroup() { + ctrl := gomock.NewController(s.T()) + storageKeeper := types.NewMockStorageKeeper(ctrl) + storageKeeper.EXPECT().Logger(gomock.Any()).Return(s.ctx.Logger()).AnyTimes() + + app := keeper.NewGroupApp(storageKeeper) + ackPackage := types.CreateGroupSynPackage{ + Creator: sample.RandAccAddress(), + GroupName: "group", + ExtraData: []byte("extra data"), + } + + serializedAckPackage := ackPackage.MustSerialize() + serializedAckPackage = append([]byte{types.OperationCreateGroup}, serializedAckPackage...) + + // case 1: normal case + res := app.ExecuteFailAckPackage(s.ctx, &sdk.CrossChainAppContext{}, serializedAckPackage) + s.Require().NoError(res.Err) +} + +func (s *TestSuite) TestFailAckDeleteGroup() { + ctrl := gomock.NewController(s.T()) + storageKeeper := types.NewMockStorageKeeper(ctrl) + storageKeeper.EXPECT().Logger(gomock.Any()).Return(s.ctx.Logger()).AnyTimes() + + app := keeper.NewGroupApp(storageKeeper) + ackPackage := types.DeleteBucketSynPackage{ + Operator: sample.RandAccAddress(), + Id: big.NewInt(10), + ExtraData: []byte("extra data"), + } + + serializedAckPackage := ackPackage.MustSerialize() + serializedAckPackage = append([]byte{types.OperationDeleteGroup}, serializedAckPackage...) + + // case 1: normal case + res := app.ExecuteFailAckPackage(s.ctx, &sdk.CrossChainAppContext{}, serializedAckPackage) + s.Require().NoError(res.Err) +} + +func (s *TestSuite) TestFailAckUpdateGroupMember() { + ctrl := gomock.NewController(s.T()) + storageKeeper := types.NewMockStorageKeeper(ctrl) + storageKeeper.EXPECT().Logger(gomock.Any()).Return(s.ctx.Logger()).AnyTimes() + + app := keeper.NewGroupApp(storageKeeper) + ackPackage := types.UpdateGroupMemberSynPackage{ + Operator: sample.RandAccAddress(), + GroupId: big.NewInt(10), + ExtraData: []byte("extra data"), + } + + serializedAckPackage := ackPackage.MustSerialize() + serializedAckPackage = append([]byte{types.OperationUpdateGroupMember}, serializedAckPackage...) + + // case 1: normal case + res := app.ExecuteFailAckPackage(s.ctx, &sdk.CrossChainAppContext{}, serializedAckPackage) + s.Require().NoError(res.Err) +} + +func (s *TestSuite) TestSynMirrorGroup() { + ctrl := gomock.NewController(s.T()) + storageKeeper := types.NewMockStorageKeeper(ctrl) + storageKeeper.EXPECT().Logger(gomock.Any()).Return(s.ctx.Logger()).AnyTimes() + + app := keeper.NewGroupApp(storageKeeper) + synPackage := types.MirrorGroupSynPackage{ + Owner: sample.RandAccAddress(), + Id: big.NewInt(10), + } + + serializedSynPackage, err := synPackage.Serialize() + s.Require().NoError(err) + serializedSynPackage = append([]byte{types.OperationMirrorGroup}, serializedSynPackage...) + + // case 1: normal case + res := app.ExecuteSynPackage(s.ctx, &sdk.CrossChainAppContext{}, serializedSynPackage) + s.Require().NoError(res.Err) +} + +func (s *TestSuite) TestSynCreateGroup() { + ctrl := gomock.NewController(s.T()) + storageKeeper := types.NewMockStorageKeeper(ctrl) + storageKeeper.EXPECT().Logger(gomock.Any()).Return(s.ctx.Logger()).AnyTimes() + + app := keeper.NewGroupApp(storageKeeper) + synPackage := types.CreateGroupSynPackage{ + Creator: sample.RandAccAddress(), + GroupName: "group", + ExtraData: []byte("extra data"), + } + + // case 1: invalid group name + synPackage.GroupName = "g" + serializedSynPackage := synPackage.MustSerialize() + serializedSynPackage = append([]byte{types.OperationCreateGroup}, serializedSynPackage...) + + res := app.ExecuteSynPackage(s.ctx, &sdk.CrossChainAppContext{}, serializedSynPackage) + s.Require().ErrorIs(res.Err, gnfderrors.ErrInvalidGroupName) + s.Require().NotEmpty(res.Payload) + + // case 2: create group error + synPackage.GroupName = "group" + serializedSynPackage = synPackage.MustSerialize() + serializedSynPackage = append([]byte{types.OperationCreateGroup}, serializedSynPackage...) + + storageKeeper.EXPECT().CreateGroup(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(math.NewUint(0), fmt.Errorf("create group error")) + res = app.ExecuteSynPackage(s.ctx, &sdk.CrossChainAppContext{}, serializedSynPackage) + s.Require().ErrorContains(res.Err, "create group error") + + // case 3: normal case + storageKeeper.EXPECT().CreateGroup(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(math.NewUint(10), nil) + res = app.ExecuteSynPackage(s.ctx, &sdk.CrossChainAppContext{}, serializedSynPackage) + s.Require().NoError(res.Err) +} + +func (s *TestSuite) TestSynDeleteGroup() { + ctrl := gomock.NewController(s.T()) + storageKeeper := types.NewMockStorageKeeper(ctrl) + storageKeeper.EXPECT().Logger(gomock.Any()).Return(s.ctx.Logger()).AnyTimes() + + app := keeper.NewGroupApp(storageKeeper) + synPackage := types.DeleteBucketSynPackage{ + Operator: sample.RandAccAddress(), + Id: big.NewInt(10), + ExtraData: []byte("extra data"), + } + serializedSynPackage := synPackage.MustSerialize() + serializedSynPackage = append([]byte{types.OperationDeleteGroup}, serializedSynPackage...) + + // case 1: group not exist + storageKeeper.EXPECT().GetGroupInfoById(gomock.Any(), gomock.Any()).Return(nil, false) + res := app.ExecuteSynPackage(s.ctx, &sdk.CrossChainAppContext{}, serializedSynPackage) + s.Require().ErrorIs(res.Err, types.ErrNoSuchGroup) + s.Require().NotEmpty(res.Payload) + + // case 2: delete group error + storageKeeper.EXPECT().GetGroupInfoById(gomock.Any(), gomock.Any()).Return(&types.GroupInfo{}, true) + storageKeeper.EXPECT().DeleteGroup(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(fmt.Errorf("delete group error")) + res = app.ExecuteSynPackage(s.ctx, &sdk.CrossChainAppContext{}, serializedSynPackage) + s.Require().ErrorContains(res.Err, "delete group error") + + // case 3: normal case + storageKeeper.EXPECT().GetGroupInfoById(gomock.Any(), gomock.Any()).Return(&types.GroupInfo{ + Id: sdk.NewUint(10), + }, true) + storageKeeper.EXPECT().DeleteGroup(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) + res = app.ExecuteSynPackage(s.ctx, &sdk.CrossChainAppContext{}, serializedSynPackage) + s.Require().NoError(res.Err) +} + +func (s *TestSuite) TestSynUpdateGroupMember() { + ctrl := gomock.NewController(s.T()) + storageKeeper := types.NewMockStorageKeeper(ctrl) + storageKeeper.EXPECT().Logger(gomock.Any()).Return(s.ctx.Logger()).AnyTimes() + + app := keeper.NewGroupApp(storageKeeper) + synPackage := types.UpdateGroupMemberSynPackage{ + Operator: sample.RandAccAddress(), + GroupId: big.NewInt(10), + ExtraData: []byte("extra data"), + } + + // case 1: invalid package + synPackage.OperationType = 3 + serializedSynPackage := synPackage.MustSerialize() + serializedSynPackage = append([]byte{types.OperationUpdateGroupMember}, serializedSynPackage...) + + res := app.ExecuteSynPackage(s.ctx, &sdk.CrossChainAppContext{}, serializedSynPackage) + s.Require().ErrorIs(res.Err, types.ErrInvalidOperationType) + s.Require().NotEmpty(res.Payload) + + // case 2: group not exist + synPackage.OperationType = types.OperationAddGroupMember + serializedSynPackage = synPackage.MustSerialize() + serializedSynPackage = append([]byte{types.OperationUpdateGroupMember}, serializedSynPackage...) + + storageKeeper.EXPECT().GetGroupInfoById(gomock.Any(), gomock.Any()).Return(nil, false) + res = app.ExecuteSynPackage(s.ctx, &sdk.CrossChainAppContext{}, serializedSynPackage) + s.Require().ErrorIs(res.Err, types.ErrNoSuchGroup) + s.Require().NotEmpty(res.Payload) + + // case 3: update group member error + storageKeeper.EXPECT().GetGroupInfoById(gomock.Any(), gomock.Any()).Return(&types.GroupInfo{}, true) + storageKeeper.EXPECT().UpdateGroupMember(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(fmt.Errorf("update group member error")) + res = app.ExecuteSynPackage(s.ctx, &sdk.CrossChainAppContext{}, serializedSynPackage) + s.Require().ErrorContains(res.Err, "update group member error") + + // case 4: normal case + storageKeeper.EXPECT().GetGroupInfoById(gomock.Any(), gomock.Any()).Return(&types.GroupInfo{ + Id: sdk.NewUint(10), + }, true) + storageKeeper.EXPECT().UpdateGroupMember(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) + res = app.ExecuteSynPackage(s.ctx, &sdk.CrossChainAppContext{}, serializedSynPackage) + s.Require().NoError(res.Err) +} diff --git a/x/storage/keeper/cross_app_object_test.go b/x/storage/keeper/cross_app_object_test.go new file mode 100644 index 000000000..d535d3aeb --- /dev/null +++ b/x/storage/keeper/cross_app_object_test.go @@ -0,0 +1,162 @@ +package keeper_test + +import ( + "fmt" + "math/big" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/golang/mock/gomock" + + "github.com/bnb-chain/greenfield/testutil/sample" + "github.com/bnb-chain/greenfield/x/storage/keeper" + "github.com/bnb-chain/greenfield/x/storage/types" +) + +func (s *TestSuite) TestAckMirrorObject() { + ctrl := gomock.NewController(s.T()) + storageKeeper := types.NewMockStorageKeeper(ctrl) + storageKeeper.EXPECT().Logger(gomock.Any()).Return(s.ctx.Logger()).AnyTimes() + + app := keeper.NewObjectApp(storageKeeper) + ackPackage := types.MirrorObjectAckPackage{ + Status: types.StatusSuccess, + Id: big.NewInt(10), + } + + serializedAckPackage, err := ackPackage.Serialize() + s.Require().NoError(err) + serializedAckPackage = append([]byte{types.OperationMirrorObject}, serializedAckPackage...) + + // case 1: object not exist + storageKeeper.EXPECT().GetObjectInfoById(gomock.Any(), gomock.Any()).Return(nil, false) + res := app.ExecuteAckPackage(s.ctx, nil, serializedAckPackage) + s.Require().ErrorIs(res.Err, types.ErrNoSuchObject) + + // case 2: normal case + storageKeeper.EXPECT().GetObjectInfoById(gomock.Any(), gomock.Any()).Return(&types.ObjectInfo{}, true) + storageKeeper.EXPECT().SetObjectInfo(gomock.Any(), gomock.Any()).Return() + res = app.ExecuteAckPackage(s.ctx, &sdk.CrossChainAppContext{}, serializedAckPackage) + s.Require().NoError(res.Err) +} + +func (s *TestSuite) TestAckDeleteObject() { + ctrl := gomock.NewController(s.T()) + storageKeeper := types.NewMockStorageKeeper(ctrl) + storageKeeper.EXPECT().Logger(gomock.Any()).Return(s.ctx.Logger()).AnyTimes() + + app := keeper.NewObjectApp(storageKeeper) + ackPackage := types.DeleteObjectAckPackage{ + Status: types.StatusSuccess, + Id: big.NewInt(10), + ExtraData: []byte("extra data"), + } + + serializedAckPackage := ackPackage.MustSerialize() + serializedAckPackage = append([]byte{types.OperationDeleteObject}, serializedAckPackage...) + + // case 1: normal case + res := app.ExecuteAckPackage(s.ctx, &sdk.CrossChainAppContext{}, serializedAckPackage) + s.Require().NoError(res.Err) +} + +func (s *TestSuite) TestFailAckMirrorObject() { + ctrl := gomock.NewController(s.T()) + storageKeeper := types.NewMockStorageKeeper(ctrl) + storageKeeper.EXPECT().Logger(gomock.Any()).Return(s.ctx.Logger()).AnyTimes() + + app := keeper.NewObjectApp(storageKeeper) + ackPackage := types.MirrorObjectSynPackage{ + Owner: sample.RandAccAddress(), + Id: big.NewInt(10), + } + + serializedAckPackage, err := ackPackage.Serialize() + s.Require().NoError(err) + serializedAckPackage = append([]byte{types.OperationMirrorObject}, serializedAckPackage...) + + // case 1: object not exist + storageKeeper.EXPECT().GetObjectInfoById(gomock.Any(), gomock.Any()).Return(nil, false) + res := app.ExecuteFailAckPackage(s.ctx, nil, serializedAckPackage) + s.Require().ErrorIs(res.Err, types.ErrNoSuchObject) + + // case 2: normal case + storageKeeper.EXPECT().GetObjectInfoById(gomock.Any(), gomock.Any()).Return(&types.ObjectInfo{}, true) + storageKeeper.EXPECT().SetObjectInfo(gomock.Any(), gomock.Any()).Return() + res = app.ExecuteFailAckPackage(s.ctx, &sdk.CrossChainAppContext{}, serializedAckPackage) + s.Require().NoError(res.Err) +} + +func (s *TestSuite) TestFailAckDeleteObject() { + ctrl := gomock.NewController(s.T()) + storageKeeper := types.NewMockStorageKeeper(ctrl) + storageKeeper.EXPECT().Logger(gomock.Any()).Return(s.ctx.Logger()).AnyTimes() + + app := keeper.NewObjectApp(storageKeeper) + ackPackage := types.DeleteBucketSynPackage{ + Operator: sample.RandAccAddress(), + Id: big.NewInt(10), + ExtraData: []byte("extra data"), + } + + serializedAckPackage := ackPackage.MustSerialize() + serializedAckPackage = append([]byte{types.OperationDeleteObject}, serializedAckPackage...) + + // case 1: normal case + res := app.ExecuteFailAckPackage(s.ctx, &sdk.CrossChainAppContext{}, serializedAckPackage) + s.Require().NoError(res.Err) +} + +func (s *TestSuite) TestSynMirrorObject() { + ctrl := gomock.NewController(s.T()) + storageKeeper := types.NewMockStorageKeeper(ctrl) + storageKeeper.EXPECT().Logger(gomock.Any()).Return(s.ctx.Logger()).AnyTimes() + + app := keeper.NewObjectApp(storageKeeper) + synPackage := types.MirrorObjectSynPackage{ + Owner: sample.RandAccAddress(), + Id: big.NewInt(10), + } + + serializedSynPackage, err := synPackage.Serialize() + s.Require().NoError(err) + serializedSynPackage = append([]byte{types.OperationMirrorObject}, serializedSynPackage...) + + // case 1: normal case + res := app.ExecuteSynPackage(s.ctx, nil, serializedSynPackage) + s.Require().NoError(res.Err) +} + +func (s *TestSuite) TestSynDeleteObject() { + ctrl := gomock.NewController(s.T()) + storageKeeper := types.NewMockStorageKeeper(ctrl) + storageKeeper.EXPECT().Logger(gomock.Any()).Return(s.ctx.Logger()).AnyTimes() + + app := keeper.NewObjectApp(storageKeeper) + synPackage := types.DeleteBucketSynPackage{ + Operator: sample.RandAccAddress(), + Id: big.NewInt(10), + ExtraData: []byte("extra data"), + } + + serializedSynPackage := synPackage.MustSerialize() + serializedSynPackage = append([]byte{types.OperationDeleteObject}, serializedSynPackage...) + + // case 1: object not exist + storageKeeper.EXPECT().GetObjectInfoById(gomock.Any(), gomock.Any()).Return(nil, false) + res := app.ExecuteSynPackage(s.ctx, nil, serializedSynPackage) + s.Require().ErrorIs(res.Err, types.ErrNoSuchObject) + + // case 2: delete object error + storageKeeper.EXPECT().GetObjectInfoById(gomock.Any(), gomock.Any()).Return(&types.ObjectInfo{}, true) + storageKeeper.EXPECT().DeleteObject(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(fmt.Errorf("delete object error")) + res = app.ExecuteSynPackage(s.ctx, nil, serializedSynPackage) + s.Require().ErrorContains(res.Err, "delete object error") + + // case 3: normal case + storageKeeper.EXPECT().GetObjectInfoById(gomock.Any(), gomock.Any()).Return(&types.ObjectInfo{ + Id: sdk.NewUint(10), + }, true) + storageKeeper.EXPECT().DeleteObject(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) + res = app.ExecuteSynPackage(s.ctx, nil, serializedSynPackage) + s.Require().NoError(res.Err) +} diff --git a/x/storage/keeper/grpc_query_test.go b/x/storage/keeper/grpc_query_test.go index 05c1199b7..05c911219 100644 --- a/x/storage/keeper/grpc_query_test.go +++ b/x/storage/keeper/grpc_query_test.go @@ -8,11 +8,13 @@ import ( "github.com/cosmos/cosmos-sdk/testutil" sdk "github.com/cosmos/cosmos-sdk/types" moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" + "github.com/cosmos/cosmos-sdk/types/query" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" "github.com/cosmos/cosmos-sdk/x/mint" "github.com/stretchr/testify/require" + "github.com/bnb-chain/greenfield/testutil/sample" "github.com/bnb-chain/greenfield/x/storage/keeper" "github.com/bnb-chain/greenfield/x/storage/types" ) @@ -85,3 +87,305 @@ func TestVersionedParamsQuery(t *testing.T) { p = responseT2.GetParams() require.EqualValues(t, p.GetMaxSegmentSize(), 2) } + +func TestHeadBucket(t *testing.T) { + // invalid argument + k, ctx := makeKeeper(t) + _, err := k.HeadBucket(ctx, nil) + require.ErrorContains(t, err, "invalid request") + + _, err = k.HeadBucket(ctx, &types.QueryHeadBucketRequest{ + BucketName: "bucket", + }) + require.ErrorIs(t, err, types.ErrNoSuchBucket) +} + +func TestHeadGroupNFT(t *testing.T) { + // invalid argument + k, ctx := makeKeeper(t) + _, err := k.HeadGroupNFT(ctx, nil) + require.ErrorContains(t, err, "invalid request") + + _, err = k.HeadGroupNFT(ctx, &types.QueryNFTRequest{ + TokenId: "xxx", + }) + require.ErrorContains(t, err, "invalid token id") + + // group not exist + _, err = k.HeadGroupNFT(ctx, &types.QueryNFTRequest{ + TokenId: "0", + }) + require.ErrorIs(t, err, types.ErrNoSuchGroup) +} + +func TestHeadObjectNFT(t *testing.T) { + // invalid argument + k, ctx := makeKeeper(t) + _, err := k.HeadObjectNFT(ctx, nil) + require.ErrorContains(t, err, "invalid request") + + _, err = k.HeadObjectNFT(ctx, &types.QueryNFTRequest{ + TokenId: "xxx", + }) + require.ErrorContains(t, err, "invalid token id") + + // object not exist + _, err = k.HeadObjectNFT(ctx, &types.QueryNFTRequest{ + TokenId: "0", + }) + require.ErrorIs(t, err, types.ErrNoSuchObject) +} + +func TestHeadBucketNFT(t *testing.T) { + // invalid argument + k, ctx := makeKeeper(t) + _, err := k.HeadBucketNFT(ctx, nil) + require.ErrorContains(t, err, "invalid request") + + _, err = k.HeadBucketNFT(ctx, &types.QueryNFTRequest{ + TokenId: "xxx", + }) + require.ErrorContains(t, err, "invalid token id") + + // bucket not exist + _, err = k.HeadBucketNFT(ctx, &types.QueryNFTRequest{ + TokenId: "0", + }) + require.ErrorIs(t, err, types.ErrNoSuchBucket) +} + +func TestHeadBucketById(t *testing.T) { + // invalid argument + k, ctx := makeKeeper(t) + _, err := k.HeadBucketById(ctx, nil) + require.ErrorContains(t, err, "invalid request") + + _, err = k.HeadBucketById(ctx, &types.QueryHeadBucketByIdRequest{ + BucketId: "xxx", + }) + require.ErrorContains(t, err, "invalid bucket id") + + // bucket not exist + _, err = k.HeadBucketById(ctx, &types.QueryHeadBucketByIdRequest{ + BucketId: "0", + }) + require.ErrorIs(t, err, types.ErrNoSuchBucket) +} + +func TestHeadObject(t *testing.T) { + // invalid argument + k, ctx := makeKeeper(t) + _, err := k.HeadObject(ctx, nil) + require.ErrorContains(t, err, "invalid request") + + // object not exist + _, err = k.HeadObject(ctx, &types.QueryHeadObjectRequest{ + BucketName: "bucket", + ObjectName: "object", + }) + require.ErrorIs(t, err, types.ErrNoSuchObject) +} + +func TestHeadObjectById(t *testing.T) { + // invalid argument + k, ctx := makeKeeper(t) + _, err := k.HeadBucketById(ctx, nil) + require.ErrorContains(t, err, "invalid request") + + _, err = k.HeadObjectById(ctx, &types.QueryHeadObjectByIdRequest{ + ObjectId: "xxx", + }) + require.ErrorContains(t, err, "invalid object id") + + // bucket not exist + _, err = k.HeadObjectById(ctx, &types.QueryHeadObjectByIdRequest{ + ObjectId: "1", + }) + require.ErrorIs(t, err, types.ErrNoSuchObject) +} + +func TestListBuckets(t *testing.T) { + // invalid argument + k, ctx := makeKeeper(t) + _, err := k.ListBuckets(ctx, nil) + require.ErrorContains(t, err, "invalid request") + + _, err = k.ListBuckets(ctx, &types.QueryListBucketsRequest{ + Pagination: &query.PageRequest{ + Limit: types.MaxPaginationLimit + 1, + }, + }) + require.ErrorContains(t, err, "exceed pagination limit") +} + +func TestListObjects(t *testing.T) { + // invalid argument + k, ctx := makeKeeper(t) + _, err := k.ListObjects(ctx, nil) + require.ErrorContains(t, err, "invalid request") + + _, err = k.ListObjects(ctx, &types.QueryListObjectsRequest{ + Pagination: &query.PageRequest{ + Limit: types.MaxPaginationLimit + 1, + }, + }) + require.ErrorContains(t, err, "exceed pagination limit") +} + +func TestListObjectsByBucketId(t *testing.T) { + // invalid argument + k, ctx := makeKeeper(t) + _, err := k.ListObjectsByBucketId(ctx, nil) + require.ErrorContains(t, err, "invalid request") + + _, err = k.ListObjectsByBucketId(ctx, &types.QueryListObjectsByBucketIdRequest{ + Pagination: &query.PageRequest{ + Limit: types.MaxPaginationLimit + 1, + }, + }) + require.ErrorContains(t, err, "exceed pagination limit") + + _, err = k.ListObjectsByBucketId(ctx, &types.QueryListObjectsByBucketIdRequest{ + BucketId: "xxx", + }) + require.ErrorContains(t, err, "invalid bucket id") + + _, err = k.ListObjectsByBucketId(ctx, &types.QueryListObjectsByBucketIdRequest{ + BucketId: "0", + }) + require.ErrorIs(t, err, types.ErrNoSuchBucket) +} + +func TestQueryPolicyForAccount(t *testing.T) { + // invalid argument + k, ctx := makeKeeper(t) + _, err := k.QueryPolicyForAccount(ctx, nil) + require.ErrorContains(t, err, "invalid request") + + _, err = k.QueryPolicyForAccount(ctx, &types.QueryPolicyForAccountRequest{ + PrincipalAddress: "xxxx", + }) + require.ErrorContains(t, err, "invalid address hex length") + + _, err = k.QueryPolicyForAccount(ctx, &types.QueryPolicyForAccountRequest{ + PrincipalAddress: sample.RandAccAddressHex(), + Resource: "xxx", + }) + require.ErrorContains(t, err, "regex match error") +} + +func TestQueryPolicyForGroup(t *testing.T) { + // invalid argument + k, ctx := makeKeeper(t) + _, err := k.QueryPolicyForGroup(ctx, nil) + require.ErrorContains(t, err, "invalid request") + + _, err = k.QueryPolicyForGroup(ctx, &types.QueryPolicyForGroupRequest{ + PrincipalGroupId: "xxx", + }) + require.ErrorContains(t, err, "invalid group id") + + _, err = k.QueryPolicyForGroup(ctx, &types.QueryPolicyForGroupRequest{ + PrincipalGroupId: "10", + Resource: "xxx", + }) + require.ErrorContains(t, err, "regex match error") +} + +func TestVerifyPermission(t *testing.T) { + // invalid argument + k, ctx := makeKeeper(t) + _, err := k.VerifyPermission(ctx, nil) + require.ErrorContains(t, err, "invalid request") + + _, err = k.VerifyPermission(ctx, &types.QueryVerifyPermissionRequest{ + Operator: "xxx", + }) + require.ErrorContains(t, err, "invalid operator address") + + _, err = k.VerifyPermission(ctx, &types.QueryVerifyPermissionRequest{ + Operator: sample.RandAccAddressHex(), + BucketName: "", + }) + require.ErrorContains(t, err, "No bucket specified") + + _, err = k.VerifyPermission(ctx, &types.QueryVerifyPermissionRequest{ + Operator: sample.RandAccAddressHex(), + BucketName: "bucket", + }) + require.ErrorIs(t, err, types.ErrNoSuchBucket) +} + +func TestHeadGroup(t *testing.T) { + // invalid argument + k, ctx := makeKeeper(t) + _, err := k.HeadGroup(ctx, nil) + require.ErrorContains(t, err, "invalid request") + + _, err = k.HeadGroup(ctx, &types.QueryHeadGroupRequest{ + GroupOwner: "xxx", + }) + require.ErrorContains(t, err, "invalid address hex length") + + _, err = k.HeadGroup(ctx, &types.QueryHeadGroupRequest{ + GroupOwner: sample.RandAccAddressHex(), + GroupName: "group", + }) + require.ErrorIs(t, err, types.ErrNoSuchGroup) +} + +func TestListGroup(t *testing.T) { + // invalid argument + k, ctx := makeKeeper(t) + _, err := k.ListGroup(ctx, nil) + require.ErrorContains(t, err, "invalid request") + + _, err = k.ListGroup(ctx, &types.QueryListGroupRequest{ + Pagination: &query.PageRequest{ + Limit: types.MaxPaginationLimit + 1, + }, + }) + require.ErrorContains(t, err, "exceed pagination limit") + + _, err = k.ListGroup(ctx, &types.QueryListGroupRequest{ + GroupOwner: "xxx", + }) + require.ErrorContains(t, err, "invalid address hex length") +} + +func TestHeadGroupMember(t *testing.T) { + // invalid argument + k, ctx := makeKeeper(t) + _, err := k.ListGroup(ctx, nil) + require.ErrorContains(t, err, "invalid request") + + _, err = k.HeadGroupMember(ctx, &types.QueryHeadGroupMemberRequest{ + Member: "xxx", + }) + require.ErrorContains(t, err, "invalid address hex length") + + _, err = k.HeadGroupMember(ctx, &types.QueryHeadGroupMemberRequest{ + Member: sample.RandAccAddressHex(), + GroupOwner: "xxx", + }) + require.ErrorContains(t, err, "invalid address hex length") + + _, err = k.HeadGroupMember(ctx, &types.QueryHeadGroupMemberRequest{ + Member: sample.RandAccAddressHex(), + GroupOwner: sample.RandAccAddressHex(), + GroupName: "group", + }) + require.ErrorIs(t, err, types.ErrNoSuchGroup) +} + +func TestQueryPolicyById(t *testing.T) { + // invalid argument + k, ctx := makeKeeper(t) + _, err := k.QueryPolicyById(ctx, nil) + require.ErrorContains(t, err, "invalid request") + + _, err = k.QueryPolicyById(ctx, &types.QueryPolicyByIdRequest{ + PolicyId: "xxx", + }) + require.ErrorContains(t, err, "invalid policy id") +} diff --git a/x/storage/keeper/keeper.go b/x/storage/keeper/keeper.go index 383088411..75e2d9e21 100644 --- a/x/storage/keeper/keeper.go +++ b/x/storage/keeper/keeper.go @@ -446,7 +446,7 @@ func (k Keeper) DiscontinueBucket(ctx sdk.Context, operator sdk.AccAddress, buck "only primary sp is allowed to do discontinue bucket, expect sp id : %d", spInState.Id) } - count := k.getDiscontinueBucketCount(ctx, operator) + count := k.GetDiscontinueBucketCount(ctx, operator) max := k.DiscontinueBucketMax(ctx) if count+1 > max { return types.ErrNoMoreDiscontinue.Wrapf("no more buckets can be requested in this window") @@ -461,7 +461,7 @@ func (k Keeper) DiscontinueBucket(ctx sdk.Context, operator sdk.AccAddress, buck deleteAt := ctx.BlockTime().Unix() + k.DiscontinueConfirmPeriod(ctx) k.appendDiscontinueBucketIds(ctx, deleteAt, []sdkmath.Uint{bucketInfo.Id}) - k.setDiscontinueBucketCount(ctx, operator, count+1) + k.SetDiscontinueBucketCount(ctx, operator, count+1) if err := ctx.EventManager().EmitTypedEvents(&types.EventDiscontinueBucket{ BucketId: bucketInfo.Id, @@ -1126,7 +1126,7 @@ func (k Keeper) DiscontinueObject(ctx sdk.Context, operator sdk.AccAddress, buck return errors.Wrapf(types.ErrAccessDenied, "only primary sp is allowed to do discontinue objects") } - count := k.getDiscontinueObjectCount(ctx, operator) + count := k.GetDiscontinueObjectCount(ctx, operator) max := k.DiscontinueObjectMax(ctx) if count+uint64(len(objectIds)) > max { return types.ErrNoMoreDiscontinue.Wrapf("only %d objects can be requested in this window", max-count) @@ -1155,7 +1155,7 @@ func (k Keeper) DiscontinueObject(ctx sdk.Context, operator sdk.AccAddress, buck deleteAt := ctx.BlockTime().Unix() + k.DiscontinueConfirmPeriod(ctx) k.AppendDiscontinueObjectIds(ctx, deleteAt, objectIds) - k.setDiscontinueObjectCount(ctx, operator, count+uint64(len(objectIds))) + k.SetDiscontinueObjectCount(ctx, operator, count+uint64(len(objectIds))) events := make([]proto.Message, 0) for _, objectId := range objectIds { @@ -1470,7 +1470,7 @@ func (k Keeper) isNonEmptyBucket(ctx sdk.Context, bucketName string) bool { return iter.Valid() } -func (k Keeper) getDiscontinueObjectCount(ctx sdk.Context, operator sdk.AccAddress) uint64 { +func (k Keeper) GetDiscontinueObjectCount(ctx sdk.Context, operator sdk.AccAddress) uint64 { store := prefix.NewStore(ctx.KVStore(k.storeKey), types.DiscontinueObjectCountPrefix) bz := store.Get(operator.Bytes()) @@ -1480,7 +1480,7 @@ func (k Keeper) getDiscontinueObjectCount(ctx sdk.Context, operator sdk.AccAddre return binary.BigEndian.Uint64(bz) } -func (k Keeper) setDiscontinueObjectCount(ctx sdk.Context, operator sdk.AccAddress, count uint64) { +func (k Keeper) SetDiscontinueObjectCount(ctx sdk.Context, operator sdk.AccAddress, count uint64) { store := prefix.NewStore(ctx.KVStore(k.storeKey), types.DiscontinueObjectCountPrefix) countBytes := make([]byte, 8) @@ -1551,7 +1551,7 @@ func (k Keeper) DeleteDiscontinueObjectsUntil(ctx sdk.Context, timestamp int64, return deleted, nil } -func (k Keeper) getDiscontinueBucketCount(ctx sdk.Context, operator sdk.AccAddress) uint64 { +func (k Keeper) GetDiscontinueBucketCount(ctx sdk.Context, operator sdk.AccAddress) uint64 { store := prefix.NewStore(ctx.KVStore(k.storeKey), types.DiscontinueBucketCountPrefix) bz := store.Get(operator.Bytes()) @@ -1561,7 +1561,7 @@ func (k Keeper) getDiscontinueBucketCount(ctx sdk.Context, operator sdk.AccAddre return binary.BigEndian.Uint64(bz) } -func (k Keeper) setDiscontinueBucketCount(ctx sdk.Context, operator sdk.AccAddress, count uint64) { +func (k Keeper) SetDiscontinueBucketCount(ctx sdk.Context, operator sdk.AccAddress, count uint64) { store := prefix.NewStore(ctx.KVStore(k.storeKey), types.DiscontinueBucketCountPrefix) countBytes := make([]byte, 8) diff --git a/x/storage/keeper/keeper_test.go b/x/storage/keeper/keeper_test.go new file mode 100644 index 000000000..5c0c44b2c --- /dev/null +++ b/x/storage/keeper/keeper_test.go @@ -0,0 +1,29 @@ +package keeper_test + +import "github.com/bnb-chain/greenfield/testutil/sample" + +func (s *TestSuite) TestClearDiscontinueBucketCount() { + acc1 := sample.RandAccAddress() + s.storageKeeper.SetDiscontinueBucketCount(s.ctx, acc1, 1) + + count := s.storageKeeper.GetDiscontinueBucketCount(s.ctx, acc1) + s.Require().Equal(uint64(1), count) + + s.storageKeeper.ClearDiscontinueBucketCount(s.ctx) + + count = s.storageKeeper.GetDiscontinueBucketCount(s.ctx, acc1) + s.Require().Equal(uint64(0), count) +} + +func (s *TestSuite) TestClearDiscontinueObjectCount() { + acc1 := sample.RandAccAddress() + s.storageKeeper.SetDiscontinueObjectCount(s.ctx, acc1, 1) + + count := s.storageKeeper.GetDiscontinueObjectCount(s.ctx, acc1) + s.Require().Equal(uint64(1), count) + + s.storageKeeper.ClearDiscontinueObjectCount(s.ctx) + + count = s.storageKeeper.GetDiscontinueObjectCount(s.ctx, acc1) + s.Require().Equal(uint64(0), count) +} diff --git a/x/storage/keeper/msg_server_test.go b/x/storage/keeper/msg_server_test.go deleted file mode 100644 index 942926490..000000000 --- a/x/storage/keeper/msg_server_test.go +++ /dev/null @@ -1 +0,0 @@ -package keeper_test diff --git a/x/storage/keeper/payment_test.go b/x/storage/keeper/payment_test.go index 5a9614572..a13db11d6 100644 --- a/x/storage/keeper/payment_test.go +++ b/x/storage/keeper/payment_test.go @@ -112,8 +112,6 @@ func (s *TestSuite) TestGetObjectLockFee() { s.paymentKeeper.EXPECT().GetStoragePrice(gomock.Any(), gomock.Any()). Return(price, nil).AnyTimes() params := paymenttypes.DefaultParams() - s.paymentKeeper.EXPECT().GetParams(gomock.Any()). - Return(params).AnyTimes() s.paymentKeeper.EXPECT().GetVersionedParamsWithTs(gomock.Any(), gomock.Any()). Return(params.VersionedParams, nil).AnyTimes() diff --git a/x/storage/keeper/query.go b/x/storage/keeper/query.go index f18e900e1..185b60908 100644 --- a/x/storage/keeper/query.go +++ b/x/storage/keeper/query.go @@ -252,7 +252,7 @@ func (k Keeper) HeadGroupNFT(goCtx context.Context, req *types.QueryNFTRequest) ctx := sdk.UnwrapSDKContext(goCtx) groupInfo, found := k.GetGroupInfoById(ctx, id) if !found { - return nil, types.ErrNoSuchObject + return nil, types.ErrNoSuchGroup } return &types.QueryGroupNFTResponse{ MetaData: groupInfo.ToNFTMetadata(), @@ -443,6 +443,10 @@ func (k Keeper) HeadGroupMember(goCtx context.Context, req *types.QueryHeadGroup func (k Keeper) QueryPolicyById(goCtx context.Context, req *types.QueryPolicyByIdRequest) (*types. QueryPolicyByIdResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + ctx := sdk.UnwrapSDKContext(goCtx) policyId, err := math.ParseUint(req.PolicyId) if err != nil { diff --git a/x/storage/module.go b/x/storage/module.go index a63693144..1dbf7ea73 100644 --- a/x/storage/module.go +++ b/x/storage/module.go @@ -79,7 +79,7 @@ func (a AppModuleBasic) GetTxCmd() *cobra.Command { // GetQueryCmd returns the root query command for the module. The subcommands of this root command are used by end-users to generate new queries to the subset of the state defined by the module func (AppModuleBasic) GetQueryCmd() *cobra.Command { - return cli.GetQueryCmd(types.StoreKey) + return cli.GetQueryCmd() } // ---------------------------------------------------------------------------- @@ -143,11 +143,11 @@ func (AppModule) ConsensusVersion() uint64 { return 1 } // BeginBlock contains the logic that is automatically triggered at the beginning of each block func (am AppModule) BeginBlock(ctx sdk.Context, _ abci.RequestBeginBlock) { - BeginBlocker(ctx, am.keeper) + keeper.BeginBlocker(ctx, am.keeper) } // EndBlock contains the logic that is automatically triggered at the end of each block func (am AppModule) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { - EndBlocker(ctx, am.keeper) + keeper.EndBlocker(ctx, am.keeper) return []abci.ValidatorUpdate{} } diff --git a/x/storage/types/crosschain.go b/x/storage/types/crosschain.go index 36938efd8..e43b2650b 100644 --- a/x/storage/types/crosschain.go +++ b/x/storage/types/crosschain.go @@ -381,6 +381,24 @@ var ( } ) +func (p CreateBucketSynPackage) MustSerialize() []byte { + encodedBytes, err := createBucketSynPackageStructArgs.Pack(&CreateBucketSynPackageStruct{ + Creator: common.BytesToAddress(p.Creator), + BucketName: p.BucketName, + Visibility: p.Visibility, + PaymentAddress: common.BytesToAddress(p.PaymentAddress), + PrimarySpAddress: common.BytesToAddress(p.PrimarySpAddress), + PrimarySpApprovalExpiredHeight: p.PrimarySpApprovalExpiredHeight, + PrimarySpApprovalSignature: p.PrimarySpApprovalSignature, + ChargedReadQuota: p.ChargedReadQuota, + ExtraData: p.ExtraData, + }) + if err != nil { + panic("encode create bucket syn package error") + } + return encodedBytes +} + func (p CreateBucketSynPackage) ValidateBasic() error { msg := MsgCreateBucket{ Creator: p.Creator.String(), @@ -643,6 +661,18 @@ func (p CreateGroupSynPackage) ValidateBasic() error { return msg.ValidateBasic() } +func (p CreateGroupSynPackage) MustSerialize() []byte { + encodedBytes, err := createGroupSynPackageArgs.Pack(&CreateGroupSynPackageStruct{ + Creator: common.BytesToAddress(p.Creator), + GroupName: p.GroupName, + ExtraData: p.ExtraData, + }) + if err != nil { + panic("encode create group syn package error") + } + return encodedBytes +} + func DeserializeCreateGroupSynPackage(serializedPackage []byte) (interface{}, error) { unpacked, err := createGroupSynPackageArgs.Unpack(serializedPackage) if err != nil { @@ -883,6 +913,26 @@ func (p UpdateGroupMemberSynPackage) GetMembers() []string { return members } +func (p UpdateGroupMemberSynPackage) MustSerialize() []byte { + totalMember := len(p.Members) + members := make([]common.Address, totalMember) + for i, member := range p.Members { + members[i] = common.BytesToAddress(member) + } + + encodedBytes, err := updateGroupMemberSynPackageArgs.Pack(&UpdateGroupMemberSynPackageStruct{ + common.BytesToAddress(p.Operator), + SafeBigInt(p.GroupId), + p.OperationType, + members, + p.ExtraData, + }) + if err != nil { + panic("encode update group member syn package error") + } + return encodedBytes +} + func (p UpdateGroupMemberSynPackage) ValidateBasic() error { if p.OperationType != OperationAddGroupMember && p.OperationType != OperationDeleteGroupMember { return ErrInvalidOperationType @@ -1007,7 +1057,7 @@ func (p UpdateGroupMemberAckPackage) MustSerialize() []byte { p.ExtraData, }) if err != nil { - panic("encode delete group ack package error") + panic("encode update group member ack package error") } return encodedBytes } diff --git a/x/storage/types/expected_keepers.go b/x/storage/types/expected_keepers.go index b225eed5e..e1244eae7 100644 --- a/x/storage/types/expected_keepers.go +++ b/x/storage/types/expected_keepers.go @@ -41,15 +41,11 @@ type SpKeeper interface { } type PaymentKeeper interface { - GetParams(ctx sdk.Context) paymenttypes.Params GetVersionedParamsWithTs(ctx sdk.Context, time int64) (paymenttypes.VersionedParams, error) IsPaymentAccountOwner(ctx sdk.Context, addr, owner sdk.AccAddress) bool GetStoragePrice(ctx sdk.Context, params paymenttypes.StoragePriceParams) (price paymenttypes.StoragePrice, err error) ApplyUserFlowsList(ctx sdk.Context, userFlows []paymenttypes.UserFlows) (err error) UpdateStreamRecordByAddr(ctx sdk.Context, change *paymenttypes.StreamRecordChange) (ret *paymenttypes.StreamRecord, err error) - GetStreamRecord(ctx sdk.Context, account sdk.AccAddress) (val *paymenttypes.StreamRecord, found bool) - UpdateStreamRecord(ctx sdk.Context, streamRecord *paymenttypes.StreamRecord, change *paymenttypes.StreamRecordChange) error - Withdraw(ctx sdk.Context, fromAddr, toAddr sdk.AccAddress, amount math.Int) error } type PermissionKeeper interface { diff --git a/x/storage/types/expected_keepers_mocks.go b/x/storage/types/expected_keepers_mocks.go index 718ac345f..cfeddb94f 100644 --- a/x/storage/types/expected_keepers_mocks.go +++ b/x/storage/types/expected_keepers_mocks.go @@ -284,20 +284,6 @@ func (mr *MockPaymentKeeperMockRecorder) ApplyUserFlowsList(ctx, userFlows inter return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ApplyUserFlowsList", reflect.TypeOf((*MockPaymentKeeper)(nil).ApplyUserFlowsList), ctx, userFlows) } -// GetParams mocks base method. -func (m *MockPaymentKeeper) GetParams(ctx types3.Context) types.Params { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetParams", ctx) - ret0, _ := ret[0].(types.Params) - return ret0 -} - -// GetParams indicates an expected call of GetParams. -func (mr *MockPaymentKeeperMockRecorder) GetParams(ctx interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetParams", reflect.TypeOf((*MockPaymentKeeper)(nil).GetParams), ctx) -} - // GetStoragePrice mocks base method. func (m *MockPaymentKeeper) GetStoragePrice(ctx types3.Context, params types.StoragePriceParams) (types.StoragePrice, error) { m.ctrl.T.Helper() @@ -313,21 +299,6 @@ func (mr *MockPaymentKeeperMockRecorder) GetStoragePrice(ctx, params interface{} return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStoragePrice", reflect.TypeOf((*MockPaymentKeeper)(nil).GetStoragePrice), ctx, params) } -// GetStreamRecord mocks base method. -func (m *MockPaymentKeeper) GetStreamRecord(ctx types3.Context, account types3.AccAddress) (*types.StreamRecord, bool) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetStreamRecord", ctx, account) - ret0, _ := ret[0].(*types.StreamRecord) - ret1, _ := ret[1].(bool) - return ret0, ret1 -} - -// GetStreamRecord indicates an expected call of GetStreamRecord. -func (mr *MockPaymentKeeperMockRecorder) GetStreamRecord(ctx, account interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStreamRecord", reflect.TypeOf((*MockPaymentKeeper)(nil).GetStreamRecord), ctx, account) -} - // GetVersionedParamsWithTs mocks base method. func (m *MockPaymentKeeper) GetVersionedParamsWithTs(ctx types3.Context, time int64) (types.VersionedParams, error) { m.ctrl.T.Helper() @@ -357,20 +328,6 @@ func (mr *MockPaymentKeeperMockRecorder) IsPaymentAccountOwner(ctx, addr, owner return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsPaymentAccountOwner", reflect.TypeOf((*MockPaymentKeeper)(nil).IsPaymentAccountOwner), ctx, addr, owner) } -// UpdateStreamRecord mocks base method. -func (m *MockPaymentKeeper) UpdateStreamRecord(ctx types3.Context, streamRecord *types.StreamRecord, change *types.StreamRecordChange) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "UpdateStreamRecord", ctx, streamRecord, change) - ret0, _ := ret[0].(error) - return ret0 -} - -// UpdateStreamRecord indicates an expected call of UpdateStreamRecord. -func (mr *MockPaymentKeeperMockRecorder) UpdateStreamRecord(ctx, streamRecord, change interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateStreamRecord", reflect.TypeOf((*MockPaymentKeeper)(nil).UpdateStreamRecord), ctx, streamRecord, change) -} - // UpdateStreamRecordByAddr mocks base method. func (m *MockPaymentKeeper) UpdateStreamRecordByAddr(ctx types3.Context, change *types.StreamRecordChange) (*types.StreamRecord, error) { m.ctrl.T.Helper() @@ -386,20 +343,6 @@ func (mr *MockPaymentKeeperMockRecorder) UpdateStreamRecordByAddr(ctx, change in return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateStreamRecordByAddr", reflect.TypeOf((*MockPaymentKeeper)(nil).UpdateStreamRecordByAddr), ctx, change) } -// Withdraw mocks base method. -func (m *MockPaymentKeeper) Withdraw(ctx types3.Context, fromAddr, toAddr types3.AccAddress, amount math.Int) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Withdraw", ctx, fromAddr, toAddr, amount) - ret0, _ := ret[0].(error) - return ret0 -} - -// Withdraw indicates an expected call of Withdraw. -func (mr *MockPaymentKeeperMockRecorder) Withdraw(ctx, fromAddr, toAddr, amount interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Withdraw", reflect.TypeOf((*MockPaymentKeeper)(nil).Withdraw), ctx, fromAddr, toAddr, amount) -} - // MockPermissionKeeper is a mock of PermissionKeeper interface. type MockPermissionKeeper struct { ctrl *gomock.Controller diff --git a/x/virtualgroup/client/cli/query.go b/x/virtualgroup/client/cli/query.go index 0ca76153b..7193e9ffd 100644 --- a/x/virtualgroup/client/cli/query.go +++ b/x/virtualgroup/client/cli/query.go @@ -12,7 +12,7 @@ import ( ) // GetQueryCmd returns the cli query commands for this module -func GetQueryCmd(queryRoute string) *cobra.Command { +func GetQueryCmd() *cobra.Command { // Group storage queries under a subcommand cmd := &cobra.Command{ Use: types.ModuleName, diff --git a/x/virtualgroup/client/cli/query_global_virtual_group_by_family_id.go b/x/virtualgroup/client/cli/query_global_virtual_group_by_family_id.go index 8fbc62947..5c57f751e 100644 --- a/x/virtualgroup/client/cli/query_global_virtual_group_by_family_id.go +++ b/x/virtualgroup/client/cli/query_global_virtual_group_by_family_id.go @@ -19,7 +19,6 @@ func CmdGlobalVirtualGroupByFamilyID() *cobra.Command { Short: "query virtual group by family id", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) (err error) { - familyID, err := strconv.ParseInt(args[0], 10, 32) if err != nil || familyID <= 0 { return fmt.Errorf("invalid GVG id %s", args[1]) diff --git a/x/virtualgroup/client/cli/query_global_virtual_group_families.go b/x/virtualgroup/client/cli/query_global_virtual_group_families.go index 6714a67dd..dba4e5311 100644 --- a/x/virtualgroup/client/cli/query_global_virtual_group_families.go +++ b/x/virtualgroup/client/cli/query_global_virtual_group_families.go @@ -20,10 +20,9 @@ func CmdGlobalVirtualGroupFamilies() *cobra.Command { Short: "query all global virtual groups families of the storage provider.", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) (err error) { - - limit, err := strconv.ParseInt(args[1], 10, 32) + limit, err := strconv.ParseInt(args[0], 10, 32) if err != nil || limit <= 0 { - return fmt.Errorf("invalid GVG id %s", args[1]) + return fmt.Errorf("invalid limit %s", args[0]) } clientCtx, err := client.GetClientQueryContext(cmd) diff --git a/x/virtualgroup/client/cli/query_test.go b/x/virtualgroup/client/cli/query_test.go new file mode 100644 index 000000000..51838ba12 --- /dev/null +++ b/x/virtualgroup/client/cli/query_test.go @@ -0,0 +1,98 @@ +package cli_test + +import ( + "fmt" + + clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" + "github.com/cosmos/gogoproto/proto" + + "github.com/bnb-chain/greenfield/x/virtualgroup/client/cli" + "github.com/bnb-chain/greenfield/x/virtualgroup/types" + "github.com/cosmos/cosmos-sdk/client/flags" +) + +func (s *CLITestSuite) TestQueryCmd() { + commonFlags := []string{ + fmt.Sprintf("--%s=%s", flags.FlagOutput, "json"), + } + + testCases := []struct { + name string + args []string + expectErr bool + expectErrMsg string + respType proto.Message + }{ + { + "query params", + append( + []string{ + "params", + }, + commonFlags..., + ), + false, "", &types.QueryParamsResponse{}, + }, + { + "query global-virtual-group", + append( + []string{ + "global-virtual-group", + "1", + }, + commonFlags..., + ), + false, "", &types.QueryGlobalVirtualGroupResponse{}, + }, + { + "query global-virtual-group-by-family-id", + append( + []string{ + "global-virtual-group-by-family-id", + "1", + }, + commonFlags..., + ), + false, "", &types.QueryGlobalVirtualGroupByFamilyIDResponse{}, + }, + { + "query global-virtual-group-families", + append( + []string{ + "global-virtual-group-families", + "100", + }, + commonFlags..., + ), + false, "", &types.QueryGlobalVirtualGroupFamiliesResponse{}, + }, + { + "query global-virtual-group-family", + append( + []string{ + "global-virtual-group-family", + "1", + }, + commonFlags..., + ), + false, "", &types.QueryGlobalVirtualGroupFamilyResponse{}, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.GetQueryCmd() + out, err := clitestutil.ExecTestCLICmd(s.clientCtx, cmd, tc.args) + + if tc.expectErr { + s.Require().Error(err) + s.Require().Contains(err.Error(), tc.expectErrMsg) + } else { + s.Require().NoError(err) + s.Require().NoError(s.clientCtx.Codec.UnmarshalJSON(out.Bytes(), tc.respType), out.String()) + } + }) + } +} diff --git a/x/virtualgroup/client/cli/tx_test.go b/x/virtualgroup/client/cli/tx_test.go new file mode 100644 index 000000000..0c726d75b --- /dev/null +++ b/x/virtualgroup/client/cli/tx_test.go @@ -0,0 +1,70 @@ +package cli_test + +import ( + "bytes" + "io" + "testing" + + abci "github.com/cometbft/cometbft/abci/types" + rpcclientmock "github.com/cometbft/cometbft/rpc/client/mock" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/cosmos/cosmos-sdk/testutil" + clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/suite" + + "github.com/bnb-chain/greenfield/app" + "github.com/bnb-chain/greenfield/app/params" + "github.com/bnb-chain/greenfield/sdk/client/test" +) + +type CLITestSuite struct { + suite.Suite + + kr keyring.Keyring + baseCtx client.Context + encCfg params.EncodingConfig + clientCtx client.Context +} + +func TestCLITestSuite(t *testing.T) { + suite.Run(t, new(CLITestSuite)) +} + +func (s *CLITestSuite) SetupSuite() { + s.T().Log("setting up integration test suite") + + s.encCfg = app.MakeEncodingConfig() + s.kr = keyring.NewInMemory(s.encCfg.Marshaler) + s.baseCtx = client.Context{}. + WithKeyring(s.kr). + WithTxConfig(s.encCfg.TxConfig). + WithCodec(s.encCfg.Marshaler). + WithClient(clitestutil.MockTendermintRPC{Client: rpcclientmock.Client{}}). + WithAccountRetriever(client.MockAccountRetriever{}). + WithOutput(io.Discard). + WithChainID(test.TEST_CHAIN_ID) + + accounts := testutil.CreateKeyringAccounts(s.T(), s.kr, 1) + s.baseCtx = s.baseCtx.WithFrom(accounts[0].Address.String()) + s.baseCtx = s.baseCtx.WithFromName(accounts[0].Name) + s.baseCtx = s.baseCtx.WithFromAddress(accounts[0].Address) + + var outBuf bytes.Buffer + ctxGen := func() client.Context { + bz, _ := s.encCfg.Marshaler.Marshal(&sdk.TxResponse{}) + c := clitestutil.NewMockTendermintRPC(abci.ResponseQuery{ + Value: bz, + }) + + return s.baseCtx.WithClient(c) + } + s.clientCtx = ctxGen().WithOutput(&outBuf) + + if testing.Short() { + s.T().Skip("skipping test in unit-tests mode.") + } +} + +// TODO: Add more tests diff --git a/x/virtualgroup/keeper/genesis_test.go b/x/virtualgroup/keeper/genesis_test.go new file mode 100644 index 000000000..49adbad00 --- /dev/null +++ b/x/virtualgroup/keeper/genesis_test.go @@ -0,0 +1,27 @@ +package keeper_test + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + types2 "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/golang/mock/gomock" + + "github.com/bnb-chain/greenfield/x/virtualgroup" + "github.com/bnb-chain/greenfield/x/virtualgroup/types" +) + +func (s *TestSuite) TestGenesis() { + genesisState := types.GenesisState{ + Params: types.DefaultParams(), + + // this line is used by starport scaffolding # genesis/test/state + } + + s.accountKeeper.EXPECT().GetModuleAccount(gomock.Any(), gomock.Any()).Return(types2.NewEmptyModuleAccount(types.ModuleName)) + s.accountKeeper.EXPECT().SetModuleAccount(gomock.Any(), gomock.Any()).Return() + s.bankKeeper.EXPECT().GetAllBalances(gomock.Any(), gomock.Any()).Return(sdk.NewCoins(sdk.NewCoin(genesisState.Params.DepositDenom, sdk.ZeroInt()))) + virtualgroup.InitGenesis(s.ctx, *s.virtualgroupKeeper, genesisState) + + got := virtualgroup.ExportGenesis(s.ctx, *s.virtualgroupKeeper) + s.Require().NotNil(got) + s.Require().Equal(genesisState.Params, got.Params) +} diff --git a/x/virtualgroup/module.go b/x/virtualgroup/module.go index 9afdd6832..8d05904be 100644 --- a/x/virtualgroup/module.go +++ b/x/virtualgroup/module.go @@ -81,7 +81,7 @@ func (a AppModuleBasic) GetTxCmd() *cobra.Command { // GetQueryCmd returns the root query command for the module. The subcommands of this root command are used by end-users to generate new queries to the subset of the state defined by the module func (AppModuleBasic) GetQueryCmd() *cobra.Command { - return cli.GetQueryCmd(types.StoreKey) + return cli.GetQueryCmd() } // ----------------------------------------------------------------------------