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  }