github.com/Finschia/finschia-sdk@v0.49.1/x/feegrant/filtered_fee.go (about) 1 package feegrant 2 3 import ( 4 "github.com/gogo/protobuf/proto" 5 6 "github.com/Finschia/finschia-sdk/codec/types" 7 sdk "github.com/Finschia/finschia-sdk/types" 8 sdkerrors "github.com/Finschia/finschia-sdk/types/errors" 9 ) 10 11 // TODO: Revisit this once we have propoer gas fee framework. 12 // Tracking issues https://github.com/cosmos/cosmos-sdk/issues/9054, https://github.com/Finschia/finschia-sdk/discussions/9072 13 const ( 14 gasCostPerIteration = uint64(10) 15 ) 16 17 var ( 18 _ FeeAllowanceI = (*AllowedMsgAllowance)(nil) 19 _ types.UnpackInterfacesMessage = (*AllowedMsgAllowance)(nil) 20 ) 21 22 // UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces 23 func (a *AllowedMsgAllowance) UnpackInterfaces(unpacker types.AnyUnpacker) error { 24 var allowance FeeAllowanceI 25 return unpacker.UnpackAny(a.Allowance, &allowance) 26 } 27 28 // NewAllowedMsgFeeAllowance creates new filtered fee allowance. 29 func NewAllowedMsgAllowance(allowance FeeAllowanceI, allowedMsgs []string) (*AllowedMsgAllowance, error) { 30 msg, ok := allowance.(proto.Message) 31 if !ok { 32 return nil, sdkerrors.Wrapf(sdkerrors.ErrPackAny, "cannot proto marshal %T", msg) 33 } 34 any, err := types.NewAnyWithValue(msg) 35 if err != nil { 36 return nil, err 37 } 38 39 return &AllowedMsgAllowance{ 40 Allowance: any, 41 AllowedMessages: allowedMsgs, 42 }, nil 43 } 44 45 // GetAllowance returns allowed fee allowance. 46 func (a *AllowedMsgAllowance) GetAllowance() (FeeAllowanceI, error) { 47 allowance, ok := a.Allowance.GetCachedValue().(FeeAllowanceI) 48 if !ok { 49 return nil, sdkerrors.Wrap(ErrNoAllowance, "failed to get allowance") 50 } 51 52 return allowance, nil 53 } 54 55 // SetAllowance sets allowed fee allowance. 56 func (a *AllowedMsgAllowance) SetAllowance(allowance FeeAllowanceI) error { 57 var err error 58 protoAllowance, ok := allowance.(proto.Message) 59 if !ok { 60 return sdkerrors.Wrapf(sdkerrors.ErrPackAny, "cannot proto marshal %T", allowance) 61 } 62 a.Allowance, err = types.NewAnyWithValue(protoAllowance) 63 if err != nil { 64 return sdkerrors.Wrapf(sdkerrors.ErrPackAny, "cannot proto marshal %T", protoAllowance) 65 } 66 return nil 67 } 68 69 // Accept method checks for the filtered messages has valid expiry 70 func (a *AllowedMsgAllowance) Accept(ctx sdk.Context, fee sdk.Coins, msgs []sdk.Msg) (bool, error) { 71 if !a.allMsgTypesAllowed(ctx, msgs) { 72 return false, sdkerrors.Wrap(ErrMessageNotAllowed, "message does not exist in allowed messages") 73 } 74 75 allowance, err := a.GetAllowance() 76 if err != nil { 77 return false, err 78 } 79 80 remove, err := allowance.Accept(ctx, fee, msgs) 81 if err == nil && !remove { 82 if err = a.SetAllowance(allowance); err != nil { 83 return false, err 84 } 85 } 86 return remove, err 87 } 88 89 func (a *AllowedMsgAllowance) allowedMsgsToMap(ctx sdk.Context) map[string]bool { 90 msgsMap := make(map[string]bool, len(a.AllowedMessages)) 91 for _, msg := range a.AllowedMessages { 92 ctx.GasMeter().ConsumeGas(gasCostPerIteration, "check msg") 93 msgsMap[msg] = true 94 } 95 96 return msgsMap 97 } 98 99 func (a *AllowedMsgAllowance) allMsgTypesAllowed(ctx sdk.Context, msgs []sdk.Msg) bool { 100 msgsMap := a.allowedMsgsToMap(ctx) 101 102 for _, msg := range msgs { 103 ctx.GasMeter().ConsumeGas(gasCostPerIteration, "check msg") 104 if !msgsMap[sdk.MsgTypeURL(msg)] { 105 return false 106 } 107 } 108 109 return true 110 } 111 112 // ValidateBasic implements FeeAllowance and enforces basic sanity checks 113 func (a *AllowedMsgAllowance) ValidateBasic() error { 114 if a.Allowance == nil { 115 return sdkerrors.Wrap(ErrNoAllowance, "allowance should not be empty") 116 } 117 if len(a.AllowedMessages) == 0 { 118 return sdkerrors.Wrap(ErrNoMessages, "allowed messages shouldn't be empty") 119 } 120 121 allowance, err := a.GetAllowance() 122 if err != nil { 123 return err 124 } 125 126 return allowance.ValidateBasic() 127 }