From e2bdcaa9957dc6b208dc12c8f12b3807171945de Mon Sep 17 00:00:00 2001 From: VictorTrustyDev Date: Thu, 26 Sep 2024 17:24:42 +0700 Subject: [PATCH] patch 2 --- x/dymns/keeper/msg_server.go | 16 ++++- x/dymns/keeper/msg_server_accept_buy_order.go | 3 +- .../msg_server_accept_buy_order_test.go | 52 ++++++++++++++++ x/dymns/keeper/msg_server_cancel_buy_order.go | 3 +- .../msg_server_cancel_buy_order_test.go | 34 +++++++++++ .../keeper/msg_server_cancel_sell_order.go | 3 +- .../msg_server_cancel_sell_order_test.go | 14 +++++ x/dymns/keeper/msg_server_place_buy_order.go | 3 +- .../keeper/msg_server_place_buy_order_test.go | 54 +++++++++++++++++ x/dymns/keeper/msg_server_place_sell_order.go | 3 +- .../msg_server_place_sell_order_test.go | 43 +++++++++++++ x/dymns/keeper/msg_server_purchase_order.go | 3 +- .../keeper/msg_server_purchase_order_test.go | 60 +++++++++++++++++++ x/dymns/keeper/msg_server_test.go | 59 ++++++++++++++++++ x/dymns/keeper/msg_server_update_details.go | 3 +- .../keeper/msg_server_update_details_test.go | 25 ++++++++ .../msg_server_update_resolve_address.go | 3 +- .../msg_server_update_resolve_address_test.go | 31 ++++++++++ 18 files changed, 402 insertions(+), 10 deletions(-) diff --git a/x/dymns/keeper/msg_server.go b/x/dymns/keeper/msg_server.go index 66732c9b3..97d258403 100644 --- a/x/dymns/keeper/msg_server.go +++ b/x/dymns/keeper/msg_server.go @@ -1,6 +1,8 @@ package keeper import ( + "fmt" + sdk "github.com/cosmos/cosmos-sdk/types" dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types" ) @@ -19,9 +21,19 @@ func NewMsgServerImpl(keeper Keeper) dymnstypes.MsgServer { // consumeMinimumGas consumes the minimum gas // if the consumed gas during tx is less than the minimum gas. -func consumeMinimumGas(ctx sdk.Context, minimumGas uint64, actionName string) { +// The original consumed gas should be captured from gas meter before invoke message execution. +// This function will panic if the gas meter consumed gas is less than the original consumed gas. +func consumeMinimumGas(ctx sdk.Context, minimumGas, originalConsumedGas uint64, actionName string) { if minimumGas > 0 { - if consumedGas := ctx.GasMeter().GasConsumed(); consumedGas < minimumGas { + laterConsumedGas := ctx.GasMeter().GasConsumed() + if laterConsumedGas < originalConsumedGas { + // unexpect gas consumption + panic(fmt.Sprintf( + "later gas is less than original gas: %d < %d", + laterConsumedGas, originalConsumedGas, + )) + } + if consumedGas := laterConsumedGas - originalConsumedGas; consumedGas < minimumGas { // we only consume the gas that is needed to reach the target minimum gas gasToConsume := minimumGas - consumedGas diff --git a/x/dymns/keeper/msg_server_accept_buy_order.go b/x/dymns/keeper/msg_server_accept_buy_order.go index b241536e3..c0af94328 100644 --- a/x/dymns/keeper/msg_server_accept_buy_order.go +++ b/x/dymns/keeper/msg_server_accept_buy_order.go @@ -16,6 +16,7 @@ import ( // performed by the owner of the asset. func (k msgServer) AcceptBuyOrder(goCtx context.Context, msg *dymnstypes.MsgAcceptBuyOrder) (*dymnstypes.MsgAcceptBuyOrderResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) + originalConsumedGas := ctx.GasMeter().GasConsumed() if err := msg.ValidateBasic(); err != nil { return nil, err @@ -47,7 +48,7 @@ func (k msgServer) AcceptBuyOrder(goCtx context.Context, msg *dymnstypes.MsgAcce } // charge protocol fee - consumeMinimumGas(ctx, dymnstypes.OpGasUpdateBuyOrder, "AcceptBuyOrder") + consumeMinimumGas(ctx, dymnstypes.OpGasUpdateBuyOrder, originalConsumedGas, "AcceptBuyOrder") return resp, nil } diff --git a/x/dymns/keeper/msg_server_accept_buy_order_test.go b/x/dymns/keeper/msg_server_accept_buy_order_test.go index e56ac2d30..f80191111 100644 --- a/x/dymns/keeper/msg_server_accept_buy_order_test.go +++ b/x/dymns/keeper/msg_server_accept_buy_order_test.go @@ -677,6 +677,30 @@ func (s *KeeperTestSuite) Test_msgServer_AcceptBuyOrder_Type_DymName() { wantLaterOwnerBalance: sdkmath.NewInt(2).Mul(priceMultiplier), wantMinConsumeGas: dymnstypes.OpGasUpdateBuyOrder, }, + { + name: "pass - independently charge gas", + existingDymName: dymName, + existingOffer: offer, + buyOrderId: offer.Id, + owner: dymName.Owner, + minAccept: offer.OfferPrice, + originalModuleBalance: offer.OfferPrice.Amount, + originalOwnerBalance: sdk.NewInt(0), + preRunSetupFunc: func(s *KeeperTestSuite) { + s.ctx.GasMeter().ConsumeGas(100_000_000, "simulate previous run") + }, + wantErr: false, + wantLaterOffer: nil, + wantLaterDymName: &dymnstypes.DymName{ + Name: dymName.Name, + Owner: offer.Buyer, + Controller: offer.Buyer, + ExpireAt: dymName.ExpireAt, + }, + wantLaterModuleBalance: sdkmath.ZeroInt(), + wantLaterOwnerBalance: offer.OfferPrice.Amount, + wantMinConsumeGas: 100_000_000 + dymnstypes.OpGasUpdateBuyOrder, + }, } for _, tt := range tests { s.Run(tt.name, func() { @@ -1453,6 +1477,34 @@ func (s *KeeperTestSuite) Test_msgServer_AcceptBuyOrder_Type_Alias() { wantLaterOwnerBalance: sdk.NewInt(2), wantMinConsumeGas: dymnstypes.OpGasUpdateBuyOrder, }, + { + name: "pass - independently charge gas", + existingRollApps: []rollapp{rollApp_One_By1_SingleAlias, rollApp_Two_By2_SingleAlias}, + existingOffer: offerAliasOfRollAppOne, + buyOrderId: offerAliasOfRollAppOne.Id, + owner: rollApp_One_By1_SingleAlias.owner, + minAccept: offerAliasOfRollAppOne.OfferPrice, + originalModuleBalance: offerAliasOfRollAppOne.OfferPrice.Amount, + originalOwnerBalance: sdk.NewInt(0), + preRunSetupFunc: func(s *KeeperTestSuite) { + s.ctx.GasMeter().ConsumeGas(100_000_000, "simulate previous run") + }, + wantErr: false, + wantLaterOffer: nil, + wantLaterRollApps: []rollapp{ + { + rollAppId: rollApp_One_By1_SingleAlias.rollAppId, + aliases: []string{}, + }, + { + rollAppId: rollApp_Two_By2_SingleAlias.rollAppId, + aliases: append(rollApp_Two_By2_SingleAlias.aliases, offerAliasOfRollAppOne.AssetId), + }, + }, + wantLaterModuleBalance: sdk.NewInt(0), + wantLaterOwnerBalance: offerAliasOfRollAppOne.OfferPrice.Amount, + wantMinConsumeGas: 100_000_000 + dymnstypes.OpGasUpdateBuyOrder, + }, } for _, tt := range tests { s.Run(tt.name, func() { diff --git a/x/dymns/keeper/msg_server_cancel_buy_order.go b/x/dymns/keeper/msg_server_cancel_buy_order.go index eef165336..1d15f66c5 100644 --- a/x/dymns/keeper/msg_server_cancel_buy_order.go +++ b/x/dymns/keeper/msg_server_cancel_buy_order.go @@ -14,6 +14,7 @@ import ( // handles canceling a Buy-Order, performed by the buyer who placed the offer. func (k msgServer) CancelBuyOrder(goCtx context.Context, msg *dymnstypes.MsgCancelBuyOrder) (*dymnstypes.MsgCancelBuyOrderResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) + originalConsumedGas := ctx.GasMeter().GasConsumed() if err := msg.ValidateBasic(); err != nil { return nil, err @@ -41,7 +42,7 @@ func (k msgServer) CancelBuyOrder(goCtx context.Context, msg *dymnstypes.MsgCanc } // charge protocol fee - consumeMinimumGas(ctx, dymnstypes.OpGasCloseBuyOrder, "CancelBuyOrder") + consumeMinimumGas(ctx, dymnstypes.OpGasCloseBuyOrder, originalConsumedGas, "CancelBuyOrder") return resp, nil } diff --git a/x/dymns/keeper/msg_server_cancel_buy_order_test.go b/x/dymns/keeper/msg_server_cancel_buy_order_test.go index 669648ab3..0b4832b44 100644 --- a/x/dymns/keeper/msg_server_cancel_buy_order_test.go +++ b/x/dymns/keeper/msg_server_cancel_buy_order_test.go @@ -237,6 +237,23 @@ func (s *KeeperTestSuite) Test_msgServer_CancelBuyOrder_DymName() { wantLaterBuyerBalance: sdk.NewInt(2), wantMinConsumeGas: 1, }, + { + name: "pass - independently charge gas", + existingDymName: dymName, + existingOffer: offer, + buyOrderId: offer.Id, + buyer: offer.Buyer, + originalModuleBalance: offer.OfferPrice.Amount, + originalBuyerBalance: sdk.NewInt(0), + preRunSetupFunc: func(s *KeeperTestSuite) { + s.ctx.GasMeter().ConsumeGas(100_000_000, "simulate previous run") + }, + wantErr: false, + wantLaterOffer: nil, + wantLaterModuleBalance: sdk.NewInt(0), + wantLaterBuyerBalance: offer.OfferPrice.Amount, + wantMinConsumeGas: 100_000_000 + dymnstypes.OpGasCloseBuyOrder, + }, } for _, tt := range tests { s.Run(tt.name, func() { @@ -615,6 +632,23 @@ func (s *KeeperTestSuite) Test_msgServer_CancelBuyOrder_Alias() { wantLaterBuyerBalance: sdk.NewInt(2), wantMinConsumeGas: 1, }, + { + name: "pass - independently charge gas", + existingRollApps: []rollapp{rollApp_One_By1, rollApp_Two_By2}, + existingOffer: offerAliasOfRollAppOne, + buyOrderId: offerAliasOfRollAppOne.Id, + buyer: offerAliasOfRollAppOne.Buyer, + originalModuleBalance: offerAliasOfRollAppOne.OfferPrice.Amount, + originalBuyerBalance: sdk.NewInt(0), + preRunSetupFunc: func(s *KeeperTestSuite) { + s.ctx.GasMeter().ConsumeGas(100_000_000, "simulate previous run") + }, + wantErr: false, + wantLaterOffer: nil, + wantLaterModuleBalance: sdk.NewInt(0), + wantLaterBuyerBalance: offerAliasOfRollAppOne.OfferPrice.Amount, + wantMinConsumeGas: 100_000_000 + dymnstypes.OpGasCloseBuyOrder, + }, } for _, tt := range tests { s.Run(tt.name, func() { diff --git a/x/dymns/keeper/msg_server_cancel_sell_order.go b/x/dymns/keeper/msg_server_cancel_sell_order.go index a6b1d29cc..035017283 100644 --- a/x/dymns/keeper/msg_server_cancel_sell_order.go +++ b/x/dymns/keeper/msg_server_cancel_sell_order.go @@ -16,6 +16,7 @@ import ( // Can only be performed if no one has placed a bid on the asset. func (k msgServer) CancelSellOrder(goCtx context.Context, msg *dymnstypes.MsgCancelSellOrder) (*dymnstypes.MsgCancelSellOrderResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) + originalConsumedGas := ctx.GasMeter().GasConsumed() if err := msg.ValidateBasic(); err != nil { return nil, err @@ -38,7 +39,7 @@ func (k msgServer) CancelSellOrder(goCtx context.Context, msg *dymnstypes.MsgCan } // charge protocol fee - consumeMinimumGas(ctx, dymnstypes.OpGasCloseSellOrder, "CancelSellOrder") + consumeMinimumGas(ctx, dymnstypes.OpGasCloseSellOrder, originalConsumedGas, "CancelSellOrder") return resp, nil } diff --git a/x/dymns/keeper/msg_server_cancel_sell_order_test.go b/x/dymns/keeper/msg_server_cancel_sell_order_test.go index 2a9c9f5e1..230672150 100644 --- a/x/dymns/keeper/msg_server_cancel_sell_order_test.go +++ b/x/dymns/keeper/msg_server_cancel_sell_order_test.go @@ -214,6 +214,9 @@ func (s *KeeperTestSuite) Test_msgServer_CancelSellOrder_DymName() { }) s.Run("can cancel if satisfied conditions", func() { + const previousRunGasConsumed = 100_000_000 + s.ctx.GasMeter().ConsumeGas(previousRunGasConsumed, "simulate previous run") + moduleParams := s.dymNsKeeper.GetParams(s.ctx) moduleParams.Misc.EnableTradingName = false // allowed to cancel even if trading is disabled s.Require().NoError(s.dymNsKeeper.SetParams(s.ctx, moduleParams)) @@ -256,6 +259,10 @@ func (s *KeeperTestSuite) Test_msgServer_CancelSellOrder_DymName() { s.ctx.GasMeter().GasConsumed(), dymnstypes.OpGasCloseSellOrder, "should consume params gas", ) + s.GreaterOrEqual( + s.ctx.GasMeter().GasConsumed(), previousRunGasConsumed+dymnstypes.OpGasCloseSellOrder, + "gas consumption should be stacked with previous run", + ) }) } @@ -438,6 +445,9 @@ func (s *KeeperTestSuite) Test_msgServer_CancelSellOrder_Alias() { }) s.Run("can cancel if satisfied conditions", func() { + const previousRunGasConsumed = 100_000_000 + s.ctx.GasMeter().ConsumeGas(previousRunGasConsumed, "simulate previous run") + moduleParams := s.dymNsKeeper.GetParams(s.ctx) moduleParams.Misc.EnableTradingAlias = false // allowed to cancel even if trading is disabled s.Require().NoError(s.dymNsKeeper.SetParams(s.ctx, moduleParams)) @@ -480,6 +490,10 @@ func (s *KeeperTestSuite) Test_msgServer_CancelSellOrder_Alias() { s.ctx.GasMeter().GasConsumed(), dymnstypes.OpGasCloseSellOrder, "should consume params gas", ) + s.GreaterOrEqual( + s.ctx.GasMeter().GasConsumed(), previousRunGasConsumed+dymnstypes.OpGasCloseSellOrder, + "gas consumption should be stacked with previous run", + ) s.requireAlias(rollapp_1_ofOwner.alias).LinkedToRollApp(rollapp_1_ofOwner.rollAppId) }) diff --git a/x/dymns/keeper/msg_server_place_buy_order.go b/x/dymns/keeper/msg_server_place_buy_order.go index d0702343b..197e4a77f 100644 --- a/x/dymns/keeper/msg_server_place_buy_order.go +++ b/x/dymns/keeper/msg_server_place_buy_order.go @@ -14,6 +14,7 @@ import ( // handles creating an offer to buy a Dym-Name/Alias, performed by the buyer. func (k msgServer) PlaceBuyOrder(goCtx context.Context, msg *dymnstypes.MsgPlaceBuyOrder) (*dymnstypes.MsgPlaceBuyOrderResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) + originalConsumedGas := ctx.GasMeter().GasConsumed() if err := msg.ValidateBasic(); err != nil { return nil, err @@ -45,7 +46,7 @@ func (k msgServer) PlaceBuyOrder(goCtx context.Context, msg *dymnstypes.MsgPlace } else { minimumTxGasRequired = dymnstypes.OpGasPutBuyOrder } - consumeMinimumGas(ctx, minimumTxGasRequired, "PlaceBuyOrder") + consumeMinimumGas(ctx, minimumTxGasRequired, originalConsumedGas, "PlaceBuyOrder") return resp, nil } diff --git a/x/dymns/keeper/msg_server_place_buy_order_test.go b/x/dymns/keeper/msg_server_place_buy_order_test.go index 93ce0e397..39392ed21 100644 --- a/x/dymns/keeper/msg_server_place_buy_order_test.go +++ b/x/dymns/keeper/msg_server_place_buy_order_test.go @@ -797,6 +797,32 @@ func (s *KeeperTestSuite) Test_msgServer_PlaceBuyOrder_DymName() { s.Equal([]string{"102", "103"}, orderIds.OrderIds) }, }, + { + name: "pass - independently charge gas", + existingDymName: dymName, + existingOffer: nil, + dymName: dymName.Name, + buyer: buyerA, + offer: minOfferPriceCoin, + existingBuyOrderId: "", + originalModuleBalance: sdk.NewInt(5), + originalBuyerBalance: minOfferPriceCoin.Amount.AddRaw(2), + preRunSetupFunc: func(s *KeeperTestSuite) { + s.ctx.GasMeter().ConsumeGas(100_000_000, "simulate previous run") + }, + wantErr: false, + wantBuyOrderId: "101", + wantLaterOffer: &dymnstypes.BuyOrder{ + Id: "101", + AssetId: dymName.Name, + AssetType: dymnstypes.TypeName, + Buyer: buyerA, + OfferPrice: minOfferPriceCoin, + }, + wantLaterModuleBalance: minOfferPriceCoin.Amount.AddRaw(5), + wantLaterBuyerBalance: sdk.NewInt(2), + wantMinConsumeGas: 100_000_000 + dymnstypes.OpGasPutBuyOrder, + }, } for _, tt := range tests { s.Run(tt.name, func() { @@ -1856,6 +1882,34 @@ func (s *KeeperTestSuite) Test_msgServer_PlaceBuyOrder_Alias() { s.Equal([]string{"202", "203"}, orderIds.OrderIds) }, }, + { + name: "pass - independently charge gas", + existingRollApps: []rollapp{rollApp_1_by1_asSrc, rollApp_2_by2_asDest}, + existingOffer: nil, + alias: rollApp_1_by1_asSrc.alias, + buyer: rollApp_2_by2_asDest.creator, + dstRollAppId: rollApp_2_by2_asDest.rollAppID, + offer: minOfferPriceCoin, + existingBuyOrderId: "", + originalModuleBalance: sdk.NewInt(5), + originalBuyerBalance: minOfferPriceCoin.Amount.AddRaw(2), + preRunSetupFunc: func(s *KeeperTestSuite) { + s.ctx.GasMeter().ConsumeGas(100_000_000, "simulate previous run") + }, + wantErr: false, + wantBuyOrderId: "201", + wantLaterOffer: &dymnstypes.BuyOrder{ + Id: "201", + AssetId: rollApp_1_by1_asSrc.alias, + AssetType: dymnstypes.TypeAlias, + Params: []string{rollApp_2_by2_asDest.rollAppID}, + Buyer: rollApp_2_by2_asDest.creator, + OfferPrice: minOfferPriceCoin, + }, + wantLaterModuleBalance: minOfferPriceCoin.Amount.AddRaw(5), + wantLaterBuyerBalance: sdk.NewInt(2), + wantMinConsumeGas: 100_000_000 + dymnstypes.OpGasPutBuyOrder, + }, } for _, tt := range tests { s.Run(tt.name, func() { diff --git a/x/dymns/keeper/msg_server_place_sell_order.go b/x/dymns/keeper/msg_server_place_sell_order.go index 8f102cfcd..980ec787e 100644 --- a/x/dymns/keeper/msg_server_place_sell_order.go +++ b/x/dymns/keeper/msg_server_place_sell_order.go @@ -14,6 +14,7 @@ import ( // handles creating a Sell-Order that advertise a Dym-Name/Alias is for sale, performed by the owner. func (k msgServer) PlaceSellOrder(goCtx context.Context, msg *dymnstypes.MsgPlaceSellOrder) (*dymnstypes.MsgPlaceSellOrderResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) + originalConsumedGas := ctx.GasMeter().GasConsumed() if err := msg.ValidateBasic(); err != nil { return nil, err @@ -40,7 +41,7 @@ func (k msgServer) PlaceSellOrder(goCtx context.Context, msg *dymnstypes.MsgPlac // Charge protocol fee. // The protocol fee mechanism is used to prevent spamming to the network. - consumeMinimumGas(ctx, dymnstypes.OpGasPlaceSellOrder, "PlaceSellOrder") + consumeMinimumGas(ctx, dymnstypes.OpGasPlaceSellOrder, originalConsumedGas, "PlaceSellOrder") return resp, nil } diff --git a/x/dymns/keeper/msg_server_place_sell_order_test.go b/x/dymns/keeper/msg_server_place_sell_order_test.go index 262629445..33e6d6e77 100644 --- a/x/dymns/keeper/msg_server_place_sell_order_test.go +++ b/x/dymns/keeper/msg_server_place_sell_order_test.go @@ -52,6 +52,7 @@ func (s *KeeperTestSuite) Test_msgServer_PlaceSellOrder_DymName() { preRunSetup func(*KeeperTestSuite) wantErr bool wantErrContains string + afterRunFunc func(*KeeperTestSuite) }{ { name: "fail - Dym-Name does not exists", @@ -186,6 +187,21 @@ func (s *KeeperTestSuite) Test_msgServer_PlaceSellOrder_DymName() { wantErr: true, wantErrContains: "trading of Dym-Name is disabled", }, + { + name: "pass - independently charge gas", + dymNameExpiryOffsetDays: 9999, + minPrice: coin100, + sellPrice: nil, + preRunSetup: func(s *KeeperTestSuite) { + s.ctx.GasMeter().ConsumeGas(100_000_000, "simulate previous run") + }, + afterRunFunc: func(s *KeeperTestSuite) { + s.Require().GreaterOrEqual( + s.ctx.GasMeter().GasConsumed(), 100_000_000+dymnstypes.OpGasPlaceSellOrder, + "gas consumption should be stacked", + ) + }, + }, } for _, tt := range tests { s.Run(tt.name, func() { @@ -251,6 +267,12 @@ func (s *KeeperTestSuite) Test_msgServer_PlaceSellOrder_DymName() { }, *laterDymName, "Dym-Name record should not be changed in any case") }() + defer func() { + if tt.afterRunFunc != nil { + tt.afterRunFunc(s) + } + }() + if tt.wantErr { s.Require().NotEmpty(tt.wantErrContains, "mis-configured test case") s.Require().Error(err) @@ -349,6 +371,7 @@ func (s *KeeperTestSuite) Test_msgServer_PlaceSellOrder_Alias() { preRunSetup func(*KeeperTestSuite) wantErr bool wantErrContains string + afterRunFunc func(*KeeperTestSuite) }{ { name: "fail - alias does not exists", @@ -480,6 +503,20 @@ func (s *KeeperTestSuite) Test_msgServer_PlaceSellOrder_Alias() { wantErr: true, wantErrContains: "trading of Alias is disabled", }, + { + name: "pass - independently charge gas", + minPrice: coin100, + sellPrice: nil, + preRunSetup: func(s *KeeperTestSuite) { + s.ctx.GasMeter().ConsumeGas(100_000_000, "simulate previous run") + }, + afterRunFunc: func(s *KeeperTestSuite) { + s.Require().GreaterOrEqual( + s.ctx.GasMeter().GasConsumed(), 100_000_000+dymnstypes.OpGasPlaceSellOrder, + "gas consumption should be stacked", + ) + }, + }, } for _, tt := range tests { s.Run(tt.name, func() { @@ -533,6 +570,12 @@ func (s *KeeperTestSuite) Test_msgServer_PlaceSellOrder_Alias() { } }() + defer func() { + if tt.afterRunFunc != nil { + tt.afterRunFunc(s) + } + }() + if tt.wantErr { s.Require().NotEmpty(tt.wantErrContains, "mis-configured test case") s.Require().Error(err) diff --git a/x/dymns/keeper/msg_server_purchase_order.go b/x/dymns/keeper/msg_server_purchase_order.go index 35fcfd2cb..859985186 100644 --- a/x/dymns/keeper/msg_server_purchase_order.go +++ b/x/dymns/keeper/msg_server_purchase_order.go @@ -14,6 +14,7 @@ import ( // handles purchasing a Dym-Name/Alias from a Sell-Order, performed by the buyer. func (k msgServer) PurchaseOrder(goCtx context.Context, msg *dymnstypes.MsgPurchaseOrder) (*dymnstypes.MsgPurchaseOrderResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) + originalConsumedGas := ctx.GasMeter().GasConsumed() if err := msg.ValidateBasic(); err != nil { return nil, err @@ -38,7 +39,7 @@ func (k msgServer) PurchaseOrder(goCtx context.Context, msg *dymnstypes.MsgPurch } // charge protocol fee - consumeMinimumGas(ctx, dymnstypes.OpGasPlaceBidOnSellOrder, "PurchaseOrder") + consumeMinimumGas(ctx, dymnstypes.OpGasPlaceBidOnSellOrder, originalConsumedGas, "PurchaseOrder") return resp, nil } diff --git a/x/dymns/keeper/msg_server_purchase_order_test.go b/x/dymns/keeper/msg_server_purchase_order_test.go index fa2911efd..8805b3358 100644 --- a/x/dymns/keeper/msg_server_purchase_order_test.go +++ b/x/dymns/keeper/msg_server_purchase_order_test.go @@ -2,6 +2,7 @@ package keeper_test import ( "fmt" + "time" "github.com/dymensionxyz/sdk-utils/utils/uptr" @@ -567,6 +568,35 @@ func (s *KeeperTestSuite) Test_msgServer_PurchaseOrder_DymName() { }) s.Require().ErrorContains(err, "unmet precondition") }) + + s.Run("independently charge gas", func() { + s.RefreshContext() + + s.ctx.GasMeter().ConsumeGas(100_000_000, "simulate previous run") + + s.setDymNameWithFunctionsAfter(dymName) + so := dymnstypes.SellOrder{ + AssetId: dymName.Name, + AssetType: dymnstypes.TypeName, + MinPrice: s.coin(minPrice), + ExpireAt: s.now.Add(time.Second).Unix(), + } + err := s.dymNsKeeper.SetSellOrder(s.ctx, so) + s.Require().NoError(err) + s.mintToAccount(buyerA, minPrice) + + _, err = dymnskeeper.NewMsgServerImpl(s.dymNsKeeper).PurchaseOrder(s.ctx, &dymnstypes.MsgPurchaseOrder{ + AssetId: "my-name", + AssetType: dymnstypes.TypeName, + Offer: s.coin(minPrice), + Buyer: buyerA, + }) + s.Require().NoError(err) + s.Require().GreaterOrEqual( + s.ctx.GasMeter().GasConsumed(), 100_000_000+dymnstypes.OpGasPlaceBidOnSellOrder, + "should consume params gas", + ) + }) } //goland:noinspection GoSnakeCaseUsage @@ -1243,4 +1273,34 @@ func (s *KeeperTestSuite) Test_msgServer_PurchaseOrder_Alias() { }) s.Require().ErrorContains(err, "trading of Alias is disabled") }) + + s.Run("independently charge gas", func() { + s.RefreshContext() + + s.ctx.GasMeter().ConsumeGas(100_000_000, "simulate previous run") + + s.persistRollApp(rollApp_1_byOwner_asSrc) + s.persistRollApp(rollApp_2_byBuyer_asDst) + so := s.newAliasSellOrder(rollApp_1_byOwner_asSrc.alias). + WithMinPrice(minPrice). + WithSellPrice(300). + BuildP() + err := s.dymNsKeeper.SetSellOrder(s.ctx, *so) + s.Require().NoError(err) + s.mintToAccount(creator_2_asBuyer, 100) + + _, err = dymnskeeper.NewMsgServerImpl(s.dymNsKeeper).PurchaseOrder(s.ctx, &dymnstypes.MsgPurchaseOrder{ + AssetId: "alias", + AssetType: dymnstypes.TypeAlias, + Params: []string{rollApp_2_byBuyer_asDst.rollAppId}, + Offer: s.coin(minPrice), + Buyer: creator_2_asBuyer, + }) + s.Require().NoError(err) + + s.Require().GreaterOrEqual( + s.ctx.GasMeter().GasConsumed(), 100_000_000+dymnstypes.OpGasPlaceBidOnSellOrder, + "gas consumption should be stacked", + ) + }) } diff --git a/x/dymns/keeper/msg_server_test.go b/x/dymns/keeper/msg_server_test.go index 6ffeb3e47..f6e4ab702 100644 --- a/x/dymns/keeper/msg_server_test.go +++ b/x/dymns/keeper/msg_server_test.go @@ -4,6 +4,8 @@ import ( "testing" "time" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" ) @@ -14,3 +16,60 @@ func TestTime(t *testing.T) { "if mis-match, 100% sure will causes AppHash", ) } + +func Test_consumeMinimumGas(t *testing.T) { + tests := []struct { + name string + originalConsumedGas uint64 + overrideConsumedGas *uint64 + minimumGas uint64 + wantPanic bool + wantGasMeterConsumedGas uint64 + }{ + { + name: "pass - normal gas consumption", + originalConsumedGas: 0, + minimumGas: 1_000, + wantGasMeterConsumedGas: 1_000, + }, + { + name: "pass - should be stacked with previous run", + originalConsumedGas: 20_000, + minimumGas: 1_000, + wantGasMeterConsumedGas: 21_000, + }, + { + name: "fail - should panic if later consumed gas is less than original consumed gas", + originalConsumedGas: 2, + overrideConsumedGas: func() *uint64 { + v := uint64(1) + return &v + }(), + minimumGas: 1_000, + wantPanic: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ctx := sdk.Context{}.WithGasMeter(sdk.NewInfiniteGasMeter()) + + originalConsumedGas := tt.originalConsumedGas + if tt.overrideConsumedGas != nil { + originalConsumedGas = *tt.overrideConsumedGas + } + if originalConsumedGas > 0 { + ctx.GasMeter().ConsumeGas(originalConsumedGas, "simulate pre-run gas consumption") + } + + if tt.wantPanic { + require.Panics(t, func() { + consumeMinimumGas(ctx, tt.minimumGas, tt.originalConsumedGas, "test") + }) + return + } + + consumeMinimumGas(ctx, tt.minimumGas, tt.originalConsumedGas, "test") + require.Equal(t, tt.wantGasMeterConsumedGas, ctx.GasMeter().GasConsumed()) + }) + } +} diff --git a/x/dymns/keeper/msg_server_update_details.go b/x/dymns/keeper/msg_server_update_details.go index b11946741..c45ae8e78 100644 --- a/x/dymns/keeper/msg_server_update_details.go +++ b/x/dymns/keeper/msg_server_update_details.go @@ -14,6 +14,7 @@ import ( // handles updating Dym-Name details, performed by the controller. func (k msgServer) UpdateDetails(goCtx context.Context, msg *dymnstypes.MsgUpdateDetails) (*dymnstypes.MsgUpdateDetailsResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) + originalConsumedGas := ctx.GasMeter().GasConsumed() dymName, err := k.validateUpdateDetails(ctx, msg) if err != nil { @@ -56,7 +57,7 @@ func (k msgServer) UpdateDetails(goCtx context.Context, msg *dymnstypes.MsgUpdat } // charge protocol fee - consumeMinimumGas(ctx, minimumTxGasRequired, "UpdateDetails") + consumeMinimumGas(ctx, minimumTxGasRequired, originalConsumedGas, "UpdateDetails") return &dymnstypes.MsgUpdateDetailsResponse{}, nil } diff --git a/x/dymns/keeper/msg_server_update_details_test.go b/x/dymns/keeper/msg_server_update_details_test.go index 2b0d7d9d3..dd2d00199 100644 --- a/x/dymns/keeper/msg_server_update_details_test.go +++ b/x/dymns/keeper/msg_server_update_details_test.go @@ -594,6 +594,31 @@ func (s *KeeperTestSuite) Test_msgServer_UpdateDetails() { s.requireFallbackAddress(controllerAcc.fallback()).notMappedToAnyDymName() }, }, + { + name: "pass - independently charge gas", + dymName: &dymnstypes.DymName{ + Owner: ownerA, + Controller: controllerA, + ExpireAt: s.now.Unix() + 100, + Contact: "old-contact@example.com", + }, + preTestFunc: func(s *KeeperTestSuite) { + s.ctx.GasMeter().ConsumeGas(100_000_000, "simulate previous run") + }, + msg: &dymnstypes.MsgUpdateDetails{ + Contact: "new-contact@example.com", + Controller: controllerA, + }, + wantErr: false, + wantDymName: &dymnstypes.DymName{ + Owner: ownerA, + Controller: controllerA, + ExpireAt: s.now.Unix() + 100, + Contact: "new-contact@example.com", + }, + wantMinGasConsumed: 100_000_000 + dymnstypes.OpGasUpdateContact, + postTestFunc: func(*KeeperTestSuite) {}, + }, } for _, tt := range tests { s.Run(tt.name, func() { diff --git a/x/dymns/keeper/msg_server_update_resolve_address.go b/x/dymns/keeper/msg_server_update_resolve_address.go index 3cb89f901..4fb0a142f 100644 --- a/x/dymns/keeper/msg_server_update_resolve_address.go +++ b/x/dymns/keeper/msg_server_update_resolve_address.go @@ -17,6 +17,7 @@ import ( // handles updating Dym-Name-Address resolution configuration, performed by the controller. func (k msgServer) UpdateResolveAddress(goCtx context.Context, msg *dymnstypes.MsgUpdateResolveAddress) (*dymnstypes.MsgUpdateResolveAddressResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) + originalConsumedGas := ctx.GasMeter().GasConsumed() dymName, err := k.validateUpdateResolveAddress(ctx, msg) if err != nil { @@ -96,7 +97,7 @@ func (k msgServer) UpdateResolveAddress(goCtx context.Context, msg *dymnstypes.M // Charge protocol fee. // The protocol fee mechanism is used to prevent spamming to the network. - consumeMinimumGas(ctx, minimumTxGasRequired, "UpdateResolveAddress") + consumeMinimumGas(ctx, minimumTxGasRequired, originalConsumedGas, "UpdateResolveAddress") return &dymnstypes.MsgUpdateResolveAddressResponse{}, nil } diff --git a/x/dymns/keeper/msg_server_update_resolve_address_test.go b/x/dymns/keeper/msg_server_update_resolve_address_test.go index 86ceeb542..217dc0f7f 100644 --- a/x/dymns/keeper/msg_server_update_resolve_address_test.go +++ b/x/dymns/keeper/msg_server_update_resolve_address_test.go @@ -1504,6 +1504,37 @@ func (s *KeeperTestSuite) Test_msgServer_UpdateResolveAddress() { s.requireFallbackAddress(anotherAcc.fallback()).notMappedToAnyDymName() }, }, + { + name: "pass - independently charge gas", + dymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + }, + preTestFunc: func(s *KeeperTestSuite) { + s.ctx.GasMeter().ConsumeGas(100_000_000, "simulate previous run") + }, + msg: &dymnstypes.MsgUpdateResolveAddress{ + ResolveTo: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + }, + wantErr: false, + wantDymName: &dymnstypes.DymName{ + Owner: ownerAcc.bech32(), + Controller: controllerAcc.bech32(), + ExpireAt: s.now.Unix() + 100, + Configs: []dymnstypes.DymNameConfig{ + { + Type: dymnstypes.DymNameConfigType_DCT_NAME, + ChainId: "", + Path: "", + Value: ownerAcc.bech32(), + }, + }, + }, + wantMinGasConsumed: 100_000_000 + dymnstypes.OpGasConfig, + postTestFunc: func(*KeeperTestSuite) {}, + }, } for _, tt := range tests { s.Run(tt.name, func() {