From 6f63ea22b32b7b70d5514a6d9e5cc97e1a4b6db7 Mon Sep 17 00:00:00 2001 From: matthiasmatt Date: Sun, 24 Jul 2022 16:35:25 -0300 Subject: [PATCH 01/28] feat: Gasless txs for post price and liquidate --- app/ante.go | 15 +++++++ app/antedecorators/gasless.go | 83 +++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 app/antedecorators/gasless.go diff --git a/app/ante.go b/app/ante.go index c149854e6..27ee1cc01 100644 --- a/app/ante.go +++ b/app/ante.go @@ -6,11 +6,18 @@ import ( "github.com/cosmos/cosmos-sdk/x/auth/ante" ibcante "github.com/cosmos/ibc-go/v3/modules/core/ante" ibckeeper "github.com/cosmos/ibc-go/v3/modules/core/keeper" + + "github.com/NibiruChain/nibiru/app/antedecorators" + perpkeeper "github.com/NibiruChain/nibiru/x/perp/keeper" + pricefeedkeeper "github.com/NibiruChain/nibiru/x/pricefeed/keeper" ) type AnteHandlerOptions struct { ante.HandlerOptions IBCKeeper *ibckeeper.Keeper + + PricefeedKeeper *pricefeedkeeper.Keeper + PerpKeeper *perpkeeper.Keeper } /* NewAnteHandler returns and AnteHandler that checks and increments sequence @@ -32,9 +39,17 @@ func NewAnteHandler(options AnteHandlerOptions) (sdk.AnteHandler, error) { if options.IBCKeeper == nil { return nil, sdkerrors.Wrap(sdkerrors.ErrLogic, "ibc keeper is required for AnteHandler") } + if options.PricefeedKeeper == nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrLogic, "pricefeed keeper is required for ante builder") + } + if options.PerpKeeper == nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrLogic, "perp keeper is required for ante builder") + } + memPoolDecorator := ante.NewMempoolFeeDecorator() anteDecorators := []sdk.AnteDecorator{ ante.NewSetUpContextDecorator(), + antedecorators.NewGaslessDecorator([]sdk.AnteDecorator{&memPoolDecorator}, *options.PricefeedKeeper, *options.PerpKeeper), ante.NewRejectExtensionOptionsDecorator(), ante.NewMempoolFeeDecorator(), ante.NewValidateBasicDecorator(), diff --git a/app/antedecorators/gasless.go b/app/antedecorators/gasless.go new file mode 100644 index 000000000..d567d2af8 --- /dev/null +++ b/app/antedecorators/gasless.go @@ -0,0 +1,83 @@ +package antedecorators + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + perpkeeper "github.com/NibiruChain/nibiru/x/perp/keeper" + perptypes "github.com/NibiruChain/nibiru/x/perp/types" + pricefeedkeeper "github.com/NibiruChain/nibiru/x/pricefeed/keeper" + pricefeedtypes "github.com/NibiruChain/nibiru/x/pricefeed/types" +) + +type GaslessDecorator struct { + wrapped []sdk.AnteDecorator + pricefeedKeeper pricefeedkeeper.Keeper + perpKeeper perpkeeper.Keeper +} + +func NewGaslessDecorator(wrapped []sdk.AnteDecorator, pricefeedKeeper pricefeedkeeper.Keeper, perpKeeper perpkeeper.Keeper) GaslessDecorator { + return GaslessDecorator{wrapped: wrapped, pricefeedKeeper: pricefeedKeeper, perpKeeper: perpKeeper} +} + +func (gd GaslessDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { + if !isTxGasless(tx, ctx, gd.pricefeedKeeper, gd.perpKeeper) { + // if not gasless, then we use the wrappers + + // AnteHandle always takes a `next` so we need a no-op to execute only one handler at a time + terminatorHandler := func(ctx sdk.Context, _ sdk.Tx, _ bool) (sdk.Context, error) { + return ctx, nil + } + // iterating instead of recursing the handler for readability + // we use blank here because we shouldn't handle the error + for _, handler := range gd.wrapped { + ctx, _ = handler.AnteHandle(ctx, tx, simulate, terminatorHandler) + } + return next(ctx, tx, simulate) + } + gaslessMeter := sdk.NewInfiniteGasMeter() + + return next(ctx.WithGasMeter(gaslessMeter), tx, simulate) +} + +func isTxGasless(tx sdk.Tx, ctx sdk.Context, pricefeedKeeper pricefeedkeeper.Keeper, perpKeeper perpkeeper.Keeper) bool { + if len(tx.GetMsgs()) == 0 { + // empty TX shouldn't be gasless + return false + } + for _, msg := range tx.GetMsgs() { + switch m := msg.(type) { + case *pricefeedtypes.MsgPostPrice: + if pricefeedPostPriceIsGasless(m, ctx, pricefeedKeeper) { + continue + } + return false + case *perptypes.MsgLiquidate: + if liquidateIsGasless(m, ctx, perpKeeper) { + continue + } + return false + default: + return false + } + } + return true +} + +func pricefeedPostPriceIsGasless(msg *pricefeedtypes.MsgPostPrice, ctx sdk.Context, keeper pricefeedkeeper.Keeper) bool { + return true + // valAddr, err := sdk.AccAddressFromBech32(msg.Oracle) + // if err != nil { + // return false + // } + + // pair := common.AssetPair{Token0: msg.Token0, Token1: msg.Token1} + // return keeper.IsWhitelistedOracle(ctx, pair.String(), valAddr) +} + +func liquidateIsGasless(msg *perptypes.MsgLiquidate, ctx sdk.Context, keeper perpkeeper.Keeper) bool { + _, err := sdk.AccAddressFromBech32(msg.Sender) + if err != nil { + return false + } + return true // TODO: check if within whitelist for liquidators +} From caa2addfb536bae45e1df1871b9e069349f8e97b Mon Sep 17 00:00:00 2001 From: matthiasmatt Date: Mon, 25 Jul 2022 12:51:54 -0300 Subject: [PATCH 02/28] fix: Fix anteHandler call to include keepers --- app/app.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/app.go b/app/app.go index 9ec4682a9..5fd3f6ce3 100644 --- a/app/app.go +++ b/app/app.go @@ -749,7 +749,9 @@ func NewNibiruApp( SignModeHandler: encodingConfig.TxConfig.SignModeHandler(), SigGasConsumer: ante.DefaultSigVerificationGasConsumer, }, - IBCKeeper: app.IBCKeeper, + PricefeedKeeper: &app.PricefeedKeeper, + PerpKeeper: &app.PerpKeeper, + IBCKeeper: app.IBCKeeper, }) if err != nil { From e452d55814c2188c1b5c9c2dcafbd2621d148a2b Mon Sep 17 00:00:00 2001 From: matthiasmatt Date: Mon, 25 Jul 2022 12:57:59 -0300 Subject: [PATCH 03/28] fix: Update changelog and lint fix --- CHANGELOG.md | 1 + app/antedecorators/gasless.go | 5 +---- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 332b09787..16315f97a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -59,6 +59,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * [#730](https://github.com/NibiruChain/nibiru/pull/730) - update localnet script. * [#736](https://github.com/NibiruChain/nibiru/pull/736) - Bumps [github.com/spf13/cast](https://github.com/spf13/cast) from 1.4.1 to 1.5.0 * [#735](https://github.com/NibiruChain/nibiru/pull/735) - Bump github.com/spf13/cobra from 1.4.0 to 1.5.0 +* [#738](https://github.com/NibiruChain/nibiru/pull/738) - Make post prices transactions gasless for whitelisted oracles ### API Breaking diff --git a/app/antedecorators/gasless.go b/app/antedecorators/gasless.go index d567d2af8..07054e542 100644 --- a/app/antedecorators/gasless.go +++ b/app/antedecorators/gasless.go @@ -76,8 +76,5 @@ func pricefeedPostPriceIsGasless(msg *pricefeedtypes.MsgPostPrice, ctx sdk.Conte func liquidateIsGasless(msg *perptypes.MsgLiquidate, ctx sdk.Context, keeper perpkeeper.Keeper) bool { _, err := sdk.AccAddressFromBech32(msg.Sender) - if err != nil { - return false - } - return true // TODO: check if within whitelist for liquidators + return err == nil // TODO: check if within whitelist for liquidators } From 21f1eed702659819e11edf9b81ed31a02fd5c61f Mon Sep 17 00:00:00 2001 From: matthiasmatt Date: Wed, 27 Jul 2022 12:43:45 -0300 Subject: [PATCH 04/28] fix: merge with master --- app/ante.go | 4 +-- app/antedecorators/gasless_test.go | 58 ++++++++++++++++++++++++++++++ x/pricefeed/client/cli/cli_test.go | 1 + 3 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 app/antedecorators/gasless_test.go diff --git a/app/ante.go b/app/ante.go index 27ee1cc01..267f23e43 100644 --- a/app/ante.go +++ b/app/ante.go @@ -49,9 +49,9 @@ func NewAnteHandler(options AnteHandlerOptions) (sdk.AnteHandler, error) { memPoolDecorator := ante.NewMempoolFeeDecorator() anteDecorators := []sdk.AnteDecorator{ ante.NewSetUpContextDecorator(), - antedecorators.NewGaslessDecorator([]sdk.AnteDecorator{&memPoolDecorator}, *options.PricefeedKeeper, *options.PerpKeeper), ante.NewRejectExtensionOptionsDecorator(), - ante.NewMempoolFeeDecorator(), + antedecorators.NewGaslessDecorator([]sdk.AnteDecorator{&memPoolDecorator}, *options.PricefeedKeeper, *options.PerpKeeper), + //ante.NewMempoolFeeDecorator(), ante.NewValidateBasicDecorator(), ante.NewTxTimeoutHeightDecorator(), ante.NewValidateMemoDecorator(options.AccountKeeper), diff --git a/app/antedecorators/gasless_test.go b/app/antedecorators/gasless_test.go new file mode 100644 index 000000000..a7874bc8d --- /dev/null +++ b/app/antedecorators/gasless_test.go @@ -0,0 +1,58 @@ +package antedecorators_test + +import ( + "fmt" + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + + "github.com/NibiruChain/nibiru/app/antedecorators" + + perpkeeper "github.com/NibiruChain/nibiru/x/perp/keeper" + pricefeedkeeper "github.com/NibiruChain/nibiru/x/pricefeed/keeper" +) + +var output = "" + +type FakeAnteDecoratorOne struct{} + +func (ad FakeAnteDecoratorOne) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { + output = fmt.Sprintf("%sone", output) + return next(ctx, tx, simulate) +} + +type FakeAnteDecoratorTwo struct{} + +func (ad FakeAnteDecoratorTwo) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { + output = fmt.Sprintf("%stwo", output) + return next(ctx, tx, simulate) +} + +type FakeAnteDecoratorThree struct{} + +func (ad FakeAnteDecoratorThree) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { + output = fmt.Sprintf("%sthree", output) + return next(ctx, tx, simulate) +} + +type FakeTx struct{} + +func (tx FakeTx) GetMsgs() []sdk.Msg { + return []sdk.Msg{} +} + +func (tx FakeTx) ValidateBasic() error { + return nil +} + +func TestGaslessDecorator(t *testing.T) { + anteDecorators := []sdk.AnteDecorator{ + FakeAnteDecoratorOne{}, + antedecorators.NewGaslessDecorator([]sdk.AnteDecorator{FakeAnteDecoratorTwo{}}, pricefeedkeeper.Keeper{}, perpkeeper.Keeper{}), + FakeAnteDecoratorThree{}, + } + chainedHandler := sdk.ChainAnteDecorators(anteDecorators...) + chainedHandler(sdk.Context{}, FakeTx{}, false) + require.Equal(t, "onetwothree", output) +} diff --git a/x/pricefeed/client/cli/cli_test.go b/x/pricefeed/client/cli/cli_test.go index e4d9b0d99..4c50d8a06 100644 --- a/x/pricefeed/client/cli/cli_test.go +++ b/x/pricefeed/client/cli/cli_test.go @@ -611,6 +611,7 @@ func (s IntegrationTestSuite) TestX_CmdAddOracleProposalAndVote() { cmd := cli.CmdAddOracleProposal() flags.AddTxFlagsToCmd(cmd) out, err := sdktestutilcli.ExecTestCLICmd(clientCtx, cmd, args) + fmt.Println(out) s.Require().NoError(err) s.Assert().NotContains(out.String(), "fail") var txRespProtoMessage proto.Message = &sdk.TxResponse{} From 471efa591559afdc7834491e239067c371333d40 Mon Sep 17 00:00:00 2001 From: Unique-Divine Date: Wed, 27 Jul 2022 13:50:15 -0500 Subject: [PATCH 05/28] fix,docs: vpool gov cli test --- x/perp/spec/01_concepts.md | 15 +++++++------- x/vpool/client/cli/cli_test.go | 37 ++++++++++------------------------ 2 files changed, 18 insertions(+), 34 deletions(-) diff --git a/x/perp/spec/01_concepts.md b/x/perp/spec/01_concepts.md index cbbf8d058..f3f7b3d14 100644 --- a/x/perp/spec/01_concepts.md +++ b/x/perp/spec/01_concepts.md @@ -46,11 +46,11 @@ position_size = baseReserves - baseReservesAfterSwap The notional value of the position, or **position notional**, is the total value a position controls in units of the quote asset. Notional value expresses the value a derivatives contract theoretically controls. On Nibiru, it is defined more concretely by ```go -positionNotional = abs(quoteReserves - k/(baseReserves + position_size)) +positionNotional = abs(quoteReserves - k / (baseReserves + position_size)) leverage = positionNotional / margin. ``` -Let's say that the mark price of ether is $3000 in our previous example. This implies that the trader with a long position of size 5 has a position notional of $15,000. And if the trader has 10x **leverage**, for example, she must have put down $1500 as margin (collateral backing the position). +Let's say that the mark price of ether is \$3000 in our previous example. This implies that the trader with a long position of size 5 has a position notional of \$15,000. And if the trader has 10x **leverage**, for example, she must have put down \$1500 as margin (collateral backing the position). ## Margin and Margin Ratio @@ -66,7 +66,7 @@ Here, `unrealizedPnL` is computed using either the mark price or the 15 minute T When the virtual price is not within the spread tolerance to the index price, the margin ratio used is the highest value between a calculation with the index price (oracle based on underlying) and the mark price (derivative price). -Another good way to think about margin ratio is as the inverse of a position's effective leverage. I.e. if a trader puts down $100 as margin with 5x leverage, the notional is $500 and the margin ratio is 20%, which is equivalent ot `1 / leverage`. +Another good way to think about margin ratio is as the inverse of a position's effective leverage. I.e. if a trader puts down $100 as margin with 5x leverage, the notional is \$500 and the margin ratio is 20%, which is equivalent ot `1 / leverage`. #### Cross Margin versus Isolated Margin @@ -91,17 +91,16 @@ Perpetual contracts rely on a scheduled payment between longs and shorts known a Longs and shorts are paid with the exact funding rate formula [used by FTX](https://help.ftx.com/hc/en-us/articles/360027946571-Funding). Realized and unrealized funding payments are updated every block directly on each position. Global funding calculations are recorded in a time-weighted fashion, where the **funding rate** is the difference between the mark TWAP and index TWAP divided by the number of funding payments per day: -```python -funding_rate -= (mark_TWAP - index_TWAP) / funding_payments_per_day +```go +fundingRate = (markTWAP - indexTWAP) / fundingPaymentsPerDay ``` In the initial version of Nibi-Perps, these payments will occur every half-hour, implying a `funding_payments_per_day` value of 48. This setup is analogous to a traditional future that expires once a day. If a perp trades consistently at 2% above its underlying index price, the funding payments would amount to 2% of the position size after a full day. If the funding rate is positive, mark price > index price and longs pay shorts. Nibi-Perps automatically deducts the funding payment amount from the margin of the long positions. -```python -funding_payment = position_size * funding_rate +```go +fundingPayment = positionSize * fundingRate ``` Here, position size refers to amount of base asset represented by the derivative. I.e., a BTC:USD perp with 7 BTC of exposure would have a position size of 7. diff --git a/x/vpool/client/cli/cli_test.go b/x/vpool/client/cli/cli_test.go index ba6c1e77b..e9a8cb7c3 100644 --- a/x/vpool/client/cli/cli_test.go +++ b/x/vpool/client/cli/cli_test.go @@ -8,8 +8,6 @@ import ( "time" "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/crypto/hd" - "github.com/cosmos/cosmos-sdk/crypto/keyring" sdktestutil "github.com/cosmos/cosmos-sdk/testutil" sdktestutilcli "github.com/cosmos/cosmos-sdk/testutil/cli" sdk "github.com/cosmos/cosmos-sdk/types" @@ -27,14 +25,14 @@ import ( vpooltypes "github.com/NibiruChain/nibiru/x/vpool/types" ) -type IntegrationTestSuite struct { +type VpoolCLISuite struct { suite.Suite cfg testutilcli.Config network *testutilcli.Network } -func (s *IntegrationTestSuite) SetupSuite() { +func (s *VpoolCLISuite) SetupSuite() { if testing.Short() { s.T().Skip("skipping integration test suite") } @@ -61,29 +59,16 @@ func (s *IntegrationTestSuite) SetupSuite() { s.Assert().Equal(sdk.NewDec(10), res.Price.Price) } -func (s *IntegrationTestSuite) TearDownSuite() { +func (s *VpoolCLISuite) TearDownSuite() { s.T().Log("tearing down integration test suite") s.network.Cleanup() } -func (s IntegrationTestSuite) TestX_CmdAddVpool() { +func (s VpoolCLISuite) TestX_CmdAddVpool() { + s.Require().Len(s.network.Validators, 1) val := s.network.Validators[0] clientCtx := val.ClientCtx.WithOutputFormat("json") - proposer, _, err := val.ClientCtx.Keyring.NewMnemonic( - /* uid */ "proposer", - /* language */ keyring.English, - /* hdPath */ sdk.FullFundraiserPath, - /* bip39Passphrase */ "", - /* algo */ hd.Secp256k1, - ) - s.Require().NoError(err) - - s.T().Log("Fill proposer wallet to pay gas for prosal") - gasTokens := sdk.NewCoins(sdk.NewInt64Coin(s.cfg.BondDenom, 100_000_000)) - oracle := sdk.AccAddress(proposer.GetPubKey().Address()) - _, err = testutilcli.FillWalletFromValidator(oracle, gasTokens, val, s.cfg.BondDenom) - s.Require().NoError(err) s.T().Log("load example json as bytes") proposal := &vpooltypes.CreatePoolProposal{ @@ -131,7 +116,7 @@ func (s IntegrationTestSuite) TestX_CmdAddVpool() { s.Assert().EqualValues(0, txResp.Code, out.String()) s.T().Log(`Check that proposal was correctly submitted with gov client - $ nibid query gov proposal 1`) + $ nibid query gov proposal 2`) // the proposal tx won't be included until next block s.Assert().NoError(s.network.WaitForNextBlock()) govQueryClient := govtypes.NewQueryClient(clientCtx) @@ -153,11 +138,12 @@ func (s IntegrationTestSuite) TestX_CmdAddVpool() { s.T().Log(`Move proposal to vote status by meeting min deposit $ nibid tx gov deposit [proposal-id] [deposit] [flags]`) + expectedProposalIDStr := "1" govDepositParams, err := govQueryClient.Params( context.Background(), &govtypes.QueryParamsRequest{ParamsType: govtypes.ParamDeposit}) s.Assert().NoError(err) args = []string{ - /*proposal-id=*/ "1", + /*proposal-id=*/ expectedProposalIDStr, /*deposit=*/ govDepositParams.DepositParams.MinDeposit.String(), fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), fmt.Sprintf("--%s=test", flags.FlagKeyringBackend), @@ -180,7 +166,7 @@ func (s IntegrationTestSuite) TestX_CmdAddVpool() { $ nibid tx gov vote [proposal-id] [option] [flags] e.g. $ nibid tx gov vote 1 yes`) args = []string{ - /*proposal-id=*/ "1", + /*proposal-id=*/ expectedProposalIDStr, /*option=*/ "yes", fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), fmt.Sprintf("--%s=test", flags.FlagKeyringBackend), @@ -223,10 +209,9 @@ func (s IntegrationTestSuite) TestX_CmdAddVpool() { found = true } } - require.True(s.T(), found, "pool does not exist") } -func TestIntegrationTestSuite(t *testing.T) { - suite.Run(t, new(IntegrationTestSuite)) +func TestVpoolCLISuite(t *testing.T) { + suite.Run(t, new(VpoolCLISuite)) } From c7717db81936786822b60d6943e86084c2c05358 Mon Sep 17 00:00:00 2001 From: Unique-Divine Date: Wed, 27 Jul 2022 14:18:38 -0500 Subject: [PATCH 06/28] docs(vpool,pricefeed): readadbility and cli commenets for the gov cli tests --- x/pricefeed/client/cli/cli_test.go | 30 ++++++++++++++++++++++++------ x/vpool/client/cli/cli_test.go | 16 ++++++++++++++-- 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/x/pricefeed/client/cli/cli_test.go b/x/pricefeed/client/cli/cli_test.go index 4c50d8a06..79b4cbea0 100644 --- a/x/pricefeed/client/cli/cli_test.go +++ b/x/pricefeed/client/cli/cli_test.go @@ -570,7 +570,9 @@ func (s IntegrationTestSuite) TestX_CmdAddOracleProposalAndVote() { _, err = testutilcli.FillWalletFromValidator(oracle, gasTokens, val, s.cfg.BondDenom) s.Require().NoError(err) - s.T().Log("load example json as bytes") + // ---------------------------------------------------------------------- + s.T().Log("load example proposal json as bytes") + // ---------------------------------------------------------------------- proposal := &pricefeedtypes.AddOracleProposal{ Title: "Cataclysm-004", Description: "Whitelists Delphi to post prices for OHM and BTC", @@ -593,14 +595,19 @@ func (s IntegrationTestSuite) TestX_CmdAddOracleProposalAndVote() { contents, err := ioutil.ReadFile(proposalJSON.Name()) s.Assert().NoError(err) + // ---------------------------------------------------------------------- s.T().Log("Unmarshal json bytes into proposal object; check validity") + // ---------------------------------------------------------------------- encodingConfig := simappparams.MakeTestEncodingConfig() proposal = &pricefeedtypes.AddOracleProposal{} err = encodingConfig.Marshaler.UnmarshalJSON(contents, proposal) s.Assert().NoError(err) s.Require().NoError(proposal.Validate()) - s.T().Log("Submit proposal and unmarshal tx response") + // ---------------------------------------------------------------------- + s.T().Log(`Submit proposal and unmarshal tx response + $ nibid tx gov submit-proposal add-oracle [proposal-json] --deposit=[deposit] [flags]`) + // ---------------------------------------------------------------------- args := []string{ proposalJSON.Name(), fmt.Sprintf("--%s=1000unibi", govcli.FlagDeposit), @@ -623,8 +630,11 @@ func (s IntegrationTestSuite) TestX_CmdAddOracleProposalAndVote() { s.Assert().NoError(err) s.Assert().EqualValues(0, txResp.Code, out.String()) + // ---------------------------------------------------------------------- s.T().Log(`Check that proposal was correctly submitted with gov client - $ nibid query gov proposal 1`) + $ nibid query gov proposal 1 + `) + // ---------------------------------------------------------------------- // the proposal tx won't be included until next block s.Assert().NoError(s.network.WaitForNextBlock()) govQueryClient := govtypes.NewQueryClient(clientCtx) @@ -644,8 +654,10 @@ func (s IntegrationTestSuite) TestX_CmdAddOracleProposalAndVote() { proposalsQueryResponse.Proposals[0].TotalDeposit, ) + // ---------------------------------------------------------------------- s.T().Log(`Move proposal to vote status by meeting min deposit - $ nibid tx gov deposit [proposal-id] [deposit] [flags]`) + $ nibid tx gov deposit [proposal-id] [deposit] [flags]`) + // ---------------------------------------------------------------------- govDepositParams, err := govQueryClient.Params( context.Background(), &govtypes.QueryParamsRequest{ParamsType: govtypes.ParamDeposit}) s.Assert().NoError(err) @@ -669,9 +681,11 @@ func (s IntegrationTestSuite) TestX_CmdAddOracleProposalAndVote() { proposalsQueryResponse.Proposals[0].Status, "proposal should be in voting period since min deposit has been met") + // ---------------------------------------------------------------------- s.T().Log(`Vote on the proposal. - $ nibid tx gov vote [proposal-id] [option] [flags] - e.g. $ nibid tx gov vote 1 yes`) + $ nibid tx gov vote [proposal-id] [option] [flags] + For example, $ nibid tx gov vote 1 yes`) + // ---------------------------------------------------------------------- args = []string{ /*proposal-id=*/ "1", /*option=*/ "yes", @@ -695,7 +709,9 @@ func (s IntegrationTestSuite) TestX_CmdAddOracleProposalAndVote() { }, 20*time.Second, 2*time.Second, "proposal should pass after voting period") + // ---------------------------------------------------------------------- s.T().Log("verify that the new proposed pairs have been added to the params") + // ---------------------------------------------------------------------- cmd = cli.CmdQueryParams() args = []string{} queryResp := &pricefeedtypes.QueryParamsResponse{} @@ -704,7 +720,9 @@ func (s IntegrationTestSuite) TestX_CmdAddOracleProposalAndVote() { expectedPairs := append(pricefeedtypes.DefaultPairs, proposalPairs...) s.Assert().EqualValues(expectedPairs, queryResp.Params.Pairs) + // ---------------------------------------------------------------------- s.T().Log("verify that the oracle was whitelisted with a query") + // ---------------------------------------------------------------------- cmd = cli.CmdQueryOracles() for _, pair := range proposalPairs { args = []string{pair.String()} diff --git a/x/vpool/client/cli/cli_test.go b/x/vpool/client/cli/cli_test.go index e9a8cb7c3..8ddb876f5 100644 --- a/x/vpool/client/cli/cli_test.go +++ b/x/vpool/client/cli/cli_test.go @@ -70,7 +70,9 @@ func (s VpoolCLISuite) TestX_CmdAddVpool() { val := s.network.Validators[0] clientCtx := val.ClientCtx.WithOutputFormat("json") - s.T().Log("load example json as bytes") + // ---------------------------------------------------------------------- + s.T().Log("load example proposal json as bytes") + // ---------------------------------------------------------------------- proposal := &vpooltypes.CreatePoolProposal{ Title: "Create ETH:USD pool", Description: "Creates an ETH:USD pool", @@ -93,7 +95,9 @@ func (s VpoolCLISuite) TestX_CmdAddVpool() { val.ClientCtx.Codec.MustUnmarshalJSON(contents, proposal) s.Require().NoError(proposal.ValidateBasic()) + // ---------------------------------------------------------------------- s.T().Log("Submit proposal and unmarshal tx response") + // ---------------------------------------------------------------------- args := []string{ proposalJSON.Name(), fmt.Sprintf("--%s=1000unibi", govcli.FlagDeposit), @@ -115,8 +119,10 @@ func (s VpoolCLISuite) TestX_CmdAddVpool() { s.Assert().NoError(err) s.Assert().EqualValues(0, txResp.Code, out.String()) + // ---------------------------------------------------------------------- s.T().Log(`Check that proposal was correctly submitted with gov client - $ nibid query gov proposal 2`) + $ nibid query gov proposal 1`) + // ---------------------------------------------------------------------- // the proposal tx won't be included until next block s.Assert().NoError(s.network.WaitForNextBlock()) govQueryClient := govtypes.NewQueryClient(clientCtx) @@ -136,8 +142,10 @@ func (s VpoolCLISuite) TestX_CmdAddVpool() { proposalsQueryResponse.Proposals[0].TotalDeposit, ) + // ---------------------------------------------------------------------- s.T().Log(`Move proposal to vote status by meeting min deposit $ nibid tx gov deposit [proposal-id] [deposit] [flags]`) + // ---------------------------------------------------------------------- expectedProposalIDStr := "1" govDepositParams, err := govQueryClient.Params( context.Background(), &govtypes.QueryParamsRequest{ParamsType: govtypes.ParamDeposit}) @@ -162,9 +170,11 @@ func (s VpoolCLISuite) TestX_CmdAddVpool() { proposalsQueryResponse.Proposals[0].Status, "proposal should be in voting period since min deposit has been met") + // ---------------------------------------------------------------------- s.T().Log(`Vote on the proposal. $ nibid tx gov vote [proposal-id] [option] [flags] e.g. $ nibid tx gov vote 1 yes`) + // ---------------------------------------------------------------------- args = []string{ /*proposal-id=*/ expectedProposalIDStr, /*option=*/ "yes", @@ -188,7 +198,9 @@ func (s VpoolCLISuite) TestX_CmdAddVpool() { }, 20*time.Second, 2*time.Second, "proposal should pass after voting period") + // ---------------------------------------------------------------------- s.T().Log("verify that the new proposed pool exists") + // ---------------------------------------------------------------------- cmd = cli.CmdGetVpools() args = []string{} queryResp := &vpooltypes.QueryAllPoolsResponse{} From b69a4327e54c1b1f6e1dd8ea392b4775b2b1d4f8 Mon Sep 17 00:00:00 2001 From: matthiasmatt Date: Wed, 27 Jul 2022 16:28:35 -0300 Subject: [PATCH 07/28] fix: check for error on chainedhandler --- app/antedecorators/gasless_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/antedecorators/gasless_test.go b/app/antedecorators/gasless_test.go index a7874bc8d..e5c766987 100644 --- a/app/antedecorators/gasless_test.go +++ b/app/antedecorators/gasless_test.go @@ -53,6 +53,7 @@ func TestGaslessDecorator(t *testing.T) { FakeAnteDecoratorThree{}, } chainedHandler := sdk.ChainAnteDecorators(anteDecorators...) - chainedHandler(sdk.Context{}, FakeTx{}, false) + _, err := chainedHandler(sdk.Context{}, FakeTx{}, false) + require.NoError(t, err) require.Equal(t, "onetwothree", output) } From e816badb59f221181f03287e54b51f91461ca5a9 Mon Sep 17 00:00:00 2001 From: matthiasmatt Date: Wed, 27 Jul 2022 18:17:26 -0300 Subject: [PATCH 08/28] wip: Update the fees to 0 in cli test for pricefeed --- app/ante.go | 2 +- app/antedecorators/gasless.go | 17 ++++---- x/pricefeed/client/cli/cli_test.go | 64 ++++++++++++++++++++++-------- x/vpool/client/cli/cli_test.go | 1 - 4 files changed, 58 insertions(+), 26 deletions(-) diff --git a/app/ante.go b/app/ante.go index 267f23e43..bc4845294 100644 --- a/app/ante.go +++ b/app/ante.go @@ -51,7 +51,7 @@ func NewAnteHandler(options AnteHandlerOptions) (sdk.AnteHandler, error) { ante.NewSetUpContextDecorator(), ante.NewRejectExtensionOptionsDecorator(), antedecorators.NewGaslessDecorator([]sdk.AnteDecorator{&memPoolDecorator}, *options.PricefeedKeeper, *options.PerpKeeper), - //ante.NewMempoolFeeDecorator(), + ante.NewMempoolFeeDecorator(), ante.NewValidateBasicDecorator(), ante.NewTxTimeoutHeightDecorator(), ante.NewValidateMemoDecorator(options.AccountKeeper), diff --git a/app/antedecorators/gasless.go b/app/antedecorators/gasless.go index 07054e542..c66b4b649 100644 --- a/app/antedecorators/gasless.go +++ b/app/antedecorators/gasless.go @@ -1,8 +1,11 @@ package antedecorators import ( + "fmt" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/NibiruChain/nibiru/x/common" perpkeeper "github.com/NibiruChain/nibiru/x/perp/keeper" perptypes "github.com/NibiruChain/nibiru/x/perp/types" pricefeedkeeper "github.com/NibiruChain/nibiru/x/pricefeed/keeper" @@ -64,14 +67,14 @@ func isTxGasless(tx sdk.Tx, ctx sdk.Context, pricefeedKeeper pricefeedkeeper.Kee } func pricefeedPostPriceIsGasless(msg *pricefeedtypes.MsgPostPrice, ctx sdk.Context, keeper pricefeedkeeper.Keeper) bool { - return true - // valAddr, err := sdk.AccAddressFromBech32(msg.Oracle) - // if err != nil { - // return false - // } + valAddr, err := sdk.AccAddressFromBech32(msg.Oracle) + if err != nil { + return false + } - // pair := common.AssetPair{Token0: msg.Token0, Token1: msg.Token1} - // return keeper.IsWhitelistedOracle(ctx, pair.String(), valAddr) + pair := common.AssetPair{Token0: msg.Token0, Token1: msg.Token1} + fmt.Println(msg.Oracle, keeper.IsWhitelistedOracle(ctx, pair.String(), valAddr)) + return keeper.IsWhitelistedOracle(ctx, pair.String(), valAddr) } func liquidateIsGasless(msg *perptypes.MsgLiquidate, ctx sdk.Context, keeper perpkeeper.Keeper) bool { diff --git a/x/pricefeed/client/cli/cli_test.go b/x/pricefeed/client/cli/cli_test.go index 79b4cbea0..e089c7247 100644 --- a/x/pricefeed/client/cli/cli_test.go +++ b/x/pricefeed/client/cli/cli_test.go @@ -8,6 +8,10 @@ import ( "testing" "time" + "github.com/cosmos/cosmos-sdk/client" + banktestutil "github.com/cosmos/cosmos-sdk/x/bank/client/testutil" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/crypto/hd" "github.com/cosmos/cosmos-sdk/crypto/keyring" @@ -375,6 +379,7 @@ func (s IntegrationTestSuite) TestOraclesCmd() { clientCtx := val.ClientCtx.WithOutputFormat("json") out, err := sdktestutilcli.ExecTestCLICmd(clientCtx, cmd, tc.args) + s.Require().NoError(err) if tc.expectErr { s.Require().Error(err, out.String()) } else { @@ -389,6 +394,15 @@ func (s IntegrationTestSuite) TestOraclesCmd() { }) } } + +func queryBankBalance(ctx client.Context, s IntegrationTestSuite, account sdk.AccAddress) (finalBalance banktypes.QueryAllBalancesResponse) { + resp, err := banktestutil.QueryBalancesExec(ctx, account) + s.Require().NoError(err) + s.Require().NoError(ctx.Codec.UnmarshalJSON(resp.Bytes(), &finalBalance)) + + return +} + func (s IntegrationTestSuite) TestSetPriceCmd() { err := s.network.WaitForNextBlock() s.Require().NoError(err) @@ -400,11 +414,11 @@ func (s IntegrationTestSuite) TestSetPriceCmd() { expireInOneHour := strconv.Itoa(int(now.Add(1 * time.Hour).Unix())) expiredTS := strconv.Itoa(int(now.Add(-1 * time.Hour).Unix())) - gasFeeToken := sdk.NewCoins(sdk.NewInt64Coin(s.cfg.BondDenom, 1_000_000)) + gasFeeToken := sdk.NewCoins(sdk.NewInt64Coin(s.cfg.BondDenom, 1)) for _, oracleName := range []string{"genOracle", "wrongOracle"} { _, err = testutilcli.FillWalletFromValidator( /*addr=*/ s.oracleMap[oracleName], - /*balanece=*/ gasFeeToken, + /*balance=*/ gasFeeToken, /*Validator=*/ val, /*feesDenom=*/ s.cfg.BondDenom) s.Require().NoError(err) @@ -414,13 +428,13 @@ func (s IntegrationTestSuite) TestSetPriceCmd() { fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), fmt.Sprintf("--%s=test", flags.FlagKeyringBackend), fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), - fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10))).String()), } testCases := []struct { name string args []string expectedPriceForPair map[string]sdk.Dec + expectedFeePaid sdk.Int respType proto.Message expectedCode uint32 fromOracle string @@ -432,8 +446,9 @@ func (s IntegrationTestSuite) TestSetPriceCmd() { }, expectedPriceForPair: map[string]sdk.Dec{ gov.String(): sdk.NewDec(100)}, - respType: &sdk.TxResponse{}, - fromOracle: "genOracle", + expectedFeePaid: sdk.NewInt(0), + respType: &sdk.TxResponse{}, + fromOracle: "genOracle", }, { name: "Set the price of the collateral token", @@ -442,35 +457,39 @@ func (s IntegrationTestSuite) TestSetPriceCmd() { }, expectedPriceForPair: map[string]sdk.Dec{ col.String(): sdk.MustNewDecFromStr("0.85")}, - respType: &sdk.TxResponse{}, - fromOracle: "genOracle", + expectedFeePaid: sdk.NewInt(0), + respType: &sdk.TxResponse{}, + fromOracle: "genOracle", }, { name: "Use invalid oracle", args: []string{ col.Token0, col.Token1, "0.5", expireInOneHour, }, - respType: &sdk.TxResponse{}, - expectedCode: 6, - fromOracle: "wrongOracle", + expectedFeePaid: sdk.NewInt(0), + respType: &sdk.TxResponse{}, + expectedCode: 6, + fromOracle: "wrongOracle", }, { name: "Set invalid pair returns an error", args: []string{ "invalid", "pair", "123", expireInOneHour, }, - expectedCode: 6, - respType: &sdk.TxResponse{}, - fromOracle: "genOracle", + expectedFeePaid: sdk.NewInt(0), + expectedCode: 6, + respType: &sdk.TxResponse{}, + fromOracle: "genOracle", }, { name: "Set expired pair returns an error", args: []string{ col.Token0, col.Token1, "100", expiredTS, }, - expectedCode: 3, - respType: &sdk.TxResponse{}, - fromOracle: "genOracle", + expectedCode: 3, + expectedFeePaid: sdk.NewInt(0), + respType: &sdk.TxResponse{}, + fromOracle: "genOracle", }, } @@ -482,11 +501,22 @@ func (s IntegrationTestSuite) TestSetPriceCmd() { clientCtx := val.ClientCtx commonArgs = append(commonArgs, - fmt.Sprintf("--%s=%s", flags.FlagFrom, s.oracleMap[tc.fromOracle])) + fmt.Sprintf("--%s=%s", flags.FlagFrom, s.oracleMap[tc.fromOracle]), + ) + + bankBalanceStart := queryBankBalance(clientCtx, s, s.oracleMap[tc.fromOracle]) + out, err := sdktestutilcli.ExecTestCLICmd(clientCtx, cmd, append(tc.args, commonArgs...)) + bankBalanceEnd := queryBankBalance(clientCtx, s, s.oracleMap[tc.fromOracle]) + s.Require().NoError(err) s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), tc.respType)) + s.Require().EqualValues( + tc.expectedFeePaid.Int64(), + bankBalanceEnd.Balances.AmountOf(common.DenomGov).Sub(bankBalanceStart.Balances.AmountOf(common.DenomGov)).Int64(), + ) + txResp := tc.respType.(*sdk.TxResponse) err = val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), txResp) s.Require().NoError(err) diff --git a/x/vpool/client/cli/cli_test.go b/x/vpool/client/cli/cli_test.go index 8ddb876f5..f899ceedf 100644 --- a/x/vpool/client/cli/cli_test.go +++ b/x/vpool/client/cli/cli_test.go @@ -65,7 +65,6 @@ func (s *VpoolCLISuite) TearDownSuite() { } func (s VpoolCLISuite) TestX_CmdAddVpool() { - s.Require().Len(s.network.Validators, 1) val := s.network.Validators[0] clientCtx := val.ClientCtx.WithOutputFormat("json") From 6c6a5f61285e02a9663ab521727801b9e5f9698a Mon Sep 17 00:00:00 2001 From: matthiasmatt Date: Wed, 10 Aug 2022 10:48:20 -0300 Subject: [PATCH 09/28] feat: Fork fee deduct ante-handler --- app/ante.go | 7 +- app/antedecorators/fee/expected_keepers.go | 20 ++ app/antedecorators/fee/fee.go | 142 +++++++++++ app/antedecorators/fee/fee_test.go | 104 ++++++++ app/antedecorators/fee/testutil_test.go | 200 ++++++++++++++++ app/antedecorators/{ => gasless}/gasless.go | 19 +- .../{ => gasless}/gasless_test.go | 6 +- app/antedecorators/types/gas.go | 224 ++++++++++++++++++ 8 files changed, 702 insertions(+), 20 deletions(-) create mode 100644 app/antedecorators/fee/expected_keepers.go create mode 100644 app/antedecorators/fee/fee.go create mode 100644 app/antedecorators/fee/fee_test.go create mode 100644 app/antedecorators/fee/testutil_test.go rename app/antedecorators/{ => gasless}/gasless.go (81%) rename app/antedecorators/{ => gasless}/gasless_test.go (87%) create mode 100644 app/antedecorators/types/gas.go diff --git a/app/ante.go b/app/ante.go index bc4845294..7b8b10d33 100644 --- a/app/ante.go +++ b/app/ante.go @@ -7,7 +7,8 @@ import ( ibcante "github.com/cosmos/ibc-go/v3/modules/core/ante" ibckeeper "github.com/cosmos/ibc-go/v3/modules/core/keeper" - "github.com/NibiruChain/nibiru/app/antedecorators" + feeante "github.com/NibiruChain/nibiru/app/antedecorators/fee" + gaslessante "github.com/NibiruChain/nibiru/app/antedecorators/gasless" perpkeeper "github.com/NibiruChain/nibiru/x/perp/keeper" pricefeedkeeper "github.com/NibiruChain/nibiru/x/pricefeed/keeper" ) @@ -50,13 +51,13 @@ func NewAnteHandler(options AnteHandlerOptions) (sdk.AnteHandler, error) { anteDecorators := []sdk.AnteDecorator{ ante.NewSetUpContextDecorator(), ante.NewRejectExtensionOptionsDecorator(), - antedecorators.NewGaslessDecorator([]sdk.AnteDecorator{&memPoolDecorator}, *options.PricefeedKeeper, *options.PerpKeeper), ante.NewMempoolFeeDecorator(), ante.NewValidateBasicDecorator(), ante.NewTxTimeoutHeightDecorator(), ante.NewValidateMemoDecorator(options.AccountKeeper), ante.NewConsumeGasForTxSizeDecorator(options.AccountKeeper), - ante.NewDeductFeeDecorator(options.AccountKeeper, options.BankKeeper, options.FeegrantKeeper), + gaslessante.NewGaslessDecorator([]sdk.AnteDecorator{&memPoolDecorator}, *options.PricefeedKeeper, *options.PerpKeeper), + feeante.NewDeductFeeDecorator(options.AccountKeeper, options.BankKeeper, options.FeegrantKeeper), // Replace fee ante from cosmos auth with a custom one. // SetPubKeyDecorator must be called before all signature verification decorators ante.NewSetPubKeyDecorator(options.AccountKeeper), ante.NewValidateSigCountDecorator(options.AccountKeeper), diff --git a/app/antedecorators/fee/expected_keepers.go b/app/antedecorators/fee/expected_keepers.go new file mode 100644 index 000000000..884171d90 --- /dev/null +++ b/app/antedecorators/fee/expected_keepers.go @@ -0,0 +1,20 @@ +package fee + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth/types" +) + +// AccountKeeper defines the contract needed for AccountKeeper related APIs. +// Interface provides support to use non-sdk AccountKeeper for AnteHandler's decorators. +type AccountKeeper interface { + GetParams(ctx sdk.Context) (params types.Params) + GetAccount(ctx sdk.Context, addr sdk.AccAddress) types.AccountI + SetAccount(ctx sdk.Context, acc types.AccountI) + GetModuleAddress(moduleName string) sdk.AccAddress +} + +// FeegrantKeeper defines the expected feegrant keeper. +type FeegrantKeeper interface { + UseGrantedFees(ctx sdk.Context, granter, grantee sdk.AccAddress, fee sdk.Coins, msgs []sdk.Msg) error +} diff --git a/app/antedecorators/fee/fee.go b/app/antedecorators/fee/fee.go new file mode 100644 index 000000000..c39862494 --- /dev/null +++ b/app/antedecorators/fee/fee.go @@ -0,0 +1,142 @@ +package fee + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/auth/types" +) + +// MempoolFeeDecorator will check if the transaction's fee is at least as large +// as the local validator's minimum gasFee (defined in validator config). +// If fee is too low, decorator returns error and tx is rejected from mempool. +// Note this only applies when ctx.CheckTx = true +// If fee is high enough or not CheckTx, then call next AnteHandler +// CONTRACT: Tx must implement FeeTx to use MempoolFeeDecorator +type MempoolFeeDecorator struct{} + +func NewMempoolFeeDecorator() MempoolFeeDecorator { + return MempoolFeeDecorator{} +} + +func (mfd MempoolFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { + feeTx, ok := tx.(sdk.FeeTx) + if !ok { + return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "Tx must be a FeeTx") + } + + feeCoins := feeTx.GetFee() + gas := feeTx.GetGas() + + // Ensure that the provided fees meet a minimum threshold for the validator, + // if this is a CheckTx. This is only for local mempool purposes, and thus + // is only ran on check tx. + if ctx.IsCheckTx() && !simulate { + minGasPrices := ctx.MinGasPrices() + if !minGasPrices.IsZero() { + requiredFees := make(sdk.Coins, len(minGasPrices)) + + // Determine the required fees by multiplying each required minimum gas + // price by the gas limit, where fee = ceil(minGasPrice * gasLimit). + glDec := sdk.NewDec(int64(gas)) + for i, gp := range minGasPrices { + fee := gp.Amount.Mul(glDec) + requiredFees[i] = sdk.NewCoin(gp.Denom, fee.Ceil().RoundInt()) + } + + if !feeCoins.IsAnyGTE(requiredFees) { + return ctx, sdkerrors.Wrapf(sdkerrors.ErrInsufficientFee, "insufficient fees; got: %s required: %s", feeCoins, requiredFees) + } + } + } + + return next(ctx, tx, simulate) +} + +// DeductFeeDecorator deducts fees from the first signer of the tx +// If the first signer does not have the funds to pay for the fees, return with InsufficientFunds error +// Call next AnteHandler if fees successfully deducted +// CONTRACT: Tx must implement FeeTx interface to use DeductFeeDecorator +type DeductFeeDecorator struct { + ak AccountKeeper + bankKeeper types.BankKeeper + feegrantKeeper FeegrantKeeper +} + +func NewDeductFeeDecorator(ak AccountKeeper, bk types.BankKeeper, fk FeegrantKeeper) DeductFeeDecorator { + return DeductFeeDecorator{ + ak: ak, + bankKeeper: bk, + feegrantKeeper: fk, + } +} + +func (dfd DeductFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { + feeTx, ok := tx.(sdk.FeeTx) + if !ok { + return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "Tx must be a FeeTx") + } + + if addr := dfd.ak.GetModuleAddress(types.FeeCollectorName); addr == nil { + return ctx, fmt.Errorf("Fee collector module account (%s) has not been set", types.FeeCollectorName) + } + + fee := feeTx.GetFee() + feePayer := feeTx.FeePayer() + feeGranter := feeTx.FeeGranter() + + deductFeesFrom := feePayer + + // if feegranter set deduct fee from feegranter account. + // this works with only when feegrant enabled. + if feeGranter != nil { + if dfd.feegrantKeeper == nil { + return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "fee grants are not enabled") + } else if !feeGranter.Equals(feePayer) { + err := dfd.feegrantKeeper.UseGrantedFees(ctx, feeGranter, feePayer, fee, tx.GetMsgs()) + + if err != nil { + return ctx, sdkerrors.Wrapf(err, "%s not allowed to pay fees from %s", feeGranter, feePayer) + } + } + + deductFeesFrom = feeGranter + } + + deductFeesFromAcc := dfd.ak.GetAccount(ctx, deductFeesFrom) + if deductFeesFromAcc == nil { + return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "fee payer address: %s does not exist", deductFeesFrom) + } + + // Gas meter is set to 1 for gasless transactions. + if ctx.GasMeter().GasConsumed() == 1 { + // do nothing + } else if !feeTx.GetFee().IsZero() { + err = DeductFees(dfd.bankKeeper, ctx, deductFeesFromAcc, feeTx.GetFee()) + if err != nil { + return ctx, err + } + } + + events := sdk.Events{sdk.NewEvent(sdk.EventTypeTx, + sdk.NewAttribute(sdk.AttributeKeyFee, feeTx.GetFee().String()), + )} + ctx.EventManager().EmitEvents(events) + + return next(ctx, tx, simulate) +} + +// DeductFees deducts fees from the given account. +func DeductFees(bankKeeper types.BankKeeper, ctx sdk.Context, acc types.AccountI, fees sdk.Coins) error { + if !fees.IsValid() { + return sdkerrors.Wrapf(sdkerrors.ErrInsufficientFee, "invalid fee amount: %s", fees) + } + + err := bankKeeper.SendCoinsFromAccountToModule(ctx, acc.GetAddress(), types.FeeCollectorName, fees) + if err != nil { + return sdkerrors.Wrapf(sdkerrors.ErrInsufficientFunds, err.Error()) + } + + return nil +} diff --git a/app/antedecorators/fee/fee_test.go b/app/antedecorators/fee/fee_test.go new file mode 100644 index 000000000..ea4ca3b72 --- /dev/null +++ b/app/antedecorators/fee/fee_test.go @@ -0,0 +1,104 @@ +package fee_test + +import ( + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/simapp" + "github.com/cosmos/cosmos-sdk/testutil/testdata" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth/ante" +) + +func (suite *AnteTestSuite) TestEnsureMempoolFees() { + suite.SetupTest(true) // setup + suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder() + + mfd := ante.NewMempoolFeeDecorator() + antehandler := sdk.ChainAnteDecorators(mfd) + + // keys and addresses + priv1, _, addr1 := testdata.KeyTestPubAddr() + + // msg and signatures + msg := testdata.NewTestMsg(addr1) + feeAmount := testdata.NewTestFeeAmount() + gasLimit := testdata.NewTestGasLimit() + suite.Require().NoError(suite.txBuilder.SetMsgs(msg)) + suite.txBuilder.SetFeeAmount(feeAmount) + suite.txBuilder.SetGasLimit(gasLimit) + + privs, accNums, accSeqs := []cryptotypes.PrivKey{priv1}, []uint64{0}, []uint64{0} + tx, err := suite.CreateTestTx(privs, accNums, accSeqs, suite.ctx.ChainID()) + suite.Require().NoError(err) + + // Set high gas price so standard test fee fails + atomPrice := sdk.NewDecCoinFromDec("atom", sdk.NewDec(200).Quo(sdk.NewDec(100000))) + highGasPrice := []sdk.DecCoin{atomPrice} + suite.ctx = suite.ctx.WithMinGasPrices(highGasPrice) + + // Set IsCheckTx to true + suite.ctx = suite.ctx.WithIsCheckTx(true) + + // antehandler errors with insufficient fees + _, err = antehandler(suite.ctx, tx, false) + suite.Require().NotNil(err, "Decorator should have errored on too low fee for local gasPrice") + + // Set IsCheckTx to false + suite.ctx = suite.ctx.WithIsCheckTx(false) + + // antehandler should not error since we do not check minGasPrice in DeliverTx + _, err = antehandler(suite.ctx, tx, false) + suite.Require().Nil(err, "MempoolFeeDecorator returned error in DeliverTx") + + // Set IsCheckTx back to true for testing sufficient mempool fee + suite.ctx = suite.ctx.WithIsCheckTx(true) + + atomPrice = sdk.NewDecCoinFromDec("atom", sdk.NewDec(0).Quo(sdk.NewDec(100000))) + lowGasPrice := []sdk.DecCoin{atomPrice} + suite.ctx = suite.ctx.WithMinGasPrices(lowGasPrice) + + _, err = antehandler(suite.ctx, tx, false) + suite.Require().Nil(err, "Decorator should not have errored on fee higher than local gasPrice") +} + +func (suite *AnteTestSuite) TestDeductFees() { + suite.SetupTest(false) // setup + suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder() + + // keys and addresses + priv1, _, addr1 := testdata.KeyTestPubAddr() + + // msg and signatures + msg := testdata.NewTestMsg(addr1) + feeAmount := testdata.NewTestFeeAmount() + gasLimit := testdata.NewTestGasLimit() + suite.Require().NoError(suite.txBuilder.SetMsgs(msg)) + suite.txBuilder.SetFeeAmount(feeAmount) + suite.txBuilder.SetGasLimit(gasLimit) + + privs, accNums, accSeqs := []cryptotypes.PrivKey{priv1}, []uint64{0}, []uint64{0} + tx, err := suite.CreateTestTx(privs, accNums, accSeqs, suite.ctx.ChainID()) + suite.Require().NoError(err) + + // Set account with insufficient funds + acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr1) + suite.app.AccountKeeper.SetAccount(suite.ctx, acc) + coins := sdk.NewCoins(sdk.NewCoin("atom", sdk.NewInt(10))) + err = simapp.FundAccount(suite.app.BankKeeper, suite.ctx, addr1, coins) + suite.Require().NoError(err) + + dfd := ante.NewDeductFeeDecorator(suite.app.AccountKeeper, suite.app.BankKeeper, nil) + antehandler := sdk.ChainAnteDecorators(dfd) + + _, err = antehandler(suite.ctx, tx, false) + + suite.Require().NotNil(err, "Tx did not error when fee payer had insufficient funds") + + // Set account with sufficient funds + suite.app.AccountKeeper.SetAccount(suite.ctx, acc) + err = simapp.FundAccount(suite.app.BankKeeper, suite.ctx, addr1, sdk.NewCoins(sdk.NewCoin("atom", sdk.NewInt(200)))) + suite.Require().NoError(err) + + _, err = antehandler(suite.ctx, tx, false) + + suite.Require().Nil(err, "Tx errored after account has been set with sufficient funds") +} diff --git a/app/antedecorators/fee/testutil_test.go b/app/antedecorators/fee/testutil_test.go new file mode 100644 index 000000000..5fd85f4b9 --- /dev/null +++ b/app/antedecorators/fee/testutil_test.go @@ -0,0 +1,200 @@ +package fee_test + +import ( + "errors" + "fmt" + "testing" + + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" + + "github.com/stretchr/testify/suite" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/tx" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/simapp" + "github.com/cosmos/cosmos-sdk/testutil/testdata" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + "github.com/cosmos/cosmos-sdk/x/auth/ante" + xauthsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" + "github.com/cosmos/cosmos-sdk/x/auth/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" +) + +// TestAccount represents an account used in the tests in x/auth/ante. +type TestAccount struct { + acc types.AccountI + priv cryptotypes.PrivKey +} + +// AnteTestSuite is a test suite to be used with ante handler tests. +type AnteTestSuite struct { + suite.Suite + + app *simapp.SimApp + anteHandler sdk.AnteHandler + ctx sdk.Context + clientCtx client.Context + txBuilder client.TxBuilder +} + +// returns context and app with params set on account keeper +func createTestApp(isCheckTx bool) (*simapp.SimApp, sdk.Context) { + app := simapp.Setup(isCheckTx) + ctx := app.BaseApp.NewContext(isCheckTx, tmproto.Header{}) + app.AccountKeeper.SetParams(ctx, authtypes.DefaultParams()) + + return app, ctx +} + +// SetupTest setups a new test, with new app, context, and anteHandler. +func (suite *AnteTestSuite) SetupTest(isCheckTx bool) { + suite.app, suite.ctx = createTestApp(isCheckTx) + suite.ctx = suite.ctx.WithBlockHeight(1) + + // Set up TxConfig. + encodingConfig := simapp.MakeTestEncodingConfig() + // We're using TestMsg encoding in some tests, so register it here. + encodingConfig.Amino.RegisterConcrete(&testdata.TestMsg{}, "testdata.TestMsg", nil) + testdata.RegisterInterfaces(encodingConfig.InterfaceRegistry) + + suite.clientCtx = client.Context{}. + WithTxConfig(encodingConfig.TxConfig) + + anteHandler, err := ante.NewAnteHandler( + ante.HandlerOptions{ + AccountKeeper: suite.app.AccountKeeper, + BankKeeper: suite.app.BankKeeper, + FeegrantKeeper: suite.app.FeeGrantKeeper, + SignModeHandler: encodingConfig.TxConfig.SignModeHandler(), + SigGasConsumer: ante.DefaultSigVerificationGasConsumer, + }, + ) + + suite.Require().NoError(err) + suite.anteHandler = anteHandler +} + +// CreateTestAccounts creates `numAccs` accounts, and return all relevant +// information about them including their private keys. +func (suite *AnteTestSuite) CreateTestAccounts(numAccs int) []TestAccount { + var accounts []TestAccount + + for i := 0; i < numAccs; i++ { + priv, _, addr := testdata.KeyTestPubAddr() + acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr) + err := acc.SetAccountNumber(uint64(i)) + suite.Require().NoError(err) + suite.app.AccountKeeper.SetAccount(suite.ctx, acc) + someCoins := sdk.Coins{ + sdk.NewInt64Coin("atom", 10000000), + } + err = suite.app.BankKeeper.MintCoins(suite.ctx, minttypes.ModuleName, someCoins) + suite.Require().NoError(err) + + err = suite.app.BankKeeper.SendCoinsFromModuleToAccount(suite.ctx, minttypes.ModuleName, addr, someCoins) + suite.Require().NoError(err) + + accounts = append(accounts, TestAccount{acc, priv}) + } + + return accounts +} + +// CreateTestTx is a helper function to create a tx given multiple inputs. +func (suite *AnteTestSuite) CreateTestTx(privs []cryptotypes.PrivKey, accNums []uint64, accSeqs []uint64, chainID string) (xauthsigning.Tx, error) { + // First round: we gather all the signer infos. We use the "set empty + // signature" hack to do that. + var sigsV2 []signing.SignatureV2 + for i, priv := range privs { + sigV2 := signing.SignatureV2{ + PubKey: priv.PubKey(), + Data: &signing.SingleSignatureData{ + SignMode: suite.clientCtx.TxConfig.SignModeHandler().DefaultMode(), + Signature: nil, + }, + Sequence: accSeqs[i], + } + + sigsV2 = append(sigsV2, sigV2) + } + err := suite.txBuilder.SetSignatures(sigsV2...) + if err != nil { + return nil, err + } + + // Second round: all signer infos are set, so each signer can sign. + sigsV2 = []signing.SignatureV2{} + for i, priv := range privs { + signerData := xauthsigning.SignerData{ + ChainID: chainID, + AccountNumber: accNums[i], + Sequence: accSeqs[i], + } + sigV2, err := tx.SignWithPrivKey( + suite.clientCtx.TxConfig.SignModeHandler().DefaultMode(), signerData, + suite.txBuilder, priv, suite.clientCtx.TxConfig, accSeqs[i]) + if err != nil { + return nil, err + } + + sigsV2 = append(sigsV2, sigV2) + } + err = suite.txBuilder.SetSignatures(sigsV2...) + if err != nil { + return nil, err + } + + return suite.txBuilder.GetTx(), nil +} + +// TestCase represents a test case used in test tables. +type TestCase struct { + desc string + malleate func() + simulate bool + expPass bool + expErr error +} + +// CreateTestTx is a helper function to create a tx given multiple inputs. +func (suite *AnteTestSuite) RunTestCase(privs []cryptotypes.PrivKey, msgs []sdk.Msg, feeAmount sdk.Coins, gasLimit uint64, accNums, accSeqs []uint64, chainID string, tc TestCase) { + suite.Run(fmt.Sprintf("Case %s", tc.desc), func() { + suite.Require().NoError(suite.txBuilder.SetMsgs(msgs...)) + suite.txBuilder.SetFeeAmount(feeAmount) + suite.txBuilder.SetGasLimit(gasLimit) + + // Theoretically speaking, ante handler unit tests should only test + // ante handlers, but here we sometimes also test the tx creation + // process. + tx, txErr := suite.CreateTestTx(privs, accNums, accSeqs, chainID) + newCtx, anteErr := suite.anteHandler(suite.ctx, tx, tc.simulate) + + if tc.expPass { + suite.Require().NoError(txErr) + suite.Require().NoError(anteErr) + suite.Require().NotNil(newCtx) + + suite.ctx = newCtx + } else { + switch { + case txErr != nil: + suite.Require().Error(txErr) + suite.Require().True(errors.Is(txErr, tc.expErr)) + + case anteErr != nil: + suite.Require().Error(anteErr) + suite.Require().True(errors.Is(anteErr, tc.expErr)) + + default: + suite.Fail("expected one of txErr,anteErr to be an error") + } + } + }) +} + +func TestAnteTestSuite(t *testing.T) { + suite.Run(t, new(AnteTestSuite)) +} diff --git a/app/antedecorators/gasless.go b/app/antedecorators/gasless/gasless.go similarity index 81% rename from app/antedecorators/gasless.go rename to app/antedecorators/gasless/gasless.go index c66b4b649..f62561b7d 100644 --- a/app/antedecorators/gasless.go +++ b/app/antedecorators/gasless/gasless.go @@ -1,13 +1,13 @@ -package antedecorators +package gasless import ( "fmt" sdk "github.com/cosmos/cosmos-sdk/types" + types "github.com/NibiruChain/nibiru/app/antedecorators/types" "github.com/NibiruChain/nibiru/x/common" perpkeeper "github.com/NibiruChain/nibiru/x/perp/keeper" - perptypes "github.com/NibiruChain/nibiru/x/perp/types" pricefeedkeeper "github.com/NibiruChain/nibiru/x/pricefeed/keeper" pricefeedtypes "github.com/NibiruChain/nibiru/x/pricefeed/types" ) @@ -23,7 +23,7 @@ func NewGaslessDecorator(wrapped []sdk.AnteDecorator, pricefeedKeeper pricefeedk } func (gd GaslessDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { - if !isTxGasless(tx, ctx, gd.pricefeedKeeper, gd.perpKeeper) { + if simulate || !isTxGasless(tx, ctx, gd.pricefeedKeeper, gd.perpKeeper) { // if not gasless, then we use the wrappers // AnteHandle always takes a `next` so we need a no-op to execute only one handler at a time @@ -37,7 +37,7 @@ func (gd GaslessDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, } return next(ctx, tx, simulate) } - gaslessMeter := sdk.NewInfiniteGasMeter() + gaslessMeter := types.NewInfiniteGasMeter() return next(ctx.WithGasMeter(gaslessMeter), tx, simulate) } @@ -54,11 +54,6 @@ func isTxGasless(tx sdk.Tx, ctx sdk.Context, pricefeedKeeper pricefeedkeeper.Kee continue } return false - case *perptypes.MsgLiquidate: - if liquidateIsGasless(m, ctx, perpKeeper) { - continue - } - return false default: return false } @@ -66,6 +61,7 @@ func isTxGasless(tx sdk.Tx, ctx sdk.Context, pricefeedKeeper pricefeedkeeper.Kee return true } +// Check if the sender is a whitelisted oracle func pricefeedPostPriceIsGasless(msg *pricefeedtypes.MsgPostPrice, ctx sdk.Context, keeper pricefeedkeeper.Keeper) bool { valAddr, err := sdk.AccAddressFromBech32(msg.Oracle) if err != nil { @@ -76,8 +72,3 @@ func pricefeedPostPriceIsGasless(msg *pricefeedtypes.MsgPostPrice, ctx sdk.Conte fmt.Println(msg.Oracle, keeper.IsWhitelistedOracle(ctx, pair.String(), valAddr)) return keeper.IsWhitelistedOracle(ctx, pair.String(), valAddr) } - -func liquidateIsGasless(msg *perptypes.MsgLiquidate, ctx sdk.Context, keeper perpkeeper.Keeper) bool { - _, err := sdk.AccAddressFromBech32(msg.Sender) - return err == nil // TODO: check if within whitelist for liquidators -} diff --git a/app/antedecorators/gasless_test.go b/app/antedecorators/gasless/gasless_test.go similarity index 87% rename from app/antedecorators/gasless_test.go rename to app/antedecorators/gasless/gasless_test.go index e5c766987..646e63e85 100644 --- a/app/antedecorators/gasless_test.go +++ b/app/antedecorators/gasless/gasless_test.go @@ -1,4 +1,4 @@ -package antedecorators_test +package gasless_test import ( "fmt" @@ -7,7 +7,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" - "github.com/NibiruChain/nibiru/app/antedecorators" + gaslessante "github.com/NibiruChain/nibiru/app/antedecorators/gasless" perpkeeper "github.com/NibiruChain/nibiru/x/perp/keeper" pricefeedkeeper "github.com/NibiruChain/nibiru/x/pricefeed/keeper" @@ -49,7 +49,7 @@ func (tx FakeTx) ValidateBasic() error { func TestGaslessDecorator(t *testing.T) { anteDecorators := []sdk.AnteDecorator{ FakeAnteDecoratorOne{}, - antedecorators.NewGaslessDecorator([]sdk.AnteDecorator{FakeAnteDecoratorTwo{}}, pricefeedkeeper.Keeper{}, perpkeeper.Keeper{}), + gaslessante.NewGaslessDecorator([]sdk.AnteDecorator{FakeAnteDecoratorTwo{}}, pricefeedkeeper.Keeper{}, perpkeeper.Keeper{}), FakeAnteDecoratorThree{}, } chainedHandler := sdk.ChainAnteDecorators(anteDecorators...) diff --git a/app/antedecorators/types/gas.go b/app/antedecorators/types/gas.go new file mode 100644 index 000000000..19fb7aa7f --- /dev/null +++ b/app/antedecorators/types/gas.go @@ -0,0 +1,224 @@ +package types + +import ( + "fmt" + "math" +) + +// Gas consumption descriptors. +const ( + GasIterNextCostFlatDesc = "IterNextFlat" + GasValuePerByteDesc = "ValuePerByte" + GasWritePerByteDesc = "WritePerByte" + GasReadPerByteDesc = "ReadPerByte" + GasWriteCostFlatDesc = "WriteFlat" + GasReadCostFlatDesc = "ReadFlat" + GasHasDesc = "Has" + GasDeleteDesc = "Delete" +) + +// Gas measured by the SDK +type Gas = uint64 + +// ErrorNegativeGasConsumed defines an error thrown when the amount of gas refunded results in a +// negative gas consumed amount. +type ErrorNegativeGasConsumed struct { + Descriptor string +} + +// ErrorOutOfGas defines an error thrown when an action results in out of gas. +type ErrorOutOfGas struct { + Descriptor string +} + +// ErrorGasOverflow defines an error thrown when an action results gas consumption +// unsigned integer overflow. +type ErrorGasOverflow struct { + Descriptor string +} + +// GasMeter interface to track gas consumption +type GasMeter interface { + GasConsumed() Gas + GasConsumedToLimit() Gas + Limit() Gas + ConsumeGas(amount Gas, descriptor string) + RefundGas(amount Gas, descriptor string) + IsPastLimit() bool + IsOutOfGas() bool + String() string +} + +type basicGasMeter struct { + limit Gas + consumed Gas +} + +// NewGasMeter returns a reference to a new basicGasMeter. +func NewGasMeter(limit Gas) GasMeter { + return &basicGasMeter{ + limit: limit, + consumed: 0, + } +} + +func (g *basicGasMeter) GasConsumed() Gas { + return g.consumed +} + +func (g *basicGasMeter) Limit() Gas { + return g.limit +} + +func (g *basicGasMeter) GasConsumedToLimit() Gas { + if g.IsPastLimit() { + return g.limit + } + return g.consumed +} + +// addUint64Overflow performs the addition operation on two uint64 integers and +// returns a boolean on whether or not the result overflows. +func addUint64Overflow(a, b uint64) (uint64, bool) { + if math.MaxUint64-a < b { + return 0, true + } + + return a + b, false +} + +func (g *basicGasMeter) ConsumeGas(amount Gas, descriptor string) { + var overflow bool + g.consumed, overflow = addUint64Overflow(g.consumed, amount) + g.consumed = uint64(0) + g.limit = uint64(1) + if overflow { + g.consumed = math.MaxUint64 + panic(ErrorGasOverflow{descriptor}) + } + + if g.consumed > g.limit { + panic(ErrorOutOfGas{descriptor}) + } +} + +// RefundGas will deduct the given amount from the gas consumed. If the amount is greater than the +// gas consumed, the function will panic. +// +// Use case: This functionality enables refunding gas to the transaction or block gas pools so that +// EVM-compatible chains can fully support the go-ethereum StateDb interface. +// See https://github.com/cosmos/cosmos-sdk/pull/9403 for reference. +func (g *basicGasMeter) RefundGas(amount Gas, descriptor string) { + if g.consumed < amount { + panic(ErrorNegativeGasConsumed{Descriptor: descriptor}) + } + + g.consumed -= amount +} + +func (g *basicGasMeter) IsPastLimit() bool { + return false +} + +func (g *basicGasMeter) IsOutOfGas() bool { + return false +} + +func (g *basicGasMeter) String() string { + return fmt.Sprintf("BasicGasMeter:\n limit: %d\n consumed: %d", g.limit, g.consumed) +} + +type infiniteGasMeter struct { + consumed Gas +} + +// NewInfiniteGasMeter returns a reference to a new infiniteGasMeter. +func NewInfiniteGasMeter() GasMeter { + return &infiniteGasMeter{ + consumed: 1, + } +} + +func (g *infiniteGasMeter) GasConsumed() Gas { + return 1 +} + +func (g *infiniteGasMeter) GasConsumedToLimit() Gas { + return 1 +} + +func (g *infiniteGasMeter) Limit() Gas { + return 1 +} + +func (g *infiniteGasMeter) ConsumeGas(amount Gas, descriptor string) { + // var overflow bool + // // TODO: Should we set the consumed field after overflow checking? + // g.consumed, overflow = addUint64Overflow(g.consumed, amount) + // if overflow { + // panic(ErrorGasOverflow{descriptor}) + // } +} + +// RefundGas will deduct the given amount from the gas consumed. If the amount is greater than the +// gas consumed, the function will panic. +// +// Use case: This functionality enables refunding gas to the trasaction or block gas pools so that +// EVM-compatible chains can fully support the go-ethereum StateDb interface. +// See https://github.com/cosmos/cosmos-sdk/pull/9403 for reference. +func (g *infiniteGasMeter) RefundGas(amount Gas, descriptor string) { + if g.consumed < amount { + panic(ErrorNegativeGasConsumed{Descriptor: descriptor}) + } + + g.consumed -= amount +} + +func (g *infiniteGasMeter) IsPastLimit() bool { + return false +} + +func (g *infiniteGasMeter) IsOutOfGas() bool { + return false +} + +func (g *infiniteGasMeter) String() string { + return fmt.Sprintf("InfiniteGasMeter:\n consumed: %d", g.consumed) +} + +// GasConfig defines gas cost for each operation on KVStores +type GasConfig struct { + HasCost Gas + DeleteCost Gas + ReadCostFlat Gas + ReadCostPerByte Gas + WriteCostFlat Gas + WriteCostPerByte Gas + IterNextCostFlat Gas +} + +// KVGasConfig returns a default gas config for KVStores. +func KVGasConfig() GasConfig { + return GasConfig{ + HasCost: 1000, + DeleteCost: 1000, + ReadCostFlat: 1000, + ReadCostPerByte: 3, + WriteCostFlat: 2000, + WriteCostPerByte: 30, + IterNextCostFlat: 30, + } +} + +// TransientGasConfig returns a default gas config for TransientStores. +func TransientGasConfig() GasConfig { + return GasConfig{ + HasCost: 100, + DeleteCost: 100, + ReadCostFlat: 100, + ReadCostPerByte: 0, + WriteCostFlat: 200, + WriteCostPerByte: 3, + IterNextCostFlat: 3, + } +} From 03f1bb8b3fb6c2ce42de8f182f0e7dff1404ee84 Mon Sep 17 00:00:00 2001 From: matthiasmatt Date: Wed, 10 Aug 2022 11:40:04 -0300 Subject: [PATCH 10/28] fix: Remove unused function --- app/antedecorators/fee/testutil_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/app/antedecorators/fee/testutil_test.go b/app/antedecorators/fee/testutil_test.go index 5fd85f4b9..4b096444c 100644 --- a/app/antedecorators/fee/testutil_test.go +++ b/app/antedecorators/fee/testutil_test.go @@ -153,7 +153,6 @@ func (suite *AnteTestSuite) CreateTestTx(privs []cryptotypes.PrivKey, accNums [] // TestCase represents a test case used in test tables. type TestCase struct { desc string - malleate func() simulate bool expPass bool expErr error From 3a36e265c62a4e4a19b25e5d841de71477b76005 Mon Sep 17 00:00:00 2001 From: matthiasmatt Date: Wed, 10 Aug 2022 12:11:52 -0300 Subject: [PATCH 11/28] tests: Add test for gas or no gas in post price cli test --- x/pricefeed/client/cli/cli_test.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/x/pricefeed/client/cli/cli_test.go b/x/pricefeed/client/cli/cli_test.go index 9efc95ac8..93c19ec4b 100644 --- a/x/pricefeed/client/cli/cli_test.go +++ b/x/pricefeed/client/cli/cli_test.go @@ -410,7 +410,7 @@ func (s IntegrationTestSuite) TestSetPriceCmd() { expireInOneHour := strconv.Itoa(int(now.Add(1 * time.Hour).Unix())) expiredTS := strconv.Itoa(int(now.Add(-1 * time.Hour).Unix())) - gasFeeToken := sdk.NewCoins(sdk.NewInt64Coin(s.cfg.BondDenom, 1)) + gasFeeToken := sdk.NewCoins(sdk.NewInt64Coin(s.cfg.BondDenom, 1000)) for _, oracleName := range []string{"genOracle", "wrongOracle"} { _, err = testutilcli.FillWalletFromValidator( /*addr=*/ s.oracleMap[oracleName], @@ -463,7 +463,7 @@ func (s IntegrationTestSuite) TestSetPriceCmd() { args: []string{ col.Token0, col.Token1, "0.5", expireInOneHour, }, - expectedFeePaid: sdk.NewInt(0), + expectedFeePaid: sdk.NewInt(10), // Pay fee since this oracle is not whitelisted respType: &sdk.TxResponse{}, expectedCode: 6, fromOracle: "wrongOracle", @@ -473,7 +473,7 @@ func (s IntegrationTestSuite) TestSetPriceCmd() { args: []string{ "invalid", "pair", "123", expireInOneHour, }, - expectedFeePaid: sdk.NewInt(0), + expectedFeePaid: sdk.NewInt(10), // Invalid pair means that oracle is not whitelisted for this, needs to pay fees expectedCode: 6, respType: &sdk.TxResponse{}, fromOracle: "genOracle", @@ -511,7 +511,8 @@ func (s IntegrationTestSuite) TestSetPriceCmd() { s.Require().EqualValues( tc.expectedFeePaid.Int64(), - bankBalanceEnd.Balances.AmountOf(common.DenomGov).Sub(bankBalanceStart.Balances.AmountOf(common.DenomGov)).Int64(), + bankBalanceStart.Balances.AmountOf(common.DenomGov). + Sub(bankBalanceEnd.Balances.AmountOf(common.DenomGov)).Int64(), ) txResp := tc.respType.(*sdk.TxResponse) From 9901125ff4638b12d646355eaa3ba737b1eb97e5 Mon Sep 17 00:00:00 2001 From: matthiasmatt Date: Wed, 10 Aug 2022 12:14:15 -0300 Subject: [PATCH 12/28] feat: Update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 26b60891c..729ff0f74 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,6 +52,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * [#801](https://github.com/NibiruChain/nibiru/pull/801) - remove unused pair constants * [#788](https://github.com/NibiruChain/nibiru/pull/788) - add --overwrite flag to the nibid init call of localnet.sh * [#804](https://github.com/NibiruChain/nibiru/pull/804) - bump ibc-go to v3.1.1 +* [#817](https://github.com/NibiruChain/nibiru/pull/817) - Make post prices transactions gasless for whitelisted oracles ### Features @@ -105,7 +106,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * [#741](https://github.com/NibiruChain/nibiru/pull/741) - remove unused code and refactored variable names. * [#742](https://github.com/NibiruChain/nibiru/pull/742) - Vpools are not tradeable if they have invalid oracle prices. * [#739](https://github.com/NibiruChain/nibiru/pull/739) - Bump github.com/spf13/viper from 1.11.0 to 1.12.0 -* [#738](https://github.com/NibiruChain/nibiru/pull/738) - Make post prices transactions gasless for whitelisted oracles ### API Breaking From a20fb30b2f28fa800f14d9bccd95fec43aa305ab Mon Sep 17 00:00:00 2001 From: matthiasmatt Date: Wed, 10 Aug 2022 12:16:22 -0300 Subject: [PATCH 13/28] fix: Remove perpkeeper from gasless ante --- app/ante.go | 7 +------ app/antedecorators/gasless/gasless.go | 10 ++++------ app/antedecorators/gasless/gasless_test.go | 3 +-- app/app.go | 1 - 4 files changed, 6 insertions(+), 15 deletions(-) diff --git a/app/ante.go b/app/ante.go index 2adac6b11..4074f8ac0 100644 --- a/app/ante.go +++ b/app/ante.go @@ -9,7 +9,6 @@ import ( feeante "github.com/NibiruChain/nibiru/app/antedecorators/fee" gaslessante "github.com/NibiruChain/nibiru/app/antedecorators/gasless" - perpkeeper "github.com/NibiruChain/nibiru/x/perp/keeper" pricefeedkeeper "github.com/NibiruChain/nibiru/x/pricefeed/keeper" ) @@ -18,7 +17,6 @@ type AnteHandlerOptions struct { IBCKeeper *ibckeeper.Keeper PricefeedKeeper *pricefeedkeeper.Keeper - PerpKeeper *perpkeeper.Keeper } /* @@ -45,9 +43,6 @@ func NewAnteHandler(options AnteHandlerOptions) (sdk.AnteHandler, error) { if options.PricefeedKeeper == nil { return nil, sdkerrors.Wrap(sdkerrors.ErrLogic, "pricefeed keeper is required for ante builder") } - if options.PerpKeeper == nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrLogic, "perp keeper is required for ante builder") - } memPoolDecorator := ante.NewMempoolFeeDecorator() anteDecorators := []sdk.AnteDecorator{ @@ -58,7 +53,7 @@ func NewAnteHandler(options AnteHandlerOptions) (sdk.AnteHandler, error) { ante.NewTxTimeoutHeightDecorator(), ante.NewValidateMemoDecorator(options.AccountKeeper), ante.NewConsumeGasForTxSizeDecorator(options.AccountKeeper), - gaslessante.NewGaslessDecorator([]sdk.AnteDecorator{&memPoolDecorator}, *options.PricefeedKeeper, *options.PerpKeeper), + gaslessante.NewGaslessDecorator([]sdk.AnteDecorator{&memPoolDecorator}, *options.PricefeedKeeper), feeante.NewDeductFeeDecorator(options.AccountKeeper, options.BankKeeper, options.FeegrantKeeper), // Replace fee ante from cosmos auth with a custom one. // SetPubKeyDecorator must be called before all signature verification decorators ante.NewSetPubKeyDecorator(options.AccountKeeper), diff --git a/app/antedecorators/gasless/gasless.go b/app/antedecorators/gasless/gasless.go index f62561b7d..f31b69a57 100644 --- a/app/antedecorators/gasless/gasless.go +++ b/app/antedecorators/gasless/gasless.go @@ -7,7 +7,6 @@ import ( types "github.com/NibiruChain/nibiru/app/antedecorators/types" "github.com/NibiruChain/nibiru/x/common" - perpkeeper "github.com/NibiruChain/nibiru/x/perp/keeper" pricefeedkeeper "github.com/NibiruChain/nibiru/x/pricefeed/keeper" pricefeedtypes "github.com/NibiruChain/nibiru/x/pricefeed/types" ) @@ -15,15 +14,14 @@ import ( type GaslessDecorator struct { wrapped []sdk.AnteDecorator pricefeedKeeper pricefeedkeeper.Keeper - perpKeeper perpkeeper.Keeper } -func NewGaslessDecorator(wrapped []sdk.AnteDecorator, pricefeedKeeper pricefeedkeeper.Keeper, perpKeeper perpkeeper.Keeper) GaslessDecorator { - return GaslessDecorator{wrapped: wrapped, pricefeedKeeper: pricefeedKeeper, perpKeeper: perpKeeper} +func NewGaslessDecorator(wrapped []sdk.AnteDecorator, pricefeedKeeper pricefeedkeeper.Keeper) GaslessDecorator { + return GaslessDecorator{wrapped: wrapped, pricefeedKeeper: pricefeedKeeper} } func (gd GaslessDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { - if simulate || !isTxGasless(tx, ctx, gd.pricefeedKeeper, gd.perpKeeper) { + if simulate || !isTxGasless(tx, ctx, gd.pricefeedKeeper) { // if not gasless, then we use the wrappers // AnteHandle always takes a `next` so we need a no-op to execute only one handler at a time @@ -42,7 +40,7 @@ func (gd GaslessDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, return next(ctx.WithGasMeter(gaslessMeter), tx, simulate) } -func isTxGasless(tx sdk.Tx, ctx sdk.Context, pricefeedKeeper pricefeedkeeper.Keeper, perpKeeper perpkeeper.Keeper) bool { +func isTxGasless(tx sdk.Tx, ctx sdk.Context, pricefeedKeeper pricefeedkeeper.Keeper) bool { if len(tx.GetMsgs()) == 0 { // empty TX shouldn't be gasless return false diff --git a/app/antedecorators/gasless/gasless_test.go b/app/antedecorators/gasless/gasless_test.go index 646e63e85..3fc07d30e 100644 --- a/app/antedecorators/gasless/gasless_test.go +++ b/app/antedecorators/gasless/gasless_test.go @@ -9,7 +9,6 @@ import ( gaslessante "github.com/NibiruChain/nibiru/app/antedecorators/gasless" - perpkeeper "github.com/NibiruChain/nibiru/x/perp/keeper" pricefeedkeeper "github.com/NibiruChain/nibiru/x/pricefeed/keeper" ) @@ -49,7 +48,7 @@ func (tx FakeTx) ValidateBasic() error { func TestGaslessDecorator(t *testing.T) { anteDecorators := []sdk.AnteDecorator{ FakeAnteDecoratorOne{}, - gaslessante.NewGaslessDecorator([]sdk.AnteDecorator{FakeAnteDecoratorTwo{}}, pricefeedkeeper.Keeper{}, perpkeeper.Keeper{}), + gaslessante.NewGaslessDecorator([]sdk.AnteDecorator{FakeAnteDecoratorTwo{}}, pricefeedkeeper.Keeper{}), FakeAnteDecoratorThree{}, } chainedHandler := sdk.ChainAnteDecorators(anteDecorators...) diff --git a/app/app.go b/app/app.go index dc77e3911..7b00fecf2 100644 --- a/app/app.go +++ b/app/app.go @@ -755,7 +755,6 @@ func NewNibiruApp( SigGasConsumer: ante.DefaultSigVerificationGasConsumer, }, PricefeedKeeper: &app.PricefeedKeeper, - PerpKeeper: &app.PerpKeeper, IBCKeeper: app.IBCKeeper, }) From 903c44ff0edee53fe1588cc6ead12f82ba1e6706 Mon Sep 17 00:00:00 2001 From: matthiasmatt Date: Wed, 10 Aug 2022 12:18:55 -0300 Subject: [PATCH 14/28] fix: Remove println from codebase --- app/antedecorators/gasless/gasless.go | 3 --- x/dex/client/testutil/cli_test.go | 3 --- x/epochs/keeper/epoch.go | 1 - x/pricefeed/client/cli/cli_test.go | 1 - 4 files changed, 8 deletions(-) diff --git a/app/antedecorators/gasless/gasless.go b/app/antedecorators/gasless/gasless.go index f31b69a57..4afa21e82 100644 --- a/app/antedecorators/gasless/gasless.go +++ b/app/antedecorators/gasless/gasless.go @@ -1,8 +1,6 @@ package gasless import ( - "fmt" - sdk "github.com/cosmos/cosmos-sdk/types" types "github.com/NibiruChain/nibiru/app/antedecorators/types" @@ -67,6 +65,5 @@ func pricefeedPostPriceIsGasless(msg *pricefeedtypes.MsgPostPrice, ctx sdk.Conte } pair := common.AssetPair{Token0: msg.Token0, Token1: msg.Token1} - fmt.Println(msg.Oracle, keeper.IsWhitelistedOracle(ctx, pair.String(), valAddr)) return keeper.IsWhitelistedOracle(ctx, pair.String(), valAddr) } diff --git a/x/dex/client/testutil/cli_test.go b/x/dex/client/testutil/cli_test.go index 42f19f010..2411ea5ad 100644 --- a/x/dex/client/testutil/cli_test.go +++ b/x/dex/client/testutil/cli_test.go @@ -324,9 +324,6 @@ func (s IntegrationTestSuite) TestCNewExitPoolCmd() { var finalBalance banktypes.QueryAllBalancesResponse s.Require().NoError(ctx.Codec.UnmarshalJSON(resp.Bytes(), &finalBalance)) - fmt.Println("Final balance:") - fmt.Println(finalBalance) - s.Require().Equal( originalBalance.Balances.AmountOf("uusdc").Add(tc.expectedOtherToken), finalBalance.Balances.AmountOf("uusdc"), diff --git a/x/epochs/keeper/epoch.go b/x/epochs/keeper/epoch.go index a4f54c9c0..19645176b 100644 --- a/x/epochs/keeper/epoch.go +++ b/x/epochs/keeper/epoch.go @@ -48,7 +48,6 @@ func (k Keeper) IterateEpochInfo(ctx sdk.Context, fn func(index int64, epochInfo defer func(iterator sdk.Iterator) { err := iterator.Close() if err != nil { - fmt.Println(err) panic(err) } }(iterator) diff --git a/x/pricefeed/client/cli/cli_test.go b/x/pricefeed/client/cli/cli_test.go index 93c19ec4b..fad67c3bc 100644 --- a/x/pricefeed/client/cli/cli_test.go +++ b/x/pricefeed/client/cli/cli_test.go @@ -646,7 +646,6 @@ func (s IntegrationTestSuite) TestX_CmdAddOracleProposalAndVote() { cmd := cli.CmdAddOracleProposal() flags.AddTxFlagsToCmd(cmd) out, err := sdktestutilcli.ExecTestCLICmd(clientCtx, cmd, args) - fmt.Println(out) s.Require().NoError(err) s.Assert().NotContains(out.String(), "fail") var txRespProtoMessage proto.Message = &sdk.TxResponse{} From 4c9a9e42cac68a4d4a76230852e52c4080eab38f Mon Sep 17 00:00:00 2001 From: Agent Smith Date: Wed, 10 Aug 2022 19:41:56 +0200 Subject: [PATCH 15/28] test gasmeter infinite --- app/ante.go | 5 +- app/antedecorators/gasless/gasless.go | 18 ++----- app/antedecorators/gasless/gasless_test.go | 55 +++++++++++++--------- go.mod | 4 +- 4 files changed, 41 insertions(+), 41 deletions(-) diff --git a/app/ante.go b/app/ante.go index 4074f8ac0..7025f13a7 100644 --- a/app/ante.go +++ b/app/ante.go @@ -8,7 +8,6 @@ import ( ibckeeper "github.com/cosmos/ibc-go/v3/modules/core/keeper" feeante "github.com/NibiruChain/nibiru/app/antedecorators/fee" - gaslessante "github.com/NibiruChain/nibiru/app/antedecorators/gasless" pricefeedkeeper "github.com/NibiruChain/nibiru/x/pricefeed/keeper" ) @@ -44,7 +43,7 @@ func NewAnteHandler(options AnteHandlerOptions) (sdk.AnteHandler, error) { return nil, sdkerrors.Wrap(sdkerrors.ErrLogic, "pricefeed keeper is required for ante builder") } - memPoolDecorator := ante.NewMempoolFeeDecorator() + // memPoolDecorator := ante.NewMempoolFeeDecorator() anteDecorators := []sdk.AnteDecorator{ ante.NewSetUpContextDecorator(), ante.NewRejectExtensionOptionsDecorator(), @@ -53,7 +52,7 @@ func NewAnteHandler(options AnteHandlerOptions) (sdk.AnteHandler, error) { ante.NewTxTimeoutHeightDecorator(), ante.NewValidateMemoDecorator(options.AccountKeeper), ante.NewConsumeGasForTxSizeDecorator(options.AccountKeeper), - gaslessante.NewGaslessDecorator([]sdk.AnteDecorator{&memPoolDecorator}, *options.PricefeedKeeper), + //gaslessante.NewGaslessDecorator(*options.PricefeedKeeper), feeante.NewDeductFeeDecorator(options.AccountKeeper, options.BankKeeper, options.FeegrantKeeper), // Replace fee ante from cosmos auth with a custom one. // SetPubKeyDecorator must be called before all signature verification decorators ante.NewSetPubKeyDecorator(options.AccountKeeper), diff --git a/app/antedecorators/gasless/gasless.go b/app/antedecorators/gasless/gasless.go index 4afa21e82..e0a80ed1d 100644 --- a/app/antedecorators/gasless/gasless.go +++ b/app/antedecorators/gasless/gasless.go @@ -10,31 +10,19 @@ import ( ) type GaslessDecorator struct { - wrapped []sdk.AnteDecorator pricefeedKeeper pricefeedkeeper.Keeper } -func NewGaslessDecorator(wrapped []sdk.AnteDecorator, pricefeedKeeper pricefeedkeeper.Keeper) GaslessDecorator { - return GaslessDecorator{wrapped: wrapped, pricefeedKeeper: pricefeedKeeper} +func NewGaslessDecorator(pricefeedKeeper pricefeedkeeper.Keeper) GaslessDecorator { + return GaslessDecorator{pricefeedKeeper: pricefeedKeeper} } func (gd GaslessDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { if simulate || !isTxGasless(tx, ctx, gd.pricefeedKeeper) { - // if not gasless, then we use the wrappers - - // AnteHandle always takes a `next` so we need a no-op to execute only one handler at a time - terminatorHandler := func(ctx sdk.Context, _ sdk.Tx, _ bool) (sdk.Context, error) { - return ctx, nil - } - // iterating instead of recursing the handler for readability - // we use blank here because we shouldn't handle the error - for _, handler := range gd.wrapped { - ctx, _ = handler.AnteHandle(ctx, tx, simulate, terminatorHandler) - } return next(ctx, tx, simulate) } - gaslessMeter := types.NewInfiniteGasMeter() + gaslessMeter := types.NewInfiniteGasMeter() return next(ctx.WithGasMeter(gaslessMeter), tx, simulate) } diff --git a/app/antedecorators/gasless/gasless_test.go b/app/antedecorators/gasless/gasless_test.go index 3fc07d30e..dd8d4a03a 100644 --- a/app/antedecorators/gasless/gasless_test.go +++ b/app/antedecorators/gasless/gasless_test.go @@ -1,44 +1,50 @@ package gasless_test import ( - "fmt" + types2 "github.com/NibiruChain/nibiru/app/antedecorators/types" + "github.com/NibiruChain/nibiru/x/pricefeed/types" + "github.com/NibiruChain/nibiru/x/testutil/sample" + "github.com/NibiruChain/nibiru/x/testutil/testapp" "testing" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" gaslessante "github.com/NibiruChain/nibiru/app/antedecorators/gasless" - - pricefeedkeeper "github.com/NibiruChain/nibiru/x/pricefeed/keeper" ) -var output = "" - -type FakeAnteDecoratorOne struct{} +var oracleAddr = sample.AccAddress() -func (ad FakeAnteDecoratorOne) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { - output = fmt.Sprintf("%sone", output) - return next(ctx, tx, simulate) +type PreDecoratorWithBasicMeter struct { + t *testing.T } -type FakeAnteDecoratorTwo struct{} +func (ad PreDecoratorWithBasicMeter) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { + require.IsType(ad.t, sdk.NewGasMeter(111), ctx.GasMeter()) -func (ad FakeAnteDecoratorTwo) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { - output = fmt.Sprintf("%stwo", output) return next(ctx, tx, simulate) } -type FakeAnteDecoratorThree struct{} +type PostDecoratorWithInifiniteMeter struct { + t *testing.T +} + +func (ad PostDecoratorWithInifiniteMeter) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { + require.IsType(ad.t, types2.NewInfiniteGasMeter(), ctx.GasMeter()) -func (ad FakeAnteDecoratorThree) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { - output = fmt.Sprintf("%sthree", output) return next(ctx, tx, simulate) } type FakeTx struct{} func (tx FakeTx) GetMsgs() []sdk.Msg { - return []sdk.Msg{} + return []sdk.Msg{ + &types.MsgPostPrice{ + Oracle: oracleAddr.String(), + Token0: "unibi", + Token1: "unusd", + }, + } } func (tx FakeTx) ValidateBasic() error { @@ -46,13 +52,20 @@ func (tx FakeTx) ValidateBasic() error { } func TestGaslessDecorator(t *testing.T) { + app, ctx := testapp.NewNibiruAppAndContext(true) + ctx = ctx.WithGasMeter(sdk.NewGasMeter(10000000)) + + // Whitelist an oracle address. + app.PricefeedKeeper.WhitelistOracles(ctx, []sdk.AccAddress{oracleAddr}) + anteDecorators := []sdk.AnteDecorator{ - FakeAnteDecoratorOne{}, - gaslessante.NewGaslessDecorator([]sdk.AnteDecorator{FakeAnteDecoratorTwo{}}, pricefeedkeeper.Keeper{}), - FakeAnteDecoratorThree{}, + PreDecoratorWithBasicMeter{t}, + gaslessante.NewGaslessDecorator(app.PricefeedKeeper), + PostDecoratorWithInifiniteMeter{t}, } + chainedHandler := sdk.ChainAnteDecorators(anteDecorators...) - _, err := chainedHandler(sdk.Context{}, FakeTx{}, false) + + _, err := chainedHandler(ctx, FakeTx{}, false) require.NoError(t, err) - require.Equal(t, "onetwothree", output) } diff --git a/go.mod b/go.mod index 0d6152413..f898bd63b 100644 --- a/go.mod +++ b/go.mod @@ -8,8 +8,10 @@ require ( github.com/gogo/protobuf v1.3.3 github.com/golang/mock v1.6.0 github.com/golang/protobuf v1.5.2 + github.com/google/gofuzz v1.2.0 github.com/gorilla/mux v1.8.0 github.com/grpc-ecosystem/grpc-gateway v1.16.0 + github.com/pkg/errors v0.9.1 github.com/rakyll/statik v0.1.7 github.com/regen-network/cosmos-proto v0.3.1 github.com/spf13/cast v1.5.0 @@ -63,7 +65,6 @@ require ( github.com/gogo/gateway v1.1.0 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/btree v1.0.0 // indirect - github.com/google/gofuzz v1.2.0 // indirect github.com/google/orderedcode v0.0.1 // indirect github.com/gorilla/handlers v1.5.1 // indirect github.com/gorilla/websocket v1.5.0 // indirect @@ -94,7 +95,6 @@ require ( github.com/pelletier/go-toml v1.9.5 // indirect github.com/pelletier/go-toml/v2 v2.0.1 // indirect github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect - github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v1.12.1 // indirect github.com/prometheus/client_model v0.2.0 // indirect From 1ab48a0a9038083530568d233aabb7f0f48e27bd Mon Sep 17 00:00:00 2001 From: Agent Smith Date: Wed, 10 Aug 2022 20:16:59 +0200 Subject: [PATCH 16/28] change test for gassless decorator --- Makefile | 4 ++-- app/antedecorators/gasless/gasless_test.go | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index db6c9e688..94053ff94 100644 --- a/Makefile +++ b/Makefile @@ -124,10 +124,10 @@ PACKAGES_NOSIMULATION = $(shell go list ./... | grep -v '/simapp') RUNSIM = $(BINDIR)/runsim test-unit: - go test $(PACKAGES_NOSIMULATION) -short -cover + @go test $(PACKAGES_NOSIMULATION) -short -cover test-integration: - go test -v $(PACKAGES_NOSIMULATION) -cover + @go test -v $(PACKAGES_NOSIMULATION) -cover runsim: $(RUNSIM) $(RUNSIM): diff --git a/app/antedecorators/gasless/gasless_test.go b/app/antedecorators/gasless/gasless_test.go index dd8d4a03a..130b7e132 100644 --- a/app/antedecorators/gasless/gasless_test.go +++ b/app/antedecorators/gasless/gasless_test.go @@ -15,21 +15,21 @@ import ( var oracleAddr = sample.AccAddress() -type PreDecoratorWithBasicMeter struct { +type DecoratorWithNormalGasMeterCheck struct { t *testing.T } -func (ad PreDecoratorWithBasicMeter) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { +func (ad DecoratorWithNormalGasMeterCheck) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { require.IsType(ad.t, sdk.NewGasMeter(111), ctx.GasMeter()) return next(ctx, tx, simulate) } -type PostDecoratorWithInifiniteMeter struct { +type DecoratorWithInfiniteGasMeterCheck struct { t *testing.T } -func (ad PostDecoratorWithInifiniteMeter) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { +func (ad DecoratorWithInfiniteGasMeterCheck) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { require.IsType(ad.t, types2.NewInfiniteGasMeter(), ctx.GasMeter()) return next(ctx, tx, simulate) @@ -59,9 +59,9 @@ func TestGaslessDecorator(t *testing.T) { app.PricefeedKeeper.WhitelistOracles(ctx, []sdk.AccAddress{oracleAddr}) anteDecorators := []sdk.AnteDecorator{ - PreDecoratorWithBasicMeter{t}, + DecoratorWithNormalGasMeterCheck{t}, gaslessante.NewGaslessDecorator(app.PricefeedKeeper), - PostDecoratorWithInifiniteMeter{t}, + DecoratorWithInfiniteGasMeterCheck{t}, } chainedHandler := sdk.ChainAnteDecorators(anteDecorators...) From b56cd3a4ec91974c124797dd4e323f451d23b82f Mon Sep 17 00:00:00 2001 From: Agent Smith Date: Wed, 10 Aug 2022 20:30:12 +0200 Subject: [PATCH 17/28] update gasless test --- app/antedecorators/gasless/gasless_test.go | 92 ++++++++++++++++++---- 1 file changed, 77 insertions(+), 15 deletions(-) diff --git a/app/antedecorators/gasless/gasless_test.go b/app/antedecorators/gasless/gasless_test.go index 130b7e132..809bca14a 100644 --- a/app/antedecorators/gasless/gasless_test.go +++ b/app/antedecorators/gasless/gasless_test.go @@ -5,6 +5,7 @@ import ( "github.com/NibiruChain/nibiru/x/pricefeed/types" "github.com/NibiruChain/nibiru/x/testutil/sample" "github.com/NibiruChain/nibiru/x/testutil/testapp" + types3 "github.com/cosmos/cosmos-sdk/x/bank/types" "testing" sdk "github.com/cosmos/cosmos-sdk/types" @@ -35,9 +36,9 @@ func (ad DecoratorWithInfiniteGasMeterCheck) AnteHandle(ctx sdk.Context, tx sdk. return next(ctx, tx, simulate) } -type FakeTx struct{} +type TxWithPostPriceMsg struct{} -func (tx FakeTx) GetMsgs() []sdk.Msg { +func (tx TxWithPostPriceMsg) GetMsgs() []sdk.Msg { return []sdk.Msg{ &types.MsgPostPrice{ Oracle: oracleAddr.String(), @@ -47,25 +48,86 @@ func (tx FakeTx) GetMsgs() []sdk.Msg { } } -func (tx FakeTx) ValidateBasic() error { +func (tx TxWithPostPriceMsg) ValidateBasic() error { return nil } -func TestGaslessDecorator(t *testing.T) { - app, ctx := testapp.NewNibiruAppAndContext(true) - ctx = ctx.WithGasMeter(sdk.NewGasMeter(10000000)) +type TxWithoutPostPriceMsg struct{} - // Whitelist an oracle address. - app.PricefeedKeeper.WhitelistOracles(ctx, []sdk.AccAddress{oracleAddr}) +func (tx TxWithoutPostPriceMsg) GetMsgs() []sdk.Msg { + return []sdk.Msg{ + &types3.MsgSend{}, + } +} + +func (tx TxWithoutPostPriceMsg) ValidateBasic() error { + return nil +} - anteDecorators := []sdk.AnteDecorator{ - DecoratorWithNormalGasMeterCheck{t}, - gaslessante.NewGaslessDecorator(app.PricefeedKeeper), - DecoratorWithInfiniteGasMeterCheck{t}, +func TestGaslessDecorator_Whitelisted(t *testing.T) { + tests := []struct { + name string + isWhitelisted bool + shouldChangeMeter bool + tx sdk.Tx + }{ + { + "whitelisted address", + true, + true, + TxWithPostPriceMsg{}, + }, + { + "whitelisted address but tx without price feed message", + true, + false, + TxWithoutPostPriceMsg{}, + }, + { + "not whitelisted address with post price tx", + false, + false, + TxWithPostPriceMsg{}, + }, + { + "not whitelisted address without post price tx", + false, + false, + TxWithoutPostPriceMsg{}, + }, } - chainedHandler := sdk.ChainAnteDecorators(anteDecorators...) + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + app, ctx := testapp.NewNibiruAppAndContext(true) + ctx = ctx.WithGasMeter(sdk.NewGasMeter(10000000)) + + if tc.isWhitelisted { + // If we whitelist, the gas meter changes. + app.PricefeedKeeper.WhitelistOracles(ctx, []sdk.AccAddress{oracleAddr}) + } + + var anteDecorators []sdk.AnteDecorator + if tc.shouldChangeMeter { + anteDecorators = []sdk.AnteDecorator{ + DecoratorWithNormalGasMeterCheck{t}, + gaslessante.NewGaslessDecorator(app.PricefeedKeeper), + DecoratorWithInfiniteGasMeterCheck{t}, + } + } else { + anteDecorators = []sdk.AnteDecorator{ + DecoratorWithNormalGasMeterCheck{t}, + gaslessante.NewGaslessDecorator(app.PricefeedKeeper), + DecoratorWithNormalGasMeterCheck{t}, + } + } + + chainedHandler := sdk.ChainAnteDecorators(anteDecorators...) + + _, err := chainedHandler(ctx, tc.tx, false) + require.NoError(t, err) + }) + } - _, err := chainedHandler(ctx, FakeTx{}, false) - require.NoError(t, err) } From 06ab94a168cd256e5d35e0cd9b860f66dc5fc78a Mon Sep 17 00:00:00 2001 From: Agent Smith Date: Wed, 10 Aug 2022 20:58:35 +0200 Subject: [PATCH 18/28] remove unused code --- app/ante.go | 3 +- app/antedecorators/fee/fee.go | 65 ++--------------------------------- 2 files changed, 5 insertions(+), 63 deletions(-) diff --git a/app/ante.go b/app/ante.go index 7025f13a7..f75805110 100644 --- a/app/ante.go +++ b/app/ante.go @@ -1,6 +1,7 @@ package app import ( + gaslessante "github.com/NibiruChain/nibiru/app/antedecorators/gasless" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/auth/ante" @@ -52,7 +53,7 @@ func NewAnteHandler(options AnteHandlerOptions) (sdk.AnteHandler, error) { ante.NewTxTimeoutHeightDecorator(), ante.NewValidateMemoDecorator(options.AccountKeeper), ante.NewConsumeGasForTxSizeDecorator(options.AccountKeeper), - //gaslessante.NewGaslessDecorator(*options.PricefeedKeeper), + gaslessante.NewGaslessDecorator(*options.PricefeedKeeper), feeante.NewDeductFeeDecorator(options.AccountKeeper, options.BankKeeper, options.FeegrantKeeper), // Replace fee ante from cosmos auth with a custom one. // SetPubKeyDecorator must be called before all signature verification decorators ante.NewSetPubKeyDecorator(options.AccountKeeper), diff --git a/app/antedecorators/fee/fee.go b/app/antedecorators/fee/fee.go index c39862494..7392a18ae 100644 --- a/app/antedecorators/fee/fee.go +++ b/app/antedecorators/fee/fee.go @@ -2,58 +2,12 @@ package fee import ( "fmt" - sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/auth/ante" "github.com/cosmos/cosmos-sdk/x/auth/types" ) -// MempoolFeeDecorator will check if the transaction's fee is at least as large -// as the local validator's minimum gasFee (defined in validator config). -// If fee is too low, decorator returns error and tx is rejected from mempool. -// Note this only applies when ctx.CheckTx = true -// If fee is high enough or not CheckTx, then call next AnteHandler -// CONTRACT: Tx must implement FeeTx to use MempoolFeeDecorator -type MempoolFeeDecorator struct{} - -func NewMempoolFeeDecorator() MempoolFeeDecorator { - return MempoolFeeDecorator{} -} - -func (mfd MempoolFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { - feeTx, ok := tx.(sdk.FeeTx) - if !ok { - return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "Tx must be a FeeTx") - } - - feeCoins := feeTx.GetFee() - gas := feeTx.GetGas() - - // Ensure that the provided fees meet a minimum threshold for the validator, - // if this is a CheckTx. This is only for local mempool purposes, and thus - // is only ran on check tx. - if ctx.IsCheckTx() && !simulate { - minGasPrices := ctx.MinGasPrices() - if !minGasPrices.IsZero() { - requiredFees := make(sdk.Coins, len(minGasPrices)) - - // Determine the required fees by multiplying each required minimum gas - // price by the gas limit, where fee = ceil(minGasPrice * gasLimit). - glDec := sdk.NewDec(int64(gas)) - for i, gp := range minGasPrices { - fee := gp.Amount.Mul(glDec) - requiredFees[i] = sdk.NewCoin(gp.Denom, fee.Ceil().RoundInt()) - } - - if !feeCoins.IsAnyGTE(requiredFees) { - return ctx, sdkerrors.Wrapf(sdkerrors.ErrInsufficientFee, "insufficient fees; got: %s required: %s", feeCoins, requiredFees) - } - } - } - - return next(ctx, tx, simulate) -} - // DeductFeeDecorator deducts fees from the first signer of the tx // If the first signer does not have the funds to pay for the fees, return with InsufficientFunds error // Call next AnteHandler if fees successfully deducted @@ -110,10 +64,11 @@ func (dfd DeductFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bo } // Gas meter is set to 1 for gasless transactions. + // Depends on GasLessDecorator for this to happen. if ctx.GasMeter().GasConsumed() == 1 { // do nothing } else if !feeTx.GetFee().IsZero() { - err = DeductFees(dfd.bankKeeper, ctx, deductFeesFromAcc, feeTx.GetFee()) + err = ante.DeductFees(dfd.bankKeeper, ctx, deductFeesFromAcc, feeTx.GetFee()) if err != nil { return ctx, err } @@ -126,17 +81,3 @@ func (dfd DeductFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bo return next(ctx, tx, simulate) } - -// DeductFees deducts fees from the given account. -func DeductFees(bankKeeper types.BankKeeper, ctx sdk.Context, acc types.AccountI, fees sdk.Coins) error { - if !fees.IsValid() { - return sdkerrors.Wrapf(sdkerrors.ErrInsufficientFee, "invalid fee amount: %s", fees) - } - - err := bankKeeper.SendCoinsFromAccountToModule(ctx, acc.GetAddress(), types.FeeCollectorName, fees) - if err != nil { - return sdkerrors.Wrapf(sdkerrors.ErrInsufficientFunds, err.Error()) - } - - return nil -} From 3ef21a2c0abbface68d8e6e72e9748678e6d2379 Mon Sep 17 00:00:00 2001 From: Agent Smith Date: Wed, 10 Aug 2022 21:00:30 +0200 Subject: [PATCH 19/28] fix comment --- app/antedecorators/fee/fee.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/antedecorators/fee/fee.go b/app/antedecorators/fee/fee.go index 7392a18ae..353ce5477 100644 --- a/app/antedecorators/fee/fee.go +++ b/app/antedecorators/fee/fee.go @@ -33,7 +33,7 @@ func (dfd DeductFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bo } if addr := dfd.ak.GetModuleAddress(types.FeeCollectorName); addr == nil { - return ctx, fmt.Errorf("Fee collector module account (%s) has not been set", types.FeeCollectorName) + return ctx, fmt.Errorf("fee collector module account (%s) has not been set", types.FeeCollectorName) } fee := feeTx.GetFee() From 681ab280b1cc2accbd73536f3a2bcf48be41be58 Mon Sep 17 00:00:00 2001 From: Agent Smith Date: Wed, 10 Aug 2022 21:27:53 +0200 Subject: [PATCH 20/28] remove unused code --- app/antedecorators/fee/expected_keepers.go | 20 -- app/antedecorators/fee/fee.go | 6 +- app/antedecorators/gasless/gasless.go | 2 +- app/antedecorators/gasless/gasless_test.go | 2 +- app/antedecorators/types/gas.go | 209 ++------------------- 5 files changed, 20 insertions(+), 219 deletions(-) delete mode 100644 app/antedecorators/fee/expected_keepers.go diff --git a/app/antedecorators/fee/expected_keepers.go b/app/antedecorators/fee/expected_keepers.go deleted file mode 100644 index 884171d90..000000000 --- a/app/antedecorators/fee/expected_keepers.go +++ /dev/null @@ -1,20 +0,0 @@ -package fee - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth/types" -) - -// AccountKeeper defines the contract needed for AccountKeeper related APIs. -// Interface provides support to use non-sdk AccountKeeper for AnteHandler's decorators. -type AccountKeeper interface { - GetParams(ctx sdk.Context) (params types.Params) - GetAccount(ctx sdk.Context, addr sdk.AccAddress) types.AccountI - SetAccount(ctx sdk.Context, acc types.AccountI) - GetModuleAddress(moduleName string) sdk.AccAddress -} - -// FeegrantKeeper defines the expected feegrant keeper. -type FeegrantKeeper interface { - UseGrantedFees(ctx sdk.Context, granter, grantee sdk.AccAddress, fee sdk.Coins, msgs []sdk.Msg) error -} diff --git a/app/antedecorators/fee/fee.go b/app/antedecorators/fee/fee.go index 353ce5477..40863fc28 100644 --- a/app/antedecorators/fee/fee.go +++ b/app/antedecorators/fee/fee.go @@ -13,12 +13,12 @@ import ( // Call next AnteHandler if fees successfully deducted // CONTRACT: Tx must implement FeeTx interface to use DeductFeeDecorator type DeductFeeDecorator struct { - ak AccountKeeper + ak ante.AccountKeeper bankKeeper types.BankKeeper - feegrantKeeper FeegrantKeeper + feegrantKeeper ante.FeegrantKeeper } -func NewDeductFeeDecorator(ak AccountKeeper, bk types.BankKeeper, fk FeegrantKeeper) DeductFeeDecorator { +func NewDeductFeeDecorator(ak ante.AccountKeeper, bk types.BankKeeper, fk ante.FeegrantKeeper) DeductFeeDecorator { return DeductFeeDecorator{ ak: ak, bankKeeper: bk, diff --git a/app/antedecorators/gasless/gasless.go b/app/antedecorators/gasless/gasless.go index e0a80ed1d..0c57c8a92 100644 --- a/app/antedecorators/gasless/gasless.go +++ b/app/antedecorators/gasless/gasless.go @@ -22,7 +22,7 @@ func (gd GaslessDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, return next(ctx, tx, simulate) } - gaslessMeter := types.NewInfiniteGasMeter() + gaslessMeter := types.GasLessMeter() return next(ctx.WithGasMeter(gaslessMeter), tx, simulate) } diff --git a/app/antedecorators/gasless/gasless_test.go b/app/antedecorators/gasless/gasless_test.go index 809bca14a..82887c1b0 100644 --- a/app/antedecorators/gasless/gasless_test.go +++ b/app/antedecorators/gasless/gasless_test.go @@ -31,7 +31,7 @@ type DecoratorWithInfiniteGasMeterCheck struct { } func (ad DecoratorWithInfiniteGasMeterCheck) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { - require.IsType(ad.t, types2.NewInfiniteGasMeter(), ctx.GasMeter()) + require.IsType(ad.t, types2.GasLessMeter(), ctx.GasMeter()) return next(ctx, tx, simulate) } diff --git a/app/antedecorators/types/gas.go b/app/antedecorators/types/gas.go index 19fb7aa7f..2ff587b1c 100644 --- a/app/antedecorators/types/gas.go +++ b/app/antedecorators/types/gas.go @@ -2,223 +2,44 @@ package types import ( "fmt" - "math" + "github.com/cosmos/cosmos-sdk/types" ) -// Gas consumption descriptors. -const ( - GasIterNextCostFlatDesc = "IterNextFlat" - GasValuePerByteDesc = "ValuePerByte" - GasWritePerByteDesc = "WritePerByte" - GasReadPerByteDesc = "ReadPerByte" - GasWriteCostFlatDesc = "WriteFlat" - GasReadCostFlatDesc = "ReadFlat" - GasHasDesc = "Has" - GasDeleteDesc = "Delete" -) - -// Gas measured by the SDK -type Gas = uint64 - -// ErrorNegativeGasConsumed defines an error thrown when the amount of gas refunded results in a -// negative gas consumed amount. -type ErrorNegativeGasConsumed struct { - Descriptor string -} - -// ErrorOutOfGas defines an error thrown when an action results in out of gas. -type ErrorOutOfGas struct { - Descriptor string -} - -// ErrorGasOverflow defines an error thrown when an action results gas consumption -// unsigned integer overflow. -type ErrorGasOverflow struct { - Descriptor string -} - -// GasMeter interface to track gas consumption -type GasMeter interface { - GasConsumed() Gas - GasConsumedToLimit() Gas - Limit() Gas - ConsumeGas(amount Gas, descriptor string) - RefundGas(amount Gas, descriptor string) - IsPastLimit() bool - IsOutOfGas() bool - String() string -} - -type basicGasMeter struct { - limit Gas - consumed Gas -} - -// NewGasMeter returns a reference to a new basicGasMeter. -func NewGasMeter(limit Gas) GasMeter { - return &basicGasMeter{ - limit: limit, - consumed: 0, - } -} - -func (g *basicGasMeter) GasConsumed() Gas { - return g.consumed -} - -func (g *basicGasMeter) Limit() Gas { - return g.limit -} - -func (g *basicGasMeter) GasConsumedToLimit() Gas { - if g.IsPastLimit() { - return g.limit - } - return g.consumed -} - -// addUint64Overflow performs the addition operation on two uint64 integers and -// returns a boolean on whether or not the result overflows. -func addUint64Overflow(a, b uint64) (uint64, bool) { - if math.MaxUint64-a < b { - return 0, true - } - - return a + b, false -} - -func (g *basicGasMeter) ConsumeGas(amount Gas, descriptor string) { - var overflow bool - g.consumed, overflow = addUint64Overflow(g.consumed, amount) - g.consumed = uint64(0) - g.limit = uint64(1) - if overflow { - g.consumed = math.MaxUint64 - panic(ErrorGasOverflow{descriptor}) - } - - if g.consumed > g.limit { - panic(ErrorOutOfGas{descriptor}) - } -} - -// RefundGas will deduct the given amount from the gas consumed. If the amount is greater than the -// gas consumed, the function will panic. -// -// Use case: This functionality enables refunding gas to the transaction or block gas pools so that -// EVM-compatible chains can fully support the go-ethereum StateDb interface. -// See https://github.com/cosmos/cosmos-sdk/pull/9403 for reference. -func (g *basicGasMeter) RefundGas(amount Gas, descriptor string) { - if g.consumed < amount { - panic(ErrorNegativeGasConsumed{Descriptor: descriptor}) - } - - g.consumed -= amount -} - -func (g *basicGasMeter) IsPastLimit() bool { - return false -} - -func (g *basicGasMeter) IsOutOfGas() bool { - return false -} - -func (g *basicGasMeter) String() string { - return fmt.Sprintf("BasicGasMeter:\n limit: %d\n consumed: %d", g.limit, g.consumed) +type gaslessMeter struct { + consumed types.Gas } -type infiniteGasMeter struct { - consumed Gas -} - -// NewInfiniteGasMeter returns a reference to a new infiniteGasMeter. -func NewInfiniteGasMeter() GasMeter { - return &infiniteGasMeter{ +// GasLessMeter returns a reference to a new gaslessMeter. +func GasLessMeter() types.GasMeter { + return &gaslessMeter{ consumed: 1, } } -func (g *infiniteGasMeter) GasConsumed() Gas { +func (g *gaslessMeter) GasConsumed() types.Gas { return 1 } -func (g *infiniteGasMeter) GasConsumedToLimit() Gas { +func (g *gaslessMeter) GasConsumedToLimit() types.Gas { return 1 } -func (g *infiniteGasMeter) Limit() Gas { +func (g *gaslessMeter) Limit() types.Gas { return 1 } -func (g *infiniteGasMeter) ConsumeGas(amount Gas, descriptor string) { - // var overflow bool - // // TODO: Should we set the consumed field after overflow checking? - // g.consumed, overflow = addUint64Overflow(g.consumed, amount) - // if overflow { - // panic(ErrorGasOverflow{descriptor}) - // } +func (g *gaslessMeter) ConsumeGas(types.Gas, string) { } +func (g *gaslessMeter) RefundGas(types.Gas, string) {} -// RefundGas will deduct the given amount from the gas consumed. If the amount is greater than the -// gas consumed, the function will panic. -// -// Use case: This functionality enables refunding gas to the trasaction or block gas pools so that -// EVM-compatible chains can fully support the go-ethereum StateDb interface. -// See https://github.com/cosmos/cosmos-sdk/pull/9403 for reference. -func (g *infiniteGasMeter) RefundGas(amount Gas, descriptor string) { - if g.consumed < amount { - panic(ErrorNegativeGasConsumed{Descriptor: descriptor}) - } - - g.consumed -= amount -} - -func (g *infiniteGasMeter) IsPastLimit() bool { +func (g *gaslessMeter) IsPastLimit() bool { return false } -func (g *infiniteGasMeter) IsOutOfGas() bool { +func (g *gaslessMeter) IsOutOfGas() bool { return false } -func (g *infiniteGasMeter) String() string { - return fmt.Sprintf("InfiniteGasMeter:\n consumed: %d", g.consumed) -} - -// GasConfig defines gas cost for each operation on KVStores -type GasConfig struct { - HasCost Gas - DeleteCost Gas - ReadCostFlat Gas - ReadCostPerByte Gas - WriteCostFlat Gas - WriteCostPerByte Gas - IterNextCostFlat Gas -} - -// KVGasConfig returns a default gas config for KVStores. -func KVGasConfig() GasConfig { - return GasConfig{ - HasCost: 1000, - DeleteCost: 1000, - ReadCostFlat: 1000, - ReadCostPerByte: 3, - WriteCostFlat: 2000, - WriteCostPerByte: 30, - IterNextCostFlat: 30, - } -} - -// TransientGasConfig returns a default gas config for TransientStores. -func TransientGasConfig() GasConfig { - return GasConfig{ - HasCost: 100, - DeleteCost: 100, - ReadCostFlat: 100, - ReadCostPerByte: 0, - WriteCostFlat: 200, - WriteCostPerByte: 3, - IterNextCostFlat: 3, - } +func (g *gaslessMeter) String() string { + return fmt.Sprintf("GaslessMeter:\n consumed: %d", g.consumed) } From 5614d3792bcd19911f476846d4642474835fa268 Mon Sep 17 00:00:00 2001 From: Agent Smith Date: Wed, 10 Aug 2022 21:28:41 +0200 Subject: [PATCH 21/28] linter --- app/ante.go | 3 ++- app/antedecorators/fee/fee.go | 1 + app/antedecorators/gasless/gasless_test.go | 7 ++++--- app/antedecorators/types/gas.go | 1 + x/perp/keeper/clearing_house.go | 1 - x/vpool/keeper/keeper.go | 3 ++- 6 files changed, 10 insertions(+), 6 deletions(-) diff --git a/app/ante.go b/app/ante.go index f75805110..1f8a087b4 100644 --- a/app/ante.go +++ b/app/ante.go @@ -1,13 +1,14 @@ package app import ( - gaslessante "github.com/NibiruChain/nibiru/app/antedecorators/gasless" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/auth/ante" ibcante "github.com/cosmos/ibc-go/v3/modules/core/ante" ibckeeper "github.com/cosmos/ibc-go/v3/modules/core/keeper" + gaslessante "github.com/NibiruChain/nibiru/app/antedecorators/gasless" + feeante "github.com/NibiruChain/nibiru/app/antedecorators/fee" pricefeedkeeper "github.com/NibiruChain/nibiru/x/pricefeed/keeper" ) diff --git a/app/antedecorators/fee/fee.go b/app/antedecorators/fee/fee.go index 40863fc28..f6b29f4e8 100644 --- a/app/antedecorators/fee/fee.go +++ b/app/antedecorators/fee/fee.go @@ -2,6 +2,7 @@ package fee import ( "fmt" + sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/auth/ante" diff --git a/app/antedecorators/gasless/gasless_test.go b/app/antedecorators/gasless/gasless_test.go index 82887c1b0..0b7825e4a 100644 --- a/app/antedecorators/gasless/gasless_test.go +++ b/app/antedecorators/gasless/gasless_test.go @@ -1,12 +1,14 @@ package gasless_test import ( + "testing" + + types3 "github.com/cosmos/cosmos-sdk/x/bank/types" + types2 "github.com/NibiruChain/nibiru/app/antedecorators/types" "github.com/NibiruChain/nibiru/x/pricefeed/types" "github.com/NibiruChain/nibiru/x/testutil/sample" "github.com/NibiruChain/nibiru/x/testutil/testapp" - types3 "github.com/cosmos/cosmos-sdk/x/bank/types" - "testing" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" @@ -129,5 +131,4 @@ func TestGaslessDecorator_Whitelisted(t *testing.T) { require.NoError(t, err) }) } - } diff --git a/app/antedecorators/types/gas.go b/app/antedecorators/types/gas.go index 2ff587b1c..24064c4a4 100644 --- a/app/antedecorators/types/gas.go +++ b/app/antedecorators/types/gas.go @@ -2,6 +2,7 @@ package types import ( "fmt" + "github.com/cosmos/cosmos-sdk/types" ) diff --git a/x/perp/keeper/clearing_house.go b/x/perp/keeper/clearing_house.go index 58f54105e..9f21eff83 100644 --- a/x/perp/keeper/clearing_house.go +++ b/x/perp/keeper/clearing_house.go @@ -95,7 +95,6 @@ func (k Keeper) OpenPosition( // - Checks that quote asset is not zero. // - Checks that leverage is not zero. // - Checks that leverage is below requirement. -// func (k Keeper) checkOpenPositionRequirements(ctx sdk.Context, pair common.AssetPair, quoteAssetAmount sdk.Int, leverage sdk.Dec) error { if err := k.requireVpool(ctx, pair); err != nil { return err diff --git a/x/vpool/keeper/keeper.go b/x/vpool/keeper/keeper.go index 9ab34f45e..effb52f91 100644 --- a/x/vpool/keeper/keeper.go +++ b/x/vpool/keeper/keeper.go @@ -355,7 +355,8 @@ func (k Keeper) GetMaintenanceMarginRatio(ctx sdk.Context, pair common.AssetPair return pool.MaintenanceMarginRatio } -/** +/* +* GetMaxLeverage returns the maximum leverage required to open a position in the pool. args: From af767cdbe8f33eb4813d4ff992f20da5aa656d40 Mon Sep 17 00:00:00 2001 From: Agent Smith Date: Wed, 10 Aug 2022 21:29:45 +0200 Subject: [PATCH 22/28] linter 2 --- app/antedecorators/gasless/gasless_test.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/app/antedecorators/gasless/gasless_test.go b/app/antedecorators/gasless/gasless_test.go index 0b7825e4a..2059e372f 100644 --- a/app/antedecorators/gasless/gasless_test.go +++ b/app/antedecorators/gasless/gasless_test.go @@ -3,17 +3,15 @@ package gasless_test import ( "testing" + sdk "github.com/cosmos/cosmos-sdk/types" types3 "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/stretchr/testify/require" + gaslessante "github.com/NibiruChain/nibiru/app/antedecorators/gasless" types2 "github.com/NibiruChain/nibiru/app/antedecorators/types" "github.com/NibiruChain/nibiru/x/pricefeed/types" "github.com/NibiruChain/nibiru/x/testutil/sample" "github.com/NibiruChain/nibiru/x/testutil/testapp" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/stretchr/testify/require" - - gaslessante "github.com/NibiruChain/nibiru/app/antedecorators/gasless" ) var oracleAddr = sample.AccAddress() From 48511f1101c723fc2c31a8cfba86e748db2a70ea Mon Sep 17 00:00:00 2001 From: Agent Smith Date: Wed, 10 Aug 2022 21:52:48 +0200 Subject: [PATCH 23/28] final clean --- app/antedecorators/fee/fee_test.go | 63 ++---------------- app/antedecorators/fee/testutil_test.go | 85 ++----------------------- 2 files changed, 12 insertions(+), 136 deletions(-) diff --git a/app/antedecorators/fee/fee_test.go b/app/antedecorators/fee/fee_test.go index ea4ca3b72..b0c8be8d0 100644 --- a/app/antedecorators/fee/fee_test.go +++ b/app/antedecorators/fee/fee_test.go @@ -1,65 +1,14 @@ package fee_test import ( + "github.com/NibiruChain/nibiru/app" + "github.com/NibiruChain/nibiru/app/antedecorators/fee" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" "github.com/cosmos/cosmos-sdk/simapp" "github.com/cosmos/cosmos-sdk/testutil/testdata" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth/ante" ) -func (suite *AnteTestSuite) TestEnsureMempoolFees() { - suite.SetupTest(true) // setup - suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder() - - mfd := ante.NewMempoolFeeDecorator() - antehandler := sdk.ChainAnteDecorators(mfd) - - // keys and addresses - priv1, _, addr1 := testdata.KeyTestPubAddr() - - // msg and signatures - msg := testdata.NewTestMsg(addr1) - feeAmount := testdata.NewTestFeeAmount() - gasLimit := testdata.NewTestGasLimit() - suite.Require().NoError(suite.txBuilder.SetMsgs(msg)) - suite.txBuilder.SetFeeAmount(feeAmount) - suite.txBuilder.SetGasLimit(gasLimit) - - privs, accNums, accSeqs := []cryptotypes.PrivKey{priv1}, []uint64{0}, []uint64{0} - tx, err := suite.CreateTestTx(privs, accNums, accSeqs, suite.ctx.ChainID()) - suite.Require().NoError(err) - - // Set high gas price so standard test fee fails - atomPrice := sdk.NewDecCoinFromDec("atom", sdk.NewDec(200).Quo(sdk.NewDec(100000))) - highGasPrice := []sdk.DecCoin{atomPrice} - suite.ctx = suite.ctx.WithMinGasPrices(highGasPrice) - - // Set IsCheckTx to true - suite.ctx = suite.ctx.WithIsCheckTx(true) - - // antehandler errors with insufficient fees - _, err = antehandler(suite.ctx, tx, false) - suite.Require().NotNil(err, "Decorator should have errored on too low fee for local gasPrice") - - // Set IsCheckTx to false - suite.ctx = suite.ctx.WithIsCheckTx(false) - - // antehandler should not error since we do not check minGasPrice in DeliverTx - _, err = antehandler(suite.ctx, tx, false) - suite.Require().Nil(err, "MempoolFeeDecorator returned error in DeliverTx") - - // Set IsCheckTx back to true for testing sufficient mempool fee - suite.ctx = suite.ctx.WithIsCheckTx(true) - - atomPrice = sdk.NewDecCoinFromDec("atom", sdk.NewDec(0).Quo(sdk.NewDec(100000))) - lowGasPrice := []sdk.DecCoin{atomPrice} - suite.ctx = suite.ctx.WithMinGasPrices(lowGasPrice) - - _, err = antehandler(suite.ctx, tx, false) - suite.Require().Nil(err, "Decorator should not have errored on fee higher than local gasPrice") -} - func (suite *AnteTestSuite) TestDeductFees() { suite.SetupTest(false) // setup suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder() @@ -69,7 +18,7 @@ func (suite *AnteTestSuite) TestDeductFees() { // msg and signatures msg := testdata.NewTestMsg(addr1) - feeAmount := testdata.NewTestFeeAmount() + feeAmount := sdk.NewCoins(sdk.NewInt64Coin(app.BondDenom, 150)) gasLimit := testdata.NewTestGasLimit() suite.Require().NoError(suite.txBuilder.SetMsgs(msg)) suite.txBuilder.SetFeeAmount(feeAmount) @@ -82,11 +31,11 @@ func (suite *AnteTestSuite) TestDeductFees() { // Set account with insufficient funds acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr1) suite.app.AccountKeeper.SetAccount(suite.ctx, acc) - coins := sdk.NewCoins(sdk.NewCoin("atom", sdk.NewInt(10))) + coins := sdk.NewCoins(sdk.NewCoin(app.BondDenom, sdk.NewInt(10))) err = simapp.FundAccount(suite.app.BankKeeper, suite.ctx, addr1, coins) suite.Require().NoError(err) - dfd := ante.NewDeductFeeDecorator(suite.app.AccountKeeper, suite.app.BankKeeper, nil) + dfd := fee.NewDeductFeeDecorator(suite.app.AccountKeeper, suite.app.BankKeeper, nil) antehandler := sdk.ChainAnteDecorators(dfd) _, err = antehandler(suite.ctx, tx, false) @@ -95,7 +44,7 @@ func (suite *AnteTestSuite) TestDeductFees() { // Set account with sufficient funds suite.app.AccountKeeper.SetAccount(suite.ctx, acc) - err = simapp.FundAccount(suite.app.BankKeeper, suite.ctx, addr1, sdk.NewCoins(sdk.NewCoin("atom", sdk.NewInt(200)))) + err = simapp.FundAccount(suite.app.BankKeeper, suite.ctx, addr1, sdk.NewCoins(sdk.NewCoin(app.BondDenom, sdk.NewInt(200)))) suite.Require().NoError(err) _, err = antehandler(suite.ctx, tx, false) diff --git a/app/antedecorators/fee/testutil_test.go b/app/antedecorators/fee/testutil_test.go index 4b096444c..3ab834048 100644 --- a/app/antedecorators/fee/testutil_test.go +++ b/app/antedecorators/fee/testutil_test.go @@ -1,19 +1,16 @@ package fee_test import ( - "errors" - "fmt" + "github.com/NibiruChain/nibiru/app" + "github.com/NibiruChain/nibiru/x/testutil/testapp" "testing" - minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" - "github.com/stretchr/testify/suite" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/tx" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" - "github.com/cosmos/cosmos-sdk/simapp" "github.com/cosmos/cosmos-sdk/testutil/testdata" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/tx/signing" @@ -33,7 +30,7 @@ type TestAccount struct { type AnteTestSuite struct { suite.Suite - app *simapp.SimApp + app *app.NibiruApp anteHandler sdk.AnteHandler ctx sdk.Context clientCtx client.Context @@ -41,8 +38,8 @@ type AnteTestSuite struct { } // returns context and app with params set on account keeper -func createTestApp(isCheckTx bool) (*simapp.SimApp, sdk.Context) { - app := simapp.Setup(isCheckTx) +func createTestApp(isCheckTx bool) (*app.NibiruApp, sdk.Context) { + app := testapp.NewNibiruApp(true) ctx := app.BaseApp.NewContext(isCheckTx, tmproto.Header{}) app.AccountKeeper.SetParams(ctx, authtypes.DefaultParams()) @@ -55,7 +52,7 @@ func (suite *AnteTestSuite) SetupTest(isCheckTx bool) { suite.ctx = suite.ctx.WithBlockHeight(1) // Set up TxConfig. - encodingConfig := simapp.MakeTestEncodingConfig() + encodingConfig := app.MakeTestEncodingConfig() // We're using TestMsg encoding in some tests, so register it here. encodingConfig.Amino.RegisterConcrete(&testdata.TestMsg{}, "testdata.TestMsg", nil) testdata.RegisterInterfaces(encodingConfig.InterfaceRegistry) @@ -77,32 +74,6 @@ func (suite *AnteTestSuite) SetupTest(isCheckTx bool) { suite.anteHandler = anteHandler } -// CreateTestAccounts creates `numAccs` accounts, and return all relevant -// information about them including their private keys. -func (suite *AnteTestSuite) CreateTestAccounts(numAccs int) []TestAccount { - var accounts []TestAccount - - for i := 0; i < numAccs; i++ { - priv, _, addr := testdata.KeyTestPubAddr() - acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr) - err := acc.SetAccountNumber(uint64(i)) - suite.Require().NoError(err) - suite.app.AccountKeeper.SetAccount(suite.ctx, acc) - someCoins := sdk.Coins{ - sdk.NewInt64Coin("atom", 10000000), - } - err = suite.app.BankKeeper.MintCoins(suite.ctx, minttypes.ModuleName, someCoins) - suite.Require().NoError(err) - - err = suite.app.BankKeeper.SendCoinsFromModuleToAccount(suite.ctx, minttypes.ModuleName, addr, someCoins) - suite.Require().NoError(err) - - accounts = append(accounts, TestAccount{acc, priv}) - } - - return accounts -} - // CreateTestTx is a helper function to create a tx given multiple inputs. func (suite *AnteTestSuite) CreateTestTx(privs []cryptotypes.PrivKey, accNums []uint64, accSeqs []uint64, chainID string) (xauthsigning.Tx, error) { // First round: we gather all the signer infos. We use the "set empty @@ -150,50 +121,6 @@ func (suite *AnteTestSuite) CreateTestTx(privs []cryptotypes.PrivKey, accNums [] return suite.txBuilder.GetTx(), nil } -// TestCase represents a test case used in test tables. -type TestCase struct { - desc string - simulate bool - expPass bool - expErr error -} - -// CreateTestTx is a helper function to create a tx given multiple inputs. -func (suite *AnteTestSuite) RunTestCase(privs []cryptotypes.PrivKey, msgs []sdk.Msg, feeAmount sdk.Coins, gasLimit uint64, accNums, accSeqs []uint64, chainID string, tc TestCase) { - suite.Run(fmt.Sprintf("Case %s", tc.desc), func() { - suite.Require().NoError(suite.txBuilder.SetMsgs(msgs...)) - suite.txBuilder.SetFeeAmount(feeAmount) - suite.txBuilder.SetGasLimit(gasLimit) - - // Theoretically speaking, ante handler unit tests should only test - // ante handlers, but here we sometimes also test the tx creation - // process. - tx, txErr := suite.CreateTestTx(privs, accNums, accSeqs, chainID) - newCtx, anteErr := suite.anteHandler(suite.ctx, tx, tc.simulate) - - if tc.expPass { - suite.Require().NoError(txErr) - suite.Require().NoError(anteErr) - suite.Require().NotNil(newCtx) - - suite.ctx = newCtx - } else { - switch { - case txErr != nil: - suite.Require().Error(txErr) - suite.Require().True(errors.Is(txErr, tc.expErr)) - - case anteErr != nil: - suite.Require().Error(anteErr) - suite.Require().True(errors.Is(anteErr, tc.expErr)) - - default: - suite.Fail("expected one of txErr,anteErr to be an error") - } - } - }) -} - func TestAnteTestSuite(t *testing.T) { suite.Run(t, new(AnteTestSuite)) } From 386d34427b7d367bf48969ec764655c8d12bad20 Mon Sep 17 00:00:00 2001 From: Agent Smith Date: Thu, 11 Aug 2022 17:21:42 +0200 Subject: [PATCH 24/28] remove unused func --- app/antedecorators/fee/fee_test.go | 5 +++-- app/antedecorators/fee/testutil_test.go | 17 +++++------------ 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/app/antedecorators/fee/fee_test.go b/app/antedecorators/fee/fee_test.go index b0c8be8d0..5beec11fa 100644 --- a/app/antedecorators/fee/fee_test.go +++ b/app/antedecorators/fee/fee_test.go @@ -1,12 +1,13 @@ package fee_test import ( - "github.com/NibiruChain/nibiru/app" - "github.com/NibiruChain/nibiru/app/antedecorators/fee" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" "github.com/cosmos/cosmos-sdk/simapp" "github.com/cosmos/cosmos-sdk/testutil/testdata" sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/NibiruChain/nibiru/app" + "github.com/NibiruChain/nibiru/app/antedecorators/fee" ) func (suite *AnteTestSuite) TestDeductFees() { diff --git a/app/antedecorators/fee/testutil_test.go b/app/antedecorators/fee/testutil_test.go index 3ab834048..e955cce54 100644 --- a/app/antedecorators/fee/testutil_test.go +++ b/app/antedecorators/fee/testutil_test.go @@ -1,13 +1,8 @@ package fee_test import ( - "github.com/NibiruChain/nibiru/app" - "github.com/NibiruChain/nibiru/x/testutil/testapp" "testing" - "github.com/stretchr/testify/suite" - tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/tx" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" @@ -16,15 +11,13 @@ import ( "github.com/cosmos/cosmos-sdk/types/tx/signing" "github.com/cosmos/cosmos-sdk/x/auth/ante" xauthsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" - "github.com/cosmos/cosmos-sdk/x/auth/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" -) + "github.com/stretchr/testify/suite" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" -// TestAccount represents an account used in the tests in x/auth/ante. -type TestAccount struct { - acc types.AccountI - priv cryptotypes.PrivKey -} + "github.com/NibiruChain/nibiru/app" + "github.com/NibiruChain/nibiru/x/testutil/testapp" +) // AnteTestSuite is a test suite to be used with ante handler tests. type AnteTestSuite struct { From 12594ab54107d47c2209b8f950c921bc7f5e5c8b Mon Sep 17 00:00:00 2001 From: Agent Smith Date: Thu, 11 Aug 2022 17:39:14 +0200 Subject: [PATCH 25/28] remove comment --- app/ante.go | 1 - 1 file changed, 1 deletion(-) diff --git a/app/ante.go b/app/ante.go index 1f8a087b4..175880652 100644 --- a/app/ante.go +++ b/app/ante.go @@ -45,7 +45,6 @@ func NewAnteHandler(options AnteHandlerOptions) (sdk.AnteHandler, error) { return nil, sdkerrors.Wrap(sdkerrors.ErrLogic, "pricefeed keeper is required for ante builder") } - // memPoolDecorator := ante.NewMempoolFeeDecorator() anteDecorators := []sdk.AnteDecorator{ ante.NewSetUpContextDecorator(), ante.NewRejectExtensionOptionsDecorator(), From d9eade73832933c9b26820a2ceafad277187f12e Mon Sep 17 00:00:00 2001 From: matthiasmatt Date: Fri, 12 Aug 2022 11:17:38 -0300 Subject: [PATCH 26/28] fix: Fix logic on pricefeed query tests for cli_test --- x/pricefeed/client/cli/cli_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/x/pricefeed/client/cli/cli_test.go b/x/pricefeed/client/cli/cli_test.go index fad67c3bc..a31cad328 100644 --- a/x/pricefeed/client/cli/cli_test.go +++ b/x/pricefeed/client/cli/cli_test.go @@ -337,7 +337,7 @@ func (s IntegrationTestSuite) TestOraclesCmd() { args []string expectedOracles []string - expectErr bool + expectPass bool respType proto.Message }{ { @@ -361,7 +361,7 @@ func (s IntegrationTestSuite) TestOraclesCmd() { args: []string{ "invalid:pair", }, - expectErr: false, + expectPass: false, expectedOracles: []string{}, respType: &pricefeedtypes.QueryOraclesResponse{}, }, @@ -375,8 +375,8 @@ func (s IntegrationTestSuite) TestOraclesCmd() { clientCtx := val.ClientCtx.WithOutputFormat("json") out, err := sdktestutilcli.ExecTestCLICmd(clientCtx, cmd, tc.args) - s.Require().NoError(err) - if tc.expectErr { + + if tc.expectPass { s.Require().Error(err, out.String()) } else { s.Require().NoError(err, out.String()) From 0da7c2282c8c97f3cfc6d2eb0e542597035c4f21 Mon Sep 17 00:00:00 2001 From: matthiasmatt Date: Fri, 12 Aug 2022 11:18:05 -0300 Subject: [PATCH 27/28] test: Add test for simulation run --- app/antedecorators/gasless/gasless_test.go | 46 ++++++++++++++-------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/app/antedecorators/gasless/gasless_test.go b/app/antedecorators/gasless/gasless_test.go index 2059e372f..bff2e8613 100644 --- a/app/antedecorators/gasless/gasless_test.go +++ b/app/antedecorators/gasless/gasless_test.go @@ -70,30 +70,42 @@ func TestGaslessDecorator_Whitelisted(t *testing.T) { isWhitelisted bool shouldChangeMeter bool tx sdk.Tx + simulate bool }{ { - "whitelisted address", - true, - true, - TxWithPostPriceMsg{}, + /* name */ "whitelisted address", + /* isWhitelisted */ true, + /* shouldChangeMeter */ true, + /* tx */ TxWithPostPriceMsg{}, + /* simulate */ false, }, { - "whitelisted address but tx without price feed message", - true, - false, - TxWithoutPostPriceMsg{}, + /* name */ "whitelisted address, simulation", + /* isWhitelisted */ true, + /* shouldChangeMeter */ false, + /* tx */ TxWithPostPriceMsg{}, + /* simulate */ true, }, { - "not whitelisted address with post price tx", - false, - false, - TxWithPostPriceMsg{}, + /* name */ "whitelisted address but tx without price feed message", + /* isWhitelisted */ true, + /* shouldChangeMeter */ false, + /* tx */ TxWithoutPostPriceMsg{}, + /* simulate */ false, }, { - "not whitelisted address without post price tx", - false, - false, - TxWithoutPostPriceMsg{}, + /* name */ "not whitelisted address with post price tx", + /* isWhitelisted */ false, + /* shouldChangeMeter */ false, + /* tx */ TxWithPostPriceMsg{}, + /* simulate */ false, + }, + { + /* name */ "not whitelisted address without post price tx", + /* isWhitelisted */ false, + /* shouldChangeMeter */ false, + /* tx */ TxWithoutPostPriceMsg{}, + /* simulate */ false, }, } @@ -125,7 +137,7 @@ func TestGaslessDecorator_Whitelisted(t *testing.T) { chainedHandler := sdk.ChainAnteDecorators(anteDecorators...) - _, err := chainedHandler(ctx, tc.tx, false) + _, err := chainedHandler(ctx, tc.tx, tc.simulate) require.NoError(t, err) }) } From edfebca4c4cbba49c5896fd4e17ce33f1a9d0231 Mon Sep 17 00:00:00 2001 From: matthiasmatt Date: Fri, 12 Aug 2022 11:46:51 -0300 Subject: [PATCH 28/28] fix: Remove nil check for address of variable --- app/ante.go | 7 ++----- app/app.go | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/app/ante.go b/app/ante.go index 175880652..1586937ae 100644 --- a/app/ante.go +++ b/app/ante.go @@ -17,7 +17,7 @@ type AnteHandlerOptions struct { ante.HandlerOptions IBCKeeper *ibckeeper.Keeper - PricefeedKeeper *pricefeedkeeper.Keeper + PricefeedKeeper pricefeedkeeper.Keeper } /* @@ -41,9 +41,6 @@ func NewAnteHandler(options AnteHandlerOptions) (sdk.AnteHandler, error) { if options.IBCKeeper == nil { return nil, sdkerrors.Wrap(sdkerrors.ErrLogic, "ibc keeper is required for AnteHandler") } - if options.PricefeedKeeper == nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrLogic, "pricefeed keeper is required for ante builder") - } anteDecorators := []sdk.AnteDecorator{ ante.NewSetUpContextDecorator(), @@ -53,7 +50,7 @@ func NewAnteHandler(options AnteHandlerOptions) (sdk.AnteHandler, error) { ante.NewTxTimeoutHeightDecorator(), ante.NewValidateMemoDecorator(options.AccountKeeper), ante.NewConsumeGasForTxSizeDecorator(options.AccountKeeper), - gaslessante.NewGaslessDecorator(*options.PricefeedKeeper), + gaslessante.NewGaslessDecorator(options.PricefeedKeeper), feeante.NewDeductFeeDecorator(options.AccountKeeper, options.BankKeeper, options.FeegrantKeeper), // Replace fee ante from cosmos auth with a custom one. // SetPubKeyDecorator must be called before all signature verification decorators ante.NewSetPubKeyDecorator(options.AccountKeeper), diff --git a/app/app.go b/app/app.go index 7b00fecf2..e3e9d6909 100644 --- a/app/app.go +++ b/app/app.go @@ -754,7 +754,7 @@ func NewNibiruApp( SignModeHandler: encodingConfig.TxConfig.SignModeHandler(), SigGasConsumer: ante.DefaultSigVerificationGasConsumer, }, - PricefeedKeeper: &app.PricefeedKeeper, + PricefeedKeeper: app.PricefeedKeeper, IBCKeeper: app.IBCKeeper, })