477 lines
14 KiB
Go
477 lines
14 KiB
Go
package keeper_test
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/golang/mock/gomock"
|
|
"github.com/stretchr/testify/suite"
|
|
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
|
tmtime "github.com/tendermint/tendermint/types/time"
|
|
|
|
"github.com/cosmos/cosmos-sdk/baseapp"
|
|
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
|
|
"github.com/cosmos/cosmos-sdk/testutil"
|
|
simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil"
|
|
"github.com/cosmos/cosmos-sdk/x/authz"
|
|
authzkeeper "github.com/cosmos/cosmos-sdk/x/authz/keeper"
|
|
authzmodule "github.com/cosmos/cosmos-sdk/x/authz/module"
|
|
authztestutil "github.com/cosmos/cosmos-sdk/x/authz/testutil"
|
|
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
|
|
"github.com/tendermint/tendermint/libs/log"
|
|
)
|
|
|
|
var (
|
|
bankSendAuthMsgType = banktypes.SendAuthorization{}.MsgTypeURL()
|
|
coins10 = sdk.NewCoins(sdk.NewInt64Coin("stake", 10))
|
|
coins100 = sdk.NewCoins(sdk.NewInt64Coin("stake", 100))
|
|
coins1000 = sdk.NewCoins(sdk.NewInt64Coin("stake", 1000))
|
|
)
|
|
|
|
type TestSuite struct {
|
|
suite.Suite
|
|
|
|
ctx sdk.Context
|
|
addrs []sdk.AccAddress
|
|
authzKeeper authzkeeper.Keeper
|
|
accountKeeper *authztestutil.MockAccountKeeper
|
|
bankKeeper *authztestutil.MockBankKeeper
|
|
interfaceRegistry codectypes.InterfaceRegistry
|
|
baseApp *baseapp.BaseApp
|
|
encCfg moduletestutil.TestEncodingConfig
|
|
queryClient authz.QueryClient
|
|
}
|
|
|
|
func (s *TestSuite) SetupTest() {
|
|
key := sdk.NewKVStoreKey(authzkeeper.StoreKey)
|
|
testCtx := testutil.DefaultContextWithDB(s.T(), key, sdk.NewTransientStoreKey("transient_test"))
|
|
s.ctx = testCtx.Ctx.WithBlockHeader(tmproto.Header{Time: tmtime.Now()})
|
|
s.encCfg = moduletestutil.MakeTestEncodingConfig(authzmodule.AppModuleBasic{})
|
|
|
|
s.baseApp = baseapp.NewBaseApp(
|
|
"authz",
|
|
log.NewNopLogger(),
|
|
testCtx.DB,
|
|
s.encCfg.TxConfig.TxDecoder(),
|
|
)
|
|
s.baseApp.SetCMS(testCtx.CMS)
|
|
s.baseApp.SetInterfaceRegistry(s.encCfg.InterfaceRegistry)
|
|
|
|
s.addrs = simtestutil.CreateIncrementalAccounts(7)
|
|
|
|
// gomock initializations
|
|
ctrl := gomock.NewController(s.T())
|
|
s.accountKeeper = authztestutil.NewMockAccountKeeper(ctrl)
|
|
s.bankKeeper = authztestutil.NewMockBankKeeper(ctrl)
|
|
banktypes.RegisterInterfaces(s.encCfg.InterfaceRegistry)
|
|
banktypes.RegisterMsgServer(s.baseApp.MsgServiceRouter(), s.bankKeeper)
|
|
|
|
s.authzKeeper = authzkeeper.NewKeeper(key, s.encCfg.Codec, s.baseApp.MsgServiceRouter(), s.accountKeeper)
|
|
|
|
queryHelper := baseapp.NewQueryServerTestHelper(s.ctx, s.encCfg.InterfaceRegistry)
|
|
authz.RegisterQueryServer(queryHelper, s.authzKeeper)
|
|
queryClient := authz.NewQueryClient(queryHelper)
|
|
s.queryClient = queryClient
|
|
|
|
s.queryClient = queryClient
|
|
}
|
|
|
|
func (s *TestSuite) TestKeeper() {
|
|
ctx, addrs := s.ctx, s.addrs
|
|
now := ctx.BlockTime()
|
|
require := s.Require()
|
|
|
|
granterAddr := addrs[0]
|
|
granteeAddr := addrs[1]
|
|
|
|
s.T().Log("verify that no authorization returns nil")
|
|
authorizations, err := s.authzKeeper.GetAuthorizations(ctx, granteeAddr, granterAddr)
|
|
require.NoError(err)
|
|
require.Len(authorizations, 0)
|
|
|
|
s.T().Log("verify save, get and delete")
|
|
sendAutz := &banktypes.SendAuthorization{SpendLimit: coins100}
|
|
expire := now.AddDate(1, 0, 0)
|
|
err = s.authzKeeper.SaveGrant(ctx, granteeAddr, granterAddr, sendAutz, &expire)
|
|
require.NoError(err)
|
|
|
|
authorizations, err = s.authzKeeper.GetAuthorizations(ctx, granteeAddr, granterAddr)
|
|
require.NoError(err)
|
|
require.Len(authorizations, 1)
|
|
|
|
err = s.authzKeeper.DeleteGrant(ctx, granteeAddr, granterAddr, sendAutz.MsgTypeURL())
|
|
require.NoError(err)
|
|
|
|
authorizations, err = s.authzKeeper.GetAuthorizations(ctx, granteeAddr, granterAddr)
|
|
require.NoError(err)
|
|
require.Len(authorizations, 0)
|
|
|
|
s.T().Log("verify granting same authorization overwrite existing authorization")
|
|
err = s.authzKeeper.SaveGrant(ctx, granteeAddr, granterAddr, sendAutz, &expire)
|
|
require.NoError(err)
|
|
|
|
authorizations, err = s.authzKeeper.GetAuthorizations(ctx, granteeAddr, granterAddr)
|
|
require.NoError(err)
|
|
require.Len(authorizations, 1)
|
|
|
|
sendAutz = &banktypes.SendAuthorization{SpendLimit: coins1000}
|
|
err = s.authzKeeper.SaveGrant(ctx, granteeAddr, granterAddr, sendAutz, &expire)
|
|
require.NoError(err)
|
|
authorizations, err = s.authzKeeper.GetAuthorizations(ctx, granteeAddr, granterAddr)
|
|
require.NoError(err)
|
|
require.Len(authorizations, 1)
|
|
authorization := authorizations[0]
|
|
sendAuth := authorization.(*banktypes.SendAuthorization)
|
|
require.Equal(sendAuth.SpendLimit, sendAutz.SpendLimit)
|
|
require.Equal(sendAuth.MsgTypeURL(), sendAutz.MsgTypeURL())
|
|
|
|
s.T().Log("verify removing non existing authorization returns error")
|
|
err = s.authzKeeper.DeleteGrant(ctx, granterAddr, granteeAddr, "abcd")
|
|
s.Require().Error(err)
|
|
}
|
|
|
|
func (s *TestSuite) TestKeeperIter() {
|
|
ctx, addrs := s.ctx, s.addrs
|
|
|
|
granterAddr := addrs[0]
|
|
granteeAddr := addrs[1]
|
|
granter2Addr := addrs[2]
|
|
e := ctx.BlockTime().AddDate(1, 0, 0)
|
|
sendAuthz := banktypes.NewSendAuthorization(coins100, nil)
|
|
|
|
s.authzKeeper.SaveGrant(ctx, granteeAddr, granterAddr, sendAuthz, &e)
|
|
s.authzKeeper.SaveGrant(ctx, granteeAddr, granter2Addr, sendAuthz, &e)
|
|
|
|
s.authzKeeper.IterateGrants(ctx, func(granter, grantee sdk.AccAddress, grant authz.Grant) bool {
|
|
s.Require().Equal(granteeAddr, grantee)
|
|
s.Require().Contains([]sdk.AccAddress{granterAddr, granter2Addr}, granter)
|
|
return true
|
|
})
|
|
}
|
|
|
|
func (s *TestSuite) TestDispatchAction() {
|
|
addrs := s.addrs
|
|
require := s.Require()
|
|
now := s.ctx.BlockTime()
|
|
|
|
granterAddr := addrs[0]
|
|
granteeAddr := addrs[1]
|
|
recipientAddr := addrs[2]
|
|
a := banktypes.NewSendAuthorization(coins100, nil)
|
|
|
|
testCases := []struct {
|
|
name string
|
|
req authz.MsgExec
|
|
expectErr bool
|
|
errMsg string
|
|
preRun func() sdk.Context
|
|
postRun func()
|
|
}{
|
|
{
|
|
"expect error authorization not found",
|
|
authz.NewMsgExec(granteeAddr, []sdk.Msg{
|
|
&banktypes.MsgSend{
|
|
Amount: coins10,
|
|
FromAddress: granterAddr.String(),
|
|
ToAddress: recipientAddr.String(),
|
|
},
|
|
}),
|
|
true,
|
|
"authorization not found",
|
|
func() sdk.Context {
|
|
// remove any existing authorizations
|
|
s.authzKeeper.DeleteGrant(s.ctx, granteeAddr, granterAddr, bankSendAuthMsgType)
|
|
return s.ctx
|
|
},
|
|
func() {},
|
|
},
|
|
{
|
|
"expect error expired authorization",
|
|
authz.NewMsgExec(granteeAddr, []sdk.Msg{
|
|
&banktypes.MsgSend{
|
|
Amount: coins10,
|
|
FromAddress: granterAddr.String(),
|
|
ToAddress: recipientAddr.String(),
|
|
},
|
|
}),
|
|
true,
|
|
"authorization expired",
|
|
func() sdk.Context {
|
|
e := now.AddDate(0, 0, 1)
|
|
err := s.authzKeeper.SaveGrant(s.ctx, granteeAddr, granterAddr, a, &e)
|
|
require.NoError(err)
|
|
return s.ctx.WithBlockTime(s.ctx.BlockTime().AddDate(0, 0, 2))
|
|
},
|
|
func() {},
|
|
},
|
|
{
|
|
"expect error over spent limit",
|
|
authz.NewMsgExec(granteeAddr, []sdk.Msg{
|
|
&banktypes.MsgSend{
|
|
Amount: coins1000,
|
|
FromAddress: granterAddr.String(),
|
|
ToAddress: recipientAddr.String(),
|
|
},
|
|
}),
|
|
true,
|
|
"requested amount is more than spend limit",
|
|
func() sdk.Context {
|
|
e := now.AddDate(0, 1, 0)
|
|
err := s.authzKeeper.SaveGrant(s.ctx, granteeAddr, granterAddr, a, &e)
|
|
require.NoError(err)
|
|
return s.ctx
|
|
},
|
|
func() {},
|
|
},
|
|
{
|
|
"valid test verify amount left",
|
|
authz.NewMsgExec(granteeAddr, []sdk.Msg{
|
|
&banktypes.MsgSend{
|
|
Amount: coins10,
|
|
FromAddress: granterAddr.String(),
|
|
ToAddress: recipientAddr.String(),
|
|
},
|
|
}),
|
|
false,
|
|
"",
|
|
func() sdk.Context {
|
|
e := now.AddDate(0, 1, 0)
|
|
err := s.authzKeeper.SaveGrant(s.ctx, granteeAddr, granterAddr, a, &e)
|
|
require.NoError(err)
|
|
return s.ctx
|
|
},
|
|
func() {
|
|
authzs, err := s.authzKeeper.GetAuthorizations(s.ctx, granteeAddr, granterAddr)
|
|
require.NoError(err)
|
|
require.Len(authzs, 1)
|
|
authorization := authzs[0].(*banktypes.SendAuthorization)
|
|
require.NotNil(authorization)
|
|
require.Equal(authorization.SpendLimit, coins100.Sub(coins10...))
|
|
},
|
|
},
|
|
{
|
|
"valid test verify authorization is removed when it is used up",
|
|
authz.NewMsgExec(granteeAddr, []sdk.Msg{
|
|
&banktypes.MsgSend{
|
|
Amount: coins100,
|
|
FromAddress: granterAddr.String(),
|
|
ToAddress: recipientAddr.String(),
|
|
},
|
|
}),
|
|
false,
|
|
"",
|
|
func() sdk.Context {
|
|
e := now.AddDate(0, 1, 0)
|
|
err := s.authzKeeper.SaveGrant(s.ctx, granteeAddr, granterAddr, a, &e)
|
|
require.NoError(err)
|
|
return s.ctx
|
|
},
|
|
func() {
|
|
authzs, err := s.authzKeeper.GetAuthorizations(s.ctx, granteeAddr, granterAddr)
|
|
require.NoError(err)
|
|
require.Len(authzs, 0)
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
s.Run(tc.name, func() {
|
|
ctx := tc.preRun()
|
|
executeMsgs, err := tc.req.GetMessages()
|
|
require.NoError(err)
|
|
result, err := s.authzKeeper.DispatchActions(ctx, granteeAddr, executeMsgs)
|
|
if tc.expectErr {
|
|
require.Error(err)
|
|
require.Nil(result)
|
|
require.Contains(err.Error(), tc.errMsg)
|
|
} else {
|
|
require.NoError(err)
|
|
require.NotNil(result)
|
|
}
|
|
tc.postRun()
|
|
})
|
|
}
|
|
}
|
|
|
|
// Tests that all msg events included in an authz MsgExec tx
|
|
// Ref: https://github.com/cosmos/cosmos-sdk/issues/9501
|
|
func (s *TestSuite) TestDispatchedEvents() {
|
|
require := s.Require()
|
|
addrs := s.addrs
|
|
granterAddr := addrs[0]
|
|
granteeAddr := addrs[1]
|
|
recipientAddr := addrs[2]
|
|
expiration := s.ctx.BlockTime().Add(1 * time.Second) // must be in the future
|
|
|
|
msgs := authz.NewMsgExec(granteeAddr, []sdk.Msg{
|
|
&banktypes.MsgSend{
|
|
Amount: coins10,
|
|
FromAddress: granterAddr.String(),
|
|
ToAddress: recipientAddr.String(),
|
|
},
|
|
})
|
|
|
|
// grant authorization
|
|
err := s.authzKeeper.SaveGrant(s.ctx, granteeAddr, granterAddr, &banktypes.SendAuthorization{SpendLimit: coins10}, &expiration)
|
|
require.NoError(err)
|
|
authorizations, err := s.authzKeeper.GetAuthorizations(s.ctx, granteeAddr, granterAddr)
|
|
require.NoError(err)
|
|
require.Len(authorizations, 1)
|
|
authorization := authorizations[0].(*banktypes.SendAuthorization)
|
|
require.Equal(authorization.MsgTypeURL(), bankSendAuthMsgType)
|
|
|
|
executeMsgs, err := msgs.GetMessages()
|
|
require.NoError(err)
|
|
|
|
result, err := s.authzKeeper.DispatchActions(s.ctx, granteeAddr, executeMsgs)
|
|
require.NoError(err)
|
|
require.NotNil(result)
|
|
|
|
events := s.ctx.EventManager().Events()
|
|
|
|
// get last 5 events (events that occur *after* the grant)
|
|
events = events[len(events)-2:]
|
|
|
|
requiredEvents := map[string]bool{
|
|
"cosmos.authz.v1beta1.EventGrant": true,
|
|
"cosmos.authz.v1beta1.EventRevoke": true,
|
|
}
|
|
for _, e := range events {
|
|
requiredEvents[e.Type] = true
|
|
}
|
|
for _, v := range requiredEvents {
|
|
require.True(v)
|
|
}
|
|
}
|
|
|
|
func (s *TestSuite) TestDequeueAllGrantsQueue() {
|
|
require := s.Require()
|
|
addrs := s.addrs
|
|
granter := addrs[0]
|
|
grantee := addrs[1]
|
|
grantee1 := addrs[2]
|
|
exp := s.ctx.BlockTime().AddDate(0, 0, 1)
|
|
a := banktypes.SendAuthorization{SpendLimit: coins100}
|
|
|
|
// create few authorizations
|
|
err := s.authzKeeper.SaveGrant(s.ctx, grantee, granter, &a, &exp)
|
|
require.NoError(err)
|
|
|
|
err = s.authzKeeper.SaveGrant(s.ctx, grantee1, granter, &a, &exp)
|
|
require.NoError(err)
|
|
|
|
exp2 := exp.AddDate(0, 1, 0)
|
|
err = s.authzKeeper.SaveGrant(s.ctx, granter, grantee1, &a, &exp2)
|
|
require.NoError(err)
|
|
|
|
exp2 = exp.AddDate(2, 0, 0)
|
|
err = s.authzKeeper.SaveGrant(s.ctx, granter, grantee, &a, &exp2)
|
|
require.NoError(err)
|
|
|
|
newCtx := s.ctx.WithBlockTime(exp.AddDate(1, 0, 0))
|
|
err = s.authzKeeper.DequeueAndDeleteExpiredGrants(newCtx)
|
|
require.NoError(err)
|
|
|
|
s.T().Log("verify expired grants are pruned from the state")
|
|
authzs, err := s.authzKeeper.GetAuthorizations(newCtx, grantee, granter)
|
|
require.NoError(err)
|
|
require.Len(authzs, 0)
|
|
|
|
authzs, err = s.authzKeeper.GetAuthorizations(newCtx, granter, grantee1)
|
|
require.NoError(err)
|
|
require.Len(authzs, 0)
|
|
|
|
authzs, err = s.authzKeeper.GetAuthorizations(newCtx, grantee1, granter)
|
|
require.NoError(err)
|
|
require.Len(authzs, 0)
|
|
|
|
authzs, err = s.authzKeeper.GetAuthorizations(newCtx, granter, grantee)
|
|
require.NoError(err)
|
|
require.Len(authzs, 1)
|
|
}
|
|
|
|
func (s *TestSuite) TestGetAuthorization() {
|
|
addr1 := s.addrs[3]
|
|
addr2 := s.addrs[4]
|
|
addr3 := s.addrs[5]
|
|
addr4 := s.addrs[6]
|
|
|
|
genAuthMulti := authz.NewGenericAuthorization(sdk.MsgTypeURL(&banktypes.MsgMultiSend{}))
|
|
genAuthSend := authz.NewGenericAuthorization(sdk.MsgTypeURL(&banktypes.MsgSend{}))
|
|
sendAuth := banktypes.NewSendAuthorization(coins10, nil)
|
|
|
|
start := s.ctx.BlockHeader().Time
|
|
expired := start.Add(time.Duration(1) * time.Second)
|
|
notExpired := start.Add(time.Duration(5) * time.Hour)
|
|
|
|
s.Require().NoError(s.authzKeeper.SaveGrant(s.ctx, addr1, addr2, genAuthMulti, nil), "creating grant 1->2")
|
|
s.Require().NoError(s.authzKeeper.SaveGrant(s.ctx, addr1, addr3, genAuthSend, &expired), "creating grant 1->3")
|
|
s.Require().NoError(s.authzKeeper.SaveGrant(s.ctx, addr1, addr4, sendAuth, ¬Expired), "creating grant 1->4")
|
|
// Without access to private keeper methods, I don't know how to save a grant with an invalid authorization.
|
|
newCtx := s.ctx.WithBlockTime(start.Add(time.Duration(1) * time.Minute))
|
|
|
|
tests := []struct {
|
|
name string
|
|
grantee sdk.AccAddress
|
|
granter sdk.AccAddress
|
|
msgType string
|
|
expAuth authz.Authorization
|
|
expExp *time.Time
|
|
}{
|
|
{
|
|
name: "grant has nil exp and is returned",
|
|
grantee: addr1,
|
|
granter: addr2,
|
|
msgType: genAuthMulti.MsgTypeURL(),
|
|
expAuth: genAuthMulti,
|
|
expExp: nil,
|
|
},
|
|
{
|
|
name: "grant is expired not returned",
|
|
grantee: addr1,
|
|
granter: addr3,
|
|
msgType: genAuthSend.MsgTypeURL(),
|
|
expAuth: nil,
|
|
expExp: nil,
|
|
},
|
|
{
|
|
name: "grant is not expired and is returned",
|
|
grantee: addr1,
|
|
granter: addr4,
|
|
msgType: sendAuth.MsgTypeURL(),
|
|
expAuth: sendAuth,
|
|
expExp: ¬Expired,
|
|
},
|
|
{
|
|
name: "grant is not expired but wrong msg type returns nil",
|
|
grantee: addr1,
|
|
granter: addr4,
|
|
msgType: genAuthMulti.MsgTypeURL(),
|
|
expAuth: nil,
|
|
expExp: nil,
|
|
},
|
|
{
|
|
name: "no grant exists between the two",
|
|
grantee: addr2,
|
|
granter: addr3,
|
|
msgType: genAuthSend.MsgTypeURL(),
|
|
expAuth: nil,
|
|
expExp: nil,
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
s.Run(tc.name, func() {
|
|
actAuth, actExp := s.authzKeeper.GetAuthorization(newCtx, tc.grantee, tc.granter, tc.msgType)
|
|
s.Assert().Equal(tc.expAuth, actAuth, "authorization")
|
|
s.Assert().Equal(tc.expExp, actExp, "expiration")
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestTestSuite(t *testing.T) {
|
|
suite.Run(t, new(TestSuite))
|
|
}
|