From 7f05ad4f2ad7ffa1184a30fbd24fd8144d8a4d7d Mon Sep 17 00:00:00 2001 From: Sai Kumar <17549398+gsk967@users.noreply.github.com> Date: Wed, 12 Apr 2023 14:39:23 +0530 Subject: [PATCH] tests: add e2e tests for ibc_status params (#1978) * chore: add tests in flow 1. check the quota after ibc_transfer 2. send the tokens to umee to gaia and back 3. disable the quota check 4. check the prices effected or not * chore: fix the lint * review: address the pr comments * Apply suggestions from code review * review: fix the lint --------- Co-authored-by: Robert Zaremba --- client/client.go | 14 +++---- client/gov.go | 20 +++++++++ client/oracle.go | 10 ++--- client/uibc.go | 20 +++++++++ sdkclient/client.go | 9 ++-- sdkclient/tx/client.go | 26 ++++++++---- sdkclient/tx/gov.go | 84 +++++++++++++++++++++++++++---------- sdkclient/tx/key.go | 14 ++----- tests/e2e/e2e_ibc_test.go | 35 ++++++++++++++-- tests/e2e/e2e_setup_test.go | 12 ++++-- tests/e2e/e2e_test.go | 1 - tests/grpc/gov.go | 82 ++++++++++++++++++++++++------------ 12 files changed, 239 insertions(+), 88 deletions(-) create mode 100644 client/gov.go create mode 100644 client/uibc.go diff --git a/client/client.go b/client/client.go index 5fa6a3be11..4a482cbf9f 100644 --- a/client/client.go +++ b/client/client.go @@ -13,17 +13,17 @@ type Client struct { } // NewClient constructs Client object. +// Accounts are generated using the list of mnemonics. Each string must be a sequence of words, +// eg: `["w11 w12 w13", "w21 w22 w23"]`. Keyring names for created accounts will be: val1, val2.... func NewClient( chainID, tmrpcEndpoint, - grpcEndpoint, - accountName, - accountMnemonic string, + grpcEndpoint string, + mnemonics []string, gasAdjustment float64, encCfg sdkparams.EncodingConfig, ) (Client, error) { - c, err := sdkclient.NewClient(chainID, tmrpcEndpoint, grpcEndpoint, - accountName, accountMnemonic, gasAdjustment, encCfg) + c, err := sdkclient.NewClient(chainID, tmrpcEndpoint, grpcEndpoint, mnemonics, gasAdjustment, encCfg) if err != nil { return Client{}, err } @@ -32,10 +32,10 @@ func NewClient( }, nil } -func (c *Client) NewQCtx() (context.Context, context.CancelFunc) { +func (c Client) NewQCtx() (context.Context, context.CancelFunc) { return c.Query.NewCtx() } -func (c *Client) NewQCtxWithCancel() (context.Context, context.CancelFunc) { +func (c Client) NewQCtxWithCancel() (context.Context, context.CancelFunc) { return c.Query.NewCtxWithCancel() } diff --git a/client/gov.go b/client/gov.go new file mode 100644 index 0000000000..263116b5ea --- /dev/null +++ b/client/gov.go @@ -0,0 +1,20 @@ +package client + +import ( + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1" +) + +func (c Client) GovQClient() govtypes.QueryClient { + return govtypes.NewQueryClient(c.Query.GrpcConn) +} + +func (c Client) GovProposal(proposalID uint64) (*govtypes.Proposal, error) { + ctx, cancel := c.NewQCtx() + defer cancel() + + queryResponse, err := c.GovQClient().Proposal(ctx, &govtypes.QueryProposalRequest{ProposalId: proposalID}) + if err != nil { + return nil, err + } + return queryResponse.Proposal, nil +} diff --git a/client/oracle.go b/client/oracle.go index 2a901036ce..c37023ec3b 100644 --- a/client/oracle.go +++ b/client/oracle.go @@ -6,11 +6,11 @@ import ( oracletypes "github.com/umee-network/umee/v4/x/oracle/types" ) -func (c *Client) OracleQueryClient() oracletypes.QueryClient { +func (c Client) OracleQueryClient() oracletypes.QueryClient { return oracletypes.NewQueryClient(c.Query.GrpcConn) } -func (c *Client) QueryOracleParams() (oracletypes.Params, error) { +func (c Client) QueryOracleParams() (oracletypes.Params, error) { ctx, cancel := c.NewQCtx() defer cancel() @@ -18,7 +18,7 @@ func (c *Client) QueryOracleParams() (oracletypes.Params, error) { return queryResponse.Params, err } -func (c *Client) QueryExchangeRates() ([]sdk.DecCoin, error) { +func (c Client) QueryExchangeRates() ([]sdk.DecCoin, error) { ctx, cancel := c.NewQCtx() defer cancel() @@ -26,7 +26,7 @@ func (c *Client) QueryExchangeRates() ([]sdk.DecCoin, error) { return queryResponse.ExchangeRates, err } -func (c *Client) QueryMedians() ([]oracletypes.Price, error) { +func (c Client) QueryMedians() ([]oracletypes.Price, error) { ctx, cancel := c.NewQCtx() defer cancel() @@ -34,7 +34,7 @@ func (c *Client) QueryMedians() ([]oracletypes.Price, error) { return resp.Medians, err } -func (c *Client) QueryMedianDeviations() ([]oracletypes.Price, error) { +func (c Client) QueryMedianDeviations() ([]oracletypes.Price, error) { ctx, cancel := c.NewQCtx() defer cancel() diff --git a/client/uibc.go b/client/uibc.go new file mode 100644 index 0000000000..41e500758a --- /dev/null +++ b/client/uibc.go @@ -0,0 +1,20 @@ +package client + +import ( + "github.com/umee-network/umee/v4/x/uibc" +) + +func (c Client) UIBCQueryClient() uibc.QueryClient { + return uibc.NewQueryClient(c.Query.GrpcConn) +} + +func (c Client) QueryUIBCParams() (uibc.Params, error) { + ctx, cancel := c.NewQCtx() + defer cancel() + + queryResponse, err := c.UIBCQueryClient().Params(ctx, &uibc.QueryParams{}) + if err != nil { + return uibc.Params{}, err + } + return queryResponse.Params, nil +} diff --git a/sdkclient/client.go b/sdkclient/client.go index 6b105c1b93..212a712c1f 100644 --- a/sdkclient/client.go +++ b/sdkclient/client.go @@ -15,6 +15,8 @@ import ( // transactions and queries. The object should be extended by another struct to provide // chain specific transactions and queries. Example: // https://github.com/umee-network/umee/blob/main/client +// Accounts are generated using the list of mnemonics. Each string must be a sequence of words, +// eg: `["w11 w12 w13", "w21 w22 w23"]`. Keyring names for created accounts will be: val1, val2.... type Client struct { Query *query.Client Tx *tx.Client @@ -23,9 +25,8 @@ type Client struct { func NewClient( chainID, tmrpcEndpoint, - grpcEndpoint, - accountName, - accountMnemonic string, + grpcEndpoint string, + mnemonics []string, gasAdjustment float64, encCfg sdkparams.EncodingConfig, ) (uc Client, err error) { @@ -34,7 +35,7 @@ func NewClient( if err != nil { return Client{}, err } - uc.Tx, err = tx.NewClient(chainID, tmrpcEndpoint, accountName, accountMnemonic, gasAdjustment, encCfg) + uc.Tx, err = tx.NewClient(chainID, tmrpcEndpoint, mnemonics, gasAdjustment, encCfg) return uc, err } diff --git a/sdkclient/tx/client.go b/sdkclient/tx/client.go index 6d5f939221..13e0559a80 100644 --- a/sdkclient/tx/client.go +++ b/sdkclient/tx/client.go @@ -1,6 +1,7 @@ package tx import ( + "fmt" "os" "github.com/cosmos/cosmos-sdk/client" @@ -23,18 +24,18 @@ type Client struct { gasAdjustment float64 keyringKeyring keyring.Keyring - keyringRecord *keyring.Record + keyringRecord []*keyring.Record txFactory *tx.Factory encCfg sdkparams.EncodingConfig } // Initializes a cosmos sdk client context and transaction factory for // signing and broadcasting transactions +// Note: For signing the transactions accounts are created by names like this val0, val1.... func NewClient( chainID string, tmrpcEndpoint string, - accountName string, - accountMnemonic string, + mnemonics []string, gasAdjustment float64, encCfg sdkparams.EncodingConfig, ) (c *Client, err error) { @@ -45,11 +46,19 @@ func NewClient( encCfg: encCfg, } - c.keyringRecord, c.keyringKeyring, err = CreateAccountFromMnemonic(accountName, accountMnemonic, encCfg.Codec) + c.keyringKeyring, err = keyring.New(keyringAppName, keyring.BackendTest, "", nil, encCfg.Codec) if err != nil { return nil, err } + for index, menomic := range mnemonics { + kr, err := CreateAccountFromMnemonic(c.keyringKeyring, fmt.Sprintf("val%d", index), menomic) + c.keyringRecord = append(c.keyringRecord, kr) + if err != nil { + return nil, err + } + } + err = c.initClientCtx() if err != nil { return nil, err @@ -60,7 +69,7 @@ func NewClient( } func (c *Client) initClientCtx() error { - fromAddress, _ := c.keyringRecord.GetAddress() + fromAddress, _ := c.keyringRecord[0].GetAddress() tmHTTPClient, err := tmjsonclient.DefaultHTTPClient(c.TMRPCEndpoint) if err != nil { @@ -85,8 +94,8 @@ func (c *Client) initClientCtx() error { Client: tmRPCClient, Keyring: c.keyringKeyring, FromAddress: fromAddress, - FromName: c.keyringRecord.Name, - From: c.keyringRecord.Name, + FromName: c.keyringRecord[0].Name, + From: c.keyringRecord[0].Name, OutputFormat: "json", UseLedger: false, Simulate: false, @@ -110,5 +119,8 @@ func (c *Client) initTxFactory() { } func (c *Client) BroadcastTx(msgs ...sdk.Msg) (*sdk.TxResponse, error) { + c.ClientContext.From = c.keyringRecord[0].Name + c.ClientContext.FromName = c.keyringRecord[0].Name + c.ClientContext.FromAddress, _ = c.keyringRecord[0].GetAddress() return BroadcastTx(*c.ClientContext, *c.txFactory, msgs...) } diff --git a/sdkclient/tx/gov.go b/sdkclient/tx/gov.go index fbb3283148..e84860faeb 100644 --- a/sdkclient/tx/gov.go +++ b/sdkclient/tx/gov.go @@ -1,34 +1,18 @@ package tx import ( + "time" + sdk "github.com/cosmos/cosmos-sdk/types" + v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" proposal "github.com/cosmos/cosmos-sdk/x/params/types/proposal" ) -func (c *Client) GovVoteYes(proposalID uint64) (*sdk.TxResponse, error) { - voter, err := c.keyringRecord.GetAddress() - if err != nil { - return nil, err - } - - voteType, err := govtypes.VoteOptionFromString("VOTE_OPTION_YES") - if err != nil { - return nil, err - } - - msg := govtypes.NewMsgVote( - voter, - proposalID, - voteType, - ) - return c.BroadcastTx(msg) -} - func (c *Client) GovParamChange(title, description string, changes []proposal.ParamChange, deposit sdk.Coins, ) (*sdk.TxResponse, error) { content := proposal.NewParameterChangeProposal(title, description, changes) - fromAddr, err := c.keyringRecord.GetAddress() + fromAddr, err := c.keyringRecord[0].GetAddress() if err != nil { return nil, err } @@ -47,7 +31,7 @@ func (c *Client) GovSubmitProposal(changes []proposal.ParamChange, deposit sdk.C changes, ) - fromAddr, err := c.keyringRecord.GetAddress() + fromAddr, err := c.keyringRecord[0].GetAddress() if err != nil { return nil, err } @@ -58,3 +42,61 @@ func (c *Client) GovSubmitProposal(changes []proposal.ParamChange, deposit sdk.C return c.BroadcastTx(msg) } + +func (c *Client) TxSubmitProposalWithMsg(msgs []sdk.Msg) (*sdk.TxResponse, error) { + deposit, err := sdk.ParseCoinsNormalized("1000uumee") + if err != nil { + return nil, err + } + + fromAddr, err := c.keyringRecord[0].GetAddress() + if err != nil { + return nil, err + } + + submitProposal, err := v1.NewMsgSubmitProposal(msgs, deposit, fromAddr.String(), "") + if err != nil { + return nil, err + } + + return c.BroadcastTx(submitProposal) +} + +// TxGovVoteYesAll creates transactions (one for each registered account) to approve a given proposal. +func (c *Client) TxGovVoteYesAll(proposalID uint64) error { + for index := range c.keyringRecord { + voter, err := c.keyringRecord[index].GetAddress() + if err != nil { + return err + } + + voteType, err := govtypes.VoteOptionFromString("VOTE_OPTION_YES") + if err != nil { + return err + } + + msg := govtypes.NewMsgVote( + voter, + proposalID, + voteType, + ) + + c.ClientContext.From = c.keyringRecord[index].Name + c.ClientContext.FromName = c.keyringRecord[index].Name + c.ClientContext.FromAddress, _ = c.keyringRecord[index].GetAddress() + + for retry := 0; retry < 5; retry++ { + // retry if txs fails, because sometimes account sequence mismatch occurs due to txs pending + if _, err = BroadcastTx(*c.ClientContext, *c.txFactory, []sdk.Msg{msg}...); err == nil { + break + } + time.Sleep(time.Second * 1) + } + + if err != nil { + return err + } + } + + return nil +} diff --git a/sdkclient/tx/key.go b/sdkclient/tx/key.go index ec9ddbac98..aea24dd4c3 100644 --- a/sdkclient/tx/key.go +++ b/sdkclient/tx/key.go @@ -1,7 +1,6 @@ package tx import ( - "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/crypto/hd" "github.com/cosmos/cosmos-sdk/crypto/keyring" sdk "github.com/cosmos/cosmos-sdk/types" @@ -12,22 +11,17 @@ const ( keyringAppName = "testnet" ) -func CreateAccountFromMnemonic(name, mnemonic string, cdc codec.Codec) (*keyring.Record, keyring.Keyring, error) { - kb, err := keyring.New(keyringAppName, keyring.BackendTest, "", nil, cdc) - if err != nil { - return nil, nil, err - } - +func CreateAccountFromMnemonic(kb keyring.Keyring, name, mnemonic string) (*keyring.Record, error) { keyringAlgos, _ := kb.SupportedAlgorithms() algo, err := keyring.NewSigningAlgoFromString(string(hd.Secp256k1Type), keyringAlgos) if err != nil { - return nil, nil, err + return nil, err } account, err := kb.NewAccount(name, mnemonic, "", sdk.FullFundraiserPath, algo) if err != nil { - return nil, nil, err + return nil, err } - return account, kb, nil + return account, nil } diff --git a/tests/e2e/e2e_ibc_test.go b/tests/e2e/e2e_ibc_test.go index 2c6185ee85..a8e4926c7d 100644 --- a/tests/e2e/e2e_ibc_test.go +++ b/tests/e2e/e2e_ibc_test.go @@ -7,6 +7,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" appparams "github.com/umee-network/umee/v4/app/params" + "github.com/umee-network/umee/v4/tests/grpc" + "github.com/umee-network/umee/v4/x/uibc" ) var powerReduction = sdk.MustNewDecFromStr("10").Power(6) @@ -157,11 +159,38 @@ func (s *IntegrationTestSuite) TestIBCTokenTransfer() { 5*time.Second, ) - // after reset sending again tokens from umee to gaia - // send 100UMEE from umee to gaia - // Note: receiver is null means hermes will default send to key_name (from config) of target chain (gaia) + /**** + IBC_Status : disble (making ibc_transfer quota check disabled) + No Outflows will updated + ***/ + // Make gov proposal to disable the quota check on ibc-transfer + err = grpc.UIBCIBCTransferSatusUpdate( + s.umee, + uibc.IBCTransferStatus_IBC_TRANSFER_STATUS_QUOTA_DISABLED, + ) + s.Require().NoError(err) + // Get the uibc params for quota checking + uibcParams, err := s.umee.QueryUIBCParams() + s.Require().NoError(err) + s.Require().Equal(uibcParams.IbcStatus, uibc.IBCTransferStatus_IBC_TRANSFER_STATUS_QUOTA_DISABLED) + token = sdk.NewInt64Coin("uumee", 100000000) // 100 Umee + // sending the umee tokens s.sendIBC(s.chain.id, gaiaChainID, "", token) + // Check the outflows s.checkSupply(gaiaAPIEndpoint, umeeIBCHash, token.Amount) + s.Require().Eventually( + func() bool { + a, err := queryOutflows(umeeAPIEndpoint, appparams.BondDenom) + s.Require().NoError(err) + return a.Equal(sdk.ZeroDec()) + }, + time.Minute, + 5*time.Second, + ) + // resend the umee token from gaia to umee + s.sendIBC(gaiaChainID, s.chain.id, "", sdk.NewInt64Coin(umeeIBCHash, token.Amount.Int64())) + s.checkSupply(gaiaAPIEndpoint, umeeIBCHash, sdk.ZeroInt()) + }) // Non registered tokens (not exists in oracle for quota test) diff --git a/tests/e2e/e2e_setup_test.go b/tests/e2e/e2e_setup_test.go index 00d9d34051..3415a08e68 100644 --- a/tests/e2e/e2e_setup_test.go +++ b/tests/e2e/e2e_setup_test.go @@ -35,6 +35,7 @@ import ( govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" govtypesv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + "github.com/umee-network/umee/v4/app" appparams "github.com/umee-network/umee/v4/app/params" "github.com/umee-network/umee/v4/client" "github.com/umee-network/umee/v4/x/leverage/fixtures" @@ -302,6 +303,7 @@ func (s *IntegrationTestSuite) initGenesis() { votingPeroid := 5 * time.Second govGenState.VotingParams.VotingPeriod = &votingPeroid + govGenState.DepositParams.MinDeposit = sdk.NewCoins(sdk.NewCoin(appparams.BondDenom, sdk.NewInt(100))) bz, err = cdc.MarshalJSON(&govGenState) s.Require().NoError(err) @@ -1077,13 +1079,17 @@ func (s *IntegrationTestSuite) runPriceFeeder() { func (s *IntegrationTestSuite) initUmeeClient() { var err error - ecfg := appparams.MakeEncodingConfig() + mnemonics := make([]string, 0) + for _, v := range s.chain.validators { + mnemonics = append(mnemonics, v.mnemonic) + } + ecfg := app.MakeEncodingConfig() + s.umee, err = client.NewClient( s.chain.id, "tcp://localhost:26657", "tcp://localhost:9090", - "val1", - s.chain.validators[0].mnemonic, + mnemonics, 1, ecfg, ) diff --git a/tests/e2e/e2e_test.go b/tests/e2e/e2e_test.go index 5dbd0e0bd3..f116fe1709 100644 --- a/tests/e2e/e2e_test.go +++ b/tests/e2e/e2e_test.go @@ -77,7 +77,6 @@ func (s *IntegrationTestSuite) TestMedians() { } func (s *IntegrationTestSuite) TestUpdateOracleParams() { - s.T().Skip("paused due to validator power threshold enforcing") params, err := s.umee.QueryOracleParams() s.Require().NoError(err) diff --git a/tests/grpc/gov.go b/tests/grpc/gov.go index 9e499c0195..b75c97e8ae 100644 --- a/tests/grpc/gov.go +++ b/tests/grpc/gov.go @@ -6,8 +6,12 @@ import ( "time" sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + gtypes "github.com/cosmos/cosmos-sdk/x/gov/types" proposal "github.com/cosmos/cosmos-sdk/x/params/types/proposal" + "github.com/umee-network/umee/v4/client" + "github.com/umee-network/umee/v4/x/uibc" ) var govDeposit sdk.Coins @@ -26,6 +30,54 @@ func SubmitAndPassProposal(umee client.Client, changes []proposal.ParamChange) e return err } + return MakeVoteAndCheckProposal(umee, *resp) +} + +func OracleParamChanges( + historicStampPeriod uint64, + maximumPriceStamps uint64, + medianStampPeriod uint64, +) []proposal.ParamChange { + return []proposal.ParamChange{ + { + Subspace: "oracle", + Key: "HistoricStampPeriod", + Value: fmt.Sprintf("\"%d\"", historicStampPeriod), + }, + { + Subspace: "oracle", + Key: "MaximumPriceStamps", + Value: fmt.Sprintf("\"%d\"", maximumPriceStamps), + }, + { + Subspace: "oracle", + Key: "MedianStampPeriod", + Value: fmt.Sprintf("\"%d\"", medianStampPeriod), + }, + } +} + +func UIBCIBCTransferSatusUpdate(umeeClient client.Client, status uibc.IBCTransferStatus) error { + msg := uibc.MsgGovSetIBCStatus{ + Authority: authtypes.NewModuleAddress(gtypes.ModuleName).String(), + Title: "Update the ibc transfer status", + Description: "Update the ibc transfer status", + IbcStatus: status, + } + + var err error + for retry := 0; retry < 5; retry++ { + // retry if txs fails, because sometimes account sequence mismatch occurs due to txs pending + if resp, err := umeeClient.Tx.TxSubmitProposalWithMsg([]sdk.Msg{&msg}); err == nil { + return MakeVoteAndCheckProposal(umeeClient, *resp) + } + time.Sleep(time.Second * 1) + } + + return err +} + +func MakeVoteAndCheckProposal(umeeClient client.Client, resp sdk.TxResponse) error { var proposalID string for _, event := range resp.Logs[0].Events { if event.Type == "submit_proposal" { @@ -46,12 +98,12 @@ func SubmitAndPassProposal(umee client.Client, changes []proposal.ParamChange) e return err } - _, err = umee.Tx.GovVoteYes(proposalIDInt) + err = umeeClient.Tx.TxGovVoteYesAll(proposalIDInt) if err != nil { return err } - prop, err := umee.Query.GovProposal(proposalIDInt) + prop, err := umeeClient.GovProposal(proposalIDInt) if err != nil { return err } @@ -61,7 +113,7 @@ func SubmitAndPassProposal(umee client.Client, changes []proposal.ParamChange) e fmt.Printf("sleeping %s until end of voting period + 1 block\n", sleepDuration) time.Sleep(sleepDuration) - prop, err = umee.Query.GovProposal(proposalIDInt) + prop, err = umeeClient.GovProposal(proposalIDInt) if err != nil { return err } @@ -72,27 +124,3 @@ func SubmitAndPassProposal(umee client.Client, changes []proposal.ParamChange) e } return nil } - -func OracleParamChanges( - historicStampPeriod uint64, - maximumPriceStamps uint64, - medianStampPeriod uint64, -) []proposal.ParamChange { - return []proposal.ParamChange{ - { - Subspace: "oracle", - Key: "HistoricStampPeriod", - Value: fmt.Sprintf("\"%d\"", historicStampPeriod), - }, - { - Subspace: "oracle", - Key: "MaximumPriceStamps", - Value: fmt.Sprintf("\"%d\"", maximumPriceStamps), - }, - { - Subspace: "oracle", - Key: "MedianStampPeriod", - Value: fmt.Sprintf("\"%d\"", medianStampPeriod), - }, - } -}