github.com/Finschia/finschia-sdk@v0.48.1/x/feegrant/keeper/keeper_test.go (about)

     1  package keeper_test
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  
     7  	"github.com/stretchr/testify/suite"
     8  	tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
     9  
    10  	"github.com/Finschia/finschia-sdk/simapp"
    11  	sdk "github.com/Finschia/finschia-sdk/types"
    12  	"github.com/Finschia/finschia-sdk/x/feegrant"
    13  	"github.com/Finschia/finschia-sdk/x/feegrant/keeper"
    14  )
    15  
    16  type KeeperTestSuite struct {
    17  	suite.Suite
    18  
    19  	app     *simapp.SimApp
    20  	sdkCtx  sdk.Context
    21  	addrs   []sdk.AccAddress
    22  	msgSrvr feegrant.MsgServer
    23  	ctx     context.Context
    24  	atom    sdk.Coins
    25  	keeper  keeper.Keeper
    26  }
    27  
    28  func TestKeeperTestSuite(t *testing.T) {
    29  	suite.Run(t, new(KeeperTestSuite))
    30  }
    31  
    32  func (suite *KeeperTestSuite) SetupTest() {
    33  	app := simapp.Setup(false)
    34  	ctx := app.BaseApp.NewContext(false, tmproto.Header{})
    35  
    36  	suite.app = app
    37  	suite.sdkCtx = ctx
    38  	suite.addrs = simapp.AddTestAddrsIncremental(app, ctx, 4, sdk.NewInt(30000000))
    39  	suite.ctx = sdk.WrapSDKContext(ctx)
    40  	suite.keeper = suite.app.FeeGrantKeeper
    41  	suite.msgSrvr = keeper.NewMsgServerImpl(suite.keeper)
    42  	suite.atom = sdk.NewCoins(sdk.NewCoin("atom", sdk.NewInt(555)))
    43  }
    44  
    45  func (suite *KeeperTestSuite) TestKeeperCrud() {
    46  	// some helpers
    47  	eth := sdk.NewCoins(sdk.NewInt64Coin("eth", 123))
    48  	exp := suite.sdkCtx.BlockTime().AddDate(1, 0, 0)
    49  	basic := &feegrant.BasicAllowance{
    50  		SpendLimit: suite.atom,
    51  		Expiration: &exp,
    52  	}
    53  
    54  	basic2 := &feegrant.BasicAllowance{
    55  		SpendLimit: eth,
    56  		Expiration: &exp,
    57  	}
    58  
    59  	// let's set up some initial state here
    60  	err := suite.keeper.GrantAllowance(suite.sdkCtx, suite.addrs[0], suite.addrs[1], basic)
    61  	suite.Require().NoError(err)
    62  
    63  	err = suite.keeper.GrantAllowance(suite.sdkCtx, suite.addrs[0], suite.addrs[2], basic2)
    64  	suite.Require().NoError(err)
    65  
    66  	err = suite.keeper.GrantAllowance(suite.sdkCtx, suite.addrs[1], suite.addrs[2], basic)
    67  	suite.Require().NoError(err)
    68  
    69  	err = suite.keeper.GrantAllowance(suite.sdkCtx, suite.addrs[1], suite.addrs[3], basic)
    70  	suite.Require().NoError(err)
    71  
    72  	err = suite.keeper.GrantAllowance(suite.sdkCtx, suite.addrs[3], suite.addrs[0], basic2)
    73  	suite.Require().NoError(err)
    74  
    75  	// remove some, overwrite other
    76  	_, err = suite.msgSrvr.RevokeAllowance(suite.ctx, &feegrant.MsgRevokeAllowance{Granter: suite.addrs[0].String(), Grantee: suite.addrs[1].String()})
    77  	suite.Require().NoError(err)
    78  	_, err = suite.msgSrvr.RevokeAllowance(suite.ctx, &feegrant.MsgRevokeAllowance{Granter: suite.addrs[0].String(), Grantee: suite.addrs[2].String()})
    79  	suite.Require().NoError(err)
    80  
    81  	// revoke non-exist fee allowance
    82  	_, err = suite.msgSrvr.RevokeAllowance(suite.ctx, &feegrant.MsgRevokeAllowance{Granter: suite.addrs[0].String(), Grantee: suite.addrs[2].String()})
    83  	suite.Require().Error(err)
    84  
    85  	err = suite.keeper.GrantAllowance(suite.sdkCtx, suite.addrs[0], suite.addrs[2], basic)
    86  	suite.Require().NoError(err)
    87  
    88  	err = suite.keeper.GrantAllowance(suite.sdkCtx, suite.addrs[1], suite.addrs[2], basic2)
    89  	suite.Require().NoError(err)
    90  
    91  	// end state:
    92  	// addr -> addr3 (basic)
    93  	// addr2 -> addr3 (basic2), addr4(basic)
    94  	// addr4 -> addr (basic2)
    95  
    96  	// then lots of queries
    97  	cases := map[string]struct {
    98  		grantee   sdk.AccAddress
    99  		granter   sdk.AccAddress
   100  		allowance feegrant.FeeAllowanceI
   101  	}{
   102  		"addr revoked": {
   103  			granter: suite.addrs[0],
   104  			grantee: suite.addrs[1],
   105  		},
   106  		"addr revoked and added": {
   107  			granter:   suite.addrs[0],
   108  			grantee:   suite.addrs[2],
   109  			allowance: basic,
   110  		},
   111  		"addr never there": {
   112  			granter: suite.addrs[0],
   113  			grantee: suite.addrs[3],
   114  		},
   115  		"addr modified": {
   116  			granter:   suite.addrs[1],
   117  			grantee:   suite.addrs[2],
   118  			allowance: basic2,
   119  		},
   120  	}
   121  
   122  	for name, tc := range cases {
   123  		tc := tc
   124  		suite.Run(name, func() {
   125  			allow, _ := suite.keeper.GetAllowance(suite.sdkCtx, tc.granter, tc.grantee)
   126  
   127  			if tc.allowance == nil {
   128  				suite.Nil(allow)
   129  				return
   130  			}
   131  			suite.NotNil(allow)
   132  			suite.Equal(tc.allowance, allow)
   133  		})
   134  	}
   135  	accAddr, err := sdk.AccAddressFromBech32("link1k907plrxssuh2dxsd8k2jtp3de2t4xyhq6rkxd")
   136  	suite.Require().NoError(err)
   137  
   138  	// let's grant and revoke authorization to non existing account
   139  	err = suite.keeper.GrantAllowance(suite.sdkCtx, suite.addrs[3], accAddr, basic2)
   140  	suite.Require().NoError(err)
   141  
   142  	_, err = suite.keeper.GetAllowance(suite.sdkCtx, suite.addrs[3], accAddr)
   143  	suite.Require().NoError(err)
   144  
   145  	_, err = suite.msgSrvr.RevokeAllowance(suite.ctx, &feegrant.MsgRevokeAllowance{Granter: suite.addrs[3].String(), Grantee: accAddr.String()})
   146  	suite.Require().NoError(err)
   147  }
   148  
   149  func (suite *KeeperTestSuite) TestUseGrantedFee() {
   150  	eth := sdk.NewCoins(sdk.NewInt64Coin("eth", 123))
   151  	blockTime := suite.sdkCtx.BlockTime()
   152  	oneYear := blockTime.AddDate(1, 0, 0)
   153  
   154  	future := &feegrant.BasicAllowance{
   155  		SpendLimit: suite.atom,
   156  		Expiration: &oneYear,
   157  	}
   158  
   159  	// for testing limits of the contract
   160  	hugeAtom := sdk.NewCoins(sdk.NewInt64Coin("atom", 9999))
   161  	smallAtom := sdk.NewCoins(sdk.NewInt64Coin("atom", 1))
   162  	futureAfterSmall := &feegrant.BasicAllowance{
   163  		SpendLimit: sdk.NewCoins(sdk.NewInt64Coin("atom", 554)),
   164  		Expiration: &oneYear,
   165  	}
   166  
   167  	// then lots of queries
   168  	cases := map[string]struct {
   169  		grantee sdk.AccAddress
   170  		granter sdk.AccAddress
   171  		fee     sdk.Coins
   172  		allowed bool
   173  		final   feegrant.FeeAllowanceI
   174  	}{
   175  		"use entire pot": {
   176  			granter: suite.addrs[0],
   177  			grantee: suite.addrs[1],
   178  			fee:     suite.atom,
   179  			allowed: true,
   180  			final:   nil,
   181  		},
   182  		"too high": {
   183  			granter: suite.addrs[0],
   184  			grantee: suite.addrs[1],
   185  			fee:     hugeAtom,
   186  			allowed: false,
   187  			final:   future,
   188  		},
   189  		"use a little": {
   190  			granter: suite.addrs[0],
   191  			grantee: suite.addrs[1],
   192  			fee:     smallAtom,
   193  			allowed: true,
   194  			final:   futureAfterSmall,
   195  		},
   196  	}
   197  
   198  	for name, tc := range cases {
   199  		tc := tc
   200  		suite.Run(name, func() {
   201  			err := suite.keeper.GrantAllowance(suite.sdkCtx, suite.addrs[0], suite.addrs[1], future)
   202  			suite.Require().NoError(err)
   203  
   204  			err = suite.keeper.UseGrantedFees(suite.sdkCtx, tc.granter, tc.grantee, tc.fee, []sdk.Msg{})
   205  			if tc.allowed {
   206  				suite.NoError(err)
   207  			} else {
   208  				suite.Error(err)
   209  			}
   210  
   211  			loaded, _ := suite.keeper.GetAllowance(suite.sdkCtx, tc.granter, tc.grantee)
   212  			suite.Equal(tc.final, loaded)
   213  		})
   214  	}
   215  
   216  	expired := &feegrant.BasicAllowance{
   217  		SpendLimit: eth,
   218  		Expiration: &blockTime,
   219  	}
   220  	// creating expired feegrant
   221  	ctx := suite.sdkCtx.WithBlockTime(oneYear)
   222  	err := suite.keeper.GrantAllowance(ctx, suite.addrs[0], suite.addrs[2], expired)
   223  	suite.Require().NoError(err)
   224  
   225  	// expect error: feegrant expired
   226  	err = suite.keeper.UseGrantedFees(ctx, suite.addrs[0], suite.addrs[2], eth, []sdk.Msg{})
   227  	suite.Error(err)
   228  	suite.Contains(err.Error(), "fee allowance expired")
   229  
   230  	// verify: feegrant is revoked
   231  	_, err = suite.keeper.GetAllowance(ctx, suite.addrs[0], suite.addrs[2])
   232  	suite.Error(err)
   233  	suite.Contains(err.Error(), "fee-grant not found")
   234  }
   235  
   236  func (suite *KeeperTestSuite) TestIterateGrants() {
   237  	eth := sdk.NewCoins(sdk.NewInt64Coin("eth", 123))
   238  	exp := suite.sdkCtx.BlockTime().AddDate(1, 0, 0)
   239  
   240  	allowance := &feegrant.BasicAllowance{
   241  		SpendLimit: suite.atom,
   242  		Expiration: &exp,
   243  	}
   244  
   245  	allowance1 := &feegrant.BasicAllowance{
   246  		SpendLimit: eth,
   247  		Expiration: &exp,
   248  	}
   249  
   250  	suite.keeper.GrantAllowance(suite.sdkCtx, suite.addrs[0], suite.addrs[1], allowance)
   251  	suite.keeper.GrantAllowance(suite.sdkCtx, suite.addrs[2], suite.addrs[1], allowance1)
   252  
   253  	suite.keeper.IterateAllFeeAllowances(suite.sdkCtx, func(grant feegrant.Grant) bool {
   254  		suite.Require().Equal(suite.addrs[1].String(), grant.Grantee)
   255  		suite.Require().Contains([]string{suite.addrs[0].String(), suite.addrs[2].String()}, grant.Granter)
   256  		return true
   257  	})
   258  }