github.com/KiraCore/sekai@v0.3.43/app/ante/ante.go (about) 1 package ante 2 3 import ( 4 "crypto/sha256" 5 "encoding/hex" 6 "fmt" 7 "time" 8 9 kiratypes "github.com/KiraCore/sekai/types" 10 custodykeeper "github.com/KiraCore/sekai/x/custody/keeper" 11 custodytypes "github.com/KiraCore/sekai/x/custody/types" 12 feeprocessingkeeper "github.com/KiraCore/sekai/x/feeprocessing/keeper" 13 feeprocessingtypes "github.com/KiraCore/sekai/x/feeprocessing/types" 14 customgovkeeper "github.com/KiraCore/sekai/x/gov/keeper" 15 customstakingkeeper "github.com/KiraCore/sekai/x/staking/keeper" 16 tokenskeeper "github.com/KiraCore/sekai/x/tokens/keeper" 17 sdk "github.com/cosmos/cosmos-sdk/types" 18 sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" 19 "github.com/cosmos/cosmos-sdk/x/auth/ante" 20 "github.com/cosmos/cosmos-sdk/x/auth/keeper" 21 "github.com/cosmos/cosmos-sdk/x/auth/signing" 22 "github.com/cosmos/cosmos-sdk/x/auth/types" 23 bank "github.com/cosmos/cosmos-sdk/x/bank/types" 24 ) 25 26 // NewAnteHandler returns an AnteHandler that checks and increments sequence 27 // numbers, checks signatures & account numbers, and deducts fees from the first 28 // signer. 29 func NewAnteHandler( 30 sk customstakingkeeper.Keeper, 31 cgk customgovkeeper.Keeper, 32 tk tokenskeeper.Keeper, 33 fk feeprocessingkeeper.Keeper, 34 ak keeper.AccountKeeper, 35 bk types.BankKeeper, 36 ck custodykeeper.Keeper, 37 feegrantKeeper ante.FeegrantKeeper, 38 extensionOptionChecker ante.ExtensionOptionChecker, 39 sigGasConsumer ante.SignatureVerificationGasConsumer, 40 signModeHandler signing.SignModeHandler, 41 txFeeChecker ante.TxFeeChecker, 42 ) sdk.AnteHandler { 43 return sdk.ChainAnteDecorators( 44 ante.NewSetUpContextDecorator(), // outermost AnteDecorator. SetUpContext must be called first 45 NewCustodyDecorator(ck, cgk), 46 NewZeroGasMeterDecorator(), 47 ante.NewExtensionOptionsDecorator(extensionOptionChecker), 48 ante.NewValidateBasicDecorator(), 49 ante.TxTimeoutHeightDecorator{}, 50 ante.NewValidateMemoDecorator(ak), 51 ante.NewConsumeGasForTxSizeDecorator(ak), 52 // custom fee range validator 53 NewValidateFeeRangeDecorator(sk, cgk, tk, ak), 54 ante.NewSetPubKeyDecorator(ak), // SetPubKeyDecorator must be called before all signature verification decorators 55 ante.NewValidateSigCountDecorator(ak), 56 ante.NewDeductFeeDecorator(ak, bk, feegrantKeeper, txFeeChecker), 57 // poor network management decorator 58 NewPoorNetworkManagementDecorator(ak, cgk, sk), 59 NewBlackWhiteTokensCheckDecorator(cgk, sk, tk), 60 // custom execution fee consume decorator 61 NewExecutionFeeRegistrationDecorator(ak, cgk, fk), 62 ante.NewSigGasConsumeDecorator(ak, sigGasConsumer), 63 ante.NewSigVerificationDecorator(ak, signModeHandler), 64 ante.NewIncrementSequenceDecorator(ak), 65 ) 66 } 67 68 type CustodyDecorator struct { 69 ck custodykeeper.Keeper 70 gk customgovkeeper.Keeper 71 } 72 73 func NewCustodyDecorator(ck custodykeeper.Keeper, gk customgovkeeper.Keeper) CustodyDecorator { 74 return CustodyDecorator{ 75 ck: ck, 76 gk: gk, 77 } 78 } 79 80 func (cd CustodyDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { 81 feeTx, ok := tx.(sdk.FeeTx) 82 if !ok { 83 return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "invalid transaction type") 84 } 85 86 for _, msg := range feeTx.GetMsgs() { 87 settings := cd.ck.GetCustodyInfoByAddress(ctx, msg.GetSigners()[0]) 88 89 if settings != nil && settings.CustodyEnabled { 90 switch kiratypes.MsgType(msg) { 91 case kiratypes.MsgTypeCreateCustody: 92 { 93 msg, ok := msg.(*custodytypes.MsgCreateCustodyRecord) 94 if !ok { 95 return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidType, "Not a MsgCreateCustodyRecord") 96 } 97 98 hash := sha256.Sum256([]byte(msg.OldKey)) 99 hashString := hex.EncodeToString(hash[:]) 100 101 if msg.TargetAddress != "" && msg.TargetAddress != settings.NextController { 102 return ctx, sdkerrors.Wrap(custodytypes.ErrWrongTargetAddr, "Custody module") 103 } 104 105 if hashString != settings.Key { 106 return ctx, sdkerrors.Wrap(custodytypes.ErrWrongKey, "Custody module") 107 } 108 } 109 case kiratypes.MsgTypeAddToCustodyWhiteList: 110 { 111 msg, ok := msg.(*custodytypes.MsgAddToCustodyWhiteList) 112 if !ok { 113 return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidType, "Not a MsgCreateCustodyRecord") 114 } 115 116 hash := sha256.Sum256([]byte(msg.OldKey)) 117 hashString := hex.EncodeToString(hash[:]) 118 119 if msg.TargetAddress != "" && msg.TargetAddress != settings.NextController { 120 return ctx, sdkerrors.Wrap(custodytypes.ErrWrongTargetAddr, "Custody module") 121 } 122 123 if hashString != settings.Key { 124 return ctx, sdkerrors.Wrap(custodytypes.ErrWrongKey, "Custody module") 125 } 126 } 127 case kiratypes.MsgTypeAddToCustodyCustodians: 128 { 129 msg, ok := msg.(*custodytypes.MsgAddToCustodyCustodians) 130 if !ok { 131 return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidType, "Not a MsgCreateCustodyRecord") 132 } 133 134 hash := sha256.Sum256([]byte(msg.OldKey)) 135 hashString := hex.EncodeToString(hash[:]) 136 137 if msg.TargetAddress != "" && msg.TargetAddress != settings.NextController { 138 return ctx, sdkerrors.Wrap(custodytypes.ErrWrongTargetAddr, "Custody module") 139 } 140 141 if hashString != settings.Key { 142 return ctx, sdkerrors.Wrap(custodytypes.ErrWrongKey, "Custody module") 143 } 144 } 145 case kiratypes.MsgTypeRemoveFromCustodyCustodians: 146 { 147 msg, ok := msg.(*custodytypes.MsgRemoveFromCustodyCustodians) 148 if !ok { 149 return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidType, "Not a MsgCreateCustodyRecord") 150 } 151 152 hash := sha256.Sum256([]byte(msg.OldKey)) 153 hashString := hex.EncodeToString(hash[:]) 154 155 if msg.TargetAddress != "" && msg.TargetAddress != settings.NextController { 156 return ctx, sdkerrors.Wrap(custodytypes.ErrWrongTargetAddr, "Custody module") 157 } 158 159 if hashString != settings.Key { 160 return ctx, sdkerrors.Wrap(custodytypes.ErrWrongKey, "Custody module") 161 } 162 } 163 case kiratypes.MsgTypeDropCustodyCustodians: 164 { 165 msg, ok := msg.(*custodytypes.MsgDropCustodyCustodians) 166 if !ok { 167 return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidType, "Not a MsgCreateCustodyRecord") 168 } 169 170 hash := sha256.Sum256([]byte(msg.OldKey)) 171 hashString := hex.EncodeToString(hash[:]) 172 173 if msg.TargetAddress != "" && msg.TargetAddress != settings.NextController { 174 return ctx, sdkerrors.Wrap(custodytypes.ErrWrongTargetAddr, "Custody module") 175 } 176 177 if hashString != settings.Key { 178 return ctx, sdkerrors.Wrap(custodytypes.ErrWrongKey, "Custody module") 179 } 180 } 181 case kiratypes.MsgTypeRemoveFromCustodyWhiteList: 182 { 183 msg, ok := msg.(*custodytypes.MsgRemoveFromCustodyWhiteList) 184 if !ok { 185 return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidType, "Not a MsgCreateCustodyRecord") 186 } 187 188 hash := sha256.Sum256([]byte(msg.OldKey)) 189 hashString := hex.EncodeToString(hash[:]) 190 191 if msg.TargetAddress != "" && msg.TargetAddress != settings.NextController { 192 return ctx, sdkerrors.Wrap(custodytypes.ErrWrongTargetAddr, "Custody module") 193 } 194 195 if hashString != settings.Key { 196 return ctx, sdkerrors.Wrap(custodytypes.ErrWrongKey, "Custody module") 197 } 198 } 199 case kiratypes.MsgTypeDropCustodyWhiteList: 200 { 201 msg, ok := msg.(*custodytypes.MsgDropCustodyWhiteList) 202 if !ok { 203 return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidType, "Not a MsgCreateCustodyRecord") 204 } 205 206 hash := sha256.Sum256([]byte(msg.OldKey)) 207 hashString := hex.EncodeToString(hash[:]) 208 209 if msg.TargetAddress != "" && msg.TargetAddress != settings.NextController { 210 return ctx, sdkerrors.Wrap(custodytypes.ErrWrongTargetAddr, "Custody module") 211 } 212 213 if hashString != settings.Key { 214 return ctx, sdkerrors.Wrap(custodytypes.ErrWrongKey, "Custody module") 215 } 216 } 217 case kiratypes.MsgTypeSend: 218 { 219 msg := msg.(*custodytypes.MsgSend) 220 properties := cd.gk.GetNetworkProperties(ctx) 221 custodians := cd.ck.GetCustodyCustodiansByAddress(ctx, msg.GetSigners()[0]) 222 count := uint64(len(custodians.Addresses)) 223 224 if len(msg.Reward) < 1 { 225 return ctx, sdkerrors.Wrap(custodytypes.ErrNotEnoughReward, "no reward") 226 } 227 228 if msg.Reward[0].Amount.Uint64() < properties.MinCustodyReward*count { 229 return ctx, sdkerrors.Wrap(custodytypes.ErrNotEnoughReward, "to small reward") 230 } 231 232 if msg.Reward[0].Denom != cd.ck.DefaultDenom(ctx) { 233 return ctx, sdkerrors.Wrap(custodytypes.ErrNotEnoughReward, "wrong reward denom") 234 } 235 } 236 } 237 238 } 239 240 if kiratypes.MsgType(msg) == bank.TypeMsgSend { 241 msg := msg.(*bank.MsgSend) 242 243 if settings != nil && settings.CustodyEnabled { 244 custodians := cd.ck.GetCustodyCustodiansByAddress(ctx, msg.GetSigners()[0]) 245 246 if len(custodians.Addresses) > 0 { 247 return ctx, sdkerrors.Wrap(sdkerrors.ErrConflict, "Custody module is enabled. Please use custody send instead.") 248 } 249 } 250 251 if settings != nil && settings.UseWhiteList { 252 whiteList := cd.ck.GetCustodyWhiteListByAddress(ctx, msg.GetSigners()[0]) 253 254 if whiteList != nil && !whiteList.Addresses[msg.ToAddress] { 255 return ctx, custodytypes.ErrNotInWhiteList 256 } 257 } 258 259 if settings != nil && settings.UseLimits { 260 limits := cd.ck.GetCustodyLimitsByAddress(ctx, msg.GetSigners()[0]) 261 262 custodyLimitStatusRecord := custodytypes.CustodyLimitStatusRecord{ 263 Address: msg.GetSigners()[0], 264 CustodyStatuses: cd.ck.GetCustodyLimitsStatusByAddress(ctx, msg.GetSigners()[0]), 265 } 266 267 newAmount := msg.Amount.AmountOf(msg.Amount[0].Denom).Uint64() 268 269 if custodyLimitStatusRecord.CustodyStatuses != nil && custodyLimitStatusRecord.CustodyStatuses.Statuses[msg.Amount[0].Denom] != nil { 270 limit, _ := time.ParseDuration(limits.Limits[msg.Amount[0].Denom].Limit) 271 rate := limits.Limits[msg.Amount[0].Denom].Amount / (uint64(limit.Milliseconds())) 272 period := uint64(time.Now().Unix() - custodyLimitStatusRecord.CustodyStatuses.Statuses[msg.Amount[0].Denom].Time) 273 newAmount = custodyLimitStatusRecord.CustodyStatuses.Statuses[msg.Amount[0].Denom].Amount + msg.Amount.AmountOf(msg.Amount[0].Denom).Uint64() - (period * rate) 274 275 if newAmount <= 0 { 276 return ctx, custodytypes.ErrNotInLimits 277 } 278 } 279 280 custodyLimitStatusRecord.CustodyStatuses.Statuses[msg.Amount[0].Denom] = &custodytypes.CustodyStatus{ 281 Amount: newAmount, 282 } 283 284 cd.ck.AddToCustodyLimitsStatus(ctx, custodyLimitStatusRecord) 285 } 286 } 287 } 288 289 return next(ctx, tx, simulate) 290 } 291 292 // ValidateFeeRangeDecorator check if fee is within range defined as network properties 293 type ValidateFeeRangeDecorator struct { 294 sk customstakingkeeper.Keeper 295 cgk customgovkeeper.Keeper 296 tk tokenskeeper.Keeper 297 ak keeper.AccountKeeper 298 } 299 300 // NewValidateFeeRangeDecorator check if fee is within range defined as network properties 301 func NewValidateFeeRangeDecorator( 302 sk customstakingkeeper.Keeper, 303 cgk customgovkeeper.Keeper, 304 tk tokenskeeper.Keeper, 305 ak keeper.AccountKeeper, 306 ) ValidateFeeRangeDecorator { 307 return ValidateFeeRangeDecorator{ 308 sk: sk, 309 cgk: cgk, 310 ak: ak, 311 tk: tk, 312 } 313 } 314 315 // AnteHandle is a handler for ValidateFeeRangeDecorator 316 func (svd ValidateFeeRangeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { 317 feeTx, ok := tx.(sdk.FeeTx) 318 if !ok { 319 return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "Tx must be a FeeTx") 320 } 321 322 properties := svd.cgk.GetNetworkProperties(ctx) 323 defaultDenom := svd.sk.DefaultDenom(ctx) 324 325 feeAmount := sdk.NewDec(0) 326 feeCoins := feeTx.GetFee() 327 tokensBlackWhite := svd.tk.GetTokenBlackWhites(ctx) 328 for _, feeCoin := range feeCoins { 329 rate := svd.tk.GetTokenRate(ctx, feeCoin.Denom) 330 if !properties.EnableForeignFeePayments && feeCoin.Denom != defaultDenom { 331 return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, fmt.Sprintf("foreign fee payments is disabled by governance")) 332 } 333 if rate == nil || !rate.FeePayments { 334 return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, fmt.Sprintf("currency you are trying to use was not whitelisted as fee payment")) 335 } 336 if tokensBlackWhite.IsFrozen(feeCoin.Denom, defaultDenom, properties.EnableTokenBlacklist, properties.EnableTokenWhitelist) { 337 return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, fmt.Sprintf("currency you are trying to use as fee is frozen")) 338 } 339 feeAmount = feeAmount.Add(sdk.NewDecFromInt(feeCoin.Amount).Mul(rate.FeeRate)) 340 } 341 342 // execution fee should be prepaid 343 executionMaxFee := uint64(0) 344 for _, msg := range feeTx.GetMsgs() { 345 fee := svd.cgk.GetExecutionFee(ctx, kiratypes.MsgType(msg)) 346 if fee != nil { // execution fee exist 347 maxFee := fee.FailureFee 348 if fee.ExecutionFee > maxFee { 349 maxFee = fee.ExecutionFee 350 } 351 executionMaxFee += maxFee 352 } 353 } 354 355 if feeAmount.LT(sdk.NewDec(int64(properties.MinTxFee))) || feeAmount.GT(sdk.NewDec(int64(properties.MaxTxFee))) { 356 return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, fmt.Sprintf("fee %+v(%d) is out of range [%d, %d]%s", feeTx.GetFee(), feeAmount.RoundInt().Int64(), properties.MinTxFee, properties.MaxTxFee, defaultDenom)) 357 } 358 359 if feeAmount.LT(sdk.NewDec(int64(executionMaxFee))) { 360 return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, fmt.Sprintf("fee %+v(%d) is less than max execution fee %d%s", feeTx.GetFee(), feeAmount.RoundInt().Int64(), executionMaxFee, defaultDenom)) 361 } 362 363 return next(ctx, tx, simulate) 364 } 365 366 // ExecutionFeeRegistrationDecorator register paid execution fee 367 type ExecutionFeeRegistrationDecorator struct { 368 ak keeper.AccountKeeper 369 cgk customgovkeeper.Keeper 370 fk feeprocessingkeeper.Keeper 371 } 372 373 // NewExecutionFeeRegistrationDecorator returns instance of CustomExecutionFeeConsumeDecorator 374 func NewExecutionFeeRegistrationDecorator(ak keeper.AccountKeeper, cgk customgovkeeper.Keeper, fk feeprocessingkeeper.Keeper) ExecutionFeeRegistrationDecorator { 375 return ExecutionFeeRegistrationDecorator{ 376 ak, 377 cgk, 378 fk, 379 } 380 } 381 382 // AnteHandle handle ExecutionFeeRegistrationDecorator 383 func (sgcd ExecutionFeeRegistrationDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { 384 sigTx, ok := tx.(sdk.FeeTx) 385 if !ok { 386 return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "invalid transaction type") 387 } 388 389 // execution fee consume gas 390 for _, msg := range sigTx.GetMsgs() { 391 fee := sgcd.cgk.GetExecutionFee(ctx, kiratypes.MsgType(msg)) 392 if fee != nil { // execution fee exist 393 sgcd.fk.AddExecutionStart(ctx, msg) 394 } 395 } 396 397 return next(ctx, tx, simulate) 398 } 399 400 // PoorNetworkManagementDecorator register poor network manager 401 type PoorNetworkManagementDecorator struct { 402 ak keeper.AccountKeeper 403 cgk customgovkeeper.Keeper 404 csk customstakingkeeper.Keeper 405 } 406 407 // NewPoorNetworkManagementDecorator returns instance of PoorNetworkManagementDecorator 408 func NewPoorNetworkManagementDecorator(ak keeper.AccountKeeper, cgk customgovkeeper.Keeper, csk customstakingkeeper.Keeper) PoorNetworkManagementDecorator { 409 return PoorNetworkManagementDecorator{ 410 ak, 411 cgk, 412 csk, 413 } 414 } 415 416 func findString(a []string, x string) int { 417 for i, n := range a { 418 if x == n { 419 return i 420 } 421 } 422 return -1 423 } 424 425 // AnteHandle handle PoorNetworkManagementDecorator 426 func (pnmd PoorNetworkManagementDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { 427 sigTx, ok := tx.(sdk.FeeTx) 428 if !ok { 429 return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "invalid transaction type") 430 } 431 432 // if not poor network, skip this process 433 if pnmd.csk.IsNetworkActive(ctx) { 434 return next(ctx, tx, simulate) 435 } 436 // handle messages on poor network 437 pnmsgs := pnmd.cgk.GetPoorNetworkMessages(ctx) 438 for _, msg := range sigTx.GetMsgs() { 439 if kiratypes.MsgType(msg) == bank.TypeMsgSend { 440 // on poor network, we introduce POOR_NETWORK_MAX_BANK_TX_SEND network property to limit transaction send amount 441 msg := msg.(*bank.MsgSend) 442 if len(msg.Amount) > 1 || msg.Amount[0].Denom != pnmd.csk.DefaultDenom(ctx) { 443 return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "only bond denom is allowed on poor network") 444 } 445 if msg.Amount[0].Amount.Uint64() > pnmd.cgk.GetNetworkProperties(ctx).PoorNetworkMaxBankSend { 446 return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "only restricted amount send is allowed on poor network") 447 } 448 // TODO: we could do restriction to send only when target account does not exist on chain yet for more restriction 449 return next(ctx, tx, simulate) 450 } 451 if findString(pnmsgs.Messages, kiratypes.MsgType(msg)) >= 0 { 452 return next(ctx, tx, simulate) 453 } 454 return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "invalid transaction type on poor network") 455 } 456 457 return next(ctx, tx, simulate) 458 } 459 460 // BlackWhiteTokensCheckDecorator register black white tokens check decorator 461 type BlackWhiteTokensCheckDecorator struct { 462 cgk customgovkeeper.Keeper 463 csk customstakingkeeper.Keeper 464 tk tokenskeeper.Keeper 465 } 466 467 // NewBlackWhiteTokensCheckDecorator returns instance of BlackWhiteTokensCheckDecorator 468 func NewBlackWhiteTokensCheckDecorator(cgk customgovkeeper.Keeper, csk customstakingkeeper.Keeper, tk tokenskeeper.Keeper) BlackWhiteTokensCheckDecorator { 469 return BlackWhiteTokensCheckDecorator{ 470 cgk, 471 csk, 472 tk, 473 } 474 } 475 476 // AnteHandle handle NewPoorNetworkManagementDecorator 477 func (pnmd BlackWhiteTokensCheckDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { 478 sigTx, ok := tx.(sdk.FeeTx) 479 if !ok { 480 return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "invalid transaction type") 481 } 482 483 defaultDenom := pnmd.csk.DefaultDenom(ctx) 484 tokensBlackWhite := pnmd.tk.GetTokenBlackWhites(ctx) 485 properties := pnmd.cgk.GetNetworkProperties(ctx) 486 for _, msg := range sigTx.GetMsgs() { 487 if kiratypes.MsgType(msg) == bank.TypeMsgSend { 488 msg := msg.(*bank.MsgSend) 489 for _, amt := range msg.Amount { 490 if tokensBlackWhite.IsFrozen(amt.Denom, defaultDenom, properties.EnableTokenBlacklist, properties.EnableTokenWhitelist) { 491 return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "token is frozen") 492 } 493 } 494 } 495 } 496 497 return next(ctx, tx, simulate) 498 } 499 500 // ZeroGasMeterDecorator uses infinite gas decorator to avoid gas usage in the network 501 type ZeroGasMeterDecorator struct{} 502 503 // NewZeroGasMeterDecorator returns instance of ZeroGasMeterDecorator 504 func NewZeroGasMeterDecorator() ZeroGasMeterDecorator { 505 return ZeroGasMeterDecorator{} 506 } 507 508 func (igm ZeroGasMeterDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { 509 newCtx = ctx.WithGasMeter(feeprocessingtypes.NewZeroGasMeter()) 510 return next(newCtx, tx, simulate) 511 }