From 3d2b38f7480c19d77619a2b8cd45bef6c8232e8f Mon Sep 17 00:00:00 2001 From: jamilahmadzai Date: Sat, 13 Jun 2026 00:18:04 +0200 Subject: [PATCH] fix: guard v47 upgrade fee bypass --- app/fee_deduct.go | 36 +++++++++++++++++-------- app/fee_deduct_test.go | 61 ++++++++++++++++++++++++++++++++++++++++++ app/upgrade_v47.go | 25 ++++++++++++++--- 3 files changed, 107 insertions(+), 15 deletions(-) create mode 100644 app/fee_deduct_test.go diff --git a/app/fee_deduct.go b/app/fee_deduct.go index 413a9e7..5be36e4 100644 --- a/app/fee_deduct.go +++ b/app/fee_deduct.go @@ -93,17 +93,9 @@ func (dfd DeductFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bo feeGranter := feeTx.FeeGranter() admin := dfd.stakingKeeper.GetGlobalAdminAddress(ctx) meidAdmin := dfd.stakingKeeper.GetMeidAdminAddress(ctx) - freeGas := feePayer.String() == admin || feePayer.String() == meidAdmin - - for _, msg := range feeTx.GetMsgs() { - switch msg.(type) { - case *checkintypes.MsgCheckIn: - freeGas = true - case *grouptypes.MsgUpdateGroupMembers: - freeGas = true - } - break - } + freeGas := feePayer.String() == admin || + feePayer.String() == meidAdmin || + messagesAllowFreeGas(feeTx.GetMsgs()) if !simulate && !freeGas { _, priority, err = dfd.txFeeChecker(ctx, tx) @@ -219,6 +211,28 @@ func (dfd DeductFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bo return next(newCtx, tx, simulate) } +func messagesAllowFreeGas(msgs []sdk.Msg) bool { + if len(msgs) == 0 { + return false + } + + for _, msg := range msgs { + if !messageAllowsFreeGas(msg) { + return false + } + } + return true +} + +func messageAllowsFreeGas(msg sdk.Msg) bool { + switch msg.(type) { + case *checkintypes.MsgCheckIn, *grouptypes.MsgUpdateGroupMembers: + return true + default: + return false + } +} + // checkTxFeeWithValidatorMinGasPrices implements the default fee logic, where the minimum price per // unit of gas is fixed and set by each validator, can the tx priority is computed from the gas price. func checkTxFeeWithValidatorMinGasPrices(ctx sdk.Context, tx sdk.Tx) (sdk.Coins, int64, error) { diff --git a/app/fee_deduct_test.go b/app/fee_deduct_test.go new file mode 100644 index 0000000..c63360a --- /dev/null +++ b/app/fee_deduct_test.go @@ -0,0 +1,61 @@ +package app + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + grouptypes "github.com/cosmos/cosmos-sdk/x/group" + "github.com/stretchr/testify/require" + + checkintypes "me-chain/x/checkin/types" +) + +func TestMessagesAllowFreeGas(t *testing.T) { + checkIn := &checkintypes.MsgCheckIn{} + groupUpdate := &grouptypes.MsgUpdateGroupMembers{} + send := &banktypes.MsgSend{} + + tests := []struct { + name string + msgs []sdk.Msg + want bool + }{ + { + name: "check in only", + msgs: []sdk.Msg{checkIn}, + want: true, + }, + { + name: "group update only", + msgs: []sdk.Msg{groupUpdate}, + want: true, + }, + { + name: "multiple free gas messages", + msgs: []sdk.Msg{checkIn, groupUpdate}, + want: true, + }, + { + name: "mixed check in and paid message", + msgs: []sdk.Msg{checkIn, send}, + want: false, + }, + { + name: "paid message only", + msgs: []sdk.Msg{send}, + want: false, + }, + { + name: "empty transaction", + msgs: nil, + want: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require.Equal(t, tt.want, messagesAllowFreeGas(tt.msgs)) + }) + } +} diff --git a/app/upgrade_v47.go b/app/upgrade_v47.go index 3ab83cb..736060b 100644 --- a/app/upgrade_v47.go +++ b/app/upgrade_v47.go @@ -28,10 +28,11 @@ import ( ) const ( - UpgradeNameV47 = "v47" - meidAdminAddress = "me1jy4sl7pq7vwsmap0f4ylq7hst4nejjk3fa6avs" - airDropAddress = "me1w4tjt9pupkuzk0mgcz9ajp7nn96dm4uwxpvhl7" - exponentMultiplier = 100 + UpgradeNameV47 = "v47" + meidAdminAddress = "me1jy4sl7pq7vwsmap0f4ylq7hst4nejjk3fa6avs" + airDropAddress = "me1w4tjt9pupkuzk0mgcz9ajp7nn96dm4uwxpvhl7" + exponentMultiplier = 100 + v47ExponentResetAppliedKey = "v47/exponent-reset-applied" ) var UpgradeV47 = Upgrade{ @@ -139,6 +140,11 @@ func CreateUpgradeHandler( } func resetExponent(ctx sdk.Context, app *App) error { + if v47ExponentResetApplied(ctx, app) { + ctx.Logger().Info("v47 exponent reset already applied") + return nil + } + allAccounts := app.AccountKeeper.GetAllAccounts(ctx) for _, account := range allAccounts { balancesBeforeUpgrade := app.BankKeeper.GetAllBalances(ctx, account.GetAddress()) @@ -206,9 +212,20 @@ func resetExponent(ctx sdk.Context, app *App) error { ctx.Logger().Info("new account number 3 for air drop account") } } + markV47ExponentResetApplied(ctx, app) return nil } +func v47ExponentResetApplied(ctx sdk.Context, app *App) bool { + store := ctx.KVStore(app.GetKey(upgradetypes.StoreKey)) + return store.Has([]byte(v47ExponentResetAppliedKey)) +} + +func markV47ExponentResetApplied(ctx sdk.Context, app *App) { + store := ctx.KVStore(app.GetKey(upgradetypes.StoreKey)) + store.Set([]byte(v47ExponentResetAppliedKey), []byte{1}) +} + func fixValidator(ctx sdk.Context, app *App) error { var ( validatorOperatorAddressNode1 = sdk.ValAddress(sdk.MustAccAddressFromBech32("me1fm7xpeu2uxp03afq4ea9v27l5krvq29yqnc7nn")).String()