github.com/Finschia/finschia-sdk@v0.49.1/x/feegrant/filtered_fee_test.go (about) 1 package feegrant_test 2 3 import ( 4 "testing" 5 "time" 6 7 proto "github.com/gogo/protobuf/proto" 8 "github.com/stretchr/testify/assert" 9 "github.com/stretchr/testify/require" 10 tmproto "github.com/tendermint/tendermint/proto/tendermint/types" 11 12 "github.com/Finschia/finschia-sdk/simapp" 13 sdk "github.com/Finschia/finschia-sdk/types" 14 banktypes "github.com/Finschia/finschia-sdk/x/bank/types" 15 "github.com/Finschia/finschia-sdk/x/feegrant" 16 ) 17 18 func TestFilteredFeeValidAllow(t *testing.T) { 19 app := simapp.Setup(false) 20 21 ctx := app.BaseApp.NewContext(false, tmproto.Header{ 22 Time: time.Now(), 23 }) 24 eth := sdk.NewCoins(sdk.NewInt64Coin("eth", 10)) 25 atom := sdk.NewCoins(sdk.NewInt64Coin("atom", 555)) 26 smallAtom := sdk.NewCoins(sdk.NewInt64Coin("atom", 43)) 27 bigAtom := sdk.NewCoins(sdk.NewInt64Coin("atom", 1000)) 28 leftAtom := sdk.NewCoins(sdk.NewInt64Coin("atom", 512)) 29 now := ctx.BlockTime() 30 oneHour := now.Add(1 * time.Hour) 31 32 // msg we will call in the all cases 33 call := banktypes.MsgSend{} 34 cases := map[string]struct { 35 allowance *feegrant.BasicAllowance 36 msgs []string 37 fee sdk.Coins 38 blockTime time.Time 39 accept bool 40 remove bool 41 remains sdk.Coins 42 }{ 43 "msg contained": { 44 allowance: &feegrant.BasicAllowance{}, 45 msgs: []string{sdk.MsgTypeURL(&call)}, 46 accept: true, 47 }, 48 "msg not contained": { 49 allowance: &feegrant.BasicAllowance{}, 50 msgs: []string{"/cosmos.gov.v1beta1.MsgVote"}, 51 accept: false, 52 }, 53 "small fee without expire": { 54 allowance: &feegrant.BasicAllowance{ 55 SpendLimit: atom, 56 }, 57 msgs: []string{sdk.MsgTypeURL(&call)}, 58 fee: smallAtom, 59 accept: true, 60 remove: false, 61 remains: leftAtom, 62 }, 63 "all fee without expire": { 64 allowance: &feegrant.BasicAllowance{ 65 SpendLimit: smallAtom, 66 }, 67 msgs: []string{sdk.MsgTypeURL(&call)}, 68 fee: smallAtom, 69 accept: true, 70 remove: true, 71 }, 72 "wrong fee": { 73 allowance: &feegrant.BasicAllowance{ 74 SpendLimit: smallAtom, 75 }, 76 msgs: []string{sdk.MsgTypeURL(&call)}, 77 fee: eth, 78 accept: false, 79 }, 80 "non-expired": { 81 allowance: &feegrant.BasicAllowance{ 82 SpendLimit: atom, 83 Expiration: &oneHour, 84 }, 85 msgs: []string{sdk.MsgTypeURL(&call)}, 86 fee: smallAtom, 87 blockTime: now, 88 accept: true, 89 remove: false, 90 remains: leftAtom, 91 }, 92 "expired": { 93 allowance: &feegrant.BasicAllowance{ 94 SpendLimit: atom, 95 Expiration: &now, 96 }, 97 msgs: []string{sdk.MsgTypeURL(&call)}, 98 fee: smallAtom, 99 blockTime: oneHour, 100 accept: false, 101 remove: true, 102 }, 103 "fee more than allowed": { 104 allowance: &feegrant.BasicAllowance{ 105 SpendLimit: atom, 106 Expiration: &oneHour, 107 }, 108 msgs: []string{sdk.MsgTypeURL(&call)}, 109 fee: bigAtom, 110 blockTime: now, 111 accept: false, 112 }, 113 "with out spend limit": { 114 allowance: &feegrant.BasicAllowance{ 115 Expiration: &oneHour, 116 }, 117 msgs: []string{sdk.MsgTypeURL(&call)}, 118 fee: bigAtom, 119 blockTime: now, 120 accept: true, 121 }, 122 "expired no spend limit": { 123 allowance: &feegrant.BasicAllowance{ 124 Expiration: &now, 125 }, 126 msgs: []string{sdk.MsgTypeURL(&call)}, 127 fee: bigAtom, 128 blockTime: oneHour, 129 accept: false, 130 }, 131 } 132 133 for name, stc := range cases { 134 tc := stc // to make scopelint happy 135 t.Run(name, func(t *testing.T) { 136 err := tc.allowance.ValidateBasic() 137 require.NoError(t, err) 138 139 ctx := app.BaseApp.NewContext(false, tmproto.Header{}).WithBlockTime(tc.blockTime) 140 141 // create grant 142 var granter, grantee sdk.AccAddress 143 allowance, err := feegrant.NewAllowedMsgAllowance(tc.allowance, tc.msgs) 144 require.NoError(t, err) 145 grant, err := feegrant.NewGrant(granter, grantee, allowance) 146 require.NoError(t, err) 147 148 // now try to deduct 149 removed, err := allowance.Accept(ctx, tc.fee, []sdk.Msg{&call}) 150 if !tc.accept { 151 require.Error(t, err) 152 return 153 } 154 require.NoError(t, err) 155 156 require.Equal(t, tc.remove, removed) 157 if !removed { 158 // mimic save & load process (#10564) 159 // the cached allowance was correct even before the fix, 160 // however, the saved value was not. 161 // so we need this to catch the bug. 162 163 newGranter, _ := sdk.AccAddressFromBech32(grant.Granter) 164 newGrantee, _ := sdk.AccAddressFromBech32(grant.Grantee) 165 // create a new updated grant 166 newGrant, err := feegrant.NewGrant( 167 newGranter, 168 newGrantee, 169 allowance) 170 require.NoError(t, err) 171 172 // save the grant 173 cdc := simapp.MakeTestEncodingConfig().Marshaler 174 bz, err := cdc.Marshal(&newGrant) 175 require.NoError(t, err) 176 177 // load the grant 178 var loadedGrant feegrant.Grant 179 err = cdc.Unmarshal(bz, &loadedGrant) 180 require.NoError(t, err) 181 182 newAllowance, err := loadedGrant.GetGrant() 183 require.NoError(t, err) 184 feeAllowance, err := newAllowance.(*feegrant.AllowedMsgAllowance).GetAllowance() 185 require.NoError(t, err) 186 assert.Equal(t, tc.remains, feeAllowance.(*feegrant.BasicAllowance).SpendLimit) 187 } 188 }) 189 } 190 } 191 192 // invalidInterfaceAllowance does not implement proto.Message 193 type invalidInterfaceAllowance struct{} 194 195 // compilation time interface implementation check 196 var _ feegrant.FeeAllowanceI = (*invalidInterfaceAllowance)(nil) 197 198 func (i invalidInterfaceAllowance) Accept(ctx sdk.Context, fee sdk.Coins, msgs []sdk.Msg) (remove bool, err error) { 199 return false, nil 200 } 201 202 func (i invalidInterfaceAllowance) ValidateBasic() error { 203 return nil 204 } 205 206 // invalidProtoAllowance can not run proto.Marshal 207 type invalidProtoAllowance struct { 208 invalidInterfaceAllowance 209 } 210 211 // compilation time interface implementation check 212 var ( 213 _ feegrant.FeeAllowanceI = (*invalidProtoAllowance)(nil) 214 _ proto.Message = (*invalidProtoAllowance)(nil) 215 ) 216 217 func (i invalidProtoAllowance) Reset() { 218 } 219 220 func (i invalidProtoAllowance) String() string { 221 return "" 222 } 223 224 func (i invalidProtoAllowance) ProtoMessage() { 225 } 226 227 func TestSetAllowance(t *testing.T) { 228 cases := map[string]struct { 229 allowance feegrant.FeeAllowanceI 230 valid bool 231 }{ 232 "valid allowance": { 233 allowance: &feegrant.BasicAllowance{}, 234 valid: true, 235 }, 236 "invalid interface allowance": { 237 allowance: &invalidInterfaceAllowance{}, 238 valid: false, 239 }, 240 "empty allowance": { 241 allowance: (*invalidProtoAllowance)(nil), 242 valid: false, 243 }, 244 } 245 246 for name, tc := range cases { 247 t.Run(name, func(t *testing.T) { 248 allowance := &feegrant.BasicAllowance{} 249 msgs := []string{sdk.MsgTypeURL(&banktypes.MsgSend{})} 250 allowed, err := feegrant.NewAllowedMsgAllowance(allowance, msgs) 251 require.NoError(t, err) 252 require.NotNil(t, allowed) 253 err = allowed.SetAllowance(tc.allowance) 254 if tc.valid { 255 require.NoError(t, err) 256 } else { 257 require.Error(t, err) 258 } 259 }) 260 } 261 }